@stll/folio-core 0.1.2 → 0.2.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/docx/headerFooterParser.js +3 -0
- package/dist/docx/headerFooterVerbatim.d.ts +19 -0
- package/dist/docx/headerFooterVerbatim.js +25 -0
- package/dist/docx/index.d.ts +2 -0
- package/dist/docx/index.js +2 -0
- package/dist/docx/metafileRaster.d.ts +30 -0
- package/dist/docx/metafileRaster.js +80 -0
- package/dist/docx/paragraphParser.js +5 -1
- package/dist/docx/parser.d.ts +12 -2
- package/dist/docx/parser.js +31 -1
- package/dist/docx/serializer/headerFooterSerializer.js +3 -0
- package/dist/docx/styleParser.js +2 -2
- package/dist/docx/tableParser.js +2 -3
- package/dist/docx/unzip.js +7 -1
- package/dist/docx/wrapTypes.d.ts +8 -1
- package/dist/docx/wrapTypes.js +0 -8
- package/dist/docx/xmlParser.d.ts +7 -1
- package/dist/docx/xmlParser.js +17 -1
- package/dist/i18n/messages/catalogs.gen.d.ts +2776 -0
- package/dist/i18n/messages/catalogs.gen.js +2742 -0
- package/dist/i18n/messages/messages.gen.d.ts +166 -0
- package/dist/i18n/messages/messages.gen.js +0 -0
- package/dist/i18n/messages.d.ts +13 -0
- package/dist/i18n/messages.js +39 -0
- package/dist/layout-bridge/cellSelectionHighlight.d.ts +18 -0
- package/dist/layout-bridge/cellSelectionHighlight.js +36 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -2
- package/dist/layout-bridge/convert/headerFooterLayout.js +39 -15
- package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
- package/dist/layout-bridge/headerFooterLayout.d.ts +50 -0
- package/dist/layout-bridge/headerFooterLayout.js +216 -0
- package/dist/layout-bridge/measuring/measureBlocksPipeline.d.ts +33 -0
- package/dist/layout-bridge/measuring/measureBlocksPipeline.js +0 -0
- package/dist/layout-bridge/sectionColumns.d.ts +11 -0
- package/dist/layout-bridge/sectionColumns.js +21 -0
- package/dist/layout-bridge/tableInsertHover.d.ts +42 -0
- package/dist/layout-bridge/tableInsertHover.js +102 -0
- package/dist/layout-engine/index.d.ts +2 -1
- package/dist/layout-engine/index.js +2 -1
- package/dist/layout-engine/measure/lineBreaks.d.ts +10 -0
- package/dist/layout-engine/measure/lineBreaks.js +30 -0
- package/dist/layout-engine/measure/measureContainer.js +1 -1
- package/dist/layout-engine/measure/measureParagraph.js +63 -22
- package/dist/layout-engine/pmPageIndex.d.ts +24 -0
- package/dist/layout-engine/pmPageIndex.js +37 -0
- package/dist/layout-engine/types.d.ts +2 -1
- package/dist/layout-painter/imageLayout.d.ts +101 -0
- package/dist/layout-painter/imageLayout.js +161 -0
- package/dist/layout-painter/renderPage.d.ts +16 -1
- package/dist/layout-painter/renderPage.js +28 -1
- package/dist/layout-painter/renderParagraph.js +2 -1
- package/dist/layout-painter/sdtBoundary.d.ts +24 -1
- package/dist/layout-painter/sdtBoundary.js +40 -1
- package/dist/managers/AutoSaveManager.d.ts +89 -0
- package/dist/managers/AutoSaveManager.js +279 -0
- package/dist/managers/ClipboardManager.d.ts +31 -0
- package/dist/managers/ClipboardManager.js +147 -0
- package/dist/paged-layout/paragraphFlash.d.ts +17 -0
- package/dist/paged-layout/paragraphFlash.js +56 -0
- package/dist/paged-layout/paragraphFlashTypes.d.ts +18 -0
- package/dist/paged-layout/paragraphFlashTypes.js +0 -0
- package/dist/paged-layout/rangeProjection.d.ts +23 -1
- package/dist/paged-layout/rangeProjection.js +34 -1
- package/dist/prosemirror/cellDragSelection.d.ts +34 -0
- package/dist/prosemirror/cellDragSelection.js +69 -0
- package/dist/prosemirror/commands/sectionBreak.d.ts +15 -0
- package/dist/prosemirror/commands/sectionBreak.js +54 -0
- package/dist/prosemirror/commentIdAllocator.d.ts +32 -0
- package/dist/prosemirror/commentIdAllocator.js +35 -0
- package/dist/prosemirror/commentOps.d.ts +35 -0
- package/dist/prosemirror/commentOps.js +104 -0
- package/dist/prosemirror/conversion/toProseDoc.js +29 -6
- package/dist/prosemirror/extensions/index.d.ts +3 -0
- package/dist/prosemirror/extensions/index.js +3 -0
- package/dist/prosemirror/imageCommit.d.ts +42 -0
- package/dist/prosemirror/imageCommit.js +118 -0
- package/dist/prosemirror/paraText.d.ts +26 -0
- package/dist/prosemirror/paraText.js +73 -0
- package/dist/prosemirror/plugins/templateDirectives.d.ts +21 -1
- package/dist/prosemirror/plugins/templateDirectives.js +40 -1
- package/dist/prosemirror/queries.d.ts +46 -0
- package/dist/prosemirror/queries.js +74 -0
- package/dist/prosemirror/styles/styleResolver.d.ts +43 -1
- package/dist/prosemirror/styles/styleResolver.js +34 -0
- package/dist/prosemirror/tableResize.d.ts +49 -0
- package/dist/prosemirror/tableResize.js +162 -0
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +106 -0
- package/dist/prosemirror/utils/extractTrackedChanges.js +379 -0
- package/dist/prosemirror/utils/visualLineNavigation.d.ts +27 -0
- package/dist/prosemirror/utils/visualLineNavigation.js +217 -0
- package/dist/style-engine/index.d.ts +2 -2
- package/dist/style-engine/styleEngine.d.ts +14 -1
- package/dist/style-engine/styleEngine.js +15 -0
- package/dist/utils/colorResolver.d.ts +8 -1
- package/dist/utils/colorResolver.js +12 -1
- package/dist/utils/findVerticalScrollParent.d.ts +20 -0
- package/dist/utils/findVerticalScrollParent.js +30 -0
- package/dist/utils/fontResolver.d.ts +22 -3
- package/dist/utils/fontResolver.js +216 -31
- package/dist/utils/headerFooter.js +2 -0
- package/package.json +9 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { collectXmlnsDeclarations, parseXml } from "./xmlParser.js";
|
|
2
2
|
import { parseWatermark } from "./watermarkParser.js";
|
|
3
3
|
import { parseFooterReference, parseFooterReferences, parseHeaderReference, parseHeaderReferences } from "./headerFooterRefParser.js";
|
|
4
|
+
import { assignHeaderFooterVerbatimXml } from "./headerFooterVerbatim.js";
|
|
4
5
|
import { parseBlockContent } from "./blockContentParser.js";
|
|
5
6
|
//#region src/docx/headerFooterParser.ts
|
|
6
7
|
/**
|
|
@@ -34,6 +35,7 @@ function parseHeader(headerXml, hdrFtrType = "default", styles = null, theme = n
|
|
|
34
35
|
inHeaderFooter: true,
|
|
35
36
|
rootXmlns: collectXmlnsDeclarations(rootElement)
|
|
36
37
|
});
|
|
38
|
+
assignHeaderFooterVerbatimXml(result, headerXml);
|
|
37
39
|
return result;
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
@@ -74,6 +76,7 @@ function parseFooter(footerXml, hdrFtrType = "default", styles = null, theme = n
|
|
|
74
76
|
inHeaderFooter: true,
|
|
75
77
|
rootXmlns: collectXmlnsDeclarations(rootElement)
|
|
76
78
|
});
|
|
79
|
+
assignHeaderFooterVerbatimXml(result, footerXml);
|
|
77
80
|
return result;
|
|
78
81
|
}
|
|
79
82
|
/**
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
|
|
3
|
+
//#region src/docx/headerFooterVerbatim.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Folio extension on {@link HeaderFooter}: original part XML captured at parse
|
|
6
|
+
* time so unedited headers/footers re-emit byte-identically on save (VML OLE
|
|
7
|
+
* wrappers, smart tags, and other constructs the model cannot fully represent).
|
|
8
|
+
* Cleared on first edit.
|
|
9
|
+
*/
|
|
10
|
+
type HeaderFooterWithVerbatim = document_d_exports.HeaderFooter & {
|
|
11
|
+
verbatimXml?: string; /** Fingerprint of modeled fields at parse time; verbatim replay is safe only while it matches. */
|
|
12
|
+
verbatimFingerprint?: string;
|
|
13
|
+
};
|
|
14
|
+
declare const getHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter) => string | undefined;
|
|
15
|
+
declare const canReplayHeaderFooterVerbatim: (hf: document_d_exports.HeaderFooter) => boolean;
|
|
16
|
+
declare const assignHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter, xml: string) => void;
|
|
17
|
+
declare const clearHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter) => void;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { HeaderFooterWithVerbatim, assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/docx/headerFooterVerbatim.ts
|
|
2
|
+
const headerFooterSerializationFingerprint = (hf) => JSON.stringify({
|
|
3
|
+
content: hf.content,
|
|
4
|
+
watermark: hf.watermark,
|
|
5
|
+
watermarkBlockIndex: hf.watermarkBlockIndex,
|
|
6
|
+
rawWatermarkXml: hf.rawWatermarkXml
|
|
7
|
+
});
|
|
8
|
+
const getHeaderFooterVerbatimXml = (hf) => hf.verbatimXml;
|
|
9
|
+
const canReplayHeaderFooterVerbatim = (hf) => {
|
|
10
|
+
const ext = hf;
|
|
11
|
+
if (!ext.verbatimXml || !ext.verbatimFingerprint) return false;
|
|
12
|
+
return ext.verbatimFingerprint === headerFooterSerializationFingerprint(hf);
|
|
13
|
+
};
|
|
14
|
+
const assignHeaderFooterVerbatimXml = (hf, xml) => {
|
|
15
|
+
const ext = hf;
|
|
16
|
+
ext.verbatimXml = xml;
|
|
17
|
+
ext.verbatimFingerprint = headerFooterSerializationFingerprint(hf);
|
|
18
|
+
};
|
|
19
|
+
const clearHeaderFooterVerbatimXml = (hf) => {
|
|
20
|
+
const ext = hf;
|
|
21
|
+
delete ext.verbatimXml;
|
|
22
|
+
delete ext.verbatimFingerprint;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/docx/metafileRaster.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Extract a browser-renderable raster (PNG/JPEG) embedded inside an
|
|
4
|
+
* EMF/WMF metafile.
|
|
5
|
+
*
|
|
6
|
+
* Word frequently stores header logos / OLE preview pictures as EMF — a
|
|
7
|
+
* Windows GDI metafile browsers cannot decode. In practice such an EMF almost
|
|
8
|
+
* always carries the actual artwork as a single embedded PNG or JPEG (an
|
|
9
|
+
* `EmfPlusObject` bitmap record, or a `StretchDIBits` payload). Rather than
|
|
10
|
+
* implement a GDI renderer, this scans the byte stream for a raster signature,
|
|
11
|
+
* brackets it to its container's end marker, and returns the slice. The caller
|
|
12
|
+
* uses it as the media entry's `dataUrl` so `<img>` just works; the original
|
|
13
|
+
* EMF bytes stay on `MediaFile.data` for round-trip.
|
|
14
|
+
*
|
|
15
|
+
* When no embedded raster is found this returns `null`; callers fall back to a
|
|
16
|
+
* sized placeholder and/or the host-supplied `mediaResolver` hook.
|
|
17
|
+
*/
|
|
18
|
+
type ExtractedRaster = {
|
|
19
|
+
bytes: Uint8Array;
|
|
20
|
+
mimeType: "image/png" | "image/jpeg";
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Scan a metafile (EMF or WMF) for an embedded browser-renderable raster.
|
|
24
|
+
* Returns the first PNG, else first JPEG found; `null` when none is present.
|
|
25
|
+
*/
|
|
26
|
+
declare function extractMetafileRaster(data: ArrayBuffer | Uint8Array): ExtractedRaster | null;
|
|
27
|
+
/** True for EMF/WMF MIME types — the formats browsers cannot render natively. */
|
|
28
|
+
declare function isMetafileMimeType(mimeType: string | undefined): boolean;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { ExtractedRaster, extractMetafileRaster, isMetafileMimeType };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//#region src/docx/metafileRaster.ts
|
|
2
|
+
function indexOfBytes(haystack, needle, from = 0) {
|
|
3
|
+
outer: for (let i = from; i + needle.length <= haystack.length; i++) {
|
|
4
|
+
for (let j = 0; j < needle.length; j++) if (haystack[i + j] !== needle[j]) continue outer;
|
|
5
|
+
return i;
|
|
6
|
+
}
|
|
7
|
+
return -1;
|
|
8
|
+
}
|
|
9
|
+
function extractPng(bytes) {
|
|
10
|
+
const start = indexOfBytes(bytes, [
|
|
11
|
+
137,
|
|
12
|
+
80,
|
|
13
|
+
78,
|
|
14
|
+
71,
|
|
15
|
+
13,
|
|
16
|
+
10,
|
|
17
|
+
26,
|
|
18
|
+
10
|
|
19
|
+
]);
|
|
20
|
+
if (start < 0) return null;
|
|
21
|
+
let off = start + 8;
|
|
22
|
+
while (off + 12 <= bytes.length) {
|
|
23
|
+
const b0 = bytes[off];
|
|
24
|
+
const b1 = bytes[off + 1];
|
|
25
|
+
const b2 = bytes[off + 2];
|
|
26
|
+
const b3 = bytes[off + 3];
|
|
27
|
+
const len = b0 << 24 | b1 << 16 | b2 << 8 | b3;
|
|
28
|
+
const type = String.fromCharCode(bytes[off + 4], bytes[off + 5], bytes[off + 6], bytes[off + 7]);
|
|
29
|
+
const next = off + 12 + (len >>> 0);
|
|
30
|
+
if (type === "IEND") return bytes.slice(start, off + 12);
|
|
31
|
+
if (next <= off || next > bytes.length) break;
|
|
32
|
+
off = next;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
function extractJpeg(bytes) {
|
|
37
|
+
const start = indexOfBytes(bytes, [
|
|
38
|
+
255,
|
|
39
|
+
216,
|
|
40
|
+
255
|
|
41
|
+
]);
|
|
42
|
+
if (start < 0) return null;
|
|
43
|
+
let end = -1;
|
|
44
|
+
for (let i = bytes.length - 2; i > start + 1; i--) if (bytes[i] === 255 && bytes[i + 1] === 217) {
|
|
45
|
+
end = i + 2;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
return end > start ? bytes.slice(start, end) : null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Scan a metafile (EMF or WMF) for an embedded browser-renderable raster.
|
|
52
|
+
* Returns the first PNG, else first JPEG found; `null` when none is present.
|
|
53
|
+
*/
|
|
54
|
+
function extractMetafileRaster(data) {
|
|
55
|
+
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
56
|
+
if (bytes.length < 32) return null;
|
|
57
|
+
const png = extractPng(bytes);
|
|
58
|
+
if (png) return {
|
|
59
|
+
bytes: png,
|
|
60
|
+
mimeType: "image/png"
|
|
61
|
+
};
|
|
62
|
+
const jpeg = extractJpeg(bytes);
|
|
63
|
+
if (jpeg) return {
|
|
64
|
+
bytes: jpeg,
|
|
65
|
+
mimeType: "image/jpeg"
|
|
66
|
+
};
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const METAFILE_MIME = /* @__PURE__ */ new Set([
|
|
70
|
+
"image/x-emf",
|
|
71
|
+
"image/emf",
|
|
72
|
+
"image/x-wmf",
|
|
73
|
+
"image/wmf"
|
|
74
|
+
]);
|
|
75
|
+
/** True for EMF/WMF MIME types — the formats browsers cannot render natively. */
|
|
76
|
+
function isMetafileMimeType(mimeType) {
|
|
77
|
+
return !!mimeType && METAFILE_MIME.has(mimeType);
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { extractMetafileRaster, isMetafileMimeType };
|
|
@@ -815,7 +815,11 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
|
|
|
815
815
|
parsedContent: parseParagraphContents(child, styles, theme, null, rels, media, "default", inScopeXmlns)
|
|
816
816
|
});
|
|
817
817
|
break;
|
|
818
|
-
case "smartTag":
|
|
818
|
+
case "smartTag": {
|
|
819
|
+
const inner = parseParagraphContents(child, styles, theme, null, rels, media, trackedContext, mergeXmlnsDeclarations(inScopeXmlns, child));
|
|
820
|
+
contents.push(...inner);
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
819
823
|
case "moveFromRangeStart": {
|
|
820
824
|
const id = Number.parseInt(getAttribute(child, "w", "id") ?? "0", 10);
|
|
821
825
|
const name = getAttribute(child, "w", "name") ?? "";
|
package/dist/docx/parser.d.ts
CHANGED
|
@@ -7,6 +7,15 @@ import { DocxUnzipLimits } from "./unzip.js";
|
|
|
7
7
|
* Progress callback for tracking parsing stages
|
|
8
8
|
*/
|
|
9
9
|
type ProgressCallback = (stage: string, percent: number) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Host hook for converting media the browser cannot render natively
|
|
12
|
+
* (EMF/WMF/TIFF) into a displayable `data:` or `blob:` URL. Receives the
|
|
13
|
+
* parsed {@link MediaFile} (original bytes on `.data`); return the replacement
|
|
14
|
+
* URL, or `null`/`undefined` to keep the built-in handling. Built-in handling
|
|
15
|
+
* already extracts an embedded PNG/JPEG from EMF/WMF when one exists; this
|
|
16
|
+
* hook is for vector-only metafiles where the host rasterizes server-side.
|
|
17
|
+
*/
|
|
18
|
+
type MediaResolver = (file: document_d_exports.MediaFile) => Promise<string | null | undefined>;
|
|
10
19
|
/**
|
|
11
20
|
* Parsing options
|
|
12
21
|
*/
|
|
@@ -18,7 +27,8 @@ type ParseOptions = {
|
|
|
18
27
|
detectVariables?: boolean; /** Security limits for DOCX ZIP extraction */
|
|
19
28
|
unzipLimits?: Partial<Omit<DocxUnzipLimits, "allowedMediaMimeTypes">> & {
|
|
20
29
|
allowedMediaMimeTypes?: Iterable<string>;
|
|
21
|
-
};
|
|
30
|
+
}; /** Optional async hook to override display URLs for non-browser media. */
|
|
31
|
+
mediaResolver?: MediaResolver;
|
|
22
32
|
};
|
|
23
33
|
/**
|
|
24
34
|
* Parse a DOCX file into a complete Document model
|
|
@@ -64,4 +74,4 @@ declare function getDocxSummary(buffer: ArrayBuffer): Promise<{
|
|
|
64
74
|
variableCount: number;
|
|
65
75
|
}>;
|
|
66
76
|
//#endregion
|
|
67
|
-
export { DocxParseError, ParseOptions, ProgressCallback, fullParseDocx, getDocxSummary, getDocxVariables, parseDocx, quickParseDocx };
|
|
77
|
+
export { DocxParseError, MediaResolver, ParseOptions, ProgressCallback, fullParseDocx, getDocxSummary, getDocxVariables, parseDocx, quickParseDocx };
|
package/dist/docx/parser.js
CHANGED
|
@@ -12,6 +12,7 @@ import { normalizeCommentReferences } from "./commentReferenceNormalization.js";
|
|
|
12
12
|
import { extractAllTemplateVariables, parseDocumentBody } from "./documentParser.js";
|
|
13
13
|
import { parseFooter, parseHeader } from "./headerFooterParser.js";
|
|
14
14
|
import { normalizeHeaderFooterReferences } from "./headerFooterReferenceNormalization.js";
|
|
15
|
+
import { extractMetafileRaster, isMetafileMimeType } from "./metafileRaster.js";
|
|
15
16
|
import { normalizeNumberingReferences } from "./numberingReferenceNormalization.js";
|
|
16
17
|
import { parseSettings } from "./settingsParser.js";
|
|
17
18
|
import { parseStylesPackage } from "./styleParser.js";
|
|
@@ -47,7 +48,7 @@ import { TaggedError } from "better-result";
|
|
|
47
48
|
*/
|
|
48
49
|
async function parseDocx(input, options = {}) {
|
|
49
50
|
const buffer = input instanceof ArrayBuffer ? input : await toArrayBuffer(input);
|
|
50
|
-
const { onProgress = () => {}, preloadFonts = true, parseHeadersFooters = true, parseNotes = true, detectVariables = true, unzipLimits } = options;
|
|
51
|
+
const { onProgress = () => {}, preloadFonts = true, parseHeadersFooters = true, parseNotes = true, detectVariables = true, unzipLimits, mediaResolver } = options;
|
|
51
52
|
const warnings = [];
|
|
52
53
|
try {
|
|
53
54
|
const timeStage = (_name, fn) => fn();
|
|
@@ -80,6 +81,7 @@ async function parseDocx(input, options = {}) {
|
|
|
80
81
|
onProgress("Parsed numbering", 35);
|
|
81
82
|
onProgress("Processing media files...", 35);
|
|
82
83
|
const media = await timeStageAsync("media", () => buildMediaMap(raw, rels));
|
|
84
|
+
if (mediaResolver) await timeStageAsync("mediaResolver", () => applyMediaResolver(media, mediaResolver));
|
|
83
85
|
onProgress("Processed media", 40);
|
|
84
86
|
onProgress("Parsing document body...", 40);
|
|
85
87
|
let documentBody = { content: [] };
|
|
@@ -196,6 +198,11 @@ var DocxParseError = class extends TaggedError("DocxParseError")() {};
|
|
|
196
198
|
/**
|
|
197
199
|
* Build media file map from raw content and relationships
|
|
198
200
|
*/
|
|
201
|
+
function copyBytesToArrayBuffer(bytes) {
|
|
202
|
+
const buffer = new ArrayBuffer(bytes.byteLength);
|
|
203
|
+
new Uint8Array(buffer).set(bytes);
|
|
204
|
+
return buffer;
|
|
205
|
+
}
|
|
199
206
|
async function buildMediaMap(raw, _rels) {
|
|
200
207
|
const media = /* @__PURE__ */ new Map();
|
|
201
208
|
for (const [path, data] of raw.media.entries()) {
|
|
@@ -217,6 +224,20 @@ async function buildMediaMap(raw, _rels) {
|
|
|
217
224
|
continue;
|
|
218
225
|
}
|
|
219
226
|
}
|
|
227
|
+
const raster = isMetafileMimeType(mimeType) ? extractMetafileRaster(data) : null;
|
|
228
|
+
if (raster) {
|
|
229
|
+
const mediaFile = {
|
|
230
|
+
path,
|
|
231
|
+
filename,
|
|
232
|
+
mimeType,
|
|
233
|
+
data,
|
|
234
|
+
dataUrl: mediaToDataUrl(copyBytesToArrayBuffer(raster.bytes), raster.mimeType)
|
|
235
|
+
};
|
|
236
|
+
media.set(path, mediaFile);
|
|
237
|
+
const normalizedPath = path.replace(/^word\//u, "");
|
|
238
|
+
if (normalizedPath !== path) media.set(normalizedPath, mediaFile);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
220
241
|
const mediaFile = {
|
|
221
242
|
path,
|
|
222
243
|
filename,
|
|
@@ -230,6 +251,15 @@ async function buildMediaMap(raw, _rels) {
|
|
|
230
251
|
}
|
|
231
252
|
return media;
|
|
232
253
|
}
|
|
254
|
+
async function applyMediaResolver(media, resolver) {
|
|
255
|
+
const files = [...new Set(media.values())];
|
|
256
|
+
await Promise.all(files.map(async (file) => {
|
|
257
|
+
try {
|
|
258
|
+
const url = await resolver(file);
|
|
259
|
+
if (url) file.dataUrl = url;
|
|
260
|
+
} catch {}
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
233
263
|
function attachLazyDataUrl(mediaFile) {
|
|
234
264
|
let cachedDataUrl;
|
|
235
265
|
Object.defineProperty(mediaFile, "dataUrl", {
|
|
@@ -2,6 +2,7 @@ import { escapeXml } from "./xmlUtils.js";
|
|
|
2
2
|
import { serializeBlockSdt } from "./blockSdtSerializer.js";
|
|
3
3
|
import { serializeParagraph } from "./paragraphSerializer.js";
|
|
4
4
|
import { serializeTable } from "./tableSerializer.js";
|
|
5
|
+
import { canReplayHeaderFooterVerbatim, getHeaderFooterVerbatimXml } from "../headerFooterVerbatim.js";
|
|
5
6
|
//#region src/docx/serializer/headerFooterSerializer.ts
|
|
6
7
|
const NAMESPACES = {
|
|
7
8
|
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
|
@@ -45,6 +46,8 @@ function serializeBlock(block) {
|
|
|
45
46
|
* @returns Complete XML string for header*.xml or footer*.xml
|
|
46
47
|
*/
|
|
47
48
|
function serializeHeaderFooter(hf) {
|
|
49
|
+
const verbatim = getHeaderFooterVerbatimXml(hf);
|
|
50
|
+
if (verbatim && canReplayHeaderFooterVerbatim(hf)) return verbatim;
|
|
48
51
|
const rootTag = hf.type === "header" ? "w:hdr" : "w:ftr";
|
|
49
52
|
const nsDecl = buildNamespaceDeclarations();
|
|
50
53
|
const watermarkXml = serializeWatermarkParagraph(hf);
|
package/dist/docx/styleParser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findChild, findChildren, getAttribute, getLocalName, parseBooleanElement, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
|
|
1
|
+
import { findChild, findChildren, getAttribute, getLocalName, parseBooleanElement, parseNumericAttribute, parseTableMeasurementValue, parseXmlDocument } from "./xmlParser.js";
|
|
2
2
|
import { BorderStyleSchema, ConditionalStyleTypeSchema, EmphasisMarkSchema, FontThemeSchema, HighlightColorSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, ShadingPatternSchema, StyleTypeSchema, TabLeaderSchema, TabStopAlignmentSchema, TableCellTextDirectionSchema, TableRowHeightRuleSchema, TableWidthTypeSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
|
|
3
3
|
import { resolveThemeFontRef } from "./themeParser.js";
|
|
4
4
|
import { mergeTextFormatting } from "../utils/textFormattingMerge.js";
|
|
@@ -360,9 +360,9 @@ function parseParagraphProperties(pPr, theme) {
|
|
|
360
360
|
*/
|
|
361
361
|
function parseTableMeasurement(element) {
|
|
362
362
|
if (!element) return;
|
|
363
|
-
const w = parseNumericAttribute(element, "w", "w");
|
|
364
363
|
const rawType = getAttribute(element, "w", "type");
|
|
365
364
|
const type = rawType === null ? "dxa" : narrowEnum(rawType, TableWidthTypeSchema);
|
|
365
|
+
const w = type ? parseTableMeasurementValue(element, type) : void 0;
|
|
366
366
|
if (w !== void 0 && type) return {
|
|
367
367
|
value: w,
|
|
368
368
|
type
|
package/dist/docx/tableParser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findChild, findChildByLocalName, findChildren, getAttribute, getChildElements, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
|
|
1
|
+
import { findChild, findChildByLocalName, findChildren, getAttribute, getChildElements, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute, parseTableMeasurementValue } from "./xmlParser.js";
|
|
2
2
|
import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
|
|
3
3
|
import { BorderStyleSchema, FloatingTableXSpecSchema, FloatingTableYSpecSchema, ShadingPatternSchema, TableCellTextDirectionSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
|
|
4
4
|
import { parseParagraph } from "./paragraphParser.js";
|
|
@@ -12,12 +12,11 @@ import { appendBookmarkMarkerToLastParagraphInBlocks, appendBookmarkMarkerToLast
|
|
|
12
12
|
*/
|
|
13
13
|
function parseTableMeasurement(element) {
|
|
14
14
|
if (!element) return;
|
|
15
|
-
const value = parseNumericAttribute(element, "w", "w") ?? 0;
|
|
16
15
|
const typeStr = getAttribute(element, "w", "type") ?? "dxa";
|
|
17
16
|
let type = "dxa";
|
|
18
17
|
if (typeStr === "auto" || typeStr === "dxa" || typeStr === "nil" || typeStr === "pct") type = typeStr;
|
|
19
18
|
return {
|
|
20
|
-
value,
|
|
19
|
+
value: parseTableMeasurementValue(element, type) ?? 0,
|
|
21
20
|
type
|
|
22
21
|
};
|
|
23
22
|
}
|
package/dist/docx/unzip.js
CHANGED
|
@@ -37,7 +37,9 @@ const DEFAULT_ALLOWED_MEDIA_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
|
37
37
|
"image/gif",
|
|
38
38
|
"image/bmp",
|
|
39
39
|
"image/tiff",
|
|
40
|
-
"image/webp"
|
|
40
|
+
"image/webp",
|
|
41
|
+
"image/x-emf",
|
|
42
|
+
"image/x-wmf"
|
|
41
43
|
]);
|
|
42
44
|
const PRESERVABLE_MEDIA_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
43
45
|
...DEFAULT_ALLOWED_MEDIA_MIME_TYPES,
|
|
@@ -279,6 +281,10 @@ function isMediaContentAllowed(data, mimeType) {
|
|
|
279
281
|
case "image/bmp": return bytes[0] === 66 && bytes[1] === 77;
|
|
280
282
|
case "image/webp": return bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80;
|
|
281
283
|
case "image/tiff": return bytes[0] === 73 && bytes[1] === 73 || bytes[0] === 77 && bytes[1] === 77;
|
|
284
|
+
case "image/x-emf":
|
|
285
|
+
case "image/emf": return bytes.length >= 44 && bytes[0] === 1 && bytes[40] === 32 && bytes[41] === 69 && bytes[42] === 77 && bytes[43] === 70;
|
|
286
|
+
case "image/x-wmf":
|
|
287
|
+
case "image/wmf": return bytes.length >= 4 && (bytes[0] === 215 && bytes[1] === 205 && bytes[2] === 198 && bytes[3] === 154 || (bytes[0] === 1 || bytes[0] === 2) && bytes[1] === 0 && bytes[2] === 9 && bytes[3] === 0);
|
|
282
288
|
default: return false;
|
|
283
289
|
}
|
|
284
290
|
}
|
package/dist/docx/wrapTypes.d.ts
CHANGED
|
@@ -7,6 +7,13 @@
|
|
|
7
7
|
* which OOXML wrap variants are floating, which paint over body text, and
|
|
8
8
|
* which wrap around it.
|
|
9
9
|
*/
|
|
10
|
+
/**
|
|
11
|
+
* OOXML text-wrap variants for a drawing anchor (`wp:inline` vs the
|
|
12
|
+
* `wp:wrap*` elements). `inline` keeps the object in the text run; every other
|
|
13
|
+
* variant takes it out of inline flow. Mirrors the eigenpal docx-editor union
|
|
14
|
+
* so adapters share one wrap vocabulary.
|
|
15
|
+
*/
|
|
16
|
+
type WrapType = "inline" | "square" | "tight" | "through" | "topAndBottom" | "behind" | "inFront";
|
|
10
17
|
/**
|
|
11
18
|
* `true` for any wrap type that takes the object out of inline flow.
|
|
12
19
|
* Includes `topAndBottom` and the wrapNone variants (`behind`/`inFront`):
|
|
@@ -26,4 +33,4 @@ declare function isWrapNone(wrapType: string | undefined): boolean;
|
|
|
26
33
|
*/
|
|
27
34
|
declare function wrapsAroundText(wrapType: string | undefined): boolean;
|
|
28
35
|
//#endregion
|
|
29
|
-
export { isFloatingWrapType, isWrapNone, wrapsAroundText };
|
|
36
|
+
export { WrapType, isFloatingWrapType, isWrapNone, wrapsAroundText };
|
package/dist/docx/wrapTypes.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
//#region src/docx/wrapTypes.ts
|
|
2
|
-
/**
|
|
3
|
-
* Wrap-type predicates shared across the layout pipeline.
|
|
4
|
-
*
|
|
5
|
-
* Mirrors eigenpal docx-editor `wrapTypes` so that the new textBoxFlow
|
|
6
|
-
* predicates (eigenpal #474) and the existing image classifiers agree on
|
|
7
|
-
* which OOXML wrap variants are floating, which paint over body text, and
|
|
8
|
-
* which wrap around it.
|
|
9
|
-
*/
|
|
10
2
|
const FLOATING_WRAP_TYPES = /* @__PURE__ */ new Set([
|
|
11
3
|
"square",
|
|
12
4
|
"tight",
|
package/dist/docx/xmlParser.d.ts
CHANGED
|
@@ -237,6 +237,12 @@ declare function parseColorElement(element: XmlElement | null | undefined): {
|
|
|
237
237
|
* @returns Parsed number or undefined
|
|
238
238
|
*/
|
|
239
239
|
declare function parseNumericAttribute(element: XmlElement | null | undefined, namespace: string | null, name: string, scale?: number): number | undefined;
|
|
240
|
+
/**
|
|
241
|
+
* Parse `w:w` on a table width/height element. For `w:type="pct"`, producers
|
|
242
|
+
* sometimes emit human-readable percentages (`100%`) instead of 50ths-of-percent
|
|
243
|
+
* (`5000`); normalize those to the ECMA-376 unit the layout engine expects.
|
|
244
|
+
*/
|
|
245
|
+
declare function parseTableMeasurementValue(element: XmlElement | null | undefined, widthType: string): number | undefined;
|
|
240
246
|
/**
|
|
241
247
|
* Parse a boolean value from an attribute or element presence
|
|
242
248
|
*
|
|
@@ -294,4 +300,4 @@ declare function mergeXmlnsDeclarations(inherited: Record<string, string>, eleme
|
|
|
294
300
|
*/
|
|
295
301
|
declare function cloneWithXmlnsDeclarations(element: XmlElement, xmlnsDecls: Record<string, string>): XmlElement;
|
|
296
302
|
//#endregion
|
|
297
|
-
export { NAMESPACES, XmlElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumericAttribute, parseXml, parseXmlDocument };
|
|
303
|
+
export { NAMESPACES, XmlElement, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
package/dist/docx/xmlParser.js
CHANGED
|
@@ -409,6 +409,22 @@ function parseNumericAttribute(element, namespace, name, scale = 1) {
|
|
|
409
409
|
return num * scale;
|
|
410
410
|
}
|
|
411
411
|
/**
|
|
412
|
+
* Parse `w:w` on a table width/height element. For `w:type="pct"`, producers
|
|
413
|
+
* sometimes emit human-readable percentages (`100%`) instead of 50ths-of-percent
|
|
414
|
+
* (`5000`); normalize those to the ECMA-376 unit the layout engine expects.
|
|
415
|
+
*/
|
|
416
|
+
function parseTableMeasurementValue(element, widthType) {
|
|
417
|
+
const raw = getAttribute(element, "w", "w");
|
|
418
|
+
if (raw === null) return;
|
|
419
|
+
const trimmed = raw.trim();
|
|
420
|
+
if (widthType === "pct" && trimmed.endsWith("%")) {
|
|
421
|
+
const pct = Number.parseFloat(trimmed.slice(0, -1));
|
|
422
|
+
if (!Number.isNaN(pct)) return Math.round(pct * 50);
|
|
423
|
+
}
|
|
424
|
+
const num = Number.parseInt(trimmed, 10);
|
|
425
|
+
return Number.isNaN(num) ? void 0 : num;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
412
428
|
* Parse a boolean value from an attribute or element presence
|
|
413
429
|
*
|
|
414
430
|
* OOXML boolean conventions:
|
|
@@ -524,4 +540,4 @@ function cloneWithXmlnsDeclarations(element, xmlnsDecls) {
|
|
|
524
540
|
};
|
|
525
541
|
}
|
|
526
542
|
//#endregion
|
|
527
|
-
export { NAMESPACES, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumericAttribute, parseXml, parseXmlDocument };
|
|
543
|
+
export { NAMESPACES, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumericAttribute, parseTableMeasurementValue, parseXml, parseXmlDocument };
|