Skip to content

Instantly share code, notes, and snippets.

@szhu
Last active November 30, 2022 21:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save szhu/6791eb05ddbd9530d95cd13fdb04df7a to your computer and use it in GitHub Desktop.
Save szhu/6791eb05ddbd9530d95cd13fdb04df7a to your computer and use it in GitHub Desktop.
Makes it so that I can import Keynote/Pages/Numbers drawings into Figma.
// 1. Export the file to PDF.
//
// 2. Convert it to SVG:
//
// inkscape $file.pdf -o $file.svg --export-text-to-path
//
// Note: Don't use pdftocairo or pdf2svg; they don't work properly with the next steps.
//
// 3. Open the SVG file in your browser.
//
// open -a Safari $file.svg
//
// 4. Open the console and run the code below. It will copy the result.
//
// 5. Paste the same SVG file and save:
//
// pbpaste > $file.svg
//
// Note: Don't copy this command, otherwise it'll override your clipboard.
//
// 6. Drag the updated SVG into Figma.
function changeEl(el, newEl) {
// Based on https://stackoverflow.com/a/15086834
while (el.firstChild) {
newEl.append(el.firstChild);
}
for (let attr of el.attributes) {
newEl.setAttributeNode(attr.cloneNode());
}
el.parentNode.replaceChild(newEl, el);
return newEl;
}
function imageLoaded(img) {
return new Promise(resolve => {
img.onload = resolve;
if (img.complete) resolve();
});
}
function resolveCssUrlFragment(cssUrl) {
let result = document.querySelector(cssUrl.match(/url\((#.*)\)/)[1]);
return result;
}
function resolveUseOrImage(useOrImage) {
if (useOrImage.matches("use")) {
return document.querySelector(useOrImage.getAttributeNS("http://www.w3.org/1999/xlink", "href"))
} else {
return useOrImage;
}
}
async function flattenMask(maskedUseImage, copy) {
// For each masked <use> image:
// Get the URL of the masked image.
let maskedImage = resolveUseOrImage(maskedUseImage);
let maskedUrl = maskedImage.getAttributeNS("http://www.w3.org/1999/xlink", "href");
let maskedImg = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
maskedImg.setAttribute("src", maskedUrl);
await imageLoaded(maskedImg);
// Get the URL of the mask image.
let maskImage = resolveUseOrImage(resolveCssUrlFragment(maskedUseImage.getAttribute("mask")).firstElementChild);
let maskUrl = maskImage.getAttributeNS("http://www.w3.org/1999/xlink", "href");
let maskImg = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
maskImg.setAttribute("src", maskUrl);
await imageLoaded(maskImg);
// Create a canvas.
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
let context = canvas.getContext('2d');
// Draw the mask image onto the canvas.
canvas.width = maskImg.width;
canvas.height = maskImg.height;
context.drawImage(maskImg, 0, 0);
// // Invert the image.
// context.globalCompositeOperation = 'difference';
// context.fillStyle = 'white';
// context.fillRect(0, 0, canvas.width, canvas.height);
// Convert intensity to alpha.
// https://stackoverflow.com/a/40642855/782045
let pixels = context.getImageData(0, 0, canvas.width, canvas.height);
let pixels32 = new Uint32Array(pixels.data.buffer);
let i = 0, len = pixels32.length;
while(i < len) {
pixels32[i] = pixels32[i++] << 8; // shift blue channel into alpha (little-endian)
}
context.putImageData(pixels, 0, 0);
// Draw the masked image onto the canvas.
context.globalCompositeOperation = 'source-in';
context.drawImage(maskedImg, 0, 0);
// Get the data URL of the canvas.
let dataUrl = canvas.toDataURL("image/png");
// Set the new image as the masked image's href.
let newImage = changeEl(maskedUseImage, document.createElementNS("http://www.w3.org/2000/svg", "image"));
newImage.setAttributeNS("http://www.w3.org/1999/xlink", "href", dataUrl);
// Remove the mask attribute from the masked image.
newImage.removeAttribute("mask");
// Wait for the image to load.
await imageLoaded(newImage);
}
async function main(copy) {
for (let el of document.querySelectorAll("image[mask], use[mask]")) {
await flattenMask(el, copy)
}
for (let el of document.querySelectorAll(`path[d=""]`)) {
console.log("Removing:", el);
el.remove()
}
for (let el of document.querySelectorAll("symbol")) {
if (el.childElementCount === 0) {
console.log("Removing:", el);
el.remove()
}
}
copy(document.documentElement.outerHTML)
}
main(copy).then(
() => console.log("Done"),
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment