/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include "afm.hh" #include #include #include #include #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 Glyphs; typedef std::map 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::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