Skip to content

Commit

Permalink
Merge branch 'varc-table'
Browse files Browse the repository at this point in the history
  • Loading branch information
behdad committed May 25, 2024
2 parents 7b5fbd6 + a83a7e4 commit 4a377ac
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 242 deletions.
27 changes: 12 additions & 15 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,28 @@
async def main(args):
print("Loading glyphs")

count = 10000000

rcjk_path = args[0]
glyphset = None
if len(args) == 2:
try:
count = int(args[1])
except ValueError:
glyphset = args[1:]
else:
glyphset = args[1:]
status = None
i = 1
if len(args) > i and args[i][0] == "-":
status = int(args[i][1:])
i += 1
glyphset = args[i:]

rcjkfont = RCJKBackend.fromPath(rcjk_path)
revCmap = await rcjkfont.getGlyphMap()

glyphs = {}
for glyphname in list(revCmap.keys())[:count] if not glyphset else glyphset:
for glyphname in revCmap.keys() if not glyphset else glyphset:
print("Loading glyph", glyphname)
glyph = await rcjkfont.getGlyph(glyphname)
glyph_masters = glyphMasters(glyph)
glyphs[glyphname] = glyph
if status is not None:
if not any(source.customData.get("fontra.development.status", status) == status for source in glyph.sources):
print("Skipping glyph", glyphname)
continue

# Check that glyph does not mix contours and components
for layer in glyph_masters.values():
assert not layer.glyph.path.coordinates or not layer.glyph.components
glyphs[glyphname] = glyph

await buildVarcFont(rcjkfont, glyphs)
await buildFlatFont(rcjkfont, glyphs)
Expand Down
212 changes: 83 additions & 129 deletions component.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
from fontTools.misc.roundTools import otRound
from fontTools.misc.fixedTools import floatToFixed as fl2fi
from fontTools.varLib.models import normalizeLocation
from fontTools.ttLib.tables.otTables import (
VarComponent,
VarComponentFlags,
VAR_TRANSFORM_MAPPING,
)
from fontTools.misc.transform import DecomposedTransform
from rcjkTools import *
import struct

Expand All @@ -27,6 +33,33 @@ def __init__(self):
self.coordinatesReset = None
self.transformHave = TransformHave()

def getComponentFlags(self):
flags = 0

if self.coordinatesReset:
flags |= VarComponentFlags.RESET_UNSPECIFIED_AXES

if self.transformHave.have_translateX:
flags |= VarComponentFlags.HAVE_TRANSLATE_X
if self.transformHave.have_translateY:
flags |= VarComponentFlags.HAVE_TRANSLATE_Y
if self.transformHave.have_rotation:
flags |= VarComponentFlags.HAVE_ROTATION
if self.transformHave.have_scaleX:
flags |= VarComponentFlags.HAVE_SCALE_X
if self.transformHave.have_scaleY:
flags |= VarComponentFlags.HAVE_SCALE_Y
if self.transformHave.have_skewX:
flags |= VarComponentFlags.HAVE_SKEW_X
if self.transformHave.have_skewY:
flags |= VarComponentFlags.HAVE_SKEW_Y
if self.transformHave.have_tcenterX:
flags |= VarComponentFlags.HAVE_TCENTER_X
if self.transformHave.have_tcenterY:
flags |= VarComponentFlags.HAVE_TCENTER_Y

return flags


def analyzeComponents(glyph_masters, glyphs, glyphAxes, publicAxes):
layer = next(iter(glyph_masters.values()))
Expand Down Expand Up @@ -67,7 +100,9 @@ def analyzeComponents(glyph_masters, glyphs, glyphAxes, publicAxes):
ca.transformHave.have_rotation = True
if fl2fi(t.scaleX, 10) != 1 << 10:
ca.transformHave.have_scaleX = True
if fl2fi(t.scaleY, 10) != 1 << 10:
if fl2fi(t.scaleY, 10) != 1 << 10 and fl2fi(t.scaleY, 10) != fl2fi(
t.scaleX, 10
):
ca.transformHave.have_scaleY = True
if fl2fi(t.skewX / 180.0, 12):
ca.transformHave.have_skewX = True
Expand All @@ -80,14 +115,14 @@ def analyzeComponents(glyph_masters, glyphs, glyphAxes, publicAxes):

