Skip to content

Instantly share code, notes, and snippets.

@jmsole
Last active March 30, 2023 22:27
Show Gist options
  • Save jmsole/e6176f26a3e3eb33cef2f90f4e627d98 to your computer and use it in GitHub Desktop.
Save jmsole/e6176f26a3e3eb33cef2f90f4e627d98 to your computer and use it in GitHub Desktop.
Type proof generator script for DrawBot. It uses the `wordsiv` library to generate words when the character is not complete. The over script is based on the `simpleproof` script by DJR.
import unicodedata
import datetime
import os
import string
from wordsiv import WordSiv
from itertools import product
import en_wordcount_web
from fontTools.ttLib import TTFont
from fontTools.agl import toUnicode
from fontTools.pens.boundsPen import BoundsPen
# import en_markov_gutenberg
########################
# SETUP
# Font path
myFontPath = '../builds/MyFont_Regular.ttf'
#myFontPath = '../builds/MyFontVF_Wght.ttf'
# If yes, then what axis values do you want to proof?
axesValues = {"wght": (100, 400, 900)}
# Character set
f = TTFont(myFontPath)
gset = f.getGlyphSet()
charset = ''
for i in gset:
if '.' in i:
pass
else:
bp = BoundsPen(i)
gset[i].draw(bp)
if bp.bounds is None:
continue
else:
charset = charset + toUnicode(i)
# Font sizes
charsetFontSize = 98
spacingFontSize = 20
largeTextFontSize = 36
smallTextFontSize = 11
# OpenType features
otfeatures = dict(liga=False, lnum=False, kern=False, ss01=True)
# Fallback font. Adobe Blank should be in the same folder as the script when running it.
myFallbackFont = 'AdobeBlank.otf'
useFontContainsCharacters = True
#################################
# Some helpers and categorization
# Is it a Variable Font?
isVariableFont = ""
variableDict = listFontVariations(myFontPath)
if bool(variableDict):
isVariableFont = True
elif not variableDict:
isVariableFont = False
def product_dict(**kwargs):
keys = kwargs.keys()
vals = kwargs.values()
for instance in product(*vals):
yield dict(zip(keys, instance))
if isVariableFont:
axesProduct = list(product_dict(**axesValues))
else:
axesProduct = ""
# Some categoriazation
upperTemplate = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lowerTemplate = "abcdefghijklmnopqrstuvwxyz"
uniLu = "" # Letter Uppercase
uniLl = "" # Letter Lowercase
uniPo = "" # Punctuation Other
uniPc = "" # Punctuation Connector
uniPd = "" # Punctuation Dash
uniPs = "" # Punctuation Open
uniPe = "" # Punctuation Close
uniPi = "" # Punctuation Initial Quote
uniPf = "" # Punctuation Final Quote
uniSm = "" # Symbol Math
uniSc = "" # Symbol Currency
for i in charset:
if unicodedata.category(i) == 'Ll':
uniLl += i
elif unicodedata.category(i) == 'Lu':
uniLu += i
elif unicodedata.category(i) == 'Po':
uniPo += i
elif unicodedata.category(i) == 'Pc':
uniPc += i
elif unicodedata.category(i) == 'Pd':
uniPd += i
elif unicodedata.category(i) == 'Ps':
uniPs += i
elif unicodedata.category(i) == 'Pe':
uniPe += i
elif unicodedata.category(i) == 'Pi':
uniPi += i
elif unicodedata.category(i) == 'Pf':
uniPf += i
elif unicodedata.category(i) == 'Sm':
uniSm += i
elif unicodedata.category(i) == 'Sc':
uniSc += i
uppercaseOnly = False
lowercaseOnly = False
# Check if Uppercase-only or Lowercase-only, or mixed-case
if uniLu == "":
lowercaseOnly = True
elif uniLl == "":
uppercaseOnly = True
##################
# Predefined texts
bigMixedText = "Alpha Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliet Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xylophone Yankee Zulu. Jackdaws love my big sphinx of quartz. Victors flank gypsy who mixed up on job quiz. Wolves exit quickly as fanged zoo chimps jabber. Five jumbo oxen graze quietly with packs of dogs. Grumpy wizards make toxic brew for the evil queen and jack. Lazy movers quit hard packing of jewelry boxes. Ban foul, toxic smogs which quickly jeopardize lives. Hark! Toxic jungle water vipers quietly drop on zebras for meals! New farm hand, picking just six quinces, proves strong but lazy. Back in my quaint garden, jaunty zinnias vie with flaunting phlox. Waltz, nymph, for quick jigs vex Bud. Crazy Fredericka bought many very exquisite opal jewels. Jolly housewives made inexpensive meals using quick-frozen vegetables. Sixty zippers were quickly picked from the woven jute bag. Call 1 (800) 435 8293 Jaded zombies acted quaintly but kept driving their oxen forward. Six big juicy steaks sizzled in a pan as five workmen left the quarry. Will Major Douglas be expected to take this true-false quiz very soon? A mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent. Jimmy and Zack, the police explained, were last seen diving into a field of buttered quahogs. Monique, the buxom coed, likes to fight for Pez with the junior varsity team. The jukebox music puzzled a gentle visitor from a quaint valley town. Just work for improved basic techniques to maximize your typing skills. When we go back to Juarez, Mexico, do we fly over picturesque Arizona? Murky haze enveloped a city as jarring quakes broke forty-six windows. Nancy Bizal exchanged vows with Robert J. Kumpf at Quincy Temple. The quick brown fox jumps over the lazy dog."
bigLowerText = "alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xylophone yankee zulu. jackdaws love my big sphinx of quartz. victors flank gypsy who mixed up on job quiz. wolves exit quickly as fanged zoo chimps jabber. five jumbo oxen graze quietly with packs of dogs. grumpy wizards make toxic brew for the evil queen and jack. lazy movers quit hard packing of jewelry boxes. ban foul, toxic smogs which quickly jeopardize lives. hark! toxic jungle water vipers quietly drop on zebras for meals! new farm hand, picking just six quinces, proves strong but lazy. back in my quaint garden, jaunty zinnias vie with flaunting phlox. waltz, nymph, for quick jigs vex bud. crazy fredericka bought many very exquisite opal jewels. jolly housewives made inexpensive meals using quick-frozen vegetables. sixty zippers were quickly picked from the woven jute bag. call 1 (800) 435 8293 jaded zombies acted quaintly but kept driving their oxen forward. six big juicy steaks sizzled in a pan as five workmen left the quarry. will major douglas be expected to take this true-false quiz very soon? a mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent. jimmy and zack, the police explained, were last seen diving into a field of buttered quahogs. monique, the buxom coed, likes to fight for pez with the junior varsity team. the jukebox music puzzled a gentle visitor from a quaint valley town. just work for improved basic techniques to maximize your typing skills. when we go back to juarez, mexico, do we fly over picturesque arizona? murky haze enveloped a city as jarring quakes broke forty-six windows. nancy bizal exchanged vows with robert j. kumpf at quincy temple. the quick brown fox jumps over the lazy dog."
bigUpperText = "ALPHA BRAVO CHARLIE DELTA ECHO FOXTROT GOLF HOTEL INDIA JULIET KILO LIMA MIKE NOVEMBER OSCAR PAPA QUEBEC ROMEO SIERRA TANGO UNIFORM VICTOR WHISKEY XYLOPHONE YANKEE ZULU. JACKDAWS LOVE MY BIG SPHINX OF QUARTZ. VICTORS FLANK GYPSY WHO MIXED UP ON JOB QUIZ. WOLVES EXIT QUICKLY AS FANGED ZOO CHIMPS JABBER. FIVE JUMBO OXEN GRAZE QUIETLY WITH PACKS OF DOGS. GRUMPY WIZARDS MAKE TOXIC BREW FOR THE EVIL QUEEN AND JACK. LAZY MOVERS QUIT HARD PACKING OF JEWELRY BOXES. BAN FOUL, TOXIC SMOGS WHICH QUICKLY JEOPARDIZE LIVES. HARK! TOXIC JUNGLE WATER VIPERS QUIETLY DROP ON ZEBRAS FOR MEALS! NEW FARM HAND, PICKING JUST SIX QUINCES, PROVES STRONG BUT LAZY. BACK IN MY QUAINT GARDEN, JAUNTY ZINNIAS VIE WITH FLAUNTING PHLOX. WALTZ, NYMPH, FOR QUICK JIGS VEX BUD. CRAZY FREDERICKA BOUGHT MANY VERY EXQUISITE OPAL JEWELS. JOLLY HOUSEWIVES MADE INEXPENSIVE MEALS USING QUICK-FROZEN VEGETABLES. SIXTY ZIPPERS WERE QUICKLY PICKED FROM THE WOVEN JUTE BAG. CALL 1 (800) 435 8293 JADED ZOMBIES ACTED QUAINTLY BUT KEPT DRIVING THEIR OXEN FORWARD. SIX BIG JUICY STEAKS SIZZLED IN A PAN AS FIVE WORKMEN LEFT THE QUARRY. WILL MAJOR DOUGLAS BE EXPECTED TO TAKE THIS TRUE-FALSE QUIZ VERY SOON? A MAD BOXER SHOT A QUICK, GLOVED JAB TO THE JAW OF HIS DIZZY OPPONENT. JIMMY AND ZACK, THE POLICE EXPLAINED, WERE LAST SEEN DIVING INTO A FIELD OF BUTTERED QUAHOGS. MONIQUE, THE BUXOM COED, LIKES TO FIGHT FOR PEZ WITH THE JUNIOR VARSITY TEAM. THE JUKEBOX MUSIC PUZZLED A GENTLE VISITOR FROM A QUAINT VALLEY TOWN. JUST WORK FOR IMPROVED BASIC TECHNIQUES TO MAXIMIZE YOUR TYPING SKILLS. WHEN WE GO BACK TO JUAREZ, MEXICO, DO WE FLY OVER PICTURESQUE ARIZONA? MURKY HAZE ENVELOPED A CITY AS JARRING QUAKES BROKE FORTY-SIX WINDOWS. NANCY BIZAL EXCHANGED VOWS WITH ROBERT J. KUMPF AT QUINCY TEMPLE. THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG."
smallMixedText = "Alpha Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliet Kilo Lima Mike November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey Xylophone Yankee Zulu. Jackdaws love my big sphinx of quartz. Victors flank gypsy who mixed up on job quiz. Wolves exit quickly as fanged zoo chimps jabber. Five jumbo oxen graze quietly with packs of dogs. Grumpy wizards make toxic brew for the evil queen and jack. Lazy movers quit hard packing of jewelry boxes. Ban foul, toxic smogs which quickly jeopardize lives. Hark! Toxic jungle water vipers quietly drop on zebras for meals! New farm hand, picking just six quinces, proves strong but lazy. Back in my quaint garden, jaunty zinnias vie with flaunting phlox. Waltz, nymph, for quick jigs vex Bud. Crazy Fredericka bought many very exquisite opal jewels. Jolly housewives made inexpensive meals using quick-frozen vegetables. Sixty zippers were quickly picked from the woven jute bag. Call 1 (800) 435 8293 Jaded zombies acted quaintly but kept driving their oxen forward. Six big juicy steaks sizzled in a pan as five workmen left the quarry. Will Major Douglas be expected to take this true-false quiz very soon? A mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent. Jimmy and Zack, the police explained, were last seen diving into a field of buttered quahogs. Monique, the buxom coed, likes to fight for Pez with the junior varsity team. The jukebox music puzzled a gentle visitor from a quaint valley town. Just work for improved basic techniques to maximize your typing skills. When we go back to Juarez, Mexico, do we fly over picturesque Arizona? Murky haze enveloped a city as jarring quakes broke forty-six windows. Nancy Bizal exchanged vows with Robert J. Kumpf at Quincy Temple. The quick brown fox jumps over the lazy dog.Angel Adept Blind Bodice Clique Coast Dunce Docile Enact Eosin Furlong Focal Gnome Gondola Human Hoist Inlet Iodine Justin Jocose Knoll Koala Linden Loads Milliner Modal Number Nodule Onset Oddball Pneumo Poncho Quanta Qophs Rhone Roman Snout Sodium Tundra Tocsin Uncle Udder Vulcan Vocal Whale Woman Xmas Xenon Yunnan Young Zloty Zodiac. Angel angel adept for the nuance loads of the arena cocoa and quaalude. Blind blind bodice for the submit oboe of the club snob and abbot. Clique clique coast for the pouch loco of the franc assoc and accede. Dunce dunce docile for the loudness mastodon of the loud statehood and huddle. Enact enact eosin for the quench coed of the pique canoe and bleep. Furlong furlong focal for the genuflect profound of the motif aloof and offers. Gnome gnome gondola for the impugn logos of the unplug analog and smuggle. Human human hoist for the buddhist alcohol of the riyadh caliph and bathhouse. Inlet inlet iodine for the quince champion of the ennui scampi and shiite. Justin justin jocose for the djibouti sojourn of the oranj raj and hajjis. Knoll knoll koala for the banknote lookout of the dybbuk outlook and trekked. Linden linden loads for the ulna monolog of the consul menthol and shallot. Milliner milliner modal for the alumna solomon of the album custom and summon. Number number nodule for the unmade economic of the shotgun bison and tunnel. Onset onset oddball for the abandon podium of the antiquo tempo and moonlit. Pneumo pneumo poncho for the dauphin opossum of the holdup bishop and supplies. Quanta quanta qophs for the inquest sheqel of the cinq coq and suqqu. Rhone rhone roman for the burnt porous of the lemur clamor and carrot. Snout snout sodium for the ensnare bosom of the genus pathos and missing. Tundra tundra tocsin for the nutmeg isotope of the peasant ingot and ottoman. Uncle uncle udder for the dunes cloud of the hindu thou and continuum. Vulcan vulcan vocal for the alluvial ovoid of the yugoslav chekhov and revved. Whale whale woman for the meanwhile blowout of the forepaw meadow and glowworm. Xmas xmas xenon for the bauxite doxology of the tableaux equinox and exxon. Yunnan yunnan young for the dynamo coyote of the obloquy employ and sayyid. Zloty zloty zodiac for the gizmo ozone of the franz laissez and buzzing."
smallLowerText = "alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xylophone yankee zulu. jackdaws love my big sphinx of quartz. victors flank gypsy who mixed up on job quiz. wolves exit quickly as fanged zoo chimps jabber. five jumbo oxen graze quietly with packs of dogs. grumpy wizards make toxic brew for the evil queen and jack. lazy movers quit hard packing of jewelry boxes. ban foul, toxic smogs which quickly jeopardize lives. hark! toxic jungle water vipers quietly drop on zebras for meals! new farm hand, picking just six quinces, proves strong but lazy. back in my quaint garden, jaunty zinnias vie with flaunting phlox. waltz, nymph, for quick jigs vex bud. crazy fredericka bought many very exquisite opal jewels. jolly housewives made inexpensive meals using quick-frozen vegetables. sixty zippers were quickly picked from the woven jute bag. call 1 (800) 435 8293 jaded zombies acted quaintly but kept driving their oxen forward. six big juicy steaks sizzled in a pan as five workmen left the quarry. will major douglas be expected to take this true-false quiz very soon? a mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent. jimmy and zack, the police explained, were last seen diving into a field of buttered quahogs. monique, the buxom coed, likes to fight for pez with the junior varsity team. the jukebox music puzzled a gentle visitor from a quaint valley town. just work for improved basic techniques to maximize your typing skills. when we go back to juarez, mexico, do we fly over picturesque arizona? murky haze enveloped a city as jarring quakes broke forty-six windows. nancy bizal exchanged vows with robert j. kumpf at quincy temple. the quick brown fox jumps over the lazy dog.angel adept blind bodice clique coast dunce docile enact eosin furlong focal gnome gondola human hoist inlet iodine justin jocose knoll koala linden loads milliner modal number nodule onset oddball pneumo poncho quanta qophs rhone roman snout sodium tundra tocsin uncle udder vulcan vocal whale woman xmas xenon yunnan young zloty zodiac. angel angel adept for the nuance loads of the arena cocoa and quaalude. blind blind bodice for the submit oboe of the club snob and abbot. clique clique coast for the pouch loco of the franc assoc and accede. dunce dunce docile for the loudness mastodon of the loud statehood and huddle. enact enact eosin for the quench coed of the pique canoe and bleep. furlong furlong focal for the genuflect profound of the motif aloof and offers. gnome gnome gondola for the impugn logos of the unplug analog and smuggle. human human hoist for the buddhist alcohol of the riyadh caliph and bathhouse. inlet inlet iodine for the quince champion of the ennui scampi and shiite. justin justin jocose for the djibouti sojourn of the oranj raj and hajjis. knoll knoll koala for the banknote lookout of the dybbuk outlook and trekked. linden linden loads for the ulna monolog of the consul menthol and shallot. milliner milliner modal for the alumna solomon of the album custom and summon. number number nodule for the unmade economic of the shotgun bison and tunnel. onset onset oddball for the abandon podium of the antiquo tempo and moonlit. pneumo pneumo poncho for the dauphin opossum of the holdup bishop and supplies. quanta quanta qophs for the inquest sheqel of the cinq coq and suqqu. rhone rhone roman for the burnt porous of the lemur clamor and carrot. snout snout sodium for the ensnare bosom of the genus pathos and missing. tundra tundra tocsin for the nutmeg isotope of the peasant ingot and ottoman. uncle uncle udder for the dunes cloud of the hindu thou and continuum. vulcan vulcan vocal for the alluvial ovoid of the yugoslav chekhov and revved. whale whale woman for the meanwhile blowout of the forepaw meadow and glowworm. xmas xmas xenon for the bauxite doxology of the tableaux equinox and exxon. yunnan yunnan young for the dynamo coyote of the obloquy employ and sayyid. zloty zloty zodiac for the gizmo ozone of the franz laissez and buzzing."
smallUpperText = "ABIDE ACORN OF THE HABIT DACRON FOR THE BUDDHA GOUDA QUAALUDE. BENCH BOGUS OF THE SCRIBE ROBOT FOR THE APLOMB JACOB RIBBON. CENSUS CORAL OF THE SPICED JOCOSE FOR THE BASIC HAVOC SOCCER. DEMURE DOCILE OF THE TIDBIT LODGER FOR THE CUSPID PERIOD BIDDER. EBBING ECHOING OF THE BUSHED DECAL FOR THE APACHE ANODE NEEDS. FEEDER FOCUS OF THE LIFER BEDFORD FOR THE SERIF PROOF BUFFER. GENDER GOSPEL OF THE PIGEON DOGCART FOR THE SPRIG QUAHOG DIGGER. HERALD HONORS OF THE DIHEDRAL MADHOUSE FOR THE PENH RIYADH BATHHOUSE. IBSEN ICEMAN OF THE APHID NORDIC FOR THE SUSHI SAUDI SHIITE. JENNIES JOGGER OF THE TIJERA ADJOURN FOR THE ORANJ KOWBOJ HAJJIS. KEEPER KOSHER OF THE SHRIKE BOOKCASE FOR THE SHEIK LOGBOOK CHUKKAS. LENDER LOCKER OF THE CHILD GIGOLO FOR THE UNCOIL GAMBOL ENROLLED. MENACE MCCOY OF THE NIMBLE TOMCAT FOR THE DENIM RANDOM SUMMON. NEBULA NOSHED OF THE INBRED BRONCO FOR THE COUSIN CARBON KENNEL. OBSESS OCEAN OF THE PHOBIC DOCKSIDE FOR THE GAUCHO LIBIDO HOODED. PENNIES PODIUM OF THE SNIPER OPCODE FOR THE SCRIP BISHOP HOPPER. QUANTA QOPHS OF THE INQUEST OQOS FOR THE CINQ COQ SUQQU. REDUCE ROGUE OF THE GIRDLE ORCHID FOR THE MEMOIR SENSOR SORREL. SENIOR SCONCE OF THE DISBAR GODSON FOR THE HUBRIS AMENDS LESSEN. TENDON TORQUE OF THE UNITED SCOTCH FOR THE NOUGHT FORGOT BITTERS. UNDER UGLINESS OF THE RHUBARB SEDUCE FOR THE MANCHU HINDU CONTINUUM. VERSED VOUCH OF THE DIVER OVOID FOR THE TELAVIV KARPOV FLIVVER. WENCH WORKER OF THE UNWED SNOWCAP FOR THE ANDREW ESCROW GLOWWORM. XENON XOCHITL OF THE MIXED BOXCAR FOR THE SUFFIX ICEBOX EXXON. YEOMAN YONDER OF THE HYBRID ARROYO FOR THE DINGHY BRANDY SAYYID. ZEBRA ZOMBIE OF THE PRIZED OZONE FOR THE FRANZ ARROZ BUZZING."
###########################################
# Some constants related to page and layout
# Page dimensions and margin
inch = 72
# A4
pageDimensions = 11.7*inch, 8.3*inch
margin = inch/2
# column information (for spacing proof)
columnWidth = (pageDimensions[0]-margin*3)/2
# define a function for drawing a simple footer
def drawFooter(title):
with savedState():
# get date and font name
today = datetime.date.today()
fontName = os.path.split(myFontPath)[1]
# assemble footer text
footerText = f'{today} | {fontName} | {title}'
# and display formatted string
footer = FormattedString(footerText,
font='Courier',
fontSize=9,
lineHeight=9
)
folio = FormattedString(str(pageCount()),
font='Courier',
fontSize=9,
lineHeight=9,
align="right"
)
textBox(footer, (margin, margin-6, width()-margin*2, 9))
textBox(folio, (margin, margin-6, width()-margin*2, 9))
# Function to create a formatted string to feed into textBox
def stringMaker(textInput, fontSizeInput, alignInput="left", trackingInput=0, OTFeaInput=None, VFAxisInput=None):
textString = FormattedString(
textInput,
font=myFontPath,
fallbackFont=myFallbackFont,
fontSize=fontSizeInput,
align=alignInput,
tracking=trackingInput,
openTypeFeatures=OTFeaInput,
fontVariations=VFAxisInput
)
return textString
# Function to draw content into a single column in as many pages as it needs
def drawSingleColumnContent(textToDraw, pageTitle):
while textToDraw:
newPage(*pageDimensions)
drawFooter(pageTitle)
textToDraw = textBox(textToDraw, (margin, margin, width()-margin*2, height()-margin*2))
# Function to draw content into two columns in as many pages as it needs
def drawTwoColumnContent(textToDraw, pageTitle):
xoffset = 0
while textToDraw:
if xoffset+columnWidth > width():
newPage(*pageDimensions)
drawFooter(pageTitle)
xoffset = 0
textToDraw = textBox(textToDraw, (margin+xoffset, margin, width()-margin*2, height()-margin*2))
xoffset += columnWidth + margin
def drawTwoColumnTextContent(textToDraw, pageTitle):
xoffset = 0
while textToDraw:
if xoffset+columnWidth > width():
newPage(*pageDimensions)
drawFooter(pageTitle)
xoffset = 0
textToDraw = textBox(textToDraw, (margin+xoffset, margin, (width()/2)-margin*2, height()-margin*2))
xoffset += columnWidth + margin
# Function to generate long text proofing strings either through wordsiv or by taking the premade string from Hoefler
def generateTextProofString(characterSet, numberParagraph=2, bigProof=True):
textProofString = ""
if uppercaseOnly and set(uniLu) == set(upperTemplate):
if bigProof:
textProofString = bigUpperText
else:
textProofString = smallUpperText
elif lowercaseOnly and set(uniLl) == set(lowerTemplate):
if bigProof:
textProofString = bigLowerText
else:
textProofString = smallLowerText
elif set(uniLu) == set(upperTemplate) and set(uniLl) == set(lowerTemplate):
if bigProof:
textProofString = bigMixedText
else:
textProofString = smallMixedText + " " + smallUpperText
elif uppercaseOnly is False and lowercaseOnly is False:
capitalised = ""
for u in uniLu:
capAndLower = ""
capAndLower = u + uniLl
capitalwsv = WordSiv(limit_glyphs = capAndLower, seed=123456)
capitalwsv.add_source_module(en_wordcount_web)
capitalisedList = capitalwsv.words(source="en_wordcount_web", cap=True, num_words=4, prob=False, min_wl=5, max_wl=14)
capitalisedString = " ". join([str(elem) for elem in capitalisedList])
capitalised += capitalisedString + " "
wsv = WordSiv(limit_glyphs=charset)
wsv.add_source_module(en_wordcount_web)
wsvtext = (wsv.paragraphs(source="en_wordcount_web", num_paras=numberParagraph, min_wl=1, max_wl=14, prob=False, cap_sent=True, model="rand"))
textProofString = capitalised
mixedTextProof = " ". join([str(elem) for elem in wsvtext])
textProofString += "- " + mixedTextProof + " " + mixedTextProof.upper()
elif uppercaseOnly:
upperInitials = ""
upperInitialsHelper = charset.lower()
for u in uniLu:
individualUpper = ""
individualUpper = u + upperInitialsHelper
upperwsv = WordSiv(limit_glyphs = individualUpper, seed=123456)
upperwsv.add_source_module(en_wordcount_web)
upperList = upperwsv.words(source="en_wordcount_web", cap=True, num_words=4, min_wl=5, max_wl=14)
upperInitialsString = " ". join([str(elem) for elem in upperList])
upperInitials += upperInitialsString.upper() + " "
wsv = WordSiv(limit_glyphs=charset)
wsv.add_source_module(en_wordcount_web)
wsvtext = (wsv.paragraphs(source="en_wordcount_web", num_paras=numberParagraph, min_wl=1, max_wl=14, prob=False, uc=True, model="rand"))
textProofString = upperInitials
textProofString +="- " + " ". join([str(elem) for elem in wsvtext])
elif lowercaseOnly:
lowerInitials = ""
for l in uniLl:
individualLower = ""
individualLower = l.upper() + charset
lowerwsv = WordSiv(limit_glyphs = individualLower, seed=123456)
lowerwsv.add_source_module(en_wordcount_web)
lowerList = lowerwsv.words(source="en_wordcount_web", cap=True, num_words=4, min_wl=5, max_wl=14)
lowerInitialsString = " ". join([str(elem) for elem in lowerList])
lowerInitials += lowerInitialsString.lower() + " "
wsv = WordSiv(limit_glyphs=charset)
wsv.add_source_module(en_wordcount_web)
wsvtext = (wsv.paragraphs(source="en_wordcount_web", num_paras=numberParagraph, min_wl=1, max_wl=14, prob=False, cap_sent= not lowercaseOnly, model="rand"))
textProofString = lowerInitials
textProofString += " ". join([str(elem) for elem in wsvtext])
return textProofString
#####################
# DRAW
## Let’s build this proof.
#####################
# charset proof
def charsetProof(characterSet, axisProduct):
sectionName = 'Character set proof'
if axesProduct:
axisDict = {}
for axisData in axesProduct:
axisDict = dict(axisData)
charsetString = stringMaker(characterSet, charsetFontSize, "center", 24, otfeatures, axisDict)
drawSingleColumnContent(charsetString, sectionName + " - " + str(axisData))
elif axesProduct == "":
charsetString = stringMaker(characterSet, charsetFontSize, "center", 24, otfeatures)
drawSingleColumnContent(charsetString, sectionName)
charsetProof(charset, axesProduct)
#####################
# spacing proof
#newPage(*pageDimensions)
#drawFooter('Spacing proof')
# create empty formatted string that we will fill with spacing strings
def generateSpacingString(characterSet):
spacingString = ''
for char in characterSet:
# determine control characters for each character
font(myFontPath)
if useFontContainsCharacters and not fontContainsCharacters(char):
continue
# ignoring linebreaks and space characters
if char not in ['\n', ' ']:
control1 = 'H'
control2 = 'O'
if unicodedata.category(char) == 'Ll':
control1 = 'n'
control2 = 'o'
elif unicodedata.category(char) == 'Nd':
control1 = '0'
control2 = '1'
perCharSpacingString = f'{control1}{control1}{control1}{char}{control1}{control2}{control1}{char}{control2}{char}{control2}{control2}{control2}\n'
spacingString += perCharSpacingString
return spacingString
def spacingProof(characterSet, axesProduct):
sectionName = 'Spacing proof'
if axesProduct:
axisDict = {}
for axisData in axesProduct:
newPage(*pageDimensions)
drawFooter(sectionName + " - " + str(axisData))
axisDict = dict(axisData)
spacingStringInput = generateSpacingString(characterSet)
spacingString = stringMaker(spacingStringInput, spacingFontSize, VFAxisInput=axisDict)
drawTwoColumnContent(spacingString, sectionName + " - " + str(axisData))
elif axesProduct == "":
newPage(*pageDimensions)
drawFooter(sectionName)
spacingStringInput = generateSpacingString(characterSet)
spacingString = stringMaker(spacingStringInput, spacingFontSize)
drawTwoColumnContent(spacingString, sectionName)
spacingProof(charset, axesProduct)
#####################
# big text proof
def bigTextProof (characterSet, axesProduct, numberParagraph):
sectionName = 'Big text proof'
if axesProduct:
axisDict = {}
for axisData in axesProduct:
axisDict = dict(axisData)
bigTextStringInput = generateTextProofString(characterSet, numberParagraph)
bigTextString = stringMaker(bigTextStringInput, largeTextFontSize,OTFeaInput=otfeatures, VFAxisInput=axisDict)
drawSingleColumnContent(bigTextString, sectionName + " - " + str(axisData))
elif axesProduct == "":
bigTextStringInput = generateTextProofString(characterSet, numberParagraph)
bigTextString = stringMaker(bigTextStringInput, largeTextFontSize,OTFeaInput=otfeatures)
drawSingleColumnContent(bigTextString, sectionName)
bigTextProof(charset, axesProduct, 2)
#####################
# small text proof
def smallTextProof (characterSet, axesProduct, numberParagraph):
sectionName = 'Small text proof'
if axesProduct:
axisDict = {}
for axisData in axesProduct:
newPage(*pageDimensions)
drawFooter(sectionName + " - " + str(axisData))
axisDict = dict(axisData)
smallTextStringInput = generateTextProofString(characterSet, numberParagraph, bigProof=False)
smallTextString = stringMaker(smallTextStringInput, smallTextFontSize,OTFeaInput=otfeatures, VFAxisInput=axisDict)
drawTwoColumnTextContent(smallTextString, sectionName + " - " + str(axisData))
elif axesProduct == "":
newPage(*pageDimensions)
drawFooter(sectionName)
smallTextStringInput = generateTextProofString(characterSet, numberParagraph, bigProof=False)
smallTextString = stringMaker(smallTextStringInput, smallTextFontSize, OTFeaInput=otfeatures)
drawTwoColumnTextContent(smallTextString, sectionName)
smallTextProof(charset, axesProduct ,5)
#####################
# full set
# newPage(*pageDimensions)
fullSet = FormattedString(txt=None,
font=myFontPath,
fallbackFont=myFallbackFont,
fontSize=48,
align="left",
tracking=16,
openTypeFeatures=otfeatures,
#fontVariations=dict(wght=i)
)
#textBox(charsetString, (margin, margin, width()-margin*2, height()-margin*2))
#drawFooter('Character set proof')
for j in fullSet.listFontGlyphNames():
if j == 'space':
pass
else:
fullSet.appendGlyph(j)
while fullSet:
newPage(*pageDimensions)
drawFooter('Full set proof')
fullSet = textBox(fullSet, (margin, margin, width()-margin*2, height()-margin*2))
#############
# Saving the entire proof doc
now = datetime.datetime.now()
nowformat = now.strftime('%Y-%m-%d_%H%M')
saveImage(f'{nowformat}_myfont-proof.pdf')
@jmsole
Copy link
Author

jmsole commented Mar 29, 2023

You will need AdobeBlank.otf to be present in the same directory as your script. You also need to set up the wordsiv library to work on DrawBot. There are instruction on how to do this on that repo. A big part of the script is based on DRJ's simpleproof script. The default text used when you have a complete set from A-Z and a-z is taken from Hoefler & Co proof texts.

@jmsole
Copy link
Author

jmsole commented Mar 30, 2023

I made a small adjustment. The script now filters out any glyphs that don't contain outlines. That way you don't have to define the character set to manually. It won't impact the space character used for the long text string proofs. I mean, you can't really make proofs of space characters, can you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment