html2canvas-pro 2.4.5 → 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 +3058 -1607
  3. package/dist/html2canvas-pro.cjs.map +1 -1
  4. package/dist/html2canvas-pro.esm.js +3058 -1607
  5. package/dist/html2canvas-pro.esm.js.map +1 -1
  6. package/dist/html2canvas-pro.js +3055 -1604
  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/canvas-renderer.d.ts +21 -15
  51. package/dist/types/render/canvas/mask-renderer.d.ts +36 -0
  52. package/dist/types/render/canvas/text-clip-renderer.d.ts +36 -0
  53. package/dist/types/render/canvas/text-renderer.d.ts +22 -0
  54. package/dist/types/render/stacking-context.d.ts +4 -1
  55. package/package.json +39 -40
  56. package/dist/lib/config.js +0 -49
  57. package/dist/lib/core/abort-helper.js +0 -20
  58. package/dist/lib/core/background-parser.js +0 -41
  59. package/dist/lib/core/bitwise.js +0 -5
  60. package/dist/lib/core/cache-storage.js +0 -283
  61. package/dist/lib/core/config-assembler.js +0 -88
  62. package/dist/lib/core/constants.js +0 -25
  63. package/dist/lib/core/context.js +0 -19
  64. package/dist/lib/core/debugger.js +0 -27
  65. package/dist/lib/core/features.js +0 -190
  66. package/dist/lib/core/logger.js +0 -56
  67. package/dist/lib/core/lru-map.js +0 -66
  68. package/dist/lib/core/origin-checker.js +0 -53
  69. package/dist/lib/core/performance-monitor.js +0 -185
  70. package/dist/lib/core/render-element.js +0 -131
  71. package/dist/lib/core/util.js +0 -4
  72. package/dist/lib/core/validator.js +0 -500
  73. package/dist/lib/css/grouped/background-styles.js +0 -36
  74. package/dist/lib/css/grouped/border-styles.js +0 -75
  75. package/dist/lib/css/grouped/font-styles.js +0 -93
  76. package/dist/lib/css/grouped/layout-styles.js +0 -127
  77. package/dist/lib/css/index.js +0 -339
  78. package/dist/lib/css/layout/bounds.js +0 -52
  79. package/dist/lib/css/layout/text.js +0 -133
  80. package/dist/lib/css/property-descriptor.js +0 -2
  81. package/dist/lib/css/property-descriptors/background-blend-mode.js +0 -41
  82. package/dist/lib/css/property-descriptors/background-clip.js +0 -25
  83. package/dist/lib/css/property-descriptors/background-color.js +0 -10
  84. package/dist/lib/css/property-descriptors/background-image.js +0 -24
  85. package/dist/lib/css/property-descriptors/background-origin.js +0 -23
  86. package/dist/lib/css/property-descriptors/background-position.js +0 -19
  87. package/dist/lib/css/property-descriptors/background-repeat.js +0 -33
  88. package/dist/lib/css/property-descriptors/background-size.js +0 -23
  89. package/dist/lib/css/property-descriptors/border-color.js +0 -14
  90. package/dist/lib/css/property-descriptors/border-image-repeat.js +0 -42
  91. package/dist/lib/css/property-descriptors/border-image-slice.js +0 -45
  92. package/dist/lib/css/property-descriptors/border-image-source.js +0 -22
  93. package/dist/lib/css/property-descriptors/border-radius.js +0 -15
  94. package/dist/lib/css/property-descriptors/border-style.js +0 -26
  95. package/dist/lib/css/property-descriptors/border-width.js +0 -20
  96. package/dist/lib/css/property-descriptors/box-decoration-break.js +0 -18
  97. package/dist/lib/css/property-descriptors/box-shadow.js +0 -54
  98. package/dist/lib/css/property-descriptors/clip-path.js +0 -189
  99. package/dist/lib/css/property-descriptors/color.js +0 -10
  100. package/dist/lib/css/property-descriptors/content.js +0 -19
  101. package/dist/lib/css/property-descriptors/counter-increment.js +0 -35
  102. package/dist/lib/css/property-descriptors/counter-reset.js +0 -18
  103. package/dist/lib/css/property-descriptors/direction.js +0 -18
  104. package/dist/lib/css/property-descriptors/display.js +0 -81
  105. package/dist/lib/css/property-descriptors/duration.js +0 -14
  106. package/dist/lib/css/property-descriptors/filter.js +0 -86
  107. package/dist/lib/css/property-descriptors/float.js +0 -22
  108. package/dist/lib/css/property-descriptors/font-family.js +0 -47
  109. package/dist/lib/css/property-descriptors/font-size.js +0 -10
  110. package/dist/lib/css/property-descriptors/font-style.js +0 -20
  111. package/dist/lib/css/property-descriptors/font-variant-ligatures.js +0 -34
  112. package/dist/lib/css/property-descriptors/font-variant.js +0 -13
  113. package/dist/lib/css/property-descriptors/font-weight.js +0 -25
  114. package/dist/lib/css/property-descriptors/image-rendering.js +0 -33
  115. package/dist/lib/css/property-descriptors/letter-spacing.js +0 -21
  116. package/dist/lib/css/property-descriptors/line-break.js +0 -23
  117. package/dist/lib/css/property-descriptors/line-height.js +0 -24
  118. package/dist/lib/css/property-descriptors/list-style-image.js +0 -17
  119. package/dist/lib/css/property-descriptors/list-style-position.js +0 -18
  120. package/dist/lib/css/property-descriptors/list-style-type.js +0 -122
  121. package/dist/lib/css/property-descriptors/margin.js +0 -13
  122. package/dist/lib/css/property-descriptors/mix-blend-mode.js +0 -31
  123. package/dist/lib/css/property-descriptors/object-fit.js +0 -28
  124. package/dist/lib/css/property-descriptors/object-position.js +0 -42
  125. package/dist/lib/css/property-descriptors/opacity.js +0 -16
  126. package/dist/lib/css/property-descriptors/overflow-wrap.js +0 -18
  127. package/dist/lib/css/property-descriptors/overflow.js +0 -27
  128. package/dist/lib/css/property-descriptors/padding.js +0 -14
  129. package/dist/lib/css/property-descriptors/paint-order.js +0 -33
  130. package/dist/lib/css/property-descriptors/position.js +0 -22
  131. package/dist/lib/css/property-descriptors/quotes.js +0 -41
  132. package/dist/lib/css/property-descriptors/rotate.js +0 -27
  133. package/dist/lib/css/property-descriptors/text-align.js +0 -21
  134. package/dist/lib/css/property-descriptors/text-decoration-color.js +0 -10
  135. package/dist/lib/css/property-descriptors/text-decoration-line.js +0 -28
  136. package/dist/lib/css/property-descriptors/text-decoration-style.js +0 -24
  137. package/dist/lib/css/property-descriptors/text-decoration-thickness.js +0 -26
  138. package/dist/lib/css/property-descriptors/text-overflow.js +0 -18
  139. package/dist/lib/css/property-descriptors/text-shadow.js +0 -46
  140. package/dist/lib/css/property-descriptors/text-transform.js +0 -20
  141. package/dist/lib/css/property-descriptors/text-underline-offset.js +0 -23
  142. package/dist/lib/css/property-descriptors/transform-origin.js +0 -24
  143. package/dist/lib/css/property-descriptors/transform.js +0 -57
  144. package/dist/lib/css/property-descriptors/visibility.js +0 -20
  145. package/dist/lib/css/property-descriptors/webkit-line-clamp.js +0 -26
  146. package/dist/lib/css/property-descriptors/webkit-text-stroke-color.js +0 -10
  147. package/dist/lib/css/property-descriptors/webkit-text-stroke-width.js +0 -16
  148. package/dist/lib/css/property-descriptors/white-space.js +0 -32
  149. package/dist/lib/css/property-descriptors/word-break.js +0 -26
  150. package/dist/lib/css/property-descriptors/writing-mode.js +0 -28
  151. package/dist/lib/css/property-descriptors/z-index.js +0 -19
  152. package/dist/lib/css/property-descriptors/zoom.js +0 -18
  153. package/dist/lib/css/syntax/parser.js +0 -141
  154. package/dist/lib/css/syntax/token-constants.js +0 -214
  155. package/dist/lib/css/syntax/token-singletons.js +0 -36
  156. package/dist/lib/css/syntax/token-types.js +0 -10
  157. package/dist/lib/css/syntax/tokenizer.js +0 -502
  158. package/dist/lib/css/type-descriptor.js +0 -2
  159. package/dist/lib/css/types/angle.js +0 -80
  160. package/dist/lib/css/types/color-math.js +0 -25
  161. package/dist/lib/css/types/color-spaces/a98.js +0 -78
  162. package/dist/lib/css/types/color-spaces/p3.js +0 -86
  163. package/dist/lib/css/types/color-spaces/pro-photo.js +0 -83
  164. package/dist/lib/css/types/color-spaces/rec2020.js +0 -85
  165. package/dist/lib/css/types/color-spaces/srgb.js +0 -79
  166. package/dist/lib/css/types/color-utilities.js +0 -342
  167. package/dist/lib/css/types/color.js +0 -400
  168. package/dist/lib/css/types/functions/-prefix-linear-gradient.js +0 -32
  169. package/dist/lib/css/types/functions/-prefix-radial-gradient.js +0 -83
  170. package/dist/lib/css/types/functions/-webkit-gradient.js +0 -59
  171. package/dist/lib/css/types/functions/counter.js +0 -367
  172. package/dist/lib/css/types/functions/gradient.js +0 -163
  173. package/dist/lib/css/types/functions/linear-gradient.js +0 -27
  174. package/dist/lib/css/types/functions/radial-gradient.js +0 -90
  175. package/dist/lib/css/types/functions/repeating-linear-gradient.js +0 -9
  176. package/dist/lib/css/types/image.js +0 -62
  177. package/dist/lib/css/types/index.js +0 -2
  178. package/dist/lib/css/types/length-percentage.js +0 -197
  179. package/dist/lib/css/types/length.js +0 -5
  180. package/dist/lib/css/types/safe-eval.js +0 -80
  181. package/dist/lib/css/types/time.js +0 -17
  182. package/dist/lib/dom/document-cloner.js +0 -618
  183. package/dist/lib/dom/dom-normalizer.js +0 -115
  184. package/dist/lib/dom/element-container.js +0 -71
  185. package/dist/lib/dom/elements/li-element-container.js +0 -11
  186. package/dist/lib/dom/elements/ol-element-container.js +0 -12
  187. package/dist/lib/dom/elements/select-element-container.js +0 -12
  188. package/dist/lib/dom/elements/textarea-element-container.js +0 -11
  189. package/dist/lib/dom/node-parser.js +0 -125
  190. package/dist/lib/dom/node-type-guards.js +0 -81
  191. package/dist/lib/dom/replaced-elements/canvas-element-container.js +0 -13
  192. package/dist/lib/dom/replaced-elements/iframe-element-container.js +0 -38
  193. package/dist/lib/dom/replaced-elements/image-element-container.js +0 -14
  194. package/dist/lib/dom/replaced-elements/index.js +0 -2
  195. package/dist/lib/dom/replaced-elements/input-element-container.js +0 -89
  196. package/dist/lib/dom/replaced-elements/svg-element-container.js +0 -57
  197. package/dist/lib/dom/slot-cloner.js +0 -176
  198. package/dist/lib/dom/text-container.js +0 -35
  199. package/dist/lib/index.js +0 -49
  200. package/dist/lib/options.js +0 -2
  201. package/dist/lib/render/background.js +0 -188
  202. package/dist/lib/render/bezier-curve.js +0 -34
  203. package/dist/lib/render/border.js +0 -104
  204. package/dist/lib/render/bound-curves.js +0 -188
  205. package/dist/lib/render/box-sizing.js +0 -20
  206. package/dist/lib/render/canvas/background-renderer.js +0 -338
  207. package/dist/lib/render/canvas/border-image-renderer.js +0 -153
  208. package/dist/lib/render/canvas/border-renderer.js +0 -184
  209. package/dist/lib/render/canvas/box-shadow-geometry.js +0 -52
  210. package/dist/lib/render/canvas/box-shadow-painter.js +0 -194
  211. package/dist/lib/render/canvas/box-shadow-transform.js +0 -42
  212. package/dist/lib/render/canvas/canvas-path.js +0 -25
  213. package/dist/lib/render/canvas/canvas-renderer.js +0 -610
  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,270 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.wrapTextToWidth = exports.inputTextClipBounds = void 0;
