@cj-tech-master/excelts 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/browser/index.browser.d.ts +2 -0
  2. package/dist/browser/index.browser.js +1 -0
  3. package/dist/browser/index.d.ts +2 -0
  4. package/dist/browser/index.js +1 -0
  5. package/dist/browser/modules/excel/form-control.d.ts +157 -0
  6. package/dist/browser/modules/excel/form-control.js +267 -0
  7. package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +2 -0
  8. package/dist/browser/modules/excel/utils/ooxml-paths.js +8 -0
  9. package/dist/browser/modules/excel/worksheet.d.ts +32 -0
  10. package/dist/browser/modules/excel/worksheet.js +44 -1
  11. package/dist/browser/modules/excel/xlsx/rel-type.d.ts +1 -0
  12. package/dist/browser/modules/excel/xlsx/rel-type.js +2 -1
  13. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +17 -1
  14. package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +22 -0
  15. package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
  16. package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -0
  17. package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
  18. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
  19. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +1 -0
  20. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +24 -5
  21. package/dist/cjs/index.js +3 -1
  22. package/dist/cjs/modules/excel/form-control.js +270 -0
  23. package/dist/cjs/modules/excel/utils/ooxml-paths.js +10 -0
  24. package/dist/cjs/modules/excel/worksheet.js +44 -1
  25. package/dist/cjs/modules/excel/xlsx/rel-type.js +2 -1
  26. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +16 -0
  27. package/dist/cjs/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +55 -0
  28. package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +184 -0
  29. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +23 -0
  30. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +23 -4
  31. package/dist/esm/index.browser.js +1 -0
  32. package/dist/esm/index.js +1 -0
  33. package/dist/esm/modules/excel/form-control.js +267 -0
  34. package/dist/esm/modules/excel/utils/ooxml-paths.js +8 -0
  35. package/dist/esm/modules/excel/worksheet.js +44 -1
  36. package/dist/esm/modules/excel/xlsx/rel-type.js +2 -1
  37. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +17 -1
  38. package/dist/esm/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
  39. package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
  40. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
  41. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +24 -5
  42. package/dist/iife/excelts.iife.js +466 -30
  43. package/dist/iife/excelts.iife.js.map +1 -1
  44. package/dist/iife/excelts.iife.min.js +30 -30
  45. package/dist/types/index.browser.d.ts +2 -0
  46. package/dist/types/index.d.ts +2 -0
  47. package/dist/types/modules/excel/form-control.d.ts +157 -0
  48. package/dist/types/modules/excel/utils/ooxml-paths.d.ts +2 -0
  49. package/dist/types/modules/excel/worksheet.d.ts +32 -0
  50. package/dist/types/modules/excel/xlsx/rel-type.d.ts +1 -0
  51. package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +22 -0
  52. package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -0
  53. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +1 -0
  54. package/package.json +2 -2
@@ -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 };
@@ -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
  }
@@ -24,7 +24,8 @@ import { PivotCacheRecordsXform } from "./xform/pivot-table/pivot-cache-records-
24
24
  import { PivotCacheDefinitionXform } from "./xform/pivot-table/pivot-cache-definition-xform.js";
25
25
  import { PivotTableXform } from "./xform/pivot-table/pivot-table-xform.js";
26
26
  import { CommentsXform } from "./xform/comment/comments-xform.js";
27
- import { VmlNotesXform } from "./xform/comment/vml-notes-xform.js";
27
+ import { VmlDrawingXform } from "./xform/drawing/vml-drawing-xform.js";
28
+ import { CtrlPropXform } from "./xform/drawing/ctrl-prop-xform.js";
28
29
  import { theme1Xml } from "./xml/theme1.js";
29
30
  import { RelType } from "./rel-type.js";
30
31
  import { StreamBuf } from "../utils/stream-buf.js";
@@ -32,7 +33,7 @@ import { bufferToString, base64ToUint8Array } from "../../../utils/utils.js";
32
33
  import { StreamingZip, ZipDeflateFile } from "../../archive/streaming-zip.js";
33
34
  import { ZipParser } from "../../archive/index.js";
34
35
  import { PassThrough, concatUint8Arrays } from "../../stream/index.js";
35
- 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";
36
37
  class StreamingZipWriterAdapter {
37
38
  constructor(options) {
38
39
  this.events = new Map();
@@ -702,7 +703,7 @@ class XLSX {
702
703
  model.drawingRels[name] = relationships;
703
704
  }
704
705
  async _processVmlDrawingEntry(entry, model, name) {
705
- const xform = new VmlNotesXform();
706
+ const xform = new VmlDrawingXform();
706
707
  const vmlDrawing = await xform.parseStream(entry);
707
708
  model.vmlDrawings[vmlDrawingRelTargetFromWorksheetName(name)] = vmlDrawing;
708
709
  }
@@ -1028,7 +1029,8 @@ class XLSX {
1028
1029
  const worksheetXform = new WorkSheetXform();
1029
1030
  const relationshipsXform = new RelationshipsXform();
1030
1031
  const commentsXform = new CommentsXform();
1031
- const vmlNotesXform = new VmlNotesXform();
1032
+ const vmlDrawingXform = new VmlDrawingXform();
1033
+ const ctrlPropXform = new CtrlPropXform();
1032
1034
  model.worksheets.forEach((worksheet, index) => {
1033
1035
  const fileIndex = worksheet.fileIndex || index + 1;
1034
1036
  let xmlStream = new XmlStream();
@@ -1039,14 +1041,30 @@ class XLSX {
1039
1041
  relationshipsXform.render(xmlStream, worksheet.rels);
1040
1042
  zip.append(xmlStream.xml, { name: worksheetRelsPath(fileIndex) });
1041
1043
  }
1044
+ // Generate comments XML (separate from VML)
1042
1045
  if (worksheet.comments.length > 0) {
1043
1046
  xmlStream = new XmlStream();
1044
1047
  commentsXform.render(xmlStream, worksheet);
1045
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) {
1046
1054
  xmlStream = new XmlStream();
1047
- vmlNotesXform.render(xmlStream, worksheet);
1055
+ vmlDrawingXform.render(xmlStream, {
1056
+ comments: hasComments ? worksheet.comments : [],
1057
+ formControls: hasFormControls ? worksheet.formControls : []
1058
+ });
1048
1059
  zip.append(xmlStream.xml, { name: vmlDrawingPath(fileIndex) });
1049
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
+ }
1050
1068
  });
1051
1069
  }
1052
1070
  addDrawings(zip, model) {
@@ -1152,6 +1170,7 @@ class XLSX {
1152
1170
  };
1153
1171
  worksheetOptions.drawings = model.drawings = [];
1154
1172
  worksheetOptions.commentRefs = model.commentRefs = [];
1173
+ worksheetOptions.formControlRefs = model.formControlRefs = [];
1155
1174
  let tableCount = 0;
1156
1175
  model.tables = [];
1157
1176
  model.worksheets.forEach((worksheet) => {