class FeatureTester(BaseWindowController): def __init__(self, font): if font is None: print "A open UFO is needed" return roboFabFont = font font = font.naked() self.font = font self.featureFont = None topHeight = 40 left = 160 self.w = Window((700, 400), "Feature Preview", minSize=(300, 300)) previewGroup = Group((0, 0, -0, -0)) self.glyphLineInputPosSize = (10, 10, -85, 22) self.glyphLineInputPosSizeWithSpinner = (10, 10, -106, 22) previewGroup.glyphNameInput = self.glyphLineInput = GlyphSequenceEditText(self.glyphLineInputPosSize, font, callback=self.glyphLineViewInputCallback) previewGroup.progressSpinner = self.glyphLineProgressSpinner = ProgressSpinner((-98, 13, 16, 16), sizeStyle="small") previewGroup.updateButton = self.glyphLineUpdateButton = Button((-75, 11, -10, 20), "Update", callback=self.updateFeatureFontCallback) self.w.pg = previewGroup # tab container self.w.previewTabs = Tabs((left, topHeight, -0, -0), ["Preview", "Records"], showTabs=False) # line view self.w.previewTabs[0].lineView = self.glyphLineView = GlyphLineView((0, 0, -0, -0), showPointSizePlacard=True) # records columnDescriptions = [ dict(title="Name", width=100), dict(title="XP", width=50), dict(title="YP", width=50), dict(title="XA", width=50), dict(title="YA", width=50), dict(title="Alternates", width=100) ] self.w.previewTabs[1].recordsList = self.glyphRecordsList = List((0, 0, -0, -0), [], columnDescriptions=columnDescriptions, showColumnTitles=True, drawVerticalLines=True, drawFocusRing=False) # controls self.w.controlsView = self.glyphLineControls = OpenTypeControlsView((0, topHeight, left+1, 0), self.glyphLineViewControlsCallback) self.font.addObserver(self, "_fontChanged", "Font.Changed") self.w.setDefaultButton(self.glyphLineUpdateButton) self.w.bind("close", self.windowClose) self.setUpBaseWindowBehavior() document = roboFabFont.document() if document is not None: document.addWindowController_(self.w.getNSWindowController()) self.w.open() self.updateFeatureFontCallback(None) def windowClose(self, sender): self.destroyFeatureFont() self.font.removeObserver(self, "Font.Changed") def destroyFeatureFont(self): if self.featureFont is not None: path = self.featureFont.path self.featureFont = None os.remove(path) def _fontChanged(self, notification): self.w.setDefaultButton(self.glyphLineUpdateButton) # self.glyphLineUpdateButton.enable(True) def glyphLineViewInputCallback(self, sender): self.updateGlyphLineView() def updateFeatureFontCallback(self, sender): self._compileFeatureFont() self.updateGlyphLineViewViewControls() self.updateGlyphLineView() def glyphLineViewControlsCallback(self, sender): self.updateGlyphLineView() def _compileFeatureFont(self, showReport=True): # reposition the text field self.glyphLineInput.setPosSize(self.glyphLineInputPosSizeWithSpinner) self.glyphLineInput.getNSTextField().superview().display() # start the progress self.glyphLineProgressSpinner.start() # compile path = tempfile.mkstemp()[1] compiler = EmptyOTFCompiler() options = FontCompilerOptions() options.fdk = CurrentFDK() options.outputPath = path # clean up if self.font.info.openTypeOS2WinDescent is not None and self.font.info.openTypeOS2WinDescent < 0: self.font.info.openTypeOS2WinDescent = abs(self.font.info.openTypeOS2WinDescent) self.font.info.postscriptNominalWidthX = None reports = compiler.compile(self.font, options) # load the compiled font if os.path.exists(path) and reports["makeotf"] is not None and "makeotfexe [FATAL]" not in reports["makeotf"]: self.featureFont = FeatureFont(path) else: self.featureFont = None if showReport: report = [] if reports["makeotf"] is not None: for line in reports["makeotf"].splitlines(): if line.startswith("makeotfexe [NOTE] Wrote new font file "): continue report.append(line) self.showMessage("Error while compiling features", "\n".join(report)) # stop the progress self.glyphLineProgressSpinner.stop() # color the update button window = self.w.getNSWindow() window.setDefaultButtonCell_(None) # self.glyphLineUpdateButton.enable(False) # reposition the text field self.glyphLineInput.setPosSize(self.glyphLineInputPosSize) def updateGlyphLineView(self): # get the settings settings = self.glyphLineControls.get() # set the display mode mode = settings["mode"] if mode == "preview": self.w.previewTabs.set(0) else: self.w.previewTabs.set(1) # set the direction self.glyphLineView.setRightToLeft(settings["rightToLeft"]) # get the typed glyphs glyphs = self.glyphLineInput.get() # set into the view case = settings["case"] if self.featureFont is None: # convert case if case != "unchanged": # the case converter expects a slightly # more strict set of mappings than the # ones provided in font.unicodeData. # so, make them. cmap = {} reversedCMAP = {} for uniValue, glyphName in self.font.unicodeData.items(): cmap[uniValue] = glyphName[0] reversedCMAP[glyphName[0]] = [uniValue] # transform to glyph names glyphNames = [glyph.name for glyph in glyphs] # convert glyphNames = convertCase(case, glyphNames, cmap, reversedCMAP, None, ".notdef") # back to glyphs glyphs = [self.font[glyphName] for glyphName in glyphNames if glyphName in self.font] # set the glyphs self.glyphLineView.set(glyphs) records = [dict(Name=glyph.name, XP=0, YP=0, XA=0, YA=0, Alternates="") for glyph in glyphs] self.glyphRecordsList.set(records) else: # get the settings script = settings["script"] language = settings["language"] rightToLeft = settings["rightToLeft"] case = settings["case"] for tag, state in settings["gsub"].items(): self.featureFont.gsub.setFeatureState(tag, state) for tag, state in settings["gpos"].items(): self.featureFont.gpos.setFeatureState(tag, state) # convert to glyph names glyphNames = [glyph.name for glyph in glyphs] # process glyphRecords = self.featureFont.process(glyphNames, script=script, langSys=language, rightToLeft=rightToLeft, case=case) # set the UFO's glyphs into the records finalRecords = [] for glyphRecord in glyphRecords: if glyphRecord.glyphName not in self.font: continue glyphRecord.glyph = self.font[glyphRecord.glyphName] finalRecords.append(glyphRecord) # set the records self.glyphLineView.set(finalRecords) records = [dict(Name=record.glyph.name, XP=record.xPlacement, YP=record.yPlacement, XA=record.xAdvance, YA=record.yAdvance, Alternates=", ".join(record.alternates)) for record in finalRecords] self.glyphRecordsList.set(records) def updateGlyphLineViewViewControls(self): if self.featureFont is not None: existingStates = self.glyphLineControls.get() # GSUB if self.featureFont.gsub is not None: for tag in self.featureFont.gsub.getFeatureList(): state = existingStates["gsub"].get(tag, False) self.featureFont.gsub.setFeatureState(tag, state) # GPOS if self.featureFont.gpos is not None: for tag in self.featureFont.gpos.getFeatureList(): state = existingStates["gpos"].get(tag, False) self.featureFont.gpos.setFeatureState(tag, state) self.glyphLineControls.setFont(self.featureFont)
# a path to a font fontPath = aPathToYourFont # a path to save the image to imagePath = "demo.tiff" # setup the layout engine font = Font(fontPath) # turn the aalt feature on so that we get any alternates font.setFeatureState("aalt", True) # process some text glyphRecords = font.process(u"HERE IS SOME TEXT!") # calculate the image size pointSize = 50.0 offset = 20 scale = pointSize / font.info.unitsPerEm imageWidth = sum([ font[record.glyphName].width + record.xAdvance for record in glyphRecords ]) * scale imageWidth = int(round(imageWidth)) imageWidth += offset * 2 imageHeight = pointSize + (offset * 2) # setup the image image = NSImage.alloc().initWithSize_((imageWidth, imageHeight)) image.lockFocus()
# a path to a font fontPath = sys.argv[1] # a path to save the image to imagePath = "demo.tiff" # setup the layout engine font = Font(fontPath) # turn the aalt feature on so that we get any alternates font.setFeatureState("aalt", True) font.setFeatureState("liga", True) font.setFeatureState("dlig", True) # process some text glyphRecords = font.process(u"Office flourishes!") # calculate the image size pointSize = 50.0 offset = 20 scale = pointSize / font.info.unitsPerEm imageWidth = sum([font[record.glyphName].width + record.xAdvance for record in glyphRecords]) * scale imageWidth = int(round(imageWidth)) imageWidth += offset * 2 imageHeight = pointSize + (offset * 2) # setup the image image = NSImage.alloc().initWithSize_((imageWidth, imageHeight)) image.lockFocus() # fill it with white NSColor.whiteColor().set()
return glyph.nsBezierPath # a path to a font fontPath = aPathToYourFont # a path to save the image to imagePath = "demo.tiff" # setup the layout engine font = Font(fontPath) # turn the aalt feature on so that we get any alternates font.setFeatureState("aalt", True) # process some text glyphRecords = font.process(u"HERE IS SOME TEXT!") # calculate the image size pointSize = 50.0 offset = 20 scale = pointSize / font.info.unitsPerEm imageWidth = sum([font[record.glyphName].width + record.xAdvance for record in glyphRecords]) * scale imageWidth = int(round(imageWidth)) imageWidth += offset * 2 imageHeight = pointSize + (offset * 2) # setup the image image = NSImage.alloc().initWithSize_((imageWidth, imageHeight)) image.lockFocus() # fill it with white NSColor.whiteColor().set()
class FeatureTester(BaseWindowController): def __init__(self, font): if font is None: print "A open UFO is needed" return roboFabFont = font font = font.naked() self.font = font self.featureFont = None topHeight = 40 left = 160 self.w = Window((700, 400), "Feature Preview", minSize=(300, 300)) previewGroup = Group((0, 0, -0, -0)) self.glyphLineInputPosSize = (10, 10, -85, 22) self.glyphLineInputPosSizeWithSpinner = (10, 10, -106, 22) previewGroup.glyphNameInput = self.glyphLineInput = GlyphSequenceEditText(self.glyphLineInputPosSize, font, callback=self.glyphLineViewInputCallback) previewGroup.progressSpinner = self.glyphLineProgressSpinner = ProgressSpinner((-98, 13, 16, 16), sizeStyle="small") previewGroup.updateButton = self.glyphLineUpdateButton = Button((-75, 11, -10, 20), "Update", callback=self.updateFeatureFontCallback) self.w.pg = previewGroup # tab container self.w.previewTabs = Tabs((left, topHeight, -0, -0), ["Preview", "Records"], showTabs=False) # line view self.w.previewTabs[0].lineView = self.glyphLineView = GlyphLineView((0, 0, -0, -0), showPointSizePlacard=True) # records columnDescriptions = [ dict(title="Name", width=100), dict(title="XP", width=50), dict(title="YP", width=50), dict(title="XA", width=50), dict(title="YA", width=50), dict(title="Alternates", width=100) ] self.w.previewTabs[1].recordsList = self.glyphRecordsList = List((0, 0, -0, -0), [], columnDescriptions=columnDescriptions, showColumnTitles=True, drawVerticalLines=True, drawFocusRing=False) # controls self.w.controlsView = self.glyphLineControls = OpenTypeControlsView((0, topHeight, left+1, 0), self.glyphLineViewControlsCallback) self.font.addObserver(self, "_fontChanged", "Font.Changed") self.w.setDefaultButton(self.glyphLineUpdateButton) self.w.bind("close", self.windowClose) self.setUpBaseWindowBehavior() document = roboFabFont.document() if document is not None: document.addWindowController_(self.w.getNSWindowController()) self.w.open() self.updateFeatureFontCallback(None) def windowClose(self, sender): self.destroyFeatureFont() self.font.removeObserver(self, "Font.Changed") def destroyFeatureFont(self): if self.featureFont is not None: path = self.featureFont.path self.featureFont = None os.remove(path) def _fontChanged(self, notification): self.w.setDefaultButton(self.glyphLineUpdateButton) # self.glyphLineUpdateButton.enable(True) def glyphLineViewInputCallback(self, sender): self.updateGlyphLineView() def updateFeatureFontCallback(self, sender): self._compileFeatureFont() self.updateGlyphLineViewViewControls() self.updateGlyphLineView() def glyphLineViewControlsCallback(self, sender): self.updateGlyphLineView() def _compileFeatureFont(self, showReport=True): # reposition the text field self.glyphLineInput.setPosSize(self.glyphLineInputPosSizeWithSpinner) self.glyphLineInput.getNSTextField().superview().display() # start the progress self.glyphLineProgressSpinner.start() # compile path = tempfile.mkstemp()[1] compiler = EmptyOTFCompiler() # clean up if self.font.info.openTypeOS2WinDescent is not None and self.font.info.openTypeOS2WinDescent < 0: self.font.info.openTypeOS2WinDescent = abs(self.font.info.openTypeOS2WinDescent) self.font.info.postscriptNominalWidthX = None reports = compiler.compile(self.font, path) # load the compiled font if os.path.exists(path) and reports["makeotf"] is not None and "makeotfexe [FATAL]" not in reports["makeotf"]: self.featureFont = FeatureFont(path) else: self.featureFont = None if showReport: report = [] if reports["makeotf"] is not None: for line in reports["makeotf"].splitlines(): if line.startswith("makeotfexe [NOTE] Wrote new font file "): continue report.append(line) self.showMessage("Error while compiling features", "\n".join(report)) # stop the progress self.glyphLineProgressSpinner.stop() # color the update button window = self.w.getNSWindow() window.setDefaultButtonCell_(None) # self.glyphLineUpdateButton.enable(False) # reposition the text field self.glyphLineInput.setPosSize(self.glyphLineInputPosSize) def updateGlyphLineView(self): # get the settings settings = self.glyphLineControls.get() # set the display mode mode = settings["mode"] if mode == "preview": self.w.previewTabs.set(0) else: self.w.previewTabs.set(1) # set the direction self.glyphLineView.setRightToLeft(settings["rightToLeft"]) # get the typed glyphs glyphs = self.glyphLineInput.get() # set into the view case = settings["case"] if self.featureFont is None: # convert case if case != "unchanged": # the case converter expects a slightly # more strict set of mappings than the # ones provided in font.unicodeData. # so, make them. cmap = {} reversedCMAP = {} for uniValue, glyphName in self.font.unicodeData.items(): cmap[uniValue] = glyphName[0] reversedCMAP[glyphName[0]] = [uniValue] # transform to glyph names glyphNames = [glyph.name for glyph in glyphs] # convert glyphNames = convertCase(case, glyphNames, cmap, reversedCMAP, None, ".notdef") # back to glyphs glyphs = [self.font[glyphName] for glyphName in glyphNames if glyphName in self.font] # set the glyphs self.glyphLineView.set(glyphs) records = [dict(Name=glyph.name, XP=0, YP=0, XA=0, YA=0, Alternates="") for glyph in glyphs] self.glyphRecordsList.set(records) else: # get the settings script = settings["script"] language = settings["language"] rightToLeft = settings["rightToLeft"] case = settings["case"] for tag, state in settings["gsub"].items(): self.featureFont.gsub.setFeatureState(tag, state) for tag, state in settings["gpos"].items(): self.featureFont.gpos.setFeatureState(tag, state) # convert to glyph names glyphNames = [glyph.name for glyph in glyphs] # process glyphRecords = self.featureFont.process(glyphNames, script=script, langSys=language, rightToLeft=rightToLeft, case=case) # set the UFO's glyphs into the records finalRecords = [] for glyphRecord in glyphRecords: if glyphRecord.glyphName not in self.font: continue glyphRecord.glyph = self.font[glyphRecord.glyphName] finalRecords.append(glyphRecord) # set the records self.glyphLineView.set(finalRecords) records = [dict(Name=record.glyph.name, XP=record.xPlacement, YP=record.yPlacement, XA=record.xAdvance, YA=record.yAdvance, Alternates=", ".join(record.alternates)) for record in finalRecords] self.glyphRecordsList.set(records) def updateGlyphLineViewViewControls(self): if self.featureFont is not None: existingStates = self.glyphLineControls.get() # GSUB if self.featureFont.gsub is not None: for tag in self.featureFont.gsub.getFeatureList(): state = existingStates["gsub"].get(tag, False) self.featureFont.gsub.setFeatureState(tag, state) # GPOS if self.featureFont.gpos is not None: for tag in self.featureFont.gpos.getFeatureList(): state = existingStates["gpos"].get(tag, False) self.featureFont.gpos.setFeatureState(tag, state) self.glyphLineControls.setFont(self.featureFont)
class FontProofer(object): def getPickedFont(): print "Picked Font" print self.font def __init__(self, font=None, format_index=0, font_index=0, caps_lock=False, linespace=0.7, marginsupdown = 20, marginsside = 20, nib_simulate=False, nib_width_index=0, nib_angle=30, Color_Nib=False, send_to_plotter=False, features = [], # Use features that are active by default (kern, liga, calt) ): self.linespace = linespace self.format_index = format_index self.font_index = font_index self.set_font(font) self.mark_fill = False self.mark_composites = False # A6 Format self.width = 842/2 self.height = 595/2 if format_index == 1: self.width = 595/2 self.height = 842/2 self.margins = { "top": marginsupdown, "bottom": marginsupdown, "left": marginsside, "right": marginsside, } self.breite = self.width - marginsside * 2 self.scale = 1 self.nib_simulate = nib_simulate self.features = features if nib_width_index in pp_sizes: self.nib_width_pt = pp_sizes[nib_width_index] else: self.nib_width_pt = 14.173 self.nib_width = self.nib_width_pt / self.scale self.nib_angle = radians(nib_angle) self.pen_width = 0.7 / 25.4 * 72 # mm in pt if font_index in fonts_list: print "Selected font: ", font_index + 1 self.set_font(fonts_list[font_index]) print "\nNib Simulation Parameters","\nNib Width: ", (round((self.nib_width / 4),1))+0.1,"mm", "\nNib Angle: ",(round((nib_angle),1)),"°","\n" self.x_pad = 10 self.y_pad = 24 print "Leading: ", round(linespace*(1/0.7), 2),"\nVertical Shift:", round((marginsupdown-20)*-1,1),"\nScale-Factor: ", round(((marginsside-260)*-1)/2.4, 1), "%","\n" self.Color_Nib = Color_Nib self.send_to_plotter = send_to_plotter def set_font(self, font=None): # open UFO with defcon if font is not None: font_path = expanduser(font[0]) self.font = Font(font_path) # open OTF with compositor if font[1] is None: shaping_font_path = None self.shaping_font = None else: shaping_font_path = expanduser(font[1]) self.shaping_font = FeatureFont(shaping_font_path) else: self.font = None self.shaping_font = None if self.font is not None: self.upm = self.font.info.unitsPerEm self.desc = self.font.info.descender self.x_height = self.font.info.xHeight self.cap_height = self.font.info.capHeight self.asc = self.font.info.ascender self.angle = self.font.info.italicAngle else: self.upm = 1000 self.desc = -250 self.x_height = 500 self.cap_height = 700 self.asc = 750 self.angle = 0 def new_page(self): newPage(self.width, self.height) translate(self.margins["left"], self.height - self.margins["top"] - self.asc * self.scale) def _drawGlyph(self, glyph): save() #scale(self.scale) fill(None) lineJoin("round") miterLimit(1) strokeWidth(self.pen_width/self.scale) stroke(0, 0, 0, 1) drawGlyph(glyph) if self.nib_simulate: if self.Color_Nib: stroke(1, 0, 0, 1) save() translate((self.nib_width * -0.5 * cos(self.nib_angle))*(1/self.scale), self.nib_width * -0.5 * sin(self.nib_angle)*(1/self.scale)) drawGlyph(glyph) restore() save() translate((self.nib_width * 0.5 * cos(self.nib_angle))*(1/self.scale), self.nib_width * 0.5 * sin(self.nib_angle)*(1/self.scale)) drawGlyph(glyph) restore() restore() def setText(self, my_text): my_list = getGlyphNamesFromString(my_text+"\n") if self.font is None: print "Pick a font" return None if self.shaping_font is None: glyphRecords = [SimpleGlyphRecord(n) for n in my_list] else: for tag, state in self.features: try: self.shaping_font.gpos.setFeatureState(tag, state) except: pass try: self.shaping_font.gsub.setFeatureState(tag, state) except: pass glyphRecords = self.shaping_font.process( my_list, script="DFLT", langSys="DEU", rightToLeft=False, case="unchanged", ) # Measure lines, draw line when a \n character occurs. line_width = 0 line_width_prev = 0 lineGlyphRecords = [] first_line = True for gr in glyphRecords: if gr.glyphName != r"\n": glyphname = gr.glyphName try: glyph = self.font[glyphname] except KeyError: print "Ignored missing glyph:", glyphname glyph = None if glyph is not None: line_width += glyph.width + gr.xAdvance lineGlyphRecords.append(gr) else: #print [gr.glyphName for gr in lineGlyphRecords] #print "Line width:", line_width self.scale = self.breite / line_width #print "Scale:", self.scale if first_line: self.new_page() first_line = False else: #print "Translate" translate(0, -self.upm * self.scale * self.linespace) #rect(0, 0, 10, 10) self.opticalSize() save() scale(self.scale) #fill(None) #stroke(1,0,0) #rect(0, 10, line_width, self.asc-10) #rect(0, self.desc, line_width, -self.desc-10) for gr in lineGlyphRecords: glyphname = gr.glyphName glyph = self.font[glyphname] save() translate(gr.xPlacement, gr.yPlacement) self._drawGlyph(glyph) restore() translate(glyph.width + gr.xAdvance, gr.yAdvance) line_width = 0 lineGlyphRecords = [] restore() if self.send_to_plotter: saveImage(expanduser("~/Documents/Penplotter_Cards/Postcard_%s_%d.pdf") % (Line_1, now())) print "PDF saved to /Documents/Penplotter_Cards/Postcard_%s_%d.pdf" % (Line_1, now()) print "Plotting..." plot("~/Desktop/temp.hpgl") def opticalSize(self): fontsize_list = { 0: ("~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_Centerline.ufo", "~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_black_01_.otf"), 4: ("~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_fourlines_01.ufo", "~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_black_01_.otf"), 2: ("~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_oneline_01.ufo", "~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_black_01_.otf"), 1: ("~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_Outline_01.ufo", "~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_black_01_.otf"), 3: ("~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_twoline_01.ufo", "~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/Bronco_black_01_.otf"), #5: ("~/Documents/Schriften/_MeineSchriften/PenPlotterFaces/DrawbotSketches/fonts/petrosian_04.ufo", None), } if self.font_index == 2: self.set_font(fontsize_list[ min( 4, int(self.scale/0.069) ) ] )