Skip to content

Commit

Permalink
Merge pull request #1698 from googlefonts/export-as
Browse files Browse the repository at this point in the history
Add some infra for an "Export as" feature
  • Loading branch information
justvanrossum authored Oct 8, 2024
2 parents c03d5cd + e6fe32c commit 2eddbeb
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 18 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,9 @@ The following list of features is not complete, but gives you a rough idea of wh

### Export

- FontMake/Fontations integration
- Incremental compilation using Fontations
- FontMake integration (in Fontra Pak)
- `fontc` integration
- Incremental compilation using `fontc`
- Experimental [`glyf1`](https://github.com/harfbuzz/boring-expansion-spec/blob/main/glyf1-varComposites.md) export ✅ (Via [fontra-compile](https://github.com/googlefonts/fontra-compile))

### Collaborative features
Expand Down
4 changes: 4 additions & 0 deletions src/fontra/client/core/font-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,10 @@ export class FontController {
mapMappedSourceLocationToSourceLocation(mappedSourceLocation) {
return { ...this.crossAxisMapping.unmapLocation(mappedSourceLocation) };
}

async exportAs(options) {
return await this.font.exportAs(options);
}
}

export function reverseUndoRecord(undoRecord) {
Expand Down
8 changes: 8 additions & 0 deletions src/fontra/client/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"action-topics.designspace-navigation": "Designspace Navigation",
"action-topics.export-as": "Export as",
"action-topics.glyph-editor-appearance": "Glyph editor appearance",
"action-topics.menu.edit": "Edit",
"action-topics.menu.view": "View",
Expand All @@ -19,6 +20,12 @@
"action.delete": "Delete",
"action.delete-glyph": "Delete Glyph",
"action.delete-selection": "Delete Selection",
"action.export-as.designspace": "Designspace + UFO (*.designspace)",
"action.export-as.fontra": "Fontra (*.fontra)",
"action.export-as.otf": "OpenType (*.otf)",
"action.export-as.rcjk": "RCJK (*.rcjk)",
"action.export-as.ttf": "TrueType (*.ttf)",
"action.export-as.ufo": "UFO (*.ufo)",
"action.join-contours": "Join Contours",
"action.lock-guidelines": "Lock Guidelines",
"action.paste": "Paste",
Expand Down Expand Up @@ -110,6 +117,7 @@
"menubar.edit": "Edit",
"menubar.extensions": "Extensions",
"menubar.file": "File",
"menubar.file.export-as": "Export as",
"menubar.file.new": "New...",
"menubar.file.open": "Open...",
"menubar.font": "Font",
Expand Down
8 changes: 8 additions & 0 deletions src/fontra/client/lang/zh-CN.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"action-topics.designspace-navigation": "Designspace 导航",
"action-topics.export-as": "Export as",
"action-topics.glyph-editor-appearance": "字符形编辑器外观",
"action-topics.menu.edit": "编辑",
"action-topics.menu.view": "视图",
Expand All @@ -18,6 +19,12 @@
"action.delete": "Delete",
"action.delete-glyph": "删除字符形",
"action.delete-selection": "删除选中",
"action.export-as.designspace": "Designspace + UFO (*.designspace)",
"action.export-as.fontra": "Fontra (*.fontra)",
"action.export-as.otf": "OpenType (*.otf)",
"action.export-as.rcjk": "RCJK (*.rcjk)",
"action.export-as.ttf": "TrueType (*.ttf)",
"action.export-as.ufo": "UFO (*.ufo)",
"action.join-contours": "Join Contours",
"action.lock-guidelines": "Lock Guidelines",
"action.paste": "Paste",
Expand Down Expand Up @@ -110,6 +117,7 @@
"menubar.edit": "编辑",
"menubar.extensions": "扩展",
"menubar.file": "文件",
"menubar.file.export-as": "Export as",
"menubar.file.new": "新建……",
"menubar.file.open": "打开……",
"menubar.font": "字体",
Expand Down
28 changes: 26 additions & 2 deletions src/fontra/core/fonthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
)
from .classes import Font, FontInfo, FontSource, VariableGlyph
from .lrucache import LRUCache
from .protocols import ReadableFontBackend, WatchableFontBackend, WritableFontBackend
from .protocols import (
ProjectManager,
ReadableFontBackend,
WatchableFontBackend,
WritableFontBackend,
)

logger = logging.getLogger(__name__)

Expand All @@ -41,6 +46,8 @@ class FontHandler:
readOnly: bool = False
dummyEditor: bool = False # allow editing in read-only mode, don't write to backend
allConnectionsClosedCallback: Optional[Callable[[], Awaitable[Any]]] = None
projectManager: ProjectManager | None = None
projectIdentifier: str | None = None

def __post_init__(self):
if self.writableBackend is None:
Expand Down Expand Up @@ -160,7 +167,19 @@ async def getBackEndInfo(self, *, connection=None) -> dict:
("find-glyphs-that-use-glyph", "findGlyphsThatUseGlyph")
]:
features[key] = hasattr(self.backend, methodName)
return dict(name=self.backend.__class__.__name__, features=features)
projectManagerFeatures = {}
for key, methodName in [("export-as", "exportAs")]:
projectManagerFeatures[key] = hasattr(self.projectManager, methodName)
return dict(
name=self.backend.__class__.__name__,
features=features,
projectManagerName=(
None
if self.projectManager is None
else self.projectManager.__class__.__name__
),
projectManagerFeatures=projectManagerFeatures,
)

