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
|
@@ -5,36 +5,28 @@ const stacking_context_1 = require("../stacking-context");
|
|
|
5
5
|
const color_utilities_1 = require("../../css/types/color-utilities");
|
|
6
6
|
const path_1 = require("../path");
|
|
7
7
|
const bound_curves_1 = require("../bound-curves");
|
|
8
|
-
const vector_1 = require("../vector");
|
|
9
8
|
const background_1 = require("../background");
|
|
10
|
-
const text_1 = require("../../css/layout/text");
|
|
11
|
-
const image_element_container_1 = require("../../dom/replaced-elements/image-element-container");
|
|
12
9
|
const box_sizing_1 = require("../box-sizing");
|
|
13
|
-
const canvas_element_container_1 = require("../../dom/replaced-elements/canvas-element-container");
|
|
14
|
-
const svg_element_container_1 = require("../../dom/replaced-elements/svg-element-container");
|
|
15
|
-
const bitwise_1 = require("../../core/bitwise");
|
|
16
|
-
const length_percentage_1 = require("../../css/types/length-percentage");
|
|
17
10
|
const font_metrics_1 = require("../font-metrics");
|
|
18
|
-
const
|
|
19
|
-
const image_rendering_1 = require("../../css/property-descriptors/image-rendering");
|
|
20
|
-
const line_height_1 = require("../../css/property-descriptors/line-height");
|
|
21
|
-
const input_element_container_1 = require("../../dom/replaced-elements/input-element-container");
|
|
22
|
-
const textarea_element_container_1 = require("../../dom/elements/textarea-element-container");
|
|
23
|
-
const select_element_container_1 = require("../../dom/elements/select-element-container");
|
|
24
|
-
const iframe_element_container_1 = require("../../dom/replaced-elements/iframe-element-container");
|
|
11
|
+
const text_renderer_1 = require("./text-renderer");
|
|
25
12
|
const background_renderer_1 = require("./background-renderer");
|
|
26
13
|
const border_renderer_1 = require("./border-renderer");
|
|
14
|
+
const border_image_renderer_1 = require("./border-image-renderer");
|
|
27
15
|
const effects_renderer_1 = require("./effects-renderer");
|
|
28
|
-
const text_renderer_1 = require("./text-renderer");
|
|
29
16
|
const canvas_path_1 = require("./canvas-path");
|
|
30
17
|
const object_fit_1 = require("../object-fit");
|
|
18
|
+
const content_renderer_1 = require("./content-renderer");
|
|
31
19
|
const MASK_OFFSET = 10000;
|
|
32
20
|
class CanvasRenderer {
|
|
33
21
|
constructor(context, options) {
|
|
34
22
|
this.context = context;
|
|
35
23
|
this.options = options;
|
|
36
24
|
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
|
37
|
-
|
|
25
|
+
const ctx = this.canvas.getContext('2d');
|
|
26
|
+
if (!ctx) {
|
|
27
|
+
throw new Error('Failed to get 2D rendering context from canvas');
|
|
28
|
+
}
|
|
29
|
+
this.ctx = ctx;
|
|
38
30
|
if (!options.canvas) {
|
|
39
31
|
this.canvas.width = Math.floor(options.width * options.scale);
|
|
40
32
|
this.canvas.height = Math.floor(options.height * options.scale);
|
|
@@ -67,6 +59,7 @@ class CanvasRenderer {
|
|
|
67
59
|
path: (paths) => this.path(paths),
|
|
68
60
|
formatPath: (paths) => this.formatPath(paths)
|
|
69
61
|
});
|
|
62
|
+
this.borderImageRenderer = new border_image_renderer_1.BorderImageRenderer(this.ctx);
|
|
70
63
|
this.effectsRenderer = new effects_renderer_1.EffectsRenderer({ ctx: this.ctx }, { path: (paths) => this.path(paths) });
|
|
71
64
|
this.textRenderer = new text_renderer_1.TextRenderer({
|
|
72
65
|
ctx: this.ctx,
|
|
@@ -104,7 +97,7 @@ class CanvasRenderer {
|
|
|
104
97
|
this.path(path);
|
|
105
98
|
this.ctx.save();
|
|
106
99
|
this.ctx.clip();
|
|
107
|
-
const { sx, sy, sw, sh, dx, dy, dw, dh } = (0, object_fit_1.calculateObjectFitRendering)(intrinsicWidth, intrinsicHeight, box, container.styles.objectFit);
|
|
100
|
+
const { sx, sy, sw, sh, dx, dy, dw, dh } = (0, object_fit_1.calculateObjectFitRendering)(intrinsicWidth, intrinsicHeight, box, container.styles.objectFit, container.styles.objectPosition);
|
|
108
101
|
this.ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
109
102
|
this.ctx.restore();
|
|
110
103
|
}
|
|
@@ -115,160 +108,20 @@ class CanvasRenderer {
|
|
|
115
108
|
const curves = paint.curves;
|
|
116
109
|
const styles = container.styles;
|
|
117
110
|
// Use content box for text overflow calculation (excludes padding and border)
|
|
118
|
-
// This matches browser behavior where text-overflow uses the content width
|
|
119
111
|
const textBounds = (0, box_sizing_1.contentBox)(container);
|
|
120
112
|
for (const child of container.textNodes) {
|
|
121
113
|
await this.textRenderer.renderTextNode(child, styles, textBounds);
|
|
122
114
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
else if (styles.imageRendering === image_rendering_1.IMAGE_RENDERING.SMOOTH) {
|
|
135
|
-
this.context.logger.debug(`Enabling image smoothing for ${container.src} due to CSS image-rendering: smooth`);
|
|
136
|
-
this.ctx.imageSmoothingEnabled = true;
|
|
137
|
-
}
|
|
138
|
-
// IMAGE_RENDERING.AUTO: keep current global setting
|
|
139
|
-
this.renderReplacedElement(container, curves, image);
|
|
140
|
-
// Restore previous smoothing state
|
|
141
|
-
this.ctx.imageSmoothingEnabled = prevSmoothing;
|
|
142
|
-
}
|
|
143
|
-
catch (e) {
|
|
144
|
-
this.context.logger.error(`Error loading image ${container.src}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (container instanceof canvas_element_container_1.CanvasElementContainer) {
|
|
148
|
-
this.renderReplacedElement(container, curves, container.canvas);
|
|
149
|
-
}
|
|
150
|
-
if (container instanceof svg_element_container_1.SVGElementContainer) {
|
|
151
|
-
try {
|
|
152
|
-
const image = await this.context.cache.match(container.svg);
|
|
153
|
-
this.renderReplacedElement(container, curves, image);
|
|
154
|
-
}
|
|
155
|
-
catch (e) {
|
|
156
|
-
this.context.logger.error(`Error loading svg ${container.svg.substring(0, 255)}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (container instanceof iframe_element_container_1.IFrameElementContainer && container.tree) {
|
|
160
|
-
const iframeRenderer = new CanvasRenderer(this.context, {
|
|
161
|
-
scale: this.options.scale,
|
|
162
|
-
backgroundColor: container.backgroundColor,
|
|
163
|
-
x: 0,
|
|
164
|
-
y: 0,
|
|
165
|
-
width: container.width,
|
|
166
|
-
height: container.height
|
|
167
|
-
});
|
|
168
|
-
const canvas = await iframeRenderer.render(container.tree);
|
|
169
|
-
if (container.width && container.height) {
|
|
170
|
-
this.ctx.drawImage(canvas, 0, 0, container.width, container.height, container.bounds.left, container.bounds.top, container.bounds.width, container.bounds.height);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
if (container instanceof input_element_container_1.InputElementContainer) {
|
|
174
|
-
const size = Math.min(container.bounds.width, container.bounds.height);
|
|
175
|
-
if (container.type === input_element_container_1.CHECKBOX) {
|
|
176
|
-
if (container.checked) {
|
|
177
|
-
this.ctx.save();
|
|
178
|
-
this.path([
|
|
179
|
-
new vector_1.Vector(container.bounds.left + size * 0.39363, container.bounds.top + size * 0.79),
|
|
180
|
-
new vector_1.Vector(container.bounds.left + size * 0.16, container.bounds.top + size * 0.5549),
|
|
181
|
-
new vector_1.Vector(container.bounds.left + size * 0.27347, container.bounds.top + size * 0.44071),
|
|
182
|
-
new vector_1.Vector(container.bounds.left + size * 0.39694, container.bounds.top + size * 0.5649),
|
|
183
|
-
new vector_1.Vector(container.bounds.left + size * 0.72983, container.bounds.top + size * 0.23),
|
|
184
|
-
new vector_1.Vector(container.bounds.left + size * 0.84, container.bounds.top + size * 0.34085),
|
|
185
|
-
new vector_1.Vector(container.bounds.left + size * 0.39363, container.bounds.top + size * 0.79)
|
|
186
|
-
]);
|
|
187
|
-
this.ctx.fillStyle = (0, color_utilities_1.asString)(input_element_container_1.INPUT_COLOR);
|
|
188
|
-
this.ctx.fill();
|
|
189
|
-
this.ctx.restore();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
else if (container.type === input_element_container_1.RADIO) {
|
|
193
|
-
if (container.checked) {
|
|
194
|
-
this.ctx.save();
|
|
195
|
-
this.ctx.beginPath();
|
|
196
|
-
this.ctx.arc(container.bounds.left + size / 2, container.bounds.top + size / 2, size / 4, 0, Math.PI * 2, true);
|
|
197
|
-
this.ctx.fillStyle = (0, color_utilities_1.asString)(input_element_container_1.INPUT_COLOR);
|
|
198
|
-
this.ctx.fill();
|
|
199
|
-
this.ctx.restore();
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
if (isTextInputElement(container) && container.value.length) {
|
|
204
|
-
const [font, fontFamily, fontSize] = this.textRenderer.createFontStyle(styles);
|
|
205
|
-
const { baseline } = this.fontMetrics.getMetrics(fontFamily, fontSize);
|
|
206
|
-
this.ctx.font = font;
|
|
207
|
-
// Fix for Issue #92: Use placeholder color when rendering placeholder text
|
|
208
|
-
const isPlaceholder = container instanceof input_element_container_1.InputElementContainer && container.isPlaceholder;
|
|
209
|
-
this.ctx.fillStyle = isPlaceholder ? (0, color_utilities_1.asString)(input_element_container_1.PLACEHOLDER_COLOR) : (0, color_utilities_1.asString)(styles.color);
|
|
210
|
-
this.ctx.textBaseline = 'alphabetic';
|
|
211
|
-
this.ctx.textAlign = canvasTextAlign(container.styles.textAlign);
|
|
212
|
-
const bounds = (0, box_sizing_1.contentBox)(container);
|
|
213
|
-
let x = 0;
|
|
214
|
-
switch (container.styles.textAlign) {
|
|
215
|
-
case 1 /* TEXT_ALIGN.CENTER */:
|
|
216
|
-
x += bounds.width / 2;
|
|
217
|
-
break;
|
|
218
|
-
case 2 /* TEXT_ALIGN.RIGHT */:
|
|
219
|
-
x += bounds.width;
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
// Fix for Issue #92: Position text vertically centered in single-line input
|
|
223
|
-
// Only apply vertical centering for InputElementContainer, not for textarea or select
|
|
224
|
-
let verticalOffset = 0;
|
|
225
|
-
if (container instanceof input_element_container_1.InputElementContainer) {
|
|
226
|
-
const fontSizeValue = (0, length_percentage_1.getAbsoluteValue)(styles.fontSize, 0);
|
|
227
|
-
verticalOffset = (bounds.height - fontSizeValue) / 2;
|
|
228
|
-
}
|
|
229
|
-
// Create text bounds with horizontal and vertical offsets
|
|
230
|
-
// Height is not modified as it doesn't affect text rendering position
|
|
231
|
-
const textBounds = bounds.add(x, verticalOffset, 0, 0);
|
|
232
|
-
this.ctx.save();
|
|
233
|
-
this.path([
|
|
234
|
-
new vector_1.Vector(bounds.left, bounds.top),
|
|
235
|
-
new vector_1.Vector(bounds.left + bounds.width, bounds.top),
|
|
236
|
-
new vector_1.Vector(bounds.left + bounds.width, bounds.top + bounds.height),
|
|
237
|
-
new vector_1.Vector(bounds.left, bounds.top + bounds.height)
|
|
238
|
-
]);
|
|
239
|
-
this.ctx.clip();
|
|
240
|
-
this.textRenderer.renderTextWithLetterSpacing(new text_1.TextBounds(container.value, textBounds), styles.letterSpacing, baseline, styles.writingMode);
|
|
241
|
-
this.ctx.restore();
|
|
242
|
-
this.ctx.textBaseline = 'alphabetic';
|
|
243
|
-
this.ctx.textAlign = 'left';
|
|
244
|
-
}
|
|
245
|
-
if ((0, bitwise_1.contains)(container.styles.display, 2048 /* DISPLAY.LIST_ITEM */)) {
|
|
246
|
-
if (container.styles.listStyleImage !== null) {
|
|
247
|
-
const img = container.styles.listStyleImage;
|
|
248
|
-
if (img.type === 0 /* CSSImageType.URL */) {
|
|
249
|
-
let image;
|
|
250
|
-
const url = img.url;
|
|
251
|
-
try {
|
|
252
|
-
image = await this.context.cache.match(url);
|
|
253
|
-
this.ctx.drawImage(image, container.bounds.left - (image.width + 10), container.bounds.top);
|
|
254
|
-
}
|
|
255
|
-
catch (e) {
|
|
256
|
-
this.context.logger.error(`Error loading list-style-image ${url}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
else if (paint.listValue && container.styles.listStyleType !== -1 /* LIST_STYLE_TYPE.NONE */) {
|
|
261
|
-
const [font] = this.textRenderer.createFontStyle(styles);
|
|
262
|
-
this.ctx.font = font;
|
|
263
|
-
this.ctx.fillStyle = (0, color_utilities_1.asString)(styles.color);
|
|
264
|
-
this.ctx.textBaseline = 'middle';
|
|
265
|
-
this.ctx.textAlign = 'right';
|
|
266
|
-
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);
|
|
267
|
-
this.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);
|
|
268
|
-
this.ctx.textBaseline = 'bottom';
|
|
269
|
-
this.ctx.textAlign = 'left';
|
|
270
|
-
}
|
|
271
|
-
}
|
|
115
|
+
await (0, content_renderer_1.renderReplacedElements)(this.ctx, this.context, {
|
|
116
|
+
scale: this.options.scale,
|
|
117
|
+
backgroundColor: this.options.backgroundColor,
|
|
118
|
+
x: this.options.x,
|
|
119
|
+
y: this.options.y,
|
|
120
|
+
width: this.options.width,
|
|
121
|
+
height: this.options.height
|
|
122
|
+
}, (ctx, opts) => new CanvasRenderer(ctx, opts), container, curves, styles, (c, cv, img) => this.renderReplacedElement(c, cv, img));
|
|
123
|
+
(0, content_renderer_1.renderFormElements)(this.ctx, this.fontMetrics, this.textRenderer, this.path.bind(this), container, styles);
|
|
124
|
+
await (0, content_renderer_1.renderListMarker)(this.ctx, this.context, this.textRenderer, paint, container, styles);
|
|
272
125
|
}
|
|
273
126
|
async renderStackContent(stack) {
|
|
274
127
|
if (stack.element.container.debugRender) {
|
|
@@ -399,6 +252,25 @@ class CanvasRenderer {
|
|
|
399
252
|
this.ctx.restore();
|
|
400
253
|
});
|
|
401
254
|
}
|
|
255
|
+
// Render border-image if present (replaces traditional borders per CSS spec)
|
|
256
|
+
if (styles.borderImageSource) {
|
|
257
|
+
const source = styles.borderImageSource;
|
|
258
|
+
if (source.type === 0 /* CSSImageType.URL */) {
|
|
259
|
+
const url = source.url;
|
|
260
|
+
try {
|
|
261
|
+
const image = await this.context.cache.match(url);
|
|
262
|
+
if (image) {
|
|
263
|
+
const bounds = paint.container.bounds;
|
|
264
|
+
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));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
this.context.logger.error(`Error loading border-image ${url}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// When border-image is present, skip regular border rendering
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
402
274
|
let side = 0;
|
|
403
275
|
for (const border of borders) {
|
|
404
276
|
if (border.style !== 0 /* BORDER_STYLE.NONE */ && !(0, color_utilities_1.isTransparent)(border.color) && border.width > 0) {
|
|
@@ -430,18 +302,6 @@ class CanvasRenderer {
|
|
|
430
302
|
}
|
|
431
303
|
}
|
|
432
304
|
exports.CanvasRenderer = CanvasRenderer;
|
|
433
|
-
const isTextInputElement = (container) => {
|
|
434
|
-
if (container instanceof textarea_element_container_1.TextareaElementContainer) {
|
|
435
|
-
return true;
|
|
436
|
-
}
|
|
437
|
-
else if (container instanceof select_element_container_1.SelectElementContainer) {
|
|
438
|
-
return true;
|
|
439
|
-
}
|
|
440
|
-
else if (container instanceof input_element_container_1.InputElementContainer && container.type !== input_element_container_1.RADIO && container.type !== input_element_container_1.CHECKBOX) {
|
|
441
|
-
return true;
|
|
442
|
-
}
|
|
443
|
-
return false;
|
|
444
|
-
};
|
|
445
305
|
const calculateBackgroundCurvedPaintingArea = (clip, curves) => {
|
|
446
306
|
switch (clip) {
|
|
447
307
|
case 0 /* BACKGROUND_CLIP.BORDER_BOX */:
|
|
@@ -453,14 +313,3 @@ const calculateBackgroundCurvedPaintingArea = (clip, curves) => {
|
|
|
453
313
|
return (0, bound_curves_1.calculatePaddingBoxPath)(curves);
|
|
454
314
|
}
|
|
455
315
|
};
|
|
456
|
-
const canvasTextAlign = (textAlign) => {
|
|
457
|
-
switch (textAlign) {
|
|
458
|
-
case 1 /* TEXT_ALIGN.CENTER */:
|
|
459
|
-
return 'center';
|
|
460
|
-
case 2 /* TEXT_ALIGN.RIGHT */:
|
|
461
|
-
return 'right';
|
|
462
|
-
case 0 /* TEXT_ALIGN.LEFT */:
|
|
463
|
-
default:
|
|
464
|
-
return 'left';
|
|
465
|
-
}
|
|
466
|
-
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderReplacedElements = renderReplacedElements;
|
|
4
|
+
exports.renderFormElements = renderFormElements;
|
|
5
|
+
exports.renderListMarker = renderListMarker;
|
|
6
|
+
const color_utilities_1 = require("../../css/types/color-utilities");
|
|
7
|
+
const image_element_container_1 = require("../../dom/replaced-elements/image-element-container");
|
|
8
|
+
const canvas_element_container_1 = require("../../dom/replaced-elements/canvas-element-container");
|
|
9
|
+
const svg_element_container_1 = require("../../dom/replaced-elements/svg-element-container");
|
|
10
|
+
const iframe_element_container_1 = require("../../dom/replaced-elements/iframe-element-container");
|
|
11
|
+
const input_element_container_1 = require("../../dom/replaced-elements/input-element-container");
|
|
12
|
+
const textarea_element_container_1 = require("../../dom/elements/textarea-element-container");
|
|
13
|
+
const select_element_container_1 = require("../../dom/elements/select-element-container");
|
|
14
|
+
const bounds_1 = require("../../css/layout/bounds");
|
|
15
|
+
const text_1 = require("../../css/layout/text");
|
|
16
|
+
const vector_1 = require("../vector");
|
|
17
|
+
const box_sizing_1 = require("../box-sizing");
|
|
18
|
+
const image_rendering_1 = require("../../css/property-descriptors/image-rendering");
|
|
19
|
+
const length_percentage_1 = require("../../css/types/length-percentage");
|
|
20
|
+
const line_height_1 = require("../../css/property-descriptors/line-height");
|
|
21
|
+
const bitwise_1 = require("../../core/bitwise");
|
|
22
|
+
/**
|
|
23
|
+
* Render replaced elements: Image, Canvas, SVG, IFrame.
|
|
24
|
+
*/
|
|
25
|
+
async function renderReplacedElements(ctx, context, options, iframeRendererFactory, container, curves, styles, renderReplacedElementFn) {
|
|
26
|
+
if (container instanceof image_element_container_1.ImageElementContainer) {
|
|
27
|
+
try {
|
|
28
|
+
const image = await context.cache.match(container.src);
|
|
29
|
+
const prevSmoothing = ctx.imageSmoothingEnabled;
|
|
30
|
+
if (styles.imageRendering === image_rendering_1.IMAGE_RENDERING.PIXELATED ||
|
|
31
|
+
styles.imageRendering === image_rendering_1.IMAGE_RENDERING.CRISP_EDGES) {
|
|
32
|
+
ctx.imageSmoothingEnabled = false;
|
|
33
|
+
}
|
|
34
|
+
else if (styles.imageRendering === image_rendering_1.IMAGE_RENDERING.SMOOTH) {
|
|
35
|
+
ctx.imageSmoothingEnabled = true;
|
|
36
|
+
}
|
|
37
|
+
renderReplacedElementFn(container, curves, image);
|
|
38
|
+
ctx.imageSmoothingEnabled = prevSmoothing;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
context.logger.error(`Error loading image ${container.src}`);
|
|
42
|
+
context.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (container instanceof canvas_element_container_1.CanvasElementContainer) {
|
|
46
|
+
renderReplacedElementFn(container, curves, container.canvas);
|
|
47
|
+
}
|
|
48
|
+
if (container instanceof svg_element_container_1.SVGElementContainer) {
|
|
49
|
+
try {
|
|
50
|
+
const image = await context.cache.match(container.svg);
|
|
51
|
+
renderReplacedElementFn(container, curves, image);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
context.logger.error(`Error loading svg ${container.svg.substring(0, 255)}`);
|
|
55
|
+
context.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (container instanceof iframe_element_container_1.IFrameElementContainer && container.tree) {
|
|
59
|
+
const iframeRenderer = iframeRendererFactory(context, {
|
|
60
|
+
scale: options.scale,
|
|
61
|
+
backgroundColor: container.backgroundColor,
|
|
62
|
+
x: 0,
|
|
63
|
+
y: 0,
|
|
64
|
+
width: container.width,
|
|
65
|
+
height: container.height
|
|
66
|
+
});
|
|
67
|
+
const canvas = await iframeRenderer.render(container.tree);
|
|
68
|
+
if (container.width && container.height) {
|
|
69
|
+
ctx.drawImage(canvas, 0, 0, container.width, container.height, container.bounds.left, container.bounds.top, container.bounds.width, container.bounds.height);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Render form element content: checkbox, radio, text input.
|
|
75
|
+
*/
|
|
76
|
+
function renderFormElements(ctx, fontMetrics, textRenderer, pathFn, container, styles) {
|
|
77
|
+
if (container instanceof input_element_container_1.InputElementContainer) {
|
|
78
|
+
const size = Math.min(container.bounds.width, container.bounds.height);
|
|
79
|
+
if (container.type === input_element_container_1.CHECKBOX && container.checked) {
|
|
80
|
+
ctx.save();
|
|
81
|
+
pathFn([
|
|
82
|
+
new vector_1.Vector(container.bounds.left + size * 0.39363, container.bounds.top + size * 0.79),
|
|
83
|
+
new vector_1.Vector(container.bounds.left + size * 0.16, container.bounds.top + size * 0.5549),
|
|
84
|
+
new vector_1.Vector(container.bounds.left + size * 0.27347, container.bounds.top + size * 0.44071),
|
|
85
|
+
new vector_1.Vector(container.bounds.left + size * 0.39694, container.bounds.top + size * 0.5649),
|
|
86
|
+
new vector_1.Vector(container.bounds.left + size * 0.72983, container.bounds.top + size * 0.23),
|
|
87
|
+
new vector_1.Vector(container.bounds.left + size * 0.84, container.bounds.top + size * 0.34085),
|
|
88
|
+
new vector_1.Vector(container.bounds.left + size * 0.39363, container.bounds.top + size * 0.79)
|
|
89
|
+
]);
|
|
90
|
+
ctx.fillStyle = (0, color_utilities_1.asString)(input_element_container_1.INPUT_COLOR);
|
|
91
|
+
ctx.fill();
|
|
92
|
+
ctx.restore();
|
|
93
|
+
}
|
|
94
|
+
else if (container.type === input_element_container_1.RADIO && container.checked) {
|
|
95
|
+
ctx.save();
|
|
96
|
+
ctx.beginPath();
|
|
97
|
+
ctx.arc(container.bounds.left + size / 2, container.bounds.top + size / 2, size / 4, 0, Math.PI * 2, true);
|
|
98
|
+
ctx.fillStyle = (0, color_utilities_1.asString)(input_element_container_1.INPUT_COLOR);
|
|
99
|
+
ctx.fill();
|
|
100
|
+
ctx.restore();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (isTextInputElement(container) && container.value.length) {
|
|
104
|
+
const [font, fontFamily, fontSize] = textRenderer.createFontStyle(styles);
|
|
105
|
+
const { baseline } = fontMetrics.getMetrics(fontFamily, fontSize);
|
|
106
|
+
ctx.font = font;
|
|
107
|
+
const isPlaceholder = container instanceof input_element_container_1.InputElementContainer && container.isPlaceholder;
|
|
108
|
+
ctx.fillStyle = isPlaceholder ? (0, color_utilities_1.asString)(input_element_container_1.PLACEHOLDER_COLOR) : (0, color_utilities_1.asString)(styles.color);
|
|
109
|
+
ctx.textBaseline = 'alphabetic';
|
|
110
|
+
ctx.textAlign = canvasTextAlign(container.styles.textAlign);
|
|
111
|
+
const bounds = (0, box_sizing_1.contentBox)(container);
|
|
112
|
+
let x = 0;
|
|
113
|
+
switch (container.styles.textAlign) {
|
|
114
|
+
case 1 /* TEXT_ALIGN.CENTER */:
|
|
115
|
+
x += bounds.width / 2;
|
|
116
|
+
break;
|
|
117
|
+
case 2 /* TEXT_ALIGN.RIGHT */:
|
|
118
|
+
x += bounds.width;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
let verticalOffset = 0;
|
|
122
|
+
if (container instanceof input_element_container_1.InputElementContainer) {
|
|
123
|
+
const fontSizeValue = (0, length_percentage_1.getAbsoluteValue)(styles.fontSize, 0);
|
|
124
|
+
verticalOffset = (bounds.height - fontSizeValue) / 2;
|
|
125
|
+
}
|
|
126
|
+
const textBounds = bounds.add(x, verticalOffset, 0, 0);
|
|
127
|
+
ctx.save();
|
|
128
|
+
pathFn([
|
|
129
|
+
new vector_1.Vector(bounds.left, bounds.top),
|
|
130
|
+
new vector_1.Vector(bounds.left + bounds.width, bounds.top),
|
|
131
|
+
new vector_1.Vector(bounds.left + bounds.width, bounds.top + bounds.height),
|
|
132
|
+
new vector_1.Vector(bounds.left, bounds.top + bounds.height)
|
|
133
|
+
]);
|
|
134
|
+
ctx.clip();
|
|
135
|
+
textRenderer.renderTextWithLetterSpacing(new text_1.TextBounds(container.value, textBounds), styles.letterSpacing, baseline, styles.writingMode);
|
|
136
|
+
ctx.restore();
|
|
137
|
+
ctx.textBaseline = 'alphabetic';
|
|
138
|
+
ctx.textAlign = 'left';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Render list-item marker (image or text).
|
|
143
|
+
*/
|
|
144
|
+
async function renderListMarker(ctx, context, textRenderer, paint, container, styles) {
|
|
145
|
+
if (!(0, bitwise_1.contains)(container.styles.display, 2048 /* DISPLAY.LIST_ITEM */)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (container.styles.listStyleImage !== null) {
|
|
149
|
+
const img = container.styles.listStyleImage;
|
|
150
|
+
if (img.type === 0 /* CSSImageType.URL */) {
|
|
151
|
+
const url = img.url;
|
|
152
|
+
try {
|
|
153
|
+
const image = await context.cache.match(url);
|
|
154
|
+
ctx.drawImage(image, container.bounds.left - (image.width + 10), container.bounds.top);
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
context.logger.error(`Error loading list-style-image ${url}`);
|
|
158
|
+
context.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (paint.listValue && container.styles.listStyleType !== -1 /* LIST_STYLE_TYPE.NONE */) {
|
|
163
|
+
const [font] = textRenderer.createFontStyle(styles);
|
|
164
|
+
ctx.font = font;
|
|
165
|
+
ctx.fillStyle = (0, color_utilities_1.asString)(styles.color);
|
|
166
|
+
ctx.textBaseline = 'middle';
|
|
167
|
+
ctx.textAlign = 'right';
|
|
168
|
+
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);
|
|
169
|
+
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);
|
|
170
|
+
ctx.textBaseline = 'bottom';
|
|
171
|
+
ctx.textAlign = 'left';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Type guard for text input containers.
|
|
176
|
+
*/
|
|
177
|
+
const isTextInputElement = (container) => {
|
|
178
|
+
if (container instanceof textarea_element_container_1.TextareaElementContainer) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
else if (container instanceof select_element_container_1.SelectElementContainer) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
else if (container instanceof input_element_container_1.InputElementContainer && container.type !== input_element_container_1.RADIO && container.type !== input_element_container_1.CHECKBOX) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
return false;
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Map CSS text-align to Canvas textAlign.
|
|
191
|
+
*/
|
|
192
|
+
const canvasTextAlign = (textAlign) => {
|
|
193
|
+
switch (textAlign) {
|
|
194
|
+
case 1 /* TEXT_ALIGN.CENTER */:
|
|
195
|
+
return 'center';
|
|
196
|
+
case 2 /* TEXT_ALIGN.RIGHT */:
|
|
197
|
+
return 'right';
|
|
198
|
+
case 0 /* TEXT_ALIGN.LEFT */:
|
|
199
|
+
default:
|
|
200
|
+
return 'left';
|
|
201
|
+
}
|
|
202
|
+
};
|
|
@@ -66,6 +66,9 @@ class EffectsRenderer {
|
|
|
66
66
|
else if ((0, effects_1.isBlendEffect)(effect)) {
|
|
67
67
|
this.ctx.globalCompositeOperation = effect.compositeOperation;
|
|
68
68
|
}
|
|
69
|
+
else if ((0, effects_1.isFilterEffect)(effect)) {
|
|
70
|
+
this.ctx.filter = effect.filterString;
|
|
71
|
+
}
|
|
69
72
|
this.activeEffects.push(effect);
|
|
70
73
|
}
|
|
71
74
|
/**
|
|
@@ -8,7 +8,11 @@ class ForeignObjectRenderer {
|
|
|
8
8
|
this.context = context;
|
|
9
9
|
this.options = options;
|
|
10
10
|
this.canvas = options.canvas ? options.canvas : document.createElement('canvas');
|
|
11
|
-
|
|
11
|
+
const ctx = this.canvas.getContext('2d');
|
|
12
|
+
if (!ctx) {
|
|
13
|
+
throw new Error('Failed to get 2D rendering context from canvas');
|
|
14
|
+
}
|
|
15
|
+
this.ctx = ctx;
|
|
12
16
|
this.canvas.width = Math.floor(options.width * options.scale);
|
|
13
17
|
this.canvas.height = Math.floor(options.height * options.scale);
|
|
14
18
|
this.canvas.style.width = `${options.width}px`;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TextDecorationRenderer = void 0;
|
|
4
|
+
const color_utilities_1 = require("../../../css/types/color-utilities");
|
|
5
|
+
class TextDecorationRenderer {
|
|
6
|
+
constructor(ctx) {
|
|
7
|
+
this.ctx = ctx;
|
|
8
|
+
}
|
|
9
|
+
render(bounds, styles) {
|
|
10
|
+
this.ctx.fillStyle = (0, color_utilities_1.asString)(styles.textDecorationColor || styles.color);
|
|
11
|
+
let thickness = 1;
|
|
12
|
+
if (typeof styles.textDecorationThickness === 'number') {
|
|
13
|
+
thickness = styles.textDecorationThickness;
|
|
14
|
+
}
|
|
15
|
+
else if (styles.textDecorationThickness === 'from-font') {
|
|
16
|
+
thickness = Math.max(1, Math.floor(styles.fontSize.number * 0.05));
|
|
17
|
+
}
|
|
18
|
+
let underlineOffset = 0;
|
|
19
|
+
if (typeof styles.textUnderlineOffset === 'number') {
|
|
20
|
+
underlineOffset = styles.textUnderlineOffset;
|
|
21
|
+
}
|
|
22
|
+
const decorationStyle = styles.textDecorationStyle;
|
|
23
|
+
styles.textDecorationLine.forEach((line) => {
|
|
24
|
+
let y = 0;
|
|
25
|
+
switch (line) {
|
|
26
|
+
case 1 /* TEXT_DECORATION_LINE.UNDERLINE */:
|
|
27
|
+
y = bounds.top + bounds.height - thickness + underlineOffset;
|
|
28
|
+
break;
|
|
29
|
+
case 2 /* TEXT_DECORATION_LINE.OVERLINE */:
|
|
30
|
+
y = bounds.top;
|
|
31
|
+
break;
|
|
32
|
+
case 3 /* TEXT_DECORATION_LINE.LINE_THROUGH */:
|
|
33
|
+
y = bounds.top + (bounds.height / 2 - thickness / 2);
|
|
34
|
+
break;
|
|
35
|
+
default:
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.draw(bounds.left, y, bounds.width, thickness, decorationStyle);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
draw(x, y, width, thickness, style) {
|
|
42
|
+
switch (style) {
|
|
43
|
+
case 0 /* TEXT_DECORATION_STYLE.SOLID */:
|
|
44
|
+
this.ctx.fillRect(x, y, width, thickness);
|
|
45
|
+
break;
|
|
46
|
+
case 1 /* TEXT_DECORATION_STYLE.DOUBLE */: {
|
|
47
|
+
const gap = Math.max(1, thickness);
|
|
48
|
+
this.ctx.fillRect(x, y, width, thickness);
|
|
49
|
+
this.ctx.fillRect(x, y + thickness + gap, width, thickness);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 2 /* TEXT_DECORATION_STYLE.DOTTED */:
|
|
53
|
+
this.drawPattern(x, y, width, thickness, [thickness, thickness * 2]);
|
|
54
|
+
break;
|
|
55
|
+
case 3 /* TEXT_DECORATION_STYLE.DASHED */:
|
|
56
|
+
this.drawPattern(x, y, width, thickness, [thickness * 3, thickness * 2]);
|
|
57
|
+
break;
|
|
58
|
+
case 4 /* TEXT_DECORATION_STYLE.WAVY */:
|
|
59
|
+
this.drawWavy(x, y, width, thickness);
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
this.ctx.fillRect(x, y, width, thickness);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
drawPattern(x, y, width, thickness, dash) {
|
|
66
|
+
this.ctx.save();
|
|
67
|
+
this.ctx.beginPath();
|
|
68
|
+
this.ctx.setLineDash(dash);
|
|
69
|
+
this.ctx.lineWidth = thickness;
|
|
70
|
+
this.ctx.strokeStyle = this.ctx.fillStyle;
|
|
71
|
+
this.ctx.moveTo(x, y + thickness / 2);
|
|
72
|
+
this.ctx.lineTo(x + width, y + thickness / 2);
|
|
73
|
+
this.ctx.stroke();
|
|
74
|
+
this.ctx.restore();
|
|
75
|
+
}
|
|
76
|
+
drawWavy(x, y, width, thickness) {
|
|
77
|
+
this.ctx.save();
|
|
78
|
+
this.ctx.beginPath();
|
|
79
|
+
this.ctx.lineWidth = thickness;
|
|
80
|
+
this.ctx.strokeStyle = this.ctx.fillStyle;
|
|
81
|
+
const amplitude = thickness * 2;
|
|
82
|
+
const wavelength = thickness * 4;
|
|
83
|
+
let currentX = x;
|
|
84
|
+
this.ctx.moveTo(currentX, y + thickness / 2);
|
|
85
|
+
while (currentX < x + width) {
|
|
86
|
+
const nextX = Math.min(currentX + wavelength / 2, x + width);
|
|
87
|
+
this.ctx.quadraticCurveTo(currentX + wavelength / 4, y + thickness / 2 - amplitude, nextX, y + thickness / 2);
|
|
88
|
+
currentX = nextX;
|
|
89
|
+
if (currentX < x + width) {
|
|
90
|
+
const nextX2 = Math.min(currentX + wavelength / 2, x + width);
|
|
91
|
+
this.ctx.quadraticCurveTo(currentX + wavelength / 4, y + thickness / 2 + amplitude, nextX2, y + thickness / 2);
|
|
92
|
+
currentX = nextX2;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.ctx.stroke();
|
|
96
|
+
this.ctx.restore();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.TextDecorationRenderer = TextDecorationRenderer;
|