reamkit 0.1.0-alpha.0 → 1.0.1
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 +107 -30
- package/dist/esm/{bidi → core/bidi}/algorithm.js +1 -1
- package/dist/esm/{bidi → core/bidi}/char-types.js +1 -1
- package/dist/esm/{bidi → core/bidi}/index.d.ts +2 -0
- package/dist/esm/{bidi → core/bidi}/index.js +2 -1
- package/dist/esm/core/bidi/segments.d.ts +6 -0
- package/dist/esm/core/bidi/segments.js +41 -0
- package/dist/esm/core/bytes.d.ts +1 -0
- package/dist/esm/core/bytes.js +11 -0
- package/dist/esm/core/converter/facade.d.ts +45 -0
- package/dist/esm/core/converter/facade.js +89 -0
- package/dist/esm/core/converter/index.d.ts +4 -0
- package/dist/esm/core/converter/project.d.ts +4 -0
- package/dist/esm/core/converter/project.js +17 -0
- package/dist/esm/core/converter/ream.d.ts +35 -0
- package/dist/esm/core/converter/ream.js +101 -0
- package/dist/esm/{crypto → core/crypto}/asn1.js +1 -1
- package/dist/esm/{crypto → core/crypto}/cms.js +1 -1
- package/dist/esm/core/document-model/index.d.ts +1 -0
- package/dist/esm/{document-model → core/document-model}/types.d.ts +57 -41
- package/dist/esm/{ooxml → core}/drawingml/chart-parser.d.ts +1 -1
- package/dist/esm/{ooxml → core}/drawingml/chart-parser.js +6 -29
- package/dist/esm/{ooxml → core}/drawingml/colors.d.ts +3 -0
- package/dist/esm/{ooxml → core}/drawingml/colors.js +32 -2
- package/dist/esm/{ooxml → core}/drawingml/theme-parser.js +2 -2
- package/dist/esm/{font → core/font}/arabic-joining.js +1 -1
- package/dist/esm/{font → core/font}/binary-reader.js +1 -1
- package/dist/esm/{font → core/font}/font-registry.d.ts +1 -0
- package/dist/esm/{font → core/font}/font-registry.js +17 -16
- package/dist/esm/{font → core/font}/index.d.ts +3 -0
- package/dist/esm/core/font/measure.d.ts +7 -0
- package/dist/esm/core/font/measure.js +30 -0
- package/dist/esm/{font → core/font}/opentype-layout.js +1 -1
- package/dist/esm/{font → core/font}/ttf-parser.js +1 -1
- package/dist/esm/{font → core/font}/ttf-subset.js +1 -1
- package/dist/esm/core/fonts/provider.d.ts +60 -0
- package/dist/esm/core/fonts/provider.js +163 -0
- package/dist/esm/{fonts → core/fonts}/remote-fonts.js +1 -1
- package/dist/esm/{hyphenation → core/hyphenation}/index.js +1 -1
- package/dist/esm/{hyphenation → core/hyphenation}/liang.js +1 -1
- package/dist/esm/{hyphenation → core/hyphenation}/patterns-en-us.js +1 -1
- package/dist/esm/{hyphenation → core/hyphenation}/patterns-ru.js +1 -1
- package/dist/esm/core/images.d.ts +17 -0
- package/dist/esm/core/images.js +308 -0
- package/dist/esm/core/ir/adapters.d.ts +37 -0
- package/dist/esm/core/ir/features.d.ts +25 -0
- package/dist/esm/core/ir/features.js +24 -0
- package/dist/esm/core/ir/flow.d.ts +24 -0
- package/dist/esm/core/ir/index.d.ts +11 -0
- package/dist/esm/core/ir/loss.d.ts +22 -0
- package/dist/esm/core/ir/loss.js +15 -0
- package/dist/esm/core/ir/resources.d.ts +16 -0
- package/dist/esm/core/ir/resources.js +50 -0
- package/dist/esm/core/ir/units.d.ts +16 -0
- package/dist/esm/core/ir/units.js +41 -0
- package/dist/esm/core/line-breaker/index.d.ts +2 -0
- package/dist/esm/{line-breaker → core/line-breaker}/knuth-plass.d.ts +1 -0
- package/dist/esm/{line-breaker → core/line-breaker}/knuth-plass.js +3 -3
- package/dist/esm/core/numbering/apply.d.ts +3 -0
- package/dist/esm/core/numbering/apply.js +62 -0
- package/dist/esm/core/numbering/index.d.ts +2 -0
- package/dist/esm/{numbering → core/numbering}/state.js +1 -1
- package/dist/esm/{opc → core/opc}/core-properties.js +1 -1
- package/dist/esm/{opc → core/opc}/package.d.ts +2 -1
- package/dist/esm/{opc → core/opc}/package.js +7 -3
- package/dist/esm/{opc → core/opc}/relationship-types.js +1 -1
- package/dist/esm/{opc → core/opc}/relationships.js +1 -1
- package/dist/esm/{ooxml/wordproc → core}/po-helpers.js +1 -1
- package/dist/esm/core/style-cascade/index.d.ts +3 -0
- package/dist/esm/core/style-cascade/index.js +2 -0
- package/dist/esm/core/style-cascade/resolver.d.ts +7 -0
- package/dist/esm/{style-cascade → core/style-cascade}/resolver.js +82 -10
- package/dist/esm/{style-cascade → core/style-cascade}/types.d.ts +10 -8
- package/dist/esm/{style-cascade → core/style-cascade}/types.js +9 -8
- package/dist/esm/core/vector.d.ts +45 -0
- package/dist/esm/core/vector.js +48 -0
- package/dist/esm/{ooxml/spreadsheet → excel}/cell-reference.js +1 -1
- package/dist/esm/{ooxml/spreadsheet → excel}/defined-name-ref.js +1 -1
- package/dist/esm/{ooxml/spreadsheet → excel}/index.d.ts +1 -1
- package/dist/esm/{ooxml/spreadsheet → excel}/number-format.js +1 -1
- package/dist/esm/excel/print-model.d.ts +18 -0
- package/dist/esm/{converter/xlsx-to-pdf.js → excel/print-model.js} +39 -148
- package/dist/esm/{ooxml/spreadsheet → excel}/shared-strings-parser.js +1 -1
- package/dist/esm/{ooxml/spreadsheet → excel}/styles-parser.js +1 -1
- package/dist/esm/{ooxml/spreadsheet → excel}/workbook-parser.js +1 -1
- package/dist/esm/{ooxml/spreadsheet → excel}/worksheet-parser.js +1 -1
- package/dist/esm/excel/xlsx-reader.d.ts +4 -0
- package/dist/esm/excel/xlsx-reader.js +91 -0
- package/dist/esm/{converter → excel}/xlsx-to-pdf.d.ts +13 -2
- package/dist/esm/excel/xlsx-to-pdf.js +69 -0
- package/dist/esm/index.d.ts +23 -8
- package/dist/esm/index.js +18 -10
- package/dist/esm/{pdf → layout}/arc-to-bezier.d.ts +1 -1
- package/dist/esm/{pdf → layout}/arc-to-bezier.js +2 -2
- package/dist/esm/{pdf → layout}/chart-geometry.d.ts +1 -1
- package/dist/esm/{pdf → layout}/math-layout.d.ts +2 -2
- package/dist/esm/{pdf → layout}/math-layout.js +1 -1
- package/dist/esm/layout/page-doc.d.ts +133 -0
- package/dist/esm/layout/page-doc.js +38 -0
- package/dist/esm/{pdf → layout}/preset-geometry.d.ts +2 -2
- package/dist/esm/{pdf → layout}/preset-geometry.js +2 -2
- package/dist/esm/layout/styled-layout.d.ts +84 -0
- package/dist/esm/layout/styled-layout.js +1729 -0
- package/dist/esm/pdf/cid-font.d.ts +1 -1
- package/dist/esm/pdf/cid-font.js +6 -21
- package/dist/esm/pdf/image-xobject.d.ts +4 -5
- package/dist/esm/pdf/image-xobject.js +21 -324
- package/dist/esm/pdf/index.d.ts +2 -2
- package/dist/esm/pdf/index.js +1 -0
- package/dist/esm/pdf/signature.js +4 -4
- package/dist/esm/pdf/styled-page-emitter.d.ts +11 -0
- package/dist/esm/pdf/styled-page-emitter.js +557 -0
- package/dist/esm/pdf/styled-page-renderer.d.ts +4 -42
- package/dist/esm/pdf/styled-page-renderer.js +4 -2311
- package/dist/esm/pdf/text-page-renderer.d.ts +1 -1
- package/dist/esm/pdf/vector-graphics.d.ts +3 -45
- package/dist/esm/pdf/vector-graphics.js +2 -46
- package/dist/esm/svg/svg-writer.d.ts +8 -0
- package/dist/esm/svg/svg-writer.js +137 -0
- package/dist/esm/word/document-parser.d.ts +16 -0
- package/dist/esm/{ooxml/wordproc → word}/document-parser.js +55 -47
- package/dist/esm/word/docx-reader.d.ts +4 -0
- package/dist/esm/word/docx-reader.js +191 -0
- package/dist/esm/word/docx-to-pdf.d.ts +34 -0
- package/dist/esm/word/docx-to-pdf.js +112 -0
- package/dist/esm/{ooxml/wordproc → word}/drawing-parser.d.ts +10 -9
- package/dist/esm/{ooxml/wordproc → word}/drawing-parser.js +15 -42
- package/dist/esm/{ooxml/wordproc → word}/font-table.d.ts +2 -2
- package/dist/esm/{ooxml/wordproc → word}/font-table.js +2 -2
- package/dist/esm/{ooxml/wordproc → word}/index.d.ts +3 -2
- package/dist/esm/{ooxml/wordproc → word}/numbering-parser.d.ts +1 -1
- package/dist/esm/{ooxml/wordproc → word}/numbering-parser.js +1 -1
- package/dist/esm/{ooxml/math → word}/omml-parser.d.ts +2 -2
- package/dist/esm/{ooxml/math → word}/omml-parser.js +2 -2
- package/dist/esm/{ooxml/wordproc → word}/paragraph-properties.d.ts +1 -1
- package/dist/esm/{ooxml/wordproc → word}/paragraph-properties.js +9 -8
- package/dist/esm/{ooxml/wordproc → word}/po-to-flat.d.ts +1 -1
- package/dist/esm/{ooxml/wordproc → word}/po-to-flat.js +2 -2
- package/dist/esm/{ooxml/wordproc → word}/run-properties.d.ts +1 -1
- package/dist/esm/{ooxml/wordproc → word}/run-properties.js +3 -2
- package/dist/esm/{ooxml/wordproc → word}/settings-parser.js +2 -2
- package/dist/esm/word/styles-parser.d.ts +2 -0
- package/dist/esm/{ooxml/wordproc → word}/styles-parser.js +4 -7
- package/dist/esm/word/table-parser.d.ts +4 -0
- package/dist/esm/{ooxml/wordproc → word}/table-parser.js +80 -26
- package/dist/esm/{ooxml/wordproc → word}/xml-helpers.js +1 -1
- package/package.json +5 -6
- package/dist/esm/converter/docx-to-pdf.d.ts +0 -15
- package/dist/esm/converter/docx-to-pdf.js +0 -227
- package/dist/esm/converter/index.d.ts +0 -4
- package/dist/esm/converter/index.js +0 -2
- package/dist/esm/document-model/index.d.ts +0 -1
- package/dist/esm/line-breaker/index.d.ts +0 -2
- package/dist/esm/numbering/index.d.ts +0 -1
- package/dist/esm/ooxml/index.d.ts +0 -1
- package/dist/esm/ooxml/wordproc/document-parser.d.ts +0 -9
- package/dist/esm/ooxml/wordproc/styles-parser.d.ts +0 -3
- package/dist/esm/ooxml/wordproc/table-parser.d.ts +0 -3
- package/dist/esm/style-cascade/index.d.ts +0 -3
- package/dist/esm/style-cascade/resolver.d.ts +0 -4
- /package/dist/esm/{bidi → core/bidi}/algorithm.d.ts +0 -0
- /package/dist/esm/{bidi → core/bidi}/char-types.d.ts +0 -0
- /package/dist/esm/{crypto → core/crypto}/asn1.d.ts +0 -0
- /package/dist/esm/{crypto → core/crypto}/cms.d.ts +0 -0
- /package/dist/esm/{crypto → core/crypto}/index.d.ts +0 -0
- /package/dist/esm/{crypto → core/crypto}/index.js +0 -0
- /package/dist/esm/{document-model → core/document-model}/index.js +0 -0
- /package/dist/esm/{ooxml → core}/drawingml/theme-parser.d.ts +0 -0
- /package/dist/esm/{font → core/font}/arabic-joining.d.ts +0 -0
- /package/dist/esm/{font → core/font}/binary-reader.d.ts +0 -0
- /package/dist/esm/{font → core/font}/opentype-layout.d.ts +0 -0
- /package/dist/esm/{font → core/font}/ttf-parser.d.ts +0 -0
- /package/dist/esm/{font → core/font}/ttf-subset.d.ts +0 -0
- /package/dist/esm/{fonts → core/fonts}/index.d.ts +0 -0
- /package/dist/esm/{fonts → core/fonts}/remote-fonts.d.ts +0 -0
- /package/dist/esm/{hyphenation → core/hyphenation}/index.d.ts +0 -0
- /package/dist/esm/{hyphenation → core/hyphenation}/liang.d.ts +0 -0
- /package/dist/esm/{hyphenation → core/hyphenation}/patterns-en-us.d.ts +0 -0
- /package/dist/esm/{hyphenation → core/hyphenation}/patterns-ru.d.ts +0 -0
- /package/dist/esm/{numbering → core/numbering}/state.d.ts +0 -0
- /package/dist/esm/{opc → core/opc}/core-properties.d.ts +0 -0
- /package/dist/esm/{opc → core/opc}/index.d.ts +0 -0
- /package/dist/esm/{opc → core/opc}/index.js +0 -0
- /package/dist/esm/{opc → core/opc}/relationship-types.d.ts +0 -0
- /package/dist/esm/{opc → core/opc}/relationships.d.ts +0 -0
- /package/dist/esm/{ooxml/wordproc → core}/po-helpers.d.ts +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/cell-reference.d.ts +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/defined-name-ref.d.ts +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/index.js +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/number-format.d.ts +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/shared-strings-parser.d.ts +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/styles-parser.d.ts +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/workbook-parser.d.ts +0 -0
- /package/dist/esm/{ooxml/spreadsheet → excel}/worksheet-parser.d.ts +0 -0
- /package/dist/esm/{pdf → layout}/chart-geometry.js +0 -0
- /package/dist/esm/{ooxml/wordproc → word}/index.js +0 -0
- /package/dist/esm/{ooxml/wordproc → word}/settings-parser.d.ts +0 -0
- /package/dist/esm/{ooxml/wordproc → word}/text-extractor.d.ts +0 -0
- /package/dist/esm/{ooxml/wordproc → word}/text-extractor.js +0 -0
- /package/dist/esm/{ooxml/wordproc → word}/xml-helpers.d.ts +0 -0
|
@@ -1,2317 +1,10 @@
|
|
|
1
|
-
import { shapeText } from "../font/opentype-layout.js";
|
|
2
|
-
import { resolveFamilyKey } from "../fonts/remote-fonts.js";
|
|
3
|
-
import { dict, name, ref, stream, unicodeString } from "./objects.js";
|
|
4
|
-
import { embedTtfFont } from "./cid-font.js";
|
|
5
1
|
import { PdfDocument } from "./writer.js";
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import { NumberingState } from "../numbering/state.js";
|
|
10
|
-
import { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN } from "../style-cascade/types.js";
|
|
11
|
-
import { resolveParagraphProperties, resolveRunProperties } from "../style-cascade/resolver.js";
|
|
12
|
-
import { PathBuilder, emitVectorShape } from "./vector-graphics.js";
|
|
13
|
-
import { arcPoint, arcToBeziers } from "./arc-to-bezier.js";
|
|
14
|
-
import { buildChartScene } from "./chart-geometry.js";
|
|
15
|
-
import { buildSrgbIccProfile } from "./icc-profile.js";
|
|
16
|
-
import { embedImage } from "./image-xobject.js";
|
|
17
|
-
import { layoutMath, mathGlyphSegments, variantStyle } from "./math-layout.js";
|
|
18
|
-
import { customPaths, presetPaths, rectPath } from "./preset-geometry.js";
|
|
19
|
-
import { embedAssociatedFile } from "./embedded-file.js";
|
|
20
|
-
import { StructTreeBuilder } from "./struct-tree.js";
|
|
21
|
-
import { addSignaturePlaceholder } from "./signature.js";
|
|
22
|
-
import { buildXmpPacket } from "./xmp.js";
|
|
2
|
+
import { layoutStyledDocument } from "../layout/styled-layout.js";
|
|
3
|
+
import "../layout/page-doc.js";
|
|
4
|
+
import { emitStyledPdf } from "./styled-page-emitter.js";
|
|
23
5
|
//#region src/pdf/styled-page-renderer.ts
|
|
24
|
-
function parsePdfAProfile(pdfA) {
|
|
25
|
-
const part = Number(pdfA.charAt(6));
|
|
26
|
-
const level = pdfA.charAt(7);
|
|
27
|
-
return {
|
|
28
|
-
part,
|
|
29
|
-
level,
|
|
30
|
-
tagged: level === "a",
|
|
31
|
-
version: part === 1 ? "1.4" : "1.7"
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
var A4_WIDTH = 595;
|
|
35
|
-
var A4_HEIGHT = 842;
|
|
36
|
-
var TWIP_TO_PT = 1 / 20;
|
|
37
|
-
var EIGHTH_PT = 1 / 8;
|
|
38
|
-
var DEFAULT_CELL_PADDING_TWIPS = 108;
|
|
39
|
-
var DEFAULT_BORDER_SIZE_EIGHTH = 4;
|
|
40
|
-
var encoder = new TextEncoder();
|
|
41
6
|
function renderStyledPdf(body, options) {
|
|
42
|
-
|
|
43
|
-
const numberedBody = applyNumbering(body, options.numbering);
|
|
44
|
-
const numberedHeadersFooters = applyNumberingToHeadersFooters(options.headersFooters, options.numbering);
|
|
45
|
-
const doc = new PdfDocument();
|
|
46
|
-
const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
|
|
47
|
-
const tagged = options.tagged === true || (pdfaProfile?.tagged ?? false);
|
|
48
|
-
const structBuilder = tagged ? new StructTreeBuilder() : void 0;
|
|
49
|
-
const fontResources = embedUsedFonts(doc, numberedBody, numberedHeadersFooters, options);
|
|
50
|
-
const imageResources = embedUsedImages(doc, numberedBody, numberedHeadersFooters, options);
|
|
51
|
-
const sectionCtxs = sectionList.map((s) => buildSectionContext(s, options, numberedHeadersFooters, fontResources));
|
|
52
|
-
let sectionIdx = 0;
|
|
53
|
-
const renderedPages = paginateSections(numberedBody.map((el, idx) => {
|
|
54
|
-
while (sectionIdx < sectionCtxs.length - 1 && idx >= sectionCtxs[sectionIdx].endIndex) sectionIdx++;
|
|
55
|
-
const ctx = sectionCtxs[sectionIdx];
|
|
56
|
-
return layoutBodyElement(el, options, fontResources, imageResources, ctx.contentWidth, ctx.pageContentHeight);
|
|
57
|
-
}), sectionCtxs, structBuilder, options.language ?? "en-US");
|
|
58
|
-
const pagesDict = dict({
|
|
59
|
-
Type: name("Pages"),
|
|
60
|
-
Count: 0,
|
|
61
|
-
Kids: []
|
|
62
|
-
});
|
|
63
|
-
const pagesRef = doc.add(pagesDict);
|
|
64
|
-
let firstPageDict;
|
|
65
|
-
const fontResourceDict = buildFontResourceDict(fontResources);
|
|
66
|
-
const xobjectResourceDict = buildXObjectResourceDict(imageResources);
|
|
67
|
-
const resourcesDict = dict({
|
|
68
|
-
Font: fontResourceDict,
|
|
69
|
-
...xobjectResourceDict ? { XObject: xobjectResourceDict } : {}
|
|
70
|
-
});
|
|
71
|
-
let pdfaIccRef;
|
|
72
|
-
let transparencyGroup;
|
|
73
|
-
if (pdfaProfile) {
|
|
74
|
-
pdfaIccRef = doc.add(stream({
|
|
75
|
-
N: 3,
|
|
76
|
-
Alternate: name("DeviceRGB")
|
|
77
|
-
}, buildSrgbIccProfile()));
|
|
78
|
-
if (pdfaProfile.part >= 2) transparencyGroup = dict({
|
|
79
|
-
S: name("Transparency"),
|
|
80
|
-
CS: [name("ICCBased"), ref(pdfaIccRef.id)]
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
const pageRefs = [];
|
|
84
|
-
renderedPages.forEach((page, pageIndex) => {
|
|
85
|
-
let pageTagging;
|
|
86
|
-
if (structBuilder) {
|
|
87
|
-
const builder = structBuilder;
|
|
88
|
-
pageTagging = {
|
|
89
|
-
next: 0,
|
|
90
|
-
assigned: false,
|
|
91
|
-
record: (structId, mcid) => builder.addMcref(structId, pageIndex, mcid),
|
|
92
|
-
tagFor: (structId) => builder.node(structId).type
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
const contentBytes = emitPageContent(page.commands, pageTagging);
|
|
96
|
-
const contentsRef = doc.add(stream({}, contentBytes));
|
|
97
|
-
const pageEntries = {
|
|
98
|
-
Type: name("Page"),
|
|
99
|
-
Parent: ref(pagesRef.id),
|
|
100
|
-
MediaBox: [
|
|
101
|
-
0,
|
|
102
|
-
0,
|
|
103
|
-
page.width,
|
|
104
|
-
page.height
|
|
105
|
-
],
|
|
106
|
-
Resources: resourcesDict,
|
|
107
|
-
Contents: ref(contentsRef.id)
|
|
108
|
-
};
|
|
109
|
-
if (transparencyGroup) pageEntries["Group"] = transparencyGroup;
|
|
110
|
-
if (pageTagging?.assigned) {
|
|
111
|
-
pageEntries["StructParents"] = pageIndex;
|
|
112
|
-
pageEntries["Tabs"] = name("S");
|
|
113
|
-
}
|
|
114
|
-
const pageDictObj = dict(pageEntries);
|
|
115
|
-
if (firstPageDict === void 0) firstPageDict = pageDictObj;
|
|
116
|
-
pageRefs.push(doc.add(pageDictObj));
|
|
117
|
-
});
|
|
118
|
-
if (pageRefs.length === 0) {
|
|
119
|
-
const fallback = sectionCtxs[0] ?? defaultPageCtx();
|
|
120
|
-
const contentsRef = doc.add(stream({}, new Uint8Array(0)));
|
|
121
|
-
const fallbackDict = dict({
|
|
122
|
-
Type: name("Page"),
|
|
123
|
-
Parent: ref(pagesRef.id),
|
|
124
|
-
MediaBox: [
|
|
125
|
-
0,
|
|
126
|
-
0,
|
|
127
|
-
fallback.pageWidth,
|
|
128
|
-
fallback.pageHeight
|
|
129
|
-
],
|
|
130
|
-
Resources: resourcesDict,
|
|
131
|
-
Contents: ref(contentsRef.id),
|
|
132
|
-
...transparencyGroup ? { Group: transparencyGroup } : {}
|
|
133
|
-
});
|
|
134
|
-
firstPageDict = fallbackDict;
|
|
135
|
-
pageRefs.push(doc.add(fallbackDict));
|
|
136
|
-
}
|
|
137
|
-
pagesDict.set("Count", pageRefs.length);
|
|
138
|
-
pagesDict.set("Kids", pageRefs);
|
|
139
|
-
const catalogEntries = {
|
|
140
|
-
Type: name("Catalog"),
|
|
141
|
-
Pages: ref(pagesRef.id)
|
|
142
|
-
};
|
|
143
|
-
if (pdfaProfile) {
|
|
144
|
-
catalogEntries["OutputIntents"] = [ref(doc.add(dict({
|
|
145
|
-
Type: name("OutputIntent"),
|
|
146
|
-
S: name("GTS_PDFA1"),
|
|
147
|
-
OutputConditionIdentifier: "sRGB",
|
|
148
|
-
Info: "sRGB IEC61966-2.1",
|
|
149
|
-
DestOutputProfile: ref(pdfaIccRef.id)
|
|
150
|
-
})).id)];
|
|
151
|
-
catalogEntries["Metadata"] = ref(doc.add(stream({
|
|
152
|
-
Type: name("Metadata"),
|
|
153
|
-
Subtype: name("XML")
|
|
154
|
-
}, buildXmpPacket(xmpFromInfo(options.info, options.pdfA)))).id);
|
|
155
|
-
}
|
|
156
|
-
if (tagged && structBuilder) {
|
|
157
|
-
const structRootRef = structBuilder.emit(doc, pageRefs);
|
|
158
|
-
catalogEntries["MarkInfo"] = dict({ Marked: true });
|
|
159
|
-
catalogEntries["StructTreeRoot"] = ref(structRootRef.id);
|
|
160
|
-
catalogEntries["Lang"] = options.language ?? "en-US";
|
|
161
|
-
if (options.info?.title) catalogEntries["ViewerPreferences"] = dict({ DisplayDocTitle: true });
|
|
162
|
-
}
|
|
163
|
-
if (options.attachments?.length && (!pdfaProfile || pdfaProfile.part === 3)) {
|
|
164
|
-
const embedded = options.attachments.map((file) => ({
|
|
165
|
-
file,
|
|
166
|
-
ref: embedAssociatedFile(doc, file)
|
|
167
|
-
}));
|
|
168
|
-
catalogEntries["AF"] = embedded.map((e) => ref(e.ref.id));
|
|
169
|
-
const sorted = [...embedded].sort((a, b) => a.file.name < b.file.name ? -1 : a.file.name > b.file.name ? 1 : 0);
|
|
170
|
-
const names = [];
|
|
171
|
-
for (const e of sorted) names.push(e.file.name, ref(e.ref.id));
|
|
172
|
-
catalogEntries["Names"] = dict({ EmbeddedFiles: ref(doc.add(dict({ Names: names })).id) });
|
|
173
|
-
}
|
|
174
|
-
if (options.signaturePlaceholder && firstPageDict) {
|
|
175
|
-
const { fieldRef, acroForm } = addSignaturePlaceholder(doc, options.signaturePlaceholder, pageRefs[0]);
|
|
176
|
-
firstPageDict.set("Annots", [ref(fieldRef.id)]);
|
|
177
|
-
catalogEntries["AcroForm"] = acroForm;
|
|
178
|
-
}
|
|
179
|
-
const catalogRef = doc.add(dict(catalogEntries));
|
|
180
|
-
const infoRef = buildInfoDict(doc, options.info);
|
|
181
|
-
return doc.build(catalogRef, infoRef, { ...pdfaProfile ? {
|
|
182
|
-
version: pdfaProfile.version,
|
|
183
|
-
id: true
|
|
184
|
-
} : {} });
|
|
185
|
-
}
|
|
186
|
-
var DEFAULT_PRODUCER = "Ream";
|
|
187
|
-
function xmpFromInfo(info, pdfA) {
|
|
188
|
-
const p = pdfA ? parsePdfAProfile(pdfA) : void 0;
|
|
189
|
-
return {
|
|
190
|
-
pdfaPart: p ? String(p.part) : "1",
|
|
191
|
-
pdfaConformance: p ? p.level.toUpperCase() : "B",
|
|
192
|
-
producer: info?.producer ?? DEFAULT_PRODUCER,
|
|
193
|
-
...info?.title ? { title: info.title } : {},
|
|
194
|
-
...info?.author ? { author: info.author } : {},
|
|
195
|
-
...info?.subject ? { subject: info.subject } : {},
|
|
196
|
-
...info?.keywords ? { keywords: info.keywords } : {},
|
|
197
|
-
...info?.creator ? { creator: info.creator } : {},
|
|
198
|
-
...info?.creationDate ? { createDate: info.creationDate } : {},
|
|
199
|
-
...info?.modificationDate ? { modifyDate: info.modificationDate } : {}
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
function buildInfoDict(doc, info) {
|
|
203
|
-
const merged = {
|
|
204
|
-
...info,
|
|
205
|
-
producer: info?.producer ?? DEFAULT_PRODUCER
|
|
206
|
-
};
|
|
207
|
-
const entries = {};
|
|
208
|
-
if (merged.title) entries["Title"] = unicodeString(merged.title);
|
|
209
|
-
if (merged.author) entries["Author"] = unicodeString(merged.author);
|
|
210
|
-
if (merged.subject) entries["Subject"] = unicodeString(merged.subject);
|
|
211
|
-
if (merged.keywords) entries["Keywords"] = unicodeString(merged.keywords);
|
|
212
|
-
if (merged.creator) entries["Creator"] = unicodeString(merged.creator);
|
|
213
|
-
entries["Producer"] = unicodeString(merged.producer);
|
|
214
|
-
if (merged.creationDate) entries["CreationDate"] = formatPdfDate(merged.creationDate);
|
|
215
|
-
if (merged.modificationDate) entries["ModDate"] = formatPdfDate(merged.modificationDate);
|
|
216
|
-
return doc.add(dict(entries));
|
|
217
|
-
}
|
|
218
|
-
function formatPdfDate(d) {
|
|
219
|
-
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
220
|
-
return `D:${pad(d.getUTCFullYear(), 4)}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}Z`;
|
|
221
|
-
}
|
|
222
|
-
function resolvePageDimensions(options, section) {
|
|
223
|
-
const sectionPageWidth = section?.pageSize !== void 0 ? section.pageSize.widthTwips * TWIP_TO_PT : void 0;
|
|
224
|
-
const sectionPageHeight = section?.pageSize !== void 0 ? section.pageSize.heightTwips * TWIP_TO_PT : void 0;
|
|
225
|
-
const sectionLeft = section?.margins?.leftTwips !== void 0 ? section.margins.leftTwips * TWIP_TO_PT : void 0;
|
|
226
|
-
const sectionRight = section?.margins?.rightTwips !== void 0 ? section.margins.rightTwips * TWIP_TO_PT : void 0;
|
|
227
|
-
const sectionTop = section?.margins?.topTwips !== void 0 ? section.margins.topTwips * TWIP_TO_PT : void 0;
|
|
228
|
-
const sectionBottom = section?.margins?.bottomTwips !== void 0 ? section.margins.bottomTwips * TWIP_TO_PT : void 0;
|
|
229
|
-
const headerOffsetPt = (section?.margins?.headerTwips ?? 720) * TWIP_TO_PT;
|
|
230
|
-
const footerOffsetPt = (section?.margins?.footerTwips ?? 720) * TWIP_TO_PT;
|
|
231
|
-
return {
|
|
232
|
-
pageWidth: options.pageWidth ?? sectionPageWidth ?? A4_WIDTH,
|
|
233
|
-
pageHeight: options.pageHeight ?? sectionPageHeight ?? A4_HEIGHT,
|
|
234
|
-
marginLeft: options.marginLeft ?? sectionLeft ?? 72,
|
|
235
|
-
marginRight: options.marginRight ?? sectionRight ?? 72,
|
|
236
|
-
marginTop: options.marginTop ?? sectionTop ?? 72,
|
|
237
|
-
marginBottom: options.marginBottom ?? sectionBottom ?? 72,
|
|
238
|
-
headerOffsetPt,
|
|
239
|
-
footerOffsetPt
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
function defaultPageCtx() {
|
|
243
|
-
return {
|
|
244
|
-
endIndex: 0,
|
|
245
|
-
properties: {
|
|
246
|
-
headers: [],
|
|
247
|
-
footers: []
|
|
248
|
-
},
|
|
249
|
-
pageWidth: A4_WIDTH,
|
|
250
|
-
pageHeight: A4_HEIGHT,
|
|
251
|
-
marginLeft: 72,
|
|
252
|
-
marginTop: 72,
|
|
253
|
-
marginBottom: 72,
|
|
254
|
-
contentWidth: A4_WIDTH - 144,
|
|
255
|
-
pageContentHeight: A4_HEIGHT - 144,
|
|
256
|
-
headerSet: {
|
|
257
|
-
default: [],
|
|
258
|
-
first: [],
|
|
259
|
-
even: []
|
|
260
|
-
},
|
|
261
|
-
footerSet: {
|
|
262
|
-
default: [],
|
|
263
|
-
first: [],
|
|
264
|
-
even: []
|
|
265
|
-
},
|
|
266
|
-
titlePg: false,
|
|
267
|
-
evenAndOddHeaders: false
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
function resolveSectionList(body, options) {
|
|
271
|
-
if (options.sections && options.sections.length > 0) return options.sections;
|
|
272
|
-
if (options.section) return [{
|
|
273
|
-
properties: options.section,
|
|
274
|
-
endIndex: body.length
|
|
275
|
-
}];
|
|
276
|
-
return [{
|
|
277
|
-
properties: {
|
|
278
|
-
headers: [],
|
|
279
|
-
footers: []
|
|
280
|
-
},
|
|
281
|
-
endIndex: body.length
|
|
282
|
-
}];
|
|
283
|
-
}
|
|
284
|
-
function buildSectionContext(section, options, headersFooters, fontResources) {
|
|
285
|
-
const dims = resolvePageDimensions(options, section.properties);
|
|
286
|
-
const contentWidth = dims.pageWidth - dims.marginLeft - dims.marginRight;
|
|
287
|
-
const headerSet = layoutHeaderSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.pageHeight, dims.headerOffsetPt);
|
|
288
|
-
const footerSet = layoutFooterSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.footerOffsetPt);
|
|
289
|
-
return {
|
|
290
|
-
endIndex: section.endIndex,
|
|
291
|
-
properties: section.properties,
|
|
292
|
-
pageWidth: dims.pageWidth,
|
|
293
|
-
pageHeight: dims.pageHeight,
|
|
294
|
-
marginLeft: dims.marginLeft,
|
|
295
|
-
marginTop: dims.marginTop,
|
|
296
|
-
marginBottom: dims.marginBottom,
|
|
297
|
-
contentWidth,
|
|
298
|
-
pageContentHeight: dims.pageHeight - dims.marginTop - dims.marginBottom,
|
|
299
|
-
headerSet,
|
|
300
|
-
footerSet,
|
|
301
|
-
titlePg: section.properties.titlePg === true,
|
|
302
|
-
evenAndOddHeaders: section.properties.evenAndOddHeaders === true
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
function applyNumberingToHeadersFooters(hf, numbering) {
|
|
306
|
-
if (!hf || hf.size === 0) return /* @__PURE__ */ new Map();
|
|
307
|
-
if (!numbering || numbering.abstractNums.size === 0) return hf;
|
|
308
|
-
const out = /* @__PURE__ */ new Map();
|
|
309
|
-
for (const [key, value] of hf) out.set(key, applyNumbering(value, numbering));
|
|
310
|
-
return out;
|
|
311
|
-
}
|
|
312
|
-
function refByType(refs, type) {
|
|
313
|
-
if (!refs || refs.length === 0) return void 0;
|
|
314
|
-
return refs.find((r) => r.type === type);
|
|
315
|
-
}
|
|
316
|
-
function markPagination(cmds) {
|
|
317
|
-
return cmds.map((c) => ({
|
|
318
|
-
...c,
|
|
319
|
-
artifact: "pagination"
|
|
320
|
-
}));
|
|
321
|
-
}
|
|
322
|
-
function layoutHeaderSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, pageHeight, headerOffsetPt) {
|
|
323
|
-
const band = (type) => {
|
|
324
|
-
const ref = refByType(section.headers, type);
|
|
325
|
-
if (!ref) return [];
|
|
326
|
-
const content = headersFooters.get(ref.relationshipId);
|
|
327
|
-
if (!content) return [];
|
|
328
|
-
return markPagination(drawBlocksSequentially(laidOutBlocksFor(content, options, fontResources, contentWidth), marginLeft, pageHeight - headerOffsetPt));
|
|
329
|
-
};
|
|
330
|
-
return {
|
|
331
|
-
default: band("default"),
|
|
332
|
-
first: band("first"),
|
|
333
|
-
even: band("even")
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
function layoutFooterSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, footerOffsetPt) {
|
|
337
|
-
const band = (type) => {
|
|
338
|
-
const ref = refByType(section.footers, type);
|
|
339
|
-
if (!ref) return [];
|
|
340
|
-
const content = headersFooters.get(ref.relationshipId);
|
|
341
|
-
if (!content) return [];
|
|
342
|
-
const blocks = laidOutBlocksFor(content, options, fontResources, contentWidth);
|
|
343
|
-
return markPagination(drawBlocksSequentially(blocks, marginLeft, footerOffsetPt + blocks.reduce((sum, b) => sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : b.heightPt), 0)));
|
|
344
|
-
};
|
|
345
|
-
return {
|
|
346
|
-
default: band("default"),
|
|
347
|
-
first: band("first"),
|
|
348
|
-
even: band("even")
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function pickBand(set, band) {
|
|
352
|
-
if (band === "first") return set.first.length > 0 ? set.first : set.default;
|
|
353
|
-
if (band === "even") return set.even.length > 0 ? set.even : set.default;
|
|
354
|
-
return set.default;
|
|
355
|
-
}
|
|
356
|
-
function bandForPage(pageInSection, globalPageIdx, titlePg, evenAndOddHeaders) {
|
|
357
|
-
if (pageInSection === 0 && titlePg) return "first";
|
|
358
|
-
if (evenAndOddHeaders && (globalPageIdx + 1) % 2 === 0) return "even";
|
|
359
|
-
return "default";
|
|
360
|
-
}
|
|
361
|
-
function laidOutBlocksFor(elements, options, fontResources, contentWidth) {
|
|
362
|
-
return elements.map((el) => layoutBodyElement(el, options, fontResources, void 0, contentWidth));
|
|
363
|
-
}
|
|
364
|
-
function layoutBodyElement(el, options, fontResources, imageResources, contentWidth, maxHeight) {
|
|
365
|
-
if (el.kind === "paragraph") return layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, contentWidth);
|
|
366
|
-
if (el.kind === "table") return layoutTableBlock(el.table, options, fontResources, imageResources, contentWidth);
|
|
367
|
-
if (el.kind === "image") return layoutImageBlock(el.image, imageResources, contentWidth);
|
|
368
|
-
if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight);
|
|
369
|
-
return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight);
|
|
370
|
-
}
|
|
371
|
-
var EMU_PER_PT = 12700;
|
|
372
|
-
function layoutImageBlock(image, imageResources, contentWidth) {
|
|
373
|
-
let widthPt = image.widthEmu / EMU_PER_PT;
|
|
374
|
-
let heightPt = image.heightEmu / EMU_PER_PT;
|
|
375
|
-
if (widthPt > contentWidth) {
|
|
376
|
-
const scale = contentWidth / widthPt;
|
|
377
|
-
widthPt = contentWidth;
|
|
378
|
-
heightPt = heightPt * scale;
|
|
379
|
-
}
|
|
380
|
-
const res = imageResources?.get(image.imageId);
|
|
381
|
-
const resolvedAlignment = image.paragraphProperties.alignment ?? "left";
|
|
382
|
-
return {
|
|
383
|
-
kind: "image",
|
|
384
|
-
widthPt,
|
|
385
|
-
heightPt,
|
|
386
|
-
resolvedAlignment,
|
|
387
|
-
resourceName: res?.resourceName ?? "",
|
|
388
|
-
spacingBeforePt: (image.paragraphProperties.spacingBeforeTwips ?? 0) * TWIP_TO_PT,
|
|
389
|
-
spacingAfterPt: (image.paragraphProperties.spacingAfterTwips ?? 0) * TWIP_TO_PT,
|
|
390
|
-
...image.altText ? { altText: image.altText } : {}
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
var DEFAULT_LINE_WIDTH_EMU = 9525;
|
|
394
|
-
var DEFAULT_INSET_LR_EMU = 91440;
|
|
395
|
-
var DEFAULT_INSET_TB_EMU = 45720;
|
|
396
|
-
function layoutShapeBlock(shape, options, fontResources, imageResources, contentWidth, maxHeight) {
|
|
397
|
-
let widthPt = shape.widthEmu / EMU_PER_PT;
|
|
398
|
-
let heightPt = shape.heightEmu / EMU_PER_PT;
|
|
399
|
-
if (widthPt > contentWidth && widthPt > 0) {
|
|
400
|
-
const scale = contentWidth / widthPt;
|
|
401
|
-
widthPt = contentWidth;
|
|
402
|
-
heightPt *= scale;
|
|
403
|
-
}
|
|
404
|
-
if (maxHeight !== void 0 && heightPt > maxHeight && heightPt > 0) {
|
|
405
|
-
const scale = maxHeight / heightPt;
|
|
406
|
-
widthPt *= scale;
|
|
407
|
-
heightPt = maxHeight;
|
|
408
|
-
}
|
|
409
|
-
const paths = buildShapePaths(shape.geometry, widthPt, heightPt);
|
|
410
|
-
const fillColorHex = shape.fill.kind === "solid" ? shape.fill.colorHex : void 0;
|
|
411
|
-
const stroke = buildStroke(shape.line);
|
|
412
|
-
const t = shape.transform;
|
|
413
|
-
const pp = shape.paragraphProperties;
|
|
414
|
-
const text = shape.text;
|
|
415
|
-
const insetLeftPt = (text?.insetLeftEmu ?? DEFAULT_INSET_LR_EMU) / EMU_PER_PT;
|
|
416
|
-
const insetRightPt = (text?.insetRightEmu ?? DEFAULT_INSET_LR_EMU) / EMU_PER_PT;
|
|
417
|
-
const insetTopPt = (text?.insetTopEmu ?? DEFAULT_INSET_TB_EMU) / EMU_PER_PT;
|
|
418
|
-
const insetBottomPt = (text?.insetBottomEmu ?? DEFAULT_INSET_TB_EMU) / EMU_PER_PT;
|
|
419
|
-
const textLines = [];
|
|
420
|
-
let textHeightPt = 0;
|
|
421
|
-
if (text && text.content.length > 0) {
|
|
422
|
-
const innerWidth = Math.max(1, widthPt - insetLeftPt - insetRightPt);
|
|
423
|
-
for (const el of text.content) {
|
|
424
|
-
if (el.kind !== "paragraph") continue;
|
|
425
|
-
const blk = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, innerWidth);
|
|
426
|
-
for (const line of blk.lines) {
|
|
427
|
-
textLines.push(line);
|
|
428
|
-
textHeightPt += computeLineHeight(line, blk.resolved);
|
|
429
|
-
}
|
|
430
|
-
textHeightPt += blk.spacingAfterPt;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
return {
|
|
434
|
-
kind: "shape",
|
|
435
|
-
widthPt,
|
|
436
|
-
heightPt,
|
|
437
|
-
paths,
|
|
438
|
-
...fillColorHex ? { fillColorHex } : {},
|
|
439
|
-
...stroke ? { stroke } : {},
|
|
440
|
-
rotation60k: t?.rotation60k ?? 0,
|
|
441
|
-
flipH: t?.flipH ?? false,
|
|
442
|
-
flipV: t?.flipV ?? false,
|
|
443
|
-
resolvedAlignment: pp.alignment ?? "left",
|
|
444
|
-
spacingBeforePt: (pp.spacingBeforeTwips ?? 0) * TWIP_TO_PT,
|
|
445
|
-
spacingAfterPt: (pp.spacingAfterTwips ?? 0) * TWIP_TO_PT,
|
|
446
|
-
textLines,
|
|
447
|
-
textHeightPt,
|
|
448
|
-
insetLeftPt,
|
|
449
|
-
insetRightPt,
|
|
450
|
-
insetTopPt,
|
|
451
|
-
insetBottomPt,
|
|
452
|
-
anchor: text?.anchor ?? "t",
|
|
453
|
-
...shape.altText ? { altText: shape.altText } : {}
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
function buildShapePaths(geometry, widthPt, heightPt) {
|
|
457
|
-
if (geometry.kind === "preset") return presetPaths(geometry.preset ?? "rect", widthPt, heightPt, geometry.adjust ?? /* @__PURE__ */ new Map()) ?? [rectPath(widthPt, heightPt)];
|
|
458
|
-
if (geometry.custom) return customPaths(geometry.custom, widthPt, heightPt);
|
|
459
|
-
return [rectPath(widthPt, heightPt)];
|
|
460
|
-
}
|
|
461
|
-
function buildStroke(line) {
|
|
462
|
-
if (!line || line.fill === "none") return void 0;
|
|
463
|
-
const widthPt = (line.widthEmu ?? DEFAULT_LINE_WIDTH_EMU) / EMU_PER_PT;
|
|
464
|
-
const dash = line.dash && line.dash !== "solid" ? dashPattern(line.dash, widthPt) : void 0;
|
|
465
|
-
const cap = line.cap === "flat" ? "butt" : line.cap;
|
|
466
|
-
return {
|
|
467
|
-
colorHex: line.colorHex ?? "000000",
|
|
468
|
-
widthPt,
|
|
469
|
-
...dash ? { dash } : {},
|
|
470
|
-
...cap ? { cap } : {}
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
function dashPattern(dash, w) {
|
|
474
|
-
const u = Math.max(w, .1);
|
|
475
|
-
switch (dash) {
|
|
476
|
-
case "solid": return;
|
|
477
|
-
case "dot": return [u, 2 * u];
|
|
478
|
-
case "dash": return [4 * u, 3 * u];
|
|
479
|
-
case "dashDot": return [
|
|
480
|
-
4 * u,
|
|
481
|
-
3 * u,
|
|
482
|
-
u,
|
|
483
|
-
3 * u
|
|
484
|
-
];
|
|
485
|
-
case "lgDash": return [8 * u, 3 * u];
|
|
486
|
-
case "lgDashDot": return [
|
|
487
|
-
8 * u,
|
|
488
|
-
3 * u,
|
|
489
|
-
u,
|
|
490
|
-
3 * u
|
|
491
|
-
];
|
|
492
|
-
case "sysDash": return [3 * u, u];
|
|
493
|
-
case "sysDot": return [u, u];
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
function buildShapeTransform(pageX, pageY, widthPt, heightPt, rotation60k, flipH, flipV) {
|
|
497
|
-
const theta = -rotation60k / 6e4 * (Math.PI / 180);
|
|
498
|
-
const sx = flipH ? -1 : 1;
|
|
499
|
-
const sy = flipV ? -1 : 1;
|
|
500
|
-
const cos = Math.cos(theta);
|
|
501
|
-
const sin = Math.sin(theta);
|
|
502
|
-
const a = sx * cos;
|
|
503
|
-
const b = sx * sin;
|
|
504
|
-
const c = -sy * sin;
|
|
505
|
-
const d = sy * cos;
|
|
506
|
-
const cxL = widthPt / 2;
|
|
507
|
-
const cyL = heightPt / 2;
|
|
508
|
-
const centerX = pageX + cxL;
|
|
509
|
-
const centerY = pageY + cyL;
|
|
510
|
-
return [
|
|
511
|
-
a,
|
|
512
|
-
b,
|
|
513
|
-
c,
|
|
514
|
-
d,
|
|
515
|
-
centerX - (a * cxL + c * cyL),
|
|
516
|
-
centerY - (b * cxL + d * cyL)
|
|
517
|
-
];
|
|
518
|
-
}
|
|
519
|
-
function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight) {
|
|
520
|
-
let widthPt = block.widthEmu / EMU_PER_PT;
|
|
521
|
-
let heightPt = block.heightEmu / EMU_PER_PT;
|
|
522
|
-
if (widthPt > contentWidth && widthPt > 0) {
|
|
523
|
-
const scale = contentWidth / widthPt;
|
|
524
|
-
widthPt = contentWidth;
|
|
525
|
-
heightPt *= scale;
|
|
526
|
-
}
|
|
527
|
-
if (maxHeight !== void 0 && heightPt > maxHeight && heightPt > 0) {
|
|
528
|
-
const scale = maxHeight / heightPt;
|
|
529
|
-
widthPt *= scale;
|
|
530
|
-
heightPt = maxHeight;
|
|
531
|
-
}
|
|
532
|
-
const chart = options.charts?.get(block.chartRelId);
|
|
533
|
-
const { variant } = options.registry.resolveByStyle(false, false);
|
|
534
|
-
const font = fontResources.get(variant);
|
|
535
|
-
const layout = chart && font ? buildChartLayout(chart, widthPt, heightPt, font) : {
|
|
536
|
-
shapes: [],
|
|
537
|
-
texts: []
|
|
538
|
-
};
|
|
539
|
-
const pp = block.paragraphProperties;
|
|
540
|
-
const altText = block.altText ?? chart?.title;
|
|
541
|
-
return {
|
|
542
|
-
kind: "chart",
|
|
543
|
-
widthPt,
|
|
544
|
-
heightPt,
|
|
545
|
-
layout,
|
|
546
|
-
resolvedAlignment: pp.alignment ?? "left",
|
|
547
|
-
spacingBeforePt: (pp.spacingBeforeTwips ?? 0) * TWIP_TO_PT,
|
|
548
|
-
spacingAfterPt: (pp.spacingAfterTwips ?? 0) * TWIP_TO_PT,
|
|
549
|
-
...altText ? { altText } : {}
|
|
550
|
-
};
|
|
551
|
-
}
|
|
552
|
-
function buildChartLayout(chart, widthPt, heightPt, font) {
|
|
553
|
-
const measure = (text, sizePt) => font.embedded.textWidthPt(text, sizePt);
|
|
554
|
-
const scene = buildChartScene(chart, widthPt, heightPt, measure);
|
|
555
|
-
if (!scene) return {
|
|
556
|
-
shapes: [{
|
|
557
|
-
paths: [rectPath(widthPt, heightPt)],
|
|
558
|
-
stroke: {
|
|
559
|
-
colorHex: "D9D9D9",
|
|
560
|
-
widthPt: 1
|
|
561
|
-
}
|
|
562
|
-
}],
|
|
563
|
-
texts: []
|
|
564
|
-
};
|
|
565
|
-
const shapes = [];
|
|
566
|
-
for (const pg of scene.polygons ?? []) shapes.push(polygonPrim(pg));
|
|
567
|
-
for (const r of scene.rects) shapes.push(rectPrim(r));
|
|
568
|
-
for (const p of scene.polylines) shapes.push(polylinePrim(p));
|
|
569
|
-
for (const w of scene.wedges) shapes.push(wedgePrim(w));
|
|
570
|
-
return {
|
|
571
|
-
shapes,
|
|
572
|
-
texts: scene.labels.map((l) => labelPrim(l, font))
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
function rectAtPath(x, y, w, h) {
|
|
576
|
-
return new PathBuilder().moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).close().build();
|
|
577
|
-
}
|
|
578
|
-
function rectPrim(r) {
|
|
579
|
-
return {
|
|
580
|
-
paths: [rectAtPath(r.x, r.y, r.w, r.h)],
|
|
581
|
-
...r.fillHex ? { fillColorHex: r.fillHex } : {},
|
|
582
|
-
...r.strokeHex ? { stroke: {
|
|
583
|
-
colorHex: r.strokeHex,
|
|
584
|
-
widthPt: r.strokeWidthPt ?? 1
|
|
585
|
-
} } : {}
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function polylinePrim(p) {
|
|
589
|
-
const b = new PathBuilder();
|
|
590
|
-
p.points.forEach(([x, y], i) => i === 0 ? b.moveTo(x, y) : b.lineTo(x, y));
|
|
591
|
-
return {
|
|
592
|
-
paths: [b.build()],
|
|
593
|
-
stroke: {
|
|
594
|
-
colorHex: p.strokeHex,
|
|
595
|
-
widthPt: p.widthPt
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
function polygonPrim(p) {
|
|
600
|
-
const b = new PathBuilder();
|
|
601
|
-
p.points.forEach(([x, y], i) => i === 0 ? b.moveTo(x, y) : b.lineTo(x, y));
|
|
602
|
-
b.close();
|
|
603
|
-
return {
|
|
604
|
-
paths: [b.build()],
|
|
605
|
-
fillColorHex: p.fillHex,
|
|
606
|
-
...p.strokeHex ? { stroke: {
|
|
607
|
-
colorHex: p.strokeHex,
|
|
608
|
-
widthPt: p.widthPt ?? 1
|
|
609
|
-
} } : {}
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
function wedgePrim(w) {
|
|
613
|
-
const start = arcPoint(w.cx, w.cy, w.r, w.r, w.startRad);
|
|
614
|
-
return {
|
|
615
|
-
paths: [new PathBuilder().moveTo(w.cx, w.cy).lineTo(start[0], start[1]).append(arcToBeziers(w.cx, w.cy, w.r, w.r, w.startRad, w.sweepRad)).close().build()],
|
|
616
|
-
fillColorHex: w.fillHex,
|
|
617
|
-
...w.strokeHex ? { stroke: {
|
|
618
|
-
colorHex: w.strokeHex,
|
|
619
|
-
widthPt: 1
|
|
620
|
-
} } : {}
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
function labelPrim(l, font) {
|
|
624
|
-
const line = makeChartLabelLine(l.text, font, l.sizePt, l.colorHex);
|
|
625
|
-
const w = line.contentWidthPt;
|
|
626
|
-
return {
|
|
627
|
-
line,
|
|
628
|
-
x: l.align === "center" ? l.x - w / 2 : l.align === "right" ? l.x - w : l.x,
|
|
629
|
-
y: l.y
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
function makeChartLabelLine(text, font, sizePt, colorHex) {
|
|
633
|
-
const widthPt = font.embedded.textWidthPt(text, sizePt);
|
|
634
|
-
return {
|
|
635
|
-
tokens: [{
|
|
636
|
-
kind: "text",
|
|
637
|
-
text,
|
|
638
|
-
isSpace: false,
|
|
639
|
-
resolvedRun: {
|
|
640
|
-
...DEFAULT_RESOLVED_RUN,
|
|
641
|
-
colorHex,
|
|
642
|
-
fontSizeHalfPoints: Math.round(sizePt * 2)
|
|
643
|
-
},
|
|
644
|
-
font,
|
|
645
|
-
fontSizePt: sizePt,
|
|
646
|
-
widthPt,
|
|
647
|
-
bidiLevel: 0
|
|
648
|
-
}],
|
|
649
|
-
contentWidthPt: widthPt,
|
|
650
|
-
maxFontSizePt: sizePt,
|
|
651
|
-
availableWidthPt: widthPt,
|
|
652
|
-
firstLine: true,
|
|
653
|
-
resolved: DEFAULT_RESOLVED_PARAGRAPH,
|
|
654
|
-
isLastInParagraph: true
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
function embedUsedImages(doc, body, headersFooters, options) {
|
|
658
|
-
const out = /* @__PURE__ */ new Map();
|
|
659
|
-
if (!options.images || options.images.size === 0) return out;
|
|
660
|
-
const seen = /* @__PURE__ */ new Set();
|
|
661
|
-
const visit = (elements) => {
|
|
662
|
-
for (const el of elements) if (el.kind === "image") seen.add(el.image.imageId);
|
|
663
|
-
else if (el.kind === "paragraph") {
|
|
664
|
-
for (const run of el.paragraph.runs) if (run.inlineImage) seen.add(run.inlineImage.imageId);
|
|
665
|
-
} else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
666
|
-
else if (el.kind === "shape") {
|
|
667
|
-
if (el.shape.text) visit(el.shape.text.content);
|
|
668
|
-
}
|
|
669
|
-
};
|
|
670
|
-
visit(body);
|
|
671
|
-
for (const hf of headersFooters.values()) visit(hf);
|
|
672
|
-
const flattenAlpha = options.pdfA ? parsePdfAProfile(options.pdfA).part === 1 : false;
|
|
673
|
-
let counter = 0;
|
|
674
|
-
for (const imageId of seen) {
|
|
675
|
-
const bytes = options.images.get(imageId);
|
|
676
|
-
if (!bytes) continue;
|
|
677
|
-
let embedded;
|
|
678
|
-
try {
|
|
679
|
-
embedded = embedImage(doc, bytes, { flattenAlpha });
|
|
680
|
-
} catch {
|
|
681
|
-
continue;
|
|
682
|
-
}
|
|
683
|
-
counter++;
|
|
684
|
-
out.set(imageId, {
|
|
685
|
-
resourceName: `Im${counter}`,
|
|
686
|
-
ref: embedded.ref
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
return out;
|
|
690
|
-
}
|
|
691
|
-
function drawBlocksSequentially(blocks, startX, startY) {
|
|
692
|
-
const out = [];
|
|
693
|
-
let cursorY = startY;
|
|
694
|
-
for (const block of blocks) {
|
|
695
|
-
if (block.kind !== "paragraph") continue;
|
|
696
|
-
cursorY -= block.spacingBeforePt;
|
|
697
|
-
for (const line of block.lines) {
|
|
698
|
-
const h = computeLineHeight(line, block.resolved);
|
|
699
|
-
cursorY -= h;
|
|
700
|
-
const indentLeft = block.resolved.indentLeftTwips * TWIP_TO_PT + (line.firstLine ? block.resolved.indentFirstLineTwips * TWIP_TO_PT : 0);
|
|
701
|
-
const offset = alignmentOffset(block.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
|
|
702
|
-
out.push({
|
|
703
|
-
type: "line",
|
|
704
|
-
line,
|
|
705
|
-
originX: startX + indentLeft + offset,
|
|
706
|
-
baselineY: cursorY + lineDescent(line)
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
cursorY -= block.spacingAfterPt;
|
|
710
|
-
}
|
|
711
|
-
return out;
|
|
712
|
-
}
|
|
713
|
-
function applyNumbering(body, numbering) {
|
|
714
|
-
if (!numbering || numbering.abstractNums.size === 0) return body.map((b) => b);
|
|
715
|
-
const state = new NumberingState();
|
|
716
|
-
const transformParagraph = (p) => {
|
|
717
|
-
const ref = p.properties.numbering;
|
|
718
|
-
if (!ref) return p;
|
|
719
|
-
const marker = state.resolveMarker(numbering, ref);
|
|
720
|
-
if (marker === null) return p;
|
|
721
|
-
const instance = numbering.numInstances.get(ref.numId);
|
|
722
|
-
const level = (instance ? numbering.abstractNums.get(instance.abstractNumId) : void 0)?.levels.get(ref.ilvl);
|
|
723
|
-
const markerRun = {
|
|
724
|
-
text: `${marker}\t`,
|
|
725
|
-
properties: level?.runProperties ?? {}
|
|
726
|
-
};
|
|
727
|
-
return {
|
|
728
|
-
properties: mergeIndentFromLevel(p.properties, level?.paragraphProperties),
|
|
729
|
-
runs: [markerRun, ...p.runs]
|
|
730
|
-
};
|
|
731
|
-
};
|
|
732
|
-
const visit = (el) => {
|
|
733
|
-
if (el.kind === "paragraph") return {
|
|
734
|
-
kind: "paragraph",
|
|
735
|
-
paragraph: transformParagraph(el.paragraph)
|
|
736
|
-
};
|
|
737
|
-
if (el.kind === "image") return el;
|
|
738
|
-
if (el.kind === "shape") return el;
|
|
739
|
-
if (el.kind === "chart") return el;
|
|
740
|
-
return {
|
|
741
|
-
kind: "table",
|
|
742
|
-
table: {
|
|
743
|
-
...el.table,
|
|
744
|
-
rows: el.table.rows.map((row) => ({
|
|
745
|
-
...row,
|
|
746
|
-
cells: row.cells.map((cell) => ({
|
|
747
|
-
...cell,
|
|
748
|
-
content: cell.content.map(visit)
|
|
749
|
-
}))
|
|
750
|
-
}))
|
|
751
|
-
}
|
|
752
|
-
};
|
|
753
|
-
};
|
|
754
|
-
return body.map(visit);
|
|
755
|
-
}
|
|
756
|
-
function mergeIndentFromLevel(paragraphProps, levelProps) {
|
|
757
|
-
if (!levelProps) return paragraphProps;
|
|
758
|
-
const out = { ...paragraphProps };
|
|
759
|
-
if (out.indentLeftTwips === void 0 && levelProps.indentLeftTwips !== void 0) out.indentLeftTwips = levelProps.indentLeftTwips;
|
|
760
|
-
if (out.indentRightTwips === void 0 && levelProps.indentRightTwips !== void 0) out.indentRightTwips = levelProps.indentRightTwips;
|
|
761
|
-
if (out.indentFirstLineTwips === void 0 && levelProps.indentFirstLineTwips !== void 0) out.indentFirstLineTwips = levelProps.indentFirstLineTwips;
|
|
762
|
-
return out;
|
|
763
|
-
}
|
|
764
|
-
function runFontKeyAndParsed(options, ascii, bold, italic) {
|
|
765
|
-
if (ascii && options.embeddedFonts) {
|
|
766
|
-
const name = ascii.trim().toLowerCase();
|
|
767
|
-
const emb = options.embeddedFonts.get(name);
|
|
768
|
-
if (emb) {
|
|
769
|
-
const { variant, parsed } = emb.resolveByStyle(bold, italic);
|
|
770
|
-
return {
|
|
771
|
-
fontKey: `embed:${name}:${variant}`,
|
|
772
|
-
parsed
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
const byFamily = options.registriesByFamily;
|
|
777
|
-
if (byFamily && byFamily.size > 0) {
|
|
778
|
-
let key = resolveFamilyKey(ascii);
|
|
779
|
-
let reg = byFamily.get(key);
|
|
780
|
-
if (!reg) {
|
|
781
|
-
key = byFamily.keys().next().value;
|
|
782
|
-
reg = byFamily.get(key);
|
|
783
|
-
}
|
|
784
|
-
const { variant, parsed } = reg.resolveByStyle(bold, italic);
|
|
785
|
-
return {
|
|
786
|
-
fontKey: `${key}:${variant}`,
|
|
787
|
-
parsed
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
const { variant, parsed } = options.registry.resolveByStyle(bold, italic);
|
|
791
|
-
return {
|
|
792
|
-
fontKey: variant,
|
|
793
|
-
parsed
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
function lookupFont(resources, fontKey) {
|
|
797
|
-
return resources.get(fontKey) ?? resources.values().next().value;
|
|
798
|
-
}
|
|
799
|
-
function embedUsedFonts(doc, body, headersFooters, options) {
|
|
800
|
-
const used = /* @__PURE__ */ new Map();
|
|
801
|
-
const addRun = (run, para) => {
|
|
802
|
-
const resolved = resolveRunProperties(run.properties, para.properties, options.styles);
|
|
803
|
-
const { fontKey, parsed } = runFontKeyAndParsed(options, resolved.fontFamily.ascii, resolved.bold, resolved.italic);
|
|
804
|
-
let bucket = used.get(fontKey);
|
|
805
|
-
if (!bucket) {
|
|
806
|
-
bucket = {
|
|
807
|
-
parsed,
|
|
808
|
-
gids: /* @__PURE__ */ new Set()
|
|
809
|
-
};
|
|
810
|
-
used.set(fontKey, bucket);
|
|
811
|
-
}
|
|
812
|
-
const shaped = shapeText(run.text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
|
|
813
|
-
for (const g of shaped.gids) bucket.gids.add(g);
|
|
814
|
-
};
|
|
815
|
-
const addMath = (node) => {
|
|
816
|
-
for (const seg of mathGlyphSegments(node)) {
|
|
817
|
-
const { bold, italic } = variantStyle(seg.variant);
|
|
818
|
-
const { variant, parsed } = options.registry.resolveByStyle(bold, italic);
|
|
819
|
-
let bucket = used.get(variant);
|
|
820
|
-
if (!bucket) {
|
|
821
|
-
bucket = {
|
|
822
|
-
parsed,
|
|
823
|
-
gids: /* @__PURE__ */ new Set()
|
|
824
|
-
};
|
|
825
|
-
used.set(variant, bucket);
|
|
826
|
-
}
|
|
827
|
-
for (const ch of seg.text) bucket.gids.add(parsed.glyphForCodepoint(ch.codePointAt(0)));
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
const visit = (elements) => {
|
|
831
|
-
for (const el of elements) if (el.kind === "paragraph") for (const run of el.paragraph.runs) {
|
|
832
|
-
if (run.math) {
|
|
833
|
-
addMath(run.math);
|
|
834
|
-
continue;
|
|
835
|
-
}
|
|
836
|
-
if (run.inlineImage && !run.text) continue;
|
|
837
|
-
addRun(run, el.paragraph);
|
|
838
|
-
}
|
|
839
|
-
else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
840
|
-
else if (el.kind === "shape") {
|
|
841
|
-
if (el.shape.text) visit(el.shape.text.content);
|
|
842
|
-
} else if (el.kind === "chart") {
|
|
843
|
-
const chart = options.charts?.get(el.chart.chartRelId);
|
|
844
|
-
if (chart) {
|
|
845
|
-
const reg = options.registry.resolveByStyle(false, false);
|
|
846
|
-
let bucket = used.get(reg.variant);
|
|
847
|
-
if (!bucket) {
|
|
848
|
-
bucket = {
|
|
849
|
-
parsed: reg.parsed,
|
|
850
|
-
gids: /* @__PURE__ */ new Set()
|
|
851
|
-
};
|
|
852
|
-
used.set(reg.variant, bucket);
|
|
853
|
-
}
|
|
854
|
-
const add = (s) => {
|
|
855
|
-
for (const ch of s) bucket.gids.add(reg.parsed.glyphForCodepoint(ch.codePointAt(0)));
|
|
856
|
-
};
|
|
857
|
-
if (chart.title) add(chart.title);
|
|
858
|
-
for (const c of chart.categories) add(c);
|
|
859
|
-
for (const sr of chart.series) if (sr.name) add(sr.name);
|
|
860
|
-
add("0123456789.,-%() ");
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
};
|
|
864
|
-
visit(body);
|
|
865
|
-
for (const hf of headersFooters.values()) visit(hf);
|
|
866
|
-
if (used.size === 0) {
|
|
867
|
-
const regular = options.registry.resolveByStyle(false, false);
|
|
868
|
-
used.set(regular.variant, {
|
|
869
|
-
parsed: regular.parsed,
|
|
870
|
-
gids: new Set([0])
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
const cidSet = options.pdfA ? parsePdfAProfile(options.pdfA).part === 1 : false;
|
|
874
|
-
const out = /* @__PURE__ */ new Map();
|
|
875
|
-
let counter = 0;
|
|
876
|
-
for (const [variant, info] of used) {
|
|
877
|
-
counter++;
|
|
878
|
-
const resourceName = `F${counter}`;
|
|
879
|
-
const embedded = embedTtfFont(doc, info.parsed, {
|
|
880
|
-
usedGids: info.gids,
|
|
881
|
-
cidSet
|
|
882
|
-
});
|
|
883
|
-
out.set(variant, {
|
|
884
|
-
resourceName,
|
|
885
|
-
parsed: info.parsed,
|
|
886
|
-
embedded
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
return out;
|
|
890
|
-
}
|
|
891
|
-
function buildFontResourceDict(resources) {
|
|
892
|
-
const entries = {};
|
|
893
|
-
for (const res of resources.values()) entries[res.resourceName] = ref(res.embedded.fontRef.id);
|
|
894
|
-
return dict(entries);
|
|
895
|
-
}
|
|
896
|
-
function buildXObjectResourceDict(resources) {
|
|
897
|
-
if (resources.size === 0) return void 0;
|
|
898
|
-
const entries = {};
|
|
899
|
-
for (const res of resources.values()) entries[res.resourceName] = ref(res.ref.id);
|
|
900
|
-
return dict(entries);
|
|
901
|
-
}
|
|
902
|
-
function layoutParagraphBlock(paragraph, options, fontResources, imageResources, contentWidth) {
|
|
903
|
-
const baseResolved = resolveParagraphProperties(paragraph.properties, options.styles);
|
|
904
|
-
const baseDir = paragraphBaseDirection(paragraph, baseResolved);
|
|
905
|
-
const resolved = baseDir === "rtl" && baseResolved.alignment === "left" ? {
|
|
906
|
-
...baseResolved,
|
|
907
|
-
alignment: "right"
|
|
908
|
-
} : baseResolved;
|
|
909
|
-
const lines = wrap(tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir), paragraphMaxWidth(resolved, contentWidth, true), paragraphMaxWidth(resolved, contentWidth, false), resolved, options.hyphenator);
|
|
910
|
-
let heightPt = 0;
|
|
911
|
-
for (const line of lines) heightPt += computeLineHeight(line, resolved);
|
|
912
|
-
const numbering = paragraph.properties.numbering;
|
|
913
|
-
return {
|
|
914
|
-
kind: "paragraph",
|
|
915
|
-
resolved,
|
|
916
|
-
lines,
|
|
917
|
-
heightPt,
|
|
918
|
-
spacingBeforePt: resolved.spacingBeforeTwips * TWIP_TO_PT,
|
|
919
|
-
spacingAfterPt: resolved.spacingAfterTwips * TWIP_TO_PT,
|
|
920
|
-
...numbering ? { list: {
|
|
921
|
-
numId: numbering.numId,
|
|
922
|
-
level: numbering.ilvl
|
|
923
|
-
} } : {},
|
|
924
|
-
...paragraph.runs.some((r) => r.pageBreak) ? { pageBreakAfter: true } : {}
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
var OBJECT_REPLACEMENT = 65532;
|
|
928
|
-
var RLE = 8235;
|
|
929
|
-
var PDF_FMT = 8236;
|
|
930
|
-
var LRE = 8234;
|
|
931
|
-
function resolveMathItems(items, fontFor) {
|
|
932
|
-
return items.map((it) => {
|
|
933
|
-
if (it.kind === "glyph") return {
|
|
934
|
-
kind: "glyph",
|
|
935
|
-
x: it.x,
|
|
936
|
-
y: it.y,
|
|
937
|
-
text: it.text,
|
|
938
|
-
font: fontFor(it.variant),
|
|
939
|
-
sizePt: it.sizePt
|
|
940
|
-
};
|
|
941
|
-
if (it.kind === "rule") return {
|
|
942
|
-
kind: "rule",
|
|
943
|
-
x: it.x,
|
|
944
|
-
y: it.y,
|
|
945
|
-
w: it.w,
|
|
946
|
-
h: it.h
|
|
947
|
-
};
|
|
948
|
-
return {
|
|
949
|
-
kind: "path",
|
|
950
|
-
segments: it.segments,
|
|
951
|
-
...it.strokeWidthPt !== void 0 ? { strokeWidthPt: it.strokeWidthPt } : {},
|
|
952
|
-
...it.fill ? { fill: true } : {}
|
|
953
|
-
};
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
function tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir) {
|
|
957
|
-
const plans = paragraph.runs.map((run) => {
|
|
958
|
-
if (run.inlineImage) {
|
|
959
|
-
const naturalW = run.inlineImage.widthEmu / EMU_PER_PT;
|
|
960
|
-
const widthPt = Math.min(naturalW, contentWidth);
|
|
961
|
-
const scale = naturalW > 0 ? widthPt / naturalW : 1;
|
|
962
|
-
const heightPt = run.inlineImage.heightEmu / EMU_PER_PT * scale;
|
|
963
|
-
const res = imageResources?.get(run.inlineImage.imageId);
|
|
964
|
-
const resolvedRun = resolveRunProperties(run.properties, paragraph.properties, options.styles);
|
|
965
|
-
const { fontKey } = runFontKeyAndParsed(options, resolvedRun.fontFamily.ascii, resolvedRun.bold, resolvedRun.italic);
|
|
966
|
-
return {
|
|
967
|
-
run,
|
|
968
|
-
resolvedRun,
|
|
969
|
-
font: lookupFont(fontResources, fontKey),
|
|
970
|
-
fontSizePt: resolvedRun.fontSizeHalfPoints / 2,
|
|
971
|
-
isImage: true,
|
|
972
|
-
imageWidthPt: widthPt,
|
|
973
|
-
imageHeightPt: heightPt,
|
|
974
|
-
imageResourceName: res?.resourceName ?? ""
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
if (run.math) {
|
|
978
|
-
const resolvedRun = resolveRunProperties(run.properties, paragraph.properties, options.styles);
|
|
979
|
-
const sizePt = resolvedRun.fontSizeHalfPoints / 2;
|
|
980
|
-
const fontFor = (v) => {
|
|
981
|
-
const { bold, italic } = variantStyle(v);
|
|
982
|
-
const r = options.registry.resolveByStyle(bold, italic);
|
|
983
|
-
return fontResources.get(r.variant) ?? fontResources.get(options.registry.resolveByStyle(false, false).variant);
|
|
984
|
-
};
|
|
985
|
-
const measure = (text, sz, v) => fontFor(v).embedded.textWidthPt(text, sz);
|
|
986
|
-
const box = layoutMath(run.math, { sizePt }, measure);
|
|
987
|
-
const { variant } = options.registry.resolveByStyle(resolvedRun.bold, resolvedRun.italic);
|
|
988
|
-
return {
|
|
989
|
-
run,
|
|
990
|
-
resolvedRun,
|
|
991
|
-
font: lookupFont(fontResources, variant),
|
|
992
|
-
fontSizePt: sizePt,
|
|
993
|
-
isImage: false,
|
|
994
|
-
imageWidthPt: 0,
|
|
995
|
-
imageHeightPt: 0,
|
|
996
|
-
imageResourceName: "",
|
|
997
|
-
math: {
|
|
998
|
-
items: resolveMathItems(box.items, fontFor),
|
|
999
|
-
widthPt: box.width,
|
|
1000
|
-
ascentPt: box.ascent,
|
|
1001
|
-
descentPt: box.descent
|
|
1002
|
-
}
|
|
1003
|
-
};
|
|
1004
|
-
}
|
|
1005
|
-
const resolvedRun = resolveRunProperties(run.properties, paragraph.properties, options.styles);
|
|
1006
|
-
const { fontKey } = runFontKeyAndParsed(options, resolvedRun.fontFamily.ascii, resolvedRun.bold, resolvedRun.italic);
|
|
1007
|
-
return {
|
|
1008
|
-
run,
|
|
1009
|
-
resolvedRun,
|
|
1010
|
-
font: lookupFont(fontResources, fontKey),
|
|
1011
|
-
fontSizePt: resolvedRun.fontSizeHalfPoints / 2,
|
|
1012
|
-
isImage: false,
|
|
1013
|
-
imageWidthPt: 0,
|
|
1014
|
-
imageHeightPt: 0,
|
|
1015
|
-
imageResourceName: ""
|
|
1016
|
-
};
|
|
1017
|
-
});
|
|
1018
|
-
const anyRtlRun = plans.some((p) => p.resolvedRun.rtl);
|
|
1019
|
-
const anyBidiChars = plans.some((p) => !p.isImage && hasBidiCharacters(p.run.text));
|
|
1020
|
-
if (!(baseDir === "rtl" || anyRtlRun || anyBidiChars)) return tokenizePlansLtr(plans);
|
|
1021
|
-
const bidiCps = [];
|
|
1022
|
-
const realIndexOfBidi = [];
|
|
1023
|
-
let realCount = 0;
|
|
1024
|
-
for (const plan of plans) {
|
|
1025
|
-
const isObject = plan.isImage || plan.math !== void 0;
|
|
1026
|
-
const wrap = plan.resolvedRun.rtl ? RLE : baseDir === "rtl" && !isObject ? LRE : 0;
|
|
1027
|
-
if (wrap) {
|
|
1028
|
-
bidiCps.push(wrap);
|
|
1029
|
-
realIndexOfBidi.push(-1);
|
|
1030
|
-
}
|
|
1031
|
-
if (isObject) {
|
|
1032
|
-
bidiCps.push(OBJECT_REPLACEMENT);
|
|
1033
|
-
realIndexOfBidi.push(realCount);
|
|
1034
|
-
realCount++;
|
|
1035
|
-
} else for (const ch of plan.run.text) {
|
|
1036
|
-
bidiCps.push(ch.codePointAt(0));
|
|
1037
|
-
realIndexOfBidi.push(realCount);
|
|
1038
|
-
realCount++;
|
|
1039
|
-
}
|
|
1040
|
-
if (wrap) {
|
|
1041
|
-
bidiCps.push(PDF_FMT);
|
|
1042
|
-
realIndexOfBidi.push(-1);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
const { levels } = computeBidi(bidiCps, baseDir);
|
|
1046
|
-
const realLevels = new Array(realCount).fill(baseDir === "rtl" ? 1 : 0);
|
|
1047
|
-
for (let i = 0; i < bidiCps.length; i++) {
|
|
1048
|
-
const ri = realIndexOfBidi[i];
|
|
1049
|
-
if (ri >= 0) realLevels[ri] = levels[i];
|
|
1050
|
-
}
|
|
1051
|
-
return tokenizePlansBidi(plans, realLevels);
|
|
1052
|
-
}
|
|
1053
|
-
function tokenizePlansLtr(plans) {
|
|
1054
|
-
const tokens = [];
|
|
1055
|
-
for (const plan of plans) {
|
|
1056
|
-
if (plan.isImage) {
|
|
1057
|
-
tokens.push({
|
|
1058
|
-
kind: "image",
|
|
1059
|
-
imageResourceName: plan.imageResourceName,
|
|
1060
|
-
widthPt: plan.imageWidthPt,
|
|
1061
|
-
heightPt: plan.imageHeightPt,
|
|
1062
|
-
isSpace: false,
|
|
1063
|
-
bidiLevel: 0
|
|
1064
|
-
});
|
|
1065
|
-
continue;
|
|
1066
|
-
}
|
|
1067
|
-
if (plan.math) {
|
|
1068
|
-
tokens.push({
|
|
1069
|
-
kind: "math",
|
|
1070
|
-
items: plan.math.items,
|
|
1071
|
-
widthPt: plan.math.widthPt,
|
|
1072
|
-
ascentPt: plan.math.ascentPt,
|
|
1073
|
-
descentPt: plan.math.descentPt,
|
|
1074
|
-
isSpace: false,
|
|
1075
|
-
bidiLevel: 0
|
|
1076
|
-
});
|
|
1077
|
-
continue;
|
|
1078
|
-
}
|
|
1079
|
-
for (const t of tokenizeText(plan.run.text)) tokens.push({
|
|
1080
|
-
kind: "text",
|
|
1081
|
-
text: t.text,
|
|
1082
|
-
isSpace: t.isSpace,
|
|
1083
|
-
resolvedRun: plan.resolvedRun,
|
|
1084
|
-
font: plan.font,
|
|
1085
|
-
fontSizePt: plan.fontSizePt,
|
|
1086
|
-
widthPt: plan.font.embedded.textWidthPt(t.text, plan.fontSizePt),
|
|
1087
|
-
bidiLevel: 0
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
return tokens;
|
|
1091
|
-
}
|
|
1092
|
-
function tokenizePlansBidi(plans, realLevels) {
|
|
1093
|
-
const tokens = [];
|
|
1094
|
-
let realIdx = 0;
|
|
1095
|
-
for (let r = 0; r < plans.length; r++) {
|
|
1096
|
-
const plan = plans[r];
|
|
1097
|
-
if (plan.isImage) {
|
|
1098
|
-
tokens.push({
|
|
1099
|
-
kind: "image",
|
|
1100
|
-
imageResourceName: plan.imageResourceName,
|
|
1101
|
-
widthPt: plan.imageWidthPt,
|
|
1102
|
-
heightPt: plan.imageHeightPt,
|
|
1103
|
-
isSpace: false,
|
|
1104
|
-
bidiLevel: realLevels[realIdx] ?? 0
|
|
1105
|
-
});
|
|
1106
|
-
realIdx++;
|
|
1107
|
-
continue;
|
|
1108
|
-
}
|
|
1109
|
-
if (plan.math) {
|
|
1110
|
-
tokens.push({
|
|
1111
|
-
kind: "math",
|
|
1112
|
-
items: plan.math.items,
|
|
1113
|
-
widthPt: plan.math.widthPt,
|
|
1114
|
-
ascentPt: plan.math.ascentPt,
|
|
1115
|
-
descentPt: plan.math.descentPt,
|
|
1116
|
-
isSpace: false,
|
|
1117
|
-
bidiLevel: realLevels[realIdx] ?? 0
|
|
1118
|
-
});
|
|
1119
|
-
realIdx++;
|
|
1120
|
-
continue;
|
|
1121
|
-
}
|
|
1122
|
-
const chars = [...plan.run.text];
|
|
1123
|
-
let bufStart = 0;
|
|
1124
|
-
let curSpace = false;
|
|
1125
|
-
let curLevel = -1;
|
|
1126
|
-
const flush = (endExclusive) => {
|
|
1127
|
-
if (endExclusive <= bufStart) return;
|
|
1128
|
-
const text = chars.slice(bufStart, endExclusive).join("");
|
|
1129
|
-
tokens.push({
|
|
1130
|
-
kind: "text",
|
|
1131
|
-
text,
|
|
1132
|
-
isSpace: curSpace,
|
|
1133
|
-
resolvedRun: plan.resolvedRun,
|
|
1134
|
-
font: plan.font,
|
|
1135
|
-
fontSizePt: plan.fontSizePt,
|
|
1136
|
-
widthPt: plan.font.embedded.textWidthPt(text, plan.fontSizePt),
|
|
1137
|
-
bidiLevel: curLevel
|
|
1138
|
-
});
|
|
1139
|
-
};
|
|
1140
|
-
for (let c = 0; c < chars.length; c++) {
|
|
1141
|
-
const ch = chars[c];
|
|
1142
|
-
const isSpace = /\s/.test(ch);
|
|
1143
|
-
const level = realLevels[realIdx] ?? 0;
|
|
1144
|
-
if (c === 0) {
|
|
1145
|
-
curSpace = isSpace;
|
|
1146
|
-
curLevel = level;
|
|
1147
|
-
} else if (isSpace !== curSpace || level !== curLevel) {
|
|
1148
|
-
flush(c);
|
|
1149
|
-
bufStart = c;
|
|
1150
|
-
curSpace = isSpace;
|
|
1151
|
-
curLevel = level;
|
|
1152
|
-
}
|
|
1153
|
-
realIdx++;
|
|
1154
|
-
}
|
|
1155
|
-
flush(chars.length);
|
|
1156
|
-
}
|
|
1157
|
-
return tokens;
|
|
1158
|
-
}
|
|
1159
|
-
function paragraphBaseDirection(paragraph, resolved) {
|
|
1160
|
-
if (resolved.bidi) return "rtl";
|
|
1161
|
-
let text = "";
|
|
1162
|
-
for (const run of paragraph.runs) {
|
|
1163
|
-
if (!run.inlineImage) text += run.text;
|
|
1164
|
-
if (text.length > 64) break;
|
|
1165
|
-
}
|
|
1166
|
-
if (!hasBidiCharacters(text)) return "ltr";
|
|
1167
|
-
return analyzeString(text, "auto").paragraphLevel === 1 ? "rtl" : "ltr";
|
|
1168
|
-
}
|
|
1169
|
-
function paragraphMaxWidth(p, contentWidth, firstLine) {
|
|
1170
|
-
const indentLeft = p.indentLeftTwips * TWIP_TO_PT;
|
|
1171
|
-
const indentRight = p.indentRightTwips * TWIP_TO_PT;
|
|
1172
|
-
const firstLineExtra = firstLine ? p.indentFirstLineTwips * TWIP_TO_PT : 0;
|
|
1173
|
-
return Math.max(1, contentWidth - indentLeft - indentRight - firstLineExtra);
|
|
1174
|
-
}
|
|
1175
|
-
function tokenizeText(text) {
|
|
1176
|
-
const out = [];
|
|
1177
|
-
if (text.length === 0) return out;
|
|
1178
|
-
const re = /(\s+)|(\S+)/g;
|
|
1179
|
-
let m;
|
|
1180
|
-
while ((m = re.exec(text)) !== null) if (m[1] !== void 0) out.push({
|
|
1181
|
-
text: m[1],
|
|
1182
|
-
isSpace: true
|
|
1183
|
-
});
|
|
1184
|
-
else if (m[2] !== void 0) out.push({
|
|
1185
|
-
text: m[2],
|
|
1186
|
-
isSpace: false
|
|
1187
|
-
});
|
|
1188
|
-
return out;
|
|
1189
|
-
}
|
|
1190
|
-
var HYPHENATION_PENALTY = 50;
|
|
1191
|
-
function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator) {
|
|
1192
|
-
if (tokens.length === 0) return [];
|
|
1193
|
-
const tokenLineSize = (t) => t.kind === "text" ? t.fontSizePt : t.kind === "image" ? t.heightPt : 0;
|
|
1194
|
-
const items = [];
|
|
1195
|
-
const fragmentTokens = [];
|
|
1196
|
-
const hyphenAtItem = [];
|
|
1197
|
-
const hyphenWidthAtItem = [];
|
|
1198
|
-
const pushItem = (item, frag, isHyphen = false, hyphenWidth = 0) => {
|
|
1199
|
-
items.push(item);
|
|
1200
|
-
fragmentTokens.push(frag);
|
|
1201
|
-
hyphenAtItem.push(isHyphen);
|
|
1202
|
-
hyphenWidthAtItem.push(hyphenWidth);
|
|
1203
|
-
};
|
|
1204
|
-
for (const tok of tokens) {
|
|
1205
|
-
if (tok.isSpace || tok.kind === "image" || tok.kind === "math") {
|
|
1206
|
-
pushItem(tok.isSpace ? {
|
|
1207
|
-
type: "glue",
|
|
1208
|
-
width: tok.widthPt,
|
|
1209
|
-
stretch: tok.widthPt * .6,
|
|
1210
|
-
shrink: tok.widthPt * .3
|
|
1211
|
-
} : {
|
|
1212
|
-
type: "box",
|
|
1213
|
-
width: tok.widthPt
|
|
1214
|
-
}, tok);
|
|
1215
|
-
continue;
|
|
1216
|
-
}
|
|
1217
|
-
const positions = hyphenator ? hyphenator.hyphenate(tok.text) : [];
|
|
1218
|
-
if (positions.length === 0) {
|
|
1219
|
-
pushItem({
|
|
1220
|
-
type: "box",
|
|
1221
|
-
width: tok.widthPt
|
|
1222
|
-
}, tok);
|
|
1223
|
-
continue;
|
|
1224
|
-
}
|
|
1225
|
-
const hyphenWidth = tok.font.embedded.textWidthPt("-", tok.fontSizePt);
|
|
1226
|
-
let prev = 0;
|
|
1227
|
-
for (let pi = 0; pi <= positions.length; pi++) {
|
|
1228
|
-
const end = pi < positions.length ? positions[pi] : tok.text.length;
|
|
1229
|
-
const fragText = tok.text.substring(prev, end);
|
|
1230
|
-
const fragWidth = tok.font.embedded.textWidthPt(fragText, tok.fontSizePt);
|
|
1231
|
-
const fragTok = {
|
|
1232
|
-
kind: "text",
|
|
1233
|
-
text: fragText,
|
|
1234
|
-
isSpace: false,
|
|
1235
|
-
resolvedRun: tok.resolvedRun,
|
|
1236
|
-
font: tok.font,
|
|
1237
|
-
fontSizePt: tok.fontSizePt,
|
|
1238
|
-
widthPt: fragWidth,
|
|
1239
|
-
bidiLevel: tok.bidiLevel
|
|
1240
|
-
};
|
|
1241
|
-
pushItem({
|
|
1242
|
-
type: "box",
|
|
1243
|
-
width: fragWidth
|
|
1244
|
-
}, fragTok);
|
|
1245
|
-
if (pi < positions.length) pushItem({
|
|
1246
|
-
type: "penalty",
|
|
1247
|
-
width: hyphenWidth,
|
|
1248
|
-
penalty: HYPHENATION_PENALTY,
|
|
1249
|
-
flagged: true
|
|
1250
|
-
}, null, true, hyphenWidth);
|
|
1251
|
-
prev = end;
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
pushItem({
|
|
1255
|
-
type: "glue",
|
|
1256
|
-
width: 0,
|
|
1257
|
-
stretch: 1e6,
|
|
1258
|
-
shrink: 0
|
|
1259
|
-
}, null);
|
|
1260
|
-
pushItem({
|
|
1261
|
-
type: "penalty",
|
|
1262
|
-
width: 0,
|
|
1263
|
-
penalty: -1e4,
|
|
1264
|
-
flagged: false
|
|
1265
|
-
}, null);
|
|
1266
|
-
const { breaks } = breakLines(items, [firstLineWidth, otherWidth]);
|
|
1267
|
-
const lines = [];
|
|
1268
|
-
let start = 0;
|
|
1269
|
-
let isFirst = true;
|
|
1270
|
-
for (const breakIdx of breaks) {
|
|
1271
|
-
let st = start;
|
|
1272
|
-
let et = breakIdx;
|
|
1273
|
-
while (st < et && (fragmentTokens[st] === null || fragmentTokens[st]?.isSpace)) st++;
|
|
1274
|
-
while (et > st && (fragmentTokens[et - 1] === null || fragmentTokens[et - 1]?.isSpace)) et--;
|
|
1275
|
-
if (st < et) {
|
|
1276
|
-
const lineTokens = [];
|
|
1277
|
-
for (let i = st; i < et; i++) {
|
|
1278
|
-
const ft = fragmentTokens[i];
|
|
1279
|
-
if (ft) lineTokens.push(ft);
|
|
1280
|
-
}
|
|
1281
|
-
if (hyphenAtItem[breakIdx]) {
|
|
1282
|
-
const lastIdx = lineTokens.length - 1;
|
|
1283
|
-
const last = lineTokens[lastIdx];
|
|
1284
|
-
if (last && last.kind === "text") {
|
|
1285
|
-
const hyphenWidth = hyphenWidthAtItem[breakIdx];
|
|
1286
|
-
lineTokens[lastIdx] = {
|
|
1287
|
-
...last,
|
|
1288
|
-
text: last.text + "-",
|
|
1289
|
-
widthPt: last.widthPt + hyphenWidth
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
let contentWidth = 0;
|
|
1294
|
-
let maxSize = 0;
|
|
1295
|
-
let mathAscent = 0;
|
|
1296
|
-
let mathDescent = 0;
|
|
1297
|
-
for (const t of lineTokens) {
|
|
1298
|
-
contentWidth += t.widthPt;
|
|
1299
|
-
const sz = tokenLineSize(t);
|
|
1300
|
-
if (sz > maxSize) maxSize = sz;
|
|
1301
|
-
if (t.kind === "math") {
|
|
1302
|
-
mathAscent = Math.max(mathAscent, t.ascentPt);
|
|
1303
|
-
mathDescent = Math.max(mathDescent, t.descentPt);
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
lines.push({
|
|
1307
|
-
tokens: lineTokens,
|
|
1308
|
-
contentWidthPt: contentWidth,
|
|
1309
|
-
maxFontSizePt: maxSize,
|
|
1310
|
-
availableWidthPt: isFirst ? firstLineWidth : otherWidth,
|
|
1311
|
-
firstLine: isFirst,
|
|
1312
|
-
resolved,
|
|
1313
|
-
isLastInParagraph: false,
|
|
1314
|
-
mathAscentPt: mathAscent,
|
|
1315
|
-
mathDescentPt: mathDescent
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
start = breakIdx + 1;
|
|
1319
|
-
isFirst = false;
|
|
1320
|
-
}
|
|
1321
|
-
if (lines.length > 0) lines[lines.length - 1].isLastInParagraph = true;
|
|
1322
|
-
return lines;
|
|
1323
|
-
}
|
|
1324
|
-
function computeLineHeight(line, p) {
|
|
1325
|
-
const fontSize = line.maxFontSizePt || 12;
|
|
1326
|
-
const mathH = (line.mathAscentPt ?? 0) + (line.mathDescentPt ?? 0);
|
|
1327
|
-
const mathNeed = mathH > 0 ? mathH * 1.05 : 0;
|
|
1328
|
-
if (p.spacingLineRule === "exact" && p.spacingLineTwips > 0) return Math.max(p.spacingLineTwips * TWIP_TO_PT, mathNeed);
|
|
1329
|
-
if (p.spacingLineRule === "atLeast" && p.spacingLineTwips > 0) return Math.max(fontSize * 1.2, p.spacingLineTwips * TWIP_TO_PT, mathNeed);
|
|
1330
|
-
const multiple = p.spacingLineTwips > 0 ? p.spacingLineTwips / 240 : 1;
|
|
1331
|
-
return Math.max(fontSize * 1.2 * multiple, mathNeed);
|
|
1332
|
-
}
|
|
1333
|
-
function lineDescent(line) {
|
|
1334
|
-
const fs = line.maxFontSizePt || 12;
|
|
1335
|
-
return Math.max(fs * .2, line.mathDescentPt ?? 0);
|
|
1336
|
-
}
|
|
1337
|
-
function alignmentOffset(alignment, lineWidth, available) {
|
|
1338
|
-
if (alignment === "right") return Math.max(0, available - lineWidth);
|
|
1339
|
-
if (alignment === "center") return Math.max(0, (available - lineWidth) / 2);
|
|
1340
|
-
return 0;
|
|
1341
|
-
}
|
|
1342
|
-
function layoutTableBlock(table, options, fontResources, imageResources, contentWidth) {
|
|
1343
|
-
const columnWidthsPt = computeColumnWidths(table, options, fontResources, contentWidth);
|
|
1344
|
-
const mergeRoles = computeMergeRoles(table);
|
|
1345
|
-
const rows = [];
|
|
1346
|
-
const colCount = columnWidthsPt.length;
|
|
1347
|
-
let aboveBordersByCol = [];
|
|
1348
|
-
for (let r = 0; r < table.rows.length; r++) {
|
|
1349
|
-
const rl = layoutTableRow(table.rows[r], r, table.rows.length, colCount, table.properties, columnWidthsPt, options, fontResources, imageResources, mergeRoles[r] ?? [], aboveBordersByCol);
|
|
1350
|
-
const nextAbove = [];
|
|
1351
|
-
let ci = 0;
|
|
1352
|
-
for (const cell of table.rows[r].cells) {
|
|
1353
|
-
const span = Math.max(1, cell.properties.gridSpan ?? 1);
|
|
1354
|
-
for (let k = 0; k < span; k++) nextAbove[ci + k] = cell.properties.borders;
|
|
1355
|
-
ci += span;
|
|
1356
|
-
}
|
|
1357
|
-
aboveBordersByCol = nextAbove;
|
|
1358
|
-
const rp = table.rows[r].properties;
|
|
1359
|
-
rows.push(rp.isHeader || rp.pageBreakBefore ? {
|
|
1360
|
-
...rl,
|
|
1361
|
-
...rp.isHeader ? { isHeader: true } : {},
|
|
1362
|
-
...rp.pageBreakBefore ? { breakBefore: true } : {}
|
|
1363
|
-
} : rl);
|
|
1364
|
-
}
|
|
1365
|
-
const heightPt = rows.reduce((s, r) => s + r.heightPt, 0);
|
|
1366
|
-
const totalWidthPt = columnWidthsPt.reduce((s, w) => s + w, 0);
|
|
1367
|
-
return {
|
|
1368
|
-
kind: "table",
|
|
1369
|
-
rows,
|
|
1370
|
-
heightPt,
|
|
1371
|
-
totalWidthPt,
|
|
1372
|
-
colCount,
|
|
1373
|
-
xOffsetPt: tableXOffset(table.properties.alignment, contentWidth, totalWidthPt)
|
|
1374
|
-
};
|
|
1375
|
-
}
|
|
1376
|
-
function tableXOffset(alignment, contentWidth, totalWidthPt) {
|
|
1377
|
-
const slack = contentWidth - totalWidthPt;
|
|
1378
|
-
if (slack <= 0 || !alignment || alignment === "left") return 0;
|
|
1379
|
-
return alignment === "center" ? slack / 2 : slack;
|
|
1380
|
-
}
|
|
1381
|
-
function computeMergeRoles(table) {
|
|
1382
|
-
const out = table.rows.map((r) => new Array(r.cells.length).fill("standalone"));
|
|
1383
|
-
const colSlots = /* @__PURE__ */ new Map();
|
|
1384
|
-
for (let rowIdx = 0; rowIdx < table.rows.length; rowIdx++) {
|
|
1385
|
-
const row = table.rows[rowIdx];
|
|
1386
|
-
let colIdx = 0;
|
|
1387
|
-
for (let cellIdx = 0; cellIdx < row.cells.length; cellIdx++) {
|
|
1388
|
-
const cell = row.cells[cellIdx];
|
|
1389
|
-
let arr = colSlots.get(colIdx);
|
|
1390
|
-
if (!arr) {
|
|
1391
|
-
arr = [];
|
|
1392
|
-
colSlots.set(colIdx, arr);
|
|
1393
|
-
}
|
|
1394
|
-
arr.push({
|
|
1395
|
-
rowIdx,
|
|
1396
|
-
cellIdx,
|
|
1397
|
-
vMerge: cell.properties.vMerge
|
|
1398
|
-
});
|
|
1399
|
-
colIdx += Math.max(1, cell.properties.gridSpan ?? 1);
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
for (const slots of colSlots.values()) for (let i = 0; i < slots.length; i++) {
|
|
1403
|
-
const cur = slots[i];
|
|
1404
|
-
const next = slots[i + 1];
|
|
1405
|
-
const nextIsContinue = !!next && next.vMerge === "continue";
|
|
1406
|
-
let role = "standalone";
|
|
1407
|
-
if (cur.vMerge === "restart") role = nextIsContinue ? "start" : "standalone";
|
|
1408
|
-
else if (cur.vMerge === "continue") role = nextIsContinue ? "middle" : "end";
|
|
1409
|
-
out[cur.rowIdx][cur.cellIdx] = role;
|
|
1410
|
-
}
|
|
1411
|
-
return out;
|
|
1412
|
-
}
|
|
1413
|
-
function computeColumnWidths(table, options, fontResources, contentWidth) {
|
|
1414
|
-
let colCount = table.grid.length;
|
|
1415
|
-
for (const row of table.rows) {
|
|
1416
|
-
let rowCols = 0;
|
|
1417
|
-
for (const cell of row.cells) rowCols += Math.max(1, cell.properties.gridSpan ?? 1);
|
|
1418
|
-
if (rowCols > colCount) colCount = rowCols;
|
|
1419
|
-
}
|
|
1420
|
-
if (colCount === 0) return [];
|
|
1421
|
-
if (table.properties.layout === "fixed") return gridWidthsScaled(table, contentWidth, colCount);
|
|
1422
|
-
const colNaturalWidths = new Array(colCount).fill(0);
|
|
1423
|
-
for (const row of table.rows) {
|
|
1424
|
-
let colIdx = 0;
|
|
1425
|
-
for (let i = 0; i < row.cells.length; i++) {
|
|
1426
|
-
const cell = row.cells[i];
|
|
1427
|
-
const span = Math.max(1, cell.properties.gridSpan ?? 1);
|
|
1428
|
-
const padLeft = (cell.properties.margins?.leftTwips ?? table.properties.defaultCellMargins?.leftTwips ?? DEFAULT_CELL_PADDING_TWIPS) * TWIP_TO_PT;
|
|
1429
|
-
const padRight = (cell.properties.margins?.rightTwips ?? table.properties.defaultCellMargins?.rightTwips ?? DEFAULT_CELL_PADDING_TWIPS) * TWIP_TO_PT;
|
|
1430
|
-
let maxContent = 0;
|
|
1431
|
-
for (const el of cell.content) {
|
|
1432
|
-
if (el.kind !== "paragraph") continue;
|
|
1433
|
-
const w = measureSingleLine(el.paragraph, options, fontResources);
|
|
1434
|
-
if (w > maxContent) maxContent = w;
|
|
1435
|
-
}
|
|
1436
|
-
const perColumn = (maxContent + padLeft + padRight) / span;
|
|
1437
|
-
for (let k = 0; k < span && colIdx + k < colCount; k++) if (perColumn > colNaturalWidths[colIdx + k]) colNaturalWidths[colIdx + k] = perColumn;
|
|
1438
|
-
colIdx += span;
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
const naturalTotal = colNaturalWidths.reduce((s, w) => s + w, 0);
|
|
1442
|
-
const explicitTarget = explicitTableTargetWidth(table, contentWidth);
|
|
1443
|
-
if (naturalTotal === 0) return gridWidthsScaled(table, contentWidth, colCount);
|
|
1444
|
-
if (explicitTarget !== void 0 && explicitTarget > 0) {
|
|
1445
|
-
const scale = explicitTarget / naturalTotal;
|
|
1446
|
-
return colNaturalWidths.map((w) => w * scale);
|
|
1447
|
-
}
|
|
1448
|
-
if (naturalTotal > contentWidth) {
|
|
1449
|
-
const scale = contentWidth / naturalTotal;
|
|
1450
|
-
return colNaturalWidths.map((w) => w * scale);
|
|
1451
|
-
}
|
|
1452
|
-
return colNaturalWidths;
|
|
1453
|
-
}
|
|
1454
|
-
function gridWidthsScaled(table, contentWidth, colCount) {
|
|
1455
|
-
const totalGridTwips = table.grid.reduce((s, w) => s + w, 0);
|
|
1456
|
-
if (totalGridTwips > 0) {
|
|
1457
|
-
const target = totalTableTarget(table, contentWidth);
|
|
1458
|
-
return table.grid.map((w) => w / totalGridTwips * target);
|
|
1459
|
-
}
|
|
1460
|
-
const each = contentWidth / colCount;
|
|
1461
|
-
return new Array(colCount).fill(each);
|
|
1462
|
-
}
|
|
1463
|
-
function totalTableTarget(table, contentWidth) {
|
|
1464
|
-
const explicit = explicitTableTargetWidth(table, contentWidth);
|
|
1465
|
-
if (explicit !== void 0 && explicit > 0) return explicit;
|
|
1466
|
-
const sum = table.grid.reduce((s, g) => s + g, 0);
|
|
1467
|
-
if (sum > 0) return sum * TWIP_TO_PT;
|
|
1468
|
-
return contentWidth;
|
|
1469
|
-
}
|
|
1470
|
-
function explicitTableTargetWidth(table, contentWidth) {
|
|
1471
|
-
const w = table.properties.widthTwips;
|
|
1472
|
-
const type = table.properties.widthType;
|
|
1473
|
-
if (w !== void 0 && w > 0 && type === "dxa") return w * TWIP_TO_PT;
|
|
1474
|
-
if (w !== void 0 && type === "pct") return w / 5e3 * contentWidth;
|
|
1475
|
-
}
|
|
1476
|
-
function measureSingleLine(paragraph, options, fontResources) {
|
|
1477
|
-
let total = 0;
|
|
1478
|
-
for (const run of paragraph.runs) {
|
|
1479
|
-
const resolved = resolveRunProperties(run.properties, paragraph.properties, options.styles);
|
|
1480
|
-
const { variant } = options.registry.resolveByStyle(resolved.bold, resolved.italic);
|
|
1481
|
-
const font = fontResources.get(variant);
|
|
1482
|
-
const fontSizePt = resolved.fontSizeHalfPoints / 2;
|
|
1483
|
-
total += font.embedded.textWidthPt(run.text, fontSizePt);
|
|
1484
|
-
}
|
|
1485
|
-
return total;
|
|
1486
|
-
}
|
|
1487
|
-
function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidthsPt, options, fontResources, imageResources, rowMergeRoles, aboveBordersByCol) {
|
|
1488
|
-
const cells = [];
|
|
1489
|
-
const columnXOffsets = [];
|
|
1490
|
-
let cursorX = 0;
|
|
1491
|
-
let colIdx = 0;
|
|
1492
|
-
for (let cellIdx = 0; cellIdx < row.cells.length; cellIdx++) {
|
|
1493
|
-
const cell = row.cells[cellIdx];
|
|
1494
|
-
const span = Math.max(1, cell.properties.gridSpan ?? 1);
|
|
1495
|
-
let widthPt = 0;
|
|
1496
|
-
for (let k = 0; k < span && colIdx + k < columnWidthsPt.length; k++) widthPt += columnWidthsPt[colIdx + k];
|
|
1497
|
-
columnXOffsets.push(cursorX);
|
|
1498
|
-
const mergeRole = rowMergeRoles[cellIdx] ?? "standalone";
|
|
1499
|
-
const leftNeighbor = cellIdx > 0 ? row.cells[cellIdx - 1].properties.borders : void 0;
|
|
1500
|
-
const cellLayout = layoutTableCell(cell, tableProps, widthPt, options, fontResources, imageResources, rowIdx, colIdx, span, rowCount, colCount, mergeRole, leftNeighbor, aboveBordersByCol[colIdx]);
|
|
1501
|
-
cells.push(cellLayout);
|
|
1502
|
-
cursorX += widthPt;
|
|
1503
|
-
colIdx += span;
|
|
1504
|
-
}
|
|
1505
|
-
let heightPt = 0;
|
|
1506
|
-
for (const c of cells) if (c.totalHeightPt > heightPt) heightPt = c.totalHeightPt;
|
|
1507
|
-
if (row.properties.heightTwips && row.properties.heightRule === "exact") heightPt = row.properties.heightTwips * TWIP_TO_PT;
|
|
1508
|
-
else if (row.properties.heightTwips && row.properties.heightRule === "atLeast") heightPt = Math.max(heightPt, row.properties.heightTwips * TWIP_TO_PT);
|
|
1509
|
-
return {
|
|
1510
|
-
heightPt,
|
|
1511
|
-
cells,
|
|
1512
|
-
columnXOffsets,
|
|
1513
|
-
rowIdx,
|
|
1514
|
-
rowCount
|
|
1515
|
-
};
|
|
1516
|
-
}
|
|
1517
|
-
function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imageResources, rowIdx, colStart, colSpan, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders) {
|
|
1518
|
-
const cellMar = cell.properties.margins;
|
|
1519
|
-
const tableMar = tableProps.defaultCellMargins;
|
|
1520
|
-
const padTopPt = (cellMar?.topTwips ?? tableMar?.topTwips ?? 0) * TWIP_TO_PT;
|
|
1521
|
-
const padBottomPt = (cellMar?.bottomTwips ?? tableMar?.bottomTwips ?? 0) * TWIP_TO_PT;
|
|
1522
|
-
const padLeftPt = (cellMar?.leftTwips ?? tableMar?.leftTwips ?? DEFAULT_CELL_PADDING_TWIPS) * TWIP_TO_PT;
|
|
1523
|
-
const padRightPt = (cellMar?.rightTwips ?? tableMar?.rightTwips ?? DEFAULT_CELL_PADDING_TWIPS) * TWIP_TO_PT;
|
|
1524
|
-
const innerWidth = Math.max(1, widthPt - padLeftPt - padRightPt);
|
|
1525
|
-
const lines = [];
|
|
1526
|
-
const nestedTables = [];
|
|
1527
|
-
let contentHeightPt = 0;
|
|
1528
|
-
if (mergeRole !== "middle" && mergeRole !== "end") {
|
|
1529
|
-
for (const el of cell.content) if (el.kind === "paragraph") {
|
|
1530
|
-
const block = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, innerWidth);
|
|
1531
|
-
for (const line of block.lines) {
|
|
1532
|
-
lines.push(line);
|
|
1533
|
-
contentHeightPt += computeLineHeight(line, block.resolved);
|
|
1534
|
-
}
|
|
1535
|
-
contentHeightPt += block.spacingAfterPt;
|
|
1536
|
-
} else if (el.kind === "table") {
|
|
1537
|
-
const nested = layoutTableBlock(el.table, options, fontResources, imageResources, innerWidth);
|
|
1538
|
-
nestedTables.push(nested);
|
|
1539
|
-
contentHeightPt += nested.heightPt;
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
const colEnd = colStart + colSpan - 1;
|
|
1543
|
-
const borders = resolveCellBorders(cell.properties.borders, tableProps.borders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders);
|
|
1544
|
-
const totalHeightPt = mergeRole === "middle" || mergeRole === "end" ? 0 : padTopPt + contentHeightPt + padBottomPt;
|
|
1545
|
-
return {
|
|
1546
|
-
widthPt,
|
|
1547
|
-
padTopPt,
|
|
1548
|
-
padRightPt,
|
|
1549
|
-
padBottomPt,
|
|
1550
|
-
padLeftPt,
|
|
1551
|
-
borders,
|
|
1552
|
-
...cell.properties.shading?.colorHex ? { shadingColorHex: cell.properties.shading.colorHex } : {},
|
|
1553
|
-
lines,
|
|
1554
|
-
...nestedTables.length > 0 ? { nestedTables } : {},
|
|
1555
|
-
contentHeightPt,
|
|
1556
|
-
totalHeightPt,
|
|
1557
|
-
colStart,
|
|
1558
|
-
colSpan,
|
|
1559
|
-
mergeRole
|
|
1560
|
-
};
|
|
1561
|
-
}
|
|
1562
|
-
var BORDER_STYLE_RANK = {
|
|
1563
|
-
double: 5,
|
|
1564
|
-
thick: 4,
|
|
1565
|
-
single: 3,
|
|
1566
|
-
dashed: 2,
|
|
1567
|
-
dotted: 1,
|
|
1568
|
-
none: 0
|
|
1569
|
-
};
|
|
1570
|
-
function borderWeight(b) {
|
|
1571
|
-
if (!b || b.style === "none") return 0;
|
|
1572
|
-
return b.sizeEighthPt ?? 4;
|
|
1573
|
-
}
|
|
1574
|
-
function heavierBorder(a, b) {
|
|
1575
|
-
const wa = borderWeight(a);
|
|
1576
|
-
const wb = borderWeight(b);
|
|
1577
|
-
if (wa !== wb) return wa > wb ? a : b;
|
|
1578
|
-
if (wa === 0) return void 0;
|
|
1579
|
-
return BORDER_STYLE_RANK[a.style] >= BORDER_STYLE_RANK[b.style] ? a : b;
|
|
1580
|
-
}
|
|
1581
|
-
function resolveCellBorders(cellBorders, tableBorders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighbor, aboveNeighbor) {
|
|
1582
|
-
const isFirstRow = rowIdx === 0;
|
|
1583
|
-
const isLastRow = rowIdx === rowCount - 1;
|
|
1584
|
-
const isFirstCol = colStart === 0;
|
|
1585
|
-
const isLastCol = colEnd === colCount - 1;
|
|
1586
|
-
const out = {};
|
|
1587
|
-
const ownTop = cellBorders?.top ?? (isFirstRow ? tableBorders?.top : tableBorders?.insideH);
|
|
1588
|
-
const ownBottom = cellBorders?.bottom ?? (isLastRow ? tableBorders?.bottom : tableBorders?.insideH);
|
|
1589
|
-
const ownLeft = cellBorders?.left ?? (isFirstCol ? tableBorders?.left : tableBorders?.insideV);
|
|
1590
|
-
const ownRight = cellBorders?.right ?? (isLastCol ? tableBorders?.right : tableBorders?.insideV);
|
|
1591
|
-
const aboveBottom = aboveNeighbor ? aboveNeighbor.bottom ?? tableBorders?.insideH : void 0;
|
|
1592
|
-
const leftRight = leftNeighbor ? leftNeighbor.right ?? tableBorders?.insideV : void 0;
|
|
1593
|
-
const top = isFirstRow ? ownTop : heavierBorder(ownTop, aboveBottom);
|
|
1594
|
-
const left = isFirstCol ? ownLeft : heavierBorder(ownLeft, leftRight);
|
|
1595
|
-
const bottom = ownBottom;
|
|
1596
|
-
const right = ownRight;
|
|
1597
|
-
const suppressTop = mergeRole === "middle" || mergeRole === "end";
|
|
1598
|
-
const suppressBottom = mergeRole === "start" || mergeRole === "middle";
|
|
1599
|
-
if (top && !suppressTop) out.top = top;
|
|
1600
|
-
if (bottom && !suppressBottom) out.bottom = bottom;
|
|
1601
|
-
if (left) out.left = left;
|
|
1602
|
-
if (right) out.right = right;
|
|
1603
|
-
return out;
|
|
1604
|
-
}
|
|
1605
|
-
function paragraphStructType(resolved) {
|
|
1606
|
-
const lvl = resolved.outlineLevel;
|
|
1607
|
-
if (lvl !== void 0 && lvl >= 0 && lvl <= 8) return `H${Math.min(lvl, 5) + 1}`;
|
|
1608
|
-
return headingFromStyleId(resolved.styleId) ?? "P";
|
|
1609
|
-
}
|
|
1610
|
-
function headingFromStyleId(styleId) {
|
|
1611
|
-
if (!styleId) return null;
|
|
1612
|
-
const m = /^Heading\s*([1-9])$/i.exec(styleId);
|
|
1613
|
-
if (m) return `H${Math.min(Number(m[1]), 6)}`;
|
|
1614
|
-
if (/^(Title|Subtitle)$/i.test(styleId)) return "H1";
|
|
1615
|
-
return null;
|
|
1616
|
-
}
|
|
1617
|
-
function dominantParagraphLang(lines) {
|
|
1618
|
-
const counts = /* @__PURE__ */ new Map();
|
|
1619
|
-
for (const line of lines) for (const tok of line.tokens) {
|
|
1620
|
-
if (tok.kind !== "text") continue;
|
|
1621
|
-
const l = tok.resolvedRun.lang;
|
|
1622
|
-
if (l) counts.set(l, (counts.get(l) ?? 0) + tok.text.length);
|
|
1623
|
-
}
|
|
1624
|
-
let best;
|
|
1625
|
-
let bestN = 0;
|
|
1626
|
-
for (const [l, n] of counts) if (n > bestN) {
|
|
1627
|
-
best = l;
|
|
1628
|
-
bestN = n;
|
|
1629
|
-
}
|
|
1630
|
-
return best;
|
|
1631
|
-
}
|
|
1632
|
-
function createFigure(builder, alt, fallback) {
|
|
1633
|
-
const node = builder.create("Figure", builder.root);
|
|
1634
|
-
node.alt = alt && alt.trim().length > 0 ? alt : fallback;
|
|
1635
|
-
return node.id;
|
|
1636
|
-
}
|
|
1637
|
-
function listItemParagraphNode(builder, stack, list) {
|
|
1638
|
-
const lvl = list.level;
|
|
1639
|
-
while (stack.length > 0) {
|
|
1640
|
-
const t = stack[stack.length - 1];
|
|
1641
|
-
if (t.level > lvl || t.level === lvl && t.numId !== list.numId) stack.pop();
|
|
1642
|
-
else break;
|
|
1643
|
-
}
|
|
1644
|
-
let frame = stack[stack.length - 1];
|
|
1645
|
-
if (!frame || frame.level < lvl) {
|
|
1646
|
-
const parent = frame?.lbody ?? builder.root;
|
|
1647
|
-
const listNode = builder.create("L", parent);
|
|
1648
|
-
frame = {
|
|
1649
|
-
level: lvl,
|
|
1650
|
-
numId: list.numId,
|
|
1651
|
-
listNode,
|
|
1652
|
-
lbody: null
|
|
1653
|
-
};
|
|
1654
|
-
stack.push(frame);
|
|
1655
|
-
}
|
|
1656
|
-
const li = builder.create("LI", frame.listNode);
|
|
1657
|
-
const lbody = builder.create("LBody", li);
|
|
1658
|
-
frame.lbody = lbody;
|
|
1659
|
-
return builder.create("P", lbody).id;
|
|
1660
|
-
}
|
|
1661
|
-
function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
|
|
1662
|
-
if (sectionCtxs.length === 0) return [];
|
|
1663
|
-
const pages = [];
|
|
1664
|
-
let ctx = sectionCtxs[0];
|
|
1665
|
-
let secIdx = 0;
|
|
1666
|
-
let pageInSection = 0;
|
|
1667
|
-
let globalPageIdx = 0;
|
|
1668
|
-
let current = [];
|
|
1669
|
-
let pendingPageBreak = false;
|
|
1670
|
-
let cursorY = ctx.pageHeight - ctx.marginTop;
|
|
1671
|
-
const listStack = [];
|
|
1672
|
-
const flushPage = (force = false) => {
|
|
1673
|
-
if (current.length === 0 && !force) return;
|
|
1674
|
-
const band = bandForPage(pageInSection, globalPageIdx, ctx.titlePg, ctx.evenAndOddHeaders);
|
|
1675
|
-
const header = pickBand(ctx.headerSet, band);
|
|
1676
|
-
const footer = pickBand(ctx.footerSet, band);
|
|
1677
|
-
pages.push({
|
|
1678
|
-
commands: [
|
|
1679
|
-
...header,
|
|
1680
|
-
...current,
|
|
1681
|
-
...footer
|
|
1682
|
-
],
|
|
1683
|
-
width: ctx.pageWidth,
|
|
1684
|
-
height: ctx.pageHeight
|
|
1685
|
-
});
|
|
1686
|
-
current = [];
|
|
1687
|
-
pageInSection++;
|
|
1688
|
-
globalPageIdx++;
|
|
1689
|
-
cursorY = ctx.pageHeight - ctx.marginTop;
|
|
1690
|
-
};
|
|
1691
|
-
for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
|
|
1692
|
-
while (secIdx < sectionCtxs.length - 1 && blockIdx >= ctx.endIndex) {
|
|
1693
|
-
flushPage();
|
|
1694
|
-
secIdx++;
|
|
1695
|
-
ctx = sectionCtxs[secIdx];
|
|
1696
|
-
pageInSection = 0;
|
|
1697
|
-
cursorY = ctx.pageHeight - ctx.marginTop;
|
|
1698
|
-
}
|
|
1699
|
-
const block = blocks[blockIdx];
|
|
1700
|
-
if (pendingPageBreak) {
|
|
1701
|
-
pendingPageBreak = false;
|
|
1702
|
-
if (current.length > 0) flushPage();
|
|
1703
|
-
}
|
|
1704
|
-
if (builder && !(block.kind === "paragraph" && block.list)) listStack.length = 0;
|
|
1705
|
-
if (block.kind === "paragraph") {
|
|
1706
|
-
if (block.resolved.pageBreakBefore && current.length > 0) flushPage();
|
|
1707
|
-
cursorY -= block.spacingBeforePt;
|
|
1708
|
-
let structId;
|
|
1709
|
-
if (builder) {
|
|
1710
|
-
structId = block.list ? listItemParagraphNode(builder, listStack, block.list) : builder.create(paragraphStructType(block.resolved), builder.root).id;
|
|
1711
|
-
const lang = dominantParagraphLang(block.lines);
|
|
1712
|
-
if (lang && lang !== defaultLang) builder.node(structId).lang = lang;
|
|
1713
|
-
}
|
|
1714
|
-
for (const line of block.lines) {
|
|
1715
|
-
const h = computeLineHeight(line, block.resolved);
|
|
1716
|
-
if (cursorY - h < ctx.marginBottom && current.length > 0) flushPage();
|
|
1717
|
-
cursorY -= h;
|
|
1718
|
-
const indentLeft = block.resolved.indentLeftTwips * TWIP_TO_PT + (line.firstLine ? block.resolved.indentFirstLineTwips * TWIP_TO_PT : 0);
|
|
1719
|
-
const offset = alignmentOffset(block.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
|
|
1720
|
-
current.push({
|
|
1721
|
-
type: "line",
|
|
1722
|
-
line,
|
|
1723
|
-
originX: ctx.marginLeft + indentLeft + offset,
|
|
1724
|
-
baselineY: cursorY + lineDescent(line),
|
|
1725
|
-
...structId !== void 0 ? { structId } : {}
|
|
1726
|
-
});
|
|
1727
|
-
}
|
|
1728
|
-
cursorY -= block.spacingAfterPt;
|
|
1729
|
-
if (block.pageBreakAfter) pendingPageBreak = true;
|
|
1730
|
-
} else if (block.kind === "image") {
|
|
1731
|
-
const figId = builder ? createFigure(builder, block.altText, "Image") : void 0;
|
|
1732
|
-
cursorY -= block.spacingBeforePt;
|
|
1733
|
-
if (cursorY - block.heightPt < ctx.marginBottom && current.length > 0) flushPage();
|
|
1734
|
-
cursorY -= block.heightPt;
|
|
1735
|
-
current.push({
|
|
1736
|
-
type: "image",
|
|
1737
|
-
x: ctx.marginLeft,
|
|
1738
|
-
y: cursorY,
|
|
1739
|
-
width: block.widthPt,
|
|
1740
|
-
height: block.heightPt,
|
|
1741
|
-
imageResourceName: block.resourceName,
|
|
1742
|
-
...figId !== void 0 ? { structId: figId } : {}
|
|
1743
|
-
});
|
|
1744
|
-
cursorY -= block.spacingAfterPt;
|
|
1745
|
-
} else if (block.kind === "shape") {
|
|
1746
|
-
const figId = builder ? createFigure(builder, block.altText, "Shape") : void 0;
|
|
1747
|
-
cursorY -= block.spacingBeforePt;
|
|
1748
|
-
if (cursorY - block.heightPt < ctx.marginBottom && current.length > 0) flushPage();
|
|
1749
|
-
cursorY -= block.heightPt;
|
|
1750
|
-
const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, ctx.contentWidth);
|
|
1751
|
-
const x = ctx.marginLeft + offset;
|
|
1752
|
-
const transform = buildShapeTransform(x, cursorY, block.widthPt, block.heightPt, block.rotation60k, block.flipH, block.flipV);
|
|
1753
|
-
current.push({
|
|
1754
|
-
type: "shape",
|
|
1755
|
-
shape: {
|
|
1756
|
-
paths: block.paths,
|
|
1757
|
-
...block.fillColorHex ? { fillColorHex: block.fillColorHex } : {},
|
|
1758
|
-
...block.stroke ? { stroke: block.stroke } : {},
|
|
1759
|
-
transform
|
|
1760
|
-
},
|
|
1761
|
-
...figId !== void 0 ? { structId: figId } : {}
|
|
1762
|
-
});
|
|
1763
|
-
if (block.textLines.length > 0) {
|
|
1764
|
-
const shapeBottom = cursorY;
|
|
1765
|
-
const shapeTop = cursorY + block.heightPt;
|
|
1766
|
-
const innerWidth = Math.max(1, block.widthPt - block.insetLeftPt - block.insetRightPt);
|
|
1767
|
-
let textY;
|
|
1768
|
-
if (block.anchor === "b") textY = shapeBottom + block.insetBottomPt + block.textHeightPt;
|
|
1769
|
-
else if (block.anchor === "ctr") textY = shapeBottom + (block.heightPt + block.textHeightPt) / 2;
|
|
1770
|
-
else textY = shapeTop - block.insetTopPt;
|
|
1771
|
-
for (const line of block.textLines) {
|
|
1772
|
-
const h = computeLineHeight(line, line.resolved);
|
|
1773
|
-
textY -= h;
|
|
1774
|
-
const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, innerWidth);
|
|
1775
|
-
current.push({
|
|
1776
|
-
type: "line",
|
|
1777
|
-
line,
|
|
1778
|
-
originX: x + block.insetLeftPt + lineOffset,
|
|
1779
|
-
baselineY: textY + lineDescent(line),
|
|
1780
|
-
...figId !== void 0 ? { structId: figId } : {}
|
|
1781
|
-
});
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
cursorY -= block.spacingAfterPt;
|
|
1785
|
-
} else if (block.kind === "chart") {
|
|
1786
|
-
const figId = builder ? createFigure(builder, block.altText, "Chart") : void 0;
|
|
1787
|
-
const fig = figId !== void 0 ? { structId: figId } : {};
|
|
1788
|
-
cursorY -= block.spacingBeforePt;
|
|
1789
|
-
if (cursorY - block.heightPt < ctx.marginBottom && current.length > 0) flushPage();
|
|
1790
|
-
cursorY -= block.heightPt;
|
|
1791
|
-
const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, ctx.contentWidth);
|
|
1792
|
-
const x = ctx.marginLeft + offset;
|
|
1793
|
-
const y = cursorY;
|
|
1794
|
-
for (const s of block.layout.shapes) current.push({
|
|
1795
|
-
type: "shape",
|
|
1796
|
-
shape: {
|
|
1797
|
-
paths: s.paths,
|
|
1798
|
-
...s.fillColorHex ? { fillColorHex: s.fillColorHex } : {},
|
|
1799
|
-
...s.stroke ? { stroke: s.stroke } : {},
|
|
1800
|
-
transform: [
|
|
1801
|
-
1,
|
|
1802
|
-
0,
|
|
1803
|
-
0,
|
|
1804
|
-
1,
|
|
1805
|
-
x,
|
|
1806
|
-
y
|
|
1807
|
-
]
|
|
1808
|
-
},
|
|
1809
|
-
...fig
|
|
1810
|
-
});
|
|
1811
|
-
for (const t of block.layout.texts) current.push({
|
|
1812
|
-
type: "line",
|
|
1813
|
-
line: t.line,
|
|
1814
|
-
originX: x + t.x,
|
|
1815
|
-
baselineY: y + t.y,
|
|
1816
|
-
...fig
|
|
1817
|
-
});
|
|
1818
|
-
cursorY -= block.spacingAfterPt;
|
|
1819
|
-
} else {
|
|
1820
|
-
const colCount = block.rows.reduce((max, r) => Math.max(max, r.cells.reduce((s, c) => Math.max(s, c.colStart + c.colSpan), 0)), 0);
|
|
1821
|
-
const headerRows = [];
|
|
1822
|
-
for (const r of block.rows) if (r.isHeader) headerRows.push(r);
|
|
1823
|
-
else break;
|
|
1824
|
-
const headerHeightPt = headerRows.reduce((s, r) => s + r.heightPt, 0);
|
|
1825
|
-
const tableNode = builder ? builder.create("Table", builder.root) : void 0;
|
|
1826
|
-
const tableX = ctx.marginLeft + block.xOffsetPt;
|
|
1827
|
-
for (let ri = 0; ri < block.rows.length; ri++) {
|
|
1828
|
-
const row = block.rows[ri];
|
|
1829
|
-
const isLeadingHeader = ri < headerRows.length;
|
|
1830
|
-
let cellStructIds;
|
|
1831
|
-
if (builder && tableNode) {
|
|
1832
|
-
const b = builder;
|
|
1833
|
-
const tr = b.create("TR", tableNode);
|
|
1834
|
-
cellStructIds = row.cells.map((cell) => {
|
|
1835
|
-
if (cell.mergeRole === "middle" || cell.mergeRole === "end") return void 0;
|
|
1836
|
-
const cellNode = b.create(row.isHeader ? "TH" : "TD", tr);
|
|
1837
|
-
if (row.isHeader) cellNode.scope = "Column";
|
|
1838
|
-
if (cell.colSpan > 1) cellNode.colSpan = cell.colSpan;
|
|
1839
|
-
const rowSpan = tableCellRowSpan(block.rows, ri, cell.colStart);
|
|
1840
|
-
if (rowSpan > 1) cellNode.rowSpan = rowSpan;
|
|
1841
|
-
return b.create("P", cellNode).id;
|
|
1842
|
-
});
|
|
1843
|
-
}
|
|
1844
|
-
const chunks = row.heightPt > ctx.pageContentHeight ? splitRowIntoChunks(row, ctx.pageContentHeight) : [row];
|
|
1845
|
-
for (let ci = 0; ci < chunks.length; ci++) {
|
|
1846
|
-
const chunk = chunks[ci];
|
|
1847
|
-
const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && current.length > 0;
|
|
1848
|
-
const overflow = cursorY - chunk.heightPt < ctx.marginBottom && current.length > 0;
|
|
1849
|
-
if (forcedBreak || overflow) {
|
|
1850
|
-
flushPage();
|
|
1851
|
-
if (!isLeadingHeader && headerRows.length > 0 && cursorY - headerHeightPt - chunk.heightPt >= ctx.marginBottom) for (const hr of headerRows) {
|
|
1852
|
-
emitRowChunk(current, hr, tableX, cursorY, colCount, void 0);
|
|
1853
|
-
cursorY -= hr.heightPt;
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
|
-
emitRowChunk(current, chunk, tableX, cursorY, colCount, cellStructIds);
|
|
1857
|
-
cursorY -= chunk.heightPt;
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
flushPage();
|
|
1863
|
-
if (pages.length === 0) flushPage(true);
|
|
1864
|
-
return pages;
|
|
1865
|
-
}
|
|
1866
|
-
function tableCellRowSpan(rows, startRowIdx, colStart) {
|
|
1867
|
-
let span = 1;
|
|
1868
|
-
for (let r = startRowIdx + 1; r < rows.length; r++) {
|
|
1869
|
-
const cont = rows[r].cells.find((c) => c.colStart === colStart && (c.mergeRole === "middle" || c.mergeRole === "end"));
|
|
1870
|
-
if (!cont) break;
|
|
1871
|
-
span++;
|
|
1872
|
-
if (cont.mergeRole === "end") break;
|
|
1873
|
-
}
|
|
1874
|
-
return span;
|
|
1875
|
-
}
|
|
1876
|
-
function emitRowChunk(out, row, marginLeft, cursorY, colCount, cellStructIds) {
|
|
1877
|
-
const rowTop = cursorY;
|
|
1878
|
-
const rowBottom = cursorY - row.heightPt;
|
|
1879
|
-
for (let i = 0; i < row.cells.length; i++) {
|
|
1880
|
-
const cell = row.cells[i];
|
|
1881
|
-
const cellX = marginLeft + (row.columnXOffsets[i] ?? 0);
|
|
1882
|
-
const structId = cellStructIds?.[i];
|
|
1883
|
-
if (cell.shadingColorHex && cell.mergeRole !== "middle" && cell.mergeRole !== "end") out.push({
|
|
1884
|
-
type: "fill",
|
|
1885
|
-
x: cellX,
|
|
1886
|
-
y: rowBottom,
|
|
1887
|
-
width: cell.widthPt,
|
|
1888
|
-
height: row.heightPt,
|
|
1889
|
-
fillColorHex: cell.shadingColorHex
|
|
1890
|
-
});
|
|
1891
|
-
emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, row.rowIdx, row.rowCount, colCount);
|
|
1892
|
-
if (cell.mergeRole === "middle" || cell.mergeRole === "end") continue;
|
|
1893
|
-
let textY = rowTop - cell.padTopPt;
|
|
1894
|
-
for (const line of cell.lines) {
|
|
1895
|
-
const h = computeLineHeight(line, line.resolved);
|
|
1896
|
-
textY -= h;
|
|
1897
|
-
const offset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, cell.widthPt - cell.padLeftPt - cell.padRightPt);
|
|
1898
|
-
out.push({
|
|
1899
|
-
type: "line",
|
|
1900
|
-
line,
|
|
1901
|
-
originX: cellX + cell.padLeftPt + offset,
|
|
1902
|
-
baselineY: textY + lineDescent(line),
|
|
1903
|
-
...structId !== void 0 ? { structId } : {}
|
|
1904
|
-
});
|
|
1905
|
-
}
|
|
1906
|
-
if (cell.nestedTables) {
|
|
1907
|
-
const nestedX = cellX + cell.padLeftPt;
|
|
1908
|
-
for (const nt of cell.nestedTables) for (const nrow of nt.rows) {
|
|
1909
|
-
const nestedIds = structId !== void 0 ? nrow.cells.map(() => structId) : void 0;
|
|
1910
|
-
emitRowChunk(out, nrow, nestedX, textY, nt.colCount, nestedIds);
|
|
1911
|
-
textY -= nrow.heightPt;
|
|
1912
|
-
}
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
function splitRowIntoChunks(row, capacity) {
|
|
1917
|
-
if (row.cells.some((c) => c.nestedTables && c.nestedTables.length > 0)) return [row];
|
|
1918
|
-
const queues = row.cells.map((c) => ({
|
|
1919
|
-
remaining: [...c.lines],
|
|
1920
|
-
template: c
|
|
1921
|
-
}));
|
|
1922
|
-
const anyHasLines = () => queues.some((q) => q.template.mergeRole === "middle" || q.template.mergeRole === "end" ? false : q.remaining.length > 0);
|
|
1923
|
-
const out = [];
|
|
1924
|
-
let isFirst = true;
|
|
1925
|
-
let safety = 1e3;
|
|
1926
|
-
while (anyHasLines() && safety-- > 0) {
|
|
1927
|
-
const chunkCells = [];
|
|
1928
|
-
let chunkHeight = 0;
|
|
1929
|
-
for (let i = 0; i < queues.length; i++) {
|
|
1930
|
-
const q = queues[i];
|
|
1931
|
-
const tpl = q.template;
|
|
1932
|
-
if (tpl.mergeRole === "middle" || tpl.mergeRole === "end") {
|
|
1933
|
-
chunkCells.push(tpl);
|
|
1934
|
-
continue;
|
|
1935
|
-
}
|
|
1936
|
-
const padTop = isFirst ? tpl.padTopPt : 0;
|
|
1937
|
-
const capacityForLines = Math.max(0, capacity - padTop);
|
|
1938
|
-
const taken = [];
|
|
1939
|
-
let takenHeight = 0;
|
|
1940
|
-
while (q.remaining.length > 0) {
|
|
1941
|
-
const next = q.remaining[0];
|
|
1942
|
-
const lh = computeLineHeight(next, next.resolved);
|
|
1943
|
-
if (taken.length > 0 && takenHeight + lh > capacityForLines) break;
|
|
1944
|
-
taken.push(next);
|
|
1945
|
-
takenHeight += lh;
|
|
1946
|
-
q.remaining.shift();
|
|
1947
|
-
}
|
|
1948
|
-
const cellHeight = padTop + takenHeight;
|
|
1949
|
-
const chunkCell = {
|
|
1950
|
-
...tpl,
|
|
1951
|
-
padTopPt: padTop,
|
|
1952
|
-
padBottomPt: 0,
|
|
1953
|
-
lines: taken,
|
|
1954
|
-
contentHeightPt: takenHeight,
|
|
1955
|
-
totalHeightPt: cellHeight
|
|
1956
|
-
};
|
|
1957
|
-
chunkCells.push(chunkCell);
|
|
1958
|
-
if (cellHeight > chunkHeight) chunkHeight = cellHeight;
|
|
1959
|
-
}
|
|
1960
|
-
const isLast = !anyHasLines();
|
|
1961
|
-
if (isLast) for (let i = 0; i < chunkCells.length; i++) {
|
|
1962
|
-
const tpl = queues[i].template;
|
|
1963
|
-
if (tpl.mergeRole === "middle" || tpl.mergeRole === "end") continue;
|
|
1964
|
-
const cell = chunkCells[i];
|
|
1965
|
-
const total = cell.padTopPt + cell.contentHeightPt + tpl.padBottomPt;
|
|
1966
|
-
chunkCells[i] = {
|
|
1967
|
-
...cell,
|
|
1968
|
-
padBottomPt: tpl.padBottomPt,
|
|
1969
|
-
totalHeightPt: total
|
|
1970
|
-
};
|
|
1971
|
-
if (total > chunkHeight) chunkHeight = total;
|
|
1972
|
-
}
|
|
1973
|
-
for (let i = 0; i < chunkCells.length; i++) {
|
|
1974
|
-
const cell = chunkCells[i];
|
|
1975
|
-
const src = cell.borders;
|
|
1976
|
-
const borders = {
|
|
1977
|
-
...isFirst && src.top ? { top: src.top } : {},
|
|
1978
|
-
...isLast && src.bottom ? { bottom: src.bottom } : {},
|
|
1979
|
-
...src.left ? { left: src.left } : {},
|
|
1980
|
-
...src.right ? { right: src.right } : {}
|
|
1981
|
-
};
|
|
1982
|
-
chunkCells[i] = {
|
|
1983
|
-
...cell,
|
|
1984
|
-
borders
|
|
1985
|
-
};
|
|
1986
|
-
}
|
|
1987
|
-
out.push({
|
|
1988
|
-
heightPt: chunkHeight,
|
|
1989
|
-
cells: chunkCells,
|
|
1990
|
-
columnXOffsets: row.columnXOffsets,
|
|
1991
|
-
rowIdx: row.rowIdx,
|
|
1992
|
-
rowCount: row.rowCount
|
|
1993
|
-
});
|
|
1994
|
-
isFirst = false;
|
|
1995
|
-
}
|
|
1996
|
-
return out.length > 0 ? out : [row];
|
|
1997
|
-
}
|
|
1998
|
-
function emitCellBorders(out, cell, cellX, cellY, rowHeight, rowIdx, rowCount, colCount) {
|
|
1999
|
-
const pushSide = (side, border) => {
|
|
2000
|
-
if (!border || border.style === "none") return;
|
|
2001
|
-
const sz = (border.sizeEighthPt ?? DEFAULT_BORDER_SIZE_EIGHTH) * EIGHTH_PT;
|
|
2002
|
-
out.push({
|
|
2003
|
-
type: "border",
|
|
2004
|
-
side,
|
|
2005
|
-
x: cellX,
|
|
2006
|
-
y: cellY,
|
|
2007
|
-
width: cell.widthPt,
|
|
2008
|
-
height: rowHeight,
|
|
2009
|
-
borderSizePt: sz,
|
|
2010
|
-
borderColorHex: border.colorHex ?? "000000"
|
|
2011
|
-
});
|
|
2012
|
-
};
|
|
2013
|
-
pushSide("top", cell.borders.top);
|
|
2014
|
-
pushSide("left", cell.borders.left);
|
|
2015
|
-
if (rowIdx === rowCount - 1) pushSide("bottom", cell.borders.bottom);
|
|
2016
|
-
if (cell.colStart + cell.colSpan - 1 === colCount - 1) pushSide("right", cell.borders.right);
|
|
2017
|
-
}
|
|
2018
|
-
function emitTaggedRuns(out, cmds, tagging, body) {
|
|
2019
|
-
if (!tagging) {
|
|
2020
|
-
for (const c of cmds) body(c);
|
|
2021
|
-
return;
|
|
2022
|
-
}
|
|
2023
|
-
let openKey = null;
|
|
2024
|
-
const close = () => {
|
|
2025
|
-
if (openKey !== null) {
|
|
2026
|
-
out.push("EMC");
|
|
2027
|
-
openKey = null;
|
|
2028
|
-
}
|
|
2029
|
-
};
|
|
2030
|
-
for (const c of cmds) {
|
|
2031
|
-
const key = c.structId !== void 0 ? `f${c.structId}` : c.artifact === "pagination" ? "p" : "a";
|
|
2032
|
-
if (key !== openKey) {
|
|
2033
|
-
close();
|
|
2034
|
-
if (c.structId !== void 0) {
|
|
2035
|
-
const mcid = tagging.next++;
|
|
2036
|
-
tagging.assigned = true;
|
|
2037
|
-
tagging.record(c.structId, mcid);
|
|
2038
|
-
out.push(`/${tagging.tagFor(c.structId)} <</MCID ${mcid}>> BDC`);
|
|
2039
|
-
} else if (c.artifact === "pagination") out.push("/Artifact <</Type /Pagination>> BDC");
|
|
2040
|
-
else out.push("/Artifact BMC");
|
|
2041
|
-
openKey = key;
|
|
2042
|
-
}
|
|
2043
|
-
body(c);
|
|
2044
|
-
}
|
|
2045
|
-
close();
|
|
2046
|
-
}
|
|
2047
|
-
function emitPageContent(commands, tagging) {
|
|
2048
|
-
if (commands.length === 0) return new Uint8Array(0);
|
|
2049
|
-
const out = [];
|
|
2050
|
-
const fills = commands.filter((c) => c.type === "fill");
|
|
2051
|
-
if (fills.length > 0) {
|
|
2052
|
-
if (tagging) out.push("/Artifact BMC");
|
|
2053
|
-
out.push("q");
|
|
2054
|
-
let lastColor = "";
|
|
2055
|
-
for (const f of fills) {
|
|
2056
|
-
const color = f.fillColorHex;
|
|
2057
|
-
if (color !== lastColor) {
|
|
2058
|
-
const [r, g, b] = hexToRgb01(color);
|
|
2059
|
-
out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
|
|
2060
|
-
lastColor = color;
|
|
2061
|
-
}
|
|
2062
|
-
out.push(`${formatNumber(f.x)} ${formatNumber(f.y)} ${formatNumber(f.width)} ${formatNumber(f.height)} re`);
|
|
2063
|
-
out.push("f");
|
|
2064
|
-
}
|
|
2065
|
-
out.push("Q");
|
|
2066
|
-
if (tagging) out.push("EMC");
|
|
2067
|
-
}
|
|
2068
|
-
emitTaggedRuns(out, commands.filter((c) => c.type === "image" && !!c.imageResourceName), tagging, (img) => {
|
|
2069
|
-
out.push("q");
|
|
2070
|
-
out.push(`${formatNumber(img.width)} 0 0 ${formatNumber(img.height)} ${formatNumber(img.x)} ${formatNumber(img.y)} cm`);
|
|
2071
|
-
out.push(`/${img.imageResourceName} Do`);
|
|
2072
|
-
out.push("Q");
|
|
2073
|
-
});
|
|
2074
|
-
const borders = commands.filter((c) => c.type === "border");
|
|
2075
|
-
if (borders.length > 0) {
|
|
2076
|
-
if (tagging) out.push("/Artifact BMC");
|
|
2077
|
-
out.push("q");
|
|
2078
|
-
let lastWidth = -1;
|
|
2079
|
-
let lastColor = "";
|
|
2080
|
-
for (const b of borders) {
|
|
2081
|
-
const width = b.borderSizePt;
|
|
2082
|
-
const color = b.borderColorHex;
|
|
2083
|
-
if (width !== lastWidth) {
|
|
2084
|
-
out.push(`${formatNumber(width)} w`);
|
|
2085
|
-
lastWidth = width;
|
|
2086
|
-
}
|
|
2087
|
-
if (color !== lastColor) {
|
|
2088
|
-
const [r, g, bl] = hexToRgb01(color);
|
|
2089
|
-
out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(bl)} RG`);
|
|
2090
|
-
lastColor = color;
|
|
2091
|
-
}
|
|
2092
|
-
const x = b.x;
|
|
2093
|
-
const y = b.y;
|
|
2094
|
-
const w = b.width;
|
|
2095
|
-
const h = b.height;
|
|
2096
|
-
switch (b.side) {
|
|
2097
|
-
case "top":
|
|
2098
|
-
out.push(`${formatNumber(x)} ${formatNumber(y + h)} m`);
|
|
2099
|
-
out.push(`${formatNumber(x + w)} ${formatNumber(y + h)} l`);
|
|
2100
|
-
break;
|
|
2101
|
-
case "bottom":
|
|
2102
|
-
out.push(`${formatNumber(x)} ${formatNumber(y)} m`);
|
|
2103
|
-
out.push(`${formatNumber(x + w)} ${formatNumber(y)} l`);
|
|
2104
|
-
break;
|
|
2105
|
-
case "left":
|
|
2106
|
-
out.push(`${formatNumber(x)} ${formatNumber(y)} m`);
|
|
2107
|
-
out.push(`${formatNumber(x)} ${formatNumber(y + h)} l`);
|
|
2108
|
-
break;
|
|
2109
|
-
case "right":
|
|
2110
|
-
out.push(`${formatNumber(x + w)} ${formatNumber(y)} m`);
|
|
2111
|
-
out.push(`${formatNumber(x + w)} ${formatNumber(y + h)} l`);
|
|
2112
|
-
break;
|
|
2113
|
-
}
|
|
2114
|
-
out.push("S");
|
|
2115
|
-
}
|
|
2116
|
-
out.push("Q");
|
|
2117
|
-
if (tagging) out.push("EMC");
|
|
2118
|
-
}
|
|
2119
|
-
emitTaggedRuns(out, commands.filter((c) => c.type === "shape"), tagging, (sh) => {
|
|
2120
|
-
for (const op of emitVectorShape(sh.shape)) out.push(op);
|
|
2121
|
-
});
|
|
2122
|
-
const lines = commands.filter((c) => c.type === "line");
|
|
2123
|
-
if (lines.length > 0) {
|
|
2124
|
-
let inBT = false;
|
|
2125
|
-
let lastFont = "";
|
|
2126
|
-
let lastSize = -1;
|
|
2127
|
-
let lastColor = "";
|
|
2128
|
-
const switchFontIfNeeded = (tok) => {
|
|
2129
|
-
const fontKey = tok.font.resourceName;
|
|
2130
|
-
if (fontKey !== lastFont || tok.fontSizePt !== lastSize) {
|
|
2131
|
-
out.push(`/${fontKey} ${formatNumber(tok.fontSizePt)} Tf`);
|
|
2132
|
-
lastFont = fontKey;
|
|
2133
|
-
lastSize = tok.fontSizePt;
|
|
2134
|
-
}
|
|
2135
|
-
if (tok.resolvedRun.colorHex !== lastColor) {
|
|
2136
|
-
const [r, g, b] = hexToRgb01(tok.resolvedRun.colorHex);
|
|
2137
|
-
out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
|
|
2138
|
-
lastColor = tok.resolvedRun.colorHex;
|
|
2139
|
-
}
|
|
2140
|
-
};
|
|
2141
|
-
const emitImageToken = (tok, x, baselineY) => {
|
|
2142
|
-
if (!tok.imageResourceName) return;
|
|
2143
|
-
if (inBT) {
|
|
2144
|
-
out.push("ET");
|
|
2145
|
-
inBT = false;
|
|
2146
|
-
}
|
|
2147
|
-
out.push("q");
|
|
2148
|
-
out.push(`${formatNumber(tok.widthPt)} 0 0 ${formatNumber(tok.heightPt)} ${formatNumber(x)} ${formatNumber(baselineY)} cm`);
|
|
2149
|
-
out.push(`/${tok.imageResourceName} Do`);
|
|
2150
|
-
out.push("Q");
|
|
2151
|
-
lastFont = "";
|
|
2152
|
-
lastSize = -1;
|
|
2153
|
-
lastColor = "";
|
|
2154
|
-
};
|
|
2155
|
-
const emitMathToken = (tok, originX, baselineY) => {
|
|
2156
|
-
for (const it of tok.items) if (it.kind === "glyph") {
|
|
2157
|
-
if (!inBT) {
|
|
2158
|
-
out.push("BT");
|
|
2159
|
-
inBT = true;
|
|
2160
|
-
}
|
|
2161
|
-
if (it.font.resourceName !== lastFont || it.sizePt !== lastSize) {
|
|
2162
|
-
out.push(`/${it.font.resourceName} ${formatNumber(it.sizePt)} Tf`);
|
|
2163
|
-
lastFont = it.font.resourceName;
|
|
2164
|
-
lastSize = it.sizePt;
|
|
2165
|
-
}
|
|
2166
|
-
if (lastColor !== "000000") {
|
|
2167
|
-
out.push("0 0 0 rg");
|
|
2168
|
-
lastColor = "000000";
|
|
2169
|
-
}
|
|
2170
|
-
out.push(`1 0 0 1 ${formatNumber(originX + it.x)} ${formatNumber(baselineY + it.y)} Tm`);
|
|
2171
|
-
out.push(`<${it.font.embedded.encodeTextAsCidHex(it.text)}> Tj`);
|
|
2172
|
-
} else if (it.kind === "rule") {
|
|
2173
|
-
if (inBT) {
|
|
2174
|
-
out.push("ET");
|
|
2175
|
-
inBT = false;
|
|
2176
|
-
}
|
|
2177
|
-
out.push("q");
|
|
2178
|
-
out.push("0 0 0 rg");
|
|
2179
|
-
out.push(`${formatNumber(originX + it.x)} ${formatNumber(baselineY + it.y)} ${formatNumber(it.w)} ${formatNumber(it.h)} re`);
|
|
2180
|
-
out.push("f");
|
|
2181
|
-
out.push("Q");
|
|
2182
|
-
lastFont = "";
|
|
2183
|
-
lastSize = -1;
|
|
2184
|
-
lastColor = "";
|
|
2185
|
-
} else {
|
|
2186
|
-
if (inBT) {
|
|
2187
|
-
out.push("ET");
|
|
2188
|
-
inBT = false;
|
|
2189
|
-
}
|
|
2190
|
-
const shape = {
|
|
2191
|
-
paths: [{ segments: it.segments }],
|
|
2192
|
-
...it.fill ? { fillColorHex: "000000" } : {},
|
|
2193
|
-
...it.strokeWidthPt !== void 0 ? { stroke: {
|
|
2194
|
-
colorHex: "000000",
|
|
2195
|
-
widthPt: it.strokeWidthPt
|
|
2196
|
-
} } : {},
|
|
2197
|
-
transform: [
|
|
2198
|
-
1,
|
|
2199
|
-
0,
|
|
2200
|
-
0,
|
|
2201
|
-
1,
|
|
2202
|
-
originX,
|
|
2203
|
-
baselineY
|
|
2204
|
-
]
|
|
2205
|
-
};
|
|
2206
|
-
for (const op of emitVectorShape(shape)) out.push(op);
|
|
2207
|
-
lastFont = "";
|
|
2208
|
-
lastSize = -1;
|
|
2209
|
-
lastColor = "";
|
|
2210
|
-
}
|
|
2211
|
-
};
|
|
2212
|
-
const emitOneLine = (cmd) => {
|
|
2213
|
-
const line = cmd.line;
|
|
2214
|
-
const originX = cmd.originX;
|
|
2215
|
-
const baselineY = cmd.baselineY;
|
|
2216
|
-
const extraPerSpace = computeJustifyExtra(line);
|
|
2217
|
-
const hasImageToken = line.tokens.some((t) => t.kind === "image");
|
|
2218
|
-
const hasMathToken = line.tokens.some((t) => t.kind === "math");
|
|
2219
|
-
const hasRtl = line.tokens.some((t) => t.bidiLevel % 2 === 1);
|
|
2220
|
-
const encodeToken = (tok) => {
|
|
2221
|
-
const text = tok.bidiLevel % 2 === 1 ? reverseByCodePoint(tok.text) : tok.text;
|
|
2222
|
-
return tok.font.embedded.encodeTextAsCidHex(text);
|
|
2223
|
-
};
|
|
2224
|
-
if (extraPerSpace > 0 || hasImageToken || hasMathToken || hasRtl) {
|
|
2225
|
-
const order = hasRtl ? lineVisualOrder(line) : line.tokens.map((_, i) => i);
|
|
2226
|
-
let x = originX;
|
|
2227
|
-
for (const ti of order) {
|
|
2228
|
-
const tok = line.tokens[ti];
|
|
2229
|
-
if (tok.kind === "image") {
|
|
2230
|
-
emitImageToken(tok, x, baselineY);
|
|
2231
|
-
x += tok.widthPt;
|
|
2232
|
-
continue;
|
|
2233
|
-
}
|
|
2234
|
-
if (tok.kind === "math") {
|
|
2235
|
-
emitMathToken(tok, x, baselineY);
|
|
2236
|
-
x += tok.widthPt;
|
|
2237
|
-
continue;
|
|
2238
|
-
}
|
|
2239
|
-
if (!inBT) {
|
|
2240
|
-
out.push("BT");
|
|
2241
|
-
inBT = true;
|
|
2242
|
-
}
|
|
2243
|
-
switchFontIfNeeded(tok);
|
|
2244
|
-
out.push(`1 0 0 1 ${formatNumber(x)} ${formatNumber(baselineY)} Tm`);
|
|
2245
|
-
out.push(`<${encodeToken(tok)}> Tj`);
|
|
2246
|
-
x += tok.widthPt;
|
|
2247
|
-
if (tok.isSpace) x += extraPerSpace;
|
|
2248
|
-
}
|
|
2249
|
-
} else {
|
|
2250
|
-
if (!inBT) {
|
|
2251
|
-
out.push("BT");
|
|
2252
|
-
inBT = true;
|
|
2253
|
-
}
|
|
2254
|
-
out.push(`1 0 0 1 ${formatNumber(originX)} ${formatNumber(baselineY)} Tm`);
|
|
2255
|
-
for (const tok of line.tokens) {
|
|
2256
|
-
if (tok.kind !== "text") continue;
|
|
2257
|
-
switchFontIfNeeded(tok);
|
|
2258
|
-
const hex = tok.font.embedded.encodeTextAsCidHex(tok.text);
|
|
2259
|
-
out.push(`<${hex}> Tj`);
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
};
|
|
2263
|
-
if (tagging) for (const cmd of lines) {
|
|
2264
|
-
if (inBT) {
|
|
2265
|
-
out.push("ET");
|
|
2266
|
-
inBT = false;
|
|
2267
|
-
}
|
|
2268
|
-
const sid = cmd.structId;
|
|
2269
|
-
if (sid !== void 0) {
|
|
2270
|
-
const mcid = tagging.next++;
|
|
2271
|
-
tagging.assigned = true;
|
|
2272
|
-
tagging.record(sid, mcid);
|
|
2273
|
-
out.push(`/${tagging.tagFor(sid)} <</MCID ${mcid}>> BDC`);
|
|
2274
|
-
} else if (cmd.artifact === "pagination") out.push("/Artifact <</Type /Pagination>> BDC");
|
|
2275
|
-
else out.push("/Artifact BMC");
|
|
2276
|
-
emitOneLine(cmd);
|
|
2277
|
-
if (inBT) {
|
|
2278
|
-
out.push("ET");
|
|
2279
|
-
inBT = false;
|
|
2280
|
-
}
|
|
2281
|
-
out.push("EMC");
|
|
2282
|
-
}
|
|
2283
|
-
else {
|
|
2284
|
-
out.push("BT");
|
|
2285
|
-
inBT = true;
|
|
2286
|
-
for (const cmd of lines) emitOneLine(cmd);
|
|
2287
|
-
if (inBT) out.push("ET");
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
return encoder.encode(out.join("\n"));
|
|
2291
|
-
}
|
|
2292
|
-
function lineVisualOrder(line) {
|
|
2293
|
-
return reorderVisual(line.tokens.map((t) => t.bidiLevel));
|
|
2294
|
-
}
|
|
2295
|
-
function computeJustifyExtra(line) {
|
|
2296
|
-
if (line.resolved.alignment !== "both") return 0;
|
|
2297
|
-
if (line.isLastInParagraph) return 0;
|
|
2298
|
-
let spaces = 0;
|
|
2299
|
-
for (const tok of line.tokens) if (tok.isSpace) spaces++;
|
|
2300
|
-
if (spaces === 0) return 0;
|
|
2301
|
-
const extra = line.availableWidthPt - line.contentWidthPt;
|
|
2302
|
-
if (extra <= 0) return 0;
|
|
2303
|
-
return extra / spaces;
|
|
2304
|
-
}
|
|
2305
|
-
function hexToRgb01(hex) {
|
|
2306
|
-
return [
|
|
2307
|
-
parseInt(hex.slice(0, 2), 16) / 255,
|
|
2308
|
-
parseInt(hex.slice(2, 4), 16) / 255,
|
|
2309
|
-
parseInt(hex.slice(4, 6), 16) / 255
|
|
2310
|
-
];
|
|
2311
|
-
}
|
|
2312
|
-
function formatNumber(n) {
|
|
2313
|
-
if (Number.isInteger(n)) return String(n);
|
|
2314
|
-
return n.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
7
|
+
return emitStyledPdf(layoutStyledDocument(body, options), options, new PdfDocument());
|
|
2315
8
|
}
|
|
2316
9
|
//#endregion
|
|
2317
10
|
export { renderStyledPdf };
|