js.documents 1.62.0 → 1.63.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 +39 -6
- package/dist/convert/convert.cjs +69 -17
- package/dist/convert/convert.d.cts +2 -1
- package/dist/convert/convert.d.ts +2 -1
- package/dist/convert/convert.js +70 -18
- package/dist/convert/local.cjs +15 -2
- package/dist/convert/local.js +15 -2
- package/dist/convert/port.d.cts +8 -4
- package/dist/convert/port.d.ts +8 -4
- package/dist/fonts/obfuscation.cjs +50 -0
- package/dist/fonts/obfuscation.d.cts +9 -0
- package/dist/fonts/obfuscation.d.ts +9 -0
- package/dist/fonts/obfuscation.js +46 -0
- package/dist/fonts/odf.cjs +86 -0
- package/dist/fonts/odf.d.cts +9 -0
- package/dist/fonts/odf.d.ts +9 -0
- package/dist/fonts/odf.js +84 -0
- package/dist/fonts/ooxml.cjs +129 -0
- package/dist/fonts/ooxml.d.cts +9 -0
- package/dist/fonts/ooxml.d.ts +9 -0
- package/dist/fonts/ooxml.js +127 -0
- package/dist/fonts/registry.cjs +18 -0
- package/dist/fonts/registry.d.cts +2 -0
- package/dist/fonts/registry.d.ts +2 -0
- package/dist/fonts/registry.js +16 -0
- package/dist/index.cjs +32 -0
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +6 -2
- package/dist/registry-CrSqLIcn.d.ts +19 -0
- package/dist/registry-CtPhMD0f.d.cts +19 -0
- package/package.json +1 -1
package/dist/convert/convert.js
CHANGED
|
@@ -7,6 +7,7 @@ import { buildOdtPackage } from "../edit/odt/content.js";
|
|
|
7
7
|
import { buildOdpPackage } from "../edit/odp/content.js";
|
|
8
8
|
import { buildOdsPackage } from "../edit/ods/content.js";
|
|
9
9
|
import { buildOdgPackage } from "../edit/odg/content.js";
|
|
10
|
+
import { createDocumentFontRegistry, extractSourceFonts } from "../fonts/registry.js";
|
|
10
11
|
import { layoutFormula } from "../mathml/layout.js";
|
|
11
12
|
import { readDocxContent } from "../ooxml/docx/read.js";
|
|
12
13
|
import { readPptxContent } from "../ooxml/pptx/read.js";
|
|
@@ -30,13 +31,17 @@ import { odbTablesToSpreadsheetDocument } from "../odb/spreadsheet.js";
|
|
|
30
31
|
import { buildXlsxPackage, decodePackage, encodePackage, readXlsxContent } from "ooxml.js";
|
|
31
32
|
import { COLOR_BLACK, CONTENT_FORMAT_VERSION, DOCUMENT_PACKAGE_FORMAT_VERSION, LAYOUT_FORMAT_VERSION } from "document-schema.js";
|
|
32
33
|
import { decodePackage as decodePackage$1, encodePackage as encodePackage$1, readOdfMetadata, readOdfParagraph, readOdfTable, readOdm } from "odf.js";
|
|
33
|
-
import {
|
|
34
|
+
import { createFontMeasurer, createFontRegistry, loadMathFont, readPdf, writePdf } from "pdf-codec";
|
|
34
35
|
//#region src/convert/convert.ts
|
|
35
36
|
function docxToPdf(bytes, options) {
|
|
36
37
|
const pkg = openDocx(bytes).toPackage();
|
|
37
38
|
const content = readDocxContent(pkg);
|
|
38
39
|
if (content.kind !== "wordprocessing") throw new Error("readDocxContent returned a non-wordprocessing ContentDocument");
|
|
39
|
-
const
|
|
40
|
+
const fonts = createDocumentFontRegistry({
|
|
41
|
+
kind: "docx",
|
|
42
|
+
package: pkg
|
|
43
|
+
}, options);
|
|
44
|
+
const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
40
45
|
options?.onDocument?.({
|
|
41
46
|
formatVersion: DOCUMENT_PACKAGE_FORMAT_VERSION,
|
|
42
47
|
content,
|
|
@@ -45,14 +50,19 @@ function docxToPdf(bytes, options) {
|
|
|
45
50
|
return writePdf(layout, {
|
|
46
51
|
signal: options?.signal,
|
|
47
52
|
onSubstitution: options?.onSubstitution,
|
|
48
|
-
formulas
|
|
53
|
+
formulas,
|
|
54
|
+
fonts
|
|
49
55
|
});
|
|
50
56
|
}
|
|
51
57
|
function odtToPdf(bytes, options) {
|
|
52
58
|
const pkg = decodePackage$1(bytes);
|
|
53
59
|
const content = readOdtContent(pkg);
|
|
54
60
|
if (content.kind !== "wordprocessing") throw new Error("readOdtContent returned a non-wordprocessing ContentDocument");
|
|
55
|
-
const
|
|
61
|
+
const fonts = createDocumentFontRegistry({
|
|
62
|
+
kind: "odf",
|
|
63
|
+
package: pkg
|
|
64
|
+
}, options);
|
|
65
|
+
const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
56
66
|
options?.onDocument?.({
|
|
57
67
|
formatVersion: DOCUMENT_PACKAGE_FORMAT_VERSION,
|
|
58
68
|
content,
|
|
@@ -61,14 +71,19 @@ function odtToPdf(bytes, options) {
|
|
|
61
71
|
return writePdf(layout, {
|
|
62
72
|
signal: options?.signal,
|
|
63
73
|
onSubstitution: options?.onSubstitution,
|
|
64
|
-
formulas
|
|
74
|
+
formulas,
|
|
75
|
+
fonts
|
|
65
76
|
});
|
|
66
77
|
}
|
|
67
78
|
function pptxToPdf(bytes, options) {
|
|
68
79
|
const pkg = openPptx(bytes).toPackage();
|
|
69
80
|
const content = readPptxContent(pkg);
|
|
70
81
|
if (content.kind !== "presentation") throw new Error("readPptxContent returned a non-presentation ContentDocument");
|
|
71
|
-
const
|
|
82
|
+
const fonts = createDocumentFontRegistry({
|
|
83
|
+
kind: "pptx",
|
|
84
|
+
package: pkg
|
|
85
|
+
}, options);
|
|
86
|
+
const { document: layout, formulas } = convertPresentationToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
72
87
|
options?.onDocument?.({
|
|
73
88
|
formatVersion: DOCUMENT_PACKAGE_FORMAT_VERSION,
|
|
74
89
|
content,
|
|
@@ -77,14 +92,19 @@ function pptxToPdf(bytes, options) {
|
|
|
77
92
|
return writePdf(layout, {
|
|
78
93
|
signal: options?.signal,
|
|
79
94
|
onSubstitution: options?.onSubstitution,
|
|
80
|
-
formulas
|
|
95
|
+
formulas,
|
|
96
|
+
fonts
|
|
81
97
|
});
|
|
82
98
|
}
|
|
83
99
|
function odpToPdf(bytes, options) {
|
|
84
100
|
const pkg = decodePackage$1(bytes);
|
|
85
101
|
const content = readOdpContent(pkg);
|
|
86
102
|
if (content.kind !== "presentation") throw new Error("readOdpContent returned a non-presentation ContentDocument");
|
|
87
|
-
const
|
|
103
|
+
const fonts = createDocumentFontRegistry({
|
|
104
|
+
kind: "odf",
|
|
105
|
+
package: pkg
|
|
106
|
+
}, options);
|
|
107
|
+
const { document: layout, formulas } = convertPresentationToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
88
108
|
options?.onDocument?.({
|
|
89
109
|
formatVersion: DOCUMENT_PACKAGE_FORMAT_VERSION,
|
|
90
110
|
content,
|
|
@@ -93,15 +113,20 @@ function odpToPdf(bytes, options) {
|
|
|
93
113
|
return writePdf(layout, {
|
|
94
114
|
signal: options?.signal,
|
|
95
115
|
onSubstitution: options?.onSubstitution,
|
|
96
|
-
formulas
|
|
116
|
+
formulas,
|
|
117
|
+
fonts
|
|
97
118
|
});
|
|
98
119
|
}
|
|
99
120
|
function odsToPdf(bytes, options) {
|
|
100
121
|
const pkg = decodePackage$1(bytes);
|
|
101
122
|
const content = readOdsContent(pkg);
|
|
102
123
|
if (content.kind !== "spreadsheet") throw new Error("readOdsContent returned a non-spreadsheet ContentDocument");
|
|
124
|
+
const fonts = createDocumentFontRegistry({
|
|
125
|
+
kind: "odf",
|
|
126
|
+
package: pkg
|
|
127
|
+
}, options);
|
|
103
128
|
const layout = convertSpreadsheetToLayout(content, {
|
|
104
|
-
measurer:
|
|
129
|
+
measurer: createFontMeasurer(fonts),
|
|
105
130
|
signal: options?.signal
|
|
106
131
|
});
|
|
107
132
|
options?.onDocument?.({
|
|
@@ -111,14 +136,19 @@ function odsToPdf(bytes, options) {
|
|
|
111
136
|
});
|
|
112
137
|
return writePdf(layout, {
|
|
113
138
|
signal: options?.signal,
|
|
114
|
-
onSubstitution: options?.onSubstitution
|
|
139
|
+
onSubstitution: options?.onSubstitution,
|
|
140
|
+
fonts
|
|
115
141
|
});
|
|
116
142
|
}
|
|
117
143
|
function odgToPdf(bytes, options) {
|
|
118
144
|
const pkg = decodePackage$1(bytes);
|
|
119
145
|
const content = readOdgContent(pkg);
|
|
120
146
|
if (content.kind !== "drawing") throw new Error("readOdgContent returned a non-drawing ContentDocument");
|
|
121
|
-
const
|
|
147
|
+
const fonts = createDocumentFontRegistry({
|
|
148
|
+
kind: "odf",
|
|
149
|
+
package: pkg
|
|
150
|
+
}, options);
|
|
151
|
+
const layout = convertDrawingToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
122
152
|
options?.onDocument?.({
|
|
123
153
|
formatVersion: DOCUMENT_PACKAGE_FORMAT_VERSION,
|
|
124
154
|
content,
|
|
@@ -126,7 +156,8 @@ function odgToPdf(bytes, options) {
|
|
|
126
156
|
});
|
|
127
157
|
return writePdf(layout, {
|
|
128
158
|
signal: options?.signal,
|
|
129
|
-
onSubstitution: options?.onSubstitution
|
|
159
|
+
onSubstitution: options?.onSubstitution,
|
|
160
|
+
fonts
|
|
130
161
|
});
|
|
131
162
|
}
|
|
132
163
|
function markdownToPdf(bytes, options) {
|
|
@@ -134,7 +165,11 @@ function markdownToPdf(bytes, options) {
|
|
|
134
165
|
const text = decodeMarkdownText(bytes);
|
|
135
166
|
const content = readMarkdownContent(text, { signal: options?.signal });
|
|
136
167
|
if (content.kind !== "wordprocessing") throw new Error("readMarkdownContent returned a non-wordprocessing ContentDocument");
|
|
137
|
-
const
|
|
168
|
+
const fonts = createFontRegistry({
|
|
169
|
+
fonts: options?.fonts,
|
|
170
|
+
onSubstitution: options?.onFontSubstitution
|
|
171
|
+
});
|
|
172
|
+
const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
138
173
|
options?.onDocument?.({
|
|
139
174
|
formatVersion: DOCUMENT_PACKAGE_FORMAT_VERSION,
|
|
140
175
|
content,
|
|
@@ -143,7 +178,8 @@ function markdownToPdf(bytes, options) {
|
|
|
143
178
|
return writePdf(layout, {
|
|
144
179
|
signal: options?.signal,
|
|
145
180
|
onSubstitution: options?.onSubstitution,
|
|
146
|
-
formulas
|
|
181
|
+
formulas,
|
|
182
|
+
fonts
|
|
147
183
|
});
|
|
148
184
|
}
|
|
149
185
|
const STANDALONE_FORMULA_SIZE_PT = 18;
|
|
@@ -412,7 +448,9 @@ function xlsxToPdf(bytes, options) {
|
|
|
412
448
|
return odsToPdf(odsBytes, {
|
|
413
449
|
signal: options?.signal,
|
|
414
450
|
onSubstitution: options?.onSubstitution,
|
|
415
|
-
onDocument: options?.onDocument
|
|
451
|
+
onDocument: options?.onDocument,
|
|
452
|
+
fonts: options?.fonts,
|
|
453
|
+
onFontSubstitution: options?.onFontSubstitution
|
|
416
454
|
});
|
|
417
455
|
}
|
|
418
456
|
function pdfToXlsx(bytes, options) {
|
|
@@ -468,6 +506,10 @@ function odmToPdf(bytes, options) {
|
|
|
468
506
|
const odm = readOdm(pkg);
|
|
469
507
|
const unresolvedHrefs = [];
|
|
470
508
|
const chapterSections = [];
|
|
509
|
+
const sourceFonts = extractSourceFonts({
|
|
510
|
+
kind: "odf",
|
|
511
|
+
package: pkg
|
|
512
|
+
});
|
|
471
513
|
for (const section of odm.sections) {
|
|
472
514
|
throwIfAborted(options?.signal);
|
|
473
515
|
if (section.inlineContent !== void 0) {
|
|
@@ -480,6 +522,10 @@ function odmToPdf(bytes, options) {
|
|
|
480
522
|
continue;
|
|
481
523
|
}
|
|
482
524
|
const chapterPkg = decodePackage$1(chapterBytes);
|
|
525
|
+
sourceFonts.push(...extractSourceFonts({
|
|
526
|
+
kind: "odf",
|
|
527
|
+
package: chapterPkg
|
|
528
|
+
}));
|
|
483
529
|
const chapterContent = readOdtContent(chapterPkg);
|
|
484
530
|
if (chapterContent.kind !== "wordprocessing") throw new Error("readOdtContent returned a non-wordprocessing ContentDocument");
|
|
485
531
|
chapterSections.push(chapterContent.sections);
|
|
@@ -500,11 +546,17 @@ function odmToPdf(bytes, options) {
|
|
|
500
546
|
metadata: readOdfMetadata(pkg),
|
|
501
547
|
sections: combinedSections
|
|
502
548
|
};
|
|
503
|
-
const
|
|
549
|
+
const fonts = createFontRegistry({
|
|
550
|
+
sourceFonts,
|
|
551
|
+
fonts: options?.fonts,
|
|
552
|
+
onSubstitution: options?.onFontSubstitution
|
|
553
|
+
});
|
|
554
|
+
const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
504
555
|
return writePdf(layout, {
|
|
505
556
|
signal: options?.signal,
|
|
506
557
|
onSubstitution: options?.onSubstitution,
|
|
507
|
-
formulas
|
|
558
|
+
formulas,
|
|
559
|
+
fonts
|
|
508
560
|
});
|
|
509
561
|
}
|
|
510
562
|
function odbToXlsx(bytes, options) {
|
package/dist/convert/local.cjs
CHANGED
|
@@ -13,6 +13,13 @@ function substitutionDiagnostic(substitution, context) {
|
|
|
13
13
|
pageIndex: context.pageIndex
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
+
function fontSubstitutionDiagnostic(substitution) {
|
|
17
|
+
return {
|
|
18
|
+
severity: "info",
|
|
19
|
+
code: "font/substituted",
|
|
20
|
+
message: `"${`${substitution.requestedFamily}${substitution.requestedBold ? " bold" : ""}${substitution.requestedItalic ? " italic" : ""}`}" is not available; ${substitution.reason === "vendored-substitute" ? `substituted the metric-compatible "${substitution.resolvedFamily}"` : `substituted another face of "${substitution.resolvedFamily}"`}`
|
|
21
|
+
};
|
|
22
|
+
}
|
|
16
23
|
function fromPdfDiagnostic(diagnostic) {
|
|
17
24
|
return {
|
|
18
25
|
severity: diagnostic.severity,
|
|
@@ -23,7 +30,7 @@ function fromPdfDiagnostic(diagnostic) {
|
|
|
23
30
|
}
|
|
24
31
|
function createLocalDocumentConverter() {
|
|
25
32
|
return {
|
|
26
|
-
contractVersion:
|
|
33
|
+
contractVersion: 3,
|
|
27
34
|
conversions: SUPPORTED_CONVERSIONS,
|
|
28
35
|
convert(request, options) {
|
|
29
36
|
const { source, targetFormat } = request;
|
|
@@ -32,6 +39,10 @@ function createLocalDocumentConverter() {
|
|
|
32
39
|
const onDocument = (pkg) => {
|
|
33
40
|
documentPackage = pkg;
|
|
34
41
|
};
|
|
42
|
+
const onFontSubstitution = (substitution) => {
|
|
43
|
+
diagnostics.push(fontSubstitutionDiagnostic(substitution));
|
|
44
|
+
options.onFontSubstitution?.(substitution);
|
|
45
|
+
};
|
|
35
46
|
const strategy = require_convert_capability.resolveConversionPath(source.format, targetFormat);
|
|
36
47
|
if (strategy?.kind !== "direct") return Promise.reject(/* @__PURE__ */ new Error(`unsupported conversion: ${source.format} -> ${targetFormat}`));
|
|
37
48
|
const edge = strategy.edge;
|
|
@@ -39,7 +50,9 @@ function createLocalDocumentConverter() {
|
|
|
39
50
|
const bytes = edge.convert(source.bytes, {
|
|
40
51
|
signal: options.signal,
|
|
41
52
|
onSubstitution: (s, c) => diagnostics.push(substitutionDiagnostic(s, c)),
|
|
42
|
-
onDocument
|
|
53
|
+
onDocument,
|
|
54
|
+
fonts: options.fonts,
|
|
55
|
+
onFontSubstitution
|
|
43
56
|
});
|
|
44
57
|
return Promise.resolve({
|
|
45
58
|
document: {
|
package/dist/convert/local.js
CHANGED
|
@@ -12,6 +12,13 @@ function substitutionDiagnostic(substitution, context) {
|
|
|
12
12
|
pageIndex: context.pageIndex
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
+
function fontSubstitutionDiagnostic(substitution) {
|
|
16
|
+
return {
|
|
17
|
+
severity: "info",
|
|
18
|
+
code: "font/substituted",
|
|
19
|
+
message: `"${`${substitution.requestedFamily}${substitution.requestedBold ? " bold" : ""}${substitution.requestedItalic ? " italic" : ""}`}" is not available; ${substitution.reason === "vendored-substitute" ? `substituted the metric-compatible "${substitution.resolvedFamily}"` : `substituted another face of "${substitution.resolvedFamily}"`}`
|
|
20
|
+
};
|
|
21
|
+
}
|
|
15
22
|
function fromPdfDiagnostic(diagnostic) {
|
|
16
23
|
return {
|
|
17
24
|
severity: diagnostic.severity,
|
|
@@ -22,7 +29,7 @@ function fromPdfDiagnostic(diagnostic) {
|
|
|
22
29
|
}
|
|
23
30
|
function createLocalDocumentConverter() {
|
|
24
31
|
return {
|
|
25
|
-
contractVersion:
|
|
32
|
+
contractVersion: 3,
|
|
26
33
|
conversions: SUPPORTED_CONVERSIONS,
|
|
27
34
|
convert(request, options) {
|
|
28
35
|
const { source, targetFormat } = request;
|
|
@@ -31,6 +38,10 @@ function createLocalDocumentConverter() {
|
|
|
31
38
|
const onDocument = (pkg) => {
|
|
32
39
|
documentPackage = pkg;
|
|
33
40
|
};
|
|
41
|
+
const onFontSubstitution = (substitution) => {
|
|
42
|
+
diagnostics.push(fontSubstitutionDiagnostic(substitution));
|
|
43
|
+
options.onFontSubstitution?.(substitution);
|
|
44
|
+
};
|
|
34
45
|
const strategy = resolveConversionPath(source.format, targetFormat);
|
|
35
46
|
if (strategy?.kind !== "direct") return Promise.reject(/* @__PURE__ */ new Error(`unsupported conversion: ${source.format} -> ${targetFormat}`));
|
|
36
47
|
const edge = strategy.edge;
|
|
@@ -38,7 +49,9 @@ function createLocalDocumentConverter() {
|
|
|
38
49
|
const bytes = edge.convert(source.bytes, {
|
|
39
50
|
signal: options.signal,
|
|
40
51
|
onSubstitution: (s, c) => diagnostics.push(substitutionDiagnostic(s, c)),
|
|
41
|
-
onDocument
|
|
52
|
+
onDocument,
|
|
53
|
+
fonts: options.fonts,
|
|
54
|
+
onFontSubstitution
|
|
42
55
|
});
|
|
43
56
|
return Promise.resolve({
|
|
44
57
|
document: {
|
package/dist/convert/port.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DocumentPackage } from "document-schema.js";
|
|
2
|
+
import { FontSubstitution, ProvidedFont } from "pdf-codec";
|
|
2
3
|
//#region src/convert/port.d.ts
|
|
3
4
|
type DocumentFormat = 'docx' | 'pptx' | 'xlsx' | 'odt' | 'odp' | 'ods' | 'odg' | 'odf' | 'markdown' | 'pdf';
|
|
4
5
|
interface DocumentPayload {
|
|
@@ -20,15 +21,18 @@ interface ConversionResult {
|
|
|
20
21
|
readonly diagnostics: readonly Diagnostic[];
|
|
21
22
|
readonly package?: DocumentPackage;
|
|
22
23
|
}
|
|
24
|
+
interface ConversionOptions {
|
|
25
|
+
readonly signal: AbortSignal;
|
|
26
|
+
readonly fonts?: readonly ProvidedFont[];
|
|
27
|
+
readonly onFontSubstitution?: (substitution: FontSubstitution) => void;
|
|
28
|
+
}
|
|
23
29
|
interface DocumentConverter {
|
|
24
30
|
readonly contractVersion: number;
|
|
25
31
|
readonly conversions: readonly {
|
|
26
32
|
readonly source: DocumentFormat;
|
|
27
33
|
readonly target: DocumentFormat;
|
|
28
34
|
}[];
|
|
29
|
-
convert(request: ConversionRequest, options:
|
|
30
|
-
readonly signal: AbortSignal;
|
|
31
|
-
}): Promise<ConversionResult>;
|
|
35
|
+
convert(request: ConversionRequest, options: ConversionOptions): Promise<ConversionResult>;
|
|
32
36
|
}
|
|
33
37
|
//#endregion
|
|
34
|
-
export { ConversionRequest, ConversionResult, Diagnostic, DocumentConverter, DocumentFormat, DocumentPayload };
|
|
38
|
+
export { ConversionOptions, ConversionRequest, ConversionResult, Diagnostic, DocumentConverter, DocumentFormat, DocumentPayload };
|
package/dist/convert/port.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DocumentPackage } from "document-schema.js";
|
|
2
|
+
import { FontSubstitution, ProvidedFont } from "pdf-codec";
|
|
2
3
|
//#region src/convert/port.d.ts
|
|
3
4
|
type DocumentFormat = 'docx' | 'pptx' | 'xlsx' | 'odt' | 'odp' | 'ods' | 'odg' | 'odf' | 'markdown' | 'pdf';
|
|
4
5
|
interface DocumentPayload {
|
|
@@ -20,15 +21,18 @@ interface ConversionResult {
|
|
|
20
21
|
readonly diagnostics: readonly Diagnostic[];
|
|
21
22
|
readonly package?: DocumentPackage;
|
|
22
23
|
}
|
|
24
|
+
interface ConversionOptions {
|
|
25
|
+
readonly signal: AbortSignal;
|
|
26
|
+
readonly fonts?: readonly ProvidedFont[];
|
|
27
|
+
readonly onFontSubstitution?: (substitution: FontSubstitution) => void;
|
|
28
|
+
}
|
|
23
29
|
interface DocumentConverter {
|
|
24
30
|
readonly contractVersion: number;
|
|
25
31
|
readonly conversions: readonly {
|
|
26
32
|
readonly source: DocumentFormat;
|
|
27
33
|
readonly target: DocumentFormat;
|
|
28
34
|
}[];
|
|
29
|
-
convert(request: ConversionRequest, options:
|
|
30
|
-
readonly signal: AbortSignal;
|
|
31
|
-
}): Promise<ConversionResult>;
|
|
35
|
+
convert(request: ConversionRequest, options: ConversionOptions): Promise<ConversionResult>;
|
|
32
36
|
}
|
|
33
37
|
//#endregion
|
|
34
|
-
export { ConversionRequest, ConversionResult, Diagnostic, DocumentConverter, DocumentFormat, DocumentPayload };
|
|
38
|
+
export { ConversionOptions, ConversionRequest, ConversionResult, Diagnostic, DocumentConverter, DocumentFormat, DocumentPayload };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let pdf_codec_sfnt = require("pdf-codec/sfnt");
|
|
3
|
+
//#region src/fonts/obfuscation.ts
|
|
4
|
+
const FONT_KEY_HEX_LENGTH = 32;
|
|
5
|
+
const FONT_KEY_BYTE_LENGTH = 16;
|
|
6
|
+
const OBFUSCATED_PREFIX_LENGTH = 32;
|
|
7
|
+
const SFNT_HEADER_LENGTH = 12;
|
|
8
|
+
const HEX_PAIR_PATTERN = /^[0-9A-Fa-f]{2}$/;
|
|
9
|
+
var FontDeobfuscationError = class extends Error {
|
|
10
|
+
constructor(detail) {
|
|
11
|
+
super(`deobfuscateEmbeddedFont: ${detail}`);
|
|
12
|
+
this.name = "FontDeobfuscationError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function deriveFontKey(fontKey) {
|
|
16
|
+
const hex = fontKey.replace(/[{}-]/g, "");
|
|
17
|
+
if (hex.length !== FONT_KEY_HEX_LENGTH) throw new FontDeobfuscationError(`w:fontKey ${JSON.stringify(fontKey)} has ${String(hex.length)} hex digit(s) after stripping braces and hyphens, expected ${String(FONT_KEY_HEX_LENGTH)}`);
|
|
18
|
+
const key = new Uint8Array(FONT_KEY_BYTE_LENGTH);
|
|
19
|
+
for (let i = 0; i < FONT_KEY_BYTE_LENGTH; i++) {
|
|
20
|
+
const start = 30 - i * 2;
|
|
21
|
+
const pair = hex.slice(start, start + 2);
|
|
22
|
+
if (!HEX_PAIR_PATTERN.test(pair)) throw new FontDeobfuscationError(`w:fontKey ${JSON.stringify(fontKey)} contains the non-hexadecimal pair ${JSON.stringify(pair)}`);
|
|
23
|
+
key[i] = Number.parseInt(pair, 16);
|
|
24
|
+
}
|
|
25
|
+
return key;
|
|
26
|
+
}
|
|
27
|
+
function looksLikeSfnt(bytes) {
|
|
28
|
+
return bytes.length >= SFNT_HEADER_LENGTH && (0, pdf_codec_sfnt.parseSfnt)(bytes) !== void 0;
|
|
29
|
+
}
|
|
30
|
+
function deobfuscateEmbeddedFont(bytes, fontKey) {
|
|
31
|
+
if (looksLikeSfnt(bytes)) return bytes;
|
|
32
|
+
if (fontKey === void 0) throw new FontDeobfuscationError("the part does not begin with an sfnt signature and carries no font key to deobfuscate it with");
|
|
33
|
+
if (bytes.length < OBFUSCATED_PREFIX_LENGTH) throw new FontDeobfuscationError(`an obfuscated part must be at least ${String(OBFUSCATED_PREFIX_LENGTH)} bytes long, got ${String(bytes.length)}`);
|
|
34
|
+
const key = deriveFontKey(fontKey);
|
|
35
|
+
const clear = new Uint8Array(bytes.length);
|
|
36
|
+
clear.set(bytes);
|
|
37
|
+
for (let i = 0; i < OBFUSCATED_PREFIX_LENGTH; i++) {
|
|
38
|
+
const keyByte = key[i % FONT_KEY_BYTE_LENGTH];
|
|
39
|
+
const cipherByte = clear[i];
|
|
40
|
+
if (keyByte === void 0 || cipherByte === void 0) throw new FontDeobfuscationError(`byte ${String(i)} of the obfuscated prefix is out of range`);
|
|
41
|
+
clear[i] = cipherByte ^ keyByte;
|
|
42
|
+
}
|
|
43
|
+
if (!looksLikeSfnt(clear)) throw new FontDeobfuscationError(`deobfuscating with font key ${JSON.stringify(fontKey)} did not produce a recognisable sfnt font`);
|
|
44
|
+
return clear;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
exports.FontDeobfuscationError = FontDeobfuscationError;
|
|
48
|
+
exports.deobfuscateEmbeddedFont = deobfuscateEmbeddedFont;
|
|
49
|
+
exports.deriveFontKey = deriveFontKey;
|
|
50
|
+
exports.looksLikeSfnt = looksLikeSfnt;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/fonts/obfuscation.d.ts
|
|
2
|
+
declare class FontDeobfuscationError extends Error {
|
|
3
|
+
constructor(detail: string);
|
|
4
|
+
}
|
|
5
|
+
declare function deriveFontKey(fontKey: string): Uint8Array<ArrayBuffer>;
|
|
6
|
+
declare function looksLikeSfnt(bytes: Uint8Array<ArrayBuffer>): boolean;
|
|
7
|
+
declare function deobfuscateEmbeddedFont(bytes: Uint8Array<ArrayBuffer>, fontKey: string | undefined): Uint8Array<ArrayBuffer>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { FontDeobfuscationError, deobfuscateEmbeddedFont, deriveFontKey, looksLikeSfnt };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/fonts/obfuscation.d.ts
|
|
2
|
+
declare class FontDeobfuscationError extends Error {
|
|
3
|
+
constructor(detail: string);
|
|
4
|
+
}
|
|
5
|
+
declare function deriveFontKey(fontKey: string): Uint8Array<ArrayBuffer>;
|
|
6
|
+
declare function looksLikeSfnt(bytes: Uint8Array<ArrayBuffer>): boolean;
|
|
7
|
+
declare function deobfuscateEmbeddedFont(bytes: Uint8Array<ArrayBuffer>, fontKey: string | undefined): Uint8Array<ArrayBuffer>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { FontDeobfuscationError, deobfuscateEmbeddedFont, deriveFontKey, looksLikeSfnt };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { parseSfnt } from "pdf-codec/sfnt";
|
|
2
|
+
//#region src/fonts/obfuscation.ts
|
|
3
|
+
const FONT_KEY_HEX_LENGTH = 32;
|
|
4
|
+
const FONT_KEY_BYTE_LENGTH = 16;
|
|
5
|
+
const OBFUSCATED_PREFIX_LENGTH = 32;
|
|
6
|
+
const SFNT_HEADER_LENGTH = 12;
|
|
7
|
+
const HEX_PAIR_PATTERN = /^[0-9A-Fa-f]{2}$/;
|
|
8
|
+
var FontDeobfuscationError = class extends Error {
|
|
9
|
+
constructor(detail) {
|
|
10
|
+
super(`deobfuscateEmbeddedFont: ${detail}`);
|
|
11
|
+
this.name = "FontDeobfuscationError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function deriveFontKey(fontKey) {
|
|
15
|
+
const hex = fontKey.replace(/[{}-]/g, "");
|
|
16
|
+
if (hex.length !== FONT_KEY_HEX_LENGTH) throw new FontDeobfuscationError(`w:fontKey ${JSON.stringify(fontKey)} has ${String(hex.length)} hex digit(s) after stripping braces and hyphens, expected ${String(FONT_KEY_HEX_LENGTH)}`);
|
|
17
|
+
const key = new Uint8Array(FONT_KEY_BYTE_LENGTH);
|
|
18
|
+
for (let i = 0; i < FONT_KEY_BYTE_LENGTH; i++) {
|
|
19
|
+
const start = 30 - i * 2;
|
|
20
|
+
const pair = hex.slice(start, start + 2);
|
|
21
|
+
if (!HEX_PAIR_PATTERN.test(pair)) throw new FontDeobfuscationError(`w:fontKey ${JSON.stringify(fontKey)} contains the non-hexadecimal pair ${JSON.stringify(pair)}`);
|
|
22
|
+
key[i] = Number.parseInt(pair, 16);
|
|
23
|
+
}
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
function looksLikeSfnt(bytes) {
|
|
27
|
+
return bytes.length >= SFNT_HEADER_LENGTH && parseSfnt(bytes) !== void 0;
|
|
28
|
+
}
|
|
29
|
+
function deobfuscateEmbeddedFont(bytes, fontKey) {
|
|
30
|
+
if (looksLikeSfnt(bytes)) return bytes;
|
|
31
|
+
if (fontKey === void 0) throw new FontDeobfuscationError("the part does not begin with an sfnt signature and carries no font key to deobfuscate it with");
|
|
32
|
+
if (bytes.length < OBFUSCATED_PREFIX_LENGTH) throw new FontDeobfuscationError(`an obfuscated part must be at least ${String(OBFUSCATED_PREFIX_LENGTH)} bytes long, got ${String(bytes.length)}`);
|
|
33
|
+
const key = deriveFontKey(fontKey);
|
|
34
|
+
const clear = new Uint8Array(bytes.length);
|
|
35
|
+
clear.set(bytes);
|
|
36
|
+
for (let i = 0; i < OBFUSCATED_PREFIX_LENGTH; i++) {
|
|
37
|
+
const keyByte = key[i % FONT_KEY_BYTE_LENGTH];
|
|
38
|
+
const cipherByte = clear[i];
|
|
39
|
+
if (keyByte === void 0 || cipherByte === void 0) throw new FontDeobfuscationError(`byte ${String(i)} of the obfuscated prefix is out of range`);
|
|
40
|
+
clear[i] = cipherByte ^ keyByte;
|
|
41
|
+
}
|
|
42
|
+
if (!looksLikeSfnt(clear)) throw new FontDeobfuscationError(`deobfuscating with font key ${JSON.stringify(fontKey)} did not produce a recognisable sfnt font`);
|
|
43
|
+
return clear;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { FontDeobfuscationError, deobfuscateEmbeddedFont, deriveFontKey, looksLikeSfnt };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_fonts_obfuscation = require("./obfuscation.cjs");
|
|
3
|
+
let odf_js = require("odf.js");
|
|
4
|
+
let pdf_codec_sfnt = require("pdf-codec/sfnt");
|
|
5
|
+
let pdf_codec_font_tables = require("pdf-codec/font-tables");
|
|
6
|
+
//#region src/fonts/odf.ts
|
|
7
|
+
const FONT_FACE_DECL_PARTS = ["content.xml", "styles.xml"];
|
|
8
|
+
const FS_SELECTION_ITALIC = 1;
|
|
9
|
+
const FS_SELECTION_BOLD = 32;
|
|
10
|
+
const BOLD_WEIGHT_THRESHOLD = 600;
|
|
11
|
+
var OdfEmbeddedFontError = class extends Error {
|
|
12
|
+
constructor(detail) {
|
|
13
|
+
super(`extractOdfEmbeddedFonts: ${detail}`);
|
|
14
|
+
this.name = "OdfEmbeddedFontError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function normaliseFamily(value) {
|
|
18
|
+
const first = (0, odf_js.decodeXmlText)(value).split(",")[0]?.trim() ?? "";
|
|
19
|
+
const quoted = /^'(.*)'$|^"(.*)"$/.exec(first);
|
|
20
|
+
return (quoted?.[1] ?? quoted?.[2] ?? first).trim();
|
|
21
|
+
}
|
|
22
|
+
function boldFromWeightAttribute(weight) {
|
|
23
|
+
const keyword = weight.trim().toLowerCase();
|
|
24
|
+
if (keyword === "bold" || keyword === "bolder") return true;
|
|
25
|
+
if (keyword === "normal" || keyword === "lighter") return false;
|
|
26
|
+
const numeric = Number.parseInt(keyword, 10);
|
|
27
|
+
return Number.isNaN(numeric) ? void 0 : numeric >= BOLD_WEIGHT_THRESHOLD;
|
|
28
|
+
}
|
|
29
|
+
function italicFromStyleAttribute(style) {
|
|
30
|
+
const keyword = style.trim().toLowerCase();
|
|
31
|
+
if (keyword === "italic" || keyword === "oblique") return true;
|
|
32
|
+
return keyword === "normal" ? false : void 0;
|
|
33
|
+
}
|
|
34
|
+
function styleFromFontBytes(bytes) {
|
|
35
|
+
const font = (0, pdf_codec_sfnt.parseSfnt)(bytes);
|
|
36
|
+
if (font === void 0) return;
|
|
37
|
+
const os2 = (0, pdf_codec_font_tables.parseOs2)(font);
|
|
38
|
+
if (os2 === void 0) return;
|
|
39
|
+
return {
|
|
40
|
+
bold: (os2.fsSelection & FS_SELECTION_BOLD) !== 0,
|
|
41
|
+
italic: (os2.fsSelection & FS_SELECTION_ITALIC) !== 0
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function fontPartBytes(pkg, href) {
|
|
45
|
+
const part = pkg.parts[href];
|
|
46
|
+
if (part === void 0) throw new OdfEmbeddedFontError(`svg:font-face-uri references ${JSON.stringify(href)}, which is not a part of this package`);
|
|
47
|
+
if (part.kind !== "binary") throw new OdfEmbeddedFontError(`embedded font part ${JSON.stringify(href)} is an XML part, not binary font data`);
|
|
48
|
+
return (0, odf_js.base64ToBytes)(part.base64);
|
|
49
|
+
}
|
|
50
|
+
function collectFontFace(pkg, fontFace, seenHrefs, out) {
|
|
51
|
+
const declaredFamily = (0, odf_js.attrValue)(fontFace, "svg:font-family") ?? (0, odf_js.attrValue)(fontFace, "style:name");
|
|
52
|
+
for (const src of (0, odf_js.childrenWithTag)(fontFace, "svg:font-face-src")) for (const uri of (0, odf_js.childrenWithTag)(src, "svg:font-face-uri")) {
|
|
53
|
+
const rawHref = (0, odf_js.attrValue)(uri, "xlink:href");
|
|
54
|
+
if (rawHref === void 0) throw new OdfEmbeddedFontError("a <svg:font-face-uri> carries no xlink:href");
|
|
55
|
+
const href = (0, odf_js.decodeXmlText)(rawHref);
|
|
56
|
+
if (seenHrefs.has(href)) continue;
|
|
57
|
+
seenHrefs.add(href);
|
|
58
|
+
if (declaredFamily === void 0) throw new OdfEmbeddedFontError(`the <style:font-face> embedding ${JSON.stringify(href)} declares neither svg:font-family nor style:name`);
|
|
59
|
+
const bytes = fontPartBytes(pkg, href);
|
|
60
|
+
if (!require_fonts_obfuscation.looksLikeSfnt(bytes)) throw new OdfEmbeddedFontError(`embedded font part ${JSON.stringify(href)} does not begin with a recognisable sfnt signature`);
|
|
61
|
+
const declaredWeight = (0, odf_js.attrValue)(uri, "loext:font-weight");
|
|
62
|
+
const declaredStyle = (0, odf_js.attrValue)(uri, "loext:font-style");
|
|
63
|
+
const intrinsic = styleFromFontBytes(bytes);
|
|
64
|
+
out.push({
|
|
65
|
+
family: normaliseFamily(declaredFamily),
|
|
66
|
+
bold: (declaredWeight === void 0 ? void 0 : boldFromWeightAttribute(declaredWeight)) ?? intrinsic?.bold ?? false,
|
|
67
|
+
italic: (declaredStyle === void 0 ? void 0 : italicFromStyleAttribute(declaredStyle)) ?? intrinsic?.italic ?? false,
|
|
68
|
+
bytes
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function extractOdfEmbeddedFonts(pkg) {
|
|
73
|
+
const out = [];
|
|
74
|
+
const seenHrefs = /* @__PURE__ */ new Set();
|
|
75
|
+
for (const partPath of FONT_FACE_DECL_PARTS) {
|
|
76
|
+
const part = pkg.parts[partPath];
|
|
77
|
+
if (part?.kind !== "xml") continue;
|
|
78
|
+
const root = (0, odf_js.rootElement)(part.nodes);
|
|
79
|
+
if (root === void 0) continue;
|
|
80
|
+
for (const decls of (0, odf_js.childrenWithTag)(root, "office:font-face-decls")) for (const fontFace of (0, odf_js.childrenWithTag)(decls, "style:font-face")) collectFontFace(pkg, fontFace, seenHrefs, out);
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
exports.OdfEmbeddedFontError = OdfEmbeddedFontError;
|
|
86
|
+
exports.extractOdfEmbeddedFonts = extractOdfEmbeddedFonts;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Package } from "odf.js";
|
|
2
|
+
import { ProvidedFont } from "pdf-codec";
|
|
3
|
+
//#region src/fonts/odf.d.ts
|
|
4
|
+
declare class OdfEmbeddedFontError extends Error {
|
|
5
|
+
constructor(detail: string);
|
|
6
|
+
}
|
|
7
|
+
declare function extractOdfEmbeddedFonts(pkg: Package): ProvidedFont[];
|
|
8
|
+
//#endregion
|
|
9
|
+
export { OdfEmbeddedFontError, extractOdfEmbeddedFonts };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Package } from "odf.js";
|
|
2
|
+
import { ProvidedFont } from "pdf-codec";
|
|
3
|
+
//#region src/fonts/odf.d.ts
|
|
4
|
+
declare class OdfEmbeddedFontError extends Error {
|
|
5
|
+
constructor(detail: string);
|
|
6
|
+
}
|
|
7
|
+
declare function extractOdfEmbeddedFonts(pkg: Package): ProvidedFont[];
|
|
8
|
+
//#endregion
|
|
9
|
+
export { OdfEmbeddedFontError, extractOdfEmbeddedFonts };
|