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

pdf.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "pdf.h"
#include "pfb2pfa.h"
#include "fonts/fontmetrics.hh"
#include "fonts/fontmanager.hh"
#include "fonts/freetype.hh" /// \todo remove
#include "util/filesys.h"
#include "util/stringutil.h"
#include "util/warning.h"
#include <string>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <glibmm/convert.h>
#include <fstream>


namespace {
  const char* standard_fonts[] = {
    "Times-Roman",
    "Times-Bold",
    "Times-Italic",
    "Times-BoldItalic",
    "Helvetica",
    "Helvetica-BoldOblique",
    "Helvetica-Oblique",
    "Helvetica-Bold",
    "Courier",
    "Courier-Bold",
    "Courier-Oblique",
    "Courier-BoldOblique",
    "Symbol",
    "ZapfDingbats",
    0
  };

  /// \todo move into FontInfo or something
  enum FontType { TYPE1, TRUETYPE };
}

/// \todo move this into FontInfo itself
FontType getFontType(const font::FontInfo &fontinfo) {
  // find out the font type
  try {
    dynamic_cast<const font::FTMetrics&>(fontinfo.getMetrics());
    return TRUETYPE;
  }
  catch(const std::bad_cast&) {
  }
  return TYPE1; // the type is type1 unless it is truetype
}

namespace PDF {

  std::ostream& operator << (std::ostream& out, Object& obj) {
    return obj.write(out);
  }
  
  ReferencedObject::ReferencedObject(Ref::Ptr ref, Object::Ptr object)
    :obj_(object), ref_(ref)
  {}
  
  Ref::Ptr ReferencedObject::get_ref() const {
    if(!ref_)
      throw std::runtime_error("get_ref for object with no ref yet");
    return ref_;
  }
  
  std::ostream&
  ReferencedObject::write(std::ostream& out) {
    out << ref_->get_num() << ' ' << ref_->get_generation() << " obj\n"
      << obj_
      << "endobj\n";
    
    return out;
  }

  XRefs::Ptr XRefs::create() {
    return Ptr(new XRefs);
  }
  
  XRefs::XRefs() {}
  
  Ref::Ptr
  XRefs::add_object(Object::Ptr object) {
    ReferencedObject::Ptr ro = 
      ReferencedObject::create(Ref::create(objects.size() + 1, 0u), object);
    objects.push_back(ro); 
    
    return ro->get_ref();
  }
  
  std::streampos
  XRefs::get_xref_offset() { return xref_offset; }
  
  Ref::Num
  XRefs::get_num_of_refs() const { return objects.size(); }

  Name::Name(const std::string& name_)
    : name(name_) 
  {}
  
  bool Name::operator < (const Name& x) const {
    return this->name < x.name;
  }
    
  const std::string& Name::get_name() const { return name; }
  
  std::ostream& Name::write(std::ostream& out) {
    out << '/' << name;
    return out;
  }

  template<> std::ostream& String::write(std::ostream& out) {
    out << '(';
    out << value_;            // Todo: Handle special chars!
    out << ')';
    return out;
  }
  
  Object::Ptr Array::push_back(Object::Ptr object) {
    items.push_back(object); 
    return object;
  }
    
  std::ostream& Array::write(std::ostream& out) {
    out << '[';
    for(Items::const_iterator i = items.begin(); i != items.end(); ++i)
      out << *i << ' ';
    out << ']';
    return out;
  }
  
  Array::Ptr rectangle(int llx, int lly, int urx, int ury) {
    Array::Ptr result = Array::create();
    result->push_back(Integer::create(llx));
    result->push_back(Integer::create(lly));
    result->push_back(Integer::create(urx));
    result->push_back(Integer::create(ury));
    return result;
  }
  
  Dictionary::Dictionary() {}
  
  Object::Ptr
  Dictionary::set_entry(const std::string& name, Object::Ptr object) {
    entries[name] = object; 
    return object;
  } 
  
  Object::Ptr
  Dictionary::set_entry_name(const std::string& name, const std::string& value){
    return set_entry(name, Name::create(value));
  }

  Object::Ptr
  Dictionary::set_entry_int(const std::string& name, int value) {
    return set_entry(name, Integer::create(value));
  } 

  const Object::Ptr
  Dictionary::get_entry(const std::string& name) const {
    Entries::const_iterator i = entries.find(name);
    if(i != entries.end())
      return i->second;
    else
      return Object::Ptr();
  }
  
  Object::Ptr
  Dictionary::get_entry(const std::string& name) {
    Entries::iterator i = entries.find(name);
    if(i != entries.end())
      return i->second;
    else
      return Object::Ptr();
  }
  
  std::ostream&
  Dictionary::write(std::ostream& out) {
    out << "<<\n";
    
    for(Entries::const_iterator i = entries.begin(); i != entries.end(); i++) {
      out << '/' << i->first << ' ' << i->second << '\n';
    }
    out << ">>\n";
    return out;
  }
  
  class Resources : public Dictionary {
  public:
    typedef RefCountPtr<Resources> Ptr;
    enum Id { FONTS, XOBJS, N };
    
    static Ptr create() { return Ptr(new Resources); }
    
    Dictionary::Ptr get(Id id, const std::string& name) {
      if(!resource[id]) {
      resource[id] = Dictionary::create();
      set_entry(name, resource[id]);
      }
      return resource[id];
    }
    
    std::string registerXObj(Ref::Ptr object) {
      Dictionary::Ptr res = get(XOBJS, "XObject");
      const std::string name = "X" + tostr(res->size());
      
      res->set_entry(name, object);
      
      return name;
    }
    
    const std::string& registerFont(PDF::Document::FontWithName fwn) {
      Dictionary::Ptr res = get(Resources::FONTS, "Font");
      res->set_entry(fwn.second, fwn.first);
      return fwn.second;
    }

  protected:
    Resources() {
      resource[FONTS] = Dictionary::Ptr();
      resource[XOBJS] = Dictionary::Ptr();
    }
    
  private:
    Dictionary::Ptr resource[N];
  };
  
  Stream::Stream() {
    // Scientific notation is not allowed in PDF.
    /// \note Here I set fixed, but I actually don't want that.  Trailing zero
    /// decimals should be truncated ...
    data_.setf(std::ios_base::fixed, std::ios_base::floatfield);
  }
  
  std::ostream& Stream::write(std::ostream& out) {
      const std::string streamdata = data_.str();
      set_entry_int("Length", streamdata.length());
      Dictionary::write(out);
      out << "stream\n"
        << streamdata
        << "\nendstream\n";
      return out;
  }

  std::string Stream::rawdata() const {
    return data_.str();
  }
  
  class Page: public Dictionary {
  public:
    typedef RefCountPtr<Page> Ptr;
    
    static Ptr create(int width, int height, Ref::Ptr parent, 
                  Document::Ptr document);
    
    Content::Ptr get_content();
    
    /**
     * Make sure a font is loaded in the document, return the font resource
     * name for this page.
     */
    std::string getFont(const font::FontInfo& font);
    
    /**
     * Register an object for use as an XObject.  The object is
     * given a name in the resource dictionary for XObjects.
     * \param object the object
     * \return the XObject name of the object
     */
    std::string registerXObj(Object::Ptr object);
    
    void set_parent(Ref::Ptr ref);

    Document::Ptr document() { return document_; }
    Resources::Ptr resources() { return resources_; }
    
  protected:
    Page(int width, int height, Ref::Ptr parent, Document::Ptr document);
    
  private:
    Document::Ptr document_;
    Content::Ptr content;  
    Resources::Ptr resources_;
    int num_reffed_fonts;
  };

00291   Content::Ptr Content::create(RefCountPtr<Page> page) {
    return Ptr(new Content(page->document(), page->resources()));
  }
  
00295   Content::Ptr Content::create(DocumentPtr document, int width, int height) {
    ResourcesPtr resources = Resources::create();
    Ptr result(new Content(document, resources));
    result->set_entry_name("Type", "XObject");
    result->set_entry_name("Subtype", "Form");
    //set_entry_int("FormType", 1);
    /// \note the 20 point margin is rather arbitrary - it's enough
    /// for the inital in Emissionen :-)
    result->set_entry("BBox", rectangle(-20, -20, width + 20, height + 20));
    result->set_entry("Resources", resources);
    return result;
  }
  
  Content::Content(Document::Ptr doc, Resources::Ptr res)
    : document_(doc), resources(res),
      last_xpos(0), last_ypos(0), cur_charspace(0), cur_wordspace(0),
      cur_size(1)
  {}
  
