html2canvas-pro 2.0.0 → 2.0.1

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 (31) hide show
  1. package/dist/html2canvas-pro.esm.js +524 -91
  2. package/dist/html2canvas-pro.esm.js.map +1 -1
  3. package/dist/html2canvas-pro.js +524 -91
  4. package/dist/html2canvas-pro.js.map +1 -1
  5. package/dist/html2canvas-pro.min.js +5 -5
  6. package/dist/lib/css/index.js +2 -0
  7. package/dist/lib/css/index.js.map +1 -1
  8. package/dist/lib/css/property-descriptors/__tests__/clip-path.test.js +273 -0
  9. package/dist/lib/css/property-descriptors/__tests__/clip-path.test.js.map +1 -0
  10. package/dist/lib/css/property-descriptors/clip-path.js +190 -0
  11. package/dist/lib/css/property-descriptors/clip-path.js.map +1 -0
  12. package/dist/lib/dom/dom-normalizer.js +57 -21
  13. package/dist/lib/dom/dom-normalizer.js.map +1 -1
  14. package/dist/lib/render/canvas/__tests__/text-renderer.test.js +283 -36
  15. package/dist/lib/render/canvas/__tests__/text-renderer.test.js.map +1 -1
  16. package/dist/lib/render/canvas/effects-renderer.js +11 -6
  17. package/dist/lib/render/canvas/effects-renderer.js.map +1 -1
  18. package/dist/lib/render/canvas/text-renderer.js +131 -64
  19. package/dist/lib/render/canvas/text-renderer.js.map +1 -1
  20. package/dist/lib/render/effects.js +17 -1
  21. package/dist/lib/render/effects.js.map +1 -1
  22. package/dist/lib/render/stacking-context.js +131 -0
  23. package/dist/lib/render/stacking-context.js.map +1 -1
  24. package/dist/types/css/index.d.ts +2 -0
  25. package/dist/types/css/property-descriptors/__tests__/clip-path.test.d.ts +1 -0
  26. package/dist/types/css/property-descriptors/clip-path.d.ts +62 -0
  27. package/dist/types/dom/dom-normalizer.d.ts +30 -11
  28. package/dist/types/render/canvas/effects-renderer.d.ts +2 -1
  29. package/dist/types/render/canvas/text-renderer.d.ts +20 -2
  30. package/dist/types/render/effects.d.ts +15 -1
  31. package/package.json +1 -1
@@ -9,11 +9,38 @@ exports.DOMNormalizer = void 0;
9
9
  const node_type_guards_1 = require("./node-type-guards");
10
10
  /**
11
11
  * Normalize element styles for accurate rendering
12
- * This includes disabling animations and resetting transforms
12
+ * This includes disabling animations and neutralizing transforms.
13
13
  */
