-
Notifications
You must be signed in to change notification settings - Fork 16
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
Add COLR/CPAL support #41
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,7 @@ def extractFontFromOpenType( | |
doKerning=True, | ||
customFunctions=[], | ||
doInstructions=True, | ||
doColors=True, | ||
): | ||
source = TTFont(pathOrFile) | ||
if doInfo: | ||
|
@@ -60,6 +61,8 @@ def extractFontFromOpenType( | |
function(source, destination) | ||
if doInstructions: | ||
extractInstructions(source, destination) | ||
if doColors: | ||
extractColorsFromOpenType(source, destination) | ||
source.close() | ||
|
||
|
||
|
@@ -1046,3 +1049,44 @@ def _extractOpenTypeKerningFromKern(source): | |
# there are no minimum values. | ||
kerning.update(subtable.kernTable) | ||
return kerning | ||
|
||
# ----- | ||
# Color | ||
# ----- | ||
|
||
COLOR_LAYERS_KEY = "com.github.googlei18n.ufo2ft.colorLayers" | ||
COLOR_PALETTES_KEY = "com.github.googlei18n.ufo2ft.colorPalettes" | ||
COLOR_LAYER_MAPPING_KEY = "com.github.googlei18n.ufo2ft.colorLayerMapping" | ||
UFO2FT_FILTER_KEY = "com.github.googlei18n.ufo2ft.filters" | ||
def extractColorsFromOpenType( | ||
source, | ||
destination, | ||
): | ||
if not "CPAL" in source or not "COLR" in source: return | ||
destination.lib[UFO2FT_FILTER_KEY] = [{"name": "Explode Color Layer Glyphs", "pre": True}] | ||
# UFO format only supports one palette. Others ignored. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's not true, colorPalettes key can contain multiple palettes it's not "UFO format", btw. This is currently a private set of keys only defined by ufo2ft, not made public in the UFO spec as of yet There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uh, actually it's both. I added certain keys to support building in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But there's the rub: there's only a single There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we deliberately chose not to use the layerinfo.plist color for this, since that one is traditionally used for GUI annotation purposes, in fact it's called
I can accept that you say they are not documented properly, but they are not "hacky" keys. They are the ufo2ft's way to build COLR/CPAL fonts. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no idea why it'd be expected that the layer color stops at the GUI, and I don't see that in the spec either? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes sense to say it because a lot of work went into getting it to its current state based on the current reading, and more work would be needed to change it to a different reading. Also, the project is one year old in September, though the color layers are only a few months old, true. And only a few days old if you are only going by releases and not development branch activity. I put more stock in how much effort something would take to change than how long it's been a certain way, though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @benkiel This probably isn't the place, but what about instead of promoting the keys, we add a disambiguating key? Something like I just see it as duplicated data when most of the time users are going to want the GUI color and exported color to equal one another, in a world of color fonts. As far as the multiple palettes go, this is going to have to go into UFO4. I'd naively solve it by making the layer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I use colors a lot for grouping glyphs in the GUI, even in COLR/CPAL fonts, so making layer colors work also as CPAL colors would be the wrong thing for my fonts, since the two have different uses. It is better to keep the current UFO layer color as they have been understood by almost all implementations so far, and use a different structure for color font layers. Glyphs also uses distinct structures for GUI colors and CPAL palettes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't a well-tread part of the spec, but since UFO4 will likely support COLRv1, this GUI color separation concern makes me think of the fact that COLRv1 has a flags array with masks for So if we're already saying that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or not? Do we think that the flags are useless and not worth supporting? I think they'll end up used eventually, emoji for use with dark themes seems to be the intent. |
||
firstPalette = source["CPAL"].palettes[0] | ||
colorIDs = list() | ||
for i, color in enumerate(firstPalette): | ||
colorIDs.append((color.red/255.0, color.green/255.0, color.blue/255.0, color.alpha/255.0)) | ||
|
||
destination.lib[COLOR_PALETTES_KEY] = [[(color.red/255.0, color.green/255.0, color.blue/255.0, color.alpha/255.0) for color in firstPalette]] | ||
|
||
layerGlyphs = list() | ||
for (base_glyph, layer_records) in source["COLR"].ColorLayers.items(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is assuming COLR v0 table (flat colors only); maybe you should check the table.version and error out nicely if it's == 1 (instead of AttributeError). |
||
for i, layer_record in enumerate(layer_records): | ||
(name, colorID) = (layer_record.name, layer_record.colorID) | ||
layer_name = "color{}_layer{}".format(colorID, i) | ||
if not layer_name in destination.layers: | ||
layer = destination.newLayer(layer_name) | ||
layer._set_color(colorIDs[colorID]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is that a private method of defcon? consider some alternative way that doesn't require accessing a private member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should just be |
||
glyph = destination[name] | ||
destination.layers[layer_name].insertGlyph(glyph, name=base_glyph) | ||
layerGlyphs.append(name) | ||
|
||
for (base_glyph, layer_records) in source["COLR"].ColorLayers.items(): | ||
destination[base_glyph].lib[COLOR_LAYER_MAPPING_KEY] = [("color{}_layer{}".format(lr.colorID, i), lr.colorID) for i, lr in enumerate(layer_records)] | ||
|
||
for name in layerGlyphs: | ||
if name in destination.layers.defaultLayer: | ||
del destination.layers.defaultLayer[name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this
COLOR_LAYERS_KEY
is unused, because you're using the more high-level alternative approach ofCOLOR_LAYER_MAPPING_KEY
combined with "Explode Color Layer Glyphs" filter.Maybe you could it made optional whether to create multiple layers containing glyphs with the same colorID (i.e. what you're currently doing) vs. creating a single default layer that contains all the glyphs, both the base color glyphs and their "glyphlets", with
colorLayers
key defining the mapping with the respective palette index.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but where should that option be passed in?