html2canvas-pro 2.4.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +115 -13
  2. package/dist/html2canvas-pro.cjs +3246 -1601
  3. package/dist/html2canvas-pro.cjs.map +1 -1
  4. package/dist/html2canvas-pro.esm.js +3246 -1601
  5. package/dist/html2canvas-pro.esm.js.map +1 -1
  6. package/dist/html2canvas-pro.js +3242 -1597
  7. package/dist/html2canvas-pro.js.map +1 -1
  8. package/dist/html2canvas-pro.min.js +5 -5
  9. package/dist/types/core/cache-storage.d.ts +4 -1
  10. package/dist/types/core/context.d.ts +10 -1
  11. package/dist/types/core/render-element.d.ts +11 -0
  12. package/dist/types/core/util.d.ts +6 -0
  13. package/dist/types/css/index.d.ts +77 -46
  14. package/dist/types/css/parse.d.ts +15 -0
  15. package/dist/types/css/property-descriptors/accent-color.d.ts +5 -0
  16. package/dist/types/css/property-descriptors/backdrop-filter.d.ts +9 -0
  17. package/dist/types/css/property-descriptors/background-blend-mode.d.ts +16 -16
  18. package/dist/types/css/property-descriptors/border-image-width.d.ts +36 -0
  19. package/dist/types/css/property-descriptors/clip-path.d.ts +16 -2
  20. package/dist/types/css/property-descriptors/isolation.d.ts +6 -0
  21. package/dist/types/css/property-descriptors/mask.d.ts +25 -0
  22. package/dist/types/css/property-descriptors/mix-blend-mode.d.ts +16 -16
  23. package/dist/types/css/property-descriptors/outline.d.ts +15 -0
  24. package/dist/types/css/property-descriptors/text-emphasis.d.ts +22 -0
  25. package/dist/types/css/property-descriptors/text-shadow.d.ts +1 -1
  26. package/dist/types/css/property-descriptors/webkit-box-reflect.d.ts +19 -0
  27. package/dist/types/css/property-descriptors/webkit-text-fill-color.d.ts +9 -0
  28. package/dist/types/css/syntax/token-constants.d.ts +1 -1
  29. package/dist/types/css/types/color-math.d.ts +7 -1
  30. package/dist/types/css/types/color-spaces/a98.d.ts +2 -1
  31. package/dist/types/css/types/color-spaces/p3.d.ts +5 -4
  32. package/dist/types/css/types/color-spaces/pro-photo.d.ts +5 -4
  33. package/dist/types/css/types/color-spaces/rec2020.d.ts +5 -4
  34. package/dist/types/css/types/color-spaces/srgb.d.ts +5 -4
  35. package/dist/types/css/types/color-utilities.d.ts +11 -11
  36. package/dist/types/css/types/color.d.ts +1 -0
  37. package/dist/types/css/types/functions/conic-gradient.d.ts +11 -0
  38. package/dist/types/css/types/functions/image-set.d.ts +11 -0
  39. package/dist/types/css/types/functions/repeating-radial-gradient.d.ts +8 -0
  40. package/dist/types/css/types/image.d.ts +15 -2
  41. package/dist/types/css/types/safe-eval.d.ts +0 -6
  42. package/dist/types/dom/copy-css-styles.d.ts +8 -0
  43. package/dist/types/dom/document-cloner.d.ts +3 -13
  44. package/dist/types/dom/iframe-mount.d.ts +37 -0
  45. package/dist/types/dom/pseudo-content.d.ts +33 -0
  46. package/dist/types/dom/replaced-elements/iframe-element-container.d.ts +2 -2
  47. package/dist/types/options.d.ts +8 -2
  48. package/dist/types/render/canvas/background-renderer.d.ts +19 -0
  49. package/dist/types/render/canvas/border-image-renderer.d.ts +50 -1
  50. package/dist/types/render/canvas/box-shadow-geometry.d.ts +3 -0
  51. package/dist/types/render/canvas/box-shadow-painter.d.ts +13 -0
  52. package/dist/types/render/canvas/box-shadow-transform.d.ts +16 -0
  53. package/dist/types/render/canvas/canvas-renderer.d.ts +21 -15
  54. package/dist/types/render/canvas/mask-renderer.d.ts +36 -0
  55. package/dist/types/render/canvas/text-clip-renderer.d.ts +36 -0
  56. package/dist/types/render/canvas/text-renderer.d.ts +22 -0
  57. package/dist/types/render/stacking-context.d.ts +4 -1
  58. package/package.json +39 -40
  59. package/dist/lib/config.js +0 -49
  60. package/dist/lib/core/abort-helper.js +0 -20
  61. package/dist/lib/core/background-parser.js +0 -41
  62. package/dist/lib/core/bitwise.js +0 -5
  63. package/dist/lib/core/cache-storage.js +0 -283
  64. package/dist/lib/core/config-assembler.js +0 -88
  65. package/dist/lib/core/constants.js +0 -25
  66. package/dist/lib/core/context.js +0 -19
  67. package/dist/lib/core/debugger.js +0 -27
  68. package/dist/lib/core/features.js +0 -190
  69. package/dist/lib/core/logger.js +0 -56
  70. package/dist/lib/core/lru-map.js +0 -66
  71. package/dist/lib/core/origin-checker.js +0 -53
  72. package/dist/lib/core/performance-monitor.js +0 -185
  73. package/dist/lib/core/render-element.js +0 -131
  74. package/dist/lib/core/util.js +0 -4
  75. package/dist/lib/core/validator.js +0 -500
  76. package/dist/lib/css/grouped/background-styles.js +0 -36
  77. package/dist/lib/css/grouped/border-styles.js +0 -75
  78. package/dist/lib/css/grouped/font-styles.js +0 -93
  79. package/dist/lib/css/grouped/layout-styles.js +0 -127
  80. package/dist/lib/css/index.js +0 -339
  81. package/dist/lib/css/layout/bounds.js +0 -52
  82. package/dist/lib/css/layout/text.js +0 -133
  83. package/dist/lib/css/property-descriptor.js +0 -2
  84. package/dist/lib/css/property-descriptors/background-blend-mode.js +0 -41
  85. package/dist/lib/css/property-descriptors/background-clip.js +0 -25
  86. package/dist/lib/css/property-descriptors/background-color.js +0 -10
  87. package/dist/lib/css/property-descriptors/background-image.js +0 -24
  88. package/dist/lib/css/property-descriptors/background-origin.js +0 -23
  89. package/dist/lib/css/property-descriptors/background-position.js +0 -19
  90. package/dist/lib/css/property-descriptors/background-repeat.js +0 -33
  91. package/dist/lib/css/property-descriptors/background-size.js +0 -23
  92. package/dist/lib/css/property-descriptors/border-color.js +0 -14
  93. package/dist/lib/css/property-descriptors/border-image-repeat.js +0 -42
  94. package/dist/lib/css/property-descriptors/border-image-slice.js +0 -45
  95. package/dist/lib/css/property-descriptors/border-image-source.js +0 -22
  96. package/dist/lib/css/property-descriptors/border-radius.js +0 -15
  97. package/dist/lib/css/property-descriptors/border-style.js +0 -26
  98. package/dist/lib/css/property-descriptors/border-width.js +0 -20
  99. package/dist/lib/css/property-descriptors/box-decoration-break.js +0 -18
  100. package/dist/lib/css/property-descriptors/box-shadow.js +0 -54
  101. package/dist/lib/css/property-descriptors/clip-path.js +0 -189
  102. package/dist/lib/css/property-descriptors/color.js +0 -10
  103. package/dist/lib/css/property-descriptors/content.js +0 -19
  104. package/dist/lib/css/property-descriptors/counter-increment.js +0 -35
  105. package/dist/lib/css/property-descriptors/counter-reset.js +0 -18
  106. package/dist/lib/css/property-descriptors/direction.js +0 -18
  107. package/dist/lib/css/property-descriptors/display.js +0 -81
  108. package/dist/lib/css/property-descriptors/duration.js +0 -14
  109. package/dist/lib/css/property-descriptors/filter.js +0 -86
  110. package/dist/lib/css/property-descriptors/float.js +0 -22
  111. package/dist/lib/css/property-descriptors/font-family.js +0 -47
  112. package/dist/lib/css/property-descriptors/font-size.js +0 -10
  113. package/dist/lib/css/property-descriptors/font-style.js +0 -20
  114. package/dist/lib/css/property-descriptors/font-variant-ligatures.js +0 -34
  115. package/dist/lib/css/property-descriptors/font-variant.js +0 -13
  116. package/dist/lib/css/property-descriptors/font-weight.js +0 -25
  117. package/dist/lib/css/property-descriptors/image-rendering.js +0 -33
  118. package/dist/lib/css/property-descriptors/letter-spacing.js +0 -21
  119. package/dist/lib/css/property-descriptors/line-break.js +0 -23
  120. package/dist/lib/css/property-descriptors/line-height.js +0 -24
  121. package/dist/lib/css/property-descriptors/list-style-image.js +0 -17
  122. package/dist/lib/css/property-descriptors/list-style-position.js +0 -18
  123. package/dist/lib/css/property-descriptors/list-style-type.js +0 -122
  124. package/dist/lib/css/property-descriptors/margin.js +0 -13
  125. package/dist/lib/css/property-descriptors/mix-blend-mode.js +0 -31
  126. package/dist/lib/css/property-descriptors/object-fit.js +0 -28
  127. package/dist/lib/css/property-descriptors/object-position.js +0 -42
  128. package/dist/lib/css/property-descriptors/opacity.js +0 -16
  129. package/dist/lib/css/property-descriptors/overflow-wrap.js +0 -18
  130. package/dist/lib/css/property-descriptors/overflow.js +0 -27
  131. package/dist/lib/css/property-descriptors/padding.js +0 -14
  132. package/dist/lib/css/property-descriptors/paint-order.js +0 -33
  133. package/dist/lib/css/property-descriptors/position.js +0 -22
  134. package/dist/lib/css/property-descriptors/quotes.js +0 -41
  135. package/dist/lib/css/property-descriptors/rotate.js +0 -27
  136. package/dist/lib/css/property-descriptors/text-align.js +0 -21
  137. package/dist/lib/css/property-descriptors/text-decoration-color.js +0 -10
  138. package/dist/lib/css/property-descriptors/text-decoration-line.js +0 -28
  139. package/dist/lib/css/property-descriptors/text-decoration-style.js +0 -24
  140. package/dist/lib/css/property-descriptors/text-decoration-thickness.js +0 -26
  141. package/dist/lib/css/property-descriptors/text-overflow.js +0 -18
  142. package/dist/lib/css/property-descriptors/text-shadow.js +0 -46
  143. package/dist/lib/css/property-descriptors/text-transform.js +0 -20
  144. package/dist/lib/css/property-descriptors/text-underline-offset.js +0 -23
  145. package/dist/lib/css/property-descriptors/transform-origin.js +0 -24
  146. package/dist/lib/css/property-descriptors/transform.js +0 -57
  147. package/dist/lib/css/property-descriptors/visibility.js +0 -20
  148. package/dist/lib/css/property-descriptors/webkit-line-clamp.js +0 -26
  149. package/dist/lib/css/property-descriptors/webkit-text-stroke-color.js +0 -10
  150. package/dist/lib/css/property-descriptors/webkit-text-stroke-width.js +0 -16
  151. package/dist/lib/css/property-descriptors/white-space.js +0 -32
  152. package/dist/lib/css/property-descriptors/word-break.js +0 -26
  153. package/dist/lib/css/property-descriptors/writing-mode.js +0 -28
  154. package/dist/lib/css/property-descriptors/z-index.js +0 -19
  155. package/dist/lib/css/property-descriptors/zoom.js +0 -18
  156. package/dist/lib/css/syntax/parser.js +0 -141
  157. package/dist/lib/css/syntax/token-constants.js +0 -214
  158. package/dist/lib/css/syntax/token-singletons.js +0 -36
  159. package/dist/lib/css/syntax/token-types.js +0 -10
  160. package/dist/lib/css/syntax/tokenizer.js +0 -502
  161. package/dist/lib/css/type-descriptor.js +0 -2
  162. package/dist/lib/css/types/angle.js +0 -80
  163. package/dist/lib/css/types/color-math.js +0 -25
  164. package/dist/lib/css/types/color-spaces/a98.js +0 -78
  165. package/dist/lib/css/types/color-spaces/p3.js +0 -86
  166. package/dist/lib/css/types/color-spaces/pro-photo.js +0 -83
  167. package/dist/lib/css/types/color-spaces/rec2020.js +0 -85
  168. package/dist/lib/css/types/color-spaces/srgb.js +0 -79
  169. package/dist/lib/css/types/color-utilities.js +0 -342
  170. package/dist/lib/css/types/color.js +0 -400
  171. package/dist/lib/css/types/functions/-prefix-linear-gradient.js +0 -32
  172. package/dist/lib/css/types/functions/-prefix-radial-gradient.js +0 -83
  173. package/dist/lib/css/types/functions/-webkit-gradient.js +0 -59
  174. package/dist/lib/css/types/functions/counter.js +0 -367
  175. package/dist/lib/css/types/functions/gradient.js +0 -163
  176. package/dist/lib/css/types/functions/linear-gradient.js +0 -27
  177. package/dist/lib/css/types/functions/radial-gradient.js +0 -90
  178. package/dist/lib/css/types/functions/repeating-linear-gradient.js +0 -9
  179. package/dist/lib/css/types/image.js +0 -62
  180. package/dist/lib/css/types/index.js +0 -2
  181. package/dist/lib/css/types/length-percentage.js +0 -197
  182. package/dist/lib/css/types/length.js +0 -5
  183. package/dist/lib/css/types/safe-eval.js +0 -80
  184. package/dist/lib/css/types/time.js +0 -17
  185. package/dist/lib/dom/document-cloner.js +0 -618
  186. package/dist/lib/dom/dom-normalizer.js +0 -115
  187. package/dist/lib/dom/element-container.js +0 -71
  188. package/dist/lib/dom/elements/li-element-container.js +0 -11
  189. package/dist/lib/dom/elements/ol-element-container.js +0 -12
  190. package/dist/lib/dom/elements/select-element-container.js +0 -12
  191. package/dist/lib/dom/elements/textarea-element-container.js +0 -11
  192. package/dist/lib/dom/node-parser.js +0 -125
  193. package/dist/lib/dom/node-type-guards.js +0 -81
  194. package/dist/lib/dom/replaced-elements/canvas-element-container.js +0 -13
  195. package/dist/lib/dom/replaced-elements/iframe-element-container.js +0 -38
  196. package/dist/lib/dom/replaced-elements/image-element-container.js +0 -14
  197. package/dist/lib/dom/replaced-elements/index.js +0 -2
  198. package/dist/lib/dom/replaced-elements/input-element-container.js +0 -89
  199. package/dist/lib/dom/replaced-elements/svg-element-container.js +0 -57
  200. package/dist/lib/dom/slot-cloner.js +0 -176
  201. package/dist/lib/dom/text-container.js +0 -35
  202. package/dist/lib/index.js +0 -49
  203. package/dist/lib/options.js +0 -2
  204. package/dist/lib/render/background.js +0 -188
  205. package/dist/lib/render/bezier-curve.js +0 -34
  206. package/dist/lib/render/border.js +0 -104
  207. package/dist/lib/render/bound-curves.js +0 -188
  208. package/dist/lib/render/box-sizing.js +0 -20
  209. package/dist/lib/render/canvas/background-renderer.js +0 -338
  210. package/dist/lib/render/canvas/border-image-renderer.js +0 -153
  211. package/dist/lib/render/canvas/border-renderer.js +0 -184
  212. package/dist/lib/render/canvas/canvas-path.js +0 -25
  213. package/dist/lib/render/canvas/canvas-renderer.js +0 -633
  214. package/dist/lib/render/canvas/content-renderer.js +0 -270
  215. package/dist/lib/render/canvas/effects-renderer.js +0 -171
  216. package/dist/lib/render/canvas/filter-surface.js +0 -270
  217. package/dist/lib/render/canvas/font-utils.js +0 -46
  218. package/dist/lib/render/canvas/foreignobject-renderer.js +0 -38
  219. package/dist/lib/render/canvas/surface-bounds.js +0 -64
  220. package/dist/lib/render/canvas/text/text-decoration-renderer.js +0 -99
  221. package/dist/lib/render/canvas/text-renderer.js +0 -486
  222. package/dist/lib/render/effects.js +0 -231
  223. package/dist/lib/render/font-metrics.js +0 -53
  224. package/dist/lib/render/object-fit.js +0 -91
  225. package/dist/lib/render/path.js +0 -26
  226. package/dist/lib/render/renderer-interface.js +0 -2
  227. package/dist/lib/render/stacking-context.js +0 -341
  228. package/dist/lib/render/vector.js +0 -16
