reamkit 1.27.0 → 1.28.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 (65) hide show
  1. package/README.md +6 -5
  2. package/dist/esm/core/converter/ream.d.ts +0 -9
  3. package/dist/esm/core/converter/ream.js +0 -1
  4. package/dist/esm/core/document-model/types.d.ts +5 -3
  5. package/dist/esm/core/font/index.d.ts +1 -0
  6. package/dist/esm/core/font/ligatures.d.ts +17 -0
  7. package/dist/esm/core/font/ligatures.js +49 -0
  8. package/dist/esm/core/font/ttf-parser.d.ts +2 -1
  9. package/dist/esm/core/font/ttf-parser.js +11 -2
  10. package/dist/esm/core/fonts/remote-fonts.d.ts +1 -1
  11. package/dist/esm/core/fonts/remote-fonts.js +47 -2
  12. package/dist/esm/core/fonts/scripts.js +10 -5
  13. package/dist/esm/excel/header-footer.js +53 -8
  14. package/dist/esm/layout/styled-layout.js +53 -3
  15. package/dist/esm/pdf/cid-font.js +35 -8
  16. package/dist/esm/pdf-reader/annot-draw.js +114 -1
  17. package/dist/esm/pdf-reader/annots.d.ts +0 -14
  18. package/dist/esm/pdf-reader/annots.js +13 -1
  19. package/dist/esm/pdf-reader/ccitt.d.ts +2 -3
  20. package/dist/esm/pdf-reader/ccitt.js +32 -4
  21. package/dist/esm/pdf-reader/cff-outline.d.ts +36 -0
  22. package/dist/esm/pdf-reader/cff-outline.js +1122 -0
  23. package/dist/esm/pdf-reader/cmap.js +5 -2
  24. package/dist/esm/pdf-reader/content.d.ts +76 -0
  25. package/dist/esm/pdf-reader/content.js +142 -59
  26. package/dist/esm/pdf-reader/dingbats.d.ts +11 -0
  27. package/dist/esm/pdf-reader/dingbats.js +1033 -0
  28. package/dist/esm/pdf-reader/display.js +41 -1
  29. package/dist/esm/pdf-reader/document.js +26 -9
  30. package/dist/esm/pdf-reader/embedded-fonts.d.ts +11 -0
  31. package/dist/esm/pdf-reader/embedded-fonts.js +14 -1
  32. package/dist/esm/pdf-reader/encodings.d.ts +25 -0
  33. package/dist/esm/pdf-reader/encodings.js +110 -0
  34. package/dist/esm/pdf-reader/flow-build.d.ts +9 -3
  35. package/dist/esm/pdf-reader/flow-build.js +13 -7
  36. package/dist/esm/pdf-reader/font.js +329 -8
  37. package/dist/esm/pdf-reader/glyf-outline.d.ts +43 -0
  38. package/dist/esm/pdf-reader/glyf-outline.js +351 -0
  39. package/dist/esm/pdf-reader/glyph-names.js +20 -0
  40. package/dist/esm/pdf-reader/icc.d.ts +10 -0
  41. package/dist/esm/pdf-reader/icc.js +210 -0
  42. package/dist/esm/pdf-reader/image-decode.d.ts +4 -3
  43. package/dist/esm/pdf-reader/image-decode.js +85 -64
  44. package/dist/esm/pdf-reader/images.d.ts +6 -0
  45. package/dist/esm/pdf-reader/images.js +50 -7
  46. package/dist/esm/pdf-reader/jbig2.js +104 -19
  47. package/dist/esm/pdf-reader/layout.js +1148 -45
  48. package/dist/esm/pdf-reader/math-rows.d.ts +23 -0
  49. package/dist/esm/pdf-reader/math-rows.js +198 -0
  50. package/dist/esm/pdf-reader/predefined-cmap.d.ts +21 -0
  51. package/dist/esm/pdf-reader/predefined-cmap.js +102 -0
  52. package/dist/esm/pdf-reader/reader.d.ts +6 -9
  53. package/dist/esm/pdf-reader/reader.js +34 -21
  54. package/dist/esm/pdf-reader/shading.d.ts +81 -2
  55. package/dist/esm/pdf-reader/shading.js +191 -25
  56. package/dist/esm/pdf-reader/stream-filters.d.ts +6 -0
  57. package/dist/esm/pdf-reader/stream-filters.js +67 -0
  58. package/dist/esm/pdf-reader/text-rules.js +83 -10
  59. package/dist/esm/pdf-reader/text.js +82 -3
  60. package/dist/esm/pdf-reader/type1-outline.d.ts +21 -0
  61. package/dist/esm/pdf-reader/type1-outline.js +576 -0
  62. package/dist/esm/pdf-reader/vector.js +43 -4
  63. package/dist/esm/word/document-parser.js +4 -1
  64. package/dist/esm/word/docx-writer.js +1 -0
  65. package/package.json +1 -1
