postext-pdf 1.29.1 → 1.30.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.
Files changed (102) hide show
  1. package/dist/__tests__/canvasShaping.test.d.ts +2 -0
  2. package/dist/__tests__/canvasShaping.test.d.ts.map +1 -0
  3. package/dist/__tests__/canvasShaping.test.js +258 -0
  4. package/dist/__tests__/canvasShaping.test.js.map +1 -0
  5. package/dist/__tests__/canvasWords.test.d.ts +2 -0
  6. package/dist/__tests__/canvasWords.test.d.ts.map +1 -0
  7. package/dist/__tests__/canvasWords.test.js +66 -0
  8. package/dist/__tests__/canvasWords.test.js.map +1 -0
  9. package/dist/__tests__/declaredFiles.test.d.ts +2 -0
  10. package/dist/__tests__/declaredFiles.test.d.ts.map +1 -0
  11. package/dist/__tests__/declaredFiles.test.js +435 -0
  12. package/dist/__tests__/declaredFiles.test.js.map +1 -0
  13. package/dist/__tests__/designTextRtl.test.js +17 -6
  14. package/dist/__tests__/designTextRtl.test.js.map +1 -1
  15. package/dist/__tests__/glyphPositions.d.ts +25 -0
  16. package/dist/__tests__/glyphPositions.d.ts.map +1 -0
  17. package/dist/__tests__/glyphPositions.js +288 -0
  18. package/dist/__tests__/glyphPositions.js.map +1 -0
  19. package/dist/__tests__/japaneseShaping.test.js +3 -3
  20. package/dist/__tests__/japaneseShaping.test.js.map +1 -1
  21. package/dist/__tests__/listMarkerShaping.test.d.ts +2 -0
  22. package/dist/__tests__/listMarkerShaping.test.d.ts.map +1 -0
  23. package/dist/__tests__/listMarkerShaping.test.js +64 -0
  24. package/dist/__tests__/listMarkerShaping.test.js.map +1 -0
  25. package/dist/__tests__/pdfWorker.test.js +27 -0
  26. package/dist/__tests__/pdfWorker.test.js.map +1 -1
  27. package/dist/__tests__/scriptContextHan.test.js +3 -3
  28. package/dist/__tests__/scriptContextHan.test.js.map +1 -1
  29. package/dist/__tests__/shapingLanguageDocument.test.js +1 -1
  30. package/dist/__tests__/shapingLanguageDocument.test.js.map +1 -1
  31. package/dist/__tests__/shapingLanguageRuns.test.d.ts +2 -0
  32. package/dist/__tests__/shapingLanguageRuns.test.d.ts.map +1 -0
  33. package/dist/__tests__/shapingLanguageRuns.test.js +250 -0
  34. package/dist/__tests__/shapingLanguageRuns.test.js.map +1 -0
  35. package/dist/__tests__/shapingLanguageRunsDocument.test.d.ts +2 -0
  36. package/dist/__tests__/shapingLanguageRunsDocument.test.d.ts.map +1 -0
  37. package/dist/__tests__/shapingLanguageRunsDocument.test.js +477 -0
  38. package/dist/__tests__/shapingLanguageRunsDocument.test.js.map +1 -0
  39. package/dist/__tests__/trackedLetters.test.d.ts +2 -0
  40. package/dist/__tests__/trackedLetters.test.d.ts.map +1 -0
  41. package/dist/__tests__/trackedLetters.test.js +77 -0
  42. package/dist/__tests__/trackedLetters.test.js.map +1 -0
  43. package/dist/canvasWords.d.ts +55 -0
  44. package/dist/canvasWords.d.ts.map +1 -0
  45. package/dist/canvasWords.js +160 -0
  46. package/dist/canvasWords.js.map +1 -0
  47. package/dist/faceFiles.d.ts +102 -5
  48. package/dist/faceFiles.d.ts.map +1 -1
  49. package/dist/faceFiles.js +271 -10
  50. package/dist/faceFiles.js.map +1 -1
  51. package/dist/fontCache.d.ts +92 -11
  52. package/dist/fontCache.d.ts.map +1 -1
  53. package/dist/fontCache.js +82 -17
  54. package/dist/fontCache.js.map +1 -1
  55. package/dist/index.d.ts +2 -2
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/pdf-backend/blockRender.d.ts.map +1 -1
  60. package/dist/pdf-backend/blockRender.js +72 -22
  61. package/dist/pdf-backend/blockRender.js.map +1 -1
  62. package/dist/pdf-backend/index.d.ts +3 -2
  63. package/dist/pdf-backend/index.d.ts.map +1 -1
  64. package/dist/pdf-backend/index.js +9 -3
  65. package/dist/pdf-backend/index.js.map +1 -1
  66. package/dist/pdf-backend/primitives.d.ts +55 -5
  67. package/dist/pdf-backend/primitives.d.ts.map +1 -1
  68. package/dist/pdf-backend/primitives.js +207 -26
  69. package/dist/pdf-backend/primitives.js.map +1 -1
  70. package/dist/pdf-backend/renderResourceBlock.d.ts.map +1 -1
  71. package/dist/pdf-backend/renderResourceBlock.js +32 -6
  72. package/dist/pdf-backend/renderResourceBlock.js.map +1 -1
  73. package/dist/pdf-backend/shapedText.d.ts +4 -0
  74. package/dist/pdf-backend/shapedText.d.ts.map +1 -1
  75. package/dist/pdf-backend/shapedText.js +30 -9
  76. package/dist/pdf-backend/shapedText.js.map +1 -1
  77. package/dist/pdf-backend/svgVector.js +1 -1
  78. package/dist/pdf-backend/svgVector.js.map +1 -1
  79. package/dist/pdf-backend/verticalText.d.ts.map +1 -1
  80. package/dist/pdf-backend/verticalText.js +7 -4
  81. package/dist/pdf-backend/verticalText.js.map +1 -1
  82. package/dist/pdf-backend/wordPieces.d.ts +29 -0
  83. package/dist/pdf-backend/wordPieces.d.ts.map +1 -0
  84. package/dist/pdf-backend/wordPieces.js +85 -0
  85. package/dist/pdf-backend/wordPieces.js.map +1 -0
  86. package/dist/shapingLanguage.d.ts +56 -3
  87. package/dist/shapingLanguage.d.ts.map +1 -1
  88. package/dist/shapingLanguage.js +202 -9
  89. package/dist/shapingLanguage.js.map +1 -1
  90. package/dist/unicodeRange.d.ts +19 -0
  91. package/dist/unicodeRange.d.ts.map +1 -0
  92. package/dist/unicodeRange.js +68 -0
  93. package/dist/unicodeRange.js.map +1 -0
  94. package/dist/worker/client.d.ts +1 -1
  95. package/dist/worker/client.d.ts.map +1 -1
  96. package/dist/worker/client.js +5 -2
  97. package/dist/worker/client.js.map +1 -1
  98. package/dist/worker/pdf.worker.js +7 -0
  99. package/dist/worker/pdf.worker.js.map +1 -1
  100. package/dist/worker/protocol.d.ts +6 -0
  101. package/dist/worker/protocol.d.ts.map +1 -1
  102. package/package.json +3 -3
