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

fontmanager.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "fontmanager.hh"

#include <fstream>
#include <iostream>
#include <fontconfig/fontconfig.h>

#include "afm.hh"
#include "freetype.hh"

#include "util/stringutil.h"
#include "util/filesys.h"
#include "util/warning.h"

font::FontManager* font::FontManager::_instance = 0;
// StaticMutex can, unlike regular Mutex, be created before the call
// to thread_init
Glib::StaticMutex font::FontManager::mutex = GLIBMM_STATIC_MUTEX_INIT;

font::FontManager& font::FontManager::instance() {
  // prevent two threads from creating the instance at once
  Glib::Mutex::Lock lock(mutex);
  if(!_instance)
    _instance = new FontManager(); 
  return *_instance;
}

namespace {
  /// \note FcConfigGetFontDirs could be used to get a list of paths,
  /// but it would include paths for fonts formats other than TrueType
  /// or Type1
  void addPath(font::FontManager::FontPaths &fontpaths,
               const std::string &filename) {
    fontpaths.insert(path(filename));
  }
}

namespace font {

FontManager::FontManager() {
  // Ghostscript replacements for Adobe fonts:
  fontAlias("Bookman-Demi",               "URWBookmanL-DemiBold");
  fontAlias("Bookman-DemiItalic",         "URWBookmanL-DemiBoldItal"); 
  fontAlias("Bookman-Light",              "URWBookmanL-Ligh"); 
  fontAlias("Bookman-LightItalic",        "URWBookmanL-LighItal");
  
  fontAlias("Courier",                    "NimbusMonL-Regu");
  fontAlias("Courier-Oblique",            "NimbusMonL-ReguObli");
  fontAlias("Courier-Bold",               "NimbusMonL-Bold");
  fontAlias("Courier-BoldOblique",        "NimbusMonL-BoldObli");
  
  fontAlias("Times-Roman",                "NimbusRomNo9L-Regu");
  fontAlias("Times-Italic",               "NimbusRomNo9L-ReguItal");
  fontAlias("Times-Bold",                 "NimbusRomNo9L-Medi");
  fontAlias("Times-BoldItalic",           "NimbusRomNo9L-MediItal");
  
  fontAlias("Helvetica",                  "NimbusSanL-Regu");
  fontAlias("Helvetica-Oblique",          "NimbusSanL-ReguItal");
  fontAlias("Helvetica-Bold",             "NimbusSanL-Bold");
  fontAlias("Helvetica-BoldOblique",      "NimbusSanL-Regu");
  
  fontAlias("Helvetica-Narrow",           "NimbusSanL-ReguCond");
  fontAlias("Helvetica-Narrow-Oblique",   "NimbusSanL-ReguCondItal");
  fontAlias("Helvetica-Narrow-Bold",             "NimbusSanL-BoldCond");
  fontAlias("Helvetica-Narrow-BoldOblique",      "NimbusSanL-ReguCond");
  
  fontAlias("Palatino-Roman",             "URWPalladioL-Roma");
  fontAlias("Palatino-Italic",            "URWPalladioL-Ital");
  fontAlias("Palatino-Bold",              "URWPalladioL-Bold");
  fontAlias("Palatino-BoldItalic",        "URWPalladioL-BoldItal");
  
  fontAlias("AvantGarde-Book",            "URWGothicL-Book");
  fontAlias("AvantGarde-BookOblique",     "URWGothicL-BookObli");
  fontAlias("AvantGarde-Demi",            "URWGothicL-Demi");
  fontAlias("AvantGarde-DemiOblique",     "URWGothicL-DemiObli");
  
  fontAlias("NewCenturySchlbk-Roman",     "CenturySchL-Roma"); 
  fontAlias("NewCenturySchlbk-Italic",    "CenturySchL-Ital"); 
  fontAlias("NewCenturySchlbk-Bold",      "CenturySchL-Bold"); 
  fontAlias("NewCenturySchlbk-BoldItalic","CenturySchL-BoldItal"); 
  
  
  fontAlias("Symbol",                     "StandardSymL");
  
  fontAlias("ZapfChancery-MediumItalic",  "URWChanceryL-MediItal");
  
  fontAlias("ZapfDingbats",               "Dingbats");


  // read font list from fontconfig
  // mostly following the procedure in fc-list.c
  if(!FcInit())
    throw std::runtime_error("Failed to initialize fontconfig");
  FcPattern *pat = FcPatternCreate();
  FcObjectSet *os = FcObjectSetBuild(FC_FILE, 0);
  FcFontSet *fs = FcFontList(0, pat, os);
  if(os)
    FcObjectSetDestroy(os);
  if(pat)
    FcPatternDestroy(pat);
  if(fs) {
    for(int i = 0; i < fs->nfont; i++) {
      FcChar8 *_filename;
      if(FcPatternGetString(fs->fonts[i], FC_FILE, 0, &_filename)
         == FcResultMatch) {
        std::string filename(reinterpret_cast<char*>(_filename));
        std::string ext = suffix(filename);
        try {
          if(ext == "pfa" // || ext == "gsf"
             || ext == "pfb") { // Type 1
            // if there is no AFM file, don't bother
            std::string afmfilename = no_suffix(filename) + ".afm";
            if(std::ifstream(afmfilename.c_str())) {
              checkOutType1(filename);
              checkOutAFM(afmfilename);
              // make sure it is in the path list
              addPath(fontpaths, filename);
            }
          } else if(ext == "ttf") { // TrueType
            checkOutTrueType(filename);
            // make sure it is in the path list
            addPath(fontpaths, filename);
          } // else ignore
        }
        catch(const std::exception &e) {
          warning << e.what() << std::endl;
        }
      }
    }
    FcFontSetDestroy(fs);
  } else {
    warning << "Didn't get any fonts from fontconfig" << std::endl;
  }
}

using std::string;

/// \todo should we really allow more than one font to
/// have the same alias?
string
00143 FontManager::unalias(const string &fontname, bool recursive) const {
  FontAliases::const_iterator i = fontaliases.find(fontname);
  if(i == fontaliases.end())
    return fontname;
  else if(recursive)
    return unalias(i->second); // recursive aliases possible
  else
    return i->second;
}

void
00154 FontManager::fontAlias(const string &alias, const string &fontname) {
  fontaliases.insert(std::make_pair(alias, fontname));
}

FontManager::FontNames
00159 FontManager::getAvailableFonts() const {
  FontNames fontnames;
  for(FontFiles::const_iterator i = fontfiles.begin();
      i != fontfiles.end();
      i++)
    fontnames.insert(i->first);
  return fontnames;
}

void
FontManager::reportFonts(std::ostream &out) const {
  using std::endl;
  out << "Fonts:" << endl;
  for(FontFiles::const_iterator i = fontfiles.begin();
      i != fontfiles.end();
      i++)
    out << "\t   Name: " << i->first << endl
      << "\tMetrics: " << i->second.first << endl
      << "\t   File: " << i->second.second << endl << endl;

  out << endl << "Aliases:" << endl;
  for(FontAliases::const_iterator i = fontaliases.begin();
      i != fontaliases.end();
      i++)
    out << "\t" << i->first << ": " << i->second << endl;
} 

namespace {
  /** A getline that strips whitespace. */
  std::istream &getline_nows(std::istream &in, std::string &line) {
    std::istream &tmp = safe_getline(in, line);
    line = strip_whitespace(line);
    return tmp;
  }
}

void
FontManager::checkOutTrueType(const string &filename) {
  FTMetrics font(filename);
  std::string fontname = font.getPostscriptName();
  if(fontname.empty())
    throw std::runtime_error("The font does not have a PostScript name");
  FontFiles::iterator i = fontfiles.find(fontname);
  // TrueType doesn't have a separate metrics file
  if(i == fontfiles.end())
    fontfiles[fontname] = std::make_pair(filename, filename);
}

void
FontManager::checkOutAFM(const string &filename) {
  // we could use an AFMetrics object instead, but the constructor is
  // rather slow
  std::ifstream in(filename.c_str());
  if(!in)
    throw FontError("Failed to open " + filename);
  string tmp;
  getline_nows(in, tmp);
  if(!starts_with(tmp, "StartFontMetrics"))
    throw FontError(filename + " is not an AFM");
  while(getline_nows(in, tmp))
    if(starts_with(tmp, "FontName")) {
      string fontname(strip_whitespace(tmp.substr(9), true, true));
      FontFiles::iterator i = fontfiles.find(fontname);
      if(i == fontfiles.end())
        fontfiles[fontname] = std::make_pair(filename, string());
      else if(i->second.first.empty()) // don't overwrite
        i->second.first = filename;
      return;
    }
  throw FontError(" has no FontName");
}

void
FontManager::checkOutType1(const string &filename) {
  std::ifstream in(filename.c_str());
  if(!in)
    throw FontError("Failed to open " + filename);
  string tmp;
  getline_nows(in, tmp);
  while(in && !starts_with(tmp, "/FontName"))
    getline_nows(in, tmp);
  if(starts_with(tmp, "/FontName")) {
    string fontname(tmp.substr(tmp.find('/', 1) + 1));
    fontname = fontname.substr(0, fontname.find(' '));
    FontFiles::iterator i = fontfiles.find(fontname);
    if(i == fontfiles.end())
      fontfiles[fontname] = std::make_pair(string(), filename);
    else if(i->second.second.empty() 
          || (suffix(filename) == "pfa" 
            && suffix(i->second.second) == "pfb")) // prefer pfa over pfb
      i->second.second = filename;
  } else {
    throw FontError(filename + " has no FontName");
  }
}

FontManager::FilePair
FontManager::getFontFiles(const string &fontname) const {
  FontFiles::const_iterator i = fontfiles.find(fontname);
  string filename;
  if(i != fontfiles.end())
    return std::make_pair(i->second.first, i->second.second);
  else {
    FontAliases::const_iterator l = fontaliases.lower_bound(fontname);
    FontAliases::const_iterator u = fontaliases.upper_bound(fontname);
    while(l != u){
      try { return getFontFiles(l->second); }
      catch(FontError&) {}
      l++;
    }
  }
  throw FontError("No font files found for " + fontname);
}

void
FontManager::loadFont(const string &fontname) {
  if(metrics_map.find(fontname) != metrics_map.end())
    throw FontError("Font metrics for " + fontname
                + " have already been loaded");
  
  const string filename(getFontFiles(fontname).first);
  std::ifstream metricsfile(filename.c_str());
  
  if(!metricsfile)
    throw FontError("Font metrics file " + filename + " not found for "
                + fontname);
  
  std::auto_ptr<font::Metrics> metrics;
  const string suff = suffix(filename);
  if(suff == "afm")
    metrics.reset(new font::AFMetrics(filename));
  else if(suff == "ttf")
    metrics.reset(new font::FTMetrics(filename));
  else
    throw FontError("\"" + suff
                + "\" is not a known font metrics suffix");
  metrics_map[fontname] = metrics.release();
}

const font::Metrics&
00299 FontManager::getFont(const string &fontname){
  MetricsMap::const_iterator i = metrics_map.find(fontname);
  if(i == metrics_map.end()) {
    loadFont(fontname);
    i = metrics_map.find(fontname);
    if(i == metrics_map.end()) // should never happen, but doesn't hurt
      throw FontError("No font metrics found for " + fontname);
  }
  return *i->second;
}
}

Generated by  Doxygen 1.6.0   Back to index