@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.
|
|
@@ -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,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
|
+
}
|
|
@@ -66,12 +66,17 @@ export function renderWatermarkHeader(xml, watermark, imageRId) {
|
|
|
66
66
|
"xmlns:o": NS_O,
|
|
67
67
|
"xmlns:w10": NS_W10
|
|
68
68
|
});
|
|
69
|
-
// Watermark
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
// for an auto-generated watermark header.
|
|
69
|
+
// Watermark lives in a paragraph styled as a Header, matching what
|
|
70
|
+
// Microsoft Word emits. The run carries <w:noProof/> so the WordArt
|
|
71
|
+
// text is not spell-checked.
|
|
73
72
|
xml.openNode("w:p");
|
|
73
|
+
xml.openNode("w:pPr");
|
|
74
|
+
xml.leafNode("w:pStyle", { "w:val": "Header" });
|
|
75
|
+
xml.closeNode(); // pPr
|
|
74
76
|
xml.openNode("w:r");
|
|
77
|
+
xml.openNode("w:rPr");
|
|
78
|
+
xml.leafNode("w:noProof");
|
|
79
|
+
xml.closeNode(); // rPr
|
|
75
80
|
xml.openNode("w:pict");
|
|
76
81
|
if (watermark.type === "text") {
|
|
77
82
|
renderTextWatermarkVml(xml, watermark);
|
|
@@ -87,49 +92,130 @@ export function renderWatermarkHeader(xml, watermark, imageRId) {
|
|
|
87
92
|
function renderTextWatermarkVml(xml, wm) {
|
|
88
93
|
const color = wm.color ?? "C0C0C0";
|
|
89
94
|
const font = wm.font ?? "Calibri";
|
|
90
|
-
const fontSize = wm.fontSize ?? 1; // half-points; Word uses pt string in style
|
|
91
|
-
const fontPt = fontSize / 2;
|
|
92
95
|
const rotation = wm.rotation ?? -45;
|
|
93
|
-
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
+
// Full WordArt shapetype definition (t136) exactly as Microsoft Word
|
|
97
|
+
// emits it. Word for Mac will NOT render the text path unless the
|
|
98
|
+
// shapetype carries the formula/path/textpath/handles/lock children —
|
|
99
|
+
// an empty <v:shapetype/> produces an invisible watermark.
|
|
100
|
+
xml.openNode("v:shapetype", {
|
|
96
101
|
id: "_x0000_t136",
|
|
97
102
|
coordsize: "21600,21600",
|
|
98
103
|
"o:spt": "136",
|
|
104
|
+
adj: "10800",
|
|
99
105
|
path: "m@7,l@8,m@5,21600l@6,21600e"
|
|
100
106
|
});
|
|
107
|
+
xml.openNode("v:formulas");
|
|
108
|
+
for (const eqn of [
|
|
109
|
+
"sum #0 0 10800",
|
|
110
|
+
"prod #0 2 1",
|
|
111
|
+
"sum 21600 0 @1",
|
|
112
|
+
"sum 0 0 @2",
|
|
113
|
+
"sum 21600 0 @3",
|
|
114
|
+
"if @0 @3 0",
|
|
115
|
+
"if @0 21600 @1",
|
|
116
|
+
"if @0 0 @2",
|
|
117
|
+
"if @0 @4 21600",
|
|
118
|
+
"mid @5 @6",
|
|
119
|
+
"mid @8 @5",
|
|
120
|
+
"mid @7 @8",
|
|
121
|
+
"mid @6 @7",
|
|
122
|
+
"sum @6 0 @5"
|
|
123
|
+
]) {
|
|
124
|
+
xml.leafNode("v:f", { eqn });
|
|
125
|
+
}
|
|
126
|
+
xml.closeNode(); // formulas
|
|
127
|
+
xml.leafNode("v:path", {
|
|
128
|
+
textpathok: "t",
|
|
129
|
+
"o:connecttype": "custom",
|
|
130
|
+
"o:connectlocs": "@9,0;@10,10800;@11,21600;@12,10800",
|
|
131
|
+
"o:connectangles": "270,180,90,0"
|
|
132
|
+
});
|
|
133
|
+
xml.leafNode("v:textpath", { on: "t", fitshape: "t" });
|
|
134
|
+
xml.openNode("v:handles");
|
|
135
|
+
xml.leafNode("v:h", { position: "#0,bottomRight", xrange: "6629,14971" });
|
|
136
|
+
xml.closeNode(); // handles
|
|
137
|
+
xml.leafNode("o:lock", { "v:ext": "edit", text: "t", shapetype: "t" });
|
|
138
|
+
xml.closeNode(); // shapetype
|
|
139
|
+
// The watermark shape itself. Word fixes font-size at 1pt and relies on
|
|
140
|
+
// fitshape="t" to scale the text to fill the shape box; rotation is
|
|
141
|
+
// applied on the shape via the style string.
|
|
101
142
|
const style = `position:absolute;margin-left:0;margin-top:0;width:468pt;height:234pt;` +
|
|
102
|
-
`rotation:${rotation}
|
|
103
|
-
`mso-position-horizontal
|
|
104
|
-
`mso-position-vertical-relative:margin`;
|
|
143
|
+
`${rotation ? `rotation:${rotation};` : ""}z-index:-251658752;` +
|
|
144
|
+
`mso-position-horizontal:center;mso-position-horizontal-relative:margin;` +
|
|
145
|
+
`mso-position-vertical:center;mso-position-vertical-relative:margin`;
|
|
105
146
|
xml.openNode("v:shape", {
|
|
106
147
|
id: "PowerPlusWaterMarkObject",
|
|
107
148
|
"o:spid": "_x0000_s2049",
|
|
108
149
|
type: "#_x0000_t136",
|
|
150
|
+
alt: "",
|
|
109
151
|
style,
|
|
110
152
|
"o:allowincell": "f",
|
|
111
153
|
fillcolor: `#${color}`,
|
|
112
154
|
stroked: "f"
|
|
113
155
|
});
|
|
114
|
-
|
|
156
|
+
if (wm.semiTransparent !== false) {
|
|
157
|
+
xml.leafNode("v:fill", { opacity: ".5" });
|
|
158
|
+
}
|
|
115
159
|
xml.leafNode("v:textpath", {
|
|
116
|
-
style: `font-family
|
|
160
|
+
style: `font-family:"${font}";font-size:1pt`,
|
|
117
161
|
string: wm.text
|
|
118
162
|
});
|
|
119
|
-
xml.leafNode("w10:wrap", { anchorx: "margin", anchory: "margin" });
|
|
120
163
|
xml.closeNode(); // v:shape
|
|
121
164
|
}
|
|
122
165
|
function renderImageWatermarkVml(xml, wm, rId) {
|
|
123
|
-
const scale = wm.scale ?? 100;
|
|
124
166
|
const rid = rId ?? wm.rId;
|
|
125
|
-
|
|
167
|
+
// Default to a large area covering most of the body so the picture is
|
|
168
|
+
// actually visible. width:0;height:0 produces an invisible dot.
|
|
169
|
+
const widthPt = wm.widthPt ?? 415.2;
|
|
170
|
+
const heightPt = wm.heightPt ?? 233.5;
|
|
171
|
+
// Picture-frame shapetype (t75) as Microsoft Word emits it. Without a
|
|
172
|
+
// proper shapetype + non-zero size the image watermark will not render.
|
|
173
|
+
xml.openNode("v:shapetype", {
|
|
174
|
+
id: "_x0000_t75",
|
|
175
|
+
coordsize: "21600,21600",
|
|
176
|
+
"o:spt": "75",
|
|
177
|
+
"o:preferrelative": "t",
|
|
178
|
+
path: "m@4@5l@4@11@9@11@9@5xe",
|
|
179
|
+
filled: "f",
|
|
180
|
+
stroked: "f"
|
|
181
|
+
});
|
|
182
|
+
xml.openNode("v:stroke", { joinstyle: "miter" });
|
|
183
|
+
xml.closeNode();
|
|
184
|
+
xml.openNode("v:formulas");
|
|
185
|
+
for (const eqn of [
|
|
186
|
+
"if lineDrawn pixelLineWidth 0",
|
|
187
|
+
"sum @0 1 0",
|
|
188
|
+
"sum 0 0 @1",
|
|
189
|
+
"prod @2 1 2",
|
|
190
|
+
"prod @3 21600 pixelWidth",
|
|
191
|
+
"prod @3 21600 pixelHeight",
|
|
192
|
+
"sum @0 0 1",
|
|
193
|
+
"prod @6 1 2",
|
|
194
|
+
"prod @7 21600 pixelWidth",
|
|
195
|
+
"sum @8 21600 0",
|
|
196
|
+
"prod @7 21600 pixelHeight",
|
|
197
|
+
"sum @10 21600 0"
|
|
198
|
+
]) {
|
|
199
|
+
xml.leafNode("v:f", { eqn });
|
|
200
|
+
}
|
|
201
|
+
xml.closeNode(); // formulas
|
|
202
|
+
xml.leafNode("v:path", {
|
|
203
|
+
"o:extrusionok": "f",
|
|
204
|
+
gradientshapeok: "t",
|
|
205
|
+
"o:connecttype": "rect"
|
|
206
|
+
});
|
|
207
|
+
xml.leafNode("o:lock", { "v:ext": "edit", aspectratio: "t" });
|
|
208
|
+
xml.closeNode(); // shapetype
|
|
209
|
+
const style = `position:absolute;margin-left:0;margin-top:0;` +
|
|
210
|
+
`width:${widthPt}pt;height:${heightPt}pt;` +
|
|
126
211
|
`z-index:-251658752;mso-position-horizontal:center;` +
|
|
127
212
|
`mso-position-horizontal-relative:margin;mso-position-vertical:center;` +
|
|
128
213
|
`mso-position-vertical-relative:margin`;
|
|
129
214
|
xml.openNode("v:shape", {
|
|
130
215
|
id: "PowerPlusWaterMarkObject",
|
|
131
216
|
"o:spid": "_x0000_s2050",
|
|
132
|
-
type: "",
|
|
217
|
+
type: "#_x0000_t75",
|
|
218
|
+
alt: "",
|
|
133
219
|
style,
|
|
134
220
|
"o:allowincell": "f"
|
|
135
221
|
});
|
|
@@ -139,8 +225,7 @@ function renderImageWatermarkVml(xml, wm, rId) {
|
|
|
139
225
|
"r:id": rid,
|
|
140
226
|
"o:title": "",
|
|
141
227
|
gain,
|
|
142
|
-
blacklevel
|
|
143
|
-
...(scale !== 100 ? { "o:detectmouseclick": "t" } : {})
|
|
228
|
+
blacklevel
|
|
144
229
|
});
|
|
145
230
|
xml.leafNode("w10:wrap", { anchorx: "margin", anchory: "margin" });
|
|
146
231
|
xml.closeNode(); // v:shape
|
|
@@ -175,8 +175,13 @@ function renderMathPhantom(xml, p) {
|
|
|
175
175
|
p.transparent !== undefined;
|
|
176
176
|
if (hasProps) {
|
|
177
177
|
xml.openNode("m:phantPr");
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
// `m:show` defaults to ON in OOXML (the phantom base is still drawn). To
|
|
179
|
+
// make a phantom "occupy space but stay invisible" the producer must
|
|
180
|
+
// emit `<m:show m:val="0"/>` explicitly — simply omitting it leaves the
|
|
181
|
+
// content visible. So serialize `show` whenever it is defined, mapping
|
|
182
|
+
// false → "0" and true → "1".
|
|
183
|
+
if (p.show !== undefined) {
|
|
184
|
+
xml.leafNode("m:show", { "m:val": p.show ? "1" : "0" });
|
|
180
185
|
}
|
|
181
186
|
if (p.zeroWidth) {
|
|
182
187
|
xml.leafNode("m:zeroWid", { "m:val": "1" });
|