Compare commits

...

997 Commits

Author SHA1 Message Date
6020386bbe examples: fix duplication (#2813) 2022-01-23 00:27:44 +08:00
f7c2289db1 Update CHANGELOG.md 2022-01-23 00:24:05 +08:00
CI
7c3269bdbe chore(release): 1.4.1 2022-01-22 16:19:57 +00:00
e587a82dca fix: Properties x and y of BoundingRect is undefined in old browser (#2797) 2022-01-23 00:18:49 +08:00
67c5e8dec4 deps: fix source maps (#2812) 2022-01-23 00:17:25 +08:00
181d1b1103 feat: add support for <video> elements (#2788) 2022-01-02 16:14:27 +08:00
46db86755f fix: source maps (#2787) 2022-01-02 16:08:10 +08:00
CI
9fda3e1ede chore(release): 1.4.0 2022-01-01 15:08:48 +00:00
6521a487d7 feat: use native text segmenter where available (#2782) 2022-01-01 21:22:31 +08:00
0476d06515 fix: ios text wrapping with 0 width rect (#2786) 2022-01-01 13:07:52 +08:00
74696faf47 fix: adopted stylesheets (#2785) 2022-01-01 01:57:17 +08:00
1cc853a318 fix: reduce SLICE_STACK_SIZE to 50k (#2784) 2021-12-31 15:30:18 +08:00
CI
04787ee88a chore(release): 1.3.4 2021-12-29 14:11:55 +00:00
ba2b1cd8e9 fix: ios 15 font rendering crash (#2645) 2021-12-29 22:09:32 +08:00
d9222075da ci: add ios 15.0 testing (#2780) 2021-12-29 21:02:16 +08:00
CI
101c32ed71 chore(release): 1.3.3 2021-11-23 10:09:40 +00:00
ddffaecf6d add missing changelog entries for 1.0.0 (#2758) 2021-11-23 17:47:45 +08:00
fd22a01a3c fix: "offsets" text when font is big 2021-11-23 17:32:41 +08:00
ed57781594 ci: fix macos action runners (#2757) 2021-11-23 17:17:51 +08:00
eeda86bd5e fix: const enums (#2651) 2021-08-16 19:30:33 +08:00
CI
0b1f0a7e90 chore(release): 1.3.2 2021-08-15 12:54:26 +00:00
38c682955a fix: overflows with absolutely positioned content (#2663) 2021-08-15 19:41:57 +08:00
01ed87907a docs: add warning for webgl cloning with preserveDrawingBuffer=false (#2661) 2021-08-14 17:18:30 +08:00
58ff0003f7 docs: include src files on www (#2660) 2021-08-14 16:48:22 +08:00
f143166551 fix: disable transition properties (#2659) 2021-08-14 16:22:36 +08:00
cd0d7258c3 feat: add support for data-html2canvas-debug property for debugging (#2658) 2021-08-14 15:01:41 +08:00
CI
b482725994 chore(release): 1.3.1 2021-08-14 06:06:09 +00:00
1b55ed5668 fix: multi arg transition/animation duration (#2657) 2021-08-14 14:05:15 +08:00
CI
68377b3244 chore(release): 1.3.0 2021-08-13 11:52:05 +00:00
6947982848 feat: add rtl render support (#2653) 2021-08-13 19:50:59 +08:00
437b367d3b feat: correctly break graphemes (#2652) 2021-08-13 18:57:49 +08:00
969638fb94 fix: finish animation/transitions for elements (#2632) 2021-08-13 18:15:55 +08:00
f919204efa test: add letter-spacing test for zwj emoji (#2650) 2021-08-13 17:45:42 +08:00
c378e22069 fix: correctly handle allowTaint canvas cloning (#2649) 2021-08-13 17:32:55 +08:00
CI
2b4de68e92 chore(release): 1.2.2 2021-08-10 10:39:01 +00:00
1941b9e0ac fix: parsing counter content in pseudo element (#2640) 2021-08-09 18:43:42 +08:00
e429e0443a ci: add ios15 target (#2564) 2021-08-09 17:15:00 +08:00
f43f942fcd fix: test for ios range line break error (#2635) 2021-08-09 17:14:40 +08:00
7a06d0c2c2 docs: update test previewer (#2637) 2021-08-07 18:05:08 +08:00
e36408ad03 test: large base64 encoded background (#2636) 2021-08-07 15:47:15 +08:00
a0dd38a8be fix: radial gradient ry check (#2631) 2021-08-05 14:07:10 +08:00
CI
b988d9d657 chore(release): 1.2.1 2021-08-05 02:39:44 +00:00
c5c6fa00d7 fix: type import that is only available ts 3.8 or higher (#2629) 2021-08-05 10:17:30 +08:00
6651fc6789 fix: none image (#2627) 2021-08-05 09:30:22 +08:00
CI
df223c3ff2 chore(release): 1.2.0 2021-08-04 15:00:38 +00:00
95a46b00c5 fix: overflow-wrap break-word (#2626) 2021-08-04 22:53:48 +08:00
878e37a242 fix: element cropping & scrolling (#2625) 2021-08-04 20:58:17 +08:00
1338c7b203 test: element with scrolled window (#2624) 2021-08-04 11:28:05 +08:00
CI
f284752295 chore(release): 1.1.5 2021-08-02 10:38:51 +00:00
96e23d1851 fix: natural sizes for images with srcset (#2622) 2021-08-02 18:27:03 +08:00
7d788c6f3d fix: emoji line breaking (fix #1813) (#2621)
* fix: emoji line breaking (fix #1813)

* test: fix text.html reftest
2021-08-02 17:49:46 +08:00
5dea36bd69 docs: update README to github discussion Q/A 2021-07-17 17:24:15 +08:00
CI
11d16d2b77 chore(release): 1.1.4 2021-07-15 16:44:42 +00:00
e9f7f48d57 fix: word-break seperators (#2593) 2021-07-15 21:05:26 +08:00
4c360fc1f0 test: refactor language tests (#2594) 2021-07-15 20:58:04 +08:00
578bb771bf test: update box-shadow with radius 2021-07-15 18:24:39 +08:00
522e5aac5f feat: add support for webkit-text-stroke and paint-order (#2591) 2021-07-15 18:19:26 +08:00
dd6d8856ec fix: svg d path getting truncated on copy (#2589) 2021-07-15 17:15:47 +08:00
45efe54da8 fix: this.canvas.ownerDocument is undefined (#2590) 2021-07-15 17:08:43 +08:00
cd99f11b1b fix: text position for form elements and list markers (#2588) 2021-07-15 16:54:01 +08:00
fa60716d07 fix: don't copy 'all' css property (#2586) 2021-07-14 22:49:52 +08:00
CI
99b687c412 chore(release): 1.1.3 2021-07-14 13:08:25 +00:00
1d00bfe175 test: add test cases for text-stroke and textarea from (#1540 and #2132) (#2585) 2021-07-14 21:07:39 +08:00
58b4591174 feat: allow access to reference element in onclone (#2584) 2021-07-14 21:07:10 +08:00
92fa448913 fix: responsive svg images (#2583) 2021-07-14 20:44:04 +08:00
1acdc827a4 fix: image blob rendering 2021-07-14 20:21:57 +08:00
acb4cd24b8 feat: support for custom and slot elements (#2581) 2021-07-14 17:59:08 +08:00
eeb5a3ea1d fix: iframe load to ensure images are loaded (#2577) 2021-07-14 16:18:43 +08:00
CI
52a03c76b6 chore(release): 1.1.2 2021-07-13 14:01:24 +00:00
439e365ea8 fix: text-shadow position with baseline (#2576) 2021-07-13 19:14:27 +08:00
e29af58661 ci: implement screenshot diffing (#2571) 2021-07-13 18:48:18 +08:00
171585491d fix: logger unique names (#2575) 2021-07-13 18:07:09 +08:00
CI
99b8182991 chore(release): 1.1.1 2021-07-12 11:31:49 +00:00
a4a3ce8a2e fix: allow proxy url with parameters (#2100) 2021-07-12 19:14:01 +08:00
084017a673 fix: crash on background-size with calc() (fix #2469) (#2569) 2021-07-12 19:09:57 +08:00
4555940d0b fix: handle unhandled promise rejections (#2568) 2021-07-12 19:01:43 +08:00
CI
382853cb33 chore(release): 1.1.0 2021-07-11 14:15:36 +00:00
44296e5293 fix: text-decoration-line fallback (#2088) (#2567) 2021-07-11 22:11:29 +08:00
b2902ec31c deps: update dependencies with lint fixes (#2565) 2021-07-11 20:25:22 +08:00
e7a021ab93 ci: fail build if no artifacts uploaded (#2566) 2021-07-11 19:48:18 +08:00
85f79c1a5e fix: use baseline for text positioning (#2109) 2021-07-11 13:23:23 +08:00
cf35a282b2 docs: update border-style support 2021-07-04 14:12:48 +08:00
bb9237155c fix: Ensure resizeImage's canvas has at least 1px of width and height (#2409)
* ensure resizeImage canvas has > 0 width, height

* add tests for sliver background images
2021-07-04 13:40:25 +08:00
CI
b09ee30dae chore(release): 1.0.0 2021-07-04 05:15:53 +00:00
72cd528429 add features for border-style dashed, dotted, double. (#2531) 2021-07-04 12:17:07 +08:00
2a013e20c8 deps: update www deps (#2525) 2021-05-08 20:31:35 +08:00
ff35c7dbd3 test: update karma runner (#2524)
* test: update karma runner

* fix: Promise polyfill for testrunner
2021-05-08 18:32:03 +08:00
ba172678f0 fix top right border radius (#2522) 2021-05-07 17:32:51 +08:00
7222aba1b4 ci: update docs publish action (#2451) 2020-12-29 15:25:08 +08:00
82b7da558c fix: opacity with overflow hidden (#2450) 2020-12-29 12:29:00 +08:00
CI
3982df1492 chore(release): 1.0.0-rc.7 2020-08-09 14:49:12 +00:00
1514220812 fix: external styles on svg elements (#2320) 2020-08-09 14:14:50 +08:00
d07b6811c6 fix(z-index):z-index order (fix #2089) 2020-08-09 12:49:31 +08:00
bacfadff96 fix: concatenate contiguous font-family tokens (#2219) 2020-08-09 12:41:53 +08:00
CI
d7d17adf70 chore(release): 1.0.0-rc.6 2020-08-08 10:19:43 +00:00
60f6b85083 Update release script (#2319) 2020-08-08 18:18:10 +08:00
CI
e3237dc4ce chore(release): 1.0.0-rc.6 2020-08-08 08:46:37 +00:00
659f820170 update standard-release (#2317) 2020-08-08 16:45:26 +08:00
f7b39c0914 update npm build (#2316) 2020-08-08 16:00:41 +08:00
cd77e1cea1 update dependencies (#2315) 2020-08-08 15:37:50 +08:00
f23e6f6f26 fix: image loading="lazy" fix #2312 (#2314) 2020-08-08 15:37:34 +08:00
04e145b5eb Update CI 2020-08-08 14:54:12 +08:00
003cfd9073 Update ci 2020-08-02 16:34:56 +08:00
9749bb1fb1 Update ci (#2307) 2020-08-02 16:14:29 +08:00
3c2c826ad7 Update ci (#2306) 2020-08-02 15:49:02 +08:00
e496047888 Update ci.yml 2020-08-02 14:39:09 +08:00
51de34787a ci: build docs (#2305) 2020-07-31 19:21:02 +08:00
6c86f5f653 Update ci.yml 2020-07-31 00:57:23 +08:00
8f4ec8a0c0 Update ci.yml 2020-07-31 00:18:23 +08:00
51aadbb7f1 Update ci.yml 2020-07-30 23:38:22 +08:00
8af7745e80 Update release.yml 2020-07-30 23:20:26 +08:00
610997923a Create release.yml 2020-07-30 23:11:27 +08:00
d844328e0a Update ci.yml 2020-07-29 18:52:46 +08:00
e0d536777a Update new ios simulators 2020-07-29 18:47:43 +08:00
cc19105c91 Update ci.yml 2020-07-29 18:30:41 +08:00
2d30554b2a Update ci.yml 2020-07-29 18:17:46 +08:00
401df79087 Update ci.yml 2020-07-29 18:16:52 +08:00
7cb0019594 Update ci.yml 2020-07-28 23:59:49 +08:00
ebe9eccbd2 Update ci.yml 2020-07-28 23:48:05 +08:00
df44c86f68 Update ci.yml 2020-07-28 23:47:14 +08:00
e083e229c9 Update ci.yml 2020-07-28 23:07:29 +08:00
b308cbd998 Create ci.yml 2020-07-28 22:59:26 +08:00
4dd4a69c7a tests(reftests): Upgrade to fontawesome v5.13.0 (#2202)
* support font-family names with numbers in (e.g. Font Awesome 5)

* check for spaces in font family names first so generic font names don't get wrapped in quotes

* Update reftest to use fontawesome 5.13.0

* Add fontawesome v4 shims

* Revert "Merge remote-tracking branch 'grahamkane/master' into issue-1948-test-coverage"

This reverts commit 5211ab4065, reversing
changes made to ae0bd29d37.

* Revert "Revert "Merge remote-tracking branch 'grahamkane/master' into issue-1948-test-coverage""

This reverts commit 4911fe25f4.

* Revert "Merge remote-tracking branch 'grahamkane/master' into issue-1948-test-coverage"

This reverts commit 5211ab4065, reversing
changes made to ae0bd29d37.

Co-authored-by: Graham Kane <grahamkane@gmail.com>
2020-04-19 16:46:25 +08:00
c366e8790d ci: Azure Pipelines: upgrade from macOS 10.13 -> 10.14 (#2204) 2020-04-15 13:03:04 +08:00
ae5f866b37 Migrate base Chrome profiles to ChromeHeadless (#2203) 2020-04-14 18:15:13 +08:00
f139b513c5 fix: #1868 Clone node, Setting className for SVG element raises error (#2079)
* fix: #1868 Clone node, Setting className for SVG element raises error

* fix: svg element type information
2019-11-25 20:55:28 -08:00
e4d52a1ac6 Fix error in server-side rendering (#2039) 2019-11-25 20:16:07 -08:00
CI
8c04f94bc9 chore(release): 1.0.0-rc.5 2019-09-27 13:58:48 +00:00
3f599103fb fix: safari pseudo element content parsing (#2018)
* fix: await for fonts to be ready in document clone

* fix: safari pseudo element content parsing

* fix: safari counter-increment / counter-reset
2019-09-27 06:42:13 -07:00
076492042a fix: using existing canvas option (#2017)
* refactor: document cleanup to DocumentCloner

* fix: using existing canvas option

* fix: lint errors

* fix: preview transform origin
2019-09-25 23:34:18 -07:00
34b06d6365 fix: correctly respect logging option (#2013)
* test: update to using jest for unit tests

* remove mocha types

* revert to using mocha for testrunner.ts

* add logger unit testing

* fix reftest previewer scaling

* fix LoggerOptions to interface

* fix linting
2019-09-25 03:37:59 -07:00
CI
99f105cb66 chore(release): 1.0.0-rc.4 2019-09-22 05:11:36 +00:00
7d3456b78c fix: null backgroundColor option as transparent (#2012) 2019-09-21 21:45:05 -07:00
00555cf1ef fix: nested z-index ordering (#2011)
* fix zindex bug

* test: add z-index reftest for #1978

* fix: z-index ordering early exit
2019-09-21 21:12:36 -07:00
eedb81ef9e fix: correctly render partial borders (fix #1920) (#2010) 2019-09-21 20:33:54 -07:00
d4b58960dc Update canvas-renderer.ts (#2004)
* Update canvas-renderer.ts

Fixed an issue were a page wouldn't render.

Line 581 (now 582) threw exception: "Uncaught (in promise) DOMException: Failed to execute 'createPattern' on 'CanvasRenderingContext2D': The image argument is a canvas element with a width or height of 0."

* Update canvas-renderer.ts
2019-09-21 19:33:48 -07:00
ee3ca35636 add missing -ms-grid display property to support IE grid layouts (#1926) 2019-09-21 19:32:42 -07:00
9a63797aa7 docs: fix typo (#1864) 2019-06-17 21:24:34 -07:00
81dcf7b6be fix: zero size iframe rendering (#1863) 2019-06-17 21:23:45 -07:00
61f4819e02 feat: ignore unsupported image functions (#1873) 2019-06-17 21:22:05 -07:00
CI
86a650bfd5 chore(release): 1.0.0-rc.3 2019-05-30 05:31:15 +00:00
cbaecdca28 fix: stack exceeding for css tokenizer (#1862)
* fix: stack exceeding for css tokenizer

* fix: token string recursion
2019-05-29 22:26:51 -07:00
cae44a6f0a fix: typescript options type definition (#1861) 2019-05-29 21:11:50 -07:00
CI
28dc05c4a3 chore(release): 1.0.0-rc.2 2019-05-29 02:44:57 +00:00
409674fba6 fix: multi token overflow #1850 (#1851) 2019-05-26 14:08:56 -07:00
5f31b74177 feat: box-shadow rendering (#1848)
* feat: box-shadow rendering

* test: add box-shadow test
2019-05-25 21:53:50 -07:00
522a443055 Typescript conversion (#1828)
* initial typescript conversion

* test: update overflow+transform ref test

* fix: correctly render pseudo element content

* fix: testrunner build

* fix: karma test urls

* test: update underline tests with <u> elements

* test: update to es6-promise polyfill

* test: remove watch from server

* test: remove flow

* format: update prettier for typescript

* test: update eslint to use typescript parser

* test: update linear gradient reftest

* test: update test runner

* test: update testrunner promise polyfill

* fix: handle display: -webkit-flex correctly (fix #1817)

* fix: correctly render gradients with clip & repeat (fix #1773)

* fix: webkit-gradient function support

* fix: implement radial gradients

* fix: text-decoration rendering

* fix: missing scroll positions for elements

* ci: fix ios 11 tests

* fix: ie logging

* ci: improve device availability logging

* fix: lint errors

* ci: update to ios 12

* fix: check for console availability

* ci: fix build dependency

* test: update text reftests

* fix: window reference for unit tests

* feat: add hsl/hsla color support

* fix: render options

* fix: CSSKeyframesRule cssText Permission Denied on Internet Explorer 11 (#1830)

* fix: option lint

* fix: list type rendering

* test: fix platform import

* fix: ie css parsing for numbers

* ci: add minified build

* fix: form element rendering

* fix: iframe rendering

* fix: re-introduce experimental foreignobject renderer

* fix: text-shadow rendering

* feat: improve logging

* fix: unit test logging

* fix: cleanup resources

* test: update overflow scrolling to work with ie

* build: update build to include typings

* fix: do not parse select element children

* test: fix onclone test to work with older IEs

* test: reduce reftest canvas sizes

* test: remove dynamic setUp from list tests

* test: update linear-gradient tests

* build: remove old source files

* build: update docs dependencies

* build: fix typescript definition path

* ci: include test.js on docs website
2019-05-25 15:54:41 -07:00
20a797cbeb docs: fix README documentation 2019-04-12 23:37:01 -07:00
43058241b4 docs: remove dead donation link (fix #1802) 2019-04-12 23:28:10 -07:00
cdc4ca8296 test: include reftests previewer with docs website (#1799) 2019-04-12 23:17:23 -07:00
a7d881019b ci: refactor browser tests (#1804) 2019-04-10 21:09:08 -07:00
CI
b89458611b chore(release): 1.0.0-rc.1 2019-04-10 03:43:30 +00:00
4e4a231683 fix: safari data url taints (#1797) 2019-04-09 20:32:22 -07:00
a63cb3c0f1 ci: add ios simulator tests (#1794) 2019-04-08 21:56:22 -07:00
7775d3c0d6 docs: remove invalid async option from docs (fix #1769) (#1796) 2019-04-07 22:16:29 -07:00
397595afb5 fix: don't apply text shadows on elements (#1795)
* Fix for Issue-1638 (https://github.com/niklasvh/html2canvas/issues/1638)
Resolution: Clearing the Shadow Properties after they are consumed
2019-04-07 21:36:14 -07:00
7027900f49 fix: context scale for high resolution displays with foreignobjectrendering (#1782) 2019-04-07 16:49:29 -07:00
49f87fb680 test: fix RefTestRenderer.js inclusion with karma 2019-04-07 15:08:23 -07:00
238de790a9 docs: fix release date in changelog 2019-04-07 15:03:14 -07:00
CI
029235a652 chore(release): 1.0.0-rc.0 2019-04-07 22:01:14 +00:00
44f3d79f68 build: update webpack and babel (#1793) 2019-04-07 14:24:17 -07:00
7ebef72e92 ci: automate changelog generation (#1792) 2019-04-07 12:49:10 -07:00
2c018d1987 fix: wrap .sheet.cssRules access in try...catch. (#1693) 2019-04-07 12:17:43 -07:00
5cbe5db351 fix: prevent unhandled promise rejections for hidden frames (#1762) 2019-04-06 23:44:01 -07:00
3212184146 docs: improve canvas size limit documentation (#1576) 2019-04-06 23:36:29 -07:00
349bbf137a fix: enforce colorstop min 0 (#1743) 2019-04-06 23:24:07 -07:00
c45ef099fe ci: Improve CI pipeline (#1790) 2019-04-06 23:07:52 -07:00
24823d0491 Upgrade gatsbyjs to v2 2019-04-05 21:28:45 -07:00
41eb8ab22f Add support for ES and browser bundlers (#1534) 2018-07-06 23:03:14 +08:00
4e02a4c7a1 Remove unintended space (#1501) 2018-07-06 23:02:50 +08:00
ce45c1bbdd Update proxy version 2018-07-06 22:55:59 +08:00
078b388974 Update mocha 2018-07-06 22:55:11 +08:00
3ae7dd2ebb Flow ignore rollup config 2018-07-06 22:39:35 +08:00
dab77acde4 Update package-lock.json 2018-07-06 22:31:13 +08:00
83e7eaa795 Fix type import 2018-07-06 22:28:48 +08:00
cb7fbcf33e Begin implementing rollup 2018-07-06 22:28:30 +08:00
9a6e57aa00 v1.0.0-alpha.12 2018-04-05 20:49:54 +08:00
a3e25d71cb Fix white space appearing on element rendering (Fix #1438) 2018-04-05 20:41:16 +08:00
9da0f60551 Reset canvas transform on finish (Fix #1494) 2018-04-05 19:42:32 +08:00
f347953042 Remove iOS 8.4 tests 2018-04-01 17:42:27 +08:00
48b4c20e24 v1.0.0-alpha.11 2018-04-01 17:14:38 +08:00
6341788edf Update package-lock.json 2018-04-01 17:09:49 +08:00
0e273418c7 IE11 Member not found fix Wrap accesing the cssText property in a try...catch (#1415)
* https://github.com/niklasvh/html2canvas/issues/1374 - Wrap accesing the cssText property in a try...catch
2018-04-01 16:49:23 +08:00
e6bbc1abb5 Merge pull request #1487 from mapleeit/support-blob-image-resources
support blob image resources in non-foreignObjectRendering mode
2018-04-01 16:43:40 +08:00
13bbc90048 support blob image resources in non-foreignObjectRendering mode 2018-03-30 16:37:18 +08:00
102b5a1282 v1.0.0-alpha.10 2018-02-15 22:50:40 +08:00
01e4920876 Fix window reference for node tests 2018-02-15 22:19:25 +08:00
da2794f7f7 Fix version logging (Fix #1421) 2018-02-15 22:07:40 +08:00
9fb9898894 Re-introduce onclone option (Fix #1434) 2018-02-15 21:40:48 +08:00
952eb4cf7c Fix Travis chrome tests 2018-02-15 21:28:11 +08:00
fad4f837c9 Add ignoreElements predicate function option 2018-02-15 21:26:09 +08:00
e6c44afca1 Merge pull request #1417 from eKoopmans/bugfix/underlines
Revert "Fix underlines, relative to 'bottom' baseline"
2018-02-15 21:00:48 +08:00
d023de0b99 Revert "Fix underlines, relative to 'bottom' baseline"
This reverts commit 0c8d38d9c0.
2018-01-26 18:10:15 +11:00
0f01810005 Update website styles 2018-01-08 22:21:11 +08:00
bf03cf5237 Make website responsive 2018-01-08 21:38:54 +08:00
a555dfc085 Revert "Revert "Update html2canvas version""
This reverts commit 69fb48969e.
2018-01-08 20:42:48 +08:00
69fb48969e Revert "Update html2canvas version"
This reverts commit c9a60c4ff9.

# Conflicts:
#	www/package-lock.json
#	www/package.json
2018-01-07 23:19:47 +08:00
974c35c368 Update website html2canvas package 2018-01-07 22:14:16 +08:00
0fe9632a32 v1.0.0-alpha.9 2018-01-07 20:56:59 +08:00
c9a60c4ff9 Update html2canvas version 2018-01-07 20:54:22 +08:00
4c14894a0a Correctly clone dynami CSSStyleSheets (Fix #1370) 2018-01-07 20:13:26 +08:00
8788a9f458 Add npm badges 2018-01-07 19:22:36 +08:00
e198eae398 Merge branch 'jkrielaars-border-radius' 2018-01-07 19:20:24 +08:00
474b5e81a7 Refactor border-radius update 2018-01-07 19:19:55 +08:00
b97972eeb6 updated calculation of border-radius 2018-01-04 08:59:38 +01:00
b7c7464c5f v1.0.0-alpha.8 2018-01-02 20:24:12 +08:00
ae019f174c Use correct doctype in cloned Document (Fix #1298) 2018-01-02 20:06:24 +08:00
ea6062c85b Fix individual border rendering (Fix #1349) 2018-01-02 20:04:28 +08:00
9a4a506366 v1.0.0-alpha.7 2017-12-31 20:22:20 +08:00
cb93b80d0d Add thai text test 2017-12-31 20:19:27 +08:00
79e1c857e6 Fix form input text positions (Fix #1338 #1347) 2017-12-31 19:38:31 +08:00
cc9d1f89dc Merge pull request #1348 from niklasvh/line-breaking
Implement unicode line-breaking
2017-12-31 19:14:26 +08:00
d0f7ecfa9a Update css-line-breaking to 1.0.1 2017-12-31 00:24:38 +08:00
1870433307 Implement unicode line-breaking 2017-12-31 00:14:21 +08:00
3a5ed43e97 Update package-lock.json 2017-12-29 19:15:49 +08:00
8429761e8f Fix tag names 2017-12-28 14:25:29 +08:00
c4e670addf v1.0.0-alpha6 2017-12-28 14:19:52 +08:00
0aeb54ca2e Remove console.logs 2017-12-28 13:58:42 +08:00
eec84fa39e Fix list-style-type: none (Fix #1340) 2017-12-28 13:52:05 +08:00
22f58d5d1c Merge branch 'vnmc-feature/HTC-0010_PseudoContent' 2017-12-24 17:09:27 +08:00
9046e0d554 Update to use list style parser from ListItem 2017-12-24 17:08:54 +08:00
afa5d7cb8e Merge branch 'feature/HTC-0010_PseudoContent' of git://github.com/vnmc/html2canvas into vnmc-feature/HTC-0010_PseudoContent 2017-12-24 16:30:13 +08:00
3881e3cf96 Update support for list-style 2017-12-22 00:07:10 +08:00
0aa973ab0d v1.0.0-alpha.5 2017-12-21 23:54:27 +08:00
baaf9b0701 Merge branch 'bugfix/underlines' of git://github.com/eKoopmans/html2canvas into eKoopmans-bugfix/underlines 2017-12-21 23:45:06 +08:00
02de2ee829 Document data-html2canvas-ignore (Fix #1316) 2017-12-21 23:42:59 +08:00
a570f5df74 Update useCORS documentation (Fix #1323) 2017-12-21 23:41:01 +08:00
38749bc4b6 Fix canvas rendering on Chrome 2017-12-21 23:31:55 +08:00
e1d6b4c76f Fix overflow: auto 2017-12-21 23:29:59 +08:00
31f2c22477 Fix list style issues 2017-12-21 23:22:09 +08:00
6d0cd2d226 fixed flow problems in PseudoNodeContent.js 2017-12-15 23:46:26 +01:00
7335984ab7 added support for rendering ordered lists and list-style 2017-12-15 22:55:27 +01:00
78c3c7fc71 improved support of 'content' for pseudo elements (multiple components, counters, attr, quotes) 2017-12-15 12:40:04 +01:00
4551976246 Merge pull request #1312 from a0viedo/patch-1
change build badge to SVG
2017-12-14 11:01:32 +08:00
9e04772b42 change build badge to SVG
since it will be better for high-res screens
2017-12-13 13:01:35 -03:00
54c4002df7 Fix example button hitbox 2017-12-12 23:58:09 +08:00
91641a3746 Deploy new website 2017-12-12 23:26:28 +08:00
c4ba6f795c Update CHANGELOG 2017-12-12 22:55:32 +08:00
0b9f34a5bf Fix formatting 2017-12-12 22:39:27 +08:00
757c32f6c4 Update unsupported features 2017-12-12 22:32:20 +08:00
09bab18b48 Fix flow error 2017-12-12 22:32:12 +08:00
7e53b195ea Add no-response bot config 2017-12-12 22:28:27 +08:00
ab966ff311 Fix formatting 2017-12-12 22:23:48 +08:00
7bb4a6f08f Fix compiled code using symbols 2017-12-12 22:18:15 +08:00
f50da9718f Fix NaN color stop in IE 2017-12-12 22:08:46 +08:00
3965a0fd40 Fix backgroundColor option documentation (Fix #1164) 2017-12-12 21:23:53 +08:00
77d258f1d8 Fix rendering with multiple fonts defined (Fix #796) 2017-12-12 21:08:19 +08:00
261702a693 Merge branch 'vnmc-feature/HTC-0009_RadialGradients' 2017-12-12 20:58:07 +08:00
cacb9f64e4 Radial gradient support 2017-12-12 20:57:48 +08:00
8ef3861a5c added support for radial gradients 2017-12-12 20:16:04 +08:00
0b74e69611 Update website demo 2017-12-12 19:43:59 +08:00
c272b2e122 Remove reftest results 2017-12-11 21:38:16 +08:00
2d132b85c6 Add gzipped package size to website (Fix #992) 2017-12-11 21:20:14 +08:00
50608e9cd4 Fix external SVG loading with proxies (#802) 2017-12-11 20:51:39 +08:00
d87fef11a4 Fix logging option (#1302) 2017-12-11 20:23:43 +08:00
250208dc99 Add support for rendering webgl canvas content (#646) 2017-12-11 20:17:20 +08:00
2237e8e230 Update wip website 2017-12-10 22:28:34 +08:00
d3c640088c Merge branch 'feature/HTC-0008_LinearGradients' of git://github.com/vnmc/html2canvas into vnmc-feature/HTC-0008_LinearGradients 2017-12-10 16:46:31 +08:00
8fd616aed2 Update wip website 2017-12-10 16:43:34 +08:00
d1e870de88 added support for gradient background size and fixed linear gradient angle when vendor prefix is used 2017-12-09 23:07:27 +01:00
9bc0fb0bd1 Fix __DEV__ value for minified build 2017-12-09 18:00:45 +08:00
d83bc0247a Disable foreignObjectRendering by default (#1295) 2017-12-09 17:51:28 +08:00
13e80cc635 Begin implementing new website 2017-12-09 17:47:25 +08:00
b239937e00 Refactor Font.js 2017-12-09 17:46:32 +08:00
e8a4d775e8 Update docs 2017-12-09 17:46:07 +08:00
a6a3c1bd0f Fix tests and refactor background calculations out from Renderer 2017-12-09 17:45:58 +08:00
850338a76a added support for background-origin: content-box, fixed background-origin related background sizes 2017-12-09 00:12:29 +01:00
63377d47a4 Begin adding documentation 2017-12-07 23:50:30 +08:00
1d1c74a74e Add Github issue templates 2017-12-07 17:30:13 +08:00
ef5c59e26d Fix scroll positions for CanvasRenderer (Fix #1259) 2017-12-07 16:36:09 +08:00
8b653f89bc Prevent generated iframe from getting re-rendered 2017-12-07 16:16:22 +08:00
9db8580b97 Fix data-html2canvas-ignore attribute (Fix #1253) 2017-12-07 16:12:39 +08:00
4a09264103 Update CHANGELOG 2017-12-07 15:47:53 +08:00
b8178e92b4 Add deprecation warning for onrendered (Fix #1290) 2017-12-07 15:47:42 +08:00
166cbba7c2 Fix decimal letter spacing values (#1293) 2017-12-07 15:20:24 +08:00
0c8d38d9c0 Fix underlines, relative to 'bottom' baseline 2017-12-06 23:49:11 +11:00
2c8c604f9a Update safari tests to use v10.1 2017-12-04 19:35:34 +08:00
a967475826 Disable beta firefox tests temporarily 2017-12-03 22:37:56 +08:00
158435761f Fix package version for npm build 2017-12-03 18:22:46 +08:00
0e8a924ea2 Fix formatting 2017-12-03 17:30:52 +08:00
0b4f922405 Add Github release to travis build 2017-12-03 17:24:31 +08:00
7e0b2b5201 Add npm and minified builds 2017-12-03 17:07:10 +08:00
9445b0b598 Inline fonts for ForeignObjectRenderer 2017-10-18 20:34:17 +08:00
f16d581f04 Add foreignObjectRendering option 2017-09-28 19:46:00 +08:00
53dd885279 Implementing cropping and dimension options for rendering (Fix #1230) 2017-09-27 22:14:50 +08:00
ae47d901a1 Add no console.log lint rule 2017-09-25 23:01:59 +08:00
929b9de6e0 Implement proxied cross-origin iframe rendering 2017-09-25 22:53:09 +08:00
57dc7137b2 Fix wrong context for cloned element instanceof check 2017-09-24 15:26:37 +08:00
90a8422938 Implement Iframe rendering 2017-09-11 22:36:23 +08:00
aa47a3a3a6 Add support for loading cross origin images using proxy 2017-09-04 23:36:19 +08:00
c2b7ed9c42 Update link for releases in README (Fix #1203) 2017-09-04 20:36:54 +08:00
0f9f8ff494 Add dev server script 2017-09-03 21:25:06 +08:00
1a53643457 Improve logging 2017-09-03 21:24:17 +08:00
906a66eec7 Use crossOrigin images when useCORS option set 2017-09-03 21:24:06 +08:00
c013e49192 Only use foreignObject rendering if browser is capable of rendering images 2017-09-03 18:20:12 +08:00
c28263ddc2 Update README with build instruction (#1196) 2017-08-30 22:23:26 +08:00
6d9639d0af Use prefixed transform values 2017-08-30 22:20:55 +08:00
badbf52c1c Reduce test output size 2017-08-30 22:20:42 +08:00
23b6f29ecf Capture screenshots while running karma tests 2017-08-30 21:31:51 +08:00
a41ba8852f Set default config as empty object (Fix #1195) 2017-08-30 20:59:18 +08:00
b75fd70042 Add screenshot collecting server 2017-08-28 21:27:39 +08:00
5dbb197a82 Remove redundant style nodes from clone 2017-08-18 22:22:24 +08:00
bd463d9343 Correctly apply image timeout for loading images 2017-08-18 21:53:42 +08:00
c093c95881 Correctly resolve unsupported svg image testing 2017-08-18 21:34:05 +08:00
f79ae2b73a Don't copy styles for regular computed rendering 2017-08-18 20:52:29 +08:00
4f96abfb7b Handle inline svg correctly with foreignObject 2017-08-17 23:32:03 +08:00
26d8d8ea5b Remove reftests for now 2017-08-17 23:30:00 +08:00
a73dbf8067 Implement foreignObject renderer 2017-08-17 23:14:44 +08:00
26cdc0441b Fix formatting 2017-08-16 19:54:48 +08:00
fa4a4a4db5 Add saucelabs browsers 2017-08-16 19:50:14 +08:00
d77301a353 Fix base64 images for ios 10.3 2017-08-16 19:50:05 +08:00
8999c76181 Fix iOS 10.3 base64 image tainting canvas (Fix #1151) 2017-08-13 23:27:03 +08:00
fd1447a6e7 Add sauceconnect launcher 2017-08-13 23:24:39 +08:00
ea080e0f5d Fix recursion for safari 10 on pseudoelements 2017-08-13 21:39:57 +08:00
a101b52685 Render SVG nodes correctly 2017-08-13 17:48:37 +08:00
ae46010476 Merge pull request #1186 from niklasvh/travis-karma
Travis karma tests
2017-08-13 14:41:27 +08:00
05e5d932f0 Add IE and Edge browsers to saucelabs tests 2017-08-13 14:27:30 +08:00
068480f606 Add saucelabs to karma runner 2017-08-13 14:09:43 +08:00
c9fb5d5026 Add Chrome reftests to Travis 2017-08-13 14:02:48 +08:00
f3d6d2fdf4 Don't resolve images immediately if they appear complete 2017-08-13 14:02:35 +08:00
37a9249a4a Don't render SVG nodes if it taints canvas 2017-08-13 13:11:03 +08:00
c765e2042f Don't render 0 sized images 2017-08-13 12:16:48 +08:00
d327327166 Fix pseudoelement image content not being loaded in time 2017-08-11 23:21:28 +08:00
96cde64c6e Normalize more tests 2017-08-11 22:22:39 +08:00
31a9f913ed Allow tests to be ignored by specific browsers 2017-08-11 22:22:10 +08:00
af09280c38 Draw checkboxes as vector path 2017-08-11 22:21:23 +08:00
a1b8cbc2fb Remove profiler 2017-08-11 20:41:05 +08:00
18761b7352 Fix textShadow parsing 2017-08-11 20:40:49 +08:00
ad487f4585 Set overflow hidden for input elements 2017-08-11 20:40:37 +08:00
fb58d1f0b6 Fix incorrect value 2017-08-11 20:40:08 +08:00
42a87b8354 Normalize reftests 2017-08-10 23:26:22 +08:00
97b0a1f21d Fix reftest precision 2017-08-10 23:26:01 +08:00
5bd06895e9 Begin implementing webkit-gradient parsing 2017-08-10 23:25:50 +08:00
eb380f023f Fix zIndex value 2017-08-10 23:24:38 +08:00
1c318ab607 Add ignored reftests 2017-08-10 23:24:26 +08:00
8f575a446d Don't toggle canvas on right-mouse click 2017-08-10 21:59:26 +08:00
5969c95481 Set line number for text renders 2017-08-10 21:59:08 +08:00
82cfcf8704 Round reftest repeat values 2017-08-10 21:58:56 +08:00
c287f51cb6 Fix incorrect render order in Firefox for position: static an z-index value 2017-08-10 21:58:36 +08:00
edebe082f3 Remove animations from reftests 2017-08-09 12:05:16 +08:00
77393074ba Use tree order when z-index is the same 2017-08-09 11:52:42 +08:00
ed92d2354c Fix build 2017-08-09 11:10:40 +08:00
58d1bef3b6 Beging implementing reftests 2017-08-09 00:52:56 +08:00
93f08c7547 Implement RefTestRenderer 2017-08-07 00:26:09 +08:00
a2895691ba Extract render target logic out of renderer to be target agnostic 2017-08-06 20:21:35 +08:00
f7f445c71e Add license info to builds (Fix #1126) 2017-08-06 18:14:27 +08:00
8da77eb689 Add options to define window dimensions 2017-08-06 17:53:38 +08:00
965f850e68 Assign cssText string to same named property on target 2017-08-06 17:50:01 +08:00
216c290c4b Check availability of console before using it (Fix IE9) 2017-08-06 17:37:34 +08:00
68900c3087 Copy CSS properties individual with IE 2017-08-06 17:37:10 +08:00
018ed765ad Update CHANGELOG with changes for v1.0.0-alpha1 2017-08-06 16:05:36 +08:00
f0fdeac703 Fix formatting 2017-08-06 15:52:56 +08:00
6baa847092 Handle undefined values for textShadow/transform (IE9) 2017-08-06 15:44:30 +08:00
10ec079762 Remove direct console call (breaks IE9 when console is not open) 2017-08-06 15:43:53 +08:00
6554d4c8c8 Implement textShadow rendering (Fix #499 and #908) 2017-08-05 23:34:12 +08:00
30a2578f38 Fix pseudo-element css text assignment for Edge 2017-08-05 21:59:48 +08:00
82a7349e43 Use first background repeat value for multiple backgrounds if only 1 available (Edge bug) 2017-08-05 21:41:37 +08:00
0224592a96 Don't parse TEXTAREA child nodes (Edge bug) 2017-08-05 21:40:22 +08:00
12672839f1 Use correct JS context to find cloned element in Edge 2017-08-05 21:40:03 +08:00
9bdb871307 Implement linear-gradient rendering 2017-08-05 21:13:53 +08:00
56b3b6df27 Implement input/textarea/select element rendering 2017-08-05 00:00:17 +08:00
adb1f50f00 Support percentage border-radius values (Fix #1154) 2017-08-04 20:41:36 +08:00
e380e2c873 Use correct JS context to enable use of instanceof 2017-08-04 19:27:35 +08:00
3977ebeadd Log errors in __DEV__ mode (Fix #905) 2017-08-04 00:13:20 +08:00
9a7075252b Fix ImageLoader flow types to reflect possible error'd images 2017-08-04 00:00:02 +08:00
96fbe954e9 Correctly strip quotes from pseudoelement url() 2017-08-03 23:54:44 +08:00
b8450f4d4a Allow image loads to fail without crashing render 2017-08-03 23:54:23 +08:00
b3db735415 Render :before and :after pseudoelements 2017-08-03 23:46:29 +08:00
959b75a441 Update travis build 2017-08-03 22:03:05 +08:00
f6a5153d99 Implement support for multiple text-transforms with independent colors 2017-08-03 21:47:35 +08:00
ad1119a76c Apply border radius correctly on image elements 2017-08-03 21:05:17 +08:00
fe97851988 Implement HTMLCanvasElement rendering 2017-08-03 20:57:55 +08:00
f2b8c16c2c Implement visibility css prop 2017-08-03 20:28:39 +08:00
f278ba4f22 Begin implementing overflow clipping 2017-08-02 21:35:54 +08:00
52a815a13f Fix background-clip and background-origin rendering 2017-08-02 20:29:45 +08:00
213f35f61c Keep scroll position when toggling canvas view 2017-08-02 20:15:39 +08:00
7cc2b856cb Use correct canvas size for full document render 2017-08-01 23:41:12 +08:00
aafb0cfb9c Calculate correct bounds for text/elements under nested transforms 2017-08-01 23:27:12 +08:00
c5135f4839 Assign default options values 2017-08-01 22:51:59 +08:00
478155af64 Clone document before parsing it 2017-08-01 20:54:18 +08:00
7a3bad2fcb Add missing Flow tags 2017-08-01 18:51:59 +08:00
4f48bc9b0c Define browser build target as umd 2017-08-01 18:32:37 +08:00
6f7a3145fe Remove dist folder from git 2017-08-01 18:26:11 +08:00
ba089b4771 Render multiple backgrounds in correct order 2017-08-01 18:25:32 +08:00
9f8bae4b09 Correctly parse multi background-repeat values 2017-08-01 18:25:20 +08:00
f89ba365bd Fix README typos (#779 and #930) 2017-08-01 18:14:37 +08:00
50579f399e Update README and Fix #1140 2017-08-01 00:31:41 +08:00
8a6fb5f733 Library rewrite 2017-08-01 00:25:58 +08:00
83e9b85e1e 0.5.0-beta4 2016-01-23 22:21:21 +02:00
3c7c3ddf60 Update changelog 2016-01-23 22:20:02 +02:00
2ef53b37cc Remove safari browser tests 2016-01-23 22:07:42 +02:00
3b49cba21c Fix rendering of content when window is scrolled 2016-01-23 22:05:43 +02:00
a4aa0c6444 Derequire browserify bundles 2016-01-23 20:53:20 +02:00
4ebe9c5fcc Don't require logger to be exposed to window object 2016-01-23 20:41:53 +02:00
e17bbacd17 0.5.0-beta3 2015-12-06 13:48:42 -05:00
47a7240d6b Re-adding dist/ to version control for now 2015-12-06 13:46:23 -05:00
6539f9d9c3 Fix webdriver tests for node 4.0 2015-12-06 13:32:35 -05:00
3cb0911de3 Update dependencies 2015-12-06 12:01:18 -05:00
144c9a903e Merge pull request #724 from usmonster/adds-editorconfig
Adds .editorconfig
2015-11-11 20:40:21 +02:00
57dd9b5461 Adds .editorconfig
Makes contributing consistently-formatted code a bit easier
2015-11-11 13:21:29 -05:00
6d168f46be Merge pull request #708 from usmonster/fix-firefox-gradients
Linear gradients now parse color names
2015-11-09 19:14:46 +02:00
318ca48157 Linear gradients now parse color names
Also:
- Cleans up color stop and linear gradient regular expressions.
- Handles percentage-based linear gradient positions (fixes Firefox).

Fixes niklasvh/html2canvas#469.
2015-10-25 09:44:14 -04:00
bebb353b3f Merge pull request #703 from niklasvh/travis-fix
Lower travis nodejs version back to 0.10
2015-10-20 23:37:35 +03:00
eb5ac1122c Lower travis nodejs version back to 0.10 2015-10-20 23:16:49 +03:00
ae97dd9a3d Remove npm bower dependency 2015-10-19 23:13:05 +03:00
11fdc501b1 Fix package dependency versions 2015-10-19 02:57:40 +03:00
4df19968b5 0.5.0-beta2 2015-10-19 01:49:34 +03:00
9ab7f8cdb1 Update travis npm deployment 2015-10-19 01:44:08 +03:00
5c5531fd47 Update node version for travis 2015-10-19 01:34:38 +03:00
e88ac871a3 Remove Promise polyfill 2015-10-19 01:25:03 +03:00
2a2ad9bb65 0.5.0-beta1 2015-10-19 01:00:58 +03:00
81e60975cc Update build/deploy workflow 2015-10-19 01:00:04 +03:00
a0669300c4 Update grunt-mocha-phantomjs to 2.0.0 2015-10-18 23:58:10 +03:00
ba9758cf14 Reformat mocha text shadow test names 2015-10-18 23:57:55 +03:00
aa05241ff8 Fix gradient tests 2015-10-18 23:57:41 +03:00
5b4a6c26ee Add another border-radius test 2015-10-18 23:15:51 +03:00
364a8aac1c Merge remote-tracking branch 'origin/pr/645' 2015-10-18 23:15:12 +03:00
46078acf71 Fix #688 fatal exception on unmatched color stop 2015-10-18 16:51:53 +03:00
4b37909f09 Fix support for requirejs/amd 2015-08-30 02:27:38 +03:00
90f9eeba83 Fix #599 2015-06-10 16:13:17 +09:00
98ee30643a Fix iOS scrolling issue on clone 2015-03-30 00:58:50 +03:00
a49c3a2320 Correctly handle named colors in gradients 2015-03-29 23:20:17 +03:00
4b80102e77 Merge pull request #545 from cornedor/ie-transforms
Transform matrix for IE
2015-03-08 01:03:22 +02:00
9201cf7e95 Accept matrix3d and convert it 2015-03-05 15:52:23 +01:00
c2baf42145 Fix fail on transparent colors in linear gradients 2015-03-01 17:43:18 +02:00
d9a9615ed7 Add acid2 test case 2015-03-01 17:12:09 +02:00
585a96a918 Preserve scrolling positions of nodes on clone (#511) 2015-02-28 16:51:52 +02:00
9a0d43d60b Bump v0.5.0-alpha2 2015-02-03 22:11:20 +02:00
3671de81f9 Fix module name casing 2015-02-03 21:39:57 +02:00
f3b6df267e Switch build to use browserify (#502) 2015-02-03 21:34:05 +02:00
60619dca72 Fix #517 2015-01-26 22:55:10 +02:00
ed299b3db1 Remove dead code 2015-01-20 19:51:52 +02:00
c7e484af89 Add npm bower postpublish script 2015-01-19 23:52:50 +02:00
19ecd8bd7f Remove version from bower.json 2015-01-19 23:39:18 +02:00
edb113c230 Make html2canvas requireable in nodejs 2015-01-19 23:33:29 +02:00
33cd2c5ef6 Move Changelog away from Readme 2015-01-19 22:52:04 +02:00
3400354d78 Remove old test results 2015-01-19 22:51:45 +02:00
fc01263f68 Add comment regarding IE9 clone node 2015-01-19 22:30:10 +02:00
399ae9f33d Fix #503 2015-01-19 22:28:10 +02:00
db88784655 Add test for adding class prior to rendering (#508) 2015-01-19 20:31:31 +02:00
77c73c478f Fix iframe proxy 2015-01-18 21:38:05 +02:00
37b39ec716 Add task for running mocha tests with webdriver for testing proxies 2015-01-18 14:31:53 +02:00
6d53461a68 Update bluebird to 2.7.1 2015-01-17 22:12:48 +02:00
33844129e9 Update .gitignore 2015-01-17 22:12:28 +02:00
0df71b34f3 Add travis notification hook for gitter 2015-01-13 23:18:58 +02:00
3996449e88 Add gitter badge to README 2015-01-13 23:13:00 +02:00
2699670bf1 Improve text-shadow tests 2015-01-13 22:50:57 +02:00
7a58ad019f Fix background rendering regression #496 2015-01-10 20:53:06 +02:00
9b372a4399 Correctly clone <select> and <textarea> value property 2015-01-05 22:58:36 +02:00
498527918c Correctly render input[type="password"] value 2015-01-05 21:36:53 +02:00
e363f3813e Rename mocha/index.html to mocha/parsing.html 2015-01-05 21:22:58 +02:00
433dc98177 Reduce render count for multiple render mocha test 2015-01-05 20:54:07 +02:00
b6a73b635a Merge pull request #495 from usmonster/update-promise-polyfill
Temporarily add manual update to es6-promise polyfill
2014-12-29 21:35:22 +02:00
e2713cd6f9 Temporarily add manual update to es6-promise polyfill
This replaces an old and outdated Promise polyfill that was included
with previous versions of html2canvas, and which consistently overwrote
native browser implementations when it shouldn't have. It is essentially
a (slightly modified) paste of the most current minified version of
es6-promise, found here:
https://es6-promises.s3.amazonaws.com/es6-promise-2.0.1.min.js

The plan is to eventually include this polyfill as a submodule
dependency, but the build process for es6-promise is a bit
cumbersome/opaque/painful at the moment, so this manual update was an
easier interim solution.

Please note that I had to manually add the actual call to
`ES6Promise.polyfill()` to load the polyfill--this will become
unnecessarry in future versions of es6-promise. Note also that I had to
manually pass `window` to the polyfill's IIFE, since `this` is an empty
object in the current scope.

Ref niklasvh/html2canvas#494.
2014-12-29 16:35:39 +01:00
4b771d558a Increase mocha timeout for multiple-render tests 2014-12-24 17:45:04 +02:00
2f5f9f6e59 Allow multiple renders to occur simultaneously 2014-12-24 17:34:31 +02:00
60368d4670 Update build instructions with grunt and uglifyjs details 2014-12-24 17:24:07 +02:00
f5e318d968 Manually call toString for colors 2014-12-13 19:23:00 +02:00
612e59c3d3 Implement border-inset color transforms 2014-12-13 19:00:29 +02:00
fcbcb9bfaa Add color object to accept array of rgb(a) 2014-12-13 18:30:52 +02:00
3b0352a3d7 Use Color objects for colors 2014-12-13 18:10:41 +02:00
313c227a1f Add color parsing 2014-12-13 17:24:54 +02:00
893ce74a33 Implement checkbox and radio input element rendering 2014-12-06 18:17:04 +02:00
e4329c4d37 Fix phantomjs scrolling test failure 2014-12-06 16:19:38 +02:00
069140974b Don't scroll owner document after cloned document load 2014-11-30 14:23:52 +02:00
1e826e32ae Fix scroll to top with pages using hashtag anchors 2014-11-30 14:16:02 +02:00
0579804cbb Merge pull request #478 from ClickWithMeNow/master
add classname to html2canvas iframe
2014-11-21 00:31:01 +02:00
f15666e156 adding submodules 2014-11-20 14:31:21 -06:00
d4cb7e8868 add html2canvas-container classname to iframe 2014-11-20 14:27:34 -06:00
657eb983cf Add options.background option 2014-11-04 21:53:26 +02:00
fc800bff9d Partial fix for borders 2014-11-04 21:40:09 +02:00
36648afc3d Add test case for border-style: inset 2014-11-03 23:49:39 +02:00
aa3aafbc0c Fix race condition for content load 2014-11-03 23:29:57 +02:00
525b5c4f36 Fix font rendering for IE with multiple inherited fonts 2014-11-02 22:15:48 +02:00
706b2abb19 Update readme with information regarding submodules 2014-11-02 20:58:01 +02:00
6b1d116a5e Add build warning regarding missing source files 2014-11-02 20:57:48 +02:00
772767d0d9 Disable transitions for cloned page 2014-11-02 20:26:25 +02:00
7ebd191488 Fix form placeholder/value positioning for scrolled pages 2014-11-02 19:18:39 +02:00
6ece2a3d5a Fix svg matching performance issue 2014-11-02 18:54:39 +02:00
1793b802b1 Add onclone callback option to allow modifying cloned document prior to rendering 2014-10-26 19:10:09 +02:00
8184b7cf51 Refactor test 2014-10-19 22:38:08 +03:00
04bdb48cba Fix layer ordering with multiple stacks on same auto z-index 2014-10-19 22:29:34 +03:00
7e13231807 Fix canvas cropping with type: 'view' 2014-10-15 20:28:26 +03:00
199685ebd1 Disable animations on rendered page 2014-10-12 20:44:39 +03:00
444869e3ca Implement css clipping for rect() (with pixel values) 2014-10-12 20:32:47 +03:00
717f69d99a Don't remove frame container in tests 2014-10-12 20:32:10 +03:00
541f6a62d8 Expand clip test case 2014-10-12 20:32:00 +03:00
d8dcbdedd8 Add test case for css clip 2014-10-07 20:11:46 +03:00
5712b621ca Correctly clip content that has border-radius (#399) 2014-10-07 19:11:24 +03:00
6073928978 Fix origin check for IE (#460) 2014-10-07 18:59:26 +03:00
e103ad8219 Fix pseudoelement rendering (with nth-child selectors etc.) 2014-10-06 22:46:43 +03:00
d5430070a2 Ignore exports and module assignments within scope 2014-09-29 19:33:01 +03:00
f3d45e005e Don't leak punycode to global scope 2014-09-29 19:15:50 +03:00
b60b4b2a45 Add option to use existing canvas for rendering 2014-09-28 22:51:11 +03:00
bd1abe1857 Provide fallbacks for html parsing 2014-09-28 00:11:54 +03:00
8a3d1d7f22 Fix firefox cross-origin iframe rendering 2014-09-27 23:03:57 +03:00
e6d31ada4a Don't load javascript on cloned content by default 2014-09-27 21:20:08 +03:00
19777c6623 Add simpler api for rendering external urls 2014-09-27 21:07:25 +03:00
33d84c82b0 Add simpler api for rendering external urls 2014-09-27 20:37:12 +03:00
1e19832171 Add google maps test 2014-09-27 20:20:08 +03:00
fa659ad1df Fix background-size with background-repeat x and y as well (#447) 2014-09-27 18:03:18 +03:00
f517a35781 Clone and render canvas content correctly 2014-09-27 18:00:14 +03:00
3f3424e49c Fix: Render background-size correctly with no-repeat #447 2014-09-27 17:02:46 +03:00
1d8a316f13 Correctly handle overflow content 2014-09-27 16:54:53 +03:00
b1f948bb60 Fix z-index stacking with fixed position 2014-09-21 11:44:11 +03:00
24d9a22556 Correctly apply canvas background color 2014-09-21 11:24:20 +03:00
7ee2f411b0 Continue render with failed svg images 2014-09-20 21:54:03 +03:00
9406f76a18 Fix test retry logic 2014-09-20 21:09:18 +03:00
6092629679 Add proxy server to travis task 2014-09-20 20:54:43 +03:00
349d0a300c Fix tests to use local proxy 2014-09-20 20:25:48 +03:00
0a7df6d9b9 Correctly use native rendering with inline <svg> nodes if possible 2014-09-20 20:21:25 +03:00
70241a789d Fallback to jsonp proxy if browser doesn't support cors xhr/image 2014-09-20 19:53:22 +03:00
075c836d16 Fix missing jsonp feature with grunt-contrib-connect 2014-09-20 18:25:28 +03:00
c9993a7237 Revert back to grunt-contrib-connect 2014-09-20 18:18:39 +03:00
3b8d4dece2 Use correct window size for cloned iframe 2014-09-20 18:01:57 +03:00
440120b087 Merge branch 'ddrscott-fix-doc-measurements' 2014-09-20 17:40:27 +03:00
e80fe312ee Merge branch 'fix-doc-measurements' of https://github.com/ddrscott/html2canvas into ddrscott-fix-doc-measurements
Conflicts:
	dist/html2canvas.js
	dist/html2canvas.min.js
	src/core.js
2014-09-20 17:40:09 +03:00
b141c9f0d1 Begin implementing cross-origin iframes 2014-09-20 17:36:15 +03:00
3a3a61e316 Fix tests 2014-09-18 22:25:37 +03:00
6c08c3fa04 Simplify webdriver tests 2014-09-17 00:11:47 +03:00
d4c9a41873 Add preliminary support for same-origin iframes 2014-09-16 20:12:13 +03:00
6347e7f043 Use correct document context for canvas render 2014-09-14 20:14:38 +03:00
9220eb6def Update examples to v0.5 api 2014-09-14 20:04:37 +03:00
af965cc3a6 Add fontawesome test 2014-09-14 19:45:58 +03:00
9d088fa431 Use punycode to parse unicode characters correctly 2014-09-14 19:32:26 +03:00
b8d3688c29 upd 2014-09-14 18:55:14 +03:00
9907149513 Don't parse/render input hidden nodes 2014-09-14 17:44:55 +03:00
645fcd60b3 Ensure webdriver session is closed before exiting 2014-09-09 19:54:13 +03:00
0325a9b836 Add atob polyfill for ie9 for SVG base64 rendering 2014-09-09 18:42:00 +03:00
36052c2765 Add support for inline, base64 and node svg's 2014-09-08 23:44:10 +03:00
281efd2d5e Use correct build with ci and default grunt tasks 2014-09-08 23:43:00 +03:00
44b958beaf Default to element dimension if image size cannot be determined (vector images) 2014-09-08 21:25:21 +03:00
2a020e5a21 Allow tainting images to be drawn if option enabled 2014-09-08 21:24:26 +03:00
c20e679f2c Prefer native svg rendering if available 2014-09-08 21:16:30 +03:00
52c669fe5b Merge branch 'master' of https://github.com/niklasvh/html2canvas
Conflicts:
	build/html2canvas.min.js
	dist/html2canvas.js
	src/core.js
2014-09-04 16:24:23 -05:00
382c16a522 use cloned document for measurements 2014-09-04 14:02:44 -05:00
ba9d5201cf Add svg rendering with fabric.js 2014-09-04 20:50:31 +03:00
2e2d722e3d Fix rendering for safari 6 2014-09-04 19:29:42 +03:00
b9edf0b1c5 Add indentation rule to jshintrc 2014-09-04 18:47:17 +03:00
07f793b0ed Preliminary support for svg rendering 2014-09-04 18:47:06 +03:00
180b624cb3 reformat testing 2014-09-04 18:46:17 +03:00
b2280bc8ec rename build folder to dist 2014-09-01 20:22:56 +03:00
f3f92ab425 Fix travis segfault 2014-08-27 21:35:48 +03:00
503add6e2f Refactor webdriver test running 2014-08-26 21:03:54 +03:00
d2bfb810d4 Update webdriver configs to GruntFile 2014-08-26 20:01:53 +03:00
649cdd4e37 Update readme regarding 0.5 2014-08-25 18:23:18 +03:00
73a34493ac Queue webdriver tests 2014-05-18 23:31:49 +03:00
ce1c4c84f5 Fix CSS gradients fail to render when non-vendor prefix property is included #388 2014-05-18 23:20:45 +03:00
44beaf2989 Update webdriver browser versions to test 2014-05-18 21:37:56 +03:00
d716210509 Update travis webdriver settings 2014-05-18 21:17:59 +03:00
be5d1f8665 Don't use proxy for cors test 2014-05-18 21:17:46 +03:00
340b125b19 Replace saucelabs travis script with sauce_connect addon 2014-05-18 20:31:53 +03:00
ad1f0d418c Add travis env parameters to webdriver tests 2014-05-18 20:25:51 +03:00
08373f0bd4 Update travis webdriver host details 2014-05-18 19:28:50 +03:00
6959058560 Fix linear gradient rendering 2014-05-18 19:14:22 +03:00
d6ed6c0194 Fallback to DummyImageContainer if no suitable ImageContainer found 2014-05-18 17:48:32 +03:00
9ee87339a3 Fix cors loading of images 2014-05-18 17:40:01 +03:00
b7595e19e9 Fix webdriver tests 2014-05-18 17:39:24 +03:00
281e6bbedf Use backgroundColor from documentElement if rendered element lacks backgroundColor 2014-04-06 21:43:20 +03:00
fee91055b2 Fix bug with iframe not being loaded in-time 2014-03-24 19:05:32 +02:00
650ead63e5 Fix transform nesting 2014-03-15 14:30:26 +02:00
b35fcaeaf9 Move non-canvas supported fallback to seperate file 2014-03-15 13:22:55 +02:00
81c22866bc Don't fail on broken images 2014-03-15 13:20:05 +02:00
25d892f525 Fix IE9 https origin check bug 2014-03-15 13:01:04 +02:00
9db1ecfdfc Fix Qunit tests 2014-03-10 19:56:23 +02:00
12d85e3c04 Fail with a Promise.reject when no canvas support 2014-03-10 18:46:25 +02:00
3101f2007a Fix Firefox DOM clone not being ready 2014-03-05 22:24:53 +02:00
85b77ca49f Fix cropping bug 2014-03-05 19:19:24 +02:00
bb8c5a973b Limit crop to the maximum size of outputted canvas 2014-03-04 21:54:24 +02:00
0187fcab42 Fix internal method name for spec 2014-03-04 20:42:57 +02:00
cfe4137bcc Account for negative bounds in cropping 2014-03-04 20:42:34 +02:00
d2c3378c3e Correctly crop content 2014-03-03 21:19:28 +02:00
95f4bcea0a Render input elements correctly 2014-03-02 19:51:46 +02:00
9bae5b610a Fix IE misalignment of content after scroll 2014-03-02 18:03:30 +02:00
7ce46e95cd Fix logging in IE9 2014-03-02 16:55:40 +02:00
84c1dc6283 Remove script tags from cloned DOM 2014-03-02 16:03:22 +02:00
15ca3381eb Fix text rendering for IE and Opera 2014-03-02 16:00:59 +02:00
18d95d669b Partial implementation for transforms 2014-02-23 17:35:46 +02:00
5137e5f35a Render correct element 2014-02-23 17:02:49 +02:00
314d26f1f1 Hide iframe container from view during render 2014-02-23 16:26:23 +02:00
82e5a8a7c0 Fix image loading for cross-origin resources 2014-02-17 00:04:09 +02:00
9af96d3812 Fix render ordering of nodes that form fake stacking contexts 2014-02-15 00:33:09 +02:00
6f2a775841 Fix background position rendering 2014-02-15 00:31:48 +02:00
f2b662801e Add another z-index test 2014-02-15 00:30:38 +02:00
60587c72bf Fix unit tests 2014-02-15 00:30:28 +02:00
d9d516d27e Force doctype for iframe 2014-02-10 19:53:13 +02:00
899d5321d4 Fix pseudoelement rendering for Firefox 2014-02-08 17:42:40 +02:00
5d20493f46 Add support for pseudoelements 2014-02-08 16:52:41 +02:00
b5891c49b4 Correctly assign image loading method for <img> elements 2014-02-08 14:07:20 +02:00
467ff87482 Switch to using Promises 2014-02-03 19:42:42 +02:00
9beae48cf0 Start implementing background gradients 2014-02-01 21:48:30 +02:00
17731169e9 Add support for single text-shadow 2014-02-01 20:03:23 +02:00
e27c41efd3 Add font metrics and text-decorations 2014-02-01 19:36:43 +02:00
1f90defbfa Fix text rendering for Opera/IE 2014-02-01 19:11:59 +02:00
b4bb34c95b Move NodeParser to seperate file 2014-02-01 18:52:53 +02:00
64668fe694 ios window size and scroll fixes 2014-02-01 18:32:05 +02:00
9ebae161e2 Fix IE issues 2014-01-26 20:43:50 +02:00
729bc88d1f Increase logging 2014-01-26 20:43:36 +02:00
b1c2f03ae9 Add option to only render current view 2014-01-26 20:42:57 +02:00
74cb3466ec Draw <img> elements 2014-01-26 18:10:04 +02:00
2afdcaff35 Add Promise polyfill 2014-01-26 16:06:39 +02:00
1070cec852 Implement background rendering 2014-01-26 16:06:16 +02:00
ba9a33b1bc Add work around for chrome related background-image issues in iframe 2014-01-26 15:50:13 +02:00
f474542382 Begin implementing background-image rendering 2014-01-21 21:41:00 +02:00
0cb259f6cd Cache backgroundImages request for nodes 2014-01-20 22:42:58 +02:00
1a7f5732bf Filter nodes and render correct size correctly 2014-01-20 22:42:39 +02:00
2b8389cb64 Make image loading to work on top of Promises/polyfill 2014-01-19 21:05:07 +02:00
8b8c080841 0.5.0 rewrite 2014-01-19 18:04:27 +02:00
6201e09118 Merge branch 'patch-1' of github.com:bensho/html2canvas into bensho-patch-1 2013-12-23 16:33:58 +02:00
517fd8cd1d Refactor background parsing 2013-12-23 16:07:49 +02:00
e228fc57ce Merge branch 'patch-2' of github.com:brcontainer/html2canvas into brcontainer-patch-2 2013-12-23 15:32:18 +02:00
46cc8b6975 Fix z-index ordering bug 2013-12-23 15:27:46 +02:00
443fd17a12 Add all js files to grunt watch 2013-12-17 20:01:03 +02:00
0b213eecef Fix bug in WebKitGTK+, backgroundRepeat return "wrong" values 2013-12-06 09:49:37 -02:00
ae1a15f7c5 Update pseudoelements.html 2013-11-25 16:39:35 -05:00
cea3005056 Fix invalid selector exception with empty class values
After removing :before and :after pseudo selectors, a class name may be empty, causing an invalid selector string when joined. Remove empty elements before calling querySelectorAll.
2013-11-25 09:48:12 -05:00
1d4b1753d6 Merge pull request #297 from Grom-S/patch-1
incorect z-index sorting fixed
2013-11-24 03:32:57 -08:00
f00b23a9ec incorect z-index sorting fixed
If you omit compare function in javascript `sort()` method it will sort  lexicographically (in dictionary order) according to the string conversion of each element. 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters

For example 
[0, 9, 8, 10].sort()
will place element with z-index 10 on 2nd position of the array
2013-11-21 20:37:40 +02:00
e9afe03960 Implement background-size cover/contain 2013-11-12 19:35:28 +02:00
57d20a9794 Fix missing background color bug 2013-11-12 19:34:20 +02:00
35c5ca3340 Add tests for background-size 2013-11-12 19:33:56 +02:00
7cc7f80ee2 Add dom depth information to render queue 2013-10-27 22:08:12 +02:00
4c75d819a4 Merge pull request #270 from ssafejava/parseSpeed
Speed parsing by as much as 50% & add async parsing option.
2013-09-22 11:32:25 -07:00
838f91e156 Merge pull request #269 from ssafejava/jshint
Move jshint rules into jshintrc and include from Gruntfile.
2013-09-17 23:29:37 -07:00
8bea01b81d Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.

This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.

After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.

Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 13:38:21 +08:00
e115180731 Add async parsing option.
In my testing, the major time sink is parsing. This commit adds a setTimeout() around parsing
of each item so control can return to the browser. This increases the total time it takes to finish
a screenshot but will not freeze the browser when it does. This is a good option when e.g. doing
error reporting, where you might not want to freeze the browser while sending debugging information
back to your server.
2013-09-18 13:38:21 +08:00
e782efa614 Move jshint rules into jshintrc and include from Gruntfile. 2013-09-18 13:37:06 +08:00
806cd60474 Add links to readme 2013-09-07 22:07:27 +03:00
0515765788 Release version 0.4.1 2013-09-07 21:29:31 +03:00
0fd25f048d Add bower.json 2013-09-07 21:24:41 +03:00
14ff672c6e Disable screenshot storing 2013-09-07 21:10:06 +03:00
38fad5ac17 Update travis key 2013-09-07 00:10:53 +03:00
a31de83368 update token 2013-09-07 00:05:47 +03:00
1fb3b53fc0 update certificate 2013-09-06 23:57:47 +03:00
4d465116da update travis.yml key decrypt argument 2013-09-06 23:48:18 +03:00
74ce2c5062 update travis.yml secret 2013-09-06 23:26:35 +03:00
fbeb6e72f2 Update test certificate 2013-09-06 23:11:04 +03:00
c097f11ce3 Store webdriver screenshots to google drive 2013-09-06 22:30:24 +03:00
b6ebf2acf6 Remove unnecessary custom complete event checker 2013-09-04 20:17:38 +03:00
e9c3d9d332 Fix background color rendering layering with no documentElement color 2013-09-04 20:03:16 +03:00
c232da2595 Fix grunt watch build order 2013-09-04 20:02:20 +03:00
c759600c06 Refactoring 2013-09-04 19:29:12 +03:00
5f45968154 Merge pull request #260 from arrix/develop
negative z-index
2013-08-15 12:15:04 -07:00
feb2fd0a63 test case for body bgcolor and negative z-index #256 2013-08-16 00:26:40 +08:00
fb944d9381 test case for negative z-index below text 2013-08-15 23:31:48 +08:00
564634ba97 stacking context creator should be at the bottom
passes z-index12. issue #256
2013-08-15 23:24:01 +08:00
dd7468c446 use solid border in z-index tests 2013-08-15 22:31:32 +08:00
eb00650b02 fix text 2013-08-14 21:56:43 +03:00
1d03a5f9a4 z-index test case for negative z-indexes 2013-08-14 21:56:32 +03:00
ea7d6b485d preserve stacking nesting with special ancestor. issue #256 2013-08-15 02:12:38 +08:00
fd4fd95429 Test case for static position inside position relative 2013-08-13 19:59:54 +03:00
10b40821e5 first implementation for matrix transforms 2013-08-06 21:11:08 +03:00
518dd702a2 list all tests in index.html 2013-08-06 21:10:46 +03:00
056953f2c1 Fix cropping of canvas for float sized elements 2013-08-06 19:15:46 +03:00
9a57a08c72 Refactoring logging and gradients 2013-08-06 18:55:04 +03:00
26a81da2f0 Ignore transforms if non found 2013-08-06 18:20:20 +03:00
57028ab423 initial commit for transforms 2013-08-06 18:17:33 +03:00
c9e2fc27c8 Refactoring 2013-08-06 17:55:13 +03:00
2777a3e079 Refactoring 2013-08-06 17:46:47 +03:00
02ab96dc5f passes z-index8 non-positioned element with opactiy < 1
The MDN article Understanding_z_index/Stacking_and_float is wrong about this
2013-08-06 10:34:13 +08:00
65746bd2e3 coding optimizations 2013-08-06 10:01:20 +08:00
16d3bef255 z-index overhaul. relative above static; negative z-index
1. when stacking without z-index, positioned > floated > normal flow
2. supports negative z-index
3. new stacking context formed when opacity < 1 (standard)
4. new stacking context formed for position:fixed (no standard yet, done in mobile webkit and chrome 22+)
2013-08-06 03:36:12 +08:00
0277c34310 first transform test 2013-08-04 19:21:00 +03:00
f35ef0fe6f Refactor 2013-08-04 18:33:18 +03:00
2c8dd18d55 Fix build order 2013-08-04 18:33:02 +03:00
6b5f31eef0 Add tests for text shadow 2013-08-04 17:00:41 +03:00
832b9ee934 add grunt watch 2013-08-04 16:42:12 +03:00
73698e8ceb Update readme 2013-08-04 16:41:41 +03:00
37fbd3f90e Fix border rendering bug 2013-08-04 16:41:36 +03:00
5300f20b78 use element instead of element-array in examples 2013-08-04 16:03:47 +03:00
f0e234a1d8 Remove jQuery from examples 2013-08-04 15:58:42 +03:00
30163ab16f Update readme 2013-08-04 15:51:16 +03:00
407145da94 Update package.json 2013-08-04 15:50:54 +03:00
c5e6eaa849 Change iframe test url 2013-08-04 15:41:46 +03:00
2d39cd0719 fix shadow parsing for IE9 2013-08-04 15:41:00 +03:00
ebd7828dc8 Update sauce-connect logging 2013-08-04 15:14:34 +03:00
877367d499 Merge branch 'arrix-child_textnodes' into develop 2013-08-04 14:36:55 +03:00
fd888bde8d fixes #251. inline text in top element 2013-08-02 14:36:42 +08:00
7d2e12c3dd added test case for #251 inline text in top element 2013-08-02 10:11:07 +08:00
a7d3e9c2a2 Merge branch 'master' of git://github.com/fdn/html2canvas into fdn-master 2013-06-23 19:27:53 +03:00
f49e147b2f Added qunit tests for text-shadow 2013-06-18 23:47:08 -07:00
a902f92a14 remove svg rendering 2013-06-15 11:53:28 +03:00
e1573f8aed Parse out multiple text-shadow values and only honor the first one. 2013-06-12 16:48:23 -07:00
655779743b Better text-shadow parsing 2013-06-12 15:48:00 -07:00
1a30167f6a Basic implementation of text-shadow 2013-06-12 14:54:46 -07:00
2c58c56fbe add iframe test 2013-05-31 18:07:57 +03:00
288b851d05 revert image smooth disabling 2013-05-29 22:50:01 +03:00
0afb0fae0e disable image smoothing 2013-05-29 22:31:00 +03:00
a4702423cc adjust logging for results 2013-05-29 22:28:42 +03:00
f4aef61e5a update readme 2013-05-29 22:28:32 +03:00
cb210b2e61 delete .DS_Store 2013-05-29 22:14:50 +03:00
a80ef26c42 http->https 2013-05-29 22:13:22 +03:00
2580b48d16 compare results from db 2013-05-29 22:01:07 +03:00
87d7894d71 fix test result report posting 2013-05-29 21:16:28 +03:00
7842768707 encrypt api key only 2013-05-29 20:52:27 +03:00
403908c7da add logging for test reporting 2013-05-29 20:32:58 +03:00
8c8128b80a add test result storing 2013-05-29 20:12:37 +03:00
0d4b6ba665 setup travis config 2013-05-29 18:41:15 +03:00
b91fd9bc87 use sync-webdriver for selenium tests 2013-05-29 18:01:28 +03:00
62d27c20c3 fix typo 2013-05-21 23:23:56 +03:00
e811effe2a update download link 2013-05-21 23:23:31 +03:00
6156e28721 Merge pull request #214 from felfert/master
Fixed #207
2013-05-21 12:32:26 -07:00
2b000f0061 Fixed #207 2013-05-19 22:34:57 +02:00
843db27f72 Merge pull request #190 from cobexer/zIndex-1
added test for zIndex -1 element used as background
2013-04-15 07:44:47 -07:00
bbdfe8a035 added test for zIndex -1 element used as background 2013-04-15 13:08:54 +02:00
9da3bd7769 Merge pull request #189 from felfert/master
Fixed parsing of generic INPUT elements on IE9
2013-04-09 12:24:32 -07:00
85fa81ad95 - Fix parsing of input fields on IE9 2013-04-07 21:56:49 +02:00
a5d74bcfd9 Merge pull request #188 from felfert/master
Fetch images of current element only
2013-04-07 10:52:26 -07:00
cf735a9fa1 - Fixed rendering of ExtJS 4.2 windows. 2013-04-06 00:30:16 +02:00
9b051b8749 - Fetch images of current element only 2013-04-05 18:06:37 +02:00
822311ed0c Merge branch 'develop' of github.com:niklasvh/html2canvas into develop 2013-04-02 18:34:49 +03:00
c39225ceac add idea files to gitignore 2013-04-02 18:31:18 +03:00
763125ce6d Merge pull request #180 from djfarrelly/master
Upgraded to Grunt 0.4.0 and added Safari for Mac bug fix.
2013-03-24 07:52:45 -07:00
5ac4b42e33 Added reference to build shortcut on readme.md 2013-03-23 20:06:31 -04:00
cacb9a468f Modified markdown function. Added grunt build task.
Markdown format output currently commented out.
2013-03-23 20:00:01 -04:00
8623e4014b Upgrade to Grunt 0.4.0 complete. Safari on OSX bug fix. 2013-03-22 14:57:38 -04:00
2d95a761b0 Upgraded to Grunt 0.4.0. 2013-03-21 22:40:09 -04:00
b2df50a858 Allow element node to be passed 2013-01-30 20:12:25 +02:00
8ddf10fc04 Fix taint testing 2013-01-12 22:28:00 +02:00
410537456a Reordered grunt tasks 2013-01-12 19:01:24 +02:00
3aa7d69cc7 updated examples 2013-01-12 01:41:09 +02:00
7a3dd7572c updated readme 2013-01-12 01:34:00 +02:00
1b37c5d1ea Refactored preload 2013-01-11 23:01:29 +02:00
67850f2cee Refactoring 2013-01-11 22:50:46 +02:00
88dd1e41c0 Moved pseudoelement rendering to parse.js 2013-01-11 22:36:23 +02:00
554185ed4a pseudoelement tests 2013-01-11 22:33:40 +02:00
222dfa84b7 remove legacy code 2013-01-11 20:38:33 +02:00
9b0c32c62c Fix firefox pseudoelement bug for images 2013-01-11 20:36:07 +02:00
2bb926c7d0 Fix webkit-gradient() parsing 2013-01-11 20:31:34 +02:00
861a18f977 Fix Chrome background-repeat parse error 2013-01-11 19:51:53 +02:00
cf15c4a59f Updated readme 2013-01-11 19:39:35 +02:00
2f3f27b672 Fix crash for browsers that don't support placeholder attribute 2013-01-11 19:34:56 +02:00
3032dc6ce0 Fix empty content getting rendered on firefox/IE 2013-01-11 19:31:05 +02:00
2b0db917e3 don't process pseudo elements for hidden elements; cleanup pseudo elements after render 2013-01-11 10:46:53 -06:00
d73e53fbf0 Merge remote-tracking branch 'niklasvh/develop' into develop 2013-01-11 09:51:59 -06:00
6d29cc5df3 chinese word rendering 2013-01-04 23:47:59 +02:00
dc21fab450 missing braces 2013-01-03 17:10:57 -06:00
5492d80135 simply resize images to background-size 2013-01-03 17:09:23 -06:00
a313524aa4 refactored renderer 2013-01-03 23:15:06 +02:00
3edf9fa743 Merge remote-tracking branch 'niklasvh/develop' into develop
Conflicts:
	src/Parse.js
	src/Preload.js
2013-01-03 15:07:38 -06:00
7d7deca342 updated results 2013-01-03 22:30:52 +02:00
053a0a4787 switched background rendering to use patterns 2013-01-03 22:25:35 +02:00
65b4bdf282 background clipping support 2013-01-03 20:34:47 +02:00
56780565f4 refactored border radius calculation 2013-01-03 18:37:27 +02:00
55ed0ffde0 render placeholder text 2013-01-02 17:03:16 -06:00
7da4326885 background-size fixes
generated gradients need a unique key (the same value can generate a
different image based on background-size); fix so that a single value
specified for background-size yields a scaled height as the second
parameter
2013-01-02 14:51:03 -06:00
eb57b61859 backgroundPosition should use backgroundSize as a baseline for %'s 2013-01-02 13:45:58 -06:00
bb73d3c15e initial border-radius rendering 2013-01-02 21:26:24 +02:00
9b5ae9e191 support for pseudo elements 2013-01-02 12:58:48 -06:00
2557a83dbe remove todo comment 2013-01-02 10:51:47 -06:00
67ccb33dd5 add tests for resizeBounds 2013-01-02 10:47:36 -06:00
85706166cc trimText regexp needs /g 2013-01-02 10:40:56 -06:00
57d6003b65 backgroundSize needs trimmed before second split
also move trimText to Util
2013-01-02 10:39:00 -06:00
9ce03d6e86 working background-size 2013-01-02 10:10:03 -06:00
3d3f923ed8 implementing background-size; use parseBackgroundImage in render 2013-01-01 10:42:22 -06:00
42abcfe5fc added border radius test 2012-12-31 20:10:18 +02:00
473ff45267 Merge remote-tracking branch 'niklasvh/develop' into develop
Conflicts:
	src/Core.js
	src/Parse.js
	src/Util.js
2012-12-30 17:25:31 -06:00
496c8488bd Moved font metrics to seperate file 2012-12-30 21:26:25 +02:00
df0f436e66 initial support for backgroundSize 2012-12-30 12:30:48 -06:00
6ce619f0c0 refactor Preload 2012-12-30 12:20:35 -06:00
3774f3655c update test names 2012-12-30 11:22:47 -06:00
0c66766d55 replace definition property, w/ args[]
Also fix reference for preloader
2012-12-30 11:21:44 -06:00
ba9ace71ba improved text-decoration: overline accuracy 2012-12-30 16:46:31 +02:00
b19c200c6c renamed results to readme 2012-12-30 16:30:28 +02:00
29bd4c8c05 added result markdown creator 2012-12-30 16:29:01 +02:00
0dd2c24ab4 refactored parsing init 2012-12-30 16:06:59 +02:00
d93e36d768 Moved browser support checks to seperate file 2012-12-30 15:48:55 +02:00
1357057cbf refactored background rendering 2012-12-30 15:38:17 +02:00
74e93cbb93 refactoring backgroundimage rendering 2012-12-30 15:21:36 +02:00
45853a083c refactored text rendering 2012-12-30 04:15:51 +02:00
cb43e09899 refactor parsing 2012-12-30 01:39:37 +02:00
bb1cd21367 refactored background color rendering 2012-12-30 01:11:05 +02:00
5faa45847e refactored image rendering 2012-12-30 01:06:11 +02:00
0c2572b5ce refactoring 2012-12-30 00:27:38 +02:00
aa5b3d41c4 refactoring 2012-12-29 23:35:52 +02:00
516edbceea Improve background-repeat accuracy for non int positions 2012-12-29 23:05:49 +02:00
c72a02bf64 refactoring 2012-12-29 21:06:40 +02:00
c3e9636e4f Moved renderer logic to renderer.js 2012-12-29 17:43:15 +02:00
816ff6d3c5 updated testing instructions 2012-12-29 16:34:56 +02:00
5f1fedf8f0 added npm dependencies 2012-12-29 16:31:57 +02:00
a82234873e updated .gitignore 2012-12-29 16:04:50 +02:00
d1ee6e9d64 removed baseline files 2012-12-29 16:02:46 +02:00
e7b4dd17b9 updated readme 2012-12-29 15:31:24 +02:00
05f3af4901 webdriver grunt task 2012-12-29 15:02:05 +02:00
630bed968e baseline results 2012-12-29 15:01:48 +02:00
7c870a6fb8 add border tests 2012-12-29 14:57:06 +02:00
52033a5d72 Fixed jquery path 2012-12-29 14:34:01 +02:00
07e80df399 Fixed lint error 2012-12-29 14:33:30 +02:00
7e38df782c don't generate gradient on invisible elements 2012-12-28 16:52:48 -06:00
7f1cbc70a8 fully remove regex; simplify whitespace check
jshint complained about case statements w/o break (boo)
2012-12-28 16:26:39 -06:00
b81d7473e3 rewrite parseBackgroundImage to remove RegExp
RegExp couldn't handle parens inside parens:
linear-gradient(rgb(0,0,0),rgb(255,0,0))
2012-12-28 16:22:43 -06:00
d7bef66cc5 export html2canvas, pass value not def to backgroundImage 2012-12-28 15:49:38 -06:00
04782c1716 parse out prefixed methods 2012-12-28 12:54:58 -06:00
a4b7d04e80 move parseBackgroundImage to Util; add tests 2012-12-28 12:33:57 -06:00
62cb111956 style update 2012-12-28 12:24:49 -06:00
3171390f80 satisfy lint task 2012-12-28 12:12:47 -06:00
8fe61a43b0 initial support for multiple background images
Adding code from http://jsbin.com/ebigux/latest
2012-12-28 11:53:15 -06:00
f0ce6917fa updated license formatting 2012-12-28 19:46:23 +02:00
118b42eb7e remove .baseline files 2012-12-28 19:42:59 +02:00
3bbcfe36e0 Test result outputs 2012-12-28 19:41:14 +02:00
cdc7a744e3 Different quote styles 2012-12-28 18:06:44 +02:00
fb0e7ca29d removed external files 2012-12-27 23:57:06 +02:00
4e978c60cc formatted tests into smaller units 2012-12-27 23:53:27 +02:00
c9ed8d91fa updated renderer 2012-12-27 23:07:00 +02:00
582d10e00d webdriver testing 2012-12-27 23:06:47 +02:00
4684177df8 fix ie 9 bug 2012-12-27 23:06:35 +02:00
5198028c7b updated description 2012-12-22 16:29:43 +02:00
d6ddb7e29d Added automated testing with selenium 2012-12-22 16:28:34 +02:00
9df9426c91 update gitignore 2012-12-22 16:27:28 +02:00
76aa1e8feb moved examples 2012-11-25 23:48:08 +02:00
2841f19647 List refactoring 2012-11-25 23:36:28 +02:00
c6baabc99c Text rendering refactoring 2012-11-25 23:26:18 +02:00
44023015b6 Refactoring 2012-11-25 23:05:30 +02:00
bca6458301 Clean up 2012-11-25 22:39:09 +02:00
7c0b893564 Added support for "data-html2canvas-ignore" attribute 2012-11-25 22:25:08 +02:00
084bf4b039 Switched build process to use grunt 2012-11-25 20:59:31 +02:00
e83de7ae00 Merge pull request #130 from CyberShadow/opera-fixes
Opera performance fixes
2012-10-22 04:33:37 -07:00
1b81f7d517 Accept 0px as an acceptable letter-spacing value for per-word rendering
This improves performance in Opera.
2012-10-20 22:18:46 +03:00
3164e5bae0 Don't create zero-width text node after each render
This fixes rendering the same node getting slower each time
when using Opera.
2012-10-20 22:15:58 +03:00
81ae37cbd1 syntax error in example 2012-07-10 22:42:16 +03:00
730ebcfcaa border-radius parsing 2012-06-26 17:18:34 +03:00
cce6e3537c corrected border drawing with multiple colors
initial code for border-radius implemented
2012-06-26 15:15:46 +03:00
311a67ee22 added SVG 2012-06-26 01:55:46 +03:00
be143935cb added usage example 2012-06-26 01:48:41 +03:00
d6cb548a5c updated to version 0.34 2012-06-26 01:35:11 +03:00
1ba911912d fixed firefox iframe permission error 2012-06-26 01:30:45 +03:00
343b86705f removed last jQuery dependencies
should be fully jQuery free now!
2012-06-26 01:17:03 +03:00
9f76f94a82 fix jQuery noconflict 2012-06-13 13:56:01 +03:00
b02bb4d452 Merge pull request #100 from BugHerd/linting
Added a sublime-project file for Sublime Text 2 with linting and standards.
2012-05-28 23:38:20 -07:00
3bdba0617a Merge pull request #99 from BugHerd/makefile
Added a makefile to build html2canvas
2012-05-28 23:32:17 -07:00
1059314258 Added a sublime-project file for Sublime Text 2 that contains the detected linting and standards set. Allows Sublime Text 2 to automatically follow the standards and have live linting enabled. Trimmed useless whitespace. 2012-05-29 11:26:26 +10:00
331c057273 Added a makefile to build html2canvas 2012-05-29 11:18:01 +10:00
8d3a0c2b0d cache computed style 2012-05-24 14:10:40 +03:00
16022b81c3 Merge pull request #92 from bdkzero/master
Support for other renderers than Canvas and fixed image rendering in SVG renderer
2012-05-22 05:19:23 -07:00
17f4701ee5 Created new build target 'build-allrends'. Use this to build html2canvas including all renderers 2012-05-21 00:34:21 +02:00
00c3fb791c Readded /build/ directory to .gitignore and removed /build directory 2012-05-20 23:55:23 +02:00
98bc1f0833 Prebuilt distribution files 2012-05-08 10:59:14 +02:00
a5969be6f6 Fixed custom renderer option 2012-04-23 23:04:23 +02:00
de1d1d7087 Minor changes 2012-04-17 12:43:51 +02:00
d494b8dfbd Added direnv conf file to .gitignore 2012-04-17 11:09:09 +02:00
cfc45e4f6e Fixed image rendering in SVG renderer 2012-04-17 10:58:14 +02:00
8d965029da Fixed support for other Renderers other than Canvas 2012-04-17 10:57:25 +02:00
1ad7ed3e1c Merge pull request #77 from SunboX/background-gradients
ellipse gradient generation should now work
2012-03-14 03:38:05 -07:00
d7f4509253 Merge pull request #78 from cobexer/fix-undefined-refs-and-bookmarklet
fixed a few undefined references, fixed missing files in a few places
2012-03-14 03:37:05 -07:00
b47347d6b8 fixed a few undefined references, fixed missing files in a few places 2012-03-12 07:37:18 +01:00
106b5ff214 "fixed circle gradient generation - image loading bug" 2012-03-11 17:12:24 +00:00
d1dec8712e "added ellipse background gradient generation" 2012-03-10 15:31:16 +00:00
fe4d2c5b81 "added TODO" 2012-03-10 14:28:09 +00:00
6cf3d36624 "added TODO" 2012-03-09 23:27:22 +00:00
4dc4132818 "removed TODO" 2012-03-09 23:26:23 +00:00
1ab9941df6 "big performance boost ;o)" 2012-03-09 23:23:17 +00:00
0fc5f643ba "cleanup" 2012-03-09 23:20:46 +00:00
c7995061c9 "added TODO" 2012-03-09 23:11:01 +00:00
12cf519e37 "some cleanup" 2012-03-09 23:09:16 +00:00
40bce5e84c "added -o-radial-gradient support" 2012-03-09 23:05:42 +00:00
0556892e12 "added -moz-radial-gradient support" 2012-03-09 22:58:32 +00:00
6c29664e35 "added tests for radial gradients" 2012-03-09 22:37:45 +00:00
155ad45292 "added -webkit-radial-gradient support" 2012-03-09 22:20:34 +00:00
66ad7190c0 "ups, missed one radial gradient" 2012-03-09 20:00:56 +00:00
4f22c18043 "added some radial gradient examples to background.html" 2012-03-09 19:58:04 +00:00
d83b06458c "removed IE9 svg gradients from background.html" 2012-03-09 19:55:41 +00:00
0d35571bbf Merge branch 'color-stops' of https://github.com/SunboX/html2canvas.git 2012-03-08 11:35:12 +02:00
211467fcc1 "better unit test for gradient parsing, check inverted width/height" 2012-03-07 18:24:43 +00:00
6390c1c7ac "center gradient BugFix" 2012-03-07 18:20:09 +00:00
a0b498fbf5 "added TODO" 2012-03-06 21:50:09 +00:00
cde96bb17e "added TODO´s" 2012-03-06 21:45:38 +00:00
57bff5292d "added contributors" 2012-03-06 21:41:22 +00:00
82446ee3c3 "cleanup" 2012-03-06 21:36:29 +00:00
ffd998b015 "-o-linear-gradient parsing" 2012-03-06 21:26:43 +00:00
51b2c01b0c "-moz-linear-gradient parsing" 2012-03-06 21:11:01 +00:00
c08ac5d0c4 add svg powered rendering 2012-03-06 17:11:10 +02:00
59306c839b Merge branch 'master' of https://github.com/niklasvh/html2canvas.git 2012-03-06 14:37:28 +02:00
5fb8cb3e0b reverting back to jQuery.contents() 2012-03-06 14:35:33 +02:00
67d3e0d0f5 first commit for SVG powered rendering 2012-03-06 14:32:45 +02:00
f387267c0f "TODO: implement radial gradient generation" 2012-03-05 23:22:01 +00:00
b65a850997 "cleanup test" 2012-03-05 23:15:58 +00:00
163219b656 "added -webkit-gradient parsing + tests" 2012-03-05 23:03:36 +00:00
a4f13de455 "renamed getColorStopsFromGradient to parseGradient" 2012-03-05 22:21:28 +00:00
75ba867988 "added tests for getColorStopsFromGradient" 2012-03-05 21:04:25 +00:00
ae8d499942 "small bugfix" 2012-03-05 19:37:54 +00:00
e479c952f7 "added another background gradient to backgrounds.html" 2012-03-05 19:33:52 +00:00
6637ba1bd7 "-webkit-linear-gradient color stops and rendering" 2012-03-05 19:20:44 +00:00
883d8bb75b Merge pull request #71 from cobexer/some-cleanup
Some cleanup
2012-03-05 02:53:25 -08:00
cfde57cb9f some whitespace/warning cleanup 2012-03-05 07:56:45 +01:00
0674543ab1 cleanup event handers of image objects after use 2012-03-05 07:54:42 +01:00
954631d045 "BugFixes" 2012-03-04 20:18:03 +00:00
9fc366b3f7 "tests: moved Generate into own module; fixed QUnit messages" 2012-03-04 20:15:10 +00:00
8a5b09be70 gradient check fix 2012-03-04 21:20:22 +02:00
7a3ca77471 few bug fixes to getCSS and unit tests 2012-03-04 21:16:18 +02:00
e82d703288 "tests: splitted Generate from CSS; if not supported pass test" 2012-03-04 19:06:25 +00:00
c018166563 exclude children 2012-03-04 20:17:01 +02:00
89d749e30a Merge branch 'master' of https://github.com/niklasvh/html2canvas.git 2012-03-04 20:15:51 +02:00
c056acee43 children test 2012-03-04 20:15:35 +02:00
c6ec65616c Merge pull request #70 from SunboX/tests-background-gradient
Tests background gradient
2012-03-04 10:14:03 -08:00
f47f9025b7 "tests: small bugfix" 2012-03-04 17:30:01 +00:00
d78687a3dc "fixed typo" 2012-03-04 17:00:58 +00:00
2a0dff32b2 "test: backgroundGradient" 2012-03-04 16:59:00 +00:00
00d73c0bf8 "test: backgroundGradients markup" 2012-03-04 15:16:07 +00:00
5e57ebc0ce "preventing JSHint messages" 2012-03-04 15:02:03 +00:00
3d7a6374ad bug fixes 2012-03-03 21:03:59 +02:00
4579fb25c6 removed jQuery.css dependancy and few general CSS bug fixes 2012-03-03 19:18:39 +02:00
e84d505f46 padding width needs to be in pixels 2012-03-03 16:56:31 +02:00
946bdef0d4 Merge pull request #67 from cobexer/some-fixes
Some fixes
2012-03-03 06:42:46 -08:00
b60fc931b5 moved backgroundPosition to utils 2012-03-03 16:39:52 +02:00
8affbc3db5 add unit testing for css 2012-03-03 16:38:05 +02:00
187ae9816e update tests/origin.html to use the new options overriding
allow tests to override selector and rendering options
2012-03-03 15:24:26 +01:00
38fe643b25 clear the canvas with the documentElement background color
see tests/background.html
2012-03-03 15:07:14 +01:00
fb7879fd17 background test: add html and body background-color 2012-03-03 15:07:14 +01:00
7726cd9f39 updated test.js to allow options 2012-03-03 00:52:46 +02:00
0b065ad5d8 bug fixes 2012-03-02 21:53:20 +02:00
ec881018b3 capitalized name 2012-03-02 21:25:08 +02:00
f83fb59053 fix naming 2012-03-02 21:24:21 +02:00
6eab1d5a9c removed screenshots.html, it's available in gh-pages branch 2012-03-02 20:37:10 +02:00
94f2f799a4 Split renderers to their own objects 2012-03-02 20:35:48 +02:00
cad3be2c66 bug fixes, and further simplification of API 2012-03-02 19:07:15 +02:00
c7d526c9ea simplified API and cleaned up code 2012-03-02 18:05:03 +02:00
bf994849e0 bug fixes for body background & firefox font 2012-03-02 14:43:25 +02:00
2dc8b9385e SVG taint fix, and additional taint testing options 2012-03-01 22:31:51 +02:00
6ef6c79f24 todo update proxy 2012-03-01 19:51:07 +02:00
3ad49efa00 added support for CORS images and option to create canvas as tainted 2012-03-01 19:44:25 +02:00
c86d12b915 test if canvas has become tainted 2012-03-01 18:44:52 +02:00
1447b031c6 html2canvas -> h2clog 2012-02-28 12:40:44 +02:00
e01d97df19 fixed an instance of html2canvas.log to h2clog 2012-02-28 12:39:38 +02:00
af60621d4f Update readme.md 2012-02-27 21:24:19 +02:00
f485028d30 Merge pull request #63 from cobexer/minification-improvements
Minification improvements
2012-02-27 11:09:26 -08:00
5878c201b3 added changelog entry 2012-02-26 23:19:39 +01:00
b6d6f44678 make FlashCanvas check closure compiler friendly 2012-02-26 23:04:46 +01:00
19f505214b cleanup .gitignore 2012-02-26 22:57:28 +01:00
c24223ca85 renamed html2canvas.log to h2clog (minifies better)
renamed the html2canvas.canvasContext to h2cRenderContext,
it's used by both backends and thus not canvas specific
2012-02-26 22:57:22 +01:00
b82be022b2 build: improve minification, more verbose 2012-02-26 22:30:34 +01:00
84a676403f Merge pull request #62 from cobexer/for-niklasvh
fix image loaded through the proxy hanging the preload process
2012-02-26 12:52:46 -08:00
afc358fb12 fix image loaded through the proxy hanging the preload process
images loaded through the proxy could hang the preload process if
they finish loading through the proxy but then fail to decode
and thus don't call the onload handler of the image object.
2012-02-26 13:14:16 +01:00
e925719151 added flashcanvas integration and some legacy IE bug fixes 2012-02-26 00:21:01 +02:00
b65357c55d added flashcanvas integration and some legacy IE bug fixes 2012-02-26 00:19:16 +02:00
c4cc1fe180 Update readme.md 2012-02-20 17:26:21 +02:00
abea4a89da Added changelog 2012-02-20 17:25:57 +02:00
0cb252ada9 add support for selecting single elements to render 2012-02-20 17:16:57 +02:00
d5040e119a update version 2012-02-20 16:44:46 +02:00
83363a5cf4 Merge branch 'fix-warnings' of https://github.com/cobexer/html2canvas.git 2012-02-20 16:43:16 +02:00
96db0b8ff4 Merge pull request #55 from cobexer/profiling
jquery.plugin.html2canvas: add profiling option, tests: enable profiling
2012-02-20 05:36:36 -08:00
b320dd306e Merge pull request #53 from cobexer/bookmarklet
Bookmarklet
2012-02-20 05:35:30 -08:00
36ff1ec7aa fix warnings reported by the closure compiler 2012-02-18 23:01:18 +01:00
014c0ed98f add a note about NoScript's ABE, small fix 2012-02-18 22:50:11 +01:00
45fa47100e fix typo 2012-02-15 18:47:21 +01:00
db211317ff draft for a bookmarklet to screenshot any page
primarily intended to do debugging/testing
2012-02-14 20:13:08 +01:00
bbd75286a8 jquery.plugin.html2canvas: add profiling option, tests: enable profiling
in case you run the tests in firefox and have firebug installed and
active you now get a profiling run of html2canvas ;)
2012-02-04 15:32:09 +01:00
8bbbace790 Merge pull request #41 from cobexer/for-niklasvh
a few fixes / improvements
2011-12-21 14:21:45 -08:00
3dec1cd4ab extended .gitignore for eclipse projects 2011-12-21 23:14:32 +01:00
594f735d29 faster imeplementation of 0d370d09f
also added try/catch around that addColourStop call
because trowing an exception and failing to finish
the rendering is not nice...
2011-12-21 23:06:09 +01:00
447db0c5f5 added logs for used renderer 2011-12-21 23:01:48 +01:00
c57ed30c3e add test for image without src attribute 2011-12-21 23:00:44 +01:00
50f5f53e2b cleanup jquery.plugin.html2canvas.js; correctly pass the logging option 2011-12-21 22:59:44 +01:00
2fdab0d753 disable logging by default 2011-12-17 20:05:20 +01:00
0d8937435d fix crash if an img tag has no src, fix undefined variables 2011-12-17 19:50:27 +01:00
433d7ce81c Merge pull request #40 from cobexer/unify-tests
Unify tests: moved common code into tests/test.js; run tests from source instead from built version
2011-12-11 12:47:41 -08:00
4dba62e2fd convert all tests to use tests/test.js
all tests now run from the source files directly instead of the built
verion in build/.
2011-12-11 18:12:20 +01:00
2e494bde23 moved common code into tests/test.js 2011-12-11 18:06:57 +01:00
753ab77ef6 Merge pull request #37 from gatapia/patch-2
Fix for gradient parsing (when step positions are specified)
2011-12-07 06:13:35 -08:00
0d24b9a734 Merge pull request #38 from gatapia/patch-3
Fix demo screenshots.html with head element contains attributes
2011-12-07 06:12:56 -08:00
98c2bd638e Update screenshots.html 2011-12-07 10:48:06 +11:00
0d370d09fb This fixes an exception when trying to parse gradient like: 'rgb(254, 254, 254) 0%, rgb(236, 236, 236) 100%'
Note: This does not parse the % which would be good to delineate steps correctly, just a fix for an Exception.
2011-12-07 09:53:40 +11:00
88174fe136 IE fix 2011-11-27 04:33:41 +02:00
1c6469d29b IE origin fix 2011-11-26 21:29:46 +02:00
ab5055ffad Added canvas image test 2011-11-26 20:38:55 +02:00
501f559783 Merge pull request #31 from cobexer/add-render-canvas-support
add support to render a canvas element to the screenshot
2011-11-26 10:18:37 -08:00
f75456cc88 Merge pull request #33 from cobexer/fix-jpeg-export
clear canvas before rendering to allow jpeg export
2011-11-26 10:17:35 -08:00
961f6caf21 Merge pull request #34 from cobexer/rewrite-image-handling
rewrote image handling from using an array to an object
2011-11-26 10:15:40 -08:00
097a4e58d7 Merge pull request #35 from cobexer/small-fixes
Small fixes
2011-11-26 10:14:00 -08:00
73763c8114 rewrote image handling from using an array to an object
The image loading done in Preload.js used an array to store
image src and image object as 2 consecutive entries in that
array. Using the src as an index in a hash allows direct
instead of a linear search and also allows to store more data.

 * improved cleanup of images loaded with the proxy

 * this also adds a timeout for the image loading phase, after that
   running image requests are aborted and the rendering is started
2011-11-26 18:09:25 +01:00
1f7314747e instead of scrolling the view on every Bounds() call, do it only once 2011-11-26 18:06:56 +01:00
4f49bd6e9b accessing nodeType may throw an exception 2011-11-26 18:06:56 +01:00
217a75c0f6 fix permission denied access on inner iFrame (crossDomain) 2011-11-26 18:06:55 +01:00
62fa6038e7 fix script error in html2canvas.Util.getCSS 2011-11-26 18:06:55 +01:00
66d328d1e3 make sure the image used for the detection of the underline is positioned correctly 2011-11-26 18:06:55 +01:00
2ec7fda1f1 replaced external image http://html2canvas.hertzen.com/images/8.jpg with data URI 2011-11-26 18:06:55 +01:00
044b612d6e clear canvas before rendering to allow jpeg export
* normally when exporting jpeg any pixel that was transparent
   becomes black, make them white
 * check for window.console.log before use (avoid crash)
 * html2canvas.Parse: fix missing call to html2canvas.Util.Extend for the
   given options
2011-11-26 17:38:57 +01:00
4aba46e247 fix origin check in IE 2011-11-26 17:38:11 +01:00
91ac445fdf add support to render a canvas element to the screenshot 2011-11-26 17:34:19 +01:00
2a3d6a0a35 Merge pull request #30 from cobexer/build-improvements
Build improvements
2011-11-16 01:10:07 -08:00
03495f851d drop obsolete files from the build folder 2011-11-16 00:36:44 +01:00
16c74d1f8c Improve build system: read version from version.txt, fix build without compiler
* read the version from version.txt
 * set default target to build
 * added dependencies to all targets
 * allow the build to run without a local copy of the closure compiler
 * updated license header with @VERSION@ which will be replaced during the build
 * added the license header to all files (that one will be stripped out by the closure compiler)
2011-11-16 00:36:06 +01:00
74c7373362 Merge pull request #29 from gatapia/patch-1
It is possible for image tags not to have a 'src' (or background-image) ...
2011-11-15 04:12:37 -08:00
7ee7d7fa67 It is possible for image tags not to have a 'src' (or background-image) attribute specified. This case currently crashes html2canvas. I know its an edge case but it bit me. I set the image src programatically much later (element is actually not visible at the time I call html2canvas). 2011-11-15 16:07:05 +11:00
962d5b8e8d Merge pull request #25 from kilokeith/width_fix
Fixed a width/height issue that caused smaller elements to render full bo
2011-10-15 02:14:20 -07:00
2ca4b88702 Fixed a width/height issue that caused smaller elements to render full body width. 2011-10-14 00:47:17 -05:00
ff635115dc Enabled list-style-type's for position inside 2011-09-13 17:25:51 +03:00
a75f41c076 Merge remote branch 'upstream/master' 2011-09-13 17:09:42 +03:00
6dfb5299c9 Added list support 2011-09-13 17:09:33 +03:00
a89b83cb11 Merge pull request #17 from cthackers/master
webkit-gradient fix
2011-09-13 02:04:35 -07:00
52150b09cd Fixed webkit-gradient regular expr 2011-09-13 09:59:12 +03:00
61f71c1839 sync 2011-09-13 09:49:09 +03:00
6fe3218b50 Added some support for CSS gradients (by cthackers) 2011-09-12 23:50:43 +03:00
f0112ff3ab merged from dev 2011-09-12 21:39:28 +03:00
27ee971bba merged from dev 2011-09-12 21:35:37 +03:00
5ba5ef571c Added support for background linear gradients on mozilla and webkit 2011-09-12 13:44:39 +03:00
180cd38265 Fixed regular expression for chrome 2011-09-12 11:22:17 +03:00
958cfdf4eb Added support for base64 encoded images as sources 2011-09-12 11:02:15 +03:00
360 changed files with 107797 additions and 19031 deletions

13
.babelrc Normal file
View File

@ -0,0 +1,13 @@
{
"presets": [[
"@babel/preset-env",
{
"targets": {
"ie": "9"
}
}
], "@babel/preset-flow"],
"plugins": [
"add-module-exports"
]
}

17
.editorconfig Normal file
View File

@ -0,0 +1,17 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
[{*.yml,package.json}]
# The indent size used in the `package.json` file cannot be changed
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
indent_size = 2

26
.eslintrc Normal file
View File

@ -0,0 +1,26 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier"
],
"parserOptions": {
"project": "./tsconfig.json",
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"prettier"
],
"rules": {
"no-console": ["error", { "allow": ["warn", "error"] }],
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/class-name-casing": "off",
"prettier/prettier": "error"
}
}

19
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,19 @@
Please make sure you are testing with the latest [release of html2canvas](https://github.com/niklasvh/html2canvas/releases).
Old versions are not supported and issues reported for them will be closed.
# Please follow the general troubleshooting steps first:
- [ ] You are using the latest [version](https://github.com/niklasvh/html2canvas/releases)
- [ ] You are testing using the non-minified version of html2canvas and checked any potential issues reported in the console
<!-- You can erase any parts of this template not applicable to your Issue. -->
### Bug reports:
Please replace this line with a brief summary of your issue **AND** if possible an example on [jsfiddle](https://jsfiddle.net/).
### Specifications:
* html2canvas version tested with:
* Browser & version:
* Operating system:

37
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,37 @@
A similar PR may already be submitted!
Please search among the [Pull request](https://github.com/niklasvh/html2canvas/pulls) before creating one.
Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:
Before opening a pull request, please make sure all the tests pass locally by running `npm test`.
**Summary**
<!-- Summary of the PR -->
This PR fixes/implements the following **bugs/features**
* [ ] Bug 1
* [ ] Bug 2
* [ ] Feature 1
* [ ] Feature 2
* [ ] Breaking changes
<!-- You can skip this if you're fixing a typo or adding an app to the Showcase. -->
Explain the **motivation** for making this change. What existing problem does the pull request solve?
<!-- Example: When "Adding a function to do X", explain why it is necessary to have a way to do X. -->
**Test plan (required)**
Demonstrate how the issue/feature can be replicated. For most cases, simply adding an appropriate html/css template into the [reftests](https://github.com/niklasvh/html2canvas/tree/master/tests/reftests) should be sufficient. Please see other tests there for reference.
**Code formatting**
Please make sure that code adheres to the project code formatting. Running `npm run format` will automatically format your code correctly.
**Closing issues**
<!-- Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). -->
Fixes #

13
.github/no-response.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# Configuration for probot-no-response - https://github.com/probot/no-response
# Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 14
# Label requiring a response
responseRequiredLabel: Needs More Information
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: >
This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so
that we can investigate further.

359
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,359 @@
name: CI
on:
push:
branches: [ master ]
tags:
- 'v*'
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
name: Build
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Npm install
run: npm ci
- name: Build
run: npm run build
- name: Pack
run: |
npm pack
mv html2canvas-*.tgz html2canvas.tgz
tar --list --verbose --file=html2canvas.tgz
- name: Upload npm pack
uses: actions/upload-artifact@v2
with:
name: npm
path: html2canvas.tgz
if-no-files-found: error
- name: Upload dist
uses: actions/upload-artifact@v2
with:
name: dist
path: dist
if-no-files-found: error
- name: Upload build
uses: actions/upload-artifact@v2
with:
name: build
path: build
if-no-files-found: error
test:
runs-on: ubuntu-latest
name: Test
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Npm install
run: npm ci
- name: Build
run: npm run build
- name: Lint
run: npm run lint
- name: Unit tests
run: npm run unittest
browser-test:
strategy:
fail-fast: false
matrix:
config:
- os: ubuntu-latest
name: Linux Firefox Stable
targetBrowser: Firefox_Stable
xvfb: true
- os: ubuntu-latest
name: Linux Chrome Stable
targetBrowser: Chrome_Stable
xvfb: true
- os: macos-latest
name: OSX Safari Stable
targetBrowser: Safari_Stable
- os: macos-10.15
name: iOS Simulator Safari 12
targetBrowser: Safari_IOS_12
xcode: /Applications/Xcode_10.3.app
- os: macos-10.15
name: iOS Simulator Safari 13
targetBrowser: Safari_IOS_13
xcode: /Applications/Xcode_11.7.app
- os: macos-10.15
name: iOS Simulator Safari 14
targetBrowser: Safari_IOS_14
xcode: /Applications/Xcode_12.4.app
- os: macos-11
name: iOS Simulator Safari 15.0
targetBrowser: Safari_IOS_15_0
xcode: /Applications/Xcode_13.0.app
- os: macos-11
name: iOS Simulator Safari 15
targetBrowser: Safari_IOS_15
xcode: /Applications/Xcode_13.2.app
- os: windows-latest
name: Windows Internet Explorer 9 (Emulated)
targetBrowser: IE_9
- os: windows-latest
name: Windows Internet Explorer 10 (Emulated)
targetBrowser: IE_10
- os: windows-latest
name: Windows Internet Explorer 11
targetBrowser: IE_11
runs-on: ${{ matrix.config.os }}
name: ${{ matrix.config.name }}
env:
TARGET_BROWSER: ${{ matrix.config.targetBrowser }}
needs: build
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Npm install
run: npm ci
- name: Download library
uses: actions/download-artifact@v2
with:
name: dist
path: dist
- name: Download test-runner
uses: actions/download-artifact@v2
with:
name: build
path: build
- name: xcode selection
if: ${{ matrix.config.xcode != '' }}
run: sudo xcode-select -s "${{ matrix.config.xcode }}"
- name: Run browser tests
if: ${{ matrix.config.xvfb != true }}
run: npm run karma
- name: Start Xvfb
if: ${{ matrix.config.xvfb == true }}
run: Xvfb :99 &
- name: Run browser tests
if: ${{ matrix.config.xvfb == true }}
run: DISPLAY=:99 npm run karma
- name: Upload screenshots
uses: actions/upload-artifact@v2
with:
name: reftest-results
path: tmp/reftests
if-no-files-found: error
publish:
runs-on: ubuntu-latest
name: Publish
if: startsWith(github.ref, 'refs/tags/v')
needs: browser-test
steps:
- uses: actions/checkout@v2
- name: Download NPM package
uses: actions/download-artifact@v2
with:
name: npm
path: npm
- name: Download library
uses: actions/download-artifact@v2
with:
name: dist
path: dist
- name: Create Github Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: false
prerelease: ${{ contains(github.ref, '-rc.') || contains(github.ref, '-alpha.') }}
- name: Upload html2canvas.js
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dist/html2canvas.js
asset_name: html2canvas.js
asset_content_type: text/javascript
- name: Upload html2canvas.min.js
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dist/html2canvas.min.js
asset_name: html2canvas.min.js
asset_content_type: text/javascript
- name: Upload html2canvas.esm.js
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dist/html2canvas.esm.js
asset_name: html2canvas.esm.js
asset_content_type: text/javascript
- name: Unpack npm
run: cd npm && tar -xvzf "html2canvas.tgz"
- uses: actions/setup-node@v1
with:
node-version: 12
registry-url: 'https://registry.npmjs.org'
- name: NPM Publish
run: cd npm/package && npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
docs:
runs-on: ubuntu-latest
name: Build docs
needs: browser-test
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Npm install
run: npm ci
- name: Download library
uses: actions/download-artifact@v2
with:
name: dist
path: www/static/dist
- name: Download test results
uses: actions/download-artifact@v2
with:
name: reftest-results
path: www/static/results
- name: Copy reftests to docs website
run: cp -R tests/reftests www/static/tests/reftests && cp -R tests/assets www/static/tests/assets && cp tests/test.js www/static/tests/test.js
- name: Create reftest result index
run: npm run build:reftest-result-list www/static/results/metadata www/src/results.json
- name: Create reftest previewer
run: npm run build:reftest-preview
- name: Clean metadata folder
run: rm -rf www/static/results/metadata
- name: Build docs
run: npm run build && cd www && npm install && npm run build && cd ..
- name: Upload docs
uses: actions/upload-artifact@v2
with:
name: docs
path: www/public
if-no-files-found: error
publish-docs:
runs-on: ubuntu-latest
name: Publish Docs
if: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') }}
needs: docs
steps:
- uses: actions/checkout@v2
with:
persist-credentials: false
- name: Download docs
uses: actions/download-artifact@v2
with:
name: docs
path: docs
- name: Publish docs
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: docs
SINGLE_COMMIT: true
CLEAN: true
diff-reftests:
runs-on: ubuntu-latest
name: Diff reftest screenshots
needs: browser-test
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Npm install
run: npm ci
- name: Checkout current snapshots
run: git checkout origin/gh-pages results
- name: Download test results
uses: actions/download-artifact@v2
with:
name: reftest-results
path: tmp/reftests
- name: Run diff
run: npm run reftests-diff
continue-on-error: true
- name: Upload diff
uses: actions/upload-artifact@v2
with:
name: snapshot-diffs
path: tmp/snapshot-diffs

37
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,37 @@
name: Create Release
on:
workflow_dispatch:
inputs:
version:
description: 'Semantic version (major | minor | patch | premajor | preminor | prepatch | prerelease)'
default: 'patch'
required: true
jobs:
version:
name: Create version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Npm install
run: npm ci
- name: Configure git
run: |
git config user.name "CI"
git config user.email "niklasvh@gmail.com"
- name: Create release
run: npm run release -- --preset eslint --release-as ${{ github.event.inputs.version }}
- name: Print details
run: |
cat package.json
cat CHANGELOG.md
git tag
- name: Push git version
run: git push --follow-tags origin master

24
.gitignore vendored
View File

@ -1,9 +1,19 @@
/dist
/tmp
/build
/nbproject/
/images/
/tests/templates/
/tests/cache/
/dist/
/build/tmp.js
index.html
image.jpg
screenshots_local.html
/.project
/.settings/
node_modules/
.envrc
*.sublime-workspace
*.baseline
*.iml
.idea/
.DS_Store
npm-debug.log
debug.log
tests/reftests.js
*.log
.rpt2_cache

22
.npmignore Normal file
View File

@ -0,0 +1,22 @@
.github/
.idea/
.rpt2_cache
build/
configs/
docs/
examples/
scripts/
src/
tests/
www/
tmp/
*.iml
.babelrc
.editorconfig
.eslintrc
.npmignore
.prettierrc
jest.config.js
karma.conf.js
karma.js
rollup.config.ts

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"trailingComma": "none",
"tabWidth": 4,
"bracketSpacing": false,
"singleQuote": true,
"printWidth": 120
}

566
CHANGELOG.md Normal file
View File

@ -0,0 +1,566 @@
# Changelog
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [1.4.1](https://github.com/niklasvh/html2canvas/compare/v1.4.0...v1.4.1) (2022-01-22)
### deps
* fix source maps (#2812) ([67c5e8d](https://github.com/niklasvh/html2canvas/commit/67c5e8dec4b2af9260a2b5b75b3399495fd1fee9)), closes [#2812](https://github.com/niklasvh/html2canvas/issues/2812)
### feat
* add support for `<video>` elements (#2788) ([181d1b1](https://github.com/niklasvh/html2canvas/commit/181d1b1103910d6e1b5277d5c007fc5e3006c6bf)), closes [#2788](https://github.com/niklasvh/html2canvas/issues/2788)
### fix
* Properties x and y of BoundingRect is undefined in old browser (#2797) ([e587a82](https://github.com/niklasvh/html2canvas/commit/e587a82dca01d9ada78cae34fd1bdb934e547f9b)), closes [#2797](https://github.com/niklasvh/html2canvas/issues/2797)
* source maps (#2787) ([46db867](https://github.com/niklasvh/html2canvas/commit/46db86755f064828559a4b0b37310f3ae94f5494)), closes [#2787](https://github.com/niklasvh/html2canvas/issues/2787)
# [1.4.0](https://github.com/niklasvh/html2canvas/compare/v1.3.4...v1.4.0) (2022-01-01)
### feat
* use native text segmenter where available (#2782) ([6521a48](https://github.com/niklasvh/html2canvas/commit/6521a487d78172f7179f7c973c1a3af40eb92009)), closes [#2782](https://github.com/niklasvh/html2canvas/issues/2782)
### fix
* adopted stylesheets (#2785) ([74696fa](https://github.com/niklasvh/html2canvas/commit/74696faf47c07b48b9c9587db0b999da1c08a8be)), closes [#2785](https://github.com/niklasvh/html2canvas/issues/2785)
* ios text wrapping with 0 width rect (#2786) ([0476d06](https://github.com/niklasvh/html2canvas/commit/0476d065158c33d2020a9f602b3043e5e2f90c75)), closes [#2786](https://github.com/niklasvh/html2canvas/issues/2786)
* reduce SLICE_STACK_SIZE to 50k (#2784) ([1cc853a](https://github.com/niklasvh/html2canvas/commit/1cc853a3186853eaca00af060f651697dc3497a9)), closes [#2784](https://github.com/niklasvh/html2canvas/issues/2784)
## [1.3.4](https://github.com/niklasvh/html2canvas/compare/v1.3.3...v1.3.4) (2021-12-29)
### ci
* add ios 15.0 testing (#2780) ([d922207](https://github.com/niklasvh/html2canvas/commit/d9222075daaed08884491b0563fc899ee0ced731)), closes [#2780](https://github.com/niklasvh/html2canvas/issues/2780)
### fix
* ios 15 font rendering crash (#2645) ([ba2b1cd](https://github.com/niklasvh/html2canvas/commit/ba2b1cd8e9a9d7932675d7abffce1526a609e769)), closes [#2645](https://github.com/niklasvh/html2canvas/issues/2645)
## [1.3.3](https://github.com/niklasvh/html2canvas/compare/v1.3.2...v1.3.3) (2021-11-23)
### ci
* fix macos action runners (#2757) ([ed57781](https://github.com/niklasvh/html2canvas/commit/ed577815949b6a565df54f2101cf6f0fb731c290)), closes [#2757](https://github.com/niklasvh/html2canvas/issues/2757)
### fix
* "offsets" text when font is big ([fd22a01](https://github.com/niklasvh/html2canvas/commit/fd22a01a3c9e39293f47caaed0c0e13d2dc8170c))
* const enums (#2651) ([eeda86b](https://github.com/niklasvh/html2canvas/commit/eeda86bd5e81fb4e97675fe9bee3d4d15899997f)), closes [#2651](https://github.com/niklasvh/html2canvas/issues/2651)
## [1.3.2](https://github.com/niklasvh/html2canvas/compare/v1.3.1...v1.3.2) (2021-08-15)
### docs
* add warning for webgl cloning with preserveDrawingBuffer=false (#2661) ([01ed879](https://github.com/niklasvh/html2canvas/commit/01ed87907ad9c7688880e2c5cb8ebc22ef73a4d8)), closes [#2661](https://github.com/niklasvh/html2canvas/issues/2661)
* include src files on www (#2660) ([58ff000](https://github.com/niklasvh/html2canvas/commit/58ff0003f77d825ac027eeec95fa80c0123eaf8f)), closes [#2660](https://github.com/niklasvh/html2canvas/issues/2660)
### feat
* add support for data-html2canvas-debug property for debugging (#2658) ([cd0d725](https://github.com/niklasvh/html2canvas/commit/cd0d7258c3a93f2989d5d9ec0244ba2763ea2d23)), closes [#2658](https://github.com/niklasvh/html2canvas/issues/2658)
### fix
* disable transition properties (#2659) ([f143166](https://github.com/niklasvh/html2canvas/commit/f1431665513e0a4636fb167a241f4a0571ba728a)), closes [#2659](https://github.com/niklasvh/html2canvas/issues/2659)
* overflows with absolutely positioned content (#2663) ([38c6829](https://github.com/niklasvh/html2canvas/commit/38c682955a9299ca7785af71d8f251df799405b0)), closes [#2663](https://github.com/niklasvh/html2canvas/issues/2663)
## [1.3.1](https://github.com/niklasvh/html2canvas/compare/v1.3.0...v1.3.1) (2021-08-14)
### fix
* multi arg transition/animation duration (#2657) ([1b55ed5](https://github.com/niklasvh/html2canvas/commit/1b55ed5668dcbbe1c6d8d7e94736d8f2da2d31c5)), closes [#2657](https://github.com/niklasvh/html2canvas/issues/2657)
# [1.3.0](https://github.com/niklasvh/html2canvas/compare/v1.2.2...v1.3.0) (2021-08-13)
### feat
* add rtl render support (#2653) ([6947982](https://github.com/niklasvh/html2canvas/commit/694798284838b16882e648914da0905818aa366c)), closes [#2653](https://github.com/niklasvh/html2canvas/issues/2653)
* correctly break graphemes (#2652) ([437b367](https://github.com/niklasvh/html2canvas/commit/437b367d3ba9dfd7f9a4c8042ac8d00208c09770)), closes [#2652](https://github.com/niklasvh/html2canvas/issues/2652)
### fix
* correctly handle allowTaint canvas cloning (#2649) ([c378e22](https://github.com/niklasvh/html2canvas/commit/c378e220694c14cb7b3b4b8650a7757f8fc23c7a)), closes [#2649](https://github.com/niklasvh/html2canvas/issues/2649)
* finish animation/transitions for elements (#2632) ([969638f](https://github.com/niklasvh/html2canvas/commit/969638fb94a0a14c64a667fa6e5689f79d9f1044)), closes [#2632](https://github.com/niklasvh/html2canvas/issues/2632)
### test
* add letter-spacing test for zwj emoji (#2650) ([f919204](https://github.com/niklasvh/html2canvas/commit/f919204efa06af219f155ca279f96124bb92862b)), closes [#2650](https://github.com/niklasvh/html2canvas/issues/2650)
## [1.2.2](https://github.com/niklasvh/html2canvas/compare/v1.2.1...v1.2.2) (2021-08-10)
### ci
* add ios15 target (#2564) ([e429e04](https://github.com/niklasvh/html2canvas/commit/e429e0443adf5c7ca3041b97a8157b8911302206)), closes [#2564](https://github.com/niklasvh/html2canvas/issues/2564)
### docs
* update test previewer (#2637) ([7a06d0c](https://github.com/niklasvh/html2canvas/commit/7a06d0c2c2f3b8a1d1a8a85c540f8288b782e8c6)), closes [#2637](https://github.com/niklasvh/html2canvas/issues/2637)
### fix
* parsing counter content in pseudo element (#2640) ([1941b9e](https://github.com/niklasvh/html2canvas/commit/1941b9e0acfd9243da0beaf70e1643cab1b4a963)), closes [#2640](https://github.com/niklasvh/html2canvas/issues/2640)
* radial gradient ry check (#2631) ([a0dd38a](https://github.com/niklasvh/html2canvas/commit/a0dd38a8be4e540ae1c1f4b4e41f6c386f3e454f)), closes [#2631](https://github.com/niklasvh/html2canvas/issues/2631)
* test for ios range line break error (#2635) ([f43f942](https://github.com/niklasvh/html2canvas/commit/f43f942fcd793dde9cdc6c0438f379ec3c05c405)), closes [#2635](https://github.com/niklasvh/html2canvas/issues/2635)
### test
* large base64 encoded background (#2636) ([e36408a](https://github.com/niklasvh/html2canvas/commit/e36408ad030fe31acd9969a37fe24c1621c0bd04)), closes [#2636](https://github.com/niklasvh/html2canvas/issues/2636)
## [1.2.1](https://github.com/niklasvh/html2canvas/compare/v1.2.0...v1.2.1) (2021-08-05)
### fix
* none image (#2627) ([6651fc6](https://github.com/niklasvh/html2canvas/commit/6651fc6789d5902d171dc53b4094887870433018)), closes [#2627](https://github.com/niklasvh/html2canvas/issues/2627)
* type import that is only available ts 3.8 or higher (#2629) ([c5c6fa0](https://github.com/niklasvh/html2canvas/commit/c5c6fa00d71f36ef963ba5170ebc7b668d39c407)), closes [#2629](https://github.com/niklasvh/html2canvas/issues/2629)
# [1.2.0](https://github.com/niklasvh/html2canvas/compare/v1.1.5...v1.2.0) (2021-08-04)
### fix
* element cropping & scrolling (#2625) ([878e37a](https://github.com/niklasvh/html2canvas/commit/878e37a24272d0412fe589975ef8eed931c56e0b)), closes [#2625](https://github.com/niklasvh/html2canvas/issues/2625)
* overflow-wrap break-word (#2626) ([95a46b0](https://github.com/niklasvh/html2canvas/commit/95a46b00c53563722c035a0e45fdf5fb507275e4)), closes [#2626](https://github.com/niklasvh/html2canvas/issues/2626)
### test
* element with scrolled window (#2624) ([1338c7b](https://github.com/niklasvh/html2canvas/commit/1338c7b203535d53509416358d74014200a994eb)), closes [#2624](https://github.com/niklasvh/html2canvas/issues/2624)
## [1.1.5](https://github.com/niklasvh/html2canvas/compare/v1.1.4...v1.1.5) (2021-08-02)
### docs
* update README to github discussion Q/A ([5dea36b](https://github.com/niklasvh/html2canvas/commit/5dea36bd6964164e8ba3f8780309e792f5d16255))
### fix
* emoji line breaking (fix #1813) (#2621) ([7d788c6](https://github.com/niklasvh/html2canvas/commit/7d788c6f3d221b87f6b59fcda8517731340b2d1f)), closes [#1813](https://github.com/niklasvh/html2canvas/issues/1813) [#2621](https://github.com/niklasvh/html2canvas/issues/2621) [#1813](https://github.com/niklasvh/html2canvas/issues/1813)
* natural sizes for images with srcset (#2622) ([96e23d1](https://github.com/niklasvh/html2canvas/commit/96e23d185198b7131cf0cfa31c14c165790464e9)), closes [#2622](https://github.com/niklasvh/html2canvas/issues/2622)
## [1.1.4](https://github.com/niklasvh/html2canvas/compare/v1.1.3...v1.1.4) (2021-07-15)
### feat
* add support for webkit-text-stroke and paint-order (#2591) ([522e5aa](https://github.com/niklasvh/html2canvas/commit/522e5aac5fdad090953d095b5d558053a5e2d43d)), closes [#2591](https://github.com/niklasvh/html2canvas/issues/2591)
### fix
* don't copy 'all' css property (#2586) ([fa60716](https://github.com/niklasvh/html2canvas/commit/fa60716d07ed590ec64543a586a7960cbc8557df)), closes [#2586](https://github.com/niklasvh/html2canvas/issues/2586)
* svg d path getting truncated on copy (#2589) ([dd6d885](https://github.com/niklasvh/html2canvas/commit/dd6d8856eca820a13a0990c467b9e531433fd4a9)), closes [#2589](https://github.com/niklasvh/html2canvas/issues/2589)
* text position for form elements and list markers (#2588) ([cd99f11](https://github.com/niklasvh/html2canvas/commit/cd99f11b1b9eb1260a548a63e2a370a0a5ddafa0)), closes [#2588](https://github.com/niklasvh/html2canvas/issues/2588)
* this.canvas.ownerDocument is undefined (#2590) ([45efe54](https://github.com/niklasvh/html2canvas/commit/45efe54da8145f97b9ee0463e686103280e3c8b1)), closes [#2590](https://github.com/niklasvh/html2canvas/issues/2590)
* word-break seperators (#2593) ([e9f7f48](https://github.com/niklasvh/html2canvas/commit/e9f7f48d571304be14610a181feedca3c3b42864)), closes [#2593](https://github.com/niklasvh/html2canvas/issues/2593)
### test
* refactor language tests (#2594) ([4c360fc](https://github.com/niklasvh/html2canvas/commit/4c360fc1f059f4dcab71a79f9dc8a5b2e25411ea)), closes [#2594](https://github.com/niklasvh/html2canvas/issues/2594)
* update box-shadow with radius ([578bb77](https://github.com/niklasvh/html2canvas/commit/578bb771bfeb7e81362e9e355d6cc9ae910e3920))
## [1.1.3](https://github.com/niklasvh/html2canvas/compare/v1.1.2...v1.1.3) (2021-07-14)
### feat
* allow access to reference element in onclone (#2584) ([58b4591](https://github.com/niklasvh/html2canvas/commit/58b45911741c0dbbccd462b2976560bb3999eaef)), closes [#2584](https://github.com/niklasvh/html2canvas/issues/2584)
* support for custom and slot elements (#2581) ([acb4cd2](https://github.com/niklasvh/html2canvas/commit/acb4cd24b85527908c02a60794768949578678f0)), closes [#2581](https://github.com/niklasvh/html2canvas/issues/2581)
### fix
* iframe load to ensure images are loaded (#2577) ([eeb5a3e](https://github.com/niklasvh/html2canvas/commit/eeb5a3ea1d6c94e0f6dcfd40695eb88ebb3e0041)), closes [#2577](https://github.com/niklasvh/html2canvas/issues/2577)
* image blob rendering ([1acdc82](https://github.com/niklasvh/html2canvas/commit/1acdc827a4e05933c2f7c9558405c66b7cd82f58))
* responsive svg images (#2583) ([92fa448](https://github.com/niklasvh/html2canvas/commit/92fa448913192d5e4e82bfe14f6644b669d4e6ef)), closes [#2583](https://github.com/niklasvh/html2canvas/issues/2583)
### test
* add test cases for text-stroke and textarea from (#1540 and #2132) (#2585) ([1d00bfe](https://github.com/niklasvh/html2canvas/commit/1d00bfe175d51e663d0bae88b6dbd10a266a71f1)), closes [#1540](https://github.com/niklasvh/html2canvas/issues/1540) [#2132](https://github.com/niklasvh/html2canvas/issues/2132) [#2585](https://github.com/niklasvh/html2canvas/issues/2585)
## [1.1.2](https://github.com/niklasvh/html2canvas/compare/v1.1.1...v1.1.2) (2021-07-13)
### ci
* implement screenshot diffing (#2571) ([e29af58](https://github.com/niklasvh/html2canvas/commit/e29af586618125bbad10ad6bee3d69fddbc5d22a)), closes [#2571](https://github.com/niklasvh/html2canvas/issues/2571)
### fix
* logger unique names (#2575) ([1715854](https://github.com/niklasvh/html2canvas/commit/171585491dd1bee4f30691328bd22e978f3ac79e)), closes [#2575](https://github.com/niklasvh/html2canvas/issues/2575)
* text-shadow position with baseline (#2576) ([439e365](https://github.com/niklasvh/html2canvas/commit/439e365ea8c703b528778a268dcfc3bf9ccad6a9)), closes [#2576](https://github.com/niklasvh/html2canvas/issues/2576)
## [1.1.1](https://github.com/niklasvh/html2canvas/compare/v1.1.0...v1.1.1) (2021-07-12)
### fix
* allow proxy url with parameters (#2100) ([a4a3ce8](https://github.com/niklasvh/html2canvas/commit/a4a3ce8a2eb6a4f43f1bc9a7935eb16f2b98a3cd)), closes [#2100](https://github.com/niklasvh/html2canvas/issues/2100)
* crash on background-size with calc() (fix #2469) (#2569) ([084017a](https://github.com/niklasvh/html2canvas/commit/084017a67319a993d73c6bdf612dd8532f1b8dbe)), closes [#2469](https://github.com/niklasvh/html2canvas/issues/2469) [#2569](https://github.com/niklasvh/html2canvas/issues/2569)
* handle unhandled promise rejections (#2568) ([4555940](https://github.com/niklasvh/html2canvas/commit/4555940d0bc17b7fd9327dd0164c382a3dbf1858)), closes [#2568](https://github.com/niklasvh/html2canvas/issues/2568)
# [1.1.0](https://github.com/niklasvh/html2canvas/compare/v1.0.0...v1.1.0) (2021-07-11)
### ci
* fail build if no artifacts uploaded (#2566) ([e7a021a](https://github.com/niklasvh/html2canvas/commit/e7a021ab931f3c0de060b4643e88fad85345c3d3)), closes [#2566](https://github.com/niklasvh/html2canvas/issues/2566)
### deps
* update dependencies with lint fixes (#2565) ([b2902ec](https://github.com/niklasvh/html2canvas/commit/b2902ec31c2a414ea26f864f8e00aa8846890ffc)), closes [#2565](https://github.com/niklasvh/html2canvas/issues/2565)
### docs
* update border-style support ([cf35a28](https://github.com/niklasvh/html2canvas/commit/cf35a282b2c9d41b601c3148e8c08fe7ba61a5f9))
### fix
* Ensure resizeImage's canvas has at least 1px of width and height (#2409) ([bb92371](https://github.com/niklasvh/html2canvas/commit/bb9237155cf0ec090432ee6c5d9c555eb6ffa81f)), closes [#2409](https://github.com/niklasvh/html2canvas/issues/2409)
* text-decoration-line fallback (#2088) (#2567) ([44296e5](https://github.com/niklasvh/html2canvas/commit/44296e529368140ec06a937383e05f3074b19ee2)), closes [#2088](https://github.com/niklasvh/html2canvas/issues/2088) [#2567](https://github.com/niklasvh/html2canvas/issues/2567)
* use baseline for text positioning (#2109) ([85f79c1](https://github.com/niklasvh/html2canvas/commit/85f79c1a5e4c0b422ace552c430dd389c8477a44)), closes [#2109](https://github.com/niklasvh/html2canvas/issues/2109)
# [1.0.0](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.7...v1.0.0) (2021-07-04)
### ci
* update docs publish action (#2451) ([7222aba](https://github.com/niklasvh/html2canvas/commit/7222aba1b42138c3d466525172411b3d9869095f)), closes [#2451](https://github.com/niklasvh/html2canvas/issues/2451)
### deps
* update www deps (#2525) ([2a013e2](https://github.com/niklasvh/html2canvas/commit/2a013e20c814b7dbaea98f54f0bde8f553eb79a2)), closes [#2525](https://github.com/niklasvh/html2canvas/issues/2525)
### feat
* add support for border-style dashed, dotted, double (#2531) ([72cd528](https://github.com/niklasvh/html2canvas/commit/72cd5284296e4cdb3fe88f2982ec7528604b6618))
### fix
* opacity with overflow hidden (#2450) ([82b7da5](https://github.com/niklasvh/html2canvas/commit/82b7da558c342e7f53d298bb1d843a5db86c3b21)), closes [#2450](https://github.com/niklasvh/html2canvas/issues/2450)
* top right border radius (#2522) ([ba17267](https://github.com/niklasvh/html2canvas/commit/ba172678f07f962e9f54b398df087e86217d7a13))
### test
* update karma runner (#2524) ([ff35c7d](https://github.com/niklasvh/html2canvas/commit/ff35c7dbd33f863f5b614d778baf8cb1e8dded60)), closes [#2524](https://github.com/niklasvh/html2canvas/issues/2524)
# [1.0.0-rc.7](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.6...v1.0.0-rc.7) (2020-08-09)
### fix
* concatenate contiguous font-family tokens (#2219) ([bacfadf](https://github.com/niklasvh/html2canvas/commit/bacfadff96d907d9e8ab4ef515ca6487de9e51fc)), closes [#2219](https://github.com/niklasvh/html2canvas/issues/2219)
* external styles on svg elements (#2320) ([1514220](https://github.com/niklasvh/html2canvas/commit/1514220812cfb22d64d0974558d9c14fe90a41d3)), closes [#2320](https://github.com/niklasvh/html2canvas/issues/2320)
# [1.0.0-rc.6](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.5...v1.0.0-rc.6) (2020-08-08)
### ci
* Azure Pipelines: upgrade from macOS 10.13 -> 10.14 (#2204) ([c366e87](https://github.com/niklasvh/html2canvas/commit/c366e8790d346ea981b24b7425aef6bf6d7ebcec)), closes [#2204](https://github.com/niklasvh/html2canvas/issues/2204)
* build docs (#2305) ([51de347](https://github.com/niklasvh/html2canvas/commit/51de34787ad8aba3f213800be45e878cddb064e9)), closes [#2305](https://github.com/niklasvh/html2canvas/issues/2305)
### fix
* #1868 Clone node, Setting className for SVG element raises error (#2079) ([f139b51](https://github.com/niklasvh/html2canvas/commit/f139b513c5cf9673dc727fd47124e0d779891e3a)), closes [#1868](https://github.com/niklasvh/html2canvas/issues/1868) [#2079](https://github.com/niklasvh/html2canvas/issues/2079) [#1868](https://github.com/niklasvh/html2canvas/issues/1868)
* image loading="lazy" fix #2312 (#2314) ([f23e6f6](https://github.com/niklasvh/html2canvas/commit/f23e6f6f2690dc0dbd02621c3bb81025904e6647)), closes [#2312](https://github.com/niklasvh/html2canvas/issues/2312) [#2314](https://github.com/niklasvh/html2canvas/issues/2314)
# [1.0.0-rc.5](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.4...v1.0.0-rc.5) (2019-09-27)
### fix
* correctly respect logging option (#2013) ([34b06d6365603c3b16664ab7804efe94c7945946](https://github.com/niklasvh/html2canvas/commit/34b06d6365603c3b16664ab7804efe94c7945946)), closes [#2013](https://github.com/niklasvh/html2canvas/issues/2013)
* safari pseudo element content parsing (#2018) ([3f599103fb139f218ffe917800e74af2c7cc7ad5](https://github.com/niklasvh/html2canvas/commit/3f599103fb139f218ffe917800e74af2c7cc7ad5)), closes [#2018](https://github.com/niklasvh/html2canvas/issues/2018)
* using existing canvas option (#2017) ([076492042a73d67b30e4562f2964200e07d25f5e](https://github.com/niklasvh/html2canvas/commit/076492042a73d67b30e4562f2964200e07d25f5e)), closes [#2017](https://github.com/niklasvh/html2canvas/issues/2017)
# [1.0.0-rc.4](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.3...v1.0.0-rc.4) (2019-09-22)
### docs
* fix typo (#1864) ([9a63797aa7fb81454008745d2a1c069ca24339a4](https://github.com/niklasvh/html2canvas/commit/9a63797aa7fb81454008745d2a1c069ca24339a4)), closes [#1864](https://github.com/niklasvh/html2canvas/issues/1864)
### feat
* ignore unsupported image functions (#1873) ([61f4819e02102b112513d57b16ec7d37e989af20](https://github.com/niklasvh/html2canvas/commit/61f4819e02102b112513d57b16ec7d37e989af20)), closes [#1873](https://github.com/niklasvh/html2canvas/issues/1873)
### fix
* correctly render partial borders (fix #1920) (#2010) ([eedb81ef9e114366a7e286e975659360cf9d0983](https://github.com/niklasvh/html2canvas/commit/eedb81ef9e114366a7e286e975659360cf9d0983)), closes [#1920](https://github.com/niklasvh/html2canvas/issues/1920) [#2010](https://github.com/niklasvh/html2canvas/issues/2010)
* nested z-index ordering (#2011) ([00555cf1efddfed5877811d8a03a326f9943ab06](https://github.com/niklasvh/html2canvas/commit/00555cf1efddfed5877811d8a03a326f9943ab06)), closes [#2011](https://github.com/niklasvh/html2canvas/issues/2011) [#1978](https://github.com/niklasvh/html2canvas/issues/1978)
* null backgroundColor option as transparent (#2012) ([7d3456b78c37e7333db087601805b32ec7ca0253](https://github.com/niklasvh/html2canvas/commit/7d3456b78c37e7333db087601805b32ec7ca0253)), closes [#2012](https://github.com/niklasvh/html2canvas/issues/2012)
* zero size iframe rendering (#1863) ([81dcf7b6be66920260a60908aa4b86e7530f6e17](https://github.com/niklasvh/html2canvas/commit/81dcf7b6be66920260a60908aa4b86e7530f6e17)), closes [#1863](https://github.com/niklasvh/html2canvas/issues/1863)
# [1.0.0-rc.3](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.2...v1.0.0-rc.3) (2019-05-30)
### fix
* stack exceeding for css tokenizer (#1862) ([cbaecdca28cfaf9bd854e1b0c005cc8058208b36](https://github.com/niklasvh/html2canvas/commit/cbaecdca28cfaf9bd854e1b0c005cc8058208b36)), closes [#1862](https://github.com/niklasvh/html2canvas/issues/1862)
* typescript options type definition (#1861) ([cae44a6f0a6649bd8a7c4250a20792bb5c2e5b42](https://github.com/niklasvh/html2canvas/commit/cae44a6f0a6649bd8a7c4250a20792bb5c2e5b42)), closes [#1861](https://github.com/niklasvh/html2canvas/issues/1861)
# [1.0.0-rc.2](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.1...v1.0.0-rc.2) (2019-05-29)
### ci
* refactor browser tests (#1804) ([a7d881019bfe1fd6404c341ca1c6fa69e0274ef5](https://github.com/niklasvh/html2canvas/commit/a7d881019bfe1fd6404c341ca1c6fa69e0274ef5)), closes [#1804](https://github.com/niklasvh/html2canvas/issues/1804)
### docs
* fix README documentation ([20a797cbeb21baca4ce5b9a0642a5959cdf94a51](https://github.com/niklasvh/html2canvas/commit/20a797cbeb21baca4ce5b9a0642a5959cdf94a51))
* remove dead donation link (fix #1802) ([43058241b420a5dabe94b0a4e4f6534d16a75ec0](https://github.com/niklasvh/html2canvas/commit/43058241b420a5dabe94b0a4e4f6534d16a75ec0)), closes [#1802](https://github.com/niklasvh/html2canvas/issues/1802)
### fix
* multi token overflow #1850 (#1851) ([409674fba6f8038eb174b9c89360ef8b342971e9](https://github.com/niklasvh/html2canvas/commit/409674fba6f8038eb174b9c89360ef8b342971e9)), closes [#1850](https://github.com/niklasvh/html2canvas/issues/1850) [#1851](https://github.com/niklasvh/html2canvas/issues/1851)
### test
* include reftests previewer with docs website (#1799) ([cdc4ca8296570bf842e937c6fb7cc32a1ce2bc09](https://github.com/niklasvh/html2canvas/commit/cdc4ca8296570bf842e937c6fb7cc32a1ce2bc09)), closes [#1799](https://github.com/niklasvh/html2canvas/issues/1799)
# [1.0.0-rc.1](https://github.com/niklasvh/html2canvas/compare/v1.0.0-rc.0...v1.0.0-rc.1) (2019-04-10)
### ci
* add ios simulator tests (#1794) ([a63cb3c0f132b1af915d9ef55a4c174f6e5502ce](https://github.com/niklasvh/html2canvas/commit/a63cb3c0f132b1af915d9ef55a4c174f6e5502ce)), closes [#1794](https://github.com/niklasvh/html2canvas/issues/1794)
### docs
* fix release date in changelog ([238de790a9f223becbc8726633c0f2a2dabf2cb7](https://github.com/niklasvh/html2canvas/commit/238de790a9f223becbc8726633c0f2a2dabf2cb7))
* remove invalid `async` option from docs (fix #1769) (#1796) ([7775d3c0d6f3efca00611bedd5fc9200689a9f7a](https://github.com/niklasvh/html2canvas/commit/7775d3c0d6f3efca00611bedd5fc9200689a9f7a)), closes [#1769](https://github.com/niklasvh/html2canvas/issues/1769) [#1796](https://github.com/niklasvh/html2canvas/issues/1796)
### fix
* context scale for high resolution displays with foreignobjectrendering (#1782) ([7027900f4993dcd00745a4db045ed1c0e3255f8a](https://github.com/niklasvh/html2canvas/commit/7027900f4993dcd00745a4db045ed1c0e3255f8a)), closes [#1782](https://github.com/niklasvh/html2canvas/issues/1782)
* don't apply text shadows on elements (#1795) ([397595afb59ee50f0d128abb5945b5b9ddc6650d](https://github.com/niklasvh/html2canvas/commit/397595afb59ee50f0d128abb5945b5b9ddc6650d)), closes [#1795](https://github.com/niklasvh/html2canvas/issues/1795)
* safari data url taints (#1797) ([4e4a231683904dfdc1f82472ece5a160a158dbb8](https://github.com/niklasvh/html2canvas/commit/4e4a231683904dfdc1f82472ece5a160a158dbb8)), closes [#1797](https://github.com/niklasvh/html2canvas/issues/1797)
### test
* fix RefTestRenderer.js inclusion with karma ([49f87fb680dbfe1898b3aeb60f2f5c3a93bfbe6d](https://github.com/niklasvh/html2canvas/commit/49f87fb680dbfe1898b3aeb60f2f5c3a93bfbe6d))
# [1.0.0-rc.0](https://github.com/niklasvh/html2canvas/compare/v1.0.0-alpha.12...v1.0.0-rc.0) (2019-04-07)
### build
* update webpack and babel (#1793) ([44f3d79f68836624c2673a86f9ad47c17ef843c3](https://github.com/niklasvh/html2canvas/commit/44f3d79f68836624c2673a86f9ad47c17ef843c3)), closes [#1793](https://github.com/niklasvh/html2canvas/issues/1793)
### ci
* automate changelog generation (#1792) ([7ebef72e927eaafd34a1792ece431d2a73109230](https://github.com/niklasvh/html2canvas/commit/7ebef72e927eaafd34a1792ece431d2a73109230)), closes [#1792](https://github.com/niklasvh/html2canvas/issues/1792)
* Improve CI pipeline (#1790) ([c45ef099fe8f7142e174f4fce39448a370a987d5](https://github.com/niklasvh/html2canvas/commit/c45ef099fe8f7142e174f4fce39448a370a987d5)), closes [#1790](https://github.com/niklasvh/html2canvas/issues/1790)
### docs
* improve canvas size limit documentation (#1576) ([3212184146b33c3564c2f416e1bfda911737c38b](https://github.com/niklasvh/html2canvas/commit/3212184146b33c3564c2f416e1bfda911737c38b)), closes [#1576](https://github.com/niklasvh/html2canvas/issues/1576)
### fix
* enforce colorstop min 0 (#1743) ([349bbf137abd83464e074db3948fc79a541c2ef3](https://github.com/niklasvh/html2canvas/commit/349bbf137abd83464e074db3948fc79a541c2ef3)), closes [#1743](https://github.com/niklasvh/html2canvas/issues/1743)
* prevent unhandled promise rejections for hidden frames (#1762) ([5cbe5db35155e3a9790a30de09feb17843053b7a](https://github.com/niklasvh/html2canvas/commit/5cbe5db35155e3a9790a30de09feb17843053b7a)), closes [#1762](https://github.com/niklasvh/html2canvas/issues/1762)
* wrap .sheet.cssRules access in try...catch. (#1693) ([2c018d19875ced30caafdc40f84ca531de6e6f91](https://github.com/niklasvh/html2canvas/commit/2c018d19875ced30caafdc40f84ca531de6e6f91)), closes [#1693](https://github.com/niklasvh/html2canvas/issues/1693)
# [1.0.0-alpha.12](https://github.com/niklasvh/html2canvas/compare/v1.0.0-alpha.12...v1.0.0-alpha.13) (2018-04-05)
* Fix white space appearing on element rendering (Fix #1438)
* Reset canvas transform on finish (Fix #1494)
# v1.0.0-alpha.11 - 1.4.2018
* Fix IE11 member not found error
* Support blob image resources in non-foreignObjectRendering mode
# v1.0.0-alpha.10 - 15.2.2018
* Re-introduce `onclone` option (Fix #1434)
* Add `ignoreElements` predicate function option
* Fix version console logging
# v1.0.0-alpha.9 - 7.1.2018
* Fix dynamic style sheets
* Fix > 50% border-radius values
# v1.0.0-alpha.8 - 2.1.2018
* Use correct doctype in cloned Document (Fix #1298)
* Fix individual border rendering (Fix #1349)
# v1.0.0-alpha.7 - 31.12.2017
* Fix form input rendering (#1338)
* Improve word line breaking algorithm
# v1.0.0-alpha.6 - 28.12.2017
* Fix list-style: none (#1340)
* Extend supported values for pseudo element content
# v1.0.0-alpha.5 - 21.12.2017
* Fix underline positioning
* Fix canvas rendering on Chrome
* Fix overflow: auto
* Added support for rendering list-style
v1.0.0-alpha.4 - 12.12.2017
* Fix rendering with multiple fonts defined (Fix #796)
* Add support for radial-gradients
* Fix logging option (#1302)
* Add support for rendering webgl canvas content (#646)
* Fix external SVG loading with proxies (#802)
# v1.0.0-alpha.3 - 9.12.2017
* Disable `foreignObjectRendering` by default (#1295)
* Fix background-size when using background-origin and background-size: cover/contain (#1299)
* Added support for background-origin: content-box (#1299)
# v1.0.0-alpha.2 - 7.12.2017
* Fix scroll positions for CanvasRenderer (#1259)
* Fix `data-html2canvas-ignore` attribute (#1253)
* Fix decimal `letter-spacing` values (#1293)
# v1.0.0-alpha.1 - 5.12.2017
* Complete rewrite of library
##### Breaking Changes #####
* Remove deprecated onrendered callback, calling `html2canvas` returns a `Promise<HTMLCanvasElement>`
* Removed option `type`, same results can be achieved by assigning `x`, `y`, `scrollX`, `scrollY`, `width` and `height` properties.
## New featues / fixes
* Add support for scaling canvas (defaults to device pixel ratio)
* Add support for multiple text-shadows
* Add support for multiple text-decorations
* Add support for text-decoration-color
* Add support for percentage values for border-radius
* Correctly handle px and percentage values in linear-gradients
* Correctly support all angle types for linear-gradients
* Add support for multiple values for background-repeat, background-position and background-size
# v0.5.0-beta4 - 23.1.2016
* Fix logger requiring access to window object
* Derequire browserify build
* Fix rendering of specific elements when window is scrolled and `type` isn't set to `view`
# v0.5.0-beta3 - 6.12.2015
* Handle color names in linear gradients
# v0.5.0-beta2 - 20.10.2015
* Remove Promise polyfill (use native or provide it yourself)
# v0.5.0-beta1 - 19.10.2015
* Fix bug with unmatched color stops in gradients
* Fix scrolling issues with iOS
* Correctly handle named colors in gradients
* Accept matrix3d transforms
* Fix transparent colors breaking gradients
* Preserve scrolling positions on render
# v0.5.0-alpha2 - 3.2.2015
* Switch to using browserify for building
* Fix (#517) Chrome stretches background images with 'auto' or single attributes
# v0.5.0-alpha - 19.1.2015
* Complete rewrite of library
* Switched interface to return Promise
* Uses hidden iframe window to perform rendering, allowing async rendering and doesn't force the viewport to be scrolled to the top anymore.
* Better support for unicode
* Checkbox/radio button rendering
* SVG rendering
* iframe rendering
* Changed format for proxy requests, permitting binary responses with CORS headers as well
* Fixed many layering issues (see z-index tests)
# 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 <a href="https://github.com/niklasvh/webdriver.js">webdriver</a>
* 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 (<a href="https://github.com/niklasvh/html2canvas/commit/343b86705fe163766fcf735eb0217130e4bd5b17">niklasvh</a>)
* SVG-powered rendering (<a href="https://github.com/niklasvh/html2canvas/commit/67d3e0d0f59a5a654caf71a2e3be6494ff146c75">niklasvh</a>)
* Radial gradients (<a href="https://github.com/niklasvh/html2canvas/commit/4f22c18043a73c0c3bbf3b5e4d62714c56acd3c7">SunboX</a>)
* Split renderers to their own objects (<a href="https://github.com/niklasvh/html2canvas/commit/94f2f799a457cd29a21cc56ef8c06f1697866739">niklasvh</a>)
* Simplified API, cleaned up code (<a href="https://github.com/niklasvh/html2canvas/commit/c7d526c9eaa6a4abf4754d205fe1dee360c7660e">niklasvh</a>)
# v0.3.3 - 2.3.2012
* SVG taint fix, and additional taint testing options for rendering (<a href="https://github.com/niklasvh/html2canvas/commit/2dc8b9385e656696cb019d615bdfa1d98b17d5d4">niklasvh</a>)
* Added support for CORS images and option to create canvas as tainted (<a href="https://github.com/niklasvh/html2canvas/commit/3ad49efa0032cde25c6ed32a39e35d1505d3b2ef">niklasvh</a>)
* Improved minification saved ~1K! (<a href="https://github.com/cobexer/html2canvas/commit/b82be022b2b9240bd503e078ac980bde2b953e43">cobexer</a>)
* Added integrated support for Flashcanvas (<a href="https://github.com/niklasvh/html2canvas/commit/e9257191519f67d74fd5e364d8dee3c0963ba5fc">niklasvh</a>)
* Fixed a variety of legacy IE bugs (<a href="https://github.com/niklasvh/html2canvas/commit/b65357c55d0701017bafcd357bc654b54d458f8f">niklasvh</a>)
# v0.3.2 - 20.2.2012
* Added changelog!
* Added bookmarklet (<a href="https://github.com/niklasvh/html2canvas/commit/b320dd306e1a2d32a3bc5a71b6ebf6d8c060cde5">cobexer</a>)
* Option to select single element to render (<a href="https://github.com/niklasvh/html2canvas/commit/0cb252ada91c84ef411288b317c03e97da1f12ad">niklasvh</a>)
* Fixed closure compiler warnings (<a href="https://github.com/niklasvh/html2canvas/commit/36ff1ec7aadcbdf66851a0b77f0b9e87e4a8e4a1">cobexer</a>)
* Enable profiling in FF (<a href="https://github.com/niklasvh/html2canvas/commit/bbd75286a8406cf9e5aea01fdb7950d547edefb9">cobexer</a>)

37
LICENSE
View File

@ -1,21 +1,22 @@
/*
* The MIT License
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:
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 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.
*/
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.

73
README.md Normal file
View File

@ -0,0 +1,73 @@
html2canvas
===========
[Homepage](https://html2canvas.hertzen.com) | [Downloads](https://github.com/niklasvh/html2canvas/releases) | [Questions](https://github.com/niklasvh/html2canvas/discussions/categories/q-a)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/niklasvh/html2canvas?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
![CI](https://github.com/niklasvh/html2canvas/workflows/CI/badge.svg?branch=master)
[![NPM Downloads](https://img.shields.io/npm/dm/html2canvas.svg)](https://www.npmjs.org/package/html2canvas)
[![NPM Version](https://img.shields.io/npm/v/html2canvas.svg)](https://www.npmjs.org/package/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 **client's 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 library should work fine on the following browsers (with `Promise` polyfill):
* 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 ###
The html2canvas library utilizes `Promise`s and expects them to be available in the global context. If you wish to
support [older browsers](http://caniuse.com/#search=promise) that do not natively support `Promise`s, please include a polyfill such as
[es6-promise](https://github.com/jakearchibald/es6-promise) before including `html2canvas`.
To render an `element` with html2canvas, simply call:
` html2canvas(element[, options]);`
The function returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) containing the `<canvas>` element. Simply add a promise fulfillment handler to the promise using `then`:
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
### Building ###
You can download ready builds [here](https://github.com/niklasvh/html2canvas/releases).
Clone git repository:
$ git clone git://github.com/niklasvh/html2canvas.git
Install dependencies:
$ npm install
Build browser bundle
$ npm run build
### Examples ###
For more information and examples, please visit the [homepage](https://html2canvas.hertzen.com) or try the [test console](https://html2canvas.hertzen.com/tests/).
### 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.

View File

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="html2canvas" basedir=".">
<property name="src.dir" location="src"/>
<property name="lib.dir" location="lib"/>
<property name="build.dir" location="build"/>
<property name="dist" location="dist"/>
<property name="jquery-externs" value="jquery-1.4.4.externs.js"/>
<property name="JS_NAME" value="html2canvas.js"/>
<property name="JS_NAME_MIN" value="html2canvas.min.js"/>
<property name="JQUERY_PLUGIN_NAME" value="jquery.plugin.html2canvas.js"/>
<path id="sourcefiles">
<fileset dir="${src.dir}" includes="LICENSE"/>
<fileset dir="." includes="LICENSE"/>
<fileset dir="${src.dir}" includes="Core.js"/>
<fileset dir="${src.dir}" includes="Background.js"/>
<fileset dir="${src.dir}" includes="Border.js"/>
<fileset dir="${src.dir}" includes="Draw.js"/>
<fileset dir="${src.dir}" includes="Forms.js"/>
<fileset dir="${src.dir}" includes="Images.js"/>
<fileset dir="${src.dir}" includes="Renderer.js"/>
<fileset dir="${src.dir}" includes="Lists.js"/>
<fileset dir="${src.dir}" includes="Text.js"/>
<fileset dir="${src.dir}" includes="Traversing.js"/>
<fileset dir="${src.dir}" includes="Util.js"/>
</path>
<path id="jquery-plugin">
<fileset dir="${src.dir}" includes="LICENSE"/>
<fileset dir="." includes="LICENSE"/>
<fileset dir="${src.dir}/plugins" includes="${JQUERY_PLUGIN_NAME}"/>
</path>
<path id="minified">
<fileset dir="${src.dir}" includes="LICENSE"/>
<fileset dir="${build.dir}" includes="tmp.js"/>
</path>
<target name="plugins">
<concat fixlastline="yes" destfile="${build.dir}/${JQUERY_PLUGIN_NAME}">
<path refid="jquery-plugin"/>
</concat>
</target>
<target name="source">
<concat fixlastline="yes" destfile="${build.dir}/${JS_NAME}">
<path refid="sourcefiles"/>
</concat>
</target>
<taskdef name="jscomp" classname="com.google.javascript.jscomp.ant.CompileTask"
classpath="${lib.dir}/compiler.jar"/>
<target name="release">
<jscomp compilationLevel="simple" warning="verbose"
debug="false"
output="${build.dir}/tmp.js">
<externs dir="${lib.dir}">
<file name="${jquery-externs}"/>
</externs>
<sources dir="${build.dir}">
<file name="${JS_NAME}" />
</sources>
</jscomp>
<concat fixlastline="yes" destfile="${build.dir}/${JS_NAME_MIN}">
<path refid="minified"/>
</concat>
</target>
</project>

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +0,0 @@
/*
* html2canvas v0.27 <http://html2canvas.hertzen.com>
* Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
* http://www.twitter.com/niklasvh
*
* Released under MIT License
*/
function html2canvas(a,b){this.opts=this.extendObj(b||{},{logging:!1,ready:function(a){document.body.appendChild(a.canvas)},storageReady:function(a){a.Renderer(a.contextStacks)},iframeDefault:"default",flashCanvasPath:"http://html2canvas.hertzen.com/external/flashcanvas/flashcanvas.js",renderViewport:!1,reorderZ:!0,throttle:!0,letterRendering:!1,proxyUrl:null,logger:function(a){window.console&&window.console.log?window.console.log(a):alert(a)},canvasWidth:0,canvasHeight:0,useOverflow:!0,renderOrder:"canvas flash html"});
this.element=a;this.imagesLoaded=0;this.images=[];this.fontData=[];this.numDraws=0;this.contextStacks=[];this.ignoreElements="IFRAME|OBJECT|PARAM";this.needReorder=!1;this.blockElements=/(BR|PARAM)/;this.pageOrigin=window.location.protocol+window.location.host;this.queue=[];this.ignoreRe=RegExp("("+this.ignoreElements+")");this.support={rangeBounds:!1};if(document.createRange){var c=document.createRange();if(c.getBoundingClientRect){var d=document.createElement("boundtest");d.style.height="123px";
d.style.display="block";document.getElementsByTagName("body")[0].appendChild(d);c.selectNode(d);if(c.getBoundingClientRect().height==123)this.support.rangeBounds=!0;document.getElementsByTagName("body")[0].removeChild(d)}}this.init();return this}
html2canvas.prototype.init=function(){var a=this;this.log("Finding background-images");this.images.push("start");this.getImages(this.element);this.log("Finding images");this.each(this.element.ownerDocument.images,function(b,c){a.preloadImage(a.getAttr(c,"src"))});this.images.splice(0,1);this.images.length==0&&this.start()};
html2canvas.prototype.start=function(){if(this.images.length==0||this.imagesLoaded==this.images.length/2){this.log("Finished loading "+this.imagesLoaded+" images, Started parsing");this.bodyOverflow=document.getElementsByTagName("body")[0].style.overflow;document.getElementsByTagName("body")[0].style.overflow="hidden";var a=new this.storageContext($(document).width(),$(document).height());a.opacity=this.getCSS(this.element,"opacity");this.parseElement(this.element,this.newElement(this.element,a))}};
html2canvas.prototype.stackingContext=function(a){this.canvas=document.createElement("canvas");this.canvas.width=a;this.canvas.height=a;if(this.canvas.getContext)this.ctx=this.canvas.getContext("2d");this.ctx.textBaseline="bottom";return this.ctx};
html2canvas.prototype.storageContext=function(a,b){this.storage=[];this.width=a;this.height=b;this.fillRect=function(a,b,e,f){this.storage.push({type:"function",name:"fillRect",arguments:[a,b,e,f]})};this.drawImage=function(a,b,e,f,g,h,i,j,m){this.storage.push({type:"function",name:"drawImage",arguments:[a,b,e,f,g,h,i,j,m]})};this.fillText=function(a,b,e){this.storage.push({type:"function",name:"fillText",arguments:[a,b,e]})};return this};
html2canvas.prototype.finish=function(){this.log("Finished rendering");document.getElementsByTagName("body")[0].style.overflow=this.bodyOverflow;this.opts.ready(this)};
html2canvas.prototype.drawBackground=function(a,b,c){var d=this.getCSS(a,"background-image").split(",")[0],e=this.getCSS(a,"background-repeat").split(",")[0];if(typeof d!="undefined"&&/^(1|none)$/.test(d)==!1&&/^(-webkit|-moz|linear-gradient|-o-)/.test(d)==!1){var d=this.backgroundImageUrl(d),f=this.loadImage(d),a=this.getBackgroundPosition(a,b,f);if(f)switch(e){case "repeat-x":this.drawbackgroundRepeatX(c,f,a,b.left,b.top,b.width,b.height);break;case "repeat-y":this.drawbackgroundRepeatY(c,f,a,b.left,
b.top,b.width,b.height);break;case "no-repeat":var d=b.width-a.left,e=b.height-a.top,g=a.left,h=a.top,i=a.left+b.left,j=a.top+b.top;g<0?(g=Math.abs(g),i+=g,d=Math.min(b.width,f.width-g)):(d=Math.min(d,f.width),g=0);h<0?(h=Math.abs(h),j+=h,e=Math.min(b.height,f.height-h)):(e=Math.min(e,f.height),h=0);if(e>0&&d>0){this.drawImage(c,f,g,h,d,e,i,j,d,e);break}default:a.top-=Math.ceil(a.top/f.height)*f.height;for(d=b.top+a.top;d<b.height+b.top;)e=Math.min(f.height,b.height+b.top-d),e=Math.floor(d+f.height)>
e+d?e+d-d:f.height,d<b.top?(g=b.top-d,d=b.top):g=0,this.drawbackgroundRepeatX(c,f,a,b.left,d,b.width,e),g>0&&(a.top+=g),d=Math.floor(d+f.height)-g}else this.log("Error loading background:"+d)}};html2canvas.prototype.backgroundImageUrl=function(a){a.substr(0,5)=='url("'?(a=a.substr(5),a=a.substr(0,a.length-2)):(a=a.substr(4),a=a.substr(0,a.length-1));return a};
html2canvas.prototype.getBackgroundPosition=function(a,b,c){var d=(this.getCSS(a,"backgroundPosition").split(",")[0]||"0 0").split(" "),e;d.length==1&&(a=d,d=[],d[0]=a,d[1]=a);d[0].toString().indexOf("%")!=-1?(e=parseFloat(d[0])/100,a=b.width*e-c.width*e):a=parseInt(d[0],10);d[1].toString().indexOf("%")!=-1?(e=parseFloat(d[1])/100,b=b.height*e-c.height*e):b=parseInt(d[1],10);c={};c.top=b;c.left=a;return c};
html2canvas.prototype.drawbackgroundRepeatY=function(a,b,c,d,e,f,g){var h=Math.min(b.width,f),i;c.top-=Math.ceil(c.top/b.height)*b.height;for(i=e+c.top;i<g+e;)f=Math.floor(i+b.height)>g+e?g+e-i:b.height,this.drawBackgroundRepeat(a,b,d+c.left,i,h,f,d,e),i=Math.floor(i+b.height)};
html2canvas.prototype.drawbackgroundRepeatX=function(a,b,c,d,e,f,g){var g=Math.min(b.height,g),h,i;c.left-=Math.ceil(c.left/b.width)*b.width;for(i=d+c.left;i<f+d;)h=Math.floor(i+b.width)>f+d?f+d-i:b.width,this.drawBackgroundRepeat(a,b,i,e+c.top,h,g,d,e),i=Math.floor(i+b.width)};html2canvas.prototype.drawBackgroundRepeat=function(a,b,c,d,e,f,g,h){var i=0,j=0;g-c>0&&(i=g-c);h-d>0&&(j=h-d);this.drawImage(a,b,i,j,e-i,f-j,c+i,d+j,e-i,f-j)};
html2canvas.prototype.getBorderData=function(a){var b=[],c=this;this.each(["top","right","bottom","left"],function(d,e){b.push({width:parseInt(c.getCSS(a,"border-"+e+"-width"),10),color:c.getCSS(a,"border-"+e+"-color")})});return b};
html2canvas.prototype.drawBorders=function(a,b,c,d){var e=c.left,f=c.top,g=c.width,h=c.height,i=this.getBorderData(a),j=this;this.each(i,function(a,c){if(c.width>0){var k=e,o=f,n=g,p=h-i[2].width;switch(a){case 0:p=i[0].width;break;case 1:k=e+g-i[1].width;n=i[1].width;break;case 2:o=o+h-i[2].width;p=i[2].width;break;case 3:n=i[3].width}n={left:k,top:o,width:n,height:p};d&&(n=j.clipBounds(n,d));n.width>0&&n.height>0&&j.newRect(b,k,o,n.width,n.height,c.color)}});return i};
html2canvas.prototype.newElement=function(a,b){var c=this.getBounds(a),d=c.left,e=c.top,f=c.width,g=c.height,h;h=this.getCSS(a,"background-color");var i=this.getCSS(a,"position"),b=b||{},j=this.setZ(this.getCSS(a,"zIndex"),i,b.zIndex,a.parentNode),m=this.getCSS(a,"opacity"),l={ctx:new this.storageContext,zIndex:j,opacity:m*b.opacity,cssPosition:i};if(b.clip)l.clip=$.extend({},b.clip),l.clip.height-=b.borders[2].width;if(this.opts.useOverflow&&/(hidden|scroll|auto)/.test(this.getCSS(a,"overflow"))&&
!/(BODY)/i.test(a.nodeName))l.clip=l.clip?this.clipBounds(l.clip,c):c;i=j.children.push(l);m=j.children[i-1].ctx;this.setContextVariable(m,"globalAlpha",l.opacity);var k=this.drawBorders(a,m,c);l.borders=k;this.ignoreRe.test(a.nodeName)&&this.opts.iframeDefault!="transparent"&&(h=this.opts.iframeDefault=="default"?"#efefef":this.opts.iframeDefault);f={left:d+k[3].width,top:e+k[0].width,width:f-(k[1].width+k[3].width),height:g-(k[0].width+k[2].width)};l.clip&&(f=this.clipBounds(f,l.clip));f.height>
0&&f.width>0&&(this.newRect(m,f.left,f.top,f.width,f.height,h),this.drawBackground(a,f,m));switch(a.nodeName){case "IMG":(h=this.loadImage(this.getAttr(a,"src")))?this.drawImage(m,h,0,0,h.width,h.height,d+parseInt(this.getCSS(a,"padding-left"),10)+k[3].width,e+parseInt(this.getCSS(a,"padding-top"),10)+k[0].width,c.width-(k[1].width+k[3].width+parseInt(this.getCSS(a,"padding-left"),10)+parseInt(this.getCSS(a,"padding-right"),10)),c.height-(k[0].width+k[2].width+parseInt(this.getCSS(a,"padding-top"),
10)+parseInt(this.getCSS(a,"padding-bottom"),10))):this.log("Error loading <img>:"+this.getAttr(a,"src"));break;case "INPUT":/^(text|url|email|submit|button|reset)$/.test(a.type)&&a.value.length>0&&this.renderFormValue(a,c,l);break;case "TEXTAREA":a.value.length>0&&this.renderFormValue(a,c,l);break;case "SELECT":a.options.length>0&&this.renderFormValue(a,c,l);break;case "LI":this.drawListItem(a,l,f)}return j.children[i-1]};
html2canvas.prototype.printText=function(a,b,c,d){this.trim(a).length>0&&(d.fillText(a,b,c),this.numDraws++)};html2canvas.prototype.newRect=function(a,b,c,d,e,f){f!="transparent"&&(this.setContextVariable(a,"fillStyle",f),a.fillRect(b,c,d,e),this.numDraws++)};html2canvas.prototype.drawImage=function(a,b,c,d,e,f,g,h,i,j){a.drawImage(b,c,d,e,f,g,h,i,j);this.numDraws++};
html2canvas.prototype.renderFormValue=function(a,b,c){var d=document.createElement("valuewrap"),e=this;this.each(["lineHeight","textAlign","fontFamily","color","fontSize","paddingLeft","paddingTop","width","height","border","borderLeftWidth","borderTopWidth"],function(b,c){d.style[c]=e.getCSS(a,c)});d.style.borderColor="black";d.style.borderStyle="solid";d.style.display="block";d.style.position="absolute";if(/^(submit|reset|button|text|password)$/.test(a.type)||a.nodeName=="SELECT")d.style.lineHeight=
e.getCSS(a,"height");d.style.top=b.top+"px";d.style.left=b.left+"px";b=document.createTextNode(a.nodeName=="SELECT"?a.options[a.selectedIndex].text:a.value);d.appendChild(b);$("body").append(d);this.newText(a,b,c);$(d).remove()};
html2canvas.prototype.getImages=function(a){var b=this;this.ignoreRe.test(a.nodeName)||this.each($(a).contents(),function(a,d){RegExp("("+this.ignoreElements+")").test(d.nodeName)||b.getImages(d)});if(a.nodeType==1||typeof a.nodeType=="undefined")(a=this.getCSS(a,"background-image"))&&a!="1"&&a!="none"&&a.substring(0,7)!="-webkit"&&a.substring(0,3)!="-o-"&&a.substring(0,4)!="-moz"&&this.preloadImage(this.backgroundImageUrl(a.split(",")[0]))};
html2canvas.prototype.loadImage=function(a){a=this.getIndex(this.images,a);return a!=-1?this.images[a+1]:!1};html2canvas.prototype.preloadImage=function(a){if(this.getIndex(this.images,a)==-1)if(this.isSameOrigin(a)){this.images.push(a);var b=new Image,c=this;$(b).load(function(){c.imagesLoaded++;c.start()});b.onerror=function(){c.images.splice(c.images.indexOf(b.src),2);c.start()};b.src=a;this.images.push(b)}else this.opts.proxyUrl&&(this.images.push(a),b=new Image,this.proxyGetImage(a,b),this.images.push(b))};
html2canvas.prototype.proxyGetImage=function(a,b){var c=this,d=document.createElement("a");d.href=a;a=d.href;$.ajax({data:{xhr2:!1,url:a},url:this.opts.proxyUrl,dataType:"jsonp",success:function(d){d.substring(0,6)=="error:"?(c.images.splice(c.images.indexOf(a),2),c.start(),c.log("Proxy was unable to load "+a+" "+d)):(b.onload=function(){c.imagesLoaded++;c.start()},b.src=d)},error:function(){c.images.splice(c.images.indexOf(a),2);c.start()}})};
html2canvas.prototype.Renderer=function(a){var b=this;this.log("Renderer initiated");this.each(this.opts.renderOrder.split(" "),function(c,d){switch(d){case "canvas":b.canvas=document.createElement("canvas");if(b.canvas.getContext)return b.canvasRenderer(a),b.log("Using canvas renderer"),!1;break;case "html":return b.log("Using HTML renderer"),!1}});return this};html2canvas.prototype.throttler=function(){};
html2canvas.prototype.canvasRenderContext=function(a,b){b.textBaseline="bottom";var c=this;a.clip&&(b.save(),b.beginPath(),b.rect(a.clip.left,a.clip.top,a.clip.width,a.clip.height),b.clip());a.ctx.storage&&c.each(a.ctx.storage,function(a,e){switch(e.type){case "variable":b[e.name]=e.arguments;break;case "function":e.name=="fillRect"?b.fillRect(e.arguments[0],e.arguments[1],e.arguments[2],e.arguments[3]):e.name=="fillText"?b.fillText(e.arguments[0],e.arguments[1],e.arguments[2]):e.name=="drawImage"?
e.arguments[8]>0&&e.arguments[7]&&b.drawImage(e.arguments[0],e.arguments[1],e.arguments[2],e.arguments[3],e.arguments[4],e.arguments[5],e.arguments[6],e.arguments[7],e.arguments[8]):c.log(e)}});a.clip&&b.restore()};html2canvas.prototype.canvasRenderStorage=function(a,b){for(;0<a.length;){var c=a.splice(0,1)[0];c.canvasPosition=c.canvasPosition||{};this.canvasRenderContext(c,b)}};
html2canvas.prototype.canvasRenderer=function(a){this.sortZ(this.zStack);a=this.queue;this.canvas.width=Math.max($(document).width(),this.opts.canvasWidth);this.canvas.height=Math.max($(document).height(),this.opts.canvasHeight);this.ctx=this.canvas.getContext("2d");this.canvasRenderStorage(a,this.ctx)};html2canvas.prototype.setContextVariable=function(a,b,c){a.storage?a.storage.push({type:"variable",name:b,arguments:c}):a[b]=c};
html2canvas.prototype.drawListItem=function(a,b,c){var d=this.getCSS(a,"list-style-position",!1);this.getListItem(a);var e=this.getCSS(a,"list-style-type",!1);if(/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(e)){var f=$(a).index()+1,g;e=="decimal"?g=f:e=="decimal-leading-zero"?g=f.toString().length==1?"0"+f.toString():f.toString():e=="upper-roman"?g=this.getListRoman(f):e=="lower-roman"?g=this.getListRoman(f).toLowerCase():
e=="lower-alpha"?g=this.getListAlpha(f).toLowerCase():e=="upper-alpha"&&(g=this.getListAlpha(f));g+=". ";e=this.getListPosition(a,g);if(d=="inside")this.setFont(b.ctx,a,!1),a=c.left,c=e.bottom,this.printText(g,a,c,b.ctx)}};
html2canvas.prototype.getListPosition=function(a,b){var c=document.createElement("boundelement");c.style.display="inline";var d=a.style.listStyleType;a.style.listStyleType="none";c.appendChild(document.createTextNode(b));a.insertBefore(c,a.firstChild);var e=this.getBounds(c);a.removeChild(c);a.style.listStyleType=d;return e};html2canvas.prototype.getListItem=function(){};html2canvas.prototype.getListAlpha=function(a){var b="";do b=String.fromCharCode(a%26+64)+b,a/=26;while(a*26>26);return b};
html2canvas.prototype.getListRoman=function(a){var b=["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"],c=[1E3,900,500,400,100,90,50,40,10,9,5,4,1],d="";if(!(a<=0||a>=4E3)){for(var e=0;e<b.length;e++)for(;a>=c[e];)a-=c[e],d+=b[e];return d}};
html2canvas.prototype.newText=function(a,b,c){var c=c.ctx,d=this.getCSS(a,"font-family"),e=this.getCSS(a,"font-size"),f=this.getCSS(a,"color"),g=this.getCSS(a,"text-decoration"),h=this.getCSS(a,"text-align"),i=this.getCSS(a,"letter-spacing");b.nodeValue=this.textTransform(b.nodeValue,this.getCSS(a,"text-transform"));if(this.trim(b.nodeValue).length>0){if(g!="none")var j=this.fontMetrics(d,e);h=h.replace(["-webkit-auto"],["auto"]);d=this.opts.letterRendering==!1&&/^(left|right|justify|auto)$/.test(h)&&
/^(normal|none)$/.test(i)?b.nodeValue.split(/(\b| )/):b.nodeValue.split("");this.setFont(c,a,!1);a=b;for(b=0;b<d.length;b++)if(typeof a.nodeValue=="string"){e=a.splitText(d[b].length);if(g!="none"||this.trim(a.nodeValue).length!=0){if(this.support.rangeBounds)document.createRange?(h=document.createRange(),h.selectNode(a)):h=document.body.createTextRange(),h=h.getBoundingClientRect()?h.getBoundingClientRect():{};else{var i=a.parentNode,m=document.createElement("wrapper"),l=a.cloneNode(!0);m.appendChild(a.cloneNode(!0));
i.replaceChild(m,a);h=this.getBounds(m);i.replaceChild(l,m)}this.printText(a.nodeValue,h.left,h.bottom,c);switch(g){case "underline":this.newRect(c,h.left,Math.round(h.top+j.baseline+j.lineWidth),h.width,1,f);break;case "overline":this.newRect(c,h.left,h.top,h.width,1,f);break;case "line-through":this.newRect(c,h.left,Math.ceil(h.top+j.middle+j.lineWidth),h.width,1,f)}}a=e}}};
html2canvas.prototype.setFont=function(a,b,c){var d=this.getCSS(b,"font-family"),e=this.getCSS(b,"font-size"),f=this.getCSS(b,"color"),g=this.getCSS(b,"font-weight"),h=this.getCSS(b,"font-style"),b=this.getCSS(b,"font-variant");switch(g){case 401:g="bold";break;case 400:g="normal"}d=b+" "+g+" "+h+" "+e+" "+d;this.setContextVariable(a,"fillStyle",f);this.setContextVariable(a,"font",d);c?this.setContextVariable(a,"textAlign","right"):this.setContextVariable(a,"textAlign","left")};
html2canvas.prototype.fontMetrics=function(a,b){var c=this.fontData.indexOf(a+"-"+b);if(c>-1)return this.fontData[c+1];c=document.createElement("div");document.getElementsByTagName("body")[0].appendChild(c);$(c).css({visibility:"hidden",fontFamily:a,fontSize:b,margin:0,padding:0});var d=document.createElement("img");d.src="http://html2canvas.hertzen.com/images/8.jpg";d.width=1;d.height=1;$(d).css({margin:0,padding:0});var e=document.createElement("span");$(e).css({fontFamily:a,fontSize:b,margin:0,
padding:0});e.appendChild(document.createTextNode("Hidden Text"));c.appendChild(e);c.appendChild(d);var f=d.offsetTop-e.offsetTop+1;c.removeChild(e);c.appendChild(document.createTextNode("Hidden Text"));$(c).css("line-height","normal");$(d).css("vertical-align","super");d={baseline:f,lineWidth:1,middle:d.offsetTop-c.offsetTop+1};this.fontData.push(a+"-"+b);this.fontData.push(d);$(c).remove();return d};
html2canvas.prototype.textTransform=function(a,b){switch(b){case "lowercase":return a.toLowerCase();case "capitalize":return a.replace(/(^|\s|:|-|\(|\))([a-z])/g,function(a,b,e){return b+e.toUpperCase()});case "uppercase":return a.toUpperCase();default:return a}};html2canvas.prototype.trim=function(a){return a.replace(/^\s*/,"").replace(/\s*$/,"")};
html2canvas.prototype.parseElement=function(a,b){var c=this;this.each(a.children,function(a,e){c.parsing(e,b)});this.log("Render queue stored");this.opts.storageReady(this);this.finish()};html2canvas.prototype.parsing=function(a,b){if(this.getCSS(a,"display")!="none"&&this.getCSS(a,"visibility")!="hidden"){var c=this,b=this.newElement(a,b)||b;this.ignoreRe.test(a.nodeName)||this.each(this.contentsInZ(a),function(d,e){e.nodeType==1?c.parsing(e,b):e.nodeType==3&&c.newText(a,e,b)})}};
html2canvas.prototype.log=function(a){this.opts.logging&&this.opts.logger(a)};html2canvas.prototype.withinBounds=function(a,b){return!a?!0:(a.left<=b.left||b.left+b.width<a.left)&&(a.top<=b.top||b.top+b.height<a.top)};html2canvas.prototype.clipBounds=function(a,b){var c=Math.max(a.left,b.left),d=Math.max(a.top,b.top);return{left:c,top:d,width:Math.min(a.left+a.width,b.left+b.width)-c,height:Math.min(a.top+a.height,b.top+b.height)-d}};
html2canvas.prototype.getBounds=function(a){window.scroll(0,0);if(a.getBoundingClientRect){var a=a.getBoundingClientRect(),b={};b.top=a.top;b.bottom=a.bottom||a.top+a.height;b.left=a.left;b.width=a.width;b.height=a.height;return b}else return b=$(a).offset(),{left:b.left+this.getCSS(a,"border-left-width",!0),top:b.top+this.getCSS(a,"border-top-width",!0),width:$(a).innerWidth(),height:$(a).innerHeight()}};
html2canvas.prototype.each=function(a,b){for(var b=b||function(){},c=0;c<a.length;c++)if(b(c,a[c])===!1)break};html2canvas.prototype.contentsInZ=function(a){return $(a).contents()};html2canvas.prototype.getAttr=function(a,b){return a.getAttribute(b)};html2canvas.prototype.extendObj=function(a,b){for(var c in a)b[c]=a[c];return b};html2canvas.prototype.zContext=function(a){return{zindex:a,children:[]}};
html2canvas.prototype.setZ=function(a,b,c){return!c?this.zStack=new this.zContext(0):a!="auto"?(this.needReorder=!0,a=new this.zContext(a),c.children.push(a),a):c};html2canvas.prototype.sortZ=function(a){var b=[],c=[],d=this;this.each(a.children,function(a,f){f.children&&f.children.length>0?(b.push(f),c.push(f.zindex)):d.queue.push(f)});c.sort(function(a,b){return a-b});this.each(c,function(a,c){for(var g=0;g<=b.length;g++)if(b[g].zindex==c){g=b.splice(g,1);d.sortZ(g[0]);break}})};
html2canvas.prototype.getContents=function(a){return a.nodeName=="iframe"?a.contentDocument||a.contentWindow.document:a.childNodes};html2canvas.prototype.getCSS=function(a,b,c){return c?parseInt($(a).css(b),10):$(a).css(b)};html2canvas.prototype.getIndex=function(a,b){if(a.indexOf)return a.indexOf(b);else{for(var c=0;c<a.length;c++)if(this[c]==b)return c;return-1}};html2canvas.prototype.isSameOrigin=function(a){var b=document.createElement("a");b.href=a;return b.protocol+b.host==this.pageOrigin};

View File

@ -1,118 +0,0 @@
/*
* html2canvas v0.25 <http://html2canvas.hertzen.com>
* Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
* http://www.twitter.com/niklasvh
*
* Released under MIT License
*/
/*
* The MIT License
* 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.
*/
/*
* jQuery helper plugin for examples and tests
*/
(function( $ ){
$.fn.html2canvas = function(options) {
var date = new Date();
var message,
timeoutTimer,
timer = date.getTime();
var object = $.extend({},{
logging: false,
proxyUrl: "http://html2canvas.appspot.com/", // running html2canvas-python proxy
ready: function(renderer) {
var finishTime = new Date();
// console.log((finishTime.getTime()-timer)/1000);
document.body.appendChild(renderer.canvas);
var canvas = $(renderer.canvas);
canvas.css('position','absolute')
.css('left',0).css('top',0);
// $('body').append(canvas);
$(canvas).siblings().toggle();
throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)/1000) + " seconds<br />Total of "+renderer.numDraws+" draws performed",4000);
$(window).click(function(){
if (!canvas.is(':visible')){
$(canvas).toggle().siblings().toggle();
throwMessage("Canvas Render visible");
} else{
$(canvas).siblings().toggle();
$(canvas).toggle();
throwMessage("Canvas Render hidden");
}
});
}
},options)
new html2canvas(this.get(0), object);
function throwMessage(msg,duration){
window.clearTimeout(timeoutTimer);
timeoutTimer = window.setTimeout(function(){
message.fadeOut(function(){
message.remove();
});
},duration || 2000);
$(message).remove();
message = $('<div />').html(msg).css({
margin:0,
padding:10,
background: "#000",
opacity:0.7,
position:"fixed",
top:10,
right:10,
fontFamily: 'Tahoma' ,
color:'#fff',
fontSize:12,
borderRadius:12,
width:'auto',
height:'auto',
textAlign:'center',
textDecoration:'none'
}).hide().fadeIn().appendTo('body');
}
};
})( jQuery );

11
configs/base.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"resolveJsonModule": true
}
}

10
configs/preview.json Normal file
View File

@ -0,0 +1,10 @@
{
"extends": "./base",
"include": [
"../www/src/preview.ts"
],
"exclude": [
"node_modules"
]
}

10
configs/scripts.json Normal file
View File

@ -0,0 +1,10 @@
{
"extends": "./base",
"include": [
"scripts/**/*.ts"
],
"exclude": [
"node_modules"
]
}

2364
demo.html

File diff suppressed because it is too large Load Diff

View File

@ -1,186 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<script type="text/javascript" src="external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="build/html2canvas.js"></script>
<script type="text/javascript" src="build/jquery.plugin.html2canvas.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('body').html2canvas();
// var ss = $('ul').offset();
// alert(ss.left);
});
</script>
<title>
display/box/float/clear test
</title>
<style type="text/css">
/* last modified: 1 Dec 98 */
html {
font: 10px/1 Verdana, sans-serif;
background-color: blue;
color: white;
}
body {
margin: 1.5em;
border: .5em solid black;
padding: 0;
width: 48em;
background-color: white;
}
dl {
margin: 0;
border: 0;
padding: .5em;
}
dt {
background-color: rgb(204,0,0);
margin: 0;
padding: 1em;
width: 10.638%; /* refers to parent element's width of 47em. = 5em or 50px */
height: 28em;
border: .5em solid black;
float: left;
}
dd {
float: right;
margin: 0 0 0 1em;
border: 1em solid black;
padding: 1em;
width: 34em;
height: 27em;
}
ul {
margin: 0;
border: 0;
padding: 0;
}
li {
display: block; /* i.e., suppress marker */
color: black;
height: 9em;
width: 5em;
margin: 0;
border: .5em solid black;
padding: 1em;
float: left;
background-color: #FC0;
}
#bar {
background-color: black;
color: white;
width: 41.17%; /* = 14em */
border: 0;
margin: 0 1em;
}
#baz {
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
background-color: black;
color: white;
}
form {
margin: 0;
display: inline;
}
p {
margin: 0;
}
form p {
line-height: 1.9;
}
blockquote {
margin: 1em 1em 1em 2em;
border-width: 1em 1.5em 2em .5em;
border-style: solid;
border-color: black;
padding: 1em 0;
width: 5em;
height: 9em;
float: left;
background-color: #FC0;
color: black;
}
address {
font-style: normal;
}
h1 {
background-color: black;
color: white;
float: left;
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
font-weight: normal;
font-size: 1em;
}
</style>
</head>
<body>
<dl>
<dt>
toggle
</dt>
<dd>
<ul>
<li>
the way
</li>
<li id="bar">
<p>
the world ends
</p>
<form action="./" method="get">
<p>
bang
<input type="radio" name="foo" value="off">
</p>
<p>
whimper
<input type="radio" name="foo2" value="on">
</p>
</form>
</li>
<li>
i grow old
</li>
<li id="baz">
pluot?
</li>
</ul>
<blockquote>
<address>
bar maids,
</address>
</blockquote>
<h1>
sing to me, erbarme dich
</h1>
</dd>
</dl>
<p style="color: black; font-size: 1em; line-height: 1.3em; clear: both">
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 <b>indistinguishably</b> (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 <A HREF="sec5526c.htm" style="text-decoration:none">parent page</A>.
</p>
</body>
</html>

View File

@ -1,72 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="build/html2canvas.js"></script>
<script type="text/javascript" src="build/jquery.plugin.html2canvas.js"></script>
<script type="text/javascript">
$(function(){
$('body').html2canvas();
});
</script>
<style>
.feedback-overlay-black{
background-color:#000;
opacity:0.5;
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
margin:0;
}
</style>
<style>
div{
padding:20px;
margin:0 auto;
border:5px solid black;
}
h1{
border-bottom:2px solid white;
}
h2{
background: #efefef;
padding:10px;
}
</style>
</head>
<body>
<div style="background:red;">
<div style="background:green;">
<div style="background:blue;border-color:white;">
<div style="background:yellow;"><div style="background:orange;"><h1>Heading</h1>
Text that isn't wrapped in anything.
<p>Followed by some text wrapped in a <b>&lt;p&gt; paragraph.</b> </p>
Maybe add a <a href="#">link</a> or a different style of <a href="#" style="background:white;" id="highlight">link with a highlight</a>.
<hr />
<h2>More content</h2>
<div style="width:10px;height:10px;border-width:10px;padding:0;">a</div>
</div></div>
</div>
</div>
</div>
</body>
</html>

35
docs/configuration.md Normal file
View File

@ -0,0 +1,35 @@
---
title: "Options"
description: "Explore the different configuration options available for html2canvas"
previousUrl: "/getting-started"
previousTitle: "Getting Started"
nextUrl: "/features"
nextTitle: "Features"
---
These are all of the available configuration options.
| Name | Default | Description |
| ------------- | :------: | ----------- |
| allowTaint | `false` | Whether to allow cross-origin images to taint the canvas
| backgroundColor | `#ffffff` | Canvas background color, if none is specified in DOM. Set `null` for transparent
| canvas | `null` | Existing `canvas` element to use as a base for drawing on
| foreignObjectRendering | `false` | Whether to use ForeignObject rendering if the browser supports it
| imageTimeout | `15000` | Timeout for loading an image (in milliseconds). Set to `0` to disable timeout.
| ignoreElements | `(element) => false` | Predicate function which removes the matching elements from the render.
| logging | `true` | Enable logging for debug purposes
| onclone | `null` | Callback function which is called when the Document has been cloned for rendering, can be used to modify the contents that will be rendered without affecting the original source document.
| proxy | `null` | Url to the [proxy](/proxy/) which is to be used for loading cross-origin images. If left empty, cross-origin images won't be loaded.
| removeContainer | `true` | Whether to cleanup the cloned DOM elements html2canvas creates temporarily
| scale | `window.devicePixelRatio` | The scale to use for rendering. Defaults to the browsers device pixel ratio.
| useCORS | `false` | Whether to attempt to load images from a server using CORS
| width | `Element` width | The width of the `canvas`
| height | `Element` height | The height of the `canvas`
| x | `Element` x-offset | Crop canvas x-coordinate
| y | `Element` y-offset| Crop canvas y-coordinate
| scrollX | `Element` scrollX | The x-scroll position to used when rendering element, (for example if the Element uses `position: fixed`)
| scrollY | `Element` scrollY | The y-scroll position to used when rendering element, (for example if the Element uses `position: fixed`)
| windowWidth | `Window.innerWidth` | Window width to use when rendering `Element`, which may affect things like Media queries
| windowHeight | `Window.innerHeight` | Window height to use when rendering `Element`, which may affect things like Media queries
If you wish to exclude certain `Element`s from getting rendered, you can add a `data-html2canvas-ignore` attribute to those elements and html2canvas will exclude them from the rendering.

42
docs/documentation.md Normal file
View File

@ -0,0 +1,42 @@
---
title: "About"
description: "Learn about html2canvas, how it works and some of its limitations"
nextUrl: "/getting-started"
nextTitle: "Getting Started"
---
Before you get started with the script, there are a few things that are good to know regarding the
script and some of its limitations.
## Introduction
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 it works
The script traverses through the DOM of the page it is loaded on. It gathers information on all the elements
there, which it then uses to build a representation of the page. In other words, it does not actually take a
screenshot of the page, but builds a representation of it based on the properties it reads from the DOM.
As a result, it is only able to render correctly properties that it understands, meaning there are many
CSS properties which do not work. For a full list of supported CSS properties, check out the
[supported features](/features/) page.
## Limitations
All the images that the script uses need to reside under the [same origin](http://en.wikipedia.org/wiki/Same_origin_policy)
for it to be able to read them without the assistance of a [proxy](/proxy/). Similarly, if you have other `canvas`
elements on the page, which have been tainted with cross-origin content, they will become dirty and no longer readable by html2canvas.
The script doesn't render plugin content such as Flash or Java applets.
## Browser compatibility
The library should work fine on the following browsers (with `Promise` polyfill):
- Firefox 3.5+
- Google Chrome
- Opera 12+
- IE9+
- Edge
- Safari 6+

48
docs/faq.md Normal file
View File

@ -0,0 +1,48 @@
---
title: "FAQ"
description: "Explore Frequently Asked Questions regarding html2canvas"
---
## Why aren't my images rendered?
html2canvas does not get around content policy restrictions set by your browser. Drawing images that reside outside of
the [origin](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) of the current page [taint the
canvas](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image#What_is_a_tainted_canvas) that they are drawn upon. If the canvas gets tainted, it cannot be read anymore. As such, html2canvas implements
methods to check whether an image would taint the canvas before applying it. If you have set the `allowTaint`
[option](/configuration) to `false`, it will not draw the image.
If you wish to load images that reside outside of your pages origin, you can use a [proxy](/proxy) to load the images.
## Why is the produced canvas empty or cuts off half way through?
Make sure that `canvas` element doesn't hit [browser limitations](https://stackoverflow.com/questions/6081483/maximum-size-of-a-canvas-element) for the `canvas` size or use the window configuration options to set a custom window size based on the `canvas` element:
```
await html2canvas(element, {
windowWidth: element.scrollWidth,
windowHeight: element.scrollHeight
});
```
The window limitations vary by browser, operating system and system hardware.
### Chrome
> Maximum height/width: 32,767 pixels
> Maximum area: 268,435,456 pixels (e.g., 16,384 x 16,384)
### Firefox
> Maximum height/width: 32,767 pixels
> Maximum area: 472,907,776 pixels (e.g., 22,528 x 20,992)
### Internet Explorer
> Maximum height/width: 8,192 pixels
> Maximum area: N/A
### iOS
> The maximum size for a canvas element is 3 megapixels for devices with less than 256 MB RAM and 5 megapixels for devices with greater or equal than 256 MB RAM
## Why doesn't CSS property X render correctly or only partially?
As each CSS property needs to be manually coded to render correctly, html2canvas will *never* have full CSS support.
The library tries to support the most [commonly used CSS properties](/features) to the extent that it can. If some CSS property
is missing or incomplete and you feel that it should be part of the library, create test cases for it and a new issue for it.
## How do I get html2canvas to work in a browser extension?
You shouldn't use html2canvas in a browser extension. Most browsers have native support for capturing screenshots from
tabs within extensions. Relevant information for [Chrome](https://developer.chrome.com/extensions/tabs#method-captureVisibleTab) and
[Firefox](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#drawWindow()).

87
docs/features.md Normal file
View File

@ -0,0 +1,87 @@
---
title: "Features"
description: "Discover the different features supported by html2canvas"
---
Below is a list of all the supported CSS properties and values.
- background
- background-clip (**Does not support `text`**)
- background-color
- background-image
- url()
- linear-gradient()
- radial-gradient()
- background-origin
- background-position
- background-size
- border
- border-color
- border-radius
- border-style
- border-width
- bottom
- box-sizing
- content
- color
- display
- flex
- float
- font
- font-family
- font-size
- font-style
- font-variant
- font-weight
- height
- left
- letter-spacing
- line-break
- list-style
- list-style-image
- list-style-position
- list-style-type
- margin
- max-height
- max-width
- min-height
- min-width
- opacity
- overflow
- overflow-wrap
- padding
- paint-order
- position
- right
- text-align
- text-decoration
- text-decoration-color
- text-decoration-line
- text-decoration-style (**Only supports `solid`**)
- text-shadow
- text-transform
- top
- transform (**Limited support**)
- visibility
- white-space
- width
- webkit-text-stroke
- word-break
- word-spacing
- word-wrap
- z-index
## Unsupported CSS properties
These CSS properties are **NOT** currently supported
- [background-blend-mode](https://github.com/niklasvh/html2canvas/issues/966)
- [border-image](https://github.com/niklasvh/html2canvas/issues/1287)
- [box-decoration-break](https://github.com/niklasvh/html2canvas/issues/552)
- [box-shadow](https://github.com/niklasvh/html2canvas/pull/1086)
- [filter](https://github.com/niklasvh/html2canvas/issues/493)
- [font-variant-ligatures](https://github.com/niklasvh/html2canvas/pull/1085)
- [mix-blend-mode](https://github.com/niklasvh/html2canvas/issues/580)
- [object-fit](https://github.com/niklasvh/html2canvas/issues/1064)
- [repeating-linear-gradient()](https://github.com/niklasvh/html2canvas/issues/1162)
- [writing-mode](https://github.com/niklasvh/html2canvas/issues/1258)
- [zoom](https://github.com/niklasvh/html2canvas/issues/732)

30
docs/getting-started.md Normal file
View File

@ -0,0 +1,30 @@
---
title: "Getting Started"
description: "Learn how to start using html2canvas"
previousUrl: "/documentation"
previousTitle: "About"
nextUrl: "/configuration"
nextTitle: "Configuration"
---
## Installing
You can install `html2canvas` through npm or [download a built release](https://github.com/niklasvh/html2canvas/releases).
### npm
npm install html2canvas
```javascript
import html2canvas from 'html2canvas';
```
## Usage
To render an `element` with html2canvas with some (optional) [options](/configuration/), simply call `html2canvas(element, options);`
```javascript
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
```

12
docs/proxy.md Normal file
View File

@ -0,0 +1,12 @@
---
title: "Proxy"
description: "Browse different proxies available for supporting CORS content"
---
html2canvas does not get around content policy restrictions set by your browser. Drawing images that reside outside of
the origin of the current page taint the canvas that they are drawn upon. If the canvas gets tainted,
it cannot be read anymore. If you wish to load images that reside outside of your pages origin, you can use a proxy to load the images.
## Available proxies
- [node.js](https://github.com/niklasvh/html2canvas-proxy-nodejs)

181
examples/demo.html Normal file
View File

@ -0,0 +1,181 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html>
<head>
<title>
display/box/float/clear test
</title>
<style type="text/css">
/* last modified: 1 Dec 98 */
html {
font: 10px/1 Verdana, sans-serif;
background-color: blue;
color: white;
}
body {
margin: 1.5em;
border: .5em solid black;
padding: 0;
width: 48em;
background-color: white;
}
dl {
margin: 0;
border: 0;
padding: .5em;
}
dt {
background-color: rgb(204,0,0);
margin: 0;
padding: 1em;
width: 10.638%; /* refers to parent element's width of 47em. = 5em or 50px */
height: 28em;
border: .5em solid black;
float: left;
}
dd {
float: right;
margin: 0 0 0 1em;
border: 1em solid black;
padding: 1em;
width: 34em;
height: 27em;
}
ul {
margin: 0;
border: 0;
padding: 0;
}
li {
display: block; /* i.e., suppress marker */
color: black;
height: 9em;
width: 5em;
margin: 0;
border: .5em solid black;
padding: 1em;
float: left;
background-color: #FC0;
}
#bar {
background-color: black;
color: white;
width: 41.17%; /* = 14em */
border: 0;
margin: 0 1em;
}
#baz {
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
background-color: black;
color: white;
}
form {
margin: 0;
display: inline;
}
p {
margin: 0;
}
form p {
line-height: 1.9;
}
blockquote {
margin: 1em 1em 1em 2em;
border-width: 1em 1.5em 2em .5em;
border-style: solid;
border-color: black;
padding: 1em 0;
width: 5em;
height: 9em;
float: left;
background-color: #FC0;
color: black;
}
address {
font-style: normal;
}
h1 {
background-color: black;
color: white;
float: left;
margin: 1em 0;
border: 0;
padding: 1em;
width: 10em;
height: 10em;
font-weight: normal;
font-size: 1em;
}
</style>
</head>
<body>
<dl>
<dt>
toggle
</dt>
<dd>
<ul>
<li>
the way
</li>
<li id="bar">
<p>
the world ends
</p>
<form action="./" method="get">
<p>
bang
<input type="radio" name="foo" value="off">
</p>
<p>
whimper
<input type="radio" name="foo2" value="on">
</p>
</form>
</li>
<li>
i grow old
</li>
<li id="baz">
pluot?
</li>
</ul>
<blockquote>
<address>
bar maids,
</address>
</blockquote>
<h1>
sing to me, erbarme dich
</h1>
</dd>
</dl>
<p style="color: black; font-size: 1em; line-height: 1.3em; clear: both">
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 <b>indistinguishably</b> (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 <A HREF="sec5526c.htm" style="text-decoration:none">parent page</A>.
</p>
<script type="text/javascript" src="../dist/html2canvas.js"></script>
<script type="text/javascript">
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
</script>
</body>
</html>

63
examples/demo2.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>
.feedback-overlay-black{
background-color:#000;
opacity:0.5;
position:absolute;
top:0;
left:0;
width:100%;
height:100%;
margin:0;
}
</style>
<style>
div{
padding:20px;
margin:0 auto;
border:5px solid black;
}
h1{
border-bottom:2px solid white;
}
h2{
background: #efefef;
padding:10px;
}
</style>
</head>
<body>
<div style="background:red;">
<div style="background:green;">
<div style="background:blue;border-color:white;">
<div style="background:yellow;"><div style="background:orange;"><h1>Heading</h1>
Text that isn't wrapped in anything.
<p>Followed by some text wrapped in a <b>&lt;p&gt; paragraph.</b> </p>
Maybe add a <a href="#">link</a> or a different style of <a href="#" style="background:white;" id="highlight">link with a highlight</a>.
<hr />
<h2>More content</h2>
<div style="width:10px;height:10px;border-width:10px;padding:0;">a</div>
</div></div>
</div>
</div>
</div>
<script type="text/javascript" src="../dist/html2canvas.js"></script>
<script type="text/javascript">
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
</script>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Using an existing canvas to draw on</title>
<style>
canvas {
border: 1px solid black;
}
button {
clear: both;
display: block;
}
#content {
background: rgba(100, 255, 255, 0.5);
padding: 50px 10px;
}
</style>
</head>
<body>
<div><h1>HTML content to render:</h1>
<div id="content">Render the content in this element <strong>only</strong> onto the existing canvas element</div>
</div>
<h1>Existing canvas:</h1>
<canvas width="500" height="200"></canvas>
<script type="text/javascript" src="../dist/html2canvas.js"></script>
<button>Run html2canvas</button>
<script type="text/javascript">
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false); // Mouth (clockwise)
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye
ctx.stroke();
document.querySelector("button").addEventListener("click", function() {
html2canvas(document.querySelector("#content"), {canvas: canvas, scale: 1}).then(function(canvas) {
console.log('Drew on the existing canvas');
});
}, false);
</script>
</body>
</html>

8981
external/jquery-1.6.2.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

5
jest.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['src']
};

295
karma.conf.js Normal file
View File

@ -0,0 +1,295 @@
// Karma configuration
// Generated on Sat Aug 05 2017 23:42:26 GMT+0800 (Malay Peninsula Standard Time)
const path = require('path');
const simctl = require('node-simctl');
const iosSimulator = require('appium-ios-simulator');
const listenAddress = 'localhost';
const port = 9876;
const log = require('karma/lib/logger').create('launcher:MobileSafari');
module.exports = function(config) {
// https://github.com/actions/virtual-environments/blob/master/images/macos/macos-10.15-Readme.md
const launchers = {
Safari_IOS_9: {
base: 'MobileSafari',
name: 'iPhone 5s',
platform: 'iOS',
sdk: '9.0'
},
Safari_IOS_10: {
base: 'MobileSafari',
name: 'iPhone 5s',
platform: 'iOS',
sdk: '10.0'
},
Safari_IOS_12: {
base: 'MobileSafari',
name: 'iPhone 5s',
platform: 'iOS',
sdk: '12.4'
},
Safari_IOS_13: {
base: 'MobileSafari',
name: 'iPhone 8',
platform: 'iOS',
sdk: '13.7'
},
Safari_IOS_14: {
base: 'MobileSafari',
name: 'iPhone 8',
platform: 'iOS',
sdk: '14.4'
},
Safari_IOS_15_0: {
base: 'MobileSafari',
name: 'iPhone 13',
platform: 'iOS',
sdk: '15.0'
},
Safari_IOS_15: {
base: 'MobileSafari',
name: 'iPhone 13',
platform: 'iOS',
sdk: '15.2'
},
SauceLabs_IE9: {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '9.0',
platform: 'Windows 7'
},
SauceLabs_IE10: {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '10.0',
platform: 'Windows 7'
},
SauceLabs_IE11: {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '11.0',
platform: 'Windows 7'
},
SauceLabs_Edge18: {
base: 'SauceLabs',
browserName: 'MicrosoftEdge',
version: '18.17763',
platform: 'Windows 10'
},
SauceLabs_Android4: {
base: 'SauceLabs',
browserName: 'Browser',
platform: 'Android',
version: '4.4',
device: 'Android Emulator',
},
SauceLabs_iOS10_3: {
base: 'SauceLabs',
browserName: 'Safari',
platform: 'iOS',
version: '10.3',
device: 'iPhone 7 Plus Simulator'
},
SauceLabs_iOS9_3: {
base: 'SauceLabs',
browserName: 'Safari',
platform: 'iOS',
version: '9.3',
device: 'iPhone 6 Plus Simulator'
},
IE_9: {
base: 'IE',
'x-ua-compatible': 'IE=EmulateIE9',
flags: ['-extoff']
},
IE_10: {
base: 'IE',
'x-ua-compatible': 'IE=EmulateIE10',
flags: ['-extoff']
},
IE_11: {
base: 'IE',
flags: ['-extoff']
},
Safari_Stable: {
base: 'SafariNative'
},
Chrome_Stable: {
base: 'ChromeHeadless'
},
Firefox_Stable: {
base: 'Firefox'
}
};
const ciLauncher = launchers[process.env.TARGET_BROWSER];
const customLaunchers = ciLauncher ? {target_browser: ciLauncher} : {
stable_chrome: {
base: 'ChromeHeadless'
},
stable_firefox: {
base: 'Firefox'
}
};
const injectTypedArrayPolyfills = function(files) {
files.unshift({
pattern: path.resolve(__dirname, './node_modules/js-polyfills/typedarray.js'),
included: true,
served: true,
watched: false
});
};
injectTypedArrayPolyfills.$inject = ['config.files'];
const MobileSafari = function(baseBrowserDecorator, args) {
if(process.platform !== "darwin"){
log.error("This launcher only works in MacOS.");
this._process.kill();
return;
}
baseBrowserDecorator(this);
this.on('start', url => {
simctl.getDevices(args.sdk, args.platform).then(devices => {
const d = devices.find(d => {
return d.name === args.name;
});
if (!d) {
log.error(`No device found for sdk ${args.sdk} with name ${args.name}`);
log.info(`Available devices:`, devices);
this._process.kill();
return;
}
return iosSimulator.getSimulator(d.udid).then(device => {
return simctl.bootDevice(d.udid).then(() => device);
}).then(device => {
return device.waitForBoot(60 * 5 * 1000).then(() => {
return device.openUrl(url);
});
});
}).catch(e => {
console.log('err,', e);
});
});
};
MobileSafari.prototype = {
name: 'MobileSafari',
DEFAULT_CMD: {
darwin: '/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator',
},
ENV_CMD: null,
};
MobileSafari.$inject = ['baseBrowserDecorator', 'args'];
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'inline-mocha-fix'],
// list of files / patterns to load in the browser
files: [
'build/testrunner.js',
{ pattern: './tests/**/*', 'watched': true, 'included': false, 'served': true},
{ pattern: './dist/**/*', 'watched': true, 'included': false, 'served': true},
{ pattern: './node_modules/**/*', 'watched': true, 'included': false, 'served': true},
],
plugins: [
'karma-*',
{
'framework:inline-mocha-fix': ['factory', injectTypedArrayPolyfills]
},
{
'launcher:MobileSafari': ['type', MobileSafari]
}
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['dots', 'junit'],
junitReporter: {
outputDir: 'tmp/junit/'
},
// web server listen address,
listenAddress,
// web server port
port,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: Object.keys(customLaunchers),
customLaunchers,
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true,
// Concurrency level
// how many browser should be started simultaneous
concurrency: 5,
proxies: {
'/dist': `http://localhost:${port}/base/dist`,
'/node_modules': `http://localhost:${port}/base/node_modules`,
'/tests': `http://localhost:${port}/base/tests`,
'/assets': `http://localhost:${port}/base/tests/assets`
},
client: {
mocha: {
// change Karma's debug.html to the mocha web reporter
reporter: 'html'
}
},
captureTimeout: 300000,
browserDisconnectTimeout: 60000,
browserNoActivityTimeout: 1200000
})
};

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

44894
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

124
package.json Normal file
View File

@ -0,0 +1,124 @@
{
"title": "html2canvas",
"name": "html2canvas",
"description": "Screenshots with JavaScript",
"main": "dist/html2canvas.js",
"module": "dist/html2canvas.esm.js",
"typings": "dist/types/index.d.ts",
"browser": "dist/html2canvas.js",
"version": "1.4.1",
"author": {
"name": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
"url": "https://hertzen.com"
},
"engines": {
"node": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "git@github.com:niklasvh/html2canvas.git"
},
"bugs": {
"url": "https://github.com/niklasvh/html2canvas/issues"
},
"devDependencies": {
"@babel/cli": "^7.4.3",
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@babel/preset-flow": "^7.0.0",
"@rollup/plugin-commonjs": "^19.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-typescript": "^8.2.1",
"@types/chai": "^4.1.7",
"@types/express": "^4.17.13",
"@types/glob": "^7.1.1",
"@types/jest": "^26.0.24",
"@types/jest-image-snapshot": "^4.3.1",
"@types/karma": "^6.3.0",
"@types/mocha": "^8.2.3",
"@types/node": "^16.3.1",
"@types/platform": "^1.3.4",
"@types/promise-polyfill": "^6.0.3",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"appium-ios-simulator": "^3.10.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.5",
"babel-plugin-add-module-exports": "^1.0.2",
"babel-plugin-dev-expression": "^0.2.1",
"base64-arraybuffer": "1.0.1",
"body-parser": "^1.19.0",
"chai": "4.1.1",
"chromeless": "^1.5.2",
"cors": "^2.8.5",
"es6-promise": "^4.2.8",
"eslint": "^7.30.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "3.4.0",
"express": "^4.17.1",
"filenamify-url": "1.0.0",
"glob": "7.1.3",
"html2canvas-proxy": "1.0.1",
"jest": "^27.0.6",
"jest-image-snapshot": "^4.5.1",
"jquery": "^3.5.1",
"js-polyfills": "^0.1.42",
"karma": "^6.3.2",
"karma-chrome-launcher": "^3.1.0",
"karma-edge-launcher": "^0.4.2",
"karma-firefox-launcher": "^2.1.0",
"karma-ie-launcher": "^1.0.0",
"karma-junit-reporter": "^2.0.1",
"karma-mocha": "^2.0.1",
"karma-safarinative-launcher": "^1.1.0",
"karma-sauce-launcher": "^2.0.2",
"mocha": "^9.0.2",
"node-simctl": "^5.3.0",
"platform": "^1.3.6",
"prettier": "^2.3.2",
"replace-in-file": "^3.0.0",
"rimraf": "^3.0.2",
"rollup": "^2.53.1",
"rollup-plugin-sourcemaps": "^0.6.3",
"serve-index": "^1.9.1",
"slash": "1.0.0",
"standard-version": "^8.0.2",
"ts-jest": "^27.0.3",
"ts-loader": "^8.3.0",
"ts-node": "^10.1.0",
"tslib": "^2.3.0",
"typescript": "^4.3.5",
"uglify-js": "^3.13.10",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"yargs": "^17.0.1"
},
"scripts": {
"prebuild": "rimraf dist/ && rimraf build/ && mkdirp dist && mkdirp build",
"build": "tsc --module commonjs && rollup -c rollup.config.ts && npm run build:create-reftest-list && npm run build:testrunner && npm run build:minify",
"build:testrunner": "rollup -c tests/rollup.config.ts",
"build:minify": "uglifyjs --compress --comments /^!/ -o dist/html2canvas.min.js --mangle -- dist/html2canvas.js",
"build:reftest-result-list": "ts-node scripts/create-reftest-result-list.ts",
"build:create-reftest-list": "ts-node scripts/create-reftest-list.ts tests/reftests/ignore.txt build/reftests.ts",
"build:reftest-preview": "webpack --config www/webpack.config.js",
"release": "standard-version",
"format": "prettier --write \"{src,www/src,tests,scripts}/**/*.ts\"",
"lint": "eslint src/**/*.ts --max-warnings 0",
"test": "npm run lint && npm run unittest && npm run karma",
"unittest": "jest",
"reftests-diff": "mkdirp tmp/snapshots && jest --roots=tests --testMatch=**/reftest-diff.ts",
"karma": "ts-node tests/karma",
"watch": "rollup -c rollup.config.ts -w",
"watch:unittest": "mocha --require ts-node/register --watch-extensions ts -w src/**/__tests__/*.ts",
"start": "ts-node tests/server --port=8080 --cors=8081"
},
"homepage": "https://html2canvas.hertzen.com",
"license": "MIT",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
}
}

View File

@ -1,36 +0,0 @@
html2canvas
===========
#### JavaScript HTML renderer ####
This 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. However, as many elements are displayed differently on different browsers and operating systems (such as form elements such as radio buttons or checkboxes) as well as
It does <b>not require any rendering from the server</b>, as the whole image is created on the <b>clients browser</b>. However, for browsers without <code>canvas</code> support alternatives such as <a href="http://flashcanvas.net/">flashcanvas</a> or <a href="http://excanvas.sourceforge.net/">ExplorerCanvas</a> are necessary to create the image.
Additionally, to render <code>iframe</code> content or images situated outside of the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">same origin policy</a> a proxy will be necessary to load the content to the users browser.
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. However, please do test it out and report your findings, especially if something should be working, but is displaying it incorrectly.
###Browser compatibility###
The script should work fine on the following browsers:
* Firefox 3.5+
* Google Chrome
* Newer versions of Opera (exactly how new is yet to be determined)
* >=IE9 (Older versions compatible with the use of flashcanvas)
Note that the compatibility will most likely be increased in future builds, as many of the current restrictions have at least partial work arounds, which can be used with older browser versions.
###So what isn't included yet?###
There are still a lot of CSS properties missing, including most CSS3 properties such as <code>text-shadow</code>, <code>box-radius</code> etc. as well as all elements created by the browser, such as radio and checkbox buttons and list icons. I will compile a full list of supported elements and CSS properties soon.
There is no support for <code>frame</code> and <code>object</code> content such as Flash.
### Examples ###
For more information and examples, please visit the <a href="http://html2canvas.hertzen.com">homepage</a> or try the <a href="http://html2canvas.hertzen.com/screenshots.html">test console</a>.

42
rollup.config.ts Normal file
View File

@ -0,0 +1,42 @@
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import sourceMaps from 'rollup-plugin-sourcemaps';
import typescript from '@rollup/plugin-typescript';
import json from '@rollup/plugin-json';
const pkg = require('./package.json');
const banner = `/*!
* ${pkg.title} ${pkg.version} <${pkg.homepage}>
* Copyright (c) ${(new Date()).getFullYear()} ${pkg.author.name} <${pkg.author.url}>
* Released under ${pkg.license} License
*/`;
export default {
input: `src/index.ts`,
output: [
{ file: pkg.main, name: pkg.name, format: 'umd', banner, sourcemap: true },
{ file: pkg.module, format: 'esm', banner, sourcemap: true },
],
external: [],
watch: {
include: 'src/**',
},
plugins: [
// Allow node_modules resolution, so you can use 'external' to control
// which external modules to include in the bundle
// https://github.com/rollup/rollup-plugin-node-resolve#usage
resolve(),
// Allow json resolution
json(),
// Compile TypeScript files
typescript({ sourceMap: true, inlineSources: true }),
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
commonjs({
include: 'node_modules/**'
}),
// Resolve source maps to the original source
sourceMaps(),
],
}

View File

@ -1,326 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>JavaScript screenshot creator</title>
<style type="text/css">
a {
color: #0B0B0B;
background-color: #FDF9EE;
padding: 0 8px;
text-decoration: none;
font: normal 12px/16px "Trebuchet MS", Arial, Helvetica, sans-serif;
}
a:hover {
color: #0B0B0B;
background-color: #EFEBDE;
padding: 0 8px;
text-decoration: none;
font: normal 12px/16px "Trebuchet MS", Arial, Helvetica, sans-serif;
}
body {
font: normal 14px/19px Arial, Helvetica, sans-serif;
background-color: white;
color: #4E4628;
}
textarea {
background-color: #EFEBDE;
color: #0B0B0B;
border: #C3BCA4 1px solid;
font: normal 11px Arial, Helvetica, sans-serif;
width:300px;
height:150px;
}
h2 {
background-color: white;
color: #0B0B0B;
font: normal 28px/46px Georgia, "Times New Roman", Times, serif;
margin:0;
clear:both;
}
h3 {
color: #786E4E;
padding: 0 0 10px 55px;
height: 37px;
font: normal 24px/30px Georgia, "Times New Roman", Times, serif;
}
ul{
float:left;
margin:0;
}
table{
margin:0 auto;
width:400px;
border:1px solid black;
}
#content{
clear:both;
text-align:center;
}
#about{
padding:0 10px;
width:450px;
float:left;
}
</style>
<script type="text/javascript" src="external/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="build/html2canvas.js?221"></script>
<script type="text/javascript" src="build/jquery.plugin.html2canvas.js"></script>
<script type="text/javascript" src="http://www.hertzen.com/js/ganalytics-heatmap.js"></script>
<script type="text/javascript">
var date = new Date();
var message,
timeoutTimer,
timer;
var proxyUrl = "http://html2canvas.appspot.com";
function addRow(table,field,val){
var tr = $('<tr />').appendTo( $(table));
tr.append($('<td />').css('font-weight','bold').text(field)).append($('<td />').text(val));
}
function throwMessage(msg,duration){
window.clearTimeout(timeoutTimer);
timeoutTimer = window.setTimeout(function(){
message.fadeOut(function(){
message.remove();
});
},duration || 2000);
$(message).remove();
message = $('<div />').html(msg).css({
margin:0,
padding:10,
background: "#000",
opacity:0.7,
position:"fixed",
top:10,
right:10,
fontFamily: 'Tahoma' ,
color:'#fff',
fontSize:12,
borderRadius:12,
width:'auto',
height:'auto',
textAlign:'center',
textDecoration:'none'
}).hide().fadeIn().appendTo('body');
}
$(function(){
$('ul li a').click(function(e){
e.preventDefault();
$('#url').val(this.href);
$('button').click();
})
var iframe,d;
$('input[type="button"]').click(function(){
$(iframe.contentWindow).unbind('load');
$(iframe).contents().find('body').html2canvas({
canvasHeight: d.body.scrollHeight,
canvasWidth: d.body.scrollWidth,
logging:true
});
});
$('button').click(function(){
$(this).prop('disabled',true);
var url = $('#url').val();
$('#content').append($('<img />').attr('src','loading.gif').css('margin-top',40));
var urlParts = document.createElement('a');
urlParts.href = url;
$.ajax({
data: {
xhr2:false,
url:urlParts.href
},
url: proxyUrl,
dataType: "jsonp",
success: function(html){
iframe = document.createElement('iframe');
$(iframe).css({
'visibility':'hidden'
}).width($(window).width()).height($(window).height());
$('#content').append(iframe);
d = iframe.contentWindow.document;
d.open();
$(iframe.contentWindow).load(function(){
timer = date.getTime();
$(iframe).contents().find('body').html2canvas({
canvasHeight: d.body.scrollHeight,
canvasWidth: d.body.scrollWidth,
logging:true,
proxyUrl: proxyUrl,
logger:function(msg){
$('#logger').val(function(e,i){
return i+"\n"+msg;
});
},
ready: function(renderer) {
$('button').prop('disabled',false);
$("#content").empty();
var finishTime = new Date();
var table = $('<table />');
$('#content')
.append('<h2>Screenshot</h2>')
.append(renderer.canvas)
.append('<h3>Details</h3>')
.append(table);
addRow(table,"Creation time",((finishTime.getTime()-timer)/1000) + " seconds");
addRow(table,"Total draws", renderer.numDraws);
addRow(table,"Context stacks", renderer.contextStacks.length);
addRow(table,"Loaded images", renderer.images.length/2);
addRow(table,"Performed z-index reorder", renderer.needReorder);
addRow(table,"Used rangeBounds", renderer.support.rangeBounds);
throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)/1000) + " seconds<br />Total of "+renderer.numDraws+" draws performed",4000);
}
});
});
$('base').attr('href',urlParts.protocol+"//"+urlParts.hostname+"/");
html = html.replace("<head>","<head><base href='"+urlParts.protocol+"//"+urlParts.hostname+"/' />");
if ($("#disablejs").prop('checked')){
html = html.replace(/\<script/gi,"<!--<script");
html = html.replace(/\<\/script\>/gi,"<\/script>-->");
}
// console.log(html);
d.write(html);
d.close();
}
});
});
});
</script>
<script type="text/javascript">
var _gaq = _gaq || [];_gaq.push(['_setAccount', 'UA-188600-10']);_gaq.push(['_trackPageview']);(function() {var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);})();
</script>
<base />
</head>
<body>
<!-- <div style="background:red;padding:10px;color:#fff">
App engine proxy is <a href="http://twitter.com/#!/Niklasvh/status/96265826713350144">temporarily out of use</a> due to exceeded bandwidth use. Please try again tomorrow or meanwhile check other examples <a href="http://html2canvas.hertzen.com/">here</a>.
</div>-->
<div style="float:left;width:500px;">
<h1>JavaScript screenshot creator</h1>
<label for="url">Website URL:</label>
<input type="url" id="url" value="http://www.yahoo.com" /><button>Get screenshot!</button>
<!-- <input type="button" value="Try anyway" />--><br />
<label for="disablejs">Disable JavaScript (recommended, doesn't work well with the proxy)</label> <input type="checkbox" id="disablejs" checked /><br />
<small>Tested with Google Chrome 12, Firefox 4 and Opera 11.5</small>
</div>
<div style="float:right;">
<div style="margin-left:17px;float:right;">
<!-- Place this tag in your head or just before your close body tag -->
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
<!-- Place this tag where you want the +1 button to render -->
<g:plusone size="tall"></g:plusone>
</div>
<div style="float:right;">
<a href="http://twitter.com/share" class="twitter-share-button" data-url="http://html2canvas.hertzen.com/" data-text="html2canvas - screenshots with #JavaScript" data-count="vertical" data-via="niklasvh">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
</div>
</div>
<div style="clear:both;"></div>
<h3>Recommended (tested) pages:</h3>
<ul>
<li><a href="http://www.yahoo.com">yahoo.com</a></li>
<li><a href="http://www.google.com">google.com</a></li>
<li><a href="https://github.com/niklasvh/html2canvas">github.com</a></li>
<li><a href="http://www.smashingmagazine.com">smashingmagazine.com</a></li>
<li><a href="http://www.mashable.com">mashable.com</a></li>
<li><a href="http://www.facebook.com/google">facebook.com/google</a></li>
<li><a href="http://www.youtube.com/">youtube.com</a></li>
<li><a href="http://www.cnn.com/">cnn.com</a></li>
<li><a href="http://www.engadget.com/">engadget.com (lot of elements, very slow)</a></li>
<li><a href="http://eu.battle.net/en/">battle.net</a></li>
</ul>
<div style="float:left;">
<textarea id="logger"></textarea>
</div>
<div id="about"><b> About</b><br />
The whole screenshot is created with JavaScript. The only server interaction that is happening on this page is the proxy for loading the external pages/images into JSONP/CORS enabled page and onwards onto the JavaScript renderer script.
There are a lot of problems of loading external pages, even with a proxy, and as such many pages will not render at all. If you wish to try the script properly, I recommend you get a copy of the source from <a href="https://github.com/niklasvh/html2canvas">here</a> instead.
</div>
<div id="content"></div>
</body>
</html>

View File

@ -0,0 +1,47 @@
'use strict';
import {readFileSync, writeFileSync} from 'fs';
import {resolve, relative} from 'path';
import {sync} from 'glob';
const slash = require('slash');
if (process.argv.length <= 2) {
console.log('No ignore.txt file provided');
process.exit(1);
}
if (process.argv.length <= 3) {
console.log('No output file provided');
process.exit(1);
}
const path = resolve(__dirname, '../', process.argv[2]);
const outputPath = resolve(__dirname, '../', process.argv[3]);
const ignoredTests = readFileSync(path)
.toString()
.split(/\r\n|\r|\n/)
.filter((l) => l.length)
.reduce((acc: {[key: string]: string[]}, l) => {
const m = l.match(/^(\[(.+)\])?(.+)$/i);
if (m) {
acc[m[3]] = m[2] ? m[2].split(',') : [];
}
return acc;
}, {});
const files: string[] = sync('../tests/reftests/**/*.html', {
cwd: __dirname,
root: resolve(__dirname, '../../')
});
const testList = files.map((filename: string) => `/${slash(relative('../', filename))}`);
writeFileSync(
outputPath,
[
`export const testList: string[] = ${JSON.stringify(testList, null, 4)};`,
`export const ignoredTests: {[key: string]: string[]} = ${JSON.stringify(ignoredTests, null, 4)};`
].join('\n')
);
console.log(`${outputPath} updated`);

View File

@ -0,0 +1,44 @@
import {readdirSync, readFileSync, writeFileSync} from 'fs';
import {resolve} from 'path';
if (process.argv.length <= 2) {
console.log('No metadata path provided');
process.exit(1);
}
if (process.argv.length <= 3) {
console.log('No output file given');
process.exit(1);
}
const path = resolve(__dirname, '../', process.argv[2]);
const files = readdirSync(path);
interface RefTestMetadata {}
interface RefTestSingleMetadata extends RefTestMetadata {
test?: string;
}
interface RefTestResults {
[key: string]: Array<RefTestMetadata>;
}
const result: RefTestResults = files.reduce((result: RefTestResults, file) => {
const json: RefTestSingleMetadata = JSON.parse(readFileSync(resolve(__dirname, path, file)).toString());
if (json.test) {
if (!result[json.test]) {
result[json.test] = [];
}
result[json.test].push(json);
delete json.test;
}
return result;
}, {});
const output = resolve(__dirname, '../', process.argv[3]);
writeFileSync(output, JSON.stringify(result));
console.log(`Wrote file ${output}`);

View File

@ -0,0 +1,37 @@
const {Chromeless} = require('chromeless');
const path = require('path');
const fs = require('fs');
const express = require('express');
const reftests = require('../tests/reftests');
const app = express();
app.use('/', express.static(path.resolve(__dirname, '../')));
const listener = app.listen(0, () => {
async function run() {
const chromeless = new Chromeless();
const tests = Object.keys(reftests.testList);
let i = 0;
while (tests[i]) {
const filename = tests[i];
i++;
const reftest = await chromeless
.goto(`http://localhost:${listener.address().port}${filename}?reftest&run=false`)
.evaluate(() =>
html2canvas(document.documentElement, {
windowWidth: 800,
windowHeight: 600,
target: new RefTestRenderer()
})
);
fs.writeFileSync(
path.resolve(__dirname, `..${filename.replace(/\.html$/i, '.txt')}`),
reftest
);
}
await chromeless.end();
}
run().catch(console.error.bind(console)).then(() => process.exit(0));
});

155
scripts/parse-reftest.js Normal file
View File

@ -0,0 +1,155 @@
const ACTION = /^\s*(\w+ ?\w*):\s+(.+)/;
const TEXT = /^\s*\[(-?\d+), (-?\d+)\]:\s+(.+)/;
const WINDOW_SIZE = /^\[(-?\d+), (-?\d+)\]$/;
const RECTANGLE = /^\[(-?\d+), (-?\d+), (-?\d+), (-?\d+)\]\s+(.+)$/;
const REPEAT = /^Image\s+\("(.+)"\)\s+\[(-?\d+), (-?\d+)\]\s+Size\s+\((-?\d+), (-?\d+)\)\s+(.+)$/;
const PATH = /^Path \((.+)\)$/;
const VECTOR = /^Vector\(x: (-?\d+), y: (-?\d+)\)$/;
const BEZIER_CURVE = /^BezierCurve\(x0: (-?\d+), y0: (-?\d+), x1: (-?\d+), y1: (-?\d+), cx0: (-?\d+), cy0: (-?\d+), cx1: (-?\d+), cy1: (-?\d+)\)$/;
const SHAPE = /^(rgba?\((:?.+)\)) (Path .+)$/;
const CIRCLE = /^(rgba?\((:?.+)\)) Circle\(x: (-?\d+), y: (-?\d+), r: (-?\d+)\)$/;
const IMAGE = /^Image\s+\("(.+)"\)\s+\(source:\s+\[(-?\d+), (-?\d+), (-?\d+), (-?\d+)\]\)\s+\(destination:\s+\[(-?\d+), (-?\d+), (-?\d+), (-?\d+)\]\)$/;
const CANVAS = /^(Canvas)\s+\(source:\s+\[(-?\d+), (-?\d+), (-?\d+), (-?\d+)\]\)\s+\(destination:\s+\[(-?\d+), (-?\d+), (-?\d+), (-?\d+)\]\)$/;
const GRADIENT = /^\[(-?\d+), (-?\d+), (-?\d+), (-?\d+)\]\s+linear-gradient\(x0: (-?\d+), x1: (-?\d+), y0: (-?\d+), y1: (-?\d+) (.+)\)$/;
const TRANSFORM = /^\((-?\d+), (-?\d+)\) \[(.+)\]$/;
function parsePath(path) {
const parts = path.match(PATH)[1];
return parts.split(' > ').map(p => {
const vector = p.match(VECTOR);
if (vector) {
return {
type: 'Vector',
x: parseInt(vector[1], 10),
y: parseInt(vector[2], 10)
};
} else {
const bezier = p.match(BEZIER_CURVE);
return {
type: 'BezierCurve',
x0: parseInt(bezier[1], 10),
y0: parseInt(bezier[2], 10),
x1: parseInt(bezier[3], 10),
y1: parseInt(bezier[4], 10),
cx0: parseInt(bezier[5], 10),
cy0: parseInt(bezier[6], 10),
cx1: parseInt(bezier[7], 10),
cy1: parseInt(bezier[8], 10)
};
}
});
}
function parseRefTest(txt) {
return txt.split(/\n/g).filter(l => l.length > 0).map((l, i) => {
const parseAction = l.match(ACTION);
if (!parseAction) {
const text = l.match(TEXT);
return {
action: 'T',
x: parseInt(text[1], 10),
y: parseInt(text[2], 10),
text: text[3],
line: i + 1
};
}
const args = parseAction[2];
const data = {
action: parseAction[1],
line: i + 1
};
switch (data.action) {
case 'Opacity':
data.opacity = parseFloat(args);
break;
case 'Fill':
data.color = args;
break;
case 'Clip':
data.path = args.split(' | ').map(path => parsePath(path));
break;
case 'Window':
const windowSize = args.match(WINDOW_SIZE);
data.width = parseInt(windowSize[1], 10);
data.height = parseInt(windowSize[2], 10);
break;
case 'Rectangle':
const rectangle = args.match(RECTANGLE);
data.x = parseInt(rectangle[1], 10);
data.y = parseInt(rectangle[2], 10);
data.width = parseInt(rectangle[3], 10);
data.height = parseInt(rectangle[4], 10);
data.color = rectangle[5];
break;
case 'Repeat':
const repeat = args.match(REPEAT);
data.imageSrc = repeat[1];
data.x = parseInt(repeat[2], 10);
data.y = parseInt(repeat[3], 10);
data.width = parseInt(repeat[4], 10);
data.height = parseInt(repeat[5], 10);
data.path = parsePath(repeat[6]);
break;
case 'Shape':
const shape = args.match(SHAPE);
if (!shape) {
const circle = args.match(CIRCLE);
data.color = circle[1];
data.path = [
{
type: 'Circle',
x: parseInt(circle[2], 10),
y: parseInt(circle[3], 10),
r: parseInt(circle[4], 10)
}
];
} else {
data.color = shape[1];
data.path = parsePath(shape[3]);
}
break;
case 'Text':
data.font = args;
break;
case 'Draw image':
const image = args.match(IMAGE) ? args.match(IMAGE) : args.match(CANVAS);
data.imageSrc = image[1];
data.sx = parseInt(image[2], 10);
data.xy = parseInt(image[3], 10);
data.sw = parseInt(image[4], 10);
data.sh = parseInt(image[5], 10);
data.dx = parseInt(image[6], 10);
data.dy = parseInt(image[7], 10);
data.dw = parseInt(image[8], 10);
data.dh = parseInt(image[9], 10);
break;
case 'Gradient':
const gradient = args.match(GRADIENT);
data.x = parseInt(gradient[1], 10);
data.y = parseInt(gradient[2], 10);
data.width = parseInt(gradient[3], 10);
data.height = parseInt(gradient[4], 10);
data.x0 = parseInt(gradient[5], 10);
data.x1 = parseInt(gradient[6], 10);
data.y0 = parseInt(gradient[7], 10);
data.y1 = parseInt(gradient[8], 10);
data.stops = gradient[9];
break;
case 'Transform':
const transform = args.match(TRANSFORM);
data.x = parseInt(transform[1], 10);
data.y = parseInt(transform[2], 10);
data.matrix = transform[3];
break;
default:
console.log(args);
throw new Error('Unhandled action ' + data.action);
}
return data;
});
}
module.exports = parseRefTest;

View File

@ -1,300 +0,0 @@
html2canvas.prototype.drawBackground = function(el,bounds,ctx){
// TODO add support for multi background-images
var background_image = this.getCSS(el,"background-image").split(",")[0];
var background_repeat = this.getCSS(el,"background-repeat").split(",")[0];
if (typeof background_image != "undefined" && /^(1|none)$/.test(background_image)==false && /^(-webkit|-moz|linear-gradient|-o-)/.test(background_image)==false){
background_image = this.backgroundImageUrl(background_image);
var image = this.loadImage(background_image);
var bgp = this.getBackgroundPosition(el,bounds,image),
bgy;
if (image){
switch(background_repeat){
case "repeat-x":
this.drawbackgroundRepeatX(ctx,image,bgp,bounds.left,bounds.top,bounds.width,bounds.height);
break;
case "repeat-y":
this.drawbackgroundRepeatY(ctx,image,bgp,bounds.left,bounds.top,bounds.width,bounds.height);
break;
case "no-repeat":
/*
this.drawBackgroundRepeat(
ctx,
image,
bgp.left+bounds.left, // sx
bgp.top+bounds.top, // sy
Math.min(bounds.width,image.width),
Math.min(bounds.height,image.height),
bounds.left,
bounds.top
);*/
// console.log($(el).css('background-image'));
var bgw = bounds.width-bgp.left,
bgh = bounds.height-bgp.top,
bgsx = bgp.left,
bgsy = bgp.top,
bgdx = bgp.left+bounds.left,
bgdy = bgp.top+bounds.top;
//
// bgw = Math.min(bgw,image.width);
// bgh = Math.min(bgh,image.height);
if (bgsx<0){
bgsx = Math.abs(bgsx);
bgdx += bgsx;
bgw = Math.min(bounds.width,image.width-bgsx);
}else{
bgw = Math.min(bgw,image.width);
bgsx = 0;
}
if (bgsy<0){
bgsy = Math.abs(bgsy);
bgdy += bgsy;
// bgh = bgh-bgsy;
bgh = Math.min(bounds.height,image.height-bgsy);
}else{
bgh = Math.min(bgh,image.height);
bgsy = 0;
}
// bgh = Math.abs(bgh);
// bgw = Math.abs(bgw);
if (bgh>0 && bgw > 0){
this.drawImage(
ctx,
image,
bgsx, // source X : 0
bgsy, // source Y : 1695
bgw, // source Width : 18
bgh, // source Height : 1677
bgdx, // destination X :906
bgdy, // destination Y : 1020
bgw, // destination width : 18
bgh // destination height : 1677
);
// ctx.drawImage(image,(bounds.left+bgp.left),(bounds.top+bgp.top));
break;
}
default:
var height,
add;
bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height;
for(bgy=(bounds.top+bgp.top);bgy<bounds.height+bounds.top;){
var h = Math.min(image.height,(bounds.height+bounds.top)-bgy);
if ( Math.floor(bgy+image.height)>h+bgy){
height = (h+bgy)-bgy;
}else{
height = image.height;
}
// console.log(height);
if (bgy<bounds.top){
add = bounds.top-bgy;
bgy = bounds.top;
}else{
add = 0;
}
this.drawbackgroundRepeatX(ctx,image,bgp,bounds.left,bgy,bounds.width,height);
if (add>0){
bgp.top += add;
}
bgy = Math.floor(bgy+image.height)-add;
}
break;
}
}else{
this.log("Error loading background:" + background_image);
//console.log(images);
}
}
}
/*
* Function to retrieve the actual src of a background-image
*/
html2canvas.prototype.backgroundImageUrl = function(src){
if (src.substr(0,5)=='url("'){
src = src.substr(5);
src = src.substr(0,src.length-2);
}else{
src = src.substr(4);
src = src.substr(0,src.length-1);
}
return src;
}
/*
* Function to retrieve background-position, both in pixels and %
*/
html2canvas.prototype.getBackgroundPosition = function(el,bounds,image){
// TODO add support for multi image backgrounds
var bgpos = this.getCSS(el,"backgroundPosition").split(",")[0] || "0 0";
// var bgpos = $(el).css("backgroundPosition") || "0 0";
var bgposition = bgpos.split(" "),
topPos,
left,
percentage;
if (bgposition.length==1){
var val = bgposition,
bgposition = [];
bgposition[0] = val,
bgposition[1] = val;
}
if (bgposition[0].toString().indexOf("%")!=-1){
percentage = (parseFloat(bgposition[0])/100);
left = ((bounds.width * percentage)-(image.width*percentage));
}else{
left = parseInt(bgposition[0],10);
}
if (bgposition[1].toString().indexOf("%")!=-1){
percentage = (parseFloat(bgposition[1])/100);
topPos = ((bounds.height * percentage)-(image.height*percentage));
}else{
topPos = parseInt(bgposition[1],10);
}
var returnObj = {}
/*
"top": topPos,
"left": left
};*/
returnObj.top = topPos;
returnObj.left = left;
return returnObj;
}
html2canvas.prototype.drawbackgroundRepeatY = function(ctx,image,bgp,x,y,w,h){
var height,
width = Math.min(image.width,w),bgy;
bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height;
for(bgy=(y+bgp.top);bgy<h+y;){
if ( Math.floor(bgy+image.height)>h+y){
height = (h+y)-bgy;
}else{
height = image.height;
}
this.drawBackgroundRepeat(ctx,image,x+bgp.left,bgy,width,height,x,y);
bgy = Math.floor(bgy+image.height);
}
}
html2canvas.prototype.drawbackgroundRepeatX = function(ctx,image,bgp,x,y,w,h){
var height = Math.min(image.height,h),
width,bgx;
bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width;
for(bgx=(x+bgp.left);bgx<w+x;){
if (Math.floor(bgx+image.width)>w+x){
width = (w+x)-bgx;
}else{
width = image.width;
}
this.drawBackgroundRepeat(ctx,image,bgx,(y+bgp.top),width,height,x,y);
bgx = Math.floor(bgx+image.width);
}
}
html2canvas.prototype.drawBackgroundRepeat = function(ctx,image,x,y,width,height,elx,ely){
var sourceX = 0,
sourceY=0;
if (elx-x>0){
sourceX = elx-x;
}
if (ely-y>0){
sourceY = ely-y;
}
this.drawImage(
ctx,
image,
sourceX, // source X
sourceY, // source Y
width-sourceX, // source Width
height-sourceY, // source Height
x+sourceX, // destination X
y+sourceY, // destination Y
width-sourceX, // destination width
height-sourceY // destination height
);
}

View File

@ -1,85 +0,0 @@
/*
* Function to provide border details for an element
*/
html2canvas.prototype.getBorderData = function(el){
var borders = [];
var _ = this;
this.each(["top","right","bottom","left"],function(i,borderSide){
borders.push({
width: parseInt(_.getCSS(el,'border-'+borderSide+'-width'),10),
color: _.getCSS(el,'border-'+borderSide+'-color')
});
});
return borders;
}
html2canvas.prototype.drawBorders = function(el,ctx, bounds,clip){
var x = bounds.left;
var y = bounds.top;
var w = bounds.width;
var h = bounds.height;
/*
* TODO add support for different border-style's than solid
*/
var borders = this.getBorderData(el);
var _ = this;
this.each(borders,function(borderSide,borderData){
if (borderData.width>0){
var bx = x,
by = y,
bw = w,
bh = h-(borders[2].width);
switch(borderSide){
case 0:
// top border
bh = borders[0].width;
break;
case 1:
// right border
bx = x+w-(borders[1].width);
bw = borders[1].width;
break;
case 2:
// bottom border
by = (by+h)-(borders[2].width);
bh = borders[2].width;
break;
case 3:
// left border
bw = borders[3].width;
break;
}
var borderBounds = {
left:bx,
top:by,
width: bw,
height:bh
};
if (clip){
borderBounds = _.clipBounds(borderBounds,clip);
}
if (borderBounds.width>0 && borderBounds.height>0){
_.newRect(ctx,bx,by,borderBounds.width,borderBounds.height,borderData.color);
}
}
});
return borders;
};

View File

@ -1,260 +0,0 @@
/**
* Creates a render of the element el
* @constructor
*/
function html2canvas(el, userOptions) {
var options = userOptions || {};
this.opts = this.extendObj(options, {
logging: false,
ready: function (stack) {
document.body.appendChild(stack.canvas);
},
storageReady: function(obj){
obj.Renderer(obj.contextStacks);
},
iframeDefault: "default",
flashCanvasPath: "http://html2canvas.hertzen.com/external/flashcanvas/flashcanvas.js",
renderViewport: false,
reorderZ: true,
throttle:true,
letterRendering:false,
proxyUrl: null,
logger: function(a){
if (window.console && window.console.log){
window.console.log(a);
}else{
alert(a);
}
},
canvasWidth:0,
canvasHeight:0,
useOverflow: true,
renderOrder: "canvas flash html"
});
this.element = el;
var imageLoaded,
canvas,
ctx,
bgx,
bgy,
image;
this.imagesLoaded = 0;
this.images = [];
this.fontData = [];
this.numDraws = 0;
this.contextStacks = [];
this.ignoreElements = "IFRAME|OBJECT|PARAM";
this.needReorder = false;
this.blockElements = new RegExp("(BR|PARAM)");
this.pageOrigin = window.location.protocol + window.location.host;
this.queue = [];
this.ignoreRe = new RegExp("("+this.ignoreElements+")");
this.support = {
rangeBounds: false
};
// Test whether we can use ranges to measure bounding boxes
// Opera doesn't provide valid bounds.height/bottom even though it supports the method.
if (document.createRange){
var r = document.createRange();
//this.support.rangeBounds = new Boolean(r.getBoundingClientRect);
if (r.getBoundingClientRect){
var testElement = document.createElement('boundtest');
testElement.style.height = "123px";
testElement.style.display = "block";
document.getElementsByTagName('body')[0].appendChild(testElement);
r.selectNode(testElement);
var rangeBounds = r.getBoundingClientRect();
var rangeHeight = rangeBounds.height;
if (rangeHeight==123){
this.support.rangeBounds = true;
}
document.getElementsByTagName('body')[0].removeChild(testElement);
}
}
// Start script
this.init();
return this;
}
html2canvas.prototype.init = function(){
var _ = this;
/*
this.ctx = new this.stackingContext($(document).width(),$(document).height());
if (!this.ctx){
// canvas not initialized, let's kill it here
this.log('Canvas not available');
return;
}
this.canvas = this.ctx.canvas;
*/
this.log('Finding background-images');
this.images.push('start');
this.getImages(this.element);
this.log('Finding images');
// console.log(this.element.ownerDocument);
this.each(this.element.ownerDocument.images,function(i,e){
_.preloadImage(_.getAttr(e,'src'));
});
this.images.splice(0,1);
// console.log(this.images);
if (this.images.length == 0){
this.start();
}
}
/*
* Check whether all assets have been loaded and start traversing the DOM
*/
html2canvas.prototype.start = function(){
// console.log(this.images);
if (this.images.length == 0 || this.imagesLoaded==this.images.length/2){
this.log('Finished loading '+this.imagesLoaded+' images, Started parsing');
this.bodyOverflow = document.getElementsByTagName('body')[0].style.overflow;
document.getElementsByTagName('body')[0].style.overflow = "hidden";
var rootStack = new this.storageContext($(document).width(),$(document).height());
rootStack.opacity = this.getCSS(this.element,"opacity");
var stack = this.newElement(this.element,rootStack);
this.parseElement(this.element,stack);
}
}
html2canvas.prototype.stackingContext = function(width,height){
this.canvas = document.createElement('canvas');
this.canvas.width = width;
this.canvas.height = width;
if (!this.canvas.getContext){
// TODO include Flashcanvas
/*
var script = document.createElement('script');
script.type = "text/javascript";
script.src = this.opts.flashCanvasPath;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(script, s);
if (typeof FlashCanvas != "undefined") {
FlashCanvas.initElement(this.canvas);
this.ctx = this.canvas.getContext('2d');
} */
}else{
this.ctx = this.canvas.getContext('2d');
}
// set common settings for canvas
this.ctx.textBaseline = "bottom";
return this.ctx;
}
html2canvas.prototype.storageContext = function(width,height){
this.storage = [];
this.width = width;
this.height = height;
//this.zIndex;
// todo simplify this whole section
this.fillRect = function(x, y, w, h){
this.storage.push(
{
type: "function",
name:"fillRect",
arguments:[x,y,w,h]
});
};
this.drawImage = function(image,sx,sy,sw,sh,dx,dy,dw,dh){
this.storage.push(
{
type: "function",
name:"drawImage",
arguments:[image,sx,sy,sw,sh,dx,dy,dw,dh]
});
};
this.fillText = function(currentText,x,y){
this.storage.push(
{
type: "function",
name:"fillText",
arguments:[currentText,x,y]
});
}
return this;
}
/*
* Finished rendering, send callback
*/
html2canvas.prototype.finish = function(){
this.log("Finished rendering");
document.getElementsByTagName('body')[0].style.overflow = this.bodyOverflow;
/*
if (this.opts.renderViewport){
// let's crop it to viewport only then
var newCanvas = document.createElement('canvas');
var newctx = newCanvas.getContext('2d');
newCanvas.width = window.innerWidth;
newCanvas.height = window.innerHeight;
}*/
this.opts.ready(this);
}

View File

@ -1,226 +0,0 @@
html2canvas.prototype.newElement = function(el,parentStack){
var bounds = this.getBounds(el);
var x = bounds.left;
var y = bounds.top;
var w = bounds.width;
var h = bounds.height;
var _ = this,
image;
var bgcolor = this.getCSS(el,"background-color");
var cssPosition = this.getCSS(el,"position");
parentStack = parentStack || {};
//var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode);
var zindex = this.setZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode);
//console.log(el.nodeName+":"+zindex+":"+this.getCSS(el,"position")+":"+this.numDraws+":"+this.getCSS(el,"z-index"))
var opacity = this.getCSS(el,"opacity");
var stack = {
ctx: new this.storageContext(),
zIndex: zindex,
opacity: opacity*parentStack.opacity,
cssPosition: cssPosition
};
// TODO correct overflow for absolute content residing under a static position
if (parentStack.clip){
stack.clip = $.extend({}, parentStack.clip);
//stack.clip = parentStack.clip;
stack.clip.height = stack.clip.height - parentStack.borders[2].width;
}
if (this.opts.useOverflow && /(hidden|scroll|auto)/.test(this.getCSS(el,"overflow")) && !/(BODY)/i.test(el.nodeName)){
if (stack.clip){
stack.clip = this.clipBounds(stack.clip,bounds);
}else{
stack.clip = bounds;
}
}
/*
var stackLength = this.contextStacks.push(stack);
var ctx = this.contextStacks[stackLength-1].ctx;
*/
var stackLength = zindex.children.push(stack);
var ctx = zindex.children[stackLength-1].ctx;
this.setContextVariable(ctx,"globalAlpha",stack.opacity);
// draw element borders
var borders = this.drawBorders(el, ctx, bounds);
stack.borders = borders;
// let's modify clip area for child elements, so borders dont get overwritten
/*
if (stack.clip){
stack.clip.width = stack.clip.width-(borders[1].width);
stack.clip.height = stack.clip.height-(borders[2].width);
}
*/
if (this.ignoreRe.test(el.nodeName) && this.opts.iframeDefault != "transparent"){
if (this.opts.iframeDefault=="default"){
bgcolor = "#efefef";
/*
* TODO write X over frame
*/
}else{
bgcolor = this.opts.iframeDefault;
}
}
// draw base element bgcolor
var bgbounds = {
left: x+borders[3].width,
top: y+borders[0].width,
width: w-(borders[1].width+borders[3].width),
height: h-(borders[0].width+borders[2].width)
};
//if (this.withinBounds(stack.clip,bgbounds)){
if (stack.clip){
bgbounds = this.clipBounds(bgbounds,stack.clip);
//}
}
if (bgbounds.height>0 && bgbounds.width>0){
this.newRect(
ctx,
bgbounds.left,
bgbounds.top,
bgbounds.width,
bgbounds.height,
bgcolor
);
this.drawBackground(el,bgbounds,ctx);
}
switch(el.nodeName){
case "IMG":
image = _.loadImage(_.getAttr(el,'src'));
if (image){
// console.log(image.width);
this.drawImage(
ctx,
image,
0, //sx
0, //sy
image.width, //sw
image.height, //sh
x+parseInt(_.getCSS(el,'padding-left'),10) + borders[3].width, //dx
y+parseInt(_.getCSS(el,'padding-top'),10) + borders[0].width, // dy
bounds.width - (borders[1].width + borders[3].width + parseInt(_.getCSS(el,'padding-left'),10) + parseInt(_.getCSS(el,'padding-right'),10)), //dw
bounds.height - (borders[0].width + borders[2].width + parseInt(_.getCSS(el,'padding-top'),10) + parseInt(_.getCSS(el,'padding-bottom'),10)) //dh
);
}else {
this.log("Error loading <img>:" + _.getAttr(el,'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(el.type) && el.value.length > 0){
this.renderFormValue(el,bounds,stack);
/*
this just doesn't work well enough
this.newText(el,{
nodeValue:el.value,
splitText: function(){
return this;
},
formValue:true
},stack);
*/
}
break;
case "TEXTAREA":
if (el.value.length > 0){
this.renderFormValue(el,bounds,stack);
}
break;
case "SELECT":
if (el.options.length > 0){
this.renderFormValue(el,bounds,stack);
}
break;
case "LI":
this.drawListItem(el,stack,bgbounds);
break;
}
// return this.contextStacks[stackLength-1];
return zindex.children[stackLength-1];
}
/*
* Function to draw the text on the canvas
*/
html2canvas.prototype.printText = function(currentText,x,y,ctx){
if (this.trim(currentText).length>0){
ctx.fillText(currentText,x,y);
this.numDraws++;
}
}
// Drawing a rectangle
html2canvas.prototype.newRect = function(ctx,x,y,w,h,bgcolor){
if (bgcolor!="transparent"){
this.setContextVariable(ctx,"fillStyle",bgcolor);
ctx.fillRect (x, y, w, h);
this.numDraws++;
}
}
html2canvas.prototype.drawImage = function(ctx,image,sx,sy,sw,sh,dx,dy,dw,dh){
ctx.drawImage(
image,
sx, //sx
sy, //sy
sw, //sw
sh, //sh
dx, //dx
dy, // dy
dw, //dw
dh //dh
);
this.numDraws++;
}

View File

@ -1,38 +0,0 @@
html2canvas.prototype.renderFormValue = function(el,bounds,stack){
var valueWrap = document.createElement('valuewrap'),
_ = this;
this.each(['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'],function(i,style){
valueWrap.style[style] = _.getCSS(el,style);
});
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";
if (el.nodeName == "SELECT"){
// TODO increase accuracy of text position
var textValue = el.options[el.selectedIndex].text;
} else{
var textValue = el.value;
}
var textNode = document.createTextNode(textValue);
valueWrap.appendChild(textNode);
$('body').append(valueWrap);
this.newText(el,textNode,stack);
$(valueWrap).remove();
}

View File

@ -1,137 +0,0 @@
/*
* Function to find all images from <img> and background-image
*/
html2canvas.prototype.getImages = function(el) {
var self = this;
if (!this.ignoreRe.test(el.nodeName)){
// TODO remove jQuery dependancy
this.each($(el).contents(),function(i,element){
var ignRe = new RegExp("("+this.ignoreElements+")");
if (!ignRe.test(element.nodeName)){
self.getImages(element);
}
})
}
if (el.nodeType==1 || typeof el.nodeType == "undefined"){
var background_image = this.getCSS(el,'background-image');
if (background_image && background_image != "1" && background_image != "none" && background_image.substring(0,7)!="-webkit" && background_image.substring(0,3)!="-o-" && background_image.substring(0,4)!="-moz"){
// TODO add multi image background support
var src = this.backgroundImageUrl(background_image.split(",")[0]);
this.preloadImage(src);
}
}
}
/*
* Load image from storage
*/
html2canvas.prototype.loadImage = function(src){
var imgIndex = this.getIndex(this.images,src);
if (imgIndex!=-1){
return this.images[imgIndex+1];
}else{
return false;
}
}
html2canvas.prototype.preloadImage = function(src){
if (this.getIndex(this.images,src)==-1){
if(src.substr(0, 5) == 'data:'){
//Base64 src
this.images.push(src);
var img = new Image();
img.src = src;
this.images.push(img);
this.imagesLoaded++;
this.start();
}else if (this.isSameOrigin(src)){
this.images.push(src);
// console.log('a'+src);
var img = new Image();
// TODO remove jQuery dependancy
var _ = this;
$(img).load(function(){
_.imagesLoaded++;
_.start();
});
img.onerror = function(){
_.images.splice(_.images.indexOf(img.src),2);
// _.imagesLoaded++;
_.start();
}
img.src = src;
this.images.push(img);
}else if (this.opts.proxyUrl){
// console.log('b'+src);
this.images.push(src);
var img = new Image();
this.proxyGetImage(src,img);
this.images.push(img);
}
}
}
html2canvas.prototype.proxyGetImage = function(url,img){
var _ = this;
var link = document.createElement("a");
link.href = url;
url = link.href; // work around for pages with base href="" set
// todo remove jQuery dependency and enable xhr2 requests where available (no need for base64 / json)
$.ajax({
data:{
xhr2:false,
url:url
},
url: this.opts.proxyUrl,
dataType: "jsonp",
success: function(a){
if (a.substring(0,6)=="error:"){
_.images.splice(_.images.indexOf(url),2);
_.start();
_.log('Proxy was unable to load '+url+' '+a);
}else{
// document.createElement(a);
// console.log(img);
img.onload = function(){
// console.log('w'+img.width);
_.imagesLoaded++;
_.start();
}
img.src = a;
}
},
error: function(){
_.images.splice(_.images.indexOf(url),2);
// _.imagesLoaded++;
_.start();
}
});
}

View File

@ -1,7 +0,0 @@
/*
* html2canvas v0.27 <http://html2canvas.hertzen.com>
* Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
* http://www.twitter.com/niklasvh
*
* Released under MIT License
*/

View File

@ -1,121 +0,0 @@
html2canvas.prototype.drawListItem = function(element,stack,elBounds){
var position = this.getCSS(element,"list-style-position",false);
var item = this.getListItem(element),
x,
y;
var type = this.getCSS(element,"list-style-type",false);
if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)){
// TODO remove jQuery dependency
var currentIndex = $(element).index()+1,
text;
if (type == "decimal"){
text = currentIndex;
}else if (type == "decimal-leading-zero"){
if (currentIndex.toString().length == 1){
text = currentIndex = "0" + currentIndex.toString();
}else{
text = currentIndex.toString();
}
}else if (type == "upper-roman"){
text = this.getListRoman(currentIndex);
}else if (type == "lower-roman"){
text = this.getListRoman(currentIndex).toLowerCase();
}else if (type == "lower-alpha"){
text = this.getListAlpha(currentIndex).toLowerCase();
}else if (type == "upper-alpha"){
text = this.getListAlpha(currentIndex);
}
text += ". ";
var listBounds = this.getListPosition(element,text);
if (position == "inside"){
this.setFont(stack.ctx,element,false);
x = elBounds.left;
}else{
return; /* TODO really need to figure out some more accurate way to try and find the position.
as defined in http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-position, it does not even have a specified "correct" position, so each browser
may display it whatever way it feels like.
"The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order"
*/
this.setFont(stack.ctx,element,true);
x = elBounds.left-10;
}
y = listBounds.bottom;
this.printText(text, x, y, stack.ctx);
}
}
html2canvas.prototype.getListPosition = function(element,val){
var boundElement = document.createElement("boundelement");
boundElement.style.display = "inline";
//boundElement.style.width = "1px";
//boundElement.style.height = "1px";
var type = element.style.listStyleType;
element.style.listStyleType = "none";
boundElement.appendChild(document.createTextNode(val));
element.insertBefore(boundElement,element.firstChild);
var bounds = this.getBounds(boundElement);
element.removeChild(boundElement);
element.style.listStyleType = type;
return bounds;
}
html2canvas.prototype.getListItem = function(element){
}
html2canvas.prototype.getListAlpha = function(number){
var tmp = "";
do{
var modulus = number % 26;
tmp = String.fromCharCode((modulus) + 64) + tmp;
number = number / 26;
}while((number*26) > 26);
return tmp;
}
html2canvas.prototype.getListRoman = 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 = "";
if (number <= 0 || number >= 4000) return;
for (var v=0; v<romanArray.length; v++) {
while (number >= decimal[v]) {
number -= decimal[v];
roman += romanArray[v];
}
}
return roman;
}

View File

@ -1,348 +0,0 @@
html2canvas.prototype.Renderer = function(queue){
var _ = this;
this.log('Renderer initiated');
this.each(this.opts.renderOrder.split(" "),function(i,renderer){
switch(renderer){
case "canvas":
_.canvas = document.createElement('canvas');
if (_.canvas.getContext){
_.canvasRenderer(queue);
_.log('Using canvas renderer');
return false;
}
break;
case "flash":
/*
var script = document.createElement('script');
script.type = "text/javascript";
script.src = _.opts.flashCanvasPath;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(script, s);
if (typeof FlashCanvas != "undefined") {
_.canvas = initCanvas(document.getElementById("testflash"));
FlashCanvas.initElement(_.canvas);
_.ctx = _.canvas.getContext("2d");
// _.canvas = document.createElement('canvas');
//
_.log('Using Flashcanvas renderer');
_.canvasRenderer(queue);
return false;
}
*/
break;
case "html":
// TODO add renderer
_.log("Using HTML renderer");
return false;
break;
}
});
// this.log('No renderer chosen, rendering quit');
return this;
// this.canvasRenderer(queue);
/*
if (!this.canvas.getContext){
}*/
// TODO include Flashcanvas
/*
var script = document.createElement('script');
script.type = "text/javascript";
script.src = this.opts.flashCanvasPath;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(script, s);
if (typeof FlashCanvas != "undefined") {
FlashCanvas.initElement(this.canvas);
this.ctx = this.canvas.getContext('2d');
} */
}
html2canvas.prototype.throttler = function(queue){
};
html2canvas.prototype.canvasRenderContext = function(storageContext,ctx){
// set common settings for canvas
ctx.textBaseline = "bottom";
var _ = this;
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){
_.each(storageContext.ctx.storage,function(a,renderItem){
switch(renderItem.type){
case "variable":
ctx[renderItem.name] = renderItem.arguments;
break;
case "function":
if (renderItem.name=="fillRect"){
ctx.fillRect(
renderItem.arguments[0],
renderItem.arguments[1],
renderItem.arguments[2],
renderItem.arguments[3]
);
}else if(renderItem.name=="fillText"){
// console.log(renderItem.arguments[0]);
ctx.fillText(renderItem.arguments[0],renderItem.arguments[1],renderItem.arguments[2]);
}else if(renderItem.name=="drawImage"){
// console.log(renderItem);
// console.log(renderItem.arguments[0].width);
if (renderItem.arguments[8] > 0 && renderItem.arguments[7]){
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]
);
}
}else{
_.log(renderItem);
}
break;
default:
}
});
}
if (storageContext.clip){
ctx.restore();
}
}
/*
html2canvas.prototype.canvasRenderContextChildren = function(storageContext,parentctx){
var ctx = storageContext.canvas.getContext('2d');
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,ctx);
for (var s = 0; s<this.queue.length;){
if (storageContext.parentStack && this.queue[s].canvas === storageContext.parentStack.canvas){
var substorageContext = this.queue.splice(s,1)[0];
if (substorageContext.ctx.storage[5] && substorageContext.ctx.storage[5].arguments[0]=="Highlights"){
console.log('ssssssadasda');
}
this.canvasRenderContextChildren(substorageContext,ctx);
// this.canvasRenderContext(substorageContext,ctx);
// this.canvasRenderStorage(this.queue,ctx);
}else{
s++;
}
}
if (storageContext.ctx.storage[5] && storageContext.ctx.storage[5].arguments[0]=="Highlights"){
$('body').append(parentctx.canvas);
}
//var parentctx = this.canvas.getContext("2d");
if (storageContext.canvas.width>0 && storageContext.canvas.height > 0){
parentctx.drawImage(storageContext.canvas,(storageContext.canvasPosition.x || 0),(storageContext.canvasPosition.y || 0));
}
}
*/
html2canvas.prototype.canvasRenderStorage = function(queue,parentctx){
for (var i = 0; i<queue.length;){
var storageContext = queue.splice(0,1)[0];
// storageContext.removeOverflow = storageContext.removeOverflow || {};
/*
if (storageContext.canvas){
this.canvasRenderContextChildren(storageContext,parentctx);
var ctx = storageContext.canvas.getContext('2d');
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,ctx);
for (var s = 0; s<this.queue.length;){
if (this.queue[s].canvas === storageContext.canvas){
var substorageContext = this.queue.splice(s,1)[0];
substorageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(substorageContext,ctx);
// this.canvasRenderStorage(this.queue,ctx);
}else{
s++;
}
}
//var parentctx = this.canvas.getContext("2d");
if (storageContext.canvas.width>0 && storageContext.canvas.height > 0){
parentctx.drawImage(storageContext.canvas,(storageContext.canvasPosition.x || 0),(storageContext.canvasPosition.y || 0));
}
ctx = parentctx;
}else{
*/
storageContext.canvasPosition = storageContext.canvasPosition || {};
this.canvasRenderContext(storageContext,parentctx);
// }
/*
if (storageContext.newCanvas){
var ctx = _.canvas.getContext("2d");
ctx.drawImage(canvas,(storageContext.removeOverflow.left || 0),(storageContext.removeOverflow.top || 0));
_.ctx = ctx;
}*/
}
}
html2canvas.prototype.canvasRenderer = function(queue){
var _ = this;
this.sortZ(this.zStack);
queue = this.queue;
//console.log(queue);
//queue = this.sortQueue(queue);
this.canvas.width = Math.max($(document).width(),this.opts.canvasWidth);
this.canvas.height = Math.max($(document).height(),this.opts.canvasHeight);
this.ctx = this.canvas.getContext("2d");
this.canvasRenderStorage(queue,this.ctx);
};
/*
* Sort elements based on z-index and position attributes
*/
/*
html2canvas.prototype.sortQueue = function(queue){
if (!this.opts.reorderZ || !this.needReorder) return queue;
var longest = 0;
this.each(queue,function(i,e){
if (longest<e.zIndex.length){
longest = e.zIndex.length;
}
});
var counter = 0;
//console.log(((queue.length).toString().length)-(count.length).toString().length);
this.each(queue,function(i,e){
var more = ((queue.length).toString().length)-((counter).toString().length);
while(longest>e.zIndex.length){
e.zIndex += "0";
}
e.zIndex = e.zIndex+counter;
while((longest+more+(counter).toString().length)>e.zIndex.length){
e.zIndex += "0";
}
counter++;
// console.log(e.zIndex);
});
queue = queue.sort(function(a,b){
if (a.zIndex < b.zIndex) return -1;
if (a.zIndex > b.zIndex) return 1;
return 0;
});
return queue;
}
*/
html2canvas.prototype.setContextVariable = function(ctx,variable,value){
if (!ctx.storage){
ctx[variable] = value;
}else{
ctx.storage.push(
{
type: "variable",
name:variable,
arguments:value
});
}
}

View File

@ -1,288 +0,0 @@
html2canvas.prototype.newText = function(el,textNode,stack,form){
var ctx = stack.ctx;
var family = this.getCSS(el,"font-family");
var size = this.getCSS(el,"font-size");
var color = this.getCSS(el,"color");
var text_decoration = this.getCSS(el,"text-decoration");
var text_align = this.getCSS(el,"text-align");
var letter_spacing = this.getCSS(el,"letter-spacing");
// apply text-transform:ation to the text
textNode.nodeValue = this.textTransform(textNode.nodeValue,this.getCSS(el,"text-transform"));
var text = this.trim(textNode.nodeValue);
//text = $.trim(text);
if (text.length>0){
if (text_decoration!="none"){
var metrics = this.fontMetrics(family,size);
}
var renderList,
renderWords = false;
text_align = text_align.replace(["-webkit-auto"],["auto"])
if (this.opts.letterRendering == false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){
// this.setContextVariable(ctx,"textAlign",text_align);
renderWords = true;
renderList = textNode.nodeValue.split(/(\b| )/);
}else{
// this.setContextVariable(ctx,"textAlign","left");
renderList = textNode.nodeValue.split("");
}
this.setFont(ctx,el,false);
/*
if (stack.clip){
ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height);
ctx.clip();
}
*/
var oldTextNode = textNode;
for(var c=0;c<renderList.length;c++){
// IE 9 bug
if (typeof oldTextNode.nodeValue != "string"){
continue;
}
// TODO only do the splitting for non-range prints
var newTextNode = oldTextNode.splitText(renderList[c].length);
if (text_decoration!="none" || this.trim(oldTextNode.nodeValue).length != 0){
if (this.support.rangeBounds){
// getBoundingClientRect is supported for ranges
if (document.createRange){
var range = document.createRange();
range.selectNode(oldTextNode);
}else{
// TODO add IE support
var range = document.body.createTextRange();
}
if (range.getBoundingClientRect()){
var bounds = range.getBoundingClientRect();
}else{
var bounds = {};
}
}else{
// it isn't supported, so let's wrap it inside an element instead and the bounds there
var parent = oldTextNode.parentNode;
var wrapElement = document.createElement('wrapper');
var backupText = oldTextNode.cloneNode(true);
wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement,oldTextNode);
var bounds = this.getBounds(wrapElement);
parent.replaceChild(backupText,wrapElement);
}
// console.log(range);
// console.log("'"+oldTextNode.nodeValue+"'"+bounds.left)
this.printText(oldTextNode.nodeValue,bounds.left,bounds.bottom,ctx);
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
this.newRect(ctx,bounds.left,Math.round(bounds.top+metrics.baseline+metrics.lineWidth),bounds.width,1,color);
break;
case "overline":
this.newRect(ctx,bounds.left,bounds.top,bounds.width,1,color);
break;
case "line-through":
// TODO try and find exact position for line-through
this.newRect(ctx,bounds.left,Math.ceil(bounds.top+metrics.middle+metrics.lineWidth),bounds.width,1,color);
break;
}
}
oldTextNode = newTextNode;
}
}
}
html2canvas.prototype.setFont = function(ctx,el,align){
var family = this.getCSS(el,"font-family");
var size = this.getCSS(el,"font-size");
var color = this.getCSS(el,"color");
var bold = this.getCSS(el,"font-weight");
var font_style = this.getCSS(el,"font-style");
var font_variant = this.getCSS(el,"font-variant");
switch(bold){
case 401:
bold = "bold";
break;
case 400:
bold = "normal";
break;
}
var font = font_variant+" "+bold+" "+font_style+" "+size+" "+family;
this.setContextVariable(ctx,"fillStyle",color);
this.setContextVariable(ctx,"font",font);
if (align){
this.setContextVariable(ctx,"textAlign","right");
}else{
this.setContextVariable(ctx,"textAlign","left");
}
}
/*
* Function to find baseline for font with a specicic size
*/
html2canvas.prototype.fontMetrics = function(font,fontSize){
var findMetrics = this.fontData.indexOf(font+"-"+fontSize);
if (findMetrics>-1){
return this.fontData[findMetrics+1];
}
var container = document.createElement('div');
document.getElementsByTagName('body')[0].appendChild(container);
// jquery to speed this up, TODO remove jquery dependancy
$(container).css({
visibility:'hidden',
fontFamily:font,
fontSize:fontSize,
margin:0,
padding:0
});
var img = document.createElement('img');
// TODO add another image
img.src = "http://html2canvas.hertzen.com/images/8.jpg";
img.width = 1;
img.height = 1;
$(img).css({
margin:0,
padding:0
});
var span = document.createElement('span');
$(span).css({
fontFamily:font,
fontSize:fontSize,
margin:0,
padding:0
});
span.appendChild(document.createTextNode('Hidden Text'));
container.appendChild(span);
container.appendChild(img);
var baseline = (img.offsetTop-span.offsetTop)+1;
container.removeChild(span);
container.appendChild(document.createTextNode('Hidden Text'));
$(container).css('line-height','normal');
$(img).css("vertical-align","super");
var middle = (img.offsetTop-container.offsetTop)+1;
var metricsObj = {
baseline: baseline,
lineWidth: 1,
middle: middle
};
this.fontData.push(font+"-"+fontSize);
this.fontData.push(metricsObj);
$(container).remove();
return metricsObj;
}
/*
* Function to apply text-transform attribute to text
*/
html2canvas.prototype.textTransform = function(text,transform){
switch(transform){
case "lowercase":
return text.toLowerCase();
break;
case "capitalize":
return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function(m,p1,p2){
return p1+p2.toUpperCase();
} );
break;
case "uppercase":
return text.toUpperCase();
break;
default:
return text;
}
}
/*
*Function to trim whitespace from text
*/
html2canvas.prototype.trim = function(text) {
return text.replace(/^\s*/, "").replace(/\s*$/, "");
}

View File

@ -1,46 +0,0 @@
html2canvas.prototype.parseElement = function(element,stack){
var _ = this;
this.each(element.children,function(index,el){
_.parsing(el,stack);
});
this.log('Render queue stored');
this.opts.storageReady(this);
this.finish();
}
html2canvas.prototype.parsing = function(el,stack){
if (this.getCSS(el,'display') != "none" && this.getCSS(el,'visibility')!="hidden"){
var _ = this;
stack = this.newElement(el,stack) || stack;
var ctx = stack.ctx;
if (!this.ignoreRe.test(el.nodeName)){
// TODO remove jQuery dependancy
this.each(this.contentsInZ(el),function(cid,node){
if (node.nodeType==1){
// element
_.parsing(node,stack);
}else if (node.nodeType==3){
_.newText(el,node,stack);
}
});
}
}
// }
}

View File

@ -1,285 +0,0 @@
// Simple logger
html2canvas.prototype.log = function(a){
if (this.opts.logging){
this.opts.logger(a);
}
}
html2canvas.prototype.withinBounds = function(src,dst){
if (!src) return true;
// return true;
return (
(src.left <= dst.left || dst.left+dst.width < src.left) &&
(src.top <= dst.top || dst.top+dst.height < src.top)
);
}
html2canvas.prototype.clipBounds = function(src,dst){
var x = Math.max(src.left,dst.left);
var y = Math.max(src.top,dst.top);
var x2 = Math.min((src.left+src.width),(dst.left+dst.width));
var y2 = Math.min((src.top+src.height),(dst.top+dst.height));
return {
left:x,
top:y,
width:x2-x,
height:y2-y
};
}
/**
* Function to provide bounds for element
* @return {Bounds} object with position and dimension information
*/
html2canvas.prototype.getBounds = function(el){
window.scroll(0,0);
if (el.getBoundingClientRect){
var clientRect = el.getBoundingClientRect();
var bounds = {};
// need to create new object, as clientrect bounds can't be modified, thanks pufuwozu
// 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 = clientRect.width;
bounds.height = clientRect.height;
return bounds;
}else{
// TODO remove jQuery dependancy
var p = $(el).offset();
return {
left: p.left + this.getCSS(el,"border-left-width",true),
top: p.top + this.getCSS(el,"border-top-width",true),
width:$(el).innerWidth(),
height:$(el).innerHeight()
}
}
}
/*
* Function for looping through array
*/
html2canvas.prototype.each = function(arrayLoop,callbackFunc){
callbackFunc = callbackFunc || function(){};
for (var i=0;i<arrayLoop.length;i++){
if (callbackFunc(i,arrayLoop[i]) === false) return;
}
}
/*
* Function to get childNodes of an element in the order they should be rendered (based on z-index)
* reference http://www.w3.org/TR/CSS21/zindex.html
*/
html2canvas.prototype.contentsInZ = function(el){
// TODO remove jQuery dependency
var contents = $(el).contents();
return contents;
}
/*
* Function for fetching the element attribute
*/
html2canvas.prototype.getAttr = function(el,attribute){
return el.getAttribute(attribute);
//return $(el).attr(attribute);
}
/*
* Function to extend object
*/
html2canvas.prototype.extendObj = function(options,defaults){
for (var key in options){
defaults[key] = options[key];
}
return defaults;
}
/*
*todo remove this function
html2canvas.prototype.leadingZero = function(num,size){
var s = "000000000" + num;
return s.substr(s.length-size);
}
*/
html2canvas.prototype.zContext = function(zindex){
return {
zindex: zindex,
children: []
}
}
html2canvas.prototype.setZ = function(zindex,position,parentZ,parentNode){
// TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them
if (!parentZ){
this.zStack = new this.zContext(0);
return this.zStack;
}
if (zindex!="auto"){
this.needReorder = true;
var newContext = new this.zContext(zindex);
parentZ.children.push(newContext);
return newContext;
}else {
return parentZ;
}
}
html2canvas.prototype.sortZ = function(zStack){
var subStacks = [];
var stackValues = [];
var _ = this;
this.each(zStack.children, function(i,stackChild){
if (stackChild.children && stackChild.children.length > 0){
subStacks.push(stackChild);
stackValues.push(stackChild.zindex);
}else{
_.queue.push(stackChild);
}
});
stackValues.sort(function(a,b){
return a - b
});
this.each(stackValues, function(i,zValue){
for (var s = 0;s<=subStacks.length;s++){
if (subStacks[s].zindex == zValue){
var stackChild = subStacks.splice(s,1);
_.sortZ(stackChild[0]);
break;
}
}
});
}
/*
*todo remove this function
html2canvas.prototype.formatZ = function(zindex,position,parentZ,parentNode){
if (!parentZ){
parentZ = "0";
}
if (position!="static" && parentZ.charAt(0)=="0"){
this.needReorder = true;
parentZ = "1"+parentZ.slice(1);
}
if (zindex=="auto"){
var parentPosition = this.getCSS(parentNode,"position");
if (parentPosition!="static" && typeof parentPosition != "undefined"){
zindex = 0;
}
else{
return parentZ;
}
}
var b = this.leadingZero(this.numDraws,9);
var s = this.leadingZero(zindex+1,9);
// var s = "000000000" + num;
return parentZ+""+""+s+""+b;
}
*/
/*
* Get element childNodes
*/
html2canvas.prototype.getContents = function(el){
return (el.nodeName == "iframe" ) ?
el.contentDocument || el.contentWindow.document :
el.childNodes;
}
/*
* Function for fetching the css attribute
* TODO remove jQuery dependancy
*/
html2canvas.prototype.getCSS = function(el,attribute,intOnly){
if (intOnly){
return parseInt($(el).css(attribute),10);
}else{
return $(el).css(attribute);
}
}
html2canvas.prototype.getIndex = function(array,src){
if (array.indexOf){
return array.indexOf(src);
}else{
for(var i = 0; i < array.length; i++){
if(this[i] == src) return i;
}
return -1;
}
}
html2canvas.prototype.isSameOrigin = function(url){
var link = document.createElement("a");
link.href = url;
return ((link.protocol + link.host) == this.pageOrigin);
}

93
src/__tests__/index.ts Normal file
View File

@ -0,0 +1,93 @@
import html2canvas from '../index';
import {CanvasRenderer} from '../render/canvas/canvas-renderer';
import {DocumentCloner} from '../dom/document-cloner';
import {COLORS} from '../css/types/color';
jest.mock('../core/logger');
jest.mock('../css/layout/bounds');
jest.mock('../dom/document-cloner');
jest.mock('../dom/node-parser', () => {
return {
isBodyElement: () => false,
isHTMLElement: () => false,
parseTree: jest.fn().mockImplementation(() => {
return {styles: {}};
})
};
});
jest.mock('../render/stacking-context');
jest.mock('../render/canvas/canvas-renderer');
describe('html2canvas', () => {
const element = {
ownerDocument: {
defaultView: {
pageXOffset: 12,
pageYOffset: 34
}
}
} as HTMLElement;
it('should render with an element', async () => {
DocumentCloner.destroy = jest.fn().mockReturnValue(true);
await html2canvas(element);
expect(CanvasRenderer).toHaveBeenLastCalledWith(
expect.objectContaining({
cache: expect.any(Object),
logger: expect.any(Object),
windowBounds: expect.objectContaining({left: 12, top: 34})
}),
expect.objectContaining({
backgroundColor: 0xffffffff,
scale: 1,
height: 50,
width: 200,
x: 0,
y: 0,
canvas: undefined
})
);
expect(DocumentCloner.destroy).toBeCalled();
});
it('should have transparent background with backgroundColor: null', async () => {
await html2canvas(element, {backgroundColor: null});
expect(CanvasRenderer).toHaveBeenLastCalledWith(
expect.anything(),
expect.objectContaining({
backgroundColor: COLORS.TRANSPARENT
})
);
});
it('should use existing canvas when given as option', async () => {
const canvas = {} as HTMLCanvasElement;
await html2canvas(element, {canvas});
expect(CanvasRenderer).toHaveBeenLastCalledWith(
expect.anything(),
expect.objectContaining({
canvas
})
);
});
it('should not remove cloned window when removeContainer: false', async () => {
DocumentCloner.destroy = jest.fn();
await html2canvas(element, {removeContainer: false});
expect(CanvasRenderer).toHaveBeenLastCalledWith(
expect.anything(),
expect.objectContaining({
backgroundColor: 0xffffffff,
scale: 1,
height: 50,
width: 200,
x: 0,
y: 0,
canvas: undefined
})
);
expect(DocumentCloner.destroy).not.toBeCalled();
});
});

View File

@ -0,0 +1 @@
export class CacheStorage {}

View File

@ -0,0 +1,19 @@
import {logger, Logger} from './logger';
export class Context {
readonly logger: Logger = logger;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly _cache: {[key: string]: Promise<any>} = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly cache: any;
constructor() {
this.cache = {
addImage: jest.fn().mockImplementation((src: string): Promise<void> => {
const result = Promise.resolve();
this._cache[src] = result;
return result;
})
};
}
}

View File

@ -0,0 +1,8 @@
export const FEATURES = {
SUPPORT_RANGE_BOUNDS: true,
SUPPORT_SVG_DRAWING: true,
SUPPORT_FOREIGNOBJECT_DRAWING: true,
SUPPORT_CORS_IMAGES: true,
SUPPORT_RESPONSE_TYPE: true,
SUPPORT_CORS_XHR: true
};

View File

@ -0,0 +1,22 @@
export class Logger {
// eslint-disable-next-line @typescript-eslint/no-empty-function
debug(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
static create(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
static destroy(): void {}
static getInstance(): Logger {
return logger;
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
info(): void {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
error(): void {}
}
export const logger = new Logger();

View File

@ -0,0 +1,273 @@
import {deepStrictEqual, fail} from 'assert';
import {FEATURES} from '../features';
import {CacheStorage} from '../cache-storage';
import {Context} from '../context';
import {Bounds} from '../../css/layout/bounds';
const proxy = 'http://example.com/proxy';
const createMockContext = (origin: string, opts = {}) => {
const context = {
location: {
href: origin
},
document: {
createElement(_name: string) {
let _href = '';
return {
set href(value: string) {
_href = value;
},
get href() {
return _href;
},
get protocol() {
return new URL(_href).protocol;
},
get hostname() {
return new URL(_href).hostname;
},
get port() {
return new URL(_href).port;
}
};
}
}
};
CacheStorage.setContext(context as Window);
return new Context(
{
logging: false,
imageTimeout: 0,
useCORS: false,
allowTaint: false,
proxy,
...opts
},
new Bounds(0, 0, 0, 0)
);
};
const images: ImageMock[] = [];
const xhr: XMLHttpRequestMock[] = [];
const sleep = async (timeout: number) => await new Promise((resolve) => setTimeout(resolve, timeout));
class ImageMock {
src?: string;
crossOrigin?: string;
onload?: () => void;
constructor() {
images.push(this);
}
}
class XMLHttpRequestMock {
sent: boolean;
status: number;
timeout: number;
method?: string;
url?: string;
response?: string;
onload?: () => void;
ontimeout?: () => void;
constructor() {
this.sent = false;
this.status = 500;
this.timeout = 5000;
xhr.push(this);
}
async load(status: number, response: string) {
this.response = response;
this.status = status;
if (this.onload) {
this.onload();
}
await sleep(0);
}
open(method: string, url: string) {
this.method = method;
this.url = url;
}
send() {
this.sent = true;
}
}
Object.defineProperty(global, 'Image', {value: ImageMock, writable: true});
Object.defineProperty(global, 'XMLHttpRequest', {
value: XMLHttpRequestMock,
writable: true
});
const setFeatures = (opts: {[key: string]: boolean} = {}) => {
const defaults: {[key: string]: boolean} = {
SUPPORT_SVG_DRAWING: true,
SUPPORT_CORS_IMAGES: true,
SUPPORT_CORS_XHR: true,
SUPPORT_RESPONSE_TYPE: false
};
Object.keys(defaults).forEach((key) => {
Object.defineProperty(FEATURES, key, {
value: typeof opts[key] === 'boolean' ? opts[key] : defaults[key],
writable: true
});
});
};
describe('cache-storage', () => {
beforeEach(() => setFeatures());
afterEach(() => {
xhr.splice(0, xhr.length);
images.splice(0, images.length);
});
it('addImage adds images to cache', async () => {
const {cache} = createMockContext('http://example.com', {proxy: null});
await cache.addImage('http://example.com/test.jpg');
await cache.addImage('http://example.com/test2.jpg');
deepStrictEqual(images.length, 2);
deepStrictEqual(images[0].src, 'http://example.com/test.jpg');
deepStrictEqual(images[1].src, 'http://example.com/test2.jpg');
});
it('addImage should not add duplicate entries', async () => {
const {cache} = createMockContext('http://example.com');
await cache.addImage('http://example.com/test.jpg');
await cache.addImage('http://example.com/test.jpg');
deepStrictEqual(images.length, 1);
deepStrictEqual(images[0].src, 'http://example.com/test.jpg');
});
describe('svg', () => {
it('should add svg images correctly', async () => {
const {cache} = createMockContext('http://example.com');
await cache.addImage('http://example.com/test.svg');
await cache.addImage('http://example.com/test2.svg');
deepStrictEqual(images.length, 2);
deepStrictEqual(images[0].src, 'http://example.com/test.svg');
deepStrictEqual(images[1].src, 'http://example.com/test2.svg');
});
it('should omit svg images if not supported', async () => {
setFeatures({SUPPORT_SVG_DRAWING: false});
const {cache} = createMockContext('http://example.com');
await cache.addImage('http://example.com/test.svg');
await cache.addImage('http://example.com/test2.svg');
deepStrictEqual(images.length, 0);
});
});
describe('cross-origin', () => {
it('addImage should not add images it cannot load/render', async () => {
const {cache} = createMockContext('http://example.com', {
proxy: undefined
});
await cache.addImage('http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images.length, 0);
});
it('addImage should add images if tainting enabled', async () => {
const {cache} = createMockContext('http://example.com', {
allowTaint: true,
proxy: undefined
});
await cache.addImage('http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images.length, 1);
deepStrictEqual(images[0].src, 'http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images[0].crossOrigin, undefined);
});
it('addImage should add images if cors enabled', async () => {
const {cache} = createMockContext('http://example.com', {useCORS: true});
await cache.addImage('http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images.length, 1);
deepStrictEqual(images[0].src, 'http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images[0].crossOrigin, 'anonymous');
});
it('addImage should not add images if cors enabled but not supported', async () => {
setFeatures({SUPPORT_CORS_IMAGES: false});
const {cache} = createMockContext('http://example.com', {
useCORS: true,
proxy: undefined
});
await cache.addImage('http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images.length, 0);
});
it('addImage should not add images to proxy if cors enabled', async () => {
const {cache} = createMockContext('http://example.com', {useCORS: true});
await cache.addImage('http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images.length, 1);
deepStrictEqual(images[0].src, 'http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(images[0].crossOrigin, 'anonymous');
});
it('addImage should use proxy ', async () => {
const {cache} = createMockContext('http://example.com');
await cache.addImage('http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(xhr.length, 1);
deepStrictEqual(
xhr[0].url,
`${proxy}?url=${encodeURIComponent('http://html2canvas.hertzen.com/test.jpg')}&responseType=text`
);
await xhr[0].load(200, '<data response>');
deepStrictEqual(images.length, 1);
deepStrictEqual(images[0].src, '<data response>');
});
it('proxy should respect imageTimeout', async () => {
const {cache} = createMockContext('http://example.com', {
imageTimeout: 10
});
await cache.addImage('http://html2canvas.hertzen.com/test.jpg');
deepStrictEqual(xhr.length, 1);
deepStrictEqual(
xhr[0].url,
`${proxy}?url=${encodeURIComponent('http://html2canvas.hertzen.com/test.jpg')}&responseType=text`
);
deepStrictEqual(xhr[0].timeout, 10);
if (xhr[0].ontimeout) {
xhr[0].ontimeout();
}
try {
await cache.match('http://html2canvas.hertzen.com/test.jpg');
fail('Expected result to timeout');
} catch (e) {}
});
});
it('match should return cache entry', async () => {
const {cache} = createMockContext('http://example.com');
await cache.addImage('http://example.com/test.jpg');
if (images[0].onload) {
images[0].onload();
}
const response = await cache.match('http://example.com/test.jpg');
deepStrictEqual(response.src, 'http://example.com/test.jpg');
});
it('image should respect imageTimeout', async () => {
const {cache} = createMockContext('http://example.com', {imageTimeout: 10});
cache.addImage('http://example.com/test.jpg');
try {
await cache.match('http://example.com/test.jpg');
fail('Expected result to timeout');
} catch (e) {}
});
});

View File

@ -0,0 +1,30 @@
import {Logger} from '../logger';
describe('logger', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let infoSpy: any;
beforeEach(() => {
infoSpy = jest.spyOn(console, 'info').mockImplementation(() => {
// do nothing
});
});
afterEach(() => {
infoSpy.mockRestore();
});
it('should call console.info when logger enabled', () => {
const id = Math.random().toString();
const logger = new Logger({id, enabled: true});
logger.info('testing');
expect(infoSpy).toHaveBeenLastCalledWith(id, expect.stringMatching(/\d+ms/), 'testing');
});
it("shouldn't call console.info when logger disabled", () => {
const id = Math.random().toString();
const logger = new Logger({id, enabled: false});
logger.info('testing');
expect(infoSpy).not.toHaveBeenCalled();
});
});

1
src/core/bitwise.ts Normal file
View File

@ -0,0 +1 @@
export const contains = (bit: number, value: number): boolean => (bit & value) !== 0;

177
src/core/cache-storage.ts Normal file
View File

@ -0,0 +1,177 @@
import {FEATURES} from './features';
import {Context} from './context';
export class CacheStorage {
private static _link?: HTMLAnchorElement;
private static _origin = 'about:blank';
static getOrigin(url: string): string {
const link = CacheStorage._link;
if (!link) {
return 'about:blank';
}
link.href = url;
link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/
return link.protocol + link.hostname + link.port;
}
static isSameOrigin(src: string): boolean {
return CacheStorage.getOrigin(src) === CacheStorage._origin;
}
static setContext(window: Window): void {
CacheStorage._link = window.document.createElement('a');
CacheStorage._origin = CacheStorage.getOrigin(window.location.href);
}
}
export interface ResourceOptions {
imageTimeout: number;
useCORS: boolean;
allowTaint: boolean;
proxy?: string;
}
export class Cache {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private readonly _cache: {[key: string]: Promise<any>} = {};
constructor(private readonly context: Context, private readonly _options: ResourceOptions) {}
addImage(src: string): Promise<void> {
const result = Promise.resolve();
if (this.has(src)) {
return result;
}
if (isBlobImage(src) || isRenderable(src)) {
(this._cache[src] = this.loadImage(src)).catch(() => {
// prevent unhandled rejection
});
return result;
}
return result;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
match(src: string): Promise<any> {
return this._cache[src];
}
private async loadImage(key: string) {
const isSameOrigin = CacheStorage.isSameOrigin(key);
const useCORS =
!isInlineImage(key) && this._options.useCORS === true && FEATURES.SUPPORT_CORS_IMAGES && !isSameOrigin;
const useProxy =
!isInlineImage(key) &&
!isSameOrigin &&
!isBlobImage(key) &&
typeof this._options.proxy === 'string' &&
FEATURES.SUPPORT_CORS_XHR &&
!useCORS;
if (
!isSameOrigin &&
this._options.allowTaint === false &&
!isInlineImage(key) &&
!isBlobImage(key) &&
!useProxy &&
!useCORS
) {
return;
}
let src = key;
if (useProxy) {
src = await this.proxy(src);
}
this.context.logger.debug(`Added image ${key.substring(0, 256)}`);
return await new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
//ios safari 10.3 taints canvas with data urls unless crossOrigin is set to anonymous
if (isInlineBase64Image(src) || useCORS) {
img.crossOrigin = 'anonymous';
}
img.src = src;
if (img.complete === true) {
// Inline XML images may fail to parse, throwing an Error later on
setTimeout(() => resolve(img), 500);
}
if (this._options.imageTimeout > 0) {
setTimeout(
() => reject(`Timed out (${this._options.imageTimeout}ms) loading image`),
this._options.imageTimeout
);
}
});
}
private has(key: string): boolean {
return typeof this._cache[key] !== 'undefined';
}
keys(): Promise<string[]> {
return Promise.resolve(Object.keys(this._cache));
}
private proxy(src: string): Promise<string> {
const proxy = this._options.proxy;
if (!proxy) {
throw new Error('No proxy defined');
}
const key = src.substring(0, 256);
return new Promise((resolve, reject) => {
const responseType = FEATURES.SUPPORT_RESPONSE_TYPE ? 'blob' : 'text';
const xhr = new XMLHttpRequest();
xhr.onload = () => {
if (xhr.status === 200) {
if (responseType === 'text') {
resolve(xhr.response);
} else {
const reader = new FileReader();
reader.addEventListener('load', () => resolve(reader.result as string), false);
reader.addEventListener('error', (e) => reject(e), false);
reader.readAsDataURL(xhr.response);
}
} else {
reject(`Failed to proxy resource ${key} with status code ${xhr.status}`);
}
};
xhr.onerror = reject;
const queryString = proxy.indexOf('?') > -1 ? '&' : '?';
xhr.open('GET', `${proxy}${queryString}url=${encodeURIComponent(src)}&responseType=${responseType}`);
if (responseType !== 'text' && xhr instanceof XMLHttpRequest) {
xhr.responseType = responseType;
}
if (this._options.imageTimeout) {
const timeout = this._options.imageTimeout;
xhr.timeout = timeout;
xhr.ontimeout = () => reject(`Timed out (${timeout}ms) proxying ${key}`);
}
xhr.send();
});
}
}
const INLINE_SVG = /^data:image\/svg\+xml/i;
const INLINE_BASE64 = /^data:image\/.*;base64,/i;
const INLINE_IMG = /^data:image\/.*/i;
const isRenderable = (src: string): boolean => FEATURES.SUPPORT_SVG_DRAWING || !isSVG(src);
const isInlineImage = (src: string): boolean => INLINE_IMG.test(src);
const isInlineBase64Image = (src: string): boolean => INLINE_BASE64.test(src);
const isBlobImage = (src: string): boolean => src.substr(0, 4) === 'blob';
const isSVG = (src: string): boolean => src.substr(-3).toLowerCase() === 'svg' || INLINE_SVG.test(src);

21
src/core/context.ts Normal file
View File

@ -0,0 +1,21 @@
import {Logger} from './logger';
import {Cache, ResourceOptions} from './cache-storage';
import {Bounds} from '../css/layout/bounds';
export type ContextOptions = {
logging: boolean;
cache?: Cache;
} & ResourceOptions;
export class Context {
private readonly instanceName = `#${Context.instanceCount++}`;
readonly logger: Logger;
readonly cache: Cache;
private static instanceCount = 1;
constructor(options: ContextOptions, public windowBounds: Bounds) {
this.logger = new Logger({id: this.instanceName, enabled: options.logging});
this.cache = options.cache ?? new Cache(this, options);
}
}

29
src/core/debugger.ts Normal file
View File

@ -0,0 +1,29 @@
const elementDebuggerAttribute = 'data-html2canvas-debug';
export const enum DebuggerType {
NONE,
ALL,
CLONE,
PARSE,
RENDER
}
const getElementDebugType = (element: Element): DebuggerType => {
const attribute = element.getAttribute(elementDebuggerAttribute);
switch (attribute) {
case 'all':
return DebuggerType.ALL;
case 'clone':
return DebuggerType.CLONE;
case 'parse':
return DebuggerType.PARSE;
case 'render':
return DebuggerType.RENDER;
default:
return DebuggerType.NONE;
}
};
export const isDebugging = (element: Element, type: Omit<DebuggerType, DebuggerType.NONE>): boolean => {
const elementType = getElementDebugType(element);
return elementType === DebuggerType.ALL || type === elementType;
};

222
src/core/features.ts Normal file
View File

@ -0,0 +1,222 @@
import {fromCodePoint, toCodePoints} from 'css-line-break';
const testRangeBounds = (document: Document) => {
const TEST_HEIGHT = 123;
if (document.createRange) {
const range = document.createRange();
if (range.getBoundingClientRect) {
const testElement = document.createElement('boundtest');
testElement.style.height = `${TEST_HEIGHT}px`;
testElement.style.display = 'block';
document.body.appendChild(testElement);
range.selectNode(testElement);
const rangeBounds = range.getBoundingClientRect();
const rangeHeight = Math.round(rangeBounds.height);
document.body.removeChild(testElement);
if (rangeHeight === TEST_HEIGHT) {
return true;
}
}
}
return false;
};
const testIOSLineBreak = (document: Document) => {
const testElement = document.createElement('boundtest');
testElement.style.width = '50px';
testElement.style.display = 'block';
testElement.style.fontSize = '12px';
testElement.style.letterSpacing = '0px';
testElement.style.wordSpacing = '0px';
document.body.appendChild(testElement);
const range = document.createRange();
testElement.innerHTML = typeof ''.repeat === 'function' ? '&#128104;'.repeat(10) : '';
const node = testElement.firstChild as Text;
const textList = toCodePoints(node.data).map((i) => fromCodePoint(i));
let offset = 0;
let prev: DOMRect = {} as DOMRect;
// ios 13 does not handle range getBoundingClientRect line changes correctly #2177
const supports = textList.every((text, i) => {
range.setStart(node, offset);
range.setEnd(node, offset + text.length);
const rect = range.getBoundingClientRect();
offset += text.length;
const boundAhead = rect.x > prev.x || rect.y > prev.y;
prev = rect;
if (i === 0) {
return true;
}
return boundAhead;
});
document.body.removeChild(testElement);
return supports;
};
const testCORS = (): boolean => typeof new Image().crossOrigin !== 'undefined';
const testResponseType = (): boolean => typeof new XMLHttpRequest().responseType === 'string';
const testSVG = (document: Document): boolean => {
const img = new Image();
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
return false;
}
img.src = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>`;
try {
ctx.drawImage(img, 0, 0);
canvas.toDataURL();
} catch (e) {
return false;
}
return true;
};
const isGreenPixel = (data: Uint8ClampedArray): boolean =>
data[0] === 0 && data[1] === 255 && data[2] === 0 && data[3] === 255;
const testForeignObject = (document: Document): Promise<boolean> => {
const canvas = document.createElement('canvas');
const size = 100;
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
if (!ctx) {
return Promise.reject(false);
}
ctx.fillStyle = 'rgb(0, 255, 0)';
ctx.fillRect(0, 0, size, size);
const img = new Image();
const greenImageSrc = canvas.toDataURL();
img.src = greenImageSrc;
const svg = createForeignObjectSVG(size, size, 0, 0, img);
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, size, size);
return loadSerializedSVG(svg)
.then((img: HTMLImageElement) => {
ctx.drawImage(img, 0, 0);
const data = ctx.getImageData(0, 0, size, size).data;
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, size, size);
const node = document.createElement('div');
node.style.backgroundImage = `url(${greenImageSrc})`;
node.style.height = `${size}px`;
// Firefox 55 does not render inline <img /> tags
return isGreenPixel(data)
? loadSerializedSVG(createForeignObjectSVG(size, size, 0, 0, node))
: Promise.reject(false);
})
.then((img: HTMLImageElement) => {
ctx.drawImage(img, 0, 0);
// Edge does not render background-images
return isGreenPixel(ctx.getImageData(0, 0, size, size).data);
})
.catch(() => false);
};
export const createForeignObjectSVG = (
width: number,
height: number,
x: number,
y: number,
node: Node
): SVGForeignObjectElement => {
const xmlns = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(xmlns, 'svg');
const foreignObject = document.createElementNS(xmlns, 'foreignObject');
svg.setAttributeNS(null, 'width', width.toString());
svg.setAttributeNS(null, 'height', height.toString());
foreignObject.setAttributeNS(null, 'width', '100%');
foreignObject.setAttributeNS(null, 'height', '100%');
foreignObject.setAttributeNS(null, 'x', x.toString());
foreignObject.setAttributeNS(null, 'y', y.toString());
foreignObject.setAttributeNS(null, 'externalResourcesRequired', 'true');
svg.appendChild(foreignObject);
foreignObject.appendChild(node);
return svg;
};
export const loadSerializedSVG = (svg: Node): Promise<HTMLImageElement> => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(svg))}`;
});
};
export const FEATURES = {
get SUPPORT_RANGE_BOUNDS(): boolean {
'use strict';
const value = testRangeBounds(document);
Object.defineProperty(FEATURES, 'SUPPORT_RANGE_BOUNDS', {value});
return value;
},
get SUPPORT_WORD_BREAKING(): boolean {
'use strict';
const value = FEATURES.SUPPORT_RANGE_BOUNDS && testIOSLineBreak(document);
Object.defineProperty(FEATURES, 'SUPPORT_WORD_BREAKING', {value});
return value;
},
get SUPPORT_SVG_DRAWING(): boolean {
'use strict';
const value = testSVG(document);
Object.defineProperty(FEATURES, 'SUPPORT_SVG_DRAWING', {value});
return value;
},
get SUPPORT_FOREIGNOBJECT_DRAWING(): Promise<boolean> {
'use strict';
const value =
typeof Array.from === 'function' && typeof window.fetch === 'function'
? testForeignObject(document)
: Promise.resolve(false);
Object.defineProperty(FEATURES, 'SUPPORT_FOREIGNOBJECT_DRAWING', {value});
return value;
},
get SUPPORT_CORS_IMAGES(): boolean {
'use strict';
const value = testCORS();
Object.defineProperty(FEATURES, 'SUPPORT_CORS_IMAGES', {value});
return value;
},
get SUPPORT_RESPONSE_TYPE(): boolean {
'use strict';
const value = testResponseType();
Object.defineProperty(FEATURES, 'SUPPORT_RESPONSE_TYPE', {value});
return value;
},
get SUPPORT_CORS_XHR(): boolean {
'use strict';
const value = 'withCredentials' in new XMLHttpRequest();
Object.defineProperty(FEATURES, 'SUPPORT_CORS_XHR', {value});
return value;
},
get SUPPORT_NATIVE_TEXT_SEGMENTATION(): boolean {
'use strict';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const value = !!(typeof Intl !== 'undefined' && (Intl as any).Segmenter);
Object.defineProperty(FEATURES, 'SUPPORT_NATIVE_TEXT_SEGMENTATION', {value});
return value;
}
};

72
src/core/logger.ts Normal file
View File

@ -0,0 +1,72 @@
export interface LoggerOptions {
id: string;
enabled: boolean;
}
export class Logger {
static instances: {[key: string]: Logger} = {};
private readonly id: string;
private readonly enabled: boolean;
private readonly start: number;
constructor({id, enabled}: LoggerOptions) {
this.id = id;
this.enabled = enabled;
this.start = Date.now();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
debug(...args: unknown[]): void {
if (this.enabled) {
// eslint-disable-next-line no-console
if (typeof window !== 'undefined' && window.console && typeof console.debug === 'function') {
// eslint-disable-next-line no-console
console.debug(this.id, `${this.getTime()}ms`, ...args);
} else {
this.info(...args);
}
}
}
getTime(): number {
return Date.now() - this.start;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
info(...args: unknown[]): void {
if (this.enabled) {
// eslint-disable-next-line no-console
if (typeof window !== 'undefined' && window.console && typeof console.info === 'function') {
// eslint-disable-next-line no-console
console.info(this.id, `${this.getTime()}ms`, ...args);
}
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
warn(...args: unknown[]): void {
if (this.enabled) {
// eslint-disable-next-line no-console
if (typeof window !== 'undefined' && window.console && typeof console.warn === 'function') {
// eslint-disable-next-line no-console
console.warn(this.id, `${this.getTime()}ms`, ...args);
} else {
this.info(...args);
}
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error(...args: unknown[]): void {
if (this.enabled) {
// eslint-disable-next-line no-console
if (typeof window !== 'undefined' && window.console && typeof console.error === 'function') {
// eslint-disable-next-line no-console
console.error(this.id, `${this.getTime()}ms`, ...args);
} else {
this.info(...args);
}
}
}
}

1
src/core/util.ts Normal file
View File

@ -0,0 +1 @@
export const SMALL_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';

View File

@ -0,0 +1,49 @@
import {CSSValue} from './syntax/parser';
import {CSSTypes} from './types';
import {Context} from '../core/context';
export const enum PropertyDescriptorParsingType {
VALUE,
LIST,
IDENT_VALUE,
TYPE_VALUE,
TOKEN_VALUE
}
export interface IPropertyDescriptor {
name: string;
type: PropertyDescriptorParsingType;
initialValue: string;
prefix: boolean;
}
export interface IPropertyIdentValueDescriptor<T> extends IPropertyDescriptor {
type: PropertyDescriptorParsingType.IDENT_VALUE;
parse: (context: Context, token: string) => T;
}
export interface IPropertyTypeValueDescriptor extends IPropertyDescriptor {
type: PropertyDescriptorParsingType.TYPE_VALUE;
format: CSSTypes;
}
export interface IPropertyValueDescriptor<T> extends IPropertyDescriptor {
type: PropertyDescriptorParsingType.VALUE;
parse: (context: Context, token: CSSValue) => T;
}
export interface IPropertyListDescriptor<T> extends IPropertyDescriptor {
type: PropertyDescriptorParsingType.LIST;
parse: (context: Context, tokens: CSSValue[]) => T;
}
export interface IPropertyTokenValueDescriptor extends IPropertyDescriptor {
type: PropertyDescriptorParsingType.TOKEN_VALUE;
}
export type CSSPropertyDescriptor<T> =
| IPropertyValueDescriptor<T>
| IPropertyListDescriptor<T>
| IPropertyIdentValueDescriptor<T>
| IPropertyTypeValueDescriptor
| IPropertyTokenValueDescriptor;

View File

@ -0,0 +1,7 @@
import {CSSValue} from './syntax/parser';
import {Context} from '../core/context';
export interface ITypeDescriptor<T> {
name: string;
parse: (context: Context, value: CSSValue) => T;
}

321
src/css/index.ts Normal file
View File

@ -0,0 +1,321 @@
import {CSSPropertyDescriptor, PropertyDescriptorParsingType} from './IPropertyDescriptor';
import {backgroundClip} from './property-descriptors/background-clip';
import {backgroundColor} from './property-descriptors/background-color';
import {backgroundImage} from './property-descriptors/background-image';
import {backgroundOrigin} from './property-descriptors/background-origin';
import {backgroundPosition} from './property-descriptors/background-position';
import {backgroundRepeat} from './property-descriptors/background-repeat';
import {backgroundSize} from './property-descriptors/background-size';
import {
borderBottomColor,
borderLeftColor,
borderRightColor,
borderTopColor
} from './property-descriptors/border-color';
import {
borderBottomLeftRadius,
borderBottomRightRadius,
borderTopLeftRadius,
borderTopRightRadius
} from './property-descriptors/border-radius';
import {
borderBottomStyle,
borderLeftStyle,
borderRightStyle,
borderTopStyle
} from './property-descriptors/border-style';
import {
borderBottomWidth,
borderLeftWidth,
borderRightWidth,
borderTopWidth
} from './property-descriptors/border-width';
import {color} from './property-descriptors/color';
import {direction} from './property-descriptors/direction';
import {display, DISPLAY} from './property-descriptors/display';
import {float, FLOAT} from './property-descriptors/float';
import {letterSpacing} from './property-descriptors/letter-spacing';
import {lineBreak} from './property-descriptors/line-break';
import {lineHeight} from './property-descriptors/line-height';
import {listStyleImage} from './property-descriptors/list-style-image';
import {listStylePosition} from './property-descriptors/list-style-position';
import {listStyleType} from './property-descriptors/list-style-type';
import {marginBottom, marginLeft, marginRight, marginTop} from './property-descriptors/margin';
import {overflow, OVERFLOW} from './property-descriptors/overflow';
import {overflowWrap} from './property-descriptors/overflow-wrap';
import {paddingBottom, paddingLeft, paddingRight, paddingTop} from './property-descriptors/padding';
import {textAlign} from './property-descriptors/text-align';
import {position, POSITION} from './property-descriptors/position';
import {textShadow} from './property-descriptors/text-shadow';
import {textTransform} from './property-descriptors/text-transform';
import {transform} from './property-descriptors/transform';
import {transformOrigin} from './property-descriptors/transform-origin';
import {visibility, VISIBILITY} from './property-descriptors/visibility';
import {wordBreak} from './property-descriptors/word-break';
import {zIndex} from './property-descriptors/z-index';
import {CSSValue, isIdentToken, Parser} from './syntax/parser';
import {Tokenizer} from './syntax/tokenizer';
import {Color, color as colorType, isTransparent} from './types/color';
import {angle} from './types/angle';
import {image} from './types/image';
import {time} from './types/time';
import {opacity} from './property-descriptors/opacity';
import {textDecorationColor} from './property-descriptors/text-decoration-color';
import {textDecorationLine} from './property-descriptors/text-decoration-line';
import {isLengthPercentage, LengthPercentage, ZERO_LENGTH} from './types/length-percentage';
import {fontFamily} from './property-descriptors/font-family';
import {fontSize} from './property-descriptors/font-size';
import {isLength} from './types/length';
import {fontWeight} from './property-descriptors/font-weight';
import {fontVariant} from './property-descriptors/font-variant';
import {fontStyle} from './property-descriptors/font-style';
import {contains} from '../core/bitwise';
import {content} from './property-descriptors/content';
import {counterIncrement} from './property-descriptors/counter-increment';
import {counterReset} from './property-descriptors/counter-reset';
import {duration} from './property-descriptors/duration';
import {quotes} from './property-descriptors/quotes';
import {boxShadow} from './property-descriptors/box-shadow';
import {paintOrder} from './property-descriptors/paint-order';
import {webkitTextStrokeColor} from './property-descriptors/webkit-text-stroke-color';
import {webkitTextStrokeWidth} from './property-descriptors/webkit-text-stroke-width';
import {Context} from '../core/context';
export class CSSParsedDeclaration {
animationDuration: ReturnType<typeof duration.parse>;
backgroundClip: ReturnType<typeof backgroundClip.parse>;
backgroundColor: Color;
backgroundImage: ReturnType<typeof backgroundImage.parse>;
backgroundOrigin: ReturnType<typeof backgroundOrigin.parse>;
backgroundPosition: ReturnType<typeof backgroundPosition.parse>;
backgroundRepeat: ReturnType<typeof backgroundRepeat.parse>;
backgroundSize: ReturnType<typeof backgroundSize.parse>;
borderTopColor: Color;
borderRightColor: Color;
borderBottomColor: Color;
borderLeftColor: Color;
borderTopLeftRadius: ReturnType<typeof borderTopLeftRadius.parse>;
borderTopRightRadius: ReturnType<typeof borderTopRightRadius.parse>;
borderBottomRightRadius: ReturnType<typeof borderBottomRightRadius.parse>;
borderBottomLeftRadius: ReturnType<typeof borderBottomLeftRadius.parse>;
borderTopStyle: ReturnType<typeof borderTopStyle.parse>;
borderRightStyle: ReturnType<typeof borderRightStyle.parse>;
borderBottomStyle: ReturnType<typeof borderBottomStyle.parse>;
borderLeftStyle: ReturnType<typeof borderLeftStyle.parse>;
borderTopWidth: ReturnType<typeof borderTopWidth.parse>;
borderRightWidth: ReturnType<typeof borderRightWidth.parse>;
borderBottomWidth: ReturnType<typeof borderBottomWidth.parse>;
borderLeftWidth: ReturnType<typeof borderLeftWidth.parse>;
boxShadow: ReturnType<typeof boxShadow.parse>;
color: Color;
direction: ReturnType<typeof direction.parse>;
display: ReturnType<typeof display.parse>;
float: ReturnType<typeof float.parse>;
fontFamily: ReturnType<typeof fontFamily.parse>;
fontSize: LengthPercentage;
fontStyle: ReturnType<typeof fontStyle.parse>;
fontVariant: ReturnType<typeof fontVariant.parse>;
fontWeight: ReturnType<typeof fontWeight.parse>;
letterSpacing: ReturnType<typeof letterSpacing.parse>;
lineBreak: ReturnType<typeof lineBreak.parse>;
lineHeight: CSSValue;
listStyleImage: ReturnType<typeof listStyleImage.parse>;
listStylePosition: ReturnType<typeof listStylePosition.parse>;
listStyleType: ReturnType<typeof listStyleType.parse>;
marginTop: CSSValue;
marginRight: CSSValue;
marginBottom: CSSValue;
marginLeft: CSSValue;
opacity: ReturnType<typeof opacity.parse>;
overflowX: OVERFLOW;
overflowY: OVERFLOW;
overflowWrap: ReturnType<typeof overflowWrap.parse>;
paddingTop: LengthPercentage;
paddingRight: LengthPercentage;
paddingBottom: LengthPercentage;
paddingLeft: LengthPercentage;
paintOrder: ReturnType<typeof paintOrder.parse>;
position: ReturnType<typeof position.parse>;
textAlign: ReturnType<typeof textAlign.parse>;
textDecorationColor: Color;
textDecorationLine: ReturnType<typeof textDecorationLine.parse>;
textShadow: ReturnType<typeof textShadow.parse>;
textTransform: ReturnType<typeof textTransform.parse>;
transform: ReturnType<typeof transform.parse>;
transformOrigin: ReturnType<typeof transformOrigin.parse>;
visibility: ReturnType<typeof visibility.parse>;
webkitTextStrokeColor: Color;
webkitTextStrokeWidth: ReturnType<typeof webkitTextStrokeWidth.parse>;
wordBreak: ReturnType<typeof wordBreak.parse>;
zIndex: ReturnType<typeof zIndex.parse>;
constructor(context: Context, declaration: CSSStyleDeclaration) {
this.animationDuration = parse(context, duration, declaration.animationDuration);
this.backgroundClip = parse(context, backgroundClip, declaration.backgroundClip);
this.backgroundColor = parse(context, backgroundColor, declaration.backgroundColor);
this.backgroundImage = parse(context, backgroundImage, declaration.backgroundImage);
this.backgroundOrigin = parse(context, backgroundOrigin, declaration.backgroundOrigin);
this.backgroundPosition = parse(context, backgroundPosition, declaration.backgroundPosition);
this.backgroundRepeat = parse(context, backgroundRepeat, declaration.backgroundRepeat);
this.backgroundSize = parse(context, backgroundSize, declaration.backgroundSize);
this.borderTopColor = parse(context, borderTopColor, declaration.borderTopColor);
this.borderRightColor = parse(context, borderRightColor, declaration.borderRightColor);
this.borderBottomColor = parse(context, borderBottomColor, declaration.borderBottomColor);
this.borderLeftColor = parse(context, borderLeftColor, declaration.borderLeftColor);
this.borderTopLeftRadius = parse(context, borderTopLeftRadius, declaration.borderTopLeftRadius);
this.borderTopRightRadius = parse(context, borderTopRightRadius, declaration.borderTopRightRadius);
this.borderBottomRightRadius = parse(context, borderBottomRightRadius, declaration.borderBottomRightRadius);
this.borderBottomLeftRadius = parse(context, borderBottomLeftRadius, declaration.borderBottomLeftRadius);
this.borderTopStyle = parse(context, borderTopStyle, declaration.borderTopStyle);
this.borderRightStyle = parse(context, borderRightStyle, declaration.borderRightStyle);
this.borderBottomStyle = parse(context, borderBottomStyle, declaration.borderBottomStyle);
this.borderLeftStyle = parse(context, borderLeftStyle, declaration.borderLeftStyle);
this.borderTopWidth = parse(context, borderTopWidth, declaration.borderTopWidth);
this.borderRightWidth = parse(context, borderRightWidth, declaration.borderRightWidth);
this.borderBottomWidth = parse(context, borderBottomWidth, declaration.borderBottomWidth);
this.borderLeftWidth = parse(context, borderLeftWidth, declaration.borderLeftWidth);
this.boxShadow = parse(context, boxShadow, declaration.boxShadow);
this.color = parse(context, color, declaration.color);
this.direction = parse(context, direction, declaration.direction);
this.display = parse(context, display, declaration.display);
this.float = parse(context, float, declaration.cssFloat);
this.fontFamily = parse(context, fontFamily, declaration.fontFamily);
this.fontSize = parse(context, fontSize, declaration.fontSize);
this.fontStyle = parse(context, fontStyle, declaration.fontStyle);
this.fontVariant = parse(context, fontVariant, declaration.fontVariant);
this.fontWeight = parse(context, fontWeight, declaration.fontWeight);
this.letterSpacing = parse(context, letterSpacing, declaration.letterSpacing);
this.lineBreak = parse(context, lineBreak, declaration.lineBreak);
this.lineHeight = parse(context, lineHeight, declaration.lineHeight);
this.listStyleImage = parse(context, listStyleImage, declaration.listStyleImage);
this.listStylePosition = parse(context, listStylePosition, declaration.listStylePosition);
this.listStyleType = parse(context, listStyleType, declaration.listStyleType);
this.marginTop = parse(context, marginTop, declaration.marginTop);
this.marginRight = parse(context, marginRight, declaration.marginRight);
this.marginBottom = parse(context, marginBottom, declaration.marginBottom);
this.marginLeft = parse(context, marginLeft, declaration.marginLeft);
this.opacity = parse(context, opacity, declaration.opacity);
const overflowTuple = parse(context, overflow, declaration.overflow);
this.overflowX = overflowTuple[0];
this.overflowY = overflowTuple[overflowTuple.length > 1 ? 1 : 0];
this.overflowWrap = parse(context, overflowWrap, declaration.overflowWrap);
this.paddingTop = parse(context, paddingTop, declaration.paddingTop);
this.paddingRight = parse(context, paddingRight, declaration.paddingRight);
this.paddingBottom = parse(context, paddingBottom, declaration.paddingBottom);
this.paddingLeft = parse(context, paddingLeft, declaration.paddingLeft);
this.paintOrder = parse(context, paintOrder, declaration.paintOrder);
this.position = parse(context, position, declaration.position);
this.textAlign = parse(context, textAlign, declaration.textAlign);
this.textDecorationColor = parse(
context,
textDecorationColor,
declaration.textDecorationColor ?? declaration.color
);
this.textDecorationLine = parse(
context,
textDecorationLine,
declaration.textDecorationLine ?? declaration.textDecoration
);
this.textShadow = parse(context, textShadow, declaration.textShadow);
this.textTransform = parse(context, textTransform, declaration.textTransform);
this.transform = parse(context, transform, declaration.transform);
this.transformOrigin = parse(context, transformOrigin, declaration.transformOrigin);
this.visibility = parse(context, visibility, declaration.visibility);
this.webkitTextStrokeColor = parse(context, webkitTextStrokeColor, declaration.webkitTextStrokeColor);
this.webkitTextStrokeWidth = parse(context, webkitTextStrokeWidth, declaration.webkitTextStrokeWidth);
this.wordBreak = parse(context, wordBreak, declaration.wordBreak);
this.zIndex = parse(context, zIndex, declaration.zIndex);
}
isVisible(): boolean {
return this.display > 0 && this.opacity > 0 && this.visibility === VISIBILITY.VISIBLE;
}
isTransparent(): boolean {
return isTransparent(this.backgroundColor);
}
isTransformed(): boolean {
return this.transform !== null;
}
isPositioned(): boolean {
return this.position !== POSITION.STATIC;
}
isPositionedWithZIndex(): boolean {
return this.isPositioned() && !this.zIndex.auto;
}
isFloating(): boolean {
return this.float !== FLOAT.NONE;
}
isInlineLevel(): boolean {
return (
contains(this.display, DISPLAY.INLINE) ||
contains(this.display, DISPLAY.INLINE_BLOCK) ||
contains(this.display, DISPLAY.INLINE_FLEX) ||
contains(this.display, DISPLAY.INLINE_GRID) ||
contains(this.display, DISPLAY.INLINE_LIST_ITEM) ||
contains(this.display, DISPLAY.INLINE_TABLE)
);
}
}
export class CSSParsedPseudoDeclaration {
content: ReturnType<typeof content.parse>;
quotes: ReturnType<typeof quotes.parse>;
constructor(context: Context, declaration: CSSStyleDeclaration) {
this.content = parse(context, content, declaration.content);
this.quotes = parse(context, quotes, declaration.quotes);
}
}
export class CSSParsedCounterDeclaration {
counterIncrement: ReturnType<typeof counterIncrement.parse>;
counterReset: ReturnType<typeof counterReset.parse>;
constructor(context: Context, declaration: CSSStyleDeclaration) {
this.counterIncrement = parse(context, counterIncrement, declaration.counterIncrement);
this.counterReset = parse(context, counterReset, declaration.counterReset);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const parse = (context: Context, descriptor: CSSPropertyDescriptor<any>, style?: string | null) => {
const tokenizer = new Tokenizer();
const value = style !== null && typeof style !== 'undefined' ? style.toString() : descriptor.initialValue;
tokenizer.write(value);
const parser = new Parser(tokenizer.read());
switch (descriptor.type) {
case PropertyDescriptorParsingType.IDENT_VALUE:
const token = parser.parseComponentValue();
return descriptor.parse(context, isIdentToken(token) ? token.value : descriptor.initialValue);
case PropertyDescriptorParsingType.VALUE:
return descriptor.parse(context, parser.parseComponentValue());
case PropertyDescriptorParsingType.LIST:
return descriptor.parse(context, parser.parseComponentValues());
case PropertyDescriptorParsingType.TOKEN_VALUE:
return parser.parseComponentValue();
case PropertyDescriptorParsingType.TYPE_VALUE:
switch (descriptor.format) {
case 'angle':
return angle.parse(context, parser.parseComponentValue());
case 'color':
return colorType.parse(context, parser.parseComponentValue());
case 'image':
return image.parse(context, parser.parseComponentValue());
case 'length':
const length = parser.parseComponentValue();
return isLength(length) ? length : ZERO_LENGTH;
case 'length-percentage':
const value = parser.parseComponentValue();
return isLengthPercentage(value) ? value : ZERO_LENGTH;
case 'time':
return time.parse(context, parser.parseComponentValue());
}
break;
}
};

View File

@ -0,0 +1,4 @@
export const {Bounds} = jest.requireActual('../bounds');
export const parseBounds = (): typeof Bounds => {
return new Bounds(0, 0, 200, 50);
};

58
src/css/layout/bounds.ts Normal file
View File

@ -0,0 +1,58 @@
import {Context} from '../../core/context';
export class Bounds {
constructor(readonly left: number, readonly top: number, readonly width: number, readonly height: number) {}
add(x: number, y: number, w: number, h: number): Bounds {
return new Bounds(this.left + x, this.top + y, this.width + w, this.height + h);
}
static fromClientRect(context: Context, clientRect: ClientRect): Bounds {
return new Bounds(
clientRect.left + context.windowBounds.left,
clientRect.top + context.windowBounds.top,
clientRect.width,
clientRect.height
);
}
static fromDOMRectList(context: Context, domRectList: DOMRectList): Bounds {
const domRect = Array.from(domRectList).find((rect) => rect.width !== 0);
return domRect
? new Bounds(
domRect.left + context.windowBounds.left,
domRect.top + context.windowBounds.top,
domRect.width,
domRect.height
)
: Bounds.EMPTY;
}
static EMPTY = new Bounds(0, 0, 0, 0);
}
export const parseBounds = (context: Context, node: Element): Bounds => {
return Bounds.fromClientRect(context, node.getBoundingClientRect());
};
export const parseDocumentSize = (document: Document): Bounds => {
const body = document.body;
const documentElement = document.documentElement;
if (!body || !documentElement) {
throw new Error(`Unable to get document size`);
}
const width = Math.max(
Math.max(body.scrollWidth, documentElement.scrollWidth),
Math.max(body.offsetWidth, documentElement.offsetWidth),
Math.max(body.clientWidth, documentElement.clientWidth)
);
const height = Math.max(
Math.max(body.scrollHeight, documentElement.scrollHeight),
Math.max(body.offsetHeight, documentElement.offsetHeight),
Math.max(body.clientHeight, documentElement.clientHeight)
);
return new Bounds(0, 0, width, height);
};

158
src/css/layout/text.ts Normal file
View File

@ -0,0 +1,158 @@
import {OVERFLOW_WRAP} from '../property-descriptors/overflow-wrap';
import {CSSParsedDeclaration} from '../index';
import {fromCodePoint, LineBreaker, toCodePoints} from 'css-line-break';
import {splitGraphemes} from 'text-segmentation';
import {Bounds, parseBounds} from './bounds';
import {FEATURES} from '../../core/features';
import {Context} from '../../core/context';
export class TextBounds {
readonly text: string;
readonly bounds: Bounds;
constructor(text: string, bounds: Bounds) {
this.text = text;
this.bounds = bounds;
}
}
export const parseTextBounds = (
context: Context,
value: string,
styles: CSSParsedDeclaration,
node: Text
): TextBounds[] => {
const textList = breakText(value, styles);
const textBounds: TextBounds[] = [];
let offset = 0;
textList.forEach((text) => {
if (styles.textDecorationLine.length || text.trim().length > 0) {
if (FEATURES.SUPPORT_RANGE_BOUNDS) {
const clientRects = createRange(node, offset, text.length).getClientRects();
if (clientRects.length > 1) {
const subSegments = segmentGraphemes(text);
let subOffset = 0;
subSegments.forEach((subSegment) => {
textBounds.push(
new TextBounds(
subSegment,
Bounds.fromDOMRectList(
context,
createRange(node, subOffset + offset, subSegment.length).getClientRects()
)
)
);
subOffset += subSegment.length;
});
} else {
textBounds.push(new TextBounds(text, Bounds.fromDOMRectList(context, clientRects)));
}
} else {
const replacementNode = node.splitText(text.length);
textBounds.push(new TextBounds(text, getWrapperBounds(context, node)));
node = replacementNode;
}
} else if (!FEATURES.SUPPORT_RANGE_BOUNDS) {
node = node.splitText(text.length);
}
offset += text.length;
});
return textBounds;
};
const getWrapperBounds = (context: Context, node: Text): Bounds => {
const ownerDocument = node.ownerDocument;
if (ownerDocument) {
const wrapper = ownerDocument.createElement('html2canvaswrapper');
wrapper.appendChild(node.cloneNode(true));
const parentNode = node.parentNode;
if (parentNode) {
parentNode.replaceChild(wrapper, node);
const bounds = parseBounds(context, wrapper);
if (wrapper.firstChild) {
parentNode.replaceChild(wrapper.firstChild, wrapper);
}
return bounds;
}
}
return Bounds.EMPTY;
};
const createRange = (node: Text, offset: number, length: number): Range => {
const ownerDocument = node.ownerDocument;
if (!ownerDocument) {
throw new Error('Node has no owner document');
}
const range = ownerDocument.createRange();
range.setStart(node, offset);
range.setEnd(node, offset + length);
return range;
};
export const segmentGraphemes = (value: string): string[] => {
if (FEATURES.SUPPORT_NATIVE_TEXT_SEGMENTATION) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const segmenter = new (Intl as any).Segmenter(void 0, {granularity: 'grapheme'});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return Array.from(segmenter.segment(value)).map((segment: any) => segment.segment);
}
return splitGraphemes(value);
};
const segmentWords = (value: string, styles: CSSParsedDeclaration): string[] => {
if (FEATURES.SUPPORT_NATIVE_TEXT_SEGMENTATION) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const segmenter = new (Intl as any).Segmenter(void 0, {
granularity: 'word'
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return Array.from(segmenter.segment(value)).map((segment: any) => segment.segment);
}
return breakWords(value, styles);
};
const breakText = (value: string, styles: CSSParsedDeclaration): string[] => {
return styles.letterSpacing !== 0 ? segmentGraphemes(value) : segmentWords(value, styles);
};
// https://drafts.csswg.org/css-text/#word-separator
const wordSeparators = [0x0020, 0x00a0, 0x1361, 0x10100, 0x10101, 0x1039, 0x1091];
const breakWords = (str: string, styles: CSSParsedDeclaration): string[] => {
const breaker = LineBreaker(str, {
lineBreak: styles.lineBreak,
wordBreak: styles.overflowWrap === OVERFLOW_WRAP.BREAK_WORD ? 'break-word' : styles.wordBreak
});
const words = [];
let bk;
while (!(bk = breaker.next()).done) {
if (bk.value) {
const value = bk.value.slice();
const codePoints = toCodePoints(value);
let word = '';
codePoints.forEach((codePoint) => {
if (wordSeparators.indexOf(codePoint) === -1) {
word += fromCodePoint(codePoint);
} else {
if (word.length) {
words.push(word);
}
words.push(fromCodePoint(codePoint));
word = '';
}
});
if (word.length) {
words.push(word);
}
}
}
return words;
};

View File

@ -0,0 +1,59 @@
import {deepStrictEqual} from 'assert';
import {Parser} from '../../syntax/parser';
import {backgroundImage} from '../background-image';
import {CSSImageType} from '../../types/image';
import {pack} from '../../types/color';
import {deg} from '../../types/angle';
jest.mock('../../../core/context');
import {Context} from '../../../core/context';
jest.mock('../../../core/features');
const backgroundImageParse = (context: Context, value: string) =>
backgroundImage.parse(context, Parser.parseValues(value));
describe('property-descriptors', () => {
let context: Context;
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context = new Context({} as any, {} as any);
});
describe('background-image', () => {
it('none', () => {
deepStrictEqual(backgroundImageParse(context, 'none'), []);
expect(context.cache.addImage).not.toHaveBeenCalled();
});
it('url(test.jpg), url(test2.jpg)', () => {
deepStrictEqual(
backgroundImageParse(context, 'url(http://example.com/test.jpg), url(http://example.com/test2.jpg)'),
[
{url: 'http://example.com/test.jpg', type: CSSImageType.URL},
{url: 'http://example.com/test2.jpg', type: CSSImageType.URL}
]
);
expect(context.cache.addImage).toHaveBeenCalledWith('http://example.com/test.jpg');
expect(context.cache.addImage).toHaveBeenCalledWith('http://example.com/test2.jpg');
});
it(`linear-gradient(to bottom, rgba(255,255,0,0.5), rgba(0,0,255,0.5)), url('https://html2canvas.hertzen.com')`, () =>
deepStrictEqual(
backgroundImageParse(
context,
`linear-gradient(to bottom, rgba(255,255,0,0.5), rgba(0,0,255,0.5)), url('https://html2canvas.hertzen.com')`
),
[
{
angle: deg(180),
type: CSSImageType.LINEAR_GRADIENT,
stops: [
{color: pack(255, 255, 0, 0.5), stop: null},
{color: pack(0, 0, 255, 0.5), stop: null}
]
},
{url: 'https://html2canvas.hertzen.com', type: CSSImageType.URL}
]
));
});
});

View File

@ -0,0 +1,25 @@
import {deepEqual} from 'assert';
import {Parser} from '../../syntax/parser';
import {fontFamily} from '../font-family';
import {Context} from '../../../core/context';
const fontFamilyParse = (value: string) => fontFamily.parse({} as Context, Parser.parseValues(value));
describe('property-descriptors', () => {
describe('font-family', () => {
it('sans-serif', () => deepEqual(fontFamilyParse('sans-serif'), ['sans-serif']));
it('great fonts 40 library', () =>
deepEqual(fontFamilyParse('great fonts 40 library'), ["'great fonts 40 library'"]));
it('preferred font, "quoted fallback font", font', () =>
deepEqual(fontFamilyParse('preferred font, "quoted fallback font", font'), [
"'preferred font'",
"'quoted fallback font'",
'font'
]));
it("'escaping test\\'s font'", () =>
deepEqual(fontFamilyParse("'escaping test\\'s font'"), ["'escaping test's font'"]));
});
});

View File

@ -0,0 +1,87 @@
import {deepStrictEqual} from 'assert';
import {Parser} from '../../syntax/parser';
import {paintOrder, PAINT_ORDER_LAYER} from '../paint-order';
import {Context} from '../../../core/context';
const paintOrderParse = (value: string) => paintOrder.parse({} as Context, Parser.parseValues(value));
describe('property-descriptors', () => {
describe('paint-order', () => {
it('none', () =>
deepStrictEqual(paintOrderParse('none'), [
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.MARKERS
]));
it('EMPTY', () =>
deepStrictEqual(paintOrderParse(''), [
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.MARKERS
]));
it('other values', () =>
deepStrictEqual(paintOrderParse('other values'), [
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.MARKERS
]));
it('normal', () =>
deepStrictEqual(paintOrderParse('normal'), [
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.MARKERS
]));
it('stroke', () =>
deepStrictEqual(paintOrderParse('stroke'), [
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.MARKERS
]));
it('fill', () =>
deepStrictEqual(paintOrderParse('fill'), [
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.MARKERS
]));
it('markers', () =>
deepStrictEqual(paintOrderParse('markers'), [
PAINT_ORDER_LAYER.MARKERS,
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.STROKE
]));
it('stroke fill', () =>
deepStrictEqual(paintOrderParse('stroke fill'), [
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.MARKERS
]));
it('markers stroke', () =>
deepStrictEqual(paintOrderParse('markers stroke'), [
PAINT_ORDER_LAYER.MARKERS,
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.FILL
]));
it('markers stroke fill', () =>
deepStrictEqual(paintOrderParse('markers stroke fill'), [
PAINT_ORDER_LAYER.MARKERS,
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.FILL
]));
it('stroke fill markers', () =>
deepStrictEqual(paintOrderParse('stroke fill markers'), [
PAINT_ORDER_LAYER.STROKE,
PAINT_ORDER_LAYER.FILL,
PAINT_ORDER_LAYER.MARKERS
]));
});
});

View File

@ -0,0 +1,94 @@
import {deepStrictEqual} from 'assert';
import {Parser} from '../../syntax/parser';
import {color, COLORS} from '../../types/color';
import {textShadow} from '../text-shadow';
import {FLAG_INTEGER, DimensionToken, TokenType} from '../../syntax/tokenizer';
import {ZERO_LENGTH} from '../../types/length-percentage';
import {Context} from '../../../core/context';
const textShadowParse = (value: string) => textShadow.parse({} as Context, Parser.parseValues(value));
const colorParse = (value: string) => color.parse({} as Context, Parser.parseValue(value));
const dimension = (number: number, unit: string): DimensionToken => ({
flags: FLAG_INTEGER,
number,
unit,
type: TokenType.DIMENSION_TOKEN
});
describe('property-descriptors', () => {
describe('text-shadow', () => {
it('none', () => deepStrictEqual(textShadowParse('none'), []));
it('1px 1px 2px pink', () =>
deepStrictEqual(textShadowParse('1px 1px 2px pink'), [
{
color: colorParse('pink'),
offsetX: dimension(1, 'px'),
offsetY: dimension(1, 'px'),
blur: dimension(2, 'px')
}
]));
it('#fc0 1px 0 10px', () =>
deepStrictEqual(textShadowParse('#fc0 1px 0 10px'), [
{
color: colorParse('#fc0'),
offsetX: dimension(1, 'px'),
offsetY: ZERO_LENGTH,
blur: dimension(10, 'px')
}
]));
it('5px 5px #558abb', () =>
deepStrictEqual(textShadowParse('5px 5px #558abb'), [
{
color: colorParse('#558abb'),
offsetX: dimension(5, 'px'),
offsetY: dimension(5, 'px'),
blur: ZERO_LENGTH
}
]));
it('white 2px 5px', () =>
deepStrictEqual(textShadowParse('white 2px 5px'), [
{
color: colorParse('#fff'),
offsetX: dimension(2, 'px'),
offsetY: dimension(5, 'px'),
blur: ZERO_LENGTH
}
]));
it('white 2px 5px', () =>
deepStrictEqual(textShadowParse('5px 10px'), [
{
color: COLORS.TRANSPARENT,
offsetX: dimension(5, 'px'),
offsetY: dimension(10, 'px'),
blur: ZERO_LENGTH
}
]));
it('1px 1px 2px red, 0 0 1em blue, 0 0 2em blue', () =>
deepStrictEqual(textShadowParse('1px 1px 2px red, 0 0 1em blue, 0 0 2em blue'), [
{
color: colorParse('red'),
offsetX: dimension(1, 'px'),
offsetY: dimension(1, 'px'),
blur: dimension(2, 'px')
},
{
color: colorParse('blue'),
offsetX: ZERO_LENGTH,
offsetY: ZERO_LENGTH,
blur: dimension(1, 'em')
},
{
color: colorParse('blue'),
offsetX: ZERO_LENGTH,
offsetY: ZERO_LENGTH,
blur: dimension(2, 'em')
}
]));
});
});

View File

@ -0,0 +1,18 @@
import {transform} from '../transform';
import {Parser} from '../../syntax/parser';
import {deepStrictEqual} from 'assert';
import {Context} from '../../../core/context';
const parseValue = (value: string) => transform.parse({} as Context, Parser.parseValue(value));
describe('property-descriptors', () => {
describe('transform', () => {
it('none', () => deepStrictEqual(parseValue('none'), null));
it('matrix(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)', () =>
deepStrictEqual(parseValue('matrix(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)'), [1, 2, 3, 4, 5, 6]));
it('matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)', () =>
deepStrictEqual(
parseValue('matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)'),
[1, 0, 0, 1, 0, 0]
));
});
});

View File

@ -0,0 +1,30 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isIdentToken} from '../syntax/parser';
import {Context} from '../../core/context';
export const enum BACKGROUND_CLIP {
BORDER_BOX = 0,
PADDING_BOX = 1,
CONTENT_BOX = 2
}
export type BackgroundClip = BACKGROUND_CLIP[];
export const backgroundClip: IPropertyListDescriptor<BackgroundClip> = {
name: 'background-clip',
initialValue: 'border-box',
prefix: false,
type: PropertyDescriptorParsingType.LIST,
parse: (_context: Context, tokens: CSSValue[]): BackgroundClip => {
return tokens.map((token) => {
if (isIdentToken(token)) {
switch (token.value) {
case 'padding-box':
return BACKGROUND_CLIP.PADDING_BOX;
case 'content-box':
return BACKGROUND_CLIP.CONTENT_BOX;
}
}
return BACKGROUND_CLIP.BORDER_BOX;
});
}
};

View File

@ -0,0 +1,9 @@
import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
export const backgroundColor: IPropertyTypeValueDescriptor = {
name: `background-color`,
initialValue: 'transparent',
prefix: false,
type: PropertyDescriptorParsingType.TYPE_VALUE,
format: 'color'
};

View File

@ -0,0 +1,27 @@
import {TokenType} from '../syntax/tokenizer';
import {ICSSImage, image, isSupportedImage} from '../types/image';
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, nonFunctionArgSeparator} from '../syntax/parser';
import {Context} from '../../core/context';
export const backgroundImage: IPropertyListDescriptor<ICSSImage[]> = {
name: 'background-image',
initialValue: 'none',
type: PropertyDescriptorParsingType.LIST,
prefix: false,
parse: (context: Context, tokens: CSSValue[]) => {
if (tokens.length === 0) {
return [];
}
const first = tokens[0];
if (first.type === TokenType.IDENT_TOKEN && first.value === 'none') {
return [];
}
return tokens
.filter((value) => nonFunctionArgSeparator(value) && isSupportedImage(value))
.map((value) => image.parse(context, value));
}
};

View File

@ -0,0 +1,31 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isIdentToken} from '../syntax/parser';
import {Context} from '../../core/context';
export const enum BACKGROUND_ORIGIN {
BORDER_BOX = 0,
PADDING_BOX = 1,
CONTENT_BOX = 2
}
export type BackgroundOrigin = BACKGROUND_ORIGIN[];
export const backgroundOrigin: IPropertyListDescriptor<BackgroundOrigin> = {
name: 'background-origin',
initialValue: 'border-box',
prefix: false,
type: PropertyDescriptorParsingType.LIST,
parse: (_context: Context, tokens: CSSValue[]): BackgroundOrigin => {
return tokens.map((token) => {
if (isIdentToken(token)) {
switch (token.value) {
case 'padding-box':
return BACKGROUND_ORIGIN.PADDING_BOX;
case 'content-box':
return BACKGROUND_ORIGIN.CONTENT_BOX;
}
}
return BACKGROUND_ORIGIN.BORDER_BOX;
});
}
};

View File

@ -0,0 +1,19 @@
import {PropertyDescriptorParsingType, IPropertyListDescriptor} from '../IPropertyDescriptor';
import {CSSValue, parseFunctionArgs} from '../syntax/parser';
import {isLengthPercentage, LengthPercentageTuple, parseLengthPercentageTuple} from '../types/length-percentage';
import {Context} from '../../core/context';
export type BackgroundPosition = BackgroundImagePosition[];
export type BackgroundImagePosition = LengthPercentageTuple;
export const backgroundPosition: IPropertyListDescriptor<BackgroundPosition> = {
name: 'background-position',
initialValue: '0% 0%',
type: PropertyDescriptorParsingType.LIST,
prefix: false,
parse: (_context: Context, tokens: CSSValue[]): BackgroundPosition => {
return parseFunctionArgs(tokens)
.map((values: CSSValue[]) => values.filter(isLengthPercentage))
.map(parseLengthPercentageTuple);
}
};

View File

@ -0,0 +1,44 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isIdentToken, parseFunctionArgs} from '../syntax/parser';
import {Context} from '../../core/context';
export type BackgroundRepeat = BACKGROUND_REPEAT[];
export const enum BACKGROUND_REPEAT {
REPEAT = 0,
NO_REPEAT = 1,
REPEAT_X = 2,
REPEAT_Y = 3
}
export const backgroundRepeat: IPropertyListDescriptor<BackgroundRepeat> = {
name: 'background-repeat',
initialValue: 'repeat',
prefix: false,
type: PropertyDescriptorParsingType.LIST,
parse: (_context: Context, tokens: CSSValue[]): BackgroundRepeat => {
return parseFunctionArgs(tokens)
.map((values) =>
values
.filter(isIdentToken)
.map((token) => token.value)
.join(' ')
)
.map(parseBackgroundRepeat);
}
};
const parseBackgroundRepeat = (value: string): BACKGROUND_REPEAT => {
switch (value) {
case 'no-repeat':
return BACKGROUND_REPEAT.NO_REPEAT;
case 'repeat-x':
case 'repeat no-repeat':
return BACKGROUND_REPEAT.REPEAT_X;
case 'repeat-y':
case 'no-repeat repeat':
return BACKGROUND_REPEAT.REPEAT_Y;
case 'repeat':
default:
return BACKGROUND_REPEAT.REPEAT;
}
};

View File

@ -0,0 +1,27 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isIdentToken, parseFunctionArgs} from '../syntax/parser';
import {isLengthPercentage, LengthPercentage} from '../types/length-percentage';
import {StringValueToken} from '../syntax/tokenizer';
import {Context} from '../../core/context';
export enum BACKGROUND_SIZE {
AUTO = 'auto',
CONTAIN = 'contain',
COVER = 'cover'
}
export type BackgroundSizeInfo = LengthPercentage | StringValueToken;
export type BackgroundSize = BackgroundSizeInfo[][];
export const backgroundSize: IPropertyListDescriptor<BackgroundSize> = {
name: 'background-size',
initialValue: '0',
prefix: false,
type: PropertyDescriptorParsingType.LIST,
parse: (_context: Context, tokens: CSSValue[]): BackgroundSize => {
return parseFunctionArgs(tokens).map((values) => values.filter(isBackgroundSizeInfoToken));
}
};
const isBackgroundSizeInfoToken = (value: CSSValue): value is BackgroundSizeInfo =>
isIdentToken(value) || isLengthPercentage(value);

View File

@ -0,0 +1,13 @@
import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
const borderColorForSide = (side: string): IPropertyTypeValueDescriptor => ({
name: `border-${side}-color`,
initialValue: 'transparent',
prefix: false,
type: PropertyDescriptorParsingType.TYPE_VALUE,
format: 'color'
});
export const borderTopColor: IPropertyTypeValueDescriptor = borderColorForSide('top');
export const borderRightColor: IPropertyTypeValueDescriptor = borderColorForSide('right');
export const borderBottomColor: IPropertyTypeValueDescriptor = borderColorForSide('bottom');
export const borderLeftColor: IPropertyTypeValueDescriptor = borderColorForSide('left');

View File

@ -0,0 +1,19 @@
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue} from '../syntax/parser';
import {isLengthPercentage, LengthPercentageTuple, parseLengthPercentageTuple} from '../types/length-percentage';
import {Context} from '../../core/context';
export type BorderRadius = LengthPercentageTuple;
const borderRadiusForSide = (side: string): IPropertyListDescriptor<BorderRadius> => ({
name: `border-radius-${side}`,
initialValue: '0 0',
prefix: false,
type: PropertyDescriptorParsingType.LIST,
parse: (_context: Context, tokens: CSSValue[]): BorderRadius =>
parseLengthPercentageTuple(tokens.filter(isLengthPercentage))
});
export const borderTopLeftRadius: IPropertyListDescriptor<BorderRadius> = borderRadiusForSide('top-left');
export const borderTopRightRadius: IPropertyListDescriptor<BorderRadius> = borderRadiusForSide('top-right');
export const borderBottomRightRadius: IPropertyListDescriptor<BorderRadius> = borderRadiusForSide('bottom-right');
export const borderBottomLeftRadius: IPropertyListDescriptor<BorderRadius> = borderRadiusForSide('bottom-left');

View File

@ -0,0 +1,34 @@
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {Context} from '../../core/context';
export const enum BORDER_STYLE {
NONE = 0,
SOLID = 1,
DASHED = 2,
DOTTED = 3,
DOUBLE = 4
}
const borderStyleForSide = (side: string): IPropertyIdentValueDescriptor<BORDER_STYLE> => ({
name: `border-${side}-style`,
initialValue: 'solid',
prefix: false,
type: PropertyDescriptorParsingType.IDENT_VALUE,
parse: (_context: Context, style: string): BORDER_STYLE => {
switch (style) {
case 'none':
return BORDER_STYLE.NONE;
case 'dashed':
return BORDER_STYLE.DASHED;
case 'dotted':
return BORDER_STYLE.DOTTED;
case 'double':
return BORDER_STYLE.DOUBLE;
}
return BORDER_STYLE.SOLID;
}
});
export const borderTopStyle: IPropertyIdentValueDescriptor<BORDER_STYLE> = borderStyleForSide('top');
export const borderRightStyle: IPropertyIdentValueDescriptor<BORDER_STYLE> = borderStyleForSide('right');
export const borderBottomStyle: IPropertyIdentValueDescriptor<BORDER_STYLE> = borderStyleForSide('bottom');
export const borderLeftStyle: IPropertyIdentValueDescriptor<BORDER_STYLE> = borderStyleForSide('left');

View File

@ -0,0 +1,20 @@
import {IPropertyValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue, isDimensionToken} from '../syntax/parser';
import {Context} from '../../core/context';
const borderWidthForSide = (side: string): IPropertyValueDescriptor<number> => ({
name: `border-${side}-width`,
initialValue: '0',
type: PropertyDescriptorParsingType.VALUE,
prefix: false,
parse: (_context: Context, token: CSSValue): number => {
if (isDimensionToken(token)) {
return token.number;
}
return 0;
}
});
export const borderTopWidth: IPropertyValueDescriptor<number> = borderWidthForSide('top');
export const borderRightWidth: IPropertyValueDescriptor<number> = borderWidthForSide('right');
export const borderBottomWidth: IPropertyValueDescriptor<number> = borderWidthForSide('bottom');
export const borderLeftWidth: IPropertyValueDescriptor<number> = borderWidthForSide('left');

View File

@ -0,0 +1,60 @@
import {PropertyDescriptorParsingType, IPropertyListDescriptor} from '../IPropertyDescriptor';
import {CSSValue, isIdentWithValue, parseFunctionArgs} from '../syntax/parser';
import {ZERO_LENGTH} from '../types/length-percentage';
import {color, Color} from '../types/color';
import {isLength, Length} from '../types/length';
import {Context} from '../../core/context';
export type BoxShadow = BoxShadowItem[];
interface BoxShadowItem {
inset: boolean;
color: Color;
offsetX: Length;
offsetY: Length;
blur: Length;
spread: Length;
}
export const boxShadow: IPropertyListDescriptor<BoxShadow> = {
name: 'box-shadow',
initialValue: 'none',
type: PropertyDescriptorParsingType.LIST,
prefix: false,
parse: (context: Context, tokens: CSSValue[]): BoxShadow => {
if (tokens.length === 1 && isIdentWithValue(tokens[0], 'none')) {
return [];
}
return parseFunctionArgs(tokens).map((values: CSSValue[]) => {
const shadow: BoxShadowItem = {
color: 0x000000ff,
offsetX: ZERO_LENGTH,
offsetY: ZERO_LENGTH,
blur: ZERO_LENGTH,
spread: ZERO_LENGTH,
inset: false
};
let c = 0;
for (let i = 0; i < values.length; i++) {
const token = values[i];
if (isIdentWithValue(token, 'inset')) {
shadow.inset = true;
} else if (isLength(token)) {
if (c === 0) {
shadow.offsetX = token;
} else if (c === 1) {
shadow.offsetY = token;
} else if (c === 2) {
shadow.blur = token;
} else {
shadow.spread = token;
}
c++;
} else {
shadow.color = color.parse(context, token);
}
}
return shadow;
});
}
};

View File

@ -0,0 +1,9 @@
import {IPropertyTypeValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
export const color: IPropertyTypeValueDescriptor = {
name: `color`,
initialValue: 'transparent',
prefix: false,
type: PropertyDescriptorParsingType.TYPE_VALUE,
format: 'color'
};

View File

@ -0,0 +1,26 @@
import {TokenType} from '../syntax/tokenizer';
import {IPropertyListDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
import {CSSValue} from '../syntax/parser';
import {Context} from '../../core/context';
export type Content = CSSValue[];
export const content: IPropertyListDescriptor<Content> = {
name: 'content',
initialValue: 'none',
type: PropertyDescriptorParsingType.LIST,
prefix: false,
parse: (_context: Context, tokens: CSSValue[]) => {
if (tokens.length === 0) {
return [];
}
const first = tokens[0];
if (first.type === TokenType.IDENT_TOKEN && first.value === 'none') {
return [];
}
return tokens;
}
};

Some files were not shown because too many files have changed in this diff Show More