odf.js 1.3.1 → 1.5.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/dist/index.cjs +517 -4
- package/dist/index.d.cts +56 -2
- package/dist/index.d.ts +56 -2
- package/dist/index.js +505 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1243,6 +1243,17 @@ function childrenWithTag(element, tag) {
|
|
|
1243
1243
|
for (const child of element.children) if (child.type === "element" && child.tag === tag) out.push(child);
|
|
1244
1244
|
return out;
|
|
1245
1245
|
}
|
|
1246
|
+
function* walk(nodes) {
|
|
1247
|
+
for (const node of nodes) {
|
|
1248
|
+
yield node;
|
|
1249
|
+
if (node.type === "element") yield* walk(node.children);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function elementsWithTag(nodes, tag) {
|
|
1253
|
+
const out = [];
|
|
1254
|
+
for (const node of walk(nodes)) if (node.type === "element" && node.tag === tag) out.push(node);
|
|
1255
|
+
return out;
|
|
1256
|
+
}
|
|
1246
1257
|
function attrValue(element, name) {
|
|
1247
1258
|
return element.attributes.find((attribute) => attribute.name === name)?.value;
|
|
1248
1259
|
}
|
|
@@ -1522,13 +1533,14 @@ function collectStyles(pkg) {
|
|
|
1522
1533
|
defaultByFamily
|
|
1523
1534
|
};
|
|
1524
1535
|
}
|
|
1525
|
-
function
|
|
1536
|
+
function resolveStyleElementChain(styleName, family, pkg) {
|
|
1526
1537
|
const { byName, defaultByFamily } = collectStyles(pkg);
|
|
1527
1538
|
const diagnostics = [];
|
|
1539
|
+
const elements = [];
|
|
1528
1540
|
const defaultElement = defaultByFamily.get(family);
|
|
1529
|
-
|
|
1541
|
+
if (defaultElement !== void 0) elements.push(defaultElement);
|
|
1530
1542
|
if (styleName === void 0) return {
|
|
1531
|
-
|
|
1543
|
+
elements,
|
|
1532
1544
|
diagnostics
|
|
1533
1545
|
};
|
|
1534
1546
|
const chain = [];
|
|
@@ -1556,7 +1568,16 @@ function resolveStyle(styleName, family, pkg) {
|
|
|
1556
1568
|
currentName = attrValue(element, "style:parent-style-name");
|
|
1557
1569
|
}
|
|
1558
1570
|
chain.reverse();
|
|
1559
|
-
|
|
1571
|
+
elements.push(...chain);
|
|
1572
|
+
return {
|
|
1573
|
+
elements,
|
|
1574
|
+
diagnostics
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
function resolveStyle(styleName, family, pkg) {
|
|
1578
|
+
const { elements, diagnostics } = resolveStyleElementChain(styleName, family, pkg);
|
|
1579
|
+
let properties = {};
|
|
1580
|
+
for (const element of elements) properties = {
|
|
1560
1581
|
...properties,
|
|
1561
1582
|
...parseStyleElementProperties(element).properties
|
|
1562
1583
|
};
|
|
@@ -1565,6 +1586,10 @@ function resolveStyle(styleName, family, pkg) {
|
|
|
1565
1586
|
diagnostics
|
|
1566
1587
|
};
|
|
1567
1588
|
}
|
|
1589
|
+
function findStyleElement(styleName, family, pkg) {
|
|
1590
|
+
const { byName } = collectStyles(pkg);
|
|
1591
|
+
return byName.get(nameKey(family, styleName));
|
|
1592
|
+
}
|
|
1568
1593
|
//#endregion
|
|
1569
1594
|
//#region src/typed/shared/metadata.ts
|
|
1570
1595
|
const META_PART = "meta.xml";
|
|
@@ -1603,6 +1628,480 @@ function readOdfMetadata(pkg) {
|
|
|
1603
1628
|
return metadata;
|
|
1604
1629
|
}
|
|
1605
1630
|
//#endregion
|
|
1631
|
+
//#region src/typed/shared/paragraph.ts
|
|
1632
|
+
function collectRuns(nodes, baseProperties, pkg, out) {
|
|
1633
|
+
for (const node of nodes) {
|
|
1634
|
+
if (node.type === "text") {
|
|
1635
|
+
if (node.value.length > 0) out.push(runFromText(decodeXmlText(node.value), baseProperties));
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
if (node.type !== "element") continue;
|
|
1639
|
+
if (node.tag === "text:s") out.push(runFromText(" ".repeat(getOdfSpaceCount(node)), baseProperties));
|
|
1640
|
+
else if (node.tag === "text:tab") out.push(runFromText(" ", baseProperties));
|
|
1641
|
+
else if (node.tag === "text:line-break") out.push(runFromText("\n", baseProperties));
|
|
1642
|
+
else if (node.tag === "text:span") {
|
|
1643
|
+
const styleName = attrValue(node, "text:style-name");
|
|
1644
|
+
const spanProperties = {
|
|
1645
|
+
...baseProperties,
|
|
1646
|
+
...resolveStyle(styleName, "text", pkg).properties
|
|
1647
|
+
};
|
|
1648
|
+
collectRuns(node.children, spanProperties, pkg, out);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
function runFromText(text, properties) {
|
|
1653
|
+
return {
|
|
1654
|
+
text,
|
|
1655
|
+
bold: properties.bold,
|
|
1656
|
+
italic: properties.italic,
|
|
1657
|
+
underline: properties.underline,
|
|
1658
|
+
strike: properties.strike,
|
|
1659
|
+
fontFamily: properties.fontFamily,
|
|
1660
|
+
sizePt: properties.sizePt,
|
|
1661
|
+
color: properties.color
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function readOdfParagraph(pElement, pkg) {
|
|
1665
|
+
const styleName = attrValue(pElement, "text:style-name");
|
|
1666
|
+
const paragraphProperties = resolveStyle(styleName, "paragraph", pkg).properties;
|
|
1667
|
+
const runs = [];
|
|
1668
|
+
collectRuns(pElement.children, paragraphProperties, pkg, runs);
|
|
1669
|
+
return {
|
|
1670
|
+
kind: "paragraph",
|
|
1671
|
+
runs,
|
|
1672
|
+
styleId: styleName,
|
|
1673
|
+
alignment: paragraphProperties.alignment,
|
|
1674
|
+
spacingBeforePt: paragraphProperties.spacingBeforePt,
|
|
1675
|
+
spacingAfterPt: paragraphProperties.spacingAfterPt,
|
|
1676
|
+
lineSpacing: paragraphProperties.lineSpacing,
|
|
1677
|
+
indentLeftPt: paragraphProperties.indentLeftPt,
|
|
1678
|
+
indentFirstLinePt: paragraphProperties.indentFirstLinePt
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
//#endregion
|
|
1682
|
+
//#region src/typed/shared/table.ts
|
|
1683
|
+
function readRepeatCount(element, attrName) {
|
|
1684
|
+
const raw = attrValue(element, attrName);
|
|
1685
|
+
if (raw === void 0) return 1;
|
|
1686
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1687
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 1;
|
|
1688
|
+
}
|
|
1689
|
+
function resolveColumnWidthPt(columnElement, pkg) {
|
|
1690
|
+
const styleName = attrValue(columnElement, "table:style-name");
|
|
1691
|
+
const styleElement = styleName === void 0 ? void 0 : findStyleElement(styleName, "table-column", pkg);
|
|
1692
|
+
const props = styleElement === void 0 ? void 0 : childrenWithTag(styleElement, "style:table-column-properties")[0];
|
|
1693
|
+
const widthValue = props === void 0 ? void 0 : attrValue(props, "style:column-width");
|
|
1694
|
+
return widthValue === void 0 ? 0 : parseOdfLength(widthValue) ?? 0;
|
|
1695
|
+
}
|
|
1696
|
+
function resolveRowHeightPt(rowElement, pkg) {
|
|
1697
|
+
const styleName = attrValue(rowElement, "table:style-name");
|
|
1698
|
+
const styleElement = styleName === void 0 ? void 0 : findStyleElement(styleName, "table-row", pkg);
|
|
1699
|
+
const props = styleElement === void 0 ? void 0 : childrenWithTag(styleElement, "style:table-row-properties")[0];
|
|
1700
|
+
const heightValue = props === void 0 ? void 0 : attrValue(props, "style:row-height");
|
|
1701
|
+
return heightValue === void 0 ? void 0 : parseOdfLength(heightValue);
|
|
1702
|
+
}
|
|
1703
|
+
function readTableCellBackground(cellElement, pkg) {
|
|
1704
|
+
const styleName = attrValue(cellElement, "table:style-name");
|
|
1705
|
+
const styleElement = styleName === void 0 ? void 0 : findStyleElement(styleName, "table-cell", pkg);
|
|
1706
|
+
const props = styleElement === void 0 ? void 0 : childrenWithTag(styleElement, "style:table-cell-properties")[0];
|
|
1707
|
+
const value = props === void 0 ? void 0 : attrValue(props, "fo:background-color");
|
|
1708
|
+
return value === void 0 ? void 0 : parseOdfColor(value);
|
|
1709
|
+
}
|
|
1710
|
+
function readTableCell(cellElement, pkg) {
|
|
1711
|
+
const blocks = childrenWithTag(cellElement, "text:p").map((p) => readOdfParagraph(p, pkg));
|
|
1712
|
+
const colSpanRaw = attrValue(cellElement, "table:number-columns-spanned");
|
|
1713
|
+
const rowSpanRaw = attrValue(cellElement, "table:number-rows-spanned");
|
|
1714
|
+
return {
|
|
1715
|
+
blocks,
|
|
1716
|
+
colSpan: colSpanRaw === void 0 ? void 0 : Number.parseInt(colSpanRaw, 10),
|
|
1717
|
+
rowSpan: rowSpanRaw === void 0 ? void 0 : Number.parseInt(rowSpanRaw, 10),
|
|
1718
|
+
background: readTableCellBackground(cellElement, pkg)
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function readTableRow(rowElement, pkg) {
|
|
1722
|
+
const cells = [];
|
|
1723
|
+
for (const child of rowElement.children) {
|
|
1724
|
+
if (child.type !== "element") continue;
|
|
1725
|
+
if (child.tag === "table:covered-table-cell") {
|
|
1726
|
+
const repeat = readRepeatCount(child, "table:number-columns-repeated");
|
|
1727
|
+
for (let i = 0; i < repeat; i++) cells.push({ blocks: [] });
|
|
1728
|
+
} else if (child.tag === "table:table-cell") {
|
|
1729
|
+
const cell = readTableCell(child, pkg);
|
|
1730
|
+
const repeat = readRepeatCount(child, "table:number-columns-repeated");
|
|
1731
|
+
for (let i = 0; i < repeat; i++) cells.push(cell);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
return {
|
|
1735
|
+
cells,
|
|
1736
|
+
heightPt: resolveRowHeightPt(rowElement, pkg)
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function readOdfTable(tableElement, pkg) {
|
|
1740
|
+
const columnWidthsPt = [];
|
|
1741
|
+
for (const column of childrenWithTag(tableElement, "table:table-column")) {
|
|
1742
|
+
const widthPt = resolveColumnWidthPt(column, pkg);
|
|
1743
|
+
const repeat = readRepeatCount(column, "table:number-columns-repeated");
|
|
1744
|
+
for (let i = 0; i < repeat; i++) columnWidthsPt.push(widthPt);
|
|
1745
|
+
}
|
|
1746
|
+
const rows = [];
|
|
1747
|
+
for (const rowElement of childrenWithTag(tableElement, "table:table-row")) {
|
|
1748
|
+
const row = readTableRow(rowElement, pkg);
|
|
1749
|
+
const repeat = readRepeatCount(rowElement, "table:number-rows-repeated");
|
|
1750
|
+
for (let i = 0; i < repeat; i++) rows.push(row);
|
|
1751
|
+
}
|
|
1752
|
+
return {
|
|
1753
|
+
kind: "table",
|
|
1754
|
+
rows,
|
|
1755
|
+
columnWidthsPt
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
//#endregion
|
|
1759
|
+
//#region src/typed/shared/transform.ts
|
|
1760
|
+
const FUNCTION_PATTERN = /([a-zA-Z]+)\s*\(\s*([^)]*?)\s*\)/g;
|
|
1761
|
+
function parseOdfTransform(value) {
|
|
1762
|
+
const functions = [];
|
|
1763
|
+
for (const match of value.matchAll(FUNCTION_PATTERN)) {
|
|
1764
|
+
const name = match[1];
|
|
1765
|
+
const argsRaw = match[2];
|
|
1766
|
+
if (name === void 0 || argsRaw === void 0) continue;
|
|
1767
|
+
const args = argsRaw.split(/\s+/).filter((arg) => arg.length > 0);
|
|
1768
|
+
if (name === "rotate") {
|
|
1769
|
+
const angleArg = args[0];
|
|
1770
|
+
if (angleArg === void 0) continue;
|
|
1771
|
+
const angleRad = Number(angleArg);
|
|
1772
|
+
if (!Number.isFinite(angleRad)) continue;
|
|
1773
|
+
functions.push({
|
|
1774
|
+
kind: "rotate",
|
|
1775
|
+
angleRad
|
|
1776
|
+
});
|
|
1777
|
+
} else if (name === "translate") {
|
|
1778
|
+
const xArg = args[0];
|
|
1779
|
+
if (xArg === void 0) continue;
|
|
1780
|
+
const yArg = args[1];
|
|
1781
|
+
const xPt = parseOdfLength(xArg);
|
|
1782
|
+
const yPt = yArg === void 0 ? 0 : parseOdfLength(yArg);
|
|
1783
|
+
if (xPt === void 0 || yPt === void 0) continue;
|
|
1784
|
+
functions.push({
|
|
1785
|
+
kind: "translate",
|
|
1786
|
+
xPt,
|
|
1787
|
+
yPt
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
return functions;
|
|
1792
|
+
}
|
|
1793
|
+
function applyOdfTransform(functions, point) {
|
|
1794
|
+
let current = point;
|
|
1795
|
+
for (const fn of functions) if (fn.kind === "rotate") {
|
|
1796
|
+
const cos = Math.cos(fn.angleRad);
|
|
1797
|
+
const sin = Math.sin(fn.angleRad);
|
|
1798
|
+
current = {
|
|
1799
|
+
xPt: current.xPt * cos + current.yPt * sin,
|
|
1800
|
+
yPt: current.yPt * cos - current.xPt * sin
|
|
1801
|
+
};
|
|
1802
|
+
} else current = {
|
|
1803
|
+
xPt: current.xPt + fn.xPt,
|
|
1804
|
+
yPt: current.yPt + fn.yPt
|
|
1805
|
+
};
|
|
1806
|
+
return current;
|
|
1807
|
+
}
|
|
1808
|
+
function netRotationDeg(functions) {
|
|
1809
|
+
let totalRad = 0;
|
|
1810
|
+
for (const fn of functions) if (fn.kind === "rotate") totalRad += fn.angleRad;
|
|
1811
|
+
return -totalRad * 180 / Math.PI;
|
|
1812
|
+
}
|
|
1813
|
+
function resolveOdfShapeGeometry(element) {
|
|
1814
|
+
const transformValue = attrValue(element, "draw:transform");
|
|
1815
|
+
if (transformValue === void 0) {
|
|
1816
|
+
const box = parseBox(element);
|
|
1817
|
+
return box === void 0 ? void 0 : {
|
|
1818
|
+
frame: box,
|
|
1819
|
+
rotationDeg: void 0
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
const widthValue = attrValue(element, "svg:width");
|
|
1823
|
+
const heightValue = attrValue(element, "svg:height");
|
|
1824
|
+
if (widthValue === void 0 || heightValue === void 0) return;
|
|
1825
|
+
const widthPt = parseOdfLength(widthValue);
|
|
1826
|
+
const heightPt = parseOdfLength(heightValue);
|
|
1827
|
+
if (widthPt === void 0 || heightPt === void 0) return;
|
|
1828
|
+
const functions = parseOdfTransform(transformValue);
|
|
1829
|
+
const center = applyOdfTransform(functions, {
|
|
1830
|
+
xPt: widthPt / 2,
|
|
1831
|
+
yPt: heightPt / 2
|
|
1832
|
+
});
|
|
1833
|
+
const rotationDeg = netRotationDeg(functions);
|
|
1834
|
+
return {
|
|
1835
|
+
frame: {
|
|
1836
|
+
xPt: center.xPt - widthPt / 2,
|
|
1837
|
+
yPt: center.yPt - heightPt / 2,
|
|
1838
|
+
widthPt,
|
|
1839
|
+
heightPt
|
|
1840
|
+
},
|
|
1841
|
+
rotationDeg: rotationDeg === 0 ? void 0 : rotationDeg
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
function composeOdfGroupTransform(groupFunctions, child) {
|
|
1845
|
+
if (groupFunctions.length === 0) return child;
|
|
1846
|
+
const newCenter = applyOdfTransform(groupFunctions, {
|
|
1847
|
+
xPt: child.frame.xPt + child.frame.widthPt / 2,
|
|
1848
|
+
yPt: child.frame.yPt + child.frame.heightPt / 2
|
|
1849
|
+
});
|
|
1850
|
+
const newRotationDeg = (child.rotationDeg ?? 0) + netRotationDeg(groupFunctions);
|
|
1851
|
+
return {
|
|
1852
|
+
frame: {
|
|
1853
|
+
xPt: newCenter.xPt - child.frame.widthPt / 2,
|
|
1854
|
+
yPt: newCenter.yPt - child.frame.heightPt / 2,
|
|
1855
|
+
widthPt: child.frame.widthPt,
|
|
1856
|
+
heightPt: child.frame.heightPt
|
|
1857
|
+
},
|
|
1858
|
+
rotationDeg: newRotationDeg === 0 ? void 0 : newRotationDeg
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
//#endregion
|
|
1862
|
+
//#region src/typed/draw/shapes.ts
|
|
1863
|
+
const ZERO_INSETS = {
|
|
1864
|
+
insetLeftPt: 0,
|
|
1865
|
+
insetTopPt: 0,
|
|
1866
|
+
insetRightPt: 0,
|
|
1867
|
+
insetBottomPt: 0
|
|
1868
|
+
};
|
|
1869
|
+
function readPaddingPt(props, attrName) {
|
|
1870
|
+
const value = attrValue(props, attrName);
|
|
1871
|
+
return value === void 0 ? void 0 : parseOdfLength(value);
|
|
1872
|
+
}
|
|
1873
|
+
function readFrameInsets(frame, pkg) {
|
|
1874
|
+
const { elements } = resolveStyleElementChain(attrValue(frame, "draw:style-name"), "graphic", pkg);
|
|
1875
|
+
let insets = ZERO_INSETS;
|
|
1876
|
+
for (const element of elements) {
|
|
1877
|
+
const props = childrenWithTag(element, "style:graphic-properties")[0];
|
|
1878
|
+
if (props === void 0) continue;
|
|
1879
|
+
insets = {
|
|
1880
|
+
insetLeftPt: readPaddingPt(props, "fo:padding-left") ?? insets.insetLeftPt,
|
|
1881
|
+
insetTopPt: readPaddingPt(props, "fo:padding-top") ?? insets.insetTopPt,
|
|
1882
|
+
insetRightPt: readPaddingPt(props, "fo:padding-right") ?? insets.insetRightPt,
|
|
1883
|
+
insetBottomPt: readPaddingPt(props, "fo:padding-bottom") ?? insets.insetBottomPt
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
return insets;
|
|
1887
|
+
}
|
|
1888
|
+
function readDrawImageBlock(image, frameBox, pkg) {
|
|
1889
|
+
const href = attrValue(image, "xlink:href");
|
|
1890
|
+
const part = href === void 0 ? void 0 : pkg.parts[href];
|
|
1891
|
+
if (part?.kind !== "binary") return;
|
|
1892
|
+
const format = sniffImageFormat(base64ToBytes(part.base64));
|
|
1893
|
+
if (format === void 0) return;
|
|
1894
|
+
return {
|
|
1895
|
+
kind: "image",
|
|
1896
|
+
format,
|
|
1897
|
+
base64: part.base64,
|
|
1898
|
+
widthPt: frameBox.widthPt,
|
|
1899
|
+
heightPt: frameBox.heightPt
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
function readDrawFrameContent(frame, frameBox, pkg) {
|
|
1903
|
+
const table = childrenWithTag(frame, "table:table")[0];
|
|
1904
|
+
if (table !== void 0) return [readOdfTable(table, pkg)];
|
|
1905
|
+
const textBox = childrenWithTag(frame, "draw:text-box")[0];
|
|
1906
|
+
if (textBox !== void 0) return elementsWithTag(textBox.children, "text:p").map((p) => readOdfParagraph(p, pkg));
|
|
1907
|
+
const image = childrenWithTag(frame, "draw:image")[0];
|
|
1908
|
+
if (image !== void 0) {
|
|
1909
|
+
const block = readDrawImageBlock(image, frameBox, pkg);
|
|
1910
|
+
return block === void 0 ? [] : [block];
|
|
1911
|
+
}
|
|
1912
|
+
return [];
|
|
1913
|
+
}
|
|
1914
|
+
function readDrawFrame(frame, groupFunctions, pkg) {
|
|
1915
|
+
const ownGeometry = resolveOdfShapeGeometry(frame);
|
|
1916
|
+
if (ownGeometry === void 0) return;
|
|
1917
|
+
const geometry = composeOdfGroupTransform(groupFunctions, ownGeometry);
|
|
1918
|
+
return {
|
|
1919
|
+
name: attrValue(frame, "draw:name"),
|
|
1920
|
+
frame: geometry.frame,
|
|
1921
|
+
rotationDeg: geometry.rotationDeg,
|
|
1922
|
+
...readFrameInsets(frame, pkg),
|
|
1923
|
+
blocks: readDrawFrameContent(frame, geometry.frame, pkg)
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
function readOwnTransformFunctions(element) {
|
|
1927
|
+
const value = attrValue(element, "draw:transform");
|
|
1928
|
+
return value === void 0 ? [] : parseOdfTransform(value);
|
|
1929
|
+
}
|
|
1930
|
+
function walkDrawShapes(children, groupFunctions, pkg, out) {
|
|
1931
|
+
for (const node of children) {
|
|
1932
|
+
if (node.type !== "element") continue;
|
|
1933
|
+
if (node.tag === "draw:frame") {
|
|
1934
|
+
const shape = readDrawFrame(node, groupFunctions, pkg);
|
|
1935
|
+
if (shape !== void 0) out.push(shape);
|
|
1936
|
+
} else if (node.tag === "draw:g") {
|
|
1937
|
+
const ownFunctions = readOwnTransformFunctions(node);
|
|
1938
|
+
const nested = ownFunctions.length === 0 ? groupFunctions : [...ownFunctions, ...groupFunctions];
|
|
1939
|
+
walkDrawShapes(node.children, nested, pkg, out);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
//#endregion
|
|
1944
|
+
//#region src/typed/odp/read.ts
|
|
1945
|
+
const CONTENT_PART$1 = "content.xml";
|
|
1946
|
+
const STYLES_PART$1 = "styles.xml";
|
|
1947
|
+
const AUTOMATIC_STYLE_PARTS$1 = [CONTENT_PART$1, STYLES_PART$1];
|
|
1948
|
+
function findMasterPageElement(pkg, masterPageName) {
|
|
1949
|
+
if (masterPageName === void 0) return;
|
|
1950
|
+
const stylesPart = pkg.parts[STYLES_PART$1];
|
|
1951
|
+
if (stylesPart?.kind !== "xml") return;
|
|
1952
|
+
const root = rootElement(stylesPart.nodes);
|
|
1953
|
+
const masterStyles = root === void 0 ? void 0 : findChildElement(root.children, "office:master-styles");
|
|
1954
|
+
if (masterStyles === void 0) return;
|
|
1955
|
+
return childrenWithTag(masterStyles, "style:master-page").find((element) => attrValue(element, "style:name") === masterPageName);
|
|
1956
|
+
}
|
|
1957
|
+
function findPageLayoutElement$1(pkg, pageLayoutName) {
|
|
1958
|
+
if (pageLayoutName === void 0) return;
|
|
1959
|
+
for (const partPath of AUTOMATIC_STYLE_PARTS$1) {
|
|
1960
|
+
const part = pkg.parts[partPath];
|
|
1961
|
+
if (part?.kind !== "xml") continue;
|
|
1962
|
+
const root = rootElement(part.nodes);
|
|
1963
|
+
const automaticStyles = root === void 0 ? void 0 : findChildElement(root.children, "office:automatic-styles");
|
|
1964
|
+
if (automaticStyles === void 0) continue;
|
|
1965
|
+
const found = childrenWithTag(automaticStyles, "style:page-layout").find((element) => attrValue(element, "style:name") === pageLayoutName);
|
|
1966
|
+
if (found !== void 0) return found;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
function readSlideSize(page, pkg) {
|
|
1970
|
+
const masterPage = findMasterPageElement(pkg, attrValue(page, "draw:master-page-name"));
|
|
1971
|
+
const pageLayout = findPageLayoutElement$1(pkg, masterPage === void 0 ? void 0 : attrValue(masterPage, "style:page-layout-name"));
|
|
1972
|
+
const properties = pageLayout === void 0 ? void 0 : childrenWithTag(pageLayout, "style:page-layout-properties")[0];
|
|
1973
|
+
return (properties === void 0 ? void 0 : parsePageSize(properties)) ?? document_content_model.SLIDE_SIZE_WIDESCREEN;
|
|
1974
|
+
}
|
|
1975
|
+
function readSlideNotes(page) {
|
|
1976
|
+
const notes = childrenWithTag(page, "presentation:notes")[0];
|
|
1977
|
+
if (notes === void 0) return "";
|
|
1978
|
+
return elementsWithTag(notes.children, "text:p").map(decodeOdfText).join("\n");
|
|
1979
|
+
}
|
|
1980
|
+
function readSlide(page, pkg) {
|
|
1981
|
+
const shapes = [];
|
|
1982
|
+
walkDrawShapes(page.children, [], pkg, shapes);
|
|
1983
|
+
return {
|
|
1984
|
+
size: readSlideSize(page, pkg),
|
|
1985
|
+
shapes,
|
|
1986
|
+
notes: readSlideNotes(page)
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
function readOdp(pkg) {
|
|
1990
|
+
const contentPart = pkg.parts[CONTENT_PART$1];
|
|
1991
|
+
const root = contentPart?.kind === "xml" ? rootElement(contentPart.nodes) : void 0;
|
|
1992
|
+
const body = root === void 0 ? void 0 : findChildElement(root.children, "office:body");
|
|
1993
|
+
const presentation = body === void 0 ? void 0 : findChildElement(body.children, "office:presentation");
|
|
1994
|
+
const pages = presentation === void 0 ? [] : childrenWithTag(presentation, "draw:page");
|
|
1995
|
+
return {
|
|
1996
|
+
metadata: readOdfMetadata(pkg),
|
|
1997
|
+
slides: pages.map((page) => readSlide(page, pkg))
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
//#endregion
|
|
2001
|
+
//#region src/typed/odt/read.ts
|
|
2002
|
+
const CONTENT_PART = "content.xml";
|
|
2003
|
+
const STYLES_PART = "styles.xml";
|
|
2004
|
+
const AUTOMATIC_STYLE_PARTS = [CONTENT_PART, STYLES_PART];
|
|
2005
|
+
function readOutlineLevel(headingElement) {
|
|
2006
|
+
const raw = attrValue(headingElement, "text:outline-level");
|
|
2007
|
+
if (raw === void 0) return 1;
|
|
2008
|
+
const parsed = Number.parseInt(raw, 10);
|
|
2009
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 1;
|
|
2010
|
+
}
|
|
2011
|
+
function readParagraphOrHeading(element, pkg, list) {
|
|
2012
|
+
const paragraph = readOdfParagraph(element, pkg);
|
|
2013
|
+
if (element.tag === "text:h") paragraph.styleId = `Heading${readOutlineLevel(element)}`;
|
|
2014
|
+
if (list !== void 0) paragraph.list = list;
|
|
2015
|
+
return paragraph;
|
|
2016
|
+
}
|
|
2017
|
+
function readListItems(listElement, context, pkg, blocks) {
|
|
2018
|
+
for (const item of listElement.children) {
|
|
2019
|
+
if (item.type !== "element" || item.tag !== "text:list-item") continue;
|
|
2020
|
+
for (const itemChild of item.children) {
|
|
2021
|
+
if (itemChild.type !== "element") continue;
|
|
2022
|
+
if (itemChild.tag === "text:p" || itemChild.tag === "text:h") blocks.push(readParagraphOrHeading(itemChild, pkg, context));
|
|
2023
|
+
else if (itemChild.tag === "text:list") readListItems(itemChild, {
|
|
2024
|
+
numId: context.numId,
|
|
2025
|
+
level: context.level + 1
|
|
2026
|
+
}, pkg, blocks);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
function readBlocks(nodes, pkg, listIdState) {
|
|
2031
|
+
const blocks = [];
|
|
2032
|
+
for (const node of nodes) {
|
|
2033
|
+
if (node.type !== "element") continue;
|
|
2034
|
+
if (node.tag === "text:p" || node.tag === "text:h") blocks.push(readParagraphOrHeading(node, pkg, void 0));
|
|
2035
|
+
else if (node.tag === "text:list") {
|
|
2036
|
+
const numId = `list${listIdState.next}`;
|
|
2037
|
+
listIdState.next += 1;
|
|
2038
|
+
readListItems(node, {
|
|
2039
|
+
numId,
|
|
2040
|
+
level: 0
|
|
2041
|
+
}, pkg, blocks);
|
|
2042
|
+
} else if (node.tag === "table:table") blocks.push(readOdfTable(node, pkg));
|
|
2043
|
+
else if (node.tag === "text:section") blocks.push(...readBlocks(node.children, pkg, listIdState));
|
|
2044
|
+
}
|
|
2045
|
+
return blocks;
|
|
2046
|
+
}
|
|
2047
|
+
function parseKnownOdfLength(value) {
|
|
2048
|
+
const parsed = parseOdfLength(value);
|
|
2049
|
+
if (parsed === void 0) throw new Error(`readOdt: internal error -- "${value}" is not a valid ODF length literal`);
|
|
2050
|
+
return parsed;
|
|
2051
|
+
}
|
|
2052
|
+
const DEFAULT_PAGE_SIZE = document_content_model.PAGE_SIZE_A4;
|
|
2053
|
+
const DEFAULT_MARGIN_PT = parseKnownOdfLength("2cm");
|
|
2054
|
+
const DEFAULT_MARGINS = {
|
|
2055
|
+
topPt: DEFAULT_MARGIN_PT,
|
|
2056
|
+
rightPt: DEFAULT_MARGIN_PT,
|
|
2057
|
+
bottomPt: DEFAULT_MARGIN_PT,
|
|
2058
|
+
leftPt: DEFAULT_MARGIN_PT
|
|
2059
|
+
};
|
|
2060
|
+
function findPageLayoutElement(pkg, pageLayoutName) {
|
|
2061
|
+
if (pageLayoutName === void 0) return;
|
|
2062
|
+
for (const partPath of AUTOMATIC_STYLE_PARTS) {
|
|
2063
|
+
const part = pkg.parts[partPath];
|
|
2064
|
+
if (part?.kind !== "xml") continue;
|
|
2065
|
+
const root = rootElement(part.nodes);
|
|
2066
|
+
const automaticStyles = root === void 0 ? void 0 : findChildElement(root.children, "office:automatic-styles");
|
|
2067
|
+
if (automaticStyles === void 0) continue;
|
|
2068
|
+
const found = childrenWithTag(automaticStyles, "style:page-layout").find((element) => attrValue(element, "style:name") === pageLayoutName);
|
|
2069
|
+
if (found !== void 0) return found;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
function readFirstMasterPageGeometry(pkg) {
|
|
2073
|
+
const stylesPart = pkg.parts[STYLES_PART];
|
|
2074
|
+
const stylesRoot = stylesPart?.kind === "xml" ? rootElement(stylesPart.nodes) : void 0;
|
|
2075
|
+
const masterStyles = stylesRoot === void 0 ? void 0 : findChildElement(stylesRoot.children, "office:master-styles");
|
|
2076
|
+
const masterPage = masterStyles === void 0 ? void 0 : findChildElement(masterStyles.children, "style:master-page");
|
|
2077
|
+
const layout = findPageLayoutElement(pkg, masterPage === void 0 ? void 0 : attrValue(masterPage, "style:page-layout-name"));
|
|
2078
|
+
const properties = layout === void 0 ? void 0 : findChildElement(layout.children, "style:page-layout-properties");
|
|
2079
|
+
const pageSize = properties === void 0 ? void 0 : parsePageSize(properties);
|
|
2080
|
+
const margins = properties === void 0 ? void 0 : parseMargins(properties);
|
|
2081
|
+
return {
|
|
2082
|
+
pageSize: pageSize ?? DEFAULT_PAGE_SIZE,
|
|
2083
|
+
margins: margins ?? DEFAULT_MARGINS
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
function readOdt(pkg) {
|
|
2087
|
+
const contentPart = pkg.parts[CONTENT_PART];
|
|
2088
|
+
if (contentPart?.kind !== "xml") throw new Error(`readOdt: package has no ${CONTENT_PART} part`);
|
|
2089
|
+
const contentRoot = rootElement(contentPart.nodes);
|
|
2090
|
+
const body = contentRoot === void 0 ? void 0 : findChildElement(contentRoot.children, "office:body");
|
|
2091
|
+
const textElement = body === void 0 ? void 0 : findChildElement(body.children, "office:text");
|
|
2092
|
+
if (textElement === void 0) throw new Error(`readOdt: ${CONTENT_PART} has no office:body/office:text element`);
|
|
2093
|
+
const metadata = readOdfMetadata(pkg);
|
|
2094
|
+
const { pageSize, margins } = readFirstMasterPageGeometry(pkg);
|
|
2095
|
+
return {
|
|
2096
|
+
metadata,
|
|
2097
|
+
sections: [{
|
|
2098
|
+
pageSize,
|
|
2099
|
+
margins,
|
|
2100
|
+
blocks: readBlocks(textElement.children, pkg, { next: 1 })
|
|
2101
|
+
}]
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
//#endregion
|
|
1606
2105
|
Object.defineProperty(exports, "AlignmentSchema", {
|
|
1607
2106
|
enumerable: true,
|
|
1608
2107
|
get: function() {
|
|
@@ -1633,6 +2132,7 @@ exports.XmlNodeSchema = XmlNodeSchema;
|
|
|
1633
2132
|
exports.XmlPartSchema = XmlPartSchema;
|
|
1634
2133
|
exports.XmlPiSchema = XmlPiSchema;
|
|
1635
2134
|
exports.XmlTextSchema = XmlTextSchema;
|
|
2135
|
+
exports.applyOdfTransform = applyOdfTransform;
|
|
1636
2136
|
exports.attrValue = attrValue;
|
|
1637
2137
|
exports.base64ToBytes = base64ToBytes;
|
|
1638
2138
|
exports.buildManifest = buildManifest;
|
|
@@ -1643,14 +2143,17 @@ exports.canonicalPropertiesString = canonicalPropertiesString;
|
|
|
1643
2143
|
exports.cellReference = cellReference;
|
|
1644
2144
|
exports.childrenWithTag = childrenWithTag;
|
|
1645
2145
|
exports.columnIndexToLetters = columnIndexToLetters;
|
|
2146
|
+
exports.composeOdfGroupTransform = composeOdfGroupTransform;
|
|
1646
2147
|
exports.decodeOdfText = decodeOdfText;
|
|
1647
2148
|
exports.decodePackage = decodePackage;
|
|
1648
2149
|
exports.decodeXmlText = decodeXmlText;
|
|
1649
2150
|
exports.el = el;
|
|
2151
|
+
exports.elementsWithTag = elementsWithTag;
|
|
1650
2152
|
exports.encodePackage = encodePackage;
|
|
1651
2153
|
exports.encodeXmlText = encodeXmlText;
|
|
1652
2154
|
exports.ensureSpan = ensureSpan;
|
|
1653
2155
|
exports.findChildElement = findChildElement;
|
|
2156
|
+
exports.findStyleElement = findStyleElement;
|
|
1654
2157
|
exports.formatOdfColor = formatOdfColor;
|
|
1655
2158
|
exports.formatOdfLength = formatOdfLength;
|
|
1656
2159
|
exports.formatPercentageMultiplier = formatPercentageMultiplier;
|
|
@@ -1660,6 +2163,7 @@ exports.isStyleFamily = isStyleFamily;
|
|
|
1660
2163
|
exports.isXmlNode = isXmlNode;
|
|
1661
2164
|
exports.measureOdfNodeLength = measureOdfNodeLength;
|
|
1662
2165
|
exports.mediaTypeForExtension = mediaTypeForExtension;
|
|
2166
|
+
exports.netRotationDeg = netRotationDeg;
|
|
1663
2167
|
exports.packageCodec = packageCodec;
|
|
1664
2168
|
exports.paragraphPropertiesToAttributes = paragraphPropertiesToAttributes;
|
|
1665
2169
|
exports.parseBox = parseBox;
|
|
@@ -1667,16 +2171,24 @@ exports.parseLength = parseLength;
|
|
|
1667
2171
|
exports.parseMargins = parseMargins;
|
|
1668
2172
|
exports.parseOdfColor = parseOdfColor;
|
|
1669
2173
|
exports.parseOdfLength = parseOdfLength;
|
|
2174
|
+
exports.parseOdfTransform = parseOdfTransform;
|
|
1670
2175
|
exports.parsePackage = parsePackage;
|
|
1671
2176
|
exports.parsePageSize = parsePageSize;
|
|
1672
2177
|
exports.parseParagraphProperties = parseParagraphProperties;
|
|
1673
2178
|
exports.parseStyleElementProperties = parseStyleElementProperties;
|
|
1674
2179
|
exports.parseTextProperties = parseTextProperties;
|
|
1675
2180
|
exports.parseXml = parseXml;
|
|
2181
|
+
exports.readDrawFrame = readDrawFrame;
|
|
1676
2182
|
exports.readManifest = readManifest;
|
|
1677
2183
|
exports.readMimetype = readMimetype;
|
|
1678
2184
|
exports.readOdfMetadata = readOdfMetadata;
|
|
2185
|
+
exports.readOdfParagraph = readOdfParagraph;
|
|
2186
|
+
exports.readOdfTable = readOdfTable;
|
|
2187
|
+
exports.readOdp = readOdp;
|
|
2188
|
+
exports.readOdt = readOdt;
|
|
2189
|
+
exports.resolveOdfShapeGeometry = resolveOdfShapeGeometry;
|
|
1679
2190
|
exports.resolveStyle = resolveStyle;
|
|
2191
|
+
exports.resolveStyleElementChain = resolveStyleElementChain;
|
|
1680
2192
|
exports.rootElement = rootElement;
|
|
1681
2193
|
exports.serializePackage = serializePackage;
|
|
1682
2194
|
exports.setDocumentMediaType = setDocumentMediaType;
|
|
@@ -1687,6 +2199,7 @@ exports.textPropertiesToAttributes = textPropertiesToAttributes;
|
|
|
1687
2199
|
exports.txt = txt;
|
|
1688
2200
|
exports.unzipPackage = unzipPackage;
|
|
1689
2201
|
exports.validateManifest = validateManifest;
|
|
2202
|
+
exports.walkDrawShapes = walkDrawShapes;
|
|
1690
2203
|
exports.writeManifest = writeManifest;
|
|
1691
2204
|
exports.writeMimetype = writeMimetype;
|
|
1692
2205
|
exports.xmlCodec = xmlCodec;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { Alignment, AlignmentSchema, Box, Color, LayoutMetadata, Margins, PageSize } from "document-content-model";
|
|
2
|
+
import { Alignment, AlignmentSchema, Box, Color, ContentParagraph, ContentSection, ContentShape, ContentSlide, ContentTable, LayoutMetadata, Margins, PageSize } from "document-content-model";
|
|
3
3
|
//#region src/model/node.d.ts
|
|
4
4
|
declare const AttributeSchema: z.ZodObject<{
|
|
5
5
|
name: z.ZodString;
|
|
@@ -480,6 +480,7 @@ declare function ensureSpan(paragraph: XmlElement, start: number, end: number, s
|
|
|
480
480
|
declare function rootElement(nodes: readonly XmlNode[]): XmlElement | undefined;
|
|
481
481
|
declare function findChildElement(nodes: readonly XmlNode[], tag: string): XmlElement | undefined;
|
|
482
482
|
declare function childrenWithTag(element: XmlElement, tag: string): XmlElement[];
|
|
483
|
+
declare function elementsWithTag(nodes: readonly XmlNode[], tag: string): XmlElement[];
|
|
483
484
|
declare function attrValue(element: XmlElement, name: string): string | undefined;
|
|
484
485
|
//#endregion
|
|
485
486
|
//#region src/typed/shared/a1.d.ts
|
|
@@ -518,10 +519,63 @@ interface StyleCascadeResult {
|
|
|
518
519
|
properties: StyleProperties;
|
|
519
520
|
diagnostics: CascadeDiagnostic[];
|
|
520
521
|
}
|
|
522
|
+
interface StyleElementChainResult {
|
|
523
|
+
elements: XmlElement[];
|
|
524
|
+
diagnostics: CascadeDiagnostic[];
|
|
525
|
+
}
|
|
526
|
+
declare function resolveStyleElementChain(styleName: string | undefined, family: StyleFamily, pkg: Package): StyleElementChainResult;
|
|
521
527
|
declare function resolveStyle(styleName: string | undefined, family: StyleFamily, pkg: Package): StyleCascadeResult;
|
|
528
|
+
declare function findStyleElement(styleName: string, family: StyleFamily, pkg: Package): XmlElement | undefined;
|
|
522
529
|
//#endregion
|
|
523
530
|
//#region src/typed/shared/metadata.d.ts
|
|
524
531
|
declare const META_PART = "meta.xml";
|
|
525
532
|
declare function readOdfMetadata(pkg: Package): LayoutMetadata;
|
|
526
533
|
//#endregion
|
|
527
|
-
|
|
534
|
+
//#region src/typed/shared/paragraph.d.ts
|
|
535
|
+
declare function readOdfParagraph(pElement: XmlElement, pkg: Package): ContentParagraph;
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/typed/shared/table.d.ts
|
|
538
|
+
declare function readOdfTable(tableElement: XmlElement, pkg: Package): ContentTable;
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/typed/shared/transform.d.ts
|
|
541
|
+
type OdfTransformFunction = {
|
|
542
|
+
readonly kind: 'rotate';
|
|
543
|
+
readonly angleRad: number;
|
|
544
|
+
} | {
|
|
545
|
+
readonly kind: 'translate';
|
|
546
|
+
readonly xPt: number;
|
|
547
|
+
readonly yPt: number;
|
|
548
|
+
};
|
|
549
|
+
interface OdfPoint {
|
|
550
|
+
readonly xPt: number;
|
|
551
|
+
readonly yPt: number;
|
|
552
|
+
}
|
|
553
|
+
declare function parseOdfTransform(value: string): OdfTransformFunction[];
|
|
554
|
+
declare function applyOdfTransform(functions: readonly OdfTransformFunction[], point: OdfPoint): OdfPoint;
|
|
555
|
+
declare function netRotationDeg(functions: readonly OdfTransformFunction[]): number;
|
|
556
|
+
interface OdfShapeGeometry {
|
|
557
|
+
readonly frame: Box;
|
|
558
|
+
readonly rotationDeg: number | undefined;
|
|
559
|
+
}
|
|
560
|
+
declare function resolveOdfShapeGeometry(element: XmlElement): OdfShapeGeometry | undefined;
|
|
561
|
+
declare function composeOdfGroupTransform(groupFunctions: readonly OdfTransformFunction[], child: OdfShapeGeometry): OdfShapeGeometry;
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/typed/draw/shapes.d.ts
|
|
564
|
+
declare function readDrawFrame(frame: XmlElement, groupFunctions: readonly OdfTransformFunction[], pkg: Package): ContentShape | undefined;
|
|
565
|
+
declare function walkDrawShapes(children: readonly XmlNode[], groupFunctions: readonly OdfTransformFunction[], pkg: Package, out: ContentShape[]): void;
|
|
566
|
+
//#endregion
|
|
567
|
+
//#region src/typed/odp/read.d.ts
|
|
568
|
+
interface OdpDocument {
|
|
569
|
+
metadata: LayoutMetadata;
|
|
570
|
+
slides: ContentSlide[];
|
|
571
|
+
}
|
|
572
|
+
declare function readOdp(pkg: Package): OdpDocument;
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/typed/odt/read.d.ts
|
|
575
|
+
interface OdtDocument {
|
|
576
|
+
metadata: LayoutMetadata;
|
|
577
|
+
sections: ContentSection[];
|
|
578
|
+
}
|
|
579
|
+
declare function readOdt(pkg: Package): OdtDocument;
|
|
580
|
+
//#endregion
|
|
581
|
+
export { type Alignment, AlignmentSchema, type Attribute, AttributeSchema, type BinaryPart, BinaryPartSchema, type BuildManifestOptions, type CascadeDiagnostic, type ImageFormat, type InternRequest, type LengthUnit, MANIFEST_PART, META_PART, MIMETYPE_PART, type Manifest, type ManifestEntry, ManifestEntrySchema, type ManifestProblem, ManifestProblemSchema, ManifestSchema, ODF_MEDIA_TYPES, ODF_NAMESPACES, type OdfExtension, type OdfNamespacePrefix, type OdfPoint, type OdfShapeGeometry, type OdfTransformFunction, type OdpDocument, type OdtDocument, type OtherPartRef, type Package, PackageSchema, type ParsedProperties, type Part, PartSchema, STYLE_FAMILIES, type StyleCascadeResult, type StyleElementChainResult, type StyleFamily, type StyleProperties, StylePropertiesSchema, StyleRegistry, type StyleRegistryOptions, TableCursor, type XmlCdata, XmlCdataSchema, type XmlComment, XmlCommentSchema, type XmlDeclaration, XmlDeclarationSchema, type XmlElement, XmlElementSchema, type XmlNode, XmlNodeSchema, type XmlPart, XmlPartSchema, type XmlPi, XmlPiSchema, type XmlText, XmlTextSchema, type ZipEntry, applyOdfTransform, attrValue, base64ToBytes, buildManifest, buildStylePropertyElements, buildXml, bytesToBase64, canonicalPropertiesString, cellReference, childrenWithTag, columnIndexToLetters, composeOdfGroupTransform, decodeOdfText, decodePackage, decodeXmlText, el, elementsWithTag, encodePackage, encodeXmlText, ensureSpan, findChildElement, findStyleElement, formatOdfColor, formatOdfLength, formatPercentageMultiplier, formatPt, getOdfSpaceCount, isStyleFamily, isXmlNode, measureOdfNodeLength, mediaTypeForExtension, netRotationDeg, packageCodec, paragraphPropertiesToAttributes, parseBox, parseLength, parseMargins, parseOdfColor, parseOdfLength, parseOdfTransform, parsePackage, parsePageSize, parseParagraphProperties, parseStyleElementProperties, parseTextProperties, parseXml, readDrawFrame, readManifest, readMimetype, readOdfMetadata, readOdfParagraph, readOdfTable, readOdp, readOdt, resolveOdfShapeGeometry, resolveStyle, resolveStyleElementChain, rootElement, serializePackage, setDocumentMediaType, sniffImageFormat, sumOdfNodeLength, syncManifest, textPropertiesToAttributes, txt, unzipPackage, validateManifest, walkDrawShapes, writeManifest, writeMimetype, xmlCodec, xmlnsAttributes, zipPackage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { Alignment, AlignmentSchema, Box, Color, LayoutMetadata, Margins, PageSize } from "document-content-model";
|
|
2
|
+
import { Alignment, AlignmentSchema, Box, Color, ContentParagraph, ContentSection, ContentShape, ContentSlide, ContentTable, LayoutMetadata, Margins, PageSize } from "document-content-model";
|
|
3
3
|
//#region src/model/node.d.ts
|
|
4
4
|
declare const AttributeSchema: z.ZodObject<{
|
|
5
5
|
name: z.ZodString;
|
|
@@ -480,6 +480,7 @@ declare function ensureSpan(paragraph: XmlElement, start: number, end: number, s
|
|
|
480
480
|
declare function rootElement(nodes: readonly XmlNode[]): XmlElement | undefined;
|
|
481
481
|
declare function findChildElement(nodes: readonly XmlNode[], tag: string): XmlElement | undefined;
|
|
482
482
|
declare function childrenWithTag(element: XmlElement, tag: string): XmlElement[];
|
|
483
|
+
declare function elementsWithTag(nodes: readonly XmlNode[], tag: string): XmlElement[];
|
|
483
484
|
declare function attrValue(element: XmlElement, name: string): string | undefined;
|
|
484
485
|
//#endregion
|
|
485
486
|
//#region src/typed/shared/a1.d.ts
|
|
@@ -518,10 +519,63 @@ interface StyleCascadeResult {
|
|
|
518
519
|
properties: StyleProperties;
|
|
519
520
|
diagnostics: CascadeDiagnostic[];
|
|
520
521
|
}
|
|
522
|
+
interface StyleElementChainResult {
|
|
523
|
+
elements: XmlElement[];
|
|
524
|
+
diagnostics: CascadeDiagnostic[];
|
|
525
|
+
}
|
|
526
|
+
declare function resolveStyleElementChain(styleName: string | undefined, family: StyleFamily, pkg: Package): StyleElementChainResult;
|
|
521
527
|
declare function resolveStyle(styleName: string | undefined, family: StyleFamily, pkg: Package): StyleCascadeResult;
|
|
528
|
+
declare function findStyleElement(styleName: string, family: StyleFamily, pkg: Package): XmlElement | undefined;
|
|
522
529
|
//#endregion
|
|
523
530
|
//#region src/typed/shared/metadata.d.ts
|
|
524
531
|
declare const META_PART = "meta.xml";
|
|
525
532
|
declare function readOdfMetadata(pkg: Package): LayoutMetadata;
|
|
526
533
|
//#endregion
|
|
527
|
-
|
|
534
|
+
//#region src/typed/shared/paragraph.d.ts
|
|
535
|
+
declare function readOdfParagraph(pElement: XmlElement, pkg: Package): ContentParagraph;
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/typed/shared/table.d.ts
|
|
538
|
+
declare function readOdfTable(tableElement: XmlElement, pkg: Package): ContentTable;
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/typed/shared/transform.d.ts
|
|
541
|
+
type OdfTransformFunction = {
|
|
542
|
+
readonly kind: 'rotate';
|
|
543
|
+
readonly angleRad: number;
|
|
544
|
+
} | {
|
|
545
|
+
readonly kind: 'translate';
|
|
546
|
+
readonly xPt: number;
|
|
547
|
+
readonly yPt: number;
|
|
548
|
+
};
|
|
549
|
+
interface OdfPoint {
|
|
550
|
+
readonly xPt: number;
|
|
551
|
+
readonly yPt: number;
|
|
552
|
+
}
|
|
553
|
+
declare function parseOdfTransform(value: string): OdfTransformFunction[];
|
|
554
|
+
declare function applyOdfTransform(functions: readonly OdfTransformFunction[], point: OdfPoint): OdfPoint;
|
|
555
|
+
declare function netRotationDeg(functions: readonly OdfTransformFunction[]): number;
|
|
556
|
+
interface OdfShapeGeometry {
|
|
557
|
+
readonly frame: Box;
|
|
558
|
+
readonly rotationDeg: number | undefined;
|
|
559
|
+
}
|
|
560
|
+
declare function resolveOdfShapeGeometry(element: XmlElement): OdfShapeGeometry | undefined;
|
|
561
|
+
declare function composeOdfGroupTransform(groupFunctions: readonly OdfTransformFunction[], child: OdfShapeGeometry): OdfShapeGeometry;
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/typed/draw/shapes.d.ts
|
|
564
|
+
declare function readDrawFrame(frame: XmlElement, groupFunctions: readonly OdfTransformFunction[], pkg: Package): ContentShape | undefined;
|
|
565
|
+
declare function walkDrawShapes(children: readonly XmlNode[], groupFunctions: readonly OdfTransformFunction[], pkg: Package, out: ContentShape[]): void;
|
|
566
|
+
//#endregion
|
|
567
|
+
//#region src/typed/odp/read.d.ts
|
|
568
|
+
interface OdpDocument {
|
|
569
|
+
metadata: LayoutMetadata;
|
|
570
|
+
slides: ContentSlide[];
|
|
571
|
+
}
|
|
572
|
+
declare function readOdp(pkg: Package): OdpDocument;
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/typed/odt/read.d.ts
|
|
575
|
+
interface OdtDocument {
|
|
576
|
+
metadata: LayoutMetadata;
|
|
577
|
+
sections: ContentSection[];
|
|
578
|
+
}
|
|
579
|
+
declare function readOdt(pkg: Package): OdtDocument;
|
|
580
|
+
//#endregion
|
|
581
|
+
export { type Alignment, AlignmentSchema, type Attribute, AttributeSchema, type BinaryPart, BinaryPartSchema, type BuildManifestOptions, type CascadeDiagnostic, type ImageFormat, type InternRequest, type LengthUnit, MANIFEST_PART, META_PART, MIMETYPE_PART, type Manifest, type ManifestEntry, ManifestEntrySchema, type ManifestProblem, ManifestProblemSchema, ManifestSchema, ODF_MEDIA_TYPES, ODF_NAMESPACES, type OdfExtension, type OdfNamespacePrefix, type OdfPoint, type OdfShapeGeometry, type OdfTransformFunction, type OdpDocument, type OdtDocument, type OtherPartRef, type Package, PackageSchema, type ParsedProperties, type Part, PartSchema, STYLE_FAMILIES, type StyleCascadeResult, type StyleElementChainResult, type StyleFamily, type StyleProperties, StylePropertiesSchema, StyleRegistry, type StyleRegistryOptions, TableCursor, type XmlCdata, XmlCdataSchema, type XmlComment, XmlCommentSchema, type XmlDeclaration, XmlDeclarationSchema, type XmlElement, XmlElementSchema, type XmlNode, XmlNodeSchema, type XmlPart, XmlPartSchema, type XmlPi, XmlPiSchema, type XmlText, XmlTextSchema, type ZipEntry, applyOdfTransform, attrValue, base64ToBytes, buildManifest, buildStylePropertyElements, buildXml, bytesToBase64, canonicalPropertiesString, cellReference, childrenWithTag, columnIndexToLetters, composeOdfGroupTransform, decodeOdfText, decodePackage, decodeXmlText, el, elementsWithTag, encodePackage, encodeXmlText, ensureSpan, findChildElement, findStyleElement, formatOdfColor, formatOdfLength, formatPercentageMultiplier, formatPt, getOdfSpaceCount, isStyleFamily, isXmlNode, measureOdfNodeLength, mediaTypeForExtension, netRotationDeg, packageCodec, paragraphPropertiesToAttributes, parseBox, parseLength, parseMargins, parseOdfColor, parseOdfLength, parseOdfTransform, parsePackage, parsePageSize, parseParagraphProperties, parseStyleElementProperties, parseTextProperties, parseXml, readDrawFrame, readManifest, readMimetype, readOdfMetadata, readOdfParagraph, readOdfTable, readOdp, readOdt, resolveOdfShapeGeometry, resolveStyle, resolveStyleElementChain, rootElement, serializePackage, setDocumentMediaType, sniffImageFormat, sumOdfNodeLength, syncManifest, textPropertiesToAttributes, txt, unzipPackage, validateManifest, walkDrawShapes, writeManifest, writeMimetype, xmlCodec, xmlnsAttributes, zipPackage };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
3
3
|
import { unzipSync, zipSync } from "fflate";
|
|
4
|
-
import { AlignmentSchema, AlignmentSchema as AlignmentSchema$1, ColorSchema, colorToRgbHex, rgbHexToColor } from "document-content-model";
|
|
4
|
+
import { AlignmentSchema, AlignmentSchema as AlignmentSchema$1, ColorSchema, PAGE_SIZE_A4, SLIDE_SIZE_WIDESCREEN, colorToRgbHex, rgbHexToColor } from "document-content-model";
|
|
5
5
|
//#region src/model/node.ts
|
|
6
6
|
const AttributeSchema = z.object({
|
|
7
7
|
name: z.string(),
|
|
@@ -1242,6 +1242,17 @@ function childrenWithTag(element, tag) {
|
|
|
1242
1242
|
for (const child of element.children) if (child.type === "element" && child.tag === tag) out.push(child);
|
|
1243
1243
|
return out;
|
|
1244
1244
|
}
|
|
1245
|
+
function* walk(nodes) {
|
|
1246
|
+
for (const node of nodes) {
|
|
1247
|
+
yield node;
|
|
1248
|
+
if (node.type === "element") yield* walk(node.children);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
function elementsWithTag(nodes, tag) {
|
|
1252
|
+
const out = [];
|
|
1253
|
+
for (const node of walk(nodes)) if (node.type === "element" && node.tag === tag) out.push(node);
|
|
1254
|
+
return out;
|
|
1255
|
+
}
|
|
1245
1256
|
function attrValue(element, name) {
|
|
1246
1257
|
return element.attributes.find((attribute) => attribute.name === name)?.value;
|
|
1247
1258
|
}
|
|
@@ -1521,13 +1532,14 @@ function collectStyles(pkg) {
|
|
|
1521
1532
|
defaultByFamily
|
|
1522
1533
|
};
|
|
1523
1534
|
}
|
|
1524
|
-
function
|
|
1535
|
+
function resolveStyleElementChain(styleName, family, pkg) {
|
|
1525
1536
|
const { byName, defaultByFamily } = collectStyles(pkg);
|
|
1526
1537
|
const diagnostics = [];
|
|
1538
|
+
const elements = [];
|
|
1527
1539
|
const defaultElement = defaultByFamily.get(family);
|
|
1528
|
-
|
|
1540
|
+
if (defaultElement !== void 0) elements.push(defaultElement);
|
|
1529
1541
|
if (styleName === void 0) return {
|
|
1530
|
-
|
|
1542
|
+
elements,
|
|
1531
1543
|
diagnostics
|
|
1532
1544
|
};
|
|
1533
1545
|
const chain = [];
|
|
@@ -1555,7 +1567,16 @@ function resolveStyle(styleName, family, pkg) {
|
|
|
1555
1567
|
currentName = attrValue(element, "style:parent-style-name");
|
|
1556
1568
|
}
|
|
1557
1569
|
chain.reverse();
|
|
1558
|
-
|
|
1570
|
+
elements.push(...chain);
|
|
1571
|
+
return {
|
|
1572
|
+
elements,
|
|
1573
|
+
diagnostics
|
|
1574
|
+
};
|
|
1575
|
+
}
|
|
1576
|
+
function resolveStyle(styleName, family, pkg) {
|
|
1577
|
+
const { elements, diagnostics } = resolveStyleElementChain(styleName, family, pkg);
|
|
1578
|
+
let properties = {};
|
|
1579
|
+
for (const element of elements) properties = {
|
|
1559
1580
|
...properties,
|
|
1560
1581
|
...parseStyleElementProperties(element).properties
|
|
1561
1582
|
};
|
|
@@ -1564,6 +1585,10 @@ function resolveStyle(styleName, family, pkg) {
|
|
|
1564
1585
|
diagnostics
|
|
1565
1586
|
};
|
|
1566
1587
|
}
|
|
1588
|
+
function findStyleElement(styleName, family, pkg) {
|
|
1589
|
+
const { byName } = collectStyles(pkg);
|
|
1590
|
+
return byName.get(nameKey(family, styleName));
|
|
1591
|
+
}
|
|
1567
1592
|
//#endregion
|
|
1568
1593
|
//#region src/typed/shared/metadata.ts
|
|
1569
1594
|
const META_PART = "meta.xml";
|
|
@@ -1602,4 +1627,478 @@ function readOdfMetadata(pkg) {
|
|
|
1602
1627
|
return metadata;
|
|
1603
1628
|
}
|
|
1604
1629
|
//#endregion
|
|
1605
|
-
|
|
1630
|
+
//#region src/typed/shared/paragraph.ts
|
|
1631
|
+
function collectRuns(nodes, baseProperties, pkg, out) {
|
|
1632
|
+
for (const node of nodes) {
|
|
1633
|
+
if (node.type === "text") {
|
|
1634
|
+
if (node.value.length > 0) out.push(runFromText(decodeXmlText(node.value), baseProperties));
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
if (node.type !== "element") continue;
|
|
1638
|
+
if (node.tag === "text:s") out.push(runFromText(" ".repeat(getOdfSpaceCount(node)), baseProperties));
|
|
1639
|
+
else if (node.tag === "text:tab") out.push(runFromText(" ", baseProperties));
|
|
1640
|
+
else if (node.tag === "text:line-break") out.push(runFromText("\n", baseProperties));
|
|
1641
|
+
else if (node.tag === "text:span") {
|
|
1642
|
+
const styleName = attrValue(node, "text:style-name");
|
|
1643
|
+
const spanProperties = {
|
|
1644
|
+
...baseProperties,
|
|
1645
|
+
...resolveStyle(styleName, "text", pkg).properties
|
|
1646
|
+
};
|
|
1647
|
+
collectRuns(node.children, spanProperties, pkg, out);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
function runFromText(text, properties) {
|
|
1652
|
+
return {
|
|
1653
|
+
text,
|
|
1654
|
+
bold: properties.bold,
|
|
1655
|
+
italic: properties.italic,
|
|
1656
|
+
underline: properties.underline,
|
|
1657
|
+
strike: properties.strike,
|
|
1658
|
+
fontFamily: properties.fontFamily,
|
|
1659
|
+
sizePt: properties.sizePt,
|
|
1660
|
+
color: properties.color
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
function readOdfParagraph(pElement, pkg) {
|
|
1664
|
+
const styleName = attrValue(pElement, "text:style-name");
|
|
1665
|
+
const paragraphProperties = resolveStyle(styleName, "paragraph", pkg).properties;
|
|
1666
|
+
const runs = [];
|
|
1667
|
+
collectRuns(pElement.children, paragraphProperties, pkg, runs);
|
|
1668
|
+
return {
|
|
1669
|
+
kind: "paragraph",
|
|
1670
|
+
runs,
|
|
1671
|
+
styleId: styleName,
|
|
1672
|
+
alignment: paragraphProperties.alignment,
|
|
1673
|
+
spacingBeforePt: paragraphProperties.spacingBeforePt,
|
|
1674
|
+
spacingAfterPt: paragraphProperties.spacingAfterPt,
|
|
1675
|
+
lineSpacing: paragraphProperties.lineSpacing,
|
|
1676
|
+
indentLeftPt: paragraphProperties.indentLeftPt,
|
|
1677
|
+
indentFirstLinePt: paragraphProperties.indentFirstLinePt
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
//#endregion
|
|
1681
|
+
//#region src/typed/shared/table.ts
|
|
1682
|
+
function readRepeatCount(element, attrName) {
|
|
1683
|
+
const raw = attrValue(element, attrName);
|
|
1684
|
+
if (raw === void 0) return 1;
|
|
1685
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1686
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 1;
|
|
1687
|
+
}
|
|
1688
|
+
function resolveColumnWidthPt(columnElement, pkg) {
|
|
1689
|
+
const styleName = attrValue(columnElement, "table:style-name");
|
|
1690
|
+
const styleElement = styleName === void 0 ? void 0 : findStyleElement(styleName, "table-column", pkg);
|
|
1691
|
+
const props = styleElement === void 0 ? void 0 : childrenWithTag(styleElement, "style:table-column-properties")[0];
|
|
1692
|
+
const widthValue = props === void 0 ? void 0 : attrValue(props, "style:column-width");
|
|
1693
|
+
return widthValue === void 0 ? 0 : parseOdfLength(widthValue) ?? 0;
|
|
1694
|
+
}
|
|
1695
|
+
function resolveRowHeightPt(rowElement, pkg) {
|
|
1696
|
+
const styleName = attrValue(rowElement, "table:style-name");
|
|
1697
|
+
const styleElement = styleName === void 0 ? void 0 : findStyleElement(styleName, "table-row", pkg);
|
|
1698
|
+
const props = styleElement === void 0 ? void 0 : childrenWithTag(styleElement, "style:table-row-properties")[0];
|
|
1699
|
+
const heightValue = props === void 0 ? void 0 : attrValue(props, "style:row-height");
|
|
1700
|
+
return heightValue === void 0 ? void 0 : parseOdfLength(heightValue);
|
|
1701
|
+
}
|
|
1702
|
+
function readTableCellBackground(cellElement, pkg) {
|
|
1703
|
+
const styleName = attrValue(cellElement, "table:style-name");
|
|
1704
|
+
const styleElement = styleName === void 0 ? void 0 : findStyleElement(styleName, "table-cell", pkg);
|
|
1705
|
+
const props = styleElement === void 0 ? void 0 : childrenWithTag(styleElement, "style:table-cell-properties")[0];
|
|
1706
|
+
const value = props === void 0 ? void 0 : attrValue(props, "fo:background-color");
|
|
1707
|
+
return value === void 0 ? void 0 : parseOdfColor(value);
|
|
1708
|
+
}
|
|
1709
|
+
function readTableCell(cellElement, pkg) {
|
|
1710
|
+
const blocks = childrenWithTag(cellElement, "text:p").map((p) => readOdfParagraph(p, pkg));
|
|
1711
|
+
const colSpanRaw = attrValue(cellElement, "table:number-columns-spanned");
|
|
1712
|
+
const rowSpanRaw = attrValue(cellElement, "table:number-rows-spanned");
|
|
1713
|
+
return {
|
|
1714
|
+
blocks,
|
|
1715
|
+
colSpan: colSpanRaw === void 0 ? void 0 : Number.parseInt(colSpanRaw, 10),
|
|
1716
|
+
rowSpan: rowSpanRaw === void 0 ? void 0 : Number.parseInt(rowSpanRaw, 10),
|
|
1717
|
+
background: readTableCellBackground(cellElement, pkg)
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
function readTableRow(rowElement, pkg) {
|
|
1721
|
+
const cells = [];
|
|
1722
|
+
for (const child of rowElement.children) {
|
|
1723
|
+
if (child.type !== "element") continue;
|
|
1724
|
+
if (child.tag === "table:covered-table-cell") {
|
|
1725
|
+
const repeat = readRepeatCount(child, "table:number-columns-repeated");
|
|
1726
|
+
for (let i = 0; i < repeat; i++) cells.push({ blocks: [] });
|
|
1727
|
+
} else if (child.tag === "table:table-cell") {
|
|
1728
|
+
const cell = readTableCell(child, pkg);
|
|
1729
|
+
const repeat = readRepeatCount(child, "table:number-columns-repeated");
|
|
1730
|
+
for (let i = 0; i < repeat; i++) cells.push(cell);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
return {
|
|
1734
|
+
cells,
|
|
1735
|
+
heightPt: resolveRowHeightPt(rowElement, pkg)
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
function readOdfTable(tableElement, pkg) {
|
|
1739
|
+
const columnWidthsPt = [];
|
|
1740
|
+
for (const column of childrenWithTag(tableElement, "table:table-column")) {
|
|
1741
|
+
const widthPt = resolveColumnWidthPt(column, pkg);
|
|
1742
|
+
const repeat = readRepeatCount(column, "table:number-columns-repeated");
|
|
1743
|
+
for (let i = 0; i < repeat; i++) columnWidthsPt.push(widthPt);
|
|
1744
|
+
}
|
|
1745
|
+
const rows = [];
|
|
1746
|
+
for (const rowElement of childrenWithTag(tableElement, "table:table-row")) {
|
|
1747
|
+
const row = readTableRow(rowElement, pkg);
|
|
1748
|
+
const repeat = readRepeatCount(rowElement, "table:number-rows-repeated");
|
|
1749
|
+
for (let i = 0; i < repeat; i++) rows.push(row);
|
|
1750
|
+
}
|
|
1751
|
+
return {
|
|
1752
|
+
kind: "table",
|
|
1753
|
+
rows,
|
|
1754
|
+
columnWidthsPt
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
//#endregion
|
|
1758
|
+
//#region src/typed/shared/transform.ts
|
|
1759
|
+
const FUNCTION_PATTERN = /([a-zA-Z]+)\s*\(\s*([^)]*?)\s*\)/g;
|
|
1760
|
+
function parseOdfTransform(value) {
|
|
1761
|
+
const functions = [];
|
|
1762
|
+
for (const match of value.matchAll(FUNCTION_PATTERN)) {
|
|
1763
|
+
const name = match[1];
|
|
1764
|
+
const argsRaw = match[2];
|
|
1765
|
+
if (name === void 0 || argsRaw === void 0) continue;
|
|
1766
|
+
const args = argsRaw.split(/\s+/).filter((arg) => arg.length > 0);
|
|
1767
|
+
if (name === "rotate") {
|
|
1768
|
+
const angleArg = args[0];
|
|
1769
|
+
if (angleArg === void 0) continue;
|
|
1770
|
+
const angleRad = Number(angleArg);
|
|
1771
|
+
if (!Number.isFinite(angleRad)) continue;
|
|
1772
|
+
functions.push({
|
|
1773
|
+
kind: "rotate",
|
|
1774
|
+
angleRad
|
|
1775
|
+
});
|
|
1776
|
+
} else if (name === "translate") {
|
|
1777
|
+
const xArg = args[0];
|
|
1778
|
+
if (xArg === void 0) continue;
|
|
1779
|
+
const yArg = args[1];
|
|
1780
|
+
const xPt = parseOdfLength(xArg);
|
|
1781
|
+
const yPt = yArg === void 0 ? 0 : parseOdfLength(yArg);
|
|
1782
|
+
if (xPt === void 0 || yPt === void 0) continue;
|
|
1783
|
+
functions.push({
|
|
1784
|
+
kind: "translate",
|
|
1785
|
+
xPt,
|
|
1786
|
+
yPt
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
return functions;
|
|
1791
|
+
}
|
|
1792
|
+
function applyOdfTransform(functions, point) {
|
|
1793
|
+
let current = point;
|
|
1794
|
+
for (const fn of functions) if (fn.kind === "rotate") {
|
|
1795
|
+
const cos = Math.cos(fn.angleRad);
|
|
1796
|
+
const sin = Math.sin(fn.angleRad);
|
|
1797
|
+
current = {
|
|
1798
|
+
xPt: current.xPt * cos + current.yPt * sin,
|
|
1799
|
+
yPt: current.yPt * cos - current.xPt * sin
|
|
1800
|
+
};
|
|
1801
|
+
} else current = {
|
|
1802
|
+
xPt: current.xPt + fn.xPt,
|
|
1803
|
+
yPt: current.yPt + fn.yPt
|
|
1804
|
+
};
|
|
1805
|
+
return current;
|
|
1806
|
+
}
|
|
1807
|
+
function netRotationDeg(functions) {
|
|
1808
|
+
let totalRad = 0;
|
|
1809
|
+
for (const fn of functions) if (fn.kind === "rotate") totalRad += fn.angleRad;
|
|
1810
|
+
return -totalRad * 180 / Math.PI;
|
|
1811
|
+
}
|
|
1812
|
+
function resolveOdfShapeGeometry(element) {
|
|
1813
|
+
const transformValue = attrValue(element, "draw:transform");
|
|
1814
|
+
if (transformValue === void 0) {
|
|
1815
|
+
const box = parseBox(element);
|
|
1816
|
+
return box === void 0 ? void 0 : {
|
|
1817
|
+
frame: box,
|
|
1818
|
+
rotationDeg: void 0
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
const widthValue = attrValue(element, "svg:width");
|
|
1822
|
+
const heightValue = attrValue(element, "svg:height");
|
|
1823
|
+
if (widthValue === void 0 || heightValue === void 0) return;
|
|
1824
|
+
const widthPt = parseOdfLength(widthValue);
|
|
1825
|
+
const heightPt = parseOdfLength(heightValue);
|
|
1826
|
+
if (widthPt === void 0 || heightPt === void 0) return;
|
|
1827
|
+
const functions = parseOdfTransform(transformValue);
|
|
1828
|
+
const center = applyOdfTransform(functions, {
|
|
1829
|
+
xPt: widthPt / 2,
|
|
1830
|
+
yPt: heightPt / 2
|
|
1831
|
+
});
|
|
1832
|
+
const rotationDeg = netRotationDeg(functions);
|
|
1833
|
+
return {
|
|
1834
|
+
frame: {
|
|
1835
|
+
xPt: center.xPt - widthPt / 2,
|
|
1836
|
+
yPt: center.yPt - heightPt / 2,
|
|
1837
|
+
widthPt,
|
|
1838
|
+
heightPt
|
|
1839
|
+
},
|
|
1840
|
+
rotationDeg: rotationDeg === 0 ? void 0 : rotationDeg
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
function composeOdfGroupTransform(groupFunctions, child) {
|
|
1844
|
+
if (groupFunctions.length === 0) return child;
|
|
1845
|
+
const newCenter = applyOdfTransform(groupFunctions, {
|
|
1846
|
+
xPt: child.frame.xPt + child.frame.widthPt / 2,
|
|
1847
|
+
yPt: child.frame.yPt + child.frame.heightPt / 2
|
|
1848
|
+
});
|
|
1849
|
+
const newRotationDeg = (child.rotationDeg ?? 0) + netRotationDeg(groupFunctions);
|
|
1850
|
+
return {
|
|
1851
|
+
frame: {
|
|
1852
|
+
xPt: newCenter.xPt - child.frame.widthPt / 2,
|
|
1853
|
+
yPt: newCenter.yPt - child.frame.heightPt / 2,
|
|
1854
|
+
widthPt: child.frame.widthPt,
|
|
1855
|
+
heightPt: child.frame.heightPt
|
|
1856
|
+
},
|
|
1857
|
+
rotationDeg: newRotationDeg === 0 ? void 0 : newRotationDeg
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
//#endregion
|
|
1861
|
+
//#region src/typed/draw/shapes.ts
|
|
1862
|
+
const ZERO_INSETS = {
|
|
1863
|
+
insetLeftPt: 0,
|
|
1864
|
+
insetTopPt: 0,
|
|
1865
|
+
insetRightPt: 0,
|
|
1866
|
+
insetBottomPt: 0
|
|
1867
|
+
};
|
|
1868
|
+
function readPaddingPt(props, attrName) {
|
|
1869
|
+
const value = attrValue(props, attrName);
|
|
1870
|
+
return value === void 0 ? void 0 : parseOdfLength(value);
|
|
1871
|
+
}
|
|
1872
|
+
function readFrameInsets(frame, pkg) {
|
|
1873
|
+
const { elements } = resolveStyleElementChain(attrValue(frame, "draw:style-name"), "graphic", pkg);
|
|
1874
|
+
let insets = ZERO_INSETS;
|
|
1875
|
+
for (const element of elements) {
|
|
1876
|
+
const props = childrenWithTag(element, "style:graphic-properties")[0];
|
|
1877
|
+
if (props === void 0) continue;
|
|
1878
|
+
insets = {
|
|
1879
|
+
insetLeftPt: readPaddingPt(props, "fo:padding-left") ?? insets.insetLeftPt,
|
|
1880
|
+
insetTopPt: readPaddingPt(props, "fo:padding-top") ?? insets.insetTopPt,
|
|
1881
|
+
insetRightPt: readPaddingPt(props, "fo:padding-right") ?? insets.insetRightPt,
|
|
1882
|
+
insetBottomPt: readPaddingPt(props, "fo:padding-bottom") ?? insets.insetBottomPt
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
return insets;
|
|
1886
|
+
}
|
|
1887
|
+
function readDrawImageBlock(image, frameBox, pkg) {
|
|
1888
|
+
const href = attrValue(image, "xlink:href");
|
|
1889
|
+
const part = href === void 0 ? void 0 : pkg.parts[href];
|
|
1890
|
+
if (part?.kind !== "binary") return;
|
|
1891
|
+
const format = sniffImageFormat(base64ToBytes(part.base64));
|
|
1892
|
+
if (format === void 0) return;
|
|
1893
|
+
return {
|
|
1894
|
+
kind: "image",
|
|
1895
|
+
format,
|
|
1896
|
+
base64: part.base64,
|
|
1897
|
+
widthPt: frameBox.widthPt,
|
|
1898
|
+
heightPt: frameBox.heightPt
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
function readDrawFrameContent(frame, frameBox, pkg) {
|
|
1902
|
+
const table = childrenWithTag(frame, "table:table")[0];
|
|
1903
|
+
if (table !== void 0) return [readOdfTable(table, pkg)];
|
|
1904
|
+
const textBox = childrenWithTag(frame, "draw:text-box")[0];
|
|
1905
|
+
if (textBox !== void 0) return elementsWithTag(textBox.children, "text:p").map((p) => readOdfParagraph(p, pkg));
|
|
1906
|
+
const image = childrenWithTag(frame, "draw:image")[0];
|
|
1907
|
+
if (image !== void 0) {
|
|
1908
|
+
const block = readDrawImageBlock(image, frameBox, pkg);
|
|
1909
|
+
return block === void 0 ? [] : [block];
|
|
1910
|
+
}
|
|
1911
|
+
return [];
|
|
1912
|
+
}
|
|
1913
|
+
function readDrawFrame(frame, groupFunctions, pkg) {
|
|
1914
|
+
const ownGeometry = resolveOdfShapeGeometry(frame);
|
|
1915
|
+
if (ownGeometry === void 0) return;
|
|
1916
|
+
const geometry = composeOdfGroupTransform(groupFunctions, ownGeometry);
|
|
1917
|
+
return {
|
|
1918
|
+
name: attrValue(frame, "draw:name"),
|
|
1919
|
+
frame: geometry.frame,
|
|
1920
|
+
rotationDeg: geometry.rotationDeg,
|
|
1921
|
+
...readFrameInsets(frame, pkg),
|
|
1922
|
+
blocks: readDrawFrameContent(frame, geometry.frame, pkg)
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
function readOwnTransformFunctions(element) {
|
|
1926
|
+
const value = attrValue(element, "draw:transform");
|
|
1927
|
+
return value === void 0 ? [] : parseOdfTransform(value);
|
|
1928
|
+
}
|
|
1929
|
+
function walkDrawShapes(children, groupFunctions, pkg, out) {
|
|
1930
|
+
for (const node of children) {
|
|
1931
|
+
if (node.type !== "element") continue;
|
|
1932
|
+
if (node.tag === "draw:frame") {
|
|
1933
|
+
const shape = readDrawFrame(node, groupFunctions, pkg);
|
|
1934
|
+
if (shape !== void 0) out.push(shape);
|
|
1935
|
+
} else if (node.tag === "draw:g") {
|
|
1936
|
+
const ownFunctions = readOwnTransformFunctions(node);
|
|
1937
|
+
const nested = ownFunctions.length === 0 ? groupFunctions : [...ownFunctions, ...groupFunctions];
|
|
1938
|
+
walkDrawShapes(node.children, nested, pkg, out);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
//#endregion
|
|
1943
|
+
//#region src/typed/odp/read.ts
|
|
1944
|
+
const CONTENT_PART$1 = "content.xml";
|
|
1945
|
+
const STYLES_PART$1 = "styles.xml";
|
|
1946
|
+
const AUTOMATIC_STYLE_PARTS$1 = [CONTENT_PART$1, STYLES_PART$1];
|
|
1947
|
+
function findMasterPageElement(pkg, masterPageName) {
|
|
1948
|
+
if (masterPageName === void 0) return;
|
|
1949
|
+
const stylesPart = pkg.parts[STYLES_PART$1];
|
|
1950
|
+
if (stylesPart?.kind !== "xml") return;
|
|
1951
|
+
const root = rootElement(stylesPart.nodes);
|
|
1952
|
+
const masterStyles = root === void 0 ? void 0 : findChildElement(root.children, "office:master-styles");
|
|
1953
|
+
if (masterStyles === void 0) return;
|
|
1954
|
+
return childrenWithTag(masterStyles, "style:master-page").find((element) => attrValue(element, "style:name") === masterPageName);
|
|
1955
|
+
}
|
|
1956
|
+
function findPageLayoutElement$1(pkg, pageLayoutName) {
|
|
1957
|
+
if (pageLayoutName === void 0) return;
|
|
1958
|
+
for (const partPath of AUTOMATIC_STYLE_PARTS$1) {
|
|
1959
|
+
const part = pkg.parts[partPath];
|
|
1960
|
+
if (part?.kind !== "xml") continue;
|
|
1961
|
+
const root = rootElement(part.nodes);
|
|
1962
|
+
const automaticStyles = root === void 0 ? void 0 : findChildElement(root.children, "office:automatic-styles");
|
|
1963
|
+
if (automaticStyles === void 0) continue;
|
|
1964
|
+
const found = childrenWithTag(automaticStyles, "style:page-layout").find((element) => attrValue(element, "style:name") === pageLayoutName);
|
|
1965
|
+
if (found !== void 0) return found;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
function readSlideSize(page, pkg) {
|
|
1969
|
+
const masterPage = findMasterPageElement(pkg, attrValue(page, "draw:master-page-name"));
|
|
1970
|
+
const pageLayout = findPageLayoutElement$1(pkg, masterPage === void 0 ? void 0 : attrValue(masterPage, "style:page-layout-name"));
|
|
1971
|
+
const properties = pageLayout === void 0 ? void 0 : childrenWithTag(pageLayout, "style:page-layout-properties")[0];
|
|
1972
|
+
return (properties === void 0 ? void 0 : parsePageSize(properties)) ?? SLIDE_SIZE_WIDESCREEN;
|
|
1973
|
+
}
|
|
1974
|
+
function readSlideNotes(page) {
|
|
1975
|
+
const notes = childrenWithTag(page, "presentation:notes")[0];
|
|
1976
|
+
if (notes === void 0) return "";
|
|
1977
|
+
return elementsWithTag(notes.children, "text:p").map(decodeOdfText).join("\n");
|
|
1978
|
+
}
|
|
1979
|
+
function readSlide(page, pkg) {
|
|
1980
|
+
const shapes = [];
|
|
1981
|
+
walkDrawShapes(page.children, [], pkg, shapes);
|
|
1982
|
+
return {
|
|
1983
|
+
size: readSlideSize(page, pkg),
|
|
1984
|
+
shapes,
|
|
1985
|
+
notes: readSlideNotes(page)
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
function readOdp(pkg) {
|
|
1989
|
+
const contentPart = pkg.parts[CONTENT_PART$1];
|
|
1990
|
+
const root = contentPart?.kind === "xml" ? rootElement(contentPart.nodes) : void 0;
|
|
1991
|
+
const body = root === void 0 ? void 0 : findChildElement(root.children, "office:body");
|
|
1992
|
+
const presentation = body === void 0 ? void 0 : findChildElement(body.children, "office:presentation");
|
|
1993
|
+
const pages = presentation === void 0 ? [] : childrenWithTag(presentation, "draw:page");
|
|
1994
|
+
return {
|
|
1995
|
+
metadata: readOdfMetadata(pkg),
|
|
1996
|
+
slides: pages.map((page) => readSlide(page, pkg))
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
//#endregion
|
|
2000
|
+
//#region src/typed/odt/read.ts
|
|
2001
|
+
const CONTENT_PART = "content.xml";
|
|
2002
|
+
const STYLES_PART = "styles.xml";
|
|
2003
|
+
const AUTOMATIC_STYLE_PARTS = [CONTENT_PART, STYLES_PART];
|
|
2004
|
+
function readOutlineLevel(headingElement) {
|
|
2005
|
+
const raw = attrValue(headingElement, "text:outline-level");
|
|
2006
|
+
if (raw === void 0) return 1;
|
|
2007
|
+
const parsed = Number.parseInt(raw, 10);
|
|
2008
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : 1;
|
|
2009
|
+
}
|
|
2010
|
+
function readParagraphOrHeading(element, pkg, list) {
|
|
2011
|
+
const paragraph = readOdfParagraph(element, pkg);
|
|
2012
|
+
if (element.tag === "text:h") paragraph.styleId = `Heading${readOutlineLevel(element)}`;
|
|
2013
|
+
if (list !== void 0) paragraph.list = list;
|
|
2014
|
+
return paragraph;
|
|
2015
|
+
}
|
|
2016
|
+
function readListItems(listElement, context, pkg, blocks) {
|
|
2017
|
+
for (const item of listElement.children) {
|
|
2018
|
+
if (item.type !== "element" || item.tag !== "text:list-item") continue;
|
|
2019
|
+
for (const itemChild of item.children) {
|
|
2020
|
+
if (itemChild.type !== "element") continue;
|
|
2021
|
+
if (itemChild.tag === "text:p" || itemChild.tag === "text:h") blocks.push(readParagraphOrHeading(itemChild, pkg, context));
|
|
2022
|
+
else if (itemChild.tag === "text:list") readListItems(itemChild, {
|
|
2023
|
+
numId: context.numId,
|
|
2024
|
+
level: context.level + 1
|
|
2025
|
+
}, pkg, blocks);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
function readBlocks(nodes, pkg, listIdState) {
|
|
2030
|
+
const blocks = [];
|
|
2031
|
+
for (const node of nodes) {
|
|
2032
|
+
if (node.type !== "element") continue;
|
|
2033
|
+
if (node.tag === "text:p" || node.tag === "text:h") blocks.push(readParagraphOrHeading(node, pkg, void 0));
|
|
2034
|
+
else if (node.tag === "text:list") {
|
|
2035
|
+
const numId = `list${listIdState.next}`;
|
|
2036
|
+
listIdState.next += 1;
|
|
2037
|
+
readListItems(node, {
|
|
2038
|
+
numId,
|
|
2039
|
+
level: 0
|
|
2040
|
+
}, pkg, blocks);
|
|
2041
|
+
} else if (node.tag === "table:table") blocks.push(readOdfTable(node, pkg));
|
|
2042
|
+
else if (node.tag === "text:section") blocks.push(...readBlocks(node.children, pkg, listIdState));
|
|
2043
|
+
}
|
|
2044
|
+
return blocks;
|
|
2045
|
+
}
|
|
2046
|
+
function parseKnownOdfLength(value) {
|
|
2047
|
+
const parsed = parseOdfLength(value);
|
|
2048
|
+
if (parsed === void 0) throw new Error(`readOdt: internal error -- "${value}" is not a valid ODF length literal`);
|
|
2049
|
+
return parsed;
|
|
2050
|
+
}
|
|
2051
|
+
const DEFAULT_PAGE_SIZE = PAGE_SIZE_A4;
|
|
2052
|
+
const DEFAULT_MARGIN_PT = parseKnownOdfLength("2cm");
|
|
2053
|
+
const DEFAULT_MARGINS = {
|
|
2054
|
+
topPt: DEFAULT_MARGIN_PT,
|
|
2055
|
+
rightPt: DEFAULT_MARGIN_PT,
|
|
2056
|
+
bottomPt: DEFAULT_MARGIN_PT,
|
|
2057
|
+
leftPt: DEFAULT_MARGIN_PT
|
|
2058
|
+
};
|
|
2059
|
+
function findPageLayoutElement(pkg, pageLayoutName) {
|
|
2060
|
+
if (pageLayoutName === void 0) return;
|
|
2061
|
+
for (const partPath of AUTOMATIC_STYLE_PARTS) {
|
|
2062
|
+
const part = pkg.parts[partPath];
|
|
2063
|
+
if (part?.kind !== "xml") continue;
|
|
2064
|
+
const root = rootElement(part.nodes);
|
|
2065
|
+
const automaticStyles = root === void 0 ? void 0 : findChildElement(root.children, "office:automatic-styles");
|
|
2066
|
+
if (automaticStyles === void 0) continue;
|
|
2067
|
+
const found = childrenWithTag(automaticStyles, "style:page-layout").find((element) => attrValue(element, "style:name") === pageLayoutName);
|
|
2068
|
+
if (found !== void 0) return found;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
function readFirstMasterPageGeometry(pkg) {
|
|
2072
|
+
const stylesPart = pkg.parts[STYLES_PART];
|
|
2073
|
+
const stylesRoot = stylesPart?.kind === "xml" ? rootElement(stylesPart.nodes) : void 0;
|
|
2074
|
+
const masterStyles = stylesRoot === void 0 ? void 0 : findChildElement(stylesRoot.children, "office:master-styles");
|
|
2075
|
+
const masterPage = masterStyles === void 0 ? void 0 : findChildElement(masterStyles.children, "style:master-page");
|
|
2076
|
+
const layout = findPageLayoutElement(pkg, masterPage === void 0 ? void 0 : attrValue(masterPage, "style:page-layout-name"));
|
|
2077
|
+
const properties = layout === void 0 ? void 0 : findChildElement(layout.children, "style:page-layout-properties");
|
|
2078
|
+
const pageSize = properties === void 0 ? void 0 : parsePageSize(properties);
|
|
2079
|
+
const margins = properties === void 0 ? void 0 : parseMargins(properties);
|
|
2080
|
+
return {
|
|
2081
|
+
pageSize: pageSize ?? DEFAULT_PAGE_SIZE,
|
|
2082
|
+
margins: margins ?? DEFAULT_MARGINS
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
function readOdt(pkg) {
|
|
2086
|
+
const contentPart = pkg.parts[CONTENT_PART];
|
|
2087
|
+
if (contentPart?.kind !== "xml") throw new Error(`readOdt: package has no ${CONTENT_PART} part`);
|
|
2088
|
+
const contentRoot = rootElement(contentPart.nodes);
|
|
2089
|
+
const body = contentRoot === void 0 ? void 0 : findChildElement(contentRoot.children, "office:body");
|
|
2090
|
+
const textElement = body === void 0 ? void 0 : findChildElement(body.children, "office:text");
|
|
2091
|
+
if (textElement === void 0) throw new Error(`readOdt: ${CONTENT_PART} has no office:body/office:text element`);
|
|
2092
|
+
const metadata = readOdfMetadata(pkg);
|
|
2093
|
+
const { pageSize, margins } = readFirstMasterPageGeometry(pkg);
|
|
2094
|
+
return {
|
|
2095
|
+
metadata,
|
|
2096
|
+
sections: [{
|
|
2097
|
+
pageSize,
|
|
2098
|
+
margins,
|
|
2099
|
+
blocks: readBlocks(textElement.children, pkg, { next: 1 })
|
|
2100
|
+
}]
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
//#endregion
|
|
2104
|
+
export { AlignmentSchema, AttributeSchema, BinaryPartSchema, MANIFEST_PART, META_PART, MIMETYPE_PART, ManifestEntrySchema, ManifestProblemSchema, ManifestSchema, ODF_MEDIA_TYPES, ODF_NAMESPACES, PackageSchema, PartSchema, STYLE_FAMILIES, StylePropertiesSchema, StyleRegistry, TableCursor, XmlCdataSchema, XmlCommentSchema, XmlDeclarationSchema, XmlElementSchema, XmlNodeSchema, XmlPartSchema, XmlPiSchema, XmlTextSchema, applyOdfTransform, attrValue, base64ToBytes, buildManifest, buildStylePropertyElements, buildXml, bytesToBase64, canonicalPropertiesString, cellReference, childrenWithTag, columnIndexToLetters, composeOdfGroupTransform, decodeOdfText, decodePackage, decodeXmlText, el, elementsWithTag, encodePackage, encodeXmlText, ensureSpan, findChildElement, findStyleElement, formatOdfColor, formatOdfLength, formatPercentageMultiplier, formatPt, getOdfSpaceCount, isStyleFamily, isXmlNode, measureOdfNodeLength, mediaTypeForExtension, netRotationDeg, packageCodec, paragraphPropertiesToAttributes, parseBox, parseLength, parseMargins, parseOdfColor, parseOdfLength, parseOdfTransform, parsePackage, parsePageSize, parseParagraphProperties, parseStyleElementProperties, parseTextProperties, parseXml, readDrawFrame, readManifest, readMimetype, readOdfMetadata, readOdfParagraph, readOdfTable, readOdp, readOdt, resolveOdfShapeGeometry, resolveStyle, resolveStyleElementChain, rootElement, serializePackage, setDocumentMediaType, sniffImageFormat, sumOdfNodeLength, syncManifest, textPropertiesToAttributes, txt, unzipPackage, validateManifest, walkDrawShapes, writeManifest, writeMimetype, xmlCodec, xmlnsAttributes, zipPackage };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "odf.js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Type-safe, lossless round-trip conversion between OpenDocument Format packages (odt, ods, odp) and JSON, hand-written and dependency-minimal, built on Zod 4 codecs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|