Logo Search packages:      
Sourcecode: passepartout version File versions  Download package

afm.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "afm.hh"
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include "ps/unicode.h"
#include "util/warning.h"
#include "util/stringutil.h"

using std::string;

class font::AFMetrics::GlyphData {
public:
  void addGlyph(string name, float width) {
    Glib::ustring chars = PS::Unicode::chars_of_glyph(name, true);
    if(chars.size() == 1)
      glyphs[chars[0]] = Glyph(name, width);
  }
  bool hasGlyph(const string &glyphname) const {
    // This is pretty inefficient
    for(Glyphs::const_iterator i = glyphs.begin(); i != glyphs.end(); i++) {
      if(i->second.name == glyphname)
      return true;
    }
    return false;
  }
  string getGlyphName(const Glib::ustring &chars) const {
    if(chars.length() != 1)
      return string();
    Glyphs::const_iterator i = glyphs.find(chars[0]);
    if(i == glyphs.end())
      return string();
    return i->second.name;
  }
  float getWidth(gunichar c) const {
    using Glib::ustring;
    Glyphs::const_iterator i = glyphs.find(c);
    if(i != glyphs.end()) {
      return i->second.width;
    } else {
      if(c == 160 /* non-breaking space */)
      return getWidth(32 /* plain space */);
      
      std::string msg("No width found for char #" + tostr(int(c)));
      verbose << msg << std::endl;
      throw std::runtime_error(msg);
    }
  }
private:
  struct Glyph {
    Glyph() {} // for the benefit of std::map
    Glyph(string _name, float _width) : name(_name), width(_width) {}
    string name;
    float width;
  };
  //  typedef std::map<Glib::ustring, Glyph> Glyphs;
  typedef std::map<gunichar, Glyph> Glyphs;
  Glyphs glyphs;
};

font::AFMetrics::AFMetrics(const std::string &filename) 
  :ascender(0), descender(0), underline_position(0), underline_thickness(0),
   cap_height(0), x_height(0), italic_angle(0)
{
  glyphdata = new GlyphData();
  
  std::ifstream source(filename.c_str());
  if(!source)
    throw std::runtime_error("Failed to open " + filename);

  string key;
  source >> key;
  if(key != "StartFontMetrics")
    throw std::runtime_error(filename + " is not an AFM");
  source >> key; // ignore version number
  while(source >> key) {
    if(key == "Comment");     // ignore the comment!
    else if(key == "Descender") {
      source >> descender;
      descender *= -0.001;    // Metrics use neg values here, I want
                        // positive.
    } else if(key == "Ascender") {
      source >> ascender;
      ascender *= 0.001;
    } else if(key == "CapHeight") {
      source >> cap_height;
      cap_height *= 0.001;
    } else if(key == "XHeight") {
      source >> x_height;
      x_height *= 0.001;
    } else if(key == "UnderlinePosition") {
      source >> underline_position;
      underline_position *= 0.001;
    } else if(key == "UnderlineThickness") {
      source >> underline_thickness;
      underline_thickness *= 0.001;
    } else if(key == "FontBBox") {
      source >> bbox[0] >> bbox[1] >> bbox[2] >> bbox[3];
      if(ascender == 0.0) ascender = bbox[3] * 0.001;
      if(descender == 0.0) descender = bbox[1] * -0.001;
      if(cap_height == 0.0) cap_height = bbox[3] * 0.001;
    } else if(key == "ItalicAngle") {
      source >> italic_angle;
    } else if(key == "C") {
      int ch;
      float wx;
      string s1, t1, s2, t2, entity, s3;
      source >> ch >> s1 >> t1 >> wx >> s2 >> t2 >> entity >> s3;
#ifdef DEBUG_LOG
      cerr << "Find '" << entity << "' ... ";
#endif
      glyphdata->addGlyph(entity, wx * 0.001);
#ifdef DEBUG_LOG
      cerr << " found " << ch << "!\n";
#endif
    } else if(key == "FontName") {
      source >> ps_name;
    } else if(key == "FullName") {
      safe_getline(source, name);
      name = strip_whitespace(name);
    }
    source.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
  }
}

font::AFMetrics::~AFMetrics() {
  delete glyphdata;
}

float
font::AFMetrics::getWidth(const Glib::ustring &str) const {
  float result = 0;
  for(Glib::ustring::const_iterator i = str.begin(); i != str.end(); i++) {
    try {
      result += glyphdata->getWidth(*i); /// \todo multichar glyphs
    }
    catch(...) {
      /// \todo report the error somehow, but dismissing a whole
      /// string just because a single character is broken is no good
      result += 0.625; // reasonable default?
    }
  }
  return result;
}

bool font::AFMetrics::hasGlyph(const string &glyphname) const {
  return glyphdata->hasGlyph(glyphname);
}

string font::AFMetrics::getGlyphName(const Glib::ustring &chars) const {
  return glyphdata->getGlyphName(chars);
}

#ifdef DEBUG_FONTMETRICS
main(int argc, char **argv) {
  if(argc != 3) {
    cerr << "Wrong no of args. (" << argc << ")\n";
    exit(2);
  }
  font::AFMetrics afm(argv[1]);
  cout << "Width of '" << argv[2] << "' is: " << afm.getWidth(argv[2]);
  
}
#endif

Generated by  Doxygen 1.6.0   Back to index