def metadata(self, filename, force=0): """Retrieve metadata from font file filename -- fully specified path to the font file force -- if false, and the metadata is already available for this file, do not access the font file to retrieve, just return the existing metadata. return value: tuple of: filename -- fully specified absolute path modifiers -- (weightInteger, italicsFlag) specificName -- specific name of the particular font stored in the given file, the name of the "modified" font fontName -- name of the general font which the modifiers are specialising specifier -- family specifier, two-tuple of high-level and sub-level font classifications based on font characteristics as encoded in the font file. """ filename = os.path.abspath(filename) if self.files.has_key(filename) and not force: return self.specificFonts.get(self.files[filename]) font = describe.openFont(filename) try: modifiers = describe.modifiers(font) except (KeyError, AttributeError), err: modifiers = (None, None)
def metadata( self, filename, force = 0 ): """Retrieve metadata from font file filename -- fully specified path to the font file force -- if false, and the metadata is already available for this file, do not access the font file to retrieve, just return the existing metadata. return value: tuple of: filename -- fully specified absolute path modifiers -- (weightInteger, italicsFlag) specificName -- specific name of the particular font stored in the given file, the name of the "modified" font fontName -- name of the general font which the modifiers are specialising specifier -- family specifier, two-tuple of high-level and sub-level font classifications based on font characteristics as encoded in the font file. """ filename = os.path.abspath( filename ) if self.files.has_key( filename ) and not force: return self.specificFonts.get( self.files[filename] ) font = describe.openFont(filename) try: modifiers = describe.modifiers( font ) except (KeyError,AttributeError), err: modifiers = (None,None)
def buildTable( filenames=None, failureCallback=None ): """Build table mapping {family:(font:{modifiers:(name,file)})} filenames -- if provided, list of filenames to scan, otherwise the full set of system fonts provided by findsystem will be used. failureCallback -- if provided, a function taking three arguments, the failing filename, an error-type code, and the error object. If processing should stop, raise an error. codes: 0 -- couldn't open the font file 1 -- couldn't find modifiers in the font file 2 -- couldn't find font-name in the font file 3 -- couldn't find the generic family specifier for the font """ if filenames is None: filenames = findsystem.findFonts() table = {} for filename in filenames: try: font = describe.openFont(filename) except Exception as err: if failureCallback: failureCallback( filename, 0, err ) else: try: modifiers = describe.modifiers( font ) except (KeyError,AttributeError) as err: if failureCallback: failureCallback( filename, 1, err ) modifiers = (None,None) try: specificName, fontName = describe.shortName( font ) except (KeyError,AttributeError) as err: if failureCallback: failureCallback( filename, 2, err ) else: try: specifier = describe.family(font) except KeyError: if failureCallback: failureCallback( filename, 3, err ) else: table.setdefault( specifier, {} ).setdefault( fontName, {} )[modifiers] = (specificName,filename) return table
def buildTable(filenames=None, failureCallback=None): """Build table mapping {family:(font:{modifiers:(name,file)})} filenames -- if provided, list of filenames to scan, otherwise the full set of system fonts provided by findsystem will be used. failureCallback -- if provided, a function taking three arguments, the failing filename, an error-type code, and the error object. If processing should stop, raise an error. codes: 0 -- couldn't open the font file 1 -- couldn't find modifiers in the font file 2 -- couldn't find font-name in the font file 3 -- couldn't find the generic family specifier for the font """ if filenames is None: filenames = findsystem.findFonts() table = {} for filename in filenames: try: font = describe.openFont(filename) except Exception as err: if failureCallback: failureCallback(filename, 0, err) else: try: modifiers = describe.modifiers(font) except (KeyError, AttributeError) as err: if failureCallback: failureCallback(filename, 1, err) modifiers = (None, None) try: specificName, fontName = describe.shortName(font) except (KeyError, AttributeError) as err: if failureCallback: failureCallback(filename, 2, err) else: try: specifier = describe.family(font) except KeyError: if failureCallback: failureCallback(filename, 3, err) else: table.setdefault(specifier, {}).setdefault( fontName, {})[modifiers] = (specificName, filename) return table
def find_font(): #load fonts registry.scan() fonts = registry.matchName(args.font) if len(fonts)>1: print "more than 1 font by that name, aborting" for font in fonts: print font exit(1) font_file =registry.fontFile(fonts[0]) if args.debug: print "font found at", font_file font = describe.openFont(font_file) return font
def load_font(fontname, allow_fallback, ttf_registry=ttffiles.Registry()): try: return describe.openFont("C:/Windows/Fonts/" + fontname) except IOError: pass try: if 0 == len(ttf_registry.fonts): ttf_registry.scan() return ttf_registry.fontFile(os.path.splitext(fontname)[0]) except KeyError: if allow_fallback: print "could not load font", fontname, " so try to fall back to arial" return load_font("arial.ttf", False) else: raise IOError("could not load font", fontname, " and no fallback is allowed->we are done")
def withFont( self, callable, *arguments, **named ): """Call a callable while we have a .font attribute This method opens the font file and then calls the given callable object. On exit, it eliminates the font. XXX Currently this is not reentrant :( """ if __debug__: log.info( """Opening TrueType font %r with fonttools""", self.filename) self.font = describe.openFont( self.filename ) try: return callable( *arguments, **named ) finally: try: del self.font except AttributeError: pass
def getImageSizeFor(str): fontSize = 10.0 unitsPerEm = 2048.0 ppiScale = 96.0 / 72.0 scalingFactor = fontSize * ppiScale / unitsPerEm from ttfquery import describe, glyphquery myfont = describe.openFont("C:\\Windows\\Fonts\\arial.ttf") width = 0 widestWidth = getWidestHMetric() * scalingFactor for char in str: try: print "Width of char %s: %f" % (char, (glyphquery.width(myfont, char) * scalingFactor)) width += glyphquery.width(myfont, char) * scalingFactor except: print "Exception thrown on", char width += widestWidth print "Total width:", width height = getHeight() * scalingFactor return (int(math.ceil(width)), int(math.ceil(height)))
def print_text(self, text: Tuple[str, str], x_offset, y_offset, scaling): curr_offset = 0 chars = text[0] font_file = text[1] for char in chars: font = describe.openFont(f"fonts/{font_file}.ttf") glyph_name = font["cmap"].getBestCmap().get(ord(char)) g = glyph.Glyph(glyph_name) contours = g.calculateContours(font) coords_list = [] largest_point = -10000 for c in range(len(contours)): countour = contours[c] curr_coords = [] for i in range(len(countour)): point = countour[i][0] x = point[0] + curr_offset y = point[1] curr_coords.append([x * 0.05, y * 0.05]) if point[0] > largest_point: largest_point = point[0] coords_list.append(curr_coords) curr_offset += (largest_point + 5) self.print_coords(coords_list, x_offset, y_offset, scaling)
import numpy as np from ttfquery import describe from ttfquery import glyphquery import matplotlib.pyplot as plt import matplotlib.cm as cm to_write = input("Enter a word: \n") # to_write = "NaTuRaL wRiTiNg" list_char = list(to_write) width = 0 angle_threshold = 130 / 180 * np.pi point_spacing = 100 font_url = "fonts/cnc_v.ttf" font = describe.openFont(font_url) # v = 500 timestp = 0.0 space_width = 400 text_size = 1 z_distance = 0.2 amax = 50 vmax = 10 def find_angle(point1, point2, point3): a = distance(point1, point2) b = distance(point2, point3) c = distance(point3, point1) angle = np.arccos((a**2 + b**2 - c**2) / (2 * a * b)) return angle
from ttfquery import describe, glyphquery, glyph # Set font here -----> f = describe.openFont("/Library/Fonts/Impact.ttf") # Set character here, in this case its C n = glyphquery.glyphName(f, 'C') g = glyph.Glyph(n) c = g.calculateContours(f) o = glyph.decomposeOutline(c[1]) # Print out the Polyline print o
import ttfquery.glyph as glyph import numpy as np from scipy.interpolate import splprep, splev import sys if len(sys.argv) == 1: print("usage: python font.py [character]") exit(1) # Open the character given as command line argument g = glyph.Glyph(sys.argv[1]) # Open our font file font = describe.openFont("LiberationSans.ttf") # Get the curves on the font, each contour is an "island" on the glyph # For example, i has the main stem and the dot on top, which are separate # contours. contours = g.calculateContours(font) for contour in contours: x = [] y = [] flags = [] # For each point in the contour add to lists for point, flag in contour: x.append(point[0]) y.append(point[1])
def text(pos=(0,0), text="", font=None, height=1.0, align='left', spacing=0.03, rotate=0.0, scale=1.0, xscale=1.0, yscale=1.0, thickness=None, vertical_spacing=None, info=False): # If info == True, the caller wants the additional info such as upper-left, etc. # In this case we return an instance of the class text_info, with the Polygon as # an attribute, text_info.Polygon. The main client of this extra info is the text object. if thickness is not None: raise AttributeError("Thickness is not allowed in a text shape.") if scale != 1.0: xscale = yscale = scale lines = text.split('\n') while lines[-1] == '\n': # strip off trailing newlines lines = lines[:-1] if font is None: font = "serif" font = describe.openFont(findFont(font)) try: fonth = glyphquery.charHeight(font) except: fonth = 1000 if fonth == 0: fonth = 1000 try: desc = glyphquery.charDescent(font) # charDescent may not be present fontheight = fonth+desc fontscale = 1./fontheight descent = fontscale*desc except: descent = -0.3*height # approximate value fontheight = 0.7*fonth fontscale = 1.3/fontheight if vertical_spacing is None: vertical_spacing = height*fontscale*glyphquery.lineHeight(font) excludef_list = [("ITCEdscr")] excludec_list = [("FRSCRIPT", "A"), ("jokerman", "O"), ("vivaldii", "O"), ("vivaldii", "Q"), ("vivaldii", "R")] ptext = [] widths = [] width = 0.0 starts = [] start = 0.0 for line in range(len(lines)): ptext.append(Polygon()) bb = 0 for newchar in lines[line]: if newchar == " ": try: if a: bx = a.boundingBox() bba = bx[1]-bx[0] bba = min(bba, 700) bb += bba except: pass continue n = glyphquery.glyphName(font, newchar) if n == ".notdef": print("The character '"+newchar+"' is not supported in the font "+font) continue g = glyph.Glyph(n) c = g.calculateContours(font) contours = [] for contour in c: contours.append(glyph.decomposeOutline(contour)) if len(contours[-1]) == 0: contours.pop() for contour in contours: pp = 0 for i in range(len(contour)-1): if (contour[i][0] == contour[i+1][0]) and (contour[i][1] == contour[i+1][1]): contour.pop(i) pp += 1 if i+pp >= len(contour)-1: break def lenctr(contour): totlen = 0 for j in range(len(contour)-1): totlen += (vis.vector(contour[j])-vis.vector(contour[j+1])).mag return totlen lc = len(contours) if lc >= 4: mli = 0 maxl = 0 lengths = [] for i in range(len(contours)): totlen = lenctr(contours[i]) lengths.append((totlen,i)) if totlen > maxl: mli = i maxl = totlen lengths.sort() lengths.reverse() ocontours = [] for ll in lengths: ocontours.append(contours[ll[1]]) contours = ocontours indxf = -1 indxc = -1 if (mli > 0 and newchar != "%"): try: indxf = excludef_list.index(self.font) except: pass if indxf == -1: try: indxc = excludec_list.index((self.font, newchar)) except: pass if (indxf == -1 and indxc == -1): maxc = contours.pop(mli) contours.insert(0, maxc) a = Polygon(contours[0]) for i in range(1,len(contours)): b = Polygon(contours[i]) if a.covers(b): a = a - b elif b.covers(a): a = b - a else: a = a + b a.shift(bb - a.boundingBox()[0] ,0) ptext[line] += a bx = ptext[line].boundingBox() bb = bx[1] - bx[0] + spacing*fontheight newwidth = fontscale*height*(ptext[line].boundingBox()[1]-ptext[line].boundingBox()[0]) widths.append(newwidth) if newwidth > width: width = newwidth ptext[line].scale(xscale*fontscale*height, yscale*fontscale*height, 0, 0) for line in range(len(lines)): if align == 'left': ptext[line].shift(-width/2,-line*vertical_spacing) elif align == 'right': ptext[line].shift(width/2-widths[line],-line*vertical_spacing) else: ptext[line].shift(-widths[line]/2,-line*vertical_spacing) ptext[line].shift(pos[0], pos[1]) if rotate != 0.0: ptext[line].rotate(rotate) if line == 0: shape = ptext[0] upper_left = vis.vector(ptext[line].boundingBox()[0],ptext[line].boundingBox()[3],0) upper_right = vis.vector(ptext[line].boundingBox()[1],ptext[line].boundingBox()[3],0) lower_left = vis.vector(ptext[line].boundingBox()[0],ptext[line].boundingBox()[2],0) lower_right = vis.vector(ptext[line].boundingBox()[1],ptext[line].boundingBox()[2],0) else: shape += ptext[line] xleft = ptext[line].boundingBox()[0] xright = ptext[line].boundingBox()[1] y = ptext[line].boundingBox()[2] if xleft < upper_left.x: upper_left.x = xleft if xright > upper_right.x: upper_right.x = xright lower_left = vis.vector(upper_left.x,ptext[line].boundingBox()[2],0) lower_right = vis.vector(upper_right.x,ptext[line].boundingBox()[2],0) dy = vis.vector(0,(upper_left.y-lower_left.y)/2-height,0) shape.shift(0,dy.y) if not info: return shape info = text_info() info.Polygon = shape info.upper_left = upper_left+dy info.upper_right = upper_right+dy info.lower_left = lower_left+dy info.lower_right = lower_right+dy if align == 'left': x = 0 elif align == 'right': x = -width elif align == 'center': x = -0.5*width info.start = vis.vector(x,0,0)+dy info.starts = [] for line in range(len(lines)): if align == 'left': x = 0 elif align == 'right': x = -widths[line] elif align == 'center': x = -0.5*widths[line] info.starts.append(vis.vector(x,line*vertical_spacing,0)+dy) info.width = width info.widths = widths info.descent = descent info.vertical_spacing = vertical_spacing info.align = align info.height = height return info