From de073442e78588c7f34a0c30a497e2404c78d367 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 22 Aug 2023 13:48:51 -0600 Subject: [PATCH 1/4] [varcToCOLR] Start --- Lib/fontTools/colorLib/varcToCOLR.py | 86 ++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Lib/fontTools/colorLib/varcToCOLR.py diff --git a/Lib/fontTools/colorLib/varcToCOLR.py b/Lib/fontTools/colorLib/varcToCOLR.py new file mode 100644 index 0000000000..7c4c660fb2 --- /dev/null +++ b/Lib/fontTools/colorLib/varcToCOLR.py @@ -0,0 +1,86 @@ +from fontTools.colorLib.builder import buildCOLR +from fontTools.ttLib.tables import otTables as ot +from fontTools.misc.fixedTools import floatToFixed as fl2fi + + +def varcToCOLR(font): + glyf = font["glyf"] + + paintForeground = {'Format': ot.PaintFormat.PaintSolid, + 'PaletteIndex': 0xFFFF, + 'Alpha': 1.0} + + colorGlyphs = {} + for glyphName in glyf.keys(): + glyph = glyf[glyphName] + if glyph.isVarComposite(): + + layers = [] + + for component in glyph.components: + if glyf[component.glyphName].isVarComposite(): + + paint = {'Format': ot.PaintFormat.PaintColrGlyph, + 'Glyph': component.glyphName} + else: + paint = {'Format': ot.PaintFormat.PaintGlyph, + 'Paint': paintForeground, + 'Glyph': component.glyphName} + + transform = component.transform + + # Check for non-integer values + + if (transform.translateX or transform.translateY or + transform.tCenterX or transform.tCenterY): + paint = {'Format': ot.PaintFormat.PaintTranslate, + 'Paint': paint, + 'dx': transform.translateX + transform.tCenterX, + 'dy': transform.translateY + transform.tCenterY} + + if transform.rotation: + paint = {'Format': ot.PaintFormat.PaintRotate, + 'Paint': paint, + 'angle': transform.rotation} + + if transform.scaleX != 1.0 or transform.scaleY != 1.0: + paint = {'Format': ot.PaintFormat.PaintScale, + 'Paint': paint, + 'scaleX': transform.scaleX, + 'scaleY': transform.scaleY} + + if transform.skewX or transform.skewY: + paint = {'Format': ot.PaintFormat.PaintSkew, + 'Paint': paint, + 'xSkewAngle': transform.skewX, + 'ySkewAngle': -transform.skewY} + + if transform.tCenterX or transform.tCenterY: + paint = {'Format': ot.PaintFormat.PaintTranslate, + 'Paint': paint, + 'dx': -transform.tCenterX, + 'dy': -transform.tCenterY} + + layers.append(paint) + + colorGlyphs[glyphName] = {'Format': ot.PaintFormat.PaintColrLayers, + 'Layers': layers} + + for glyphName in colorGlyphs: + glyf[glyphName].numberOfContours = 0 + + colr = buildCOLR(colorGlyphs, + version=1, + glyphMap=font.getReverseGlyphMap()) + font["COLR"] = colr + + +if __name__ == "__main__": + import sys + from fontTools.ttLib import TTFont + + font = TTFont(sys.argv[1]) + + varcToCOLR(font) + + font.save(sys.argv[2]) From 8b1d53fd130e013e63fec2c028164e3f635745bb Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 22 Aug 2023 14:18:24 -0600 Subject: [PATCH 2/4] Add PaintLocation --- Lib/fontTools/colorLib/varcToCOLR.py | 21 ++++++++++ Lib/fontTools/ttLib/tables/otData.py | 57 ++++++++++++++++++++++++++ Lib/fontTools/ttLib/tables/otTables.py | 1 + 3 files changed, 79 insertions(+) diff --git a/Lib/fontTools/colorLib/varcToCOLR.py b/Lib/fontTools/colorLib/varcToCOLR.py index 7c4c660fb2..407c9b02fc 100644 --- a/Lib/fontTools/colorLib/varcToCOLR.py +++ b/Lib/fontTools/colorLib/varcToCOLR.py @@ -6,6 +6,9 @@ def varcToCOLR(font): glyf = font["glyf"] + axisTags = glyf.axisTags + axisIndices = {tag: i for i, tag in enumerate(axisTags)} + paintForeground = {'Format': ot.PaintFormat.PaintSolid, 'PaletteIndex': 0xFFFF, 'Alpha': 1.0} @@ -61,6 +64,24 @@ def varcToCOLR(font): 'dx': -transform.tCenterX, 'dy': -transform.tCenterY} + if component.location: + axisList = [] + axisValues = [] + for axisTag, value in component.location.items(): + axisList.append(axisIndices[axisTag]) + axisValues.append(value) + + AxisList = ot.AxisList() + AxisList.Axis = axisList + + AxisValues = ot.AxisValues() + AxisValues.Value = axisValues + + paint = {'Format': ot.PaintFormat.PaintLocation, + 'Paint': paint, + 'AxisList': AxisList, + 'AxisValues': AxisValues} + layers.append(paint) colorGlyphs[glyphName] = {'Format': ot.PaintFormat.PaintColrLayers, diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index 56716824ec..bcf41848f7 100644 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -6157,6 +6157,63 @@ ), ], ), + # AxisList + ( + "AxisList", + [ + ("uint8", "AxisCount", None, None, "Number of axis indices"), + ( + "uint16", + "Axis", + "AxisCount", + 0, + "Array of axis indices", + ), + ], + ), + # AxisValues + ( + "AxisValues", + [ + ("uint8", "AxisCount", None, None, "Number of axis indices"), + ( + "F2Dot14", + "Value", + "AxisCount", + 0, + "Array of axis values", + ), + ], + ), + # PaintLocation + ( + "PaintFormat33", + [ + ("uint8", "PaintFormat", None, None, "Format identifier-format = 33"), + ( + "LOffset24To(Paint)", + "Paint", + None, + None, + "Offset (from beginning of PaintLocation table) to source Paint subtable.", + ), + ( + "LOffset24To(AxisList)", + "AxisList", + None, + None, + "", + ), + ( + "AxisValues", + "AxisValues", + None, + None, + "", + ), + ], + ), + # # avar # diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 9470873440..68009b664d 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -1562,6 +1562,7 @@ class PaintFormat(IntEnum): PaintSkewAroundCenter = 30 PaintVarSkewAroundCenter = 31 PaintComposite = 32 + PaintLocation = 33 def is_variable(self): return self.name.startswith("PaintVar") From 2ef6f8fc75c987cf62e214dd2cc8c4dd95999fc6 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 22 Aug 2023 14:52:25 -0600 Subject: [PATCH 3/4] [varcToCOLR] Allow scale > 2 --- Lib/fontTools/colorLib/varcToCOLR.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Lib/fontTools/colorLib/varcToCOLR.py b/Lib/fontTools/colorLib/varcToCOLR.py index 407c9b02fc..766f8f4852 100644 --- a/Lib/fontTools/colorLib/varcToCOLR.py +++ b/Lib/fontTools/colorLib/varcToCOLR.py @@ -47,10 +47,22 @@ def varcToCOLR(font): 'angle': transform.rotation} if transform.scaleX != 1.0 or transform.scaleY != 1.0: - paint = {'Format': ot.PaintFormat.PaintScale, - 'Paint': paint, - 'scaleX': transform.scaleX, - 'scaleY': transform.scaleY} + if (abs(transform.scaleX) < 2.0 and abs(transform.scaleY) < 2.0): + paint = {'Format': ot.PaintFormat.PaintScale, + 'Paint': paint, + 'scaleX': transform.scaleX, + 'scaleY': transform.scaleY} + else: + affine = ot.Affine2x3() + affine.xx = transform.scaleX + affine.xy = 0.0 + affine.yx = 0.0 + affine.yy = transform.scaleX + affine.dx = 0.0 + affine.dy = 0.0 + paint = {'Format': ot.PaintFormat.PaintTransform, + 'Paint': paint, + 'Transform': affine} if transform.skewX or transform.skewY: paint = {'Format': ot.PaintFormat.PaintSkew, From 5b4cfae2301058f8bc396af37b20f2a5b9916460 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 22 Aug 2023 14:55:37 -0600 Subject: [PATCH 4/4] [varcToCOLR] Add two versions of AxisList One 8-bit, one 16-bit. --- Lib/fontTools/colorLib/varcToCOLR.py | 1 + Lib/fontTools/ttLib/tables/otData.py | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/colorLib/varcToCOLR.py b/Lib/fontTools/colorLib/varcToCOLR.py index 766f8f4852..58e5dbd011 100644 --- a/Lib/fontTools/colorLib/varcToCOLR.py +++ b/Lib/fontTools/colorLib/varcToCOLR.py @@ -84,6 +84,7 @@ def varcToCOLR(font): axisValues.append(value) AxisList = ot.AxisList() + AxisList.Format = 1 AxisList.Axis = axisList AxisValues = ot.AxisValues() diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index bcf41848f7..0f800ff023 100644 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -6157,10 +6157,26 @@ ), ], ), - # AxisList + # AxisListFormat1 ( - "AxisList", + "AxisListFormat1", [ + ("uint8", "Format", None, None, "Number of axis indices"), + ("uint8", "AxisCount", None, None, "Number of axis indices"), + ( + "uint8", + "Axis", + "AxisCount", + 0, + "Array of axis indices", + ), + ], + ), + # AxisListFormat2 + ( + "AxisListFormat2", + [ + ("uint8", "Format", None, None, "Number of axis indices"), ("uint8", "AxisCount", None, None, "Number of axis indices"), ( "uint16",