fonttools/Lib/fontTools/ttLib/scaleUpem.py

226 lines
6.2 KiB
Python
Raw Normal View History

2022-08-17 14:16:45 -06:00
"""Change the units-per-EM of a font.
2022-08-18 06:00:16 -06:00
Currently does not support CFF fonts. AAT, Graphite tables are not supported
either."""
2022-08-17 14:16:45 -06:00
from fontTools.ttLib.ttVisitor import TTVisitor
2022-08-15 12:17:51 -06:00
import fontTools.ttLib as ttLib
import fontTools.ttLib.tables.otBase as otBase
import fontTools.ttLib.tables.otTables as otTables
2022-08-15 12:50:16 -06:00
from fontTools.misc.fixedTools import otRound
2022-08-17 14:57:15 -06:00
__all__ = ["scale_upem", "ScalerVisitor"]
class ScalerVisitor(TTVisitor):
2022-08-15 12:50:16 -06:00
def __init__(self, scaleFactor):
self.scaleFactor = scaleFactor
def scale(self, v):
2022-08-15 12:50:16 -06:00
return otRound(v * self.scaleFactor)
2022-08-15 12:17:51 -06:00
@ScalerVisitor.register_attrs(
(
(ttLib.getTableClass("head"), ("unitsPerEm", "xMin", "yMin", "xMax", "yMax")),
2022-08-18 06:20:33 -06:00
(ttLib.getTableClass("post"), ("underlinePosition", "underlineThickness")),
2022-08-15 12:17:51 -06:00
(
ttLib.getTableClass("hhea"),
(
"ascent",
"descent",
"lineGap",
"advanceWidthMax",
"minLeftSideBearing",
"minRightSideBearing",
"xMaxExtent",
2022-08-15 14:25:15 -06:00
"caretOffset",
2022-08-15 14:27:15 -06:00
),
2022-08-15 14:25:15 -06:00
),
(
ttLib.getTableClass("vhea"),
(
"ascent",
"descent",
"lineGap",
"advanceHeightMax",
"minTopSideBearing",
"minBottomSideBearing",
"yMaxExtent",
"caretOffset",
2022-08-15 14:27:15 -06:00
),
2022-08-15 12:17:51 -06:00
),
(
ttLib.getTableClass("OS/2"),
(
"xAvgCharWidth",
"ySubscriptXSize",
"ySubscriptYSize",
"ySubscriptXOffset",
"ySubscriptYOffset",
"ySuperscriptXSize",
"ySuperscriptYSize",
"ySuperscriptXOffset",
"ySuperscriptYOffset",
"yStrikeoutSize",
"yStrikeoutPosition",
"sTypoAscender",
"sTypoDescender",
"sTypoLineGap",
"usWinAscent",
"usWinDescent",
"sxHeight",
"sCapHeight",
2022-08-15 14:27:15 -06:00
),
2022-08-15 12:17:51 -06:00
),
(otTables.ValueRecord, ("XAdvance", "YAdvance", "XPlacement", "YPlacement")),
(otTables.Anchor, ("XCoordinate", "YCoordinate")),
2022-08-15 14:21:31 -06:00
(otTables.CaretValue, ("Coordinate")),
2022-08-18 05:23:08 -06:00
(otTables.BaseCoord, ("Coordinate")),
2022-08-18 05:54:26 -06:00
(otTables.ClipBox, ("xMin", "yMin", "xMax", "yMax")),
2022-08-15 12:17:51 -06:00
)
)
def visit(visitor, obj, attr, value):
setattr(obj, attr, visitor.scale(value))
2022-08-15 14:27:15 -06:00
@ScalerVisitor.register_attr(
(ttLib.getTableClass("hmtx"), ttLib.getTableClass("vmtx")), "metrics"
)
2022-08-15 12:17:51 -06:00
def visit(visitor, obj, attr, metrics):
for g in metrics:
advance, lsb = metrics[g]
metrics[g] = visitor.scale(advance), visitor.scale(lsb)
@ScalerVisitor.register_attr(ttLib.getTableClass("glyf"), "glyphs")
def visit(visitor, obj, attr, glyphs):
for g in glyphs.values():
if g.isComposite():
for component in g.components:
component.x = visitor.scale(component.x)
component.y = visitor.scale(component.y)
else:
for attr in ("xMin", "xMax", "yMin", "yMax"):
v = getattr(g, attr, None)
if v is not None:
setattr(g, attr, visitor.scale(v))
glyf = visitor.font["glyf"]
coordinates = g.getCoordinates(glyf)[0]
for i, (x, y) in enumerate(coordinates):
coordinates[i] = visitor.scale(x), visitor.scale(y)
@ScalerVisitor.register_attr(ttLib.getTableClass("gvar"), "variations")
def visit(visitor, obj, attr, variations):
for varlist in variations.values():
for var in varlist:
coordinates = var.coordinates
for i, xy in enumerate(coordinates):
if xy is None:
continue
coordinates[i] = visitor.scale(xy[0]), visitor.scale(xy[1])
2022-08-15 12:56:38 -06:00
@ScalerVisitor.register_attr(ttLib.getTableClass("kern"), "kernTables")
def visit(visitor, obj, attr, kernTables):
for table in kernTables:
kernTable = table.kernTable
for k in kernTable.keys():
kernTable[k] = visitor.scale(kernTable[k])
# ItemVariationStore
@ScalerVisitor.register(otTables.VarData)
def visit(visitor, varData):
for item in varData.Item:
for i, v in enumerate(item):
item[i] = visitor.scale(v)
2022-08-18 05:54:26 -06:00
# COLRv1
def _setup_scale_paint(paint, scale):
if -2 <= scale <= 2 - (1 >> 14):
paint.Format = 20 # PaintScaleUniform
paint.scale = scale
return
2022-08-18 05:54:26 -06:00
transform = otTables.Affine2x3()
transform.populateDefaults()
transform.xy = transform.yx = transform.dx = transform.dy = 0
transform.xx = transform.yy = scale
paint.Format = 12 # PaintTransform
paint.Transform = transform
@ScalerVisitor.register(otTables.BaseGlyphPaintRecord)
def visit(visitor, record):
oldPaint = record.Paint
2022-08-18 05:54:26 -06:00
scale = otTables.Paint()
_setup_scale_paint(scale, visitor.scaleFactor)
2022-08-18 05:54:26 -06:00
scale.Paint = oldPaint
2022-08-18 05:54:26 -06:00
record.Paint = scale
2022-08-18 05:54:26 -06:00
return True
@ScalerVisitor.register(otTables.Paint)
def visit(visitor, paint):
2022-08-18 06:00:16 -06:00
if paint.Format != 10: # PaintGlyph
2022-08-18 05:54:26 -06:00
return True
newPaint = otTables.Paint()
newPaint.Format = paint.Format
newPaint.Paint = paint.Paint
newPaint.Glyph = paint.Glyph
del paint.Paint
del paint.Glyph
_setup_scale_paint(paint, 1 / visitor.scaleFactor)
2022-08-18 05:54:26 -06:00
paint.Paint = newPaint
visitor.visit(newPaint.Paint)
return False
2022-08-17 14:57:15 -06:00
def scale_upem(font, new_upem):
"""Change the units-per-EM of font to the new value."""
upem = font["head"].unitsPerEm
visitor = ScalerVisitor(new_upem / upem)
visitor.visit(font)
if __name__ == "__main__":
from fontTools.ttLib import TTFont
import sys
if len(sys.argv) != 3:
2022-08-18 05:08:40 -06:00
print("usage: fonttools ttLib.scaleUpem font new-upem")
sys.exit()
font = TTFont(sys.argv[1])
new_upem = int(sys.argv[2])
2022-08-17 14:16:45 -06:00
if "CFF " in font or "CFF2" in font:
2022-08-18 05:23:08 -06:00
print(
"fonttools ttLib.scaleUpem: CFF/CFF2 fonts are not supported.",
file=sys.stderr,
)
2022-08-15 14:30:04 -06:00
sys.exit(1)
2022-08-17 14:57:15 -06:00
scale_upem(font, new_upem)
2022-08-15 13:59:05 -06:00
2022-08-15 22:03:37 -06:00
print("Writing out.ttf")
2022-08-15 13:59:05 -06:00
font.save("out.ttf")