cffLib.py :  » GUI » TTX-FontTools » fonttools-2.3 » Lib » fontTools » 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 » TTX FontTools 
TTX FontTools » fonttools 2.3 » Lib » fontTools » cffLib.py
"""cffLib.py -- read/write tools for Adobe CFF fonts."""

#
# $Id: cffLib.py,v 1.34 2008-03-07 19:56:17 jvr Exp $
#

import struct, sstruct
import string
from fontTools.misc import psCharStrings
from fontTools.misc.textTools import safeEval

DEBUG = 0


cffHeaderFormat = """
  major:   B
  minor:   B
  hdrSize: B
  offSize: B
"""

class CFFFontSet:
  
  def __init__(self):
    pass
  
  def decompile(self, file, otFont):
    sstruct.unpack(cffHeaderFormat, file.read(4), self)
    assert self.major == 1 and self.minor == 0, \
        "unknown CFF format: %d.%d" % (self.major, self.minor)
    
    file.seek(self.hdrSize)
    self.fontNames = list(Index(file))
    self.topDictIndex = TopDictIndex(file)
    self.strings = IndexedStrings(file)
    self.GlobalSubrs = GlobalSubrsIndex(file)
    self.topDictIndex.strings = self.strings
    self.topDictIndex.GlobalSubrs = self.GlobalSubrs
  
  def __len__(self):
    return len(self.fontNames)
  
  def keys(self):
    return list(self.fontNames)
  
  def values(self):
    return self.topDictIndex
  
  def __getitem__(self, name):
    try:
      index = self.fontNames.index(name)
    except ValueError:
      raise KeyError, name
    return self.topDictIndex[index]
  
  def compile(self, file, otFont):
    strings = IndexedStrings()
    writer = CFFWriter()
    writer.add(sstruct.pack(cffHeaderFormat, self))
    fontNames = Index()
    for name in self.fontNames:
      fontNames.append(name)
    writer.add(fontNames.getCompiler(strings, None))
    topCompiler = self.topDictIndex.getCompiler(strings, None)
    writer.add(topCompiler)
    writer.add(strings.getCompiler())
    writer.add(self.GlobalSubrs.getCompiler(strings, None))
    
    for topDict in self.topDictIndex:
      if not hasattr(topDict, "charset") or topDict.charset is None:
        charset = otFont.getGlyphOrder()
        topDict.charset = charset
    
    for child in topCompiler.getChildren(strings):
      writer.add(child)
    
    writer.toFile(file)
  
  def toXML(self, xmlWriter, progress=None):
    xmlWriter.newline()
    for fontName in self.fontNames:
      xmlWriter.begintag("CFFFont", name=fontName)
      xmlWriter.newline()
      font = self[fontName]
      font.toXML(xmlWriter, progress)
      xmlWriter.endtag("CFFFont")
      xmlWriter.newline()
    xmlWriter.newline()
    xmlWriter.begintag("GlobalSubrs")
    xmlWriter.newline()
    self.GlobalSubrs.toXML(xmlWriter, progress)
    xmlWriter.endtag("GlobalSubrs")
    xmlWriter.newline()
    xmlWriter.newline()
  
  def fromXML(self, (name, attrs, content)):
    if not hasattr(self, "GlobalSubrs"):
      self.GlobalSubrs = GlobalSubrsIndex()
      self.major = 1
      self.minor = 0
      self.hdrSize = 4
      self.offSize = 4  # XXX ??
    if name == "CFFFont":
      if not hasattr(self, "fontNames"):
        self.fontNames = []
        self.topDictIndex = TopDictIndex()
      fontName = attrs["name"]
      topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
      topDict.charset = None  # gets filled in later
      self.fontNames.append(fontName)
      self.topDictIndex.append(topDict)
      for element in content:
        if isinstance(element, basestring):
          continue
        topDict.fromXML(element)
    elif name == "GlobalSubrs":
      for element in content:
        if isinstance(element, basestring):
          continue
        name, attrs, content = element
        subr = psCharStrings.T2CharString()
        subr.fromXML((name, attrs, content))
        self.GlobalSubrs.append(subr)


class CFFWriter:
  
  def __init__(self):
    self.data = []
  
  def add(self, table):
    self.data.append(table)
  
  def toFile(self, file):
    lastPosList = None
    count = 1
    while 1:
      if DEBUG:
        print "CFFWriter.toFile() iteration:", count
      count = count + 1
      pos = 0
      posList = [pos]
      for item in self.data:
        if hasattr(item, "getDataLength"):
          endPos = pos + item.getDataLength()
        else:
          endPos = pos + len(item)
        if hasattr(item, "setPos"):
          item.setPos(pos, endPos)
        pos = endPos
        posList.append(pos)
      if posList == lastPosList:
        break
      lastPosList = posList
    if DEBUG:
      print "CFFWriter.toFile() writing to file."
    begin = file.tell()
    posList = [0]
    for item in self.data:
      if hasattr(item, "toFile"):
        item.toFile(file)
      else:
        file.write(item)
      posList.append(file.tell() - begin)
    assert posList == lastPosList


def calcOffSize(largestOffset):
  if largestOffset < 0x100:
    offSize = 1
  elif largestOffset < 0x10000:
    offSize = 2
  elif largestOffset < 0x1000000:
    offSize = 3
  else:
    offSize = 4
  return offSize


class IndexCompiler:
  
  def __init__(self, items, strings, parent):
    self.items = self.getItems(items, strings)
    self.parent = parent
  
  def getItems(self, items, strings):
    return items
  
  def getOffsets(self):
    pos = 1
    offsets = [pos]
    for item in self.items:
      if hasattr(item, "getDataLength"):
        pos = pos + item.getDataLength()
      else:
        pos = pos + len(item)
      offsets.append(pos)
    return offsets
  
  def getDataLength(self):
    lastOffset = self.getOffsets()[-1]
    offSize = calcOffSize(lastOffset)
    dataLength = (
      2 +                                # count
      1 +                                # offSize
      (len(self.items) + 1) * offSize +  # the offsets
      lastOffset - 1                     # size of object data
    )
    return dataLength
  
  def toFile(self, file):
    offsets = self.getOffsets()
    writeCard16(file, len(self.items))
    offSize = calcOffSize(offsets[-1])
    writeCard8(file, offSize)
    offSize = -offSize
    pack = struct.pack
    for offset in offsets:
      binOffset = pack(">l", offset)[offSize:]
      assert len(binOffset) == -offSize
      file.write(binOffset)
    for item in self.items:
      if hasattr(item, "toFile"):
        item.toFile(file)
      else:
        file.write(item)


class IndexedStringsCompiler(IndexCompiler):
  
  def getItems(self, items, strings):
    return items.strings


class TopDictIndexCompiler(IndexCompiler):
  
  def getItems(self, items, strings):
    out = []
    for item in items:
      out.append(item.getCompiler(strings, self))
    return out
  
  def getChildren(self, strings):
    children = []
    for topDict in self.items:
      children.extend(topDict.getChildren(strings))
    return children


class FDArrayIndexCompiler(IndexCompiler):
  
  def getItems(self, items, strings):
    out = []
    for item in items:
      out.append(item.getCompiler(strings, self))
    return out
  
  def getChildren(self, strings):
    children = []
    for fontDict in self.items:
      children.extend(fontDict.getChildren(strings))
    return children

  def toFile(self, file):
    offsets = self.getOffsets()
    writeCard16(file, len(self.items))
    offSize = calcOffSize(offsets[-1])
    writeCard8(file, offSize)
    offSize = -offSize
    pack = struct.pack
    for offset in offsets:
      binOffset = pack(">l", offset)[offSize:]
      assert len(binOffset) == -offSize
      file.write(binOffset)
    for item in self.items:
      if hasattr(item, "toFile"):
        item.toFile(file)
      else:
        file.write(item)

  def setPos(self, pos, endPos):
    self.parent.rawDict["FDArray"] = pos


class GlobalSubrsCompiler(IndexCompiler):
  def getItems(self, items, strings):
    out = []
    for cs in items:
      cs.compile()
      out.append(cs.bytecode)
    return out

class SubrsCompiler(GlobalSubrsCompiler):
  def setPos(self, pos, endPos):
    offset = pos - self.parent.pos
    self.parent.rawDict["Subrs"] = offset

class CharStringsCompiler(GlobalSubrsCompiler):
  def setPos(self, pos, endPos):
    self.parent.rawDict["CharStrings"] = pos


class Index:
  
  """This class represents what the CFF spec calls an INDEX."""
  
  compilerClass = IndexCompiler
  
  def __init__(self, file=None):
    name = self.__class__.__name__
    if file is None:
      self.items = []
      return
    if DEBUG:
      print "loading %s at %s" % (name, file.tell())
    self.file = file
    count = readCard16(file)
    self.count = count
    self.items = [None] * count
    if count == 0:
      self.items = []
      return
    offSize = readCard8(file)
    if DEBUG:
      print "    index count: %s offSize: %s" % (count, offSize)
    assert offSize <= 4, "offSize too large: %s" % offSize
    self.offsets = offsets = []
    pad = '\0' * (4 - offSize)
    for index in range(count+1):
      chunk = file.read(offSize)
      chunk = pad + chunk
      offset, = struct.unpack(">L", chunk)
      offsets.append(int(offset))
    self.offsetBase = file.tell() - 1
    file.seek(self.offsetBase + offsets[-1])  # pretend we've read the whole lot
    if DEBUG:
      print "    end of %s at %s" % (name, file.tell())
  
  def __len__(self):
    return len(self.items)
  
  def __getitem__(self, index):
    item = self.items[index]
    if item is not None:
      return item
    offset = self.offsets[index] + self.offsetBase
    size = self.offsets[index+1] - self.offsets[index]
    file = self.file
    file.seek(offset)
    data = file.read(size)
    assert len(data) == size
    item = self.produceItem(index, data, file, offset, size)
    self.items[index] = item
    return item
  
  def produceItem(self, index, data, file, offset, size):
    return data
  
  def append(self, item):
    self.items.append(item)
  
  def getCompiler(self, strings, parent):
    return self.compilerClass(self, strings, parent)


class GlobalSubrsIndex(Index):
  
  compilerClass = GlobalSubrsCompiler
  
  def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
    Index.__init__(self, file)
    self.globalSubrs = globalSubrs
    self.private = private
    if fdSelect:
      self.fdSelect = fdSelect
    if fdArray:
      self.fdArray = fdArray
  
  def produceItem(self, index, data, file, offset, size):
    if self.private is not None:
      private = self.private
    elif hasattr(self, 'fdArray') and self.fdArray is not None:
      private = self.fdArray[self.fdSelect[index]].Private
    else:
      private = None
    return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
  
  def toXML(self, xmlWriter, progress):
    xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
    xmlWriter.newline()
    for i in range(len(self)):
      subr = self[i]
      if subr.needsDecompilation():
        xmlWriter.begintag("CharString", index=i, raw=1)
      else:
        xmlWriter.begintag("CharString", index=i)
      xmlWriter.newline()
      subr.toXML(xmlWriter)
      xmlWriter.endtag("CharString")
      xmlWriter.newline()
  
  def fromXML(self, (name, attrs, content)):
    if name <> "CharString":
      return
    subr = psCharStrings.T2CharString()
    subr.fromXML((name, attrs, content))
    self.append(subr)
  
  def getItemAndSelector(self, index):
    sel = None
    if hasattr(self, 'fdSelect'):
      sel = self.fdSelect[index]
    return self[index], sel


class SubrsIndex(GlobalSubrsIndex):
  compilerClass = SubrsCompiler


class TopDictIndex(Index):
  
  compilerClass = TopDictIndexCompiler
  
  def produceItem(self, index, data, file, offset, size):
    top = TopDict(self.strings, file, offset, self.GlobalSubrs)
    top.decompile(data)
    return top
  
  def toXML(self, xmlWriter, progress):
    for i in range(len(self)):
      xmlWriter.begintag("FontDict", index=i)
      xmlWriter.newline()
      self[i].toXML(xmlWriter, progress)
      xmlWriter.endtag("FontDict")
      xmlWriter.newline()