@remoteMethod
async def getGlyph(
Expand Down Expand Up @@ -485,6 +504,11 @@ def _getCombinedSubscribePattern(self, connection):
]
return patternUnion(patternA, patternB)

@remoteMethod
async def exportAs(self, options: dict, *, connection):
if self.projectManager is not None and hasattr(self.projectManager, "exportAs"):
return await self.projectManager.exportAs(self, options)


def popFirstItem(d):
key = next(iter(d))
Expand Down
4 changes: 3 additions & 1 deletion src/fontra/filesystem/projectmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pathlib
from importlib import resources
from importlib.metadata import entry_points
from os import PathLike
from os import PathLike, fspath
from types import SimpleNamespace
from typing import Callable

Expand Down Expand Up @@ -111,6 +111,8 @@ async def closeFontHandler():
backend,
readOnly=self.readOnly,
allConnectionsClosedCallback=closeFontHandler,
projectManager=self,
projectIdentifier=fspath(projectPath),
)
await fontHandler.startTasks()
self.fontHandlers[path] = fontHandler
Expand Down
56 changes: 43 additions & 13 deletions src/fontra/views/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ const MIN_CANVAS_SPACE = 200;
const PASTE_BEHAVIOR_REPLACE = "replace";
const PASTE_BEHAVIOR_ADD = "add";

const EXPORT_FORMATS = ["ttf", "otf", "fontra", "designspace", "ufo", "rcjk"];

export class EditorController {
static async fromWebSocket() {
const pathItems = window.location.pathname.split("/").slice(3);
Expand Down Expand Up @@ -645,6 +647,20 @@ export class EditorController {
}
}

initActionsAfterStart() {
if (this.fontController.backendInfo.projectManagerFeatures["export-as"]) {
for (const format of EXPORT_FORMATS) {
registerAction(
`action.export-as.${format}`,
{
topic: "0035-action-topics.export-as",
},
(event) => this.fontController.exportAs({ format })
);
}
}
}

initTopBar() {
const menuBar = new MenuBar([
{
Expand Down Expand Up @@ -673,19 +689,31 @@ export class EditorController {
},
{
title: translate("menubar.file"),
getItems() {
return [
{
title: translate("menubar.file.new"),
enabled: () => false,
callback: () => {},
},
{
title: translate("menubar.file.open"),
enabled: () => false,
callback: () => {},
},
];
getItems: () => {
if (this.fontController.backendInfo.projectManagerFeatures["export-as"]) {
return [
{
title: translate("menubar.file.export-as"),
getItems: () =>
EXPORT_FORMATS.map((format) => ({
actionIdentifier: `action.export-as.${format}`,
})),
},
];
} else {
return [
{
title: translate("menubar.file.new"),
enabled: () => false,
callback: () => {},
},
{
title: translate("menubar.file.open"),
enabled: () => false,
callback: () => {},
},
];
}
},
},
{
Expand Down Expand Up @@ -910,6 +938,8 @@ export class EditorController {
}
);

this.initActionsAfterStart();

// Delay a tiny amount to account for a delay in the sidebars being set up,
// which affects the available viewBox
setTimeout(() => this.setupFromWindowLocation(), 20);
Expand Down

0 comments on commit 2eddbeb

Please sign in to comment.