reamkit 1.6.0 → 1.9.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 +13 -1
- package/dist/esm/core/converter/ream.d.ts +1 -0
- package/dist/esm/core/converter/ream.js +1 -1
- package/dist/esm/core/document-model/types.d.ts +3 -1
- package/dist/esm/core/drawingml/chart-serializer.d.ts +2 -0
- package/dist/esm/core/drawingml/chart-serializer.js +53 -0
- package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
- package/dist/esm/core/drawingml/shape-render.js +27 -1
- package/dist/esm/core/vector.d.ts +10 -0
- package/dist/esm/excel/print-model.js +6 -3
- package/dist/esm/excel/xlsx-writer.js +81 -17
- package/dist/esm/html/html-writer.js +4 -2
- package/dist/esm/layout/styled-layout.js +5 -2
- package/dist/esm/pdf/shading.d.ts +12 -0
- package/dist/esm/pdf/shading.js +152 -0
- package/dist/esm/pdf/styled-page-emitter.js +29 -4
- package/dist/esm/pdf/vector-graphics.d.ts +1 -1
- package/dist/esm/pdf/vector-graphics.js +5 -3
- package/dist/esm/pdf-reader/ccitt.d.ts +15 -0
- package/dist/esm/pdf-reader/ccitt.js +394 -0
- package/dist/esm/pdf-reader/content.d.ts +43 -1
- package/dist/esm/pdf-reader/content.js +173 -4
- package/dist/esm/pdf-reader/crypto.d.ts +7 -0
- package/dist/esm/pdf-reader/crypto.js +609 -0
- package/dist/esm/pdf-reader/decrypt.d.ts +5 -0
- package/dist/esm/pdf-reader/decrypt.js +211 -0
- package/dist/esm/pdf-reader/document.d.ts +8 -1
- package/dist/esm/pdf-reader/document.js +210 -27
- package/dist/esm/pdf-reader/flow-build.d.ts +16 -1
- package/dist/esm/pdf-reader/flow-build.js +113 -3
- package/dist/esm/pdf-reader/image-decode.d.ts +15 -0
- package/dist/esm/pdf-reader/image-decode.js +550 -0
- package/dist/esm/pdf-reader/images.d.ts +16 -0
- package/dist/esm/pdf-reader/images.js +90 -0
- package/dist/esm/pdf-reader/layout.d.ts +2 -2
- package/dist/esm/pdf-reader/layout.js +86 -14
- package/dist/esm/pdf-reader/png-encode.d.ts +2 -0
- package/dist/esm/pdf-reader/png-encode.js +98 -0
- package/dist/esm/pdf-reader/predictor.d.ts +7 -0
- package/dist/esm/pdf-reader/predictor.js +61 -0
- package/dist/esm/pdf-reader/reader.d.ts +1 -1
- package/dist/esm/pdf-reader/reader.js +14 -7
- package/dist/esm/pdf-reader/shading.d.ts +3 -0
- package/dist/esm/pdf-reader/shading.js +147 -0
- package/dist/esm/pdf-reader/tagged.d.ts +2 -2
- package/dist/esm/pdf-reader/tagged.js +60 -7
- package/dist/esm/pdf-reader/text.js +91 -10
- package/dist/esm/pdf-reader/vector.d.ts +16 -0
- package/dist/esm/pdf-reader/vector.js +63 -0
- package/dist/esm/svg/svg-writer.js +12 -5
- package/dist/esm/word/docx-writer.js +104 -8
- package/dist/esm/word/drawing-parser.js +28 -17
- package/dist/esm/word/omml-serializer.d.ts +2 -0
- package/dist/esm/word/omml-serializer.js +48 -0
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { pt } from "../core/ir/units.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ResourceStore } from "../core/ir/resources.js";
|
|
3
|
+
import { buildFlowDoc, dedupeLosses, imageBlock, paragraphBlock, paragraphFromRuns } from "./flow-build.js";
|
|
4
|
+
import { collectPageImages } from "./images.js";
|
|
3
5
|
import { extractPageText } from "./text.js";
|
|
4
6
|
import { readStructTree } from "./struct-tree.js";
|
|
5
7
|
//#region src/pdf-reader/tagged.ts
|
|
@@ -7,12 +9,45 @@ var ASSUMED_CONTENT_WIDTH_PT = 468;
|
|
|
7
9
|
function reconstructTaggedPdf(file) {
|
|
8
10
|
const root = readStructTree(file);
|
|
9
11
|
if (!root) return void 0;
|
|
10
|
-
const
|
|
12
|
+
const pages = file.pages();
|
|
13
|
+
const pageRuns = pages.map((page) => {
|
|
11
14
|
const byMcid = /* @__PURE__ */ new Map();
|
|
12
|
-
for (const run of extractPageText(file, page))
|
|
15
|
+
for (const run of extractPageText(file, page)) {
|
|
16
|
+
if (run.mcid === void 0) continue;
|
|
17
|
+
const list = byMcid.get(run.mcid);
|
|
18
|
+
if (list) list.push(run);
|
|
19
|
+
else byMcid.set(run.mcid, [run]);
|
|
20
|
+
}
|
|
21
|
+
return byMcid;
|
|
22
|
+
});
|
|
23
|
+
const runsOfMcid = (page, mcid) => pageRuns[page]?.get(mcid) ?? [];
|
|
24
|
+
const resources = new ResourceStore();
|
|
25
|
+
const pageImages = pages.map((page) => collectPageImages(file, page));
|
|
26
|
+
const imageLosses = dedupeLosses(pageImages.flatMap((p) => p.losses));
|
|
27
|
+
const imagesByMcid = pageImages.map((p) => {
|
|
28
|
+
const byMcid = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const img of p.images) {
|
|
30
|
+
if (img.mcid === void 0) continue;
|
|
31
|
+
const list = byMcid.get(img.mcid);
|
|
32
|
+
if (list) list.push(img);
|
|
33
|
+
else byMcid.set(img.mcid, [img]);
|
|
34
|
+
}
|
|
13
35
|
return byMcid;
|
|
14
36
|
});
|
|
15
|
-
const
|
|
37
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
38
|
+
const imagesForNode = (node) => node.mcids.flatMap(({ page, mcid }) => imagesByMcid[page]?.get(mcid) ?? []);
|
|
39
|
+
const textOf = (node) => squash(node.mcids.map(({ page, mcid }) => runsOfMcid(page, mcid).map((r) => r.text).join("")).join(" "));
|
|
40
|
+
const spansOf = (node) => {
|
|
41
|
+
const spans = [];
|
|
42
|
+
node.mcids.forEach(({ page, mcid }, i) => {
|
|
43
|
+
if (i > 0) spans.push({ text: " " });
|
|
44
|
+
for (const run of runsOfMcid(page, mcid)) spans.push(run.href !== void 0 ? {
|
|
45
|
+
text: run.text,
|
|
46
|
+
href: run.href
|
|
47
|
+
} : { text: run.text });
|
|
48
|
+
});
|
|
49
|
+
return spans;
|
|
50
|
+
};
|
|
16
51
|
const collectText = (node) => squash([textOf(node), ...node.children.map(collectText)].join(" "));
|
|
17
52
|
function emit(node, out) {
|
|
18
53
|
if (node.type === "Table") {
|
|
@@ -20,14 +55,20 @@ function reconstructTaggedPdf(file) {
|
|
|
20
55
|
if (table) out.push(table);
|
|
21
56
|
return;
|
|
22
57
|
}
|
|
58
|
+
if (node.type === "Figure") {
|
|
59
|
+
for (const img of imagesForNode(node)) {
|
|
60
|
+
emitted.add(img);
|
|
61
|
+
out.push(imageBlock(img, resources, node.alt));
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
23
65
|
if (node.type === "LI") {
|
|
24
66
|
const text = collectText(node);
|
|
25
67
|
if (text.length > 0) out.push(paragraphBlock(text, void 0));
|
|
26
68
|
return;
|
|
27
69
|
}
|
|
28
70
|
if (node.children.length === 0) {
|
|
29
|
-
|
|
30
|
-
if (text.length > 0) out.push(paragraphBlock(text, headingLevel(node.type)));
|
|
71
|
+
if (textOf(node).length > 0) out.push(paragraphFromRuns(spansOf(node), headingLevel(node.type)));
|
|
31
72
|
return;
|
|
32
73
|
}
|
|
33
74
|
for (const child of node.children) emit(child, out);
|
|
@@ -74,8 +115,20 @@ function reconstructTaggedPdf(file) {
|
|
|
74
115
|
}
|
|
75
116
|
const body = [];
|
|
76
117
|
emit(root, body);
|
|
118
|
+
const orphans = [];
|
|
119
|
+
pageImages.forEach((p, page) => {
|
|
120
|
+
for (const img of p.images) if (!emitted.has(img)) orphans.push({
|
|
121
|
+
page,
|
|
122
|
+
img
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
orphans.sort((a, b) => a.page - b.page || b.img.y - a.img.y);
|
|
126
|
+
for (const { img } of orphans) body.push(imageBlock(img, resources));
|
|
77
127
|
if (body.length === 0) return void 0;
|
|
78
|
-
return
|
|
128
|
+
return {
|
|
129
|
+
doc: buildFlowDoc(body, resources),
|
|
130
|
+
losses: imageLosses
|
|
131
|
+
};
|
|
79
132
|
}
|
|
80
133
|
function squash(text) {
|
|
81
134
|
return text.replace(/\s+/g, " ").trim();
|
|
@@ -1,18 +1,99 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
|
|
2
|
+
import { IDENTITY, interpretContent, multiply } from "./content.js";
|
|
2
3
|
import { buildContentFont } from "./font.js";
|
|
3
4
|
//#region src/pdf-reader/text.ts
|
|
5
|
+
var MAX_FORM_DEPTH = 8;
|
|
4
6
|
function extractPageText(file, page) {
|
|
7
|
+
const runs = [];
|
|
8
|
+
collectRuns(file, page.resources, file.pageContent(page), IDENTITY, 0, /* @__PURE__ */ new Set(), runs);
|
|
9
|
+
const links = collectLinks(file, page);
|
|
10
|
+
if (links.length === 0) return runs;
|
|
11
|
+
return runs.map((run) => {
|
|
12
|
+
const link = links.find((l) => inRect(run.x, run.y, l.rect));
|
|
13
|
+
return link ? {
|
|
14
|
+
...run,
|
|
15
|
+
href: link.href
|
|
16
|
+
} : run;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function collectRuns(file, resources, content, baseCtm, depth, visiting, out) {
|
|
20
|
+
const result = interpretContent(content, buildFonts(file, resources), baseCtm);
|
|
21
|
+
out.push(...result.texts);
|
|
22
|
+
if (depth >= MAX_FORM_DEPTH || !resources) return;
|
|
23
|
+
const xobjects = file.get(resources, "XObject");
|
|
24
|
+
if (!(xobjects instanceof Map)) return;
|
|
25
|
+
for (const placement of result.images) {
|
|
26
|
+
const stream = file.resolve(xobjects.get(placement.name) ?? PDF_NULL);
|
|
27
|
+
if (!(stream instanceof PdfStream) || visiting.has(stream)) continue;
|
|
28
|
+
const sub = file.get(stream.dict, "Subtype");
|
|
29
|
+
if (!(sub instanceof PdfName) || sub.value !== "Form") continue;
|
|
30
|
+
visiting.add(stream);
|
|
31
|
+
const formRes = file.get(stream.dict, "Resources");
|
|
32
|
+
collectRuns(file, formRes instanceof Map ? formRes : resources, file.streamData(stream), multiply(matrixOf(file, stream.dict), placement.ctm), depth + 1, visiting, out);
|
|
33
|
+
visiting.delete(stream);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function buildFonts(file, resources) {
|
|
5
37
|
const fonts = /* @__PURE__ */ new Map();
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
38
|
+
if (!resources) return fonts;
|
|
39
|
+
const fontContainer = file.get(resources, "Font");
|
|
40
|
+
if (!(fontContainer instanceof Map)) return fonts;
|
|
41
|
+
for (const [fontName, fontRef] of fontContainer) {
|
|
42
|
+
const fontDict = file.resolve(fontRef);
|
|
43
|
+
if (fontDict instanceof Map) try {
|
|
44
|
+
fonts.set(fontName, buildContentFont(file, fontDict));
|
|
45
|
+
} catch {}
|
|
46
|
+
}
|
|
47
|
+
return fonts;
|
|
48
|
+
}
|
|
49
|
+
function matrixOf(file, dict) {
|
|
50
|
+
const m = file.resolve(dict.get("Matrix") ?? PDF_NULL);
|
|
51
|
+
if (Array.isArray(m) && m.length >= 6 && m.every((v) => typeof v === "number")) return [
|
|
52
|
+
m[0],
|
|
53
|
+
m[1],
|
|
54
|
+
m[2],
|
|
55
|
+
m[3],
|
|
56
|
+
m[4],
|
|
57
|
+
m[5]
|
|
58
|
+
];
|
|
59
|
+
return IDENTITY;
|
|
60
|
+
}
|
|
61
|
+
function collectLinks(file, page) {
|
|
62
|
+
const annots = file.get(page.dict, "Annots");
|
|
63
|
+
if (!Array.isArray(annots)) return [];
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const a of annots) {
|
|
66
|
+
const annot = file.resolve(a);
|
|
67
|
+
if (!(annot instanceof Map)) continue;
|
|
68
|
+
const sub = file.get(annot, "Subtype");
|
|
69
|
+
if (!(sub instanceof PdfName) || sub.value !== "Link") continue;
|
|
70
|
+
const rect = normRect(file.get(annot, "Rect"));
|
|
71
|
+
if (!rect) continue;
|
|
72
|
+
const action = file.get(annot, "A");
|
|
73
|
+
if (!(action instanceof Map)) continue;
|
|
74
|
+
const s = file.get(action, "S");
|
|
75
|
+
if (!(s instanceof PdfName) || s.value !== "URI") continue;
|
|
76
|
+
const uri = file.get(action, "URI");
|
|
77
|
+
if (typeof uri === "string" && uri.length > 0) out.push({
|
|
78
|
+
rect,
|
|
79
|
+
href: uri
|
|
80
|
+
});
|
|
14
81
|
}
|
|
15
|
-
return
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function normRect(v) {
|
|
85
|
+
if (!Array.isArray(v) || v.length < 4) return void 0;
|
|
86
|
+
const n = v.slice(0, 4).map((x) => typeof x === "number" ? x : NaN);
|
|
87
|
+
if (n.some((x) => !Number.isFinite(x))) return void 0;
|
|
88
|
+
return [
|
|
89
|
+
Math.min(n[0], n[2]),
|
|
90
|
+
Math.min(n[1], n[3]),
|
|
91
|
+
Math.max(n[0], n[2]),
|
|
92
|
+
Math.max(n[1], n[3])
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
function inRect(x, y, r) {
|
|
96
|
+
return x >= r[0] - 1 && x <= r[2] + 1 && y >= r[1] - 2 && y <= r[3] + 2;
|
|
16
97
|
}
|
|
17
98
|
//#endregion
|
|
18
99
|
export { extractPageText };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PathSeg } from './content.js';
|
|
2
|
+
import { ShapeGradient } from '../core/vector.js';
|
|
3
|
+
import { PdfFile, PdfPage } from './document.js';
|
|
4
|
+
export interface PdfVector {
|
|
5
|
+
readonly segs: ReadonlyArray<PathSeg>;
|
|
6
|
+
readonly fillHex?: string;
|
|
7
|
+
readonly gradient?: ShapeGradient;
|
|
8
|
+
readonly strokeHex?: string;
|
|
9
|
+
readonly lineWidth?: number;
|
|
10
|
+
readonly minX: number;
|
|
11
|
+
readonly minY: number;
|
|
12
|
+
readonly maxX: number;
|
|
13
|
+
readonly maxY: number;
|
|
14
|
+
readonly mcid?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function collectPageVectors(file: PdfFile, page: PdfPage): Array<PdfVector>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { IDENTITY, interpretContent } from "./content.js";
|
|
2
|
+
import { buildShadingMap } from "./shading.js";
|
|
3
|
+
//#region src/pdf-reader/vector.ts
|
|
4
|
+
var NO_FONTS = /* @__PURE__ */ new Map();
|
|
5
|
+
var MIN_SIDE = 2;
|
|
6
|
+
var MIN_AREA = 16;
|
|
7
|
+
var MIN_STROKE_LEN = 6;
|
|
8
|
+
var MAX_VECTORS = 2e3;
|
|
9
|
+
function collectPageVectors(file, page) {
|
|
10
|
+
const [px0, py0, px1, py1] = page.mediaBox;
|
|
11
|
+
const pageArea = Math.max(1, Math.abs((px1 - px0) * (py1 - py0)));
|
|
12
|
+
const shadings = buildShadingMap(file, page);
|
|
13
|
+
const out = [];
|
|
14
|
+
for (const v of interpretContent(file.pageContent(page), NO_FONTS, IDENTITY, shadings).vectors) {
|
|
15
|
+
if (out.length >= MAX_VECTORS) break;
|
|
16
|
+
const b = bbox(v.segs);
|
|
17
|
+
if (!b) continue;
|
|
18
|
+
const w = b.maxX - b.minX;
|
|
19
|
+
const h = b.maxY - b.minY;
|
|
20
|
+
const area = w * h;
|
|
21
|
+
const solidFill = v.fillHex !== void 0 && v.fillHex !== "FFFFFF";
|
|
22
|
+
const filled = (v.gradient !== void 0 || solidFill) && w >= MIN_SIDE && h >= MIN_SIDE && area >= MIN_AREA && area <= .85 * pageArea;
|
|
23
|
+
const stroked = v.strokeHex !== void 0 && v.strokeHex !== "FFFFFF" && Math.max(w, h) >= MIN_STROKE_LEN && area <= .85 * pageArea;
|
|
24
|
+
if (!filled && !stroked) continue;
|
|
25
|
+
out.push({
|
|
26
|
+
segs: v.segs,
|
|
27
|
+
...filled ? v.gradient ? { gradient: v.gradient } : v.fillHex !== void 0 ? { fillHex: v.fillHex } : {} : {},
|
|
28
|
+
...stroked ? {
|
|
29
|
+
strokeHex: v.strokeHex,
|
|
30
|
+
...v.lineWidth !== void 0 ? { lineWidth: v.lineWidth } : {}
|
|
31
|
+
} : {},
|
|
32
|
+
...b,
|
|
33
|
+
...v.mcid !== void 0 ? { mcid: v.mcid } : {}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function bbox(segs) {
|
|
39
|
+
let minX = Infinity;
|
|
40
|
+
let minY = Infinity;
|
|
41
|
+
let maxX = -Infinity;
|
|
42
|
+
let maxY = -Infinity;
|
|
43
|
+
const add = (x, y) => {
|
|
44
|
+
minX = Math.min(minX, x);
|
|
45
|
+
minY = Math.min(minY, y);
|
|
46
|
+
maxX = Math.max(maxX, x);
|
|
47
|
+
maxY = Math.max(maxY, y);
|
|
48
|
+
};
|
|
49
|
+
for (const s of segs) if (s.op === "move" || s.op === "line") add(s.x, s.y);
|
|
50
|
+
else if (s.op === "cubic") {
|
|
51
|
+
add(s.x1, s.y1);
|
|
52
|
+
add(s.x2, s.y2);
|
|
53
|
+
add(s.x, s.y);
|
|
54
|
+
}
|
|
55
|
+
return Number.isFinite(minX) ? {
|
|
56
|
+
minX,
|
|
57
|
+
minY,
|
|
58
|
+
maxX,
|
|
59
|
+
maxY
|
|
60
|
+
} : void 0;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { collectPageVectors };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FEATURES } from "../core/ir/features.js";
|
|
2
2
|
import { svgPathData } from "../core/vector.js";
|
|
3
|
+
import { gradientSvgDef } from "../core/drawingml/shape-render.js";
|
|
3
4
|
import { paintPlan } from "../layout/page-doc.js";
|
|
4
5
|
import { toBase64 } from "../core/bytes.js";
|
|
5
6
|
//#region src/svg/svg-writer.ts
|
|
@@ -12,10 +13,11 @@ function writeSvg(laid, opts = {}) {
|
|
|
12
13
|
const parts = [];
|
|
13
14
|
parts.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${fmt(width)}" height="${fmt(height)}" viewBox="0 0 ${fmt(width)} ${fmt(height)}">`);
|
|
14
15
|
let yOffset = 0;
|
|
16
|
+
const idc = { n: 0 };
|
|
15
17
|
laid.pages.forEach((page, i) => {
|
|
16
18
|
parts.push(`<g transform="translate(0 ${fmt(yOffset)})" data-page="${i + 1}">`);
|
|
17
19
|
parts.push(`<rect x="0" y="0" width="${fmt(page.width)}" height="${fmt(page.height)}" fill="#ffffff" stroke="#cccccc"/>`);
|
|
18
|
-
emitPage(parts, page, laid, losses);
|
|
20
|
+
emitPage(parts, page, laid, losses, idc);
|
|
19
21
|
parts.push("</g>");
|
|
20
22
|
yOffset += page.height + gap;
|
|
21
23
|
});
|
|
@@ -36,7 +38,7 @@ var svgWriter = {
|
|
|
36
38
|
]),
|
|
37
39
|
write: (doc, opts) => writeSvg(doc, opts ?? {})
|
|
38
40
|
};
|
|
39
|
-
function emitPage(out, page, laid, losses) {
|
|
41
|
+
function emitPage(out, page, laid, losses, idc) {
|
|
40
42
|
const plan = paintPlan(page.commands);
|
|
41
43
|
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}"/>`);
|
|
42
44
|
for (const img of plan.images) {
|
|
@@ -71,7 +73,7 @@ function emitPage(out, page, laid, losses) {
|
|
|
71
73
|
];
|
|
72
74
|
out.push(`<line x1="${fmt(ax)}" y1="${fmt(ay)}" x2="${fmt(bx)}" y2="${fmt(by)}" stroke="#${b.borderColorHex}" stroke-width="${fmt(b.borderSizePt)}"/>`);
|
|
73
75
|
}
|
|
74
|
-
for (const sh of plan.shapes) emitShape(out, sh.shape);
|
|
76
|
+
for (const sh of plan.shapes) emitShape(out, sh.shape, idc);
|
|
75
77
|
for (const t of plan.lines) emitTextLine(out, t, losses);
|
|
76
78
|
}
|
|
77
79
|
function emitTextLine(out, item, losses) {
|
|
@@ -95,10 +97,15 @@ function emitTextLine(out, item, losses) {
|
|
|
95
97
|
x += tok.widthPt;
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
|
-
function emitShape(out, shape) {
|
|
100
|
+
function emitShape(out, shape, idc) {
|
|
99
101
|
const [a, b, c, d, e, f] = shape.transform;
|
|
100
102
|
const transform = `matrix(${fmt(a)} ${fmt(b)} ${fmt(c)} ${fmt(d)} ${fmt(e)} ${fmt(f)})`;
|
|
101
|
-
|
|
103
|
+
let fill;
|
|
104
|
+
if (shape.fillGradient) {
|
|
105
|
+
const id = `grad${idc.n++}`;
|
|
106
|
+
out.push(gradientSvgDef(id, shape.fillGradient));
|
|
107
|
+
fill = `url(#${id})`;
|
|
108
|
+
} else fill = shape.fillColorHex ? `#${shape.fillColorHex}` : "none";
|
|
102
109
|
const stroke = shape.stroke ? ` stroke="#${shape.stroke.colorHex}" stroke-width="${fmt(shape.stroke.widthPt)}"` : "";
|
|
103
110
|
for (const path of shape.paths) {
|
|
104
111
|
const d2 = pathData(path.segments);
|
|
@@ -4,6 +4,8 @@ import { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties } f
|
|
|
4
4
|
import "../core/style-cascade/index.js";
|
|
5
5
|
import { buildOpcPackage } from "../core/opc/opc-writer.js";
|
|
6
6
|
import "../core/opc/index.js";
|
|
7
|
+
import { omathXml } from "./omml-serializer.js";
|
|
8
|
+
import { chartSpaceXml } from "../core/drawingml/chart-serializer.js";
|
|
7
9
|
//#region src/word/docx-writer.ts
|
|
8
10
|
var encoder = new TextEncoder();
|
|
9
11
|
var DEFAULT_RUN = resolveRunProperties({}, {}, EMPTY_STYLE_SHEET);
|
|
@@ -15,6 +17,14 @@ var REL_NUMBERING = "http://schemas.openxmlformats.org/officeDocument/2006/relat
|
|
|
15
17
|
var REL_HYPERLINK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
|
|
16
18
|
var REL_IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
17
19
|
var NUMBERING_PART = "word/numbering.xml";
|
|
20
|
+
var REL_FOOTNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
|
|
21
|
+
var REL_ENDNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes";
|
|
22
|
+
var FOOTNOTES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml";
|
|
23
|
+
var ENDNOTES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml";
|
|
24
|
+
var FOOTNOTES_PART = "word/footnotes.xml";
|
|
25
|
+
var ENDNOTES_PART = "word/endnotes.xml";
|
|
26
|
+
var REL_CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
|
|
27
|
+
var CHART_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
|
|
18
28
|
var EMU_PER_PT = 12700;
|
|
19
29
|
var RASTER_MEDIA = {
|
|
20
30
|
png: {
|
|
@@ -78,7 +88,10 @@ function writeDocx(flow) {
|
|
|
78
88
|
mediaParts: [],
|
|
79
89
|
mediaFileByResource: /* @__PURE__ */ new Map(),
|
|
80
90
|
bookmarkSeq: 0,
|
|
81
|
-
drawingSeq: 0
|
|
91
|
+
drawingSeq: 0,
|
|
92
|
+
chartParts: [],
|
|
93
|
+
chartSeq: 0,
|
|
94
|
+
...flow.charts ? { charts: flow.charts } : {}
|
|
82
95
|
};
|
|
83
96
|
const docScope = newScope();
|
|
84
97
|
const extraParts = [];
|
|
@@ -112,6 +125,24 @@ function writeDocx(flow) {
|
|
|
112
125
|
target: "numbering.xml",
|
|
113
126
|
targetMode: "Internal"
|
|
114
127
|
});
|
|
128
|
+
emitNotes(flow.footnotes, {
|
|
129
|
+
noteKind: "footnote",
|
|
130
|
+
partPath: FOOTNOTES_PART,
|
|
131
|
+
rootTag: "w:footnotes",
|
|
132
|
+
noteTag: "w:footnote",
|
|
133
|
+
contentType: FOOTNOTES_CONTENT_TYPE,
|
|
134
|
+
relType: REL_FOOTNOTES,
|
|
135
|
+
target: "footnotes.xml"
|
|
136
|
+
}, state, losses, docScope, extraParts, extraPartRels);
|
|
137
|
+
emitNotes(flow.endnotes, {
|
|
138
|
+
noteKind: "endnote",
|
|
139
|
+
partPath: ENDNOTES_PART,
|
|
140
|
+
rootTag: "w:endnotes",
|
|
141
|
+
noteTag: "w:endnote",
|
|
142
|
+
contentType: ENDNOTES_CONTENT_TYPE,
|
|
143
|
+
relType: REL_ENDNOTES,
|
|
144
|
+
target: "endnotes.xml"
|
|
145
|
+
}, state, losses, docScope, extraParts, extraPartRels);
|
|
115
146
|
const partRelationships = [...docScope.rels.length > 0 ? [{
|
|
116
147
|
sourcePart: "word/document.xml",
|
|
117
148
|
relationships: docScope.rels
|
|
@@ -126,6 +157,7 @@ function writeDocx(flow) {
|
|
|
126
157
|
},
|
|
127
158
|
...numberingPart ? [numberingPart] : [],
|
|
128
159
|
...extraParts,
|
|
160
|
+
...state.chartParts,
|
|
129
161
|
...state.mediaParts
|
|
130
162
|
],
|
|
131
163
|
rootRelationships: [{
|
|
@@ -139,6 +171,34 @@ function writeDocx(flow) {
|
|
|
139
171
|
losses
|
|
140
172
|
};
|
|
141
173
|
}
|
|
174
|
+
function emitNotes(notes, cfg, state, losses, docScope, extraParts, extraPartRels) {
|
|
175
|
+
if (!notes || notes.size === 0) return;
|
|
176
|
+
const scope = newScope();
|
|
177
|
+
scope.noteKind = cfg.noteKind;
|
|
178
|
+
const stub = (type, id, mark) => `<${cfg.noteTag} w:type="${type}" w:id="${id}"><w:p><w:r>${mark}</w:r></w:p></${cfg.noteTag}>`;
|
|
179
|
+
const noteXmls = [];
|
|
180
|
+
for (const [id, content] of notes) {
|
|
181
|
+
const inner = [];
|
|
182
|
+
for (const el of content) emitBlock(inner, el, losses, state, scope);
|
|
183
|
+
noteXmls.push(`<${cfg.noteTag} w:id="${escapeAttr(id)}">${inner.join("") || "<w:p/>"}</${cfg.noteTag}>`);
|
|
184
|
+
}
|
|
185
|
+
const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><${cfg.rootTag} xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">` + stub("separator", -1, "<w:separator/>") + stub("continuationSeparator", 0, "<w:continuationSeparator/>") + noteXmls.join("") + `</${cfg.rootTag}>`;
|
|
186
|
+
extraParts.push({
|
|
187
|
+
path: cfg.partPath,
|
|
188
|
+
data: encoder.encode(xml),
|
|
189
|
+
contentType: cfg.contentType
|
|
190
|
+
});
|
|
191
|
+
if (scope.rels.length > 0) extraPartRels.push({
|
|
192
|
+
sourcePart: cfg.partPath,
|
|
193
|
+
relationships: scope.rels
|
|
194
|
+
});
|
|
195
|
+
docScope.rels.push({
|
|
196
|
+
id: `rId${++docScope.relSeq}`,
|
|
197
|
+
type: cfg.relType,
|
|
198
|
+
target: cfg.target,
|
|
199
|
+
targetMode: "Internal"
|
|
200
|
+
});
|
|
201
|
+
}
|
|
142
202
|
function emitHeadersFooters(flow, section, state, docScope, extraParts, extraPartRels, hfCache, losses) {
|
|
143
203
|
const refs = {
|
|
144
204
|
headers: [],
|
|
@@ -226,12 +286,37 @@ function emitBlock(out, el, losses, state, scope, closingSectPr) {
|
|
|
226
286
|
out.push(`<w:p>${pPrWithSect(el.shape.paragraphProperties, closingSectPr)}<w:r>${drawing}</w:r></w:p>`);
|
|
227
287
|
return;
|
|
228
288
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
289
|
+
const chartDrawing = chartBlockXml(el.chart, state, scope, losses);
|
|
290
|
+
if (chartDrawing) out.push(`<w:p>${pPrWithSect(el.chart.paragraphProperties, closingSectPr)}<w:r>${chartDrawing}</w:r></w:p>`);
|
|
291
|
+
else if (closingSectPr) out.push(`<w:p><w:pPr>${closingSectPr}</w:pPr></w:p>`);
|
|
292
|
+
}
|
|
293
|
+
function chartBlockXml(chart, state, scope, losses) {
|
|
294
|
+
const data = state.charts?.get(chart.chartRelId);
|
|
295
|
+
if (!data) {
|
|
296
|
+
losses.push({
|
|
297
|
+
severity: "dropped",
|
|
298
|
+
feature: FEATURES.charts,
|
|
299
|
+
detail: "chart data missing"
|
|
300
|
+
});
|
|
301
|
+
return "";
|
|
302
|
+
}
|
|
303
|
+
const cid = ++state.chartSeq;
|
|
304
|
+
state.chartParts.push({
|
|
305
|
+
path: `word/charts/chart${cid}.xml`,
|
|
306
|
+
data: encoder.encode(chartSpaceXml(data)),
|
|
307
|
+
contentType: CHART_CONTENT_TYPE
|
|
234
308
|
});
|
|
309
|
+
const relId = `rId${++scope.relSeq}`;
|
|
310
|
+
scope.rels.push({
|
|
311
|
+
id: relId,
|
|
312
|
+
type: REL_CHART,
|
|
313
|
+
target: `charts/chart${cid}.xml`,
|
|
314
|
+
targetMode: "Internal"
|
|
315
|
+
});
|
|
316
|
+
const cx = Math.round(chart.width * EMU_PER_PT);
|
|
317
|
+
const cy = Math.round(chart.height * EMU_PER_PT);
|
|
318
|
+
const id = ++state.drawingSeq;
|
|
319
|
+
return `<w:drawing><wp:inline xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${id}" name="Chart ${id}"${chart.altText ? ` descr="${escapeAttr(chart.altText)}"` : ""}/><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart"><c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="${relId}"/></a:graphicData></a:graphic></wp:inline></w:drawing>`;
|
|
235
320
|
}
|
|
236
321
|
function drawingXml(resource, widthPt, heightPt, altText, state, scope) {
|
|
237
322
|
if (resource === void 0) return "";
|
|
@@ -274,7 +359,14 @@ function geomXml(g) {
|
|
|
274
359
|
return `<a:prstGeom prst="${escapeAttr(g.preset ?? "rect")}"><a:avLst>${gds}</a:avLst></a:prstGeom>`;
|
|
275
360
|
}
|
|
276
361
|
function fillXml(f) {
|
|
277
|
-
|
|
362
|
+
if (f.kind === "solid" && f.colorHex) return `<a:solidFill><a:srgbClr val="${f.colorHex}"/></a:solidFill>`;
|
|
363
|
+
if (f.kind === "gradient" && f.gradient) return gradFillXml(f.gradient);
|
|
364
|
+
return "<a:noFill/>";
|
|
365
|
+
}
|
|
366
|
+
function gradFillXml(g) {
|
|
367
|
+
return `<a:gradFill><a:gsLst>${g.stops.map((s) => {
|
|
368
|
+
return `<a:gs pos="${Math.round(Math.max(0, Math.min(1, s.offset)) * 1e5)}"><a:srgbClr val="${s.colorHex}"/></a:gs>`;
|
|
369
|
+
}).join("")}</a:gsLst>${g.kind === "radial" ? "<a:path path=\"circle\"/>" : `<a:lin ang="${Math.round(((g.angle ?? 0) % 360 + 360) % 360 * 6e4)}" scaled="1"/>`}</a:gradFill>`;
|
|
278
370
|
}
|
|
279
371
|
function lineXml(l) {
|
|
280
372
|
const w = l.width !== void 0 ? ` w="${Math.round(l.width * EMU_PER_PT)}"` : "";
|
|
@@ -402,7 +494,7 @@ function cellMarginsXml(tag, margins) {
|
|
|
402
494
|
return inner ? `<${tag}>${inner}</${tag}>` : "";
|
|
403
495
|
}
|
|
404
496
|
function paragraphXml(p, state, scope, closingSectPr) {
|
|
405
|
-
const visible = p.runs.filter((run) => !run.listMarker && run.math
|
|
497
|
+
const visible = p.runs.filter((run) => !run.listMarker && (run.math !== void 0 || run.text !== "" || run.inlineImage !== void 0 || run.pageBreak || run.footnoteRef !== void 0 || run.endnoteRef !== void 0 || run.noteNumber === true || run.href !== void 0 || run.anchor !== void 0));
|
|
406
498
|
const inner = [];
|
|
407
499
|
let i = 0;
|
|
408
500
|
while (i < visible.length) {
|
|
@@ -440,6 +532,7 @@ function hyperlinkXml(run, inner, _state, scope) {
|
|
|
440
532
|
return `<w:hyperlink w:anchor="${escapeAttr(run.anchor)}">${inner}</w:hyperlink>`;
|
|
441
533
|
}
|
|
442
534
|
function runXml(run, state, scope) {
|
|
535
|
+
if (run.math !== void 0) return `<m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">${omathXml(run.math)}</m:oMath>`;
|
|
443
536
|
const rPr = rPrXml(run.properties);
|
|
444
537
|
if (run.inlineImage !== void 0) {
|
|
445
538
|
const img = run.inlineImage;
|
|
@@ -447,6 +540,9 @@ function runXml(run, state, scope) {
|
|
|
447
540
|
if (drawing) return `<w:r>${rPr}${drawing}</w:r>`;
|
|
448
541
|
if (run.text === "" && !run.pageBreak) return "";
|
|
449
542
|
}
|
|
543
|
+
if (run.footnoteRef !== void 0) return `<w:r>${rPr}<w:footnoteReference w:id="${escapeAttr(run.footnoteRef)}"/></w:r>`;
|
|
544
|
+
if (run.endnoteRef !== void 0) return `<w:r>${rPr}<w:endnoteReference w:id="${escapeAttr(run.endnoteRef)}"/></w:r>`;
|
|
545
|
+
if (run.noteNumber) return `<w:r>${rPr}<${scope.noteKind === "endnote" ? "w:endnoteRef" : "w:footnoteRef"}/></w:r>`;
|
|
450
546
|
const brk = run.pageBreak ? "<w:br w:type=\"page\"/>" : "";
|
|
451
547
|
if (run.text === "") return brk ? `<w:r>${rPr}${brk}</w:r>` : "";
|
|
452
548
|
return `<w:r>${rPr}<w:t xml:space="preserve">${escapeXml(run.text)}</w:t>${brk}</w:r>`;
|
|
@@ -353,10 +353,10 @@ function parseFill(spPr, resolveColor) {
|
|
|
353
353
|
} : { kind: "none" };
|
|
354
354
|
}
|
|
355
355
|
if (poIs(child, "a:gradFill")) {
|
|
356
|
-
const
|
|
357
|
-
return
|
|
358
|
-
kind: "
|
|
359
|
-
|
|
356
|
+
const gradient = parseGradient(child, resolveColor);
|
|
357
|
+
return gradient ? {
|
|
358
|
+
kind: "gradient",
|
|
359
|
+
gradient
|
|
360
360
|
} : { kind: "none" };
|
|
361
361
|
}
|
|
362
362
|
}
|
|
@@ -404,13 +404,10 @@ function colorFromContainer(parent, resolveColor) {
|
|
|
404
404
|
if (hex) return hex;
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
|
-
function
|
|
407
|
+
function parseGradient(grad, resolveColor) {
|
|
408
408
|
const gsLst = poChildren(grad).find((c) => poIs(c, "a:gsLst"));
|
|
409
409
|
if (!gsLst) return void 0;
|
|
410
|
-
|
|
411
|
-
let g = 0;
|
|
412
|
-
let b = 0;
|
|
413
|
-
let n = 0;
|
|
410
|
+
const stops = [];
|
|
414
411
|
for (const gs of poChildren(gsLst)) {
|
|
415
412
|
if (!poIs(gs, "a:gs")) continue;
|
|
416
413
|
let hex;
|
|
@@ -419,15 +416,29 @@ function colorFromGradient(grad, resolveColor) {
|
|
|
419
416
|
if (hex) break;
|
|
420
417
|
}
|
|
421
418
|
if (!hex) continue;
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
419
|
+
const pos = poIntAttr(gs, "pos");
|
|
420
|
+
const offset = pos !== void 0 ? clampUnit(pos / 1e5) : stops.length === 0 ? 0 : 1;
|
|
421
|
+
stops.push({
|
|
422
|
+
offset,
|
|
423
|
+
colorHex: hex
|
|
424
|
+
});
|
|
427
425
|
}
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
426
|
+
if (stops.length === 0) return void 0;
|
|
427
|
+
stops.sort((a, b) => a.offset - b.offset);
|
|
428
|
+
if (poChildren(grad).some((c) => poIs(c, "a:path"))) return {
|
|
429
|
+
kind: "radial",
|
|
430
|
+
stops
|
|
431
|
+
};
|
|
432
|
+
const lin = poChildren(grad).find((c) => poIs(c, "a:lin"));
|
|
433
|
+
const ang = lin ? poIntAttr(lin, "ang") : void 0;
|
|
434
|
+
return {
|
|
435
|
+
kind: "linear",
|
|
436
|
+
angle: ang !== void 0 ? ang / 6e4 % 360 : 0,
|
|
437
|
+
stops
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function clampUnit(x) {
|
|
441
|
+
return x < 0 ? 0 : x > 1 ? 1 : x;
|
|
431
442
|
}
|
|
432
443
|
//#endregion
|
|
433
444
|
export { expandMcChildren, parseDrawing, parseVmlPicture };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/word/omml-serializer.ts
|
|
2
|
+
function omathXml(node) {
|
|
3
|
+
return ser(node);
|
|
4
|
+
}
|
|
5
|
+
function ser(node) {
|
|
6
|
+
switch (node.type) {
|
|
7
|
+
case "row": return node.children.map(ser).join("");
|
|
8
|
+
case "run": return `<m:r>${runPr(node)}<m:t xml:space="preserve">${esc(node.text)}</m:t></m:r>`;
|
|
9
|
+
case "fraction": return "<m:f>" + (node.barless ? "<m:fPr><m:type m:val=\"noBar\"/></m:fPr>" : "") + `<m:num>${ser(node.num)}</m:num><m:den>${ser(node.den)}</m:den></m:f>`;
|
|
10
|
+
case "script": return scriptXml(node);
|
|
11
|
+
case "radical": return "<m:rad>" + (node.degree ? "" : "<m:radPr><m:degHide m:val=\"1\"/></m:radPr>") + (node.degree ? `<m:deg>${ser(node.degree)}</m:deg>` : "<m:deg/>") + `<m:e>${ser(node.radicand)}</m:e></m:rad>`;
|
|
12
|
+
case "nary": return `<m:nary><m:naryPr><m:chr m:val="${attr(node.op)}"/>` + (node.limLoc ? `<m:limLoc m:val="${node.limLoc}"/>` : "") + (node.sub ? "" : "<m:subHide m:val=\"1\"/>") + (node.sup ? "" : "<m:supHide m:val=\"1\"/>") + `</m:naryPr><m:sub>${node.sub ? ser(node.sub) : ""}</m:sub><m:sup>${node.sup ? ser(node.sup) : ""}</m:sup><m:e>${ser(node.body)}</m:e></m:nary>`;
|
|
13
|
+
case "func": return `<m:func><m:fName>${ser(node.name)}</m:fName><m:e>${ser(node.body)}</m:e></m:func>`;
|
|
14
|
+
case "limit": return `<m:lim${node.pos === "upp" ? "Upp" : "Low"}><m:e>${ser(node.base)}</m:e><m:lim>${ser(node.lim)}</m:lim></m:lim${node.pos === "upp" ? "Upp" : "Low"}>`;
|
|
15
|
+
case "delimiter": return `<m:d><m:dPr><m:begChr m:val="${attr(node.begChr)}"/>` + (node.sepChr !== void 0 ? `<m:sepChr m:val="${attr(node.sepChr)}"/>` : "") + `<m:endChr m:val="${attr(node.endChr)}"/></m:dPr>` + node.children.map((c) => `<m:e>${ser(c)}</m:e>`).join("") + "</m:d>";
|
|
16
|
+
case "matrix": return "<m:m>" + node.rows.map((row) => `<m:mr>${row.map((c) => `<m:e>${ser(c)}</m:e>`).join("")}</m:mr>`).join("") + "</m:m>";
|
|
17
|
+
case "accent": return `<m:acc><m:accPr><m:chr m:val="${attr(node.char)}"/></m:accPr><m:e>${ser(node.base)}</m:e></m:acc>`;
|
|
18
|
+
case "bar": return `<m:bar><m:barPr><m:pos m:val="${node.pos}"/></m:barPr><m:e>${ser(node.base)}</m:e></m:bar>`;
|
|
19
|
+
case "groupChr": return `<m:groupChr><m:groupChrPr><m:chr m:val="${attr(node.char)}"/><m:pos m:val="${node.pos}"/></m:groupChrPr><m:e>${ser(node.base)}</m:e></m:groupChr>`;
|
|
20
|
+
case "eqArr": return `<m:eqArr>${node.rows.map((r) => `<m:e>${ser(r)}</m:e>`).join("")}</m:eqArr>`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function scriptXml(node) {
|
|
24
|
+
const base = `<m:e>${ser(node.base)}</m:e>`;
|
|
25
|
+
const sub = `<m:sub>${node.sub ? ser(node.sub) : ""}</m:sub>`;
|
|
26
|
+
const sup = `<m:sup>${node.sup ? ser(node.sup) : ""}</m:sup>`;
|
|
27
|
+
if (node.pre) return `<m:sPre>${base}${sub}${sup}</m:sPre>`;
|
|
28
|
+
if (node.sub && node.sup) return `<m:sSubSup>${base}${sub}${sup}</m:sSubSup>`;
|
|
29
|
+
if (node.sub) return `<m:sSub>${base}${sub}</m:sSub>`;
|
|
30
|
+
if (node.sup) return `<m:sSup>${base}${sup}</m:sSup>`;
|
|
31
|
+
return ser(node.base);
|
|
32
|
+
}
|
|
33
|
+
function runPr(run) {
|
|
34
|
+
let sty;
|
|
35
|
+
if (run.bold && run.italic) sty = "bi";
|
|
36
|
+
else if (run.bold) sty = "b";
|
|
37
|
+
else if (run.italic) sty = "i";
|
|
38
|
+
else if (run.nor) sty = "p";
|
|
39
|
+
return sty ? `<m:rPr><m:sty m:val="${sty}"/></m:rPr>` : "";
|
|
40
|
+
}
|
|
41
|
+
function esc(s) {
|
|
42
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
43
|
+
}
|
|
44
|
+
function attr(s) {
|
|
45
|
+
return esc(s).replace(/"/g, """);
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { omathXml };
|