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,633 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CanvasRenderer = void 0;
4
- const stacking_context_1 = require("../stacking-context");
5
- const color_utilities_1 = require("../../css/types/color-utilities");
6
- const path_1 = require("../path");
7
- const bound_curves_1 = require("../bound-curves");
8
- const background_1 = require("../background");
9
- const box_sizing_1 = require("../box-sizing");
10
- const effects_1 = require("../effects");
11
- const mix_blend_mode_1 = require("../../css/property-descriptors/mix-blend-mode");
12
- const filter_surface_1 = require("./filter-surface");
13
- const surface_bounds_1 = require("./surface-bounds");
14
- const bounds_1 = require("../../css/layout/bounds");
15
- const bitwise_1 = require("../../core/bitwise");
16
- const text_renderer_1 = require("./text-renderer");
17
- const background_renderer_1 = require("./background-renderer");
18
- const border_renderer_1 = require("./border-renderer");
19
- const border_image_renderer_1 = require("./border-image-renderer");
20
- const effects_renderer_1 = require("./effects-renderer");
21
- const canvas_path_1 = require("./canvas-path");
22
- const object_fit_1 = require("../object-fit");
23
- const content_renderer_1 = require("./content-renderer");
24
- const constants_1 = require("../../core/constants");
25
- const writing_mode_1 = require("../../css/property-descriptors/writing-mode");
26
- const font_utils_1 = require("./font-utils");
27
- const text_1 = require("../../css/layout/text");
28
- class CanvasRenderer {
29
- constructor(context, options, surfaceRoot, surfaceBudget = { pixels: 0 }) {
30
- this.surfaceRoot = surfaceRoot;
31
- this.surfaceBudget = surfaceBudget;
32
- this.legacySubtree = false;
33
- this.context = context;
34
- this.options = options;
35
- this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
36
- const ctx = this.canvas.getContext('2d');
37
- if (!ctx) {
38
- if (!options.canvas)
39
- (0, filter_surface_1.releaseSurface)(this.canvas);
40
- if (surfaceRoot)
41
- throw new filter_surface_1.FilterSurfaceError('Filter surface canvas is unavailable');
42
- throw new Error('Failed to get 2D rendering context from canvas');
43
- }
44
- this.ctx = ctx;
45
- if (!options.canvas) {
46
- this.canvas.width = Math.floor(options.width * options.scale);
47
- this.canvas.height = Math.floor(options.height * options.scale);
48
- this.canvas.style.width = `${options.width}px`;
49
- this.canvas.style.height = `${options.height}px`;
50
- }
51
- this.ctx.scale(this.options.scale, this.options.scale);
52
- this.ctx.translate(-options.x, -options.y);
53
- this.ctx.textBaseline = 'bottom';
54
- // Set image smoothing options
55
- if (options.imageSmoothing !== undefined) {
56
- this.ctx.imageSmoothingEnabled = options.imageSmoothing;
57
- }
58
- if (options.imageSmoothingQuality) {
59
- this.ctx.imageSmoothingQuality = options.imageSmoothingQuality;
60
- }
61
- // Initialize specialized renderers
62
- this.backgroundRenderer = new background_renderer_1.BackgroundRenderer({
63
- ctx: this.ctx,
64
- context: this.context,
65
- canvas: this.canvas,
66
- options: {
67
- width: options.width,
68
- height: options.height,
69
- scale: options.scale
70
- }
71
- });
72
- this.borderRenderer = new border_renderer_1.BorderRenderer({ ctx: this.ctx }, {
73
- path: (paths) => this.path(paths),
74
- formatPath: (paths) => this.formatPath(paths)
75
- });
76
- this.borderImageRenderer = new border_image_renderer_1.BorderImageRenderer(this.ctx);
77
- this.effectsRenderer = new effects_renderer_1.EffectsRenderer({ ctx: this.ctx }, { path: (paths) => this.path(paths) });
78
- this.textRenderer = new text_renderer_1.TextRenderer({
79
- ctx: this.ctx,
80
- options: { scale: options.scale }
81
- });
82
- this.context.logger.debug(`Canvas renderer initialized (${options.width}x${options.height}) with scale ${options.scale}`);
83
- }
84
- async renderStack(stack) {
85
- const styles = stack.element.container.styles;
86
- if (styles.isVisible()) {
87
- const filter = (0, filter_surface_1.parseSimpleFilter)(styles.filter);
88
- if (stack.element !== this.surfaceRoot &&
89
- !this.legacySubtree &&
90
- filter &&
91
- (styles.filter || styles.opacity < 1) &&
92
- this.canComposite(stack.element)) {
93
- if (!(await this.renderCompositedStack(stack, filter))) {
94
- // A rejected optional surface must preserve the previous subtree path.
95
- this.legacySubtree = true;
96
- try {
97
- await this.renderStackContent(stack);
98
- }
99
- finally {
100
- this.legacySubtree = false;
101
- }
102
- }
103
- return;
104
- }
105
- await this.renderStackContent(stack);
106
- }
107
- }
108
- // Draft scope: transformed, blended and clip-path subtrees keep the old path.
109
- // The surface root owns its filter/opacity; ancestors are applied at composition.
110
- canComposite(paint) {
111
- const supported = (container) => {
112
- const styles = container.styles;
113
- return (!styles.isTransformed() &&
114
- styles.zoom === 1 &&
115
- styles.mixBlendMode === mix_blend_mode_1.MIX_BLEND_MODE.NORMAL &&
116
- styles.clipPath.type === 0 /* CLIP_PATH_TYPE.NONE */ &&
117
- !(0, bitwise_1.contains)(styles.display, 2048 /* DISPLAY.LIST_ITEM */) &&
118
- (0, filter_surface_1.parseSimpleFilter)(styles.filter) !== null);
119
- };
120
- const subtree = (container) => supported(container) && container.elements.every(subtree);
121
- let ancestor = paint.parent;
122
- while (ancestor && ancestor !== this.surfaceRoot) {
123
- if (!supported(ancestor.container))
124
- return false;
125
- ancestor = ancestor.parent;
126
- }
127
- return subtree(paint.container);
128
- }
129
- async renderCompositedStack(stack, filter) {
130
- const signal = this.options.signal;
131
- if (signal?.aborted)
132
- throw new DOMException('The operation was aborted.', 'AbortError');
133
- const bounds = (0, surface_bounds_1.cropSurface)((0, surface_bounds_1.surfaceBounds)(stack.element.container), new bounds_1.Bounds(this.options.x, this.options.y, this.options.width, this.options.height), (0, filter_surface_1.filterOutset)(filter), this.options.scale);
134
- if (!bounds.width || !bounds.height)
135
- return true;
136
- const reserved = (0, surface_bounds_1.reserveSurface)(this.surfaceBudget, Math.ceil(bounds.width * this.options.scale), Math.ceil(bounds.height * this.options.scale));
137
- if (!reserved) {
138
- this.context.logger.info('Filter surface budget exceeded; using the existing renderer');
139
- return false;
140
- }
141
- const options = {
142
- ...this.options,
143
- canvas: undefined,
144
- backgroundColor: null,
145
- x: bounds.left,
146
- y: bounds.top,
147
- width: bounds.width,
148
- height: bounds.height
149
- };
150
- let source;
151
- let filtered;
152
- try {
153
- source = new CanvasRenderer(this.context, options, stack.element, this.surfaceBudget);
154
- await source.renderStackContent(stack);
155
- source.effectsRenderer.applyEffects([]);
156
- filtered = await (0, filter_surface_1.renderFilterSurface)(source.canvas, filter, stack.element.container.styles.opacity, options.scale, signal);
157
- if (signal?.aborted)
158
- throw new DOMException('The operation was aborted.', 'AbortError');
159
- const own = stack.element.effects;
160
- const effects = stack.element
161
- .getEffects(4 /* EffectTarget.CONTENT */, this.surfaceRoot)
162
- .filter((effect) => !own.includes(effect) ||
163
- (!(0, effects_1.isFilterEffect)(effect) && !(0, effects_1.isOpacityEffect)(effect) && !(0, effects_1.isClipEffect)(effect)));
164
- this.effectsRenderer.applyEffects(effects);
165
- this.ctx.drawImage(filtered, options.x, options.y, filtered.width / options.scale, filtered.height / options.scale);
166
- return true;
167
- }
168
- catch (error) {
169
- if (!(error instanceof filter_surface_1.FilterSurfaceError))
170
- throw error;
171
- this.context.logger.info(`${error.message}; using the existing renderer`);
172
- return false;
173
- }
174
- finally {
175
- if (source)
176
- (0, filter_surface_1.releaseSurface)(source.canvas);
177
- if (filtered)
178
- (0, filter_surface_1.releaseSurface)(filtered);
179
- this.surfaceBudget.pixels -= reserved;
180
- }
181
- }
182
- async renderNode(paint) {
183
- if (paint.container.debugRender) {
184
- debugger;
185
- }
186
- if (paint.container.styles.isVisible()) {
187
- await this.renderNodeBackgroundAndBorders(paint);
188
- await this.renderNodeContent(paint);
189
- }
190
- }
191
- /**
192
- * Helper method to render text with paint order support
193
- * Reduces code duplication in line-clamp and normal rendering
194
- */
195
- // Helper method to truncate text and add ellipsis if needed
196
- renderReplacedElement(container, curves, image) {
197
- const intrinsicWidth = image.naturalWidth || container.intrinsicWidth;
198
- const intrinsicHeight = image.naturalHeight || container.intrinsicHeight;
199
- if (image && intrinsicWidth > 0 && intrinsicHeight > 0) {
200
- const box = (0, box_sizing_1.contentBox)(container);
201
- const path = (0, bound_curves_1.calculatePaddingBoxPath)(curves);
202
- this.path(path);
203
- this.ctx.save();
204
- this.ctx.clip();
205
- const { sx, sy, sw, sh, dx, dy, dw, dh } = (0, object_fit_1.calculateObjectFitRendering)(intrinsicWidth, intrinsicHeight, box, container.styles.objectFit, container.styles.objectPosition);
206
- this.ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
207
- this.ctx.restore();
208
- }
209
- }
210
- async renderNodeContent(paint) {
211
- this.effectsRenderer.applyEffects(paint.getEffects(4 /* EffectTarget.CONTENT */, this.surfaceRoot));
212
- const container = paint.container;
213
- const curves = paint.curves;
214
- const styles = container.styles;
215
- // Use content box for text overflow calculation (excludes padding and border)
216
- const textBounds = (0, box_sizing_1.contentBox)(container);
217
- for (const child of container.textNodes) {
218
- await this.textRenderer.renderTextNode(child, styles, textBounds);
219
- }
220
- await (0, content_renderer_1.renderReplacedElements)(this.ctx, this.context, {
221
- scale: this.options.scale,
222
- backgroundColor: this.options.backgroundColor,
223
- x: this.options.x,
224
- y: this.options.y,
225
- width: this.options.width,
226
- height: this.options.height
227
- }, (ctx, opts) => new CanvasRenderer(ctx, opts), container, curves, styles, (c, cv, img) => this.renderReplacedElement(c, cv, img));
228
- (0, content_renderer_1.renderFormElements)(this.ctx, this.textRenderer, this.path.bind(this), container, styles);
229
- await (0, content_renderer_1.renderListMarker)(this.ctx, this.context, this.textRenderer, paint, container, styles);
230
- }
231
- async renderStackContent(stack) {
232
- if (stack.element.container.debugRender) {
233
- debugger;
234
- }
235
- const signal = this.options.signal;
236
- // https://www.w3.org/TR/css-position-3/#painting-order
237
- // 1. the background and borders of the element forming the stacking context.
238
- await this.renderNodeBackgroundAndBorders(stack.element);
239
- // 2. the child stacking contexts with negative stack levels (most negative first).
240
- for (const child of stack.negativeZIndex) {
241
- if (signal?.aborted)
242
- throw new DOMException('The operation was aborted.', 'AbortError');
243
- await this.renderStack(child);
244
- }
245
- // 3. For all its in-flow, non-positioned, block-level descendants in tree order:
246
- if (signal?.aborted)
247
- throw new DOMException('The operation was aborted.', 'AbortError');
248
- await this.renderNodeContent(stack.element);
249
- for (const child of stack.nonInlineLevel) {
250
- await this.renderNode(child);
251
- }
252
- // 4. All non-positioned floating descendants, in tree order. For each one of these,
253
- // treat the element as if it created a new stacking context, but any positioned descendants and descendants
254
- // which actually create a new stacking context should be considered part of the parent stacking context,
255
- // not this new one.
256
- for (const child of stack.nonPositionedFloats) {
257
- if (signal?.aborted)
258
- throw new DOMException('The operation was aborted.', 'AbortError');
259
- await this.renderStack(child);
260
- }
261
- // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
262
- for (const child of stack.nonPositionedInlineLevel) {
263
- if (signal?.aborted)
264
- throw new DOMException('The operation was aborted.', 'AbortError');
265
- await this.renderStack(child);
266
- }
267
- for (const child of stack.inlineLevel) {
268
- await this.renderNode(child);
269
- }
270
- // 6. All positioned, opacity or transform descendants, in tree order that fall into the following categories:
271
- // All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order.
272
- // For those with 'z-index: auto', treat the element as if it created a new stacking context,
273
- // but any positioned descendants and descendants which actually create a new stacking context should be
274
- // considered part of the parent stacking context, not this new one. For those with 'z-index: 0',
275
- // treat the stacking context generated atomically.
276
- //
277
- // All opacity descendants with opacity less than 1
278
- //
279
- // All transform descendants with transform other than none
280
- for (const child of stack.zeroOrAutoZIndexOrTransformedOrOpacity) {
281
- if (signal?.aborted)
282
- throw new DOMException('The operation was aborted.', 'AbortError');
283
- await this.renderStack(child);
284
- }
285
- // 7. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index
286
- // order (smallest first) then tree order.
287
- for (const child of stack.positiveZIndex) {
288
- if (signal?.aborted)
289
- throw new DOMException('The operation was aborted.', 'AbortError');
290
- await this.renderStack(child);
291
- }
292
- }
293
- mask(paths) {
294
- this.ctx.beginPath();
295
- // Anchor the mask rectangle to (options.x, options.y) instead of (0, 0)
296
- // because ctx.translate(-options.x, -options.y) shifts the coordinate system.
297
- // Fix for Issue #230: inset box-shadow rendering was inverted for elements
298
- // not at document origin due to the mask covering the wrong region.
299
- const x = this.options.x;
300
- const y = this.options.y;
301
- this.ctx.moveTo(x, y);
302
- this.ctx.lineTo(x + this.options.width, y);
303
- this.ctx.lineTo(x + this.options.width, y + this.options.height);
304
- this.ctx.lineTo(x, y + this.options.height);
305
- this.ctx.lineTo(x, y);
306
- this.formatPath(paths.slice(0).reverse());
307
- this.ctx.closePath();
308
- }
309
- path(paths) {
310
- (0, canvas_path_1.createCanvasPath)(this.ctx, paths);
311
- }
312
- formatPath(paths) {
313
- (0, canvas_path_1.formatCanvasPath)(this.ctx, paths);
314
- }
315
- async renderSurfaceBoxShadow(paint, shadow) {
316
- // Rasterize a visible silhouette, then blur it. Linux WebKit can drop the
317
- // interior of Canvas shadows whose solid mask is outside a cropped canvas.
318
- const spread = shadow.spread.number;
319
- const blur = shadow.blur.number / 2;
320
- const margin = Math.ceil(3 * blur) + 1;
321
- const box = paint.container.bounds.add(-spread, -spread, 2 * spread, 2 * spread);
322
- if (box.width <= 0 || box.height <= 0)
323
- return;
324
- const scale = this.options.scale;
325
- const bounds = (0, surface_bounds_1.cropSurface)(box.add(-margin, -margin, 2 * margin, 2 * margin), new bounds_1.Bounds(this.options.x - shadow.offsetX.number, this.options.y - shadow.offsetY.number, this.options.width, this.options.height), margin, scale);
326
- if (!bounds.width || !bounds.height)
327
- return;
328
- const width = Math.ceil(bounds.width * scale), height = Math.ceil(bounds.height * scale);
329
- const reserved = (0, surface_bounds_1.reserveSurface)(this.surfaceBudget, width, height);
330
- if (!reserved)
331
- throw new filter_surface_1.FilterSurfaceError('Box shadow surface budget exceeded');
332
- let source;
333
- let filtered;
334
- try {
335
- source = document.createElement('canvas');
336
- source.width = width;
337
- source.height = height;
338
- const ctx = source.getContext('2d');
339
- if (!ctx)
340
- throw new filter_surface_1.FilterSurfaceError('Box shadow surface canvas is unavailable');
341
- ctx.scale(scale, scale);
342
- ctx.translate(-bounds.left, -bounds.top);
343
- (0, canvas_path_1.createCanvasPath)(ctx, (0, path_1.transformPath)((0, bound_curves_1.calculateBorderBoxPath)(paint.curves), -spread, -spread, 2 * spread, 2 * spread));
344
- ctx.fillStyle = (0, color_utilities_1.asString)(shadow.color);
345
- ctx.fill();
346
- filtered = await (0, filter_surface_1.renderFilterSurface)(source, { blur }, 1, scale, this.options.signal);
347
- this.ctx.save();
348
- try {
349
- this.ctx.beginPath();
350
- this.ctx.rect(this.options.x, this.options.y, this.options.width, this.options.height);
351
- (0, canvas_path_1.formatCanvasPath)(this.ctx, (0, bound_curves_1.calculateBorderBoxPath)(paint.curves));
352
- this.ctx.closePath();
353
- this.ctx.clip('evenodd');
354
- this.ctx.drawImage(filtered, bounds.left + shadow.offsetX.number, bounds.top + shadow.offsetY.number, filtered.width / scale, filtered.height / scale);
355
- }
356
- finally {
357
- this.ctx.restore();
358
- }
359
- }
360
- finally {
361
- if (source)
362
- (0, filter_surface_1.releaseSurface)(source);
363
- if (filtered)
364
- (0, filter_surface_1.releaseSurface)(filtered);
365
- this.surfaceBudget.pixels -= reserved;
366
- }
367
- }
368
- async renderNodeBackgroundAndBorders(paint) {
369
- this.effectsRenderer.applyEffects(paint.getEffects(2 /* EffectTarget.BACKGROUND_BORDERS */, this.surfaceRoot));
370
- const styles = paint.container.styles;
371
- const hasBackground = !(0, color_utilities_1.isTransparent)(styles.backgroundColor) || styles.backgroundImage.length;
372
- const hasTextClip = hasTextBackgroundClip(styles);
373
- const borders = [
374
- { style: styles.borderTopStyle, color: styles.borderTopColor, width: styles.borderTopWidth },
375
- { style: styles.borderRightStyle, color: styles.borderRightColor, width: styles.borderRightWidth },
376
- { style: styles.borderBottomStyle, color: styles.borderBottomColor, width: styles.borderBottomWidth },
377
- { style: styles.borderLeftStyle, color: styles.borderLeftColor, width: styles.borderLeftWidth }
378
- ];
379
- const backgroundClipValue = (0, background_1.getBackgroundValueForIndex)(styles.backgroundClip, 0);
380
- const backgroundPaintingArea = calculateBackgroundCurvedPaintingArea(backgroundClipValue, paint.curves);
381
- if (hasBackground || styles.boxShadow.length) {
382
- // Handle background-clip: text
383
- if (hasTextClip && paint.container.textNodes.length > 0) {
384
- await this.renderTextClippedBackground(paint);
385
- }
386
- else {
387
- this.ctx.save();
388
- this.path(backgroundPaintingArea);
389
- this.ctx.clip();
390
- if (!(0, color_utilities_1.isTransparent)(styles.backgroundColor)) {
391
- this.ctx.fillStyle = (0, color_utilities_1.asString)(styles.backgroundColor);
392
- this.ctx.fill();
393
- }
394
- await this.backgroundRenderer.renderBackgroundImage(paint.container);
395
- this.ctx.restore();
396
- }
397
- for (const shadow of styles.boxShadow.slice(0).reverse()) {
398
- if (this.surfaceRoot && !shadow.inset) {
399
- await this.renderSurfaceBoxShadow(paint, shadow);
400
- continue;
401
- }
402
- this.ctx.save();
403
- const borderBoxArea = (0, bound_curves_1.calculateBorderBoxPath)(paint.curves);
404
- const maskOffset = shadow.inset ? 0 : constants_1.SHADOW_MASK_OFFSET;
405
- const shadowPaintingArea = (0, path_1.transformPath)(borderBoxArea, -maskOffset + (shadow.inset ? 1 : -1) * shadow.spread.number, (shadow.inset ? 1 : -1) * shadow.spread.number, shadow.spread.number * (shadow.inset ? -2 : 2), shadow.spread.number * (shadow.inset ? -2 : 2));
406
- if (shadow.inset) {
407
- this.path(borderBoxArea);
408
- this.ctx.clip();
409
- this.mask(shadowPaintingArea);
410
- }
411
- else {
412
- this.mask(borderBoxArea);
413
- this.ctx.clip();
414
- this.path(shadowPaintingArea);
415
- }
416
- // Canvas shadow metrics ignore the transform. Surface sources
417
- // need capture-pixel offsets, including the displaced mask.
418
- const shadowScale = this.surfaceRoot ? this.options.scale : 1;
419
- this.ctx.shadowOffsetX = (shadow.offsetX.number + maskOffset) * shadowScale;
420
- this.ctx.shadowOffsetY = shadow.offsetY.number * shadowScale;
421
- this.ctx.shadowColor = (0, color_utilities_1.asString)(shadow.color);
422
- this.ctx.shadowBlur = shadow.blur.number * shadowScale;
423
- this.ctx.fillStyle = shadow.inset ? (0, color_utilities_1.asString)(shadow.color) : 'rgba(0,0,0,1)';
424
- this.ctx.fill();
425
- this.ctx.restore();
426
- }
427
- }
428
- // Render border-image if present (replaces traditional borders per CSS spec)
429
- if (styles.borderImageSource) {
430
- const source = styles.borderImageSource;
431
- if (source.type === 0 /* CSSImageType.URL */) {
432
- const url = source.url;
433
- try {
434
- const image = await this.context.cache.match(url);
435
- if (image) {
436
- const bounds = paint.container.bounds;
437
- this.borderImageRenderer.renderBorderImage(bounds, image, styles.borderImageSlice, styles.borderImageRepeat, Math.max(0, styles.borderTopWidth), Math.max(0, styles.borderRightWidth), Math.max(0, styles.borderBottomWidth), Math.max(0, styles.borderLeftWidth));
438
- }
439
- }
440
- catch (e) {
441
- this.context.logger.error(`Error loading border-image ${url}`);
442
- }
443
- }
444
- // When border-image is present, skip regular border rendering
445
- return;
446
- }
447
- let side = 0;
448
- for (const border of borders) {
449
- if (border.style !== 0 /* BORDER_STYLE.NONE */ && !(0, color_utilities_1.isTransparent)(border.color) && border.width > 0) {
450
- const renderBorderSide = async () => {
451
- if (border.style === 2 /* BORDER_STYLE.DASHED */) {
452
- await this.borderRenderer.renderDashedDottedBorder(border.color, border.width, side, paint.curves, 2 /* BORDER_STYLE.DASHED */);
453
- }
454
- else if (border.style === 3 /* BORDER_STYLE.DOTTED */) {
455
- await this.borderRenderer.renderDashedDottedBorder(border.color, border.width, side, paint.curves, 3 /* BORDER_STYLE.DOTTED */);
456
- }
457
- else if (border.style === 4 /* BORDER_STYLE.DOUBLE */) {
458
- await this.borderRenderer.renderDoubleBorder(border.color, border.width, side, paint.curves);
459
- }
460
- else {
461
- await this.borderRenderer.renderSolidBorder(border.color, side, paint.curves);
462
- }
463
- };
464
- // Browsers break a <fieldset>'s top border where its <legend>
465
- // sits, instead of drawing the border through the legend (issue
466
- // #227). Render the top border in two clipped segments around
467
- // the legend's horizontal span.
468
- const legend = paint.container.legendBounds;
469
- if (side === 0 && legend) {
470
- const bounds = paint.container.bounds;
471
- const legendLeft = legend.left - bounds.left;
472
- const legendRight = legend.left + legend.width - bounds.left;
473
- if (legendLeft > 0) {
474
- this.ctx.save();
475
- this.ctx.beginPath();
476
- this.ctx.rect(bounds.left, bounds.top, legendLeft, bounds.height);
477
- this.ctx.clip();
478
- await renderBorderSide();
479
- this.ctx.restore();
480
- }
481
- const rightStart = bounds.left + legendRight;
482
- const rightWidth = bounds.width - legendRight;
483
- if (rightWidth > 0) {
484
- this.ctx.save();
485
- this.ctx.beginPath();
486
- this.ctx.rect(rightStart, bounds.top, rightWidth, bounds.height);
487
- this.ctx.clip();
488
- await renderBorderSide();
489
- this.ctx.restore();
490
- }
491
- }
492
- else {
493
- await renderBorderSide();
494
- }
495
- }
496
- side++;
497
- }
498
- }
499
- async render(element) {
500
- try {
501
- if (this.options.backgroundColor) {
502
- this.ctx.fillStyle = (0, color_utilities_1.asString)(this.options.backgroundColor);
503
- this.ctx.fillRect(this.options.x, this.options.y, this.options.width, this.options.height);
504
- }
505
- const stack = (0, stacking_context_1.parseStackingContexts)(element);
506
- await this.renderStack(stack);
507
- this.effectsRenderer.applyEffects([]);
508
- return this.canvas;
509
- }
510
- catch (error) {
511
- // Failed captures have no returned canvas. Release only our backing
512
- // store; a canvas supplied by the caller remains caller-owned.
513
- if (!this.options.canvas)
514
- (0, filter_surface_1.releaseSurface)(this.canvas);
515
- throw error;
516
- }
517
- }
518
- /**
519
- * Render background clipped to text shape using offscreen canvas with destination-in compositing.
520
- * This implements background-clip: text support.
521
- *
522
- * Algorithm:
523
- * 1. Draw background (color + images) onto an offscreen canvas
524
- * 2. Use destination-in compositing to clip the background to the text glyph shapes
525
- * 3. Composite the result back onto the main canvas
526
- *
527
- * The offscreen canvas uses CSS-pixel dimensions (not device-pixel) because the
528
- * background renderer handles scaling internally via BackgroundRenderer options.
529
- */
530
- async renderTextClippedBackground(paint) {
531
- const container = paint.container;
532
- const styles = container.styles;
533
- const bounds = container.bounds;
534
- if (bounds.width <= 0 || bounds.height <= 0) {
535
- return;
536
- }
537
- const ownerDocument = this.canvas.ownerDocument ?? document;
538
- const offscreen = ownerDocument.createElement('canvas');
539
- const width = Math.ceil(bounds.width);
540
- const height = Math.ceil(bounds.height);
541
- offscreen.width = width;
542
- offscreen.height = height;
543
- const offCtx = offscreen.getContext('2d');
544
- if (!offCtx) {
545
- return;
546
- }
547
- // ── Set up font matching the main context ──
548
- const [fontString] = this.textRenderer.createFontStyle(styles);
549
- offCtx.font = fontString;
550
- offCtx.textBaseline = 'alphabetic';
551
- offCtx.textAlign = 'left';
552
- offCtx.direction = styles.direction === 1 /* DIRECTION.RTL */ ? 'rtl' : 'ltr';
553
- // Measure baseline from the actual rendered font
554
- const baseline = (0, font_utils_1.measureBaseline)(offCtx, styles.fontSize.number);
555
- // ── Draw background onto offscreen canvas ──
556
- const bgRenderer = new background_renderer_1.BackgroundRenderer({
557
- ctx: offCtx,
558
- context: this.context,
559
- canvas: offscreen,
560
- options: { width, height, scale: 1 }
561
- });
562
- if (!(0, color_utilities_1.isTransparent)(styles.backgroundColor)) {
563
- offCtx.fillStyle = (0, color_utilities_1.asString)(styles.backgroundColor);
564
- offCtx.fillRect(0, 0, width, height);
565
- }
566
- // Background images are positioned relative to the element bounds,
567
- // so we need the BackgroundRenderer to compute offsets in the same
568
- // coordinate space as the main renderer. We do that by translating
569
- // the offscreen context so that the element origin (bounds.left, bounds.top)
570
- // falls at (0, 0).
571
- offCtx.save();
572
- offCtx.translate(-bounds.left, -bounds.top);
573
- await bgRenderer.renderBackgroundImage(container);
574
- offCtx.restore();
575
- // ── Clip background to text glyphs ──
576
- // destination-in: keep background pixels only where the source (text) is non-transparent.
577
- offCtx.globalCompositeOperation = 'destination-in';
578
- offCtx.fillStyle = '#000';
579
- const writingMode = styles.writingMode;
580
- const letterSpacing = styles.letterSpacing;
581
- for (const textNode of container.textNodes) {
582
- for (const textBound of textNode.textBounds) {
583
- // Offset from element bounds to canvas-local coordinates
584
- const localLeft = textBound.bounds.left - bounds.left;
585
- const localTop = textBound.bounds.top - bounds.top + baseline;
586
- if (letterSpacing > 0) {
587
- this.renderTextMaskWithLetterSpacing(offCtx, textBound, letterSpacing, localLeft, localTop, writingMode);
588
- }
589
- else {
590
- offCtx.fillText(textBound.text, localLeft, localTop);
591
- }
592
- }
593
- }
594
- // ── Composite back to main canvas ──
595
- this.ctx.drawImage(offscreen, bounds.left, bounds.top);
596
- }
597
- /**
598
- * Render text glyphs one-by-one as a mask, applying letter-spacing between characters.
599
- * This ensures the mask matches how the text renderer lays out the characters.
600
- */
601
- renderTextMaskWithLetterSpacing(ctx, text, letterSpacing, x, y, writingMode) {
602
- const letters = (0, text_1.segmentGraphemes)(text.text);
603
- let offset = x;
604
- for (const letter of letters) {
605
- if ((0, writing_mode_1.isVerticalWritingMode)(writingMode)) {
606
- // Vertical writing mode: not yet supported for text-clip;
607
- // fall back to single fillText call.
608
- ctx.fillText(text.text, x, y);
609
- return;
610
- }
611
- ctx.fillText(letter, offset, y);
612
- offset += ctx.measureText(letter).width + letterSpacing;
613
- }
614
- }
615
- }
616
- exports.CanvasRenderer = CanvasRenderer;
617
- const calculateBackgroundCurvedPaintingArea = (clip, curves) => {
618
- switch (clip) {
619
- case 0 /* BACKGROUND_CLIP.BORDER_BOX */:
620
- return (0, bound_curves_1.calculateBorderBoxPath)(curves);
621
- case 2 /* BACKGROUND_CLIP.CONTENT_BOX */:
622
- return (0, bound_curves_1.calculateContentBoxPath)(curves);
623
- case 1 /* BACKGROUND_CLIP.PADDING_BOX */:
624
- default:
625
- return (0, bound_curves_1.calculatePaddingBoxPath)(curves);
626
- }
627
- };
628
- /**
629
- * Check if any background layer uses background-clip: text
630
- */
631
- const hasTextBackgroundClip = (styles) => {
632
- return styles.backgroundClip.some((clip) => clip === 3 /* BACKGROUND_CLIP.TEXT */);
633
- };