reamkit 1.3.0 → 1.5.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 +14 -9
- package/dist/esm/core/converter/facade.d.ts +12 -6
- package/dist/esm/core/converter/facade.js +39 -3
- package/dist/esm/core/converter/ream.d.ts +5 -3
- package/dist/esm/core/converter/ream.js +26 -3
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +18 -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/index.d.ts +2 -0
- package/dist/esm/core/opc/index.js +1 -0
- package/dist/esm/core/opc/opc-writer.d.ts +28 -0
- package/dist/esm/core/opc/opc-writer.js +60 -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 +184 -0
- package/dist/esm/excel/conditional-format.d.ts +16 -0
- package/dist/esm/excel/conditional-format.js +305 -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 +114 -2
- 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 +239 -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 +271 -0
- package/dist/esm/html/html-writer.js +27 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/styled-layout.js +91 -1
- package/dist/esm/word/document-parser.js +47 -13
- package/dist/esm/word/docx-writer.d.ts +4 -0
- package/dist/esm/word/docx-writer.js +602 -0
- package/dist/esm/word/drawing-parser.d.ts +1 -0
- package/dist/esm/word/drawing-parser.js +37 -2
- package/package.json +5 -3
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { pt } from "../core/ir/units.js";
|
|
2
1
|
import { ResourceStore } from "../core/ir/resources.js";
|
|
3
2
|
import { FEATURES } from "../core/ir/features.js";
|
|
4
|
-
import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../core/style-cascade/resolver.js";
|
|
5
|
-
import "../core/style-cascade/index.js";
|
|
6
3
|
import { bytesInclude } from "../core/bytes.js";
|
|
7
4
|
import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
|
|
8
5
|
import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
|
|
@@ -16,14 +13,21 @@ import { parseWorkbook } from "./workbook-parser.js";
|
|
|
16
13
|
import { parseWorksheet } from "./worksheet-parser.js";
|
|
17
14
|
import { EMPTY_XLSX_STYLES, parseXlsxStyles } from "./styles-parser.js";
|
|
18
15
|
import "./index.js";
|
|
19
|
-
import { resolvePrintArea, resolvePrintTitleRows, sectionFromWorksheet, worksheetToBody } from "./print-model.js";
|
|
20
16
|
import { parseSheetDrawing } from "./sheet-drawing.js";
|
|
17
|
+
import { parseTablePart } from "./table-parser.js";
|
|
18
|
+
import { projectSheetDoc } from "./sheet-to-flow.js";
|
|
21
19
|
//#region src/excel/xlsx-reader.ts
|
|
22
20
|
var WORKBOOK_PART = "xl/workbook.xml";
|
|
23
21
|
var SHARED_STRINGS_PART = "xl/sharedStrings.xml";
|
|
24
22
|
var STYLES_PART = "xl/styles.xml";
|
|
25
23
|
var CORE_PROPS_PART = "docProps/core.xml";
|
|
26
24
|
function readXlsx(xlsx) {
|
|
25
|
+
return {
|
|
26
|
+
doc: projectSheetDoc(readXlsxToSheetDoc(xlsx)),
|
|
27
|
+
losses: []
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function readXlsxToSheetDoc(xlsx) {
|
|
27
31
|
const pkg = OpcPackage.open(xlsx);
|
|
28
32
|
const workbookData = pkg.getPart(WORKBOOK_PART);
|
|
29
33
|
if (!workbookData) throw new Error("Not a valid xlsx: missing xl/workbook.xml");
|
|
@@ -34,10 +38,10 @@ function readXlsx(xlsx) {
|
|
|
34
38
|
const stylesData = pkg.getPart(STYLES_PART);
|
|
35
39
|
const styles = stylesData ? parseXlsxStyles(stylesData) : EMPTY_XLSX_STYLES;
|
|
36
40
|
const workbookRels = pkg.getPartRelationships(WORKBOOK_PART);
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const resolveColor =
|
|
40
|
-
|
|
41
|
+
const chartData = /* @__PURE__ */ new Map();
|
|
42
|
+
const palette = buildThemePalette(pkg, workbookRels);
|
|
43
|
+
const resolveColor = makeColorResolver(palette);
|
|
44
|
+
const sheetsOut = [];
|
|
41
45
|
for (let sheetIdx = 0; sheetIdx < sheets.length; sheetIdx++) {
|
|
42
46
|
const sheet = sheets[sheetIdx];
|
|
43
47
|
const sheetRel = workbookRels.find((r) => r.id === sheet.relationshipId);
|
|
@@ -45,61 +49,61 @@ function readXlsx(xlsx) {
|
|
|
45
49
|
const resolved = pkg.resolveRelatedPart(WORKBOOK_PART, sheetRel);
|
|
46
50
|
if (!resolved) continue;
|
|
47
51
|
const worksheet = parseWorksheet(resolved.data);
|
|
48
|
-
|
|
49
|
-
if (sheetIdx > 0) body.push({
|
|
50
|
-
kind: "paragraph",
|
|
51
|
-
paragraph: {
|
|
52
|
-
properties: { pageBreakBefore: true },
|
|
53
|
-
runs: []
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
const printArea = resolvePrintArea(definedNames, sheetIdx);
|
|
57
|
-
const titleRows = resolvePrintTitleRows(definedNames, sheetIdx);
|
|
58
|
-
const gridLines = worksheet.printOptions?.gridLines === true;
|
|
59
|
-
body.push(...worksheetToBody(worksheet, sharedStrings, styles, date1904, {
|
|
60
|
-
...printArea ? { printArea } : {},
|
|
61
|
-
...titleRows ? { titleRows } : {},
|
|
62
|
-
gridLines
|
|
63
|
-
}));
|
|
52
|
+
const charts = [];
|
|
64
53
|
if (worksheet.drawingRelId) {
|
|
65
54
|
const drawingRel = pkg.getPartRelationships(resolved.path).find((r) => r.id === worksheet.drawingRelId);
|
|
66
55
|
const drawing = drawingRel ? pkg.resolveRelatedPart(resolved.path, drawingRel) : void 0;
|
|
67
56
|
if (drawing) for (const ref of parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet)) {
|
|
68
|
-
if (!
|
|
69
|
-
const
|
|
70
|
-
const parsed =
|
|
57
|
+
if (!chartData.has(ref.chartPartPath)) {
|
|
58
|
+
const chartXml = pkg.getPart(ref.chartPartPath);
|
|
59
|
+
const parsed = chartXml ? parseChart(chartXml, resolveColor) : null;
|
|
71
60
|
if (!parsed) continue;
|
|
72
|
-
|
|
61
|
+
chartData.set(ref.chartPartPath, withChartColorStyle(parsed, pkg, ref.chartPartPath, resolveColor));
|
|
73
62
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
width: pt(ref.widthPt),
|
|
79
|
-
height: pt(ref.heightPt),
|
|
80
|
-
paragraphProperties: {}
|
|
81
|
-
}
|
|
63
|
+
charts.push({
|
|
64
|
+
chartPartPath: ref.chartPartPath,
|
|
65
|
+
widthPt: ref.widthPt,
|
|
66
|
+
heightPt: ref.heightPt
|
|
82
67
|
});
|
|
83
68
|
}
|
|
84
69
|
}
|
|
70
|
+
let tables;
|
|
71
|
+
if (worksheet.tablePartRelIds && worksheet.tablePartRelIds.length > 0) {
|
|
72
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
73
|
+
const resolvedTables = [];
|
|
74
|
+
for (const rid of worksheet.tablePartRelIds) {
|
|
75
|
+
const rel = wsRels.find((r) => r.id === rid);
|
|
76
|
+
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
77
|
+
const parsed = part ? parseTablePart(part.data) : void 0;
|
|
78
|
+
if (parsed) resolvedTables.push(resolveTableStyle(parsed, palette));
|
|
79
|
+
}
|
|
80
|
+
if (resolvedTables.length > 0) tables = resolvedTables;
|
|
81
|
+
}
|
|
82
|
+
const grid = tables ? {
|
|
83
|
+
...worksheet,
|
|
84
|
+
tables
|
|
85
|
+
} : worksheet;
|
|
86
|
+
sheetsOut.push({
|
|
87
|
+
name: sheet.name,
|
|
88
|
+
grid,
|
|
89
|
+
...charts.length > 0 ? { charts } : {}
|
|
90
|
+
});
|
|
85
91
|
}
|
|
86
92
|
const coreData = pkg.getPart(CORE_PROPS_PART);
|
|
87
93
|
const info = infoFromCore(coreData ? parseCoreProperties(coreData) : void 0);
|
|
88
94
|
return {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
},
|
|
99
|
-
losses: []
|
|
95
|
+
kind: "sheet",
|
|
96
|
+
sheets: sheetsOut,
|
|
97
|
+
styles,
|
|
98
|
+
sharedStrings,
|
|
99
|
+
definedNames,
|
|
100
|
+
date1904,
|
|
101
|
+
...chartData.size > 0 ? { chartData } : {},
|
|
102
|
+
resources: new ResourceStore(),
|
|
103
|
+
...info ? { info } : {}
|
|
100
104
|
};
|
|
101
105
|
}
|
|
102
|
-
function
|
|
106
|
+
function buildThemePalette(pkg, workbookRels) {
|
|
103
107
|
const palette = new Map(DEFAULT_THEME_PALETTE);
|
|
104
108
|
for (const rel of workbookRels) {
|
|
105
109
|
if (!isOoxmlRel(rel.type, "theme")) continue;
|
|
@@ -108,14 +112,45 @@ function buildXlsxColorResolver(pkg, workbookRels) {
|
|
|
108
112
|
for (const [slot, hex] of parseTheme(resolved.data)) palette.set(slot, hex);
|
|
109
113
|
break;
|
|
110
114
|
}
|
|
111
|
-
return
|
|
115
|
+
return palette;
|
|
116
|
+
}
|
|
117
|
+
function resolveTableStyle(t, palette) {
|
|
118
|
+
const m = t.styleName ? /TableStyle(Light|Medium|Dark)(\d+)/i.exec(t.styleName) : null;
|
|
119
|
+
if (!m) return t;
|
|
120
|
+
const kind = m[1].toLowerCase();
|
|
121
|
+
const column = (Number(m[2]) - 1) % 7;
|
|
122
|
+
const base = column === 0 ? "7F7F7F" : palette.get(`accent${column}`) ?? "4472C4";
|
|
123
|
+
if (kind === "light") return {
|
|
124
|
+
...t,
|
|
125
|
+
headerHex: lighten(base, .6),
|
|
126
|
+
bandHex: lighten(base, .85)
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
...t,
|
|
130
|
+
headerHex: base,
|
|
131
|
+
bandHex: lighten(base, .8),
|
|
132
|
+
headerTextHex: "FFFFFF"
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function lighten(hex, amount) {
|
|
136
|
+
const m = /^#?([0-9a-fA-F]{6})$/.exec(hex);
|
|
137
|
+
const n = m ? parseInt(m[1], 16) : 4485828;
|
|
138
|
+
const ch = (shift) => {
|
|
139
|
+
const c = n >> shift & 255;
|
|
140
|
+
return Math.round(c + (255 - c) * amount);
|
|
141
|
+
};
|
|
142
|
+
const hx = (c) => c.toString(16).padStart(2, "0").toUpperCase();
|
|
143
|
+
return `${hx(ch(16))}${hx(ch(8))}${hx(ch(0))}`;
|
|
112
144
|
}
|
|
113
145
|
var xlsxReader = {
|
|
114
146
|
id: "xlsx",
|
|
115
|
-
produces: "
|
|
147
|
+
produces: "sheet",
|
|
116
148
|
supports: new Set([FEATURES.text, FEATURES.tables]),
|
|
117
149
|
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && bytesInclude(bytes, "xl/workbook.xml"),
|
|
118
|
-
read: (bytes) =>
|
|
150
|
+
read: (bytes) => ({
|
|
151
|
+
doc: readXlsxToSheetDoc(bytes),
|
|
152
|
+
losses: []
|
|
153
|
+
})
|
|
119
154
|
};
|
|
120
155
|
function infoFromCore(core) {
|
|
121
156
|
if (!core) return void 0;
|
|
@@ -0,0 +1,271 @@
|
|
|
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 + 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 tableXml(t, id) {
|
|
142
|
+
const ref = rangeRef(t.ref);
|
|
143
|
+
const name = t.name ?? `Table${id}`;
|
|
144
|
+
const ncols = t.ref.endColumn - t.ref.startColumn + 1;
|
|
145
|
+
const columns = Array.from({ length: ncols }, (_, c) => `<tableColumn id="${c + 1}" name="Column${c + 1}"/>`).join("");
|
|
146
|
+
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}"/>` : "";
|
|
147
|
+
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>";
|
|
148
|
+
}
|
|
149
|
+
function conditionalFormattingXml(cfs) {
|
|
150
|
+
if (!cfs || cfs.length === 0) return "";
|
|
151
|
+
return cfs.map((cf) => {
|
|
152
|
+
return `<conditionalFormatting sqref="${cf.ranges.map(rangeRef).join(" ")}">${cf.rules.map(cfRuleXml).join("")}</conditionalFormatting>`;
|
|
153
|
+
}).join("");
|
|
154
|
+
}
|
|
155
|
+
function cfRuleXml(rule) {
|
|
156
|
+
const p = ` priority="${rule.priority}"`;
|
|
157
|
+
switch (rule.type) {
|
|
158
|
+
case "cellIs": return `<cfRule type="cellIs"${p} operator="${rule.operator}" dxfId="${rule.dxfId}">` + rule.formulas.map((f) => `<formula>${escapeText(f)}</formula>`).join("") + "</cfRule>";
|
|
159
|
+
case "colorScale": return `<cfRule type="colorScale"${p}><colorScale>` + rule.cfvos.map(cfvoXml).join("") + rule.colorsHex.map((c) => `<color rgb="FF${c}"/>`).join("") + "</colorScale></cfRule>";
|
|
160
|
+
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>`;
|
|
161
|
+
case "iconSet": return `<cfRule type="iconSet"${p}><iconSet iconSet="${rule.iconSet}"` + (rule.reverse ? " reverse=\"1\"" : "") + ">" + rule.cfvos.map(cfvoXml).join("") + "</iconSet></cfRule>";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function cfvoXml(cfvo) {
|
|
165
|
+
return `<cfvo type="${cfvo.type}"${cfvo.val !== void 0 ? ` val="${escapeAttr(cfvo.val)}"` : ""}/>`;
|
|
166
|
+
}
|
|
167
|
+
var X14_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
|
|
168
|
+
var XM_NS = "http://schemas.microsoft.com/office/excel/2006/main";
|
|
169
|
+
function sparklineExtXml(sparklines) {
|
|
170
|
+
if (!sparklines || sparklines.length === 0) return "";
|
|
171
|
+
return `<extLst><ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}" xmlns:x14="${X14_NS}"><x14:sparklineGroups xmlns:xm="${XM_NS}">${sparklines.map((sp) => {
|
|
172
|
+
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>`;
|
|
173
|
+
}).join("")}</x14:sparklineGroups></ext></extLst>`;
|
|
174
|
+
}
|
|
175
|
+
function rangeRef(r) {
|
|
176
|
+
const a = cellRef(r.startRow, r.startColumn);
|
|
177
|
+
if (r.startRow === r.endRow && r.startColumn === r.endColumn) return a;
|
|
178
|
+
return `${a}:${cellRef(r.endRow, r.endColumn)}`;
|
|
179
|
+
}
|
|
180
|
+
function printOptionsXml(po) {
|
|
181
|
+
if (!po) return "";
|
|
182
|
+
const flag = (name, v) => v !== void 0 ? ` ${name}="${v ? "1" : "0"}"` : "";
|
|
183
|
+
const attrs = flag("gridLines", po.gridLines) + flag("horizontalCentered", po.horizontalCentered) + flag("verticalCentered", po.verticalCentered);
|
|
184
|
+
return attrs ? `<printOptions${attrs}/>` : "";
|
|
185
|
+
}
|
|
186
|
+
function pageMarginsXml(m) {
|
|
187
|
+
if (!m) return "";
|
|
188
|
+
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}"` : "") + "/>";
|
|
189
|
+
}
|
|
190
|
+
function pageSetupXml(s) {
|
|
191
|
+
if (!s) return "";
|
|
192
|
+
const attr = (name, v) => v !== void 0 ? ` ${name}="${v}"` : "";
|
|
193
|
+
const attrs = attr("paperSize", s.paperSize) + attr("orientation", s.orientation) + attr("scale", s.scale) + attr("fitToWidth", s.fitToWidth) + attr("fitToHeight", s.fitToHeight);
|
|
194
|
+
return attrs ? `<pageSetup${attrs}/>` : "";
|
|
195
|
+
}
|
|
196
|
+
function breaksXml(tag, breaks) {
|
|
197
|
+
if (!breaks || breaks.length === 0) return "";
|
|
198
|
+
const brks = breaks.map((id) => `<brk id="${id}" man="1"/>`).join("");
|
|
199
|
+
return `<${tag} count="${breaks.length}" manualBreakCount="${breaks.length}">${brks}</${tag}>`;
|
|
200
|
+
}
|
|
201
|
+
function cellXml(cell) {
|
|
202
|
+
const ref = cellRef(cell.row, cell.column);
|
|
203
|
+
const sAttr = cell.styleIndex !== void 0 ? ` s="${cell.styleIndex}"` : "";
|
|
204
|
+
const tAttr = cell.type === "n" ? "" : ` t="${cell.type}"`;
|
|
205
|
+
if (cell.type === "inlineStr") return `<c r="${ref}"${sAttr}${tAttr}><is><t xml:space="preserve">${escapeText(cell.inlineText ?? "")}</t></is></c>`;
|
|
206
|
+
if (cell.rawValue === "") return `<c r="${ref}"${sAttr}${tAttr}/>`;
|
|
207
|
+
return `<c r="${ref}"${sAttr}${tAttr}><v>${escapeText(cell.rawValue)}</v></c>`;
|
|
208
|
+
}
|
|
209
|
+
function sharedStringsXml(strings) {
|
|
210
|
+
const items = strings.map((s) => `<si><t xml:space="preserve">${escapeText(s)}</t></si>`).join("");
|
|
211
|
+
return XML_DECL + `<sst xmlns="${MAIN_NS}" count="${strings.length}" uniqueCount="${strings.length}">` + items + "</sst>";
|
|
212
|
+
}
|
|
213
|
+
function stylesXml(styles) {
|
|
214
|
+
const numFmts = styles.numFmts.size > 0 ? `<numFmts count="${styles.numFmts.size}">${[...styles.numFmts].map(([id, code]) => `<numFmt numFmtId="${id}" formatCode="${escapeAttr(code)}"/>`).join("")}</numFmts>` : "";
|
|
215
|
+
const fonts = `<fonts count="${styles.fonts.length}">${styles.fonts.map(fontXml).join("")}</fonts>`;
|
|
216
|
+
const fills = `<fills count="${styles.fills.length}">${styles.fills.map(fillXml).join("")}</fills>`;
|
|
217
|
+
const borders = `<borders count="${styles.borders.length}">${styles.borders.map(borderXml).join("")}</borders>`;
|
|
218
|
+
const cellStyleXfs = "<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>";
|
|
219
|
+
const cellXfs = `<cellXfs count="${styles.cellXfs.length}">${styles.cellXfs.map(cellXfXml).join("")}</cellXfs>`;
|
|
220
|
+
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>` : "";
|
|
221
|
+
return XML_DECL + `<styleSheet xmlns="${MAIN_NS}">` + numFmts + fonts + fills + borders + cellStyleXfs + cellXfs + dxfs + "</styleSheet>";
|
|
222
|
+
}
|
|
223
|
+
function fontXml(f) {
|
|
224
|
+
return `<font>${fontInner(f)}</font>`;
|
|
225
|
+
}
|
|
226
|
+
function fontInner(f) {
|
|
227
|
+
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)}"/>` : "");
|
|
228
|
+
}
|
|
229
|
+
function fillXml(f) {
|
|
230
|
+
return `<fill>${patternFill(f)}</fill>`;
|
|
231
|
+
}
|
|
232
|
+
function patternFill(f) {
|
|
233
|
+
const inner = (f.fgColorHex ? `<fgColor rgb="FF${f.fgColorHex}"/>` : "") + (f.bgColorHex ? `<bgColor rgb="FF${f.bgColorHex}"/>` : "");
|
|
234
|
+
return `<patternFill${f.patternType ? ` patternType="${f.patternType}"` : ""}>${inner}</patternFill>`;
|
|
235
|
+
}
|
|
236
|
+
function borderXml(b) {
|
|
237
|
+
return `<border>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}</border>`;
|
|
238
|
+
}
|
|
239
|
+
function edgeXml(name, edge) {
|
|
240
|
+
if (!edge || !edge.style) return `<${name}/>`;
|
|
241
|
+
const color = edge.colorHex ? `<color rgb="FF${edge.colorHex}"/>` : "";
|
|
242
|
+
return `<${name} style="${edge.style}">${color}</${name}>`;
|
|
243
|
+
}
|
|
244
|
+
function cellXfXml(xf) {
|
|
245
|
+
const apply = applyAttr("applyNumberFormat", xf.applyNumberFormat) + applyAttr("applyFont", xf.applyFont) + applyAttr("applyFill", xf.applyFill) + applyAttr("applyBorder", xf.applyBorder) + applyAttr("applyAlignment", xf.applyAlignment);
|
|
246
|
+
const a = xf.alignment;
|
|
247
|
+
const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}/>` : "";
|
|
248
|
+
return `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}" borderId="${xf.borderId}"${apply}>` + alignment + "</xf>";
|
|
249
|
+
}
|
|
250
|
+
function applyAttr(name, value) {
|
|
251
|
+
return value !== void 0 ? ` ${name}="${value ? "1" : "0"}"` : "";
|
|
252
|
+
}
|
|
253
|
+
var XML_DECL = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
|
|
254
|
+
function cellRef(row, col) {
|
|
255
|
+
let n = col + 1;
|
|
256
|
+
let letters = "";
|
|
257
|
+
while (n > 0) {
|
|
258
|
+
const rem = (n - 1) % 26;
|
|
259
|
+
letters = String.fromCharCode(65 + rem) + letters;
|
|
260
|
+
n = Math.floor((n - 1) / 26);
|
|
261
|
+
}
|
|
262
|
+
return `${letters}${row + 1}`;
|
|
263
|
+
}
|
|
264
|
+
function escapeText(s) {
|
|
265
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
266
|
+
}
|
|
267
|
+
function escapeAttr(s) {
|
|
268
|
+
return escapeText(s).replace(/"/g, """);
|
|
269
|
+
}
|
|
270
|
+
//#endregion
|
|
271
|
+
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";
|
|
@@ -402,6 +403,14 @@ function emitCell(out, cell, table, ctx, pos) {
|
|
|
402
403
|
pushBorder(css, "left", c?.left ?? (pos.firstCol ? t?.left : t?.insideV));
|
|
403
404
|
pushBorder(css, "right", c?.right ?? (pos.lastCol ? t?.right : t?.insideV));
|
|
404
405
|
if (cell.properties.shading) css.push(`background-color:#${cell.properties.shading.colorHex}`);
|
|
406
|
+
if (cell.properties.dataBar) {
|
|
407
|
+
const db = cell.properties.dataBar;
|
|
408
|
+
const clamp = (n) => Math.max(0, Math.min(1, n));
|
|
409
|
+
const start = (clamp(db.startFraction ?? 0) * 100).toFixed(2);
|
|
410
|
+
const end = (clamp((db.startFraction ?? 0) + db.fraction) * 100).toFixed(2);
|
|
411
|
+
const c = db.colorHex;
|
|
412
|
+
css.push(`background-image:linear-gradient(to right,transparent ${start}%,#${c} ${start}%,#${c} ${end}%,transparent ${end}%)`);
|
|
413
|
+
}
|
|
405
414
|
const margins = cell.properties.margins ?? table.properties.defaultCellMargins;
|
|
406
415
|
const padTop = margins?.top ?? 0;
|
|
407
416
|
const padRight = margins?.right ?? 5.4;
|
|
@@ -409,9 +418,27 @@ function emitCell(out, cell, table, ctx, pos) {
|
|
|
409
418
|
const padLeft = margins?.left ?? 5.4;
|
|
410
419
|
css.push(`padding:${fmt(padTop)}pt ${fmt(padRight)}pt ${fmt(padBottom)}pt ${fmt(padLeft)}pt`);
|
|
411
420
|
out.push(`<${tag}${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""} style="${css.join(";")}">`);
|
|
421
|
+
if (cell.properties.icon) out.push(cellIconSvg(cell.properties.icon));
|
|
422
|
+
if (cell.properties.sparkline) out.push(cellSparklineSvg(cell.properties.sparkline));
|
|
412
423
|
for (const child of cell.content) emitBlock(out, child, ctx);
|
|
413
424
|
out.push(`</${tag}>`);
|
|
414
425
|
}
|
|
426
|
+
function cellSparklineSvg(sp) {
|
|
427
|
+
const W = 56;
|
|
428
|
+
const H = 16;
|
|
429
|
+
if (sp.values.length === 0) return "";
|
|
430
|
+
const prims = buildSparkline(sp.kind, sp.values, W, H, sp.colorHex);
|
|
431
|
+
const fmt = (n) => (Math.round(n * 100) / 100).toString();
|
|
432
|
+
const parts = [];
|
|
433
|
+
for (const prim of prims) {
|
|
434
|
+
const d = prim.paths.map((p) => svgPathData(p.segments, fmt)).join(" ");
|
|
435
|
+
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"}"/>`);
|
|
436
|
+
}
|
|
437
|
+
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>`;
|
|
438
|
+
}
|
|
439
|
+
function cellIconSvg(icon) {
|
|
440
|
+
return `<svg width="10" height="10" viewBox="0 0 10 10" fill="${`#${icon.colorHex}`}" style="vertical-align:middle;margin-right:3px">${icon.shape === "square" ? `<rect x="1" y="1" width="8" height="8"/>` : icon.shape === "diamond" ? `<polygon points="5,1 9,5 5,9 1,5"/>` : icon.shape === "triangleUp" ? `<polygon points="5,1 9,9 1,9"/>` : icon.shape === "triangleDown" ? `<polygon points="1,1 9,1 5,9"/>` : icon.shape === "triangleRight" ? `<polygon points="9,5 1,1 1,9"/>` : `<circle cx="5" cy="5" r="4"/>`}</svg>`;
|
|
441
|
+
}
|
|
415
442
|
function pushBorder(css, side, border) {
|
|
416
443
|
if (!border || border.style === "none") return;
|
|
417
444
|
const width = border.width ?? .5;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -25,5 +25,6 @@ export type { FontProvider, FontRequest, FontAnswer } from './core/fonts/provide
|
|
|
25
25
|
export { svgWriter, writeSvg } from './svg/svg-writer.js';
|
|
26
26
|
export type { SvgWriteOptions } from './svg/svg-writer.js';
|
|
27
27
|
export { htmlWriter, writeHtml } from './html/html-writer.js';
|
|
28
|
+
export { docxWriter, writeDocx } from './word/docx-writer.js';
|
|
28
29
|
export { layoutStyledDocument } from './layout/styled-layout.js';
|
|
29
30
|
export type { LaidOutDocument } from './layout/page-doc.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -15,8 +15,9 @@ import { createLanguageHyphenator, getHyphenator } from "./core/hyphenation/inde
|
|
|
15
15
|
import { docxReader, readDocx } from "./word/docx-reader.js";
|
|
16
16
|
import { readXlsx, xlsxReader } from "./excel/xlsx-reader.js";
|
|
17
17
|
import { NO_FONT, callerFontProvider, chainProviders, embeddedDocFontProvider, isEmbeddingRestricted, localFontProvider, readOs2FsType, remoteFontProvider } from "./core/fonts/provider.js";
|
|
18
|
+
import { docxWriter, writeDocx } from "./word/docx-writer.js";
|
|
18
19
|
import { htmlWriter, writeHtml } from "./html/html-writer.js";
|
|
19
20
|
import { svgWriter, writeSvg } from "./svg/svg-writer.js";
|
|
20
21
|
import { createConverter } from "./core/converter/facade.js";
|
|
21
22
|
import { Ream } from "./core/converter/ream.js";
|
|
22
|
-
export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, layoutStyledDocument, localFontProvider, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeHtml, writeSvg, xlsxReader };
|
|
23
|
+
export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, docxWriter, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, layoutStyledDocument, localFontProvider, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeDocx, writeHtml, writeSvg, xlsxReader };
|