def rescale_font(infont_path, outfont_path): def calc_bbox(glyphs): def get_outer_bbox(a, b): x = min(a[0], b[0]) y = min(a[1], b[1]) w = max(a[0] + a[2], b[0] + b[2]) - x h = max(a[1] + a[3], b[1] + b[3]) - y return (x, y, w, h) return reduce(get_outer_bbox, glyphs) font = fontforge.open(infont_path) #ScaleToEm(800, 200); font.ascent = 800 font.descent = 200 #bbox=GetFontBoundingBox() bbox = calc_bbox([g.boundingBox() for g in font.glyphs()]) #print "bbox=", bbox #SelectAll(); font.selection.all() #Move(-bbox[0], -bbox[1]); #Scale(100*1000/(bbox[3]-bbox[1]), 0, 0); #Move(0, -200); matrix1 = psMat.translate(-bbox[0], -bbox[1]) matrix2 = psMat.scale(1000.0 / (bbox[3] - bbox[1])) matrix3 = psMat.translate(0, -200) transform_matrix = psMat.compose(psMat.compose(matrix1, matrix2), matrix3) font.transform(transform_matrix) font.generate(outfont_path)
def make_new_glyph(text, scale, list_x, y, new_glyph_codepoint, is_first_glyph, font): for index, char in enumerate(list(text)): if is_first_glyph: # 一文字目だけは、コピペして位置とサイズを変更(全ての文字が参照だと、上手くいかないっぽいので) char_codepoint = ord(char) font.selection.select(char_codepoint) font.copy() font.selection.select(('more', 'unicode'), new_glyph_codepoint) font.paste() matrix = psMat.compose(psMat.scale(scale), psMat.translate(list_x[index], y)) font[new_glyph_codepoint].transform(matrix) is_first_glyph = False else: # 親文字の二文字目以降は、文字の位置とサイズを設定して参照を追加 matrix = psMat.compose(psMat.scale(scale), psMat.translate(list_x[index], y)) char_codepoint = ord(char) font[new_glyph_codepoint].addReference( font[char_codepoint].glyphname, matrix) return is_first_glyph, font
def resolveReferences(self, owner, scale, anchorMap): self.glyph = self.font.temporary['names'][self.name] glyph = self.glyph.glyph if self.parent: pglyph = self.parent.glyph.glyph (px, py) = getanchor(pglyph, self.at) if px is None: px = pglyph.width / 2 (cx, cy) = getanchor(glyph, self.withap, isMark=True) if cx is None: cx = glyph.width / 2 self.scale = psMat.compose(self.scale, psMat.translate(px - cx, py - cy)) if self.rsb is not None: self.advance = glyph.width - (self.rsb - pointMult( (self.rsb, 0), self.scale)[0]) self.setGlyphAnchors(glyph, anchorMap) # after attached scale established scale = psMat.compose(scale, self.scale) owner.addRealGlyph(self.glyph, scale) for c in self.children: c.resolveReferences(owner, scale, anchorMap) if self.parent: self.parent.mergeAnchors(self.anchors) else: owner.mergeAnchors(self.anchors, scale) return pointMult( (self.advance if self.advance is not None else glyph.width, 0), self.scale)[0]
def rescale_font(infont_path, outfont_path): def calc_bbox(glyphs): def get_outer_bbox(a, b): x = min(a[0], b[0]) y = min(a[1], b[1]) w = max(a[0] + a[2], b[0] + b[2]) - x h = max(a[1] + a[3], b[1] + b[3]) - y return (x, y, w, h) return reduce(get_outer_bbox, glyphs) font = fontforge.open(infont_path) #ScaleToEm(800, 200); font.ascent = 800 font.descent = 200 #bbox=GetFontBoundingBox() bbox = calc_bbox([g.boundingBox() for g in font.glyphs()]) #print "bbox=", bbox #SelectAll(); font.selection.all() #Move(-bbox[0], -bbox[1]); #Scale(100*1000/(bbox[3]-bbox[1]), 0, 0); #Move(0, -200); matrix1 = psMat.translate(-bbox[0], -bbox[1]) matrix2 = psMat.scale(1000.0/(bbox[3]-bbox[1])) matrix3 = psMat.translate(0, -200) transform_matrix = psMat.compose(psMat.compose(matrix1, matrix2), matrix3) font.transform(transform_matrix) font.generate(outfont_path)
def copyglyph(font, infont, g, u, args) : extras = set() if args.scale is None : scale = psMat.identity() else : scale = psMat.scale(args.scale) o = font.findEncodingSlot(u) if o == -1 : glyph = font.createChar(u, g.glyphname) else : glyph = font[o] if len(g.references) == 0 : font.selection.select(glyph) pen = glyph.glyphPen() g.draw(pen) glyph.transform(scale) else : for r in g.references : t = psMat.compose(r[1], scale) newt = psMat.compose(psMat.identity(), psMat.translate(t[4], t[5])) glyph.addReference(r[0], newt) extras.add(r[0]) glyph.width = g.width * scale[0] if args.anchors : for a in g.anchorPoints : try : l = font.getSubtableOfAnchor(a[1]) except EnvironmentError : font.addAnchorClass("", a[0]*scale[0], a[1]*scale[3]) glyph.anchorPoints = g.anchorPoints return list(extras)
def geometric_triangle(rotation=0, black=True): sw = 46 xm = 37 b = bb + (bh - bw + 2 * xm) / 2.0 t = b + (bw - 2 * xm) * math.sin(math.pi / 3) contours = [] c = fontforge.contour() c.moveTo(bl + xm, b) c.lineTo(cx, t) c.lineTo(br - xm, b) c.closed = True contours.append(c) if not black: xm += sw * math.sqrt(3) c = fontforge.contour() c.moveTo(bl + xm, b + sw) c.lineTo(br - xm, b + sw) c.lineTo(cx, t - 2 * sw) c.closed = True contours.append(c) if rotation: cy = (b + t) / 2.0 rotT = psMat.compose(psMat.translate(-cx, -cy), psMat.compose(psMat.rotate(rotation), psMat.translate(cx, cy))) for c in contours: c.transform(rotT) return contours
def aroundCentroid(glyph, xform): xLo, yLo, xHi, yHi = glyph.layers[1].boundingBox( ) # layers[1] is foreground xShift = (xHi - xLo) / 2 + xLo yShift = (yHi - yLo) / 2 + yLo into = mat.translate(-xShift, -yShift) outof = mat.translate(xShift, yShift) return mat.compose(mat.compose(into, xform), outof)
def blackboard(font, bbold): dst = "𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ" \ + "𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫" \ + "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡" \ + "ℼℽℾℿ⅀" \ + "⨾∘" src = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") \ + list("abcdefghijklmnopqrstuvwxyz") \ + list("0123456789") \ + [chr(x) for x in [25, 13, 0, 5, 6]] \ + list(";=") adjust = { '𝕗': mat.translate(-100, 0), '𝕛': mat.translate(90, 0), '∘': mat.translate(0, 95), '⨾': mat.translate(0, 95), } for i in range(0, len(dst)): bbold.selection.select(("singletons", ), ord(src[i])) bbold.copy() font.selection.select(("singletons", ), ord(dst[i])) font.paste() glyph = font[ord(dst[i])] xform = moveToMonoNoSquash(glyph) if dst[i] in adjust: xform = mat.compose(xform, adjust[dst[i]]) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize
def space_glyph_by_anchors(bitbucket, glyph = None): if glyph == None: glyph = bitbucket lspace = left_spacing(glyph) rspace = right_spacing(glyph) if lspace != None and rspace != None: for r in glyph.references: glyph.useRefsMetrics(r[0], False) glyph.transform((1, 0, 0, 1, -lspace, 0)) glyph.width = rspace - lspace # Translate all references to this glyph, as well, but in the # opposite direction. if lspace != 0: f = glyph.font for glyph_name in f: g = f[glyph_name] references = list(g.references) refs_changed = False for i in range(0, len(references)): if references[i][0] == glyph.glyphname: references[i] = (references[i][0], psMat.compose(references[i][1], (1, 0, 0, 1, lspace, 0))) refs_changed = True if refs_changed: g.references = tuple(references)
def _add_braille(font): font.selection.select(BLACK_CIRCLE) font.copy() font.selection.select(PRIVATE) font.paste() font.selection.select(PRIVATE) move_to_origin = translate(-512, -582.4) make_small = scale(BRAILLE_DIAMETER) for glyph in list(font.selection.byGlyphs): glyph.transform(compose(move_to_origin, make_small)) with BRAILLE_JSON.open() as f: braille = load(f) for b in braille: for p in b['points']: point = BRAILLE_POINTS[p] font.selection.select(PRIVATE) for glyph in list(font.selection.byGlyphs): glyph.transform(translate(point[0], point[1])) font.copy() font.selection.select(int(b['code'], 16)) font.pasteInto() for glyph in list(font.selection.byGlyphs): glyph.width = WIDTH font.selection.select(PRIVATE) for glyph in list(font.selection.byGlyphs): glyph.transform(translate(-point[0], -point[1])) font.selection.select(PRIVATE) font.cut()
def _transform_sym(symfont, info): x_ratio = 1.0 y_ratio = 1.0 x_diff = 0 y_diff = 0 if info["name"] == "Seti-UI + Custom": x_ratio = 1.1 y_ratio = 1.1 x_diff = -100 y_diff = -450 elif info["name"] == "Devicons": x_ratio = 1.05 y_ratio = 1.05 x_diff = -100 y_diff = -250 elif info["name"] in ["Powerline Symbols", "Powerline Extra Symbols"]: x_ratio = 0.95 y_ratio = 0.88 x_diff = 0 y_diff = -30 elif info["name"] == "Font Linux": y_diff = -120 elif info["name"] == "Font Awesome Extension": y_diff = -400 elif info["name"] == "Pomicons": x_ratio = 1.2 y_ratio = 1.2 x_diff = -200 y_diff = -300 elif info["name"] == "Octicons": x_ratio = 0.95 y_ratio = 0.95 x_diff = 30 y_diff = -100 elif info["name"] == "Material": x_ratio = 1.1 y_ratio = 1.1 x_diff = -50 y_diff = -250 elif info["name"] == "Codicons": x_ratio = 0.85 y_ratio = 0.85 x_diff = 35 y_diff = -300 scale = psMat.scale(x_ratio, y_ratio) translate = psMat.translate(x_diff, y_diff) transform = psMat.compose(scale, translate) symfont.transform(transform)
def _hankaku_glyphs(font): origin = translate(-DESCENT, 0) # scale will scale glyphs with the origin (0, DESCENT) scl = scale(SCALE_DOWN) # original glyphs have width to fit this size. orig_glyph_width = WIDTH - DESCENT * 2 glyph_width = float(orig_glyph_width) * SCALE_DOWN trans_x = (WIDTH / 2 - glyph_width) / 2 trans_y = (WIDTH - glyph_width) / 2 - DESCENT trans = translate(trans_x, trans_y) mat = compose(compose(origin, scl), trans) font.selection.none() for i in HANKAKU_GLYPHS: font.selection.select(("more", "unicode"), i) for glyph in font.selection.byGlyphs: glyph.transform(mat) glyph.width = WIDTH / 2
def reference_transform(ref, glyph, deg): thatglyphname = ref[0] thisglyphname = glyph.glyphname r = ref[1] column_a = "%s's reference to %s:" % (thisglyphname, thatglyphname) column_b = str(r) ri = psMat.inverse(r) result = psMat.identity() result = psMat.compose(result, italic_shift_right(deg)) result = psMat.compose(result, italic_unskew(deg)) result = psMat.compose(result, r) result = psMat.compose(result, italic_skew(deg)) result = psMat.compose(result, italic_shift_left(deg)) return (ref[0], result)
def referenceTransform(ref, glyph, deg): thatglyphname = ref[0] thisglyphname = glyph.glyphname r = ref[1] columnA = "%s's reference to %s:" % (thisglyphname, thatglyphname) columnB = str(r) ri = psMat.inverse(r) result = psMat.identity() result = psMat.compose(result, italicShiftRight(deg)) result = psMat.compose(result, italicUnskew(deg)) result = psMat.compose(result, r) result = psMat.compose(result, italicSkew(deg)) result = psMat.compose(result, italicShiftLeft(deg)) return (ref[0], result)
def _set_proportion(font): scale = psMat.scale(SCALE_DOWN) font.selection.all() for glyph in list(font.selection.byGlyphs): x_to_center = X_TO_CENTER trans = psMat.translate(x_to_center, 0) mat = psMat.compose(scale, trans) glyph.transform(mat) glyph.width = EM
def follow_references(glyph): f = glyph.font leaves = [] for ref in glyph.references: if len(f[ref[0]].references) != 0: subrefs = follow_references(f[ref[0]]) leaves += [(r[0], psMat.compose(r[1], ref[1])) for r in subrefs] else: leaves.append(ref) return leaves
def modify_WM(_f): _f.selection.select(0x57) _f.transform(psMat.scale(0.95, 1.0)) _f.copy() _f.selection.select(0x4d) _f.paste() _f.transform(psMat.compose(psMat.rotate(math.radians(180)), psMat.translate(0, 627))) for g in _f.selection.byGlyphs: g = align_to_center(g) return _f
def _set_proportion(font): scale = psMat.scale(SCALE) font.selection.all() for glyph in list(font.selection.byGlyphs): is_hankaku_kana = glyph.encoding in range(*HANKAKU_KANA) x_to_center = X_TO_CENTER / 2 if is_hankaku_kana else X_TO_CENTER trans = psMat.translate(x_to_center, 0) mat = psMat.compose(scale, trans) glyph.transform(mat) glyph.width = EM / 2 if is_hankaku_kana else EM
def removenestedrefs(char): if outfont[char].references == (): return [(char, psMat.identity())] else: newrefs = [] for (refglyph, transform) in outfont[char].references: subrefs = removenestedrefs(refglyph) for (newglyph, newtrans) in subrefs: newrefs.append((newglyph, psMat.compose(newtrans, transform))) return newrefs
def set_vbearings_line(line): splitted = line.split() ch, method = splitted[0:2] h2v_shift = splitted[2:] c = get_glyph_by_name(ch) f.selection.select(c) f.copy() tag = 'vert' name = c.glyphname tagged_name = "%s.%s" % (name, tag) n = get_glyph_by_name(tagged_name) f.selection.select(n) alt_path = "../../../splitted/%s/%s/vert/u%04X.svg" % ( weight, mod, c.unicode) if os.path.exists(alt_path): n.clear() n.importOutlines(alt_path, ('removeoverlap', 'correctdir')) else: f.paste() if method.find('R') >= 0: rot = psMat.compose( psMat.translate(-em / 2, -ascent + em / 2), psMat.compose(psMat.rotate(-math.pi / 2), psMat.translate(em / 2, ascent - em / 2))) n.transform(rot) if method.find('F') >= 0: flip = psMat.compose( psMat.translate(-em / 2, -ascent + em / 2), psMat.compose(psMat.scale(-1, 1), psMat.translate(em / 2, ascent - em / 2))) n.transform(flip) elif method == 'S': position = weights_position[weight] * 2 x, y = h2v_shift[position:position + 2] sht = psMat.translate(int(x), int(y)) n.transform(sht) n.width = em n.vwidth = em if not (tag in alt_glyphs): alt_glyphs[tag] = [] alt_glyphs[tag].append((name, tagged_name))
def flattenNestedReferences(font, ref, new_transform=(1, 0, 0, 1, 0, 0)): """Flattens nested references by replacing them with the ultimate reference and applying any transformation matrices involved, so that the final font has only simple composite glyphs. This to work around what seems to be an Apple bug that results in ignoring transformation matrix of nested references.""" name = ref[0] transform = ref[1] glyph = font[name] new_ref = [] if glyph.references and glyph.foreground.isEmpty(): for nested_ref in glyph.references: for i in flattenNestedReferences(font, nested_ref, transform): matrix = psMat.compose(i[1], new_transform) new_ref.append((i[0], matrix)) else: matrix = psMat.compose(transform, new_transform) new_ref.append((name, matrix)) return new_ref
def patch(self): for target_font in self.target_fonts: source_font = self.source_font target_font_em_original = target_font.em target_font.em = 2048 target_font.encoding = 'ISO10646' # Rename font if self.rename_font: target_font.familyname += ' for Powerline and Tmux' target_font.fullname += ' for Powerline and Tmux' fontname, style = re.match("^([^-]*)(?:(-.*))?$", target_font.fontname).groups() target_font.fontname = fontname + 'ForPowerlineAndTmux' if style is not None: target_font.fontname += style target_font.appendSFNTName('English (US)', 'Preferred Family', target_font.familyname) target_font.appendSFNTName('English (US)', 'Compatible Full', target_font.fullname) source_bb = source_font['block'].boundingBox() target_bb = [0, 0, 0, 0] target_font_width = 0 # Find the biggest char width and height in the Latin-1 extended range and the box drawing range # This isn't ideal, but it works fairly well - some fonts may need tuning after patching for cp in range(0x00, 0x17f) + range(0x2500, 0x2600): try: bbox = target_font[cp].boundingBox() except TypeError: continue if not target_font_width: target_font_width = target_font[cp].width if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] if bbox[1] < target_bb[1]: target_bb[1] = bbox[1] if bbox[2] > target_bb[2]: target_bb[2] = bbox[2] if bbox[3] > target_bb[3]: target_bb[3] = bbox[3] # Find source and target size difference for scaling x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) y_ratio = (target_bb[3] - target_bb[1]) / (source_bb[3] - source_bb[1]) scale = psMat.scale(x_ratio, y_ratio) # Find source and target midpoints for translating x_diff = target_bb[0] - source_bb[0] y_diff = target_bb[1] - source_bb[1] translate = psMat.translate(x_diff, y_diff) transform = psMat.compose(scale, translate) sys.stderr.write("Source: %i %i %i %i\n" % (source_bb[0], source_bb[1], source_bb[2], source_bb[3])) sys.stderr.write("Target: %i %i %i %i\n" % (target_bb[0], target_bb[1], target_bb[2], target_bb[3])) sys.stderr.write("Offset: %.2f %.2f, Ratio: %.2f %.2f\n" % (x_diff, y_diff, x_ratio, y_ratio))
def _add_bar_to_shade_bottom(font): font.selection.select(LOWER_BLOCK) font.copy() font.selection.select(PRIVATE) font.paste() font.selection.select(LOWER_BLOCK) move_to_origin = translate(0, 208) shrink_to_fit = scale(1.0, 106.0 / 256) move_to_bottom = translate(0, -439) mat = compose(compose(move_to_origin, shrink_to_fit), move_to_bottom) for glyph in list(font.selection.byGlyphs): glyph.transform(mat) font.copy() font.selection.select(DARK_SHADE) font.pasteInto() font.selection.select(PRIVATE) font.cut() font.selection.select(LOWER_BLOCK) font.paste()
def decorate(font, glyph, deco_type): ascent = font.ascent descent = font.descent height = ascent + descent width = glyph.width ratio = 0.95 line_ratio = 0.1 if deco_type in (DECORATE_INVERSE, DECORATE_BOTTOMLINE, DECORATE_TOPLINE, DECORATE_BOTHLINE): trans1 = psMat.translate((-width / 2.0, -(ascent - descent) / 2.0)) scale = psMat.scale(ratio, ratio) trans2 = psMat.translate((width / 2.0, (ascent - descent) / 2.0)) comp = psMat.compose(psMat.compose(trans1, scale), trans2) glyph.transform(comp) glyph.width = width if deco_type == DECORATE_INVERSE: pen = glyph.glyphPen(replace=False) pen.moveTo(0, ascent + height * line_ratio / 2) pen.lineTo(width, ascent + height * line_ratio / 2) pen.lineTo(width, -descent - height * line_ratio / 2) pen.lineTo(0, -descent - height * line_ratio / 2) pen.closePath() if deco_type in (DECORATE_BOTTOMLINE, DECORATE_BOTHLINE): pen = glyph.glyphPen(replace=False) pen.moveTo(0, -descent - height * line_ratio / 2) pen.lineTo(0, -descent + height * line_ratio / 2) pen.lineTo(width, -descent + height * line_ratio / 2) pen.lineTo(width, -descent - height * line_ratio / 2) pen.closePath() if deco_type in (DECORATE_TOPLINE, DECORATE_BOTHLINE): pen = glyph.glyphPen(replace=False) pen.moveTo(0, ascent + height * line_ratio / 2) pen.lineTo(width, ascent + height * line_ratio / 2) pen.lineTo(width, ascent - height * line_ratio / 2) pen.lineTo(0, ascent - height * line_ratio / 2) pen.closePath() glyph.correctDirection()
def circle_at(center, size=1.0): """Create a Fontforge contour, in the shape of a circle, centered at the given point. Second parameter, optional, is the radius of the circle in em units. If not specified, the radius will default to 1.""" x = ux(center) y = uy(center) # psMat is a fontforge module to help in creating transformation matrices matrix = psMat.translate(x,y) if size != 1.0: matrix = psMat.compose(psMat.scale(size), matrix) unitcircle = fontforge.unitShape(0) unitcircle.transform(matrix) return unitcircle
def resolveReferences(self, anchorMap): for p, v in self.points.items(): # sort out config defined points anchorMap[p][0] == "basemark" self.points[p] = (None, v[1], v[0]) scale = psMat.identity() for c in self.children: adv = c.resolveReferences(self, scale, anchorMap) scale = psMat.compose(scale, psMat.translate(adv, 0)) if self.advance < 0: if self.glyph: self.advance = pointMult((self.glyph.width, 0), self.scale)[0] else: self.advance = adv if self.advance < 0: self.advance = 0
def add_gopher(_f): gopher = fontforge.open(os.path.join(SOURCE, 'gopher.sfd')) for g in gopher.glyphs(): if g.isWorthOutputting: gopher.selection.select(0x40) gopher.copy() _f.selection.select(0xE160) _f.paste() g.transform(psMat.compose(psMat.scale(-1, 1), psMat.translate(g.width, 0))) gopher.copy() _f.selection.select(0xE161) _f.paste() gopher.close() return _f
def readXml(self, elem): for e in elem: if e.tag == "advance": self.advance = int(e.get("width", None)) elif e.tag == "rsb": self.rsb = int(e.get("rsb", None)) elif e.tag == "lsb": self.lsb = int(e.get("lsb", None)) elif e.tag == "shift": self.scale = psMat.compose( self.scale, psMat.translate(int(e.get("x", 0)), int(e.get("y", 0)))) elif e.tag == "scale": self.scale = psMat.compose( self.scale, psMat.scale(float(e.get("x", 1.)), float(e.get("y", 1.)))) elif e.tag == "attach": (font, name) = findGlyph(e) child = GlyphRef(font, name, parent=self, at=e.get("at"), withap=e.get("with")) child.readXml(e)
def normalizeGlyph(g, letter): bb = g.boundingBox() #compact box that contains the letter dy = bb[3] - bb[1] # xmin,ymin,xmax,ymax newScale = 800 / (dy) transforms = deque() #shifting if letter in SPECIAL_Y_TRANS: transforms.append( psMat.translate(-bb[0], -bb[1] - SPECIAL_Y_TRANS[letter] * (bb[3] - bb[1]))) elif letter in DOWN_CHARS: #print('DOWN CASE: ', letter) #print(bb) transforms.append( psMat.translate(-bb[0], -bb[1] - 0.35 * (bb[3] - bb[1]))) # 0,0 35% of its height else: transforms.append(psMat.translate(-bb[0], -bb[1])) # if letter in SPECIAL_CHARS: # print('i: ', letter) # print(bb) # transforms.append(psMat.translate(300, 0) ) # scaling if dy > 800: transforms.append(psMat.scale(newScale)) if letter in MID_CHARS and letter not in SPECIAL_Y_SCALE: transforms.append(psMat.scale(0.5)) elif letter in SPECIAL_Y_SCALE: transforms.append(psMat.scale(SPECIAL_Y_SCALE[letter])) #composing while len(transforms) > 1: el1 = transforms.popleft() el2 = transforms.popleft() transforms.appendleft(psMat.compose(el1, el2)) g.transform(transforms[0]) print('POST: ', letter) bb2 = g.boundingBox() g.width = bb2[2] g.simplify() # g.round() # g.cluster(0, 100) print(g.boundingBox(), g.width) return g
def add_smalltriangle(_f): _f.selection.select(0x25bc) _f.copy() _f.selection.select(0x25be) _f.paste() _f.transform(psMat.compose(psMat.scale(0.64), psMat.translate(0, 68))) _f.copy() _f.selection.select(0x25b8) _f.paste() _f.transform(psMat.rotate(math.radians(90))) for g in _f.glyphs(): if g.encoding == 0x25be or g.encoding == 0x25b8: g.width = 512 g = align_to_center(g) return _f
def modify_ellipsis(_f): """3点リーダーを半角にする DejaVuSansMono の U+22EF(⋯) をU+2026(…)、U+22EE(⋮)、U+22F0(⋰)、U+22F1(⋱) にコピーした上で回転させて生成 三点リーダの文字幅について · Issue #41 · miiton/Cica https://github.com/miiton/Cica/issues/41 """ _f.selection.select(0x22ef) _f.copy() _f.selection.select(0x2026) _f.paste() _f.selection.select(0x22ee) _f.paste() _f.selection.select(0x22f0) _f.paste() _f.selection.select(0x22f1) _f.paste() for g in _f.glyphs("encoding"): if g.encoding < 0x22ee: continue elif g.encoding > 0x22f1: break elif g.encoding == 0x22ee: bb = g.boundingBox() cx = (bb[2] + bb[0]) / 2 cy = (bb[3] + bb[1]) / 2 trcen = psMat.translate(-cx, -cy) rotcen = psMat.compose( trcen, psMat.compose(psMat.rotate(math.radians(90)), psMat.inverse(trcen))) g.transform(rotcen) elif g.encoding == 0x22f0: bb = g.boundingBox() cx = (bb[2] + bb[0]) / 2 cy = (bb[3] + bb[1]) / 2 trcen = psMat.translate(-cx, -cy) rotcen = psMat.compose( trcen, psMat.compose(psMat.rotate(math.radians(45)), psMat.inverse(trcen))) g.transform(rotcen) elif g.encoding == 0x22f1: bb = g.boundingBox() cx = (bb[2] + bb[0]) / 2 cy = (bb[3] + bb[1]) / 2 trcen = psMat.translate(-cx, -cy) rotcen = psMat.compose( trcen, psMat.compose(psMat.rotate(math.radians(-45)), psMat.inverse(trcen))) g.transform(rotcen) return _f
def add_smalltriangle(_font): _font.selection.select(0x25bc) # ▼ BLACK DOWN-POINTING TRIANGLE _font.copy() _font.selection.select(0x25be) # ▾ BLACK DOWN-POINTING SMALL TRIANGLE _font.paste() _font.transform(psMat.compose(psMat.scale(0.64), psMat.translate(0, 68))) _font.copy() _font.selection.select(0x25b8) # ▸ BLACK RIGHT-POINTING SMALL TRIANGLE _font.paste() _font.transform(psMat.rotate(math.radians(90))) for g in _font.glyphs(): if g.encoding == 0x25be or g.encoding == 0x25b8: g.width = WIDTH // 2 g.left_side_bearing = g.right_side_bearing = int( (g.left_side_bearing + g.right_side_bearing) / 2) g.width = WIDTH // 2 return _font
def operators(font, dejavu): for c in "⋆⋄⊕⊖⊗⊘⊙⊚⊞⊟⊠⊡": glyph = mvGlyph(font, dejavu, c) xform = mat.scale(1000 / 2048) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize for c in "≺≻≼≽⊀⊁⋠⋡≍≭≎≏": glyph = mvGlyph(font, dejavu, c) xform = mat.scale(1000 / 2048) glyph.transform(xform, ('round', )) xform = aroundCentroid(glyph, mat.scale(0.8)) if c in "≼≽⋠⋡": xform = mat.compose(xform, mat.translate(0, -70)) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize for c in "⊢": #⊨⊩⊫": glyph = mvGlyph(font, dejavu, c) xform = mat.scale(1000 / 2048) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize
def normalizeGlyph(g, letter): bb = g.boundingBox() dy = bb[3] - bb[1] newScale = 800 / (dy) transforms = [] if letter in SPECIAL_CHARS: print('SPECIAL CASE: ', letter) print(bb) transforms.append( psMat.translate(-bb[0], -bb[1] - 0.5 * (bb[3] - bb[1]))) else: transforms.append(psMat.translate(-bb[0], -bb[1])) if dy > 800: transforms.append(psMat.scale(newScale)) if len(transforms) > 1: g.transform(psMat.compose(*transforms)) else: g.transform(transforms[0]) print(g.boundingBox()) return g
def _zenkaku_glyphs(font): hankaku_start = 0x21 zenkaku_start = 0xFF01 glyphs_num = 95 trans = translate(WIDTH / 4, 0) font.selection.none() for i in range(0, glyphs_num): font.selection.select(i + hankaku_start) font.copy() font.selection.select(i + zenkaku_start) font.paste() font.selection.none() # select copied glyphs + 2 (0xff5f & 0xff60) font.selection.select(("ranges", "unicode"), zenkaku_start, zenkaku_start + glyphs_num + 1) for glyph in list(font.selection.byGlyphs): paren = ZENKAKU_PARENTHESIS.get(glyph.encoding) if not paren: glyph.transform(trans) elif paren == "left": glyph.transform(compose(trans, trans)) glyph.width = WIDTH
def matRescale(origin_x, origin_y, scale_x, scale_y): return psMat.compose( psMat.translate(-origin_x, -origin_y), psMat.compose(psMat.scale(scale_x, scale_y), psMat.translate(origin_x, origin_y)))
fontforge.setPrefs('CoverageFormatsAllowed', 1) ttfname = sys.argv[1] middlefamily, weight = os.path.splitext(ttfname)[0].split('-')[1:] modules = sys.argv[2:] is_monospace = middlefamily[1] == 'm' ascent = 860 descent = 140 em = ascent + descent kanji_scale = 0.98 kanji_matrix = psMat.compose( psMat.translate(-em / 2, -ascent + em / 2), psMat.compose( psMat.scale(kanji_scale), psMat.translate(em / 2, ascent - em / 2))) svg_uni_name = re.compile('^u[0-9A-F]{4,5}$', re.IGNORECASE) feature_name = re.compile('^([- 0-9a-zA-Z]{4})_(uni[0-9A-F]{4,5})$') alt_glyphs = {} def svgname_to_glyphname(name): if svg_uni_name.match(name): return (int(name[1:], 16),) m = feature_name.match(name) if m: tag = m.group(1) name = m.group(2) tagged_name = "%s.%s" % (name, tag) if not (tag in alt_glyphs):
descent = 234 bl = 0 br = width bw = br - bl cx = (bl + br) / 2.0 bb = -descent bt = ascent bh = bt - bb cy = (bb + bt) / 2.0 swl2 = 72 / 2.0 swh2 = 146 / 2.0 dg2 = 118 / 2.0 dd = dg2 + 2 * swl2 xrefT = psMat.compose(psMat.translate(-2 * cx, 0), psMat.scale(-1, 1)) yrefT = psMat.compose(psMat.translate(0, -2 * cy), psMat.scale(1, -1)) def addchar(font, cp, contours): glyph = font.createChar(cp, 'uni{:04X}'.format(cp)) pen = glyph.glyphPen() for c in contours: c.draw(pen) pen = None glyph.removeOverlap() glyph.simplify(1, ('forcelines',)) glyph.round() glyph.correctDirection() glyph.canonicalStart() glyph.canonicalContours() glyph.width = width
def patch(self): for target_font in self.target_fonts: source_font = self.source_font target_font_em_original = target_font.em target_font.em = 2048 target_font.encoding = 'ISO10646' # Rename font if self.rename_font: target_font.familyname += ' for Kovid' target_font.fullname += ' for Kovid' fontname, style = re.match("^([^-]*)(?:(-.*))?$", target_font.fontname).groups() target_font.fontname = fontname + 'ForKovid' if style is not None: target_font.fontname += style target_font.appendSFNTName('English (US)', 'Preferred Family', target_font.familyname) target_font.appendSFNTName('English (US)', 'Compatible Full', target_font.fullname) source_bb = source_font['block'].boundingBox() target_bb = [0, 0, 0, 0] target_font_width = 0 # Find the biggest char width and height in the Latin-1 extended range and the box drawing range # This isn't ideal, but it works fairly well - some fonts may need tuning after patching for cp in chain(range(0x00, 0x17f), range(0x2500, 0x2600)): try: bbox = target_font[cp].boundingBox() except TypeError: continue if not target_font_width: target_font_width = target_font[cp].width if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] if bbox[1] < target_bb[1]: target_bb[1] = bbox[1] if bbox[2] > target_bb[2]: target_bb[2] = bbox[2] if bbox[3] > target_bb[3]: target_bb[3] = bbox[3] # Find source and target size difference for scaling x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) y_ratio = (target_bb[3] - target_bb[1]) / (source_bb[3] - source_bb[1]) scale = psMat.scale(x_ratio, y_ratio) # Find source and target midpoints for translating x_diff = target_bb[0] - source_bb[0] y_diff = target_bb[1] - source_bb[1] translate = psMat.translate(x_diff, y_diff) transform = psMat.compose(scale, translate) # Create new glyphs from symbol font for source_glyph in source_font.glyphs(): if source_glyph == source_font['block']: # Skip the symbol font block glyph continue # Select and copy symbol from its encoding point source_font.selection.select(source_glyph.encoding) source_font.copy() # Select and paste symbol to its unicode code point target_font.selection.select(source_glyph.unicode) target_font.paste() # Transform the glyph target_font.transform(transform) # Reset the font's glyph width so it's still considered monospaced target_font[source_glyph.unicode].width = target_font_width target_font.em = target_font_em_original # Generate patched font extension = os.path.splitext(target_font.path)[1] if extension.lower() not in ['.ttf', '.otf']: # Default to OpenType if input is not TrueType/OpenType extension = '.otf' target_font.generate( os.path.join(self.destdir, '{0}{1}'.format(target_font.fullname, extension)))
def matRescale(origin_x, origin_y, scale_x, scale_y): return psMat.compose( psMat.translate(-origin_x, -origin_y), psMat.compose( psMat.scale(scale_x, scale_y), psMat.translate(origin_x, origin_y)))
def patch(self): for target_font in self.target_fonts: source_font = self.source_font target_font_em_original = target_font.em target_font.em = 2048 target_font.encoding = 'ISO10646' # Rename font if self.rename_font: target_font.familyname += ' for Powerline' target_font.fullname += ' for Powerline' target_font.fontname += 'ForPowerline' target_font.appendSFNTName('English (US)', 'Preferred Family', target_font.familyname) target_font.appendSFNTName('English (US)', 'Compatible Full', target_font.fullname) source_bb = source_font['block'].boundingBox() target_bb = [0, 0, 0, 0] target_font_width = 0 # Find the biggest char width and height in the Latin-1 extended range and the box drawing range # This isn't ideal, but it works fairly well - some fonts may need tuning after patching for cp in range(0x00, 0x17f) + range(0x2500, 0x2600): try: bbox = target_font[cp].boundingBox() except TypeError: continue if not target_font_width: target_font_width = target_font[cp].width if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] if bbox[1] < target_bb[1]: target_bb[1] = bbox[1] if bbox[2] > target_bb[2]: target_bb[2] = bbox[2] if bbox[3] > target_bb[3]: target_bb[3] = bbox[3] # Find source and target size difference for scaling x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) y_ratio = (target_bb[3] - target_bb[1]) / (source_bb[3] - source_bb[1]) scale = psMat.scale(x_ratio, y_ratio) # Find source and target midpoints for translating x_diff = target_bb[0] - source_bb[0] y_diff = target_bb[1] - source_bb[1] translate = psMat.translate(x_diff, y_diff) transform = psMat.compose(scale, translate) # Create new glyphs from symbol font for source_glyph in source_font.glyphs(): if source_glyph == source_font['block']: # Skip the symbol font block glyph continue # Select and copy symbol from its encoding point source_font.selection.select(source_glyph.encoding) source_font.copy() # Select and paste symbol to its unicode code point target_font.selection.select(source_glyph.unicode) target_font.paste() # Transform the glyph target_font.transform(transform) # Reset the font's glyph width so it's still considered monospaced target_font[source_glyph.unicode].width = target_font_width target_font.em = target_font_em_original # Generate patched font target_font.generate('{0}.otf'.format(target_font.fullname))
if char == -1: char = letter.replace("&#", "").replace(";", "") letter = fontforge.nameFromUnicode(int(char)) print "letter: %s" % letter print "char: %s" % char importGlyph(f, letter, int(char)) bottom = font["h"].boundingBox()[1] top = font["h"].boundingBox()[3] height = top - bottom scale_ratio = 780 / height scale_matrix = psMat.scale(scale_ratio) translate_matrix = psMat.translate(0, font.descent * scale_ratio) matrix = psMat.compose(scale_matrix, translate_matrix) print matrix # Series of transformations on all glyphs font.selection.all() font.transform(matrix) font.autoWidth(100, 30) font.autoHint() autokern(font) font.descent = 216 font.ascent = 780 # create the output ufo file
def copy_glyphs(glyphs, source_font, target_font, new_font, new_suffix, overwrite): target_font_em_original = target_font.em target_font.em = 2048 target_font.encoding = 'ISO10646' # Rename font target_font.familyname += new_suffix target_font.fullname += new_suffix fontname, style = re.match("^([^-]*)(?:(-.*))?$", target_font.fontname).groups() target_font.fontname = fontname + new_suffix.replace(' ', '') if style is not None: target_font.fontname += style target_font.appendSFNTName('English (US)', 'Preferred Family', target_font.familyname) target_font.appendSFNTName('English (US)', 'Compatible Full', target_font.fullname) # range(0x00, 0x17f) + range(0x2500, 0x2600): ipdb.set_trace() source_bb = source_font['block'].boundingBox() target_bb = [0, 0, 0, 0] target_font_width = 0 # Find the biggest char width and height in the Latin-1 extended range and the box drawing range # This isn't ideal, but it works fairly well - some fonts may need tuning after patching for cp in range(0x00, 0x17f) + range(0x2500, 0x2600): try: bbox = target_font[cp].boundingBox() except TypeError: continue if not target_font_width: target_font_width = target_font[cp].width if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] if bbox[1] < target_bb[1]: target_bb[1] = bbox[1] if bbox[2] > target_bb[2]: target_bb[2] = bbox[2] if bbox[3] > target_bb[3]: target_bb[3] = bbox[3] # Find source and target size difference for scaling x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) y_ratio = (target_bb[3] - target_bb[1]) / (source_bb[3] - source_bb[1]) scale = psMat.scale(x_ratio, y_ratio) # Find source and target midpoints for translating x_diff = target_bb[0] - source_bb[0] y_diff = target_bb[1] - source_bb[1] translate = psMat.translate(x_diff, y_diff) transform = psMat.compose(scale, translate) sys.stderr.write("Source: %i %i %i %i\n" % (source_bb[0], source_bb[1], source_bb[2], source_bb[3])) sys.stderr.write("Target: %i %i %i %i\n" % (target_bb[0], target_bb[1], target_bb[2], target_bb[3])) sys.stderr.write("Offset: %.2f %.2f, Ratio: %.2f %.2f\n" % (x_diff, y_diff, x_ratio, y_ratio)) # print(scale) # print(translate) # print(transform) # Create new glyphs from symbol font for source_glyph in source_font.glyphs(): if source_glyph == source_font['block']: # Skip the symbol font block glyph continue # Select and copy symbol from its encoding point source_font.selection.select(source_glyph.encoding) source_font.copy() # Select and paste symbol to its unicode code point target_font.selection.select(source_glyph.unicode) target_font.paste() # Transform the glyph target_font.transform(transform) # Reset the font's glyph width so it's still considered monospaced target_font[source_glyph.unicode].width = target_font_width target_font.em = target_font_em_original # Generate patched font extension = os.path.splitext(target_font.path)[1] if extension.lower() not in ['.ttf', '.otf']: # Default to OpenType if input is not TrueType/OpenType extension = '.otf' target_font.generate('{0}{1}'.format(target_font.fullname, extension))
def patch_one_font(source_font, target_font, rename_font=True): target_font_em_original = target_font.em target_font.em = 2048 target_font.encoding = 'ISO10646' # Rename font if rename_font: target_font.familyname += ' for Powerline' target_font.fullname += ' for Powerline' fontname, style = FONT_NAME_RE.match(target_font.fontname).groups() target_font.fontname = fontname + 'ForPowerline' if style is not None: target_font.fontname += style target_font.appendSFNTName( 'English (US)', 'Preferred Family', target_font.familyname) target_font.appendSFNTName( 'English (US)', 'Compatible Full', target_font.fullname) source_bb = source_font['block'].boundingBox() target_bb = [0, 0, 0, 0] target_font_width = 0 # Find the biggest char width and height in the Latin-1 extended range and # the box drawing range This isn't ideal, but it works fairly well - some # fonts may need tuning after patching. for cp in chain(range(0x00, 0x17f), range(0x2500, 0x2600)): try: bbox = target_font[cp].boundingBox() except TypeError: continue if not target_font_width: target_font_width = target_font[cp].width if bbox[0] < target_bb[0]: target_bb[0] = bbox[0] if bbox[1] < target_bb[1]: target_bb[1] = bbox[1] if bbox[2] > target_bb[2]: target_bb[2] = bbox[2] if bbox[3] > target_bb[3]: target_bb[3] = bbox[3] # Find source and target size difference for scaling x_ratio = (target_bb[2] - target_bb[0]) / (source_bb[2] - source_bb[0]) y_ratio = (target_bb[3] - target_bb[1]) / (source_bb[3] - source_bb[1]) scale = psMat.scale(x_ratio, y_ratio) # Find source and target midpoints for translating x_diff = target_bb[0] - source_bb[0] y_diff = target_bb[1] - source_bb[1] translate = psMat.translate(x_diff, y_diff) transform = psMat.compose(scale, translate) # Create new glyphs from symbol font for source_glyph in source_font.glyphs(): if source_glyph == source_font['block']: # Skip the symbol font block glyph continue # Select and copy symbol from its encoding point source_font.selection.select(source_glyph.encoding) source_font.copy() # Select and paste symbol to its unicode code point target_font.selection.select(source_glyph.unicode) target_font.paste() # Transform the glyph target_font.transform(transform) # Reset the font's glyph width so it's still considered monospaced target_font[source_glyph.unicode].width = target_font_width target_font.em = target_font_em_original # FIXME: Menlo and Meslo font do have these substitutes, but U+FB01 and # U+FB02 still do not show up for fi and fl. if 0xFB01 in target_font: target_font[0xFB01].removePosSub('*') # fi ligature if 0xFB02 in target_font: target_font[0xFB02].removePosSub('*') # fl ligature # Generate patched font extension = os.path.splitext(target_font.path)[1] if extension.lower() not in ['.ttf', '.otf']: # Default to OpenType if input is not TrueType/OpenType extension = '.otf' target_font.generate('{0}{1}'.format(target_font.fullname, extension))