pdf-codec.js 5.0.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -310,6 +310,49 @@ Reading a program's built-in encoding is what stops a symbol-encoded subset —
310
310
 
311
311
  **Where nothing states an answer, the answer is the replacement character plus a `text/unmapped-encoding` diagnostic, never a guess.** Two cases reach it in practice: a symbolic font with no embedded program and no `/ToUnicode`, and a subsetted font that both strips its glyph names and maps its glyphs only from private-use code points — a private-use code point identifies a glyph inside one font and says nothing about the character it draws, so it is treated as no answer rather than a wrong one.
312
312
 
313
+ ## Grouping text runs into lines and words
314
+
315
+ `readPdf` reports one positioned run per text-showing operator, and a PDF says nothing about lines or words: a line of prose can arrive as one run, as one run per word, or as one run per glyph, entirely at the producer's discretion. `groupPdfTextRuns` turns those runs back into lines of words, and `pdf-codec/text-group` serves it without the write half or the vendored fonts, alongside `pdf-codec/read`.
316
+
317
+ ```ts
318
+ import { readPdf } from "pdf-codec/read";
319
+ import { groupPdfTextRuns } from "pdf-codec/text-group";
320
+
321
+ const page = readPdf(bytes).pages[0];
322
+ const runs = page.items.filter((item) => item.kind === "text");
323
+ for (const line of groupPdfTextRuns(runs)) {
324
+ console.log(line.text);
325
+ }
326
+ ```
327
+
328
+ Two rules decide everything, and both were got wrong often enough by hand ([#1317](https://github.com/ExaDev/documents.js/issues/1317)) to be worth stating.
329
+
330
+ Two runs are on one line when their baselines sit within a fraction of an em of each other, measured against **the smaller of the two**. Taken from the larger, a 30pt heading's own window reaches the 9pt line beneath it, the merged line's runs sort by x into a sequence whose gaps are negative, a negative gap reads as one word, and the two lines concatenate into exactly the run-together text the grouping exists to prevent. The default fraction is two thirds, bounded on both sides by real typography: two consecutive lines are never closer than solid setting puts them, one full em, so it has to stay under 1; a superscript raised by at most a third of its parent's size at no less than half that size shifts by at most two thirds of its own em, so anything tighter cuts footnote markers off the line they annotate.
331
+
332
+ A run whose `widthPt` is absent states no advance, so where it ends is unknown and the gap after it is underivable. `runGapPt` reports `undefined` for that, and an undefined gap is **no evidence of a space**. Reading it as zero puts the previous run's end at its own start, turns its whole advance into an apparent gap, and produces "Com plete ly".
333
+
334
+ Gaps that are derivable become a word space at an eighth of an em (the narrowest genuine one: the standard fourteen faces set their space glyph at 250/1000 em, and justified setting compresses to no less than half of that) and a column boundary past a full em (wider than the em space, the widest single space character there is), so a table's row reads as `"North\t4.2m\t11%"` rather than as one sentence. `runsShareBaseline` and `runGapPt` are exported on their own for a consumer with its own pipeline around them, and every threshold is overridable.
335
+
336
+ Limits, each inherent to what a PDF states rather than to the implementation: grouping is by baseline alone with no page segmentation, so on a multi-column page whose columns sit at different vertical offsets a line of one can fall within tolerance of a line of the next and the two are reported as one line with a column boundary between them, which is the cut a caller needs (`document-outline.js`'s `segmentPdfRegions` finds the gutter first if you want them genuinely apart); text comes out in visual order, so a right-to-left script needs the Unicode Bidi Algorithm applied to the result; a vertically set run groups into a column instead of a line, reported with a `rotationDeg` of 270 and its own `writingMode`, with columns ordered right to left; and a word hyphenated across a line end stays split, hyphen intact, because rejoining it is a paragraph-level judgement this package leaves to its consumers.
337
+
338
+ ## Vertical writing mode
339
+
340
+ A composite font's `/Encoding` CMap chooses a writing mode (ISO 32000-1 9.7.5.1). Mode 1 is vertical: the glyphs stay upright but the text position advances **down** the page, by the descendant CIDFont's own `/DW2` and `/W2` vertical metrics rather than by its horizontal widths, and each glyph paints offset from that position by its own position vector. `readPdf` reads all of it ([#1358](https://github.com/ExaDev/documents.js/issues/1358)), from a predefined CMap's `-V` name, from the bare `V` CMap, or from an embedded CMap stream's own `/WMode`.
341
+
342
+ A vertically set run carries `writingMode: "vertical"`, and that is a separate fact from `rotationDeg`, which reports the text rendering matrix turning the glyphs themselves on their side. An ordinary Japanese column is `writingMode: "vertical"` with a `rotationDeg` of 0: upright glyphs, running downward. On such a run, `xPt`/`yPt` is the top of the column and `widthPt` measures downward.
343
+
344
+ ```ts
345
+ const runs = page.items.filter((item) => item.kind === "text");
346
+ for (const line of groupPdfTextRuns(runs)) {
347
+ // A vertical column reports rotationDeg 270 (the direction it advances) and writingMode "vertical".
348
+ console.log(line.writingMode ?? "horizontal", line.text);
349
+ }
350
+ ```
351
+
352
+ `groupPdfTextRuns` groups such runs into columns, ordered right to left, and never lets a column and a line merge even when they advance along the same axis: a column of upright glyphs and a line of glyphs turned on their side are not the same text, whatever direction they share.
353
+
354
+ What is not supported: a **predefined non-Identity CMap's own byte decoding**. `90ms-RKSJ-V` and its siblings remap multi-byte character codes to CIDs through tables this package does not carry, so a font using one still has its codes read as two-byte CIDs. That is this package's long-standing composite-font limit, unchanged by and unrelated to writing mode; `Identity-V` (what modern producers emit) is read exactly. `writePdf` produces horizontal text only, and has no way to request a vertical one.
355
+
313
356
  ## JBIG2 scope
314
357
 
315
358
  `src/image/jbig2*.ts` is a hand-written ITU-T T.88 decoder covering what real scanned PDFs actually contain.
package/dist/codec.d.cts CHANGED
@@ -54,6 +54,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
54
54
  }, z.core.$strip>;
55
55
  widthPt: z.ZodOptional<z.ZodNumber>;
56
56
  rotationDeg: z.ZodOptional<z.ZodNumber>;
57
+ writingMode: z.ZodOptional<z.ZodLiteral<"vertical">>;
57
58
  underline: z.ZodOptional<z.ZodBoolean>;
58
59
  layer: z.ZodOptional<z.ZodString>;
59
60
  actualText: z.ZodOptional<z.ZodString>;
package/dist/codec.d.ts CHANGED
@@ -54,6 +54,7 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
54
54
  }, z.core.$strip>;
55
55
  widthPt: z.ZodOptional<z.ZodNumber>;
56
56
  rotationDeg: z.ZodOptional<z.ZodNumber>;
57
+ writingMode: z.ZodOptional<z.ZodLiteral<"vertical">>;
57
58
  underline: z.ZodOptional<z.ZodBoolean>;
58
59
  layer: z.ZodOptional<z.ZodString>;
59
60
  actualText: z.ZodOptional<z.ZodString>;
@@ -177,6 +177,63 @@ function readCidWidths(w) {
177
177
  return map;
178
178
  }
179
179
  const DEFAULT_CID_WIDTH = 1e3;
180
+ const DEFAULT_VERTICAL_POSITION_Y = 880;
181
+ const DEFAULT_VERTICAL_DISPLACEMENT_Y = -1e3;
182
+ const DW2_ENTRY_COUNT = 2;
183
+ const W2_TRIPLET_LENGTH = 3;
184
+ const VERTICAL_WRITING_MODE = 1;
185
+ function readCidVerticalMetrics(w2) {
186
+ const map = /* @__PURE__ */ new Map();
187
+ if (w2 === void 0) return map;
188
+ let i = 0;
189
+ while (i < w2.length) {
190
+ const first = require_objects.asNumber(w2[i]);
191
+ if (first === void 0) {
192
+ i++;
193
+ continue;
194
+ }
195
+ const next = w2[i + 1];
196
+ if (next?.kind === "array") {
197
+ tripletsOf(next.items).forEach((triplet, index) => {
198
+ const entry = verticalEntryFrom(require_objects.asNumber(triplet[0]), require_objects.asNumber(triplet[1]), require_objects.asNumber(triplet[2]));
199
+ if (entry !== void 0) map.set(first + index, entry);
200
+ });
201
+ i += 2;
202
+ continue;
203
+ }
204
+ const last = require_objects.asNumber(next);
205
+ const entry = verticalEntryFrom(require_objects.asNumber(w2[i + 2]), require_objects.asNumber(w2[i + 3]), require_objects.asNumber(w2[i + 4]));
206
+ if (last !== void 0 && entry !== void 0) for (let cid = first; cid <= last; cid++) map.set(cid, entry);
207
+ i += 5;
208
+ }
209
+ return map;
210
+ }
211
+ function tripletsOf(items) {
212
+ const groups = [];
213
+ for (const item of items) {
214
+ const last = groups[groups.length - 1];
215
+ if (last !== void 0 && last.length < W2_TRIPLET_LENGTH) last.push(item);
216
+ else groups.push([item]);
217
+ }
218
+ return groups;
219
+ }
220
+ function verticalEntryFrom(displacementY, positionX, positionY) {
221
+ if (displacementY === void 0 || positionX === void 0 || positionY === void 0) return;
222
+ return {
223
+ displacementY,
224
+ positionX,
225
+ positionY
226
+ };
227
+ }
228
+ function predefinedCMapIsVertical(name) {
229
+ return name === "V" || name.endsWith("-V");
230
+ }
231
+ function readsVertically(fontDict, context) {
232
+ const encoding = context.resolver.resolve(require_objects.dictGet(fontDict, "Encoding"));
233
+ if (encoding?.kind === "name") return predefinedCMapIsVertical(encoding.name);
234
+ if (encoding?.kind === "stream") return require_objects.asNumber(require_objects.dictGet(encoding.dict, "WMode")) === VERTICAL_WRITING_MODE;
235
+ return false;
236
+ }
180
237
  function buildCompositeFont(fontDict, context) {
181
238
  const baseFont = require_objects.asName(require_objects.dictGet(fontDict, "BaseFont")) ?? "Helvetica";
182
239
  const descendants = require_objects.asArray(require_objects.dictGet(fontDict, "DescendantFonts"));
@@ -186,9 +243,22 @@ function buildCompositeFont(fontDict, context) {
186
243
  const dw = descendantDict !== void 0 ? require_objects.asNumber(require_objects.dictGet(descendantDict, "DW")) ?? DEFAULT_CID_WIDTH : DEFAULT_CID_WIDTH;
187
244
  const widthMap = readCidWidths(descendantDict !== void 0 ? require_objects.asArray(require_objects.dictGet(descendantDict, "W")) : void 0);
188
245
  const widthOf = (cid) => widthMap.get(cid) ?? dw;
246
+ const dw2 = descendantDict !== void 0 ? require_objects.asArray(require_objects.dictGet(descendantDict, "DW2")) : void 0;
247
+ const defaultPositionY = (dw2?.length === DW2_ENTRY_COUNT ? require_objects.asNumber(dw2[0]) : void 0) ?? DEFAULT_VERTICAL_POSITION_Y;
248
+ const defaultDisplacementY = (dw2?.length === DW2_ENTRY_COUNT ? require_objects.asNumber(dw2[1]) : void 0) ?? DEFAULT_VERTICAL_DISPLACEMENT_Y;
249
+ const verticalMap = readCidVerticalMetrics(descendantDict !== void 0 ? require_objects.asArray(require_objects.dictGet(descendantDict, "W2")) : void 0);
250
+ const verticalMetricsOf = (cid) => {
251
+ const entry = verticalMap.get(cid);
252
+ return {
253
+ displacementY: entry?.displacementY ?? defaultDisplacementY,
254
+ positionX: entry?.positionX ?? widthOf(cid) / 2,
255
+ positionY: entry?.positionY ?? defaultPositionY
256
+ };
257
+ };
189
258
  const toUnicode = readToUnicodeCMap(fontDict, context);
190
259
  const cidToGidMap = descendantDict !== void 0 ? require_objects.dictGet(descendantDict, "CIDToGIDMap") : void 0;
191
- const programEncoding = require_objects.isName(require_objects.dictGet(fontDict, "Encoding"), "Identity-H") && (cidToGidMap === void 0 || require_objects.isName(cidToGidMap, "Identity")) ? lazyFontProgram(descriptor, context) : void 0;
260
+ const encodingName = require_objects.dictGet(fontDict, "Encoding");
261
+ const programEncoding = (require_objects.isName(encodingName, "Identity-H") || require_objects.isName(encodingName, "Identity-V")) && (cidToGidMap === void 0 || require_objects.isName(cidToGidMap, "Identity")) ? lazyFontProgram(descriptor, context) : void 0;
192
262
  const decodeToUnicode = (codes) => {
193
263
  let out = "";
194
264
  let glyphCount = 0;
@@ -222,7 +292,8 @@ function buildCompositeFont(fontDict, context) {
222
292
  bold,
223
293
  italic,
224
294
  widthOf,
225
- decodeToUnicode
295
+ decodeToUnicode,
296
+ ...readsVertically(fontDict, context) ? { verticalMetricsOf } : {}
226
297
  };
227
298
  }
228
299
  function createFontResolver(context) {
@@ -237,16 +308,27 @@ function createFontResolver(context) {
237
308
  return resolved;
238
309
  };
239
310
  return {
240
- metrics: { glyphAdvance(fontResourceName, resources, codes, byteOffset) {
241
- const font = resolve(fontResourceName, resources);
242
- if (font === void 0) return;
243
- const byteLengthConsumed = font.composite ? 2 : 1;
244
- const code = font.composite ? (codes[byteOffset] ?? 0) << 8 | (codes[byteOffset + 1] ?? 0) : codes[byteOffset] ?? 0;
245
- return {
246
- widthPer1000: font.widthOf(code),
247
- byteLengthConsumed
248
- };
249
- } },
311
+ metrics: {
312
+ glyphAdvance(fontResourceName, resources, codes, byteOffset) {
313
+ const font = resolve(fontResourceName, resources);
314
+ if (font === void 0) return;
315
+ const byteLengthConsumed = font.composite ? 2 : 1;
316
+ const code = font.composite ? (codes[byteOffset] ?? 0) << 8 | (codes[byteOffset + 1] ?? 0) : codes[byteOffset] ?? 0;
317
+ const vertical = font.verticalMetricsOf?.(code);
318
+ return {
319
+ widthPer1000: font.widthOf(code),
320
+ byteLengthConsumed,
321
+ ...vertical === void 0 ? {} : { vertical: {
322
+ displacementPer1000: vertical.displacementY,
323
+ positionXPer1000: vertical.positionX,
324
+ positionYPer1000: vertical.positionY
325
+ } }
326
+ };
327
+ },
328
+ isVertical(fontResourceName, resources) {
329
+ return resolve(fontResourceName, resources)?.verticalMetricsOf !== void 0;
330
+ }
331
+ },
250
332
  resolve
251
333
  };
252
334
  }
@@ -2,6 +2,11 @@ import { PdfDiagnosticSink } from "./diagnostics.cjs";
2
2
  import { n as PdfDict } from "./objects-B7HdMeWS.cjs";
3
3
  import { FontMetricsPort, PdfObjectResolver } from "./interpret.cjs";
4
4
  //#region src/font-read.d.ts
5
+ interface VerticalGlyphMetrics {
6
+ readonly displacementY: number;
7
+ readonly positionX: number;
8
+ readonly positionY: number;
9
+ }
5
10
  interface PdfFont {
6
11
  readonly composite: boolean;
7
12
  readonly family: string;
@@ -9,6 +14,7 @@ interface PdfFont {
9
14
  readonly italic: boolean;
10
15
  widthOf(code: number): number;
11
16
  decodeToUnicode(codes: Uint8Array<ArrayBuffer>): string;
17
+ readonly verticalMetricsOf?: (code: number) => VerticalGlyphMetrics;
12
18
  }
13
19
  interface FontReadContext {
14
20
  readonly resolver: PdfObjectResolver;
@@ -20,4 +26,4 @@ interface FontResolverService {
20
26
  }
21
27
  declare function createFontResolver(context: FontReadContext): FontResolverService;
22
28
  //#endregion
23
- export { FontReadContext, FontResolverService, PdfFont, createFontResolver };
29
+ export { FontReadContext, FontResolverService, PdfFont, VerticalGlyphMetrics, createFontResolver };
@@ -2,6 +2,11 @@ import { PdfDiagnosticSink } from "./diagnostics.js";
2
2
  import { n as PdfDict } from "./objects-B7HdMeWS.js";
3
3
  import { FontMetricsPort, PdfObjectResolver } from "./interpret.js";
4
4
  //#region src/font-read.d.ts
5
+ interface VerticalGlyphMetrics {
6
+ readonly displacementY: number;
7
+ readonly positionX: number;
8
+ readonly positionY: number;
9
+ }
5
10
  interface PdfFont {
6
11
  readonly composite: boolean;
7
12
  readonly family: string;
@@ -9,6 +14,7 @@ interface PdfFont {
9
14
  readonly italic: boolean;
10
15
  widthOf(code: number): number;
11
16
  decodeToUnicode(codes: Uint8Array<ArrayBuffer>): string;
17
+ readonly verticalMetricsOf?: (code: number) => VerticalGlyphMetrics;
12
18
  }
13
19
  interface FontReadContext {
14
20
  readonly resolver: PdfObjectResolver;
@@ -20,4 +26,4 @@ interface FontResolverService {
20
26
  }
21
27
  declare function createFontResolver(context: FontReadContext): FontResolverService;
22
28
  //#endregion
23
- export { FontReadContext, FontResolverService, PdfFont, createFontResolver };
29
+ export { FontReadContext, FontResolverService, PdfFont, VerticalGlyphMetrics, createFontResolver };
package/dist/font-read.js CHANGED
@@ -176,6 +176,63 @@ function readCidWidths(w) {
176
176
  return map;
177
177
  }
178
178
  const DEFAULT_CID_WIDTH = 1e3;
179
+ const DEFAULT_VERTICAL_POSITION_Y = 880;
180
+ const DEFAULT_VERTICAL_DISPLACEMENT_Y = -1e3;
181
+ const DW2_ENTRY_COUNT = 2;
182
+ const W2_TRIPLET_LENGTH = 3;
183
+ const VERTICAL_WRITING_MODE = 1;
184
+ function readCidVerticalMetrics(w2) {
185
+ const map = /* @__PURE__ */ new Map();
186
+ if (w2 === void 0) return map;
187
+ let i = 0;
188
+ while (i < w2.length) {
189
+ const first = asNumber(w2[i]);
190
+ if (first === void 0) {
191
+ i++;
192
+ continue;
193
+ }
194
+ const next = w2[i + 1];
195
+ if (next?.kind === "array") {
196
+ tripletsOf(next.items).forEach((triplet, index) => {
197
+ const entry = verticalEntryFrom(asNumber(triplet[0]), asNumber(triplet[1]), asNumber(triplet[2]));
198
+ if (entry !== void 0) map.set(first + index, entry);
199
+ });
200
+ i += 2;
201
+ continue;
202
+ }
203
+ const last = asNumber(next);
204
+ const entry = verticalEntryFrom(asNumber(w2[i + 2]), asNumber(w2[i + 3]), asNumber(w2[i + 4]));
205
+ if (last !== void 0 && entry !== void 0) for (let cid = first; cid <= last; cid++) map.set(cid, entry);
206
+ i += 5;
207
+ }
208
+ return map;
209
+ }
210
+ function tripletsOf(items) {
211
+ const groups = [];
212
+ for (const item of items) {
213
+ const last = groups[groups.length - 1];
214
+ if (last !== void 0 && last.length < W2_TRIPLET_LENGTH) last.push(item);
215
+ else groups.push([item]);
216
+ }
217
+ return groups;
218
+ }
219
+ function verticalEntryFrom(displacementY, positionX, positionY) {
220
+ if (displacementY === void 0 || positionX === void 0 || positionY === void 0) return;
221
+ return {
222
+ displacementY,
223
+ positionX,
224
+ positionY
225
+ };
226
+ }
227
+ function predefinedCMapIsVertical(name) {
228
+ return name === "V" || name.endsWith("-V");
229
+ }
230
+ function readsVertically(fontDict, context) {
231
+ const encoding = context.resolver.resolve(dictGet(fontDict, "Encoding"));
232
+ if (encoding?.kind === "name") return predefinedCMapIsVertical(encoding.name);
233
+ if (encoding?.kind === "stream") return asNumber(dictGet(encoding.dict, "WMode")) === VERTICAL_WRITING_MODE;
234
+ return false;
235
+ }
179
236
  function buildCompositeFont(fontDict, context) {
180
237
  const baseFont = asName(dictGet(fontDict, "BaseFont")) ?? "Helvetica";
181
238
  const descendants = asArray(dictGet(fontDict, "DescendantFonts"));
@@ -185,9 +242,22 @@ function buildCompositeFont(fontDict, context) {
185
242
  const dw = descendantDict !== void 0 ? asNumber(dictGet(descendantDict, "DW")) ?? DEFAULT_CID_WIDTH : DEFAULT_CID_WIDTH;
186
243
  const widthMap = readCidWidths(descendantDict !== void 0 ? asArray(dictGet(descendantDict, "W")) : void 0);
187
244
  const widthOf = (cid) => widthMap.get(cid) ?? dw;
245
+ const dw2 = descendantDict !== void 0 ? asArray(dictGet(descendantDict, "DW2")) : void 0;
246
+ const defaultPositionY = (dw2?.length === DW2_ENTRY_COUNT ? asNumber(dw2[0]) : void 0) ?? DEFAULT_VERTICAL_POSITION_Y;
247
+ const defaultDisplacementY = (dw2?.length === DW2_ENTRY_COUNT ? asNumber(dw2[1]) : void 0) ?? DEFAULT_VERTICAL_DISPLACEMENT_Y;
248
+ const verticalMap = readCidVerticalMetrics(descendantDict !== void 0 ? asArray(dictGet(descendantDict, "W2")) : void 0);
249
+ const verticalMetricsOf = (cid) => {
250
+ const entry = verticalMap.get(cid);
251
+ return {
252
+ displacementY: entry?.displacementY ?? defaultDisplacementY,
253
+ positionX: entry?.positionX ?? widthOf(cid) / 2,
254
+ positionY: entry?.positionY ?? defaultPositionY
255
+ };
256
+ };
188
257
  const toUnicode = readToUnicodeCMap(fontDict, context);
189
258
  const cidToGidMap = descendantDict !== void 0 ? dictGet(descendantDict, "CIDToGIDMap") : void 0;
190
- const programEncoding = isName(dictGet(fontDict, "Encoding"), "Identity-H") && (cidToGidMap === void 0 || isName(cidToGidMap, "Identity")) ? lazyFontProgram(descriptor, context) : void 0;
259
+ const encodingName = dictGet(fontDict, "Encoding");
260
+ const programEncoding = (isName(encodingName, "Identity-H") || isName(encodingName, "Identity-V")) && (cidToGidMap === void 0 || isName(cidToGidMap, "Identity")) ? lazyFontProgram(descriptor, context) : void 0;
191
261
  const decodeToUnicode = (codes) => {
192
262
  let out = "";
193
263
  let glyphCount = 0;
@@ -221,7 +291,8 @@ function buildCompositeFont(fontDict, context) {
221
291
  bold,
222
292
  italic,
223
293
  widthOf,
224
- decodeToUnicode
294
+ decodeToUnicode,
295
+ ...readsVertically(fontDict, context) ? { verticalMetricsOf } : {}
225
296
  };
226
297
  }
227
298
  function createFontResolver(context) {
@@ -236,16 +307,27 @@ function createFontResolver(context) {
236
307
  return resolved;
237
308
  };
238
309
  return {
239
- metrics: { glyphAdvance(fontResourceName, resources, codes, byteOffset) {
240
- const font = resolve(fontResourceName, resources);
241
- if (font === void 0) return;
242
- const byteLengthConsumed = font.composite ? 2 : 1;
243
- const code = font.composite ? (codes[byteOffset] ?? 0) << 8 | (codes[byteOffset + 1] ?? 0) : codes[byteOffset] ?? 0;
244
- return {
245
- widthPer1000: font.widthOf(code),
246
- byteLengthConsumed
247
- };
248
- } },
310
+ metrics: {
311
+ glyphAdvance(fontResourceName, resources, codes, byteOffset) {
312
+ const font = resolve(fontResourceName, resources);
313
+ if (font === void 0) return;
314
+ const byteLengthConsumed = font.composite ? 2 : 1;
315
+ const code = font.composite ? (codes[byteOffset] ?? 0) << 8 | (codes[byteOffset + 1] ?? 0) : codes[byteOffset] ?? 0;
316
+ const vertical = font.verticalMetricsOf?.(code);
317
+ return {
318
+ widthPer1000: font.widthOf(code),
319
+ byteLengthConsumed,
320
+ ...vertical === void 0 ? {} : { vertical: {
321
+ displacementPer1000: vertical.displacementY,
322
+ positionXPer1000: vertical.positionX,
323
+ positionYPer1000: vertical.positionY
324
+ } }
325
+ };
326
+ },
327
+ isVertical(fontResourceName, resources) {
328
+ return resolve(fontResourceName, resources)?.verticalMetricsOf !== void 0;
329
+ }
330
+ },
249
331
  resolve
250
332
  };
251
333
  }
package/dist/index.cjs CHANGED
@@ -19,8 +19,12 @@ const require_measure = require("./measure.cjs");
19
19
  const require_write = require("./write.cjs");
20
20
  const require_codec = require("./codec.cjs");
21
21
  const require_font_face = require("./font-face.cjs");
22
+ const require_text_group = require("./text-group.cjs");
22
23
  const require_raster = require("./raster.cjs");
24
+ exports.DEFAULT_BASELINE_TOLERANCE_EM = require_text_group.DEFAULT_BASELINE_TOLERANCE_EM;
25
+ exports.DEFAULT_COLUMN_GAP_EM = require_text_group.DEFAULT_COLUMN_GAP_EM;
23
26
  exports.DEFAULT_VERTICAL_METRIC_POLICY = require_measure.DEFAULT_VERTICAL_METRIC_POLICY;
27
+ exports.DEFAULT_WORD_GAP_EM = require_text_group.DEFAULT_WORD_GAP_EM;
24
28
  exports.FontFaceParseError = require_font_face.FontFaceParseError;
25
29
  exports.Jbig2ParseError = require_image_jbig2_errors.Jbig2ParseError;
26
30
  exports.Jbig2UnsupportedError = require_image_jbig2_errors.Jbig2UnsupportedError;
@@ -66,6 +70,7 @@ exports.decodeCcittFax = require_image_ccitt.decodeCcittFax;
66
70
  exports.decodeJbig2Embedded = require_image_jbig2.decodeJbig2Embedded;
67
71
  exports.decodeJpeg2000 = require_image_jpeg2000.decodeJpeg2000;
68
72
  exports.filterScanlines = require_image_png_filter.filterScanlines;
73
+ exports.groupPdfTextRuns = require_text_group.groupPdfTextRuns;
69
74
  exports.loadMathFont = require_math_font.loadMathFont;
70
75
  exports.looksLikeBareCodestream = require_image_jp2_boxes.looksLikeBareCodestream;
71
76
  exports.parseJp2Container = require_image_jp2_boxes.parseJp2Container;
@@ -76,6 +81,8 @@ exports.readPdf = require_read.readPdf;
76
81
  exports.renderPdfPage = require_raster.renderPdfPage;
77
82
  exports.resolveFaceWithRegistry = require_font_registry.resolveFaceWithRegistry;
78
83
  exports.resolveStandardFont = require_fonts.resolveStandardFont;
84
+ exports.runGapPt = require_text_group.runGapPt;
85
+ exports.runsShareBaseline = require_text_group.runsShareBaseline;
79
86
  exports.scaleMathStretchConstruction = require_math_stretch.scaleMathStretchConstruction;
80
87
  exports.unfilterScanlines = require_image_png_filter.unfilterScanlines;
81
88
  exports.writePdf = require_write.writePdf;
package/dist/index.d.cts CHANGED
@@ -23,7 +23,8 @@ import { a as MathGlyphPart, c as MathVariants, n as MathGlyphAssembly, o as Mat
23
23
  import { a as scaleMathStretchConstruction, i as assembleStretchyGlyph, n as MathStretchOptions, r as MathStretchPlacement, t as MathStretchConstruction } from "./math-stretch-C2JanDAz.cjs";
24
24
  import { LoadedMathFont, MathFont, MathFontDescriptorMetrics, loadMathFont } from "./math-font.cjs";
25
25
  import { DEFAULT_VERTICAL_METRIC_POLICY, FontMeasurerOptions, StandardFontMeasurerOptions, VerticalMetricPolicy, createFontMeasurer, createStandardFontMeasurer } from "./measure.cjs";
26
+ import { DEFAULT_BASELINE_TOLERANCE_EM, DEFAULT_COLUMN_GAP_EM, DEFAULT_WORD_GAP_EM, PdfTextBox, PdfTextGroupingOptions, PdfTextLine, PdfTextRunGeometry, PdfTextWord, PdfWordSeparator, groupPdfTextRuns, runGapPt, runsShareBaseline } from "./text-group.cjs";
26
27
  import { PageRasteriser, RasterDrawOp, RasterFillRectOp, RasterFillSpec, RasterImageOp, RasterMatrix, RasterPageGeometry, RasterPathOp, RasterPathSegment, RasterRegionPt, RasterStrokeSpec, RasterSubpath, RenderPdfPageOptions, renderPdfPage } from "./raster.cjs";
27
28
  import { FontRegistryOptions, FontSubstitution, MathAssembledGlyphs, MathBox, MathColor, MathFontMetrics, MathGlyphMetrics, MathGlyphPlacement, MathGlyphRun, MathLayoutItem, MathRule, MathStretchAxis, MathStretchGlyph, MathStretchResult, MathStroke, Point, PositionedFormula, ProvidedFont, StyledFragment, StyledRun, TextMeasurer, UnderlineMetrics, WrapOptions, WrappedLine } from "document-schema.js";
28
29
  export * from "byte-codec";
29
- export { type CcittFaxImage, type CcittFaxOptions, DEFAULT_VERTICAL_METRIC_POLICY, type EmbeddedFace, type EmbeddedFaceMetrics, type EmbeddedFaceSubstitution, type FontFace, FontFaceParseError, type FontMeasurerOptions, type FontMetrics, type FontRegistry, type FontRegistryOptions, type FontSubstitution, type GlyphInkBounds, type Jbig2DecodeOptions, type Jbig2Image, Jbig2ParseError, Jbig2UnsupportedError, type Jp2ChannelDefinition, type Jp2ColourSpace, type Jp2Container, type Jp2ImageHeader, type Jpeg2000ComponentMetadata, type Jpeg2000DecodeOptions, type Jpeg2000Image, type Jpeg2000Metadata, Jpeg2000ParseError, type Jpeg2000ProgressionOrder, type Jpeg2000QuantizationStyle, type Jpeg2000Transform, Jpeg2000UnsupportedError, LAYOUT_FORMAT_VERSION, LayoutAnnotation, LayoutAnnotationQuad, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachment, LayoutAttachmentSchema, LayoutDestination, LayoutDestinationSchema, LayoutDestinationTarget, LayoutDestinationTargetSchema, LayoutDocument, LayoutDocumentSchema, LayoutEllipse, LayoutEllipseSchema, LayoutFormField, LayoutFormFieldSchema, LayoutFormWidget, LayoutFormWidgetSchema, LayoutImage, LayoutImageAsset, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLink, LayoutInternalLinkSchema, LayoutItem, LayoutItemSchema, LayoutLayer, LayoutLayerSchema, LayoutLine, LayoutLineSchema, LayoutLink, LayoutLinkSchema, LayoutOutlineItem, LayoutOutlineItemSchema, LayoutPage, LayoutPageSchema, LayoutPath, LayoutPathSchema, LayoutPathSegment, LayoutPathSegmentSchema, LayoutRect, LayoutRectSchema, LayoutStructureElement, LayoutStructureElementSchema, LayoutSubpath, LayoutSubpathSchema, LayoutText, LayoutTextSchema, type LoadedMathFont, type MathAssembledGlyphs, type MathBox, type MathColor, type MathFont, type MathFontDescriptorMetrics, type MathFontMetrics, type MathGlyphAssembly, type MathGlyphConstruction, type MathGlyphMetrics, type MathGlyphPart, type MathGlyphPlacement, type MathGlyphRun, type MathGlyphVariant, type MathLayoutItem, type MathRule, type MathStretchAxis, type MathStretchConstruction, type MathStretchGlyph, type MathStretchOptions, type MathStretchPlacement, type MathStretchResult, type MathStroke, type MathVariants, NOOP_DIAGNOSTIC_SINK, type PageRasteriser, PdfBytesSchema, type PdfDiagnostic, type PdfDiagnosticSeverity, type PdfDiagnosticSink, PdfEncryptedError, PdfEncryptionError, type PdfEncryptionOptions, type PdfEncryptionPermissions, type PdfEncryptionScheme, PdfParseError, PdfPasswordRequiredError, type Point, type PositionedFormula, type ProvidedFont, type RasterDrawOp, type RasterFillRectOp, type RasterFillSpec, type RasterImageOp, type RasterMatrix, type RasterPageGeometry, type RasterPathOp, type RasterPathSegment, type RasterRegionPt, type RasterStrokeSpec, type RasterSubpath, type ReadPdfOptions, type RenderPdfPageOptions, type ResolvedFace, type ResolvedFont, STANDARD_METRICS, type StandardFontMeasurerOptions, type StandardFontName, type StyledFragment, type StyledRun, type TextMeasurer, type UnderlineMetrics, type VerticalMetricPolicy, type WinAnsiSubstitution, type WrapOptions, type WrappedLine, type WritePdfOptions, assembleStretchyGlyph, createFontMeasurer, createFontRegistry, createStandardFontMeasurer, decodeCcittFax, decodeJbig2Embedded, decodeJpeg2000, filterScanlines, loadMathFont, looksLikeBareCodestream, parseJp2Container, pdfCodec, readFontFace, readJpeg2000Metadata, readPdf, renderPdfPage, resolveFaceWithRegistry, resolveStandardFont, scaleMathStretchConstruction, unfilterScanlines, writePdf };
30
+ export { type CcittFaxImage, type CcittFaxOptions, DEFAULT_BASELINE_TOLERANCE_EM, DEFAULT_COLUMN_GAP_EM, DEFAULT_VERTICAL_METRIC_POLICY, DEFAULT_WORD_GAP_EM, type EmbeddedFace, type EmbeddedFaceMetrics, type EmbeddedFaceSubstitution, type FontFace, FontFaceParseError, type FontMeasurerOptions, type FontMetrics, type FontRegistry, type FontRegistryOptions, type FontSubstitution, type GlyphInkBounds, type Jbig2DecodeOptions, type Jbig2Image, Jbig2ParseError, Jbig2UnsupportedError, type Jp2ChannelDefinition, type Jp2ColourSpace, type Jp2Container, type Jp2ImageHeader, type Jpeg2000ComponentMetadata, type Jpeg2000DecodeOptions, type Jpeg2000Image, type Jpeg2000Metadata, Jpeg2000ParseError, type Jpeg2000ProgressionOrder, type Jpeg2000QuantizationStyle, type Jpeg2000Transform, Jpeg2000UnsupportedError, LAYOUT_FORMAT_VERSION, LayoutAnnotation, LayoutAnnotationQuad, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachment, LayoutAttachmentSchema, LayoutDestination, LayoutDestinationSchema, LayoutDestinationTarget, LayoutDestinationTargetSchema, LayoutDocument, LayoutDocumentSchema, LayoutEllipse, LayoutEllipseSchema, LayoutFormField, LayoutFormFieldSchema, LayoutFormWidget, LayoutFormWidgetSchema, LayoutImage, LayoutImageAsset, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLink, LayoutInternalLinkSchema, LayoutItem, LayoutItemSchema, LayoutLayer, LayoutLayerSchema, LayoutLine, LayoutLineSchema, LayoutLink, LayoutLinkSchema, LayoutOutlineItem, LayoutOutlineItemSchema, LayoutPage, LayoutPageSchema, LayoutPath, LayoutPathSchema, LayoutPathSegment, LayoutPathSegmentSchema, LayoutRect, LayoutRectSchema, LayoutStructureElement, LayoutStructureElementSchema, LayoutSubpath, LayoutSubpathSchema, LayoutText, LayoutTextSchema, type LoadedMathFont, type MathAssembledGlyphs, type MathBox, type MathColor, type MathFont, type MathFontDescriptorMetrics, type MathFontMetrics, type MathGlyphAssembly, type MathGlyphConstruction, type MathGlyphMetrics, type MathGlyphPart, type MathGlyphPlacement, type MathGlyphRun, type MathGlyphVariant, type MathLayoutItem, type MathRule, type MathStretchAxis, type MathStretchConstruction, type MathStretchGlyph, type MathStretchOptions, type MathStretchPlacement, type MathStretchResult, type MathStroke, type MathVariants, NOOP_DIAGNOSTIC_SINK, type PageRasteriser, PdfBytesSchema, type PdfDiagnostic, type PdfDiagnosticSeverity, type PdfDiagnosticSink, PdfEncryptedError, PdfEncryptionError, type PdfEncryptionOptions, type PdfEncryptionPermissions, type PdfEncryptionScheme, PdfParseError, PdfPasswordRequiredError, type PdfTextBox, type PdfTextGroupingOptions, type PdfTextLine, type PdfTextRunGeometry, type PdfTextWord, type PdfWordSeparator, type Point, type PositionedFormula, type ProvidedFont, type RasterDrawOp, type RasterFillRectOp, type RasterFillSpec, type RasterImageOp, type RasterMatrix, type RasterPageGeometry, type RasterPathOp, type RasterPathSegment, type RasterRegionPt, type RasterStrokeSpec, type RasterSubpath, type ReadPdfOptions, type RenderPdfPageOptions, type ResolvedFace, type ResolvedFont, STANDARD_METRICS, type StandardFontMeasurerOptions, type StandardFontName, type StyledFragment, type StyledRun, type TextMeasurer, type UnderlineMetrics, type VerticalMetricPolicy, type WinAnsiSubstitution, type WrapOptions, type WrappedLine, type WritePdfOptions, assembleStretchyGlyph, createFontMeasurer, createFontRegistry, createStandardFontMeasurer, decodeCcittFax, decodeJbig2Embedded, decodeJpeg2000, filterScanlines, groupPdfTextRuns, loadMathFont, looksLikeBareCodestream, parseJp2Container, pdfCodec, readFontFace, readJpeg2000Metadata, readPdf, renderPdfPage, resolveFaceWithRegistry, resolveStandardFont, runGapPt, runsShareBaseline, scaleMathStretchConstruction, unfilterScanlines, writePdf };
package/dist/index.d.ts CHANGED
@@ -23,7 +23,8 @@ import { a as MathGlyphPart, c as MathVariants, n as MathGlyphAssembly, o as Mat
23
23
  import { a as scaleMathStretchConstruction, i as assembleStretchyGlyph, n as MathStretchOptions, r as MathStretchPlacement, t as MathStretchConstruction } from "./math-stretch-BbC2o4Br.js";
24
24
  import { LoadedMathFont, MathFont, MathFontDescriptorMetrics, loadMathFont } from "./math-font.js";
25
25
  import { DEFAULT_VERTICAL_METRIC_POLICY, FontMeasurerOptions, StandardFontMeasurerOptions, VerticalMetricPolicy, createFontMeasurer, createStandardFontMeasurer } from "./measure.js";
26
+ import { DEFAULT_BASELINE_TOLERANCE_EM, DEFAULT_COLUMN_GAP_EM, DEFAULT_WORD_GAP_EM, PdfTextBox, PdfTextGroupingOptions, PdfTextLine, PdfTextRunGeometry, PdfTextWord, PdfWordSeparator, groupPdfTextRuns, runGapPt, runsShareBaseline } from "./text-group.js";
26
27
  import { PageRasteriser, RasterDrawOp, RasterFillRectOp, RasterFillSpec, RasterImageOp, RasterMatrix, RasterPageGeometry, RasterPathOp, RasterPathSegment, RasterRegionPt, RasterStrokeSpec, RasterSubpath, RenderPdfPageOptions, renderPdfPage } from "./raster.js";
27
28
  import { FontRegistryOptions, FontSubstitution, MathAssembledGlyphs, MathBox, MathColor, MathFontMetrics, MathGlyphMetrics, MathGlyphPlacement, MathGlyphRun, MathLayoutItem, MathRule, MathStretchAxis, MathStretchGlyph, MathStretchResult, MathStroke, Point, PositionedFormula, ProvidedFont, StyledFragment, StyledRun, TextMeasurer, UnderlineMetrics, WrapOptions, WrappedLine } from "document-schema.js";
28
29
  export * from "byte-codec";
29
- export { type CcittFaxImage, type CcittFaxOptions, DEFAULT_VERTICAL_METRIC_POLICY, type EmbeddedFace, type EmbeddedFaceMetrics, type EmbeddedFaceSubstitution, type FontFace, FontFaceParseError, type FontMeasurerOptions, type FontMetrics, type FontRegistry, type FontRegistryOptions, type FontSubstitution, type GlyphInkBounds, type Jbig2DecodeOptions, type Jbig2Image, Jbig2ParseError, Jbig2UnsupportedError, type Jp2ChannelDefinition, type Jp2ColourSpace, type Jp2Container, type Jp2ImageHeader, type Jpeg2000ComponentMetadata, type Jpeg2000DecodeOptions, type Jpeg2000Image, type Jpeg2000Metadata, Jpeg2000ParseError, type Jpeg2000ProgressionOrder, type Jpeg2000QuantizationStyle, type Jpeg2000Transform, Jpeg2000UnsupportedError, LAYOUT_FORMAT_VERSION, LayoutAnnotation, LayoutAnnotationQuad, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachment, LayoutAttachmentSchema, LayoutDestination, LayoutDestinationSchema, LayoutDestinationTarget, LayoutDestinationTargetSchema, LayoutDocument, LayoutDocumentSchema, LayoutEllipse, LayoutEllipseSchema, LayoutFormField, LayoutFormFieldSchema, LayoutFormWidget, LayoutFormWidgetSchema, LayoutImage, LayoutImageAsset, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLink, LayoutInternalLinkSchema, LayoutItem, LayoutItemSchema, LayoutLayer, LayoutLayerSchema, LayoutLine, LayoutLineSchema, LayoutLink, LayoutLinkSchema, LayoutOutlineItem, LayoutOutlineItemSchema, LayoutPage, LayoutPageSchema, LayoutPath, LayoutPathSchema, LayoutPathSegment, LayoutPathSegmentSchema, LayoutRect, LayoutRectSchema, LayoutStructureElement, LayoutStructureElementSchema, LayoutSubpath, LayoutSubpathSchema, LayoutText, LayoutTextSchema, type LoadedMathFont, type MathAssembledGlyphs, type MathBox, type MathColor, type MathFont, type MathFontDescriptorMetrics, type MathFontMetrics, type MathGlyphAssembly, type MathGlyphConstruction, type MathGlyphMetrics, type MathGlyphPart, type MathGlyphPlacement, type MathGlyphRun, type MathGlyphVariant, type MathLayoutItem, type MathRule, type MathStretchAxis, type MathStretchConstruction, type MathStretchGlyph, type MathStretchOptions, type MathStretchPlacement, type MathStretchResult, type MathStroke, type MathVariants, NOOP_DIAGNOSTIC_SINK, type PageRasteriser, PdfBytesSchema, type PdfDiagnostic, type PdfDiagnosticSeverity, type PdfDiagnosticSink, PdfEncryptedError, PdfEncryptionError, type PdfEncryptionOptions, type PdfEncryptionPermissions, type PdfEncryptionScheme, PdfParseError, PdfPasswordRequiredError, type Point, type PositionedFormula, type ProvidedFont, type RasterDrawOp, type RasterFillRectOp, type RasterFillSpec, type RasterImageOp, type RasterMatrix, type RasterPageGeometry, type RasterPathOp, type RasterPathSegment, type RasterRegionPt, type RasterStrokeSpec, type RasterSubpath, type ReadPdfOptions, type RenderPdfPageOptions, type ResolvedFace, type ResolvedFont, STANDARD_METRICS, type StandardFontMeasurerOptions, type StandardFontName, type StyledFragment, type StyledRun, type TextMeasurer, type UnderlineMetrics, type VerticalMetricPolicy, type WinAnsiSubstitution, type WrapOptions, type WrappedLine, type WritePdfOptions, assembleStretchyGlyph, createFontMeasurer, createFontRegistry, createStandardFontMeasurer, decodeCcittFax, decodeJbig2Embedded, decodeJpeg2000, filterScanlines, loadMathFont, looksLikeBareCodestream, parseJp2Container, pdfCodec, readFontFace, readJpeg2000Metadata, readPdf, renderPdfPage, resolveFaceWithRegistry, resolveStandardFont, scaleMathStretchConstruction, unfilterScanlines, writePdf };
30
+ export { type CcittFaxImage, type CcittFaxOptions, DEFAULT_BASELINE_TOLERANCE_EM, DEFAULT_COLUMN_GAP_EM, DEFAULT_VERTICAL_METRIC_POLICY, DEFAULT_WORD_GAP_EM, type EmbeddedFace, type EmbeddedFaceMetrics, type EmbeddedFaceSubstitution, type FontFace, FontFaceParseError, type FontMeasurerOptions, type FontMetrics, type FontRegistry, type FontRegistryOptions, type FontSubstitution, type GlyphInkBounds, type Jbig2DecodeOptions, type Jbig2Image, Jbig2ParseError, Jbig2UnsupportedError, type Jp2ChannelDefinition, type Jp2ColourSpace, type Jp2Container, type Jp2ImageHeader, type Jpeg2000ComponentMetadata, type Jpeg2000DecodeOptions, type Jpeg2000Image, type Jpeg2000Metadata, Jpeg2000ParseError, type Jpeg2000ProgressionOrder, type Jpeg2000QuantizationStyle, type Jpeg2000Transform, Jpeg2000UnsupportedError, LAYOUT_FORMAT_VERSION, LayoutAnnotation, LayoutAnnotationQuad, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachment, LayoutAttachmentSchema, LayoutDestination, LayoutDestinationSchema, LayoutDestinationTarget, LayoutDestinationTargetSchema, LayoutDocument, LayoutDocumentSchema, LayoutEllipse, LayoutEllipseSchema, LayoutFormField, LayoutFormFieldSchema, LayoutFormWidget, LayoutFormWidgetSchema, LayoutImage, LayoutImageAsset, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLink, LayoutInternalLinkSchema, LayoutItem, LayoutItemSchema, LayoutLayer, LayoutLayerSchema, LayoutLine, LayoutLineSchema, LayoutLink, LayoutLinkSchema, LayoutOutlineItem, LayoutOutlineItemSchema, LayoutPage, LayoutPageSchema, LayoutPath, LayoutPathSchema, LayoutPathSegment, LayoutPathSegmentSchema, LayoutRect, LayoutRectSchema, LayoutStructureElement, LayoutStructureElementSchema, LayoutSubpath, LayoutSubpathSchema, LayoutText, LayoutTextSchema, type LoadedMathFont, type MathAssembledGlyphs, type MathBox, type MathColor, type MathFont, type MathFontDescriptorMetrics, type MathFontMetrics, type MathGlyphAssembly, type MathGlyphConstruction, type MathGlyphMetrics, type MathGlyphPart, type MathGlyphPlacement, type MathGlyphRun, type MathGlyphVariant, type MathLayoutItem, type MathRule, type MathStretchAxis, type MathStretchConstruction, type MathStretchGlyph, type MathStretchOptions, type MathStretchPlacement, type MathStretchResult, type MathStroke, type MathVariants, NOOP_DIAGNOSTIC_SINK, type PageRasteriser, PdfBytesSchema, type PdfDiagnostic, type PdfDiagnosticSeverity, type PdfDiagnosticSink, PdfEncryptedError, PdfEncryptionError, type PdfEncryptionOptions, type PdfEncryptionPermissions, type PdfEncryptionScheme, PdfParseError, PdfPasswordRequiredError, type PdfTextBox, type PdfTextGroupingOptions, type PdfTextLine, type PdfTextRunGeometry, type PdfTextWord, type PdfWordSeparator, type Point, type PositionedFormula, type ProvidedFont, type RasterDrawOp, type RasterFillRectOp, type RasterFillSpec, type RasterImageOp, type RasterMatrix, type RasterPageGeometry, type RasterPathOp, type RasterPathSegment, type RasterRegionPt, type RasterStrokeSpec, type RasterSubpath, type ReadPdfOptions, type RenderPdfPageOptions, type ResolvedFace, type ResolvedFont, STANDARD_METRICS, type StandardFontMeasurerOptions, type StandardFontName, type StyledFragment, type StyledRun, type TextMeasurer, type UnderlineMetrics, type VerticalMetricPolicy, type WinAnsiSubstitution, type WrapOptions, type WrappedLine, type WritePdfOptions, assembleStretchyGlyph, createFontMeasurer, createFontRegistry, createStandardFontMeasurer, decodeCcittFax, decodeJbig2Embedded, decodeJpeg2000, filterScanlines, groupPdfTextRuns, loadMathFont, looksLikeBareCodestream, parseJp2Container, pdfCodec, readFontFace, readJpeg2000Metadata, readPdf, renderPdfPage, resolveFaceWithRegistry, resolveStandardFont, runGapPt, runsShareBaseline, scaleMathStretchConstruction, unfilterScanlines, writePdf };
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ import { DEFAULT_VERTICAL_METRIC_POLICY, createFontMeasurer, createStandardFontM
18
18
  import { writePdf } from "./write.js";
19
19
  import { PdfBytesSchema, pdfCodec } from "./codec.js";
20
20
  import { FontFaceParseError, readFontFace } from "./font-face.js";
21
+ import { DEFAULT_BASELINE_TOLERANCE_EM, DEFAULT_COLUMN_GAP_EM, DEFAULT_WORD_GAP_EM, groupPdfTextRuns, runGapPt, runsShareBaseline } from "./text-group.js";
21
22
  import { renderPdfPage } from "./raster.js";
22
23
  export * from "byte-codec";
23
- export { DEFAULT_VERTICAL_METRIC_POLICY, FontFaceParseError, Jbig2ParseError, Jbig2UnsupportedError, Jpeg2000ParseError, Jpeg2000UnsupportedError, LAYOUT_FORMAT_VERSION, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachmentSchema, LayoutDestinationSchema, LayoutDestinationTargetSchema, LayoutDocumentSchema, LayoutEllipseSchema, LayoutFormFieldSchema, LayoutFormWidgetSchema, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLinkSchema, LayoutItemSchema, LayoutLayerSchema, LayoutLineSchema, LayoutLinkSchema, LayoutOutlineItemSchema, LayoutPageSchema, LayoutPathSchema, LayoutPathSegmentSchema, LayoutRectSchema, LayoutStructureElementSchema, LayoutSubpathSchema, LayoutTextSchema, NOOP_DIAGNOSTIC_SINK, PdfBytesSchema, PdfEncryptedError, PdfEncryptionError, PdfParseError, PdfPasswordRequiredError, STANDARD_METRICS, assembleStretchyGlyph, createFontMeasurer, createFontRegistry, createStandardFontMeasurer, decodeCcittFax, decodeJbig2Embedded, decodeJpeg2000, filterScanlines, loadMathFont, looksLikeBareCodestream, parseJp2Container, pdfCodec, readFontFace, readJpeg2000Metadata, readPdf, renderPdfPage, resolveFaceWithRegistry, resolveStandardFont, scaleMathStretchConstruction, unfilterScanlines, writePdf };
24
+ export { DEFAULT_BASELINE_TOLERANCE_EM, DEFAULT_COLUMN_GAP_EM, DEFAULT_VERTICAL_METRIC_POLICY, DEFAULT_WORD_GAP_EM, FontFaceParseError, Jbig2ParseError, Jbig2UnsupportedError, Jpeg2000ParseError, Jpeg2000UnsupportedError, LAYOUT_FORMAT_VERSION, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachmentSchema, LayoutDestinationSchema, LayoutDestinationTargetSchema, LayoutDocumentSchema, LayoutEllipseSchema, LayoutFormFieldSchema, LayoutFormWidgetSchema, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLinkSchema, LayoutItemSchema, LayoutLayerSchema, LayoutLineSchema, LayoutLinkSchema, LayoutOutlineItemSchema, LayoutPageSchema, LayoutPathSchema, LayoutPathSegmentSchema, LayoutRectSchema, LayoutStructureElementSchema, LayoutSubpathSchema, LayoutTextSchema, NOOP_DIAGNOSTIC_SINK, PdfBytesSchema, PdfEncryptedError, PdfEncryptionError, PdfParseError, PdfPasswordRequiredError, STANDARD_METRICS, assembleStretchyGlyph, createFontMeasurer, createFontRegistry, createStandardFontMeasurer, decodeCcittFax, decodeJbig2Embedded, decodeJpeg2000, filterScanlines, groupPdfTextRuns, loadMathFont, looksLikeBareCodestream, parseJp2Container, pdfCodec, readFontFace, readJpeg2000Metadata, readPdf, renderPdfPage, resolveFaceWithRegistry, resolveStandardFont, runGapPt, runsShareBaseline, scaleMathStretchConstruction, unfilterScanlines, writePdf };
@@ -433,24 +433,38 @@ function runContentStream(bytes, resources, initialState, context, items, depth,
433
433
  const widthPer1000 = glyph?.widthPer1000 ?? FALLBACK_GLYPH_WIDTH_PER_1000;
434
434
  const byteLength = glyph?.byteLengthConsumed ?? 1;
435
435
  const isSingleByteSpace = byteLength === 1 && codes[offset] === 32;
436
- const tx = (widthPer1000 / 1e3 * gs.fontSizePt + gs.charSpace + (isSingleByteSpace ? gs.wordSpace : 0)) * gs.horizScale;
437
- text.tm = require_matrix.multiplyMatrices(require_matrix.translationMatrix(tx, 0), text.tm);
436
+ const spacing = gs.charSpace + (isSingleByteSpace ? gs.wordSpace : 0);
437
+ const vertical = glyph?.vertical;
438
+ text.tm = vertical === void 0 ? require_matrix.multiplyMatrices(require_matrix.translationMatrix((widthPer1000 / 1e3 * gs.fontSizePt + spacing) * gs.horizScale, 0), text.tm) : require_matrix.multiplyMatrices(require_matrix.translationMatrix(0, vertical.displacementPer1000 / 1e3 * gs.fontSizePt + spacing), text.tm);
438
439
  offset += byteLength;
439
440
  }
440
441
  };
442
+ const positionVectorOf = (codes) => {
443
+ const fontResourceName = gs.fontResourceName;
444
+ if (fontResourceName === void 0) return;
445
+ const vertical = context.fontMetrics.glyphAdvance(fontResourceName, resources, codes, 0)?.vertical;
446
+ if (vertical === void 0) return;
447
+ return {
448
+ x: vertical.positionXPer1000 / 1e3,
449
+ y: vertical.positionYPer1000 / 1e3
450
+ };
451
+ };
441
452
  const showTextArray = (elements) => {
442
453
  const fontResourceName = gs.fontResourceName;
443
454
  if (fontResourceName === void 0) return;
455
+ const vertical = context.fontMetrics.isVertical(fontResourceName, resources);
444
456
  const startMatrix = computeTrm(gs, text);
445
457
  const chunks = [];
446
458
  let totalLength = 0;
459
+ let firstGlyphPosition;
447
460
  for (const el of elements) if (el.kind === "string") {
461
+ firstGlyphPosition ??= vertical ? positionVectorOf(el.bytes) : void 0;
448
462
  chunks.push(el.bytes);
449
463
  totalLength += el.bytes.length;
450
464
  advanceThroughString(el.bytes);
451
465
  } else if (el.kind === "number") {
452
- const adjustment = -(el.value / 1e3) * gs.fontSizePt * gs.horizScale;
453
- text.tm = require_matrix.multiplyMatrices(require_matrix.translationMatrix(adjustment, 0), text.tm);
466
+ const adjustment = -(el.value / 1e3) * gs.fontSizePt;
467
+ text.tm = require_matrix.multiplyMatrices(vertical ? require_matrix.translationMatrix(0, adjustment) : require_matrix.translationMatrix(adjustment * gs.horizScale, 0), text.tm);
454
468
  }
455
469
  if (totalLength === 0) return;
456
470
  const combined = new Uint8Array(totalLength);
@@ -460,15 +474,17 @@ function runContentStream(bytes, resources, initialState, context, items, depth,
460
474
  at += chunk.length;
461
475
  }
462
476
  const endMatrix = computeTrm(gs, text);
477
+ const shift = firstGlyphPosition === void 0 ? void 0 : require_matrix.translationMatrix(-firstGlyphPosition.x, -firstGlyphPosition.y);
463
478
  pushItem({
464
479
  kind: "text",
465
480
  codes: combined,
466
481
  fontResourceName,
467
482
  resources,
468
- startMatrix,
469
- endMatrix,
483
+ startMatrix: shift === void 0 ? startMatrix : require_matrix.multiplyMatrices(shift, startMatrix),
484
+ endMatrix: shift === void 0 ? endMatrix : require_matrix.multiplyMatrices(shift, endMatrix),
470
485
  sizePt: gs.fontSizePt,
471
- color: gs.fillColor
486
+ color: gs.fillColor,
487
+ ...vertical ? { vertical: true } : {}
472
488
  });
473
489
  };
474
490
  const showText = (bytes) => {
@@ -16,6 +16,7 @@ interface ExtractedTextRun {
16
16
  readonly actualText?: string;
17
17
  readonly alt?: string;
18
18
  readonly mcid?: number;
19
+ readonly vertical?: boolean;
19
20
  }
20
21
  interface ExtractedPaint {
21
22
  readonly fill: Color | undefined;
@@ -94,12 +95,19 @@ interface ExtractedInlineImage {
94
95
  readonly mcid?: number;
95
96
  }
96
97
  type ExtractedItem = ExtractedTextRun | ExtractedRect | ExtractedEllipse | ExtractedLine | ExtractedPath | ExtractedImage | ExtractedInlineImage;
98
+ interface VerticalGlyphAdvance {
99
+ readonly displacementPer1000: number;
100
+ readonly positionXPer1000: number;
101
+ readonly positionYPer1000: number;
102
+ }
97
103
  interface GlyphAdvance {
98
104
  readonly widthPer1000: number;
99
105
  readonly byteLengthConsumed: number;
106
+ readonly vertical?: VerticalGlyphAdvance;
100
107
  }
101
108
  interface FontMetricsPort {
102
109
  glyphAdvance(fontResourceName: string, resources: PdfDict, codes: Uint8Array<ArrayBuffer>, byteOffset: number): GlyphAdvance | undefined;
110
+ isVertical(fontResourceName: string, resources: PdfDict): boolean;
103
111
  }
104
112
  interface PdfObjectResolver {
105
113
  resolve(obj: PdfObject | undefined): PdfObject | undefined;
@@ -113,4 +121,4 @@ interface InterpretContext {
113
121
  }
114
122
  declare function interpretContentStream(bytes: Uint8Array<ArrayBuffer>, resources: PdfDict, context: InterpretContext): ExtractedItem[];
115
123
  //#endregion
116
- export { ExtractedEllipse, ExtractedImage, ExtractedInlineImage, ExtractedItem, ExtractedLine, ExtractedPaint, ExtractedPath, ExtractedPathSegment, ExtractedRect, ExtractedSubpath, ExtractedTextRun, FontMetricsPort, GlyphAdvance, InterpretContext, PdfObjectResolver, interpretContentStream };
124
+ export { ExtractedEllipse, ExtractedImage, ExtractedInlineImage, ExtractedItem, ExtractedLine, ExtractedPaint, ExtractedPath, ExtractedPathSegment, ExtractedRect, ExtractedSubpath, ExtractedTextRun, FontMetricsPort, GlyphAdvance, InterpretContext, PdfObjectResolver, VerticalGlyphAdvance, interpretContentStream };
@@ -16,6 +16,7 @@ interface ExtractedTextRun {
16
16
  readonly actualText?: string;
17
17
  readonly alt?: string;
18
18
  readonly mcid?: number;
19
+ readonly vertical?: boolean;
19
20
  }
20
21
  interface ExtractedPaint {
21
22
  readonly fill: Color | undefined;
@@ -94,12 +95,19 @@ interface ExtractedInlineImage {
94
95
  readonly mcid?: number;
95
96
  }
96
97
  type ExtractedItem = ExtractedTextRun | ExtractedRect | ExtractedEllipse | ExtractedLine | ExtractedPath | ExtractedImage | ExtractedInlineImage;
98
+ interface VerticalGlyphAdvance {
99
+ readonly displacementPer1000: number;
100
+ readonly positionXPer1000: number;
101
+ readonly positionYPer1000: number;
102
+ }
97
103
  interface GlyphAdvance {
98
104
  readonly widthPer1000: number;
99
105
  readonly byteLengthConsumed: number;
106
+ readonly vertical?: VerticalGlyphAdvance;
100
107
  }
101
108
  interface FontMetricsPort {
102
109
  glyphAdvance(fontResourceName: string, resources: PdfDict, codes: Uint8Array<ArrayBuffer>, byteOffset: number): GlyphAdvance | undefined;
110
+ isVertical(fontResourceName: string, resources: PdfDict): boolean;
103
111
  }
104
112
  interface PdfObjectResolver {
105
113
  resolve(obj: PdfObject | undefined): PdfObject | undefined;
@@ -113,4 +121,4 @@ interface InterpretContext {
113
121
  }
114
122
  declare function interpretContentStream(bytes: Uint8Array<ArrayBuffer>, resources: PdfDict, context: InterpretContext): ExtractedItem[];
115
123
  //#endregion
116
- export { ExtractedEllipse, ExtractedImage, ExtractedInlineImage, ExtractedItem, ExtractedLine, ExtractedPaint, ExtractedPath, ExtractedPathSegment, ExtractedRect, ExtractedSubpath, ExtractedTextRun, FontMetricsPort, GlyphAdvance, InterpretContext, PdfObjectResolver, interpretContentStream };
124
+ export { ExtractedEllipse, ExtractedImage, ExtractedInlineImage, ExtractedItem, ExtractedLine, ExtractedPaint, ExtractedPath, ExtractedPathSegment, ExtractedRect, ExtractedSubpath, ExtractedTextRun, FontMetricsPort, GlyphAdvance, InterpretContext, PdfObjectResolver, VerticalGlyphAdvance, interpretContentStream };