reamkit 1.25.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.
|
@@ -51,7 +51,7 @@ function readPdf(bytes, password = "", layout = "flow", filters = {}) {
|
|
|
51
51
|
losses.push({
|
|
52
52
|
severity: "dropped",
|
|
53
53
|
feature: FEATURES.images,
|
|
54
|
-
detail: "PDF
|
|
54
|
+
detail: "PDF bare-shading (sh) vector regions are not reconstructed"
|
|
55
55
|
});
|
|
56
56
|
return {
|
|
57
57
|
doc: reconstruction.doc,
|
|
@@ -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
|
|
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("") : "";
|
|
@@ -691,25 +721,47 @@ function pPrBody(p) {
|
|
|
691
721
|
if (ind) out.push(ind);
|
|
692
722
|
const spacing = spacingXml(p);
|
|
693
723
|
if (spacing) out.push(spacing);
|
|
694
|
-
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}"/>`);
|
|
695
725
|
return out.join("");
|
|
696
726
|
}
|
|
697
727
|
function pPrWithSect(p, closingSectPr) {
|
|
698
728
|
const inner = pPrBody(p) + (closingSectPr ?? "");
|
|
699
729
|
return inner !== "" ? `<w:pPr>${inner}</w:pPr>` : "";
|
|
700
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
|
+
]);
|
|
701
753
|
function indXml(p) {
|
|
702
754
|
const attrs = [];
|
|
703
|
-
if (p.indentLeft !== DEFAULT_PARA.indentLeft) attrs.push(`w:left="${twips(p.indentLeft)}"`);
|
|
704
|
-
if (p.indentRight !== DEFAULT_PARA.indentRight) attrs.push(`w:right="${twips(p.indentRight)}"`);
|
|
705
|
-
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)}"`);
|
|
706
758
|
else attrs.push(`w:firstLine="${twips(p.indentFirstLine)}"`);
|
|
707
759
|
return attrs.length > 0 ? `<w:ind ${attrs.join(" ")}/>` : "";
|
|
708
760
|
}
|
|
709
761
|
function spacingXml(p) {
|
|
710
762
|
const attrs = [];
|
|
711
|
-
if (p.spacingBefore !== DEFAULT_PARA.spacingBefore) attrs.push(`w:before="${twips(p.spacingBefore)}"`);
|
|
712
|
-
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)}"`);
|
|
713
765
|
if ((p.spacingLineRule !== DEFAULT_PARA.spacingLineRule || p.spacingLine !== DEFAULT_PARA.spacingLine) && p.spacingLine > 0) {
|
|
714
766
|
const lineVal = p.spacingLineRule === "auto" ? Math.round(p.spacingLine * 12) : twips(p.spacingLine);
|
|
715
767
|
attrs.push(`w:line="${lineVal}"`, `w:lineRule="${p.spacingLineRule}"`);
|
|
@@ -752,12 +804,12 @@ function levelXml(level) {
|
|
|
752
804
|
}
|
|
753
805
|
function rawParaPrXml(p) {
|
|
754
806
|
const attrs = [];
|
|
755
|
-
if (p.indentLeft !== void 0) attrs.push(`w:left="${twips(p.indentLeft)}"`);
|
|
756
|
-
if (p.indentRight !== void 0) attrs.push(`w:right="${twips(p.indentRight)}"`);
|
|
757
|
-
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)}"`);
|
|
758
810
|
else attrs.push(`w:firstLine="${twips(p.indentFirstLine)}"`);
|
|
759
811
|
const ind = attrs.length > 0 ? `<w:ind ${attrs.join(" ")}/>` : "";
|
|
760
|
-
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}"/>` : "";
|
|
761
813
|
return ind || jc ? `<w:pPr>${jc}${ind}</w:pPr>` : "";
|
|
762
814
|
}
|
|
763
815
|
function rawRunPrXml(r) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.25.
|
|
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>",
|