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

xly.cc

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

#include <stdexcept>
#include <algorithm>
#include <memory>       // auto_ptr

#include <util/stringutil.h>
#include <util/valueunit.h>

// - - - Attributes - - -

Glib::ustring
xml2ps::Attributes::get(const Glib::ustring& name, 
                  const Glib::ustring& defaultvalue) 
  const 
{
  using namespace xmlpp;
  SaxParser::AttributeList::const_iterator i =
    std::find_if(p_.begin(), p_.end(), SaxParser::AttributeHasName(name));
  return i != p_.end() ? Glib::ustring(i->value) : defaultvalue;
}

float
xml2ps::Attributes::get(const Glib::ustring& name, const float& defaultvalue,
                  const float embase) const {
  using namespace xmlpp;
  SaxParser::AttributeList::const_iterator i =
    std::find_if(p_.begin(), p_.end(), SaxParser::AttributeHasName(name));
  if ( i != p_.end() ) {
    ValueUnit<float> value = to<ValueUnit<float> >(i->value);
    if(value.unit() == "" || value.unit() == "pt")
      return value.value();
    else if(value.unit() == "%")
      return 0.01 * value.value() * defaultvalue;
    else if(value.unit() == "em"
          // Ugly workaround: On some systems float reading eats the "e".
          || value.unit() == "m") {
      if(embase != 0)
      return value.value() * embase;
      else return value.value() * defaultvalue;
    } else {
      std::cerr << "Bad unit: ''" <<  value.unit() << "'' in ''"
            << i->value << "''" << std::endl;
      throw std::runtime_error("Bad ''unit'': " + value.unit());
    }
  } else
    return defaultvalue;
}

// - - - Node - - -
xml2ps::Node*
xml2ps::Node::nodeBefore() const {
  Node* node = getParent().nodeBefore(this);
  if(node) return node;
  else
    // This is the first node in parent, return node before parent.
    return getParent().nodeBefore();
}

// - - - TextNode - - -
const font::FontInfo&
xml2ps::TextNode::getFont() const { return getParent().getFont(); }

// - - - Element - - -

template<>
xml2ps::Element::Align to<xml2ps::Element::Align>(const std::string& a) {
  if(a == "left")    return xml2ps::Element::left;
  if(a == "justify") return xml2ps::Element::justify;
  if(a == "right")   return xml2ps::Element::right;
  if(a == "center")  return xml2ps::Element::center;
  throw std::runtime_error("Bad alignment \"" + a + "\"");
}

namespace {
  font::FontInfo getFont(const xml2ps::Attributes& attr, 
                   xml2ps::Element& parent) {
    const Glib::ustring name(attr.get("font-family", parent.getFontName()));
    const float size(attr.get("font-size", 
                        parent.getFontSize(), parent.getFontSize()));
    const float letter_spacing(attr.get("letter-spacing", 0, size));
    return font::FontInfo(name, size, letter_spacing);
  }
}

xml2ps::Element::Element(Element& parent, const Glib::ustring& n, 
                   const Attributes& attr)
  : Node(&parent), name(n), 
    font_info(::getFont(attr, parent)),
    align(to<Align>(attr.get("align", "left"))),
    underline(attr.get("underline", 0) > 0),
    baseline(attr.get("baseline", 0, font_info.getSize())),
    gray(attr.get("gray", 0))
{}

xml2ps::Element::Element(Element& parent, const Glib::ustring& n, 
                   const font::FontInfo& fi)
  : Node(&parent), name(n), font_info(fi),
    align(parent.getAlign()), underline(false), baseline(0)
{}

void xml2ps::Element::add(Node* node) {
  nodes.push_back(node);
}
void xml2ps::Element::debug(std::ostream& out, bool nl) {
  getParent().debug(out, false);
  out << '|' << name;
  if(nl) out << '>' << std::endl;
}
void xml2ps::Element::close() {
//   cerr << '/';
//   debug(cerr);
}

xml2ps::Node*
xml2ps::Element::nodeBefore(const xml2ps::Node* node) const {
  if(!node) return Node::nodeBefore();
  NodeVect::const_iterator i = find(nodes.begin(), nodes.end(), node);
  if(i == nodes.end()) throw std::runtime_error("Node before non-child node");
  
  if(i == nodes.begin()) return 0;
  return *(--i);
};