  std::ostream& Content::write(std::ostream& out) {
    return Stream::write(out);
  }
  
00318   std::string Content::registerXObj(Object::Ptr object) {
    return resources->registerXObj(document_->get_xrefs()->add_object(object));
  }
  
00322   void Content::beginText() {
    data() << "BT\n";
  }
  
00326   void Content::endText() {
    commitText();
    data() << "ET\n";
  }


  void Content::selectfont(const font::FontInfo& font) { 
    commitText();
    cur_size = font.getSize();
    data() << '/' << resources->registerFont(document_->getFontObject(font))
           << ' ' << cur_size << " Tf\n"; 
    // Make sure show() and friends know whether the current font is
    // multi-byte or not
    simple_font = getFontType(font) != TRUETYPE;
  }
  
  void Content::setgray(float gray) { 
    commitText();
    data() << gray << " g\n"; 
  }
  void Content::moveto(float xpos, float ypos) 
  {
    commitText();
    data() << (xpos-last_xpos) << ' ' << (ypos-last_ypos) << " Td\n";
    last_xpos=xpos; last_ypos=ypos;
  }
  void Content::textRise(float rise) {
    commitText();
    data() << rise << " Ts\n";
  }
  
  void Content::setWordSpace(const float& w) {
    if(w != cur_wordspace) {
      commitText();
      cur_wordspace = w;
      if(simple_font)
        // Tw seems to want the space scaled by font size
      data() << w * cur_size << " Tw\n";
    }
  }
  void Content::setCharSpace(const float& w) {
    if(w != cur_charspace) {
      commitText();
      cur_charspace = w;
      // Tc seems to want the space scaled by font size
      data() << w * cur_size << " Tc\n";
    }
  }
  
00375   void Content::show(const Glib::ustring& s) {
    // double byte chars for CID
    if(!simple_font) {
      using namespace std;
      textbuf << " <";
      for(Glib::ustring::const_iterator i = s.begin(); i != s.end(); i++)
        textbuf << hex << setw(4) << setfill('0') << int(*i);
      textbuf << dec << '>';
      return;
    }

    std::ostringstream encoded;

    // MacRoman seems to go by different names in different
    // installations ...
    /// \todo ugly hack
    bool macromanexists = true;
    try {
      Glib::IConv("MacRoman", "UTF-8");
    } catch(...) {
      macromanexists = false;
    }
    Glib::IConv macroman(macromanexists ? "MacRoman" : "Mac", "UTF-8");
    
    // In PDF, backslashes and parentheses need to be escaped
    for(Glib::ustring::const_iterator i = s.begin(); i != s.end(); ++i) {
      switch(*i) {
      case '\\': encoded << "\\\\"; break;
      case '(':  encoded << "\\(";  break;
      case ')':  encoded << "\\)";  break;
      default: 
        try {
          Glib::ustring t(1, *i);
          encoded << macroman.convert(t.raw());
        } catch(const Glib::ConvertError &e) {
          document_->convert_failures++;
          verbose << e.what() << std::endl;
          // PDF doesn't seem to have anything like glyphshow in postscript.
          encoded << '?';
        }
        break;
      }
    }
    textbuf << " (" << encoded.str() << ')';
  } 
  
  void Content::whitespace() {
    // we expect the charspace to be added around the space as well
    if(simple_font) {
      textbuf << " ( )";
      if(cur_charspace)
        textbuf << ' ' << -1000 * (2 * cur_charspace);
    } else { // Tw doesn't work for multibyte fonts
      textbuf << " <0020>";
      if(cur_wordspace)
        textbuf << ' ' << -1000 * (cur_wordspace + 2 * cur_charspace);
    }
  }
  
  void Content::whitespace(float width) {
    textbuf << ' ' << -1000 * width;
  }
  
  DocumentPtr Content::getDocument() { return document_; }

  
  void Content::commitText() {
    if(!textbuf.str().empty()) {
      data() << '[' << textbuf.str() << "] TJ\n";
      textbuf.str("");
    }
  }

  Page::Ptr Page::create(int width, int height, Ref::Ptr parent,
                   Document::Ptr document)
  {
    return Ptr(new Page(width, height, parent, document));
  }
  
