diff options
| author | Liam P. White <inkscapebrony@gmail.com> | 2014-10-17 20:03:14 +0000 |
|---|---|---|
| committer | Liam P. White <inkscapebrony@gmail.com> | 2014-10-17 20:03:14 +0000 |
| commit | 4fc13b246c0c03c26c10c421c63c33331aa57d85 (patch) | |
| tree | 82f8ceea42ace9c0512b6073935e4bd9c3d14f7a /src | |
| parent | Small warning cleanup (diff) | |
| parent | Port inkscape to librevenge framework for WPG, CDR and VSD imports (diff) | |
| download | inkscape-4fc13b246c0c03c26c10c421c63c33331aa57d85.tar.gz inkscape-4fc13b246c0c03c26c10c421c63c33331aa57d85.zip | |
Update to trunk r13621
(bzr r13341.1.278)
Diffstat (limited to 'src')
64 files changed, 3153 insertions, 508 deletions
diff --git a/src/2geom/math-utils.h b/src/2geom/math-utils.h index 83c7b4f5e..0d5d3667b 100644 --- a/src/2geom/math-utils.h +++ b/src/2geom/math-utils.h @@ -111,7 +111,7 @@ inline void sincos(double angle, double &sin_, double &cos_) { # define IS_NAN(_a) (_isnan(_a)) /* Win32 definition */ #elif defined(isnan) || defined(__FreeBSD__) || defined(__osf__) # define IS_NAN(_a) (isnan(_a)) /* GNU definition */ -#elif defined (SOLARIS_2_8) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 +#elif defined (SOLARIS) # define IS_NAN(_a) (isnan(_a)) /* GNU definition */ #else # define IS_NAN(_a) (boost::math::isnan(_a)) @@ -129,7 +129,7 @@ inline void sincos(double angle, double &sin_, double &cos_) { # define IS_FINITE(_a) (isfinite(_a)) #elif defined(__osf__) # define IS_FINITE(_a) (finite(_a) && !IS_NAN(_a)) -#elif defined (SOLARIS_2_8) && __GNUC__ == 3 && __GNUC_MINOR__ == 2 +#elif defined (SOLARIS) #include <ieeefp.h> #define IS_FINITE(_a) (finite(_a) && !IS_NAN(_a)) #else diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c77a9bf63..1455b762e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,8 @@ set(sp_SRC sp-gradient-reference.cpp sp-gradient.cpp sp-guide.cpp + sp-hatch.cpp + sp-hatch-path.cpp sp-image.cpp sp-item-group.cpp sp-item-notify-moveto.cpp @@ -116,6 +118,8 @@ set(sp_SRC sp-guide-attachment.h sp-guide-constraint.h sp-guide.h + sp-hatch.h + sp-hatch-path.h sp-image.h sp-item-group.h sp-item-notify-moveto.h diff --git a/src/Makefile_insert b/src/Makefile_insert index d6db76eea..2fff44cb8 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -145,6 +145,8 @@ ink_common_sources += \ sp-guide-attachment.h \ sp-guide-constraint.h \ sp-guide.cpp sp-guide.h \ + sp-hatch.cpp sp-hatch.h \ + sp-hatch-path.cpp sp-hatch-path.h \ sp-image.cpp sp-image.h \ sp-item.cpp sp-item.h \ sp-item-group.cpp sp-item-group.h \ diff --git a/src/attributes.cpp b/src/attributes.cpp index 87bfdbe88..d142d1a06 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -293,6 +293,11 @@ static SPStyleProp const props[] = { {SP_ATTR_PATTERNUNITS, "patternUnits"}, {SP_ATTR_PATTERNCONTENTUNITS, "patternContentUnits"}, {SP_ATTR_PATTERNTRANSFORM, "patternTransform"}, + /* SPHatch */ + {SP_ATTR_HATCHUNITS, "hatchUnits"}, + {SP_ATTR_HATCHCONTENTUNITS, "hatchContentUnits"}, + {SP_ATTR_HATCHTRANSFORM, "hatchTransform"}, + {SP_ATTR_PITCH, "pitch"}, /* SPClipPath */ {SP_ATTR_CLIPPATHUNITS, "clipPathUnits"}, /* SPMask */ diff --git a/src/attributes.h b/src/attributes.h index 598d68fa3..8e091df71 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -295,6 +295,11 @@ enum SPAttributeEnum { SP_ATTR_PATTERNUNITS, SP_ATTR_PATTERNCONTENTUNITS, SP_ATTR_PATTERNTRANSFORM, + /* SPHatch */ + SP_ATTR_HATCHUNITS, + SP_ATTR_HATCHCONTENTUNITS, + SP_ATTR_HATCHTRANSFORM, + SP_ATTR_PITCH, /* SPClipPath */ SP_ATTR_CLIPPATHUNITS, /* SPMask */ diff --git a/src/box3d-side.h b/src/box3d-side.h index 89b3b0399..4783a5f24 100644 --- a/src/box3d-side.h +++ b/src/box3d-side.h @@ -7,6 +7,7 @@ * Authors: * Maximilian Albert <Anhalter42@gmx.de> * Abhishek Sharma + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2007 Authors * @@ -17,9 +18,6 @@ #include "axis-manip.h" -#define SP_BOX3D_SIDE(obj) (dynamic_cast<Box3DSide*>((SPObject*)obj)) -#define SP_IS_BOX3D_SIDE(obj) (dynamic_cast<const Box3DSide*>((SPObject*)obj) != NULL) - class SPBox3D; class Persp3D; diff --git a/src/box3d.cpp b/src/box3d.cpp index eb82524dd..5f60766f4 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -6,6 +6,7 @@ * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak <buliabyak@users.sf.net> * Abhishek Sharma + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2007 Authors * Copyright (C) 1999-2002 Lauris Kaplinski @@ -263,9 +264,10 @@ void box3d_position_set(SPBox3D *box) { /* This draws the curve and calls requestDisplayUpdate() for each side (the latter is done in box3d_side_position_set() to avoid update conflicts with the parent box) */ - for ( SPObject *child = box->firstChild(); child; child = child->getNext() ) { - if (SP_IS_BOX3D_SIDE(child)) { - box3d_side_position_set(SP_BOX3D_SIDE(child)); + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + box3d_side_position_set(side); } } } @@ -1079,10 +1081,10 @@ box3d_recompute_z_orders (SPBox3D *box) { static std::map<int, Box3DSide *> box3d_get_sides(SPBox3D *box) { std::map<int, Box3DSide *> sides; - for ( SPObject *side = box->firstChild(); side; side = side->getNext() ) { - if (SP_IS_BOX3D_SIDE(side)){ - Box3DSide *bside = SP_BOX3D_SIDE(side); - sides[Box3D::face_to_int(bside->getFaceId())] = bside; + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + sides[Box3D::face_to_int(side->getFaceId())] = side; } } sides.erase(-1); @@ -1280,9 +1282,10 @@ SPGroup *box3d_convert_to_group(SPBox3D *box) // create a new group and add the sides (converted to ordinary paths) as its children Inkscape::XML::Node *grepr = xml_doc->createElement("svg:g"); - for ( SPObject *child = box->firstChild(); child; child = child->getNext() ) { - if (SP_IS_BOX3D_SIDE(child)) { - Inkscape::XML::Node *repr = box3d_side_convert_to_path(SP_BOX3D_SIDE(child)); + for ( SPObject *obj = box->firstChild(); obj; obj = obj->getNext() ) { + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + Inkscape::XML::Node *repr = box3d_side_convert_to_path(side); grepr->appendChild(repr); } else { g_warning("Non-side item encountered as child of a 3D box."); diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index ce8a06f29..15dc339cf 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -174,8 +174,10 @@ sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write for (const GSList *i = desktop->selection->itemList(); i != NULL; i = i->next) { /* last used styles for 3D box faces are stored separately */ - if (SP_IS_BOX3D_SIDE (i->data)) { - const char * descr = box3d_side_axes_string(SP_BOX3D_SIDE(i->data)); + SPObject *obj = reinterpret_cast<SPObject *>(i->data); // TODO unsafe until Selection is refactored. + Box3DSide *side = dynamic_cast<Box3DSide *>(obj); + if (side) { + const char * descr = box3d_side_axes_string(side); if (descr != NULL) { prefs->mergeStyle(Glib::ustring("/desktop/") + descr + "/style", css_write); } diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 20424c845..800c4d0d4 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -13,6 +13,7 @@ set(display_SRC drawing-group.cpp drawing-image.cpp drawing-item.cpp + drawing-pattern.cpp drawing-shape.cpp drawing-surface.cpp drawing-text.cpp @@ -75,6 +76,7 @@ set(display_SRC drawing-group.h drawing-image.h drawing-item.h + drawing-pattern.h drawing-shape.h drawing-surface.h drawing-text.h diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index abbd89a68..2355c3653 100644 --- a/src/display/Makefile_insert +++ b/src/display/Makefile_insert @@ -33,6 +33,8 @@ ink_common_sources += \ display/drawing-image.h \ display/drawing-item.cpp \ display/drawing-item.h \ + display/drawing-pattern.cpp \ + display/drawing-pattern.h \ display/drawing-shape.cpp \ display/drawing-shape.h \ display/drawing-surface.cpp \ diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 56507bacb..9bb81cea5 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -15,6 +15,7 @@ #include "display/drawing-context.h" #include "display/drawing-item.h" #include "display/drawing-group.h" +#include "display/drawing-pattern.h" #include "display/drawing-surface.h" #include "nr-filter.h" #include "preferences.h" @@ -112,6 +113,8 @@ DrawingItem::DrawingItem(Drawing &drawing) , _transform(NULL) , _clip(NULL) , _mask(NULL) + , _fill_pattern(NULL) + , _stroke_pattern(NULL) , _filter(NULL) , _user_data(NULL) , _cache(NULL) @@ -166,6 +169,12 @@ DrawingItem::~DrawingItem() case CHILD_ROOT: _drawing._root = NULL; break; + case CHILD_FILL_PATTERN: + _parent->_fill_pattern = NULL; + break; + case CHILD_STROKE_PATTERN: + _parent->_stroke_pattern = NULL; + break; default: ; } @@ -174,6 +183,8 @@ DrawingItem::~DrawingItem() } clearChildren(); delete _transform; + delete _stroke_pattern; + delete _fill_pattern; delete _clip; delete _mask; delete _filter; @@ -368,6 +379,34 @@ DrawingItem::setMask(DrawingItem *item) _markForUpdate(STATE_ALL, true); } +void +DrawingItem::setFillPattern(DrawingPattern *pattern) +{ + _markForRendering(); + delete _fill_pattern; + _fill_pattern = pattern; + if (pattern) { + pattern->_parent = this; + assert(pattern->_child_type == CHILD_ORPHAN); + pattern->_child_type = CHILD_FILL_PATTERN; + } + _markForUpdate(STATE_ALL, true); +} + +void +DrawingItem::setStrokePattern(DrawingPattern *pattern) +{ + _markForRendering(); + delete _stroke_pattern; + _stroke_pattern = pattern; + if (pattern) { + pattern->_parent = this; + assert(pattern->_child_type == CHILD_ORPHAN); + pattern->_child_type = CHILD_STROKE_PATTERN; + } + _markForUpdate(STATE_ALL, true); +} + /// Move this item to the given place in the Z order of siblings. /// Does nothing if the item has no parent. void @@ -532,6 +571,12 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne if (to_update & STATE_RENDER) { // now that we know drawbox, dirty the corresponding rect on canvas // unless filtered, groups do not need to render by themselves, only their members + if (_fill_pattern) { + _fill_pattern->update(area, child_ctx, flags, reset); + } + if (_stroke_pattern) { + _stroke_pattern->update(area, child_ctx, flags, reset); + } if (!is_drawing_group(this) || (_filter && render_filters)) { _markForRendering(); } @@ -970,7 +1015,7 @@ DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style) if (_style) sp_style_unref(_style); _style = style; - if (style->filter.set && style->getFilter()) { + if (style && style->filter.set && style->getFilter()) { if (!_filter) { int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); _filter = new Inkscape::Filters::Filter(primitives); diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index dda5cd6ac..9b399e6e3 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -28,6 +28,7 @@ class Drawing; class DrawingCache; class DrawingContext; class DrawingItem; +class DrawingPattern; namespace Filters { @@ -114,6 +115,8 @@ public: void setTransform(Geom::Affine const &trans); void setClip(DrawingItem *item); void setMask(DrawingItem *item); + void setFillPattern(DrawingPattern *pattern); + void setStrokePattern(DrawingPattern *pattern); void setZOrder(unsigned z); void setItemBounds(Geom::OptRect const &bounds); void setFilterBounds(Geom::OptRect const &bounds); @@ -135,8 +138,8 @@ protected: CHILD_CLIP = 2, // referenced by _clip member of parent CHILD_MASK = 3, // referenced by _mask member of parent CHILD_ROOT = 4, // root item of _drawing - CHILD_FILL_PATTERN = 5, // not yet implemented: referenced by fill pattern of parent - CHILD_STROKE_PATTERN = 6 // not yet implemented: referenced by stroke pattern of parent + CHILD_FILL_PATTERN = 5, // referenced by fill pattern of parent + CHILD_STROKE_PATTERN = 6 // referenced by stroke pattern of parent }; enum RenderResult { RENDER_OK = 0, @@ -185,6 +188,8 @@ protected: DrawingItem *_clip; DrawingItem *_mask; + DrawingPattern *_fill_pattern; + DrawingPattern *_stroke_pattern; Inkscape::Filters::Filter *_filter; void *_user_data; ///< Used to associate DrawingItems with SPItems that created them DrawingCache *_cache; diff --git a/src/display/drawing-pattern.cpp b/src/display/drawing-pattern.cpp new file mode 100644 index 000000000..cf6358278 --- /dev/null +++ b/src/display/drawing-pattern.cpp @@ -0,0 +1,194 @@ +/** + * @file + * Canvas belonging to SVG pattern. + *//* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-pattern.h" +#include "display/drawing-surface.h" + +namespace Inkscape { + +DrawingPattern::DrawingPattern(Drawing &drawing, bool debug) + : DrawingGroup(drawing) + , _pattern_to_user(NULL) + , _overflow_steps(1) + , _debug(debug) +{ +} + +DrawingPattern::~DrawingPattern() +{ + delete _pattern_to_user; // delete NULL; is safe +} + +void +DrawingPattern::setPatternToUserTransform(Geom::Affine const &new_trans) { + Geom::Affine current; + if (_pattern_to_user) { + current = *_pattern_to_user; + } + + if (!Geom::are_near(current, new_trans, 1e-18)) { + // mark the area where the object was for redraw. + _markForRendering(); + if (new_trans.isIdentity()) { + delete _pattern_to_user; // delete NULL; is safe + _pattern_to_user = NULL; + } else { + _pattern_to_user = new Geom::Affine(new_trans); + } + _markForUpdate(STATE_ALL, true); + } +} + +void +DrawingPattern::setTileRect(Geom::Rect const &tile_rect) { + _tile_rect = tile_rect; +} + +void +DrawingPattern::setOverflow(Geom::Affine initial_transform, int steps, Geom::Affine step_transform) { + _overflow_initial_transform = initial_transform; + _overflow_steps = steps; + _overflow_step_transform = step_transform; +} + +cairo_pattern_t * +DrawingPattern::renderPattern(float opacity) { + bool needs_opacity = (1.0 - opacity) >= 1e-3; + bool visible = opacity >= 1e-3; + + if (!visible) { + return NULL; + } + + if (!_tile_rect || (_tile_rect->area() == 0)) { + return NULL; + } + Geom::Rect pattern_tile = *_tile_rect; + + //TODO: If pattern_to_user set it to identity transform + + // The DrawingSurface class handles the mapping from "logical space" + // (coordinates in the rendering) to "physical space" (surface pixels). + // An oversampling is done as the pattern may not pixel align with the final surface. + // The cairo surface is created when the DrawingContext is declared. + // Create drawing surface with size of pattern tile (in pattern space) but with number of pixels + // based on required resolution (c). + Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution); + Inkscape::DrawingContext dc(pattern_surface); + dc.transform( pattern_surface.drawingTransform().inverse() ); + + pattern_tile *= pattern_surface.drawingTransform(); + Geom::IntRect one_tile = pattern_tile.roundOutwards(); + + // Render pattern. + if (needs_opacity) { + dc.pushGroup(); // this group is for pattern + opacity + } + + if (_debug) { + dc.setSource(0.8, 0.0, 0.8); + dc.paint(); + } + + //FIXME: What flags to choose? + if (_overflow_steps == 1) { + render(dc, one_tile, RENDER_DEFAULT); + } else { + //Overflow transforms need to be transformed to the new coordinate system + //introduced by dc.transform( pattern_surface.drawingTransform().inverse() ); + Geom::Affine dt = pattern_surface.drawingTransform(); + Geom::Affine idt = pattern_surface.drawingTransform().inverse(); + Geom::Affine initial_transform = idt * _overflow_initial_transform * dt; + Geom::Affine step_transform = idt * _overflow_step_transform * dt; + + dc.transform(initial_transform); + for (int i = 0; i < _overflow_steps; i++) { + render(dc, one_tile, RENDER_DEFAULT); + dc.transform(step_transform); + } + } + + //Uncomment to debug + // cairo_surface_t* raw = pattern_surface.raw(); + // std::cout << " cairo_surface (sp-pattern): " + // << " width: " << cairo_image_surface_get_width( raw ) + // << " height: " << cairo_image_surface_get_height( raw ) + // << std::endl; + // std::string filename = "drawing-pattern.png"; + // cairo_surface_write_to_png( pattern_surface.raw(), filename.c_str() ); + + if (needs_opacity) { + dc.popGroupToSource(); // pop raw pattern + dc.paint(opacity); // apply opacity + } + + cairo_pattern_t *cp = cairo_pattern_create_for_surface(pattern_surface.raw()); + // Apply transformation to user space. Also compensate for oversampling. + if (_pattern_to_user) { + ink_cairo_pattern_set_matrix(cp, _pattern_to_user->inverse() * pattern_surface.drawingTransform()); + } else { + ink_cairo_pattern_set_matrix(cp, pattern_surface.drawingTransform()); + } + + if (_debug) { + cairo_pattern_set_extend(cp, CAIRO_EXTEND_NONE); + } else { + cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT); + } + + return cp; +} + +unsigned +DrawingPattern::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) +{ + UpdateContext pattern_ctx; + + if (!_tile_rect || (_tile_rect->area() == 0)) { + return STATE_NONE; + } + + Geom::Rect pattern_tile = *_tile_rect; + Geom::Coord det_ctm = ctx.ctm.descrim(); + Geom::Coord det_ps2user = _pattern_to_user ? _pattern_to_user->descrim() : 1.0; + Geom::Coord det_child_transform = _child_transform ? _child_transform->descrim() : 1.0; + const double oversampling = 2.0; + double scale = det_ctm*det_ps2user*det_child_transform * oversampling; + //FIXME: When scale is too big (zooming in a hatch), cairo doesn't render the pattern + //More precisely it fails when seting pattern matrix in DrawingPattern::renderPattern + //Fully correct solution should make use of visible area bbox and change hach tile rect + //accordingly + if (scale > 25) { + scale = 25; + } + Geom::Point c(pattern_tile.dimensions()*scale*oversampling); + _pattern_resolution = c.ceil(); + + Inkscape::DrawingSurface pattern_surface(pattern_tile, _pattern_resolution); + + pattern_ctx.ctm = pattern_surface.drawingTransform(); + return DrawingGroup::_updateItem(Geom::IntRect::infinite(), pattern_ctx, flags, reset); +} + +} // 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 : diff --git a/src/display/drawing-pattern.h b/src/display/drawing-pattern.h new file mode 100644 index 000000000..7483ba067 --- /dev/null +++ b/src/display/drawing-pattern.h @@ -0,0 +1,87 @@ +/** + * @file + * Canvas belonging to SVG pattern. + *//* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H +#define SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H + +#include "display/drawing-group.h" + +typedef struct _cairo_pattern cairo_pattern_t; +class SPStyle; + +namespace Inkscape { + +/** + * @brief Drawing tree node used for rendering paints. + * + * DrawingPattern is used for rendering patterns and hatches. + * + * It renders it's children to a cairo_pattern_t structure that can be + * applied as source for fill or stroke operations. + */ +class DrawingPattern + : public DrawingGroup +{ +public: + DrawingPattern(Drawing &drawing, bool debug = false); + ~DrawingPattern(); + + /** + * Set the transformation from pattern to user coordinate systems. + * @see SPPattern description for explanation of coordinate systems. + */ + void setPatternToUserTransform(Geom::Affine const &new_trans); + /** + * Set the tile rect position and dimentions in content coordinate system + */ + void setTileRect(Geom::Rect const &tile_rect); + /** + * Turn on overflow rendering. + * + * Overflow is implemented as repeated rendering of pattern contents. In every step + * a translation transform is applied. + */ + void setOverflow(Geom::Affine initial_transform, int steps, Geom::Affine step_transform); + /** + * Render the pattern. + * + * Returns caito_pattern_t structure that can be set as source surface. + */ + cairo_pattern_t *renderPattern(float opacity); +protected: + virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, + unsigned flags, unsigned reset); + + Geom::Affine *_pattern_to_user; + Geom::Affine _overflow_initial_transform; + Geom::Affine _overflow_step_transform; + int _overflow_steps; + Geom::OptRect _tile_rect; + bool _debug; + Geom::IntPoint _pattern_resolution; +}; + +bool is_drawing_group(DrawingItem *item); + +} // end namespace Inkscape + +#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_PATTERN_H + +/* + 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 : diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp index 88506f2b9..66160638f 100644 --- a/src/display/drawing-shape.cpp +++ b/src/display/drawing-shape.cpp @@ -155,7 +155,7 @@ DrawingShape::_renderFill(DrawingContext &dc) Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - bool has_fill = _nrstyle.prepareFill(dc, _item_bbox); + bool has_fill = _nrstyle.prepareFill(dc, _item_bbox, _fill_pattern); if( has_fill ) { dc.path(_curve->get_pathvector()); @@ -171,7 +171,7 @@ DrawingShape::_renderStroke(DrawingContext &dc) Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox); + bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox, _stroke_pattern); has_stroke &= (_nrstyle.stroke_width != 0); if( has_stroke ) { @@ -231,8 +231,8 @@ DrawingShape::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne // update fill and stroke paints. // this cannot be done during nr_arena_shape_update, because we need a Cairo context // to render svg:pattern - bool has_fill = _nrstyle.prepareFill(dc, _item_bbox); - bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox); + bool has_fill = _nrstyle.prepareFill(dc, _item_bbox, _fill_pattern); + bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox, _stroke_pattern); has_stroke &= (_nrstyle.stroke_width != 0); if (has_fill || has_stroke) { diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 97a8c23d3..afe661b2e 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -448,13 +448,13 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - has_fill = _nrstyle.prepareFill( dc, _item_bbox); - has_stroke = _nrstyle.prepareStroke( dc, _item_bbox); + has_fill = _nrstyle.prepareFill( dc, _item_bbox, _fill_pattern); + has_stroke = _nrstyle.prepareStroke( dc, _item_bbox, _stroke_pattern); // Avoid creating patterns if not needed if( decorate ) { - has_td_fill = _nrstyle.prepareTextDecorationFill( dc, _item_bbox); - has_td_stroke = _nrstyle.prepareTextDecorationStroke(dc, _item_bbox); + has_td_fill = _nrstyle.prepareTextDecorationFill( dc, _item_bbox, _fill_pattern); + has_td_stroke = _nrstyle.prepareTextDecorationStroke(dc, _item_bbox, _stroke_pattern); } } diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index a9f101f69..96d16bf06 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -14,6 +14,7 @@ #include "sp-paint-server.h" #include "display/canvas-bpath.h" // contains SPStrokeJoinType, SPStrokeCapType etc. (WTF!) #include "display/drawing-context.h" +#include "display/drawing-pattern.h" void NRStyle::Paint::clear() { @@ -299,13 +300,16 @@ void NRStyle::set(SPStyle *style) update(); } -bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { // update fill pattern if (!fill_pattern) { switch (fill.type) { - case PAINT_SERVER: { - fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity); + case PAINT_SERVER: + if (pattern) { + fill_pattern = pattern->renderPattern(fill.opacity); + } else { + fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity); } break; case PAINT_COLOR: { SPColor const &c = fill.color; @@ -325,14 +329,20 @@ void NRStyle::applyFill(Inkscape::DrawingContext &dc) dc.setFillRule(fill_rule); } -bool NRStyle::prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { // update text decoration pattern if (!text_decoration_fill_pattern) { switch (text_decoration_fill.type) { - case PAINT_SERVER: { - text_decoration_fill_pattern = text_decoration_fill.server->pattern_new(dc.raw(), paintbox, text_decoration_fill.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + text_decoration_fill_pattern = pattern->renderPattern( + text_decoration_fill.opacity); + } else { + text_decoration_fill_pattern = text_decoration_fill.server->pattern_new(dc.raw(), + paintbox, text_decoration_fill.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = text_decoration_fill.color; text_decoration_fill_pattern = cairo_pattern_create_rgba( @@ -351,19 +361,25 @@ void NRStyle::applyTextDecorationFill(Inkscape::DrawingContext &dc) // Fill rule does not matter, no intersections. } -bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { if (!stroke_pattern) { switch (stroke.type) { - case PAINT_SERVER: { - stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + stroke_pattern = pattern->renderPattern(stroke.opacity); + } else { + stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = stroke.color; - stroke_pattern = cairo_pattern_create_rgba( - c.v.c[0], c.v.c[1], c.v.c[2], stroke.opacity); - } break; - default: break; + stroke_pattern = cairo_pattern_create_rgba(c.v.c[0], c.v.c[1], c.v.c[2], + stroke.opacity); + } + break; + default: + break; } } if (!stroke_pattern) return false; @@ -380,13 +396,19 @@ void NRStyle::applyStroke(Inkscape::DrawingContext &dc) cairo_set_dash(dc.raw(), dash, n_dash, dash_offset); // fixme } -bool NRStyle::prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox) +bool NRStyle::prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern) { if (!text_decoration_stroke_pattern) { switch (text_decoration_stroke.type) { - case PAINT_SERVER: { - text_decoration_stroke_pattern = text_decoration_stroke.server->pattern_new(dc.raw(), paintbox, text_decoration_stroke.opacity); - } break; + case PAINT_SERVER: + if (pattern) { + text_decoration_stroke_pattern = pattern->renderPattern( + text_decoration_stroke.opacity); + } else { + text_decoration_stroke_pattern = text_decoration_stroke.server->pattern_new( + dc.raw(), paintbox, text_decoration_stroke.opacity); + } + break; case PAINT_COLOR: { SPColor const &c = text_decoration_stroke.color; text_decoration_stroke_pattern = cairo_pattern_create_rgba( diff --git a/src/display/nr-style.h b/src/display/nr-style.h index 83bcb1ab7..f324fdb56 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -21,6 +21,7 @@ class SPStyle; namespace Inkscape { class DrawingContext; +class DrawingPattern; } struct NRStyle { @@ -28,10 +29,10 @@ struct NRStyle { ~NRStyle(); void set(SPStyle *); - bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); - bool prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox); + bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareTextDecorationFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); + bool prepareTextDecorationStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox, Inkscape::DrawingPattern *pattern); void applyFill(Inkscape::DrawingContext &dc); void applyStroke(Inkscape::DrawingContext &dc); void applyTextDecorationFill(Inkscape::DrawingContext &dc); diff --git a/src/document.cpp b/src/document.cpp index b50fc9575..64ccb8c36 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -706,7 +706,12 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) double const h = rect.height(); double const old_height = getHeight().value("px"); - Inkscape::Util::Unit const *px = unit_table.getUnit("px"); + Inkscape::Util::Unit const *nv_units = unit_table.getUnit("px"); + SPNamedView *nv = sp_document_namedview(this, NULL); + if (nv != NULL) { + if (nv->getAttribute("units")) + nv_units = unit_table.getUnit(nv->getAttribute("units")); + } /* in px */ double margin_top = 0.0; @@ -714,22 +719,16 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) double margin_right = 0.0; double margin_bottom = 0.0; - SPNamedView *nv = sp_document_namedview(this, NULL); - if (with_margins && nv) { if (nv != NULL) { - gchar const * const units_abbr = nv->getAttribute("units"); - Inkscape::Util::Unit const *margin_units = NULL; - if (units_abbr) { - margin_units = unit_table.getUnit(units_abbr); - } - if (!margin_units) { - margin_units = px; - } - margin_top = nv->getMarginLength("fit-margin-top",margin_units, px, w, h, false); - margin_left = nv->getMarginLength("fit-margin-left",margin_units, px, w, h, true); - margin_right = nv->getMarginLength("fit-margin-right",margin_units, px, w, h, true); - margin_bottom = nv->getMarginLength("fit-margin-bottom",margin_units, px, w, h, false); + margin_top = nv->getMarginLength("fit-margin-top", nv_units, unit_table.getUnit("px"), w, h, false); + margin_left = nv->getMarginLength("fit-margin-left", nv_units, unit_table.getUnit("px"), w, h, true); + margin_right = nv->getMarginLength("fit-margin-right", nv_units, unit_table.getUnit("px"), w, h, true); + margin_bottom = nv->getMarginLength("fit-margin-bottom", nv_units, unit_table.getUnit("px"), w, h, false); + margin_top = Inkscape::Util::Quantity::convert(margin_top, nv_units, "px"); + margin_left = Inkscape::Util::Quantity::convert(margin_left, nv_units, "px"); + margin_right = Inkscape::Util::Quantity::convert(margin_right, nv_units, "px"); + margin_bottom = Inkscape::Util::Quantity::convert(margin_bottom, nv_units, "px"); } } @@ -738,8 +737,8 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) rect.max() + Geom::Point(margin_right, margin_top)); - setWidth(Inkscape::Util::Quantity(rect_with_margins.width(), px)); - setHeight(Inkscape::Util::Quantity(rect_with_margins.height(), px)); + setWidth(Inkscape::Util::Quantity(Inkscape::Util::Quantity::convert(rect_with_margins.width(), "px", nv_units), nv_units)); + setHeight(Inkscape::Util::Quantity(Inkscape::Util::Quantity::convert(rect_with_margins.height(), "px", nv_units), nv_units)); Geom::Translate const tr( Geom::Point(0, old_height - rect_with_margins.height()) diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index c09b8e9c8..7e61cdbbb 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -39,6 +39,7 @@ #include "sp-item.h" #include "sp-item-group.h" #include "style.h" +#include "sp-hatch.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" #include "sp-pattern.h" @@ -1130,6 +1131,82 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver } cairo_pattern_t* +CairoRenderContext::_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox) { + g_assert( SP_IS_HATCH(paintserver) ); + SPHatch *hatch = SP_HATCH(paintserver); + + g_assert(hatch->pitch() > 0); + + // create drawing and group + Inkscape::Drawing drawing; + unsigned dkey = SPItem::display_key_new(1); + + hatch->show(drawing, dkey, pbox); + + SPHatch::RenderInfo render_info = hatch->calculateRenderInfo(dkey); + Geom::Rect tile_rect = render_info.tile_rect; + + // Cairo requires an integer pattern surface width/height. + // Subtract 0.5 to prevent small rounding errors from increasing pattern size by one pixel. + // Multiply by SUBPIX_SCALE to allow for less than a pixel precision + const int subpix_scale = 10; + double surface_width = MAX(ceil(subpix_scale * tile_rect.width() - 0.5), 1); + double surface_height = MAX(ceil(subpix_scale * tile_rect.height() - 0.5), 1); + Geom::Affine drawing_scale = Geom::Scale(surface_width / tile_rect.width(), surface_height / tile_rect.height()); + Geom::Affine drawing_transform = Geom::Translate(-tile_rect.min()) * drawing_scale; + + Geom::Affine child_transform = render_info.child_transform; + child_transform *= drawing_transform; + + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + gdouble overflow_right_strip = 0.0; + int overflow_steps = 1; + Geom::Affine overflow_transform; + if (hatch->style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = hatch->bounds(); + overflow_right_strip = floor(bounds.max() / hatch->pitch()) * hatch->pitch(); + overflow_steps = ceil((overflow_right_strip - bounds.min()) / hatch->pitch()) + 1; + overflow_transform = Geom::Translate(hatch->pitch(), 0.0); + } + + CairoRenderContext *pattern_ctx = cloneMe(surface_width, surface_height); + pattern_ctx->setTransform(child_transform); + pattern_ctx->transform(Geom::Translate(-overflow_right_strip, 0.0)); + pattern_ctx->pushState(); + + std::vector<SPHatchPath *> children; + hatch->hatchPaths(children); + + for (int i = 0; i < overflow_steps; i++) { + for (std::vector<SPHatchPath *>::iterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *path = *iter; + _renderer->renderHatchPath(pattern_ctx, *path, dkey); + } + pattern_ctx->transform(overflow_transform); + } + + pattern_ctx->popState(); + + // setup a cairo_pattern_t + cairo_surface_t *pattern_surface = pattern_ctx->getSurface(); + TEST(pattern_ctx->saveAsPng("hatch.png")); + cairo_pattern_t *result = cairo_pattern_create_for_surface(pattern_surface); + cairo_pattern_set_extend(result, CAIRO_EXTEND_REPEAT); + + Geom::Affine pattern_transform; + pattern_transform = render_info.pattern_to_user_transform.inverse() * drawing_transform; + ink_cairo_pattern_set_matrix(result, pattern_transform); + + hatch->hide(dkey); + + delete pattern_ctx; + return result; +} + +cairo_pattern_t* CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha) { @@ -1182,8 +1259,9 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain cairo_pattern_add_color_stop_rgba(pattern, rg->vector.stops[i].offset, rgb[0], rgb[1], rgb[2], rg->vector.stops[i].opacity * alpha); } } else if (SP_IS_PATTERN (paintserver)) { - pattern = _createPatternPainter(paintserver, pbox); + } else if (SP_IS_HATCH (paintserver)) { + pattern = _createHatchPainter(paintserver, pbox); } else { return NULL; } @@ -1249,26 +1327,29 @@ CairoRenderContext::_setFillStyle(SPStyle const *const style, Geom::OptRect cons TRACE(("merged op=%f\n", alpha)); } - if (style->fill.isColor()) { - float rgb[3]; - sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + SPPaintServer const *paint_server = style->getFillPaintServer(); + if (paint_server && paint_server->isValid()) { - cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); - - } else if (!style->fill.set) { // unset fill is black - cairo_set_source_rgba(_cr, 0, 0, 0, alpha); - - } else { - g_assert( style->fill.isPaintserver() - || SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) ); - - cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_FILL_SERVER(style), pbox, alpha); + g_assert(SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) + || SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_FILL_SERVER(style))); + cairo_pattern_t *pattern = _createPatternForPaintServer(paint_server, pbox, alpha); if (pattern) { cairo_set_source(_cr, pattern); cairo_pattern_destroy(pattern); } + } else if (style->fill.colorSet) { + float rgb[3]; + sp_color_get_rgb_floatv(&style->fill.value.color, rgb); + + cairo_set_source_rgba(_cr, rgb[0], rgb[1], rgb[2], alpha); + + } else { // unset fill is black + g_assert(!style->fill.set + || (paint_server && !paint_server->isValid())); + + cairo_set_source_rgba(_cr, 0, 0, 0, alpha); } } @@ -1279,7 +1360,7 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p if (_state->merge_opacity) alpha *= _state->opacity; - if (style->stroke.isColor()) { + if (style->stroke.isColor() || (style->stroke.isPaintserver() && !style->getStrokePaintServer()->isValid())) { float rgb[3]; sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); @@ -1287,7 +1368,8 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p } else { g_assert( style->stroke.isPaintserver() || SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style)) - || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) ); + || SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) + || SP_IS_HATCH(SP_STYLE_STROKE_SERVER(style))); cairo_pattern_t *pattern = _createPatternForPaintServer(SP_STYLE_STROKE_SERVER(style), pbox, alpha); diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h index 8071cae36..57d155b60 100644 --- a/src/extension/internal/cairo-render-context.h +++ b/src/extension/internal/cairo-render-context.h @@ -204,6 +204,7 @@ protected: cairo_pattern_t *_createPatternForPaintServer(SPPaintServer const *const paintserver, Geom::OptRect const &pbox, float alpha); cairo_pattern_t *_createPatternPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); + cairo_pattern_t *_createHatchPainter(SPPaintServer const *const paintserver, Geom::OptRect const &pbox); unsigned int _showGlyphs(cairo_t *cr, PangoFont *font, std::vector<CairoGlyphInfo> const &glyphtext, bool is_stroke); diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 8196e34fb..a725685d4 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -50,6 +50,7 @@ #include "sp-use.h" #include "sp-text.h" #include "sp-flowtext.h" +#include "sp-hatch-path.h" #include "sp-image.h" #include "sp-symbol.h" #include "sp-pattern.h" @@ -598,6 +599,21 @@ void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) ctx->popState(); } +void CairoRenderer::renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key) { + ctx->pushState(); + ctx->setStateForStyle(hatchPath.style); + ctx->transform(Geom::Translate(hatchPath.offset.computed, 0)); + + SPCurve *curve = hatchPath.calculateRenderCurve(key); + Geom::PathVector const & pathv =curve->get_pathvector(); + if (!pathv.empty()) { + ctx->renderPathVector(pathv, hatchPath.style, Geom::OptRect()); + } + + curve->unref(); + ctx->popState(); +} + bool CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, float bleedmargin_px, SPItem *base) { diff --git a/src/extension/internal/cairo-renderer.h b/src/extension/internal/cairo-renderer.h index cfef6bdea..abc0447d8 100644 --- a/src/extension/internal/cairo-renderer.h +++ b/src/extension/internal/cairo-renderer.h @@ -29,6 +29,7 @@ class SPClipPath; class SPMask; +class SPHatchPath; namespace Inkscape { namespace Extension { @@ -57,6 +58,7 @@ public: /** Traverses the object tree and invokes the render methods. */ void renderItem(CairoRenderContext *ctx, SPItem *item); + void renderHatchPath(CairoRenderContext *ctx, SPHatchPath const &hatchPath, unsigned key); }; // FIXME: this should be a static method of CairoRenderer diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp index 239c99688..3a3e2b58e 100644 --- a/src/extension/internal/cdr-input.cpp +++ b/src/extension/internal/cdr-input.cpp @@ -24,7 +24,21 @@ #include <cstring> #include <libcdr/libcdr.h> -#include <libwpd-stream/libwpd-stream.h> + +// TODO: Drop this check when librevenge is widespread. +#if WITH_LIBCDR01 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; +#else + #include <libwpd-stream/libwpd-stream.h> + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef libcdr::CDRStringVector RVNGStringVector; +#endif #include <gtkmm/alignment.h> #include <gtkmm/comboboxtext.h> @@ -60,7 +74,7 @@ namespace Internal { class CdrImportDialog : public Gtk::Dialog { public: - CdrImportDialog(const std::vector<WPXString> &vec); + CdrImportDialog(const std::vector<RVNGString> &vec); virtual ~CdrImportDialog(); bool showDialog(); @@ -86,12 +100,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector<WPXString> &_vec; // Document to be imported + const std::vector<RVNGString> &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -CdrImportDialog::CdrImportDialog(const std::vector<WPXString> &vec) +CdrImportDialog::CdrImportDialog(const std::vector<RVNGString> &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -216,14 +230,20 @@ void CdrImportDialog::_setPreviewPage(unsigned page) SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - WPXFileStream input(uri); + RVNGFileStream input(uri); if (!libcdr::CDRDocument::isSupported(&input)) { return NULL; } - libcdr::CDRStringVector output; + RVNGStringVector output; +#if WITH_LIBCDR01 + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libcdr::CDRDocument::parse(&input, &generator)) { +#else if (!libcdr::CDRDocument::generateSVG(&input, output)) { +#endif return NULL; } @@ -231,9 +251,9 @@ SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector<WPXString> tmpSVGOutput; + std::vector<RVNGString> tmpSVGOutput; for (unsigned i=0; i<output.size(); ++i) { - WPXString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + RVNGString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/pdfinput/pdf-input.cpp b/src/extension/internal/pdfinput/pdf-input.cpp index b2bf61321..c8a7feabf 100644 --- a/src/extension/internal/pdfinput/pdf-input.cpp +++ b/src/extension/internal/pdfinput/pdf-input.cpp @@ -647,7 +647,31 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { // Initialize the globalParams variable for poppler if (!globalParams) { +#ifdef ENABLE_OSX_APP_LOCATIONS + // + // data files for poppler are not relocatable (loaded from + // path defined at build time). This fails to work with relocatable + // application bundles for OS X. + // + // Workaround: + // 1. define $POPPLER_DATADIR env variable in app launcher script + // 2. pass custom $POPPLER_DATADIR via poppler's GlobalParams() + // + // relevant poppler commit: + // <http://cgit.freedesktop.org/poppler/poppler/commit/?id=869584a84eed507775ff1c3183fe484c14b6f77b> + // + // FIXES: Inkscape bug #956282, #1264793 + // TODO: report RFE upstream (full relocation support for OS X packaging) + // + gchar const *poppler_datadir = g_getenv("POPPLER_DATADIR"); + if (poppler_datadir != NULL) { + globalParams = new GlobalParams(poppler_datadir); + } else { + globalParams = new GlobalParams(); + } +#else globalParams = new GlobalParams(); +#endif // ENABLE_OSX_APP_LOCATIONS } // poppler does not use glib g_open. So on win32 we must use unicode call. code was copied from glib gstdio.c #ifndef WIN32 diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index c5f03e5aa..0b7dc7905 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -247,30 +247,74 @@ PdfOperator PdfParser::opTab[] = { #define numOps (sizeof(opTab) / sizeof(PdfOperator)) +namespace { + +GfxPatch blankPatch() +{ + GfxPatch patch; + memset(&patch, 0, sizeof(patch)); // quick-n-dirty + return patch; +} + +} // namespace + //------------------------------------------------------------------------ -// PdfParser +// ClipHistoryEntry //------------------------------------------------------------------------ -PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA, - int /*pageNum*/, int rotate, Dict *resDict, - PDFRectangle *box, PDFRectangle *cropBox) -{ - xref = xrefA; - subPage = gFalse; - printCommands = false; +class ClipHistoryEntry { +public: + + ClipHistoryEntry(GfxPath *clipPath = NULL, GfxClipType clipType = clipNormal); + virtual ~ClipHistoryEntry(); + + // Manipulate clip path stack + ClipHistoryEntry *save(); + ClipHistoryEntry *restore(); + GBool hasSaves() { return saved != NULL; } + void setClip(GfxPath *newClipPath, GfxClipType newClipType = clipNormal); + GfxPath *getClipPath() { return clipPath; } + GfxClipType getClipType() { return clipType; } - // start the resource stack - res = new GfxResources(xref, resDict, NULL); +private: - // initialize - state = new GfxState(72.0, 72.0, box, rotate, gTrue); - clipHistory = new ClipHistoryEntry(); + ClipHistoryEntry *saved; // next clip path on stack + + GfxPath *clipPath; // used as the path to be filled for an 'sh' operator + GfxClipType clipType; + + ClipHistoryEntry(ClipHistoryEntry *other); +}; + +//------------------------------------------------------------------------ +// PdfParser +//------------------------------------------------------------------------ + +PdfParser::PdfParser(XRef *xrefA, + Inkscape::Extension::Internal::SvgBuilder *builderA, + int /*pageNum*/, + int rotate, + Dict *resDict, + PDFRectangle *box, + PDFRectangle *cropBox) : + xref(xrefA), + builder(builderA), + subPage(gFalse), + printCommands(false), + res(new GfxResources(xref, resDict, NULL)), // start the resource stack + state(new GfxState(72.0, 72.0, box, rotate, gTrue)), + fontChanged(gFalse), + clip(clipNone), + ignoreUndef(0), + baseMatrix(), + formDepth(0), + parser(NULL), + colorDeltas(), + maxDepths(), + clipHistory(new ClipHistoryEntry()), + operatorHistory(NULL) +{ setDefaultApproximationPrecision(); - fontChanged = gFalse; - clip = clipNone; - ignoreUndef = 0; - operatorHistory = NULL; - builder = builderA; builder->setDocumentSize(Inkscape::Util::Quantity::convert(state->getPageWidth(), "pt", "px"), Inkscape::Util::Quantity::convert(state->getPageHeight(), "pt", "px")); @@ -306,50 +350,62 @@ PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *bui pushOperator("startPage"); } -PdfParser::PdfParser(XRef *xrefA, Inkscape::Extension::Internal::SvgBuilder *builderA, - Dict *resDict, PDFRectangle *box) { - - int i; - parser = NULL; - - xref = xrefA; - subPage = gTrue; - printCommands = false; - - // start the resource stack - res = new GfxResources(xref, resDict, NULL); - - // initialize - operatorHistory = NULL; - builder = builderA; - state = new GfxState(72, 72, box, 0, gFalse); - clipHistory = new ClipHistoryEntry(); +PdfParser::PdfParser(XRef *xrefA, + Inkscape::Extension::Internal::SvgBuilder *builderA, + Dict *resDict, + PDFRectangle *box) : + xref(xrefA), + builder(builderA), + subPage(gTrue), + printCommands(false), + res(new GfxResources(xref, resDict, NULL)), // start the resource stack + state(new GfxState(72, 72, box, 0, gFalse)), + fontChanged(gFalse), + clip(clipNone), + ignoreUndef(0), + baseMatrix(), + formDepth(0), + parser(NULL), + colorDeltas(), + maxDepths(), + clipHistory(new ClipHistoryEntry()), + operatorHistory(NULL) +{ setDefaultApproximationPrecision(); - fontChanged = gFalse; - clip = clipNone; - ignoreUndef = 0; - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { baseMatrix[i] = state->getCTM()[i]; } formDepth = 0; } PdfParser::~PdfParser() { - while (state->hasSaves()) { + while(operatorHistory) { + OpHistoryEntry *tmp = operatorHistory->next; + delete operatorHistory; + operatorHistory = tmp; + } + + while (state && state->hasSaves()) { restoreState(); } + if (!subPage) { //out->endPage(); } + while (res) { popResources(); } + if (state) { delete state; + state = NULL; } + if (clipHistory) { delete clipHistory; + clipHistory = NULL; } } @@ -460,7 +516,8 @@ void PdfParser::go(GBool /*topLevel*/) } } -void PdfParser::pushOperator(const char *name) { +void PdfParser::pushOperator(const char *name) +{ OpHistoryEntry *newEntry = new OpHistoryEntry; newEntry->name = name; newEntry->state = NULL; @@ -2086,13 +2143,19 @@ void PdfParser::doPatchMeshShFill(GfxPatchMeshShading *shading) { } void PdfParser::fillPatch(GfxPatch *patch, int nComps, int depth) { - GfxPatch patch00, patch01, patch10, patch11; + GfxPatch patch00 = blankPatch(); + GfxPatch patch01 = blankPatch(); + GfxPatch patch10 = blankPatch(); + GfxPatch patch11 = blankPatch(); #ifdef POPPLER_NEW_GFXPATCH - GfxColor color; + GfxColor color = {{0}}; #endif - double xx[4][8], yy[4][8]; - double xxm, yym; - double patchColorDelta = colorDeltas[pdfPatchMeshShading-1]; + double xx[4][8]; + double yy[4][8]; + double xxm; + double yym; + double patchColorDelta = colorDeltas[pdfPatchMeshShading - 1]; + int i; for (i = 0; i < nComps; ++i) { @@ -3463,9 +3526,7 @@ void PdfParser::popResources() { } void PdfParser::setDefaultApproximationPrecision() { - int i; - - for (i = 1; i <= pdfNumShadingTypes; ++i) { + for (int i = 1; i <= pdfNumShadingTypes; ++i) { setApproximationPrecision(i, defaultShadingColorDelta, defaultShadingMaxDepth); } } @@ -3484,19 +3545,18 @@ void PdfParser::setApproximationPrecision(int shadingType, double colorDelta, // ClipHistoryEntry //------------------------------------------------------------------------ -ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) { - if (clipPathA) { - clipPath = clipPathA->copy(); - } else { - clipPath = NULL; - } - clipType = clipTypeA; - saved = NULL; +ClipHistoryEntry::ClipHistoryEntry(GfxPath *clipPathA, GfxClipType clipTypeA) : + saved(NULL), + clipPath((clipPathA) ? clipPathA->copy() : NULL), + clipType(clipTypeA) +{ } -ClipHistoryEntry::~ClipHistoryEntry() { +ClipHistoryEntry::~ClipHistoryEntry() +{ if (clipPath) { delete clipPath; + clipPath = NULL; } } @@ -3510,6 +3570,7 @@ void ClipHistoryEntry::setClip(GfxPath *clipPathA, GfxClipType clipTypeA) { clipType = clipTypeA; } else { clipPath = NULL; + clipType = clipNormal; } } @@ -3526,7 +3587,7 @@ ClipHistoryEntry *ClipHistoryEntry::restore() { if (saved) { oldEntry = saved; saved = NULL; - delete this; + delete this; // TODO really should avoid deleting from inside. } else { oldEntry = this; } @@ -3540,6 +3601,7 @@ ClipHistoryEntry::ClipHistoryEntry(ClipHistoryEntry *other) { this->clipType = other->clipType; } else { this->clipPath = NULL; + this->clipType = clipNormal; } saved = NULL; } diff --git a/src/extension/internal/pdfinput/pdf-parser.h b/src/extension/internal/pdfinput/pdf-parser.h index a63d669c7..e28fecc2e 100644 --- a/src/extension/internal/pdfinput/pdf-parser.h +++ b/src/extension/internal/pdfinput/pdf-parser.h @@ -58,6 +58,8 @@ class AnnotBorderStyle; class PdfParser; +class ClipHistoryEntry; + //------------------------------------------------------------------------ #ifndef GFX_H @@ -101,34 +103,6 @@ struct OpHistoryEntry { }; //------------------------------------------------------------------------ -// ClipHistoryEntry -//------------------------------------------------------------------------ - -class ClipHistoryEntry { -public: - - ClipHistoryEntry(GfxPath *clipPath=NULL, GfxClipType clipType=clipNormal); - virtual ~ClipHistoryEntry(); - - // Manipulate clip path stack - ClipHistoryEntry *save(); - ClipHistoryEntry *restore(); - GBool hasSaves() { return saved != NULL; } - void setClip(GfxPath *newClipPath, GfxClipType newClipType=clipNormal); - GfxPath *getClipPath() { return clipPath; } - GfxClipType getClipType() { return clipType; } - -private: - - ClipHistoryEntry *saved; // next clip path on stack - - GfxPath *clipPath; // used as the path to be filled for an 'sh' operator - GfxClipType clipType; - - ClipHistoryEntry(ClipHistoryEntry *other); -}; - -//------------------------------------------------------------------------ // PdfParser //------------------------------------------------------------------------ diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp index 302a3a6f9..9f9bf2651 100644 --- a/src/extension/internal/vsd-input.cpp +++ b/src/extension/internal/vsd-input.cpp @@ -24,7 +24,22 @@ #include <cstring> #include <libvisio/libvisio.h> -#include <libwpd-stream/libwpd-stream.h> + +// TODO: Drop this check when librevenge is widespread. +#if WITH_LIBVISIO01 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; +#else + #include <libwpd-stream/libwpd-stream.h> + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef libvisio::VSDStringVector RVNGStringVector; +#endif + #include <gtkmm/alignment.h> #include <gtkmm/comboboxtext.h> @@ -59,7 +74,7 @@ namespace Internal { class VsdImportDialog : public Gtk::Dialog { public: - VsdImportDialog(const std::vector<WPXString> &vec); + VsdImportDialog(const std::vector<RVNGString> &vec); virtual ~VsdImportDialog(); bool showDialog(); @@ -85,12 +100,12 @@ private: class Gtk::VBox * vbox2; class Gtk::Widget * _previewArea; - const std::vector<WPXString> &_vec; // Document to be imported + const std::vector<RVNGString> &_vec; // Document to be imported unsigned _current_page; // Current selected page int _preview_width, _preview_height; // Size of the preview area }; -VsdImportDialog::VsdImportDialog(const std::vector<WPXString> &vec) +VsdImportDialog::VsdImportDialog(const std::vector<RVNGString> &vec) : _vec(vec), _current_page(1) { int num_pages = _vec.size(); @@ -215,14 +230,20 @@ void VsdImportDialog::_setPreviewPage(unsigned page) SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { - WPXFileStream input(uri); + RVNGFileStream input(uri); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - libvisio::VSDStringVector output; + RVNGStringVector output; +#if WITH_LIBVISIO01 + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libvisio::VisioDocument::parse(&input, &generator)) { +#else if (!libvisio::VisioDocument::generateSVG(&input, output)) { +#endif return NULL; } @@ -230,9 +251,9 @@ SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } - std::vector<WPXString> tmpSVGOutput; + std::vector<RVNGString> tmpSVGOutput; for (unsigned i=0; i<output.size(); ++i) { - WPXString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + RVNGString tmpString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); tmpString.append(output[i]); tmpSVGOutput.push_back(tmpString); } diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp index 14ff3ec77..12d86a99a 100644 --- a/src/extension/internal/wpg-input.cpp +++ b/src/extension/internal/wpg-input.cpp @@ -52,16 +52,24 @@ #include "util/units.h" #include <cstring> -// Take a guess and fallback to 0.1.x if no configure has run -#if !defined(WITH_LIBWPG01) && !defined(WITH_LIBWPG02) -#define WITH_LIBWPG01 1 +// Take a guess and fallback to 0.2.x if no configure has run +#if !defined(WITH_LIBWPG03) && !defined(WITH_LIBWPG02) +#define WITH_LIBWPG02 1 #endif #include "libwpg/libwpg.h" -#if WITH_LIBWPG01 -#include "libwpg/WPGStreamImplementation.h" -#elif WITH_LIBWPG02 -#include "libwpd-stream/libwpd-stream.h" +#if WITH_LIBWPG03 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGString; + using librevenge::RVNGFileStream; + using librevenge::RVNGInputStream; +#else + #include "libwpd-stream/libwpd-stream.h" + + typedef WPXString RVNGString; + typedef WPXFileStream RVNGFileStream; + typedef WPXInputStream RVNGInputStream; #endif using namespace libwpg; @@ -73,17 +81,15 @@ namespace Internal { SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { -#if WITH_LIBWPG01 - WPXInputStream* input = new libwpg::WPGFileStream(uri); -#elif WITH_LIBWPG02 - WPXInputStream* input = new WPXFileStream(uri); -#endif + RVNGInputStream* input = new RVNGFileStream(uri); +#if WITH_LIBWPG03 + if (input->isStructured()) { + RVNGInputStream* olestream = input->getSubStreamByName("PerfectOffice_MAIN"); +#else if (input->isOLEStream()) { -#if WITH_LIBWPG01 - WPXInputStream* olestream = input->getDocumentOLEStream(); -#elif WITH_LIBWPG02 - WPXInputStream* olestream = input->getDocumentOLEStream("PerfectOffice_MAIN"); + RVNGInputStream* olestream = input->getDocumentOLEStream("PerfectOffice_MAIN"); #endif + if (olestream) { delete input; input = olestream; @@ -98,15 +104,24 @@ SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u return NULL; } -#if WITH_LIBWPG01 - libwpg::WPGString output; -#elif WITH_LIBWPG02 - WPXString output; -#endif +#if WITH_LIBWPG03 + librevenge::RVNGStringVector vec; + librevenge::RVNGSVGDrawingGenerator generator(vec, ""); + + if (!libwpg::WPGraphics::parse(input, &generator) || vec.empty() || vec[0].empty()) { + delete input; + return NULL; + } + + RVNGString output("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); + output.append(vec[0]); +#else + RVNGString output; if (!libwpg::WPGraphics::generateSVG(input, output)) { delete input; return NULL; } +#endif //printf("I've got a doc: \n%s", painter.document.c_str()); diff --git a/src/extension/param/color.cpp b/src/extension/param/color.cpp index 0a2598c56..5bd70359f 100644 --- a/src/extension/param/color.cpp +++ b/src/extension/param/color.cpp @@ -59,9 +59,10 @@ guint32 ParamColor::set( guint32 in, SPDocument * /*doc*/, Inkscape::XML::Node * return _value; } -ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _changeSignal(0) +ParamColor::ParamColor(const gchar *name, const gchar *guitext, const gchar *desc, const Parameter::_scope_t scope, + bool gui_hidden, const gchar *gui_tip, Inkscape::Extension::Extension *ext, + Inkscape::XML::Node *xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), _value(0), _changeSignal(0) { const char * defaulthex = NULL; if (xml->firstChild() != NULL) @@ -75,7 +76,8 @@ ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * if (!paramval.empty()) defaulthex = paramval.data(); - _value = atoi(defaulthex); + if (defaulthex) + _value = atoi(defaulthex); } void ParamColor::string(std::string &string) const @@ -87,7 +89,7 @@ void ParamColor::string(std::string &string) const Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/, sigc::signal<void> * changeSignal ) { - if (_gui_hidden) return NULL; + if (_gui_hidden) return NULL; _changeSignal = new sigc::signal<void>(*changeSignal); Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); diff --git a/src/extension/param/enum.cpp b/src/extension/param/enum.cpp index bb50c06e1..74b2a75ad 100644 --- a/src/extension/param/enum.cpp +++ b/src/extension/param/enum.cpp @@ -41,8 +41,8 @@ namespace Extension { class enumentry { public: enumentry (Glib::ustring &val, Glib::ustring &text) : - value(val), - guitext(text) + value(val), + guitext(text) {} Glib::ustring value; @@ -50,16 +50,19 @@ public: }; -ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), _indent(0) +ParamComboBox::ParamComboBox(const gchar *name, const gchar *guitext, const gchar *desc, + const Parameter::_scope_t scope, bool gui_hidden, const gchar *gui_tip, + Inkscape::Extension::Extension *ext, Inkscape::XML::Node *xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext) + , _value(NULL) + , _indent(0) + , choices(NULL) { - choices = NULL; - _value = NULL; + const char *xmlval = NULL; // the value stored in XML - // Read XML tree to add enumeration items: - // printf("Extension Constructor: "); if (xml != NULL) { - for (Inkscape::XML::Node *node = xml->firstChild(); node; node = node->next()) { + // Read XML tree to add enumeration items: + for (Inkscape::XML::Node *node = xml->firstChild(); node; node = node->next()) { char const * chname = node->name(); if (!strcmp(chname, INKSCAPE_EXTENSION_NS "item") || !strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) { Glib::ustring newguitext, newvalue; @@ -69,8 +72,8 @@ ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const g } if (contents != NULL) { // don't translate when 'item' but do translate when '_item' - // NOTE: internal extensions use build_from_mem and don't need _item but - // still need to include if are to be localized + // NOTE: internal extensions use build_from_mem and don't need _item but + // still need to include if are to be localized if (!strcmp(chname, INKSCAPE_EXTENSION_NS "_item")) { if (node->attribute("msgctxt") != NULL) { newguitext = g_dpgettext2(NULL, node->attribute("msgctxt"), contents); @@ -95,30 +98,28 @@ ParamComboBox::ParamComboBox (const gchar * name, const gchar * guitext, const g } } } - } - - // Initialize _value with the default value from xml - // for simplicity : default to the contents of the first xml-child - const char * defaultval = NULL; - if (xml->firstChild() && xml->firstChild()->firstChild()) { - defaultval = xml->firstChild()->attribute("value"); - } + + // Initialize _value with the default value from xml + // for simplicity : default to the contents of the first xml-child + if (xml->firstChild() && xml->firstChild()->firstChild()) { + xmlval = xml->firstChild()->attribute("value"); + } - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; + const char *indent = xml->attribute("indent"); + if (indent != NULL) { + _indent = atoi(indent) * 12; + } } gchar * pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name); + Glib::ustring paramval = prefs ? prefs->getString(extension_pref_root + pref_name) : ""; g_free(pref_name); if (!paramval.empty()) { - defaultval = paramval.data(); - } - if (defaultval != NULL) { - _value = g_strdup(defaultval); + _value = g_strdup(paramval.data()); + } else if (xmlval) { + _value = g_strdup(xmlval); } } diff --git a/src/filters/blend.cpp b/src/filters/blend.cpp index fd5a9871e..ca1d5bf96 100644 --- a/src/filters/blend.cpp +++ b/src/filters/blend.cpp @@ -183,6 +183,8 @@ void SPFeBlend::update(SPCtx *ctx, guint flags) { /* Unlike normal in, in2 is required attribute. Make sure, we can call * it by some name. */ + /* This may not be true.... see issue at + * http://www.w3.org/TR/filter-effects/#feBlendElement (but it doesn't hurt). */ if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET || this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT) { @@ -206,26 +208,30 @@ Inkscape::XML::Node* SPFeBlend::write(Inkscape::XML::Document *doc, Inkscape::XM repr = doc->createElement("svg:feBlend"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + if( !in2_name ) { + + // This code is very similar to sp_filter_primtive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feBlend"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feBlend"); + } + char const *mode; switch(this->blend_mode) { case Inkscape::Filters::BLEND_NORMAL: diff --git a/src/filters/composite.cpp b/src/filters/composite.cpp index 257292f12..e600b6d20 100644 --- a/src/filters/composite.cpp +++ b/src/filters/composite.cpp @@ -205,6 +205,8 @@ void SPFeComposite::update(SPCtx *ctx, guint flags) { /* Unlike normal in, in2 is required attribute. Make sure, we can call * it by some name. */ + /* This may not be true.... see issue at + * http://www.w3.org/TR/filter-effects/#feBlendElement (but it doesn't hurt). */ if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET || this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT) { @@ -228,26 +230,30 @@ Inkscape::XML::Node* SPFeComposite::write(Inkscape::XML::Document *doc, Inkscape repr = doc->createElement("svg:feComposite"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + if( !in2_name ) { + + // This code is very similar to sp_filter_primitive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feComposite"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feComposite"); + } + char const *comp_op; switch (this->composite_operator) { diff --git a/src/filters/displacementmap.cpp b/src/filters/displacementmap.cpp index 473e4913f..7dedfc031 100644 --- a/src/filters/displacementmap.cpp +++ b/src/filters/displacementmap.cpp @@ -202,25 +202,30 @@ Inkscape::XML::Node* SPFeDisplacementMap::write(Inkscape::XML::Document *doc, In repr = doc->createElement("svg:feDisplacementMap"); } - gchar const *out_name = sp_filter_name_for_image(parent, this->in2); - if (out_name) { - repr->setAttribute("in2", out_name); - } else { + gchar const *in2_name = sp_filter_name_for_image(parent, this->in2); + + if( !in2_name ) { + + // This code is very similar to sp_filter_primtive_name_previous_out() SPObject *i = parent->children; + // Find previous filter primitive while (i && i->next != this) { i = i->next; } - SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); - out_name = sp_filter_name_for_image(parent, i_prim->image_out); - repr->setAttribute("in2", out_name); - - if (!out_name) { - g_warning("Unable to set in2 for feDisplacementMap"); + if( i ) { + SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i); + in2_name = sp_filter_name_for_image(parent, i_prim->image_out); } } + if (in2_name) { + repr->setAttribute("in2", in2_name); + } else { + g_warning("Unable to set in2 for feDisplacementMap"); + } + sp_repr_set_svg_double(repr, "scale", this->scale); repr->setAttribute("xChannelSelector", get_channelselector_name(this->xChannelSelector)); diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index 46df0181a..bd5a4460c 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -21,7 +21,7 @@ #include <pango/pango.h> #include "nr-type-primitives.h" -#include "../style.h" +#include "style.h" /* Freetype */ #ifdef USE_PANGO_WIN32 diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index 7c1913076..b52816c87 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -132,8 +132,8 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) int path_in_complexity = 0; for (unsigned k = 0; k < path_in.size(); k++){ path_in_complexity+=path_in[k].size(); - } - double complexity = std::pow(transforms.size(),nbgenerations)*path_in_complexity; + } + double complexity = std::pow(transforms.size(), nbgenerations) * path_in_complexity; if (drawall.get_value()){ int k = transforms.size(); if(k>1){ @@ -141,8 +141,6 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) }else{ complexity = nbgenerations*k*path_in_complexity; } - }else{ - complexity = std::pow(transforms.size(),nbgenerations)*path_in_complexity; } if (complexity > double(maxComplexity)){ g_warning("VonKoch lpe's output too complex. Effect bypassed."); diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 9ff6df3bf..2b29814b0 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -289,7 +289,7 @@ void Inkscape::ObjectSnapper::_snapNodes(IntermSnapResults &isr, for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); ++k) { if (_allowSourceToSnapToTarget(p.getSourceType(), (*k).getTargetType(), strict_snapping)) { Geom::Point target_pt = (*k).getPoint(); - Geom::Coord dist = Geom::infinity(); + Geom::Coord dist = Geom::L2(target_pt - p.getPoint()); // Default: free (unconstrained) snapping if (!c.isUndefined()) { // We're snapping to nodes along a constraint only, so find out if this node // is at the constraint, while allowing for a small margin @@ -299,9 +299,6 @@ void Inkscape::ObjectSnapper::_snapNodes(IntermSnapResults &isr, continue; } dist = Geom::L2(target_pt - p_proj_on_constraint); - } else { - // Free (unconstrained) snapping - dist = Geom::L2(target_pt - p.getPoint()); } if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index a72601276..fe241381f 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -79,6 +79,7 @@ sp_selected_path_combine(SPDesktop *desktop) items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); items = g_slist_reverse(items); + assert(items); // cannot be NULL because of list length check at top of function // remember the position, id, transform and style of the topmost path, they will be assigned to the combined one gint position = 0; diff --git a/src/path-prefix.h b/src/path-prefix.h index 6ef0ccbe9..7042d5124 100644 --- a/src/path-prefix.h +++ b/src/path-prefix.h @@ -66,23 +66,23 @@ # define CREATE_PALETTESDIR WIN32_DATADIR("create\\swatches") # define CREATE_PATTERNSDIR WIN32_DATADIR("create\\patterns\\vector") # elif defined ENABLE_OSX_APP_LOCATIONS -# define INKSCAPE_APPICONDIR "Contents/Resources/pixmaps" -# define INKSCAPE_ATTRRELDIR "Contents/Resources/attributes" -# define INKSCAPE_BINDDIR "Contents/Resources/bind" -# define INKSCAPE_EXAMPLESDIR "Contents/Resources/examples" -# define INKSCAPE_EXTENSIONDIR "Contents/Resources/extensions" -# define INKSCAPE_FILTERDIR "Contents/Resources/filters" -# define INKSCAPE_GRADIENTSDIR "Contents/Resources/gradients" -# define INKSCAPE_KEYSDIR "Contents/Resources/keys" -# define INKSCAPE_PIXMAPDIR "Contents/Resources/icons" -# define INKSCAPE_MARKERSDIR "Contents/Resources/markers" -# define INKSCAPE_PALETTESDIR "Contents/Resources/palettes" -# define INKSCAPE_PATTERNSDIR "Contents/Resources/patterns" -# define INKSCAPE_SCREENSDIR "Contents/Resources/screens" -# define INKSCAPE_SYMBOLSDIR "Contents/Resources/symbols" -# define INKSCAPE_TUTORIALSDIR "Contents/Resources/tutorials" -# define INKSCAPE_TEMPLATESDIR "Contents/Resources/templates" -# define INKSCAPE_UIDIR "Contents/Resources/ui" +# define INKSCAPE_APPICONDIR "Contents/Resources/share/pixmaps" +# define INKSCAPE_ATTRRELDIR "Contents/Resources/share/inkscape/attributes" +# define INKSCAPE_BINDDIR "Contents/Resources/share/inkscape/bind" +# define INKSCAPE_EXAMPLESDIR "Contents/Resources/share/inkscape/examples" +# define INKSCAPE_EXTENSIONDIR "Contents/Resources/share/inkscape/extensions" +# define INKSCAPE_FILTERDIR "Contents/Resources/share/inkscape/filters" +# define INKSCAPE_GRADIENTSDIR "Contents/Resources/share/inkscape/gradients" +# define INKSCAPE_KEYSDIR "Contents/Resources/share/inkscape/keys" +# define INKSCAPE_PIXMAPDIR "Contents/Resources/share/inkscape/icons" +# define INKSCAPE_MARKERSDIR "Contents/Resources/share/inkscape/markers" +# define INKSCAPE_PALETTESDIR "Contents/Resources/share/inkscape/palettes" +# define INKSCAPE_PATTERNSDIR "Contents/Resources/share/inkscape/patterns" +# define INKSCAPE_SCREENSDIR "Contents/Resources/share/inkscape/screens" +# define INKSCAPE_SYMBOLSDIR "Contents/Resources/share/inkscape/symbols" +# define INKSCAPE_TUTORIALSDIR "Contents/Resources/share/inkscape/tutorials" +# define INKSCAPE_TEMPLATESDIR "Contents/Resources/share/inkscape/templates" +# define INKSCAPE_UIDIR "Contents/Resources/share/inkscape/ui" //CREATE V0.1 support # define CREATE_GRADIENTSDIR "/Library/Application Support/create/gradients/gimp" # define CREATE_PALETTESDIR "/Library/Application Support/create/swatches" diff --git a/src/sp-hatch-path.cpp b/src/sp-hatch-path.cpp new file mode 100644 index 000000000..f7138fac2 --- /dev/null +++ b/src/sp-hatch-path.cpp @@ -0,0 +1,331 @@ +/** @file + * SVG <hatchPath> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <2geom/path.h> +#include <2geom/transforms.h> + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/curve.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-shape.h" +#include "helper/geom.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch-path.h" +#include "svg/css-ostringstream.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatchPath() { + return new SPHatchPath(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatchPath", createHatchPath); +} + +SPHatchPath::SPHatchPath() + : _curve(NULL) + , _continuous(false) +{ + offset.unset(); +} + +SPHatchPath::~SPHatchPath() { +} + +void SPHatchPath::setCurve(SPCurve *new_curve, bool owner) { + if (_curve) { + _curve = _curve->unref(); + } + + if (new_curve) { + if (owner) { + _curve = new_curve->ref(); + } else { + _curve = new_curve->copy(); + } + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void SPHatchPath::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPObject::build(doc, repr); + + this->readAttr("d"); + this->readAttr("offset"); + this->readAttr( "style" ); + + style->fill.setNone(); +} + +void SPHatchPath::release() { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + delete iter->arenaitem; + iter->arenaitem = NULL; + } + + SPObject::release(); +} + +void SPHatchPath::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_D: + if (value) { + Geom::PathVector pv; + _readHatchPathVector(value, pv, _continuous); + SPCurve *curve = new SPCurve(pv); + + if (curve) { + this->setCurve(curve, true); + curve->unref(); + } + } else { + this->setCurve(NULL, true); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_OFFSET: + offset.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPObject::set(key, value); + } + break; + } +} + + +void SPHatchPath::update(SPCtx* ctx, unsigned int flags) { + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; + } + + if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + if (this->style->stroke_width.unit == SP_CSS_UNIT_PERCENT) { + //TODO: Check specification + + SPItemCtx *ictx = (SPItemCtx *) ctx; + double const aw = 1.0 / ictx->i2vp.descrim(); + this->style->stroke_width.computed = this->style->stroke_width.value * aw; + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + iter->arenaitem->setStyle(this->style); + } + } + } + + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } + } +} + +bool SPHatchPath::isValid() const { + if (_curve && (_repeatLength() <= 0)) { + return false; + } + return true; +} + +Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents) { + Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing); + _display.push_front(View(s, key)); + _display.front().extents = extents; + + _updateView(_display.front()); + + return s; +} + +void SPHatchPath::hide(unsigned int key) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + +void SPHatchPath::setStripExtents(unsigned int key, Geom::OptInterval const &extents) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->extents = extents; + break; + } + } +} + +Geom::Interval SPHatchPath::bounds() const { + Geom::OptRect bbox; + Geom::Interval result; + + Geom::Affine transform = Geom::Translate(offset.computed, 0); + if (!this->_curve) { + SPCurve test_curve; + test_curve.moveto(Geom::Point(0, 0)); + test_curve.moveto(Geom::Point(0, 1)); + bbox = bounds_exact_transformed(test_curve.get_pathvector(), transform); + } else { + bbox = bounds_exact_transformed(this->_curve->get_pathvector(), transform); + } + + gdouble stroke_width = style->stroke_width.computed; + result.setMin(bbox->left() - stroke_width / 2); + result.setMax(bbox->right() + stroke_width / 2); + return result; +} + +SPCurve *SPHatchPath::calculateRenderCurve(unsigned key) const { + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderCurve(*iter); + } + } + g_assert_not_reached(); + return NULL; +} + +gdouble SPHatchPath::_repeatLength() const { + if (!_curve) { + return 0; + } + + if (!_curve->last_point()) { + return 0; + } + + return _curve->last_point()->y(); +} + +void SPHatchPath::_updateView(View &view) { + SPCurve *calculated_curve = _calculateRenderCurve(view); + + Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); + view.arenaitem->setTransform(offset_transform); + style->fill.setNone(); + view.arenaitem->setStyle(this->style); + view.arenaitem->setPath(calculated_curve); + + calculated_curve->unref(); +} + +SPCurve *SPHatchPath::_calculateRenderCurve(View const &view) const { + SPCurve *calculated_curve = new SPCurve; + + if (!view.extents) { + return calculated_curve; + } + + if (!_curve) { + calculated_curve->moveto(0, view.extents->min()); + calculated_curve->lineto(0, view.extents->max()); + //TODO: if hatch has a dasharray defined, adjust line ends + } else { + gdouble repeatLength = _repeatLength(); + if (repeatLength > 0) { + gdouble initial_y = floor(view.extents->min() / repeatLength) * repeatLength; + int segment_cnt = ceil((view.extents->extent()) / repeatLength) + 1; + + SPCurve *segment =_curve->copy(); + segment->transform(Geom::Translate(0, initial_y)); + + Geom::Affine step_transform = Geom::Translate(0, repeatLength); + for (int i = 0; i < segment_cnt; i++) { + if (_continuous) { + calculated_curve->append_continuous(segment, 0.0625); + } else { + calculated_curve->append(segment, false); + } + segment->transform(step_transform); + } + + segment->unref(); + } + } + return calculated_curve; +} + + +void SPHatchPath::_readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join) { + if (!str) { + return; + } + + pathv = sp_svg_read_pathv(str); + + if (!pathv.empty()) { + continous_join = false; + } else { + Glib::ustring str2 = Glib::ustring::compose("M0,0 %1", str); + pathv = sp_svg_read_pathv(str2.c_str()); + if (pathv.empty()) { + return; + } + + gdouble last_point_x = pathv.back().finalPoint().x(); + Inkscape::CSSOStringStream stream; + stream << last_point_x; + Glib::ustring str3 = Glib::ustring::compose("M%1,0 %2", stream.str(), str); + Geom::PathVector pathv3 = sp_svg_read_pathv(str3.c_str()); + + //Path can be composed of relative commands only. In this case final point + //coordinates would depend on first point position. If this happens, fall + //back to using 0,0 as first path point + if (pathv3.back().finalPoint().y() == pathv.back().finalPoint().y()) { + pathv = pathv3; + } + continous_join = true; + } +} + +SPHatchPath::View::View(Inkscape::DrawingShape *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} + + +/* + 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 : diff --git a/src/sp-hatch-path.h b/src/sp-hatch-path.h new file mode 100644 index 000000000..57b3a8237 --- /dev/null +++ b/src/sp-hatch-path.h @@ -0,0 +1,91 @@ +/** @file + * SVG <hatchPath> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_PATH_H +#define SEEN_SP_HATCH_PATH_H + +#include <list> +#include <stddef.h> +#include <glibmm/ustring.h> +#include <sigc++/connection.h> + +#include "svg/svg-length.h" + +namespace Inkscape { + +class Drawing; +class DrawingShape; + +} + +#define SP_HATCH_PATH(obj) (dynamic_cast<SPHatchPath*>((SPObject*)obj)) +#define SP_IS_HATCH_PATH(obj) (dynamic_cast<const SPHatchPath*>((SPObject*)obj) != NULL) + +class SPHatchPath : public SPObject { +public: + SPHatchPath(); + virtual ~SPHatchPath(); + + SVGLength offset; + + void setCurve(SPCurve *curve, bool owner); + + bool isValid() const; + + Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents); + void hide(unsigned int key); + + void setStripExtents(unsigned int key, Geom::OptInterval const &extents); + Geom::Interval bounds() const; + + SPCurve *calculateRenderCurve(unsigned key) const; + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingShape *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingShape *arenaitem; + Geom::OptInterval extents; + unsigned int key; + }; + typedef std::list<SPHatchPath::View>::iterator ViewIterator; + typedef std::list<SPHatchPath::View>::const_iterator ConstViewIterator; + std::list<View> _display; + + gdouble _repeatLength() const; + void _updateView(View &view); + SPCurve *_calculateRenderCurve(View const &view) const; + + void _readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join); + + SPCurve *_curve; + bool _continuous; +}; + +#endif // SEEN_SP_HATCH_PATH_H + +/* + 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 : diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp new file mode 100644 index 000000000..b007fc846 --- /dev/null +++ b/src/sp-hatch.cpp @@ -0,0 +1,679 @@ +/** @file + * SVG <hatch> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <2geom/transforms.h> +#include <sigc++/functors/mem_fun.h> + +#include "svg/svg.h" +#include "display/cairo-utils.h" +#include "display/drawing-context.h" +#include "display/drawing-surface.h" +#include "display/drawing.h" +#include "display/drawing-pattern.h" +#include "attributes.h" +#include "document-private.h" +#include "uri.h" +#include "style.h" +#include "sp-hatch.h" +#include "sp-hatch-path.h" +#include "xml/repr.h" + +#include "sp-factory.h" + +namespace { +SPObject* createHatch() { + return new SPHatch(); +} + +bool hatchRegistered = SPFactory::instance().registerObject("svg:hatch", createHatch); +} + +SPHatch::SPHatch() + : SPPaintServer() +{ + this->ref = new SPHatchReference(this); + this->ref->changedSignal().connect(sigc::mem_fun(this, &SPHatch::_onRefChanged)); + + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + this->_hatchUnits_set = false; + + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + this->_hatchContentUnits_set = false; + + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + + this->_x.unset(); + this->_y.unset(); + this->_pitch.unset(); + this->_rotate.unset(); +} + +SPHatch::~SPHatch() { +} + +void SPHatch::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPPaintServer::build(doc, repr); + + this->readAttr("hatchUnits"); + this->readAttr("hatchContentUnits"); + this->readAttr("hatchTransform"); + this->readAttr("x"); + this->readAttr("y"); + this->readAttr("pitch"); + this->readAttr("rotate"); + this->readAttr("xlink:href"); + this->readAttr( "style" ); + + /* Register ourselves */ + doc->addResource("hatch", this); +} + +void SPHatch::release() { + if (this->document) { + // Unregister ourselves + this->document->removeResource("hatch", this); + } + + std::vector<SPHatchPath *> children; + hatchPaths(children); + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + for (ChildIterator child_iter = children.begin(); child_iter != children.end(); + child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(view_iter->key); + } + delete view_iter->arenaitem; + view_iter->arenaitem = NULL; + } + + if (this->ref) { + this->_modified_connection.disconnect(); + this->ref->detach(); + delete this->ref; + this->ref = NULL; + } + + SPPaintServer::release(); +} + +void SPHatch::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) { + SPObject::child_added(child, ref); + + SPHatchPath *path_child = SP_HATCH_PATH(this->document->getObjectByRepr(child)); + + if (path_child) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + Inkscape::DrawingItem *ac = path_child->show(iter->arenaitem->drawing(), iter->key, extents); + + path_child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (ac) { + iter->arenaitem->prependChild(ac); + } + } + } + //FIXME: notify all hatches that refer to this child set +} + +void SPHatch::set(unsigned int key, const gchar* value) { + switch (key) { + case SP_ATTR_HATCHUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchUnits_set = true; + } else { + this->_hatchUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHCONTENTUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_hatchContentUnits = UNITS_USERSPACEONUSE; + } else { + this->_hatchContentUnits = UNITS_OBJECTBOUNDINGBOX; + } + + this->_hatchContentUnits_set = true; + } else { + this->_hatchContentUnits_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HATCHTRANSFORM: { + Geom::Affine t; + + if (value && sp_svg_transform_read(value, &t)) { + this->_hatchTransform = t; + this->_hatchTransform_set = true; + } else { + this->_hatchTransform = Geom::identity(); + this->_hatchTransform_set = false; + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + } + case SP_ATTR_X: + this->_x.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_Y: + this->_y.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_PITCH: + this->_pitch.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_ROTATE: + this->_rotate.readOrUnset(value); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_XLINK_HREF: + if (value && this->href == value) { + /* Href unchanged, do nothing. */ + } else { + this->href.clear(); + + if (value) { + // First, set the href field; it's only used in the "unchanged" check above. + this->href = value; + // Now do the attaching, which emits the changed signal. + if (value) { + try { + this->ref->attach(Inkscape::URI(value)); + } catch (Inkscape::BadURIException &e) { + g_warning("%s", e.what()); + this->ref->detach(); + } + } else { + this->ref->detach(); + } + } + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + default: + if (SP_ATTRIBUTE_IS_CSS(key)) { + sp_style_read_from_object(this->style, this); + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } else { + SPPaintServer::set(key, value); + } + break; + } +} + +bool SPHatch::_hasHatchPatchChildren(SPHatch const *hatch) { + for (SPObject const *child = hatch->firstChild(); child; child = child->getNext() ) { + if (SP_IS_HATCH_PATH(child)) { + return true; + } + } + return false; +} + +void SPHatch::hatchPaths(std::vector<SPHatchPath*>& l) { + SPHatch *src = chase_hrefs<SPHatch>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +void SPHatch::hatchPaths(std::vector<SPHatchPath const*>& l) const { + SPHatch const *src = chase_hrefs<SPHatch const>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + + if (src) { + for (SPObject const *child = src->firstChild(); child; child = child->getNext()) { + if (SP_IS_HATCH_PATH(child)) { + l.push_back(SP_HATCH_PATH(child)); + } + } + } +} + +/* TODO: ::remove_child and ::order_changed handles - see SPPattern */ + + +void SPHatch::update(SPCtx* ctx, unsigned int flags) { + typedef std::list<SPHatch::View>::iterator ViewIterator; + + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector<SPHatchPath *> children; + hatchPaths(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath* child = *iter; + + sp_object_ref(child, NULL); + + for (ViewIterator view_iter = _display.begin(); view_iter != _display.end(); view_iter++) { + Geom::OptInterval strip_extents = _calculateStripExtents(view_iter->bbox); + child->setStripExtents(view_iter->key, strip_extents); + } + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + + child->updateDisplay(ctx, flags); + } + + sp_object_unref(child, NULL); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + _updateView(*iter); + } +} + +void SPHatch::modified(unsigned int flags) { + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + flags &= SP_OBJECT_MODIFIED_CASCADE; + + std::vector<SPHatchPath *> children; + hatchPaths(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPObject *child = *iter; + + sp_object_ref(child, NULL); + + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->emitModified(flags); + } + + sp_object_unref(child, NULL); + } +} + +void SPHatch::_onRefChanged(SPObject *old_ref, SPObject *ref) { + typedef std::list<SPHatch::View>::iterator ViewIterator; + + if (old_ref) { + _modified_connection.disconnect(); + } + + if (SP_IS_HATCH(ref)) { + _modified_connection = ref->connectModified(sigc::mem_fun(this, &SPHatch::_onRefModified)); + } + + if (!_hasHatchPatchChildren(this)) { + SPHatch *old_shown = NULL; + SPHatch *new_shown = NULL; + std::vector<SPHatchPath *> oldhatchPaths; + std::vector<SPHatchPath *> newhatchPaths; + if (SP_IS_HATCH(old_ref)) { + old_shown = SP_HATCH(old_ref)->rootHatch(); + old_shown->hatchPaths(oldhatchPaths); + } + if (SP_IS_HATCH(ref)) { + new_shown = SP_HATCH(ref)->rootHatch(); + new_shown->hatchPaths(newhatchPaths); + } + if (old_shown != new_shown) { + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + Geom::OptInterval extents = _calculateStripExtents(iter->bbox); + + for (ChildIterator child_iter = oldhatchPaths.begin(); child_iter != oldhatchPaths.end(); child_iter++) { + SPHatchPath *child = *child_iter; + child->hide(iter->key); + } + for (ChildIterator child_iter = newhatchPaths.begin(); child_iter != newhatchPaths.end(); child_iter++) { + SPHatchPath *child = *child_iter; + Inkscape::DrawingItem *cai = child->show(iter->arenaitem->drawing(), iter->key, extents); + child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (cai) { + iter->arenaitem->appendChild(cai); + } + + } + } + } + } + + _onRefModified(ref, 0); +} + +void SPHatch::_onRefModified(SPObject */*ref*/, guint /*flags*/) { + requestModified(SP_OBJECT_MODIFIED_FLAG); + // Conditional to avoid causing infinite loop if there's a cycle in the href chain. +} + + +SPHatch *SPHatch::rootHatch() { + SPHatch *src = chase_hrefs<SPHatch>(this, sigc::ptr_fun(&_hasHatchPatchChildren)); + return src ? src : this; // document is broken, we can't get to root; but at least we can return pat which is supposedly a valid hatch +} + +// Access functions that look up fields up the chain of referenced hatchs and return the first one which is set +// FIXME: all of them must use chase_hrefs as children() and rootHatch() + +SPHatch::HatchUnits SPHatch::hatchUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchUnits_set) + return pat_i->_hatchUnits; + } + return _hatchUnits; +} + +SPHatch::HatchUnits SPHatch::hatchContentUnits() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchContentUnits_set) + return pat_i->_hatchContentUnits; + } + return _hatchContentUnits; +} + +Geom::Affine const &SPHatch::hatchTransform() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_hatchTransform_set) + return pat_i->_hatchTransform; + } + return _hatchTransform; +} + +gdouble SPHatch::x() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_x._set) + return pat_i->_x.computed; + } + return 0; +} + +gdouble SPHatch::y() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_y._set) + return pat_i->_y.computed; + } + return 0; +} + +gdouble SPHatch::pitch() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_pitch._set) + return pat_i->_pitch.computed; + } + return 0; +} + +gdouble SPHatch::rotate() const { + for (SPHatch const *pat_i = this; pat_i != NULL; + pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_rotate._set) + return pat_i->_rotate.computed; + } + return 0; +} + +bool SPHatch::isValid() const { + double strip_pitch = pitch(); + if (strip_pitch <= 0) { + return false; + } + + std::vector<SPHatchPath const *> children; + hatchPaths(children); + if (children.empty()) { + return false; + } + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (!child->isValid()) { + return false; + } + } + + return true; +} + +Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox) { + Inkscape::DrawingPattern *ai = new Inkscape::DrawingPattern(drawing); + //TODO: set some debug flag to see DrawingPattern + _display.push_front(View(ai, key)); + _display.front().bbox = bbox; + + std::vector<SPHatchPath *> children; + hatchPaths(children); + + Geom::OptInterval extents = _calculateStripExtents(bbox); + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + Inkscape::DrawingItem *cai = child->show(drawing, key, extents); + if (cai) { + ai->appendChild(cai); + } + } + + View& view = _display.front(); + _updateView(view); + + return ai; +} + +void SPHatch::hide(unsigned int key) { + std::vector<SPHatchPath *> children; + hatchPaths(children); + + for (ChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath *child = *iter; + child->hide(key); + } + + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + delete iter->arenaitem; + _display.erase(iter); + return; + } + } + + g_assert_not_reached(); +} + + +Geom::Interval SPHatch::bounds() const { + Geom::Interval result; + std::vector<SPHatchPath const *> children; + hatchPaths(children); + + for (ConstChildIterator iter = children.begin(); iter != children.end(); iter++) { + SPHatchPath const *child = *iter; + if (result.extent() == 0) { + result = child->bounds(); + } else { + result |= child->bounds(); + } + } + return result; +} + +SPHatch::RenderInfo SPHatch::calculateRenderInfo(unsigned key) const { + RenderInfo info; + for (ConstViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + return _calculateRenderInfo(*iter); + } + } + g_assert_not_reached(); + return info; +} + +void SPHatch::_updateView(View &view) { + RenderInfo info = _calculateRenderInfo(view); + //The rendering of hatch overflow is implemented by repeated drawing + //of hatch paths over one strip. Within each iteration paths are moved by pitch value. + //The movement progresses from right to left. This gives the same result + //as drawing whole strips in left-to-right order. + + + view.arenaitem->setChildTransform(info.child_transform); + view.arenaitem->setPatternToUserTransform(info.pattern_to_user_transform); + view.arenaitem->setTileRect(info.tile_rect); + view.arenaitem->setStyle(this->style); + view.arenaitem->setOverflow(info.overflow_initial_transform, info.overflow_steps, + info.overflow_step_transform); +} + +SPHatch::RenderInfo SPHatch::_calculateRenderInfo(View const &view) const { + RenderInfo info; + + Geom::OptInterval extents = _calculateStripExtents(view.bbox); + if (!extents) { + return info; + } + + double tile_x = x(); + double tile_y = y(); + double tile_width = pitch(); + double tile_height = extents->max() - extents->min(); + double tile_rotate = rotate(); + double tile_render_y = extents->min(); + + if (view.bbox && (hatchUnits() == UNITS_OBJECTBOUNDINGBOX)) { + tile_x *= view.bbox->width(); + tile_y *= view.bbox->height(); + tile_width *= view.bbox->width(); + tile_height *= view.bbox->height(); + tile_render_y *= view.bbox->height(); + } + + // Pattern size in hatch space + Geom::Rect hatch_tile = Geom::Rect::from_xywh(0, tile_render_y, tile_width, tile_height); + // Content to bbox + Geom::Affine content2ps; + if (view.bbox && (hatchContentUnits() == UNITS_OBJECTBOUNDINGBOX)) { + content2ps = Geom::Affine(view.bbox->width(), 0.0, 0.0, view.bbox->height(), 0, 0); + } + + // Tile (hatch space) to user. + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + + info.child_transform = content2ps; + info.pattern_to_user_transform = ps2user; + info.tile_rect = hatch_tile; + + if (style->overflow.computed == SP_CSS_OVERFLOW_VISIBLE) { + Geom::Interval bounds = this->bounds(); + gdouble pitch = this->pitch(); + gdouble overflow_right_strip = floor(bounds.max() / pitch) * pitch; + info.overflow_steps = ceil((overflow_right_strip - bounds.min()) / pitch) + 1; + info.overflow_step_transform = Geom::Translate(pitch, 0.0); + info.overflow_initial_transform = Geom::Translate(-overflow_right_strip, 0.0); + } else { + info.overflow_steps = 1; + } + + return info; +} + +//calculates strip extents in content space +Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect bbox) const { + if (!bbox || (bbox->area() == 0)) { + return Geom::OptInterval(); + } + + double tile_x = x(); + double tile_y = y(); + double tile_rotate = rotate(); + + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * Geom::Rotate::from_degrees(tile_rotate) * hatchTransform(); + Geom::Affine user2ps = ps2user.inverse(); + + Geom::Interval extents; + for (int i = 0; i < 4; i++) { + Geom::Point corner = bbox->corner(i); + Geom::Point corner_ps = corner * user2ps; + if (i == 0 || corner_ps.y() < extents.min()) { + extents.setMin(corner_ps.y()); + } + if (i == 0 || corner_ps.y() > extents.max()) { + extents.setMax(corner_ps.y()); + } + } + + if (hatchUnits() == UNITS_OBJECTBOUNDINGBOX) { + extents /= bbox->height(); + } + + return extents; +} + +cairo_pattern_t* SPHatch::pattern_new(cairo_t * /*base_ct*/, Geom::OptRect const &/*bbox*/, double /*opacity*/) +{ + //this code should not be used + //it is however required by the fact that SPPaintServer::hatch_new is pure virtual + return cairo_pattern_create_rgb(0.5, 0.5, 1.0); +} + +void SPHatch::setBBox(unsigned int key, Geom::OptRect const &bbox) { + for (ViewIterator iter = _display.begin(); iter != _display.end(); iter++) { + if (iter->key == key) { + iter->bbox = bbox; + break; + } + } +} + +SPHatch::View::View(Inkscape::DrawingPattern *arenaitem, int key) + : arenaitem(arenaitem), key(key) +{ +} +/* + 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 : diff --git a/src/sp-hatch.h b/src/sp-hatch.h new file mode 100644 index 000000000..dc6ee0add --- /dev/null +++ b/src/sp-hatch.h @@ -0,0 +1,174 @@ +/** @file + * SVG <hatch> implementation + *//* + * Author: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Tomasz Boczkowski + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_SP_HATCH_H +#define SEEN_SP_HATCH_H + +#include <list> +#include <stddef.h> +#include <glibmm/ustring.h> +#include <sigc++/connection.h> + +#include "svg/svg-length.h" +#include "svg/svg-angle.h" +#include "sp-paint-server.h" +#include "uri-references.h" + +class SPHatchReference; +class SPHatchPath; +class SPItem; + +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +namespace XML { + +class Node; + +} +} + +#define SP_HATCH(obj) (dynamic_cast<SPHatch*>((SPObject*)obj)) +#define SP_IS_HATCH(obj) (dynamic_cast<const SPHatch*>((SPObject*)obj) != NULL) + +class SPHatch : public SPPaintServer { +public: + enum HatchUnits { + UNITS_USERSPACEONUSE, + UNITS_OBJECTBOUNDINGBOX + }; + + struct RenderInfo { + Geom::Affine child_transform; + Geom::Affine pattern_to_user_transform; + Geom::Rect tile_rect; + + int overflow_steps; + Geom::Affine overflow_step_transform; + Geom::Affine overflow_initial_transform; + }; + + SPHatch(); + virtual ~SPHatch(); + + /* Reference (href) */ + Glib::ustring href; + SPHatchReference *ref; + + gdouble x() const; + gdouble y() const; + gdouble pitch() const; + gdouble rotate() const; + HatchUnits hatchUnits() const; + HatchUnits hatchContentUnits() const; + Geom::Affine const &hatchTransform() const; + SPHatch *rootHatch(); //TODO: const + + void hatchPaths(std::vector<SPHatchPath*>& l); + void hatchPaths(std::vector<SPHatchPath const *>& l) const; + + bool isValid() const; + + Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox); + void hide(unsigned int key); + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); + + RenderInfo calculateRenderInfo(unsigned key) const; + Geom::Interval bounds() const; + void setBBox(unsigned int key, Geom::OptRect const &bbox); + +protected: + virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); + virtual void release(); + virtual void child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref); + virtual void set(unsigned int key, const gchar* value); + virtual void update(SPCtx* ctx, unsigned int flags); + virtual void modified(unsigned int flags); + +private: + struct View { + View(Inkscape::DrawingPattern *arenaitem, int key); + //Do not delete arenaitem in destructor. + + Inkscape::DrawingPattern *arenaitem; + Geom::OptRect bbox; + unsigned int key; + }; + + typedef std::vector<SPHatchPath *>::iterator ChildIterator; + typedef std::vector<SPHatchPath const *>::const_iterator ConstChildIterator; + typedef std::list<View>::iterator ViewIterator; + typedef std::list<View>::const_iterator ConstViewIterator; + + static bool _hasHatchPatchChildren(SPHatch const* hatch); + + void _updateView(View &view); + RenderInfo _calculateRenderInfo(View const &view) const; + Geom::OptInterval _calculateStripExtents(Geom::OptRect bbox) const; + + + /** + Gets called when the hatch is reattached to another <hatch> + */ + void _onRefChanged(SPObject *old_ref, SPObject *ref); + + /** + Gets called when the referenced <hatch> is changed + */ + void _onRefModified(SPObject *ref, guint flags); + + /* patternUnits and patternContentUnits attribute */ + HatchUnits _hatchUnits : 1; + bool _hatchUnits_set : 1; + HatchUnits _hatchContentUnits : 1; + bool _hatchContentUnits_set : 1; + /* hatchTransform attribute */ + Geom::Affine _hatchTransform; + bool _hatchTransform_set : 1; + /* Strip */ + SVGLength _x; + SVGLength _y; + SVGLength _pitch; + SVGAngle _rotate; + + sigc::connection _modified_connection; + + std::list<View> _display; +}; + + +class SPHatchReference : public Inkscape::URIReference { +public: + SPHatchReference (SPObject *obj) : URIReference(obj) {} + SPHatch *getObject() const { + return reinterpret_cast<SPHatch *>(URIReference::getObject()); + } + +protected: + virtual bool _acceptObject(SPObject *obj) const { + return SP_IS_HATCH (obj); + } +}; + +#endif // SEEN_SP_HATCH_H + +/* + 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 : diff --git a/src/sp-item.cpp b/src/sp-item.cpp index c5b4b1667..967756055 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -120,6 +120,9 @@ SPItem::SPItem() : SPObject() { mask_ref = new SPMaskReference(this); mask_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(mask_ref_changed), this)); + style->signal_fill_ps_changed.connect(sigc::bind(sigc::ptr_fun(fill_ps_ref_changed), this)); + style->signal_stroke_ps_changed.connect(sigc::bind(sigc::ptr_fun(stroke_ps_ref_changed), this)); + avoidRef = new SPAvoidRef(this); } @@ -465,7 +468,15 @@ void SPItem::release() { SPObject::release(); + SPPaintServer *fill_ps = style->getFillPaintServer(); + SPPaintServer *stroke_ps = style->getStrokePaintServer(); while (item->display) { + if (fill_ps) { + fill_ps->hide(item->display->arenaitem->key()); + } + if (stroke_ps) { + stroke_ps->hide(item->display->arenaitem->key()); + } item->display = sp_item_view_list_remove(item->display, item->display); } @@ -623,6 +634,56 @@ void SPItem::mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item) } } +void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { + SPPaintServer *old_fill_ps = SP_PAINT_SERVER(old_ps); + if (old_fill_ps) { + for (SPItemView *v =item->display; v != NULL; v = v->next) { + old_fill_ps->hide(v->arenaitem->key()); + } + } + + SPPaintServer *new_fill_ps = SP_PAINT_SERVER(ps); + if (new_fill_ps) { + Geom::OptRect bbox = item->geometricBounds(); + for (SPItemView *v = item->display; v != NULL; v = v->next) { + if (!v->arenaitem->key()) { + v->arenaitem->setKey(SPItem::display_key_new(3)); + } + Inkscape::DrawingPattern *pi = new_fill_ps->show( + v->arenaitem->drawing(), v->arenaitem->key(), bbox); + v->arenaitem->setFillPattern(pi); + if (pi) { + new_fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + } +} + +void SPItem::stroke_ps_ref_changed(SPObject *old_ps, SPObject *ps, SPItem *item) { + SPPaintServer *old_stroke_ps = SP_PAINT_SERVER(old_ps); + if (old_stroke_ps) { + for (SPItemView *v =item->display; v != NULL; v = v->next) { + old_stroke_ps->hide(v->arenaitem->key()); + } + } + + SPPaintServer *new_stroke_ps = SP_PAINT_SERVER(ps); + if (new_stroke_ps) { + Geom::OptRect bbox = item->geometricBounds(); + for (SPItemView *v = item->display; v != NULL; v = v->next) { + if (!v->arenaitem->key()) { + v->arenaitem->setKey(SPItem::display_key_new(3)); + } + Inkscape::DrawingPattern *pi = new_stroke_ps->show( + v->arenaitem->drawing(), v->arenaitem->key(), bbox); + v->arenaitem->setStrokePattern(pi); + if (pi) { + new_stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + } +} + void SPItem::update(SPCtx* /*ctx*/, guint flags) { SPItem *item = this; SPItem* object = item; @@ -682,6 +743,10 @@ void SPItem::update(SPCtx* /*ctx*/, guint flags) { item->avoidRef->handleSettingChange(); } +void SPItem::modified(unsigned int /*flags*/) +{ +} + Inkscape::XML::Node* SPItem::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { SPItem *item = this; SPItem* object = item; @@ -1130,6 +1195,33 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned SP_MASK(mask)->sp_mask_set_bbox(mask_key, item_bbox); mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } + + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { + if (!display->arenaitem->key()) { + display->arenaitem->setKey(display_key_new(3)); + } + int fill_key = display->arenaitem->key(); + + Inkscape::DrawingPattern *ap = fill_ps->show(drawing, fill_key, item_bbox); + ai->setFillPattern(ap); + if (ap) { + fill_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { + if (!display->arenaitem->key()) { + display->arenaitem->setKey(display_key_new(3)); + } + int stroke_key = display->arenaitem->key(); + + Inkscape::DrawingPattern *ap = stroke_ps->show(drawing, stroke_key, item_bbox); + ai->setStrokePattern(ap); + if (ap) { + stroke_ps->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } ai->setData(this); ai->setItemBounds(geometricBounds()); } @@ -1159,6 +1251,14 @@ void SPItem::invoke_hide(unsigned key) mask_ref->getObject()->sp_mask_hide(v->arenaitem->key()); v->arenaitem->setMask(NULL); } + SPPaintServer *fill_ps = style->getFillPaintServer(); + if (fill_ps) { + fill_ps->hide(v->arenaitem->key()); + } + SPPaintServer *stroke_ps = style->getStrokePaintServer(); + if (stroke_ps) { + stroke_ps->hide(v->arenaitem->key()); + } if (!ref) { display = v->next; } else { diff --git a/src/sp-item.h b/src/sp-item.h index 1e4f5f4c6..1656a0c2b 100644 --- a/src/sp-item.h +++ b/src/sp-item.h @@ -34,6 +34,7 @@ class SPClipPathReference; class SPMaskReference; class SPAvoidRef; +class SPPattern; struct SPPrintContext; typedef unsigned int guint32; @@ -252,12 +253,15 @@ private: static SPItemView *sp_item_view_new_prepend(SPItemView *list, SPItem *item, unsigned flags, unsigned key, Inkscape::DrawingItem *arenaitem); static void clip_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); static void mask_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); + static void fill_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); + static void stroke_ps_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item); public: virtual void build(SPDocument *document, Inkscape::XML::Node *repr); virtual void release(); virtual void set(unsigned int key, char const* value); virtual void update(SPCtx *ctx, unsigned int flags); + virtual void modified(unsigned int flags); virtual Inkscape::XML::Node* write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags); virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const; diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index a2a935951..019d15162 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -702,9 +702,8 @@ SPLPEItem::apply_to_mask(SPItem *item) { SPMask *mask = item->mask_ref->getObject(); if(mask) { - SPObject * mask_data = mask->firstChild(); + SPObject *mask_data = mask->firstChild(); SPCurve * mask_curve = NULL; - mask_data = mask->firstChild(); if (SP_IS_PATH(mask_data)) { mask_curve = SP_PATH(mask_data)->get_original_curve(); } else if(SP_IS_SHAPE(mask_data)) { diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index 77ad7a35f..ae40ad1aa 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -60,7 +60,25 @@ bool SPPaintServer::isSolid() const bool SPPaintServer::isValid() const { - return true; + return true; +} + +Inkscape::DrawingPattern *SPPaintServer::show(Inkscape::Drawing &/*drawing*/, unsigned int /*key*/, Geom::OptRect /*bbox*/) +{ + return NULL; +} + +void SPPaintServer::hide(unsigned int /*key*/) +{ +} + +void SPPaintServer::setBBox(unsigned int /*key*/, Geom::OptRect const &/*bbox*/) +{ +} + +cairo_pattern_t* SPPaintServer::pattern_new(cairo_t * /*ct*/, Geom::OptRect const &/*bbox*/, double /*opacity*/) +{ + return NULL; } /* diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index beac72af5..7f3bfcba0 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -15,11 +15,17 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <cairo.h> #include <2geom/rect.h> +#include <sigc++/slot.h> #include "sp-object.h" -typedef struct _cairo cairo_t; -typedef struct _cairo_pattern cairo_pattern_t; +namespace Inkscape { + +class Drawing; +class DrawingPattern; + +} #define SP_PAINT_SERVER(obj) (dynamic_cast<SPPaintServer*>((SPObject*)obj)) #define SP_IS_PAINT_SERVER(obj) (dynamic_cast<const SPPaintServer*>((SPObject*)obj) != NULL) @@ -33,12 +39,59 @@ public: bool isSolid() const; virtual bool isValid() const; - virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity) = 0; + //There are two ways to render a paint. The simple one is to create cairo_pattern_t structure + //on demand by pattern_new method. It is used for gradients. The other one is to add elements + //representing PaintServer in NR tree. It is used by hatches and patterns. + //Either pattern new or all three methods show, hide, setBBox need to be implemented + virtual Inkscape::DrawingPattern *show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptRect bbox); // TODO check passing bbox by value. Looks suspicious. + virtual void hide(unsigned int key); + virtual void setBBox(unsigned int key, Geom::OptRect const &bbox); + + virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); protected: bool swatch; }; +/** + * Returns the first of {src, src-\>ref-\>getObject(), + * src-\>ref-\>getObject()-\>ref-\>getObject(),...} + * for which \a match is true, or NULL if none found. + * + * The raison d'être of this routine is that it correctly handles cycles in the href chain (e.g., if + * a gradient gives itself as its href, or if each of two gradients gives the other as its href). + * + * \pre SP_IS_GRADIENT(src). + */ +template <class PaintServer> +PaintServer *chase_hrefs(PaintServer *src, sigc::slot<bool, PaintServer const *> match) { + /* Use a pair of pointers for detecting loops: p1 advances half as fast as p2. If there is a + loop, then once p1 has entered the loop, we'll detect it the next time the distance between + p1 and p2 is a multiple of the loop size. */ + PaintServer *p1 = src, *p2 = src; + bool do1 = false; + for (;;) { + if (match(p2)) { + return p2; + } + + p2 = p2->ref->getObject(); + if (!p2) { + return p2; + } + if (do1) { + p1 = p1->ref->getObject(); + } + do1 = !do1; + + if ( p2 == p1 ) { + /* We've been here before, so return NULL to indicate that no matching gradient found + * in the chain. */ + return NULL; + } + } +} + #endif // SEEN_SP_PAINT_SERVER_H /* Local Variables: diff --git a/src/style-internal.cpp b/src/style-internal.cpp index a55a11403..b858e5cb6 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -964,7 +964,11 @@ SPIPaint::read( gchar const *str ) { if (!value.href && document) { // std::cout << " Creating value.href" << std::endl; value.href = new SPPaintServerReference(document); - value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((this == &style->fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), style)); + if (this == &style->fill) { + style->fill_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style)); + } else { + style->stroke_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style)); + } } // std::cout << "uri: " << (uri?uri:"null") << std::endl; @@ -981,7 +985,16 @@ SPIPaint::read( gchar const *str ) { if (streq(str, "currentColor")) { set = true; currentcolor = true; - setColor( style->color.value.color ); + if (style) { + setColor( style->color.value.color ); + } else { + // Normally an SPIPaint is part of an SPStyle and the value of 'color' is + // available. SPIPaint can be used 'stand-alone' (e.g. to parse color values) in + // which case a value of 'currentColor' is meaningless, thus we shouldn't reach + // here. + std::cerr << "SPIPaint::read(): value is 'currentColor' but 'color' not available." << std::endl; + setColor( 0 ); + } } else if (streq(str, "none")) { set = true; noneSet = true; @@ -1396,11 +1409,20 @@ SPIFilter::read( gchar const *str ) { set = true; // Create href if not already done. - if (!href && style->object) { - href = new SPFilterReference(style->object); - href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + if (!href) { + if (style->object) { + href = new SPFilterReference(style->object); + } + // Do we have href now? + if ( href ) { + href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + } else { + std::cerr << "SPIFilter::read(): Could not allocate 'href'" << std::endl; + return; + } } + // We have href try { href->attach(Inkscape::URI(uri)); } catch (Inkscape::BadURIException &e) { diff --git a/src/style-internal.h b/src/style-internal.h index 5a853fcef..32792827a 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -104,22 +104,45 @@ static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1); */ /// Virtual base class for all SPStyle interal classes -class SPIBase { +class SPIBase +{ - public: +public: SPIBase( Glib::ustring const &name, bool inherits = true ) - : name(name), inherits(inherits), set(false), inherit(false), style_att(false), style(NULL) {}; - virtual ~SPIBase() {}; + : name(name), + inherits(inherits), + set(false), + inherit(false), + style_att(false), + style(NULL) + {} + + virtual ~SPIBase() + {} + virtual void read( gchar const *str ) = 0; - virtual void readIfUnset( gchar const *str ) { if( !set ) read( str ); } - virtual void readAttribute( Inkscape::XML::Node *repr ) { readIfUnset( repr->attribute( name.c_str() ) ); } + virtual void readIfUnset( gchar const *str ) { + if ( !set ) { + read( str ); + } + } + + virtual void readAttribute( Inkscape::XML::Node *repr ) { + readIfUnset( repr->attribute( name.c_str() ) ); + } + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const = 0; - virtual void clear() { set = false, inherit = false; }; + virtual void clear() { + set = false, inherit = false; + } + virtual void cascade( const SPIBase* const parent ) = 0; virtual void merge( const SPIBase* const parent ) = 0; - virtual void setStylePointer( SPStyle *style_in ) { style = style_in; }; + virtual void setStylePointer( SPStyle *style_in ) { + style = style_in; + } // Explicit assignment operator required due to templates. SPIBase& operator=(const SPIBase& rhs) { @@ -133,11 +156,16 @@ class SPIBase { } // Check apples being compared to apples - virtual bool operator==(const SPIBase& rhs) { return (name == rhs.name); }; - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator==(const SPIBase& rhs) { + return (name == rhs.name); + } + + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: Glib::ustring name; // Make const unsigned inherits : 1; // Property inherits by default from parent. unsigned set : 1; // Property has been explicitly set (vs. inherited). @@ -145,22 +173,35 @@ class SPIBase { unsigned style_att : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME // To do: make private after g_asserts removed - public: +public: SPStyle* style; // Used by SPIPaint, SPIFilter... to find values of other properties }; /// Float type internal to SPStyle. (Only 'stroke-miterlimit') -class SPIFloat : public SPIBase { - - public: - SPIFloat() : SPIBase( "anonymous_float" ), value(0.0) {}; - SPIFloat( Glib::ustring name, float value_default = 0.0 ) - : SPIBase( name ), value(value_default), value_default(value_default) {}; - virtual ~SPIFloat() {}; +class SPIFloat : public SPIBase +{ + +public: + SPIFloat() + : SPIBase( "anonymous_float" ), + value(0.0) + {} + + SPIFloat( Glib::ustring const &name, float value_default = 0.0 ) + : SPIBase( name ), + value(value_default), + value_default(value_default) + {} + + virtual ~SPIFloat() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -172,13 +213,15 @@ class SPIFloat : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: float value; - private: +private: float value_default; }; @@ -214,17 +257,32 @@ static const unsigned SP_SCALE24_MAX = 0xff0000; /// 24 bit data type internal to SPStyle. // Used only for opacity, fill-opacity, stroke-opacity. // Opacity does not inherit but stroke-opacity and fill-opacity do. -class SPIScale24 : public SPIBase { +class SPIScale24 : public SPIBase +{ + +public: + SPIScale24() + : SPIBase( "anonymous_scale24" ), + value(0) + {} + + SPIScale24( Glib::ustring const &name, unsigned value = 0, bool inherits = true ) + : SPIBase( name, inherits ), + value(value), + value_default(value) + {} + + virtual ~SPIScale24() + {} - public: - SPIScale24() : SPIBase( "anonymous_scale24" ), value(0) {}; - SPIScale24( Glib::ustring name, unsigned value = 0, bool inherits = true ) - : SPIBase( name, inherits ), value(value), value_default(value) {}; - virtual ~SPIScale24() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -236,14 +294,16 @@ class SPIScale24 : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: unsigned value : 24; - private: +private: unsigned value_default : 24; }; @@ -265,17 +325,37 @@ enum SPCSSUnit { /// Length type internal to SPStyle. // Needs access to 'font-size' and 'font-family' for computed values. // Used for 'stroke-width' 'stroke-dash-offset' ('none' not handled), text-indent -class SPILength : public SPIBase { +class SPILength : public SPIBase +{ + +public: + SPILength() + : SPIBase( "anonymous_length" ), + unit(SP_CSS_UNIT_NONE), + value(0), + computed(0) + {} + + SPILength( Glib::ustring const &name, unsigned value = 0 ) + : SPIBase( name ), + unit(SP_CSS_UNIT_NONE), + value(value), + computed(value), + value_default(value) + {} + + virtual ~SPILength() + {} - public: - SPILength() : SPIBase( "anonymous_length" ), unit(SP_CSS_UNIT_NONE), value(0), computed(0) {}; - SPILength( Glib::ustring name, unsigned value = 0 ) - : SPIBase( name ), unit(SP_CSS_UNIT_NONE), value(value), computed(value), value_default(value) {}; - virtual ~SPILength() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); unit = SP_CSS_UNIT_NONE, value = value_default; computed = value_default; }; + virtual void clear() { + SPIBase::clear(); + unit = SP_CSS_UNIT_NONE, value = value_default; + computed = value_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -286,35 +366,51 @@ class SPILength : public SPIBase { computed = rhs.computed; value_default = rhs.value_default; return *this; - }; + } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: unsigned unit : 4; float value; float computed; - private: +private: float value_default; }; /// Extended length type internal to SPStyle. // Used for: line-height, letter-spacing, word-spacing -class SPILengthOrNormal : public SPILength { +class SPILengthOrNormal : public SPILength +{ + +public: + SPILengthOrNormal() + : SPILength( "anonymous_length" ), + normal(true) + {} + + SPILengthOrNormal( Glib::ustring const &name, unsigned value = 0 ) + : SPILength( name, value ), + normal(true) + {} + + virtual ~SPILengthOrNormal() + {} - public: - SPILengthOrNormal() : SPILength( "anonymous_length" ), normal(true) {}; - SPILengthOrNormal( Glib::ustring name, unsigned value = 0 ) - : SPILength( name, value ), normal(true) {}; - virtual ~SPILengthOrNormal() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPILength::clear(); normal = true; }; + virtual void clear() { + SPILength::clear(); + normal = true; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -325,33 +421,59 @@ class SPILengthOrNormal : public SPILength { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool normal : 1; }; /// Enum type internal to SPStyle. // Used for many properties. 'font-stretch' and 'font-weight' must be special cased. -class SPIEnum : public SPIBase { +class SPIEnum : public SPIBase +{ - public: +public: SPIEnum() : - SPIBase( "anonymous_enum" ), enums( NULL ), value(0), computed(0) {}; - SPIEnum( Glib::ustring const name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : - SPIBase( name, inherits ), enums( enums ), value(value), computed(value), - value_default(value), computed_default(value) {}; + SPIBase( "anonymous_enum" ), + enums( NULL ), + value(0), + computed(0) + {} + + SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : + SPIBase( name, inherits ), + enums( enums ), + value(value), + computed(value), + value_default(value), + computed_default(value) + {} + // Following is needed for font-weight - SPIEnum( Glib::ustring name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) : - SPIBase( name ), enums( enums ), value(value), computed(computed), - value_default(value), computed_default(computed) {}; - virtual ~SPIEnum() {}; + SPIEnum( Glib::ustring const &name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) : + SPIBase( name ), + enums( enums ), + value(value), + computed(computed), + value_default(value), + computed_default(computed) + {} + + virtual ~SPIEnum() + {} + virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value = value_default, computed = computed_default; }; + virtual void clear() { + SPIBase::clear(); + value = value_default, computed = computed_default; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -365,16 +487,18 @@ class SPIEnum : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPStyleEnum const *enums; unsigned value : 8; unsigned computed: 8; - private: +private: unsigned value_default : 8; unsigned computed_default: 8; // for font-weight }; @@ -382,20 +506,31 @@ class SPIEnum : public SPIBase { /// String type internal to SPStyle. // Used for 'marker', ..., 'font', 'font-family', 'inkscape-font-specification' -class SPIString : public SPIBase { - - public: - SPIString() : - SPIBase( "anonymous_string" ), value(NULL) {}; - SPIString( Glib::ustring name, gchar const* value_default_in = NULL ) : - SPIBase( name ) , value(NULL) , value_default(NULL) { - value_default = value_default_in?g_strdup(value_default_in):NULL; - }; - virtual ~SPIString() { g_free(value); g_free(value_default); }; +class SPIString : public SPIBase +{ + +public: + SPIString() + : SPIBase( "anonymous_string" ), + value(NULL) + {} + + // TODO probably want to avoid gchar* and c-style strings. + SPIString( Glib::ustring const &name, gchar const* value_default_in = NULL ) + : SPIBase( name ), + value(NULL), + value_default(value_default_in ? g_strdup(value_default_in) : NULL) + {} + + virtual ~SPIString() { + g_free(value); + g_free(value_default); + } + virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear(); + virtual void clear(); // TODO check about value and value_default virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -403,31 +538,50 @@ class SPIString : public SPIBase { SPIBase::operator=(rhs); g_free(value); g_free(value_default); - value = rhs.value?g_strdup(rhs.value):NULL; - value_default = rhs.value_default?g_strdup(rhs.value_default):NULL; + value = rhs.value ? g_strdup(rhs.value) : NULL; + value_default = rhs.value_default ? g_strdup(rhs.value_default) : NULL; return *this; } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private, convert value to Glib::ustring - public: +public: gchar *value; gchar *value_default; }; /// Color type interal to SPStyle, FIXME Add string value to store SVG named color. -class SPIColor : public SPIBase { +class SPIColor : public SPIBase +{ + +public: + SPIColor() + : SPIBase( "anonymous_color" ), + currentcolor(false) { + value.color.set(0); + } + + SPIColor( Glib::ustring const &name ) + : SPIBase( name ), + currentcolor(false) { + value.color.set(0); + } + + virtual ~SPIColor() + {} - public: - SPIColor() : SPIBase( "anonymous_color" ), currentcolor(false) { value.color.set(0); } - SPIColor( Glib::ustring name ) : SPIBase( name ), currentcolor(false) { value.color.set(0); } - virtual ~SPIColor() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); value.color.set(0); } + virtual void clear() { + SPIBase::clear(); + value.color.set(0); + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -439,13 +593,23 @@ class SPIColor : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); } + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - void setColor( float r, float g, float b ) { value.color.set( r, g, b ); } - void setColor( guint32 val ) { value.color.set( val ); } - void setColor( SPColor const& color ) { value.color = color; } + void setColor( float r, float g, float b ) { + value.color.set( r, g, b ); + } + + void setColor( guint32 val ) { + value.color.set( val ); + } + + void setColor( SPColor const& color ) { + value.color = color; + } - public: +public: bool currentcolor : 1; // FIXME: remove structure and derive SPIPaint from this class. struct { @@ -459,18 +623,28 @@ class SPIColor : public SPIBase { #define SP_STYLE_STROKE_SERVER(s) ((const_cast<SPStyle *> (s))->getStrokePaintServer()) /// Paint type internal to SPStyle. -class SPIPaint : public SPIBase { - - public: - SPIPaint() : SPIBase( "anonymous_paint" ), currentcolor(false), colorSet(false), noneSet(false) { +class SPIPaint : public SPIBase +{ + +public: + SPIPaint() + : SPIBase( "anonymous_paint" ), + currentcolor(false), + colorSet(false), + noneSet(false) { value.href = NULL; clear(); - }; - SPIPaint( Glib::ustring name ) - : SPIBase( name ), currentcolor(false), colorSet(false), noneSet(false) { + } + + SPIPaint( Glib::ustring const &name ) + : SPIBase( name ), + currentcolor(false), + colorSet(false), + noneSet(false) { value.href = NULL; clear(); // Sets defaults - }; + } + virtual ~SPIPaint(); // Clear and delete href. virtual void read( gchar const *str ); virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0); @@ -492,24 +666,46 @@ class SPIPaint : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } + + bool isSameType( SPIPaint const & other ) const { + return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor); + } - bool isSameType( SPIPaint const & other ) const {return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor);} + bool isNoneSet() const { + return noneSet; + } + + bool isNone() const { + return !currentcolor && !colorSet && !isPaintserver(); + } // TODO refine + + bool isColor() const { + return colorSet && !isPaintserver(); + } - bool isNoneSet() const {return noneSet;} + bool isPaintserver() const { + return (value.href) ? value.href->getObject() : 0; + } - bool isNone() const {return !currentcolor && !colorSet && !isPaintserver();} // TODO refine - bool isColor() const {return colorSet && !isPaintserver();} - bool isPaintserver() const {return (value.href) ? value.href->getObject():0;} + void setColor( float r, float g, float b ) { + value.color.set( r, g, b ); colorSet = true; + } - void setColor( float r, float g, float b ) {value.color.set( r, g, b ); colorSet = true;} - void setColor( guint32 val ) {value.color.set( val ); colorSet = true;} - void setColor( SPColor const& color ) {value.color = color; colorSet = true;} + void setColor( guint32 val ) { + value.color.set( val ); colorSet = true; + } + void setColor( SPColor const& color ) { + value.color = color; colorSet = true; + } + void setNone() {noneSet = true; colorSet=false;} // To do: make private - public: +public: bool currentcolor : 1; bool colorSet : 1; bool noneSet : 1; @@ -536,11 +732,20 @@ enum SPPaintOrderLayer { const size_t PAINT_ORDER_LAYERS = 3; /// Paint order type internal to SPStyle -class SPIPaintOrder : public SPIBase { +class SPIPaintOrder : public SPIBase +{ + +public: + SPIPaintOrder() + : SPIBase( "paint-order" ), + value(NULL) { + this->clear(); + } + + virtual ~SPIPaintOrder() { + g_free( value ); + } - public: - SPIPaintOrder() : SPIBase( "paint-order" ), value(NULL) { this->clear(); }; - virtual ~SPIPaintOrder() { g_free( value ); }; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; @@ -568,11 +773,13 @@ class SPIPaintOrder : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPPaintOrderLayer layer[PAINT_ORDER_LAYERS]; bool layer_set[PAINT_ORDER_LAYERS]; gchar *value; // Raw string @@ -580,15 +787,25 @@ class SPIPaintOrder : public SPIBase { /// Filter type internal to SPStyle -class SPIDashArray : public SPIBase { +class SPIDashArray : public SPIBase +{ + +public: + SPIDashArray() + : SPIBase( "stroke-dasharray" ) + {} // Only one instance of SPIDashArray + + virtual ~SPIDashArray() + {} - public: - SPIDashArray() : SPIBase( "stroke-dasharray" ) {}; // Only one instance of SPIDashArray - virtual ~SPIDashArray() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); values.clear(); }; + virtual void clear() { + SPIBase::clear(); + values.clear(); + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -599,19 +816,26 @@ class SPIDashArray : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private, change double to SVGLength - public: +public: std::vector<double> values; }; /// Filter type internal to SPStyle -class SPIFilter : public SPIBase { +class SPIFilter : public SPIBase +{ + +public: + SPIFilter() + : SPIBase( "filter", false ), + href(NULL) + {} - public: - SPIFilter() : SPIBase( "filter", false ), href(NULL) {}; virtual ~SPIFilter(); virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, @@ -627,10 +851,12 @@ class SPIFilter : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: SPFilterReference *href; }; @@ -643,16 +869,27 @@ enum { }; /// Fontsize type internal to SPStyle (also used by libnrtype/Layout-TNG-Input.cpp). -class SPIFontSize : public SPIBase { +class SPIFontSize : public SPIBase +{ + +public: + SPIFontSize() + : SPIBase( "font-size" ) { + this->clear(); + } + + virtual ~SPIFontSize() + {} - public: - SPIFontSize() : SPIBase( "font-size" ) { this->clear(); }; - virtual ~SPIFontSize() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE, - literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0; } + virtual void clear() { + SPIBase::clear(); + type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE, + literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -667,39 +904,51 @@ class SPIFontSize : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - public: +public: static float const font_size_default; // To do: make private - public: +public: unsigned type : 2; unsigned unit : 4; unsigned literal : 4; float value; float computed; - private: +private: double relative_fraction() const; static float const font_size_table[]; }; /// Font type internal to SPStyle ('font' shorthand) -class SPIFont : public SPIBase { +class SPIFont : public SPIBase +{ + +public: + SPIFont() + : SPIBase( "font" ) + {} + + virtual ~SPIFont() + {} - public: - SPIFont() : SPIBase( "font" ) {}; - virtual ~SPIFont() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); - }; - virtual void cascade( const SPIBase* const parent ) { (void)parent; }; // Done in dependent properties - virtual void merge( const SPIBase* const parent ) { (void)parent; }; + } + + virtual void cascade( const SPIBase* const /*parent*/ ) + {} // Done in dependent properties + + virtual void merge( const SPIBase* const /*parent*/ ) + {} SPIFont& operator=(const SPIFont& rhs) { SPIBase::operator=(rhs); @@ -707,7 +956,9 @@ class SPIFont : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } }; @@ -718,16 +969,27 @@ enum { }; /// Baseline shift type internal to SPStyle. (This is actually just like SPIFontSize) -class SPIBaselineShift : public SPIBase { +class SPIBaselineShift : public SPIBase +{ + +public: + SPIBaselineShift() + : SPIBase( "baseline-shift", false ) { + this->clear(); + } + + virtual ~SPIBaselineShift() + {} - public: - SPIBaselineShift() : SPIBase( "baseline-shift", false ) { this->clear(); }; - virtual ~SPIBaselineShift() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE, - literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0; } + virtual void clear() { + SPIBase::clear(); + type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE, + literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -743,11 +1005,14 @@ class SPIBaselineShift : public SPIBase { // This is not used but we have it for completeness, it has not been tested. virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } + bool isZero() const; // To do: make private - public: +public: unsigned type : 2; unsigned unit : 4; unsigned literal: 2; @@ -760,15 +1025,26 @@ class SPIBaselineShift : public SPIBase { // CSS3 2.2 /// Text decoration line type internal to SPStyle. THIS SHOULD BE A GENERIC CLASS -class SPITextDecorationLine : public SPIBase { +class SPITextDecorationLine : public SPIBase +{ + +public: + SPITextDecorationLine() + : SPIBase( "text-decoration-line" ) { + this->clear(); + } + + virtual ~SPITextDecorationLine() + {} - public: - SPITextDecorationLine() : SPIBase( "text-decoration-line" ) { this->clear(); }; - virtual ~SPITextDecorationLine() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); underline = false, overline = false, line_through = false, blink = false; } + virtual void clear() { + SPIBase::clear(); + underline = false, overline = false, line_through = false, blink = false; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -782,10 +1058,12 @@ class SPITextDecorationLine : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool underline : 1; bool overline : 1; bool line_through : 1; @@ -794,15 +1072,26 @@ class SPITextDecorationLine : public SPIBase { // CSS3 2.2 /// Text decoration style type internal to SPStyle. THIS SHOULD JUST BE SPIEnum! -class SPITextDecorationStyle : public SPIBase { +class SPITextDecorationStyle : public SPIBase +{ + +public: + SPITextDecorationStyle() + : SPIBase( "text-decoration-style" ) { + this->clear(); + } + + virtual ~SPITextDecorationStyle() + {} - public: - SPITextDecorationStyle() : SPIBase( "text-decoration-style" ) { this->clear(); }; - virtual ~SPITextDecorationStyle() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; - virtual void clear() { SPIBase::clear(); solid = true, isdouble = false, dotted = false, dashed = false, wavy = false; } + virtual void clear() { + SPIBase::clear(); + solid = true, isdouble = false, dotted = false, dashed = false, wavy = false; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -817,10 +1106,12 @@ class SPITextDecorationStyle : public SPIBase { } virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } // To do: make private - public: +public: bool solid : 1; bool isdouble : 1; // cannot use "double" as it is a reserved keyword bool dotted : 1; @@ -837,18 +1128,26 @@ class SPITextDecorationStyle : public SPIBase { // the right style. (See http://www.w3.org/TR/css-text-decor-3/#text-decoration-property ) /// Text decoration type internal to SPStyle. -class SPITextDecoration : public SPIBase { +class SPITextDecoration : public SPIBase +{ + +public: + SPITextDecoration() + : SPIBase( "text-decoration" ), + style_td( NULL ) + {} + + virtual ~SPITextDecoration() + {} - public: - SPITextDecoration() : SPIBase( "text-decoration" ), style_td( NULL ) {}; - virtual ~SPITextDecoration() {}; virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); style_td = NULL; - }; + } + virtual void cascade( const SPIBase* const parent ); virtual void merge( const SPIBase* const parent ); @@ -859,9 +1158,11 @@ class SPITextDecoration : public SPIBase { // Use CSS2 value virtual bool operator==(const SPIBase& rhs); - virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + virtual bool operator!=(const SPIBase& rhs) { + return !(*this == rhs); + } - public: +public: SPStyle* style_td; // Style to be used for drawing CSS2 text decorations }; diff --git a/src/style.cpp b/src/style.cpp index 66672e949..e0c8536fc 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -440,6 +440,8 @@ SPStyle::~SPStyle() { // Remove connections release_connection.disconnect(); + fill_ps_changed_connection.disconnect(); + stroke_ps_changed_connection.disconnect(); // The following shoud be moved into SPIPaint and SPIFilter if (fill.value.href) { @@ -499,10 +501,10 @@ SPStyle::clear() { filter.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), this)); fill.value.href = new SPPaintServerReference(document); - fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), this)); + fill_ps_changed_connection = fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), this)); stroke.value.href = new SPPaintServerReference(document); - stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), this)); + stroke_ps_changed_connection = stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), this)); } cloned = false; @@ -1118,6 +1120,7 @@ sp_style_fill_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } + style->signal_fill_ps_changed.emit(old_ref, ref); sp_style_paint_server_ref_modified(ref, 0, style); } @@ -1135,6 +1138,7 @@ sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPSty ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } + style->signal_stroke_ps_changed.emit(old_ref, ref); sp_style_paint_server_ref_modified(ref, 0, style); } @@ -1363,7 +1367,11 @@ sp_style_set_ipaint_to_uri(SPStyle *style, SPIPaint *paint, const Inkscape::URI // now that we have a document, we can create it here if (!paint->value.href && document) { paint->value.href = new SPPaintServerReference(document); - paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((paint == &style->fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), style)); + if (paint == &style->fill) { + style->fill_ps_changed_connection = paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style)); + } else { + style->stroke_ps_changed_connection = paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style)); + } } if (paint->value.href){ diff --git a/src/style.h b/src/style.h index 48eb55430..a4c2b3043 100644 --- a/src/style.h +++ b/src/style.h @@ -252,6 +252,22 @@ public: sigc::connection filter_modified_connection; sigc::connection fill_ps_modified_connection; sigc::connection stroke_ps_modified_connection; + sigc::connection fill_ps_changed_connection; + sigc::connection stroke_ps_changed_connection; + + /** + * Emitted when paint server object, fill paint refers to, is changed. That is + * when the reference starts pointing to a different address in memory. + * + * NB It is different from fill_ps_modified signal. When paint server is modified + * it means some of it's attributes or chilren change. + */ + sigc::signal<void, SPObject *, SPObject *> signal_fill_ps_changed; + /** + * Emitted when paint server object, fill paint refers to, is changed. That is + * when the reference starts pointing to a different address in memory. + */ + sigc::signal<void, SPObject *, SPObject *> signal_stroke_ps_changed; SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : NULL; } SPObject const *getFilter() const { return (filter.href) ? filter.href->getObject() : NULL; } diff --git a/src/svg/CMakeLists.txt b/src/svg/CMakeLists.txt index 968287895..f9d0bc52d 100644 --- a/src/svg/CMakeLists.txt +++ b/src/svg/CMakeLists.txt @@ -7,6 +7,7 @@ set(svg_SRC strip-trailing-zeros.cpp svg-affine.cpp svg-color.cpp + svg-angle.cpp svg-length.cpp svg-path.cpp # test-stubs.cpp @@ -24,6 +25,7 @@ set(svg_SRC svg-color-test.h svg-color.h svg-icc-color.h + svg-angle.h svg-length-test.h svg-length.h svg-path-geom-test.h diff --git a/src/svg/Makefile_insert b/src/svg/Makefile_insert index cf9bf3fbb..4f82bdd76 100644 --- a/src/svg/Makefile_insert +++ b/src/svg/Makefile_insert @@ -13,6 +13,8 @@ ink_common_sources += \ svg/svg-color.cpp \ svg/svg-color.h \ svg/svg-icc-color.h \ + svg/svg-angle.cpp \ + svg/svg-angle.h \ svg/svg-length.cpp \ svg/svg-length.h \ svg/svg-path.cpp \ diff --git a/src/svg/svg-angle.cpp b/src/svg/svg-angle.cpp new file mode 100644 index 000000000..63152368e --- /dev/null +++ b/src/svg/svg-angle.cpp @@ -0,0 +1,136 @@ +/** + * \file src/svg/svg-angle.cpp + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <cstring> +#include <string> +#include <math.h> +#include <glib.h> + +#include "svg.h" +#include "stringstream.h" +#include "svg/svg-angle.h" +#include "util/units.h" + + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed); + +SVGAngle::SVGAngle() + : _set(false) + , unit(NONE) + , value(0) + , computed(0) +{ +} + +/* Angle */ + +bool SVGAngle::read(gchar const *str) +{ + if (!str) { + return false; + } + + SVGAngle::Unit u; + float v; + float c; + if (!sp_svg_angle_read_lff(str, u, v, c)) { + return false; + } + + _set = true; + unit = u; + value = v; + computed = c; + + return true; +} + +void SVGAngle::unset(SVGAngle::Unit u, float v, float c) { + _set = false; + unit = u; + value = v; + computed = c; +} + +void SVGAngle::readOrUnset(gchar const *str, Unit u, float v, float c) { + if (!read(str)) { + unset(u, v, c); + } +} + +static bool sp_svg_angle_read_lff(gchar const *str, SVGAngle::Unit &unit, float &val, float &computed) +{ + if (!str) { + return false; + } + + gchar const *e; + float const v = g_ascii_strtod(str, (char **) &e); + if (e == str) { + return false; + } + + if (!e[0]) { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } else if (!g_ascii_isalnum(e[0])) { + if (g_ascii_isspace(e[0]) && e[1] && g_ascii_isalpha(e[1])) { + return false; // spaces between value and unit are not allowed + } else { + /* Unitless (defaults to degrees)*/ + unit = SVGAngle::NONE; + val = v; + computed = v; + return true; + } + } else { + if (strncmp(e, "deg", 3) == 0) { + unit = SVGAngle::DEG; + computed = v; + } else if (strncmp(e, "grad", 4) == 0) { + unit = SVGAngle::GRAD; + computed = Inkscape::Util::Quantity::convert(v, "grad", "°"); + } else if (strncmp(e, "rad", 3) == 0) { + unit = SVGAngle::RAD; + computed = Inkscape::Util::Quantity::convert(v, "rad", "°"); + } else if (strncmp(e, "turn", 4) == 0) { + unit = SVGAngle::TURN; + computed = Inkscape::Util::Quantity::convert(v, "turn", "°"); + } else { + return false; + } + return true; + } + + /* Invalid */ + return false; +} + +/* + 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 : diff --git a/src/svg/svg-angle.h b/src/svg/svg-angle.h new file mode 100644 index 000000000..966491174 --- /dev/null +++ b/src/svg/svg-angle.h @@ -0,0 +1,69 @@ +#ifndef SEEN_SP_SVG_ANGLE_H +#define SEEN_SP_SVG_ANGLE_H + +/** + * \file src/svg/svg-angle.h + * \brief SVG angle type + */ +/* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> + +class SVGAngle +{ +public: + SVGAngle(); + + enum Unit { + NONE, + DEG, + GRAD, + RAD, + TURN, + LAST_UNIT = TURN + }; + + // The object's value is valid / exists in SVG. + bool _set; + + // The unit of value. + Unit unit; + + // The value of this SVGAngle as found in the SVG. + float value; + + // The value in degrees. + float computed; + + float operator=(float v) { + _set = true; + unit = NONE; + value = computed = v; + return v; + } + + bool read(gchar const *str); + void unset(Unit u = NONE, float v = 0, float c = 0); + void readOrUnset(gchar const *str, Unit u = NONE, float v = 0, float c = 0); +}; + +#endif // SEEN_SP_SVG_ANGLE_H + +/* + 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 : diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 5108b7702..c9f22f8a4 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -511,18 +511,18 @@ sp_svg_create_color_hash() void icc_color_to_sRGB(SVGICCColor* icc, guchar* r, guchar* g, guchar* b) { - guchar color_out[4]; - guchar color_in[4]; if (icc) { g_message("profile name: %s", icc->colorProfile.c_str()); Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(icc->colorProfile.c_str()); if ( prof ) { + guchar color_out[4] = {0,0,0,0}; cmsHTRANSFORM trans = prof->getTransfToSRGB8(); if ( trans ) { std::vector<colorspace::Component> comps = colorspace::getColorSpaceInfo( prof ); size_t count = CMSSystem::getChannelCount( prof ); size_t cap = std::min(count, comps.size()); + guchar color_in[4]; for (size_t i = 0; i < cap; i++) { color_in[i] = static_cast<guchar>((((gdouble)icc->colors[i]) * 256.0) * (gdouble)comps[i].scale); g_message("input[%d]: %d", (int)i, (int)color_in[i]); diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index 1d6d0403e..5ef885ab2 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -2235,7 +2235,11 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) gdk_window_process_all_updates(); SPObject *obj = selection->singleItem(); - SPItem *item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0; + if (!obj) { + // Should never happen (empty selection checked above). + std::cerr << "CloneTiler::clonetile_apply(): No object in single item selection!!!" << std::endl; + return; + } Inkscape::XML::Node *obj_repr = obj->getRepr(); const char *id_href = g_strdup_printf("#%s", obj_repr->attribute("id")); SPObject *parent = obj->parent; @@ -2326,6 +2330,7 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) bool invert_picked = prefs->getBool(prefs_path + "invert_picked"); double gamma_picked = prefs->getDoubleLimited(prefs_path + "gamma_picked", 0, -10, 10); + SPItem *item = SP_IS_ITEM(obj) ? SP_ITEM(obj) : 0; if (dotrace) { clonetiler_trace_setup (sp_desktop_document(desktop), 1.0, item); } diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp index c58df864c..a9cf8d068 100644 --- a/src/ui/dialog/symbols.cpp +++ b/src/ui/dialog/symbols.cpp @@ -62,8 +62,20 @@ #include "widgets/icon.h" #ifdef WITH_LIBVISIO -#include <libvisio/libvisio.h> -#include <libwpd-stream/libwpd-stream.h> + #include <libvisio/libvisio.h> + + // TODO: Drop this check when librevenge is widespread. + #if WITH_LIBVISIO01 + #include <librevenge-stream/librevenge-stream.h> + + using librevenge::RVNGFileStream; + using librevenge::RVNGStringVector; + #else + #include <libwpd-stream/libwpd-stream.h> + + typedef WPXFileStream RVNGFileStream; + typedef libvisio::VSDStringVector RVNGStringVector; + #endif #endif #include "verbs.h" @@ -495,14 +507,20 @@ void SymbolsDialog::iconChanged() { // Read Visio stencil files SPDocument* read_vss( gchar* fullname, gchar* filename ) { - WPXFileStream input(fullname); + RVNGFileStream input(fullname); if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } - libvisio::VSDStringVector output; + RVNGStringVector output; +#if WITH_LIBVISIO01 + librevenge::RVNGSVGDrawingGenerator generator(output, "svg"); + + if (!libvisio::VisioDocument::parseStencils(&input, &generator)) { +#else if (!libvisio::VisioDocument::generateSVGStencils(&input, output)) { +#endif return NULL; } diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index d3adb6307..d22c59aa4 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -1220,7 +1220,9 @@ void SelectedStyle::on_opacity_menu (Gtk::Menu *menu) { menu->show_all(); } -void SelectedStyle::on_opacity_changed () { +void SelectedStyle::on_opacity_changed () +{ + g_return_if_fail(_desktop); // TODO this shouldn't happen! if (_opacity_blocked) return; _opacity_blocked = true; diff --git a/src/viewbox.cpp b/src/viewbox.cpp index f59909abc..662b05686 100644 --- a/src/viewbox.cpp +++ b/src/viewbox.cpp @@ -164,16 +164,17 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) { void SPViewBox::apply_viewbox(const Geom::Rect& in) { /* Determine actual viewbox in viewport coordinates */ - double x = 0.0; - double y = 0.0; - double width = in.width(); - double height = in.height(); + /* These are floats since SVGLength is a float: See bug 1374614 */ + float x = 0.0; + float y = 0.0; + float width = in.width(); + float height = in.height(); // std::cout << " width: " << width << " height: " << height << std::endl; if (this->aspect_align != SP_ASPECT_NONE) { /* Things are getting interesting */ - double scalex = in.width() / this->viewBox.width(); - double scaley = in.height() / this->viewBox.height(); + double scalex = in.width() / ((float) this->viewBox.width()); + double scaley = in.height() / ((float) this->viewBox.height()); double scale = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley); width = this->viewBox.width() * scale; height = this->viewBox.height() * scale; @@ -217,12 +218,12 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in) { /* Viewbox transform from scale and position */ Geom::Affine q; - q[0] = width / this->viewBox.width(); + q[0] = width / ((float) this->viewBox.width()); q[1] = 0.0; q[2] = 0.0; - q[3] = height / this->viewBox.height(); - q[4] = x - q[0] * this->viewBox.left(); - q[5] = y - q[3] * this->viewBox.top(); + q[3] = height / ((float) this->viewBox.height()); + q[4] = x - q[0] * ((float) this->viewBox.left()); + q[5] = y - q[3] * ((float) this->viewBox.top()); // std::cout << " q\n" << q << std::endl; diff --git a/src/widgets/ink-comboboxentry-action.cpp b/src/widgets/ink-comboboxentry-action.cpp index 5c59f6961..1114d2cdb 100644 --- a/src/widgets/ink-comboboxentry-action.cpp +++ b/src/widgets/ink-comboboxentry-action.cpp @@ -389,7 +389,7 @@ GtkWidget* create_tool_item( GtkAction* action ) GtkTreeViewRowSeparatorFunc (ink_comboboxentry_action->separator_func), NULL, NULL ); } - + // FIXME: once gtk3 migration is done this can be removed // https://bugzilla.gnome.org/show_bug.cgi?id=734915 gtk_widget_show_all (comboBoxEntry); diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index 3fa39da6f..cb18207f4 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -763,6 +763,9 @@ StrokeStyle::setJoinType (unsigned const jointype) tb = joinBevel; break; default: + // Should not happen + std::cerr << "StrokeStyle::setJoinType(): Invalid value" << std::endl; + tb = joinMiter; break; } setJoinButtons(tb); @@ -786,6 +789,9 @@ StrokeStyle::setCapType (unsigned const captype) tb = capSquare; break; default: + // Should not happen + std::cerr << "StrokeStyle::setCapType(): Invalid value" << std::endl; + tb = capButt; break; } setCapButtons(tb); diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp index 0319bb5e3..a4146f215 100644 --- a/src/xml/repr-io.cpp +++ b/src/xml/repr-io.cpp @@ -623,7 +623,6 @@ static Node *sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gc repr->setContent(reinterpret_cast<gchar*>(node->content)); } - child = node->xmlChildrenNode; for (child = node->xmlChildrenNode; child != NULL; child = child->next) { Node *crepr = sp_repr_svg_read_node (xml_doc, child, default_ns, prefix_map); if (crepr) { |
