reamkit 1.30.0 → 1.32.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 (147) hide show
  1. package/README.md +70 -24
  2. package/dist/esm/core/bidi/index.d.ts +10 -0
  3. package/dist/esm/core/bidi/index.js +18 -2
  4. package/dist/esm/core/converter/facade.js +10 -5
  5. package/dist/esm/core/converter/project.js +4 -1
  6. package/dist/esm/core/converter/ream.js +17 -6
  7. package/dist/esm/core/converter/sheet-images.d.ts +17 -0
  8. package/dist/esm/core/converter/sheet-images.js +33 -0
  9. package/dist/esm/core/crypto/offcrypto.js +1 -1
  10. package/dist/esm/core/document-model/index.d.ts +1 -1
  11. package/dist/esm/core/document-model/types.d.ts +175 -0
  12. package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
  13. package/dist/esm/core/drawingml/chart-geometry.js +198 -108
  14. package/dist/esm/core/drawingml/chart-parser.js +73 -2
  15. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  16. package/dist/esm/core/drawingml/colors.js +23 -3
  17. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  18. package/dist/esm/core/drawingml/shape-render.js +4 -2
  19. package/dist/esm/core/font/index.d.ts +2 -0
  20. package/dist/esm/core/font/measure.d.ts +9 -0
  21. package/dist/esm/core/font/measure.js +9 -1
  22. package/dist/esm/core/font/ttf-build.d.ts +113 -0
  23. package/dist/esm/core/font/ttf-build.js +1224 -0
  24. package/dist/esm/core/font/ttf-subset.d.ts +19 -0
  25. package/dist/esm/core/font/ttf-subset.js +14 -1
  26. package/dist/esm/core/fonts/families.js +4 -2
  27. package/dist/esm/core/fonts/provider.d.ts +17 -0
  28. package/dist/esm/core/fonts/provider.js +27 -2
  29. package/dist/esm/core/fonts/remote-fonts.js +8 -1
  30. package/dist/esm/core/fonts/scripts.js +4 -2
  31. package/dist/esm/core/ir/flow.d.ts +77 -1
  32. package/dist/esm/core/ir/sheet.d.ts +43 -0
  33. package/dist/esm/core/number-format.js +16 -11
  34. package/dist/esm/core/numbering/index.d.ts +1 -1
  35. package/dist/esm/core/numbering/state.d.ts +11 -1
  36. package/dist/esm/core/numbering/state.js +10 -1
  37. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  38. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  39. package/dist/esm/core/style-cascade/resolver.js +39 -6
  40. package/dist/esm/core/style-cascade/types.d.ts +19 -1
  41. package/dist/esm/core/style-cascade/types.js +3 -0
  42. package/dist/esm/excel/column-bands.d.ts +39 -6
  43. package/dist/esm/excel/column-bands.js +41 -17
  44. package/dist/esm/excel/comments-parser.js +19 -0
  45. package/dist/esm/excel/conditional-format.d.ts +7 -3
  46. package/dist/esm/excel/conditional-format.js +77 -39
  47. package/dist/esm/excel/formula/context.d.ts +18 -0
  48. package/dist/esm/excel/formula/eval.js +51 -0
  49. package/dist/esm/excel/formula/index.d.ts +2 -2
  50. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  51. package/dist/esm/excel/formula/lexer.js +41 -0
  52. package/dist/esm/excel/formula/parser.d.ts +25 -0
  53. package/dist/esm/excel/formula/parser.js +95 -0
  54. package/dist/esm/excel/print-model.d.ts +38 -1
  55. package/dist/esm/excel/print-model.js +522 -80
  56. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  57. package/dist/esm/excel/shared-strings-parser.js +11 -1
  58. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  59. package/dist/esm/excel/sheet-drawing.js +47 -13
  60. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  61. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  62. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  63. package/dist/esm/excel/sheet-to-flow.js +434 -47
  64. package/dist/esm/excel/styles-parser.d.ts +9 -0
  65. package/dist/esm/excel/styles-parser.js +80 -26
  66. package/dist/esm/excel/table-parser.js +5 -0
  67. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  68. package/dist/esm/excel/table-style-presets.js +1555 -0
  69. package/dist/esm/excel/table-style.d.ts +13 -0
  70. package/dist/esm/excel/table-style.js +75 -0
  71. package/dist/esm/excel/tint.d.ts +10 -0
  72. package/dist/esm/excel/tint.js +75 -0
  73. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  74. package/dist/esm/excel/vml-drawing.js +143 -10
  75. package/dist/esm/excel/worksheet-parser.js +89 -15
  76. package/dist/esm/excel/xls/biff-reader.js +10 -2
  77. package/dist/esm/excel/xlsx-reader.js +195 -14
  78. package/dist/esm/excel/xlsx-writer.js +8 -7
  79. package/dist/esm/html/html-writer.js +202 -20
  80. package/dist/esm/index.d.ts +2 -2
  81. package/dist/esm/layout/fit-pages.d.ts +11 -0
  82. package/dist/esm/layout/fit-pages.js +86 -0
  83. package/dist/esm/layout/line-paint.d.ts +58 -0
  84. package/dist/esm/layout/line-paint.js +107 -0
  85. package/dist/esm/layout/page-doc.d.ts +61 -3
  86. package/dist/esm/layout/page-doc.js +141 -1
  87. package/dist/esm/layout/styled-layout.d.ts +55 -7
  88. package/dist/esm/layout/styled-layout.js +1154 -169
  89. package/dist/esm/layout/turned-section.d.ts +38 -0
  90. package/dist/esm/layout/turned-section.js +205 -0
  91. package/dist/esm/markdown/markdown-writer.js +17 -6
  92. package/dist/esm/pdf/styled-page-emitter.js +114 -60
  93. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  94. package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
  95. package/dist/esm/pdf-reader/cff-outline.js +169 -21
  96. package/dist/esm/pdf-reader/content.d.ts +53 -0
  97. package/dist/esm/pdf-reader/content.js +9 -1
  98. package/dist/esm/pdf-reader/display.d.ts +36 -0
  99. package/dist/esm/pdf-reader/display.js +66 -1
  100. package/dist/esm/pdf-reader/document.js +5 -1
  101. package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
  102. package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
  103. package/dist/esm/pdf-reader/encodings.d.ts +8 -0
  104. package/dist/esm/pdf-reader/encodings.js +25 -3
  105. package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
  106. package/dist/esm/pdf-reader/face-outlines.js +362 -0
  107. package/dist/esm/pdf-reader/figures.d.ts +52 -0
  108. package/dist/esm/pdf-reader/figures.js +433 -0
  109. package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
  110. package/dist/esm/pdf-reader/flow-build.js +345 -26
  111. package/dist/esm/pdf-reader/font.js +321 -36
  112. package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
  113. package/dist/esm/pdf-reader/glyf-outline.js +135 -1
  114. package/dist/esm/pdf-reader/glyph-names.js +154 -1
  115. package/dist/esm/pdf-reader/layout.d.ts +93 -0
  116. package/dist/esm/pdf-reader/layout.js +1703 -214
  117. package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
  118. package/dist/esm/pdf-reader/page-numbers.js +167 -0
  119. package/dist/esm/pdf-reader/tagged.js +182 -23
  120. package/dist/esm/pdf-reader/text.d.ts +6 -3
  121. package/dist/esm/pdf-reader/text.js +98 -9
  122. package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
  123. package/dist/esm/pdf-reader/type1-outline.js +63 -8
  124. package/dist/esm/pdf-reader/vector.js +71 -1
  125. package/dist/esm/svg/svg-text.d.ts +58 -0
  126. package/dist/esm/svg/svg-text.js +322 -0
  127. package/dist/esm/svg/svg-writer.js +135 -71
  128. package/dist/esm/word/doc/doc-reader.js +6 -2
  129. package/dist/esm/word/doc/doc-text.d.ts +6 -0
  130. package/dist/esm/word/doc/doc-text.js +19 -1
  131. package/dist/esm/word/document-parser.d.ts +2 -2
  132. package/dist/esm/word/document-parser.js +11 -1
  133. package/dist/esm/word/docx-reader.js +5 -3
  134. package/dist/esm/word/docx-writer.js +218 -23
  135. package/dist/esm/word/drawing-parser.d.ts +5 -3
  136. package/dist/esm/word/drawing-parser.js +49 -8
  137. package/dist/esm/word/font-embed.d.ts +30 -0
  138. package/dist/esm/word/font-embed.js +173 -0
  139. package/dist/esm/word/font-table.d.ts +10 -0
  140. package/dist/esm/word/font-table.js +13 -1
  141. package/dist/esm/word/index.js +1 -1
  142. package/dist/esm/word/numbering-parser.d.ts +3 -1
  143. package/dist/esm/word/numbering-parser.js +2 -1
  144. package/dist/esm/word/paragraph-properties.d.ts +7 -6
  145. package/dist/esm/word/paragraph-properties.js +14 -2
  146. package/dist/esm/word/run-properties.js +26 -0
  147. package/package.json +12 -3