class FDArrayIndex(TopDictIndex):
  
  compilerClass = FDArrayIndexCompiler

  def fromXML(self, (name, attrs, content)):
    if name <> "FontDict":
      return
    fontDict = FontDict()
    for element in content:
      if isinstance(element, basestring):
        continue
      fontDict.fromXML(element)
    self.append(fontDict)


class  FDSelect:
  def __init__(self, file = None, numGlyphs = None, format=None):
    if file:
      # read data in from file
      self.format = readCard8(file)
      if self.format == 0:
        from array import array
        self.gidArray = array("B", file.read(numGlyphs)).tolist()
      elif self.format == 3:
        gidArray = [None] * numGlyphs
        nRanges = readCard16(file)
        prev = None
        for i in range(nRanges):
          first = readCard16(file)
          if prev is not None:
            for glyphID in range(prev, first):
              gidArray[glyphID] = fd
          prev = first
          fd = readCard8(file)
        if prev is not None:
          first = readCard16(file)
          for glyphID in range(prev, first):
            gidArray[glyphID] = fd
        self.gidArray = gidArray
      else:
        assert 0, "unsupported FDSelect format: %s" % format
    else:
      # reading from XML. Make empty gidArray,, and leave format as passed in.
      # format == None will result in the smallest representation being used.
      self.format = format
      self.gidArray = []


  def __len__(self):
    return len(self.gidArray)
  
  def __getitem__(self, index):
    return self.gidArray[index]
  
  def __setitem__(self, index, fdSelectValue):
    self.gidArray[index] = fdSelectValue

  def append(self, fdSelectValue):
    self.gidArray.append(fdSelectValue)
  

class CharStrings:
  
  def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
    if file is not None:
      self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
      self.charStrings = charStrings = {}
      for i in range(len(charset)):
        charStrings[charset[i]] = i
      self.charStringsAreIndexed = 1
    else:
      self.charStrings = {}
      self.charStringsAreIndexed = 0
      self.globalSubrs = globalSubrs
      self.private = private
      if fdSelect != None:
        self.fdSelect = fdSelect
      if fdArray!= None:
        self.fdArray = fdArray
  
  def keys(self):
    return self.charStrings.keys()
  
  def values(self):
    if self.charStringsAreIndexed:
      return self.charStringsIndex
    else:
      return self.charStrings.values()
  
  def has_key(self, name):
    return self.charStrings.has_key(name)
  
  def __len__(self):
    return len(self.charStrings)
  
  def __getitem__(self, name):
    charString = self.charStrings[name]
    if self.charStringsAreIndexed:
      charString = self.charStringsIndex[charString]
    return charString
  
  def __setitem__(self, name, charString):
    if self.charStringsAreIndexed:
      index = self.charStrings[name]
      self.charStringsIndex[index] = charString
    else:
      self.charStrings[name] = charString
  
  def getItemAndSelector(self, name):
    if self.charStringsAreIndexed:
      index = self.charStrings[name]
      return self.charStringsIndex.getItemAndSelector(index)
    else:
      if hasattr(self, 'fdSelect'):
        sel = self.fdSelect[index]
      else:
        raise KeyError("fdSelect array not yet defined.")
      return self.charStrings[name], sel
  
  def toXML(self, xmlWriter, progress):
    names = self.keys()
    names.sort()
    i = 0
    step = 10
    numGlyphs = len(names)
    for name in names:
      charStr, fdSelectIndex = self.getItemAndSelector(name)
      if charStr.needsDecompilation():
        raw = [("raw", 1)]
      else:
        raw = []
      if fdSelectIndex is None:
        xmlWriter.begintag("CharString", [('name', name)] + raw)
      else:
        xmlWriter.begintag("CharString",
            [('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
      xmlWriter.newline()
      charStr.toXML(xmlWriter)
      xmlWriter.endtag("CharString")
      xmlWriter.newline()
      if not i % step and progress is not None:
        progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
        progress.increment(step / float(numGlyphs))
      i = i + 1
  
  def fromXML(self, (name, attrs, content)):
    for element in content:
      if isinstance(element, basestring):
        continue
      name, attrs, content = element
      if name <> "CharString":
        continue
      fdID = -1
      if hasattr(self, "fdArray"):
        fdID = safeEval(attrs["fdSelectIndex"])
        private = self.fdArray[fdID].Private
      else:
        private = self.private
        
      glyphName = attrs["name"]
      charString = psCharStrings.T2CharString(
          private=private,
          globalSubrs=self.globalSubrs)
      charString.fromXML((name, attrs, content))
      if fdID >= 0:
        charString.fdSelectIndex = fdID
      self[glyphName] = charString


def readCard8(file):
  return ord(file.read(1))

def readCard16(file):
  value, = struct.unpack(">H", file.read(2))
  return value

def writeCard8(file, value):
  file.write(chr(value))

def writeCard16(file, value):
  file.write(struct.pack(">H", value))

def packCard8(value):
  return chr(value)

def packCard16(value):
  return struct.pack(">H", value)

def buildOperatorDict(table):
  d = {}
  for op, name, arg, default, conv in table:
    d[op] = (name, arg)
  return d

def buildOpcodeDict(table):
  d = {}
  for op, name, arg, default, conv in table:
    if isinstance(op, tuple):
      op = chr(op[0]) + chr(op[1])
    else:
      op = chr(op)
    d[name] = (op, arg)
  return d

def buildOrder(table):
  l = []
  for op, name, arg, default, conv in table:
    l.append(name)
  return l

def buildDefaults(table):
  d = {}
  for op, name, arg, default, conv in table:
    if default is not None:
      d[name] = default
  return d

def buildConverters(table):
  d = {}
  for op, name, arg, default, conv in table:
    d[name] = conv
  return d


class SimpleConverter:
  def read(self, parent, value):
    return value
  def write(self, parent, value):
    return value
  def xmlWrite(self, xmlWriter, name, value, progress):
    xmlWriter.simpletag(name, value=value)
    xmlWriter.newline()
  def xmlRead(self, (name, attrs, content), parent):
    return attrs["value"]

class Latin1Converter(SimpleConverter):
  def xmlRead(self, (name, attrs, content), parent):
    s = unicode(attrs["value"], "utf-8")
    return s.encode("latin-1")


def parseNum(s):
  try:
    value = int(s)
  except:
    value = float(s)
  return value

class NumberConverter(SimpleConverter):
  def xmlRead(self, (name, attrs, content), parent):
    return parseNum(attrs["value"])

class ArrayConverter(SimpleConverter):
  def xmlWrite(self, xmlWriter, name, value, progress):
    value = map(str, value)
    xmlWriter.simpletag(name, value=" ".join(value))
    xmlWriter.newline()
  def xmlRead(self, (name, attrs, content), parent):
    values = attrs["value"].split()
    return map(parseNum, values)

class TableConverter(SimpleConverter):
  def xmlWrite(self, xmlWriter, name, value, progress):
    xmlWriter.begintag(name)
    xmlWriter.newline()
    value.toXML(xmlWriter, progress)
    xmlWriter.endtag(name)
    xmlWriter.newline()
  def xmlRead(self, (name, attrs, content), parent):
    ob = self.getClass()()
    for element in content:
      if isinstance(element, basestring):
        continue
      ob.fromXML(element)
    return ob

class PrivateDictConverter(TableConverter):
  def getClass(self):
    return PrivateDict
  def read(self, parent, value):
    size, offset = value
    file = parent.file
    priv = PrivateDict(parent.strings, file, offset)
    file.seek(offset)
    data = file.read(size)
    len(data) == size
    priv.decompile(data)
    return priv
  def write(self, parent, value):
    return (0, 0)  # dummy value

class SubrsConverter(TableConverter):
  def getClass(self):
    return SubrsIndex
  def read(self, parent, value):
    file = parent.file
    file.seek(parent.offset + value)  # Offset(self)
    return SubrsIndex(file)
  def write(self, parent, value):
    return 0  # dummy value

class CharStringsConverter(TableConverter):
  def read(self, parent, value):
    file = parent.file
    charset = parent.charset
    globalSubrs = parent.GlobalSubrs
    if hasattr(parent, "ROS"):
      fdSelect, fdArray = parent.FDSelect, parent.FDArray
      private = None
    else:
      fdSelect, fdArray = None, None
      private = parent.Private
    file.seek(value)  # Offset(0)
    return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
  def write(self, parent, value):
    return 0  # dummy value
  def xmlRead(self, (name, attrs, content), parent):
    if hasattr(parent, "ROS"):
      # if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray 
      private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
    else:
      # if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray. 
      private, fdSelect, fdArray = parent.Private, None, None
    charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
    charStrings.fromXML((name, attrs, content))
    return charStrings

class CharsetConverter:
  def read(self, parent, value):
    isCID = hasattr(parent, "ROS")
    if value > 2:
      numGlyphs = parent.numGlyphs
      file = parent.file
      file.seek(value)
      if DEBUG:
        print "loading charset at %s" % value
      format = readCard8(file)
      if format == 0:
        charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
      elif format == 1 or format == 2:
        charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
      else:
        raise NotImplementedError
      assert len(charset) == numGlyphs
      if DEBUG:
        print "    charset end at %s" % file.tell()
    else: # offset == 0 -> no charset data.
      if isCID or not parent.rawDict.has_key("CharStrings"): 
        assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
        charset = None
      elif value == 0:
        charset = cffISOAdobeStrings
      elif value == 1:
        charset = cffIExpertStrings
      elif value == 2:
        charset = cffExpertSubsetStrings
    return charset

  def write(self, parent, value):
    return 0  # dummy value
  def xmlWrite(self, xmlWriter, name, value, progress):
    # XXX only write charset when not in OT/TTX context, where we
    # dump charset as a separate "GlyphOrder" table.
    ##xmlWriter.simpletag("charset")
    xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
    xmlWriter.newline()
  def xmlRead(self, (name, attrs, content), parent):
    if 0:
      return safeEval(attrs["value"])


class CharsetCompiler:
  
  def __init__(self, strings, charset, parent):
    assert charset[0] == '.notdef'
    isCID = hasattr(parent.dictObj, "ROS")
    data0 = packCharset0(charset, isCID, strings)
    data = packCharset(charset, isCID, strings)
    if len(data) < len(data0):
      self.data = data
    else:
      self.data = data0
    self.parent = parent
  
  def setPos(self, pos, endPos):
    self.parent.rawDict["charset"] = pos
  
  def getDataLength(self):
    return len(self.data)
  
  def toFile(self, file):
    file.write(self.data)


def getCIDfromName(name, strings):
  return int(name[3:])

def getSIDfromName(name, strings):
  return strings.getSID(name)

def packCharset0(charset, isCID, strings):
  format = 0
  data = [packCard8(format)]
  if isCID:
    getNameID = getCIDfromName
  else:
    getNameID = getSIDfromName

  for name in charset[1:]:
    data.append(packCard16(getNameID(name,strings)))
  return "".join(data)


def packCharset(charset, isCID, strings):
  format = 1
  ranges = []
  first = None
  end = 0
  if isCID:
    getNameID = getCIDfromName
  else:
    getNameID = getSIDfromName
  
  for name in charset[1:]:
    SID = getNameID(name, strings)
    if first is None:
      first = SID
    elif end + 1 <> SID:
      nLeft = end - first
      if nLeft > 255:
        format = 2
      ranges.append((first, nLeft))
      first = SID
    end = SID
  nLeft = end - first
  if nLeft > 255:
    format = 2
  ranges.append((first, nLeft))
  
  data = [packCard8(format)]
  if format == 1:
    nLeftFunc = packCard8
  else:
    nLeftFunc = packCard16
  for first, nLeft in ranges:
    data.append(packCard16(first) + nLeftFunc(nLeft))
  return "".join(data)

def parseCharset0(numGlyphs, file, strings, isCID):
  charset = [".notdef"]
  if isCID:
    for i in range(numGlyphs - 1):
      CID = readCard16(file)
      charset.append("cid" + string.zfill(str(CID), 5) )
  else:
    for i in range(numGlyphs - 1):
      SID = readCard16(file)
      charset.append(strings[SID])
  return charset

def parseCharset(numGlyphs, file, strings, isCID, format):
  charset = ['.notdef']
  count = 1
  if format == 1:
    nLeftFunc = readCard8
  else:
    nLeftFunc = readCard16
  while count < numGlyphs:
    first = readCard16(file)
    nLeft = nLeftFunc(file)
    if isCID:
      for CID in range(first, first+nLeft+1):
        charset.append("cid" + string.zfill(str(CID), 5) )
    else:
      for SID in range(first, first+nLeft+1):
        charset.append(strings[SID])
    count = count + nLeft + 1
  return charset


class EncodingCompiler:

  def __init__(self, strings, encoding, parent):
    assert not isinstance(encoding, basestring)
    data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
    data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
    if len(data0) < len(data1):
      self.data = data0
    else:
      self.data = data1
    self.parent = parent

  def setPos(self, pos, endPos):
    self.parent.rawDict["Encoding"] = pos
  
  def getDataLength(self):
    return len(self.data)
  
  def toFile(self, file):
    file.write(self.data)


class EncodingConverter(SimpleConverter):

  def read(self, parent, value):
    if value == 0:
      return "StandardEncoding"
    elif value == 1:
      return "ExpertEncoding"
    else:
      assert value > 1
      file = parent.file
      file.seek(value)
      if DEBUG:
        print "loading Encoding at %s" % value
      format = readCard8(file)
      haveSupplement = format & 0x80
      if haveSupplement:
        raise NotImplementedError, "Encoding supplements are not yet supported"
      format = format & 0x7f
      if format == 0:
        encoding = parseEncoding0(parent.charset, file, haveSupplement,
            parent.strings)
      elif format == 1:
        encoding = parseEncoding1(parent.charset, file, haveSupplement,
            parent.strings)
      return encoding

  def write(self, parent, value):
    if value == "StandardEncoding":
      return 0
    elif value == "ExpertEncoding":
      return 1
    return 0  # dummy value

  def xmlWrite(self, xmlWriter, name, value, progress):
    if value in ("StandardEncoding", "ExpertEncoding"):
      xmlWriter.simpletag(name, name=value)
      xmlWriter.newline()
      return
    xmlWriter.begintag(name)
    xmlWriter.newline()
    for code in range(len(value)):
      glyphName = value[code]
      if glyphName != ".notdef":
        xmlWriter.simpletag("map", code=hex(code), name=glyphName)
        xmlWriter.newline()
    xmlWriter.endtag(name)
    xmlWriter.newline()

  def xmlRead(self, (name, attrs, content), parent):
    if attrs.has_key("name"):
      return attrs["name"]
    encoding = [".notdef"] * 256
    for element in content:
      if isinstance(element, basestring):
        continue
      name, attrs, content = element
      code = safeEval(attrs["code"])
      glyphName = attrs["name"]
      encoding[code] = glyphName
    return encoding


def parseEncoding0(charset, file, haveSupplement, strings):
  nCodes = readCard8(file)
  encoding = [".notdef"] * 256
  for glyphID in range(1, nCodes + 1):
    code = readCard8(file)
    if code != 0:
      encoding[code] = charset[glyphID]
  return encoding

def parseEncoding1(charset, file, haveSupplement, strings):
  nRanges = readCard8(file)
  encoding = [".notdef"] * 256
  glyphID = 1
  for i in range(nRanges):
    code = readCard8(file)
    nLeft = readCard8(file)
    for glyphID in range(glyphID, glyphID + nLeft + 1):
      encoding[code] = charset[glyphID]
      code = code + 1
    glyphID = glyphID + 1
  return encoding

def packEncoding0(charset, encoding, strings):
  format = 0
  m = {}
  for code in range(len(encoding)):
    name = encoding[code]
    if name != ".notdef":
      m[name] = code
  codes = []
  for name in charset[1:]:
    code = m.get(name)
    codes.append(code)
  
  while codes and codes[-1] is None:
    codes.pop()

  data = [packCard8(format), packCard8(len(codes))]
  for code in codes:
    if code is None:
      code = 0
    data.append(packCard8(code))
  return "".join(data)

def packEncoding1(charset, encoding, strings):
  format = 1
  m = {}
  for code in range(len(encoding)):
    name = encoding[code]
    if name != ".notdef":
      m[name] = code
  ranges = []
  first = None
  end = 0
  for name in charset[1:]:
    code = m.get(name, -1)
    if first is None:
      first = code
    elif end + 1 <> code:
      nLeft = end - first
      ranges.append((first, nLeft))
      first = code
    end = code
  nLeft = end - first
  ranges.append((first, nLeft))
  
  # remove unencoded glyphs at the end.
  while ranges and ranges[-1][0] == -1:
    ranges.pop()

  data = [packCard8(format), packCard8(len(ranges))]
  for first, nLeft in ranges:
    if first == -1:  # unencoded
      first = 0
    data.append(packCard8(first) + packCard8(nLeft))
  return "".join(data)


class FDArrayConverter(TableConverter):

  def read(self, parent, value):
    file = parent.file
    file.seek(value)
    fdArray = FDArrayIndex(file)
    fdArray.strings = parent.strings
    fdArray.GlobalSubrs = parent.GlobalSubrs
    return fdArray

  def write(self, parent, value):
    return 0  # dummy value

  def xmlRead(self, (name, attrs, content), parent):
    fdArray = FDArrayIndex()
    for element in content:
      if isinstance(element, basestring):
        continue
      fdArray.fromXML(element)
    return fdArray


class FDSelectConverter:

  def read(self, parent, value):
    file = parent.file
    file.seek(value)
    fdSelect = FDSelect(file, parent.numGlyphs)
    return   fdSelect

  def write(self, parent, value):
    return 0  # dummy value

  # The FDSelect glyph data is written out to XML in the charstring keys,
  # so we write out only the format selector
  def xmlWrite(self, xmlWriter, name, value, progress):
    xmlWriter.simpletag(name, [('format', value.format)])
    xmlWriter.newline()

  def xmlRead(self, (name, attrs, content), parent):
    format = safeEval(attrs["format"])
    file = None
    numGlyphs = None
    fdSelect = FDSelect(file, numGlyphs, format)
    return fdSelect
    

def packFDSelect0(fdSelectArray):
  format = 0
  data = [packCard8(format)]
  for index in fdSelectArray:
    data.append(packCard8(index))
  return "".join(data)


def packFDSelect3(fdSelectArray):
  format = 3
  fdRanges = []
  first = None
  end = 0
  lenArray = len(fdSelectArray)
  lastFDIndex = -1
  for i in range(lenArray):
    fdIndex = fdSelectArray[i]
    if lastFDIndex != fdIndex:
      fdRanges.append([i, fdIndex])
      lastFDIndex = fdIndex
  sentinelGID = i + 1
    
  data = [packCard8(format)]
  data.append(packCard16( len(fdRanges) ))
  for fdRange in fdRanges:
    data.append(packCard16(fdRange[0]))
    data.append(packCard8(fdRange[1]))
  data.append(packCard16(sentinelGID))
  return "".join(data)


class FDSelectCompiler:
  
  def __init__(self, fdSelect, parent):
    format = fdSelect.format
    fdSelectArray = fdSelect.gidArray
    if format == 0:
      self.data = packFDSelect0(fdSelectArray)
    elif format == 3:
      self.data = packFDSelect3(fdSelectArray)
    else:
      # choose smaller of the two formats
      data0 = packFDSelect0(fdSelectArray)
      data3 = packFDSelect3(fdSelectArray)
      if len(data0) < len(data3):
        self.data = data0
        fdSelect.format = 0
      else:
        self.data = data3
        fdSelect.format = 3

    self.parent = parent
  
  def setPos(self, pos, endPos):
    self.parent.rawDict["FDSelect"] = pos
  
  def getDataLength(self):
    return len(self.data)
  
  def toFile(self, file):
    file.write(self.data)


class ROSConverter(SimpleConverter):

  def xmlWrite(self, xmlWriter, name, value, progress):
    registry, order, supplement = value
    xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
      ('Supplement', supplement)])
    xmlWriter.newline()

  def xmlRead(self, (name, attrs, content), parent):
    return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))



