def test(): font0 = fontforge.open('fonts/gbsn00lp.ttf') c0 = fontforge.nameFromUnicode(ord(u'艹')) c1 = fontforge.nameFromUnicode(ord(u'五')) cy = fontforge.nameFromUnicode(ord(u'\uE000')) g0 = font0[c0] g1 = font0[c1] l0 = g0.layers[1] l1 = g1.layers[1] l0.transform(psMat.scale(1,0.9)) l0.transform(psMat.translate(0,250)) l1.transform(psMat.scale(1, 0.5)) l1.transform(psMat.translate(0,-104)) #l1.transform(psMat.rotate(1.7)) g0.layers[1] = l0 + l1 font0.selection.select(c0) font0.copy() font0.selection.select(cy) font0.paste() font0.generate('output/out.ttf')
def fit_glyph_box(glyph, percent): '''Scale a font to nearly fit a square.''' bounding = glyph.boundingBox() # xmin, ymin, xmax, ymax gwidth = (bounding[2] - bounding[0]) gheight = (bounding[3] - bounding[1]) if not gwidth or not gheight: return if 0.64*max(gwidth, gheight) < min(gwidth, gheight): if gwidth > gheight: rate = ((gwidth - gheight)*percent + gwidth)/gwidth matrix = psMat.scale(1,rate) else: rate = ((gheight - gwidth)*percent + gheight)/gheight matrix = psMat.scale(rate,1) glyph.transform(matrix)
def trim_spacing(self, xtrim, orig_width=None): "trims xtrim points around each glyph of font" if orig_width is None: orig_width = self._get_width() for sym in self.font: glyph = self.font[sym] if glyph.width == orig_width: rbearing = int(math.floor(max(0, glyph.right_side_bearing))) lbearing = int(math.floor(max(0, glyph.left_side_bearing))) if lbearing + rbearing == orig_width: glyph.width = orig_width - 2 * xtrim else: glyph_trim = min(xtrim, lbearing, rbearing) glyph.transform(psMat.translate(glyph_trim, 0)) glyph.width = orig_width glyph.transform(psMat.translate(-2 * glyph_trim, 0)) glyph_width = glyph.width to_shrink = 2 * (xtrim - glyph_trim) if to_shrink > 0: print "%d out of %d points could not be trimmed on %s, scaling" \ % (to_shrink/2, xtrim, sym) xratio = float(glyph_width - to_shrink)/ glyph_width glyph.transform(psMat.scale(xratio, 1))
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 scale(self, glyph): if glyph.width > 0: source_cells_width = glyph.width / self.avg_width scale = 1.0 / (1.15 ** max(0, source_cells_width - 1)) # if glyph.unicode == 10239: # print("\n\n====\n" + "\n".join("{}: {}".format(attr, str(getattr(glyph, attr))) for attr in dir(glyph))) matrix = psMat.scale(scale, 1) glyph.transform(matrix) GlyphScaler.set_width(glyph, self.cell_width)
def scale(self, ratio, orig_width=None): "scale vertically and horizontally to the same ratio" if orig_width is None: orig_width = self._get_width() if ratio != 1: for sym in self.font: glyph = self.font[sym] if glyph.width == orig_width: glyph.transform(psMat.scale(ratio, ratio))
def horiz_condense(self, xratio, orig_width=None): "condense the font horizontally to the given xratio" if orig_width is None: orig_width = self._get_width() if xratio != 1: for sym in self.font: glyph = self.font[sym] if glyph.width == orig_width: glyph.transform(psMat.scale(xratio, 1))
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 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 apply_rescale(glyph, origin, scale): """Rescale glyph""" # move scale origin point to (0, 0) sx, sy = origin translate_matrix = psMat.translate(-sx, -sy) glyph.transform(translate_matrix) # scale around (0, 0) scale_matrix = psMat.scale(scale) glyph.transform(scale_matrix) # move scale origin point back to its old position translate_matrix = psMat.translate(origin) glyph.transform(translate_matrix)
def fit_glyph_plus(glyph, em, percent, wavg, havg): '''Scale a glyph horizontally to fit the width.''' bounding = glyph.boundingBox() # xmin, ymin, xmax, ymax gwidth = (bounding[2] - bounding[0]) gheight = (bounding[3] - bounding[1]) if not gwidth or not gheight: return rate = (em-min(wavg-gwidth, havg-gheight)*0.01)*percent/max(gwidth, gheight) if gwidth < 0.95*wavg and gheight < 0.95*havg: rate = (em-min(wavg-gwidth, havg-gheight)*0.6)*percent/max(gwidth, gheight) #print gwidth, rate, em if rate*max(gwidth, gheight) > em: rate = em*percent/max(gwidth, gheight) matrix = psMat.scale(rate) glyph.transform(matrix)
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))
#!/usr/bin/env fontforge import fontforge, psMat from sys import argv, stderr from os import system from math import radians if len(argv) < 4: stderr.write("Usage: "+argv[0]+" skew-flag infile outfile\n") quit(1) fontforge.setPrefs('CoverageFormatsAllowed', 1) font = fontforge.open(argv[2]) for glyph in font.glyphs(): if glyph.isWorthOutputting(): width = glyph.width glyph.transform(psMat.translate(-font.em / 2, 0)) glyph.transform(psMat.scale(0.8, 1.0)) if int(argv[1]): glyph.transform(psMat.rotate(radians(90))) glyph.transform(psMat.skew(radians(-4))) glyph.transform(psMat.rotate(radians(-90))) glyph.transform(psMat.translate(font.em / 2, 0)) glyph.width = width font.save(argv[3]) font.close()
print "Loading base file %s..." % sys.argv[1] BaseFont = fontforge.open(sys.argv[1]) BaseFont.selection.none() for glyph in BaseFont.glyphs(): if glyph.isWorthOutputting(): if isJGlyphP(glyph): glyph.unlinkRef() if isJGlyph(glyph): BaseFont.selection.select(("more",), glyph) WidthE = BaseFont["ahiragana"].width WidthV = BaseFont["ahiragana"].vwidth print "Rescaling..." BaseFont.transform(psMat.scale(1.2)) BaseFont.transform(psMat.translate(0, -50)) WidthJ = BaseFont["ahiragana"].width WidthA = BaseFont["A"].width BaseFont.selection.none() for glyph in BaseFont.glyphs(): if glyph.isWorthOutputting(): if not isJGlyph(glyph): BaseFont.selection.select(("more",), glyph) print "Moving..." BaseFont.transform(psMat.translate(float(WidthJ - WidthE) / 2.0, 0.0)) for glyph in BaseFont.selection.byGlyphs: if glyph.width >= WidthE:
def generate(italic_deg=0, italic_name="", condensed_scale=1, condensed_name="", auto_hint=False, manual_hints=False, auto_instr=False, auto_kern=False, auto_width=False, no_remove_overlap=False, no_add_extrema=False, generate_super_and_subscripts=True, generate_super_and_subscripts_method=3, font_name="RoutedGothic", family_name="Routed Gothic", weight_name="Regular", font_file_basename=FONT_FILE_BASENAME): font = fontforge.open(SOURCE_FILENAME) if generate_super_and_subscripts: for ss in SUPERSCRIPTS: sscp = codepoint(ss['codepoint']) ssof = codepoint(ss['of']) make_superscript(font, ssof, sscp) for ss in SUBSCRIPTS: sscp = codepoint(ss['codepoint']) ssof = codepoint(ss['of']) make_subscript(font, ssof, sscp) super_digit_glyphs = [font[cp] for cp in SUPERSCRIPT_DIGIT_CODEPOINTS] fractionlinewidth = max([g.width for g in super_digit_glyphs ]) + FRACTION_LINE_EXTRA_WIDTH fractionline = font.createChar(CODEPOINT_FRACTION_LINE) pen = fractionline.glyphPen() pen.moveTo((STROKE_WIDTH / 2 + FRACTION_LINE_BEARING, SUPERSUBSCRIPT_FRACTION_LINE)) pen.lineTo( (fractionlinewidth - STROKE_WIDTH / 2 - FRACTION_LINE_BEARING, SUPERSUBSCRIPT_FRACTION_LINE)) pen.endPath() # leave path open pen = None # finalize fractionline.width = fractionlinewidth for vf in VULGAR_FRACTIONS: numerator = vf['numerator'] denominator = vf['denominator'] fraction = vf['codepoint'] numerator = superscript_codepoint(numerator) denominator = subscript_codepoint(denominator) fraction = codepoint(fraction) make_vulgar_fraction(font, numerator, denominator, fraction) if auto_kern: for lookupName in font.gpos_lookups: for subtableName in font.getLookupSubtables(lookupName): if font.isKerningClass(subtableName): font.removeLookupSubtable(subtableName) # condense kerning pairs if needed if (condensed_scale != 1) and not auto_kern: for lookupName in font.gpos_lookups: for subtableName in font.getLookupSubtables(lookupName): if font.isKerningClass(subtableName): kc = font.getKerningClass(subtableName) offsets = kc[2] newOffsets = [o * condensed_scale for o in offsets] font.alterKerningClass(subtableName, kc[0], kc[1], newOffsets) condensed_transform = psMat.scale(condensed_scale, 1) if condensed_scale != 1: font.selection.all() font.transform(condensed_transform) for glyph in font.glyphs(): if manual_hints: glyph.manualHints = True else: glyph.manualHints = False if italic_deg: width = glyph.width for name in glyph.layers: layer = glyph.layers[name] layer.transform(italic_transform(italic_deg)) glyph.layers[name] = layer glyph.width = width glyph.anchorPoints = [ anchor_point_transform(p, italic_transform(italic_deg)) for p in glyph.anchorPoints ] glyph.round() glyph.stroke("circular", STROKE_WIDTH, "round", "round") if not no_remove_overlap: glyph.removeOverlap() if not no_add_extrema: glyph.addExtrema() if auto_width: font.autoWidth(AUTO_WIDTH, -1024, 1024) # call build() on glyphs that reference two glyphs if anchor # points would be used for glyph in font.glyphs(): built = False if len(glyph.references) == 2: glyphname1 = glyph.references[0][0] glyphname2 = glyph.references[1][0] g1 = font[glyphname1] g2 = font[glyphname2] g1_base_aps = tuple( [ap[0] for ap in g1.anchorPoints if ap[1] == "base"]) g1_mark_aps = tuple( [ap[0] for ap in g1.anchorPoints if ap[1] == "mark"]) g2_base_aps = tuple( [ap[0] for ap in g2.anchorPoints if ap[1] == "base"]) g2_mark_aps = tuple( [ap[0] for ap in g2.anchorPoints if ap[1] == "mark"]) i1 = intersect(g1_base_aps, g2_mark_aps) i2 = intersect(g2_base_aps, g1_mark_aps) if len(i1) or len(i2): glyph.build() built = True if not built: if italic_deg: glyph.references = [ reference_transform(r, glyph, italic_deg) for r in glyph.references ] for glyph in font.glyphs(): if auto_hint: glyph.autoHint() if auto_instr: glyph.autoInstr() font.strokedfont = False if auto_kern: font.selection.all() font.addLookup("autoKern", "gpos_pair", (), (("liga", (("latn", ("dflt")), )), )) font.addKerningClass("autoKern", "autoKern", 288, 16, False, True) font.fontname = font_name font.familyname = family_name font.fullname = family_name font.weight = weight_name font.italicangle = italic_deg basename = font_file_basename if condensed_scale != 1: # separate family names for condensed variants. don't # remember why. font.familyname = font.familyname + " " + condensed_name.replace( "-", " ") font.fontname = font.fontname + condensed_name.replace( "-", "").replace(" ", "") font.fullname = font.fullname + " " + condensed_name.replace("-", " ") basename = basename + "-" + condensed_name.lower().replace(" ", "-") if italic_deg: font.fontname = font.fontname + "-" + italic_name.replace(" ", "-") font.fullname = font.fullname + " " + italic_name.replace("-", " ") basename = basename + "-" + italic_name.lower().replace(" ", "-") font.italicangle = -ITALIC_ANGLE_DEG sfd_filename = DIST_SFD_DIRECTORY + "/" + basename + ".sfd" ttf_filename = DIST_TTF_DIRECTORY + "/" + basename + ".ttf" sfd_dir = os.path.dirname(sfd_filename) ttf_dir = os.path.dirname(ttf_filename) if not os.path.exists(sfd_dir): print "makedirs " + sfd_dir os.makedirs(sfd_dir) if not os.path.exists(ttf_dir): print "makedirs " + ttf_dir os.makedirs(ttf_dir) print "Saving " + sfd_filename + " ..." font.save(sfd_filename) print "Saving " + ttf_filename + " ..." font.generate(ttf_filename, flags=("no-hints", "omit-instructions")) font.close()
int(nm[0], 16 if nm[0][0:2] == '0x' else 10), strid, nm[1] ]) addSfntNames(args.sfnt_name) addSfntNames(args.sfnt_family_name, 1) addSfntNames(args.sfnt_full_name, 4) addSfntNames(args.sfnt_weight, 2) for nomen in sfnt: font.appendSFNTName(nomen[0], nomen[1], nomen[2]) for glyph in glyphsWorthOutputting(font): if args.action == 'scale': glyph.transform( psMat.scale(args.width / glyph.width, 1.0), ('partialRefs', 'round')) elif args.action == 'pad': glyph.transform( psMat.translate((args.width - glyph.width) / 2, 0), ('partialRefs', 'round')) glyph.width = int(args.width) if args.skew is not None: glyph.transform( psMat.skew(radians(args.skew)), ('partialRefs', 'round')) if args.merge_with is not None: for fileName in args.merge_with: font2 = fontforge.open(fileName) font.encoding = font2.encoding
if (len(sys.argv) < 3): print 'Usage: %s source-file target-file' % sys.argv[0] quit(1) print 'Loading base file %s...' % sys.argv[1] BaseFont = fontforge.open(sys.argv[1]) BaseFont.selection.none() for glyph in BaseFont.glyphs(): if glyph.isWorthOutputting(): if isJGlyphP(glyph): glyph.unlinkRef() if isJGlyph(glyph): BaseFont.selection.select(("more",), glyph) print 'Rescaling J...' BaseFont.transform(psMat.scale(1.28206)) BaseFont.transform(psMat.translate(0,-120)) BaseFont.selection.none() for glyph in BaseFont.glyphs(): if glyph.isWorthOutputting(): if not isJGlyph(glyph): BaseFont.selection.select(("more",), glyph) print 'Rescaling E...' BaseFont.transform(psMat.scale(1.28206)) print 'Saving target file %s...' % sys.argv[2] BaseFont.save(sys.argv[2])
def calculate_matrix(scale): if scale == 1.0: return None else: return psMat.scale(scale)
#!/home/rakesh/pdf2htmlEX/fontforge-pdf2htmlEX/fontforgeexe/fontforge #Needs: fonts/StrokeTests.sfd import os import fontforge import psMat stroketests = os.path.join("/home/rakesh/pdf2htmlEX/fontforge-pdf2htmlEX/tests", "fonts", "StrokeTests.sfd") print "triangle" font=fontforge.open(stroketests) font.selection.all() triangle=fontforge.unitShape(3).transform(psMat.scale(25)) font.stroke("polygonal",triangle) font.close() print "square" font=fontforge.open(stroketests) font.selection.all() square=fontforge.unitShape(4).transform(psMat.scale(25)) font.stroke("polygonal",square) font.close() print "pentagon" font=fontforge.open(stroketests) font.selection.all() pent=fontforge.unitShape(5).transform(psMat.scale(25)) font.stroke("polygonal",pent) font.close() print "circular"
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))
def merge_one(self, ctx): self.log.debug('"%s" merging...', ctx.filename) font = fontforge.open(ctx.filename) unicode_range = ctx.unicode_range glyph_scale = ctx.scale ext_start = 0 ext_end = 0 if len(unicode_range) >= 1: ext_start = int(unicode_range[0], 16) if len(unicode_range) >= 2: ext_end = int(unicode_range[1], 16) base_start = ext_start remap_start_point = ctx.remap_start_point if remap_start_point is not None: base_start = int(remap_start_point, 16) base_font_height = get_height(self.base_font) font_height = get_height(font) # scale transform scale_ratio_x = round(float(self.base_font.em) / font.em, 3) scale_ratio_y = round(float(base_font_height) / font_height, 3) scale = psMat.scale(scale_ratio_x * glyph_scale, scale_ratio_y * glyph_scale) if ext_start is 0: font.selection.all() else: font.selection.select(('ranges', 'unicode'), ext_start, ext_end) # copy and transform glyphs for glyph in list(font.selection.byGlyphs): index = base_start + (glyph.encoding - ext_start) font.selection.select(glyph.encoding) font.copy() self.base_font.selection.select(index) self.base_font.paste() _glyph = self.base_font[index] _glyph.transform(scale) _glyph.glyphname = glyph.glyphname info = get_glyph_size_info(_glyph) move_x = 0 move_y = 0 if self.hints.ascent < info.ymax: move_y = self.hints.ascent - info.ymax if glyph_scale < 1.0 and ctx.adjust_position: move_x += info.width * (1.0 - glyph_scale) / 2.0 move_y -= info.height * (1.0 - glyph_scale) / 2.0 info = get_glyph_size_info(_glyph) if info.width + move_x < self.base_font.em: _glyph.left_side_bearing += move_x hint = self.get_hint(info.width) if hint.glyph_width < info.width and ctx.adjust_position: delta = info.width - hint.glyph_width move_x += 1.0 - (delta / hint.glyph_width) _glyph.transform(psMat.translate(move_x, move_y)) _glyph.width = hint.glyph_width _glyph.vwidth = hint.glyph_vwidth # add copyright if font.copyright and font.copyright not in self.base_font.copyright: self.base_font.copyright += '\n' + font.copyright font.close() self.log.debug('"%s" merged.', ctx.filename)
"teema2luuva", "teema2luuvar", "teema3luuva", "teema3luuvar", "teema4luuva", "teema4luuvar", "capteema1luuva", "capteema1luuvar", "capteema2luuva", "capteema2luuvar", "capteema3luuva", "capteema3luuvar", "capteema4luuva", "capteema4luuvar"): font[char].unlinkThisGlyph() font[char].clear() #end for # Clear the bitmaps (native script command ClearBackground();). # This does not work at the moment. Bug? for char in font: font[char].layers[0] = fontforge.layer() font.selection.all() font.removeOverlap() font.transform(psMat.scale(1.25)) # Should match scale factor in tracing.mf font.addExtrema() font.simplify(2, ("removesingletonpoints"), 0, 0, 5000) font.round() font.simplify(2, ("ignoreextrema"), 0, 0, 5000) font.addExtrema() font.round() font.simplify(2, ("setstarttoextremum"), 0, 0, 5000) font.mergeFonts("numerals.sfd") font.encoding = "unicode" font.encoding = "compacted" # Older versions of grcompiler can't handle version=4 (fixed in SVN rev. 1105) font.os2_version = 3
# Remove previous data from subprocess import call call(["rm", "-rf", "./dist"]) call(["mkdir", "./dist"]) call(["mkdir", "./dist/css"]) call(["mkdir", "./dist/fonts"]) call([ "cp", "./fontawesome/css/font-awesome.css", "./dist/css/font-awesome.css" ]) # Load font font = fontforge.open('fontawesome/fonts/fontawesome-webfont.ttf') # and now the automated process a = psMat.scale(0.6) a2 = psMat.scale(0.6) cur_unicode = start_char icon_list = [] # Detects if the next unicode character is a blacklisted one def next_unicode(cur_unicode): new_unicode = cur_unicode + 1 while new_unicode in range( 0xfe00, 0xfe10) or new_unicode == 0xfeff or new_unicode in range( 0xf000, 0xf2ff): new_unicode = new_unicode + 1 return new_unicode
import fontforge import sys import os import struct import psMat src = fontforge.open("Myriad-CnSemibold.ttf") src.fontname = "Myriad-UcnSemibold" src.fullname = "Myriad Ultra-Condensed Semibold" src.copyright = "As free as Myriad-CnSemibold." src.version = "001.200" src.em = 1024; cnd = psMat.scale(0.7,1) src.selection.all() for glyph in src.selection.byGlyphs: glyph.transform(cnd) src.generate("Myriad-UcnSemibold.ttf") src = fontforge.open("FreeSans.ttf") src.fontname = "FreeSansCn" src.fullname = "Free Sans Condensed" src.copyright = "As free as Free Sans." src.version = "001.200" src.em = 1024; cnd = psMat.scale(0.7,1) src.selection.all()
def mergeLatin(font, feafile, italic=False, glyphs=None, quran=False): styles = {"Regular": "Roman", "Slanted": "Italic", "Bold": "Bold", "BoldSlanted": "BoldItalic"} style = styles[font.fontname.split("-")[1]] latinfile = "Crimson-%s.sfd" %style tmpfont = mkstemp(suffix=os.path.basename(latinfile))[1] latinfont = fontforge.open("sources/crimson/%s" %latinfile) validateGlyphs(latinfont) # to flatten nested refs mainly if glyphs: latinglyphs = list(glyphs) else: # collect latin glyphs we want to keep latinglyphs = [] # we want all glyphs in latin0-9 encodings for i in range(0, 9): latinfont.encoding = 'latin%d' %i for glyph in latinfont.glyphs("encoding"): if glyph.encoding <= 255: if glyph.glyphname not in latinglyphs: latinglyphs.append(glyph.glyphname) elif glyph.unicode != -1 and glyph.unicode <= 0x017F: # keep also Unicode Latin Extended-A block if glyph.glyphname not in latinglyphs: latinglyphs.append(glyph.glyphname) elif glyph.unicode == -1 and '.prop' in glyph.glyphname: # proportional digits latinglyphs.append(glyph.glyphname) # keep ligatures too ligatures = ("f_b", "f_f_b", "f_h", "f_f_h", "f_i", "f_f_i", "f_j", "f_f_j", "f_k", "f_f_k", "f_l", "f_f_l", "f_f") # and Arabic romanisation characters romanisation = ("uni02BC", "uni02BE", "uni02BE", "amacron", "uni02BE", "amacron", "eacute", "uni1E6F", "ccedilla", "uni1E6F", "gcaron", "ycircumflex", "uni1E29", "uni1E25", "uni1E2B", "uni1E96", "uni1E0F", "dcroat", "scaron", "scedilla", "uni1E63", "uni1E11", "uni1E0D", "uni1E6D", "uni1E93", "dcroat", "uni02BB", "uni02BF", "rcaron", "grave", "gdotaccent", "gbreve", "umacron", "imacron", "amacron", "amacron", "uni02BE", "amacron", "uni02BE", "acircumflex", "amacron", "uni1E97", "tbar", "aacute", "amacron", "ygrave", "agrave", "uni02BE", "aacute", "Amacron", "Amacron", "Eacute", "uni1E6E", "Ccedilla", "uni1E6E", "Gcaron", "Ycircumflex", "uni1E28", "uni1E24", "uni1E2A", "uni1E0E", "Dcroat", "Scaron", "Scedilla", "uni1E62", "uni1E10", "uni1E0C", "uni1E6C", "uni1E92", "Dcroat", "Rcaron", "Gdotaccent", "Gbreve", "Umacron", "Imacron", "Amacron", "Amacron", "Amacron", "Acircumflex", "Amacron", "Tbar", "Aacute", "Amacron", "Ygrave", "Agrave", "Aacute") # and some typographic characters typographic = ("uni2010", "uni2011", "figuredash", "endash", "emdash", "uni2015", "quoteleft", "quoteright", "quotesinglbase", "quotereversed", "quotedblleft", "quotedblright", "quotedblbase", "uni201F", "dagger", "daggerdbl", "bullet", "onedotenleader", "ellipsis", "uni202F", "perthousand", "minute", "second", "uni2038", "guilsinglleft", "guilsinglright", "uni203E", "fraction", "i.TRK", "minus", "uni2213", "radical", "uni2042") for l in (ligatures, romanisation, typographic): for name in l: if name not in latinglyphs: latinglyphs.append(name) if not quran: # we want our ring above and below in Quran font only for name in ("uni030A", "uni0325"): font[name].clear() latinglyphs += buildComposition(latinfont, latinglyphs) subsetFont(latinfont, latinglyphs) digits = ("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine") # common characters that can be used in Arabic and Latin need to be handled # carefully in the slanted font so that right leaning italic is used with # Latin, and left leaning slanted is used with Arabic, using ltra and rtla # features respectively, for less OpenType savvy apps we make the default # upright so it works reasonably with bot scripts if italic: if "Bold" in style: upright = fontforge.open("sources/crimson/Crimson-Bold.sfd") else: upright = fontforge.open("sources/crimson/Crimson-Roman.sfd") shared = ("exclam", "quotedbl", "numbersign", "dollar", "percent", "quotesingle", "asterisk", "plus", "colon", "semicolon", "less", "equal", "greater", "question", "at", "asciicircum", "exclamdown", "section", "copyright", "logicalnot", "registered", "plusminus", "uni00B2", "uni00B3", "paragraph", "uni00B9", "ordmasculine", "onequarter", "onehalf", "threequarters", "questiondown", "quoteleft", "quoteright", "quotesinglbase", "quotereversed", "quotedblleft", "quotedblright", "quotedblbase", "uni201F", "dagger", "daggerdbl", "perthousand", "minute", "second", "guilsinglleft", "guilsinglright", "fraction", "uni2213") for name in shared: glyph = latinfont[name] glyph.clear() upright.selection.select(name) upright.copy() latinfont.createChar(upright[name].encoding, name) latinfont.selection.select(name) latinfont.paste() for name in digits: glyph = latinfont[name] glyph.glyphname += '.ltr' glyph.unicode = -1 upright.selection.select(name) upright.copy() latinfont.createChar(upright[name].encoding, name) latinfont.selection.select(name) latinfont.paste() rtl = latinfont.createChar(-1, name + ".rtl") rtl.addReference(name, italic) rtl.useRefsMetrics(name) for name in digits: pname = name + ".prop" upright.selection.select(pname) upright.copy() latinfont.createChar(-1, pname) latinfont.selection.select(pname) latinfont.paste() for name in digits: pname = name + ".prop" rtl = latinfont.createChar(-1, name + ".rtl" + ".prop") rtl.addReference(pname, italic) rtl.useRefsMetrics(pname) # copy kerning classes kern_lookups = {} if not quran: for lookup in latinfont.gpos_lookups: kern_lookups[lookup] = {} kern_lookups[lookup]["subtables"] = [] kern_lookups[lookup]["type"], kern_lookups[lookup]["flags"] = latinfont.getLookupInfo(lookup)[:2] for subtable in latinfont.getLookupSubtables(lookup): if latinfont.isKerningClass(subtable): kern_lookups[lookup]["subtables"].append((subtable, latinfont.getKerningClass(subtable))) for lookup in latinfont.gpos_lookups: latinfont.removeLookup(lookup) for lookup in latinfont.gsub_lookups: latinfont.removeLookup(lookup) latinfont.save(tmpfont) latinfont.close() font.mergeFonts(tmpfont) os.remove(tmpfont) if not quran: buildComposition(font, latinglyphs) # add Latin small and medium digits for name in digits: if italic: # they are only used in Arabic contexts, so always reference the # italic rtl variant refname = name +".rtl" else: refname = name small = font.createChar(-1, name + ".small") small.clear() small.addReference(refname, psMat.scale(0.6)) small.transform(psMat.translate(0, -40)) small.width = 600 centerGlyph(small) medium = font.createChar(-1, name + ".medium") medium.clear() medium.addReference(refname, psMat.scale(0.8)) medium.transform(psMat.translate(0, 50)) medium.width = 900 centerGlyph(medium) for lookup in kern_lookups: font.addLookup(lookup, kern_lookups[lookup]["type"], kern_lookups[lookup]["flags"], (('kern', script_lang),) ) for subtable in kern_lookups[lookup]["subtables"]: first = [] second = [] offsets = subtable[1][2] # drop non-existing glyphs for new_klasses, klasses in ((first, subtable[1][0]), (second, subtable[1][1])): for klass in klasses: new_klass = [] if klass: for name in klass: if name in font: new_klass.append(name) new_klasses.append(new_klass) # if either of the classes is empty, don’t bother with the subtable if any(first) and any(second): font.addKerningClass(lookup, subtable[0], first, second, offsets)
def build_font(_f, emoji): log('Generating %s ...' % _f.get('weight_name')) hack = fontforge.open('./sourceFonts/%s' % _f.get('hack')) hack = remove_glyph_from_hack(hack) cica = fontforge.open('./sourceFonts/%s' % _f.get('mgen_plus')) nerd = fontforge.open('./sourceFonts/nerd.ttf') for g in hack.glyphs(): g.transform((0.42, 0, 0, 0.42, 0, 0)) if _f.get('hack_weight_reduce') != 0: # g.changeWeight(_f.get('hack_weight_reduce'), 'auto', 0, 0, 'auto') g.stroke("circular", _f.get('hack_weight_reduce'), 'butt', 'round', 'removeexternal') g = align_to_center(g) hack = modify_m(hack, _f.get('weight_name')) alternate_expands = [ 0x306e, ] if _f.get('mgen_weight_add') != 0: for g in cica.glyphs(): # g.changeWeight(_f.get('mgen_weight_add'), 'auto', 0, 0, 'auto') g.stroke("caligraphic", _f.get('mgen_weight_add'), _f.get('mgen_weight_add'), 45, 'removeinternal') # g.stroke("circular", _f.get('mgen_weight_add'), 'butt', 'round', 'removeinternal') ignoring_center = [ 0x3001, 0x3002, 0x3008, 0x3009, 0x300a, 0x300b, 0x300c, 0x300d, 0x300e, 0x300f, 0x3010, 0x3011, 0x3014, 0x3015, 0x3016, 0x3017, 0x3018, 0x3019, 0x301a, 0x301b, 0x301d, 0x301e, 0x3099, 0x309a, 0x309b, 0x309c, ] for g in cica.glyphs(): g.transform((0.91, 0, 0, 0.91, 0, 0)) full_half_threshold = 700 if _f.get('italic'): g.transform(psMat.skew(0.25)) skew_amount = g.font.ascent * 0.91 * 0.25 g.width = g.width + skew_amount full_half_threshold += skew_amount if g.width > full_half_threshold: width = 1024 else: width = 512 g.transform(psMat.translate((width - g.width) / 2, 0)) g.width = width if g.encoding in ignoring_center: pass else: g = align_to_center(g) for g in hack.glyphs(): if g.isWorthOutputting: if _f.get('italic'): g.transform(psMat.skew(0.25)) if g.encoding >= 0x2500 and g.encoding <= 0x25af: g.transform( psMat.compose(psMat.scale(1.024, 1.024), psMat.translate(0, -30))) g = align_to_center(g) hack.selection.select(g.encoding) hack.copy() cica.selection.select(g.encoding) cica.paste() for g in nerd.glyphs(): if g.encoding < 0xe0a0 or g.encoding > 0xf4ff: continue g = modify_nerd(g) nerd.selection.select(g.encoding) nerd.copy() cica.selection.select(g.encoding) cica.paste() cica = fix_box_drawings(cica) cica = zenkaku_space(cica) cica = zero(cica) cica = modify_WM(cica) cica = vertical_line_to_broken_bar(cica) cica = emdash_to_broken_dash(cica) cica = add_gopher(cica) if emoji: cica = add_notoemoji(cica) cica = add_smalltriangle(cica) cica = add_dejavu(cica, _f) cica = resize_supersub(cica) cica.ascent = ASCENT cica.descent = DESCENT cica.upos = 45 cica.fontname = _f.get('family') cica.familyname = _f.get('family') cica.fullname = _f.get('name') cica.weight = _f.get('weight_name') cica = set_os2_values(cica, _f) cica.appendSFNTName(0x411, 0, COPYRIGHT) cica.appendSFNTName(0x411, 1, _f.get('family')) cica.appendSFNTName(0x411, 2, _f.get('style_name')) # cica.appendSFNTName(0x411,3, "") cica.appendSFNTName(0x411, 4, _f.get('name')) cica.appendSFNTName(0x411, 5, "Version " + VERSION) cica.appendSFNTName(0x411, 6, _f.get('family') + "-" + _f.get('weight_name')) # cica.appendSFNTName(0x411,7, "") # cica.appendSFNTName(0x411,8, "") # cica.appendSFNTName(0x411,9, "") # cica.appendSFNTName(0x411,10, "") # cica.appendSFNTName(0x411,11, "") # cica.appendSFNTName(0x411,12, "") cica.appendSFNTName(0x411, 13, LICENSE) # cica.appendSFNTName(0x411,14, "") # cica.appendSFNTName(0x411,15, "") cica.appendSFNTName(0x411, 16, _f.get('family')) cica.appendSFNTName(0x411, 17, _f.get('style_name')) cica.appendSFNTName(0x409, 0, COPYRIGHT) cica.appendSFNTName(0x409, 1, _f.get('family')) cica.appendSFNTName(0x409, 2, _f.get('style_name')) cica.appendSFNTName( 0x409, 3, VERSION + ";" + _f.get('family') + "-" + _f.get('style_name')) cica.appendSFNTName(0x409, 4, _f.get('name')) cica.appendSFNTName(0x409, 5, "Version " + VERSION) cica.appendSFNTName(0x409, 6, _f.get('name')) # cica.appendSFNTName(0x409,7, "") # cica.appendSFNTName(0x409,8, "") # cica.appendSFNTName(0x409,9, "") # cica.appendSFNTName(0x409,10, "") # cica.appendSFNTName(0x409,11, "") # cica.appendSFNTName(0x409,12, "") cica.appendSFNTName(0x409, 13, LICENSE) # cica.appendSFNTName(0x409,14, "") # cica.appendSFNTName(0x409,15, "") cica.appendSFNTName(0x409, 16, _f.get('family')) cica.appendSFNTName(0x409, 17, _f.get('style_name')) if emoji: fontpath = './dist/%s' % _f.get('filename') else: fontpath = './dist/noemoji/%s' % _f.get('filename') cica.generate(fontpath) cica.close() hack.close() nerd.close()
def main(aArgs): ############################################################################ # Open the font print("Opening file %s... " % aArgs.input, end="") try: font = fontforge.open(aArgs.input) except EnvironmentError: print("Failed!") exit(1) print("Done") print("") tolerance = font.em / 50 ############################################################################ # Ensure that the font has glyphs for all the ASCII characters. print("Testing Basic Latin Unicode Block... ") for u in range(0x20, 0x7F): print("Testing U+%2X... " % u, end="") if u not in font: print("Failed") warnMissingGlyph(u) else: print("Done") print("") ############################################################################ # Test the "use typo metrics" bit print("Testing OS/2 version... ", end="") if font.os2_version and font.os2_version < 4: print("Failed") print("Warning: OS/2 version does not support USE_TYPO_METRICS!", file=sys.stderr) else: print("Done") print("Testing USE_TYPO_METRICS... ", end="") if not font.os2_use_typo_metrics: print("Failed") print("Warning: USE_TYPO_METRICS set to false in the OS/2 table!", file=sys.stderr) else: print("Done") print("") ############################################################################ # Ensure that the MathConstants table exists. print("Testing MathConstants table... ", end="") if not font.math.exists(): print("Not found!") print("Creating a new MathConstants table... Done") # Dummy read operation to force the creation of the table. font.math.ScriptPercentScaleDown else: print("Done") print("") # ScriptPercentScaleDown # Suggested value: 80% # ScriptScriptPercentScaleDown # Suggested value: 60% # DelimitedSubFormulaMinHeight # Suggested value: normal line height x 1.5 # DisplayOperatorMinHeight print("Testing DisplayOperatorMinHeight... ") if font.math.DisplayOperatorMinHeight == 0: print("Error: DisplayOperatorMinHeight is set to 0!", file=sys.stderr) if 0x4F in font: # use the height of the letter 'O' box = font[0x4F].boundingBox() suggestedValue = (box[3] - box[1]) * \ kLargeOpMinDisplayOperatorFactor print("Setting DisplayOperatorMinHeight to %d." % suggestedValue) font.math.DisplayOperatorMinHeight = suggestedValue for c in kLargeOperators: # Verify that the DisplayOperatorMinHeight ensure that the size of # operator will really be increased in display mode. if c not in font: continue print("Testing large operator U+%04X... " % c, end="") glyph = font[c] box = glyph.boundingBox() baseHeight = box[3] - box[1] print("Done") if (font.math.DisplayOperatorMinHeight < kLargeOpMinDisplayOperatorFactor * baseHeight): print( "Warning: DisplayOperatorMinHeight is less than %f times the base height of U+%04X." % (kLargeOpMinDisplayOperatorFactor, c), file=sys.stderr) print("") # MathLeading # AxisHeight # Note: FontForge defaults to zero. # See https://github.com/fontforge/fontforge/pull/2242 if 0x2B in font: plusBoundingBox = font[0x2B].boundingBox() suggestedValue = (plusBoundingBox[1] + plusBoundingBox[3]) / 2 else: suggestedValue = 0 print("Testing AxisHeight... ", end="") if font.math.AxisHeight == 0: print("Failed") print("Error: AxisHeight is set to 0!", file=sys.stderr) if suggestedValue > 0: print("Setting AxisHeight to %d." % suggestedValue) font.math.AxisHeight = suggestedValue else: print("Done") if (suggestedValue > 0 and (abs(font.math.AxisHeight - suggestedValue) > tolerance)): print("Warning: AxisHeight is set to %d while the center of the\ plus sign is %d." % (font.math.AxisHeight, suggestedValue), file=sys.stderr) print("") # AccentBaseHeight # FlattenedAccentBaseHeight # SubscriptShiftDown # SubscriptTopMax # SubscriptBaselineDropMin # SuperscriptShiftUp # SuperscriptShiftUpCramped # SuperscriptBottomMin # SuperscriptBaselineDropMax # SubSuperscriptGapMin # SuperscriptBottomMaxWithSubscript # SpaceAfterScript # UpperLimitGapMin # UpperLimitBaselineRiseMin # LowerLimitGapMin # LowerLimitBaselineDropMin # StackTopShiftUp # StackTopDisplayStyleShiftUp # StackBottomShiftDown # StackBottomDisplayStyleShiftDown # StackGapMin # StackDisplayStyleGapMin # StretchStackTopShiftUp # StretchStackBottomShiftDown # StretchStackGapAboveMin # StretchStackGapBelowMin # FractionNumeratorShiftUp # FractionNumeratorDisplayStyleShiftUp # FractionDenominatorShiftDown # FractionDenominatorDisplayShiftDown # FractionNumeratorGapMin # FractionNumDisplayStyleGapMin # FractionRuleThickness # Suggested value: default rule thickness print("Testing FractionRuleThickness... ", end="") if font.math.FractionRuleThickness == 0: print("Failed") print("Error: FractionRuleThickness is set to 0!", file=sys.stderr) print("Setting FractionRuleThickness to %d." % font.uwidth) font.math.FractionRuleThickness = font.uwidth else: print("Done") print("") # FractionDenominatorGapMin # FractionDenomDisplayStyleGapMin # SkewedFractionHorizontalGap # SkewedFractionVerticalGap # OverbarVerticalGap # OverbarRuleThickness # Suggested value: default rule thickness print("Testing OverbarRuleThickness... ", end="") if font.math.OverbarRuleThickness == 0: print("Failed") print("Error: OverbarRuleThickness is set to 0!", file=sys.stderr) print("Setting OverBarRuleThickness to %d." % font.uwidth) font.math.OverbarRuleThickness = font.uwidth else: print("Done") print("") # OverbarExtraAscender # UnderbarVerticalGap # UnderbarRuleThickness print("Testing UnderbarRuleThickness... ", end="") if font.math.UnderbarRuleThickness == 0: print("Failed") print("Error: UnderbarRuleThickness is set to 0!", file=sys.stderr) print("Setting OverBarRuleThickness to %d." % font.uwidth) font.math.UnderbarRuleThickness = font.uwidth else: print("Done") print("") # UnderbarExtraDescender # RadicalVerticalGap # RadicalDisplayStyleVerticalGap # Suggested value: default rule thickness + 1/4 x-height # Note: FontForge defaults to zero. # See https://github.com/fontforge/fontforge/pull/2224 print("Testing RadicalDisplayStyleVerticalGap... ", end="") suggestedValue = font.uwidth + font.xHeight / 4 if font.math.RadicalDisplayStyleVerticalGap == 0: print("Failed") print("Error: RadicalDisplayStyleVerticalGap is set to 0!", file=sys.stderr) print("Setting RadicalDisplayStyleVerticalGap to %d." % suggestedValue) font.math.RadicalDisplayStyleVerticalGap = suggestedValue else: print("Done") print("") # RadicalRuleThickness # Suggested value: default rule thickness # Note: FontForge defaults to zero. # See https://github.com/fontforge/fontforge/pull/2224 print("Testing RadicalRuleThickness... ", end="") if font.math.RadicalRuleThickness == 0: print("Failed") print("Error: RadicalRuleThickness is set to 0!", file=sys.stderr) print("Setting RadicalRuleThickness to %d." % font.uwidth) font.math.RadicalRuleThickness = font.uwidth else: print("Done") print("") # RadicalExtraAscender # RadicalKernBeforeDegree # RadicalKernAfterDegree # RadicalDegreeBottomRaisePercent ############################################################################ # Verify whether the MathVariant table has appropriate data for some basic # unicode constructions. print("Testing Unicode Constructions in the MathVariant table...") for c in kUnicodeConstructions: codePoint = c[0] isVertical = c[1] variants = c[2] parts = c[3] if variants is None and parts is None: raise ("no data provided for glyph U+%04X" % codePoint) continue # Verify whether the character is present. print("Testing base glyph for U+%04X... " % codePoint, end="") if codePoint not in font: print("Failed") warnMissingGlyph(codePoint) continue print("Done") glyph = font[codePoint] # Verify whether the variants are available. if variants is not None: print("Testing variants for U+%04X... " % codePoint, end="") v = None if isVertical: v = glyph.verticalVariants else: v = glyph.horizontalVariants if v is not None: print("Done") else: print("Failed") print("Warning: missing variants for operator U+%04X!" % codePoint, file=sys.stderr) print("Setting variants for operator U+%04X... " % codePoint, end="") # FIXME: Is it really necessary to specify the base glyph? # This is done in Latin Modern but not XITS. v = "%s " % font[codePoint].glyphname allGlyphsAvailable = True for u in variants: if u not in font: warnMissingGlyph(u) allGlyphsAvailable = False break v += "%s " % font[u].glyphname if not allGlyphsAvailable: print("Failed") else: # Set the variants if isVertical: glyph.verticalVariants = v else: glyph.horizontalVariants = v print("Done") # Verify whether the components are available. if parts is not None: print("Testing components for U+%04X... " % codePoint, end="") components = None if isVertical: components = glyph.verticalComponents else: components = glyph.horizontalComponents if components: print("Done") else: print("Failed") print("Warning: missing components for operator U+%04X!" % codePoint, file=sys.stderr) print("Setting components for operator U+%04X... " % codePoint, end="") # Verify that all parts are available allGlyphsAvailable = True components = [] overlap = font.math.MinConnectorOverlap i = 0 for p in parts: if p[0] not in font: warnMissingGlyph(p[0]) allGlyphsAvailable = False break if i == 0: startConnectorLength = 0 else: startConnectorLength = overlap if i == len(parts) - 1: endConnectorLength = 0 else: endConnectorLength = overlap boundingBox = font[p[0]].boundingBox() if isVertical: fullAdvance = int(boundingBox[3] - boundingBox[1]) else: fullAdvance = int(boundingBox[2] - boundingBox[0]) components.append( (font[p[0]].glyphname, p[1], startConnectorLength, endConnectorLength, fullAdvance)) i = i + 1 if not allGlyphsAvailable: print("Failed") else: # Set the components. # Note: this makes FontForge crash. # See https://github.com/fontforge/fontforge/pull/2225 components = tuple(components) if isVertical: glyph.verticalComponents = components else: glyph.horizontalComponents = components print("Done") print("") ############################################################################ # Verify whether the MathVariant table has appropriate data for large # operators. print("Testing large operators in the MathVariant table...") for c in kLargeOperators: # Verify whether the character is present. print("Testing base glyph for large operator U+%04X... " % c, end="") if c not in font: print("Failed") warnMissingGlyph(c) continue print("Done") glyph = font[c] # Verify variants print("Testing variants for large operator U+%04X... " % c, end="") if glyph.verticalVariants is not None: # Verify whether DisplayOperatorMinHeight can be satisfied. variants = glyph.verticalVariants.split(" ") hasDisplaySize = False for v in variants: if v in font: box = font[v].boundingBox() height = box[3] - box[1] if font.math.DisplayOperatorMinHeight <= height: hasDisplaySize = True break if hasDisplaySize: print("Done") else: print("Failed") print( "Warning: U+%04X does not have any size variant of height at least DisplayOperatorMinHeight" % c, file=sys.stderr) else: print("Failed") print("Setting variants for operator U+%04X... " % c, end="") # Add a glyph for the operator in display mode baseGlyphName = font[c].glyphname displayGlyphName = "%s.display" % baseGlyphName g = font.createChar(-1, displayGlyphName) font.selection.select(baseGlyphName) font.copy() font.selection.select(displayGlyphName) font.paste() g.transform(psMat.scale(kLargeOpDisplayOperatorFactor), ("round", )) # FIXME: Is it really necessary to specify the base glyph? # This is done in Latin Modern but not XITS. glyph.verticalVariants = "%s %s" % (baseGlyphName, displayGlyphName) print("Done") print("") ############################################################################ # Verify whether integrals have italic correction print("Testing italic correction for integrals...") for c in kLargeOpIntegrals: if c not in font: continue print("Testing italic correction for operator U+%04X..." % c) # Get the list of variants, including the base size variants = font[c].verticalVariants.split(" ") baseGlyphName = font[c].glyphname if variants[0] != baseGlyphName: variants.insert(0, baseGlyphName) # Test italic correction for each variant for v in variants: if v in font: testItalicCorrection(font[v]) print("") ############################################################################ # Testing Prescripted Operators / ssty tables print("Testing Prescripted Operators / ssty tables...") for c in kPreScriptedOperators: testSSTY(font, c) print("") ############################################################################ # Testing Mathematical Alphanumeric Characters testMathVariants(font, "bold", ((0x1D400, 0x1D433), (0x1D6A8, 0x1D6E1), (0x1D7CA, 0x1D7CB), (0x1D7CE, 0x1D7D7)), aArgs.bold) testMathVariants(font, "italic", ((0x1D434, 0x1D454), (0x210E, ), (0x1D456, 0x1D467), (0x1D6A4, 0x1D6A5), (0x1D6E2, 0x1D6D6)), aArgs.italic) testMathVariants(font, "bold-italic", ((0x1D468, 0x1D49B), (0x1D71C, 0x1D755)), aArgs.bold_italic) testMathVariants(font, "script", ((0x1D49C, ), (0x212C, ), (0x1D49E, 0x1D49F), (0x2130, 0x2131), (0x1D4A2, ), (0x210B, ), (0x2110, ), (0x1D4A5, 0x1D4A6), (0x2112, ), (0x2133, ), (0x1D4A9, 0x1D4AC), (0x211B, ), (0x1D4AE, 0x1D4B9), (0x212F, ), (0x1D4BB, ), (0x210A, ), (0x1D4BD, 0x1D4C3), (0x2134, ), (0x1D4C5, 0x1D4CF)), None) testMathVariants(font, "bold-script", ((0x1D4D0, 0x1D503), ), None) testMathVariants(font, "fraktur", ((0x1D504, 0x1D505), (0x212D, ), (0x1D507, 0x1D50A), (0x210C, ), (0x2111, ), (0x1D50D, 0x1D514), (0x211C, ), (0x1D516, 0x1D51C), (0x2128, ), (0x1D51E, 0x1D537)), None) testMathVariants(font, "bold-fraktur", ((0x1D56C, 0x1D59F), ), None) testMathVariants(font, "sans-serif", ((0x1D5A0, 0x1D5D3), (0x1D7E2, 0x1D7EB)), aArgs.sans_serif) testMathVariants(font, "sans-serif-bold", ((0x1D5D4, 0x1D607), (0x1D756, 0x1D78F), (0x1D7EC, 0x1D7F5)), aArgs.sans_serif_bold) testMathVariants(font, "sans-serif-italic", ((0x1D608, 0x1D63B), ), aArgs.sans_serif_italic) testMathVariants(font, "sans-serif-bold-italic", ((0x1D63C, 0x1D66F), (0x1D790, 0x1D7C9)), aArgs.sans_serif_bold_italic) testMathVariants(font, "monospace", ((0x1D670, 0x1D6A3), (0x1D7F6, 0x1D7FF)), aArgs.monospace) testMathVariants(font, "double-struck", ((0x1D538, 0x1D539), (0x2102, ), (0x1D53B, 0x1D53E), (0x210D, ), (0x1D540, 0x1D544), (0x2115, ), (0x1D546, ), (0x2119, 0x211A), (0x211D, ), (0x1D54A, 0x1D550), (0x2124, ), (0x1D552, 0x1D56B), (0x1D7D8, 0x1D7E1), (0x1EEA1, 0x1EEA3), (0x1EEA5, 0x1EEA9), (0x1EEAB, 0x1EEBB)), None) testMathVariants(font, "initial", ((0x1EE21, 0x1EE22), (0x1EE24, ), (0x1EE27, ), (0x1EE29, 0x1EE32), (0x1EE34, 0x1EE37), (0x1EE39, ), (0x1EE3B, )), None) testMathVariants(font, "tailed", ((0x1EE42, ), (0x1EE47, ), (0x1EE49, ), (0x1EE4B, ), (0x1EE4D, 0x1EE4F), (0x1EE51, 0x1EE52), (0x1EE54, ), (0x1EE57, ), (0x1EE59, ), (0x1EE5B, ), (0x1EE5D, ), (0x1EE5F, )), None) testMathVariants(font, "looped", ((0x1EE80, 0x1EE89), (0x1EE8B, 0x1EE9B)), None) testMathVariants(font, "stretched", ((0x1EE61, 0x1EE62), (0x1EE64, ), (0x1EE67, 0x1EE6A), (0x1EE6C, 0x1EE72), (0x1EE74, 0x1EE77), (0x1EE79, 0x1EE7C), (0x1EE7E, )), None) ############################################################################ if aArgs.output: # Output the modified font. output = "%s.fixed.sfd" % aArgs.input print("Saving file %s... " % output, end="") font.save(output) print("Done") font.close()
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))
def script(font, felipa): vratio = 681 / 754 adjust = { # majuscule "𝒜": compose(mat.scale(0.9, 1), mat.translate(50, 0)), "𝒟": mat.translate(-20), "ℋ": mat.scale(0.87, 1), "ℐ": mat.translate(30), "𝒥": mat.translate(50), "𝒦": mat.scale(0.93, 1), "ℳ": mat.scale(0.72, 1), "𝒩": mat.scale(0.9, 1), "ℛ": mat.scale(0.95, 1), "𝒮": mat.translate(50, 0), "𝒰": mat.scale(0.9, 1), "𝒱": mat.scale(0.9, 1), "𝒲": mat.scale(0.67, 1), "𝒳": mat.scale(0.85, 1), "𝒴": compose(mat.scale(0.95, 1), mat.translate(-15, 0)), # miniscule "𝒻": mat.translate(100, 0), "𝒾": mat.translate(100, 0), "𝒿": mat.translate(100, 0), "𝓂": compose(mat.translate(-90, 0), mat.scale(0.98, 1)), "𝓅": mat.translate(40, 0), "𝓆": mat.translate(40, 0), "𝓇": mat.translate(40, 0), "𝓈": mat.translate(40, 0), "𝓉": mat.translate(40, 0), "𝓋": mat.translate(40, 0), "𝓍": mat.translate(40, 0), "𝓏": mat.translate(40, 0), } # majuscule dst = "𝒜ℬ𝒞𝒟ℰℱ𝒢ℋℐ𝒥𝒦ℒℳ𝒩𝒪𝒫𝒬ℛ𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵" src = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" for i in range(0, len(dst)): felipa.selection.select(("singletons", ), ord(src[i])) felipa.copy() font.selection.select(("singletons", ), ord(dst[i])) font.paste() glyph = font[ord(dst[i])] xform = mat.scale(vratio) if dst[i] in adjust: xform = mat.compose(xform, adjust[dst[i]]) glyph.transform(xform, ("round", )) glyph.width, glyph.vwidth = monosize # miniscule dst = "𝒶𝒷𝒸𝒹ℯ𝒻ℊ𝒽𝒾𝒿𝓀𝓁𝓂𝓃ℴ𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏" src = "abcdefghijklmnopqrstuvwxyz" for i in range(0, len(dst)): felipa.selection.select(("singletons", ), ord(src[i])) felipa.copy() font.selection.select(("singletons", ), ord(dst[i])) font.paste() glyph = font[ord(dst[i])] xform = mat.scale(vratio) if dst[i] in adjust: xform = mat.compose(xform, adjust[dst[i]]) xform = mat.compose(xform, mat.translate(60, 0)) glyph.transform(xform, ("round", )) glyph.width, glyph.vwidth = monosize
import psMat from sys import argv, stderr from GlyphComment import getfield fontforge.setPrefs('CoverageFormatsAllowed', 1) if len(argv) < 4: stderr.write("Usage: "+argv[0]+" ratio infile outfile\n") quit(1) widthScale = None widthList = {} try: widthScale = float(argv[1]) except ValueError: import csv reader = csv.reader(open(argv[1]), delimiter='\t') for data in reader: widthList[data[0]] = float(data[3]) font = fontforge.open(argv[2]) for glyph in font.glyphs(): if glyph.isWorthOutputting(): kagename = getfield(glyph, "Kage") glyphWidth = glyph.width glyphScale = widthList[kagename] if kagename in widthList else widthScale if glyphScale != 1.0: glyph.transform(psMat.scale(glyphScale, 1.0), ("partialRefs", "round")) glyph.width = glyphWidth font.save(argv[3])
def fraktur(font, unifracktur): unifracktur.selection.select(("singletons", ), "i", "j", "u") unifracktur.unlinkReferences() dst = "𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ" src = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" vratio = 683 / 1471 scale = [ 0.9, # A 0.8, # B 1.0, # C 0.9, # D 1.0, # E 0.95, # F 0.95, # G 0.95, # H 1.0, # I 1.0, # J 0.85, # K 1.0, # L 0.65, # M 0.8, # N 1.0, # O 0.8, # P 0.93, # Q 0.82, # R 0.95, # S 0.95, # T 1.0, # U 0.85, # V 0.65, # W 1.0, # X 0.83, # Y 1.0, # Z ] adjust = { '𝔣': mat.translate(220, 0), '𝔦': mat.translate(-100, 0), '𝔧': mat.translate(-455, 0), '𝔩': mat.translate(1970, 0), '𝔰': mat.translate(-65, 0), '𝔵': mat.translate(200, 0), } # majuscule for i in range(0, len(dst)): unifracktur.selection.select(("singletons", ), ord(src[i])) unifracktur.copy() font.selection.select(("singletons", ), ord(dst[i])) font.paste() glyph = font[ord(dst[i])] xform = compose( mat.scale(vratio), mat.scale(scale[i], 1), ) glyph.transform(xform, ("round", )) xform = moveToCenter(glyph) if dst[i] in adjust: xform = mat.compose(xform, adjust[dst[i]]) glyph.transform(xform, ("round", )) glyph.width, glyph.vwidth = monosize # miniscule dst = "𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷" src = "abcdefghijklmnopqrstuvwxyz" for i in range(0, len(dst)): unifracktur.selection.select(("singletons", ), ord(src[i])) unifracktur.copy() font.selection.select(("singletons", ), ord(dst[i])) font.paste() glyph = font[ord(dst[i])] xform = mat.scale(vratio) glyph.transform(xform, ("round", )) xform = moveToMonoNoSquash(glyph) if dst[i] in adjust: xform = mat.compose(xform, adjust[dst[i]]) glyph.transform(xform, ("round", )) # FIXME iju glyph.width, glyph.vwidth = monosize
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 arrows(font, dejavu): sideArrs = "←→↔↚↛↮" \ + "⇐⇒⇔⇍⇏⇎" \ + "↜↝↭⇜⇝" \ + "↞↠↼⇀" \ + "⇤⇥⟜⊸⧟" \ + "↢↣↩↪↤↦" \ + "⤆⤇" \ + "⤙⤚⤛⤜" tallArrs = "↑↓↕⇑⇓⇕" \ + "↟↡↾⇂" \ + "⤒⤓⫯⫰↥↧" for c in sideArrs: if ord(c) in dejavu: dejavu.selection.select(("singletons", ), ord(c)) dejavu.copy() font.selection.select(("singletons", ), ord(c)) font.paste() glyph = font[ord(c)] xform = mat.scale(monosize[0] / glyph.width) glyph.transform(xform, ('round', )) xFront, _, xBack, _ = glyph.layers[1].boundingBox() xform = aroundCentroid( glyph, mat.scale(glyph.width / (xBack - xFront + 20))) # xform = mat.translate(10 - xFront) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize for c in tallArrs: if ord(c) in dejavu: dejavu.selection.select(("singletons", ), ord(c)) dejavu.copy() font.selection.select(("singletons", ), ord(c)) font.paste() glyph = font[ord(c)] _, yBot, _, yTop = glyph.layers[1].boundingBox() xform = mat.scale(monosize[0] / (yTop - yBot)) glyph.transform(xform, ('round', )) xform = moveToCenter(glyph) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize glyph = mvGlyph(font, font, '⫯', '⊸') xform = aroundCentroid(glyph, mat.rotate(math.pi / 2)) glyph.transform(xform, ('round', )) glyph = mvGlyph(font, font, '⫰', '⫯') xform = aroundCentroid(glyph, mat.scale(1, -1)) glyph.transform(xform, ('round', )) glyph = mvGlyph(font, font, '⤓', '⤒') xform = aroundCentroid(glyph, mat.scale(1, -1)) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize glyph = mvGlyph(font, font, '⤆', '⤇') xform = aroundCentroid(glyph, mat.scale(-1, 1)) glyph.transform(xform, ('round', )) glyph = mvGlyph(font, font, '⤙', '⤚') xform = aroundCentroid(glyph, mat.scale(-1, 1)) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize glyph = mvGlyph(font, font, '⤛', '⤜') xform = aroundCentroid(glyph, mat.scale(-1, 1)) glyph.transform(xform, ('round', )) glyph.width, glyph.vwidth = monosize
def modify_iconsfordevs(_g): _g.transform(psMat.compose(psMat.scale(2), psMat.translate(0, -126))) _g.width = 1024 _g = align_to_center(_g) return _g
base_font.generate("otf/"+base_font.fontname+".otf") base_font.printSample("fontdisplay", 24, "", "specimens/"+base_font.fontname+".pdf") print("Generated regular") base_font.close() ## condensed base_font = fontforge.open("sfd/14seg-gen.sfd") base_font.selection.all() matrix = psMat.scale(0.5, 1.0) base_font.transform(matrix) base_font.fullname = "LCD Display: 14 Segment (Condensed)" base_font.fontname = "LCD14Condensed" base_font.os2_width = 3 # Condensed base_font.save("sfd/14seg-condensed.sfd") base_font.generate("otf/"+base_font.fontname+".otf") base_font.printSample("fontdisplay", 24, "", "specimens/"+base_font.fontname+".pdf")
# Create new font, set some properties. dst = fontforge.font() dst.fontname = "MSyms" dst.familyname = "MusicalSymbolsForPlaytab" dst.fullname = "Musical Symbols For Playtab" dst.copyright = "Free as a bird" dst.version = "000.100" dst.em = 1000; # Next slot for glyphs. ind = 33; # Copy glyphs from MusicSymbols. src = fontforge.open("MusicSymbols.ttf") gcopy( "Sharp", 0x0023 ) gcopy( "Flat", 0x0062 ) gcopy( "Natural", 0x006e ) gcopy( "Same2", 0x00d4 ) stretch = psMat.scale( 1, 1.2 ) # Copy glyphs from TimesRoman. src = fontforge.open("../../../.fonts/TimesNewRomanPSMT.ttf") gcopy( "Dim", 0x006f, stretch ) gcopy( "HDim", 0x00f8, stretch ) gcopy( "Major", 0x0394 ) # Generate new font. dst.generate("new.pfa")
import fontforge import sys import os import struct import psMat src = fontforge.open("Myriad-CnSemibold.ttf") src.fontname = "Myriad-UcnSemibold" src.fullname = "Myriad Ultra-Condensed Semibold" src.copyright = "As free as Myriad-CnSemibold." src.version = "001.200" src.em = 1024 cnd = psMat.scale(0.7, 1) src.selection.all() for glyph in src.selection.byGlyphs: glyph.transform(cnd) src.generate("Myriad-UcnSemibold.ttf") src = fontforge.open("FreeSans.ttf") src.fontname = "FreeSansCn" src.fullname = "Free Sans Condensed" src.copyright = "As free as Free Sans." src.version = "001.200" src.em = 1024 cnd = psMat.scale(0.7, 1) src.selection.all()
def squish_layer(layer, squish): layer = layer.dup() mat = psMat.scale(squish, 1.0) layer.transform(mat) return layer
def add_dejavu(_f, conf): dejavu = None weight_name = conf.get('weight_name') if weight_name == "Regular": dejavu = fontforge.open('./sourceFonts/DejaVuSansMono.ttf') elif weight_name == "Bold": dejavu = fontforge.open('./sourceFonts/DejaVuSansMono-Bold.ttf') for g in dejavu.glyphs(): g.transform( psMat.compose(psMat.scale(0.45, 0.45), psMat.translate(-21, 0))) g.width = 512 _f.importLookups(dejavu, dejavu.gpos_lookups) # 0x0300 - 0x036f - Combining Diacritical Marks for g in dejavu.glyphs(): if g.encoding < 0x0300 or g.encoding > 0x036f: continue else: if len(g.references) > 0: anchorPoints = g.anchorPoints g.anchorPoints = () g.transform(psMat.scale(2.22, 2.22)) g.transform(psMat.translate(50, 0)) g.width = 512 g.anchorPoints = anchorPoints if g.encoding <= 0x0304: anchorPoints = g.anchorPoints g.anchorPoints = () g.transform(psMat.scale(1.2, 1.2)) g.transform(psMat.translate(-100, -60)) g.width = 512 g.anchorPoints = anchorPoints elif g.encoding == 0x0305: g.transform(psMat.translate(0, -60)) elif g.encoding <= 0x0315: g.transform(psMat.translate(0, 0)) elif g.encoding <= 0x0317: g.transform(psMat.translate(0, 302)) elif g.encoding <= 0x0319: g.transform(psMat.translate(0, 200)) elif g.encoding <= 0x031b: g.transform(psMat.translate(0, -60)) elif g.encoding <= 0x031c: g.transform(psMat.translate(0, 22)) elif g.encoding <= 0x031f: g.transform(psMat.translate(0, 141)) elif g.encoding <= 0x0332: g.transform(psMat.translate(0, 90)) elif g.encoding == 0x0333: g.transform( psMat.compose(psMat.scale(0.9, 0.9), psMat.translate(-415, 29))) g.width = 512 elif g.encoding <= 0x0338: g.transform(psMat.translate(0, 0)) elif g.encoding <= 0x033c: g.transform(psMat.translate(0, 138)) else: g.transform(psMat.translate(0, 0)) dejavu.selection.select(g.encoding) dejavu.copy() _f.selection.select(g.encoding) _f.paste() # 0x0370 - 0x03ff - GREEK for g in dejavu.glyphs(): if g.encoding < 0x0370 or g.encoding > 0x03ff: continue else: if len(g.references) == 0: bb = g.boundingBox() g.anchorPoints = (('Anchor-7', 'mark', 256, bb[3] + 20), ) dejavu.selection.select(g.encoding) dejavu.copy() _f.selection.select(g.encoding) _f.paste() # 0x2100 - 0x214f Letterlike Symbols for g in dejavu.glyphs(): if g.encoding < 0x2100 or g.encoding > 0x214f or g.encoding == 0x2122: continue else: if len(g.references) == 0: dejavu.selection.select(g.encoding) dejavu.copy() _f.selection.select(g.encoding) _f.paste() # 0x2150 - 0x218f Number Forms for g in dejavu.glyphs(): if g.encoding < 0x2150 or g.encoding > 0x218f: continue else: if len(g.references) == 0: dejavu.selection.select(g.encoding) dejavu.copy() _f.selection.select(g.encoding) _f.paste() # 0x2190 - 0x21ff Arrows for g in dejavu.glyphs(): if g.encoding < 0x2190 or g.encoding > 0x21ff: continue else: if len(g.references) == 0: dejavu.selection.select(g.encoding) dejavu.copy() _f.selection.select(g.encoding) _f.paste() # 0x2200 - 0x22ff Mathematical Operators for g in dejavu.glyphs(): if g.encoding < 0x2200 or g.encoding > 0x22ff: continue else: if len(g.references) == 0: dejavu.selection.select(g.encoding) dejavu.copy() _f.selection.select(g.encoding) _f.paste() # 0x2300 - 0x23ff Miscellaneous Technical for g in dejavu.glyphs(): if g.encoding < 0x2300 or g.encoding > 0x23ff: continue else: if len(g.references) == 0: dejavu.selection.select(g.encoding) dejavu.copy() _f.selection.select(g.encoding) _f.paste() dejavu.close() return _f
base_font.generate("otf/" + base_font.fontname + ".otf") base_font.printSample("fontdisplay", 24, "", "specimens/" + base_font.fontname + ".pdf") print("Generated regular") base_font.close() ## condensed base_font = fontforge.open("sfd/14seg-gen.sfd") base_font.selection.all() matrix = psMat.scale(0.5, 1.0) base_font.transform(matrix) base_font.fullname = "LCD Display: 14 Segment (Condensed)" base_font.fontname = "LCD14Condensed" base_font.os2_width = 3 # Condensed base_font.save("sfd/14seg-condensed.sfd") base_font.generate("otf/" + base_font.fontname + ".otf") base_font.printSample("fontdisplay", 24, "", "specimens/" + base_font.fontname + ".pdf")
def makeQuran(infile, outfile, feafile, version): font = makeDesktop(infile, outfile, feafile, version, False, False) # fix metadata font.fontname = font.fontname.replace("-Regular", "Quran-Regular") font.familyname += " Quran" font.fullname += " Quran" digits = ("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine") mergeLatin(font, feafile, glyphs=digits, quran=True) punct = ("period", "guillemotleft", "guillemotright", "braceleft", "bar", "braceright", "bracketleft", "bracketright", "parenleft", "parenright", "slash", "backslash") for name in punct: if name+".ara" in font: glyph = font[name+".ara"] glyph.glyphname = name glyph.unicode = fontforge.unicodeFromName(name) # abuse U+065C as a below form of U+06EC, for Qaloon dotabove = font["uni06EC"] dotbelow = font["uni065C"] delta = dotbelow.boundingBox()[-1] - dotabove.boundingBox()[-1] dotbelow.references = [] dotbelow.addReference(dotabove.glyphname, psMat.translate(0, delta)) dotbelow.addAnchorPoint("TashkilTashkilBelow", "basemark", 220, dotbelow.boundingBox()[1] - 100) # scale some vowel marks and dots down a bit font["uni0651"].transform(psMat.scale(0.8)) for mark in ("uni064B", "uni064C", "uni064E", "uni064F", "uni06E1"): font[mark].transform(psMat.scale(0.9)) for dot in ("TwoDots.a", "ThreeDots.a", "vTwoDots.a"): font[dot].transform(psMat.scale(0.9)) quran_glyphs = [] # create dummy glyphs used for some coding hacks for i in range(1, 11): dummy = font.createChar(-1, "dummy%s" %i) dummy.width = 0 quran_glyphs.append(dummy.glyphname) mergeFeatures(font, feafile) quran_glyphs += digits quran_glyphs += punct quran_glyphs += ("space", "uni060C", "uni0615", "uni0617", "uni0618", "uni0619", "uni061A", "uni061B", "uni061E", "uni061F", "uni0621", "uni0622", "uni0623", "uni0624", "uni0625", "uni0626", "uni0627", "uni0628", "uni0629", "uni062A", "uni062B", "uni062C", "uni062D", "uni062E", "uni062F", "uni0630", "uni0631", "uni0632", "uni0633", "uni0634", "uni0635", "uni0636", "uni0637", "uni0638", "uni0639", "uni063A", "uni0640", "uni0641", "uni0642", "uni0643", "uni0644", "uni0645", "uni0646", "uni0647", "uni0648", "uni0649", "uni064A", "uni064B", "uni064C", "uni064D", "uni064E", "uni064F", "uni0650", "uni0651", "uni0652", "uni0653", "uni0654", "uni0655", "uni0656", "uni0657", "uni0658", "uni065C", "uni0660", "uni0661", "uni0662", "uni0663", "uni0664", "uni0665", "uni0666", "uni0667", "uni0668", "uni0669", "uni066E", "uni066F", "uni06A1", "uni06BA", "uni0670", "uni0671", "uni067A", "uni06CC", "uni06D6", "uni06D7", "uni06D8", "uni06D9", "uni06DA", "uni06DB", "uni06DC", "uni06DD", "uni06DE", "uni06DF", "uni06E0", "uni06E1", "uni06E2", "uni06E3", "uni06E4", "uni06E5", "uni06E6", "uni06E7", "uni06E8", "uni06E9", "uni06EA", "uni06EB", "uni06EC", "uni06ED", "uni06F0", "uni06F1", "uni06F2", "uni06F3", "uni06F4", "uni06F5", "uni06F6", "uni06F7", "uni06F8", "uni06F9", "uni08F0", "uni08F1", "uni08F2", "uni2000", "uni2001", "uni2002", "uni2003", "uni2004", "uni2005", "uni2006", "uni2007", "uni2008", "uni2009", "uni200A", "uni200B", "uni200C", "uni200D", "uni200E", "uni200F", "uni2028", "uni2029", "uni202A", "uni202B", "uni202C", "uni202D", "uni202E", "uni202F", "uni25CC", "uniFDFA", "uniFDFD") quran_glyphs += ("uni030A", "uni0325") # ring above and below subsetFont(font, quran_glyphs, True) # set font ascent to the highest glyph in the font so that waqf marks don't # get truncated # we could have set os2_typoascent_add and hhea_ascent_add, but ff makes # the offset relative to em-size in the former and font bounds in the # later, but we want both to be relative to font bounds ymax = 0 for glyph in font.glyphs(): bb = glyph.boundingBox() if bb[-1] > ymax: ymax = bb[-1] font.os2_typoascent = font.hhea_ascent = ymax # create overline glyph to be used for sajda line, it is positioned # vertically at the level of the base of waqf marks overline_pos = font[0x06D7].boundingBox()[1] makeOverUnderline(font, under=False, o_pos=overline_pos) generateFont(font, outfile)
def scale(glyph, scalex, scaley): glyph.transform(psMat.scale(scalex, scaley))
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 modify_nerd(_g): align_left = [ 0xe0b0, 0xe0b1, 0xe0b4, 0xe0b5, 0xe0b8, 0xe0b9, 0xe0bc, 0xe0bd, 0xe0c0, 0xe0c1, 0xe0c4, 0xe0c6, 0xe0c8, 0xe0cc, 0xe0cd, 0xe0d1, 0xe0d2, ] align_right = [ 0xe0b2, 0xe0b3, 0xe0b6, 0xe0b7, 0xe0b7, 0xe0ba, 0xe0bb, 0xe0be, 0xe0bf, 0xe0c2, 0xe0c3, 0xe0c5, 0xe0c7, 0xe0ca, 0xe0ce, 0xe0d4, ] # Powerline if _g.encoding >= 0xe0b0 and _g.encoding <= 0xe0d4: _g.transform(psMat.translate(0, -55)) _g.width = 1024 if _g.encoding >= 0xe0b0 and _g.encoding <= 0xe0b7: _g.transform( psMat.compose(psMat.scale(1.0, 0.982), psMat.translate(0, -1))) if _g.encoding in align_right: bb = _g.boundingBox() left = 1024 - (bb[2] - bb[0]) _g.left_side_bearing = left _g.width = 1024 if _g.encoding in align_left: _g.left_side_bearing = 0 _g.width = 1024 if _g.encoding >= 0xe0b8 and _g.encoding <= 0xe0bf: _g.transform( psMat.compose(psMat.scale(0.8, 0.982), psMat.translate(0, -1))) if _g.encoding in align_right: bb = _g.boundingBox() left = 1024 - (bb[2] - bb[0]) _g.left_side_bearing = left _g.width = 1024 if _g.encoding in align_left: _g.left_side_bearing = 0 _g.width = 1024 if _g.encoding >= 0xe0c0 and _g.encoding <= 0xe0c3: _g.transform(psMat.scale(0.7, 1.0)) if _g.encoding in align_right: bb = _g.boundingBox() left = 1024 - (bb[2] - bb[0]) _g.left_side_bearing = left _g.width = 1024 if _g.encoding in align_left: _g.left_side_bearing = 0 _g.width = 1024 if _g.encoding >= 0xe0c4 and _g.encoding <= 0xe0c7: if _g.encoding in align_right: bb = _g.boundingBox() left = 1024 - (bb[2] - bb[0]) _g.left_side_bearing = left _g.width = 1024 if _g.encoding in align_left: _g.left_side_bearing = 0 _g.width = 1024 if _g.encoding == 0xe0c8 or _g.encoding == 0xe0ca: _g.transform(psMat.scale(0.7, 1.0)) if _g.encoding in align_right: bb = _g.boundingBox() left = 1024 - (bb[2] - bb[0]) _g.left_side_bearing = left _g.width = 1024 if _g.encoding in align_left: _g.left_side_bearing = 0 _g.width = 1024 if _g.encoding == 0xe0ce: _g.transform(psMat.scale(0.8, 1.0)) bb = _g.boundingBox() left = 1024 - (bb[2] - bb[0]) _g.left_side_bearing = left _g.width = 1024 if _g.encoding == 0xe0cf: _g.transform(psMat.scale(0.9, 1.0)) _g = align_to_center(_g) if _g.encoding == 0xe0d0: _g = align_to_center(_g) if _g.encoding == 0xe0d1: _g.transform( psMat.compose(psMat.scale(1.0, 0.982), psMat.translate(0, -1))) _g.left_side_bearing = 0 _g.width = 1024 if _g.encoding == 0xe0d2 or _g.encoding == 0xe0d4: _g.transform( psMat.compose(psMat.scale(1.0, 0.982), psMat.translate(0, -1))) if _g.encoding in align_right: bb = _g.boundingBox() left = 1024 - (bb[2] - bb[0]) _g.left_side_bearing = left _g.width = 1024 if _g.encoding in align_left: _g.left_side_bearing = 0 _g.width = 1024 elif _g.encoding >= 0xf000 and _g.encoding <= 0xf2e0: _g.transform( psMat.compose(psMat.scale(0.75, 0.75), psMat.translate(0, 55))) _g.width = 1024 _g = align_to_center(_g) else: _g.transform(psMat.translate(0, -55)) _g.width = 1024 _g = align_to_center(_g) return _g
if not os.path.exists(sys.argv[arg]): print ("ファイル名 " + sys.argv[arg] + " が見つかりません。") exit(0) # 拡縮倍率 scale_x = 0.8 scale_y = 0.9 cidFontFileName = sys.argv[1] # ファイルを開く cidFont = fontforge.open(cidFontFileName) #cidFont.em = 1000 for g in cidFont.glyphs(): prevWidth = g.width prevWidth = 500 g.transform(psMat.scale(scale_x, scale_y)); movedist = prevWidth / 2 - g.width / 2; g.width = prevWidth; g.transform(psMat.translate(movedist, 0)); g.width = prevWidth; cidFont.generate(sys.argv[2] + ".ps") #cidFont.save(sys.argv[2]+".sfd") cidFont.close()
def resize_supersub(_f): superscripts = [ { "src": 0x0031, "dest": 0x00b9 }, { "src": 0x0032, "dest": 0x00b2 }, { "src": 0x0033, "dest": 0x00b3 }, { "src": 0x0030, "dest": 0x2070 }, { "src": 0x0069, "dest": 0x2071 }, { "src": 0x0034, "dest": 0x2074 }, { "src": 0x0037, "dest": 0x2077 }, { "src": 0x0038, "dest": 0x2078 }, { "src": 0x0039, "dest": 0x2079 }, { "src": 0x002b, "dest": 0x207a }, { "src": 0x002d, "dest": 0x207b }, { "src": 0x003d, "dest": 0x207c }, { "src": 0x0028, "dest": 0x207d }, { "src": 0x0029, "dest": 0x207e }, { "src": 0x006e, "dest": 0x207f }, # ↓上付きの大文字 { "src": 0x0041, "dest": 0x1d2c }, { "src": 0x00c6, "dest": 0x1d2d }, { "src": 0x0042, "dest": 0x1d2e }, { "src": 0x0044, "dest": 0x1d30 }, { "src": 0x0045, "dest": 0x1d31 }, { "src": 0x018e, "dest": 0x1d32 }, { "src": 0x0047, "dest": 0x1d33 }, { "src": 0x0048, "dest": 0x1d34 }, { "src": 0x0049, "dest": 0x1d35 }, { "src": 0x004a, "dest": 0x1d36 }, { "src": 0x004b, "dest": 0x1d37 }, { "src": 0x004c, "dest": 0x1d38 }, { "src": 0x004d, "dest": 0x1d39 }, { "src": 0x004e, "dest": 0x1d3a }, ## ↓REVERSED N なのでNを左右反転させる必要あり { "src": 0x004e, "dest": 0x1d3b, "reversed": True }, { "src": 0x004f, "dest": 0x1d3c }, { "src": 0x0222, "dest": 0x1d3d }, { "src": 0x0050, "dest": 0x1d3e }, { "src": 0x0052, "dest": 0x1d3f }, { "src": 0x0054, "dest": 0x1d40 }, { "src": 0x0055, "dest": 0x1d41 }, { "src": 0x0057, "dest": 0x1d42 }, # ↓上付きの小文字 { "src": 0x0061, "dest": 0x1d43 }, { "src": 0x0250, "dest": 0x1d44 }, { "src": 0x0251, "dest": 0x1d45 }, { "src": 0x1d02, "dest": 0x1d46 }, { "src": 0x0062, "dest": 0x1d47 }, { "src": 0x0064, "dest": 0x1d48 }, { "src": 0x0065, "dest": 0x1d49 }, { "src": 0x0259, "dest": 0x1d4a }, { "src": 0x025b, "dest": 0x1d4b }, { "src": 0x025c, "dest": 0x1d4c }, { "src": 0x0067, "dest": 0x1d4d }, ## ↓TURNED i なので 180度回す必要あり { "src": 0x0069, "dest": 0x1d4e, "turned": True }, { "src": 0x006b, "dest": 0x1d4f }, { "src": 0x006d, "dest": 0x1d50 }, { "src": 0x014b, "dest": 0x1d51 }, { "src": 0x006f, "dest": 0x1d52 }, { "src": 0x0254, "dest": 0x1d53 }, { "src": 0x1d16, "dest": 0x1d54 }, { "src": 0x1d17, "dest": 0x1d55 }, { "src": 0x0070, "dest": 0x1d56 }, { "src": 0x0074, "dest": 0x1d57 }, { "src": 0x0075, "dest": 0x1d58 }, { "src": 0x1d1d, "dest": 0x1d59 }, { "src": 0x026f, "dest": 0x1d5a }, { "src": 0x0076, "dest": 0x1d5b }, { "src": 0x1d25, "dest": 0x1d5c }, { "src": 0x03b2, "dest": 0x1d5d }, { "src": 0x03b3, "dest": 0x1d5e }, { "src": 0x03b4, "dest": 0x1d5f }, { "src": 0x03c6, "dest": 0x1d60 }, { "src": 0x03c7, "dest": 0x1d61 }, { "src": 0x0056, "dest": 0x2c7d }, { "src": 0x0068, "dest": 0x02b0 }, { "src": 0x0266, "dest": 0x02b1 }, { "src": 0x006a, "dest": 0x02b2 }, { "src": 0x006c, "dest": 0x02e1 }, { "src": 0x0073, "dest": 0x02e2 }, { "src": 0x0078, "dest": 0x02e3 }, { "src": 0x0072, "dest": 0x02b3 }, { "src": 0x0077, "dest": 0x02b7 }, { "src": 0x0079, "dest": 0x02b8 }, { "src": 0x0063, "dest": 0x1d9c }, { "src": 0x0066, "dest": 0x1da0 }, { "src": 0x007a, "dest": 0x1dbb }, { "src": 0x0061, "dest": 0x00aa }, { "src": 0x0252, "dest": 0x1d9b }, { "src": 0x0255, "dest": 0x1d9d }, { "src": 0x00f0, "dest": 0x1d9e }, { "src": 0x025c, "dest": 0x1d9f }, { "src": 0x025f, "dest": 0x1da1 }, { "src": 0x0261, "dest": 0x1da2 }, { "src": 0x0265, "dest": 0x1da3 }, { "src": 0x0268, "dest": 0x1da4 }, { "src": 0x0269, "dest": 0x1da5 }, { "src": 0x026a, "dest": 0x1da6 }, { "src": 0x1d7b, "dest": 0x1da7 }, { "src": 0x029d, "dest": 0x1da8 }, { "src": 0x026d, "dest": 0x1da9 }, { "src": 0x1d85, "dest": 0x1daa }, { "src": 0x029f, "dest": 0x1dab }, { "src": 0x0271, "dest": 0x1dac }, { "src": 0x0270, "dest": 0x1dad }, { "src": 0x0272, "dest": 0x1dae }, { "src": 0x0273, "dest": 0x1daf }, { "src": 0x0274, "dest": 0x1db0 }, { "src": 0x0275, "dest": 0x1db1 }, { "src": 0x0278, "dest": 0x1db2 }, { "src": 0x0282, "dest": 0x1db3 }, { "src": 0x0283, "dest": 0x1db4 }, { "src": 0x01ab, "dest": 0x1db5 }, { "src": 0x0289, "dest": 0x1db6 }, { "src": 0x028a, "dest": 0x1db7 }, { "src": 0x1d1c, "dest": 0x1db8 }, { "src": 0x028b, "dest": 0x1db9 }, { "src": 0x028c, "dest": 0x1dba }, { "src": 0x0290, "dest": 0x1dbc }, { "src": 0x0291, "dest": 0x1dbd }, { "src": 0x0292, "dest": 0x1dbe }, { "src": 0x03b8, "dest": 0x1dbf }, ] subscripts = [{ "src": 0x0069, "dest": 0x1d62 }, { "src": 0x0072, "dest": 0x1d63 }, { "src": 0x0075, "dest": 0x1d64 }, { "src": 0x0076, "dest": 0x1d65 }, { "src": 0x03b2, "dest": 0x1d66 }, { "src": 0x03b3, "dest": 0x1d67 }, { "src": 0x03c1, "dest": 0x1d68 }, { "src": 0x03c6, "dest": 0x1d69 }, { "src": 0x03c7, "dest": 0x1d6a }, { "src": 0x006a, "dest": 0x2c7c }, { "src": 0x0030, "dest": 0x2080 }, { "src": 0x0031, "dest": 0x2081 }, { "src": 0x0032, "dest": 0x2082 }, { "src": 0x0033, "dest": 0x2083 }, { "src": 0x0034, "dest": 0x2084 }, { "src": 0x0035, "dest": 0x2085 }, { "src": 0x0036, "dest": 0x2086 }, { "src": 0x0037, "dest": 0x2087 }, { "src": 0x0038, "dest": 0x2088 }, { "src": 0x0039, "dest": 0x2089 }, { "src": 0x002b, "dest": 0x208a }, { "src": 0x002d, "dest": 0x208b }, { "src": 0x003d, "dest": 0x208c }, { "src": 0x0028, "dest": 0x208d }, { "src": 0x0029, "dest": 0x208e }, { "src": 0x0061, "dest": 0x2090 }, { "src": 0x0065, "dest": 0x2091 }, { "src": 0x006f, "dest": 0x2092 }, { "src": 0x0078, "dest": 0x2093 }, { "src": 0x0259, "dest": 0x2094 }, { "src": 0x0068, "dest": 0x2095 }, { "src": 0x006b, "dest": 0x2096 }, { "src": 0x006c, "dest": 0x2097 }, { "src": 0x006d, "dest": 0x2098 }, { "src": 0x006e, "dest": 0x2099 }, { "src": 0x0070, "dest": 0x209a }, { "src": 0x0073, "dest": 0x209b }, { "src": 0x0074, "dest": 0x209c }] for g in superscripts: _f.selection.select(g["src"]) _f.copy() _f.selection.select(g["dest"]) _f.paste() for g in subscripts: _f.selection.select(g["src"]) _f.copy() _f.selection.select(g["dest"]) _f.paste() for g in _f.glyphs("encoding"): if g.encoding > 0x2c7d: continue elif in_scripts(g.encoding, superscripts): if g.encoding == 0x1d5d or g.encoding == 0x1d61: g.transform(psMat.scale(0.70, 0.70)) elif g.encoding == 0x1d3b: g.transform(psMat.scale(0.75, 0.75)) g.transform( psMat.compose(psMat.scale(-1, 1), psMat.translate(g.width, 0))) elif g.encoding == 0x1d4e: g.transform(psMat.scale(0.75, 0.75)) g.transform(psMat.rotate(3.14159)) g.transform(psMat.translate(0, 512)) else: g.transform(psMat.scale(0.75, 0.75)) bb = g.boundingBox() g.transform(psMat.translate(0, 244)) align_to_center(g) elif in_scripts(g.encoding, subscripts): if g.encoding == 0x1d66 or g.encoding == 0x1d6a: g.transform(psMat.scale(0.70, 0.70)) else: g.transform(psMat.scale(0.75, 0.75)) bb = g.boundingBox() y = -144 if bb[1] < -60: # DESCENT - 144 y = -60 g.transform(psMat.translate(0, y)) align_to_center(g) return _f
def _set_proportion(font): mat = scale(SCALE_DOWN) font.selection.all() for glyph in list(font.selection.byGlyphs): glyph.transform(mat) glyph.width = WIDTH
circled.addReference(sc_name, psMat.translate((circle_width - sc.width) / 2, 0)) circled.addReference('modifier_circle') circled.width = circle_width modifier_lines_width = iso['modifier_lines'].width # create left variant modifier_left_width = iso['modifier_left'].width left = iso.createChar(-1, name + gm.suffixes.small_caps + gm.suffixes.modleft) left.addReference('modifier_left') left.addReference(sc_name, psMat.translate(modifier_left_width - (sc.width / 2), 0)) left.addReference( 'modifier_lines', psMat.compose(psMat.scale(sc.width / 2 / modifier_lines_width, 1), psMat.translate(modifier_left_width, 0))) left.width = modifier_left_width + (sc.width / 2) # create right variant modifier_right_width = iso['modifier_right'].width right = iso.createChar( -1, name + gm.suffixes.small_caps + gm.suffixes.modright) right.addReference('modifier_right', psMat.translate(sc.width / 2, 0)) right.addReference(sc_name) right.addReference( 'modifier_lines', psMat.scale(sc.width / 2 / modifier_lines_width, 1), ) right.width = modifier_left_width + (sc.width / 2) # create inline variant
#!/usr/bin/python # -*- coding: utf-8 -*- # 花園明朝フォント(実験版)を生成する。 # 注意! UCS4オプションでコンパイルしたPythonを使うこと! # 確認方法: ord(u'𠀀’) がちゃんと#x20000 になるかどうか。 import fontforge import psMat import sys, os, re, fnmatch, codecs mat1 = psMat.scale(1.05,1.05) mat2 = psMat.translate(-25,-25) mat3 = psMat.translate(-256,0) mat4 = psMat.scale(1.0, 2.0) aalt = {} # add_aalt で異体字を入れる。溢れないよう、数は抑制する。 vari_dir = '/home/kawabata/Dropbox/cvs/kanji-database/variants/' def make_font (name,style,version): global aalt font=fontforge.open("./basefont.ttf") font.os2_width=1024 aalt = {} font.sfntRevision = float(version) fullname_en="Hanazono Mincho "+style fullname_ja="花園明朝 "+style print ("now generating "+fullname_en+" ("+fullname_ja+")") # 注意。 # pid=1,eid=0,lid=0x0 の、id=4 は、自由文字列 (Hanazono-Mincho B) でいいが、 # pid=3,eid=1,lid=0x409 の、id=4 は、pid=3,eid=1,lid=0x409 の、id=6と同じでなければいけない。 # fontforgeでは、両方は同じである必要があるので、これは変更できない。
def m_scale(scaleX, scaleY): return m_compose(psMat.translate(-c_halfsize, -c_middle), psMat.scale(scaleX, scaleY), psMat.translate(c_halfsize, c_middle))
#Needs: fonts/StrokeTests.sfd import sys, fontforge, psMat stroketests = sys.argv[1] print("triangle") font = fontforge.open(stroketests) font.selection.all() triangle = fontforge.unitShape(3).transform(psMat.scale(25)) font.stroke("polygonal", triangle) font.close() print("square") font = fontforge.open(stroketests) font.selection.all() square = fontforge.unitShape(4).transform(psMat.scale(25)) font.stroke("polygonal", square) font.close() print("pentagon") font = fontforge.open(stroketests) font.selection.all() pent = fontforge.unitShape(5).transform(psMat.scale(25)) font.stroke("polygonal", pent) font.close() print("circular") font = fontforge.open(stroketests) font.selection.all() font.stroke("circular", 25)
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)))
lastX = point.x; lastY = point.y if hypot(firstX - lastX, firstY - lastY) > 3.0: newContour += tmpContour newContour.closed = True newLayer += newContour glyph.foreground = newLayer if (len(sys.argv) < 3): print 'Usage: %s source-file target-file' % sys.argv[0] quit(1) print 'Loading base file %s...' % sys.argv[1] BaseFont = fontforge.open(sys.argv[1]) for glyph in BaseFont.glyphs(): if glyph.isWorthOutputting(): if isJGlyphP(glyph): glyph.unlinkRef() print 'Changing weight...' for glyph in BaseFont.glyphs(): if glyph.isWorthOutputting(): if isJGlyph(glyph): glyph.transform(psMat.scale(2.0)) glyph.stroke("circular", 10, "butt", "miter", ("removeexternal", "cleanup")) fixContour(glyph) glyph.transform(psMat.scale(0.5)) print 'Saving target file %s...' % sys.argv[2] BaseFont.save(sys.argv[2])
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)))
font = fontforge.font() font.fontname = FONTNAME font.familyname = FONTNAME font.copyright = 'Some of symbols are imported from FontAwesome SVGs' font.fullname = FONTNAME try: for svg_file in os.listdir(DIR): if svg_file.find('.svg') == -1: # is not a svg file continue g = font.createChar(int(svg_file.split('-')[0], base=16)) print(svg_file) g.importOutlines(os.path.join(DIR, svg_file), ('removeoverlap', 'correctdir')) g.removeOverlap() move_mat = psMat.translate(-100, 0) g.transform(move_mat) scale_mat = psMat.scale(1.1) g.transform(scale_mat) except ValueError: print('"%s" is not a valid file name.' % svg_file) print( 'File name should be in format "hexnumber-name.svg". e.g. "ff5a-search.svg"' ) except Exception as e: raise e font.generate('%s.ttf' % FONTNAME)
#Needs: fonts/StrokeTests.sfd import sys, fontforge, psMat, math from collections import OrderedDict stroketests = sys.argv[1] shortlist = ['D', 'Q', 'asciicircum'] convex = fontforge.unitShape(7).transform(psMat.scale(25)) def do_stroke_test(short, *args, **kwargs): font=fontforge.open(stroketests) if short: font.selection.select(*shortlist) else: font.selection.all() font.stroke(*args, **kwargs) font.close() # Full font tests shapes = OrderedDict() shapes['triangle'] = fontforge.unitShape(3).transform(psMat.scale(25)) shapes['square'] = fontforge.unitShape(4).transform(psMat.scale(25)) shapes['pentagon'] = fontforge.unitShape(5).transform(psMat.scale(25)) shapes['circular'] = (25,) shapes['calligraphic'] = (25, 2, 45) for sn, s in shapes.items(): print(sn) if isinstance(s, tuple): do_stroke_test(False, sn, *s) else:
else: # Widen WidthInterpol = BaseFont.interpolateFonts((float(argv[3]) - 1.0) * 5.0, ExpandedFontFile) MidInterpol = WeightInterpol.interpolateFonts(0.5, WidthInterpol.path) Interpolated = BaseFont.interpolateFonts(2.0, MidInterpol.path) WeightInterpol.close() WeightInterpol = None WidthInterpol.close() WidthInterpol = None MidInterpol.close() MidInterpol = None # Change width of font for glyph in BaseFont.glyphs(): if glyph.isWorthOutputting(): glyph.transform(scale(float(argv[3]), 1.0), ("partialRefs", )) (kernFirst, kernSecond, kernVal) = BaseFont.getKerningClass("Kerning-1") BaseFont.alterKerningClass( "Kerning-1", kernFirst, kernSecond, tuple(map(lambda x: int(round(float(x) * float(argv[3]))), kernVal))) # Set output font properties BaseFont.strokedfont = False BaseFont.fontname = FamilyName + WidthDat[WidthCode]['Name'] + "-" + WeightDat[ WeightCode]['Name'] BaseFont.familyname = HumanReadableFamilyName + WidthDat[WidthCode][ 'HumanReadableName'] BaseFont.fullname = HumanReadableFamilyName + WidthDat[WidthCode][ 'Abbr'] + " " + WeightDat[WeightCode]['Abbr'] BaseFont.weight = WeightDat[WeightCode]['HumanReadableName'] BaseFont.os2_weight = (WeightCode + 1) * 100
de_font.selection.none() # EastAsianWidthで全角(WまたはF)のグリフを削除 for glyph in de_font.glyphs(): if glyph.unicode > 0 and unicodedata.east_asian_width(chr( glyph.unicode)) in ("W", "F"): de_font.selection.select(("more", ), glyph.glyphname) de_font.clear() # modify em de_font.em = newfont_em de_font.ascent = newfont_ascent de_font.descent = newfont_descent # scale down for glyph in de_font.glyphs(): glyph.transform(psMat.scale(0.83)) glyph.width = 500 de_font.selection.all() de_font.round() de_font.generate("../Work/modDejaVu.ttf", '', generate_flags) de_font.close() ######################################## # modified GenShin ######################################## print() print("Open " + srcfont_GenShin) fGs = fontforge.open(srcfont_GenShin)
def copy_glyphs(sourceFont, sourceFontStart, sourceFontEnd, symbolFont, symbolFontStart, symbolFontEnd, exactEncoding=False): if exactEncoding is False: sourceFontList = [] sourceFontCounter = 0 for i in xrange(sourceFontStart, sourceFontEnd + 1): sourceFontList.append(format(i, 'X')) # Create glyphs from symbol font symbolFont.selection.select(("ranges", "unicode"), symbolFontStart, symbolFontEnd) sourceFont.selection.select(("ranges", "unicode"), sourceFontStart, sourceFontEnd) for sym_glyph in symbolFont.selection.byGlyphs: #sym_attr = SYM_ATTR[sym_glyph.unicode] glyphName = sym_glyph.glyphname if exactEncoding: # use the exact same hex values for the source font as for the symbol font currentSourceFontGlyph = sym_glyph.encoding copiedToSlot = str(sym_glyph.unicode) else: # use source font defined hex values based on passed in start and end # convince that this string really is a hex: currentSourceFontGlyph = int( "0x" + sourceFontList[sourceFontCounter], 16) copiedToSlot = sourceFontList[sourceFontCounter] if args.quiet == False: print "updating glyph: " + str(sym_glyph) + " " + str( sym_glyph.glyphname) + " putting at: " + str(copiedToSlot) # Prepare symbol glyph dimensions sym_dim = get_dim(sym_glyph) # Select and copy symbol from its encoding point symbolFont.selection.select(sym_glyph.encoding) symbolFont.copy() # check it if args.careful: if copiedToSlot.startswith("uni"): copiedToSlot = copiedToSlot[3:] codepoint = int("0x" + copiedToSlot, 16) try: sourceFont[codepoint] except TypeError: # nothing there go ahead and paste at this codepoint sourceFont.selection.select(currentSourceFontGlyph) sourceFont.paste() else: sourceFont.selection.select(currentSourceFontGlyph) sourceFont.paste() sourceFont[currentSourceFontGlyph].glyphname = glyphName if args.single: # Now that we have copy/pasted the glyph, it's time to scale and move it # Handle glyph stretching #if 'x' in sym_attr['stretch']: # # Stretch the glyph horizontally # scale_ratio = font_dim['width'] / sym_dim['width'] # sourceFont.transform(psMat.scale(scale_ratio, 1)) #if 'y' in sym_attr['stretch']: # # Stretch the glyph vertically # scale_ratio = font_dim['height'] / sym_dim['height'] # sourceFont.transform(psMat.scale(1, scale_ratio)) # Use the dimensions from the pasted and stretched glyph sym_dim = get_dim(sourceFont[currentSourceFontGlyph]) # Center-align the glyph vertically font_ycenter = font_dim['height'] / 2 sym_ycenter = sym_dim['height'] / 2 # First move it to the ymax (top) sourceFont.transform( psMat.translate(0, font_dim['ymax'] - sym_dim['ymax'])) # Then move it the y center difference sourceFont.transform(psMat.translate(0, sym_ycenter - font_ycenter)) # Ensure that the glyph doesn't extend outside the font's bounding box if sym_dim['width'] > font_dim['width']: # The glyph is too wide, scale it down to fit scale_matrix = psMat.scale( font_dim['width'] / sym_dim['width'], 1) sourceFont.transform(scale_matrix) # Use the dimensions from the stretched glyph sym_dim = get_dim(sourceFont[currentSourceFontGlyph]) # Handle glyph alignment #if sym_attr['align'] == 'c': # # Center align # align_matrix = psMat.translate(font_dim['width'] / 2 - sym_dim['width'] / 2 , 0) align_matrix = psMat.translate( font_dim['width'] / 2 - sym_dim['width'] / 2, 0) #elif sym_attr['align'] == 'r': # # Right align # align_matrix = psMat.translate(font_dim['width'] - sym_dim['width'], 0) #else: # No alignment (left alignment) #align_matrix = psMat.translate(0, 0) sourceFont.transform(align_matrix) #if sym_attr['overlap'] is True: # overlap_width = sourceFont.em / 48 # # Stretch the glyph slightly horizontally if it should overlap # sourceFont.transform(psMat.scale((sym_dim['width'] + overlap_width) / sym_dim['width'], 1)) # if sym_attr['align'] == 'l': # # The glyph should be left-aligned, so it must be moved overlap_width to the left # # This only applies to left-aligned glyphs because the glyph is scaled to the right # sourceFont.transform(psMat.translate(-overlap_width, 0)) # Ensure the font is considered monospaced on Windows sourceFont[currentSourceFontGlyph].width = font_dim['width'] if exactEncoding is False: sourceFontCounter += 1 # reset selection so iteration works propertly @todo fix? rookie misunderstanding? symbolFont.selection.select(("ranges", "unicode"), symbolFontStart, symbolFontEnd) # end for return