@json-to-office/core-docx 7.5.1 → 7.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/fontEmbedding.d.ts +39 -0
- package/dist/core/fontEmbedding.d.ts.map +1 -0
- package/dist/core/fontResolution.d.ts +13 -9
- package/dist/core/fontResolution.d.ts.map +1 -1
- package/dist/core/generateFromIr.d.ts +8 -1
- package/dist/core/generateFromIr.d.ts.map +1 -1
- package/dist/core/generationContext.d.ts.map +1 -1
- package/dist/index.js +176 -29
- package/dist/index.js.map +1 -1
- package/dist/plugin/example/index.js +164 -17
- package/dist/plugin/example/index.js.map +1 -1
- package/dist/renderers/docxjs/index.d.ts +3 -1
- package/dist/renderers/docxjs/index.d.ts.map +1 -1
- package/dist/renderers/types.d.ts +11 -1
- package/dist/renderers/types.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/packageDocument.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `embed` export mode's share of the DOCX pipeline: whether this render
|
|
3
|
+
* embeds at all, which faces it embeds, and the one summary warning.
|
|
4
|
+
*
|
|
5
|
+
* Face selection itself — slots, formats, licence bits — lives in
|
|
6
|
+
* `@json-to-office/shared/fonts/node` (`toEmbeddedFontFaces`), beside the
|
|
7
|
+
* other encoders of resolved fonts. What stays here is what only the DOCX
|
|
8
|
+
* pipeline knows: which renderer will write the package, and what the
|
|
9
|
+
* document references, which is what the summary has to answer for.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here imports `docx`: the faces travel to the adapter as a render
|
|
12
|
+
* option (`DocxRenderOptions.embeddedFonts`), and only the docx.js adapter
|
|
13
|
+
* reads it.
|
|
14
|
+
*/
|
|
15
|
+
import type { EmbeddedFontFace, FontRuntimeOpts, GenerationWarning, ResolvedFont } from '@json-to-office/shared';
|
|
16
|
+
import { type DocxRendererId } from '../renderers/types';
|
|
17
|
+
/**
|
|
18
|
+
* Whether this render embeds fonts: `embed` was asked for, and the renderer
|
|
19
|
+
* that will write the package can do it.
|
|
20
|
+
*
|
|
21
|
+
* `office-open` cannot yet. Its published docx embedding names the part after
|
|
22
|
+
* the family, which is the defect docx.js fixed in 9.7.0, writes the regular
|
|
23
|
+
* face only and leaves `embedTrueTypeFonts` off, so a request for it falls
|
|
24
|
+
* back to `custom` — references kept, nothing embedded — and says so rather
|
|
25
|
+
* than shipping a package Word repairs or drops the fonts from on re-save.
|
|
26
|
+
*/
|
|
27
|
+
export declare function embedsFonts(fonts: FontRuntimeOpts | undefined, renderer: DocxRendererId | undefined, warnings: GenerationWarning[]): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* The faces to embed, with one `FONT_MODE_EMBED` summary for the document.
|
|
30
|
+
*
|
|
31
|
+
* The summary walks the resolved fonts rather than the report alone: the
|
|
32
|
+
* resolver hands back one entry per family the document and its theme
|
|
33
|
+
* reference — the same set `FONT_MODE_CUSTOM` is gated on — so a non-safe
|
|
34
|
+
* family that resolved to nothing still gets its line, instead of vanishing
|
|
35
|
+
* from a message that claims to account for the document's fonts. A document
|
|
36
|
+
* that references only safe fonts gets no summary, as in custom mode.
|
|
37
|
+
*/
|
|
38
|
+
export declare function selectEmbeddedFonts(resolved: readonly ResolvedFont[], warnings: GenerationWarning[]): EmbeddedFontFace[];
|
|
39
|
+
//# sourceMappingURL=fontEmbedding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontEmbedding.d.ts","sourceRoot":"","sources":["../../src/core/fontEmbedding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,YAAY,EACb,MAAM,wBAAwB,CAAC;AAMhC,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAY5B;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,GAAG,SAAS,EAClC,QAAQ,EAAE,cAAc,GAAG,SAAS,EACpC,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,OAAO,CAaT;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,SAAS,YAAY,EAAE,EACjC,QAAQ,EAAE,iBAAiB,EAAE,GAC5B,gBAAgB,EAAE,CAKpB"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Font resolution for a DOCX render: validation, SAFE_FONTS awareness, Google
|
|
3
|
+
* Fonts materialization, and the `onResolved` side-channel consumed by the
|
|
4
|
+
* LibreOffice preview font stager.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* validation, SAFE_FONTS awareness, Google Fonts materialization, and the
|
|
9
|
-
* `onResolved` side-channel consumed by the LibreOffice preview font stager.
|
|
6
|
+
* One call site, `compileDocumentScoped` in `generateFromIr.ts`, which both
|
|
7
|
+
* public entry paths — `generator.ts` and the plugin-aware
|
|
8
|
+
* `plugin/createDocumentGenerator.ts` — reach, so they cannot disagree about
|
|
9
|
+
* any of the above.
|
|
10
10
|
*
|
|
11
11
|
* Materialization has two triggers: an `onResolved` listener, or the
|
|
12
12
|
* caller's `forceMaterialize` flag (set when the document contains a
|
|
13
13
|
* `visual`, whose PNG is produced by an out-of-process LibreOffice that
|
|
14
|
-
* needs real font files
|
|
15
|
-
*
|
|
14
|
+
* needs real font files, a `highcharts`, whose export server needs them
|
|
15
|
+
* likewise, or when `embed` mode will write them into the package). The
|
|
16
|
+
* resolved fonts are returned so the caller can forward them to whichever
|
|
17
|
+
* of those needs them.
|
|
16
18
|
*/
|
|
17
19
|
import type { FontRuntimeOpts, ResolvedFont, GenerationWarning } from '@json-to-office/shared';
|
|
18
20
|
import type { ThemeConfig } from '../styles';
|
|
@@ -24,6 +26,8 @@ export declare function resolveDocumentFonts(document: ReportComponentDefinition
|
|
|
24
26
|
* whose PNG is rendered by an out-of-process LibreOffice that needs the
|
|
25
27
|
* actual font files — there is no listener on the plain CLI path, so
|
|
26
28
|
* without this a visual-bearing build silently renders with fallbacks.
|
|
29
|
+
* `embed` mode sets it for the same reason: without it there would be
|
|
30
|
+
* nothing to embed.
|
|
27
31
|
*/
|
|
28
32
|
forceMaterialize?: boolean): Promise<ResolvedFont[]>;
|
|
29
33
|
//# sourceMappingURL=fontResolution.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fontResolution.d.ts","sourceRoot":"","sources":["../../src/core/fontResolution.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fontResolution.d.ts","sourceRoot":"","sources":["../../src/core/fontResolution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAchC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAE1D,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,yBAAyB,EACnC,KAAK,EAAE,WAAW,EAClB,KAAK,CAAC,EAAE,eAAe,EACvB,QAAQ,CAAC,EAAE,iBAAiB,EAAE;AAC9B;;;;;;;;GAQG;AACH,gBAAgB,UAAQ,GACvB,OAAO,CAAC,YAAY,EAAE,CAAC,CAmFzB"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
/// <reference types="node" />
|
|
16
16
|
/// <reference types="node" />
|
|
17
17
|
import type { GenerationWarning, ServicesConfig } from '@json-to-office/shared';
|
|
18
|
-
import type { FontRuntimeOpts } from '@json-to-office/shared';
|
|
18
|
+
import type { EmbeddedFontFace, FontRuntimeOpts } from '@json-to-office/shared';
|
|
19
19
|
import { compileDocument, type UnsupportedComponent } from '../ir/compiler';
|
|
20
20
|
import type { DocxIR } from '../ir/types';
|
|
21
21
|
import type { DocxRendererId } from '../renderers/types';
|
|
@@ -76,6 +76,13 @@ export interface CompiledDocx {
|
|
|
76
76
|
warnings: GenerationWarning[];
|
|
77
77
|
required: ReturnType<typeof compileDocument>['required'];
|
|
78
78
|
unsupported: UnsupportedComponent[];
|
|
79
|
+
/**
|
|
80
|
+
* The font faces `embed` mode writes into the package; empty in every other
|
|
81
|
+
* mode. Kept beside the IR rather than in it: they are a resource of this
|
|
82
|
+
* render, chosen for the renderer that will write it, not something the
|
|
83
|
+
* document says, and the IR stays the same in every mode.
|
|
84
|
+
*/
|
|
85
|
+
embeddedFonts: EmbeddedFontFace[];
|
|
79
86
|
}
|
|
80
87
|
/**
|
|
81
88
|
* Compile a report definition to DocxIR without rendering it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateFromIr.d.ts","sourceRoot":"","sources":["../../src/core/generateFromIr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;;;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"generateFromIr.d.ts","sourceRoot":"","sources":["../../src/core/generateFromIr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;;;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAWhF,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAE1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAKlE,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC,iEAAiE;IACjE,QAAQ,CAAC,EAAE,gBAAgB,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,SAAgB,IAAI,0BAA0B;IAC9C,SAAgB,UAAU,EAAE,SAAS,oBAAoB,EAAE,CAAC;gBAEhD,UAAU,EAAE,SAAS,oBAAoB,EAAE;CAiBxD;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,QAAQ,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC,UAAU,CAAC,CAAC;IACzD,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC;;;;;OAKG;IACH,aAAa,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,yBAAyB,EACnC,OAAO,GAAE,uBAA4B,EACrC,SAAS,CAAC,EAAE,iBAAiB,EAAE,GAC9B,OAAO,CAAC,YAAY,CAAC,CAOvB;AAsHD,0EAA0E;AAC1E,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,yBAAyB,EACnC,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAcjC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generationContext.d.ts","sourceRoot":"","sources":["../../src/core/generationContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAMhC,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC;IAC9C,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC/B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,iBAAiB,EAAE,GAAG,SAAS,EACzC,QAAQ,EAAE,OAAO,KACd,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,sEAAsE;IACtE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,2EAA2E;IAC3E,QAAQ,EAAE,yBAAyB,CAAC;IACpC,KAAK,EAAE,WAAW,CAAC;IACnB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAoBD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,yBAAyB,EACrC,OAAO,GAAE,mBAAwB,GAChC,sBAAsB,
|
|
1
|
+
{"version":3,"file":"generationContext.d.ts","sourceRoot":"","sources":["../../src/core/generationContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAMhC,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC;IAC9C,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC/B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,iBAAiB,EAAE,GAAG,SAAS,EACzC,QAAQ,EAAE,OAAO,KACd,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,sEAAsE;IACtE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,2EAA2E;IAC3E,QAAQ,EAAE,yBAAyB,CAAC;IACpC,KAAK,EAAE,WAAW,CAAC;IACnB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAoBD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,yBAAyB,EACrC,OAAO,GAAE,mBAAwB,GAChC,sBAAsB,CAgExB"}
|
package/dist/index.js
CHANGED
|
@@ -4265,6 +4265,8 @@ var init_normalizeTextCase = __esm({
|
|
|
4265
4265
|
|
|
4266
4266
|
// src/utils/packageDocument.ts
|
|
4267
4267
|
import AdmZip from "adm-zip";
|
|
4268
|
+
import { createHash as createHash3 } from "crypto";
|
|
4269
|
+
import { posix } from "path";
|
|
4268
4270
|
function resolveGenerationDate(options) {
|
|
4269
4271
|
if (options?.generatedAt !== void 0) {
|
|
4270
4272
|
const date = new Date(options.generatedAt);
|
|
@@ -4332,6 +4334,70 @@ function canonicalizeRelationshipIds(zip) {
|
|
|
4332
4334
|
if (entry) zip.updateFile(entry, Buffer.from(update.xml, "utf8"));
|
|
4333
4335
|
}
|
|
4334
4336
|
}
|
|
4337
|
+
function xorFontKey(bytes, guid) {
|
|
4338
|
+
const key = Buffer.from(guid.replace(/-/g, ""), "hex").reverse();
|
|
4339
|
+
const out = Buffer.from(bytes);
|
|
4340
|
+
for (let i = 0; i < Math.min(32, out.length); i++) out[i] ^= key[i % 16];
|
|
4341
|
+
return out;
|
|
4342
|
+
}
|
|
4343
|
+
function canonicalizeEmbeddedFontKeys(zip) {
|
|
4344
|
+
const table2 = zip.getEntry("word/fontTable.xml");
|
|
4345
|
+
if (!table2) return;
|
|
4346
|
+
const xml = table2.getData().toString("utf8");
|
|
4347
|
+
const embeds = [...xml.matchAll(FONT_EMBED)].flatMap(([element]) => {
|
|
4348
|
+
const id = /\br:id="([^"]+)"/.exec(element)?.[1];
|
|
4349
|
+
const key = /\bw:fontKey="\{([0-9A-Fa-f-]{36})\}"/.exec(element)?.[1];
|
|
4350
|
+
return id && key ? [{ id, key }] : [];
|
|
4351
|
+
});
|
|
4352
|
+
if (embeds.length === 0) return;
|
|
4353
|
+
const rels = zip.getEntry("word/_rels/fontTable.xml.rels")?.getData().toString("utf8") ?? "";
|
|
4354
|
+
const targets = /* @__PURE__ */ new Map();
|
|
4355
|
+
for (const [relationship] of rels.matchAll(/<Relationship\b[^>]*>/g)) {
|
|
4356
|
+
const id = /\bId="([^"]+)"/.exec(relationship)?.[1];
|
|
4357
|
+
const target = /\bTarget="([^"]+)"/.exec(relationship)?.[1];
|
|
4358
|
+
if (id && target) targets.set(id, target);
|
|
4359
|
+
}
|
|
4360
|
+
const rekey = /* @__PURE__ */ new Map();
|
|
4361
|
+
const parts = [];
|
|
4362
|
+
for (const { id, key } of embeds) {
|
|
4363
|
+
const target = targets.get(id);
|
|
4364
|
+
if (!target) continue;
|
|
4365
|
+
const name = target.startsWith("/") ? target.slice(1) : posix.normalize(posix.join("word", target));
|
|
4366
|
+
const part = zip.getEntry(name);
|
|
4367
|
+
if (!part) continue;
|
|
4368
|
+
const font = xorFontKey(part.getData(), key);
|
|
4369
|
+
const hex = createHash3("sha256").update(font).digest("hex").slice(0, 32).toUpperCase();
|
|
4370
|
+
const fresh = [
|
|
4371
|
+
hex.slice(0, 8),
|
|
4372
|
+
hex.slice(8, 12),
|
|
4373
|
+
hex.slice(12, 16),
|
|
4374
|
+
hex.slice(16, 20),
|
|
4375
|
+
hex.slice(20)
|
|
4376
|
+
].join("-");
|
|
4377
|
+
rekey.set(key.toUpperCase(), fresh);
|
|
4378
|
+
parts.push({ name, data: xorFontKey(font, fresh) });
|
|
4379
|
+
}
|
|
4380
|
+
if (rekey.size === 0) return;
|
|
4381
|
+
for (const { name, data } of parts) {
|
|
4382
|
+
const entry = zip.getEntry(name);
|
|
4383
|
+
if (entry) zip.updateFile(entry, data);
|
|
4384
|
+
}
|
|
4385
|
+
const fontTable = zip.getEntry("word/fontTable.xml");
|
|
4386
|
+
if (!fontTable) return;
|
|
4387
|
+
zip.updateFile(
|
|
4388
|
+
fontTable,
|
|
4389
|
+
Buffer.from(
|
|
4390
|
+
xml.replace(
|
|
4391
|
+
/\bw:fontKey="\{([0-9A-Fa-f-]{36})\}"/g,
|
|
4392
|
+
(whole, key) => {
|
|
4393
|
+
const fresh = rekey.get(key.toUpperCase());
|
|
4394
|
+
return fresh ? `w:fontKey="{${fresh}}"` : whole;
|
|
4395
|
+
}
|
|
4396
|
+
),
|
|
4397
|
+
"utf8"
|
|
4398
|
+
)
|
|
4399
|
+
);
|
|
4400
|
+
}
|
|
4335
4401
|
function canonicalizeEmbeddedPackages(zip, generatedAt, depth = 1) {
|
|
4336
4402
|
const names = zip.getEntries().map((entry) => entry.entryName).filter((name) => EMBEDDED_OFFICE_PACKAGE.test(name));
|
|
4337
4403
|
let changed = false;
|
|
@@ -4384,6 +4450,7 @@ function canonicalizeDocxBuffer(buffer, generatedAt = DEFAULT_GENERATION_DATE) {
|
|
|
4384
4450
|
normalizeTextCase(zip);
|
|
4385
4451
|
canonicalizeRelationshipIds(zip);
|
|
4386
4452
|
canonicalizeEmbeddedPackages(zip, generatedAt);
|
|
4453
|
+
canonicalizeEmbeddedFontKeys(zip);
|
|
4387
4454
|
const isoTimestamp = generatedAt.toISOString();
|
|
4388
4455
|
const coreProperties3 = zip.getEntry("docProps/core.xml");
|
|
4389
4456
|
if (coreProperties3) {
|
|
@@ -4400,13 +4467,14 @@ function normalizeDocxCaseBuffer(buffer) {
|
|
|
4400
4467
|
const zip = new AdmZip(buffer);
|
|
4401
4468
|
return normalizeTextCase(zip) ? zip.toBuffer() : buffer;
|
|
4402
4469
|
}
|
|
4403
|
-
var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID, CORE_TIMESTAMP, EMBEDDED_OFFICE_PACKAGE, MAX_EMBEDDED_DEPTH;
|
|
4470
|
+
var DEFAULT_GENERATION_DATE, STABLE_RELATIONSHIP_ID, FONT_EMBED, CORE_TIMESTAMP, EMBEDDED_OFFICE_PACKAGE, MAX_EMBEDDED_DEPTH;
|
|
4404
4471
|
var init_packageDocument = __esm({
|
|
4405
4472
|
"src/utils/packageDocument.ts"() {
|
|
4406
4473
|
"use strict";
|
|
4407
4474
|
init_normalizeTextCase();
|
|
4408
4475
|
DEFAULT_GENERATION_DATE = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
|
|
4409
4476
|
STABLE_RELATIONSHIP_ID = /^rId\d+$/;
|
|
4477
|
+
FONT_EMBED = /<w:embed(?:Regular|Bold|Italic|BoldItalic)\b[^>]*>/g;
|
|
4410
4478
|
CORE_TIMESTAMP = /(<dcterms:(?:created|modified)\b[^>]*>)[^<]*(<\/dcterms:(?:created|modified)>)/g;
|
|
4411
4479
|
EMBEDDED_OFFICE_PACKAGE = /\.(?:docx|pptx|xlsx|xlsm)$/i;
|
|
4412
4480
|
MAX_EMBEDDED_DEPTH = 3;
|
|
@@ -5007,7 +5075,12 @@ function createDocxJsRenderer() {
|
|
|
5007
5075
|
});
|
|
5008
5076
|
}
|
|
5009
5077
|
const pageNumbers = needsPageNumbers(ir) ? await createPageNumberEstimator(ir) : void 0;
|
|
5010
|
-
const document = buildDocument(
|
|
5078
|
+
const document = buildDocument(
|
|
5079
|
+
ir,
|
|
5080
|
+
resources,
|
|
5081
|
+
pageNumbers,
|
|
5082
|
+
renderOptions?.embeddedFonts
|
|
5083
|
+
);
|
|
5011
5084
|
const packed = await Packer.toBuffer(document);
|
|
5012
5085
|
const fixed = fixFloatingImageIdsInBuffer(packed);
|
|
5013
5086
|
const repaired = markers ? repairDrawingGroupsInBuffer(fixed, markers) : fixed;
|
|
@@ -5025,10 +5098,17 @@ function createDocxJsRenderer() {
|
|
|
5025
5098
|
}
|
|
5026
5099
|
};
|
|
5027
5100
|
}
|
|
5028
|
-
function buildDocument(ir, resources = /* @__PURE__ */ new Map(), pageNumbers) {
|
|
5101
|
+
function buildDocument(ir, resources = /* @__PURE__ */ new Map(), pageNumbers, embeddedFonts) {
|
|
5029
5102
|
return new Document({
|
|
5030
5103
|
...pageNumbers ? { pageNumbers } : {},
|
|
5031
5104
|
styles: emitStyles(ir.styles),
|
|
5105
|
+
// word/fonts/font<N>.odttf, their `w:embed*` entries in the font table and
|
|
5106
|
+
// `w:embedTrueTypeFonts` in the settings, all from this one key. Passed
|
|
5107
|
+
// only when a face embeds, so a document in any other mode is built from
|
|
5108
|
+
// exactly the options it always was. `docx/layout` measures the text of an
|
|
5109
|
+
// embedded family from these files, so a contents field's page numbers
|
|
5110
|
+
// need nothing more.
|
|
5111
|
+
...embeddedFonts && embeddedFonts.length > 0 ? { fonts: embeddedFonts.map(fontOptions) } : {},
|
|
5032
5112
|
...ir.theme ? { theme: themeOptions(ir.theme) } : {},
|
|
5033
5113
|
sections: ir.sections.map((section2) => sectionOptions(section2, resources)),
|
|
5034
5114
|
...coreProperties(ir),
|
|
@@ -5059,6 +5139,15 @@ function buildDocument(ir, resources = /* @__PURE__ */ new Map(), pageNumbers) {
|
|
|
5059
5139
|
} : {}
|
|
5060
5140
|
});
|
|
5061
5141
|
}
|
|
5142
|
+
function fontOptions(face) {
|
|
5143
|
+
return {
|
|
5144
|
+
name: face.family,
|
|
5145
|
+
data: face.regular,
|
|
5146
|
+
...face.bold ? { bold: face.bold } : {},
|
|
5147
|
+
...face.italic ? { italic: face.italic } : {},
|
|
5148
|
+
...face.boldItalic ? { boldItalic: face.boldItalic } : {}
|
|
5149
|
+
};
|
|
5150
|
+
}
|
|
5062
5151
|
function themeOptions(theme) {
|
|
5063
5152
|
const colors = {};
|
|
5064
5153
|
for (const [slot, color] of Object.entries(theme.colors)) {
|
|
@@ -7054,6 +7143,67 @@ import {
|
|
|
7054
7143
|
toRasterizeFontFaces
|
|
7055
7144
|
} from "@json-to-office/shared/fonts/node";
|
|
7056
7145
|
|
|
7146
|
+
// src/core/fontEmbedding.ts
|
|
7147
|
+
import { isSafeFont } from "@json-to-office/shared";
|
|
7148
|
+
import {
|
|
7149
|
+
toEmbeddedFontFaces
|
|
7150
|
+
} from "@json-to-office/shared/fonts/node";
|
|
7151
|
+
|
|
7152
|
+
// src/renderers/types.ts
|
|
7153
|
+
import {
|
|
7154
|
+
DEFAULT_DOCX_RENDERER_ID
|
|
7155
|
+
} from "@json-to-office/shared-docx";
|
|
7156
|
+
|
|
7157
|
+
// src/core/fontEmbedding.ts
|
|
7158
|
+
var EMBEDDING_RENDERER = "docxjs";
|
|
7159
|
+
var warning = (code, message) => ({
|
|
7160
|
+
component: "fontRegistry",
|
|
7161
|
+
message,
|
|
7162
|
+
severity: "warning",
|
|
7163
|
+
context: { code }
|
|
7164
|
+
});
|
|
7165
|
+
function embedsFonts(fonts, renderer, warnings) {
|
|
7166
|
+
if (fonts?.mode !== "embed") return false;
|
|
7167
|
+
const id = renderer ?? DEFAULT_DOCX_RENDERER_ID;
|
|
7168
|
+
if (id === EMBEDDING_RENDERER) return true;
|
|
7169
|
+
warnings.push(
|
|
7170
|
+
warning(
|
|
7171
|
+
"FONT_EMBED_UNSUPPORTED_RENDERER",
|
|
7172
|
+
`Export mode "embed" needs the ${EMBEDDING_RENDERER} renderer; the ${id} renderer does not embed fonts, so references are kept as in custom mode and nothing is embedded.`
|
|
7173
|
+
)
|
|
7174
|
+
);
|
|
7175
|
+
return false;
|
|
7176
|
+
}
|
|
7177
|
+
function selectEmbeddedFonts(resolved, warnings) {
|
|
7178
|
+
const { faces, report } = toEmbeddedFontFaces(resolved, warnings);
|
|
7179
|
+
const summary = embedSummary(resolved, report);
|
|
7180
|
+
if (summary) warnings.push(warning("FONT_MODE_EMBED", summary));
|
|
7181
|
+
return faces;
|
|
7182
|
+
}
|
|
7183
|
+
function embedSummary(resolved, report) {
|
|
7184
|
+
const byFamily = new Map(report.map((r) => [r.family.toLowerCase(), r]));
|
|
7185
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7186
|
+
const lines = [];
|
|
7187
|
+
for (const font of resolved) {
|
|
7188
|
+
const key = font.family.toLowerCase();
|
|
7189
|
+
if (isSafeFont(font.family.trim()) || seen.has(key)) continue;
|
|
7190
|
+
seen.add(key);
|
|
7191
|
+
const entry = byFamily.get(key);
|
|
7192
|
+
if (!entry) {
|
|
7193
|
+
lines.push(`${font.family}: not embedded (unresolved)`);
|
|
7194
|
+
} else if (entry.slots.length === 0) {
|
|
7195
|
+
lines.push(
|
|
7196
|
+
`${font.family}: not embedded (${entry.reason === "no-regular-face" ? "no regular face" : "no TTF/OTF face it may embed"})`
|
|
7197
|
+
);
|
|
7198
|
+
} else {
|
|
7199
|
+
const nearest = entry.nearest.length ? ` (${entry.nearest.map((n) => `${n.slot} \u2190 ${n.weight}`).join(", ")})` : "";
|
|
7200
|
+
lines.push(`${font.family}: ${entry.slots.join(", ")}${nearest}`);
|
|
7201
|
+
}
|
|
7202
|
+
}
|
|
7203
|
+
if (lines.length === 0) return void 0;
|
|
7204
|
+
return `Export mode "embed": ${lines.join("; ")}. Readers without these fonts installed see the embedded faces; any face or family not embedded stays a reference, as in custom mode.`;
|
|
7205
|
+
}
|
|
7206
|
+
|
|
7057
7207
|
// src/core/prerasterizeVisuals.ts
|
|
7058
7208
|
import {
|
|
7059
7209
|
clampVisualDpi,
|
|
@@ -7540,7 +7690,7 @@ function assertExportServerAllowed(serverUrl, servicesConfig, warnings) {
|
|
|
7540
7690
|
const notice = remoteExportNotice(serverUrl, servicesConfig?.allowRemote);
|
|
7541
7691
|
if (!notice || !warnings) return;
|
|
7542
7692
|
const alreadySaid = warnings.some(
|
|
7543
|
-
(
|
|
7693
|
+
(warning2) => warning2.context?.code === REMOTE_EXPORT_WARNING && warning2.context?.serverUrl === serverUrl
|
|
7544
7694
|
);
|
|
7545
7695
|
if (alreadySaid) return;
|
|
7546
7696
|
warnings.push({
|
|
@@ -13586,13 +13736,6 @@ function warnOnce(ctx, component, message, code) {
|
|
|
13586
13736
|
import {
|
|
13587
13737
|
RendererRegistry
|
|
13588
13738
|
} from "@json-to-office/shared/rendering";
|
|
13589
|
-
|
|
13590
|
-
// src/renderers/types.ts
|
|
13591
|
-
import {
|
|
13592
|
-
DEFAULT_DOCX_RENDERER_ID
|
|
13593
|
-
} from "@json-to-office/shared-docx";
|
|
13594
|
-
|
|
13595
|
-
// src/renderers/registry.ts
|
|
13596
13739
|
var registry = new RendererRegistry(
|
|
13597
13740
|
"docx",
|
|
13598
13741
|
DEFAULT_DOCX_RENDERER_ID
|
|
@@ -16240,13 +16383,15 @@ async function compileDocumentScoped(document, options, warnings) {
|
|
|
16240
16383
|
const { context } = prepared.model;
|
|
16241
16384
|
const hasVisual = collectVisualProps(context.document).length > 0;
|
|
16242
16385
|
const hasHighcharts = containsComponent(context.document, "highcharts");
|
|
16386
|
+
const embedding = embedsFonts(options.fonts, options.renderer, warnings);
|
|
16243
16387
|
const resolvedFonts = await resolveDocumentFonts(
|
|
16244
16388
|
context.document,
|
|
16245
16389
|
context.theme,
|
|
16246
16390
|
options.fonts,
|
|
16247
16391
|
warnings,
|
|
16248
|
-
hasVisual || hasHighcharts
|
|
16392
|
+
hasVisual || hasHighcharts || embedding
|
|
16249
16393
|
);
|
|
16394
|
+
const embeddedFonts = embedding ? selectEmbeddedFonts(resolvedFonts, warnings) : [];
|
|
16250
16395
|
const visualFonts = hasVisual ? toRasterizeFontFaces(resolvedFonts, warnings) : [];
|
|
16251
16396
|
const chartFonts = hasHighcharts ? toChartFontFaces(resolvedFonts) : [];
|
|
16252
16397
|
const desugared = await desugarExternals(context.document, {
|
|
@@ -16286,7 +16431,8 @@ async function compileDocumentScoped(document, options, warnings) {
|
|
|
16286
16431
|
theme: context.theme,
|
|
16287
16432
|
warnings: compiled.warnings,
|
|
16288
16433
|
required: compiled.required,
|
|
16289
|
-
unsupported: compiled.unsupported
|
|
16434
|
+
unsupported: compiled.unsupported,
|
|
16435
|
+
embeddedFonts
|
|
16290
16436
|
};
|
|
16291
16437
|
}
|
|
16292
16438
|
async function generateBufferViaIr(document, options = {}) {
|
|
@@ -16310,6 +16456,7 @@ async function renderScoped(document, options, warnings) {
|
|
|
16310
16456
|
...options.deterministic !== void 0 ? { deterministic: options.deterministic } : {},
|
|
16311
16457
|
...options.generatedAt !== void 0 ? { generatedAt: toDate(options.generatedAt) } : {},
|
|
16312
16458
|
...options.svgRasterFallback !== void 0 ? { svgRasterFallback: options.svgRasterFallback } : {},
|
|
16459
|
+
...compiled.embeddedFonts.length > 0 ? { embeddedFonts: compiled.embeddedFonts } : {},
|
|
16313
16460
|
warnings: compiled.warnings
|
|
16314
16461
|
});
|
|
16315
16462
|
return { buffer: Buffer.from(bytes), warnings: compiled.warnings };
|
|
@@ -17815,10 +17962,10 @@ function generateWarningsDocument(warnings) {
|
|
|
17815
17962
|
}
|
|
17816
17963
|
];
|
|
17817
17964
|
const warningsByComponent = /* @__PURE__ */ new Map();
|
|
17818
|
-
for (const
|
|
17819
|
-
const existing = warningsByComponent.get(
|
|
17820
|
-
existing.push(
|
|
17821
|
-
warningsByComponent.set(
|
|
17965
|
+
for (const warning2 of warnings) {
|
|
17966
|
+
const existing = warningsByComponent.get(warning2.component) || [];
|
|
17967
|
+
existing.push(warning2);
|
|
17968
|
+
warningsByComponent.set(warning2.component, existing);
|
|
17822
17969
|
}
|
|
17823
17970
|
for (const [
|
|
17824
17971
|
componentName,
|
|
@@ -17832,10 +17979,10 @@ function generateWarningsDocument(warnings) {
|
|
|
17832
17979
|
}
|
|
17833
17980
|
});
|
|
17834
17981
|
const warningItems = [];
|
|
17835
|
-
for (const
|
|
17836
|
-
let warningText =
|
|
17837
|
-
if (
|
|
17838
|
-
const contextStr = Object.entries(
|
|
17982
|
+
for (const warning2 of componentWarnings) {
|
|
17983
|
+
let warningText = warning2.message;
|
|
17984
|
+
if (warning2.context && Object.keys(warning2.context).length > 0) {
|
|
17985
|
+
const contextStr = Object.entries(warning2.context).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(", ");
|
|
17839
17986
|
warningText += ` (${contextStr})`;
|
|
17840
17987
|
}
|
|
17841
17988
|
warningItems.push(warningText);
|
|
@@ -17871,10 +18018,10 @@ function formatWarningsText(warnings) {
|
|
|
17871
18018
|
""
|
|
17872
18019
|
];
|
|
17873
18020
|
const warningsByComponent = /* @__PURE__ */ new Map();
|
|
17874
|
-
for (const
|
|
17875
|
-
const existing = warningsByComponent.get(
|
|
17876
|
-
existing.push(
|
|
17877
|
-
warningsByComponent.set(
|
|
18021
|
+
for (const warning2 of warnings) {
|
|
18022
|
+
const existing = warningsByComponent.get(warning2.component) || [];
|
|
18023
|
+
existing.push(warning2);
|
|
18024
|
+
warningsByComponent.set(warning2.component, existing);
|
|
17878
18025
|
}
|
|
17879
18026
|
for (const [
|
|
17880
18027
|
componentName,
|
|
@@ -17883,10 +18030,10 @@ function formatWarningsText(warnings) {
|
|
|
17883
18030
|
lines.push(`Component: ${componentName}`);
|
|
17884
18031
|
lines.push("-".repeat(30));
|
|
17885
18032
|
for (let i = 0; i < componentWarnings.length; i++) {
|
|
17886
|
-
const
|
|
17887
|
-
lines.push(` ${i + 1}. ${
|
|
17888
|
-
if (
|
|
17889
|
-
lines.push(` Context: ${JSON.stringify(
|
|
18033
|
+
const warning2 = componentWarnings[i];
|
|
18034
|
+
lines.push(` ${i + 1}. ${warning2.message}`);
|
|
18035
|
+
if (warning2.context && Object.keys(warning2.context).length > 0) {
|
|
18036
|
+
lines.push(` Context: ${JSON.stringify(warning2.context, null, 2)}`);
|
|
17890
18037
|
}
|
|
17891
18038
|
}
|
|
17892
18039
|
lines.push("");
|