reamkit 1.24.0 → 1.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +22 -11
  2. package/dist/esm/core/converter/facade.d.ts +3 -3
  3. package/dist/esm/core/converter/facade.js +12 -0
  4. package/dist/esm/core/converter/ream.d.ts +48 -4
  5. package/dist/esm/core/converter/ream.js +25 -4
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +17 -0
  8. package/dist/esm/core/outline.d.ts +17 -0
  9. package/dist/esm/core/outline.js +30 -0
  10. package/dist/esm/core/style-cascade/resolver.js +1 -0
  11. package/dist/esm/core/style-cascade/types.d.ts +3 -1
  12. package/dist/esm/excel/sheet-to-flow.d.ts +10 -0
  13. package/dist/esm/excel/sheet-to-flow.js +14 -1
  14. package/dist/esm/html/html-writer.js +3 -2
  15. package/dist/esm/index.d.ts +3 -0
  16. package/dist/esm/index.js +2 -1
  17. package/dist/esm/layout/page-doc.js +1 -1
  18. package/dist/esm/layout/styled-layout.js +41 -15
  19. package/dist/esm/markdown/markdown-writer.d.ts +41 -0
  20. package/dist/esm/markdown/markdown-writer.js +733 -0
  21. package/dist/esm/pdf/styled-page-emitter.js +20 -1
  22. package/dist/esm/pdf-reader/annots.d.ts +24 -0
  23. package/dist/esm/pdf-reader/annots.js +126 -0
  24. package/dist/esm/pdf-reader/content.d.ts +131 -5
  25. package/dist/esm/pdf-reader/content.js +169 -12
  26. package/dist/esm/pdf-reader/display.d.ts +56 -0
  27. package/dist/esm/pdf-reader/display.js +162 -0
  28. package/dist/esm/pdf-reader/document.d.ts +36 -1
  29. package/dist/esm/pdf-reader/document.js +92 -25
  30. package/dist/esm/pdf-reader/embedded-fonts.d.ts +31 -0
  31. package/dist/esm/pdf-reader/embedded-fonts.js +94 -0
  32. package/dist/esm/pdf-reader/flow-build.d.ts +61 -6
  33. package/dist/esm/pdf-reader/flow-build.js +128 -22
  34. package/dist/esm/pdf-reader/font.js +185 -4
  35. package/dist/esm/pdf-reader/image-decode.js +55 -4
  36. package/dist/esm/pdf-reader/images.d.ts +6 -0
  37. package/dist/esm/pdf-reader/images.js +25 -5
  38. package/dist/esm/pdf-reader/jpeg.d.ts +18 -0
  39. package/dist/esm/pdf-reader/jpeg.js +419 -0
  40. package/dist/esm/pdf-reader/layout.d.ts +1 -1
  41. package/dist/esm/pdf-reader/layout.js +221 -32
  42. package/dist/esm/pdf-reader/pattern-tint.d.ts +17 -0
  43. package/dist/esm/pdf-reader/pattern-tint.js +181 -0
  44. package/dist/esm/pdf-reader/reader.d.ts +9 -1
  45. package/dist/esm/pdf-reader/reader.js +23 -7
  46. package/dist/esm/pdf-reader/shading.d.ts +14 -0
  47. package/dist/esm/pdf-reader/shading.js +27 -1
  48. package/dist/esm/pdf-reader/tagged.js +156 -17
  49. package/dist/esm/pdf-reader/text.d.ts +13 -1
  50. package/dist/esm/pdf-reader/text.js +70 -3
  51. package/dist/esm/pdf-reader/vector.d.ts +25 -1
  52. package/dist/esm/pdf-reader/vector.js +168 -12
  53. package/dist/esm/pptx/slide-parser.js +5 -0
  54. package/dist/esm/word/docx-writer.js +79 -17
  55. package/dist/esm/word/drawing-parser.js +7 -1
  56. package/package.json +1 -1
