Parent

PDF::Writer::FontMetrics

Attributes

differences[RW]
encoding[RW]
font_num[RW]
path[RW]

Public Class Methods

new() click to toggle source
# File lib/pdf/writer/fontmetrics.rb, line 21
def initialize
  @kpx      = {}
  @c        = {}
  @version  = 1
  @font_num = nil
end
open(font_name) click to toggle source

Open the font file and return a PDF::Writer::FontMetrics object containing it. The font_name may specify just a font file or a full path. If a path is specified, that is the only place where the font file will be looked for.

# File lib/pdf/writer/fontmetrics.rb, line 39
def self.open(font_name)
  file  = font_name.gsub(/\\/, "/")
  dir   = File.dirname(file)
  name  = File.basename(file)

  metrics_path = []

    # Check to see if the directory is "." or a non-path
  if dir == "."
    metrics_path << dir << METRICS_PATH << $LOAD_PATH
  elsif dir !~ %{^(\w:|/)} and dir.index("/")
    METRICS_PATH.each { |path| metrics_path << File.join(path, dir) }
    $LOAD_PATH.each { |path| metrics_path << File.join(path, dir) }
  else
    metric_path = [ dir ]
  end
  metrics_path.flatten!

  font = nil
  afm = nil

  metrics_path.each do |path|
    afm_file  = File.join(path, "#{name}.afm").gsub(/\.afm\.afm$/, ".afm")
    rfm_file  = "#{afm_file}.rfm"

      # Attempt to unmarshal an .afm.rfm file first. If it is loaded,
      # we're in good shape.
    begin
      if File.exists?(rfm_file)
        data = File.open(rfm_file, "rb") { |file| file.read }
        font = Marshal.load(data)
        return font
      end
    rescue
      nil
    end

      # Attempt to open and process the font.
    File.open(afm_file, "rb") do |file|
      font = PDF::Writer::FontMetrics.new

        # An AFM file contains key names followed by valuees.
      file.each do |line|
        line.chomp!
        line.strip!
        key, *values = line.split
        next if key.nil?
        op = "#{key.downcase}=".to_sym

          # I probably need to deal with MetricsSet. The default value is
          # 0, which is writing direction 0 (W0X). If MetricsSet 1 is
          # specified, then only writing direction 1 is present (W1X). If
          # MetricsSet 2 is specified, then both W0X and W1X are present.

          # Measurements are always 1/1000th of a scale factor (point
          # size). So a 12pt character with a width of 222 is going to be
          # 222 * 12 / 1000 or 2.664 points wide.
        case key
        when 'FontName', 'FullName', 'FamilyName', 'Weight',
          'IsFixedPitch', 'CharacterSet', 'Version', 'EncodingScheme'
            # These values are string values.
          font.__send__(op, values.join(" "))
        when 'ItalicAngle', 'UnderlinePosition', 'UnderlineThickness',
          'CapHeight', 'XHeight', 'Ascender', 'Descender', 'StdHW',
          'StdVW', 'StartCharMetrics'
            # These values are floating point values.
          font.__send__(op, values.join(" ").to_f)
        when 'FontBBox'
            # These values are an array of floating point values
          font.fontbbox = values.map { |el| el.to_f }
        when 'C', 'CH'
            # Individual Character Metrics Values:
            #   C  <character number>
            #   CH <hex character number>
            #     One of C or CH must be provided. Specifies the encoding
            #     number for the character. -1 if the character is not
            #     encoded in the font.
            #
            #   WX  <x width number>
            #   W0X <x0 width number>
            #   W1X <x1 width number>
            #     Character width in x for writing direction 0 (WX, W0X)
            #     or 1 (W1X) where y is 0. Optional.
            #
            #   WY  <y width number>
            #   W0Y <y0 width number>
            #   W1Y <y1 width number>
            #     Character width in y for writing direction 0 (WY, W0Y)
            #     or 1 (W1Y) where x is 0. Optional.
            #
            #   W  <x width> <y width>
            #   W0 <x0 width> <y0 width>
            #   W1 <x1 width> <y1 width>
            #     Character width in x, y for writing direction 0 (W, W0)
            #     or 1 (W1). Optional.
            #
            #   VV <x number> <y number>
            #     Same as VVector in the global font definition, but for
            #     this single character. Optional.
            #
            #   N <name>
            #     The PostScript name of the font. Optional.
            #   
            #   B <llx> <lly> <urx> <ury>
            #     Character bounding box for the lower left corner and the
            #     upper right corner. Optional.
            #
            #   L <sucessor> <ligature>
            #     Ligature sequence where both <successor> and <ligature>
            #     are N <names>. For the fragment "N f; L i fi; L l fl",
            #     two ligatures are defined: fi and fl. Optional,
            #     multiples permitted.
            #
            # C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
          bits = line.chomp.strip.split(/;/).collect { |r| r.strip }
          dtmp = {}

          bits.each do |bit|
            b = bit.split
            if b.size > 2
              dtmp[b[0]] = []
              b[1..-1].each do |z|
                if z =~ NUMBER
                  dtmp[b[0]] << z.to_f
                else
                  dtmp[b[0]] << z
                end
              end
            elsif b.size == 2
              if b[0] == 'C' and b[1] =~ NUMBER
                dtmp[b[0]] = b[1].to_i
              elsif b[0] == 'CH'
                dtmp['C'] = b[1].to_i(16)
              elsif b[1] =~ NUMBER
                dtmp[b[0]] = b[1].to_f
              else
                dtmp[b[0]] = b[1]
              end
            end
          end

          font.c[dtmp['N']] = dtmp
          font.c[dtmp['C']] = dtmp unless dtmp['C'].nil?
        when 'KPX' # KPX Adieresis yacute -40
          # KPX: Kerning Pair
          font.kpx[values[0]] = { }
          font.kpx[values[0]][values[1]] = values[2].to_f
        end
      end
      font.path = afm_file
    end rescue nil # Ignore file errors
    break unless font.nil?
  end

  raise ArgumentError, "Font #{font_name} not found." if font.nil?
  font
end

Public Instance Methods

save_as_rfm() click to toggle source

Save the loaded font metrics file as a binary marshaled value.

# File lib/pdf/writer/fontmetrics.rb, line 198
def save_as_rfm
  rfm = File.basename(@path).gsub(/\.afm.*$/, '')
  rfm << ".afm.rfm"
  File.open(rfm, "wb") { |file| file.write Marshal.dump(self) }
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.