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
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
import { ok, strictEqual, deepStrictEqual } from 'assert';
|
|
2
|
-
import { TextRenderer, TextRendererDependencies, hasCJKCharacters } from '../text-renderer';
|
|
3
|
-
import { Context } from '../../../core/context';
|
|
4
|
-
import { Bounds } from '../../../css/layout/bounds';
|
|
5
|
-
import { TextBounds } from '../../../css/layout/text';
|
|
6
|
-
import { Html2CanvasConfig } from '../../../config';
|
|
7
|
-
import { WRITING_MODE } from '../../../css/property-descriptors/writing-mode';
|
|
8
|
-
|
|
9
|
-
const createMockContext = (): Context => {
|
|
10
|
-
const mockWindow = {
|
|
11
|
-
document: {
|
|
12
|
-
createElement: (_name: string) => {
|
|
13
|
-
let _href = '';
|
|
14
|
-
return {
|
|
15
|
-
set href(value: string) {
|
|
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
|
-
} as unknown as Window;
|
|
35
|
-
|
|
36
|
-
const config = new Html2CanvasConfig({ window: mockWindow });
|
|
37
|
-
return new Context(
|
|
38
|
-
{
|
|
39
|
-
logging: false,
|
|
40
|
-
imageTimeout: 15000,
|
|
41
|
-
useCORS: false,
|
|
42
|
-
allowTaint: false
|
|
43
|
-
},
|
|
44
|
-
new Bounds(0, 0, 800, 600),
|
|
45
|
-
config
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
describe('TextRenderer', () => {
|
|
50
|
-
it('should be instantiated', () => {
|
|
51
|
-
const ctx = {
|
|
52
|
-
fillStyle: '',
|
|
53
|
-
font: '',
|
|
54
|
-
save: () => {},
|
|
55
|
-
restore: () => {}
|
|
56
|
-
} as unknown as CanvasRenderingContext2D;
|
|
57
|
-
|
|
58
|
-
const deps: TextRendererDependencies = {
|
|
59
|
-
ctx,
|
|
60
|
-
context: createMockContext(),
|
|
61
|
-
options: { scale: 1 }
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const renderer = new TextRenderer(deps);
|
|
65
|
-
ok(renderer);
|
|
66
|
-
|
|
67
|
-
// Test public methods exist
|
|
68
|
-
strictEqual(typeof renderer.renderTextNode, 'function');
|
|
69
|
-
strictEqual(typeof renderer.renderTextWithLetterSpacing, 'function');
|
|
70
|
-
strictEqual(typeof renderer.createFontStyle, 'function');
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('hasCJKCharacters', () => {
|
|
75
|
-
it('should return true for Chinese characters', () => {
|
|
76
|
-
strictEqual(hasCJKCharacters('快照'), true);
|
|
77
|
-
strictEqual(hasCJKCharacters('截图'), true);
|
|
78
|
-
strictEqual(hasCJKCharacters('中文'), true);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should return true for Japanese characters', () => {
|
|
82
|
-
strictEqual(hasCJKCharacters('ひらがな'), true); // Hiragana
|
|
83
|
-
strictEqual(hasCJKCharacters('カタカナ'), true); // Katakana
|
|
84
|
-
strictEqual(hasCJKCharacters('漢字'), true); // Kanji
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should return true for Korean characters', () => {
|
|
88
|
-
strictEqual(hasCJKCharacters('한글'), true);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should return true for CJK punctuation and symbols', () => {
|
|
92
|
-
strictEqual(hasCJKCharacters('。'), true); // CJK full stop (U+3002)
|
|
93
|
-
strictEqual(hasCJKCharacters('、'), true); // CJK comma (U+3001)
|
|
94
|
-
strictEqual(hasCJKCharacters('「」'), true); // CJK brackets
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should return true for fullwidth characters (U+FF01–U+FFEF)', () => {
|
|
98
|
-
strictEqual(hasCJKCharacters('!'), true); // Fullwidth ! (U+FF01, range start)
|
|
99
|
-
strictEqual(hasCJKCharacters('A'), true); // Fullwidth A (U+FF21)
|
|
100
|
-
strictEqual(hasCJKCharacters('1'), true); // Fullwidth 1 (U+FF11)
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should return false for Latin characters', () => {
|
|
104
|
-
strictEqual(hasCJKCharacters('Hello'), false);
|
|
105
|
-
strictEqual(hasCJKCharacters('SOS'), false);
|
|
106
|
-
strictEqual(hasCJKCharacters('abc123'), false);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should return false for empty string', () => {
|
|
110
|
-
strictEqual(hasCJKCharacters(''), false);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should return true for mixed text containing CJK', () => {
|
|
114
|
-
strictEqual(hasCJKCharacters('SOS 快照'), true);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe('renderTextWithLetterSpacing', () => {
|
|
119
|
-
it('should apply letterSpacing to each character x position (Issue #73 Bug1)', () => {
|
|
120
|
-
const fillCalls: Array<{ text: string; x: number; y: number }> = [];
|
|
121
|
-
const measureResults: Record<string, number> = { A: 10, B: 12, C: 8 };
|
|
122
|
-
|
|
123
|
-
const ctx = {
|
|
124
|
-
fillStyle: '',
|
|
125
|
-
font: '',
|
|
126
|
-
textBaseline: 'alphabetic' as CanvasTextBaseline,
|
|
127
|
-
fillText(text: string, x: number, y: number) {
|
|
128
|
-
fillCalls.push({ text, x, y });
|
|
129
|
-
},
|
|
130
|
-
measureText(text: string) {
|
|
131
|
-
return { width: measureResults[text] ?? 10 };
|
|
132
|
-
}
|
|
133
|
-
} as unknown as CanvasRenderingContext2D;
|
|
134
|
-
|
|
135
|
-
const deps: TextRendererDependencies = {
|
|
136
|
-
ctx,
|
|
137
|
-
context: createMockContext(),
|
|
138
|
-
options: { scale: 1 }
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const renderer = new TextRenderer(deps);
|
|
142
|
-
const bounds = new Bounds(100, 50, 200, 25);
|
|
143
|
-
const text = new TextBounds('ABC', bounds);
|
|
144
|
-
const letterSpacing = 5;
|
|
145
|
-
const baseline = 20;
|
|
146
|
-
|
|
147
|
-
renderer.renderTextWithLetterSpacing(text, letterSpacing, baseline);
|
|
148
|
-
|
|
149
|
-
// Verify letter spacing is added between characters
|
|
150
|
-
// A: x=100, B: x=100+10+5=115, C: x=115+12+5=132
|
|
151
|
-
strictEqual(fillCalls.length, 3);
|
|
152
|
-
strictEqual(fillCalls[0].text, 'A');
|
|
153
|
-
strictEqual(fillCalls[0].x, 100);
|
|
154
|
-
strictEqual(fillCalls[1].text, 'B');
|
|
155
|
-
strictEqual(fillCalls[1].x, 115); // 100 + measureText('A').width(10) + letterSpacing(5)
|
|
156
|
-
strictEqual(fillCalls[2].text, 'C');
|
|
157
|
-
strictEqual(fillCalls[2].x, 132); // 115 + measureText('B').width(12) + letterSpacing(5)
|
|
158
|
-
|
|
159
|
-
// Verify y position uses baseline
|
|
160
|
-
fillCalls.forEach((call) => {
|
|
161
|
-
strictEqual(call.y, bounds.top + baseline); // 50 + 20 = 70
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should use ideographic baseline for CJK characters (Issue #73 Bug2)', () => {
|
|
166
|
-
const baselineChanges: string[] = [];
|
|
167
|
-
let currentBaseline: CanvasTextBaseline = 'alphabetic';
|
|
168
|
-
|
|
169
|
-
const ctx = {
|
|
170
|
-
fillStyle: '',
|
|
171
|
-
font: '',
|
|
172
|
-
get textBaseline(): CanvasTextBaseline {
|
|
173
|
-
return currentBaseline;
|
|
174
|
-
},
|
|
175
|
-
set textBaseline(value: CanvasTextBaseline) {
|
|
176
|
-
currentBaseline = value;
|
|
177
|
-
baselineChanges.push(value);
|
|
178
|
-
},
|
|
179
|
-
fillText(_text: string, _x: number, _y: number) {},
|
|
180
|
-
measureText(_text: string) {
|
|
181
|
-
return { width: 25 };
|
|
182
|
-
}
|
|
183
|
-
} as unknown as CanvasRenderingContext2D;
|
|
184
|
-
|
|
185
|
-
const deps: TextRendererDependencies = {
|
|
186
|
-
ctx,
|
|
187
|
-
context: createMockContext(),
|
|
188
|
-
options: { scale: 1 }
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const renderer = new TextRenderer(deps);
|
|
192
|
-
const bounds = new Bounds(0, 0, 100, 25);
|
|
193
|
-
const text = new TextBounds('快照', bounds);
|
|
194
|
-
|
|
195
|
-
renderer.renderTextWithLetterSpacing(text, 10, 20);
|
|
196
|
-
|
|
197
|
-
// Should have switched to ideographic for each CJK char and restored
|
|
198
|
-
// Pattern: [ideographic, alphabetic, ideographic, alphabetic]
|
|
199
|
-
ok(baselineChanges.includes('ideographic'), 'should switch to ideographic baseline for CJK');
|
|
200
|
-
// Should restore alphabetic after each CJK char
|
|
201
|
-
const ideographicIdx = baselineChanges.indexOf('ideographic');
|
|
202
|
-
strictEqual(baselineChanges[ideographicIdx + 1], 'alphabetic');
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('should not change textBaseline for non-CJK characters', () => {
|
|
206
|
-
const baselineChanges: string[] = [];
|
|
207
|
-
let currentBaseline: CanvasTextBaseline = 'alphabetic';
|
|
208
|
-
|
|
209
|
-
const ctx = {
|
|
210
|
-
fillStyle: '',
|
|
211
|
-
font: '',
|
|
212
|
-
get textBaseline(): CanvasTextBaseline {
|
|
213
|
-
return currentBaseline;
|
|
214
|
-
},
|
|
215
|
-
set textBaseline(value: CanvasTextBaseline) {
|
|
216
|
-
currentBaseline = value;
|
|
217
|
-
baselineChanges.push(value);
|
|
218
|
-
},
|
|
219
|
-
fillText(_text: string, _x: number, _y: number) {},
|
|
220
|
-
measureText(_text: string) {
|
|
221
|
-
return { width: 10 };
|
|
222
|
-
}
|
|
223
|
-
} as unknown as CanvasRenderingContext2D;
|
|
224
|
-
|
|
225
|
-
const deps: TextRendererDependencies = {
|
|
226
|
-
ctx,
|
|
227
|
-
context: createMockContext(),
|
|
228
|
-
options: { scale: 1 }
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
const renderer = new TextRenderer(deps);
|
|
232
|
-
const bounds = new Bounds(0, 0, 100, 25);
|
|
233
|
-
const text = new TextBounds('ABC', bounds);
|
|
234
|
-
|
|
235
|
-
renderer.renderTextWithLetterSpacing(text, 5, 20);
|
|
236
|
-
|
|
237
|
-
// Should not switch to ideographic for Latin characters
|
|
238
|
-
ok(!baselineChanges.includes('ideographic'), 'should not switch to ideographic for Latin text');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should render whole string in one call when letterSpacing is 0', () => {
|
|
242
|
-
const fillCalls: Array<{ text: string; x: number; y: number }> = [];
|
|
243
|
-
|
|
244
|
-
const ctx = {
|
|
245
|
-
fillStyle: '',
|
|
246
|
-
font: '',
|
|
247
|
-
textBaseline: 'alphabetic' as CanvasTextBaseline,
|
|
248
|
-
fillText(text: string, x: number, y: number) {
|
|
249
|
-
fillCalls.push({ text, x, y });
|
|
250
|
-
},
|
|
251
|
-
measureText(_text: string) {
|
|
252
|
-
return { width: 30 };
|
|
253
|
-
}
|
|
254
|
-
} as unknown as CanvasRenderingContext2D;
|
|
255
|
-
|
|
256
|
-
const deps: TextRendererDependencies = {
|
|
257
|
-
ctx,
|
|
258
|
-
context: createMockContext(),
|
|
259
|
-
options: { scale: 1 }
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const renderer = new TextRenderer(deps);
|
|
263
|
-
const bounds = new Bounds(10, 20, 100, 25);
|
|
264
|
-
const text = new TextBounds('Hello', bounds);
|
|
265
|
-
|
|
266
|
-
renderer.renderTextWithLetterSpacing(text, 0, 22);
|
|
267
|
-
|
|
268
|
-
deepStrictEqual(fillCalls, [{ text: 'Hello', x: 10, y: 42 }]);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('should handle negative letterSpacing correctly', () => {
|
|
272
|
-
const fillCalls: Array<{ text: string; x: number }> = [];
|
|
273
|
-
|
|
274
|
-
const ctx = {
|
|
275
|
-
fillStyle: '',
|
|
276
|
-
font: '',
|
|
277
|
-
textBaseline: 'alphabetic' as CanvasTextBaseline,
|
|
278
|
-
fillText(text: string, x: number, _y: number) {
|
|
279
|
-
fillCalls.push({ text, x });
|
|
280
|
-
},
|
|
281
|
-
measureText(_text: string) {
|
|
282
|
-
return { width: 10 };
|
|
283
|
-
}
|
|
284
|
-
} as unknown as CanvasRenderingContext2D;
|
|
285
|
-
|
|
286
|
-
const deps: TextRendererDependencies = {
|
|
287
|
-
ctx,
|
|
288
|
-
context: createMockContext(),
|
|
289
|
-
options: { scale: 1 }
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
const renderer = new TextRenderer(deps);
|
|
293
|
-
const bounds = new Bounds(100, 0, 50, 20);
|
|
294
|
-
const text = new TextBounds('AB', bounds);
|
|
295
|
-
|
|
296
|
-
renderer.renderTextWithLetterSpacing(text, -3, 15);
|
|
297
|
-
|
|
298
|
-
// A: x=100, B: x=100 + 10 + (-3) = 107
|
|
299
|
-
strictEqual(fillCalls[0].x, 100);
|
|
300
|
-
strictEqual(fillCalls[1].x, 107);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it('should handle mixed CJK and Latin text with correct baseline per character', () => {
|
|
304
|
-
const baselineAtRender: Record<string, string> = {};
|
|
305
|
-
let currentBaseline: CanvasTextBaseline = 'alphabetic';
|
|
306
|
-
|
|
307
|
-
const ctx = {
|
|
308
|
-
fillStyle: '',
|
|
309
|
-
font: '',
|
|
310
|
-
get textBaseline(): CanvasTextBaseline {
|
|
311
|
-
return currentBaseline;
|
|
312
|
-
},
|
|
313
|
-
set textBaseline(value: CanvasTextBaseline) {
|
|
314
|
-
currentBaseline = value;
|
|
315
|
-
},
|
|
316
|
-
fillText(text: string, _x: number, _y: number) {
|
|
317
|
-
baselineAtRender[text] = currentBaseline;
|
|
318
|
-
},
|
|
319
|
-
measureText(_text: string) {
|
|
320
|
-
return { width: 12 };
|
|
321
|
-
}
|
|
322
|
-
} as unknown as CanvasRenderingContext2D;
|
|
323
|
-
|
|
324
|
-
const deps: TextRendererDependencies = {
|
|
325
|
-
ctx,
|
|
326
|
-
context: createMockContext(),
|
|
327
|
-
options: { scale: 1 }
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const renderer = new TextRenderer(deps);
|
|
331
|
-
const bounds = new Bounds(0, 0, 200, 25);
|
|
332
|
-
// Mixed: Latin 'A', CJK '快', Latin 'B'
|
|
333
|
-
const text = new TextBounds('A快B', bounds);
|
|
334
|
-
|
|
335
|
-
renderer.renderTextWithLetterSpacing(text, 5, 20);
|
|
336
|
-
|
|
337
|
-
strictEqual(baselineAtRender['A'], 'alphabetic', 'Latin char should use alphabetic baseline');
|
|
338
|
-
strictEqual(baselineAtRender['快'], 'ideographic', 'CJK char should use ideographic baseline');
|
|
339
|
-
strictEqual(baselineAtRender['B'], 'alphabetic', 'Latin char after CJK should restore alphabetic baseline');
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it('should handle empty string without errors', () => {
|
|
343
|
-
const ctx = {
|
|
344
|
-
fillStyle: '',
|
|
345
|
-
font: '',
|
|
346
|
-
textBaseline: 'alphabetic' as CanvasTextBaseline,
|
|
347
|
-
fillText(_text: string, _x: number, _y: number) {},
|
|
348
|
-
measureText(_text: string) {
|
|
349
|
-
return { width: 0 };
|
|
350
|
-
}
|
|
351
|
-
} as unknown as CanvasRenderingContext2D;
|
|
352
|
-
|
|
353
|
-
const deps: TextRendererDependencies = {
|
|
354
|
-
ctx,
|
|
355
|
-
context: createMockContext(),
|
|
356
|
-
options: { scale: 1 }
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
const renderer = new TextRenderer(deps);
|
|
360
|
-
const bounds = new Bounds(0, 0, 100, 25);
|
|
361
|
-
const text = new TextBounds('', bounds);
|
|
362
|
-
|
|
363
|
-
// Should not throw
|
|
364
|
-
renderer.renderTextWithLetterSpacing(text, 5, 20);
|
|
365
|
-
renderer.renderTextWithLetterSpacing(text, 0, 20);
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('should render CJK glyphs upright in vertical writing mode', () => {
|
|
369
|
-
const fillCalls: Array<{ text: string; x: number; y: number }> = [];
|
|
370
|
-
const baselineAtRender: Record<string, string> = {};
|
|
371
|
-
let currentBaseline: CanvasTextBaseline = 'alphabetic';
|
|
372
|
-
|
|
373
|
-
const ctx = {
|
|
374
|
-
fillStyle: '',
|
|
375
|
-
font: '',
|
|
376
|
-
get textBaseline(): CanvasTextBaseline {
|
|
377
|
-
return currentBaseline;
|
|
378
|
-
},
|
|
379
|
-
set textBaseline(value: CanvasTextBaseline) {
|
|
380
|
-
currentBaseline = value;
|
|
381
|
-
},
|
|
382
|
-
fillText(text: string, x: number, y: number) {
|
|
383
|
-
baselineAtRender[text] = currentBaseline;
|
|
384
|
-
fillCalls.push({ text, x, y });
|
|
385
|
-
},
|
|
386
|
-
measureText(_text: string) {
|
|
387
|
-
return { width: 20 };
|
|
388
|
-
},
|
|
389
|
-
save() {},
|
|
390
|
-
restore() {},
|
|
391
|
-
translate(_x: number, _y: number) {},
|
|
392
|
-
rotate(_angle: number) {}
|
|
393
|
-
} as unknown as CanvasRenderingContext2D;
|
|
394
|
-
|
|
395
|
-
const deps: TextRendererDependencies = {
|
|
396
|
-
ctx,
|
|
397
|
-
context: createMockContext(),
|
|
398
|
-
options: { scale: 1 }
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
const renderer = new TextRenderer(deps);
|
|
402
|
-
const bounds = new Bounds(100, 50, 24, 80);
|
|
403
|
-
const text = new TextBounds('縦書', bounds);
|
|
404
|
-
|
|
405
|
-
renderer.renderTextWithLetterSpacing(text, 4, 18, WRITING_MODE.VERTICAL_RL);
|
|
406
|
-
|
|
407
|
-
deepStrictEqual(fillCalls, [
|
|
408
|
-
{ text: '縦', x: 100, y: 68 },
|
|
409
|
-
{ text: '書', x: 100, y: 92 }
|
|
410
|
-
]);
|
|
411
|
-
strictEqual(baselineAtRender['縦'], 'ideographic');
|
|
412
|
-
strictEqual(baselineAtRender['書'], 'ideographic');
|
|
413
|
-
strictEqual(currentBaseline, 'alphabetic');
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
it('should rotate Latin glyphs in vertical writing mode', () => {
|
|
417
|
-
const operations: string[] = [];
|
|
418
|
-
const fillCalls: Array<{ text: string; x: number; y: number }> = [];
|
|
419
|
-
|
|
420
|
-
const ctx = {
|
|
421
|
-
fillStyle: '',
|
|
422
|
-
font: '',
|
|
423
|
-
textBaseline: 'alphabetic' as CanvasTextBaseline,
|
|
424
|
-
fillText(text: string, x: number, y: number) {
|
|
425
|
-
fillCalls.push({ text, x, y });
|
|
426
|
-
},
|
|
427
|
-
measureText(_text: string) {
|
|
428
|
-
return { width: 12 };
|
|
429
|
-
},
|
|
430
|
-
save() {
|
|
431
|
-
operations.push('save');
|
|
432
|
-
},
|
|
433
|
-
restore() {
|
|
434
|
-
operations.push('restore');
|
|
435
|
-
},
|
|
436
|
-
translate(x: number, y: number) {
|
|
437
|
-
operations.push(`translate:${x},${y}`);
|
|
438
|
-
},
|
|
439
|
-
rotate(angle: number) {
|
|
440
|
-
operations.push(`rotate:${angle}`);
|
|
441
|
-
}
|
|
442
|
-
} as unknown as CanvasRenderingContext2D;
|
|
443
|
-
|
|
444
|
-
const deps: TextRendererDependencies = {
|
|
445
|
-
ctx,
|
|
446
|
-
context: createMockContext(),
|
|
447
|
-
options: { scale: 1 }
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
const renderer = new TextRenderer(deps);
|
|
451
|
-
const bounds = new Bounds(40, 10, 24, 60);
|
|
452
|
-
const text = new TextBounds('AB', bounds);
|
|
453
|
-
|
|
454
|
-
renderer.renderTextWithLetterSpacing(text, 2, 16, WRITING_MODE.VERTICAL_RL);
|
|
455
|
-
|
|
456
|
-
deepStrictEqual(fillCalls, [
|
|
457
|
-
{ text: 'A', x: 0, y: 0 },
|
|
458
|
-
{ text: 'B', x: 0, y: 0 }
|
|
459
|
-
]);
|
|
460
|
-
deepStrictEqual(operations, [
|
|
461
|
-
'save',
|
|
462
|
-
'translate:56,10',
|
|
463
|
-
`rotate:${Math.PI / 2}`,
|
|
464
|
-
'restore',
|
|
465
|
-
'save',
|
|
466
|
-
'translate:56,24',
|
|
467
|
-
`rotate:${Math.PI / 2}`,
|
|
468
|
-
'restore'
|
|
469
|
-
]);
|
|
470
|
-
});
|
|
471
|
-
});
|