diff --git a/static/js/markdown.js b/static/js/markdown.js index 78d4656..8729b0e 100644 --- a/static/js/markdown.js +++ b/static/js/markdown.js @@ -13,37 +13,70 @@ const colorToGrayscale = (color) => { }; /** - * Extract color using canvas2d - * @param {HTMLImageElement} image Image source - * @returns Colors represeting the image + * Extract colors from an image or SVG element using canvas + * @param {HTMLImageElement | SVGSVGElement} element Image or SVG source element + * @returns {Promise} Colors representing the element */ -const extractColors = (image) => { - const canvas = document.createElement("canvas"); - canvas.width = image.naturalWidth || image.width; - canvas.height = image.naturalHeight || image.height; - const ctx = canvas.getContext("2d"); - ctx.drawImage(image, 0, 0); +const extractColors = (element) => { + return new Promise((resolve, reject) => { + // Create a canvas with the same dimensions as the element + const canvas = document.createElement("canvas"); + let imageSource; - const imageData = ctx.getImageData( - 0, - 0, - Math.max(1, canvas.width), - Math.max(1, canvas.height) - ); - const pixelData = imageData.data; + if (element instanceof SVGSVGElement) { + // SVG element handling + canvas.width = element.viewBox.baseVal.width; + canvas.height = element.viewBox.baseVal.height; - const colors = []; - for (let i = 0; i < pixelData.length; i += 4) { - if (pixelData[i + 3] > 0) { - colors.push({ - r: pixelData[i], - g: pixelData[i + 1], - b: pixelData[i + 2], - }); + const svgString = new XMLSerializer().serializeToString(element); + const svgBlob = new Blob([svgString], { type: "image/svg+xml" }); + const svgUrl = URL.createObjectURL(svgBlob); + + imageSource = new Image(); + imageSource.src = svgUrl; + + imageSource.onload = () => { + processImage(imageSource); + URL.revokeObjectURL(svgUrl); + }; + + imageSource.onerror = (error) => { + URL.revokeObjectURL(svgUrl); + reject(error); + }; + } else { + // Regular image handling + canvas.width = element.naturalWidth || element.width; + canvas.height = element.naturalHeight || element.height; + processImage(element); } - } - return colors; + const processImage = (img) => { + const ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + + const imageData = ctx.getImageData( + 0, + 0, + Math.max(1, canvas.width), + Math.max(1, canvas.height) + ); + const pixelData = imageData.data; + + const colors = []; + for (let i = 0; i < pixelData.length; i += 4) { + if (pixelData[i + 3] > 0) { + colors.push({ + r: pixelData[i], + g: pixelData[i + 1], + b: pixelData[i + 2], + }); + } + } + + resolve(colors); + }; + }); }; /** @@ -51,28 +84,31 @@ const extractColors = (image) => { * @param {Mode} mode */ const changeTheme = (mode) => { - for (const item of document.getElementsByTagName("svg")) { - const image = document.createElement("img"); - image.src = `data:image/svg+xml;base64,${btoa( - new XMLSerializer().serializeToString(item) - )}`; - - image.width = item.viewBox.baseVal.width; - image.height = item.viewBox.baseVal.height; - - image.onload = () => { - applyFilter(mode, item, extractColors(image)); - }; + // Process SVG elements + for (const svgElement of document.getElementsByTagName("svg")) { + extractColors(svgElement) + .then((colors) => { + applyFilter(mode, svgElement, colors); + }) + .catch((error) => { + console.error("Error extracting colors:", error); + }); } - // SVG embedded in images as base64 + // Process SVG images embedded as base64 for (const item of document.getElementsByTagName("img")) { if (!item.src.startsWith("data:image/svg+xml;base64,")) { - // Exclude image who aren't SVG and base64 encoded + // Exclude images that aren't SVG and base64 encoded continue; } - applyFilter(mode, item, extractColors(item)); + extractColors(item) + .then((colors) => { + applyFilter(mode, item, colors); + }) + .catch((error) => { + console.error("Error extracting colors:", error); + }); } };