@@ -1,3 +1,4 @@
1
+ import { standardEncodingTable } from "./encodings.js";
1
2
  //#region src/pdf-reader/cff-outline.ts
2
3
  /**
3
4
  * Prepare an embedded CFF program for outline reading.
@@ -69,6 +70,90 @@ function cffCidToGid(program) {
69
70
  return;
70
71
  }
71
72
  }
73
+ /**
74
+ * How far the pen moves after a name-keyed CFF program's `space`, in
75
+ * thousandths of an em — the width a writer gives the space a page never
76
+ * showed, which is the one it was typeset with.
77
+ *
78
+ * @param program The raw CFF bytes.
79
+ * @returns The advance, or `undefined` where the program has no `space` or its
80
+ * width cannot be read off the charstring.
81
+ */
82
+ function cffSpaceAdvance(program) {
83
+ try {
84
+ const font = parseCff(program);
85
+ const gid = font?.nameToGid?.get("space");
86
+ if (!font || gid === void 0) return void 0;
87
+ const width = glyphWidth(font, gid);
88
+ return width === void 0 ? void 0 : width * font.scale * 1e3;
89
+ } catch {
90
+ return;
91
+ }
92
+ }
93
+ /**
94
+ * TN 5177 §4.1 — a glyph's width: `nominalWidthX` plus the operand a
95
+ * charstring puts before its first stack-clearing operator, or
96
+ * `defaultWidthX` where it puts none. Whether one is there is a matter of
97
+ * COUNT — one more than the operator takes.
98
+ */
99
+ function glyphWidth(font, gid) {
100
+ const code = font.charStrings[gid];
101
+ if (!code) return void 0;
102
+ const { defaultWidth, nominalWidth } = font.widthsOf(gid);
103
+ const stack = [];
104
+ for (let i = 0; i < code.length;) {
105
+ const b = code[i++];
106
+ if (b === 28) {
107
+ stack.push((code[i] << 24 | code[i + 1] << 16) >> 16);
108
+ i += 2;
109
+ } else if (b >= 32 && b <= 246) stack.push(b - 139);
110
+ else if (b >= 247 && b <= 250) stack.push((b - 247) * 256 + code[i++] + 108);
111
+ else if (b >= 251 && b <= 254) stack.push(-(b - 251) * 256 - code[i++] - 108);
112
+ else if (b === 255) {
113
+ stack.push((code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3]) / 65536);
114
+ i += 4;
115
+ } else {
116
+ const takes = b === 21 ? 2 : b === 22 || b === 4 ? 1 : b === 14 ? stack.length >= 4 ? 4 : 0 : -1;
117
+ if (b === 1 || b === 3 || b === 18 || b === 23 || b === 19 || b === 20) return stack.length % 2 === 1 ? nominalWidth + stack[0] : defaultWidth;
118
+ if (takes < 0) return void 0;
119
+ return stack.length > takes ? nominalWidth + stack[0] : defaultWidth;
120
+ }
121
+ }
122
+ }
123
+ /**
124
+ * The embedding a CFF program's licence allows: CFF has no OS/2 table, and
125
+ * Adobe writes the OS/2 `fsType` into the Top DICT's `PostScript` string
126
+ * instead (TN 5176 §9, key 12 21) as `/FSType 8 def`.
127
+ *
128
+ * @param program The raw CFF bytes.
129
+ * @returns The value, or `undefined` where the program states none.
130
+ */
131
+ function cffFsType(program) {
132
+ try {
133
+ const names = readIndex(program, program[2] ?? 4);
134
+ const tops = names ? readIndex(program, names.end) : void 0;
135
+ const strings = tops ? readIndex(program, tops.end) : void 0;
136
+ const top = tops?.items[0];
137
+ if (!top || !strings) return void 0;
138
+ const sid = parseDict(top).get(1221)?.[0];
139
+ if (sid === void 0 || sid < STANDARD_STRINGS.length) return void 0;
140
+ const own = strings.items[sid - STANDARD_STRINGS.length];
141
+ return own ? postScriptFsType(new TextDecoder("latin1").decode(own)) : void 0;
142
+ } catch {
143
+ return;
144
+ }
145
+ }
146
+ /**
147
+ * The `/FSType n def` a PostScript font program writes where an OpenType one
148
+ * has its OS/2 field (Adobe TN 5176 §9; a Type 1 font's `FontInfo` alike).
149
+ *
150
+ * @param text The PostScript to search.
151
+ * @returns The value, or `undefined` where the text states none.
152
+ */
153
+ function postScriptFsType(text) {
154
+ const m = /\/FSType\s+(\d+)\s+def/u.exec(text);
155
+ return m ? Number(m[1]) : void 0;
156
+ }
72
157
  /** The `CFF ` table of an OpenType wrapper, where the program is one. */
73
158
  function openTypeCff(program) {
74
159
  if (program.length < 12) return void 0;
@@ -102,27 +187,52 @@ function parseCff(program) {
102
187
  if (!charStrings) return void 0;
103
188
  const matrix = top.get(1207);
104
189
  const scale = matrix && matrix.length >= 4 ? matrix[0] ?? .001 : .001;
105
- const privateSubrs = (dict) => {
190
+ const privateOf = (dict) => {
191
+ const none = {
192
+ subrs: [],
193
+ widths: {
194
+ defaultWidth: 0,
195
+ nominalWidth: 0
196
+ }
197
+ };
106
198
  const priv = dict.get(18);
107
- if (!priv || priv.length < 2) return [];
199
+ if (!priv || priv.length < 2) return none;
108
200
  const size = priv[0];
109
201
  const offset = priv[1];
110
- if (offset < 0 || offset + size > program.length) return [];
111
- const subrsAt = parseDict(program.subarray(offset, offset + size)).get(19)?.[0];
112
- if (subrsAt === void 0) return [];
113
- return readIndex(program, offset + subrsAt)?.items ?? [];
202
+ if (offset < 0 || offset + size > program.length) return none;
203
+ const inner = parseDict(program.subarray(offset, offset + size));
204
+ const widths = {
205
+ defaultWidth: inner.get(20)?.[0] ?? 0,
206
+ nominalWidth: inner.get(21)?.[0] ?? 0
207
+ };
208
+ const subrsAt = inner.get(19)?.[0];
209
+ if (subrsAt === void 0) return {
210
+ subrs: [],
211
+ widths
212
+ };
213
+ return {
214
+ subrs: readIndex(program, offset + subrsAt)?.items ?? [],
215
+ widths
216
+ };
114
217
  };
115
218
  const isCid = top.has(1230);
116
219
  let localSubrs;
220
+ let widthsOf;
117
221
  if (isCid) {
118
222
  const fdArrayAt = top.get(1236)?.[0];
119
223
  const fdSelectAt = top.get(1237)?.[0];
120
- const subrsPerFd = (fdArrayAt !== void 0 ? readIndex(program, fdArrayAt)?.items ?? [] : []).map((fd) => privateSubrs(parseDict(fd)));
224
+ const privatePerFd = (fdArrayAt !== void 0 ? readIndex(program, fdArrayAt)?.items ?? [] : []).map((fd) => privateOf(parseDict(fd)));
121
225
  const select = fdSelectAt !== void 0 ? readFdSelect(program, fdSelectAt, charStrings.items.length) : void 0;
122
- localSubrs = (gid) => subrsPerFd[select ? select[gid] ?? 0 : 0] ?? [];
226
+ const privateFor = (gid) => privatePerFd[select ? select[gid] ?? 0 : 0];
227
+ localSubrs = (gid) => privateFor(gid)?.subrs ?? [];
228
+ widthsOf = (gid) => privateFor(gid)?.widths ?? {
229
+ defaultWidth: 0,
230
+ nominalWidth: 0
231
+ };
123
232
  } else {
124
- const only = privateSubrs(top);
125
- localSubrs = () => only;
233
+ const only = privateOf(top);
234
+ localSubrs = () => only.subrs;
235
+ widthsOf = () => only.widths;
126
236
  }
127
237
  const charsetAt = top.get(15)?.[0];
128
238
  const sids = charsetAt !== void 0 && charsetAt > 2 ? readCharset(program, charsetAt, charStrings.items.length) : void 0;
@@ -134,7 +244,8 @@ function parseCff(program) {
134
244
  localSubrs,
135
245
  scale,
136
246
  ...cidToGid ? { cidToGid } : {},
137
- ...nameToGid ? { nameToGid } : {}
247
+ ...nameToGid ? { nameToGid } : {},
248
+ widthsOf
138
249
  };
139
250
  }
140
251
  /**
@@ -689,9 +800,10 @@ function bias(subrs) {
689
800
  /** How deep a charstring may call, and how many operators it may run. */
690
801
  var MAX_CALL_DEPTH = 10;
691
802
  var MAX_STEPS = 65536;
692
- function runGlyph(font, gid) {
803
+ function runGlyph(font, gid, part = false) {
693
804
  const program = font.charStrings[gid];
694
805
  if (!program) return void 0;
806
+ let seac;
695
807
  const local = font.localSubrs(gid);
696
808
  const localBias = bias(local);
697
809
  const globalBias = bias(font.globalSubrs);
@@ -885,7 +997,16 @@ function runGlyph(font, gid) {
885
997
  }
886
998
  case 11: return true;
887
999
  case 14:
888
- takeWidth(stack.length > 4 ? 4 : 0);
1000
+ takeWidth(stack.length >= 4 ? 4 : 0);
1001
+ if (stack.length >= 4) {
1002
+ const [adx = 0, ady = 0, bchar = 0, achar = 0] = stack;
1003
+ seac = {
1004
+ adx,
1005
+ ady,
1006
+ bchar,
1007
+ achar
1008
+ };
1009
+ }
889
1010
  if (open) out.push({ op: "close" });
890
1011
  open = false;
891
1012
  return false;
@@ -919,8 +1040,35 @@ function runGlyph(font, gid) {
919
1040
  }
920
1041
  run(program, 0);
921
1042
  if (out.length > 0 && out[out.length - 1]?.op !== "close") out.push({ op: "close" });
922
- if (out.length === 0) return void 0;
923
- return out.map((seg) => scaleSeg(seg, font.scale));
1043
+ const own = out.map((seg) => scaleSeg(seg, font.scale));
1044
+ if (seac && !part) {
1045
+ const at = seac;
1046
+ const glyphOf = (code) => {
1047
+ const name = standardEncodingTable().get(code);
1048
+ const gid = name !== void 0 ? font.nameToGid?.get(name) : void 0;
1049
+ return gid !== void 0 ? runGlyph(font, gid, true) ?? [] : [];
1050
+ };
1051
+ own.push(...glyphOf(at.bchar), ...glyphOf(at.achar).map((seg) => shiftSeg(seg, at.adx * font.scale, at.ady * font.scale)));
1052
+ }
1053
+ return own.length > 0 ? own : void 0;
1054
+ }
1055
+ /** A segment moved by `(dx, dy)`: where a `seac` puts its accent. */
1056
+ function shiftSeg(seg, dx, dy) {
1057
+ if (seg.op === "close") return seg;
1058
+ if (seg.op === "cubic") return {
1059
+ op: "cubic",
1060
+ x1: seg.x1 + dx,
1061
+ y1: seg.y1 + dy,
1062
+ x2: seg.x2 + dx,
1063
+ y2: seg.y2 + dy,
1064
+ x: seg.x + dx,
1065
+ y: seg.y + dy
1066
+ };
1067
+ return {
1068
+ op: seg.op,
1069
+ x: seg.x + dx,
1070
+ y: seg.y + dy
1071
+ };
924
1072
  }
925
1073
  function runEscape(op, stack, trans) {
926
1074
  switch (op) {
@@ -1030,8 +1178,8 @@ function flexCurves(op, stack, x0, y0) {
1030
1178
  ]);
1031
1179
  push([
1032
1180
  n(4),
1033
- n(6),
1034
- 0
1181
+ n(5),
1182
+ n(6)
1035
1183
  ], [
1036
1184
  0,
1037
1185
  startY - y,
@@ -1051,12 +1199,12 @@ function flexCurves(op, stack, x0, y0) {
1051
1199
  ]);
1052
1200
  push([
1053
1201
  n(5),
1054
- n(7),
1202
+ n(6),
1055
1203
  n(8)
1056
1204
  ], [
1057
1205
  0,
1058
- n(6),
1059
- startY - (y + n(6))
1206
+ n(7),
1207
+ startY - (y + n(7))
1060
1208
  ]);
1061
1209
  y = startY;
1062
1210
  } else if (op === 37) {
@@ -1119,4 +1267,4 @@ function scaleSeg(seg, k) {
1119
1267
  };
1120
1268
  }
1121
1269
  //#endregion
1122
- export { cffCidToGid, cffNameToGid, cffOutlineSource, openTypeCff };
1270
+ export { cffCidToGid, cffFsType, cffNameToGid, cffOutlineSource, cffSpaceAdvance, openTypeCff, postScriptFsType };
@@ -21,6 +21,15 @@ export interface ContentFont {
21
21
  decode: (codes: ReadonlyArray<number>) => string;
22
22
  /** Glyph advance for one code, in 1000-unit text space. */
23
23
  width: (code: number) => number;
24
+ /**
25
+ * §9.6.2.1 — the advance of the face's SPACE as the file states it, in
26
+ * 1000-unit text space: the code its encoding names `space`. Absent where
27
+ * the font states none — not the fallback {@link width} gives a code with no
28
+ * width of its own.
29
+ */
30
+ readonly spaceWidth?: number;
31
+ /** §9.8.2 — every glyph of the face is as wide as the next: a typewriter's. */
32
+ readonly fixedPitch?: boolean;
24
33
  /**
25
34
  * §9.4.4 / §9.7.4.3 — the face sets its text DOWN the page, not across, and
26
35
  * the pen advances by the vertical displacement `w1` rather than by `w0`.
@@ -47,6 +56,43 @@ export interface ContentFont {
47
56
  * this is the last resort before a blank page.
48
57
  */
49
58
  readonly outline?: GlyphOutline;
59
+ /**
60
+ * §9.9 — the face's embedded program, for a writer that embeds the face:
61
+ * the outline EVERY code draws, the program's licence, the descriptor's
62
+ * metrics. Only a face whose program this reads has one.
63
+ */
64
+ readonly program?: FaceProgram;
65
+ }
66
+ /**
67
+ * §9.9 — what a writer that embeds a face needs from its program: the outline
68
+ * each code draws, what the licence allows, and the metrics the descriptor
69
+ * states (§9.8.1).
70
+ */
71
+ export interface FaceProgram {
72
+ /**
73
+ * The contours a code draws, in a one-unit em — empty for a glyph the program
74
+ * holds that draws nothing, `undefined` where it holds none for the code.
75
+ */
76
+ readonly glyph: (code: number) => ReadonlyArray<PathSeg> | undefined;
77
+ /**
78
+ * OS/2 `fsType` — or the `/FSType` a CFF or Type 1 program writes in its
79
+ * place — where the program states one.
80
+ */
81
+ readonly fsType?: number;
82
+ /**
83
+ * How far the program's own space advances, in thousandths of an em — the
84
+ * width a writer gives a space the page never showed as a glyph.
85
+ */
86
+ readonly spaceAdvance?: number;
87
+ /** The program's PostScript name: the `/BaseFont` less its subset tag. */
88
+ readonly postScriptName: string;
89
+ /** §9.8.1 `/CapHeight` and `/XHeight`, in thousandths of an em, where stated. */
90
+ readonly capHeight?: number;
91
+ readonly xHeight?: number;
92
+ /** §9.8.1 `/ItalicAngle`, in degrees counterclockwise from the vertical. */
93
+ readonly italicAngle: number;
94
+ /** §9.8.2 `/Flags` bit 1 — every glyph has the same width. */
95
+ readonly fixedPitch: boolean;
50
96
  }
51
97
  /** §9.6.6 — the outlines of a face read by glyph index, and their glyph space. */
52
98
  export interface GlyphOutline {
@@ -106,6 +152,8 @@ export interface TextRun {
106
152
  * Absent where the face states no width for it.
107
153
  */
108
154
  readonly spaceWidthPt?: number;
155
+ /** §9.8.2 — the face sets every glyph as wide as the next (see `ContentFont`). */
156
+ readonly fixedPitch?: boolean;
109
157
  /** §9.8.1 — the face the glyphs were shown in is a bold one. */
110
158
  readonly bold?: boolean;
111
159
  /**
@@ -309,6 +357,11 @@ export interface InterpretResult {
309
357
  * there is something to draw.
310
358
  */
311
359
  readonly outlines: Array<VectorPlacement>;
360
+ /**
361
+ * §9.4.3 — the codes each font with a readable program showed where the
362
+ * page paints them: the glyphs a writer embedding the face has to carry.
363
+ */
364
+ readonly shown: ReadonlyMap<ContentFont, ReadonlySet<number>>;
312
365
  }
313
366
  /**
314
367
  * §9.6.5 — one showing of a Type 3 glyph: which procedure, and the matrix that
@@ -191,6 +191,7 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
191
191
  const glyphs = [];
192
192
  const painted = [];
193
193
  const outlines = [];
194
+ const codesShown = /* @__PURE__ */ new Map();
194
195
  const lexer = new Lexer(bytes);
195
196
  const stack = [];
196
197
  let state = initialState();
@@ -373,6 +374,11 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
373
374
  const bytes = toBytes(operand);
374
375
  const codes = state.font.splitCodes ? state.font.splitCodes(bytes) : splitCodes(bytes, state.font.bytesPerCode);
375
376
  for (const code of codes) advanceGlyph(code);
377
+ if (state.font.program && visible() && state.renderMode !== 3 && state.renderMode !== 7) {
378
+ let set = codesShown.get(state.font);
379
+ if (!set) codesShown.set(state.font, set = /* @__PURE__ */ new Set());
380
+ for (const code of codes) set.add(code);
381
+ }
376
382
  return state.font.decode(codes);
377
383
  };
378
384
  const emitAt = (origin, shown, end) => {
@@ -393,6 +399,7 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
393
399
  fontSizePt: Math.abs(state.fontSize) * scaleY,
394
400
  spaceWidthPt: (state.font.width(32) / 1e3 * state.fontSize + state.charSpacing + state.wordSpacing) * scaleX,
395
401
  fontKey: state.fontKey,
402
+ ...state.font.fixedPitch === true ? { fixedPitch: true } : {},
396
403
  ...state.font.name !== void 0 ? { fontName: state.font.name } : {},
397
404
  ...state.font.type3 ? { type3: true } : {},
398
405
  ...state.renderMode === 3 || state.renderMode === 7 ? { invisible: true } : {},
@@ -745,7 +752,8 @@ function interpretContent(bytes, fonts, initialCtm = IDENTITY, shadings = /* @__
745
752
  vectors,
746
753
  glyphs,
747
754
  shadings: painted,
748
- outlines
755
+ outlines,
756
+ shown: codesShown
749
757
  };
750
758
  }
751
759
  /** One path segment through a matrix — the glyph's own space onto the page. */
@@ -15,6 +15,16 @@ export interface Display {
15
15
  x: number;
16
16
  y: number;
17
17
  };
18
+ /**
19
+ * §17.6.20 — present when the page is READ in a frame turned back from the
20
+ * sheet it is shown on: its words run DOWN the sheet, and they are read in
21
+ * the frame where they stand upright (see {@link textFrameOf}). `width` and
22
+ * `height` above are that frame's; these are the sheet's.
23
+ */
24
+ readonly sheet?: {
25
+ readonly width: number;
26
+ readonly height: number;
27
+ };
18
28
  }
19
29
  /**
20
30
  * The {@link Display} a page's shown box and `/Rotate` describe.
@@ -27,6 +37,32 @@ export interface Display {
27
37
  * @returns Its size, its turn, and the map onto it.
28
38
  */
29
39
  export declare function displayOf(page: PdfPage): Display;
40
+ /**
41
+ * The frame a page whose words run DOWN its sheet is read in: the sheet turned
42
+ * back a quarter, so the words stand upright in it and every line, paragraph
43
+ * and margin is found the way it is on any other page.
44
+ *
45
+ * A viewer turns such a page with its words, and so does the document it is
46
+ * read into (§17.6.20 `tbRl`; see {@link wordsTurnOf} for when a page's words
47
+ * run down it): hello_world_rotated.pdf sets "Hello world"
48
+ * upright in a portrait box and turns the page by `/Rotate 90`, and read on
49
+ * the shown sheet the words were set flat across it where every viewer shows
50
+ * them running down.
51
+ *
52
+ * @param shown The page's shown geometry.
53
+ * @returns The frame, which knows the sheet it was turned back from.
54
+ */
55
+ export declare function textFrameOf(shown: Display): Display;
56
+ /**
57
+ * How a page's words stand on its sheet: the quarter turn, counter-clockwise,
58
+ * whose words outweigh every other on it together, by their letters at their
59
+ * size — or 0 where none does. A label set on its side beside the text does
60
+ * not turn a page; a page of it does.
61
+ *
62
+ * @param runs The page's runs, placed on the shown sheet.
63
+ * @returns 0, 90, 180 or 270; 270 is a page whose words run DOWN it.
64
+ */
65
+ export declare function wordsTurnOf(runs: ReadonlyArray<TextRun>): 0 | 90 | 180 | 270;
30
66
  /**
31
67
  * The same runs, placed on the shown page.
32
68
  *
@@ -46,6 +46,71 @@ function displayOf(page) {
46
46
  };
47
47
  }
48
48
  /**
49
+ * The frame a page whose words run DOWN its sheet is read in: the sheet turned
50
+ * back a quarter, so the words stand upright in it and every line, paragraph
51
+ * and margin is found the way it is on any other page.
52
+ *
53
+ * A viewer turns such a page with its words, and so does the document it is
54
+ * read into (§17.6.20 `tbRl`; see {@link wordsTurnOf} for when a page's words
55
+ * run down it): hello_world_rotated.pdf sets "Hello world"
56
+ * upright in a portrait box and turns the page by `/Rotate 90`, and read on
57
+ * the shown sheet the words were set flat across it where every viewer shows
58
+ * them running down.
59
+ *
60
+ * @param shown The page's shown geometry.
61
+ * @returns The frame, which knows the sheet it was turned back from.
62
+ */
63
+ function textFrameOf(shown) {
64
+ return {
65
+ width: shown.height,
66
+ height: shown.width,
67
+ turnDeg: shown.turnDeg === 270 || shown.turnDeg === -90 ? 0 : shown.turnDeg + 90,
68
+ place: (x, y) => {
69
+ const p = shown.place(x, y);
70
+ return {
71
+ x: shown.height - p.y,
72
+ y: p.x
73
+ };
74
+ },
75
+ sheet: {
76
+ width: shown.width,
77
+ height: shown.height
78
+ }
79
+ };
80
+ }
81
+ /**
82
+ * How a page's words stand on its sheet: the quarter turn, counter-clockwise,
83
+ * whose words outweigh every other on it together, by their letters at their
84
+ * size — or 0 where none does. A label set on its side beside the text does
85
+ * not turn a page; a page of it does.
86
+ *
87
+ * @param runs The page's runs, placed on the shown sheet.
88
+ * @returns 0, 90, 180 or 270; 270 is a page whose words run DOWN it.
89
+ */
90
+ function wordsTurnOf(runs) {
91
+ const by = [
92
+ 0,
93
+ 0,
94
+ 0,
95
+ 0
96
+ ];
97
+ let all = 0;
98
+ for (const r of runs) {
99
+ const letters = r.text.trim().length;
100
+ if (letters === 0) continue;
101
+ const weight = letters * (r.fontSizePt || 10);
102
+ all += weight;
103
+ const angle = (Math.round(r.angleDeg ?? 0) % 360 + 360) % 360;
104
+ if (angle % 90 === 0) by[angle / 90] += weight;
105
+ }
106
+ const most = by.findIndex((w) => w * 2 > all);
107
+ return most > 0 ? [
108
+ 90,
109
+ 180,
110
+ 270
111
+ ][most - 1] : 0;
112
+ }
113
+ /**
49
114
  * The same runs, placed on the shown page.
50
115
  *
51
116
  * @param runs The runs as the content stream drew them.
@@ -230,4 +295,4 @@ function placeVectors(vectors, d) {
230
295
  }).filter((v) => shows(d, v.minX, v.minY, v.maxX, v.maxY));
231
296
  }
232
297
  //#endregion
233
- export { displayOf, placeImages, placeRuns, placeVectors };
298
+ export { displayOf, placeImages, placeRuns, placeVectors, textFrameOf, wordsTurnOf };
@@ -183,7 +183,7 @@ var PdfFile = class PdfFile {
183
183
  }
184
184
  return;
185
185
  }
186
- const media = mediaBox ?? DEFAULT_MEDIA_BOX;
186
+ const media = mediaBox !== void 0 && hasArea(mediaBox) ? mediaBox : DEFAULT_MEDIA_BOX;
187
187
  out.push({
188
188
  dict: node,
189
189
  mediaBox: media,
@@ -556,6 +556,10 @@ function lastIndexOfAscii(buf, needle) {
556
556
  * bug1844576.pdf is 612×792 of paper cropped to 181×53.75, and read as its
557
557
  * media box the form on it arrived a fifth of the way down a letter page.
558
558
  */
559
+ /** Whether a rectangle bounds anything: a point or a line is no page. */
560
+ function hasArea([x0, y0, x1, y1]) {
561
+ return Math.abs(x1 - x0) > 1 && Math.abs(y1 - y0) > 1;
562
+ }
559
563
  function shownBox(media, crop) {
560
564
  if (!crop) return media;
561
565
  const [mx0, my0, mx1, my1] = media;
@@ -62,7 +62,8 @@ export declare function hasLiftableProgram(file: PdfFile, fontDict: PdfDict): bo
62
62
  /**
63
63
  * The style a font's embedded program states for itself — the `head` table's
64
64
  * macStyle, bit 0 bold and bit 1 italic — where the file carries a TrueType
65
- * program to read it from.
65
+ * program to read it from, and the weight a Type 1 program names (see
66
+ * {@link typeOneStyle}).
66
67
  *
67
68
  * @param file The document.
68
69
  * @param fontDict The font dictionary.
@@ -119,7 +119,8 @@ function hasLiftableProgram(file, fontDict) {
119
119
  /**
120
120
  * The style a font's embedded program states for itself — the `head` table's
121
121
  * macStyle, bit 0 bold and bit 1 italic — where the file carries a TrueType
122
- * program to read it from.
122
+ * program to read it from, and the weight a Type 1 program names (see
123
+ * {@link typeOneStyle}).
123
124
  *
124
125
  * @param file The document.
125
126
  * @param fontDict The font dictionary.
@@ -128,7 +129,8 @@ function hasLiftableProgram(file, fontDict) {
128
129
  */
129
130
  function programStyle(file, fontDict) {
130
131
  const program = fontProgram(file, fontDict);
131
- if (!program || program.length < 12) return void 0;
132
+ if (!program) return typeOneStyle(file, fontDict);
133
+ if (program.length < 12) return void 0;
132
134
  const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
133
135
  const tables = view.getUint16(4);
134
136
  for (let i = 0; i < tables; i++) {
@@ -144,6 +146,34 @@ function programStyle(file, fontDict) {
144
146
  };
145
147
  }
146
148
  }
149
+ /**
150
+ * §9.9 `/FontFile` — the weight a Type 1 program names in its `FontInfo`
151
+ * (`/Weight (Bold)`), read off the cleartext that opens it. pdfTeX states no
152
+ * weight in the descriptor, and names URW's bold Times "NimbusRomNo9L-Medi",
153
+ * which no rule of names reads as bold: comments.pdf's headings and captions
154
+ * came back in the body's weight. Its program says "Bold"; Computer Modern's
155
+ * say "Medium", which is their book weight.
156
+ *
157
+ * @param file The document.
158
+ * @param fontDict The font dictionary.
159
+ * @returns The style, or `undefined` where no program names a weight.
160
+ */
161
+ function typeOneStyle(file, fontDict) {
162
+ const owner = descendant(file, fontDict) ?? fontDict;
163
+ const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
164
+ if (!(descriptor instanceof Map)) return void 0;
165
+ const program = file.resolve(descriptor.get("FontFile") ?? PDF_NULL);
166
+ if (!(program instanceof PdfStream)) return void 0;
167
+ const bytes = file.streamData(program);
168
+ const clear = new TextDecoder("latin1").decode(bytes.subarray(0, TYPE_ONE_HEAD));
169
+ const weight = /\/Weight\s*\(([^)]*)\)/u.exec(clear)?.[1];
170
+ return weight === void 0 ? void 0 : {
171
+ bold: /bold|black|heavy|semib|demi/iu.test(weight),
172
+ italic: false
173
+ };
174
+ }
175
+ /** How far into a Type 1 program its `FontInfo` stands, at the most. */
176
+ var TYPE_ONE_HEAD = 8192;
147
177
  /** §9.9 `/FontFile2` — the TrueType program, off the font or its descendant. */
148
178
  function fontProgram(file, fontDict) {
149
179
  const owner = descendant(file, fontDict) ?? fontDict;
@@ -1,5 +1,13 @@
1
1
  /** One of the base encodings this reader has a table for (Annex D.2). */
2
2
  export type BaseEncodingName = 'StandardEncoding' | 'WinAnsiEncoding' | 'MacRomanEncoding';
3
+ /**
4
+ * The glyph a code of WinAnsiEncoding's Latin-1 half selects, by name (see
5
+ * `WIN_ANSI_LATIN`).
6
+ *
7
+ * @param code A one-byte code.
8
+ * @returns The Annex D.2 glyph name, or `undefined` outside 0xA0–0xFF.
9
+ */
10
+ export declare function winAnsiLatinName(code: number): string | undefined;
3
11
  /**
4
12
  * The glyph names of a base encoding, or `undefined` for a name this has no
5
13
  * table for — `/MacExpertEncoding`, whose repertory no reader here can set.
@@ -21,10 +21,32 @@ var STANDARD = new Map([
21
21
  ...table(96, "quoteleft"),
22
22
  ...table(160, ". exclamdown cent sterling fraction yen florin section currency quotesingle quotedblleft guillemotleft guilsinglleft guilsinglright fi fl", ". endash dagger daggerdbl periodcentered . paragraph bullet quotesinglbase quotedblbase quotedblright guillemotright ellipsis perthousand . questiondown", ". grave acute circumflex tilde macron breve dotaccent dieresis . ring cedilla . hungarumlaut ogonek caron", "emdash . . . . . . . . . . . . . . .", ". AE . ordfeminine . . . . Lslash Oslash OE ordmasculine . . . .", ". ae . . . dotlessi . . lslash oslash oe germandbls . . . .")
23
23
  ]);
24
+ /** Annex D.2 WIN — CP-1252. Only its punctuation block departs from Latin-1. */
25
+ var WIN_ANSI = new Map([...ASCII, ...table(128, "Euro . quotesinglbase florin quotedblbase ellipsis dagger daggerdbl circumflex perthousand Scaron guilsinglleft OE . Zcaron .", ". quoteleft quoteright quotedblleft quotedblright bullet endash emdash tilde trademark scaron guilsinglright oe . zcaron Ydieresis")]);
26
+ /** Annex D.2 MAC — Mac OS Roman, as the PDF variant states it. */
27
+ var MAC_ROMAN = new Map([...ASCII, ...table(128, "Adieresis Aring Ccedilla Eacute Ntilde Odieresis Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute egrave", "ecircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve ocircumflex odieresis otilde uacute ugrave ucircumflex udieresis", "dagger degree cent sterling section bullet paragraph germandbls registered copyright trademark acute dieresis notequal AE Oslash", "infinity plusminus lessequal greaterequal yen mu partialdiff summation product pi integral ordfeminine ordmasculine Omega ae oslash", "questiondown exclamdown logicalnot radical florin approxequal Delta guillemotleft guillemotright ellipsis space Agrave Atilde Otilde OE oe", "endash emdash quotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis fraction currency guilsinglleft guilsinglright fi fl", "daggerdbl periodcentered quotesinglbase quotedblbase perthousand Acircumflex Ecircumflex Aacute Edieresis Egrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex", ". Ograve Uacute Ucircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent ring cedilla hungarumlaut ogonek caron")]);
28
+ /**
29
+ * Annex D.2 WIN — the glyph NAMES of CP-1252's upper half, which the table
30
+ * above leaves to Latin-1 for the TEXT. A program addressed by name still has
31
+ * to be told which glyph 0xE9 is: `eacute`, looked up by name or through the
32
+ * character a Unicode `cmap` keys it by. (0xA0 is `space` and 0xAD `hyphen`
33
+ * here, as Annex D names the glyphs they draw.)
34
+ */
35
+ var WIN_ANSI_LATIN = table(160, "space exclamdown cent sterling currency yen brokenbar section dieresis copyright ordfeminine guillemotleft logicalnot hyphen registered macron", "degree plusminus twosuperior threesuperior acute mu paragraph periodcentered cedilla onesuperior ordmasculine guillemotright onequarter onehalf threequarters questiondown", "Agrave Aacute Acircumflex Atilde Adieresis Aring AE Ccedilla Egrave Eacute Ecircumflex Edieresis Igrave Iacute Icircumflex Idieresis", "Eth Ntilde Ograve Oacute Ocircumflex Otilde Odieresis multiply Oslash Ugrave Uacute Ucircumflex Udieresis Yacute Thorn germandbls", "agrave aacute acircumflex atilde adieresis aring ae ccedilla egrave eacute ecircumflex edieresis igrave iacute icircumflex idieresis", "eth ntilde ograve oacute ocircumflex otilde odieresis divide oslash ugrave uacute ucircumflex udieresis yacute thorn ydieresis");
36
+ /**
37
+ * The glyph a code of WinAnsiEncoding's Latin-1 half selects, by name (see
38
+ * `WIN_ANSI_LATIN`).
39
+ *
40
+ * @param code A one-byte code.
41
+ * @returns The Annex D.2 glyph name, or `undefined` outside 0xA0–0xFF.
42
+ */
43
+ function winAnsiLatinName(code) {
44
+ return WIN_ANSI_LATIN.get(code);
45
+ }
24
46
  var TABLES = {
25
47
  StandardEncoding: STANDARD,
26
- WinAnsiEncoding: new Map([...ASCII, ...table(128, "Euro . quotesinglbase florin quotedblbase ellipsis dagger daggerdbl circumflex perthousand Scaron guilsinglleft OE . Zcaron .", ". quoteleft quoteright quotedblleft quotedblright bullet endash emdash tilde trademark scaron guilsinglright oe . zcaron Ydieresis")]),
27
- MacRomanEncoding: new Map([...ASCII, ...table(128, "Adieresis Aring Ccedilla Eacute Ntilde Odieresis Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute egrave", "ecircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve ocircumflex odieresis otilde uacute ugrave ucircumflex udieresis", "dagger degree cent sterling section bullet paragraph germandbls registered copyright trademark acute dieresis notequal AE Oslash", "infinity plusminus lessequal greaterequal yen mu partialdiff summation product pi integral ordfeminine ordmasculine Omega ae oslash", "questiondown exclamdown logicalnot radical florin approxequal Delta guillemotleft guillemotright ellipsis space Agrave Atilde Otilde OE oe", "endash emdash quotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis fraction currency guilsinglleft guilsinglright fi fl", "daggerdbl periodcentered quotesinglbase quotedblbase perthousand Acircumflex Ecircumflex Aacute Edieresis Egrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex", ". Ograve Uacute Ucircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent ring cedilla hungarumlaut ogonek caron")])
48
+ WinAnsiEncoding: WIN_ANSI,
49
+ MacRomanEncoding: MAC_ROMAN
28
50
  };
29
51
  /**
30
52
  * The glyph names of a base encoding, or `undefined` for a name this has no
@@ -107,4 +129,4 @@ function isStandardLatinFace(baseFont) {
107
129
  return STANDARD_LATIN_FACES.has(baseFont.trim().replace(/^[A-Za-z]{6}\+/u, "").toLowerCase());
108
130
  }
109
131
  //#endregion
110
- export { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable };
132
+ export { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable, winAnsiLatinName };