mkfontdb.py :  » GUI » Sketch » skencil-0.6.17 » Tools » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » Sketch 
Sketch » skencil 0.6.17 » Tools » mkfontdb.py
#! /usr/bin/env python

# mkfontdb.py - Generate 'font databases' from Type 1 AFM-files.
# Copyright (C) 1997, 1998, 1999, 2000 by Bernhard Herzog
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# Script to extract information from afm-files and to create `font
# databases' for Sketch and X.
#
# X manages a font database (which basically maps font file names to X
# font names) in fonts.dir files in each directory in the font path. For
# Type1 fonts (usually in the Type1 directory) this file is a copy of
# fonts.scale in the same directory. The fonts.dir files are generated
# by mkfontdir(1) while the fonts.scale file is meant to be edited by
# the system administrator. This script can create the entries for that
# file from the afm files.
#
# An X font name (X Logical Font Description, XLFD) for scalable fonts
# has the form:
#
# -foundry-family-weight-slant-setwidth--0-0-0-0-spacing-0-char-set
#
# where the fields have the following meanings (simplifying a bit):
#
# foundry:  The manufacturer of the font. (e.g. adobe)
#
# family:  The font family. (e.g. times)
#
# weight:  The `boldness'. This can not only be medium and bold
#     but also light, demibold or black, for instance.
#     Synonyms for medium are normal or regular.
#
# slant:  Either r (roman), i (italic) or o (oblique).
#
# setwidth:  A name for average width. Often just normal.
#     Other setwidths are for instance narrow or extended.
#
# spacing:  Whether the font is monospace (m) (i.e. all characters
#     have the same width) or proportional (p)
#
# char-set:  How character codes are mapped to glyphs.
#     Often iso8859-1. Another example is adobe-fontspecific
#
# Part of this information (family, weight and spacing) can be read
# directly from the afm file. The other fields have to be deduced from
# the information available. This script uses the following strategy:
#
#
# foundry
#
# The foundry is often mentioned in a copyright note. Try to find the
# name of a known foundry in the note and use that foundry. The known
# foundries are listed in the variable foundries.
#
# family, weight, spacing
#
# This information is listed directly in the afm file. Just translate
# IsFixedPitch to m or p. There can be some problems with the family
# name, though. See below.
#
# slant
#
# The afm file has a field ItalicAngle. If this angle is 0, we assume a
# roman font. Otherwise it can be italic or oblique. To decide which to
# use, we search the FullName for the word `Oblique'. If we find it we
# assume oblique, italic otherwise.
#
# setwidth
#
# This is not mentioned in the afm file. It is usually available in the
# FullName, so we search it for common setwidth names. If we find
# something we use that, otherwise we assume normal. The known setwidths
# are listed in the variable setwidths. They are possibly preceded by a
# modifier from the list modifiers.
#
# char-set
#
# The afm file has a field EncodingScheme. If this is FontSpecific, we
# use adobe-fontspecific for the XLFD, iso8859-1 otherwise.
#
# The XFree86 servers I have used so far don't seem to accept char-sets
# like e.g. bitstream-fontspecific so we have to use adobe here.
#
# Problems
#
# Many of the fields in an afm-file are optional. This script tries to
# use reasonable default values. Fortunately, many of the optional
# fields are provided by the afm-files I tested this with.
#
# Another problem when generating x font names is that some fonts have
# somewhat unusual attribute names. The bitstream font
# ShelleyAndanteBT-Regular, for instance, is the variant Andante from
# the Shelley family. Other variants of this family are Allegro and
# Volante. How do we fit this into x's scheme where variants involve
# just weight, slant and setwidth?
#
# This script tries to form the x family name by removing those parts of
# the full font name that describe weight, slant and setwidth.
# Whatever's left is taken as the family name if it is longer than the
# family name indicated in the afm file. The family name of
# ShelleyAndanteBT-Regular becomes "Shelley Andante", for instance.
#
#
# Sketch
#
# Sketch's font directories (.sfd files) contain similar information,
# which has to be extracted from the afm files. This script can create
# sfd files.
#

__version__ = "1.3"

import sys, os

import glob, string, re
from string import atof,lower,find,split,join,strip,translate


# Most of these lists are far from complete.
foundries = ['adobe', 'bitstream', 'urw', 'softmaker', 'letraset', 'corel',
             'monotype']
slants = ['Italic', 'Oblique', 'Roman']
weights = ['Medium', 'Normal', 'Bold', 'Black', 'Light']
setwidths = ['Extended', 'Condensed', 'Narrow', 'Compressed']
modifiers = ['Ultra', 'Extra', 'Semi', 'Demi']

# create a dict with the words that sould be removed from the full name
# to get the x family name. See 'Problems' above.
remove_words = {}
for word in setwidths + weights + slants + modifiers:
    remove_words[word] = 1


