@cj-tech-master/excelts 4.0.4 → 4.1.0-canary.20260110033117.75bcac1
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/browser/index.browser.d.ts +2 -0
- package/dist/browser/index.browser.js +1 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/modules/excel/cell.js +39 -1
- package/dist/browser/modules/excel/enums.d.ts +2 -1
- package/dist/browser/modules/excel/enums.js +2 -1
- package/dist/browser/modules/excel/form-control.d.ts +157 -0
- package/dist/browser/modules/excel/form-control.js +267 -0
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -0
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +19 -1
- package/dist/browser/modules/excel/table.d.ts +6 -2
- package/dist/browser/modules/excel/table.js +33 -5
- package/dist/browser/modules/excel/types.d.ts +5 -1
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +4 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +12 -2
- package/dist/browser/modules/excel/worksheet.d.ts +32 -0
- package/dist/browser/modules/excel/worksheet.js +44 -1
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +3 -1
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +24 -1
- package/dist/browser/modules/excel/xlsx/xform/core/feature-property-bag-xform.d.ts +8 -0
- package/dist/browser/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +36 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +22 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +11 -0
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +3 -0
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +43 -5
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/modules/excel/cell.js +39 -1
- package/dist/cjs/modules/excel/enums.js +2 -1
- package/dist/cjs/modules/excel/form-control.js +270 -0
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +19 -1
- package/dist/cjs/modules/excel/table.js +33 -5
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +14 -2
- package/dist/cjs/modules/excel/worksheet.js +44 -1
- package/dist/cjs/modules/excel/xlsx/rel-type.js +3 -1
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +23 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +39 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +55 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +184 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +23 -0
- package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +11 -0
- package/dist/cjs/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +42 -4
- package/dist/esm/index.browser.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/modules/excel/cell.js +39 -1
- package/dist/esm/modules/excel/enums.js +2 -1
- package/dist/esm/modules/excel/form-control.js +267 -0
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +19 -1
- package/dist/esm/modules/excel/table.js +33 -5
- package/dist/esm/modules/excel/utils/ooxml-paths.js +12 -2
- package/dist/esm/modules/excel/worksheet.js +44 -1
- package/dist/esm/modules/excel/xlsx/rel-type.js +3 -1
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +24 -1
- package/dist/esm/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +36 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
- package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +11 -0
- package/dist/esm/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +43 -5
- package/dist/iife/excelts.iife.js +629 -40
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +30 -30
- package/dist/types/index.browser.d.ts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/modules/excel/enums.d.ts +2 -1
- package/dist/types/modules/excel/form-control.d.ts +157 -0
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -0
- package/dist/types/modules/excel/table.d.ts +6 -2
- package/dist/types/modules/excel/types.d.ts +5 -1
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +4 -0
- package/dist/types/modules/excel/worksheet.d.ts +32 -0
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xform/core/feature-property-bag-xform.d.ts +8 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +22 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -0
- package/dist/types/modules/excel/xlsx/xform/style/style-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xform/style/styles-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +3 -0
- package/package.json +9 -9
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { XmlStream } from "../../../utils/xml-stream.js";
|
|
2
|
+
import { BaseXform } from "../base-xform.js";
|
|
3
|
+
import { VmlShapeXform } from "../comment/vml-shape-xform.js";
|
|
4
|
+
import { FormCheckbox } from "../../../form-control.js";
|
|
5
|
+
class VmlDrawingXform extends BaseXform {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.map = {
|
|
9
|
+
"v:shape": new VmlShapeXform()
|
|
10
|
+
};
|
|
11
|
+
this.model = { comments: [], formControls: [] };
|
|
12
|
+
}
|
|
13
|
+
get tag() {
|
|
14
|
+
return "xml";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Render VML drawing containing both notes and form controls
|
|
18
|
+
*/
|
|
19
|
+
render(xmlStream, model) {
|
|
20
|
+
const renderModel = model || this.model;
|
|
21
|
+
const hasComments = renderModel.comments && renderModel.comments.length > 0;
|
|
22
|
+
const hasFormControls = renderModel.formControls && renderModel.formControls.length > 0;
|
|
23
|
+
xmlStream.openXml(XmlStream.StdDocAttributes);
|
|
24
|
+
xmlStream.openNode(this.tag, VmlDrawingXform.DRAWING_ATTRIBUTES);
|
|
25
|
+
// Shape layout - shared by both notes and form controls
|
|
26
|
+
xmlStream.openNode("o:shapelayout", { "v:ext": "edit" });
|
|
27
|
+
xmlStream.leafNode("o:idmap", { "v:ext": "edit", data: 1 });
|
|
28
|
+
xmlStream.closeNode();
|
|
29
|
+
// Shapetype 202 for notes/comments
|
|
30
|
+
if (hasComments) {
|
|
31
|
+
xmlStream.openNode("v:shapetype", {
|
|
32
|
+
id: "_x0000_t202",
|
|
33
|
+
coordsize: "21600,21600",
|
|
34
|
+
"o:spt": 202,
|
|
35
|
+
path: "m,l,21600r21600,l21600,xe"
|
|
36
|
+
});
|
|
37
|
+
xmlStream.leafNode("v:stroke", { joinstyle: "miter" });
|
|
38
|
+
xmlStream.leafNode("v:path", { gradientshapeok: "t", "o:connecttype": "rect" });
|
|
39
|
+
xmlStream.closeNode();
|
|
40
|
+
}
|
|
41
|
+
// Shapetype 201 for form control checkboxes
|
|
42
|
+
if (hasFormControls) {
|
|
43
|
+
xmlStream.openNode("v:shapetype", {
|
|
44
|
+
id: "_x0000_t201",
|
|
45
|
+
coordsize: "21600,21600",
|
|
46
|
+
"o:spt": "201",
|
|
47
|
+
path: "m,l,21600r21600,l21600,xe"
|
|
48
|
+
});
|
|
49
|
+
xmlStream.leafNode("v:stroke", { joinstyle: "miter" });
|
|
50
|
+
xmlStream.leafNode("v:path", {
|
|
51
|
+
shadowok: "f",
|
|
52
|
+
"o:extrusionok": "f",
|
|
53
|
+
strokeok: "f",
|
|
54
|
+
fillok: "f",
|
|
55
|
+
"o:connecttype": "rect"
|
|
56
|
+
});
|
|
57
|
+
xmlStream.leafNode("o:lock", { "v:ext": "edit", shapetype: "t" });
|
|
58
|
+
xmlStream.closeNode();
|
|
59
|
+
}
|
|
60
|
+
// Render comment shapes
|
|
61
|
+
if (hasComments) {
|
|
62
|
+
const comments = renderModel.comments;
|
|
63
|
+
for (let i = 0; i < comments.length; i++) {
|
|
64
|
+
this.map["v:shape"].render(xmlStream, comments[i], i);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Render form control shapes
|
|
68
|
+
if (hasFormControls) {
|
|
69
|
+
for (const control of renderModel.formControls) {
|
|
70
|
+
this._renderCheckboxShape(xmlStream, control);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
xmlStream.closeNode();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Render a checkbox form control shape
|
|
77
|
+
*/
|
|
78
|
+
_renderCheckboxShape(xmlStream, control) {
|
|
79
|
+
const shapeAttrs = {
|
|
80
|
+
id: `_x0000_s${control.shapeId}`,
|
|
81
|
+
type: "#_x0000_t201",
|
|
82
|
+
style: FormCheckbox.getVmlStyle(control),
|
|
83
|
+
"o:insetmode": "auto",
|
|
84
|
+
fillcolor: "buttonFace [67]",
|
|
85
|
+
strokecolor: "windowText [64]",
|
|
86
|
+
"o:preferrelative": "t",
|
|
87
|
+
filled: "f",
|
|
88
|
+
stroked: "f"
|
|
89
|
+
};
|
|
90
|
+
xmlStream.openNode("v:shape", shapeAttrs);
|
|
91
|
+
// Fill element
|
|
92
|
+
xmlStream.leafNode("v:fill", { "o:detectmouseclick": "t" });
|
|
93
|
+
// Lock element
|
|
94
|
+
xmlStream.leafNode("o:lock", { "v:ext": "edit", text: "t" });
|
|
95
|
+
// Textbox for label
|
|
96
|
+
if (control.text) {
|
|
97
|
+
xmlStream.openNode("v:textbox", {
|
|
98
|
+
style: "mso-direction-alt:auto",
|
|
99
|
+
"o:singleclick": "t"
|
|
100
|
+
});
|
|
101
|
+
xmlStream.openNode("div", { style: "text-align:left" });
|
|
102
|
+
xmlStream.openNode("font", { face: "Tahoma", size: "160", color: "auto" });
|
|
103
|
+
xmlStream.writeText(control.text);
|
|
104
|
+
xmlStream.closeNode(); // font
|
|
105
|
+
xmlStream.closeNode(); // div
|
|
106
|
+
xmlStream.closeNode(); // v:textbox
|
|
107
|
+
}
|
|
108
|
+
// ClientData - the core of the checkbox control
|
|
109
|
+
xmlStream.openNode("x:ClientData", { ObjectType: "Checkbox" });
|
|
110
|
+
// Anchor position
|
|
111
|
+
xmlStream.openNode("x:Anchor");
|
|
112
|
+
xmlStream.writeText(FormCheckbox.getVmlAnchor(control));
|
|
113
|
+
xmlStream.closeNode();
|
|
114
|
+
// Print settings
|
|
115
|
+
xmlStream.leafNode("x:PrintObject", undefined, control.print ? "True" : "False");
|
|
116
|
+
xmlStream.leafNode("x:AutoFill", undefined, "False");
|
|
117
|
+
xmlStream.leafNode("x:AutoLine", undefined, "False");
|
|
118
|
+
xmlStream.leafNode("x:TextVAlign", undefined, "Center");
|
|
119
|
+
// Linked cell
|
|
120
|
+
if (control.link) {
|
|
121
|
+
xmlStream.leafNode("x:FmlaLink", undefined, control.link);
|
|
122
|
+
}
|
|
123
|
+
// 3D appearance
|
|
124
|
+
if (control.noThreeD) {
|
|
125
|
+
xmlStream.leafNode("x:NoThreeD");
|
|
126
|
+
}
|
|
127
|
+
// Checked state (0 = unchecked, 1 = checked, 2 = mixed)
|
|
128
|
+
xmlStream.leafNode("x:Checked", undefined, String(FormCheckbox.getVmlCheckedValue(control)));
|
|
129
|
+
xmlStream.closeNode(); // x:ClientData
|
|
130
|
+
xmlStream.closeNode(); // v:shape
|
|
131
|
+
}
|
|
132
|
+
// Parsing - delegate to VmlShapeXform for notes
|
|
133
|
+
parseOpen(node) {
|
|
134
|
+
if (this.parser) {
|
|
135
|
+
this.parser.parseOpen(node);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
switch (node.name) {
|
|
139
|
+
case this.tag:
|
|
140
|
+
this.reset();
|
|
141
|
+
this.model = {
|
|
142
|
+
comments: [],
|
|
143
|
+
formControls: []
|
|
144
|
+
};
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
this.parser = this.map[node.name];
|
|
148
|
+
if (this.parser) {
|
|
149
|
+
this.parser.parseOpen(node);
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
parseText(text) {
|
|
156
|
+
if (this.parser) {
|
|
157
|
+
this.parser.parseText(text);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
parseClose(name) {
|
|
161
|
+
if (this.parser) {
|
|
162
|
+
if (!this.parser.parseClose(name)) {
|
|
163
|
+
this.model.comments.push(this.parser.model);
|
|
164
|
+
this.parser = undefined;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
switch (name) {
|
|
169
|
+
case this.tag:
|
|
170
|
+
return false;
|
|
171
|
+
default:
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
VmlDrawingXform.DRAWING_ATTRIBUTES = {
|
|
177
|
+
"xmlns:v": "urn:schemas-microsoft-com:vml",
|
|
178
|
+
"xmlns:o": "urn:schemas-microsoft-com:office:office",
|
|
179
|
+
"xmlns:x": "urn:schemas-microsoft-com:office:excel"
|
|
180
|
+
};
|
|
181
|
+
export { VmlDrawingXform };
|
|
@@ -189,6 +189,11 @@ class CellXform extends BaseXform {
|
|
|
189
189
|
xmlStream.addAttribute("t", "b");
|
|
190
190
|
xmlStream.leafNode("v", null, model.value ? "1" : "0");
|
|
191
191
|
break;
|
|
192
|
+
case Enums.ValueType.Checkbox:
|
|
193
|
+
// Checkboxes are stored as boolean values
|
|
194
|
+
xmlStream.addAttribute("t", "b");
|
|
195
|
+
xmlStream.leafNode("v", null, model.value ? "1" : "0");
|
|
196
|
+
break;
|
|
192
197
|
case Enums.ValueType.Error:
|
|
193
198
|
xmlStream.addAttribute("t", "e");
|
|
194
199
|
xmlStream.leafNode("v", null, model.value.error);
|
|
@@ -26,7 +26,7 @@ import { ColBreaksXform } from "./col-breaks-xform.js";
|
|
|
26
26
|
import { HeaderFooterXform } from "./header-footer-xform.js";
|
|
27
27
|
import { ConditionalFormattingsXform } from "./cf/conditional-formattings-xform.js";
|
|
28
28
|
import { ExtLstXform } from "./ext-lst-xform.js";
|
|
29
|
-
import { commentsRelTargetFromWorksheet, drawingRelTargetFromWorksheet, mediaRelTargetFromRels, pivotTableRelTargetFromWorksheet, tableRelTargetFromWorksheet, vmlDrawingRelTargetFromWorksheet } from "../../../utils/ooxml-paths.js";
|
|
29
|
+
import { commentsRelTargetFromWorksheet, ctrlPropRelTargetFromWorksheet, drawingRelTargetFromWorksheet, mediaRelTargetFromRels, pivotTableRelTargetFromWorksheet, tableRelTargetFromWorksheet, vmlDrawingRelTargetFromWorksheet } from "../../../utils/ooxml-paths.js";
|
|
30
30
|
const mergeRule = (rule, extRule) => {
|
|
31
31
|
Object.keys(extRule).forEach(key => {
|
|
32
32
|
const value = rule[key];
|
|
@@ -275,6 +275,29 @@ class WorkSheetXform extends BaseXform {
|
|
|
275
275
|
Target: pivotTableRelTargetFromWorksheet(pivotTable.tableNumber)
|
|
276
276
|
});
|
|
277
277
|
});
|
|
278
|
+
// prepare form controls (legacy checkboxes)
|
|
279
|
+
// Form controls share the VML file with comments, but need separate ctrlProp relationships
|
|
280
|
+
if (model.formControls && model.formControls.length > 0) {
|
|
281
|
+
// If no comments, we need to add the VML drawing relationship for form controls
|
|
282
|
+
if (model.comments.length === 0) {
|
|
283
|
+
rels.push({
|
|
284
|
+
Id: nextRid(rels),
|
|
285
|
+
Type: RelType.VmlDrawing,
|
|
286
|
+
Target: vmlDrawingRelTargetFromWorksheet(model.id)
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// Add ctrlProp relationships for each form control
|
|
290
|
+
for (const control of model.formControls) {
|
|
291
|
+
const globalCtrlPropId = options.formControlRefs.length + 1;
|
|
292
|
+
control.ctrlPropId = globalCtrlPropId;
|
|
293
|
+
rels.push({
|
|
294
|
+
Id: nextRid(rels),
|
|
295
|
+
Type: RelType.CtrlProp,
|
|
296
|
+
Target: ctrlPropRelTargetFromWorksheet(globalCtrlPropId)
|
|
297
|
+
});
|
|
298
|
+
options.formControlRefs.push(globalCtrlPropId);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
278
301
|
// prepare ext items
|
|
279
302
|
this.map.extLst.prepare(model, options);
|
|
280
303
|
}
|
|
@@ -52,6 +52,17 @@ class StyleXform extends BaseXform {
|
|
|
52
52
|
if (model.protection) {
|
|
53
53
|
this.map.protection.render(xmlStream, model.protection);
|
|
54
54
|
}
|
|
55
|
+
// Add checkbox extLst if needed
|
|
56
|
+
if (model.checkbox && model.xfComplementIndex !== undefined) {
|
|
57
|
+
xmlStream.openNode("extLst");
|
|
58
|
+
xmlStream.openNode("ext", {
|
|
59
|
+
"xmlns:xfpb": "http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag",
|
|
60
|
+
uri: "{C7286773-470A-42A8-94C5-96B5CB345126}"
|
|
61
|
+
});
|
|
62
|
+
xmlStream.leafNode("xfpb:xfComplement", { i: model.xfComplementIndex });
|
|
63
|
+
xmlStream.closeNode();
|
|
64
|
+
xmlStream.closeNode();
|
|
65
|
+
}
|
|
55
66
|
xmlStream.closeNode();
|
|
56
67
|
}
|
|
57
68
|
parseOpen(node) {
|
|
@@ -82,6 +82,7 @@ class StylesXform extends BaseXform {
|
|
|
82
82
|
this._addFill({ type: "pattern", pattern: "none" });
|
|
83
83
|
this._addFill({ type: "pattern", pattern: "gray125" });
|
|
84
84
|
this.weakMap = new WeakMap();
|
|
85
|
+
this._hasCheckboxes = false;
|
|
85
86
|
}
|
|
86
87
|
render(xmlStream, model) {
|
|
87
88
|
const renderModel = model || this.model;
|
|
@@ -222,12 +223,15 @@ class StylesXform extends BaseXform {
|
|
|
222
223
|
// default (zero) font
|
|
223
224
|
this._addFont({ size: 11, color: { theme: 1 }, name: "Calibri", family: 2, scheme: "minor" });
|
|
224
225
|
}
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
const type = cellType || Enums.ValueType.Number;
|
|
227
|
+
// If we have seen this style object before, assume it has the same styleId.
|
|
228
|
+
// Do not cache by object identity for checkbox cells because the styleId must
|
|
229
|
+
// include checkbox-specific extLst, and the same style object may be reused
|
|
230
|
+
// for non-checkbox cells.
|
|
231
|
+
if (type !== Enums.ValueType.Checkbox && this.weakMap && this.weakMap.has(model)) {
|
|
227
232
|
return this.weakMap.get(model);
|
|
228
233
|
}
|
|
229
234
|
const style = {};
|
|
230
|
-
const type = cellType || Enums.ValueType.Number;
|
|
231
235
|
if (model.numFmt) {
|
|
232
236
|
style.numFmtId = this._addNumFmtStr(model.numFmt);
|
|
233
237
|
}
|
|
@@ -258,8 +262,17 @@ class StylesXform extends BaseXform {
|
|
|
258
262
|
if (model.protection) {
|
|
259
263
|
style.protection = model.protection;
|
|
260
264
|
}
|
|
265
|
+
if (type === Enums.ValueType.Checkbox) {
|
|
266
|
+
// Checkbox rendering relies on style extensions (extLst) and workbook-level parts.
|
|
267
|
+
// Force applyAlignment="1" (without emitting an <alignment/> node) by providing
|
|
268
|
+
// an empty alignment object when none is specified.
|
|
269
|
+
this._hasCheckboxes = true;
|
|
270
|
+
style.alignment = style.alignment || {};
|
|
271
|
+
style.checkbox = true;
|
|
272
|
+
style.xfComplementIndex = 0;
|
|
273
|
+
}
|
|
261
274
|
const styleId = this._addStyle(style);
|
|
262
|
-
if (this.weakMap) {
|
|
275
|
+
if (type !== Enums.ValueType.Checkbox && this.weakMap) {
|
|
263
276
|
this.weakMap.set(model, styleId);
|
|
264
277
|
}
|
|
265
278
|
return styleId;
|
|
@@ -322,6 +335,10 @@ class StylesXform extends BaseXform {
|
|
|
322
335
|
getDxfStyle(id) {
|
|
323
336
|
return this.model.dxfs[id];
|
|
324
337
|
}
|
|
338
|
+
// Check if workbook uses checkbox feature
|
|
339
|
+
get hasCheckboxes() {
|
|
340
|
+
return !!this._hasCheckboxes;
|
|
341
|
+
}
|
|
325
342
|
// =========================================================================
|
|
326
343
|
// Private Interface
|
|
327
344
|
_addStyle(style) {
|
|
@@ -457,12 +474,19 @@ class StylesXformMock extends StylesXform {
|
|
|
457
474
|
// the styleId is returned. Note: cellType is used when numFmt not defined
|
|
458
475
|
addStyleModel(model, cellType) {
|
|
459
476
|
switch (cellType) {
|
|
477
|
+
case Enums.ValueType.Checkbox:
|
|
478
|
+
// Checkbox rendering relies on style extensions (extLst) and workbook-level parts.
|
|
479
|
+
// The mock style manager intentionally does not build those structures.
|
|
480
|
+
throw new Error("Checkbox cells require styles to be enabled (useStyles: true)");
|
|
460
481
|
case Enums.ValueType.Date:
|
|
461
482
|
return this.dateStyleId;
|
|
462
483
|
default:
|
|
463
484
|
return 0;
|
|
464
485
|
}
|
|
465
486
|
}
|
|
487
|
+
get hasCheckboxes() {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
466
490
|
get dateStyleId() {
|
|
467
491
|
if (!this._dateStyleId) {
|
|
468
492
|
const dateStyle = {
|
|
@@ -17,13 +17,15 @@ import { ContentTypesXform } from "./xform/core/content-types-xform.js";
|
|
|
17
17
|
import { AppXform } from "./xform/core/app-xform.js";
|
|
18
18
|
import { WorkbookXform } from "./xform/book/workbook-xform.js";
|
|
19
19
|
import { WorkSheetXform } from "./xform/sheet/worksheet-xform.js";
|
|
20
|
+
import { FeaturePropertyBagXform } from "./xform/core/feature-property-bag-xform.js";
|
|
20
21
|
import { DrawingXform } from "./xform/drawing/drawing-xform.js";
|
|
21
22
|
import { TableXform } from "./xform/table/table-xform.js";
|
|
22
23
|
import { PivotCacheRecordsXform } from "./xform/pivot-table/pivot-cache-records-xform.js";
|
|
23
24
|
import { PivotCacheDefinitionXform } from "./xform/pivot-table/pivot-cache-definition-xform.js";
|
|
24
25
|
import { PivotTableXform } from "./xform/pivot-table/pivot-table-xform.js";
|
|
25
26
|
import { CommentsXform } from "./xform/comment/comments-xform.js";
|
|
26
|
-
import {
|
|
27
|
+
import { VmlDrawingXform } from "./xform/drawing/vml-drawing-xform.js";
|
|
28
|
+
import { CtrlPropXform } from "./xform/drawing/ctrl-prop-xform.js";
|
|
27
29
|
import { theme1Xml } from "./xml/theme1.js";
|
|
28
30
|
import { RelType } from "./rel-type.js";
|
|
29
31
|
import { StreamBuf } from "../utils/stream-buf.js";
|
|
@@ -31,7 +33,7 @@ import { bufferToString, base64ToUint8Array } from "../../../utils/utils.js";
|
|
|
31
33
|
import { StreamingZip, ZipDeflateFile } from "../../archive/streaming-zip.js";
|
|
32
34
|
import { ZipParser } from "../../archive/index.js";
|
|
33
35
|
import { PassThrough, concatUint8Arrays } from "../../stream/index.js";
|
|
34
|
-
import { commentsPath, commentsRelTargetFromWorksheetName, drawingPath, drawingRelsPath, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, worksheetPath, worksheetRelsPath } from "../utils/ooxml-paths.js";
|
|
36
|
+
import { commentsPath, commentsRelTargetFromWorksheetName, ctrlPropPath, drawingPath, drawingRelsPath, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, worksheetPath, worksheetRelsPath } from "../utils/ooxml-paths.js";
|
|
35
37
|
class StreamingZipWriterAdapter {
|
|
36
38
|
constructor(options) {
|
|
37
39
|
this.events = new Map();
|
|
@@ -203,6 +205,7 @@ class XLSX {
|
|
|
203
205
|
this.addTables(zip, model);
|
|
204
206
|
this.addPivotTables(zip, model);
|
|
205
207
|
await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
|
|
208
|
+
await this.addFeaturePropertyBag(zip, model);
|
|
206
209
|
await this.addMedia(zip, model);
|
|
207
210
|
await Promise.all([this.addApp(zip, model), this.addCore(zip, model)]);
|
|
208
211
|
await this.addWorkbook(zip, model);
|
|
@@ -700,7 +703,7 @@ class XLSX {
|
|
|
700
703
|
model.drawingRels[name] = relationships;
|
|
701
704
|
}
|
|
702
705
|
async _processVmlDrawingEntry(entry, model, name) {
|
|
703
|
-
const xform = new
|
|
706
|
+
const xform = new VmlDrawingXform();
|
|
704
707
|
const vmlDrawing = await xform.parseStream(entry);
|
|
705
708
|
model.vmlDrawings[vmlDrawingRelTargetFromWorksheetName(name)] = vmlDrawing;
|
|
706
709
|
}
|
|
@@ -971,6 +974,14 @@ class XLSX {
|
|
|
971
974
|
Target: OOXML_REL_TARGETS.workbookSharedStrings
|
|
972
975
|
});
|
|
973
976
|
}
|
|
977
|
+
// Add FeaturePropertyBag relationship if checkboxes are used
|
|
978
|
+
if (model.hasCheckboxes) {
|
|
979
|
+
relationships.push({
|
|
980
|
+
Id: `rId${count++}`,
|
|
981
|
+
Type: XLSX.RelType.FeaturePropertyBag,
|
|
982
|
+
Target: OOXML_REL_TARGETS.workbookFeaturePropertyBag
|
|
983
|
+
});
|
|
984
|
+
}
|
|
974
985
|
(model.pivotTables || []).forEach((pivotTable) => {
|
|
975
986
|
pivotTable.rId = `rId${count++}`;
|
|
976
987
|
relationships.push({
|
|
@@ -992,6 +1003,13 @@ class XLSX {
|
|
|
992
1003
|
const xml = xform.toXml(relationships);
|
|
993
1004
|
zip.append(xml, { name: OOXML_PATHS.xlWorkbookRels });
|
|
994
1005
|
}
|
|
1006
|
+
async addFeaturePropertyBag(zip, model) {
|
|
1007
|
+
if (!model.hasCheckboxes) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const xform = new FeaturePropertyBagXform();
|
|
1011
|
+
zip.append(xform.toXml({}), { name: OOXML_PATHS.xlFeaturePropertyBag });
|
|
1012
|
+
}
|
|
995
1013
|
async addSharedStrings(zip, model) {
|
|
996
1014
|
if (model.sharedStrings && model.sharedStrings.count) {
|
|
997
1015
|
zip.append(model.sharedStrings.xml, { name: OOXML_PATHS.xlSharedStrings });
|
|
@@ -1011,7 +1029,8 @@ class XLSX {
|
|
|
1011
1029
|
const worksheetXform = new WorkSheetXform();
|
|
1012
1030
|
const relationshipsXform = new RelationshipsXform();
|
|
1013
1031
|
const commentsXform = new CommentsXform();
|
|
1014
|
-
const
|
|
1032
|
+
const vmlDrawingXform = new VmlDrawingXform();
|
|
1033
|
+
const ctrlPropXform = new CtrlPropXform();
|
|
1015
1034
|
model.worksheets.forEach((worksheet, index) => {
|
|
1016
1035
|
const fileIndex = worksheet.fileIndex || index + 1;
|
|
1017
1036
|
let xmlStream = new XmlStream();
|
|
@@ -1022,14 +1041,30 @@ class XLSX {
|
|
|
1022
1041
|
relationshipsXform.render(xmlStream, worksheet.rels);
|
|
1023
1042
|
zip.append(xmlStream.xml, { name: worksheetRelsPath(fileIndex) });
|
|
1024
1043
|
}
|
|
1044
|
+
// Generate comments XML (separate from VML)
|
|
1025
1045
|
if (worksheet.comments.length > 0) {
|
|
1026
1046
|
xmlStream = new XmlStream();
|
|
1027
1047
|
commentsXform.render(xmlStream, worksheet);
|
|
1028
1048
|
zip.append(xmlStream.xml, { name: commentsPath(fileIndex) });
|
|
1049
|
+
}
|
|
1050
|
+
// Generate unified VML drawing (contains both notes and form controls)
|
|
1051
|
+
const hasComments = worksheet.comments.length > 0;
|
|
1052
|
+
const hasFormControls = worksheet.formControls && worksheet.formControls.length > 0;
|
|
1053
|
+
if (hasComments || hasFormControls) {
|
|
1029
1054
|
xmlStream = new XmlStream();
|
|
1030
|
-
|
|
1055
|
+
vmlDrawingXform.render(xmlStream, {
|
|
1056
|
+
comments: hasComments ? worksheet.comments : [],
|
|
1057
|
+
formControls: hasFormControls ? worksheet.formControls : []
|
|
1058
|
+
});
|
|
1031
1059
|
zip.append(xmlStream.xml, { name: vmlDrawingPath(fileIndex) });
|
|
1032
1060
|
}
|
|
1061
|
+
// Generate ctrlProp files for form controls
|
|
1062
|
+
if (hasFormControls) {
|
|
1063
|
+
worksheet.formControls.forEach((control) => {
|
|
1064
|
+
const xml = ctrlPropXform.toXml(control);
|
|
1065
|
+
zip.append(xml, { name: ctrlPropPath(control.ctrlPropId) });
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1033
1068
|
});
|
|
1034
1069
|
}
|
|
1035
1070
|
addDrawings(zip, model) {
|
|
@@ -1135,6 +1170,7 @@ class XLSX {
|
|
|
1135
1170
|
};
|
|
1136
1171
|
worksheetOptions.drawings = model.drawings = [];
|
|
1137
1172
|
worksheetOptions.commentRefs = model.commentRefs = [];
|
|
1173
|
+
worksheetOptions.formControlRefs = model.formControlRefs = [];
|
|
1138
1174
|
let tableCount = 0;
|
|
1139
1175
|
model.tables = [];
|
|
1140
1176
|
model.worksheets.forEach((worksheet) => {
|
|
@@ -1146,6 +1182,8 @@ class XLSX {
|
|
|
1146
1182
|
});
|
|
1147
1183
|
worksheetXform.prepare(worksheet, worksheetOptions);
|
|
1148
1184
|
});
|
|
1185
|
+
// ContentTypesXform expects this flag
|
|
1186
|
+
model.hasCheckboxes = model.styles.hasCheckboxes;
|
|
1149
1187
|
}
|
|
1150
1188
|
}
|
|
1151
1189
|
XLSX.RelType = RelType;
|