js.documents 14.4.124 → 14.6.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 (50) hide show
  1. package/README.md +45 -3
  2. package/dist/{composition-plan-CfvhXeOC.cjs → composition-plan-CSHdRyji.cjs} +21 -7
  3. package/dist/{composition-plan-DU9nPPN7.d.cts → composition-plan-D7mR0HWB.d.cts} +15 -1
  4. package/dist/{composition-plan-l6QPO4FX.d.ts → composition-plan-DPZq6NhK.d.ts} +15 -1
  5. package/dist/{composition-plan-f0zaExUW.js → composition-plan-DQ8lPKIW.js} +21 -7
  6. package/dist/convert/composition-plan.cjs +1 -1
  7. package/dist/convert/composition-plan.d.cts +1 -1
  8. package/dist/convert/composition-plan.d.ts +1 -1
  9. package/dist/convert/composition-plan.js +1 -1
  10. package/dist/convert/composition-to-pdf.cjs +1 -1
  11. package/dist/convert/composition-to-pdf.d.cts +1 -1
  12. package/dist/convert/composition-to-pdf.d.ts +1 -1
  13. package/dist/convert/composition-to-pdf.js +1 -1
  14. package/dist/convert/composition.cjs +1 -1
  15. package/dist/convert/composition.d.cts +1 -1
  16. package/dist/convert/composition.d.ts +1 -1
  17. package/dist/convert/composition.js +1 -1
  18. package/dist/convert/from-package.cjs +1 -1
  19. package/dist/convert/from-package.js +1 -1
  20. package/dist/convert/from-pdf.cjs +20 -6
  21. package/dist/convert/from-pdf.d.cts +19 -0
  22. package/dist/convert/from-pdf.d.ts +19 -0
  23. package/dist/convert/from-pdf.js +21 -7
  24. package/dist/convert/local.cjs +1 -1
  25. package/dist/convert/local.js +1 -1
  26. package/dist/convert/pdf-package-tables.cjs +3 -1
  27. package/dist/convert/pdf-package-tables.js +3 -1
  28. package/dist/edit/pdf/editor.cjs +4 -1
  29. package/dist/edit/pdf/editor.d.cts +1 -1
  30. package/dist/edit/pdf/editor.d.ts +1 -1
  31. package/dist/edit/pdf/editor.js +4 -1
  32. package/dist/index.cjs +28 -2
  33. package/dist/index.d.cts +4 -4
  34. package/dist/index.d.ts +4 -4
  35. package/dist/index.js +5 -5
  36. package/dist/layout/reconstruct-presentations-prod.cjs +1 -1
  37. package/dist/layout/reconstruct-presentations-prod.js +1 -1
  38. package/dist/layout/reconstruct-tables.cjs +1 -1
  39. package/dist/layout/reconstruct-tables.js +1 -1
  40. package/dist/layout/reconstruct.cjs +1 -1
  41. package/dist/layout/reconstruct.js +1 -1
  42. package/dist/markdown/render.d.cts +2 -2
  43. package/dist/markdown/render.d.ts +2 -2
  44. package/dist/markdown/write.cjs +54 -1
  45. package/dist/markdown/write.d.cts +23 -2
  46. package/dist/markdown/write.d.ts +23 -2
  47. package/dist/markdown/write.js +53 -2
  48. package/dist/{reconstruct-presentations-prod-X315aBmI.cjs → reconstruct-presentations-prod-CA4no_aw.cjs} +13 -12
  49. package/dist/{reconstruct-presentations-prod-B5fkPiRZ.js → reconstruct-presentations-prod-CC8ct-5O.js} +13 -12
  50. package/package.json +2 -2
package/README.md CHANGED
@@ -383,7 +383,49 @@ One subpath is a declared entry point in its own right: **`documents.js/read`**
383
383
  import { pdfToMarkdown } from "documents.js/read";