loc = component.location
loc = normalizeLocation(loc, allComponentAxes[i])
for tag in ca.coordinates:
c = loc.get(tag, 0)
for name in ca.coordinates:
c = loc.get(name, 0)
if c:
ca.coordinateHaveReset.add(tag)
if c != masterLocation.get(tag, 0) or (
tag in publicAxes and tag not in glyphAxes
ca.coordinateHaveReset.add(name)
if c != masterLocation.get(name, 0) or (
name in publicAxes and name not in glyphAxes
):
ca.coordinateHaveOverlay.add(tag)
ca.coordinateHaveOverlay.add(name)

for ca in cas:
ca.coordinatesReset = len(ca.coordinateHaveReset) <= len(
Expand All @@ -97,157 +132,76 @@ def analyzeComponents(glyph_masters, glyphs, glyphAxes, publicAxes):
ca.coordinateHaveReset if ca.coordinatesReset else ca.coordinateHaveOverlay
)

for layer in glyph_masters.values():
for layer in list(glyph_masters.values())[1:]:
for i, component in enumerate(layer.glyph.components):
ca = cas[i]
loc = component.location
loc = normalizeLocation(loc, allComponentAxes[i])
for tag in ca.coordinates:
if tag in ca.coordinateHave and loc.get(tag, 0) != defaultLocations[
for name in ca.coordinates:
# XXX Is this logic correct for coordinatesReset?
if name in ca.coordinateHave and loc.get(name, 0) != defaultLocations[
i
].get(tag, 0):
].get(name, 0):
ca.coordinateVaries = True

return cas


def buildComponentPoints(rcjkfont, component, componentGlyph, componentAnalysis):
ca = componentAnalysis

componentAxes = {
axis.name: (axis.minValue, axis.defaultValue, axis.maxValue)
for axis in componentGlyph.axes
}
coords = component.location
coords = normalizeLocation(coords, componentAxes)

t = component.transformation

points = []

if ca.coordinateVaries:
for tag in componentAxes.keys():
if tag in ca.coordinateHave:
coord = coords.get(tag, 0)
points.append((fl2fi(coord, 14), 0))

c = ca.transformHave
if c.have_translateX or c.have_translateY:
points.append((t.translateX, t.translateY))
if c.have_rotation:
points.append((fl2fi(t.rotation / 180.0, 12), 0))
if c.have_scaleX or c.have_scaleY:
points.append((fl2fi(t.scaleX, 10), fl2fi(t.scaleY, 10)))
if c.have_skewX or c.have_skewY:
points.append((fl2fi(t.skewX / 180.0, 12), fl2fi(t.skewY / 180.0, 12)))
if c.have_tcenterX or c.have_tcenterY:
points.append((t.tCenterX, t.tCenterY))

return points


def buildComponentRecord(
component, componentGlyph, componentAnalysis, fvarTags, reverseGlyphMap
def getComponentMasters(
rcjkfont, component, componentGlyph, componentAnalysis, fvarTags, publicAxes
):
ca = componentAnalysis

componentAxes = {
axis.name: (axis.minValue, axis.defaultValue, axis.maxValue)
for axis in componentGlyph.axes
}
axesMap = {}
i = 0
for name in sorted(componentAxes.keys()):
if name in publicAxes:
axesMap[name] = name
elif name in fvarTags:
axesMap[name] = name
else:
axesMap[name] = "%04d" % i
i += 1

coords = component.location
coords = normalizeLocation(coords, componentAxes)

flag = 0

numAxes = struct.pack(">B", len(ca.coordinateHave))

gid = reverseGlyphMap[component.name]
if gid <= 65535:
# gid16
gid = struct.pack(">H", gid)
else:
# gid24
gid = struct.pack(">L", gid)[1:]
flag |= 1 << 12

if ca.coordinatesReset:
flag |= 1 << 14

axisIndices = []
for i, tag in enumerate(componentAxes.keys()):
if tag not in ca.coordinateHave:
continue
name = "%4d" % i if tag not in fvarTags else tag
axisIndices.append(fvarTags.index(name))

if ca.coordinateVaries:
flag |= 1 << 13

if all(v <= 255 for v in axisIndices):
axisIndices = b"".join(struct.pack(">B", v) for v in axisIndices)
else:
axisIndices = b"".join(struct.pack(">H", v) for v in axisIndices)
flag |= 1 << 1

axisValues = b"".join(
struct.pack(">h", fl2fi(coords.get(tag, 0), 14))
for tag in componentAxes.keys()
if tag in ca.coordinateHave
)
axisIndexMasters, axisValueMasters, transformMasters = [], [], []

for name in ca.coordinateHave:
coord = coords.get(name, 0)
i = fvarTags.index(axesMap[name])
axisIndexMasters.append(i)
axisValueMasters.append(fl2fi(coord, 14))
if axisIndexMasters:
# Sort them for better sharing
axisIndexMasters, axisValueMasters = zip(
*sorted(zip(axisIndexMasters, axisValueMasters))
)

c = ca.transformHave

t = component.transformation

translateX = (
translateY
) = rotation = scaleX = scaleY = skewX = skewY = tcenterX = tcenterY = b""
if c.have_translateX:
translateX = struct.pack(">h", otRound(t.translateX))
flag |= 1 << 3
transformMasters.append(otRound(t.translateX))
if c.have_translateY:
translateY = struct.pack(">h", otRound(t.translateY))
flag |= 1 << 4
transformMasters.append(otRound(t.translateY))
if c.have_rotation:
rotation = struct.pack(">h", fl2fi(t.rotation / 180.0, 12))
flag |= 1 << 5
transformMasters.append(fl2fi(t.rotation / 180.0, 12))
if c.have_scaleX:
scaleX = struct.pack(">h", fl2fi(t.scaleX, 10))
flag |= 1 << 6
transformMasters.append(fl2fi(t.scaleX, 10))
if c.have_scaleY:
if not c.have_scaleX or fl2fi(t.scaleY, 10) != fl2fi(t.scaleX, 10):
scaleY = struct.pack(">h", fl2fi(t.scaleY, 10))
flag |= 1 << 7
else:
flag |= 1 << 2
transformMasters.append(fl2fi(t.scaleY, 10))
if c.have_skewX:
skewX = struct.pack(">h", fl2fi(t.skewX / 180.0, 12))
flag |= 1 << 8
transformMasters.append(fl2fi(t.skewX / 180.0, 12))
if c.have_skewY:
skewY = struct.pack(">h", fl2fi(t.skewY / 180.0, 12))
flag |= 1 << 9
transformMasters.append(fl2fi(t.skewY / 180.0, 12))
if c.have_tcenterX:
tcenterX = struct.pack(">h", otRound(t.tCenterX))
flag |= 1 << 10
transformMasters.append(otRound(t.tCenterX))
if c.have_tcenterY:
tcenterY = struct.pack(">h", otRound(t.tCenterY))
flag |= 1 << 11

transform = (
translateX
+ translateY
+ rotation
+ scaleX
+ scaleY
+ skewX
+ skewY
+ tcenterX
+ tcenterY
)

flag = struct.pack(">H", flag)

rec = flag + numAxes + gid + axisIndices + axisValues + transform

return rec
transformMasters.append(otRound(t.tCenterY))

return tuple(axisIndexMasters), tuple(axisValueMasters), tuple(transformMasters)
17 changes: 13 additions & 4 deletions decompose.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from font import mapTuple
from transform import composeTransform, Identity
from mathRecording import MathRecording
from rcjkTools import *
Expand All @@ -11,14 +12,22 @@
async def decomposeGlyph(glyph, rcjkfont, location=(), trans=Identity):
value = []
axes = {
axis.name: (axis.minValue, axis.defaultValue, axis.maxValue)
for axis in glyph.axes
axis.name: mapTuple(
(axis.minValue, axis.defaultValue, axis.maxValue), axis.mapping
)
for axis in (await rcjkfont.getAxes()).axes
}
axes.update(
{
axis.name: (axis.minValue, axis.defaultValue, axis.maxValue)
for axis in glyph.axes
}
)

glyph_masters = glyphMasters(glyph)

masterLocs = list(dictifyLocation(l) for l in glyph_masters.keys())
masterLocs = [normalizeLocation(m, axes) for m in masterLocs]
masterLocs = [normalizeLocation(m, axes, validate=True) for m in masterLocs]

model = VariationModel(masterLocs, list(axes.keys()))

Expand All @@ -29,7 +38,7 @@ async def decomposeGlyph(glyph, rcjkfont, location=(), trans=Identity):
for layer in glyph_masters.values()
]

loc = normalizeLocation(location, axes)
loc = normalizeLocation(location, axes) # , validate=True)
shape = model.interpolateFromMasters(loc, masterShapes)

value.extend(shape.value)
Expand Down
Loading

0 comments on commit 4a377ac

Please sign in to comment.