def getVarFontInstance(fontOrPath, location, styleName=None, normalize=True, cached=True, lazy=True): """The getVarFontInstance refers to the file of the source variable font. The nLocation is dictionary axis locations of the instance with values between (0, 1000), e.g. dict(wght=0, wdth=1000) or values between (0, 1), e.g. dict(wght=0.2, wdth=0.6). Set normalize to False if the values in location already are matching the axis min/max of the font. If there is a [opsz] Optical Size value defined, then store that information in the font.info.opticalSize. The optional *styleName* overwrites the *font.info.styleName* of the *ttFont* or the automatic location name.""" if isinstance(fontOrPath, str): varFont = getFont(fontOrPath, lazy=lazy) else: varFont = fontOrPath if varFont is None: # Could not read the Variable Font on that path. return None path = generateInstance(varFont.path, location, targetDirectory=getInstancePath(), normalize=normalize, cached=cached, lazy=lazy) # Answer the generated Variable Font instance. Add [opsz] value if is defined in the location, otherwise None. instance = getFont(path, lazy=lazy) instance.info.opticalSize = location.get('opsz') instance.info.location = location instance.info.varStyleName = styleName return instance
def getFamilyPaths(useFontInfo=True, useFileName=True, force=False): """Construct a dictionary of familyName-->[fontPath, fontPath, ...]. If omitted, then create the families from all avaible font paths found by the context. The flag useFontInfo defines if the familyName, styleName) should be taken from the font.info or just guessed from the font file name. >>> familyPaths = getFamilyPaths() >>> len(familyPaths['Roboto']) 38 >>> len(familyPaths['Bungee']) 5 """ global FAMILY_PATHS if force: FAMILY_PATHS = {} if not FAMILY_PATHS: # If forced or not initialized yet for fontPath in getFontPaths().values(): familyName = None if useFontInfo: font = getFont(fontPath) if font is not None: familyName = font.info.familyName if not familyName and useFileName: familyName = path2FamilyName(fontPath) if familyName is not None: if familyName not in FAMILY_PATHS: FAMILY_PATHS[familyName] = [] FAMILY_PATHS[familyName].append(fontPath) return FAMILY_PATHS
def addFont(self, fontOrPath): """Add the fonts to the family. This can be a list of Font instances, a list of font names or a list of font paths.""" """ FIXME: restore doctests. >>> from pagebot.fonttoolbox.objects.font import findFont >>> font = findFont('Roboto-Regular') >>> path = font.path >>> families = getFamilies() >>> family = newFamily('MyOtherFamily') >>> font = family.addFont(path) >>> font.path == path True >>> len(family) 1 """ font = None if isinstance(fontOrPath, self.FONT_CLASS): self.fonts[fontOrPath.path] = font = fontOrPath elif os.path.isdir(fontOrPath): for fileName in os.listdir(fontOrPath): if not fontOrPath.endswith('/'): fontOrPath += '/' filePath = fontOrPath + fileName if not filePath in self.fonts: # Only create if not already there. font = getFont(filePath) if font is not None: self.fonts[ filePath] = font # Not recursive, this just folder. else: # Font exists, just return it font = self.fonts[filePath] else: font = getFont(fontOrPath) if font is not None: self.fonts[fontOrPath] = font return font
def getFamilies(familyPaths=None, useFontInfo=True, useFileName=True, force=False): u"""Construct a dictionary of Family instances from dictionary familyPaths. If omitted, then create the families from all aviable font paths found in the by the context. The flag useFontInfo defines if the familyName, styleName) should be taken from the font.info or guess from the font file name. >>> families = getFamilies() >>> 'Roboto' in families True >>> 'Bungee' in families True >>> families = getFamilies(useFontInfo=False, force=True) # Forced to look an fileName only, Roboto is a family >>> 'Roboto' in families True >>> families = getFamilies(useFileName=False, force=True) # Looking into font.info, Roboto is the family name. >>> 'Roboto' in families True >>> #families = getFamilies(useFontInfo=False, useFileName=False) finds nothing """ global FAMILIES if force: FAMILIES = {} if not FAMILIES: # If forced or not initialized yet for fontPath in getFontPaths().values(): font = getFont(fontPath) if font is not None: #print(font.path.split('/')[-1], repr(font.info.familyName), repr(font.info.styleName)) familyName = None if useFontInfo: familyName = font.info.familyName if not familyName and useFileName: familyName = path2FamilyName(font.path) if familyName: if familyName not in FAMILIES: FAMILIES[familyName] = Family(familyName) FAMILIES[familyName].addFont(font) return FAMILIES
def fitString(cls, t, context, e=None, style=None, w=None, h=None, useXTRA=True, pixelFit=True): u"""Answer the DrawBotString instance from valid attributes in style. Set all values after testing their existence, so they can inherit from previous style formats in the string. If the target width w and height are defined, and if there is a [wdth] or [XTRA] axis in the current Variable Font, then values are iterated to make the best location/instance for the rectangle fit. In case the fontSize is set and the width w is set, then just use the [wdth] or [XTRA] to make a horizontal fit, keeping the size. If the axes run to extreme, the string is return without changing width. In case the a font path was supplied, then try to get a Font instance for that path, as we need to test it for existing axes as Variable Font. >>> from pagebot.contexts.drawbotcontext import DrawBotContext >>> context = DrawBotContext() >>> from pagebot.fonttoolbox.objects.font import findFont >>> #font = findFont('RobotoDelta-VF') >>> font = findFont('Fit-Variable_1') >>> style = dict(font=font) >>> 'wdth' in font.axes.keys() or 'XTRA' in font.axes.keys() # One of them is there True >>> s = DrawBotString.newString('Hello', context, style=style, w=300) >>> int(round(s.bounds()[2])), int(round(s.fittingFontSize)) # Rounded width (297, 195) >>> s = DrawBotString.fitString('Hello', context, style=style, w=400, h=220) >>> int(round(s.bounds()[2]-s.bounds()[0])) # Rounded pixel width 399 >>> int(round(s.bounds()[3]-s.bounds()[1])) # Rounded pixel height 220 >>> #s.bounds() """ style = copy(style) location = copy(css( '', e, style, {})) # In case the used already supplied a VF location, use it. font = css('font', e, style) if isinstance(font, str): # Assuming it's a path, get the Font instance. font = getFont(font) # Make sure we gave a real Font instance. style['font'] = font # Get the flag if fit locations should be rounded (less cached instance) or accurate. roundVariableFitLocation = style.get('roundVariableFitLocation', True) # In case font is not a variable font, or not [wdth] or [XTRA] present, then using normal # string fit is the best we can do. if not 'wdth' in font.axes and not 'XTRA' in font.axes: return cls.newString(t, context, e=e, style=style, w=w, h=h, pixelFit=pixelFit) # Decide which axis to use for width adjustments and get the axis values. if not useXTRA or not 'XTRA' in font.axes: # Try to force usage of [XTRA] if it exists, otherwise use[wdth] axisTag = 'wdth' else: axisTag = 'XTRA' minValue, defaultValue, maxValue = font.axes[axisTag] #print(0, minValue, defaultValue, maxValue ) if h is not None: # Fitting in heigt, calculate/iterate for the fitting font size. bs = cls.newString(t, context, e=e, style=style, h=h, pixelFit=pixelFit) style['fontSize'] = bs.fittingFontSize else: # Assuming there is a fontSize set, we'll use that as vertical requirement bs = cls.newString(t, context, e=e, style=style, pixelFit=pixelFit) # Now we have a formatted string with a given fontSize, guess to fit on the width. tx, _, tw, _ = bs.bounds() # Get pixel bounds of the string tw = tw - tx # Pixel width of the current string. #print(0, tw, w, h) prevW = None # Testing if something changed, for extreme of axes. axisValue = defaultValue for n in range( 100): # Limit the maximum amount of iterations as safeguard if tw > w: # Too wide, try iterate smaller in ratio of wdth/XTRA axis values #print(1, tw, w) maxValue = axisValue # Clip wide range to current # Guess the new axisvalue from the ratio of tw/w axisValue = (axisValue - minValue) / 2 + minValue if roundVariableFitLocation: axisValue = int(round(axisValue)) #print(2, axisValue, minValue, defaultValue, maxValue) loc = copy(location) loc[axisTag] = axisValue loc['opsz'] = int(style['fontSize']) #print(3, loc, font.axes.keys()) style['font'] = getInstance(font, loc) bs = cls.newString(t, context, e=e, style=style, pixelFit=pixelFit) tx, ty, tw, th = bs.bounds() # Get pixel bounds of the string tw = tw - tx # Total width for the current #print(5, tw, w, th-ty, h) if prevW == tw: # Did not change, probably not able to get more condensed break prevW = tw elif tw < w - cls.FITTING_TOLERANCE: # Too condensed, try to make wider. #print(11, tw, w) minValue = axisValue # Clip narrow range to current axisValue = (maxValue - axisValue) / 2 + axisValue if roundVariableFitLocation: axisValue = int(round(axisValue)) loc = copy(location) loc[axisTag] = axisValue loc['opsz'] = int(style['fontSize']) style['font'] = getInstance(font, loc) bs = cls.newString(t, context, e=e, style=style, pixelFit=pixelFit) tx, ty, tw, th = bs.bounds() # Get pixel bounds of the string tw = tw - tx # Total width for the current #print(15, tw, w, th-ty, h) if prevW == tw: # Did not change, probably not able to get more condensed break prevW = tw else: # We found a fitting VF-location within tolerance. Back out. break #print('Number of iterations', n) return bs
if context.onBlack((x, y), path) and (not context.onBlack( (x + grid, y), path) or not context.onBlack( (x + grid, y - grid), path)): context.fill(0) context.oval(pt(x - r / 2), pt(y - r / 2), r, r) context.stroke((1, 0, 0)) context.fill(noColor) context.drawPath(path) context.stroke((0, 1, 0), 5) context.rect(e.x, e.y, e.w, e.h) fontPath = getTestFontsPath() + '/djr/bungee/Bungee-Regular.ttf' font = getFont(fontPath) glyphName = 'e' #'cid05405.1' doc = Document(w=W, h=H, originTop=False, autoPages=1) view = doc.getView() view.padding = 40 # Aboid showing of crop marks, etc. view.showCropMarks = True view.showRegistrationMarks = True view.showFrame = True view.showOrigin = True view.showDimensions = False view.showFrame = True # Get list of pages with equal y, then equal x. #page = doc[1][0] # Get the single page from te document.
print(fontNames) fontNames = context.installedFonts('Verdana-BoldItalic') # ['Verdana-BoldItalic'] print(fontNames) fontNames = context.installedFonts(['Verdana', 'Georgia']) # ['Georgia', 'Georgia-Bold', 'Georgia-BoldItalic', 'Georgia-Italic', 'Verdana', 'Verdana-Bold', 'Verdana-BoldItalic', 'Verdana-Italic'] print(fontNames) # The PageBot method to look for fonts is find a fanily with an exact name match family = getFamily('Bungee') # <PageBot Family Bungee (5 fonts)> print(family) font = getFont('Verdana-BoldItalic') font = findFont('Roboto') # None print(font) robotoRegular = findFont('Roboto-Regular') # <Font Roboto-Regular> print(robotoRegular) font = findFont('Roboto-cannot-be-found', default='Roboto-Regular') # <Font Roboto-Regular> print(font) font = findFont('Roboto-cannot-be-found', default=robotoRegular) # <Font Roboto-Regular>