  Page::Page(int width, int height, Ref::Ptr parent, Document::Ptr document)
    : document_(document), content(0), resources_(Resources::create()),
      num_reffed_fonts(0)
  {
    set_entry_name("Type", "Page");
    set_entry("Parent", parent);
    set_entry("Resources", resources_);
    set_entry("MediaBox", rectangle(0, 0, width, height));
  }
  
  Content::Ptr Page::get_content() {
    if(!content) {
      content = Content::create(Page::Ptr(this));
      set_entry("Contents", document_->get_xrefs()->add_object(content));
    }
    return content;
  }
    
  std::string Page::getFont(const font::FontInfo& font) {
    return resources_->registerFont(document_->getFontObject(font));
  }
  
  std::string Page::registerXObj(Object::Ptr object) {
    return resources_->registerXObj(document_->get_xrefs()->add_object(object));
  }
  
  void Page::set_parent(Ref::Ptr ref) {
    set_entry("Parent", ref);
  }

  Document::Document()
    : xrefs(XRefs::create()),
      page_tree_root(Dictionary::create()),
      page_tree_root_ref(xrefs->add_object(page_tree_root)),
      convert_failures(0)
  {}

  std::ostream& XRefs::write(std::ostream& out) {
    typedef std::vector<std::streampos> PVec;
    PVec pos;
    for(Objects::iterator i = objects.begin(); i != objects.end(); i++) {
      pos.push_back(out.tellp());
      out << *i;
    }
    objects.clear();
    
    xref_offset = out.tellp();
    
    out << "xref \n0 " << (pos.size()+1) << " \n"
      << std::setw(10) << std::setfill('0') << 0 << ' '
      << std::setw(0) << std::setfill(' ') << "65535 f \n";
    for(PVec::const_iterator i = pos.begin(); i != pos.end(); i++)
      out << std::setw(10) << std::setfill('0') << *i << ' '
        << std::setw(0) << std::setfill(' ') << "00000 n \n";
    
    return out;
  }
  
  Content::Ptr Document::add_page(int width, int height) {
    ///\todo There might be a page tree, its not always just a simple array.
    Page::Ptr page = Page::create(width, height, page_tree_root_ref, 
                          Document::Ptr(this));
    pages.push_back(xrefs->add_object(page));
    return page->get_content();
  }

  std::ostream& Document::write(std::ostream& out) {
    out.setf(std::ios_base::boolalpha);
    {
      // 1.5 was the latest version as of sep 2004, but my acroread was older
      // than that :-)
      const int version_maj = 1, version_min = 4;
      
      out << "%PDF-" << version_maj << '.' << version_min << "\n"
      // Binary (>127) junk comment to make sure that e.g. ftp programs 
      // treat the file as binary data:
        << "%\xff\xff\xff\xff\n";
    }
    
    Dictionary::Ptr catalog = Dictionary::create();
    Ref::Ptr rcatalog = xrefs->add_object(catalog);
    
    catalog->set_entry_name("Type", "Catalog");
    catalog->set_entry("Pages", page_tree_root_ref);

    page_tree_root->set_entry_name("Type",  "Pages");
    page_tree_root->set_entry_int("Count", pages.size());
    Array::Ptr kids = Array::create();
    for(Pages::iterator i = pages.begin(); i != pages.end(); i++)
      kids->push_back(*i);
    page_tree_root->set_entry("Kids", kids);

    xrefs->write(out);
    
    Dictionary::Ptr trailer = Dictionary::create();
    trailer->set_entry_int("Size", xrefs->get_num_of_refs());
    trailer->set_entry("Root", rcatalog);
    out << "trailer\n"
      << trailer
      << "startxref \n" << xrefs->get_xref_offset() << " \n%%EOF\n";
    
    return out;
  }

};

// The PDF Reference counts bits as LSB=1.
inline int PdfBit(int pos) {
  return 1 << (pos - 1);
}

