summaryrefslogtreecommitdiffstats
path: root/src/display/drawing-text.cpp
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2011-08-06 12:18:32 +0000
committerKrzysztof Kosiński <tweenk.pl@gmail.com>2011-08-06 12:18:32 +0000
commit4dd33aa4d5c57706c7f64f63391174954160a308 (patch)
treeb9d4ea1278348836a718c5e6aaeaff9f82c790bf /src/display/drawing-text.cpp
parentPer-item render cache. (diff)
downloadinkscape-4dd33aa4d5c57706c7f64f63391174954160a308.tar.gz
inkscape-4dd33aa4d5c57706c7f64f63391174954160a308.zip
Rewrite NRArenaItem hierarchy into C++
(bzr r10347.1.21)
Diffstat (limited to 'src/display/drawing-text.cpp')
-rw-r--r--src/display/drawing-text.cpp275
1 files changed, 275 insertions, 0 deletions
diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp
new file mode 100644
index 000000000..784888bd7
--- /dev/null
+++ b/src/display/drawing-text.cpp
@@ -0,0 +1,275 @@
+/**
+ * @file
+ * @brief Group belonging to an SVG drawing element
+ *//*
+ * Authors:
+ * Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2011 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "display/cairo-utils.h"
+#include "display/canvas-bpath.h" // for SPWindRule (WTF!)
+#include "display/drawing-context.h"
+#include "display/drawing-surface.h"
+#include "display/drawing-text.h"
+#include "display/nr-arena.h"
+#include "helper/geom.h"
+#include "libnrtype/font-instance.h"
+#include "style.h"
+
+namespace Inkscape {
+
+DrawingGlyphs::DrawingGlyphs(Drawing *drawing)
+ : DrawingItem(drawing)
+ , _glyph_transform(NULL)
+ , _font(NULL)
+ , _glyph(0)
+{}
+
+DrawingGlyphs::~DrawingGlyphs()
+{
+ if (_font) {
+ _font->Unref();
+ _font = NULL;
+ }
+ delete _glyph_transform;
+}
+
+void
+DrawingGlyphs::setGlyph(font_instance *font, int glyph, Geom::Affine const &trans)
+{
+ _markForRendering();
+
+ if (trans.isIdentity()) {
+ delete _glyph_transform; // delete NULL; is safe
+ _glyph_transform = NULL;
+ } else {
+ _glyph_transform = new Geom::Affine(trans);
+ }
+
+ if (font) font->Ref();
+ if (_font) _font->Unref();
+ _font = font;
+ _glyph = glyph;
+
+ _markForUpdate(STATE_ALL, false);
+}
+
+unsigned
+DrawingGlyphs::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
+{
+ DrawingText *ggroup = dynamic_cast<DrawingText *>(_parent);
+ if (!ggroup) throw InvalidItemException();
+
+ if (!_font || !ggroup->_style) return STATE_ALL;
+ if (ggroup->_nrstyle.fill.type == NRStyle::PAINT_NONE &&
+ ggroup->_nrstyle.stroke.type == NRStyle::PAINT_NONE)
+ {
+ return STATE_ALL;
+ }
+
+ Geom::OptRect b;
+ Geom::Affine t = _glyph_transform ? *_glyph_transform * ctx.ctm : ctx.ctm;
+ _x = t[4];
+ _y = t[5];
+
+ b = bounds_exact_transformed(*_font->PathVector(_glyph), t);
+ if (b && ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE) {
+ float width, scale;
+ scale = ctx.ctm.descrim();
+ width = MAX(0.125, ggroup->_nrstyle.stroke_width * scale);
+ if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true
+ b->expandBy(width);
+ }
+ // those pesky miters, now
+ float miterMax = width * ggroup->_nrstyle.miter_limit;
+ if ( miterMax > 0.01 ) {
+ // grunt mode. we should compute the various miters instead
+ // (one for each point on the curve)
+ b->expandBy(miterMax);
+ }
+ }
+
+ if (b) {
+ _bbox = b->roundOutwards();
+ } else {
+ _bbox = Geom::OptIntRect();
+ }
+
+ return STATE_ALL;
+}
+
+DrawingItem *
+DrawingGlyphs::_pickItem(Geom::Point const &p, double delta)
+{
+ if (!_font || !_bbox) return NULL;
+
+ // With text we take a simple approach: pick if the point is in a characher bbox
+ Geom::Rect expanded(*_bbox);
+ expanded.expandBy(delta);
+ if (expanded.contains(p)) return this;
+ return NULL;
+}
+
+
+
+DrawingText::DrawingText(Drawing *drawing)
+ : DrawingGroup(drawing)
+{}
+
+DrawingText::~DrawingText()
+{}
+
+void
+DrawingText::clear()
+{
+ _markForRendering();
+ _children.clear_and_dispose(DeleteDisposer());
+}
+
+void
+DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans)
+{
+ if (!font || !font->PathVector(glyph)) return;
+
+ _markForRendering();
+ DrawingGlyphs *ng = new DrawingGlyphs(_drawing);
+ ng->setGlyph(font, glyph, trans);
+ appendChild(ng);
+}
+
+void
+DrawingText::setStyle(SPStyle *style)
+{
+ _nrstyle.set(style);
+ DrawingGroup::setStyle(style);
+}
+
+void
+DrawingText::setPaintBox(Geom::OptRect const &box)
+{
+ _paintbox = box;
+ _markForUpdate(STATE_ALL, false);
+}
+
+unsigned
+DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
+{
+ _nrstyle.update();
+ return DrawingGroup::_updateItem(area, ctx, flags, reset);
+}
+
+void
+DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags)
+{
+ if (_drawing->rendermode == RENDERMODE_OUTLINE) {
+ DrawingContext::Save save(ct);
+ guint32 rgba = _drawing->outlinecolor;
+ ct.setSource(rgba);
+ ct.setTolerance(1.25); // low quality, but good enough for outline mode
+ ct.newPath();
+ ct.transform(_ctm);
+
+ for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
+ DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i);
+ if (!g) throw InvalidItemException();
+
+ Inkscape::DrawingContext::Save save(ct);
+ if (g->_glyph_transform) {
+ ct.transform(*g->_glyph_transform);
+ }
+ ct.path(*g->_font->PathVector(g->_glyph));
+ ct.fill();
+ }
+ return;
+ }
+
+ // NOTE: this is very similar to drawing-shape.cpp; the only difference is in path feeding
+ bool has_stroke, has_fill;
+
+ Inkscape::DrawingContext::Save save(ct);
+ ct.transform(_ctm);
+
+ has_fill = _nrstyle.prepareFill(ct, _paintbox);
+ has_stroke = _nrstyle.prepareStroke(ct, _paintbox);
+
+ if (has_fill || has_stroke) {
+ for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
+ DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i);
+ if (!g) throw InvalidItemException();
+
+ Inkscape::DrawingContext::Save save(ct);
+ if (g->_glyph_transform) {
+ ct.transform(*g->_glyph_transform);
+ }
+ ct.path(*g->_font->PathVector(g->_glyph));
+ }
+
+ if (has_fill) {
+ _nrstyle.applyFill(ct);
+ ct.fillPreserve();
+ }
+ if (has_stroke) {
+ _nrstyle.applyStroke(ct);
+ ct.strokePreserve();
+ }
+ ct.newPath(); // clear path
+ }
+}
+
+void
+DrawingText::_clipItem(DrawingContext &ct, Geom::IntRect const &area)
+{
+ Inkscape::DrawingContext::Save save(ct);
+
+ // handle clip-rule
+ if (_style) {
+ if (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) {
+ ct.setFillRule(CAIRO_FILL_RULE_EVEN_ODD);
+ } else {
+ ct.setFillRule(CAIRO_FILL_RULE_WINDING);
+ }
+ }
+ ct.transform(_ctm);
+
+ for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
+ DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i);
+ if (!g) throw InvalidItemException();
+
+ Inkscape::DrawingContext::Save save(ct);
+ if (g->_glyph_transform) {
+ ct.transform(*g->_glyph_transform);
+ }
+ ct.path(*g->_font->PathVector(g->_glyph));
+ }
+ ct.fill();
+}
+
+DrawingItem *
+DrawingText::_pickItem(Geom::Point const &p, double delta)
+{
+ DrawingItem *picked = DrawingGroup::_pickItem(p, delta);
+ if (picked) return this;
+ return NULL;
+}
+
+bool
+DrawingText::_canClip()
+{
+ return true;
+}
+
+} // end namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :