@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
|
@@ -2460,6 +2460,107 @@ async function _readDocxInner(buffer, policy) {
|
|
|
2460
2460
|
break;
|
|
2461
2461
|
}
|
|
2462
2462
|
}
|
|
2463
|
+
// Extract OLE embedded objects wired on document.xml.rels. We surface
|
|
2464
|
+
// them as structured `oleObjects` so callers can query/round-trip them
|
|
2465
|
+
// (getOleObjectData/extractOleObjects) without depending on the part's
|
|
2466
|
+
// own .rels (OLE binaries carry no .rels — their relationship lives on
|
|
2467
|
+
// document.xml.rels). The body still references each object through an
|
|
2468
|
+
// opaqueDrawing carrying the same r:id. Honours `preserveOleObjects`.
|
|
2469
|
+
let oleObjects;
|
|
2470
|
+
if (policy.preserveOleObjects) {
|
|
2471
|
+
const collected = [];
|
|
2472
|
+
// progId may be recoverable from the body's <o:OLEObject ProgID="…">.
|
|
2473
|
+
// The <w:object> markup is preserved either as a body-level opaqueDrawing
|
|
2474
|
+
// or (more commonly) as a run-level opaqueRun inside a paragraph. Collect
|
|
2475
|
+
// raw XML from both so the ProgID round-trips.
|
|
2476
|
+
const progIdByRId = new Map();
|
|
2477
|
+
const scanOleMarkup = (rawXml) => {
|
|
2478
|
+
// Match ProgID + r:id from within the same <o:OLEObject> element so a
|
|
2479
|
+
// preview <v:imagedata r:id="…"> earlier in the markup is not mistaken
|
|
2480
|
+
// for the OLE binary's relationship id.
|
|
2481
|
+
const oleTagRe = /<o:OLEObject\b[^>]*>/gi;
|
|
2482
|
+
let m;
|
|
2483
|
+
while ((m = oleTagRe.exec(rawXml)) !== null) {
|
|
2484
|
+
const tag = m[0];
|
|
2485
|
+
const progMatch = tag.match(/ProgID="([^"]+)"/i);
|
|
2486
|
+
const ridMatch = tag.match(/r:id="([^"]+)"/i);
|
|
2487
|
+
if (progMatch && ridMatch) {
|
|
2488
|
+
progIdByRId.set(ridMatch[1], progMatch[1]);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
};
|
|
2492
|
+
for (const item of body) {
|
|
2493
|
+
if (item.type === "opaqueDrawing") {
|
|
2494
|
+
scanOleMarkup(item.rawXml);
|
|
2495
|
+
}
|
|
2496
|
+
else if (item.type === "paragraph") {
|
|
2497
|
+
for (const child of item.children) {
|
|
2498
|
+
if ((0, text_utils_1.isRun)(child)) {
|
|
2499
|
+
for (const rc of child.content) {
|
|
2500
|
+
if (rc.type === "opaqueRun") {
|
|
2501
|
+
scanOleMarkup(rc.rawXml);
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
for (const rel of docRels) {
|
|
2509
|
+
if (rel.type !== constants_1.RelType.Package) {
|
|
2510
|
+
continue;
|
|
2511
|
+
}
|
|
2512
|
+
const olePath = (0, parse_utils_1.resolvePartPath)(documentPartPath, rel.target);
|
|
2513
|
+
if (!olePath.startsWith("word/embeddings/")) {
|
|
2514
|
+
continue;
|
|
2515
|
+
}
|
|
2516
|
+
const oleData = entries.get(olePath);
|
|
2517
|
+
if (!oleData) {
|
|
2518
|
+
continue;
|
|
2519
|
+
}
|
|
2520
|
+
consumedPaths.add(olePath);
|
|
2521
|
+
collected.push({
|
|
2522
|
+
path: olePath,
|
|
2523
|
+
data: oleData,
|
|
2524
|
+
rId: rel.id,
|
|
2525
|
+
progId: progIdByRId.get(rel.id),
|
|
2526
|
+
contentType: constants_1.ContentType.OleObject
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
if (collected.length > 0) {
|
|
2530
|
+
oleObjects = collected;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
// Glossary document (Building Blocks). The glossary is a self-contained
|
|
2534
|
+
// sub-document: word/glossary/document.xml plus its own companion parts
|
|
2535
|
+
// (styles/settings/webSettings/fontTable) referenced from
|
|
2536
|
+
// word/glossary/_rels/document.xml.rels. We carry document.xml verbatim as
|
|
2537
|
+
// `rawXml` and ALL sibling glossary parts (including the .rels) verbatim in
|
|
2538
|
+
// `rawParts`, so a read→write round-trip re-emits a complete, Word-valid
|
|
2539
|
+
// glossary rather than dropping it (opaqueParts would lose both the
|
|
2540
|
+
// glossaryDocument relationship and the companion .rels). The structured
|
|
2541
|
+
// `blocks` are not reverse-parsed.
|
|
2542
|
+
let glossary;
|
|
2543
|
+
for (const rel of docRels) {
|
|
2544
|
+
if (rel.type !== constants_1.RelType.Glossary) {
|
|
2545
|
+
continue;
|
|
2546
|
+
}
|
|
2547
|
+
const glossaryPath = (0, parse_utils_1.resolvePartPath)(documentPartPath, rel.target);
|
|
2548
|
+
const glossaryData = entries.get(glossaryPath);
|
|
2549
|
+
if (glossaryData) {
|
|
2550
|
+
// Collect every word/glossary/** part (document.xml, its .rels and all
|
|
2551
|
+
// companions) verbatim, and mark them consumed so they are not also
|
|
2552
|
+
// emitted via opaqueParts.
|
|
2553
|
+
const rawParts = new Map();
|
|
2554
|
+
for (const [path, data] of entries) {
|
|
2555
|
+
if (path.startsWith("word/glossary/")) {
|
|
2556
|
+
rawParts.set(path, data);
|
|
2557
|
+
consumedPaths.add(path);
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
glossary = { blocks: [], rawXml: decoder.decode(glossaryData), rawParts };
|
|
2561
|
+
}
|
|
2562
|
+
break;
|
|
2563
|
+
}
|
|
2463
2564
|
// Resolve altChunk data: body elements of type "altChunk" reference a rId.
|
|
2464
2565
|
// The target file is stored in docRels + entries. We populate the altChunk
|
|
2465
2566
|
// body item with its data here AND mark the target path as consumed so the
|
|
@@ -2578,6 +2679,8 @@ async function _readDocxInner(buffer, policy) {
|
|
|
2578
2679
|
theme,
|
|
2579
2680
|
watermark,
|
|
2580
2681
|
opaqueParts: opaqueParts.length > 0 ? opaqueParts : undefined,
|
|
2581
|
-
vbaProject
|
|
2682
|
+
vbaProject,
|
|
2683
|
+
oleObjects,
|
|
2684
|
+
glossary
|
|
2582
2685
|
};
|
|
2583
2686
|
}
|
|
@@ -125,8 +125,14 @@ function parseMathContent(el) {
|
|
|
125
125
|
const v = (0, parse_utils_1.attrLocal)(el, "val");
|
|
126
126
|
return v !== "0" && v !== "false";
|
|
127
127
|
};
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
// `show` is tri-state: it may be explicitly off (`m:val="0"`), which
|
|
129
|
+
// is what makes a phantom invisible, explicitly on, or absent
|
|
130
|
+
// (defaults to on). Preserve an explicit value so a round-trip keeps
|
|
131
|
+
// the invisibility, but leave it unset when the element is absent.
|
|
132
|
+
const showEl = (0, parse_utils_1.findChildLocal)(phantPrEl, "show");
|
|
133
|
+
if (showEl) {
|
|
134
|
+
const v = (0, parse_utils_1.attrLocal)(showEl, "val");
|
|
135
|
+
ph.show = v !== "0" && v !== "false";
|
|
130
136
|
}
|
|
131
137
|
if (boolAttr("zeroWid")) {
|
|
132
138
|
ph.zeroWidth = true;
|
|
@@ -340,9 +340,8 @@ function writeCfb(entries) {
|
|
|
340
340
|
const miniNodes = streamNodes.filter(n => n.size > 0 && n.size < MINI_STREAM_CUTOFF);
|
|
341
341
|
const regularNodes = streamNodes.filter(n => n.size >= MINI_STREAM_CUTOFF);
|
|
342
342
|
// Assemble the mini-stream and the mini-FAT.
|
|
343
|
-
let miniStream = new Uint8Array(0);
|
|
344
343
|
const miniFat = [];
|
|
345
|
-
{
|
|
344
|
+
const miniStream = (() => {
|
|
346
345
|
const parts = [];
|
|
347
346
|
let miniSectorIdx = 0;
|
|
348
347
|
let totalLen = 0;
|
|
@@ -358,13 +357,14 @@ function writeCfb(entries) {
|
|
|
358
357
|
parts.push(padded);
|
|
359
358
|
totalLen += padded.length;
|
|
360
359
|
}
|
|
361
|
-
|
|
360
|
+
const stream = new Uint8Array(totalLen);
|
|
362
361
|
let off = 0;
|
|
363
362
|
for (const p of parts) {
|
|
364
|
-
|
|
363
|
+
stream.set(p, off);
|
|
365
364
|
off += p.length;
|
|
366
365
|
}
|
|
367
|
-
|
|
366
|
+
return stream;
|
|
367
|
+
})();
|
|
368
368
|
root.size = miniStream.length;
|
|
369
369
|
// ---------------------------------------------------------------------------
|
|
370
370
|
// 4. Lay out regular sectors.
|
|
@@ -56,6 +56,7 @@ const comment_writer_1 = require("./comment-writer");
|
|
|
56
56
|
const content_types_1 = require("./content-types");
|
|
57
57
|
const document_writer_1 = require("./document-writer");
|
|
58
58
|
const footnote_writer_1 = require("./footnote-writer");
|
|
59
|
+
const glossary_writer_1 = require("./glossary-writer");
|
|
59
60
|
const header_footer_writer_1 = require("./header-footer-writer");
|
|
60
61
|
const numbering_writer_1 = require("./numbering-writer");
|
|
61
62
|
const parts_writer_1 = require("./parts-writer");
|
|
@@ -1119,7 +1120,13 @@ async function _packageDocxInner(doc, options) {
|
|
|
1119
1120
|
// word/styles.xml
|
|
1120
1121
|
archive.add(constants_1.PartPath.Styles, renderXml(xml => (0, styles_writer_1.renderStyles)(xml, doc.docDefaults, doc.styles)));
|
|
1121
1122
|
// word/settings.xml
|
|
1122
|
-
|
|
1123
|
+
// When the document defines a page background, Word only paints it if
|
|
1124
|
+
// <w:displayBackgroundShape/> is present in settings. Inject it
|
|
1125
|
+
// automatically so setBackground() actually shows up on screen.
|
|
1126
|
+
const settingsForRender = doc.background && !doc.settings?.displayBackgroundShape
|
|
1127
|
+
? { ...(doc.settings ?? {}), displayBackgroundShape: true }
|
|
1128
|
+
: doc.settings;
|
|
1129
|
+
archive.add(constants_1.PartPath.Settings, renderXml(xml => (0, parts_writer_1.renderSettings)(xml, settingsForRender, rawXmlPolicy)));
|
|
1123
1130
|
// word/fontTable.xml
|
|
1124
1131
|
archive.add(constants_1.PartPath.FontTable, renderXml(xml => (0, parts_writer_1.renderFontTable)(xml, doc.fonts)));
|
|
1125
1132
|
// word/fonts/*.odttf (embedded fonts)
|
|
@@ -1381,6 +1388,81 @@ async function _packageDocxInner(doc, options) {
|
|
|
1381
1388
|
(0, relationships_1.addRelationship)(documentRels, constants_1.RelType.VbaProject, "vbaProject.bin");
|
|
1382
1389
|
(0, content_types_1.addContentTypeOverride)(contentTypes, "/word/vbaProject.bin", constants_1.ContentType.VbaProject);
|
|
1383
1390
|
}
|
|
1391
|
+
// OLE embedded objects (word/embeddings/*.bin + optional preview media).
|
|
1392
|
+
// Each object is registered with the *exact* rId referenced from the body
|
|
1393
|
+
// `<w:object r:id="…">` markup, so the reference always resolves. The
|
|
1394
|
+
// `preserveOleObjects` security policy can still strip the binaries below.
|
|
1395
|
+
if (doc.oleObjects && securityPolicy.preserveOleObjects) {
|
|
1396
|
+
for (const ole of doc.oleObjects) {
|
|
1397
|
+
archive.add(ole.path, ole.data);
|
|
1398
|
+
// Target is relative to word/ (document.xml lives in word/).
|
|
1399
|
+
const oleTarget = ole.path.replace(/^word\//, "");
|
|
1400
|
+
(0, relationships_1.addRelationshipWithId)(documentRels, ole.rId, constants_1.RelType.Package, oleTarget);
|
|
1401
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, `/${ole.path}`, ole.contentType ?? constants_1.ContentType.OleObject);
|
|
1402
|
+
if (ole.previewPath && ole.previewData && ole.previewRId) {
|
|
1403
|
+
archive.add(ole.previewPath, ole.previewData);
|
|
1404
|
+
(0, relationships_1.addRelationshipWithId)(documentRels, ole.previewRId, constants_1.RelType.Image, ole.previewPath.replace(/^word\//, ""));
|
|
1405
|
+
if (ole.previewContentType) {
|
|
1406
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, `/${ole.previewPath}`, ole.previewContentType);
|
|
1407
|
+
}
|
|
1408
|
+
else {
|
|
1409
|
+
const ext = (0, opc_paths_1.getFileExt)(ole.previewPath);
|
|
1410
|
+
const inferred = ext ? inferContentType(ext) : undefined;
|
|
1411
|
+
if (inferred) {
|
|
1412
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, `/${ole.previewPath}`, inferred);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
// word/glossary/document.xml (Building Blocks / AutoText / Quick Parts).
|
|
1419
|
+
//
|
|
1420
|
+
// A glossary is a self-contained sub-document: Word expects it to carry its
|
|
1421
|
+
// own styles / settings / webSettings / fontTable parts (referenced from
|
|
1422
|
+
// word/glossary/_rels/document.xml.rels) rather than sharing the main
|
|
1423
|
+
// document's. Omitting them makes Word discard the whole glossary on open.
|
|
1424
|
+
//
|
|
1425
|
+
// - Round-tripped glossary (`rawParts` set): re-emit every captured
|
|
1426
|
+
// word/glossary/** part verbatim (document.xml, its .rels, companions).
|
|
1427
|
+
// - Freshly-built glossary: synthesise document.xml + the companion parts
|
|
1428
|
+
// + the glossary's own .rels, reusing the main document's styles/fonts so
|
|
1429
|
+
// block content (e.g. Heading1) resolves.
|
|
1430
|
+
if (doc.glossary && (doc.glossary.rawXml || doc.glossary.blocks.length > 0)) {
|
|
1431
|
+
(0, relationships_1.addRelationship)(documentRels, constants_1.RelType.Glossary, "glossary/document.xml");
|
|
1432
|
+
if (doc.glossary.rawParts && doc.glossary.rawParts.size > 0) {
|
|
1433
|
+
for (const [path, data] of doc.glossary.rawParts) {
|
|
1434
|
+
archive.add(path, data);
|
|
1435
|
+
if (path.endsWith(".rels")) {
|
|
1436
|
+
continue; // .rels parts are typed by the Default rels content type
|
|
1437
|
+
}
|
|
1438
|
+
const ct = path === "word/glossary/document.xml"
|
|
1439
|
+
? constants_1.ContentType.Glossary
|
|
1440
|
+
: inferContentType((0, opc_paths_1.getFileExt)(path));
|
|
1441
|
+
if (ct) {
|
|
1442
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, `/${path}`, ct);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
archive.add("word/glossary/document.xml", (0, glossary_writer_1.renderGlossaryDocument)(doc.glossary));
|
|
1448
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, "/word/glossary/document.xml", constants_1.ContentType.Glossary);
|
|
1449
|
+
// Synthesise the companion sub-document parts + the glossary's own rels.
|
|
1450
|
+
const glossaryRels = (0, relationships_1.createRelationships)();
|
|
1451
|
+
(0, relationships_1.addRelationshipWithId)(glossaryRels, "rId1", constants_1.RelType.Styles, "styles.xml");
|
|
1452
|
+
(0, relationships_1.addRelationshipWithId)(glossaryRels, "rId2", constants_1.RelType.Settings, "settings.xml");
|
|
1453
|
+
(0, relationships_1.addRelationshipWithId)(glossaryRels, "rId3", constants_1.RelType.WebSettings, "webSettings.xml");
|
|
1454
|
+
(0, relationships_1.addRelationshipWithId)(glossaryRels, "rId4", constants_1.RelType.FontTable, "fontTable.xml");
|
|
1455
|
+
archive.add("word/glossary/_rels/document.xml.rels", renderXml(xml => (0, relationships_1.renderRelationships)(glossaryRels, xml)));
|
|
1456
|
+
archive.add("word/glossary/styles.xml", renderXml(xml => (0, styles_writer_1.renderStyles)(xml, doc.docDefaults, doc.styles)));
|
|
1457
|
+
archive.add("word/glossary/settings.xml", renderXml(xml => (0, parts_writer_1.renderSettings)(xml, undefined, rawXmlPolicy)));
|
|
1458
|
+
archive.add("word/glossary/webSettings.xml", renderXml(xml => (0, parts_writer_1.renderWebSettings)(xml, undefined, rawXmlPolicy)));
|
|
1459
|
+
archive.add("word/glossary/fontTable.xml", renderXml(xml => (0, parts_writer_1.renderFontTable)(xml, doc.fonts)));
|
|
1460
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, "/word/glossary/styles.xml", constants_1.ContentType.Styles);
|
|
1461
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, "/word/glossary/settings.xml", constants_1.ContentType.Settings);
|
|
1462
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, "/word/glossary/webSettings.xml", constants_1.ContentType.WebSettings);
|
|
1463
|
+
(0, content_types_1.addContentTypeOverride)(contentTypes, "/word/glossary/fontTable.xml", constants_1.ContentType.FontTable);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1384
1466
|
// Write opaque (unrecognized) parts for round-trip preservation.
|
|
1385
1467
|
//
|
|
1386
1468
|
// Opaque parts are written as-is into the ZIP. Their paths must not
|
|
@@ -1421,8 +1503,23 @@ async function _packageDocxInner(doc, options) {
|
|
|
1421
1503
|
constants_1.PartPath.CustomProps,
|
|
1422
1504
|
constants_1.PartPath.Thumbnail,
|
|
1423
1505
|
"word/vbaProject.bin",
|
|
1424
|
-
"word/_rels/vbaProject.bin.rels"
|
|
1506
|
+
"word/_rels/vbaProject.bin.rels",
|
|
1507
|
+
"word/glossary/document.xml",
|
|
1508
|
+
"word/glossary/_rels/document.xml.rels"
|
|
1425
1509
|
]);
|
|
1510
|
+
// Glossary sub-document parts (emitted either verbatim from rawParts or
|
|
1511
|
+
// synthesised); reserve them so opaqueParts can't collide.
|
|
1512
|
+
if (doc.glossary && (doc.glossary.rawXml || doc.glossary.blocks.length > 0)) {
|
|
1513
|
+
reservedExact.add("word/glossary/styles.xml");
|
|
1514
|
+
reservedExact.add("word/glossary/settings.xml");
|
|
1515
|
+
reservedExact.add("word/glossary/webSettings.xml");
|
|
1516
|
+
reservedExact.add("word/glossary/fontTable.xml");
|
|
1517
|
+
if (doc.glossary.rawParts) {
|
|
1518
|
+
for (const path of doc.glossary.rawParts.keys()) {
|
|
1519
|
+
reservedExact.add(path);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1426
1523
|
// Headers/footers we are emitting in this run.
|
|
1427
1524
|
if (doc.headers) {
|
|
1428
1525
|
let i = 1;
|
|
@@ -1466,6 +1563,15 @@ async function _packageDocxInner(doc, options) {
|
|
|
1466
1563
|
}
|
|
1467
1564
|
}
|
|
1468
1565
|
}
|
|
1566
|
+
// OLE embedded objects emitted by this run.
|
|
1567
|
+
if (doc.oleObjects) {
|
|
1568
|
+
for (const ole of doc.oleObjects) {
|
|
1569
|
+
reservedExact.add(ole.path);
|
|
1570
|
+
if (ole.previewPath) {
|
|
1571
|
+
reservedExact.add(ole.previewPath);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1469
1575
|
for (const part of doc.opaqueParts) {
|
|
1470
1576
|
if (reservedExact.has(part.path)) {
|
|
1471
1577
|
throw new errors_1.DocxWriteError(`Opaque part path "${part.path}" conflicts with a part the packager ` +
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DOCX Module — Glossary (Building Blocks) Part Writer
|
|
4
|
+
*
|
|
5
|
+
* Serialises a {@link GlossaryDocument} into the canonical
|
|
6
|
+
* `word/glossary/document.xml` OOXML form:
|
|
7
|
+
*
|
|
8
|
+
* ```xml
|
|
9
|
+
* <w:glossaryDocument>
|
|
10
|
+
* <w:docParts>
|
|
11
|
+
* <w:docPart>
|
|
12
|
+
* <w:docPartPr>
|
|
13
|
+
* <w:name w:val="…"/>
|
|
14
|
+
* <w:category><w:name w:val="…"/><w:gallery w:val="…"/></w:category>
|
|
15
|
+
* <w:behaviors><w:behavior w:val="content"/></w:behaviors>
|
|
16
|
+
* <w:guid w:val="{…}"/>
|
|
17
|
+
* </w:docPartPr>
|
|
18
|
+
* <w:docPartBody>… body content …</w:docPartBody>
|
|
19
|
+
* </w:docPart>
|
|
20
|
+
* </w:docParts>
|
|
21
|
+
* </w:glossaryDocument>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Lives in the writer layer (not `advanced/`) so the packager can depend on
|
|
25
|
+
* it without creating a `advanced/ → writer/` import cycle.
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.renderGlossaryDocument = renderGlossaryDocument;
|
|
29
|
+
const writer_1 = require("../../xml/writer.js");
|
|
30
|
+
const constants_1 = require("../constants");
|
|
31
|
+
const document_writer_1 = require("./document-writer");
|
|
32
|
+
/**
|
|
33
|
+
* Map the friendly {@link BuildingBlockGallery} token to the OOXML
|
|
34
|
+
* `ST_DocPartGallery` value used in `<w:gallery w:val="…">` (ECMA-376
|
|
35
|
+
* §17.18.23). Values MUST be exact enum members — Word rejects (and silently
|
|
36
|
+
* discards) the entire glossary if it sees an out-of-enum gallery value.
|
|
37
|
+
* There is no plain "quickParts" value; Quick Parts map to "custQuickParts".
|
|
38
|
+
*/
|
|
39
|
+
const GALLERY_TO_OOXML = {
|
|
40
|
+
autoText: "autoTxt",
|
|
41
|
+
quickParts: "custQuickParts",
|
|
42
|
+
coverPages: "coverPg",
|
|
43
|
+
tableOfContents: "tblOfContents",
|
|
44
|
+
headers: "hdrs",
|
|
45
|
+
footers: "ftrs",
|
|
46
|
+
pageNumbers: "pgNum",
|
|
47
|
+
tables: "tbls",
|
|
48
|
+
textBoxes: "txtBox",
|
|
49
|
+
watermarks: "watermarks",
|
|
50
|
+
equations: "eq",
|
|
51
|
+
bibliographies: "bib",
|
|
52
|
+
custom1: "custom1",
|
|
53
|
+
custom2: "custom2",
|
|
54
|
+
custom3: "custom3",
|
|
55
|
+
custom4: "custom4",
|
|
56
|
+
custom5: "custom5"
|
|
57
|
+
};
|
|
58
|
+
/** Render a {@link GlossaryDocument} to a `word/glossary/document.xml` string. */
|
|
59
|
+
function renderGlossaryDocument(glossary) {
|
|
60
|
+
// Byte-faithful round-trip: a glossary read from an existing document is
|
|
61
|
+
// carried as verbatim XML and re-emitted unchanged.
|
|
62
|
+
if (glossary.rawXml) {
|
|
63
|
+
return glossary.rawXml;
|
|
64
|
+
}
|
|
65
|
+
const writer = new writer_1.XmlWriter();
|
|
66
|
+
renderGlossary(writer, glossary);
|
|
67
|
+
return writer.xml;
|
|
68
|
+
}
|
|
69
|
+
function renderGlossary(xml, glossary) {
|
|
70
|
+
xml.openXml(constants_1.STD_DOC_ATTRIBUTES);
|
|
71
|
+
xml.openNode("w:glossaryDocument", constants_1.DOCUMENT_NAMESPACES);
|
|
72
|
+
xml.openNode("w:docParts");
|
|
73
|
+
for (const block of glossary.blocks) {
|
|
74
|
+
renderDocPart(xml, block);
|
|
75
|
+
}
|
|
76
|
+
xml.closeNode(); // w:docParts
|
|
77
|
+
xml.closeNode(); // w:glossaryDocument
|
|
78
|
+
}
|
|
79
|
+
function renderDocPart(xml, block) {
|
|
80
|
+
xml.openNode("w:docPart");
|
|
81
|
+
// CT_DocPartPr — child order is fixed by the schema (ECMA-376 §17.12.1):
|
|
82
|
+
// name → style → category → types → behaviors → description → guid.
|
|
83
|
+
// Emitting these out of order makes Word reject the package on open.
|
|
84
|
+
xml.openNode("w:docPartPr");
|
|
85
|
+
xml.leafNode("w:name", { "w:val": block.name });
|
|
86
|
+
xml.openNode("w:category");
|
|
87
|
+
xml.leafNode("w:name", { "w:val": block.category ?? "General" });
|
|
88
|
+
xml.leafNode("w:gallery", { "w:val": GALLERY_TO_OOXML[block.gallery] ?? "placeholder" });
|
|
89
|
+
xml.closeNode(); // w:category
|
|
90
|
+
// `<w:types>` is optional; we omit it so we never emit an out-of-enum
|
|
91
|
+
// ST_DocPartType value. A docPart placed in the body inserts its content.
|
|
92
|
+
xml.openNode("w:behaviors");
|
|
93
|
+
xml.leafNode("w:behavior", { "w:val": "content" });
|
|
94
|
+
xml.closeNode(); // w:behaviors
|
|
95
|
+
if (block.description) {
|
|
96
|
+
xml.leafNode("w:description", { "w:val": block.description });
|
|
97
|
+
}
|
|
98
|
+
if (block.guid) {
|
|
99
|
+
xml.leafNode("w:guid", { "w:val": normaliseGuid(block.guid) });
|
|
100
|
+
}
|
|
101
|
+
xml.closeNode(); // w:docPartPr
|
|
102
|
+
// docPartBody — reuse the main body renderer. A fresh render context keeps
|
|
103
|
+
// id counters local; building-block content here is plain text/tables so it
|
|
104
|
+
// does not need image/chart rId remapping.
|
|
105
|
+
xml.openNode("w:docPartBody");
|
|
106
|
+
for (const item of block.content) {
|
|
107
|
+
(0, document_writer_1.renderBodyContent)(xml, item);
|
|
108
|
+
}
|
|
109
|
+
// CT_Body must end with a paragraph; emit one if the block had no content.
|
|
110
|
+
if (block.content.length === 0) {
|
|
111
|
+
xml.openNode("w:p");
|
|
112
|
+
xml.closeNode();
|
|
113
|
+
}
|
|
114
|
+
xml.closeNode(); // w:docPartBody
|
|
115
|
+
xml.closeNode(); // w:docPart
|
|
116
|
+
}
|
|
117
|
+
/** Word expects the docPart guid wrapped in braces: `{XXXXXXXX-…}`. */
|
|
118
|
+
function normaliseGuid(guid) {
|
|
119
|
+
const trimmed = guid.trim();
|
|
120
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
121
|
+
return trimmed;
|
|
122
|
+
}
|
|
123
|
+
return `{${trimmed}}`;
|
|
124
|
+
}
|
|
@@ -71,12 +71,17 @@ function renderWatermarkHeader(xml, watermark, imageRId) {
|
|
|
71
71
|
"xmlns:o": constants_1.NS_O,
|
|
72
72
|
"xmlns:w10": constants_1.NS_W10
|
|
73
73
|
});
|
|
74
|
-
// Watermark
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
// for an auto-generated watermark header.
|
|
74
|
+
// Watermark lives in a paragraph styled as a Header, matching what
|
|
75
|
+
// Microsoft Word emits. The run carries <w:noProof/> so the WordArt
|
|
76
|
+
// text is not spell-checked.
|
|
78
77
|
xml.openNode("w:p");
|
|
78
|
+
xml.openNode("w:pPr");
|
|
79
|
+
xml.leafNode("w:pStyle", { "w:val": "Header" });
|
|
80
|
+
xml.closeNode(); // pPr
|
|
79
81
|
xml.openNode("w:r");
|
|
82
|
+
xml.openNode("w:rPr");
|
|
83
|
+
xml.leafNode("w:noProof");
|
|
84
|
+
xml.closeNode(); // rPr
|
|
80
85
|
xml.openNode("w:pict");
|
|
81
86
|
if (watermark.type === "text") {
|
|
82
87
|
renderTextWatermarkVml(xml, watermark);
|
|
@@ -92,49 +97,130 @@ function renderWatermarkHeader(xml, watermark, imageRId) {
|
|
|
92
97
|
function renderTextWatermarkVml(xml, wm) {
|
|
93
98
|
const color = wm.color ?? "C0C0C0";
|
|
94
99
|
const font = wm.font ?? "Calibri";
|
|
95
|
-
const fontSize = wm.fontSize ?? 1; // half-points; Word uses pt string in style
|
|
96
|
-
const fontPt = fontSize / 2;
|
|
97
100
|
const rotation = wm.rotation ?? -45;
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
+
// Full WordArt shapetype definition (t136) exactly as Microsoft Word
|
|
102
|
+
// emits it. Word for Mac will NOT render the text path unless the
|
|
103
|
+
// shapetype carries the formula/path/textpath/handles/lock children —
|
|
104
|
+
// an empty <v:shapetype/> produces an invisible watermark.
|
|
105
|
+
xml.openNode("v:shapetype", {
|
|
101
106
|
id: "_x0000_t136",
|
|
102
107
|
coordsize: "21600,21600",
|
|
103
108
|
"o:spt": "136",
|
|
109
|
+
adj: "10800",
|
|
104
110
|
path: "m@7,l@8,m@5,21600l@6,21600e"
|
|
105
111
|
});
|
|
112
|
+
xml.openNode("v:formulas");
|
|
113
|
+
for (const eqn of [
|
|
114
|
+
"sum #0 0 10800",
|
|
115
|
+
"prod #0 2 1",
|
|
116
|
+
"sum 21600 0 @1",
|
|
117
|
+
"sum 0 0 @2",
|
|
118
|
+
"sum 21600 0 @3",
|
|
119
|
+
"if @0 @3 0",
|
|
120
|
+
"if @0 21600 @1",
|
|
121
|
+
"if @0 0 @2",
|
|
122
|
+
"if @0 @4 21600",
|
|
123
|
+
"mid @5 @6",
|
|
124
|
+
"mid @8 @5",
|
|
125
|
+
"mid @7 @8",
|
|
126
|
+
"mid @6 @7",
|
|
127
|
+
"sum @6 0 @5"
|
|
128
|
+
]) {
|
|
129
|
+
xml.leafNode("v:f", { eqn });
|
|
130
|
+
}
|
|
131
|
+
xml.closeNode(); // formulas
|
|
132
|
+
xml.leafNode("v:path", {
|
|
133
|
+
textpathok: "t",
|
|
134
|
+
"o:connecttype": "custom",
|
|
135
|
+
"o:connectlocs": "@9,0;@10,10800;@11,21600;@12,10800",
|
|
136
|
+
"o:connectangles": "270,180,90,0"
|
|
137
|
+
});
|
|
138
|
+
xml.leafNode("v:textpath", { on: "t", fitshape: "t" });
|
|
139
|
+
xml.openNode("v:handles");
|
|
140
|
+
xml.leafNode("v:h", { position: "#0,bottomRight", xrange: "6629,14971" });
|
|
141
|
+
xml.closeNode(); // handles
|
|
142
|
+
xml.leafNode("o:lock", { "v:ext": "edit", text: "t", shapetype: "t" });
|
|
143
|
+
xml.closeNode(); // shapetype
|
|
144
|
+
// The watermark shape itself. Word fixes font-size at 1pt and relies on
|
|
145
|
+
// fitshape="t" to scale the text to fill the shape box; rotation is
|
|
146
|
+
// applied on the shape via the style string.
|
|
106
147
|
const style = `position:absolute;margin-left:0;margin-top:0;width:468pt;height:234pt;` +
|
|
107
|
-
`rotation:${rotation}
|
|
108
|
-
`mso-position-horizontal
|
|
109
|
-
`mso-position-vertical-relative:margin`;
|
|
148
|
+
`${rotation ? `rotation:${rotation};` : ""}z-index:-251658752;` +
|
|
149
|
+
`mso-position-horizontal:center;mso-position-horizontal-relative:margin;` +
|
|
150
|
+
`mso-position-vertical:center;mso-position-vertical-relative:margin`;
|
|
110
151
|
xml.openNode("v:shape", {
|
|
111
152
|
id: "PowerPlusWaterMarkObject",
|
|
112
153
|
"o:spid": "_x0000_s2049",
|
|
113
154
|
type: "#_x0000_t136",
|
|
155
|
+
alt: "",
|
|
114
156
|
style,
|
|
115
157
|
"o:allowincell": "f",
|
|
116
158
|
fillcolor: `#${color}`,
|
|
117
159
|
stroked: "f"
|
|
118
160
|
});
|
|
119
|
-
|
|
161
|
+
if (wm.semiTransparent !== false) {
|
|
162
|
+
xml.leafNode("v:fill", { opacity: ".5" });
|
|
163
|
+
}
|
|
120
164
|
xml.leafNode("v:textpath", {
|
|
121
|
-
style: `font-family
|
|
165
|
+
style: `font-family:"${font}";font-size:1pt`,
|
|
122
166
|
string: wm.text
|
|
123
167
|
});
|
|
124
|
-
xml.leafNode("w10:wrap", { anchorx: "margin", anchory: "margin" });
|
|
125
168
|
xml.closeNode(); // v:shape
|
|
126
169
|
}
|
|
127
170
|
function renderImageWatermarkVml(xml, wm, rId) {
|
|
128
|
-
const scale = wm.scale ?? 100;
|
|
129
171
|
const rid = rId ?? wm.rId;
|
|
130
|
-
|
|
172
|
+
// Default to a large area covering most of the body so the picture is
|
|
173
|
+
// actually visible. width:0;height:0 produces an invisible dot.
|
|
174
|
+
const widthPt = wm.widthPt ?? 415.2;
|
|
175
|
+
const heightPt = wm.heightPt ?? 233.5;
|
|
176
|
+
// Picture-frame shapetype (t75) as Microsoft Word emits it. Without a
|
|
177
|
+
// proper shapetype + non-zero size the image watermark will not render.
|
|
178
|
+
xml.openNode("v:shapetype", {
|
|
179
|
+
id: "_x0000_t75",
|
|
180
|
+
coordsize: "21600,21600",
|
|
181
|
+
"o:spt": "75",
|
|
182
|
+
"o:preferrelative": "t",
|
|
183
|
+
path: "m@4@5l@4@11@9@11@9@5xe",
|
|
184
|
+
filled: "f",
|
|
185
|
+
stroked: "f"
|
|
186
|
+
});
|
|
187
|
+
xml.openNode("v:stroke", { joinstyle: "miter" });
|
|
188
|
+
xml.closeNode();
|
|
189
|
+
xml.openNode("v:formulas");
|
|
190
|
+
for (const eqn of [
|
|
191
|
+
"if lineDrawn pixelLineWidth 0",
|
|
192
|
+
"sum @0 1 0",
|
|
193
|
+
"sum 0 0 @1",
|
|
194
|
+
"prod @2 1 2",
|
|
195
|
+
"prod @3 21600 pixelWidth",
|
|
196
|
+
"prod @3 21600 pixelHeight",
|
|
197
|
+
"sum @0 0 1",
|
|
198
|
+
"prod @6 1 2",
|
|
199
|
+
"prod @7 21600 pixelWidth",
|
|
200
|
+
"sum @8 21600 0",
|
|
201
|
+
"prod @7 21600 pixelHeight",
|
|
202
|
+
"sum @10 21600 0"
|
|
203
|
+
]) {
|
|
204
|
+
xml.leafNode("v:f", { eqn });
|
|
205
|
+
}
|
|
206
|
+
xml.closeNode(); // formulas
|
|
207
|
+
xml.leafNode("v:path", {
|
|
208
|
+
"o:extrusionok": "f",
|
|
209
|
+
gradientshapeok: "t",
|
|
210
|
+
"o:connecttype": "rect"
|
|
211
|
+
});
|
|
212
|
+
xml.leafNode("o:lock", { "v:ext": "edit", aspectratio: "t" });
|
|
213
|
+
xml.closeNode(); // shapetype
|
|
214
|
+
const style = `position:absolute;margin-left:0;margin-top:0;` +
|
|
215
|
+
`width:${widthPt}pt;height:${heightPt}pt;` +
|
|
131
216
|
`z-index:-251658752;mso-position-horizontal:center;` +
|
|
132
217
|
`mso-position-horizontal-relative:margin;mso-position-vertical:center;` +
|
|
133
218
|
`mso-position-vertical-relative:margin`;
|
|
134
219
|
xml.openNode("v:shape", {
|
|
135
220
|
id: "PowerPlusWaterMarkObject",
|
|
136
221
|
"o:spid": "_x0000_s2050",
|
|
137
|
-
type: "",
|
|
222
|
+
type: "#_x0000_t75",
|
|
223
|
+
alt: "",
|
|
138
224
|
style,
|
|
139
225
|
"o:allowincell": "f"
|
|
140
226
|
});
|
|
@@ -144,8 +230,7 @@ function renderImageWatermarkVml(xml, wm, rId) {
|
|
|
144
230
|
"r:id": rid,
|
|
145
231
|
"o:title": "",
|
|
146
232
|
gain,
|
|
147
|
-
blacklevel
|
|
148
|
-
...(scale !== 100 ? { "o:detectmouseclick": "t" } : {})
|
|
233
|
+
blacklevel
|
|
149
234
|
});
|
|
150
235
|
xml.leafNode("w10:wrap", { anchorx: "margin", anchory: "margin" });
|
|
151
236
|
xml.closeNode(); // v:shape
|
|
@@ -178,8 +178,13 @@ function renderMathPhantom(xml, p) {
|
|
|
178
178
|
p.transparent !== undefined;
|
|
179
179
|
if (hasProps) {
|
|
180
180
|
xml.openNode("m:phantPr");
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
// `m:show` defaults to ON in OOXML (the phantom base is still drawn). To
|
|
182
|
+
// make a phantom "occupy space but stay invisible" the producer must
|
|
183
|
+
// emit `<m:show m:val="0"/>` explicitly — simply omitting it leaves the
|
|
184
|
+
// content visible. So serialize `show` whenever it is defined, mapping
|
|
185
|
+
// false → "0" and true → "1".
|
|
186
|
+
if (p.show !== undefined) {
|
|
187
|
+
xml.leafNode("m:show", { "m:val": p.show ? "1" : "0" });
|
|
183
188
|
}
|
|
184
189
|
if (p.zeroWidth) {
|
|
185
190
|
xml.leafNode("m:zeroWid", { "m:val": "1" });
|