topDictOperators = [
#  opcode     name                  argument type   default    converter
  ((12, 30), 'ROS',        ('SID','SID','number'), None,      ROSConverter()),
  ((12, 20), 'SyntheticBase',      'number',       None,      None),
  (0,        'version',            'SID',          None,      None),
  (1,        'Notice',             'SID',          None,      Latin1Converter()),
  ((12, 0),  'Copyright',          'SID',          None,      Latin1Converter()),
  (2,        'FullName',           'SID',          None,      None),
  ((12, 38), 'FontName',           'SID',          None,      None),
  (3,        'FamilyName',         'SID',          None,      None),
  (4,        'Weight',             'SID',          None,      None),
  ((12, 1),  'isFixedPitch',       'number',       0,         None),
  ((12, 2),  'ItalicAngle',        'number',       0,         None),
  ((12, 3),  'UnderlinePosition',  'number',       None,      None),
  ((12, 4),  'UnderlineThickness', 'number',       50,        None),
  ((12, 5),  'PaintType',          'number',       0,         None),
  ((12, 6),  'CharstringType',     'number',       2,         None),
  ((12, 7),  'FontMatrix',         'array',  [0.001,0,0,0.001,0,0],  None),
  (13,       'UniqueID',           'number',       None,      None),
  (5,        'FontBBox',           'array',  [0,0,0,0],       None),
  ((12, 8),  'StrokeWidth',        'number',       0,         None),
  (14,       'XUID',               'array',        None,      None),
  ((12, 21), 'PostScript',         'SID',          None,      None),
  ((12, 22), 'BaseFontName',       'SID',          None,      None),
  ((12, 23), 'BaseFontBlend',      'delta',        None,      None),
  ((12, 31), 'CIDFontVersion',     'number',       0,         None),
  ((12, 32), 'CIDFontRevision',    'number',       0,         None),
  ((12, 33), 'CIDFontType',        'number',       0,         None),
  ((12, 34), 'CIDCount',           'number',       8720,      None),
  (15,       'charset',            'number',       0,         CharsetConverter()),
  ((12, 35), 'UIDBase',            'number',       None,      None),
  (16,       'Encoding',           'number',       0,         EncodingConverter()),
  (18,       'Private',       ('number','number'), None,      PrivateDictConverter()),
  ((12, 37), 'FDSelect',           'number',       None,      FDSelectConverter()),
  ((12, 36), 'FDArray',            'number',       None,      FDArrayConverter()),
  (17,       'CharStrings',        'number',       None,      CharStringsConverter()),
]

# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
# in order for the font to compile back from xml.


privateDictOperators = [
#  opcode     name                  argument type   default    converter
  (6,        'BlueValues',         'delta',        None,      None),
  (7,        'OtherBlues',         'delta',        None,      None),
  (8,        'FamilyBlues',        'delta',        None,      None),
  (9,        'FamilyOtherBlues',   'delta',        None,      None),
  ((12, 9),  'BlueScale',          'number',       0.039625,  None),
  ((12, 10), 'BlueShift',          'number',       7,         None),
  ((12, 11), 'BlueFuzz',           'number',       1,         None),
  (10,       'StdHW',              'number',       None,      None),
  (11,       'StdVW',              'number',       None,      None),
  ((12, 12), 'StemSnapH',          'delta',        None,      None),
  ((12, 13), 'StemSnapV',          'delta',        None,      None),
  ((12, 14), 'ForceBold',          'number',       0,         None),
  ((12, 15), 'ForceBoldThreshold', 'number',       None,      None),  # deprecated
  ((12, 16), 'lenIV',              'number',       None,      None),  # deprecated
  ((12, 17), 'LanguageGroup',      'number',       0,         None),
  ((12, 18), 'ExpansionFactor',    'number',       0.06,      None),
  ((12, 19), 'initialRandomSeed',  'number',       0,         None),
  (20,       'defaultWidthX',      'number',       0,         None),
  (21,       'nominalWidthX',      'number',       0,         None),
  (19,       'Subrs',              'number',       None,      SubrsConverter()),
]

