reamkit 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/esm/core/converter/facade.js +1 -1
- package/dist/esm/core/converter/project.js +1 -0
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +23 -0
- package/dist/esm/core/font/measure.d.ts +1 -1
- package/dist/esm/core/font/measure.js +5 -3
- package/dist/esm/core/font/ttf-parser.d.ts +9 -0
- package/dist/esm/core/font/ttf-parser.js +27 -2
- package/dist/esm/core/fonts/provider.d.ts +2 -1
- package/dist/esm/core/fonts/provider.js +2 -1
- package/dist/esm/core/fonts/remote-fonts.d.ts +1 -1
- package/dist/esm/core/fonts/remote-fonts.js +32 -11
- package/dist/esm/core/ir/features.d.ts +1 -0
- package/dist/esm/core/ir/features.js +1 -0
- package/dist/esm/core/ir/flow.d.ts +3 -1
- package/dist/esm/core/line-breaker/greedy.d.ts +2 -0
- package/dist/esm/core/line-breaker/greedy.js +67 -0
- package/dist/esm/core/line-breaker/index.d.ts +1 -0
- package/dist/esm/core/line-breaker/knuth-plass.d.ts +1 -0
- package/dist/esm/core/line-breaker/knuth-plass.js +3 -3
- package/dist/esm/core/po-helpers.d.ts +2 -0
- package/dist/esm/core/po-helpers.js +14 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +16 -0
- package/dist/esm/excel/pivot-table-parser.d.ts +2 -0
- package/dist/esm/excel/pivot-table-parser.js +68 -0
- package/dist/esm/excel/print-model.js +28 -6
- package/dist/esm/excel/xlsx-reader.js +33 -2
- package/dist/esm/excel/xlsx-to-pdf.js +1 -1
- package/dist/esm/html/html-writer.js +52 -2
- package/dist/esm/layout/page-doc.d.ts +3 -0
- package/dist/esm/layout/styled-layout.d.ts +10 -1
- package/dist/esm/layout/styled-layout.js +156 -15
- package/dist/esm/pdf/styled-page-emitter.js +38 -0
- package/dist/esm/pptx/pptx-reader.d.ts +2 -0
- package/dist/esm/pptx/pptx-reader.js +30 -3
- package/dist/esm/pptx/slide-parser.d.ts +8 -2
- package/dist/esm/pptx/slide-parser.js +80 -12
- package/dist/esm/word/document-parser.d.ts +17 -2
- package/dist/esm/word/document-parser.js +178 -18
- package/dist/esm/word/docx-reader.js +54 -4
- package/dist/esm/word/docx-to-pdf.js +2 -2
- package/dist/esm/word/docx-writer.js +84 -1
- package/dist/esm/word/drawing-parser.d.ts +7 -0
- package/dist/esm/word/drawing-parser.js +13 -0
- package/dist/esm/word/index.d.ts +2 -1
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ import { EMPTY_XLSX_STYLES, parseXlsxStyles } from "./styles-parser.js";
|
|
|
15
15
|
import "./index.js";
|
|
16
16
|
import { parseSheetDrawing } from "./sheet-drawing.js";
|
|
17
17
|
import { parseTablePart } from "./table-parser.js";
|
|
18
|
+
import { parsePivotTablePart } from "./pivot-table-parser.js";
|
|
18
19
|
import { projectSheetDoc } from "./sheet-to-flow.js";
|
|
19
20
|
//#region src/excel/xlsx-reader.ts
|
|
20
21
|
var WORKBOOK_PART = "xl/workbook.xml";
|
|
@@ -79,9 +80,21 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
79
80
|
}
|
|
80
81
|
if (resolvedTables.length > 0) tables = resolvedTables;
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
+
let pivotTables;
|
|
84
|
+
{
|
|
85
|
+
const resolvedPivots = [];
|
|
86
|
+
for (const rel of pkg.getPartRelationships(resolved.path)) {
|
|
87
|
+
if (!isOoxmlRel(rel.type, "pivotTable")) continue;
|
|
88
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
89
|
+
const parsed = part ? parsePivotTablePart(part.data) : void 0;
|
|
90
|
+
if (parsed) resolvedPivots.push(resolvePivotStyle(parsed, palette));
|
|
91
|
+
}
|
|
92
|
+
if (resolvedPivots.length > 0) pivotTables = resolvedPivots;
|
|
93
|
+
}
|
|
94
|
+
const grid = tables || pivotTables ? {
|
|
83
95
|
...worksheet,
|
|
84
|
-
tables
|
|
96
|
+
...tables ? { tables } : {},
|
|
97
|
+
...pivotTables ? { pivotTables } : {}
|
|
85
98
|
} : worksheet;
|
|
86
99
|
sheetsOut.push({
|
|
87
100
|
name: sheet.name,
|
|
@@ -132,6 +145,24 @@ function resolveTableStyle(t, palette) {
|
|
|
132
145
|
headerTextHex: "FFFFFF"
|
|
133
146
|
};
|
|
134
147
|
}
|
|
148
|
+
function resolvePivotStyle(p, palette) {
|
|
149
|
+
const m = p.styleName ? /PivotStyle(Light|Medium|Dark)(\d+)/i.exec(p.styleName) : null;
|
|
150
|
+
if (!m) return p;
|
|
151
|
+
const kind = m[1].toLowerCase();
|
|
152
|
+
const column = (Number(m[2]) - 1) % 7;
|
|
153
|
+
const base = column === 0 ? "7F7F7F" : palette.get(`accent${column}`) ?? "4472C4";
|
|
154
|
+
if (kind === "light") return {
|
|
155
|
+
...p,
|
|
156
|
+
headerHex: lighten(base, .6),
|
|
157
|
+
bandHex: lighten(base, .85)
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
...p,
|
|
161
|
+
headerHex: base,
|
|
162
|
+
bandHex: lighten(base, .8),
|
|
163
|
+
headerTextHex: "FFFFFF"
|
|
164
|
+
};
|
|
165
|
+
}
|
|
135
166
|
function lighten(hex, amount) {
|
|
136
167
|
const m = /^#?([0-9a-fA-F]{6})$/.exec(hex);
|
|
137
168
|
const n = m ? parseInt(m[1], 16) : 4485828;
|
|
@@ -46,7 +46,7 @@ function prepareXlsxStyledRender(xlsx, options) {
|
|
|
46
46
|
if (!fonts) throw new Error("convertXlsxToPdfSync requires options.fonts or options.fontBytes");
|
|
47
47
|
const registry = FontRegistry.fromBytes(fonts);
|
|
48
48
|
const { doc: flow } = readXlsx(xlsx);
|
|
49
|
-
const { fontBytes: _ignoreA, fonts: _ignoreB, section: sectionOverride, info: callerInfo, embedSource, attachments: callerAttachments, signature: _ignoreSig, ...renderOptions } = options;
|
|
49
|
+
const { fontBytes: _ignoreA, fonts: _ignoreB, section: sectionOverride, info: callerInfo, embedSource, attachments: callerAttachments, signature: _ignoreSig, layoutProfile: _ignoreProfile, ...renderOptions } = options;
|
|
50
50
|
const section = sectionOverride ?? flow.section;
|
|
51
51
|
const info = flow.info || callerInfo ? {
|
|
52
52
|
...flow.info,
|
|
@@ -41,6 +41,7 @@ function writeHtml(flow) {
|
|
|
41
41
|
for (const el of flow.body) emitBlock(out, el, ctx);
|
|
42
42
|
emitNotesSection(out, flow.footnotes, ctx.notes.footnotes, "fn", ctx);
|
|
43
43
|
emitNotesSection(out, flow.endnotes, ctx.notes.endnotes, "en", ctx);
|
|
44
|
+
emitCommentsSection(out, flow.comments, ctx.notes.comments, ctx);
|
|
44
45
|
out.push("</article>");
|
|
45
46
|
out.push("</body>");
|
|
46
47
|
out.push("</html>");
|
|
@@ -76,7 +77,10 @@ var BASE_CSS = [
|
|
|
76
77
|
".tab{display:inline-block;min-width:18pt}",
|
|
77
78
|
"img{vertical-align:baseline}",
|
|
78
79
|
".notes{margin-top:18pt;font-size:smaller}",
|
|
79
|
-
".notes hr{margin:0 0 6pt;border:none;border-top:0.75pt solid #000;width:144pt;margin-left:0}"
|
|
80
|
+
".notes hr{margin:0 0 6pt;border:none;border-top:0.75pt solid #000;width:144pt;margin-left:0}",
|
|
81
|
+
".comment-range{background:#fff3a3}",
|
|
82
|
+
".comment-replies{margin-left:18pt}",
|
|
83
|
+
".comments .resolved>.comment-meta::after{content:\" \\2713\";color:#3a7d3a}"
|
|
80
84
|
].join("");
|
|
81
85
|
function collectReferencedAnchors(body) {
|
|
82
86
|
const out = /* @__PURE__ */ new Set();
|
|
@@ -92,10 +96,12 @@ function collectReferencedAnchors(body) {
|
|
|
92
96
|
function collectNoteNumbers(flow) {
|
|
93
97
|
const footnotes = /* @__PURE__ */ new Map();
|
|
94
98
|
const endnotes = /* @__PURE__ */ new Map();
|
|
99
|
+
const comments = /* @__PURE__ */ new Map();
|
|
95
100
|
const visit = (els) => {
|
|
96
101
|
for (const el of els) if (el.kind === "paragraph") for (const r of el.paragraph.runs) {
|
|
97
102
|
if (r.footnoteRef !== void 0 && !footnotes.has(r.footnoteRef)) footnotes.set(r.footnoteRef, footnotes.size + 1);
|
|
98
103
|
if (r.endnoteRef !== void 0 && !endnotes.has(r.endnoteRef)) endnotes.set(r.endnoteRef, endnotes.size + 1);
|
|
104
|
+
if (r.commentRef !== void 0 && !comments.has(r.commentRef)) comments.set(r.commentRef, comments.size + 1);
|
|
99
105
|
}
|
|
100
106
|
else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
101
107
|
else if (el.kind === "shape" && el.shape.text) visit(el.shape.text.content);
|
|
@@ -103,7 +109,8 @@ function collectNoteNumbers(flow) {
|
|
|
103
109
|
visit(flow.body);
|
|
104
110
|
return {
|
|
105
111
|
footnotes,
|
|
106
|
-
endnotes
|
|
112
|
+
endnotes,
|
|
113
|
+
comments
|
|
107
114
|
};
|
|
108
115
|
}
|
|
109
116
|
function emitNotesSection(out, content, numbers, prefix, ctx) {
|
|
@@ -118,6 +125,43 @@ function emitNotesSection(out, content, numbers, prefix, ctx) {
|
|
|
118
125
|
}
|
|
119
126
|
out.push("</ol></section>");
|
|
120
127
|
}
|
|
128
|
+
function emitCommentsSection(out, comments, numbers, ctx) {
|
|
129
|
+
if (!comments || numbers.size === 0) return;
|
|
130
|
+
const ordered = [...numbers.entries()].sort((a, b) => a[1] - b[1]).map(([id]) => id);
|
|
131
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
132
|
+
const roots = [];
|
|
133
|
+
for (const id of ordered) {
|
|
134
|
+
const parent = comments.get(id)?.parentId;
|
|
135
|
+
if (parent !== void 0 && parent !== id && numbers.has(parent)) {
|
|
136
|
+
const kids = childrenOf.get(parent);
|
|
137
|
+
if (kids) kids.push(id);
|
|
138
|
+
else childrenOf.set(parent, [id]);
|
|
139
|
+
} else roots.push(id);
|
|
140
|
+
}
|
|
141
|
+
const seen = /* @__PURE__ */ new Set();
|
|
142
|
+
const emitOne = (id) => {
|
|
143
|
+
if (seen.has(id)) return;
|
|
144
|
+
seen.add(id);
|
|
145
|
+
const c = comments.get(id);
|
|
146
|
+
const n = numbers.get(id);
|
|
147
|
+
if (!c || n === void 0) return;
|
|
148
|
+
out.push(`<li id="cm-${n}" class="${c.done ? "comment resolved" : "comment"}">`);
|
|
149
|
+
const meta = [[c.author !== void 0 ? c.author + (c.authorId ? ` <${c.authorId}>` : "") : c.authorId, c.date].filter((s) => s !== void 0 && s.length > 0).join(", "), c.done ? "Resolved" : void 0].filter((s) => s).join(" — ");
|
|
150
|
+
if (meta) out.push(`<p class="comment-meta">${textHtml(meta)}</p>`);
|
|
151
|
+
for (const el of c.content) emitBlock(out, el, ctx);
|
|
152
|
+
out.push(`<a href="#cmref-${n}">\u21a9</a>`);
|
|
153
|
+
const kids = childrenOf.get(id);
|
|
154
|
+
if (kids && kids.length > 0) {
|
|
155
|
+
out.push("<ol class=\"comment-replies\">");
|
|
156
|
+
for (const k of kids) emitOne(k);
|
|
157
|
+
out.push("</ol>");
|
|
158
|
+
}
|
|
159
|
+
out.push("</li>");
|
|
160
|
+
};
|
|
161
|
+
out.push("<section class=\"comments\"><hr/><h2>Comments</h2><ol>");
|
|
162
|
+
for (const id of roots) emitOne(id);
|
|
163
|
+
out.push("</ol></section>");
|
|
164
|
+
}
|
|
121
165
|
function emitBlock(out, el, ctx) {
|
|
122
166
|
if (el.kind === "paragraph") emitParagraph(out, el.paragraph, ctx);
|
|
123
167
|
else if (el.kind === "table") emitTable(out, el.table, ctx);
|
|
@@ -272,6 +316,11 @@ function runHtml(run, p, ctx) {
|
|
|
272
316
|
const prefix = isFoot ? "fn" : "en";
|
|
273
317
|
return `<sup><a href="#${prefix}-${n}" id="${prefix}ref-${n}">${n}</a></sup>`;
|
|
274
318
|
}
|
|
319
|
+
if (run.commentRef !== void 0) {
|
|
320
|
+
const n = ctx.notes.comments.get(run.commentRef);
|
|
321
|
+
if (n === void 0) return "";
|
|
322
|
+
return `<sup class="comment-ref"><a href="#cm-${n}" id="cmref-${n}">[${n}]</a></sup>`;
|
|
323
|
+
}
|
|
275
324
|
if (run.noteNumber) return "";
|
|
276
325
|
if (run.math !== void 0) {
|
|
277
326
|
ctx.losses.push({
|
|
@@ -293,6 +342,7 @@ function runHtml(run, p, ctx) {
|
|
|
293
342
|
let html = `<span${resolved.rtl ? " dir=\"rtl\"" : ""}${style ? ` style="${style}"` : ""}>${textHtml(run.text)}</span>`;
|
|
294
343
|
if (resolved.verticalAlign === "superscript") html = `<sup>${html}</sup>`;
|
|
295
344
|
else if (resolved.verticalAlign === "subscript") html = `<sub>${html}</sub>`;
|
|
345
|
+
if (run.commentRangeRefs && run.commentRangeRefs.length > 0) html = `<span class="comment-range">${html}</span>`;
|
|
296
346
|
if (run.href === void 0 && run.anchor !== void 0) return `<a href="#bm-${encodeURIComponent(run.anchor)}">${html}</a>`;
|
|
297
347
|
if (run.href !== void 0) {
|
|
298
348
|
const safe = sanitizeHref(run.href);
|
|
@@ -17,6 +17,7 @@ export interface TextToken {
|
|
|
17
17
|
readonly footnoteRef?: string;
|
|
18
18
|
readonly anchor?: string;
|
|
19
19
|
readonly listMarker?: true;
|
|
20
|
+
readonly highlight?: true;
|
|
20
21
|
readonly resolvedRun: ResolvedRunProperties;
|
|
21
22
|
readonly font: FontResource;
|
|
22
23
|
readonly fontSizePt: number;
|
|
@@ -70,6 +71,8 @@ export interface Line {
|
|
|
70
71
|
isLastInParagraph: boolean;
|
|
71
72
|
readonly mathAscentPt?: number;
|
|
72
73
|
readonly mathDescentPt?: number;
|
|
74
|
+
readonly metricHeightPt?: number;
|
|
75
|
+
readonly metricDescentPt?: number;
|
|
73
76
|
}
|
|
74
77
|
export interface ImageResource {
|
|
75
78
|
readonly resourceName: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BodyElement, Chart, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../core/document-model/index.js';
|
|
1
|
+
import { BodyElement, Chart, Comment, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../core/document-model/index.js';
|
|
2
2
|
import { FontRegistry } from '../core/font/index.js';
|
|
3
3
|
import { FamilyKey } from '../core/fonts/index.js';
|
|
4
4
|
import { Hyphenator } from '../core/hyphenation/index.js';
|
|
@@ -15,8 +15,10 @@ export interface PdfAProfile {
|
|
|
15
15
|
readonly tagged: boolean;
|
|
16
16
|
readonly version: '1.4' | '1.7';
|
|
17
17
|
}
|
|
18
|
+
export type LayoutProfile = 'ream' | 'word' | 'libreoffice';
|
|
18
19
|
export interface StyledRenderOptions {
|
|
19
20
|
readonly registry: FontRegistry;
|
|
21
|
+
readonly layoutProfile?: LayoutProfile;
|
|
20
22
|
readonly registriesByFamily?: ReadonlyMap<FamilyKey, FontRegistry>;
|
|
21
23
|
readonly embeddedFonts?: ReadonlyMap<string, FontRegistry>;
|
|
22
24
|
readonly styles: StyleSheet;
|
|
@@ -26,6 +28,8 @@ export interface StyledRenderOptions {
|
|
|
26
28
|
readonly headersFooters?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
27
29
|
readonly footnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
28
30
|
readonly endnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
31
|
+
readonly comments?: ReadonlyMap<string, Comment>;
|
|
32
|
+
readonly commentAnnotations?: boolean;
|
|
29
33
|
readonly resources?: ResourceStore;
|
|
30
34
|
readonly charts?: ReadonlyMap<string, Chart>;
|
|
31
35
|
readonly pageWidth?: number;
|
|
@@ -51,12 +55,17 @@ export interface BookmarkPosition {
|
|
|
51
55
|
readonly pageIdx: number;
|
|
52
56
|
readonly yTopPt: number;
|
|
53
57
|
}
|
|
58
|
+
export interface CommentNote {
|
|
59
|
+
readonly author?: string;
|
|
60
|
+
readonly contents: string;
|
|
61
|
+
}
|
|
54
62
|
export interface PdfLayoutAux {
|
|
55
63
|
readonly structBuilder: StructTreeBuilder | undefined;
|
|
56
64
|
readonly sectionCtxs: ReadonlyArray<SectionRenderCtx>;
|
|
57
65
|
readonly pdfaProfile: PdfAProfile | undefined;
|
|
58
66
|
readonly tagged: boolean;
|
|
59
67
|
readonly bookmarks: ReadonlyMap<string, BookmarkPosition>;
|
|
68
|
+
readonly commentNotes?: ReadonlyMap<string, CommentNote>;
|
|
60
69
|
}
|
|
61
70
|
export interface LaidOutPdfDocument extends LaidOutDocument {
|
|
62
71
|
readonly pdf: PdfLayoutAux;
|
|
@@ -7,6 +7,7 @@ import { prepareImage } from "../core/images.js";
|
|
|
7
7
|
import { segmentLevels } from "../core/bidi/segments.js";
|
|
8
8
|
import { analyzeString, hasBidiCharacters } from "../core/bidi/index.js";
|
|
9
9
|
import { FORCED_BREAK, breakLines } from "../core/line-breaker/knuth-plass.js";
|
|
10
|
+
import { greedyBreakLines } from "../core/line-breaker/greedy.js";
|
|
10
11
|
import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
|
|
11
12
|
import { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN } from "../core/style-cascade/types.js";
|
|
12
13
|
import { resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
|
|
@@ -43,9 +44,10 @@ function isOutOfFlowFloat(f) {
|
|
|
43
44
|
return f !== void 0 && f.wrap !== "topAndBottom";
|
|
44
45
|
}
|
|
45
46
|
var FOOTNOTE_RULE_WIDTH = 144;
|
|
46
|
-
function assignNoteNumbers(body) {
|
|
47
|
+
function assignNoteNumbers(body, commentIds) {
|
|
47
48
|
const footnotes = /* @__PURE__ */ new Map();
|
|
48
49
|
const endnotes = /* @__PURE__ */ new Map();
|
|
50
|
+
const comments = /* @__PURE__ */ new Map();
|
|
49
51
|
const paragraphFootnotes = /* @__PURE__ */ new Set();
|
|
50
52
|
const numberRun = (run, n) => ({
|
|
51
53
|
...run,
|
|
@@ -55,8 +57,17 @@ function assignNoteNumbers(body) {
|
|
|
55
57
|
verticalAlign: "superscript"
|
|
56
58
|
}
|
|
57
59
|
});
|
|
60
|
+
const commentMarkerRun = (run, n) => ({
|
|
61
|
+
...run,
|
|
62
|
+
text: `[${n}]`,
|
|
63
|
+
...run.commentRef !== void 0 && commentIds?.has(run.commentRef) ? { anchor: `comment-${n}` } : {},
|
|
64
|
+
properties: {
|
|
65
|
+
...run.properties,
|
|
66
|
+
verticalAlign: "superscript"
|
|
67
|
+
}
|
|
68
|
+
});
|
|
58
69
|
const mapParagraph = (paragraph) => {
|
|
59
|
-
if (!paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0)) return paragraph;
|
|
70
|
+
if (!paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0 || r.commentRef !== void 0)) return paragraph;
|
|
60
71
|
return {
|
|
61
72
|
...paragraph,
|
|
62
73
|
runs: paragraph.runs.map((run) => {
|
|
@@ -76,6 +87,14 @@ function assignNoteNumbers(body) {
|
|
|
76
87
|
}
|
|
77
88
|
return numberRun(run, n);
|
|
78
89
|
}
|
|
90
|
+
if (run.commentRef !== void 0) {
|
|
91
|
+
let n = comments.get(run.commentRef);
|
|
92
|
+
if (n === void 0) {
|
|
93
|
+
n = comments.size + 1;
|
|
94
|
+
comments.set(run.commentRef, n);
|
|
95
|
+
}
|
|
96
|
+
return commentMarkerRun(run, n);
|
|
97
|
+
}
|
|
79
98
|
return run;
|
|
80
99
|
})
|
|
81
100
|
};
|
|
@@ -116,7 +135,7 @@ function assignNoteNumbers(body) {
|
|
|
116
135
|
return el;
|
|
117
136
|
};
|
|
118
137
|
const hasRefs = (els) => els.some((el) => {
|
|
119
|
-
if (el.kind === "paragraph") return el.paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0);
|
|
138
|
+
if (el.kind === "paragraph") return el.paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0 || r.commentRef !== void 0);
|
|
120
139
|
if (el.kind === "table") return el.table.rows.some((row) => row.cells.some((c) => hasRefs(c.content)));
|
|
121
140
|
if (el.kind === "shape" && el.shape.text) return hasRefs(el.shape.text.content);
|
|
122
141
|
return false;
|
|
@@ -125,12 +144,14 @@ function assignNoteNumbers(body) {
|
|
|
125
144
|
body,
|
|
126
145
|
footnotes,
|
|
127
146
|
endnotes,
|
|
147
|
+
comments,
|
|
128
148
|
deferredFootnotes: []
|
|
129
149
|
};
|
|
130
150
|
return {
|
|
131
151
|
body: body.map((el) => mapElement(el, true)),
|
|
132
152
|
footnotes,
|
|
133
153
|
endnotes,
|
|
154
|
+
comments,
|
|
134
155
|
deferredFootnotes: [...footnotes.keys()].filter((id) => !paragraphFootnotes.has(id))
|
|
135
156
|
};
|
|
136
157
|
}
|
|
@@ -168,9 +189,62 @@ function substituteNoteNumber(content, n) {
|
|
|
168
189
|
}
|
|
169
190
|
}, ...out.slice(1)];
|
|
170
191
|
}
|
|
192
|
+
function flattenCommentText(content) {
|
|
193
|
+
const lines = [];
|
|
194
|
+
const visit = (els) => {
|
|
195
|
+
for (const el of els) if (el.kind === "paragraph") lines.push(el.paragraph.runs.map((r) => r.text).join(""));
|
|
196
|
+
else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
197
|
+
};
|
|
198
|
+
visit(content);
|
|
199
|
+
return lines.join("\n").trim();
|
|
200
|
+
}
|
|
201
|
+
function buildCommentNotes(numbers, comments) {
|
|
202
|
+
const out = /* @__PURE__ */ new Map();
|
|
203
|
+
for (const [id, n] of numbers) {
|
|
204
|
+
const c = comments.get(id);
|
|
205
|
+
if (!c) continue;
|
|
206
|
+
out.set(`comment-${n}`, {
|
|
207
|
+
...c.author !== void 0 ? { author: c.author } : {},
|
|
208
|
+
contents: flattenCommentText(c.content)
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return out;
|
|
212
|
+
}
|
|
213
|
+
function commentTailBlocks(comment, n, opts) {
|
|
214
|
+
const who = [comment.author, comment.date].filter((s) => s !== void 0 && s.length > 0).join(", ");
|
|
215
|
+
const inReplyTo = opts?.parentN !== void 0 ? ` (in reply to [${opts.parentN}])` : "";
|
|
216
|
+
const resolved = opts?.done ? " (resolved)" : "";
|
|
217
|
+
const labelRun = {
|
|
218
|
+
text: who ? `[${n}] ${who}${inReplyTo}${resolved}: ` : `[${n}]${inReplyTo}${resolved} `,
|
|
219
|
+
properties: {}
|
|
220
|
+
};
|
|
221
|
+
const bm = `comment-${n}`;
|
|
222
|
+
const indent = opts?.depth && opts.depth > 0 ? { indentLeft: pt(18 * opts.depth) } : {};
|
|
223
|
+
const first = comment.content[0];
|
|
224
|
+
if (first && first.kind === "paragraph") return [{
|
|
225
|
+
kind: "paragraph",
|
|
226
|
+
paragraph: {
|
|
227
|
+
...first.paragraph,
|
|
228
|
+
properties: {
|
|
229
|
+
...first.paragraph.properties,
|
|
230
|
+
...indent
|
|
231
|
+
},
|
|
232
|
+
runs: [labelRun, ...first.paragraph.runs],
|
|
233
|
+
bookmarks: [bm, ...first.paragraph.bookmarks ?? []]
|
|
234
|
+
}
|
|
235
|
+
}, ...comment.content.slice(1)];
|
|
236
|
+
return [{
|
|
237
|
+
kind: "paragraph",
|
|
238
|
+
paragraph: {
|
|
239
|
+
properties: { ...indent },
|
|
240
|
+
runs: [labelRun],
|
|
241
|
+
bookmarks: [bm]
|
|
242
|
+
}
|
|
243
|
+
}, ...comment.content];
|
|
244
|
+
}
|
|
171
245
|
function layoutStyledDocument(body, options) {
|
|
172
246
|
const sectionList = resolveSectionList(body, options);
|
|
173
|
-
const noteAssigned = assignNoteNumbers(applyNumbering(body, options.numbering));
|
|
247
|
+
const noteAssigned = assignNoteNumbers(applyNumbering(body, options.numbering), options.comments ? new Set(options.comments.keys()) : void 0);
|
|
174
248
|
const numberedBody = noteAssigned.body;
|
|
175
249
|
const numberedHeadersFooters = applyNumberingToHeadersFooters(options.headersFooters, options.numbering);
|
|
176
250
|
const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
|
|
@@ -228,8 +302,35 @@ function layoutStyledDocument(body, options) {
|
|
|
228
302
|
});
|
|
229
303
|
}
|
|
230
304
|
for (const note of tailNotes.sort((a, b) => a.n - b.n)) for (const el of substituteNoteNumber(note.content, note.n)) blocks.push(layoutBodyElement(el, options, fontResources, imageResources, lastCtx.contentWidth, lastCtx.pageContentHeight));
|
|
305
|
+
const commentNums = noteAssigned.comments;
|
|
306
|
+
const threadDepth = (id) => {
|
|
307
|
+
let depth = 0;
|
|
308
|
+
const seen = new Set([id]);
|
|
309
|
+
let cur = options.comments?.get(id)?.parentId;
|
|
310
|
+
while (cur !== void 0 && commentNums.has(cur) && !seen.has(cur)) {
|
|
311
|
+
depth++;
|
|
312
|
+
seen.add(cur);
|
|
313
|
+
cur = options.comments?.get(cur)?.parentId;
|
|
314
|
+
}
|
|
315
|
+
return depth;
|
|
316
|
+
};
|
|
317
|
+
const commentTail = [...commentNums].map(([id, n]) => ({
|
|
318
|
+
id,
|
|
319
|
+
comment: options.comments?.get(id),
|
|
320
|
+
n
|
|
321
|
+
})).filter((e) => e.comment !== void 0).sort((a, b) => a.n - b.n);
|
|
322
|
+
for (const { id, comment, n } of commentTail) {
|
|
323
|
+
const parentId = comment.parentId;
|
|
324
|
+
const parentN = parentId !== void 0 && commentNums.has(parentId) ? commentNums.get(parentId) : void 0;
|
|
325
|
+
for (const el of commentTailBlocks(comment, n, {
|
|
326
|
+
...parentN !== void 0 ? { parentN } : {},
|
|
327
|
+
depth: threadDepth(id),
|
|
328
|
+
...comment.done ? { done: true } : {}
|
|
329
|
+
})) blocks.push(layoutBodyElement(el, options, fontResources, imageResources, lastCtx.contentWidth, lastCtx.pageContentHeight));
|
|
330
|
+
}
|
|
231
331
|
}
|
|
232
332
|
const bookmarks = /* @__PURE__ */ new Map();
|
|
333
|
+
const commentNotes = options.commentAnnotations && options.comments ? buildCommentNotes(noteAssigned.comments, options.comments) : void 0;
|
|
233
334
|
const reflowParagraph = (paragraph, width, widths) => layoutParagraphBlock(paragraph, options, fontResources, imageResources, width, widths);
|
|
234
335
|
return {
|
|
235
336
|
pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph),
|
|
@@ -241,7 +342,8 @@ function layoutStyledDocument(body, options) {
|
|
|
241
342
|
sectionCtxs,
|
|
242
343
|
pdfaProfile,
|
|
243
344
|
tagged,
|
|
244
|
-
bookmarks
|
|
345
|
+
bookmarks,
|
|
346
|
+
...commentNotes ? { commentNotes } : {}
|
|
245
347
|
}
|
|
246
348
|
};
|
|
247
349
|
}
|
|
@@ -755,6 +857,7 @@ function collectImageResources(body, headersFooters, options) {
|
|
|
755
857
|
for (const hf of headersFooters.values()) visit(hf);
|
|
756
858
|
for (const note of options.footnotes?.values() ?? []) visit(note);
|
|
757
859
|
for (const note of options.endnotes?.values() ?? []) visit(note);
|
|
860
|
+
for (const comment of options.comments?.values() ?? []) visit(comment.content);
|
|
758
861
|
const flattenAlpha = options.pdfA ? parsePdfAProfile(options.pdfA).part === 1 : false;
|
|
759
862
|
let counter = 0;
|
|
760
863
|
for (const resourceId of seen) {
|
|
@@ -915,6 +1018,7 @@ function collectFontResources(body, headersFooters, options) {
|
|
|
915
1018
|
for (const hf of headersFooters.values()) visit(hf);
|
|
916
1019
|
for (const note of options.footnotes?.values() ?? []) visit(note);
|
|
917
1020
|
for (const note of options.endnotes?.values() ?? []) visit(note);
|
|
1021
|
+
for (const comment of options.comments?.values() ?? []) visit(comment.content);
|
|
918
1022
|
if (used.size === 0) {
|
|
919
1023
|
const regular = options.registry.resolveByStyle(false, false);
|
|
920
1024
|
used.set(regular.variant, {
|
|
@@ -930,7 +1034,7 @@ function collectFontResources(body, headersFooters, options) {
|
|
|
930
1034
|
out.set(variant, {
|
|
931
1035
|
resourceName,
|
|
932
1036
|
parsed: info.parsed,
|
|
933
|
-
measure: createFontMeasure(info.parsed),
|
|
1037
|
+
measure: createFontMeasure(info.parsed, options.layoutProfile !== "word"),
|
|
934
1038
|
gids: info.gids
|
|
935
1039
|
});
|
|
936
1040
|
}
|
|
@@ -943,7 +1047,7 @@ function layoutParagraphBlock(paragraph, options, fontResources, imageResources,
|
|
|
943
1047
|
...baseResolved,
|
|
944
1048
|
alignment: "right"
|
|
945
1049
|
} : baseResolved;
|
|
946
|
-
const lines = wrap(tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir), paragraphMaxWidth(resolved, contentWidth, true), paragraphMaxWidth(resolved, contentWidth, false), resolved, options.hyphenator, lineWidths);
|
|
1050
|
+
const lines = wrap(tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir), paragraphMaxWidth(resolved, contentWidth, true), paragraphMaxWidth(resolved, contentWidth, false), resolved, options.hyphenator, options.layoutProfile ?? "ream", lineWidths);
|
|
947
1051
|
let heightPt = 0;
|
|
948
1052
|
for (const line of lines) heightPt += computeLineHeight(line, resolved);
|
|
949
1053
|
const numbering = paragraph.properties.numbering;
|
|
@@ -1085,6 +1189,7 @@ function tokenizePlansLtr(plans) {
|
|
|
1085
1189
|
});
|
|
1086
1190
|
continue;
|
|
1087
1191
|
}
|
|
1192
|
+
const highlight = (plan.run.commentRangeRefs?.length ?? 0) > 0;
|
|
1088
1193
|
for (const t of tokenizeText(plan.run.text)) tokens.push({
|
|
1089
1194
|
kind: "text",
|
|
1090
1195
|
text: t.text,
|
|
@@ -1093,6 +1198,7 @@ function tokenizePlansLtr(plans) {
|
|
|
1093
1198
|
...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
|
|
1094
1199
|
...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
|
|
1095
1200
|
...plan.run.listMarker ? { listMarker: true } : {},
|
|
1201
|
+
...highlight ? { highlight: true } : {},
|
|
1096
1202
|
resolvedRun: plan.resolvedRun,
|
|
1097
1203
|
font: plan.font,
|
|
1098
1204
|
fontSizePt: plan.fontSizePt,
|
|
@@ -1147,6 +1253,7 @@ function tokenizePlansBidi(plans, realLevels) {
|
|
|
1147
1253
|
...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
|
|
1148
1254
|
...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
|
|
1149
1255
|
...plan.run.listMarker ? { listMarker: true } : {},
|
|
1256
|
+
...(plan.run.commentRangeRefs?.length ?? 0) > 0 ? { highlight: true } : {},
|
|
1150
1257
|
resolvedRun: plan.resolvedRun,
|
|
1151
1258
|
font: plan.font,
|
|
1152
1259
|
fontSizePt: plan.fontSizePt,
|
|
@@ -1290,7 +1397,7 @@ function paragraphItemStream(tokens, hyphenator) {
|
|
|
1290
1397
|
});
|
|
1291
1398
|
return entries;
|
|
1292
1399
|
}
|
|
1293
|
-
function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, resolved) {
|
|
1400
|
+
function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, resolved, profile) {
|
|
1294
1401
|
let st = start;
|
|
1295
1402
|
let et = breakIdx;
|
|
1296
1403
|
while (st < et && (entries[st].token === null || entries[st].token?.isSpace)) st++;
|
|
@@ -1316,6 +1423,10 @@ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, reso
|
|
|
1316
1423
|
let maxSize = 0;
|
|
1317
1424
|
let mathAscent = 0;
|
|
1318
1425
|
let mathDescent = 0;
|
|
1426
|
+
const useMetric = profile !== "ream";
|
|
1427
|
+
let metricAscent = 0;
|
|
1428
|
+
let metricDescent = 0;
|
|
1429
|
+
let metricLineGap = 0;
|
|
1319
1430
|
for (const t of lineTokens) {
|
|
1320
1431
|
contentWidth += t.widthPt;
|
|
1321
1432
|
const sz = tokenLineSize(t);
|
|
@@ -1323,8 +1434,14 @@ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, reso
|
|
|
1323
1434
|
if (t.kind === "math") {
|
|
1324
1435
|
mathAscent = Math.max(mathAscent, t.ascentPt);
|
|
1325
1436
|
mathDescent = Math.max(mathDescent, t.descentPt);
|
|
1437
|
+
} else if (useMetric && t.kind === "text") {
|
|
1438
|
+
const m = fontLeadingPt(t.font.parsed, t.fontSizePt, profile);
|
|
1439
|
+
if (m.ascentPt > metricAscent) metricAscent = m.ascentPt;
|
|
1440
|
+
if (m.descentPt > metricDescent) metricDescent = m.descentPt;
|
|
1441
|
+
if (m.lineGapPt > metricLineGap) metricLineGap = m.lineGapPt;
|
|
1326
1442
|
}
|
|
1327
1443
|
}
|
|
1444
|
+
const hasMetric = useMetric && metricAscent + metricDescent > 0;
|
|
1328
1445
|
return {
|
|
1329
1446
|
tokens: lineTokens,
|
|
1330
1447
|
contentWidthPt: contentWidth,
|
|
@@ -1334,21 +1451,26 @@ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, reso
|
|
|
1334
1451
|
resolved,
|
|
1335
1452
|
isLastInParagraph: false,
|
|
1336
1453
|
mathAscentPt: mathAscent,
|
|
1337
|
-
mathDescentPt: mathDescent
|
|
1454
|
+
mathDescentPt: mathDescent,
|
|
1455
|
+
...hasMetric ? {
|
|
1456
|
+
metricHeightPt: metricAscent + metricDescent + metricLineGap,
|
|
1457
|
+
metricDescentPt: metricDescent
|
|
1458
|
+
} : {}
|
|
1338
1459
|
};
|
|
1339
1460
|
}
|
|
1340
|
-
function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, lineWidths) {
|
|
1461
|
+
function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, profile, lineWidths) {
|
|
1341
1462
|
if (tokens.length === 0) return [];
|
|
1342
1463
|
const widths = lineWidths ?? [firstLineWidth, otherWidth];
|
|
1343
1464
|
const entries = paragraphItemStream(tokens, hyphenator);
|
|
1344
|
-
const
|
|
1465
|
+
const items = entries.map((e) => e.item);
|
|
1466
|
+
const breaks = profile === "ream" ? breakLines(items, widths).breaks : greedyBreakLines(items, widths);
|
|
1345
1467
|
const lines = [];
|
|
1346
1468
|
let start = 0;
|
|
1347
1469
|
let isFirst = true;
|
|
1348
1470
|
let lineIdx = 0;
|
|
1349
1471
|
for (const breakIdx of breaks) {
|
|
1350
1472
|
const width = lineIdx < widths.length ? widths[lineIdx] : widths[widths.length - 1];
|
|
1351
|
-
const line = lineFromRange(entries, start, breakIdx, width, isFirst, resolved);
|
|
1473
|
+
const line = lineFromRange(entries, start, breakIdx, width, isFirst, resolved, profile);
|
|
1352
1474
|
if (line) lines.push(line);
|
|
1353
1475
|
start = breakIdx + 1;
|
|
1354
1476
|
isFirst = false;
|
|
@@ -1357,19 +1479,38 @@ function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, lineWidt
|
|
|
1357
1479
|
if (lines.length > 0) lines[lines.length - 1].isLastInParagraph = true;
|
|
1358
1480
|
return lines;
|
|
1359
1481
|
}
|
|
1482
|
+
function fontLeadingPt(parsed, sizePt, profile) {
|
|
1483
|
+
const s = sizePt / parsed.unitsPerEm;
|
|
1484
|
+
const vm = parsed.vmetrics;
|
|
1485
|
+
if (profile === "word") return {
|
|
1486
|
+
ascentPt: vm.winAscent * s,
|
|
1487
|
+
descentPt: vm.winDescent * s,
|
|
1488
|
+
lineGapPt: 0
|
|
1489
|
+
};
|
|
1490
|
+
const asc = vm.useTypoMetrics ? vm.typoAscent : parsed.ascender;
|
|
1491
|
+
const desc = vm.useTypoMetrics ? vm.typoDescent : parsed.descender;
|
|
1492
|
+
const gap = vm.useTypoMetrics ? vm.typoLineGap : parsed.lineGap;
|
|
1493
|
+
return {
|
|
1494
|
+
ascentPt: asc * s,
|
|
1495
|
+
descentPt: -desc * s,
|
|
1496
|
+
lineGapPt: gap * s
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1360
1499
|
function computeLineHeight(line, p) {
|
|
1361
1500
|
const fontSize = line.maxFontSizePt || 12;
|
|
1501
|
+
const natural = line.metricHeightPt ?? fontSize * 1.2;
|
|
1362
1502
|
const mathH = (line.mathAscentPt ?? 0) + (line.mathDescentPt ?? 0);
|
|
1363
1503
|
const mathNeed = mathH > 0 ? mathH * 1.05 : 0;
|
|
1364
1504
|
if (p.spacingLineRule === "exact" && p.spacingLine > 0) return Math.max(p.spacingLine, mathNeed);
|
|
1365
|
-
if (p.spacingLineRule === "atLeast" && p.spacingLine > 0) return Math.max(
|
|
1505
|
+
if (p.spacingLineRule === "atLeast" && p.spacingLine > 0) return Math.max(natural, p.spacingLine, mathNeed);
|
|
1366
1506
|
const lineTwips = Math.round(p.spacingLine * 20);
|
|
1367
1507
|
const multiple = lineTwips > 0 ? lineTwips / 240 : 1;
|
|
1368
|
-
return Math.max(
|
|
1508
|
+
return Math.max(natural * multiple, mathNeed);
|
|
1369
1509
|
}
|
|
1370
1510
|
function lineDescent(line) {
|
|
1371
1511
|
const fs = line.maxFontSizePt || 12;
|
|
1372
|
-
|
|
1512
|
+
const base = line.metricDescentPt ?? fs * .2;
|
|
1513
|
+
return Math.max(base, line.mathDescentPt ?? 0);
|
|
1373
1514
|
}
|
|
1374
1515
|
function alignmentOffset(alignment, lineWidth, available) {
|
|
1375
1516
|
if (alignment === "right") return Math.max(0, available - lineWidth);
|
|
@@ -33,6 +33,7 @@ async function emitStyledPdfEncrypted(laid, options, doc, encrypt) {
|
|
|
33
33
|
function assembleStyledPdf(laid, options, doc) {
|
|
34
34
|
const { pages: renderedPages } = laid;
|
|
35
35
|
const { structBuilder, sectionCtxs, pdfaProfile, tagged } = laid.pdf;
|
|
36
|
+
const commentNotes = !pdfaProfile && !structBuilder ? laid.pdf.commentNotes : void 0;
|
|
36
37
|
const docInfo = options.pdfUA && !options.info?.title ? {
|
|
37
38
|
...options.info,
|
|
38
39
|
title: "Untitled document"
|
|
@@ -158,6 +159,24 @@ function assembleStyledPdf(laid, options, doc) {
|
|
|
158
159
|
annotParentCount++;
|
|
159
160
|
}
|
|
160
161
|
annots.push(ref(annotRef.id));
|
|
162
|
+
const note = l.anchor !== void 0 ? commentNotes?.get(l.anchor) : void 0;
|
|
163
|
+
if (note) {
|
|
164
|
+
const textAnnot = doc.add(dict({
|
|
165
|
+
Type: name("Annot"),
|
|
166
|
+
Subtype: name("Text"),
|
|
167
|
+
Name: name("Comment"),
|
|
168
|
+
Rect: [
|
|
169
|
+
l.rect[0],
|
|
170
|
+
l.rect[1],
|
|
171
|
+
l.rect[2],
|
|
172
|
+
l.rect[3]
|
|
173
|
+
],
|
|
174
|
+
F: 28,
|
|
175
|
+
Contents: note.contents,
|
|
176
|
+
...note.author !== void 0 ? { T: note.author } : {}
|
|
177
|
+
}));
|
|
178
|
+
annots.push(ref(textAnnot.id));
|
|
179
|
+
}
|
|
161
180
|
}
|
|
162
181
|
if (annots.length > 0) {
|
|
163
182
|
pageEntries["Annots"] = annots;
|
|
@@ -624,6 +643,25 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
624
643
|
const text = tok.bidiLevel % 2 === 1 ? reverseByCodePoint(tok.text) : tok.text;
|
|
625
644
|
return tok.font.measure.encodeTextAsCidHex(text);
|
|
626
645
|
};
|
|
646
|
+
if (line.tokens.some((t) => t.kind === "text" && t.highlight === true)) {
|
|
647
|
+
if (inBT) {
|
|
648
|
+
out.push("ET");
|
|
649
|
+
inBT = false;
|
|
650
|
+
}
|
|
651
|
+
const hlAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
|
|
652
|
+
const hlDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
|
|
653
|
+
const [hr, hg, hb] = hexToRgb01("fff3a3");
|
|
654
|
+
out.push("q");
|
|
655
|
+
out.push(`${formatNumber(hr)} ${formatNumber(hg)} ${formatNumber(hb)} rg`);
|
|
656
|
+
let hx = originX;
|
|
657
|
+
for (const tok of line.tokens) {
|
|
658
|
+
const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
|
|
659
|
+
if (tok.kind === "text" && tok.highlight === true) out.push(`${formatNumber(hx)} ${formatNumber(baselineY - hlDescent)} ${formatNumber(w)} ${formatNumber(hlAscent + hlDescent)} re`);
|
|
660
|
+
hx += w;
|
|
661
|
+
}
|
|
662
|
+
out.push("f");
|
|
663
|
+
out.push("Q");
|
|
664
|
+
}
|
|
627
665
|
if (extraPerSpace > 0 || hasImageToken || hasMathToken || hasRtl) {
|
|
628
666
|
const order = hasRtl ? lineVisualOrder(line) : line.tokens.map((_, i) => i);
|
|
629
667
|
let x = originX;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { DocumentReader, ReadResult } from '../core/ir/adapters.js';
|
|
2
2
|
import { FlowDoc } from '../core/ir/flow.js';
|
|
3
|
+
import { PoNode } from '../core/po-helpers.js';
|
|
3
4
|
export declare function readPptx(bytes: Uint8Array): ReadResult<FlowDoc>;
|
|
5
|
+
export declare function parseXml(data: Uint8Array): Array<PoNode>;
|
|
4
6
|
export declare const pptxReader: DocumentReader<FlowDoc>;
|