superdoc 1.13.0-next.9 → 1.13.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/chunks/{helpers-OFep8CYR.cjs → DocxZipper-B6nm2R9-.cjs} +300 -392
- package/dist/chunks/{helpers-BqdwtJE0.es.js → DocxZipper-D_TE2vfz.es.js} +300 -392
- package/dist/chunks/{PdfViewer-H0MTPIfL.es.js → PdfViewer-D_iK97se.es.js} +2 -2
- package/dist/chunks/{PdfViewer-CrKV8eHY.cjs → PdfViewer-Dq6pz8jV.cjs} +2 -2
- package/dist/chunks/{SuperConverter-BAshNZxb.es.js → SuperConverter-BCWk5AhG.es.js} +181 -488
- package/dist/chunks/{SuperConverter-MBrfq8sX.cjs → SuperConverter-BfyuO4lJ.cjs} +179 -486
- package/dist/chunks/helpers-CCzma9mJ.cjs +419 -0
- package/dist/chunks/helpers-Zjk_AcyH.es.js +420 -0
- package/dist/chunks/{index-BzVFH_iV.cjs → index-8clxyRgd.cjs} +6 -5
- package/dist/chunks/{index-DCaJJXb5.cjs → index-Dc_gvtx4.cjs} +15 -15
- package/dist/chunks/{index-DR_0n8Ql.es.js → index-Dlq64L1l.es.js} +6 -6
- package/dist/chunks/{index-DM0PJv0N.es.js → index-RjXF1sKR.es.js} +6 -5
- package/dist/super-editor/converter.cjs +2 -2
- package/dist/super-editor/converter.es.js +2 -2
- package/dist/super-editor/core/DocxZipper.d.ts.map +1 -1
- package/dist/super-editor/core/super-converter/SuperConverter.d.ts.map +1 -1
- package/dist/super-editor/core/super-converter/helpers.d.ts +7 -0
- package/dist/super-editor/core/super-converter/helpers.d.ts.map +1 -1
- package/dist/super-editor/core/super-converter/v3/handlers/wp/helpers/metafile-converter.d.ts.map +1 -1
- package/dist/super-editor/docx-zipper.cjs +4 -313
- package/dist/super-editor/docx-zipper.es.js +4 -313
- package/dist/super-editor/src/core/DocxZipper.d.ts.map +1 -1
- package/dist/super-editor/src/core/super-converter/SuperConverter.d.ts.map +1 -1
- package/dist/super-editor/src/core/super-converter/helpers.d.ts +7 -0
- package/dist/super-editor/src/core/super-converter/helpers.d.ts.map +1 -1
- package/dist/super-editor/src/core/super-converter/v3/handlers/wp/helpers/metafile-converter.d.ts.map +1 -1
- package/dist/super-editor/tsconfig.types.tsbuildinfo +1 -1
- package/dist/super-editor.cjs +4 -4
- package/dist/super-editor.es.js +5 -5
- package/dist/superdoc.cjs +5 -4
- package/dist/superdoc.es.js +5 -4
- package/dist/superdoc.umd.js +787 -1097
- package/dist/superdoc.umd.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const
|
|
2
|
+
const helpers = require("./helpers-CCzma9mJ.cjs");
|
|
3
|
+
const jszip_min = require("./jszip.min-oAFpNMh5.cjs");
|
|
3
4
|
var buffer = {};
|
|
4
5
|
var base64Js = {};
|
|
5
6
|
base64Js.byteLength = byteLength;
|
|
@@ -1759,409 +1760,316 @@ ieee754.write = function(buffer2, value, offset, isLE, mLen, nBytes) {
|
|
|
1759
1760
|
}
|
|
1760
1761
|
})(buffer);
|
|
1761
1762
|
const Buffer = buffer.Buffer;
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1763
|
+
const isXmlLike = (name) => /\.xml$|\.rels$/i.test(name);
|
|
1764
|
+
function sniffEncoding(u8) {
|
|
1765
|
+
if (u8.length >= 2) {
|
|
1766
|
+
const b0 = u8[0], b1 = u8[1];
|
|
1767
|
+
if (b0 === 255 && b1 === 254) return "utf-16le";
|
|
1768
|
+
if (b0 === 254 && b1 === 255) return "utf-16be";
|
|
1769
|
+
}
|
|
1770
|
+
let nul = 0;
|
|
1771
|
+
for (let i = 0; i < Math.min(64, u8.length); i++) if (u8[i] === 0) nul++;
|
|
1772
|
+
if (nul > 16) return "utf-16le";
|
|
1773
|
+
return "utf-8";
|
|
1768
1774
|
}
|
|
1769
|
-
function
|
|
1770
|
-
|
|
1771
|
-
const value = Number(twips);
|
|
1772
|
-
if (Number.isNaN(value)) return;
|
|
1773
|
-
return value / 1440;
|
|
1775
|
+
function stripBOM(str) {
|
|
1776
|
+
return str && str.charCodeAt(0) === 65279 ? str.slice(1) : str;
|
|
1774
1777
|
}
|
|
1775
|
-
function
|
|
1776
|
-
if (
|
|
1777
|
-
|
|
1778
|
-
|
|
1778
|
+
function ensureXmlString(content) {
|
|
1779
|
+
if (typeof content === "string") return stripBOM(content);
|
|
1780
|
+
let u8 = null;
|
|
1781
|
+
if (content && typeof content === "object") {
|
|
1782
|
+
if (content instanceof Uint8Array) {
|
|
1783
|
+
u8 = content;
|
|
1784
|
+
} else if (typeof Buffer !== "undefined" && Buffer.isBuffer && Buffer.isBuffer(content)) {
|
|
1785
|
+
u8 = new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
1786
|
+
} else if (ArrayBuffer.isView && ArrayBuffer.isView(content)) {
|
|
1787
|
+
u8 = new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
1788
|
+
} else if (content.constructor && (content instanceof ArrayBuffer || content.constructor.name === "ArrayBuffer")) {
|
|
1789
|
+
u8 = new Uint8Array(content);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
if (!u8) throw new Error("Unsupported content type for XML");
|
|
1793
|
+
const enc = sniffEncoding(u8);
|
|
1794
|
+
let xml = new TextDecoder(enc).decode(u8);
|
|
1795
|
+
return stripBOM(xml);
|
|
1779
1796
|
}
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
}
|
|
1839
|
-
function rotToDegrees(rot) {
|
|
1840
|
-
if (rot == null) return;
|
|
1841
|
-
return rot / 6e4;
|
|
1842
|
-
}
|
|
1843
|
-
function degreesToRot(degrees) {
|
|
1844
|
-
if (degrees == null) return;
|
|
1845
|
-
return degrees * 6e4;
|
|
1846
|
-
}
|
|
1847
|
-
function pixelsToPolygonUnits(pixels) {
|
|
1848
|
-
if (pixels == null) return;
|
|
1849
|
-
const pu = pixels * PIXELS_PER_INCH;
|
|
1850
|
-
return Math.round(pu);
|
|
1851
|
-
}
|
|
1852
|
-
function polygonUnitsToPixels(pu) {
|
|
1853
|
-
if (pu == null) return;
|
|
1854
|
-
const pixels = Number(pu) / PIXELS_PER_INCH;
|
|
1855
|
-
return Math.round(pixels * 1e3) / 1e3;
|
|
1856
|
-
}
|
|
1857
|
-
function polygonToObj(polygonNode) {
|
|
1858
|
-
if (!polygonNode) return null;
|
|
1859
|
-
const points = [];
|
|
1860
|
-
polygonNode.elements.forEach((element) => {
|
|
1861
|
-
if (["wp:start", "wp:lineTo"].includes(element.name)) {
|
|
1862
|
-
const { x, y } = element.attributes;
|
|
1863
|
-
points.push([polygonUnitsToPixels(x), polygonUnitsToPixels(y)]);
|
|
1864
|
-
}
|
|
1865
|
-
});
|
|
1866
|
-
if (points.length > 1) {
|
|
1867
|
-
const firstPoint = points[0];
|
|
1868
|
-
const lastPoint = points[points.length - 1];
|
|
1869
|
-
if (firstPoint[0] === lastPoint[0] && firstPoint[1] === lastPoint[1]) {
|
|
1870
|
-
points.pop();
|
|
1797
|
+
class DocxZipper {
|
|
1798
|
+
constructor(params = {}) {
|
|
1799
|
+
this.debug = params.debug || false;
|
|
1800
|
+
this.zip = new jszip_min.JSZip();
|
|
1801
|
+
this.files = [];
|
|
1802
|
+
this.media = {};
|
|
1803
|
+
this.mediaFiles = {};
|
|
1804
|
+
this.fonts = {};
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Get all docx data from the zipped docx
|
|
1808
|
+
*
|
|
1809
|
+
* [ContentTypes].xml
|
|
1810
|
+
* _rels/.rels
|
|
1811
|
+
* word/document.xml
|
|
1812
|
+
* word/_rels/document.xml.rels
|
|
1813
|
+
* word/footnotes.xml
|
|
1814
|
+
* word/endnotes.xml
|
|
1815
|
+
* word/header1.xml
|
|
1816
|
+
* word/theme/theme1.xml
|
|
1817
|
+
* word/settings.xml
|
|
1818
|
+
* word/styles.xml
|
|
1819
|
+
* word/webSettings.xml
|
|
1820
|
+
* word/fontTable.xml
|
|
1821
|
+
* docProps/core.xml
|
|
1822
|
+
* docProps/app.xml
|
|
1823
|
+
* */
|
|
1824
|
+
async getDocxData(file, isNode = false) {
|
|
1825
|
+
const extractedFiles = await this.unzip(file);
|
|
1826
|
+
const files = Object.entries(extractedFiles.files);
|
|
1827
|
+
for (const [, zipEntry] of files) {
|
|
1828
|
+
const name = zipEntry.name;
|
|
1829
|
+
if (isXmlLike(name)) {
|
|
1830
|
+
const u8 = await zipEntry.async("uint8array");
|
|
1831
|
+
const content = ensureXmlString(u8);
|
|
1832
|
+
this.files.push({ name, content });
|
|
1833
|
+
} else if (name.startsWith("word/media") && name !== "word/media/" || zipEntry.name.startsWith("media") && zipEntry.name !== "media/" || name.startsWith("media") && name !== "media/" || name.startsWith("word/embeddings") && name !== "word/embeddings/") {
|
|
1834
|
+
if (isNode) {
|
|
1835
|
+
const buffer2 = await zipEntry.async("nodebuffer");
|
|
1836
|
+
const fileBase64 = buffer2.toString("base64");
|
|
1837
|
+
this.mediaFiles[name] = fileBase64;
|
|
1838
|
+
} else {
|
|
1839
|
+
const fileBase64 = await zipEntry.async("base64");
|
|
1840
|
+
const extension = this.getFileExtension(name)?.toLowerCase();
|
|
1841
|
+
const imageTypes = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "bmp", "tiff", "emf", "wmf", "svg", "webp"]);
|
|
1842
|
+
if (imageTypes.has(extension)) {
|
|
1843
|
+
this.mediaFiles[name] = `data:image/${extension};base64,${fileBase64}`;
|
|
1844
|
+
const blob = await zipEntry.async("blob");
|
|
1845
|
+
const fileObj = new File([blob], name, { type: blob.type });
|
|
1846
|
+
const imageUrl = URL.createObjectURL(fileObj);
|
|
1847
|
+
this.media[name] = imageUrl;
|
|
1848
|
+
} else {
|
|
1849
|
+
this.mediaFiles[name] = fileBase64;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
} else if (name.startsWith("word/fonts") && name !== "word/fonts/") {
|
|
1853
|
+
const uint8array = await zipEntry.async("uint8array");
|
|
1854
|
+
this.fonts[name] = uint8array;
|
|
1855
|
+
}
|
|
1871
1856
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
y: pixelsToPolygonUnits(y)
|
|
1857
|
+
return this.files;
|
|
1858
|
+
}
|
|
1859
|
+
getFileExtension(fileName) {
|
|
1860
|
+
const fileSplit = fileName.split(".");
|
|
1861
|
+
if (fileSplit.length < 2) return null;
|
|
1862
|
+
return fileSplit[fileSplit.length - 1];
|
|
1863
|
+
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Update [Content_Types].xml with extensions of new Image annotations
|
|
1866
|
+
*/
|
|
1867
|
+
async updateContentTypes(docx, media, fromJson, updatedDocs = {}) {
|
|
1868
|
+
const additionalPartNames = Object.keys(updatedDocs || {});
|
|
1869
|
+
const imageExts = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "bmp", "tiff", "emf", "wmf", "svg", "webp"]);
|
|
1870
|
+
const newMediaTypes = Object.keys(media).map((name) => this.getFileExtension(name)).filter((ext) => ext && imageExts.has(ext));
|
|
1871
|
+
const contentTypesPath = "[Content_Types].xml";
|
|
1872
|
+
let contentTypesXml;
|
|
1873
|
+
if (fromJson) {
|
|
1874
|
+
if (Array.isArray(docx.files)) {
|
|
1875
|
+
contentTypesXml = docx.files.find((file) => file.name === contentTypesPath)?.content || "";
|
|
1876
|
+
} else {
|
|
1877
|
+
contentTypesXml = docx.files?.[contentTypesPath] || "";
|
|
1894
1878
|
}
|
|
1895
|
-
};
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
type
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1879
|
+
} else contentTypesXml = await docx.file(contentTypesPath).async("string");
|
|
1880
|
+
let typesString = "";
|
|
1881
|
+
const defaultMediaTypes = helpers.getContentTypesFromXml(contentTypesXml);
|
|
1882
|
+
const seenTypes = /* @__PURE__ */ new Set();
|
|
1883
|
+
for (let type of newMediaTypes) {
|
|
1884
|
+
if (defaultMediaTypes.includes(type)) continue;
|
|
1885
|
+
if (seenTypes.has(type)) continue;
|
|
1886
|
+
const newContentType = `<Default Extension="${type}" ContentType="image/${type}"/>`;
|
|
1887
|
+
typesString += newContentType;
|
|
1888
|
+
seenTypes.add(type);
|
|
1889
|
+
}
|
|
1890
|
+
const xmlJson = JSON.parse(helpers.libExports.xml2json(contentTypesXml, null, 2));
|
|
1891
|
+
const types = xmlJson.elements?.find((el) => el.name === "Types") || {};
|
|
1892
|
+
const hasComments = types.elements?.some(
|
|
1893
|
+
(el) => el.name === "Override" && el.attributes.PartName === "/word/comments.xml"
|
|
1894
|
+
);
|
|
1895
|
+
const hasCommentsExtended = types.elements?.some(
|
|
1896
|
+
(el) => el.name === "Override" && el.attributes.PartName === "/word/commentsExtended.xml"
|
|
1897
|
+
);
|
|
1898
|
+
const hasCommentsIds = types.elements?.some(
|
|
1899
|
+
(el) => el.name === "Override" && el.attributes.PartName === "/word/commentsIds.xml"
|
|
1900
|
+
);
|
|
1901
|
+
const hasCommentsExtensible = types.elements?.some(
|
|
1902
|
+
(el) => el.name === "Override" && el.attributes.PartName === "/word/commentsExtensible.xml"
|
|
1903
|
+
);
|
|
1904
|
+
const hasFile = (filename) => {
|
|
1905
|
+
if (updatedDocs && Object.prototype.hasOwnProperty.call(updatedDocs, filename)) {
|
|
1906
|
+
return true;
|
|
1906
1907
|
}
|
|
1908
|
+
if (!docx?.files) return false;
|
|
1909
|
+
if (!fromJson) return Boolean(docx.files[filename]);
|
|
1910
|
+
if (Array.isArray(docx.files)) return docx.files.some((file) => file.name === filename);
|
|
1911
|
+
return Boolean(docx.files[filename]);
|
|
1907
1912
|
};
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
}
|
|
1912
|
-
const REMOTE_RESOURCE_PATTERN = /^https?:|^blob:|^file:/i;
|
|
1913
|
-
const DATA_URI_PATTERN = /^data:/i;
|
|
1914
|
-
const getArrayBufferFromUrl = async (input) => {
|
|
1915
|
-
if (input == null) {
|
|
1916
|
-
return new ArrayBuffer(0);
|
|
1917
|
-
}
|
|
1918
|
-
if (input instanceof ArrayBuffer) {
|
|
1919
|
-
return input;
|
|
1920
|
-
}
|
|
1921
|
-
if (ArrayBuffer.isView(input)) {
|
|
1922
|
-
const view = input;
|
|
1923
|
-
return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
|
|
1924
|
-
}
|
|
1925
|
-
if (typeof Blob !== "undefined" && input instanceof Blob) {
|
|
1926
|
-
return await input.arrayBuffer();
|
|
1927
|
-
}
|
|
1928
|
-
if (typeof input !== "string") {
|
|
1929
|
-
throw new TypeError("Unsupported media input type");
|
|
1930
|
-
}
|
|
1931
|
-
const trimmed = input.trim();
|
|
1932
|
-
const shouldFetchRemote = REMOTE_RESOURCE_PATTERN.test(trimmed);
|
|
1933
|
-
const isDataUri = DATA_URI_PATTERN.test(trimmed);
|
|
1934
|
-
if (shouldFetchRemote) {
|
|
1935
|
-
if (typeof fetch !== "function") {
|
|
1936
|
-
throw new Error(`Fetch API is not available to retrieve media: ${trimmed}`);
|
|
1937
|
-
}
|
|
1938
|
-
const response = await fetch(trimmed);
|
|
1939
|
-
if (!response.ok) {
|
|
1940
|
-
throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);
|
|
1941
|
-
}
|
|
1942
|
-
return await response.arrayBuffer();
|
|
1943
|
-
}
|
|
1944
|
-
const base64Payload = isDataUri ? trimmed.split(",", 2)[1] : trimmed.replace(/\s/g, "");
|
|
1945
|
-
try {
|
|
1946
|
-
if (typeof globalThis.atob === "function") {
|
|
1947
|
-
const binary = globalThis.atob(base64Payload);
|
|
1948
|
-
const bytes = new Uint8Array(binary.length);
|
|
1949
|
-
for (let i = 0; i < binary.length; i++) {
|
|
1950
|
-
bytes[i] = binary.charCodeAt(i);
|
|
1951
|
-
}
|
|
1952
|
-
return bytes.buffer;
|
|
1913
|
+
if (hasFile("word/comments.xml")) {
|
|
1914
|
+
const commentsDef = `<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" />`;
|
|
1915
|
+
if (!hasComments) typesString += commentsDef;
|
|
1953
1916
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
const
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
const resolved = [];
|
|
1978
|
-
for (const seg of segments) {
|
|
1979
|
-
if (seg === "..") {
|
|
1980
|
-
resolved.pop();
|
|
1981
|
-
} else if (seg !== "." && seg !== "") {
|
|
1982
|
-
resolved.push(seg);
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
return resolved.join("/");
|
|
1986
|
-
};
|
|
1987
|
-
const DOCX_HIGHLIGHT_KEYWORD_MAP = /* @__PURE__ */ new Map([
|
|
1988
|
-
["yellow", "FFFF00"],
|
|
1989
|
-
["green", "00FF00"],
|
|
1990
|
-
["blue", "0000FF"],
|
|
1991
|
-
["cyan", "00FFFF"],
|
|
1992
|
-
["magenta", "FF00FF"],
|
|
1993
|
-
["red", "FF0000"],
|
|
1994
|
-
["darkYellow", "808000"],
|
|
1995
|
-
["darkGreen", "008000"],
|
|
1996
|
-
["darkBlue", "000080"],
|
|
1997
|
-
["darkCyan", "008080"],
|
|
1998
|
-
["darkMagenta", "800080"],
|
|
1999
|
-
["darkGray", "808080"],
|
|
2000
|
-
["darkRed", "800000"],
|
|
2001
|
-
["lightGray", "C0C0C0"],
|
|
2002
|
-
["black", "000000"],
|
|
2003
|
-
["white", "FFFFFF"]
|
|
2004
|
-
]);
|
|
2005
|
-
const normalizeHexColor = (hex) => {
|
|
2006
|
-
if (!hex) return null;
|
|
2007
|
-
let value = hex.replace("#", "").trim();
|
|
2008
|
-
if (!value) return null;
|
|
2009
|
-
value = value.toUpperCase();
|
|
2010
|
-
if (value.length === 3)
|
|
2011
|
-
value = value.split("").map((c) => c + c).join("");
|
|
2012
|
-
if (value.length === 8) value = value.slice(0, 6);
|
|
2013
|
-
return value;
|
|
2014
|
-
};
|
|
2015
|
-
const getHexColorFromDocxSystem = (docxColor) => {
|
|
2016
|
-
const hex = DOCX_HIGHLIGHT_KEYWORD_MAP.get(docxColor);
|
|
2017
|
-
return hex ? `#${hex}` : null;
|
|
2018
|
-
};
|
|
2019
|
-
const getDocxHighlightKeywordFromHex = (hexColor) => {
|
|
2020
|
-
if (!hexColor) return null;
|
|
2021
|
-
if (DOCX_HIGHLIGHT_KEYWORD_MAP.has(hexColor)) return hexColor;
|
|
2022
|
-
const normalized = normalizeHexColor(hexColor);
|
|
2023
|
-
if (!normalized) return null;
|
|
2024
|
-
for (const [keyword, hex] of DOCX_HIGHLIGHT_KEYWORD_MAP.entries()) {
|
|
2025
|
-
if (hex === normalized) return keyword;
|
|
2026
|
-
}
|
|
2027
|
-
return null;
|
|
2028
|
-
};
|
|
2029
|
-
function isValidHexColor(color) {
|
|
2030
|
-
if (!color || typeof color !== "string") return false;
|
|
2031
|
-
switch (color.length) {
|
|
2032
|
-
case 3:
|
|
2033
|
-
return /^[0-9A-F]{3}$/i.test(color);
|
|
2034
|
-
case 6:
|
|
2035
|
-
return /^[0-9A-F]{6}$/i.test(color);
|
|
2036
|
-
case 8:
|
|
2037
|
-
return /^[0-9A-F]{8}$/i.test(color);
|
|
2038
|
-
default:
|
|
2039
|
-
return false;
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
const componentToHex = (val) => {
|
|
2043
|
-
const a = Number(val).toString(16);
|
|
2044
|
-
return a.length === 1 ? "0" + a : a;
|
|
2045
|
-
};
|
|
2046
|
-
const rgbToHex = (rgb) => {
|
|
2047
|
-
return "#" + rgb.match(/\d+/g).map(componentToHex).join("");
|
|
2048
|
-
};
|
|
2049
|
-
const DEFAULT_SHADING_FOREGROUND_COLOR = "#000000";
|
|
2050
|
-
const hexToRgb = (hex) => {
|
|
2051
|
-
const normalized = normalizeHexColor(hex);
|
|
2052
|
-
if (!normalized) return null;
|
|
2053
|
-
return {
|
|
2054
|
-
r: Number.parseInt(normalized.slice(0, 2), 16),
|
|
2055
|
-
g: Number.parseInt(normalized.slice(2, 4), 16),
|
|
2056
|
-
b: Number.parseInt(normalized.slice(4, 6), 16)
|
|
2057
|
-
};
|
|
2058
|
-
};
|
|
2059
|
-
const clamp01 = (value) => {
|
|
2060
|
-
if (!Number.isFinite(value)) return 0;
|
|
2061
|
-
return Math.min(1, Math.max(0, value));
|
|
2062
|
-
};
|
|
2063
|
-
const blendHexColors = (backgroundHex, foregroundHex, foregroundRatio) => {
|
|
2064
|
-
const background = hexToRgb(backgroundHex);
|
|
2065
|
-
const foreground = hexToRgb(foregroundHex);
|
|
2066
|
-
if (!background || !foreground) return null;
|
|
2067
|
-
const ratio = clamp01(foregroundRatio);
|
|
2068
|
-
const r = Math.round(background.r * (1 - ratio) + foreground.r * ratio);
|
|
2069
|
-
const g = Math.round(background.g * (1 - ratio) + foreground.g * ratio);
|
|
2070
|
-
const b = Math.round(background.b * (1 - ratio) + foreground.b * ratio);
|
|
2071
|
-
const toByte = (n) => n.toString(16).padStart(2, "0").toUpperCase();
|
|
2072
|
-
return `${toByte(r)}${toByte(g)}${toByte(b)}`;
|
|
2073
|
-
};
|
|
2074
|
-
const resolveShadingFillColor = (shading) => {
|
|
2075
|
-
if (!shading || typeof shading !== "object") return null;
|
|
2076
|
-
const fill = normalizeHexColor(shading.fill);
|
|
2077
|
-
if (!fill) return null;
|
|
2078
|
-
const val = typeof shading.val === "string" ? shading.val.trim().toLowerCase() : "";
|
|
2079
|
-
const pctMatch = val.match(/^pct(\d{1,3})$/);
|
|
2080
|
-
if (!pctMatch) return fill;
|
|
2081
|
-
const pct = Number.parseInt(pctMatch[1], 10);
|
|
2082
|
-
if (!Number.isFinite(pct) || pct < 0 || pct > 100) return fill;
|
|
2083
|
-
const foreground = normalizeHexColor(shading.color) ?? DEFAULT_SHADING_FOREGROUND_COLOR;
|
|
2084
|
-
return blendHexColors(fill, foreground, pct / 100) ?? fill;
|
|
2085
|
-
};
|
|
2086
|
-
const deobfuscateFont = (arrayBuffer, guidHex) => {
|
|
2087
|
-
const dta = new Uint8Array(arrayBuffer);
|
|
2088
|
-
const guidStr = guidHex.replace(/[-{}]/g, "");
|
|
2089
|
-
if (guidStr.length !== 32) {
|
|
2090
|
-
console.error("Invalid GUID");
|
|
2091
|
-
return;
|
|
2092
|
-
}
|
|
2093
|
-
const guidBytes = new Uint8Array(16);
|
|
2094
|
-
for (let i = 0, j = 0; i < 32; i += 2, j++) {
|
|
2095
|
-
const hexByte = guidStr[i] + guidStr[i + 1];
|
|
2096
|
-
guidBytes[j] = parseInt(hexByte, 16);
|
|
2097
|
-
}
|
|
2098
|
-
for (let i = 0; i < 32; i++) {
|
|
2099
|
-
const gi = 15 - i % 16;
|
|
2100
|
-
dta[i] ^= guidBytes[gi];
|
|
2101
|
-
}
|
|
2102
|
-
return dta.buffer;
|
|
2103
|
-
};
|
|
2104
|
-
function convertSizeToCSS(value, type) {
|
|
2105
|
-
if (typeof value === "string" && value.endsWith("%")) {
|
|
2106
|
-
type = "pct";
|
|
2107
|
-
}
|
|
2108
|
-
if (value === null || value === void 0) {
|
|
2109
|
-
value = 0;
|
|
2110
|
-
}
|
|
2111
|
-
switch (type) {
|
|
2112
|
-
case "dxa":
|
|
2113
|
-
case null:
|
|
2114
|
-
case void 0:
|
|
2115
|
-
return `${twipsToPixels(value)}px`;
|
|
2116
|
-
case "nil":
|
|
2117
|
-
return "0";
|
|
2118
|
-
case "auto":
|
|
2119
|
-
return null;
|
|
2120
|
-
case "pct":
|
|
2121
|
-
let percent;
|
|
2122
|
-
if (typeof value === "number") {
|
|
2123
|
-
percent = value * 0.02;
|
|
1917
|
+
if (hasFile("word/commentsExtended.xml")) {
|
|
1918
|
+
const commentsExtendedDef = `<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml" />`;
|
|
1919
|
+
if (!hasCommentsExtended) typesString += commentsExtendedDef;
|
|
1920
|
+
}
|
|
1921
|
+
if (hasFile("word/commentsIds.xml")) {
|
|
1922
|
+
const commentsIdsDef = `<Override PartName="/word/commentsIds.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml" />`;
|
|
1923
|
+
if (!hasCommentsIds) typesString += commentsIdsDef;
|
|
1924
|
+
}
|
|
1925
|
+
if (hasFile("word/commentsExtensible.xml")) {
|
|
1926
|
+
const commentsExtendedDef = `<Override PartName="/word/commentsExtensible.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml" />`;
|
|
1927
|
+
if (!hasCommentsExtensible) typesString += commentsExtendedDef;
|
|
1928
|
+
}
|
|
1929
|
+
const hasFootnotes = types.elements?.some(
|
|
1930
|
+
(el) => el.name === "Override" && el.attributes.PartName === "/word/footnotes.xml"
|
|
1931
|
+
);
|
|
1932
|
+
if (hasFile("word/footnotes.xml")) {
|
|
1933
|
+
const footnotesDef = `<Override PartName="/word/footnotes.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" />`;
|
|
1934
|
+
if (!hasFootnotes) typesString += footnotesDef;
|
|
1935
|
+
}
|
|
1936
|
+
const partNames = new Set(additionalPartNames);
|
|
1937
|
+
if (docx?.files) {
|
|
1938
|
+
if (fromJson && Array.isArray(docx.files)) {
|
|
1939
|
+
docx.files.forEach((file) => partNames.add(file.name));
|
|
2124
1940
|
} else {
|
|
2125
|
-
|
|
2126
|
-
|
|
1941
|
+
Object.keys(docx.files).forEach((key) => partNames.add(key));
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
partNames.forEach((name) => {
|
|
1945
|
+
if (name.includes(".rels")) return;
|
|
1946
|
+
if (!name.includes("header") && !name.includes("footer")) return;
|
|
1947
|
+
const hasExtensible = types.elements?.some(
|
|
1948
|
+
(el) => el.name === "Override" && el.attributes.PartName === `/${name}`
|
|
1949
|
+
);
|
|
1950
|
+
const type = name.includes("header") ? "header" : "footer";
|
|
1951
|
+
const extendedDef = `<Override PartName="/${name}" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.${type}+xml"/>`;
|
|
1952
|
+
if (!hasExtensible) {
|
|
1953
|
+
typesString += extendedDef;
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
const beginningString = '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
|
|
1957
|
+
let updatedContentTypesXml = contentTypesXml.replace(beginningString, `${beginningString}${typesString}`);
|
|
1958
|
+
let relationshipsXml = updatedDocs["word/_rels/document.xml.rels"];
|
|
1959
|
+
if (!relationshipsXml) {
|
|
1960
|
+
if (fromJson) {
|
|
1961
|
+
if (Array.isArray(docx.files)) {
|
|
1962
|
+
relationshipsXml = docx.files.find((file) => file.name === "word/_rels/document.xml.rels")?.content;
|
|
2127
1963
|
} else {
|
|
2128
|
-
|
|
1964
|
+
relationshipsXml = docx.files?.["word/_rels/document.xml.rels"];
|
|
2129
1965
|
}
|
|
1966
|
+
} else {
|
|
1967
|
+
relationshipsXml = await docx.file("word/_rels/document.xml.rels")?.async("string");
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
if (relationshipsXml) {
|
|
1971
|
+
try {
|
|
1972
|
+
const relJson = helpers.libExports.xml2js(relationshipsXml, { compact: false });
|
|
1973
|
+
const relationships = relJson.elements?.find((el) => el.name === "Relationships");
|
|
1974
|
+
relationships?.elements?.forEach((rel) => {
|
|
1975
|
+
const type = rel.attributes?.Type;
|
|
1976
|
+
const target = rel.attributes?.Target;
|
|
1977
|
+
if (!type || !target) return;
|
|
1978
|
+
const isHeader = type.includes("/header");
|
|
1979
|
+
const isFooter = type.includes("/footer");
|
|
1980
|
+
if (!isHeader && !isFooter) return;
|
|
1981
|
+
let sanitizedTarget = target.replace(/^\.\//, "");
|
|
1982
|
+
if (sanitizedTarget.startsWith("../")) sanitizedTarget = sanitizedTarget.slice(3);
|
|
1983
|
+
if (sanitizedTarget.startsWith("/")) sanitizedTarget = sanitizedTarget.slice(1);
|
|
1984
|
+
const partName = sanitizedTarget.startsWith("word/") ? sanitizedTarget : `word/${sanitizedTarget}`;
|
|
1985
|
+
partNames.add(partName);
|
|
1986
|
+
});
|
|
1987
|
+
} catch (error) {
|
|
1988
|
+
console.warn("Failed to parse document relationships while updating content types", error);
|
|
2130
1989
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
return
|
|
1990
|
+
}
|
|
1991
|
+
partNames.forEach((name) => {
|
|
1992
|
+
if (name.includes(".rels")) return;
|
|
1993
|
+
if (!name.includes("header") && !name.includes("footer")) return;
|
|
1994
|
+
if (updatedContentTypesXml.includes(`PartName="/${name}"`)) return;
|
|
1995
|
+
const type = name.includes("header") ? "header" : "footer";
|
|
1996
|
+
const extendedDef = `<Override PartName="/${name}" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.${type}+xml"/>`;
|
|
1997
|
+
updatedContentTypesXml = updatedContentTypesXml.replace("</Types>", `${extendedDef}</Types>`);
|
|
1998
|
+
});
|
|
1999
|
+
if (fromJson) return updatedContentTypesXml;
|
|
2000
|
+
docx.file(contentTypesPath, updatedContentTypesXml);
|
|
2001
|
+
}
|
|
2002
|
+
async unzip(file) {
|
|
2003
|
+
const zip = await this.zip.loadAsync(file);
|
|
2004
|
+
return zip;
|
|
2005
|
+
}
|
|
2006
|
+
async updateZip({ docx, updatedDocs, originalDocxFile, media, fonts, isHeadless, compression = "DEFLATE" }) {
|
|
2007
|
+
let zip;
|
|
2008
|
+
if (originalDocxFile) {
|
|
2009
|
+
zip = await this.exportFromOriginalFile(originalDocxFile, updatedDocs, media);
|
|
2010
|
+
} else {
|
|
2011
|
+
zip = await this.exportFromCollaborativeDocx(docx, updatedDocs, media, fonts);
|
|
2012
|
+
}
|
|
2013
|
+
const exportType = isHeadless ? "nodebuffer" : "blob";
|
|
2014
|
+
return await zip.generateAsync({
|
|
2015
|
+
type: exportType,
|
|
2016
|
+
compression,
|
|
2017
|
+
compressionOptions: compression === "DEFLATE" ? { level: 6 } : void 0
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Export the Editor content to a docx file, updating changed docs
|
|
2022
|
+
* @param {Object} docx An object containing the unzipped docx files (keys are relative file names)
|
|
2023
|
+
* @param {Object} updatedDocs An object containing the updated docs (keys are relative file names)
|
|
2024
|
+
* @returns {Promise<JSZip>} The unzipped but updated docx file ready for zipping
|
|
2025
|
+
*/
|
|
2026
|
+
async exportFromCollaborativeDocx(docx, updatedDocs, media, fonts) {
|
|
2027
|
+
const zip = new jszip_min.JSZip();
|
|
2028
|
+
for (const file of docx) {
|
|
2029
|
+
const content = file.content;
|
|
2030
|
+
zip.file(file.name, content);
|
|
2031
|
+
}
|
|
2032
|
+
Object.keys(updatedDocs).forEach((key) => {
|
|
2033
|
+
const content = updatedDocs[key];
|
|
2034
|
+
zip.file(key, content);
|
|
2035
|
+
});
|
|
2036
|
+
Object.keys(media).forEach((path) => {
|
|
2037
|
+
const value = media[path];
|
|
2038
|
+
const binaryData = typeof value === "string" ? helpers.base64ToUint8Array(value) : value;
|
|
2039
|
+
zip.file(path, binaryData);
|
|
2040
|
+
});
|
|
2041
|
+
for (const [fontName, fontUintArray] of Object.entries(fonts)) {
|
|
2042
|
+
zip.file(fontName, fontUintArray);
|
|
2043
|
+
}
|
|
2044
|
+
await this.updateContentTypes(zip, media, false, updatedDocs);
|
|
2045
|
+
return zip;
|
|
2046
|
+
}
|
|
2047
|
+
/**
|
|
2048
|
+
* Export the Editor content to a docx file, updating changed docs
|
|
2049
|
+
* Requires the original docx file
|
|
2050
|
+
* @param {File} originalDocxFile The original docx file
|
|
2051
|
+
* @param {Object} updatedDocs An object containing the updated docs (keys are relative file names)
|
|
2052
|
+
* @returns {Promise<JSZip>} The unzipped but updated docx file ready for zipping
|
|
2053
|
+
*/
|
|
2054
|
+
async exportFromOriginalFile(originalDocxFile, updatedDocs, media) {
|
|
2055
|
+
const unzippedOriginalDocx = await this.unzip(originalDocxFile);
|
|
2056
|
+
const filePromises = [];
|
|
2057
|
+
unzippedOriginalDocx.forEach((relativePath, zipEntry) => {
|
|
2058
|
+
const promise = zipEntry.async("string").then((content) => {
|
|
2059
|
+
unzippedOriginalDocx.file(zipEntry.name, content);
|
|
2060
|
+
});
|
|
2061
|
+
filePromises.push(promise);
|
|
2062
|
+
});
|
|
2063
|
+
await Promise.all(filePromises);
|
|
2064
|
+
Object.keys(updatedDocs).forEach((key) => {
|
|
2065
|
+
unzippedOriginalDocx.file(key, updatedDocs[key]);
|
|
2066
|
+
});
|
|
2067
|
+
Object.keys(media).forEach((path) => {
|
|
2068
|
+
unzippedOriginalDocx.file(path, media[path]);
|
|
2069
|
+
});
|
|
2070
|
+
await this.updateContentTypes(unzippedOriginalDocx, media, false, updatedDocs);
|
|
2071
|
+
return unzippedOriginalDocx;
|
|
2134
2072
|
}
|
|
2135
2073
|
}
|
|
2136
2074
|
exports.Buffer = Buffer;
|
|
2137
|
-
exports.
|
|
2138
|
-
exports.degreesToRot = degreesToRot;
|
|
2139
|
-
exports.deobfuscateFont = deobfuscateFont;
|
|
2140
|
-
exports.eighthPointsToPixels = eighthPointsToPixels;
|
|
2141
|
-
exports.emuToPixels = emuToPixels;
|
|
2142
|
-
exports.getArrayBufferFromUrl = getArrayBufferFromUrl;
|
|
2143
|
-
exports.getContentTypesFromXml = getContentTypesFromXml;
|
|
2144
|
-
exports.getDocxHighlightKeywordFromHex = getDocxHighlightKeywordFromHex;
|
|
2145
|
-
exports.getHexColorFromDocxSystem = getHexColorFromDocxSystem;
|
|
2146
|
-
exports.halfPointToPoints = halfPointToPoints;
|
|
2147
|
-
exports.inchesToPixels = inchesToPixels;
|
|
2148
|
-
exports.inchesToTwips = inchesToTwips;
|
|
2149
|
-
exports.isValidHexColor = isValidHexColor;
|
|
2150
|
-
exports.libExports = libExports;
|
|
2151
|
-
exports.linesToTwips = linesToTwips;
|
|
2152
|
-
exports.normalizeHexColor = normalizeHexColor;
|
|
2153
|
-
exports.objToPolygon = objToPolygon;
|
|
2154
|
-
exports.pixelsToEightPoints = pixelsToEightPoints;
|
|
2155
|
-
exports.pixelsToEmu = pixelsToEmu;
|
|
2156
|
-
exports.pixelsToTwips = pixelsToTwips;
|
|
2157
|
-
exports.pointsToTwips = pointsToTwips;
|
|
2158
|
-
exports.polygonToObj = polygonToObj;
|
|
2159
|
-
exports.ptToTwips = ptToTwips;
|
|
2160
|
-
exports.resolveOpcTargetPath = resolveOpcTargetPath;
|
|
2161
|
-
exports.resolveShadingFillColor = resolveShadingFillColor;
|
|
2162
|
-
exports.rgbToHex = rgbToHex;
|
|
2163
|
-
exports.rotToDegrees = rotToDegrees;
|
|
2164
|
-
exports.twipsToInches = twipsToInches;
|
|
2165
|
-
exports.twipsToLines = twipsToLines;
|
|
2166
|
-
exports.twipsToPixels = twipsToPixels;
|
|
2167
|
-
exports.twipsToPt = twipsToPt;
|
|
2075
|
+
exports.DocxZipper = DocxZipper;
|