@@ -1,618 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serializeDoctype = exports.copyCSSStyles = exports.DocumentCloner = void 0;
4
- const node_type_guards_1 = require("./node-type-guards");
5
- const parser_1 = require("../css/syntax/parser");
6
- const counter_1 = require("../css/types/functions/counter");
7
- const list_style_type_1 = require("../css/property-descriptors/list-style-type");
8
- const index_1 = require("../css/index");
9
- const quotes_1 = require("../css/property-descriptors/quotes");
10
- const debugger_1 = require("../core/debugger");
11
- const slot_cloner_1 = require("./slot-cloner");
12
- /**
13
- * Find the parent ShadowRoot of an element, if any
14
- * @param element - The element to check
15
- * @returns The parent ShadowRoot or null
16
- */
17
- const findParentShadowRoot = (element) => {
18
- let current = element;
19
- while (current) {
20
- // Check if we've reached a shadow root boundary
21
- if (current.parentNode && current.parentNode.host) {
22
- return current.parentNode;
23
- }
24
- // Use getRootNode to check if we're in a shadow root
25
- const root = current.getRootNode();
26
- if (root && root !== current.ownerDocument && root.host) {
27
- return root;
28
- }
29
- current = current.parentNode;
30
- }
31
- return null;
32
- };
33
- class DocumentCloner {
34
- constructor(context, element, options) {
35
- this.context = context;
36
- this.options = options;
37
- this.scrolledElements = [];
38
- this.referenceElement = element;
39
- this.counters = new counter_1.CounterState();
40
- this.quoteDepth = 0;
41
- this.slotCloner = new slot_cloner_1.SlotCloner((node, copyStyles) => this.cloneNode(node, copyStyles), { ignoreElements: options.ignoreElements, copyStyles: options.copyStyles ?? true }, context);
42
- if (!element.ownerDocument) {
43
- throw new Error('Cloned element does not have an owner document');
44
- }
45
- // Auto-detect Shadow Root if not explicitly provided
46
- if (!this.options.iframeContainer) {
47
- const shadowRoot = findParentShadowRoot(element);
48
- if (shadowRoot) {
49
- this.options.iframeContainer = shadowRoot;
50
- }
51
- }
52
- this.documentElement = this.cloneNode(element.ownerDocument.documentElement, false);
53
- }
54
- toIFrame(ownerDocument, windowSize) {
55
- const iframe = createIFrameContainer(ownerDocument, windowSize, this.options.iframeContainer);
56
- if (!iframe.contentWindow) {
57
- throw new Error('Unable to find iframe window');
58
- }
59
- const scrollX = ownerDocument.defaultView.pageXOffset;
60
- const scrollY = ownerDocument.defaultView.pageYOffset;
61
- const cloneWindow = iframe.contentWindow;
62
- const documentClone = cloneWindow.document;
63
- /* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle
64
- if window url is about:blank, we can assign the url to current by writing onto the document
65
- */
66
- const iframeLoad = iframeLoader(iframe).then(async () => {
67
- this.scrolledElements.forEach(restoreNodeScroll);
68
- if (cloneWindow) {
69
- cloneWindow.scrollTo(windowSize.left, windowSize.top);
70
- }
71
- const onclone = this.options.onclone;
72
- const referenceElement = this.clonedReferenceElement;
73
- if (typeof referenceElement === 'undefined') {
74
- throw new Error(`Error finding the ${this.referenceElement.nodeName} in the cloned document`);
75
- }
76
- if (documentClone.fonts && documentClone.fonts.ready) {
77
- await documentClone.fonts.ready;
78
- }
79
- if (/(AppleWebKit)/g.test(navigator.userAgent)) {
80
- await imagesReady(documentClone);
81
- }
82
- if (typeof onclone === 'function') {
83
- await Promise.resolve().then(() => onclone(documentClone, referenceElement));
84
- }
85
- // onclone can change layout above the viewport and trigger browser scroll
86
- // anchoring. Reapply the requested offset after the callback so element
87
- // bounds stay aligned with the window bounds used by the renderer.
88
- cloneWindow.scrollTo(windowSize.left, windowSize.top);
89
- if (/AppleWebKit/g.test(navigator.userAgent) &&
90
- (cloneWindow.scrollY !== windowSize.top || cloneWindow.scrollX !== windowSize.left)) {
91
- this.context.logger.warn('Unable to restore scroll position for cloned document');
92
- this.context.windowBounds = this.context.windowBounds.add(cloneWindow.scrollX - windowSize.left, cloneWindow.scrollY - windowSize.top, 0, 0);
93
- }
94
- return iframe;
95
- });
96
- /**
97
- * The base URI used for resolving relative URLs (e.g. background-image) in the clone.
98
- * Must come from the source document: the iframe document is about:blank, so
99
- * documentClone.baseURI would break getComputedStyle() for relative background URLs.
100
- */
101
- const baseUri = ownerDocument.baseURI;
102
- documentClone.open();
103
- // rawHTML is always a static, internally-generated string:
104
- // serializeDoctype(document.doctype) + '<html></html>'
105
- // No user-controlled input — safe for document.write in the sandbox iframe.
106
- const rawHTML = (0, exports.serializeDoctype)(document.doctype) + '<html></html>';
107
- try {
108
- const ownerWindow = this.referenceElement.ownerDocument?.defaultView;
109
- const trustedTypesFactory = ownerWindow && ownerWindow.trustedTypes;
110
- let policy = trustedTypesFactory?.getPolicy?.('html2canvas-pro');
111
- if (!policy && trustedTypesFactory) {
112
- policy = trustedTypesFactory.createPolicy('html2canvas-pro', {
113
- createHTML: (s) => s
114
- });
115
- }
116
- // Prefer Trusted Types when available; fallback is the same static HTML.
117
- const html = policy ? policy.createHTML(rawHTML) : rawHTML;
118
- // CodeQL:no - rawHTML is a static internal string, never user-controlled
119
- documentClone.write(html);
120
- }
121
- catch (_e) {
122
- // CodeQL:no - rawHTML is a static internal string, never user-controlled
123
- documentClone.write(rawHTML);
124
- }
125
- // Chrome scrolls the parent document for some reason after the write to the cloned window???
126
- restoreOwnerScroll(this.referenceElement.ownerDocument, scrollX, scrollY);
127
- /**
128
- * IMPORTANT: documentClone.close() MUST be called BEFORE adoptNode().
129
- *
130
- * In Chrome, calling adoptNode() while the document is still "open"
131
- * (between document.open() and document.close()) causes CSS rules with
132
- * uppercase characters in class names (e.g. ".MyClass") to not match
133
- * correctly. Chrome's CSS engine only enters a fully-resolved matching
134
- * mode once the document is closed.
135
- *
136
- * Correct order: open() → write() → close() → adoptNode() → replaceChild()
137
- *
138
- * Timing: close() queues the iframe 'load' event; because JS is single-threaded,
139
- * the synchronous adoptNode() and replaceChild() below complete before that
140
- * event is dispatched. iframeLoader's setInterval will therefore see the body
141
- * already populated on its first tick.
142
- */
143
- documentClone.close();
144
- const adoptedNode = documentClone.adoptNode(this.documentElement);
145
- addBase(adoptedNode, baseUri);
146
- documentClone.replaceChild(adoptedNode, documentClone.documentElement);
147
- return iframeLoad;
148
- }
149
- createElementClone(node) {
150
- if ((0, debugger_1.isDebugging)(node, 2 /* DebuggerType.CLONE */)) {
151
- debugger;
152
- }
153
- if ((0, node_type_guards_1.isCanvasElement)(node)) {
154
- return this.createCanvasClone(node);
155
- }
156
- if ((0, node_type_guards_1.isVideoElement)(node)) {
157
- return this.createVideoClone(node);
158
- }
159
- if ((0, node_type_guards_1.isStyleElement)(node)) {
160
- return this.createStyleClone(node);
161
- }
162
- const clone = node.cloneNode(false);
163
- if ((0, node_type_guards_1.isImageElement)(clone)) {
164
- if ((0, node_type_guards_1.isImageElement)(node) && node.currentSrc && node.currentSrc !== node.src) {
165
- clone.src = node.currentSrc;
166
- clone.srcset = '';
167
- }
168
- if (clone.loading === 'lazy') {
169
- clone.loading = 'eager';
170
- }
171
- }
172
- if ((0, node_type_guards_1.isCustomElement)(clone) && !(0, node_type_guards_1.isSVGElementNode)(clone)) {
173
- return this.createCustomElementClone(node, clone);
174
- }
175
- return clone;
176
- }
177
- createCustomElementClone(originalNode, clonedNode) {
178
- const clone = document.createElement('div');
179
- clone.className = clonedNode.className;
180
- (0, exports.copyCSSStyles)(clonedNode.style, clone);
181
- // Copy all attributes onto the fresh <div> (except class/style which are
182
- // handled above). Without this, attributes such as `slot="suffix"` are
183
- // dropped and slotted custom elements lose their slot assignment in the
184
- // cloned shadow DOM, so their content is never rendered (issue #226).
185
- // Iterate by index: `NamedNodeMap` is not guaranteed to be iterable
186
- // across TypeScript DOM lib versions (TS2488 on older libs).
187
- const attributes = clonedNode.attributes;
188
- for (let i = 0; i < attributes.length; i++) {
189
- const attr = attributes[i];
190
- if (attr.name !== 'class' && attr.name !== 'style') {
191
- clone.setAttribute(attr.name, attr.value);
192
- }
193
- }
194
- // Clone shadow DOM if it exists. The shadow root must be detected on the
195
- // ORIGINAL element: `clonedNode` comes from `cloneNode(false)`, which only
196
- // preserves the shadow root when it was created with `clonable: true` (or
197
- // by frameworks that set it). For other components (e.g. Shoelace / Web
198
- // Awesome) the clone has no shadow root, so without this check the clone
199
- // falls back to flattening the shadow content and the slotted light-DOM
200
- // label content is lost.
201
- // Fix for Issue #108: This is critical for Web Components with slots to work correctly
202
- if (originalNode.shadowRoot) {
203
- try {
204
- clone.attachShadow({ mode: 'open' });
205
- // The actual shadow DOM content will be cloned in cloneChildNodes
206
- }
207
- catch (e) {
208
- // Some elements cannot have shadow roots attached
209
- // This can happen if the element doesn't support shadow DOM
210
- this.context.logger.error('Failed to attach shadow root to custom element clone:', e);
211
- }
212
- }
213
- return clone;
214
- }
215
- createStyleClone(node) {
216
- try {
217
- const sheet = node.sheet;
218
- if (sheet && sheet.cssRules) {
219
- const css = [].slice.call(sheet.cssRules, 0).reduce((css, rule) => {
220
- if (rule && typeof rule.cssText === 'string') {
221
- return css + rule.cssText;
222
- }
223
- return css;
224
- }, '');
225
- const style = node.cloneNode(false);
226
- style.textContent = css;
227
- if (this.options.cspNonce) {
228
- style.nonce = this.options.cspNonce;
229
- }
230
- return style;
231
- }
232
- }
233
- catch (e) {
234
- // accessing node.sheet.cssRules throws a DOMException
235
- this.context.logger.error('Unable to access cssRules property', e);
236
- if (e.name !== 'SecurityError') {
237
- throw e;
238
- }
239
- }
240
- const cloned = node.cloneNode(false);
241
- if (this.options.cspNonce) {
242
- cloned.nonce = this.options.cspNonce;
243
- }
244
- return cloned;
245
- }
246
- createCanvasClone(canvas) {
247
- if (this.options.inlineImages && canvas.ownerDocument) {
248
- const img = canvas.ownerDocument.createElement('img');
249
- try {
250
- img.src = canvas.toDataURL();
251
- return img;
252
- }
253
- catch (e) {
254
- this.context.logger.info(`Unable to inline canvas contents, canvas is tainted`, canvas);
255
- }
256
- }
257
- const clonedCanvas = canvas.cloneNode(false);
258
- try {
259
- clonedCanvas.width = canvas.width;
260
- clonedCanvas.height = canvas.height;
261
- const ctx = canvas.getContext('2d');
262
- const clonedCtx = clonedCanvas.getContext('2d', { willReadFrequently: true });
263
- if (clonedCtx) {
264
- if (!this.options.allowTaint && ctx) {
265
- clonedCtx.putImageData(ctx.getImageData(0, 0, canvas.width, canvas.height), 0, 0);
266
- }
267
- else {
268
- const gl = canvas.getContext('webgl2') ?? canvas.getContext('webgl');
269
- if (gl) {
270
- const attribs = gl.getContextAttributes();
271
- if (attribs?.preserveDrawingBuffer === false) {
272
- this.context.logger.warn('Unable to clone WebGL context as it has preserveDrawingBuffer=false', canvas);
273
- }
274
- }
275
- clonedCtx.drawImage(canvas, 0, 0);
276
- }
277
- }
278
- return clonedCanvas;
279
- }
280
- catch (e) {
281
- this.context.logger.info(`Unable to clone canvas as it is tainted`, canvas);
282
- }
283
- return clonedCanvas;
284
- }
285
- createVideoClone(video) {
286
- const canvas = video.ownerDocument.createElement('canvas');
287
- canvas.width = video.offsetWidth;
288
- canvas.height = video.offsetHeight;
289
- const ctx = canvas.getContext('2d');
290
- try {
291
- if (ctx) {
292
- ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
293
- if (!this.options.allowTaint) {
294
- ctx.getImageData(0, 0, canvas.width, canvas.height);
295
- }
296
- }
297
- return canvas;
298
- }
299
- catch (e) {
300
- this.context.logger.info(`Unable to clone video as it is tainted`, video);
301
- }
302
- const blankCanvas = video.ownerDocument.createElement('canvas');
303
- blankCanvas.width = video.offsetWidth;
304
- blankCanvas.height = video.offsetHeight;
305
- return blankCanvas;
306
- }
307
- appendChildNode(clone, child, copyStyles) {
308
- this.slotCloner.appendChildNode(clone, child, copyStyles);
309
- }
310
- /**
311
- * Clone child nodes from source element to clone element.
312
- * Delegates to SlotCloner which handles shadow DOM, slots, and light DOM.
313
- */
314
- cloneChildNodes(node, clone, copyStyles) {
315
- this.slotCloner.cloneChildNodes(node, clone, copyStyles);
316
- }
317
- cloneNode(node, copyStyles) {
318
- if ((0, node_type_guards_1.isTextNode)(node)) {
319
- return document.createTextNode(node.data);
320
- }
321
- if (!node.ownerDocument) {
322
- return node.cloneNode(false);
323
- }
324
- const window = node.ownerDocument.defaultView;
325
- if (window && (0, node_type_guards_1.isElementNode)(node) && ((0, node_type_guards_1.isHTMLElementNode)(node) || (0, node_type_guards_1.isSVGElementNode)(node))) {
326
- const clone = this.createElementClone(node);
327
- clone.style.transitionProperty = 'none';
328
- const style = window.getComputedStyle(node);
329
- // Per CSS spec, replaced elements, void elements, and SVG elements
330
- // cannot have ::before / ::after pseudo-elements — skip these queries.
331
- const checkPseudoElements = (0, node_type_guards_1.canHavePseudoElements)(node);
332
- if (this.referenceElement === node && (0, node_type_guards_1.isHTMLElementNode)(clone)) {
333
- this.clonedReferenceElement = clone;
334
- }
335
- if ((0, node_type_guards_1.isBodyElement)(clone)) {
336
- createPseudoHideStyles(clone, this.options.cspNonce);
337
- }
338
- const counters = this.counters.parse(new index_1.CSSParsedCounterDeclaration(this.context, style));
339
- if (checkPseudoElements) {
340
- const styleBefore = window.getComputedStyle(node, ':before');
341
- const before = this.resolvePseudoContent(node, clone, styleBefore, PseudoElementType.BEFORE);
342
- if (before) {
343
- clone.insertBefore(before, clone.firstChild);
344
- }
345
- const styleAfter = window.getComputedStyle(node, ':after');
346
- const after = this.resolvePseudoContent(node, clone, styleAfter, PseudoElementType.AFTER);
347
- if (after) {
348
- clone.appendChild(after);
349
- }
350
- }
351
- if ((0, node_type_guards_1.isCustomElement)(node)) {
352
- copyStyles = true;
353
- }
354
- if (!(0, node_type_guards_1.isVideoElement)(node)) {
355
- this.cloneChildNodes(node, clone, copyStyles);
356
- }
357
- this.counters.pop(counters);
358
- if ((style && (this.options.copyStyles || (0, node_type_guards_1.isSVGElementNode)(node)) && !(0, node_type_guards_1.isIFrameElement)(node)) ||
359
- copyStyles) {
360
- (0, exports.copyCSSStyles)(style, clone);
361
- }
362
- if (node.scrollTop !== 0 || node.scrollLeft !== 0) {
363
- this.scrolledElements.push([clone, node.scrollLeft, node.scrollTop]);
364
- }
365
- if (((0, node_type_guards_1.isTextareaElement)(node) || (0, node_type_guards_1.isSelectElement)(node)) &&
366
- ((0, node_type_guards_1.isTextareaElement)(clone) || (0, node_type_guards_1.isSelectElement)(clone))) {
367
- clone.value = node.value;
368
- }
369
- return clone;
370
- }
371
- return node.cloneNode(false);
372
- }
373
- resolvePseudoContent(node, clone, style, pseudoElt) {
374
- if (!style) {
375
- return;
376
- }
377
- const value = style.content;
378
- const document = clone.ownerDocument;
379
- if (!document || !value || value === 'none' || value === '-moz-alt-content' || style.display === 'none') {
380
- return;
381
- }
382
- this.counters.parse(new index_1.CSSParsedCounterDeclaration(this.context, style));
383
- const declaration = new index_1.CSSParsedPseudoDeclaration(this.context, style);
384
- const anonymousReplacedElement = document.createElement('html2canvaspseudoelement');
385
- (0, exports.copyCSSStyles)(style, anonymousReplacedElement);
386
- declaration.content.forEach((token) => {
387
- if (token.type === 0 /* TokenType.STRING_TOKEN */) {
388
- anonymousReplacedElement.appendChild(document.createTextNode(token.value));
389
- }
390
- else if (token.type === 22 /* TokenType.URL_TOKEN */) {
391
- const img = document.createElement('img');
392
- img.src = token.value;
393
- img.style.opacity = '1';
394
- anonymousReplacedElement.appendChild(img);
395
- }
396
- else if (token.type === 18 /* TokenType.FUNCTION */) {
397
- if (token.name === 'attr') {
398
- const attr = token.values.filter(parser_1.isIdentToken);
399
- if (attr.length) {
400
- anonymousReplacedElement.appendChild(document.createTextNode(node.getAttribute(attr[0].value) || ''));
401
- }
402
- }
403
- else if (token.name === 'counter') {
404
- const [counter, counterStyle] = token.values.filter(parser_1.nonFunctionArgSeparator);
405
- if (counter && (0, parser_1.isIdentToken)(counter)) {
406
- const counterState = this.counters.getCounterValue(counter.value);
407
- const counterType = counterStyle && (0, parser_1.isIdentToken)(counterStyle)
408
- ? list_style_type_1.listStyleType.parse(this.context, counterStyle.value)
409
- : 3 /* LIST_STYLE_TYPE.DECIMAL */;
410
- anonymousReplacedElement.appendChild(document.createTextNode((0, counter_1.createCounterText)(counterState, counterType, false)));
411
- }
412
- }
413
- else if (token.name === 'counters') {
414
- const [counter, delim, counterStyle] = token.values.filter(parser_1.nonFunctionArgSeparator);
415
- if (counter && (0, parser_1.isIdentToken)(counter)) {
416
- const counterStates = this.counters.getCounterValues(counter.value);
417
- const counterType = counterStyle && (0, parser_1.isIdentToken)(counterStyle)
418
- ? list_style_type_1.listStyleType.parse(this.context, counterStyle.value)
419
- : 3 /* LIST_STYLE_TYPE.DECIMAL */;
420
- const separator = delim && delim.type === 0 /* TokenType.STRING_TOKEN */ ? delim.value : '';
421
- const text = counterStates
422
- .map((value) => (0, counter_1.createCounterText)(value, counterType, false))
423
- .join(separator);
424
- anonymousReplacedElement.appendChild(document.createTextNode(text));
425
- }
426
- }
427
- }
428
- else if (token.type === 20 /* TokenType.IDENT_TOKEN */) {
429
- switch (token.value) {
430
- case 'open-quote':
431
- anonymousReplacedElement.appendChild(document.createTextNode((0, quotes_1.getQuote)(declaration.quotes, this.quoteDepth++, true)));
432
- break;
433
- case 'close-quote':
434
- anonymousReplacedElement.appendChild(document.createTextNode((0, quotes_1.getQuote)(declaration.quotes, --this.quoteDepth, false)));
435
- break;
436
- default:
437
- // safari doesn't parse string tokens correctly because of lack of quotes
438
- anonymousReplacedElement.appendChild(document.createTextNode(token.value));
439
- }
440
- }
441
- });
442
- anonymousReplacedElement.className = `${PSEUDO_HIDE_ELEMENT_CLASS_BEFORE} ${PSEUDO_HIDE_ELEMENT_CLASS_AFTER}`;
443
- const newClassName = pseudoElt === PseudoElementType.BEFORE
444
- ? ` ${PSEUDO_HIDE_ELEMENT_CLASS_BEFORE}`
445
- : ` ${PSEUDO_HIDE_ELEMENT_CLASS_AFTER}`;
446
- if ((0, node_type_guards_1.isSVGElementNode)(clone)) {
447
- clone.className.baseValue += newClassName;
448
- }
449
- else {
450
- clone.className += newClassName;
451
- }
452
- return anonymousReplacedElement;
453
- }
454
- static destroy(container) {
455
- if (container.parentNode) {
456
- container.parentNode.removeChild(container);
457
- return true;
458
- }
459
- return false;
460
- }
461
- }
462
- exports.DocumentCloner = DocumentCloner;
463
- var PseudoElementType;
464
- (function (PseudoElementType) {
465
- PseudoElementType[PseudoElementType["BEFORE"] = 0] = "BEFORE";
466
- PseudoElementType[PseudoElementType["AFTER"] = 1] = "AFTER";
467
- })(PseudoElementType || (PseudoElementType = {}));
468
- const createIFrameContainer = (ownerDocument, bounds, customContainer) => {
469
- const cloneIframeContainer = ownerDocument.createElement('iframe');
470
- cloneIframeContainer.className = 'html2canvas-container';
471
- cloneIframeContainer.style.visibility = 'hidden';
472
- cloneIframeContainer.style.position = 'fixed';
473
- cloneIframeContainer.style.left = '-10000px';
474
- cloneIframeContainer.style.top = '0px';
475
- cloneIframeContainer.style.border = '0';
476
- cloneIframeContainer.width = bounds.width.toString();
477
- cloneIframeContainer.height = bounds.height.toString();
478
- cloneIframeContainer.scrolling = 'no'; // ios won't scroll without it
479
- cloneIframeContainer.setAttribute(slot_cloner_1.IGNORE_ATTRIBUTE, 'true');
480
- // Use custom container if provided, otherwise use body
481
- const container = customContainer || ownerDocument.body;
482
- container.appendChild(cloneIframeContainer);
483
- return cloneIframeContainer;
484
- };
485
- const imageReady = (img) => {
486
- return new Promise((resolve) => {
487
- if (img.complete) {
488
- resolve();
489
- return;
490
- }
491
- if (!img.src) {
492
- resolve();
493
- return;
494
- }
495
- img.onload = resolve;
496
- img.onerror = resolve;
497
- });
498
- };
499
- const imagesReady = (document) => {
500
- return Promise.all([].slice.call(document.images, 0).map(imageReady));
501
- };
502
- const iframeLoader = (iframe) => {
503
- return new Promise((resolve, reject) => {
504
- const cloneWindow = iframe.contentWindow;
505
- if (!cloneWindow) {
506
- return reject(`No window assigned for iframe`);
507
- }
508
- const documentClone = cloneWindow.document;
509
- cloneWindow.onload = iframe.onload = () => {
510
- cloneWindow.onload = iframe.onload = null;
511
- const MAX_POLL_ATTEMPTS = 600; // 30 seconds at 50ms intervals
512
- let attempts = 0;
513
- const interval = setInterval(() => {
514
- attempts++;
515
- if (documentClone.body.childNodes.length > 0 && documentClone.readyState === 'complete') {
516
- clearInterval(interval);
517
- resolve(iframe);
518
- }
519
- else if (attempts >= MAX_POLL_ATTEMPTS) {
520
- clearInterval(interval);
521
- resolve(iframe); // resolve anyway to avoid hanging
522
- }
523
- }, 50);
524
- };
525
- });
526
- };
527
- const ignoredStyleProperties = [
528
- 'all', // #2476
529
- 'd', // #2483
530
- 'content' // Safari shows pseudoelements if content is set
531
- ];
532
- const copyCSSStyles = (style, target) => {
533
- const parts = [];
534
- for (let i = style.length - 1; i >= 0; i--) {
535
- const property = style.item(i);
536
- // fix: Chrome_138 ignore custom properties
537
- if (ignoredStyleProperties.indexOf(property) === -1 && !property.startsWith('--')) {
538
- const value = style.getPropertyValue(property);
539
- if (value) {
540
- const priority = style.getPropertyPriority(property);
541
- parts.push(priority ? `${property}:${value} !${priority}` : `${property}:${value}`);
542
- }
543
- }
544
- }
545
- if (parts.length > 0) {
546
- target.style.cssText = parts.join(';') + ';';
547
- }
548
- return target;
549
- };
550
- exports.copyCSSStyles = copyCSSStyles;
551
- /**
552
- * Serialise a document type declaration to an HTML string.
553
- * @internal – exported for testing only, not part of the public API.
554
- */
555
- const serializeDoctype = (doctype) => {
556
- let str = '';
557
- if (doctype) {
558
- str += '<!DOCTYPE ';
559
- if (doctype.name) {
560
- str += doctype.name;
561
- }
562
- if (doctype.internalSubset) {
563
- str += ' ' + doctype.internalSubset.replace(/"/g, '&quot;').replace(/>/g, '&gt;');
564
- }
565
- if (doctype.publicId) {
566
- str += ' PUBLIC "' + doctype.publicId.replace(/"/g, '&quot;') + '"';
567
- if (doctype.systemId) {
568
- str += ' "' + doctype.systemId.replace(/"/g, '&quot;') + '"';
569
- }
570
- }
571
- else if (doctype.systemId) {
572
- str += ' SYSTEM "' + doctype.systemId.replace(/"/g, '&quot;') + '"';
573
- }
574
- str += '>';
575
- }
576
- return str;
577
- };
578
- exports.serializeDoctype = serializeDoctype;
579
- const restoreOwnerScroll = (ownerDocument, x, y) => {
580
- if (ownerDocument &&
581
- ownerDocument.defaultView &&
582
- (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) {
583
- ownerDocument.defaultView.scrollTo(x, y);
584
- }
585
- };
586
- const restoreNodeScroll = ([element, x, y]) => {
587
- element.scrollLeft = x;
588
- element.scrollTop = y;
589
- };
590
- const PSEUDO_BEFORE = ':before';
591
- const PSEUDO_AFTER = ':after';
592
- const PSEUDO_HIDE_ELEMENT_CLASS_BEFORE = '___html2canvas___pseudoelement_before';
593
- const PSEUDO_HIDE_ELEMENT_CLASS_AFTER = '___html2canvas___pseudoelement_after';
594
- const PSEUDO_HIDE_ELEMENT_STYLE = `{
595
- content: "" !important;
596
- display: none !important;
597
- }`;
598
- const createPseudoHideStyles = (body, cspNonce) => {
599
- createStyles(body, `.${PSEUDO_HIDE_ELEMENT_CLASS_BEFORE}${PSEUDO_BEFORE}${PSEUDO_HIDE_ELEMENT_STYLE}
600
- .${PSEUDO_HIDE_ELEMENT_CLASS_AFTER}${PSEUDO_AFTER}${PSEUDO_HIDE_ELEMENT_STYLE}`, cspNonce);
601
- };
602
- const createStyles = (body, styles, cspNonce) => {
603
- const document = body.ownerDocument;
604
- if (document) {
605
- const style = document.createElement('style');
606
- style.textContent = styles;
607
- if (cspNonce) {
608
- style.nonce = cspNonce;
609
- }
610
- body.appendChild(style);
611
- }
612
- };
613
- const addBase = (targetELement, baseUri) => {
614
- const baseNode = targetELement.ownerDocument.createElement('base');
615
- baseNode.href = baseUri;
616
- const headEle = targetELement.getElementsByTagName('head').item(0);
617
- headEle?.insertBefore(baseNode, headEle?.firstChild ?? null);
618
- };