robotools / fontParts

The replacement for RoboFab
MIT License
135 stars 44 forks source link

Collaboration #35

Closed schriftgestalt closed 8 years ago

schriftgestalt commented 8 years ago

I just found out about this project.

The purpose of fontParts is to make it easier to implement the APIs in other environments, I would have though that developers of potential other environments would have been involved on setting up the API. If the API is developed around a particulary implementation it makes it quite a bit more difficult to support it everywhere else.

benkiel commented 8 years ago

@schriftgestalt Sorry that you feel that way; there has been much discussion (starting with Tal's talk about needing to rewrite roboFab at the last RoboThon, in the robofab repository, https://github.com/robofab-developers/robofab/pull/63#issuecomment-191914848, https://github.com/robofab-developers/robofab/issues/51#issuecomment-197454018, https://github.com/robofab-developers/robofab/issues/51#issuecomment-197454018, and in other places — https://github.com/googlei18n/fontmake/issues/38#issuecomment-191736388) and involvement around fontParts for a bit — this hasn't been hidden. Also things are still very much in the infancy / this is still in the sketching stage, so there is plenty of time for input.

A main goal with fontParts is to keep the same (as much as possible) API as roboFab, make it easier for all to implement, and to get rid of a lot of the things that make roboFab very hard to maintain across multiple environments (interface things, FontLab specific behavior, 10+ years of legacy code, etc).

benkiel commented 8 years ago

I should also be clear, the goal with fontParts to to keep, as much as is possible, the same API from roboFab that everyone has invested 10+ years in writing scripts for — in that sense the API is set and isn't new. Decoupling it from the FontLab specific behavior (when roboFab was written, that's all there was...) and cleaning it up to work with UFO3 is new. I know that you've had issues with getting roboFab and Glyphs to play nicely — your thoughts on how to make that much easier would be very welcome.

typesupply commented 8 years ago

The purpose of fontParts is to make it easier to implement the APIs in other environments, I would have though that developers of potential other environments would have been involved on setting up the API. If the API is developed around a particulary implementation it makes it quite a bit more difficult to support it everywhere else.

Ouch. Have you looked at the documentation, especially the readme and this part? Anyway, this is not an official thing yet. I haven't even announced that I'm working on it anywhere other than a few GitHub issues. It's a test that I started based on the various discussions that have been going on for years. RoboFab is stuck with legacy issues and the infrastructure can't be rebuilt in a way that addresses those without breaking lots of stuff. So, the idea has been to take the RoboFab API, remove the stuff that wasn't needed (I alone added a ton of cruft in the very beginning) and publish the API. An environment can implement their own version of this public API in whatever way that they want. Someone could write a Python wrapper around Fortran code that calls commandline tools written in ABC that deal with outlines stored in .bez files. The main idea of fontParts is that it is the API not an implementation.

I started this project with a dump of the public RoboFab API and edited it. This was just a list of class and method names. No implementation code. I wanted to make this self documenting, so I put it into some Python objects that can be autodocumented with Sphinx. It's kind of magical. This became abstract code that is no way dependent on any implementation. This is the source of the public, scripter-facing API documentation. Inside of that abstract API is some top-level code for generic actions like interpolation. All of this will be documented at some point (maybe at an upcoming sprint) so that implementors know what they need to do. This still is not dependent on any implementation. It uses fontTools, fontMath and maybe a few others, but everything that is used is vetted for dependencies. And, again, an implementator isn't required to use any of this code.

As I was building this, I also tried to make it very easy for an environment to have it's own implementation with minimal effort. I know what a pain that was in RoboFab and I don't want to repeat it. So, the abstract implementation is subclassable. All of the top-level code (like interpolation) calls a minimal number of private methods. An environment that doesn't want to implement the whole public API can subclass the abstract API and define the necessary private methods. Then, they have full fontParts support with way, way, way, way * 10000000 less code than it took do the same in RoboFab. Also, the top-level code validates input and output so that the environment implementations don't have to do any type of sanity checking.

To test the viability of the idea, I have been implementing a NoneLab version of this in parallel. That is a completely optional part of this module. It may be a part of fontParts in the end, but it will never be required of implementations.

As far as the API goes, the first goal has been to get the RoboFab API working. There are things that RoboFab didn't handle that I think we should add, such as undo. That'll be phase 2. If you have suggestions for that, please let us know via a GitHub issue or the RoboFab mailing list.

schriftgestalt commented 8 years ago

@typesupply I did understand it from the readme. But there are already some structural and very central changes that will make it more difficult for me. That is the support for layers. I saw how you implemented it in ufo3 and it is the same here now. The structure in ufo3 and fontParts is font > layers > glyph. That might work if you like to store a background layer. But what if you have arbitrary layers (e.g., to store a version of the outline). Lets say you have a font with 2000 glyphs. Each glyph might have two extra layers (each with a unique name). That means you have 4000 layers with one glyph each. To show all glyphs that belong to the current glyph, you need to go through all 4000 layers and check if it has a glyph that corresponds to the current glyph. Or do I miss something here.

If you turn it around: font > glyph > layers (with glyphs.layers being a dict with keys to access the layers we just need to decide on some default keys ('background')) all drawings that belong to one logical unit are in one place.

typesupply commented 8 years ago

Or do I miss something here.

Yes.

schriftgestalt commented 8 years ago

Good. But that is not clear in the class documentation. It only explains the font level layers.

And can someone explain the advantages of font level layers (implementation/performance and structurally)? You mention chromatic fonts. But I would consider that an edge case and even they will have a lot empty layers.

typesupply commented 8 years ago

Good. But that is not clear in the class documentation. It only explains the font level layers.

Can you point to the line of code or portion of the documentation where this is the case? Here's the API glyph.layer documentation. There are probably places where there are old notes from when I was thinking all of this through. That may be what you are seeing. Anyway, did I mention that this is all a work in progress? fontParts is a currently incomplete experiment, especially incomplete in the documentation. I'm actually surprised that anyone is even paying attention to it.

And can someone explain the advantages of font level layers (implementation/performance and structurally)? You mention chromatic fonts. But I would consider that an edge case and even they will have a lot empty layers.

I don't want to get into that that debate again. Fonts with global layers exist and have existed for a very long time. Glyph level layers are cool, too. fontParts supports both. People have different opinions. təˈmeɪtəʊ - təˈmɑːtəʊ 🔨🔨🔨🔨💀🐴 No need to start a fight. Let's all just get along. fontParts doesn't say that one is better than the other and works hard to make the distinction invisible to scripters. If you want to use the abstract API classes, you can implement these things however you want. See all the NotImplementedErrors raised in the layer retrieval methods.

https://github.com/robofab-developers/fontParts/blob/master/Lib/fontParts/base/font.py#L483 https://github.com/robofab-developers/fontParts/blob/master/Lib/fontParts/base/glyph.py#L1721 https://github.com/robofab-developers/fontParts/blob/master/Lib/fontParts/base/glyph.py#L1756

schriftgestalt commented 8 years ago

No need to start a fight

I didn’t like to start a fight. I just like to see some arguments to better understand the matter.

Sorry for not understanding the documentation. I didn't see that there is a glyph.layers, only layer.glyphs. It takes a moment to understand something like this. Again, I didn't mean to be rude, if I was, I apologize.

To avoid confusion like this, there might be a small section in the main sidebar above the Layer entry that explains this.