Archivo text -> SVG path outlines, one font instance per weight.
| 105 | |
| 106 | |
| 107 | class Outliner: |
| 108 | """Archivo text -> SVG path outlines, one font instance per weight.""" |
| 109 | |
| 110 | def __init__(self): |
| 111 | self._by_weight = {} |
| 112 | |
| 113 | def _font(self, weight): |
| 114 | if weight not in self._by_weight: |
| 115 | ft = TTFont(FONT) |
| 116 | instantiateVariableFont(ft, {"wght": weight}, inplace=True) |
| 117 | self._by_weight[weight] = (ft, ft.getBestCmap(), ft.getGlyphSet(), ft["hmtx"]) |
| 118 | return self._by_weight[weight] |
| 119 | |
| 120 | def measure(self, text, size, weight, track=TRACK): |
| 121 | ft, cmap, _, hmtx = self._font(weight) |
| 122 | scale = size / ft["head"].unitsPerEm |
| 123 | w = sum(hmtx[cmap[ord(ch)]][0] * scale + track for ch in text) |
| 124 | return w - track |
| 125 | |
| 126 | def cap_height(self, size, weight): |
| 127 | ft, _, _, _ = self._font(weight) |
| 128 | return getattr(ft["OS/2"], "sCapHeight", 700) * size / ft["head"].unitsPerEm |
| 129 | |
| 130 | def outline(self, text, size, weight, start_x, baseline_y, track=TRACK): |
| 131 | ft, cmap, glyphs, hmtx = self._font(weight) |
| 132 | scale = size / ft["head"].unitsPerEm |
| 133 | sink = SVGPathPen(glyphs, ntos=lambda v: f"{round(v, 2):g}") |
| 134 | pen_x = start_x |
| 135 | for ch in text: |
| 136 | gname = cmap[ord(ch)] |
| 137 | if ch != " ": |
| 138 | t = Transform(scale, 0, 0, -scale, pen_x, baseline_y) |
| 139 | glyphs[gname].draw(TransformPen(sink, t)) |
| 140 | pen_x += hmtx[gname][0] * scale + track |
| 141 | return sink.getCommands() |
| 142 | |
| 143 | def centered(self, text, size, weight, center_x, baseline_y, track=TRACK): |
| 144 | w = self.measure(text, size, weight, track) |
| 145 | return self.outline(text, size, weight, center_x - w / 2, baseline_y, track) |
| 146 | |
| 147 | |
| 148 | def si_glyph(slug): |