float
xml2ps::Element::getWidth() const {
  float width = 0;
  for(NodeVect::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
    width += (*i)->getWidth();

  /// \todo Add support for margins / padding ...
  return width;
}

namespace {
  // Special marker-object
  class Underline {
  public:
    Underline(xml2ps::Canvas &c, const font::FontInfo& font)
      : canvas(c),
      pos(font.getUnderlinePos()), thick(font.getUnderlineThickness())
      { canvas.underlineFrom(pos); }
    ~Underline() {
      canvas.underlineTo(pos, thick);
    }
  private:
    xml2ps::Canvas& canvas;
    float pos, thick;
  };
  
  class BaseShift {
  public:
    BaseShift(xml2ps::Canvas& c, const float& shift)
      : canvas(c), oldvalue(canvas.getRise())
    {
      canvas.textRise(oldvalue + shift);
    }
    ~BaseShift() { canvas.textRise(oldvalue); }
  private:
    xml2ps::Canvas& canvas;
    float oldvalue;
  };
}

xml2ps::Element::CharSpaceCount
xml2ps::Element::countChars(const xml2ps::Node* from, const xml2ps::Node* to)
  const 
{
  int chars = 0, spaces = 0;
  const Node* actualfrom = from;
  while(actualfrom && &actualfrom->getParent() != this)
    actualfrom = &actualfrom->getParent();
  NodeVect::const_iterator start = (!actualfrom
                            ? nodes.begin()
                            : find(nodes.begin(), nodes.end(), 
                                 actualfrom));
  NodeVect::const_iterator end = std::find(start, nodes.end(), to);
  if(end != nodes.end()) ++end;     // to is inclusive!

  const Node* n = 0;
  for(NodeVect::const_iterator i = start; i != end; ++i) {
    n = *i;
    if(dynamic_cast<WhiteSpaceNode*>(*i)) {
      ++spaces;
      
    } else if(TextNode* tn = dynamic_cast<TextNode*>(*i)) {
      chars += tn->getContent().length();
      
    } else if(Element *elem = dynamic_cast<Element*>(*i)) {
      if(from == actualfrom) from = 0; // level of the from
      CharSpaceCount t = elem->countChars(from, to);
      chars += t.first;
      spaces += t.second;
    }
    from = 0;
  }
  return std::make_pair(chars, spaces);
}

const xml2ps::Node* 
xml2ps::Element::printPart(Canvas& canvas, const Node* from, const Node* to,
                     const float& whitewidth, const float& cwidth) const
{
  const Node* actualfrom = from;
  while(actualfrom && &actualfrom->getParent() != this)
    actualfrom = &actualfrom->getParent();
  NodeVect::const_iterator start = (!actualfrom
                            ? nodes.begin()
                            : find(nodes.begin(), nodes.end(), 
                                 actualfrom));
  NodeVect::const_iterator end = std::find(start, nodes.end(), to);
  if(end != nodes.end()) ++end;     // to is inclusive!
  
  const font::FontInfo font = font::FontInfo::WidenFont(getFont(), cwidth);
  canvas.setfont(font);
  canvas.setWordSpace(whitewidth); // after setting font
  canvas.setgray(gray);
  std::auto_ptr<Underline> ul(underline? new Underline(canvas, font): 0);
  std::auto_ptr<BaseShift> bs(baseline!=0? new BaseShift(canvas, baseline): 0);
  
  const Node* n = 0;
  for(NodeVect::const_iterator i = start; i != end; ++i) {
    n = *i;
    if(dynamic_cast<WhiteSpaceNode*>(*i)) {
      canvas.whitespace();
      
    } else if(TextNode* tn = dynamic_cast<TextNode*>(*i)) {
      canvas.show(tn->getContent());
      
    } else if(Element *elem = dynamic_cast<Element*>(*i)) {
      if(from == actualfrom) from = 0; // level of the from
      if(elem->printPart(canvas, from, to, whitewidth, cwidth) == to)
      return to;
      
      canvas.setfont(font);
      canvas.setgray(gray);
    }
    from = 0;
  }
  return n;
}

Glib::ustring
xml2ps::Element::d() const { return name + "(" + tostr(nodes.size()) + ")"; }

// - - - PageBreak - - -

xml2ps::PageBreak::PageBreak(Element& parent, Canvas& out)
  : Element(parent, "pagebreak", Attributes(xmlpp::SaxParser::AttributeList()))
{
  out.newPage();
}

// - - - BreakPoint - - -

// noop
const xml2ps::Node*
xml2ps::BreakPoint::printPart(xml2ps::Canvas& canvas, 
                        const xml2ps::Node* from, const xml2ps::Node* to,
                        const float& whitewidth, const float& cwidth)
  const
{
  return this;
}

// - - - LeaderNode - - -

xml2ps::LeaderNode::LeaderNode(Element& parent, const Attributes& attr)
  : Element(parent, "leader", attr), 
    /// \todo Get column width for percentages
    width_(attr.get("width", 100 /*%*/, getFont().getSize()))
{}


// Neither the whitespace- or the char width is of any concern, and there are
// no child nodes.
const xml2ps::Node*
xml2ps::LeaderNode::printPart(xml2ps::Canvas& canvas, 
                        const xml2ps::Node*, const xml2ps::Node*,
                        const float&, const float&) const {
  canvas.whitespace(width_);
  return this;
}

// - - - ObstacleNode - - -

xml2ps::ObstacleNode::ObstacleNode(Element& parent, const Attributes& attr)
  : Element(parent, "obstacle", attr),
    /// \todo Get column width for percentages
    left_(attr.get("left", 0, getFont().getSize())),
    right_(attr.get("right", 0, getFont().getSize())),
    top_(attr.get("top", 0, getFont().getSize())),
    bottom_(attr.get("bottom", 0, getFont().getSize()))
{}

const xml2ps::Node* 
xml2ps::ObstacleNode::printPart(Canvas& canvas, 
                        const Node* /*from*/, const Node* /*to*/,
                        const float& /*ww*/, const float& /*cw*/)
  const
{
  canvas.addRelObstacle(left_, bottom_, right_, top_);
  return this;
}

// - - - LineBreak - - -

// noop
const xml2ps::Node*
xml2ps::LineBreak::printPart(xml2ps::Canvas& canvas, 
                       const xml2ps::Node* from, const xml2ps::Node* to,
                       const float& whitewidth, const float& cwidth)
  const
{
  return this;
}

// - - - PageNum - - -

xml2ps::PageNum::PageNum(Element& parent, const Attributes& attr)
  : Element(parent, "pagenum", parent.getFont())
{
  type = ARABIC;
  if(attr.get("type") == "roman-lower")
    type = ROMAN_LOWER;
  else if(attr.get("type") == "roman-upper")
    type = ROMAN_UPPER;
}

float
xml2ps::PageNum::getWidth() const {
  TextNode textnode(getParent(), getStr(1976));
  return textnode.getWidth();
}

const xml2ps::Node*
xml2ps::PageNum::printPart(xml2ps::Canvas& canvas, 
                     const xml2ps::Node*, const xml2ps::Node*,
                     const float&, const float&) const {
  canvas.show(getStr(canvas.getCurrentPage().num()));
  return this;
}

std::string 
xml2ps::PageNum::getStr(int num) const {
  switch(type) {
  case ROMAN_LOWER: return to_roman(num); break;
  case ROMAN_UPPER: return Glib::ustring(to_roman(num)).uppercase(); break;
  default: return tostr(num);
  }
}

// - - TextContainer - -

void xml2ps::TextContainer::add(Node* node) {
  makeTextParts();
  Element::add(node);
}
void xml2ps::TextContainer::close() {
  makeTextParts();
  Element::close();
}

namespace {

  bool hasWhite(Glib::ustring s) {
    /// \todo unicode whitespace
    return s.length() > 0 && whitespace(s[0]);
  }

}

void xml2ps::TextContainer::makeTextParts() {
  if(hasWhite(text) && !nodes.empty()) 
    {
      Element::add(new WhiteSpaceNode(*this));
    }
  
  int start = -1;
  unsigned int i = 0;
  while(whitespace(text[i]))
    i++;
  for(; i < text.length(); i++) {
    if(whitespace(text[i])) {
      if(start >= 0){
      Element::add(new TextNode(*this, text.substr(start, i - start)));
      }
      start = -1;
      // one whitespace is enough
      while(whitespace(text[i]) && i < text.length())
      ++i;
      Element::add(new WhiteSpaceNode(*this));
    }
    if(start < 0 && i < text.length()) start = i;
  }
  if(start >= 0) {
    Element::add(new TextNode(*this, 
                        text.substr(start, text.length() - start)));
  }
  text = "";
}

Generated by  Doxygen 1.6.0   Back to index