384
384
  ```
385
385
 
386
- It carries the ten `pdfTo*` conversions, `PdfToDocumentOptions`, `readDocumentMetadata` (with `ReadDocumentMetadataOptions`), `readNativeDocumentTree` (with `ReadNativeDocumentTreeOptions`), and `readContentDocument` (with `ReadContentDocumentOptions`) — identical functions to the root barrel's (the same forwarders, run through the composition engine's read half, and the same dispatch through the read-only codec half `src/codecs/read.ts`), never a forked behaviour; `convertDocument` and every X-to-PDF direction stay on the root barrel. `src/read-graph.test.ts` walks the entry's static import graph, follows `pdf-codec` specifiers through that package's real `exports` map into its source, and fails the build if the write path or any font asset becomes reachable.
386
+ It carries the ten `pdfTo*` conversions, `PdfToDocumentOptions`, `readDocumentMetadata` (with `ReadDocumentMetadataOptions`), `readNativeDocumentTree` (with `ReadNativeDocumentTreeOptions`), and `readContentDocument` (with `ReadContentDocumentOptions`) — identical functions to the root barrel's (the same forwarders, run through the composition engine's read half, and the same read-only dispatch: `src/codecs/read.ts`'s `CONTENT_READERS` for a content format, and pdf-codec's `readPdf` directly for pdf), never a forked behaviour; `convertDocument` and every X-to-PDF direction stay on the root barrel. `src/read-graph.test.ts` walks the entry's static import graph, follows `pdf-codec` specifiers through that package's real `exports` map into its source, and fails the build if the write path or any font asset becomes reachable.
387
+
388
+ ### Converting a selection of a PDF's pages
389
+
390
+ The PDF-sourced entry points that produce content take a `pages` option: the `pdfTo*` conversions (`PdfToDocumentOptions.pages`), `convertDocument` with a pdf source (`UnifiedConversionOptions.pages`), and `readNativeDocumentTree`/`readContentDocument` (`ReadNativeDocumentTreeOptions.pages`). It is pdf-codec's `ReadPdfOptions.pages` threaded through: zero-based page-tree indices, a negative index counted from the end (`-1` is the last page), duplicates collapsed, the result in document order, and `PdfPageSelectionError` for an index naming no page. Two entry points never take it: `readDocumentMetadata` always reads with an empty selection (and every document-level surface skipped, below), since metadata never depends on page content, so identifying a document reads no page at all; and `openPdf` always reads every page, since an editor holds a document to write back. `convertDocument` applies `pages` only when the source itself is pdf: a conversion from another format ignores it, including one whose route renders an intermediate pdf internally (docx to ods), because that pdf is not the caller's document.
391
+
392
+ ```ts
393
+ import { pdfToMarkdown } from "documents.js/read";
394
+
395
+ const coverAndLastPage = pdfToMarkdown(pdfBytes, { pages: [0, 1, 2, -1] });
396
+ ```
397
+
398
+ What a selection skips: the unselected pages' content streams are never fetched, decoded or interpreted, the fonts and images only those pages use are never resolved, and the source's embedded files are never decoded, so the result carries no attachments; a caller holding the `LayoutDocument` checks `pageCount` (set only on a page-selected read) before concluding the document has none (see pdf-codec's README for exactly what is and is not read). That is what lets a caller identify a document from its first pages, or read a code from its last page, inside a memory-capped runtime such as a Cloudflare Worker isolate, without paying for every page's content.
399
+
400
+ The output holds only the selected pages, and reconstruction sees only them, so a page can come out differently than in a full conversion. Heading detection measures text against the body size, taken as the most common text size across the pages read: a cover page read on its own, dominated by large title text, takes the title's size as its body, so the title becomes a paragraph rather than a heading. Reading the cover together with a few ordinary pages restores the document's real body size.
401
+
402
+ Markdown output labels each selected page with its original one-based page number, written as `<!-- page N -->` on its own line at the start of the page (`pageNumberMarker`), alongside the `<!-- page break -->` marker between pages that share a section, so a caller can tell which pages it got without re-deriving the selection. The label is output-only: reading markdown back keeps it as an ordinary HTML-comment paragraph, exactly as any other comment in a markdown source is kept. A full conversion writes no labels, so its output is unchanged. A caller composing the stages itself (`readPdf` with `pages`, `reconstructWordprocessing`, then `buildMarkdownText` or `renderContentDocumentToMarkdown`) gets the same labels by passing `pageNumbers: layout.pages.map((page, position) => sourcePageIndex(page, position) + 1)`; `buildMarkdownText` throws `MarkdownPageNumberCountError` when the count does not match the document's pages, and a non-wordprocessing document flattened by `renderContentDocumentToMarkdown` counts as a single page.
403
+
404
+ Every reconstructed node's frames name the page's index in the source PDF (the same index every destination and form widget names), which is why a page-selected conversion's reported `DocumentTree` carries no `pages` envelope: that array is indexed by frame page index, and a selection cannot fill it. Such a tree cannot be rebuilt to pdf either: `buildDocumentBytes(tree, "pdf")` needs that envelope and throws without it. A later stage's own page-addressing option, such as `page` for an svg target, counts within the selection. A page-selected `LayoutDocument` cannot be written back: `writePdf` refuses one with `PdfPartialDocumentError`. `PdfPageSelectionError`, `PdfPartialDocumentError` and `sourcePageIndex` are exported from the root barrel; the `documents.js/read` entry re-exports nothing (this package allows re-exports only in its root barrel), so a caller of that entry recognises a bad selection by the thrown error's `code` (`pdf/page-out-of-range` or `pdf/page-index-not-integer`), and resolves a page's source index as `page.index ?? position`.
405
+
406
+ ### Leaving a PDF's document-level surfaces out
407
+
408
+ The same entry points take a `surfaces` option (`PdfToDocumentOptions.surfaces`, `UnifiedConversionOptions.surfaces`, `ReadNativeDocumentTreeOptions.surfaces`): pdf-codec's `ReadPdfOptions.surfaces` threaded through, one boolean per document-level surface (`outline`, `destinations`, `form`, `layers`, `structure`, `residue`), each read unless set to `false`, so omitting it converts exactly as before. It is independent of `pages`, and `convertDocument` hands it to the same hop `pages` goes to, so an intermediate pdf a route renders internally is always read whole. Combined with a small page selection it is what keeps identifying a document from a few pages cheap on a long tagged PDF, whose structure tree and `/ParentTree` a page selection alone still parses whole.
409
+
410
+ ```ts
411
+ import { pdfToMarkdown } from "documents.js/read";
412
+
413
+ const firstPages = pdfToMarkdown(pdfBytes, {
414
+ pages: [0, 1, 2, -1],
415
+ surfaces: {
416
+ outline: false,
417
+ destinations: false,
418
+ form: false,
419
+ layers: false,
420
+ structure: false,
421
+ residue: false,
422
+ },
423
+ });
424
+ ```
425
+
426
+ A skipped surface is never parsed, and the conversion carries nothing built from it. The `LayoutDocument` such a read produces lists the skipped surfaces in `skippedSurfaces`, so an absent field there means "not read" rather than "none", and `writePdf` refuses that `LayoutDocument` with `PdfSkippedSurfacesError`. The reported `DocumentTree` keeps its outline as `outline-N` entries inside the `destinations` table: without `outline` those entries are absent (and so are the `destN` entries only the outline minted, below), and without `destinations` the table holds none of the document's destinations, while a read outline's `outline-N` entries stay in it, each without a `destination`. There are no layers or residue tables for a skipped surface, and no structure entries in `definitions` without `structure`; internal link constructs are left out without `destinations` (links to a URI are kept), and the form's `contentControl` constructs are absent without `form`. Skipping the outline also renames internal link anchors: a destination stated as a direct array gets a reader-minted `destN` name, numbered in the order the reader meets them, and the outline is read before any page, so without it the names minted for page links (and the anchors their link constructs carry) are renumbered, while each still names the same target, and the tree's `destinations` table loses the `destN` entries only the outline minted. Two skips change reconstruction itself. Without `structure`, a tagged PDF's headings, table cells and divisions are inferred from geometry exactly as for an untagged file, so they can come out differently than in a full conversion. Without `layers`, nothing marks content as belonging to a hidden layer, so content the default view hides is kept as if it were visible. Those are also the two differences that reach a rebuild: a `DocumentTree` from a conversion without `pages` still carries its `pages` envelope, so `buildDocumentBytes(tree, "pdf")` rebuilds it, and the rebuilt PDF draws hidden-layer text visibly when `layers` was skipped and holds the geometry-inferred blocks when `structure` was skipped. The rebuild never re-emits the outline, destinations, form, layers, structure or residue whether or not a surface was skipped, since it builds the PDF from the tree's metadata, page sizes, embedded fonts and the positioned content alone: text runs, images, vectors, formulas, table-cell background rectangles and border lines, sheet-cell rectangles and border lines, and URI links attached to a recovered run. An external link whose rectangle covers no recovered run (a link over an image, say) stays a link construct in the tree and is not re-emitted. Its `LayoutDocument` never carries `skippedSurfaces`, so `writePdf` never refuses a rebuild.
427
+
428
+ `readDocumentMetadata` always reads a pdf with an empty page selection and every surface skipped, since metadata comes from the trailer's `/Info`, the catalog's `/Lang` and the XMP packet alone, so identifying a document parses neither its pages nor its outline, destinations, form, layers, structure tree or residue, and returns the same metadata a full read does. `openPdf` takes neither option, since an editor holds a whole document to write back. `ReadPdfSurfaces`, `LayoutDocumentSurface` and `PdfSkippedSurfacesError` are exported from the root barrel.
387
429
 
388
430
  ### Reading and building xlsx content directly
389
431
 
@@ -821,8 +863,8 @@ The package is layered from generic primitives outward to the two conversion dir
821
863
  - **`src/firebird/`** — Tier 3: gbak logical-backup reader. `reader.ts` (attribute framing + RLE decompression + XDR decoding), `schema.ts`/`data.ts` (table/row walking). No ratified spec — built against Firebird's own engine source.
822
864
  - **`src/odb/`** — decoder-selection and pivot-mapping: `read.ts` routes to the right tier, `spreadsheet.ts`/`csv.ts` map to output formats. `odb/sql/` is the bounded SQL engine, `odb/formula/` is the rpt formula engine, `odb/report/` is the renderer, `odb/values.ts` is shared comparison/aggregation semantics.
823
865
  - **`src/convert/`** — the composition layer: `convert.ts` (the named conversion functions and their option types), `composition.ts` (the pathfinder, the primitive registry, the bridge/fromPdf executors, and `convertDocumentFromPdf` — the read half of the engine), `composition-to-pdf.ts` (`executeToPdf`, the layout-engine registry, and the full `convertDocument` binding — the write half, split out so a read-only consumer never statically imports a renderer), `from-pdf.ts` (the `pdfTo*` family, `readDocumentMetadata`, `readNativeDocumentTree`, `readContentDocument`, and the `documents.js/read` entry), `codec.ts` (`z.codec()` pairs), `port.ts`/`local.ts` (the `DocumentConverter` port), `variant-bridges.ts` (cross-variant semantic transforms), and `from-package.ts` (`buildDocumentBytes`, which flattens once at the boundary). The tree ⇄ flat transform those construction sites call — `assembleTree`, `decompose`, `flattenTree`, `factorStyles` — is not implemented here: it lives in `document-schema.js`, which owns both encodings. `bijection.test.ts` is this package's own gate on it, re-running the three laws over the real corpus every reader, editor, and conversion here produces.
824
- - **`src/codecs/`** — `DOCUMENT_FORMAT_CODECS`: every format's read/build capability as data, so `readDocumentMetadata`/`setDocumentMetadata`/`buildDocumentBytes` dispatch through one registry.
825
- - **`src/metadata/`** — cross-format metadata read/write via `DOCUMENT_FORMAT_CODECS`.
866
+ - **`src/codecs/`** — `DOCUMENT_FORMAT_CODECS`: every format's read/build capability as data, so `setDocumentMetadata`/`buildDocumentBytes` dispatch through one registry; its read halves live in `read.ts`, whose `CONTENT_READERS` `readDocumentMetadata` uses for content formats (it reads pdf through pdf-codec's `readPdf` directly, with no page selected).
867
+ - **`src/metadata/`**: cross-format metadata write via `DOCUMENT_FORMAT_CODECS`; the read side is `readDocumentMetadata` in `src/convert/from-pdf.ts`, which reads a content format through `CONTENT_READERS` and a pdf through `readPdf` with an empty page selection and every document-level surface skipped.
826
868
  - **`src/package-codec.ts`** — `decodeDocumentPackage`/`encodeDocumentPackage`/`decodeOdbPackage`.
827
869
 
828
870
  Dependency direction is downward and checkable. Twelve external dependencies each own a distinct concern: `ooxml.js` (docx/pptx/xlsx), `odf.js` (odt/ods/odp/odg), `document-schema.js` (shared schemas + port contracts), `pdf-codec` (PDF codec + text-layout/font primitives), `byte-codec` (byte/image utilities), `markdown-codec` (markdown), `rtf-codec` (rtf), `wpd-codec` (wpd, read-only), `doc-codec` (doc), `xls-codec` (xls), `ppt-codec` (ppt, through this package's own `src/ppt/` envelope adapter), `archive-codec` (the `[MS-CFB]` compound-file detection all three legacy binary codecs' own bytes schemas build on). No `PdfObject`/`PdfDict`/`PdfStream` type appears anywhere in this package.
@@ -23,7 +23,7 @@ const require_csv_write = require("./csv/write.cjs");
23
23
  const require_read$1 = require("./read-CahCZMwG.cjs");
24
24
  const require_svg_write = require("./svg/write.cjs");
25
25
  const require_ports_abort = require("./ports/abort.cjs");
26
- const require_reconstruct_presentations_prod = require("./reconstruct-presentations-prod-X315aBmI.cjs");
26
+ const require_reconstruct_presentations_prod = require("./reconstruct-presentations-prod-CA4no_aw.cjs");
27
27
  const require_convert_pdf_package_tables = require("./convert/pdf-package-tables.cjs");
28
28
  const require_convert_variant_bridges = require("./convert/variant-bridges.cjs");
29
29
  const require_convert_capability = require("./convert/capability.cjs");
@@ -34,6 +34,7 @@ let doc_codec = require("doc-codec");
34
34
  let xls_codec = require("xls-codec");
35
35
  let rtf_codec = require("rtf-codec");
36
36
  let epub_codec = require("epub-codec");
37
+ let pdf_codec_layout = require("pdf-codec/layout");
37
38
  require("markdown-codec");
38
39
  let wpd_codec = require("wpd-codec");
39
40
  let pdf_codec_read = require("pdf-codec/read");
@@ -127,7 +128,7 @@ const FORMAT_NODES = {
127
128
  signal: options?.signal,
128
129
  images: options?.images
129
130
  }),
130
- build: (content) => require_markdown_write.buildMarkdownText(content),
131
+ build: (content, options) => require_markdown_write.buildMarkdownText(content, { pageNumbers: options?.pageNumbers }),
131
132
  encode: (text) => require_markdown_text.encodeMarkdownText(text),
132
133
  hasSourcePackage: false
133
134
  },
@@ -335,23 +336,32 @@ function executeBridge(source, target, bytes, options) {
335
336
  options?.onDocument?.((0, document_schema_js.assembleTree)(buildContent));
336
337
  return out;
337
338
  }
339
+ function layoutPageNumbers(layout) {
340
+ return layout.pages.map((page, position) => (0, pdf_codec_layout.sourcePageIndex)(page, position) + 1);
341
+ }
338
342
  function executeFromPdf(target, bytes, options) {
339
343
  const node = FORMAT_NODES[target];
340
344
  const layout = (0, pdf_codec_read.readPdf)(bytes, {
341
345
  signal: options?.signal,
342
- sink: options?.sink
346
+ sink: options?.sink,
347
+ pages: options?.pages,
348
+ surfaces: options?.surfaces
343
349
  });
344
350
  const content = RECONSTRUCTORS[node.variant](layout, {
345
351
  signal: options?.signal,
346
352
  onCellTypeInference: options?.onCellTypeInference
347
353
  });
348
- const pages = layout.pages.map((page) => ({
354
+ const selected = options?.pages !== void 0;
355
+ const pages = selected ? void 0 : layout.pages.map((page) => ({
349
356
  widthPt: page.widthPt,
350
357
  heightPt: page.heightPt
351
358
  }));
352
359
  let out;
353
360
  if (isTextFormatNode(node)) {
354
- const text = node.build(content, options);
361
+ const text = node.build(content, {
362
+ ...options,
363
+ pageNumbers: selected ? layoutPageNumbers(layout) : void 0
364
+ });
355
365
  out = node.encode(text);
356
366
  } else {
357
367
  const pkg = node.build(content);
@@ -367,9 +377,13 @@ function runCompositionPlan(plan, bytes, options, executors) {
367
377
  for (let i = 0; i < plan.hops.length; i++) {
368
378
  const hop = plan.hops[i];
369
379
  if (hop === void 0) throw new Error("runCompositionPlan: resolveCompositionPlan returned a malformed hop");
370
- const hopOptions = i === plan.hops.length - 1 ? options : options === void 0 ? void 0 : {
380
+ const isLastHop = i === plan.hops.length - 1;
381
+ const readsCallerPdf = i === 0 && hop.from === "pdf";
382
+ const hopOptions = options === void 0 ? void 0 : {
371
383
  ...options,
372
- onDocument: void 0
384
+ onDocument: isLastHop ? options.onDocument : void 0,
385
+ pages: readsCallerPdf ? options.pages : void 0,
386
+ surfaces: readsCallerPdf ? options.surfaces : void 0
373
387
  };
374
388
  if (hop.executor === "toPdf") {
375
389
  if (!isSourceContentFormat(hop.from)) throw new Error(`runCompositionPlan: toPdf source '${hop.from}' is not a content format`);
@@ -9,6 +9,7 @@ import { ContentDocument, DocumentTree, FontSubstitution, ProvidedFont } from "d
9
9
  import { LayoutDocument, PdfDiagnosticSink, WinAnsiSubstitution } from "pdf-codec";
10
10
  import { MarkdownImageResolver } from "markdown-codec";
11
11
  import { Package } from "ooxml.js";
12
+ import { ReadPdfSurfaces } from "pdf-codec/read";
12
13
  //#region src/convert/composition.d.ts
13
14
  declare const THROUGH_PDF_EDGE_COST = 3;
14
15
  declare const MAX_CONVERSION_PATH_NODES = 4;
@@ -33,6 +34,19 @@ interface UnifiedConversionOptions {
33
34
  readonly page?: number;
34
35
  readonly onSvgDiagnostic?: SvgDiagnosticSink;
35
36
  readonly clock?: ClockPort;
37
+ /**
38
+ * The pages of a pdf source to convert, as pdf-codec's `ReadPdfOptions.pages` reads them: zero-based page-tree indices, a negative index counted from the end, duplicates collapsed, the result in document order, and an index naming no page throwing `PdfPageSelectionError`. Only the plan's first hop receives it, and only when the conversion's source is pdf: a conversion from any other format ignores it, including one whose route renders an intermediate pdf internally (docx to ods goes through toPdf then fromPdf), since that pdf is not the caller's document. The unselected pages' content streams, the fonts and images only they use, and the source's embedded files are never read (see `ReadPdfOptions.pages` for exactly what is and is not skipped).
39
+ *
40
+ * The output holds only those pages. A markdown target labels each with its original one-based page number (`<!-- page 7 -->` at the start of the page, beside the usual `<!-- page break -->` between pages), so the text says which pages it holds. A page-addressing option of a later stage, such as `page` for an svg target, counts within the selection. Reconstruction infers from the selected pages alone, so a page can reconstruct differently than it would in a full conversion: the body text size heading detection compares against is the most common size across the selection, which on a cover page dominated by large title text is the title's own size, leaving the title a paragraph rather than a heading. The DocumentTree `onDocument` reports carries no `pages` envelope for a page-selected conversion: its frames name source page indices, which an envelope holding only the selected pages could not be indexed by.
41
+ */
42
+ readonly pages?: readonly number[];
43
+ /**
44
+ * The document-level surfaces of a pdf source to read, as pdf-codec's `ReadPdfOptions.surfaces` takes them: one boolean per surface (`outline`, `destinations`, `form`, `layers`, `structure`, `residue`), each read unless set to `false`, so omitting it reads everything as before. Independent of `pages`, and handed to the same hop: only the plan's first hop, and only when the conversion's source is pdf, so an intermediate pdf a route renders internally is always read whole. A skipped surface is never parsed and is absent from the read, so what the conversion builds from it is absent too: no `outline-N` entries in the tree's `destinations` table without `outline`, no destinations and no internal link constructs without `destinations` (a link to a URI is kept, and a read outline's `outline-N` entries stay, each without a `destination`), no form `contentControl` constructs without `form`, no layers table without `layers`, no structure entries in `definitions` without `structure`, no residue without `residue`; skipping the outline also renumbers the reader-minted `destN` names internal link anchors carry, since the outline mints its direct destinations first. Two skips also change reconstruction: without `structure` a tagged PDF's headings, table cells and divisions are inferred from geometry as for an untagged file, and without `layers` content the default view hides cannot be told apart and is kept as if visible. See `ReadPdfOptions.surfaces` for exactly what each covers.
45
+ */
46
+ readonly surfaces?: ReadPdfSurfaces;
47
+ }
48
+ interface TextBuildContext {
49
+ readonly pageNumbers?: readonly number[];
36
50
  }
37
51
  type ContentFormat = "docx" | "pptx" | "xlsx" | "odt" | "odp" | "ods" | "odg" | "svg" | "csv" | "markdown" | "rtf" | "doc" | "xls" | "ppt" | "epub";
38
52
  declare const CONTENT_FORMATS: readonly ContentFormat[];
@@ -51,7 +65,7 @@ interface TextFormatNode {
51
65
  readonly family: "markdown" | "csv" | "svg" | "rtf" | "doc" | "xls" | "ppt" | "epub";
52
66
  readonly decode: (bytes: Uint8Array<ArrayBuffer>) => string;
53
67
  readonly read: (text: string, options?: UnifiedConversionOptions) => ContentDocument;
54
- readonly build: (content: ContentDocument, options?: UnifiedConversionOptions) => string;
68
+ readonly build: (content: ContentDocument, options?: UnifiedConversionOptions & TextBuildContext) => string;
55
69
  readonly encode: (text: string) => Uint8Array<ArrayBuffer>;
56
70
  readonly hasSourcePackage: false;
57
71
  }
@@ -9,6 +9,7 @@ import { Package } from "ooxml.js";
9
9
  import { ContentDocument, DocumentTree, FontSubstitution, ProvidedFont } from "document-schema.js";
10
10
  import { LayoutDocument, PdfDiagnosticSink, WinAnsiSubstitution } from "pdf-codec";
11
11
  import { MarkdownImageResolver } from "markdown-codec";
12
+ import { ReadPdfSurfaces } from "pdf-codec/read";
12
13
  //#region src/convert/composition.d.ts
13
14
  declare const THROUGH_PDF_EDGE_COST = 3;
14
15
  declare const MAX_CONVERSION_PATH_NODES = 4;
@@ -33,6 +34,19 @@ interface UnifiedConversionOptions {
33
34
  readonly page?: number;
34
35
  readonly onSvgDiagnostic?: SvgDiagnosticSink;
35
36
  readonly clock?: ClockPort;
37
+ /**
38
+ * The pages of a pdf source to convert, as pdf-codec's `ReadPdfOptions.pages` reads them: zero-based page-tree indices, a negative index counted from the end, duplicates collapsed, the result in document order, and an index naming no page throwing `PdfPageSelectionError`. Only the plan's first hop receives it, and only when the conversion's source is pdf: a conversion from any other format ignores it, including one whose route renders an intermediate pdf internally (docx to ods goes through toPdf then fromPdf), since that pdf is not the caller's document. The unselected pages' content streams, the fonts and images only they use, and the source's embedded files are never read (see `ReadPdfOptions.pages` for exactly what is and is not skipped).
39
+ *
40
+ * The output holds only those pages. A markdown target labels each with its original one-based page number (`<!-- page 7 -->` at the start of the page, beside the usual `<!-- page break -->` between pages), so the text says which pages it holds. A page-addressing option of a later stage, such as `page` for an svg target, counts within the selection. Reconstruction infers from the selected pages alone, so a page can reconstruct differently than it would in a full conversion: the body text size heading detection compares against is the most common size across the selection, which on a cover page dominated by large title text is the title's own size, leaving the title a paragraph rather than a heading. The DocumentTree `onDocument` reports carries no `pages` envelope for a page-selected conversion: its frames name source page indices, which an envelope holding only the selected pages could not be indexed by.
41
+ */
42
+ readonly pages?: readonly number[];
43
+ /**
44
+ * The document-level surfaces of a pdf source to read, as pdf-codec's `ReadPdfOptions.surfaces` takes them: one boolean per surface (`outline`, `destinations`, `form`, `layers`, `structure`, `residue`), each read unless set to `false`, so omitting it reads everything as before. Independent of `pages`, and handed to the same hop: only the plan's first hop, and only when the conversion's source is pdf, so an intermediate pdf a route renders internally is always read whole. A skipped surface is never parsed and is absent from the read, so what the conversion builds from it is absent too: no `outline-N` entries in the tree's `destinations` table without `outline`, no destinations and no internal link constructs without `destinations` (a link to a URI is kept, and a read outline's `outline-N` entries stay, each without a `destination`), no form `contentControl` constructs without `form`, no layers table without `layers`, no structure entries in `definitions` without `structure`, no residue without `residue`; skipping the outline also renumbers the reader-minted `destN` names internal link anchors carry, since the outline mints its direct destinations first. Two skips also change reconstruction: without `structure` a tagged PDF's headings, table cells and divisions are inferred from geometry as for an untagged file, and without `layers` content the default view hides cannot be told apart and is kept as if visible. See `ReadPdfOptions.surfaces` for exactly what each covers.
45
+ */
46
+ readonly surfaces?: ReadPdfSurfaces;
47
+ }
48
+ interface TextBuildContext {
49
+ readonly pageNumbers?: readonly number[];
36
50
  }
37
51
  type ContentFormat = "docx" | "pptx" | "xlsx" | "odt" | "odp" | "ods" | "odg" | "svg" | "csv" | "markdown" | "rtf" | "doc" | "xls" | "ppt" | "epub";
38
52
  declare const CONTENT_FORMATS: readonly ContentFormat[];
@@ -51,7 +65,7 @@ interface TextFormatNode {
51
65
  readonly family: "markdown" | "csv" | "svg" | "rtf" | "doc" | "xls" | "ppt" | "epub";
52
66
  readonly decode: (bytes: Uint8Array<ArrayBuffer>) => string;
53
67
  readonly read: (text: string, options?: UnifiedConversionOptions) => ContentDocument;
54
- readonly build: (content: ContentDocument, options?: UnifiedConversionOptions) => string;
68
+ readonly build: (content: ContentDocument, options?: UnifiedConversionOptions & TextBuildContext) => string;
55
69
  readonly encode: (text: string) => Uint8Array<ArrayBuffer>;
56
70
  readonly hasSourcePackage: false;
57
71
  }
@@ -23,7 +23,7 @@ import { buildCsvText } from "./csv/write.js";
23
23
  import { i as readSvgContent } from "./read-BSH--XB4.js";
24
24
  import { buildSvgText } from "./svg/write.js";
25
25
  import { throwIfAborted } from "./ports/abort.js";
26
- import { _ as reconstructWordprocessing, g as reconstructSpreadsheet, i as reconstructPresentation, r as reconstructDrawing } from "./reconstruct-presentations-prod-B5fkPiRZ.js";
26
+ import { _ as reconstructWordprocessing, g as reconstructSpreadsheet, i as reconstructPresentation, r as reconstructDrawing } from "./reconstruct-presentations-prod-CC8ct-5O.js";
27
27
  import { stampPdfPackageTables } from "./convert/pdf-package-tables.js";
28
28
  import { drawingToPresentation, presentationToDrawing, presentationToWordprocessing, spreadsheetToWordprocessing, wordprocessingToPresentation } from "./convert/variant-bridges.js";
29
29
  import { UnsupportedConversionError } from "./convert/capability.js";
@@ -34,6 +34,7 @@ import { readDocContent, writeDocContent } from "doc-codec";
34
34
  import { readXlsContent, writeXlsContent } from "xls-codec";
35
35
  import { readRtfContent, rtfBytesFromLatin1, writeRtfContent } from "rtf-codec";
36
36
  import { readEpubContent, writeEpubContent } from "epub-codec";
37
+ import { sourcePageIndex } from "pdf-codec/layout";
37
38
  import "markdown-codec";
38
39
  import { readWpdContent } from "wpd-codec";
39
40
  import { readPdf } from "pdf-codec/read";
@@ -127,7 +128,7 @@ const FORMAT_NODES = {
127
128
  signal: options?.signal,
128
129
  images: options?.images
129
130
  }),
130
- build: (content) => buildMarkdownText(content),
131
+ build: (content, options) => buildMarkdownText(content, { pageNumbers: options?.pageNumbers }),
131
132
  encode: (text) => encodeMarkdownText(text),
132
133
  hasSourcePackage: false
133
134
  },
@@ -335,23 +336,32 @@ function executeBridge(source, target, bytes, options) {
335
336
  options?.onDocument?.(assembleTree(buildContent));
336
337
  return out;
337
338
  }
339
+ function layoutPageNumbers(layout) {
340
+ return layout.pages.map((page, position) => sourcePageIndex(page, position) + 1);
341
+ }
338
342
  function executeFromPdf(target, bytes, options) {
339
343
  const node = FORMAT_NODES[target];
340
344
  const layout = readPdf(bytes, {
341
345
  signal: options?.signal,
342
- sink: options?.sink
346
+ sink: options?.sink,
347
+ pages: options?.pages,
348
+ surfaces: options?.surfaces
343
349
  });
344
350
  const content = RECONSTRUCTORS[node.variant](layout, {
345
351
  signal: options?.signal,
346
352
  onCellTypeInference: options?.onCellTypeInference
347
353
  });
348
- const pages = layout.pages.map((page) => ({
354
+ const selected = options?.pages !== void 0;
355
+ const pages = selected ? void 0 : layout.pages.map((page) => ({
349
356
  widthPt: page.widthPt,
350
357
  heightPt: page.heightPt
351
358
  }));
352
359
  let out;
353
360
  if (isTextFormatNode(node)) {
354
- const text = node.build(content, options);
361
+ const text = node.build(content, {
362
+ ...options,
363
+ pageNumbers: selected ? layoutPageNumbers(layout) : void 0
364
+ });
355
365
  out = node.encode(text);
356
366
  } else {
357
367
  const pkg = node.build(content);
@@ -367,9 +377,13 @@ function runCompositionPlan(plan, bytes, options, executors) {
367
377
  for (let i = 0; i < plan.hops.length; i++) {
368
378
  const hop = plan.hops[i];
369
379
  if (hop === void 0) throw new Error("runCompositionPlan: resolveCompositionPlan returned a malformed hop");
370
- const hopOptions = i === plan.hops.length - 1 ? options : options === void 0 ? void 0 : {
380
+ const isLastHop = i === plan.hops.length - 1;
381
+ const readsCallerPdf = i === 0 && hop.from === "pdf";
382
+ const hopOptions = options === void 0 ? void 0 : {
371
383
  ...options,
372
- onDocument: void 0
384
+ onDocument: isLastHop ? options.onDocument : void 0,
385
+ pages: readsCallerPdf ? options.pages : void 0,
386
+ surfaces: readsCallerPdf ? options.surfaces : void 0
373
387
  };
374
388
  if (hop.executor === "toPdf") {
375
389
  if (!isSourceContentFormat(hop.from)) throw new Error(`runCompositionPlan: toPdf source '${hop.from}' is not a content format`);
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_composition_plan = require("../composition-plan-CfvhXeOC.cjs");
2
+ const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
3
3
  exports.buildCompositionGraph = require_composition_plan.buildCompositionGraph;
4
4
  exports.isContentFormat = require_composition_plan.isContentFormat;
5
5
  exports.isSourceContentFormat = require_composition_plan.isSourceContentFormat;
@@ -1,2 +1,2 @@
1
- import { a as buildCompositionGraph, c as resolveCompositionPlan, i as HopExecutor, l as shortestPath, n as ConversionPlan, o as isContentFormat, r as GraphEdge, s as isSourceContentFormat, t as CompositionHop } from "../composition-plan-DU9nPPN7.cjs";
1
+ import { a as buildCompositionGraph, c as resolveCompositionPlan, i as HopExecutor, l as shortestPath, n as ConversionPlan, o as isContentFormat, r as GraphEdge, s as isSourceContentFormat, t as CompositionHop } from "../composition-plan-D7mR0HWB.cjs";
2
2
  export { CompositionHop, ConversionPlan, GraphEdge, HopExecutor, buildCompositionGraph, isContentFormat, isSourceContentFormat, resolveCompositionPlan, shortestPath };
@@ -1,2 +1,2 @@
1
- import { a as buildCompositionGraph, c as resolveCompositionPlan, i as HopExecutor, l as shortestPath, n as ConversionPlan, o as isContentFormat, r as GraphEdge, s as isSourceContentFormat, t as CompositionHop } from "../composition-plan-l6QPO4FX.js";
1
+ import { a as buildCompositionGraph, c as resolveCompositionPlan, i as HopExecutor, l as shortestPath, n as ConversionPlan, o as isContentFormat, r as GraphEdge, s as isSourceContentFormat, t as CompositionHop } from "../composition-plan-DPZq6NhK.js";
2
2
  export { CompositionHop, ConversionPlan, GraphEdge, HopExecutor, buildCompositionGraph, isContentFormat, isSourceContentFormat, resolveCompositionPlan, shortestPath };
@@ -1,2 +1,2 @@
1
- import { a as shortestPath, i as resolveCompositionPlan, n as isContentFormat, r as isSourceContentFormat, t as buildCompositionGraph } from "../composition-plan-f0zaExUW.js";
1
+ import { a as shortestPath, i as resolveCompositionPlan, n as isContentFormat, r as isSourceContentFormat, t as buildCompositionGraph } from "../composition-plan-DQ8lPKIW.js";
2
2
  export { buildCompositionGraph, isContentFormat, isSourceContentFormat, resolveCompositionPlan, shortestPath };
@@ -7,7 +7,7 @@ const require_ports_abort = require("../ports/abort.cjs");
7
7
  const require_sheets = require("../sheets-BQCpNXsE.cjs");
8
8
  const require_layout_drawing = require("../layout/drawing.cjs");
9
9
  const require_convert_capability = require("./capability.cjs");
10
- const require_composition_plan = require("../composition-plan-CfvhXeOC.cjs");
10
+ const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
11
11
  let document_schema_js = require("document-schema.js");
12
12
  let pdf_codec = require("pdf-codec");
13
13
  //#region src/convert/composition-to-pdf.ts
@@ -1,5 +1,5 @@
1
1
  import { DocumentFormat } from "./port.cjs";
2
- import { S as SourceContentFormat, T as UnifiedConversionOptions } from "../composition-plan-DU9nPPN7.cjs";
2
+ import { S as SourceContentFormat, T as UnifiedConversionOptions } from "../composition-plan-D7mR0HWB.cjs";
3
3
  //#region src/convert/composition-to-pdf.d.ts
4
4
  declare function executeToPdf(format: SourceContentFormat, bytes: Uint8Array<ArrayBuffer>, options?: UnifiedConversionOptions): Uint8Array<ArrayBuffer>;
5
5
  declare function convertDocument(source: DocumentFormat, target: DocumentFormat, bytes: Uint8Array<ArrayBuffer>, options?: UnifiedConversionOptions): Uint8Array<ArrayBuffer>;
@@ -1,5 +1,5 @@
1
1
  import { DocumentFormat } from "./port.js";
2
- import { S as SourceContentFormat, T as UnifiedConversionOptions } from "../composition-plan-l6QPO4FX.js";
2
+ import { S as SourceContentFormat, T as UnifiedConversionOptions } from "../composition-plan-DPZq6NhK.js";
3
3
  //#region src/convert/composition-to-pdf.d.ts
4
4
  declare function executeToPdf(format: SourceContentFormat, bytes: Uint8Array<ArrayBuffer>, options?: UnifiedConversionOptions): Uint8Array<ArrayBuffer>;
5
5
  declare function convertDocument(source: DocumentFormat, target: DocumentFormat, bytes: Uint8Array<ArrayBuffer>, options?: UnifiedConversionOptions): Uint8Array<ArrayBuffer>;
@@ -6,7 +6,7 @@ import { throwIfAborted } from "../ports/abort.js";
6
6
  import { l as convertSpreadsheetToLayout } from "../sheets-DWDvGN-g.js";
7
7
  import { convertDrawingToLayout } from "../layout/drawing.js";
8
8
  import { UnsupportedConversionError } from "./capability.js";
9
- import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, g as executeBridge, i as resolveCompositionPlan, s as FORMAT_NODES, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-f0zaExUW.js";
9
+ import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, g as executeBridge, i as resolveCompositionPlan, s as FORMAT_NODES, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-DQ8lPKIW.js";
10
10
  import { assembleTree } from "document-schema.js";
11
11
  import { createFontMeasurer, createFontRegistry, loadMathFont, writePdf } from "pdf-codec";
12
12
  //#region src/convert/composition-to-pdf.ts
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_composition_plan = require("../composition-plan-CfvhXeOC.cjs");
2
+ const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
3
3
  exports.CONTENT_FORMATS = require_composition_plan.CONTENT_FORMATS;
4
4
  exports.FORMAT_NODES = require_composition_plan.FORMAT_NODES;
5
5
  exports.LAYOUT_CAPABLE = require_composition_plan.LAYOUT_CAPABLE;
@@ -1,2 +1,2 @@
1
- import { A as isTextFormatNode, C as THROUGH_PDF_EDGE_COST, D as executeBridge, E as convertDocumentFromPdf, O as executeFromPdf, S as SourceContentFormat, T as UnifiedConversionOptions, _ as READ_ONLY_CONTENT_FORMATS, b as ReadOnlyContentFormat, d as CompositionExecutorBinding, f as ContentFormat, g as MAX_CONVERSION_PATH_NODES, h as LAYOUT_CAPABLE, j as runCompositionPlan, k as isReadOnlyContentFormat, m as FormatNode, p as FORMAT_NODES, u as CONTENT_FORMATS, v as READ_ONLY_FORMAT_NODES, w as TRANSFORMS, x as ReadOnlyFormatNode, y as RECONSTRUCTORS } from "../composition-plan-DU9nPPN7.cjs";
1
+ import { A as isTextFormatNode, C as THROUGH_PDF_EDGE_COST, D as executeBridge, E as convertDocumentFromPdf, O as executeFromPdf, S as SourceContentFormat, T as UnifiedConversionOptions, _ as READ_ONLY_CONTENT_FORMATS, b as ReadOnlyContentFormat, d as CompositionExecutorBinding, f as ContentFormat, g as MAX_CONVERSION_PATH_NODES, h as LAYOUT_CAPABLE, j as runCompositionPlan, k as isReadOnlyContentFormat, m as FormatNode, p as FORMAT_NODES, u as CONTENT_FORMATS, v as READ_ONLY_FORMAT_NODES, w as TRANSFORMS, x as ReadOnlyFormatNode, y as RECONSTRUCTORS } from "../composition-plan-D7mR0HWB.cjs";
2
2
  export { CONTENT_FORMATS, CompositionExecutorBinding, ContentFormat, FORMAT_NODES, FormatNode, LAYOUT_CAPABLE, MAX_CONVERSION_PATH_NODES, READ_ONLY_CONTENT_FORMATS, READ_ONLY_FORMAT_NODES, RECONSTRUCTORS, ReadOnlyContentFormat, ReadOnlyFormatNode, SourceContentFormat, THROUGH_PDF_EDGE_COST, TRANSFORMS, UnifiedConversionOptions, convertDocumentFromPdf, executeBridge, executeFromPdf, isReadOnlyContentFormat, isTextFormatNode, runCompositionPlan };
@@ -1,2 +1,2 @@
1
- import { A as isTextFormatNode, C as THROUGH_PDF_EDGE_COST, D as executeBridge, E as convertDocumentFromPdf, O as executeFromPdf, S as SourceContentFormat, T as UnifiedConversionOptions, _ as READ_ONLY_CONTENT_FORMATS, b as ReadOnlyContentFormat, d as CompositionExecutorBinding, f as ContentFormat, g as MAX_CONVERSION_PATH_NODES, h as LAYOUT_CAPABLE, j as runCompositionPlan, k as isReadOnlyContentFormat, m as FormatNode, p as FORMAT_NODES, u as CONTENT_FORMATS, v as READ_ONLY_FORMAT_NODES, w as TRANSFORMS, x as ReadOnlyFormatNode, y as RECONSTRUCTORS } from "../composition-plan-l6QPO4FX.js";
1
+ import { A as isTextFormatNode, C as THROUGH_PDF_EDGE_COST, D as executeBridge, E as convertDocumentFromPdf, O as executeFromPdf, S as SourceContentFormat, T as UnifiedConversionOptions, _ as READ_ONLY_CONTENT_FORMATS, b as ReadOnlyContentFormat, d as CompositionExecutorBinding, f as ContentFormat, g as MAX_CONVERSION_PATH_NODES, h as LAYOUT_CAPABLE, j as runCompositionPlan, k as isReadOnlyContentFormat, m as FormatNode, p as FORMAT_NODES, u as CONTENT_FORMATS, v as READ_ONLY_FORMAT_NODES, w as TRANSFORMS, x as ReadOnlyFormatNode, y as RECONSTRUCTORS } from "../composition-plan-DPZq6NhK.js";
2
2
  export { CONTENT_FORMATS, CompositionExecutorBinding, ContentFormat, FORMAT_NODES, FormatNode, LAYOUT_CAPABLE, MAX_CONVERSION_PATH_NODES, READ_ONLY_CONTENT_FORMATS, READ_ONLY_FORMAT_NODES, RECONSTRUCTORS, ReadOnlyContentFormat, ReadOnlyFormatNode, SourceContentFormat, THROUGH_PDF_EDGE_COST, TRANSFORMS, UnifiedConversionOptions, convertDocumentFromPdf, executeBridge, executeFromPdf, isReadOnlyContentFormat, isTextFormatNode, runCompositionPlan };
@@ -1,2 +1,2 @@
1
- import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, f as RECONSTRUCTORS, g as executeBridge, h as convertDocumentFromPdf, l as MAX_CONVERSION_PATH_NODES, m as TRANSFORMS, o as CONTENT_FORMATS, p as THROUGH_PDF_EDGE_COST, s as FORMAT_NODES, u as READ_ONLY_CONTENT_FORMATS, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-f0zaExUW.js";
1
+ import { _ as executeFromPdf, b as runCompositionPlan, c as LAYOUT_CAPABLE, d as READ_ONLY_FORMAT_NODES, f as RECONSTRUCTORS, g as executeBridge, h as convertDocumentFromPdf, l as MAX_CONVERSION_PATH_NODES, m as TRANSFORMS, o as CONTENT_FORMATS, p as THROUGH_PDF_EDGE_COST, s as FORMAT_NODES, u as READ_ONLY_CONTENT_FORMATS, v as isReadOnlyContentFormat, y as isTextFormatNode } from "../composition-plan-DQ8lPKIW.js";
2
2
  export { CONTENT_FORMATS, FORMAT_NODES, LAYOUT_CAPABLE, MAX_CONVERSION_PATH_NODES, READ_ONLY_CONTENT_FORMATS, READ_ONLY_FORMAT_NODES, RECONSTRUCTORS, THROUGH_PDF_EDGE_COST, TRANSFORMS, convertDocumentFromPdf, executeBridge, executeFromPdf, isReadOnlyContentFormat, isTextFormatNode, runCompositionPlan };
@@ -15,7 +15,7 @@ let pdf_codec = require("pdf-codec");
15
15
  //#region src/convert/from-package.ts
16
16
  function buildDocumentBytes(pkg, target) {
17
17
  if (target === "pdf") {
18
- if (pkg.pages === void 0) throw new Error("this DocumentTree has no pages — only a package dumped from a <format>-to-pdf or pdf-to-<format> conversion carries them; a bridge conversion's own dump (e.g. odt-to-docx) never does, so 'pdf' is not a reachable target from it");
18
+ if (pkg.pages === void 0) throw new Error("this DocumentTree has no pages, so 'pdf' is not a reachable target from it: only a package dumped from a <format>-to-pdf conversion or a full pdf-to-<format> conversion carries them. A bridge conversion's own dump (e.g. odt-to-docx) never does, and neither does a page-selected pdf-to-<format> conversion's, whose frames name source page indices a pages array of only the selection could not be indexed by");
19
19
  const { document: layout, formulas, fonts } = packageToLayout(pkg);
20
20
  return (0, pdf_codec.writePdf)(layout, formulas.length > 0 ? {
21
21
  formulas,
@@ -14,7 +14,7 @@ import { LAYOUT_FORMAT_VERSION, createFontMeasurer, createFontRegistry, loadMath
14
14
  //#region src/convert/from-package.ts
15
15
  function buildDocumentBytes(pkg, target) {
16
16
  if (target === "pdf") {
17
- if (pkg.pages === void 0) throw new Error("this DocumentTree has no pages — only a package dumped from a <format>-to-pdf or pdf-to-<format> conversion carries them; a bridge conversion's own dump (e.g. odt-to-docx) never does, so 'pdf' is not a reachable target from it");
17
+ if (pkg.pages === void 0) throw new Error("this DocumentTree has no pages, so 'pdf' is not a reachable target from it: only a package dumped from a <format>-to-pdf conversion or a full pdf-to-<format> conversion carries them. A bridge conversion's own dump (e.g. odt-to-docx) never does, and neither does a page-selected pdf-to-<format> conversion's, whose frames name source page indices a pages array of only the selection could not be indexed by");
18
18
  const { document: layout, formulas, fonts } = packageToLayout(pkg);
19
19
  return writePdf(layout, formulas.length > 0 ? {
20
20
  formulas,
@@ -1,20 +1,34 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-X315aBmI.cjs");
2
+ const require_reconstruct_presentations_prod = require("../reconstruct-presentations-prod-CA4no_aw.cjs");
3
3
  const require_convert_pdf_package_tables = require("./pdf-package-tables.cjs");
4
- const require_composition_plan = require("../composition-plan-CfvhXeOC.cjs");
4
+ const require_composition_plan = require("../composition-plan-CSHdRyji.cjs");
5
5
  const require_codecs_read = require("../codecs/read.cjs");
6
6
  let document_schema_js = require("document-schema.js");
7
7
  let pdf_codec_read = require("pdf-codec/read");
8
8
  //#region src/convert/from-pdf.ts
9
+ const METADATA_SURFACES = {
10
+ outline: false,
11
+ destinations: false,
12
+ form: false,
13
+ layers: false,
14
+ structure: false,
15
+ residue: false
16
+ };
9
17
  function readDocumentMetadata(format, bytes, options) {
10
- if (format === "pdf") return require_codecs_read.readDocumentLayout(bytes, { signal: options?.signal }).metadata;
18
+ if (format === "pdf") return (0, pdf_codec_read.readPdf)(bytes, {
19
+ signal: options?.signal,
20
+ pages: [],
21
+ surfaces: METADATA_SURFACES
22
+ }).metadata;
11
23
  return require_codecs_read.CONTENT_READERS[format](bytes, { signal: options?.signal }).metadata;
12
24
  }
13
25
  function readNativeContent(format, bytes, options) {
14
26
  if (format === "pdf") {
15
27
  const layout = (0, pdf_codec_read.readPdf)(bytes, {
16
28
  signal: options?.signal,
17
- sink: options?.sink
29
+ sink: options?.sink,
30
+ pages: options?.pages,
31
+ surfaces: options?.surfaces
18
32
  });
19
33
  return {
20
34
  content: require_reconstruct_presentations_prod.reconstructWordprocessing(layout, { signal: options?.signal }),
@@ -29,10 +43,10 @@ function readNativeContent(format, bytes, options) {
29
43
  function readNativeDocumentTree(format, bytes, options) {
30
44
  const { content, layout } = readNativeContent(format, bytes, options);
31
45
  if (layout !== void 0) {
32
- const pages = layout.pages.map((page) => ({
46
+ const pages = options?.pages === void 0 ? layout.pages.map((page) => ({
33
47
  widthPt: page.widthPt,
34
48
  heightPt: page.heightPt
35
- }));
49
+ })) : void 0;
36
50
  const reported = (0, document_schema_js.assembleTree)(content, pages);
37
51
  require_convert_pdf_package_tables.stampPdfPackageTables(reported, layout);
38
52
  return reported;
@@ -3,6 +3,7 @@ import { CsvWriteOptions, SvgWriteOptions } from "./convert.cjs";
3
3
  import { ContentDocument, DocumentTree, LayoutMetadata } from "document-schema.js";
4
4
  import { PdfDiagnosticSink } from "pdf-codec";
5
5
  import { MarkdownImageResolver } from "markdown-codec";
6
+ import { ReadPdfSurfaces } from "pdf-codec/read";
6
7
  //#region src/convert/from-pdf.d.ts
7
8
  interface ReadDocumentMetadataOptions {
8
9
  readonly signal?: AbortSignal;
@@ -12,6 +13,14 @@ interface ReadNativeDocumentTreeOptions {
12
13
  readonly signal?: AbortSignal;
13
14
  readonly images?: MarkdownImageResolver;
14
15
  readonly sink?: PdfDiagnosticSink;
16
+ /**
17
+ * The pages of a pdf source to read, exactly as UnifiedConversionOptions.pages describes them (zero-based, negative from the end, document order, `PdfPageSelectionError` for an index naming no page). Consulted only for a pdf source, since no other format's reader has pages to select. The unselected pages' content streams, the fonts and images only they use, and the source's embedded files are never read, and the selected pages are reconstructed from themselves alone (see PdfToDocumentOptions.pages for how that can change a cover page's headings). Every frame in the result names the page's index in the source PDF, so a DocumentTree read this way carries no `pages` envelope: that array is indexed by those source indices and a selection cannot fill it.
18
+ */
19
+ readonly pages?: readonly number[];
20
+ /**
21
+ * The document-level surfaces of a pdf source to read, exactly as UnifiedConversionOptions.surfaces describes them: one boolean per surface, each read unless set to `false`, independent of `pages`. Consulted only for a pdf source. A skipped surface is absent from the read, so the tree carries nothing built from it (see UnifiedConversionOptions.surfaces for exactly what: `outline-N` entries in `destinations`, destinations and internal link constructs, form `contentControl` constructs, the layers table, structure entries in `definitions`, residue), and reconstruction loses what it took from it: without `structure` a tagged PDF's headings, table cells and divisions are inferred from geometry, and without `layers` hidden-layer content is kept as if visible.
22
+ */
23
+ readonly surfaces?: ReadPdfSurfaces;
15
24
  }
16
25
  type ReadContentDocumentOptions = ReadNativeDocumentTreeOptions;
17
26
  declare function readNativeDocumentTree(format: DocumentFormat, bytes: Uint8Array<ArrayBuffer>, options?: ReadNativeDocumentTreeOptions): DocumentTree;
@@ -19,6 +28,16 @@ declare function readContentDocument(format: DocumentFormat, bytes: Uint8Array<A
19
28
  interface PdfToDocumentOptions {
20
29
  readonly signal?: AbortSignal;
21
30
  readonly sink?: PdfDiagnosticSink;
31
+ /**
32
+ * The pages to convert, exactly as UnifiedConversionOptions.pages describes them: zero-based page-tree indices, a negative index counted from the end, duplicates collapsed, document order, and `PdfPageSelectionError` for an index naming no page. The unselected pages' content streams, the fonts and images only they use, and the source's embedded files are never read. pdfToMarkdown labels each page it writes with its original one-based page number (`<!-- page 7 -->`), so the text says which pages it holds.
33
+ *
34
+ * Reconstruction sees only the selected pages, so a page can come out differently than it would in a full conversion. Heading detection measures text against the body size, taken as the most common size across the pages read: a cover page read on its own, dominated by large title text, takes the title's size as its body, and the title is then a paragraph rather than a heading. Reading the cover together with a few ordinary pages restores the document's real body size.
35
+ */
36
+ readonly pages?: readonly number[];
37
+ /**
38
+ * The document-level surfaces to read, exactly as UnifiedConversionOptions.surfaces describes them: one boolean per surface (`outline`, `destinations`, `form`, `layers`, `structure`, `residue`), each read unless set to `false`, independent of `pages`. A skipped surface is never parsed, and the conversion carries nothing built from it. Without `structure` a tagged PDF's headings, table cells and divisions are inferred from geometry as for an untagged file, and without `layers` content the default view hides is kept as if visible.
39
+ */
40
+ readonly surfaces?: ReadPdfSurfaces;
22
41
  readonly onDocument?: (pkg: DocumentTree) => void;
23
42
  }
24
43
  declare function pdfToDocx(bytes: Uint8Array<ArrayBuffer>, options?: PdfToDocumentOptions): Uint8Array<ArrayBuffer>;
@@ -3,6 +3,7 @@ import { CsvWriteOptions, SvgWriteOptions } from "./convert.js";
3
3
  import { ContentDocument, DocumentTree, LayoutMetadata } from "document-schema.js";
4
4
  import { PdfDiagnosticSink } from "pdf-codec";
5
5
  import { MarkdownImageResolver } from "markdown-codec";
6
+ import { ReadPdfSurfaces } from "pdf-codec/read";
6
7
  //#region src/convert/from-pdf.d.ts
7
8
  interface ReadDocumentMetadataOptions {
8
9
  readonly signal?: AbortSignal;
@@ -12,6 +13,14 @@ interface ReadNativeDocumentTreeOptions {
12
13
  readonly signal?: AbortSignal;
13
14
  readonly images?: MarkdownImageResolver;
14
15
  readonly sink?: PdfDiagnosticSink;
16
+ /**
17
+ * The pages of a pdf source to read, exactly as UnifiedConversionOptions.pages describes them (zero-based, negative from the end, document order, `PdfPageSelectionError` for an index naming no page). Consulted only for a pdf source, since no other format's reader has pages to select. The unselected pages' content streams, the fonts and images only they use, and the source's embedded files are never read, and the selected pages are reconstructed from themselves alone (see PdfToDocumentOptions.pages for how that can change a cover page's headings). Every frame in the result names the page's index in the source PDF, so a DocumentTree read this way carries no `pages` envelope: that array is indexed by those source indices and a selection cannot fill it.
18
+ */
19
+ readonly pages?: readonly number[];
20
+ /**
21
+ * The document-level surfaces of a pdf source to read, exactly as UnifiedConversionOptions.surfaces describes them: one boolean per surface, each read unless set to `false`, independent of `pages`. Consulted only for a pdf source. A skipped surface is absent from the read, so the tree carries nothing built from it (see UnifiedConversionOptions.surfaces for exactly what: `outline-N` entries in `destinations`, destinations and internal link constructs, form `contentControl` constructs, the layers table, structure entries in `definitions`, residue), and reconstruction loses what it took from it: without `structure` a tagged PDF's headings, table cells and divisions are inferred from geometry, and without `layers` hidden-layer content is kept as if visible.
22
+ */
23
+ readonly surfaces?: ReadPdfSurfaces;
15
24
  }
16
25
  type ReadContentDocumentOptions = ReadNativeDocumentTreeOptions;
17
26
  declare function readNativeDocumentTree(format: DocumentFormat, bytes: Uint8Array<ArrayBuffer>, options?: ReadNativeDocumentTreeOptions): DocumentTree;
@@ -19,6 +28,16 @@ declare function readContentDocument(format: DocumentFormat, bytes: Uint8Array<A
19
28
  interface PdfToDocumentOptions {
20
29
  readonly signal?: AbortSignal;
21
30
  readonly sink?: PdfDiagnosticSink;
31
+ /**
32
+ * The pages to convert, exactly as UnifiedConversionOptions.pages describes them: zero-based page-tree indices, a negative index counted from the end, duplicates collapsed, document order, and `PdfPageSelectionError` for an index naming no page. The unselected pages' content streams, the fonts and images only they use, and the source's embedded files are never read. pdfToMarkdown labels each page it writes with its original one-based page number (`<!-- page 7 -->`), so the text says which pages it holds.
33
+ *
34
+ * Reconstruction sees only the selected pages, so a page can come out differently than it would in a full conversion. Heading detection measures text against the body size, taken as the most common size across the pages read: a cover page read on its own, dominated by large title text, takes the title's size as its body, and the title is then a paragraph rather than a heading. Reading the cover together with a few ordinary pages restores the document's real body size.
35
+ */
36
+ readonly pages?: readonly number[];
37
+ /**
38
+ * The document-level surfaces to read, exactly as UnifiedConversionOptions.surfaces describes them: one boolean per surface (`outline`, `destinations`, `form`, `layers`, `structure`, `residue`), each read unless set to `false`, independent of `pages`. A skipped surface is never parsed, and the conversion carries nothing built from it. Without `structure` a tagged PDF's headings, table cells and divisions are inferred from geometry as for an untagged file, and without `layers` content the default view hides is kept as if visible.
39
+ */
40
+ readonly surfaces?: ReadPdfSurfaces;
22
41
  readonly onDocument?: (pkg: DocumentTree) => void;
23
42
  }
24
43
  declare function pdfToDocx(bytes: Uint8Array<ArrayBuffer>, options?: PdfToDocumentOptions): Uint8Array<ArrayBuffer>;