def glyph(self, componentFlags=0x4): assert self._isClosed(), "Didn't close last contour." components = [] for glyphName, transformation in self.components: if self.points: # can't have both, so decompose the glyph tpen = TransformPen(self, transformation) self.glyphSet[glyphName].draw(tpen) continue component = GlyphComponent() component.glyphName = glyphName if transformation[:4] != (1, 0, 0, 1): component.transform = (transformation[:2], transformation[2:4]) component.x, component.y = transformation[4:] component.flags = componentFlags components.append(component) glyph = Glyph() glyph.coordinates = GlyphCoordinates(self.points) glyph.endPtsOfContours = self.endPts glyph.flags = array("B", self.types) self.init() if components: glyph.components = components glyph.numberOfContours = -1 else: glyph.numberOfContours = len(glyph.endPtsOfContours) glyph.program = ttProgram.Program() glyph.program.fromBytecode(b"") return glyph
def makeTTFGlyph(polygons): result = Glyph() result.numberOfContours = len(polygons) result.coordinates = GlyphCoordinates([coordinate for polygon in polygons for coordinate in polygon]) result.flags = array.array("B", [1] * len(result.coordinates)) result.endPtsOfContours = [sum(len(polygon) for polygon in polygons[:idx + 1]) - 1 for idx in range(len(polygons))] result.program = ttProgram.Program() result.program.assembly = [] return result
def glyph(self): glyph = Glyph() glyph.coordinates = GlyphCoordinates(self.points) glyph.endPtsOfContours = self.endPts glyph.flags = array("B", self.types) glyph.components = self.components if glyph.components: assert not glyph.endPtsOfContours, "TrueType glyph can't have both contours and components." glyph.numberOfContours = -1 else: glyph.numberOfContours = len(glyph.endPtsOfContours) glyph.program = ttProgram.Program() glyph.program.fromBytecode("") return glyph
def glyph(self, componentFlags=0x4): """Returns a :py:class:`~._g_l_y_f.Glyph` object representing the glyph.""" assert self._isClosed(), "Didn't close last contour." components = self._buildComponents(componentFlags) glyph = Glyph() glyph.coordinates = GlyphCoordinates(self.points) glyph.coordinates.toInt() glyph.endPtsOfContours = self.endPts glyph.flags = array("B", self.types) self.init() if components: glyph.components = components glyph.numberOfContours = -1 else: glyph.numberOfContours = len(glyph.endPtsOfContours) glyph.program = ttProgram.Program() glyph.program.fromBytecode(b"") return glyph
def _make_fontfile_with_OS2(*, version, **kwargs): upem = 1000 glyphOrder = [".notdef", "a"] cmap = {0x61: "a"} glyphs = {gn: Glyph() for gn in glyphOrder} hmtx = {gn: (500, 0) for gn in glyphOrder} names = {"familyName": "TestOS2", "styleName": "Regular"} fb = FontBuilder(unitsPerEm=upem) fb.setupGlyphOrder(glyphOrder) fb.setupCharacterMap(cmap) fb.setupGlyf(glyphs) fb.setupHorizontalMetrics(hmtx) fb.setupHorizontalHeader() fb.setupNameTable(names) fb.setupOS2(version=version, **kwargs) return _compile(fb.font)
def glyph(self, componentFlags=0x4): assert self._isClosed(), "Didn't close last contour." components = self._buildComponents(componentFlags) glyph = Glyph() glyph.coordinates = GlyphCoordinates(self.points) glyph.endPtsOfContours = self.endPts glyph.flags = array("B", self.types) self.init() if components: glyph.components = components glyph.numberOfContours = -1 else: glyph.numberOfContours = len(glyph.endPtsOfContours) glyph.program = ttProgram.Program() glyph.program.fromBytecode(b"") return glyph
def coordinates_report(glyph: Glyph, glyf_table: Any, nocolor: bool) -> str: """ Returns a coordinates report string from glyph-level parameter data. """ if glyph.numberOfContours > 0: coords, endpoints, flags = glyph.getCoordinates(glyf_table) endpoint_coordinates = [coords[endpoint] for endpoint in endpoints] coordinates_string: str = "" for x, coord in enumerate(coords): # on- and off-curve points are defined in # the `flags` integer array that are mapped # 1:1 to coordinate indices # test the on curve bit mask here and # use this to define the appropriate # string value in the report on_off = ON_OFF[flags[x] & FLAG_ON_CURVE] # this is a start coordinate if it # (1) is the first coordinate in the iterable # (2) follows a previous endpoint coordinate start_coord = (x == 0) or (coords[x - 1] in endpoint_coordinates) if start_coord: coordinates_string += ( f"{str(coord): >13} " f"{green_text(START_STRING, nocolor=nocolor): <13}{os.linesep}" ) # end coordinates are defined by the indices returned # in the Glyph.getGlyphCoordinates method return tuple # compare current test coordinate with those coordinate # values elif coords[x] in endpoint_coordinates: coordinates_string += ( f"{str(coord): >13} " f"{red_text(END_STRING, nocolor=nocolor): <13}{os.linesep}" ) else: coordinates_string += f"{str(coord): >13} {on_off: >13}{os.linesep}" return coordinates_string else: return f" No contours{os.linesep}"
def _get_components_with_transforms(glyph: Glyph) -> Sequence[Tuple]: """ Returns list with component glyph names and x,y transforms for composite glyphs with transforms. In all other cases, returns an empty list """ components_with_transforms = [] if glyph.isComposite(): for component in glyph.components: if hasattr(component, "transform"): # transform attribute will include one of the # following data sets: # (1) simple X and Y scale only @ [0][0] # (2) x-scale @ [0][0] and y-scale @ [1][1] # (3) x-scale @ [0][0], scale01 @ [0][1], # y-scale @ [1][1], scale10 @ [1][0] a1 = round(component.transform[0][0], 3) a2 = round(component.transform[0][1], 3) b1 = round(component.transform[1][0], 3) b2 = round(component.transform[1][1], 3) components_with_transforms.append( (component.glyphName, [[a1, a2], [b1, b2]])) return components_with_transforms
def componentsOverlap(glyph: _g_l_y_f.Glyph, glyphSet: _TTGlyphMapping) -> bool: if not glyph.isComposite(): raise ValueError( "This method only works with TrueType composite glyphs") if len(glyph.components) < 2: return False # single component, no overlaps component_paths = {} def _get_nth_component_path(index: int) -> pathops.Path: if index not in component_paths: component_paths[index] = skPathFromGlyphComponent( glyph.components[index], glyphSet) return component_paths[index] return any( pathops.op( _get_nth_component_path(i), _get_nth_component_path(j), pathops.PathOp.INTERSECTION, fix_winding=False, keep_starting_points=False, ) for i, j in itertools.combinations(range(len(glyph.components)), 2))
def _copyGlyph(glyph, glyfTable): glyph = TTGlyph(glyph.compile(glyfTable)) glyph.expand(glyfTable) return glyph
for key, value in data.items(): dflt, mn, mx = value["default"], value["min"], value["max"] try: f = 1.0 * (dflt - mn) / (mx - mn) except ZeroDivisionError: f = 0 # TEST THIS loc = (xtramax - xtramin) * f + xtramin location = {"XOPQ": xopq, "YOPQ": yopq, "XTRA": loc} tempFont = instantiateVariableFont(ttFont, location) tempglyph = tempFont.getGlyphSet()[key] glyf[key] = Glyph() pen = TTGlyphPen(None) tempglyph.draw(pen) glyf[key] = pen.glyph() """ #print "Removing GX tables" for tag in ('fvar','avar','gvar'): if tag in gradeFont: del gradeFont[tag] """ import os TARGET_DIR = "" fileName = "RobotoDelta-GRADmax.ttf"
def get_tt_glyph(self): """Return a special TT Glyph record for the sbix format. It contains two dummy contours with one point (bottom left and top right) each.""" # make dummy contours glyph = TTGlyph() glyph.program = NoProgram() glyph.numberOfContours = 0 box = self.get_box() if box is not None: contours = [ [(box[0], box[1], 1)], [(box[2], box[3], 1)], ] for contour in contours: coordinates = [] flags = [] for x, y, flag in contour: if not hasattr(glyph, "xMin"): glyph.xMin = x glyph.yMin = y glyph.xMax = x glyph.yMax = y else: glyph.xMin = min(glyph.xMin, x) glyph.yMin = min(glyph.yMin, y) glyph.xMax = max(glyph.xMax, x) glyph.yMax = max(glyph.yMax, y) coordinates.append([x, y]) flags.append(flag) coordinates = GlyphCoordinates(coordinates) flags = array.array("B", flags) if not hasattr(glyph, "coordinates"): glyph.coordinates = coordinates glyph.flags = flags glyph.endPtsOfContours = [len(coordinates)-1] else: glyph.coordinates.extend(coordinates) glyph.flags.extend(flags) glyph.endPtsOfContours.append(len(glyph.coordinates)-1) glyph.numberOfContours += 1 return glyph
def get_tt_glyph(self): """Return a special TT Glyph record for the sbix format. It contains two dummy contours with one point (bottom left and top right) each.""" # make dummy contours glyph = TTGlyph() glyph.program = NoProgram() glyph.numberOfContours = 0 box = self.get_box() if box is not None: contours = [ [(box[0], box[1], 1)], [(box[2], box[3], 1)], ] for contour in contours: coordinates = [] flags = [] for x, y, flag in contour: if not hasattr(glyph, "xMin"): glyph.xMin = x glyph.yMin = y glyph.xMax = x glyph.yMax = y else: glyph.xMin = min(glyph.xMin, x) glyph.yMin = min(glyph.yMin, y) glyph.xMax = max(glyph.xMax, x) glyph.yMax = max(glyph.yMax, y) coordinates.append([x, y]) flags.append(flag) coordinates = GlyphCoordinates(coordinates) flags = array.array("B", flags) if not hasattr(glyph, "coordinates"): glyph.coordinates = coordinates glyph.flags = flags glyph.endPtsOfContours = [len(coordinates) - 1] else: glyph.coordinates.extend(coordinates) glyph.flags.extend(flags) glyph.endPtsOfContours.append(len(glyph.coordinates) - 1) glyph.numberOfContours += 1 return glyph
def quadratic_path(glyph: Glyph, glyf_table: Any, include_implied=False) -> List[Coordinate]: """ Returns a list of datastructure.Coordinate objects for a quadratic curve path without implied on-curve points by default. Implied on-curve points are calculated and added to the path when the include_implied parameter is set to `True`. Note: composite fontTools.ttLib.tables._g_l_y_f.Glyph *must* be decomposed before they are passed to this function. See https://stackoverflow.com/a/20772557/2848172 for detailed information about implied on-curve points, loss of information, and the TTF binary quadratic curve specification. """ coords, endpoints, flags = glyph.getCoordinates(glyf_table) endpoint_coordinates = [coords[endpoint] for endpoint in endpoints] new_coords: List[Coordinate] = [] for x, coord in enumerate(coords): # on- and off-curve points are defined in # the `flags` integer array that are mapped # 1:1 to coordinate indices on_curve: bool = (flags[x] & FLAG_ON_CURVE) != 0 # this is a start coordinate if it # (1) is the first coordinate in the iterable # (2) follows a previous endpoint coordinate start_coord: bool = (x == 0) or (coords[x - 1] in endpoint_coordinates) # this is an end coordinate if the coordinate tuple is in # endpoint_coordinates end_coord: bool = coord in endpoint_coordinates # cannot be both start and end point assert not (end_coord and start_coord) if include_implied: if not on_curve: # there should always be a previous point in the contour # if this is *not* an on-curve point last_on_curve = (flags[x - 1] & FLAG_ON_CURVE) != 0 if not last_on_curve: last = coords[x - 1] # get the implied on curve point between two off curve points implied_coordinate = midpoint_between_coordinates( last, coord) new_implied_coordinate = Coordinate( implied_coordinate[0], implied_coordinate[1], oncurve=True, startpoint=False, endpoint=False, implied=True, ) # add *new* implied coordinate that was not in the original # coordinate set # set next coordinate attr in the last coordinate if there is one if len(new_coords) > 0 and not new_coords[-1].endpoint: new_coords[-1].set_coord_next(new_implied_coordinate) new_implied_coordinate.set_coord_previous( new_coords[-1]) # add the new implied coordinate new_coords.append(new_implied_coordinate) # add current coordinate object from this # iteration on the original coordinate set new_coordinate = Coordinate( coord[0], coord[1], oncurve=on_curve, startpoint=start_coord, endpoint=end_coord, implied=False, ) if len(new_coords) > 0 and not new_coords[-1].endpoint: new_coords[-1].set_coord_next(new_coordinate) new_coordinate.set_coord_previous(new_coords[-1]) new_coords.append(new_coordinate) return new_coords
def _newGlyph(self, name, **kwargs): import array layer = self.naked() self._trashPost(layer) layer["hmtx"][name] = (0, 0) layer.glyphOrder.append(name) # newId = layer["maxp"].numGlyphs # layer["maxp"].numGlyphs = newId + 1 if "hdmx" in layer: del (layer["hdmx"]) # Obviously this is wrong. XXX layer["glyf"][name] = Glyph() # XXX Only TTF layer["glyf"][name].numberOfContours = -1 # Only components right now layer["glyf"][name].flags = array.array("B", []) layer["glyf"][name].coordinates = GlyphCoordinates([]) layer["glyf"][name].endPtsOfContours = [] layer["glyf"][name].program = ttProgram.Program() layer["glyf"][name].program.fromBytecode([]) layer["glyf"][name].xMin = 0 layer["glyf"][name].yMin = 0 layer["glyf"][name].xMax = 0 layer["glyf"][name].yMax = 0 return self[name]