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.
- 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/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/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/html/html-writer.js +52 -2
- package/dist/esm/layout/page-doc.d.ts +1 -0
- package/dist/esm/layout/styled-layout.d.ts +8 -1
- package/dist/esm/layout/styled-layout.js +111 -5
- 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-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
|
@@ -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;
|
|
@@ -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,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
|
-
|
|
531
|
-
const
|
|
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 =
|
|
538
|
-
fontColorHex =
|
|
539
|
-
} else if (
|
|
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
|
-
|
|
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;
|