@@ -0,0 +1,477 @@
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { execFileSync } from 'node:child_process';
6
+ import { buildDocument, lineRuns, resolveParagraph } from 'postext';
7
+ import { renderToPdf } from '../pdf-backend';
8
+ import { browserSizePx } from '../pdf-backend/wordPieces';
9
+ import { parseFontString } from '../fontString';
10
+ import { loadComplexShaper, shapeRun } from '../complexShaping';
11
+ import { paintedGlyphs } from './glyphPositions';
12
+ // #764, end to end, in the faces the page review found off: an Arabic
13
+ // paragraph with quotations in guillemets, brackets and full stops in
14
+ // Markazi Text and in Amiri, and a justified Catalan paragraph with `l·l`
15
+ // words in Crimson Pro. The document is laid out by a measurer that
16
+ // shapes as Chrome 154's canvas does (see `shapingLanguageRuns.test.ts`,
17
+ // which holds Chrome's own widths of these files), in the language and
18
+ // the direction the engine sets on it, and its PDF prints every word
19
+ // where the layout put it.
20
+ const fixture = (name) => new Uint8Array(fs.readFileSync(new URL(`./fixtures/fontsource/${name}`, import.meta.url)));
21
+ /** The files of each family, as Fontsource cuts it: `latin`, then
22
+ * `arabic`. */
23
+ const FACES = {
24
+ 'Markazi Text': [fixture('markazi-text-latin.ttf'), fixture('markazi-text-arabic.ttf')],
25
+ Amiri: [fixture('amiri-latin.ttf'), fixture('amiri-arabic.ttf')],
26
+ 'Crimson Pro': [fixture('crimson-pro-latin.ttf')],
27
+ // The same file under a name the measurer sets as a browser does: at
28
+ // its size cut to a hundredth of a pixel (see `CanvasCtx`).
29
+ 'Crimson Pro Cut': [fixture('crimson-pro-latin.ttf')],
30
+ };
31
+ /** The families the measurer sets at the size a browser does. */
32
+ const CUT_SIZES = /Cut$/;
33
+ const fontProvider = async (family) => FACES[family] ?? FACES.Amiri;
34
+ /** The file the browser takes a character from: the `arabic` one for the
35
+ * Arabic blocks (its `unicode-range`), the `latin` one for the rest. */
36
+ const fileOf = (files, cp) => (files.length > 1 && cp >= 0x0600 && cp <= 0x08FF ? files[1] : files[0]);
37
+ const SCRIPTS = [['Arab', /\p{Script=Arabic}/u], ['Latn', /\p{Script=Latin}/u], ['Grek', /\p{Script=Greek}/u], ['Cyrl', /\p{Script=Cyrillic}/u]];
38
+ const scriptOfLetter = (ch) => SCRIPTS.find(([, re]) => re.test(ch))?.[0];
39
+ /** The advance (em) of one word of a run, as Chrome shapes it: each
40
+ * character in the script of the letter before it in the word, else of
41
+ * the first after it (none for a word with no letter; `latin`: the whole
42
+ * string had no character above U+00FF, and is Latin text), then cut by
43
+ * the file that draws it. */
44
+ function wordAdvance(files, word, rtl, language, latin) {
45
+ const chars = [...word];
46
+ const own = chars.map((ch) => (latin ? 'Latn' : scriptOfLetter(ch)));
47
+ const first = own.find((s) => s !== undefined);
48
+ let before;
49
+ const scripts = own.map((s) => (before = s ?? before) ?? first);
50
+ let advance = 0;
51
+ for (let a = 0; a < chars.length;) {
52
+ const file = fileOf(files, chars[a].codePointAt(0));
53
+ let b = a + 1;
54
+ while (b < chars.length && scripts[b] === scripts[a] && fileOf(files, chars[b].codePointAt(0)) === file)
55
+ b++;
56
+ const script = scripts[a];
57
+ const run = shapeRun(file, chars.slice(a, b).join(''), { direction: rtl ? 'rtl' : 'ltr', ...(script ? { script } : {}), ...(language ? { language } : {}) });
58
+ advance += run ? run.glyphs.reduce((sum, g) => sum + g.xAdvance, 0) / run.upem : (b - a) * 0.5;
59
+ a = b;
60
+ }
61
+ return advance;
62
+ }
63
+ /** The strings the engine measured, each with the context's direction. */
64
+ const measured = new Set();
65
+ /**
66
+ * Measures as Chrome 154's canvas does, with HarfBuzz: the string cut
67
+ * into runs of one direction (UAX #9, in the context's `direction`), each
68
+ * run into words at its spaces, each word shaped in the script of its
69
+ * letters and the context's language.
70
+ */
71
+ class CanvasCtx {
72
+ direction = 'ltr';
73
+ letterSpacing = '0px';
74
+ fontString = '';
75
+ language = 'inherit';
76
+ get font() {
77
+ return this.fontString;
78
+ }
79
+ set font(value) {
80
+ this.fontString = value;
81
+ }
82
+ get lang() {
83
+ return this.language;
84
+ }
85
+ set lang(value) {
86
+ this.language = value;
87
+ }
88
+ measureText(s) {
89
+ const parsed = parseFontString(this.fontString);
90
+ const files = FACES[parsed?.family ?? ''] ?? FACES.Amiri;
91
+ const language = this.language === 'inherit' || this.language === '' ? undefined : this.language;
92
+ const latin = /^[\u0000-\u00FF]*$/.test(s);
93
+ measured.add(`${this.direction} ${s}`);
94
+ let advance = 0;
95
+ for (const run of lineRuns(resolveParagraph(s, this.direction === 'rtl' ? 'rtl' : 'ltr'))) {
96
+ for (const word of s.slice(run.start, run.end).split(/( )/)) {
97
+ if (word !== '')
98
+ advance += wordAdvance(files, word, (run.level & 1) === 1, language, latin);
99
+ }
100
+ }
101
+ // Chrome sets a face at its size cut to a hundredth of a pixel
102
+ // (11.4583 px at 11.45); a measurer with no browser at the size asked.
103
+ const sizePx = parsed?.sizePx ?? 16;
104
+ return { width: advance * (CUT_SIZES.test(parsed?.family ?? '') ? Math.floor(sizePx * 100) / 100 : sizePx) };
105
+ }
106
+ }
107
+ const host = globalThis;
108
+ host.OffscreenCanvasRenderingContext2D = CanvasCtx;
109
+ host.OffscreenCanvas = class {
110
+ getContext() {
111
+ return new CanvasCtx();
112
+ }
113
+ };
114
+ const pt = (value) => ({ value, unit: 'pt' });
115
+ const SIZE = 20;
116
+ const EM = SIZE / 1000;
117
+ const PAGE = 420;
118
+ const MARGIN = 20;
119
+ function config(locale, fontFamily, bodyText = {}) {
120
+ return {
121
+ page: { width: pt(PAGE), height: pt(400), dpi: 72, margins: { top: pt(MARGIN), bottom: pt(MARGIN), left: pt(MARGIN), right: pt(MARGIN) } },
122
+ locale,
123
+ layout: { layoutType: 'single' },
124
+ header: { elements: [] },
125
+ footer: { elements: [] },
126
+ bodyText: { fontFamily, fontSize: pt(SIZE), lineHeight: pt(36), firstLineIndent: pt(0), textAlign: 'justify', hyphenation: { enabled: false }, shapingLanguage: 'document', ...bodyText },
127
+ };
128
+ }
129
+ const hasPdftotext = (() => {
130
+ try {
131
+ execFileSync('pdftotext', ['-v'], { stdio: 'ignore' });
132
+ return true;
133
+ }
134
+ catch {
135
+ return false;
136
+ }
137
+ })();
138
+ /** What pdftotext reads as words on each line of the PDF's first page,
139
+ * px at 72 dpi. It cuts a word where its font changes, and at a letter
140
+ * that leaves air before the next: a word may come in several boxes. */
141
+ function wordBoxes(bytes) {
142
+ const file = path.join(os.tmpdir(), `postext-language-runs-${process.pid}-${Math.random().toString(36).slice(2)}.pdf`);
143
+ fs.writeFileSync(file, bytes);
144
+ const xml = execFileSync('pdftotext', ['-bbox', '-l', '1', file, '-'], { encoding: 'utf8' });
145
+ fs.unlinkSync(file);
146
+ const lines = new Map();
147
+ for (const m of xml.matchAll(/<word xMin="([\d.]+)" yMin="([\d.]+)" xMax="([\d.]+)" yMax="[\d.]+">/g)) {
148
+ const y = Math.round(Number(m[2]));
149
+ const key = [...lines.keys()].find((k) => Math.abs(k - y) < 8) ?? y;
150
+ if (!lines.has(key))
151
+ lines.set(key, []);
152
+ lines.get(key).push({ left: Number(m[1]), right: Number(m[3]) });
153
+ }
154
+ return [...lines.entries()].sort((a, b) => a[0] - b[0]).map(([, boxes]) => boxes);
155
+ }
156
+ const firstBlock = (doc) => doc.pages[0].columns[0].blocks[0];
157
+ /**
158
+ * The words of a line as the layout placed them, on the sheet: its
159
+ * segments in the order the line gives (flow order), from the line's
160
+ * start, the spaces of a justified line that is not its paragraph's last
161
+ * stretched to fill the measure; what stands between two spaces is one
162
+ * word. A right-to-left page is the mirror of its flow.
163
+ */
164
+ function wordPlaces(page, block, line) {
165
+ const segments = line.segments;
166
+ const measure = PAGE - 2 * MARGIN - (line.bbox.x - MARGIN);
167
+ const spaces = segments.filter((s) => s.kind === 'space').length;
168
+ const words = segments.reduce((sum, s) => sum + (s.kind === 'space' ? 0 : s.width), 0);
169
+ const space = block.textAlign === 'justify' && !line.isLastLine && spaces > 0 ? (measure - words) / spaces : undefined;
170
+ const places = [];
171
+ let x = line.bbox.x;
172
+ let open;
173
+ for (const i of line.order ?? segments.map((_, k) => k)) {
174
+ const seg = segments[i];
175
+ const width = seg.kind === 'space' ? space ?? seg.width : seg.width;
176
+ if (seg.kind === 'space')
177
+ open = undefined;
178
+ else if (open)
179
+ open.right = x + width;
180
+ else {
181
+ open = { left: x, right: x + width };
182
+ places.push(open);
183
+ }
184
+ x += width;
185
+ }
186
+ const flow = page.flow;
187
+ const originX = flow && flow.writingMode === 'horizontal-tb' && flow.direction === 'rtl' ? flow.mirror.originX : undefined;
188
+ return originX === undefined ? places : places.map((p) => ({ left: originX - p.right, right: originX - p.left }));
189
+ }
190
+ /** Every word of the PDF against its place: the boxes pdftotext read
191
+ * inside a place start where it starts and end where it ends, to a
192
+ * twentieth of a pixel, and no box lies outside every place. With
193
+ * `whole`, a place holds one box: pdftotext cuts no Latin word that has
194
+ * no hole in it. */
195
+ async function expectInPlace(doc, what, whole = false) {
196
+ const boxes = wordBoxes(await renderToPdf(doc, { fontProvider, accessible: false }));
197
+ const block = firstBlock(doc);
198
+ expect(boxes.length, what).toBe(block.lines.length);
199
+ block.lines.forEach((line, n) => {
200
+ const places = wordPlaces(doc.pages[0], block, line);
201
+ const inside = places.map((place) => boxes[n].filter((box) => (box.left + box.right) / 2 > place.left && (box.left + box.right) / 2 < place.right));
202
+ expect(inside.reduce((sum, list) => sum + list.length, 0), `${what} line ${n + 1}: ${line.text}`).toBe(boxes[n].length);
203
+ places.forEach((place, i) => {
204
+ expect(inside[i].length, `${what} line ${n + 1} word ${i + 1}`).toBeGreaterThan(0);
205
+ if (whole)
206
+ expect(inside[i].length, `${what} line ${n + 1} word ${i + 1} in one piece`).toBe(1);
207
+ expect(Math.min(...inside[i].map((box) => box.left)), `${what} line ${n + 1} word ${i + 1} left`).toBeCloseTo(place.left, 1);
208
+ expect(Math.max(...inside[i].map((box) => box.right)), `${what} line ${n + 1} word ${i + 1} right`).toBeCloseTo(place.right, 1);
209
+ });
210
+ });
211
+ }
212
+ /** The width the layout gave each text, thousandths of the em. */
213
+ const widthsOf = (doc) => Object.fromEntries(firstBlock(doc).lines.flatMap((line) => line.segments.filter((s) => s.kind !== 'space').map((s) => [s.text, Math.round((s.width / EM) * 10) / 10])));
214
+ beforeAll(async () => {
215
+ await loadComplexShaper();
216
+ });
217
+ // An Arabic paragraph: «…» quotations, a bracketed aside, full stops, a
218
+ // question mark inside a quotation.
219
+ const ARABIC = '\u0642\u0644\u062A: \u00AB\u0623\u0646\u0627 \u064A\u0627 \u062E\u0627\u0644\u0629.\u00BB \u062B\u0645 \u0645\u0634\u064A\u062A (\u0648\u064A\u0642\u0627\u0644 \u0633\u0627\u0641\u0631\u062A) \u0625\u0644\u0649 \u0627\u0644\u0628\u064A\u0648\u062A. \u00AB\u064A\u0648\u0633\u0641\u061F\u00BB \u0643\u062A\u0627\u0628.';
220
+ // Set from its start side: a justified Arabic line takes kashidas.
221
+ const fromStart = { textAlign: 'left' };
222
+ describe('an Arabic paragraph with quotations, brackets and full stops (#764)', () => {
223
+ it('Markazi Text: the layout measures the guillemets and the stops of Arabic text in Arabic', () => {
224
+ measured.clear();
225
+ const widths = widthsOf(buildDocument({ markdown: ARABIC }, config('ar', 'Markazi Text', fromStart)));
226
+ // Each word as a right-to-left run, as it is painted.
227
+ expect([...measured]).toContain('rtl \u00AB\u0623\u0646\u0627');
228
+ // Chrome's widths of these words in Arabic.
229
+ expect(widths).toMatchObject({ '\u00AB\u0623\u0646\u0627': 939.5, '\u062E\u0627\u0644\u0629.\u00BB': 1892.1, '\u0643\u062A\u0627\u0628.': 1708 });
230
+ // In Persian the face has no forms of its own, and the word is still
231
+ // measured as the right-to-left run every output paints (#770): the
232
+ // stop is kerned to the guillemet there (0.582 em; 0.587 when the two
233
+ // were measured as a left-to-right run of their own, up to 1.29.2).
234
+ measured.clear();
235
+ const persian = widthsOf(buildDocument({ markdown: ARABIC }, config('fa', 'Markazi Text', fromStart)));
236
+ expect([...measured]).toContain('rtl \u00AB\u0623\u0646\u0627');
237
+ expect([...measured]).not.toContain('ltr \u00AB\u0623\u0646\u0627');
238
+ expect(persian).toMatchObject({ '\u00AB\u0623\u0646\u0627': 1043.5, '\u062E\u0627\u0644\u0629.\u00BB': 1988.3, '\u0643\u062A\u0627\u0628.': 1714.8 });
239
+ });
240
+ it('Amiri: the stop, the brackets and the guillemets of an Arabic word are the Arabic ones', () => {
241
+ const widths = widthsOf(buildDocument({ markdown: ARABIC }, config('ar', 'Amiri', fromStart)));
242
+ expect(widths).toMatchObject({ '\u00AB\u0623\u0646\u0627': 1068, '\u062E\u0627\u0644\u0629.\u00BB': 2112, '(\u0648\u064A\u0642\u0627\u0644': 2380, '\u0643\u062A\u0627\u0628.': 2040 });
243
+ });
244
+ it.skipIf(!hasPdftotext)('the PDF prints every word where the layout put it, in both faces', async () => {
245
+ for (const family of ['Markazi Text', 'Amiri']) {
246
+ // Not Markazi Text in Persian: its words were measured left to
247
+ // right (see above), five thousandths of the em wider than any
248
+ // output sets `.»`.
249
+ for (const locale of family === 'Amiri' ? ['ar', 'ar-EG', 'fa'] : ['ar', 'ar-EG']) {
250
+ const doc = buildDocument({ markdown: ARABIC }, config(locale, family, fromStart));
251
+ expect(firstBlock(doc).lines.length, `${family} ${locale}`).toBeGreaterThan(1);
252
+ await expectInPlace(doc, `${family} ${locale}`);
253
+ }
254
+ }
255
+ }, 120_000);
256
+ it.skipIf(!hasPdftotext)('and in a document that keeps the host\u2019s language, measured in none', async () => {
257
+ const doc = buildDocument({ markdown: ARABIC }, config('ar', 'Amiri', { ...fromStart, shapingLanguage: 'host' }));
258
+ expect(widthsOf(doc)).toMatchObject({ '\u00AB\u0623\u0646\u0627': 1068, '\u062E\u0627\u0644\u0629.\u00BB': 2112, '\u0643\u062A\u0627\u0628.': 2040 });
259
+ await expectInPlace(doc, 'Amiri host');
260
+ // Markazi Text has no forms of its own in no language.
261
+ expect(widthsOf(buildDocument({ markdown: ARABIC }, config('ar', 'Markazi Text', { ...fromStart, shapingLanguage: 'host' })))['\u00AB\u0623\u0646\u0627']).toBe(1043.5);
262
+ }, 120_000);
263
+ });
264
+ describe('a justified Catalan paragraph in Crimson Pro (#764)', () => {
265
+ // A paragraph with a word in italics is cut word by word: the pieces of
266
+ // a word are as wide as the word grew by each. A plain one goes to the
267
+ // text library, which measures each piece alone.
268
+ const STYLED = 'La instal\u00B7laci\u00F3 de la il\u00B7luminaci\u00F3 col\u00B7lectiva *fou* una il\u00B7lusi\u00F3 paral\u00B7lela del col\u00B7legi.';
269
+ const PLAIN = STYLED.replace(/\*/g, '');
270
+ const hyphenated = (locale) => ({ hyphenation: { enabled: true, locale } });
271
+ /** The words of a paragraph: the pieces of each joined, with the width
272
+ * the layout gave them together. */
273
+ const wordWidths = (doc) => {
274
+ const words = {};
275
+ for (const line of firstBlock(doc).lines) {
276
+ let text = '';
277
+ let width = 0;
278
+ for (const seg of [...line.segments, { kind: 'space', text: ' ', width: 0 }]) {
279
+ if (seg.kind !== 'space') {
280
+ text += seg.text;
281
+ width += seg.width;
282
+ }
283
+ else if (text !== '') {
284
+ words[text] = Math.round((width / EM) * 10) / 10;
285
+ text = '';
286
+ width = 0;
287
+ }
288
+ }
289
+ }
290
+ return words;
291
+ };
292
+ /** Each line ends between two words: the piece that opens a line after
293
+ * a break inside a word keeps the width the word had beyond the break,
294
+ * whatever stood before it. */
295
+ const expectNoWordBroken = (doc) => {
296
+ for (const line of firstBlock(doc).lines)
297
+ expect(line.text).not.toMatch(/-$/);
298
+ };
299
+ it('a word cut at its syllables is measured whole in a styled paragraph: the Catalan dot is in the piece after it', () => {
300
+ const doc = buildDocument({ markdown: STYLED }, config('ca', 'Crimson Pro', hyphenated('ca')));
301
+ expectNoWordBroken(doc);
302
+ // Chrome's widths in Catalan (4381.8, 2708 and 2856.4 in any other
303
+ // language).
304
+ expect(wordWidths(doc)).toMatchObject({ 'instal\u00B7laci\u00F3': 4727.5, 'il\u00B7lusi\u00F3': 3053.7, 'col\u00B7legi.': 3430.7 });
305
+ // `tal·` alone has the narrow dot (1292); the 354 units of 1024 the
306
+ // Catalan one adds went to the piece after it, 725.6 wide alone.
307
+ const pieces = firstBlock(doc).lines[0].segments.filter((s) => s.kind !== 'space').map((s) => [s.text, Math.round((s.width / EM) * 10) / 10]);
308
+ expect(pieces.slice(1, 5)).toEqual([['ins', 1191.4], ['tal\u00B7', 1292], ['la', 1071.3], ['ci\u00F3', 1172.9]]);
309
+ const spanish = buildDocument({ markdown: STYLED }, config('es', 'Crimson Pro', hyphenated('es')));
310
+ expect(wordWidths(spanish)).toMatchObject({ 'instal\u00B7laci\u00F3': 4381.8, 'il\u00B7lusi\u00F3': 2708, 'col\u00B7legi.': 3085 });
311
+ });
312
+ it('and piece by piece in a plain paragraph: each as wide as it is alone', () => {
313
+ const doc = buildDocument({ markdown: PLAIN }, config('ca', 'Crimson Pro', hyphenated('ca')));
314
+ expectNoWordBroken(doc);
315
+ const pieces = firstBlock(doc).lines[0].segments.filter((s) => s.kind !== 'space').map((s) => [s.text, Math.round((s.width / EM) * 10) / 10]);
316
+ expect(pieces.slice(1, 5)).toEqual([['ins', 1191.4], ['tal\u00B7', 1292], ['la', 725.6], ['ci\u00F3', 1172.9]]);
317
+ });
318
+ it.skipIf(!hasPdftotext)('the PDF prints each word where the layout put it, as wide as measured: no hole after the dot', async () => {
319
+ for (const markdown of [STYLED, PLAIN]) {
320
+ for (const locale of ['ca', 'es']) {
321
+ const doc = buildDocument({ markdown }, config(locale, 'Crimson Pro', hyphenated(locale)));
322
+ expect(firstBlock(doc).lines.length, locale).toBeGreaterThan(1);
323
+ expectNoWordBroken(doc);
324
+ await expectInPlace(doc, `justified ${locale} ${markdown === STYLED ? 'styled' : 'plain'}`, true);
325
+ }
326
+ // Set ragged, and with no syllables: the words come whole.
327
+ await expectInPlace(buildDocument({ markdown }, config('ca', 'Crimson Pro', { textAlign: 'left' })), 'ragged ca', true);
328
+ await expectInPlace(buildDocument({ markdown }, config('ca', 'Crimson Pro')), 'whole words ca', true);
329
+ }
330
+ }, 120_000);
331
+ it.skipIf(!hasPdftotext)('a document that keeps the host\u2019s language is measured with the plain dot and printed piece by piece', async () => {
332
+ const doc = buildDocument({ markdown: STYLED }, config('ca', 'Crimson Pro', { ...hyphenated('ca'), shapingLanguage: 'host' }));
333
+ expectNoWordBroken(doc);
334
+ expect(wordWidths(doc)).toMatchObject({ 'instal\u00B7laci\u00F3': 4381.8, 'il\u00B7lusi\u00F3': 2708 });
335
+ await expectInPlace(doc, 'host ca', true);
336
+ }, 120_000);
337
+ });
338
+ describe('a word cut at its hyphen, on a line painted as one text (#770)', () => {
339
+ // A compound may end a line at its hyphen, so a line carries it in two
340
+ // pieces. In a paragraph with a style in it (the word in italics goes to
341
+ // the second line) the first is as wide as it is alone and the second as
342
+ // wide as the word grew by it, with whatever the face does across the
343
+ // hyphen: Crimson Pro kerns it against the `o` after it by +14.6
344
+ // thousandths of the em and against an `x` by -4.9. The first line has
345
+ // no style of its own and is painted as one text, as the canvas paints
346
+ // it in one `fillText`: Chrome 154 puts the `l` of `revisar-los.` in
347
+ // Schibsted Grotesk (kerned -0.765 px at 19.58 px) 0.765 px before the
348
+ // place of `los.`, and ends the word where that place ends.
349
+ const TEXT = 'un porta-objectes de vidre i un para-xocs de *goma* negra.';
350
+ const laidOut = (shapingLanguage) => buildDocument({ markdown: TEXT }, config('ca', 'Crimson Pro', { textAlign: 'left', shapingLanguage }));
351
+ /** Where the layout starts and ends each text of the first line,
352
+ * thousandths of the em from the sheet's left. */
353
+ const places = (doc) => {
354
+ const line = firstBlock(doc).lines[0];
355
+ const out = {};
356
+ let x = line.bbox.x;
357
+ for (const seg of line.segments) {
358
+ if (seg.kind !== 'space')
359
+ out[seg.text] = { start: x / EM, end: (x + seg.width) / EM, width: Math.round((seg.width / EM) * 10) / 10 };
360
+ x += seg.width;
361
+ }
362
+ return out;
363
+ };
364
+ /** Where the PDF sets the rest of each compound of the first line: the
365
+ * origin of the glyph after the hyphen, the pen the hyphen left, and
366
+ * the pen after the word's last glyph. */
367
+ const rests = async (doc) => {
368
+ const glyphs = (await paintedGlyphs(await renderToPdf(doc, { fontProvider, accessible: false })))[0].filter((g) => g.object === 0);
369
+ // The line is one text object, and reads as its text.
370
+ expect(glyphs.map((g) => g.text).join('')).toBe(firstBlock(doc).lines[0].text);
371
+ const out = [];
372
+ glyphs.forEach((g, i) => {
373
+ if (g.text !== '-')
374
+ return;
375
+ let last = i + 1;
376
+ while (glyphs[last + 1] && glyphs[last + 1].text !== ' ')
377
+ last++;
378
+ out.push({ start: glyphs[i + 1].x / EM, afterHyphen: (g.x + g.advance) / EM, end: (glyphs[last].x + glyphs[last].advance) / EM });
379
+ });
380
+ return out;
381
+ };
382
+ it('the layout holds the head as wide as alone and the rest as wide as the word grew by it', () => {
383
+ for (const mode of ['document', 'host']) {
384
+ const doc = laidOut(mode);
385
+ expect(firstBlock(doc).lines.map((line) => line.text)).toEqual(['un porta-objectes de vidre i un para-xocs de', 'goma negra.']);
386
+ // `objectes` is 3267.6 wide alone, `xocs` 1756.9.
387
+ expect(places(doc)).toMatchObject({ 'porta-': { width: 2529.3 }, objectes: { width: 3282.2 }, 'para-': { width: 2150.4 }, xocs: { width: 1752 } });
388
+ }
389
+ });
390
+ it('the PDF sets the word as one text: the rest stands the kerning from its place and ends where its place ends', async () => {
391
+ const doc = laidOut('document');
392
+ const at = places(doc);
393
+ const [objectes, xocs] = await rests(doc);
394
+ expect(objectes.afterHyphen).toBeCloseTo(at.objectes.start, 0);
395
+ expect(objectes.start - at.objectes.start).toBeCloseTo(14.6, 0);
396
+ expect(objectes.end).toBeCloseTo(at.objectes.end, 0);
397
+ expect(xocs.afterHyphen).toBeCloseTo(at.xocs.start, 0);
398
+ expect(xocs.start - at.xocs.start).toBeCloseTo(-4.9, 0);
399
+ expect(xocs.end).toBeCloseTo(at.xocs.end, 0);
400
+ }, 120_000);
401
+ it('a document that keeps the host\u2019s language sets the rest alone at its place: it ends the kerning short of its place, or past it', async () => {
402
+ const doc = laidOut('host');
403
+ const at = places(doc);
404
+ const [objectes, xocs] = await rests(doc);
405
+ expect(objectes.start).toBeCloseTo(at.objectes.start, 0);
406
+ expect(objectes.end - at.objectes.end).toBeCloseTo(-14.6, 0);
407
+ expect(xocs.start).toBeCloseTo(at.xocs.start, 0);
408
+ expect(xocs.end - at.xocs.end).toBeCloseTo(4.9, 0);
409
+ }, 120_000);
410
+ });
411
+ describe('the size a browser sets a face at (#770)', () => {
412
+ // Chrome measures and paints 11.4583 px text at 11.45 px: every width of
413
+ // the layout is 0.072 % under the PDF's advance at the size asked for,
414
+ // more than the half thousandth the word rule allows two widths of one
415
+ // text. `Crimson Pro Cut` is measured so; `Crimson Pro` at the size
416
+ // asked for, as a layout made with no browser is.
417
+ const SIZE_PX = 11.4583;
418
+ const UNIT = SIZE_PX / 1000;
419
+ const laidOut = (markdown, fontFamily, bodyText = {}, width = PAGE) => {
420
+ const base = config('ca', fontFamily, { fontSize: pt(SIZE_PX), lineHeight: pt(20), ...bodyText });
421
+ return buildDocument({ markdown }, { ...base, page: { ...base.page, width: pt(width) } });
422
+ };
423
+ /** The glyphs of the first line, by what they read as. */
424
+ const firstLine = async (doc) => {
425
+ const glyphs = (await paintedGlyphs(await renderToPdf(doc, { fontProvider, accessible: false })))[0];
426
+ return glyphs.filter((g) => Math.abs(g.y - glyphs[0].y) < 0.5);
427
+ };
428
+ it('is the size cut to a hundredth of a pixel, in single precision', () => {
429
+ // Chrome 154, the advance of thirty letters against the em.
430
+ expect([20.208333333333332, 19.583333333333336, 21.666666666666668, 15.4167, 11.4583, 20.3, 20.22, 20.23, 15.42, 20.25, 20, 12].map(browserSizePx))
431
+ .toEqual([20.2, 19.58, 21.66, 15.41, 11.45, 20.29, 20.21, 20.23, 15.42, 20.25, 20, 12]);
432
+ });
433
+ // The compound of the test above, on a line with no style of its own.
434
+ const COMPOUND = 'un porta-objectes de vidre i un para-xocs de goma negra per a la casa gran de la vila vella, *nova* i bona.';
435
+ for (const family of ['Crimson Pro Cut', 'Crimson Pro']) {
436
+ it(`a word cut at its hyphen is set as one text, measured ${family === 'Crimson Pro' ? 'at the size asked for' : 'as a browser sets the face'}`, async () => {
437
+ const doc = laidOut(COMPOUND, family, { textAlign: 'left' });
438
+ const line = firstBlock(doc).lines[0];
439
+ expect(line.text).toBe('un porta-objectes de vidre i un para-xocs de goma negra per a la casa gran de la vila');
440
+ const head = line.segments.findIndex((seg) => seg.text === 'porta-');
441
+ const start = line.bbox.x + line.segments.slice(0, head + 1).reduce((sum, seg) => sum + seg.width, 0);
442
+ // The rest is as wide as the word grew by it: its 3267.6 alone and
443
+ // the 14.6 of the hyphen against its `o`, at the size measured.
444
+ const measuredAt = family === 'Crimson Pro' ? SIZE_PX : 11.45;
445
+ expect(line.segments[head + 1].width / (measuredAt / 1000)).toBeCloseTo(3282.2, 0);
446
+ const glyphs = await firstLine(doc);
447
+ const hyphen = glyphs.findIndex((g) => g.text === '-');
448
+ // The `o` stands the kerning after the hyphen's pen, which is where
449
+ // the place of the rest starts, to the 2 units the head is wider at
450
+ // the size asked for than at the size cut.
451
+ expect((glyphs[hyphen + 1].x - (glyphs[hyphen].x + glyphs[hyphen].advance)) / UNIT).toBeCloseTo(14.6, 0);
452
+ expect(Math.abs(glyphs[hyphen].x + glyphs[hyphen].advance - start) / UNIT).toBeLessThan(2.5);
453
+ }, 120_000);
454
+ it(`the hyphen a line ends on is set apart, measured ${family === 'Crimson Pro' ? 'at the size asked for' : 'as a browser sets the face'}`, async () => {
455
+ // `po-`: the head 1020.5 and the hyphen 364.3, measured apart;
456
+ // Crimson Pro kerns the hyphen against the `o` before it by +14.6.
457
+ const WIDTH = 104;
458
+ const doc = laidOut('el noi no po\u00ADdria venir a la casa del *pare* avui', family, {}, WIDTH);
459
+ const line = firstBlock(doc).lines[0];
460
+ expect(line.text).toBe('el noi no po-');
461
+ expect(line.hyphenated).toBe(true);
462
+ const measuredAt = family === 'Crimson Pro' ? SIZE_PX : 11.45;
463
+ expect(line.segments[line.segments.length - 1].width / (measuredAt / 1000)).toBeCloseTo(1384.8, 0);
464
+ const glyphs = await firstLine(doc);
465
+ expect(glyphs.map((g) => g.text).join('').replace(/ /g, '')).toBe('elnoinopo-');
466
+ const hyphen = glyphs[glyphs.length - 1];
467
+ const o = glyphs[glyphs.length - 2];
468
+ // The hyphen starts where the `o` left the pen, to the unit the head
469
+ // is wider at the size asked for than at the size cut, and the
470
+ // justified line ends at its measure; set in the head's text the
471
+ // hyphen stood 14.6 further on.
472
+ expect(Math.abs(hyphen.x - (o.x + o.advance)) / UNIT).toBeLessThan(1.5);
473
+ expect(Math.abs(hyphen.x + hyphen.advance - (WIDTH - MARGIN)) / UNIT).toBeLessThan(1.5);
474
+ }, 120_000);
475
+ }
476
+ });
477
+ //# sourceMappingURL=shapingLanguageRunsDocument.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shapingLanguageRunsDocument.test.js","sourceRoot":"","sources":["../../src/__tests__/shapingLanguageRunsDocument.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=trackedLetters.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trackedLetters.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/trackedLetters.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import fontkit from '@pdf-lib/fontkit';
4
+ import { PDFDocument } from 'pdf-lib';
5
+ import { shapeInCurrentLanguage, withDocumentLanguage } from '../shapingLanguage';
6
+ import { letterTrackingTj, measuredTextShows, showTextShaped, textAdvancePx, textShows } from '../pdf-backend/primitives';
7
+ // #770: the layout counts the tracking of a tracked word once a letter, and
8
+ // `Tc` follows every glyph: a ligature, one glyph for two or three letters,
9
+ // advanced by one tracking, so the letters after it stood a tracking early
10
+ // for each letter joined and the word ended that much short of its width.
11
+ // In a document shaped in its own language the pen moves on after a
12
+ // ligature by the tracking of its other letters; one that keeps its host's
13
+ // language prints as it did.
14
+ // Cormorant Garamond sets fi, ffi and fl as one glyph each.
15
+ const cormorant = new Uint8Array(fs.readFileSync(new URL('../../../../apps/web/public/fonts/CormorantGaramond.ttf', import.meta.url)));
16
+ let font;
17
+ beforeAll(async () => {
18
+ const pdf = await PDFDocument.create();
19
+ pdf.registerFontkit(fontkit);
20
+ font = await pdf.embedFont(cormorant, { subset: true });
21
+ shapeInCurrentLanguage(font);
22
+ });
23
+ const inDocument = (fn) => withDocumentLanguage('en', 'en', fn);
24
+ const inHost = (fn) => withDocumentLanguage(undefined, 'en', fn);
25
+ const SIZE = 20;
26
+ const T = 0.4;
27
+ /** The numbers of a `TJ` operator (thousandths of the font size). */
28
+ const numbers = (op) => (/\[(.*)\]\s*TJ/s.exec(op.toString())?.[1] ?? '').replace(/<[0-9A-Fa-f]*>/g, ' ').trim().split(/\s+/).filter(Boolean).map(Number);
29
+ describe('the tracking of a ligature in the PDF (#770)', () => {
30
+ it('a word advances by the tracking of each of its letters', () => {
31
+ for (const [word, glyphs, letters] of [['first', 4, 5], ['office', 4, 6], ['fly', 2, 3], ['stood', 5, 5]]) {
32
+ const natural = inDocument(() => textAdvancePx(font, word, SIZE).advance);
33
+ const shown = inDocument(() => textAdvancePx(font, word, SIZE, T));
34
+ expect(shown.glyphs, word).toBe(glyphs);
35
+ expect(shown.advance, word).toBeCloseTo(natural + letters * T, 9);
36
+ // Keeping the host's language: once a glyph, as up to 1.29.
37
+ expect(inHost(() => textAdvancePx(font, word, SIZE, T).advance), word).toBeCloseTo(inHost(() => textAdvancePx(font, word, SIZE).advance) + glyphs * T, 9);
38
+ }
39
+ });
40
+ it('the pen moves on after the ligature by the tracking of its other letters', () => {
41
+ const tj = (T / SIZE) * 1000;
42
+ expect(inDocument(() => letterTrackingTj(T, SIZE))).toBeCloseTo(tj, 9);
43
+ expect(inHost(() => letterTrackingTj(T, SIZE))).toBe(0);
44
+ expect(inDocument(() => letterTrackingTj(0, SIZE))).toBe(0);
45
+ const plain = inDocument(() => numbers(showTextShaped(font, 'office')));
46
+ const tracked = inDocument(() => numbers(showTextShaped(font, 'office', tj)));
47
+ // One number more in the array, or one of its own changed: the pen
48
+ // moves two trackings further (o, ffi, c, e: the ligature joins three).
49
+ const sum = (ns) => ns.reduce((a, b) => a + b, 0);
50
+ expect(sum(plain) - sum(tracked)).toBeCloseTo(2 * tj, 2);
51
+ // A word with no ligature is the same show.
52
+ expect(inDocument(() => showTextShaped(font, 'stood', tj).toString())).toBe(inDocument(() => showTextShaped(font, 'stood').toString()));
53
+ });
54
+ it('a line set piece by piece and a text set whole carry the same movement', () => {
55
+ const tj = (T / SIZE) * 1000;
56
+ const sum = (shows) => shows.flatMap((s) => numbers(s.op)).reduce((a, b) => a + b, 0);
57
+ const whole = inDocument(() => sum(textShows(font, 'first', true, false, 0)) - sum(textShows(font, 'first', true, false, tj)));
58
+ expect(whole).toBeCloseTo(tj, 2);
59
+ // Measured pieces: `first` at the width the layout gave it, natural
60
+ // plus five trackings; the space after it starts where the layout put
61
+ // it, with no correction left to make.
62
+ const natural = inDocument(() => textAdvancePx(font, 'first', SIZE).advance);
63
+ const space = inDocument(() => textAdvancePx(font, ' ', SIZE).advance);
64
+ const pieces = [{ text: 'first', width: natural + 5 * T }, { text: ' ', width: space + T }, { text: 'stood', width: 0 }];
65
+ const own = inDocument(() => sum(measuredTextShows(font, pieces, SIZE, T)));
66
+ const untracked = inDocument(() => sum(measuredTextShows(font, [{ text: 'first', width: natural }, { text: ' ', width: space }, { text: 'stood', width: 0 }], SIZE, 0)));
67
+ expect(untracked - own).toBeCloseTo(tj, 1);
68
+ // Keeping the host's language the ligature takes one tracking and the
69
+ // correction after the word makes up the other: the same total, the
70
+ // letters `rst` a tracking early.
71
+ const host = inHost(() => measuredTextShows(font, pieces, SIZE, T));
72
+ const ownShows = inDocument(() => measuredTextShows(font, pieces, SIZE, T));
73
+ expect(sum(host)).toBeCloseTo(own, 1);
74
+ expect(host.map((s) => s.op.toString()).join('')).not.toBe(ownShows.map((s) => s.op.toString()).join(''));
75
+ });
76
+ });
77
+ //# sourceMappingURL=trackedLetters.test.js.map