single-file-core 1.6.15 → 1.6.17

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/core/index.js CHANGED
@@ -526,6 +526,7 @@ class Processor {
526
526
  return this.loadPage(pageContent, charset);
527
527
  }
528
528
  }
529
+ await this.processorHelper.resizeCanvasImages(this.doc, this.options.canvases, this.options);
529
530
  this.workStyleElement = this.doc.createElement("style");
530
531
  this.doc.body.appendChild(this.workStyleElement);
531
532
  this.onEventAttributeNames = new Set(getOnEventAttributeNames(this.doc));
@@ -64,6 +64,8 @@ const FONT_STRETCHES = {
64
64
  const Blob = globalThis.Blob;
65
65
  const Image = globalThis.Image;
66
66
  const OffscreenCanvas = globalThis.OffscreenCanvas;
67
+ const DEFAULT_IMAGE_QUALITY = 0.8;
68
+ const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
67
69
 
68
70
  let util, cssTree;
69
71
 
@@ -266,6 +268,16 @@ class ProcessorHelperCommon {
266
268
  }
267
269
  }
268
270
 
271
+ async resizeCanvasImages(doc, canvases, options) {
272
+ if (canvases && options.imageReductionFactor > 1) {
273
+ await Promise.all(canvases.map(async canvasData => {
274
+ if (canvasData && canvasData.dataURI) {
275
+ canvasData.dataURI = await resizeImage(doc, canvasData.dataURI, options);
276
+ }
277
+ }));
278
+ }
279
+ }
280
+
269
281
  setBackgroundImage(element, url, style) {
270
282
  element.style.setProperty("background-blend-mode", "normal", "important");
271
283
  element.style.setProperty("background-clip", "content-box", "important");
@@ -690,7 +702,7 @@ function getFontStretch(stretch) {
690
702
  return FONT_STRETCHES[stretch] || stretch;
691
703
  }
692
704
 
693
- async function resizeImage(doc, dataURI, { imageReductionFactor }) {
705
+ async function resizeImage(doc, dataURI, { imageReductionFactor, imageQuality }, element) {
694
706
  if (dataURI) {
695
707
  const contentType = dataURI.substring(5, dataURI.indexOf(";"));
696
708
  if (contentType == "image/jpeg" ||
@@ -705,28 +717,33 @@ async function resizeImage(doc, dataURI, { imageReductionFactor }) {
705
717
  });
706
718
  const width = image.naturalWidth / imageReductionFactor;
707
719
  const height = image.naturalHeight / imageReductionFactor;
720
+ const quality = imageQuality === undefined ? DEFAULT_IMAGE_QUALITY : Number(imageQuality);
708
721
  let blob;
709
722
  try {
710
723
  const canvas = new OffscreenCanvas(width, height);
711
724
  const context = canvas.getContext("2d");
712
725
  context.drawImage(image, 0, 0, width, height);
713
- blob = await canvas.convertToBlob({ type: contentType });
726
+ blob = await canvas.convertToBlob({ type: contentType, quality });
714
727
  } catch {
715
728
  const canvas = doc.createElement("canvas");
716
729
  canvas.width = width;
717
730
  canvas.height = height;
718
731
  const context = canvas.getContext("2d");
719
732
  context.drawImage(image, 0, 0, width, height);
720
- blob = await new Promise(resolve => canvas.toBlob(blob => {
733
+ blob = await new Promise((resolve, reject) => canvas.toBlob(blob => {
721
734
  if (blob) {
722
735
  resolve(blob);
723
736
  } else {
724
- throw new Error("Canvas toBlob failed");
737
+ reject(new Error("Canvas toBlob failed"));
725
738
  }
726
- }, contentType));
739
+ }, contentType, quality));
727
740
  }
728
741
  if (blob.type == contentType) {
729
742
  dataURI = await toDataURI(blob, contentType);
743
+ if (element && isUnsizedSVGImage(element)) {
744
+ element.setAttribute("width", image.naturalWidth);
745
+ element.setAttribute("height", image.naturalHeight);
746
+ }
730
747
  }
731
748
  } catch {
732
749
  // ignored
@@ -736,6 +753,17 @@ async function resizeImage(doc, dataURI, { imageReductionFactor }) {
736
753
  return dataURI;
737
754
  }
738
755
 
756
+ function isUnsizedSVGImage(element) {
757
+ return element.namespaceURI == SVG_NAMESPACE &&
758
+ element.localName == "image" &&
759
+ isAutoSize(element.getAttribute("width")) &&
760
+ isAutoSize(element.getAttribute("height"));
761
+ }
762
+
763
+ function isAutoSize(value) {
764
+ return value == null || value.trim().toLowerCase() == "auto";
765
+ }
766
+
739
767
  function toDataURI(content, contentType, charset) {
740
768
  const blob = content instanceof Blob ? content : new Blob([content], { type: (contentType || "") + (charset ? ";charset=" + charset : "") });
741
769
  return getDataURI(blob);
@@ -403,7 +403,7 @@ function getProcessorHelperClass(utilInstance) {
403
403
  }
404
404
  }
405
405
  if (options.imageReductionFactor > 1 && expectedType == "image") {
406
- content = await resizeImage(doc, content, options);
406
+ content = await resizeImage(doc, content, options, resourceElement);
407
407
  }
408
408
  if (removeElementIfMissing && this.testEmptyResource(content)) {
409
409
  resourceElement.remove();
@@ -470,7 +470,7 @@ function getProcessorHelperClass(utilInstance) {
470
470
  }
471
471
  }
472
472
  if (options.imageReductionFactor > 1 && expectedType == "image") {
473
- const dataURI = await resizeImage(doc, await toDataURI(new Blob([content], { type: contentType }), charset), options);
473
+ const dataURI = await resizeImage(doc, await toDataURI(new Blob([content], { type: contentType }), charset), options, resourceElement);
474
474
  content = (await util.getContent(dataURI, { asBinary: true })).data;
475
475
  }
476
476
  if (removeElementIfMissing && this.testEmptyResource(content)) {
@@ -643,7 +643,7 @@ function getRemovableSelectors(ruleData, removedSelectors, docContext) {
643
643
  const removedSet = new Set(removedSelectors);
644
644
  let keptSpecificity = { a: 0, b: 0, c: 0 };
645
645
  for (let selector = ruleData.prelude.children.head; selector; selector = selector.next) {
646
- if (!removedSet.has(selector)) {
646
+ if (!removedSet.has(selector) && !hasPseudoElement(selector.data)) {
647
647
  const { specificity } = docContext.selectorData.get(selector);
648
648
  if (compareSpecificities(specificity, keptSpecificity) > 0) {
649
649
  keptSpecificity = specificity;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.6.15",
3
+ "version": "1.6.17",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",