def addConverters(table):
  for i in range(len(table)):
    op, name, arg, default, conv = table[i]
    if conv is not None:
      continue
    if arg in ("delta", "array"):
      conv = ArrayConverter()
    elif arg == "number":
      conv = NumberConverter()
    elif arg == "SID":
      conv = SimpleConverter()
    else:
      assert 0
    table[i] = op, name, arg, default, conv

addConverters(privateDictOperators)
addConverters(topDictOperators)


class TopDictDecompiler(psCharStrings.DictDecompiler):
  operators = buildOperatorDict(topDictOperators)


class PrivateDictDecompiler(psCharStrings.DictDecompiler):
  operators = buildOperatorDict(privateDictOperators)


class DictCompiler:
  
  def __init__(self, dictObj, strings, parent):
    assert isinstance(strings, IndexedStrings)
    self.dictObj = dictObj
    self.strings = strings
    self.parent = parent
    rawDict = {}
    for name in dictObj.order:
      value = getattr(dictObj, name, None)
      if value is None:
        continue
      conv = dictObj.converters[name]
      value = conv.write(dictObj, value)
      if value == dictObj.defaults.get(name):
        continue
      rawDict[name] = value
    self.rawDict = rawDict
  
  def setPos(self, pos, endPos):
    pass
  
  def getDataLength(self):
    return len(self.compile("getDataLength"))
  
  def compile(self, reason):
    if DEBUG:
      print "-- compiling %s for %s" % (self.__class__.__name__, reason)
      print "in baseDict: ", self
    rawDict = self.rawDict
    data = []
    for name in self.dictObj.order:
      value = rawDict.get(name)
      if value is None:
        continue
      op, argType = self.opcodes[name]
      if isinstance(argType, tuple):
        l = len(argType)
        assert len(value) == l, "value doesn't match arg type"
        for i in range(l):
          arg = argType[i]
          v = value[i]
          arghandler = getattr(self, "arg_" + arg)
          data.append(arghandler(v))
      else:
        arghandler = getattr(self, "arg_" + argType)
        data.append(arghandler(value))
      data.append(op)
    return "".join(data)
  
  def toFile(self, file):
    file.write(self.compile("toFile"))
  
  def arg_number(self, num):
    return encodeNumber(num)
  def arg_SID(self, s):
    return psCharStrings.encodeIntCFF(self.strings.getSID(s))
  def arg_array(self, value):
    data = []
    for num in value:
      data.append(encodeNumber(num))
    return "".join(data)
  def arg_delta(self, value):
    out = []
    last = 0
    for v in value:
      out.append(v - last)
      last = v
    data = []
    for num in out:
      data.append(encodeNumber(num))
    return "".join(data)


def encodeNumber(num):
  if isinstance(num, float):
    return psCharStrings.encodeFloat(num)
  else:
    return psCharStrings.encodeIntCFF(num)


class TopDictCompiler(DictCompiler):
  
  opcodes = buildOpcodeDict(topDictOperators)
  
  def getChildren(self, strings):
    children = []
    if hasattr(self.dictObj, "charset") and self.dictObj.charset:
      children.append(CharsetCompiler(strings, self.dictObj.charset, self))
    if hasattr(self.dictObj, "Encoding"):
      encoding = self.dictObj.Encoding
      if not isinstance(encoding, basestring):
        children.append(EncodingCompiler(strings, encoding, self))
    if hasattr(self.dictObj, "FDSelect"):
      # I have not yet supported merging a ttx CFF-CID font, as there are interesting
      # issues about merging the FDArrays. Here I assume that
      # either the font was read from XML, and teh FDSelect indices are all
      # in the charstring data, or the FDSelect array is already fully defined.
      fdSelect = self.dictObj.FDSelect
      if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
        charStrings = self.dictObj.CharStrings
        for name in self.dictObj.charset:
          charstring = charStrings[name]
          fdSelect.append(charStrings[name].fdSelectIndex)
      fdSelectComp = FDSelectCompiler(fdSelect, self)
      children.append(fdSelectComp)
    if hasattr(self.dictObj, "CharStrings"):
      items = []
      charStrings = self.dictObj.CharStrings
      for name in self.dictObj.charset:
        items.append(charStrings[name])
      charStringsComp = CharStringsCompiler(items, strings, self)
      children.append(charStringsComp)
    if hasattr(self.dictObj, "FDArray"):
      # I have not yet supported merging a ttx CFF-CID font, as there are interesting
      # issues about merging the FDArrays. Here I assume that the FDArray info is correct
      # and complete.
      fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
      children.append(fdArrayIndexComp)
      children.extend(fdArrayIndexComp.getChildren(strings))
    if hasattr(self.dictObj, "Private"):
      privComp = self.dictObj.Private.getCompiler(strings, self)
      children.append(privComp)
      children.extend(privComp.getChildren(strings))
    return children


class FontDictCompiler(DictCompiler):
  
  opcodes = buildOpcodeDict(topDictOperators)
  
  def getChildren(self, strings):
    children = []
    if hasattr(self.dictObj, "Private"):
      privComp = self.dictObj.Private.getCompiler(strings, self)
      children.append(privComp)
      children.extend(privComp.getChildren(strings))
    return children


class PrivateDictCompiler(DictCompiler):
  
  opcodes = buildOpcodeDict(privateDictOperators)
  
  def setPos(self, pos, endPos):
    size = endPos - pos
    self.parent.rawDict["Private"] = size, pos
    self.pos = pos
  
  def getChildren(self, strings):
    children = []
    if hasattr(self.dictObj, "Subrs"):
      children.append(self.dictObj.Subrs.getCompiler(strings, self))
    return children


