diff --git a/src/fontra/client/core/utils.js b/src/fontra/client/core/utils.js index ab098b63e..fd2f3ffca 100644 --- a/src/fontra/client/core/utils.js +++ b/src/fontra/client/core/utils.js @@ -279,12 +279,12 @@ export async function readClipboardTypes() { return clipboardTypes; } -export async function readFromClipboard(type) { +export async function readFromClipboard(type, plainText = true) { const clipboardContents = await navigator.clipboard.read(); for (const item of clipboardContents) { if (item.types.includes(type)) { const blob = await item.getType(type); - return await blob.text(); + return plainText ? await blob.text() : blob; } } return undefined; diff --git a/src/fontra/views/editor/editor.js b/src/fontra/views/editor/editor.js index f79174994..b87eacef6 100644 --- a/src/fontra/views/editor/editor.js +++ b/src/fontra/views/editor/editor.js @@ -2017,6 +2017,7 @@ export class EditorController { let { pasteVarGlyph, pasteLayerGlyphs, backgroundImageData } = await this._unpackClipboard(); if (!pasteVarGlyph && !pasteLayerGlyphs?.length) { + await this._pasteClipboardImage(); return; } @@ -2106,7 +2107,7 @@ export class EditorController { _makeBackgroundImageIdentifierMapping(backgroundImageData) { if (!backgroundImageData || isObjectEmpty(backgroundImageData)) { - return null; + return {}; } const mapping = {}; for (const originalImageIdentifier of Object.keys(backgroundImageData)) { @@ -2127,6 +2128,9 @@ export class EditorController { } async _writeBackgroundImageData(backgroundImageData, identifierMapping) { + if (!backgroundImageData) { + return; + } for (const [imageIdentifier, imageData] of Object.entries(backgroundImageData)) { const mappedIdentifier = identifierMapping[imageIdentifier] || imageIdentifier; await this.fontController.putBackgroundImageData(mappedIdentifier, imageData); @@ -2178,11 +2182,60 @@ export class EditorController { console.log("couldn't paste from JSON:", error.toString()); } } else { - pasteLayerGlyphs = [{ glyph: await this.parseClipboard(plainText) }]; + const glyph = await this.parseClipboard(plainText); + if (glyph) { + pasteLayerGlyphs = [{ glyph }]; + } } return { pasteVarGlyph, pasteLayerGlyphs, backgroundImageData }; } + async _pasteClipboardImage() { + if (!this.sceneSettings.selectedGlyph?.isEditing) { + return; + } + + const imageBlob = + (await readFromClipboard("image/png", false)) || + (await readFromClipboard("image/jpeg", false)); + + if (!imageBlob) { + return; + } + + const reader = new FileReader(); + const resultPromise = new Promise((resolve, reject) => { + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + }); + + reader.readAsDataURL(imageBlob); + + const dataURL = await resultPromise; + if (!dataURL) { + return; + } + + const imageIdentifiers = []; + + await this.sceneController.editLayersAndRecordChanges((layerGlyphs) => { + for (const layerGlyph of Object.values(layerGlyphs)) { + const imageIdentifier = crypto.randomUUID(); + layerGlyph.backgroundImage = { + identifier: imageIdentifier, + transformation: getDecomposedIdentity(), + }; + imageIdentifiers.push(imageIdentifier); + } + this.sceneController.selection = new Set(["backgroundImage/0"]); + return "paste background image"; // TODO: translate + }); + + for (const imageIdentifier of imageIdentifiers) { + await this.fontController.putBackgroundImageData(imageIdentifier, dataURL); + } + } + async _pasteReplaceGlyph(varGlyph) { await this.sceneController.editGlyphAndRecordChanges( (glyph) => {