From 03798bd8c29ab2fc9b1e794a526509306982f0c3 Mon Sep 17 00:00:00 2001 From: gandhimathi Date: Tue, 19 Dec 2017 18:55:22 +0530 Subject: [PATCH] pdf folder commited --- .../angular-save-html-to-pdf/.bower.json | 43 + .../angular-save-html-to-pdf/README.md | 61 + .../angular-save-html-to-pdf/bower.json | 33 + .../angular-save-html-to-pdf/demo/index.html | 34 + .../dist/saveHtmlToPdf.js | 194 + .../dist/saveHtmlToPdf.min.js | 1 + .../angular-save-html-to-pdf/gulpfile.js | 34 + .../angular-save-html-to-pdf/package.json | 32 + .../src/htlmToPdfSave.module.js | 5 + .../src/htmlToPdfSave.buttonDirective.js | 23 + .../src/htmlToPdfSave.contentDirective.js | 153 + .../src/htmlToPdfSave.services.js | 10 + .../bower_components/html2canvas/.bower.json | 20 + .../bower_components/html2canvas/.gitignore | 20 + .../bower_components/html2canvas/Gruntfile.js | 99 + Apollo/bower_components/html2canvas/LICENSE | 22 + .../bower_components/html2canvas/bower.json | 10 + .../html2canvas/build/html2canvas.js | 2868 +++ .../html2canvas/build/html2canvas.min.js | 8 + .../html2canvas/examples/demo.html | 183 + .../html2canvas/examples/demo2.html | 65 + .../bower_components/html2canvas/package.json | 46 + Apollo/bower_components/html2canvas/readme.md | 122 + .../bower_components/html2canvas/src/Core.js | 410 + .../bower_components/html2canvas/src/Font.js | 64 + .../html2canvas/src/Generate.js | 424 + .../bower_components/html2canvas/src/Parse.js | 1143 ++ .../html2canvas/src/Preload.js | 317 + .../bower_components/html2canvas/src/Queue.js | 123 + .../html2canvas/src/Renderer.js | 101 + .../html2canvas/src/Support.js | 63 + .../bower_components/html2canvas/src/Util.js | 81 + .../html2canvas/src/renderers/Canvas.js | 128 + .../html2canvas/src/renderers/SVG.js | 206 + Apollo/bower_components/jsPDF/.bower.json | 37 + .../bower_components/jsPDF/CODE_OF_CONDUCT.md | 46 + .../bower_components/jsPDF/HOTFIX_README.md | 53 + .../bower_components/jsPDF/ISSUE_TEMPLATE.md | 13 + Apollo/bower_components/jsPDF/MIT-LICENSE.txt | 20 + Apollo/bower_components/jsPDF/README.md | 95 + Apollo/bower_components/jsPDF/RELEASE.md | 11 + Apollo/bower_components/jsPDF/bower.json | 27 + Apollo/bower_components/jsPDF/build.js | 110 + .../jsPDF/dist/jspdf.debug.js | 17089 ++++++++++++++++ .../bower_components/jsPDF/dist/jspdf.min.js | 172 + .../jsPDF/docs/FontObject.html | 273 + .../bower_components/jsPDF/docs/PubSub.html | 176 + .../jsPDF/docs/examples_js_editor.js.html | 238 + .../docs/examples_js_test_harness.js.html | 219 + .../jsPDF/docs/getting_started.md | 5 + .../bower_components/jsPDF/docs/global.html | 5426 +++++ Apollo/bower_components/jsPDF/docs/index.html | 111 + Apollo/bower_components/jsPDF/docs/jsPDF.html | 365 + .../bower_components/jsPDF/docs/jspdf.js.html | 2344 +++ .../jsPDF/docs/libs_css_colors.js.html | 221 + .../jsPDF/docs/libs_html2pdf.js.html | 168 + .../jsPDF/docs/libs_require_require.js.html | 2053 ++ .../jsPDF/docs/old-tests_libs_qunit.js.html | 1690 ++ .../jsPDF/docs/plugins_acroform.js.html | 1758 ++ .../jsPDF/docs/plugins_addhtml.js.html | 175 + .../jsPDF/docs/plugins_addimage.js.html | 790 + .../jsPDF/docs/plugins_annotations.js.html | 330 + .../jsPDF/docs/plugins_autoprint.js.html | 96 + .../jsPDF/docs/plugins_cell.js.html | 470 + .../jsPDF/docs/plugins_context2d.js.html | 1693 ++ .../jsPDF/docs/plugins_from_html.js.html | 1148 ++ .../jsPDF/docs/plugins_outline.js.html | 300 + .../docs/plugins_split_text_to_size.js.html | 383 + .../jsPDF/docs/plugins_svg.js.html | 239 + .../jsPDF/docs/plugins_xmp_metadata.js.html | 146 + .../jsPDF/docs/scripts/linenumber.js | 25 + .../scripts/prettify/Apache-License-2.0.txt | 202 + .../jsPDF/docs/scripts/prettify/lang-css.js | 2 + .../jsPDF/docs/scripts/prettify/prettify.js | 28 + .../jsPDF/docs/specs_utils_compare.js.html | 104 + .../docs/specs_utils_reference-server.js.html | 86 + .../jsPDF/docs/styles/jsdoc.css | 645 + .../jsPDF/docs/styles/prettify.css | 79 + .../jsPDF/docs/test_libs_qunit.js.html | 1690 ++ .../annotation/test_annotation-magFactor.pdf | 940 + .../examples/annotation/test_annotation.html | 86 + .../examples/annotation/test_annotation.pdf | 556 + .../annotation/test_annotation_2.html | 84 + .../jsPDF/examples/basic.html | 523 + .../bootstrap/css/bootstrap-responsive.css | 1092 + .../css/bootstrap-responsive.min.css | 9 + .../examples/bootstrap/css/bootstrap.css | 6039 ++++++ .../examples/bootstrap/css/bootstrap.min.css | 9 + .../img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes .../bootstrap/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes .../jsPDF/examples/bootstrap/js/bootstrap.js | 2159 ++ .../examples/bootstrap/js/bootstrap.min.js | 6 + .../bar_graph_with_text_and_lines.html | 689 + .../examples/context2d/test_context2d.html | 409 + .../context2d/test_context2d_paths.html | 47 + .../jsPDF/examples/css/editor.css | 51 + .../jsPDF/examples/css/main.css | 30 + .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 180 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 178 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 120 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 105 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 110 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 119 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 101 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 4369 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4369 bytes .../smoothness/jquery-ui-1.8.17.custom.css | 565 + .../jsPDF/examples/downloadify.html | 45 + .../jsPDF/examples/html2pdf/auto_break.html | 171 + .../jsPDF/examples/html2pdf/examples.css | 1 + .../examples/html2pdf/images/favicon.png | Bin 0 -> 28124 bytes .../jsPDF/examples/html2pdf/lists.html | 65 + .../jsPDF/examples/html2pdf/page_break.html | 54 + .../jsPDF/examples/html2pdf/pdf.html | 180 + .../jsPDF/examples/html2pdf/pdf2.html | 87 + .../jsPDF/examples/html2pdf/showcase.html | 93 + .../html2pdf/showcase_supported_html.html | 218 + .../jsPDF/examples/html2pdf/simple.html | 27 + .../jsPDF/examples/html2pdf/tables.html | 239 + .../jsPDF/examples/html2pdf/total_mess.html | 269 + .../jsPDF/examples/images.html | 157 + .../jsPDF/examples/images/24_bit.png | Bin 0 -> 36615 bytes .../jsPDF/examples/images/32_bit.png | Bin 0 -> 44856 bytes .../jsPDF/examples/images/RGBA_16bpc.png | Bin 0 -> 76032 bytes .../jsPDF/examples/images/RGB_16bpc.png | Bin 0 -> 61159 bytes .../jsPDF/examples/images/grayscale_16bpc.png | Bin 0 -> 22830 bytes .../jsPDF/examples/images/grayscale_8bpc.png | Bin 0 -> 13698 bytes .../images/grayscale_alpha_16_bpc.png | Bin 0 -> 37853 bytes .../examples/images/grayscale_alpha_8bpc.png | Bin 0 -> 22902 bytes .../jsPDF/examples/images/grid.png | Bin 0 -> 979 bytes .../jsPDF/examples/images/jpg.jpg | Bin 0 -> 12405 bytes .../jsPDF/examples/images/png8_flat.png | Bin 0 -> 14038 bytes .../jsPDF/examples/images/png8_trans.png | Bin 0 -> 13595 bytes .../examples/images/tiny_png_indexed.png | Bin 0 -> 11014 bytes .../jsPDF/examples/jaxer.html | 14 + .../jsPDF/examples/js/acroforms.js | 70 + .../jsPDF/examples/js/autoprint.js | 12 + .../jsPDF/examples/js/basic.js | 386 + .../jsPDF/examples/js/circles.js | 11 + .../jsPDF/examples/js/editor.js | 180 + .../jsPDF/examples/js/font-faces.js | 30 + .../jsPDF/examples/js/font-size.js | 6 + .../jsPDF/examples/js/from-html.js | 18 + .../jsPDF/examples/js/html2canvas.js | 7 + .../jsPDF/examples/js/images.js | 9 + .../jsPDF/examples/js/images_png.js | 9 + .../examples/js/jquery/jquery-1.7.1.min.js | 4 + .../js/jquery/jquery-ui-1.8.17.custom.min.js | 356 + .../jsPDF/examples/js/kitchen-sink.js | 3 + .../jsPDF/examples/js/landscape.js | 2 + .../jsPDF/examples/js/lines.js | 26 + .../jsPDF/examples/js/rectangles.js | 30 + .../jsPDF/examples/js/string-splitting.js | 45 + .../jsPDF/examples/js/test_harness.js | 161 + .../jsPDF/examples/js/text-colors.js | 17 + .../jsPDF/examples/js/theme-ambiance.js | 202 + .../jsPDF/examples/js/triangles.js | 8 + .../jsPDF/examples/js/two-page.js | 5 + .../jsPDF/examples/js/user-input.js | 27 + .../jsPDF/examples/null-logo-trans.png | Bin 0 -> 3728 bytes .../jsPDF/examples/octocat.jpg | Bin 0 -> 63908 bytes .../jsPDF/examples/octocat.png | Bin 0 -> 55219 bytes .../jsPDF/examples/outline/test_outline.html | 54 + .../jsPDF/examples/outline/test_outline.pdf | 302 + .../jsPDF/examples/runner.html | 89 + .../jsPDF/examples/test_from_html.html | 78 + .../test_from_html_css_page_breaks.html | 85 + .../jsPDF/examples/test_insert_page.html | 85 + .../jsPDF/examples/thinking-monkey.jpg | Bin 0 -> 67506 bytes Apollo/bower_components/jsPDF/index.html | 191 + Apollo/bower_components/jsPDF/jsdoc.json | 27 + Apollo/bower_components/jsPDF/karma.conf.js | 98 + Apollo/bower_components/jsPDF/main.js | 32 + .../bower_components/jsPDF/package-lock.json | 6305 ++++++ Apollo/bower_components/jsPDF/package.json | 57 + .../jsPDF/saucelabs.karma.conf.js | 131 + Apollo/bower_components/jsPDF/tests/README.md | 31 + .../tests/acroform/reference/checkbox.pdf | 260 + .../tests/acroform/reference/combobox.pdf | Bin 0 -> 6205 bytes .../tests/acroform/reference/listbox.pdf | 187 + .../tests/acroform/reference/password.pdf | 185 + .../tests/acroform/reference/pushbutton.pdf | 184 + .../tests/acroform/reference/radiogroup.pdf | 427 + .../tests/acroform/reference/textfield.pdf | 220 + .../jsPDF/tests/acroform/standard.spec.js | 118 + .../tests/addhtml/reference/html-output.pdf | Bin 0 -> 20991 bytes .../jsPDF/tests/addhtml/standard.spec.js | 21 + .../tests/annotations/reference/closed.pdf | 165 + .../tests/annotations/reference/freetext.pdf | 157 + .../tests/annotations/reference/open.pdf | 165 + .../jsPDF/tests/annotations/standard.spec.js | 58 + .../tests/autoprint/reference/autoprint.pdf | 165 + .../jsPDF/tests/autoprint/standard.spec.js | 14 + .../jsPDF/tests/init/reference/cm.pdf | 354 + .../jsPDF/tests/init/reference/compress.pdf | Bin 0 -> 2507 bytes .../jsPDF/tests/init/reference/em.pdf | 354 + .../jsPDF/tests/init/reference/ex.pdf | 354 + .../jsPDF/tests/init/reference/in.pdf | 354 + .../jsPDF/tests/init/reference/landscape.pdf | 161 + .../jsPDF/tests/init/reference/mm.pdf | 354 + .../jsPDF/tests/init/reference/pc.pdf | 354 + .../jsPDF/tests/init/reference/properties.pdf | 159 + .../jsPDF/tests/init/reference/pt.pdf | 354 + .../jsPDF/tests/init/reference/px.pdf | 354 + .../jsPDF/tests/init/standard.spec.js | 185 + .../jsPDF/tests/pages/display-mode.spec.js | 177 + .../tests/pages/reference/continuous.pdf | 233 + .../tests/pages/reference/deleted-page.pdf | 209 + .../tests/pages/reference/fullscreen.pdf | 234 + .../pages/reference/insert-page-middle.pdf | 209 + .../tests/pages/reference/insert-page.pdf | 209 + .../tests/pages/reference/move-page-2.pdf | 185 + .../jsPDF/tests/pages/reference/move-page.pdf | 185 + .../jsPDF/tests/pages/reference/outlines.pdf | 234 + .../jsPDF/tests/pages/reference/set-page.pdf | 209 + .../jsPDF/tests/pages/reference/single.pdf | 233 + .../jsPDF/tests/pages/reference/thumbs.pdf | 234 + .../jsPDF/tests/pages/reference/twoleft.pdf | 233 + .../jsPDF/tests/pages/reference/tworight.pdf | 233 + .../jsPDF/tests/pages/reference/zoom-2x.pdf | 161 + .../jsPDF/tests/pages/reference/zoom-3x.pdf | 161 + .../pages/reference/zoom-full-height.pdf | 161 + .../tests/pages/reference/zoom-full-page.pdf | 161 + .../tests/pages/reference/zoom-full-width.pdf | 161 + .../tests/pages/reference/zoom-original.pdf | 161 + .../jsPDF/tests/pages/standard.spec.js | 82 + .../jsPDF/tests/shapes/reference/circles.pdf | 173 + .../tests/shapes/reference/fill-color.pdf | 157 + .../jsPDF/tests/shapes/reference/line.pdf | 157 + .../jsPDF/tests/shapes/reference/lines.pdf | 186 + .../tests/shapes/reference/rectangles.pdf | 184 + .../jsPDF/tests/shapes/standard.spec.js | 111 + .../jsPDF/tests/text/reference/alignment.pdf | 189 + .../jsPDF/tests/text/reference/angle.pdf | 161 + .../jsPDF/tests/text/reference/blank.pdf | 154 + .../jsPDF/tests/text/reference/color.pdf | 168 + .../tests/text/reference/different-sizes.pdf | 168 + .../jsPDF/tests/text/reference/font-faces.pdf | 189 + .../tests/text/reference/letter-spacing.pdf | 259 + .../jsPDF/tests/text/reference/line-break.pdf | 162 + .../jsPDF/tests/text/reference/red-black.pdf | 168 + .../jsPDF/tests/text/reference/standard.pdf | 161 + .../jsPDF/tests/text/reference/stroke.pdf | 185 + .../jsPDF/tests/text/reference/two-page.pdf | 192 + .../jsPDF/tests/text/standard.spec.js | 143 + .../jsPDF/tests/utils/compare.js | 46 + .../jsPDF/tests/utils/reference-server.js | 29 + Apollo/bower_components/jsPDF/yarn.lock | 4155 ++++ 252 files changed, 89825 insertions(+) create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/.bower.json create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/README.md create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/bower.json create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/demo/index.html create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.js create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.min.js create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/gulpfile.js create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/package.json create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/src/htlmToPdfSave.module.js create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/src/htmlToPdfSave.buttonDirective.js create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/src/htmlToPdfSave.contentDirective.js create mode 100755 Apollo/bower_components/angular-save-html-to-pdf/src/htmlToPdfSave.services.js create mode 100755 Apollo/bower_components/html2canvas/.bower.json create mode 100755 Apollo/bower_components/html2canvas/.gitignore create mode 100755 Apollo/bower_components/html2canvas/Gruntfile.js create mode 100755 Apollo/bower_components/html2canvas/LICENSE create mode 100755 Apollo/bower_components/html2canvas/bower.json create mode 100755 Apollo/bower_components/html2canvas/build/html2canvas.js create mode 100755 Apollo/bower_components/html2canvas/build/html2canvas.min.js create mode 100755 Apollo/bower_components/html2canvas/examples/demo.html create mode 100755 Apollo/bower_components/html2canvas/examples/demo2.html create mode 100755 Apollo/bower_components/html2canvas/package.json create mode 100755 Apollo/bower_components/html2canvas/readme.md create mode 100755 Apollo/bower_components/html2canvas/src/Core.js create mode 100755 Apollo/bower_components/html2canvas/src/Font.js create mode 100755 Apollo/bower_components/html2canvas/src/Generate.js create mode 100755 Apollo/bower_components/html2canvas/src/Parse.js create mode 100755 Apollo/bower_components/html2canvas/src/Preload.js create mode 100755 Apollo/bower_components/html2canvas/src/Queue.js create mode 100755 Apollo/bower_components/html2canvas/src/Renderer.js create mode 100755 Apollo/bower_components/html2canvas/src/Support.js create mode 100755 Apollo/bower_components/html2canvas/src/Util.js create mode 100755 Apollo/bower_components/html2canvas/src/renderers/Canvas.js create mode 100755 Apollo/bower_components/html2canvas/src/renderers/SVG.js create mode 100755 Apollo/bower_components/jsPDF/.bower.json create mode 100755 Apollo/bower_components/jsPDF/CODE_OF_CONDUCT.md create mode 100755 Apollo/bower_components/jsPDF/HOTFIX_README.md create mode 100755 Apollo/bower_components/jsPDF/ISSUE_TEMPLATE.md create mode 100755 Apollo/bower_components/jsPDF/MIT-LICENSE.txt create mode 100755 Apollo/bower_components/jsPDF/README.md create mode 100755 Apollo/bower_components/jsPDF/RELEASE.md create mode 100755 Apollo/bower_components/jsPDF/bower.json create mode 100755 Apollo/bower_components/jsPDF/build.js create mode 100755 Apollo/bower_components/jsPDF/dist/jspdf.debug.js create mode 100755 Apollo/bower_components/jsPDF/dist/jspdf.min.js create mode 100755 Apollo/bower_components/jsPDF/docs/FontObject.html create mode 100755 Apollo/bower_components/jsPDF/docs/PubSub.html create mode 100755 Apollo/bower_components/jsPDF/docs/examples_js_editor.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/examples_js_test_harness.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/getting_started.md create mode 100755 Apollo/bower_components/jsPDF/docs/global.html create mode 100755 Apollo/bower_components/jsPDF/docs/index.html create mode 100755 Apollo/bower_components/jsPDF/docs/jsPDF.html create mode 100755 Apollo/bower_components/jsPDF/docs/jspdf.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/libs_css_colors.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/libs_html2pdf.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/libs_require_require.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/old-tests_libs_qunit.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_acroform.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_addhtml.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_addimage.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_annotations.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_autoprint.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_cell.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_context2d.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_from_html.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_outline.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_split_text_to_size.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_svg.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/plugins_xmp_metadata.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/scripts/linenumber.js create mode 100755 Apollo/bower_components/jsPDF/docs/scripts/prettify/Apache-License-2.0.txt create mode 100755 Apollo/bower_components/jsPDF/docs/scripts/prettify/lang-css.js create mode 100755 Apollo/bower_components/jsPDF/docs/scripts/prettify/prettify.js create mode 100755 Apollo/bower_components/jsPDF/docs/specs_utils_compare.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/specs_utils_reference-server.js.html create mode 100755 Apollo/bower_components/jsPDF/docs/styles/jsdoc.css create mode 100755 Apollo/bower_components/jsPDF/docs/styles/prettify.css create mode 100755 Apollo/bower_components/jsPDF/docs/test_libs_qunit.js.html create mode 100755 Apollo/bower_components/jsPDF/examples/annotation/test_annotation-magFactor.pdf create mode 100755 Apollo/bower_components/jsPDF/examples/annotation/test_annotation.html create mode 100755 Apollo/bower_components/jsPDF/examples/annotation/test_annotation.pdf create mode 100755 Apollo/bower_components/jsPDF/examples/annotation/test_annotation_2.html create mode 100755 Apollo/bower_components/jsPDF/examples/basic.html create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/css/bootstrap-responsive.css create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/css/bootstrap-responsive.min.css create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/css/bootstrap.css create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/css/bootstrap.min.css create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/img/glyphicons-halflings-white.png create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/img/glyphicons-halflings.png create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/js/bootstrap.js create mode 100755 Apollo/bower_components/jsPDF/examples/bootstrap/js/bootstrap.min.js create mode 100755 Apollo/bower_components/jsPDF/examples/canvg_context2d/bar_graph_with_text_and_lines.html create mode 100755 Apollo/bower_components/jsPDF/examples/context2d/test_context2d.html create mode 100755 Apollo/bower_components/jsPDF/examples/context2d/test_context2d_paths.html create mode 100755 Apollo/bower_components/jsPDF/examples/css/editor.css create mode 100755 Apollo/bower_components/jsPDF/examples/css/main.css create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-icons_222222_256x240.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-icons_2e83ff_256x240.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-icons_454545_256x240.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-icons_888888_256x240.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/images/ui-icons_cd0a0a_256x240.png create mode 100755 Apollo/bower_components/jsPDF/examples/css/smoothness/jquery-ui-1.8.17.custom.css create mode 100755 Apollo/bower_components/jsPDF/examples/downloadify.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/auto_break.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/examples.css create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/images/favicon.png create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/lists.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/page_break.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/pdf.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/pdf2.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/showcase.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/showcase_supported_html.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/simple.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/tables.html create mode 100755 Apollo/bower_components/jsPDF/examples/html2pdf/total_mess.html create mode 100755 Apollo/bower_components/jsPDF/examples/images.html create mode 100755 Apollo/bower_components/jsPDF/examples/images/24_bit.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/32_bit.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/RGBA_16bpc.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/RGB_16bpc.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/grayscale_16bpc.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/grayscale_8bpc.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/grayscale_alpha_16_bpc.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/grayscale_alpha_8bpc.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/grid.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/jpg.jpg create mode 100755 Apollo/bower_components/jsPDF/examples/images/png8_flat.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/png8_trans.png create mode 100755 Apollo/bower_components/jsPDF/examples/images/tiny_png_indexed.png create mode 100755 Apollo/bower_components/jsPDF/examples/jaxer.html create mode 100755 Apollo/bower_components/jsPDF/examples/js/acroforms.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/autoprint.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/basic.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/circles.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/editor.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/font-faces.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/font-size.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/from-html.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/html2canvas.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/images.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/images_png.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/jquery/jquery-1.7.1.min.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/jquery/jquery-ui-1.8.17.custom.min.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/kitchen-sink.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/landscape.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/lines.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/rectangles.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/string-splitting.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/test_harness.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/text-colors.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/theme-ambiance.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/triangles.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/two-page.js create mode 100755 Apollo/bower_components/jsPDF/examples/js/user-input.js create mode 100755 Apollo/bower_components/jsPDF/examples/null-logo-trans.png create mode 100755 Apollo/bower_components/jsPDF/examples/octocat.jpg create mode 100755 Apollo/bower_components/jsPDF/examples/octocat.png create mode 100755 Apollo/bower_components/jsPDF/examples/outline/test_outline.html create mode 100755 Apollo/bower_components/jsPDF/examples/outline/test_outline.pdf create mode 100755 Apollo/bower_components/jsPDF/examples/runner.html create mode 100755 Apollo/bower_components/jsPDF/examples/test_from_html.html create mode 100755 Apollo/bower_components/jsPDF/examples/test_from_html_css_page_breaks.html create mode 100755 Apollo/bower_components/jsPDF/examples/test_insert_page.html create mode 100755 Apollo/bower_components/jsPDF/examples/thinking-monkey.jpg create mode 100755 Apollo/bower_components/jsPDF/index.html create mode 100755 Apollo/bower_components/jsPDF/jsdoc.json create mode 100755 Apollo/bower_components/jsPDF/karma.conf.js create mode 100755 Apollo/bower_components/jsPDF/main.js create mode 100755 Apollo/bower_components/jsPDF/package-lock.json create mode 100755 Apollo/bower_components/jsPDF/package.json create mode 100755 Apollo/bower_components/jsPDF/saucelabs.karma.conf.js create mode 100755 Apollo/bower_components/jsPDF/tests/README.md create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/reference/checkbox.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/reference/combobox.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/reference/listbox.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/reference/password.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/reference/pushbutton.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/reference/radiogroup.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/reference/textfield.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/acroform/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/addhtml/reference/html-output.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/addhtml/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/annotations/reference/closed.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/annotations/reference/freetext.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/annotations/reference/open.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/annotations/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/autoprint/reference/autoprint.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/autoprint/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/cm.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/compress.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/em.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/ex.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/in.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/landscape.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/mm.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/pc.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/properties.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/pt.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/reference/px.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/init/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/pages/display-mode.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/continuous.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/deleted-page.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/fullscreen.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/insert-page-middle.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/insert-page.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/move-page-2.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/move-page.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/outlines.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/set-page.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/single.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/thumbs.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/twoleft.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/tworight.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/zoom-2x.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/zoom-3x.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/zoom-full-height.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/zoom-full-page.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/zoom-full-width.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/reference/zoom-original.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/pages/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/shapes/reference/circles.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/shapes/reference/fill-color.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/shapes/reference/line.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/shapes/reference/lines.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/shapes/reference/rectangles.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/shapes/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/alignment.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/angle.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/blank.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/color.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/different-sizes.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/font-faces.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/letter-spacing.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/line-break.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/red-black.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/standard.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/stroke.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/reference/two-page.pdf create mode 100755 Apollo/bower_components/jsPDF/tests/text/standard.spec.js create mode 100755 Apollo/bower_components/jsPDF/tests/utils/compare.js create mode 100755 Apollo/bower_components/jsPDF/tests/utils/reference-server.js create mode 100755 Apollo/bower_components/jsPDF/yarn.lock diff --git a/Apollo/bower_components/angular-save-html-to-pdf/.bower.json b/Apollo/bower_components/angular-save-html-to-pdf/.bower.json new file mode 100755 index 00000000..fe9ef0af --- /dev/null +++ b/Apollo/bower_components/angular-save-html-to-pdf/.bower.json @@ -0,0 +1,43 @@ +{ + "name": "angular-save-html-to-pdf", + "description": "Save HTML in pdf format by angularjs . Directives which are using libraries to convert html to html5canvas and save html5canvas as pdf .", + "main": "dist/saveHtmlToPdf.js", + "authors": [ + "hearsid" + ], + "license": "ISC", + "keywords": [ + "html5canvas", + "pdf", + "html", + "html", + "to", + "pdf", + "angular" + ], + "moduleType": [], + "homepage": "https://github.com/hearsid/ng-html-to-pdf-save", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "angular": "^1.5.5", + "html2canvas": "git://github.com/niklasvh/html2canvas.git", + "jquery": "^1.12", + "jsPDF": "git://github.com/MrRio/jsPDF.git" + }, + "_release": "da9894b508", + "_resolution": { + "type": "branch", + "branch": "master", + "commit": "da9894b5080e0fa9c8d8a46c89cbd2f87b17b6c3" + }, + "_source": "https://github.com/hearsid/ng-html-to-pdf-save.git", + "_target": "*", + "_originalSource": "angular-save-html-to-pdf", + "_direct": true +} \ No newline at end of file diff --git a/Apollo/bower_components/angular-save-html-to-pdf/README.md b/Apollo/bower_components/angular-save-html-to-pdf/README.md new file mode 100755 index 00000000..3c324688 --- /dev/null +++ b/Apollo/bower_components/angular-save-html-to-pdf/README.md @@ -0,0 +1,61 @@ +## Angularjs save HTML as PDF in your browser + +This is an angularjs module to save HTML as PDF DEMO, it basically converts the HTML to HTML5 canvas and captures the same and converts it to PDF and saves it in your browser . + +
+Here are the steps : +
+1) bower install angular-save-html-to-pdf +
OR
+npm install angular-save-html-to-pdf + + 2) Link the JS files in your HTML file : + + ``` + + + + + + + ``` + + 3) Add this module to your angular app : + ``` + var app = angular.module('app' , ['htmlToPdfSave']) ; + ``` + 4) Use the directives in your app , here is a code snippet from a working copy in demo folder : + ``` + + + +
+ Hello Someone +
+ + + + + +
+ Hello World +
+ ``` + + To allow addition of multiple pdf save button and linking them to the pdf save content block every pdf-save-button and pdf-save-content directive is associated with an ID , the pdf-save-button will match the ID with pdf-save-content block and the matching HTML block will be saved . +
+
+ + ### Developer instructions: + If you would like to run the project locally, you can download the repo and run :
+ 1) + ``` + > gulp concat + > gulp compress + ``` + commands to create the bundled file which is then used in the demo/index.html file. + + 2) demo/index.html file can be served easily by any static web server to test the project, I use and thus recommend python server which you can start by writing + ``` python -m SimpleHTTPServer 9090 ``` + 9090 can be replaced by your prefered port.
+ NOTE : This is a new repository and has been tested with basic HTML and google graphs , please create github issues if you find it is not working with something and consider contributing. Cheers . diff --git a/Apollo/bower_components/angular-save-html-to-pdf/bower.json b/Apollo/bower_components/angular-save-html-to-pdf/bower.json new file mode 100755 index 00000000..81de65d9 --- /dev/null +++ b/Apollo/bower_components/angular-save-html-to-pdf/bower.json @@ -0,0 +1,33 @@ +{ + "name": "angular-save-html-to-pdf", + "description": "Save HTML in pdf format by angularjs . Directives which are using libraries to convert html to html5canvas and save html5canvas as pdf .", + "main": "dist/saveHtmlToPdf.js", + "authors": [ + "hearsid" + ], + "license": "ISC", + "keywords": [ + "html5canvas", + "pdf", + "html", + "html", + "to", + "pdf", + "angular" + ], + "moduleType": [], + "homepage": "", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "angular": "^1.5.5" , + "html2canvas" : "git://github.com/niklasvh/html2canvas.git" , + "jquery" : "^1.12" , + "jsPDF" : "git://github.com/MrRio/jsPDF.git" + } +} diff --git a/Apollo/bower_components/angular-save-html-to-pdf/demo/index.html b/Apollo/bower_components/angular-save-html-to-pdf/demo/index.html new file mode 100755 index 00000000..10d22052 --- /dev/null +++ b/Apollo/bower_components/angular-save-html-to-pdf/demo/index.html @@ -0,0 +1,34 @@ + + + + + + +
+ + Hello Someone +
+ + + + +
+ + Hello World +
+ + + + + + + + + diff --git a/Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.js b/Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.js new file mode 100755 index 00000000..f51665ab --- /dev/null +++ b/Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.js @@ -0,0 +1,194 @@ + +angular.module('htmlToPdfSave' , []) ; + + + + + +angular.module('htmlToPdfSave') +.directive('pdfSaveButton' , ['$rootScope' , '$pdfStorage' , function($rootScope , $pdfStorage) { + + return { + restrict: 'A', + link : function(scope , element , attrs ) { + $pdfStorage.pdfSaveButtons.push(element) ; + + scope.buttonText = "Button"; + element.on('click' , function() { + var activePdfSaveId = attrs.pdfSaveButton ; + var activePdfSaveName = attrs.pdfName; + $rootScope.$broadcast('savePdfEvent' , {activePdfSaveId : activePdfSaveId, activePdfSaveName: activePdfSaveName}) ; + + + }) + } + + + } + +}]) ; + + +angular.module('htmlToPdfSave') +.directive('pdfSaveContent' , [ '$rootScope' , '$pdfStorage' , function ($rootScope , $pdfStorage) { + + + return { + link : function(scope , element , attrs ) { + + $pdfStorage.pdfSaveContents.push(element) ; + + var myListener = scope.$on('savePdfEvent' , function(event , args) { + + var currentElement = element ; + var currentElementId = currentElement[0].getAttribute('pdf-save-content') ; + + // save a call of query selector because angular loads the element on load by default + // var elem = document.querySelectorAll('[pdf-save]') ; + var elem = $pdfStorage.pdfSaveContents ; + var broadcastedId = args.activePdfSaveId ; + var broadcastedName = args.activePdfSaveName || 'default.pdf'; + + + + //iterate through the element array to match the id + for(var i = 0;i < elem.length ; i++) { + + // handle the case of elem getting length + // if(i == 'length' || i == 'item') + // continue ; + + // if the event is received by other element than for whom it what propogated for continue + + + if(!matchTheIds(broadcastedId , currentElementId)) + continue ; + + var single = elem[i] ; + var singleElement = single[0]; + //var parent = single[0] ; + var pdfId = singleElement.getAttribute('pdf-save-content') ; + + if(matchTheIds(pdfId , broadcastedId)) { + console.log('Id is same'); + convertToPdf(elem , pdfId); + break ; // exit the loop once pdf gets printed + } + + } + + function matchTheIds(elemId , broadcastedId) { + return elemId == broadcastedId ; + } + + function convertToPdf(theElement , id) { + //theElement = [theElement]; + convert(theElement , id ) ; + + } + + + function convert(theElement , id) { + var quotes = $('div[pdf-save-content='+id+']')[0]; + html2canvas(quotes, { + onrendered: function(canvas) { + var pdf = new jsPDF('p', 'pt', 'letter'); + for (var i = 0; i <= quotes.clientHeight/980; i++) { + var srcImg = canvas; + var sX = 0; + var sY = 980*i; // start 980 pixels down for every new page + var sWidth = 900; + var sHeight = 980; + var dX = 0; + var dY = 0; + var dWidth = 900; + var dHeight = 980; + + window.onePageCanvas = document.createElement("canvas"); + onePageCanvas.setAttribute('width', 900); + onePageCanvas.setAttribute('height', 980); + var ctx = onePageCanvas.getContext('2d'); + // details on this usage of this function: + // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing + ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight); + + // document.body.appendChild(canvas); + var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0); + + var width = onePageCanvas.width; + var height = onePageCanvas.clientHeight; + + //! If we're on anything other than the first page, + // add another page + if (i > 0) { + pdf.addPage(612, 791); //8.5" x 11" in pts (in*72) + } + //! now we declare that we're working on that page + pdf.setPage(i+1); + //! now we add content to that page! + pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62)); + + } + //! after the for loop is finished running, we save the pdf. + pdf.save(broadcastedName); + } + }); + /*var element = $('[pdf-save-content='+id+']') , + cache_width = element.width(), + a4 =[ 595.28, 841.89]; // for a4 size paper width and height + + + $('body').scrollTop(0); + createPDF(); + + //create pdf + function createPDF(){ + getCanvas().then(function(canvas){ + console.log('resolved get canvas'); + var img = canvas.toDataURL("image/png"), + doc = new jsPDF({ + unit:'px', + format:'a4' + }); + doc.addImage(img, 'JPEG', 20, 20); + doc.save(broadcastedName); + element.width(cache_width); + + }) + } + + // create canvas object + function getCanvas(){ + element.width((a4[0]*1.33333) -80).css('max-width','none'); + return html2canvas(element,{ + imageTimeout:2000, + removeContainer:true + }); + }*/ + + + } + + + + }) ; + // handle the memory leak + // unbind the event + scope.$on('$destroy', myListener); + +} + +} + +}]) ; + + +angular.module('htmlToPdfSave') +.service('$pdfStorage' , function() { + this.pdfSaveButtons = [] ; + this.pdfSaveContents = [] ; +}) +.service('pdfSaveConfig' , function() { + this.pdfName = "default.pdf"; +}) + diff --git a/Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.min.js b/Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.min.js new file mode 100755 index 00000000..539ee046 --- /dev/null +++ b/Apollo/bower_components/angular-save-html-to-pdf/dist/saveHtmlToPdf.min.js @@ -0,0 +1 @@ +angular.module("htmlToPdfSave",[]),angular.module("htmlToPdfSave").directive("pdfSaveButton",["$rootScope","$pdfStorage",function(e,t){return{restrict:"A",link:function(a,n,o){t.pdfSaveButtons.push(n),a.buttonText="Button",n.on("click",function(){var t=o.pdfSaveButton,a=o.pdfName;e.$broadcast("savePdfEvent",{activePdfSaveId:t,activePdfSaveName:a})})}}}]),angular.module("htmlToPdfSave").directive("pdfSaveContent",["$rootScope","$pdfStorage",function(e,t){return{link:function(e,a,n){t.pdfSaveContents.push(a);var o=e.$on("savePdfEvent",function(e,n){function o(e,t){return e==t}function d(e,t){v(e,t)}function v(e,t){var a=$("div[pdf-save-content="+t+"]")[0];html2canvas(a,{onrendered:function(e){for(var t=new jsPDF("p","pt","letter"),n=0;n<=a.clientHeight/980;n++){var o=e,d=0,v=980*n,f=900,i=980,r=0,u=0,c=900,g=980;window.onePageCanvas=document.createElement("canvas"),onePageCanvas.setAttribute("width",900),onePageCanvas.setAttribute("height",980);var p=onePageCanvas.getContext("2d");p.drawImage(o,d,v,f,i,r,u,c,g);var l=onePageCanvas.toDataURL("image/png",1),S=onePageCanvas.width,P=onePageCanvas.clientHeight;n>0&&t.addPage(612,791),t.setPage(n+1),t.addImage(l,"PNG",20,40,.62*S,.62*P)}t.save(s)}})}for(var f=a,i=f[0].getAttribute("pdf-save-content"),r=t.pdfSaveContents,u=n.activePdfSaveId,s=n.activePdfSaveName||"default.pdf",c=0;c 0) { + pdf.addPage(612, 791); //8.5" x 11" in pts (in*72) + } + //! now we declare that we're working on that page + pdf.setPage(i+1); + //! now we add content to that page! + pdf.addImage(canvasDataURL, 'PNG', 20, 40, (width*.62), (height*.62)); + + } + //! after the for loop is finished running, we save the pdf. + pdf.save(broadcastedName); + } + }); + /*var element = $('[pdf-save-content='+id+']') , + cache_width = element.width(), + a4 =[ 595.28, 841.89]; // for a4 size paper width and height + + + $('body').scrollTop(0); + createPDF(); + + //create pdf + function createPDF(){ + getCanvas().then(function(canvas){ + console.log('resolved get canvas'); + var img = canvas.toDataURL("image/png"), + doc = new jsPDF({ + unit:'px', + format:'a4' + }); + doc.addImage(img, 'JPEG', 20, 20); + doc.save(broadcastedName); + element.width(cache_width); + + }) + } + + // create canvas object + function getCanvas(){ + element.width((a4[0]*1.33333) -80).css('max-width','none'); + return html2canvas(element,{ + imageTimeout:2000, + removeContainer:true + }); + }*/ + + + } + + + + }) ; + // handle the memory leak + // unbind the event + scope.$on('$destroy', myListener); + +} + +} + +}]) ; diff --git a/Apollo/bower_components/angular-save-html-to-pdf/src/htmlToPdfSave.services.js b/Apollo/bower_components/angular-save-html-to-pdf/src/htmlToPdfSave.services.js new file mode 100755 index 00000000..bfd8c0d3 --- /dev/null +++ b/Apollo/bower_components/angular-save-html-to-pdf/src/htmlToPdfSave.services.js @@ -0,0 +1,10 @@ + +angular.module('htmlToPdfSave') +.service('$pdfStorage' , function() { + this.pdfSaveButtons = [] ; + this.pdfSaveContents = [] ; +}) +.service('pdfSaveConfig' , function() { + this.pdfName = "default.pdf"; +}) + diff --git a/Apollo/bower_components/html2canvas/.bower.json b/Apollo/bower_components/html2canvas/.bower.json new file mode 100755 index 00000000..d724ee46 --- /dev/null +++ b/Apollo/bower_components/html2canvas/.bower.json @@ -0,0 +1,20 @@ +{ + "name": "html2canvas", + "version": "0.4.1", + "description": "Screenshots with JavaScript", + "main": "build/html2canvas.js", + "ignore": [ + "tests", + ".travis.yml" + ], + "homepage": "https://github.com/niklasvh/html2canvas", + "_release": "0.4.1", + "_resolution": { + "type": "version", + "tag": "0.4.1", + "commit": "051576578827a7e7c99d9ec70f654b6f50396dbf" + }, + "_source": "git://github.com/niklasvh/html2canvas.git", + "_target": "*", + "_originalSource": "git://github.com/niklasvh/html2canvas.git" +} \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/.gitignore b/Apollo/bower_components/html2canvas/.gitignore new file mode 100755 index 00000000..6df82bbf --- /dev/null +++ b/Apollo/bower_components/html2canvas/.gitignore @@ -0,0 +1,20 @@ +/nbproject/ +/images/ +/tests/templates/ +/tests/cache/ +/tests/flashcanvas.html +/lib/ +/bin/ +image.jpg +/.project +/.settings/ +/tests/certificate.pem +node_modules/ +.envrc +server.js +*.sublime-workspace +chromedriver.log +*.baseline +*.iml +.idea/ +.DS_Store diff --git a/Apollo/bower_components/html2canvas/Gruntfile.js b/Apollo/bower_components/html2canvas/Gruntfile.js new file mode 100755 index 00000000..b116d0fa --- /dev/null +++ b/Apollo/bower_components/html2canvas/Gruntfile.js @@ -0,0 +1,99 @@ +/*global module:false*/ +module.exports = function(grunt) { + + var meta = { + banner: '/*\n <%= pkg.title || pkg.name %> <%= pkg.version %>' + + '<%= pkg.homepage ? " <" + pkg.homepage + ">" : "" %>' + '\n' + + ' Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>' + + '\n\n Released under <%= _.pluck(pkg.licenses, "type").join(", ") %> License\n*/\n', + pre: '\n(function(window, document, undefined){\n\n', + post: '\n})(window,document);' + }; + + // Project configuration. + grunt.initConfig({ + + pkg: grunt.file.readJSON('package.json'), + + qunit: { + files: ['tests/qunit/index.html'] + }, + concat: { + dist: { + src: [ + 'src/Core.js', + 'src/Font.js', + 'src/Generate.js', + 'src/Queue.js', + 'src/Parse.js', + 'src/Preload.js', + 'src/Renderer.js', + 'src/Support.js', + 'src/Util.js', + 'src/renderers/Canvas.js' + ], + dest: 'build/<%= pkg.name %>.js' + }, + options:{ + banner: meta.banner + meta.pre, + footer: meta.post + } + }, + uglify: { + dist: { + src: ['<%= concat.dist.dest %>'], + dest: 'build/<%= pkg.name %>.min.js' + }, + options: { + banner: meta.banner + } + }, + watch: { + files: 'src/*', + tasks: ['build', 'jshint'] + }, + jshint: { + all: ['<%= concat.dist.dest %>'], + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + browser: true, + globals: { + jQuery: true + } + } + } + }); + + grunt.registerTask('webdriver', 'Browser render tests', function(arg1) { + var selenium = require("./tests/selenium.js"); + var done = this.async(); + + if (arguments.length) { + selenium[arg1].apply(null, arguments); + } else { + selenium.tests(); + } + }); + + // Load tasks + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-qunit'); + + // Default task. + grunt.registerTask('build', ['concat', 'uglify']); + grunt.registerTask('default', ['concat', 'jshint', 'qunit', 'uglify']); + grunt.registerTask('travis', ['concat', 'jshint', 'qunit', 'uglify', 'webdriver']); + +}; diff --git a/Apollo/bower_components/html2canvas/LICENSE b/Apollo/bower_components/html2canvas/LICENSE new file mode 100755 index 00000000..a73ffc91 --- /dev/null +++ b/Apollo/bower_components/html2canvas/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012 Niklas von Hertzen + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/bower.json b/Apollo/bower_components/html2canvas/bower.json new file mode 100755 index 00000000..901c3378 --- /dev/null +++ b/Apollo/bower_components/html2canvas/bower.json @@ -0,0 +1,10 @@ +{ + "name": "html2canvas", + "version": "0.4.1", + "description": "Screenshots with JavaScript", + "main": "build/html2canvas.js", + "ignore": [ + "tests", + ".travis.yml" + ] +} \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/build/html2canvas.js b/Apollo/bower_components/html2canvas/build/html2canvas.js new file mode 100755 index 00000000..3a44bc37 --- /dev/null +++ b/Apollo/bower_components/html2canvas/build/html2canvas.js @@ -0,0 +1,2868 @@ +/* + html2canvas 0.4.1 + Copyright (c) 2013 Niklas von Hertzen + + Released under MIT License +*/ + +(function(window, document, undefined){ + +"use strict"; + +var _html2canvas = {}, +previousElement, +computedCSS, +html2canvas; + +_html2canvas.Util = {}; + +_html2canvas.Util.log = function(a) { + if (_html2canvas.logging && window.console && window.console.log) { + window.console.log(a); + } +}; + +_html2canvas.Util.trimText = (function(isNative){ + return function(input) { + return isNative ? isNative.apply(input) : ((input || '') + '').replace( /^\s+|\s+$/g , '' ); + }; +})(String.prototype.trim); + +_html2canvas.Util.asFloat = function(v) { + return parseFloat(v); +}; + +(function() { + // TODO: support all possible length values + var TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g; + var TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g; + _html2canvas.Util.parseTextShadows = function (value) { + if (!value || value === 'none') { + return []; + } + + // find multiple shadow declarations + var shadows = value.match(TEXT_SHADOW_PROPERTY), + results = []; + for (var i = 0; shadows && (i < shadows.length); i++) { + var s = shadows[i].match(TEXT_SHADOW_VALUES); + results.push({ + color: s[0], + offsetX: s[1] ? s[1].replace('px', '') : 0, + offsetY: s[2] ? s[2].replace('px', '') : 0, + blur: s[3] ? s[3].replace('px', '') : 0 + }); + } + return results; + }; +})(); + + +_html2canvas.Util.parseBackgroundImage = function (value) { + var whitespace = ' \r\n\t', + method, definition, prefix, prefix_i, block, results = [], + c, mode = 0, numParen = 0, quote, args; + + var appendResult = function(){ + if(method) { + if(definition.substr( 0, 1 ) === '"') { + definition = definition.substr( 1, definition.length - 2 ); + } + if(definition) { + args.push(definition); + } + if(method.substr( 0, 1 ) === '-' && + (prefix_i = method.indexOf( '-', 1 ) + 1) > 0) { + prefix = method.substr( 0, prefix_i); + method = method.substr( prefix_i ); + } + results.push({ + prefix: prefix, + method: method.toLowerCase(), + value: block, + args: args + }); + } + args = []; //for some odd reason, setting .length = 0 didn't work in safari + method = + prefix = + definition = + block = ''; + }; + + appendResult(); + for(var i = 0, ii = value.length; i -1){ + continue; + } + switch(c) { + case '"': + if(!quote) { + quote = c; + } + else if(quote === c) { + quote = null; + } + break; + + case '(': + if(quote) { break; } + else if(mode === 0) { + mode = 1; + block += c; + continue; + } else { + numParen++; + } + break; + + case ')': + if(quote) { break; } + else if(mode === 1) { + if(numParen === 0) { + mode = 0; + block += c; + appendResult(); + continue; + } else { + numParen--; + } + } + break; + + case ',': + if(quote) { break; } + else if(mode === 0) { + appendResult(); + continue; + } + else if (mode === 1) { + if(numParen === 0 && !method.match(/^url$/i)) { + args.push(definition); + definition = ''; + block += c; + continue; + } + } + break; + } + + block += c; + if(mode === 0) { method += c; } + else { definition += c; } + } + appendResult(); + + return results; +}; + +_html2canvas.Util.Bounds = function (element) { + var clientRect, bounds = {}; + + if (element.getBoundingClientRect){ + clientRect = element.getBoundingClientRect(); + + // TODO add scroll position to bounds, so no scrolling of window necessary + bounds.top = clientRect.top; + bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); + bounds.left = clientRect.left; + + bounds.width = element.offsetWidth; + bounds.height = element.offsetHeight; + } + + return bounds; +}; + +// TODO ideally, we'd want everything to go through this function instead of Util.Bounds, +// but would require further work to calculate the correct positions for elements with offsetParents +_html2canvas.Util.OffsetBounds = function (element) { + var parent = element.offsetParent ? _html2canvas.Util.OffsetBounds(element.offsetParent) : {top: 0, left: 0}; + + return { + top: element.offsetTop + parent.top, + bottom: element.offsetTop + element.offsetHeight + parent.top, + left: element.offsetLeft + parent.left, + width: element.offsetWidth, + height: element.offsetHeight + }; +}; + +function toPX(element, attribute, value ) { + var rsLeft = element.runtimeStyle && element.runtimeStyle[attribute], + left, + style = element.style; + + // Check if we are not dealing with pixels, (Opera has issues with this) + // Ported from jQuery css.js + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + + if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( value ) && /^-?\d/.test(value) ) { + // Remember the original values + left = style.left; + + // Put in the new values to get a computed value out + if (rsLeft) { + element.runtimeStyle.left = element.currentStyle.left; + } + style.left = attribute === "fontSize" ? "1em" : (value || 0); + value = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if (rsLeft) { + element.runtimeStyle.left = rsLeft; + } + } + + if (!/^(thin|medium|thick)$/i.test(value)) { + return Math.round(parseFloat(value)) + "px"; + } + + return value; +} + +function asInt(val) { + return parseInt(val, 10); +} + +function parseBackgroundSizePosition(value, element, attribute, index) { + value = (value || '').split(','); + value = value[index || 0] || value[0] || 'auto'; + value = _html2canvas.Util.trimText(value).split(' '); + + if(attribute === 'backgroundSize' && (!value[0] || value[0].match(/cover|contain|auto/))) { + //these values will be handled in the parent function + } else { + value[0] = (value[0].indexOf( "%" ) === -1) ? toPX(element, attribute + "X", value[0]) : value[0]; + if(value[1] === undefined) { + if(attribute === 'backgroundSize') { + value[1] = 'auto'; + return value; + } else { + // IE 9 doesn't return double digit always + value[1] = value[0]; + } + } + value[1] = (value[1].indexOf("%") === -1) ? toPX(element, attribute + "Y", value[1]) : value[1]; + } + return value; +} + +_html2canvas.Util.getCSS = function (element, attribute, index) { + if (previousElement !== element) { + computedCSS = document.defaultView.getComputedStyle(element, null); + } + + var value = computedCSS[attribute]; + + if (/^background(Size|Position)$/.test(attribute)) { + return parseBackgroundSizePosition(value, element, attribute, index); + } else if (/border(Top|Bottom)(Left|Right)Radius/.test(attribute)) { + var arr = value.split(" "); + if (arr.length <= 1) { + arr[1] = arr[0]; + } + return arr.map(asInt); + } + + return value; +}; + +_html2canvas.Util.resizeBounds = function( current_width, current_height, target_width, target_height, stretch_mode ){ + var target_ratio = target_width / target_height, + current_ratio = current_width / current_height, + output_width, output_height; + + if(!stretch_mode || stretch_mode === 'auto') { + output_width = target_width; + output_height = target_height; + } else if(target_ratio < current_ratio ^ stretch_mode === 'contain') { + output_height = target_height; + output_width = target_height * current_ratio; + } else { + output_width = target_width; + output_height = target_width / current_ratio; + } + + return { + width: output_width, + height: output_height + }; +}; + +function backgroundBoundsFactory( prop, el, bounds, image, imageIndex, backgroundSize ) { + var bgposition = _html2canvas.Util.getCSS( el, prop, imageIndex ) , + topPos, + left, + percentage, + val; + + if (bgposition.length === 1){ + val = bgposition[0]; + + bgposition = []; + + bgposition[0] = val; + bgposition[1] = val; + } + + if (bgposition[0].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[0])/100); + left = bounds.width * percentage; + if(prop !== 'backgroundSize') { + left -= (backgroundSize || image).width*percentage; + } + } else { + if(prop === 'backgroundSize') { + if(bgposition[0] === 'auto') { + left = image.width; + } else { + if (/contain|cover/.test(bgposition[0])) { + var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, bgposition[0]); + left = resized.width; + topPos = resized.height; + } else { + left = parseInt(bgposition[0], 10); + } + } + } else { + left = parseInt( bgposition[0], 10); + } + } + + + if(bgposition[1] === 'auto') { + topPos = left / image.width * image.height; + } else if (bgposition[1].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[1])/100); + topPos = bounds.height * percentage; + if(prop !== 'backgroundSize') { + topPos -= (backgroundSize || image).height * percentage; + } + + } else { + topPos = parseInt(bgposition[1],10); + } + + return [left, topPos]; +} + +_html2canvas.Util.BackgroundPosition = function( el, bounds, image, imageIndex, backgroundSize ) { + var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize ); + return { left: result[0], top: result[1] }; +}; + +_html2canvas.Util.BackgroundSize = function( el, bounds, image, imageIndex ) { + var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex ); + return { width: result[0], height: result[1] }; +}; + +_html2canvas.Util.Extend = function (options, defaults) { + for (var key in options) { + if (options.hasOwnProperty(key)) { + defaults[key] = options[key]; + } + } + return defaults; +}; + + +/* + * Derived from jQuery.contents() + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ +_html2canvas.Util.Children = function( elem ) { + var children; + try { + children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? elem.contentDocument || elem.contentWindow.document : (function(array) { + var ret = []; + if (array !== null) { + (function(first, second ) { + var i = first.length, + j = 0; + + if (typeof second.length === "number") { + for (var l = second.length; j < l; j++) { + first[i++] = second[j]; + } + } else { + while (second[j] !== undefined) { + first[i++] = second[j++]; + } + } + + first.length = i; + + return first; + })(ret, array); + } + return ret; + })(elem.childNodes); + + } catch (ex) { + _html2canvas.Util.log("html2canvas.Util.Children failed with exception: " + ex.message); + children = []; + } + return children; +}; + +_html2canvas.Util.isTransparent = function(backgroundColor) { + return (backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)"); +}; +_html2canvas.Util.Font = (function () { + + var fontData = {}; + + return function(font, fontSize, doc) { + if (fontData[font + "-" + fontSize] !== undefined) { + return fontData[font + "-" + fontSize]; + } + + var container = doc.createElement('div'), + img = doc.createElement('img'), + span = doc.createElement('span'), + sampleText = 'Hidden Text', + baseline, + middle, + metricsObj; + + container.style.visibility = "hidden"; + container.style.fontFamily = font; + container.style.fontSize = fontSize; + container.style.margin = 0; + container.style.padding = 0; + + doc.body.appendChild(container); + + // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) + img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; + img.width = 1; + img.height = 1; + + img.style.margin = 0; + img.style.padding = 0; + img.style.verticalAlign = "baseline"; + + span.style.fontFamily = font; + span.style.fontSize = fontSize; + span.style.margin = 0; + span.style.padding = 0; + + span.appendChild(doc.createTextNode(sampleText)); + container.appendChild(span); + container.appendChild(img); + baseline = (img.offsetTop - span.offsetTop) + 1; + + container.removeChild(span); + container.appendChild(doc.createTextNode(sampleText)); + + container.style.lineHeight = "normal"; + img.style.verticalAlign = "super"; + + middle = (img.offsetTop-container.offsetTop) + 1; + metricsObj = { + baseline: baseline, + lineWidth: 1, + middle: middle + }; + + fontData[font + "-" + fontSize] = metricsObj; + + doc.body.removeChild(container); + + return metricsObj; + }; +})(); + +(function(){ + var Util = _html2canvas.Util, + Generate = {}; + + _html2canvas.Generate = Generate; + + var reGradients = [ + /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)\-]+)\)$/, + /^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/, + /^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z\-]*)([\w\d\.\s,%\(\)]+)\)$/, + /^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/ + ]; + + /* + * TODO: Add IE10 vendor prefix (-ms) support + * TODO: Add W3C gradient (linear-gradient) support + * TODO: Add old Webkit -webkit-gradient(radial, ...) support + * TODO: Maybe some RegExp optimizations are possible ;o) + */ + Generate.parseGradient = function(css, bounds) { + var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl; + + for(i = 0; i < len; i+=1){ + m1 = css.match(reGradients[i]); + if(m1) { + break; + } + } + + if(m1) { + switch(m1[1]) { + case '-webkit-linear-gradient': + case '-o-linear-gradient': + + gradient = { + type: 'linear', + x0: null, + y0: null, + x1: null, + y1: null, + colorStops: [] + }; + + // get coordinates + m2 = m1[2].match(/\w+/g); + if(m2){ + m2Len = m2.length; + for(i = 0; i < m2Len; i+=1){ + switch(m2[i]) { + case 'top': + gradient.y0 = 0; + gradient.y1 = bounds.height; + break; + + case 'right': + gradient.x0 = bounds.width; + gradient.x1 = 0; + break; + + case 'bottom': + gradient.y0 = bounds.height; + gradient.y1 = 0; + break; + + case 'left': + gradient.x0 = 0; + gradient.x1 = bounds.width; + break; + } + } + } + if(gradient.x0 === null && gradient.x1 === null){ // center + gradient.x0 = gradient.x1 = bounds.width / 2; + } + if(gradient.y0 === null && gradient.y1 === null){ // center + gradient.y0 = gradient.y1 = bounds.height / 2; + } + + // get colors and stops + m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3] === '%'){ + stop /= 100; + } else { // px - stupid opera + stop /= bounds.width; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + + case '-webkit-gradient': + + gradient = { + type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions + x0: 0, + y0: 0, + x1: 0, + y1: 0, + colorStops: [] + }; + + // get coordinates + m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/); + if(m2){ + gradient.x0 = (m2[1] * bounds.width) / 100; + gradient.y0 = (m2[2] * bounds.height) / 100; + gradient.x1 = (m2[3] * bounds.width) / 100; + gradient.y1 = (m2[4] * bounds.height) / 100; + } + + // get colors and stops + m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g); + if(m2){ + m2Len = m2.length; + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/); + stop = parseFloat(m3[2]); + if(m3[1] === 'from') { + stop = 0.0; + } + if(m3[1] === 'to') { + stop = 1.0; + } + gradient.colorStops.push({ + color: m3[3], + stop: stop + }); + } + } + break; + + case '-moz-linear-gradient': + + gradient = { + type: 'linear', + x0: 0, + y0: 0, + x1: 0, + y1: 0, + colorStops: [] + }; + + // get coordinates + m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); + + // m2[1] == 0% -> left + // m2[1] == 50% -> center + // m2[1] == 100% -> right + + // m2[2] == 0% -> top + // m2[2] == 50% -> center + // m2[2] == 100% -> bottom + + if(m2){ + gradient.x0 = (m2[1] * bounds.width) / 100; + gradient.y0 = (m2[2] * bounds.height) / 100; + gradient.x1 = bounds.width - gradient.x0; + gradient.y1 = bounds.height - gradient.y0; + } + + // get colors and stops + m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3]){ // percentage + stop /= 100; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + + case '-webkit-radial-gradient': + case '-moz-radial-gradient': + case '-o-radial-gradient': + + gradient = { + type: 'circle', + x0: 0, + y0: 0, + x1: bounds.width, + y1: bounds.height, + cx: 0, + cy: 0, + rx: 0, + ry: 0, + colorStops: [] + }; + + // center + m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); + if(m2){ + gradient.cx = (m2[1] * bounds.width) / 100; + gradient.cy = (m2[2] * bounds.height) / 100; + } + + // size + m2 = m1[3].match(/\w+/); + m3 = m1[4].match(/[a-z\-]*/); + if(m2 && m3){ + switch(m3[0]){ + case 'farthest-corner': + case 'cover': // is equivalent to farthest-corner + case '': // mozilla removes "cover" from definition :( + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.max(tl, tr, br, bl); + break; + case 'closest-corner': + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.min(tl, tr, br, bl); + break; + case 'farthest-side': + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.max( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // ellipse + + gradient.type = m2[0]; + + gradient.rx = Math.max( + gradient.cx, + gradient.x1 - gradient.cx + ); + gradient.ry = Math.max( + gradient.cy, + gradient.y1 - gradient.cy + ); + } + break; + case 'closest-side': + case 'contain': // is equivalent to closest-side + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.min( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // ellipse + + gradient.type = m2[0]; + + gradient.rx = Math.min( + gradient.cx, + gradient.x1 - gradient.cx + ); + gradient.ry = Math.min( + gradient.cy, + gradient.y1 - gradient.cy + ); + } + break; + + // TODO: add support for "30px 40px" sizes (webkit only) + } + } + + // color stops + m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3] === '%'){ + stop /= 100; + } else { // px - stupid opera + stop /= bounds.width; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + } + } + + return gradient; + }; + + function addScrollStops(grad) { + return function(colorStop) { + try { + grad.addColorStop(colorStop.stop, colorStop.color); + } + catch(e) { + Util.log(['failed to add color stop: ', e, '; tried to add: ', colorStop]); + } + }; + } + + Generate.Gradient = function(src, bounds) { + if(bounds.width === 0 || bounds.height === 0) { + return; + } + + var canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'), + gradient, grad; + + canvas.width = bounds.width; + canvas.height = bounds.height; + + // TODO: add support for multi defined background gradients + gradient = _html2canvas.Generate.parseGradient(src, bounds); + + if(gradient) { + switch(gradient.type) { + case 'linear': + grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); + gradient.colorStops.forEach(addScrollStops(grad)); + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + break; + + case 'circle': + grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); + gradient.colorStops.forEach(addScrollStops(grad)); + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + break; + + case 'ellipse': + var canvasRadial = document.createElement('canvas'), + ctxRadial = canvasRadial.getContext('2d'), + ri = Math.max(gradient.rx, gradient.ry), + di = ri * 2; + + canvasRadial.width = canvasRadial.height = di; + + grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); + gradient.colorStops.forEach(addScrollStops(grad)); + + ctxRadial.fillStyle = grad; + ctxRadial.fillRect(0, 0, di, di); + + ctx.fillStyle = gradient.colorStops[gradient.colorStops.length - 1].color; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); + break; + } + } + + return canvas; + }; + + Generate.ListAlpha = function(number) { + var tmp = "", + modulus; + + do { + modulus = number % 26; + tmp = String.fromCharCode((modulus) + 64) + tmp; + number = number / 26; + }while((number*26) > 26); + + return tmp; + }; + + Generate.ListRoman = function(number) { + var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"], + decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], + roman = "", + v, + len = romanArray.length; + + if (number <= 0 || number >= 4000) { + return number; + } + + for (v=0; v < len; v+=1) { + while (number >= decimal[v]) { + number -= decimal[v]; + roman += romanArray[v]; + } + } + + return roman; + }; +})(); +function h2cRenderContext(width, height) { + var storage = []; + return { + storage: storage, + width: width, + height: height, + clip: function() { + storage.push({ + type: "function", + name: "clip", + 'arguments': arguments + }); + }, + translate: function() { + storage.push({ + type: "function", + name: "translate", + 'arguments': arguments + }); + }, + fill: function() { + storage.push({ + type: "function", + name: "fill", + 'arguments': arguments + }); + }, + save: function() { + storage.push({ + type: "function", + name: "save", + 'arguments': arguments + }); + }, + restore: function() { + storage.push({ + type: "function", + name: "restore", + 'arguments': arguments + }); + }, + fillRect: function () { + storage.push({ + type: "function", + name: "fillRect", + 'arguments': arguments + }); + }, + createPattern: function() { + storage.push({ + type: "function", + name: "createPattern", + 'arguments': arguments + }); + }, + drawShape: function() { + + var shape = []; + + storage.push({ + type: "function", + name: "drawShape", + 'arguments': shape + }); + + return { + moveTo: function() { + shape.push({ + name: "moveTo", + 'arguments': arguments + }); + }, + lineTo: function() { + shape.push({ + name: "lineTo", + 'arguments': arguments + }); + }, + arcTo: function() { + shape.push({ + name: "arcTo", + 'arguments': arguments + }); + }, + bezierCurveTo: function() { + shape.push({ + name: "bezierCurveTo", + 'arguments': arguments + }); + }, + quadraticCurveTo: function() { + shape.push({ + name: "quadraticCurveTo", + 'arguments': arguments + }); + } + }; + + }, + drawImage: function () { + storage.push({ + type: "function", + name: "drawImage", + 'arguments': arguments + }); + }, + fillText: function () { + storage.push({ + type: "function", + name: "fillText", + 'arguments': arguments + }); + }, + setVariable: function (variable, value) { + storage.push({ + type: "variable", + name: variable, + 'arguments': value + }); + return value; + } + }; +} +_html2canvas.Parse = function (images, options) { + window.scroll(0,0); + + var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default + numDraws = 0, + doc = element.ownerDocument, + Util = _html2canvas.Util, + support = Util.Support(options, doc), + ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), + body = doc.body, + getCSS = Util.getCSS, + pseudoHide = "___html2canvas___pseudoelement", + hidePseudoElements = doc.createElement('style'); + + hidePseudoElements.innerHTML = '.' + pseudoHide + '-before:before { content: "" !important; display: none !important; }' + + '.' + pseudoHide + '-after:after { content: "" !important; display: none !important; }'; + + body.appendChild(hidePseudoElements); + + images = images || {}; + + function documentWidth () { + return Math.max( + Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), + Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), + Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) + ); + } + + function documentHeight () { + return Math.max( + Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), + Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), + Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) + ); + } + + function getCSSInt(element, attribute) { + var val = parseInt(getCSS(element, attribute), 10); + return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html + } + + function renderRect (ctx, x, y, w, h, bgcolor) { + if (bgcolor !== "transparent"){ + ctx.setVariable("fillStyle", bgcolor); + ctx.fillRect(x, y, w, h); + numDraws+=1; + } + } + + function capitalize(m, p1, p2) { + if (m.length > 0) { + return p1 + p2.toUpperCase(); + } + } + + function textTransform (text, transform) { + switch(transform){ + case "lowercase": + return text.toLowerCase(); + case "capitalize": + return text.replace( /(^|\s|:|-|\(|\))([a-z])/g, capitalize); + case "uppercase": + return text.toUpperCase(); + default: + return text; + } + } + + function noLetterSpacing(letter_spacing) { + return (/^(normal|none|0px)$/.test(letter_spacing)); + } + + function drawText(currentText, x, y, ctx){ + if (currentText !== null && Util.trimText(currentText).length > 0) { + ctx.fillText(currentText, x, y); + numDraws+=1; + } + } + + function setTextVariables(ctx, el, text_decoration, color) { + var align = false, + bold = getCSS(el, "fontWeight"), + family = getCSS(el, "fontFamily"), + size = getCSS(el, "fontSize"), + shadows = Util.parseTextShadows(getCSS(el, "textShadow")); + + switch(parseInt(bold, 10)){ + case 401: + bold = "bold"; + break; + case 400: + bold = "normal"; + break; + } + + ctx.setVariable("fillStyle", color); + ctx.setVariable("font", [getCSS(el, "fontStyle"), getCSS(el, "fontVariant"), bold, size, family].join(" ")); + ctx.setVariable("textAlign", (align) ? "right" : "left"); + + if (shadows.length) { + // TODO: support multiple text shadows + // apply the first text shadow + ctx.setVariable("shadowColor", shadows[0].color); + ctx.setVariable("shadowOffsetX", shadows[0].offsetX); + ctx.setVariable("shadowOffsetY", shadows[0].offsetY); + ctx.setVariable("shadowBlur", shadows[0].blur); + } + + if (text_decoration !== "none"){ + return Util.Font(family, size, doc); + } + } + + function renderTextDecoration(ctx, text_decoration, bounds, metrics, color) { + switch(text_decoration) { + case "underline": + // Draws a line at the baseline of the font + // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size + renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); + break; + case "overline": + renderRect(ctx, bounds.left, Math.round(bounds.top), bounds.width, 1, color); + break; + case "line-through": + // TODO try and find exact position for line-through + renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); + break; + } + } + + function getTextBounds(state, text, textDecoration, isLast, transform) { + var bounds; + if (support.rangeBounds && !transform) { + if (textDecoration !== "none" || Util.trimText(text).length !== 0) { + bounds = textRangeBounds(text, state.node, state.textOffset); + } + state.textOffset += text.length; + } else if (state.node && typeof state.node.nodeValue === "string" ){ + var newTextNode = (isLast) ? state.node.splitText(text.length) : null; + bounds = textWrapperBounds(state.node, transform); + state.node = newTextNode; + } + return bounds; + } + + function textRangeBounds(text, textNode, textOffset) { + var range = doc.createRange(); + range.setStart(textNode, textOffset); + range.setEnd(textNode, textOffset + text.length); + return range.getBoundingClientRect(); + } + + function textWrapperBounds(oldTextNode, transform) { + var parent = oldTextNode.parentNode, + wrapElement = doc.createElement('wrapper'), + backupText = oldTextNode.cloneNode(true); + + wrapElement.appendChild(oldTextNode.cloneNode(true)); + parent.replaceChild(wrapElement, oldTextNode); + + var bounds = transform ? Util.OffsetBounds(wrapElement) : Util.Bounds(wrapElement); + parent.replaceChild(backupText, wrapElement); + return bounds; + } + + function renderText(el, textNode, stack) { + var ctx = stack.ctx, + color = getCSS(el, "color"), + textDecoration = getCSS(el, "textDecoration"), + textAlign = getCSS(el, "textAlign"), + metrics, + textList, + state = { + node: textNode, + textOffset: 0 + }; + + if (Util.trimText(textNode.nodeValue).length > 0) { + textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); + textAlign = textAlign.replace(["-webkit-auto"],["auto"]); + + textList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(textAlign) && noLetterSpacing(getCSS(el, "letterSpacing"))) ? + textNode.nodeValue.split(/(\b| )/) + : textNode.nodeValue.split(""); + + metrics = setTextVariables(ctx, el, textDecoration, color); + + if (options.chinese) { + textList.forEach(function(word, index) { + if (/.*[\u4E00-\u9FA5].*$/.test(word)) { + word = word.split(""); + word.unshift(index, 1); + textList.splice.apply(textList, word); + } + }); + } + + textList.forEach(function(text, index) { + var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix); + if (bounds) { + drawText(text, bounds.left, bounds.bottom, ctx); + renderTextDecoration(ctx, textDecoration, bounds, metrics, color); + } + }); + } + } + + function listPosition (element, val) { + var boundElement = doc.createElement( "boundelement" ), + originalType, + bounds; + + boundElement.style.display = "inline"; + + originalType = element.style.listStyleType; + element.style.listStyleType = "none"; + + boundElement.appendChild(doc.createTextNode(val)); + + element.insertBefore(boundElement, element.firstChild); + + bounds = Util.Bounds(boundElement); + element.removeChild(boundElement); + element.style.listStyleType = originalType; + return bounds; + } + + function elementIndex(el) { + var i = -1, + count = 1, + childs = el.parentNode.childNodes; + + if (el.parentNode) { + while(childs[++i] !== el) { + if (childs[i].nodeType === 1) { + count++; + } + } + return count; + } else { + return -1; + } + } + + function listItemText(element, type) { + var currentIndex = elementIndex(element), text; + switch(type){ + case "decimal": + text = currentIndex; + break; + case "decimal-leading-zero": + text = (currentIndex.toString().length === 1) ? currentIndex = "0" + currentIndex.toString() : currentIndex.toString(); + break; + case "upper-roman": + text = _html2canvas.Generate.ListRoman( currentIndex ); + break; + case "lower-roman": + text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase(); + break; + case "lower-alpha": + text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase(); + break; + case "upper-alpha": + text = _html2canvas.Generate.ListAlpha( currentIndex ); + break; + } + + return text + ". "; + } + + function renderListItem(element, stack, elBounds) { + var x, + text, + ctx = stack.ctx, + type = getCSS(element, "listStyleType"), + listBounds; + + if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { + text = listItemText(element, type); + listBounds = listPosition(element, text); + setTextVariables(ctx, element, "none", getCSS(element, "color")); + + if (getCSS(element, "listStylePosition") === "inside") { + ctx.setVariable("textAlign", "left"); + x = elBounds.left; + } else { + return; + } + + drawText(text, x, listBounds.bottom, ctx); + } + } + + function loadImage (src){ + var img = images[src]; + return (img && img.succeeded === true) ? img.img : false; + } + + function clipBounds(src, dst){ + var x = Math.max(src.left, dst.left), + y = Math.max(src.top, dst.top), + x2 = Math.min((src.left + src.width), (dst.left + dst.width)), + y2 = Math.min((src.top + src.height), (dst.top + dst.height)); + + return { + left:x, + top:y, + width:x2-x, + height:y2-y + }; + } + + function setZ(element, stack, parentStack){ + var newContext, + isPositioned = stack.cssPosition !== 'static', + zIndex = isPositioned ? getCSS(element, 'zIndex') : 'auto', + opacity = getCSS(element, 'opacity'), + isFloated = getCSS(element, 'cssFloat') !== 'none'; + + // https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context + // When a new stacking context should be created: + // the root element (HTML), + // positioned (absolutely or relatively) with a z-index value other than "auto", + // elements with an opacity value less than 1. (See the specification for opacity), + // on mobile WebKit and Chrome 22+, position: fixed always creates a new stacking context, even when z-index is "auto" (See this post) + + stack.zIndex = newContext = h2czContext(zIndex); + newContext.isPositioned = isPositioned; + newContext.isFloated = isFloated; + newContext.opacity = opacity; + newContext.ownStacking = (zIndex !== 'auto' || opacity < 1); + + if (parentStack) { + parentStack.zIndex.children.push(stack); + } + } + + function renderImage(ctx, element, image, bounds, borders) { + + var paddingLeft = getCSSInt(element, 'paddingLeft'), + paddingTop = getCSSInt(element, 'paddingTop'), + paddingRight = getCSSInt(element, 'paddingRight'), + paddingBottom = getCSSInt(element, 'paddingBottom'); + + drawImage( + ctx, + image, + 0, //sx + 0, //sy + image.width, //sw + image.height, //sh + bounds.left + paddingLeft + borders[3].width, //dx + bounds.top + paddingTop + borders[0].width, // dy + bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw + bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh + ); + } + + function getBorderData(element) { + return ["Top", "Right", "Bottom", "Left"].map(function(side) { + return { + width: getCSSInt(element, 'border' + side + 'Width'), + color: getCSS(element, 'border' + side + 'Color') + }; + }); + } + + function getBorderRadiusData(element) { + return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) { + return getCSS(element, 'border' + side + 'Radius'); + }); + } + + var getCurvePoints = (function(kappa) { + + return function(x, y, r1, r2) { + var ox = (r1) * kappa, // control point offset horizontal + oy = (r2) * kappa, // control point offset vertical + xm = x + r1, // x-middle + ym = y + r2; // y-middle + return { + topLeft: bezierCurve({ + x:x, + y:ym + }, { + x:x, + y:ym - oy + }, { + x:xm - ox, + y:y + }, { + x:xm, + y:y + }), + topRight: bezierCurve({ + x:x, + y:y + }, { + x:x + ox, + y:y + }, { + x:xm, + y:ym - oy + }, { + x:xm, + y:ym + }), + bottomRight: bezierCurve({ + x:xm, + y:y + }, { + x:xm, + y:y + oy + }, { + x:x + ox, + y:ym + }, { + x:x, + y:ym + }), + bottomLeft: bezierCurve({ + x:xm, + y:ym + }, { + x:xm - ox, + y:ym + }, { + x:x, + y:y + oy + }, { + x:x, + y:y + }) + }; + }; + })(4 * ((Math.sqrt(2) - 1) / 3)); + + function bezierCurve(start, startControl, endControl, end) { + + var lerp = function (a, b, t) { + return { + x:a.x + (b.x - a.x) * t, + y:a.y + (b.y - a.y) * t + }; + }; + + return { + start: start, + startControl: startControl, + endControl: endControl, + end: end, + subdivide: function(t) { + var ab = lerp(start, startControl, t), + bc = lerp(startControl, endControl, t), + cd = lerp(endControl, end, t), + abbc = lerp(ab, bc, t), + bccd = lerp(bc, cd, t), + dest = lerp(abbc, bccd, t); + return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)]; + }, + curveTo: function(borderArgs) { + borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]); + }, + curveToReversed: function(borderArgs) { + borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]); + } + }; + } + + function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) { + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]); + corner1[0].curveTo(borderArgs); + corner1[1].curveTo(borderArgs); + } else { + borderArgs.push(["line", x, y]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]); + } + } + + function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) { + var borderArgs = []; + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]); + outer1[1].curveTo(borderArgs); + } else { + borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]); + outer2[0].curveTo(borderArgs); + borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]); + inner2[0].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c2[0], borderData.c2[1]]); + borderArgs.push([ "line", borderData.c3[0], borderData.c3[1]]); + } + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]); + inner1[1].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c4[0], borderData.c4[1]]); + } + + return borderArgs; + } + + function calculateCurvePoints(bounds, borderRadius, borders) { + + var x = bounds.left, + y = bounds.top, + width = bounds.width, + height = bounds.height, + + tlh = borderRadius[0][0], + tlv = borderRadius[0][1], + trh = borderRadius[1][0], + trv = borderRadius[1][1], + brh = borderRadius[2][0], + brv = borderRadius[2][1], + blh = borderRadius[3][0], + blv = borderRadius[3][1], + + topWidth = width - trh, + rightHeight = height - brv, + bottomWidth = width - brh, + leftHeight = height - blv; + + return { + topLeftOuter: getCurvePoints( + x, + y, + tlh, + tlv + ).topLeft.subdivide(0.5), + + topLeftInner: getCurvePoints( + x + borders[3].width, + y + borders[0].width, + Math.max(0, tlh - borders[3].width), + Math.max(0, tlv - borders[0].width) + ).topLeft.subdivide(0.5), + + topRightOuter: getCurvePoints( + x + topWidth, + y, + trh, + trv + ).topRight.subdivide(0.5), + + topRightInner: getCurvePoints( + x + Math.min(topWidth, width + borders[3].width), + y + borders[0].width, + (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, + trv - borders[0].width + ).topRight.subdivide(0.5), + + bottomRightOuter: getCurvePoints( + x + bottomWidth, + y + rightHeight, + brh, + brv + ).bottomRight.subdivide(0.5), + + bottomRightInner: getCurvePoints( + x + Math.min(bottomWidth, width + borders[3].width), + y + Math.min(rightHeight, height + borders[0].width), + Math.max(0, brh - borders[1].width), + Math.max(0, brv - borders[2].width) + ).bottomRight.subdivide(0.5), + + bottomLeftOuter: getCurvePoints( + x, + y + leftHeight, + blh, + blv + ).bottomLeft.subdivide(0.5), + + bottomLeftInner: getCurvePoints( + x + borders[3].width, + y + leftHeight, + Math.max(0, blh - borders[3].width), + Math.max(0, blv - borders[2].width) + ).bottomLeft.subdivide(0.5) + }; + } + + function getBorderClip(element, borderPoints, borders, radius, bounds) { + var backgroundClip = getCSS(element, 'backgroundClip'), + borderArgs = []; + + switch(backgroundClip) { + case "content-box": + case "padding-box": + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width); + break; + + default: + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height); + break; + } + + return borderArgs; + } + + function parseBorders(element, bounds, borders){ + var x = bounds.left, + y = bounds.top, + width = bounds.width, + height = bounds.height, + borderSide, + bx, + by, + bw, + bh, + borderArgs, + // http://www.w3.org/TR/css3-background/#the-border-radius + borderRadius = getBorderRadiusData(element), + borderPoints = calculateCurvePoints(bounds, borderRadius, borders), + borderData = { + clip: getBorderClip(element, borderPoints, borders, borderRadius, bounds), + borders: [] + }; + + for (borderSide = 0; borderSide < 4; borderSide++) { + + if (borders[borderSide].width > 0) { + bx = x; + by = y; + bw = width; + bh = height - (borders[2].width); + + switch(borderSide) { + case 0: + // top border + bh = borders[0].width; + + borderArgs = drawSide({ + c1: [bx, by], + c2: [bx + bw, by], + c3: [bx + bw - borders[1].width, by + bh], + c4: [bx + borders[3].width, by + bh] + }, borderRadius[0], borderRadius[1], + borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner); + break; + case 1: + // right border + bx = x + width - (borders[1].width); + bw = borders[1].width; + + borderArgs = drawSide({ + c1: [bx + bw, by], + c2: [bx + bw, by + bh + borders[2].width], + c3: [bx, by + bh], + c4: [bx, by + borders[0].width] + }, borderRadius[1], borderRadius[2], + borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner); + break; + case 2: + // bottom border + by = (by + height) - (borders[2].width); + bh = borders[2].width; + + borderArgs = drawSide({ + c1: [bx + bw, by + bh], + c2: [bx, by + bh], + c3: [bx + borders[3].width, by], + c4: [bx + bw - borders[3].width, by] + }, borderRadius[2], borderRadius[3], + borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner); + break; + case 3: + // left border + bw = borders[3].width; + + borderArgs = drawSide({ + c1: [bx, by + bh + borders[2].width], + c2: [bx, by], + c3: [bx + bw, by + borders[0].width], + c4: [bx + bw, by + bh] + }, borderRadius[3], borderRadius[0], + borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner); + break; + } + + borderData.borders.push({ + args: borderArgs, + color: borders[borderSide].color + }); + + } + } + + return borderData; + } + + function createShape(ctx, args) { + var shape = ctx.drawShape(); + args.forEach(function(border, index) { + shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1)); + }); + return shape; + } + + function renderBorders(ctx, borderArgs, color) { + if (color !== "transparent") { + ctx.setVariable( "fillStyle", color); + createShape(ctx, borderArgs); + ctx.fill(); + numDraws+=1; + } + } + + function renderFormValue (el, bounds, stack){ + + var valueWrap = doc.createElement('valuewrap'), + cssPropertyArray = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], + textValue, + textNode; + + cssPropertyArray.forEach(function(property) { + try { + valueWrap.style[property] = getCSS(el, property); + } catch(e) { + // Older IE has issues with "border" + Util.log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); + } + }); + + valueWrap.style.borderColor = "black"; + valueWrap.style.borderStyle = "solid"; + valueWrap.style.display = "block"; + valueWrap.style.position = "absolute"; + + if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){ + valueWrap.style.lineHeight = getCSS(el, "height"); + } + + valueWrap.style.top = bounds.top + "px"; + valueWrap.style.left = bounds.left + "px"; + + textValue = (el.nodeName === "SELECT") ? (el.options[el.selectedIndex] || 0).text : el.value; + if(!textValue) { + textValue = el.placeholder; + } + + textNode = doc.createTextNode(textValue); + + valueWrap.appendChild(textNode); + body.appendChild(valueWrap); + + renderText(el, textNode, stack); + body.removeChild(valueWrap); + } + + function drawImage (ctx) { + ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1)); + numDraws+=1; + } + + function getPseudoElement(el, which) { + var elStyle = window.getComputedStyle(el, which); + if(!elStyle || !elStyle.content || elStyle.content === "none" || elStyle.content === "-moz-alt-content" || elStyle.display === "none") { + return; + } + var content = elStyle.content + '', + first = content.substr( 0, 1 ); + //strips quotes + if(first === content.substr( content.length - 1 ) && first.match(/'|"/)) { + content = content.substr( 1, content.length - 2 ); + } + + var isImage = content.substr( 0, 3 ) === 'url', + elps = document.createElement( isImage ? 'img' : 'span' ); + + elps.className = pseudoHide + "-before " + pseudoHide + "-after"; + + Object.keys(elStyle).filter(indexedProperty).forEach(function(prop) { + // Prevent assigning of read only CSS Rules, ex. length, parentRule + try { + elps.style[prop] = elStyle[prop]; + } catch (e) { + Util.log(['Tried to assign readonly property ', prop, 'Error:', e]); + } + }); + + if(isImage) { + elps.src = Util.parseBackgroundImage(content)[0].args[0]; + } else { + elps.innerHTML = content; + } + return elps; + } + + function indexedProperty(property) { + return (isNaN(window.parseInt(property, 10))); + } + + function injectPseudoElements(el, stack) { + var before = getPseudoElement(el, ':before'), + after = getPseudoElement(el, ':after'); + if(!before && !after) { + return; + } + + if(before) { + el.className += " " + pseudoHide + "-before"; + el.parentNode.insertBefore(before, el); + parseElement(before, stack, true); + el.parentNode.removeChild(before); + el.className = el.className.replace(pseudoHide + "-before", "").trim(); + } + + if (after) { + el.className += " " + pseudoHide + "-after"; + el.appendChild(after); + parseElement(after, stack, true); + el.removeChild(after); + el.className = el.className.replace(pseudoHide + "-after", "").trim(); + } + + } + + function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) { + var offsetX = Math.round(bounds.left + backgroundPosition.left), + offsetY = Math.round(bounds.top + backgroundPosition.top); + + ctx.createPattern(image); + ctx.translate(offsetX, offsetY); + ctx.fill(); + ctx.translate(-offsetX, -offsetY); + } + + function backgroundRepeatShape(ctx, image, backgroundPosition, bounds, left, top, width, height) { + var args = []; + args.push(["line", Math.round(left), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(height + top)]); + args.push(["line", Math.round(left), Math.round(height + top)]); + createShape(ctx, args); + ctx.save(); + ctx.clip(); + renderBackgroundRepeat(ctx, image, backgroundPosition, bounds); + ctx.restore(); + } + + function renderBackgroundColor(ctx, backgroundBounds, bgcolor) { + renderRect( + ctx, + backgroundBounds.left, + backgroundBounds.top, + backgroundBounds.width, + backgroundBounds.height, + bgcolor + ); + } + + function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) { + var backgroundSize = Util.BackgroundSize(el, bounds, image, imageIndex), + backgroundPosition = Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize), + backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(Util.trimText); + + image = resizeImage(image, backgroundSize); + + backgroundRepeat = backgroundRepeat[imageIndex] || backgroundRepeat[0]; + + switch (backgroundRepeat) { + case "repeat-x": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left, bounds.top + backgroundPosition.top, 99999, image.height); + break; + + case "repeat-y": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top, image.width, 99999); + break; + + case "no-repeat": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height); + break; + + default: + renderBackgroundRepeat(ctx, image, backgroundPosition, { + top: bounds.top, + left: bounds.left, + width: image.width, + height: image.height + }); + break; + } + } + + function renderBackgroundImage(element, bounds, ctx) { + var backgroundImage = getCSS(element, "backgroundImage"), + backgroundImages = Util.parseBackgroundImage(backgroundImage), + image, + imageIndex = backgroundImages.length; + + while(imageIndex--) { + backgroundImage = backgroundImages[imageIndex]; + + if (!backgroundImage.args || backgroundImage.args.length === 0) { + continue; + } + + var key = backgroundImage.method === 'url' ? + backgroundImage.args[0] : + backgroundImage.value; + + image = loadImage(key); + + // TODO add support for background-origin + if (image) { + renderBackgroundRepeating(element, bounds, ctx, image, imageIndex); + } else { + Util.log("html2canvas: Error loading background:", backgroundImage); + } + } + } + + function resizeImage(image, bounds) { + if(image.width === bounds.width && image.height === bounds.height) { + return image; + } + + var ctx, canvas = doc.createElement('canvas'); + canvas.width = bounds.width; + canvas.height = bounds.height; + ctx = canvas.getContext("2d"); + drawImage(ctx, image, 0, 0, image.width, image.height, 0, 0, bounds.width, bounds.height ); + return canvas; + } + + function setOpacity(ctx, element, parentStack) { + return ctx.setVariable("globalAlpha", getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1)); + } + + function removePx(str) { + return str.replace("px", ""); + } + + var transformRegExp = /(matrix)\((.+)\)/; + + function getTransform(element, parentStack) { + var transform = getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform"); + var transformOrigin = getCSS(element, "transform-origin") || getCSS(element, "-webkit-transform-origin") || getCSS(element, "-moz-transform-origin") || getCSS(element, "-ms-transform-origin") || getCSS(element, "-o-transform-origin") || "0px 0px"; + + transformOrigin = transformOrigin.split(" ").map(removePx).map(Util.asFloat); + + var matrix; + if (transform && transform !== "none") { + var match = transform.match(transformRegExp); + if (match) { + switch(match[1]) { + case "matrix": + matrix = match[2].split(",").map(Util.trimText).map(Util.asFloat); + break; + } + } + } + + return { + origin: transformOrigin, + matrix: matrix + }; + } + + function createStack(element, parentStack, bounds, transform) { + var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), + stack = { + ctx: ctx, + opacity: setOpacity(ctx, element, parentStack), + cssPosition: getCSS(element, "position"), + borders: getBorderData(element), + transform: transform, + clip: (parentStack && parentStack.clip) ? Util.Extend( {}, parentStack.clip ) : null + }; + + setZ(element, stack, parentStack); + + // TODO correct overflow for absolute content residing under a static position + if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(element, "overflow")) === true && /(BODY)/i.test(element.nodeName) === false){ + stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds; + } + + return stack; + } + + function getBackgroundBounds(borders, bounds, clip) { + var backgroundBounds = { + left: bounds.left + borders[3].width, + top: bounds.top + borders[0].width, + width: bounds.width - (borders[1].width + borders[3].width), + height: bounds.height - (borders[0].width + borders[2].width) + }; + + if (clip) { + backgroundBounds = clipBounds(backgroundBounds, clip); + } + + return backgroundBounds; + } + + function getBounds(element, transform) { + var bounds = (transform.matrix) ? Util.OffsetBounds(element) : Util.Bounds(element); + transform.origin[0] += bounds.left; + transform.origin[1] += bounds.top; + return bounds; + } + + function renderElement(element, parentStack, pseudoElement, ignoreBackground) { + var transform = getTransform(element, parentStack), + bounds = getBounds(element, transform), + image, + stack = createStack(element, parentStack, bounds, transform), + borders = stack.borders, + ctx = stack.ctx, + backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip), + borderData = parseBorders(element, bounds, borders), + backgroundColor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"); + + + createShape(ctx, borderData.clip); + + ctx.save(); + ctx.clip(); + + if (backgroundBounds.height > 0 && backgroundBounds.width > 0 && !ignoreBackground) { + renderBackgroundColor(ctx, bounds, backgroundColor); + renderBackgroundImage(element, backgroundBounds, ctx); + } else if (ignoreBackground) { + stack.backgroundColor = backgroundColor; + } + + ctx.restore(); + + borderData.borders.forEach(function(border) { + renderBorders(ctx, border.args, border.color); + }); + + if (!pseudoElement) { + injectPseudoElements(element, stack); + } + + switch(element.nodeName){ + case "IMG": + if ((image = loadImage(element.getAttribute('src')))) { + renderImage(ctx, element, image, bounds, borders); + } else { + Util.log("html2canvas: Error loading :" + element.getAttribute('src')); + } + break; + case "INPUT": + // TODO add all relevant type's, i.e. HTML5 new stuff + // todo add support for placeholder attribute for browsers which support it + if (/^(text|url|email|submit|button|reset)$/.test(element.type) && (element.value || element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "TEXTAREA": + if ((element.value || element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "SELECT": + if ((element.options||element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "LI": + renderListItem(element, stack, backgroundBounds); + break; + case "CANVAS": + renderImage(ctx, element, element, bounds, borders); + break; + } + + return stack; + } + + function isElementVisible(element) { + return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore")); + } + + function parseElement (element, stack, pseudoElement) { + if (isElementVisible(element)) { + stack = renderElement(element, stack, pseudoElement, false) || stack; + if (!ignoreElementsRegExp.test(element.nodeName)) { + parseChildren(element, stack, pseudoElement); + } + } + } + + function parseChildren(element, stack, pseudoElement) { + Util.Children(element).forEach(function(node) { + if (node.nodeType === node.ELEMENT_NODE) { + parseElement(node, stack, pseudoElement); + } else if (node.nodeType === node.TEXT_NODE) { + renderText(element, node, stack); + } + }); + } + + function init() { + var background = getCSS(document.documentElement, "backgroundColor"), + transparentBackground = (Util.isTransparent(background) && element === document.body), + stack = renderElement(element, null, false, transparentBackground); + parseChildren(element, stack); + + if (transparentBackground) { + background = stack.backgroundColor; + } + + body.removeChild(hidePseudoElements); + return { + backgroundColor: background, + stack: stack + }; + } + + return init(); +}; + +function h2czContext(zindex) { + return { + zindex: zindex, + children: [] + }; +} + +_html2canvas.Preload = function( options ) { + + var images = { + numLoaded: 0, // also failed are counted here + numFailed: 0, + numTotal: 0, + cleanupDone: false + }, + pageOrigin, + Util = _html2canvas.Util, + methods, + i, + count = 0, + element = options.elements[0] || document.body, + doc = element.ownerDocument, + domImages = element.getElementsByTagName('img'), // Fetch images of the present element only + imgLen = domImages.length, + link = doc.createElement("a"), + supportCORS = (function( img ){ + return (img.crossOrigin !== undefined); + })(new Image()), + timeoutTimer; + + link.href = window.location.href; + pageOrigin = link.protocol + link.host; + + function isSameOrigin(url){ + link.href = url; + link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ + var origin = link.protocol + link.host; + return (origin === pageOrigin); + } + + function start(){ + Util.log("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); + if (!images.firstRun && images.numLoaded >= images.numTotal){ + Util.log("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); + + if (typeof options.complete === "function"){ + options.complete(images); + } + + } + } + + // TODO modify proxy to serve images with CORS enabled, where available + function proxyGetImage(url, img, imageObj){ + var callback_name, + scriptUrl = options.proxy, + script; + + link.href = url; + url = link.href; // work around for pages with base href="" set - WARNING: this may change the url + + callback_name = 'html2canvas_' + (count++); + imageObj.callbackname = callback_name; + + if (scriptUrl.indexOf("?") > -1) { + scriptUrl += "&"; + } else { + scriptUrl += "?"; + } + scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; + script = doc.createElement("script"); + + window[callback_name] = function(a){ + if (a.substring(0,6) === "error:"){ + imageObj.succeeded = false; + images.numLoaded++; + images.numFailed++; + start(); + } else { + setImageLoadHandlers(img, imageObj); + img.src = a; + } + window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[callback_name]; // for all browser that support this + } catch(ex) {} + script.parentNode.removeChild(script); + script = null; + delete imageObj.script; + delete imageObj.callbackname; + }; + + script.setAttribute("type", "text/javascript"); + script.setAttribute("src", scriptUrl); + imageObj.script = script; + window.document.body.appendChild(script); + + } + + function loadPseudoElement(element, type) { + var style = window.getComputedStyle(element, type), + content = style.content; + if (content.substr(0, 3) === 'url') { + methods.loadImage(_html2canvas.Util.parseBackgroundImage(content)[0].args[0]); + } + loadBackgroundImages(style.backgroundImage, element); + } + + function loadPseudoElementImages(element) { + loadPseudoElement(element, ":before"); + loadPseudoElement(element, ":after"); + } + + function loadGradientImage(backgroundImage, bounds) { + var img = _html2canvas.Generate.Gradient(backgroundImage, bounds); + + if (img !== undefined){ + images[backgroundImage] = { + img: img, + succeeded: true + }; + images.numTotal++; + images.numLoaded++; + start(); + } + } + + function invalidBackgrounds(background_image) { + return (background_image && background_image.method && background_image.args && background_image.args.length > 0 ); + } + + function loadBackgroundImages(background_image, el) { + var bounds; + + _html2canvas.Util.parseBackgroundImage(background_image).filter(invalidBackgrounds).forEach(function(background_image) { + if (background_image.method === 'url') { + methods.loadImage(background_image.args[0]); + } else if(background_image.method.match(/\-?gradient$/)) { + if(bounds === undefined) { + bounds = _html2canvas.Util.Bounds(el); + } + loadGradientImage(background_image.value, bounds); + } + }); + } + + function getImages (el) { + var elNodeType = false; + + // Firefox fails with permission denied on pages with iframes + try { + Util.Children(el).forEach(getImages); + } + catch( e ) {} + + try { + elNodeType = el.nodeType; + } catch (ex) { + elNodeType = false; + Util.log("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); + } + + if (elNodeType === 1 || elNodeType === undefined) { + loadPseudoElementImages(el); + try { + loadBackgroundImages(Util.getCSS(el, 'backgroundImage'), el); + } catch(e) { + Util.log("html2canvas: failed to get background-image - Exception: " + e.message); + } + loadBackgroundImages(el); + } + } + + function setImageLoadHandlers(img, imageObj) { + img.onload = function() { + if ( imageObj.timer !== undefined ) { + // CORS succeeded + window.clearTimeout( imageObj.timer ); + } + + images.numLoaded++; + imageObj.succeeded = true; + img.onerror = img.onload = null; + start(); + }; + img.onerror = function() { + if (img.crossOrigin === "anonymous") { + // CORS failed + window.clearTimeout( imageObj.timer ); + + // let's try with proxy instead + if ( options.proxy ) { + var src = img.src; + img = new Image(); + imageObj.img = img; + img.src = src; + + proxyGetImage( img.src, img, imageObj ); + return; + } + } + + images.numLoaded++; + images.numFailed++; + imageObj.succeeded = false; + img.onerror = img.onload = null; + start(); + }; + } + + methods = { + loadImage: function( src ) { + var img, imageObj; + if ( src && images[src] === undefined ) { + img = new Image(); + if ( src.match(/data:image\/.*;base64,/i) ) { + img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + } else if ( isSameOrigin( src ) || options.allowTaint === true ) { + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + } else if ( supportCORS && !options.allowTaint && options.useCORS ) { + // attempt to load with CORS + + img.crossOrigin = "anonymous"; + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + } else if ( options.proxy ) { + imageObj = images[src] = { + img: img + }; + images.numTotal++; + proxyGetImage( src, img, imageObj ); + } + } + + }, + cleanupDOM: function(cause) { + var img, src; + if (!images.cleanupDone) { + if (cause && typeof cause === "string") { + Util.log("html2canvas: Cleanup because: " + cause); + } else { + Util.log("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); + } + + for (src in images) { + if (images.hasOwnProperty(src)) { + img = images[src]; + if (typeof img === "object" && img.callbackname && img.succeeded === undefined) { + // cancel proxy image request + window[img.callbackname] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[img.callbackname]; // for all browser that support this + } catch(ex) {} + if (img.script && img.script.parentNode) { + img.script.setAttribute("src", "about:blank"); // try to cancel running request + img.script.parentNode.removeChild(img.script); + } + images.numLoaded++; + images.numFailed++; + Util.log("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); + } + } + } + + // cancel any pending requests + if(window.stop !== undefined) { + window.stop(); + } else if(document.execCommand !== undefined) { + document.execCommand("Stop", false); + } + if (document.close !== undefined) { + document.close(); + } + images.cleanupDone = true; + if (!(cause && typeof cause === "string")) { + start(); + } + } + }, + + renderingDone: function() { + if (timeoutTimer) { + window.clearTimeout(timeoutTimer); + } + } + }; + + if (options.timeout > 0) { + timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); + } + + Util.log('html2canvas: Preload starts: finding background-images'); + images.firstRun = true; + + getImages(element); + + Util.log('html2canvas: Preload: Finding images'); + // load images + for (i = 0; i < imgLen; i+=1){ + methods.loadImage( domImages[i].getAttribute( "src" ) ); + } + + images.firstRun = false; + Util.log('html2canvas: Preload: Done.'); + if (images.numTotal === images.numLoaded) { + start(); + } + + return methods; +}; + +_html2canvas.Renderer = function(parseQueue, options){ + + // http://www.w3.org/TR/CSS21/zindex.html + function createRenderQueue(parseQueue) { + var queue = [], + rootContext; + + rootContext = (function buildStackingContext(rootNode) { + var rootContext = {}; + function insert(context, node, specialParent) { + var zi = (node.zIndex.zindex === 'auto') ? 0 : Number(node.zIndex.zindex), + contextForChildren = context, // the stacking context for children + isPositioned = node.zIndex.isPositioned, + isFloated = node.zIndex.isFloated, + stub = {node: node}, + childrenDest = specialParent; // where children without z-index should be pushed into + + if (node.zIndex.ownStacking) { + // '!' comes before numbers in sorted array + contextForChildren = stub.context = { '!': [{node:node, children: []}]}; + childrenDest = undefined; + } else if (isPositioned || isFloated) { + childrenDest = stub.children = []; + } + + if (zi === 0 && specialParent) { + specialParent.push(stub); + } else { + if (!context[zi]) { context[zi] = []; } + context[zi].push(stub); + } + + node.zIndex.children.forEach(function(childNode) { + insert(contextForChildren, childNode, childrenDest); + }); + } + insert(rootContext, rootNode); + return rootContext; + })(parseQueue); + + function sortZ(context) { + Object.keys(context).sort().forEach(function(zi) { + var nonPositioned = [], + floated = [], + positioned = [], + list = []; + + // positioned after static + context[zi].forEach(function(v) { + if (v.node.zIndex.isPositioned || v.node.zIndex.opacity < 1) { + // http://www.w3.org/TR/css3-color/#transparency + // non-positioned element with opactiy < 1 should be stacked as if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’. + positioned.push(v); + } else if (v.node.zIndex.isFloated) { + floated.push(v); + } else { + nonPositioned.push(v); + } + }); + + (function walk(arr) { + arr.forEach(function(v) { + list.push(v); + if (v.children) { walk(v.children); } + }); + })(nonPositioned.concat(floated, positioned)); + + list.forEach(function(v) { + if (v.context) { + sortZ(v.context); + } else { + queue.push(v.node); + } + }); + }); + } + + sortZ(rootContext); + + return queue; + } + + function getRenderer(rendererName) { + var renderer; + + if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) { + renderer = _html2canvas.Renderer[rendererName](options); + } else if (typeof rendererName === "function") { + renderer = rendererName(options); + } else { + throw new Error("Unknown renderer"); + } + + if ( typeof renderer !== "function" ) { + throw new Error("Invalid renderer defined"); + } + return renderer; + } + + return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue.stack), _html2canvas); +}; + +_html2canvas.Util.Support = function (options, doc) { + + function supportSVGRendering() { + var img = new Image(), + canvas = doc.createElement("canvas"), + ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); + if (ctx === false) { + return false; + } + canvas.width = canvas.height = 10; + img.src = [ + "data:image/svg+xml,", + "", + "", + "
", + "sup", + "
", + "
", + "
" + ].join(""); + try { + ctx.drawImage(img, 0, 0); + canvas.toDataURL(); + } catch(e) { + return false; + } + _html2canvas.Util.log('html2canvas: Parse: SVG powered rendering available'); + return true; + } + + // Test whether we can use ranges to measure bounding boxes + // Opera doesn't provide valid bounds.height/bottom even though it supports the method. + + function supportRangeBounds() { + var r, testElement, rangeBounds, rangeHeight, support = false; + + if (doc.createRange) { + r = doc.createRange(); + if (r.getBoundingClientRect) { + testElement = doc.createElement('boundtest'); + testElement.style.height = "123px"; + testElement.style.display = "block"; + doc.body.appendChild(testElement); + + r.selectNode(testElement); + rangeBounds = r.getBoundingClientRect(); + rangeHeight = rangeBounds.height; + + if (rangeHeight === 123) { + support = true; + } + doc.body.removeChild(testElement); + } + } + + return support; + } + + return { + rangeBounds: supportRangeBounds(), + svgRendering: options.svgRendering && supportSVGRendering() + }; +}; +window.html2canvas = function(elements, opts) { + elements = (elements.length) ? elements : [elements]; + var queue, + canvas, + options = { + // general + logging: false, + elements: elements, + background: "#fff", + + // preload options + proxy: null, + timeout: 0, // no timeout + useCORS: false, // try to load images as CORS (where available), before falling back to proxy + allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true + + // parse options + svgRendering: false, // use svg powered rendering where available (FF11+) + ignoreElements: "IFRAME|OBJECT|PARAM", + useOverflow: true, + letterRendering: false, + chinese: false, + + // render options + + width: null, + height: null, + taintTest: true, // do a taint test with all images before applying to canvas + renderer: "Canvas" + }; + + options = _html2canvas.Util.Extend(opts, options); + + _html2canvas.logging = options.logging; + options.complete = function( images ) { + + if (typeof options.onpreloaded === "function") { + if ( options.onpreloaded( images ) === false ) { + return; + } + } + queue = _html2canvas.Parse( images, options ); + + if (typeof options.onparsed === "function") { + if ( options.onparsed( queue ) === false ) { + return; + } + } + + canvas = _html2canvas.Renderer( queue, options ); + + if (typeof options.onrendered === "function") { + options.onrendered( canvas ); + } + + + }; + + // for pages without images, we still want this to be async, i.e. return methods before executing + window.setTimeout( function(){ + _html2canvas.Preload( options ); + }, 0 ); + + return { + render: function( queue, opts ) { + return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) ); + }, + parse: function( images, opts ) { + return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) ); + }, + preload: function( opts ) { + return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) ); + }, + log: _html2canvas.Util.log + }; +}; + +window.html2canvas.log = _html2canvas.Util.log; // for renderers +window.html2canvas.Renderer = { + Canvas: undefined // We are assuming this will be used +}; +_html2canvas.Renderer.Canvas = function(options) { + options = options || {}; + + var doc = document, + safeImages = [], + testCanvas = document.createElement("canvas"), + testctx = testCanvas.getContext("2d"), + Util = _html2canvas.Util, + canvas = options.canvas || doc.createElement('canvas'); + + function createShape(ctx, args) { + ctx.beginPath(); + args.forEach(function(arg) { + ctx[arg.name].apply(ctx, arg['arguments']); + }); + ctx.closePath(); + } + + function safeImage(item) { + if (safeImages.indexOf(item['arguments'][0].src ) === -1) { + testctx.drawImage(item['arguments'][0], 0, 0); + try { + testctx.getImageData(0, 0, 1, 1); + } catch(e) { + testCanvas = doc.createElement("canvas"); + testctx = testCanvas.getContext("2d"); + return false; + } + safeImages.push(item['arguments'][0].src); + } + return true; + } + + function renderItem(ctx, item) { + switch(item.type){ + case "variable": + ctx[item.name] = item['arguments']; + break; + case "function": + switch(item.name) { + case "createPattern": + if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) { + try { + ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat"); + } + catch(e) { + Util.log("html2canvas: Renderer: Error creating pattern", e.message); + } + } + break; + case "drawShape": + createShape(ctx, item['arguments']); + break; + case "drawImage": + if (item['arguments'][8] > 0 && item['arguments'][7] > 0) { + if (!options.taintTest || (options.taintTest && safeImage(item))) { + ctx.drawImage.apply( ctx, item['arguments'] ); + } + } + break; + default: + ctx[item.name].apply(ctx, item['arguments']); + } + break; + } + } + + return function(parsedData, options, document, queue, _html2canvas) { + var ctx = canvas.getContext("2d"), + newCanvas, + bounds, + fstyle, + zStack = parsedData.stack; + + canvas.width = canvas.style.width = options.width || zStack.ctx.width; + canvas.height = canvas.style.height = options.height || zStack.ctx.height; + + fstyle = ctx.fillStyle; + ctx.fillStyle = (Util.isTransparent(zStack.backgroundColor) && options.background !== undefined) ? options.background : parsedData.backgroundColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = fstyle; + + queue.forEach(function(storageContext) { + // set common settings for canvas + ctx.textBaseline = "bottom"; + ctx.save(); + + if (storageContext.transform.matrix) { + ctx.translate(storageContext.transform.origin[0], storageContext.transform.origin[1]); + ctx.transform.apply(ctx, storageContext.transform.matrix); + ctx.translate(-storageContext.transform.origin[0], -storageContext.transform.origin[1]); + } + + if (storageContext.clip){ + ctx.beginPath(); + ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); + ctx.clip(); + } + + if (storageContext.ctx.storage) { + storageContext.ctx.storage.forEach(function(item) { + renderItem(ctx, item); + }); + } + + ctx.restore(); + }); + + Util.log("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); + + if (options.elements.length === 1) { + if (typeof options.elements[0] === "object" && options.elements[0].nodeName !== "BODY") { + // crop image to the bounds of selected (single) element + bounds = _html2canvas.Util.Bounds(options.elements[0]); + newCanvas = document.createElement('canvas'); + newCanvas.width = Math.ceil(bounds.width); + newCanvas.height = Math.ceil(bounds.height); + ctx = newCanvas.getContext("2d"); + + ctx.drawImage(canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height); + canvas = null; + return newCanvas; + } + } + + return canvas; + }; +}; +})(window,document); \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/build/html2canvas.min.js b/Apollo/bower_components/html2canvas/build/html2canvas.min.js new file mode 100755 index 00000000..2ea032de --- /dev/null +++ b/Apollo/bower_components/html2canvas/build/html2canvas.min.js @@ -0,0 +1,8 @@ +/* + html2canvas 0.4.1 + Copyright (c) 2013 Niklas von Hertzen + + Released under MIT License +*/ +(function(t,e,n){"use strict";function r(t,e,n){var r,a=t.runtimeStyle&&t.runtimeStyle[e],o=t.style;return!/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test(n)&&/^-?\d/.test(n)&&(r=o.left,a&&(t.runtimeStyle.left=t.currentStyle.left),o.left="fontSize"===e?"1em":n||0,n=o.pixelLeft+"px",o.left=r,a&&(t.runtimeStyle.left=a)),/^(thin|medium|thick)$/i.test(n)?n:Math.round(parseFloat(n))+"px"}function a(t){return parseInt(t,10)}function o(t,e,a,o){if(t=(t||"").split(","),t=t[o||0]||t[0]||"auto",t=u.Util.trimText(t).split(" "),"backgroundSize"!==a||t[0]&&!t[0].match(/cover|contain|auto/)){if(t[0]=-1===t[0].indexOf("%")?r(e,a+"X",t[0]):t[0],t[1]===n){if("backgroundSize"===a)return t[1]="auto",t;t[1]=t[0]}t[1]=-1===t[1].indexOf("%")?r(e,a+"Y",t[1]):t[1]}else;return t}function i(t,e,n,r,a,o){var i,l,s,c,d=u.Util.getCSS(e,t,a);if(1===d.length&&(c=d[0],d=[],d[0]=c,d[1]=c),-1!==(""+d[0]).indexOf("%"))s=parseFloat(d[0])/100,l=n.width*s,"backgroundSize"!==t&&(l-=(o||r).width*s);else if("backgroundSize"===t)if("auto"===d[0])l=r.width;else if(/contain|cover/.test(d[0])){var h=u.Util.resizeBounds(r.width,r.height,n.width,n.height,d[0]);l=h.width,i=h.height}else l=parseInt(d[0],10);else l=parseInt(d[0],10);return"auto"===d[1]?i=l/r.width*r.height:-1!==(""+d[1]).indexOf("%")?(s=parseFloat(d[1])/100,i=n.height*s,"backgroundSize"!==t&&(i-=(o||r).height*s)):i=parseInt(d[1],10),[l,i]}function l(t,e){var n=[];return{storage:n,width:t,height:e,clip:function(){n.push({type:"function",name:"clip",arguments:arguments})},translate:function(){n.push({type:"function",name:"translate",arguments:arguments})},fill:function(){n.push({type:"function",name:"fill",arguments:arguments})},save:function(){n.push({type:"function",name:"save",arguments:arguments})},restore:function(){n.push({type:"function",name:"restore",arguments:arguments})},fillRect:function(){n.push({type:"function",name:"fillRect",arguments:arguments})},createPattern:function(){n.push({type:"function",name:"createPattern",arguments:arguments})},drawShape:function(){var t=[];return n.push({type:"function",name:"drawShape",arguments:t}),{moveTo:function(){t.push({name:"moveTo",arguments:arguments})},lineTo:function(){t.push({name:"lineTo",arguments:arguments})},arcTo:function(){t.push({name:"arcTo",arguments:arguments})},bezierCurveTo:function(){t.push({name:"bezierCurveTo",arguments:arguments})},quadraticCurveTo:function(){t.push({name:"quadraticCurveTo",arguments:arguments})}}},drawImage:function(){n.push({type:"function",name:"drawImage",arguments:arguments})},fillText:function(){n.push({type:"function",name:"fillText",arguments:arguments})},setVariable:function(t,e){return n.push({type:"variable",name:t,arguments:e}),e}}}function s(t){return{zindex:t,children:[]}}var c,d,u={};u.Util={},u.Util.log=function(e){u.logging&&t.console&&t.console.log&&t.console.log(e)},u.Util.trimText=function(t){return function(e){return t?t.apply(e):((e||"")+"").replace(/^\s+|\s+$/g,"")}}(String.prototype.trim),u.Util.asFloat=function(t){return parseFloat(t)},function(){var t=/((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g,e=/(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g;u.Util.parseTextShadows=function(n){if(!n||"none"===n)return[];for(var r=n.match(t),a=[],o=0;r&&r.length>o;o++){var i=r[o].match(e);a.push({color:i[0],offsetX:i[1]?i[1].replace("px",""):0,offsetY:i[2]?i[2].replace("px",""):0,blur:i[3]?i[3].replace("px",""):0})}return a}}(),u.Util.parseBackgroundImage=function(t){var e,n,r,a,o,i,l,s,c=" \r\n ",d=[],u=0,h=0,f=function(){e&&('"'===n.substr(0,1)&&(n=n.substr(1,n.length-2)),n&&s.push(n),"-"===e.substr(0,1)&&(a=e.indexOf("-",1)+1)>0&&(r=e.substr(0,a),e=e.substr(a)),d.push({prefix:r,method:e.toLowerCase(),value:o,args:s})),s=[],e=r=n=o=""};f();for(var p=0,g=t.length;g>p;p++)if(i=t[p],!(0===u&&c.indexOf(i)>-1)){switch(i){case'"':l?l===i&&(l=null):l=i;break;case"(":if(l)break;if(0===u){u=1,o+=i;continue}h++;break;case")":if(l)break;if(1===u){if(0===h){u=0,o+=i,f();continue}h--}break;case",":if(l)break;if(0===u){f();continue}if(1===u&&0===h&&!e.match(/^url$/i)){s.push(n),n="",o+=i;continue}}o+=i,0===u?e+=i:n+=i}return f(),d},u.Util.Bounds=function(t){var e,n={};return t.getBoundingClientRect&&(e=t.getBoundingClientRect(),n.top=e.top,n.bottom=e.bottom||e.top+e.height,n.left=e.left,n.width=t.offsetWidth,n.height=t.offsetHeight),n},u.Util.OffsetBounds=function(t){var e=t.offsetParent?u.Util.OffsetBounds(t.offsetParent):{top:0,left:0};return{top:t.offsetTop+e.top,bottom:t.offsetTop+t.offsetHeight+e.top,left:t.offsetLeft+e.left,width:t.offsetWidth,height:t.offsetHeight}},u.Util.getCSS=function(t,n,r){c!==t&&(d=e.defaultView.getComputedStyle(t,null));var i=d[n];if(/^background(Size|Position)$/.test(n))return o(i,t,n,r);if(/border(Top|Bottom)(Left|Right)Radius/.test(n)){var l=i.split(" ");return 1>=l.length&&(l[1]=l[0]),l.map(a)}return i},u.Util.resizeBounds=function(t,e,n,r,a){var o,i,l=n/r,s=t/e;return a&&"auto"!==a?s>l^"contain"===a?(i=r,o=r*s):(o=n,i=n/s):(o=n,i=r),{width:o,height:i}},u.Util.BackgroundPosition=function(t,e,n,r,a){var o=i("backgroundPosition",t,e,n,r,a);return{left:o[0],top:o[1]}},u.Util.BackgroundSize=function(t,e,n,r){var a=i("backgroundSize",t,e,n,r);return{width:a[0],height:a[1]}},u.Util.Extend=function(t,e){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e},u.Util.Children=function(t){var e;try{e=t.nodeName&&"IFRAME"===t.nodeName.toUpperCase()?t.contentDocument||t.contentWindow.document:function(t){var e=[];return null!==t&&function(t,e){var r=t.length,a=0;if("number"==typeof e.length)for(var o=e.length;o>a;a++)t[r++]=e[a];else for(;e[a]!==n;)t[r++]=e[a++];return t.length=r,t}(e,t),e}(t.childNodes)}catch(r){u.Util.log("html2canvas.Util.Children failed with exception: "+r.message),e=[]}return e},u.Util.isTransparent=function(t){return"transparent"===t||"rgba(0, 0, 0, 0)"===t},u.Util.Font=function(){var t={};return function(e,r,a){if(t[e+"-"+r]!==n)return t[e+"-"+r];var o,i,l,s=a.createElement("div"),c=a.createElement("img"),d=a.createElement("span"),u="Hidden Text";return s.style.visibility="hidden",s.style.fontFamily=e,s.style.fontSize=r,s.style.margin=0,s.style.padding=0,a.body.appendChild(s),c.src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=",c.width=1,c.height=1,c.style.margin=0,c.style.padding=0,c.style.verticalAlign="baseline",d.style.fontFamily=e,d.style.fontSize=r,d.style.margin=0,d.style.padding=0,d.appendChild(a.createTextNode(u)),s.appendChild(d),s.appendChild(c),o=c.offsetTop-d.offsetTop+1,s.removeChild(d),s.appendChild(a.createTextNode(u)),s.style.lineHeight="normal",c.style.verticalAlign="super",i=c.offsetTop-s.offsetTop+1,l={baseline:o,lineWidth:1,middle:i},t[e+"-"+r]=l,a.body.removeChild(s),l}}(),function(){function t(t){return function(e){try{t.addColorStop(e.stop,e.color)}catch(r){n.log(["failed to add color stop: ",r,"; tried to add: ",e])}}}var n=u.Util,r={};u.Generate=r;var a=[/^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,/^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,/^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)\-]+)\)$/,/^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/,/^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/,/^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z\-]*)([\w\d\.\s,%\(\)]+)\)$/,/^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/];r.parseGradient=function(t,e){var n,r,o,i,l,s,c,d,u,h,f,p,g=a.length;for(r=0;g>r&&!(o=t.match(a[r]));r+=1);if(o)switch(o[1]){case"-webkit-linear-gradient":case"-o-linear-gradient":if(n={type:"linear",x0:null,y0:null,x1:null,y1:null,colorStops:[]},l=o[2].match(/\w+/g))for(s=l.length,r=0;s>r;r+=1)switch(l[r]){case"top":n.y0=0,n.y1=e.height;break;case"right":n.x0=e.width,n.x1=0;break;case"bottom":n.y0=e.height,n.y1=0;break;case"left":n.x0=0,n.x1=e.width}if(null===n.x0&&null===n.x1&&(n.x0=n.x1=e.width/2),null===n.y0&&null===n.y1&&(n.y0=n.y1=e.height/2),l=o[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g))for(s=l.length,c=1/Math.max(s-1,1),r=0;s>r;r+=1)d=l[r].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/),d[2]?(i=parseFloat(d[2]),i/="%"===d[3]?100:e.width):i=r*c,n.colorStops.push({color:d[1],stop:i});break;case"-webkit-gradient":if(n={type:"radial"===o[2]?"circle":o[2],x0:0,y0:0,x1:0,y1:0,colorStops:[]},l=o[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/),l&&(n.x0=l[1]*e.width/100,n.y0=l[2]*e.height/100,n.x1=l[3]*e.width/100,n.y1=l[4]*e.height/100),l=o[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g))for(s=l.length,r=0;s>r;r+=1)d=l[r].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/),i=parseFloat(d[2]),"from"===d[1]&&(i=0),"to"===d[1]&&(i=1),n.colorStops.push({color:d[3],stop:i});break;case"-moz-linear-gradient":if(n={type:"linear",x0:0,y0:0,x1:0,y1:0,colorStops:[]},l=o[2].match(/(\d{1,3})%?\s(\d{1,3})%?/),l&&(n.x0=l[1]*e.width/100,n.y0=l[2]*e.height/100,n.x1=e.width-n.x0,n.y1=e.height-n.y0),l=o[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g))for(s=l.length,c=1/Math.max(s-1,1),r=0;s>r;r+=1)d=l[r].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/),d[2]?(i=parseFloat(d[2]),d[3]&&(i/=100)):i=r*c,n.colorStops.push({color:d[1],stop:i});break;case"-webkit-radial-gradient":case"-moz-radial-gradient":case"-o-radial-gradient":if(n={type:"circle",x0:0,y0:0,x1:e.width,y1:e.height,cx:0,cy:0,rx:0,ry:0,colorStops:[]},l=o[2].match(/(\d{1,3})%?\s(\d{1,3})%?/),l&&(n.cx=l[1]*e.width/100,n.cy=l[2]*e.height/100),l=o[3].match(/\w+/),d=o[4].match(/[a-z\-]*/),l&&d)switch(d[0]){case"farthest-corner":case"cover":case"":u=Math.sqrt(Math.pow(n.cx,2)+Math.pow(n.cy,2)),h=Math.sqrt(Math.pow(n.cx,2)+Math.pow(n.y1-n.cy,2)),f=Math.sqrt(Math.pow(n.x1-n.cx,2)+Math.pow(n.y1-n.cy,2)),p=Math.sqrt(Math.pow(n.x1-n.cx,2)+Math.pow(n.cy,2)),n.rx=n.ry=Math.max(u,h,f,p);break;case"closest-corner":u=Math.sqrt(Math.pow(n.cx,2)+Math.pow(n.cy,2)),h=Math.sqrt(Math.pow(n.cx,2)+Math.pow(n.y1-n.cy,2)),f=Math.sqrt(Math.pow(n.x1-n.cx,2)+Math.pow(n.y1-n.cy,2)),p=Math.sqrt(Math.pow(n.x1-n.cx,2)+Math.pow(n.cy,2)),n.rx=n.ry=Math.min(u,h,f,p);break;case"farthest-side":"circle"===l[0]?n.rx=n.ry=Math.max(n.cx,n.cy,n.x1-n.cx,n.y1-n.cy):(n.type=l[0],n.rx=Math.max(n.cx,n.x1-n.cx),n.ry=Math.max(n.cy,n.y1-n.cy));break;case"closest-side":case"contain":"circle"===l[0]?n.rx=n.ry=Math.min(n.cx,n.cy,n.x1-n.cx,n.y1-n.cy):(n.type=l[0],n.rx=Math.min(n.cx,n.x1-n.cx),n.ry=Math.min(n.cy,n.y1-n.cy))}if(l=o[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g))for(s=l.length,c=1/Math.max(s-1,1),r=0;s>r;r+=1)d=l[r].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/),d[2]?(i=parseFloat(d[2]),i/="%"===d[3]?100:e.width):i=r*c,n.colorStops.push({color:d[1],stop:i})}return n},r.Gradient=function(n,r){if(0!==r.width&&0!==r.height){var a,o,i=e.createElement("canvas"),l=i.getContext("2d");if(i.width=r.width,i.height=r.height,a=u.Generate.parseGradient(n,r))switch(a.type){case"linear":o=l.createLinearGradient(a.x0,a.y0,a.x1,a.y1),a.colorStops.forEach(t(o)),l.fillStyle=o,l.fillRect(0,0,r.width,r.height);break;case"circle":o=l.createRadialGradient(a.cx,a.cy,0,a.cx,a.cy,a.rx),a.colorStops.forEach(t(o)),l.fillStyle=o,l.fillRect(0,0,r.width,r.height);break;case"ellipse":var s=e.createElement("canvas"),c=s.getContext("2d"),d=Math.max(a.rx,a.ry),h=2*d;s.width=s.height=h,o=c.createRadialGradient(a.rx,a.ry,0,a.rx,a.ry,d),a.colorStops.forEach(t(o)),c.fillStyle=o,c.fillRect(0,0,h,h),l.fillStyle=a.colorStops[a.colorStops.length-1].color,l.fillRect(0,0,i.width,i.height),l.drawImage(s,a.cx-a.rx,a.cy-a.ry,2*a.rx,2*a.ry)}return i}},r.ListAlpha=function(t){var e,n="";do e=t%26,n=String.fromCharCode(e+64)+n,t/=26;while(26*t>26);return n},r.ListRoman=function(t){var e,n=["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"],r=[1e3,900,500,400,100,90,50,40,10,9,5,4,1],a="",o=n.length;if(0>=t||t>=4e3)return t;for(e=0;o>e;e+=1)for(;t>=r[e];)t-=r[e],a+=n[e];return a}}(),u.Parse=function(r,a){function o(){return Math.max(Math.max(de.body.scrollWidth,de.documentElement.scrollWidth),Math.max(de.body.offsetWidth,de.documentElement.offsetWidth),Math.max(de.body.clientWidth,de.documentElement.clientWidth))}function i(){return Math.max(Math.max(de.body.scrollHeight,de.documentElement.scrollHeight),Math.max(de.body.offsetHeight,de.documentElement.offsetHeight),Math.max(de.body.clientHeight,de.documentElement.clientHeight))}function c(t,e){var n=parseInt(ge(t,e),10);return isNaN(n)?0:n}function d(t,e,n,r,a,o){"transparent"!==o&&(t.setVariable("fillStyle",o),t.fillRect(e,n,r,a),ce+=1)}function h(t,e,r){return t.length>0?e+r.toUpperCase():n}function f(t,e){switch(e){case"lowercase":return t.toLowerCase();case"capitalize":return t.replace(/(^|\s|:|-|\(|\))([a-z])/g,h);case"uppercase":return t.toUpperCase();default:return t}}function p(t){return/^(normal|none|0px)$/.test(t)}function g(t,e,n,r){null!==t&&ue.trimText(t).length>0&&(r.fillText(t,e,n),ce+=1)}function m(t,e,r,a){var o=!1,i=ge(e,"fontWeight"),l=ge(e,"fontFamily"),s=ge(e,"fontSize"),c=ue.parseTextShadows(ge(e,"textShadow"));switch(parseInt(i,10)){case 401:i="bold";break;case 400:i="normal"}return t.setVariable("fillStyle",a),t.setVariable("font",[ge(e,"fontStyle"),ge(e,"fontVariant"),i,s,l].join(" ")),t.setVariable("textAlign",o?"right":"left"),c.length&&(t.setVariable("shadowColor",c[0].color),t.setVariable("shadowOffsetX",c[0].offsetX),t.setVariable("shadowOffsetY",c[0].offsetY),t.setVariable("shadowBlur",c[0].blur)),"none"!==r?ue.Font(l,s,de):n}function y(t,e,n,r,a){switch(e){case"underline":d(t,n.left,Math.round(n.top+r.baseline+r.lineWidth),n.width,1,a);break;case"overline":d(t,n.left,Math.round(n.top),n.width,1,a);break;case"line-through":d(t,n.left,Math.ceil(n.top+r.middle+r.lineWidth),n.width,1,a)}}function b(t,e,n,r,a){var o;if(he.rangeBounds&&!a)("none"!==n||0!==ue.trimText(e).length)&&(o=w(e,t.node,t.textOffset)),t.textOffset+=e.length;else if(t.node&&"string"==typeof t.node.nodeValue){var i=r?t.node.splitText(e.length):null;o=x(t.node,a),t.node=i}return o}function w(t,e,n){var r=de.createRange();return r.setStart(e,n),r.setEnd(e,n+t.length),r.getBoundingClientRect()}function x(t,e){var n=t.parentNode,r=de.createElement("wrapper"),a=t.cloneNode(!0);r.appendChild(t.cloneNode(!0)),n.replaceChild(r,t);var o=e?ue.OffsetBounds(r):ue.Bounds(r);return n.replaceChild(a,r),o}function v(t,e,n){var r,o,i=n.ctx,l=ge(t,"color"),s=ge(t,"textDecoration"),c=ge(t,"textAlign"),d={node:e,textOffset:0};ue.trimText(e.nodeValue).length>0&&(e.nodeValue=f(e.nodeValue,ge(t,"textTransform")),c=c.replace(["-webkit-auto"],["auto"]),o=!a.letterRendering&&/^(left|right|justify|auto)$/.test(c)&&p(ge(t,"letterSpacing"))?e.nodeValue.split(/(\b| )/):e.nodeValue.split(""),r=m(i,t,s,l),a.chinese&&o.forEach(function(t,e){/.*[\u4E00-\u9FA5].*$/.test(t)&&(t=t.split(""),t.unshift(e,1),o.splice.apply(o,t))}),o.forEach(function(t,e){var a=b(d,t,s,o.length-1>e,n.transform.matrix);a&&(g(t,a.left,a.bottom,i),y(i,s,a,r,l))}))}function k(t,e){var n,r,a=de.createElement("boundelement");return a.style.display="inline",n=t.style.listStyleType,t.style.listStyleType="none",a.appendChild(de.createTextNode(e)),t.insertBefore(a,t.firstChild),r=ue.Bounds(a),t.removeChild(a),t.style.listStyleType=n,r}function C(t){var e=-1,n=1,r=t.parentNode.childNodes;if(t.parentNode){for(;r[++e]!==t;)1===r[e].nodeType&&n++;return n}return-1}function T(t,e){var n,r=C(t);switch(e){case"decimal":n=r;break;case"decimal-leading-zero":n=1===(""+r).length?r="0"+(""+r):""+r;break;case"upper-roman":n=u.Generate.ListRoman(r);break;case"lower-roman":n=u.Generate.ListRoman(r).toLowerCase();break;case"lower-alpha":n=u.Generate.ListAlpha(r).toLowerCase();break;case"upper-alpha":n=u.Generate.ListAlpha(r)}return n+". "}function S(t,e,n){var r,a,o,i=e.ctx,l=ge(t,"listStyleType");if(/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(l)){if(a=T(t,l),o=k(t,a),m(i,t,"none",ge(t,"color")),"inside"!==ge(t,"listStylePosition"))return;i.setVariable("textAlign","left"),r=n.left,g(a,r,o.bottom,i)}}function M(t){var e=r[t];return e&&e.succeeded===!0?e.img:!1}function R(t,e){var n=Math.max(t.left,e.left),r=Math.max(t.top,e.top),a=Math.min(t.left+t.width,e.left+e.width),o=Math.min(t.top+t.height,e.top+e.height);return{left:n,top:r,width:a-n,height:o-r}}function E(t,e,n){var r,a="static"!==e.cssPosition,o=a?ge(t,"zIndex"):"auto",i=ge(t,"opacity"),l="none"!==ge(t,"cssFloat");e.zIndex=r=s(o),r.isPositioned=a,r.isFloated=l,r.opacity=i,r.ownStacking="auto"!==o||1>i,n&&n.zIndex.children.push(e)}function I(t,e,n,r,a){var o=c(e,"paddingLeft"),i=c(e,"paddingTop"),l=c(e,"paddingRight"),s=c(e,"paddingBottom");$(t,n,0,0,n.width,n.height,r.left+o+a[3].width,r.top+i+a[0].width,r.width-(a[1].width+a[3].width+o+l),r.height-(a[0].width+a[2].width+i+s))}function L(t){return["Top","Right","Bottom","Left"].map(function(e){return{width:c(t,"border"+e+"Width"),color:ge(t,"border"+e+"Color")}})}function O(t){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(e){return ge(t,"border"+e+"Radius")})}function z(t,e,n,r){var a=function(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}};return{start:t,startControl:e,endControl:n,end:r,subdivide:function(o){var i=a(t,e,o),l=a(e,n,o),s=a(n,r,o),c=a(i,l,o),d=a(l,s,o),u=a(c,d,o);return[z(t,i,c,u),z(u,d,s,r)]},curveTo:function(t){t.push(["bezierCurve",e.x,e.y,n.x,n.y,r.x,r.y])},curveToReversed:function(r){r.push(["bezierCurve",n.x,n.y,e.x,e.y,t.x,t.y])}}}function A(t,e,n,r,a,o,i){e[0]>0||e[1]>0?(t.push(["line",r[0].start.x,r[0].start.y]),r[0].curveTo(t),r[1].curveTo(t)):t.push(["line",o,i]),(n[0]>0||n[1]>0)&&t.push(["line",a[0].start.x,a[0].start.y])}function B(t,e,n,r,a,o,i){var l=[];return e[0]>0||e[1]>0?(l.push(["line",r[1].start.x,r[1].start.y]),r[1].curveTo(l)):l.push(["line",t.c1[0],t.c1[1]]),n[0]>0||n[1]>0?(l.push(["line",o[0].start.x,o[0].start.y]),o[0].curveTo(l),l.push(["line",i[0].end.x,i[0].end.y]),i[0].curveToReversed(l)):(l.push(["line",t.c2[0],t.c2[1]]),l.push(["line",t.c3[0],t.c3[1]])),e[0]>0||e[1]>0?(l.push(["line",a[1].end.x,a[1].end.y]),a[1].curveToReversed(l)):l.push(["line",t.c4[0],t.c4[1]]),l}function U(t,e,n){var r=t.left,a=t.top,o=t.width,i=t.height,l=e[0][0],s=e[0][1],c=e[1][0],d=e[1][1],u=e[2][0],h=e[2][1],f=e[3][0],p=e[3][1],g=o-c,m=i-h,y=o-u,b=i-p;return{topLeftOuter:be(r,a,l,s).topLeft.subdivide(.5),topLeftInner:be(r+n[3].width,a+n[0].width,Math.max(0,l-n[3].width),Math.max(0,s-n[0].width)).topLeft.subdivide(.5),topRightOuter:be(r+g,a,c,d).topRight.subdivide(.5),topRightInner:be(r+Math.min(g,o+n[3].width),a+n[0].width,g>o+n[3].width?0:c-n[3].width,d-n[0].width).topRight.subdivide(.5),bottomRightOuter:be(r+y,a+m,u,h).bottomRight.subdivide(.5),bottomRightInner:be(r+Math.min(y,o+n[3].width),a+Math.min(m,i+n[0].width),Math.max(0,u-n[1].width),Math.max(0,h-n[2].width)).bottomRight.subdivide(.5),bottomLeftOuter:be(r,a+b,f,p).bottomLeft.subdivide(.5),bottomLeftInner:be(r+n[3].width,a+b,Math.max(0,f-n[3].width),Math.max(0,p-n[2].width)).bottomLeft.subdivide(.5)}}function N(t,e,n,r,a){var o=ge(t,"backgroundClip"),i=[];switch(o){case"content-box":case"padding-box":A(i,r[0],r[1],e.topLeftInner,e.topRightInner,a.left+n[3].width,a.top+n[0].width),A(i,r[1],r[2],e.topRightInner,e.bottomRightInner,a.left+a.width-n[1].width,a.top+n[0].width),A(i,r[2],r[3],e.bottomRightInner,e.bottomLeftInner,a.left+a.width-n[1].width,a.top+a.height-n[2].width),A(i,r[3],r[0],e.bottomLeftInner,e.topLeftInner,a.left+n[3].width,a.top+a.height-n[2].width);break;default:A(i,r[0],r[1],e.topLeftOuter,e.topRightOuter,a.left,a.top),A(i,r[1],r[2],e.topRightOuter,e.bottomRightOuter,a.left+a.width,a.top),A(i,r[2],r[3],e.bottomRightOuter,e.bottomLeftOuter,a.left+a.width,a.top+a.height),A(i,r[3],r[0],e.bottomLeftOuter,e.topLeftOuter,a.left,a.top+a.height)}return i}function P(t,e,n){var r,a,o,i,l,s,c=e.left,d=e.top,u=e.width,h=e.height,f=O(t),p=U(e,f,n),g={clip:N(t,p,n,f,e),borders:[]};for(r=0;4>r;r++)if(n[r].width>0){switch(a=c,o=d,i=u,l=h-n[2].width,r){case 0:l=n[0].width,s=B({c1:[a,o],c2:[a+i,o],c3:[a+i-n[1].width,o+l],c4:[a+n[3].width,o+l]},f[0],f[1],p.topLeftOuter,p.topLeftInner,p.topRightOuter,p.topRightInner);break;case 1:a=c+u-n[1].width,i=n[1].width,s=B({c1:[a+i,o],c2:[a+i,o+l+n[2].width],c3:[a,o+l],c4:[a,o+n[0].width]},f[1],f[2],p.topRightOuter,p.topRightInner,p.bottomRightOuter,p.bottomRightInner);break;case 2:o=o+h-n[2].width,l=n[2].width,s=B({c1:[a+i,o+l],c2:[a,o+l],c3:[a+n[3].width,o],c4:[a+i-n[3].width,o]},f[2],f[3],p.bottomRightOuter,p.bottomRightInner,p.bottomLeftOuter,p.bottomLeftInner);break;case 3:i=n[3].width,s=B({c1:[a,o+l+n[2].width],c2:[a,o],c3:[a+i,o+n[0].width],c4:[a+i,o+l]},f[3],f[0],p.bottomLeftOuter,p.bottomLeftInner,p.topLeftOuter,p.topLeftInner)}g.borders.push({args:s,color:n[r].color})}return g}function F(t,e){var n=t.drawShape();return e.forEach(function(t,e){n[0===e?"moveTo":t[0]+"To"].apply(null,t.slice(1))}),n}function V(t,e,n){"transparent"!==n&&(t.setVariable("fillStyle",n),F(t,e),t.fill(),ce+=1)}function D(t,e,n){var r,a,o=de.createElement("valuewrap"),i=["lineHeight","textAlign","fontFamily","color","fontSize","paddingLeft","paddingTop","width","height","border","borderLeftWidth","borderTopWidth"];i.forEach(function(e){try{o.style[e]=ge(t,e)}catch(n){ue.log("html2canvas: Parse: Exception caught in renderFormValue: "+n.message)}}),o.style.borderColor="black",o.style.borderStyle="solid",o.style.display="block",o.style.position="absolute",(/^(submit|reset|button|text|password)$/.test(t.type)||"SELECT"===t.nodeName)&&(o.style.lineHeight=ge(t,"height")),o.style.top=e.top+"px",o.style.left=e.left+"px",r="SELECT"===t.nodeName?(t.options[t.selectedIndex]||0).text:t.value,r||(r=t.placeholder),a=de.createTextNode(r),o.appendChild(a),pe.appendChild(o),v(t,a,n),pe.removeChild(o)}function $(t){t.drawImage.apply(t,Array.prototype.slice.call(arguments,1)),ce+=1}function G(n,r){var a=t.getComputedStyle(n,r);if(a&&a.content&&"none"!==a.content&&"-moz-alt-content"!==a.content&&"none"!==a.display){var o=a.content+"",i=o.substr(0,1);i===o.substr(o.length-1)&&i.match(/'|"/)&&(o=o.substr(1,o.length-2));var l="url"===o.substr(0,3),s=e.createElement(l?"img":"span");return s.className=me+"-before "+me+"-after",Object.keys(a).filter(W).forEach(function(t){try{s.style[t]=a[t]}catch(e){ue.log(["Tried to assign readonly property ",t,"Error:",e])}}),l?s.src=ue.parseBackgroundImage(o)[0].args[0]:s.innerHTML=o,s}}function W(e){return isNaN(t.parseInt(e,10))}function H(t,e){var n=G(t,":before"),r=G(t,":after");(n||r)&&(n&&(t.className+=" "+me+"-before",t.parentNode.insertBefore(n,t),oe(n,e,!0),t.parentNode.removeChild(n),t.className=t.className.replace(me+"-before","").trim()),r&&(t.className+=" "+me+"-after",t.appendChild(r),oe(r,e,!0),t.removeChild(r),t.className=t.className.replace(me+"-after","").trim()))}function j(t,e,n,r){var a=Math.round(r.left+n.left),o=Math.round(r.top+n.top);t.createPattern(e),t.translate(a,o),t.fill(),t.translate(-a,-o)}function q(t,e,n,r,a,o,i,l){var s=[];s.push(["line",Math.round(a),Math.round(o)]),s.push(["line",Math.round(a+i),Math.round(o)]),s.push(["line",Math.round(a+i),Math.round(l+o)]),s.push(["line",Math.round(a),Math.round(l+o)]),F(t,s),t.save(),t.clip(),j(t,e,n,r),t.restore()}function X(t,e,n){d(t,e.left,e.top,e.width,e.height,n)}function _(t,e,n,r,a){var o=ue.BackgroundSize(t,e,r,a),i=ue.BackgroundPosition(t,e,r,a,o),l=ge(t,"backgroundRepeat").split(",").map(ue.trimText);switch(r=Q(r,o),l=l[a]||l[0]){case"repeat-x":q(n,r,i,e,e.left,e.top+i.top,99999,r.height);break;case"repeat-y":q(n,r,i,e,e.left+i.left,e.top,r.width,99999);break;case"no-repeat":q(n,r,i,e,e.left+i.left,e.top+i.top,r.width,r.height);break;default:j(n,r,i,{top:e.top,left:e.left,width:r.width,height:r.height})}}function Y(t,e,n){for(var r,a=ge(t,"backgroundImage"),o=ue.parseBackgroundImage(a),i=o.length;i--;)if(a=o[i],a.args&&0!==a.args.length){var l="url"===a.method?a.args[0]:a.value;r=M(l),r?_(t,e,n,r,i):ue.log("html2canvas: Error loading background:",a)}}function Q(t,e){if(t.width===e.width&&t.height===e.height)return t;var n,r=de.createElement("canvas");return r.width=e.width,r.height=e.height,n=r.getContext("2d"),$(n,t,0,0,t.width,t.height,0,0,e.width,e.height),r}function J(t,e,n){return t.setVariable("globalAlpha",ge(e,"opacity")*(n?n.opacity:1))}function K(t){return t.replace("px","")}function Z(t){var e=ge(t,"transform")||ge(t,"-webkit-transform")||ge(t,"-moz-transform")||ge(t,"-ms-transform")||ge(t,"-o-transform"),n=ge(t,"transform-origin")||ge(t,"-webkit-transform-origin")||ge(t,"-moz-transform-origin")||ge(t,"-ms-transform-origin")||ge(t,"-o-transform-origin")||"0px 0px";n=n.split(" ").map(K).map(ue.asFloat);var r;if(e&&"none"!==e){var a=e.match(we);if(a)switch(a[1]){case"matrix":r=a[2].split(",").map(ue.trimText).map(ue.asFloat)}}return{origin:n,matrix:r}}function te(t,e,n,r){var s=l(e?n.width:o(),e?n.height:i()),c={ctx:s,opacity:J(s,t,e),cssPosition:ge(t,"position"),borders:L(t),transform:r,clip:e&&e.clip?ue.Extend({},e.clip):null};return E(t,c,e),a.useOverflow===!0&&/(hidden|scroll|auto)/.test(ge(t,"overflow"))===!0&&/(BODY)/i.test(t.nodeName)===!1&&(c.clip=c.clip?R(c.clip,n):n),c}function ee(t,e,n){var r={left:e.left+t[3].width,top:e.top+t[0].width,width:e.width-(t[1].width+t[3].width),height:e.height-(t[0].width+t[2].width)};return n&&(r=R(r,n)),r}function ne(t,e){var n=e.matrix?ue.OffsetBounds(t):ue.Bounds(t);return e.origin[0]+=n.left,e.origin[1]+=n.top,n}function re(t,e,n,r){var a,o=Z(t,e),i=ne(t,o),l=te(t,e,i,o),s=l.borders,c=l.ctx,d=ee(s,i,l.clip),u=P(t,i,s),h=fe.test(t.nodeName)?"#efefef":ge(t,"backgroundColor");switch(F(c,u.clip),c.save(),c.clip(),d.height>0&&d.width>0&&!r?(X(c,i,h),Y(t,d,c)):r&&(l.backgroundColor=h),c.restore(),u.borders.forEach(function(t){V(c,t.args,t.color)}),n||H(t,l),t.nodeName){case"IMG":(a=M(t.getAttribute("src")))?I(c,t,a,i,s):ue.log("html2canvas: Error loading :"+t.getAttribute("src"));break;case"INPUT":/^(text|url|email|submit|button|reset)$/.test(t.type)&&(t.value||t.placeholder||"").length>0&&D(t,i,l);break;case"TEXTAREA":(t.value||t.placeholder||"").length>0&&D(t,i,l);break;case"SELECT":(t.options||t.placeholder||"").length>0&&D(t,i,l);break;case"LI":S(t,l,d);break;case"CANVAS":I(c,t,t,i,s)}return l}function ae(t){return"none"!==ge(t,"display")&&"hidden"!==ge(t,"visibility")&&!t.hasAttribute("data-html2canvas-ignore")}function oe(t,e,n){ae(t)&&(e=re(t,e,n,!1)||e,fe.test(t.nodeName)||ie(t,e,n))}function ie(t,e,n){ue.Children(t).forEach(function(r){r.nodeType===r.ELEMENT_NODE?oe(r,e,n):r.nodeType===r.TEXT_NODE&&v(t,r,e)})}function le(){var t=ge(e.documentElement,"backgroundColor"),n=ue.isTransparent(t)&&se===e.body,r=re(se,null,!1,n);return ie(se,r),n&&(t=r.backgroundColor),pe.removeChild(ye),{backgroundColor:t,stack:r}}t.scroll(0,0);var se=a.elements===n?e.body:a.elements[0],ce=0,de=se.ownerDocument,ue=u.Util,he=ue.Support(a,de),fe=RegExp("("+a.ignoreElements+")"),pe=de.body,ge=ue.getCSS,me="___html2canvas___pseudoelement",ye=de.createElement("style");ye.innerHTML="."+me+'-before:before { content: "" !important; display: none !important; }'+"."+me+'-after:after { content: "" !important; display: none !important; }',pe.appendChild(ye),r=r||{};var be=function(t){return function(e,n,r,a){var o=r*t,i=a*t,l=e+r,s=n+a;return{topLeft:z({x:e,y:s},{x:e,y:s-i},{x:l-o,y:n},{x:l,y:n}),topRight:z({x:e,y:n},{x:e+o,y:n},{x:l,y:s-i},{x:l,y:s}),bottomRight:z({x:l,y:n},{x:l,y:n+i},{x:e+o,y:s},{x:e,y:s}),bottomLeft:z({x:l,y:s},{x:l-o,y:s},{x:e,y:n+i},{x:e,y:n})}}}(4*((Math.sqrt(2)-1)/3)),we=/(matrix)\((.+)\)/;return le()},u.Preload=function(r){function a(t){M.href=t,M.href=M.href;var e=M.protocol+M.host;return e===g}function o(){x.log("html2canvas: start: images: "+w.numLoaded+" / "+w.numTotal+" (failed: "+w.numFailed+")"),!w.firstRun&&w.numLoaded>=w.numTotal&&(x.log("Finished loading images: # "+w.numTotal+" (failed: "+w.numFailed+")"),"function"==typeof r.complete&&r.complete(w))}function i(e,a,i){var l,s,c=r.proxy;M.href=e,e=M.href,l="html2canvas_"+v++,i.callbackname=l,c+=c.indexOf("?")>-1?"&":"?",c+="url="+encodeURIComponent(e)+"&callback="+l,s=C.createElement("script"),t[l]=function(e){"error:"===e.substring(0,6)?(i.succeeded=!1,w.numLoaded++,w.numFailed++,o()):(p(a,i),a.src=e),t[l]=n;try{delete t[l]}catch(r){}s.parentNode.removeChild(s),s=null,delete i.script,delete i.callbackname},s.setAttribute("type","text/javascript"),s.setAttribute("src",c),i.script=s,t.document.body.appendChild(s)}function l(e,n){var r=t.getComputedStyle(e,n),a=r.content;"url"===a.substr(0,3)&&m.loadImage(u.Util.parseBackgroundImage(a)[0].args[0]),h(r.backgroundImage,e)}function s(t){l(t,":before"),l(t,":after")}function c(t,e){var r=u.Generate.Gradient(t,e);r!==n&&(w[t]={img:r,succeeded:!0},w.numTotal++,w.numLoaded++,o())}function d(t){return t&&t.method&&t.args&&t.args.length>0}function h(t,e){var r;u.Util.parseBackgroundImage(t).filter(d).forEach(function(t){"url"===t.method?m.loadImage(t.args[0]):t.method.match(/\-?gradient$/)&&(r===n&&(r=u.Util.Bounds(e)),c(t.value,r))})}function f(t){var e=!1;try{x.Children(t).forEach(f)}catch(r){}try{e=t.nodeType}catch(a){e=!1,x.log("html2canvas: failed to access some element's nodeType - Exception: "+a.message)}if(1===e||e===n){s(t);try{h(x.getCSS(t,"backgroundImage"),t)}catch(r){x.log("html2canvas: failed to get background-image - Exception: "+r.message)}h(t)}}function p(e,a){e.onload=function(){a.timer!==n&&t.clearTimeout(a.timer),w.numLoaded++,a.succeeded=!0,e.onerror=e.onload=null,o()},e.onerror=function(){if("anonymous"===e.crossOrigin&&(t.clearTimeout(a.timer),r.proxy)){var l=e.src;return e=new Image,a.img=e,e.src=l,i(e.src,e,a),n}w.numLoaded++,w.numFailed++,a.succeeded=!1,e.onerror=e.onload=null,o()}}var g,m,y,b,w={numLoaded:0,numFailed:0,numTotal:0,cleanupDone:!1},x=u.Util,v=0,k=r.elements[0]||e.body,C=k.ownerDocument,T=k.getElementsByTagName("img"),S=T.length,M=C.createElement("a"),R=function(t){return t.crossOrigin!==n}(new Image);for(M.href=t.location.href,g=M.protocol+M.host,m={loadImage:function(t){var e,o;t&&w[t]===n&&(e=new Image,t.match(/data:image\/.*;base64,/i)?(e.src=t.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),o=w[t]={img:e},w.numTotal++,p(e,o)):a(t)||r.allowTaint===!0?(o=w[t]={img:e},w.numTotal++,p(e,o),e.src=t):R&&!r.allowTaint&&r.useCORS?(e.crossOrigin="anonymous",o=w[t]={img:e},w.numTotal++,p(e,o),e.src=t):r.proxy&&(o=w[t]={img:e},w.numTotal++,i(t,e,o)))},cleanupDOM:function(a){var i,l;if(!w.cleanupDone){a&&"string"==typeof a?x.log("html2canvas: Cleanup because: "+a):x.log("html2canvas: Cleanup after timeout: "+r.timeout+" ms.");for(l in w)if(w.hasOwnProperty(l)&&(i=w[l],"object"==typeof i&&i.callbackname&&i.succeeded===n)){t[i.callbackname]=n;try{delete t[i.callbackname]}catch(s){}i.script&&i.script.parentNode&&(i.script.setAttribute("src","about:blank"),i.script.parentNode.removeChild(i.script)),w.numLoaded++,w.numFailed++,x.log("html2canvas: Cleaned up failed img: '"+l+"' Steps: "+w.numLoaded+" / "+w.numTotal)}t.stop!==n?t.stop():e.execCommand!==n&&e.execCommand("Stop",!1),e.close!==n&&e.close(),w.cleanupDone=!0,a&&"string"==typeof a||o()}},renderingDone:function(){b&&t.clearTimeout(b)}},r.timeout>0&&(b=t.setTimeout(m.cleanupDOM,r.timeout)),x.log("html2canvas: Preload starts: finding background-images"),w.firstRun=!0,f(k),x.log("html2canvas: Preload: Finding images"),y=0;S>y;y+=1)m.loadImage(T[y].getAttribute("src"));return w.firstRun=!1,x.log("html2canvas: Preload: Done."),w.numTotal===w.numLoaded&&o(),m},u.Renderer=function(t,r){function a(t){function e(t){Object.keys(t).sort().forEach(function(n){var r=[],o=[],i=[],l=[]; +t[n].forEach(function(t){t.node.zIndex.isPositioned||1>t.node.zIndex.opacity?i.push(t):t.node.zIndex.isFloated?o.push(t):r.push(t)}),function s(t){t.forEach(function(t){l.push(t),t.children&&s(t.children)})}(r.concat(o,i)),l.forEach(function(t){t.context?e(t.context):a.push(t.node)})})}var r,a=[];return r=function(t){function e(t,r,a){var o="auto"===r.zIndex.zindex?0:Number(r.zIndex.zindex),i=t,l=r.zIndex.isPositioned,s=r.zIndex.isFloated,c={node:r},d=a;r.zIndex.ownStacking?(i=c.context={"!":[{node:r,children:[]}]},d=n):(l||s)&&(d=c.children=[]),0===o&&a?a.push(c):(t[o]||(t[o]=[]),t[o].push(c)),r.zIndex.children.forEach(function(t){e(i,t,d)})}var r={};return e(r,t),r}(t),e(r),a}function o(t){var e;if("string"==typeof r.renderer&&u.Renderer[t]!==n)e=u.Renderer[t](r);else{if("function"!=typeof t)throw Error("Unknown renderer");e=t(r)}if("function"!=typeof e)throw Error("Invalid renderer defined");return e}return o(r.renderer)(t,r,e,a(t.stack),u)},u.Util.Support=function(t,e){function r(){var t=new Image,r=e.createElement("canvas"),a=r.getContext===n?!1:r.getContext("2d");if(a===!1)return!1;r.width=r.height=10,t.src=["data:image/svg+xml,","","","
","sup","
","
","
"].join("");try{a.drawImage(t,0,0),r.toDataURL()}catch(o){return!1}return u.Util.log("html2canvas: Parse: SVG powered rendering available"),!0}function a(){var t,n,r,a,o=!1;return e.createRange&&(t=e.createRange(),t.getBoundingClientRect&&(n=e.createElement("boundtest"),n.style.height="123px",n.style.display="block",e.body.appendChild(n),t.selectNode(n),r=t.getBoundingClientRect(),a=r.height,123===a&&(o=!0),e.body.removeChild(n))),o}return{rangeBounds:a(),svgRendering:t.svgRendering&&r()}},t.html2canvas=function(e,n){e=e.length?e:[e];var r,a,o={logging:!1,elements:e,background:"#fff",proxy:null,timeout:0,useCORS:!1,allowTaint:!1,svgRendering:!1,ignoreElements:"IFRAME|OBJECT|PARAM",useOverflow:!0,letterRendering:!1,chinese:!1,width:null,height:null,taintTest:!0,renderer:"Canvas"};return o=u.Util.Extend(n,o),u.logging=o.logging,o.complete=function(t){("function"!=typeof o.onpreloaded||o.onpreloaded(t)!==!1)&&(r=u.Parse(t,o),("function"!=typeof o.onparsed||o.onparsed(r)!==!1)&&(a=u.Renderer(r,o),"function"==typeof o.onrendered&&o.onrendered(a)))},t.setTimeout(function(){u.Preload(o)},0),{render:function(t,e){return u.Renderer(t,u.Util.Extend(e,o))},parse:function(t,e){return u.Parse(t,u.Util.Extend(e,o))},preload:function(t){return u.Preload(u.Util.Extend(t,o))},log:u.Util.log}},t.html2canvas.log=u.Util.log,t.html2canvas.Renderer={Canvas:n},u.Renderer.Canvas=function(t){function r(t,e){t.beginPath(),e.forEach(function(e){t[e.name].apply(t,e.arguments)}),t.closePath()}function a(t){if(-1===l.indexOf(t.arguments[0].src)){c.drawImage(t.arguments[0],0,0);try{c.getImageData(0,0,1,1)}catch(e){return s=i.createElement("canvas"),c=s.getContext("2d"),!1}l.push(t.arguments[0].src)}return!0}function o(e,n){switch(n.type){case"variable":e[n.name]=n.arguments;break;case"function":switch(n.name){case"createPattern":if(n.arguments[0].width>0&&n.arguments[0].height>0)try{e.fillStyle=e.createPattern(n.arguments[0],"repeat")}catch(o){d.log("html2canvas: Renderer: Error creating pattern",o.message)}break;case"drawShape":r(e,n.arguments);break;case"drawImage":n.arguments[8]>0&&n.arguments[7]>0&&(!t.taintTest||t.taintTest&&a(n))&&e.drawImage.apply(e,n.arguments);break;default:e[n.name].apply(e,n.arguments)}}}t=t||{};var i=e,l=[],s=e.createElement("canvas"),c=s.getContext("2d"),d=u.Util,h=t.canvas||i.createElement("canvas");return function(t,e,r,a,i){var l,s,c,u=h.getContext("2d"),f=t.stack;return h.width=h.style.width=e.width||f.ctx.width,h.height=h.style.height=e.height||f.ctx.height,c=u.fillStyle,u.fillStyle=d.isTransparent(f.backgroundColor)&&e.background!==n?e.background:t.backgroundColor,u.fillRect(0,0,h.width,h.height),u.fillStyle=c,a.forEach(function(t){u.textBaseline="bottom",u.save(),t.transform.matrix&&(u.translate(t.transform.origin[0],t.transform.origin[1]),u.transform.apply(u,t.transform.matrix),u.translate(-t.transform.origin[0],-t.transform.origin[1])),t.clip&&(u.beginPath(),u.rect(t.clip.left,t.clip.top,t.clip.width,t.clip.height),u.clip()),t.ctx.storage&&t.ctx.storage.forEach(function(t){o(u,t)}),u.restore()}),d.log("html2canvas: Renderer: Canvas renderer done - returning canvas obj"),1===e.elements.length&&"object"==typeof e.elements[0]&&"BODY"!==e.elements[0].nodeName?(s=i.Util.Bounds(e.elements[0]),l=r.createElement("canvas"),l.width=Math.ceil(s.width),l.height=Math.ceil(s.height),u=l.getContext("2d"),u.drawImage(h,s.left,s.top,s.width,s.height,0,0,s.width,s.height),h=null,l):h}}})(window,document); \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/examples/demo.html b/Apollo/bower_components/html2canvas/examples/demo.html new file mode 100755 index 00000000..41f24673 --- /dev/null +++ b/Apollo/bower_components/html2canvas/examples/demo.html @@ -0,0 +1,183 @@ + + + + + display/box/float/clear test + + + + +
+
+ toggle +
+
+
    +
  • + the way +
  • +
  • +

    + the world ends +

    +
    +

    + bang + +

    +

    + whimper + +

    +
    +
  • +
  • + i grow old +
  • +
  • + pluot? +
  • +
+
+
+ bar maids, +
+
+

+ sing to me, erbarme dich +

+
+
+

+ This is a nonsensical document, but syntactically valid HTML 4.0. All 100% conformant CSS1 agents should be able to render the document elements above this paragraph indistinguishably (to the pixel) from this reference rendering, (except font rasterization and form widgets). All discrepancies should be traceable to CSS1 implementation shortcomings. Once you have finished evaluating this test, you can return to the parent page. +

+ + + + diff --git a/Apollo/bower_components/html2canvas/examples/demo2.html b/Apollo/bower_components/html2canvas/examples/demo2.html new file mode 100755 index 00000000..89c90139 --- /dev/null +++ b/Apollo/bower_components/html2canvas/examples/demo2.html @@ -0,0 +1,65 @@ + + + + + + + + + + +
+
+
+

Heading

+ Text that isn't wrapped in anything. +

Followed by some text wrapped in a <p> paragraph.

+ Maybe add a link or a different style of link with a highlight. +
+

More content

+
a
+
+ +
+ +
+
+ + + + diff --git a/Apollo/bower_components/html2canvas/package.json b/Apollo/bower_components/html2canvas/package.json new file mode 100755 index 00000000..c84885cf --- /dev/null +++ b/Apollo/bower_components/html2canvas/package.json @@ -0,0 +1,46 @@ +{ + "title": "html2canvas", + "name": "html2canvas", + "description": "Screenshots with JavaScript", + "version": "0.4.1", + "author": { + "name": "Niklas von Hertzen", + "email": "niklasvh@gmail.com", + "url": "http://hertzen.com" + }, + "engines": { + "node": ">=0.8.0" + }, + "dependencies": {}, + "repository": { + "type": "git", + "url": "git@github.com:niklasvh/html2canvas.git" + }, + "bugs": { + "url": "https://github.com/niklasvh/html2canvas/issues" + }, + "devDependencies": { + "grunt": ">=0.4.0", + "grunt-contrib-concat": "*", + "grunt-contrib-uglify": "*", + "grunt-contrib-jshint": "*", + "grunt-contrib-qunit": "*", + "grunt-contrib-watch": "~0.5.1", + "googleapis": "~0.4.3", + "jwt-sign": "~0.1.0", + "base64-arraybuffer": ">= 0.1.0", + "png-js": ">= 0.1.1", + "sync-webdriver": ">=0.1.1", + "express": "~3.2.3", + "baconjs": "~0.3.15" + }, + "scripts": { + "test": "grunt travis --verbose" + }, + "homepage": "http://html2canvas.hertzen.com", + "licenses": [ + { + "type": "MIT" + } + ] +} diff --git a/Apollo/bower_components/html2canvas/readme.md b/Apollo/bower_components/html2canvas/readme.md new file mode 100755 index 00000000..18f00b43 --- /dev/null +++ b/Apollo/bower_components/html2canvas/readme.md @@ -0,0 +1,122 @@ +html2canvas +=========== + +### Current build status ### +[![Build Status](https://travis-ci.org/niklasvh/html2canvas.png)](https://travis-ci.org/niklasvh/html2canvas) + +#### JavaScript HTML renderer #### + + The script allows you to take "screenshots" of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page. + + +###How does it work?### +The script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements. + +It does **not require any rendering from the server**, as the whole image is created on the **clients browser**. However, as it is heavily dependent on the browser, this library is *not suitable* to be used in nodejs. +It doesn't magically circumvent any browser content policy restrictions either, so rendering cross-origin content will require a [proxy](https://github.com/niklasvh/html2canvas/wiki/Proxies) to get the content to the [same origin](http://en.wikipedia.org/wiki/Same_origin_policy). + +The script is still in a **very experimental state**, so I don't recommend using it in a production environment nor start building applications with it yet, as there will be still major changes made. + +###Browser compatibility### + +The script should work fine on the following browsers: + +* Firefox 3.5+ +* Google Chrome +* Opera 12+ +* IE9+ +* Safari 6+ + +As each CSS property needs to be manually built to be supported, there are a number of properties that are not yet supported. + +### Usage ### +To render an `element` with html2canvas, simply call: +` html2canvas(element, options);` + +To access the created canvas, provide the `onrendered` event in the options which returns the canvas element as the first argument, as such: + + html2canvas(document.body, { + onrendered: function(canvas) { + /* canvas is the actual canvas element, + to append it to the page call for example + document.body.appendChild( canvas ); + */ + } + }); + +### Building ### + +The library uses [grunt](http://gruntjs.com/) for building. Alternatively, you can download the latest build from [here](http://html2canvas.hertzen.com/build/html2canvas.js). + +Run the full build process (including lint, qunit and webdriver tests): + + $ grunt + +Skip lint and tests and simply build from source: + + $ grunt build + +### Running tests ### + +The library has two sets of tests. The first set is a number of qunit tests that check that different values parsed by browsers are correctly converted in html2canvas. To run these tests with grunt you'll need [phantomjs](http://phantomjs.org/). + +The other set of tests run Firefox, Chrome and Internet Explorer with [webdriver](https://github.com/niklasvh/webdriver.js). The selenium standalone server (runs on Java) is required for these tests and can be downloaded from [here](http://code.google.com/p/selenium/downloads/list). They capture an actual screenshot from the test pages and compare the image to the screenshot created by html2canvas and calculate the percentage differences. These tests generally aren't expected to provide 100% matches, but while commiting changes, these should generally not go decrease from the baseline values. + +Start by downloading the dependencies: + + $ npm install + +Run qunit tests: + + $ grunt test + +### Examples ### + +For more information and examples, please visit the [homepage](http://html2canvas.hertzen.com) or try the [test console](http://html2canvas.hertzen.com/screenshots.html). + +### Contributing ### + +If you wish to contribute to the project, please send the pull requests to the develop branch. Before submitting any changes, try and test that the changes work with all the support browsers. If some CSS property isn't supported or is incomplete, please create appropriate tests for it as well before submitting any code changes. + +### Changelog ### + +v0.4.1 - 7.9.2013 + * Added support for bower + * Improved z-index ordering + * Basic implementation for CSS transformations + * Fixed inline text in top element + * Basic implementation for text-shadow + +v0.4.0 - 30.1.2013 + * Added rendering tests with webdriver + * Switched to using grunt for building + * Removed support for IE<9, including any FlashCanvas bits + * Support for border-radius + * Support for multiple background images, size, and clipping + * Support for :before and :after pseudo elements + * Support for placeholder rendering + * Reformatted all tests to small units to test specific features + +v0.3.4 - 26.6.2012 + +* Removed (last?) jQuery dependencies (niklasvh) +* SVG-powered rendering (niklasvh) +* Radial gradients (SunboX) +* Split renderers to their own objects (niklasvh) +* Simplified API, cleaned up code (niklasvh) + +v0.3.3 - 2.3.2012 + +* SVG taint fix, and additional taint testing options for rendering (niklasvh) +* Added support for CORS images and option to create canvas as tainted (niklasvh) +* Improved minification saved ~1K! (cobexer) +* Added integrated support for Flashcanvas (niklasvh) +* Fixed a variety of legacy IE bugs (niklasvh) + +v0.3.2 - 20.2.2012 + +* Added changelog! +* Added bookmarklet (cobexer) +* Option to select single element to render (niklasvh) +* Fixed closure compiler warnings (cobexer) +* Enable profiling in FF (cobexer) diff --git a/Apollo/bower_components/html2canvas/src/Core.js b/Apollo/bower_components/html2canvas/src/Core.js new file mode 100755 index 00000000..fc8e6e8a --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Core.js @@ -0,0 +1,410 @@ +"use strict"; + +var _html2canvas = {}, +previousElement, +computedCSS, +html2canvas; + +_html2canvas.Util = {}; + +_html2canvas.Util.log = function(a) { + if (_html2canvas.logging && window.console && window.console.log) { + window.console.log(a); + } +}; + +_html2canvas.Util.trimText = (function(isNative){ + return function(input) { + return isNative ? isNative.apply(input) : ((input || '') + '').replace( /^\s+|\s+$/g , '' ); + }; +})(String.prototype.trim); + +_html2canvas.Util.asFloat = function(v) { + return parseFloat(v); +}; + +(function() { + // TODO: support all possible length values + var TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g; + var TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g; + _html2canvas.Util.parseTextShadows = function (value) { + if (!value || value === 'none') { + return []; + } + + // find multiple shadow declarations + var shadows = value.match(TEXT_SHADOW_PROPERTY), + results = []; + for (var i = 0; shadows && (i < shadows.length); i++) { + var s = shadows[i].match(TEXT_SHADOW_VALUES); + results.push({ + color: s[0], + offsetX: s[1] ? s[1].replace('px', '') : 0, + offsetY: s[2] ? s[2].replace('px', '') : 0, + blur: s[3] ? s[3].replace('px', '') : 0 + }); + } + return results; + }; +})(); + + +_html2canvas.Util.parseBackgroundImage = function (value) { + var whitespace = ' \r\n\t', + method, definition, prefix, prefix_i, block, results = [], + c, mode = 0, numParen = 0, quote, args; + + var appendResult = function(){ + if(method) { + if(definition.substr( 0, 1 ) === '"') { + definition = definition.substr( 1, definition.length - 2 ); + } + if(definition) { + args.push(definition); + } + if(method.substr( 0, 1 ) === '-' && + (prefix_i = method.indexOf( '-', 1 ) + 1) > 0) { + prefix = method.substr( 0, prefix_i); + method = method.substr( prefix_i ); + } + results.push({ + prefix: prefix, + method: method.toLowerCase(), + value: block, + args: args + }); + } + args = []; //for some odd reason, setting .length = 0 didn't work in safari + method = + prefix = + definition = + block = ''; + }; + + appendResult(); + for(var i = 0, ii = value.length; i -1){ + continue; + } + switch(c) { + case '"': + if(!quote) { + quote = c; + } + else if(quote === c) { + quote = null; + } + break; + + case '(': + if(quote) { break; } + else if(mode === 0) { + mode = 1; + block += c; + continue; + } else { + numParen++; + } + break; + + case ')': + if(quote) { break; } + else if(mode === 1) { + if(numParen === 0) { + mode = 0; + block += c; + appendResult(); + continue; + } else { + numParen--; + } + } + break; + + case ',': + if(quote) { break; } + else if(mode === 0) { + appendResult(); + continue; + } + else if (mode === 1) { + if(numParen === 0 && !method.match(/^url$/i)) { + args.push(definition); + definition = ''; + block += c; + continue; + } + } + break; + } + + block += c; + if(mode === 0) { method += c; } + else { definition += c; } + } + appendResult(); + + return results; +}; + +_html2canvas.Util.Bounds = function (element) { + var clientRect, bounds = {}; + + if (element.getBoundingClientRect){ + clientRect = element.getBoundingClientRect(); + + // TODO add scroll position to bounds, so no scrolling of window necessary + bounds.top = clientRect.top; + bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height); + bounds.left = clientRect.left; + + bounds.width = element.offsetWidth; + bounds.height = element.offsetHeight; + } + + return bounds; +}; + +// TODO ideally, we'd want everything to go through this function instead of Util.Bounds, +// but would require further work to calculate the correct positions for elements with offsetParents +_html2canvas.Util.OffsetBounds = function (element) { + var parent = element.offsetParent ? _html2canvas.Util.OffsetBounds(element.offsetParent) : {top: 0, left: 0}; + + return { + top: element.offsetTop + parent.top, + bottom: element.offsetTop + element.offsetHeight + parent.top, + left: element.offsetLeft + parent.left, + width: element.offsetWidth, + height: element.offsetHeight + }; +}; + +function toPX(element, attribute, value ) { + var rsLeft = element.runtimeStyle && element.runtimeStyle[attribute], + left, + style = element.style; + + // Check if we are not dealing with pixels, (Opera has issues with this) + // Ported from jQuery css.js + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + + if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( value ) && /^-?\d/.test(value) ) { + // Remember the original values + left = style.left; + + // Put in the new values to get a computed value out + if (rsLeft) { + element.runtimeStyle.left = element.currentStyle.left; + } + style.left = attribute === "fontSize" ? "1em" : (value || 0); + value = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if (rsLeft) { + element.runtimeStyle.left = rsLeft; + } + } + + if (!/^(thin|medium|thick)$/i.test(value)) { + return Math.round(parseFloat(value)) + "px"; + } + + return value; +} + +function asInt(val) { + return parseInt(val, 10); +} + +function parseBackgroundSizePosition(value, element, attribute, index) { + value = (value || '').split(','); + value = value[index || 0] || value[0] || 'auto'; + value = _html2canvas.Util.trimText(value).split(' '); + + if(attribute === 'backgroundSize' && (!value[0] || value[0].match(/cover|contain|auto/))) { + //these values will be handled in the parent function + } else { + value[0] = (value[0].indexOf( "%" ) === -1) ? toPX(element, attribute + "X", value[0]) : value[0]; + if(value[1] === undefined) { + if(attribute === 'backgroundSize') { + value[1] = 'auto'; + return value; + } else { + // IE 9 doesn't return double digit always + value[1] = value[0]; + } + } + value[1] = (value[1].indexOf("%") === -1) ? toPX(element, attribute + "Y", value[1]) : value[1]; + } + return value; +} + +_html2canvas.Util.getCSS = function (element, attribute, index) { + if (previousElement !== element) { + computedCSS = document.defaultView.getComputedStyle(element, null); + } + + var value = computedCSS[attribute]; + + if (/^background(Size|Position)$/.test(attribute)) { + return parseBackgroundSizePosition(value, element, attribute, index); + } else if (/border(Top|Bottom)(Left|Right)Radius/.test(attribute)) { + var arr = value.split(" "); + if (arr.length <= 1) { + arr[1] = arr[0]; + } + return arr.map(asInt); + } + + return value; +}; + +_html2canvas.Util.resizeBounds = function( current_width, current_height, target_width, target_height, stretch_mode ){ + var target_ratio = target_width / target_height, + current_ratio = current_width / current_height, + output_width, output_height; + + if(!stretch_mode || stretch_mode === 'auto') { + output_width = target_width; + output_height = target_height; + } else if(target_ratio < current_ratio ^ stretch_mode === 'contain') { + output_height = target_height; + output_width = target_height * current_ratio; + } else { + output_width = target_width; + output_height = target_width / current_ratio; + } + + return { + width: output_width, + height: output_height + }; +}; + +function backgroundBoundsFactory( prop, el, bounds, image, imageIndex, backgroundSize ) { + var bgposition = _html2canvas.Util.getCSS( el, prop, imageIndex ) , + topPos, + left, + percentage, + val; + + if (bgposition.length === 1){ + val = bgposition[0]; + + bgposition = []; + + bgposition[0] = val; + bgposition[1] = val; + } + + if (bgposition[0].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[0])/100); + left = bounds.width * percentage; + if(prop !== 'backgroundSize') { + left -= (backgroundSize || image).width*percentage; + } + } else { + if(prop === 'backgroundSize') { + if(bgposition[0] === 'auto') { + left = image.width; + } else { + if (/contain|cover/.test(bgposition[0])) { + var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, bgposition[0]); + left = resized.width; + topPos = resized.height; + } else { + left = parseInt(bgposition[0], 10); + } + } + } else { + left = parseInt( bgposition[0], 10); + } + } + + + if(bgposition[1] === 'auto') { + topPos = left / image.width * image.height; + } else if (bgposition[1].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[1])/100); + topPos = bounds.height * percentage; + if(prop !== 'backgroundSize') { + topPos -= (backgroundSize || image).height * percentage; + } + + } else { + topPos = parseInt(bgposition[1],10); + } + + return [left, topPos]; +} + +_html2canvas.Util.BackgroundPosition = function( el, bounds, image, imageIndex, backgroundSize ) { + var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize ); + return { left: result[0], top: result[1] }; +}; + +_html2canvas.Util.BackgroundSize = function( el, bounds, image, imageIndex ) { + var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex ); + return { width: result[0], height: result[1] }; +}; + +_html2canvas.Util.Extend = function (options, defaults) { + for (var key in options) { + if (options.hasOwnProperty(key)) { + defaults[key] = options[key]; + } + } + return defaults; +}; + + +/* + * Derived from jQuery.contents() + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ +_html2canvas.Util.Children = function( elem ) { + var children; + try { + children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? elem.contentDocument || elem.contentWindow.document : (function(array) { + var ret = []; + if (array !== null) { + (function(first, second ) { + var i = first.length, + j = 0; + + if (typeof second.length === "number") { + for (var l = second.length; j < l; j++) { + first[i++] = second[j]; + } + } else { + while (second[j] !== undefined) { + first[i++] = second[j++]; + } + } + + first.length = i; + + return first; + })(ret, array); + } + return ret; + })(elem.childNodes); + + } catch (ex) { + _html2canvas.Util.log("html2canvas.Util.Children failed with exception: " + ex.message); + children = []; + } + return children; +}; + +_html2canvas.Util.isTransparent = function(backgroundColor) { + return (backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)"); +}; \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/src/Font.js b/Apollo/bower_components/html2canvas/src/Font.js new file mode 100755 index 00000000..2bc264bb --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Font.js @@ -0,0 +1,64 @@ +_html2canvas.Util.Font = (function () { + + var fontData = {}; + + return function(font, fontSize, doc) { + if (fontData[font + "-" + fontSize] !== undefined) { + return fontData[font + "-" + fontSize]; + } + + var container = doc.createElement('div'), + img = doc.createElement('img'), + span = doc.createElement('span'), + sampleText = 'Hidden Text', + baseline, + middle, + metricsObj; + + container.style.visibility = "hidden"; + container.style.fontFamily = font; + container.style.fontSize = fontSize; + container.style.margin = 0; + container.style.padding = 0; + + doc.body.appendChild(container); + + // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) + img.src = "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs="; + img.width = 1; + img.height = 1; + + img.style.margin = 0; + img.style.padding = 0; + img.style.verticalAlign = "baseline"; + + span.style.fontFamily = font; + span.style.fontSize = fontSize; + span.style.margin = 0; + span.style.padding = 0; + + span.appendChild(doc.createTextNode(sampleText)); + container.appendChild(span); + container.appendChild(img); + baseline = (img.offsetTop - span.offsetTop) + 1; + + container.removeChild(span); + container.appendChild(doc.createTextNode(sampleText)); + + container.style.lineHeight = "normal"; + img.style.verticalAlign = "super"; + + middle = (img.offsetTop-container.offsetTop) + 1; + metricsObj = { + baseline: baseline, + lineWidth: 1, + middle: middle + }; + + fontData[font + "-" + fontSize] = metricsObj; + + doc.body.removeChild(container); + + return metricsObj; + }; +})(); diff --git a/Apollo/bower_components/html2canvas/src/Generate.js b/Apollo/bower_components/html2canvas/src/Generate.js new file mode 100755 index 00000000..bf3c7ccc --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Generate.js @@ -0,0 +1,424 @@ +(function(){ + var Util = _html2canvas.Util, + Generate = {}; + + _html2canvas.Generate = Generate; + + var reGradients = [ + /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)\-]+)\)$/, + /^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/, + /^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/, + /^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z\-]*)([\w\d\.\s,%\(\)]+)\)$/, + /^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/ + ]; + + /* + * TODO: Add IE10 vendor prefix (-ms) support + * TODO: Add W3C gradient (linear-gradient) support + * TODO: Add old Webkit -webkit-gradient(radial, ...) support + * TODO: Maybe some RegExp optimizations are possible ;o) + */ + Generate.parseGradient = function(css, bounds) { + var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl; + + for(i = 0; i < len; i+=1){ + m1 = css.match(reGradients[i]); + if(m1) { + break; + } + } + + if(m1) { + switch(m1[1]) { + case '-webkit-linear-gradient': + case '-o-linear-gradient': + + gradient = { + type: 'linear', + x0: null, + y0: null, + x1: null, + y1: null, + colorStops: [] + }; + + // get coordinates + m2 = m1[2].match(/\w+/g); + if(m2){ + m2Len = m2.length; + for(i = 0; i < m2Len; i+=1){ + switch(m2[i]) { + case 'top': + gradient.y0 = 0; + gradient.y1 = bounds.height; + break; + + case 'right': + gradient.x0 = bounds.width; + gradient.x1 = 0; + break; + + case 'bottom': + gradient.y0 = bounds.height; + gradient.y1 = 0; + break; + + case 'left': + gradient.x0 = 0; + gradient.x1 = bounds.width; + break; + } + } + } + if(gradient.x0 === null && gradient.x1 === null){ // center + gradient.x0 = gradient.x1 = bounds.width / 2; + } + if(gradient.y0 === null && gradient.y1 === null){ // center + gradient.y0 = gradient.y1 = bounds.height / 2; + } + + // get colors and stops + m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3] === '%'){ + stop /= 100; + } else { // px - stupid opera + stop /= bounds.width; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + + case '-webkit-gradient': + + gradient = { + type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions + x0: 0, + y0: 0, + x1: 0, + y1: 0, + colorStops: [] + }; + + // get coordinates + m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/); + if(m2){ + gradient.x0 = (m2[1] * bounds.width) / 100; + gradient.y0 = (m2[2] * bounds.height) / 100; + gradient.x1 = (m2[3] * bounds.width) / 100; + gradient.y1 = (m2[4] * bounds.height) / 100; + } + + // get colors and stops + m2 = m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g); + if(m2){ + m2Len = m2.length; + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/); + stop = parseFloat(m3[2]); + if(m3[1] === 'from') { + stop = 0.0; + } + if(m3[1] === 'to') { + stop = 1.0; + } + gradient.colorStops.push({ + color: m3[3], + stop: stop + }); + } + } + break; + + case '-moz-linear-gradient': + + gradient = { + type: 'linear', + x0: 0, + y0: 0, + x1: 0, + y1: 0, + colorStops: [] + }; + + // get coordinates + m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); + + // m2[1] == 0% -> left + // m2[1] == 50% -> center + // m2[1] == 100% -> right + + // m2[2] == 0% -> top + // m2[2] == 50% -> center + // m2[2] == 100% -> bottom + + if(m2){ + gradient.x0 = (m2[1] * bounds.width) / 100; + gradient.y0 = (m2[2] * bounds.height) / 100; + gradient.x1 = bounds.width - gradient.x0; + gradient.y1 = bounds.height - gradient.y0; + } + + // get colors and stops + m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3]){ // percentage + stop /= 100; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + + case '-webkit-radial-gradient': + case '-moz-radial-gradient': + case '-o-radial-gradient': + + gradient = { + type: 'circle', + x0: 0, + y0: 0, + x1: bounds.width, + y1: bounds.height, + cx: 0, + cy: 0, + rx: 0, + ry: 0, + colorStops: [] + }; + + // center + m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); + if(m2){ + gradient.cx = (m2[1] * bounds.width) / 100; + gradient.cy = (m2[2] * bounds.height) / 100; + } + + // size + m2 = m1[3].match(/\w+/); + m3 = m1[4].match(/[a-z\-]*/); + if(m2 && m3){ + switch(m3[0]){ + case 'farthest-corner': + case 'cover': // is equivalent to farthest-corner + case '': // mozilla removes "cover" from definition :( + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.max(tl, tr, br, bl); + break; + case 'closest-corner': + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.min(tl, tr, br, bl); + break; + case 'farthest-side': + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.max( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // ellipse + + gradient.type = m2[0]; + + gradient.rx = Math.max( + gradient.cx, + gradient.x1 - gradient.cx + ); + gradient.ry = Math.max( + gradient.cy, + gradient.y1 - gradient.cy + ); + } + break; + case 'closest-side': + case 'contain': // is equivalent to closest-side + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.min( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // ellipse + + gradient.type = m2[0]; + + gradient.rx = Math.min( + gradient.cx, + gradient.x1 - gradient.cx + ); + gradient.ry = Math.min( + gradient.cy, + gradient.y1 - gradient.cy + ); + } + break; + + // TODO: add support for "30px 40px" sizes (webkit only) + } + } + + // color stops + m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g); + if(m2){ + m2Len = m2.length; + step = 1 / Math.max(m2Len - 1, 1); + for(i = 0; i < m2Len; i+=1){ + m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/); + if(m3[2]){ + stop = parseFloat(m3[2]); + if(m3[3] === '%'){ + stop /= 100; + } else { // px - stupid opera + stop /= bounds.width; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + } + } + + return gradient; + }; + + function addScrollStops(grad) { + return function(colorStop) { + try { + grad.addColorStop(colorStop.stop, colorStop.color); + } + catch(e) { + Util.log(['failed to add color stop: ', e, '; tried to add: ', colorStop]); + } + }; + } + + Generate.Gradient = function(src, bounds) { + if(bounds.width === 0 || bounds.height === 0) { + return; + } + + var canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'), + gradient, grad; + + canvas.width = bounds.width; + canvas.height = bounds.height; + + // TODO: add support for multi defined background gradients + gradient = _html2canvas.Generate.parseGradient(src, bounds); + + if(gradient) { + switch(gradient.type) { + case 'linear': + grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); + gradient.colorStops.forEach(addScrollStops(grad)); + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + break; + + case 'circle': + grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); + gradient.colorStops.forEach(addScrollStops(grad)); + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + break; + + case 'ellipse': + var canvasRadial = document.createElement('canvas'), + ctxRadial = canvasRadial.getContext('2d'), + ri = Math.max(gradient.rx, gradient.ry), + di = ri * 2; + + canvasRadial.width = canvasRadial.height = di; + + grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); + gradient.colorStops.forEach(addScrollStops(grad)); + + ctxRadial.fillStyle = grad; + ctxRadial.fillRect(0, 0, di, di); + + ctx.fillStyle = gradient.colorStops[gradient.colorStops.length - 1].color; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); + break; + } + } + + return canvas; + }; + + Generate.ListAlpha = function(number) { + var tmp = "", + modulus; + + do { + modulus = number % 26; + tmp = String.fromCharCode((modulus) + 64) + tmp; + number = number / 26; + }while((number*26) > 26); + + return tmp; + }; + + Generate.ListRoman = function(number) { + var romanArray = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"], + decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], + roman = "", + v, + len = romanArray.length; + + if (number <= 0 || number >= 4000) { + return number; + } + + for (v=0; v < len; v+=1) { + while (number >= decimal[v]) { + number -= decimal[v]; + roman += romanArray[v]; + } + } + + return roman; + }; +})(); \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/src/Parse.js b/Apollo/bower_components/html2canvas/src/Parse.js new file mode 100755 index 00000000..d1557895 --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Parse.js @@ -0,0 +1,1143 @@ +_html2canvas.Parse = function (images, options) { + window.scroll(0,0); + + var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default + numDraws = 0, + doc = element.ownerDocument, + Util = _html2canvas.Util, + support = Util.Support(options, doc), + ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), + body = doc.body, + getCSS = Util.getCSS, + pseudoHide = "___html2canvas___pseudoelement", + hidePseudoElements = doc.createElement('style'); + + hidePseudoElements.innerHTML = '.' + pseudoHide + '-before:before { content: "" !important; display: none !important; }' + + '.' + pseudoHide + '-after:after { content: "" !important; display: none !important; }'; + + body.appendChild(hidePseudoElements); + + images = images || {}; + + function documentWidth () { + return Math.max( + Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth), + Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth), + Math.max(doc.body.clientWidth, doc.documentElement.clientWidth) + ); + } + + function documentHeight () { + return Math.max( + Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight), + Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight), + Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) + ); + } + + function getCSSInt(element, attribute) { + var val = parseInt(getCSS(element, attribute), 10); + return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html + } + + function renderRect (ctx, x, y, w, h, bgcolor) { + if (bgcolor !== "transparent"){ + ctx.setVariable("fillStyle", bgcolor); + ctx.fillRect(x, y, w, h); + numDraws+=1; + } + } + + function capitalize(m, p1, p2) { + if (m.length > 0) { + return p1 + p2.toUpperCase(); + } + } + + function textTransform (text, transform) { + switch(transform){ + case "lowercase": + return text.toLowerCase(); + case "capitalize": + return text.replace( /(^|\s|:|-|\(|\))([a-z])/g, capitalize); + case "uppercase": + return text.toUpperCase(); + default: + return text; + } + } + + function noLetterSpacing(letter_spacing) { + return (/^(normal|none|0px)$/.test(letter_spacing)); + } + + function drawText(currentText, x, y, ctx){ + if (currentText !== null && Util.trimText(currentText).length > 0) { + ctx.fillText(currentText, x, y); + numDraws+=1; + } + } + + function setTextVariables(ctx, el, text_decoration, color) { + var align = false, + bold = getCSS(el, "fontWeight"), + family = getCSS(el, "fontFamily"), + size = getCSS(el, "fontSize"), + shadows = Util.parseTextShadows(getCSS(el, "textShadow")); + + switch(parseInt(bold, 10)){ + case 401: + bold = "bold"; + break; + case 400: + bold = "normal"; + break; + } + + ctx.setVariable("fillStyle", color); + ctx.setVariable("font", [getCSS(el, "fontStyle"), getCSS(el, "fontVariant"), bold, size, family].join(" ")); + ctx.setVariable("textAlign", (align) ? "right" : "left"); + + if (shadows.length) { + // TODO: support multiple text shadows + // apply the first text shadow + ctx.setVariable("shadowColor", shadows[0].color); + ctx.setVariable("shadowOffsetX", shadows[0].offsetX); + ctx.setVariable("shadowOffsetY", shadows[0].offsetY); + ctx.setVariable("shadowBlur", shadows[0].blur); + } + + if (text_decoration !== "none"){ + return Util.Font(family, size, doc); + } + } + + function renderTextDecoration(ctx, text_decoration, bounds, metrics, color) { + switch(text_decoration) { + case "underline": + // Draws a line at the baseline of the font + // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size + renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); + break; + case "overline": + renderRect(ctx, bounds.left, Math.round(bounds.top), bounds.width, 1, color); + break; + case "line-through": + // TODO try and find exact position for line-through + renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); + break; + } + } + + function getTextBounds(state, text, textDecoration, isLast, transform) { + var bounds; + if (support.rangeBounds && !transform) { + if (textDecoration !== "none" || Util.trimText(text).length !== 0) { + bounds = textRangeBounds(text, state.node, state.textOffset); + } + state.textOffset += text.length; + } else if (state.node && typeof state.node.nodeValue === "string" ){ + var newTextNode = (isLast) ? state.node.splitText(text.length) : null; + bounds = textWrapperBounds(state.node, transform); + state.node = newTextNode; + } + return bounds; + } + + function textRangeBounds(text, textNode, textOffset) { + var range = doc.createRange(); + range.setStart(textNode, textOffset); + range.setEnd(textNode, textOffset + text.length); + return range.getBoundingClientRect(); + } + + function textWrapperBounds(oldTextNode, transform) { + var parent = oldTextNode.parentNode, + wrapElement = doc.createElement('wrapper'), + backupText = oldTextNode.cloneNode(true); + + wrapElement.appendChild(oldTextNode.cloneNode(true)); + parent.replaceChild(wrapElement, oldTextNode); + + var bounds = transform ? Util.OffsetBounds(wrapElement) : Util.Bounds(wrapElement); + parent.replaceChild(backupText, wrapElement); + return bounds; + } + + function renderText(el, textNode, stack) { + var ctx = stack.ctx, + color = getCSS(el, "color"), + textDecoration = getCSS(el, "textDecoration"), + textAlign = getCSS(el, "textAlign"), + metrics, + textList, + state = { + node: textNode, + textOffset: 0 + }; + + if (Util.trimText(textNode.nodeValue).length > 0) { + textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); + textAlign = textAlign.replace(["-webkit-auto"],["auto"]); + + textList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(textAlign) && noLetterSpacing(getCSS(el, "letterSpacing"))) ? + textNode.nodeValue.split(/(\b| )/) + : textNode.nodeValue.split(""); + + metrics = setTextVariables(ctx, el, textDecoration, color); + + if (options.chinese) { + textList.forEach(function(word, index) { + if (/.*[\u4E00-\u9FA5].*$/.test(word)) { + word = word.split(""); + word.unshift(index, 1); + textList.splice.apply(textList, word); + } + }); + } + + textList.forEach(function(text, index) { + var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix); + if (bounds) { + drawText(text, bounds.left, bounds.bottom, ctx); + renderTextDecoration(ctx, textDecoration, bounds, metrics, color); + } + }); + } + } + + function listPosition (element, val) { + var boundElement = doc.createElement( "boundelement" ), + originalType, + bounds; + + boundElement.style.display = "inline"; + + originalType = element.style.listStyleType; + element.style.listStyleType = "none"; + + boundElement.appendChild(doc.createTextNode(val)); + + element.insertBefore(boundElement, element.firstChild); + + bounds = Util.Bounds(boundElement); + element.removeChild(boundElement); + element.style.listStyleType = originalType; + return bounds; + } + + function elementIndex(el) { + var i = -1, + count = 1, + childs = el.parentNode.childNodes; + + if (el.parentNode) { + while(childs[++i] !== el) { + if (childs[i].nodeType === 1) { + count++; + } + } + return count; + } else { + return -1; + } + } + + function listItemText(element, type) { + var currentIndex = elementIndex(element), text; + switch(type){ + case "decimal": + text = currentIndex; + break; + case "decimal-leading-zero": + text = (currentIndex.toString().length === 1) ? currentIndex = "0" + currentIndex.toString() : currentIndex.toString(); + break; + case "upper-roman": + text = _html2canvas.Generate.ListRoman( currentIndex ); + break; + case "lower-roman": + text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase(); + break; + case "lower-alpha": + text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase(); + break; + case "upper-alpha": + text = _html2canvas.Generate.ListAlpha( currentIndex ); + break; + } + + return text + ". "; + } + + function renderListItem(element, stack, elBounds) { + var x, + text, + ctx = stack.ctx, + type = getCSS(element, "listStyleType"), + listBounds; + + if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { + text = listItemText(element, type); + listBounds = listPosition(element, text); + setTextVariables(ctx, element, "none", getCSS(element, "color")); + + if (getCSS(element, "listStylePosition") === "inside") { + ctx.setVariable("textAlign", "left"); + x = elBounds.left; + } else { + return; + } + + drawText(text, x, listBounds.bottom, ctx); + } + } + + function loadImage (src){ + var img = images[src]; + return (img && img.succeeded === true) ? img.img : false; + } + + function clipBounds(src, dst){ + var x = Math.max(src.left, dst.left), + y = Math.max(src.top, dst.top), + x2 = Math.min((src.left + src.width), (dst.left + dst.width)), + y2 = Math.min((src.top + src.height), (dst.top + dst.height)); + + return { + left:x, + top:y, + width:x2-x, + height:y2-y + }; + } + + function setZ(element, stack, parentStack){ + var newContext, + isPositioned = stack.cssPosition !== 'static', + zIndex = isPositioned ? getCSS(element, 'zIndex') : 'auto', + opacity = getCSS(element, 'opacity'), + isFloated = getCSS(element, 'cssFloat') !== 'none'; + + // https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context + // When a new stacking context should be created: + // the root element (HTML), + // positioned (absolutely or relatively) with a z-index value other than "auto", + // elements with an opacity value less than 1. (See the specification for opacity), + // on mobile WebKit and Chrome 22+, position: fixed always creates a new stacking context, even when z-index is "auto" (See this post) + + stack.zIndex = newContext = h2czContext(zIndex); + newContext.isPositioned = isPositioned; + newContext.isFloated = isFloated; + newContext.opacity = opacity; + newContext.ownStacking = (zIndex !== 'auto' || opacity < 1); + + if (parentStack) { + parentStack.zIndex.children.push(stack); + } + } + + function renderImage(ctx, element, image, bounds, borders) { + + var paddingLeft = getCSSInt(element, 'paddingLeft'), + paddingTop = getCSSInt(element, 'paddingTop'), + paddingRight = getCSSInt(element, 'paddingRight'), + paddingBottom = getCSSInt(element, 'paddingBottom'); + + drawImage( + ctx, + image, + 0, //sx + 0, //sy + image.width, //sw + image.height, //sh + bounds.left + paddingLeft + borders[3].width, //dx + bounds.top + paddingTop + borders[0].width, // dy + bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw + bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh + ); + } + + function getBorderData(element) { + return ["Top", "Right", "Bottom", "Left"].map(function(side) { + return { + width: getCSSInt(element, 'border' + side + 'Width'), + color: getCSS(element, 'border' + side + 'Color') + }; + }); + } + + function getBorderRadiusData(element) { + return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) { + return getCSS(element, 'border' + side + 'Radius'); + }); + } + + var getCurvePoints = (function(kappa) { + + return function(x, y, r1, r2) { + var ox = (r1) * kappa, // control point offset horizontal + oy = (r2) * kappa, // control point offset vertical + xm = x + r1, // x-middle + ym = y + r2; // y-middle + return { + topLeft: bezierCurve({ + x:x, + y:ym + }, { + x:x, + y:ym - oy + }, { + x:xm - ox, + y:y + }, { + x:xm, + y:y + }), + topRight: bezierCurve({ + x:x, + y:y + }, { + x:x + ox, + y:y + }, { + x:xm, + y:ym - oy + }, { + x:xm, + y:ym + }), + bottomRight: bezierCurve({ + x:xm, + y:y + }, { + x:xm, + y:y + oy + }, { + x:x + ox, + y:ym + }, { + x:x, + y:ym + }), + bottomLeft: bezierCurve({ + x:xm, + y:ym + }, { + x:xm - ox, + y:ym + }, { + x:x, + y:y + oy + }, { + x:x, + y:y + }) + }; + }; + })(4 * ((Math.sqrt(2) - 1) / 3)); + + function bezierCurve(start, startControl, endControl, end) { + + var lerp = function (a, b, t) { + return { + x:a.x + (b.x - a.x) * t, + y:a.y + (b.y - a.y) * t + }; + }; + + return { + start: start, + startControl: startControl, + endControl: endControl, + end: end, + subdivide: function(t) { + var ab = lerp(start, startControl, t), + bc = lerp(startControl, endControl, t), + cd = lerp(endControl, end, t), + abbc = lerp(ab, bc, t), + bccd = lerp(bc, cd, t), + dest = lerp(abbc, bccd, t); + return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)]; + }, + curveTo: function(borderArgs) { + borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]); + }, + curveToReversed: function(borderArgs) { + borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]); + } + }; + } + + function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) { + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]); + corner1[0].curveTo(borderArgs); + corner1[1].curveTo(borderArgs); + } else { + borderArgs.push(["line", x, y]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]); + } + } + + function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) { + var borderArgs = []; + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]); + outer1[1].curveTo(borderArgs); + } else { + borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]); + } + + if (radius2[0] > 0 || radius2[1] > 0) { + borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]); + outer2[0].curveTo(borderArgs); + borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]); + inner2[0].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c2[0], borderData.c2[1]]); + borderArgs.push([ "line", borderData.c3[0], borderData.c3[1]]); + } + + if (radius1[0] > 0 || radius1[1] > 0) { + borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]); + inner1[1].curveToReversed(borderArgs); + } else { + borderArgs.push([ "line", borderData.c4[0], borderData.c4[1]]); + } + + return borderArgs; + } + + function calculateCurvePoints(bounds, borderRadius, borders) { + + var x = bounds.left, + y = bounds.top, + width = bounds.width, + height = bounds.height, + + tlh = borderRadius[0][0], + tlv = borderRadius[0][1], + trh = borderRadius[1][0], + trv = borderRadius[1][1], + brh = borderRadius[2][0], + brv = borderRadius[2][1], + blh = borderRadius[3][0], + blv = borderRadius[3][1], + + topWidth = width - trh, + rightHeight = height - brv, + bottomWidth = width - brh, + leftHeight = height - blv; + + return { + topLeftOuter: getCurvePoints( + x, + y, + tlh, + tlv + ).topLeft.subdivide(0.5), + + topLeftInner: getCurvePoints( + x + borders[3].width, + y + borders[0].width, + Math.max(0, tlh - borders[3].width), + Math.max(0, tlv - borders[0].width) + ).topLeft.subdivide(0.5), + + topRightOuter: getCurvePoints( + x + topWidth, + y, + trh, + trv + ).topRight.subdivide(0.5), + + topRightInner: getCurvePoints( + x + Math.min(topWidth, width + borders[3].width), + y + borders[0].width, + (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, + trv - borders[0].width + ).topRight.subdivide(0.5), + + bottomRightOuter: getCurvePoints( + x + bottomWidth, + y + rightHeight, + brh, + brv + ).bottomRight.subdivide(0.5), + + bottomRightInner: getCurvePoints( + x + Math.min(bottomWidth, width + borders[3].width), + y + Math.min(rightHeight, height + borders[0].width), + Math.max(0, brh - borders[1].width), + Math.max(0, brv - borders[2].width) + ).bottomRight.subdivide(0.5), + + bottomLeftOuter: getCurvePoints( + x, + y + leftHeight, + blh, + blv + ).bottomLeft.subdivide(0.5), + + bottomLeftInner: getCurvePoints( + x + borders[3].width, + y + leftHeight, + Math.max(0, blh - borders[3].width), + Math.max(0, blv - borders[2].width) + ).bottomLeft.subdivide(0.5) + }; + } + + function getBorderClip(element, borderPoints, borders, radius, bounds) { + var backgroundClip = getCSS(element, 'backgroundClip'), + borderArgs = []; + + switch(backgroundClip) { + case "content-box": + case "padding-box": + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width); + break; + + default: + parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top); + parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top); + parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height); + parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height); + break; + } + + return borderArgs; + } + + function parseBorders(element, bounds, borders){ + var x = bounds.left, + y = bounds.top, + width = bounds.width, + height = bounds.height, + borderSide, + bx, + by, + bw, + bh, + borderArgs, + // http://www.w3.org/TR/css3-background/#the-border-radius + borderRadius = getBorderRadiusData(element), + borderPoints = calculateCurvePoints(bounds, borderRadius, borders), + borderData = { + clip: getBorderClip(element, borderPoints, borders, borderRadius, bounds), + borders: [] + }; + + for (borderSide = 0; borderSide < 4; borderSide++) { + + if (borders[borderSide].width > 0) { + bx = x; + by = y; + bw = width; + bh = height - (borders[2].width); + + switch(borderSide) { + case 0: + // top border + bh = borders[0].width; + + borderArgs = drawSide({ + c1: [bx, by], + c2: [bx + bw, by], + c3: [bx + bw - borders[1].width, by + bh], + c4: [bx + borders[3].width, by + bh] + }, borderRadius[0], borderRadius[1], + borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner); + break; + case 1: + // right border + bx = x + width - (borders[1].width); + bw = borders[1].width; + + borderArgs = drawSide({ + c1: [bx + bw, by], + c2: [bx + bw, by + bh + borders[2].width], + c3: [bx, by + bh], + c4: [bx, by + borders[0].width] + }, borderRadius[1], borderRadius[2], + borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner); + break; + case 2: + // bottom border + by = (by + height) - (borders[2].width); + bh = borders[2].width; + + borderArgs = drawSide({ + c1: [bx + bw, by + bh], + c2: [bx, by + bh], + c3: [bx + borders[3].width, by], + c4: [bx + bw - borders[3].width, by] + }, borderRadius[2], borderRadius[3], + borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner); + break; + case 3: + // left border + bw = borders[3].width; + + borderArgs = drawSide({ + c1: [bx, by + bh + borders[2].width], + c2: [bx, by], + c3: [bx + bw, by + borders[0].width], + c4: [bx + bw, by + bh] + }, borderRadius[3], borderRadius[0], + borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner); + break; + } + + borderData.borders.push({ + args: borderArgs, + color: borders[borderSide].color + }); + + } + } + + return borderData; + } + + function createShape(ctx, args) { + var shape = ctx.drawShape(); + args.forEach(function(border, index) { + shape[(index === 0) ? "moveTo" : border[0] + "To" ].apply(null, border.slice(1)); + }); + return shape; + } + + function renderBorders(ctx, borderArgs, color) { + if (color !== "transparent") { + ctx.setVariable( "fillStyle", color); + createShape(ctx, borderArgs); + ctx.fill(); + numDraws+=1; + } + } + + function renderFormValue (el, bounds, stack){ + + var valueWrap = doc.createElement('valuewrap'), + cssPropertyArray = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], + textValue, + textNode; + + cssPropertyArray.forEach(function(property) { + try { + valueWrap.style[property] = getCSS(el, property); + } catch(e) { + // Older IE has issues with "border" + Util.log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message); + } + }); + + valueWrap.style.borderColor = "black"; + valueWrap.style.borderStyle = "solid"; + valueWrap.style.display = "block"; + valueWrap.style.position = "absolute"; + + if (/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName === "SELECT"){ + valueWrap.style.lineHeight = getCSS(el, "height"); + } + + valueWrap.style.top = bounds.top + "px"; + valueWrap.style.left = bounds.left + "px"; + + textValue = (el.nodeName === "SELECT") ? (el.options[el.selectedIndex] || 0).text : el.value; + if(!textValue) { + textValue = el.placeholder; + } + + textNode = doc.createTextNode(textValue); + + valueWrap.appendChild(textNode); + body.appendChild(valueWrap); + + renderText(el, textNode, stack); + body.removeChild(valueWrap); + } + + function drawImage (ctx) { + ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1)); + numDraws+=1; + } + + function getPseudoElement(el, which) { + var elStyle = window.getComputedStyle(el, which); + if(!elStyle || !elStyle.content || elStyle.content === "none" || elStyle.content === "-moz-alt-content" || elStyle.display === "none") { + return; + } + var content = elStyle.content + '', + first = content.substr( 0, 1 ); + //strips quotes + if(first === content.substr( content.length - 1 ) && first.match(/'|"/)) { + content = content.substr( 1, content.length - 2 ); + } + + var isImage = content.substr( 0, 3 ) === 'url', + elps = document.createElement( isImage ? 'img' : 'span' ); + + elps.className = pseudoHide + "-before " + pseudoHide + "-after"; + + Object.keys(elStyle).filter(indexedProperty).forEach(function(prop) { + // Prevent assigning of read only CSS Rules, ex. length, parentRule + try { + elps.style[prop] = elStyle[prop]; + } catch (e) { + Util.log(['Tried to assign readonly property ', prop, 'Error:', e]); + } + }); + + if(isImage) { + elps.src = Util.parseBackgroundImage(content)[0].args[0]; + } else { + elps.innerHTML = content; + } + return elps; + } + + function indexedProperty(property) { + return (isNaN(window.parseInt(property, 10))); + } + + function injectPseudoElements(el, stack) { + var before = getPseudoElement(el, ':before'), + after = getPseudoElement(el, ':after'); + if(!before && !after) { + return; + } + + if(before) { + el.className += " " + pseudoHide + "-before"; + el.parentNode.insertBefore(before, el); + parseElement(before, stack, true); + el.parentNode.removeChild(before); + el.className = el.className.replace(pseudoHide + "-before", "").trim(); + } + + if (after) { + el.className += " " + pseudoHide + "-after"; + el.appendChild(after); + parseElement(after, stack, true); + el.removeChild(after); + el.className = el.className.replace(pseudoHide + "-after", "").trim(); + } + + } + + function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) { + var offsetX = Math.round(bounds.left + backgroundPosition.left), + offsetY = Math.round(bounds.top + backgroundPosition.top); + + ctx.createPattern(image); + ctx.translate(offsetX, offsetY); + ctx.fill(); + ctx.translate(-offsetX, -offsetY); + } + + function backgroundRepeatShape(ctx, image, backgroundPosition, bounds, left, top, width, height) { + var args = []; + args.push(["line", Math.round(left), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(top)]); + args.push(["line", Math.round(left + width), Math.round(height + top)]); + args.push(["line", Math.round(left), Math.round(height + top)]); + createShape(ctx, args); + ctx.save(); + ctx.clip(); + renderBackgroundRepeat(ctx, image, backgroundPosition, bounds); + ctx.restore(); + } + + function renderBackgroundColor(ctx, backgroundBounds, bgcolor) { + renderRect( + ctx, + backgroundBounds.left, + backgroundBounds.top, + backgroundBounds.width, + backgroundBounds.height, + bgcolor + ); + } + + function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex) { + var backgroundSize = Util.BackgroundSize(el, bounds, image, imageIndex), + backgroundPosition = Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize), + backgroundRepeat = getCSS(el, "backgroundRepeat").split(",").map(Util.trimText); + + image = resizeImage(image, backgroundSize); + + backgroundRepeat = backgroundRepeat[imageIndex] || backgroundRepeat[0]; + + switch (backgroundRepeat) { + case "repeat-x": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left, bounds.top + backgroundPosition.top, 99999, image.height); + break; + + case "repeat-y": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top, image.width, 99999); + break; + + case "no-repeat": + backgroundRepeatShape(ctx, image, backgroundPosition, bounds, + bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height); + break; + + default: + renderBackgroundRepeat(ctx, image, backgroundPosition, { + top: bounds.top, + left: bounds.left, + width: image.width, + height: image.height + }); + break; + } + } + + function renderBackgroundImage(element, bounds, ctx) { + var backgroundImage = getCSS(element, "backgroundImage"), + backgroundImages = Util.parseBackgroundImage(backgroundImage), + image, + imageIndex = backgroundImages.length; + + while(imageIndex--) { + backgroundImage = backgroundImages[imageIndex]; + + if (!backgroundImage.args || backgroundImage.args.length === 0) { + continue; + } + + var key = backgroundImage.method === 'url' ? + backgroundImage.args[0] : + backgroundImage.value; + + image = loadImage(key); + + // TODO add support for background-origin + if (image) { + renderBackgroundRepeating(element, bounds, ctx, image, imageIndex); + } else { + Util.log("html2canvas: Error loading background:", backgroundImage); + } + } + } + + function resizeImage(image, bounds) { + if(image.width === bounds.width && image.height === bounds.height) { + return image; + } + + var ctx, canvas = doc.createElement('canvas'); + canvas.width = bounds.width; + canvas.height = bounds.height; + ctx = canvas.getContext("2d"); + drawImage(ctx, image, 0, 0, image.width, image.height, 0, 0, bounds.width, bounds.height ); + return canvas; + } + + function setOpacity(ctx, element, parentStack) { + return ctx.setVariable("globalAlpha", getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1)); + } + + function removePx(str) { + return str.replace("px", ""); + } + + var transformRegExp = /(matrix)\((.+)\)/; + + function getTransform(element, parentStack) { + var transform = getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform"); + var transformOrigin = getCSS(element, "transform-origin") || getCSS(element, "-webkit-transform-origin") || getCSS(element, "-moz-transform-origin") || getCSS(element, "-ms-transform-origin") || getCSS(element, "-o-transform-origin") || "0px 0px"; + + transformOrigin = transformOrigin.split(" ").map(removePx).map(Util.asFloat); + + var matrix; + if (transform && transform !== "none") { + var match = transform.match(transformRegExp); + if (match) { + switch(match[1]) { + case "matrix": + matrix = match[2].split(",").map(Util.trimText).map(Util.asFloat); + break; + } + } + } + + return { + origin: transformOrigin, + matrix: matrix + }; + } + + function createStack(element, parentStack, bounds, transform) { + var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height), + stack = { + ctx: ctx, + opacity: setOpacity(ctx, element, parentStack), + cssPosition: getCSS(element, "position"), + borders: getBorderData(element), + transform: transform, + clip: (parentStack && parentStack.clip) ? Util.Extend( {}, parentStack.clip ) : null + }; + + setZ(element, stack, parentStack); + + // TODO correct overflow for absolute content residing under a static position + if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(element, "overflow")) === true && /(BODY)/i.test(element.nodeName) === false){ + stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds; + } + + return stack; + } + + function getBackgroundBounds(borders, bounds, clip) { + var backgroundBounds = { + left: bounds.left + borders[3].width, + top: bounds.top + borders[0].width, + width: bounds.width - (borders[1].width + borders[3].width), + height: bounds.height - (borders[0].width + borders[2].width) + }; + + if (clip) { + backgroundBounds = clipBounds(backgroundBounds, clip); + } + + return backgroundBounds; + } + + function getBounds(element, transform) { + var bounds = (transform.matrix) ? Util.OffsetBounds(element) : Util.Bounds(element); + transform.origin[0] += bounds.left; + transform.origin[1] += bounds.top; + return bounds; + } + + function renderElement(element, parentStack, pseudoElement, ignoreBackground) { + var transform = getTransform(element, parentStack), + bounds = getBounds(element, transform), + image, + stack = createStack(element, parentStack, bounds, transform), + borders = stack.borders, + ctx = stack.ctx, + backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip), + borderData = parseBorders(element, bounds, borders), + backgroundColor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"); + + + createShape(ctx, borderData.clip); + + ctx.save(); + ctx.clip(); + + if (backgroundBounds.height > 0 && backgroundBounds.width > 0 && !ignoreBackground) { + renderBackgroundColor(ctx, bounds, backgroundColor); + renderBackgroundImage(element, backgroundBounds, ctx); + } else if (ignoreBackground) { + stack.backgroundColor = backgroundColor; + } + + ctx.restore(); + + borderData.borders.forEach(function(border) { + renderBorders(ctx, border.args, border.color); + }); + + if (!pseudoElement) { + injectPseudoElements(element, stack); + } + + switch(element.nodeName){ + case "IMG": + if ((image = loadImage(element.getAttribute('src')))) { + renderImage(ctx, element, image, bounds, borders); + } else { + Util.log("html2canvas: Error loading :" + element.getAttribute('src')); + } + break; + case "INPUT": + // TODO add all relevant type's, i.e. HTML5 new stuff + // todo add support for placeholder attribute for browsers which support it + if (/^(text|url|email|submit|button|reset)$/.test(element.type) && (element.value || element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "TEXTAREA": + if ((element.value || element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "SELECT": + if ((element.options||element.placeholder || "").length > 0){ + renderFormValue(element, bounds, stack); + } + break; + case "LI": + renderListItem(element, stack, backgroundBounds); + break; + case "CANVAS": + renderImage(ctx, element, element, bounds, borders); + break; + } + + return stack; + } + + function isElementVisible(element) { + return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore")); + } + + function parseElement (element, stack, pseudoElement) { + if (isElementVisible(element)) { + stack = renderElement(element, stack, pseudoElement, false) || stack; + if (!ignoreElementsRegExp.test(element.nodeName)) { + parseChildren(element, stack, pseudoElement); + } + } + } + + function parseChildren(element, stack, pseudoElement) { + Util.Children(element).forEach(function(node) { + if (node.nodeType === node.ELEMENT_NODE) { + parseElement(node, stack, pseudoElement); + } else if (node.nodeType === node.TEXT_NODE) { + renderText(element, node, stack); + } + }); + } + + function init() { + var background = getCSS(document.documentElement, "backgroundColor"), + transparentBackground = (Util.isTransparent(background) && element === document.body), + stack = renderElement(element, null, false, transparentBackground); + parseChildren(element, stack); + + if (transparentBackground) { + background = stack.backgroundColor; + } + + body.removeChild(hidePseudoElements); + return { + backgroundColor: background, + stack: stack + }; + } + + return init(); +}; + +function h2czContext(zindex) { + return { + zindex: zindex, + children: [] + }; +} diff --git a/Apollo/bower_components/html2canvas/src/Preload.js b/Apollo/bower_components/html2canvas/src/Preload.js new file mode 100755 index 00000000..f7b1ed78 --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Preload.js @@ -0,0 +1,317 @@ +_html2canvas.Preload = function( options ) { + + var images = { + numLoaded: 0, // also failed are counted here + numFailed: 0, + numTotal: 0, + cleanupDone: false + }, + pageOrigin, + Util = _html2canvas.Util, + methods, + i, + count = 0, + element = options.elements[0] || document.body, + doc = element.ownerDocument, + domImages = element.getElementsByTagName('img'), // Fetch images of the present element only + imgLen = domImages.length, + link = doc.createElement("a"), + supportCORS = (function( img ){ + return (img.crossOrigin !== undefined); + })(new Image()), + timeoutTimer; + + link.href = window.location.href; + pageOrigin = link.protocol + link.host; + + function isSameOrigin(url){ + link.href = url; + link.href = link.href; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ + var origin = link.protocol + link.host; + return (origin === pageOrigin); + } + + function start(){ + Util.log("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); + if (!images.firstRun && images.numLoaded >= images.numTotal){ + Util.log("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); + + if (typeof options.complete === "function"){ + options.complete(images); + } + + } + } + + // TODO modify proxy to serve images with CORS enabled, where available + function proxyGetImage(url, img, imageObj){ + var callback_name, + scriptUrl = options.proxy, + script; + + link.href = url; + url = link.href; // work around for pages with base href="" set - WARNING: this may change the url + + callback_name = 'html2canvas_' + (count++); + imageObj.callbackname = callback_name; + + if (scriptUrl.indexOf("?") > -1) { + scriptUrl += "&"; + } else { + scriptUrl += "?"; + } + scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; + script = doc.createElement("script"); + + window[callback_name] = function(a){ + if (a.substring(0,6) === "error:"){ + imageObj.succeeded = false; + images.numLoaded++; + images.numFailed++; + start(); + } else { + setImageLoadHandlers(img, imageObj); + img.src = a; + } + window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[callback_name]; // for all browser that support this + } catch(ex) {} + script.parentNode.removeChild(script); + script = null; + delete imageObj.script; + delete imageObj.callbackname; + }; + + script.setAttribute("type", "text/javascript"); + script.setAttribute("src", scriptUrl); + imageObj.script = script; + window.document.body.appendChild(script); + + } + + function loadPseudoElement(element, type) { + var style = window.getComputedStyle(element, type), + content = style.content; + if (content.substr(0, 3) === 'url') { + methods.loadImage(_html2canvas.Util.parseBackgroundImage(content)[0].args[0]); + } + loadBackgroundImages(style.backgroundImage, element); + } + + function loadPseudoElementImages(element) { + loadPseudoElement(element, ":before"); + loadPseudoElement(element, ":after"); + } + + function loadGradientImage(backgroundImage, bounds) { + var img = _html2canvas.Generate.Gradient(backgroundImage, bounds); + + if (img !== undefined){ + images[backgroundImage] = { + img: img, + succeeded: true + }; + images.numTotal++; + images.numLoaded++; + start(); + } + } + + function invalidBackgrounds(background_image) { + return (background_image && background_image.method && background_image.args && background_image.args.length > 0 ); + } + + function loadBackgroundImages(background_image, el) { + var bounds; + + _html2canvas.Util.parseBackgroundImage(background_image).filter(invalidBackgrounds).forEach(function(background_image) { + if (background_image.method === 'url') { + methods.loadImage(background_image.args[0]); + } else if(background_image.method.match(/\-?gradient$/)) { + if(bounds === undefined) { + bounds = _html2canvas.Util.Bounds(el); + } + loadGradientImage(background_image.value, bounds); + } + }); + } + + function getImages (el) { + var elNodeType = false; + + // Firefox fails with permission denied on pages with iframes + try { + Util.Children(el).forEach(getImages); + } + catch( e ) {} + + try { + elNodeType = el.nodeType; + } catch (ex) { + elNodeType = false; + Util.log("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); + } + + if (elNodeType === 1 || elNodeType === undefined) { + loadPseudoElementImages(el); + try { + loadBackgroundImages(Util.getCSS(el, 'backgroundImage'), el); + } catch(e) { + Util.log("html2canvas: failed to get background-image - Exception: " + e.message); + } + loadBackgroundImages(el); + } + } + + function setImageLoadHandlers(img, imageObj) { + img.onload = function() { + if ( imageObj.timer !== undefined ) { + // CORS succeeded + window.clearTimeout( imageObj.timer ); + } + + images.numLoaded++; + imageObj.succeeded = true; + img.onerror = img.onload = null; + start(); + }; + img.onerror = function() { + if (img.crossOrigin === "anonymous") { + // CORS failed + window.clearTimeout( imageObj.timer ); + + // let's try with proxy instead + if ( options.proxy ) { + var src = img.src; + img = new Image(); + imageObj.img = img; + img.src = src; + + proxyGetImage( img.src, img, imageObj ); + return; + } + } + + images.numLoaded++; + images.numFailed++; + imageObj.succeeded = false; + img.onerror = img.onload = null; + start(); + }; + } + + methods = { + loadImage: function( src ) { + var img, imageObj; + if ( src && images[src] === undefined ) { + img = new Image(); + if ( src.match(/data:image\/.*;base64,/i) ) { + img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + } else if ( isSameOrigin( src ) || options.allowTaint === true ) { + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + } else if ( supportCORS && !options.allowTaint && options.useCORS ) { + // attempt to load with CORS + + img.crossOrigin = "anonymous"; + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + } else if ( options.proxy ) { + imageObj = images[src] = { + img: img + }; + images.numTotal++; + proxyGetImage( src, img, imageObj ); + } + } + + }, + cleanupDOM: function(cause) { + var img, src; + if (!images.cleanupDone) { + if (cause && typeof cause === "string") { + Util.log("html2canvas: Cleanup because: " + cause); + } else { + Util.log("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); + } + + for (src in images) { + if (images.hasOwnProperty(src)) { + img = images[src]; + if (typeof img === "object" && img.callbackname && img.succeeded === undefined) { + // cancel proxy image request + window[img.callbackname] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[img.callbackname]; // for all browser that support this + } catch(ex) {} + if (img.script && img.script.parentNode) { + img.script.setAttribute("src", "about:blank"); // try to cancel running request + img.script.parentNode.removeChild(img.script); + } + images.numLoaded++; + images.numFailed++; + Util.log("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); + } + } + } + + // cancel any pending requests + if(window.stop !== undefined) { + window.stop(); + } else if(document.execCommand !== undefined) { + document.execCommand("Stop", false); + } + if (document.close !== undefined) { + document.close(); + } + images.cleanupDone = true; + if (!(cause && typeof cause === "string")) { + start(); + } + } + }, + + renderingDone: function() { + if (timeoutTimer) { + window.clearTimeout(timeoutTimer); + } + } + }; + + if (options.timeout > 0) { + timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); + } + + Util.log('html2canvas: Preload starts: finding background-images'); + images.firstRun = true; + + getImages(element); + + Util.log('html2canvas: Preload: Finding images'); + // load images + for (i = 0; i < imgLen; i+=1){ + methods.loadImage( domImages[i].getAttribute( "src" ) ); + } + + images.firstRun = false; + Util.log('html2canvas: Preload: Done.'); + if (images.numTotal === images.numLoaded) { + start(); + } + + return methods; +}; diff --git a/Apollo/bower_components/html2canvas/src/Queue.js b/Apollo/bower_components/html2canvas/src/Queue.js new file mode 100755 index 00000000..a0b2a8ac --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Queue.js @@ -0,0 +1,123 @@ +function h2cRenderContext(width, height) { + var storage = []; + return { + storage: storage, + width: width, + height: height, + clip: function() { + storage.push({ + type: "function", + name: "clip", + 'arguments': arguments + }); + }, + translate: function() { + storage.push({ + type: "function", + name: "translate", + 'arguments': arguments + }); + }, + fill: function() { + storage.push({ + type: "function", + name: "fill", + 'arguments': arguments + }); + }, + save: function() { + storage.push({ + type: "function", + name: "save", + 'arguments': arguments + }); + }, + restore: function() { + storage.push({ + type: "function", + name: "restore", + 'arguments': arguments + }); + }, + fillRect: function () { + storage.push({ + type: "function", + name: "fillRect", + 'arguments': arguments + }); + }, + createPattern: function() { + storage.push({ + type: "function", + name: "createPattern", + 'arguments': arguments + }); + }, + drawShape: function() { + + var shape = []; + + storage.push({ + type: "function", + name: "drawShape", + 'arguments': shape + }); + + return { + moveTo: function() { + shape.push({ + name: "moveTo", + 'arguments': arguments + }); + }, + lineTo: function() { + shape.push({ + name: "lineTo", + 'arguments': arguments + }); + }, + arcTo: function() { + shape.push({ + name: "arcTo", + 'arguments': arguments + }); + }, + bezierCurveTo: function() { + shape.push({ + name: "bezierCurveTo", + 'arguments': arguments + }); + }, + quadraticCurveTo: function() { + shape.push({ + name: "quadraticCurveTo", + 'arguments': arguments + }); + } + }; + + }, + drawImage: function () { + storage.push({ + type: "function", + name: "drawImage", + 'arguments': arguments + }); + }, + fillText: function () { + storage.push({ + type: "function", + name: "fillText", + 'arguments': arguments + }); + }, + setVariable: function (variable, value) { + storage.push({ + type: "variable", + name: variable, + 'arguments': value + }); + return value; + } + }; +} \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/src/Renderer.js b/Apollo/bower_components/html2canvas/src/Renderer.js new file mode 100755 index 00000000..99afa23e --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Renderer.js @@ -0,0 +1,101 @@ +_html2canvas.Renderer = function(parseQueue, options){ + + // http://www.w3.org/TR/CSS21/zindex.html + function createRenderQueue(parseQueue) { + var queue = [], + rootContext; + + rootContext = (function buildStackingContext(rootNode) { + var rootContext = {}; + function insert(context, node, specialParent) { + var zi = (node.zIndex.zindex === 'auto') ? 0 : Number(node.zIndex.zindex), + contextForChildren = context, // the stacking context for children + isPositioned = node.zIndex.isPositioned, + isFloated = node.zIndex.isFloated, + stub = {node: node}, + childrenDest = specialParent; // where children without z-index should be pushed into + + if (node.zIndex.ownStacking) { + // '!' comes before numbers in sorted array + contextForChildren = stub.context = { '!': [{node:node, children: []}]}; + childrenDest = undefined; + } else if (isPositioned || isFloated) { + childrenDest = stub.children = []; + } + + if (zi === 0 && specialParent) { + specialParent.push(stub); + } else { + if (!context[zi]) { context[zi] = []; } + context[zi].push(stub); + } + + node.zIndex.children.forEach(function(childNode) { + insert(contextForChildren, childNode, childrenDest); + }); + } + insert(rootContext, rootNode); + return rootContext; + })(parseQueue); + + function sortZ(context) { + Object.keys(context).sort().forEach(function(zi) { + var nonPositioned = [], + floated = [], + positioned = [], + list = []; + + // positioned after static + context[zi].forEach(function(v) { + if (v.node.zIndex.isPositioned || v.node.zIndex.opacity < 1) { + // http://www.w3.org/TR/css3-color/#transparency + // non-positioned element with opactiy < 1 should be stacked as if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’. + positioned.push(v); + } else if (v.node.zIndex.isFloated) { + floated.push(v); + } else { + nonPositioned.push(v); + } + }); + + (function walk(arr) { + arr.forEach(function(v) { + list.push(v); + if (v.children) { walk(v.children); } + }); + })(nonPositioned.concat(floated, positioned)); + + list.forEach(function(v) { + if (v.context) { + sortZ(v.context); + } else { + queue.push(v.node); + } + }); + }); + } + + sortZ(rootContext); + + return queue; + } + + function getRenderer(rendererName) { + var renderer; + + if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) { + renderer = _html2canvas.Renderer[rendererName](options); + } else if (typeof rendererName === "function") { + renderer = rendererName(options); + } else { + throw new Error("Unknown renderer"); + } + + if ( typeof renderer !== "function" ) { + throw new Error("Invalid renderer defined"); + } + return renderer; + } + + return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue.stack), _html2canvas); +}; diff --git a/Apollo/bower_components/html2canvas/src/Support.js b/Apollo/bower_components/html2canvas/src/Support.js new file mode 100755 index 00000000..34e26226 --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Support.js @@ -0,0 +1,63 @@ +_html2canvas.Util.Support = function (options, doc) { + + function supportSVGRendering() { + var img = new Image(), + canvas = doc.createElement("canvas"), + ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); + if (ctx === false) { + return false; + } + canvas.width = canvas.height = 10; + img.src = [ + "data:image/svg+xml,", + "", + "", + "
", + "sup", + "
", + "
", + "
" + ].join(""); + try { + ctx.drawImage(img, 0, 0); + canvas.toDataURL(); + } catch(e) { + return false; + } + _html2canvas.Util.log('html2canvas: Parse: SVG powered rendering available'); + return true; + } + + // Test whether we can use ranges to measure bounding boxes + // Opera doesn't provide valid bounds.height/bottom even though it supports the method. + + function supportRangeBounds() { + var r, testElement, rangeBounds, rangeHeight, support = false; + + if (doc.createRange) { + r = doc.createRange(); + if (r.getBoundingClientRect) { + testElement = doc.createElement('boundtest'); + testElement.style.height = "123px"; + testElement.style.display = "block"; + doc.body.appendChild(testElement); + + r.selectNode(testElement); + rangeBounds = r.getBoundingClientRect(); + rangeHeight = rangeBounds.height; + + if (rangeHeight === 123) { + support = true; + } + doc.body.removeChild(testElement); + } + } + + return support; + } + + return { + rangeBounds: supportRangeBounds(), + svgRendering: options.svgRendering && supportSVGRendering() + }; +}; \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/src/Util.js b/Apollo/bower_components/html2canvas/src/Util.js new file mode 100755 index 00000000..861402b1 --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/Util.js @@ -0,0 +1,81 @@ +window.html2canvas = function(elements, opts) { + elements = (elements.length) ? elements : [elements]; + var queue, + canvas, + options = { + // general + logging: false, + elements: elements, + background: "#fff", + + // preload options + proxy: null, + timeout: 0, // no timeout + useCORS: false, // try to load images as CORS (where available), before falling back to proxy + allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true + + // parse options + svgRendering: false, // use svg powered rendering where available (FF11+) + ignoreElements: "IFRAME|OBJECT|PARAM", + useOverflow: true, + letterRendering: false, + chinese: false, + + // render options + + width: null, + height: null, + taintTest: true, // do a taint test with all images before applying to canvas + renderer: "Canvas" + }; + + options = _html2canvas.Util.Extend(opts, options); + + _html2canvas.logging = options.logging; + options.complete = function( images ) { + + if (typeof options.onpreloaded === "function") { + if ( options.onpreloaded( images ) === false ) { + return; + } + } + queue = _html2canvas.Parse( images, options ); + + if (typeof options.onparsed === "function") { + if ( options.onparsed( queue ) === false ) { + return; + } + } + + canvas = _html2canvas.Renderer( queue, options ); + + if (typeof options.onrendered === "function") { + options.onrendered( canvas ); + } + + + }; + + // for pages without images, we still want this to be async, i.e. return methods before executing + window.setTimeout( function(){ + _html2canvas.Preload( options ); + }, 0 ); + + return { + render: function( queue, opts ) { + return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) ); + }, + parse: function( images, opts ) { + return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) ); + }, + preload: function( opts ) { + return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) ); + }, + log: _html2canvas.Util.log + }; +}; + +window.html2canvas.log = _html2canvas.Util.log; // for renderers +window.html2canvas.Renderer = { + Canvas: undefined // We are assuming this will be used +}; \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/src/renderers/Canvas.js b/Apollo/bower_components/html2canvas/src/renderers/Canvas.js new file mode 100755 index 00000000..ebebeba5 --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/renderers/Canvas.js @@ -0,0 +1,128 @@ +_html2canvas.Renderer.Canvas = function(options) { + options = options || {}; + + var doc = document, + safeImages = [], + testCanvas = document.createElement("canvas"), + testctx = testCanvas.getContext("2d"), + Util = _html2canvas.Util, + canvas = options.canvas || doc.createElement('canvas'); + + function createShape(ctx, args) { + ctx.beginPath(); + args.forEach(function(arg) { + ctx[arg.name].apply(ctx, arg['arguments']); + }); + ctx.closePath(); + } + + function safeImage(item) { + if (safeImages.indexOf(item['arguments'][0].src ) === -1) { + testctx.drawImage(item['arguments'][0], 0, 0); + try { + testctx.getImageData(0, 0, 1, 1); + } catch(e) { + testCanvas = doc.createElement("canvas"); + testctx = testCanvas.getContext("2d"); + return false; + } + safeImages.push(item['arguments'][0].src); + } + return true; + } + + function renderItem(ctx, item) { + switch(item.type){ + case "variable": + ctx[item.name] = item['arguments']; + break; + case "function": + switch(item.name) { + case "createPattern": + if (item['arguments'][0].width > 0 && item['arguments'][0].height > 0) { + try { + ctx.fillStyle = ctx.createPattern(item['arguments'][0], "repeat"); + } + catch(e) { + Util.log("html2canvas: Renderer: Error creating pattern", e.message); + } + } + break; + case "drawShape": + createShape(ctx, item['arguments']); + break; + case "drawImage": + if (item['arguments'][8] > 0 && item['arguments'][7] > 0) { + if (!options.taintTest || (options.taintTest && safeImage(item))) { + ctx.drawImage.apply( ctx, item['arguments'] ); + } + } + break; + default: + ctx[item.name].apply(ctx, item['arguments']); + } + break; + } + } + + return function(parsedData, options, document, queue, _html2canvas) { + var ctx = canvas.getContext("2d"), + newCanvas, + bounds, + fstyle, + zStack = parsedData.stack; + + canvas.width = canvas.style.width = options.width || zStack.ctx.width; + canvas.height = canvas.style.height = options.height || zStack.ctx.height; + + fstyle = ctx.fillStyle; + ctx.fillStyle = (Util.isTransparent(zStack.backgroundColor) && options.background !== undefined) ? options.background : parsedData.backgroundColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = fstyle; + + queue.forEach(function(storageContext) { + // set common settings for canvas + ctx.textBaseline = "bottom"; + ctx.save(); + + if (storageContext.transform.matrix) { + ctx.translate(storageContext.transform.origin[0], storageContext.transform.origin[1]); + ctx.transform.apply(ctx, storageContext.transform.matrix); + ctx.translate(-storageContext.transform.origin[0], -storageContext.transform.origin[1]); + } + + if (storageContext.clip){ + ctx.beginPath(); + ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); + ctx.clip(); + } + + if (storageContext.ctx.storage) { + storageContext.ctx.storage.forEach(function(item) { + renderItem(ctx, item); + }); + } + + ctx.restore(); + }); + + Util.log("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); + + if (options.elements.length === 1) { + if (typeof options.elements[0] === "object" && options.elements[0].nodeName !== "BODY") { + // crop image to the bounds of selected (single) element + bounds = _html2canvas.Util.Bounds(options.elements[0]); + newCanvas = document.createElement('canvas'); + newCanvas.width = Math.ceil(bounds.width); + newCanvas.height = Math.ceil(bounds.height); + ctx = newCanvas.getContext("2d"); + + ctx.drawImage(canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height); + canvas = null; + return newCanvas; + } + } + + return canvas; + }; +}; \ No newline at end of file diff --git a/Apollo/bower_components/html2canvas/src/renderers/SVG.js b/Apollo/bower_components/html2canvas/src/renderers/SVG.js new file mode 100755 index 00000000..e6350773 --- /dev/null +++ b/Apollo/bower_components/html2canvas/src/renderers/SVG.js @@ -0,0 +1,206 @@ +/* + html2canvas @VERSION@ + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ + + +// WARNING THIS file is outdated, and hasn't been tested in quite a while + +_html2canvas.Renderer.SVG = function( options ) { + + options = options || {}; + + var doc = document, + svgNS = "http://www.w3.org/2000/svg", + svg = doc.createElementNS(svgNS, "svg"), + xlinkNS = "http://www.w3.org/1999/xlink", + defs = doc.createElementNS(svgNS, "defs"), + i, + a, + queueLen, + storageLen, + storageContext, + renderItem, + el, + settings = {}, + text, + fontStyle, + clipId = 0, + methods; + + + methods = { + _create: function( zStack, options, doc, queue, _html2canvas ) { + svg.setAttribute("version", "1.1"); + svg.setAttribute("baseProfile", "full"); + + svg.setAttribute("viewBox", "0 0 " + Math.max(zStack.ctx.width, options.width) + " " + Math.max(zStack.ctx.height, options.height)); + svg.setAttribute("width", Math.max(zStack.ctx.width, options.width) + "px"); + svg.setAttribute("height", Math.max(zStack.ctx.height, options.height) + "px"); + svg.setAttribute("preserveAspectRatio", "none"); + svg.appendChild(defs); + + + + for (i = 0, queueLen = queue.length; i < queueLen; i+=1){ + + storageContext = queue.splice(0, 1)[0]; + storageContext.canvasPosition = storageContext.canvasPosition || {}; + + //this.canvasRenderContext(storageContext,parentctx); + + + /* + if (storageContext.clip){ + ctx.save(); + ctx.beginPath(); + // console.log(storageContext); + ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); + ctx.clip(); + + }*/ + + if (storageContext.ctx.storage){ + + for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ + + renderItem = storageContext.ctx.storage[a]; + + + + switch(renderItem.type){ + case "variable": + settings[renderItem.name] = renderItem['arguments']; + break; + case "function": + if (renderItem.name === "fillRect") { + + el = doc.createElementNS(svgNS, "rect"); + el.setAttribute("x", renderItem['arguments'][0]); + el.setAttribute("y", renderItem['arguments'][1]); + el.setAttribute("width", renderItem['arguments'][2]); + el.setAttribute("height", renderItem['arguments'][3]); + el.setAttribute("fill", settings.fillStyle); + svg.appendChild(el); + + } else if(renderItem.name === "fillText") { + el = doc.createElementNS(svgNS, "text"); + + fontStyle = settings.font.split(" "); + + el.style.fontVariant = fontStyle.splice(0, 1)[0]; + el.style.fontWeight = fontStyle.splice(0, 1)[0]; + el.style.fontStyle = fontStyle.splice(0, 1)[0]; + el.style.fontSize = fontStyle.splice(0, 1)[0]; + + el.setAttribute("x", renderItem['arguments'][1]); + el.setAttribute("y", renderItem['arguments'][2] - (parseInt(el.style.fontSize, 10) + 3)); + + el.setAttribute("fill", settings.fillStyle); + + + + + // TODO get proper baseline + el.style.dominantBaseline = "text-before-edge"; + el.style.fontFamily = fontStyle.join(" "); + + text = doc.createTextNode(renderItem['arguments'][0]); + el.appendChild(text); + + + svg.appendChild(el); + + + + } else if(renderItem.name === "drawImage") { + + if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ + + // TODO check whether even any clipping is necessary for this particular image + el = doc.createElementNS(svgNS, "clipPath"); + el.setAttribute("id", "clipId" + clipId); + + text = doc.createElementNS(svgNS, "rect"); + text.setAttribute("x", renderItem['arguments'][5] ); + text.setAttribute("y", renderItem['arguments'][6]); + + text.setAttribute("width", renderItem['arguments'][3]); + text.setAttribute("height", renderItem['arguments'][4]); + el.appendChild(text); + defs.appendChild(el); + + el = doc.createElementNS(svgNS, "image"); + el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src); + el.setAttribute("width", renderItem['arguments'][7]); + el.setAttribute("height", renderItem['arguments'][8]); + el.setAttribute("x", renderItem['arguments'][5]); + el.setAttribute("y", renderItem['arguments'][6]); + el.setAttribute("clip-path", "url(#clipId" + clipId + ")"); + // el.setAttribute("xlink:href", ); + + + el.setAttribute("preserveAspectRatio", "none"); + + svg.appendChild(el); + + + clipId += 1; + /* + ctx.drawImage( + renderItem['arguments'][0], + renderItem['arguments'][1], + renderItem['arguments'][2], + renderItem['arguments'][3], + renderItem['arguments'][4], + renderItem['arguments'][5], + renderItem['arguments'][6], + renderItem['arguments'][7], + renderItem['arguments'][8] + ); + */ + } + } + + + + break; + default: + + } + + } + + } + /* + if (storageContext.clip){ + ctx.restore(); + } + */ + + + + } + + + + + + + + + + + _html2canvas.Util.log("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj"); + + return svg; + } + }; + + return methods; + + +}; diff --git a/Apollo/bower_components/jsPDF/.bower.json b/Apollo/bower_components/jsPDF/.bower.json new file mode 100755 index 00000000..dcaf51b4 --- /dev/null +++ b/Apollo/bower_components/jsPDF/.bower.json @@ -0,0 +1,37 @@ +{ + "name": "jspdf", + "homepage": "https://github.com/mrrio/jspdf", + "description": "PDF Document creation from JavaScript", + "main": "dist/jspdf.debug.js", + "moduleType": [ + "amd", + "globals" + ], + "keywords": [ + "pdf" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "libs", + "CNAME", + "jspdf.js", + "examples/jspdf.PLUGINTEMPLATE.js", + "plugins/*", + "todo.txt", + "wscript.py", + "build.sh", + "test", + "tools" + ], + "version": "1.3.5", + "_release": "1.3.5", + "_resolution": { + "type": "version", + "tag": "v1.3.5", + "commit": "ff30727c294bc1e91f4eeac97bf73108f4794c84" + }, + "_source": "git://github.com/MrRio/jsPDF.git", + "_target": "*", + "_originalSource": "git://github.com/MrRio/jsPDF.git" +} \ No newline at end of file diff --git a/Apollo/bower_components/jsPDF/CODE_OF_CONDUCT.md b/Apollo/bower_components/jsPDF/CODE_OF_CONDUCT.md new file mode 100755 index 00000000..8050cd60 --- /dev/null +++ b/Apollo/bower_components/jsPDF/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at james@parall.ax. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Apollo/bower_components/jsPDF/HOTFIX_README.md b/Apollo/bower_components/jsPDF/HOTFIX_README.md new file mode 100755 index 00000000..dcd713e0 --- /dev/null +++ b/Apollo/bower_components/jsPDF/HOTFIX_README.md @@ -0,0 +1,53 @@ +#Hotfixes + +We sometimes bake-in solutions (A.K.A. hotfixes) to solve issues for specific use cases. + +When we deem a hotfix will not break existing code, + will make it default behaviour and mark the hotfix as _accepted_, + At that point the define can be removed. + + To enable a hotfix, define the following member of your created PDF, +where the pdf.hotfix field is the name of the hotfix. + + var pdf new jsPDF(...); + pdf.hotfix.fill_close = true; + +# Active Hotfixes +## px_scaling + +### Applies To +jsPDF Core + +### Description +When supplying 'px' as the unit for the PDF, the internal scaling factor was being miscalculated making drawn components +larger than they should be. Enabling this hotfix will correct this scaling calculation and items will be drawn to the +correct scale. + +### To Enable +To enable this hotfix, supply a 'hotfixes' array to the options object in the jsPDF constructor function, and add the +string 'px_scaling' to this array. + +#Accepted Hotfixes +## scale_text + +### Applies To +context2d plugin + +### Affects +Drawing and Filling Text when a scale transformation is active. + +### Description +jsPDF currently has no way to draw scaled text. +This hotfix scales the current font size by the x-axis scale factor. + +## fill_close +### Applies To +context2d plugin + +### Affects +Filling paths + +### Description +In certain cases, closing a fill would result in a path resolving to an incorrect point. +The was most likely fixed when we refactored matrix logic. Enabling this hotfix will ignore a most-likely unneeded workaround. + diff --git a/Apollo/bower_components/jsPDF/ISSUE_TEMPLATE.md b/Apollo/bower_components/jsPDF/ISSUE_TEMPLATE.md new file mode 100755 index 00000000..8d08e8b6 --- /dev/null +++ b/Apollo/bower_components/jsPDF/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +Thank you for submitting an issue to jsPDF. Please read carefully. + +**Are you using the latest version of jsPDF?** + +**Have you tried using jspdf.debug.js?** + +**Steps to reproduce** + +Ideally a link too. Try fork this http://jsbin.com/rilace/edit?html,js,output + +**What I saw** + +**What I expected** diff --git a/Apollo/bower_components/jsPDF/MIT-LICENSE.txt b/Apollo/bower_components/jsPDF/MIT-LICENSE.txt new file mode 100755 index 00000000..09b01db3 --- /dev/null +++ b/Apollo/bower_components/jsPDF/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2010-2016 James Hall, https://github.com/MrRio/jsPDF + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Apollo/bower_components/jsPDF/README.md b/Apollo/bower_components/jsPDF/README.md new file mode 100755 index 00000000..6cfd7141 --- /dev/null +++ b/Apollo/bower_components/jsPDF/README.md @@ -0,0 +1,95 @@ +# jsPDF + +[![Greenkeeper badge](https://badges.greenkeeper.io/MrRio/jsPDF.svg)](https://greenkeeper.io/) + +[![Build Status](https://saucelabs.com/buildstatus/jspdf)](https://saucelabs.com/beta/builds/526e7fda50bd4f97a854bf10f280305d) + +[![Code Climate](https://codeclimate.com/repos/57f943855cdc43705e00592f/badges/2665cddeba042dc5191f/gpa.svg)](https://codeclimate.com/repos/57f943855cdc43705e00592f/feed) [![Test Coverage](https://codeclimate.com/repos/57f943855cdc43705e00592f/badges/2665cddeba042dc5191f/coverage.svg)](https://codeclimate.com/repos/57f943855cdc43705e00592f/coverage) + +**A library to generate PDFs in client-side JavaScript.** + +You can [catch me on twitter](http://twitter.com/MrRio): [@MrRio](http://twitter.com/MrRio) or head over to [my company's website](http://parall.ax) for consultancy. + +## [Live Demo](http://rawgit.com/MrRio/jsPDF/master/) | [Documentation](http://rawgit.com/MrRio/jsPDF/master/docs/) + +## Creating your first document + +The easiest way to get started is to drop the CDN hosted library into your page: + +```html + +``` + +or can always get latest version via [unpkg](https://unpkg.com/#/) + +```html + +``` + +NPM + +```bash +npm i jspdf --save +``` + +Bower + +```bash +bower install jspdf --save +``` + +Then you're ready to start making your document: + +```javascript +// Default export is a4 paper, portrait, using milimeters for units +var doc = new jsPDF() + +doc.text('Hello world!', 10, 10) +doc.save('a4.pdf') +``` + +If you want to change the paper size, orientation, or units, you can do: + +```javascript +// Landscape export, 2×4 inches +var doc = new jsPDF({ + orientation: 'landscape', + unit: 'in', + format: [4, 2] +}) + +doc.text('Hello world!', 1, 1) +doc.save('two-by-four.pdf') +``` + +Great! Now give us a Star :) + +## Contributing +Build the library with `npm run build`. This will fetch all dependencies and then compile the `dist` files. To see the examples locally you can start a web server with `npm start` and go to `localhost:8000`. + +## Credits +- Big thanks to Daniel Dotsenko from [Willow Systems Corporation](http://willow-systems.com) for making huge contributions to the codebase. +- Thanks to Ajaxian.com for [featuring us back in 2009](http://ajaxian.com/archives/dynamically-generic-pdfs-with-javascript). +- Everyone else that's contributed patches or bug reports. You rock. + +## License (MIT) +Copyright (c) 2010-2017 James Hall, https://github.com/MrRio/jsPDF + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Apollo/bower_components/jsPDF/RELEASE.md b/Apollo/bower_components/jsPDF/RELEASE.md new file mode 100755 index 00000000..76d1dfc3 --- /dev/null +++ b/Apollo/bower_components/jsPDF/RELEASE.md @@ -0,0 +1,11 @@ +Release Instructions +==================== +PLEASE DO NOT POST A RELEASE UNLESS YOU HAVE ACCESS TO NPM + +- Add a new draft release in GitHub. +- Describe the Release +- Update the CDN link in the README (@TODO: Automate?) +- `npm version 1.x.y` +- git push origin v1.x.y +- Publish the release on GitHub +- `npm publish` diff --git a/Apollo/bower_components/jsPDF/bower.json b/Apollo/bower_components/jsPDF/bower.json new file mode 100755 index 00000000..9033b17a --- /dev/null +++ b/Apollo/bower_components/jsPDF/bower.json @@ -0,0 +1,27 @@ +{ + "name": "jspdf", + "homepage": "https://github.com/mrrio/jspdf", + "description": "PDF Document creation from JavaScript", + "main": "dist/jspdf.debug.js", + "moduleType": [ + "amd", + "globals" + ], + "keywords": [ + "pdf" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "libs", + "CNAME", + "jspdf.js", + "examples/jspdf.PLUGINTEMPLATE.js", + "plugins/*", + "todo.txt", + "wscript.py", + "build.sh", + "test", + "tools" + ] +} diff --git a/Apollo/bower_components/jsPDF/build.js b/Apollo/bower_components/jsPDF/build.js new file mode 100755 index 00000000..9076b99f --- /dev/null +++ b/Apollo/bower_components/jsPDF/build.js @@ -0,0 +1,110 @@ +'use strict' + +var fs = require('fs') +var rollup = require('rollup') +var uglify = require('uglify-js') +var babel = require('rollup-plugin-babel') +var execSync = require('child_process').execSync + +bundle({ + minified: 'dist/jspdf.min.js', + debug: 'dist/jspdf.debug.js' +}) + +// Monkey patching adler32 and filesaver +function monkeyPatch () { + return { + transform: (code, id) => { + var file = id.split('/').pop() + if (file === 'adler32cs.js') { + code = code.replace(/this, function/g, 'jsPDF, function') + code = code.replace(/require\('buffer'\)/g, '{}') + } + return code + } + } +} + +// Rollup removes local variables unless used within a module. +// This plugin makes sure specified local variables are preserved +// and kept local. This plugin wouldn't be necessary if es2015 +// modules would be used. +function rawjs (opts) { + opts = opts || {} + return { + transform: (code, id) => { + var variable = opts[id.split('/').pop()] + if (!variable) return code + + var keepStr = '/*rollup-keeper-start*/window.tmp=' + variable + + ';/*rollup-keeper-end*/' + return code + keepStr + }, + transformBundle: (code) => { + for (var file in opts) { + var r = new RegExp(opts[file] + '\\$\\d+', 'g') + code = code.replace(r, opts[file]) + } + var re = /\/\*rollup-keeper-start\*\/.*\/\*rollup-keeper-end\*\//g + return code.replace(re, '') + } + } +} + +function bundle (paths) { + rollup.rollup({ + entry: './main.js', + plugins: [ + monkeyPatch(), + rawjs({ + 'jspdf.js': 'jsPDF', + 'filesaver.tmp.js': 'saveAs', + 'deflate.js': 'Deflater', + 'zlib.js': 'FlateStream', + 'css_colors.js': 'CssColors', + 'html2pdf.js': 'html2pdf' + }), + babel({ + presets: ['es2015-rollup'], + exclude: ['node_modules/**', 'libs/**'] + }) + ] + }).then((bundle) => { + return bundle.generate({ + format: 'umd', + moduleName: 'jspdf' + }) + }).then(output => { + let code = output.code + code = code.replace( + /Permission\s+is\s+hereby\s+granted[\S\s]+?IN\s+THE\s+SOFTWARE\./, + 'Licensed under the MIT License' + ) + code = code.replace( + /Permission\s+is\s+hereby\s+granted[\S\s]+?IN\s+THE\s+SOFTWARE\./g, + '' + ) + fs.writeFileSync(paths.debug, renew(code)) + + var minified = uglify.minify(code, { + output: { + comments: /@preserve|@license|copyright/i + } + }) + fs.writeFileSync(paths.minified, renew(minified.code)) + }).catch((err) => { + console.error(err) + }) +} + +function renew (code) { + var date = new Date().toISOString() + var version = require('./package.json').version + var whoami = execSync('whoami').toString().trim() + var commit = execSync('git rev-parse --short=10 HEAD').toString().trim() + code = code.replace('${versionID}', version + ' Built on ' + date) + code = code.replace('${commitID}', commit) + code = code.replace(/1\.0\.0-trunk/, version + ' ' + date + ':' + whoami) + + return code +} diff --git a/Apollo/bower_components/jsPDF/dist/jspdf.debug.js b/Apollo/bower_components/jsPDF/dist/jspdf.debug.js new file mode 100755 index 00000000..f8c4c27b --- /dev/null +++ b/Apollo/bower_components/jsPDF/dist/jspdf.debug.js @@ -0,0 +1,17089 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.jspdf = factory()); +}(this, (function () { 'use strict'; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; +} : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; +}; + + + + + +var asyncGenerator = function () { + function AwaitValue(value) { + this.value = value; + } + + function AsyncGenerator(gen) { + var front, back; + + function send(key, arg) { + return new Promise(function (resolve, reject) { + var request = { + key: key, + arg: arg, + resolve: resolve, + reject: reject, + next: null + }; + + if (back) { + back = back.next = request; + } else { + front = back = request; + resume(key, arg); + } + }); + } + + function resume(key, arg) { + try { + var result = gen[key](arg); + var value = result.value; + + if (value instanceof AwaitValue) { + Promise.resolve(value.value).then(function (arg) { + resume("next", arg); + }, function (arg) { + resume("throw", arg); + }); + } else { + settle(result.done ? "return" : "normal", result.value); + } + } catch (err) { + settle("throw", err); + } + } + + function settle(type, value) { + switch (type) { + case "return": + front.resolve({ + value: value, + done: true + }); + break; + + case "throw": + front.reject(value); + break; + + default: + front.resolve({ + value: value, + done: false + }); + break; + } + + front = front.next; + + if (front) { + resume(front.key, front.arg); + } else { + back = null; + } + } + + this._invoke = send; + + if (typeof gen.return !== "function") { + this.return = undefined; + } + } + + if (typeof Symbol === "function" && Symbol.asyncIterator) { + AsyncGenerator.prototype[Symbol.asyncIterator] = function () { + return this; + }; + } + + AsyncGenerator.prototype.next = function (arg) { + return this._invoke("next", arg); + }; + + AsyncGenerator.prototype.throw = function (arg) { + return this._invoke("throw", arg); + }; + + AsyncGenerator.prototype.return = function (arg) { + return this._invoke("return", arg); + }; + + return { + wrap: function (fn) { + return function () { + return new AsyncGenerator(fn.apply(this, arguments)); + }; + }, + await: function (value) { + return new AwaitValue(value); + } + }; +}(); + +/** @preserve + * jsPDF - PDF Document creation from JavaScript + * Version 1.3.5 Built on 2017-09-14T15:10:09.569Z + * CommitID 05dc915baf + * + * Copyright (c) 2010-2016 James Hall , https://github.com/MrRio/jsPDF + * 2010 Aaron Spike, https://github.com/acspike + * 2012 Willow Systems Corporation, willow-systems.com + * 2012 Pablo Hess, https://github.com/pablohess + * 2012 Florian Jenett, https://github.com/fjenett + * 2013 Warren Weckesser, https://github.com/warrenweckesser + * 2013 Youssef Beddad, https://github.com/lifof + * 2013 Lee Driscoll, https://github.com/lsdriscoll + * 2013 Stefan Slonevskiy, https://github.com/stefslon + * 2013 Jeremy Morel, https://github.com/jmorel + * 2013 Christoph Hartmann, https://github.com/chris-rock + * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria + * 2014 James Makes, https://github.com/dollaruw + * 2014 Diego Casorran, https://github.com/diegocr + * 2014 Steven Spungin, https://github.com/Flamenco + * 2014 Kenneth Glassey, https://github.com/Gavvers + * + * Licensed under the MIT License + * + * Contributor(s): + * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, + * kim3er, mfo, alnorth, Flamenco + */ + +/** + * Creates new jsPDF document object instance. + * @name jsPDF + * @class + * @param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l")
+ * Can also be an options object. + * @param unit Measurement unit to be used when coordinates are specified. + * One of "pt" (points), "mm" (Default), "cm", "in" + * @param format One of 'pageFormats' as shown below, default: a4 + * @returns {jsPDF} + * @description + * If the first parameter (orientation) is an object, it will be interpreted as an object of named parameters + * ``` + * { + * orientation: 'p', + * unit: 'mm', + * format: 'a4', + * hotfixes: [] // an array of hotfix strings to enable + * } + * ``` + */ +var jsPDF = function (global) { + 'use strict'; + + var pdfVersion = '1.3', + pageFormats = { // Size in pt of various paper formats + 'a0': [2383.94, 3370.39], + 'a1': [1683.78, 2383.94], + 'a2': [1190.55, 1683.78], + 'a3': [841.89, 1190.55], + 'a4': [595.28, 841.89], + 'a5': [419.53, 595.28], + 'a6': [297.64, 419.53], + 'a7': [209.76, 297.64], + 'a8': [147.40, 209.76], + 'a9': [104.88, 147.40], + 'a10': [73.70, 104.88], + 'b0': [2834.65, 4008.19], + 'b1': [2004.09, 2834.65], + 'b2': [1417.32, 2004.09], + 'b3': [1000.63, 1417.32], + 'b4': [708.66, 1000.63], + 'b5': [498.90, 708.66], + 'b6': [354.33, 498.90], + 'b7': [249.45, 354.33], + 'b8': [175.75, 249.45], + 'b9': [124.72, 175.75], + 'b10': [87.87, 124.72], + 'c0': [2599.37, 3676.54], + 'c1': [1836.85, 2599.37], + 'c2': [1298.27, 1836.85], + 'c3': [918.43, 1298.27], + 'c4': [649.13, 918.43], + 'c5': [459.21, 649.13], + 'c6': [323.15, 459.21], + 'c7': [229.61, 323.15], + 'c8': [161.57, 229.61], + 'c9': [113.39, 161.57], + 'c10': [79.37, 113.39], + 'dl': [311.81, 623.62], + 'letter': [612, 792], + 'government-letter': [576, 756], + 'legal': [612, 1008], + 'junior-legal': [576, 360], + 'ledger': [1224, 792], + 'tabloid': [792, 1224], + 'credit-card': [153, 243] + }; + + /** + * jsPDF's Internal PubSub Implementation. + * See mrrio.github.io/jsPDF/doc/symbols/PubSub.html + * Backward compatible rewritten on 2014 by + * Diego Casorran, https://github.com/diegocr + * + * @class + * @name PubSub + * @ignore This should not be in the public docs. + */ + function PubSub(context) { + var topics = {}; + + this.subscribe = function (topic, callback, once) { + if (typeof callback !== 'function') { + return false; + } + + if (!topics.hasOwnProperty(topic)) { + topics[topic] = {}; + } + + var id = Math.random().toString(35); + topics[topic][id] = [callback, !!once]; + + return id; + }; + + this.unsubscribe = function (token) { + for (var topic in topics) { + if (topics[topic][token]) { + delete topics[topic][token]; + return true; + } + } + return false; + }; + + this.publish = function (topic) { + if (topics.hasOwnProperty(topic)) { + var args = Array.prototype.slice.call(arguments, 1), + idr = []; + + for (var id in topics[topic]) { + var sub = topics[topic][id]; + try { + sub[0].apply(context, args); + } catch (ex) { + if (global.console) { + console.error('jsPDF PubSub Error', ex.message, ex); + } + } + if (sub[1]) idr.push(id); + } + if (idr.length) idr.forEach(this.unsubscribe); + } + }; + } + + /** + * @constructor + * @private + */ + function jsPDF(orientation, unit, format, compressPdf) { + var options = {}; + + if ((typeof orientation === 'undefined' ? 'undefined' : _typeof(orientation)) === 'object') { + options = orientation; + + orientation = options.orientation; + unit = options.unit || unit; + format = options.format || format; + compressPdf = options.compress || options.compressPdf || compressPdf; + } + + // Default options + unit = unit || 'mm'; + format = format || 'a4'; + orientation = ('' + (orientation || 'P')).toLowerCase(); + + var format_as_string = ('' + format).toLowerCase(), + compress = !!compressPdf && typeof Uint8Array === 'function', + textColor = options.textColor || '0 g', + drawColor = options.drawColor || '0 G', + activeFontSize = options.fontSize || 16, + lineHeightProportion = options.lineHeight || 1.15, + lineWidth = options.lineWidth || 0.200025, + // 2mm + objectNumber = 2, + // 'n' Current object number + outToPages = !1, + // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content + offsets = [], + // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes. + fonts = {}, + // collection of font objects, where key is fontKey - a dynamically created label for a given font. + fontmap = {}, + // mapping structure fontName > fontStyle > font key - performance layer. See addFont() + activeFontKey, + // will be string representing the KEY of the font as combination of fontName + fontStyle + k, + // Scale factor + tmp, + page = 0, + currentPage, + pages = [], + pagesContext = [], + // same index as pages and pagedim + pagedim = [], + content = [], + additionalObjects = [], + lineCapID = 0, + lineJoinID = 0, + content_length = 0, + pageWidth, + pageHeight, + pageMode, + zoomMode, + layoutMode, + documentProperties = { + 'title': '', + 'subject': '', + 'author': '', + 'keywords': '', + 'creator': '' + }, + API = {}, + events = new PubSub(API), + hotfixes = options.hotfixes || [], + + + ///////////////////// + // Private functions + ///////////////////// + f2 = function f2(number) { + return number.toFixed(2); // Ie, %.2f + }, + f3 = function f3(number) { + return number.toFixed(3); // Ie, %.3f + }, + padd2 = function padd2(number) { + return ('0' + parseInt(number)).slice(-2); + }, + out = function out(string) { + if (outToPages) { + /* set by beginPage */ + pages[currentPage].push(string); + } else { + // +1 for '\n' that will be used to join 'content' + content_length += string.length + 1; + content.push(string); + } + }, + newObject = function newObject() { + // Begin a new object + objectNumber++; + offsets[objectNumber] = content_length; + out(objectNumber + ' 0 obj'); + return objectNumber; + }, + + // Does not output the object until after the pages have been output. + // Returns an object containing the objectId and content. + // All pages have been added so the object ID can be estimated to start right after. + // This does not modify the current objectNumber; It must be updated after the newObjects are output. + newAdditionalObject = function newAdditionalObject() { + var objId = pages.length * 2 + 1; + objId += additionalObjects.length; + var obj = { + objId: objId, + content: '' + }; + additionalObjects.push(obj); + return obj; + }, + + // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data + newObjectDeferred = function newObjectDeferred() { + objectNumber++; + offsets[objectNumber] = function () { + return content_length; + }; + return objectNumber; + }, + newObjectDeferredBegin = function newObjectDeferredBegin(oid) { + offsets[oid] = content_length; + }, + putStream = function putStream(str) { + out('stream'); + out(str); + out('endstream'); + }, + putPages = function putPages() { + var n, + p, + arr, + i, + deflater, + adler32, + adler32cs, + wPt, + hPt, + pageObjectNumbers = []; + + adler32cs = global.adler32cs || jsPDF.adler32cs; + if (compress && typeof adler32cs === 'undefined') { + compress = false; + } + + // outToPages = false as set in endDocument(). out() writes to content. + + for (n = 1; n <= page; n++) { + pageObjectNumbers.push(newObject()); + wPt = (pageWidth = pagedim[n].width) * k; + hPt = (pageHeight = pagedim[n].height) * k; + out('<>'); + out('endobj'); + + // Page content + p = pages[n].join('\n'); + newObject(); + if (compress) { + arr = []; + i = p.length; + while (i--) { + arr[i] = p.charCodeAt(i); + } + adler32 = adler32cs.from(p); + deflater = new Deflater(6); + deflater.append(new Uint8Array(arr)); + p = deflater.flush(); + arr = new Uint8Array(p.length + 6); + arr.set(new Uint8Array([120, 156])), arr.set(p, 2); + arr.set(new Uint8Array([adler32 & 0xFF, adler32 >> 8 & 0xFF, adler32 >> 16 & 0xFF, adler32 >> 24 & 0xFF]), p.length + 2); + p = String.fromCharCode.apply(null, arr); + out('<>'); + } else { + out('<>'); + } + putStream(p); + out('endobj'); + } + offsets[1] = content_length; + out('1 0 obj'); + out('<>'); + out('endobj'); + events.publish('postPutPages'); + }, + putFont = function putFont(font) { + font.objectNumber = newObject(); + out('<>'); + out('endobj'); + }, + putFonts = function putFonts() { + for (var fontKey in fonts) { + if (fonts.hasOwnProperty(fontKey)) { + putFont(fonts[fontKey]); + } + } + }, + putXobjectDict = function putXobjectDict() { + // Loop through images, or other data objects + events.publish('putXobjectDict'); + }, + putResourceDictionary = function putResourceDictionary() { + out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + out('/Font <<'); + + // Do this for each font, the '1' bit is the index of the font + for (var fontKey in fonts) { + if (fonts.hasOwnProperty(fontKey)) { + out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R'); + } + } + out('>>'); + out('/XObject <<'); + putXobjectDict(); + out('>>'); + }, + putResources = function putResources() { + putFonts(); + events.publish('putResources'); + // Resource dictionary + offsets[2] = content_length; + out('2 0 obj'); + out('<<'); + putResourceDictionary(); + out('>>'); + out('endobj'); + events.publish('postPutResources'); + }, + putAdditionalObjects = function putAdditionalObjects() { + events.publish('putAdditionalObjects'); + for (var i = 0; i < additionalObjects.length; i++) { + var obj = additionalObjects[i]; + offsets[obj.objId] = content_length; + out(obj.objId + ' 0 obj'); + out(obj.content); + out('endobj'); + } + objectNumber += additionalObjects.length; + events.publish('postPutAdditionalObjects'); + }, + addToFontDictionary = function addToFontDictionary(fontKey, fontName, fontStyle) { + // this is mapping structure for quick font key lookup. + // returns the KEY of the font (ex: "F1") for a given + // pair of font name and type (ex: "Arial". "Italic") + if (!fontmap.hasOwnProperty(fontName)) { + fontmap[fontName] = {}; + } + fontmap[fontName][fontStyle] = fontKey; + }, + + /** + * FontObject describes a particular font as member of an instnace of jsPDF + * + * It's a collection of properties like 'id' (to be used in PDF stream), + * 'fontName' (font's family name), 'fontStyle' (font's style variant label) + * + * @class + * @public + * @property id {String} PDF-document-instance-specific label assinged to the font. + * @property PostScriptName {String} PDF specification full name for the font + * @property encoding {Object} Encoding_name-to-Font_metrics_object mapping. + * @name FontObject + * @ignore This should not be in the public docs. + */ + addFont = function addFont(PostScriptName, fontName, fontStyle, encoding) { + var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10), + + // This is FontObject + font = fonts[fontKey] = { + 'id': fontKey, + 'PostScriptName': PostScriptName, + 'fontName': fontName, + 'fontStyle': fontStyle, + 'encoding': encoding, + 'metadata': {} + }; + addToFontDictionary(fontKey, fontName, fontStyle); + events.publish('addFont', font); + + return fontKey; + }, + addFonts = function addFonts() { + + var HELVETICA = "helvetica", + TIMES = "times", + COURIER = "courier", + NORMAL = "normal", + BOLD = "bold", + ITALIC = "italic", + BOLD_ITALIC = "bolditalic", + encoding = 'StandardEncoding', + ZAPF = "zapfdingbats", + standardFonts = [['Helvetica', HELVETICA, NORMAL], ['Helvetica-Bold', HELVETICA, BOLD], ['Helvetica-Oblique', HELVETICA, ITALIC], ['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC], ['Courier', COURIER, NORMAL], ['Courier-Bold', COURIER, BOLD], ['Courier-Oblique', COURIER, ITALIC], ['Courier-BoldOblique', COURIER, BOLD_ITALIC], ['Times-Roman', TIMES, NORMAL], ['Times-Bold', TIMES, BOLD], ['Times-Italic', TIMES, ITALIC], ['Times-BoldItalic', TIMES, BOLD_ITALIC], ['ZapfDingbats', ZAPF]]; + + for (var i = 0, l = standardFonts.length; i < l; i++) { + var fontKey = addFont(standardFonts[i][0], standardFonts[i][1], standardFonts[i][2], encoding); + + // adding aliases for standard fonts, this time matching the capitalization + var parts = standardFonts[i][0].split('-'); + addToFontDictionary(fontKey, parts[0], parts[1] || ''); + } + events.publish('addFonts', { + fonts: fonts, + dictionary: fontmap + }); + }, + SAFE = function __safeCall(fn) { + fn.foo = function __safeCallWrapper() { + try { + return fn.apply(this, arguments); + } catch (e) { + var stack = e.stack || ''; + if (~stack.indexOf(' at ')) stack = stack.split(" at ")[1]; + var m = "Error in function " + stack.split("\n")[0].split('<')[0] + ": " + e.message; + if (global.console) { + global.console.error(m, e); + if (global.alert) alert(m); + } else { + throw new Error(m); + } + } + }; + fn.foo.bar = fn; + return fn.foo; + }, + to8bitStream = function to8bitStream(text, flags) { + /** + * PDF 1.3 spec: + * "For text strings encoded in Unicode, the first two bytes must be 254 followed by + * 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts + * with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely + * to be a meaningful beginning of a word or phrase.) The remainder of the + * string consists of Unicode character codes, according to the UTF-16 encoding + * specified in the Unicode standard, version 2.0. Commonly used Unicode values + * are represented as 2 bytes per character, with the high-order byte appearing first + * in the string." + * + * In other words, if there are chars in a string with char code above 255, we + * recode the string to UCS2 BE - string doubles in length and BOM is prepended. + * + * HOWEVER! + * Actual *content* (body) text (as opposed to strings used in document properties etc) + * does NOT expect BOM. There, it is treated as a literal GID (Glyph ID) + * + * Because of Adobe's focus on "you subset your fonts!" you are not supposed to have + * a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could + * fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode + * code page. There, however, all characters in the stream are treated as GIDs, + * including BOM, which is the reason we need to skip BOM in content text (i.e. that + * that is tied to a font). + * + * To signal this "special" PDFEscape / to8bitStream handling mode, + * API.text() function sets (unless you overwrite it with manual values + * given to API.text(.., flags) ) + * flags.autoencode = true + * flags.noBOM = true + * + * =================================================================================== + * `flags` properties relied upon: + * .sourceEncoding = string with encoding label. + * "Unicode" by default. = encoding of the incoming text. + * pass some non-existing encoding name + * (ex: 'Do not touch my strings! I know what I am doing.') + * to make encoding code skip the encoding step. + * .outputEncoding = Either valid PDF encoding name + * (must be supported by jsPDF font metrics, otherwise no encoding) + * or a JS object, where key = sourceCharCode, value = outputCharCode + * missing keys will be treated as: sourceCharCode === outputCharCode + * .noBOM + * See comment higher above for explanation for why this is important + * .autoencode + * See comment higher above for explanation for why this is important + */ + + var i, l, sourceEncoding, encodingBlock, outputEncoding, newtext, isUnicode, ch, bch; + + flags = flags || {}; + sourceEncoding = flags.sourceEncoding || 'Unicode'; + outputEncoding = flags.outputEncoding; + + // This 'encoding' section relies on font metrics format + // attached to font objects by, among others, + // "Willow Systems' standard_font_metrics plugin" + // see jspdf.plugin.standard_font_metrics.js for format + // of the font.metadata.encoding Object. + // It should be something like + // .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}} + // .widths = {0:width, code:width, ..., 'fof':divisor} + // .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...} + if ((flags.autoencode || outputEncoding) && fonts[activeFontKey].metadata && fonts[activeFontKey].metadata[sourceEncoding] && fonts[activeFontKey].metadata[sourceEncoding].encoding) { + encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding; + + // each font has default encoding. Some have it clearly defined. + if (!outputEncoding && fonts[activeFontKey].encoding) { + outputEncoding = fonts[activeFontKey].encoding; + } + + // Hmmm, the above did not work? Let's try again, in different place. + if (!outputEncoding && encodingBlock.codePages) { + outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default + } + + if (typeof outputEncoding === 'string') { + outputEncoding = encodingBlock[outputEncoding]; + } + // we want output encoding to be a JS Object, where + // key = sourceEncoding's character code and + // value = outputEncoding's character code. + if (outputEncoding) { + isUnicode = false; + newtext = []; + for (i = 0, l = text.length; i < l; i++) { + ch = outputEncoding[text.charCodeAt(i)]; + if (ch) { + newtext.push(String.fromCharCode(ch)); + } else { + newtext.push(text[i]); + } + + // since we are looping over chars anyway, might as well + // check for residual unicodeness + if (newtext[i].charCodeAt(0) >> 8) { + /* more than 255 */ + isUnicode = true; + } + } + text = newtext.join(''); + } + } + + i = text.length; + // isUnicode may be set to false above. Hence the triple-equal to undefined + while (isUnicode === undefined && i !== 0) { + if (text.charCodeAt(i - 1) >> 8) { + /* more than 255 */ + isUnicode = true; + } + i--; + } + if (!isUnicode) { + return text; + } + + newtext = flags.noBOM ? [] : [254, 255]; + for (i = 0, l = text.length; i < l; i++) { + ch = text.charCodeAt(i); + bch = ch >> 8; // divide by 256 + if (bch >> 8) { + /* something left after dividing by 256 second time */ + throw new Error("Character at position " + i + " of string '" + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE"); + } + newtext.push(bch); + newtext.push(ch - (bch << 8)); + } + return String.fromCharCode.apply(undefined, newtext); + }, + pdfEscape = function pdfEscape(text, flags) { + /** + * Replace '/', '(', and ')' with pdf-safe versions + * + * Doing to8bitStream does NOT make this PDF display unicode text. For that + * we also need to reference a unicode font and embed it - royal pain in the rear. + * + * There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars, + * which JavaScript Strings are happy to provide. So, while we still cannot display + * 2-byte characters property, at least CONDITIONALLY converting (entire string containing) + * 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF + * is still parseable. + * This will allow immediate support for unicode in document properties strings. + */ + return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); + }, + putInfo = function putInfo() { + out('/Producer (jsPDF ' + jsPDF.version + ')'); + for (var key in documentProperties) { + if (documentProperties.hasOwnProperty(key) && documentProperties[key]) { + out('/' + key.substr(0, 1).toUpperCase() + key.substr(1) + ' (' + pdfEscape(documentProperties[key]) + ')'); + } + } + var created = new Date(), + tzoffset = created.getTimezoneOffset(), + tzsign = tzoffset < 0 ? '+' : '-', + tzhour = Math.floor(Math.abs(tzoffset / 60)), + tzmin = Math.abs(tzoffset % 60), + tzstr = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join(''); + out(['/CreationDate (D:', created.getFullYear(), padd2(created.getMonth() + 1), padd2(created.getDate()), padd2(created.getHours()), padd2(created.getMinutes()), padd2(created.getSeconds()), tzstr, ')'].join('')); + }, + putCatalog = function putCatalog() { + out('/Type /Catalog'); + out('/Pages 1 0 R'); + // PDF13ref Section 7.2.1 + if (!zoomMode) zoomMode = 'fullwidth'; + switch (zoomMode) { + case 'fullwidth': + out('/OpenAction [3 0 R /FitH null]'); + break; + case 'fullheight': + out('/OpenAction [3 0 R /FitV null]'); + break; + case 'fullpage': + out('/OpenAction [3 0 R /Fit]'); + break; + case 'original': + out('/OpenAction [3 0 R /XYZ null null 1]'); + break; + default: + var pcn = '' + zoomMode; + if (pcn.substr(pcn.length - 1) === '%') zoomMode = parseInt(zoomMode) / 100; + if (typeof zoomMode === 'number') { + out('/OpenAction [3 0 R /XYZ null null ' + f2(zoomMode) + ']'); + } + } + if (!layoutMode) layoutMode = 'continuous'; + switch (layoutMode) { + case 'continuous': + out('/PageLayout /OneColumn'); + break; + case 'single': + out('/PageLayout /SinglePage'); + break; + case 'two': + case 'twoleft': + out('/PageLayout /TwoColumnLeft'); + break; + case 'tworight': + out('/PageLayout /TwoColumnRight'); + break; + } + if (pageMode) { + /** + * A name object specifying how the document should be displayed when opened: + * UseNone : Neither document outline nor thumbnail images visible -- DEFAULT + * UseOutlines : Document outline visible + * UseThumbs : Thumbnail images visible + * FullScreen : Full-screen mode, with no menu bar, window controls, or any other window visible + */ + out('/PageMode /' + pageMode); + } + events.publish('putCatalog'); + }, + putTrailer = function putTrailer() { + out('/Size ' + (objectNumber + 1)); + out('/Root ' + objectNumber + ' 0 R'); + out('/Info ' + (objectNumber - 1) + ' 0 R'); + }, + beginPage = function beginPage(width, height) { + // Dimensions are stored as user units and converted to points on output + var orientation = typeof height === 'string' && height.toLowerCase(); + if (typeof width === 'string') { + var format = width.toLowerCase(); + if (pageFormats.hasOwnProperty(format)) { + width = pageFormats[format][0] / k; + height = pageFormats[format][1] / k; + } + } + if (Array.isArray(width)) { + height = width[1]; + width = width[0]; + } + if (orientation) { + switch (orientation.substr(0, 1)) { + case 'l': + if (height > width) orientation = 's'; + break; + case 'p': + if (width > height) orientation = 's'; + break; + } + if (orientation === 's') { + tmp = width; + width = height; + height = tmp; + } + } + outToPages = true; + pages[++page] = []; + pagedim[page] = { + width: Number(width) || pageWidth, + height: Number(height) || pageHeight + }; + pagesContext[page] = {}; + _setPage(page); + }, + _addPage = function _addPage() { + beginPage.apply(this, arguments); + // Set line width + out(f2(lineWidth * k) + ' w'); + // Set draw color + out(drawColor); + // resurrecting non-default line caps, joins + if (lineCapID !== 0) { + out(lineCapID + ' J'); + } + if (lineJoinID !== 0) { + out(lineJoinID + ' j'); + } + events.publish('addPage', { + pageNumber: page + }); + }, + _deletePage = function _deletePage(n) { + if (n > 0 && n <= page) { + pages.splice(n, 1); + pagedim.splice(n, 1); + page--; + if (currentPage > page) { + currentPage = page; + } + this.setPage(currentPage); + } + }, + _setPage = function _setPage(n) { + if (n > 0 && n <= page) { + currentPage = n; + pageWidth = pagedim[n].width; + pageHeight = pagedim[n].height; + } + }, + + /** + * Returns a document-specific font key - a label assigned to a + * font name + font type combination at the time the font was added + * to the font inventory. + * + * Font key is used as label for the desired font for a block of text + * to be added to the PDF document stream. + * @private + * @function + * @param fontName {String} can be undefined on "falthy" to indicate "use current" + * @param fontStyle {String} can be undefined on "falthy" to indicate "use current" + * @returns {String} Font key. + */ + _getFont = function _getFont(fontName, fontStyle) { + var key; + + fontName = fontName !== undefined ? fontName : fonts[activeFontKey].fontName; + fontStyle = fontStyle !== undefined ? fontStyle : fonts[activeFontKey].fontStyle; + + if (fontName !== undefined) { + fontName = fontName.toLowerCase(); + } + switch (fontName) { + case 'sans-serif': + case 'verdana': + case 'arial': + case 'helvetica': + fontName = 'helvetica'; + break; + case 'fixed': + case 'monospace': + case 'terminal': + case 'courier': + fontName = 'courier'; + break; + case 'serif': + case 'cursive': + case 'fantasy': + default: + fontName = 'times'; + break; + } + + try { + // get a string like 'F3' - the KEY corresponding tot he font + type combination. + key = fontmap[fontName][fontStyle]; + } catch (e) {} + + if (!key) { + //throw new Error("Unable to look up font label for font '" + fontName + "', '" + //+ fontStyle + "'. Refer to getFontList() for available fonts."); + key = fontmap['times'][fontStyle]; + if (key == null) { + key = fontmap['times']['normal']; + } + } + return key; + }, + buildDocument = function buildDocument() { + outToPages = false; // switches out() to content + + objectNumber = 2; + content_length = 0; + content = []; + offsets = []; + additionalObjects = []; + // Added for AcroForm + events.publish('buildDocument'); + + // putHeader() + out('%PDF-' + pdfVersion); + + putPages(); + + // Must happen after putPages + // Modifies current object Id + putAdditionalObjects(); + + putResources(); + + // Info + newObject(); + out('<<'); + putInfo(); + out('>>'); + out('endobj'); + + // Catalog + newObject(); + out('<<'); + putCatalog(); + out('>>'); + out('endobj'); + + // Cross-ref + var o = content_length, + i, + p = "0000000000"; + out('xref'); + out('0 ' + (objectNumber + 1)); + out(p + ' 65535 f '); + for (i = 1; i <= objectNumber; i++) { + var offset = offsets[i]; + if (typeof offset === 'function') { + out((p + offsets[i]()).slice(-10) + ' 00000 n '); + } else { + out((p + offsets[i]).slice(-10) + ' 00000 n '); + } + } + // Trailer + out('trailer'); + out('<<'); + putTrailer(); + out('>>'); + out('startxref'); + out('' + o); + out('%%EOF'); + + outToPages = true; + + return content.join('\n'); + }, + getStyle = function getStyle(style) { + // see path-painting operators in PDF spec + var op = 'S'; // stroke + if (style === 'F') { + op = 'f'; // fill + } else if (style === 'FD' || style === 'DF') { + op = 'B'; // both + } else if (style === 'f' || style === 'f*' || style === 'B' || style === 'B*') { + /* + Allow direct use of these PDF path-painting operators: + - f fill using nonzero winding number rule + - f* fill using even-odd rule + - B fill then stroke with fill using non-zero winding number rule + - B* fill then stroke with fill using even-odd rule + */ + op = style; + } + return op; + }, + getArrayBuffer = function getArrayBuffer() { + var data = buildDocument(), + len = data.length, + ab = new ArrayBuffer(len), + u8 = new Uint8Array(ab); + + while (len--) { + u8[len] = data.charCodeAt(len); + }return ab; + }, + getBlob = function getBlob() { + return new Blob([getArrayBuffer()], { + type: "application/pdf" + }); + }, + + /** + * Generates the PDF document. + * + * If `type` argument is undefined, output is raw body of resulting PDF returned as a string. + * + * @param {String} type A string identifying one of the possible output types. + * @param {Object} options An object providing some additional signalling to PDF generator. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name output + */ + _output = SAFE(function (type, options) { + var datauri = ('' + type).substr(0, 6) === 'dataur' ? 'data:application/pdf;base64,' + btoa(buildDocument()) : 0; + + switch (type) { + case undefined: + return buildDocument(); + case 'save': + if (navigator.getUserMedia) { + if (global.URL === undefined || global.URL.createObjectURL === undefined) { + return API.output('dataurlnewwindow'); + } + } + saveAs(getBlob(), options); + if (typeof saveAs.unload === 'function') { + if (global.setTimeout) { + setTimeout(saveAs.unload, 911); + } + } + break; + case 'arraybuffer': + return getArrayBuffer(); + case 'blob': + return getBlob(); + case 'bloburi': + case 'bloburl': + // User is responsible of calling revokeObjectURL + return global.URL && global.URL.createObjectURL(getBlob()) || void 0; + case 'datauristring': + case 'dataurlstring': + return datauri; + case 'dataurlnewwindow': + var nW = global.open(datauri); + if (nW || typeof safari === "undefined") return nW; + /* pass through */ + case 'datauri': + case 'dataurl': + return global.document.location.href = datauri; + default: + throw new Error('Output type "' + type + '" is not supported.'); + } + // @TODO: Add different output options + }), + + + /** + * Used to see if a supplied hotfix was requested when the pdf instance was created. + * @param {String} hotfixName - The name of the hotfix to check. + * @returns {boolean} + */ + hasHotfix = function hasHotfix(hotfixName) { + return Array.isArray(hotfixes) === true && hotfixes.indexOf(hotfixName) > -1; + }; + + switch (unit) { + case 'pt': + k = 1; + break; + case 'mm': + k = 72 / 25.4000508; + break; + case 'cm': + k = 72 / 2.54000508; + break; + case 'in': + k = 72; + break; + case 'px': + if (hasHotfix('px_scaling') == true) { + k = 72 / 96; + } else { + k = 96 / 72; + } + break; + case 'pc': + k = 12; + break; + case 'em': + k = 12; + break; + case 'ex': + k = 6; + break; + default: + throw 'Invalid unit: ' + unit; + } + + //--------------------------------------- + // Public API + + /** + * Object exposing internal API to plugins + * @public + */ + API.internal = { + 'pdfEscape': pdfEscape, + 'getStyle': getStyle, + /** + * Returns {FontObject} describing a particular font. + * @public + * @function + * @param fontName {String} (Optional) Font's family name + * @param fontStyle {String} (Optional) Font's style variation name (Example:"Italic") + * @returns {FontObject} + */ + 'getFont': function getFont() { + return fonts[_getFont.apply(API, arguments)]; + }, + 'getFontSize': function getFontSize() { + return activeFontSize; + }, + 'getLineHeight': function getLineHeight() { + return activeFontSize * lineHeightProportion; + }, + 'write': function write(string1 /*, string2, string3, etc */) { + out(arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' ')); + }, + 'getCoordinateString': function getCoordinateString(value) { + return f2(value * k); + }, + 'getVerticalCoordinateString': function getVerticalCoordinateString(value) { + return f2((pageHeight - value) * k); + }, + 'collections': {}, + 'newObject': newObject, + 'newAdditionalObject': newAdditionalObject, + 'newObjectDeferred': newObjectDeferred, + 'newObjectDeferredBegin': newObjectDeferredBegin, + 'putStream': putStream, + 'events': events, + // ratio that you use in multiplication of a given "size" number to arrive to 'point' + // units of measurement. + // scaleFactor is set at initialization of the document and calculated against the stated + // default measurement units for the document. + // If default is "mm", k is the number that will turn number in 'mm' into 'points' number. + // through multiplication. + 'scaleFactor': k, + 'pageSize': { + get width() { + return pageWidth; + }, + get height() { + return pageHeight; + } + }, + 'output': function output(type, options) { + return _output(type, options); + }, + 'getNumberOfPages': function getNumberOfPages() { + return pages.length - 1; + }, + 'pages': pages, + 'out': out, + 'f2': f2, + 'getPageInfo': function getPageInfo(pageNumberOneBased) { + var objId = (pageNumberOneBased - 1) * 2 + 3; + return { + objId: objId, + pageNumber: pageNumberOneBased, + pageContext: pagesContext[pageNumberOneBased] + }; + }, + 'getCurrentPageInfo': function getCurrentPageInfo() { + var objId = (currentPage - 1) * 2 + 3; + return { + objId: objId, + pageNumber: currentPage, + pageContext: pagesContext[currentPage] + }; + }, + 'getPDFVersion': function getPDFVersion() { + return pdfVersion; + }, + 'hasHotfix': hasHotfix //Expose the hasHotfix check so plugins can also check them. + }; + + /** + * Adds (and transfers the focus to) new page to the PDF document. + * @function + * @returns {jsPDF} + * + * @methodOf jsPDF# + * @name addPage + */ + API.addPage = function () { + _addPage.apply(this, arguments); + return this; + }; + /** + * Adds (and transfers the focus to) new page to the PDF document. + * @function + * @returns {jsPDF} + * + * @methodOf jsPDF# + * @name setPage + * @param {Number} page Switch the active page to the page number specified + * @example + * doc = jsPDF() + * doc.addPage() + * doc.addPage() + * doc.text('I am on page 3', 10, 10) + * doc.setPage(1) + * doc.text('I am on page 1', 10, 10) + */ + API.setPage = function () { + _setPage.apply(this, arguments); + return this; + }; + API.insertPage = function (beforePage) { + this.addPage(); + this.movePage(currentPage, beforePage); + return this; + }; + API.movePage = function (targetPage, beforePage) { + if (targetPage > beforePage) { + var tmpPages = pages[targetPage]; + var tmpPagedim = pagedim[targetPage]; + var tmpPagesContext = pagesContext[targetPage]; + for (var i = targetPage; i > beforePage; i--) { + pages[i] = pages[i - 1]; + pagedim[i] = pagedim[i - 1]; + pagesContext[i] = pagesContext[i - 1]; + } + pages[beforePage] = tmpPages; + pagedim[beforePage] = tmpPagedim; + pagesContext[beforePage] = tmpPagesContext; + this.setPage(beforePage); + } else if (targetPage < beforePage) { + var tmpPages = pages[targetPage]; + var tmpPagedim = pagedim[targetPage]; + var tmpPagesContext = pagesContext[targetPage]; + for (var i = targetPage; i < beforePage; i++) { + pages[i] = pages[i + 1]; + pagedim[i] = pagedim[i + 1]; + pagesContext[i] = pagesContext[i + 1]; + } + pages[beforePage] = tmpPages; + pagedim[beforePage] = tmpPagedim; + pagesContext[beforePage] = tmpPagesContext; + this.setPage(beforePage); + } + return this; + }; + + API.deletePage = function () { + _deletePage.apply(this, arguments); + return this; + }; + + /** + * Set the display mode options of the page like zoom and layout. + * + * @param {integer|String} zoom You can pass an integer or percentage as + * a string. 2 will scale the document up 2x, '200%' will scale up by the + * same amount. You can also set it to 'fullwidth', 'fullheight', + * 'fullpage', or 'original'. + * + * Only certain PDF readers support this, such as Adobe Acrobat + * + * @param {String} layout Layout mode can be: 'continuous' - this is the + * default continuous scroll. 'single' - the single page mode only shows one + * page at a time. 'twoleft' - two column left mode, first page starts on + * the left, and 'tworight' - pages are laid out in two columns, with the + * first page on the right. This would be used for books. + * @param {String} pmode 'UseOutlines' - it shows the + * outline of the document on the left. 'UseThumbs' - shows thumbnails along + * the left. 'FullScreen' - prompts the user to enter fullscreen mode. + * + * @function + * @returns {jsPDF} + * @name setDisplayMode + */ + API.setDisplayMode = function (zoom, layout, pmode) { + zoomMode = zoom; + layoutMode = layout; + pageMode = pmode; + + var validPageModes = [undefined, null, 'UseNone', 'UseOutlines', 'UseThumbs', 'FullScreen']; + if (validPageModes.indexOf(pmode) == -1) { + throw new Error('Page mode must be one of UseNone, UseOutlines, UseThumbs, or FullScreen. "' + pmode + '" is not recognized.'); + } + return this; + }, + + /** + * Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings. + * + * @function + * @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down per font, spacing settings declared before this call. + * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Object} flags Collection of settings signalling how the text must be encoded. Defaults are sane. If you think you want to pass some flags, you likely can read the source. + * @returns {jsPDF} + * @methodOf jsPDF# + * @name text + */ + API.text = function (text, x, y, flags, angle, align) { + /** + * Inserts something like this into PDF + * BT + * /F1 16 Tf % Font name + size + * 16 TL % How many units down for next line in multiline text + * 0 g % color + * 28.35 813.54 Td % position + * (line one) Tj + * T* (line two) Tj + * T* (line three) Tj + * ET + */ + function ESC(s) { + s = s.split("\t").join(Array(options.TabLen || 9).join(" ")); + return pdfEscape(s, flags); + } + + // Pre-August-2012 the order of arguments was function(x, y, text, flags) + // in effort to make all calls have similar signature like + // function(data, coordinates... , miscellaneous) + // this method had its args flipped. + // code below allows backward compatibility with old arg order. + if (typeof text === 'number') { + tmp = y; + y = x; + x = text; + text = tmp; + } + + // If there are any newlines in text, we assume + // the user wanted to print multiple lines, so break the + // text up into an array. If the text is already an array, + // we assume the user knows what they are doing. + // Convert text into an array anyway to simplify + // later code. + if (typeof text === 'string') { + if (text.match(/[\n\r]/)) { + text = text.split(/\r\n|\r|\n/g); + } else { + text = [text]; + } + } + if (typeof angle === 'string') { + align = angle; + angle = null; + } + if (typeof flags === 'string') { + align = flags; + flags = null; + } + if (typeof flags === 'number') { + angle = flags; + flags = null; + } + var xtra = '', + mode = 'Td', + todo; + if (angle) { + angle *= Math.PI / 180; + var c = Math.cos(angle), + s = Math.sin(angle); + xtra = [f2(c), f2(s), f2(s * -1), f2(c), ''].join(" "); + mode = 'Tm'; + } + flags = flags || {}; + if (!('noBOM' in flags)) flags.noBOM = true; + if (!('autoencode' in flags)) flags.autoencode = true; + + var strokeOption = ''; + var pageContext = this.internal.getCurrentPageInfo().pageContext; + if (true === flags.stroke) { + if (pageContext.lastTextWasStroke !== true) { + strokeOption = '1 Tr\n'; + pageContext.lastTextWasStroke = true; + } + } else { + if (pageContext.lastTextWasStroke) { + strokeOption = '0 Tr\n'; + } + pageContext.lastTextWasStroke = false; + } + + if (typeof this._runningPageHeight === 'undefined') { + this._runningPageHeight = 0; + } + + if (typeof text === 'string') { + text = ESC(text); + } else if (Object.prototype.toString.call(text) === '[object Array]') { + // we don't want to destroy original text array, so cloning it + var sa = text.concat(), + da = [], + len = sa.length; + // we do array.join('text that must not be PDFescaped") + // thus, pdfEscape each component separately + while (len--) { + da.push(ESC(sa.shift())); + } + if (align) { + var left, + prevX, + maxLineLength, + leading = activeFontSize * lineHeightProportion, + lineWidths = text.map(function (v) { + return this.getStringUnitWidth(v) * activeFontSize / k; + }, this); + maxLineLength = Math.max.apply(Math, lineWidths); + // The first line uses the "main" Td setting, + // and the subsequent lines are offset by the + // previous line's x coordinate. + if (align === "center") { + // The passed in x coordinate defines + // the center point. + left = x - maxLineLength / 2; + x -= lineWidths[0] / 2; + } else if (align === "right") { + // The passed in x coordinate defines the + // rightmost point of the text. + left = x - maxLineLength; + x -= lineWidths[0]; + } else { + throw new Error('Unrecognized alignment option, use "center" or "right".'); + } + prevX = x; + text = da[0]; + for (var i = 1, len = da.length; i < len; i++) { + var delta = maxLineLength - lineWidths[i]; + if (align === "center") delta /= 2; + // T* = x-offset leading Td ( text ) + text += ") Tj\n" + (left - prevX + delta) + " -" + leading + " Td (" + da[i]; + prevX = left + delta; + } + } else { + text = da.join(") Tj\nT* ("); + } + } else { + throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.'); + } + // Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates + + // BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET + // if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations) + // Thus, there is NO useful, *reliable* concept of "default" font for a page. + // The fact that "default" (reuse font used before) font worked before in basic cases is an accident + // - readers dealing smartly with brokenness of jsPDF's markup. + + var curY; + + if (todo) { + //this.addPage(); + //this._runningPageHeight += y - (activeFontSize * 1.7 / k); + //curY = f2(pageHeight - activeFontSize * 1.7 /k); + } else { + curY = f2((pageHeight - y) * k); + } + //curY = f2((pageHeight - (y - this._runningPageHeight)) * k); + + // if (curY < 0){ + // console.log('auto page break'); + // this.addPage(); + // this._runningPageHeight = y - (activeFontSize * 1.7 / k); + // curY = f2(pageHeight - activeFontSize * 1.7 /k); + // } + + out('BT\n/' + activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size + activeFontSize * lineHeightProportion + ' TL\n' + // line spacing + strokeOption + // stroke option + textColor + '\n' + xtra + f2(x * k) + ' ' + curY + ' ' + mode + '\n(' + text + ') Tj\nET'); + + if (todo) { + //this.text( todo, x, activeFontSize * 1.7 / k); + //this.text( todo, x, this._runningPageHeight + (activeFontSize * 1.7 / k)); + this.text(todo, x, y); // + (activeFontSize * 1.7 / k)); + } + + return this; + }; + + /** + * Letter spacing method to print text with gaps + * + * @function + * @param {String|Array} text String to be added to the page. + * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} spacing Spacing (in units declared at inception) + * @returns {jsPDF} + * @methodOf jsPDF# + * @name lstext + * @deprecated We'll be removing this function. It doesn't take character width into account. + */ + API.lstext = function (text, x, y, spacing) { + console.warn('jsPDF.lstext is deprecated'); + for (var i = 0, len = text.length; i < len; i++, x += spacing) { + this.text(text[i], x, y); + }return this; + }; + + API.line = function (x1, y1, x2, y2) { + return this.lines([[x2 - x1, y2 - y1]], x1, y1); + }; + + API.clip = function () { + // By patrick-roberts, github.com/MrRio/jsPDF/issues/328 + // Call .clip() after calling .rect() with a style argument of null + out('W'); // clip + out('S'); // stroke path; necessary for clip to work + }; + + /** + * This fixes the previous function clip(). Perhaps the 'stroke path' hack was due to the missing 'n' instruction? + * We introduce the fixed version so as to not break API. + * @param fillRule + */ + API.clip_fixed = function (fillRule) { + // Call .clip() after calling drawing ops with a style argument of null + // W is the PDF clipping op + if ('evenodd' === fillRule) { + out('W*'); + } else { + out('W'); + } + // End the path object without filling or stroking it. + // This operator is a path-painting no-op, used primarily for the side effect of changing the current clipping path + // (see Section 4.4.3, “Clipping Path Operators”) + out('n'); + }; + + /** + * Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates. + * All data points in `lines` are relative to last line origin. + * `x`, `y` become x1,y1 for first line / curve in the set. + * For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point. + * For bezier curves you need to specify [x2,y2,x3,y3,x4,y4] - vectors to control points 1, 2, ending point. All vectors are against the start of the curve - x1,y1. + * + * @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line + * @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves). + * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} scale (Defaults to [1.0,1.0]) x,y Scaling factor for all vectors. Elements can be any floating number Sub-one makes drawing smaller. Over-one grows the drawing. Negative flips the direction. + * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument. + * @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name lines + */ + API.lines = function (lines, x, y, scale, style, closed) { + var scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4; + + // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style) + // in effort to make all calls have similar signature like + // function(content, coordinateX, coordinateY , miscellaneous) + // this method had its args flipped. + // code below allows backward compatibility with old arg order. + if (typeof lines === 'number') { + tmp = y; + y = x; + x = lines; + lines = tmp; + } + + scale = scale || [1, 1]; + + // starting point + out(f3(x * k) + ' ' + f3((pageHeight - y) * k) + ' m '); + + scalex = scale[0]; + scaley = scale[1]; + l = lines.length; + //, x2, y2 // bezier only. In page default measurement "units", *after* scaling + //, x3, y3 // bezier only. In page default measurement "units", *after* scaling + // ending point for all, lines and bezier. . In page default measurement "units", *after* scaling + x4 = x; // last / ending point = starting point for first item. + y4 = y; // last / ending point = starting point for first item. + + for (i = 0; i < l; i++) { + leg = lines[i]; + if (leg.length === 2) { + // simple line + x4 = leg[0] * scalex + x4; // here last x4 was prior ending point + y4 = leg[1] * scaley + y4; // here last y4 was prior ending point + out(f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' l'); + } else { + // bezier curve + x2 = leg[0] * scalex + x4; // here last x4 is prior ending point + y2 = leg[1] * scaley + y4; // here last y4 is prior ending point + x3 = leg[2] * scalex + x4; // here last x4 is prior ending point + y3 = leg[3] * scaley + y4; // here last y4 is prior ending point + x4 = leg[4] * scalex + x4; // here last x4 was prior ending point + y4 = leg[5] * scaley + y4; // here last y4 was prior ending point + out(f3(x2 * k) + ' ' + f3((pageHeight - y2) * k) + ' ' + f3(x3 * k) + ' ' + f3((pageHeight - y3) * k) + ' ' + f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' c'); + } + } + + if (closed) { + out(' h'); + } + + // stroking / filling / both the path + if (style !== null) { + out(getStyle(style)); + } + return this; + }; + + /** + * Adds a rectangle to PDF + * + * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} w Width (in units declared at inception of PDF document) + * @param {Number} h Height (in units declared at inception of PDF document) + * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name rect + */ + API.rect = function (x, y, w, h, style) { + var op = getStyle(style); + out([f2(x * k), f2((pageHeight - y) * k), f2(w * k), f2(-h * k), 're'].join(' ')); + + if (style !== null) { + out(getStyle(style)); + } + + return this; + }; + + /** + * Adds a triangle to PDF + * + * @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name triangle + */ + API.triangle = function (x1, y1, x2, y2, x3, y3, style) { + this.lines([[x2 - x1, y2 - y1], // vector to point 2 + [x3 - x2, y3 - y2], // vector to point 3 + [x1 - x3, y1 - y3] // closing vector back to point 1 + ], x1, y1, // start of path + [1, 1], style, true); + return this; + }; + + /** + * Adds a rectangle with rounded corners to PDF + * + * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} w Width (in units declared at inception of PDF document) + * @param {Number} h Height (in units declared at inception of PDF document) + * @param {Number} rx Radius along x axis (in units declared at inception of PDF document) + * @param {Number} rx Radius along y axis (in units declared at inception of PDF document) + * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name roundedRect + */ + API.roundedRect = function (x, y, w, h, rx, ry, style) { + var MyArc = 4 / 3 * (Math.SQRT2 - 1); + this.lines([[w - 2 * rx, 0], [rx * MyArc, 0, rx, ry - ry * MyArc, rx, ry], [0, h - 2 * ry], [0, ry * MyArc, -(rx * MyArc), ry, -rx, ry], [-w + 2 * rx, 0], [-(rx * MyArc), 0, -rx, -(ry * MyArc), -rx, -ry], [0, -h + 2 * ry], [0, -(ry * MyArc), rx * MyArc, -ry, rx, -ry]], x + rx, y, // start of path + [1, 1], style); + return this; + }; + + /** + * Adds an ellipse to PDF + * + * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} rx Radius along x axis (in units declared at inception of PDF document) + * @param {Number} rx Radius along y axis (in units declared at inception of PDF document) + * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name ellipse + */ + API.ellipse = function (x, y, rx, ry, style) { + var lx = 4 / 3 * (Math.SQRT2 - 1) * rx, + ly = 4 / 3 * (Math.SQRT2 - 1) * ry; + + out([f2((x + rx) * k), f2((pageHeight - y) * k), 'm', f2((x + rx) * k), f2((pageHeight - (y - ly)) * k), f2((x + lx) * k), f2((pageHeight - (y - ry)) * k), f2(x * k), f2((pageHeight - (y - ry)) * k), 'c'].join(' ')); + out([f2((x - lx) * k), f2((pageHeight - (y - ry)) * k), f2((x - rx) * k), f2((pageHeight - (y - ly)) * k), f2((x - rx) * k), f2((pageHeight - y) * k), 'c'].join(' ')); + out([f2((x - rx) * k), f2((pageHeight - (y + ly)) * k), f2((x - lx) * k), f2((pageHeight - (y + ry)) * k), f2(x * k), f2((pageHeight - (y + ry)) * k), 'c'].join(' ')); + out([f2((x + lx) * k), f2((pageHeight - (y + ry)) * k), f2((x + rx) * k), f2((pageHeight - (y + ly)) * k), f2((x + rx) * k), f2((pageHeight - y) * k), 'c'].join(' ')); + + if (style !== null) { + out(getStyle(style)); + } + + return this; + }; + + /** + * Adds an circle to PDF + * + * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page + * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page + * @param {Number} r Radius (in units declared at inception of PDF document) + * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name circle + */ + API.circle = function (x, y, r, style) { + return this.ellipse(x, y, r, r, style); + }; + + /** + * Adds a properties to the PDF document + * + * @param {Object} A property_name-to-property_value object structure. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setProperties + */ + API.setProperties = function (properties) { + // copying only those properties we can render. + for (var property in documentProperties) { + if (documentProperties.hasOwnProperty(property) && properties[property]) { + documentProperties[property] = properties[property]; + } + } + return this; + }; + + /** + * Sets font size for upcoming text elements. + * + * @param {Number} size Font size in points. + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setFontSize + */ + API.setFontSize = function (size) { + activeFontSize = size; + return this; + }; + + /** + * Sets text font face, variant for upcoming text elements. + * See output of jsPDF.getFontList() for possible font names, styles. + * + * @param {String} fontName Font name or family. Example: "times" + * @param {String} fontStyle Font style or variant. Example: "italic" + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setFont + */ + API.setFont = function (fontName, fontStyle) { + activeFontKey = _getFont(fontName, fontStyle); + // if font is not found, the above line blows up and we never go further + return this; + }; + + /** + * Switches font style or variant for upcoming text elements, + * while keeping the font face or family same. + * See output of jsPDF.getFontList() for possible font names, styles. + * + * @param {String} style Font style or variant. Example: "italic" + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setFontStyle + */ + API.setFontStyle = API.setFontType = function (style) { + activeFontKey = _getFont(undefined, style); + // if font is not found, the above line blows up and we never go further + return this; + }; + + /** + * Returns an object - a tree of fontName to fontStyle relationships available to + * active PDF document. + * + * @public + * @function + * @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... } + * @methodOf jsPDF# + * @name getFontList + */ + API.getFontList = function () { + // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added. + var list = {}, + fontName, + fontStyle, + tmp; + + for (fontName in fontmap) { + if (fontmap.hasOwnProperty(fontName)) { + list[fontName] = tmp = []; + for (fontStyle in fontmap[fontName]) { + if (fontmap[fontName].hasOwnProperty(fontStyle)) { + tmp.push(fontStyle); + } + } + } + } + + return list; + }; + + /** + * Add a custom font. + * + * @param {String} Postscript name of the Font. Example: "Menlo-Regular" + * @param {String} Name of font-family from @font-face definition. Example: "Menlo Regular" + * @param {String} Font style. Example: "normal" + * @function + * @returns the {fontKey} (same as the internal method) + * @methodOf jsPDF# + * @name addFont + */ + API.addFont = function (postScriptName, fontName, fontStyle) { + addFont(postScriptName, fontName, fontStyle, 'StandardEncoding'); + }; + + /** + * Sets line width for upcoming lines. + * + * @param {Number} width Line width (in units declared at inception of PDF document) + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setLineWidth + */ + API.setLineWidth = function (width) { + out((width * k).toFixed(2) + ' w'); + return this; + }; + + /** + * Sets the stroke color for upcoming elements. + * + * Depending on the number of arguments given, Gray, RGB, or CMYK + * color space is implied. + * + * When only ch1 is given, "Gray" color space is implied and it + * must be a value in the range from 0.00 (solid black) to to 1.00 (white) + * if values are communicated as String types, or in range from 0 (black) + * to 255 (white) if communicated as Number type. + * The RGB-like 0-255 range is provided for backward compatibility. + * + * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each + * value must be in the range from 0.00 (minimum intensity) to to 1.00 + * (max intensity) if values are communicated as String types, or + * from 0 (min intensity) to to 255 (max intensity) if values are communicated + * as Number types. + * The RGB-like 0-255 range is provided for backward compatibility. + * + * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each + * value must be a in the range from 0.00 (0% concentration) to to + * 1.00 (100% concentration) + * + * Because JavaScript treats fixed point numbers badly (rounds to + * floating point nearest to binary representation) it is highly advised to + * communicate the fractional numbers as String types, not JavaScript Number type. + * + * @param {Number|String} ch1 Color channel value + * @param {Number|String} ch2 Color channel value + * @param {Number|String} ch3 Color channel value + * @param {Number|String} ch4 Color channel value + * + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setDrawColor + */ + API.setDrawColor = function (ch1, ch2, ch3, ch4) { + var color; + if (ch2 === undefined || ch4 === undefined && ch1 === ch2 === ch3) { + // Gray color space. + if (typeof ch1 === 'string') { + color = ch1 + ' G'; + } else { + color = f2(ch1 / 255) + ' G'; + } + } else if (ch4 === undefined) { + // RGB + if (typeof ch1 === 'string') { + color = [ch1, ch2, ch3, 'RG'].join(' '); + } else { + color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' '); + } + } else { + // CMYK + if (typeof ch1 === 'string') { + color = [ch1, ch2, ch3, ch4, 'K'].join(' '); + } else { + color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' '); + } + } + + out(color); + return this; + }; + + /** + * Sets the fill color for upcoming elements. + * + * Depending on the number of arguments given, Gray, RGB, or CMYK + * color space is implied. + * + * When only ch1 is given, "Gray" color space is implied and it + * must be a value in the range from 0.00 (solid black) to to 1.00 (white) + * if values are communicated as String types, or in range from 0 (black) + * to 255 (white) if communicated as Number type. + * The RGB-like 0-255 range is provided for backward compatibility. + * + * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each + * value must be in the range from 0.00 (minimum intensity) to to 1.00 + * (max intensity) if values are communicated as String types, or + * from 0 (min intensity) to to 255 (max intensity) if values are communicated + * as Number types. + * The RGB-like 0-255 range is provided for backward compatibility. + * + * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each + * value must be a in the range from 0.00 (0% concentration) to to + * 1.00 (100% concentration) + * + * Because JavaScript treats fixed point numbers badly (rounds to + * floating point nearest to binary representation) it is highly advised to + * communicate the fractional numbers as String types, not JavaScript Number type. + * + * @param {Number|String} ch1 Color channel value + * @param {Number|String} ch2 Color channel value + * @param {Number|String} ch3 Color channel value + * @param {Number|String} ch4 Color channel value + * + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setFillColor + */ + API.setFillColor = function (ch1, ch2, ch3, ch4) { + var color; + + if (ch2 === undefined || ch4 === undefined && ch1 === ch2 === ch3) { + // Gray color space. + if (typeof ch1 === 'string') { + color = ch1 + ' g'; + } else { + color = f2(ch1 / 255) + ' g'; + } + } else if (ch4 === undefined || (typeof ch4 === 'undefined' ? 'undefined' : _typeof(ch4)) === 'object') { + // RGB + if (typeof ch1 === 'string') { + color = [ch1, ch2, ch3, 'rg'].join(' '); + } else { + color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' '); + } + if (ch4 && ch4.a === 0) { + //TODO Implement transparency. + //WORKAROUND use white for now + color = ['255', '255', '255', 'rg'].join(' '); + } + } else { + // CMYK + if (typeof ch1 === 'string') { + color = [ch1, ch2, ch3, ch4, 'k'].join(' '); + } else { + color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' '); + } + } + + out(color); + return this; + }; + + /** + * Sets the text color for upcoming elements. + * If only one, first argument is given, + * treats the value as gray-scale color value. + * + * @param {Number} r Red channel color value in range 0-255 or {String} r color value in hexadecimal, example: '#FFFFFF' + * @param {Number} g Green channel color value in range 0-255 + * @param {Number} b Blue channel color value in range 0-255 + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setTextColor + */ + API.setTextColor = function (r, g, b) { + if (typeof r === 'string' && /^#[0-9A-Fa-f]{6}$/.test(r)) { + var hex = parseInt(r.substr(1), 16); + r = hex >> 16 & 255; + g = hex >> 8 & 255; + b = hex & 255; + } + + if (r === 0 && g === 0 && b === 0 || typeof g === 'undefined') { + textColor = f3(r / 255) + ' g'; + } else { + textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' '); + } + return this; + }; + + /** + * Is an Object providing a mapping from human-readable to + * integer flag values designating the varieties of line cap + * and join styles. + * + * @returns {Object} + * @fieldOf jsPDF# + * @name CapJoinStyles + */ + API.CapJoinStyles = { + 0: 0, + 'butt': 0, + 'but': 0, + 'miter': 0, + 1: 1, + 'round': 1, + 'rounded': 1, + 'circle': 1, + 2: 2, + 'projecting': 2, + 'project': 2, + 'square': 2, + 'bevel': 2 + }; + + /** + * Sets the line cap styles + * See {jsPDF.CapJoinStyles} for variants + * + * @param {String|Number} style A string or number identifying the type of line cap + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setLineCap + */ + API.setLineCap = function (style) { + var id = this.CapJoinStyles[style]; + if (id === undefined) { + throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); + } + lineCapID = id; + out(id + ' J'); + + return this; + }; + + /** + * Sets the line join styles + * See {jsPDF.CapJoinStyles} for variants + * + * @param {String|Number} style A string or number identifying the type of line join + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name setLineJoin + */ + API.setLineJoin = function (style) { + var id = this.CapJoinStyles[style]; + if (id === undefined) { + throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles"); + } + lineJoinID = id; + out(id + ' j'); + + return this; + }; + + // Output is both an internal (for plugins) and external function + API.output = _output; + + /** + * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf') + * @param {String} filename The filename including extension. + * + * @function + * @returns {jsPDF} + * @methodOf jsPDF# + * @name save + */ + API.save = function (filename) { + API.output('save', filename); + }; + + // applying plugins (more methods) ON TOP of built-in API. + // this is intentional as we allow plugins to override + // built-ins + for (var plugin in jsPDF.API) { + if (jsPDF.API.hasOwnProperty(plugin)) { + if (plugin === 'events' && jsPDF.API.events.length) { + (function (events, newEvents) { + + // jsPDF.API.events is a JS Array of Arrays + // where each Array is a pair of event name, handler + // Events were added by plugins to the jsPDF instantiator. + // These are always added to the new instance and some ran + // during instantiation. + var eventname, handler_and_args, i; + + for (i = newEvents.length - 1; i !== -1; i--) { + // subscribe takes 3 args: 'topic', function, runonce_flag + // if undefined, runonce is false. + // users can attach callback directly, + // or they can attach an array with [callback, runonce_flag] + // that's what the "apply" magic is for below. + eventname = newEvents[i][0]; + handler_and_args = newEvents[i][1]; + events.subscribe.apply(events, [eventname].concat(typeof handler_and_args === 'function' ? [handler_and_args] : handler_and_args)); + } + })(events, jsPDF.API.events); + } else { + API[plugin] = jsPDF.API[plugin]; + } + } + } + + ////////////////////////////////////////////////////// + // continuing initialization of jsPDF Document object + ////////////////////////////////////////////////////// + // Add the first page automatically + addFonts(); + activeFontKey = 'F1'; + _addPage(format, orientation); + + events.publish('initialized'); + return API; + } + + /** + * jsPDF.API is a STATIC property of jsPDF class. + * jsPDF.API is an object you can add methods and properties to. + * The methods / properties you add will show up in new jsPDF objects. + * + * One property is prepopulated. It is the 'events' Object. Plugin authors can add topics, + * callbacks to this object. These will be reassigned to all new instances of jsPDF. + * Examples: + * jsPDF.API.events['initialized'] = function(){ 'this' is API object } + * jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object } + * + * @static + * @public + * @memberOf jsPDF + * @name API + * + * @example + * jsPDF.API.mymethod = function(){ + * // 'this' will be ref to internal API object. see jsPDF source + * // , so you can refer to built-in methods like so: + * // this.line(....) + * // this.text(....) + * } + * var pdfdoc = new jsPDF() + * pdfdoc.mymethod() // <- !!!!!! + */ + jsPDF.API = { + events: [] + }; + jsPDF.version = "1.x-master"; + + if (typeof define === 'function' && define.amd) { + define('jsPDF', function () { + return jsPDF; + }); + } else if (typeof module !== 'undefined' && module.exports) { + module.exports = jsPDF; + } else { + global.jsPDF = jsPDF; + } + return jsPDF; +}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || undefined); + + +/** + * jsPDF AcroForm Plugin + * Copyright (c) 2016 Alexander Weidt, https://github.com/BiggA94 + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + +(window.AcroForm = function (jsPDFAPI) { + 'use strict'; + + var AcroForm = window.AcroForm; + + AcroForm.scale = function (x) { + return x * (acroformPlugin.internal.scaleFactor / 1); // 1 = (96 / 72) + }; + AcroForm.antiScale = function (x) { + return 1 / acroformPlugin.internal.scaleFactor * x; + }; + + var acroformPlugin = { + fields: [], + xForms: [], + /** + * acroFormDictionaryRoot contains information about the AcroForm Dictionary + * 0: The Event-Token, the AcroFormDictionaryCallback has + * 1: The Object ID of the Root + */ + acroFormDictionaryRoot: null, + /** + * After the PDF gets evaluated, the reference to the root has to be reset, + * this indicates, whether the root has already been printed out + */ + printedOut: false, + internal: null + }; + + jsPDF.API.acroformPlugin = acroformPlugin; + + var annotReferenceCallback = function annotReferenceCallback() { + for (var i in this.acroformPlugin.acroFormDictionaryRoot.Fields) { + var formObject = this.acroformPlugin.acroFormDictionaryRoot.Fields[i]; + // add Annot Reference! + if (formObject.hasAnnotation) { + // If theres an Annotation Widget in the Form Object, put the Reference in the /Annot array + createAnnotationReference.call(this, formObject); + } + } + }; + + var createAcroForm = function createAcroForm() { + if (this.acroformPlugin.acroFormDictionaryRoot) { + //return; + throw new Error("Exception while creating AcroformDictionary"); + } + + // The Object Number of the AcroForm Dictionary + this.acroformPlugin.acroFormDictionaryRoot = new AcroForm.AcroFormDictionary(); + + this.acroformPlugin.internal = this.internal; + + // add Callback for creating the AcroForm Dictionary + this.acroformPlugin.acroFormDictionaryRoot._eventID = this.internal.events.subscribe('postPutResources', AcroFormDictionaryCallback); + + this.internal.events.subscribe('buildDocument', annotReferenceCallback); //buildDocument + + // Register event, that is triggered when the DocumentCatalog is written, in order to add /AcroForm + this.internal.events.subscribe('putCatalog', putCatalogCallback); + + // Register event, that creates all Fields + this.internal.events.subscribe('postPutPages', createFieldCallback); + }; + + /** + * Create the Reference to the widgetAnnotation, so that it gets referenced in the Annot[] int the+ + * (Requires the Annotation Plugin) + */ + var createAnnotationReference = function createAnnotationReference(object) { + var options = { + type: 'reference', + object: object + }; + jsPDF.API.annotationPlugin.annotations[this.internal.getPageInfo(object.page).pageNumber].push(options); + }; + + var putForm = function putForm(formObject) { + if (this.acroformPlugin.printedOut) { + this.acroformPlugin.printedOut = false; + this.acroformPlugin.acroFormDictionaryRoot = null; + } + if (!this.acroformPlugin.acroFormDictionaryRoot) { + createAcroForm.call(this); + } + this.acroformPlugin.acroFormDictionaryRoot.Fields.push(formObject); + }; + + // Callbacks + + var putCatalogCallback = function putCatalogCallback() { + //Put reference to AcroForm to DocumentCatalog + if (typeof this.acroformPlugin.acroFormDictionaryRoot != 'undefined') { + // for safety, shouldn't normally be the case + this.internal.write('/AcroForm ' + this.acroformPlugin.acroFormDictionaryRoot.objId + ' ' + 0 + ' R'); + } else { + console.log('Root missing...'); + } + }; + + /** + * Adds /Acroform X 0 R to Document Catalog, + * and creates the AcroForm Dictionary + */ + var AcroFormDictionaryCallback = function AcroFormDictionaryCallback() { + // Remove event + this.internal.events.unsubscribe(this.acroformPlugin.acroFormDictionaryRoot._eventID); + + delete this.acroformPlugin.acroFormDictionaryRoot._eventID; + + this.acroformPlugin.printedOut = true; + }; + + /** + * Creates the single Fields and writes them into the Document + * + * If fieldArray is set, use the fields that are inside it instead of the fields from the AcroRoot + * (for the FormXObjects...) + */ + var createFieldCallback = function createFieldCallback(fieldArray) { + var standardFields = !fieldArray; + + if (!fieldArray) { + // in case there is no fieldArray specified, we want to print out the Fields of the AcroForm + // Print out Root + this.internal.newObjectDeferredBegin(this.acroformPlugin.acroFormDictionaryRoot.objId); + this.internal.out(this.acroformPlugin.acroFormDictionaryRoot.getString()); + } + + var fieldArray = fieldArray || this.acroformPlugin.acroFormDictionaryRoot.Kids; + + for (var i in fieldArray) { + var form = fieldArray[i]; + + var oldRect = form.Rect; + + if (form.Rect) { + form.Rect = AcroForm.internal.calculateCoordinates.call(this, form.Rect); + } + + // Start Writing the Object + this.internal.newObjectDeferredBegin(form.objId); + + var content = ""; + content += form.objId + " 0 obj\n"; + + content += "<<\n" + form.getContent(); + + form.Rect = oldRect; + + if (form.hasAppearanceStream && !form.appearanceStreamContent) { + // Calculate Appearance + var appearance = AcroForm.internal.calculateAppearanceStream.call(this, form); + content += "/AP << /N " + appearance + " >>\n"; + + this.acroformPlugin.xForms.push(appearance); + } + + // Assume AppearanceStreamContent is a Array with N,R,D (at least one of them!) + if (form.appearanceStreamContent) { + content += "/AP << "; + // Iterate over N,R and D + for (var k in form.appearanceStreamContent) { + var value = form.appearanceStreamContent[k]; + content += "/" + k + " "; + content += "<< "; + if (Object.keys(value).length >= 1 || Array.isArray(value)) { + // appearanceStream is an Array or Object! + for (var i in value) { + var obj = value[i]; + if (typeof obj === 'function') { + // if Function is referenced, call it in order to get the FormXObject + obj = obj.call(this, form); + } + content += "/" + i + " " + obj + " "; + + // In case the XForm is already used, e.g. OffState of CheckBoxes, don't add it + if (!(this.acroformPlugin.xForms.indexOf(obj) >= 0)) this.acroformPlugin.xForms.push(obj); + } + } else { + var obj = value; + if (typeof obj === 'function') { + // if Function is referenced, call it in order to get the FormXObject + obj = obj.call(this, form); + } + content += "/" + i + " " + obj + " \n"; + if (!(this.acroformPlugin.xForms.indexOf(obj) >= 0)) this.acroformPlugin.xForms.push(obj); + } + content += " >>\n"; + } + + // appearance stream is a normal Object.. + content += ">>\n"; + } + + content += ">>\nendobj\n"; + + this.internal.out(content); + } + if (standardFields) { + createXFormObjectCallback.call(this, this.acroformPlugin.xForms); + } + }; + + var createXFormObjectCallback = function createXFormObjectCallback(fieldArray) { + for (var i in fieldArray) { + var key = i; + var form = fieldArray[i]; + // Start Writing the Object + this.internal.newObjectDeferredBegin(form && form.objId); + + var content = ""; + content += form ? form.getString() : ''; + this.internal.out(content); + + delete fieldArray[key]; + } + }; + + // Public: + + jsPDFAPI.addField = function (fieldObject) { + //var opt = parseOptions(fieldObject); + if (fieldObject instanceof AcroForm.TextField) { + addTextField.call(this, fieldObject); + } else if (fieldObject instanceof AcroForm.ChoiceField) { + addChoiceField.call(this, fieldObject); + } else if (fieldObject instanceof AcroForm.Button) { + addButton.call(this, fieldObject); + } else if (fieldObject instanceof AcroForm.ChildClass) { + putForm.call(this, fieldObject); + } else if (fieldObject) { + // try to put.. + putForm.call(this, fieldObject); + } + fieldObject.page = this.acroformPlugin.internal.getCurrentPageInfo().pageNumber; + return this; + }; + + // ############### sort in: + + /** + * Button + * FT = Btn + */ + var addButton = function addButton(options) { + var options = options || new AcroForm.Field(); + + options.FT = '/Btn'; + + /** + * Calculating the Ff entry: + * + * The Ff entry contains flags, that have to be set bitwise + * In the Following the number in the Comment is the BitPosition + */ + var flags = options.Ff || 0; + + // 17, Pushbutton + if (options.pushbutton) { + // Options.pushbutton should be 1 or 0 + flags = AcroForm.internal.setBitPosition(flags, 17); + delete options.pushbutton; + } + + //16, Radio + if (options.radio) { + //flags = options.Ff | options.radio << 15; + flags = AcroForm.internal.setBitPosition(flags, 16); + delete options.radio; + } + + // 15, NoToggleToOff (Radio buttons only + if (options.noToggleToOff) { + //flags = options.Ff | options.noToggleToOff << 14; + flags = AcroForm.internal.setBitPosition(flags, 15); + //delete options.noToggleToOff; + } + + // In case, there is no Flag set, it is a check-box + options.Ff = flags; + + putForm.call(this, options); + }; + + var addTextField = function addTextField(options) { + var options = options || new AcroForm.Field(); + + options.FT = '/Tx'; + + /** + * Calculating the Ff entry: + * + * The Ff entry contains flags, that have to be set bitwise + * In the Following the number in the Comment is the BitPosition + */ + + var flags = options.Ff || 0; + + // 13, multiline + if (options.multiline) { + // Set Flag + flags = flags | 1 << 12; + // Remove multiline from FieldObject + //delete options.multiline; + } + + // 14, Password + if (options.password) { + flags = flags | 1 << 13; + //delete options.password; + } + + // 21, FileSelect, PDF 1.4... + if (options.fileSelect) { + flags = flags | 1 << 20; + //delete options.fileSelect; + } + + // 23, DoNotSpellCheck, PDF 1.4... + if (options.doNotSpellCheck) { + flags = flags | 1 << 22; + //delete options.doNotSpellCheck; + } + + // 24, DoNotScroll, PDF 1.4... + if (options.doNotScroll) { + flags = flags | 1 << 23; + //delete options.doNotScroll; + } + + options.Ff = options.Ff || flags; + + // Add field + putForm.call(this, options); + }; + + var addChoiceField = function addChoiceField(opt) { + var options = opt || new AcroForm.Field(); + + options.FT = '/Ch'; + + /** + * Calculating the Ff entry: + * + * The Ff entry contains flags, that have to be set bitwise + * In the Following the number in the Comment is the BitPosition + */ + + var flags = options.Ff || 0; + + // 18, Combo (If not set, the choiceField is a listBox!!) + if (options.combo) { + // Set Flag + flags = AcroForm.internal.setBitPosition(flags, 18); + // Remove combo from FieldObject + delete options.combo; + } + + // 19, Edit + if (options.edit) { + flags = AcroForm.internal.setBitPosition(flags, 19); + delete options.edit; + } + + // 20, Sort + if (options.sort) { + flags = AcroForm.internal.setBitPosition(flags, 20); + delete options.sort; + } + + // 22, MultiSelect (PDF 1.4) + if (options.multiSelect && this.internal.getPDFVersion() >= 1.4) { + flags = AcroForm.internal.setBitPosition(flags, 22); + delete options.multiSelect; + } + + // 23, DoNotSpellCheck (PDF 1.4) + if (options.doNotSpellCheck && this.internal.getPDFVersion() >= 1.4) { + flags = AcroForm.internal.setBitPosition(flags, 23); + delete options.doNotSpellCheck; + } + + options.Ff = flags; + + //options.hasAnnotation = true; + + // Add field + putForm.call(this, options); + }; +})(jsPDF.API); + +var AcroForm = window.AcroForm; + +AcroForm.internal = {}; + +AcroForm.createFormXObject = function (formObject) { + var xobj = new AcroForm.FormXObject(); + var height = AcroForm.Appearance.internal.getHeight(formObject) || 0; + var width = AcroForm.Appearance.internal.getWidth(formObject) || 0; + xobj.BBox = [0, 0, width, height]; + return xobj; +}; + +// Contains Methods for creating standard appearances +AcroForm.Appearance = { + CheckBox: { + createAppearanceStream: function createAppearanceStream() { + var appearance = { + N: { + On: AcroForm.Appearance.CheckBox.YesNormal + }, + D: { + On: AcroForm.Appearance.CheckBox.YesPushDown, + Off: AcroForm.Appearance.CheckBox.OffPushDown + } + }; + + return appearance; + }, + /** + * If any other icons are needed, the number between the brackets can be changed + * @returns {string} + */ + createMK: function createMK() { + // 3-> Hook + return "<< /CA (3)>>"; + }, + /** + * Returns the standard On Appearance for a CheckBox + * @returns {AcroForm.FormXObject} + */ + YesPushDown: function YesPushDown(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + // F13 is ZapfDingbats (Symbolic) + formObject.Q = 1; // set text-alignment as centered + var calcRes = AcroForm.internal.calculateX(formObject, "3", "ZapfDingbats", 50); + stream += "0.749023 g\n\ + 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ + f\n\ + BMC\n\ + q\n\ + 0 0 1 rg\n\ + /F13 " + calcRes.fontSize + " Tf 0 g\n\ + BT\n"; + stream += calcRes.text; + stream += "ET\n\ + Q\n\ + EMC\n"; + xobj.stream = stream; + return xobj; + }, + + YesNormal: function YesNormal(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + formObject.Q = 1; // set text-alignment as centered + var calcRes = AcroForm.internal.calculateX(formObject, "3", "ZapfDingbats", AcroForm.Appearance.internal.getHeight(formObject) * 0.9); + stream += "1 g\n\ +0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ +f\n\ +q\n\ +0 0 1 rg\n\ +0 0 " + (AcroForm.Appearance.internal.getWidth(formObject) - 1) + " " + (AcroForm.Appearance.internal.getHeight(formObject) - 1) + " re\n\ +W\n\ +n\n\ +0 g\n\ +BT\n\ +/F13 " + calcRes.fontSize + " Tf 0 g\n"; + stream += calcRes.text; + stream += "ET\n\ + Q\n"; + xobj.stream = stream; + return xobj; + }, + + /** + * Returns the standard Off Appearance for a CheckBox + * @returns {AcroForm.FormXObject} + */ + OffPushDown: function OffPushDown(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + stream += "0.749023 g\n\ + 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ + f\n"; + xobj.stream = stream; + return xobj; + } + }, + + RadioButton: { + Circle: { + createAppearanceStream: function createAppearanceStream(name) { + var appearanceStreamContent = { + D: { + 'Off': AcroForm.Appearance.RadioButton.Circle.OffPushDown + }, + N: {} + }; + appearanceStreamContent.N[name] = AcroForm.Appearance.RadioButton.Circle.YesNormal; + appearanceStreamContent.D[name] = AcroForm.Appearance.RadioButton.Circle.YesPushDown; + return appearanceStreamContent; + }, + createMK: function createMK() { + return "<< /CA (l)>>"; + }, + + YesNormal: function YesNormal(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + // Make the Radius of the Circle relative to min(height, width) of formObject + var DotRadius = AcroForm.Appearance.internal.getWidth(formObject) <= AcroForm.Appearance.internal.getHeight(formObject) ? AcroForm.Appearance.internal.getWidth(formObject) / 4 : AcroForm.Appearance.internal.getHeight(formObject) / 4; + // The Borderpadding... + DotRadius *= 0.9; + var c = AcroForm.Appearance.internal.Bezier_C; + /* + The Following is a Circle created with Bezier-Curves. + */ + stream += "q\n\ +1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ +" + DotRadius + " 0 m\n\ +" + DotRadius + " " + DotRadius * c + " " + DotRadius * c + " " + DotRadius + " 0 " + DotRadius + " c\n\ +-" + DotRadius * c + " " + DotRadius + " -" + DotRadius + " " + DotRadius * c + " -" + DotRadius + " 0 c\n\ +-" + DotRadius + " -" + DotRadius * c + " -" + DotRadius * c + " -" + DotRadius + " 0 -" + DotRadius + " c\n\ +" + DotRadius * c + " -" + DotRadius + " " + DotRadius + " -" + DotRadius * c + " " + DotRadius + " 0 c\n\ +f\n\ +Q\n"; + xobj.stream = stream; + return xobj; + }, + YesPushDown: function YesPushDown(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + var DotRadius = AcroForm.Appearance.internal.getWidth(formObject) <= AcroForm.Appearance.internal.getHeight(formObject) ? AcroForm.Appearance.internal.getWidth(formObject) / 4 : AcroForm.Appearance.internal.getHeight(formObject) / 4; + // The Borderpadding... + DotRadius *= 0.9; + // Save results for later use; no need to waste processor ticks on doing math + var k = DotRadius * 2; + // var c = AcroForm.Appearance.internal.Bezier_C; + var kc = k * AcroForm.Appearance.internal.Bezier_C; + var dc = DotRadius * AcroForm.Appearance.internal.Bezier_C; + // stream += "0.749023 g\n\ + // q\n\ + // 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ + // " + DotRadius * 2 + " 0 m\n\ + // " + DotRadius * 2 + " " + DotRadius * 2 * c + " " + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 " + DotRadius * 2 + " c\n\ + // -" + DotRadius * 2 * c + " " + DotRadius * 2 + " -" + DotRadius * 2 + " " + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 c\n\ + // -" + DotRadius * 2 + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 -" + DotRadius * 2 + " c\n\ + // " + DotRadius * 2 * c + " -" + DotRadius * 2 + " " + DotRadius * 2 + " -" + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 c\n\ + // f\n\ + // Q\n\ + // 0 g\n\ + // q\n\ + // 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ + // " + DotRadius + " 0 m\n\ + // " + DotRadius + " " + DotRadius * c + " " + DotRadius * c + " " + DotRadius + " 0 " + DotRadius + " c\n\ + // -" + DotRadius * c + " " + DotRadius + " -" + DotRadius + " " + DotRadius * c + " -" + DotRadius + " 0 c\n\ + // -" + DotRadius + " -" + DotRadius * c + " -" + DotRadius * c + " -" + DotRadius + " 0 -" + DotRadius + " c\n\ + // " + DotRadius * c + " -" + DotRadius + " " + DotRadius + " -" + DotRadius * c + " " + DotRadius + " 0 c\n\ + // f\n\ + // Q\n"; + + // FASTER VERSION with less processor ticks spent on math operations + stream += "0.749023 g\n\ + q\n\ + 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ +" + k + " 0 m\n\ +" + k + " " + kc + " " + kc + " " + k + " 0 " + k + " c\n\ +-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c\n\ +-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c\n\ +" + kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c\n\ + f\n\ + Q\n\ + 0 g\n\ + q\n\ + 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ +" + DotRadius + " 0 m\n\ +" + DotRadius + " " + dc + " " + dc + " " + DotRadius + " 0 " + DotRadius + " c\n\ +-" + dc + " " + DotRadius + " -" + DotRadius + " " + dc + " -" + DotRadius + " 0 c\n\ +-" + DotRadius + " -" + dc + " -" + dc + " -" + DotRadius + " 0 -" + DotRadius + " c\n\ +" + dc + " -" + DotRadius + " " + DotRadius + " -" + dc + " " + DotRadius + " 0 c\n\ + f\n\ + Q\n"; + xobj.stream = stream; + return xobj; + }, + OffPushDown: function OffPushDown(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + var DotRadius = AcroForm.Appearance.internal.getWidth(formObject) <= AcroForm.Appearance.internal.getHeight(formObject) ? AcroForm.Appearance.internal.getWidth(formObject) / 4 : AcroForm.Appearance.internal.getHeight(formObject) / 4; + // The Borderpadding... + DotRadius *= 0.9; + // Save results for later use; no need to waste processor ticks on doing math + var k = DotRadius * 2; + // var c = AcroForm.Appearance.internal.Bezier_C; + var kc = k * AcroForm.Appearance.internal.Bezier_C; + // stream += "0.749023 g\n\ + // q\n\ + // 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ + // " + DotRadius * 2 + " 0 m\n\ + // " + DotRadius * 2 + " " + DotRadius * 2 * c + " " + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 " + DotRadius * 2 + " c\n\ + // -" + DotRadius * 2 * c + " " + DotRadius * 2 + " -" + DotRadius * 2 + " " + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 c\n\ + // -" + DotRadius * 2 + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 * c + " -" + DotRadius * 2 + " 0 -" + DotRadius * 2 + " c\n\ + // " + DotRadius * 2 * c + " -" + DotRadius * 2 + " " + DotRadius * 2 + " -" + DotRadius * 2 * c + " " + DotRadius * 2 + " 0 c\n\ + // f\n\ + // Q\n"; + + // FASTER VERSION with less processor ticks spent on math operations + stream += "0.749023 g\n\ + q\n\ + 1 0 0 1 " + AcroForm.Appearance.internal.getWidth(formObject) / 2 + " " + AcroForm.Appearance.internal.getHeight(formObject) / 2 + " cm\n\ +" + k + " 0 m\n\ +" + k + " " + kc + " " + kc + " " + k + " 0 " + k + " c\n\ +-" + kc + " " + k + " -" + k + " " + kc + " -" + k + " 0 c\n\ +-" + k + " -" + kc + " -" + kc + " -" + k + " 0 -" + k + " c\n\ +" + kc + " -" + k + " " + k + " -" + kc + " " + k + " 0 c\n\ + f\n\ + Q\n"; + xobj.stream = stream; + return xobj; + } + }, + + Cross: { + /** + * Creates the Actual AppearanceDictionary-References + * @param name + * @returns + */ + createAppearanceStream: function createAppearanceStream(name) { + var appearanceStreamContent = { + D: { + 'Off': AcroForm.Appearance.RadioButton.Cross.OffPushDown + }, + N: {} + }; + appearanceStreamContent.N[name] = AcroForm.Appearance.RadioButton.Cross.YesNormal; + appearanceStreamContent.D[name] = AcroForm.Appearance.RadioButton.Cross.YesPushDown; + return appearanceStreamContent; + }, + createMK: function createMK() { + return "<< /CA (8)>>"; + }, + + YesNormal: function YesNormal(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + var cross = AcroForm.Appearance.internal.calculateCross(formObject); + stream += "q\n\ + 1 1 " + (AcroForm.Appearance.internal.getWidth(formObject) - 2) + " " + (AcroForm.Appearance.internal.getHeight(formObject) - 2) + " re\n\ + W\n\ + n\n\ + " + cross.x1.x + " " + cross.x1.y + " m\n\ + " + cross.x2.x + " " + cross.x2.y + " l\n\ + " + cross.x4.x + " " + cross.x4.y + " m\n\ + " + cross.x3.x + " " + cross.x3.y + " l\n\ + s\n\ + Q\n"; + xobj.stream = stream; + return xobj; + }, + YesPushDown: function YesPushDown(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var cross = AcroForm.Appearance.internal.calculateCross(formObject); + var stream = ""; + stream += "0.749023 g\n\ + 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ + f\n\ + q\n\ + 1 1 " + (AcroForm.Appearance.internal.getWidth(formObject) - 2) + " " + (AcroForm.Appearance.internal.getHeight(formObject) - 2) + " re\n\ + W\n\ + n\n\ + " + cross.x1.x + " " + cross.x1.y + " m\n\ + " + cross.x2.x + " " + cross.x2.y + " l\n\ + " + cross.x4.x + " " + cross.x4.y + " m\n\ + " + cross.x3.x + " " + cross.x3.y + " l\n\ + s\n\ + Q\n"; + xobj.stream = stream; + return xobj; + }, + OffPushDown: function OffPushDown(formObject) { + var xobj = AcroForm.createFormXObject(formObject); + var stream = ""; + stream += "0.749023 g\n\ + 0 0 " + AcroForm.Appearance.internal.getWidth(formObject) + " " + AcroForm.Appearance.internal.getHeight(formObject) + " re\n\ + f\n"; + xobj.stream = stream; + return xobj; + } + } + }, + + /** + * Returns the standard Appearance + * @returns {AcroForm.FormXObject} + */ + createDefaultAppearanceStream: function createDefaultAppearanceStream(formObject) { + var stream = ""; + // Set Helvetica to Standard Font (size: auto) + // Color: Black + stream += "/Helv 0 Tf 0 g"; + return stream; + } +}; + +AcroForm.Appearance.internal = { + Bezier_C: 0.551915024494, + + calculateCross: function calculateCross(formObject) { + var min = function min(x, y) { + return x > y ? y : x; + }; + + var width = AcroForm.Appearance.internal.getWidth(formObject); + var height = AcroForm.Appearance.internal.getHeight(formObject); + var a = min(width, height); + var cross = { + x1: { // upperLeft + x: (width - a) / 2, + y: (height - a) / 2 + a //height - borderPadding + }, + x2: { // lowerRight + x: (width - a) / 2 + a, + y: (height - a) / 2 //borderPadding + }, + x3: { // lowerLeft + x: (width - a) / 2, + y: (height - a) / 2 //borderPadding + }, + x4: { // upperRight + x: (width - a) / 2 + a, + y: (height - a) / 2 + a //height - borderPadding + } + }; + + return cross; + } +}; +AcroForm.Appearance.internal.getWidth = function (formObject) { + return formObject.Rect[2]; //(formObject.Rect[2] - formObject.Rect[0]) || 0; +}; +AcroForm.Appearance.internal.getHeight = function (formObject) { + return formObject.Rect[3]; //(formObject.Rect[1] - formObject.Rect[3]) || 0; +}; + +// ########################## + +//### For inheritance: +AcroForm.internal.inherit = function (child, parent) { + child.prototype = Object.create(parent.prototype); + child.prototype.constructor = child; +}; + +// ### Handy Functions: + +AcroForm.internal.arrayToPdfArray = function (array) { + if (Array.isArray(array)) { + var content = ' ['; + for (var i in array) { + var element = array[i].toString(); + content += element; + content += i < array.length - 1 ? ' ' : ''; + } + content += ']'; + + return content; + } +}; + +AcroForm.internal.toPdfString = function (string) { + string = string || ""; + + // put Bracket at the Beginning of the String + if (string.indexOf('(') !== 0) { + string = '(' + string; + } + + if (string.substring(string.length - 1) != ')') { + string += '('; + } + return string; +}; + +// ########################## +// Classes +// ########################## + + +AcroForm.PDFObject = function () { + // The Object ID in the PDF Object Model + // todo + var _objId; + Object.defineProperty(this, 'objId', { + get: function get() { + if (!_objId) { + if (this.internal) { + _objId = this.internal.newObjectDeferred(); + } else if (jsPDF.API.acroformPlugin.internal) { + // todo - find better option, that doesn't rely on a Global Static var + _objId = jsPDF.API.acroformPlugin.internal.newObjectDeferred(); + } + } + if (!_objId) { + console.log("Couldn't create Object ID"); + } + return _objId; + }, + configurable: false + }); +}; + +AcroForm.PDFObject.prototype.toString = function () { + return this.objId + " 0 R"; +}; + +AcroForm.PDFObject.prototype.getString = function () { + var res = this.objId + " 0 obj\n<<"; + var content = this.getContent(); + + res += content + ">>\n"; + if (this.stream) { + res += "stream\n"; + res += this.stream; + res += "endstream\n"; + } + res += "endobj\n"; + return res; +}; + +AcroForm.PDFObject.prototype.getContent = function () { + /** + * Prints out all enumerable Variables from the Object + * @param fieldObject + * @returns {string} + */ + var createContentFromFieldObject = function createContentFromFieldObject(fieldObject) { + var content = ''; + + var keys = Object.keys(fieldObject).filter(function (key) { + return key != 'content' && key != 'appearanceStreamContent' && key.substring(0, 1) != "_"; + }); + + for (var i in keys) { + var key = keys[i]; + var value = fieldObject[key]; + + /*if (key == 'Rect' && value) { + value = AcroForm.internal.calculateCoordinates.call(jsPDF.API.acroformPlugin.internal, value); + }*/ + + if (value) { + if (Array.isArray(value)) { + content += '/' + key + ' ' + AcroForm.internal.arrayToPdfArray(value) + "\n"; + } else if (value instanceof AcroForm.PDFObject) { + // In case it is a reference to another PDFObject, take the referennce number + content += '/' + key + ' ' + value.objId + " 0 R" + "\n"; + } else { + content += '/' + key + ' ' + value + '\n'; + } + } + } + return content; + }; + + var object = ""; + + object += createContentFromFieldObject(this); + return object; +}; + +AcroForm.FormXObject = function () { + AcroForm.PDFObject.call(this); + this.Type = "/XObject"; + this.Subtype = "/Form"; + this.FormType = 1; + this.BBox; + this.Matrix; + this.Resources = "2 0 R"; + this.PieceInfo; + var _stream; + Object.defineProperty(this, 'Length', { + enumerable: true, + get: function get() { + return _stream !== undefined ? _stream.length : 0; + } + }); + Object.defineProperty(this, 'stream', { + enumerable: false, + set: function set(val) { + _stream = val; + }, + get: function get() { + if (_stream) { + return _stream; + } else { + return null; + } + } + }); +}; + +AcroForm.internal.inherit(AcroForm.FormXObject, AcroForm.PDFObject); + +AcroForm.AcroFormDictionary = function () { + AcroForm.PDFObject.call(this); + var _Kids = []; + Object.defineProperty(this, 'Kids', { + enumerable: false, + configurable: true, + get: function get() { + if (_Kids.length > 0) { + return _Kids; + } else { + return; + } + } + }); + Object.defineProperty(this, 'Fields', { + enumerable: true, + configurable: true, + get: function get() { + return _Kids; + } + }); + // Default Appearance + this.DA; +}; + +AcroForm.internal.inherit(AcroForm.AcroFormDictionary, AcroForm.PDFObject); + +// ##### The Objects, the User can Create: + + +// The Field Object contains the Variables, that every Field needs +// Rectangle for Appearance: lower_left_X, lower_left_Y, width, height +AcroForm.Field = function () { + 'use strict'; + + AcroForm.PDFObject.call(this); + + var _Rect; + Object.defineProperty(this, 'Rect', { + enumerable: true, + configurable: false, + get: function get() { + if (!_Rect) { + return; + } + var tmp = _Rect; + //var calculatedRes = AcroForm.internal.calculateCoordinates(_Rect); // do later! + return tmp; + }, + set: function set(val) { + _Rect = val; + } + }); + + var _FT = ""; + Object.defineProperty(this, 'FT', { + enumerable: true, + set: function set(val) { + _FT = val; + }, + get: function get() { + return _FT; + } + }); + /** + * The Partial name of the Field Object. + * It has to be unique. + */ + var _T; + + Object.defineProperty(this, 'T', { + enumerable: true, + configurable: false, + set: function set(val) { + _T = val; + }, + get: function get() { + if (!_T || _T.length < 1) { + if (this instanceof AcroForm.ChildClass) { + // In case of a Child from a Radio´Group, you don't need a FieldName!!! + return; + } + return "(FieldObject" + AcroForm.Field.FieldNum++ + ")"; + } + if (_T.substring(0, 1) == "(" && _T.substring(_T.length - 1)) { + return _T; + } + return "(" + _T + ")"; + } + }); + + var _DA; + // Defines the default appearance (Needed for variable Text) + Object.defineProperty(this, 'DA', { + enumerable: true, + get: function get() { + if (!_DA) { + return; + } + return '(' + _DA + ')'; + }, + set: function set(val) { + _DA = val; + } + }); + + var _DV; + // Defines the default value + Object.defineProperty(this, 'DV', { + enumerable: true, + configurable: true, + get: function get() { + if (!_DV) { + return; + } + return _DV; + }, + set: function set(val) { + _DV = val; + } + }); + + //this.Type = "/Annot"; + //this.Subtype = "/Widget"; + Object.defineProperty(this, 'Type', { + enumerable: true, + get: function get() { + return this.hasAnnotation ? "/Annot" : null; + } + }); + + Object.defineProperty(this, 'Subtype', { + enumerable: true, + get: function get() { + return this.hasAnnotation ? "/Widget" : null; + } + }); + + /** + * + * @type {Array} + */ + this.BG; + + Object.defineProperty(this, 'hasAnnotation', { + enumerable: false, + get: function get() { + if (this.Rect || this.BC || this.BG) { + return true; + } + return false; + } + }); + + Object.defineProperty(this, 'hasAppearanceStream', { + enumerable: false, + configurable: true, + writable: true + }); + + Object.defineProperty(this, 'page', { + enumerable: false, + configurable: true, + writable: true + }); +}; +AcroForm.Field.FieldNum = 0; + +AcroForm.internal.inherit(AcroForm.Field, AcroForm.PDFObject); + +AcroForm.ChoiceField = function () { + AcroForm.Field.call(this); + // Field Type = Choice Field + this.FT = "/Ch"; + // options + this.Opt = []; + this.V = '()'; + // Top Index + this.TI = 0; + /** + * Defines, whether the + * @type {boolean} + */ + this.combo = false; + /** + * Defines, whether the Choice Field is an Edit Field. + * An Edit Field is automatically an Combo Field. + */ + Object.defineProperty(this, 'edit', { + enumerable: true, + set: function set(val) { + if (val == true) { + this._edit = true; + // ComboBox has to be true + this.combo = true; + } else { + this._edit = false; + } + }, + get: function get() { + if (!this._edit) { + return false; + } + return this._edit; + }, + configurable: false + }); + this.hasAppearanceStream = true; + Object.defineProperty(this, 'V', { + get: function get() { + AcroForm.internal.toPdfString(); + } + }); +}; +AcroForm.internal.inherit(AcroForm.ChoiceField, AcroForm.Field); +window["ChoiceField"] = AcroForm.ChoiceField; + +AcroForm.ListBox = function () { + AcroForm.ChoiceField.call(this); + //var combo = true; +}; +AcroForm.internal.inherit(AcroForm.ListBox, AcroForm.ChoiceField); +window["ListBox"] = AcroForm.ListBox; + +AcroForm.ComboBox = function () { + AcroForm.ListBox.call(this); + this.combo = true; +}; +AcroForm.internal.inherit(AcroForm.ComboBox, AcroForm.ListBox); +window["ComboBox"] = AcroForm.ComboBox; + +AcroForm.EditBox = function () { + AcroForm.ComboBox.call(this); + this.edit = true; +}; +AcroForm.internal.inherit(AcroForm.EditBox, AcroForm.ComboBox); +window["EditBox"] = AcroForm.EditBox; + +AcroForm.Button = function () { + AcroForm.Field.call(this); + this.FT = "/Btn"; + //this.hasAnnotation = true; +}; +AcroForm.internal.inherit(AcroForm.Button, AcroForm.Field); +window["Button"] = AcroForm.Button; + +AcroForm.PushButton = function () { + AcroForm.Button.call(this); + this.pushbutton = true; +}; +AcroForm.internal.inherit(AcroForm.PushButton, AcroForm.Button); +window["PushButton"] = AcroForm.PushButton; + +AcroForm.RadioButton = function () { + AcroForm.Button.call(this); + this.radio = true; + var _Kids = []; + Object.defineProperty(this, 'Kids', { + enumerable: true, + get: function get() { + if (_Kids.length > 0) { + return _Kids; + } + } + }); + + Object.defineProperty(this, '__Kids', { + get: function get() { + return _Kids; + } + }); + + var _noToggleToOff; + + Object.defineProperty(this, 'noToggleToOff', { + enumerable: false, + get: function get() { + return _noToggleToOff; + }, + set: function set(val) { + _noToggleToOff = val; + } + }); + + //this.hasAnnotation = false; +}; +AcroForm.internal.inherit(AcroForm.RadioButton, AcroForm.Button); +window["RadioButton"] = AcroForm.RadioButton; + +/* + * The Child classs of a RadioButton (the radioGroup) + * -> The single Buttons + */ +AcroForm.ChildClass = function (parent, name) { + AcroForm.Field.call(this); + this.Parent = parent; + + // todo: set AppearanceType as variable that can be set from the outside... + this._AppearanceType = AcroForm.Appearance.RadioButton.Circle; // The Default appearanceType is the Circle + this.appearanceStreamContent = this._AppearanceType.createAppearanceStream(name); + + // Set Print in the Annot Flag + this.F = AcroForm.internal.setBitPosition(this.F, 3, 1); + + // Set AppearanceCharacteristicsDictionary with default appearance if field is not interacting with user + this.MK = this._AppearanceType.createMK(); // (8) -> Cross, (1)-> Circle, ()-> nothing + + // Default Appearance is Off + this.AS = "/Off"; // + name; + + this._Name = name; +}; +AcroForm.internal.inherit(AcroForm.ChildClass, AcroForm.Field); + +AcroForm.RadioButton.prototype.setAppearance = function (appearance) { + if (!('createAppearanceStream' in appearance && 'createMK' in appearance)) { + console.log("Couldn't assign Appearance to RadioButton. Appearance was Invalid!"); + return; + } + for (var i in this.__Kids) { + var child = this.__Kids[i]; + + child.appearanceStreamContent = appearance.createAppearanceStream(child._Name); + child.MK = appearance.createMK(); + } +}; + +AcroForm.RadioButton.prototype.createOption = function (name) { + var parent = this; + var child = new AcroForm.ChildClass(parent, name); + // Add to Parent + this.__Kids.push(child); + + jsPDF.API.addField(child); + + return child; +}; + +AcroForm.CheckBox = function () { + Button.call(this); + this.appearanceStreamContent = AcroForm.Appearance.CheckBox.createAppearanceStream(); + this.MK = AcroForm.Appearance.CheckBox.createMK(); + this.AS = "/On"; + this.V = "/On"; +}; +AcroForm.internal.inherit(AcroForm.CheckBox, AcroForm.Button); +window["CheckBox"] = AcroForm.CheckBox; + +AcroForm.TextField = function () { + AcroForm.Field.call(this); + this.DA = AcroForm.Appearance.createDefaultAppearanceStream(); + this.F = 4; + var _V; + Object.defineProperty(this, 'V', { + get: function get() { + if (_V) { + return "(" + _V + ")"; + } else { + return _V; + } + }, + enumerable: true, + set: function set(val) { + _V = val; + } + }); + + var _DV; + Object.defineProperty(this, 'DV', { + get: function get() { + if (_DV) { + return "(" + _DV + ")"; + } else { + return _DV; + } + }, + enumerable: true, + set: function set(val) { + _DV = val; + } + }); + + var _multiline = false; + Object.defineProperty(this, 'multiline', { + enumerable: false, + get: function get() { + return _multiline; + }, + set: function set(val) { + _multiline = val; + } + }); + + //this.multiline = false; + //this.password = false; + /** + * For PDF 1.4 + * @type {boolean} + */ + //this.fileSelect = false; + /** + * For PDF 1.4 + * @type {boolean} + */ + //this.doNotSpellCheck = false; + /** + * For PDF 1.4 + * @type {boolean} + */ + //this.doNotScroll = false; + + var _MaxLen = false; + Object.defineProperty(this, 'MaxLen', { + enumerable: true, + get: function get() { + return _MaxLen; + }, + set: function set(val) { + _MaxLen = val; + } + }); + + Object.defineProperty(this, 'hasAppearanceStream', { + enumerable: false, + get: function get() { + return this.V || this.DV; + } + }); +}; +AcroForm.internal.inherit(AcroForm.TextField, AcroForm.Field); +window["TextField"] = AcroForm.TextField; + +AcroForm.PasswordField = function () { + TextField.call(this); + Object.defineProperty(this, 'password', { + value: true, + enumerable: false, + configurable: false, + writable: false + }); +}; +AcroForm.internal.inherit(AcroForm.PasswordField, AcroForm.TextField); +window["PasswordField"] = AcroForm.PasswordField; + +// ############ internal functions + +/* + * small workaround for calculating the TextMetric approximately + * @param text + * @param fontsize + * @returns {TextMetrics} (Has Height and Width) + */ +AcroForm.internal.calculateFontSpace = function (text, fontsize, fonttype) { + var fonttype = fonttype || "helvetica"; + //re-use canvas object for speed improvements + var canvas = AcroForm.internal.calculateFontSpace.canvas || (AcroForm.internal.calculateFontSpace.canvas = document.createElement('canvas')); + + var context = canvas.getContext('2d'); + context.save(); + var newFont = fontsize + " " + fonttype; + context.font = newFont; + var res = context.measureText(text); + context.fontcolor = 'black'; + // Calculate height: + var context = canvas.getContext('2d'); + res.height = context.measureText("3").width * 1.5; // 3 because in ZapfDingbats its a Hook and a 3 in normal fonts + context.restore(); + + return res; +}; + +AcroForm.internal.calculateX = function (formObject, text, font, maxFontSize) { + var maxFontSize = maxFontSize || 12; + var font = font || "helvetica"; + var returnValue = { + text: "", + fontSize: "" + }; + // Remove Brackets + text = text.substr(0, 1) == '(' ? text.substr(1) : text; + text = text.substr(text.length - 1) == ')' ? text.substr(0, text.length - 1) : text; + // split into array of words + var textSplit = text.split(' '); + + /** + * the color could be ((alpha)||(r,g,b)||(c,m,y,k)) + * @type {string} + */ + var fontSize = maxFontSize; // The Starting fontSize (The Maximum) + var lineSpacing = 2; + var borderPadding = 2; + + var height = AcroForm.Appearance.internal.getHeight(formObject) || 0; + height = height < 0 ? -height : height; + var width = AcroForm.Appearance.internal.getWidth(formObject) || 0; + width = width < 0 ? -width : width; + + var isSmallerThanWidth = function isSmallerThanWidth(i, lastLine, fontSize) { + if (i + 1 < textSplit.length) { + var tmp = lastLine + " " + textSplit[i + 1]; + var TextWidth = AcroForm.internal.calculateFontSpace(tmp, fontSize + "px", font).width; + var FieldWidth = width - 2 * borderPadding; + return TextWidth <= FieldWidth; + } else { + return false; + } + }; + + fontSize++; + FontSize: while (true) { + var text = ""; + fontSize--; + var textHeight = AcroForm.internal.calculateFontSpace("3", fontSize + "px", font).height; + var startY = formObject.multiline ? height - fontSize : (height - textHeight) / 2; + startY += lineSpacing; + var startX = -borderPadding; + + var lastX = startX, + lastY = startY; + var firstWordInLine = 0, + lastWordInLine = 0; + var lastLength = 0; + + if (fontSize == 0) { + // In case, the Text doesn't fit at all + fontSize = 12; + text = "(...) Tj\n"; + text += "% Width of Text: " + AcroForm.internal.calculateFontSpace(text, "1px").width + ", FieldWidth:" + width + "\n"; + break; + } + + lastLength = AcroForm.internal.calculateFontSpace(textSplit[0] + " ", fontSize + "px", font).width; + + var lastLine = ""; + var lineCount = 0; + Line: for (var i in textSplit) { + lastLine += textSplit[i] + " "; + // Remove last blank + lastLine = lastLine.substr(lastLine.length - 1) == " " ? lastLine.substr(0, lastLine.length - 1) : lastLine; + var key = parseInt(i); + lastLength = AcroForm.internal.calculateFontSpace(lastLine + " ", fontSize + "px", font).width; + var nextLineIsSmaller = isSmallerThanWidth(key, lastLine, fontSize); + var isLastWord = i >= textSplit.length - 1; + if (nextLineIsSmaller && !isLastWord) { + lastLine += " "; + continue; // Line + } else if (!nextLineIsSmaller && !isLastWord) { + if (!formObject.multiline) { + continue FontSize; + } else { + if ((textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) { + // If the Text is higher than the FieldObject + continue FontSize; + } + lastWordInLine = key; + // go on + } + } else if (isLastWord) { + lastWordInLine = key; + } else { + if (formObject.multiline && (textHeight + lineSpacing) * (lineCount + 2) + lineSpacing > height) { + // If the Text is higher than the FieldObject + continue FontSize; + } + } + + var line = ''; + + for (var x = firstWordInLine; x <= lastWordInLine; x++) { + line += textSplit[x] + ' '; + } + + // Remove last blank + line = line.substr(line.length - 1) == " " ? line.substr(0, line.length - 1) : line; + //lastLength -= blankSpace.width; + lastLength = AcroForm.internal.calculateFontSpace(line, fontSize + "px", font).width; + + // Calculate startX + switch (formObject.Q) { + case 2: + // Right justified + startX = width - lastLength - borderPadding; + break; + case 1: + // Q = 1 := Text-Alignment: Center + startX = (width - lastLength) / 2; + break; + case 0: + default: + startX = borderPadding; + break; + } + text += startX + ' ' + lastY + ' Td\n'; + text += '(' + line + ') Tj\n'; + // reset X in PDF + text += -startX + ' 0 Td\n'; + + // After a Line, adjust y position + lastY = -(fontSize + lineSpacing); + lastLength = 0; + firstWordInLine = lastWordInLine + 1; + lineCount++; + + lastLine = ""; + continue Line; + } + break; + } + + returnValue.text = text; + returnValue.fontSize = fontSize; + + return returnValue; +}; + +AcroForm.internal.calculateAppearanceStream = function (formObject) { + if (formObject.appearanceStreamContent) { + // If appearanceStream is already set, use it + return formObject.appearanceStreamContent; + } + + if (!formObject.V && !formObject.DV) { + return; + } + + // else calculate it + + var stream = ''; + + var text = formObject.V || formObject.DV; + + var calcRes = AcroForm.internal.calculateX(formObject, text); + + stream += '/Tx BMC\n' + 'q\n' + + //color + '\n' + + '/F1 ' + calcRes.fontSize + ' Tf\n' + + // Text Matrix + '1 0 0 1 0 0 Tm\n'; + // Begin Text + stream += 'BT\n'; + stream += calcRes.text; + // End Text + stream += 'ET\n'; + stream += 'Q\n' + 'EMC\n'; + + var appearanceStreamContent = new AcroForm.createFormXObject(formObject); + + appearanceStreamContent.stream = stream; + + return appearanceStreamContent; +}; + +/* + * Converts the Parameters from x,y,w,h to lowerLeftX, lowerLeftY, upperRightX, upperRightY + * @param x + * @param y + * @param w + * @param h + * @returns {*[]} + */ +AcroForm.internal.calculateCoordinates = function (x, y, w, h) { + var coordinates = {}; + + if (this.internal) { + var mmtopx = function mmtopx(x) { + return x * this.internal.scaleFactor; + }; + + if (Array.isArray(x)) { + x[0] = AcroForm.scale(x[0]); + x[1] = AcroForm.scale(x[1]); + x[2] = AcroForm.scale(x[2]); + x[3] = AcroForm.scale(x[3]); + + coordinates.lowerLeft_X = x[0] || 0; + coordinates.lowerLeft_Y = mmtopx.call(this, this.internal.pageSize.height) - x[3] - x[1] || 0; + coordinates.upperRight_X = x[0] + x[2] || 0; + coordinates.upperRight_Y = mmtopx.call(this, this.internal.pageSize.height) - x[1] || 0; + } else { + x = AcroForm.scale(x); + y = AcroForm.scale(y); + w = AcroForm.scale(w); + h = AcroForm.scale(h); + coordinates.lowerLeft_X = x || 0; + coordinates.lowerLeft_Y = this.internal.pageSize.height - y || 0; + coordinates.upperRight_X = x + w || 0; + coordinates.upperRight_Y = this.internal.pageSize.height - y + h || 0; + } + } else { + // old method, that is fallback, if we can't get the pageheight, the coordinate-system starts from lower left + if (Array.isArray(x)) { + coordinates.lowerLeft_X = x[0] || 0; + coordinates.lowerLeft_Y = x[1] || 0; + coordinates.upperRight_X = x[0] + x[2] || 0; + coordinates.upperRight_Y = x[1] + x[3] || 0; + } else { + coordinates.lowerLeft_X = x || 0; + coordinates.lowerLeft_Y = y || 0; + coordinates.upperRight_X = x + w || 0; + coordinates.upperRight_Y = y + h || 0; + } + } + + return [coordinates.lowerLeft_X, coordinates.lowerLeft_Y, coordinates.upperRight_X, coordinates.upperRight_Y]; +}; + +AcroForm.internal.calculateColor = function (r, g, b) { + var color = new Array(3); + color.r = r | 0; + color.g = g | 0; + color.b = b | 0; + return color; +}; + +AcroForm.internal.getBitPosition = function (variable, position) { + variable = variable || 0; + var bitMask = 1; + bitMask = bitMask << position - 1; + return variable | bitMask; +}; + +AcroForm.internal.setBitPosition = function (variable, position, value) { + variable = variable || 0; + value = value || 1; + + var bitMask = 1; + bitMask = bitMask << position - 1; + + if (value == 1) { + // Set the Bit to 1 + var variable = variable | bitMask; + } else { + // Set the Bit to 0 + var variable = variable & ~bitMask; + } + + return variable; +}; + +/** + * jsPDF addHTML PlugIn + * Copyright (c) 2014 Diego Casorran + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + +(function (jsPDFAPI) { + 'use strict'; + + /** + * Renders an HTML element to canvas object which added to the PDF + * + * This feature requires [html2canvas](https://github.com/niklasvh/html2canvas) + * or [rasterizeHTML](https://github.com/cburgmer/rasterizeHTML.js) + * + * @returns {jsPDF} + * @name addHTML + * @param element {Mixed} HTML Element, or anything supported by html2canvas. + * @param x {Number} starting X coordinate in jsPDF instance's declared units. + * @param y {Number} starting Y coordinate in jsPDF instance's declared units. + * @param options {Object} Additional options, check the code below. + * @param callback {Function} to call when the rendering has finished. + * NOTE: Every parameter is optional except 'element' and 'callback', in such + * case the image is positioned at 0x0 covering the whole PDF document + * size. Ie, to easily take screenshots of webpages saving them to PDF. + * @deprecated This is being replace with a vector-supporting API. See + * [this link](https://cdn.rawgit.com/MrRio/jsPDF/master/examples/html2pdf/showcase_supported_html.html) + */ + + jsPDFAPI.addHTML = function (element, x, y, options, callback) { + 'use strict'; + + if (typeof html2canvas === 'undefined' && typeof rasterizeHTML === 'undefined') throw new Error('You need either ' + 'https://github.com/niklasvh/html2canvas' + ' or https://github.com/cburgmer/rasterizeHTML.js'); + + if (typeof x !== 'number') { + options = x; + callback = y; + } + + if (typeof options === 'function') { + callback = options; + options = null; + } + + var I = this.internal, + K = I.scaleFactor, + W = I.pageSize.width, + H = I.pageSize.height; + + options = options || {}; + options.onrendered = function (obj) { + x = parseInt(x) || 0; + y = parseInt(y) || 0; + var dim = options.dim || {}; + var h = dim.h || 0; + var w = dim.w || Math.min(W, obj.width / K) - x; + + var format = 'JPEG'; + if (options.format) format = options.format; + + if (obj.height > H && options.pagesplit) { + var crop = function () { + var cy = 0; + while (1) { + var canvas = document.createElement('canvas'); + canvas.width = Math.min(W * K, obj.width); + canvas.height = Math.min(H * K, obj.height - cy); + var ctx = canvas.getContext('2d'); + ctx.drawImage(obj, 0, cy, obj.width, canvas.height, 0, 0, canvas.width, canvas.height); + var args = [canvas, x, cy ? 0 : y, canvas.width / K, canvas.height / K, format, null, 'SLOW']; + this.addImage.apply(this, args); + cy += canvas.height; + if (cy >= obj.height) break; + this.addPage(); + } + callback(w, cy, null, args); + }.bind(this); + if (obj.nodeName === 'CANVAS') { + var img = new Image(); + img.onload = crop; + img.src = obj.toDataURL("image/png"); + obj = img; + } else { + crop(); + } + } else { + var alias = Math.random().toString(35); + var args = [obj, x, y, w, h, format, alias, 'SLOW']; + + this.addImage.apply(this, args); + + callback(w, h, alias, args); + } + }.bind(this); + + if (typeof html2canvas !== 'undefined' && !options.rstz) { + return html2canvas(element, options); + } + + if (typeof rasterizeHTML !== 'undefined') { + var meth = 'drawDocument'; + if (typeof element === 'string') { + meth = /^http/.test(element) ? 'drawURL' : 'drawHTML'; + } + options.width = options.width || W * K; + return rasterizeHTML[meth](element, void 0, options).then(function (r) { + options.onrendered(r.image); + }, function (e) { + callback(null, e); + }); + } + + return null; + }; +})(jsPDF.API); + +/** @preserve + * jsPDF addImage plugin + * Copyright (c) 2012 Jason Siefken, https://github.com/siefkenj/ + * 2013 Chris Dowling, https://github.com/gingerchris + * 2013 Trinh Ho, https://github.com/ineedfat + * 2013 Edwin Alejandro Perez, https://github.com/eaparango + * 2013 Norah Smith, https://github.com/burnburnrocket + * 2014 Diego Casorran, https://github.com/diegocr + * 2014 James Robb, https://github.com/jamesbrobb + * + * + */ + +(function (jsPDFAPI) { + 'use strict'; + + var namespace = 'addImage_', + supported_image_types = ['jpeg', 'jpg', 'png']; + + // Image functionality ported from pdf.js + var putImage = function putImage(img) { + + var objectNumber = this.internal.newObject(), + out = this.internal.write, + putStream = this.internal.putStream; + + img['n'] = objectNumber; + + out('<>'); + } + if ('trns' in img && img['trns'].constructor == Array) { + var trns = '', + i = 0, + len = img['trns'].length; + for (; i < len; i++) { + trns += img['trns'][i] + ' ' + img['trns'][i] + ' '; + }out('/Mask [' + trns + ']'); + } + if ('smask' in img) { + out('/SMask ' + (objectNumber + 1) + ' 0 R'); + } + out('/Length ' + img['data'].length + '>>'); + + putStream(img['data']); + + out('endobj'); + + // Soft mask + if ('smask' in img) { + var dp = '/Predictor ' + img['p'] + ' /Colors 1 /BitsPerComponent ' + img['bpc'] + ' /Columns ' + img['w']; + var smask = { 'w': img['w'], 'h': img['h'], 'cs': 'DeviceGray', 'bpc': img['bpc'], 'dp': dp, 'data': img['smask'] }; + if ('f' in img) smask.f = img['f']; + putImage.call(this, smask); + } + + //Palette + if (img['cs'] === this.color_spaces.INDEXED) { + + this.internal.newObject(); + //out('<< /Filter / ' + img['f'] +' /Length ' + img['pal'].length + '>>'); + //putStream(zlib.compress(img['pal'])); + out('<< /Length ' + img['pal'].length + '>>'); + putStream(this.arrayBufferToBinaryString(new Uint8Array(img['pal']))); + out('endobj'); + } + }, + putResourcesCallback = function putResourcesCallback() { + var images = this.internal.collections[namespace + 'images']; + for (var i in images) { + putImage.call(this, images[i]); + } + }, + putXObjectsDictCallback = function putXObjectsDictCallback() { + var images = this.internal.collections[namespace + 'images'], + out = this.internal.write, + image; + for (var i in images) { + image = images[i]; + out('/I' + image['i'], image['n'], '0', 'R'); + } + }, + checkCompressValue = function checkCompressValue(value) { + if (value && typeof value === 'string') value = value.toUpperCase(); + return value in jsPDFAPI.image_compression ? value : jsPDFAPI.image_compression.NONE; + }, + getImages = function getImages() { + var images = this.internal.collections[namespace + 'images']; + //first run, so initialise stuff + if (!images) { + this.internal.collections[namespace + 'images'] = images = {}; + this.internal.events.subscribe('putResources', putResourcesCallback); + this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback); + } + + return images; + }, + getImageIndex = function getImageIndex(images) { + var imageIndex = 0; + + if (images) { + // this is NOT the first time this method is ran on this instance of jsPDF object. + imageIndex = Object.keys ? Object.keys(images).length : function (o) { + var i = 0; + for (var e in o) { + if (o.hasOwnProperty(e)) { + i++; + } + } + return i; + }(images); + } + + return imageIndex; + }, + notDefined = function notDefined(value) { + return typeof value === 'undefined' || value === null; + }, + generateAliasFromData = function generateAliasFromData(data) { + return typeof data === 'string' && jsPDFAPI.sHashCode(data); + }, + doesNotSupportImageType = function doesNotSupportImageType(type) { + return supported_image_types.indexOf(type) === -1; + }, + processMethodNotEnabled = function processMethodNotEnabled(type) { + return typeof jsPDFAPI['process' + type.toUpperCase()] !== 'function'; + }, + isDOMElement = function isDOMElement(object) { + return (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && object.nodeType === 1; + }, + createDataURIFromElement = function createDataURIFromElement(element, format, angle) { + + //if element is an image which uses data url definition, just return the dataurl + if (element.nodeName === 'IMG' && element.hasAttribute('src')) { + var src = '' + element.getAttribute('src'); + if (!angle && src.indexOf('data:image/') === 0) return src; + + // only if the user doesn't care about a format + if (!format && /\.png(?:[?#].*)?$/i.test(src)) format = 'png'; + } + + if (element.nodeName === 'CANVAS') { + var canvas = element; + } else { + var canvas = document.createElement('canvas'); + canvas.width = element.clientWidth || element.width; + canvas.height = element.clientHeight || element.height; + + var ctx = canvas.getContext('2d'); + if (!ctx) { + throw 'addImage requires canvas to be supported by browser.'; + } + if (angle) { + var x, + y, + b, + c, + s, + w, + h, + to_radians = Math.PI / 180, + angleInRadians; + + if ((typeof angle === 'undefined' ? 'undefined' : _typeof(angle)) === 'object') { + x = angle.x; + y = angle.y; + b = angle.bg; + angle = angle.angle; + } + angleInRadians = angle * to_radians; + c = Math.abs(Math.cos(angleInRadians)); + s = Math.abs(Math.sin(angleInRadians)); + w = canvas.width; + h = canvas.height; + canvas.width = h * s + w * c; + canvas.height = h * c + w * s; + + if (isNaN(x)) x = canvas.width / 2; + if (isNaN(y)) y = canvas.height / 2; + + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = b || 'white'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.save(); + ctx.translate(x, y); + ctx.rotate(angleInRadians); + ctx.drawImage(element, -(w / 2), -(h / 2)); + ctx.rotate(-angleInRadians); + ctx.translate(-x, -y); + ctx.restore(); + } else { + ctx.drawImage(element, 0, 0, canvas.width, canvas.height); + } + } + return canvas.toDataURL(('' + format).toLowerCase() == 'png' ? 'image/png' : 'image/jpeg'); + }, + checkImagesForAlias = function checkImagesForAlias(alias, images) { + var cached_info; + if (images) { + for (var e in images) { + if (alias === images[e].alias) { + cached_info = images[e]; + break; + } + } + } + return cached_info; + }, + determineWidthAndHeight = function determineWidthAndHeight(w, h, info) { + if (!w && !h) { + w = -96; + h = -96; + } + if (w < 0) { + w = -1 * info['w'] * 72 / w / this.internal.scaleFactor; + } + if (h < 0) { + h = -1 * info['h'] * 72 / h / this.internal.scaleFactor; + } + if (w === 0) { + w = h * info['w'] / info['h']; + } + if (h === 0) { + h = w * info['h'] / info['w']; + } + + return [w, h]; + }, + writeImageToPDF = function writeImageToPDF(x, y, w, h, info, index, images) { + var dims = determineWidthAndHeight.call(this, w, h, info), + coord = this.internal.getCoordinateString, + vcoord = this.internal.getVerticalCoordinateString; + + w = dims[0]; + h = dims[1]; + + images[index] = info; + + this.internal.write('q', coord(w), '0 0', coord(h) // TODO: check if this should be shifted by vcoord + , coord(x), vcoord(y + h), 'cm /I' + info['i'], 'Do Q'); + }; + + /** + * COLOR SPACES + */ + jsPDFAPI.color_spaces = { + DEVICE_RGB: 'DeviceRGB', + DEVICE_GRAY: 'DeviceGray', + DEVICE_CMYK: 'DeviceCMYK', + CAL_GREY: 'CalGray', + CAL_RGB: 'CalRGB', + LAB: 'Lab', + ICC_BASED: 'ICCBased', + INDEXED: 'Indexed', + PATTERN: 'Pattern', + SEPARATION: 'Separation', + DEVICE_N: 'DeviceN' + }; + + /** + * DECODE METHODS + */ + jsPDFAPI.decode = { + DCT_DECODE: 'DCTDecode', + FLATE_DECODE: 'FlateDecode', + LZW_DECODE: 'LZWDecode', + JPX_DECODE: 'JPXDecode', + JBIG2_DECODE: 'JBIG2Decode', + ASCII85_DECODE: 'ASCII85Decode', + ASCII_HEX_DECODE: 'ASCIIHexDecode', + RUN_LENGTH_DECODE: 'RunLengthDecode', + CCITT_FAX_DECODE: 'CCITTFaxDecode' + }; + + /** + * IMAGE COMPRESSION TYPES + */ + jsPDFAPI.image_compression = { + NONE: 'NONE', + FAST: 'FAST', + MEDIUM: 'MEDIUM', + SLOW: 'SLOW' + }; + + jsPDFAPI.sHashCode = function (str) { + return Array.prototype.reduce && str.split("").reduce(function (a, b) { + a = (a << 5) - a + b.charCodeAt(0);return a & a; + }, 0); + }; + + jsPDFAPI.isString = function (object) { + return typeof object === 'string'; + }; + + /** + * Strips out and returns info from a valid base64 data URI + * @param {String[dataURI]} a valid data URI of format 'data:[][;base64],' + * @returns an Array containing the following + * [0] the complete data URI + * [1] + * [2] format - the second part of the mime-type i.e 'png' in 'image/png' + * [4] + */ + jsPDFAPI.extractInfoFromBase64DataURI = function (dataURI) { + return (/^data:([\w]+?\/([\w]+?));base64,(.+?)$/g.exec(dataURI) + ); + }; + + /** + * Check to see if ArrayBuffer is supported + */ + jsPDFAPI.supportsArrayBuffer = function () { + return typeof ArrayBuffer !== 'undefined' && typeof Uint8Array !== 'undefined'; + }; + + /** + * Tests supplied object to determine if ArrayBuffer + * @param {Object[object]} + */ + jsPDFAPI.isArrayBuffer = function (object) { + if (!this.supportsArrayBuffer()) return false; + return object instanceof ArrayBuffer; + }; + + /** + * Tests supplied object to determine if it implements the ArrayBufferView (TypedArray) interface + * @param {Object[object]} + */ + jsPDFAPI.isArrayBufferView = function (object) { + if (!this.supportsArrayBuffer()) return false; + if (typeof Uint32Array === 'undefined') return false; + return object instanceof Int8Array || object instanceof Uint8Array || typeof Uint8ClampedArray !== 'undefined' && object instanceof Uint8ClampedArray || object instanceof Int16Array || object instanceof Uint16Array || object instanceof Int32Array || object instanceof Uint32Array || object instanceof Float32Array || object instanceof Float64Array; + }; + + /** + * Exactly what it says on the tin + */ + jsPDFAPI.binaryStringToUint8Array = function (binary_string) { + /* + * not sure how efficient this will be will bigger files. Is there a native method? + */ + var len = binary_string.length; + var bytes = new Uint8Array(len); + for (var i = 0; i < len; i++) { + bytes[i] = binary_string.charCodeAt(i); + } + return bytes; + }; + + /** + * Convert the Buffer to a Binary String + */ + jsPDFAPI.arrayBufferToBinaryString = function (buffer) { + if (typeof window.atob === "function") { + return atob(this.arrayBufferToBase64(buffer)); + } else { + var data = this.isArrayBuffer(buffer) ? buffer : new Uint8Array(buffer); + var chunkSizeForSlice = 0x5000; + var binary_string = ''; + var slicesCount = Math.round(data.byteLength / chunkSizeForSlice); + for (var i = 0; i < slicesCount; i++) { + binary_string += String.fromCharCode.apply(null, data.slice(i * chunkSizeForSlice, i * chunkSizeForSlice + chunkSizeForSlice)); + } + return binary_string; + } + }; + + /** + * Converts an ArrayBuffer directly to base64 + * + * Taken from here + * + * http://jsperf.com/encoding-xhr-image-data/31 + * + * Need to test if this is a better solution for larger files + * + */ + jsPDFAPI.arrayBufferToBase64 = function (arrayBuffer) { + var base64 = ''; + var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + + var bytes = new Uint8Array(arrayBuffer); + var byteLength = bytes.byteLength; + var byteRemainder = byteLength % 3; + var mainLength = byteLength - byteRemainder; + + var a, b, c, d; + var chunk; + + // Main loop deals with bytes in chunks of 3 + for (var i = 0; i < mainLength; i = i + 3) { + // Combine the three bytes into a single integer + chunk = bytes[i] << 16 | bytes[i + 1] << 8 | bytes[i + 2]; + + // Use bitmasks to extract 6-bit segments from the triplet + a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 + b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12 + c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6 + d = chunk & 63; // 63 = 2^6 - 1 + + // Convert the raw binary segments to the appropriate ASCII encoding + base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; + } + + // Deal with the remaining bytes and padding + if (byteRemainder == 1) { + chunk = bytes[mainLength]; + + a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 + + // Set the 4 least significant bits to zero + b = (chunk & 3) << 4; // 3 = 2^2 - 1 + + base64 += encodings[a] + encodings[b] + '=='; + } else if (byteRemainder == 2) { + chunk = bytes[mainLength] << 8 | bytes[mainLength + 1]; + + a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 + b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4 + + // Set the 2 least significant bits to zero + c = (chunk & 15) << 2; // 15 = 2^4 - 1 + + base64 += encodings[a] + encodings[b] + encodings[c] + '='; + } + + return base64; + }; + + jsPDFAPI.createImageInfo = function (data, wd, ht, cs, bpc, f, imageIndex, alias, dp, trns, pal, smask, p) { + var info = { + alias: alias, + w: wd, + h: ht, + cs: cs, + bpc: bpc, + i: imageIndex, + data: data + // n: objectNumber will be added by putImage code + }; + + if (f) info.f = f; + if (dp) info.dp = dp; + if (trns) info.trns = trns; + if (pal) info.pal = pal; + if (smask) info.smask = smask; + if (p) info.p = p; // predictor parameter for PNG compression + + return info; + }; + + jsPDFAPI.addImage = function (imageData, format, x, y, w, h, alias, compression, rotation) { + 'use strict'; + + if (typeof format !== 'string') { + var tmp = h; + h = w; + w = y; + y = x; + x = format; + format = tmp; + } + + if ((typeof imageData === 'undefined' ? 'undefined' : _typeof(imageData)) === 'object' && !isDOMElement(imageData) && "imageData" in imageData) { + var options = imageData; + + imageData = options.imageData; + format = options.format || format; + x = options.x || x || 0; + y = options.y || y || 0; + w = options.w || w; + h = options.h || h; + alias = options.alias || alias; + compression = options.compression || compression; + rotation = options.rotation || options.angle || rotation; + } + + if (isNaN(x) || isNaN(y)) { + console.error('jsPDF.addImage: Invalid coordinates', arguments); + throw new Error('Invalid coordinates passed to jsPDF.addImage'); + } + + var images = getImages.call(this), + info; + + if (!(info = checkImagesForAlias(imageData, images))) { + var dataAsBinaryString; + + if (isDOMElement(imageData)) imageData = createDataURIFromElement(imageData, format, rotation); + + if (notDefined(alias)) alias = generateAliasFromData(imageData); + + if (!(info = checkImagesForAlias(alias, images))) { + + if (this.isString(imageData)) { + + var base64Info = this.extractInfoFromBase64DataURI(imageData); + + if (base64Info) { + + format = base64Info[2]; + imageData = atob(base64Info[3]); //convert to binary string + } else { + + if (imageData.charCodeAt(0) === 0x89 && imageData.charCodeAt(1) === 0x50 && imageData.charCodeAt(2) === 0x4e && imageData.charCodeAt(3) === 0x47) format = 'png'; + } + } + format = (format || 'JPEG').toLowerCase(); + + if (doesNotSupportImageType(format)) throw new Error('addImage currently only supports formats ' + supported_image_types + ', not \'' + format + '\''); + + if (processMethodNotEnabled(format)) throw new Error('please ensure that the plugin for \'' + format + '\' support is added'); + + /** + * need to test if it's more efficient to convert all binary strings + * to TypedArray - or should we just leave and process as string? + */ + if (this.supportsArrayBuffer()) { + // no need to convert if imageData is already uint8array + if (!(imageData instanceof Uint8Array)) { + dataAsBinaryString = imageData; + imageData = this.binaryStringToUint8Array(imageData); + } + } + + info = this['process' + format.toUpperCase()](imageData, getImageIndex(images), alias, checkCompressValue(compression), dataAsBinaryString); + + if (!info) throw new Error('An unkwown error occurred whilst processing the image'); + } + } + + writeImageToPDF.call(this, x, y, w, h, info, info.i, images); + + return this; + }; + + /** + * JPEG SUPPORT + **/ + + //takes a string imgData containing the raw bytes of + //a jpeg image and returns [width, height] + //Algorithm from: http://www.64lines.com/jpeg-width-height + var getJpegSize = function getJpegSize(imgData) { + 'use strict'; + + var width, height, numcomponents; + // Verify we have a valid jpeg header 0xff,0xd8,0xff,0xe0,?,?,'J','F','I','F',0x00 + if (!imgData.charCodeAt(0) === 0xff || !imgData.charCodeAt(1) === 0xd8 || !imgData.charCodeAt(2) === 0xff || !imgData.charCodeAt(3) === 0xe0 || !imgData.charCodeAt(6) === 'J'.charCodeAt(0) || !imgData.charCodeAt(7) === 'F'.charCodeAt(0) || !imgData.charCodeAt(8) === 'I'.charCodeAt(0) || !imgData.charCodeAt(9) === 'F'.charCodeAt(0) || !imgData.charCodeAt(10) === 0x00) { + throw new Error('getJpegSize requires a binary string jpeg file'); + } + var blockLength = imgData.charCodeAt(4) * 256 + imgData.charCodeAt(5); + var i = 4, + len = imgData.length; + while (i < len) { + i += blockLength; + if (imgData.charCodeAt(i) !== 0xff) { + throw new Error('getJpegSize could not find the size of the image'); + } + if (imgData.charCodeAt(i + 1) === 0xc0 || //(SOF) Huffman - Baseline DCT + imgData.charCodeAt(i + 1) === 0xc1 || //(SOF) Huffman - Extended sequential DCT + imgData.charCodeAt(i + 1) === 0xc2 || // Progressive DCT (SOF2) + imgData.charCodeAt(i + 1) === 0xc3 || // Spatial (sequential) lossless (SOF3) + imgData.charCodeAt(i + 1) === 0xc4 || // Differential sequential DCT (SOF5) + imgData.charCodeAt(i + 1) === 0xc5 || // Differential progressive DCT (SOF6) + imgData.charCodeAt(i + 1) === 0xc6 || // Differential spatial (SOF7) + imgData.charCodeAt(i + 1) === 0xc7) { + height = imgData.charCodeAt(i + 5) * 256 + imgData.charCodeAt(i + 6); + width = imgData.charCodeAt(i + 7) * 256 + imgData.charCodeAt(i + 8); + numcomponents = imgData.charCodeAt(i + 9); + return [width, height, numcomponents]; + } else { + i += 2; + blockLength = imgData.charCodeAt(i) * 256 + imgData.charCodeAt(i + 1); + } + } + }, + getJpegSizeFromBytes = function getJpegSizeFromBytes(data) { + + var hdr = data[0] << 8 | data[1]; + + if (hdr !== 0xFFD8) throw new Error('Supplied data is not a JPEG'); + + var len = data.length, + block = (data[4] << 8) + data[5], + pos = 4, + bytes, + width, + height, + numcomponents; + + while (pos < len) { + pos += block; + bytes = readBytes(data, pos); + block = (bytes[2] << 8) + bytes[3]; + if ((bytes[1] === 0xC0 || bytes[1] === 0xC2) && bytes[0] === 0xFF && block > 7) { + bytes = readBytes(data, pos + 5); + width = (bytes[2] << 8) + bytes[3]; + height = (bytes[0] << 8) + bytes[1]; + numcomponents = bytes[4]; + return { width: width, height: height, numcomponents: numcomponents }; + } + + pos += 2; + } + + throw new Error('getJpegSizeFromBytes could not find the size of the image'); + }, + readBytes = function readBytes(data, offset) { + return data.subarray(offset, offset + 5); + }; + + jsPDFAPI.processJPEG = function (data, index, alias, compression, dataAsBinaryString) { + 'use strict'; + + var colorSpace = this.color_spaces.DEVICE_RGB, + filter = this.decode.DCT_DECODE, + bpc = 8, + dims; + + if (this.isString(data)) { + dims = getJpegSize(data); + return this.createImageInfo(data, dims[0], dims[1], dims[3] == 1 ? this.color_spaces.DEVICE_GRAY : colorSpace, bpc, filter, index, alias); + } + + if (this.isArrayBuffer(data)) data = new Uint8Array(data); + + if (this.isArrayBufferView(data)) { + + dims = getJpegSizeFromBytes(data); + + // if we already have a stored binary string rep use that + data = dataAsBinaryString || this.arrayBufferToBinaryString(data); + + return this.createImageInfo(data, dims.width, dims.height, dims.numcomponents == 1 ? this.color_spaces.DEVICE_GRAY : colorSpace, bpc, filter, index, alias); + } + + return null; + }; + + jsPDFAPI.processJPG = function () /*data, index, alias, compression, dataAsBinaryString*/{ + return this.processJPEG.apply(this, arguments); + }; +})(jsPDF.API); + +/** + * jsPDF Annotations PlugIn + * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + +/** + * There are many types of annotations in a PDF document. Annotations are placed + * on a page at a particular location. They are not 'attached' to an object. + *
+ * This plugin current supports
+ *
  • Goto Page (set pageNumber and top in options) + *
  • Goto Name (set name and top in options) + *
  • Goto URL (set url in options) + *

    + * The destination magnification factor can also be specified when goto is a page number or a named destination. (see documentation below) + * (set magFactor in options). XYZ is the default. + *

    + *

    + * Links, Text, Popup, and FreeText are supported. + *

    + *

    + * Options In PDF spec Not Implemented Yet + *

  • link border + *
  • named target + *
  • page coordinates + *
  • destination page scaling and layout + *
  • actions other than URL and GotoPage + *
  • background / hover actions + *

    + */ + +/* + Destination Magnification Factors + See PDF 1.3 Page 386 for meanings and options + + [supported] + XYZ (options; left top zoom) + Fit (no options) + FitH (options: top) + FitV (options: left) + + [not supported] + FitR + FitB + FitBH + FitBV + */ + +(function (jsPDFAPI) { + 'use strict'; + + var annotationPlugin = { + + /** + * An array of arrays, indexed by pageNumber. + */ + annotations: [], + + f2: function f2(number) { + return number.toFixed(2); + }, + + notEmpty: function notEmpty(obj) { + if (typeof obj != 'undefined') { + if (obj != '') { + return true; + } + } + } + }; + + jsPDF.API.annotationPlugin = annotationPlugin; + + jsPDF.API.events.push(['addPage', function (info) { + this.annotationPlugin.annotations[info.pageNumber] = []; + }]); + + jsPDFAPI.events.push(['putPage', function (info) { + //TODO store annotations in pageContext so reorder/remove will not affect them. + var pageAnnos = this.annotationPlugin.annotations[info.pageNumber]; + + var found = false; + for (var a = 0; a < pageAnnos.length && !found; a++) { + var anno = pageAnnos[a]; + switch (anno.type) { + case 'link': + if (annotationPlugin.notEmpty(anno.options.url) || annotationPlugin.notEmpty(anno.options.pageNumber)) { + found = true; + break; + } + case 'reference': + case 'text': + case 'freetext': + found = true; + break; + } + } + if (found == false) { + return; + } + + this.internal.write("/Annots ["); + var f2 = this.annotationPlugin.f2; + var k = this.internal.scaleFactor; + var pageHeight = this.internal.pageSize.height; + var pageInfo = this.internal.getPageInfo(info.pageNumber); + for (var a = 0; a < pageAnnos.length; a++) { + var anno = pageAnnos[a]; + + switch (anno.type) { + case 'reference': + // References to Widget Anotations (for AcroForm Fields) + this.internal.write(' ' + anno.object.objId + ' 0 R '); + break; + case 'text': + // Create a an object for both the text and the popup + var objText = this.internal.newAdditionalObject(); + var objPopup = this.internal.newAdditionalObject(); + + var title = anno.title || 'Note'; + var rect = "/Rect [" + f2(anno.bounds.x * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + " " + f2((anno.bounds.x + anno.bounds.w) * k) + " " + f2((pageHeight - anno.bounds.y) * k) + "] "; + line = '<>'; + objText.content = line; + + var parent = objText.objId + ' 0 R'; + var popoff = 30; + var rect = "/Rect [" + f2((anno.bounds.x + popoff) * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + " " + f2((anno.bounds.x + anno.bounds.w + popoff) * k) + " " + f2((pageHeight - anno.bounds.y) * k) + "] "; + //var rect2 = "/Rect [" + f2(anno.bounds.x * k) + " " + f2((pageHeight - anno.bounds.y) * k) + " " + f2(anno.bounds.x + anno.bounds.w * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + "] "; + line = '<>'; + } else if (anno.options.pageNumber) { + // first page is 0 + var info = this.internal.getPageInfo(anno.options.pageNumber); + line = '< pageNumber or url [required] + *

    If pageNumber is specified, top and zoom may also be specified

    + */ + jsPDFAPI.link = function (x, y, w, h, options) { + 'use strict'; + + this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push({ + x: x, + y: y, + w: w, + h: h, + options: options, + type: 'link' + }); + }; + + /** + * Currently only supports single line text. + * Returns the width of the text/link + */ + jsPDFAPI.textWithLink = function (text, x, y, options) { + 'use strict'; + + var width = this.getTextWidth(text); + var height = this.internal.getLineHeight() / this.internal.scaleFactor; + this.text(text, x, y); + //TODO We really need the text baseline height to do this correctly. + // Or ability to draw text on top, bottom, center, or baseline. + y += height * .2; + this.link(x, y - height, width, height, options); + return width; + }; + + //TODO move into external library + jsPDFAPI.getTextWidth = function (text) { + 'use strict'; + + var fontSize = this.internal.getFontSize(); + var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor; + return txtWidth; + }; + + //TODO move into external library + jsPDFAPI.getLineHeight = function () { + return this.internal.getLineHeight(); + }; + + return this; +})(jsPDF.API); + +/** + * jsPDF Autoprint Plugin + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + +/** +* Makes the PDF automatically print. This works in Chrome, Firefox, Acrobat +* Reader. +* +* @returns {jsPDF} +* @name autoPrint +* @example +* var doc = new jsPDF() +* doc.text(10, 10, 'This is a test') +* doc.autoPrint() +* doc.save('autoprint.pdf') +*/ + +(function (jsPDFAPI) { + 'use strict'; + + jsPDFAPI.autoPrint = function () { + 'use strict'; + + var refAutoPrintTag; + + this.internal.events.subscribe('postPutResources', function () { + refAutoPrintTag = this.internal.newObject(); + this.internal.write("<< /S/Named /Type/Action /N/Print >>", "endobj"); + }); + + this.internal.events.subscribe("putCatalog", function () { + this.internal.write("/OpenAction " + refAutoPrintTag + " 0" + " R"); + }); + return this; + }; +})(jsPDF.API); + +/** + * jsPDF Canvas PlugIn + * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + +/** + * This plugin mimics the HTML5 Canvas + * + * The goal is to provide a way for current canvas users to print directly to a PDF. + */ + +(function (jsPDFAPI) { + 'use strict'; + + jsPDFAPI.events.push(['initialized', function () { + this.canvas.pdf = this; + }]); + + jsPDFAPI.canvas = { + getContext: function getContext(name) { + this.pdf.context2d._canvas = this; + return this.pdf.context2d; + }, + style: {} + }; + + Object.defineProperty(jsPDFAPI.canvas, 'width', { + get: function get() { + return this._width; + }, + set: function set(value) { + this._width = value; + this.getContext('2d').pageWrapX = value + 1; + } + }); + + Object.defineProperty(jsPDFAPI.canvas, 'height', { + get: function get() { + return this._height; + }, + set: function set(value) { + this._height = value; + this.getContext('2d').pageWrapY = value + 1; + } + }); + + return this; +})(jsPDF.API); + +/** ==================================================================== + * jsPDF Cell plugin + * Copyright (c) 2013 Youssef Beddad, youssef.beddad@gmail.com + * 2013 Eduardo Menezes de Morais, eduardo.morais@usp.br + * 2013 Lee Driscoll, https://github.com/lsdriscoll + * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria + * 2014 James Hall, james@parall.ax + * 2014 Diego Casorran, https://github.com/diegocr + * + * + * ==================================================================== + */ + +(function (jsPDFAPI) { + 'use strict'; + /*jslint browser:true */ + /*global document: false, jsPDF */ + + var fontName, + fontSize, + fontStyle, + padding = 3, + margin = 13, + headerFunction, + lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined }, + pages = 1, + setLastCellPosition = function setLastCellPosition(x, y, w, h, ln) { + lastCellPos = { 'x': x, 'y': y, 'w': w, 'h': h, 'ln': ln }; + }, + getLastCellPosition = function getLastCellPosition() { + return lastCellPos; + }, + NO_MARGINS = { left: 0, top: 0, bottom: 0 }; + + jsPDFAPI.setHeaderFunction = function (func) { + headerFunction = func; + }; + + jsPDFAPI.getTextDimensions = function (txt) { + fontName = this.internal.getFont().fontName; + fontSize = this.table_font_size || this.internal.getFontSize(); + fontStyle = this.internal.getFont().fontStyle; + // 1 pixel = 0.264583 mm and 1 mm = 72/25.4 point + var px2pt = 0.264583 * 72 / 25.4, + dimensions, + text; + + text = document.createElement('font'); + text.id = "jsPDFCell"; + + try { + text.style.fontStyle = fontStyle; + } catch (e) { + text.style.fontWeight = fontStyle; + } + + text.style.fontName = fontName; + text.style.fontSize = fontSize + 'pt'; + try { + text.textContent = txt; + } catch (e) { + text.innerText = txt; + } + + document.body.appendChild(text); + + dimensions = { w: (text.offsetWidth + 1) * px2pt, h: (text.offsetHeight + 1) * px2pt }; + + document.body.removeChild(text); + + return dimensions; + }; + + jsPDFAPI.cellAddPage = function () { + var margins = this.margins || NO_MARGINS; + + this.addPage(); + + setLastCellPosition(margins.left, margins.top, undefined, undefined); + //setLastCellPosition(undefined, undefined, undefined, undefined, undefined); + pages += 1; + }; + + jsPDFAPI.cellInitialize = function () { + lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined }; + pages = 1; + }; + + jsPDFAPI.cell = function (x, y, w, h, txt, ln, align) { + var curCell = getLastCellPosition(); + var pgAdded = false; + + // If this is not the first cell, we must change its position + if (curCell.ln !== undefined) { + if (curCell.ln === ln) { + //Same line + x = curCell.x + curCell.w; + y = curCell.y; + } else { + //New line + var margins = this.margins || NO_MARGINS; + if (curCell.y + curCell.h + h + margin >= this.internal.pageSize.height - margins.bottom) { + this.cellAddPage(); + pgAdded = true; + if (this.printHeaders && this.tableHeaderRow) { + this.printHeaderRow(ln, true); + } + } + //We ignore the passed y: the lines may have different heights + y = getLastCellPosition().y + getLastCellPosition().h; + if (pgAdded) y = margin + 10; + } + } + + if (txt[0] !== undefined) { + if (this.printingHeaderRow) { + this.rect(x, y, w, h, 'FD'); + } else { + this.rect(x, y, w, h); + } + if (align === 'right') { + if (!(txt instanceof Array)) { + txt = [txt]; + } + for (var i = 0; i < txt.length; i++) { + var currentLine = txt[i]; + var textSize = this.getStringUnitWidth(currentLine) * this.internal.getFontSize(); + this.text(currentLine, x + w - textSize - padding, y + this.internal.getLineHeight() * (i + 1)); + } + } else { + this.text(txt, x + padding, y + this.internal.getLineHeight()); + } + } + setLastCellPosition(x, y, w, h, ln); + return this; + }; + + /** + * Return the maximum value from an array + * @param array + * @param comparisonFn + * @returns {*} + */ + jsPDFAPI.arrayMax = function (array, comparisonFn) { + var max = array[0], + i, + ln, + item; + + for (i = 0, ln = array.length; i < ln; i += 1) { + item = array[i]; + + if (comparisonFn) { + if (comparisonFn(max, item) === -1) { + max = item; + } + } else { + if (item > max) { + max = item; + } + } + } + + return max; + }; + + /** + * Create a table from a set of data. + * @param {Integer} [x] : left-position for top-left corner of table + * @param {Integer} [y] top-position for top-left corner of table + * @param {Object[]} [data] As array of objects containing key-value pairs corresponding to a row of data. + * @param {String[]} [headers] Omit or null to auto-generate headers at a performance cost + * @param {Object} [config.printHeaders] True to print column headers at the top of every page + * @param {Object} [config.autoSize] True to dynamically set the column widths to match the widest cell value + * @param {Object} [config.margins] margin values for left, top, bottom, and width + * @param {Object} [config.fontSize] Integer fontSize to use (optional) + */ + + jsPDFAPI.table = function (x, y, data, headers, config) { + if (!data) { + throw 'No data for PDF table'; + } + + var headerNames = [], + headerPrompts = [], + header, + i, + ln, + cln, + columnMatrix = {}, + columnWidths = {}, + columnData, + column, + columnMinWidths = [], + j, + tableHeaderConfigs = [], + model, + jln, + func, + + + //set up defaults. If a value is provided in config, defaults will be overwritten: + autoSize = false, + printHeaders = true, + fontSize = 12, + margins = NO_MARGINS; + + margins.width = this.internal.pageSize.width; + + if (config) { + //override config defaults if the user has specified non-default behavior: + if (config.autoSize === true) { + autoSize = true; + } + if (config.printHeaders === false) { + printHeaders = false; + } + if (config.fontSize) { + fontSize = config.fontSize; + } + if (config.css && typeof config.css['font-size'] !== "undefined") { + fontSize = config.css['font-size'] * 16; + } + if (config.margins) { + margins = config.margins; + } + } + + /** + * @property {Number} lnMod + * Keep track of the current line number modifier used when creating cells + */ + this.lnMod = 0; + lastCellPos = { x: undefined, y: undefined, w: undefined, h: undefined, ln: undefined }, pages = 1; + + this.printHeaders = printHeaders; + this.margins = margins; + this.setFontSize(fontSize); + this.table_font_size = fontSize; + + // Set header values + if (headers === undefined || headers === null) { + // No headers defined so we derive from data + headerNames = Object.keys(data[0]); + } else if (headers[0] && typeof headers[0] !== 'string') { + var px2pt = 0.264583 * 72 / 25.4; + + // Split header configs into names and prompts + for (i = 0, ln = headers.length; i < ln; i += 1) { + header = headers[i]; + headerNames.push(header.name); + headerPrompts.push(header.prompt); + columnWidths[header.name] = header.width * px2pt; + } + } else { + headerNames = headers; + } + + if (autoSize) { + // Create a matrix of columns e.g., {column_title: [row1_Record, row2_Record]} + func = function func(rec) { + return rec[header]; + }; + + for (i = 0, ln = headerNames.length; i < ln; i += 1) { + header = headerNames[i]; + + columnMatrix[header] = data.map(func); + + // get header width + columnMinWidths.push(this.getTextDimensions(headerPrompts[i] || header).w); + column = columnMatrix[header]; + + // get cell widths + for (j = 0, cln = column.length; j < cln; j += 1) { + columnData = column[j]; + columnMinWidths.push(this.getTextDimensions(columnData).w); + } + + // get final column width + columnWidths[header] = jsPDFAPI.arrayMax(columnMinWidths); + + //have to reset + columnMinWidths = []; + } + } + + // -- Construct the table + + if (printHeaders) { + var lineHeight = this.calculateLineHeight(headerNames, columnWidths, headerPrompts.length ? headerPrompts : headerNames); + + // Construct the header row + for (i = 0, ln = headerNames.length; i < ln; i += 1) { + header = headerNames[i]; + tableHeaderConfigs.push([x, y, columnWidths[header], lineHeight, String(headerPrompts.length ? headerPrompts[i] : header)]); + } + + // Store the table header config + this.setTableHeaderRow(tableHeaderConfigs); + + // Print the header for the start of the table + this.printHeaderRow(1, false); + } + + // Construct the data rows + for (i = 0, ln = data.length; i < ln; i += 1) { + var lineHeight; + model = data[i]; + lineHeight = this.calculateLineHeight(headerNames, columnWidths, model); + + for (j = 0, jln = headerNames.length; j < jln; j += 1) { + header = headerNames[j]; + this.cell(x, y, columnWidths[header], lineHeight, model[header], i + 2, header.align); + } + } + this.lastCellPos = lastCellPos; + this.table_x = x; + this.table_y = y; + return this; + }; + /** + * Calculate the height for containing the highest column + * @param {String[]} headerNames is the header, used as keys to the data + * @param {Integer[]} columnWidths is size of each column + * @param {Object[]} model is the line of data we want to calculate the height of + */ + jsPDFAPI.calculateLineHeight = function (headerNames, columnWidths, model) { + var header, + lineHeight = 0; + for (var j = 0; j < headerNames.length; j++) { + header = headerNames[j]; + model[header] = this.splitTextToSize(String(model[header]), columnWidths[header] - padding); + var h = this.internal.getLineHeight() * model[header].length + padding; + if (h > lineHeight) lineHeight = h; + } + return lineHeight; + }; + + /** + * Store the config for outputting a table header + * @param {Object[]} config + * An array of cell configs that would define a header row: Each config matches the config used by jsPDFAPI.cell + * except the ln parameter is excluded + */ + jsPDFAPI.setTableHeaderRow = function (config) { + this.tableHeaderRow = config; + }; + + /** + * Output the store header row + * @param lineNumber The line number to output the header at + */ + jsPDFAPI.printHeaderRow = function (lineNumber, new_page) { + if (!this.tableHeaderRow) { + throw 'Property tableHeaderRow does not exist.'; + } + + var tableHeaderCell, tmpArray, i, ln; + + this.printingHeaderRow = true; + if (headerFunction !== undefined) { + var position = headerFunction(this, pages); + setLastCellPosition(position[0], position[1], position[2], position[3], -1); + } + this.setFontStyle('bold'); + var tempHeaderConf = []; + for (i = 0, ln = this.tableHeaderRow.length; i < ln; i += 1) { + this.setFillColor(200, 200, 200); + + tableHeaderCell = this.tableHeaderRow[i]; + if (new_page) { + this.margins.top = margin; + tableHeaderCell[1] = this.margins && this.margins.top || 0; + tempHeaderConf.push(tableHeaderCell); + } + tmpArray = [].concat(tableHeaderCell); + this.cell.apply(this, tmpArray.concat(lineNumber)); + } + if (tempHeaderConf.length > 0) { + this.setTableHeaderRow(tempHeaderConf); + } + this.setFontStyle('normal'); + this.printingHeaderRow = false; + }; +})(jsPDF.API); + +/** + * jsPDF Context2D PlugIn Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv + * + * Licensed under the MIT License. http://opensource.org/licenses/mit-license + */ + +/** + * This plugin mimics the HTML5 Canvas's context2d. + * + * The goal is to provide a way for current canvas implementations to print directly to a PDF. + */ + +/** + * TODO implement stroke opacity (refactor from fill() method ) + * TODO transform angle and radii parameters + */ + +/** + * require('jspdf.js'); require('lib/css_colors.js'); + */ + +(function (jsPDFAPI) { + 'use strict'; + + jsPDFAPI.events.push(['initialized', function () { + this.context2d.pdf = this; + this.context2d.internal.pdf = this; + this.context2d.ctx = new context(); + this.context2d.ctxStack = []; + this.context2d.path = []; + }]); + + jsPDFAPI.context2d = { + pageWrapXEnabled: false, + pageWrapYEnabled: false, + pageWrapX: 9999999, + pageWrapY: 9999999, + ctx: new context(), + f2: function f2(number) { + return number.toFixed(2); + }, + + fillRect: function fillRect(x, y, w, h) { + if (this._isFillTransparent()) { + return; + } + x = this._wrapX(x); + y = this._wrapY(y); + + var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); + this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "f"); + }, + + strokeRect: function strokeRect(x, y, w, h) { + if (this._isStrokeTransparent()) { + return; + } + x = this._wrapX(x); + y = this._wrapY(y); + + var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); + this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "s"); + }, + + /** + * We cannot clear PDF commands that were already written to PDF, so we use white instead.
    + * As a special case, read a special flag (ignoreClearRect) and do nothing if it is set. + * This results in all calls to clearRect() to do nothing, and keep the canvas transparent. + * This flag is stored in the save/restore context and is managed the same way as other drawing states. + * @param x + * @param y + * @param w + * @param h + */ + clearRect: function clearRect(x, y, w, h) { + if (this.ctx.ignoreClearRect) { + return; + } + + x = this._wrapX(x); + y = this._wrapY(y); + + var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); + this.save(); + this.setFillStyle('#ffffff'); + //TODO This is hack to fill with white. + this.pdf.rect(xRect.x, xRect.y, xRect.w, xRect.h, "f"); + this.restore(); + }, + + save: function save() { + this.ctx._fontSize = this.pdf.internal.getFontSize(); + var ctx = new context(); + ctx.copy(this.ctx); + this.ctxStack.push(this.ctx); + this.ctx = ctx; + }, + + restore: function restore() { + this.ctx = this.ctxStack.pop(); + this.setFillStyle(this.ctx.fillStyle); + this.setStrokeStyle(this.ctx.strokeStyle); + this.setFont(this.ctx.font); + this.pdf.setFontSize(this.ctx._fontSize); + this.setLineCap(this.ctx.lineCap); + this.setLineWidth(this.ctx.lineWidth); + this.setLineJoin(this.ctx.lineJoin); + }, + + rect: function rect(x, y, w, h) { + this.moveTo(x, y); + this.lineTo(x + w, y); + this.lineTo(x + w, y + h); + this.lineTo(x, y + h); + this.lineTo(x, y); //TODO not needed + this.closePath(); + }, + + beginPath: function beginPath() { + this.path = []; + }, + + closePath: function closePath() { + this.path.push({ + type: 'close' + }); + }, + + _getRGBA: function _getRGBA(style) { + // get the decimal values of r, g, and b; + var r, g, b, a; + if (!style) { + return { r: 0, g: 0, b: 0, a: 0, style: style }; + } + + if (this.internal.rxTransparent.test(style)) { + r = 0; + g = 0; + b = 0; + a = 0; + } else { + var m = this.internal.rxRgb.exec(style); + if (m != null) { + r = parseInt(m[1]); + g = parseInt(m[2]); + b = parseInt(m[3]); + a = 1; + } else { + m = this.internal.rxRgba.exec(style); + if (m != null) { + r = parseInt(m[1]); + g = parseInt(m[2]); + b = parseInt(m[3]); + a = parseFloat(m[4]); + } else { + a = 1; + if (style.charAt(0) != '#') { + style = CssColors.colorNameToHex(style); + if (!style) { + style = '#000000'; + } + } else {} + + if (style.length === 4) { + r = style.substring(1, 2); + r += r; + g = style.substring(2, 3); + g += g; + b = style.substring(3, 4); + b += b; + } else { + r = style.substring(1, 3); + g = style.substring(3, 5); + b = style.substring(5, 7); + } + r = parseInt(r, 16); + g = parseInt(g, 16); + b = parseInt(b, 16); + } + } + } + return { r: r, g: g, b: b, a: a, style: style }; + }, + + setFillStyle: function setFillStyle(style) { + var rgba = this._getRGBA(style); + + this.ctx.fillStyle = style; + this.ctx._isFillTransparent = rgba.a === 0; + this.ctx._fillOpacity = rgba.a; + + this.pdf.setFillColor(rgba.r, rgba.g, rgba.b, { + a: rgba.a + }); + this.pdf.setTextColor(rgba.r, rgba.g, rgba.b, { + a: rgba.a + }); + }, + + setStrokeStyle: function setStrokeStyle(style) { + var rgba = this._getRGBA(style); + + this.ctx.strokeStyle = rgba.style; + this.ctx._isStrokeTransparent = rgba.a === 0; + this.ctx._strokeOpacity = rgba.a; + + //TODO jsPDF to handle rgba + if (rgba.a === 0) { + this.pdf.setDrawColor(255, 255, 255); + } else if (rgba.a === 1) { + this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b); + } else { + //this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b, {a: rgba.a}); + this.pdf.setDrawColor(rgba.r, rgba.g, rgba.b); + } + }, + + fillText: function fillText(text, x, y, maxWidth) { + if (this._isFillTransparent()) { + return; + } + x = this._wrapX(x); + y = this._wrapY(y); + + var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); + x = xpt[0]; + y = xpt[1]; + var rads = this._matrix_rotation(this.ctx._transform); + var degs = rads * 57.2958; + + //TODO only push the clip if it has not been applied to the current PDF context + if (this.ctx._clip_path.length > 0) { + var lines; + if (window.outIntercept) { + lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; + } else { + lines = this.internal.getCurrentPage(); + } + lines.push("q"); + var origPath = this.path; + this.path = this.ctx._clip_path; + this.ctx._clip_path = []; + this._fill(null, true); + this.ctx._clip_path = this.path; + this.path = origPath; + } + + // We only use X axis as scale hint + var scale = 1; + try { + scale = this._matrix_decompose(this._getTransform()).scale[0]; + } catch (e) { + console.warn(e); + } + + // In some cases the transform was very small (5.715760606202283e-17). Most likely a canvg rounding error. + if (scale < .01) { + this.pdf.text(text, x, this._getBaseline(y), null, degs); + } else { + var oldSize = this.pdf.internal.getFontSize(); + this.pdf.setFontSize(oldSize * scale); + this.pdf.text(text, x, this._getBaseline(y), null, degs); + this.pdf.setFontSize(oldSize); + } + + if (this.ctx._clip_path.length > 0) { + lines.push('Q'); + } + }, + + strokeText: function strokeText(text, x, y, maxWidth) { + if (this._isStrokeTransparent()) { + return; + } + x = this._wrapX(x); + y = this._wrapY(y); + + var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); + x = xpt[0]; + y = xpt[1]; + var rads = this._matrix_rotation(this.ctx._transform); + var degs = rads * 57.2958; + + //TODO only push the clip if it has not been applied to the current PDF context + if (this.ctx._clip_path.length > 0) { + var lines; + if (window.outIntercept) { + lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; + } else { + lines = this.internal.getCurrentPage(); + } + lines.push("q"); + var origPath = this.path; + this.path = this.ctx._clip_path; + this.ctx._clip_path = []; + this._fill(null, true); + this.ctx._clip_path = this.path; + this.path = origPath; + } + + var scale = 1; + // We only use the X axis as scale hint + try { + scale = this._matrix_decompose(this._getTransform()).scale[0]; + } catch (e) { + console.warn(e); + } + + if (scale === 1) { + this.pdf.text(text, x, this._getBaseline(y), { + stroke: true + }, degs); + } else { + var oldSize = this.pdf.internal.getFontSize(); + this.pdf.setFontSize(oldSize * scale); + this.pdf.text(text, x, this._getBaseline(y), { + stroke: true + }, degs); + this.pdf.setFontSize(oldSize); + } + + if (this.ctx._clip_path.length > 0) { + lines.push('Q'); + } + }, + + setFont: function setFont(font) { + this.ctx.font = font; + + //var rx = /\s*(\w+)\s+(\w+)\s+(\w+)\s+([\d\.]+)(px|pt|em)\s+["']?(\w+)['"]?/; + var rx = /\s*(\w+)\s+(\w+)\s+(\w+)\s+([\d\.]+)(px|pt|em)\s+(.*)?/; + m = rx.exec(font); + if (m != null) { + var fontStyle = m[1]; + var fontWeight = m[3]; + var fontSize = m[4]; + var fontSizeUnit = m[5]; + var fontFamily = m[6]; + + if ('px' === fontSizeUnit) { + fontSize = Math.floor(parseFloat(fontSize)); + // fontSize = fontSize * 1.25; + } else if ('em' === fontSizeUnit) { + fontSize = Math.floor(parseFloat(fontSize) * this.pdf.getFontSize()); + } else { + fontSize = Math.floor(parseFloat(fontSize)); + } + + this.pdf.setFontSize(fontSize); + + if (fontWeight === 'bold' || fontWeight === '700') { + this.pdf.setFontStyle('bold'); + } else { + if (fontStyle === 'italic') { + this.pdf.setFontStyle('italic'); + } else { + this.pdf.setFontStyle('normal'); + } + } + + var name = fontFamily; + var parts = name.toLowerCase().split(/\s*,\s*/); + var jsPdfFontName; + + if (parts.indexOf('arial') != -1) { + jsPdfFontName = 'Arial'; + } else if (parts.indexOf('verdana') != -1) { + jsPdfFontName = 'Verdana'; + } else if (parts.indexOf('helvetica') != -1) { + jsPdfFontName = 'Helvetica'; + } else if (parts.indexOf('sans-serif') != -1) { + jsPdfFontName = 'sans-serif'; + } else if (parts.indexOf('fixed') != -1) { + jsPdfFontName = 'Fixed'; + } else if (parts.indexOf('monospace') != -1) { + jsPdfFontName = 'Monospace'; + } else if (parts.indexOf('terminal') != -1) { + jsPdfFontName = 'Terminal'; + } else if (parts.indexOf('courier') != -1) { + jsPdfFontName = 'Courier'; + } else if (parts.indexOf('times') != -1) { + jsPdfFontName = 'Times'; + } else if (parts.indexOf('cursive') != -1) { + jsPdfFontName = 'Cursive'; + } else if (parts.indexOf('fantasy') != -1) { + jsPdfFontName = 'Fantasy'; + } else if (parts.indexOf('serif') != -1) { + jsPdfFontName = 'Serif'; + } else { + jsPdfFontName = 'Serif'; + } + + //TODO check more cases + var style; + if ('bold' === fontWeight) { + style = 'bold'; + } else { + style = 'normal'; + } + + this.pdf.setFont(jsPdfFontName, style); + } else { + var rx = /\s*(\d+)(pt|px|em)\s+([\w "]+)\s*([\w "]+)?/; + var m = rx.exec(font); + if (m != null) { + var size = m[1]; + var name = m[3]; + var style = m[4]; + if (!style) { + style = 'normal'; + } + if ('em' === fontSizeUnit) { + size = Math.floor(parseFloat(fontSize) * this.pdf.getFontSize()); + } else { + size = Math.floor(parseFloat(size)); + } + this.pdf.setFontSize(size); + this.pdf.setFont(name, style); + } + } + }, + + setTextBaseline: function setTextBaseline(baseline) { + this.ctx.textBaseline = baseline; + }, + + getTextBaseline: function getTextBaseline() { + return this.ctx.textBaseline; + }, + + //TODO implement textAlign + setTextAlign: function setTextAlign(align) { + this.ctx.textAlign = align; + }, + + getTextAlign: function getTextAlign() { + return this.ctx.textAlign; + }, + + setLineWidth: function setLineWidth(width) { + this.ctx.lineWidth = width; + this.pdf.setLineWidth(width); + }, + + setLineCap: function setLineCap(style) { + this.ctx.lineCap = style; + this.pdf.setLineCap(style); + }, + + setLineJoin: function setLineJoin(style) { + this.ctx.lineJoin = style; + this.pdf.setLineJoin(style); + }, + + moveTo: function moveTo(x, y) { + x = this._wrapX(x); + y = this._wrapY(y); + + var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); + x = xpt[0]; + y = xpt[1]; + + var obj = { + type: 'mt', + x: x, + y: y + }; + this.path.push(obj); + }, + + _wrapX: function _wrapX(x) { + if (this.pageWrapXEnabled) { + return x % this.pageWrapX; + } else { + return x; + } + }, + + _wrapY: function _wrapY(y) { + if (this.pageWrapYEnabled) { + this._gotoPage(this._page(y)); + return (y - this.lastBreak) % this.pageWrapY; + } else { + return y; + } + }, + + transform: function transform(a, b, c, d, e, f) { + //TODO apply to current transformation instead of replacing + this.ctx._transform = [a, b, c, d, e, f]; + }, + + setTransform: function setTransform(a, b, c, d, e, f) { + this.ctx._transform = [a, b, c, d, e, f]; + }, + + _getTransform: function _getTransform() { + return this.ctx._transform; + }, + + lastBreak: 0, + // Y Position of page breaks. + pageBreaks: [], + // returns: One-based Page Number + // Should only be used if pageWrapYEnabled is true + _page: function _page(y) { + if (this.pageWrapYEnabled) { + this.lastBreak = 0; + var manualBreaks = 0; + var autoBreaks = 0; + for (var i = 0; i < this.pageBreaks.length; i++) { + if (y >= this.pageBreaks[i]) { + manualBreaks++; + if (this.lastBreak === 0) { + autoBreaks++; + } + var spaceBetweenLastBreak = this.pageBreaks[i] - this.lastBreak; + this.lastBreak = this.pageBreaks[i]; + var pagesSinceLastBreak = Math.floor(spaceBetweenLastBreak / this.pageWrapY); + autoBreaks += pagesSinceLastBreak; + } + } + if (this.lastBreak === 0) { + var pagesSinceLastBreak = Math.floor(y / this.pageWrapY) + 1; + autoBreaks += pagesSinceLastBreak; + } + return autoBreaks + manualBreaks; + } else { + return this.pdf.internal.getCurrentPageInfo().pageNumber; + } + }, + + _gotoPage: function _gotoPage(pageOneBased) { + // This is a stub to be overriden if needed + }, + + lineTo: function lineTo(x, y) { + x = this._wrapX(x); + y = this._wrapY(y); + + var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); + x = xpt[0]; + y = xpt[1]; + + var obj = { + type: 'lt', + x: x, + y: y + }; + this.path.push(obj); + }, + + bezierCurveTo: function bezierCurveTo(x1, y1, x2, y2, x, y) { + x1 = this._wrapX(x1); + y1 = this._wrapY(y1); + x2 = this._wrapX(x2); + y2 = this._wrapY(y2); + x = this._wrapX(x); + y = this._wrapY(y); + + var xpt; + xpt = this._matrix_map_point(this.ctx._transform, [x, y]); + x = xpt[0]; + y = xpt[1]; + xpt = this._matrix_map_point(this.ctx._transform, [x1, y1]); + x1 = xpt[0]; + y1 = xpt[1]; + xpt = this._matrix_map_point(this.ctx._transform, [x2, y2]); + x2 = xpt[0]; + y2 = xpt[1]; + + var obj = { + type: 'bct', + x1: x1, + y1: y1, + x2: x2, + y2: y2, + x: x, + y: y + }; + this.path.push(obj); + }, + + quadraticCurveTo: function quadraticCurveTo(x1, y1, x, y) { + x1 = this._wrapX(x1); + y1 = this._wrapY(y1); + x = this._wrapX(x); + y = this._wrapY(y); + + var xpt; + xpt = this._matrix_map_point(this.ctx._transform, [x, y]); + x = xpt[0]; + y = xpt[1]; + xpt = this._matrix_map_point(this.ctx._transform, [x1, y1]); + x1 = xpt[0]; + y1 = xpt[1]; + + var obj = { + type: 'qct', + x1: x1, + y1: y1, + x: x, + y: y + }; + this.path.push(obj); + }, + + arc: function arc(x, y, radius, startAngle, endAngle, anticlockwise) { + x = this._wrapX(x); + y = this._wrapY(y); + + if (!this._matrix_is_identity(this.ctx._transform)) { + var xpt = this._matrix_map_point(this.ctx._transform, [x, y]); + x = xpt[0]; + y = xpt[1]; + + var x_radPt0 = this._matrix_map_point(this.ctx._transform, [0, 0]); + var x_radPt = this._matrix_map_point(this.ctx._transform, [0, radius]); + radius = Math.sqrt(Math.pow(x_radPt[0] - x_radPt0[0], 2) + Math.pow(x_radPt[1] - x_radPt0[1], 2)); + + //TODO angles need to be transformed + } + + var obj = { + type: 'arc', + x: x, + y: y, + radius: radius, + startAngle: startAngle, + endAngle: endAngle, + anticlockwise: anticlockwise + }; + this.path.push(obj); + }, + + drawImage: function drawImage(img, x, y, w, h, x2, y2, w2, h2) { + if (x2 !== undefined) { + x = x2; + y = y2; + w = w2; + h = h2; + } + x = this._wrapX(x); + y = this._wrapY(y); + + var xRect = this._matrix_map_rect(this.ctx._transform, { x: x, y: y, w: w, h: h }); + var xRect2 = this._matrix_map_rect(this.ctx._transform, { x: x2, y: y2, w: w2, h: h2 }); + + // TODO implement source clipping and image scaling + var format; + var rx = /data:image\/(\w+).*/i; + var m = rx.exec(img); + if (m != null) { + format = m[1]; + } else { + // format = "jpeg"; + format = "png"; + } + + this.pdf.addImage(img, format, xRect.x, xRect.y, xRect.w, xRect.h); + }, + + /** + * Multiply the first matrix by the second + * @param m1 + * @param m2 + * @returns {*[]} + * @private + */ + _matrix_multiply: function _matrix_multiply(m2, m1) { + var sx = m1[0]; + var shy = m1[1]; + var shx = m1[2]; + var sy = m1[3]; + var tx = m1[4]; + var ty = m1[5]; + + var t0 = sx * m2[0] + shy * m2[2]; + var t2 = shx * m2[0] + sy * m2[2]; + var t4 = tx * m2[0] + ty * m2[2] + m2[4]; + shy = sx * m2[1] + shy * m2[3]; + sy = shx * m2[1] + sy * m2[3]; + ty = tx * m2[1] + ty * m2[3] + m2[5]; + sx = t0; + shx = t2; + tx = t4; + + return [sx, shy, shx, sy, tx, ty]; + }, + + _matrix_rotation: function _matrix_rotation(m) { + return Math.atan2(m[2], m[0]); + }, + + _matrix_decompose: function _matrix_decompose(matrix) { + + var a = matrix[0]; + var b = matrix[1]; + var c = matrix[2]; + var d = matrix[3]; + + var scaleX = Math.sqrt(a * a + b * b); + a /= scaleX; + b /= scaleX; + + var shear = a * c + b * d; + c -= a * shear; + d -= b * shear; + + var scaleY = Math.sqrt(c * c + d * d); + c /= scaleY; + d /= scaleY; + shear /= scaleY; + + if (a * d < b * c) { + a = -a; + b = -b; + shear = -shear; + scaleX = -scaleX; + } + + return { + scale: [scaleX, 0, 0, scaleY, 0, 0], + translate: [1, 0, 0, 1, matrix[4], matrix[5]], + rotate: [a, b, -b, a, 0, 0], + skew: [1, 0, shear, 1, 0, 0] + }; + }, + + _matrix_map_point: function _matrix_map_point(m1, pt) { + var sx = m1[0]; + var shy = m1[1]; + var shx = m1[2]; + var sy = m1[3]; + var tx = m1[4]; + var ty = m1[5]; + + var px = pt[0]; + var py = pt[1]; + + var x = px * sx + py * shx + tx; + var y = px * shy + py * sy + ty; + return [x, y]; + }, + + _matrix_map_point_obj: function _matrix_map_point_obj(m1, pt) { + var xpt = this._matrix_map_point(m1, [pt.x, pt.y]); + return { x: xpt[0], y: xpt[1] }; + }, + + _matrix_map_rect: function _matrix_map_rect(m1, rect) { + var p1 = this._matrix_map_point(m1, [rect.x, rect.y]); + var p2 = this._matrix_map_point(m1, [rect.x + rect.w, rect.y + rect.h]); + return { x: p1[0], y: p1[1], w: p2[0] - p1[0], h: p2[1] - p1[1] }; + }, + + _matrix_is_identity: function _matrix_is_identity(m1) { + if (m1[0] != 1) { + return false; + } + if (m1[1] != 0) { + return false; + } + if (m1[2] != 0) { + return false; + } + if (m1[3] != 1) { + return false; + } + if (m1[4] != 0) { + return false; + } + if (m1[5] != 0) { + return false; + } + return true; + }, + + rotate: function rotate(angle) { + var matrix = [Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0.0, 0.0]; + this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix); + }, + + scale: function scale(sx, sy) { + var matrix = [sx, 0.0, 0.0, sy, 0.0, 0.0]; + this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix); + }, + + translate: function translate(x, y) { + var matrix = [1.0, 0.0, 0.0, 1.0, x, y]; + this.ctx._transform = this._matrix_multiply(this.ctx._transform, matrix); + }, + + stroke: function stroke() { + if (this.ctx._clip_path.length > 0) { + + var lines; + if (window.outIntercept) { + lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; + } else { + lines = this.internal.getCurrentPage(); + } + lines.push("q"); + + var origPath = this.path; + this.path = this.ctx._clip_path; + this.ctx._clip_path = []; + this._stroke(true); + + this.ctx._clip_path = this.path; + this.path = origPath; + this._stroke(false); + + lines.push("Q"); + } else { + this._stroke(false); + } + }, + + _stroke: function _stroke(isClip) { + if (!isClip && this._isStrokeTransparent()) { + return; + } + + //TODO opacity + + var moves = []; + var xPath = this.path; + + for (var i = 0; i < xPath.length; i++) { + var pt = xPath[i]; + switch (pt.type) { + case 'mt': + moves.push({ start: pt, deltas: [], abs: [] }); + break; + case 'lt': + var delta = [pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y]; + moves[moves.length - 1].deltas.push(delta); + moves[moves.length - 1].abs.push(pt); + break; + case 'bct': + var delta = [pt.x1 - xPath[i - 1].x, pt.y1 - xPath[i - 1].y, pt.x2 - xPath[i - 1].x, pt.y2 - xPath[i - 1].y, pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y]; + moves[moves.length - 1].deltas.push(delta); + break; + case 'qct': + // convert to bezier + var x1 = xPath[i - 1].x + 2.0 / 3.0 * (pt.x1 - xPath[i - 1].x); + var y1 = xPath[i - 1].y + 2.0 / 3.0 * (pt.y1 - xPath[i - 1].y); + var x2 = pt.x + 2.0 / 3.0 * (pt.x1 - pt.x); + var y2 = pt.y + 2.0 / 3.0 * (pt.y1 - pt.y); + var x3 = pt.x; + var y3 = pt.y; + var delta = [x1 - xPath[i - 1].x, y1 - xPath[i - 1].y, x2 - xPath[i - 1].x, y2 - xPath[i - 1].y, x3 - xPath[i - 1].x, y3 - xPath[i - 1].y]; + moves[moves.length - 1].deltas.push(delta); + break; + case 'arc': + //TODO this was hack to avoid out-of-bounds issue + // No move-to before drawing the arc + if (moves.length == 0) { + moves.push({ start: { x: 0, y: 0 }, deltas: [], abs: [] }); + } + moves[moves.length - 1].arc = true; + moves[moves.length - 1].abs.push(pt); + break; + case 'close': + + break; + } + } + + for (var i = 0; i < moves.length; i++) { + var style; + if (i == moves.length - 1) { + style = 's'; + } else { + style = null; + } + if (moves[i].arc) { + var arcs = moves[i].abs; + for (var ii = 0; ii < arcs.length; ii++) { + var arc = arcs[ii]; + var start = arc.startAngle * 360 / (2 * Math.PI); + var end = arc.endAngle * 360 / (2 * Math.PI); + var x = arc.x; + var y = arc.y; + this.internal.arc2(this, x, y, arc.radius, start, end, arc.anticlockwise, style, isClip); + } + } else { + var x = moves[i].start.x; + var y = moves[i].start.y; + if (!isClip) { + this.pdf.lines(moves[i].deltas, x, y, null, style); + } else { + this.pdf.lines(moves[i].deltas, x, y, null, null); + this.pdf.clip_fixed(); + } + } + } + }, + + _isFillTransparent: function _isFillTransparent() { + return this.ctx._isFillTransparent || this.globalAlpha == 0; + }, + + _isStrokeTransparent: function _isStrokeTransparent() { + return this.ctx._isStrokeTransparent || this.globalAlpha == 0; + }, + + fill: function fill(fillRule) { + //evenodd or nonzero (default) + if (this.ctx._clip_path.length > 0) { + + var lines; + if (window.outIntercept) { + lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; + } else { + lines = this.internal.getCurrentPage(); + } + lines.push("q"); + + var origPath = this.path; + this.path = this.ctx._clip_path; + this.ctx._clip_path = []; + this._fill(fillRule, true); + + this.ctx._clip_path = this.path; + this.path = origPath; + this._fill(fillRule, false); + + lines.push('Q'); + } else { + this._fill(fillRule, false); + } + }, + + _fill: function _fill(fillRule, isClip) { + if (this._isFillTransparent()) { + return; + } + var v2Support = typeof this.pdf.internal.newObject2 === 'function'; + + var lines; + if (window.outIntercept) { + lines = window.outIntercept.type === 'group' ? window.outIntercept.stream : window.outIntercept; + } else { + lines = this.internal.getCurrentPage(); + } + + // if (this.ctx._clip_path.length > 0) { + // lines.push('q'); + // var oldPath = this.path; + // this.path = this.ctx._clip_path; + // this.ctx._clip_path = []; + // this._fill(fillRule, true); + // this.ctx._clip_path = this.path; + // this.path = oldPath; + // } + + var moves = []; + var outInterceptOld = window.outIntercept; + + if (v2Support) { + // Blend and Mask + switch (this.ctx.globalCompositeOperation) { + case 'normal': + case 'source-over': + break; + case 'destination-in': + case 'destination-out': + //TODO this need to be added to the current group or page + // define a mask stream + var obj = this.pdf.internal.newStreamObject(); + + // define a mask state + var obj2 = this.pdf.internal.newObject2(); + obj2.push('<>'); // /S /Luminosity will need to define color space + obj2.push('>>'); + + // add mask to page resources + var gsName = 'MASK' + obj2.objId; + this.pdf.internal.addGraphicsState(gsName, obj2.objId); + + var instruction = '/' + gsName + ' gs'; + // add mask to page, group, or stream + lines.splice(0, 0, 'q'); + lines.splice(1, 0, instruction); + lines.push('Q'); + + window.outIntercept = obj; + break; + default: + var dictionaryEntry = '/' + this.pdf.internal.blendModeMap[this.ctx.globalCompositeOperation.toUpperCase()]; + if (dictionaryEntry) { + this.pdf.internal.out(dictionaryEntry + ' gs'); + } + break; + } + } + + var alpha = this.ctx.globalAlpha; + if (this.ctx._fillOpacity < 1) { + // TODO combine this with global opacity + alpha = this.ctx._fillOpacity; + } + + //TODO check for an opacity graphics state that was already created + //TODO do not set opacity if current value is already active + if (v2Support) { + var objOpac = this.pdf.internal.newObject2(); + objOpac.push('<>'); + var gsName = 'GS_O_' + objOpac.objId; + this.pdf.internal.addGraphicsState(gsName, objOpac.objId); + this.pdf.internal.out('/' + gsName + ' gs'); + } + + var xPath = this.path; + + for (var i = 0; i < xPath.length; i++) { + var pt = xPath[i]; + switch (pt.type) { + case 'mt': + moves.push({ start: pt, deltas: [], abs: [] }); + break; + case 'lt': + var delta = [pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y]; + moves[moves.length - 1].deltas.push(delta); + moves[moves.length - 1].abs.push(pt); + break; + case 'bct': + var delta = [pt.x1 - xPath[i - 1].x, pt.y1 - xPath[i - 1].y, pt.x2 - xPath[i - 1].x, pt.y2 - xPath[i - 1].y, pt.x - xPath[i - 1].x, pt.y - xPath[i - 1].y]; + moves[moves.length - 1].deltas.push(delta); + break; + case 'qct': + // convert to bezier + var x1 = xPath[i - 1].x + 2.0 / 3.0 * (pt.x1 - xPath[i - 1].x); + var y1 = xPath[i - 1].y + 2.0 / 3.0 * (pt.y1 - xPath[i - 1].y); + var x2 = pt.x + 2.0 / 3.0 * (pt.x1 - pt.x); + var y2 = pt.y + 2.0 / 3.0 * (pt.y1 - pt.y); + var x3 = pt.x; + var y3 = pt.y; + var delta = [x1 - xPath[i - 1].x, y1 - xPath[i - 1].y, x2 - xPath[i - 1].x, y2 - xPath[i - 1].y, x3 - xPath[i - 1].x, y3 - xPath[i - 1].y]; + moves[moves.length - 1].deltas.push(delta); + break; + case 'arc': + //TODO this was hack to avoid out-of-bounds issue when drawing circle + // No move-to before drawing the arc + if (moves.length === 0) { + moves.push({ deltas: [], abs: [] }); + } + moves[moves.length - 1].arc = true; + moves[moves.length - 1].abs.push(pt); + break; + case 'close': + moves.push({ close: true }); + break; + } + } + + for (var i = 0; i < moves.length; i++) { + var style; + if (i == moves.length - 1) { + style = 'f'; + if (fillRule === 'evenodd') { + style += '*'; + } + } else { + style = null; + } + + if (moves[i].close) { + this.pdf.internal.out('h'); + this.pdf.internal.out('f'); + } else if (moves[i].arc) { + if (moves[i].start) { + this.internal.move2(this, moves[i].start.x, moves[i].start.y); + } + var arcs = moves[i].abs; + for (var ii = 0; ii < arcs.length; ii++) { + var arc = arcs[ii]; + //TODO lines deltas were getting in here + if (typeof arc.startAngle !== 'undefined') { + var start = arc.startAngle * 360 / (2 * Math.PI); + var end = arc.endAngle * 360 / (2 * Math.PI); + var x = arc.x; + var y = arc.y; + if (ii === 0) { + this.internal.move2(this, x, y); + } + this.internal.arc2(this, x, y, arc.radius, start, end, arc.anticlockwise, null, isClip); + if (ii === arcs.length - 1) { + // The original arc move did not occur because of the algorithm + if (moves[i].start) { + var x = moves[i].start.x; + var y = moves[i].start.y; + this.internal.line2(c2d, x, y); + } + } + } else { + this.internal.line2(c2d, arc.x, arc.y); + } + } + } else { + var x = moves[i].start.x; + var y = moves[i].start.y; + if (!isClip) { + this.pdf.lines(moves[i].deltas, x, y, null, style); + } else { + this.pdf.lines(moves[i].deltas, x, y, null, null); + this.pdf.clip_fixed(); + } + } + } + + window.outIntercept = outInterceptOld; + + // if (this.ctx._clip_path.length > 0) { + // lines.push('Q'); + // } + }, + + pushMask: function pushMask() { + var v2Support = typeof this.pdf.internal.newObject2 === 'function'; + + if (!v2Support) { + console.log('jsPDF v2 not enabled'); + return; + } + + // define a mask stream + var obj = this.pdf.internal.newStreamObject(); + + // define a mask state + var obj2 = this.pdf.internal.newObject2(); + obj2.push('<>'); // /S /Luminosity will need to define color space + obj2.push('>>'); + + // add mask to page resources + var gsName = 'MASK' + obj2.objId; + this.pdf.internal.addGraphicsState(gsName, obj2.objId); + + var instruction = '/' + gsName + ' gs'; + this.pdf.internal.out(instruction); + }, + + clip: function clip() { + //TODO do we reset the path, or just copy it? + if (this.ctx._clip_path.length > 0) { + for (var i = 0; i < this.path.length; i++) { + this.ctx._clip_path.push(this.path[i]); + } + } else { + this.ctx._clip_path = this.path; + } + this.path = []; + }, + + measureText: function measureText(text) { + var pdf = this.pdf; + return { + getWidth: function getWidth() { + var fontSize = pdf.internal.getFontSize(); + var txtWidth = pdf.getStringUnitWidth(text) * fontSize / pdf.internal.scaleFactor; + // Convert points to pixels + txtWidth *= 1.3333; + return txtWidth; + }, + + get width() { + return this.getWidth(text); + } + }; + }, + _getBaseline: function _getBaseline(y) { + var height = parseInt(this.pdf.internal.getFontSize()); + // TODO Get descent from font descriptor + var descent = height * .25; + switch (this.ctx.textBaseline) { + case 'bottom': + return y - descent; + case 'top': + return y + height; + case 'hanging': + return y + height - descent; + case 'middle': + return y + height / 2 - descent; + case 'ideographic': + // TODO not implemented + return y; + case 'alphabetic': + default: + return y; + } + } + }; + + var c2d = jsPDFAPI.context2d; + + // accessor methods + Object.defineProperty(c2d, 'fillStyle', { + set: function set(value) { + this.setFillStyle(value); + }, + get: function get() { + return this.ctx.fillStyle; + } + }); + Object.defineProperty(c2d, 'strokeStyle', { + set: function set(value) { + this.setStrokeStyle(value); + }, + get: function get() { + return this.ctx.strokeStyle; + } + }); + Object.defineProperty(c2d, 'lineWidth', { + set: function set(value) { + this.setLineWidth(value); + }, + get: function get() { + return this.ctx.lineWidth; + } + }); + Object.defineProperty(c2d, 'lineCap', { + set: function set(val) { + this.setLineCap(val); + }, + get: function get() { + return this.ctx.lineCap; + } + }); + Object.defineProperty(c2d, 'lineJoin', { + set: function set(val) { + this.setLineJoin(val); + }, + get: function get() { + return this.ctx.lineJoin; + } + }); + Object.defineProperty(c2d, 'miterLimit', { + set: function set(val) { + this.ctx.miterLimit = val; + }, + get: function get() { + return this.ctx.miterLimit; + } + }); + Object.defineProperty(c2d, 'textBaseline', { + set: function set(value) { + this.setTextBaseline(value); + }, + get: function get() { + return this.getTextBaseline(); + } + }); + Object.defineProperty(c2d, 'textAlign', { + set: function set(value) { + this.setTextAlign(value); + }, + get: function get() { + return this.getTextAlign(); + } + }); + Object.defineProperty(c2d, 'font', { + set: function set(value) { + this.setFont(value); + }, + get: function get() { + return this.ctx.font; + } + }); + Object.defineProperty(c2d, 'globalCompositeOperation', { + set: function set(value) { + this.ctx.globalCompositeOperation = value; + }, + get: function get() { + return this.ctx.globalCompositeOperation; + } + }); + Object.defineProperty(c2d, 'globalAlpha', { + set: function set(value) { + this.ctx.globalAlpha = value; + }, + get: function get() { + return this.ctx.globalAlpha; + } + }); + // Not HTML API + Object.defineProperty(c2d, 'ignoreClearRect', { + set: function set(value) { + this.ctx.ignoreClearRect = value; + }, + get: function get() { + return this.ctx.ignoreClearRect; + } + }); + // End Not HTML API + + c2d.internal = {}; + + c2d.internal.rxRgb = /rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/; + c2d.internal.rxRgba = /rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/; + c2d.internal.rxTransparent = /transparent|rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*0+\s*\)/; + + // http://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html + c2d.internal.arc = function (c2d, xc, yc, r, a1, a2, anticlockwise, style) { + var includeMove = true; + + var k = this.pdf.internal.scaleFactor; + var pageHeight = this.pdf.internal.pageSize.height; + var f2 = this.pdf.internal.f2; + + var a1r = a1 * (Math.PI / 180); + var a2r = a2 * (Math.PI / 180); + var curves = this.createArc(r, a1r, a2r, anticlockwise); + for (var i = 0; i < curves.length; i++) { + var curve = curves[i]; + if (includeMove && i === 0) { + this.pdf.internal.out([f2((curve.x1 + xc) * k), f2((pageHeight - (curve.y1 + yc)) * k), 'm', f2((curve.x2 + xc) * k), f2((pageHeight - (curve.y2 + yc)) * k), f2((curve.x3 + xc) * k), f2((pageHeight - (curve.y3 + yc)) * k), f2((curve.x4 + xc) * k), f2((pageHeight - (curve.y4 + yc)) * k), 'c'].join(' ')); + } else { + this.pdf.internal.out([f2((curve.x2 + xc) * k), f2((pageHeight - (curve.y2 + yc)) * k), f2((curve.x3 + xc) * k), f2((pageHeight - (curve.y3 + yc)) * k), f2((curve.x4 + xc) * k), f2((pageHeight - (curve.y4 + yc)) * k), 'c'].join(' ')); + } + + //c2d._lastPoint = {x: curve.x1 + xc, y: curve.y1 + yc}; + c2d._lastPoint = { x: xc, y: yc }; + // f2((curve.x1 + xc) * k), f2((pageHeight - (curve.y1 + yc)) * k), 'm', f2((curve.x2 + xc) * k), f2((pageHeight - (curve.y2 + yc)) * k), f2((curve.x3 + xc) * k), f2((pageHeight - (curve.y3 + yc)) * k), f2((curve.x4 + xc) * k), f2((pageHeight - (curve.y4 + yc)) * k), 'c' + } + + if (style !== null) { + this.pdf.internal.out(this.pdf.internal.getStyle(style)); + } + }; + + /** + * + * @param x Edge point X + * @param y Edge point Y + * @param r Radius + * @param a1 start angle + * @param a2 end angle + * @param anticlockwise + * @param style + * @param isClip + */ + c2d.internal.arc2 = function (c2d, x, y, r, a1, a2, anticlockwise, style, isClip) { + // we need to convert from cartesian to polar here methinks. + var centerX = x; // + r; + var centerY = y; + + if (!isClip) { + this.arc(c2d, centerX, centerY, r, a1, a2, anticlockwise, style); + } else { + this.arc(c2d, centerX, centerY, r, a1, a2, anticlockwise, null); + this.pdf.clip_fixed(); + } + }; + + c2d.internal.move2 = function (c2d, x, y) { + var k = this.pdf.internal.scaleFactor; + var pageHeight = this.pdf.internal.pageSize.height; + var f2 = this.pdf.internal.f2; + + this.pdf.internal.out([f2(x * k), f2((pageHeight - y) * k), 'm'].join(' ')); + c2d._lastPoint = { x: x, y: y }; + }; + + c2d.internal.line2 = function (c2d, dx, dy) { + var k = this.pdf.internal.scaleFactor; + var pageHeight = this.pdf.internal.pageSize.height; + var f2 = this.pdf.internal.f2; + + //var pt = {x: c2d._lastPoint.x + dx, y: c2d._lastPoint.y + dy}; + var pt = { x: dx, y: dy }; + + this.pdf.internal.out([f2(pt.x * k), f2((pageHeight - pt.y) * k), 'l'].join(' ')); + //this.pdf.internal.out('f'); + c2d._lastPoint = pt; + }; + + /** + * Return a array of objects that represent bezier curves which approximate the circular arc centered at the origin, from startAngle to endAngle (radians) with the specified radius. + * + * Each bezier curve is an object with four points, where x1,y1 and x4,y4 are the arc's end points and x2,y2 and x3,y3 are the cubic bezier's control points. + */ + + c2d.internal.createArc = function (radius, startAngle, endAngle, anticlockwise) { + var EPSILON = 0.00001; // Roughly 1/1000th of a degree, see below + var twoPI = Math.PI * 2; + var piOverTwo = Math.PI / 2.0; + + // normalize startAngle, endAngle to [0, 2PI] + var startAngleN = startAngle; + if (startAngleN < twoPI || startAngleN > twoPI) { + startAngleN = startAngleN % twoPI; + } + if (startAngleN < 0) { + startAngleN = twoPI + startAngleN; + } + + while (startAngle > endAngle) { + startAngle = startAngle - twoPI; + } + var totalAngle = Math.abs(endAngle - startAngle); + if (totalAngle < twoPI) { + if (anticlockwise) { + totalAngle = twoPI - totalAngle; + } + } + + // Compute the sequence of arc curves, up to PI/2 at a time. + var curves = []; + var sgn = anticlockwise ? -1 : +1; + + var a1 = startAngleN; + for (; totalAngle > EPSILON;) { + var remain = sgn * Math.min(totalAngle, piOverTwo); + var a2 = a1 + remain; + curves.push(this.createSmallArc(radius, a1, a2)); + totalAngle -= Math.abs(a2 - a1); + a1 = a2; + } + + return curves; + }; + + c2d.internal.getCurrentPage = function () { + return this.pdf.internal.pages[this.pdf.internal.getCurrentPageInfo().pageNumber]; + }; + + /** + * Cubic bezier approximation of a circular arc centered at the origin, from (radians) a1 to a2, where a2-a1 < pi/2. The arc's radius is r. + * + * Returns an object with four points, where x1,y1 and x4,y4 are the arc's end points and x2,y2 and x3,y3 are the cubic bezier's control points. + * + * This algorithm is based on the approach described in: A. Riškus, "Approximation of a Cubic Bezier Curve by Circular Arcs and Vice Versa," Information Technology and Control, 35(4), 2006 pp. 371-378. + */ + + c2d.internal.createSmallArc = function (r, a1, a2) { + // Compute all four points for an arc that subtends the same total angle + // but is centered on the X-axis + + var a = (a2 - a1) / 2.0; + + var x4 = r * Math.cos(a); + var y4 = r * Math.sin(a); + var x1 = x4; + var y1 = -y4; + + var q1 = x1 * x1 + y1 * y1; + var q2 = q1 + x1 * x4 + y1 * y4; + var k2 = 4 / 3 * (Math.sqrt(2 * q1 * q2) - q2) / (x1 * y4 - y1 * x4); + + var x2 = x1 - k2 * y1; + var y2 = y1 + k2 * x1; + var x3 = x2; + var y3 = -y2; + + // Find the arc points' actual locations by computing x1,y1 and x4,y4 + // and rotating the control points by a + a1 + + var ar = a + a1; + var cos_ar = Math.cos(ar); + var sin_ar = Math.sin(ar); + + return { + x1: r * Math.cos(a1), + y1: r * Math.sin(a1), + x2: x2 * cos_ar - y2 * sin_ar, + y2: x2 * sin_ar + y2 * cos_ar, + x3: x3 * cos_ar - y3 * sin_ar, + y3: x3 * sin_ar + y3 * cos_ar, + x4: r * Math.cos(a2), + y4: r * Math.sin(a2) + }; + }; + + function context() { + this._isStrokeTransparent = false; + this._strokeOpacity = 1; + this.strokeStyle = '#000000'; + this.fillStyle = '#000000'; + this._isFillTransparent = false; + this._fillOpacity = 1; + this.font = "12pt times"; + this.textBaseline = 'alphabetic'; // top,bottom,middle,ideographic,alphabetic,hanging + this.textAlign = 'start'; + this.lineWidth = 1; + this.lineJoin = 'miter'; // round, bevel, miter + this.lineCap = 'butt'; // butt, round, square + this._transform = [1, 0, 0, 1, 0, 0]; // sx, shy, shx, sy, tx, ty + this.globalCompositeOperation = 'normal'; + this.globalAlpha = 1.0; + this._clip_path = []; + // TODO miter limit //default 10 + + // Not HTML API + this.ignoreClearRect = false; + + this.copy = function (ctx) { + this._isStrokeTransparent = ctx._isStrokeTransparent; + this._strokeOpacity = ctx._strokeOpacity; + this.strokeStyle = ctx.strokeStyle; + this._isFillTransparent = ctx._isFillTransparent; + this._fillOpacity = ctx._fillOpacity; + this.fillStyle = ctx.fillStyle; + this.font = ctx.font; + this.lineWidth = ctx.lineWidth; + this.lineJoin = ctx.lineJoin; + this.lineCap = ctx.lineCap; + this.textBaseline = ctx.textBaseline; + this.textAlign = ctx.textAlign; + this._fontSize = ctx._fontSize; + this._transform = ctx._transform.slice(0); + this.globalCompositeOperation = ctx.globalCompositeOperation; + this.globalAlpha = ctx.globalAlpha; + this._clip_path = ctx._clip_path.slice(0); //TODO deep copy? + + // Not HTML API + this.ignoreClearRect = ctx.ignoreClearRect; + }; + } + + return this; +})(jsPDF.API); + +/** @preserve + * jsPDF fromHTML plugin. BETA stage. API subject to change. Needs browser + * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com + * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria + * 2014 Diego Casorran, https://github.com/diegocr + * 2014 Daniel Husar, https://github.com/danielhusar + * 2014 Wolfgang Gassler, https://github.com/woolfg + * 2014 Steven Spungin, https://github.com/flamenco + * + * + * ==================================================================== + */ + +(function (jsPDFAPI) { + var clone, _DrillForContent, FontNameDB, FontStyleMap, TextAlignMap, FontWeightMap, FloatMap, ClearMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, checkForFooter, process, tableToJson; + clone = function () { + return function (obj) { + Clone.prototype = obj; + return new Clone(); + }; + function Clone() {} + }(); + PurgeWhiteSpace = function PurgeWhiteSpace(array) { + var fragment, i, l, lTrimmed, r, rTrimmed, trailingSpace; + i = 0; + l = array.length; + fragment = void 0; + lTrimmed = false; + rTrimmed = false; + while (!lTrimmed && i !== l) { + fragment = array[i] = array[i].trimLeft(); + if (fragment) { + lTrimmed = true; + } + i++; + } + i = l - 1; + while (l && !rTrimmed && i !== -1) { + fragment = array[i] = array[i].trimRight(); + if (fragment) { + rTrimmed = true; + } + i--; + } + r = /\s+$/g; + trailingSpace = true; + i = 0; + while (i !== l) { + // Leave the line breaks intact + if (array[i] != "\u2028") { + fragment = array[i].replace(/\s+/g, " "); + if (trailingSpace) { + fragment = fragment.trimLeft(); + } + if (fragment) { + trailingSpace = r.test(fragment); + } + array[i] = fragment; + } + i++; + } + return array; + }; + Renderer = function Renderer(pdf, x, y, settings) { + this.pdf = pdf; + this.x = x; + this.y = y; + this.settings = settings; + //list of functions which are called after each element-rendering process + this.watchFunctions = []; + this.init(); + return this; + }; + ResolveFont = function ResolveFont(css_font_family_string) { + var name, part, parts; + name = void 0; + parts = css_font_family_string.split(","); + part = parts.shift(); + while (!name && part) { + name = FontNameDB[part.trim().toLowerCase()]; + part = parts.shift(); + } + return name; + }; + ResolveUnitedNumber = function ResolveUnitedNumber(css_line_height_string) { + + //IE8 issues + css_line_height_string = css_line_height_string === "auto" ? "0px" : css_line_height_string; + if (css_line_height_string.indexOf("em") > -1 && !isNaN(Number(css_line_height_string.replace("em", "")))) { + css_line_height_string = Number(css_line_height_string.replace("em", "")) * 18.719 + "px"; + } + if (css_line_height_string.indexOf("pt") > -1 && !isNaN(Number(css_line_height_string.replace("pt", "")))) { + css_line_height_string = Number(css_line_height_string.replace("pt", "")) * 1.333 + "px"; + } + + var normal, undef, value; + undef = void 0; + normal = 16.00; + value = UnitedNumberMap[css_line_height_string]; + if (value) { + return value; + } + value = { + "xx-small": 9, + "x-small": 11, + small: 13, + medium: 16, + large: 19, + "x-large": 23, + "xx-large": 28, + auto: 0 + }[{ css_line_height_string: css_line_height_string }]; + + if (value !== undef) { + return UnitedNumberMap[css_line_height_string] = value / normal; + } + if (value = parseFloat(css_line_height_string)) { + return UnitedNumberMap[css_line_height_string] = value / normal; + } + value = css_line_height_string.match(/([\d\.]+)(px)/); + if (value.length === 3) { + return UnitedNumberMap[css_line_height_string] = parseFloat(value[1]) / normal; + } + return UnitedNumberMap[css_line_height_string] = 1; + }; + GetCSS = function GetCSS(element) { + var css, tmp, computedCSSElement; + computedCSSElement = function (el) { + var compCSS; + compCSS = function (el) { + if (document.defaultView && document.defaultView.getComputedStyle) { + return document.defaultView.getComputedStyle(el, null); + } else if (el.currentStyle) { + return el.currentStyle; + } else { + return el.style; + } + }(el); + return function (prop) { + prop = prop.replace(/-\D/g, function (match) { + return match.charAt(1).toUpperCase(); + }); + return compCSS[prop]; + }; + }(element); + css = {}; + tmp = void 0; + css["font-family"] = ResolveFont(computedCSSElement("font-family")) || "times"; + css["font-style"] = FontStyleMap[computedCSSElement("font-style")] || "normal"; + css["text-align"] = TextAlignMap[computedCSSElement("text-align")] || "left"; + tmp = FontWeightMap[computedCSSElement("font-weight")] || "normal"; + if (tmp === "bold") { + if (css["font-style"] === "normal") { + css["font-style"] = tmp; + } else { + css["font-style"] = tmp + css["font-style"]; + } + } + css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1; + css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1; + css["display"] = computedCSSElement("display") === "inline" ? "inline" : "block"; + + tmp = css["display"] === "block"; + css["margin-top"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-top")) || 0; + css["margin-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0; + css["padding-top"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-top")) || 0; + css["padding-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0; + css["margin-left"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-left")) || 0; + css["margin-right"] = tmp && ResolveUnitedNumber(computedCSSElement("margin-right")) || 0; + css["padding-left"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-left")) || 0; + css["padding-right"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-right")) || 0; + + css["page-break-before"] = computedCSSElement("page-break-before") || "auto"; + + //float and clearing of floats + css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none"; + css["clear"] = ClearMap[computedCSSElement("clear")] || "none"; + + css["color"] = computedCSSElement("color"); + + return css; + }; + elementHandledElsewhere = function elementHandledElsewhere(element, renderer, elementHandlers) { + var handlers, i, isHandledElsewhere, l, classNames; + isHandledElsewhere = false; + i = void 0; + l = void 0; + handlers = elementHandlers["#" + element.id]; + if (handlers) { + if (typeof handlers === "function") { + isHandledElsewhere = handlers(element, renderer); + } else { + i = 0; + l = handlers.length; + while (!isHandledElsewhere && i !== l) { + isHandledElsewhere = handlers[i](element, renderer); + i++; + } + } + } + handlers = elementHandlers[element.nodeName]; + if (!isHandledElsewhere && handlers) { + if (typeof handlers === "function") { + isHandledElsewhere = handlers(element, renderer); + } else { + i = 0; + l = handlers.length; + while (!isHandledElsewhere && i !== l) { + isHandledElsewhere = handlers[i](element, renderer); + i++; + } + } + } + + // Try class names + classNames = element.className ? element.className.split(' ') : []; + for (i = 0; i < classNames.length; i++) { + handlers = elementHandlers['.' + classNames[i]]; + if (!isHandledElsewhere && handlers) { + if (typeof handlers === "function") { + isHandledElsewhere = handlers(element, renderer); + } else { + i = 0; + l = handlers.length; + while (!isHandledElsewhere && i !== l) { + isHandledElsewhere = handlers[i](element, renderer); + i++; + } + } + } + } + + return isHandledElsewhere; + }; + tableToJson = function tableToJson(table, renderer) { + var data, headers, i, j, rowData, tableRow, table_obj, table_with, cell, l; + data = []; + headers = []; + i = 0; + l = table.rows[0].cells.length; + table_with = table.clientWidth; + while (i < l) { + cell = table.rows[0].cells[i]; + headers[i] = { + name: cell.textContent.toLowerCase().replace(/\s+/g, ''), + prompt: cell.textContent.replace(/\r?\n/g, ''), + width: cell.clientWidth / table_with * renderer.pdf.internal.pageSize.width + }; + i++; + } + i = 1; + while (i < table.rows.length) { + tableRow = table.rows[i]; + rowData = {}; + j = 0; + while (j < tableRow.cells.length) { + rowData[headers[j].name] = tableRow.cells[j].textContent.replace(/\r?\n/g, ''); + j++; + } + data.push(rowData); + i++; + } + return table_obj = { + rows: data, + headers: headers + }; + }; + var SkipNode = { + SCRIPT: 1, + STYLE: 1, + NOSCRIPT: 1, + OBJECT: 1, + EMBED: 1, + SELECT: 1 + }; + var listCount = 1; + _DrillForContent = function DrillForContent(element, renderer, elementHandlers) { + var cn, cns, fragmentCSS, i, isBlock, l, px2pt, table2json, cb; + cns = element.childNodes; + cn = void 0; + fragmentCSS = GetCSS(element); + isBlock = fragmentCSS.display === "block"; + if (isBlock) { + renderer.setBlockBoundary(); + renderer.setBlockStyle(fragmentCSS); + } + i = 0; + l = cns.length; + while (i < l) { + cn = cns[i]; + if ((typeof cn === "undefined" ? "undefined" : _typeof(cn)) === "object") { + + //execute all watcher functions to e.g. reset floating + renderer.executeWatchFunctions(cn); + + /*** HEADER rendering **/ + if (cn.nodeType === 1 && cn.nodeName === 'HEADER') { + var header = cn; + //store old top margin + var oldMarginTop = renderer.pdf.margins_doc.top; + //subscribe for new page event and render header first on every page + renderer.pdf.internal.events.subscribe('addPage', function (pageInfo) { + //set current y position to old margin + renderer.y = oldMarginTop; + //render all child nodes of the header element + _DrillForContent(header, renderer, elementHandlers); + //set margin to old margin + rendered header + 10 space to prevent overlapping + //important for other plugins (e.g. table) to start rendering at correct position after header + renderer.pdf.margins_doc.top = renderer.y + 10; + renderer.y += 10; + }, false); + } + + if (cn.nodeType === 8 && cn.nodeName === "#comment") { + if (~cn.textContent.indexOf("ADD_PAGE")) { + renderer.pdf.addPage(); + renderer.y = renderer.pdf.margins_doc.top; + } + } else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) { + /*** IMAGE RENDERING ***/ + var cached_image; + if (cn.nodeName === "IMG") { + var url = cn.getAttribute("src"); + cached_image = images[renderer.pdf.sHashCode(url) || url]; + } + if (cached_image) { + if (renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom < renderer.y + cn.height && renderer.y > renderer.pdf.margins_doc.top) { + renderer.pdf.addPage(); + renderer.y = renderer.pdf.margins_doc.top; + //check if we have to set back some values due to e.g. header rendering for new page + renderer.executeWatchFunctions(cn); + } + + var imagesCSS = GetCSS(cn); + var imageX = renderer.x; + var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor; + + //define additional paddings, margins which have to be taken into account for margin calculations + var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"]) * fontToUnitRatio; + var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"]) * fontToUnitRatio; + var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"]) * fontToUnitRatio; + var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"]) * fontToUnitRatio; + + //if float is set to right, move the image to the right border + //add space if margin is set + if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') { + imageX += renderer.settings.width - cn.width - additionalSpaceRight; + } else { + imageX += additionalSpaceLeft; + } + + renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height); + cached_image = undefined; + //if the float prop is specified we have to float the text around the image + if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') { + //add functiont to set back coordinates after image rendering + renderer.watchFunctions.push(function (diffX, thresholdY, diffWidth, el) { + //undo drawing box adaptions which were set by floating + if (renderer.y >= thresholdY) { + renderer.x += diffX; + renderer.settings.width += diffWidth; + return true; + } else if (el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x + el.width > renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width) { + renderer.x += diffX; + renderer.y = thresholdY; + renderer.settings.width += diffWidth; + return true; + } else { + return false; + } + }.bind(this, imagesCSS['float'] === 'left' ? -cn.width - additionalSpaceLeft - additionalSpaceRight : 0, renderer.y + cn.height + additionalSpaceTop + additionalSpaceBottom, cn.width)); + //reset floating by clear:both divs + //just set cursorY after the floating element + renderer.watchFunctions.push(function (yPositionAfterFloating, pages, el) { + if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) { + if (el.nodeType === 1 && GetCSS(el).clear === 'both') { + renderer.y = yPositionAfterFloating; + return true; + } else { + return false; + } + } else { + return true; + } + }.bind(this, renderer.y + cn.height, renderer.pdf.internal.getNumberOfPages())); + + //if floating is set we decrease the available width by the image width + renderer.settings.width -= cn.width + additionalSpaceLeft + additionalSpaceRight; + //if left just add the image width to the X coordinate + if (imagesCSS['float'] === 'left') { + renderer.x += cn.width + additionalSpaceLeft + additionalSpaceRight; + } + } else { + //if no floating is set, move the rendering cursor after the image height + renderer.y += cn.height + additionalSpaceTop + additionalSpaceBottom; + } + + /*** TABLE RENDERING ***/ + } else if (cn.nodeName === "TABLE") { + table2json = tableToJson(cn, renderer); + renderer.y += 10; + renderer.pdf.table(renderer.x, renderer.y, table2json.rows, table2json.headers, { + autoSize: false, + printHeaders: elementHandlers.printHeaders, + margins: renderer.pdf.margins_doc, + css: GetCSS(cn) + }); + renderer.y = renderer.pdf.lastCellPos.y + renderer.pdf.lastCellPos.h + 20; + } else if (cn.nodeName === "OL" || cn.nodeName === "UL") { + listCount = 1; + if (!elementHandledElsewhere(cn, renderer, elementHandlers)) { + _DrillForContent(cn, renderer, elementHandlers); + } + renderer.y += 10; + } else if (cn.nodeName === "LI") { + var temp = renderer.x; + renderer.x += 20 / renderer.pdf.internal.scaleFactor; + renderer.y += 3; + if (!elementHandledElsewhere(cn, renderer, elementHandlers)) { + _DrillForContent(cn, renderer, elementHandlers); + } + renderer.x = temp; + } else if (cn.nodeName === "BR") { + renderer.y += fragmentCSS["font-size"] * renderer.pdf.internal.scaleFactor; + renderer.addText("\u2028", clone(fragmentCSS)); + } else { + if (!elementHandledElsewhere(cn, renderer, elementHandlers)) { + _DrillForContent(cn, renderer, elementHandlers); + } + } + } else if (cn.nodeType === 3) { + var value = cn.nodeValue; + if (cn.nodeValue && cn.parentNode.nodeName === "LI") { + if (cn.parentNode.parentNode.nodeName === "OL") { + value = listCount++ + '. ' + value; + } else { + var fontSize = fragmentCSS["font-size"]; + var offsetX = (3 - fontSize * 0.75) * renderer.pdf.internal.scaleFactor; + var offsetY = fontSize * 0.75 * renderer.pdf.internal.scaleFactor; + var radius = fontSize * 1.74 / renderer.pdf.internal.scaleFactor; + cb = function cb(x, y) { + this.pdf.circle(x + offsetX, y + offsetY, radius, 'FD'); + }; + } + } + // Only add the text if the text node is in the body element + // Add compatibility with IE11 + if (!!(cn.ownerDocument.body.compareDocumentPosition(cn) & 16)) { + renderer.addText(value, fragmentCSS); + } + } else if (typeof cn === "string") { + renderer.addText(cn, fragmentCSS); + } + } + i++; + } + elementHandlers.outY = renderer.y; + + if (isBlock) { + return renderer.setBlockBoundary(cb); + } + }; + images = {}; + loadImgs = function loadImgs(element, renderer, elementHandlers, cb) { + var imgs = element.getElementsByTagName('img'), + l = imgs.length, + found_images, + x = 0; + function done() { + renderer.pdf.internal.events.publish('imagesLoaded'); + cb(found_images); + } + function loadImage(url, width, height) { + if (!url) return; + var img = new Image(); + found_images = ++x; + img.crossOrigin = ''; + img.onerror = img.onload = function () { + if (img.complete) { + //to support data urls in images, set width and height + //as those values are not recognized automatically + if (img.src.indexOf('data:image/') === 0) { + img.width = width || img.width || 0; + img.height = height || img.height || 0; + } + //if valid image add to known images array + if (img.width + img.height) { + var hash = renderer.pdf.sHashCode(url) || url; + images[hash] = images[hash] || img; + } + } + if (! --x) { + done(); + } + }; + img.src = url; + } + while (l--) { + loadImage(imgs[l].getAttribute("src"), imgs[l].width, imgs[l].height); + }return x || done(); + }; + checkForFooter = function checkForFooter(elem, renderer, elementHandlers) { + //check if we can found a