@@ -1,10 +1,19 @@
1
+ import { resolveFamilyStyle } from "../core/fonts/remote-fonts.js";
1
2
  import { parseTtf } from "../core/font/ttf-parser.js";
2
3
  import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
3
4
  import { parseToUnicodeCMap } from "./cmap.js";
5
+ import { decodePredefined, predefinedCMap, splitPredefined } from "./predefined-cmap.js";
6
+ import { isZapfDingbats, zapfDingbatsChar } from "./dingbats.js";
4
7
  import { textForGlyphName } from "./glyph-names.js";
8
+ import { cffCidToGid, cffNameToGid, cffOutlineSource, openTypeCff } from "./cff-outline.js";
9
+ import { type1Font } from "./type1-outline.js";
10
+ import { baseEncodingTable, isStandardLatinFace, standardEncodingTable } from "./encodings.js";
11
+ import { outlineSource, postGlyphNames } from "./glyf-outline.js";
5
12
  import { standardFace, standardWidth } from "./standard-widths.js";
6
- import { embeddedFontName } from "./embedded-fonts.js";
13
+ import { embeddedFontName, hasLiftableProgram } from "./embedded-fonts.js";
7
14
  //#region src/pdf-reader/font.ts
15
+ /** §9.10.2 — what a composite code nothing can answer for comes to. */
16
+ var UNANSWERABLE = "�";
8
17
  /**
9
18
  * Build a {@link ContentFont} (the interpreter's decode + advance hooks) from a
10
19
  * `/Font` dictionary (E-PDF EP2). Unicode comes from the `/ToUnicode` CMap;
@@ -21,25 +30,50 @@ function buildContentFont(file, fontDict) {
21
30
  const isType0 = asName(file.resolve(fontDict.get("Subtype") ?? PDF_NULL)) === "Type0";
22
31
  let toUnicode = /* @__PURE__ */ new Map();
23
32
  let codeBytes = isType0 ? 2 : 1;
33
+ const encodingName = asName(file.resolve(fontDict.get("Encoding") ?? PDF_NULL));
34
+ const encodingNamed = isType0 && encodingName.length > 0;
35
+ const named = isType0 ? predefinedCMap(encodingName) : void 0;
24
36
  const tu = file.resolve(fontDict.get("ToUnicode") ?? PDF_NULL);
25
37
  if (tu instanceof PdfStream) {
26
38
  const parsed = parseToUnicodeCMap(file.streamData(tu));
27
39
  toUnicode = parsed.map;
28
- if (isType0) codeBytes = parsed.codeBytes;
40
+ if (isType0 && !encodingNamed) codeBytes = parsed.codeBytes;
29
41
  }
30
42
  const fromProgram = isType0 && toUnicode.size === 0 ? embeddedCmap(file, fontDict) : void 0;
31
- const fromNames = !isType0 ? namedGlyphs(file, fontDict) : void 0;
43
+ let programFallback = null;
44
+ const fromProgramFor = (code) => {
45
+ if (!isType0 || toUnicode.size === 0) return void 0;
46
+ programFallback ??= embeddedCmap(file, fontDict);
47
+ return programFallback?.get(code);
48
+ };
49
+ const glyphs = isType0 ? void 0 : simpleGlyphs(file, fontDict);
50
+ const fromNames = !isType0 ? namedGlyphs(file, fontDict, toUnicode.size > 0, {
51
+ draws: (name) => glyphs?.byName(name) !== void 0,
52
+ blank: (name) => glyphs?.blank(name) === true
53
+ }) : void 0;
54
+ const glyphNames = isType0 ? /* @__PURE__ */ new Map() : differences(file, fontDict);
32
55
  const unicode = fromProgram ?? (toUnicode.size > 0 ? toUnicode : fromNames ?? toUnicode);
33
56
  const bytesPerCode = codeBytes;