@@ -447,19 +447,49 @@ function shapeDrawingXml(shape, losses, state, scope) {
447
447
  function xfrmXml(t, cx, cy) {
448
448
  return `<a:xfrm${t?.rotation60k !== void 0 ? ` rot="${t.rotation60k}"` : ""}${t?.flipH ? " flipH=\"1\"" : ""}${t?.flipV ? " flipV=\"1\"" : ""}><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
449
449
  }
450
+ /**
451
+ * The factor that carries a path into whole units, and the space it lands in.
452
+ *
453
+ * §20.1.10.16 — ST_Coordinate is a LONG: a path states its geometry in whole
454
+ * units, and a fractional one is not a value the attribute admits. Word does
455
+ * not round it, and does not ignore the shape: it refuses the document, saying
456
+ * only that it "experienced an error trying to open the file".
457
+ *
458
+ * A path space is arbitrary — `a:path w`/`h` declare the coordinate system the
459
+ * shape's extent is mapped onto — so a path that does not land on whole units
460
+ * is restated in a finer space rather than rounded where it stands. A path read
461
+ * off a PDF is in points, and a point split ten thousand ways is finer than any
462
+ * renderer draws. The factor is bounded so the space stays far inside the
463
+ * coordinate range whatever the path's own size, and a path already on whole
464
+ * units is left exactly as it is.
465
+ */
466
+ var PATH_SPACE = 1e7;
467
+ function pathScale(g) {
468
+ const values = [g.pathWidth, g.pathHeight];
469
+ for (const c of g.commands) if (c.cmd === "move" || c.cmd === "line") values.push(c.x, c.y);
470
+ else if (c.cmd === "cubic") values.push(c.x1, c.y1, c.x2, c.y2, c.x, c.y);
471
+ else if (c.cmd === "quad") values.push(c.x1, c.y1, c.x, c.y);
472
+ else if (c.cmd === "arc") values.push(c.wR, c.hR);
473
+ if (values.every((v) => Number.isInteger(v))) return 1;
474
+ const span = Math.max(...values.map((v) => Number.isFinite(v) ? Math.abs(v) : 0), 1);
475
+ return Math.max(1, Math.floor(PATH_SPACE / span));
476
+ }
450
477
  function geomXml(g) {
451
478
  if (g.kind === "custom" && g.custom) {
479
+ const k = pathScale(g.custom);
480
+ const c1 = (v) => Number.isFinite(v) ? Math.round(v * k) : 0;
452
481
  const cmds = g.custom.commands.map((c) => {
453
482
  switch (c.cmd) {
454
- case "move": return `<a:moveTo><a:pt x="${c.x}" y="${c.y}"/></a:moveTo>`;
455
- case "line": return `<a:lnTo><a:pt x="${c.x}" y="${c.y}"/></a:lnTo>`;
456
- case "cubic": return `<a:cubicBezTo><a:pt x="${c.x1}" y="${c.y1}"/><a:pt x="${c.x2}" y="${c.y2}"/><a:pt x="${c.x}" y="${c.y}"/></a:cubicBezTo>`;
457
- case "quad": return `<a:quadBezTo><a:pt x="${c.x1}" y="${c.y1}"/><a:pt x="${c.x}" y="${c.y}"/></a:quadBezTo>`;
458
- case "arc": return `<a:arcTo wR="${c.wR}" hR="${c.hR}" stAng="${c.stAng}" swAng="${c.swAng}"/>`;
483
+ case "move": return `<a:moveTo><a:pt x="${c1(c.x)}" y="${c1(c.y)}"/></a:moveTo>`;
484
+ case "line": return `<a:lnTo><a:pt x="${c1(c.x)}" y="${c1(c.y)}"/></a:lnTo>`;
485
+ case "cubic": return `<a:cubicBezTo><a:pt x="${c1(c.x1)}" y="${c1(c.y1)}"/><a:pt x="${c1(c.x2)}" y="${c1(c.y2)}"/><a:pt x="${c1(c.x)}" y="${c1(c.y)}"/></a:cubicBezTo>`;
486
+ case "quad": return `<a:quadBezTo><a:pt x="${c1(c.x1)}" y="${c1(c.y1)}"/><a:pt x="${c1(c.x)}" y="${c1(c.y)}"/></a:quadBezTo>`;
487
+ case "arc": return `<a:arcTo wR="${c1(c.wR)}" hR="${c1(c.hR)}" stAng="${Math.round(c.stAng)}" swAng="${Math.round(c.swAng)}"/>`;
459
488
  case "close": return "<a:close/>";
460
489
  }
461
490
  }).join("");
462
- const { pathWidth: w, pathHeight: h } = g.custom;
491
+ const w = c1(g.custom.pathWidth);
492
+ const h = c1(g.custom.pathHeight);
463
493
  return `<a:custGeom><a:avLst/><a:gdLst/><a:rect l="0" t="0" r="${w}" b="${h}"/><a:pathLst><a:path w="${w}" h="${h}">${cmds}</a:path></a:pathLst></a:custGeom>`;
464
494
  }
465
495
  const gds = g.adjust && g.adjust.size > 0 ? [...g.adjust].map(([n, v]) => `<a:gd name="${escapeAttr(n)}" fmla="val ${v}"/>`).join("") : "";
@@ -527,6 +557,16 @@ function tableXml(table, losses, state, scope) {
527
557
  const rows = table.rows.map((row) => rowXml(row, losses, state, scope)).join("");
528
558
  return `<w:tbl>${tblPrXml(table.properties)}<w:tblGrid>${grid}</w:tblGrid>${rows}</w:tbl>`;
529
559
  }
560
+ /**
561
+ * §17.4.60 — `w:tblPr`. CT_Tbl declares it `minOccurs="1"`: a table states its
562
+ * properties even when it has none to state, and Word refuses to open a file
563
+ * whose `w:tbl` opens straight into its grid. Nothing else in the wild writes
564
+ * one without it — 5432 tables across the LibreOffice, Word and POI corpora,
565
+ * every one of them with a `w:tblPr` — and it went unnoticed here because a
566
+ * table read from a real document always carries SOMETHING (a width, a border,
567
+ * an alignment). One reconstructed from a PDF's glyph positions carries none of
568
+ * it, so `Ream.parse(pdf).convert('docx')` wrote a document Word turned away.
569
+ */
530
570
  function tblPrXml(p) {
531
571
  const out = [];
532
572
  if (p.widthType !== void 0) {
@@ -538,7 +578,7 @@ function tblPrXml(p) {
538
578
  if (borders) out.push(borders);
539
579
  const margins = cellMarginsXml("w:tblCellMar", p.defaultCellMargins);
540
580
  if (margins) out.push(margins);
541
- return out.length > 0 ? `<w:tblPr>${out.join("")}</w:tblPr>` : "";
581
+ return `<w:tblPr>${out.join("")}</w:tblPr>`;
542
582
  }
543
583
  function rowXml(row, losses, state, scope) {
544
584
  const trPr = [];
@@ -681,25 +721,47 @@ function pPrBody(p) {
681
721
  if (ind) out.push(ind);
682
722
  const spacing = spacingXml(p);
683
723
  if (spacing) out.push(spacing);
684
- if (p.alignment !== DEFAULT_PARA.alignment) out.push(`<w:jc w:val="${p.alignment}"/>`);
724
+ if (JC.has(p.alignment) && p.alignment !== DEFAULT_PARA.alignment) out.push(`<w:jc w:val="${p.alignment}"/>`);
685
725
  return out.join("");
686
726
  }
687
727
  function pPrWithSect(p, closingSectPr) {
688
728
  const inner = pPrBody(p) + (closingSectPr ?? "");
689
729
  return inner !== "" ? `<w:pPr>${inner}</w:pPr>` : "";
690
730
  }
731
+ /**
732
+ * Whether a measurement is one the file can state.
733
+ *
734
+ * A property that was COMPUTED rather than read can arrive `NaN` — an
735
+ * `undefined` that went through arithmetic, a share of a width that was zero —
736
+ * and `NaN` is not a value ST_SignedTwipsMeasure admits. Word does not ignore
737
+ * the attribute: it refuses the whole document, with no indication of what it
738
+ * objected to. `Ream.parse(pdf).convert('docx')` wrote 355 of them on one form.
739
+ *
740
+ * So a measurement is written only when there IS one, and a paragraph that
741
+ * cannot say how it is indented says nothing and inherits, which is what an
742
+ * absent attribute means (§17.3.1.12).
743
+ */
744
+ var stated = (pt) => Number.isFinite(pt);
745
+ /** §17.18.44 ST_Jc — the tokens the attribute admits, so nothing else is written. */
746
+ var JC = new Set([
747
+ "left",
748
+ "right",
749
+ "center",
750
+ "both",
751
+ "distribute"
752
+ ]);
691
753
  function indXml(p) {
692
754
  const attrs = [];
693
- if (p.indentLeft !== DEFAULT_PARA.indentLeft) attrs.push(`w:left="${twips(p.indentLeft)}"`);
694
- if (p.indentRight !== DEFAULT_PARA.indentRight) attrs.push(`w:right="${twips(p.indentRight)}"`);
695
- if (p.indentFirstLine !== DEFAULT_PARA.indentFirstLine) if (p.indentFirstLine < 0) attrs.push(`w:hanging="${twips(-p.indentFirstLine)}"`);
755
+ if (stated(p.indentLeft) && p.indentLeft !== DEFAULT_PARA.indentLeft) attrs.push(`w:left="${twips(p.indentLeft)}"`);
756
+ if (stated(p.indentRight) && p.indentRight !== DEFAULT_PARA.indentRight) attrs.push(`w:right="${twips(p.indentRight)}"`);
757
+ if (stated(p.indentFirstLine) && p.indentFirstLine !== DEFAULT_PARA.indentFirstLine) if (p.indentFirstLine < 0) attrs.push(`w:hanging="${twips(-p.indentFirstLine)}"`);
696
758
  else attrs.push(`w:firstLine="${twips(p.indentFirstLine)}"`);
697
759
  return attrs.length > 0 ? `<w:ind ${attrs.join(" ")}/>` : "";
698
760
  }
699
761
  function spacingXml(p) {
700
762
  const attrs = [];
701
- if (p.spacingBefore !== DEFAULT_PARA.spacingBefore) attrs.push(`w:before="${twips(p.spacingBefore)}"`);
702
- if (p.spacingAfter !== DEFAULT_PARA.spacingAfter) attrs.push(`w:after="${twips(p.spacingAfter)}"`);
763
+ if (stated(p.spacingBefore) && p.spacingBefore !== DEFAULT_PARA.spacingBefore) attrs.push(`w:before="${twips(p.spacingBefore)}"`);
764
+ if (stated(p.spacingAfter) && p.spacingAfter !== DEFAULT_PARA.spacingAfter) attrs.push(`w:after="${twips(p.spacingAfter)}"`);
703
765
  if ((p.spacingLineRule !== DEFAULT_PARA.spacingLineRule || p.spacingLine !== DEFAULT_PARA.spacingLine) && p.spacingLine > 0) {
704
766
  const lineVal = p.spacingLineRule === "auto" ? Math.round(p.spacingLine * 12) : twips(p.spacingLine);
705
767
  attrs.push(`w:line="${lineVal}"`, `w:lineRule="${p.spacingLineRule}"`);
@@ -742,12 +804,12 @@ function levelXml(level) {
742
804
  }
743
805
  function rawParaPrXml(p) {
744
806
  const attrs = [];
745
- if (p.indentLeft !== void 0) attrs.push(`w:left="${twips(p.indentLeft)}"`);
746
- if (p.indentRight !== void 0) attrs.push(`w:right="${twips(p.indentRight)}"`);
747
- if (p.indentFirstLine !== void 0) if (p.indentFirstLine < 0) attrs.push(`w:hanging="${twips(-p.indentFirstLine)}"`);
807
+ if (p.indentLeft !== void 0 && stated(p.indentLeft)) attrs.push(`w:left="${twips(p.indentLeft)}"`);
808
+ if (p.indentRight !== void 0 && stated(p.indentRight)) attrs.push(`w:right="${twips(p.indentRight)}"`);
809
+ if (p.indentFirstLine !== void 0 && stated(p.indentFirstLine)) if (p.indentFirstLine < 0) attrs.push(`w:hanging="${twips(-p.indentFirstLine)}"`);
748
810
  else attrs.push(`w:firstLine="${twips(p.indentFirstLine)}"`);
749
811
  const ind = attrs.length > 0 ? `<w:ind ${attrs.join(" ")}/>` : "";
750
- const jc = p.alignment !== void 0 ? `<w:jc w:val="${p.alignment}"/>` : "";
812
+ const jc = p.alignment !== void 0 && JC.has(p.alignment) ? `<w:jc w:val="${p.alignment}"/>` : "";
751
813
  return ind || jc ? `<w:pPr>${jc}${ind}</w:pPr>` : "";
752
814
  }
753
815
  function rawRunPrXml(r) {
@@ -1042,7 +1042,11 @@ function vmlTextBox(shape, parseBody) {
1042
1042
  const content = box ? poFindDescendant(box, "w:txbxContent") : void 0;
1043
1043
  if (!content) return void 0;
1044
1044
  const blocks = parseBody(poChildren(content));
1045
- return blocks.length > 0 ? { content: blocks } : void 0;
1045
+ if (blocks.length === 0) return void 0;
1046
+ return {
1047
+ content: blocks,
1048
+ upright: true
1049
+ };
1046
1050
  }
1047
1051
  /**
1048
1052
  * The `@style` of a positioned VML shape as a float anchor: `position:absolute`
@@ -1653,11 +1657,13 @@ function parseTextBox(wsp, parseBody) {
1653
1657
  const anchor = a === "ctr" ? "ctr" : a === "b" ? "b" : a === "t" ? "t" : void 0;
1654
1658
  const v = bodyPr ? poAttr(bodyPr, "vert") : void 0;
1655
1659
  const vertical = v === "vert270" ? "vert270" : v !== void 0 && v !== "horz" ? "vert" : void 0;
1660
+ const upright = bodyPr !== void 0 && poAttr(bodyPr, "upright") === "1";
1656
1661
  const autoFit = bodyPr !== void 0 && poChildren(bodyPr).some((c) => poIs(c, "a:spAutoFit"));
1657
1662
  return {
1658
1663
  content,
1659
1664
  ...chain ? { chain } : {},
1660
1665
  ...vertical ? { vertical } : {},
1666
+ ...upright ? { upright } : {},
1661
1667
  ...autoFit ? { autoFit: true } : {},
1662
1668
  ...lIns !== void 0 ? { insetLeft: emuToPt(lIns) } : {},
1663
1669
  ...tIns !== void 0 ? { insetTop: emuToPt(tIns) } : {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reamkit",
3
- "version": "1.24.0",
3
+ "version": "1.25.1",
4
4
  "description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
5
5
  "license": "MIT",
6
6
  "author": "Alex Krassavin <info@reamkit.dev>",