class BaseDict:
  
  def __init__(self, strings=None, file=None, offset=None):
    self.rawDict = {}
    if DEBUG:
      print "loading %s at %s" % (self.__class__.__name__, offset)
    self.file = file
    self.offset = offset
    self.strings = strings
    self.skipNames = []
  
  def decompile(self, data):
    if DEBUG:
      print "    length %s is %s" % (self.__class__.__name__, len(data))
    dec = self.decompilerClass(self.strings)
    dec.decompile(data)
    self.rawDict = dec.getDict()
    self.postDecompile()
  
  def postDecompile(self):
    pass
  
  def getCompiler(self, strings, parent):
    return self.compilerClass(self, strings, parent)
  
  def __getattr__(self, name):
    value = self.rawDict.get(name)
    if value is None:
      value = self.defaults.get(name)
    if value is None:
      raise AttributeError, name
    conv = self.converters[name]
    value = conv.read(self, value)
    setattr(self, name, value)
    return value
  
  def toXML(self, xmlWriter, progress):
    for name in self.order:
      if name in self.skipNames:
        continue
      value = getattr(self, name, None)
      if value is None:
        continue
      conv = self.converters[name]
      conv.xmlWrite(xmlWriter, name, value, progress)
  
  def fromXML(self, (name, attrs, content)):
    conv = self.converters[name]
    value = conv.xmlRead((name, attrs, content), self)
    setattr(self, name, value)


class TopDict(BaseDict):
  
  defaults = buildDefaults(topDictOperators)
  converters = buildConverters(topDictOperators)
  order = buildOrder(topDictOperators)
  decompilerClass = TopDictDecompiler
  compilerClass = TopDictCompiler
  
  def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
    BaseDict.__init__(self, strings, file, offset)
    self.GlobalSubrs = GlobalSubrs
  
  def getGlyphOrder(self):
    return self.charset
  
  def postDecompile(self):
    offset = self.rawDict.get("CharStrings")
    if offset is None:
      return
    # get the number of glyphs beforehand.
    self.file.seek(offset)
    self.numGlyphs = readCard16(self.file)
  
  def toXML(self, xmlWriter, progress):
    if hasattr(self, "CharStrings"):
      self.decompileAllCharStrings(progress)
    if hasattr(self, "ROS"):
      self.skipNames = ['Encoding']
    if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
      # these values have default values, but I only want them to show up
      # in CID fonts.
      self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
          'CIDCount']
    BaseDict.toXML(self, xmlWriter, progress)
  
  def decompileAllCharStrings(self, progress):
    # XXX only when doing ttdump -i?
    i = 0
    for charString in self.CharStrings.values():
      try:
        charString.decompile()
      except:
        print "Error in charstring ", i
        import sys
        type, value = sys. exc_info()[0:2]
        raise type(value)
      if not i % 30 and progress:
        progress.increment(0)  # update
      i = i + 1


class FontDict(BaseDict):
  
  defaults = buildDefaults(topDictOperators)
  converters = buildConverters(topDictOperators)
  order = buildOrder(topDictOperators)
  decompilerClass = None
  compilerClass = FontDictCompiler
  
  def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
    BaseDict.__init__(self, strings, file, offset)
    self.GlobalSubrs = GlobalSubrs
  
  def getGlyphOrder(self):
    return self.charset
  
  def toXML(self, xmlWriter, progress):
    self.skipNames = ['Encoding']
    BaseDict.toXML(self, xmlWriter, progress)
  


class PrivateDict(BaseDict):
  defaults = buildDefaults(privateDictOperators)
  converters = buildConverters(privateDictOperators)
  order = buildOrder(privateDictOperators)
  decompilerClass = PrivateDictDecompiler
  compilerClass = PrivateDictCompiler


class IndexedStrings:
  
  """SID -> string mapping."""
  
  def __init__(self, file=None):
    if file is None:
      strings = []
    else:
      strings = list(Index(file))
    self.strings = strings
  
  def getCompiler(self):
    return IndexedStringsCompiler(self, None, None)
  
  def __len__(self):
    return len(self.strings)
  
  def __getitem__(self, SID):
    if SID < cffStandardStringCount:
      return cffStandardStrings[SID]
    else:
      return self.strings[SID - cffStandardStringCount]
  
  def getSID(self, s):
    if not hasattr(self, "stringMapping"):
      self.buildStringMapping()
    if cffStandardStringMapping.has_key(s):
      SID = cffStandardStringMapping[s]
    elif self.stringMapping.has_key(s):
      SID = self.stringMapping[s]
    else:
      SID = len(self.strings) + cffStandardStringCount
      self.strings.append(s)
      self.stringMapping[s] = SID
    return SID
  
  def getStrings(self):
    return self.strings
  
  def buildStringMapping(self):
    self.stringMapping = {}
    for index in range(len(self.strings)):
      self.stringMapping[self.strings[index]] = index + cffStandardStringCount


# The 391 Standard Strings as used in the CFF format.
# from Adobe Technical None #5176, version 1.0, 18 March 1998

cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 
    'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 
    'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 
    'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 
    'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 
    'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 
    'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 
    'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 
    'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 
    's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 
    'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin', 
    'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 
    'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 
    'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase', 
    'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 
    'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 
    'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron', 
    'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 
    'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 
    'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 
    'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 
    'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 
    'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 
    'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 
    'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 
    'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 
    'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 
    'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 
    'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex', 
    'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 
    'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave', 
    'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall', 
    'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 
    'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 
    'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 
    'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 
    'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior', 
    'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 
    'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 
    'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior', 
    'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 
    'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 
    'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 
    'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 
    'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', 
    'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 
    'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 
    'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall', 
    'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 
    'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior', 
    'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 
    'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 
    'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', 
    'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', 
    'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 
    'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 
    'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall', 
    'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 
    'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall', 
    'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 
    'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002', 
    '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 
    'Semibold'
]

cffStandardStringCount = 391
assert len(cffStandardStrings) == cffStandardStringCount
# build reverse mapping
cffStandardStringMapping = {}
for _i in range(cffStandardStringCount):
  cffStandardStringMapping[cffStandardStrings[_i]] = _i

cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
"zcaron"]

cffISOAdobeStringCount = 229
assert len(cffISOAdobeStrings) == cffISOAdobeStringCount

cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
"centinferior", "dollarinferior", "periodinferior", "commainferior",
"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
"Ydieresissmall"]

cffExpertStringCount = 166
assert len(cffIExpertStrings) == cffExpertStringCount

cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
"eightinferior", "nineinferior", "centinferior", "dollarinferior",
"periodinferior", "commainferior"]

cffExpertSubsetStringCount = 87
assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.