57
+ const indices = !isType0 && glyphs?.indexed === true && fromNames === void 0 && toUnicode.size === 0 && glyphNames.size === 0;
58
+ const dingbats = !isType0 && isZapfDingbats(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
59
+ const baseNames = isType0 ? void 0 : baseEncoding(file, fontDict);
60
+ const fromBase = /* @__PURE__ */ new Map();
61
+ for (const [code, glyph] of baseNames ?? []) {
62
+ const text = textForGlyphName(glyph);
63
+ if (text !== void 0) fromBase.set(code, text);
64
+ }
65
+ const namesOf = new Map([...baseNames ?? [], ...glyphNames]);
34
66
  const style = faceStyle(file, fontDict, isType0);
35
- const name = embeddedFontName(file, fontDict);
67
+ const name = runFontName(file, fontDict, isType0);
36
68
  const type3 = asName(file.resolve(fontDict.get("Subtype") ?? PDF_NULL)) === "Type3" ? type3Face(file, fontDict) : void 0;
37
- const speechless = isType0 && unicode.size === 0;
38
- const decodeOne = (code) => unicode.get(code) ?? (bytesPerCode === 1 ? latin1(code) : speechless ? "�" : "");
69
+ const decodeOne = (code) => (indices ? UNANSWERABLE : void 0) ?? unicode.get(code) ?? fromProgramFor(code) ?? (named ? decodePredefined(named, code) : void 0) ?? fromNames?.get(code) ?? (dingbats ? zapfDingbatsChar(code) : void 0) ?? fromBase.get(code) ?? (bytesPerCode === 1 ? latin1(code) : UNANSWERABLE);
39
70
  const simple = simpleWidths(file, fontDict, decodeOne);
40
71
  const width = isType0 ? cidWidths(file, fontDict) : type3 ? (code) => simple(code) * type3.matrix[0] * 1e3 : simple;
41
72
  return {
42
73
  bytesPerCode,
74
+ ...named ? { splitCodes: (b) => splitPredefined(named, b) } : {},
75
+ ...isType0 ? outlineOf(file, fontDict, decodeOne) : simpleOutlineOf(decodeOne, namesOf, glyphs),
76
+ ...named?.vertical ? { verticalAdvance: cidVerticalAdvance(file, fontDict) } : {},
43
77
  ...type3 ? { type3 } : {},
44
78
  ...name !== void 0 ? { name } : {},
45
79
  decode: (codes) => codes.map((c) => readable(decodeOne(c))).join(""),
@@ -130,6 +164,192 @@ function embeddedCmap(file, fontDict) {
130
164
  });
131
165
  return out.size > 0 ? out : void 0;
132
166
  }
