reamkit 1.11.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.
Files changed (32) hide show
  1. package/dist/esm/core/converter/facade.js +1 -1
  2. package/dist/esm/core/converter/project.js +1 -0
  3. package/dist/esm/core/document-model/index.d.ts +1 -1
  4. package/dist/esm/core/document-model/types.d.ts +23 -0
  5. package/dist/esm/core/ir/features.d.ts +1 -0
  6. package/dist/esm/core/ir/features.js +1 -0
  7. package/dist/esm/core/ir/flow.d.ts +3 -1
  8. package/dist/esm/core/po-helpers.d.ts +2 -0
  9. package/dist/esm/core/po-helpers.js +14 -1
  10. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  11. package/dist/esm/core/spreadsheet-model/types.d.ts +16 -0
  12. package/dist/esm/excel/pivot-table-parser.d.ts +2 -0
  13. package/dist/esm/excel/pivot-table-parser.js +68 -0
  14. package/dist/esm/excel/print-model.js +28 -6
  15. package/dist/esm/excel/xlsx-reader.js +33 -2
  16. package/dist/esm/html/html-writer.js +52 -2
  17. package/dist/esm/layout/page-doc.d.ts +1 -0
  18. package/dist/esm/layout/styled-layout.d.ts +8 -1
  19. package/dist/esm/layout/styled-layout.js +111 -5
  20. package/dist/esm/pdf/styled-page-emitter.js +38 -0
  21. package/dist/esm/pptx/pptx-reader.d.ts +2 -0
  22. package/dist/esm/pptx/pptx-reader.js +30 -3
  23. package/dist/esm/pptx/slide-parser.d.ts +8 -2
  24. package/dist/esm/pptx/slide-parser.js +80 -12
  25. package/dist/esm/word/document-parser.d.ts +17 -2
  26. package/dist/esm/word/document-parser.js +178 -18
  27. package/dist/esm/word/docx-reader.js +54 -4
  28. package/dist/esm/word/docx-writer.js +84 -1
  29. package/dist/esm/word/drawing-parser.d.ts +7 -0
  30. package/dist/esm/word/drawing-parser.js +13 -0
  31. package/dist/esm/word/index.d.ts +2 -1
  32. package/package.json +1 -1
@@ -2,6 +2,7 @@ import { FontRegistry } from "../font/font-registry.js";
2
2
  import { FEATURES } from "../ir/features.js";
3
3
  import { ConversionLossError } from "../ir/loss.js";
4
4
  import { layoutStyledDocument } from "../../layout/styled-layout.js";
5
+ import { pptxReader } from "../../pptx/pptx-reader.js";
5
6
  import { docxReader } from "../../word/docx-reader.js";
6
7
  import { projectSheetDoc } from "../../excel/sheet-to-flow.js";
7
8
  import { xlsxReader } from "../../excel/xlsx-reader.js";
@@ -13,7 +14,6 @@ import { writeHtml } from "../../html/html-writer.js";
13
14
  import { writeSvg } from "../../svg/svg-writer.js";
14
15
  import { convertDocxToPdf } from "../../word/docx-to-pdf.js";
15
16
  import { convertXlsxToPdf } from "../../excel/xlsx-to-pdf.js";
16
- import { pptxReader } from "../../pptx/pptx-reader.js";
17
17
  import { pdfReader } from "../../pdf-reader/reader.js";
18
18
  //#region src/core/converter/facade.ts
