js.documents 1.81.9 → 1.83.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 +7 -7
- package/dist/{formula-B_4mdCuK.d.ts → formula-DDSYieJg.d.ts} +2 -1
- package/dist/{formula-DFc2Ofsk.d.cts → formula-_8feXQH7.d.cts} +2 -1
- package/dist/layout/engine.cjs +1 -1
- package/dist/layout/engine.js +2 -2
- package/dist/layout/shared.cjs +13 -3
- package/dist/layout/shared.d.cts +3 -3
- package/dist/layout/shared.d.ts +3 -3
- package/dist/layout/shared.js +14 -4
- package/dist/layout/sheets.cjs +38 -5
- package/dist/layout/sheets.js +39 -6
- package/dist/layout/slides.cjs +1 -1
- package/dist/layout/slides.js +2 -2
- package/dist/model/bytes.cjs +12 -9
- package/dist/model/bytes.js +12 -9
- package/dist/ooxml/docx/embedded-objects.cjs +139 -39
- package/dist/ooxml/docx/embedded-objects.d.cts +1 -1
- package/dist/ooxml/docx/embedded-objects.d.ts +1 -1
- package/dist/ooxml/docx/embedded-objects.js +140 -40
- package/dist/ooxml/docx/formula.cjs +15 -0
- package/dist/ooxml/docx/formula.d.cts +2 -2
- package/dist/ooxml/docx/formula.d.ts +2 -2
- package/dist/ooxml/docx/formula.js +15 -1
- package/dist/ooxml/docx/read.d.cts +1 -1
- package/dist/ooxml/docx/read.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -624,9 +624,9 @@ To run a single test file: `pnpm vitest run src/path/to/file.test.ts`.
|
|
|
624
624
|
- **`readPdf` recovers a rect, an ellipse, and a line as their own `LayoutRect`/`LayoutEllipse`/`LayoutLine` kinds, not merely as generic paths — pdf-codec's own shape-pattern detection, and the reason every vector kind now survives a `odgToPdf` → `pdfToOdg` round trip.** PDF has exactly one shape operator (`re`, itself defined as a four-point rectangle subpath) and no ellipse or line operator at all, so a writer has no way to record what a path *was*; pdf-codec recovers it from the geometry instead — an axis-aligned closed four-corner subpath is a `LayoutRect` (any combination of fill and stroke, and a 90°-rotated CTM as well as an unrotated one), a closed subpath of four cubic segments meeting its bounding box at the four cardinal points with kappa-ratio control points is a `LayoutEllipse`, and an open single-straight-segment stroke-only subpath is a `LayoutLine`. See pdf-codec's own README for the tolerances and the honest caveat that these are bounded heuristics: a false positive can change an item's *kind*, never its geometry. What still narrows to a generic `LayoutPath`: an off-axis rotation, a freeform curve, a multi-subpath figure. A practical consequence for `pdfToOds`: a gridline written by `sheets.ts`'s own `renderGridlines` now comes back as a real `LayoutLine`, but `reconstructSpreadsheet`'s lattice detection still accepts the stroked-single-segment `LayoutPath` shape too, so a hand-built `LayoutDocument` and one from a producer other than `readPdf` detect identically.
|
|
625
625
|
- **`pdfToOds` re-types a recovered cell heuristically, and this is explicitly PROBABILISTIC BEST-EFFORT RECOVERY, not a fidelity guarantee.** A rendered PDF genuinely never carries a spreadsheet cell's own typed value — a page holds only the string the authoring application chose to print — so every re-typed value below is an inference from that string alone, and a string that looks exactly like a number may genuinely have *been* a string in the source spreadsheet (a part number, a version, a phone extension). Nothing in this package can tell those apart with certainty, and no further heuristic would change that. What is guaranteed: `ContentSheetCell.displayText` is a required field carrying the rendered string verbatim regardless of what was inferred from it, so the printed form is never lost. `src/layout/cell-typing.ts` re-types only where the string has exactly **one** defensible reading, which resolves to four concrete requirements: the decimal must be exactly representable as a JS number (checked by round-tripping it, not by a digit-count limit — this is what keeps a 19-digit barcode a string); the separators must be unambiguous (`.` reads as the decimal separator and `,` as grouping, but a lone comma group like `"1,234"` is **declined**, since the competing European reading of the identical string is 1.234, a thousandfold error — `"1,234,567"` and `"1,234.50"` have no such competing reading and are accepted); a leading zero (`"007"`, `"01.5"`) is declined outright, since a spreadsheet never prints a numeric value with one; and a date's component roles must be stated by the text itself — ISO ordering (`"2024-01-15"`) or a named month (`"15 Jan 2024"`, `"Jan 15, 2024"`) is accepted, an all-numeric separated date (`"01/02/2024"`) is declined regardless of whether one component happens to exceed 12 in that particular cell, because resolving it per cell would type one column inconsistently. `TRUE`/`FALSE` are re-typed as booleans; `Yes`/`No`/`Y`/`N`/`On`/`Off` are **declined**, since no mainstream spreadsheet prints a boolean that way by default, so a `"Yes"` cell is far more likely genuine text. Percentages recover ODF's own fraction convention (`"15%"` → `0.15`); a currency symbol names an ISO code only where it identifies exactly one (`£`→GBP, `€`→EUR; `$` and `¥` re-type as `currency` with the code left `undefined`). `'time'`/`'dateTime'`/`'error'` are deliberately out of scope, and a formula is never claimed. Two ways to tell an inferred value from an untouched one: `value.kind !== 'string'` is itself the flag, and `ReconstructOptions.onCellTypeInference` reports every decision — both a re-typing (with the rule that fired) and a deliberate refusal (with the named ambiguity), the latter being information the output alone cannot carry, since a declined cell is indistinguishable from one that was never number-shaped at all. `inferCellValue` is exported standalone for a caller who wants to replay the same decision over their own text.
|
|
626
626
|
- **`reconstructWordprocessing`/`reconstructPresentation` recover a page's vector primitives too, in a nested drawing document, and all four OOXML/ODF builders now write them out as real shapes.** Both directions used to filter each page down to its text and image items and discard every stroke and fill; they now run the same `layoutItemToVector` classification `reconstructDrawing` does (one implementation, not two) and carry the result in a `ContentEmbeddedObjectBlock` whose `objectKind` is `'drawing'` and whose nested `document` is a real one-page drawing `ContentDocument` — the shared schema's own designed mechanism, since `ContentSection.blocks` and `ContentSlide.shapes` have no vector vocabulary of their own. One honest consequence of the recovery itself: a PDF does not distinguish a stroke drawn to decorate from one drawn as structure, so a rule under a heading, an underline (pdf-codec writes one as a filled rectangle), and a table cell's background fill are all recovered as vectors — that is intended, since discarding real content because it *might* be incidental is exactly the silent loss this package's conventions rule out, but it does mean a reconstructed document carries more than its text alone. A table's own gridlines are the one case deliberately not double-counted: when the table recovery above claims a lattice, the strokes that formed it are excluded from vector recovery. On the write side, `buildDocxPackage` and `buildPptxPackage` emit real DrawingML (`a:prstGeom prst="rect"/"ellipse"/"line"`, and a genuine `a:custGeom` with `a:moveTo`/`a:lnTo`/`a:cubicBezTo`/`a:close` for a path), and `buildOdtPackage`/`buildOdpPackage` emit real `draw:rect`/`draw:ellipse`/`draw:line`/`draw:path` — see the dedicated write-side gotcha below for how each format wraps them. `pdfToMarkdown` still drops the block entirely rather than emitting a marker for it: a rect carries no text to stand in for, unlike a formula, and CommonMark has no vector construct regardless.
|
|
627
|
-
- **A recovered vector
|
|
627
|
+
- **A recovered vector now round-trips back through all four of this package's own readers, not only reaches the output file.** `buildDocxPackage`/`buildPptxPackage`/`buildOdtPackage`/`buildOdpPackage` all write real vector shapes, and re-reading that same file through `readDocxContent`/`readPptxContent`/`readOdtContent`/`readOdpContent` now gives the embedded-drawing block back: the ODF pair (`src/odf/odt/read.ts`/`odp/read.ts`) run `collectContainerVectors` — a second pass over the same `content.xml`, mirroring the formula-detection pass — straight through to `odf.js`'s own `readDrawPageContent`; the OOXML pair (`src/ooxml/docx/read.ts` via `spliceDocxEmbeddedObjects`, `src/ooxml/pptx/read.ts` via `collapseVectorShapeRuns`) do the same over `word/document.xml` and each slide's own `p:sld`. A recovered vector is a real `ContentEmbeddedObjectBlock` whose `objectKind` is `'drawing'`, so the six PDF-bypassing bridges (`odtToDocx` and friends) now carry vector geometry across too, since a bridge's own `readXContent` half no longer drops it.
|
|
628
628
|
- **Each format wraps a vector shape in the construct that format actually has for one, and the two families each share one writer.** `src/edit/drawingml/vector.ts` holds everything inside a DrawingML shape-properties element (`a:xfrm`, the preset or custom geometry, `a:solidFill`/`a:noFill`, `a:ln`) — identical for docx and pptx, since `CT_ShapeProperties` is one type in both. Only the wrapper differs: pptx gets a plain `p:sp` on the slide's `p:spTree` (no `p:txBody` at all — a geometric primitive carries no text, and inventing an empty paragraph would make `readPptx` report a text shape where the source had pure geometry), while docx gets a `w:drawing`/`wp:anchor` positioned `relativeFrom="page"` on both axes, with `behindDoc="1"` and `wp:wrapNone`, carrying a `wps:wsp` inside `a:graphicData` — the wordprocessingShape extension part, the only DrawingML vocabulary WordprocessingML has for a non-picture shape (the pre-2010 alternative is VML, deprecated by ECMA-376 itself and deliberately not written). Anchoring rather than `wp:inline` is what preserves the recovered page-absolute coordinates; the honest limit is that the anchor still belongs to a paragraph, so which page the geometry lands on follows that paragraph if the document reflows differently in Word than it laid out in the source PDF. On the ODF side, `buildOdtPackage`/`buildOdpPackage` import `src/edit/odg/vector.ts`'s writer wholesale rather than reimplementing it — `draw:rect`/`draw:ellipse`/`draw:line`/`draw:path` carry byte-for-byte the same attribute vocabulary in a text document, a presentation, and a drawing, and `odf.js`'s own `readDrawPageContent` reads all three through one function. odp appends them directly to the slide's `draw:page` (a slide positions geometry against the page, exactly as a drawing page does); odt anchors them in a `text:p` of their own with `text:anchor-type="paragraph"` **plus** `style:horizontal-rel`/`style:vertical-rel="page"` in the graphic style, since a recovered vector's coordinates are page-absolute and a paragraph-relative reference would offset every shape by wherever its anchor paragraph flowed to. `style:wrap="run-through"`/`style:run-through="background"` are the ODF counterpart of docx's `wp:wrapNone`/`behindDoc="1"` pair.
|
|
629
|
-
- **`ContentStroke.style` (`solid`/`dashed`/`dotted`/`double`) is not written by any vector writer, ODF or DrawingML.**
|
|
629
|
+
- **`ContentStroke.style` (`solid`/`dashed`/`dotted`/`double`) is not written by any vector writer, ODF or DrawingML.** `LayoutLine`/`LayoutPath` DO carry an optional `style` enum now (`document-schema.js` 2.1.0), and a reconstruction path could populate it — but neither vector writer reads the field: `src/edit/drawingml/vector.ts`'s `outline()` writes `a:ln` with width + colour only (no `a:prstDash`), and `src/edit/odg/style.ts` writes `draw:stroke` + `svg:stroke-color`/`svg:stroke-width` only (no `draw:stroke-dash`). A hand-built `ContentVector` setting `stroke.style` consequently paints solid; a real, bounded gap rather than an oversight. Cell borders are a separate path — `pushCellBorderLines` does set the style and pdf-codec renders it (see the cell-border gotcha below) — and `a:prstDash` has no `double` member to map the fourth value onto regardless.
|
|
630
630
|
- **`pdfToOds` recovers what was printed, not what was entered.** `reconstructSpreadsheet` (`src/layout/reconstruct.ts`) tries a real gridline lattice first: it scans the page's `LayoutLine`/stroked-single-segment-`LayoutPath` items (see the `interpret.ts` gotcha above) for enough parallel horizontal and vertical lines at consistent positions to call it a printed grid (`MIN_GRIDLINE_COUNT_PER_AXIS = 3` per axis, i.e. at least a 2×2 grid, and a span-consistency check that rejects a scatter of unrelated short strokes — a page border or a couple of decorative rules — as not a genuine lattice), and uses those line positions DIRECTLY as cell boundaries when found. Absent a lattice, it clusters text into a grid from geometry alone instead: rows reuse `clusterIntoLines` verbatim (a spreadsheet cell's own text is never wrapped across lines, so a text line already IS a row), and columns generalise `clusterIntoParagraphs`'s own single `dominantLeftX` to several recurring x-position anchors, first merging directly-adjacent same-line fragments (`splitLineByLargeGaps`, the same >2em-gap signal `reconstructPresentation`'s own block clustering uses) so a cell whose text arrived as several run-level-split `LayoutText` items isn't scattered across spurious columns. Column widths and row heights are genuinely measured from whichever geometry was used (drawn gridline gaps, or measured text/anchor extents), never invented. Every recovered cell always carries its own extracted `displayText` verbatim, and additionally carries a **heuristically re-typed** `value` wherever `src/layout/cell-typing.ts` finds exactly one defensible reading of that string; a formula is still never claimed. See the dedicated heuristic-re-typing gotcha below and [Fidelity](#fidelity) for the full framing. `buildOdsPackage` (`src/edit/ods/content.ts`) is `pdfToOds`'s own package-building half, mirroring `buildOdtPackage`/`buildOdpPackage`/`buildOdgPackage`'s role for `pdfToOdt`/`pdfToOdp`/`pdfToOdg`.
|
|
631
631
|
- **`OdsSheet.printSettings` (`src/edit/ods/print-settings.ts`) now round-trips every field `ContentSheetPrintSettingsSchema` carries, not just the five it started with.** `pageSize`/`margins`/`gridlines`/`headers`/`pageOrder` resolve through the `table:style-name` → `style:style[family="table"]` → `style:master-page-name` → `style:master-page` → `style:page-layout` → `style:page-layout-properties` chain (`odf.js`'s own exported `findStyleElement`/`resolvePageLayoutProperties`/`parsePageSize`/`parseMargins`); the setter mints a fresh `style:page-layout` + `style:master-page` + `style:style[family="table"]` triple and repoints the sheet's own `table:style-name` to it on every call, the same append-only style-editing convention `src/edit/odg/style.ts` already documents. The remaining, previously-unimplemented fields are now implemented too: `printRange` reads/writes `table:print-ranges` directly on `table:table`; `scalePercent`/`fitToPages` read/write `style:scale-to` and `style:scale-to-X`/`style:scale-to-Y` on the page-layout-properties element; `repeatColumns`/`repeatRows` are read via `scanTableStructure`, a scoped-down mirror of `odf.js`'s own private `readTable`'s table-wide column/row cursor tracking (the same walk that function performs before ever calling its own `readPrintSettings`), and written by moving the real `table:table-column`/`table:table-row` elements covering the given range into a fresh `table:table-header-columns`/`table:table-header-rows` wrapper; `manualBreaks` read/write `fo:break-before="page"` on the named row/column's own style. Writing `repeatRows`/`repeatColumns` required teaching `address.ts`'s row/column addressing that a row/column may now live nested one level inside a header wrapper rather than as a direct `table:table` child, so a subsequent cell/column/row write against a wrapped index finds the real element instead of creating a spurious duplicate outside it; the width/height and manual-break writers all target the same `style:table-column-properties`/`style:table-row-properties` element, so each reads the column/row's current style first and mints a fresh style carrying the merged result, rather than a naive single-property mint clobbering whatever an earlier call had already set. No known gap remains in `ContentSheetPrintSettingsSchema` coverage.
|
|
632
632
|
- **`OdsSheet` now has a real column-width/row-height setter (`setColumnWidth`/`setRowHeight`, `src/edit/ods/column-row.ts`), closing a gap that escalated from cosmetic to a genuine correctness bug once `xlsxToPdf`/`pdfToXlsx` started composing through `buildOdsPackage` internally.** `OdsSheet.cell()`'s own column/row-materialisation (`address.ts`) creates a real, explicit `table:table-column`/`table:table-row` element for any position a caller ever addresses, but previously never gave it a width/height style. This is a genuinely different failure shape from a column/row with NO element at all: `sheets.ts`'s own `resolveAxis` only falls back to `DEFAULT_COLUMN_WIDTH_PT`/`DEFAULT_ROW_HEIGHT_PT` for an index with no `ContentSheetColumn`/`ContentSheetRow` entry whatsoever — an explicit-but-unstyled element reads back at `widthPt`/`heightPt` 0 (`odf.js`'s own `resolveColumnWidthPt`/`readRowLayout`), and that explicit zero wins over the fallback. While `buildOdsPackage`'s own output was only ever a terminal deliverable (`pdfToOds`, or a caller's own `readOdsContent` round trip), this was cosmetic: a real app reopening it would use its own defaults instead of the source's. `xlsxToPdf` (`xlsxToOds` then `odsToPdf`) made it a real bug instead — the intermediate ods bytes get laid out again by `convertSpreadsheetToLayout`, and a zero-size grid collapses every cell onto the same physical position rather than merely losing precision. `setColumnWidth`/`setRowHeight` mint a fresh `style:style[family="table-column"|"table-row"]` per column/row and repoint its own `table:style-name`, the same append-only style-minting convention `writeSheetPrintSettings`/`src/edit/odg/style.ts` already establish; `buildOdsPackage` now calls both for every `ContentSheetColumn`/`ContentSheetRow` a source sheet carries. Column/row HIDDEN state and `ContentSheetImage`/formula `embeddedObjects` are no longer gaps either, closed in the same phase: `OdsSheet.setColumnHidden`/`setRowHidden` set or clear `table:visibility="collapse"` directly on the `table:table-column`/`table:table-row` element — a plain attribute, not a style property, so it never interacts with the width/height setters above — and `buildOdsPackage` calls one of these for every column/row whose `hidden` field is `true`. **The "explicit but unstyled" hazard itself is now closed at its source, not just worked around by callers who remember to call `setColumnWidth`/`setRowHeight` explicitly.** `column-row.ts`'s `ensureColumnDefaultWidth`/`ensureRowDefaultHeight` stamp `DEFAULT_COLUMN_WIDTH_PT`/`DEFAULT_ROW_HEIGHT_PT` (matching `sheets.ts`'s own fallback values exactly) onto a column/row the moment any of `OdsSheet.cell()`, `mergeCells()`, `setColumnHidden()`, or `setRowHidden()` individuates it for the first time — but only when it has no width/height style at all yet, never overwriting a width a caller (or a real source document opened via `openOds`) already set. A plain `createOds()` sheet touched only through `.cell()` now reads back with a real, positive `widthPt`/`heightPt` on every column/row it addressed, never the ambiguous 0 that used to require an explicit `setColumnWidth`/`setRowHeight` call to avoid. One narrower instance of the identical hazard remains open, deliberately not folded into this fix: `print-settings.ts`'s `wrapRepeatRange` (the `repeatColumns`/`repeatRows` writer) still individuates its own boundary columns/rows via a bare, unstyled `replaceRun`, and — unlike `cell()`/`mergeCells()` — stamping every position that boundary individuation touches would mean materializing an unbounded range rather than the two boundary positions it is deliberately designed to touch, a genuinely different performance tradeoff this fix does not attempt to resolve. `OdsSheet.addImage` (`src/edit/ods/floating.ts`) writes a real floating `draw:frame`/`draw:image` into `table:shapes` (the ODF 1.3 content-model container for spreadsheet floating shapes, always `table:table`'s own first child in a package this editor builds), resolving a `ContentSheetImage`'s `anchorRow`/`anchorColumn` plus `offsetXPt`/`offsetYPt` to an absolute `svg:x`/`svg:y` by summing the real, currently-declared width/height of every column/row strictly before the anchor (header-wrapper-aware, hidden columns/rows contributing zero, falling back to the same default column/row size the layout engine assumes once the walk runs past what the sheet has declared) — reusing `addImageMedia` for the binary part and manifest entry, the same mechanism `src/edit/odp/image.ts` already uses for a slide. `OdsSheet.addEmbeddedObject` writes a real embedded ODF formula sub-document for `objectKind === 'formula'` (reusing `addFormulaObject`, the same mechanism `OdtBody.appendFormula` already uses); every other `objectKind` (`wordprocessing`/`presentation`/`spreadsheet`/`drawing`) is left unwritten, a documented, bounded gap mirroring `buildOdtPackage`'s identical narrowing for a `'drawing'` embedded object, since embedding one would mean writing that document's own package as a nested OLE sub-object, which no writer in this codebase implements. `buildOdsPackage` calls both for every sheet's images/embedded objects, after every column/row width/height/hidden call, so an image's own anchor resolves against the sheet's final, real column/row sizing. This is no longer write-only: `odf.js` 2.2.0's own `readOds` reads a sheet's floating shapes and embedded objects back (it previously hardcoded `images: []` and never set `embeddedObjects`), so a written image now verifies as a genuine `ContentDocument` re-read round trip — bytes, declared size, and anchor quartet — on top of the direct written-XML structural checks these tests already made.
|
|
@@ -639,7 +639,7 @@ To run a single test file: `pnpm vitest run src/path/to/file.test.ts`.
|
|
|
639
639
|
- **A drawing page's `shapes` and `vectors` are two separate arrays, but their true relative paint order is carried by a shared `paintOrder` field on both.** `ContentDrawPageSchema` (`document-schema.js`) still keeps text/image/table content (`shapes`) and vector primitives (`vectors`) apart, but `ContentVector` and `ContentShape` each carry a `paintOrder` — one monotonically increasing per-page document index `odf.js`'s own reader stamps on every element it walks (`typed/draw/shapes.ts`'s `walkDrawPageContent`/`paintOrderKey`, honouring a real `draw:z-index` where a producer wrote one, falling back to document position otherwise). `convertDrawingToLayout` merges the two arrays back into one true-paint-order walk through that field (`src/model/paint-order.ts`'s `mergeByPaintOrder`), `reconstructDrawing` stamps the same field from its own single walk over a page's recovered items, and `buildOdgPackage` appends in the same merged order (document order *is* paint order in a written `.odg` — this package never emits a `draw:z-index`). A page that genuinely interleaves the two mid-stack — a text label between two rectangles, a rectangle over a picture — consequently paints in the order its author built it, and survives `convertDrawingToLayout` → `reconstructDrawing` with that interleaving intact. The historical "every vector paints before every shape" rule survives only as the documented fallback for a page missing `paintOrder` anywhere (a hand-built `ContentDocument`, or one produced before the field existed), since an item with no value has no defensible position to be sorted into and inventing one would silently reorder content.
|
|
640
640
|
- **A rotated vector primitive renders as a `LayoutPath`, not as a rotated `LayoutRect`/`LayoutEllipse` — because neither of those carries a rotation field at all.** `ContentVectorSchema`'s `rect`/`ellipse`/`path` variants each carry a real `rotationDeg` (the `line` variant does not, and needs none — two endpoints already encode any orientation a line can have), `odf.js`'s own reader resolves one through the same `resolveOdfShapeGeometry` a `draw:frame` uses, and `OdgBoxVector.rotationDeg`/`OdgPathVector.rotationDeg` write one back through the same shared `applyOdfGeometry` (`src/edit/geometry.ts`) `OdpShape.rotationDeg` uses. `convertDrawingToLayout` resolves a rotated vector into a `LayoutPath` whose own points are the shape's corners/curve controls after rotation — a rotated rect becomes a genuine four-point closed subpath, a rotated ellipse its own four cubics rotated — since `LayoutRectSchema`/`LayoutEllipseSchema` model no rotation and only `LayoutText`/`LayoutImage` do (pdf-codec rotates those two through a text/image transformation matrix, which a path-painting operator sequence has no equivalent of). Nothing is approximated by this: an affine rotation maps a straight edge to a straight edge and a cubic Bézier to a cubic Bézier exactly. What a PDF round trip cannot preserve is the `rotationDeg` *field* — a recovered path records where the corners ended up, never that a right-angled box was turned to get there — so `pdfToOdg` returns a rotated rect as an unrotated `path` vector whose geometry is genuinely rotated, the same kind-narrowing every other vector already documents below.
|
|
641
641
|
- **`ContentVector`'s `path` variant's `fillRule` is read from real ODF markup.** `odf.js`'s `readOdfFillAndStroke` (`src/typed/draw/shapes.ts`) resolves the real `svg:fill-rule` attribute (`nonzero`/`evenodd`) when a path declares one, tested against real fixtures including a two-subpath "letter O" donut shape proving the attribute survives the full `readDrawPageContent` path — `fillRule` is `undefined` only when the source markup genuinely has no `svg:fill-rule` attribute at all, in which case `writePath` correctly falls back to PDF's default nonzero winding rule.
|
|
642
|
-
- **A cell's declared border
|
|
642
|
+
- **A cell's declared border now renders with its real `style` (`solid`/`dashed`/`dotted`/`double`) in PDF output.** `ContentSheetCellSchema` and `ContentTableCellSchema` both carry real per-cell `background`/`borders` (and `ContentSheetCellSchema` also `alignment`/`verticalAlignment`), `odf.js`'s own reader populates every one of them from a cell's resolved style chain, and `sheets.ts`/`engine.ts` render all of them: a background becomes a real `LayoutRect`, each declared border edge a real `LayoutLine`, and a cell's own alignment/vertical alignment override the value-kind default and the bottom default respectively. `ContentBorder.style` (`solid`/`dashed`/`dotted`/`double`) now reaches the PDF byte output for real: `LayoutLineSchema`/`LayoutPathSchema` both carry that optional `style` enum (added in `document-schema.js` 2.1.0), `pushCellBorderLines` (`src/layout/shared.ts`, shared by both `engine.ts`'s docx/odt table-cell borders and `sheets.ts`'s ods/xlsx sheet-cell borders) sets it from `ContentBorder.style` on every emitted `LayoutLine`, and pdf-codec (this package pins `^1.11.8`; the dash rendering landed in 1.10.0) draws it — a genuine PDF `d` dash-array operator for `'dashed'`, round-cap dots for `'dotted'`, and `'double'` as two hand-offset parallel strokes. The one honest caveat is `'double'`'s own inter-line offset: that distance is nowhere in the data model, so pdf-codec picks its own internal constant for it rather than reading a value the source never carried — every other style value is faithful.
|
|
643
643
|
- **Ordinary text in PDF output now resolves through a real font registry rather than the standard 14 alone, and the standard 14 are only the last resort in that chain.** In order: the source document's own embedded faces (docx's `word/fontTable.xml`, pptx's `p:embeddedFontLst`, ODF's `office:font-face-decls` — see [Fonts](#fonts)), then any face the caller supplied through `options.fonts`, then pdf-codec's vendored Carlito/Caladea faces (genuinely metric-compatible with Calibri/Cambria, and embedded as real subsetted TrueType programs), then the standard 14. Helvetica/Times-Roman remain metric-compatible substitutes for Arial/Times New Roman, so a document asking for either still resolves to a standard font and embeds nothing. What is still not covered: a family with no embedded face, no caller-supplied face, and no vendored substitute — Aptos, say, or any third-party typeface — still renders through the nearest standard-14 face with a width-correction factor, so line wrapping and pagination will drift slightly from what Word itself would produce. Expect a faithful visual approximation there, not a line-identical reproduction. MathML formula rendering (`odfToPdf`, and formulas embedded inside odt/odp) is separate from all of this and always was: it embeds the real STIX Two Math font, which is not a registry-resolvable face and cannot be overridden by `options.fonts` — see the CFF-embedding gotcha below, and pdf-codec's own README, for the exact scope of that embedding (the whole `CFF ` table, not glyph-subsetted).
|
|
644
644
|
- **Justified paragraphs now stretch real inter-word gaps in all three layout engines — the flow one (`engine.ts`), the direct-placement one (`slides.ts`), and the spreadsheet one (`sheets.ts`).** `justifyLineGapsPt` (`src/layout/shared.ts`) recovers each wrapped line's own word-gap positions from a line's per-fragment `xOffsetPt` (a genuine gap wider than floating-point noise means a space stood there; two touching fragments are one word split across a run boundary, and stay touching), divides the line's slack evenly across every detected gap, and returns an all-zero shift whenever there is nothing to stretch (fewer than two fragments, no detected gap, or a line already at or past its target width — this function only ever adds space, never compresses). `layoutParagraphFlow`/`layoutParagraphInCell` (`engine.ts`, covering docx/odt paragraphs, docx/odt tables, and odm-assembled chapters) and `layoutParagraph` (`slides.ts`, covering pptx/odp shape text and slide-table cells) call it for every wrapped, non-final line of a `'justify'`-aligned paragraph; the paragraph's own final line (or a paragraph that never wraps at all) stays left-aligned, matching Word/LibreOffice/Impress's own convention. `sheets.ts`'s `renderCellText` calls it too, but a spreadsheet cell only ever renders one line by this module's own documented scope, so the "non-final line" case only arises when a cell's source text carries an explicit line break — `wrapRunsToWidth` then produces more than one line, of which only the first is ever rendered, and that first line is the genuinely non-final one a justified cell stretches; justification is skipped outright when that line already overflowed its cell (the numeric-`###`/string-spill-or-truncate fragments no longer reflect the natural layout the stretch needs) or for an ordinary single-line cell, matching every real spreadsheet application's own "justify only wraps, never a single line" behaviour. `alignmentOffsetPt` itself still returns `0` for `'justify'` in all three files, unchanged — the whole-line offset it computes is the wrong shape for inter-word stretching, which is why the stretch lives in a second, sibling function each caller applies on top, not a new branch inside it. No known gap remains in inter-word justification across any layout engine this package has.
|
|
645
645
|
- **Encrypted-PDF support and `CCITTFaxDecode`/`JBIG2Decode`/`JPXDecode` image decoding are all real, implemented capabilities in pdf-codec now, not scope boundaries.** An encrypted PDF is readable whenever it opens without a real password: pdf-codec's `src/encrypt.ts` implements the full Standard Security Handler (RC4 and AES-128/256, revisions 2-6, `/EncryptMetadata false` handling, empty-user-password verification), throwing the distinct `PdfPasswordRequiredError` only when a genuine user password is needed and `PdfEncryptedError` only for a handler/version this codec doesn't implement (public-key encryption, say). CCITT Group 3/4 fax, JBIG2, and JPEG2000 images all genuinely decode via hand-written decoders (`src/image/{ccitt,jbig2,jpeg2000}.ts`), falling back to a diagnostic only for a specific feature within one of those formats the decoder doesn't cover — not unconditionally, as an earlier version of this note claimed. What remains a genuine, permanent scope boundary is adversarial/badly-malformed-input robustness: the parser targets cleanly-generated output from mainstream producers rather than the hardening a 15+-year-old library has. See pdf-codec's own README for the full statement of each.
|
|
@@ -681,13 +681,13 @@ To run a single test file: `pnpm vitest run src/path/to/file.test.ts`.
|
|
|
681
681
|
- **The MathML operator dictionary (`src/mathml/operators.ts`) is a deliberately bounded ~60-entry table, not the MathML3 specification's own multi-thousand-entry, form-dependent (prefix/infix/postfix) one.** It covers arithmetic, relational, set/logic, calculus big-operators, fences, and punctuation — the operators real formulas overwhelmingly use — with one entry per character regardless of which position it appears in, falling back to a single sane infix-shaped default (thick-space spacing, no stretch/largeop/movablelimits) for anything else.
|
|
682
682
|
- **`mover`/`munder`/`munderover` centre an over/under-script at the base glyph's own font-declared accent-attachment point (`MathTopAccentAttachment`) when one is available, falling back to geometric centring otherwise.** `src/mathml/layout.ts`'s `resolveTopAccentXPt`/`AccentAttachment`/`layoutUnderOver` resolve the embedded font's real `MathTopAccentAttachment` metric for a base that is a single-codepoint token under a genuine `accent="true"`/`accentunder="true"` mark, tested in `src/mathml/layout.test.ts` against both a real attachment-point case and the geometric-fallback case (a multi-character base, where there is no single glyph's attachment point to resolve). Geometric centring survives as the correct fallback for exactly that multi-character/non-token case, not as the general rule.
|
|
683
683
|
- **Greek `mathvariant` mapping covers the plain alphabet, nabla (∇), partial differential (∂), and the six OpenType/Unicode Greek "symbol variant" glyphs** (lunate epsilon/theta/kappa/phi/rho/pi symbols — U+03F5/U+03D1/U+03F0/U+03D5/U+03F1/U+03D6 — styled to bold, italic, bold-italic, bold-sans-serif, and sans-serif-bold-italic; Unicode never assigned symbol-variant glyphs for plain sans-serif, script, fraktur, or double-struck). Every entry is generated directly from Unicode's own `UnicodeData.txt` (see `src/mathml/variant.ts`'s own generation note) rather than transcribed by hand.
|
|
684
|
-
- **A formula anchored to a spreadsheet cell renders for real now (`src/layout/sheets.ts`'s `renderAnchoredFormulas`), and closing it needed both sibling packages to move first — it was never something this module could wire around on its own.** `odf.js` had to learn to emit a cell-anchored formula sub-object at all (2.1.0 gave `readOds` a real `TableCursor` walk and true row/column anchoring, but its embedded-object classifier still recognised only wordprocessing/presentation/spreadsheet/drawing sub-documents, so a formula was skipped outright; 2.2.0 classifies one), and `document-schema.js` had to give `ContentEmbeddedObject` somewhere to record which cell it belongs to (2.2.0's optional `anchorRow`/`anchorColumn`/`offsetXPt`/`offsetYPt` quartet, mirroring what `ContentSheetImage` already carried). That quartet is exactly what makes placement possible: a cell-anchored `draw:frame`'s own `svg:x`/`svg:y` is relative to **that cell's** top-left corner, not the sheet's origin, so without an anchor there is no coordinate space its frame can be interpreted in at all. `sheets.ts` resolves the anchor against its own already-positioned column/row axes — so band membership, the repeat band, the header gutter, and fit-to-page scaling are all accounted for by construction — and applies the cell-relative offset **unscaled**, matching this module's own existing treatment of every other cell-local inset (cell text padding, header-label padding): fit-to-page scales the grid's geometry, never a cell's internal padding or its text's point size. Three consequences worth naming. (1) The print range **widens** to cover a formula's anchor cell when the sheet declares no explicit `table:print-ranges` — a cell-anchored drawing genuinely extends a sheet's used area in Calc/Excel, and without this a formula anchored past the last populated cell would fall outside every band and silently never render; the union is over anchor *cells* only, never each formula's own rendered box, so an oversized formula overflows over whatever follows exactly as it does in Calc rather than reserving empty rows nothing occupies. An *explicit* print range is still honoured verbatim, so a formula anchored outside one is correctly not printed. (2) A formula anchored inside a repeat row/column band renders on **every** page that band appears on, which is what a repeat band means — no special case, since the band is simply present in every page's own axis. (3) A formula anchored to a hidden row or column is skipped outright, exactly as that cell's own content is. `ContentSheet.images`
|
|
684
|
+
- **A formula anchored to a spreadsheet cell renders for real now (`src/layout/sheets.ts`'s `renderAnchoredFormulas`), and closing it needed both sibling packages to move first — it was never something this module could wire around on its own.** `odf.js` had to learn to emit a cell-anchored formula sub-object at all (2.1.0 gave `readOds` a real `TableCursor` walk and true row/column anchoring, but its embedded-object classifier still recognised only wordprocessing/presentation/spreadsheet/drawing sub-documents, so a formula was skipped outright; 2.2.0 classifies one), and `document-schema.js` had to give `ContentEmbeddedObject` somewhere to record which cell it belongs to (2.2.0's optional `anchorRow`/`anchorColumn`/`offsetXPt`/`offsetYPt` quartet, mirroring what `ContentSheetImage` already carried). That quartet is exactly what makes placement possible: a cell-anchored `draw:frame`'s own `svg:x`/`svg:y` is relative to **that cell's** top-left corner, not the sheet's origin, so without an anchor there is no coordinate space its frame can be interpreted in at all. `sheets.ts` resolves the anchor against its own already-positioned column/row axes — so band membership, the repeat band, the header gutter, and fit-to-page scaling are all accounted for by construction — and applies the cell-relative offset **unscaled**, matching this module's own existing treatment of every other cell-local inset (cell text padding, header-label padding): fit-to-page scales the grid's geometry, never a cell's internal padding or its text's point size. Three consequences worth naming. (1) The print range **widens** to cover a formula's anchor cell when the sheet declares no explicit `table:print-ranges` — a cell-anchored drawing genuinely extends a sheet's used area in Calc/Excel, and without this a formula anchored past the last populated cell would fall outside every band and silently never render; the union is over anchor *cells* only, never each formula's own rendered box, so an oversized formula overflows over whatever follows exactly as it does in Calc rather than reserving empty rows nothing occupies. An *explicit* print range is still honoured verbatim, so a formula anchored outside one is correctly not printed. (2) A formula anchored inside a repeat row/column band renders on **every** page that band appears on, which is what a repeat band means — no special case, since the band is simply present in every page's own axis. (3) A formula anchored to a hidden row or column is skipped outright, exactly as that cell's own content is. `ContentSheet.images` now renders through the identical mechanism (`renderAnchoredImages`): a floating image carries the same anchor quartet a formula does, resolves against the same positioned axes, widens the print range to cover its anchor cell the same way, and is skipped for a hidden anchor row/column or an anchor outside an explicit print range on exactly the same terms — the only difference is that an image is a real `LayoutItem` (`LayoutImage`) pushed into the page's own items, with its bytes registered in the document-wide image registry `registerImage` deduplicates into, rather than a separate out-array the way a formula's CID-font glyph runs are.
|
|
685
685
|
- **`convertSpreadsheetToLayout` returns `{ document, formulas }`, not a bare `LayoutDocument`** — the same `SpreadsheetLayoutResult` shape `convertWordprocessingToLayout`/`convertPresentationToLayout` have always returned, for the same reason: a formula's CID-font glyph runs cannot travel through `LayoutDocument.pages[].items` at all (see the Gotchas entry on why), so they come back alongside the document and are handed to `writePdf({ formulas })`. `odsToPdf` threads them through exactly as `odtToPdf`/`odpToPdf` already did. A caller of the exported `convertSpreadsheetToLayout` reads `.document` where it previously used the return value directly. `convertDrawingToLayout` still returns a bare `LayoutDocument`, since `readOdgContent` runs no formula detection and a drawing page consequently never carries a formula block.
|
|
686
|
-
- **The formula-size
|
|
686
|
+
- **The formula-size fit (`formulaSizePtForFrame`, `src/layout/shared.ts`) is now one shared two-pass function rather than three copies of a height/2 heuristic**, consumed identically by `engine.ts` (flow placement), `slides.ts` (shape placement), and `sheets.ts` (cell-anchored placement): it lays the formula out once at a reference size to measure its natural width and height, then rescales by the frame's own declared width and height so the laid-out box fits both (whichever is the binding constraint), floored at 8pt. `layoutFormula`'s output scales linearly in `sizePt`, so a single rescale reaches the fit with no iteration — the height/2 heuristic this replaced overflowed a genuinely stacked formula (a fraction inside a radical is taller than twice its base font size), rendering it larger than the frame the source document drew it at. A docx OMML equation carries no geometry of its own (`equationFrame` synthesises `widthPt: 0`), so for docx the width contributes no constraint and height alone drives the size — the old heuristic's intent — while ODF-sourced formulas (which carry a real `draw:frame` width+height) get the full two-dimensional fit.
|
|
687
687
|
|
|
688
688
|
- **Embedded-formula detection inside odt/odp is genuinely new work with no `odf.js`-side equivalent (`readDrawFrameContent` doesn't recognise a `draw:object`-bearing `draw:frame` at all yet — see the `src/odf/` architecture entry above), and each format's own placement is now derived from the exact walk `odf.js` itself used, rather than approximated.** For **odt** (`src/odf/odt/read.ts`): a formula frame is found wherever it actually is — a direct child of `office:text`, one nested inside a `draw:g` group, one anchored inline inside a paragraph's own run content (`text:anchor-type="as-char"`, the shape LibreOffice writes for a formula typed into a sentence), and one inside a list item's own paragraph. Each block lands at its **true position** among the paragraphs/tables `odf.js` already read, because this adapter mirrors `readOdt`'s own `readBlocks` walk to *count* how many `ContentBlock`s each `office:text` child contributes — the per-element bookkeeping that was previously missing and forced every formula to be appended at the end (a `text:list` unwraps into one `ContentParagraph` per item at every nesting level, so "one raw child = one block" does not hold, which is exactly why counting rather than indexing is required). Two bounded, honest details remain: an *inline* formula's block is placed immediately **after** the paragraph containing it rather than truly inside it (`ContentRun` is text-only, so `ContentBlock` has no inline slot for an embedded object, and splitting the paragraph around the formula would invent a boundary the source never had), and an inline frame carries `svg:width`/`svg:height` but no `svg:x` — so its recovered frame is the declared size at a zero origin the text flow replaces, which is all the wordprocessing layout engine reads from it anyway. For **odp** (`src/odf/odp/read.ts`): every formula on every slide is detected, groups included. `collectSlideFormulaFrames` replicates `odf.js`'s own `walkDrawShapes` traversal exactly — document order, recursing into a `draw:g`'s children with that group's own `draw:transform` composed, one shape per `draw:frame` whose geometry `readDrawFrame` resolves and none for any it cannot — so the shape index it counts *is* the index `readOdp` assigned. The previous "skip the whole slide if it contains any `draw:g`" narrowing existed only because the old correspondence was "Nth top-level frame = `shapes[N]`", which a group breaks by splicing its own frames into the same flat array; deriving the index from the same walk removes the ambiguity rather than working around it. **ods needs no detection pass of its own at all, unlike odt and odp**: `odf.js` 2.2.0's own `readOds` walks each `table:table-cell`'s children with a real `TableCursor` and classifies an embedded formula sub-document directly (`readOdfFormulaDocument`, alongside the wordprocessing/presentation/spreadsheet/drawing kinds its 2.1.0 classifier already recognised), so a cell-anchored formula arrives as an ordinary `ContentSheet.embeddedObjects` entry already carrying its own anchor. `src/layout/sheets.ts` consumes that directly — see the cell-anchored-formula gotcha below.
|
|
689
689
|
- **A formula crossing a boundary that cannot typeset it degrades to its own plain-text stand-in — its StarMath annotation, or the literal `[formula]` — never to nothing. The docx bridges are no longer part of that list.** `buildDocxPackage` now writes a genuine OMML display equation (`m:oMathPara` > `m:oMath`, structurally translated by `src/omml/write.ts` — see the architecture entry above), so a formula crossing `odtToDocx`, or reaching a docx through any other `buildDocxPackage` caller, arrives as real, editable Word math rather than text. The stand-in survives there for exactly one case: a formula whose MathML produces no OMML content at all (an empty `mathml` array). An individual MathML construct with no OMML counterpart degrades on its own, *inside* the equation, as a literal-text run with an `unsupported-element` diagnostic reported through `buildDocxPackage`'s own `onMathDiagnostic` (threaded from `odtToDocx`/`markdownToDocx`'s `DocumentBridgeOptions`) — it never drags the whole formula down to text. `buildOdtPackage` is no longer on that list either: it writes a real embedded formula sub-document (a nested `Object N/content.xml` with its own `draw:frame`/`draw:object` reference and manifest entry — see the `src/odf-package/` architecture entry), with the identical single-case fallback, a formula carrying no MathML nodes at all. The markdown writer is the only genuinely stand-in-only path left, since CommonMark/GFM has no math construct whatsoever. **`odmToPdf` is not part of this list either**: a chapter's formula is an ordinary block inside that chapter's own `ContentDocument`, so it survives concatenation into the combined document exactly as a paragraph does and renders as genuine typeset MathML. That used to be a documented gap — the formulas travelled in a side-channel map keyed by `sourcePath`, and re-keying every entry against the combined document's own renumbered block indices was intractable — which moving a formula's content *into* the `ContentDocument` removed outright rather than solved.
|
|
690
|
-
- **OMML is read as well as written, but the two directions are deliberately not symmetric in coverage.** `readDocxContent` recovers a docx equation as a real `ContentEmbeddedObjectBlock` carrying its own MathML — the identical shape `readOdtContent` produces for an ODF embedded formula — so `docxToPdf` typesets a Word-authored equation, and `odt → docx → odt` carries a formula through as a formula. The reader covers strictly more than the writer emits, because it has to read what Word wrote rather than only what this package wrote: `m:d`, `m:nary`, `m:acc`, `m:bar`, `m:func`, and `m:sPre` have exact MathML inverses and no writer counterpart at all (see the `src/omml/` architecture entry). What that asymmetry costs in practice: a `docx → odt → docx` round trip of a Word-authored `m:d` comes back as explicit `mo` fence tokens inside an `mrow` rather than as an auto-growing `m:d` delimiter again, an `m:nary` comes back as a scripted operator followed by its operand rather than as an `m:nary`, and an `m:sPre` degrades outright on the way back out, since `mmultiscripts` is one of the constructs `src/omml/write.ts` has no OMML expression for. The mathematics survives every one of those hops; only the specific OMML construct that expressed it does not. Three further real, tracked read-side boundaries: an equation inside a TABLE CELL is
|
|
690
|
+
- **OMML is read as well as written, but the two directions are deliberately not symmetric in coverage.** `readDocxContent` recovers a docx equation as a real `ContentEmbeddedObjectBlock` carrying its own MathML — the identical shape `readOdtContent` produces for an ODF embedded formula — so `docxToPdf` typesets a Word-authored equation, and `odt → docx → odt` carries a formula through as a formula. The reader covers strictly more than the writer emits, because it has to read what Word wrote rather than only what this package wrote: `m:d`, `m:nary`, `m:acc`, `m:bar`, `m:func`, and `m:sPre` have exact MathML inverses and no writer counterpart at all (see the `src/omml/` architecture entry). What that asymmetry costs in practice: a `docx → odt → docx` round trip of a Word-authored `m:d` comes back as explicit `mo` fence tokens inside an `mrow` rather than as an auto-growing `m:d` delimiter again, an `m:nary` comes back as a scripted operator followed by its operand rather than as an `m:nary`, and an `m:sPre` degrades outright on the way back out, since `mmultiscripts` is one of the constructs `src/omml/write.ts` has no OMML expression for. The mathematics survives every one of those hops; only the specific OMML construct that expressed it does not. Three further real, tracked read-side boundaries: an equation inside a TABLE CELL is now recovered too — `spliceDocxEmbeddedObjects` descends into every table's cells (and any table nested in a cell, recursively), pairing each `ContentTableCell` with its own `w:tc` and splicing the formula into THAT cell's blocks rather than only walking top-level paragraphs; OMML records no geometry whatsoever, so a recovered block's `frame` is a stand-in whose only meaningful field is `heightPt`, taken from the equation's own `w:rPr/w:sz` when it states one and from Word's own 11pt body default otherwise, stated as the exact inverse of the frame fit `src/layout/shared.ts`'s `formulaSizePtForFrame` applies; and an `mtext` that carried an explicit `mathvariant` was written as an ordinary styled math run, which OMML gives no way to distinguish from a styled `mi`, so it reads back as `mi`/`mn`/`mo` rather than as `mtext`.
|
|
691
691
|
- **The OMML translator covers exactly the construct set `src/mathml/layout.ts` typesets, no more — the two are kept aligned deliberately, not by accident.** `mrow`/`mstyle`/`semantics` flatten (every OMML argument slot already holds a sequence, so OMML has no row element of its own); `mi`/`mn`/`mo`/`mtext` become `m:r`/`m:t` runs, with `mtext` written as OMML normal text (`m:nor`) and every `mathvariant` mapped onto the `m:scr` script + `m:sty` style pair — a mapping with no residue, since OMML's two axes span MathML's fourteen values exactly. The honest limits: a stretchy fence is written as an ordinary operator run rather than as an auto-growing `m:d` delimiter — which now genuinely DIVERGES from the PDF path, where a fence does stretch to its content (see the stretchy-fence gotcha above): Word will render the docx fence at its base size where the PDF renders it assembled and full height. A tracked, bounded gap, not a silent one; closing it means emitting a real `m:d` with the fence characters as its `m:begChr`/`m:endChr`, which is a different write shape from the run-per-token one the rest of this translator uses. `munderover` becomes a nested `m:limUpp`/`m:limLow` pair rather than an `m:nary`, because `m:nary`'s own `m:e` slot is the *operand* being summed and MathML records no operand inside `munderover` at all (it sits outside as a following sibling, with nothing marking where it ends — choosing one would be guessing at operand scope), and `mspace` becomes a single literal space with an `approximated-element` diagnostic, since OMML has no width-parameterised spacer anywhere in its vocabulary. `mathvariant` is carried as markup only: the characters themselves stay in their base form rather than being rewritten into the Mathematical Alphanumeric Symbols block the way `applyMathVariant` does for glyph rendering, which would double-apply the style in Word. The `xmlns:m` declaration goes on the fragment's own root rather than on `w:document`, so an equation appended through `DocxParagraph.appendOfficeMath` stays valid inside a docx this package did not scaffold.
|
|
692
692
|
- **`sourcePath` traces a `LayoutItem` back to the `ContentDocument` node it came from, but only within one read+layout pass.** `ooxml.js`'s `readDocx`/`readPptx` stamp every `ContentRun`/`ContentImageBlock`/`ContentTable`/`ContentShape` with a positional path (`sections[0].blocks[2].runs[1]`, `slides[1].shapes[3].blocks[0]`); `convertWordprocessingToLayout`/`convertPresentationToLayout` copy that same string onto whichever `LayoutText`/`LayoutImage`/`LayoutLink`/`LayoutRect` item(s) it produces, so a positioned PDF-side item can be traced back to its semantic origin. When line-wrapping splits one run's word across a run boundary, every resulting fragment gets its own run's path (not a shared or merged one); when a single run is emergency-split across several lines or pages, every resulting fragment keeps that same one run's path unchanged. A table cell's background `LayoutRect` is attributed to its containing table's own `sourcePath`, since `ContentTableCell` carries none of its own. This is **not** an edit-tracking or incremental-relayout mechanism — the path is only valid against the exact `ContentDocument`/`Package` it was assigned from in that one read; editing the document, re-reading it, or reordering its blocks invalidates every previously-captured path, and nothing here recomputes or diffs paths across two versions of a document.
|
|
693
693
|
- **`readMarkdownContent` passes markdown-codec's `readMarkdown` return value straight through, unlike `readDocxContent`/`readOdtContent`/etc., which build a fresh `ContentDocument` envelope from a narrower, format-specific shape.** `markdown-codec`'s own `readMarkdown` already produces a full `document-schema.js` `ContentDocument` directly (`kind`/`formatVersion`/`metadata`/`sections`) — the identical `ContentDocument` type `documents.js` itself imports and re-exports from `document-schema.js`, with no local schema of its own to reconcile against — so, after narrowing to the `wordprocessing` variant, there is nothing left to rebuild.
|
|
@@ -732,7 +732,7 @@ The prose below is authoritative; this table is a quick-reference summary of it,
|
|
|
732
732
|
|
|
733
733
|
**docx/pptx/odt/odp/ods/odg → PDF** is a genuine layout render: the docx/odt flow/pagination engine and the pptx/odp direct-placement engine both produce real positioned text, images, tables, and (for docx/odt) numbered/bulleted lists, styled through the full cascade (theme fonts/colours, `basedOn` chains, placeholder inheritance for docx/pptx; `style:default-style`/`style:parent-style-name` chains for odt/odp). `odg` renders its vector primitives (rect/ellipse/line/path, the last emitted as real PDF `m`/`l`/`c`/`h` content-stream operators, not a polygon approximation of any curve) and reuses the pptx/odp direct-placement engine's own shape conversion for whatever text it also carries. It is a faithful **visual approximation**, not a pixel- or line-identical reproduction of what Word/PowerPoint/Writer/Impress/Draw would themselves render — how close depends on which typeface the document asks for and whether it embedded one, see the font-resolution gotcha above.
|
|
734
734
|
|
|
735
|
-
**odf → PDF (`odfToPdf`), and a formula embedded inside odt/odp/ods,** render **faithful mathematical typesetting**, not a static image or a plain-text placeholder: real box-model layout (script/limit positioning, fraction/radical geometry with correct rule thickness, table column alignment, `mathvariant` → Mathematical Alphanumeric Symbols mapping) through the embedded STIX Two Math font, with genuine per-glyph metrics (advance width, italic correction, top-accent attachment) and font-wide layout constants (axis height, fraction/radical rule thickness and gaps, script shift amounts) parsed directly from that font's own `MATH` table — not approximated or hand-tuned. A vertical fence around a tall construct genuinely stretches too, assembled from the font's own `MathVariants` pieces and sized to what it wraps, rather than drawn at a fixed base size — and an over/under-brace spanning its own `munder`/`mover`/`munderover` base stretches horizontally on the identical basis, sized to that base's own width. The honest limits: `msqrt`/`mroot` still draw a hand-built radical sign rather than the font's own stretched one (a structural reason, not because the font data is unavailable — see the Gotchas entries above); `mover`/`munder` centre geometrically rather than at the font's own declared accent-attachment point; and the operator dictionary and Greek `mathvariant` mapping each cover a deliberately bounded, common-case set rather than the full specification. For a formula anchored to a spreadsheet **cell**, position is genuinely resolved against that sheet's real column/row geometry (verified end to end against a real LibreOffice-authored `.ods`),
|
|
735
|
+
**odf → PDF (`odfToPdf`), and a formula embedded inside odt/odp/ods,** render **faithful mathematical typesetting**, not a static image or a plain-text placeholder: real box-model layout (script/limit positioning, fraction/radical geometry with correct rule thickness, table column alignment, `mathvariant` → Mathematical Alphanumeric Symbols mapping) through the embedded STIX Two Math font, with genuine per-glyph metrics (advance width, italic correction, top-accent attachment) and font-wide layout constants (axis height, fraction/radical rule thickness and gaps, script shift amounts) parsed directly from that font's own `MATH` table — not approximated or hand-tuned. A vertical fence around a tall construct genuinely stretches too, assembled from the font's own `MathVariants` pieces and sized to what it wraps, rather than drawn at a fixed base size — and an over/under-brace spanning its own `munder`/`mover`/`munderover` base stretches horizontally on the identical basis, sized to that base's own width. The honest limits: `msqrt`/`mroot` still draw a hand-built radical sign rather than the font's own stretched one (a structural reason, not because the font data is unavailable — see the Gotchas entries above); `mover`/`munder` centre geometrically rather than at the font's own declared accent-attachment point; and the operator dictionary and Greek `mathvariant` mapping each cover a deliberately bounded, common-case set rather than the full specification. For a formula anchored to a spreadsheet **cell**, position is genuinely resolved against that sheet's real column/row geometry (verified end to end against a real LibreOffice-authored `.ods`), and the rendered **size** comes from the same two-pass frame fit every engine uses (`formulaSizePtForFrame`) — see the Gotchas entry on it for the docx-OMML width-less fallback. **`pdfToOdf` (PDF → structured MathML) is not attempted, on either direction** — recovering a semantic operator tree (is this pair of glyphs a fraction, or a coincidentally stacked pair of ordinary characters? is a raised glyph a superscript, or just a smaller font size used for emphasis?) from nothing but positioned glyphs and paths is a categorically different, OCR-adjacent problem, with no geometry-reconstruction analogue anywhere else in this package: `reconstructWordprocessing`/`reconstructPresentation` recover paragraph/shape *structure* from geometry, never semantic *meaning* the way recognising a fraction would require.
|
|
736
736
|
|
|
737
737
|
**PDF → docx/pptx/odt/odp** is necessarily a **best-effort reconstruction** from geometry: a PDF page is just positioned glyphs and images, with no semantic paragraph or shape structure to recover. Reading order, bold/italic/colour/font-size, and page/slide count are preserved; paragraph and text-block boundaries are inferred from baseline spacing and left-margin indentation, not recovered exactly. Two further kinds of content are recovered on top of that text, each on its own explicit terms: a real `ContentTable`, but **only** where a genuine drawn gridline lattice is detected, never from text alignment (which would be inventing structure, not recovering it); and a page's vector primitives, into a nested drawing document that currently reaches the `ContentDocument` pivot but not the output bytes. Both are covered in full by their own [Gotchas](#gotchas-and-quirks) entries.
|
|
738
738
|
|
|
@@ -6,7 +6,8 @@ type OmmlDiagnosticSink = (diagnostic: OmmlDiagnostic, context: {
|
|
|
6
6
|
readonly sourcePath?: string;
|
|
7
7
|
}) => void;
|
|
8
8
|
declare function collectBodyParagraphs(nodes: readonly XmlNode[], out: XmlElement[]): void;
|
|
9
|
+
declare function collectBodyTables(nodes: readonly XmlNode[], out: XmlElement[]): void;
|
|
9
10
|
declare const PARAGRAPH_NON_CONTENT_TAGS: ReadonlySet<string>;
|
|
10
11
|
declare function equationFrame(equation: XmlElement): Box;
|
|
11
12
|
//#endregion
|
|
12
|
-
export { equationFrame as i, PARAGRAPH_NON_CONTENT_TAGS as n, collectBodyParagraphs as r, OmmlDiagnosticSink as t };
|
|
13
|
+
export { equationFrame as a, collectBodyTables as i, PARAGRAPH_NON_CONTENT_TAGS as n, collectBodyParagraphs as r, OmmlDiagnosticSink as t };
|
|
@@ -6,7 +6,8 @@ type OmmlDiagnosticSink = (diagnostic: OmmlDiagnostic, context: {
|
|
|
6
6
|
readonly sourcePath?: string;
|
|
7
7
|
}) => void;
|
|
8
8
|
declare function collectBodyParagraphs(nodes: readonly XmlNode[], out: XmlElement[]): void;
|
|
9
|
+
declare function collectBodyTables(nodes: readonly XmlNode[], out: XmlElement[]): void;
|
|
9
10
|
declare const PARAGRAPH_NON_CONTENT_TAGS: ReadonlySet<string>;
|
|
10
11
|
declare function equationFrame(equation: XmlElement): Box;
|
|
11
12
|
//#endregion
|
|
12
|
-
export { equationFrame as i, PARAGRAPH_NON_CONTENT_TAGS as n, collectBodyParagraphs as r, OmmlDiagnosticSink as t };
|
|
13
|
+
export { equationFrame as a, collectBodyTables as i, PARAGRAPH_NON_CONTENT_TAGS as n, collectBodyParagraphs as r, OmmlDiagnosticSink as t };
|
package/dist/layout/engine.cjs
CHANGED
|
@@ -175,7 +175,7 @@ function layoutFormulaFlow(block, section, pages, state, contentLeftXDown, conte
|
|
|
175
175
|
layoutFormulaFallback(block, section, pages, state, contentLeftXDown, contentWidthPt, contentBottomYDown, measurer);
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
|
-
const sizePt = require_layout_shared.
|
|
178
|
+
const sizePt = require_layout_shared.formulaSizePtForFrame(formula.mathml, block.frame);
|
|
179
179
|
const metrics = (0, pdf_codec.loadMathFont)().metricsAt(sizePt);
|
|
180
180
|
const { box } = require_mathml_layout.layoutFormula(formula.mathml, {
|
|
181
181
|
metrics,
|
package/dist/layout/engine.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { flipY } from "../model/geometry.js";
|
|
2
2
|
import { formulaOfBlock, formulaPlaceholderText } from "../model/formula.js";
|
|
3
3
|
import { layoutFormula } from "../mathml/layout.js";
|
|
4
|
-
import { alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt,
|
|
4
|
+
import { alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt, formulaSizePtForFrame, justifyLineGapsPt, lineNaturalHeightPt, pushCellBorderLines, registerImage, sumColumnWidthsPt } from "./shared.js";
|
|
5
5
|
import { COLOR_BLACK, LAYOUT_FORMAT_VERSION } from "document-schema.js";
|
|
6
6
|
import { loadMathFont, wrapRunsToWidth } from "pdf-codec";
|
|
7
7
|
//#region src/layout/engine.ts
|
|
@@ -174,7 +174,7 @@ function layoutFormulaFlow(block, section, pages, state, contentLeftXDown, conte
|
|
|
174
174
|
layoutFormulaFallback(block, section, pages, state, contentLeftXDown, contentWidthPt, contentBottomYDown, measurer);
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
177
|
-
const sizePt =
|
|
177
|
+
const sizePt = formulaSizePtForFrame(formula.mathml, block.frame);
|
|
178
178
|
const metrics = loadMathFont().metricsAt(sizePt);
|
|
179
179
|
const { box } = layoutFormula(formula.mathml, {
|
|
180
180
|
metrics,
|
package/dist/layout/shared.cjs
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_mathml_layout = require("../mathml/layout.cjs");
|
|
2
3
|
let ooxml_js = require("ooxml.js");
|
|
3
4
|
let pdf_codec = require("pdf-codec");
|
|
4
5
|
let document_schema_js = require("document-schema.js");
|
|
5
6
|
//#region src/layout/shared.ts
|
|
6
7
|
const NOMINAL_TEXT_SIZE_PT = 18;
|
|
7
8
|
const MIN_FORMULA_SIZE_PT = 8;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
const REFERENCE_FORMULA_SIZE_PT = 12;
|
|
10
|
+
function formulaSizePtForFrame(mathml, frame) {
|
|
11
|
+
const referenceMetrics = (0, pdf_codec.loadMathFont)().metricsAt(REFERENCE_FORMULA_SIZE_PT);
|
|
12
|
+
const { box } = require_mathml_layout.layoutFormula(mathml, {
|
|
13
|
+
metrics: referenceMetrics,
|
|
14
|
+
sizePt: REFERENCE_FORMULA_SIZE_PT,
|
|
15
|
+
color: document_schema_js.COLOR_BLACK
|
|
16
|
+
});
|
|
17
|
+
const heightScale = frame.heightPt / box.heightPt;
|
|
18
|
+
const widthScale = frame.widthPt > 0 ? frame.widthPt / box.widthPt : Number.POSITIVE_INFINITY;
|
|
19
|
+
return Math.max(MIN_FORMULA_SIZE_PT, REFERENCE_FORMULA_SIZE_PT * Math.min(heightScale, widthScale));
|
|
10
20
|
}
|
|
11
21
|
const FALLBACK_ROW_HEIGHT_PT = 20;
|
|
12
22
|
function runFont(run) {
|
|
@@ -152,7 +162,7 @@ exports.NOMINAL_TEXT_SIZE_PT = NOMINAL_TEXT_SIZE_PT;
|
|
|
152
162
|
exports.alignmentOffsetPt = alignmentOffsetPt;
|
|
153
163
|
exports.effectiveStyledRuns = effectiveStyledRuns;
|
|
154
164
|
exports.estimateRowHeightPt = estimateRowHeightPt;
|
|
155
|
-
exports.
|
|
165
|
+
exports.formulaSizePtForFrame = formulaSizePtForFrame;
|
|
156
166
|
exports.justifyLineGapsPt = justifyLineGapsPt;
|
|
157
167
|
exports.lineNaturalHeightPt = lineNaturalHeightPt;
|
|
158
168
|
exports.pushCellBorderLines = pushCellBorderLines;
|
package/dist/layout/shared.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as LayoutFont, t as Alignment } from "../style-DkamR3aY.cjs";
|
|
2
|
-
import { Box, ContentCellBorders, ContentImageBlock, ContentRun, ContentTableRow, LayoutImageAsset, LayoutItem } from "document-schema.js";
|
|
2
|
+
import { Box, ContentCellBorders, ContentImageBlock, ContentRun, ContentTableRow, LayoutImageAsset, LayoutItem, MathMlNode } from "document-schema.js";
|
|
3
3
|
import { StyledRun, TextMeasurer, WrappedLine } from "pdf-codec";
|
|
4
4
|
//#region src/layout/shared.d.ts
|
|
5
5
|
declare const NOMINAL_TEXT_SIZE_PT = 18;
|
|
6
|
-
declare function
|
|
6
|
+
declare function formulaSizePtForFrame(mathml: readonly MathMlNode[], frame: Box): number;
|
|
7
7
|
declare function runFont(run: ContentRun): LayoutFont;
|
|
8
8
|
declare function toStyledRuns(runs: readonly ContentRun[], fontScale?: number): StyledRun[];
|
|
9
9
|
declare function effectiveStyledRuns(runs: readonly ContentRun[], fontScale?: number): StyledRun[];
|
|
@@ -15,4 +15,4 @@ declare function registerImage(block: ContentImageBlock, images: Record<string,
|
|
|
15
15
|
declare function sumColumnWidthsPt(columnWidthsPt: readonly number[], startIndex: number, span: number): number;
|
|
16
16
|
declare function estimateRowHeightPt(row: ContentTableRow, measurer: TextMeasurer, columnWidthsPt: readonly number[], scale: number): number;
|
|
17
17
|
//#endregion
|
|
18
|
-
export { NOMINAL_TEXT_SIZE_PT, alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt,
|
|
18
|
+
export { NOMINAL_TEXT_SIZE_PT, alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt, formulaSizePtForFrame, justifyLineGapsPt, lineNaturalHeightPt, pushCellBorderLines, registerImage, runFont, sumColumnWidthsPt, toStyledRuns };
|
package/dist/layout/shared.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as LayoutFont, t as Alignment } from "../style-DkamR3aY.js";
|
|
2
|
-
import { Box, ContentCellBorders, ContentImageBlock, ContentRun, ContentTableRow, LayoutImageAsset, LayoutItem } from "document-schema.js";
|
|
2
|
+
import { Box, ContentCellBorders, ContentImageBlock, ContentRun, ContentTableRow, LayoutImageAsset, LayoutItem, MathMlNode } from "document-schema.js";
|
|
3
3
|
import { StyledRun, TextMeasurer, WrappedLine } from "pdf-codec";
|
|
4
4
|
//#region src/layout/shared.d.ts
|
|
5
5
|
declare const NOMINAL_TEXT_SIZE_PT = 18;
|
|
6
|
-
declare function
|
|
6
|
+
declare function formulaSizePtForFrame(mathml: readonly MathMlNode[], frame: Box): number;
|
|
7
7
|
declare function runFont(run: ContentRun): LayoutFont;
|
|
8
8
|
declare function toStyledRuns(runs: readonly ContentRun[], fontScale?: number): StyledRun[];
|
|
9
9
|
declare function effectiveStyledRuns(runs: readonly ContentRun[], fontScale?: number): StyledRun[];
|
|
@@ -15,4 +15,4 @@ declare function registerImage(block: ContentImageBlock, images: Record<string,
|
|
|
15
15
|
declare function sumColumnWidthsPt(columnWidthsPt: readonly number[], startIndex: number, span: number): number;
|
|
16
16
|
declare function estimateRowHeightPt(row: ContentTableRow, measurer: TextMeasurer, columnWidthsPt: readonly number[], scale: number): number;
|
|
17
17
|
//#endregion
|
|
18
|
-
export { NOMINAL_TEXT_SIZE_PT, alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt,
|
|
18
|
+
export { NOMINAL_TEXT_SIZE_PT, alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt, formulaSizePtForFrame, justifyLineGapsPt, lineNaturalHeightPt, pushCellBorderLines, registerImage, runFont, sumColumnWidthsPt, toStyledRuns };
|
package/dist/layout/shared.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { COLOR_BLACK } from "../model/color.js";
|
|
2
2
|
import { DEFAULT_LAYOUT_FONT } from "../model/style.js";
|
|
3
|
+
import { layoutFormula } from "../mathml/layout.js";
|
|
3
4
|
import { base64ToBytes } from "ooxml.js";
|
|
4
|
-
import { crc32, decodePng, readJpegInfo, wrapRunsToWidth } from "pdf-codec";
|
|
5
|
+
import { crc32, decodePng, loadMathFont, readJpegInfo, wrapRunsToWidth } from "pdf-codec";
|
|
5
6
|
//#region src/layout/shared.ts
|
|
6
7
|
const NOMINAL_TEXT_SIZE_PT = 18;
|
|
7
8
|
const MIN_FORMULA_SIZE_PT = 8;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
const REFERENCE_FORMULA_SIZE_PT = 12;
|
|
10
|
+
function formulaSizePtForFrame(mathml, frame) {
|
|
11
|
+
const referenceMetrics = loadMathFont().metricsAt(REFERENCE_FORMULA_SIZE_PT);
|
|
12
|
+
const { box } = layoutFormula(mathml, {
|
|
13
|
+
metrics: referenceMetrics,
|
|
14
|
+
sizePt: REFERENCE_FORMULA_SIZE_PT,
|
|
15
|
+
color: COLOR_BLACK
|
|
16
|
+
});
|
|
17
|
+
const heightScale = frame.heightPt / box.heightPt;
|
|
18
|
+
const widthScale = frame.widthPt > 0 ? frame.widthPt / box.widthPt : Number.POSITIVE_INFINITY;
|
|
19
|
+
return Math.max(MIN_FORMULA_SIZE_PT, REFERENCE_FORMULA_SIZE_PT * Math.min(heightScale, widthScale));
|
|
10
20
|
}
|
|
11
21
|
const FALLBACK_ROW_HEIGHT_PT = 20;
|
|
12
22
|
function runFont(run) {
|
|
@@ -148,4 +158,4 @@ function estimateRowHeightPt(row, measurer, columnWidthsPt, scale) {
|
|
|
148
158
|
return max;
|
|
149
159
|
}
|
|
150
160
|
//#endregion
|
|
151
|
-
export { NOMINAL_TEXT_SIZE_PT, alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt,
|
|
161
|
+
export { NOMINAL_TEXT_SIZE_PT, alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt, formulaSizePtForFrame, justifyLineGapsPt, lineNaturalHeightPt, pushCellBorderLines, registerImage, runFont, sumColumnWidthsPt, toStyledRuns };
|
package/dist/layout/sheets.cjs
CHANGED
|
@@ -50,7 +50,7 @@ function columnLetters(index) {
|
|
|
50
50
|
}
|
|
51
51
|
function resolvePrintRange(sheet, formulas) {
|
|
52
52
|
if (sheet.printSettings.printRange !== void 0) return sheet.printSettings.printRange;
|
|
53
|
-
if (sheet.cells.length === 0 && formulas.length === 0) return;
|
|
53
|
+
if (sheet.cells.length === 0 && formulas.length === 0 && sheet.images.length === 0) return;
|
|
54
54
|
let startRow = Number.POSITIVE_INFINITY;
|
|
55
55
|
let startColumn = Number.POSITIVE_INFINITY;
|
|
56
56
|
let endRow = Number.NEGATIVE_INFINITY;
|
|
@@ -67,6 +67,12 @@ function resolvePrintRange(sheet, formulas) {
|
|
|
67
67
|
endRow = Math.max(endRow, formula.anchorRow);
|
|
68
68
|
endColumn = Math.max(endColumn, formula.anchorColumn);
|
|
69
69
|
}
|
|
70
|
+
for (const image of sheet.images) {
|
|
71
|
+
startRow = Math.min(startRow, image.anchorRow);
|
|
72
|
+
startColumn = Math.min(startColumn, image.anchorColumn);
|
|
73
|
+
endRow = Math.max(endRow, image.anchorRow);
|
|
74
|
+
endColumn = Math.max(endColumn, image.anchorColumn);
|
|
75
|
+
}
|
|
70
76
|
return {
|
|
71
77
|
startRow,
|
|
72
78
|
startColumn,
|
|
@@ -364,7 +370,7 @@ function renderAnchoredFormulas(formulas, columnAxis, rowAxis, gridLeftXPt, grid
|
|
|
364
370
|
const columnPosition = columnAxis.positionByIndex.get(anchored.anchorColumn);
|
|
365
371
|
const rowPosition = rowAxis.positionByIndex.get(anchored.anchorRow);
|
|
366
372
|
if (columnPosition === void 0 || rowPosition === void 0 || hiddenColumnIndices.has(anchored.anchorColumn) || hiddenRowIndices.has(anchored.anchorRow)) continue;
|
|
367
|
-
const sizePt = require_layout_shared.
|
|
373
|
+
const sizePt = require_layout_shared.formulaSizePtForFrame(anchored.formula.mathml, anchored.frame);
|
|
368
374
|
const metrics = (0, pdf_codec.loadMathFont)().metricsAt(sizePt);
|
|
369
375
|
const { box } = require_mathml_layout.layoutFormula(anchored.formula.mathml, {
|
|
370
376
|
metrics,
|
|
@@ -386,6 +392,31 @@ function renderAnchoredFormulas(formulas, columnAxis, rowAxis, gridLeftXPt, grid
|
|
|
386
392
|
});
|
|
387
393
|
}
|
|
388
394
|
}
|
|
395
|
+
function renderAnchoredImages(sheetImages, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageHeightPt, hiddenColumnIndices, hiddenRowIndices, out, images) {
|
|
396
|
+
for (const image of sheetImages) {
|
|
397
|
+
const columnPosition = columnAxis.positionByIndex.get(image.anchorColumn);
|
|
398
|
+
const rowPosition = rowAxis.positionByIndex.get(image.anchorRow);
|
|
399
|
+
if (columnPosition === void 0 || rowPosition === void 0 || hiddenColumnIndices.has(image.anchorColumn) || hiddenRowIndices.has(image.anchorRow)) continue;
|
|
400
|
+
const imageId = require_layout_shared.registerImage(image, images);
|
|
401
|
+
const boxYDown = {
|
|
402
|
+
xPt: gridLeftXPt + columnAxis.offsetsPt[columnPosition] + image.offsetXPt,
|
|
403
|
+
yPt: gridTopYDownPt + rowAxis.offsetsPt[rowPosition] + image.offsetYPt,
|
|
404
|
+
widthPt: image.widthPt,
|
|
405
|
+
heightPt: image.heightPt
|
|
406
|
+
};
|
|
407
|
+
const flipped = require_model_geometry.flipY(boxYDown, pageHeightPt);
|
|
408
|
+
const imageItem = {
|
|
409
|
+
kind: "image",
|
|
410
|
+
imageId,
|
|
411
|
+
xPt: flipped.xPt,
|
|
412
|
+
yPt: flipped.yPt,
|
|
413
|
+
widthPt: image.widthPt,
|
|
414
|
+
heightPt: image.heightPt,
|
|
415
|
+
sourcePath: image.sourcePath
|
|
416
|
+
};
|
|
417
|
+
out.push(imageItem);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
389
420
|
function bandableIndices(start, end, repeat) {
|
|
390
421
|
const indices = [];
|
|
391
422
|
for (let i = start; i <= end; i++) {
|
|
@@ -399,7 +430,7 @@ function rangeIndices(start, end) {
|
|
|
399
430
|
for (let i = start; i <= end; i++) indices.push(i);
|
|
400
431
|
return indices;
|
|
401
432
|
}
|
|
402
|
-
function convertSheetToPages(sheet, measurer, signal, out, formulasOut) {
|
|
433
|
+
function convertSheetToPages(sheet, measurer, signal, out, formulasOut, images) {
|
|
403
434
|
require_ports_abort.throwIfAborted(signal);
|
|
404
435
|
const formulas = anchoredFormulas(sheet);
|
|
405
436
|
const range = resolvePrintRange(sheet, formulas);
|
|
@@ -489,6 +520,7 @@ function convertSheetToPages(sheet, measurer, signal, out, formulasOut) {
|
|
|
489
520
|
if (printSettings.headers) renderHeaderLabels(gutter, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageSize.heightPt, measurer, items);
|
|
490
521
|
items.push(...textItems);
|
|
491
522
|
renderAnchoredFormulas(formulas, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageSize.heightPt, out.length, hiddenColumnIndices, hiddenRowIndices, formulasOut);
|
|
523
|
+
renderAnchoredImages(sheet.images, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageSize.heightPt, hiddenColumnIndices, hiddenRowIndices, items, images);
|
|
492
524
|
out.push({
|
|
493
525
|
widthPt: pageSize.widthPt,
|
|
494
526
|
heightPt: pageSize.heightPt,
|
|
@@ -499,13 +531,14 @@ function convertSheetToPages(sheet, measurer, signal, out, formulasOut) {
|
|
|
499
531
|
function convertSpreadsheetToLayout(doc, options) {
|
|
500
532
|
const pages = [];
|
|
501
533
|
const formulas = [];
|
|
502
|
-
|
|
534
|
+
const images = {};
|
|
535
|
+
for (const sheet of doc.sheets) convertSheetToPages(sheet, options.measurer, options.signal, pages, formulas, images);
|
|
503
536
|
return {
|
|
504
537
|
document: {
|
|
505
538
|
formatVersion: document_schema_js.LAYOUT_FORMAT_VERSION,
|
|
506
539
|
metadata: doc.metadata,
|
|
507
540
|
pages,
|
|
508
|
-
images
|
|
541
|
+
images
|
|
509
542
|
},
|
|
510
543
|
formulas
|
|
511
544
|
};
|
package/dist/layout/sheets.js
CHANGED
|
@@ -2,7 +2,7 @@ import { flipY } from "../model/geometry.js";
|
|
|
2
2
|
import { COLOR_BLACK as COLOR_BLACK$1, rgbHexToColor as rgbHexToColor$1 } from "../model/color.js";
|
|
3
3
|
import { DEFAULT_LAYOUT_FONT } from "../model/style.js";
|
|
4
4
|
import { layoutFormula } from "../mathml/layout.js";
|
|
5
|
-
import { alignmentOffsetPt,
|
|
5
|
+
import { alignmentOffsetPt, formulaSizePtForFrame, justifyLineGapsPt, lineNaturalHeightPt, pushCellBorderLines, registerImage, sumColumnWidthsPt, toStyledRuns } from "./shared.js";
|
|
6
6
|
import { throwIfAborted } from "../ports/abort.js";
|
|
7
7
|
import { LAYOUT_FORMAT_VERSION } from "document-schema.js";
|
|
8
8
|
import { loadMathFont, wrapRunsToWidth } from "pdf-codec";
|
|
@@ -51,7 +51,7 @@ function columnLetters(index) {
|
|
|
51
51
|
}
|
|
52
52
|
function resolvePrintRange(sheet, formulas) {
|
|
53
53
|
if (sheet.printSettings.printRange !== void 0) return sheet.printSettings.printRange;
|
|
54
|
-
if (sheet.cells.length === 0 && formulas.length === 0) return;
|
|
54
|
+
if (sheet.cells.length === 0 && formulas.length === 0 && sheet.images.length === 0) return;
|
|
55
55
|
let startRow = Number.POSITIVE_INFINITY;
|
|
56
56
|
let startColumn = Number.POSITIVE_INFINITY;
|
|
57
57
|
let endRow = Number.NEGATIVE_INFINITY;
|
|
@@ -68,6 +68,12 @@ function resolvePrintRange(sheet, formulas) {
|
|
|
68
68
|
endRow = Math.max(endRow, formula.anchorRow);
|
|
69
69
|
endColumn = Math.max(endColumn, formula.anchorColumn);
|
|
70
70
|
}
|
|
71
|
+
for (const image of sheet.images) {
|
|
72
|
+
startRow = Math.min(startRow, image.anchorRow);
|
|
73
|
+
startColumn = Math.min(startColumn, image.anchorColumn);
|
|
74
|
+
endRow = Math.max(endRow, image.anchorRow);
|
|
75
|
+
endColumn = Math.max(endColumn, image.anchorColumn);
|
|
76
|
+
}
|
|
71
77
|
return {
|
|
72
78
|
startRow,
|
|
73
79
|
startColumn,
|
|
@@ -365,7 +371,7 @@ function renderAnchoredFormulas(formulas, columnAxis, rowAxis, gridLeftXPt, grid
|
|
|
365
371
|
const columnPosition = columnAxis.positionByIndex.get(anchored.anchorColumn);
|
|
366
372
|
const rowPosition = rowAxis.positionByIndex.get(anchored.anchorRow);
|
|
367
373
|
if (columnPosition === void 0 || rowPosition === void 0 || hiddenColumnIndices.has(anchored.anchorColumn) || hiddenRowIndices.has(anchored.anchorRow)) continue;
|
|
368
|
-
const sizePt =
|
|
374
|
+
const sizePt = formulaSizePtForFrame(anchored.formula.mathml, anchored.frame);
|
|
369
375
|
const metrics = loadMathFont().metricsAt(sizePt);
|
|
370
376
|
const { box } = layoutFormula(anchored.formula.mathml, {
|
|
371
377
|
metrics,
|
|
@@ -387,6 +393,31 @@ function renderAnchoredFormulas(formulas, columnAxis, rowAxis, gridLeftXPt, grid
|
|
|
387
393
|
});
|
|
388
394
|
}
|
|
389
395
|
}
|
|
396
|
+
function renderAnchoredImages(sheetImages, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageHeightPt, hiddenColumnIndices, hiddenRowIndices, out, images) {
|
|
397
|
+
for (const image of sheetImages) {
|
|
398
|
+
const columnPosition = columnAxis.positionByIndex.get(image.anchorColumn);
|
|
399
|
+
const rowPosition = rowAxis.positionByIndex.get(image.anchorRow);
|
|
400
|
+
if (columnPosition === void 0 || rowPosition === void 0 || hiddenColumnIndices.has(image.anchorColumn) || hiddenRowIndices.has(image.anchorRow)) continue;
|
|
401
|
+
const imageId = registerImage(image, images);
|
|
402
|
+
const boxYDown = {
|
|
403
|
+
xPt: gridLeftXPt + columnAxis.offsetsPt[columnPosition] + image.offsetXPt,
|
|
404
|
+
yPt: gridTopYDownPt + rowAxis.offsetsPt[rowPosition] + image.offsetYPt,
|
|
405
|
+
widthPt: image.widthPt,
|
|
406
|
+
heightPt: image.heightPt
|
|
407
|
+
};
|
|
408
|
+
const flipped = flipY(boxYDown, pageHeightPt);
|
|
409
|
+
const imageItem = {
|
|
410
|
+
kind: "image",
|
|
411
|
+
imageId,
|
|
412
|
+
xPt: flipped.xPt,
|
|
413
|
+
yPt: flipped.yPt,
|
|
414
|
+
widthPt: image.widthPt,
|
|
415
|
+
heightPt: image.heightPt,
|
|
416
|
+
sourcePath: image.sourcePath
|
|
417
|
+
};
|
|
418
|
+
out.push(imageItem);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
390
421
|
function bandableIndices(start, end, repeat) {
|
|
391
422
|
const indices = [];
|
|
392
423
|
for (let i = start; i <= end; i++) {
|
|
@@ -400,7 +431,7 @@ function rangeIndices(start, end) {
|
|
|
400
431
|
for (let i = start; i <= end; i++) indices.push(i);
|
|
401
432
|
return indices;
|
|
402
433
|
}
|
|
403
|
-
function convertSheetToPages(sheet, measurer, signal, out, formulasOut) {
|
|
434
|
+
function convertSheetToPages(sheet, measurer, signal, out, formulasOut, images) {
|
|
404
435
|
throwIfAborted(signal);
|
|
405
436
|
const formulas = anchoredFormulas(sheet);
|
|
406
437
|
const range = resolvePrintRange(sheet, formulas);
|
|
@@ -490,6 +521,7 @@ function convertSheetToPages(sheet, measurer, signal, out, formulasOut) {
|
|
|
490
521
|
if (printSettings.headers) renderHeaderLabels(gutter, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageSize.heightPt, measurer, items);
|
|
491
522
|
items.push(...textItems);
|
|
492
523
|
renderAnchoredFormulas(formulas, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageSize.heightPt, out.length, hiddenColumnIndices, hiddenRowIndices, formulasOut);
|
|
524
|
+
renderAnchoredImages(sheet.images, columnAxis, rowAxis, gridLeftXPt, gridTopYDownPt, pageSize.heightPt, hiddenColumnIndices, hiddenRowIndices, items, images);
|
|
493
525
|
out.push({
|
|
494
526
|
widthPt: pageSize.widthPt,
|
|
495
527
|
heightPt: pageSize.heightPt,
|
|
@@ -500,13 +532,14 @@ function convertSheetToPages(sheet, measurer, signal, out, formulasOut) {
|
|
|
500
532
|
function convertSpreadsheetToLayout(doc, options) {
|
|
501
533
|
const pages = [];
|
|
502
534
|
const formulas = [];
|
|
503
|
-
|
|
535
|
+
const images = {};
|
|
536
|
+
for (const sheet of doc.sheets) convertSheetToPages(sheet, options.measurer, options.signal, pages, formulas, images);
|
|
504
537
|
return {
|
|
505
538
|
document: {
|
|
506
539
|
formatVersion: LAYOUT_FORMAT_VERSION,
|
|
507
540
|
metadata: doc.metadata,
|
|
508
541
|
pages,
|
|
509
|
-
images
|
|
542
|
+
images
|
|
510
543
|
},
|
|
511
544
|
formulas
|
|
512
545
|
};
|
package/dist/layout/slides.cjs
CHANGED
|
@@ -116,7 +116,7 @@ function layoutTable(table, contentLeftXDown, contentWidthPt, startYDown, slideH
|
|
|
116
116
|
function layoutShapeFormula(block, flippedFrame, formulaContext) {
|
|
117
117
|
const formula = require_model_formula.formulaOfBlock(block);
|
|
118
118
|
if (formula === void 0 || formula.mathml.length === 0) return;
|
|
119
|
-
const sizePt = require_layout_shared.
|
|
119
|
+
const sizePt = require_layout_shared.formulaSizePtForFrame(formula.mathml, block.frame);
|
|
120
120
|
const metrics = (0, pdf_codec.loadMathFont)().metricsAt(sizePt);
|
|
121
121
|
const { box } = require_mathml_layout.layoutFormula(formula.mathml, {
|
|
122
122
|
metrics,
|
package/dist/layout/slides.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { flipY } from "../model/geometry.js";
|
|
2
2
|
import { formulaOfBlock } from "../model/formula.js";
|
|
3
3
|
import { layoutFormula } from "../mathml/layout.js";
|
|
4
|
-
import { alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt,
|
|
4
|
+
import { alignmentOffsetPt, effectiveStyledRuns, estimateRowHeightPt, formulaSizePtForFrame, justifyLineGapsPt, lineNaturalHeightPt, registerImage, sumColumnWidthsPt } from "./shared.js";
|
|
5
5
|
import { COLOR_BLACK, LAYOUT_FORMAT_VERSION } from "document-schema.js";
|
|
6
6
|
import { loadMathFont, rotatePointAboutCenter, wrapRunsToWidth } from "pdf-codec";
|
|
7
7
|
//#region src/layout/slides.ts
|
|
@@ -115,7 +115,7 @@ function layoutTable(table, contentLeftXDown, contentWidthPt, startYDown, slideH
|
|
|
115
115
|
function layoutShapeFormula(block, flippedFrame, formulaContext) {
|
|
116
116
|
const formula = formulaOfBlock(block);
|
|
117
117
|
if (formula === void 0 || formula.mathml.length === 0) return;
|
|
118
|
-
const sizePt =
|
|
118
|
+
const sizePt = formulaSizePtForFrame(formula.mathml, block.frame);
|
|
119
119
|
const metrics = loadMathFont().metricsAt(sizePt);
|
|
120
120
|
const { box } = layoutFormula(formula.mathml, {
|
|
121
121
|
metrics,
|
package/dist/model/bytes.cjs
CHANGED
|
@@ -63,20 +63,23 @@ function readAsciiSlice(bytes, offset, length) {
|
|
|
63
63
|
if (bytes.length < offset + length) return;
|
|
64
64
|
return new TextDecoder().decode(bytes.subarray(offset, offset + length));
|
|
65
65
|
}
|
|
66
|
-
function hasOdfMimetypeEntry(bytes,
|
|
66
|
+
function hasOdfMimetypeEntry(bytes, mediaTypes) {
|
|
67
67
|
if (!startsWithBytes(bytes, ZIP_LOCAL_FILE_HEADER)) return false;
|
|
68
68
|
if (readUint16LE(bytes, COMPRESSION_METHOD_OFFSET) !== 0) return false;
|
|
69
69
|
if (readAsciiSlice(bytes, MIMETYPE_FILENAME_OFFSET, 8) !== MIMETYPE_ENTRY_FILENAME) return false;
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
return mediaTypes.some((mediaType) => {
|
|
71
|
+
if (readUint32LE(bytes, COMPRESSED_SIZE_OFFSET) !== mediaType.length) return false;
|
|
72
|
+
return readAsciiSlice(bytes, MIMETYPE_CONTENT_OFFSET, mediaType.length) === mediaType;
|
|
73
|
+
});
|
|
72
74
|
}
|
|
73
|
-
function odfBytesSchema(label,
|
|
74
|
-
|
|
75
|
+
function odfBytesSchema(label, mediaTypes) {
|
|
76
|
+
const accepted = mediaTypes.map((type) => `"${type}"`).join(" or ");
|
|
77
|
+
return zod.z.instanceof(Uint8Array).refine((bytes) => hasOdfMimetypeEntry(bytes, mediaTypes), { message: `not a valid ${label} file: the first zip entry is not a stored "mimetype" part declaring ${accepted}` });
|
|
75
78
|
}
|
|
76
|
-
const OdtBytesSchema = odfBytesSchema("odt", odf_js.ODF_MEDIA_TYPES.odt);
|
|
77
|
-
const OdsBytesSchema = odfBytesSchema("ods", odf_js.ODF_MEDIA_TYPES.ods);
|
|
78
|
-
const OdpBytesSchema = odfBytesSchema("odp", odf_js.ODF_MEDIA_TYPES.odp);
|
|
79
|
-
const OdgBytesSchema = odfBytesSchema("odg", odf_js.ODF_MEDIA_TYPES.odg);
|
|
79
|
+
const OdtBytesSchema = odfBytesSchema("odt", [odf_js.ODF_MEDIA_TYPES.odt, odf_js.ODF_MEDIA_TYPES.ott]);
|
|
80
|
+
const OdsBytesSchema = odfBytesSchema("ods", [odf_js.ODF_MEDIA_TYPES.ods, odf_js.ODF_MEDIA_TYPES.ots]);
|
|
81
|
+
const OdpBytesSchema = odfBytesSchema("odp", [odf_js.ODF_MEDIA_TYPES.odp, odf_js.ODF_MEDIA_TYPES.otp]);
|
|
82
|
+
const OdgBytesSchema = odfBytesSchema("odg", [odf_js.ODF_MEDIA_TYPES.odg, odf_js.ODF_MEDIA_TYPES.otg]);
|
|
80
83
|
function isWellFormedUtf8Text(bytes) {
|
|
81
84
|
try {
|
|
82
85
|
new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
package/dist/model/bytes.js
CHANGED
|
@@ -62,20 +62,23 @@ function readAsciiSlice(bytes, offset, length) {
|
|
|
62
62
|
if (bytes.length < offset + length) return;
|
|
63
63
|
return new TextDecoder().decode(bytes.subarray(offset, offset + length));
|
|
64
64
|
}
|
|
65
|
-
function hasOdfMimetypeEntry(bytes,
|
|
65
|
+
function hasOdfMimetypeEntry(bytes, mediaTypes) {
|
|
66
66
|
if (!startsWithBytes(bytes, ZIP_LOCAL_FILE_HEADER)) return false;
|
|
67
67
|
if (readUint16LE(bytes, COMPRESSION_METHOD_OFFSET) !== 0) return false;
|
|
68
68
|
if (readAsciiSlice(bytes, MIMETYPE_FILENAME_OFFSET, 8) !== MIMETYPE_ENTRY_FILENAME) return false;
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
return mediaTypes.some((mediaType) => {
|
|
70
|
+
if (readUint32LE(bytes, COMPRESSED_SIZE_OFFSET) !== mediaType.length) return false;
|
|
71
|
+
return readAsciiSlice(bytes, MIMETYPE_CONTENT_OFFSET, mediaType.length) === mediaType;
|
|
72
|
+
});
|
|
71
73
|
}
|
|
72
|
-
function odfBytesSchema(label,
|
|
73
|
-
|
|
74
|
+
function odfBytesSchema(label, mediaTypes) {
|
|
75
|
+
const accepted = mediaTypes.map((type) => `"${type}"`).join(" or ");
|
|
76
|
+
return z.instanceof(Uint8Array).refine((bytes) => hasOdfMimetypeEntry(bytes, mediaTypes), { message: `not a valid ${label} file: the first zip entry is not a stored "mimetype" part declaring ${accepted}` });
|
|
74
77
|
}
|
|
75
|
-
const OdtBytesSchema = odfBytesSchema("odt", ODF_MEDIA_TYPES.odt);
|
|
76
|
-
const OdsBytesSchema = odfBytesSchema("ods", ODF_MEDIA_TYPES.ods);
|
|
77
|
-
const OdpBytesSchema = odfBytesSchema("odp", ODF_MEDIA_TYPES.odp);
|
|
78
|
-
const OdgBytesSchema = odfBytesSchema("odg", ODF_MEDIA_TYPES.odg);
|
|
78
|
+
const OdtBytesSchema = odfBytesSchema("odt", [ODF_MEDIA_TYPES.odt, ODF_MEDIA_TYPES.ott]);
|
|
79
|
+
const OdsBytesSchema = odfBytesSchema("ods", [ODF_MEDIA_TYPES.ods, ODF_MEDIA_TYPES.ots]);
|
|
80
|
+
const OdpBytesSchema = odfBytesSchema("odp", [ODF_MEDIA_TYPES.odp, ODF_MEDIA_TYPES.otp]);
|
|
81
|
+
const OdgBytesSchema = odfBytesSchema("odg", [ODF_MEDIA_TYPES.odg, ODF_MEDIA_TYPES.otg]);
|
|
79
82
|
function isWellFormedUtf8Text(bytes) {
|
|
80
83
|
try {
|
|
81
84
|
new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
@@ -5,6 +5,7 @@ const require_omml_read = require("../../omml/read.cjs");
|
|
|
5
5
|
const require_model_block_splice = require("../../model/block-splice.cjs");
|
|
6
6
|
const require_ooxml_docx_vector = require("./vector.cjs");
|
|
7
7
|
const require_ooxml_docx_formula = require("./formula.cjs");
|
|
8
|
+
let ooxml_js = require("ooxml.js");
|
|
8
9
|
//#region src/ooxml/docx/embedded-objects.ts
|
|
9
10
|
function isVectorOnlyRun(run, vectors) {
|
|
10
11
|
const elementChildren = run.children.filter((child) => child.type === "element");
|
|
@@ -27,60 +28,159 @@ function isEmbeddedObjectOnlyParagraph(paragraph, equations, vectors) {
|
|
|
27
28
|
}
|
|
28
29
|
return true;
|
|
29
30
|
}
|
|
31
|
+
function paragraphEmbeddings(block, paragraph, blockIndex, onMathDiagnostic) {
|
|
32
|
+
const equations = require_omml_read.collectOfficeMathElements(paragraph.children);
|
|
33
|
+
const vectors = require_ooxml_docx_vector.collectParagraphVectors(paragraph);
|
|
34
|
+
if (equations.length === 0 && vectors.length === 0) return {
|
|
35
|
+
placements: [],
|
|
36
|
+
consume: false
|
|
37
|
+
};
|
|
38
|
+
const converted = equations.map((equation) => ({
|
|
39
|
+
equation,
|
|
40
|
+
...require_omml_read.readOfficeMath(equation)
|
|
41
|
+
}));
|
|
42
|
+
const rendered = converted.filter((result) => result.mathml.length > 0);
|
|
43
|
+
if (rendered.length === 0 && vectors.length === 0) {
|
|
44
|
+
for (const result of converted) for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
45
|
+
return {
|
|
46
|
+
placements: [],
|
|
47
|
+
consume: false
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const insertAt = blockIndex + 1;
|
|
51
|
+
const placements = [];
|
|
52
|
+
for (const result of rendered) placements.push({
|
|
53
|
+
index: insertAt,
|
|
54
|
+
build: (sourcePath) => {
|
|
55
|
+
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath });
|
|
56
|
+
return require_model_formula.buildFormulaBlock({ mathml: result.mathml }, require_ooxml_docx_formula.equationFrame(result.equation), sourcePath);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
for (const result of converted) {
|
|
60
|
+
if (result.mathml.length > 0) continue;
|
|
61
|
+
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
62
|
+
}
|
|
63
|
+
if (vectors.length > 0) {
|
|
64
|
+
const vectorValues = vectors.map((detected) => detected.vector);
|
|
65
|
+
placements.push({
|
|
66
|
+
index: insertAt,
|
|
67
|
+
build: (sourcePath) => ({
|
|
68
|
+
...require_model_embedded_drawing.buildDrawingBlock({
|
|
69
|
+
widthPt: 0,
|
|
70
|
+
heightPt: 0
|
|
71
|
+
}, vectorValues),
|
|
72
|
+
sourcePath
|
|
73
|
+
})
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
placements,
|
|
78
|
+
consume: isEmbeddedObjectOnlyParagraph(paragraph, equations, vectors)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function spliceContainerBlocks(blocks, containerChildren, pageSize, sourcePathPrefix, onMathDiagnostic) {
|
|
82
|
+
const paragraphElements = [];
|
|
83
|
+
require_ooxml_docx_formula.collectBodyParagraphs(containerChildren, paragraphElements);
|
|
84
|
+
const tableElements = [];
|
|
85
|
+
require_ooxml_docx_formula.collectBodyTables(containerChildren, tableElements);
|
|
86
|
+
let paragraphOrdinal = 0;
|
|
87
|
+
let tableOrdinal = 0;
|
|
88
|
+
let mutated = false;
|
|
89
|
+
const placements = [];
|
|
90
|
+
const consumedIndices = /* @__PURE__ */ new Set();
|
|
91
|
+
const rebuiltBlocks = blocks.map((block, blockIndex) => {
|
|
92
|
+
if (block.kind === "paragraph") {
|
|
93
|
+
const paragraph = paragraphElements[paragraphOrdinal];
|
|
94
|
+
paragraphOrdinal += 1;
|
|
95
|
+
if (paragraph === void 0) return block;
|
|
96
|
+
const { placements: paragraphPlacements, consume } = paragraphEmbeddings(block, paragraph, blockIndex, onMathDiagnostic);
|
|
97
|
+
if (paragraphPlacements.length > 0) placements.push(...paragraphPlacements);
|
|
98
|
+
if (consume) {
|
|
99
|
+
consumedIndices.add(blockIndex);
|
|
100
|
+
mutated = true;
|
|
101
|
+
}
|
|
102
|
+
return block;
|
|
103
|
+
}
|
|
104
|
+
if (block.kind === "table") {
|
|
105
|
+
const tableElement = tableElements[tableOrdinal];
|
|
106
|
+
tableOrdinal += 1;
|
|
107
|
+
if (tableElement === void 0) return block;
|
|
108
|
+
const rebuilt = rebuildTable(block, tableElement, pageSize, `${sourcePathPrefix}[${blockIndex}]`, onMathDiagnostic);
|
|
109
|
+
if (rebuilt !== block) mutated = true;
|
|
110
|
+
return rebuilt;
|
|
111
|
+
}
|
|
112
|
+
return block;
|
|
113
|
+
});
|
|
114
|
+
if (!mutated && placements.length === 0) return [...blocks];
|
|
115
|
+
return require_model_block_splice.spliceBlocks(rebuiltBlocks, placements, consumedIndices, (position) => `${sourcePathPrefix}[${position}]`);
|
|
116
|
+
}
|
|
117
|
+
function rebuildTable(table, tblElement, pageSize, blockPath, onMathDiagnostic) {
|
|
118
|
+
const rowElements = (0, ooxml_js.childrenWithTag)(tblElement, "w:tr");
|
|
119
|
+
let changed = false;
|
|
120
|
+
const rows = table.rows.map((row, rowIndex) => {
|
|
121
|
+
const rowElement = rowElements[rowIndex];
|
|
122
|
+
const cellElements = rowElement === void 0 ? [] : (0, ooxml_js.childrenWithTag)(rowElement, "w:tc");
|
|
123
|
+
const cells = row.cells.map((cell, cellIndex) => {
|
|
124
|
+
const cellElement = cellElements[cellIndex];
|
|
125
|
+
if (cellElement === void 0) return cell;
|
|
126
|
+
const blocks = spliceContainerBlocks(cell.blocks, cellElement.children, pageSize, `${blockPath}.rows[${rowIndex}].cells[${cellIndex}].blocks`, onMathDiagnostic);
|
|
127
|
+
if (blocks === cell.blocks) return cell;
|
|
128
|
+
changed = true;
|
|
129
|
+
return {
|
|
130
|
+
...cell,
|
|
131
|
+
blocks
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
return changed ? {
|
|
135
|
+
...row,
|
|
136
|
+
cells
|
|
137
|
+
} : row;
|
|
138
|
+
});
|
|
139
|
+
return changed ? {
|
|
140
|
+
...table,
|
|
141
|
+
rows
|
|
142
|
+
} : table;
|
|
143
|
+
}
|
|
30
144
|
function spliceDocxEmbeddedObjects(sections, bodyChildren, onMathDiagnostic) {
|
|
31
145
|
const paragraphElements = [];
|
|
32
146
|
require_ooxml_docx_formula.collectBodyParagraphs(bodyChildren, paragraphElements);
|
|
147
|
+
const tableElements = [];
|
|
148
|
+
require_ooxml_docx_formula.collectBodyTables(bodyChildren, tableElements);
|
|
33
149
|
let paragraphOrdinal = 0;
|
|
150
|
+
let tableOrdinal = 0;
|
|
34
151
|
const out = [];
|
|
35
152
|
for (const [sectionIndex, section] of sections.entries()) {
|
|
36
153
|
const placements = [];
|
|
37
154
|
const consumedIndices = /* @__PURE__ */ new Set();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}));
|
|
50
|
-
const rendered = converted.filter((result) => result.mathml.length > 0);
|
|
51
|
-
if (rendered.length === 0 && vectors.length === 0) {
|
|
52
|
-
for (const result of converted) for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const insertAt = blockIndex + 1;
|
|
56
|
-
if (isEmbeddedObjectOnlyParagraph(paragraph, equations, vectors)) consumedIndices.add(blockIndex);
|
|
57
|
-
for (const result of rendered) placements.push({
|
|
58
|
-
index: insertAt,
|
|
59
|
-
build: (sourcePath) => {
|
|
60
|
-
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath });
|
|
61
|
-
return require_model_formula.buildFormulaBlock({ mathml: result.mathml }, require_ooxml_docx_formula.equationFrame(result.equation), sourcePath);
|
|
155
|
+
let mutated = false;
|
|
156
|
+
const rebuiltBlocks = section.blocks.map((block, blockIndex) => {
|
|
157
|
+
if (block.kind === "paragraph") {
|
|
158
|
+
const paragraph = paragraphElements[paragraphOrdinal];
|
|
159
|
+
paragraphOrdinal += 1;
|
|
160
|
+
if (paragraph === void 0) return block;
|
|
161
|
+
const { placements: paragraphPlacements, consume } = paragraphEmbeddings(block, paragraph, blockIndex, onMathDiagnostic);
|
|
162
|
+
if (paragraphPlacements.length > 0) placements.push(...paragraphPlacements);
|
|
163
|
+
if (consume) {
|
|
164
|
+
consumedIndices.add(blockIndex);
|
|
165
|
+
mutated = true;
|
|
62
166
|
}
|
|
63
|
-
|
|
64
|
-
for (const result of converted) {
|
|
65
|
-
if (result.mathml.length > 0) continue;
|
|
66
|
-
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
167
|
+
return block;
|
|
67
168
|
}
|
|
68
|
-
if (
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
})
|
|
76
|
-
});
|
|
169
|
+
if (block.kind === "table") {
|
|
170
|
+
const tableElement = tableElements[tableOrdinal];
|
|
171
|
+
tableOrdinal += 1;
|
|
172
|
+
if (tableElement === void 0) return block;
|
|
173
|
+
const rebuilt = rebuildTable(block, tableElement, section.pageSize, `sections[${sectionIndex}].blocks`, onMathDiagnostic);
|
|
174
|
+
if (rebuilt !== block) mutated = true;
|
|
175
|
+
return rebuilt;
|
|
77
176
|
}
|
|
177
|
+
return block;
|
|
78
178
|
});
|
|
79
|
-
if (placements.length === 0 && consumedIndices.size === 0) {
|
|
179
|
+
if (placements.length === 0 && consumedIndices.size === 0 && !mutated) {
|
|
80
180
|
out.push(section);
|
|
81
181
|
continue;
|
|
82
182
|
}
|
|
83
|
-
const blocks = require_model_block_splice.spliceBlocks(
|
|
183
|
+
const blocks = require_model_block_splice.spliceBlocks(rebuiltBlocks, placements, consumedIndices, (position) => `sections[${sectionIndex}].blocks[${position}]`);
|
|
84
184
|
out.push({
|
|
85
185
|
...section,
|
|
86
186
|
blocks
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as OmmlDiagnosticSink } from "../../formula-
|
|
1
|
+
import { t as OmmlDiagnosticSink } from "../../formula-_8feXQH7.cjs";
|
|
2
2
|
import { ContentSection } from "document-schema.js";
|
|
3
3
|
import { XmlNode } from "ooxml.js";
|
|
4
4
|
//#region src/ooxml/docx/embedded-objects.d.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as OmmlDiagnosticSink } from "../../formula-
|
|
1
|
+
import { t as OmmlDiagnosticSink } from "../../formula-DDSYieJg.js";
|
|
2
2
|
import { XmlNode } from "ooxml.js";
|
|
3
3
|
import { ContentSection } from "document-schema.js";
|
|
4
4
|
//#region src/ooxml/docx/embedded-objects.d.ts
|
|
@@ -3,7 +3,8 @@ import { buildFormulaBlock } from "../../model/formula.js";
|
|
|
3
3
|
import { collectOfficeMathElements, readOfficeMath } from "../../omml/read.js";
|
|
4
4
|
import { spliceBlocks } from "../../model/block-splice.js";
|
|
5
5
|
import { collectParagraphVectors } from "./vector.js";
|
|
6
|
-
import { PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, equationFrame } from "./formula.js";
|
|
6
|
+
import { PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, collectBodyTables, equationFrame } from "./formula.js";
|
|
7
|
+
import { childrenWithTag } from "ooxml.js";
|
|
7
8
|
//#region src/ooxml/docx/embedded-objects.ts
|
|
8
9
|
function isVectorOnlyRun(run, vectors) {
|
|
9
10
|
const elementChildren = run.children.filter((child) => child.type === "element");
|
|
@@ -26,60 +27,159 @@ function isEmbeddedObjectOnlyParagraph(paragraph, equations, vectors) {
|
|
|
26
27
|
}
|
|
27
28
|
return true;
|
|
28
29
|
}
|
|
30
|
+
function paragraphEmbeddings(block, paragraph, blockIndex, onMathDiagnostic) {
|
|
31
|
+
const equations = collectOfficeMathElements(paragraph.children);
|
|
32
|
+
const vectors = collectParagraphVectors(paragraph);
|
|
33
|
+
if (equations.length === 0 && vectors.length === 0) return {
|
|
34
|
+
placements: [],
|
|
35
|
+
consume: false
|
|
36
|
+
};
|
|
37
|
+
const converted = equations.map((equation) => ({
|
|
38
|
+
equation,
|
|
39
|
+
...readOfficeMath(equation)
|
|
40
|
+
}));
|
|
41
|
+
const rendered = converted.filter((result) => result.mathml.length > 0);
|
|
42
|
+
if (rendered.length === 0 && vectors.length === 0) {
|
|
43
|
+
for (const result of converted) for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
44
|
+
return {
|
|
45
|
+
placements: [],
|
|
46
|
+
consume: false
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const insertAt = blockIndex + 1;
|
|
50
|
+
const placements = [];
|
|
51
|
+
for (const result of rendered) placements.push({
|
|
52
|
+
index: insertAt,
|
|
53
|
+
build: (sourcePath) => {
|
|
54
|
+
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath });
|
|
55
|
+
return buildFormulaBlock({ mathml: result.mathml }, equationFrame(result.equation), sourcePath);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
for (const result of converted) {
|
|
59
|
+
if (result.mathml.length > 0) continue;
|
|
60
|
+
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
61
|
+
}
|
|
62
|
+
if (vectors.length > 0) {
|
|
63
|
+
const vectorValues = vectors.map((detected) => detected.vector);
|
|
64
|
+
placements.push({
|
|
65
|
+
index: insertAt,
|
|
66
|
+
build: (sourcePath) => ({
|
|
67
|
+
...buildDrawingBlock({
|
|
68
|
+
widthPt: 0,
|
|
69
|
+
heightPt: 0
|
|
70
|
+
}, vectorValues),
|
|
71
|
+
sourcePath
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
placements,
|
|
77
|
+
consume: isEmbeddedObjectOnlyParagraph(paragraph, equations, vectors)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function spliceContainerBlocks(blocks, containerChildren, pageSize, sourcePathPrefix, onMathDiagnostic) {
|
|
81
|
+
const paragraphElements = [];
|
|
82
|
+
collectBodyParagraphs(containerChildren, paragraphElements);
|
|
83
|
+
const tableElements = [];
|
|
84
|
+
collectBodyTables(containerChildren, tableElements);
|
|
85
|
+
let paragraphOrdinal = 0;
|
|
86
|
+
let tableOrdinal = 0;
|
|
87
|
+
let mutated = false;
|
|
88
|
+
const placements = [];
|
|
89
|
+
const consumedIndices = /* @__PURE__ */ new Set();
|
|
90
|
+
const rebuiltBlocks = blocks.map((block, blockIndex) => {
|
|
91
|
+
if (block.kind === "paragraph") {
|
|
92
|
+
const paragraph = paragraphElements[paragraphOrdinal];
|
|
93
|
+
paragraphOrdinal += 1;
|
|
94
|
+
if (paragraph === void 0) return block;
|
|
95
|
+
const { placements: paragraphPlacements, consume } = paragraphEmbeddings(block, paragraph, blockIndex, onMathDiagnostic);
|
|
96
|
+
if (paragraphPlacements.length > 0) placements.push(...paragraphPlacements);
|
|
97
|
+
if (consume) {
|
|
98
|
+
consumedIndices.add(blockIndex);
|
|
99
|
+
mutated = true;
|
|
100
|
+
}
|
|
101
|
+
return block;
|
|
102
|
+
}
|
|
103
|
+
if (block.kind === "table") {
|
|
104
|
+
const tableElement = tableElements[tableOrdinal];
|
|
105
|
+
tableOrdinal += 1;
|
|
106
|
+
if (tableElement === void 0) return block;
|
|
107
|
+
const rebuilt = rebuildTable(block, tableElement, pageSize, `${sourcePathPrefix}[${blockIndex}]`, onMathDiagnostic);
|
|
108
|
+
if (rebuilt !== block) mutated = true;
|
|
109
|
+
return rebuilt;
|
|
110
|
+
}
|
|
111
|
+
return block;
|
|
112
|
+
});
|
|
113
|
+
if (!mutated && placements.length === 0) return [...blocks];
|
|
114
|
+
return spliceBlocks(rebuiltBlocks, placements, consumedIndices, (position) => `${sourcePathPrefix}[${position}]`);
|
|
115
|
+
}
|
|
116
|
+
function rebuildTable(table, tblElement, pageSize, blockPath, onMathDiagnostic) {
|
|
117
|
+
const rowElements = childrenWithTag(tblElement, "w:tr");
|
|
118
|
+
let changed = false;
|
|
119
|
+
const rows = table.rows.map((row, rowIndex) => {
|
|
120
|
+
const rowElement = rowElements[rowIndex];
|
|
121
|
+
const cellElements = rowElement === void 0 ? [] : childrenWithTag(rowElement, "w:tc");
|
|
122
|
+
const cells = row.cells.map((cell, cellIndex) => {
|
|
123
|
+
const cellElement = cellElements[cellIndex];
|
|
124
|
+
if (cellElement === void 0) return cell;
|
|
125
|
+
const blocks = spliceContainerBlocks(cell.blocks, cellElement.children, pageSize, `${blockPath}.rows[${rowIndex}].cells[${cellIndex}].blocks`, onMathDiagnostic);
|
|
126
|
+
if (blocks === cell.blocks) return cell;
|
|
127
|
+
changed = true;
|
|
128
|
+
return {
|
|
129
|
+
...cell,
|
|
130
|
+
blocks
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
return changed ? {
|
|
134
|
+
...row,
|
|
135
|
+
cells
|
|
136
|
+
} : row;
|
|
137
|
+
});
|
|
138
|
+
return changed ? {
|
|
139
|
+
...table,
|
|
140
|
+
rows
|
|
141
|
+
} : table;
|
|
142
|
+
}
|
|
29
143
|
function spliceDocxEmbeddedObjects(sections, bodyChildren, onMathDiagnostic) {
|
|
30
144
|
const paragraphElements = [];
|
|
31
145
|
collectBodyParagraphs(bodyChildren, paragraphElements);
|
|
146
|
+
const tableElements = [];
|
|
147
|
+
collectBodyTables(bodyChildren, tableElements);
|
|
32
148
|
let paragraphOrdinal = 0;
|
|
149
|
+
let tableOrdinal = 0;
|
|
33
150
|
const out = [];
|
|
34
151
|
for (const [sectionIndex, section] of sections.entries()) {
|
|
35
152
|
const placements = [];
|
|
36
153
|
const consumedIndices = /* @__PURE__ */ new Set();
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}));
|
|
49
|
-
const rendered = converted.filter((result) => result.mathml.length > 0);
|
|
50
|
-
if (rendered.length === 0 && vectors.length === 0) {
|
|
51
|
-
for (const result of converted) for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const insertAt = blockIndex + 1;
|
|
55
|
-
if (isEmbeddedObjectOnlyParagraph(paragraph, equations, vectors)) consumedIndices.add(blockIndex);
|
|
56
|
-
for (const result of rendered) placements.push({
|
|
57
|
-
index: insertAt,
|
|
58
|
-
build: (sourcePath) => {
|
|
59
|
-
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath });
|
|
60
|
-
return buildFormulaBlock({ mathml: result.mathml }, equationFrame(result.equation), sourcePath);
|
|
154
|
+
let mutated = false;
|
|
155
|
+
const rebuiltBlocks = section.blocks.map((block, blockIndex) => {
|
|
156
|
+
if (block.kind === "paragraph") {
|
|
157
|
+
const paragraph = paragraphElements[paragraphOrdinal];
|
|
158
|
+
paragraphOrdinal += 1;
|
|
159
|
+
if (paragraph === void 0) return block;
|
|
160
|
+
const { placements: paragraphPlacements, consume } = paragraphEmbeddings(block, paragraph, blockIndex, onMathDiagnostic);
|
|
161
|
+
if (paragraphPlacements.length > 0) placements.push(...paragraphPlacements);
|
|
162
|
+
if (consume) {
|
|
163
|
+
consumedIndices.add(blockIndex);
|
|
164
|
+
mutated = true;
|
|
61
165
|
}
|
|
62
|
-
|
|
63
|
-
for (const result of converted) {
|
|
64
|
-
if (result.mathml.length > 0) continue;
|
|
65
|
-
for (const diagnostic of result.diagnostics) onMathDiagnostic?.(diagnostic, { sourcePath: block.sourcePath });
|
|
166
|
+
return block;
|
|
66
167
|
}
|
|
67
|
-
if (
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
})
|
|
75
|
-
});
|
|
168
|
+
if (block.kind === "table") {
|
|
169
|
+
const tableElement = tableElements[tableOrdinal];
|
|
170
|
+
tableOrdinal += 1;
|
|
171
|
+
if (tableElement === void 0) return block;
|
|
172
|
+
const rebuilt = rebuildTable(block, tableElement, section.pageSize, `sections[${sectionIndex}].blocks`, onMathDiagnostic);
|
|
173
|
+
if (rebuilt !== block) mutated = true;
|
|
174
|
+
return rebuilt;
|
|
76
175
|
}
|
|
176
|
+
return block;
|
|
77
177
|
});
|
|
78
|
-
if (placements.length === 0 && consumedIndices.size === 0) {
|
|
178
|
+
if (placements.length === 0 && consumedIndices.size === 0 && !mutated) {
|
|
79
179
|
out.push(section);
|
|
80
180
|
continue;
|
|
81
181
|
}
|
|
82
|
-
const blocks = spliceBlocks(
|
|
182
|
+
const blocks = spliceBlocks(rebuiltBlocks, placements, consumedIndices, (position) => `sections[${sectionIndex}].blocks[${position}]`);
|
|
83
183
|
out.push({
|
|
84
184
|
...section,
|
|
85
185
|
blocks
|
|
@@ -15,6 +15,20 @@ function collectBodyParagraphs(nodes, out) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
function collectBodyTables(nodes, out) {
|
|
19
|
+
for (const node of nodes) {
|
|
20
|
+
if (node.type !== "element") continue;
|
|
21
|
+
if (node.tag === "w:tbl") out.push(node);
|
|
22
|
+
else if (node.tag === "w:sdt") {
|
|
23
|
+
const content = (0, ooxml_js.childrenWithTag)(node, "w:sdtContent")[0];
|
|
24
|
+
if (content !== void 0) collectBodyTables(content.children, out);
|
|
25
|
+
} else if (node.tag === "w:ins") collectBodyTables(node.children, out);
|
|
26
|
+
else if (node.tag === "mc:AlternateContent") {
|
|
27
|
+
const target = (0, ooxml_js.childrenWithTag)(node, "mc:Fallback")[0] ?? (0, ooxml_js.childrenWithTag)(node, "mc:Choice")[0];
|
|
28
|
+
if (target !== void 0) collectBodyTables(target.children, out);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
18
32
|
const PARAGRAPH_NON_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
19
33
|
"w:pPr",
|
|
20
34
|
"w:bookmarkStart",
|
|
@@ -51,4 +65,5 @@ function equationFrame(equation) {
|
|
|
51
65
|
//#endregion
|
|
52
66
|
exports.PARAGRAPH_NON_CONTENT_TAGS = PARAGRAPH_NON_CONTENT_TAGS;
|
|
53
67
|
exports.collectBodyParagraphs = collectBodyParagraphs;
|
|
68
|
+
exports.collectBodyTables = collectBodyTables;
|
|
54
69
|
exports.equationFrame = equationFrame;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { OmmlDiagnosticSink, PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, equationFrame };
|
|
1
|
+
import { a as equationFrame, i as collectBodyTables, n as PARAGRAPH_NON_CONTENT_TAGS, r as collectBodyParagraphs, t as OmmlDiagnosticSink } from "../../formula-_8feXQH7.cjs";
|
|
2
|
+
export { OmmlDiagnosticSink, PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, collectBodyTables, equationFrame };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { OmmlDiagnosticSink, PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, equationFrame };
|
|
1
|
+
import { a as equationFrame, i as collectBodyTables, n as PARAGRAPH_NON_CONTENT_TAGS, r as collectBodyParagraphs, t as OmmlDiagnosticSink } from "../../formula-DDSYieJg.js";
|
|
2
|
+
export { OmmlDiagnosticSink, PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, collectBodyTables, equationFrame };
|
|
@@ -14,6 +14,20 @@ function collectBodyParagraphs(nodes, out) {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
function collectBodyTables(nodes, out) {
|
|
18
|
+
for (const node of nodes) {
|
|
19
|
+
if (node.type !== "element") continue;
|
|
20
|
+
if (node.tag === "w:tbl") out.push(node);
|
|
21
|
+
else if (node.tag === "w:sdt") {
|
|
22
|
+
const content = childrenWithTag(node, "w:sdtContent")[0];
|
|
23
|
+
if (content !== void 0) collectBodyTables(content.children, out);
|
|
24
|
+
} else if (node.tag === "w:ins") collectBodyTables(node.children, out);
|
|
25
|
+
else if (node.tag === "mc:AlternateContent") {
|
|
26
|
+
const target = childrenWithTag(node, "mc:Fallback")[0] ?? childrenWithTag(node, "mc:Choice")[0];
|
|
27
|
+
if (target !== void 0) collectBodyTables(target.children, out);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
17
31
|
const PARAGRAPH_NON_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
18
32
|
"w:pPr",
|
|
19
33
|
"w:bookmarkStart",
|
|
@@ -48,4 +62,4 @@ function equationFrame(equation) {
|
|
|
48
62
|
};
|
|
49
63
|
}
|
|
50
64
|
//#endregion
|
|
51
|
-
export { PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, equationFrame };
|
|
65
|
+
export { PARAGRAPH_NON_CONTENT_TAGS, collectBodyParagraphs, collectBodyTables, equationFrame };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as OmmlDiagnosticSink } from "../../formula-
|
|
1
|
+
import { t as OmmlDiagnosticSink } from "../../formula-_8feXQH7.cjs";
|
|
2
2
|
import { ContentDocument } from "document-schema.js";
|
|
3
3
|
import { Package } from "ooxml.js";
|
|
4
4
|
//#region src/ooxml/docx/read.d.ts
|
package/package.json
CHANGED