reamkit 1.30.0 → 1.31.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 (78) hide show
  1. package/README.md +60 -24
  2. package/dist/esm/core/converter/project.js +3 -1
  3. package/dist/esm/core/crypto/offcrypto.js +1 -1
  4. package/dist/esm/core/document-model/index.d.ts +1 -1
  5. package/dist/esm/core/document-model/types.d.ts +63 -0
  6. package/dist/esm/core/font/index.d.ts +2 -0
  7. package/dist/esm/core/font/ttf-build.d.ts +113 -0
  8. package/dist/esm/core/font/ttf-build.js +1224 -0
  9. package/dist/esm/core/font/ttf-subset.d.ts +19 -0
  10. package/dist/esm/core/font/ttf-subset.js +14 -1
  11. package/dist/esm/core/fonts/provider.d.ts +17 -0
  12. package/dist/esm/core/fonts/provider.js +27 -2
  13. package/dist/esm/core/fonts/remote-fonts.js +8 -1
  14. package/dist/esm/core/ir/flow.d.ts +71 -1
  15. package/dist/esm/core/numbering/index.d.ts +1 -1
  16. package/dist/esm/core/numbering/state.d.ts +11 -1
  17. package/dist/esm/core/numbering/state.js +10 -1
  18. package/dist/esm/core/style-cascade/resolver.js +35 -4
  19. package/dist/esm/core/style-cascade/types.d.ts +19 -1
  20. package/dist/esm/core/style-cascade/types.js +3 -0
  21. package/dist/esm/index.d.ts +2 -2
  22. package/dist/esm/layout/page-doc.d.ts +10 -3
  23. package/dist/esm/layout/styled-layout.d.ts +48 -7
  24. package/dist/esm/layout/styled-layout.js +881 -119
  25. package/dist/esm/layout/turned-section.d.ts +38 -0
  26. package/dist/esm/layout/turned-section.js +193 -0
  27. package/dist/esm/pdf/styled-page-emitter.js +78 -2
  28. package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
  29. package/dist/esm/pdf-reader/cff-outline.js +169 -21
  30. package/dist/esm/pdf-reader/content.d.ts +53 -0
  31. package/dist/esm/pdf-reader/content.js +9 -1
  32. package/dist/esm/pdf-reader/display.d.ts +36 -0
  33. package/dist/esm/pdf-reader/display.js +66 -1
  34. package/dist/esm/pdf-reader/document.js +5 -1
  35. package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
  36. package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
  37. package/dist/esm/pdf-reader/encodings.d.ts +8 -0
  38. package/dist/esm/pdf-reader/encodings.js +25 -3
  39. package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
  40. package/dist/esm/pdf-reader/face-outlines.js +362 -0
  41. package/dist/esm/pdf-reader/figures.d.ts +52 -0
  42. package/dist/esm/pdf-reader/figures.js +433 -0
  43. package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
  44. package/dist/esm/pdf-reader/flow-build.js +345 -26
  45. package/dist/esm/pdf-reader/font.js +321 -36
  46. package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
  47. package/dist/esm/pdf-reader/glyf-outline.js +135 -1
  48. package/dist/esm/pdf-reader/glyph-names.js +154 -1
  49. package/dist/esm/pdf-reader/layout.d.ts +93 -0
  50. package/dist/esm/pdf-reader/layout.js +1703 -214
  51. package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
  52. package/dist/esm/pdf-reader/page-numbers.js +167 -0
  53. package/dist/esm/pdf-reader/tagged.js +182 -23
  54. package/dist/esm/pdf-reader/text.d.ts +6 -3
  55. package/dist/esm/pdf-reader/text.js +98 -9
  56. package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
  57. package/dist/esm/pdf-reader/type1-outline.js +63 -8
  58. package/dist/esm/pdf-reader/vector.js +71 -1
  59. package/dist/esm/word/doc/doc-reader.js +6 -2
  60. package/dist/esm/word/doc/doc-text.d.ts +6 -0
  61. package/dist/esm/word/doc/doc-text.js +19 -1
  62. package/dist/esm/word/document-parser.d.ts +2 -2
  63. package/dist/esm/word/document-parser.js +11 -1
  64. package/dist/esm/word/docx-reader.js +5 -3
  65. package/dist/esm/word/docx-writer.js +207 -21
  66. package/dist/esm/word/drawing-parser.d.ts +5 -3
  67. package/dist/esm/word/drawing-parser.js +49 -8
  68. package/dist/esm/word/font-embed.d.ts +30 -0
  69. package/dist/esm/word/font-embed.js +173 -0
  70. package/dist/esm/word/font-table.d.ts +10 -0
  71. package/dist/esm/word/font-table.js +13 -1
  72. package/dist/esm/word/index.js +1 -1
  73. package/dist/esm/word/numbering-parser.d.ts +3 -1
  74. package/dist/esm/word/numbering-parser.js +2 -1
  75. package/dist/esm/word/paragraph-properties.d.ts +7 -6
  76. package/dist/esm/word/paragraph-properties.js +14 -2
  77. package/dist/esm/word/run-properties.js +26 -0
  78. package/package.json +8 -3
@@ -5,10 +5,10 @@ import { parseToUnicodeCMap } from "./cmap.js";
5
5
  import { decodePredefined, predefinedCMap, splitPredefined } from "./predefined-cmap.js";
6
6
  import { isZapfDingbats, zapfDingbatsChar } from "./dingbats.js";
7
7
  import { textForGlyphName } from "./glyph-names.js";
8
- import { cffCidToGid, cffNameToGid, cffOutlineSource, openTypeCff } from "./cff-outline.js";
8
+ import { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable, winAnsiLatinName } from "./encodings.js";
9
+ import { cffCidToGid, cffFsType, cffNameToGid, cffOutlineSource, cffSpaceAdvance, openTypeCff } from "./cff-outline.js";
9
10
  import { type1Font } from "./type1-outline.js";
10
- import { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable } from "./encodings.js";
11
- import { outlineSource, postGlyphNames } from "./glyf-outline.js";
11
+ import { cmapSubtables, outlineSource, postGlyphNames, sfntFsType, sfntSpaceAdvance } from "./glyf-outline.js";
12
12
  import { standardFace, standardWidth } from "./standard-widths.js";
13
13
  import { eachPageFont, embeddedFontName, hasLiftableProgram, programStyle } from "./embedded-fonts.js";
14
14
  //#region src/pdf-reader/font.ts
@@ -69,6 +69,7 @@ function buildContentFont(file, fontDict) {
69
69
  const dingbats = !isType0 && isZapfDingbats(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
70
70
  const stated = file.resolve(fontDict.get("Encoding") ?? PDF_NULL);
71
71
  const statesEncoding = stated instanceof Map || stated instanceof PdfName && baseEncodingTable(stated.value) !== void 0;
72
+ const numbered = !isType0 && toUnicode.size === 0 && glyphNames.size === 0 && stated === PDF_NULL && glyphs?.numbersGlyphs?.() === true;
72
73
  const baseNames = isType0 ? void 0 : statesEncoding ? baseEncoding(file, fontDict) : glyphs?.builtIn ?? baseEncoding(file, fontDict);
73
74
  const fromBase = /* @__PURE__ */ new Map();
74
75
  for (const [code, glyph] of baseNames ?? []) {
@@ -79,18 +80,24 @@ function buildContentFont(file, fontDict) {
79
80
  const style = faceStyle(file, fontDict, isType0);
80
81
  const name = runFontName(file, fontDict, isType0);
81
82
  const type3 = asName(file.resolve(fontDict.get("Subtype") ?? PDF_NULL)) === "Type3" ? type3Face(file, fontDict) : void 0;
82
- const decodeOne = (code) => (indices ? UNANSWERABLE : void 0) ?? unicode.get(code) ?? fromProgramFor(code) ?? (named ? decodePredefined(named, code) : void 0) ?? fromNames?.get(code) ?? (glyphNames.get(code) === NOTDEF ? "" : void 0) ?? (dingbats ? zapfDingbatsChar(code) : void 0) ?? fromBase.get(code) ?? (bytesPerCode === 1 ? latin1(code) : UNANSWERABLE);
83
+ const decodeOne = (code) => (indices || numbered ? UNANSWERABLE : void 0) ?? unicode.get(code) ?? fromProgramFor(code) ?? (named ? decodePredefined(named, code) : void 0) ?? fromNames?.get(code) ?? (glyphNames.get(code) === NOTDEF ? "" : void 0) ?? (dingbats ? zapfDingbatsChar(code) : void 0) ?? fromBase.get(code) ?? (bytesPerCode === 1 ? latin1(code) : UNANSWERABLE);
84
+ const composite = isType0 ? compositeGlyphs(file, fontDict) : void 0;
83
85
  const simple = simpleWidths(file, fontDict, decodeOne);
84
- const width = isType0 ? cidWidths(file, fontDict) : type3 ? (code) => simple(code) * type3.matrix[0] * 1e3 : simple;
86
+ const width = isType0 ? cidWidths(file, fontDict) : type3 ? (code) => simple.width(code) * type3.matrix[0] * 1e3 : simple.width;
87
+ const spaceWidth = isType0 || type3 || namesOf.get(32) !== "space" && unicode.get(32) !== " " ? void 0 : simple.stated(32);
88
+ const fixedPitch = !isType0 && !type3 && fixedPitchOf(file, fontDict);
85
89
  return {
86
90
  bytesPerCode,
87
91
  ...named ? { splitCodes: (b) => splitPredefined(named, b) } : {},
88
- ...isType0 ? outlineOf(file, fontDict, decodeOne) : simpleOutlineOf(decodeOne, namesOf, glyphs),
92
+ ...isType0 ? outlineOf(composite, decodeOne) : simpleOutlineOf(decodeOne, namesOf, glyphs, numbered),
93
+ ...name !== void 0 && !type3 ? programOf(file, fontDict, isType0, isType0 ? compositeGlyph(composite, fontDict, file) : simpleGlyph(glyphs, namesOf, baseNames === baseEncodingTable("WinAnsiEncoding")), isType0 ? composite : glyphs) : {},
89
94
  ...named?.vertical ? { verticalAdvance: cidVerticalAdvance(file, fontDict) } : {},
90
95
  ...type3 ? { type3 } : {},
91
96
  ...name !== void 0 ? { name } : {},
92
97
  decode: (codes) => codes.map((c) => lettersOf(readable(decodeOne(c)))).join(""),
93
98
  width,
99
+ ...spaceWidth !== void 0 ? { spaceWidth } : {},
100
+ ...fixedPitch ? { fixedPitch: true } : {},
94
101
  ...style
95
102
  };
96
103
  }
@@ -316,46 +323,77 @@ function programCharacters(file, owner) {
316
323
  return byGlyph.size > 0 ? byGlyph : void 0;
317
324
  }
318
325
  /**
319
- * §9.6.6 — the outline a code draws, for a code that stands for no character.
320
- *
321
- * The glyph is there even when the character is not: `/Encoding /Identity-H`
322
- * makes the code a CID, `/CIDToGIDMap` turns that into a glyph index, and the
323
- * embedded program holds the contours. complex_ttf_font.pdf is eight lines of
324
- * Arabic in a subset with no `cmap` and no `/ToUnicode`, and every one of them
325
- * was dropped.
326
- *
327
- * Deliberately NOT a fallback for text: a code the font CAN answer for is set
328
- * as type, and only the unanswerable ones are traced.
326
+ * §9.7.4 — a composite font's embedded program, read by glyph: the code is a
327
+ * CID, `/CIDToGIDMap` (or, for a CID-keyed CFF, its charset) turns that into a
328
+ * glyph index, and the program holds the contours.
329
329
  *
330
- * @param file The owning file.
331
- * @param fontDict The Type 0 font dictionary.
332
- * @param decodeOne What one code comes to, to tell the two cases apart.
333
- * @returns The `outline` field of a {@link ContentFont}, or nothing where the
334
- * program carries no outlines this reads.
330
+ * @param file The owning file.
331
+ * @param fontDict The Type 0 font dictionary.
332
+ * @returns The program's outlines and how a CID reaches them, or `undefined`
333
+ * where the font embeds no program this reads.
335
334
  */
336
- function outlineOf(file, fontDict, decodeOne) {
335
+ function compositeGlyphs(file, fontDict) {
337
336
  const cidFont = descendantFont(file, fontDict);
338
337
  const descriptor = file.resolve(cidFont.get("FontDescriptor") ?? PDF_NULL);
339
- if (!(descriptor instanceof Map)) return {};
338
+ if (!(descriptor instanceof Map)) return void 0;
340
339
  const truetype = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
341
340
  const compact = file.resolve(descriptor.get("FontFile3") ?? PDF_NULL);
342
341
  const program = truetype instanceof PdfStream ? truetype : compact;
343
- if (!(program instanceof PdfStream)) return {};
342
+ if (!(program instanceof PdfStream)) return void 0;
344
343
  let source;
345
344
  let charsetCids;
345
+ let fsType;
346
+ let spaceAdvance;
346
347
  try {
347
348
  const bytes = file.streamData(program);
348
349
  source = outlineSource(bytes);
350
+ fsType = sfntFsType(bytes);
351
+ spaceAdvance = sfntSpaceAdvance(bytes);
349
352
  if (!source) {
350
353
  const cff = openTypeCff(bytes) ?? bytes;
351
354
  source = cffOutlineSource(cff);
352
355
  charsetCids = source ? cffCidToGid(cff) : void 0;
356
+ fsType ??= cffFsType(cff);
353
357
  }
354
358
  } catch {
355
- return {};
359
+ return;
356
360
  }
357
- if (!source) return {};
361
+ if (!source) return void 0;
358
362
  const cidToGid = readCidToGid(file, cidFont);
363
+ return {
364
+ source,
365
+ glyphOf: (cid) => {
366
+ const mapped = cidToGid ? cidToGid[cid] : cid;
367
+ if (mapped === void 0) return void 0;
368
+ return charsetCids ? charsetCids.get(mapped) : mapped;
369
+ },
370
+ looseGlyphOf: (cid) => {
371
+ const mapped = cidToGid ? cidToGid[cid] ?? 0 : cid;
372
+ return charsetCids ? charsetCids.get(mapped) ?? mapped : mapped;
373
+ },
374
+ ...fsType !== void 0 ? { fsType } : {},
375
+ ...spaceAdvance !== void 0 ? { spaceAdvance } : {}
376
+ };
377
+ }
378
+ /**
379
+ * §9.6.6 — the outline a code draws, for a code that stands for no character.
380
+ *
381
+ * The glyph is there even when the character is not: `/Encoding /Identity-H`
382
+ * makes the code a CID, `/CIDToGIDMap` turns that into a glyph index, and the
383
+ * embedded program holds the contours. complex_ttf_font.pdf is eight lines of
384
+ * Arabic in a subset with no `cmap` and no `/ToUnicode`, and every one of them
385
+ * was dropped.
386
+ *
387
+ * Deliberately NOT a fallback for text: a code the font CAN answer for is set
388
+ * as type, and only the unanswerable ones are traced.
389
+ *
390
+ * @param glyphs The font's program, read by glyph (see `compositeGlyphs`).
391
+ * @param decodeOne What one code comes to, to tell the two cases apart.
392
+ * @returns The `outline` field of a {@link ContentFont}, or nothing where the
393
+ * program carries no outlines this reads.
394
+ */
395
+ function outlineOf(glyphs, decodeOne) {
396
+ if (!glyphs) return {};
359
397
  return { outline: {
360
398
  matrix: [
361
399
  1,
@@ -367,13 +405,28 @@ function outlineOf(file, fontDict, decodeOne) {
367
405
  ],
368
406
  path: (code) => {
369
407
  if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
370
- const mapped = cidToGid ? cidToGid[code] ?? 0 : code;
371
- const gid = charsetCids ? charsetCids.get(mapped) ?? mapped : mapped;
372
- return source.path(gid);
408
+ return glyphs.source.path(glyphs.looseGlyphOf(code));
373
409
  }
374
410
  } };
375
411
  }
376
412
  /**
413
+ * §9.7.5.2 — the glyph ANY code of a composite font draws, for a writer that
414
+ * embeds the face.
415
+ *
416
+ * Only under `Identity-H` or `Identity-V`, where the code is the CID: any other
417
+ * CMap turns codes into CIDs by a table of its own, and a glyph looked up by
418
+ * the code there would be some other character's.
419
+ */
420
+ function compositeGlyph(glyphs, fontDict, file) {
421
+ const encoding = asName(file.resolve(fontDict.get("Encoding") ?? PDF_NULL));
422
+ if (!glyphs || encoding !== "Identity-H" && encoding !== "Identity-V") return void 0;
423
+ return (code) => {
424
+ const gid = glyphs.glyphOf(code);
425
+ if (gid === void 0 || gid <= 0 || gid >= glyphs.source.count) return void 0;
426
+ return glyphs.source.path(gid) ?? [];
427
+ };
428
+ }
429
+ /**
377
430
  * §9.6.6 — the outline a SIMPLE font's code draws, for a code that stands for
378
431
  * no character.
379
432
  *
@@ -388,9 +441,12 @@ function outlineOf(file, fontDict, decodeOne) {
388
441
  * @param fontDict The simple font's dictionary.
389
442
  * @param decodeOne What one code comes to, to tell the two cases apart.
390
443
  * @param nameOf The glyph name a code selects, where the font states one.
444
+ * @param numbered Whether the program numbers its glyphs (see
445
+ * {@link numbersGlyphs}), which its `cmap` then reaches by
446
+ * the code.
391
447
  * @returns The `outline` field of a {@link ContentFont}, or nothing.
392
448
  */
393
- function simpleOutlineOf(decodeOne, nameOf, source) {
449
+ function simpleOutlineOf(decodeOne, nameOf, source, numbered) {
394
450
  if (!source) return {};
395
451
  return { outline: {
396
452
  matrix: [
@@ -403,6 +459,10 @@ function simpleOutlineOf(decodeOne, nameOf, source) {
403
459
  ],
404
460
  path: (code) => {
405
461
  if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
462
+ if (numbered) {
463
+ const segs = source.glyph(code, void 0);
464
+ return segs !== void 0 && segs.length > 0 ? [...segs] : void 0;
465
+ }
406
466
  const name = nameOf.get(code) ?? source.builtIn?.get(code);
407
467
  if (name === void 0) return source.byIndex?.(code);
408
468
  return source.byName(name);
@@ -437,20 +497,34 @@ function simpleGlyphs(file, fontDict) {
437
497
  return {
438
498
  byName: face.path,
439
499
  blank: (name) => face.has(name) && face.path(name) === void 0,
440
- ...face.encoding ? { builtIn: face.encoding } : {}
500
+ ...face.encoding ? { builtIn: face.encoding } : {},
501
+ glyph: (_code, name) => name === void 0 || name === NOTDEF || !face.has(name) ? void 0 : face.path(name) ?? [],
502
+ ...face.fsType !== void 0 ? { fsType: face.fsType } : {},
503
+ ...spaceOf(face.advance("space"))
441
504
  };
442
505
  }
443
506
  const stream = compact instanceof PdfStream ? compact : truetype;
444
507
  if (!(stream instanceof PdfStream)) return void 0;
445
508
  const bytes = file.streamData(stream);
446
509
  const glyf = outlineSource(bytes);
510
+ const fsType = sfntFsType(bytes);
447
511
  if (glyf) {
448
512
  const named = postGlyphNames(bytes);
449
513
  const gidOf = (name) => {
450
514
  const gid = named?.get(name) ?? numberedGlyph(name, numbering);
451
515
  return gid !== void 0 && gid < glyf.count ? gid : void 0;
452
516
  };
517
+ const flags = asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0);
518
+ const encoding = file.resolve(fontDict.get("Encoding") ?? PDF_NULL);
519
+ const byCode = trueTypeGlyphs(bytes, (flags & FLAG_SYMBOLIC) !== 0 && (flags & FLAG_NONSYMBOLIC) === 0, encoding instanceof Map || encoding instanceof PdfName, gidOf);
453
520
  return {
521
+ glyph: (code, name) => {
522
+ const gid = byCode(code, name);
523
+ if (gid === void 0 || gid <= 0 || gid >= glyf.count) return void 0;
524
+ return glyf.path(gid) ?? [];
525
+ },
526
+ ...fsType !== void 0 ? { fsType } : {},
527
+ ...spaceOf(sfntSpaceAdvance(bytes)),
454
528
  byName: (name) => {
455
529
  const gid = gidOf(name);
456
530
  return gid === void 0 ? void 0 : glyf.path(gid);
@@ -460,7 +534,8 @@ function simpleGlyphs(file, fontDict) {
460
534
  return gid !== void 0 && glyf.path(gid) === void 0;
461
535
  },
462
536
  byIndex: (gid) => gid < glyf.count ? glyf.path(gid) : void 0,
463
- indexed: !glyf.cmap
537
+ indexed: !glyf.cmap,
538
+ numbersGlyphs: () => numbersGlyphs(bytes, glyf, named, (code) => byCode(code, void 0))
464
539
  };
465
540
  }
466
541
  const cff = openTypeCff(bytes) ?? bytes;
@@ -471,7 +546,14 @@ function simpleGlyphs(file, fontDict) {
471
546
  const gid = names?.get(name) ?? numberedGlyph(name, numbering);
472
547
  return gid !== void 0 && gid < outlines.count ? gid : void 0;
473
548
  };
549
+ const stated = fsType ?? cffFsType(cff);
474
550
  return {
551
+ glyph: (_code, name) => {
552
+ const gid = name === void 0 || name === NOTDEF ? void 0 : gidOf(name);
553
+ return gid === void 0 || gid === 0 ? void 0 : outlines.path(gid) ?? [];
554
+ },
555
+ ...stated !== void 0 ? { fsType: stated } : {},
556
+ ...spaceOf(sfntSpaceAdvance(bytes) ?? cffSpaceAdvance(cff)),
475
557
  byName: (name) => {
476
558
  const gid = gidOf(name);
477
559
  return gid === void 0 ? void 0 : outlines.path(gid);
@@ -485,6 +567,190 @@ function simpleGlyphs(file, fontDict) {
485
567
  return;
486
568
  }
487
569
  }
570
+ /** The `spaceAdvance` field, where a program states a sensible one. */
571
+ function spaceOf(advance) {
572
+ return advance !== void 0 && advance > 0 && advance < 1e3 ? { spaceAdvance: advance } : {};
573
+ }
574
+ /**
575
+ * §9.6.6.4 — how a SIMPLE TrueType font's code reaches a glyph: through the
576
+ * program's `cmap`, and which subtable decides what the code is looked up as.
577
+ *
578
+ * A face that states an encoding turns the code into its glyph NAME, and the
579
+ * name into a character for the Unicode subtable (3,1), a Mac Roman code for
580
+ * (1,0), or an entry of the program's own `post` names — and past ASCII, only
581
+ * that: the same byte is another letter in Mac Roman, and a glyph found by the
582
+ * code there is the wrong one. A symbolic face with a Windows symbol subtable
583
+ * (3,0), and a face that states no encoding, is looked up by the CODE: in
584
+ * (3,0), where it stands in one of four ranges (itself, or behind 0xF000,
585
+ * 0xF100 or 0xF200), or byte for byte in (1,0) — its names the fallback, as a
586
+ * viewer tries them. A program with no `cmap` at all is read by index: the
587
+ * code IS the glyph.
588
+ *
589
+ * @param program The raw `/FontFile2` bytes.
590
+ * @param symbolic Whether `/Flags` call the face symbolic (bit 3, not bit 6).
591
+ * @param hasEncoding Whether the font dictionary states an `/Encoding`.
592
+ * @param byName The glyph a name selects through `post` or the
593
+ * numbered-name convention (see {@link numberedGlyph}).
594
+ * @returns Code (and the name the encoding gives it) → glyph index, or
595
+ * `undefined` where nothing reaches one.
596
+ */
597
+ function trueTypeGlyphs(program, symbolic, hasEncoding, byName) {
598
+ let tables;
599
+ return (code, name) => {
600
+ tables ??= cmapSubtables(program);
601
+ const unicode = tables.get("3,1") ?? tables.get("0,3") ?? tables.get("0,1") ?? tables.get("0,0");
602
+ const symbol = tables.get("3,0");
603
+ const roman = tables.get("1,0");
604
+ const bySymbol = () => {
605
+ if (symbol) {
606
+ for (const high of SYMBOL_RANGES) {
607
+ const gid = symbol(high | code);
608
+ if (gid > 0) return gid;
609
+ }
610
+ return 0;
611
+ }
612
+ return roman ? roman(code) : 0;
613
+ };
614
+ const byGlyphName = () => {
615
+ if (name === void 0 || name === NOTDEF) return 0;
616
+ const text = textForGlyphName(name);
617
+ const cp = text !== void 0 && [...text].length === 1 ? text.codePointAt(0) : void 0;
618
+ const viaUnicode = unicode && cp !== void 0 ? unicode(cp) : 0;
619
+ if (viaUnicode > 0) return viaUnicode;
620
+ const mac = macRomanCode(name);
621
+ const viaRoman = roman && mac !== void 0 ? roman(mac) : 0;
622
+ if (viaRoman > 0) return viaRoman;
623
+ return byName(name) ?? 0;
624
+ };
625
+ const gid = symbolic && symbol !== void 0 ? bySymbol() || byGlyphName() : hasEncoding ? byGlyphName() || (code < ASCII_END ? bySymbol() : 0) : bySymbol() || byGlyphName() || (unicode ? unicode(code) : 0);
626
+ if (gid > 0) return gid;
627
+ return tables.size === 0 ? code : void 0;
628
+ };
629
+ }
630
+ /** Below this, a byte is the same character in every encoding a simple font uses. */
631
+ var ASCII_END = 128;
632
+ /**
633
+ * §9.6.6.4 — whether a TrueType program NUMBERS its glyphs, where a font would
634
+ * code characters with them: its codes are the order some producer came to
635
+ * each glyph in, and read as Latin-1 they spell nothing.
636
+ *
637
+ * Nothing in such a program says what its codes are — no Unicode subtable, no
638
+ * glyph names — and its SPACE says what they are not. A text face's blank
639
+ * glyph is its space, and Latin-1 puts the space at 32. A program that reaches
640
+ * no glyph at 32, and a blank one where Latin-1 reads a mark, is not coded in
641
+ * Latin-1: comments.pdf's chart faces put their spaces at 35 and 34, "#" and
642
+ * `"`.
643
+ *
644
+ * @param program The raw `/FontFile2` bytes.
645
+ * @param glyf Its outlines.
646
+ * @param named The glyphs its `post` table names.
647
+ * @param gidOf The glyph a code reaches, looked up as the text is.
648
+ */
649
+ function numbersGlyphs(program, glyf, named, gidOf) {
650
+ if (named !== void 0 && named.size > 0) return false;
651
+ const tables = cmapSubtables(program);
652
+ if (UNICODE_SUBTABLES.some((key) => tables.has(key))) return false;
653
+ if (!tables.has("1,0") && !tables.has("3,0")) return false;
654
+ const reached = (code) => {
655
+ const gid = gidOf(code);
656
+ return gid !== void 0 && gid > 0 && gid < glyf.count ? gid : void 0;
657
+ };
658
+ if (reached(SPACE) !== void 0) return false;
659
+ const blanks = [];
660
+ for (let code = 0; code <= 255; code++) {
661
+ const gid = reached(code);
662
+ if (gid !== void 0 && glyf.path(gid) === void 0) blanks.push(code);
663
+ }
664
+ return blanks.length > 0 && blanks.length <= MOST_BLANKS && blanks.every(inkedInLatin1);
665
+ }
666
+ /** The `cmap` subtables that key a program's glyphs by character. */
667
+ var UNICODE_SUBTABLES = [
668
+ "3,1",
669
+ "0,3",
670
+ "0,1",
671
+ "0,0"
672
+ ];
673
+ /**
674
+ * How many blank glyphs a text face has: its space and, beside it, a no-break
675
+ * one. A program with more has emptied the glyphs it does not use, and its
676
+ * codes may be Latin-1 for all its blanks say.
677
+ */
678
+ var MOST_BLANKS = 2;
679
+ /** Whether Latin-1 reads a code as a mark: not a control, a space or a soft hyphen. */
680
+ function inkedInLatin1(code) {
681
+ return code > 32 && code < 127 || code > 160 && code <= 255 && code !== 173;
682
+ }
683
+ /** §9.6.6.4 — where a symbolic face's (3,0) subtable may put a one-byte code. */
684
+ var SYMBOL_RANGES = [
685
+ 0,
686
+ 61440,
687
+ 61696,
688
+ 61952
689
+ ];
690
+ /** §9.8.2 `/Flags` — bit 3 is Symbolic, bit 6 Nonsymbolic (bits numbered from 1). */
691
+ var FLAG_SYMBOLIC = 4;
692
+ var FLAG_NONSYMBOLIC = 32;
693
+ /** The Mac Roman code of a glyph name, which is what a (1,0) `cmap` is keyed by. */
694
+ function macRomanCode(name) {
695
+ if (!macRomanCodes) {
696
+ macRomanCodes = /* @__PURE__ */ new Map();
697
+ for (const [code, glyph] of baseEncodingTable("MacRomanEncoding") ?? []) if (!macRomanCodes.has(glyph)) macRomanCodes.set(glyph, code);
698
+ }
699
+ return macRomanCodes.get(name);
700
+ }
701
+ var macRomanCodes;
702
+ /**
703
+ * §9.6.6 — the glyph ANY code of a simple font draws, for a writer that embeds
704
+ * the face: the name the encoding gives the code, as the outline path reads it.
705
+ *
706
+ * WinAnsiEncoding's Latin-1 half is named too (Annex D.2), though the text
707
+ * reads it as Latin-1 without a name: unnamed, an é in a program addressed by
708
+ * name selected nothing — and in a TrueType one, the Mac Roman subtable's È.
709
+ *
710
+ * @param glyphs The program, read by name and code.
711
+ * @param nameOf The name each code has: `/Differences` over the base encoding.
712
+ * @param winAnsi Whether that base encoding is WinAnsiEncoding.
713
+ */
714
+ function simpleGlyph(glyphs, nameOf, winAnsi) {
715
+ if (!glyphs) return void 0;
716
+ return (code) => glyphs.glyph(code, nameOf.get(code) ?? (winAnsi ? winAnsiLatinName(code) : void 0) ?? glyphs.builtIn?.get(code));
717
+ }
718
+ /**
719
+ * §9.9 — the {@link FaceProgram} of a font whose program this reads: the glyph
720
+ * lookup, the licence, and what the descriptor states (§9.8.1).
721
+ *
722
+ * @param file The owning file.
723
+ * @param fontDict The font dictionary.
724
+ * @param isType0 Whether it is a composite font, whose descendant owns the
725
+ * descriptor.
726
+ * @param glyph The glyph each code draws, where the program can be read.
727
+ * @param facts What the program states of itself: its licence and its space.
728
+ * @returns The `program` field of a {@link ContentFont}, or nothing.
729
+ */
730
+ function programOf(file, fontDict, isType0, glyph, facts) {
731
+ if (!glyph) return {};
732
+ const owner = isType0 ? descendantFont(file, fontDict) : fontDict;
733
+ const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
734
+ if (!(descriptor instanceof Map)) return {};
735
+ const stated = (key) => {
736
+ const v = file.resolve(descriptor.get(key) ?? PDF_NULL);
737
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
738
+ };
739
+ const flags = stated("Flags") ?? 0;
740
+ const capHeight = stated("CapHeight");
741
+ const xHeight = stated("XHeight");
742
+ const base = asName(file.resolve(owner.get("BaseFont") ?? fontDict.get("BaseFont") ?? PDF_NULL));
743
+ return { program: {
744
+ glyph,
745
+ ...facts?.fsType !== void 0 ? { fsType: facts.fsType } : {},
746
+ ...facts?.spaceAdvance !== void 0 ? { spaceAdvance: facts.spaceAdvance } : {},
747
+ postScriptName: base.replace(/^[A-Z]{6}\+/u, ""),
748
+ ...capHeight !== void 0 && capHeight > 0 ? { capHeight } : {},
749
+ ...xHeight !== void 0 && xHeight > 0 ? { xHeight } : {},
750
+ italicAngle: stated("ItalicAngle") ?? 0,
751
+ fixedPitch: (flags & FLAG_FIXED_PITCH) !== 0
752
+ } };
753
+ }
488
754
  /**
489
755
  * The glyph index a name carries, for the names that carry one.
490
756
  *
@@ -894,6 +1160,23 @@ function descendantFont(file, fontDict) {
894
1160
  const first = Array.isArray(descFonts) ? file.resolve(descFonts[0] ?? PDF_NULL) : PDF_NULL;
895
1161
  return first instanceof Map ? first : /* @__PURE__ */ new Map();
896
1162
  }
1163
+ /**
1164
+ * §9.8.2 — whether every glyph of a simple font is as wide as the next: the
1165
+ * descriptor's FixedPitch flag, the standard Courier, or a /Widths array that
1166
+ * says so itself. pdfTeX sets no flag on Computer Modern's typewriter face,
1167
+ * and its widths do: all of them 525. Ten widths at the least — a face that
1168
+ * shows four digits shows four equal widths and is no typewriter's.
1169
+ */
1170
+ function fixedPitchOf(file, fontDict) {
1171
+ const descriptor = file.resolve(fontDict.get("FontDescriptor") ?? PDF_NULL);
1172
+ if (((descriptor instanceof Map ? asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0) : 0) & FLAG_FIXED_PITCH) !== 0) return true;
1173
+ if (/^courier/iu.test(plainFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL))))) return true;
1174
+ const stated = file.resolve(fontDict.get("Widths") ?? PDF_NULL);
1175
+ const widths = (Array.isArray(stated) ? stated : []).map((w) => file.resolve(w)).filter((w) => typeof w === "number" && w > 0);
1176
+ return widths.length >= FIXED_PITCH_WIDTHS && widths.every((w) => Math.abs(w - widths[0]) < .5);
1177
+ }
1178
+ /** How many widths it takes to say every glyph is as wide as the next. */
1179
+ var FIXED_PITCH_WIDTHS = 10;
897
1180
  function simpleWidths(file, fontDict, decodeOne) {
898
1181
  const first = asNumber(file.resolve(fontDict.get("FirstChar") ?? PDF_NULL), 0);
899
1182
  const widthsVal = file.resolve(fontDict.get("Widths") ?? PDF_NULL);
@@ -901,12 +1184,14 @@ function simpleWidths(file, fontDict, decodeOne) {
901
1184
  const descriptor = file.resolve(fontDict.get("FontDescriptor") ?? PDF_NULL);
902
1185
  const missing = descriptor instanceof Map ? asNumber(file.resolve(descriptor.get("MissingWidth") ?? PDF_NULL), 0) : 0;
903
1186
  const face = standardFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
904
- return (code) => {
1187
+ const stated = (code) => {
905
1188
  const w = widths[code - first];
906
1189
  if (typeof w === "number") return w;
907
- const built = face === void 0 ? void 0 : standardWidth(face, code, decodeOne(code));
908
- if (built !== void 0) return built;
909
- return missing > 0 ? missing : 500;
1190
+ return face === void 0 ? void 0 : standardWidth(face, code, decodeOne(code));
1191
+ };
1192
+ return {
1193
+ width: (code) => stated(code) ?? (missing > 0 ? missing : 500),
1194
+ stated
910
1195
  };
911
1196
  }
912
1197
  function cidWidths(file, fontDict) {
@@ -41,3 +41,36 @@ export declare function outlineSource(program: Uint8Array): OutlineSource | unde
41
41
  * states no names of its own (formats 1.0 and 3.0).
42
42
  */
43
43
  export declare function postGlyphNames(program: Uint8Array): Map<string, number> | undefined;
44
+ /**
45
+ * OS/2 `fsType` — the embedding a TrueType or OpenType program's licence allows
46
+ * (ISO/IEC 14496-22, OS/2 table).
47
+ *
48
+ * @param program The raw sfnt bytes.
49
+ * @returns The field, or `undefined` where the program carries no OS/2 table —
50
+ * a subsetter may drop it, and then the program states nothing.
51
+ */
52
+ export declare function sfntFsType(program: Uint8Array): number | undefined;
53
+ /**
54
+ * How far the pen moves after an sfnt program's space, in thousandths of an em:
55
+ * the glyph its `cmap` (or its `post` names) gives U+0020, measured in `hmtx`.
56
+ * A subset keeps the metrics of glyphs it dropped the outlines of, and the
57
+ * width a page's words were set apart by is the one a writer should give the
58
+ * space the page never showed.
59
+ *
60
+ * @param program The raw sfnt bytes.
61
+ * @returns The advance, or `undefined` where the program says nothing of a space.
62
+ */
63
+ export declare function sfntSpaceAdvance(program: Uint8Array): number | undefined;
64
+ /**
65
+ * Every `cmap` subtable an sfnt program carries, keyed `platform,encoding` —
66
+ * `3,1` for Windows Unicode, `3,0` for Windows symbol, `1,0` for Mac Roman.
67
+ *
68
+ * A simple TrueType font reaches its glyphs through whichever of these it has,
69
+ * and which one decides what a code is looked up AS (§9.6.6.4): a character, a
70
+ * symbol code, a Mac Roman byte.
71
+ *
72
+ * @param program The raw sfnt bytes.
73
+ * @returns Subtable → code-to-glyph lookup (0 where it maps nothing). Formats
74
+ * 0, 4, 6 and 12 are read; any other is left out.
75
+ */
76
+ export declare function cmapSubtables(program: Uint8Array): Map<string, (code: number) => number>;
@@ -120,6 +120,140 @@ function postGlyphNames(program) {
120
120
  }
121
121
  return out.size > 0 ? out : void 0;
122
122
  }
123
+ /**
124
+ * OS/2 `fsType` — the embedding a TrueType or OpenType program's licence allows
125
+ * (ISO/IEC 14496-22, OS/2 table).
126
+ *
127
+ * @param program The raw sfnt bytes.
128
+ * @returns The field, or `undefined` where the program carries no OS/2 table —
129
+ * a subsetter may drop it, and then the program states nothing.
130
+ */
131
+ function sfntFsType(program) {
132
+ let tables;
133
+ try {
134
+ tables = sfntTables(program);
135
+ } catch {
136
+ return;
137
+ }
138
+ const os2 = tables.get("OS/2");
139
+ if (!os2 || os2.length < 10 || os2.offset + 10 > program.length) return void 0;
140
+ return new DataView(program.buffer, program.byteOffset, program.byteLength).getUint16(os2.offset + 8);
141
+ }
142
+ /**
143
+ * How far the pen moves after an sfnt program's space, in thousandths of an em:
144
+ * the glyph its `cmap` (or its `post` names) gives U+0020, measured in `hmtx`.
145
+ * A subset keeps the metrics of glyphs it dropped the outlines of, and the
146
+ * width a page's words were set apart by is the one a writer should give the
147
+ * space the page never showed.
148
+ *
149
+ * @param program The raw sfnt bytes.
150
+ * @returns The advance, or `undefined` where the program says nothing of a space.
151
+ */
152
+ function sfntSpaceAdvance(program) {
153
+ let tables;
154
+ try {
155
+ tables = sfntTables(program);
156
+ } catch {
157
+ return;
158
+ }
159
+ const head = tables.get("head");
160
+ const hhea = tables.get("hhea");
161
+ const hmtx = tables.get("hmtx");
162
+ if (!head || !hhea || !hmtx) return void 0;
163
+ const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
164
+ const cmaps = cmapSubtables(program);
165
+ const gid = [
166
+ cmaps.get("3,1")?.(32),
167
+ cmaps.get("0,3")?.(32),
168
+ cmaps.get("3,0")?.(61472),
169
+ cmaps.get("3,0")?.(32),
170
+ cmaps.get("1,0")?.(32)
171
+ ].find((gid) => gid !== void 0 && gid > 0) ?? postGlyphNames(program)?.get("space");
172
+ if (gid === void 0 || gid <= 0) return void 0;
173
+ const unitsPerEm = safeUint16(view, head.offset + 18) || 1e3;
174
+ const metrics = safeUint16(view, hhea.offset + 34);
175
+ if (metrics === 0) return void 0;
176
+ const at = hmtx.offset + Math.min(gid, metrics - 1) * 4;
177
+ if (at + 2 > hmtx.offset + hmtx.length) return void 0;
178
+ const advance = safeUint16(view, at);
179
+ return advance > 0 ? advance / unitsPerEm * 1e3 : void 0;
180
+ }
181
+ /**
182
+ * Every `cmap` subtable an sfnt program carries, keyed `platform,encoding` —
183
+ * `3,1` for Windows Unicode, `3,0` for Windows symbol, `1,0` for Mac Roman.
184
+ *
185
+ * A simple TrueType font reaches its glyphs through whichever of these it has,
186
+ * and which one decides what a code is looked up AS (§9.6.6.4): a character, a
187
+ * symbol code, a Mac Roman byte.
188
+ *
189
+ * @param program The raw sfnt bytes.
190
+ * @returns Subtable → code-to-glyph lookup (0 where it maps nothing). Formats
191
+ * 0, 4, 6 and 12 are read; any other is left out.
192
+ */
193
+ function cmapSubtables(program) {
194
+ const out = /* @__PURE__ */ new Map();
195
+ let tables;
196
+ try {
197
+ tables = sfntTables(program);
198
+ } catch {
199
+ return out;
200
+ }
201
+ const cmap = tables.get("cmap");
202
+ if (!cmap) return out;
203
+ const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
204
+ const count = safeUint16(view, cmap.offset + 2);
205
+ for (let i = 0; i < count; i++) {
206
+ const at = cmap.offset + 4 + i * 8;
207
+ const key = `${safeUint16(view, at)},${safeUint16(view, at + 2)}`;
208
+ if (out.has(key)) continue;
209
+ const lookup = cmapLookup(view, cmap.offset + safeUint32(view, at + 4));
210
+ if (lookup) out.set(key, lookup);
211
+ }
212
+ return out;
213
+ }
214
+ /** One `cmap` subtable as a lookup, for the formats a simple font's program uses. */
215
+ function cmapLookup(view, at) {
216
+ const format = safeUint16(view, at);
217
+ if (format === 0) return (code) => code >= 0 && code < 256 ? safeUint8(view, at + 6 + code) : 0;
218
+ if (format === 6) {
219
+ const first = safeUint16(view, at + 6);
220
+ const entries = safeUint16(view, at + 8);
221
+ return (code) => code >= first && code < first + entries ? safeUint16(view, at + 10 + (code - first) * 2) : 0;
222
+ }
223
+ if (format === 4) {
224
+ const segments = safeUint16(view, at + 6) >> 1;
225
+ const ends = at + 14;
226
+ const starts = ends + segments * 2 + 2;
227
+ const deltas = starts + segments * 2;
228
+ const ranges = deltas + segments * 2;
229
+ return (code) => {
230
+ if (code < 0 || code > 65535) return 0;
231
+ for (let s = 0; s < segments; s++) {
232
+ if (safeUint16(view, ends + s * 2) < code) continue;
233
+ const start = safeUint16(view, starts + s * 2);
234
+ if (start > code) return 0;
235
+ const delta = safeUint16(view, deltas + s * 2);
236
+ const range = safeUint16(view, ranges + s * 2);
237
+ if (range === 0) return code + delta & 65535;
238
+ const gid = safeUint16(view, ranges + s * 2 + range + (code - start) * 2);
239
+ return gid === 0 ? 0 : gid + delta & 65535;
240
+ }
241
+ return 0;
242
+ };
243
+ }
244
+ if (format === 12) {
245
+ const groups = safeUint32(view, at + 12);
246
+ return (code) => {
247
+ for (let g = 0; g < groups; g++) {
248
+ const entry = at + 16 + g * 12;
249
+ const start = safeUint32(view, entry);
250
+ const end = safeUint32(view, entry + 4);
251
+ if (code >= start && code <= end) return safeUint32(view, entry + 8) + (code - start);
252
+ }
253
+ return 0;
254
+ };
255
+ }
256
+ }
123
257
  /** `post` version 2.0 — the only one that states names of its own. */
124
258
  var POST_NAMED = 131072;
125
259
  /** How many names the Macintosh standard order holds (§post). */
@@ -359,4 +493,4 @@ function safeUint32(view, at) {
359
493
  return at >= 0 && at + 4 <= view.byteLength ? view.getUint32(at) : 0;
360
494
  }
361
495
  //#endregion
362
- export { outlineSource, postGlyphNames };
496
+ export { cmapSubtables, outlineSource, postGlyphNames, sfntFsType, sfntSpaceAdvance };