@cj-tech-master/excelts 4.2.1-canary.20260112134913.a3cecdd → 4.2.2-canary.20260115044841.88820eb
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/modules/excel/utils/parse-sax.d.ts +3 -0
- package/dist/browser/modules/excel/utils/parse-sax.js +32 -13
- package/dist/browser/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/browser/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/browser/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/browser/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/browser/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +8 -4
- package/dist/cjs/modules/excel/utils/parse-sax.js +32 -13
- package/dist/cjs/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/cjs/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/cjs/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/cjs/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/cjs/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +8 -4
- package/dist/esm/modules/excel/utils/parse-sax.js +32 -13
- package/dist/esm/modules/excel/xlsx/xform/core/app-xform.js +3 -3
- package/dist/esm/modules/excel/xlsx/xform/core/core-xform.js +56 -68
- package/dist/esm/modules/excel/xlsx/xform/list-xform.js +8 -10
- package/dist/esm/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
- package/dist/esm/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +8 -4
- package/dist/iife/excelts.iife.js +100 -100
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +24 -24
- package/dist/types/modules/excel/utils/parse-sax.d.ts +3 -0
- package/package.json +1 -1
|
@@ -54,6 +54,7 @@ export declare class SaxesParser {
|
|
|
54
54
|
private positionAtNewLine;
|
|
55
55
|
private chunkPosition;
|
|
56
56
|
ENTITIES: Record<string, string>;
|
|
57
|
+
private nsPrefix;
|
|
57
58
|
private textHandler?;
|
|
58
59
|
private openTagHandler?;
|
|
59
60
|
private closeTagHandler?;
|
|
@@ -62,6 +63,7 @@ export declare class SaxesParser {
|
|
|
62
63
|
get closed(): boolean;
|
|
63
64
|
get position(): number;
|
|
64
65
|
private _init;
|
|
66
|
+
private stripNsPrefix;
|
|
65
67
|
on(name: "text", handler: TextHandler): void;
|
|
66
68
|
on(name: "opentag", handler: OpenTagHandler): void;
|
|
67
69
|
on(name: "closetag", handler: CloseTagHandler): void;
|
|
@@ -106,6 +108,7 @@ export declare class SaxesParser {
|
|
|
106
108
|
private skipSpaces;
|
|
107
109
|
private openTag;
|
|
108
110
|
private openSelfClosingTag;
|
|
111
|
+
private processAttributes;
|
|
109
112
|
private closeTag;
|
|
110
113
|
private end;
|
|
111
114
|
}
|
|
@@ -142,6 +142,12 @@ const XML_ENTITIES = {
|
|
|
142
142
|
quot: '"',
|
|
143
143
|
apos: "'"
|
|
144
144
|
};
|
|
145
|
+
// HAN CELL namespace prefix normalization
|
|
146
|
+
// HAN CELL uses non-standard namespace prefixes (ep:, cp:, dc:, etc.)
|
|
147
|
+
// The x: prefix for spreadsheetml is detected dynamically from xmlns declarations
|
|
148
|
+
// See: https://github.com/exceljs/exceljs/issues/3014
|
|
149
|
+
const HAN_CELL_PREFIXES = /^(ep|cp|dc|dcterms|dcmitype|vt):/;
|
|
150
|
+
const SPREADSHEETML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
|
145
151
|
// ============================================================================
|
|
146
152
|
// Parser States
|
|
147
153
|
// ============================================================================
|
|
@@ -202,6 +208,8 @@ export class SaxesParser {
|
|
|
202
208
|
this.chunkPosition = 0;
|
|
203
209
|
// Entity storage
|
|
204
210
|
this.ENTITIES = { ...XML_ENTITIES };
|
|
211
|
+
// HAN CELL compatibility: spreadsheetml namespace prefix (e.g., "x")
|
|
212
|
+
this.nsPrefix = null;
|
|
205
213
|
this.trackPosition = opt?.position !== false;
|
|
206
214
|
this.fileName = opt?.fileName;
|
|
207
215
|
this.fragment = opt?.fragment ?? false;
|
|
@@ -236,6 +244,14 @@ export class SaxesParser {
|
|
|
236
244
|
this.chunk = "";
|
|
237
245
|
this.i = 0;
|
|
238
246
|
this.prevI = 0;
|
|
247
|
+
this.nsPrefix = null;
|
|
248
|
+
}
|
|
249
|
+
// Strip HAN CELL namespace prefixes from element names
|
|
250
|
+
stripNsPrefix(name) {
|
|
251
|
+
const n = name.replace(HAN_CELL_PREFIXES, "");
|
|
252
|
+
return this.nsPrefix && n.startsWith(this.nsPrefix + ":")
|
|
253
|
+
? n.slice(this.nsPrefix.length + 1)
|
|
254
|
+
: n;
|
|
239
255
|
}
|
|
240
256
|
on(name, handler) {
|
|
241
257
|
switch (name) {
|
|
@@ -651,9 +667,8 @@ export class SaxesParser {
|
|
|
651
667
|
this.name += charFromCode(c);
|
|
652
668
|
return;
|
|
653
669
|
}
|
|
654
|
-
// Tag name complete
|
|
655
670
|
this.tag = {
|
|
656
|
-
name: this.name,
|
|
671
|
+
name: this.stripNsPrefix(this.name),
|
|
657
672
|
attributes: Object.create(null),
|
|
658
673
|
isSelfClosing: false
|
|
659
674
|
};
|
|
@@ -1092,11 +1107,7 @@ export class SaxesParser {
|
|
|
1092
1107
|
openTag() {
|
|
1093
1108
|
const tag = this.tag;
|
|
1094
1109
|
tag.isSelfClosing = false;
|
|
1095
|
-
|
|
1096
|
-
for (const { name, value } of this.attribList) {
|
|
1097
|
-
tag.attributes[name] = value;
|
|
1098
|
-
}
|
|
1099
|
-
this.attribList = [];
|
|
1110
|
+
this.processAttributes(tag);
|
|
1100
1111
|
this.openTagHandler?.(tag);
|
|
1101
1112
|
this.tags.push(tag);
|
|
1102
1113
|
this.name = "";
|
|
@@ -1105,11 +1116,7 @@ export class SaxesParser {
|
|
|
1105
1116
|
openSelfClosingTag() {
|
|
1106
1117
|
const tag = this.tag;
|
|
1107
1118
|
tag.isSelfClosing = true;
|
|
1108
|
-
|
|
1109
|
-
for (const { name, value } of this.attribList) {
|
|
1110
|
-
tag.attributes[name] = value;
|
|
1111
|
-
}
|
|
1112
|
-
this.attribList = [];
|
|
1119
|
+
this.processAttributes(tag);
|
|
1113
1120
|
this.openTagHandler?.(tag);
|
|
1114
1121
|
this.closeTagHandler?.(tag);
|
|
1115
1122
|
if (this.tags.length === 0) {
|
|
@@ -1118,8 +1125,20 @@ export class SaxesParser {
|
|
|
1118
1125
|
this.name = "";
|
|
1119
1126
|
this.state = S_TEXT;
|
|
1120
1127
|
}
|
|
1128
|
+
// Process attributes and detect spreadsheetml namespace prefix
|
|
1129
|
+
processAttributes(tag) {
|
|
1130
|
+
for (const { name, value } of this.attribList) {
|
|
1131
|
+
tag.attributes[name] = value;
|
|
1132
|
+
if (name.startsWith("xmlns:") && value === SPREADSHEETML_NS) {
|
|
1133
|
+
this.nsPrefix = name.slice(6);
|
|
1134
|
+
tag.name = this.stripNsPrefix(tag.name);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
this.attribList = [];
|
|
1138
|
+
}
|
|
1121
1139
|
closeTag() {
|
|
1122
|
-
const { tags
|
|
1140
|
+
const { tags } = this;
|
|
1141
|
+
const name = this.stripNsPrefix(this.name);
|
|
1123
1142
|
this.state = S_TEXT;
|
|
1124
1143
|
this.name = "";
|
|
1125
1144
|
if (name === "") {
|
|
@@ -10,7 +10,7 @@ class AppXform extends BaseXform {
|
|
|
10
10
|
Company: new StringXform({ tag: "Company" }),
|
|
11
11
|
Manager: new StringXform({ tag: "Manager" }),
|
|
12
12
|
HeadingPairs: new AppHeadingPairsXform(),
|
|
13
|
-
|
|
13
|
+
TitlesOfParts: new AppTitlesOfPartsXform()
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
render(xmlStream, model) {
|
|
@@ -20,7 +20,7 @@ class AppXform extends BaseXform {
|
|
|
20
20
|
xmlStream.leafNode("DocSecurity", undefined, "0");
|
|
21
21
|
xmlStream.leafNode("ScaleCrop", undefined, "false");
|
|
22
22
|
this.map.HeadingPairs.render(xmlStream, model.worksheets);
|
|
23
|
-
this.map.
|
|
23
|
+
this.map.TitlesOfParts.render(xmlStream, model.worksheets);
|
|
24
24
|
this.map.Company.render(xmlStream, model.company || "");
|
|
25
25
|
this.map.Manager.render(xmlStream, model.manager);
|
|
26
26
|
xmlStream.leafNode("LinksUpToDate", undefined, "false");
|
|
@@ -62,7 +62,7 @@ class AppXform extends BaseXform {
|
|
|
62
62
|
switch (name) {
|
|
63
63
|
case "Properties":
|
|
64
64
|
this.model = {
|
|
65
|
-
worksheets: this.map.
|
|
65
|
+
worksheets: this.map.TitlesOfParts.model,
|
|
66
66
|
company: this.map.Company.model,
|
|
67
67
|
manager: this.map.Manager.model
|
|
68
68
|
};
|
|
@@ -3,31 +3,50 @@ import { BaseXform } from "../base-xform.js";
|
|
|
3
3
|
import { DateXform } from "../simple/date-xform.js";
|
|
4
4
|
import { StringXform } from "../simple/string-xform.js";
|
|
5
5
|
import { IntegerXform } from "../simple/integer-xform.js";
|
|
6
|
+
// Rendering uses namespace prefixes, parsing uses unqualified names (SAX strips prefixes)
|
|
7
|
+
const PROPS = {
|
|
8
|
+
creator: "dc:creator",
|
|
9
|
+
title: "dc:title",
|
|
10
|
+
subject: "dc:subject",
|
|
11
|
+
description: "dc:description",
|
|
12
|
+
identifier: "dc:identifier",
|
|
13
|
+
language: "dc:language",
|
|
14
|
+
keywords: "cp:keywords",
|
|
15
|
+
category: "cp:category",
|
|
16
|
+
lastModifiedBy: "cp:lastModifiedBy",
|
|
17
|
+
lastPrinted: "cp:lastPrinted",
|
|
18
|
+
revision: "cp:revision",
|
|
19
|
+
version: "cp:version",
|
|
20
|
+
contentStatus: "cp:contentStatus",
|
|
21
|
+
contentType: "cp:contentType",
|
|
22
|
+
created: "dcterms:created",
|
|
23
|
+
modified: "dcterms:modified"
|
|
24
|
+
};
|
|
6
25
|
class CoreXform extends BaseXform {
|
|
7
26
|
constructor() {
|
|
8
27
|
super();
|
|
9
28
|
this.map = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
tag:
|
|
29
|
+
creator: new StringXform({ tag: PROPS.creator }),
|
|
30
|
+
title: new StringXform({ tag: PROPS.title }),
|
|
31
|
+
subject: new StringXform({ tag: PROPS.subject }),
|
|
32
|
+
description: new StringXform({ tag: PROPS.description }),
|
|
33
|
+
identifier: new StringXform({ tag: PROPS.identifier }),
|
|
34
|
+
language: new StringXform({ tag: PROPS.language }),
|
|
35
|
+
keywords: new StringXform({ tag: PROPS.keywords }),
|
|
36
|
+
category: new StringXform({ tag: PROPS.category }),
|
|
37
|
+
lastModifiedBy: new StringXform({ tag: PROPS.lastModifiedBy }),
|
|
38
|
+
lastPrinted: new DateXform({ tag: PROPS.lastPrinted, format: CoreXform.DateFormat }),
|
|
39
|
+
revision: new IntegerXform({ tag: PROPS.revision }),
|
|
40
|
+
version: new StringXform({ tag: PROPS.version }),
|
|
41
|
+
contentStatus: new StringXform({ tag: PROPS.contentStatus }),
|
|
42
|
+
contentType: new StringXform({ tag: PROPS.contentType }),
|
|
43
|
+
created: new DateXform({
|
|
44
|
+
tag: PROPS.created,
|
|
26
45
|
attrs: CoreXform.DateAttrs,
|
|
27
46
|
format: CoreXform.DateFormat
|
|
28
47
|
}),
|
|
29
|
-
|
|
30
|
-
tag:
|
|
48
|
+
modified: new DateXform({
|
|
49
|
+
tag: PROPS.modified,
|
|
31
50
|
attrs: CoreXform.DateAttrs,
|
|
32
51
|
format: CoreXform.DateFormat
|
|
33
52
|
})
|
|
@@ -36,22 +55,9 @@ class CoreXform extends BaseXform {
|
|
|
36
55
|
render(xmlStream, model) {
|
|
37
56
|
xmlStream.openXml(XmlStream.StdDocAttributes);
|
|
38
57
|
xmlStream.openNode("cp:coreProperties", CoreXform.CORE_PROPERTY_ATTRIBUTES);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.map["dc:description"].render(xmlStream, model.description);
|
|
43
|
-
this.map["dc:identifier"].render(xmlStream, model.identifier);
|
|
44
|
-
this.map["dc:language"].render(xmlStream, model.language);
|
|
45
|
-
this.map["cp:keywords"].render(xmlStream, model.keywords);
|
|
46
|
-
this.map["cp:category"].render(xmlStream, model.category);
|
|
47
|
-
this.map["cp:lastModifiedBy"].render(xmlStream, model.lastModifiedBy);
|
|
48
|
-
this.map["cp:lastPrinted"].render(xmlStream, model.lastPrinted);
|
|
49
|
-
this.map["cp:revision"].render(xmlStream, model.revision);
|
|
50
|
-
this.map["cp:version"].render(xmlStream, model.version);
|
|
51
|
-
this.map["cp:contentStatus"].render(xmlStream, model.contentStatus);
|
|
52
|
-
this.map["cp:contentType"].render(xmlStream, model.contentType);
|
|
53
|
-
this.map["dcterms:created"].render(xmlStream, model.created);
|
|
54
|
-
this.map["dcterms:modified"].render(xmlStream, model.modified);
|
|
58
|
+
for (const key of Object.keys(PROPS)) {
|
|
59
|
+
this.map[key].render(xmlStream, model[key]);
|
|
60
|
+
}
|
|
55
61
|
xmlStream.closeNode();
|
|
56
62
|
}
|
|
57
63
|
parseOpen(node) {
|
|
@@ -59,18 +65,13 @@ class CoreXform extends BaseXform {
|
|
|
59
65
|
this.parser.parseOpen(node);
|
|
60
66
|
return true;
|
|
61
67
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
this.parser = this.map[node.name];
|
|
68
|
-
if (this.parser) {
|
|
69
|
-
this.parser.parseOpen(node);
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
throw new Error(`Unexpected xml node in parseOpen: ${JSON.stringify(node)}`);
|
|
68
|
+
if (node.name !== "coreProperties") {
|
|
69
|
+
this.parser = this.map[node.name];
|
|
70
|
+
if (this.parser) {
|
|
71
|
+
this.parser.parseOpen(node);
|
|
72
|
+
}
|
|
73
73
|
}
|
|
74
|
+
return true;
|
|
74
75
|
}
|
|
75
76
|
parseText(text) {
|
|
76
77
|
if (this.parser) {
|
|
@@ -84,30 +85,17 @@ class CoreXform extends BaseXform {
|
|
|
84
85
|
}
|
|
85
86
|
return true;
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
this.model
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
language: this.map["dc:language"].model,
|
|
97
|
-
keywords: this.map["cp:keywords"].model,
|
|
98
|
-
category: this.map["cp:category"].model,
|
|
99
|
-
lastModifiedBy: this.map["cp:lastModifiedBy"].model,
|
|
100
|
-
lastPrinted: this.map["cp:lastPrinted"].model,
|
|
101
|
-
revision: this.map["cp:revision"].model,
|
|
102
|
-
contentStatus: this.map["cp:contentStatus"].model,
|
|
103
|
-
contentType: this.map["cp:contentType"].model,
|
|
104
|
-
created: this.map["dcterms:created"].model,
|
|
105
|
-
modified: this.map["dcterms:modified"].model
|
|
106
|
-
};
|
|
107
|
-
return false;
|
|
108
|
-
default:
|
|
109
|
-
throw new Error(`Unexpected xml node in parseClose: ${name}`);
|
|
88
|
+
if (name === "coreProperties") {
|
|
89
|
+
this.model = {};
|
|
90
|
+
for (const key of Object.keys(PROPS)) {
|
|
91
|
+
const val = this.map[key].model;
|
|
92
|
+
if (val !== undefined && val !== "") {
|
|
93
|
+
this.model[key] = val;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
110
97
|
}
|
|
98
|
+
return true;
|
|
111
99
|
}
|
|
112
100
|
}
|
|
113
101
|
CoreXform.DateFormat = function (dt) {
|
|
@@ -41,17 +41,15 @@ class ListXform extends BaseXform {
|
|
|
41
41
|
this.parser.parseOpen(node);
|
|
42
42
|
return true;
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return true;
|
|
48
|
-
default:
|
|
49
|
-
if (this.childXform.parseOpen(node)) {
|
|
50
|
-
this.parser = this.childXform;
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
return false;
|
|
44
|
+
if (node.name === this.tag) {
|
|
45
|
+
this.model = [];
|
|
46
|
+
return true;
|
|
54
47
|
}
|
|
48
|
+
if (this.childXform.parseOpen(node)) {
|
|
49
|
+
this.parser = this.childXform;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
55
53
|
}
|
|
56
54
|
parseText(text) {
|
|
57
55
|
if (this.parser) {
|
|
@@ -36,16 +36,15 @@ class SharedStringXform extends BaseXform {
|
|
|
36
36
|
xmlStream.closeNode();
|
|
37
37
|
}
|
|
38
38
|
parseOpen(node) {
|
|
39
|
-
const { name } = node;
|
|
40
39
|
if (this.parser) {
|
|
41
40
|
this.parser.parseOpen(node);
|
|
42
41
|
return true;
|
|
43
42
|
}
|
|
44
|
-
if (name === this.tag) {
|
|
43
|
+
if (node.name === this.tag) {
|
|
45
44
|
this.model = {};
|
|
46
45
|
return true;
|
|
47
46
|
}
|
|
48
|
-
this.parser = this.map[name];
|
|
47
|
+
this.parser = this.map[node.name];
|
|
49
48
|
if (this.parser) {
|
|
50
49
|
this.parser.parseOpen(node);
|
|
51
50
|
return true;
|
|
@@ -13,14 +13,12 @@ class TextXform extends BaseXform {
|
|
|
13
13
|
xmlStream.closeNode();
|
|
14
14
|
}
|
|
15
15
|
parseOpen(node) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return true;
|
|
21
|
-
default:
|
|
22
|
-
return false;
|
|
16
|
+
if (node.name === "t") {
|
|
17
|
+
this._text = [];
|
|
18
|
+
this.model = "";
|
|
19
|
+
return true;
|
|
23
20
|
}
|
|
21
|
+
return false;
|
|
24
22
|
}
|
|
25
23
|
parseText(text) {
|
|
26
24
|
this._text.push(text);
|
|
@@ -364,8 +364,10 @@ class XLSX {
|
|
|
364
364
|
case OOXML_PATHS.docPropsApp: {
|
|
365
365
|
const appXform = new AppXform();
|
|
366
366
|
const appProperties = await appXform.parseStream(stream);
|
|
367
|
-
|
|
368
|
-
|
|
367
|
+
if (appProperties) {
|
|
368
|
+
model.company = appProperties.company;
|
|
369
|
+
model.manager = appProperties.manager;
|
|
370
|
+
}
|
|
369
371
|
break;
|
|
370
372
|
}
|
|
371
373
|
case OOXML_PATHS.docPropsCore: {
|
|
@@ -827,8 +829,10 @@ class XLSX {
|
|
|
827
829
|
case OOXML_PATHS.docPropsApp: {
|
|
828
830
|
const appXform = new AppXform();
|
|
829
831
|
const appProperties = await appXform.parseStream(stream);
|
|
830
|
-
|
|
831
|
-
|
|
832
|
+
if (appProperties) {
|
|
833
|
+
model.company = appProperties.company;
|
|
834
|
+
model.manager = appProperties.manager;
|
|
835
|
+
}
|
|
832
836
|
break;
|
|
833
837
|
}
|
|
834
838
|
case OOXML_PATHS.docPropsCore: {
|
|
@@ -146,6 +146,12 @@ const XML_ENTITIES = {
|
|
|
146
146
|
quot: '"',
|
|
147
147
|
apos: "'"
|
|
148
148
|
};
|
|
149
|
+
// HAN CELL namespace prefix normalization
|
|
150
|
+
// HAN CELL uses non-standard namespace prefixes (ep:, cp:, dc:, etc.)
|
|
151
|
+
// The x: prefix for spreadsheetml is detected dynamically from xmlns declarations
|
|
152
|
+
// See: https://github.com/exceljs/exceljs/issues/3014
|
|
153
|
+
const HAN_CELL_PREFIXES = /^(ep|cp|dc|dcterms|dcmitype|vt):/;
|
|
154
|
+
const SPREADSHEETML_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
|
149
155
|
// ============================================================================
|
|
150
156
|
// Parser States
|
|
151
157
|
// ============================================================================
|
|
@@ -206,6 +212,8 @@ class SaxesParser {
|
|
|
206
212
|
this.chunkPosition = 0;
|
|
207
213
|
// Entity storage
|
|
208
214
|
this.ENTITIES = { ...XML_ENTITIES };
|
|
215
|
+
// HAN CELL compatibility: spreadsheetml namespace prefix (e.g., "x")
|
|
216
|
+
this.nsPrefix = null;
|
|
209
217
|
this.trackPosition = opt?.position !== false;
|
|
210
218
|
this.fileName = opt?.fileName;
|
|
211
219
|
this.fragment = opt?.fragment ?? false;
|
|
@@ -240,6 +248,14 @@ class SaxesParser {
|
|
|
240
248
|
this.chunk = "";
|
|
241
249
|
this.i = 0;
|
|
242
250
|
this.prevI = 0;
|
|
251
|
+
this.nsPrefix = null;
|
|
252
|
+
}
|
|
253
|
+
// Strip HAN CELL namespace prefixes from element names
|
|
254
|
+
stripNsPrefix(name) {
|
|
255
|
+
const n = name.replace(HAN_CELL_PREFIXES, "");
|
|
256
|
+
return this.nsPrefix && n.startsWith(this.nsPrefix + ":")
|
|
257
|
+
? n.slice(this.nsPrefix.length + 1)
|
|
258
|
+
: n;
|
|
243
259
|
}
|
|
244
260
|
on(name, handler) {
|
|
245
261
|
switch (name) {
|
|
@@ -655,9 +671,8 @@ class SaxesParser {
|
|
|
655
671
|
this.name += charFromCode(c);
|
|
656
672
|
return;
|
|
657
673
|
}
|
|
658
|
-
// Tag name complete
|
|
659
674
|
this.tag = {
|
|
660
|
-
name: this.name,
|
|
675
|
+
name: this.stripNsPrefix(this.name),
|
|
661
676
|
attributes: Object.create(null),
|
|
662
677
|
isSelfClosing: false
|
|
663
678
|
};
|
|
@@ -1096,11 +1111,7 @@ class SaxesParser {
|
|
|
1096
1111
|
openTag() {
|
|
1097
1112
|
const tag = this.tag;
|
|
1098
1113
|
tag.isSelfClosing = false;
|
|
1099
|
-
|
|
1100
|
-
for (const { name, value } of this.attribList) {
|
|
1101
|
-
tag.attributes[name] = value;
|
|
1102
|
-
}
|
|
1103
|
-
this.attribList = [];
|
|
1114
|
+
this.processAttributes(tag);
|
|
1104
1115
|
this.openTagHandler?.(tag);
|
|
1105
1116
|
this.tags.push(tag);
|
|
1106
1117
|
this.name = "";
|
|
@@ -1109,11 +1120,7 @@ class SaxesParser {
|
|
|
1109
1120
|
openSelfClosingTag() {
|
|
1110
1121
|
const tag = this.tag;
|
|
1111
1122
|
tag.isSelfClosing = true;
|
|
1112
|
-
|
|
1113
|
-
for (const { name, value } of this.attribList) {
|
|
1114
|
-
tag.attributes[name] = value;
|
|
1115
|
-
}
|
|
1116
|
-
this.attribList = [];
|
|
1123
|
+
this.processAttributes(tag);
|
|
1117
1124
|
this.openTagHandler?.(tag);
|
|
1118
1125
|
this.closeTagHandler?.(tag);
|
|
1119
1126
|
if (this.tags.length === 0) {
|
|
@@ -1122,8 +1129,20 @@ class SaxesParser {
|
|
|
1122
1129
|
this.name = "";
|
|
1123
1130
|
this.state = S_TEXT;
|
|
1124
1131
|
}
|
|
1132
|
+
// Process attributes and detect spreadsheetml namespace prefix
|
|
1133
|
+
processAttributes(tag) {
|
|
1134
|
+
for (const { name, value } of this.attribList) {
|
|
1135
|
+
tag.attributes[name] = value;
|
|
1136
|
+
if (name.startsWith("xmlns:") && value === SPREADSHEETML_NS) {
|
|
1137
|
+
this.nsPrefix = name.slice(6);
|
|
1138
|
+
tag.name = this.stripNsPrefix(tag.name);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
this.attribList = [];
|
|
1142
|
+
}
|
|
1125
1143
|
closeTag() {
|
|
1126
|
-
const { tags
|
|
1144
|
+
const { tags } = this;
|
|
1145
|
+
const name = this.stripNsPrefix(this.name);
|
|
1127
1146
|
this.state = S_TEXT;
|
|
1128
1147
|
this.name = "";
|
|
1129
1148
|
if (name === "") {
|
|
@@ -13,7 +13,7 @@ class AppXform extends base_xform_1.BaseXform {
|
|
|
13
13
|
Company: new string_xform_1.StringXform({ tag: "Company" }),
|
|
14
14
|
Manager: new string_xform_1.StringXform({ tag: "Manager" }),
|
|
15
15
|
HeadingPairs: new app_heading_pairs_xform_1.AppHeadingPairsXform(),
|
|
16
|
-
|
|
16
|
+
TitlesOfParts: new app_titles_of_parts_xform_1.AppTitlesOfPartsXform()
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
render(xmlStream, model) {
|
|
@@ -23,7 +23,7 @@ class AppXform extends base_xform_1.BaseXform {
|
|
|
23
23
|
xmlStream.leafNode("DocSecurity", undefined, "0");
|
|
24
24
|
xmlStream.leafNode("ScaleCrop", undefined, "false");
|
|
25
25
|
this.map.HeadingPairs.render(xmlStream, model.worksheets);
|
|
26
|
-
this.map.
|
|
26
|
+
this.map.TitlesOfParts.render(xmlStream, model.worksheets);
|
|
27
27
|
this.map.Company.render(xmlStream, model.company || "");
|
|
28
28
|
this.map.Manager.render(xmlStream, model.manager);
|
|
29
29
|
xmlStream.leafNode("LinksUpToDate", undefined, "false");
|
|
@@ -65,7 +65,7 @@ class AppXform extends base_xform_1.BaseXform {
|
|
|
65
65
|
switch (name) {
|
|
66
66
|
case "Properties":
|
|
67
67
|
this.model = {
|
|
68
|
-
worksheets: this.map.
|
|
68
|
+
worksheets: this.map.TitlesOfParts.model,
|
|
69
69
|
company: this.map.Company.model,
|
|
70
70
|
manager: this.map.Manager.model
|
|
71
71
|
};
|