rx_oblique = re.compile('Oblique')
rx_setwidth = re.compile('((' + join(modifiers, '|') + ')[ \t]+)?'
          '(' + join(setwidths, '|') + ')')

trans_minus = string.maketrans('-', ' ')

_alphanum = string.letters + string.digits + ' '
def makealnum(s):
    return filter(lambda c: c in _alphanum, s)

def _str(val):
    return strip(val)

def _bool(s):
    return strip(s) == 'true'

converters = {
    'FontName':    _str,
    'FullName':    _str,
    'FamilyName':  _str,
    'Weight':    _str,
    'EncodingScheme':  _str,
    'Comment':    _str,
    'Notice':    _str,
    'ItalicAngle':  atof,
    'IsFixedPitch':  _bool,
    'StartCharMetrics':  None,
    'EndFontMetrics':  None
}

class FontInfo:
    
    def __init__(self, filename):
        self.filename = filename
        self.basename = os.path.splitext(os.path.split(filename)[1])[0]
        self.foundry = default_foundry
        self.family = ''
        self.x_family = ''
        self.weight = 'normal'
        self.slant = 'r'
        self.spacing = 'm'
        self.encoding = 'iso8859-1'
        self.fontname = ''


debug_print_fullname = 0
verbosity = 0
guess_family_from_fullname = 1

# the default foundry used if it can't be determined from the afm file.
# can be set via the command line.
default_foundry = 'adobe'


def read_afm_file(afm_file):
    # read the afm file AFM_FILE and return a tuple containing the info needed
    # to make a fonts.scale or sfd entry.
    file = open(afm_file)

    info = FontInfo(afm_file)
    slant = 0
    fullname = ''

    lines = file.readlines()
    while lines:
  line = lines[0]
  del lines[0]
        temp = split(line, None, 1)
        if not temp:
            continue
        if len(temp) == 1:
            key = temp[0]
            value = ''
        else:
            key, value = temp
  try:
      action = converters[key]
  except KeyError:
      continue
  if action:
      value = action(value)
      if key == 'FamilyName':
    info.family = value
      elif key == 'FontName':
    info.fontname = value
      elif key == 'FullName':
    fullname = value
    if debug_print_fullname:
        print fullname,
      elif key == 'Weight':
    info.weight = lower(value)
    remove_words[value] = 1
      elif key == 'ItalicAngle':
                slant = value != 0.0
      elif key == 'IsFixedPitch':
                if value:
                    info.spacing = 'm'
                else:
                    info.spacing = 'p'
      elif key == 'EncodingScheme':
                if value == 'FontSpecific':
        info.encoding = 'adobe-fontspecific'
    else:
                    # this should be probably configurable
        info.encoding = 'iso8859-1'
      elif key == 'Comment' or key == 'Notice':
    value = lower(value)
                if value[:9] == 'copyright':
        for name in foundries:
      if find(value, name) != -1:
          info.foundry = name
  else:
      # EndFontMetrics or StartCharMetrics
      break

    file.close()

    # translate the slant to roman, italic or oblique
    if slant:
  if rx_oblique.search(fullname):
      info.slant = 'o'
  else:
      info.slant = 'i'

    if info.weight == 'roman':
  info.weight = 'medium'

    # try to find the setwidth
    match = rx_setwidth.search(fullname)
    if match:
        info.x_setwidth = lower(match.group(0))
    else:
  info.x_setwidth = 'normal'

    # The font attributes displayed in Sketch's font dialog is
    # everything in the full name of the font apart of the family name
    if info.family == fullname[:len(info.family)]:
        info.sfd_attrs = strip(translate(fullname[len(info.family):],
                                         trans_minus))
        if not info.sfd_attrs:
            info.sfd_attrs = 'Roman'
    else:
        sys.stderr.write("%s: fullname doesn't start with family\n"
                         % afm_file)
        info.sfd_attrs = 'Roman'

    # The family name used in the X font names shouldn't contain any
    # funny characters, especially no '-'.
    # See 'Problems' above for this special case
    parts = split(translate(fullname, trans_minus))
    parts = filter(lambda n: not remove_words.has_key(n), parts)
    fullname = join(parts)
    if guess_family_from_fullname and len(fullname) >  len(info.family):
        info.x_family = makealnum(fullname)
    else:
        info.x_family = makealnum(info.family)
    
    info.x_fontname_start = ('-%(foundry)s-%(x_family)s-%(weight)s-'
                             '%(slant)s-%(x_setwidth)s') % info.__dict__
    return info


def write_sfd_line(file, info):
    file.write('%(fontname)s,%(family)s,%(sfd_attrs)s,%(x_fontname_start)s,'
               '%(encoding)s,%(basename)s\n' % info.__dict__)

