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
|
-
|
|
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;
|