167
+ /**
168
+ * §9.6.6 — the outline a code draws, for a code that stands for no character.
169
+ *
170
+ * The glyph is there even when the character is not: `/Encoding /Identity-H`
171
+ * makes the code a CID, `/CIDToGIDMap` turns that into a glyph index, and the
172
+ * embedded program holds the contours. complex_ttf_font.pdf is eight lines of
173
+ * Arabic in a subset with no `cmap` and no `/ToUnicode`, and every one of them
174
+ * was dropped.
175
+ *
176
+ * Deliberately NOT a fallback for text: a code the font CAN answer for is set
177
+ * as type, and only the unanswerable ones are traced.
178
+ *
179
+ * @param file The owning file.
180
+ * @param fontDict The Type 0 font dictionary.
181
+ * @param decodeOne What one code comes to, to tell the two cases apart.
182
+ * @returns The `outline` field of a {@link ContentFont}, or nothing where the
183
+ * program carries no outlines this reads.
184
+ */
185
+ function outlineOf(file, fontDict, decodeOne) {
186
+ const cidFont = descendantFont(file, fontDict);
187
+ const descriptor = file.resolve(cidFont.get("FontDescriptor") ?? PDF_NULL);
188
+ if (!(descriptor instanceof Map)) return {};
189
+ const truetype = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
190
+ const compact = file.resolve(descriptor.get("FontFile3") ?? PDF_NULL);
191
+ const program = truetype instanceof PdfStream ? truetype : compact;
192
+ if (!(program instanceof PdfStream)) return {};
193
+ let source;
194
+ let charsetCids;
195
+ try {
196
+ const bytes = file.streamData(program);
197
+ source = outlineSource(bytes);
198
+ if (!source) {
199
+ const cff = openTypeCff(bytes) ?? bytes;
200
+ source = cffOutlineSource(cff);
201
+ charsetCids = source ? cffCidToGid(cff) : void 0;
202
+ }
203
+ } catch {
204
+ return {};
205
+ }
206
+ if (!source) return {};
207
+ const cidToGid = readCidToGid(file, cidFont);
208
+ return { outline: {
209
+ matrix: [
210
+ 1,
211
+ 0,
212
+ 0,
213
+ 1,
214
+ 0,
215
+ 0
216
+ ],
217
+ path: (code) => {
218
+ if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
219
+ const mapped = cidToGid ? cidToGid[code] ?? 0 : code;
220
+ const gid = charsetCids ? charsetCids.get(mapped) ?? mapped : mapped;
221
+ return source.path(gid);
222
+ }
223
+ } };
224
+ }
225
+ /**
226
+ * §9.6.6 — the outline a SIMPLE font's code draws, for a code that stands for
227
+ * no character.
228
+ *
229
+ * A simple font addresses its glyphs by NAME: `/Differences` (or the program's
230
+ * own `/Encoding`) says code 2 is `g18`, and the program says what `g18` looks
231
+ * like. Where that name is not a character there is nothing to write and there
232
+ * is still something to draw — TAMReview.pdf sets most of its body in a Cambria
233
+ * subset whose glyphs are named `g18`, `g152`, `g135`, and seven thousand of
234
+ * its nine thousand characters were dropped.
235
+ *
236
+ * @param file The owning file.
237
+ * @param fontDict The simple font's dictionary.
238
+ * @param decodeOne What one code comes to, to tell the two cases apart.
239
+ * @param nameOf The glyph name a code selects, where the font states one.
240
+ * @returns The `outline` field of a {@link ContentFont}, or nothing.
241
+ */
242
+ function simpleOutlineOf(decodeOne, nameOf, source) {
243
+ if (!source) return {};
244
+ return { outline: {
245
+ matrix: [
246
+ 1,
247
+ 0,
248
+ 0,
249
+ 1,
250
+ 0,
251
+ 0
252
+ ],
253
+ path: (code) => {
254
+ if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
255
+ const name = nameOf.get(code) ?? source.builtIn?.get(code);
256
+ if (name === void 0) return source.byIndex?.(code);
257
+ return source.byName(name);
258
+ }
259
+ } };
260
+ }
261
+ /**
262
+ * §9.6.6 — the program a SIMPLE font embeds, ready to draw a glyph BY NAME.
263
+ *
264
+ * All three formats appear here: a Type 1 program keys its charstrings by name
265
+ * outright, a CFF says which name each glyph has in its charset, and a TrueType
266
+ * says nothing at all — but a subsetter that renames glyphs `g24` has written
267
+ * the glyph's index into the name, which is the only handle such a program
268
+ * gives.
269
+ *
270
+ * @param file The owning file.
271
+ * @param fontDict The simple font's dictionary.
272
+ * @returns How to draw one of its glyphs, or `undefined` where nothing here
273
+ * can read the program.
274
+ */
275
+ function simpleGlyphs(file, fontDict) {
276
+ const descriptor = file.resolve(fontDict.get("FontDescriptor") ?? PDF_NULL);
277
+ if (!(descriptor instanceof Map)) return void 0;
278
+ const typeOne = file.resolve(descriptor.get("FontFile") ?? PDF_NULL);
279
+ const truetype = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
280
+ const compact = file.resolve(descriptor.get("FontFile3") ?? PDF_NULL);
281
+ try {
282
+ if (typeOne instanceof PdfStream) {
283
+ const face = type1Font(file.streamData(typeOne));
284
+ if (!face) return void 0;
285
+ return {
286
+ byName: face.path,
287
+ blank: (name) => face.has(name) && face.path(name) === void 0,
288
+ ...face.encoding ? { builtIn: face.encoding } : {}
289
+ };
290
+ }
291
+ const stream = compact instanceof PdfStream ? compact : truetype;
292
+ if (!(stream instanceof PdfStream)) return void 0;
293
+ const bytes = file.streamData(stream);
294
+ const glyf = outlineSource(bytes);
295
+ if (glyf) {
296
+ const named = postGlyphNames(bytes);
297
+ const gidOf = (name) => {
298
+ const gid = named?.get(name) ?? numberedGlyph(name);
299
+ return gid !== void 0 && gid < glyf.count ? gid : void 0;
300
+ };
301
+ return {
302
+ byName: (name) => {
303
+ const gid = gidOf(name);
304
+ return gid === void 0 ? void 0 : glyf.path(gid);
305
+ },
306
+ blank: (name) => {
307
+ const gid = gidOf(name);
308
+ return gid !== void 0 && glyf.path(gid) === void 0;
309
+ },
310
+ byIndex: (gid) => gid < glyf.count ? glyf.path(gid) : void 0,
311
+ indexed: !glyf.cmap
312
+ };
313
+ }
314
+ const cff = openTypeCff(bytes) ?? bytes;
315
+ const outlines = cffOutlineSource(cff);
316
+ if (!outlines) return void 0;
317
+ const names = cffNameToGid(cff);
318
+ const gidOf = (name) => {
319
+ const gid = names?.get(name) ?? numberedGlyph(name);
320
+ return gid !== void 0 && gid < outlines.count ? gid : void 0;
321
+ };
322
+ return {
323
+ byName: (name) => {
324
+ const gid = gidOf(name);
325
+ return gid === void 0 ? void 0 : outlines.path(gid);
326
+ },
327
+ blank: (name) => {
328
+ const gid = gidOf(name);
329
+ return gid !== void 0 && outlines.path(gid) === void 0;
330
+ }
331
+ };
332
+ } catch {
333
+ return;
334
+ }
335
+ }
336
+ /**
337
+ * The glyph index a name carries, for the names that carry one.
338
+ *
339
+ * A subsetter that drops a font's `cmap` renames its glyphs after their
340
+ * INDEX — `g24`, `glyph24`, `index24`, `cid24` — and that name is then the only
341
+ * way back to the outline. bug1151216.pdf names them `g24`, `g381`, `g3`, and
342
+ * its three lines of prices are drawn from nothing else.
343
+ */
344
+ function numberedGlyph(name) {
345
+ const hex = /^g([0-9a-f]{4})$/u.exec(name);
346
+ if (hex) return Number.parseInt(hex[1], 16);
347
+ const m = /^(?:g|glyph|index|cid|G)(\d+)$/u.exec(name);
348
+ const n = m ? Number(m[1]) : NaN;
349
+ return Number.isFinite(n) && n >= 0 && n < MAX_GLYPH_INDEX ? n : void 0;
350
+ }
351
+ /** No font holds more glyphs than this; a bigger number is not an index. */
352
+ var MAX_GLYPH_INDEX = 65536;
133
353
  /** §9.7.4.2 `/CIDToGIDMap` — a stream of two-byte glyph indices, CID by CID. */