PDF::Stream::Ptr makeFontStream(std::istream& in,
                        const std::string fontfilename) {
  PDF::Stream::Ptr fontstream = PDF::Stream::create();
  
  // Ignore anything before %! in the font file.
  char ch;
  while(in.get(ch) && !((ch == '%') && in.get(ch) && (ch == '!')));
  if(!in)
    throw std::runtime_error("No PS startmark in " + fontfilename);
  fontstream->data() << "%!";
  const std::ifstream::pos_type pos0 = in.tellg() - std::ifstream::off_type(2);
  debug << "pfa: pos0 = " << pos0 << "\n";
  
  // Copy the entire "cleartext part"
  const std::string target = "currentfile eexec\n";
  unsigned int found = 0;
  while(found < target.size() && in.get(ch)) {
    fontstream->data().put(ch);
    if(ch == target[found]) ++found;
    else found = 0;
  }
  if(!in)
    throw std::runtime_error("No eexec in " + fontfilename);
  const std::ifstream::pos_type pos1 = in.tellg();
  debug << "pfa: pos1 = " << pos0 << "\n";

  fontstream->set_entry_int("Length1", pos1 - pos0);
  
  // Copy the rest, which is the eexec part and the trailing zeroes.
  fontstream->data() << in.rdbuf();
  const std::ifstream::pos_type pos3 = in.tellg();
  
  // Here I assume that the trailing zeroes part is allways 513 chars long.
  // That is probably bad.
  fontstream->set_entry_int("Length2", pos3 - pos1 - 531);
  fontstream->set_entry_int("Length3", 531);
  
  return fontstream;
}

PDF::Dictionary::Ptr createCIDSystemInfoDict() {
  PDF::Dictionary::Ptr dict = PDF::Dictionary::create();
  /// \todo I have no idea what these are supposed to be
  dict->set_entry("Registry", PDF::String::create("Adobe"));
  dict->set_entry("Ordering", PDF::String::create("Identity"));
  dict->set_entry("Supplement", PDF::Integer::create(0));
  return dict;
}


PDF::Dictionary::Ptr createFontDescriptorDict(const std::string &fontname,
                                    const font::Metrics &metrics,
                                    PDF::XRefs::Ptr xrefs) {
  // And then the FontDescriptor
  PDF::Dictionary::Ptr descriptor = PDF::Dictionary::create();
  descriptor->set_entry_name("Type", "FontDescriptor");
  descriptor->set_entry_name("FontName", fontname);
  float italicangle = metrics.getItalicAngle();
  /// \todo get these flags right
  descriptor->set_entry_int("Flags",
                            PdfBit(1) * 0 /* Todo: fixed pitch */ +
                            PdfBit(2) * 1 /* Todo: Serif */ + 
                            PdfBit(3) * 0 /* Todo: Symbolic */ +
                            PdfBit(4) * 0 /* Todo: Script */ +
                            PdfBit(6) * 1 /* !Symbolic */ +
                            PdfBit(7) * (italicangle < -3) /* arbitrary */ +
                            PdfBit(17) * 0 /* AllCap */ +
                            PdfBit(18) * 0 /* SmallCap */ +
                            PdfBit(19) * 0 /* don't force bold */);
  descriptor->set_entry("FontBBox", PDF::rectangle(metrics.getBBox(0),
                                                   metrics.getBBox(1),
                                                   metrics.getBBox(2),
                                                   metrics.getBBox(3)));
  descriptor->set_entry("ItalicAngle",   PDF::Real::create(italicangle));
  descriptor->set_entry_int("Ascent",    int(1000 * metrics.getAscender()));
  descriptor->set_entry_int("Descent",   int(1000 * metrics.getDescender()));
  descriptor->set_entry_int("CapHeight", int(1000 * metrics.getCapHeight()));
  descriptor->set_entry_int("XHeight",   int(1000 * metrics.getExHeight()));
  descriptor->set_entry_int("StemV",     0); /// \todo StemV

  // Include font file
  const std::string &fontfilename =
    font::FontManager::instance().getFontFile(fontname);
  const std::string suff = suffix(fontfilename);
  if(suff == "pfa") {
    std::ifstream pfa(fontfilename.c_str());
    descriptor->set_entry("FontFile", makeFontStream(pfa, fontfilename));

  } else if(suff == "pfb") {
    PDF::Stream::Ptr fontstream = PDF::Stream::create();
    std::vector<long> lengths;
    std::ifstream pfb(fontfilename.c_str());
    
    PS::pfb2pfa(pfb, fontstream->data(), &lengths);
    switch(lengths.size()) {
    case 3:
      /// \todo set Length3 to 0 if not present?
      fontstream->set_entry_int("Length3", lengths[2]);
      // no break
    case 2:
      fontstream->set_entry_int("Length2", lengths[1]);
      fontstream->set_entry_int("Length1", lengths[0]);
      break;
    default:
      throw std::runtime_error("Bad number of blocks, " 
                               + tostr(lengths.size())
                               + ", in pfb " + fontfilename);
    }
    descriptor->set_entry("FontFile", xrefs->add_object(fontstream));
      
  } else if(suff == "ttf") { // we only support truetype
    PDF::Stream::Ptr fontstream = PDF::Stream::create();
    std::ifstream ttf(fontfilename.c_str());
    fontstream->data() << ttf.rdbuf();
    // Length1 is the length of the file after it has been decoded by
    // the the filter specified in Filter, but we are not using a
    // filter, so Length1 should be the same as Length
    fontstream->set_entry_int("Length1", fontstream->rawdata().length());
    // Type 1 uses the "FontFile" entry, while TrueType uses the
    // "FontFile2" entry.
    descriptor->set_entry("FontFile2", xrefs->add_object(fontstream));
  } else {
    throw std::runtime_error("Unknown suffix for font file: " 
                             + fontfilename);
  }

  return descriptor;
}

