Skip to content

Commit a8d54e6

Browse files
Merge pull request #50 from BlackFoundryCom/colrv1-update
Upgrade to the current COLRv1 spec + fonttools This partially fixes #35: some changes are not tested (the test fonts don't cover all Paints) and variability is currently completely broken.
2 parents 6ab0354 + c08f06a commit a8d54e6

30 files changed

+225
-288
lines changed

Lib/blackrenderer/backends/skia.py

+7
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ def drawPathSweepGradient(
153153
extendMode,
154154
gradientTransform,
155155
):
156+
# The following is needed to please the Skia shader, but it's a but fuzzy
157+
# to me how this affects the spec. Translated from:
158+
# https://source.chromium.org/chromium/chromium/src/+/master:third_party/skia/src/ports/SkFontHost_FreeType_common.cpp;l=673-686
159+
startAngle %= 360
160+
endAngle %= 360
161+
if startAngle >= endAngle:
162+
endAngle += 360
156163
matrix = skia.Matrix()
157164
matrix.setAffine(gradientTransform)
158165
colors, stops = _unpackColorLine(colorLine)

Lib/blackrenderer/dumpCOLRv1Glyph.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from functools import singledispatch
2-
from fontTools.ttLib.tables.otTables import ColorIndex, ColorLine, Paint
2+
from fontTools.ttLib.tables.otTables import ColorLine, Paint
33
from .font import PAINT_NAMES
44

55

@@ -42,11 +42,6 @@ def unpackPaint(paint: Paint, font):
4242
return d
4343

4444

