Last active
November 30, 2022 21:43
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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