@cj-tech-master/excelts 9.6.1 → 10.0.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/README.md +18 -3
- package/README_zh.md +18 -3
- package/dist/browser/modules/excel/cell.d.ts +4 -0
- package/dist/browser/modules/excel/note.js +5 -1
- package/dist/browser/modules/excel/row.js +35 -2
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/browser/modules/excel/types.d.ts +81 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +8 -0
- package/dist/browser/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/browser/modules/excel/workbook.browser.d.ts +16 -0
- package/dist/browser/modules/excel/workbook.browser.js +32 -2
- package/dist/browser/modules/excel/worksheet.d.ts +31 -1
- package/dist/browser/modules/excel/worksheet.js +83 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/browser/modules/pdf/builder/document-builder.js +22 -49
- package/dist/browser/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/browser/modules/pdf/core/pdf-stream.d.ts +28 -1
- package/dist/browser/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/browser/modules/pdf/font/font-manager.d.ts +26 -0
- package/dist/browser/modules/pdf/font/font-manager.js +35 -18
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +51 -3
- package/dist/browser/modules/pdf/render/page-renderer.js +111 -18
- package/dist/browser/modules/word/advanced/field-engine.js +45 -20
- package/dist/browser/modules/word/advanced/glossary.d.ts +10 -36
- package/dist/browser/modules/word/advanced/glossary.js +8 -9
- package/dist/browser/modules/word/advanced/math-convert.js +94 -12
- package/dist/browser/modules/word/advanced/ole-objects.d.ts +28 -0
- package/dist/browser/modules/word/advanced/ole-objects.js +122 -19
- package/dist/browser/modules/word/advanced/style-map.js +31 -10
- package/dist/browser/modules/word/builder/run-builders.d.ts +7 -1
- package/dist/browser/modules/word/builder/run-builders.js +7 -1
- package/dist/browser/modules/word/constants.d.ts +4 -0
- package/dist/browser/modules/word/constants.js +5 -1
- package/dist/browser/modules/word/convert/docx-to-semantic.d.ts +2 -1
- package/dist/browser/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/browser/modules/word/convert/html/html-import.d.ts +32 -1
- package/dist/browser/modules/word/convert/html/html-import.js +167 -14
- package/dist/browser/modules/word/convert/html/html.d.ts +2 -2
- package/dist/browser/modules/word/convert/html/html.js +1 -1
- package/dist/browser/modules/word/convert/markdown/markdown-import.d.ts +48 -18
- package/dist/browser/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/browser/modules/word/convert/markdown/markdown.d.ts +1 -1
- package/dist/browser/modules/word/convert/odt/odt.js +407 -56
- package/dist/browser/modules/word/html.d.ts +2 -2
- package/dist/browser/modules/word/html.js +1 -1
- package/dist/browser/modules/word/index.base.d.ts +3 -3
- package/dist/browser/modules/word/index.base.js +1 -1
- package/dist/browser/modules/word/layout/layout-full.js +326 -19
- package/dist/browser/modules/word/layout/render-page.js +35 -8
- package/dist/browser/modules/word/markdown.d.ts +1 -1
- package/dist/browser/modules/word/query/compat.d.ts +10 -2
- package/dist/browser/modules/word/query/compat.js +29 -21
- package/dist/browser/modules/word/reader/docx-reader.js +105 -2
- package/dist/browser/modules/word/reader/math-parser.js +8 -2
- package/dist/browser/modules/word/security/cfb-reader.js +5 -5
- package/dist/browser/modules/word/types.d.ts +96 -1
- package/dist/browser/modules/word/writer/docx-packager.js +108 -2
- package/dist/browser/modules/word/writer/glossary-writer.d.ts +28 -0
- package/dist/browser/modules/word/writer/glossary-writer.js +121 -0
- package/dist/browser/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/browser/modules/word/writer/math-writer.js +7 -2
- package/dist/browser/utils/font-metrics.d.ts +8 -0
- package/dist/browser/utils/font-metrics.js +43 -0
- package/dist/browser/utils/theme-colors.js +4 -1
- package/dist/cjs/modules/excel/note.js +5 -1
- package/dist/cjs/modules/excel/row.js +35 -2
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/cjs/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/cjs/modules/excel/workbook.browser.js +31 -1
- package/dist/cjs/modules/excel/worksheet.js +83 -0
- package/dist/cjs/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/shape-xform.js +112 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/cjs/modules/pdf/builder/document-builder.js +21 -48
- package/dist/cjs/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/cjs/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/cjs/modules/pdf/font/font-manager.js +35 -18
- package/dist/cjs/modules/pdf/render/page-renderer.js +112 -18
- package/dist/cjs/modules/word/advanced/field-engine.js +45 -20
- package/dist/cjs/modules/word/advanced/glossary.js +8 -9
- package/dist/cjs/modules/word/advanced/math-convert.js +94 -12
- package/dist/cjs/modules/word/advanced/ole-objects.js +123 -19
- package/dist/cjs/modules/word/advanced/style-map.js +31 -10
- package/dist/cjs/modules/word/builder/run-builders.js +7 -1
- package/dist/cjs/modules/word/constants.js +5 -1
- package/dist/cjs/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/cjs/modules/word/convert/html/html-import.js +168 -14
- package/dist/cjs/modules/word/convert/html/html.js +2 -1
- package/dist/cjs/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/cjs/modules/word/convert/odt/odt.js +407 -56
- package/dist/cjs/modules/word/html.js +2 -1
- package/dist/cjs/modules/word/index.base.js +4 -3
- package/dist/cjs/modules/word/layout/layout-full.js +325 -18
- package/dist/cjs/modules/word/layout/render-page.js +35 -8
- package/dist/cjs/modules/word/query/compat.js +29 -21
- package/dist/cjs/modules/word/reader/docx-reader.js +104 -1
- package/dist/cjs/modules/word/reader/math-parser.js +8 -2
- package/dist/cjs/modules/word/security/cfb-reader.js +5 -5
- package/dist/cjs/modules/word/writer/docx-packager.js +108 -2
- package/dist/cjs/modules/word/writer/glossary-writer.js +124 -0
- package/dist/cjs/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/cjs/modules/word/writer/math-writer.js +7 -2
- package/dist/cjs/utils/font-metrics.js +44 -0
- package/dist/cjs/utils/theme-colors.js +4 -1
- package/dist/esm/modules/excel/note.js +5 -1
- package/dist/esm/modules/excel/row.js +35 -2
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/esm/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/esm/modules/excel/workbook.browser.js +32 -2
- package/dist/esm/modules/excel/worksheet.js +83 -0
- package/dist/esm/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/esm/modules/pdf/builder/document-builder.js +22 -49
- package/dist/esm/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/esm/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/esm/modules/pdf/font/font-manager.js +35 -18
- package/dist/esm/modules/pdf/render/page-renderer.js +111 -18
- package/dist/esm/modules/word/advanced/field-engine.js +45 -20
- package/dist/esm/modules/word/advanced/glossary.js +8 -9
- package/dist/esm/modules/word/advanced/math-convert.js +94 -12
- package/dist/esm/modules/word/advanced/ole-objects.js +122 -19
- package/dist/esm/modules/word/advanced/style-map.js +31 -10
- package/dist/esm/modules/word/builder/run-builders.js +7 -1
- package/dist/esm/modules/word/constants.js +5 -1
- package/dist/esm/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/esm/modules/word/convert/html/html-import.js +167 -14
- package/dist/esm/modules/word/convert/html/html.js +1 -1
- package/dist/esm/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/esm/modules/word/convert/odt/odt.js +407 -56
- package/dist/esm/modules/word/html.js +1 -1
- package/dist/esm/modules/word/index.base.js +1 -1
- package/dist/esm/modules/word/layout/layout-full.js +326 -19
- package/dist/esm/modules/word/layout/render-page.js +35 -8
- package/dist/esm/modules/word/query/compat.js +29 -21
- package/dist/esm/modules/word/reader/docx-reader.js +105 -2
- package/dist/esm/modules/word/reader/math-parser.js +8 -2
- package/dist/esm/modules/word/security/cfb-reader.js +5 -5
- package/dist/esm/modules/word/writer/docx-packager.js +108 -2
- package/dist/esm/modules/word/writer/glossary-writer.js +121 -0
- package/dist/esm/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/esm/modules/word/writer/math-writer.js +7 -2
- package/dist/esm/utils/font-metrics.js +43 -0
- package/dist/esm/utils/theme-colors.js +4 -1
- package/dist/iife/excelts.iife.js +496 -59
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +39 -39
- package/dist/types/modules/excel/cell.d.ts +4 -0
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
- package/dist/types/modules/excel/types.d.ts +81 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +8 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +16 -0
- package/dist/types/modules/excel/worksheet.d.ts +31 -1
- package/dist/types/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
- package/dist/types/modules/pdf/core/pdf-stream.d.ts +28 -1
- package/dist/types/modules/pdf/font/font-manager.d.ts +26 -0
- package/dist/types/modules/pdf/render/page-renderer.d.ts +51 -3
- package/dist/types/modules/word/advanced/glossary.d.ts +10 -36
- package/dist/types/modules/word/advanced/ole-objects.d.ts +28 -0
- package/dist/types/modules/word/builder/run-builders.d.ts +7 -1
- package/dist/types/modules/word/constants.d.ts +4 -0
- package/dist/types/modules/word/convert/docx-to-semantic.d.ts +2 -1
- package/dist/types/modules/word/convert/html/html-import.d.ts +32 -1
- package/dist/types/modules/word/convert/html/html.d.ts +2 -2
- package/dist/types/modules/word/convert/markdown/markdown-import.d.ts +48 -18
- package/dist/types/modules/word/convert/markdown/markdown.d.ts +1 -1
- package/dist/types/modules/word/html.d.ts +2 -2
- package/dist/types/modules/word/index.base.d.ts +3 -3
- package/dist/types/modules/word/markdown.d.ts +1 -1
- package/dist/types/modules/word/query/compat.d.ts +10 -2
- package/dist/types/modules/word/types.d.ts +96 -1
- package/dist/types/modules/word/writer/glossary-writer.d.ts +28 -0
- package/dist/types/utils/font-metrics.d.ts +8 -0
- package/package.json +3 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { unzip } from "../../archive/read-archive.js";
|
|
8
8
|
import { parseXml, findChild, textContent } from "../../xml/dom.js";
|
|
9
|
-
import { RelType } from "../constants.js";
|
|
9
|
+
import { RelType, ContentType } from "../constants.js";
|
|
10
10
|
import { utf8Decoder } from "../core/internal-utils.js";
|
|
11
11
|
import { isRun } from "../core/text-utils.js";
|
|
12
12
|
import { DocxError, DocxParseError, DocxMissingPartError, DocxEncryptedError, DocxLimitExceededError } from "../errors.js";
|
|
@@ -2457,6 +2457,107 @@ async function _readDocxInner(buffer, policy) {
|
|
|
2457
2457
|
break;
|
|
2458
2458
|
}
|
|
2459
2459
|
}
|
|
2460
|
+
// Extract OLE embedded objects wired on document.xml.rels. We surface
|
|
2461
|
+
// them as structured `oleObjects` so callers can query/round-trip them
|
|
2462
|
+
// (getOleObjectData/extractOleObjects) without depending on the part's
|
|
2463
|
+
// own .rels (OLE binaries carry no .rels — their relationship lives on
|
|
2464
|
+
// document.xml.rels). The body still references each object through an
|
|
2465
|
+
// opaqueDrawing carrying the same r:id. Honours `preserveOleObjects`.
|
|
2466
|
+
let oleObjects;
|
|
2467
|
+
if (policy.preserveOleObjects) {
|
|
2468
|
+
const collected = [];
|
|
2469
|
+
// progId may be recoverable from the body's <o:OLEObject ProgID="…">.
|
|
2470
|
+
// The <w:object> markup is preserved either as a body-level opaqueDrawing
|
|
2471
|
+
// or (more commonly) as a run-level opaqueRun inside a paragraph. Collect
|
|
2472
|
+
// raw XML from both so the ProgID round-trips.
|
|
2473
|
+
const progIdByRId = new Map();
|
|
2474
|
+
const scanOleMarkup = (rawXml) => {
|
|
2475
|
+
// Match ProgID + r:id from within the same <o:OLEObject> element so a
|
|
2476
|
+
// preview <v:imagedata r:id="…"> earlier in the markup is not mistaken
|
|
2477
|
+
// for the OLE binary's relationship id.
|
|
2478
|
+
const oleTagRe = /<o:OLEObject\b[^>]*>/gi;
|
|
2479
|
+
let m;
|
|
2480
|
+
while ((m = oleTagRe.exec(rawXml)) !== null) {
|
|
2481
|
+
const tag = m[0];
|
|
2482
|
+
const progMatch = tag.match(/ProgID="([^"]+)"/i);
|
|
2483
|
+
const ridMatch = tag.match(/r:id="([^"]+)"/i);
|
|
2484
|
+
if (progMatch && ridMatch) {
|
|
2485
|
+
progIdByRId.set(ridMatch[1], progMatch[1]);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2489
|
+
for (const item of body) {
|
|
2490
|
+
if (item.type === "opaqueDrawing") {
|
|
2491
|
+
scanOleMarkup(item.rawXml);
|
|
2492
|
+
}
|
|
2493
|
+
else if (item.type === "paragraph") {
|
|
2494
|
+
for (const child of item.children) {
|
|
2495
|
+
if (isRun(child)) {
|
|
2496
|
+
for (const rc of child.content) {
|
|
2497
|
+
if (rc.type === "opaqueRun") {
|
|
2498
|
+
scanOleMarkup(rc.rawXml);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
for (const rel of docRels) {
|
|
2506
|
+
if (rel.type !== RelType.Package) {
|
|
2507
|
+
continue;
|
|
2508
|
+
}
|
|
2509
|
+
const olePath = resolvePartPath(documentPartPath, rel.target);
|
|
2510
|
+
if (!olePath.startsWith("word/embeddings/")) {
|
|
2511
|
+
continue;
|
|
2512
|
+
}
|
|
2513
|
+
const oleData = entries.get(olePath);
|
|
2514
|
+
if (!oleData) {
|
|
2515
|
+
continue;
|
|
2516
|
+
}
|
|
2517
|
+
consumedPaths.add(olePath);
|
|
2518
|
+
collected.push({
|
|
2519
|
+
path: olePath,
|
|
2520
|
+
data: oleData,
|
|
2521
|
+
rId: rel.id,
|
|
2522
|
+
progId: progIdByRId.get(rel.id),
|
|
2523
|
+
contentType: ContentType.OleObject
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
if (collected.length > 0) {
|
|
2527
|
+
oleObjects = collected;
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
// Glossary document (Building Blocks). The glossary is a self-contained
|
|
2531
|
+
// sub-document: word/glossary/document.xml plus its own companion parts
|
|
2532
|
+
// (styles/settings/webSettings/fontTable) referenced from
|
|
2533
|
+
// word/glossary/_rels/document.xml.rels. We carry document.xml verbatim as
|
|
2534
|
+
// `rawXml` and ALL sibling glossary parts (including the .rels) verbatim in
|
|
2535
|
+
// `rawParts`, so a read→write round-trip re-emits a complete, Word-valid
|
|
2536
|
+
// glossary rather than dropping it (opaqueParts would lose both the
|
|
2537
|
+
// glossaryDocument relationship and the companion .rels). The structured
|
|
2538
|
+
// `blocks` are not reverse-parsed.
|
|
2539
|
+
let glossary;
|
|
2540
|
+
for (const rel of docRels) {
|
|
2541
|
+
if (rel.type !== RelType.Glossary) {
|
|
2542
|
+
continue;
|
|
2543
|
+
}
|
|
2544
|
+
const glossaryPath = resolvePartPath(documentPartPath, rel.target);
|
|
2545
|
+
const glossaryData = entries.get(glossaryPath);
|
|
2546
|
+
if (glossaryData) {
|
|
2547
|
+
// Collect every word/glossary/** part (document.xml, its .rels and all
|
|
2548
|
+
// companions) verbatim, and mark them consumed so they are not also
|
|
2549
|
+
// emitted via opaqueParts.
|
|
2550
|
+
const rawParts = new Map();
|
|
2551
|
+
for (const [path, data] of entries) {
|
|
2552
|
+
if (path.startsWith("word/glossary/")) {
|
|
2553
|
+
rawParts.set(path, data);
|
|
2554
|
+
consumedPaths.add(path);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
glossary = { blocks: [], rawXml: decoder.decode(glossaryData), rawParts };
|
|
2558
|
+
}
|
|
2559
|
+
break;
|
|
2560
|
+
}
|
|
2460
2561
|
// Resolve altChunk data: body elements of type "altChunk" reference a rId.
|
|
2461
2562
|
// The target file is stored in docRels + entries. We populate the altChunk
|
|
2462
2563
|
// body item with its data here AND mark the target path as consumed so the
|
|
@@ -2575,6 +2676,8 @@ async function _readDocxInner(buffer, policy) {
|
|
|
2575
2676
|
theme,
|
|
2576
2677
|
watermark,
|
|
2577
2678
|
opaqueParts: opaqueParts.length > 0 ? opaqueParts : undefined,
|
|
2578
|
-
vbaProject
|
|
2679
|
+
vbaProject,
|
|
2680
|
+
oleObjects,
|
|
2681
|
+
glossary
|
|
2579
2682
|
};
|
|
2580
2683
|
}
|
|
@@ -122,8 +122,14 @@ function parseMathContent(el) {
|
|
|
122
122
|
const v = mathAttrVal(el, "val");
|
|
123
123
|
return v !== "0" && v !== "false";
|
|
124
124
|
};
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
// `show` is tri-state: it may be explicitly off (`m:val="0"`), which
|
|
126
|
+
// is what makes a phantom invisible, explicitly on, or absent
|
|
127
|
+
// (defaults to on). Preserve an explicit value so a round-trip keeps
|
|
128
|
+
// the invisibility, but leave it unset when the element is absent.
|
|
129
|
+
const showEl = findMathChild(phantPrEl, "show");
|
|
130
|
+
if (showEl) {
|
|
131
|
+
const v = mathAttrVal(showEl, "val");
|
|
132
|
+
ph.show = v !== "0" && v !== "false";
|
|
127
133
|
}
|
|
128
134
|
if (boolAttr("zeroWid")) {
|
|
129
135
|
ph.zeroWidth = true;
|
|
@@ -336,9 +336,8 @@ export function writeCfb(entries) {
|
|
|
336
336
|
const miniNodes = streamNodes.filter(n => n.size > 0 && n.size < MINI_STREAM_CUTOFF);
|
|
337
337
|
const regularNodes = streamNodes.filter(n => n.size >= MINI_STREAM_CUTOFF);
|
|
338
338
|
// Assemble the mini-stream and the mini-FAT.
|
|
339
|
-
let miniStream = new Uint8Array(0);
|
|
340
339
|
const miniFat = [];
|
|
341
|
-
{
|
|
340
|
+
const miniStream = (() => {
|
|
342
341
|
const parts = [];
|
|
343
342
|
let miniSectorIdx = 0;
|
|
344
343
|
let totalLen = 0;
|
|
@@ -354,13 +353,14 @@ export function writeCfb(entries) {
|
|
|
354
353
|
parts.push(padded);
|
|
355
354
|
totalLen += padded.length;
|
|
356
355
|
}
|
|
357
|
-
|
|
356
|
+
const stream = new Uint8Array(totalLen);
|
|
358
357
|
let off = 0;
|
|
359
358
|
for (const p of parts) {
|
|
360
|
-
|
|
359
|
+
stream.set(p, off);
|
|
361
360
|
off += p.length;
|
|
362
361
|
}
|
|
363
|
-
|
|
362
|
+
return stream;
|
|
363
|
+
})();
|
|
364
364
|
root.size = miniStream.length;
|
|
365
365
|
// ---------------------------------------------------------------------------
|
|
366
366
|
// 4. Lay out regular sectors.
|
|
@@ -1283,7 +1283,13 @@ export interface MathPreSubSuperScript {
|
|
|
1283
1283
|
export interface MathPhantom {
|
|
1284
1284
|
readonly type: "mathPhantom";
|
|
1285
1285
|
readonly content: readonly MathContent[];
|
|
1286
|
-
/**
|
|
1286
|
+
/**
|
|
1287
|
+
* Whether the phantom's base content is drawn. In OOXML `m:show` defaults to
|
|
1288
|
+
* ON, so the base stays visible unless this is explicitly set to `false`
|
|
1289
|
+
* (serialized as `<m:show m:val="0"/>`). Set `false` for the classic
|
|
1290
|
+
* "occupies space but invisible" phantom; leave undefined to keep the
|
|
1291
|
+
* default (visible) behaviour.
|
|
1292
|
+
*/
|
|
1287
1293
|
readonly show?: boolean;
|
|
1288
1294
|
/** Zero width (default: false). */
|
|
1289
1295
|
readonly zeroWidth?: boolean;
|
|
@@ -1714,6 +1720,10 @@ export interface ImageWatermark {
|
|
|
1714
1720
|
readonly scale?: number;
|
|
1715
1721
|
/** Washout effect. */
|
|
1716
1722
|
readonly washout?: boolean;
|
|
1723
|
+
/** Display width in points. Defaults to 415.2pt (full body width). */
|
|
1724
|
+
readonly widthPt?: number;
|
|
1725
|
+
/** Display height in points. Defaults to 415.2pt scaled to image ratio, or 233.5pt. */
|
|
1726
|
+
readonly heightPt?: number;
|
|
1717
1727
|
}
|
|
1718
1728
|
/** Watermark (text or image). */
|
|
1719
1729
|
export type Watermark = TextWatermark | ImageWatermark;
|
|
@@ -2403,6 +2413,39 @@ export interface PersonInfo {
|
|
|
2403
2413
|
* - "macroEnabledTemplate" — .dotm (macro-enabled template)
|
|
2404
2414
|
*/
|
|
2405
2415
|
export type DocxDocumentType = "document" | "template" | "macroEnabledDocument" | "macroEnabledTemplate";
|
|
2416
|
+
/** Building block gallery category. */
|
|
2417
|
+
export type BuildingBlockGallery = "autoText" | "quickParts" | "coverPages" | "tableOfContents" | "headers" | "footers" | "pageNumbers" | "tables" | "textBoxes" | "watermarks" | "equations" | "bibliographies" | "custom1" | "custom2" | "custom3" | "custom4" | "custom5";
|
|
2418
|
+
/** A single building block (AutoText/Quick Part) entry. */
|
|
2419
|
+
export interface BuildingBlock {
|
|
2420
|
+
/** Name of the building block (displayed in gallery). */
|
|
2421
|
+
readonly name: string;
|
|
2422
|
+
/** Gallery this block belongs to. */
|
|
2423
|
+
readonly gallery: BuildingBlockGallery;
|
|
2424
|
+
/** Category within the gallery. */
|
|
2425
|
+
readonly category?: string;
|
|
2426
|
+
/** Description/tooltip. */
|
|
2427
|
+
readonly description?: string;
|
|
2428
|
+
/** The content of the building block. */
|
|
2429
|
+
readonly content: readonly BodyContent[];
|
|
2430
|
+
/** Section properties specific to this building block. */
|
|
2431
|
+
readonly sectionProperties?: SectionProperties;
|
|
2432
|
+
/** Unique identifier (GUID). */
|
|
2433
|
+
readonly guid?: string;
|
|
2434
|
+
}
|
|
2435
|
+
/** The glossary document model. */
|
|
2436
|
+
export interface GlossaryDocument {
|
|
2437
|
+
/** Building block entries. */
|
|
2438
|
+
readonly blocks: readonly BuildingBlock[];
|
|
2439
|
+
/** Raw parts preserved for round-trip (style, settings, fontTable). */
|
|
2440
|
+
readonly rawParts?: ReadonlyMap<string, Uint8Array>;
|
|
2441
|
+
/**
|
|
2442
|
+
* Verbatim `word/glossary/document.xml` markup. Populated by the reader so a
|
|
2443
|
+
* read→write round-trip preserves the glossary byte-faithfully even though
|
|
2444
|
+
* the structured `blocks` are not reverse-parsed. When present the writer
|
|
2445
|
+
* emits this verbatim and ignores `blocks`.
|
|
2446
|
+
*/
|
|
2447
|
+
readonly rawXml?: string;
|
|
2448
|
+
}
|
|
2406
2449
|
export interface DocxDocument {
|
|
2407
2450
|
/**
|
|
2408
2451
|
* Document type. Determines the content type used in the package.
|
|
@@ -2468,6 +2511,24 @@ export interface DocxDocument {
|
|
|
2468
2511
|
readonly opaqueParts?: readonly OpaquePart[];
|
|
2469
2512
|
/** VBA project binary (word/vbaProject.bin) for .docm/.dotm round-trip. */
|
|
2470
2513
|
readonly vbaProject?: Uint8Array;
|
|
2514
|
+
/**
|
|
2515
|
+
* OLE embedded objects wired into the document. Unlike a bare
|
|
2516
|
+
* {@link OpaquePart}, each entry here is registered by the packager as a
|
|
2517
|
+
* relationship on `word/_rels/document.xml.rels` (so the `r:id` used by the
|
|
2518
|
+
* body `<w:object>`/`<o:OLEObject>` markup actually resolves) and gets a
|
|
2519
|
+
* `[Content_Types].xml` override. The body still references the object via
|
|
2520
|
+
* an {@link OpaqueDrawing} carrying the matching `r:id`. Mirrors the
|
|
2521
|
+
* `vbaProject` model-field pattern.
|
|
2522
|
+
*/
|
|
2523
|
+
readonly oleObjects?: readonly OleObjectPart[];
|
|
2524
|
+
/**
|
|
2525
|
+
* Glossary document (Building Blocks / AutoText / Quick Parts). When set,
|
|
2526
|
+
* the packager serialises it to `word/glossary/document.xml`, registers the
|
|
2527
|
+
* `glossaryDocument` relationship on `document.xml.rels`, and adds the
|
|
2528
|
+
* `[Content_Types].xml` override — the canonical OOXML location Word reads
|
|
2529
|
+
* Quick Parts from.
|
|
2530
|
+
*/
|
|
2531
|
+
readonly glossary?: GlossaryDocument;
|
|
2471
2532
|
}
|
|
2472
2533
|
/** A relationship entry preserved for round-trip. */
|
|
2473
2534
|
export interface OpaqueRelationship {
|
|
@@ -2508,6 +2569,40 @@ export interface OpaquePart {
|
|
|
2508
2569
|
/** Relationships of this part (from its .rels file). */
|
|
2509
2570
|
readonly relationships?: readonly OpaqueRelationship[];
|
|
2510
2571
|
}
|
|
2572
|
+
/**
|
|
2573
|
+
* An OLE embedded object that the packager wires into the main document.
|
|
2574
|
+
*
|
|
2575
|
+
* The binary is written at {@link path}, a relationship with the exact
|
|
2576
|
+
* {@link rId} is registered on `word/_rels/document.xml.rels` (so body
|
|
2577
|
+
* `<w:object r:id="…">` markup resolves), and a `[Content_Types].xml`
|
|
2578
|
+
* override is added. An optional preview image is wired the same way.
|
|
2579
|
+
*/
|
|
2580
|
+
export interface OleObjectPart {
|
|
2581
|
+
/** Part path of the OLE binary (e.g. "word/embeddings/oleObject1.bin"). */
|
|
2582
|
+
readonly path: string;
|
|
2583
|
+
/** Raw OLE binary data (ideally an OLE2 compound document). */
|
|
2584
|
+
readonly data: Uint8Array;
|
|
2585
|
+
/**
|
|
2586
|
+
* Relationship ID referenced from the body markup. Registered verbatim on
|
|
2587
|
+
* the document relationships so the reference always resolves.
|
|
2588
|
+
*/
|
|
2589
|
+
readonly rId: string;
|
|
2590
|
+
/** OLE ProgId (e.g. "Excel.Sheet.12", "Package"). Carried for round-trip. */
|
|
2591
|
+
readonly progId?: string;
|
|
2592
|
+
/**
|
|
2593
|
+
* Content type for the binary. Defaults to
|
|
2594
|
+
* `application/vnd.openxmlformats-officedocument.oleObject`.
|
|
2595
|
+
*/
|
|
2596
|
+
readonly contentType?: string;
|
|
2597
|
+
/** Optional preview image part path (e.g. "word/media/oleImage1.png"). */
|
|
2598
|
+
readonly previewPath?: string;
|
|
2599
|
+
/** Preview image bytes (when {@link previewPath} is set). */
|
|
2600
|
+
readonly previewData?: Uint8Array;
|
|
2601
|
+
/** Relationship ID for the preview image, registered on the document. */
|
|
2602
|
+
readonly previewRId?: string;
|
|
2603
|
+
/** Content type for the preview image (e.g. "image/png"). */
|
|
2604
|
+
readonly previewContentType?: string;
|
|
2605
|
+
}
|
|
2511
2606
|
/** An opaque drawing element (e.g. chart) preserved in a paragraph. */
|
|
2512
2607
|
export interface OpaqueDrawing {
|
|
2513
2608
|
readonly type: "opaqueDrawing";
|
|
@@ -20,6 +20,7 @@ import { renderComments, renderCommentsExtended } from "./comment-writer.js";
|
|
|
20
20
|
import { createContentTypes, addContentTypeDefault, addContentTypeOverride, addImageContentTypeDefaults, renderContentTypes } from "./content-types.js";
|
|
21
21
|
import { renderDocument } from "./document-writer.js";
|
|
22
22
|
import { renderFootnotes, renderEndnotes } from "./footnote-writer.js";
|
|
23
|
+
import { renderGlossaryDocument } from "./glossary-writer.js";
|
|
23
24
|
import { renderHeader, renderFooter, renderWatermarkHeader } from "./header-footer-writer.js";
|
|
24
25
|
import { renderNumbering } from "./numbering-writer.js";
|
|
25
26
|
import { renderSettings, renderFontTable, renderCoreProperties, renderAppProperties, renderCustomProperties, renderWebSettings, renderPeople, renderTheme } from "./parts-writer.js";
|
|
@@ -1083,7 +1084,13 @@ async function _packageDocxInner(doc, options) {
|
|
|
1083
1084
|
// word/styles.xml
|
|
1084
1085
|
archive.add(PartPath.Styles, renderXml(xml => renderStyles(xml, doc.docDefaults, doc.styles)));
|
|
1085
1086
|
// word/settings.xml
|
|
1086
|
-
|
|
1087
|
+
// When the document defines a page background, Word only paints it if
|
|
1088
|
+
// <w:displayBackgroundShape/> is present in settings. Inject it
|
|
1089
|
+
// automatically so setBackground() actually shows up on screen.
|
|
1090
|
+
const settingsForRender = doc.background && !doc.settings?.displayBackgroundShape
|
|
1091
|
+
? { ...(doc.settings ?? {}), displayBackgroundShape: true }
|
|
1092
|
+
: doc.settings;
|
|
1093
|
+
archive.add(PartPath.Settings, renderXml(xml => renderSettings(xml, settingsForRender, rawXmlPolicy)));
|
|
1087
1094
|
// word/fontTable.xml
|
|
1088
1095
|
archive.add(PartPath.FontTable, renderXml(xml => renderFontTable(xml, doc.fonts)));
|
|
1089
1096
|
// word/fonts/*.odttf (embedded fonts)
|
|
@@ -1345,6 +1352,81 @@ async function _packageDocxInner(doc, options) {
|
|
|
1345
1352
|
addRelationship(documentRels, RelType.VbaProject, "vbaProject.bin");
|
|
1346
1353
|
addContentTypeOverride(contentTypes, "/word/vbaProject.bin", ContentType.VbaProject);
|
|
1347
1354
|
}
|
|
1355
|
+
// OLE embedded objects (word/embeddings/*.bin + optional preview media).
|
|
1356
|
+
// Each object is registered with the *exact* rId referenced from the body
|
|
1357
|
+
// `<w:object r:id="…">` markup, so the reference always resolves. The
|
|
1358
|
+
// `preserveOleObjects` security policy can still strip the binaries below.
|
|
1359
|
+
if (doc.oleObjects && securityPolicy.preserveOleObjects) {
|
|
1360
|
+
for (const ole of doc.oleObjects) {
|
|
1361
|
+
archive.add(ole.path, ole.data);
|
|
1362
|
+
// Target is relative to word/ (document.xml lives in word/).
|
|
1363
|
+
const oleTarget = ole.path.replace(/^word\//, "");
|
|
1364
|
+
addRelationshipWithId(documentRels, ole.rId, RelType.Package, oleTarget);
|
|
1365
|
+
addContentTypeOverride(contentTypes, `/${ole.path}`, ole.contentType ?? ContentType.OleObject);
|
|
1366
|
+
if (ole.previewPath && ole.previewData && ole.previewRId) {
|
|
1367
|
+
archive.add(ole.previewPath, ole.previewData);
|
|
1368
|
+
addRelationshipWithId(documentRels, ole.previewRId, RelType.Image, ole.previewPath.replace(/^word\//, ""));
|
|
1369
|
+
if (ole.previewContentType) {
|
|
1370
|
+
addContentTypeOverride(contentTypes, `/${ole.previewPath}`, ole.previewContentType);
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
const ext = getFileExt(ole.previewPath);
|
|
1374
|
+
const inferred = ext ? inferContentType(ext) : undefined;
|
|
1375
|
+
if (inferred) {
|
|
1376
|
+
addContentTypeOverride(contentTypes, `/${ole.previewPath}`, inferred);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
// word/glossary/document.xml (Building Blocks / AutoText / Quick Parts).
|
|
1383
|
+
//
|
|
1384
|
+
// A glossary is a self-contained sub-document: Word expects it to carry its
|
|
1385
|
+
// own styles / settings / webSettings / fontTable parts (referenced from
|
|
1386
|
+
// word/glossary/_rels/document.xml.rels) rather than sharing the main
|
|
1387
|
+
// document's. Omitting them makes Word discard the whole glossary on open.
|
|
1388
|
+
//
|
|
1389
|
+
// - Round-tripped glossary (`rawParts` set): re-emit every captured
|
|
1390
|
+
// word/glossary/** part verbatim (document.xml, its .rels, companions).
|
|
1391
|
+
// - Freshly-built glossary: synthesise document.xml + the companion parts
|
|
1392
|
+
// + the glossary's own .rels, reusing the main document's styles/fonts so
|
|
1393
|
+
// block content (e.g. Heading1) resolves.
|
|
1394
|
+
if (doc.glossary && (doc.glossary.rawXml || doc.glossary.blocks.length > 0)) {
|
|
1395
|
+
addRelationship(documentRels, RelType.Glossary, "glossary/document.xml");
|
|
1396
|
+
if (doc.glossary.rawParts && doc.glossary.rawParts.size > 0) {
|
|
1397
|
+
for (const [path, data] of doc.glossary.rawParts) {
|
|
1398
|
+
archive.add(path, data);
|
|
1399
|
+
if (path.endsWith(".rels")) {
|
|
1400
|
+
continue; // .rels parts are typed by the Default rels content type
|
|
1401
|
+
}
|
|
1402
|
+
const ct = path === "word/glossary/document.xml"
|
|
1403
|
+
? ContentType.Glossary
|
|
1404
|
+
: inferContentType(getFileExt(path));
|
|
1405
|
+
if (ct) {
|
|
1406
|
+
addContentTypeOverride(contentTypes, `/${path}`, ct);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
else {
|
|
1411
|
+
archive.add("word/glossary/document.xml", renderGlossaryDocument(doc.glossary));
|
|
1412
|
+
addContentTypeOverride(contentTypes, "/word/glossary/document.xml", ContentType.Glossary);
|
|
1413
|
+
// Synthesise the companion sub-document parts + the glossary's own rels.
|
|
1414
|
+
const glossaryRels = createRelationships();
|
|
1415
|
+
addRelationshipWithId(glossaryRels, "rId1", RelType.Styles, "styles.xml");
|
|
1416
|
+
addRelationshipWithId(glossaryRels, "rId2", RelType.Settings, "settings.xml");
|
|
1417
|
+
addRelationshipWithId(glossaryRels, "rId3", RelType.WebSettings, "webSettings.xml");
|
|
1418
|
+
addRelationshipWithId(glossaryRels, "rId4", RelType.FontTable, "fontTable.xml");
|
|
1419
|
+
archive.add("word/glossary/_rels/document.xml.rels", renderXml(xml => renderRelationships(glossaryRels, xml)));
|
|
1420
|
+
archive.add("word/glossary/styles.xml", renderXml(xml => renderStyles(xml, doc.docDefaults, doc.styles)));
|
|
1421
|
+
archive.add("word/glossary/settings.xml", renderXml(xml => renderSettings(xml, undefined, rawXmlPolicy)));
|
|
1422
|
+
archive.add("word/glossary/webSettings.xml", renderXml(xml => renderWebSettings(xml, undefined, rawXmlPolicy)));
|
|
1423
|
+
archive.add("word/glossary/fontTable.xml", renderXml(xml => renderFontTable(xml, doc.fonts)));
|
|
1424
|
+
addContentTypeOverride(contentTypes, "/word/glossary/styles.xml", ContentType.Styles);
|
|
1425
|
+
addContentTypeOverride(contentTypes, "/word/glossary/settings.xml", ContentType.Settings);
|
|
1426
|
+
addContentTypeOverride(contentTypes, "/word/glossary/webSettings.xml", ContentType.WebSettings);
|
|
1427
|
+
addContentTypeOverride(contentTypes, "/word/glossary/fontTable.xml", ContentType.FontTable);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1348
1430
|
// Write opaque (unrecognized) parts for round-trip preservation.
|
|
1349
1431
|
//
|
|
1350
1432
|
// Opaque parts are written as-is into the ZIP. Their paths must not
|
|
@@ -1385,8 +1467,23 @@ async function _packageDocxInner(doc, options) {
|
|
|
1385
1467
|
PartPath.CustomProps,
|
|
1386
1468
|
PartPath.Thumbnail,
|
|
1387
1469
|
"word/vbaProject.bin",
|
|
1388
|
-
"word/_rels/vbaProject.bin.rels"
|
|
1470
|
+
"word/_rels/vbaProject.bin.rels",
|
|
1471
|
+
"word/glossary/document.xml",
|
|
1472
|
+
"word/glossary/_rels/document.xml.rels"
|
|
1389
1473
|
]);
|
|
1474
|
+
// Glossary sub-document parts (emitted either verbatim from rawParts or
|
|
1475
|
+
// synthesised); reserve them so opaqueParts can't collide.
|
|
1476
|
+
if (doc.glossary && (doc.glossary.rawXml || doc.glossary.blocks.length > 0)) {
|
|
1477
|
+
reservedExact.add("word/glossary/styles.xml");
|
|
1478
|
+
reservedExact.add("word/glossary/settings.xml");
|
|
1479
|
+
reservedExact.add("word/glossary/webSettings.xml");
|
|
1480
|
+
reservedExact.add("word/glossary/fontTable.xml");
|
|
1481
|
+
if (doc.glossary.rawParts) {
|
|
1482
|
+
for (const path of doc.glossary.rawParts.keys()) {
|
|
1483
|
+
reservedExact.add(path);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1390
1487
|
// Headers/footers we are emitting in this run.
|
|
1391
1488
|
if (doc.headers) {
|
|
1392
1489
|
let i = 1;
|
|
@@ -1430,6 +1527,15 @@ async function _packageDocxInner(doc, options) {
|
|
|
1430
1527
|
}
|
|
1431
1528
|
}
|
|
1432
1529
|
}
|
|
1530
|
+
// OLE embedded objects emitted by this run.
|
|
1531
|
+
if (doc.oleObjects) {
|
|
1532
|
+
for (const ole of doc.oleObjects) {
|
|
1533
|
+
reservedExact.add(ole.path);
|
|
1534
|
+
if (ole.previewPath) {
|
|
1535
|
+
reservedExact.add(ole.previewPath);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1433
1539
|
for (const part of doc.opaqueParts) {
|
|
1434
1540
|
if (reservedExact.has(part.path)) {
|
|
1435
1541
|
throw new DocxWriteError(`Opaque part path "${part.path}" conflicts with a part the packager ` +
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module — Glossary (Building Blocks) Part Writer
|
|
3
|
+
*
|
|
4
|
+
* Serialises a {@link GlossaryDocument} into the canonical
|
|
5
|
+
* `word/glossary/document.xml` OOXML form:
|
|
6
|
+
*
|
|
7
|
+
* ```xml
|
|
8
|
+
* <w:glossaryDocument>
|
|
9
|
+
* <w:docParts>
|
|
10
|
+
* <w:docPart>
|
|
11
|
+
* <w:docPartPr>
|
|
12
|
+
* <w:name w:val="…"/>
|
|
13
|
+
* <w:category><w:name w:val="…"/><w:gallery w:val="…"/></w:category>
|
|
14
|
+
* <w:behaviors><w:behavior w:val="content"/></w:behaviors>
|
|
15
|
+
* <w:guid w:val="{…}"/>
|
|
16
|
+
* </w:docPartPr>
|
|
17
|
+
* <w:docPartBody>… body content …</w:docPartBody>
|
|
18
|
+
* </w:docPart>
|
|
19
|
+
* </w:docParts>
|
|
20
|
+
* </w:glossaryDocument>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Lives in the writer layer (not `advanced/`) so the packager can depend on
|
|
24
|
+
* it without creating a `advanced/ → writer/` import cycle.
|
|
25
|
+
*/
|
|
26
|
+
import type { GlossaryDocument } from "../types.js";
|
|
27
|
+
/** Render a {@link GlossaryDocument} to a `word/glossary/document.xml` string. */
|
|
28
|
+
export declare function renderGlossaryDocument(glossary: GlossaryDocument): string;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module — Glossary (Building Blocks) Part Writer
|
|
3
|
+
*
|
|
4
|
+
* Serialises a {@link GlossaryDocument} into the canonical
|
|
5
|
+
* `word/glossary/document.xml` OOXML form:
|
|
6
|
+
*
|
|
7
|
+
* ```xml
|
|
8
|
+
* <w:glossaryDocument>
|
|
9
|
+
* <w:docParts>
|
|
10
|
+
* <w:docPart>
|
|
11
|
+
* <w:docPartPr>
|
|
12
|
+
* <w:name w:val="…"/>
|
|
13
|
+
* <w:category><w:name w:val="…"/><w:gallery w:val="…"/></w:category>
|
|
14
|
+
* <w:behaviors><w:behavior w:val="content"/></w:behaviors>
|
|
15
|
+
* <w:guid w:val="{…}"/>
|
|
16
|
+
* </w:docPartPr>
|
|
17
|
+
* <w:docPartBody>… body content …</w:docPartBody>
|
|
18
|
+
* </w:docPart>
|
|
19
|
+
* </w:docParts>
|
|
20
|
+
* </w:glossaryDocument>
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Lives in the writer layer (not `advanced/`) so the packager can depend on
|
|
24
|
+
* it without creating a `advanced/ → writer/` import cycle.
|
|
25
|
+
*/
|
|
26
|
+
import { XmlWriter } from "../../xml/writer.js";
|
|
27
|
+
import { DOCUMENT_NAMESPACES, STD_DOC_ATTRIBUTES } from "../constants.js";
|
|
28
|
+
import { renderBodyContent } from "./document-writer.js";
|
|
29
|
+
/**
|
|
30
|
+
* Map the friendly {@link BuildingBlockGallery} token to the OOXML
|
|
31
|
+
* `ST_DocPartGallery` value used in `<w:gallery w:val="…">` (ECMA-376
|
|
32
|
+
* §17.18.23). Values MUST be exact enum members — Word rejects (and silently
|
|
33
|
+
* discards) the entire glossary if it sees an out-of-enum gallery value.
|
|
34
|
+
* There is no plain "quickParts" value; Quick Parts map to "custQuickParts".
|
|
35
|
+
*/
|
|
36
|
+
const GALLERY_TO_OOXML = {
|
|
37
|
+
autoText: "autoTxt",
|
|
38
|
+
quickParts: "custQuickParts",
|
|
39
|
+
coverPages: "coverPg",
|
|
40
|
+
tableOfContents: "tblOfContents",
|
|
41
|
+
headers: "hdrs",
|
|
42
|
+
footers: "ftrs",
|
|
43
|
+
pageNumbers: "pgNum",
|
|
44
|
+
tables: "tbls",
|
|
45
|
+
textBoxes: "txtBox",
|
|
46
|
+
watermarks: "watermarks",
|
|
47
|
+
equations: "eq",
|
|
48
|
+
bibliographies: "bib",
|
|
49
|
+
custom1: "custom1",
|
|
50
|
+
custom2: "custom2",
|
|
51
|
+
custom3: "custom3",
|
|
52
|
+
custom4: "custom4",
|
|
53
|
+
custom5: "custom5"
|
|
54
|
+
};
|
|
55
|
+
/** Render a {@link GlossaryDocument} to a `word/glossary/document.xml` string. */
|
|
56
|
+
export function renderGlossaryDocument(glossary) {
|
|
57
|
+
// Byte-faithful round-trip: a glossary read from an existing document is
|
|
58
|
+
// carried as verbatim XML and re-emitted unchanged.
|
|
59
|
+
if (glossary.rawXml) {
|
|
60
|
+
return glossary.rawXml;
|
|
61
|
+
}
|
|
62
|
+
const writer = new XmlWriter();
|
|
63
|
+
renderGlossary(writer, glossary);
|
|
64
|
+
return writer.xml;
|
|
65
|
+
}
|
|
66
|
+
function renderGlossary(xml, glossary) {
|
|
67
|
+
xml.openXml(STD_DOC_ATTRIBUTES);
|
|
68
|
+
xml.openNode("w:glossaryDocument", DOCUMENT_NAMESPACES);
|
|
69
|
+
xml.openNode("w:docParts");
|
|
70
|
+
for (const block of glossary.blocks) {
|
|
71
|
+
renderDocPart(xml, block);
|
|
72
|
+
}
|
|
73
|
+
xml.closeNode(); // w:docParts
|
|
74
|
+
xml.closeNode(); // w:glossaryDocument
|
|
75
|
+
}
|
|
76
|
+
function renderDocPart(xml, block) {
|
|
77
|
+
xml.openNode("w:docPart");
|
|
78
|
+
// CT_DocPartPr — child order is fixed by the schema (ECMA-376 §17.12.1):
|
|
79
|
+
// name → style → category → types → behaviors → description → guid.
|
|
80
|
+
// Emitting these out of order makes Word reject the package on open.
|
|
81
|
+
xml.openNode("w:docPartPr");
|
|
82
|
+
xml.leafNode("w:name", { "w:val": block.name });
|
|
83
|
+
xml.openNode("w:category");
|
|
84
|
+
xml.leafNode("w:name", { "w:val": block.category ?? "General" });
|
|
85
|
+
xml.leafNode("w:gallery", { "w:val": GALLERY_TO_OOXML[block.gallery] ?? "placeholder" });
|
|
86
|
+
xml.closeNode(); // w:category
|
|
87
|
+
// `<w:types>` is optional; we omit it so we never emit an out-of-enum
|
|
88
|
+
// ST_DocPartType value. A docPart placed in the body inserts its content.
|
|
89
|
+
xml.openNode("w:behaviors");
|
|
90
|
+
xml.leafNode("w:behavior", { "w:val": "content" });
|
|
91
|
+
xml.closeNode(); // w:behaviors
|
|
92
|
+
if (block.description) {
|
|
93
|
+
xml.leafNode("w:description", { "w:val": block.description });
|
|
94
|
+
}
|
|
95
|
+
if (block.guid) {
|
|
96
|
+
xml.leafNode("w:guid", { "w:val": normaliseGuid(block.guid) });
|
|
97
|
+
}
|
|
98
|
+
xml.closeNode(); // w:docPartPr
|
|
99
|
+
// docPartBody — reuse the main body renderer. A fresh render context keeps
|
|
100
|
+
// id counters local; building-block content here is plain text/tables so it
|
|
101
|
+
// does not need image/chart rId remapping.
|
|
102
|
+
xml.openNode("w:docPartBody");
|
|
103
|
+
for (const item of block.content) {
|
|
104
|
+
renderBodyContent(xml, item);
|
|
105
|
+
}
|
|
106
|
+
// CT_Body must end with a paragraph; emit one if the block had no content.
|
|
107
|
+
if (block.content.length === 0) {
|
|
108
|
+
xml.openNode("w:p");
|
|
109
|
+
xml.closeNode();
|
|
110
|
+
}
|
|
111
|
+
xml.closeNode(); // w:docPartBody
|
|
112
|
+
xml.closeNode(); // w:docPart
|
|
113
|
+
}
|
|
114
|
+
/** Word expects the docPart guid wrapped in braces: `{XXXXXXXX-…}`. */
|
|
115
|
+
function normaliseGuid(guid) {
|
|
116
|
+
const trimmed = guid.trim();
|
|
117
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
118
|
+
return trimmed;
|
|
119
|
+
}
|
|
120
|
+
return `{${trimmed}}`;
|
|
121
|
+
}
|