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,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;