45-
@unpackObject.register
46-
def unpackColorIndex(color: ColorIndex, font):
47-
return color255(font._getColor(color.PaletteIndex, color.Alpha))
48-
49-
5045
@unpackObject.register
5146
def unpackColorLine(colorLine: ColorLine, font):
5247
return [

Lib/blackrenderer/font.py

+64-26
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
from fontTools.misc.transform import Transform, Identity
77
from fontTools.misc.arrayTools import unionRect
88
from fontTools.ttLib import TTFont
9-
from fontTools.ttLib.tables.otTables import CompositeMode, PaintFormat, VariableValue
10-
from fontTools.ttLib.tables.otConverters import VarF2Dot14, VarFixed
9+
from fontTools.ttLib.tables.otTables import CompositeMode, PaintFormat
1110
from fontTools.varLib.varStore import VarStoreInstancer
1211
import uharfbuzz as hb
1312

@@ -46,9 +45,13 @@ def __init__(self, path, *, fontNumber=0, lazy=True):
4645
colrTable = colrTable.table
4746
self.colrV1Glyphs = {
4847
glyph.BaseGlyph: glyph
49-
for glyph in colrTable.BaseGlyphV1List.BaseGlyphV1Record
48+
for glyph in colrTable.BaseGlyphList.BaseGlyphPaintRecord
5049
}
51-
self.colrLayersV1 = colrTable.LayerV1List
50+
if colrTable.ClipList is None:
51+
self.clipBoxes = None
52+
else:
53+
self.clipBoxes = colrTable.ClipList.clips
54+
self.colrLayersV1 = colrTable.LayerList
5255
if colrTable.VarStore is not None:
5356
self.instancer = VarStoreInstancer(
5457
colrTable.VarStore, self.ttFont["fvar"].axes
@@ -98,9 +101,13 @@ def colrV1GlyphNames(self):
98101
return self.colrV1Glyphs.keys()
99102

100103
def getGlyphBounds(self, glyphName):
101-
if glyphName in self.colrV1Glyphs or glyphName not in self.colrV0Glyphs:
104+
if glyphName in self.colrV1Glyphs:
102105
bounds = self._getGlyphBounds(glyphName)
103-
else:
106+
if self.clipBoxes is not None:
107+
box = self.clipBoxes.get(glyphName)
108+
if box is not None:
109+
bounds = box.xMin, box.yMin, box.xMax, box.yMax
110+
elif glyphName in self.colrV0Glyphs:
104111
# For COLRv0, we take the union of all layer bounds
105112
bounds = None
106113
for layer in self.colrV0Glyphs[glyphName]:
@@ -109,6 +116,8 @@ def getGlyphBounds(self, glyphName):
109116
bounds = layerBounds
110117
else:
111118
bounds = unionRect(layerBounds, bounds)
119+
else:
120+
bounds = self._getGlyphBounds(glyphName)
112121
return bounds
113122

114123
def drawGlyph(self, glyphName, canvas, *, palette=None, textColor=(0, 0, 0, 1)):
@@ -171,7 +180,7 @@ def _drawPaintColrLayers(self, paint, canvas):
171180
self._drawPaint(self.colrLayersV1.Paint[i], canvas)
172181

173182
def _drawPaintSolid(self, paint, canvas):
174-
color = self._getColor(paint.Color.PaletteIndex, paint.Color.Alpha)
183+
color = self._getColor(paint.PaletteIndex, paint.Alpha)
175184
canvas.drawPathSolid(self.currentPath, color)
176185

177186
def _drawPaintLinearGradient(self, paint, canvas):
@@ -247,13 +256,25 @@ def _drawPaintTranslate(self, paint, canvas):
247256
self._applyTransform(transform, paint.Paint, canvas)
248257

249258
def _drawPaintRotate(self, paint, canvas):
259+
transform = Transform()
260+
transform = transform.rotate(math.radians(paint.angle))
261+
self._applyTransform(transform, paint.Paint, canvas)
262+
263+
def _drawPaintRotateAroundCenter(self, paint, canvas):
250264
transform = Transform()
251265
transform = transform.translate(paint.centerX, paint.centerY)
252266
transform = transform.rotate(math.radians(paint.angle))
253267
transform = transform.translate(-paint.centerX, -paint.centerY)
254268
self._applyTransform(transform, paint.Paint, canvas)
255269

256270
def _drawPaintSkew(self, paint, canvas):
271+
transform = Transform()
272+
transform = transform.skew(
273+
math.radians(paint.xSkewAngle), math.radians(paint.ySkewAngle)
274+
)
275+
self._applyTransform(transform, paint.Paint, canvas)
276+
277+
def _drawPaintSkewAroundCenter(self, paint, canvas):
257278
transform = Transform()
258279
transform = transform.translate(paint.centerX, paint.centerY)
259280
transform = transform.skew(
@@ -263,10 +284,26 @@ def _drawPaintSkew(self, paint, canvas):
263284
self._applyTransform(transform, paint.Paint, canvas)
264285

265286
def _drawPaintScale(self, paint, canvas):
266-
# https://github.com/googlefonts/colr-gradients-spec/issues/279
287+
transform = Transform()
288+
transform = transform.scale(paint.scaleX, paint.scaleY)
289+
self._applyTransform(transform, paint.Paint, canvas)
290+
291+
def _drawPaintScaleAroundCenter(self, paint, canvas):
267292
transform = Transform()
268293
transform = transform.translate(paint.centerX, paint.centerY)
269-
transform = transform.scale(paint.xScale, paint.yScale)
294+
transform = transform.scale(paint.scaleX, paint.scaleY)
295+
transform = transform.translate(-paint.centerX, -paint.centerY)
296+
self._applyTransform(transform, paint.Paint, canvas)
297+
298+
def _drawPaintScaleUniform(self, paint, canvas):
299+
transform = Transform()
300+
transform = transform.scale(paint.scale, paint.scale)
301+
self._applyTransform(transform, paint.Paint, canvas)
302+
303+
def _drawPaintScaleUniformAroundCenter(self, paint, canvas):
304+
transform = Transform()
305+
transform = transform.translate(paint.centerX, paint.centerY)
306+
transform = transform.scale(paint.scale, paint.scale)
270307
transform = transform.translate(-paint.centerX, -paint.centerY)
271308
self._applyTransform(transform, paint.Paint, canvas)
272309

@@ -373,7 +410,7 @@ def _getColor(self, colorIndex, alpha):
373410
def _readColorLine(self, colorLineTable):
374411
return _normalizeColorLine(
375412
[
376-
(cs.StopOffset, self._getColor(cs.Color.PaletteIndex, cs.Color.Alpha))
413+
(cs.StopOffset, self._getColor(cs.PaletteIndex, cs.Alpha))
377414
for cs in colorLineTable.ColorStop
378415
]
379416
)
@@ -442,10 +479,10 @@ def axisValuesToLocation(normalizedAxisValues, axisTags):
442479
}
443480

444481

445-
_conversionFactors = {
446-
VarF2Dot14: 1 / (1 << 14),
447-
VarFixed: 1 / (1 << 16),
448-
}
482+
# _conversionFactors = {
483+
# VarF2Dot14: 1 / (1 << 14),
484+
# VarFixed: 1 / (1 << 16),
485+
# }
449486

450487

451488
class PaintVarWrapper:
@@ -459,16 +496,17 @@ def __repr__(self):
459496

460497
def __getattr__(self, attrName):
461498
value = getattr(self._wrappedPaint, attrName)
462-
if isinstance(value, VariableValue):
463-
if value.varIdx != 0xFFFFFFFF:
464-
factor = _conversionFactors.get(
465-
type(self._wrappedPaint.getConverterByName(attrName)), 1
466-
)
467-
value = value.value + self._instancer[value.varIdx] * factor
468-
else:
469-
value = value.value
470-
elif type(value).__name__.startswith("Var"):
471-
value = PaintVarWrapper(value, self._instancer)
472-
elif isinstance(value, (list, UserList)):
473-
value = [PaintVarWrapper(item, self._instancer) for item in value]
499+
raise NotImplementedError("This code is currently not working")
500+
# if isinstance(value, VariableValue):
501+
# if value.varIdx != 0xFFFFFFFF:
502+
# factor = _conversionFactors.get(
503+
# type(self._wrappedPaint.getConverterByName(attrName)), 1
504+
# )
505+
# value = value.value + self._instancer[value.varIdx] * factor
506+
# else:
507+
# value = value.value
508+
# elif type(value).__name__.startswith("Var"):
509+
# value = PaintVarWrapper(value, self._instancer)
510+
# elif isinstance(value, (list, UserList)):
511+
# value = [PaintVarWrapper(item, self._instancer) for item in value]
474512
return value
3.91 KB
Binary file not shown.

Tests/data/noto-glyf_colr_1.ttf

-596 KB
Binary file not shown.
Loading
-7.24 KB
Loading
Loading
-7.75 KB
Loading
+11-35
Loading
6.33 KB
Loading
Loading
3.31 KB
Loading

0 commit comments

Comments
 (0)