Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Font editor perspectives #21

Open
justvanrossum opened this issue May 18, 2022 · 11 comments
Open

Font editor perspectives #21

justvanrossum opened this issue May 18, 2022 · 11 comments

Comments

@justvanrossum
Copy link

justvanrossum commented May 18, 2022

I just spoke to @chrissimpkins, and shared some loose thoughts about "faster font compilation" in the context of a font editor, and he suggested that I post here, as you are talking about use cases.

(I follow this repo only superficially, as the details of Rust table implementation strategies are beyond my expertise.)

Premise: A font editor would benefit from showing the user correctly shaped live previews.

FontGoggles attempts to do this as quickly as possible when previewing UFOs, using the following strategy:

  • It scrapes all unicode and anchor information from the .glif files as fast as is possible with Python
  • That information and the kerning and .fea data available is presented to ufo2ft to write the final .fea data
  • feaLib is used to compile that into GSUB/GPOS/GDEF
  • A "sparse" font (a TTF with the bare minimum of tables, ie. no outlines) is presented to HarfBuzz, which can then shape the user-entered string correctly
  • It watches for changes in individual .glif files in the UFO, and updates its internal cmap and anchor data incrementally, so it won't have to re-scrape ALL unicode + anchor data when a single glyph changes.

While this is a lot faster than compiling a full font with fontmake, there are still two bottlenecks:

  • The scraping of unicode + anchor data from .glif files when opening a UFO
  • The compilation of .fea into binary tables when opening a UFO, and when updating according to changes in the UFO

So: specialized (Rust-based?) tools for those two tasks would be incredibly useful.

For a font editor with in-memory data, things will look a little different:

  • cmap and anchor data can be made quickly available
  • some GSUB features may be generated on the fly, using glyph name conventions
  • kerning data will be live

With the currently available tools, we have to go via .fea to get data that HarfBuzz can use. This gives us a double bottle-neck:

  • Writing .fea data from live data
  • Compiling binary tables from .fea

My final thought here is that a superfast shaper isn't a requirement, but a shaper that can more efficiently work from live data (for example kerning and anchors) is what's really needed.

Using Rust-based tools will surely make many things much faster, but to me there is possibly more to gain with a clever (I know I'm hand-waving) system that can shape more directly from font/glyph source data, looking only at the data that is relevant for the string being displayed, without having to go via TWO intermediate formats (.fea and OTL tables).

The other thing that could be beneficial for a font editor is incremental compilation to a full font: when I edit a single glyph of a single master of a 50,000-glyph variable CJK font, it shouldn't require recompilation of the whole thing.

@cmyr
Copy link
Member

cmyr commented May 19, 2022

So this is basically the original motivation that set me down on this path, and remains one of the major goals. I have other dreams (like a step-through debugger for shaping, where it can show each rule that is applied, and its source) but I am definitely keeping this case in mind.

Incremental compilation is also definitely something nice to have; ideally that can be a layer on top of the general compilation tools.

@justvanrossum
Copy link
Author

justvanrossum commented May 19, 2022

If providing fast interactive shaping is a goal, then good Rust ⟷ Other Languages interop should be part of the goals, too.

Rust is awesome for specific low level fastness, but not so much for high level orchestrating of complex processes, where people with lower CompSci skills still want to (and deserve to be able to) customize.

@madig
Copy link

madig commented May 19, 2022

(@cmyr regarding a step-by-step non-interactive shaper, see http://corvelsoftware.co.uk/crowbar/)

@adrientetar
Copy link

adrientetar commented Jan 25, 2023

One other topic when it comes to font editors is efficient interoperability, since they might be written in different languages. e.g. do I need to write a file to disk to feed to the compiler, or can I provide the input data over pipes, or use a C API?

@rsheeter
Copy link
Contributor

do I need to write a file to disk to feed to the compiler

At time of writing, yes.

can I provide the input data over pipes, or use a C API?

Not yet, but I don't see why not. Wrt pipes, what would you expect to pipe? - a standalone .glyphs file makes sense but UFO+designspace doesn't seem to lend itself as well to piping. Or perhaps you had some other input in mind?

@rsheeter
Copy link
Contributor

Or file an issue against https://github.com/googlefonts/fontmake-rs explaining what you'd like to do?

@schriftgestalt
Copy link

adding my 2 cents:

There are two use cases:

  • Plain exporting the full font to get a finished .otf/ttf.
  • partially building some structures for different purposes. e.g. text preview, validating fea code.

The two most expensive operations I encounter in Glyphs:

  • writing .fea code (manually written and automatically enraged (kern, mark …) and parsing it in again.
  • building and compacting GPOS/GSUB tables

The current state of external compilers (like fontc or fontmake) adds the parsing and translation of the source file.

One solution to cut down most of the above could be to expose the internal representation (by an API/callbacks) that the editor app could build up data (instead of the source parser). So the editor tells the compiler: "Here are some outlines", "Here is a list of kerning pairs", "Here is another kerning pair", "Here is some fea-code" …

That could be done by a direct c style API or by pipes (potentially slower? More error prone?)

We are in the works for feaKit: it will get an API that lets me give it some fea code (a lookup or a feature) one at a time or, more importantly lets me write the GPOS lookups directly into the internal representation. That has two big advantages. It is much faster and, if done right lets me incrementally update things (e.g. a changed kerning pair).

And one important benefit: it can give much more precise errors. Because the context were that data came from is much clearer.

@rsheeter
Copy link
Contributor

One solution to cut down most of the above could be to expose the internal representation

An in memory translation to what fontc reads .glyphs files into (here) should be pretty quick, no need to touch disk or parse a source file.

feaKit [...] lets me write the GPOS lookups directly into the internal representation

Ha, us too. fea-rs is learning this trick so fontc can avoid generating strings to parse.

@anthrotype
Copy link
Member

An in memory translation to what fontc reads .glyphs files into (here)

IIUC, Georg was referring to the fontc IR (fontir/src/ir.rs) proper, not the glyph-reader/font.rs that we parse the plist into. So basically the font editor would do the job of glyphs2fontir themselves, skipping not just the plist parsing but the glyphs=>IR itself and somehow produce IR directly to feed fontc

@rsheeter
Copy link
Contributor

IIUC, Georg was referring to the fontc IR (fontir/src/ir.rs) proper, not the glyph-reader/font.rs that we parse the plist into

That was my understanding also but I think that bypasses things we do want to happen.

@schriftgestalt
Copy link

We should be able to find a level where it makes sense to hand over the data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants