Skip to content

Commit 90ab48f

Browse files
Merge pull request #110 from BlackFoundryCom/palette-index
Add --palette-index command line option; add getPalette() method to font
2 parents 83ff93d + 8a5c924 commit 90ab48f

13 files changed

+158
-70
lines changed

Lib/blackrenderer/__main__.py

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def main():
3131
parser.add_argument("--features", type=parseFeatures)
3232
parser.add_argument("--variations", type=parseVariations)
3333
parser.add_argument("--margin", type=float, default=20)
34+
parser.add_argument("--palette-index", type=int, default=0)
3435
parser.add_argument(
3536
"--backend",
3637
default=None,
@@ -47,6 +48,7 @@ def main():
4748
margin=args.margin,
4849
features=args.features,
4950
variations=args.variations,
51+
paletteIndex=args.palette_index,
5052
backendName=args.backend,
5153
)
5254

Lib/blackrenderer/font.py

+7
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ def __init__(self, path=None, *, fontNumber=0, lazy=True, ttFont=None, hbFont=No
9797
def unitsPerEm(self):
9898
return self.hbFont.face.upem
9999

100+
def getPalette(self, paletteIndex):
101+
if not self.palettes:
102+
return None
103+
# clamp index
104+
paletteIndex = max(0, min(paletteIndex, len(self.palettes) - 1))
105+
return self.palettes[paletteIndex]
106+
100107
def setLocation(self, location):
101108
if location is None:
102109
location = {}

Lib/blackrenderer/render.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def renderText(
2525
margin=20,
2626
features=None,
2727
variations=None,
28+
paletteIndex=0,
2829
backendName=None,
2930
lang=None,
3031
script=None,
@@ -44,6 +45,7 @@ def renderText(
4445
buf.language = lang
4546
if variations:
4647
font.setLocation(variations)
48+
palette = font.getPalette(paletteIndex)
4749

4850
hb.shape(font.hbFont, buf, features)
4951

@@ -73,7 +75,7 @@ def renderText(
7375
for glyph in glyphLine:
7476
with canvas.savedState():
7577
canvas.translate(glyph.xOffset, glyph.yOffset)
76-
font.drawGlyph(glyph.name, canvas)
78+
font.drawGlyph(glyph.name, canvas, palette=palette)
7779
canvas.translate(glyph.xAdvance, glyph.yAdvance)
7880

7981
if outputPath is not None:

Tests/data/Nabla.subset.ttf

5.11 KB
Binary file not shown.
11.5 KB
Loading
Loading
11.5 KB
Loading
Loading
10 KB
Loading
Loading
10.1 KB
Loading
+34
Loading

Tests/test_glyph_render.py

+78-69
Original file line numberDiff line numberDiff line change
@@ -29,97 +29,106 @@
2929
"crash": dataDir / "crash.subset.otf",
3030
"nested_paintglyph": dataDir / "nested-paintglyph.ttf",
3131
"ftvartest": dataDir / "TestVariableCOLR-VF.ttf",
32+
"nabla": dataDir / "Nabla.subset.ttf",
3233
}
3334

3435

3536
test_glyphs = [
36-
("noto", "uni2693", None),
37-
("noto", "uni2694", None),
38-
("noto", "u1F30A", None),
39-
("noto", "u1F943", None),
40-
("mutator", "B", None),
41-
("mutator", "D", {"wdth": 1000}),
42-
("twemoji", "uni3299", None),
43-
("more_samples", "cross_glyph", None),
44-
("more_samples", "skew_0_15_center_500.0_500.0", None),
45-
("more_samples", "skew_-10_20_center_500.0_500.0", None),
46-
("more_samples", "skew_-10_20_center_1000_1000", None),
47-
("more_samples", "transform_matrix_1_0_0_1_125_125", None),
48-
("more_samples", "transform_matrix_1.5_0_0_1.5_0_0", None),
49-
("more_samples", "transform_matrix_0.9659_0.2588_-0.2588_0.9659_0_0", None),
50-
("more_samples", "transform_matrix_1.0_0.0_0.6_1.0_-300.0_0.0", None),
51-
("more_samples", "clip_box_top_left", None),
52-
("more_samples", "clip_box_bottom_left", None),
53-
("more_samples", "clip_box_bottom_right", None),
54-
("more_samples", "clip_box_top_right", None),
55-
("more_samples", "clip_box_center", None),
56-
("more_samples", "composite_DEST_OVER", None),
57-
("more_samples", "composite_XOR", None),
58-
("more_samples", "composite_OVERLAY", None),
59-
("more_samples", "composite_SRC_IN", None),
60-
("more_samples", "composite_PLUS", None),
61-
("more_samples", "composite_LIGHTEN", None),
62-
("more_samples", "composite_MULTIPLY", None),
63-
("more_samples", "clip_shade_center", None),
64-
("more_samples", "clip_shade_top_left", None),
65-
("more_samples", "clip_shade_bottom_left", None),
66-
("more_samples", "clip_shade_bottom_right", None),
67-
("more_samples", "clip_shade_top_right", None),
68-
("more_samples", "inset_clipped_radial_reflect", None),
69-
# ("more_samples", "sweep", None),
70-
("more_samples", "transformed_sweep", None),
71-
("more_samples", "composite_colr_glyph", None),
72-
("more_samples", "linear_repeat_0_1", None),
73-
("more_samples", "linear_repeat_0.2_0.8", None),
74-
("more_samples", "linear_repeat_0_1.5", None),
75-
("more_samples", "linear_repeat_0.5_1.5", None),
76-
("more_samples", "scale_0.5_1.5_center_500.0_500.0", None),
77-
("more_samples", "scale_1.5_1.5_center_500.0_500.0", None),
78-
("more_samples", "scale_0.5_1.5_center_0_0", None),
79-
("more_samples", "scale_1.5_1.5_center_0_0", None),
80-
("more_samples", "scale_0.5_1.5_center_1000_1000", None),
81-
("more_samples", "scale_1.5_1.5_center_1000_1000", None),
82-
("more_samples", "linear_gradient_extend_mode_pad", None),
83-
("more_samples", "linear_gradient_extend_mode_repeat", None),
84-
("more_samples", "linear_gradient_extend_mode_reflect", None),
85-
("more_samples", "radial_gradient_extend_mode_pad", None),
86-
("more_samples", "radial_gradient_extend_mode_repeat", None),
87-
("more_samples", "radial_gradient_extend_mode_reflect", None),
88-
("more_samples", "rotate_10_center_0_0", None),
89-
("more_samples", "rotate_-10_center_1000_1000", None),
90-
("more_samples", "rotate_25_center_500.0_500.0", None),
91-
("more_samples", "rotate_-15_center_500.0_500.0", None),
92-
("more_samples", "skew_25_0_center_0_0", None),
93-
("more_samples", "skew_25_0_center_500.0_500.0", None),
94-
("more_samples", "skew_0_15_center_0_0", None),
95-
("more_samples", "upem_box_glyph", None),
96-
("nested_paintglyph", "A", None),
97-
("ftvartest", "A", {"wght": 400}),
98-
("ftvartest", "A", {"wght": 700}),
99-
("ftvartest", "B", {"wght": 400}),
100-
("ftvartest", "B", {"wght": 700}),
37+
("noto", "uni2693", None, 0),
38+
("noto", "uni2694", None, 0),
39+
("noto", "u1F30A", None, 0),
40+
("noto", "u1F943", None, 0),
41+
("mutator", "B", None, 0),
42+
("mutator", "D", {"wdth": 1000}, 0),
43+
("twemoji", "uni3299", None, 0),
44+
("more_samples", "cross_glyph", None, 0),
45+
("more_samples", "skew_0_15_center_500.0_500.0", None, 0),
46+
("more_samples", "skew_-10_20_center_500.0_500.0", None, 0),
47+
("more_samples", "skew_-10_20_center_1000_1000", None, 0),
48+
("more_samples", "transform_matrix_1_0_0_1_125_125", None, 0),
49+
("more_samples", "transform_matrix_1.5_0_0_1.5_0_0", None, 0),
50+
("more_samples", "transform_matrix_0.9659_0.2588_-0.2588_0.9659_0_0", None, 0),
51+
("more_samples", "transform_matrix_1.0_0.0_0.6_1.0_-300.0_0.0", None, 0),
52+
("more_samples", "clip_box_top_left", None, 0),
53+
("more_samples", "clip_box_bottom_left", None, 0),
54+
("more_samples", "clip_box_bottom_right", None, 0),
55+
("more_samples", "clip_box_top_right", None, 0),
56+
("more_samples", "clip_box_center", None, 0),
57+
("more_samples", "composite_DEST_OVER", None, 0),
58+
("more_samples", "composite_XOR", None, 0),
59+
("more_samples", "composite_OVERLAY", None, 0),
60+
("more_samples", "composite_SRC_IN", None, 0),
61+
("more_samples", "composite_PLUS", None, 0),
62+
("more_samples", "composite_LIGHTEN", None, 0),
63+
("more_samples", "composite_MULTIPLY", None, 0),
64+
("more_samples", "clip_shade_center", None, 0),
65+
("more_samples", "clip_shade_top_left", None, 0),
66+
("more_samples", "clip_shade_bottom_left", None, 0),
67+
("more_samples", "clip_shade_bottom_right", None, 0),
68+
("more_samples", "clip_shade_top_right", None, 0),
69+
("more_samples", "inset_clipped_radial_reflect", None, 0),
70+
("more_samples", "transformed_sweep", None, 0),
71+
("more_samples", "composite_colr_glyph", None, 0),
72+
("more_samples", "linear_repeat_0_1", None, 0),
73+
("more_samples", "linear_repeat_0.2_0.8", None, 0),
74+
("more_samples", "linear_repeat_0_1.5", None, 0),
75+
("more_samples", "linear_repeat_0.5_1.5", None, 0),
76+
("more_samples", "scale_0.5_1.5_center_500.0_500.0", None, 0),
77+
("more_samples", "scale_1.5_1.5_center_500.0_500.0", None, 0),
78+
("more_samples", "scale_0.5_1.5_center_0_0", None, 0),
79+
("more_samples", "scale_1.5_1.5_center_0_0", None, 0),
80+
("more_samples", "scale_0.5_1.5_center_1000_1000", None, 0),
81+
("more_samples", "scale_1.5_1.5_center_1000_1000", None, 0),
82+
("more_samples", "linear_gradient_extend_mode_pad", None, 0),
83+
("more_samples", "linear_gradient_extend_mode_repeat", None, 0),
84+
("more_samples", "linear_gradient_extend_mode_reflect", None, 0),
85+
("more_samples", "radial_gradient_extend_mode_pad", None, 0),
86+
("more_samples", "radial_gradient_extend_mode_repeat", None, 0),
87+
("more_samples", "radial_gradient_extend_mode_reflect", None, 0),
88+
("more_samples", "rotate_10_center_0_0", None, 0),
89+
("more_samples", "rotate_-10_center_1000_1000", None, 0),
90+
("more_samples", "rotate_25_center_500.0_500.0", None, 0),
91+
("more_samples", "rotate_-15_center_500.0_500.0", None, 0),
92+
("more_samples", "skew_25_0_center_0_0", None, 0),
93+
("more_samples", "skew_25_0_center_500.0_500.0", None, 0),
94+
("more_samples", "skew_0_15_center_0_0", None, 0),
95+
("more_samples", "upem_box_glyph", None, 0),
96+
("nested_paintglyph", "A", None, 0),
97+
("ftvartest", "A", {"wght": 400}, 0),
98+
("ftvartest", "A", {"wght": 700}, 0),
99+
("ftvartest", "B", {"wght": 400}, 0),
100+
("ftvartest", "B", {"wght": 700}, 0),
101+
("nabla", "A", None, 0),
102+
("nabla", "A", None, 1),
101103
]
102104

103105

104-
@pytest.mark.parametrize("fontName, glyphName, location", test_glyphs)
106+
@pytest.mark.parametrize("fontName, glyphName, location, paletteIndex", test_glyphs)
105107
@pytest.mark.parametrize("backendName, surfaceClass", backends)
106-
def test_renderGlyph(backendName, surfaceClass, fontName, glyphName, location):
108+
def test_renderGlyph(
109+
backendName, surfaceClass, fontName, glyphName, location, paletteIndex
110+
):
107111
font = BlackRendererFont(testFonts[fontName])
108112
font.setLocation(location)
109113

110114
scaleFactor = 1 / 4
111115
boundingBox = font.getGlyphBounds(glyphName)
112116
boundingBox = scaleRect(boundingBox, scaleFactor, scaleFactor)
113117
boundingBox = intRect(boundingBox)
118+
palette = font.getPalette(paletteIndex)
114119

115120
surface = surfaceClass()
116121
ext = surface.fileExtension
117122
with surface.canvas(boundingBox) as canvas:
118123
canvas.scale(scaleFactor)
119-
font.drawGlyph(glyphName, canvas)
124+
font.drawGlyph(glyphName, canvas, palette=palette)
120125

121126
locationString = "_" + _locationToString(location) if location else ""
122-
fileName = f"glyph_{fontName}_{glyphName}{locationString}_{backendName}{ext}"
127+
paletteString = "_" + str(paletteIndex) if paletteIndex else ""
128+
fileName = (
129+
f"glyph_{fontName}_{glyphName}{locationString}{paletteString}"
130+
f"_{backendName}{ext}"
131+
)
123132
expectedPath = expectedOutputDir / fileName
124133
outputPath = tmpOutputDir / fileName
125134
surface.saveImage(outputPath)

0 commit comments

Comments
 (0)