text: """Access to font metrics and PostScript name mappings.
  
  All access is through the PSFont class.
  """
  __version__ = '$Revision: 1.4 $'
  
  from . import fonts                            # package
  from . import utils
  
  
  # This dictionary maps PostScript font names to the normal, bold and
  # italic suffixes for the font.  Key is the short name describing the
  # font, value is a tuple indicating the real name of the font (for
  # mapping fonts to other fonts), then the regular, bold, and italic
  # suffix modifiers of the font.  Note that if their is no regular name
  # modifier, then use the empty string, but if there is a regular name
  # modifier, make sure it includes a leading dash.  Other modifiers
  # should not include the dash.
  
  fontdefs = {
      'Times':            (None, '-Roman', 'Bold', 'Italic'),
      'Helvetica':        (None, '',       'Bold', 'Oblique'),
      'NewCenturySchlbk': (None, '-Roman', 'Bold', 'Italic'),
      'Courier':          (None, '',       'Bold', 'Oblique'),
      # The code from HTML-PSformat.c says:
      # "This is a nasty trick, I have put Times in place of Lucida,
      # because most printers don't have Lucida font"
      # Hmm...  -BAW
      #'Lucida':           ('Times', None, 'Bold', 'Italic'),
      'Lucida':           (None, '', 'Bold', 'Italic'),
      #
      'Palatino':         (None, '-Roman', 'Bold', 'Italic'),
      }
  
  # Mappings between HTML header tags and font sizes
  # Entries in the dictionary are factors used with DEFAULT_FONT_SIZE
  # The values used by Mosaic
  #DEFAULT_FONT_SIZE = 12.0
  #font_sizes = {
  #    None: 1.0,
  #    'h1': 3.0,
  #    'h2': 2.0,
  #    'h3': 1.5,
  #    'h4': 1.67,
  #    'h5': 1.0,
  #    'h6': 0.83
  #    }
  
  # The values used by Grail
  DEFAULT_FONT_SIZE = 10.0
  font_sizes = {
      None: 1.0,
      'h1': 1.8,
      'h2': 1.4,
      'h3': 1.2,
      'h4': 1.0,
      'h5': 1.0,
      'h6': 1.0
      }
  
  
  class PSFont:
      """This class manages font changes and calculation of associated
      metrics for PostScript output.  It basically defines a mapping
      between a PostScript definition for a font and a short name used
      by PostScript functions defined in header.ps.
  
      When the font is created, it is passed the name of a variable
      width family and a fixed width family.  Those are the only
      configuration options you have.  Should probably allow a scaling
      factor to be passed in, mapping GUI dpi to PostScript dpi, but
      that would have to be calculated by Grail for the underlying GUI.
  
      Exported methods:
  
         __init__(optional: VARIFAMILY, FIXEDFAMILY)
         set_font((SIZE, ITALIC?, BOLD?, TT?)) ==> (PSFONTNAME, SIZE)
         text_width(TEXT) ==> WIDTH_IN_POINTS
         font_size(optional: (SIZE, ITALIC?, BOLD?, TT?)) ==> SZ_IN_POINTS
      """
      def __init__(self, varifamily='Times', fixedfamily='Courier',
                   size=DEFAULT_FONT_SIZE):
          """Create a font definition using VARIFAMILY as the variable
          width font and FIXEDFAMILY as the fixed width font.  Defaults
          to Helvetica and Courier respectively.
          """
          # current font is a tuple of size, family, italic, bold
          self.vfamily = varifamily
          self.ffamily = fixedfamily
          self.font = (size, 'FONTV', '', '')
          self.base_size = self._fontsize = size
  
          # TBD: this number is slightly bogus, but the rational is
          # thus.  The original code was tied fairly closely with X so
          # it had to map screen resolutions to PostScript.  I don't
          # want this version to be tied to X at all, if possible, so I
          # ignore all screen resolution parameters.  The tradeoff is
          # that the hardcopy will probably not be formatted exactly as
          # it appears on the screen, but I believe that is appropriate.
          # Should we decide to change that, this scaling factor may
          # come into play, but should probably be passed in from Grail,
          # since only it can interface to the underlying window system.
          self.points_per_pixel = 72.0 / 72.0
  
          # calculate document fonts
          if not fontdefs.has_key(self.vfamily): self.vfamily = 'Helvetica'
          if not fontdefs.has_key(self.ffamily): self.ffamily = 'Courier'
          vrealname, vreg, vbold, vitalic = fontdefs[self.vfamily]
          frealname, freg, fbold, fitalic = fontdefs[self.ffamily]
          # fonts may be mapped to other fonts
          if not vrealname: vrealname = self.vfamily
          if not frealname: frealname = self.ffamily
  
          # calculate font names in PostScript space. Eight fonts are
          # used, naming scheme is as follows.  All PostScript font
          # name definitions start with `FONT', followed by `V' for the
          # variable width font and `F' for the fixed width font.  `B'
          # for the bold version, `I' for italics, and for the
          # bold-italic version, `B' *must* preceed `I'.  See header.ps
          # for more info.
          self.docfonts = {
              'FONTV':   '%s%s' % (vrealname, vreg),
              'FONTVB':  '%s-%s' % (vrealname, vbold),
              'FONTVI':  '%s-%s' % (vrealname, vitalic),
              'FONTVBI': '%s-%s%s' % (vrealname, vbold, vitalic),
              'FONTF':   '%s%s' % (frealname, freg),
              'FONTFB':  '%s-%s' % (frealname, fbold),
              'FONTFI':  '%s-%s' % (frealname, fitalic),
              'FONTFBI': '%s-%s%s' % (frealname, fbold, fitalic)
              }
          # instantiated font objects
          self.fontobjs = {}
          self.tw_func = None
  
      def get_font(self):
          """Returns the font nickname and size.
  
          This is the only place the nickname is computed.
          """
          sz, family, italic, bold = self.font
          nick = "%s%s%s" % (family, bold, italic)
  ##      utils.debug("get_font(): %s ==> %s\n" % (self.font, (nick, sz)))
          return nick, sz
  
      def set_font(self, font_tuple):
          """Set the current font to that specified by FONT_TUPLE, which
          is of the form (SIZE, ITALIC?, BOLD?, TT?).  Returns the
          PostScript layer name of the font, and the font size in
          points.  """
          # we *said* we wanted a tuple
          if font_tuple is None: font_tuple = (None, None, None, None)
  ##      utils.debug("set_font(%s)\n" % (font_tuple,))
          set_sz, set_italic, set_bold, set_tt = font_tuple
          # get the current font and break up the tuple
          cur_sz, cur_family, cur_italic, cur_bold = self.font
          # calculate size
          new_sz = self.font_size(font_tuple)
          # calculate variable vs. fixed base name
          if set_tt: new_family = 'FONTF'
          else: new_family = 'FONTV'
  
          # add modifiers.  Because of the way fonts are named, always
          # add bold modifier before italics modifier, in case both are
          # present
          if set_bold: new_bold = 'B'
          else: new_bold = ''
  
          if set_italic: new_italic = 'I'
          else: new_italic = ''
  
          # save the current font specification
          self.font = (new_sz, new_family, new_italic, new_bold)
  
          # get the font nickname
          fontnickname, new_sz = self.get_font()
  
          # make sure the font object is instantiated
          if not self.fontobjs.has_key(fontnickname):
              psfontname = self.docfonts[fontnickname]
              self.fontobjs[fontnickname] = fonts.font_from_name(psfontname)
  ##      print fontnickname, "==>", self.fontobjs[fontnickname]
          self.tw_func = self.fontobjs[fontnickname].text_width
  
          self._fontsize = new_sz
  
          # return the PostScript font definition and the size in points
          return (fontnickname, new_sz)
  
      def text_width(self, text):
  ##      width = self.tw_func(self._fontsize, text)
  ##      utils.debug("%s @ %spt ==> %s" % (`text`, self._fontsize, width),
  ##                  'charsizing')
  ##      return width
          return self.tw_func(self._fontsize, text)
  
      def font_size(self, font_tuple=None):
          """Return the size of the current font, or the font defined by
          optional FONT_TUPLE if present."""
          if not font_tuple:
              return self._fontsize
          tuple_sz = font_tuple[0]
          if type(tuple_sz) is type(1.0):
              return tuple_sz
          try:
              return font_sizes[tuple_sz] * self.base_size
          except KeyError:
              return self.base_size
  