/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include "canvas.hh" #include "util/rectboundary.h" #include #include #include xml2ps::Canvas::Canvas(const xml2ps::Canvas::PageVec& pages, bool allow_extra_pages) :pages_(pages), cur_page_(0), allow_extra_pages_(allow_extra_pages), substitute_font_aliases(false) {} xml2ps::Canvas::~Canvas() {} void xml2ps::Canvas::newPage() { PageVec::iterator op = cur_page_; if(cur_page_ == PageVec::iterator(0)) cur_page_ = pages_.begin(); else ++cur_page_; if(cur_page_ == pages_.end()) { if(!allow_extra_pages_) throw OutOfPages(); if(op == PageVec::iterator(0)) throw std::runtime_error("No pages specified"); cur_page_ = op; } cur_column = 0; vpos = height_ = cur_page_->height(); margin = 0; } void xml2ps::Canvas::setfont(const font::FontInfo& font) { used_fonts.insert(font.getName()); } const float& xml2ps::Canvas::down(const float& step) { vpos -= margin; margin = 0; return vpos -= step; } void xml2ps::Canvas::reserveHeight(const float& h, bool span) { if ((vpos - margin) < h) { if(++cur_column >= cur_page_->columns()) { newPage(); } else vpos = height_; } if (span) height_ -= h; } void xml2ps::Canvas::addMargin(const float& m) { margin = std::max(margin, m); } namespace { class IntersectAdapter { public: IntersectAdapter(xml2ps::HBox& hb) : hbox(hb) {} void operator() (const Boundary o) { if(!o) return; const Polygon p = o->get_polygon(); if(p.empty()) return; float y1 = hbox.top(), y2 = hbox.bottom(), x_left = hbox.left(), x_right = hbox.right(); for(size_t i = 0; i < p.size(); ++i) { const Vector from(p[i]), to(p[(i+1)%p.size()]); float d_f1 = y1 - from.y, d_t1 = y1 - to.y; float d_f2 = y2 - from.y, d_t2 = y2 - to.y; if((d_f1 * d_t1 < 0) || (d_f2 * d_t2 < 0)) { // The line crosses the "extended baseline". float x1 = from.x + (to.x - from.x)/(to.y-from.y) * d_f1; float x2 = from.x + (to.x - from.x)/(to.y-from.y) * d_f2; if((d_f1 <= 0) || (d_f2 <= 0)) // Line goes "down"; left of obstacle x_left = std::max(x_left, std::max(x1, x2)); if((d_f1 >= 0) || (d_f2 >= 0)) // Line goes "up"; right of obstacle x_right = std::min(x_right, std::min(x1, x2)); } } // The obstacle is from x_left to x_right at this height const float leftside = x_left - hbox.left(); const float rightside = hbox.right() - x_right; if(leftside < 0 || rightside < 0) return; if(leftside > rightside) hbox.inRight(rightside); else hbox.inLeft(leftside); } private: xml2ps::HBox& hbox; }; } void xml2ps::Canvas::addRelObstacle(const float left, const float bottom, const float right, const float top) { const float x0 = cur_page_->left(cur_column); cur_page_->addObstacle(RectBoundary::create (Matrix::translation(x0+left, vpos-bottom), x0+right, vpos-top)); } xml2ps::HBox xml2ps::Canvas::hbox(bool span, float margin_left, float margin_right, float ascender, float descender) { const float minimum_width = 10.0;/// \todo Don't hardcode this! HBox result; do { reserveHeight(ascender + descender, span); const float left = cur_page_->left(cur_column); down(ascender); result = HBox(vpos, ascender, descender, left, left + cur_page_->columnWidth(span), margin_left, margin_right); down(descender); for_each(cur_page_->obegin(), cur_page_->oend(), IntersectAdapter(result)); } while(result.room() < minimum_width); return result; }