19
19
  function toFlowDoc(doc) {
@@ -9,6 +9,7 @@ function flowRenderOptions(flow) {
9
9
  ...flow.headersFooters ? { headersFooters: flow.headersFooters } : {},
10
10
  ...flow.footnotes ? { footnotes: flow.footnotes } : {},
11
11
  ...flow.endnotes ? { endnotes: flow.endnotes } : {},
12
+ ...flow.comments ? { comments: flow.comments } : {},
12
13
  resources: flow.resources,
13
14
  ...flow.charts ? { charts: flow.charts } : {},
14
15
  ...flow.embeddedFonts ? { embeddedFonts: flow.embeddedFonts } : {},
@@ -1 +1 @@
1
- export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, CellDataBar, CellIcon, CellIconShape, CellSparkline, Chart, ChartBlock, ChartDataPoint, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, Run, RunProperties, RowProperties, Section, FloatAnchor, SectionColumns, SectionProperties, ShapeBlock, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeLine, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableLook, TableProperties, TableStyleCondition, TableStyleConditionType, TableStyleLayer, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
1
+ export type { AbstractNumbering, Alignment, Border, BorderStyle, BodyElement, Comment, CellBorders, CellMerge, DocumentInfo, CellMargins, CellProperties, CellShading, CellDataBar, CellIcon, CellIconShape, CellSparkline, Chart, ChartBlock, ChartDataPoint, ChartSeries, ChartType, CustomGeometry, CustomPathCmd, DocumentModel, FontFamilyMap, HeaderFooterReference, HeaderFooterType, ImageBlock, InlineImage, MathAccent, MathBar, MathDelimiter, MathEqArray, MathFraction, MathFunc, MathGroupChr, MathLimit, MathMatrix, MathNary, MathNode, MathRadical, MathRow, MathRun, MathScript, Numbering, NumberingFormat, NumberingInstance, NumberingLevel, NumberingReference, PageMargins, PageSize, Paragraph, ParagraphProperties, Run, RunProperties, RowProperties, Section, FloatAnchor, SectionColumns, SectionProperties, ShapeBlock, ShapeDash, ShapeFill, ShapeFillKind, ShapeGeometry, ShapeLine, ShapeTextBody, ShapeTransform, Style, StyleSheet, StyleType, Table, TableCell, TableLook, TableProperties, TableStyleCondition, TableStyleConditionType, TableStyleLayer, TableRow, UnderlineStyle, VerticalAlign, } from './types.js';
@@ -132,6 +132,8 @@ export interface Run {
132
132
  readonly anchor?: string;
133
133
  readonly footnoteRef?: string;
134
134
  readonly endnoteRef?: string;
135
+ readonly commentRef?: string;
136
+ readonly commentRangeRefs?: ReadonlyArray<string>;
135
137
  readonly noteNumber?: true;
136
138
  readonly listMarker?: true;
137
139
  readonly inlineImage?: InlineImage;
@@ -144,6 +146,27 @@ export interface Paragraph {
144
146
  readonly runs: ReadonlyArray<Run>;
145
147
  readonly bookmarks?: ReadonlyArray<string>;
146
148
  }
149
+ export interface Comment {
150
+ readonly content: ReadonlyArray<BodyElement>;
151
+ readonly author?: string;
152
+ readonly initials?: string;
153
+ /** Raw w:date timestamp (ISO 8601), as authored — not reformatted. */
154
+ readonly date?: string;
155
+ /**
156
+ * The author's resolved identity (usually an email) from word/people.xml
157
+ * (`w15:person` → `w15:presenceInfo/@w15:userId`), matched on the author name.
158
+ * Absent when the file ships no people part or the author is not listed.
159
+ */
160
+ readonly authorId?: string;
161
+ /**
162
+ * Microsoft commentsExtended (w15) — the id of the comment this one replies
163
+ * to, forming a thread. Set only when word/commentsExtended.xml links this
164
+ * comment's paragraph to a parent (w15:paraIdParent). Top-level comments omit it.
165
+ */
166
+ readonly parentId?: string;
167
+ /** w15:done — the comment thread was marked resolved. */
168
+ readonly done?: boolean;
169
+ }
147
170
  export type NumberingFormat = 'decimal' | 'lowerLetter' | 'upperLetter' | 'lowerRoman' | 'upperRoman' | 'bullet' | 'none';
148
171
  export interface NumberingReference {
149
172
  readonly numId: string;
@@ -10,6 +10,7 @@ export declare const FEATURES: {
10
10
  readonly imagesJp2: "images.jp2";
11
11
  readonly hyperlinks: "hyperlinks";
12
12
  readonly shapes: "shapes";
13
+ readonly smartArt: "shapes.smartArt";
13
14
  readonly charts: "charts";
14
15
  readonly math: "math";
15
16
  readonly rtl: "rtl";
@@ -10,6 +10,7 @@ var FEATURES = {
10
10
  imagesJp2: "images.jp2",
11
11
  hyperlinks: "hyperlinks",
12
12
  shapes: "shapes",
13
+ smartArt: "shapes.smartArt",
13
14
  charts: "charts",
14
15
  math: "math",
15
16
  rtl: "rtl",
@@ -1,4 +1,4 @@
1
- import { BodyElement, Chart, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../document-model/index.js';
1
+ import { BodyElement, Chart, Comment, DocumentInfo, Numbering, Section, SectionProperties, StyleSheet } from '../document-model/index.js';
2
2
  import { FontRegistry } from '../font/index.js';
3
3
  import { ResourceStore } from './resources.js';
4
4
  export interface FlowDoc {
@@ -14,6 +14,8 @@ export interface FlowDoc {
14
14
  /** §17.11 footnotes/endnotes content by id (separator stubs excluded). */
15
15
  readonly footnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
16
16
  readonly endnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
17
+ /** §17.13.4 review comments by id, anchored from a run's `commentRef`. */
18
+ readonly comments?: ReadonlyMap<string, Comment>;
17
19
  /** Parsed charts keyed by relationship id (ChartBlock.chartRelId). */
18
20
  readonly charts?: ReadonlyMap<string, Chart>;
19
21
  /** Content-addressed binary resources (images). */
@@ -6,6 +6,8 @@ export declare function poChildren(node: PoNode | undefined): ReadonlyArray<PoNo
6
6
  export declare function poChildrenWith(node: PoNode | undefined, tag: string): Array<PoNode>;
7
7
  export declare function poFirstChild(node: PoNode | undefined, tag: string): PoNode | undefined;
8
8
  export declare function poAttr(node: PoNode | undefined, name: string): string | undefined;
9
+ export declare function poAttrLocal(node: PoNode | undefined, name: string): string | undefined;
10
+ export declare function poIsLocal(node: PoNode | undefined, name: string): boolean;
9
11
  export declare function poIntAttr(node: PoNode | undefined, name: string): number | undefined;
10
12
  export declare function poVal(node: PoNode | undefined): string | undefined;
11
13
  export declare function poToggle(node: PoNode | undefined): boolean | undefined;
@@ -28,6 +28,19 @@ function poAttr(node, name) {
28
28
  const v = a[`@_w:${name}`] ?? a[`@_r:${name}`] ?? a[`@_m:${name}`] ?? a[`@_xml:${name}`] ?? a[`@_${name}`];
29
29
  return typeof v === "string" ? v : void 0;
30
30
  }
31
+ function poAttrLocal(node, name) {
32
+ if (!node || typeof node !== "object") return void 0;
33
+ const attrs = node[ATTRS_KEY];
34
+ if (!attrs || typeof attrs !== "object") return void 0;
35
+ for (const [k, v] of Object.entries(attrs)) {
36
+ if (!k.startsWith("@_")) continue;
37
+ if (k.slice(2).split(":").pop() === name && typeof v === "string") return v;
38
+ }
39
+ }
40
+ function poIsLocal(node, name) {
41
+ const tag = poTag(node);
42
+ return tag !== void 0 && (tag === name || tag.split(":").pop() === name);
43
+ }
31
44
  function poIntAttr(node, name) {
32
45
  const v = poAttr(node, name);
33
46
  if (v === void 0) return void 0;
@@ -73,4 +86,4 @@ function poFindDescendant(node, tag) {
73
86
  }
74
87
  }
75
88
  //#endregion
76
- export { poAttr, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poTag, poText, poToggle, poVal };
89
+ export { poAttr, poAttrLocal, poChildren, poChildrenWith, poFindByPath, poFindDescendant, poFirstChild, poIntAttr, poIs, poIsLocal, poTag, poText, poToggle, poVal };
@@ -1 +1 @@
1
- export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRule, ConditionalFormat, SparklineKind, ParsedSparkline, ExcelTable, SheetPane, } from './types.js';
1
+ export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRule, ConditionalFormat, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
@@ -61,6 +61,7 @@ export interface ParsedWorksheet {
61
61
  readonly sparklines?: ReadonlyArray<ParsedSparkline>;
62
62
  readonly tablePartRelIds?: ReadonlyArray<string>;
63
63
  readonly tables?: ReadonlyArray<ExcelTable>;
64
+ readonly pivotTables?: ReadonlyArray<PivotTable>;
64
65
  readonly pane?: SheetPane;
65
66
  }
66
67
  export interface SheetPane {
@@ -81,6 +82,21 @@ export interface ExcelTable {
81
82
  readonly bandHex?: string;
82
83
  readonly headerTextHex?: string;
83
84
  }
85
+ export interface PivotTable {
86
+ readonly ref: MergedRange;
87
+ readonly name?: string;
88
+ readonly styleName?: string;
89
+ readonly firstHeaderRow: number;
90
+ readonly firstDataRow: number;
91
+ readonly firstDataCol: number;
92
+ readonly showRowStripes: boolean;
93
+ readonly showColStripes: boolean;
94
+ readonly rowItemTypes?: ReadonlyArray<string | undefined>;
95
+ readonly colItemTypes?: ReadonlyArray<string | undefined>;
96
+ readonly headerHex?: string;
97
+ readonly bandHex?: string;
98
+ readonly headerTextHex?: string;
99
+ }
84
100
  export type SparklineKind = 'line' | 'column' | 'winLoss';
85
101
  export interface ParsedSparkline {
86
102
  readonly kind: SparklineKind;
@@ -0,0 +1,2 @@
1
+ import { PivotTable } from '../core/spreadsheet-model/index.js';
2
+ export declare function parsePivotTablePart(data: Uint8Array): PivotTable | undefined;
@@ -0,0 +1,68 @@
1
+ import { parseAreaRef } from "./defined-name-ref.js";
2
+ import { XMLParser } from "fast-xml-parser";
3
+ //#region src/excel/pivot-table-parser.ts
4
+ var decoder = new TextDecoder("utf-8");
5
+ var parser = new XMLParser({
6
+ ignoreAttributes: false,
7
+ attributeNamePrefix: "@_",
8
+ parseAttributeValue: false,
9
+ parseTagValue: false,
10
+ removeNSPrefix: true
11
+ });
12
+ function parsePivotTablePart(data) {
13
+ const def = asObj(parser.parse(decoder.decode(data))["pivotTableDefinition"]);
14
+ if (!def) return void 0;
15
+ const loc = asObj(def["location"]);
16
+ const refStr = loc ? strAttr(loc, "ref") : void 0;
17
+ const ref = refStr ? parseAreaRef(refStr) : void 0;
18
+ if (!ref) return void 0;
19
+ const si = asObj(def["pivotTableStyleInfo"]);
20
+ const name = strAttr(def, "name");
21
+ const styleName = si ? strAttr(si, "name") : void 0;
22
+ const rowItemTypes = itemTypes(asObj(def["rowItems"]));
23
+ const colItemTypes = itemTypes(asObj(def["colItems"]));
24
+ const hasRowTotals = rowItemTypes?.some((t) => t !== void 0) ?? false;
25
+ const hasColTotals = colItemTypes?.some((t) => t !== void 0) ?? false;
26
+ return {
27
+ ref,
28
+ ...name ? { name } : {},
29
+ ...styleName ? { styleName } : {},
30
+ firstHeaderRow: (loc ? numAttr(loc, "firstHeaderRow") : void 0) ?? 1,
31
+ firstDataRow: (loc ? numAttr(loc, "firstDataRow") : void 0) ?? 1,
32
+ firstDataCol: (loc ? numAttr(loc, "firstDataCol") : void 0) ?? 0,
33
+ showRowStripes: si ? boolAttr(si, "showRowStripes") : false,
34
+ showColStripes: si ? boolAttr(si, "showColStripes") : false,
35
+ ...hasRowTotals && rowItemTypes ? { rowItemTypes } : {},
36
+ ...hasColTotals && colItemTypes ? { colItemTypes } : {}
37
+ };
38
+ }
39
+ function itemTypes(node) {
40
+ if (!node) return void 0;
41
+ return toArray(node["i"]).map((i) => {
42
+ const o = asObj(i);
43
+ return o ? strAttr(o, "t") : void 0;
44
+ });
45
+ }
46
+ function toArray(v) {
47
+ if (v === void 0 || v === null) return [];
48
+ return Array.isArray(v) ? v : [v];
49
+ }
50
+ function asObj(v) {
51
+ return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
52
+ }
53
+ function strAttr(obj, name) {
54
+ const v = obj[`@_${name}`];
55
+ return typeof v === "string" ? v : void 0;
56
+ }
57
+ function numAttr(obj, name) {
58
+ const v = strAttr(obj, name);
59
+ if (v === void 0) return void 0;
60
+ const n = Number(v);
61
+ return Number.isFinite(n) ? n : void 0;
62
+ }
63
+ function boolAttr(obj, name) {
64
+ const v = strAttr(obj, name);
65
+ return v === "1" || v === "true";
66
+ }
67
+ //#endregion
68
+ export { parsePivotTablePart };
@@ -525,18 +525,23 @@ function collectSeriesValues(cells, area) {
525
525
  for (let r = area.startRow; r <= area.endRow; r++) for (let col = area.startColumn; col <= area.endColumn; col++) out.push(byKey.get(key(r, col)) ?? null);
526
526
  return out;
527
527
  }
528
+ function isPivotTotal(t) {
529
+ return t !== void 0 && t !== "data" && t !== "blank";
530
+ }
528
531
  function buildTableFormatLookup(worksheet) {
529
532
  const out = /* @__PURE__ */ new Map();
530
- for (const t of worksheet.tables ?? []) {
531
- const { ref } = t;
532
- const firstDataRow = ref.startRow + t.headerRowCount;
533
+ const band = (ref, headerRows, style, isTotalRow) => {
534
+ const firstDataRow = ref.startRow + headerRows;
533
535
  for (let r = ref.startRow; r <= ref.endRow; r++) {
534
536
  let colorHex;
535
537
  let fontColorHex;
536
538
  if (r < firstDataRow) {
537
- colorHex = t.headerHex;
538
- fontColorHex = t.headerTextHex;
539
- } else if (t.showRowStripes && t.bandHex) colorHex = (r - firstDataRow) % 2 === 1 ? t.bandHex : void 0;
539
+ colorHex = style.headerHex;
540
+ fontColorHex = style.headerTextHex;
541
+ } else if (isTotalRow?.(r - firstDataRow)) {
542
+ colorHex = style.headerHex;
543
+ fontColorHex = style.headerTextHex;
544
+ } else if (style.showRowStripes && style.bandHex) colorHex = (r - firstDataRow) % 2 === 1 ? style.bandHex : void 0;
540
545
  if (!colorHex && !fontColorHex) continue;
541
546
  const fmt = {
542
547
  ...colorHex ? { shading: { colorHex } } : {},
@@ -544,6 +549,23 @@ function buildTableFormatLookup(worksheet) {
544
549
  };
545
550
  for (let c = ref.startColumn; c <= ref.endColumn; c++) out.set(key(r, c), fmt);
546
551
  }
552
+ };
553
+ for (const t of worksheet.tables ?? []) band(t.ref, t.headerRowCount, t);
554
+ for (const p of worksheet.pivotTables ?? []) band(p.ref, p.firstDataRow, p, (off) => isPivotTotal(p.rowItemTypes?.[off]));
555
+ for (const p of worksheet.pivotTables ?? []) {
556
+ if (!p.colItemTypes || p.headerHex === void 0) continue;
557
+ const firstDataRow = p.ref.startRow + p.firstDataRow;
558
+ const firstDataCol = p.ref.startColumn + p.firstDataCol;
559
+ const totalFmt = {
560
+ shading: { colorHex: p.headerHex },
561
+ ...p.headerTextHex ? { fontColorHex: p.headerTextHex } : {}
562
+ };
563
+ for (let i = 0; i < p.colItemTypes.length; i++) {
564
+ if (!isPivotTotal(p.colItemTypes[i])) continue;
565
+ const c = firstDataCol + i;
566
+ if (c > p.ref.endColumn) continue;
567
+ for (let r = firstDataRow; r <= p.ref.endRow; r++) out.set(key(r, c), totalFmt);
568
+ }
547
569
  }
548
570
  return out;
549
571
  }
@@ -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
- const grid = tables ? {
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;
@@ -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;
@@ -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';
@@ -28,6 +28,8 @@ export interface StyledRenderOptions {
28
28
  readonly headersFooters?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
29
29
  readonly footnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
30
30
  readonly endnotes?: ReadonlyMap<string, ReadonlyArray<BodyElement>>;
31
+ readonly comments?: ReadonlyMap<string, Comment>;
32
+ readonly commentAnnotations?: boolean;
31
33
  readonly resources?: ResourceStore;
32
34
  readonly charts?: ReadonlyMap<string, Chart>;
33
35
  readonly pageWidth?: number;
@@ -53,12 +55,17 @@ export interface BookmarkPosition {
53
55
  readonly pageIdx: number;
54
56
  readonly yTopPt: number;
55
57
  }
58
+ export interface CommentNote {
59
+ readonly author?: string;
60
+ readonly contents: string;
61
+ }
56
62
  export interface PdfLayoutAux {
57
63
  readonly structBuilder: StructTreeBuilder | undefined;
58
64
  readonly sectionCtxs: ReadonlyArray<SectionRenderCtx>;
59
65
  readonly pdfaProfile: PdfAProfile | undefined;
60
66
  readonly tagged: boolean;
61
67
  readonly bookmarks: ReadonlyMap<string, BookmarkPosition>;
68
+ readonly commentNotes?: ReadonlyMap<string, CommentNote>;
62
69
  }
63
70
  export interface LaidOutPdfDocument extends LaidOutDocument {
64
71
  readonly pdf: PdfLayoutAux;