diff options
Diffstat (limited to 'fonts/utilities/otfinst/otfinst.py')
-rw-r--r-- | fonts/utilities/otfinst/otfinst.py | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/fonts/utilities/otfinst/otfinst.py b/fonts/utilities/otfinst/otfinst.py new file mode 100644 index 0000000000..cf79ab3ccd --- /dev/null +++ b/fonts/utilities/otfinst/otfinst.py @@ -0,0 +1,748 @@ +#!/usr/bin/env python + +# otfinst version 1.0.3 2 January 2007 +# http://www.ece.ucdavis.edu/~jowens/code/otfinst/ +# Formerly known as otftex_install +# +# usage: As arguments, takes OTF font files or directories where the +# OTF font files are located. Configuration is within this file. + +# Copyright (c) 2005, 2006, John Owens <jowens at ece.ucdavis.edu>, +# University of California, Davis. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# Neither the name of the University of California nor the names of +# its contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. THIS +# SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import sys, os, re, math, time, datetime, operator, glob + +####################################### +# BEGIN USER-SETTABLE VARIABLES +####################################### + +# which encoding are we using? +encoding = 'LY1' + +# "encoding" (above) should have a dict entry in "encodings" that specifies +# the two-letter berry designation for the encoding and the command-line +# options to otftotfm that work for the encoding +encodings = { 'LY1': + { 'berryencoding': '8y', + 'commandline': '-e texnansx', + }, + } + +# kpsewhich will look up the right TEXMFHOME directory, but within that, +# where do local fonts go? +localfontsdir = '/tex/latex/localfonts/' + +# which OpenType options do we support in this script? +# add more to optionList if you want the script to support them +optionList = [ + # in optionList, each element is itself a list + # we generate options by always picking one choice from each element + # and taking all permutations of those choices + ['kern'], # always selected + ['liga'], + ['lnum', 'onum', 'swsh', 'sinf', 'sups', 'pnum', 'fakenum'], # one of these + ['smcp', ''], # either smcp or nothing + ] + +# categorize the OpenType options above +# globalOptionList contains options that we should apply to all fonts +globalOptionList = ['kern', 'liga'] +# newFontFamilyOptionList has options that create new font families +newFontFamilyOptionList = ['lnum', 'onum', 'swsh', 'sinf', 'sups', 'pnum', + 'fakenum'] +# weightOptionsList contains options that modify the weight +weightOptionList = [] +# widthOptionsList contains options that modify the width +widthOptionList = [] +# variantOptionsList contains options that modify the variant +variantOptionList = ['smcp'] +# fakeOptionList contains options that aren't real OpenType options +fakeOptionList = [ + 'fakenum' # fakenum is if no ?num is available + ] + +# options and their Berry encodings +# contains both: +# - features (4 letters) and their mappings (lnum, onum, smcp) +# (found in otfinfo --features) +# - subfamily attributes (italic, bold, etc.) +# (found in otfinfo --info, subfamily field) +optionMappings = { + # OpenType command line options + 'lnum' : 'x', + 'onum' : 'j', + 'fakenum' : '', # we manually put this in if lnum and + # onum are not present so we get + # something out + 'swsh' : 'w', + 'sinf' : '0', + 'sups' : '1', + 'pnum' : '2', + 'smcp' : 'sc', # also a variant + # weights + 'thin' : 'ul', + 'extralight' : 'el', + 'light' : 'l', + 'book' : 'm', + 'medium' : 'mb', + 'demibold' : 'db', + 'semibold' : 'sb', + 'bold' : 'b', + 'black' : 'eb', + 'heavy' : 'eb', + # widths + 'condensed' : 'c', + 'cond' : 'c', + 'narrow' : 'n', + 'semicondensed' : 'sc', + 'semiextended' : 'sx', + 'extended' : 'x', + # variants + 'italic' : 'it', + 'slanted' : 'sl', + 'oblique' : 'sl', + 'outline' : 'ol', + # regular is the default + 'regular' : '', + } + +# each font has a 3-letter designation +# it's the first 3 letters from +# http://www.tex.ac.uk/tex-archive/info/fontname/adobe.map +# font names are what's returned from "otfinfo --family fontfile.otf" +berryname = { 'Minion Pro' : 'pmn', + 'Myriad Pro' : 'pmy', + 'Adobe Garamond Pro' : 'pag', + 'Adobe Caslon Pro' : 'pac', + 'Adobe Jenson Pro' : 'paj', + 'Utopia Std' : 'put', + 'Warnock Pro' : 'pwp', + 'Kepler Std' : 'pkp', + 'Prestige Elite Std' : 'ppe', + 'Calibri' : 'jk0', + 'Cambria' : 'jk1', + 'Candara' : 'jk2', + 'Consolas' : 'jk3', + 'Constantia' : 'jk4', + 'Corbel' : 'jk5', + 'Arno Pro' : 'pa0', + 'ScalaSansPro' : '0sa', + 'Hypatia Sans Pro' : 'phy', + } + +# if you would like to scale the font by default, set it here as a float +scaled = { 'Minion Pro' : 1.0, + } + +####################################### +# END USER-SETTABLE VARIABLES +####################################### + +####################################### +# BEGIN UTILITY FUNCTIONS +####################################### + +def letterize(s): + s = s.replace('0', 'zero') + s = s.replace('1', 'one') + s = s.replace('2', 'two') + s = s.replace('3', 'three') + s = s.replace('4', 'four') + s = s.replace('5', 'five') + s = s.replace('6', 'six') + s = s.replace('7', 'seven') + s = s.replace('8', 'eight') + s = s.replace('9', 'nine') + return s + +glyfRE = re.compile("glyf") +CFFRE = re.compile("CFF") +# User can redefine if necessary; used to prune candidate list of fonts +def isValidFontFile(filename): + # return os.system("otfinfo --info -q %s" % filename) == 0 + fields = os.popen("otfinfo -qt '%s'" % filename, 'r').readlines() + # otfinfo -qt returns the fields in the font file; "glyf" means TrueType + # and "CFF" means PostScript + for line in fields: + if glyfRE.search(line): + print "%s (TTF)" % filename + return True + if CFFRE.search(line): + print "%s (PS)" % filename + return True + return False + +# Eddie Kohler's shell script for determining OTF type +# otfflavor () { +# # Returns 0 for not OTF, 1 for CFF-flavored, 2 for TTF-flavored +# otfinfo -qt $1 | awk '/glyf/ { exit 2 } +# /CFF/ { exit 1 }' +# } + +def generateFontfiles(l): + # Get all candidate files. This works as follows: + # 1) glob the input list, resulting in a list + # 2)a) if anything in that list is a file, add it to the fontfiles list + # 2)b) if anything in that list is a dir, add its subfiles to + # the fontfiles list + # start off assuming everything is a dir + gl = [] + fontfiles = [] + for item in l: + gl += glob.glob('%s*' % item) + for item in gl: + if os.path.isdir(item): + for file in os.listdir(item): + fontfiles.append(os.path.join(item, file)) + if os.path.isfile(item): + fontfiles.append(item) + print "Installing following font files:" + fontfiles = filter(isValidFontFile, fontfiles) + return fontfiles + +# http://www.microsoft.com/typography/otspec/recom.htm +# We recommend using name ID's 8-12, to identify manufacturer, +# designer, description, URL of the vendor, and URL of the designer. +# URL's must contain the protocol of the site: for example, http:// or +# mailto: or ftp://. The OpenType font properties extension can +# enumerate this information to the users. +# +# The Subfamily string in the 'name' table should be used for variants +# of weight (ultra light to extra black) and style (oblique/italic or +# not). So, for example, the full font name of "Helvetica Narrow +# Italic" should be defined as Family name "Helvetica Narrow" and +# Subfamily "Italic". This is so that Windows can group the standard +# four weights of a font in a reasonable fashion for +# non-typographically aware applications which only support +# combinations of "bold" and "italic." +# +# The Full font name string usually contains a concatenation of +# strings 1 and 2. However, if the font is 'Regular' as indicated in +# string 2, then use only the family name contained in string 1. This +# is the font name that Windows will expose to users. + +otfinfoversionRE = re.compile("([0-9]+\.[0-9]+)") +otfinfoRE = re.compile("^(.*):\s+(.*)$") +urlRE = re.compile("(\w*)\.(com|net|org|edu|de|fr|co\.uk)") +opticalRE = re.compile("size range \(([\d.]+) pt, ([\d.]+) pt\]") + +# list-to-string and string-to-list primitives (useful when we want to +# index into a hash with a list - we use a string instead) +sep = '-' +def l2s(l): + return sep.join(l) + +def s2l(s): + if not s: + return [] + return s.split(sep) + +# otfinfo requires version 2.38 or higher of otfinfo (which supports --info) +def checkOTFInfoVersion(): + otfiv = os.popen("otfinfo --version").read() + m = otfinfoversionRE.search(otfiv) + if float(m.group(1)) < 2.38: + sys.stderr.write("Your otfinfo version is currently %s.\nPlease upgrade your otfinfo version to at least 2.38.\n" % m.group(1)) + sys.exit() + +# given a font file, populates fonthash for that font file +# calls classifyFont on each font file too +def addToFonthash(font): + postScriptName = os.popen("otfinfo --postscript-name '%s'" % font).read()[:-1] + fonthash[postScriptName] = {} + fonthash[postScriptName]['filename'] = font + info = os.popen("otfinfo --info '%s'" % font).read()[:-1] + for line in info.split('\n'): + m = otfinfoRE.search(line) + if m: + if (m.group(1) == 'Family') or (m.group(1) == 'Preferred family'): + # 'preferred family' preferred over 'family'; relies on it coming last + # which it should under otfinfo + fonthash[postScriptName]['family'] = m.group(2) + elif (m.group(1) == 'Subfamily') or (m.group(1) == 'Preferred subfamily'): + # 'preferred subfamily' preferred over 'subfamily'; relies on + # it coming last which it should under otfinfo + # this RE separates both 'Aa Bb' and 'AaBb' into ['aa', 'bb'] + fonthash[postScriptName]['subfamily'] = \ + re.sub("(?<=[a-z])(?=[A-Z])", " ", m.group(2)).lower().split() + if 'roman' in fonthash[postScriptName]['subfamily']: + # replace 'roman' with 'regular' + roman_id = fonthash[postScriptName]['subfamily'].index('roman') + fonthash[postScriptName]['subfamily'][roman_id] = 'regular' + while 'extra' in fonthash[postScriptName]['subfamily']: + # combine 'extra' with next element, delete 'extra' + extra_id = fonthash[postScriptName]['subfamily'].index('extra') + fonthash[postScriptName]['subfamily'][extra_id+1] = \ + 'extra' + fonthash[postScriptName]['subfamily'][extra_id+1] + fonthash[postScriptName]['subfamily'].remove('extra') + elif m.group(1) == 'Vendor URL': + fonthash[postScriptName]['vendor'] = urlRE.search(m.group(2)).group(1) + if 'vendor' not in fonthash[postScriptName].keys(): + fonthash[postScriptName]['vendor'] = 'generic' + features = os.popen("otfinfo --features '%s'" % font).read()[:-1] + opticalSize = os.popen("otfinfo --optical-size '%s'" % font).read()[:-1] + m = opticalRE.search(opticalSize) + if m: + # convert from OTF's (exclusive, inclusive] to TeX's [inclusive, exclusive) + # OTF measures in decipoints, so add 0.1 to each size + fonthash[postScriptName]['opticalSize'] = [str(float(m.group(1)) + 0.1), + str(float(m.group(2)) + 0.1)] + else: + fonthash[postScriptName]['opticalSize'] = [] + fonthash[postScriptName]['weight'] = [] + fonthash[postScriptName]['width'] = [] + fonthash[postScriptName]['variant'] = [] + for s in fonthash[postScriptName]['subfamily']: + if s in ['regular', 'thin', 'extralight', 'light', 'book', 'medium', 'regular', 'demibold', 'semibold', 'bold', 'heavy', 'black']: + fonthash[postScriptName]['weight'].append(s) + if s in ['regular', 'condensed', 'cond', 'semicondensed', 'narrow', 'semiextended', 'extended']: + fonthash[postScriptName]['width'].append(s) + if s in ['regular', 'italic', 'slanted', 'oblique', 'outline']: + fonthash[postScriptName]['variant'].append(s) + fonthash[postScriptName]['weight'].sort() + fonthash[postScriptName]['width'].sort() + fonthash[postScriptName]['variant'].sort() + fonthash[postScriptName]['weight'] = l2s(fonthash[postScriptName]['weight']) + fonthash[postScriptName]['width'] = l2s(fonthash[postScriptName]['width']) + fonthash[postScriptName]['variant'] = l2s(fonthash[postScriptName]['variant']) + numFeatureCount = 0 + fonthash[postScriptName]['features'] = [] + for line in features.split('\n'): + if line[1:4] == 'num': + numFeatureCount += 1 + fonthash[postScriptName]['features'].append(line[0:4]) + if numFeatureCount == 0: + fonthash[postScriptName]['features'].append('fakenum') + # if no other ?num are present, add 'fakenum' + classifyFont(postScriptName) + +# populates classifiedFonts from fonthash +def classifyFont(postScriptName): + # classifiedFonts[postScriptName][weight][width][variant] contains list of opticals + family = fonthash[postScriptName]['family'] + weight = fonthash[postScriptName]['weight'] + width = fonthash[postScriptName]['width'] + variant = fonthash[postScriptName]['variant'] + if family not in classifiedFonts.keys(): + classifiedFonts[family] = {} + if weight not in classifiedFonts[family].keys(): + classifiedFonts[family][weight] = {} + if width not in classifiedFonts[family][weight].keys(): + classifiedFonts[family][weight][width] = {} + if variant not in classifiedFonts[family][weight][width].keys(): + classifiedFonts[family][weight][width][variant] = [] + classifiedFonts[family][weight][width][variant].append([postScriptName, fonthash[postScriptName]['opticalSize']]) + +# generates Berry series from list of series options +def buildSeries(weight, width): + seriesopts = unique(s2l(weight) + s2l(width)) + if l2s(seriesopts) == 'regular' or not seriesopts: + return 'm' + series = '' + for s in seriesopts: + series += optionMappings[s] + return series + +# generates Berry shape from list of shape options +def buildShape(variant): + shapeopts = unique(s2l(variant)) + if l2s(shapeopts) == 'regular' or not shapeopts: + return 'n' + shape = '' + if ('italic' in shapeopts) and ('smcp' in shapeopts): + shapeopts.remove('italic') + shapeopts.remove('smcp') + shape += 'si' + for s in shapeopts: + shape += optionMappings[s] + return shape + +# populates explodedFonts from classifiedFonts +def populateFontDataStructures(): + for family in classifiedFonts.keys(): + for weight in classifiedFonts[family].keys(): + for width in classifiedFonts[family][weight].keys(): + for variant in classifiedFonts[family][weight][width].keys(): + for font in classifiedFonts[family][weight][width][variant]: + name = font[0] + myOptionList = [] + for o in optionList: + opt = [] + for oo in o: + if not oo or oo in fonthash[name]['features']: + opt.append(oo) + if opt: + myOptionList.append(opt) + for o in breakout(myOptionList): + xfamily = [ fonthash[name]['family'] ] + xweight = weight + xwidth = width + xvariant = variant + cmdlineoptions = [] + for oo in o: + if oo in globalOptionList and oo not in fakeOptionList: + cmdlineoptions.append(oo) + if oo in newFontFamilyOptionList and oo not in fakeOptionList: + xfamily.append(oo) + if oo in weightOptionList and oo not in fakeOptionList: + xweight = l2s(s2l(weight) + [oo]) + cmdlineoptions.append(oo) + if oo in widthOptionList and oo not in fakeOptionList: + xwidth = l2s(s2l(width) + [oo]) + cmdlineoptions.append(oo) + if oo in variantOptionList and oo not in fakeOptionList: + xvariant = l2s(s2l(variant) + [oo]) + cmdlineoptions.append(oo) + xfamily = l2s(xfamily) + # print name, xfamily, xseries, xshape, cmdlineoptions + # print l2s([encoding, name] + s2l(xfamily)[1:] + cmdlineoptions), + # for f in (s2l(xfamily)[1:] + cmdlineoptions): + # print '-f%s' % f, + # print + # print xfamily, '|', xweight, '|', xwidth, '|', buildSeries(xweight, xwidth), '|', xvariant, '|', buildShape(xvariant), '|', name + series = buildSeries(xweight, xwidth) + shape = buildShape(xvariant) + optical = l2s(fonthash[name]['opticalSize']) + if xfamily not in explodedFonts.keys(): + explodedFonts[xfamily] = {} + if series not in explodedFonts[xfamily].keys(): + explodedFonts[xfamily][series] = {} + if shape not in explodedFonts[xfamily][series].keys(): + explodedFonts[xfamily][series][shape] = {} + if optical not in explodedFonts[xfamily][series][shape].keys(): + explodedFonts[xfamily][series][shape][optical] = {} + explodedFonts[xfamily][series][shape][optical]['filename'] = fonthash[name]['filename'] + explodedFonts[xfamily][series][shape][optical]['family'] = fonthash[name]['family'] + explodedFonts[xfamily][series][shape][optical]['vendor'] = fonthash[name]['vendor'] + explodedFonts[xfamily][series][shape][optical]['fontname'] = l2s([encoding, name] + s2l(xfamily)[1:] + cmdlineoptions) + explodedFonts[xfamily][series][shape][optical]['cmdlineoptions'] = '' + for f in (s2l(xfamily)[1:] + cmdlineoptions): + explodedFonts[xfamily][series][shape][optical]['cmdlineoptions'] += '-f%s ' % f + +# generates .fd files for each font family, .sty file for the typeface +# does the right thing if multiple typefaces/font families +# requires that explodedFonts is populated +def generateFDandSTY(): + styfiles = {} + for family in explodedFonts.keys(): + fontbase = s2l(family)[0] + fontoptions = s2l(family)[1:] + if fontbase in styfiles.keys(): + styfiles[fontbase].append(fontoptions) + else: + styfiles[fontbase] = [ fontoptions ] + fdfilebase = encoding.lower() + if fontbase not in berryname.keys(): + sys.stderr.write("Error: The three-letter Berry ID for font '%s' must be\n added to the 'berryname' hash. Please add it as '%s': 'xxx',\n replacing 'xxx' with the three-letter Berry ID.\n" % (fontbase, fontbase)) + sys.exit() + fontname = berryname[fontbase] + for o in fontoptions: + fontname += optionMappings[o] + fdfilebase += fontname + print "Generating", (fdfilebase + ".fd") + texmfhome = filenameInLocaldir(fdfilebase + ".fd") + print texmfhome + fddest = open(texmfhome, 'w') + print >> fddest, ("%% Autogenerated by %s on %s" % + (sys.argv[0], + datetime.date.today().strftime("%Y/%m/%d"))) + longsuffix = fontoptions + print >> fddest, ("\ProvidesFile{%s.fd}[%s %s/%s]" % + (fdfilebase, datetime.date.today().strftime("%Y/%m/%d"), + encoding, family)) + # example of scaling from Helvetica: + # \expandafter\ifx\csname Hv@scale\endcsname\relax + # \let\Hv@@scale\@empty + # \else + # \edef\Hv@@scale{s*[\csname Hv@scale\endcsname]}% + # \fi + print >> fddest, r"\expandafter\ifx\csname %s@scaled\endcsname\relax" % letterize(berryname[fontbase]) + if fontbase in scaled.keys(): + print >> fddest, r" \edef\%s@scaled{s*[%f]}%%" % (letterize(berryname[fontbase]), scaled[fontbase]) + else: + print >> fddest, r" \let\%s@scaled\@empty" % letterize(berryname[fontbase]) + print >> fddest, r"\fi" + print >> fddest, "\n\\DeclareFontFamily{%s}{%s}{}" % (encoding, fontname) + for se in explodedFonts[family].keys(): + for sh in explodedFonts[family][se].keys(): + print >> fddest, ("\\DeclareFontShape{%s}{%s}{%s}{%s}{" % + (encoding, fontname, se, sh)) + opticalkeys = explodedFonts[family][se][sh].keys() + sz = len(opticalkeys) + if sz > 1: + # sort by size range + opticalkeys.sort(lambda a,b: cmp(float(a.split('-')[0]), + float(b.split('-')[0]))) + # then lop off the lowest start point and highest end point + for op in opticalkeys: + if sz > 1: + range = op + if range == opticalkeys[0]: + range = '-' + range.split('-')[1] + elif range == opticalkeys[-1]: + range = range.split('-')[0] + '-' + print >> fddest, " <%s> " % range, + else: + print >> fddest, " <-> ", + print >> fddest, "\%s@scaled " % letterize(berryname[fontbase]), + print >> fddest, "%s" % explodedFonts[family][se][sh][op]['fontname'] + # example command line: + # otftotfm -a -e texnansx --no-create /Volumes/Adobe\ Type\ Classics/Western\ Fonts/Adobe\ Caslon\ Pro/ACaslonPro-Regular.otf -fkern -fliga LY1--AdobeCaslonPro-Regular + + installcommands.append("otftotfm --no-updmap -a %s --typeface '%s' --vendor '%s' '%s' %s %s" % + (encodings[encoding]['commandline'], + niceFontName(explodedFonts[family][se][sh][op]['family']), + explodedFonts[family][se][sh][op]['vendor'], + explodedFonts[family][se][sh][op]['filename'], + explodedFonts[family][se][sh][op]['cmdlineoptions'], + explodedFonts[family][se][sh][op]['fontname'])) + print >> fddest, "}{}" + # rest of fd part: + # - substitutes slanted with italic if slanted not available + # - substitutes italic with slanted if italic not available + # - substitutes bold extended with bold if bold extended not available + # - substitutes bold extended slanted with bxit if bxsl not available + for sh in [ ['sl', 'it'], ['it', 'sl'] ]: + # sh[0] is replacement sh, sh[1] is old sh + for se in explodedFonts[family].keys(): + if sh[0] in explodedFonts[family][se].keys() and sh[1] not in explodedFonts[family][se].keys(): + print >> fddest, ("\\DeclareFontShape{%s}{%s}{%s}{%s}{" % + (encoding, fontname, se, sh[1])) + print >> fddest, " <-> sub * %s/%s/%s" % (fontname, se, sh[0]) + print >> fddest, "}{}" + + if 'b' in explodedFonts[family].keys() and 'bx' not in explodedFonts[family].keys(): + se = 'b' + for sh in explodedFonts[family][se]: + print >> fddest, ("\\DeclareFontShape{%s}{%s}{%s}{%s}{" % + (encoding, fontname, 'bx', sh)) + print >> fddest, " <-> sub * %s/%s/%s" % (fontname, se, sh) + print >> fddest, "}{}" + + sh = 'sl' + if 'it' in explodedFonts[family][se].keys() and sh not in explodedFonts[family][se].keys(): + print >> fddest, ("\\DeclareFontShape{%s}{%s}{%s}{%s}{" % + (encoding, fontname, 'bx', sh)) + print >> fddest, " <-> sub * %s/%s/%s" % (fontname, se, 'it') + print >> fddest, "}{}" + print >> fddest, "\n\\endinput" + fddest.close() + + for fontbase in styfiles.keys(): + styoptions = styfiles[fontbase] + print styoptions + styname = niceFontName(fontbase) + filename = filenameInLocaldir(styname + '.sty') + stydest = open(filename, 'w') + print >> stydest, ("%% Autogenerated by %s on %s" % + (sys.argv[0], + datetime.date.today().strftime("%Y/%m/%d"))) + print >> stydest, r"\NeedsTeXFormat{LaTeX2e}" + print >> stydest, (r"\ProvidesPackage{%s}[%s %s]" % + (styname, datetime.date.today().strftime("%Y/%m/%d"), + fontbase)) + print >> stydest, r"\RequirePackage[%s]{fontenc}" % encoding + print >> stydest, r"\RequirePackage{textcomp}" + print >> stydest, r"\RequirePackage{xkeyval}" + print >> stydest, r"\RequirePackage{nfssext}" + seensups = 0 + seensinf = 0 + for o in styoptions: + if 'sups' in o: + seensups = 1 + if 'sinf' in o: + seensinf = 1 + if seensups: + print >> stydest, r"\def\@makefnmark{\hbox{\sustyle\@thefnmark}}" + if seensinf: + print >> stydest, r"\providecommand*{\textfrac}[2]{%" + print >> stydest, r" \textsu{#1}%" + print >> stydest, r" \textfractionsolidus" + print >> stydest, r" \textin{#2}}" + str = r"\define@key{%s}{scaled}" % letterize(berryname[fontbase]) + if berryname[fontbase] in scaled.keys(): + str += "[%s]" % scaled[berryname[fontbase]] + else: + str += "[1.0]" + str += "{\def\%s@scaled{s*[#1]}}" % letterize(berryname[fontbase]) + print >> stydest, str + str = r"\define@key{%s}{family}[rm]{\def\%s@family{#1}}" % (letterize(berryname[fontbase]), letterize(berryname[fontbase])) + print >> stydest, str + print >> stydest, r"\DeclareOption*{%" + print >> stydest, r" \begingroup" + print >> stydest, r" \edef\x{\endgroup" + print >> stydest, r" \noexpand\setkeys{%s}{\CurrentOption}}%%" % letterize(berryname[fontbase]) + print >> stydest, r" \x}" + + onumprinted = False + lnumprinted = False + # change this if you don't want oldstyle to be the default + oldstyleisdefault = True + styledefaultprinted = False + numoptions = '' + for o in styoptions: + if not onumprinted and 'onum' in o: + if oldstyleisdefault == True: + print >> stydest, (r"\newcommand*{\%s@style}{%s}" % + (letterize(berryname[fontbase]), + optionMappings['onum'])) + styledefaultprinted = True + numoptions += r"\DeclareOption{oldstyle}{%" + "\n" + numoptions += (r" \renewcommand*{\%s@style}{%s}%%" % + (letterize(berryname[fontbase]), optionMappings['onum'])) + numoptions += "\n}\n" + onumprinted = True + if not lnumprinted and 'lnum' in o: + if oldstyleisdefault == False: + print >> stydest, (r"\newcommand*{\%s@style}{%s}" % + (letterize(berryname[fontbase]), + optionMappings['lnum'])) + styledefaultprinted = True + numoptions += r"\DeclareOption{lining}{%" + "\n" + numoptions += (r" \renewcommand*{\%s@style}{%s}%%" % + (letterize(berryname[fontbase]), optionMappings['lnum'])) + numoptions += "\n}\n" + lnumprinted = True + if not lnumprinted and not onumprinted: + print >> stydest, (r"\newcommand*{\%s@style}{%s}" % + (letterize(berryname[fontbase]), + optionMappings['fakenum'])) + print >> stydest, numoptions + + print >> stydest, (r"\newcommand*{\%s@default}{%%" % + letterize(berryname[fontbase])) + print >> stydest, (r" \renewcommand*{\rmdefault}{%s\%s@style}%%" % + (berryname[fontbase], letterize(berryname[fontbase]))) + print >> stydest, "}\n" + + for o in ['rm', 'sf', 'tt']: + print >> stydest, (r"\DeclareOption{%s}{%%" % o) + print >> stydest, (r" \renewcommand*{\%s@default}{}%%" % + letterize(berryname[fontbase])) + print >> stydest, (r" \renewcommand*{\%sdefault}{%s\%s@style}%%" % + (o, berryname[fontbase], + letterize(berryname[fontbase]))) + print >> stydest, "}\n" + + print >> stydest, r"\ProcessOptions*" + print >> stydest, r"\%s@default" % letterize(berryname[fontbase]) + print >> stydest, r"\endinput" + stydest.close() + +# given installcommands list, runs all commands in that list +def executeCommands(): + dryrun = False # change to true to show commands but not run them + installcommands[-1] = installcommands[-1].replace(" --no-updmap", "") + for cmd in installcommands: + print cmd + if not dryrun: + os.system(cmd) + # typically these two need to be called at the end to clean up + if not dryrun: + os.system("texhash") + os.system("updmap --syncwithtrees") + +# break out all possibilities from a list +# breakout(['a', 'b'], ['c', '']) => [['a', 'c'], ['a'], ['b', 'c'], ['b']] +# Filters empty values out of output lists. +# breakout by Doug Zongker (thanks Doug!) +def breakout(lyst): + if not lyst: + yield [] + else: + for i in lyst[0]: + if i: + for j in breakout(lyst[1:]): + yield [i] + j + else: + for j in breakout(lyst[1:]): + yield j + +def filenameInLocaldir(str): + return (os.popen("kpsewhich -expand-path='$TEXMFHOME'", + 'r').readline()[0:-1] + + localfontsdir + str) + +removeproRE = re.compile(" pro") +removestdRE = re.compile(" std") +def niceFontName(str): + return removestdRE.sub('', removeproRE.sub('', str.lower())).replace(' ', '') + +# stable +def unique(s): + u = {} + ss = [] + for x in s: + if x not in u.keys(): + ss.append(x) + u[x] = 1 + return ss + +####################################### +# END UTILITY FUNCTIONS +####################################### + +####################################### +# BEGIN GLOBAL VARIABLES +# DON'T TOUCH THESE +####################################### + +# contains otftotfm commands to send to the os +installcommands = [] + +# indexed by font file name, contains (flat) info about font file: +# filename, family, subfamily, vendor, opticalSize, series, shape, features +fonthash = {} + +# indexed by [family][series][shape], contains list of optical variants +# for each [family][series][shape] +classifiedFonts = {} + +# indexed by [family][series][shape][optical], contains info for each optical +# variant: +# filename, vendor, fontname, cmdlineoptions +explodedFonts = {} + +####################################### +# END GLOBAL VARIABLES +####################################### + +####################################### +# BEGIN PROGRAM +####################################### + +checkOTFInfoVersion() +for font in generateFontfiles(sys.argv[1:]): + addToFonthash(font) +populateFontDataStructures() +generateFDandSTY() +executeCommands() + +####################################### +# END PROGRAM +####################################### |