134
354
  function readCidToGid(file, cidFont) {
135
355
  const map = file.resolve(cidFont.get("CIDToGIDMap") ?? PDF_NULL);
@@ -194,13 +414,21 @@ function fontMatrix(file, fontDict) {
194
414
  * Returns `undefined` where the font names nothing, so the caller keeps its
195
415
  * Latin-1 reading rather than replacing it with an empty map.
196
416
  */
197
- function namedGlyphs(file, fontDict) {
417
+ function namedGlyphs(file, fontDict, statesUnicode, glyph) {
198
418
  const names = differences(file, fontDict);
199
419
  if (names.size === 0) return void 0;
200
420
  const out = /* @__PURE__ */ new Map();
201
421
  for (const [code, name] of names) {
202
422
  const text = textForGlyphName(name);
203
- if (text !== void 0) out.set(code, text);
423
+ if (text !== void 0) {
424
+ out.set(code, text);
425
+ continue;
426
+ }
427
+ if (glyph.blank(name)) {
428
+ out.set(code, " ");
429
+ continue;
430
+ }
431
+ if (!statesUnicode && glyph.draws(name)) out.set(code, UNANSWERABLE);
204
432
  }
205
433
  if (out.size > 0) return out;
206
434
  if (asName(file.resolve(fontDict.get("Subtype") ?? PDF_NULL)) !== "Type3") return void 0;
@@ -208,6 +436,63 @@ function namedGlyphs(file, fontDict) {
208
436
  for (const code of names.keys()) unreadable.set(code, "�");
209
437
  return unreadable;
210
438
  }
439
+ /**
440
+ * §9.7.4.3 `/DW2` and `/W2` — how far the pen drops for one glyph, in 1000-unit
441
+ * text space.
442
+ *
443
+ * `/DW2`'s default is `[880 -1000]`: the vertical origin sits 880 above the
444
+ * horizontal one and the displacement is a full em DOWN. Only the displacement
445
+ * is wanted here — the origin shifts where the glyph is drawn, which a reader
446
+ * re-setting the words in another face does not reproduce anyway.
447
+ */
448
+ function cidVerticalAdvance(file, fontDict) {
449
+ const cid = descendantFont(file, fontDict);
450
+ const dw2 = file.resolve(cid.get("DW2") ?? PDF_NULL);
451
+ const stated = Array.isArray(dw2) && typeof file.resolve(dw2[1] ?? PDF_NULL) === "number" ? file.resolve(dw2[1]) : DEFAULT_DW2_DISPLACEMENT;
452
+ const perCode = /* @__PURE__ */ new Map();
453
+ const w2 = file.resolve(cid.get("W2") ?? PDF_NULL);
454
+ if (Array.isArray(w2)) for (let i = 0; i < w2.length;) {
455
+ const first = file.resolve(w2[i] ?? PDF_NULL);
456
+ const next = file.resolve(w2[i + 1] ?? PDF_NULL);
457
+ if (typeof first !== "number") break;
458
+ if (Array.isArray(next)) {
459
+ for (let k = 0; k + 2 < next.length; k += 3) {
460
+ const v = file.resolve(next[k] ?? PDF_NULL);
461
+ if (typeof v === "number") perCode.set(first + k / 3, v);
462
+ }
463
+ i += 2;
464
+ } else if (typeof next === "number" && typeof file.resolve(w2[i + 2] ?? PDF_NULL) === "number") {
465
+ const v = file.resolve(w2[i + 2]);
466
+ for (let c = first; c <= next && c - first < MAX_W2_RANGE; c++) perCode.set(c, v);
467
+ i += 5;
468
+ } else break;
469
+ }
470
+ return (code) => perCode.get(code) ?? stated;
471
+ }
472
+ /** §9.7.4.3 — `/DW2`'s default displacement: one em down the page. */
473
+ var DEFAULT_DW2_DISPLACEMENT = -1e3;
474
+ /** A `/W2` range wider than this is not walked out code by code. */
475
+ var MAX_W2_RANGE = 65536;
476
+ /**
477
+ * Annex D.2 — code → text for the encoding a simple font is read through, under
478
+ * whatever `/Differences` restates.
479
+ *
480
+ * `/Encoding` either names one of the base encodings, or is a dictionary that
481
+ * may name one in `/BaseEncoding`, or is absent — and absent means the encoding
482
+ * built into the FACE. Only the standard Latin faces can be answered for there:
483
+ * a file that names them embeds no program, and what they are is known
484
+ * (§9.6.2.2). An embedded font's built-in encoding lives in the program and a
485
+ * substituted face has none worth guessing at, so both keep the Latin-1
486
+ * reading, which is what the codes of such a file nearly always are.
487
+ *
488
+ * @returns Code → glyph NAME, or `undefined` where nothing better than Latin-1
489
+ * is known.
490
+ */
491
+ function baseEncoding(file, fontDict) {
492
+ const encoding = file.resolve(fontDict.get("Encoding") ?? PDF_NULL);
493
+ const named = encoding instanceof PdfName ? encoding.value : encoding instanceof Map ? asName(file.resolve(encoding.get("BaseEncoding") ?? PDF_NULL)) : "";
494
+ return named.length > 0 ? baseEncodingTable(named) : isStandardLatinFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL))) ? standardEncodingTable() : void 0;
495
+ }
211
496
  /** §9.6.6.1 `/Encoding` `/Differences` — code → glyph name, as the array runs. */
212
497
  function differences(file, fontDict) {
213
498
  const out = /* @__PURE__ */ new Map();
@@ -223,6 +508,42 @@ function differences(file, fontDict) {
223
508
  }
224
509
  return out;
225
510
  }
511
+ /**
512
+ * §9.8.2 — the family a run states: the file's own name for the face, plus what
513
+ * the file says the face IS.
514
+ *
515
+ * The name is a hint for the substitution and nothing more, unless the reader
516
+ * can lift the program itself (§9.9 `/FontFile2`) — then it is the key the face
517
+ * is filed under and must be left exactly as it is. Everything else is
518
+ * substituted by name, and TeX and PostScript producers embed Type 1 and CFF
519
+ * programs under names no table knows: `NimbusRomNo9L-Regu`, `LMRoman10`,
520
+ * `CMR10`, `stonesans`. Every one of those pages came back set in a grotesque
521
+ * — the loudest single difference between our page and a serif document's.
522
+ *
523
+ * The descriptor states the class outright, so where the name says nothing the
524
+ * flags do: bit 1 is FixedPitch and bit 2 Serif.
525
+ *
526
+ * @param file The document.
527
+ * @param fontDict The font dictionary.
528
+ * @param isType0 Whether it is a composite font, whose descendant owns the
529
+ * descriptor.
530
+ * @returns The family name for the run, or `undefined` where the font has none.
531
+ */
532
+ function runFontName(file, fontDict, isType0) {
533
+ const name = embeddedFontName(file, fontDict);
534
+ if (name === void 0 || hasLiftableProgram(file, fontDict)) return name;
535
+ if (resolveFamilyStyle(name).key !== "arimo") return name;
536
+ const owner = isType0 ? descendantFont(file, fontDict) : fontDict;
537
+ const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
538
+ if (!(descriptor instanceof Map)) return name;
539
+ const flags = asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0);
540
+ if ((flags & FLAG_FIXED_PITCH) !== 0) return `${name} monospace`;
541
+ if ((flags & FLAG_SERIF) !== 0) return `${name} serif`;
542
+ return name;
543
+ }
544
+ /** §9.8.2 `/Flags` — bit 1 is FixedPitch, bit 2 Serif (bits numbered from 1). */
545
+ var FLAG_FIXED_PITCH = 1;
546
+ var FLAG_SERIF = 2;
226
547
  /** §9.8.2 `/Flags` — bit 7 is Italic, bit 19 ForceBold (bits numbered from 1). */
227
548
  var FLAG_ITALIC = 64;
228
549
  var FLAG_FORCE_BOLD = 1 << 18;
@@ -0,0 +1,43 @@
1
+ import { PathSeg } from './content.js';
2
+ /** A reader for one font program's outlines, with its tables located once. */
3
+ export interface OutlineSource {
4
+ /** The glyph's contours in a one-unit em, or `undefined` for an empty one. */
5
+ readonly path: (gid: number) => Array<PathSeg> | undefined;
6
+ /**
7
+ * How many glyphs the program holds. A glyph INSIDE that count which draws
8
+ * nothing is a blank one — a space — and not a glyph the program lacks.
9
+ */
10
+ readonly count: number;
11
+ /**
12
+ * Whether an sfnt program carries a `cmap` at all. Without one there is no
13
+ * way to reach a glyph by character (§9.6.6.4), which says what the file
14
+ * means by its codes: they are glyph INDICES. Absent for a bare CFF, which
15
+ * has no such table and is addressed by NAME through its charset.
16
+ */
17
+ readonly cmap?: boolean;
18
+ }
19
+ /**
20
+ * Prepare an embedded TrueType program for outline reading (§9.6.6).
21
+ *
22
+ * @param program The raw `/FontFile2` bytes.
23
+ * @returns A reader, or `undefined` where the program carries no `glyf`/`loca`
24
+ * pair — an OpenType/CFF program under `/FontFile2` is one such, and
25
+ * its outlines are charstrings this does not read.
26
+ */
27
+ export declare function outlineSource(program: Uint8Array): OutlineSource | undefined;
28
+ /**
29
+ * §post format 2.0 — the NAME each glyph of a TrueType program goes by.
30
+ *
31
+ * A legacy eight-bit font is reached through this table and nothing else. Its
32
+ * program carries no `cmap`, so a code cannot be turned into a character and
33
+ * then into a glyph; what it can be turned into is a glyph NAME, through the
34
+ * encoding the font dictionary states (Annex D.2) — and the name is looked up
35
+ * here. TrueType_without_cmap.pdf is an Armenian face, Masis, whose `i` draws
36
+ * ի: read as text its line came back "'>in", and drawn by name it is the line
37
+ * the page shows.
38
+ *
39
+ * @param program The raw `/FontFile2` bytes.
40
+ * @returns Glyph name → index, or `undefined` where the table is missing or
41
+ * states no names of its own (formats 1.0 and 3.0).
42
+ */
43
+ export declare function postGlyphNames(program: Uint8Array): Map<string, number> | undefined;