4
- exports.renderReplacedElements = renderReplacedElements;
5
- exports.renderFormElements = renderFormElements;
6
- exports.renderListMarker = renderListMarker;
7
- const color_utilities_1 = require("../../css/types/color-utilities");
8
- const image_element_container_1 = require("../../dom/replaced-elements/image-element-container");
9
- const canvas_element_container_1 = require("../../dom/replaced-elements/canvas-element-container");
10
- const svg_element_container_1 = require("../../dom/replaced-elements/svg-element-container");
11
- const iframe_element_container_1 = require("../../dom/replaced-elements/iframe-element-container");
12
- const input_element_container_1 = require("../../dom/replaced-elements/input-element-container");
13
- const textarea_element_container_1 = require("../../dom/elements/textarea-element-container");
14
- const select_element_container_1 = require("../../dom/elements/select-element-container");
15
- const bounds_1 = require("../../css/layout/bounds");
16
- const text_1 = require("../../css/layout/text");
17
- const vector_1 = require("../vector");
18
- const box_sizing_1 = require("../box-sizing");
19
- const font_utils_1 = require("./font-utils");
20
- const image_rendering_1 = require("../../css/property-descriptors/image-rendering");
21
- const length_percentage_1 = require("../../css/types/length-percentage");
22
- const line_height_1 = require("../../css/property-descriptors/line-height");
23
- const white_space_1 = require("../../css/property-descriptors/white-space");
24
- const bitwise_1 = require("../../core/bitwise");
25
- /**
26
- * Render replaced elements: Image, Canvas, SVG, IFrame.
27
- */
28
- async function renderReplacedElements(ctx, context, options, iframeRendererFactory, container, curves, styles, renderReplacedElementFn) {
29
- if (container instanceof image_element_container_1.ImageElementContainer) {
30
- try {
31
- const image = await context.cache.match(container.src);
32
- const prevSmoothing = ctx.imageSmoothingEnabled;
33
- if (styles.imageRendering === image_rendering_1.IMAGE_RENDERING.PIXELATED ||
34
- styles.imageRendering === image_rendering_1.IMAGE_RENDERING.CRISP_EDGES) {
35
- ctx.imageSmoothingEnabled = false;
36
- }
37
- else if (styles.imageRendering === image_rendering_1.IMAGE_RENDERING.SMOOTH) {
38
- ctx.imageSmoothingEnabled = true;
39
- }
40
- renderReplacedElementFn(container, curves, image);
41
- ctx.imageSmoothingEnabled = prevSmoothing;
42
- }
43
- catch (e) {
44
- context.logger.error(`Error loading image ${container.src}`);
45
- context.onError?.(e instanceof Error ? e : new Error(String(e)));
46
- }
47
- }
48
- if (container instanceof canvas_element_container_1.CanvasElementContainer) {
49
- renderReplacedElementFn(container, curves, container.canvas);
50
- }
51
- if (container instanceof svg_element_container_1.SVGElementContainer) {
52
- try {
53
- const image = await context.cache.match(container.svg);
54
- renderReplacedElementFn(container, curves, image);
55
- }
56
- catch (e) {
57
- context.logger.error(`Error loading svg ${container.svg.substring(0, 255)}`);
58
- context.onError?.(e instanceof Error ? e : new Error(String(e)));
59
- }
60
- }
61
- if (container instanceof iframe_element_container_1.IFrameElementContainer && container.tree) {
62
- const iframeRenderer = iframeRendererFactory(context, {
63
- scale: options.scale,
64
- backgroundColor: container.backgroundColor,
65
- x: 0,
66
- y: 0,
67
- width: container.width,
68
- height: container.height
69
- });
70
- const canvas = await iframeRenderer.render(container.tree);
71
- if (container.width && container.height) {
72
- ctx.drawImage(canvas, 0, 0, container.width, container.height, container.bounds.left, container.bounds.top, container.bounds.width, container.bounds.height);
73
- }
74
- }
75
- }
76
- /**
77
- * Render form element content: checkbox, radio, text input.
78
- */
79
- function renderFormElements(ctx, textRenderer, pathFn, container, styles) {
80
- if (container instanceof input_element_container_1.InputElementContainer) {
81
- const size = Math.min(container.bounds.width, container.bounds.height);
82
- if (container.type === input_element_container_1.CHECKBOX && container.checked) {
83
- ctx.save();
84
- pathFn([
85
- new vector_1.Vector(container.bounds.left + size * 0.39363, container.bounds.top + size * 0.79),
86
- new vector_1.Vector(container.bounds.left + size * 0.16, container.bounds.top + size * 0.5549),
87
- new vector_1.Vector(container.bounds.left + size * 0.27347, container.bounds.top + size * 0.44071),
88
- new vector_1.Vector(container.bounds.left + size * 0.39694, container.bounds.top + size * 0.5649),
89
- new vector_1.Vector(container.bounds.left + size * 0.72983, container.bounds.top + size * 0.23),
90
- new vector_1.Vector(container.bounds.left + size * 0.84, container.bounds.top + size * 0.34085),
91
- new vector_1.Vector(container.bounds.left + size * 0.39363, container.bounds.top + size * 0.79)
92
- ]);
93
- ctx.fillStyle = (0, color_utilities_1.asString)(input_element_container_1.INPUT_COLOR);
94
- ctx.fill();
95
- ctx.restore();
96
- }
97
- else if (container.type === input_element_container_1.RADIO && container.checked) {
98
- ctx.save();
99
- ctx.beginPath();
100
- ctx.arc(container.bounds.left + size / 2, container.bounds.top + size / 2, size / 4, 0, Math.PI * 2, true);
101
- ctx.fillStyle = (0, color_utilities_1.asString)(input_element_container_1.INPUT_COLOR);
102
- ctx.fill();
103
- ctx.restore();
104
- }
105
- }
106
- if (isTextInputElement(container) && container.value.length) {
107
- const [font] = textRenderer.createFontStyle(styles);
108
- // Use Canvas API to measure baseline from the actual rendered font
109
- ctx.font = font;
110
- const fontSizeValue = (0, length_percentage_1.getAbsoluteValue)(styles.fontSize, 0);
111
- const { baseline, height: fontHeight } = (0, font_utils_1.measureFontMetrics)(ctx, fontSizeValue);
112
- const isPlaceholder = container instanceof input_element_container_1.InputElementContainer && container.isPlaceholder;
113
- ctx.fillStyle = isPlaceholder ? (0, color_utilities_1.asString)(input_element_container_1.PLACEHOLDER_COLOR) : (0, color_utilities_1.asString)(styles.color);
114
- ctx.textBaseline = 'alphabetic';
115
- ctx.textAlign = canvasTextAlign(container.styles.textAlign);
116
- const bounds = (0, box_sizing_1.contentBox)(container);
117
- const clipBounds = container instanceof input_element_container_1.InputElementContainer
118
- ? (0, exports.inputTextClipBounds)(bounds, (0, box_sizing_1.paddingBox)(container), fontHeight)
119
- : bounds;
120
- let x = 0;
121
- switch (container.styles.textAlign) {
122
- case 1 /* TEXT_ALIGN.CENTER */:
123
- x += bounds.width / 2;
124
- break;
125
- case 2 /* TEXT_ALIGN.RIGHT */:
126
- x += bounds.width;
127
- break;
128
- }
129
- let verticalOffset = 0;
130
- if (container instanceof input_element_container_1.InputElementContainer) {
131
- verticalOffset = (bounds.height - fontHeight) / 2;
132
- }
133
- const textBounds = bounds.add(x, verticalOffset, 0, 0);
134
- ctx.save();
135
- pathFn([
136
- new vector_1.Vector(clipBounds.left, clipBounds.top),
137
- new vector_1.Vector(clipBounds.left + clipBounds.width, clipBounds.top),
138
- new vector_1.Vector(clipBounds.left + clipBounds.width, clipBounds.top + clipBounds.height),
139
- new vector_1.Vector(clipBounds.left, clipBounds.top + clipBounds.height)
140
- ]);
141
- ctx.clip();
142
- // A <textarea> soft-wraps its value at the element width (unless
143
- // white-space is `pre`/`nowrap`, e.g. wrap="off"); text inputs are
144
- // single-line. Render textareas line-by-line so long values wrap
145
- // instead of being clipped on one line (issue #228).
146
- const lines = (0, exports.wrapTextToWidth)(ctx, container.value, bounds.width, styles.whiteSpace === white_space_1.WHITE_SPACE.PRE || styles.whiteSpace === white_space_1.WHITE_SPACE.NOWRAP);
147
- const lineHeight = (0, line_height_1.computeLineHeight)(styles.lineHeight, fontSizeValue);
148
- lines.forEach((line, index) => {
149
- let lineX = textBounds.left;
150
- const lineWidth = ctx.measureText(line).width;
151
- switch (styles.textAlign) {
152
- case 1 /* TEXT_ALIGN.CENTER */:
153
- lineX += (bounds.width - lineWidth) / 2;
154
- break;
155
- case 2 /* TEXT_ALIGN.RIGHT */:
156
- lineX += bounds.width - lineWidth;
157
- break;
158
- }
159
- const lineBounds = new bounds_1.Bounds(lineX, textBounds.top + index * lineHeight, 0, 0);
160
- textRenderer.renderTextWithLetterSpacing(new text_1.TextBounds(line, lineBounds), styles.letterSpacing, baseline, styles.writingMode);
161
- });
162
- ctx.restore();
163
- ctx.textBaseline = 'alphabetic';
164
- ctx.textAlign = 'left';
165
- }
166
- }
167
- /**
168
- * Native single-line inputs may paint value text into their padding area when
169
- * fixed height and vertical padding leave a content box shorter than the font.
170
- * Preserve the content box horizontally, but avoid clipping those glyphs to the
171
- * collapsed vertical content box.
172
- * @internal exported for testing
173
- */
174
- const inputTextClipBounds = (contentBounds, inputPaddingBox, fontHeight) => contentBounds.height < fontHeight
175
- ? new bounds_1.Bounds(contentBounds.left, inputPaddingBox.top, contentBounds.width, inputPaddingBox.height)
176
- : contentBounds;
177
- exports.inputTextClipBounds = inputTextClipBounds;
178
- /**
179
- * Split text into lines that fit the given pixel width, preserving hard line
180
- * breaks and (when wrapping is enabled) breaking at word boundaries — mirroring
181
- * how a <textarea> lays out its value.
182
- * @internal exported for testing
183
- */
184
- const wrapTextToWidth = (ctx, text, maxWidth, noWrap) => {
185
- const lines = [];
186
- const paragraphs = text.split('\n');
187
- for (const paragraph of paragraphs) {
188
- if (noWrap) {
189
- lines.push(paragraph);
190
- continue;
191
- }
192
- const words = paragraph.split(/(\s+)/);
193
- let line = '';
194
- for (const word of words) {
195
- const candidate = line + word;
196
- if (ctx.measureText(candidate).width <= maxWidth || line === '') {
197
- line = candidate;
198
- }
199
- else {
200
- lines.push(line);
201
- line = word;
202
- }
203
- }
204
- lines.push(line);
205
- }
206
- return lines;
207
- };
208
- exports.wrapTextToWidth = wrapTextToWidth;
209
- /**
210
- * Render list-item marker (image or text).
211
- */
212
- async function renderListMarker(ctx, context, textRenderer, paint, container, styles) {
213
- if (!(0, bitwise_1.contains)(container.styles.display, 2048 /* DISPLAY.LIST_ITEM */)) {
214
- return;
215
- }
216
- if (container.styles.listStyleImage !== null) {
217
- const img = container.styles.listStyleImage;
218
- if (img.type === 0 /* CSSImageType.URL */) {
219
- const url = img.url;
220
- try {
221
- const image = await context.cache.match(url);
222
- ctx.drawImage(image, container.bounds.left - (image.width + 10), container.bounds.top);
223
- }
224
- catch (e) {
225
- context.logger.error(`Error loading list-style-image ${url}`);
226
- context.onError?.(e instanceof Error ? e : new Error(String(e)));
227
- }
228
- }
229
- }
230
- else if (paint.listValue && container.styles.listStyleType !== -1 /* LIST_STYLE_TYPE.NONE */) {
231
- const [font] = textRenderer.createFontStyle(styles);
232
- ctx.font = font;
233
- ctx.fillStyle = (0, color_utilities_1.asString)(styles.color);
234
- ctx.textBaseline = 'middle';
235
- ctx.textAlign = 'right';
236
- const bounds = new bounds_1.Bounds(container.bounds.left, container.bounds.top + (0, length_percentage_1.getAbsoluteValue)(container.styles.paddingTop, container.bounds.width), container.bounds.width, (0, line_height_1.computeLineHeight)(styles.lineHeight, styles.fontSize.number) / 2 + 1);
237
- textRenderer.renderTextWithLetterSpacing(new text_1.TextBounds(paint.listValue, bounds), styles.letterSpacing, (0, line_height_1.computeLineHeight)(styles.lineHeight, styles.fontSize.number) / 2 + 2, styles.writingMode);
238
- ctx.textBaseline = 'bottom';
239
- ctx.textAlign = 'left';
240
- }
241
- }
242
- /**
243
- * Type guard for text input containers.
244
- */
245
- const isTextInputElement = (container) => {
246
- if (container instanceof textarea_element_container_1.TextareaElementContainer) {
247
- return true;
248
- }
249
- else if (container instanceof select_element_container_1.SelectElementContainer) {
250
- return true;
251
- }
252
- else if (container instanceof input_element_container_1.InputElementContainer && container.type !== input_element_container_1.RADIO && container.type !== input_element_container_1.CHECKBOX) {
253
- return true;
254
- }
255
- return false;
256
- };
257
- /**
258
- * Map CSS text-align to Canvas textAlign.
259
- */
260
- const canvasTextAlign = (textAlign) => {
261
- switch (textAlign) {
262
- case 1 /* TEXT_ALIGN.CENTER */:
263
- return 'center';
264
- case 2 /* TEXT_ALIGN.RIGHT */:
265
- return 'right';
266
- case 0 /* TEXT_ALIGN.LEFT */:
267
- default:
268
- return 'left';
269
- }
270
- };
@@ -1,171 +0,0 @@
1
- "use strict";
2
- /**
3
- * Effects Renderer
4
- *
5
- * Handles rendering effects including:
6
- * - Opacity effects
7
- * - Transform effects (matrix transformations)
8
- * - Clip effects (overflow / border-radius clipping via Path[])
9
- * - Clip-path effects (CSS clip-path shapes: inset, circle, ellipse, polygon, path)
10
- * - Blend effects (mix-blend-mode)
11
- * - Filter effects (CSS filter functions)
12
- */
13
- Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.EffectsRenderer = void 0;
15
- const effects_1 = require("../effects");
16
- /**
17
- * Effects Renderer
18
- *
19
- * Manages rendering effects stack including opacity, transforms, and clipping.
20
- * Extracted from CanvasRenderer to improve code organization and maintainability.
21
- *
22
- * ## Save/restore optimisation
23
- *
24
- * Canvas `save()` / `restore()` snapshot and restore the entire canvas state
25
- * (transform matrix, clip region, compositing mode, filter, shadow, …) which
26
- * is relatively expensive. The old implementation called `save()` / `restore()`
27
- * for every single effect unconditionally.
28
- *
29
- * This implementation does a **batch pre-scan** before applying effects:
30
- *
31
- * | Effect type | Modifies | Reversible? | Needs save? |
32
- * |--------------------|----------------------|---------------|-------------|
33
- * | TransformEffect | transform matrix | ❌ irreversible | **YES** |
34
- * | ClipEffect | clip region | ❌ cumulative | **YES** |
35
- * | ClipPathEffect | clip region | ❌ cumulative | **YES** |
36
- * | OpacityEffect | globalAlpha | ✅ scalar | NO |
37
- * | BlendEffect | globalCompositeOp | ✅ scalar | NO |
38
- * | FilterEffect | filter + shadow | ✅ string+vec | NO |
39
- *
40
- * When the batch contains *only* lightweight effects (opacity / blend / filter)
41
- * we skip `save()` entirely and manually reset properties on pop.
42
- *
43
- * When the batch contains at least one heavyweight effect (transform / clip /
44
- * clip-path) we call `save()` **once** before the first heavyweight effect and
45
- * `restore()` **once** when that effect is popped.
46
- */
47
- class EffectsRenderer {
48
- constructor(deps, pathCallback) {
49
- this.activeEffects = [];
50
- /** Whether a canvas state save was performed for the current batch. */
51
- this.didSave = false;
52
- /** Index (0-based, from start of activeEffects array) of the last
53
- * heavyweight effect in the batch — the one whose pop triggers
54
- * restore(). Set to -1 when no save was performed. */
55
- this.saveAtDepth = -1;
56
- this.ctx = deps.ctx;
57
- this.pathCallback = pathCallback;
58
- }
59
- /**
60
- * Apply multiple effects.
61
- * Clears existing effects and applies new ones.
62
- */
63
- applyEffects(effects) {
64
- // ── 1. Pop all previously active effects ──────────────────────
65
- while (this.activeEffects.length) {
66
- this.popEffect();
67
- }
68
- // ── 2. Determine whether to save canvas state ─────────────────
69
- this.didSave = false;
70
- this.saveAtDepth = -1;
71
- const isHeavy = (e) => (0, effects_1.isTransformEffect)(e) || (0, effects_1.isClipEffect)(e) || (0, effects_1.isClipPathEffect)(e);
72
- // Pre-compute the index of the *last* heavyweight effect so pop
73
- // can reliably match it regardless of how many heavyweight effects
74
- // are in the batch (the save was issued before the first, but the
75
- // restore must happen when the LAST heavyweight is popped).
76
- const lastHeavyIdx = effects.reduceRight((found, e, idx) => (found !== -1 ? found : isHeavy(e) ? idx : -1), -1);
77
- // ── 3. Apply each effect ──────────────────────────────────────
78
- for (let i = 0; i < effects.length; i++) {
79
- const effect = effects[i];
80
- if (isHeavy(effect)) {
81
- if (!this.didSave) {
82
- this.ctx.save();
83
- this.didSave = true;
84
- }
85
- if (i === lastHeavyIdx) {
86
- this.saveAtDepth = this.activeEffects.length;
87
- }
88
- }
89
- this.applyEffect(effect);
90
- }
91
- }
92
- /**
93
- * Apply a single effect (called internally by applyEffects).
94
- */
95
- applyEffect(effect) {
96
- if ((0, effects_1.isTransformEffect)(effect)) {
97
- this.ctx.translate(effect.offsetX, effect.offsetY);
98
- this.ctx.transform(effect.matrix[0], effect.matrix[1], effect.matrix[2], effect.matrix[3], effect.matrix[4], effect.matrix[5]);
99
- this.ctx.translate(-effect.offsetX, -effect.offsetY);
100
- }
101
- else if ((0, effects_1.isClipEffect)(effect)) {
102
- this.pathCallback.path(effect.path);
103
- this.ctx.clip();
104
- }
105
- else if ((0, effects_1.isClipPathEffect)(effect)) {
106
- effect.applyClip(this.ctx);
107
- }
108
- else if ((0, effects_1.isOpacityEffect)(effect)) {
109
- // Multiply into current globalAlpha so nested opacity works
110
- // without save/restore overhead.
111
- this.ctx.globalAlpha *= effect.opacity;
112
- }
113
- else if ((0, effects_1.isBlendEffect)(effect)) {
114
- this.ctx.globalCompositeOperation = effect.compositeOperation;
115
- }
116
- else if ((0, effects_1.isFilterEffect)(effect)) {
117
- // drop-shadow() is rendered via ctx.shadow* to avoid canvas
118
- // taint; remaining filters go through ctx.filter.
119
- this.ctx.filter = effect.safeFilterString || 'none';
120
- if (effect.shadow) {
121
- this.ctx.shadowOffsetX = effect.shadow.offsetX;
122
- this.ctx.shadowOffsetY = effect.shadow.offsetY;
123
- this.ctx.shadowBlur = effect.shadow.blur;
124
- this.ctx.shadowColor = effect.shadow.color;
125
- }
126
- }
127
- this.activeEffects.push(effect);
128
- }
129
- /**
130
- * Remove the most recent effect.
131
- * Restores canvas state if needed.
132
- */
133
- popEffect() {
134
- if (this.activeEffects.length === 0)
135
- return;
136
- // If the effect being popped is the one that triggered the save,
137
- // restore the canvas state now.
138
- if (this.didSave && this.activeEffects.length - 1 === this.saveAtDepth) {
139
- this.ctx.restore();
140
- this.didSave = false;
141
- this.saveAtDepth = -1;
142
- }
143
- this.activeEffects.pop();
144
- // If the batch had ONLY lightweight effects (no save was performed),
145
- // reset properties to their defaults. We only do this when all effects
146
- // have been popped (activeEffects is empty), so properties don't
147
- // flip mid-batch.
148
- if (!this.didSave && this.activeEffects.length === 0) {
149
- this.ctx.globalAlpha = 1;
150
- this.ctx.globalCompositeOperation = 'source-over';
151
- this.ctx.filter = 'none';
152
- this.ctx.shadowOffsetX = 0;
153
- this.ctx.shadowOffsetY = 0;
154
- this.ctx.shadowBlur = 0;
155
- this.ctx.shadowColor = 'rgba(0, 0, 0, 0)';
156
- }
157
- }
158
- /**
159
- * Get the current number of active effects.
160
- */
161
- getActiveEffectCount() {
162
- return this.activeEffects.length;
163
- }
164
- /**
165
- * Check if there are any active effects.
166
- */
167
- hasActiveEffects() {
168
- return this.activeEffects.length > 0;
169
- }
170
- }
171
- exports.EffectsRenderer = EffectsRenderer;