html2canvas-pro 2.1.0 → 2.2.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.
- package/dist/html2canvas-pro.esm.js +10226 -10526
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +10869 -11171
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +8 -8
- package/dist/lib/config.js +0 -22
- package/dist/lib/core/cache-storage.js +3 -40
- package/dist/lib/core/constants.js +25 -0
- package/dist/lib/core/context.js +1 -0
- package/dist/lib/core/features.js +3 -2
- package/dist/lib/core/validator.js +3 -3
- package/dist/lib/css/grouped/background-styles.js +36 -0
- package/dist/lib/css/grouped/border-styles.js +75 -0
- package/dist/lib/css/grouped/font-styles.js +93 -0
- package/dist/lib/css/grouped/layout-styles.js +127 -0
- package/dist/lib/css/index.js +74 -46
- package/dist/lib/css/layout/text.js +7 -6
- package/dist/lib/css/property-descriptors/background-blend-mode.js +41 -0
- package/dist/lib/css/property-descriptors/border-image-repeat.js +42 -0
- package/dist/lib/css/property-descriptors/border-image-slice.js +45 -0
- package/dist/lib/css/property-descriptors/border-image-source.js +21 -0
- package/dist/lib/css/property-descriptors/border-radius.js +1 -1
- package/dist/lib/css/property-descriptors/box-decoration-break.js +18 -0
- package/dist/lib/css/property-descriptors/counter-increment.js +17 -12
- package/dist/lib/css/property-descriptors/counter-reset.js +4 -12
- package/dist/lib/css/property-descriptors/filter.js +76 -0
- package/dist/lib/css/property-descriptors/font-variant-ligatures.js +34 -0
- package/dist/lib/css/property-descriptors/object-fit.js +1 -1
- package/dist/lib/css/property-descriptors/object-position.js +42 -0
- package/dist/lib/css/property-descriptors/visibility.js +1 -1
- package/dist/lib/css/property-descriptors/zoom.js +18 -0
- package/dist/lib/css/syntax/parser.js +0 -1
- package/dist/lib/css/types/color.js +5 -1
- package/dist/lib/css/types/functions/repeating-linear-gradient.js +9 -0
- package/dist/lib/css/types/image.js +12 -2
- package/dist/lib/css/types/length-percentage.js +6 -2
- package/dist/lib/css/types/safe-eval.js +80 -0
- package/dist/lib/dom/document-cloner.js +23 -163
- package/dist/lib/dom/slot-cloner.js +176 -0
- package/dist/lib/index.js +1 -17
- package/dist/lib/render/canvas/background-renderer.js +169 -30
- package/dist/lib/render/canvas/border-image-renderer.js +153 -0
- package/dist/lib/render/canvas/canvas-renderer.js +39 -190
- package/dist/lib/render/canvas/content-renderer.js +202 -0
- package/dist/lib/render/canvas/effects-renderer.js +3 -0
- package/dist/lib/render/canvas/foreignobject-renderer.js +5 -1
- package/dist/lib/render/canvas/text/text-decoration-renderer.js +99 -0
- package/dist/lib/render/canvas/text-renderer.js +100 -224
- package/dist/lib/render/effects.js +38 -3
- package/dist/lib/render/object-fit.js +19 -15
- package/dist/lib/render/stacking-context.js +11 -0
- package/dist/types/config.d.ts +0 -10
- package/dist/types/core/cache-storage.d.ts +0 -24
- package/dist/types/core/constants.d.ts +22 -0
- package/dist/types/core/context.d.ts +3 -0
- package/dist/types/core/performance-monitor.d.ts +4 -4
- package/dist/types/core/validator.d.ts +6 -8
- package/dist/types/css/grouped/background-styles.d.ts +16 -0
- package/dist/types/css/grouped/border-styles.d.ts +31 -0
- package/dist/types/css/grouped/font-styles.d.ts +35 -0
- package/dist/types/css/grouped/layout-styles.d.ts +46 -0
- package/dist/types/css/index.d.ts +30 -0
- package/dist/types/css/property-descriptors/background-blend-mode.d.ts +23 -0
- package/dist/types/css/property-descriptors/border-image-repeat.d.ts +12 -0
- package/dist/types/css/property-descriptors/border-image-slice.d.ts +10 -0
- package/dist/types/css/property-descriptors/border-image-source.d.ts +4 -0
- package/dist/types/css/property-descriptors/box-decoration-break.d.ts +6 -0
- package/dist/types/css/property-descriptors/counter-increment.d.ts +3 -0
- package/dist/types/css/property-descriptors/filter.d.ts +3 -0
- package/dist/types/css/property-descriptors/font-variant-ligatures.d.ts +14 -0
- package/dist/types/css/property-descriptors/object-position.d.ts +4 -0
- package/dist/types/css/property-descriptors/zoom.d.ts +3 -0
- package/dist/types/css/types/functions/repeating-linear-gradient.d.ts +4 -0
- package/dist/types/css/types/image.d.ts +4 -2
- package/dist/types/css/types/safe-eval.d.ts +8 -0
- package/dist/types/dom/document-cloner.d.ts +3 -44
- package/dist/types/dom/slot-cloner.d.ts +66 -0
- package/dist/types/index.d.ts +3 -7
- package/dist/types/options.d.ts +11 -0
- package/dist/types/render/canvas/background-renderer.d.ts +23 -0
- package/dist/types/render/canvas/border-image-renderer.d.ts +18 -0
- package/dist/types/render/canvas/canvas-renderer.d.ts +1 -0
- package/dist/types/render/canvas/content-renderer.d.ts +44 -0
- package/dist/types/render/canvas/text/text-decoration-renderer.d.ts +18 -0
- package/dist/types/render/canvas/text-renderer.d.ts +12 -1
- package/dist/types/render/effects.d.ts +12 -2
- package/dist/types/render/object-fit.d.ts +2 -1
- package/dist/types/render/renderer-interface.d.ts +11 -9
- package/package.json +7 -20
- package/dist/lib/dom/replaced-elements/pseudo-elements.js +0 -0
- package/dist/lib/invariant.js +0 -9
- package/dist/types/dom/replaced-elements/pseudo-elements.d.ts +0 -0
- package/dist/types/invariant.d.ts +0 -1
- package/src/__tests__/index.ts +0 -99
- package/src/config.ts +0 -107
- package/src/core/__mocks__/cache-storage.ts +0 -1
- package/src/core/__mocks__/context.ts +0 -19
- package/src/core/__mocks__/features.ts +0 -8
- package/src/core/__mocks__/logger.ts +0 -17
- package/src/core/__tests__/cache-storage.test.ts +0 -205
- package/src/core/__tests__/cache-storage.ts +0 -278
- package/src/core/__tests__/logger.ts +0 -29
- package/src/core/__tests__/validator.ts +0 -359
- package/src/core/bitwise.ts +0 -1
- package/src/core/cache-storage.ts +0 -315
- package/src/core/context.ts +0 -31
- package/src/core/debugger.ts +0 -32
- package/src/core/features.ts +0 -222
- package/src/core/logger.ts +0 -64
- package/src/core/origin-checker.ts +0 -57
- package/src/core/performance-monitor.ts +0 -241
- package/src/core/render-element.ts +0 -272
- package/src/core/util.ts +0 -1
- package/src/core/validator.ts +0 -593
- package/src/css/index.ts +0 -427
- package/src/css/layout/__mocks__/bounds.ts +0 -6
- package/src/css/layout/bounds.ts +0 -79
- package/src/css/layout/text.ts +0 -161
- package/src/css/property-descriptor.ts +0 -49
- package/src/css/property-descriptors/__tests__/background-tests.ts +0 -65
- package/src/css/property-descriptors/__tests__/clip-path.test.ts +0 -280
- package/src/css/property-descriptors/__tests__/font-family.ts +0 -25
- package/src/css/property-descriptors/__tests__/image-rendering-integration.test.ts +0 -153
- package/src/css/property-descriptors/__tests__/image-rendering-performance.test.ts +0 -175
- package/src/css/property-descriptors/__tests__/image-rendering.test.ts +0 -72
- package/src/css/property-descriptors/__tests__/paint-order.ts +0 -87
- package/src/css/property-descriptors/__tests__/text-shadow.ts +0 -94
- package/src/css/property-descriptors/__tests__/transform-tests.ts +0 -18
- package/src/css/property-descriptors/background-clip.ts +0 -30
- package/src/css/property-descriptors/background-color.ts +0 -9
- package/src/css/property-descriptors/background-image.ts +0 -27
- package/src/css/property-descriptors/background-origin.ts +0 -31
- package/src/css/property-descriptors/background-position.ts +0 -38
- package/src/css/property-descriptors/background-repeat.ts +0 -44
- package/src/css/property-descriptors/background-size.ts +0 -27
- package/src/css/property-descriptors/border-color.ts +0 -13
- package/src/css/property-descriptors/border-radius.ts +0 -19
- package/src/css/property-descriptors/border-style.ts +0 -34
- package/src/css/property-descriptors/border-width.ts +0 -20
- package/src/css/property-descriptors/box-shadow.ts +0 -60
- package/src/css/property-descriptors/clip-path.ts +0 -271
- package/src/css/property-descriptors/color.ts +0 -9
- package/src/css/property-descriptors/content.ts +0 -26
- package/src/css/property-descriptors/counter-increment.ts +0 -43
- package/src/css/property-descriptors/counter-reset.ts +0 -36
- package/src/css/property-descriptors/direction.ts +0 -23
- package/src/css/property-descriptors/display.ts +0 -117
- package/src/css/property-descriptors/duration.ts +0 -14
- package/src/css/property-descriptors/float.ts +0 -29
- package/src/css/property-descriptors/font-family.ts +0 -38
- package/src/css/property-descriptors/font-size.ts +0 -9
- package/src/css/property-descriptors/font-style.ts +0 -25
- package/src/css/property-descriptors/font-variant.ts +0 -12
- package/src/css/property-descriptors/font-weight.ts +0 -26
- package/src/css/property-descriptors/image-rendering.ts +0 -33
- package/src/css/property-descriptors/letter-spacing.ts +0 -25
- package/src/css/property-descriptors/line-break.ts +0 -22
- package/src/css/property-descriptors/line-height.ts +0 -22
- package/src/css/property-descriptors/list-style-image.ts +0 -19
- package/src/css/property-descriptors/list-style-position.ts +0 -22
- package/src/css/property-descriptors/list-style-type.ts +0 -179
- package/src/css/property-descriptors/margin.ts +0 -13
- package/src/css/property-descriptors/mix-blend-mode.ts +0 -35
- package/src/css/property-descriptors/object-fit.ts +0 -39
- package/src/css/property-descriptors/opacity.ts +0 -15
- package/src/css/property-descriptors/overflow-wrap.ts +0 -22
- package/src/css/property-descriptors/overflow.ts +0 -34
- package/src/css/property-descriptors/padding.ts +0 -14
- package/src/css/property-descriptors/paint-order.ts +0 -42
- package/src/css/property-descriptors/position.ts +0 -30
- package/src/css/property-descriptors/quotes.ts +0 -57
- package/src/css/property-descriptors/rotate.ts +0 -34
- package/src/css/property-descriptors/text-align.ts +0 -26
- package/src/css/property-descriptors/text-decoration-color.ts +0 -9
- package/src/css/property-descriptors/text-decoration-line.ts +0 -38
- package/src/css/property-descriptors/text-decoration-style.ts +0 -32
- package/src/css/property-descriptors/text-decoration-thickness.ts +0 -30
- package/src/css/property-descriptors/text-overflow.ts +0 -23
- package/src/css/property-descriptors/text-shadow.ts +0 -52
- package/src/css/property-descriptors/text-transform.ts +0 -27
- package/src/css/property-descriptors/text-underline-offset.ts +0 -27
- package/src/css/property-descriptors/transform-origin.ts +0 -29
- package/src/css/property-descriptors/transform.ts +0 -74
- package/src/css/property-descriptors/visibility.ts +0 -25
- package/src/css/property-descriptors/webkit-line-clamp.ts +0 -30
- package/src/css/property-descriptors/webkit-text-stroke-color.ts +0 -8
- package/src/css/property-descriptors/webkit-text-stroke-width.ts +0 -15
- package/src/css/property-descriptors/word-break.ts +0 -25
- package/src/css/property-descriptors/writing-mode.ts +0 -37
- package/src/css/property-descriptors/z-index.ts +0 -27
- package/src/css/syntax/__tests__/tokernizer-tests.ts +0 -29
- package/src/css/syntax/parser.ts +0 -188
- package/src/css/syntax/tokenizer.ts +0 -822
- package/src/css/type-descriptor.ts +0 -7
- package/src/css/types/__tests__/color-tests.ts +0 -147
- package/src/css/types/__tests__/image-tests.ts +0 -239
- package/src/css/types/angle.ts +0 -86
- package/src/css/types/color-math.ts +0 -22
- package/src/css/types/color-spaces/a98.ts +0 -86
- package/src/css/types/color-spaces/p3.ts +0 -92
- package/src/css/types/color-spaces/pro-photo.ts +0 -87
- package/src/css/types/color-spaces/rec2020.ts +0 -90
- package/src/css/types/color-spaces/srgb.ts +0 -87
- package/src/css/types/color-utilities.ts +0 -452
- package/src/css/types/color.ts +0 -485
- package/src/css/types/functions/-prefix-linear-gradient.ts +0 -35
- package/src/css/types/functions/-prefix-radial-gradient.ts +0 -106
- package/src/css/types/functions/-webkit-gradient.ts +0 -69
- package/src/css/types/functions/__tests__/radial-gradient.ts +0 -69
- package/src/css/types/functions/counter.ts +0 -511
- package/src/css/types/functions/gradient.ts +0 -206
- package/src/css/types/functions/linear-gradient.ts +0 -28
- package/src/css/types/functions/radial-gradient.ts +0 -101
- package/src/css/types/image.ts +0 -120
- package/src/css/types/index.ts +0 -1
- package/src/css/types/length-percentage.ts +0 -137
- package/src/css/types/length.ts +0 -7
- package/src/css/types/time.ts +0 -20
- package/src/dom/__mocks__/document-cloner.ts +0 -22
- package/src/dom/__tests__/dom-normalizer.test.ts +0 -133
- package/src/dom/__tests__/element-container.test.ts +0 -129
- package/src/dom/document-cloner.ts +0 -929
- package/src/dom/dom-normalizer.ts +0 -133
- package/src/dom/element-container.ts +0 -75
- package/src/dom/elements/li-element-container.ts +0 -10
- package/src/dom/elements/ol-element-container.ts +0 -12
- package/src/dom/elements/select-element-container.ts +0 -10
- package/src/dom/elements/textarea-element-container.ts +0 -9
- package/src/dom/node-parser.ts +0 -177
- package/src/dom/node-type-guards.ts +0 -70
- package/src/dom/replaced-elements/canvas-element-container.ts +0 -15
- package/src/dom/replaced-elements/iframe-element-container.ts +0 -55
- package/src/dom/replaced-elements/image-element-container.ts +0 -16
- package/src/dom/replaced-elements/index.ts +0 -5
- package/src/dom/replaced-elements/input-element-container.ts +0 -105
- package/src/dom/replaced-elements/pseudo-elements.ts +0 -0
- package/src/dom/replaced-elements/svg-element-container.ts +0 -23
- package/src/dom/text-container.ts +0 -42
- package/src/global.d.ts +0 -19
- package/src/index.ts +0 -82
- package/src/invariant.ts +0 -5
- package/src/options.ts +0 -55
- package/src/render/__tests__/object-fit.test.ts +0 -85
- package/src/render/background.ts +0 -298
- package/src/render/bezier-curve.ts +0 -47
- package/src/render/border.ts +0 -165
- package/src/render/bound-curves.ts +0 -388
- package/src/render/box-sizing.ts +0 -31
- package/src/render/canvas/__tests__/background-renderer.test.ts +0 -72
- package/src/render/canvas/__tests__/border-renderer.test.ts +0 -24
- package/src/render/canvas/__tests__/effects-renderer.test.ts +0 -32
- package/src/render/canvas/__tests__/text-renderer.test.ts +0 -471
- package/src/render/canvas/background-renderer.ts +0 -271
- package/src/render/canvas/border-renderer.ts +0 -224
- package/src/render/canvas/canvas-path.ts +0 -31
- package/src/render/canvas/canvas-renderer.ts +0 -641
- package/src/render/canvas/effects-renderer.ts +0 -130
- package/src/render/canvas/foreignobject-renderer.ts +0 -53
- package/src/render/canvas/text-renderer.ts +0 -700
- package/src/render/effects.ts +0 -75
- package/src/render/font-metrics.ts +0 -72
- package/src/render/object-fit.ts +0 -100
- package/src/render/path.ts +0 -37
- package/src/render/renderer-interface.ts +0 -28
- package/src/render/stacking-context.ts +0 -386
- package/src/render/vector.ts +0 -19
|
@@ -26,10 +26,19 @@ const canvas_path_1 = require("./canvas-path");
|
|
|
26
26
|
*/
|
|
27
27
|
class BackgroundRenderer {
|
|
28
28
|
constructor(deps) {
|
|
29
|
+
/**
|
|
30
|
+
* Instance-level LRU cache for background-image patterns.
|
|
31
|
+
* CanvasPatterns are tied to the rendering context and must not be
|
|
32
|
+
* shared across different render passes. This cache lives for the
|
|
33
|
+
* duration of one html2canvas() call.
|
|
34
|
+
*
|
|
35
|
+
* Also reused for linear-gradient and repeating-linear-gradient
|
|
36
|
+
* pattern canvases to avoid redundant offscreen canvas allocation.
|
|
37
|
+
*/
|
|
38
|
+
this.patternCache = new Map();
|
|
29
39
|
this.ctx = deps.ctx;
|
|
30
40
|
this.context = deps.context;
|
|
31
41
|
this.canvas = deps.canvas;
|
|
32
|
-
// Options stored in deps but not needed as instance property
|
|
33
42
|
}
|
|
34
43
|
/**
|
|
35
44
|
* Render background images for a container
|
|
@@ -39,17 +48,37 @@ class BackgroundRenderer {
|
|
|
39
48
|
*/
|
|
40
49
|
async renderBackgroundImage(container) {
|
|
41
50
|
let index = container.styles.backgroundImage.length - 1;
|
|
51
|
+
const blendModes = container.styles.backgroundBlendMode;
|
|
52
|
+
let layerCount = 0;
|
|
42
53
|
for (const backgroundImage of container.styles.backgroundImage.slice(0).reverse()) {
|
|
54
|
+
// Save context and apply blend mode for non-first layers
|
|
55
|
+
if (layerCount > 0) {
|
|
56
|
+
const blendMode = blendModes[layerCount] ?? blendModes[0] ?? 'normal';
|
|
57
|
+
if (blendMode !== 'normal') {
|
|
58
|
+
this.ctx.save();
|
|
59
|
+
this.ctx.globalCompositeOperation = blendMode;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
43
62
|
if (backgroundImage.type === 0 /* CSSImageType.URL */) {
|
|
44
63
|
await this.renderBackgroundURLImage(container, backgroundImage, index);
|
|
45
64
|
}
|
|
46
65
|
else if ((0, image_1.isLinearGradient)(backgroundImage)) {
|
|
47
66
|
this.renderLinearGradient(container, backgroundImage, index);
|
|
48
67
|
}
|
|
68
|
+
else if ((0, image_1.isRepeatingLinearGradient)(backgroundImage)) {
|
|
69
|
+
this.renderRepeatingLinearGradient(container, backgroundImage, index);
|
|
70
|
+
}
|
|
49
71
|
else if ((0, image_1.isRadialGradient)(backgroundImage)) {
|
|
50
72
|
this.renderRadialGradient(container, backgroundImage, index);
|
|
51
73
|
}
|
|
74
|
+
if (layerCount > 0) {
|
|
75
|
+
const blendMode = blendModes[layerCount] ?? blendModes[0] ?? 'normal';
|
|
76
|
+
if (blendMode !== 'normal') {
|
|
77
|
+
this.ctx.restore();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
52
80
|
index--;
|
|
81
|
+
layerCount++;
|
|
53
82
|
}
|
|
54
83
|
}
|
|
55
84
|
/**
|
|
@@ -63,6 +92,7 @@ class BackgroundRenderer {
|
|
|
63
92
|
}
|
|
64
93
|
catch (e) {
|
|
65
94
|
this.context.logger.error(`Error loading background-image ${url}`);
|
|
95
|
+
this.context.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
66
96
|
}
|
|
67
97
|
if (image) {
|
|
68
98
|
const imageWidth = isNaN(image.width) || image.width === 0 ? 1 : image.width;
|
|
@@ -72,7 +102,71 @@ class BackgroundRenderer {
|
|
|
72
102
|
imageHeight,
|
|
73
103
|
imageWidth / imageHeight
|
|
74
104
|
]);
|
|
75
|
-
|
|
105
|
+
// Cache key: URL + resized dimensions + imageRendering (pattern is dependent on all three)
|
|
106
|
+
const cacheKey = `${url}|${Math.round(width)}x${Math.round(height)}|${container.styles.imageRendering}`;
|
|
107
|
+
let pattern = this.lruGet(this.patternCache, cacheKey);
|
|
108
|
+
if (!pattern) {
|
|
109
|
+
const resized = this.resizeImage(image, width, height, container.styles.imageRendering);
|
|
110
|
+
pattern = this.ctx.createPattern(resized, 'repeat');
|
|
111
|
+
this.lruSet(this.patternCache, cacheKey, pattern);
|
|
112
|
+
}
|
|
113
|
+
this.renderRepeat(path, pattern, x, y);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Render a repeating linear gradient background.
|
|
118
|
+
* Renders one cycle of the gradient to a pattern canvas, then fills
|
|
119
|
+
* the background area using createPattern('repeat').
|
|
120
|
+
*/
|
|
121
|
+
renderRepeatingLinearGradient(container, backgroundImage, index) {
|
|
122
|
+
const [path, x, y, width, height] = (0, background_1.calculateBackgroundRendering)(container, index, [null, null, null]);
|
|
123
|
+
const [lineLength, x0, x1, y0, y1] = (0, gradient_1.calculateGradientDirection)(backgroundImage.angle, width, height);
|
|
124
|
+
// Determine the repeating pattern length from color stops
|
|
125
|
+
const processedStops = (0, gradient_1.processColorStops)(backgroundImage.stops, lineLength || 1);
|
|
126
|
+
const lastStop = processedStops[processedStops.length - 1];
|
|
127
|
+
const firstStop = processedStops[0];
|
|
128
|
+
const patternLength = lastStop.stop - firstStop.stop;
|
|
129
|
+
if (patternLength <= 0) {
|
|
130
|
+
// Fallback: render as normal linear gradient
|
|
131
|
+
this.renderLinearGradient(container, backgroundImage, index);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Scale direction vectors to match the pattern length
|
|
135
|
+
const dirX = x1 - x0;
|
|
136
|
+
const dirY = y1 - y0;
|
|
137
|
+
const totalLength = Math.sqrt(dirX * dirX + dirY * dirY);
|
|
138
|
+
const scale = patternLength / (totalLength || 1);
|
|
139
|
+
const pX0 = x0;
|
|
140
|
+
const pY0 = y0;
|
|
141
|
+
const pX1 = x0 + dirX * scale;
|
|
142
|
+
const pY1 = y0 + dirY * scale;
|
|
143
|
+
const ownerDocument = this.canvas.ownerDocument ?? document;
|
|
144
|
+
// Cache key for this repeating gradient pattern
|
|
145
|
+
const cacheKey = `rlg|${backgroundImage.angle}|${Math.round(patternLength)}|${JSON.stringify(backgroundImage.stops)}`;
|
|
146
|
+
let pattern = this.lruGet(this.patternCache, cacheKey);
|
|
147
|
+
if (!pattern) {
|
|
148
|
+
const canvas = ownerDocument.createElement('canvas');
|
|
149
|
+
// Create a canvas large enough to hold one full repeating unit
|
|
150
|
+
const canvasSize = Math.max(1, Math.ceil(patternLength));
|
|
151
|
+
canvas.width = canvasSize;
|
|
152
|
+
canvas.height = canvasSize;
|
|
153
|
+
const ctx = canvas.getContext('2d');
|
|
154
|
+
if (!ctx) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const gradient = ctx.createLinearGradient(pX0 - x, pY0 - y, pX1 - x, pY1 - y);
|
|
158
|
+
// Normalize stops to [0, 1] range for one repeating unit
|
|
159
|
+
processedStops.forEach((colorStop) => {
|
|
160
|
+
gradient.addColorStop((colorStop.stop - firstStop.stop) / patternLength, (0, color_utilities_1.asString)(colorStop.color));
|
|
161
|
+
});
|
|
162
|
+
ctx.fillStyle = gradient;
|
|
163
|
+
ctx.fillRect(0, 0, canvasSize, canvasSize);
|
|
164
|
+
if (canvasSize > 0) {
|
|
165
|
+
pattern = this.ctx.createPattern(canvas, 'repeat');
|
|
166
|
+
this.lruSet(this.patternCache, cacheKey, pattern);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (pattern) {
|
|
76
170
|
this.renderRepeat(path, pattern, x, y);
|
|
77
171
|
}
|
|
78
172
|
}
|
|
@@ -82,17 +176,28 @@ class BackgroundRenderer {
|
|
|
82
176
|
renderLinearGradient(container, backgroundImage, index) {
|
|
83
177
|
const [path, x, y, width, height] = (0, background_1.calculateBackgroundRendering)(container, index, [null, null, null]);
|
|
84
178
|
const [lineLength, x0, x1, y0, y1] = (0, gradient_1.calculateGradientDirection)(backgroundImage.angle, width, height);
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
179
|
+
// Cache key: angle + dimensions + serialised colour stops
|
|
180
|
+
const cacheKey = `lg|${backgroundImage.angle}|${Math.round(width)}x${Math.round(height)}|${JSON.stringify(backgroundImage.stops)}`;
|
|
181
|
+
let pattern = this.lruGet(this.patternCache, cacheKey);
|
|
182
|
+
if (!pattern) {
|
|
183
|
+
const ownerDocument = this.canvas.ownerDocument ?? document;
|
|
184
|
+
const canvas = ownerDocument.createElement('canvas');
|
|
185
|
+
canvas.width = width;
|
|
186
|
+
canvas.height = height;
|
|
187
|
+
const ctx = canvas.getContext('2d');
|
|
188
|
+
if (!ctx) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const gradient = ctx.createLinearGradient(x0, y0, x1, y1);
|
|
192
|
+
(0, gradient_1.processColorStops)(backgroundImage.stops, lineLength || 1).forEach((colorStop) => gradient.addColorStop(colorStop.stop, (0, color_utilities_1.asString)(colorStop.color)));
|
|
193
|
+
ctx.fillStyle = gradient;
|
|
194
|
+
ctx.fillRect(0, 0, width, height);
|
|
195
|
+
if (width > 0 && height > 0) {
|
|
196
|
+
pattern = this.ctx.createPattern(canvas, 'repeat');
|
|
197
|
+
this.lruSet(this.patternCache, cacheKey, pattern);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (pattern) {
|
|
96
201
|
this.renderRepeat(path, pattern, x, y);
|
|
97
202
|
}
|
|
98
203
|
}
|
|
@@ -112,26 +217,37 @@ class BackgroundRenderer {
|
|
|
112
217
|
ry = Math.max(ry, 0.01);
|
|
113
218
|
}
|
|
114
219
|
if (rx > 0 && ry > 0) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
220
|
+
// Cache key for radial gradient: position + radii + colour stops
|
|
221
|
+
const cacheKey = `rg|${Math.round(x)}x${Math.round(y)}|${Math.round(rx)}x${Math.round(ry)}|${JSON.stringify(backgroundImage.stops)}`;
|
|
222
|
+
let pattern = this.lruGet(this.patternCache, cacheKey);
|
|
223
|
+
if (!pattern) {
|
|
224
|
+
const ownerDocument = this.canvas.ownerDocument ?? document;
|
|
225
|
+
const size = Math.ceil(Math.max(rx, ry) * 2);
|
|
226
|
+
const offscreen = ownerDocument.createElement('canvas');
|
|
227
|
+
offscreen.width = size;
|
|
228
|
+
offscreen.height = size;
|
|
229
|
+
const offCtx = offscreen.getContext('2d');
|
|
230
|
+
if (offCtx) {
|
|
231
|
+
const offRadius = Math.max(rx, ry);
|
|
232
|
+
const gradient = offCtx.createRadialGradient(offRadius, offRadius, 0, offRadius, offRadius, offRadius);
|
|
233
|
+
(0, gradient_1.processColorStops)(backgroundImage.stops, offRadius * 2).forEach((s) => gradient.addColorStop(s.stop, (0, color_utilities_1.asString)(s.color)));
|
|
234
|
+
offCtx.fillStyle = gradient;
|
|
235
|
+
if (rx !== ry)
|
|
236
|
+
offCtx.scale(1, ry / rx);
|
|
237
|
+
offCtx.fillRect(0, 0, offRadius * 2, offRadius * 2);
|
|
238
|
+
pattern = this.ctx.createPattern(offscreen, 'no-repeat');
|
|
239
|
+
this.lruSet(this.patternCache, cacheKey, pattern);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (pattern) {
|
|
243
|
+
this.path(path);
|
|
125
244
|
this.ctx.save();
|
|
126
|
-
this.ctx.
|
|
127
|
-
this.ctx.
|
|
128
|
-
this.ctx.
|
|
129
|
-
this.ctx.fillRect(
|
|
245
|
+
this.ctx.clip();
|
|
246
|
+
this.ctx.translate(left + x - Math.max(rx, ry), top + y - Math.max(rx, ry));
|
|
247
|
+
this.ctx.fillStyle = pattern;
|
|
248
|
+
this.ctx.fillRect(0, 0, Math.max(rx, ry) * 2, Math.max(rx, ry) * 2);
|
|
130
249
|
this.ctx.restore();
|
|
131
250
|
}
|
|
132
|
-
else {
|
|
133
|
-
this.ctx.fill();
|
|
134
|
-
}
|
|
135
251
|
}
|
|
136
252
|
}
|
|
137
253
|
/**
|
|
@@ -168,6 +284,9 @@ class BackgroundRenderer {
|
|
|
168
284
|
canvas.width = Math.max(1, width);
|
|
169
285
|
canvas.height = Math.max(1, height);
|
|
170
286
|
const ctx = canvas.getContext('2d');
|
|
287
|
+
if (!ctx) {
|
|
288
|
+
return image;
|
|
289
|
+
}
|
|
171
290
|
// Apply image smoothing based on CSS image-rendering property
|
|
172
291
|
if (imageRendering === image_rendering_1.IMAGE_RENDERING.PIXELATED || imageRendering === image_rendering_1.IMAGE_RENDERING.CRISP_EDGES) {
|
|
173
292
|
this.context.logger.debug(`Disabling image smoothing for background image due to CSS image-rendering`);
|
|
@@ -196,5 +315,25 @@ class BackgroundRenderer {
|
|
|
196
315
|
path(paths) {
|
|
197
316
|
(0, canvas_path_1.createCanvasPath)(this.ctx, paths);
|
|
198
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* LRU-aware get: returns value and promotes the entry to end of Map.
|
|
320
|
+
*/
|
|
321
|
+
lruGet(cache, key) {
|
|
322
|
+
const value = cache.get(key);
|
|
323
|
+
if (value !== undefined) {
|
|
324
|
+
cache.delete(key);
|
|
325
|
+
cache.set(key, value);
|
|
326
|
+
}
|
|
327
|
+
return value;
|
|
328
|
+
}
|
|
329
|
+
/** LRU-aware set for CanvasPattern caches. Evicts oldest entry on overflow. */
|
|
330
|
+
lruSet(cache, key, value) {
|
|
331
|
+
if (cache.size >= BackgroundRenderer.PATTERN_CACHE_MAX) {
|
|
332
|
+
const oldestKey = cache.keys().next().value;
|
|
333
|
+
cache.delete(oldestKey);
|
|
334
|
+
}
|
|
335
|
+
cache.set(key, value);
|
|
336
|
+
}
|
|
199
337
|
}
|
|
200
338
|
exports.BackgroundRenderer = BackgroundRenderer;
|
|
339
|
+
BackgroundRenderer.PATTERN_CACHE_MAX = 50;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Border Image Renderer
|
|
4
|
+
*
|
|
5
|
+
* Renders CSS border-image using 9-slice scaling.
|
|
6
|
+
* The source image is divided into 9 regions (4 corners, 4 edges, 1 center)
|
|
7
|
+
* based on border-image-slice values, then each region is drawn to the
|
|
8
|
+
* corresponding area of the element's border box.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.BorderImageRenderer = void 0;
|
|
12
|
+
const border_image_repeat_1 = require("../../css/property-descriptors/border-image-repeat");
|
|
13
|
+
class BorderImageRenderer {
|
|
14
|
+
constructor(ctx) {
|
|
15
|
+
this.ctx = ctx;
|
|
16
|
+
}
|
|
17
|
+
renderBorderImage(bounds, image, slice, repeat, borderTopWidth, borderRightWidth, borderBottomWidth, borderLeftWidth) {
|
|
18
|
+
const imgW = image.naturalWidth || image.width;
|
|
19
|
+
const imgH = image.naturalHeight || image.height;
|
|
20
|
+
if (imgW <= 0 || imgH <= 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// Calculate source slice positions in image pixel space
|
|
24
|
+
const sT = Math.min(slice.unit === 'percent' ? (slice.top / 100) * imgH : Math.min(slice.top, imgH), imgH);
|
|
25
|
+
const sR = Math.min(slice.unit === 'percent' ? (slice.right / 100) * imgW : Math.min(slice.right, imgW), imgW);
|
|
26
|
+
const sB = Math.min(slice.unit === 'percent' ? (slice.bottom / 100) * imgH : Math.min(slice.bottom, imgH), imgH);
|
|
27
|
+
const sL = Math.min(slice.unit === 'percent' ? (slice.left / 100) * imgW : Math.min(slice.left, imgW), imgW);
|
|
28
|
+
const { left, top, width, height } = bounds;
|
|
29
|
+
if (width <= 0 || height <= 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Clamp border widths to available box dimensions
|
|
33
|
+
const dT = Math.min(borderTopWidth, height);
|
|
34
|
+
const dR = Math.min(borderRightWidth, width);
|
|
35
|
+
const dB = Math.min(borderBottomWidth, height - dT);
|
|
36
|
+
const dL = Math.min(borderLeftWidth, width - dR);
|
|
37
|
+
// Draw corners
|
|
38
|
+
this.drawRegion(image, 0, 0, sL, sT, left, top, dL, dT);
|
|
39
|
+
this.drawRegion(image, imgW - sR, 0, sR, sT, left + width - dR, top, dR, dT);
|
|
40
|
+
this.drawRegion(image, imgW - sR, imgH - sB, sR, sB, left + width - dR, top + height - dB, dR, dB);
|
|
41
|
+
this.drawRegion(image, 0, imgH - sB, sL, sB, left, top + height - dB, dL, dB);
|
|
42
|
+
// Draw edges
|
|
43
|
+
const edges = [
|
|
44
|
+
{
|
|
45
|
+
sx: sL,
|
|
46
|
+
sy: 0,
|
|
47
|
+
sw: imgW - sL - sR,
|
|
48
|
+
sh: sT,
|
|
49
|
+
dx: left + dL,
|
|
50
|
+
dy: top,
|
|
51
|
+
dw: width - dL - dR,
|
|
52
|
+
dh: dT,
|
|
53
|
+
repeat: repeat.horizontal
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
sx: imgW - sR,
|
|
57
|
+
sy: sT,
|
|
58
|
+
sw: sR,
|
|
59
|
+
sh: imgH - sT - sB,
|
|
60
|
+
dx: left + width - dR,
|
|
61
|
+
dy: top + dT,
|
|
62
|
+
dw: dR,
|
|
63
|
+
dh: height - dT - dB,
|
|
64
|
+
repeat: repeat.vertical
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
sx: sL,
|
|
68
|
+
sy: imgH - sB,
|
|
69
|
+
sw: imgW - sL - sR,
|
|
70
|
+
sh: sB,
|
|
71
|
+
dx: left + dL,
|
|
72
|
+
dy: top + height - dB,
|
|
73
|
+
dw: width - dL - dR,
|
|
74
|
+
dh: dB,
|
|
75
|
+
repeat: repeat.horizontal
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
sx: 0,
|
|
79
|
+
sy: sT,
|
|
80
|
+
sw: sL,
|
|
81
|
+
sh: imgH - sT - sB,
|
|
82
|
+
dx: left,
|
|
83
|
+
dy: top + dT,
|
|
84
|
+
dw: dL,
|
|
85
|
+
dh: height - dT - dB,
|
|
86
|
+
repeat: repeat.vertical
|
|
87
|
+
}
|
|
88
|
+
];
|
|
89
|
+
for (const edge of edges) {
|
|
90
|
+
if (edge.sw <= 0 || edge.sh <= 0 || edge.dw <= 0 || edge.dh <= 0)
|
|
91
|
+
continue;
|
|
92
|
+
const isHorizontal = edge.dw >= edge.dh;
|
|
93
|
+
if (edge.repeat === border_image_repeat_1.BORDER_IMAGE_REPEAT.STRETCH) {
|
|
94
|
+
this.ctx.drawImage(image, edge.sx, edge.sy, edge.sw, edge.sh, edge.dx, edge.dy, edge.dw, edge.dh);
|
|
95
|
+
}
|
|
96
|
+
else if (edge.repeat === border_image_repeat_1.BORDER_IMAGE_REPEAT.REPEAT || edge.repeat === border_image_repeat_1.BORDER_IMAGE_REPEAT.ROUND) {
|
|
97
|
+
this.drawRepeatedEdge(image, edge, isHorizontal, edge.repeat === border_image_repeat_1.BORDER_IMAGE_REPEAT.ROUND);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Draw center if fill is specified
|
|
101
|
+
if (slice.fill) {
|
|
102
|
+
const cx = sL;
|
|
103
|
+
const cy = sT;
|
|
104
|
+
const cw = imgW - sL - sR;
|
|
105
|
+
const ch = imgH - sT - sB;
|
|
106
|
+
const tcx = left + dL;
|
|
107
|
+
const tcy = top + dT;
|
|
108
|
+
const tcw = width - dL - dR;
|
|
109
|
+
const tch = height - dT - dB;
|
|
110
|
+
this.drawRegion(image, cx, cy, cw, ch, tcx, tcy, tcw, tch);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
drawRegion(image, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
114
|
+
if (sw > 0 && sh > 0 && dw > 0 && dh > 0) {
|
|
115
|
+
this.ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
drawRepeatedEdge(image, edge, isHorizontal, round) {
|
|
119
|
+
const srcLength = isHorizontal ? edge.sw : edge.sh;
|
|
120
|
+
const tgLength = isHorizontal ? edge.dw : edge.dh;
|
|
121
|
+
if (srcLength <= 0 || tgLength <= 0)
|
|
122
|
+
return;
|
|
123
|
+
let tileSize;
|
|
124
|
+
let tileCount;
|
|
125
|
+
if (round) {
|
|
126
|
+
tileCount = Math.max(1, Math.round(tgLength / srcLength));
|
|
127
|
+
tileSize = tgLength / tileCount;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
tileSize = srcLength;
|
|
131
|
+
tileCount = Math.ceil(tgLength / tileSize);
|
|
132
|
+
}
|
|
133
|
+
this.ctx.save();
|
|
134
|
+
this.ctx.beginPath();
|
|
135
|
+
this.ctx.rect(edge.dx, edge.dy, edge.dw, edge.dh);
|
|
136
|
+
this.ctx.clip();
|
|
137
|
+
for (let i = 0; i < tileCount; i++) {
|
|
138
|
+
const offset = i * tileSize;
|
|
139
|
+
const remaining = tgLength - offset;
|
|
140
|
+
const clampedSize = Math.min(tileSize, remaining);
|
|
141
|
+
if (clampedSize <= 0)
|
|
142
|
+
break;
|
|
143
|
+
if (isHorizontal) {
|
|
144
|
+
this.ctx.drawImage(image, edge.sx, edge.sy, edge.sw, edge.sh, edge.dx + offset, edge.dy, clampedSize, edge.dh);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.ctx.drawImage(image, edge.sx, edge.sy, edge.sw, edge.sh, edge.dx, edge.dy + offset, edge.dw, clampedSize);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
this.ctx.restore();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.BorderImageRenderer = BorderImageRenderer;
|