14
14
  class DOMNormalizer {
15
15
  /**
16
- * Normalize a single element and return original styles
16
+ * Normalize a single element and return original styles.
17
+ *
18
+ * ## Why we replace transforms with an identity value instead of "none"
19
+ *
20
+ * `getBoundingClientRect()` returns visual (post-transform) coordinates, so we
21
+ * must neutralize any active transform before measuring element bounds.
22
+ *
23
+ * The naive approach of setting `transform: none` (or `rotate: none`) has a
24
+ * critical side-effect: per **CSS Transforms Level 2**, an element whose
25
+ * `transform` is non-none automatically becomes the **containing block** for
26
+ * all of its `position: absolute` *and* `position: fixed` descendants.
27
+ * Setting it to `none` destroys that role, causing children to resolve their
28
+ * percentage dimensions and offsets against an unintended ancestor — which
29
+ * produces completely wrong bounds.
30
+ *
31
+ * Solution: instead of removing the transform, we replace it with a visually
32
+ * inert identity value:
33
+ *
34
+ * - `transform: scale(0.5)` → `transform: translate(0, 0)`
35
+ * - `translate(0, 0)` is an identity transform (no visual change, no layout shift).
36
+ * - `getBoundingClientRect()` returns the same layout-space coordinates as
37
+ * if there were no transform at all.
38
+ * - Because the value is still non-none, the element **remains a containing
39
+ * block** for both `position: absolute` and `position: fixed` descendants.
40
+ *
41
+ * - `rotate: 45deg` → `rotate: 0deg`
42
+ * - `0deg` is the identity rotation; `0deg ≠ none`, so the same containing-
43
+ * block guarantee holds.
17
44
  *
18
45
  * @param element - Element to normalize
19
46
  * @param styles - Parsed CSS styles
@@ -29,33 +56,43 @@ class DOMNormalizer {
29
56
  originalStyles.animationDuration = element.style.animationDuration;
30
57
  element.style.animationDuration = '0s';
31
58
  }
32
- // Reset transform for accurate bounds calculation
33
- // getBoundingClientRect takes transforms into account
59
+ // Replace the actual transform with an identity translate so that:
60
+ // 1. getBoundingClientRect() returns layout-space (unscaled/unrotated) coords.
61
+ // 2. The element still satisfies "transform != none" and therefore keeps
62
+ // its role as a containing block for position:absolute / position:fixed
63
+ // descendants (CSS Transforms Level 2 §2.3).
34
64
  if (styles.transform !== null) {
35
65
  originalStyles.transform = element.style.transform;
36
- element.style.transform = 'none';
66
+ element.style.transform = 'translate(0, 0)';
37
67
  }
38
- // Reset rotate property similarly to transform
68
+ // Same rationale for the standalone `rotate` property.
69
+ // `rotate: 0deg` is an identity rotation with no visual effect.
70
+ //
71
+ // However, individual transform properties (`rotate`, `translate`, `scale`)
72
+ // are part of CSS Transforms Level 2 and their containing-block guarantee
73
+ // is not uniformly implemented across all browsers. To be safe, if `rotate`
74
+ // is the only transform-like property active on this element, we also set
75
+ // `transform: translate(0, 0)` so that the containing-block role is reliably
76
+ // preserved via the well-supported `transform` property.
39
77
  if (styles.rotate !== null) {
40
78
  originalStyles.rotate = element.style.rotate;
41
- element.style.rotate = 'none';
79
+ element.style.rotate = '0deg';
80
+ // Individual transform properties (`rotate`, `translate`, `scale`) are
81
+ // CSS Transforms Level 2 and their containing-block guarantee is not
82
+ // uniformly implemented in all browsers. If `transform` was not already
83
+ // set to translate(0,0) in the block above (i.e. this element has
84
+ // `rotate` but no `transform`), we set it now so the containing-block
85
+ // role is reliably established via the widely-supported `transform`
86
+ // property – independently of browser support for individual props.
87
+ if (originalStyles.transform === undefined) {
88
+ originalStyles.transform = element.style.transform;
89
+ element.style.transform = 'translate(0, 0)';
90
+ }
42
91
  }
43
92
  return originalStyles;
44
93
  }
45
94
  /**
46
- * Normalize element and its descendants recursively
47
- *
48
- * @param element - Element to normalize
49
- * @param styles - Parsed CSS styles
50
- * @returns Original styles map for restoration
51
- */
52
- static normalizeTree(element, styles) {
53
- return this.normalizeElement(element, styles);
54
- // Could add recursive normalization here if needed
55
- // For now, only normalize the element itself
56
- }
57
- /**
58
- * Restore element styles after rendering
95
+ * Restore element styles after rendering.
59
96
  *
60
97
  * @param element - Element to restore
61
98
  * @param originalStyles - Original styles to restore
@@ -64,7 +101,6 @@ class DOMNormalizer {
64
101
  if (!(0, node_type_guards_1.isHTMLElementNode)(element)) {
65
102
  return;
66
103
  }
67
- // Restore each property that was saved
68
104
  if (originalStyles.animationDuration !== undefined) {
69
105
  element.style.animationDuration = originalStyles.animationDuration;
70
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dom-normalizer.js","sourceRoot":"","sources":["../../../src/dom/dom-normalizer.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAGH,yDAAuD;AAWvD;;;GAGG;AACH,MAAa,aAAa;IACtB;;;;;;OAMG;IACH,MAAM,CAAC,gBAAgB,CAAC,OAAgB,EAAE,MAA4B;QAClE,MAAM,cAAc,GAAmB,EAAE,CAAC;QAE1C,IAAI,CAAC,IAAA,oCAAiB,EAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,OAAO,cAAc,CAAC;QAC1B,CAAC;QAED,6CAA6C;QAC7C,IAAI,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC;YAC5D,cAAc,CAAC,iBAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC;YACnE,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC3C,CAAC;QAED,kDAAkD;QAClD,sDAAsD;QACtD,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC5B,cAAc,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;YACnD,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;QACrC,CAAC;QAED,+CAA+C;QAC/C,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YACzB,cAAc,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAClC,CAAC;QAED,OAAO,cAAc,CAAC;IAC1B,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAAC,OAAgB,EAAE,MAA4B;QAC/D,OAAO,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAE9C,mDAAmD;QACnD,6CAA6C;IACjD,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,cAAc,CAAC,OAAgB,EAAE,cAA8B;QAClE,IAAI,CAAC,IAAA,oCAAiB,EAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,OAAO;QACX,CAAC;QAED,uCAAuC;QACvC,IAAI,cAAc,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;YACjD,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,cAAc,CAAC,iBAAiB,CAAC;QACvE,CAAC;QAED,IAAI,cAAc,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC;QACvD,CAAC;QAED,IAAI,cAAc,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACtC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC;QACjD,CAAC;IACL,CAAC;CACJ;AA3ED,sCA2EC"}
1
+ {"version":3,"file":"dom-normalizer.js","sourceRoot":"","sources":["../../../src/dom/dom-normalizer.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAGH,yDAAuD;AAWvD;;;GAGG;AACH,MAAa,aAAa;IACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,MAAM,CAAC,gBAAgB,CAAC,OAAgB,EAAE,MAA4B;QAClE,MAAM,cAAc,GAAmB,EAAE,CAAC;QAE1C,IAAI,CAAC,IAAA,oCAAiB,EAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,OAAO,cAAc,CAAC;QAC1B,CAAC;QAED,6CAA6C;QAC7C,IAAI,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC;YAC5D,cAAc,CAAC,iBAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC;YACnE,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC3C,CAAC;QAED,mEAAmE;QACnE,iFAAiF;QACjF,2EAA2E;QAC3E,6EAA6E;QAC7E,kDAAkD;QAClD,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC5B,cAAc,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;YACnD,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,iBAAiB,CAAC;QAChD,CAAC;QAED,uDAAuD;QACvD,gEAAgE;QAChE,EAAE;QACF,4EAA4E;QAC5E,0EAA0E;QAC1E,4EAA4E;QAC5E,0EAA0E;QAC1E,6EAA6E;QAC7E,yDAAyD;QACzD,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YACzB,cAAc,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;YAE9B,uEAAuE;YACvE,qEAAqE;YACrE,wEAAwE;YACxE,kEAAkE;YAClE,sEAAsE;YACtE,oEAAoE;YACpE,oEAAoE;YACpE,IAAI,cAAc,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBACzC,cAAc,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;gBACnD,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,iBAAiB,CAAC;YAChD,CAAC;QACL,CAAC;QAED,OAAO,cAAc,CAAC;IAC1B,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,cAAc,CAAC,OAAgB,EAAE,cAA8B;QAClE,IAAI,CAAC,IAAA,oCAAiB,EAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,OAAO;QACX,CAAC;QAED,IAAI,cAAc,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;YACjD,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,cAAc,CAAC,iBAAiB,CAAC;QACvE,CAAC;QAED,IAAI,cAAc,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC;QACvD,CAAC;QAED,IAAI,cAAc,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACtC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC;QACjD,CAAC;IACL,CAAC;CACJ;AA9GD,sCA8GC"}
@@ -4,41 +4,44 @@ const assert_1 = require("assert");
4
4
  const text_renderer_1 = require("../text-renderer");
5
5
  const context_1 = require("../../../core/context");
6
6
  const bounds_1 = require("../../../css/layout/bounds");
7
+ const text_1 = require("../../../css/layout/text");
7
8
  const config_1 = require("../../../config");
9
+ const createMockContext = () => {
10
+ const mockWindow = {
11
+ document: {
12
+ createElement: (_name) => {
13
+ let _href = '';
14
+ return {
15
+ set href(value) {
16
+ _href = value;
17
+ },
18
+ get href() {
19
+ return _href;
20
+ },
21
+ get protocol() {
22
+ return 'http:';
23
+ },
24
+ get hostname() {
25
+ return 'localhost';
26
+ },
27
+ get port() {
28
+ return '';
29
+ }
30
+ };
31
+ }
32
+ },
33
+ location: { href: 'http://localhost/' }
34
+ };
35
+ const config = new config_1.Html2CanvasConfig({ window: mockWindow });
36
+ return new context_1.Context({
37
+ logging: false,
38
+ imageTimeout: 15000,
39
+ useCORS: false,
40
+ allowTaint: false
41
+ }, new bounds_1.Bounds(0, 0, 800, 600), config);
42
+ };
8
43
  describe('TextRenderer', () => {
9
44
  it('should be instantiated', () => {
10
- const mockWindow = {
11
- document: {
12
- createElement: (_name) => {
13
- let _href = '';
14
- return {
15
- set href(value) {
16
- _href = value;
17
- },
18
- get href() {
19
- return _href;
20
- },
21
- get protocol() {
22
- return 'http:';
23
- },
24
- get hostname() {
25
- return 'localhost';
26
- },
27
- get port() {
28
- return '';
29
- }
30
- };
31
- }
32
- },
33
- location: { href: 'http://localhost/' }
34
- };
35
- const config = new config_1.Html2CanvasConfig({ window: mockWindow });
36
- const context = new context_1.Context({
37
- logging: false,
38
- imageTimeout: 15000,
39
- useCORS: false,
40
- allowTaint: false
41
- }, new bounds_1.Bounds(0, 0, 800, 600), config);
42
45
  const ctx = {
43
46
  fillStyle: '',
44
47
  font: '',
@@ -47,10 +50,8 @@ describe('TextRenderer', () => {
47
50
  };
48
51
  const deps = {
49
52
  ctx,
50
- context,
51
- options: {
52
- scale: 1
53
- }
53
+ context: createMockContext(),
54
+ options: { scale: 1 }
54
55
  };
55
56
  const renderer = new text_renderer_1.TextRenderer(deps);
56
57
  (0, assert_1.ok)(renderer);
@@ -60,4 +61,250 @@ describe('TextRenderer', () => {
60
61
  (0, assert_1.strictEqual)(typeof renderer.createFontStyle, 'function');
61
62
  });
62
63
  });
64
+ describe('hasCJKCharacters', () => {
65
+ it('should return true for Chinese characters', () => {
66
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('快照'), true);
67
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('截图'), true);
68
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('中文'), true);
69
+ });
70
+ it('should return true for Japanese characters', () => {
71
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('ひらがな'), true); // Hiragana
72
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('カタカナ'), true); // Katakana
73
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('漢字'), true); // Kanji
74
+ });
75
+ it('should return true for Korean characters', () => {
76
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('한글'), true);
77
+ });
78
+ it('should return true for CJK punctuation and symbols', () => {
79
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('。'), true); // CJK full stop (U+3002)
80
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('、'), true); // CJK comma (U+3001)
81
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('「」'), true); // CJK brackets
82
+ });
83
+ it('should return true for fullwidth characters (U+FF01–U+FFEF)', () => {
84
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('!'), true); // Fullwidth ! (U+FF01, range start)
85
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('A'), true); // Fullwidth A (U+FF21)
86
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('1'), true); // Fullwidth 1 (U+FF11)
87
+ });
88
+ it('should return false for Latin characters', () => {
89
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('Hello'), false);
90
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('SOS'), false);
91
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('abc123'), false);
92
+ });
93
+ it('should return false for empty string', () => {
94
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)(''), false);
95
+ });
96
+ it('should return true for mixed text containing CJK', () => {
97
+ (0, assert_1.strictEqual)((0, text_renderer_1.hasCJKCharacters)('SOS 快照'), true);
98
+ });
99
+ });
100
+ describe('renderTextWithLetterSpacing', () => {
101
+ it('should apply letterSpacing to each character x position (Issue #73 Bug1)', () => {
102
+ const fillCalls = [];
103
+ const measureResults = { A: 10, B: 12, C: 8 };
104
+ const ctx = {
105
+ fillStyle: '',
106
+ font: '',
107
+ textBaseline: 'alphabetic',
108
+ fillText(text, x, y) {
109
+ fillCalls.push({ text, x, y });
110
+ },
111
+ measureText(text) {
112
+ return { width: measureResults[text] ?? 10 };
113
+ }
114
+ };
115
+ const deps = {
116
+ ctx,
117
+ context: createMockContext(),
118
+ options: { scale: 1 }
119
+ };
120
+ const renderer = new text_renderer_1.TextRenderer(deps);
121
+ const bounds = new bounds_1.Bounds(100, 50, 200, 25);
122
+ const text = new text_1.TextBounds('ABC', bounds);
123
+ const letterSpacing = 5;
124
+ const baseline = 20;
125
+ renderer.renderTextWithLetterSpacing(text, letterSpacing, baseline);
126
+ // Verify letter spacing is added between characters
127
+ // A: x=100, B: x=100+10+5=115, C: x=115+12+5=132
128
+ (0, assert_1.strictEqual)(fillCalls.length, 3);
129
+ (0, assert_1.strictEqual)(fillCalls[0].text, 'A');
130
+ (0, assert_1.strictEqual)(fillCalls[0].x, 100);
131
+ (0, assert_1.strictEqual)(fillCalls[1].text, 'B');
132
+ (0, assert_1.strictEqual)(fillCalls[1].x, 115); // 100 + measureText('A').width(10) + letterSpacing(5)
133
+ (0, assert_1.strictEqual)(fillCalls[2].text, 'C');
134
+ (0, assert_1.strictEqual)(fillCalls[2].x, 132); // 115 + measureText('B').width(12) + letterSpacing(5)
135
+ // Verify y position uses baseline
136
+ fillCalls.forEach((call) => {
137
+ (0, assert_1.strictEqual)(call.y, bounds.top + baseline); // 50 + 20 = 70
138
+ });
139
+ });
140
+ it('should use ideographic baseline for CJK characters (Issue #73 Bug2)', () => {
141
+ const baselineChanges = [];
142
+ let currentBaseline = 'alphabetic';
143
+ const ctx = {
144
+ fillStyle: '',
145
+ font: '',
146
+ get textBaseline() {
147
+ return currentBaseline;
148
+ },
149
+ set textBaseline(value) {
150
+ currentBaseline = value;
151
+ baselineChanges.push(value);
152
+ },
153
+ fillText(_text, _x, _y) { },
154
+ measureText(_text) {
155
+ return { width: 25 };
156
+ }
157
+ };
158
+ const deps = {
159
+ ctx,
160
+ context: createMockContext(),
161
+ options: { scale: 1 }
162
+ };
163
+ const renderer = new text_renderer_1.TextRenderer(deps);
164
+ const bounds = new bounds_1.Bounds(0, 0, 100, 25);
165
+ const text = new text_1.TextBounds('快照', bounds);
166
+ renderer.renderTextWithLetterSpacing(text, 10, 20);
167
+ // Should have switched to ideographic for each CJK char and restored
168
+ // Pattern: [ideographic, alphabetic, ideographic, alphabetic]
169
+ (0, assert_1.ok)(baselineChanges.includes('ideographic'), 'should switch to ideographic baseline for CJK');
170
+ // Should restore alphabetic after each CJK char
171
+ const ideographicIdx = baselineChanges.indexOf('ideographic');
172
+ (0, assert_1.strictEqual)(baselineChanges[ideographicIdx + 1], 'alphabetic');
173
+ });
174
+ it('should not change textBaseline for non-CJK characters', () => {
175
+ const baselineChanges = [];
176
+ let currentBaseline = 'alphabetic';
177
+ const ctx = {
178
+ fillStyle: '',
179
+ font: '',
180
+ get textBaseline() {
181
+ return currentBaseline;
182
+ },
183
+ set textBaseline(value) {
184
+ currentBaseline = value;
185
+ baselineChanges.push(value);
186
+ },
187
+ fillText(_text, _x, _y) { },
188
+ measureText(_text) {
189
+ return { width: 10 };
190
+ }
191
+ };
192
+ const deps = {
193
+ ctx,
194
+ context: createMockContext(),
195
+ options: { scale: 1 }
196
+ };
197
+ const renderer = new text_renderer_1.TextRenderer(deps);
198
+ const bounds = new bounds_1.Bounds(0, 0, 100, 25);
199
+ const text = new text_1.TextBounds('ABC', bounds);
200
+ renderer.renderTextWithLetterSpacing(text, 5, 20);
201
+ // Should not switch to ideographic for Latin characters
202
+ (0, assert_1.ok)(!baselineChanges.includes('ideographic'), 'should not switch to ideographic for Latin text');
203
+ });
204
+ it('should render whole string in one call when letterSpacing is 0', () => {
205
+ const fillCalls = [];
206
+ const ctx = {
207
+ fillStyle: '',
208
+ font: '',
209
+ textBaseline: 'alphabetic',
210
+ fillText(text, x, y) {
211
+ fillCalls.push({ text, x, y });
212
+ },
213
+ measureText(_text) {
214
+ return { width: 30 };
215
+ }
216
+ };
217
+ const deps = {
218
+ ctx,
219
+ context: createMockContext(),
220
+ options: { scale: 1 }
221
+ };
222
+ const renderer = new text_renderer_1.TextRenderer(deps);
223
+ const bounds = new bounds_1.Bounds(10, 20, 100, 25);
224
+ const text = new text_1.TextBounds('Hello', bounds);
225
+ renderer.renderTextWithLetterSpacing(text, 0, 22);
226
+ (0, assert_1.deepStrictEqual)(fillCalls, [{ text: 'Hello', x: 10, y: 42 }]);
227
+ });
228
+ it('should handle negative letterSpacing correctly', () => {
229
+ const fillCalls = [];
230
+ const ctx = {
231
+ fillStyle: '',
232
+ font: '',
233
+ textBaseline: 'alphabetic',
234
+ fillText(text, x, _y) {
235
+ fillCalls.push({ text, x });
236
+ },
237
+ measureText(_text) {
238
+ return { width: 10 };
239
+ }
240
+ };
241
+ const deps = {
242
+ ctx,
243
+ context: createMockContext(),
244
+ options: { scale: 1 }
245
+ };
246
+ const renderer = new text_renderer_1.TextRenderer(deps);
247
+ const bounds = new bounds_1.Bounds(100, 0, 50, 20);
248
+ const text = new text_1.TextBounds('AB', bounds);
249
+ renderer.renderTextWithLetterSpacing(text, -3, 15);
250
+ // A: x=100, B: x=100 + 10 + (-3) = 107
251
+ (0, assert_1.strictEqual)(fillCalls[0].x, 100);
252
+ (0, assert_1.strictEqual)(fillCalls[1].x, 107);
253
+ });
254
+ it('should handle mixed CJK and Latin text with correct baseline per character', () => {
255
+ const baselineAtRender = {};
256
+ let currentBaseline = 'alphabetic';
257
+ const ctx = {
258
+ fillStyle: '',
259
+ font: '',
260
+ get textBaseline() {
261
+ return currentBaseline;
262
+ },
263
+ set textBaseline(value) {
264
+ currentBaseline = value;
265
+ },
266
+ fillText(text, _x, _y) {
267
+ baselineAtRender[text] = currentBaseline;
268
+ },
269
+ measureText(_text) {
270
+ return { width: 12 };
271
+ }
272
+ };
273
+ const deps = {
274
+ ctx,
275
+ context: createMockContext(),
276
+ options: { scale: 1 }
277
+ };
278
+ const renderer = new text_renderer_1.TextRenderer(deps);
279
+ const bounds = new bounds_1.Bounds(0, 0, 200, 25);
280
+ // Mixed: Latin 'A', CJK '快', Latin 'B'
281
+ const text = new text_1.TextBounds('A快B', bounds);
282
+ renderer.renderTextWithLetterSpacing(text, 5, 20);
283
+ (0, assert_1.strictEqual)(baselineAtRender['A'], 'alphabetic', 'Latin char should use alphabetic baseline');
284
+ (0, assert_1.strictEqual)(baselineAtRender['快'], 'ideographic', 'CJK char should use ideographic baseline');
285
+ (0, assert_1.strictEqual)(baselineAtRender['B'], 'alphabetic', 'Latin char after CJK should restore alphabetic baseline');
286
+ });
287
+ it('should handle empty string without errors', () => {
288
+ const ctx = {
289
+ fillStyle: '',
290
+ font: '',
291
+ textBaseline: 'alphabetic',
292
+ fillText(_text, _x, _y) { },
293
+ measureText(_text) {
294
+ return { width: 0 };
295
+ }
296
+ };
297
+ const deps = {
298
+ ctx,
299
+ context: createMockContext(),
300
+ options: { scale: 1 }
301
+ };
302
+ const renderer = new text_renderer_1.TextRenderer(deps);
303
+ const bounds = new bounds_1.Bounds(0, 0, 100, 25);
304
+ const text = new text_1.TextBounds('', bounds);
305
+ // Should not throw
306
+ renderer.renderTextWithLetterSpacing(text, 5, 20);
307
+ renderer.renderTextWithLetterSpacing(text, 0, 20);
308
+ });
309
+ });
63
310
  //# sourceMappingURL=text-renderer.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"text-renderer.test.js","sourceRoot":"","sources":["../../../../../src/render/canvas/__tests__/text-renderer.test.ts"],"names":[],"mappings":";;AAAA,mCAAyC;AACzC,oDAA0E;AAC1E,mDAAgD;AAChD,uDAAoD;AACpD,4CAAoD;AAEpD,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAC9B,MAAM,UAAU,GAAG;YACf,QAAQ,EAAE;gBACN,aAAa,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC7B,IAAI,KAAK,GAAG,EAAE,CAAC;oBACf,OAAO;wBACH,IAAI,IAAI,CAAC,KAAa;4BAClB,KAAK,GAAG,KAAK,CAAC;wBAClB,CAAC;wBACD,IAAI,IAAI;4BACJ,OAAO,KAAK,CAAC;wBACjB,CAAC;wBACD,IAAI,QAAQ;4BACR,OAAO,OAAO,CAAC;wBACnB,CAAC;wBACD,IAAI,QAAQ;4BACR,OAAO,WAAW,CAAC;wBACvB,CAAC;wBACD,IAAI,IAAI;4BACJ,OAAO,EAAE,CAAC;wBACd,CAAC;qBACJ,CAAC;gBACN,CAAC;aACJ;YACD,QAAQ,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE;SACrB,CAAC;QAEvB,MAAM,MAAM,GAAG,IAAI,0BAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,iBAAO,CACvB;YACI,OAAO,EAAE,KAAK;YACd,YAAY,EAAE,KAAK;YACnB,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,KAAK;SACpB,EACD,IAAI,eAAM,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,EAC1B,MAAM,CACT,CAAC;QAEF,MAAM,GAAG,GAAG;YACR,SAAS,EAAE,EAAE;YACb,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;YACd,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;SACmB,CAAC;QAEzC,MAAM,IAAI,GAA6B;YACnC,GAAG;YACH,OAAO;YACP,OAAO,EAAE;gBACL,KAAK,EAAE,CAAC;aACX;SACJ,CAAC;QAEF,MAAM,QAAQ,GAAG,IAAI,4BAAY,CAAC,IAAI,CAAC,CAAC;QACxC,IAAA,WAAE,EAAC,QAAQ,CAAC,CAAC;QAEb,4BAA4B;QAC5B,IAAA,oBAAW,EAAC,OAAO,QAAQ,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QACxD,IAAA,oBAAW,EAAC,OAAO,QAAQ,CAAC,2BAA2B,EAAE,UAAU,CAAC,CAAC;QACrE,IAAA,oBAAW,EAAC,OAAO,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"text-renderer.test.js","sourceRoot":"","sources":["../../../../../src/render/canvas/__tests__/text-renderer.test.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,8 @@
5
5
  * Handles rendering effects including:
6
6
  * - Opacity effects
7
7
  * - Transform effects (matrix transformations)
8
- * - Clip effects (clipping paths)
8
+ * - Clip effects (overflow / border-radius clipping via Path[])
9
+ * - Clip-path effects (CSS clip-path shapes: inset, circle, ellipse, polygon, path)
9
10
  */
10
11
  Object.defineProperty(exports, "__esModule", { value: true });
11
12
  exports.EffectsRenderer = void 0;
@@ -43,21 +44,25 @@ class EffectsRenderer {
43
44
  */
44
45
  applyEffect(effect) {
45
46
  this.ctx.save();
46
- // Apply opacity effect
47
47
  if ((0, effects_1.isOpacityEffect)(effect)) {
48
+ // Opacity: multiply into the current global alpha for nested transparency.
48
49
  this.ctx.globalAlpha = effect.opacity;
49
50
  }
50
- // Apply transform effect
51
- if ((0, effects_1.isTransformEffect)(effect)) {
51
+ else if ((0, effects_1.isTransformEffect)(effect)) {
52
+ // Transform: translate to origin, apply matrix, translate back.
52
53
  this.ctx.translate(effect.offsetX, effect.offsetY);
53
54
  this.ctx.transform(effect.matrix[0], effect.matrix[1], effect.matrix[2], effect.matrix[3], effect.matrix[4], effect.matrix[5]);
54
55
  this.ctx.translate(-effect.offsetX, -effect.offsetY);
55
56
  }
56
- // Apply clip effect
57
- if ((0, effects_1.isClipEffect)(effect)) {
57
+ else if ((0, effects_1.isClipEffect)(effect)) {
58
+ // Clip (overflow / border-radius): build path via callback then clip.
58
59
  this.pathCallback.path(effect.path);
59
60
  this.ctx.clip();
60
61
  }
62
+ else if ((0, effects_1.isClipPathEffect)(effect)) {
63
+ // Clip-path: delegate shape drawing (beginPath … clip()) to the effect.
64
+ effect.applyClip(this.ctx);
65
+ }
61
66
  this.activeEffects.push(effect);
62
67
  }
63
68
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"effects-renderer.js","sourceRoot":"","sources":["../../../../src/render/canvas/effects-renderer.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,wCAA8F;AAiB9F;;;;;GAKG;AACH,MAAa,eAAe;IAKxB,YAAY,IAAiC,EAAE,YAAiC;QAF/D,kBAAa,GAAqB,EAAE,CAAC;QAGlD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,OAAyB;QAClC,6BAA6B;QAC7B,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;QACrB,CAAC;QAED,oBAAoB;QACpB,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,MAAsB;QAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAEhB,uBAAuB;QACvB,IAAI,IAAA,yBAAe,EAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC;QAC1C,CAAC;QAED,yBAAyB;QACzB,IAAI,IAAA,2BAAiB,EAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG,CAAC,SAAS,CACd,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CACnB,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QAED,oBAAoB;QACpB,IAAI,IAAA,sBAAY,EAAC,MAAM,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,SAAS;QACL,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,oBAAoB;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,gBAAgB;QACZ,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IACzC,CAAC;CACJ;AAxFD,0CAwFC"}
1
+ {"version":3,"file":"effects-renderer.js","sourceRoot":"","sources":["../../../../src/render/canvas/effects-renderer.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,wCAAgH;AAiBhH;;;;;GAKG;AACH,MAAa,eAAe;IAKxB,YAAY,IAAiC,EAAE,YAAiC;QAF/D,kBAAa,GAAqB,EAAE,CAAC;QAGlD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,OAAyB;QAClC,6BAA6B;QAC7B,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;QACrB,CAAC;QAED,oBAAoB;QACpB,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,MAAsB;QAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAEhB,IAAI,IAAA,yBAAe,EAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,2EAA2E;YAC3E,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC;QAC1C,CAAC;aAAM,IAAI,IAAA,2BAAiB,EAAC,MAAM,CAAC,EAAE,CAAC;YACnC,gEAAgE;YAChE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YACnD,IAAI,CAAC,GAAG,CAAC,SAAS,CACd,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAChB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CACnB,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;aAAM,IAAI,IAAA,sBAAY,EAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,sEAAsE;YACtE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACpB,CAAC;aAAM,IAAI,IAAA,0BAAgB,EAAC,MAAM,CAAC,EAAE,CAAC;YAClC,wEAAwE;YACxE,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,SAAS;QACL,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,oBAAoB;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,gBAAgB;QACZ,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IACzC,CAAC;CACJ;AAvFD,0CAuFC"}