PDF::Dictionary::Ptr createCIDFontDict(const font::FontInfo& fontinfo,
                               PDF::XRefs::Ptr xrefs) {
  PDF::Dictionary::Ptr dict = PDF::Dictionary::create();
  dict->set_entry_name("Type",            "Font");
  // different subtypes depending on whether it2 contains Type1 or TrueType
  dict->set_entry_name("Subtype",
                       getFontType(fontinfo) == TYPE1
                       ? "CIDFontType0"
                       : "CIDFontType2");
  dict->set_entry_name("BaseFont",        fontinfo.getName());
  dict->set_entry("CIDSystemInfo", createCIDSystemInfoDict());
  PDF::Dictionary::Ptr descriptor = 
    createFontDescriptorDict(fontinfo.getName(), fontinfo.getMetrics(), xrefs);
  dict->set_entry("FontDescriptor", xrefs->add_object(descriptor));

  // assume TrueType
  const font::FTMetrics &metrics =
    dynamic_cast<const font::FTMetrics &>(fontinfo.getMetrics());

  if(true) {
    // map CIDs to glyph indices in the TrueType font
    PDF::Stream::Ptr stream = PDF::Stream::create();
    metrics.write_CIDToGIDMap(stream->data());
    dict->set_entry("CIDToGIDMap", xrefs->add_object(stream));
  }

  // character widths
  // The /W array is a list of pairs where the first element is a
  // start CID and the second is an array of widths for the start CID
  // and the following CIDs for which glyphs have been defined.
  PDF::Array::Ptr widths = PDF::Array::create();
  gunichar c = 0;
  while(c < 0xffff) {
    // glyphs with index 0 are .notdef, so skip them
    if(!metrics.get_glyph_index(c)) {
      c++;
      continue;
    }
    
    // start CID
    widths->push_back(PDF::Integer::create(c));
    // an array of widths for consecutive defined glyphs
    PDF::Array::Ptr iwidths = PDF::Array::create();
    while(c < 0xffff && metrics.get_glyph_index(c)) {
      float w = metrics.getWidth(Glib::ustring(1, c));
      iwidths->push_back(PDF::Integer::create(int(1000 * w)));
      c++;
    }
    widths->push_back(iwidths);
  }
  dict->set_entry("W", xrefs->add_object(widths));
  
  return dict;
}

PDF::Stream::Ptr createCMapStream(const font::FontInfo& fontinfo) {
  PDF::Stream::Ptr stream = PDF::Stream::create();
  stream->set_entry_name("Type",    "CMap");
  /// \todo The name of the cmap is foo?!?
  stream->set_entry_name("CMapName",    "foo");
  stream->set_entry("CIDSystemInfo", createCIDSystemInfoDict());
  /// \todo we only support truetype so far
//   dynamic_cast<const font::TTYMetrics &>(fontinfo.getMetrics())
//     .get_mapping_table().write_CMap(stream->data());
  return stream;
}

