musicxml-io 0.8.2 → 0.9.1

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.
@@ -6,7 +6,7 @@ import {
6
6
  ValidationException,
7
7
  generateId,
8
8
  validate
9
- } from "./chunk-CLONJVWZ.mjs";
9
+ } from "./chunk-T6KNPR45.mjs";
10
10
  import {
11
11
  bpmToUsPerQuarter,
12
12
  buildGridTimeline,
@@ -16,39 +16,168 @@ import {
16
16
  } from "./chunk-UMEH3ENQ.mjs";
17
17
 
18
18
  // src/importers/musicxml.ts
19
- import { parse as txmlParse } from "txml/txml";
20
19
  var _entityMap = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
21
20
  var _entityRegex = /&(?:(amp|lt|gt|quot|apos)|#(\d+)|#x([0-9a-fA-F]+));/g;
21
+ var INVALID_XML_CHARS_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\uFFFE\uFFFF]/g;
22
+ var INVALID_XML_CHARS_TEST = /[\x00-\x08\x0B\x0C\x0E-\x1F\uFFFE\uFFFF]/;
23
+ function decodeCharRef(match, codePoint) {
24
+ if (codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343) return match;
25
+ return String.fromCodePoint(codePoint);
26
+ }
22
27
  function decodeXmlEntities(s) {
23
28
  if (s.indexOf("&") === -1) return s;
24
- return s.replace(
29
+ const decoded = s.replace(
25
30
  _entityRegex,
26
- (_, named, dec, hex) => named ? _entityMap[named] : dec ? String.fromCharCode(parseInt(dec, 10)) : String.fromCharCode(parseInt(hex, 16))
31
+ (match, named, dec, hex) => named ? _entityMap[named] : decodeCharRef(match, parseInt(dec ?? hex, dec ? 10 : 16))
27
32
  );
28
- }
29
- var INVALID_XML_CHARS_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\uFFFE\uFFFF]/g;
30
- function decodeTree(nodes) {
31
- for (let i = 0; i < nodes.length; i++) {
32
- const node = nodes[i];
33
- if (typeof node === "string") {
34
- let s = node;
35
- if (s.indexOf("&") !== -1) {
36
- s = decodeXmlEntities(s);
37
- }
38
- nodes[i] = s.replace(INVALID_XML_CHARS_RE, "");
33
+ return INVALID_XML_CHARS_TEST.test(decoded) ? decoded.replace(INVALID_XML_CHARS_RE, "") : decoded;
34
+ }
35
+ var EMPTY_ATTRS = Object.freeze({});
36
+ var GT = 62;
37
+ var SLASH = 47;
38
+ var BANG = 33;
39
+ var QUESTION = 63;
40
+ var EQUALS = 61;
41
+ var DQUOTE = 34;
42
+ var SQUOTE = 39;
43
+ var LBRACKET = 91;
44
+ var RBRACKET = 93;
45
+ function isXmlWhitespace(code) {
46
+ return code === 32 || code === 10 || code === 9 || code === 13;
47
+ }
48
+ function isWhitespaceOnly(s) {
49
+ for (let i = 0; i < s.length; i++) {
50
+ if (!isXmlWhitespace(s.charCodeAt(i))) return false;
51
+ }
52
+ return true;
53
+ }
54
+ function parseXml(xml) {
55
+ const root = [];
56
+ const stack = [];
57
+ let children = root;
58
+ const len = xml.length;
59
+ let pos = 0;
60
+ while (pos < len) {
61
+ const lt = xml.indexOf("<", pos);
62
+ if (lt === -1) break;
63
+ if (lt > pos) {
64
+ let text = xml.slice(pos, lt);
65
+ if (!isWhitespaceOnly(text)) {
66
+ text = decodeXmlEntities(text);
67
+ children.push(
68
+ INVALID_XML_CHARS_TEST.test(text) ? text.replace(INVALID_XML_CHARS_RE, "") : text
69
+ );
70
+ } else if (children.length === 0 && xml.charCodeAt(lt + 1) === SLASH) {
71
+ children.push(text);
72
+ }
73
+ }
74
+ const c = xml.charCodeAt(lt + 1);
75
+ if (c === SLASH) {
76
+ const gt = xml.indexOf(">", lt + 2);
77
+ if (gt === -1) break;
78
+ let end = gt;
79
+ while (end > lt + 2 && isXmlWhitespace(xml.charCodeAt(end - 1))) end--;
80
+ const name = xml.slice(lt + 2, end);
81
+ for (let i = stack.length - 1; i >= 0; i--) {
82
+ if (stack[i].tagName === name) {
83
+ stack.length = i;
84
+ break;
85
+ }
86
+ }
87
+ children = stack.length > 0 ? stack[stack.length - 1].children : root;
88
+ pos = gt + 1;
89
+ } else if (c === QUESTION) {
90
+ const end = xml.indexOf("?>", lt + 2);
91
+ pos = end === -1 ? len : end + 2;
92
+ } else if (c === BANG) {
93
+ if (xml.startsWith("<!--", lt)) {
94
+ const end = xml.indexOf("-->", lt + 4);
95
+ pos = end === -1 ? len : end + 3;
96
+ } else if (xml.startsWith("<![CDATA[", lt)) {
97
+ const end = xml.indexOf("]]>", lt + 9);
98
+ let text = xml.slice(lt + 9, end === -1 ? len : end);
99
+ if (INVALID_XML_CHARS_TEST.test(text)) text = text.replace(INVALID_XML_CHARS_RE, "");
100
+ children.push(text);
101
+ pos = end === -1 ? len : end + 3;
102
+ } else {
103
+ let i = lt + 2;
104
+ let depth = 0;
105
+ while (i < len) {
106
+ const ch = xml.charCodeAt(i);
107
+ if (ch === LBRACKET) depth++;
108
+ else if (ch === RBRACKET) depth--;
109
+ else if (ch === GT && depth <= 0) break;
110
+ i++;
111
+ }
112
+ pos = i + 1;
113
+ }
39
114
  } else {
40
- const attrs = node.attributes;
41
- for (const key in attrs) {
42
- const v = attrs[key];
43
- if (v.indexOf("&") !== -1) {
44
- attrs[key] = decodeXmlEntities(v);
115
+ let i = lt + 1;
116
+ while (i < len) {
117
+ const ch = xml.charCodeAt(i);
118
+ if (ch === GT || ch === SLASH || isXmlWhitespace(ch)) break;
119
+ i++;
120
+ }
121
+ const tagName = xml.slice(lt + 1, i);
122
+ let attributes = EMPTY_ATTRS;
123
+ let selfClosing = false;
124
+ while (i < len) {
125
+ let ch = xml.charCodeAt(i);
126
+ while (isXmlWhitespace(ch)) ch = xml.charCodeAt(++i);
127
+ if (ch === GT) {
128
+ i++;
129
+ break;
130
+ }
131
+ if (ch === SLASH) {
132
+ selfClosing = true;
133
+ i++;
134
+ continue;
135
+ }
136
+ const nameStart = i;
137
+ while (i < len) {
138
+ ch = xml.charCodeAt(i);
139
+ if (ch === EQUALS || ch === GT || ch === SLASH || isXmlWhitespace(ch)) break;
140
+ i++;
141
+ }
142
+ const attrName = xml.slice(nameStart, i);
143
+ while (isXmlWhitespace(xml.charCodeAt(i))) i++;
144
+ if (xml.charCodeAt(i) === EQUALS) {
145
+ i++;
146
+ while (isXmlWhitespace(xml.charCodeAt(i))) i++;
147
+ ch = xml.charCodeAt(i);
148
+ let value;
149
+ if (ch === DQUOTE || ch === SQUOTE) {
150
+ const valStart = ++i;
151
+ while (i < len && xml.charCodeAt(i) !== ch) i++;
152
+ value = xml.slice(valStart, i);
153
+ i++;
154
+ } else {
155
+ const valStart = i;
156
+ while (i < len) {
157
+ ch = xml.charCodeAt(i);
158
+ if (ch === GT || isXmlWhitespace(ch)) break;
159
+ i++;
160
+ }
161
+ value = xml.slice(valStart, i);
162
+ }
163
+ if (attributes === EMPTY_ATTRS) attributes = {};
164
+ attributes[attrName] = decodeXmlEntities(value);
165
+ } else if (attrName.length > 0) {
166
+ if (attributes === EMPTY_ATTRS) attributes = {};
167
+ attributes[attrName] = "";
45
168
  }
46
169
  }
47
- decodeTree(node.children);
170
+ const node = { tagName, attributes, children: [] };
171
+ children.push(node);
172
+ if (!selfClosing) {
173
+ stack.push(node);
174
+ children = node.children;
175
+ }
176
+ pos = i;
48
177
  }
49
178
  }
179
+ return root;
50
180
  }
51
- var TXML_OPTIONS = { noChildNodes: [] };
52
181
  function decodeXmlBytes(data) {
53
182
  if (data.length >= 2 && data[0] === 254 && data[1] === 255) {
54
183
  return new TextDecoder("utf-16be").decode(data);
@@ -56,6 +185,10 @@ function decodeXmlBytes(data) {
56
185
  if (data.length >= 2 && data[0] === 255 && data[1] === 254) {
57
186
  return new TextDecoder("utf-16le").decode(data);
58
187
  }
188
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
189
+ const hasBom = data.length >= 3 && data[0] === 239 && data[1] === 187 && data[2] === 191;
190
+ return data.toString("utf8", hasBom ? 3 : 0);
191
+ }
59
192
  return new TextDecoder("utf-8").decode(data);
60
193
  }
61
194
  function parse(input) {
@@ -69,9 +202,10 @@ function parse(input) {
69
202
  } else {
70
203
  xmlString = input;
71
204
  }
72
- const cleanedXml = xmlString.replace(/<\?(?!xml\s)[^?]*\?>/g, "");
73
- const parsed = txmlParse(cleanedXml, TXML_OPTIONS);
74
- decodeTree(parsed);
205
+ if (INVALID_XML_CHARS_TEST.test(xmlString)) {
206
+ xmlString = xmlString.replace(INVALID_XML_CHARS_RE, "");
207
+ }
208
+ const parsed = parseXml(xmlString);
75
209
  let scorePartwiseVersion;
76
210
  let scorePartwise;
77
211
  for (const el of parsed) {
@@ -431,8 +565,21 @@ function parseCredits(elements) {
431
565
  if (a["xml:space"]) cw.xmlSpace = a["xml:space"];
432
566
  return cw;
433
567
  });
568
+ const images = collectElements(content, "credit-image", (_c, a) => {
569
+ const ci = { source: a["source"] || "", type: a["type"] || "" };
570
+ if (a["height"]) ci.height = parseFloat(a["height"]);
571
+ if (a["width"]) ci.width = parseFloat(a["width"]);
572
+ if (a["default-x"]) ci.defaultX = parseFloat(a["default-x"]);
573
+ if (a["default-y"]) ci.defaultY = parseFloat(a["default-y"]);
574
+ if (a["relative-x"]) ci.relativeX = parseFloat(a["relative-x"]);
575
+ if (a["relative-y"]) ci.relativeY = parseFloat(a["relative-y"]);
576
+ if (a["halign"]) ci.halign = a["halign"];
577
+ if (a["valign"]) ci.valign = a["valign"];
578
+ return ci;
579
+ });
434
580
  if (types.length > 0) credit.creditType = types;
435
581
  if (words.length > 0) credit.creditWords = words;
582
+ if (images.length > 0) credit.creditImage = images[0];
436
583
  return credit;
437
584
  });
438
585
  return credits.length > 0 ? credits : void 0;
@@ -4644,6 +4791,19 @@ function serializeCredit(credit, indent, out) {
4644
4791
  out.push(`${indent}${indent}<credit-type>${escapeXml(ct)}</credit-type>`);
4645
4792
  }
4646
4793
  }
4794
+ if (credit.creditImage) {
4795
+ const ci = credit.creditImage;
4796
+ let attrs2 = ` source="${escapeXml(ci.source)}" type="${escapeXml(ci.type)}"`;
4797
+ if (ci.height !== void 0) attrs2 += ` height="${ci.height}"`;
4798
+ if (ci.width !== void 0) attrs2 += ` width="${ci.width}"`;
4799
+ if (ci.defaultX !== void 0) attrs2 += ` default-x="${ci.defaultX}"`;
4800
+ if (ci.defaultY !== void 0) attrs2 += ` default-y="${ci.defaultY}"`;
4801
+ if (ci.relativeX !== void 0) attrs2 += ` relative-x="${ci.relativeX}"`;
4802
+ if (ci.relativeY !== void 0) attrs2 += ` relative-y="${ci.relativeY}"`;
4803
+ if (ci.halign) attrs2 += ` halign="${escapeXml(ci.halign)}"`;
4804
+ if (ci.valign) attrs2 += ` valign="${escapeXml(ci.valign)}"`;
4805
+ out.push(`${indent}${indent}<credit-image${attrs2}/>`);
4806
+ }
4647
4807
  if (credit.creditWords) {
4648
4808
  for (const cw of credit.creditWords) {
4649
4809
  let attrs2 = "";