/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include #include "xly.hh" #include #include #include // auto_ptr #include #include // - - - 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 value = to >(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(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(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(*i)) { ++spaces; } else if(TextNode* tn = dynamic_cast(*i)) { chars += tn->getContent().length(); } else if(Element *elem = dynamic_cast(*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 ul(underline? new Underline(canvas, font): 0); std::auto_ptr 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(*i)) { canvas.whitespace(); } else if(TextNode* tn = dynamic_cast(*i)) { canvas.show(tn->getContent()); } else if(Element *elem = dynamic_cast(*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 = ""; }