PDF::Dictionary::Ptr createFont0Dict(const font::FontInfo &fontinfo,
                             PDF::Ref::Ptr cidfontdict,
                             PDF::Ref::Ptr cmap) {
  const std::string& fontname = fontinfo.getName();
  PDF::Dictionary::Ptr dict = PDF::Dictionary::create();
  dict->set_entry_name("Type",    "Font");
  dict->set_entry_name("Subtype",         "Type0");
  dict->set_entry_name("BaseFont",        fontname);

  // Use a cmap if we are given one
  if(cmap)
    dict->set_entry("Encoding", cmap);
  else
    dict->set_entry("Encoding", PDF::Name::create("Identity-H"));

  PDF::Array::Ptr dfonts = PDF::Array::create();
  dfonts->push_back(cidfontdict);
  dict->set_entry("DescendantFonts", dfonts);
  return dict;
}

PDF::Dictionary::Ptr createFontDict(const font::FontInfo& fontinfo,
                            PDF::XRefs::Ptr xrefs) {
  const std::string& fontname = fontinfo.getName();

  // check if the fontname is in the list of standard fonts
  bool is_standard_font = false;
  for(const char **sf = &standard_fonts[0]; *sf; sf++)
    if(fontname == *sf) {
      is_standard_font = true;
      break;
    }
  
  FontType font_type = getFontType(fontinfo);

  // First the basics, needed for all fonts.
  PDF::Dictionary::Ptr dict = PDF::Dictionary::create();
  dict->set_entry_name("Type",     "Font");
  dict->set_entry_name("Subtype",  font_type == TYPE1 ? "Type1" : "TrueType");
  dict->set_entry_name("BaseFont", fontname);
  dict->set_entry_name("Encoding", "MacRomanEncoding");
  
  // If it's one of the 14 standard fonts, we don't have to do more.
  if(is_standard_font)
    return dict;

  // glyph metrics
  PDF::Array::Ptr widths = PDF::Array::create();
  const font::Metrics& metrics(fontinfo.getMetrics());
  // MacRoman seems to go by different names in different
  // installations ...
  /// \todo ugly hack and duplicated from Content::show
  bool macromanexists = true;
  try {
    Glib::IConv("MacRoman", "UTF-8");
  } catch(...) {
    macromanexists = false;
  }
  Glib::IConv frommacroman("UTF-8", macromanexists ? "MacRoman" : "Mac");

  for(unsigned char c = 32; c <= 254; ++c) {
    try {
      Glib::ustring converted = frommacroman.convert(std::string(1, c));
      float w = metrics.getWidth(converted);
      widths->push_back(PDF::Integer::create(int(1000 * w)));
    } catch(const Glib::ConvertError &e) {
      warning << "Failed to convert char #" << int(c) << " from MacRoman"
              << std::endl;
      // PDF doesn't seem to have anything like glyphshow in postscript.
      widths->push_back(PDF::Integer::create(0));
    }
  }
  dict->set_entry_int("FirstChar", 32);
  dict->set_entry_int("LastChar", 254);
  dict->set_entry("Widths", widths);
    
  PDF::Dictionary::Ptr descriptor(createFontDescriptorDict(fontname, metrics,
                                             xrefs));
  dict->set_entry("FontDescriptor", descriptor);
  // Optional: ToUnicode
  
  return dict;
}

PDF::Document::FontWithName
PDF::Document::getFontObject(const font::FontInfo& font) {
  Fonts::const_iterator i = used_font.find(font.getName());
  if(i != used_font.end())
    return i->second;

  Dictionary::Ptr fdict;
  // we only support CIDs for TrueType for now
  if(getFontType(font) == TRUETYPE) {
    Ref::Ptr cmap; //xrefs.add_object(createCMapStream(font));
    Ref::Ptr cidfontdict(xrefs->add_object(createCIDFontDict(font, xrefs)));
    fdict = createFont0Dict(font, cidfontdict, cmap);
  } else { // Type1 as simple font
    fdict = createFontDict(font, xrefs);
  }

  FontWithName fwn(xrefs->add_object(fdict),
                   "F" + tostr(used_font.size()+1));
  used_font.insert(std::pair<std::string, FontWithName>(font.getName(), fwn));
  return used_font[font.getName()];
}


#ifdef STAND_ALONE
int main(int argc, char *argv[]) {
  PDF::Document document;
  document.add_page(200, 300);
  document.selectfont(20);
  document.moveto(20, 20);
  document.show("Hello, World!");
  std::cerr << document.write(&std::cout) << " bytes written" << std::endl;
}
#endif

Generated by  Doxygen 1.6.0   Back to index