Skip to content

Commit

Permalink
Merge pull request #1808 from googlefonts/bg-image-drop-file-issue1807
Browse files Browse the repository at this point in the history
[background image] Allow a PNG or JPEG file to be dropped onto a glyph in edit mode
  • Loading branch information
justvanrossum authored Nov 18, 2024
2 parents cb0d695 + e05b3e6 commit 7418eca
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 23 deletions.
9 changes: 6 additions & 3 deletions src/fontra/client/core/glyph-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1134,9 +1134,12 @@ function ensureGlyphCompatibility(layerGlyphs, glyphDependencies) {
}

function stripNonInterpolatables(glyph) {
if (!glyph.components.length && !glyph.guidelines.length && !glyph.backgroundImage) {
return glyph;
}
// Hm, the following optimization oddly causes a false positive when undoing a bg img
// placement. TODO: figure out what's going on.
// if (!glyph.components.length && !glyph.guidelines.length && !glyph.backgroundImage) {
// console.log("have bg img?", !!glyph.backgroundImage);
// return glyph;
// }
return StaticGlyph.fromObject(
{
...glyph,
Expand Down
4 changes: 4 additions & 0 deletions src/fontra/views/editor/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ body {
outline: none;
}

#edit-canvas.dropping-files {
background-color: #99556655;
}

.cleanable-overlay.overlay-layer-hidden {
display: none;
}
Expand Down
117 changes: 97 additions & 20 deletions src/fontra/views/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ export class EditorController {
const canvas = document.querySelector("#edit-canvas");
canvas.focus();

canvas.ondragenter = (event) => this._onDragEnter(event);
canvas.ondragover = (event) => this._onDragOver(event);
canvas.ondragleave = (event) => this._onDragLeave(event);
canvas.ondrop = (event) => this._onDrop(event);

const canvasController = new CanvasController(canvas, (magnification) =>
this.canvasMagnificationChanged(magnification)
);
Expand Down Expand Up @@ -404,21 +409,21 @@ export class EditorController {
"action.add-component",
{ topic },
() => this.doAddComponent(),
() => this.canAddComponent()
() => this.canEditGlyph()
);

registerAction(
"action.add-anchor",
{ topic },
() => this.doAddAnchor(),
() => this.canAddAnchor()
() => this.canEditGlyph()
);

registerAction(
"action.add-guideline",
{ topic },
() => this.doAddGuideline(),
() => this.canAddGuideline()
() => this.canEditGlyph()
);

registerAction(
Expand Down Expand Up @@ -2105,10 +2110,12 @@ export class EditorController {
await this._pasteLayerGlyphs(pasteLayerGlyphs);
}

await this._writeBackgroundImageData(
backgroundImageData,
backgroundImageIdentifierMapping
);
if (this.fontController.backendInfo.features["background-image"]) {
await this._writeBackgroundImageData(
backgroundImageData,
backgroundImageIdentifierMapping
);
}
}

_makeBackgroundImageIdentifierMapping(backgroundImageData) {
Expand Down Expand Up @@ -2141,6 +2148,8 @@ export class EditorController {
const mappedIdentifier = identifierMapping[imageIdentifier] || imageIdentifier;
await this.fontController.putBackgroundImageData(mappedIdentifier, imageData);
}
// Writing the background image data does not cause a refresh
this.canvasController.requestUpdate();
}

async _unpackClipboard() {
Expand Down Expand Up @@ -2197,7 +2206,7 @@ export class EditorController {
}

async _pasteClipboardImage() {
if (!this.sceneSettings.selectedGlyph?.isEditing) {
if (!this.canPlaceBackgroundImage()) {
return;
}

Expand All @@ -2222,6 +2231,10 @@ export class EditorController {
return;
}

await this._placeBackgroundImage(dataURL);
}

async _placeBackgroundImage(dataURL) {
// Ensure background images are visible and not locked
this.visualizationLayersSettings.model["fontra.background-image"] = true;
this.sceneSettings.backgroundImagesAreLocked = false;
Expand All @@ -2239,12 +2252,14 @@ export class EditorController {
imageIdentifiers.push(imageIdentifier);
}
this.sceneController.selection = new Set(["backgroundImage/0"]);
return "paste background image"; // TODO: translate
return "place background image"; // TODO: translate
});

for (const imageIdentifier of imageIdentifiers) {
await this.fontController.putBackgroundImageData(imageIdentifier, dataURL);
}
// Writing the background image data does not cause a refresh
this.canvasController.requestUpdate();
}

async _pasteReplaceGlyph(varGlyph) {
Expand Down Expand Up @@ -2448,10 +2463,6 @@ export class EditorController {
});
}

canAddComponent() {
return this.sceneModel.getSelectedPositionedGlyph()?.glyph.canEdit;
}

async doAddComponent() {
const glyphName = await this.runGlyphSearchDialog(
translate("action.add-component"),
Expand Down Expand Up @@ -2485,10 +2496,6 @@ export class EditorController {
});
}

canAddAnchor() {
return this.sceneModel.getSelectedPositionedGlyph()?.glyph.canEdit;
}

async doAddAnchor() {
const point = this.sceneController.selectedGlyphPoint(this.contextMenuPosition);
const { anchor: tempAnchor } = await this.doAddEditAnchorDialog(undefined, point);
Expand Down Expand Up @@ -2733,9 +2740,6 @@ export class EditorController {
// TODO: We may want to make a more general code for adding and editing
// so we can handle both anchors and guidelines with the same code
// Guidelines
canAddGuideline() {
return this.sceneModel.getSelectedPositionedGlyph()?.glyph.canEdit;
}

async doAddGuideline(global = false) {
this.visualizationLayersSettings.model["fontra.guidelines"] = true;
Expand Down Expand Up @@ -3657,6 +3661,79 @@ export class EditorController {
);
location.reload();
}

canPlaceBackgroundImage() {
return (
this.fontController.backendInfo.features["background-image"] &&
this.canEditGlyph()
);
}

canEditGlyph() {
const positionedGlyph = this.sceneModel.getSelectedPositionedGlyph();
return !!(
positionedGlyph &&
!this.fontController.readOnly &&
!this.sceneModel.isSelectedGlyphLocked() &&
positionedGlyph.glyph.canEdit
);
}

// Drop files onto canvas

_onDragEnter(event) {
event.preventDefault();
if (!this.canPlaceBackgroundImage()) {
return;
}
this.canvasController.canvas.classList.add("dropping-files");
}

_onDragOver(event) {
event.preventDefault();
if (!this.canPlaceBackgroundImage()) {
return;
}
this.canvasController.canvas.classList.add("dropping-files");
}

_onDragLeave(event) {
event.preventDefault();
if (!this.canPlaceBackgroundImage()) {
return;
}
this.canvasController.canvas.classList.remove("dropping-files");
}

_onDrop(event) {
event.preventDefault();
if (!this.canPlaceBackgroundImage()) {
return;
}
this.canvasController.canvas.classList.remove("dropping-files");

const items = [];

for (const item of event.dataTransfer?.files || []) {
const suffix = item.name.split(".").at(-1);
if (suffix === "png" || suffix === "jpg" || suffix === "jpeg") {
items.push(item);
}
}

if (items.length != 1) {
/* no real need for await */ dialog(
"Can't drop files",
"Please drop a single .png, .jpg or .jpeg file",
[{ title: translate("dialog.okay"), resultValue: "ok", isDefaultButton: true }]
);
return;
}
const item = items[0];
const reader = new FileReader();
reader.onload = (event) => this._placeBackgroundImage(reader.result);
reader.readAsDataURL(item);
}
}

function clearSearchParams(searchParams) {
Expand Down

0 comments on commit 7418eca

Please sign in to comment.