reamkit 1.21.0 → 1.23.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/esm/core/bytes.d.ts +22 -4
- package/dist/esm/core/bytes.js +73 -5
- package/dist/esm/core/converter/ream.d.ts +16 -0
- package/dist/esm/core/converter/ream.js +59 -9
- package/dist/esm/core/document-model/types.d.ts +78 -0
- package/dist/esm/core/drawingml/chart-geometry.js +11 -5
- package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
- package/dist/esm/core/drawingml/chart-parser.js +23 -1
- package/dist/esm/core/drawingml/colors.d.ts +30 -4
- package/dist/esm/core/drawingml/colors.js +45 -6
- package/dist/esm/core/drawingml/preset-geometry.js +236 -0
- package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
- package/dist/esm/core/drawingml/theme-parser.js +64 -1
- package/dist/esm/core/fonts/families.d.ts +10 -0
- package/dist/esm/core/fonts/families.js +44 -0
- package/dist/esm/core/fonts/index.d.ts +3 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
- package/dist/esm/core/fonts/remote-fonts.js +114 -8
- package/dist/esm/core/fonts/scripts.d.ts +26 -0
- package/dist/esm/core/fonts/scripts.js +177 -0
- package/dist/esm/core/metafile/blit.d.ts +33 -0
- package/dist/esm/core/metafile/blit.js +67 -0
- package/dist/esm/core/metafile/dib.d.ts +61 -0
- package/dist/esm/core/metafile/dib.js +205 -0
- package/dist/esm/core/metafile/emf.js +139 -24
- package/dist/esm/core/metafile/picture.d.ts +12 -1
- package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
- package/dist/esm/core/metafile/symbol-fonts.js +154 -0
- package/dist/esm/core/metafile/wmf.js +183 -10
- package/dist/esm/core/ole/escher-blip.d.ts +12 -0
- package/dist/esm/core/ole/escher-blip.js +88 -0
- package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
- package/dist/esm/core/vector.d.ts +5 -0
- package/dist/esm/excel/print-model.js +3 -1
- package/dist/esm/excel/sheet-shape-parser.js +1 -12
- package/dist/esm/excel/styles-parser.js +1 -1
- package/dist/esm/excel/xls/biff-reader.js +53 -18
- package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
- package/dist/esm/excel/xls/biff-styles.js +9 -7
- package/dist/esm/excel/xls/escher.js +90 -24
- package/dist/esm/excel/xlsx-reader.d.ts +1 -1
- package/dist/esm/excel/xlsx-reader.js +3 -3
- package/dist/esm/layout/page-doc.d.ts +77 -0
- package/dist/esm/layout/page-doc.js +34 -18
- package/dist/esm/layout/styled-layout.d.ts +2 -2
- package/dist/esm/layout/styled-layout.js +467 -76
- package/dist/esm/pdf/shading.d.ts +19 -0
- package/dist/esm/pdf/shading.js +84 -11
- package/dist/esm/pdf/styled-page-emitter.js +647 -444
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
- package/dist/esm/pptx/embedded-fonts.js +104 -0
- package/dist/esm/pptx/ole-preview.d.ts +10 -0
- package/dist/esm/pptx/ole-preview.js +52 -0
- package/dist/esm/pptx/placeholder-cascade.d.ts +136 -5
- package/dist/esm/pptx/placeholder-cascade.js +260 -27
- package/dist/esm/pptx/ppt/ppt-reader.js +120 -10
- package/dist/esm/pptx/ppt/ppt-text.d.ts +66 -1
- package/dist/esm/pptx/ppt/ppt-text.js +576 -137
- package/dist/esm/pptx/pptx-reader.d.ts +8 -0
- package/dist/esm/pptx/pptx-reader.js +271 -41
- package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
- package/dist/esm/pptx/preset-table-styles.js +27 -0
- package/dist/esm/pptx/slide-parser.d.ts +78 -10
- package/dist/esm/pptx/slide-parser.js +396 -95
- package/dist/esm/pptx/sp-helpers.d.ts +2 -1
- package/dist/esm/pptx/sp-helpers.js +22 -6
- package/dist/esm/pptx/table-style.d.ts +64 -0
- package/dist/esm/pptx/table-style.js +203 -0
- package/dist/esm/svg/svg-writer.js +53 -5
- package/dist/esm/word/document-parser.d.ts +7 -1
- package/dist/esm/word/document-parser.js +6 -6
- package/dist/esm/word/docx-reader.js +8 -5
- package/dist/esm/word/docx-to-pdf.d.ts +8 -7
- package/dist/esm/word/docx-to-pdf.js +25 -7
- package/dist/esm/word/drawing-parser.d.ts +34 -0
- package/dist/esm/word/drawing-parser.js +173 -15
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +6 -6
- package/dist/esm/word/paragraph-properties.d.ts +2 -1
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.d.ts +5 -2
- package/dist/esm/word/run-properties.js +13 -8
- package/dist/esm/word/styles-parser.d.ts +2 -1
- package/dist/esm/word/styles-parser.js +12 -12
- package/dist/esm/word/table-parser.js +3 -3
- package/dist/esm/word/theme-fonts.d.ts +10 -0
- package/dist/esm/word/theme-fonts.js +28 -0
- package/package.json +1 -1
- /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RunProperties } from '../core/document-model/index.js';
|
|
2
2
|
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
|
+
import { ThemeFonts } from '../core/drawingml/theme-parser.js';
|
|
3
4
|
import { PoNode } from '../core/po-helpers.js';
|
|
4
5
|
/**
|
|
5
6
|
* §19.3.1.36 `p:ph` — a shape's placeholder reference. `type`/`idx` tie a slide
|
|
@@ -42,4 +43,4 @@ export declare function parseXfrmBox(spPr: PoNode | undefined): ShapeBoxEmu | un
|
|
|
42
43
|
* (`sz`, hundredths of a point), bold/italic/underline, solid colour and the
|
|
43
44
|
* latin typeface. Scheme colours and the theme font cascade come in PX5.
|
|
44
45
|
*/
|
|
45
|
-
export declare function rPrToRunProps(rPr: PoNode | undefined, colors: ColorResolver): RunProperties;
|
|
46
|
+
export declare function rPrToRunProps(rPr: PoNode | undefined, colors: ColorResolver, themeFonts?: ThemeFonts): RunProperties;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { pt } from "../core/ir/units.js";
|
|
2
2
|
import { poAttr, poChildren, poIntAttr, poIs } from "../core/po-helpers.js";
|
|
3
3
|
import { resolveColorNode } from "../core/drawingml/colors.js";
|
|
4
|
+
import { resolveThemeFont } from "../core/drawingml/theme-parser.js";
|
|
4
5
|
//#region src/pptx/sp-helpers.ts
|
|
5
6
|
var isTrue = (v) => v === "1" || v === "true" || v === "on";
|
|
6
7
|
/**
|
|
@@ -54,21 +55,36 @@ function parseXfrmBox(spPr) {
|
|
|
54
55
|
* (`sz`, hundredths of a point), bold/italic/underline, solid colour and the
|
|
55
56
|
* latin typeface. Scheme colours and the theme font cascade come in PX5.
|
|
56
57
|
*/
|
|
57
|
-
function rPrToRunProps(rPr, colors) {
|
|
58
|
+
function rPrToRunProps(rPr, colors, themeFonts) {
|
|
58
59
|
if (!rPr) return {};
|
|
59
60
|
const sz = poIntAttr(rPr, "sz");
|
|
60
61
|
const u = poAttr(rPr, "u");
|
|
61
62
|
const colorHex = solidFillColor(rPr, colors);
|
|
62
63
|
const latin = poChildren(rPr).find((c) => poIs(c, "a:latin"));
|
|
63
|
-
const typeface = latin ? poAttr(latin, "typeface")?.trim() : void 0;
|
|
64
|
+
const typeface = resolveThemeFont(latin ? poAttr(latin, "typeface")?.trim() : void 0, themeFonts);
|
|
65
|
+
const flag = (name) => {
|
|
66
|
+
const v = poAttr(rPr, name);
|
|
67
|
+
return v === void 0 ? void 0 : isTrue(v);
|
|
68
|
+
};
|
|
69
|
+
const [b, i] = [flag("b"), flag("i")];
|
|
64
70
|
return {
|
|
65
|
-
...
|
|
66
|
-
...
|
|
67
|
-
...u !== void 0
|
|
71
|
+
...b !== void 0 ? { bold: b } : {},
|
|
72
|
+
...i !== void 0 ? { italic: i } : {},
|
|
73
|
+
...u !== void 0 ? { underline: u === "none" ? "none" : "single" } : {},
|
|
68
74
|
...sz !== void 0 ? { fontSizePt: pt(sz / 100) } : {},
|
|
69
75
|
...colorHex ? { colorHex } : {},
|
|
70
|
-
...typeface ? { fontFamily: { ascii: typeface } } : {}
|
|
76
|
+
...typeface ? { fontFamily: { ascii: typeface } } : {},
|
|
77
|
+
...capsOf(poAttr(rPr, "cap"))
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function capsOf(cap) {
|
|
81
|
+
if (cap === "all") return { caps: true };
|
|
82
|
+
if (cap === "small") return { smallCaps: true };
|
|
83
|
+
if (cap === "none") return {
|
|
84
|
+
caps: false,
|
|
85
|
+
smallCaps: false
|
|
71
86
|
};
|
|
87
|
+
return {};
|
|
72
88
|
}
|
|
73
89
|
function solidFillColor(rPr, colors) {
|
|
74
90
|
const solidFill = poChildren(rPr).find((c) => poIs(c, "a:solidFill"));
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Border, CellBorders, TableCell } from '../core/document-model/index.js';
|
|
2
|
+
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
|
+
import { PoNode } from '../core/po-helpers.js';
|
|
4
|
+
/** Which conditional parts of a style a table asks for (`a:tblPr` flags). */
|
|
5
|
+
export interface TableStyleFlags {
|
|
6
|
+
readonly firstRow: boolean;
|
|
7
|
+
readonly lastRow: boolean;
|
|
8
|
+
readonly firstCol: boolean;
|
|
9
|
+
readonly lastCol: boolean;
|
|
10
|
+
readonly bandRow: boolean;
|
|
11
|
+
readonly bandCol: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** What one part of a table style says about a cell. */
|
|
14
|
+
export interface TableStylePart {
|
|
15
|
+
readonly shadingHex?: string;
|
|
16
|
+
readonly borders?: CellBorders;
|
|
17
|
+
readonly bold?: boolean;
|
|
18
|
+
readonly italic?: boolean;
|
|
19
|
+
readonly colorHex?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Where a cell sits, which decides the parts that reach it. */
|
|
22
|
+
export interface CellPosition {
|
|
23
|
+
readonly row: number;
|
|
24
|
+
readonly rowCount: number;
|
|
25
|
+
readonly col: number;
|
|
26
|
+
readonly colCount: number;
|
|
27
|
+
}
|
|
28
|
+
/** The `a:tblPr` flags, all off unless the table says otherwise. */
|
|
29
|
+
export declare function tableStyleFlags(tblPr: PoNode | undefined): TableStyleFlags;
|
|
30
|
+
/** The GUID a table names, if it names one (`a:tblPr/a:tableStyleId`). */
|
|
31
|
+
export declare function tableStyleId(tblPr: PoNode | undefined): string | undefined;
|
|
32
|
+
/** What the deck's theme lends a style that points at it instead of spelling it out. */
|
|
33
|
+
export interface TableStyleTheme {
|
|
34
|
+
/** §20.1.4.1.14 `a:fillStyleLst` — the fills an `a:fillRef` indexes. */
|
|
35
|
+
readonly fills?: ReadonlyArray<PoNode>;
|
|
36
|
+
/** §20.1.4.2.19 `a:lnStyleLst` — the widths an `a:lnRef` indexes, in points. */
|
|
37
|
+
readonly lineWidths?: ReadonlyArray<number>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The style a cell wears, composed from every part that reaches it.
|
|
41
|
+
*
|
|
42
|
+
* @param style The `a:tblStyle` node.
|
|
43
|
+
* @param flags Which conditional parts the table asks for.
|
|
44
|
+
* @param at Where the cell sits.
|
|
45
|
+
* @param colors The deck's colour resolver.
|
|
46
|
+
* @param theme The theme's style lists, for the parts that point at them.
|
|
47
|
+
* @returns The composed part, empty when the style says nothing.
|
|
48
|
+
*/
|
|
49
|
+
export declare function cellStyle(style: PoNode, flags: TableStyleFlags, at: CellPosition, colors: ColorResolver, theme?: TableStyleTheme): TableStylePart;
|
|
50
|
+
/**
|
|
51
|
+
* §20.1.2.1 `a:ln` → the rule it draws on one side of a cell.
|
|
52
|
+
*
|
|
53
|
+
* Shared with the cell's OWN `a:lnL`/`a:lnR`/`a:lnT`/`a:lnB`, which are the
|
|
54
|
+
* same element under a different name.
|
|
55
|
+
*
|
|
56
|
+
* @param ln The line node.
|
|
57
|
+
* @param colors The deck's colour resolver.
|
|
58
|
+
* @returns The border, `none` when the line is one that draws nothing.
|
|
59
|
+
*/
|
|
60
|
+
export declare function lineBorder(ln: PoNode, colors: ColorResolver): Border;
|
|
61
|
+
/** The same part with the style's fill dropped, for a cell that says `a:noFill`. */
|
|
62
|
+
export declare function withoutFill(part: TableStylePart): TableStylePart;
|
|
63
|
+
/** A cell with the style's fill, borders and run properties filled in under its own. */
|
|
64
|
+
export declare function withCellStyle(cell: TableCell, part: TableStylePart): TableCell;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { pt } from "../core/ir/units.js";
|
|
2
|
+
import { poAttr, poChildren, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
|
|
3
|
+
import { readColorMods, resolveColorNode } from "../core/drawingml/colors.js";
|
|
4
|
+
//#region src/pptx/table-style.ts
|
|
5
|
+
/** The `a:tblPr` flags, all off unless the table says otherwise. */
|
|
6
|
+
function tableStyleFlags(tblPr) {
|
|
7
|
+
const on = (name) => tblPr !== void 0 && (poAttr(tblPr, name) === "1" || poAttr(tblPr, name) === "true");
|
|
8
|
+
return {
|
|
9
|
+
firstRow: on("firstRow"),
|
|
10
|
+
lastRow: on("lastRow"),
|
|
11
|
+
firstCol: on("firstCol"),
|
|
12
|
+
lastCol: on("lastCol"),
|
|
13
|
+
bandRow: on("bandRow"),
|
|
14
|
+
bandCol: on("bandCol")
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** The GUID a table names, if it names one (`a:tblPr/a:tableStyleId`). */
|
|
18
|
+
function tableStyleId(tblPr) {
|
|
19
|
+
const id = tblPr ? poChildren(tblPr).find((c) => poIs(c, "a:tableStyleId")) : void 0;
|
|
20
|
+
return id ? poText(id).trim() || void 0 : void 0;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The style a cell wears, composed from every part that reaches it.
|
|
24
|
+
*
|
|
25
|
+
* @param style The `a:tblStyle` node.
|
|
26
|
+
* @param flags Which conditional parts the table asks for.
|
|
27
|
+
* @param at Where the cell sits.
|
|
28
|
+
* @param colors The deck's colour resolver.
|
|
29
|
+
* @param theme The theme's style lists, for the parts that point at them.
|
|
30
|
+
* @returns The composed part, empty when the style says nothing.
|
|
31
|
+
*/
|
|
32
|
+
function cellStyle(style, flags, at, colors, theme) {
|
|
33
|
+
const names = ["a:wholeTbl"];
|
|
34
|
+
if (flags.bandCol) names.push(bandName(at.col, flags.firstCol, "V"));
|
|
35
|
+
if (flags.bandRow) names.push(bandName(at.row, flags.firstRow, "H"));
|
|
36
|
+
if (flags.firstCol && at.col === 0) names.push("a:firstCol");
|
|
37
|
+
if (flags.lastCol && at.col === at.colCount - 1) names.push("a:lastCol");
|
|
38
|
+
if (flags.firstRow && at.row === 0) names.push("a:firstRow");
|
|
39
|
+
if (flags.lastRow && at.row === at.rowCount - 1) names.push("a:lastRow");
|
|
40
|
+
let out = tableBackground(style, colors, theme);
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
const part = poChildren(style).find((c) => poIs(c, name));
|
|
43
|
+
if (part) out = {
|
|
44
|
+
...out,
|
|
45
|
+
...partStyle(part, colors, theme)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
function tableBackground(style, colors, theme) {
|
|
51
|
+
const bg = poChildren(style).find((c) => poIs(c, "a:tblBg"));
|
|
52
|
+
if (!bg) return {};
|
|
53
|
+
const solid = poChildren(bg).find((c) => poIs(c, "a:solidFill"));
|
|
54
|
+
const hex = (solid ? colorOf(solid, colors) : void 0) ?? refFillColor(poChildren(bg).find((c) => poIs(c, "a:fillRef")), colors, theme);
|
|
55
|
+
return hex ? { shadingHex: hex } : {};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* §20.1.4.1.16 `a:fillRef` — a fill named by its place in the theme's list,
|
|
59
|
+
* with the colour to put wherever that fill says `phClr`.
|
|
60
|
+
*
|
|
61
|
+
* Only its COLOUR is taken: a slot may be a gradient or a picture, and a cell's
|
|
62
|
+
* shading is one colour. The first solid colour the slot names, or the
|
|
63
|
+
* reference's own, is the nearest true answer.
|
|
64
|
+
*/
|
|
65
|
+
function refFillColor(ref, colors, theme) {
|
|
66
|
+
if (!ref) return void 0;
|
|
67
|
+
const own = colorOf(ref, colors);
|
|
68
|
+
const idx = poIntAttr(ref, "idx");
|
|
69
|
+
const slot = idx !== void 0 && idx > 0 ? theme?.fills?.[idx - 1] : void 0;
|
|
70
|
+
if (!slot) return own;
|
|
71
|
+
const solid = poChildren(slot).find((c) => poIs(c, "a:solidFill"));
|
|
72
|
+
const slotHex = solid ? colorOf(solid, colors) : void 0;
|
|
73
|
+
return slotHex && slotHex !== "PHCLR" ? own ?? slotHex : own;
|
|
74
|
+
}
|
|
75
|
+
function bandName(index, edge, axis) {
|
|
76
|
+
return (edge ? index - 1 : index) % 2 === 0 ? `a:band1${axis}` : `a:band2${axis}`;
|
|
77
|
+
}
|
|
78
|
+
function partStyle(part, colors, theme) {
|
|
79
|
+
const tcStyle = poChildren(part).find((c) => poIs(c, "a:tcStyle"));
|
|
80
|
+
const txStyle = poChildren(part).find((c) => poIs(c, "a:tcTxStyle"));
|
|
81
|
+
const fill = tcStyle ? poChildren(tcStyle).find((c) => poIs(c, "a:fill")) : void 0;
|
|
82
|
+
const solid = fill ? poChildren(fill).find((c) => poIs(c, "a:solidFill")) : void 0;
|
|
83
|
+
const shadingHex = solid ? colorOf(solid, colors) : void 0;
|
|
84
|
+
const bdr = tcStyle ? poChildren(tcStyle).find((c) => poIs(c, "a:tcBdr")) : void 0;
|
|
85
|
+
const borders = bdr ? partBorders(bdr, colors, theme) : void 0;
|
|
86
|
+
const on = (name) => poAttr(txStyle, name) === "on";
|
|
87
|
+
const colorHex = txStyle ? colorOf(txStyle, colors) : void 0;
|
|
88
|
+
return {
|
|
89
|
+
...shadingHex ? { shadingHex } : {},
|
|
90
|
+
...borders ? { borders } : {},
|
|
91
|
+
...txStyle && on("b") ? { bold: true } : {},
|
|
92
|
+
...txStyle && on("i") ? { italic: true } : {},
|
|
93
|
+
...colorHex ? { colorHex } : {}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
var SIDES = [
|
|
97
|
+
["a:left", "left"],
|
|
98
|
+
["a:right", "right"],
|
|
99
|
+
["a:top", "top"],
|
|
100
|
+
["a:bottom", "bottom"],
|
|
101
|
+
["a:insideH", "insideH"],
|
|
102
|
+
["a:insideV", "insideV"]
|
|
103
|
+
];
|
|
104
|
+
function partBorders(bdr, colors, theme) {
|
|
105
|
+
const out = {};
|
|
106
|
+
for (const [tag, side] of SIDES) {
|
|
107
|
+
const holder = poChildren(bdr).find((c) => poIs(c, tag));
|
|
108
|
+
const ln = holder ? poChildren(holder).find((c) => poIs(c, "a:ln")) : void 0;
|
|
109
|
+
if (ln) {
|
|
110
|
+
out[side] = lineBorder(ln, colors);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const border = refBorder(holder ? poChildren(holder).find((c) => poIs(c, "a:lnRef")) : void 0, colors, theme);
|
|
114
|
+
if (border) out[side] = border;
|
|
115
|
+
}
|
|
116
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
117
|
+
}
|
|
118
|
+
function refBorder(ref, colors, theme) {
|
|
119
|
+
if (!ref || poAttr(ref, "idx") === "0") return void 0;
|
|
120
|
+
const colorHex = colorOf(ref, colors);
|
|
121
|
+
if (colorHex === void 0) return void 0;
|
|
122
|
+
const idx = poIntAttr(ref, "idx");
|
|
123
|
+
return {
|
|
124
|
+
style: "single",
|
|
125
|
+
colorHex,
|
|
126
|
+
width: pt((idx !== void 0 && idx > 0 ? theme?.lineWidths?.[idx - 1] : void 0) ?? .75)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* §20.1.2.1 `a:ln` → the rule it draws on one side of a cell.
|
|
131
|
+
*
|
|
132
|
+
* Shared with the cell's OWN `a:lnL`/`a:lnR`/`a:lnT`/`a:lnB`, which are the
|
|
133
|
+
* same element under a different name.
|
|
134
|
+
*
|
|
135
|
+
* @param ln The line node.
|
|
136
|
+
* @param colors The deck's colour resolver.
|
|
137
|
+
* @returns The border, `none` when the line is one that draws nothing.
|
|
138
|
+
*/
|
|
139
|
+
function lineBorder(ln, colors) {
|
|
140
|
+
if (poChildren(ln).some((c) => poIs(c, "a:noFill")) || isTransparent(ln)) return { style: "none" };
|
|
141
|
+
const colorHex = colorOf(ln, colors);
|
|
142
|
+
const w = poIntAttr(ln, "w");
|
|
143
|
+
return {
|
|
144
|
+
style: "single",
|
|
145
|
+
...colorHex ? { colorHex } : {},
|
|
146
|
+
...w !== void 0 ? { width: pt(Math.max(.25, w / 12700)) } : {}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function isTransparent(ln) {
|
|
150
|
+
const fill = poChildren(ln).find((c) => poIs(c, "a:solidFill"));
|
|
151
|
+
const color = fill ? poChildren(fill).find((c) => poTag(c) !== void 0) : void 0;
|
|
152
|
+
const alpha = color ? readColorMods(color).find((m) => m.kind === "alpha") : void 0;
|
|
153
|
+
return alpha !== void 0 && alpha.val <= .001;
|
|
154
|
+
}
|
|
155
|
+
/** The same part with the style's fill dropped, for a cell that says `a:noFill`. */
|
|
156
|
+
function withoutFill(part) {
|
|
157
|
+
const { shadingHex: _fill, ...rest } = part;
|
|
158
|
+
return rest;
|
|
159
|
+
}
|
|
160
|
+
function colorOf(node, colors) {
|
|
161
|
+
for (const child of poChildren(node)) {
|
|
162
|
+
const hex = resolveColorNode(child, colors);
|
|
163
|
+
if (hex !== void 0) return hex;
|
|
164
|
+
const deeper = poTag(child) !== void 0 ? colorOf(child, colors) : void 0;
|
|
165
|
+
if (deeper !== void 0) return deeper;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** A cell with the style's fill, borders and run properties filled in under its own. */
|
|
169
|
+
function withCellStyle(cell, part) {
|
|
170
|
+
const runProps = {
|
|
171
|
+
...part.bold === true ? { bold: true } : {},
|
|
172
|
+
...part.italic === true ? { italic: true } : {},
|
|
173
|
+
...part.colorHex !== void 0 ? { colorHex: part.colorHex } : {}
|
|
174
|
+
};
|
|
175
|
+
const content = Object.keys(runProps).length === 0 ? cell.content : cell.content.map((block) => block.kind === "paragraph" ? {
|
|
176
|
+
...block,
|
|
177
|
+
paragraph: {
|
|
178
|
+
...block.paragraph,
|
|
179
|
+
runs: block.paragraph.runs.map((run) => ({
|
|
180
|
+
...run,
|
|
181
|
+
properties: {
|
|
182
|
+
...runProps,
|
|
183
|
+
...run.properties
|
|
184
|
+
}
|
|
185
|
+
}))
|
|
186
|
+
}
|
|
187
|
+
} : block);
|
|
188
|
+
const borders = part.borders || cell.properties.borders ? {
|
|
189
|
+
...part.borders,
|
|
190
|
+
...cell.properties.borders
|
|
191
|
+
} : void 0;
|
|
192
|
+
return {
|
|
193
|
+
...cell,
|
|
194
|
+
properties: {
|
|
195
|
+
...part.shadingHex !== void 0 ? { shading: { colorHex: part.shadingHex } } : {},
|
|
196
|
+
...cell.properties,
|
|
197
|
+
...borders ? { borders } : {}
|
|
198
|
+
},
|
|
199
|
+
content
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { cellStyle, lineBorder, tableStyleFlags, tableStyleId, withCellStyle, withoutFill };
|
|
@@ -56,12 +56,9 @@ var svgWriter = {
|
|
|
56
56
|
};
|
|
57
57
|
function emitPage(out, page, laid, losses, idc) {
|
|
58
58
|
const plan = paintPlan(page.commands);
|
|
59
|
+
for (const item of plan.behind) emitPageItem(out, item, laid, losses, idc);
|
|
59
60
|
for (const f of plan.fills) out.push(`<rect x="${fmt(f.x)}" y="${fmt(f.y)}" width="${fmt(f.width)}" height="${fmt(f.height)}" fill="#${f.fillColorHex}"/>`);
|
|
60
|
-
for (const img of plan.images)
|
|
61
|
-
const href = imageHref(img.imageResourceName, laid);
|
|
62
|
-
if (!href) continue;
|
|
63
|
-
out.push(`<image x="${fmt(img.x)}" y="${fmt(img.y)}" width="${fmt(img.width)}" height="${fmt(img.height)}" href="${href}" preserveAspectRatio="none"/>`);
|
|
64
|
-
}
|
|
61
|
+
for (const img of plan.images) emitImage(out, img, laid, idc);
|
|
65
62
|
for (const b of plan.borders) {
|
|
66
63
|
const x2 = b.x + b.width;
|
|
67
64
|
const yTop = b.y;
|
|
@@ -90,8 +87,59 @@ function emitPage(out, page, laid, losses, idc) {
|
|
|
90
87
|
out.push(`<line x1="${fmt(ax)}" y1="${fmt(ay)}" x2="${fmt(bx)}" y2="${fmt(by)}" stroke="#${b.borderColorHex}" stroke-width="${fmt(b.borderSizePt)}"${b.borderStyle === "dashed" ? " stroke-dasharray=\"8 2.5\"" : b.borderStyle === "dashSmallGap" ? " stroke-dasharray=\"3 1\"" : b.borderStyle === "dotted" ? " stroke-dasharray=\"0.5 1\"" : ""}/>`);
|
|
91
88
|
}
|
|
92
89
|
for (const sh of plan.shapes) emitShape(out, sh.shape, idc);
|
|
90
|
+
for (const picture of plan.pictures) for (const item of picture) emitPageItem(out, item, laid, losses, idc);
|
|
93
91
|
for (const t of plan.lines) emitTextLine(out, t, losses, idc);
|
|
94
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* One picture. §20.1.8.23 `a:duotone` recolours it between two colours — its
|
|
95
|
+
* dark end and its light end — which is a luminance-to-two-tone map, and SVG
|
|
96
|
+
* states exactly that: luminance into every channel, then a two-entry transfer
|
|
97
|
+
* table per channel.
|
|
98
|
+
*
|
|
99
|
+
* @param out The SVG fragment sink.
|
|
100
|
+
* @param img The image item.
|
|
101
|
+
* @param laid The laid-out document, for the image's data URI.
|
|
102
|
+
* @param idc The shared id counter, for the filter's id.
|
|
103
|
+
*/
|
|
104
|
+
function emitImage(out, img, laid, idc) {
|
|
105
|
+
const href = imageHref(img.imageResourceName, laid);
|
|
106
|
+
if (!href) return;
|
|
107
|
+
let filter = "";
|
|
108
|
+
if (img.duotone) {
|
|
109
|
+
const id = `duo${String(idc.n++)}`;
|
|
110
|
+
const chan = (hex, at) => parseInt(hex.slice(at, at + 2), 16) / 255;
|
|
111
|
+
const table = (at) => `${fmt(chan(img.duotone.shadowHex, at))} ${fmt(chan(img.duotone.highlightHex, at))}`;
|
|
112
|
+
out.push(`<filter id="${id}" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/><feComponentTransfer><feFuncR type="table" tableValues="${table(0)}"/><feFuncG type="table" tableValues="${table(2)}"/><feFuncB type="table" tableValues="${table(4)}"/></feComponentTransfer></filter>`);
|
|
113
|
+
filter = ` filter="url(#${id})"`;
|
|
114
|
+
}
|
|
115
|
+
out.push(`<image x="${fmt(img.x)}" y="${fmt(img.y)}" width="${fmt(img.width)}" height="${fmt(img.height)}" href="${href}" preserveAspectRatio="none"${filter}/>`);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* One page item, whatever its kind — for the runs that paint in their own order
|
|
119
|
+
* (what stands behind the content, and a picture's own primitives) rather than
|
|
120
|
+
* in the by-kind passes.
|
|
121
|
+
*
|
|
122
|
+
* @param out The SVG fragment sink.
|
|
123
|
+
* @param item The item to draw.
|
|
124
|
+
* @param laid The laid-out document, for image resources.
|
|
125
|
+
* @param losses Where an unrenderable item records itself.
|
|
126
|
+
* @param idc The shared id counter for generated defs.
|
|
127
|
+
*/
|
|
128
|
+
function emitPageItem(out, item, laid, losses, idc) {
|
|
129
|
+
if (item.type === "shape") {
|
|
130
|
+
emitShape(out, item.shape, idc);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (item.type === "line") {
|
|
134
|
+
emitTextLine(out, item, losses, idc);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (item.type === "image") {
|
|
138
|
+
emitImage(out, item, laid, idc);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (item.type === "fill") out.push(`<rect x="${fmt(item.x)}" y="${fmt(item.y)}" width="${fmt(item.width)}" height="${fmt(item.height)}" fill="#${item.fillColorHex}"/>`);
|
|
142
|
+
}
|
|
95
143
|
function emitTextLine(out, item, losses, idc) {
|
|
96
144
|
const clip = item.clip;
|
|
97
145
|
if (clip) {
|
|
@@ -2,6 +2,7 @@ import { BodyElement, Comment, RunProperties, Section, SectionProperties, ShapeF
|
|
|
2
2
|
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
3
|
import { Loss, ResourceId } from '../core/ir/index.js';
|
|
4
4
|
import { PoNode } from '../core/po-helpers.js';
|
|
5
|
+
import { ThemeFonts } from '../core/drawingml/theme-parser.js';
|
|
5
6
|
import { ThemeStyles } from './drawing-parser.js';
|
|
6
7
|
/**
|
|
7
8
|
* Resolves a drawing relationship id to a content-addressed `ResourceId` —
|
|
@@ -57,6 +58,11 @@ export interface ParseContext {
|
|
|
57
58
|
* the shadow a gallery-drawn shape wears.
|
|
58
59
|
*/
|
|
59
60
|
readonly themeStyles?: ThemeStyles;
|
|
61
|
+
/**
|
|
62
|
+
* §20.1.4.1.16 — the theme's font scheme, for the typefaces a run names by
|
|
63
|
+
* slot (`w:asciiTheme="minorHAnsi"`) rather than by name.
|
|
64
|
+
*/
|
|
65
|
+
readonly themeFonts?: ThemeFonts;
|
|
60
66
|
/**
|
|
61
67
|
* Sink for graceful-degradation notices (E-SMARTART SA3): a SmartArt with no
|
|
62
68
|
* drawing override records a dropped-feature {@link Loss} rather than vanishing.
|
|
@@ -185,7 +191,7 @@ export declare function parseBodyElements(children: ReadonlyArray<PoNode>, ctx?:
|
|
|
185
191
|
* @param sdt The `w:sdt` element.
|
|
186
192
|
* @returns The properties, or `undefined` when the control states none.
|
|
187
193
|
*/
|
|
188
|
-
export declare function sdtRunProperties(sdt: PoNode): RunProperties | undefined;
|
|
194
|
+
export declare function sdtRunProperties(sdt: PoNode, themeFonts?: ThemeFonts): RunProperties | undefined;
|
|
189
195
|
/**
|
|
190
196
|
* Parse `footnotes.xml` / `endnotes.xml` (§17.11) into note content by id. The
|
|
191
197
|
* separator / continuationSeparator / continuationNotice stubs (negative ids or
|
|
@@ -425,7 +425,7 @@ function tryExtractDrawingFromParagraph(p, ctx) {
|
|
|
425
425
|
if (!vml && drawings.every((d) => poChildren(d).some((c) => poIs(c, "wp:anchor")))) return null;
|
|
426
426
|
const parseBody = (children) => parseBodyElements(children, ctx);
|
|
427
427
|
const pPrNode = poChildren(p).find((c) => poIs(c, "w:pPr"));
|
|
428
|
-
const paragraphProperties = pPrNode ? parseParagraphProperties(poElementToFlat(pPrNode), ctx.autoSpacingPt) : {};
|
|
428
|
+
const paragraphProperties = pPrNode ? parseParagraphProperties(poElementToFlat(pPrNode), ctx.autoSpacingPt, ctx.themeFonts) : {};
|
|
429
429
|
if (drawings.length === 0) {
|
|
430
430
|
const content = parseVmlPicture(vml, parseBody);
|
|
431
431
|
if (!content) return null;
|
|
@@ -536,7 +536,7 @@ function parseParagraph(p, ctx, extraBookmarks, anchored, sdtRunProps) {
|
|
|
536
536
|
if (bookmarkName !== void 0 && bookmarkName !== "" && bookmarkName !== "_GoBack") bookmarks.push(bookmarkName);
|
|
537
537
|
}
|
|
538
538
|
const pPr = poChildren(p).find((c) => poIs(c, "w:pPr"));
|
|
539
|
-
let properties = parseParagraphProperties(pPr ? poElementToFlat(pPr) : void 0, ctx.autoSpacingPt);
|
|
539
|
+
let properties = parseParagraphProperties(pPr ? poElementToFlat(pPr) : void 0, ctx.autoSpacingPt, ctx.themeFonts);
|
|
540
540
|
if (pPr && poChildren(pPr).some((c) => poIs(c, "w:sectPr"))) properties = {
|
|
541
541
|
...properties,
|
|
542
542
|
sectionBreak: true
|
|
@@ -773,7 +773,7 @@ function collectRuns(container, out, ctx, href, anchor, anchored, sdtRunProps) {
|
|
|
773
773
|
if (bookmark !== void 0 && bookmark !== "") childAnchor = bookmark;
|
|
774
774
|
}
|
|
775
775
|
}
|
|
776
|
-
const childProps = tag === "w:sdt" ? sdtRunProperties(child) ?? sdtRunProps : sdtRunProps;
|
|
776
|
+
const childProps = tag === "w:sdt" ? sdtRunProperties(child, ctx.themeFonts) ?? sdtRunProps : sdtRunProps;
|
|
777
777
|
collectRuns(child, out, ctx, childHref, childAnchor, anchored, childProps);
|
|
778
778
|
}
|
|
779
779
|
}
|
|
@@ -785,11 +785,11 @@ function collectRuns(container, out, ctx, href, anchor, anchored, sdtRunProps) {
|
|
|
785
785
|
* @param sdt The `w:sdt` element.
|
|
786
786
|
* @returns The properties, or `undefined` when the control states none.
|
|
787
787
|
*/
|
|
788
|
-
function sdtRunProperties(sdt) {
|
|
788
|
+
function sdtRunProperties(sdt, themeFonts) {
|
|
789
789
|
const sdtPr = poChildren(sdt).find((c) => poIs(c, "w:sdtPr"));
|
|
790
790
|
const rPr = sdtPr ? poChildren(sdtPr).find((c) => poIs(c, "w:rPr")) : void 0;
|
|
791
791
|
if (!rPr) return void 0;
|
|
792
|
-
const props = parseRunProperties(poElementToFlat(rPr));
|
|
792
|
+
const props = parseRunProperties(poElementToFlat(rPr), themeFonts);
|
|
793
793
|
return Object.keys(props).length > 0 ? props : void 0;
|
|
794
794
|
}
|
|
795
795
|
var WINGDINGS_TO_UNICODE = new Map([
|
|
@@ -870,7 +870,7 @@ function symbolChar(font, char) {
|
|
|
870
870
|
}
|
|
871
871
|
function parseRun(r, ctx, anchored) {
|
|
872
872
|
const rPr = poChildren(r).find((c) => poIs(c, "w:rPr"));
|
|
873
|
-
const properties = parseRunProperties(rPr ? poElementToFlat(rPr) : void 0);
|
|
873
|
+
const properties = parseRunProperties(rPr ? poElementToFlat(rPr) : void 0, ctx.themeFonts);
|
|
874
874
|
let text = "";
|
|
875
875
|
let pageBreak = false;
|
|
876
876
|
let columnBreak = false;
|
|
@@ -6,10 +6,10 @@ import "../core/style-cascade/index.js";
|
|
|
6
6
|
import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
|
|
7
7
|
import "../core/numbering/index.js";
|
|
8
8
|
import { poFindDescendant } from "../core/po-helpers.js";
|
|
9
|
-
import {
|
|
9
|
+
import { packageHasPart } from "../core/bytes.js";
|
|
10
10
|
import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
|
|
11
11
|
import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
|
|
12
|
-
import { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
|
|
12
|
+
import { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeFonts, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
|
|
13
13
|
import { isOoxmlRel } from "../core/opc/relationship-types.js";
|
|
14
14
|
import { OpcPackage } from "../core/opc/package.js";
|
|
15
15
|
import { parseCoreProperties } from "../core/opc/core-properties.js";
|
|
@@ -52,6 +52,7 @@ function readDocx(docx) {
|
|
|
52
52
|
const resolveColor = buildColorResolver(pkg, main.path);
|
|
53
53
|
const themeData = loadTheme(pkg, main.path);
|
|
54
54
|
const themeLineWidths = themeData ? parseThemeLineWidths(themeData) : void 0;
|
|
55
|
+
const themeFonts = themeData ? parseThemeFonts(themeData) : void 0;
|
|
55
56
|
const themeStyles = themeData ? {
|
|
56
57
|
fills: parseThemeFillStyles(themeData),
|
|
57
58
|
bgFills: parseThemeBgFillStyles(themeData),
|
|
@@ -68,6 +69,7 @@ function readDocx(docx) {
|
|
|
68
69
|
...settings.compatibilityMode === void 0 ? { autoSpacingPt: 14 } : {},
|
|
69
70
|
...themeLineWidths && themeLineWidths.length > 0 ? { themeLineWidths } : {},
|
|
70
71
|
...themeStyles ? { themeStyles } : {},
|
|
72
|
+
...themeFonts ? { themeFonts } : {},
|
|
71
73
|
resolveImage,
|
|
72
74
|
resolveHyperlink,
|
|
73
75
|
resolveDiagram: makeDiagramResolver(pkg, main.path, resources),
|
|
@@ -84,11 +86,12 @@ function readDocx(docx) {
|
|
|
84
86
|
endIndex: bodyIndexForBlock(blocks, s.endIndex)
|
|
85
87
|
}));
|
|
86
88
|
const stylesData = pkg.getPart(STYLES_PART);
|
|
87
|
-
const styles = stylesData ? parseStyles(stylesData) : EMPTY_STYLE_SHEET;
|
|
89
|
+
const styles = stylesData ? parseStyles(stylesData, themeFonts) : EMPTY_STYLE_SHEET;
|
|
88
90
|
const numberingData = pkg.getPart(NUMBERING_PART);
|
|
89
|
-
const numbering = numberingData ? parseNumbering(numberingData, makeImageResolver(pkg, resources, NUMBERING_PART)) : EMPTY_NUMBERING;
|
|
91
|
+
const numbering = numberingData ? parseNumbering(numberingData, makeImageResolver(pkg, resources, NUMBERING_PART), themeFonts) : EMPTY_NUMBERING;
|
|
90
92
|
const noteCtx = (part) => ({
|
|
91
93
|
resolveColor,
|
|
94
|
+
...themeFonts ? { themeFonts } : {},
|
|
92
95
|
resolveImage: makeImageResolver(pkg, resources, part),
|
|
93
96
|
resolveHyperlink: makeHyperlinkResolver(pkg, part)
|
|
94
97
|
});
|
|
@@ -166,7 +169,7 @@ var docxReader = {
|
|
|
166
169
|
FEATURES.trackedChanges,
|
|
167
170
|
FEATURES.fontsEmbedding
|
|
168
171
|
]),
|
|
169
|
-
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && (
|
|
172
|
+
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && (packageHasPart(bytes, "word/document.xml") || packageHasPart(bytes, "word/styles.xml") || packageHasPart(bytes, "word/_rels/")),
|
|
170
173
|
read: (bytes) => readDocx(bytes)
|
|
171
174
|
};
|
|
172
175
|
function infoFromCore(core) {
|
|
@@ -38,10 +38,11 @@ export interface ConvertDocxOptions extends Omit<StyledRenderOptions, 'registry'
|
|
|
38
38
|
*/
|
|
39
39
|
export declare function convertDocxToPdfSync(docx: Uint8Array, options: ConvertDocxOptions): Uint8Array;
|
|
40
40
|
/**
|
|
41
|
-
* The distinct curated font families the document references
|
|
42
|
-
* `
|
|
43
|
-
* `'arimo'` as a fallback for
|
|
44
|
-
*
|
|
41
|
+
* The distinct curated font families the document references — the `w:ascii` of
|
|
42
|
+
* `styles.xml` + `document.xml`, and the theme slots they point at instead
|
|
43
|
+
* (§17.3.2.26) — always including the sans default `'arimo'` as a fallback for
|
|
44
|
+
* unstyled runs / math / charts. The async path fetches one substitute set per
|
|
45
|
+
* family so each run renders in the right one.
|
|
45
46
|
*
|
|
46
47
|
* @param docx The `.docx` bytes.
|
|
47
48
|
* @returns The resolved {@link FamilyKey} set; just the default when the package
|
|
@@ -76,9 +77,9 @@ export declare function resolveDocxAutoFonts(docx: Uint8Array, options?: {
|
|
|
76
77
|
}>;
|
|
77
78
|
/**
|
|
78
79
|
* Best-effort detection of the document's primary font family, used to choose a
|
|
79
|
-
* substitute for auto-download. Prefers the document defaults'
|
|
80
|
-
*
|
|
81
|
-
* need to fully parse for this hint.
|
|
80
|
+
* substitute for auto-download. Prefers the document defaults' font — spelled
|
|
81
|
+
* out or pointed at in the theme — then falls back to the most frequent run
|
|
82
|
+
* font. A cheap regex over the XML: no need to fully parse for this hint.
|
|
82
83
|
*
|
|
83
84
|
* @param docx The `.docx` bytes.
|
|
84
85
|
* @returns The detected family name, or `undefined` when none is found.
|
|
@@ -3,12 +3,15 @@ import { FontRegistry } from "../core/font/font-registry.js";
|
|
|
3
3
|
import { signPdf } from "../pdf/signature.js";
|
|
4
4
|
import { renderStyledPdf, renderStyledPdfEncrypted } from "../pdf/styled-page-renderer.js";
|
|
5
5
|
import "../pdf/index.js";
|
|
6
|
+
import { parseThemeFonts } from "../core/drawingml/theme-parser.js";
|
|
6
7
|
import { OpcPackage } from "../core/opc/package.js";
|
|
7
8
|
import "../core/opc/index.js";
|
|
9
|
+
import { resolveWordThemeFont } from "./theme-fonts.js";
|
|
8
10
|
import { readDocx } from "./docx-reader.js";
|
|
9
11
|
import { flowRenderOptions } from "../core/converter/project.js";
|
|
10
12
|
//#region src/word/docx-to-pdf.ts
|
|
11
13
|
var STYLES_PART = "word/styles.xml";
|
|
14
|
+
var THEME_PART = "word/theme/theme1.xml";
|
|
12
15
|
var MAIN_DOCUMENT_PART = "word/document.xml";
|
|
13
16
|
/**
|
|
14
17
|
* Synchronous one-shot conversion (requires `fonts`/`fontBytes`, no network).
|
|
@@ -49,10 +52,11 @@ function prepareDocxStyledRender(docx, options) {
|
|
|
49
52
|
};
|
|
50
53
|
}
|
|
51
54
|
/**
|
|
52
|
-
* The distinct curated font families the document references
|
|
53
|
-
* `
|
|
54
|
-
* `'arimo'` as a fallback for
|
|
55
|
-
*
|
|
55
|
+
* The distinct curated font families the document references — the `w:ascii` of
|
|
56
|
+
* `styles.xml` + `document.xml`, and the theme slots they point at instead
|
|
57
|
+
* (§17.3.2.26) — always including the sans default `'arimo'` as a fallback for
|
|
58
|
+
* unstyled runs / math / charts. The async path fetches one substitute set per
|
|
59
|
+
* family so each run renders in the right one.
|
|
56
60
|
*
|
|
57
61
|
* @param docx The `.docx` bytes.
|
|
58
62
|
* @returns The resolved {@link FamilyKey} set; just the default when the package
|
|
@@ -67,16 +71,30 @@ function detectDocxFamilyKeys(docx) {
|
|
|
67
71
|
return keys;
|
|
68
72
|
}
|
|
69
73
|
const decoder = new TextDecoder("utf-8");
|
|
70
|
-
const
|
|
74
|
+
const themeFonts = docxThemeFonts(pkg);
|
|
75
|
+
const re = /<w:rFonts[^>]*?\bw:(ascii|asciiTheme)="([^"]+)"/g;
|
|
71
76
|
for (const part of [STYLES_PART, MAIN_DOCUMENT_PART]) {
|
|
72
77
|
const data = pkg.getPart(part);
|
|
73
78
|
if (!data) continue;
|
|
74
79
|
const xml = decoder.decode(data);
|
|
75
80
|
let m;
|
|
76
|
-
while ((m = re.exec(xml)) !== null)
|
|
81
|
+
while ((m = re.exec(xml)) !== null) {
|
|
82
|
+
const name = m[1] === "ascii" ? m[2] : resolveWordThemeFont(m[2], themeFonts);
|
|
83
|
+
if (name !== void 0) keys.add(resolveFamilyKey(name));
|
|
84
|
+
}
|
|
77
85
|
}
|
|
78
86
|
return keys;
|
|
79
87
|
}
|
|
88
|
+
/** §20.1.4.1.16 — the theme's font scheme, for the slots `w:rFonts` points at. */
|
|
89
|
+
function docxThemeFonts(pkg) {
|
|
90
|
+
const data = pkg.getPart(THEME_PART);
|
|
91
|
+
if (!data) return void 0;
|
|
92
|
+
try {
|
|
93
|
+
return parseThemeFonts(data);
|
|
94
|
+
} catch {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
80
98
|
/**
|
|
81
99
|
* One-shot .docx → PDF (auto-downloads a substitute font set when the caller
|
|
82
100
|
* supplies none; zero network with `fonts`/`fontBytes`).
|
|
@@ -140,4 +158,4 @@ async function resolveDocxAutoFonts(docx, options = {}) {
|
|
|
140
158
|
};
|
|
141
159
|
}
|
|
142
160
|
//#endregion
|
|
143
|
-
export { convertDocxToPdf
|
|
161
|
+
export { convertDocxToPdf };
|