def write_fonts_scale_header(file, files):
    file.write("%d\n" % len(files)) 

def write_fonts_scale_line(file, info):
    fontname = '--0-0-0-0-%(spacing)s-0-%(encoding)s' % info.__dict__
    pfb = info.basename + '.pfb'
    file.write('%s %s%s\n' % (pfb, info.x_fontname_start, fontname))

def write_fontmap_line(file, info):
    file.write('/%(fontname)s\t(%(basename)s.pfb)\t;\n' % info.__dict__)

def process_files(dir, files, handlers):
    for file in files:
        if verbosity > 0:
            print file
        info = read_afm_file(os.path.join(dir, file))
        for file, write_line in handlers:
            write_line(file, info)
  

def print_debug_infos(dir, files):
    global debug_print_fullname
    debug_print_fullname = 1
    
    for file in files:
  print file,
  info = read_afm_file(os.path.join(dir, file))
  print ';\t', info.fontname, info.family, info.weight, \
              info.slant, info.x_setwidth
    

SFD = 'sfd'
FONTSSCALE = 'fonts.scale'
FONTMAP = 'Fontmap'

format_handlers = {
    SFD: (write_sfd_line, None),
    FONTSSCALE: (write_fonts_scale_line, write_fonts_scale_header),
    FONTMAP: (write_fontmap_line, None)
    }
format_filenames = {
    SFD: 'std.sfd',
    FONTSSCALE: 'fonts.scale', 
    FONTMAP: 'Fontmap',
    }

def main():

    # set defaults of options
    output_formats = []
    outfilename = ''
    dir = '.'
    debug = 0
    
    import getopt
    try:
  opts, args = getopt.getopt(sys.argv[1:], 'df:gho:svx')
    except getopt.error:
  print_usage()
  return

    for optchar, value in opts:
  if optchar == '-h':
      print_usage()
      return
  elif optchar == '-d':
      debug = 1
  elif optchar == '-v':
      global verbosity
      verbosity = 1
  elif optchar == '-s':
            output_formats.append(SFD)
        elif optchar == '-x':
            output_formats.append(FONTSSCALE)
        elif optchar == '-g':
            output_formats.append(FONTMAP)
        elif optchar == '-o':
      outfilename = value
        elif optchar == '-f':
            global default_foundry
            default_foundry = lower(value)

    if args and len(args) > 1:
  files = args
    else:
  if args:
      dir = args[0]
  if os.path.isdir(dir):
            files = glob.glob(os.path.join(dir, '*.afm'))
  else:
      files = args
            dir = '.'

    if debug:
  print_debug_infos(dir, files)
    else:
        if len(output_formats) == 0:
            output_formats = [FONTSSCALE]
        if len(output_formats) == 1 and outfilename:
            format_filenames[output_formats[0]] = outfilename

        for i in range(len(output_formats)):
            file = open(format_filenames[output_formats[i]], 'w')
            write_line, write_header = format_handlers[output_formats[i]]
            if write_header:
                write_header(file, files)
            output_formats[i] = (file, write_line)

        process_files(dir, files, handlers = output_formats)


            
usage_msg = """\
usage:
%(scriptname)s [-h] [-d] [-f] [-v] [-s] [-x] [-o file] [dir | file1 file2...]

Read afm files for Type 1 fonts and produce one or more `font database'
files. Currently three formats are supported: X11 fonts.scale (the
default), Ghostscript Fontmap and Sketch font directories (.sfd-files).
The program can generate files for several formats at once.

Arguments can be either a directory or .afm-files. If no arguments other
than options are given, assume '.' as argument.

Options:
  -h            print this help message (and do nothing else)
  -d            (debug) print some info on the files. Do nothing else.

  -x            Create a fonts.scale file for X
                Default filename is %(default_x)s
  -s            Create a Sketch font directory.
                Default filename is %(default_sfd)s
  -g            Create a Fontmap file for ghostscript
                Default filename is %(default_gs)s
  -o <file>     the name of the output file. This option is only valid
                if only one output format is chosen.

  -f <foundry>  Foundry used if it can't be determined from the afm file.
                Default: %(default_foundry)s                

  -v            verbose. print each filename as it is read

If neither -x nor -s nor -g is given, the output format defaults to -x.
If more than one of -x, -s or -g is given, the -o option is ignored.
"""
  
def print_usage():
    scriptname = os.path.basename(sys.argv[0])
    print usage_msg % {'scriptname': scriptname,
                       'default_sfd': format_filenames[SFD],
                       'default_x': format_filenames[FONTSSCALE],
                       'default_gs': format_filenames[FONTMAP],
                       'default_foundry': default_foundry}

if __name__ == '__main__':
    main()


www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.