single-file-core 1.5.47 → 1.5.49

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.
@@ -21,7 +21,7 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- /* global Node, FileReader */
24
+ /* global Node */
25
25
 
26
26
  import {
27
27
  configure,
@@ -167,11 +167,12 @@ async function process(pageData, options, lastModDate = new Date()) {
167
167
  }
168
168
  const endTags = options.preventAppendedData || options.embeddedImage ? "" : "</body></html>";
169
169
  if (options.extractDataFromPage) {
170
- const payload = await Promise.all([
171
- arrayToBase64(insertionsCRLF),
172
- arrayToBase64(substitutionsLF)
173
- ]);
174
- extraData = "<sfz-extra-data>" + payload.join(",") + "</sfz-extra-data>";
170
+ const payload = new Uint32Array(insertionsCRLF.length + substitutionsLF.length + 2);
171
+ payload.set(new Uint32Array([insertionsCRLF.length]), 0);
172
+ payload.set(new Uint32Array(insertionsCRLF), 1);
173
+ payload.set(new Uint32Array([substitutionsLF.length]), insertionsCRLF.length + 1);
174
+ payload.set(new Uint32Array(substitutionsLF), insertionsCRLF.length + 2);
175
+ extraData = "<sfz-extra-data>" + compress(payload.buffer) + "</sfz-extra-data>";
175
176
  if (options.preventAppendedData || extraData.length > 65535 - endTags.length - (options.embeddedImage ? PNG_IEND_LENGTH : 0)) {
176
177
  if (!options.extraDataSize) {
177
178
  options.extraDataSize = Math.floor(extraData.length * 1.001);
@@ -365,14 +366,6 @@ function findExtraDataTags(textContent, pageData, options, lastModDate, indexExt
365
366
  }
366
367
  }
367
368
 
368
- async function arrayToBase64(data) {
369
- const fileReader = new FileReader();
370
- return await new Promise(resolve => {
371
- fileReader.onload = event => resolve(event.target.result.substring(37));
372
- fileReader.readAsDataURL(new Blob([new Uint32Array(data)], { type: "application/octet-stream" }));
373
- });
374
- }
375
-
376
369
  async function writeData(writable, array) {
377
370
  const streamWriter = writable.getWriter();
378
371
  await streamWriter.ready;
@@ -425,7 +418,8 @@ async function addFile(zipWriter, prefixName, data) {
425
418
  }
426
419
 
427
420
  async function getContent() {
428
- const { Blob, XMLHttpRequest, fetch, document, stop } = globalThis;
421
+ const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
422
+ const { Blob, XMLHttpRequest, document, stop } = globalThis;
429
423
  const characterMap = new Map([
430
424
  [65533, 0], [8364, 128], [8218, 130], [402, 131], [8222, 132], [8230, 133], [8224, 134], [8225, 135], [710, 136], [8240, 137],
431
425
  [352, 138], [8249, 139], [338, 140], [381, 142], [8216, 145], [8217, 146], [8220, 147], [8221, 148], [8226, 149], [8211, 150],
@@ -483,9 +477,11 @@ async function getContent() {
483
477
  const charCode = textContent.charCodeAt(index);
484
478
  zipData.push(charCode > 255 ? characterMap.get(charCode) : charCode);
485
479
  }
486
- const [insertionsCRLFData, substitutionsLFData] = zipDataElement.textContent.split(",");
487
- const insertionsCRLF = await base64ToUint32Array(insertionsCRLFData);
488
- const substitutionsLF = await base64ToUint32Array(substitutionsLFData);
480
+ const payload = new Uint32Array(decompress(zipDataElement.textContent).buffer);
481
+ const insertionsCRLFLength = payload[0];
482
+ const insertionsCRLF = payload.slice(1, 1 + insertionsCRLFLength);
483
+ const substitutionsLFLength = payload[1 + insertionsCRLFLength];
484
+ const substitutionsLF = payload.slice(2 + insertionsCRLFLength, 2 + insertionsCRLFLength + substitutionsLFLength);
489
485
  insertionsCRLF.forEach(index => zipData.splice(index, 1, 13, 10));
490
486
  substitutionsLF.forEach(index => zipData[index] = 13);
491
487
  return new Blob([new Uint8Array(zipData)], { type: "application/octet-stream" });
@@ -493,7 +489,173 @@ async function getContent() {
493
489
  throw new Error("Extra zip data data not found");
494
490
  }
495
491
 
496
- async function base64ToUint32Array(data) {
497
- return new Uint32Array(await (await fetch("data:application/octet-stream;base64," + data)).arrayBuffer());
492
+ function decompress(src) {
493
+ src = base64Decode(src);
494
+ let out = new Uint8Array(1024);
495
+ let outLen = 0;
496
+ for (let i = 0; i < src.length;) {
497
+ const ctrl = src[i++];
498
+ if ((ctrl & 0x80) === 0) {
499
+ const L = ctrl;
500
+ ensure(outLen + L);
501
+ for (let j = 0; j < L && i < src.length; j++) {
502
+ out[outLen++] = src[i++];
503
+ }
504
+ } else {
505
+ const L = (ctrl & 0x7f) + 3;
506
+ const off = (src[i++] << 8) | src[i++];
507
+ const start = outLen - off;
508
+ ensure(outLen + L);
509
+ for (let k = 0; k < L; k++) {
510
+ out[outLen++] = out[start + k];
511
+ }
512
+ }
513
+ }
514
+ return new Uint8Array(out.buffer.slice(0, outLen));
515
+
516
+ function ensure(n) {
517
+ if (out.length < n) {
518
+ let nl = out.length * 2;
519
+ while (nl < n) {
520
+ nl *= 2;
521
+ }
522
+ const nbuf = new Uint8Array(nl);
523
+ nbuf.set(out.subarray(0, outLen));
524
+ out = nbuf;
525
+ }
526
+ };
527
+ }
528
+
529
+ function base64Decode(b64) {
530
+ b64 = String(b64).replace(/[^A-Za-z0-9+/=]/g, "");
531
+ const len = b64.length;
532
+ const out = [];
533
+ for (let i = 0; i < len; i += 4) {
534
+ const a = BASE64_TABLE.indexOf(b64[i]);
535
+ const b = BASE64_TABLE.indexOf(b64[i + 1]);
536
+ const c = BASE64_TABLE.indexOf(b64[i + 2]);
537
+ const d = BASE64_TABLE.indexOf(b64[i + 3]);
538
+ const n = (a << 18) | (b << 12) | ((c & 63) << 6) | (d & 63);
539
+ out.push((n >> 16) & 0xff);
540
+ if (b64[i + 2] !== "=") {
541
+ out.push((n >> 8) & 0xff);
542
+ }
543
+ if (b64[i + 3] !== "=") {
544
+ out.push(n & 0xff);
545
+ }
546
+ }
547
+ return new Uint8Array(out);
498
548
  }
499
549
  }
550
+
551
+ const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
552
+
553
+ function base64Encode(bytes) {
554
+ let out = "";
555
+ const len = bytes.length;
556
+ let i = 0;
557
+ for (; i + 2 < len; i += 3) {
558
+ const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
559
+ out += BASE64_TABLE[(n >> 18) & 63] + BASE64_TABLE[(n >> 12) & 63] + BASE64_TABLE[(n >> 6) & 63] + BASE64_TABLE[n & 63];
560
+ }
561
+ const rem = len - i;
562
+ if (rem === 1) {
563
+ const n = bytes[i] << 16;
564
+ out += BASE64_TABLE[(n >> 18) & 63] + BASE64_TABLE[(n >> 12) & 63] + "==";
565
+ } else if (rem === 2) {
566
+ const n = (bytes[i] << 16) | (bytes[i + 1] << 8);
567
+ out += BASE64_TABLE[(n >> 18) & 63] + BASE64_TABLE[(n >> 12) & 63] + BASE64_TABLE[(n >> 6) & 63] + "=";
568
+ }
569
+ return out;
570
+ }
571
+
572
+ function compress(input) {
573
+ const src = new Uint8Array(input);
574
+ const N = src.length;
575
+ const out = [];
576
+ const litBuf = [];
577
+ const MAX_OFFSET = 0xffff;
578
+ const MAX_MATCH = 130;
579
+ const MAX_CANDIDATES = 64;
580
+ const map = new Map();
581
+ let i = 0;
582
+ while (i < N) {
583
+ let bestLen = 0, bestOff = 0;
584
+ if (i + 2 < N) {
585
+ const key = (src[i] << 16) | (src[i + 1] << 8) | src[i + 2];
586
+ const cand = map.get(key) || [];
587
+ for (let c = cand.length - 1; c >= 0; c--) {
588
+ const j = cand[c];
589
+ const off = i - j;
590
+ if (off <= 0 || off > MAX_OFFSET) {
591
+ continue;
592
+ }
593
+ let k = 0;
594
+ while (k < MAX_MATCH && i + k < N && src[j + k] === src[i + k]) {
595
+ k++;
596
+ }
597
+ if (k > bestLen && k >= 3) {
598
+ bestLen = k; bestOff = off;
599
+ if (bestLen === MAX_MATCH) {
600
+ break;
601
+ }
602
+ }
603
+ }
604
+ }
605
+
606
+ if (bestLen >= 3) {
607
+ if (litBuf.length) {
608
+ flushLiterals();
609
+ }
610
+ let remain = bestLen;
611
+ let produced = 0;
612
+ while (remain > 0) {
613
+ const take = Math.min(remain, MAX_MATCH);
614
+ out.push(0x80 | ((take - 3) & 0x7f));
615
+ out.push((bestOff >> 8) & 0xff);
616
+ out.push(bestOff & 0xff);
617
+ remain -= take;
618
+ produced += take;
619
+ }
620
+ const start = i;
621
+ for (let p = start; p < start + produced; p++) {
622
+ addPos(p);
623
+ }
624
+ i += produced;
625
+ } else {
626
+ litBuf.push(src[i]);
627
+ addPos(i);
628
+ i++;
629
+ if (litBuf.length === 127) {
630
+ flushLiterals();
631
+ }
632
+ }
633
+ }
634
+ if (litBuf.length) {
635
+ flushLiterals();
636
+ }
637
+ const u8 = new Uint8Array(out);
638
+ return base64Encode(u8);
639
+
640
+ function flushLiterals() {
641
+ while (litBuf.length) {
642
+ const take = Math.min(127, litBuf.length);
643
+ out.push(take);
644
+ for (let t = 0; t < take; t++) {
645
+ out.push(litBuf.shift());
646
+ }
647
+ }
648
+ }
649
+
650
+ function addPos(pos) {
651
+ if (pos + 2 < N) {
652
+ const key = (src[pos] << 16) | (src[pos + 1] << 8) | src[pos + 2];
653
+ const arr = map.get(key) || [];
654
+ arr.push(pos);
655
+ if (arr.length > MAX_CANDIDATES) {
656
+ arr.shift();
657
+ }
658
+ map.set(key, arr);
659
+ }
660
+ }
661
+ }
@@ -21,8 +21,6 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- /* global window */
25
-
26
24
  (globalThis => {
27
25
 
28
26
  const LOAD_DEFERRED_IMAGES_START_EVENT = "single-file-load-deferred-images-start";
@@ -261,7 +259,7 @@
261
259
  if (!keepZoomLevel) {
262
260
  dispatchResizeEvent();
263
261
  const docBoundingRect = scrollingElement.getBoundingClientRect();
264
- if (window == window.top) {
262
+ if (globalThis.window == globalThis.window.top) {
265
263
  [...observers].forEach(([intersectionObserver, observer]) => {
266
264
  const getBoundingClientRectDefined = observer.options && observer.options.root && observer.options.root.getBoundingClientRect;
267
265
  const rootBoundingRect = getBoundingClientRectDefined && observer.options.root.getBoundingClientRect();
@@ -446,4 +444,4 @@
446
444
  }
447
445
  }
448
446
 
449
- })(typeof globalThis == "object" ? globalThis : window);
447
+ })(typeof globalThis == "object" ? globalThis : globalThis.window);
@@ -21,8 +21,6 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- /* global window */
25
-
26
24
  const LOAD_DEFERRED_IMAGES_START_EVENT = "single-file-load-deferred-images-start";
27
25
  const LOAD_DEFERRED_IMAGES_END_EVENT = "single-file-load-deferred-images-end";
28
26
  const LOAD_DEFERRED_IMAGES_KEEP_ZOOM_LEVEL_START_EVENT = "single-file-load-deferred-images-keep-zoom-level-start";
@@ -51,15 +49,15 @@ const JSON = globalThis.JSON;
51
49
  const MutationObserver = globalThis.MutationObserver;
52
50
 
53
51
  let fontFaces, worklets;
54
- if (window[FONT_FACE_PROPERTY_NAME]) {
55
- fontFaces = window[FONT_FACE_PROPERTY_NAME];
52
+ if (globalThis.window[FONT_FACE_PROPERTY_NAME]) {
53
+ fontFaces = globalThis.window[FONT_FACE_PROPERTY_NAME];
56
54
  } else {
57
- fontFaces = window[FONT_FACE_PROPERTY_NAME] = new Map();
55
+ fontFaces = globalThis.window[FONT_FACE_PROPERTY_NAME] = new Map();
58
56
  }
59
- if (window[WORKLET_PROPERTY_NAME]) {
60
- worklets = window[WORKLET_PROPERTY_NAME];
57
+ if (globalThis.window[WORKLET_PROPERTY_NAME]) {
58
+ worklets = globalThis.window[WORKLET_PROPERTY_NAME];
61
59
  } else {
62
- worklets = window[WORKLET_PROPERTY_NAME] = new Map();
60
+ worklets = globalThis.window[WORKLET_PROPERTY_NAME] = new Map();
63
61
  }
64
62
 
65
63
  init();
@@ -49,25 +49,24 @@
49
49
  */
50
50
 
51
51
  import * as cssTree from "./css-tree.js";
52
- const REGEXP_SIMPLE_QUOTES_STRING = /^'(.*?)'$/;
53
- const REGEXP_DOUBLE_QUOTES_STRING = /^"(.*?)"$/;
52
+ import { process as cssUnescape } from "./css-unescape.js";
54
53
 
55
- const globalKeywords = [
54
+ const GLOBAL_KEYWORDS = new Set([
56
55
  "inherit",
57
56
  "initial",
58
57
  "unset"
59
- ];
58
+ ]);
60
59
 
61
- const systemFontKeywords = [
60
+ const SYSTEM_FONT_KEYWORDS = new Set([
62
61
  "caption",
63
62
  "icon",
64
63
  "menu",
65
64
  "message-box",
66
65
  "small-caption",
67
66
  "status-bar"
68
- ];
67
+ ]);
69
68
 
70
- const fontWeightKeywords = [
69
+ const FONT_WEIGHT_KEYWORDS = new Set([
71
70
  "normal",
72
71
  "bold",
73
72
  "bolder",
@@ -81,15 +80,20 @@ const fontWeightKeywords = [
81
80
  "700",
82
81
  "800",
83
82
  "900"
84
- ];
83
+ ]);
85
84
 
86
- const fontStyleKeywords = [
85
+ const FONT_STYLE_KEYWORDS = new Set([
87
86
  "normal",
88
87
  "italic",
89
88
  "oblique"
90
- ];
89
+ ]);
91
90
 
92
- const fontStretchKeywords = [
91
+ const FONT_VARIANT_KEYWORDS = new Set([
92
+ "normal",
93
+ "small-caps"
94
+ ]);
95
+
96
+ const FONT_STRETCH_KEYWORDS = new Set([
93
97
  "normal",
94
98
  "condensed",
95
99
  "semi-condensed",
@@ -99,7 +103,29 @@ const fontStretchKeywords = [
99
103
  "semi-expanded",
100
104
  "extra-expanded",
101
105
  "ultra-expanded"
102
- ];
106
+ ]);
107
+
108
+ const SIZE_TYPES = new Set([
109
+ "Dimension",
110
+ "Identifier",
111
+ "Percentage",
112
+ "Number",
113
+ "Function",
114
+ "UnaryExpression"
115
+ ]);
116
+
117
+ const FONT_DESCRIPTOR_KEYS = new Set([
118
+ "style",
119
+ "variant",
120
+ "weight",
121
+ "stretch"
122
+ ]);
123
+
124
+ const OPERATOR_TYPE = "Operator";
125
+ const IDENTIFIER_TYPE = "Identifier";
126
+ const NORMAL_KEYWORD = "normal";
127
+ const LINE_HEIGHT_SEPARATOR = "/";
128
+ const FAMILY_SEPARATOR = ",";
103
129
 
104
130
  const errorPrefix = "[parse-css-font] ";
105
131
 
@@ -109,64 +135,67 @@ export {
109
135
 
110
136
  function parse(value) {
111
137
  const stringValue = cssTree.generate(value);
112
- if (systemFontKeywords.indexOf(stringValue) !== -1) {
138
+ const stringValueLower = stringValue.toLowerCase();
139
+ if (SYSTEM_FONT_KEYWORDS.has(stringValueLower)) {
113
140
  return { system: stringValue };
114
141
  }
142
+ if (GLOBAL_KEYWORDS.has(stringValueLower)) {
143
+ return { global: stringValue };
144
+ }
115
145
  const tokens = value.children;
116
-
117
146
  const font = {
118
- lineHeight: "normal",
119
- stretch: "normal",
120
- style: "normal",
121
- variant: "normal",
122
- weight: "normal",
147
+ lineHeight: NORMAL_KEYWORD,
148
+ stretch: NORMAL_KEYWORD,
149
+ style: NORMAL_KEYWORD,
150
+ variant: NORMAL_KEYWORD,
151
+ weight: NORMAL_KEYWORD,
123
152
  };
124
-
125
- let isLocked = false;
153
+ const seen = { style: false, variant: false, weight: false, stretch: false };
126
154
  for (let tokenNode = tokens.head; tokenNode; tokenNode = tokenNode.next) {
127
- const token = cssTree.generate(tokenNode.data);
128
- if (token === "normal" || globalKeywords.indexOf(token) !== -1) {
129
- ["style", "variant", "weight", "stretch"].forEach((prop) => {
130
- font[prop] = token;
155
+ const tokenRaw = tokenNode.data.name || tokenNode.data.value || cssTree.generate(tokenNode.data);
156
+ const token = tokenRaw.toLowerCase();
157
+ if (token === NORMAL_KEYWORD) {
158
+ FONT_DESCRIPTOR_KEYS.forEach((prop) => {
159
+ if (!seen[prop]) {
160
+ font[prop] = tokenRaw;
161
+ }
131
162
  });
132
- isLocked = true;
133
163
  continue;
134
164
  }
135
-
136
- if (fontWeightKeywords.indexOf(token) !== -1) {
137
- if (isLocked) {
138
- continue;
165
+ if (FONT_WEIGHT_KEYWORDS.has(token)) {
166
+ if (!seen.weight) {
167
+ font.weight = tokenRaw;
168
+ seen.weight = true;
139
169
  }
140
- font.weight = token;
141
170
  continue;
142
171
  }
143
-
144
- if (fontStyleKeywords.indexOf(token) !== -1) {
145
- if (isLocked) {
146
- continue;
172
+ if (FONT_STYLE_KEYWORDS.has(token)) {
173
+ if (!seen.style) {
174
+ font.style = tokenRaw;
175
+ seen.style = true;
147
176
  }
148
- font.style = token;
149
177
  continue;
150
178
  }
151
-
152
- if (fontStretchKeywords.indexOf(token) !== -1) {
153
- if (isLocked) {
154
- continue;
179
+ if (FONT_VARIANT_KEYWORDS.has(token)) {
180
+ if (!seen.variant) {
181
+ font.variant = tokenRaw;
182
+ seen.variant = true;
155
183
  }
156
- font.stretch = token;
157
184
  continue;
158
185
  }
159
-
160
- if (tokenNode.data.type == "Dimension") {
186
+ if (FONT_STRETCH_KEYWORDS.has(token)) {
187
+ if (!seen.stretch) {
188
+ font.stretch = tokenRaw;
189
+ seen.stretch = true;
190
+ }
191
+ continue;
192
+ }
193
+ if (SIZE_TYPES.has(tokenNode.data.type)) {
161
194
  font.size = cssTree.generate(tokenNode.data);
162
195
  tokenNode = tokenNode.next;
163
- if (tokenNode && tokenNode.data.type == "Operator" && tokenNode.data.value == "/" && tokenNode.next) {
164
- tokenNode = tokenNode.next;
165
- font.lineHeight = cssTree.generate(tokenNode.data);
166
- tokenNode = tokenNode.next;
167
- } else if (tokens.head.data.type == "Operator" && tokens.head.data.value == "/" && tokens.head.next) {
168
- font.lineHeight = cssTree.generate(tokens.head.next.data);
169
- tokenNode = tokens.head.next.next;
196
+ if (tokenNode && tokenNode.data.type == OPERATOR_TYPE && tokenNode.data.value == LINE_HEIGHT_SEPARATOR && tokenNode.next) {
197
+ font.lineHeight = cssTree.generate(tokenNode.next.data);
198
+ tokenNode = tokenNode.next.next;
170
199
  }
171
200
  if (!tokenNode) {
172
201
  throw error("Missing required font-family.");
@@ -174,12 +203,12 @@ function parse(value) {
174
203
  font.family = [];
175
204
  let familyName = "";
176
205
  while (tokenNode) {
177
- while (tokenNode && tokenNode.data.type == "Operator" && tokenNode.data.value == ",") {
206
+ while (tokenNode && tokenNode.data.type == OPERATOR_TYPE && tokenNode.data.value == FAMILY_SEPARATOR) {
178
207
  tokenNode = tokenNode.next;
179
208
  }
180
209
  if (tokenNode) {
181
- if (tokenNode.data.type == "Identifier") {
182
- while (tokenNode && tokenNode.data.type == "Identifier") {
210
+ if (tokenNode.data.type == IDENTIFIER_TYPE) {
211
+ while (tokenNode && tokenNode.data.type == IDENTIFIER_TYPE) {
183
212
  familyName += " " + cssTree.generate(tokenNode.data);
184
213
  tokenNode = tokenNode.next;
185
214
  }
@@ -196,15 +225,7 @@ function parse(value) {
196
225
  }
197
226
  return font;
198
227
  }
199
-
200
- if (font.variant !== "normal") {
201
- throw error("Unknown or unsupported font token: " + font.variant);
202
- }
203
-
204
- if (isLocked) {
205
- continue;
206
- }
207
- font.variant = token;
228
+ throw error("Unknown or unsupported font token: " + tokenRaw);
208
229
  }
209
230
 
210
231
  throw error("Missing required font-size.");
@@ -215,10 +236,9 @@ function error(message) {
215
236
  }
216
237
 
217
238
  function removeQuotes(string) {
218
- if (string.match(REGEXP_SIMPLE_QUOTES_STRING)) {
219
- string = string.replace(REGEXP_SIMPLE_QUOTES_STRING, "$1");
220
- } else {
221
- string = string.replace(REGEXP_DOUBLE_QUOTES_STRING, "$1");
239
+ if (!string) return string;
240
+ if ((string[0] === "\"" && string[string.length - 1] === "\"") || (string[0] === "'" && string[string.length - 1] === "'")) {
241
+ string = string.slice(1, -1);
222
242
  }
223
- return string.trim();
243
+ return cssUnescape(string).trim();
224
244
  }