postext-pdf 1.8.6 → 1.9.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/README.md +1 -1
- package/dist/__tests__/accessibility.test.js +44 -0
- package/dist/__tests__/accessibility.test.js.map +1 -1
- package/dist/__tests__/chinesePageLabels.test.d.ts +2 -0
- package/dist/__tests__/chinesePageLabels.test.d.ts.map +1 -0
- package/dist/__tests__/chinesePageLabels.test.js +58 -0
- package/dist/__tests__/chinesePageLabels.test.js.map +1 -0
- package/dist/__tests__/cjkAnnotations.test.d.ts +2 -0
- package/dist/__tests__/cjkAnnotations.test.d.ts.map +1 -0
- package/dist/__tests__/cjkAnnotations.test.js +83 -0
- package/dist/__tests__/cjkAnnotations.test.js.map +1 -0
- package/dist/__tests__/cjkDashRule.test.d.ts +2 -0
- package/dist/__tests__/cjkDashRule.test.d.ts.map +1 -0
- package/dist/__tests__/cjkDashRule.test.js +101 -0
- package/dist/__tests__/cjkDashRule.test.js.map +1 -0
- package/dist/__tests__/cjkFontFiles.test.d.ts +2 -0
- package/dist/__tests__/cjkFontFiles.test.d.ts.map +1 -0
- package/dist/__tests__/cjkFontFiles.test.js +302 -0
- package/dist/__tests__/cjkFontFiles.test.js.map +1 -0
- package/dist/__tests__/cjkGrid.test.d.ts +2 -0
- package/dist/__tests__/cjkGrid.test.d.ts.map +1 -0
- package/dist/__tests__/cjkGrid.test.js +57 -0
- package/dist/__tests__/cjkGrid.test.js.map +1 -0
- package/dist/__tests__/cjkPunctuation.test.d.ts +2 -0
- package/dist/__tests__/cjkPunctuation.test.d.ts.map +1 -0
- package/dist/__tests__/cjkPunctuation.test.js +143 -0
- package/dist/__tests__/cjkPunctuation.test.js.map +1 -0
- package/dist/__tests__/cjkTracking.test.d.ts +2 -0
- package/dist/__tests__/cjkTracking.test.d.ts.map +1 -0
- package/dist/__tests__/cjkTracking.test.js +100 -0
- package/dist/__tests__/cjkTracking.test.js.map +1 -0
- package/dist/__tests__/documentLanguage.test.js +13 -3
- package/dist/__tests__/documentLanguage.test.js.map +1 -1
- package/dist/__tests__/fallbackSpaces.test.js +49 -0
- package/dist/__tests__/fallbackSpaces.test.js.map +1 -1
- package/dist/__tests__/outlineTitles.test.js +87 -0
- package/dist/__tests__/outlineTitles.test.js.map +1 -1
- package/dist/__tests__/pdfWorker.test.js +17 -0
- package/dist/__tests__/pdfWorker.test.js.map +1 -1
- package/dist/__tests__/renderOptionsDocs.test.d.ts +2 -0
- package/dist/__tests__/renderOptionsDocs.test.d.ts.map +1 -0
- package/dist/__tests__/renderOptionsDocs.test.js +32 -0
- package/dist/__tests__/renderOptionsDocs.test.js.map +1 -0
- package/dist/__tests__/rightBinding.test.d.ts +2 -0
- package/dist/__tests__/rightBinding.test.d.ts.map +1 -0
- package/dist/__tests__/rightBinding.test.js +112 -0
- package/dist/__tests__/rightBinding.test.js.map +1 -0
- package/dist/__tests__/verticalDesignImage.test.d.ts +2 -0
- package/dist/__tests__/verticalDesignImage.test.d.ts.map +1 -0
- package/dist/__tests__/verticalDesignImage.test.js +152 -0
- package/dist/__tests__/verticalDesignImage.test.js.map +1 -0
- package/dist/__tests__/verticalText.test.d.ts +2 -0
- package/dist/__tests__/verticalText.test.d.ts.map +1 -0
- package/dist/__tests__/verticalText.test.js +380 -0
- package/dist/__tests__/verticalText.test.js.map +1 -0
- package/dist/faceFiles.d.ts +70 -0
- package/dist/faceFiles.d.ts.map +1 -0
- package/dist/faceFiles.js +154 -0
- package/dist/faceFiles.js.map +1 -0
- package/dist/fontCache.d.ts +101 -7
- package/dist/fontCache.d.ts.map +1 -1
- package/dist/fontCache.js +178 -26
- package/dist/fontCache.js.map +1 -1
- package/dist/fontFamilies.d.ts +12 -0
- package/dist/fontFamilies.d.ts.map +1 -0
- package/dist/fontFamilies.js +12 -0
- package/dist/fontFamilies.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pdf-backend/annotations.d.ts +29 -0
- package/dist/pdf-backend/annotations.d.ts.map +1 -0
- package/dist/pdf-backend/annotations.js +133 -0
- package/dist/pdf-backend/annotations.js.map +1 -0
- package/dist/pdf-backend/blockRender.d.ts.map +1 -1
- package/dist/pdf-backend/blockRender.js +160 -30
- package/dist/pdf-backend/blockRender.js.map +1 -1
- package/dist/pdf-backend/fallbackSpaces.d.ts +21 -2
- package/dist/pdf-backend/fallbackSpaces.d.ts.map +1 -1
- package/dist/pdf-backend/fallbackSpaces.js +43 -6
- package/dist/pdf-backend/fallbackSpaces.js.map +1 -1
- package/dist/pdf-backend/fontHelpers.d.ts +12 -0
- package/dist/pdf-backend/fontHelpers.d.ts.map +1 -1
- package/dist/pdf-backend/fontHelpers.js +49 -11
- package/dist/pdf-backend/fontHelpers.js.map +1 -1
- package/dist/pdf-backend/headerFooter.d.ts +8 -3
- package/dist/pdf-backend/headerFooter.d.ts.map +1 -1
- package/dist/pdf-backend/headerFooter.js +74 -16
- package/dist/pdf-backend/headerFooter.js.map +1 -1
- package/dist/pdf-backend/index.d.ts +74 -8
- package/dist/pdf-backend/index.d.ts.map +1 -1
- package/dist/pdf-backend/index.js +121 -25
- package/dist/pdf-backend/index.js.map +1 -1
- package/dist/pdf-backend/inkScale.d.ts +12 -0
- package/dist/pdf-backend/inkScale.d.ts.map +1 -0
- package/dist/pdf-backend/inkScale.js +16 -0
- package/dist/pdf-backend/inkScale.js.map +1 -0
- package/dist/pdf-backend/outlines.d.ts +12 -1
- package/dist/pdf-backend/outlines.d.ts.map +1 -1
- package/dist/pdf-backend/outlines.js +46 -16
- package/dist/pdf-backend/outlines.js.map +1 -1
- package/dist/pdf-backend/pageDecorations.d.ts +5 -1
- package/dist/pdf-backend/pageDecorations.d.ts.map +1 -1
- package/dist/pdf-backend/pageDecorations.js +22 -1
- package/dist/pdf-backend/pageDecorations.js.map +1 -1
- package/dist/pdf-backend/pageLabels.d.ts +3 -1
- package/dist/pdf-backend/pageLabels.d.ts.map +1 -1
- package/dist/pdf-backend/pageLabels.js +12 -6
- package/dist/pdf-backend/pageLabels.js.map +1 -1
- package/dist/pdf-backend/primitives.d.ts +153 -6
- package/dist/pdf-backend/primitives.d.ts.map +1 -1
- package/dist/pdf-backend/primitives.js +269 -28
- package/dist/pdf-backend/primitives.js.map +1 -1
- package/dist/pdf-backend/renderResourceBlock.d.ts +4 -0
- package/dist/pdf-backend/renderResourceBlock.d.ts.map +1 -1
- package/dist/pdf-backend/renderResourceBlock.js +105 -26
- package/dist/pdf-backend/renderResourceBlock.js.map +1 -1
- package/dist/pdf-backend/structureFlow.d.ts +5 -0
- package/dist/pdf-backend/structureFlow.d.ts.map +1 -1
- package/dist/pdf-backend/structureFlow.js +8 -0
- package/dist/pdf-backend/structureFlow.js.map +1 -1
- package/dist/pdf-backend/svgVector.d.ts.map +1 -1
- package/dist/pdf-backend/svgVector.js +13 -2
- package/dist/pdf-backend/svgVector.js.map +1 -1
- package/dist/pdf-backend/tagging.d.ts +8 -1
- package/dist/pdf-backend/tagging.d.ts.map +1 -1
- package/dist/pdf-backend/tagging.js +7 -1
- package/dist/pdf-backend/tagging.js.map +1 -1
- package/dist/pdf-backend/verticalText.d.ts +43 -0
- package/dist/pdf-backend/verticalText.d.ts.map +1 -0
- package/dist/pdf-backend/verticalText.js +284 -0
- package/dist/pdf-backend/verticalText.js.map +1 -0
- package/dist/verticalFonts.d.ts +75 -0
- package/dist/verticalFonts.d.ts.map +1 -0
- package/dist/verticalFonts.js +136 -0
- package/dist/verticalFonts.js.map +1 -0
- package/dist/worker/client.d.ts +1 -1
- package/dist/worker/client.d.ts.map +1 -1
- package/dist/worker/client.js +7 -5
- package/dist/worker/client.js.map +1 -1
- package/dist/worker/pdf.worker.js +8 -4
- package/dist/worker/pdf.worker.js.map +1 -1
- package/dist/worker/protocol.d.ts +10 -5
- package/dist/worker/protocol.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { PDFArray, PDFDict, PDFDocument, PDFName, PDFRawStream, PDFRef, PDFStream, decodePDFRawStream } from 'pdf-lib';
|
|
4
|
+
import fontkit from '@pdf-lib/fontkit';
|
|
5
|
+
import { buildDocument, bundleFontProvider } from 'postext';
|
|
6
|
+
import { renderToPdf } from '../pdf-backend';
|
|
7
|
+
import { FontCache } from '../fontCache';
|
|
8
|
+
import { parseFontString } from '../fontString';
|
|
9
|
+
import { textShows } from '../pdf-backend/primitives';
|
|
10
|
+
// Issue #196: a face made of several files (Fontsource's unicode-range
|
|
11
|
+
// slices of Noto Serif TC), picked per character, each embedded as a subset
|
|
12
|
+
// of the glyphs drawn from it; characters no file has are reported.
|
|
13
|
+
const FIXTURES = new URL('./fixtures/cjk/', import.meta.url);
|
|
14
|
+
const read = (name) => new Uint8Array(fs.readFileSync(new URL(name, FIXTURES)));
|
|
15
|
+
/** Slice file → unicode-range, in the order the Fontsource CSS declares them. */
|
|
16
|
+
const SLICES = JSON.parse(fs.readFileSync(new URL('slices.json', FIXTURES), 'utf-8'));
|
|
17
|
+
function inRange(range, cp) {
|
|
18
|
+
return range.split(',').some((part) => {
|
|
19
|
+
const [lo, hi] = part.trim().slice(2).split('-');
|
|
20
|
+
return cp >= parseInt(lo, 16) && cp <= parseInt(hi ?? lo, 16);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** The slices a provider hands over for `codePoints`: those whose range
|
|
24
|
+
* holds one of them, the last declared first (CSS font matching). */
|
|
25
|
+
function slicesFor(codePoints) {
|
|
26
|
+
const names = Object.keys(SLICES).reverse();
|
|
27
|
+
const out = new Set();
|
|
28
|
+
for (const cp of codePoints) {
|
|
29
|
+
const hit = names.find((name) => inRange(SLICES[name], cp));
|
|
30
|
+
if (hit)
|
|
31
|
+
out.add(hit);
|
|
32
|
+
}
|
|
33
|
+
return names.filter((name) => out.has(name));
|
|
34
|
+
}
|
|
35
|
+
const faces = new Map();
|
|
36
|
+
const faceOf = (name) => {
|
|
37
|
+
let face = faces.get(name);
|
|
38
|
+
if (!face)
|
|
39
|
+
faces.set(name, (face = fontkit.create(Buffer.from(read(name)))));
|
|
40
|
+
return face;
|
|
41
|
+
};
|
|
42
|
+
const latin = fontkit.create(fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url)));
|
|
43
|
+
/** Measures with the slice that has each character (the way the browser
|
|
44
|
+
* sets a unicode-range face), Latin with Lora. */
|
|
45
|
+
class StubCtx {
|
|
46
|
+
font = '';
|
|
47
|
+
measureText(s) {
|
|
48
|
+
const sizePx = parseFontString(this.font)?.sizePx ?? 16;
|
|
49
|
+
let units = 0;
|
|
50
|
+
for (const ch of s) {
|
|
51
|
+
const cp = ch.codePointAt(0);
|
|
52
|
+
const name = cp < 0x2e80 ? undefined : slicesFor([cp])[0];
|
|
53
|
+
const face = name ? faceOf(name) : latin;
|
|
54
|
+
units += (face.glyphForCodePoint(cp).advanceWidth / face.unitsPerEm) * 1000;
|
|
55
|
+
}
|
|
56
|
+
return { width: (units / 1000) * sizePx };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
globalThis.OffscreenCanvas = class {
|
|
60
|
+
getContext() {
|
|
61
|
+
return new StubCtx();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const pt = (value) => ({ value, unit: 'pt' });
|
|
65
|
+
const config = {
|
|
66
|
+
page: { width: pt(260), height: pt(360), margins: { top: pt(24), bottom: pt(24), left: pt(24), right: pt(24) } },
|
|
67
|
+
bodyText: { fontFamily: 'Noto Serif TC', fontSize: pt(11), hyphenation: { enabled: false } },
|
|
68
|
+
headings: { fontFamily: 'Noto Serif TC', levels: [{ level: 1, breakBefore: { enabled: false } }] },
|
|
69
|
+
header: { elements: [] },
|
|
70
|
+
footer: { elements: [] },
|
|
71
|
+
};
|
|
72
|
+
const TITLE = '紅樓夢第一回';
|
|
73
|
+
const PARAGRAPH = '此開卷第一回也。作者自云:因曾歷過一番夢幻之後,故將真事隱去,而借「通靈」之說,撰此《石頭記》一書也。';
|
|
74
|
+
const MARKDOWN = `# ${TITLE}\n\n${PARAGRAPH}\n`;
|
|
75
|
+
/** Every text the pages show, font by font: the glyph ids of each `Tj` /
|
|
76
|
+
* `TJ` string mapped back to characters through its font's ToUnicode. */
|
|
77
|
+
async function shownText(bytes) {
|
|
78
|
+
const pdf = await PDFDocument.load(bytes);
|
|
79
|
+
let text = '';
|
|
80
|
+
let notdef = 0;
|
|
81
|
+
const fonts = new Set();
|
|
82
|
+
for (const page of pdf.getPages()) {
|
|
83
|
+
const resources = page.node.Resources().lookup(PDFName.of('Font'), PDFDict);
|
|
84
|
+
const cmaps = new Map();
|
|
85
|
+
for (const [name, ref] of resources.entries()) {
|
|
86
|
+
const font = pdf.context.lookup(ref, PDFDict);
|
|
87
|
+
const stream = pdf.context.lookup(font.get(PDFName.of('ToUnicode')));
|
|
88
|
+
const map = new Map();
|
|
89
|
+
if (stream instanceof PDFRawStream) {
|
|
90
|
+
const cmap = new TextDecoder('latin1').decode(decodePDFRawStream(stream).decode());
|
|
91
|
+
for (const m of cmap.matchAll(/<([0-9a-f]{4})> <([0-9a-f]+)>/gi)) {
|
|
92
|
+
const units = m[2].match(/.{4}/g).map((h) => parseInt(h, 16));
|
|
93
|
+
map.set(m[1].toLowerCase(), String.fromCharCode(...units));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
cmaps.set(name.asString(), map);
|
|
97
|
+
}
|
|
98
|
+
const contents = page.node.Contents();
|
|
99
|
+
const streams = contents instanceof PDFArray ? contents.asArray() : [contents];
|
|
100
|
+
let content = '';
|
|
101
|
+
for (const ref of streams) {
|
|
102
|
+
const stream = ref instanceof PDFRef ? pdf.context.lookup(ref) : ref;
|
|
103
|
+
if (stream instanceof PDFRawStream)
|
|
104
|
+
content += new TextDecoder('latin1').decode(decodePDFRawStream(stream).decode());
|
|
105
|
+
else if (stream instanceof PDFStream)
|
|
106
|
+
content += new TextDecoder('latin1').decode(stream.getContents());
|
|
107
|
+
}
|
|
108
|
+
let current = '';
|
|
109
|
+
// The `/ActualText` of a composed line is no shown text.
|
|
110
|
+
for (const m of content.replace(/\/ActualText\s*<[0-9A-Fa-f]*>/g, '').matchAll(/(\/[^\s/]+)\s+[\d.]+\s+Tf|<([0-9A-Fa-f]*)>|\]\s*TJ/g)) {
|
|
111
|
+
if (m[1]) {
|
|
112
|
+
current = m[1];
|
|
113
|
+
fonts.add(current);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (m[2] === undefined)
|
|
117
|
+
continue;
|
|
118
|
+
for (const cid of m[2].toLowerCase().match(/.{4}/g) ?? []) {
|
|
119
|
+
if (cid === '0000')
|
|
120
|
+
notdef++;
|
|
121
|
+
text += cmaps.get(current)?.get(cid) ?? '�';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { text, notdef, fonts };
|
|
126
|
+
}
|
|
127
|
+
/** The Type0 fonts written to the file, with the size of each font program. */
|
|
128
|
+
async function embeddedFonts(bytes) {
|
|
129
|
+
const pdf = await PDFDocument.load(bytes);
|
|
130
|
+
const sizes = [];
|
|
131
|
+
for (const [, obj] of pdf.context.enumerateIndirectObjects()) {
|
|
132
|
+
if (!(obj instanceof PDFDict) || obj.get(PDFName.of('Subtype'))?.toString() !== '/Type0')
|
|
133
|
+
continue;
|
|
134
|
+
const cid = pdf.context.lookup((obj.lookup(PDFName.of('DescendantFonts'), PDFArray)).get(0), PDFDict);
|
|
135
|
+
const descriptor = cid.lookup(PDFName.of('FontDescriptor'), PDFDict);
|
|
136
|
+
const program = descriptor.lookup(PDFName.of('FontFile2'));
|
|
137
|
+
sizes.push(program.getContents().length);
|
|
138
|
+
}
|
|
139
|
+
return sizes;
|
|
140
|
+
}
|
|
141
|
+
function sliceProvider(asked) {
|
|
142
|
+
return async (_family, weight, _style, request) => {
|
|
143
|
+
const codePoints = [...(request?.codePoints ?? [])];
|
|
144
|
+
asked.push({ weight, codePoints });
|
|
145
|
+
return slicesFor(codePoints.length > 0 ? codePoints : [0x20]).map(read);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const HAN_AND_MARKS = new Set([...TITLE + PARAGRAPH]);
|
|
149
|
+
describe('faces made of several files (#196)', () => {
|
|
150
|
+
it('draws every character of 紅樓夢第一回 from the slice that has it', async () => {
|
|
151
|
+
const doc = buildDocument({ markdown: MARKDOWN }, config);
|
|
152
|
+
const asked = [];
|
|
153
|
+
const warnings = [];
|
|
154
|
+
const bytes = await renderToPdf(doc, { fontProvider: sliceProvider(asked), onWarning: (w) => warnings.push(w), accessible: false });
|
|
155
|
+
const shown = await shownText(bytes);
|
|
156
|
+
expect(shown.notdef).toBe(0);
|
|
157
|
+
expect(shown.text.replace(/\s/g, '')).toBe((TITLE + PARAGRAPH).replace(/\s/g, ''));
|
|
158
|
+
// The provider heard of every character, per face (heading 700, body 400).
|
|
159
|
+
for (const request of asked) {
|
|
160
|
+
for (const cp of request.codePoints)
|
|
161
|
+
expect(HAN_AND_MARKS.has(String.fromCodePoint(cp)) || cp === 0x20).toBe(true);
|
|
162
|
+
}
|
|
163
|
+
expect(warnings.filter((w) => w.kind === 'missingGlyph')).toEqual([]);
|
|
164
|
+
});
|
|
165
|
+
it('embeds only the slices drawn from, each as a subset', async () => {
|
|
166
|
+
const doc = buildDocument({ markdown: `${PARAGRAPH}\n` }, config);
|
|
167
|
+
// A provider handing over every slice it has, used or not.
|
|
168
|
+
const all = Object.keys(SLICES).reverse();
|
|
169
|
+
const bytes = await renderToPdf(doc, { fontProvider: async () => all.map(read), accessible: false });
|
|
170
|
+
const used = slicesFor([...PARAGRAPH].map((c) => c.codePointAt(0)));
|
|
171
|
+
const sizes = await embeddedFonts(bytes);
|
|
172
|
+
// The latin slice (first, the face's own font) and the unused one stay out.
|
|
173
|
+
expect(sizes).toHaveLength(used.length);
|
|
174
|
+
const largest = Math.max(...used.map((name) => read(name).length));
|
|
175
|
+
for (const size of sizes)
|
|
176
|
+
expect(size).toBeLessThanOrEqual(largest);
|
|
177
|
+
const shown = await shownText(bytes);
|
|
178
|
+
expect(shown.fonts.size).toBe(used.length);
|
|
179
|
+
expect(shown.notdef).toBe(0);
|
|
180
|
+
});
|
|
181
|
+
it('reports the characters the named chinese-traditional subset lacks, and still builds', async () => {
|
|
182
|
+
const doc = buildDocument({ markdown: `${PARAGRAPH}(甄士隱)!?\n` }, config);
|
|
183
|
+
const warnings = [];
|
|
184
|
+
const bytes = await renderToPdf(doc, {
|
|
185
|
+
fontProvider: async () => read('noto-serif-tc-chinese-traditional.ttf'),
|
|
186
|
+
onWarning: (w) => warnings.push(w),
|
|
187
|
+
});
|
|
188
|
+
expect(new TextDecoder().decode(bytes.slice(0, 5))).toBe('%PDF-');
|
|
189
|
+
const missing = warnings.filter((w) => w.kind === 'missingGlyph');
|
|
190
|
+
expect(missing).toHaveLength(1);
|
|
191
|
+
const [m] = missing;
|
|
192
|
+
if (m?.kind !== 'missingGlyph')
|
|
193
|
+
throw new Error('no missingGlyph');
|
|
194
|
+
expect(m).toMatchObject({ family: 'Noto Serif TC', weight: 400, style: 'normal' });
|
|
195
|
+
for (const ch of [',', ':', '(', ')', '!', '?'])
|
|
196
|
+
expect(m.characters).toContain(ch);
|
|
197
|
+
// Only the marks: every ideograph is in the named subset.
|
|
198
|
+
expect(m.characters.every((ch) => !/\p{Script=Han}/u.test(ch))).toBe(true);
|
|
199
|
+
expect(m.message).toContain('Noto Serif TC');
|
|
200
|
+
expect(m.message).toContain(',');
|
|
201
|
+
});
|
|
202
|
+
it('asks a sliced face again only for the characters its files lack', async () => {
|
|
203
|
+
const pdfDoc = await PDFDocument.create();
|
|
204
|
+
pdfDoc.registerFontkit(fontkit);
|
|
205
|
+
const asked = [];
|
|
206
|
+
const cache = new FontCache(pdfDoc, sliceProvider(asked));
|
|
207
|
+
const fs = '400 16px "Noto Serif TC"';
|
|
208
|
+
const cps = (s) => new Set([...s].map((c) => c.codePointAt(0)));
|
|
209
|
+
await cache.preloadFontStrings(new Map([[fs, cps('紅樓夢')]]));
|
|
210
|
+
await cache.preloadFontStrings(new Map([[fs, cps('紅樓夢第一回')]]));
|
|
211
|
+
await cache.preloadFontStrings(new Map([[fs, cps('第一回')]]));
|
|
212
|
+
expect(asked.map((a) => a.codePoints.map((cp) => String.fromCodePoint(cp)).join(''))).toEqual(['紅樓夢', '第一回']);
|
|
213
|
+
expect(cache.get(fs)).not.toBeNull();
|
|
214
|
+
});
|
|
215
|
+
it('lists the missing characters in the order they were set, whichever file drew them', async () => {
|
|
216
|
+
// 𠀀 follows : (slice 123) and is drawn as that file's .notdef; 𪚥
|
|
217
|
+
// opens the next line and is drawn in the face's first file (latin).
|
|
218
|
+
// Listed file by file, 𪚥 came first.
|
|
219
|
+
const pdfDoc = await PDFDocument.create();
|
|
220
|
+
pdfDoc.registerFontkit(fontkit);
|
|
221
|
+
const cache = new FontCache(pdfDoc, async () => ['noto-serif-tc-latin.ttf', 'noto-serif-tc-123.ttf'].map(read));
|
|
222
|
+
const fs = '400 16px "Noto Serif TC"';
|
|
223
|
+
await cache.preloadFontStrings([fs]);
|
|
224
|
+
const font = cache.get(fs);
|
|
225
|
+
expect(textShows(font, '一:\u{20000}').map((show) => show.font)).not.toContain(font);
|
|
226
|
+
expect(textShows(font, '\u{2A6A5}').map((show) => show.font)).toEqual([font]);
|
|
227
|
+
expect(cache.missingGlyphs()).toEqual([
|
|
228
|
+
{ family: 'Noto Serif TC', weight: 400, style: 'normal', characters: ['\u{20000}', '\u{2A6A5}'] },
|
|
229
|
+
]);
|
|
230
|
+
});
|
|
231
|
+
it('reaches a bundle\'s fallback through bundleFontProvider, request and all', async () => {
|
|
232
|
+
// A bundle that does not embed its Chinese family: the fallback gets the
|
|
233
|
+
// face's characters and answers with slices, as a PdfFontProvider does.
|
|
234
|
+
const fallback = sliceProvider([]);
|
|
235
|
+
const fontProvider = bundleFontProvider({ fonts: [] }, { fallback });
|
|
236
|
+
const doc = buildDocument({ markdown: `${PARAGRAPH}\n` }, config);
|
|
237
|
+
const warnings = [];
|
|
238
|
+
const bytes = await renderToPdf(doc, { fontProvider, onWarning: (w) => warnings.push(w), accessible: false });
|
|
239
|
+
const shown = await shownText(bytes);
|
|
240
|
+
expect(shown.notdef).toBe(0);
|
|
241
|
+
expect(shown.text.replace(/\s/g, '')).toBe(PARAGRAPH);
|
|
242
|
+
expect(warnings.filter((w) => w.kind === 'missingGlyph')).toEqual([]);
|
|
243
|
+
});
|
|
244
|
+
it('asks a single-file face once, whatever the characters', async () => {
|
|
245
|
+
const pdfDoc = await PDFDocument.create();
|
|
246
|
+
pdfDoc.registerFontkit(fontkit);
|
|
247
|
+
let calls = 0;
|
|
248
|
+
const cache = new FontCache(pdfDoc, async () => {
|
|
249
|
+
calls++;
|
|
250
|
+
return read('noto-serif-tc-chinese-traditional.ttf');
|
|
251
|
+
});
|
|
252
|
+
const fs = '400 16px "Noto Serif TC"';
|
|
253
|
+
await cache.preloadFontStrings(new Map([[fs, new Set([0x7d05])]]));
|
|
254
|
+
await cache.preloadFontStrings(new Map([[fs, new Set([0x4e00, 0xff0c])]]));
|
|
255
|
+
expect(calls).toBe(1);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
describe('font file reports (#196)', () => {
|
|
259
|
+
it('warns when a variable font is asked for at a weight other than its default instance', async () => {
|
|
260
|
+
// Cormorant Garamond's variable font defaults to weight 300.
|
|
261
|
+
const cormorant = new Uint8Array(fs.readFileSync(new URL('../../../../apps/web/public/fonts/CormorantGaramond.ttf', import.meta.url)));
|
|
262
|
+
const doc = buildDocument({ markdown: 'Light text with a **bold** word.' }, { ...config, bodyText: { fontFamily: 'Cormorant Garamond', fontWeight: 300, boldFontWeight: 700 } });
|
|
263
|
+
const warnings = [];
|
|
264
|
+
await renderToPdf(doc, { fontProvider: async () => cormorant, onWarning: (w) => warnings.push(w) });
|
|
265
|
+
const variable = warnings.filter((w) => w.kind === 'variableFontDefaultInstance');
|
|
266
|
+
expect(variable).toEqual([
|
|
267
|
+
expect.objectContaining({ kind: 'variableFontDefaultInstance', family: 'Cormorant Garamond', weight: 700, style: 'normal', defaultWeight: 300 }),
|
|
268
|
+
]);
|
|
269
|
+
});
|
|
270
|
+
it('reports a CFF face over the size limit, embedded whole', async () => {
|
|
271
|
+
const cff = read('../tiny-cff.otf');
|
|
272
|
+
expect(new TextDecoder().decode(cff.slice(0, 4))).toBe('OTTO');
|
|
273
|
+
const pdfDoc = await PDFDocument.create();
|
|
274
|
+
pdfDoc.registerFontkit(fontkit);
|
|
275
|
+
const issues = [];
|
|
276
|
+
const cache = new FontCache(pdfDoc, async () => cff, { onFileIssue: (i) => issues.push(i), cffWarnBytes: 1000 });
|
|
277
|
+
await cache.preloadFontStrings(['400 16px "Tiny CFF"']);
|
|
278
|
+
expect(issues).toEqual([{ kind: 'cffEmbeddedWhole', family: 'Tiny CFF', weight: 400, style: 'normal', bytes: cff.length }]);
|
|
279
|
+
});
|
|
280
|
+
it('stays quiet about a CFF face under the limit', async () => {
|
|
281
|
+
const cff = read('../tiny-cff.otf');
|
|
282
|
+
const doc = buildDocument({ markdown: 'Tiny CFF abc' }, { ...config, bodyText: { fontFamily: 'Tiny CFF' } });
|
|
283
|
+
const warnings = [];
|
|
284
|
+
await renderToPdf(doc, { fontProvider: async () => cff, onWarning: (w) => warnings.push(w) });
|
|
285
|
+
expect(warnings.filter((w) => w.kind === 'cffEmbeddedWhole')).toEqual([]);
|
|
286
|
+
});
|
|
287
|
+
it('keeps the one-file provider as it was: one font per face, no request needed', async () => {
|
|
288
|
+
const lora = new Uint8Array(fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url)));
|
|
289
|
+
const doc = buildDocument({ markdown: 'Plain Latin text.' }, { ...config, bodyText: { fontFamily: 'Lora' } });
|
|
290
|
+
const requests = [];
|
|
291
|
+
const bytes = await renderToPdf(doc, {
|
|
292
|
+
fontProvider: async (_f, _w, _s, request) => {
|
|
293
|
+
requests.push(request);
|
|
294
|
+
return lora;
|
|
295
|
+
},
|
|
296
|
+
accessible: false,
|
|
297
|
+
});
|
|
298
|
+
expect(await embeddedFonts(bytes)).toHaveLength(1);
|
|
299
|
+
expect([...requests[0].codePoints].map((cp) => String.fromCodePoint(cp)).sort().join('')).toBe(' .LPaeilntx');
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
//# sourceMappingURL=cjkFontFiles.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cjkFontFiles.test.js","sourceRoot":"","sources":["../../src/__tests__/cjkFontFiles.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cjkGrid.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cjkGrid.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { PDFArray, PDFDocument, PDFRawStream, decodePDFRawStream } from 'pdf-lib';
|
|
4
|
+
import { buildDocument } from 'postext';
|
|
5
|
+
import { renderToPdf } from '../pdf-backend';
|
|
6
|
+
// The character grid (`cjk.grid.show`, #187) is a screen aid: a PDF draws
|
|
7
|
+
// it only when the render asks for it.
|
|
8
|
+
const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
|
|
9
|
+
const fontProvider = async () => new Uint8Array(fontBytes);
|
|
10
|
+
class StubCtx {
|
|
11
|
+
font = '';
|
|
12
|
+
letterSpacing = '0px';
|
|
13
|
+
measureText(s) {
|
|
14
|
+
let w = 0;
|
|
15
|
+
for (const ch of s)
|
|
16
|
+
w += ch.codePointAt(0) >= 0x2e80 ? 10.5 : 5.25;
|
|
17
|
+
return { width: w };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
globalThis.OffscreenCanvas = class {
|
|
21
|
+
getContext() {
|
|
22
|
+
return new StubCtx();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const mm = (value) => ({ value, unit: 'mm' });
|
|
26
|
+
const pt = (value) => ({ value, unit: 'pt' });
|
|
27
|
+
const config = {
|
|
28
|
+
locale: 'zh-Hans',
|
|
29
|
+
page: { width: mm(140), height: mm(203), dpi: 72, margins: { top: mm(18), bottom: mm(20), left: mm(16), right: mm(20) } },
|
|
30
|
+
layout: { layoutType: 'single' },
|
|
31
|
+
header: { elements: [] },
|
|
32
|
+
footer: { elements: [] },
|
|
33
|
+
bodyText: { fontFamily: 'Lora', fontSize: pt(10.5), lineHeight: pt(16.5), textAlign: 'justify', hyphenation: { enabled: false } },
|
|
34
|
+
cjk: { grid: { enabled: true, charsPerLine: 28, linesPerPage: 28, show: true } },
|
|
35
|
+
};
|
|
36
|
+
async function contentOf(doc, characterGrid) {
|
|
37
|
+
const pdf = await PDFDocument.load(await renderToPdf(doc, { fontProvider, accessible: false, ...(characterGrid ? { characterGrid } : {}) }));
|
|
38
|
+
const contents = pdf.getPage(0).node.Contents();
|
|
39
|
+
const refs = contents instanceof PDFArray ? contents.asArray() : [contents];
|
|
40
|
+
return refs
|
|
41
|
+
.map((ref) => {
|
|
42
|
+
const s = pdf.context.lookup(ref);
|
|
43
|
+
return s instanceof PDFRawStream ? new TextDecoder('latin1').decode(decodePDFRawStream(s).decode()) : '';
|
|
44
|
+
})
|
|
45
|
+
.join('\n');
|
|
46
|
+
}
|
|
47
|
+
describe('character grid in the PDF', () => {
|
|
48
|
+
it('is left out unless the render asks for it', async () => {
|
|
49
|
+
const doc = buildDocument({ markdown: '此开卷第一回也。作者自云:因曾历过一番梦幻之后,故将真事隐去。' }, config);
|
|
50
|
+
const moves = (content) => (content.match(/ m\n/g) ?? []).length;
|
|
51
|
+
const plain = await contentOf(doc);
|
|
52
|
+
const drawn = await contentOf(doc, true);
|
|
53
|
+
// 28 rows of 2 edges and 29 cell walls.
|
|
54
|
+
expect(moves(drawn) - moves(plain)).toBe(28 * 31);
|
|
55
|
+
}, 60_000);
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=cjkGrid.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cjkGrid.test.js","sourceRoot":"","sources":["../../src/__tests__/cjkGrid.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cjkPunctuation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cjkPunctuation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { PDFArray, PDFDocument, PDFRawStream, decodePDFRawStream } from 'pdf-lib';
|
|
4
|
+
import { buildDocument, columnClipRect } from 'postext';
|
|
5
|
+
import { renderToPdf } from '../pdf-backend';
|
|
6
|
+
// Punctuation widths, hanging marks and the Han–Latin space (#185, #186) in
|
|
7
|
+
// the PDF: a compressed mark is painted where its ink belongs, a hung mark
|
|
8
|
+
// is not clipped, and a line set in pieces reads as written.
|
|
9
|
+
const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
|
|
10
|
+
const fontProvider = async () => new Uint8Array(fontBytes);
|
|
11
|
+
// CJK characters 16 px, a space 4 px, anything else 8 px.
|
|
12
|
+
class StubCtx {
|
|
13
|
+
font = '';
|
|
14
|
+
letterSpacing = '0px';
|
|
15
|
+
measureText(s) {
|
|
16
|
+
let w = 0;
|
|
17
|
+
for (const ch of s)
|
|
18
|
+
w += ch === ' ' ? 4 : ch.codePointAt(0) >= 0x2e80 ? 16 : 8;
|
|
19
|
+
return { width: w };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
globalThis.OffscreenCanvas = class {
|
|
23
|
+
getContext() {
|
|
24
|
+
return new StubCtx();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const pt = (value) => ({ value, unit: 'pt' });
|
|
28
|
+
// 72 dpi: 1 pt = 1 px; an 80 px measure holds five characters.
|
|
29
|
+
const config = (cjk, textAlign = 'justify') => ({
|
|
30
|
+
locale: 'zh-Hans',
|
|
31
|
+
page: { width: pt(120), height: pt(400), dpi: 72, margins: { top: pt(20), bottom: pt(20), left: pt(20), right: pt(20) } },
|
|
32
|
+
layout: { layoutType: 'single' },
|
|
33
|
+
header: { elements: [] },
|
|
34
|
+
footer: { elements: [] },
|
|
35
|
+
bodyText: { fontFamily: 'Lora', fontSize: pt(16), lineHeight: pt(24), textAlign, firstLineIndent: pt(0), hyphenation: { enabled: false } },
|
|
36
|
+
cjk,
|
|
37
|
+
});
|
|
38
|
+
async function contentOf(doc, accessible = false) {
|
|
39
|
+
const pdf = await PDFDocument.load(await renderToPdf(doc, { fontProvider, accessible }));
|
|
40
|
+
const contents = pdf.getPage(0).node.Contents();
|
|
41
|
+
const refs = contents instanceof PDFArray ? contents.asArray() : [contents];
|
|
42
|
+
return refs
|
|
43
|
+
.map((ref) => {
|
|
44
|
+
const s = pdf.context.lookup(ref);
|
|
45
|
+
return s instanceof PDFRawStream ? new TextDecoder('latin1').decode(decodePDFRawStream(s).decode()) : '';
|
|
46
|
+
})
|
|
47
|
+
.join('\n');
|
|
48
|
+
}
|
|
49
|
+
/** The strings of the `/ActualText` spans of a content stream. */
|
|
50
|
+
function actualTexts(content) {
|
|
51
|
+
return [...content.matchAll(/\/ActualText\s*<FEFF([0-9A-Fa-f]*)>/g)].map((m) => {
|
|
52
|
+
const units = m[1].match(/.{4}/g) ?? [];
|
|
53
|
+
return String.fromCharCode(...units.map((h) => parseInt(h, 16)));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
describe('CJK punctuation in the PDF', () => {
|
|
57
|
+
it('paints an opening bracket trimmed at a line start half an em before its box', async () => {
|
|
58
|
+
const doc = buildDocument({ markdown: '此开卷第一「回也」作者' }, config({ punctuationWidth: 'fullwidth', trimLineStart: true }, 'left'));
|
|
59
|
+
const line = doc.blocks.find((b) => b.type === 'paragraph').lines[1];
|
|
60
|
+
expect(line.segments[0]).toMatchObject({ text: '「', width: 8, inkOffset: -8 });
|
|
61
|
+
const content = await contentOf(doc);
|
|
62
|
+
const xs = [...content.matchAll(/1 0 0 1 (-?[\d.]+) (-?[\d.]+) Tm/g)].map((m) => Number(m[1]));
|
|
63
|
+
// The line's first text object starts 8 px left of the line.
|
|
64
|
+
expect(xs).toContain(line.bbox.x - 8);
|
|
65
|
+
}, 60_000);
|
|
66
|
+
it('paints a Latin-shared quote in its Chinese box, the glyph where the layout placed it (#185)', async () => {
|
|
67
|
+
// The stub sets “ ” at half an em: in Chinese text each takes a one-em
|
|
68
|
+
// box, the opening glyph at its end, the closing one at its start.
|
|
69
|
+
const doc = buildDocument({ markdown: '说“你”好' }, config({ punctuationWidth: 'fullwidth', compressAdjacent: false }, 'left'));
|
|
70
|
+
const line = doc.blocks.find((b) => b.type === 'paragraph').lines[0];
|
|
71
|
+
const open = line.segments.find((s) => s.text === '“');
|
|
72
|
+
const close = line.segments.find((s) => s.text === '”');
|
|
73
|
+
expect(open).toMatchObject({ width: 16, inkOffset: 8 });
|
|
74
|
+
expect(close).toMatchObject({ width: 16, inkOffset: 0 });
|
|
75
|
+
const content = await contentOf(doc);
|
|
76
|
+
const xs = [...content.matchAll(/1 0 0 1 (-?[\d.]+) (-?[\d.]+) Tm/g)].map((m) => Number(m[1]));
|
|
77
|
+
// 说 then the quote's box, its glyph 8 px into it; ” after 你.
|
|
78
|
+
expect(line.text).toBe('说“你”好');
|
|
79
|
+
expect(xs).toContain(line.bbox.x + 16 + 8);
|
|
80
|
+
expect(xs).toContain(line.bbox.x + 48);
|
|
81
|
+
// Each quote is shown with the spacing that ends its advance where its
|
|
82
|
+
// box ends (its glyph is narrower than the box).
|
|
83
|
+
expect(content).toMatch(/[\d.]+ Tc/);
|
|
84
|
+
}, 60_000);
|
|
85
|
+
it('reads a line with Han–Latin spaces as written', async () => {
|
|
86
|
+
for (const accessible of [false, true]) {
|
|
87
|
+
const doc = buildDocument({ markdown: '我们用iPhone拍照,1999年的iPhone 15售价为¥5,999。' }, config({}));
|
|
88
|
+
const lines = doc.blocks.find((b) => b.type === 'paragraph').lines;
|
|
89
|
+
expect(lines.some((l) => l.segments.some((s) => s.autospace))).toBe(true);
|
|
90
|
+
const texts = actualTexts(await contentOf(doc, accessible));
|
|
91
|
+
// Every line spread or spaced gets a span with its own text: no space
|
|
92
|
+
// where the layout left a gap.
|
|
93
|
+
for (const l of lines) {
|
|
94
|
+
if (l.segments.some((s) => s.autospace || s.tracking !== undefined))
|
|
95
|
+
expect(texts).toContain(l.text);
|
|
96
|
+
}
|
|
97
|
+
expect(texts.join('')).not.toMatch(/[一-鿿] [A-Za-z0-9]|[A-Za-z0-9] [一-鿿]/);
|
|
98
|
+
}
|
|
99
|
+
}, 60_000);
|
|
100
|
+
it('reads a line with compressed marks as written, each mark advancing to its box end', async () => {
|
|
101
|
+
// Kaiming, set solid (left aligned): no tracking, no Han–Latin space;
|
|
102
|
+
// the half-width :, and the trimmed 「 are what the line holds.
|
|
103
|
+
for (const accessible of [false, true]) {
|
|
104
|
+
const doc = buildDocument({ markdown: '他说:「你来了吗?」她笑道,来了' }, config({}, 'left'));
|
|
105
|
+
const lines = doc.blocks.find((b) => b.type === 'paragraph').lines;
|
|
106
|
+
const marked = lines.filter((l) => l.segments.some((s) => s.inkOffset !== undefined));
|
|
107
|
+
expect(marked.length).toBeGreaterThan(0);
|
|
108
|
+
expect(marked.every((l) => l.segments.every((s) => s.tracking === undefined && !s.autospace))).toBe(true);
|
|
109
|
+
const content = await contentOf(doc, accessible);
|
|
110
|
+
const texts = actualTexts(content);
|
|
111
|
+
for (const l of marked)
|
|
112
|
+
expect(texts).toContain(l.text);
|
|
113
|
+
// The : (8 px, painted at its box) is shown with -8 px of character
|
|
114
|
+
// spacing: Lora has no : and advances the missing glyph one em, so the
|
|
115
|
+
// glyph's advance ends where its 8 px box does. The spacing is reset
|
|
116
|
+
// after it.
|
|
117
|
+
const colon = lines.flatMap((l) => l.segments).find((s) => s.text === ':');
|
|
118
|
+
expect(colon).toMatchObject({ width: 8, inkOffset: 0 });
|
|
119
|
+
expect(content).toMatch(/-8 Tc[\s\S]*?TJ|-8 Tc[\s\S]*?Tj/);
|
|
120
|
+
const ops = [...content.matchAll(/(-?[\d.]+) Tc/g)].map((m) => Number(m[1]));
|
|
121
|
+
expect(ops).toContain(-8);
|
|
122
|
+
expect(ops[ops.length - 1]).toBe(0);
|
|
123
|
+
}
|
|
124
|
+
}, 60_000);
|
|
125
|
+
it('widens the column clip for a hung mark', async () => {
|
|
126
|
+
const doc = buildDocument({ markdown: '此开卷第一,回也作者自云' }, config({ hangingPunctuation: 'allow' }));
|
|
127
|
+
const page = doc.pages[0];
|
|
128
|
+
const col = page.columns[0];
|
|
129
|
+
const hung = col.blocks.flatMap((b) => b.lines).find((l) => l.segments.some((s) => s.hangs));
|
|
130
|
+
expect(hung).toBeDefined();
|
|
131
|
+
const clip = columnClipRect(col, 72);
|
|
132
|
+
const mark = hung.segments.find((s) => s.hangs);
|
|
133
|
+
// A Kaiming comma: half an em, past the 80 px measure.
|
|
134
|
+
expect(mark.width).toBe(8);
|
|
135
|
+
expect(hung.bbox.width).toBe(80);
|
|
136
|
+
expect(clip.x + clip.width).toBeGreaterThanOrEqual(hung.bbox.x + hung.bbox.width + mark.width);
|
|
137
|
+
const content = await contentOf(doc);
|
|
138
|
+
// The clip rectangle the page paints the column in.
|
|
139
|
+
const rects = [...content.matchAll(/(-?[\d.]+) (-?[\d.]+) (-?[\d.]+) (-?[\d.]+) re\s+W\s+n/g)].map((m) => Number(m[1]) + Number(m[3]));
|
|
140
|
+
expect(rects.some((right) => Math.abs(right - (clip.x + clip.width)) < 0.01)).toBe(true);
|
|
141
|
+
}, 60_000);
|
|
142
|
+
});
|
|
143
|
+
//# sourceMappingURL=cjkPunctuation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cjkPunctuation.test.js","sourceRoot":"","sources":["../../src/__tests__/cjkPunctuation.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cjkTracking.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cjkTracking.test.ts"],"names":[],"mappings":""}
|