reamkit 1.4.0 → 1.6.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 +10 -8
- package/dist/esm/core/converter/facade.d.ts +12 -6
- package/dist/esm/core/converter/facade.js +34 -5
- package/dist/esm/core/converter/ream.d.ts +5 -3
- package/dist/esm/core/converter/ream.js +18 -4
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +26 -0
- package/dist/esm/core/drawingml/sparkline-geometry.d.ts +8 -0
- package/dist/esm/core/drawingml/sparkline-geometry.js +111 -0
- package/dist/esm/core/ir/adapters.d.ts +5 -5
- package/dist/esm/core/ir/sheet.d.ts +31 -0
- package/dist/esm/core/opc/relationships.js +1 -0
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -0
- package/dist/esm/core/spreadsheet-model/types.d.ts +189 -0
- package/dist/esm/excel/column-bands.d.ts +7 -0
- package/dist/esm/excel/column-bands.js +87 -0
- package/dist/esm/excel/conditional-format.d.ts +16 -0
- package/dist/esm/excel/conditional-format.js +327 -0
- package/dist/esm/excel/index.d.ts +4 -3
- package/dist/esm/excel/print-model.d.ts +2 -1
- package/dist/esm/excel/print-model.js +144 -13
- package/dist/esm/excel/sheet-drawing.d.ts +1 -1
- package/dist/esm/excel/sheet-to-flow.d.ts +3 -0
- package/dist/esm/excel/sheet-to-flow.js +51 -0
- package/dist/esm/excel/styles-parser.d.ts +1 -50
- package/dist/esm/excel/styles-parser.js +38 -1
- package/dist/esm/excel/table-parser.d.ts +2 -0
- package/dist/esm/excel/table-parser.js +51 -0
- package/dist/esm/excel/workbook-parser.d.ts +1 -5
- package/dist/esm/excel/worksheet-parser.d.ts +1 -60
- package/dist/esm/excel/worksheet-parser.js +265 -1
- package/dist/esm/excel/xlsx-reader.d.ts +3 -1
- package/dist/esm/excel/xlsx-reader.js +86 -51
- package/dist/esm/excel/xlsx-writer.d.ts +4 -0
- package/dist/esm/excel/xlsx-writer.js +278 -0
- package/dist/esm/html/html-writer.js +126 -4
- package/dist/esm/layout/styled-layout.js +160 -1
- package/dist/esm/pdf-reader/cmap.d.ts +5 -0
- package/dist/esm/pdf-reader/cmap.js +72 -0
- package/dist/esm/pdf-reader/content.d.ts +14 -0
- package/dist/esm/pdf-reader/content.js +292 -0
- package/dist/esm/pdf-reader/document.d.ts +23 -0
- package/dist/esm/pdf-reader/document.js +230 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +4 -0
- package/dist/esm/pdf-reader/flow-build.js +27 -0
- package/dist/esm/pdf-reader/font.d.ts +4 -0
- package/dist/esm/pdf-reader/font.js +69 -0
- package/dist/esm/pdf-reader/layout.d.ts +3 -0
- package/dist/esm/pdf-reader/layout.js +69 -0
- package/dist/esm/pdf-reader/lexer.d.ts +43 -0
- package/dist/esm/pdf-reader/lexer.js +250 -0
- package/dist/esm/pdf-reader/parser.d.ts +10 -0
- package/dist/esm/pdf-reader/parser.js +86 -0
- package/dist/esm/pdf-reader/reader.d.ts +4 -0
- package/dist/esm/pdf-reader/reader.js +43 -0
- package/dist/esm/pdf-reader/struct-tree.d.ts +14 -0
- package/dist/esm/pdf-reader/struct-tree.js +92 -0
- package/dist/esm/pdf-reader/tagged.d.ts +3 -0
- package/dist/esm/pdf-reader/tagged.js +88 -0
- package/dist/esm/pdf-reader/text.d.ts +3 -0
- package/dist/esm/pdf-reader/text.js +18 -0
- package/package.json +4 -3
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { FEATURES } from "../core/ir/features.js";
|
|
2
|
+
import { buildOpcPackage } from "../core/opc/opc-writer.js";
|
|
3
|
+
import "../core/opc/index.js";
|
|
4
|
+
//#region src/excel/xlsx-writer.ts
|
|
5
|
+
var encoder = new TextEncoder();
|
|
6
|
+
var REL_OFFICE_DOCUMENT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
|
7
|
+
var REL_WORKSHEET = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
|
|
8
|
+
var REL_SHARED_STRINGS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings";
|
|
9
|
+
var REL_STYLES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
|
|
10
|
+
var REL_TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table";
|
|
11
|
+
var CT_WORKBOOK = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
|
|
12
|
+
var CT_WORKSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
|
|
13
|
+
var CT_SHARED_STRINGS = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml";
|
|
14
|
+
var CT_STYLES = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml";
|
|
15
|
+
var CT_TABLE = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml";
|
|
16
|
+
var MAIN_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
|
17
|
+
var R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
18
|
+
function writeXlsx(sheet) {
|
|
19
|
+
const losses = [];
|
|
20
|
+
const worksheetParts = [];
|
|
21
|
+
const tableParts = [];
|
|
22
|
+
const worksheetRels = [];
|
|
23
|
+
const sheetEntries = [];
|
|
24
|
+
const workbookRels = [];
|
|
25
|
+
let tableCounter = 0;
|
|
26
|
+
sheet.sheets.forEach((s, i) => {
|
|
27
|
+
const idx = i + 1;
|
|
28
|
+
const rid = `rId${idx}`;
|
|
29
|
+
const wsPath = `xl/worksheets/sheet${idx}.xml`;
|
|
30
|
+
const tableRelIds = [];
|
|
31
|
+
const wsTableRels = [];
|
|
32
|
+
for (const t of s.grid.tables ?? []) {
|
|
33
|
+
const tid = ++tableCounter;
|
|
34
|
+
const tRid = `rIdTbl${tid}`;
|
|
35
|
+
tableRelIds.push(tRid);
|
|
36
|
+
tableParts.push({
|
|
37
|
+
path: `xl/tables/table${tid}.xml`,
|
|
38
|
+
data: encoder.encode(tableXml(t, tid)),
|
|
39
|
+
contentType: CT_TABLE
|
|
40
|
+
});
|
|
41
|
+
wsTableRels.push(rel(tRid, REL_TABLE, `../tables/table${tid}.xml`));
|
|
42
|
+
}
|
|
43
|
+
if (wsTableRels.length > 0) worksheetRels.push({
|
|
44
|
+
sourcePart: wsPath,
|
|
45
|
+
relationships: wsTableRels
|
|
46
|
+
});
|
|
47
|
+
worksheetParts.push({
|
|
48
|
+
path: wsPath,
|
|
49
|
+
data: encoder.encode(worksheetXml(s.grid, tableRelIds)),
|
|
50
|
+
contentType: CT_WORKSHEET
|
|
51
|
+
});
|
|
52
|
+
workbookRels.push(rel(rid, REL_WORKSHEET, `worksheets/sheet${idx}.xml`));
|
|
53
|
+
sheetEntries.push({
|
|
54
|
+
name: s.name,
|
|
55
|
+
sheetId: idx,
|
|
56
|
+
rid
|
|
57
|
+
});
|
|
58
|
+
recordSheetLosses(s, losses);
|
|
59
|
+
});
|
|
60
|
+
const sstRid = `rId${sheet.sheets.length + 1}`;
|
|
61
|
+
const stylesRid = `rId${sheet.sheets.length + 2}`;
|
|
62
|
+
workbookRels.push(rel(sstRid, REL_SHARED_STRINGS, "sharedStrings.xml"));
|
|
63
|
+
workbookRels.push(rel(stylesRid, REL_STYLES, "styles.xml"));
|
|
64
|
+
return {
|
|
65
|
+
bytes: buildOpcPackage({
|
|
66
|
+
parts: [
|
|
67
|
+
{
|
|
68
|
+
path: "xl/workbook.xml",
|
|
69
|
+
data: encoder.encode(workbookXml(sheetEntries, sheet.definedNames, sheet.date1904)),
|
|
70
|
+
contentType: CT_WORKBOOK
|
|
71
|
+
},
|
|
72
|
+
...worksheetParts,
|
|
73
|
+
...tableParts,
|
|
74
|
+
{
|
|
75
|
+
path: "xl/sharedStrings.xml",
|
|
76
|
+
data: encoder.encode(sharedStringsXml(sheet.sharedStrings)),
|
|
77
|
+
contentType: CT_SHARED_STRINGS
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
path: "xl/styles.xml",
|
|
81
|
+
data: encoder.encode(stylesXml(sheet.styles)),
|
|
82
|
+
contentType: CT_STYLES
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
rootRelationships: [rel("rId1", REL_OFFICE_DOCUMENT, "xl/workbook.xml")],
|
|
86
|
+
partRelationships: [{
|
|
87
|
+
sourcePart: "xl/workbook.xml",
|
|
88
|
+
relationships: workbookRels
|
|
89
|
+
}, ...worksheetRels]
|
|
90
|
+
}),
|
|
91
|
+
losses
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
new Set([FEATURES.text, FEATURES.tables]);
|
|
95
|
+
function rel(id, type, target) {
|
|
96
|
+
return {
|
|
97
|
+
id,
|
|
98
|
+
type,
|
|
99
|
+
target,
|
|
100
|
+
targetMode: "Internal"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function recordSheetLosses(s, losses) {
|
|
104
|
+
if (s.charts && s.charts.length > 0) losses.push({
|
|
105
|
+
severity: "dropped",
|
|
106
|
+
feature: FEATURES.charts,
|
|
107
|
+
detail: `sheet "${s.name}": embedded charts not written back`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function workbookXml(sheets, definedNames, date1904) {
|
|
111
|
+
const sheetTags = sheets.map((s) => `<sheet name="${escapeAttr(s.name)}" sheetId="${s.sheetId}" r:id="${s.rid}"/>`).join("");
|
|
112
|
+
const definedNamesXml = definedNames.length > 0 ? `<definedNames>${definedNames.map((d) => `<definedName name="${escapeAttr(d.name)}"${d.localSheetId !== void 0 ? ` localSheetId="${d.localSheetId}"` : ""}>${escapeText(d.value)}</definedName>`).join("")}</definedNames>` : "";
|
|
113
|
+
return XML_DECL + `<workbook xmlns="${MAIN_NS}" xmlns:r="${R_NS}">` + (date1904 ? "<workbookPr date1904=\"1\"/>" : "") + `<sheets>${sheetTags}</sheets>` + definedNamesXml + "</workbook>";
|
|
114
|
+
}
|
|
115
|
+
function worksheetXml(grid, tableRelIds = []) {
|
|
116
|
+
const dimension = grid.maxRow >= 0 && grid.maxColumn >= 0 ? `<dimension ref="A1:${cellRef(grid.maxRow, grid.maxColumn)}"/>` : "<dimension ref=\"A1\"/>";
|
|
117
|
+
const colsXml = grid.columns.length > 0 ? `<cols>${grid.columns.map((c) => `<col min="${c.min}" max="${c.max}" width="${c.widthChars}" customWidth="1"/>`).join("")}</cols>` : "";
|
|
118
|
+
const heightByRow = /* @__PURE__ */ new Map();
|
|
119
|
+
for (const rh of grid.rowHeights) heightByRow.set(rh.row, {
|
|
120
|
+
heightPt: rh.heightPt,
|
|
121
|
+
customHeight: rh.customHeight
|
|
122
|
+
});
|
|
123
|
+
const cellsByRow = /* @__PURE__ */ new Map();
|
|
124
|
+
for (const cell of grid.cells) {
|
|
125
|
+
let row = cellsByRow.get(cell.row);
|
|
126
|
+
if (!row) {
|
|
127
|
+
row = [];
|
|
128
|
+
cellsByRow.set(cell.row, row);
|
|
129
|
+
}
|
|
130
|
+
row.push(cell);
|
|
131
|
+
}
|
|
132
|
+
const rowsXml = [...new Set([...cellsByRow.keys(), ...heightByRow.keys()])].sort((a, b) => a - b).map((r) => {
|
|
133
|
+
const cells = (cellsByRow.get(r) ?? []).slice().sort((a, b) => a.column - b.column);
|
|
134
|
+
const h = heightByRow.get(r);
|
|
135
|
+
const heightAttrs = h ? ` ht="${h.heightPt}"${h.customHeight ? " customHeight=\"1\"" : ""}` : "";
|
|
136
|
+
return `<row r="${r + 1}"${heightAttrs}>${cells.map(cellXml).join("")}</row>`;
|
|
137
|
+
}).join("");
|
|
138
|
+
const mergesXml = grid.merges.length > 0 ? `<mergeCells count="${grid.merges.length}">${grid.merges.map((m) => `<mergeCell ref="${cellRef(m.startRow, m.startColumn)}:${cellRef(m.endRow, m.endColumn)}"/>`).join("")}</mergeCells>` : "";
|
|
139
|
+
return XML_DECL + `<worksheet xmlns="${MAIN_NS}" xmlns:r="${R_NS}">` + (grid.fitToPage ? "<sheetPr><pageSetUpPr fitToPage=\"1\"/></sheetPr>" : "") + dimension + sheetViewsXml(grid.pane) + colsXml + `<sheetData>${rowsXml}</sheetData>` + mergesXml + conditionalFormattingXml(grid.conditionalFormats) + printOptionsXml(grid.printOptions) + pageMarginsXml(grid.pageMargins) + pageSetupXml(grid.pageSetup) + breaksXml("rowBreaks", grid.rowBreaks) + breaksXml("colBreaks", grid.colBreaks) + (tableRelIds.length > 0 ? `<tableParts count="${tableRelIds.length}">${tableRelIds.map((id) => `<tablePart r:id="${id}"/>`).join("")}</tableParts>` : "") + sparklineExtXml(grid.sparklines) + "</worksheet>";
|
|
140
|
+
}
|
|
141
|
+
function sheetViewsXml(pane) {
|
|
142
|
+
if (!pane || pane.frozenRows <= 0 && pane.frozenCols <= 0) return "";
|
|
143
|
+
const { frozenRows, frozenCols } = pane;
|
|
144
|
+
const topLeftCell = cellRef(frozenRows, frozenCols);
|
|
145
|
+
const activePane = frozenRows > 0 && frozenCols > 0 ? "bottomRight" : frozenRows > 0 ? "bottomLeft" : "topRight";
|
|
146
|
+
return `<sheetViews><sheetView workbookViewId="0"><pane${(frozenCols > 0 ? ` xSplit="${frozenCols}"` : "") + (frozenRows > 0 ? ` ySplit="${frozenRows}"` : "") + ` topLeftCell="${topLeftCell}" activePane="${activePane}" state="frozen"`}/></sheetView></sheetViews>`;
|
|
147
|
+
}
|
|
148
|
+
function tableXml(t, id) {
|
|
149
|
+
const ref = rangeRef(t.ref);
|
|
150
|
+
const name = t.name ?? `Table${id}`;
|
|
151
|
+
const ncols = t.ref.endColumn - t.ref.startColumn + 1;
|
|
152
|
+
const columns = Array.from({ length: ncols }, (_, c) => `<tableColumn id="${c + 1}" name="Column${c + 1}"/>`).join("");
|
|
153
|
+
const styleInfo = t.styleName ? `<tableStyleInfo name="${escapeAttr(t.styleName)}" showFirstColumn="${t.showFirstColumn ? 1 : 0}" showLastColumn="${t.showLastColumn ? 1 : 0}" showRowStripes="${t.showRowStripes ? 1 : 0}" showColumnStripes="${t.showColumnStripes ? 1 : 0}"/>` : "";
|
|
154
|
+
return XML_DECL + `<table xmlns="${MAIN_NS}" id="${id}" name="${escapeAttr(name)}" displayName="${escapeAttr(name)}" ref="${ref}"${t.headerRowCount !== 1 ? ` headerRowCount="${t.headerRowCount}"` : ""} totalsRowShown="0">` + (t.autoFilter ? `<autoFilter ref="${ref}"/>` : "") + `<tableColumns count="${ncols}">${columns}</tableColumns>` + styleInfo + "</table>";
|
|
155
|
+
}
|
|
156
|
+
function conditionalFormattingXml(cfs) {
|
|
157
|
+
if (!cfs || cfs.length === 0) return "";
|
|
158
|
+
return cfs.map((cf) => {
|
|
159
|
+
return `<conditionalFormatting sqref="${cf.ranges.map(rangeRef).join(" ")}">${cf.rules.map(cfRuleXml).join("")}</conditionalFormatting>`;
|
|
160
|
+
}).join("");
|
|
161
|
+
}
|
|
162
|
+
function cfRuleXml(rule) {
|
|
163
|
+
const p = ` priority="${rule.priority}"`;
|
|
164
|
+
switch (rule.type) {
|
|
165
|
+
case "cellIs": return `<cfRule type="cellIs"${p} operator="${rule.operator}" dxfId="${rule.dxfId}">` + rule.formulas.map((f) => `<formula>${escapeText(f)}</formula>`).join("") + "</cfRule>";
|
|
166
|
+
case "colorScale": return `<cfRule type="colorScale"${p}><colorScale>` + rule.cfvos.map(cfvoXml).join("") + rule.colorsHex.map((c) => `<color rgb="FF${c}"/>`).join("") + "</colorScale></cfRule>";
|
|
167
|
+
case "dataBar": return `<cfRule type="dataBar"${p}><dataBar` + (rule.minLength !== void 0 ? ` minLength="${rule.minLength}"` : "") + (rule.maxLength !== void 0 ? ` maxLength="${rule.maxLength}"` : "") + ">" + rule.cfvos.map(cfvoXml).join("") + `<color rgb="FF${rule.colorHex}"/></dataBar></cfRule>`;
|
|
168
|
+
case "iconSet": return `<cfRule type="iconSet"${p}><iconSet iconSet="${rule.iconSet}"` + (rule.reverse ? " reverse=\"1\"" : "") + ">" + rule.cfvos.map(cfvoXml).join("") + "</iconSet></cfRule>";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function cfvoXml(cfvo) {
|
|
172
|
+
return `<cfvo type="${cfvo.type}"${cfvo.val !== void 0 ? ` val="${escapeAttr(cfvo.val)}"` : ""}/>`;
|
|
173
|
+
}
|
|
174
|
+
var X14_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
|
|
175
|
+
var XM_NS = "http://schemas.microsoft.com/office/excel/2006/main";
|
|
176
|
+
function sparklineExtXml(sparklines) {
|
|
177
|
+
if (!sparklines || sparklines.length === 0) return "";
|
|
178
|
+
return `<extLst><ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}" xmlns:x14="${X14_NS}"><x14:sparklineGroups xmlns:xm="${XM_NS}">${sparklines.map((sp) => {
|
|
179
|
+
return `<x14:sparklineGroup${sp.kind === "column" ? " type=\"column\"" : sp.kind === "winLoss" ? " type=\"stacked\"" : ""}>${sp.colorHex ? `<x14:colorSeries rgb="FF${sp.colorHex}"/>` : ""}<x14:sparklines><x14:sparkline><xm:f>${escapeText(sp.dataRange)}</xm:f><xm:sqref>${escapeText(sp.sqref)}</xm:sqref></x14:sparkline></x14:sparklines></x14:sparklineGroup>`;
|
|
180
|
+
}).join("")}</x14:sparklineGroups></ext></extLst>`;
|
|
181
|
+
}
|
|
182
|
+
function rangeRef(r) {
|
|
183
|
+
const a = cellRef(r.startRow, r.startColumn);
|
|
184
|
+
if (r.startRow === r.endRow && r.startColumn === r.endColumn) return a;
|
|
185
|
+
return `${a}:${cellRef(r.endRow, r.endColumn)}`;
|
|
186
|
+
}
|
|
187
|
+
function printOptionsXml(po) {
|
|
188
|
+
if (!po) return "";
|
|
189
|
+
const flag = (name, v) => v !== void 0 ? ` ${name}="${v ? "1" : "0"}"` : "";
|
|
190
|
+
const attrs = flag("gridLines", po.gridLines) + flag("horizontalCentered", po.horizontalCentered) + flag("verticalCentered", po.verticalCentered);
|
|
191
|
+
return attrs ? `<printOptions${attrs}/>` : "";
|
|
192
|
+
}
|
|
193
|
+
function pageMarginsXml(m) {
|
|
194
|
+
if (!m) return "";
|
|
195
|
+
return `<pageMargins left="${m.leftInches}" right="${m.rightInches}" top="${m.topInches}" bottom="${m.bottomInches}"` + (m.headerInches !== void 0 ? ` header="${m.headerInches}"` : "") + (m.footerInches !== void 0 ? ` footer="${m.footerInches}"` : "") + "/>";
|
|
196
|
+
}
|
|
197
|
+
function pageSetupXml(s) {
|
|
198
|
+
if (!s) return "";
|
|
199
|
+
const attr = (name, v) => v !== void 0 ? ` ${name}="${v}"` : "";
|
|
200
|
+
const attrs = attr("paperSize", s.paperSize) + attr("orientation", s.orientation) + attr("scale", s.scale) + attr("fitToWidth", s.fitToWidth) + attr("fitToHeight", s.fitToHeight);
|
|
201
|
+
return attrs ? `<pageSetup${attrs}/>` : "";
|
|
202
|
+
}
|
|
203
|
+
function breaksXml(tag, breaks) {
|
|
204
|
+
if (!breaks || breaks.length === 0) return "";
|
|
205
|
+
const brks = breaks.map((id) => `<brk id="${id}" man="1"/>`).join("");
|
|
206
|
+
return `<${tag} count="${breaks.length}" manualBreakCount="${breaks.length}">${brks}</${tag}>`;
|
|
207
|
+
}
|
|
208
|
+
function cellXml(cell) {
|
|
209
|
+
const ref = cellRef(cell.row, cell.column);
|
|
210
|
+
const sAttr = cell.styleIndex !== void 0 ? ` s="${cell.styleIndex}"` : "";
|
|
211
|
+
const tAttr = cell.type === "n" ? "" : ` t="${cell.type}"`;
|
|
212
|
+
if (cell.type === "inlineStr") return `<c r="${ref}"${sAttr}${tAttr}><is><t xml:space="preserve">${escapeText(cell.inlineText ?? "")}</t></is></c>`;
|
|
213
|
+
if (cell.rawValue === "") return `<c r="${ref}"${sAttr}${tAttr}/>`;
|
|
214
|
+
return `<c r="${ref}"${sAttr}${tAttr}><v>${escapeText(cell.rawValue)}</v></c>`;
|
|
215
|
+
}
|
|
216
|
+
function sharedStringsXml(strings) {
|
|
217
|
+
const items = strings.map((s) => `<si><t xml:space="preserve">${escapeText(s)}</t></si>`).join("");
|
|
218
|
+
return XML_DECL + `<sst xmlns="${MAIN_NS}" count="${strings.length}" uniqueCount="${strings.length}">` + items + "</sst>";
|
|
219
|
+
}
|
|
220
|
+
function stylesXml(styles) {
|
|
221
|
+
const numFmts = styles.numFmts.size > 0 ? `<numFmts count="${styles.numFmts.size}">${[...styles.numFmts].map(([id, code]) => `<numFmt numFmtId="${id}" formatCode="${escapeAttr(code)}"/>`).join("")}</numFmts>` : "";
|
|
222
|
+
const fonts = `<fonts count="${styles.fonts.length}">${styles.fonts.map(fontXml).join("")}</fonts>`;
|
|
223
|
+
const fills = `<fills count="${styles.fills.length}">${styles.fills.map(fillXml).join("")}</fills>`;
|
|
224
|
+
const borders = `<borders count="${styles.borders.length}">${styles.borders.map(borderXml).join("")}</borders>`;
|
|
225
|
+
const cellStyleXfs = "<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>";
|
|
226
|
+
const cellXfs = `<cellXfs count="${styles.cellXfs.length}">${styles.cellXfs.map(cellXfXml).join("")}</cellXfs>`;
|
|
227
|
+
const dxfs = styles.dxfs && styles.dxfs.length > 0 ? `<dxfs count="${styles.dxfs.length}">${styles.dxfs.map((d) => `<dxf>${d.font ? `<font>${fontInner(d.font)}</font>` : ""}${d.fill ? `<fill>${patternFill(d.fill)}</fill>` : ""}</dxf>`).join("")}</dxfs>` : "";
|
|
228
|
+
return XML_DECL + `<styleSheet xmlns="${MAIN_NS}">` + numFmts + fonts + fills + borders + cellStyleXfs + cellXfs + dxfs + "</styleSheet>";
|
|
229
|
+
}
|
|
230
|
+
function fontXml(f) {
|
|
231
|
+
return `<font>${fontInner(f)}</font>`;
|
|
232
|
+
}
|
|
233
|
+
function fontInner(f) {
|
|
234
|
+
return (f.sizePt !== void 0 ? `<sz val="${f.sizePt}"/>` : "") + (f.bold ? "<b/>" : "") + (f.italic ? "<i/>" : "") + (f.underline ? "<u/>" : "") + (f.colorHex ? `<color rgb="FF${f.colorHex}"/>` : "") + (f.name ? `<name val="${escapeAttr(f.name)}"/>` : "");
|
|
235
|
+
}
|
|
236
|
+
function fillXml(f) {
|
|
237
|
+
return `<fill>${patternFill(f)}</fill>`;
|
|
238
|
+
}
|
|
239
|
+
function patternFill(f) {
|
|
240
|
+
const inner = (f.fgColorHex ? `<fgColor rgb="FF${f.fgColorHex}"/>` : "") + (f.bgColorHex ? `<bgColor rgb="FF${f.bgColorHex}"/>` : "");
|
|
241
|
+
return `<patternFill${f.patternType ? ` patternType="${f.patternType}"` : ""}>${inner}</patternFill>`;
|
|
242
|
+
}
|
|
243
|
+
function borderXml(b) {
|
|
244
|
+
return `<border>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}</border>`;
|
|
245
|
+
}
|
|
246
|
+
function edgeXml(name, edge) {
|
|
247
|
+
if (!edge || !edge.style) return `<${name}/>`;
|
|
248
|
+
const color = edge.colorHex ? `<color rgb="FF${edge.colorHex}"/>` : "";
|
|
249
|
+
return `<${name} style="${edge.style}">${color}</${name}>`;
|
|
250
|
+
}
|
|
251
|
+
function cellXfXml(xf) {
|
|
252
|
+
const apply = applyAttr("applyNumberFormat", xf.applyNumberFormat) + applyAttr("applyFont", xf.applyFont) + applyAttr("applyFill", xf.applyFill) + applyAttr("applyBorder", xf.applyBorder) + applyAttr("applyAlignment", xf.applyAlignment);
|
|
253
|
+
const a = xf.alignment;
|
|
254
|
+
const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}/>` : "";
|
|
255
|
+
return `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}" borderId="${xf.borderId}"${apply}>` + alignment + "</xf>";
|
|
256
|
+
}
|
|
257
|
+
function applyAttr(name, value) {
|
|
258
|
+
return value !== void 0 ? ` ${name}="${value ? "1" : "0"}"` : "";
|
|
259
|
+
}
|
|
260
|
+
var XML_DECL = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
|
|
261
|
+
function cellRef(row, col) {
|
|
262
|
+
let n = col + 1;
|
|
263
|
+
let letters = "";
|
|
264
|
+
while (n > 0) {
|
|
265
|
+
const rem = (n - 1) % 26;
|
|
266
|
+
letters = String.fromCharCode(65 + rem) + letters;
|
|
267
|
+
n = Math.floor((n - 1) / 26);
|
|
268
|
+
}
|
|
269
|
+
return `${letters}${row + 1}`;
|
|
270
|
+
}
|
|
271
|
+
function escapeText(s) {
|
|
272
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
273
|
+
}
|
|
274
|
+
function escapeAttr(s) {
|
|
275
|
+
return escapeText(s).replace(/"/g, """);
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
278
|
+
export { writeXlsx };
|
|
@@ -3,6 +3,7 @@ import { detectImageFormat } from "../core/images.js";
|
|
|
3
3
|
import { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
|
|
4
4
|
import "../core/style-cascade/index.js";
|
|
5
5
|
import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
|
|
6
|
+
import { buildSparkline } from "../core/drawingml/sparkline-geometry.js";
|
|
6
7
|
import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
|
|
7
8
|
import { buildChartScene } from "../core/drawingml/chart-geometry.js";
|
|
8
9
|
import { buildShapePaths, buildShapeTransform, buildStroke } from "../core/drawingml/shape-render.js";
|
|
@@ -355,6 +356,21 @@ function emitTable(out, table, ctx) {
|
|
|
355
356
|
return start;
|
|
356
357
|
});
|
|
357
358
|
});
|
|
359
|
+
const frozen = table.properties.frozen;
|
|
360
|
+
const leftOffsets = [];
|
|
361
|
+
const topOffsets = [];
|
|
362
|
+
if (frozen) {
|
|
363
|
+
let left = 0;
|
|
364
|
+
for (const w of table.grid) {
|
|
365
|
+
leftOffsets.push(left);
|
|
366
|
+
left += w;
|
|
367
|
+
}
|
|
368
|
+
let top = 0;
|
|
369
|
+
for (const row of table.rows) {
|
|
370
|
+
topOffsets.push(top);
|
|
371
|
+
top += row.properties.height ?? 15;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
358
374
|
for (let ri = 0; ri < table.rows.length; ri++) {
|
|
359
375
|
const row = table.rows[ri];
|
|
360
376
|
out.push("<tr>");
|
|
@@ -362,14 +378,17 @@ function emitTable(out, table, ctx) {
|
|
|
362
378
|
const cell = row.cells[ci];
|
|
363
379
|
const merge = cell.properties.merge;
|
|
364
380
|
if (merge === "middle" || merge === "end") continue;
|
|
365
|
-
const
|
|
381
|
+
const cs = colStarts[ri][ci];
|
|
382
|
+
const rowSpan = merge === "start" ? mergeRowSpan(table, colStarts, ri, cs) : 1;
|
|
366
383
|
emitCell(out, cell, table, ctx, {
|
|
367
384
|
isHeader: row.properties.isHeader === true,
|
|
368
385
|
firstRow: ri === 0,
|
|
369
386
|
lastRow: ri === table.rows.length - 1,
|
|
370
|
-
firstCol:
|
|
371
|
-
lastCol:
|
|
372
|
-
rowSpan
|
|
387
|
+
firstCol: cs === 0,
|
|
388
|
+
lastCol: cs + (cell.properties.colSpan ?? 1) >= table.grid.length,
|
|
389
|
+
rowSpan,
|
|
390
|
+
...frozen && ri < frozen.rows ? { stickyTop: topOffsets[ri] } : {},
|
|
391
|
+
...frozen && cs < frozen.cols ? { stickyLeft: leftOffsets[cs] } : {}
|
|
373
392
|
});
|
|
374
393
|
}
|
|
375
394
|
out.push("</tr>");
|
|
@@ -402,16 +421,119 @@ function emitCell(out, cell, table, ctx, pos) {
|
|
|
402
421
|
pushBorder(css, "left", c?.left ?? (pos.firstCol ? t?.left : t?.insideV));
|
|
403
422
|
pushBorder(css, "right", c?.right ?? (pos.lastCol ? t?.right : t?.insideV));
|
|
404
423
|
if (cell.properties.shading) css.push(`background-color:#${cell.properties.shading.colorHex}`);
|
|
424
|
+
if (cell.properties.dataBar) {
|
|
425
|
+
const db = cell.properties.dataBar;
|
|
426
|
+
const clamp = (n) => Math.max(0, Math.min(1, n));
|
|
427
|
+
const start = (clamp(db.startFraction ?? 0) * 100).toFixed(2);
|
|
428
|
+
const end = (clamp((db.startFraction ?? 0) + db.fraction) * 100).toFixed(2);
|
|
429
|
+
const c = db.colorHex;
|
|
430
|
+
css.push(`background-image:linear-gradient(to right,transparent ${start}%,#${c} ${start}%,#${c} ${end}%,transparent ${end}%)`);
|
|
431
|
+
}
|
|
405
432
|
const margins = cell.properties.margins ?? table.properties.defaultCellMargins;
|
|
406
433
|
const padTop = margins?.top ?? 0;
|
|
407
434
|
const padRight = margins?.right ?? 5.4;
|
|
408
435
|
const padBottom = margins?.bottom ?? 0;
|
|
409
436
|
const padLeft = margins?.left ?? 5.4;
|
|
410
437
|
css.push(`padding:${fmt(padTop)}pt ${fmt(padRight)}pt ${fmt(padBottom)}pt ${fmt(padLeft)}pt`);
|
|
438
|
+
if (pos.stickyTop !== void 0 || pos.stickyLeft !== void 0) {
|
|
439
|
+
css.push("position:sticky");
|
|
440
|
+
if (pos.stickyTop !== void 0) css.push(`top:${fmt(pos.stickyTop)}pt`);
|
|
441
|
+
if (pos.stickyLeft !== void 0) css.push(`left:${fmt(pos.stickyLeft)}pt`);
|
|
442
|
+
const z = pos.stickyTop !== void 0 && pos.stickyLeft !== void 0 ? 3 : pos.stickyTop !== void 0 ? 2 : 1;
|
|
443
|
+
css.push(`z-index:${z}`);
|
|
444
|
+
if (!cell.properties.shading) css.push("background-color:#fff");
|
|
445
|
+
}
|
|
411
446
|
out.push(`<${tag}${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""} style="${css.join(";")}">`);
|
|
447
|
+
if (cell.properties.icon) out.push(cellIconSvg(cell.properties.icon));
|
|
448
|
+
if (cell.properties.sparkline) out.push(cellSparklineSvg(cell.properties.sparkline));
|
|
412
449
|
for (const child of cell.content) emitBlock(out, child, ctx);
|
|
413
450
|
out.push(`</${tag}>`);
|
|
414
451
|
}
|
|
452
|
+
function cellSparklineSvg(sp) {
|
|
453
|
+
const W = 56;
|
|
454
|
+
const H = 16;
|
|
455
|
+
if (sp.values.length === 0) return "";
|
|
456
|
+
const prims = buildSparkline(sp.kind, sp.values, W, H, sp.colorHex);
|
|
457
|
+
const fmt = (n) => (Math.round(n * 100) / 100).toString();
|
|
458
|
+
const parts = [];
|
|
459
|
+
for (const prim of prims) {
|
|
460
|
+
const d = prim.paths.map((p) => svgPathData(p.segments, fmt)).join(" ");
|
|
461
|
+
parts.push(prim.stroke ? `<path d="${d}" fill="none" stroke="#${prim.stroke.colorHex}" stroke-width="${prim.stroke.widthPt}"/>` : `<path d="${d}" fill="#${prim.fillColorHex ?? "376092"}"/>`);
|
|
462
|
+
}
|
|
463
|
+
return `<svg width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" style="vertical-align:middle"><g transform="translate(0,${H}) scale(1,-1)">${parts.join("")}</g></svg>`;
|
|
464
|
+
}
|
|
465
|
+
var ICON_EMPTY_HEX = "BFBFBF";
|
|
466
|
+
function cellIconSvg(icon) {
|
|
467
|
+
const fill = `#${icon.colorHex}`;
|
|
468
|
+
let body;
|
|
469
|
+
switch (icon.shape) {
|
|
470
|
+
case "square":
|
|
471
|
+
body = `<rect x="1" y="1" width="8" height="8" fill="${fill}"/>`;
|
|
472
|
+
break;
|
|
473
|
+
case "diamond":
|
|
474
|
+
body = `<polygon points="5,1 9,5 5,9 1,5" fill="${fill}"/>`;
|
|
475
|
+
break;
|
|
476
|
+
case "triangleUp":
|
|
477
|
+
body = `<polygon points="5,1 9,9 1,9" fill="${fill}"/>`;
|
|
478
|
+
break;
|
|
479
|
+
case "triangleDown":
|
|
480
|
+
body = `<polygon points="1,1 9,1 5,9" fill="${fill}"/>`;
|
|
481
|
+
break;
|
|
482
|
+
case "triangleRight":
|
|
483
|
+
body = `<polygon points="9,5 1,1 1,9" fill="${fill}"/>`;
|
|
484
|
+
break;
|
|
485
|
+
case "check":
|
|
486
|
+
body = `<polyline points="1.6,5 4,7.4 8.4,2.6" fill="none" stroke="${fill}" stroke-width="1.6"/>`;
|
|
487
|
+
break;
|
|
488
|
+
case "cross":
|
|
489
|
+
body = `<path d="M2.4 2.4 L7.6 7.6 M2.4 7.6 L7.6 2.4" fill="none" stroke="${fill}" stroke-width="1.6"/>`;
|
|
490
|
+
break;
|
|
491
|
+
case "exclamation":
|
|
492
|
+
body = `<rect x="4.1" y="1" width="1.8" height="5.6" fill="${fill}"/><rect x="4.1" y="7.4" width="1.8" height="1.6" fill="${fill}"/>`;
|
|
493
|
+
break;
|
|
494
|
+
case "bars":
|
|
495
|
+
body = barsIconSvg(icon.fill, fill, `#${ICON_EMPTY_HEX}`);
|
|
496
|
+
break;
|
|
497
|
+
case "pie":
|
|
498
|
+
body = pieIconSvg(icon.fill, fill, `#${ICON_EMPTY_HEX}`);
|
|
499
|
+
break;
|
|
500
|
+
case "circle":
|
|
501
|
+
body = `<circle cx="5" cy="5" r="4" fill="${fill}"/>`;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
return `<svg width="10" height="10" viewBox="0 0 10 10" style="vertical-align:middle;margin-right:3px">${body}</svg>`;
|
|
505
|
+
}
|
|
506
|
+
var round2 = (n) => (Math.round(n * 100) / 100).toString();
|
|
507
|
+
function barsIconSvg(fill, color, empty) {
|
|
508
|
+
const n = Math.max(1, fill?.levels ?? 4);
|
|
509
|
+
const filled = fill?.filled ?? 0;
|
|
510
|
+
const gap = 10 * .12;
|
|
511
|
+
const bw = (10 - gap * (n - 1)) / n;
|
|
512
|
+
let out = "";
|
|
513
|
+
for (let i = 0; i < n; i++) {
|
|
514
|
+
const h = 10 * (.32 + .68 * (i + 1) / n);
|
|
515
|
+
out += `<rect x="${round2(i * (bw + gap))}" y="${round2(10 - h)}" width="${round2(bw)}" height="${round2(h)}" fill="${i < filled ? color : empty}"/>`;
|
|
516
|
+
}
|
|
517
|
+
return out;
|
|
518
|
+
}
|
|
519
|
+
function pieIconSvg(fill, color, empty) {
|
|
520
|
+
const cx = 5;
|
|
521
|
+
const cy = 5;
|
|
522
|
+
const r = 4.4;
|
|
523
|
+
const levels = Math.max(1, fill?.levels ?? 4);
|
|
524
|
+
const filled = Math.max(0, Math.min(levels, fill?.filled ?? 0));
|
|
525
|
+
let out = `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${empty}"/>`;
|
|
526
|
+
if (filled >= levels) out += `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${color}"/>`;
|
|
527
|
+
else if (filled > 0) {
|
|
528
|
+
const frac = filled / levels;
|
|
529
|
+
const ang = -Math.PI / 2 + frac * 2 * Math.PI;
|
|
530
|
+
const ex = cx + r * Math.cos(ang);
|
|
531
|
+
const ey = cy + r * Math.sin(ang);
|
|
532
|
+
const large = frac > .5 ? 1 : 0;
|
|
533
|
+
out += `<path d="M${cx} ${cy} L${cx} ${round2(cy - r)} A${r} ${r} 0 ${large} 1 ${round2(ex)} ${round2(ey)} Z" fill="${color}"/>`;
|
|
534
|
+
}
|
|
535
|
+
return out;
|
|
536
|
+
}
|
|
415
537
|
function pushBorder(css, side, border) {
|
|
416
538
|
if (!border || border.style === "none") return;
|
|
417
539
|
const width = border.width ?? .5;
|
|
@@ -12,6 +12,7 @@ import { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN } from "../core/style-
|
|
|
12
12
|
import { resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
|
|
13
13
|
import "../core/style-cascade/index.js";
|
|
14
14
|
import { PathBuilder, flipTransform } from "../core/vector.js";
|
|
15
|
+
import { buildSparkline } from "../core/drawingml/sparkline-geometry.js";
|
|
15
16
|
import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
|
|
16
17
|
import { buildChartScene } from "../core/drawingml/chart-geometry.js";
|
|
17
18
|
import { layoutMath, mathGlyphSegments, variantStyle } from "./math-layout.js";
|
|
@@ -560,6 +561,101 @@ function buildChartLayout(chart, widthPt, heightPt, font) {
|
|
|
560
561
|
function rectAtPath(x, y, w, h) {
|
|
561
562
|
return new PathBuilder().moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).close().build();
|
|
562
563
|
}
|
|
564
|
+
var CF_ICON_SIZE_PT = 9;
|
|
565
|
+
var CF_ICON_GUTTER_PT = 12;
|
|
566
|
+
var CF_ICON_EMPTY_HEX = "BFBFBF";
|
|
567
|
+
function buildCellIconShape(icon, s) {
|
|
568
|
+
const color = icon.colorHex;
|
|
569
|
+
switch (icon.shape) {
|
|
570
|
+
case "bars": return barsIconPrims(s, color, icon.fill);
|
|
571
|
+
case "pie": return pieIconPrims(s, color, icon.fill);
|
|
572
|
+
case "check": return [{
|
|
573
|
+
paths: [checkPath(s)],
|
|
574
|
+
stroke: {
|
|
575
|
+
colorHex: color,
|
|
576
|
+
widthPt: s * .16
|
|
577
|
+
}
|
|
578
|
+
}];
|
|
579
|
+
case "cross": return [{
|
|
580
|
+
paths: crossPaths(s),
|
|
581
|
+
stroke: {
|
|
582
|
+
colorHex: color,
|
|
583
|
+
widthPt: s * .16
|
|
584
|
+
}
|
|
585
|
+
}];
|
|
586
|
+
case "exclamation": return [{
|
|
587
|
+
paths: [rectAtPath(s * .41, s * .34, s * .18, s * .56), rectAtPath(s * .41, s * .1, s * .18, s * .16)],
|
|
588
|
+
fillColorHex: color
|
|
589
|
+
}];
|
|
590
|
+
default: return [{
|
|
591
|
+
paths: [cellIconPath(icon.shape, s)],
|
|
592
|
+
fillColorHex: color
|
|
593
|
+
}];
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function cellIconPath(shape, s) {
|
|
597
|
+
switch (shape) {
|
|
598
|
+
case "square": return rectAtPath(s * .12, s * .12, s * .76, s * .76);
|
|
599
|
+
case "diamond": return new PathBuilder().moveTo(s / 2, s * .92).lineTo(s * .92, s / 2).lineTo(s / 2, s * .08).lineTo(s * .08, s / 2).close().build();
|
|
600
|
+
case "triangleUp": return trianglePath([s / 2, s * .9], [s * .1, s * .12], [s * .9, s * .12]);
|
|
601
|
+
case "triangleDown": return trianglePath([s / 2, s * .1], [s * .1, s * .88], [s * .9, s * .88]);
|
|
602
|
+
case "triangleRight": return trianglePath([s * .9, s / 2], [s * .12, s * .1], [s * .12, s * .9]);
|
|
603
|
+
case "circle": return circlePath(s / 2, s / 2, s * .42);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function barsIconPrims(s, color, fill) {
|
|
607
|
+
const n = Math.max(1, fill?.levels ?? 4);
|
|
608
|
+
const filled = fill?.filled ?? 0;
|
|
609
|
+
const gap = s * .12;
|
|
610
|
+
const bw = (s - gap * (n - 1)) / n;
|
|
611
|
+
const prims = [];
|
|
612
|
+
for (let i = 0; i < n; i++) {
|
|
613
|
+
const h = s * (.32 + .68 * (i + 1) / n);
|
|
614
|
+
prims.push({
|
|
615
|
+
paths: [rectAtPath(i * (bw + gap), 0, bw, h)],
|
|
616
|
+
fillColorHex: i < filled ? color : CF_ICON_EMPTY_HEX
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
return prims;
|
|
620
|
+
}
|
|
621
|
+
function pieIconPrims(s, color, fill) {
|
|
622
|
+
const cx = s / 2;
|
|
623
|
+
const cy = s / 2;
|
|
624
|
+
const r = s * .46;
|
|
625
|
+
const levels = Math.max(1, fill?.levels ?? 4);
|
|
626
|
+
const filled = Math.max(0, Math.min(levels, fill?.filled ?? 0));
|
|
627
|
+
const prims = [{
|
|
628
|
+
paths: [circlePath(cx, cy, r)],
|
|
629
|
+
fillColorHex: CF_ICON_EMPTY_HEX
|
|
630
|
+
}];
|
|
631
|
+
if (filled >= levels) prims.push({
|
|
632
|
+
paths: [circlePath(cx, cy, r)],
|
|
633
|
+
fillColorHex: color
|
|
634
|
+
});
|
|
635
|
+
else if (filled > 0) {
|
|
636
|
+
const start = Math.PI / 2;
|
|
637
|
+
const sweep = -(filled / levels) * 2 * Math.PI;
|
|
638
|
+
const [sx, sy] = arcPoint(cx, cy, r, r, start);
|
|
639
|
+
prims.push({
|
|
640
|
+
paths: [new PathBuilder().moveTo(cx, cy).lineTo(sx, sy).append(arcToBeziers(cx, cy, r, r, start, sweep)).close().build()],
|
|
641
|
+
fillColorHex: color
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return prims;
|
|
645
|
+
}
|
|
646
|
+
function checkPath(s) {
|
|
647
|
+
return new PathBuilder().moveTo(s * .16, s * .5).lineTo(s * .4, s * .26).lineTo(s * .84, s * .78).build();
|
|
648
|
+
}
|
|
649
|
+
function crossPaths(s) {
|
|
650
|
+
return [new PathBuilder().moveTo(s * .24, s * .24).lineTo(s * .76, s * .76).build(), new PathBuilder().moveTo(s * .24, s * .76).lineTo(s * .76, s * .24).build()];
|
|
651
|
+
}
|
|
652
|
+
function trianglePath(a, b, c) {
|
|
653
|
+
return new PathBuilder().moveTo(a[0], a[1]).lineTo(b[0], b[1]).lineTo(c[0], c[1]).close().build();
|
|
654
|
+
}
|
|
655
|
+
function circlePath(cx, cy, r) {
|
|
656
|
+
const k = .5522847498307936 * r;
|
|
657
|
+
return new PathBuilder().moveTo(cx + r, cy).cubicTo(cx + r, cy + k, cx + k, cy + r, cx, cy + r).cubicTo(cx - k, cy + r, cx - r, cy + k, cx - r, cy).cubicTo(cx - r, cy - k, cx - k, cy - r, cx, cy - r).cubicTo(cx + k, cy - r, cx + r, cy - k, cx + r, cy).close().build();
|
|
658
|
+
}
|
|
563
659
|
function rectPrim(r) {
|
|
564
660
|
return {
|
|
565
661
|
paths: [rectAtPath(r.x, r.y, r.w, r.h)],
|
|
@@ -1431,8 +1527,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
1431
1527
|
const tableMar = tableProps.defaultCellMargins;
|
|
1432
1528
|
const padTopPt = cellMar?.top ?? tableMar?.top ?? 0;
|
|
1433
1529
|
const padBottomPt = cellMar?.bottom ?? tableMar?.bottom ?? 0;
|
|
1434
|
-
const
|
|
1530
|
+
const padLeftBase = cellMar?.left ?? tableMar?.left ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
|
|
1435
1531
|
const padRightPt = cellMar?.right ?? tableMar?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
|
|
1532
|
+
const padLeftPt = padLeftBase + (cell.properties.icon ? CF_ICON_GUTTER_PT : 0);
|
|
1436
1533
|
const innerWidth = Math.max(1, widthPt - padLeftPt - padRightPt);
|
|
1437
1534
|
const lines = [];
|
|
1438
1535
|
const nestedTables = [];
|
|
@@ -1462,6 +1559,9 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
|
|
|
1462
1559
|
padLeftPt,
|
|
1463
1560
|
borders,
|
|
1464
1561
|
...cell.properties.shading?.colorHex ? { shadingColorHex: cell.properties.shading.colorHex } : {},
|
|
1562
|
+
...cell.properties.dataBar ? { dataBar: cell.properties.dataBar } : {},
|
|
1563
|
+
...cell.properties.icon ? { icon: cell.properties.icon } : {},
|
|
1564
|
+
...cell.properties.sparkline ? { sparkline: cell.properties.sparkline } : {},
|
|
1465
1565
|
lines,
|
|
1466
1566
|
...nestedTables.length > 0 ? { nestedTables } : {},
|
|
1467
1567
|
contentHeightPt,
|
|
@@ -2084,6 +2184,65 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
2084
2184
|
height: pt(row.heightPt),
|
|
2085
2185
|
fillColorHex: cell.shadingColorHex
|
|
2086
2186
|
});
|
|
2187
|
+
if (cell.dataBar && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
2188
|
+
const start = Math.max(0, Math.min(1, cell.dataBar.startFraction ?? 0));
|
|
2189
|
+
const barWidth = cell.widthPt * Math.max(0, Math.min(1, cell.dataBar.fraction));
|
|
2190
|
+
if (barWidth > 0) out.push({
|
|
2191
|
+
type: "fill",
|
|
2192
|
+
x: pt(cellX + cell.widthPt * start),
|
|
2193
|
+
y: pt(pageHeight - rowBottom - row.heightPt),
|
|
2194
|
+
width: pt(barWidth),
|
|
2195
|
+
height: pt(row.heightPt),
|
|
2196
|
+
fillColorHex: cell.dataBar.colorHex
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
if (cell.icon && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
2200
|
+
const iconSize = Math.min(CF_ICON_SIZE_PT, Math.max(0, row.heightPt - 1));
|
|
2201
|
+
if (iconSize > 0) {
|
|
2202
|
+
const iconTransform = flipTransform([
|
|
2203
|
+
1,
|
|
2204
|
+
0,
|
|
2205
|
+
0,
|
|
2206
|
+
1,
|
|
2207
|
+
cellX + (CF_ICON_GUTTER_PT - iconSize) / 2,
|
|
2208
|
+
rowBottom + (row.heightPt - iconSize) / 2
|
|
2209
|
+
], pageHeight);
|
|
2210
|
+
for (const prim of buildCellIconShape(cell.icon, iconSize)) out.push({
|
|
2211
|
+
type: "shape",
|
|
2212
|
+
shape: {
|
|
2213
|
+
paths: prim.paths,
|
|
2214
|
+
...prim.fillColorHex ? { fillColorHex: prim.fillColorHex } : {},
|
|
2215
|
+
...prim.stroke ? { stroke: prim.stroke } : {},
|
|
2216
|
+
transform: iconTransform
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
if (cell.sparkline && cell.sparkline.values.length > 0 && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
2222
|
+
const inset = 1.5;
|
|
2223
|
+
const sw = cell.widthPt - 2 * inset;
|
|
2224
|
+
const sh = row.heightPt - 2 * inset;
|
|
2225
|
+
if (sw > 0 && sh > 0) {
|
|
2226
|
+
const prims = buildSparkline(cell.sparkline.kind, cell.sparkline.values, sw, sh, cell.sparkline.colorHex);
|
|
2227
|
+
const transform = flipTransform([
|
|
2228
|
+
1,
|
|
2229
|
+
0,
|
|
2230
|
+
0,
|
|
2231
|
+
1,
|
|
2232
|
+
cellX + inset,
|
|
2233
|
+
rowBottom + inset
|
|
2234
|
+
], pageHeight);
|
|
2235
|
+
for (const prim of prims) out.push({
|
|
2236
|
+
type: "shape",
|
|
2237
|
+
shape: {
|
|
2238
|
+
paths: prim.paths,
|
|
2239
|
+
...prim.fillColorHex ? { fillColorHex: prim.fillColorHex } : {},
|
|
2240
|
+
...prim.stroke ? { stroke: prim.stroke } : {},
|
|
2241
|
+
transform
|
|
2242
|
+
}
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2087
2246
|
emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, pageHeight, row.rowIdx, row.rowCount, colCount);
|
|
2088
2247
|
if (cell.mergeRole === "middle" || cell.mergeRole === "end") continue;
|
|
2089
2248
|
let textY = rowTop - cell.padTopPt;
|