def _drawGlyphMarker(self, axisName, mx, my, glyphName, fontSize, location, strokeW=2): # Middle circle context.fill(1) context.stroke(0.7) context.strokeWidth(strokeW) context.oval(mx - fontSize / 2 * self.R, my - fontSize / 2 * self.R, fontSize * self.R, fontSize * self.R) variableFont = getVariableFont(self.font, location) # Show axis name below circle marker? if self.showAxisNames and axisName is not None: fs = context.newString(axisName, style=dict(font=variableFont.installedName, fontSize=fontSize / 4, textFill=0)) tw, th = context.textSize(fs) context.text( fs, (mx - tw / 2, my - fontSize / 2 * self.R - th * 2 / 3)) glyphPathScale = fontSize / self.font.info.unitsPerEm context.drawGlyphPath(variableFont, glyphName, mx, my - fontSize / 3, s=glyphPathScale, fillColor=0)
def drawPageFrame(w): c.fill(1) c.stroke(0) c.newPath() c.moveTo((PADDING, H - PADDING)) c.lineTo((PADDING + w, H - PADDING)) c.lineTo((PADDING + w, H / 2 + PADDING)) c.curveTo((PADDING + w / 2, H / 2 + PADDING), (PADDING + w / 2, H / 2 + PADDING - M / 2), (PADDING, H / 2 + PADDING - M / 2)) c.closePath() c.drawPath() c.fill(None) c.stroke(0.5) c.strokeWidth(4) leading = 14 for n in range(10): c.line( (PADDING + M, H - 5 * PADDING - n * leading), (PADDING + w - M - LINE_ENDINGS[n], H - 5 * PADDING - n * leading))
def _drawGlyphMarker(self, mx, my, glyphName, fontSize, location, strokeW=2): # Middle circle c.fill(1) c.stroke(0) c.strokeWidth(strokeW) c.oval(mx - fontSize * self.R, my - fontSize * self.R, fontSize * 2 * self.R, fontSize * 2 * self.R) glyphPathScale = fontSize / self.font.info.unitsPerEm drawGlyphPath(self.font.ttFont, glyphName, mx, my - fontSize / 4, location, s=glyphPathScale, fillColor=0)
def _drawGlyphMarker(self, mx, my, glyphName, markerSize, location, strokeW=2): # Middle circle c.fill(1) c.stroke(0) c.strokeWidth(strokeW) c.oval(mx - markerSize / 2, my - markerSize / 2, markerSize, markerSize) glyphPathScale = markerSize / self.font.info.unitsPerEm * 3 / 4 drawGlyphPath(self.font.ttFont, glyphName, mx, my - markerSize / 4, location, s=glyphPathScale, fillColor=0)
def draw(self, page, x, y): u"""Draw the circle info-graphic, showing most info about the variation font as can be interpreted from the file.""" c.fill(0.9) c.stroke(None) mx = x + self.w / 2 my = y + self.h / 2 # Gray circle that defines the area of c.oval(x, y, self.w, self.h) # Draw axis spikes first, so we can cover them by the circle markers. axes = self.font.axes fontSize = self.style.get('fontSize', self.DEFAULT_FONT_SIZE) # Draw name of the font c.fill(0) c.text( c.newString(self.font.info.familyName, style=dict(font=self.style['labelFont'], fontSize=self.style['axisNameFontSize'])), (x - fontSize / 2, y + self.h + fontSize / 2)) # Draw spokes c.fill(None) c.stroke(0) c.strokeWidth(1) c.newPath() for axisName, angle in self.angles.items(): markerX, markerY = self._angle2XY(angle, self.w / 2) c.moveTo((mx, my)) c.lineTo((mx + markerX, my + markerY)) c.drawPath() # Draw default glyph marker in middle. glyphName = self.glyphNames[0] defaultLocation = {} self._drawGlyphMarker(mx, my, glyphName, fontSize, defaultLocation, strokeW=3) # Draw DeltaLocation circles. for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] # Outside maxValue location = {axisName: maxValue} markerX, markerY = self._angle2XY(angle, self.w / 2) self._drawGlyphMarker(mx + markerX, my + markerY, glyphName, fontSize / 2, location) # Interpolated DeltaLocation circles. location = { axisName: minValue + (maxValue - minValue) * INTERPOLATION } markerX, markerY = self._angle2XY(angle, self.w / 4) self._drawGlyphMarker(mx + markerX * INTERPOLATION * 2, my + markerY * INTERPOLATION * 2, glyphName, fontSize / 2, location) # Draw axis names and DeltaLocation values if self.showAxisNames: for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] location = {axisName: maxValue} valueFontSize = self.style.get('valueFontSize', 12) axisNameFontSize = self.style.get('axisNameFontSize', 12) markerX, markerY = self._angle2XY(angle, self.w / 2) fs = c.newString( makeAxisName(axisName), style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=axisNameFontSize, fill=self.style.get('axisNameColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) c.rect(mx + markerX - tw / 2 - 4, my + markerY - axisNameFontSize / 2 - th * 1.5 - 4, tw + 8, th) c.text(fs, (mx + markerX - tw / 2, my + markerY - axisNameFontSize / 2 - th * 1.5)) # DeltaLocation master value if maxValue < 10: sMaxValue = '%0.2f' % maxValue else: sMaxValue = ` int(round(maxValue)) ` fs = c.newString( sMaxValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) c.rect(mx + markerX - tw / 2 - 4, my + markerY + valueFontSize / 2 + th * 1.5 - 4, tw + 8, th) c.text(fs, (mx + markerX - tw / 2, my + markerY + valueFontSize / 2 + th * 1.5)) # DeltaLocation value interpolationValue = minValue + (maxValue - minValue) * INTERPOLATION if interpolationValue < 10: sValue = '%0.2f' % interpolationValue else: sValue = ` int(round(interpolationValue)) ` fs = c.newString( sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) c.rect( mx + markerX * INTERPOLATION - tw / 2 - 4, my + markerY * INTERPOLATION + valueFontSize / 2 + th * 1.5 - 4, tw + 8, th) c.text( fs, (mx + markerX * INTERPOLATION - tw / 2, my + markerY * INTERPOLATION + valueFontSize / 2 + th * 1.5)) # DeltaLocation value if minValue < 10: sValue = '%0.2f' % minValue else: sValue = ` int(round(minValue)) ` fs = c.newString( sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) minM = 0.2 c.rect(mx + markerX * minM - tw / 2 - 4, my + markerY * minM + th * 0.5 - 4, tw + 8, th) c.text(fs, (mx + markerX * minM - tw / 2, my + markerY * minM + th * 0.5))
def draw(self): # Try to use TYPETR-Bitcount. Only is instsalled in the system. # See samples at https://bitcount.typenetwork.com # Order a license at: https://store.typenetwork.com/foundry/typetr/fonts/bitcount USE_BITCOUNT = True LETTERS = 'ABCEFGHIJKLMNOPQRSTUVWXYZ' Frames = 80 W = H = 500 IMAGE_PATH = '_export/HorizonWorld.gif' if not USE_BITCOUNT or not 'BitcountMonoDouble-RegularCircle' in context.installedFonts( ): fontNames = ['Georgia-Bold', 'Georgia'] else: fontNames = [] for fontName in installedFonts(): if 'BitcountMono' in fontName and not 'Italic' in fontName: fontNames.append(fontName) letters = [] for n in range(10): c = choice(LETTERS) x = 0 y = 15 z = 20 + int(random() * 500) x = 1 / z + random() * 100 - 100 cc = random() * 0.8 + 0.1, random() * 0.1, random() * 0.8 * 0.1 f = choice(fontNames) letters.append((c, f, x, y, z, cc)) for n in range(Frames): context.newPage(W, H) context.fill(0.8) context.rect(0, 0, W, H) for c, f, x, y, z, (r, g, b) in letters: #y = y/z context.fill( (r, g, b) ) # Needs tuple, instead of separate r, g, b as in DrawBot context.font(f) context.stroke(None) fSize = min(200, 40000 / z) context.fontSize(fSize) context.text(c, (x + 250, y + 250 - fSize / 2)) context.fill(None) context.strokeWidth(0.5) context.stroke(0.5) context.line((0, 250), (500, 250)) context.fill( (1, 1, 1, 0.4) ) # Needs tuple, instead of separate r, g, b as in DrawBot context.rect(0, 0, W, H / 2 - 1) for n in range(0, 500, 10): context.fill(None) context.stroke(1) y = W / 2 - 2 - n * 0.4 lineThickness = (random() * 3 + 0.5) * (H / 2 - y) / 10 context.strokeWidth(lineThickness) context.line((0, y - lineThickness / 2), (W, y - lineThickness / 2)) context.saveImage(IMAGE_PATH)
def draw(self, page, x, y): u"""Draw the circle info-graphic, showing most info about the variation font as can be interpreted from the file.""" c.fill(0.9) c.stroke(None) mx = x + self.w / 2 my = y + self.h / 2 # Gray circle that defines the area of c.oval(x, y, self.w, self.h) # Draw axis spikes first, so we can cover them by the circle markers. axes = self.font.axes fontSize = self.style.get('fontSize', self.DEFAULT_FONT_SIZE) # Calculate sorted relative angle pairs. xAngles = {} # X-ref, key is angle, value is list of axisName for axisName in axes: angle = globals()[axisName] if not angle in xAngles: # Ignore overlapping xAngles[angle] = axisName #print xAngles sortedAngles = sorted(xAngles) anglePairs = [] a1 = None for a2 in sortedAngles: if a1 is not None: if abs(a2 - a1) < 35: anglePairs.append((a1, a2)) a1 = a2 # Draw name of the font c.fill(0) c.text( c.newString(self.font.info.familyName, style=dict(font=self.style['labelFont'], fontSize=self.style['titleFontSize'])), (x - fontSize / 2, y + self.h + fontSize / 4)) # Draw spokes c.fill(None) c.stroke(0.7) c.strokeWidth(1) # Gray on full circle c.newPath() for axisName, angle in self.angles.items(): markerX, markerY = self._angle2XY(angle, self.w / 2) c.moveTo((mx - markerX, my - markerY)) c.lineTo((mx + markerX, my + markerY)) c.drawPath() # Black on range of axis. c.stroke(0) c.newPath() for axisName, angle in self.angles.items(): markerX, markerY = self._angle2XY(angle, self.w / 2) c.moveTo((mx, my)) c.lineTo((mx + markerX, my + markerY)) c.drawPath() # Pair combinations if anglePairs: c.newPath() for a1, a2 in anglePairs: markerX1, markerY1 = self._angle2XY(a1, self.w / 2) markerX2, markerY2 = self._angle2XY(a2, self.w / 2) c.moveTo((mx + markerX1, my + markerY1)) c.lineTo((mx + markerX2, my + markerY2)) c.moveTo((mx + markerX1 * INTERPOLATION, my + markerY1 * INTERPOLATION)) c.lineTo((mx + markerX2 * INTERPOLATION, my + markerY2 * INTERPOLATION)) c.stroke(0, 0, 1) c.fill(None) c.drawPath() # Draw default glyph marker in middle. glyphName = self.glyphNames[0] defaultLocation = {} self._drawGlyphMarker(mx, my, glyphName, fontSize, defaultLocation, strokeW=3) # Draw DeltaLocation circles. for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] # Outside maxValue location = {axisName: maxValue} markerX, markerY = self._angle2XY(angle, self.w / 2) self._drawGlyphMarker(mx + markerX, my + markerY, glyphName, fontSize / 2, location) # Interpolated DeltaLocation circles. location = { axisName: minValue + (maxValue - minValue) * INTERPOLATION } markerX, markerY = self._angle2XY(angle, self.w / 4) self._drawGlyphMarker(mx + markerX * INTERPOLATION * 2, my + markerY * INTERPOLATION * 2, glyphName, fontSize / 2, location) # If there are any pairs, draw the interpolation between them #if anglePairs: # for a1, a2 in anglePairs: # axis1 = # helper function: def makeAxisName(axisName): if not axisName in ('wght', 'wdth', 'opsz'): return axisName.upper() return axisName # Draw axis names and DeltaLocation values if self.showAxisNames: for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] location = {axisName: maxValue} valueFontSize = self.style.get('valueFontSize', 12) axisNameFontSize = self.style.get('axisNameFontSize', 12) markerX, markerY = self._angle2XY(angle, self.w / 2) fs = c.newString( makeAxisName(axisName), style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=axisNameFontSize, fill=self.style.get('axisNameColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) c.rect(mx + markerX - tw / 2 - 4, my + markerY - axisNameFontSize / 2 - th * 1.5 - 4, tw + 8, th) c.text(fs, (mx + markerX - tw / 2, my + markerY - axisNameFontSize / 2 - th * 1.5)) # DeltaLocation master value if maxValue < 10: sMaxValue = '%0.2f' % maxValue else: sMaxValue = ` int(round(maxValue)) ` fs = c.newString( sMaxValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) c.rect(mx + markerX - tw / 2 - 4, my + markerY + valueFontSize / 2 + th * 1.5 - 4, tw + 8, th) c.text(fs, (mx + markerX - tw / 2, my + markerY + valueFontSize / 2 + th * 1.5)) # DeltaLocation value interpolationValue = minValue + (maxValue - minValue) * INTERPOLATION if interpolationValue < 10: sValue = '%0.2f' % interpolationValue else: sValue = ` int(round(interpolationValue)) ` fs = c.newString( sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) c.rect( mx + markerX * INTERPOLATION - tw / 2 - 4, my + markerY * INTERPOLATION + valueFontSize / 2 + th * 1.5 - 4, tw + 8, th) c.text( fs, (mx + markerX * INTERPOLATION - tw / 2, my + markerY * INTERPOLATION + valueFontSize / 2 + th * 1.5)) # DeltaLocation value if minValue < 10: sValue = '%0.2f' % minValue else: sValue = ` int(round(minValue)) ` fs = c.newString( sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = c.textSize(fs) c.fill(0.7, 0.7, 0.7, 0.6) c.stroke(None) minM = 0.2 c.rect(mx + markerX * minM - tw / 2 - 4, my + markerY * minM - 8, tw + 8, th) c.text(fs, (mx + markerX * minM - tw / 2, my + markerY * minM - 4))
def _drawFontCircle(self, px, py): fontSize = self.css('fontSize', self.DEFAULT_FONT_SIZE) markerSize = fontSize * self.R # Calculate the max square size w = self.w - markerSize h = self.h - markerSize context.fill(0.9) context.stroke(None) mx = px + self.pw / 2 my = py + self.ph / 2 # Gray circle that defines the area of the axis extremes. context.oval(px + markerSize / 2, py + markerSize / 2, w, h) # Draw axis spikes first, so we can cover them by the circle markers. axes = self.font.axes # Draw default glyph circle marker in middle. glyphName = self.glyphNames[0] #varLocation = getVarLocation(self.font, self.location) # Show neutral, unless a location is requested varLocation = self.location # = getVarLocation(self.font, self.location) # Show neutral, unless a location is requested self._drawGlyphMarker(None, mx, my, glyphName, fontSize, varLocation, strokeW=3) # Draw angle = 0 for axisName, (minValue, defaultValue, maxValue) in axes.items(): # Draw needles, depending on the axis values and the status of self.location if self.draw3D: needleStart = 0.40 # Just enough overlap with edge of neutral circle marker else: needleStart = 2 / 3 # Start at edge of neutral circle marker rStart = fontSize rEnd = w / 2 if self.location is not None and axisName in self.location: rEnd = rStart + (rEnd - rStart) * self.location[axisName] rStart = fontSize * needleStart #print rStart, rEnd startX, startY = self._angle2XY(angle, rStart) endX, endY = self._angle2XY(angle, rEnd) if (w / 2 + rStart) - rEnd - fontSize > fontSize: startX1, startY1 = self._angle2XY(angle - 180, fontSize / 2) endX1, endY1 = self._angle2XY(angle - 180, (w / 2 + rStart) - rEnd - fontSize) else: startX1 = None context.stroke(None) context.fill(0.3) context.oval(mx + startX - 2, my + startY - 2, 4, 4) context.fill(None) context.stroke(0) context.strokeWidth(1) context.newPath() context.moveTo((mx + startX, my + startY)) context.lineTo((mx + endX, my + endY)) if startX1 is not None: context.moveTo((mx + startX1, my + startY1)) context.lineTo((mx + endX1, my + endY1)) context.drawPath() # Show the glyph shape as it is at the max location of the axis. location = {axisName: maxValue} self._drawGlyphMarker(axisName, mx + endX, my + endY, glyphName, fontSize, location) angle += 360 / len(axes)
style=dict(lineHeight=1, font=f.installedName, fontSize=12)) # Create DrawBotString, inheriting from BabelString, based on view type. bs = context.newString('Hlxg', style=dict(font=f.installedName, textFill=(1, 0, 0), fontSize=300, leading=320, baselineShift=200)) M = 20 context.fill(0.9) context.rect(M, M, 1000 - 2 * M, 800 - 2 * M) Y = 1000 - M BASE = (f.info.ascender + f.info.descender + f.info.lineGap) / 2 print('%s %s %s %s' % (f.info.familyName, f.info.styleName, 'Ascender + Descender + lineGap =', BASE)) YS = (BASE, ) print YS for y in YS: context.stroke(0) context.strokeWidth(0.5) context.line((0, y), (1000, y)) context.fill(0) context.stroke(None) context.textBox(bs, (M, M, 1000 - 2 * M, 400 - 2 * M))
def _drawFontCircle(self, px, py): context = self.context # Get context from the parent doc. context.fill(0.9) context.stroke(None) mx = px + self.w / 2 my = py + self.h / 2 # Gray circle that defines the area of context.oval(px, py, self.w, self.h) # Draw axis spikes first, so we can cover them by the circle markers. axes = self.font.axes fontSize = self.style.get('fontSize', self.DEFAULT_FONT_SIZE) # Draw name of the font bs = context.newString(self.font.info.familyName, style=dict( font=self.style['labelFont'], fontSize=self.style['axisNameFontSize'], textFill=0)) context.text(bs, (px - fontSize / 2, py + self.h + fontSize / 2)) # Draw spokes context.fill(None) context.stroke(0) context.strokeWidth(1) context.newPath() for axisName, angle in self.angles.items(): markerX, markerY = self._angle2XY(angle, self.w / 2) context.moveTo((mx, my)) context.lineTo((mx + markerX, my + markerY)) context.drawPath() # Draw default glyph marker in middle. defaultLocation = {} self._drawGlyphIcon(mx, my, self.glyphName, fontSize, defaultLocation, strokeW=3) # Draw DeltaLocation circles. for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] # Outside maxValue location = {axisName: maxValue} markerX, markerY = self._angle2XY(angle, self.w / 2) self._drawGlyphIcon(mx + markerX, my + markerY, self.glyphName, fontSize / 2, location) # Interpolated DeltaLocation circles. location = { axisName: minValue + (maxValue - minValue) * self.INTERPOLATION } markerX, markerY = self._angle2XY(angle, self.w / 4) self._drawGlyphIcon(mx + markerX * self.INTERPOLATION * 2, my + markerY * self.INTERPOLATION * 2, self.glyphName, fontSize / 2, location) # Draw axis names and DeltaLocation values if self.showAxisNames: for axisName, (minValue, defaultValue, maxValue) in axes.items(): angle = self.angles[axisName] location = {axisName: maxValue} valueFontSize = self.style.get('valueFontSize', 12) axisNameFontSize = self.style.get('axisNameFontSize', 12) markerX, markerY = self._angle2XY(angle, self.w / 2) bs = context.newString( self.makeAxisName(axisName), style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=axisNameFontSize, fill=self.style.get('axisNameColor', 0))) tw, th = context.textSize(bs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) context.rect( mx + markerX - tw / 2 - 4, my + markerY - axisNameFontSize / 2 - th * 1.5 - 4, tw + 8, th) context.text(bs, (mx + markerX - tw / 2, my + markerY - axisNameFontSize / 2 - th * 1.5)) # DeltaLocation master value if maxValue < 10: sMaxValue = '%0.2f' % maxValue else: sMaxValue = ` int(round(maxValue)) ` bs = context.newString( sMaxValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = context.textSize(bs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) context.rect(mx + markerX - tw / 2 - 4, my + markerY + valueFontSize / 2 + th * 1.5 - 4, tw + 8, th) context.text(bs, (mx + markerX - tw / 2, my + markerY + valueFontSize / 2 + th * 1.5)) # DeltaLocation value interpolationValue = minValue + (maxValue - minValue) * self.INTERPOLATION if interpolationValue < 10: sValue = '%0.2f' % interpolationValue else: sValue = ` int(round(interpolationValue)) ` bs = context.newString( sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = context.textSize(bs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) context.rect( mx + markerX * self.INTERPOLATION - tw / 2 - 4, my + markerY * self.INTERPOLATION + valueFontSize / 2 + th * 1.5 - 4, tw + 8, th) context.text(bs, (mx + markerX * self.INTERPOLATION - tw / 2, my + markerY * self.INTERPOLATION + valueFontSize / 2 + th * 1.5)) # DeltaLocation value if minValue < 10: sValue = '%0.2f' % minValue else: sValue = ` int(round(minValue)) ` bs = context.newString( sValue, style=dict(font=self.style.get('labelFont', 'Verdana'), fontSize=valueFontSize, fill=self.style.get('axisValueColor', 0))) tw, th = context.textSize(bs) context.fill((0.7, 0.7, 0.7, 0.6)) context.stroke(None) minM = 0.2 context.rect(mx + markerX * minM - tw / 2 - 4, my + markerY * minM + th * 0.5 - 4, tw + 8, th) context.text(bs, (mx + markerX * minM - tw / 2, my + markerY * minM + th * 0.5))