Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / splivarot.cpp
index 5868140a6d25b101fe677b514e1c2cd51b84da8b..5f32e591ef906ab4b90c1dfd43a5060f6ef3e594 100644 (file)
@@ -1,4 +1,3 @@
-#define __SP_LIVAROT_C__
 /*
  *  splivarot.cpp
  *  Inkscape
@@ -33,7 +32,6 @@
 #include "text-editing.h"
 #include "sp-item-group.h"
 #include "style.h"
-#include "inkscape.h"
 #include "document.h"
 #include "message-stack.h"
 #include "selection.h"
 #include "display/canvas-bpath.h"
 #include "display/curve.h"
 #include <glibmm/i18n.h>
-#include "prefs-utils.h"
+#include "preferences.h"
 
-#include "libnr/n-art-bpath.h"
-#include "libnr/nr-path.h"
 #include "xml/repr.h"
 #include "xml/repr-sorting.h"
-
-#include <libnr/nr-matrix-fns.h>
-#include <libnr/nr-matrix-ops.h>
-#include <libnr/nr-matrix-translate-ops.h>
+#include <2geom/pathvector.h>
 #include <libnr/nr-scale-matrix-ops.h>
+#include "helper/geom.h"
 
 #include "livarot/Path.h"
 #include "livarot/Shape.h"
 
 #include "splivarot.h"
 
+using Inkscape::DocumentUndo;
+
 bool   Ancetre(Inkscape::XML::Node *a, Inkscape::XML::Node *who);
 
-void sp_selected_path_boolop(bool_op bop, const unsigned int verb=SP_VERB_NONE, const Glib::ustring description="");
-void sp_selected_path_do_offset(bool expand, double prefOffset);
-void sp_selected_path_create_offset_object(int expand, bool updating);
+void sp_selected_path_boolop(SPDesktop *desktop, bool_op bop, const unsigned int verb=SP_VERB_NONE, const Glib::ustring description="");
+void sp_selected_path_do_offset(SPDesktop *desktop, bool expand, double prefOffset);
+void sp_selected_path_create_offset_object(SPDesktop *desktop, int expand, bool updating);
 
 void
-sp_selected_path_union()
+sp_selected_path_union(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_union, SP_VERB_SELECTION_UNION, _("Union"));
+    sp_selected_path_boolop(desktop, bool_op_union, SP_VERB_SELECTION_UNION, _("Union"));
 }
 
 void
-sp_selected_path_union_skip_undo()
+sp_selected_path_union_skip_undo(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_union, SP_VERB_NONE, _("Union"));
+    sp_selected_path_boolop(desktop, bool_op_union, SP_VERB_NONE, _("Union"));
 }
 
 void
-sp_selected_path_intersect()
+sp_selected_path_intersect(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_inters, SP_VERB_SELECTION_INTERSECT, _("Intersection"));
+    sp_selected_path_boolop(desktop, bool_op_inters, SP_VERB_SELECTION_INTERSECT, _("Intersection"));
 }
 
 void
-sp_selected_path_diff()
+sp_selected_path_diff(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_diff, SP_VERB_SELECTION_DIFF, _("Difference"));
+    sp_selected_path_boolop(desktop, bool_op_diff, SP_VERB_SELECTION_DIFF, _("Difference"));
 }
 
 void
-sp_selected_path_diff_skip_undo()
+sp_selected_path_diff_skip_undo(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_diff, SP_VERB_NONE, _("Difference"));
+    sp_selected_path_boolop(desktop, bool_op_diff, SP_VERB_NONE, _("Difference"));
 }
 
 void
-sp_selected_path_symdiff()
+sp_selected_path_symdiff(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_symdiff, SP_VERB_SELECTION_SYMDIFF, _("Exclusion"));
+    sp_selected_path_boolop(desktop, bool_op_symdiff, SP_VERB_SELECTION_SYMDIFF, _("Exclusion"));
 }
 void
-sp_selected_path_cut()
+sp_selected_path_cut(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_cut, SP_VERB_SELECTION_CUT, _("Division"));
+    sp_selected_path_boolop(desktop, bool_op_cut, SP_VERB_SELECTION_CUT, _("Division"));
 }
 void
-sp_selected_path_slice()
+sp_selected_path_slice(SPDesktop *desktop)
 {
-    sp_selected_path_boolop(bool_op_slice, SP_VERB_SELECTION_SLICE,  _("Cut path"));
+    sp_selected_path_boolop(desktop, bool_op_slice, SP_VERB_SELECTION_SLICE,  _("Cut path"));
 }
 
 
 // boolean operations
 // take the source paths from the file, do the operation, delete the originals and add the results
 void
-sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustring description)
+sp_selected_path_boolop(SPDesktop *desktop, bool_op bop, const unsigned int verb, const Glib::ustring description)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
     
     GSList *il = (GSList *) selection->itemList();
@@ -134,8 +128,8 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
     }
 
     if (g_slist_length(il) > 2) {
-        if (bop == bool_op_diff || bop == bool_op_symdiff || bop == bool_op_cut || bop == bool_op_slice ) {
-            desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Select <b>exactly 2 paths</b> to perform difference, XOR, division, or path cut."));
+        if (bop == bool_op_diff || bop == bool_op_cut || bop == bool_op_slice ) {
+            desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Select <b>exactly 2 paths</b> to perform difference, division, or path cut."));
             return;
         }
     }
@@ -145,8 +139,7 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
     // topmost object (differences, cuts)
     bool reverseOrderForOp = false;
 
-    // mettre les elements de la liste dans l'ordre pour ces operations
-    if (bop == bool_op_diff || bop == bool_op_symdiff || bop == bool_op_cut || bop == bool_op_slice) {
+    if (bop == bool_op_diff || bop == bool_op_cut || bop == bool_op_slice) {
         // check in the tree to find which element of the selection list is topmost (for 2-operand commands only)
         Inkscape::XML::Node *a = SP_OBJECT_REPR(il->data);
         Inkscape::XML::Node *b = SP_OBJECT_REPR(il->next->data);
@@ -427,8 +420,8 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
         {
             SP_OBJECT(l->data)->deleteObject();
         }
-        sp_document_done(sp_desktop_document(desktop), SP_VERB_NONE, 
-                         description);
+        DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_NONE, 
+                           description);
         selection->clear();
 
         delete res;
@@ -438,7 +431,7 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
 
     // get the source path object
     SPObject *source;
-    if ( bop == bool_op_diff || bop == bool_op_symdiff || bop == bool_op_cut || bop == bool_op_slice ) {
+    if ( bop == bool_op_diff || bop == bool_op_cut || bop == bool_op_slice ) {
         if (reverseOrderForOp) {
              source = SP_OBJECT(il->data);
         } else {
@@ -459,11 +452,11 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
     // adjust style properties that depend on a possible transform in the source object in order
     // to get a correct style attribute for the new path
     SPItem* item_source = SP_ITEM(source);
-    NR::Matrix i2root = from_2geom(sp_item_i2root_affine(item_source));
-    sp_item_adjust_stroke(item_source, NR::expansion(i2root));
-    sp_item_adjust_pattern(item_source, i2root);
-    sp_item_adjust_gradient(item_source, i2root);
-    sp_item_adjust_livepatheffect(item_source, i2root);
+    Geom::Matrix i2doc(item_source->i2doc_affine());
+    item_source->adjust_stroke(i2doc.descrim());
+    item_source->adjust_pattern(i2doc);
+    item_source->adjust_gradient(i2doc);
+    item_source->adjust_livepatheffect(i2doc);
 
     Inkscape::XML::Node *repr_source = SP_OBJECT_REPR(source);
 
@@ -474,7 +467,8 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
     gchar const *style = repr_source->attribute("style");
     gchar const *mask = repr_source->attribute("mask");
     gchar const *clip_path = repr_source->attribute("clip-path");
-
+    gchar *title = source->title();
+    gchar *desc = source->desc();
     // remove source paths
     selection->clear();
     for (GSList *l = il; l != NULL; l = l->next) {
@@ -491,7 +485,7 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
 
     // premultiply by the inverse of parent's repr
     SPItem *parent_item = SP_ITEM(sp_desktop_document(desktop)->getObjectByRepr(parent));
-    NR::Matrix local = from_2geom(sp_item_i2doc_affine(parent_item));
+    Geom::Matrix local (parent_item->i2doc_affine());
     gchar *transform = sp_svg_transform_write(local.inverse());
 
     // now that we have the result, add it on the canvas
@@ -520,7 +514,7 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
         for (int i=0;i<nbRP;i++) {
             gchar *d = resPath[i]->svg_dump_path();
 
-            Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+            Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
             Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
             repr->setAttribute("style", style);
             if (mask)
@@ -566,7 +560,7 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
     } else {
         gchar *d = res->svg_dump_path();
 
-        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+        Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
         Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
         repr->setAttribute("style", style);
 
@@ -583,27 +577,304 @@ sp_selected_path_boolop(bool_op bop, const unsigned int verb, const Glib::ustrin
 
         repr->setAttribute("id", id);
         parent->appendChild(repr);
-        repr->setPosition(pos > 0 ? pos : 0);
+        if (title) {
+               sp_desktop_document(desktop)->getObjectByRepr(repr)->setTitle(title);
+        }            
+        if (desc) {
+               sp_desktop_document(desktop)->getObjectByRepr(repr)->setDesc(desc);
+        }
+               repr->setPosition(pos > 0 ? pos : 0);
 
         selection->add(repr);
         Inkscape::GC::release(repr);
     }
 
     g_free(transform);
+    if (title) g_free(title);
+    if (desc) g_free(desc);
 
     if (verb != SP_VERB_NONE) {
-        sp_document_done(sp_desktop_document(desktop), verb, description);
+        DocumentUndo::done(sp_desktop_document(desktop), verb, description);
     }
 
     delete res;
 }
 
+static
+void sp_selected_path_outline_add_marker( SPObject *marker_object, Geom::Matrix marker_transform,
+                                          Geom::Scale stroke_scale, Geom::Matrix transform,
+                                          Inkscape::XML::Node *g_repr, Inkscape::XML::Document *xml_doc, SPDocument * doc )
+{
+    SPMarker* marker = SP_MARKER (marker_object);
+    SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (marker_object));
 
-void
-sp_selected_path_outline()
+    Geom::Matrix tr(marker_transform);
+
+    if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
+        tr = stroke_scale * tr;
+    }
+
+    // total marker transform
+    tr = marker_item->transform * marker->c2p * tr * transform;
+
+    if (SP_OBJECT_REPR(marker_item)) {
+        Inkscape::XML::Node *m_repr = SP_OBJECT_REPR(marker_item)->duplicate(xml_doc);
+        g_repr->appendChild(m_repr);
+        SPItem *marker_item = (SPItem *) doc->getObjectByRepr(m_repr);
+        marker_item->doWriteTransform(m_repr, tr);
+    }
+}
+
+static
+void item_outline_add_marker( SPObject const *marker_object, Geom::Matrix marker_transform,
+                              Geom::Scale stroke_scale, Geom::PathVector* pathv_in )
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+    SPMarker* marker = SP_MARKER (marker_object);
+    SPItem* marker_item = sp_item_first_item_child(SP_OBJECT(marker_object));
 
+    Geom::Matrix tr(marker_transform);
+    if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
+        tr = stroke_scale * tr;
+    }
+    // total marker transform
+    tr = marker_item->transform * marker->c2p * tr;
+
+    Geom::PathVector* marker_pathv = item_outline(marker_item);
+    
+    if (marker_pathv) {
+        for (unsigned int j=0; j < marker_pathv->size(); j++) {
+            pathv_in->push_back((*marker_pathv)[j] * tr);
+        }
+        delete marker_pathv;
+    }
+}
+
+/**
+ *  Returns a pathvector that is the outline of the stroked item, with markers.
+ *  item must be SPShape of SPText.
+ */
+Geom::PathVector* item_outline(SPItem const *item)
+{
+    Geom::PathVector *ret_pathv = NULL;
+
+    if (!SP_IS_SHAPE(item) && !SP_IS_TEXT(item))
+        return ret_pathv;
+
+    // no stroke: no outline
+    if (!SP_OBJECT_STYLE(item) || SP_OBJECT_STYLE(item)->stroke.noneSet) {
+        return ret_pathv;
+    }
+
+    SPCurve *curve = NULL;
+    if (SP_IS_SHAPE(item)) {
+        curve = SP_SHAPE(item)->getCurve();
+    } else if (SP_IS_TEXT(item)) {
+        curve = SP_TEXT(item)->getNormalizedBpath();
+    }
+    if (curve == NULL) {
+        return ret_pathv;
+    }
+
+    if (curve->get_pathvector().empty()) {
+        return ret_pathv;
+    }
+
+    // remember old stroke style, to be set on fill
+    SPStyle *i_style = SP_OBJECT_STYLE(item);
+
+    Geom::Matrix const transform(item->transform);
+    float const scale = transform.descrim();
+
+    float o_width, o_miter;
+    JoinType o_join;
+    ButtType o_butt;
+    {
+        o_width = i_style->stroke_width.computed;
+        if (o_width < 0.1) {
+            o_width = 0.1;
+        }
+        o_miter = i_style->stroke_miterlimit.value * o_width;
+
+        switch (i_style->stroke_linejoin.computed) {
+            case SP_STROKE_LINEJOIN_MITER:
+                o_join = join_pointy;
+                break;
+            case SP_STROKE_LINEJOIN_ROUND:
+                o_join = join_round;
+                break;
+            default:
+                o_join = join_straight;
+                break;
+        }
+
+        switch (i_style->stroke_linecap.computed) {
+            case SP_STROKE_LINECAP_SQUARE:
+                o_butt = butt_square;
+                break;
+            case SP_STROKE_LINECAP_ROUND:
+                o_butt = butt_round;
+                break;
+            default:
+                o_butt = butt_straight;
+                break;
+        }
+    }
+
+    // Livarots outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly.
+    Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() );
+
+    Path *orig = new Path;
+    orig->LoadPathVector(pathv);
+
+    Path *res = new Path;
+    res->SetBackData(false);
+
+    if (i_style->stroke_dash.n_dash) {
+        // For dashed strokes, use Stroke method, because Outline can't do dashes
+        // However Stroke adds lots of extra nodes _or_ makes the path crooked, so consider this a temporary workaround
+
+        orig->ConvertWithBackData(0.1);
+
+        orig->DashPolylineFromStyle(i_style, scale, 0);
+
+        Shape* theShape = new Shape;
+        orig->Stroke(theShape, false, 0.5*o_width, o_join, o_butt,
+                     0.5 * o_miter);
+        orig->Outline(res, 0.5 * o_width, o_join, o_butt, 0.5 * o_miter);
+
+        Shape *theRes = new Shape;
+
+        theRes->ConvertToShape(theShape, fill_positive);
+
+        Path *originaux[1];
+        originaux[0] = res;
+        theRes->ConvertToForme(orig, 1, originaux);
+
+        res->Coalesce(5.0);
+
+        delete theShape;
+        delete theRes;
+    } else {
+        orig->Outline(res, 0.5 * o_width, o_join, o_butt, 0.5 * o_miter);
+
+        orig->Coalesce(0.5 * o_width);
+
+        Shape *theShape = new Shape;
+        Shape *theRes = new Shape;
+
+        res->ConvertWithBackData(1.0);
+        res->Fill(theShape, 0);
+        theRes->ConvertToShape(theShape, fill_positive);
+
+        Path *originaux[1];
+        originaux[0] = res;
+        theRes->ConvertToForme(orig, 1, originaux);
+
+        delete theShape;
+        delete theRes;
+    }
+
+    if (orig->descr_cmd.size() <= 1) {
+        // ca a merd\8e, ou bien le resultat est vide
+        delete res;
+        delete orig;
+        curve->unref();
+        return ret_pathv;
+    }
+
+
+    if (res->descr_cmd.size() > 1) { // if there's 0 or 1 node left, drop this path altogether
+        ret_pathv = orig->MakePathVector();
+
+        if (SP_IS_SHAPE(item) && SP_SHAPE(item)->hasMarkers ()) {
+            SPShape *shape = SP_SHAPE(item);
+
+            Geom::PathVector const & pathv = curve->get_pathvector();
+
+            // START marker
+            for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START
+                if ( SPObject *marker_obj = shape->marker[i] ) {
+                    Geom::Matrix const m (sp_shape_marker_get_transform_at_start(pathv.front().front()));
+                    item_outline_add_marker( marker_obj, m,
+                                             Geom::Scale(i_style->stroke_width.computed),
+                                             ret_pathv );
+                }
+            }
+            // MID marker
+            for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
+                SPObject *midmarker_obj = shape->marker[i];
+                if (!midmarker_obj) continue;
+                for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
+                    // START position
+                    if ( path_it != pathv.begin() 
+                         && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there
+                    {
+                        Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
+                        item_outline_add_marker( midmarker_obj, m,
+                                                 Geom::Scale(i_style->stroke_width.computed),
+                                                 ret_pathv );
+                    }
+                    // MID position
+                   if (path_it->size_default() > 1) {
+                        Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
+                        Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
+                        while (curve_it2 != path_it->end_default())
+                        {
+                            /* Put marker between curve_it1 and curve_it2.
+                             * Loop to end_default (so including closing segment), because when a path is closed,
+                             * there should be a midpoint marker between last segment and closing straight line segment
+                             */
+                            Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
+                            item_outline_add_marker( midmarker_obj, m,
+                                                     Geom::Scale(i_style->stroke_width.computed),
+                                                     ret_pathv);
+
+                            ++curve_it1;
+                            ++curve_it2;
+                        }
+                    }
+                    // END position
+                    if ( path_it != (pathv.end()-1) && !path_it->empty()) {
+                        Geom::Curve const &lastcurve = path_it->back_default();
+                        Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
+                        item_outline_add_marker( midmarker_obj, m,
+                                                 Geom::Scale(i_style->stroke_width.computed),
+                                                 ret_pathv );
+                    }
+                }
+            }
+            // END marker
+            for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
+                if ( SPObject *marker_obj = shape->marker[i] ) {
+                    /* Get reference to last curve in the path.
+                     * For moveto-only path, this returns the "closing line segment". */
+                    Geom::Path const &path_last = pathv.back();
+                    unsigned int index = path_last.size_default();
+                    if (index > 0) {
+                        index--;
+                    }
+                    Geom::Curve const &lastcurve = path_last[index];
+
+                    Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
+                    item_outline_add_marker( marker_obj, m,
+                                             Geom::Scale(i_style->stroke_width.computed),
+                                             ret_pathv );
+                }
+            }
+        }
+
+        curve->unref();
+    }
+
+    delete res;
+    delete orig;
+
+    return ret_pathv;
+}
+
+void
+sp_selected_path_outline(SPDesktop *desktop)
+{
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
@@ -624,7 +895,7 @@ sp_selected_path_outline()
 
         SPCurve *curve = NULL;
         if (SP_IS_SHAPE(item)) {
-            curve = sp_shape_get_curve(SP_SHAPE(item));
+            curve = SP_SHAPE(item)->getCurve();
             if (curve == NULL)
                 continue;
         }
@@ -634,30 +905,29 @@ sp_selected_path_outline()
                 continue;
         }
 
-        {   // pas de stroke pas de chocolat
-            SPCSSAttr *css = sp_repr_css_attr_inherited(SP_OBJECT_REPR(item), "style");
-            gchar const *val = sp_repr_css_property(css, "stroke", NULL);
+        if (curve->get_pathvector().empty()) {
+            continue;
+        }
 
-            if (val == NULL || strcmp(val, "none") == 0) {
-                curve->unref();
-                continue;
-            }
+        // pas de stroke pas de chocolat
+        if (!SP_OBJECT_STYLE(item) || SP_OBJECT_STYLE(item)->stroke.noneSet) {
+            curve->unref();
+            continue;
         }
 
         // remember old stroke style, to be set on fill
+        SPStyle *i_style = SP_OBJECT_STYLE(item);
         SPCSSAttr *ncss;
         {
-            SPCSSAttr *ocss = sp_repr_css_attr_inherited(SP_OBJECT_REPR(item), "style");
-            gchar const *val = sp_repr_css_property(ocss, "stroke", NULL);
-            gchar const *opac = sp_repr_css_property(ocss, "stroke-opacity", NULL);
-
-            ncss = sp_repr_css_attr_new();
+            ncss = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS);
+            gchar const *s_val = sp_repr_css_property(ncss, "stroke", NULL);
+            gchar const *s_opac = sp_repr_css_property(ncss, "stroke-opacity", NULL);
 
             sp_repr_css_set_property(ncss, "stroke", "none");
             sp_repr_css_set_property(ncss, "stroke-opacity", "1.0");
-            sp_repr_css_set_property(ncss, "fill", val);
-            if ( opac ) {
-                sp_repr_css_set_property(ncss, "fill-opacity", opac);
+            sp_repr_css_set_property(ncss, "fill", s_val);
+            if ( s_opac ) {
+                sp_repr_css_set_property(ncss, "fill-opacity", s_opac);
             } else {
                 sp_repr_css_set_property(ncss, "fill-opacity", "1.0");
             }
@@ -666,10 +936,8 @@ sp_selected_path_outline()
             sp_repr_css_unset_property(ncss, "marker-end");
         }
 
-        NR::Matrix const transform(item->transform);
-        float const scale = NR::expansion(transform);
-        gchar *style = g_strdup(SP_OBJECT_REPR(item)->attribute("style"));
-        SPStyle *i_style = SP_OBJECT(item)->style;
+        Geom::Matrix const transform(item->transform);
+        float const scale = transform.descrim();
         gchar const *mask = SP_OBJECT_REPR(item)->attribute("mask");
         gchar const *clip_path = SP_OBJECT_REPR(item)->attribute("clip-path");
 
@@ -713,12 +981,17 @@ sp_selected_path_outline()
             o_miter = i_style->stroke_miterlimit.value * o_width;
         }
 
-        Path *orig = Path_for_item(item, false);
-        if (orig == NULL) {
-            g_free(style);
+        SPCurve *curvetemp = curve_for_item(item);
+        if (curvetemp == NULL) {
             curve->unref();
             continue;
         }
+        // Livarots outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly.
+        Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curvetemp->get_pathvector() );
+        curvetemp->unref();
+
+        Path *orig = new Path;
+        orig->LoadPathVector(pathv);
 
         Path *res = new Path;
         res->SetBackData(false);
@@ -774,7 +1047,6 @@ sp_selected_path_outline()
             // ca a merd\8e, ou bien le resultat est vide
             delete res;
             delete orig;
-            g_free(style);
             continue;
         }
 
@@ -786,16 +1058,18 @@ sp_selected_path_outline()
         Inkscape::XML::Node *parent = SP_OBJECT_REPR(item)->parent();
         // remember id
         char const *id = SP_OBJECT_REPR(item)->attribute("id");
-
+        // remember title
+        gchar *title = item->title();
+        // remember description
+        gchar *desc = item->desc();
+        
         if (res->descr_cmd.size() > 1) { // if there's 0 or 1 node left, drop this path altogether
 
-            Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+            SPDocument * doc = sp_desktop_document(desktop);
+            Inkscape::XML::Document *xml_doc = doc->getReprDoc();
             Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
 
-            // restore old style
-            repr->setAttribute("style", style);
-
-            // set old stroke style on fill
+            // restore old style, but set old stroke style on fill
             sp_repr_css_change(repr, ncss, "style");
 
             sp_repr_css_attr_unref(ncss);
@@ -809,9 +1083,9 @@ sp_selected_path_outline()
             if (clip_path)
                 repr->setAttribute("clip-path", clip_path);
 
-            if (SP_IS_SHAPE(item) && sp_shape_has_markers (SP_SHAPE(item))) {
+            if (SP_IS_SHAPE(item) && SP_SHAPE(item)->hasMarkers ()) {
 
-                Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+                Inkscape::XML::Document *xml_doc = doc->getReprDoc();
                 Inkscape::XML::Node *g_repr = xml_doc->createElement("svg:g");
 
                 // add the group to the parent
@@ -820,38 +1094,91 @@ sp_selected_path_outline()
                 g_repr->setPosition(pos > 0 ? pos : 0);
 
                 g_repr->appendChild(repr);
+                // restore title, description, id, transform
                 repr->setAttribute("id", id);
-                SPItem *newitem = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
-                sp_item_write_transform(newitem, repr, transform);
-
+                SPItem *newitem = (SPItem *) doc->getObjectByRepr(repr);
+                newitem->doWriteTransform(repr, transform);
+                if (title) {
+                       newitem->setTitle(title);
+                }
+                if (desc) {
+                       newitem->setDesc(desc);
+                }
+                
                 SPShape *shape = SP_SHAPE(item);
 
-                for (NArtBpath const* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
-                    for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
-                        if (sp_shape_marker_required (shape, m, bp)) {
-
-                            SPMarker* marker = SP_MARKER (shape->marker[m]);
-                            SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
-
-                            NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
+                Geom::PathVector const & pathv = curve->get_pathvector();
 
-                            if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
-                                tr = NR::scale(i_style->stroke_width.computed) * tr;
-                            }
-
-                            // total marker transform
-                            tr = marker_item->transform * marker->c2p * tr * transform;
-
-                            if (SP_OBJECT_REPR(marker_item)) {
-                                Inkscape::XML::Node *m_repr = SP_OBJECT_REPR(marker_item)->duplicate(xml_doc);
-                                g_repr->appendChild(m_repr);
-                                SPItem *marker_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(m_repr);
-                                sp_item_write_transform(marker_item, m_repr, tr);
+                // START marker
+                for (int i = 0; i < 2; i++) {  // SP_MARKER_LOC and SP_MARKER_LOC_START
+                    if ( SPObject *marker_obj = shape->marker[i] ) {
+                        Geom::Matrix const m (sp_shape_marker_get_transform_at_start(pathv.front().front()));
+                        sp_selected_path_outline_add_marker( marker_obj, m,
+                                                             Geom::Scale(i_style->stroke_width.computed), transform,
+                                                             g_repr, xml_doc, doc );
+                    }
+                }
+                // MID marker
+                for (int i = 0; i < 3; i += 2) {  // SP_MARKER_LOC and SP_MARKER_LOC_MID
+                    SPObject *midmarker_obj = shape->marker[i];
+                    if (!midmarker_obj) continue;
+                    for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) {
+                        // START position
+                        if ( path_it != pathv.begin() 
+                             && ! ((path_it == (pathv.end()-1)) && (path_it->size_default() == 0)) ) // if this is the last path and it is a moveto-only, there is no mid marker there
+                        {
+                            Geom::Matrix const m (sp_shape_marker_get_transform_at_start(path_it->front()));
+                            sp_selected_path_outline_add_marker( midmarker_obj, m,
+                                                                 Geom::Scale(i_style->stroke_width.computed), transform,
+                                                                 g_repr, xml_doc, doc );
+                        }
+                        // MID position
+                       if (path_it->size_default() > 1) {
+                            Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
+                            Geom::Path::const_iterator curve_it2 = ++(path_it->begin());  // outgoing curve
+                            while (curve_it2 != path_it->end_default())
+                            {
+                                /* Put marker between curve_it1 and curve_it2.
+                                 * Loop to end_default (so including closing segment), because when a path is closed,
+                                 * there should be a midpoint marker between last segment and closing straight line segment
+                                 */
+                                Geom::Matrix const m (sp_shape_marker_get_transform(*curve_it1, *curve_it2));
+                                sp_selected_path_outline_add_marker(midmarker_obj, m,
+                                                                    Geom::Scale(i_style->stroke_width.computed), transform,
+                                                                    g_repr, xml_doc, doc);
+
+                                ++curve_it1;
+                                ++curve_it2;
                             }
                         }
+                        // END position
+                        if ( path_it != (pathv.end()-1) && !path_it->empty()) {
+                            Geom::Curve const &lastcurve = path_it->back_default();
+                            Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
+                            sp_selected_path_outline_add_marker( midmarker_obj, m,
+                                                                 Geom::Scale(i_style->stroke_width.computed), transform,
+                                                                 g_repr, xml_doc, doc );
+                        }
                     }
                 }
+                // END marker
+                for (int i = 0; i < 4; i += 3) {  // SP_MARKER_LOC and SP_MARKER_LOC_END
+                    if ( SPObject *marker_obj = shape->marker[i] ) {
+                        /* Get reference to last curve in the path.
+                         * For moveto-only path, this returns the "closing line segment". */
+                        Geom::Path const &path_last = pathv.back();
+                        unsigned int index = path_last.size_default();
+                        if (index > 0) {
+                            index--;
+                        }
+                        Geom::Curve const &lastcurve = path_last[index];
 
+                        Geom::Matrix const m = sp_shape_marker_get_transform_at_end(lastcurve);
+                        sp_selected_path_outline_add_marker( marker_obj, m,
+                                                             Geom::Scale(i_style->stroke_width.computed), transform,
+                                                             g_repr, xml_doc, doc );
+                    }
+                }
 
                 selection->add(g_repr);
 
@@ -866,11 +1193,18 @@ sp_selected_path_outline()
                 // move to the saved position
                 repr->setPosition(pos > 0 ? pos : 0);
 
+                // restore title, description, id, transform
                 repr->setAttribute("id", id);
 
                 SPItem *newitem = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
-                sp_item_write_transform(newitem, repr, transform);
-
+                newitem->doWriteTransform(repr, transform);
+                if (title) {
+                       newitem->setTitle(title);
+                }
+                if (desc) {
+                       newitem->setDesc(desc);
+                }
+                
                 selection->add(repr);
 
             }
@@ -882,16 +1216,16 @@ sp_selected_path_outline()
             SP_OBJECT(item)->deleteObject(false);
 
         }
+        if (title) g_free(title);
+        if (desc) g_free(desc);
 
         delete res;
         delete orig;
-        g_free(style);
-
     }
 
     if (did) {
-        sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_OUTLINE, 
-                         _("Convert stroke to path"));
+        DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_OUTLINE, 
+                           _("Convert stroke to path"));
     } else {
         // TRANSLATORS: "to outline" means "to convert stroke to path"
         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No stroked paths</b> in the selection."));
@@ -901,78 +1235,77 @@ sp_selected_path_outline()
 
 
 void
-sp_selected_path_offset()
+sp_selected_path_offset(SPDesktop *desktop)
 {
-    double prefOffset = prefs_get_double_attribute("options.defaultoffsetwidth", "value", 1.0);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    double prefOffset = prefs->getDouble("/options/defaultoffsetwidth/value", 1.0);
 
-    sp_selected_path_do_offset(true, prefOffset);
+    sp_selected_path_do_offset(desktop, true, prefOffset);
 }
 void
-sp_selected_path_inset()
+sp_selected_path_inset(SPDesktop *desktop)
 {
-    double prefOffset = prefs_get_double_attribute("options.defaultoffsetwidth", "value", 1.0);
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    double prefOffset = prefs->getDouble("/options/defaultoffsetwidth/value", 1.0);
 
-    sp_selected_path_do_offset(false, prefOffset);
+    sp_selected_path_do_offset(desktop, false, prefOffset);
 }
 
 void
-sp_selected_path_offset_screen(double pixels)
+sp_selected_path_offset_screen(SPDesktop *desktop, double pixels)
 {
-    sp_selected_path_do_offset(true,  pixels / SP_ACTIVE_DESKTOP->current_zoom());
+    sp_selected_path_do_offset(desktop, true,  pixels / desktop->current_zoom());
 }
 
 void
-sp_selected_path_inset_screen(double pixels)
+sp_selected_path_inset_screen(SPDesktop *desktop, double pixels)
 {
-    sp_selected_path_do_offset(false,  pixels / SP_ACTIVE_DESKTOP->current_zoom());
+    sp_selected_path_do_offset(desktop, false,  pixels / desktop->current_zoom());
 }
 
 
-void sp_selected_path_create_offset_object_zero()
+void sp_selected_path_create_offset_object_zero(SPDesktop *desktop)
 {
-    sp_selected_path_create_offset_object(0, false);
+    sp_selected_path_create_offset_object(desktop, 0, false);
 }
 
-void sp_selected_path_create_offset()
+void sp_selected_path_create_offset(SPDesktop *desktop)
 {
-    sp_selected_path_create_offset_object(1, false);
+    sp_selected_path_create_offset_object(desktop, 1, false);
 }
-void sp_selected_path_create_inset()
+void sp_selected_path_create_inset(SPDesktop *desktop)
 {
-    sp_selected_path_create_offset_object(-1, false);
+    sp_selected_path_create_offset_object(desktop, -1, false);
 }
 
-void sp_selected_path_create_updating_offset_object_zero()
+void sp_selected_path_create_updating_offset_object_zero(SPDesktop *desktop)
 {
-    sp_selected_path_create_offset_object(0, true);
+    sp_selected_path_create_offset_object(desktop, 0, true);
 }
 
-void sp_selected_path_create_updating_offset()
+void sp_selected_path_create_updating_offset(SPDesktop *desktop)
 {
-    sp_selected_path_create_offset_object(1, true);
+    sp_selected_path_create_offset_object(desktop, 1, true);
 }
-void sp_selected_path_create_updating_inset()
+void sp_selected_path_create_updating_inset(SPDesktop *desktop)
 {
-    sp_selected_path_create_offset_object(-1, true);
+    sp_selected_path_create_offset_object(desktop, -1, true);
 }
 
 void
-sp_selected_path_create_offset_object(int expand, bool updating)
+sp_selected_path_create_offset_object(SPDesktop *desktop, int expand, bool updating)
 {
     Inkscape::Selection *selection;
     Inkscape::XML::Node *repr;
     SPItem *item;
     SPCurve *curve;
     gchar *style, *str;
-    SPDesktop *desktop;
     float o_width, o_miter;
     JoinType o_join;
     ButtType o_butt;
 
     curve = NULL;
 
-    desktop = SP_ACTIVE_DESKTOP;
-
     selection = sp_desktop_selection(desktop);
 
     item = selection->singleItem();
@@ -983,7 +1316,7 @@ sp_selected_path_create_offset_object(int expand, bool updating)
     }
     if (SP_IS_SHAPE(item))
     {
-        curve = sp_shape_get_curve(SP_SHAPE(item));
+        curve = SP_SHAPE(item)->getCurve();
         if (curve == NULL)
             return;
     }
@@ -994,11 +1327,12 @@ sp_selected_path_create_offset_object(int expand, bool updating)
             return;
     }
 
-    NR::Matrix const transform(item->transform);
+    Geom::Matrix const transform(item->transform);
 
-    sp_item_write_transform(item, SP_OBJECT_REPR(item), NR::identity());
+    item->doWriteTransform(SP_OBJECT_REPR(item), Geom::identity());
 
-    style = g_strdup(SP_OBJECT(item)->repr->attribute("style"));
+       //XML Tree being used directly here while it shouldn't be...
+    style = g_strdup(SP_OBJECT(item)->getRepr()->attribute("style"));
 
     // remember the position of the item
     gint pos = SP_OBJECT_REPR(item)->position();
@@ -1038,9 +1372,8 @@ sp_selected_path_create_offset_object(int expand, bool updating)
         }
 
         {
-            double prefOffset = 1.0;
-            prefOffset = prefs_get_double_attribute("options.defaultoffsetwidth", "value", prefOffset);
-            o_width = prefOffset;
+            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+            o_width = prefs->getDouble("/options/defaultoffsetwidth/value", 1.0);
         }
 
         if (o_width < 0.01)
@@ -1095,11 +1428,11 @@ sp_selected_path_create_offset_object(int expand, bool updating)
     {
         // pas vraiment de points sur le resultat
         // donc il ne reste rien
-        sp_document_done(sp_desktop_document(desktop), 
-                         (updating ? SP_VERB_SELECTION_LINKED_OFFSET 
-                          : SP_VERB_SELECTION_DYNAMIC_OFFSET),
-                         (updating ? _("Create linked offset")
-                          : _("Create dynamic offset")));
+        DocumentUndo::done(sp_desktop_document(desktop), 
+                           (updating ? SP_VERB_SELECTION_LINKED_OFFSET 
+                            : SP_VERB_SELECTION_DYNAMIC_OFFSET),
+                           (updating ? _("Create linked offset")
+                            : _("Create dynamic offset")));
         selection->clear();
 
         delete res;
@@ -1113,7 +1446,7 @@ sp_selected_path_create_offset_object(int expand, bool updating)
 
         tstr[79] = '\0';
 
-        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+        Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
         repr = xml_doc->createElement("svg:path");
         repr->setAttribute("sodipodi:type", "inkscape:offset");
         sp_repr_set_svg_double(repr, "inkscape:radius", ( expand > 0
@@ -1127,7 +1460,9 @@ sp_selected_path_create_offset_object(int expand, bool updating)
         g_free(str);
 
         if ( updating ) {
-            char const *id = SP_OBJECT(item)->repr->attribute("id");
+
+                       //XML Tree being used directly here while it shouldn't be
+            char const *id = SP_OBJECT(item)->getRepr()->attribute("id");
             char const *uri = g_strdup_printf("#%s", id);
             repr->setAttribute("xlink:href", uri);
             g_free((void *) uri);
@@ -1148,11 +1483,11 @@ sp_selected_path_create_offset_object(int expand, bool updating)
         if ( updating ) {
             // on conserve l'original
             // we reapply the transform to the original (offset will feel it)
-            sp_item_write_transform(item, SP_OBJECT_REPR(item), transform);
+            item->doWriteTransform(SP_OBJECT_REPR(item), transform);
         } else {
             // delete original, apply the transform to the offset
             SP_OBJECT(item)->deleteObject(false);
-            sp_item_write_transform(nitem, repr, transform);
+            nitem->doWriteTransform(repr, transform);
         }
 
         // The object just created from a temporary repr is only a seed.
@@ -1164,11 +1499,11 @@ sp_selected_path_create_offset_object(int expand, bool updating)
         selection->set(nitem);
     }
 
-    sp_document_done(sp_desktop_document(desktop), 
-                     (updating ? SP_VERB_SELECTION_LINKED_OFFSET 
-                      : SP_VERB_SELECTION_DYNAMIC_OFFSET),
-                     (updating ? _("Create linked offset")
-                      : _("Create dynamic offset")));
+    DocumentUndo::done(sp_desktop_document(desktop), 
+                       (updating ? SP_VERB_SELECTION_LINKED_OFFSET 
+                        : SP_VERB_SELECTION_DYNAMIC_OFFSET),
+                       (updating ? _("Create linked offset")
+                        : _("Create dynamic offset")));
 
     delete res;
     delete orig;
@@ -1188,10 +1523,8 @@ sp_selected_path_create_offset_object(int expand, bool updating)
 
 
 void
-sp_selected_path_do_offset(bool expand, double prefOffset)
+sp_selected_path_do_offset(SPDesktop *desktop, bool expand, double prefOffset)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
@@ -1212,7 +1545,7 @@ sp_selected_path_do_offset(bool expand, double prefOffset)
 
         SPCurve *curve = NULL;
         if (SP_IS_SHAPE(item)) {
-            curve = sp_shape_get_curve(SP_SHAPE(item));
+            curve = SP_SHAPE(item)->getCurve();
             if (curve == NULL)
                 continue;
         }
@@ -1222,9 +1555,9 @@ sp_selected_path_do_offset(bool expand, double prefOffset)
                 continue;
         }
 
-        NR::Matrix const transform(item->transform);
+        Geom::Matrix const transform(item->transform);
 
-        sp_item_write_transform(item, SP_OBJECT_REPR(item), NR::identity());
+        item->doWriteTransform(SP_OBJECT_REPR(item), Geom::identity());
 
         gchar *style = g_strdup(SP_OBJECT_REPR(item)->attribute("style"));
 
@@ -1379,7 +1712,7 @@ sp_selected_path_do_offset(bool expand, double prefOffset)
 
             tstr[79] = '\0';
 
-            Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+            Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
             Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
 
             repr->setAttribute("style", style);
@@ -1397,7 +1730,7 @@ sp_selected_path_do_offset(bool expand, double prefOffset)
             SPItem *newitem = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
 
             // reapply the transform
-            sp_item_write_transform(newitem, repr, transform);
+            newitem->doWriteTransform(repr, transform);
 
             repr->setAttribute("id", id);
 
@@ -1411,9 +1744,9 @@ sp_selected_path_do_offset(bool expand, double prefOffset)
     }
 
     if (did) {
-        sp_document_done(sp_desktop_document(desktop), 
-                         (expand ? SP_VERB_SELECTION_OFFSET : SP_VERB_SELECTION_INSET),
-                         (expand ? _("Outset path") : _("Inset path")));
+        DocumentUndo::done(sp_desktop_document(desktop), 
+                           (expand ? SP_VERB_SELECTION_OFFSET : SP_VERB_SELECTION_INSET),
+                           (expand ? _("Outset path") : _("Inset path")));
     } else {
         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No paths</b> to inset/outset in the selection."));
         return;
@@ -1454,7 +1787,7 @@ sp_selected_path_simplify_item(SPDesktop *desktop,
     SPCurve *curve = NULL;
 
     if (SP_IS_SHAPE(item)) {
-        curve = sp_shape_get_curve(SP_SHAPE(item));
+        curve = SP_SHAPE(item)->getCurve();
         if (!curve)
             return false;
     }
@@ -1465,15 +1798,18 @@ sp_selected_path_simplify_item(SPDesktop *desktop,
             return false;
     }
 
+    // correct virtual size by full transform (bug #166937)
+    size /= item->i2doc_affine().descrim();
+
     // save the transform, to re-apply it after simplification
-    NR::Matrix const transform(item->transform);
+    Geom::Matrix const transform(item->transform);
 
     /*
        reset the transform, effectively transforming the item by transform.inverse();
        this is necessary so that the item is transformed twice back and forth,
        allowing all compensations to cancel out regardless of the preferences
     */
-    sp_item_write_transform(item, SP_OBJECT_REPR(item), NR::identity());
+    item->doWriteTransform(SP_OBJECT_REPR(item), Geom::identity());
 
     gchar *style = g_strdup(SP_OBJECT_REPR(item)->attribute("style"));
     gchar *mask = g_strdup(SP_OBJECT_REPR(item)->attribute("mask"));
@@ -1495,7 +1831,11 @@ sp_selected_path_simplify_item(SPDesktop *desktop,
     char const *id = SP_OBJECT_REPR(item)->attribute("id");
     // remember path effect
     char const *patheffect = SP_OBJECT_REPR(item)->attribute("inkscape:path-effect");
-
+    // remember title
+    gchar *title = item->title();
+    // remember description
+    gchar *desc = item->desc();
+    
     //If a group was selected, to not change the selection list
     if (modifySelection)
         selection->remove(item);
@@ -1509,7 +1849,7 @@ sp_selected_path_simplify_item(SPDesktop *desktop,
         orig->Simplify(threshold * size);
     }
 
-    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+    Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
     Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
 
     // restore style, mask and clip-path
@@ -1526,6 +1866,9 @@ sp_selected_path_simplify_item(SPDesktop *desktop,
         g_free(clip_path);
     }
 
+    // restore path effect
+    repr->setAttribute("inkscape:path-effect", patheffect);
+
     // path
     gchar *str = orig->svg_dump_path();
     if (patheffect)
@@ -1546,11 +1889,18 @@ sp_selected_path_simplify_item(SPDesktop *desktop,
     SPItem *newitem = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
 
     // reapply the transform
-    sp_item_write_transform(newitem, repr, transform);
-
-    // restore path effect
-    repr->setAttribute("inkscape:path-effect", patheffect);
+    newitem->doWriteTransform(repr, transform);
 
+    // restore title & description
+    if (title) {
+       newitem->setTitle(title);
+        g_free(title);
+    }
+    if (desc) {
+       newitem->setDesc(desc);
+        g_free(desc);
+    }
+    
     //If we are not in a selected group
     if (modifySelection)
         selection->add(repr);
@@ -1571,73 +1921,72 @@ sp_selected_path_simplify_items(SPDesktop *desktop,
                                 float angleLimit, bool breakableAngles,
                                 bool modifySelection)
 {
-  bool simplifyIndividualPaths =
-    (bool) prefs_get_int_attribute("options.simplifyindividualpaths", "value", 0);
-  
-  gchar *simplificationType;
-  if (simplifyIndividualPaths) {
-      simplificationType = _("Simplifying paths (separately):");
-  } else {
-      simplificationType = _("Simplifying paths:");
-  }
-
-  bool didSomething = false;
-
-  NR::Maybe<NR::Rect> selectionBbox = selection->bounds();
-  if (!selectionBbox) {
-    return false;
-  }
-  gdouble selectionSize  = L2(selectionBbox->dimensions());
-
-  gdouble simplifySize  = selectionSize;
-  
-  int pathsSimplified = 0;
-  int totalPathCount  = g_slist_length(items);
-  
-  // set "busy" cursor
-  desktop->setWaitingCursor();
-  
-  for (; items != NULL; items = items->next) {
-      SPItem *item = (SPItem *) items->data;
-      
-      if (!(SP_IS_GROUP(item) || SP_IS_SHAPE(item) || SP_IS_TEXT(item)))
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    bool simplifyIndividualPaths = prefs->getBool("/options/simplifyindividualpaths/value");
+
+    gchar *simplificationType;
+    if (simplifyIndividualPaths) {
+        simplificationType = _("Simplifying paths (separately):");
+    } else {
+        simplificationType = _("Simplifying paths:");
+    }
+
+    bool didSomething = false;
+
+    Geom::OptRect selectionBbox = selection->bounds();
+    if (!selectionBbox) {
+        return false;
+    }
+    gdouble selectionSize  = L2(selectionBbox->dimensions());
+
+    gdouble simplifySize  = selectionSize;
+
+    int pathsSimplified = 0;
+    int totalPathCount  = g_slist_length(items);
+
+    // set "busy" cursor
+    desktop->setWaitingCursor();
+
+    for (; items != NULL; items = items->next) {
+        SPItem *item = (SPItem *) items->data;
+
+        if (!(SP_IS_GROUP(item) || SP_IS_SHAPE(item) || SP_IS_TEXT(item)))
           continue;
 
-      if (simplifyIndividualPaths) {
-          NR::Maybe<NR::Rect> itemBbox = item->getBounds(from_2geom(sp_item_i2d_affine(item)));
-          if (itemBbox) {
-              simplifySize      = L2(itemBbox->dimensions());
-          } else {
-              simplifySize      = 0;
-          }
-      }
-
-      pathsSimplified++;
-
-      if (pathsSimplified % 20 == 0) {
-        gchar *message = g_strdup_printf(_("%s <b>%d</b> of <b>%d</b> paths simplified..."), simplificationType, pathsSimplified, totalPathCount);
-        desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, message);
-      }
-
-      didSomething |= sp_selected_path_simplify_item(desktop, selection, item,
-                          threshold, justCoalesce, angleLimit, breakableAngles, simplifySize, modifySelection);
-  }
-
-  desktop->clearWaitingCursor();
-
-  if (pathsSimplified > 20) {
-    desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, g_strdup_printf(_("<b>%d</b> paths simplified."), pathsSimplified));
-  }
-  
-  return didSomething;
+        if (simplifyIndividualPaths) {
+            Geom::OptRect itemBbox = item->getBounds(item->i2d_affine());
+            if (itemBbox) {
+                simplifySize      = L2(itemBbox->dimensions());
+            } else {
+                simplifySize      = 0;
+            }
+        }
+
+        pathsSimplified++;
+
+        if (pathsSimplified % 20 == 0) {
+            gchar *message = g_strdup_printf(_("%s <b>%d</b> of <b>%d</b> paths simplified..."),
+                simplificationType, pathsSimplified, totalPathCount);
+            desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, message);
+        }
+
+        didSomething |= sp_selected_path_simplify_item(desktop, selection, item,
+            threshold, justCoalesce, angleLimit, breakableAngles, simplifySize, modifySelection);
+    }
+
+    desktop->clearWaitingCursor();
+
+    if (pathsSimplified > 20) {
+        desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, g_strdup_printf(_("<b>%d</b> paths simplified."), pathsSimplified));
+    }
+
+    return didSomething;
 }
 
 void
-sp_selected_path_simplify_selection(float threshold, bool justCoalesce,
-                       float angleLimit, bool breakableAngles)
+sp_selected_path_simplify_selection(SPDesktop *desktop, float threshold, bool justCoalesce,
+                                    float angleLimit, bool breakableAngles)
 {
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-
     Inkscape::Selection *selection = sp_desktop_selection(desktop);
 
     if (selection->isEmpty()) {
@@ -1655,8 +2004,8 @@ sp_selected_path_simplify_selection(float threshold, bool justCoalesce,
                                                         breakableAngles, true);
 
     if (didSomething)
-        sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_SIMPLIFY, 
-                         _("Simplify"));
+        DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_SIMPLIFY, 
+                           _("Simplify"));
     else
         desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No paths</b> to simplify in the selection."));
 
@@ -1668,12 +2017,12 @@ static double previousTime      = 0.0;
 static gdouble simplifyMultiply = 1.0;
 
 void
-sp_selected_path_simplify(void)
+sp_selected_path_simplify(SPDesktop *desktop)
 {
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
     gdouble simplifyThreshold =
-        prefs_get_double_attribute("options.simplifythreshold", "value", 0.003);
-    bool simplifyJustCoalesce =
-        (bool) prefs_get_int_attribute("options.simplifyjustcoalesce", "value", 0);
+        prefs->getDouble("/options/simplifythreshold/value", 0.003);
+    bool simplifyJustCoalesce = prefs->getBool("/options/simplifyjustcoalesce/value", 0);
 
     //Get the current time
     GTimeVal currentTimeVal;
@@ -1699,8 +2048,8 @@ sp_selected_path_simplify(void)
     //g_print("%g\n", simplify_threshold);
 
     //Make the actual call
-    sp_selected_path_simplify_selection(simplifyThreshold,
-                      simplifyJustCoalesce, 0.0, false);
+    sp_selected_path_simplify_selection(desktop, simplifyThreshold,
+                                        simplifyJustCoalesce, 0.0, false);
 }
 
 
@@ -1721,61 +2070,57 @@ Path *
 Path_for_item(SPItem *item, bool doTransformation, bool transformFull)
 {
     SPCurve *curve = curve_for_item(item);
-    NArtBpath *bpath = bpath_for_curve(item, curve, doTransformation, transformFull);
-    
-    if (bpath == NULL) {
+
+    if (curve == NULL)
         return NULL;
-    }
     
-    Path *dest = bpath_to_Path(bpath);
-
-    g_free(bpath);
-
+    Geom::PathVector *pathv = pathvector_for_curve(item, curve, doTransformation, transformFull, Geom::identity(), Geom::identity());
     curve->unref();
     
+    Path *dest = new Path;
+    dest->LoadPathVector(*pathv);    
+    delete pathv;
+    
     return dest;
 }
 
 /* 
- * This function always returns a new NArtBpath, the caller must g_free the returned path!
-*/
-NArtBpath *
-bpath_for_curve(SPItem *item, SPCurve *curve, bool doTransformation, bool transformFull)
+ * NOTE: Returns empty pathvector if curve == NULL
+ * TODO: see if calling this method can be optimized. All the pathvector copying might be slow.
+ */
+Geom::PathVector*
+pathvector_for_curve(SPItem *item, SPCurve *curve, bool doTransformation, bool transformFull, Geom::Matrix extraPreAffine, Geom::Matrix extraPostAffine)
 {
     if (curve == NULL)
         return NULL;
 
-    NArtBpath const *bpath = SP_CURVE_BPATH(curve);
-    if (bpath == NULL) {
-        return NULL;
-    }
-
-    NArtBpath *new_bpath; // we will get a duplicate which has to be freed at some point!
+    Geom::PathVector *dest = new Geom::PathVector;    
+    *dest = curve->get_pathvector(); // Make a copy; must be freed by the caller!
+    
     if (doTransformation) {
         if (transformFull) {
-            new_bpath = nr_artpath_affine(bpath, from_2geom(sp_item_i2doc_affine(item)));
+            *dest *= extraPreAffine * item->i2doc_affine() * extraPostAffine;
         } else {
-            new_bpath = nr_artpath_affine(bpath, item->transform);
+            *dest *= extraPreAffine * (Geom::Matrix)item->transform * extraPostAffine;
         }
     } else {
-        new_bpath = nr_artpath_affine(bpath, NR::identity());
+        *dest *= extraPreAffine * extraPostAffine;
     }
-
-    return new_bpath;
+    
+    return dest;
 }
 
 SPCurve* curve_for_item(SPItem *item)
 {
-    if (!item)
+    if (!item) 
         return NULL;
-
+    
     SPCurve *curve = NULL;
-
     if (SP_IS_SHAPE(item)) {
         if (SP_IS_PATH(item)) {
             curve = sp_path_get_curve_for_edit(SP_PATH(item));
         } else {
-            curve = sp_shape_get_curve(SP_SHAPE(item));
+            curve = SP_SHAPE(item)->getCurve();
         }
     }
     else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))
@@ -1784,79 +2129,22 @@ SPCurve* curve_for_item(SPItem *item)
     }
     else if (SP_IS_IMAGE(item))
     {
-        curve = sp_image_get_curve(SP_IMAGE(item));
+    curve = sp_image_get_curve(SP_IMAGE(item));
     }
     
     return curve; // do not forget to unref the curve at some point!
 }
 
-Path *bpath_to_Path(NArtBpath const *bpath) {
-    Path *dest = new Path;
-    dest->SetBackData(false);
-    {
-        int   i;
-        bool  closed = false;
-        float lastX  = 0.0;
-        float lastY  = 0.0;
-
-        for (i = 0; bpath[i].code != NR_END; i++) {
-            switch (bpath[i].code) {
-                case NR_LINETO:
-                    lastX = bpath[i].x3;
-                    lastY = bpath[i].y3;
-                    {
-                        NR::Point tmp(lastX, lastY);
-                        dest->LineTo(tmp);
-                    }
-                    break;
-
-                case NR_CURVETO:
-                {
-                    NR::Point tmp, tms, tme;
-                    tmp[0]=bpath[i].x3;
-                    tmp[1]=bpath[i].y3;
-                    tms[0]=3 * (bpath[i].x1 - lastX);
-                    tms[1]=3 * (bpath[i].y1 - lastY);
-                    tme[0]=3 * (bpath[i].x3 - bpath[i].x2);
-                    tme[1]=3 * (bpath[i].y3 - bpath[i].y2);
-                    dest->CubicTo(tmp,tms,tme);
-                }
-                lastX = bpath[i].x3;
-                lastY = bpath[i].y3;
-                break;
-
-                case NR_MOVETO_OPEN:
-                case NR_MOVETO:
-                    if (closed)
-                        dest->Close();
-                    closed = (bpath[i].code == NR_MOVETO);
-                    lastX = bpath[i].x3;
-                    lastY = bpath[i].y3;
-                    {
-                        NR::Point  tmp(lastX, lastY);
-                        dest->MoveTo(tmp);
-                    }
-                    break;
-                default:
-                    break;
-            }
-        }
-        if (closed)
-            dest->Close();
-    }
-    return dest;
-}
-
-NR::Maybe<Path::cut_position> get_nearest_position_on_Path(Path *path, NR::Point p, unsigned seg)
+boost::optional<Path::cut_position> get_nearest_position_on_Path(Path *path, Geom::Point p, unsigned seg)
 {
     //get nearest position on path
     Path::cut_position pos = path->PointToCurvilignPosition(p, seg);
     return pos;
 }
 
-NR::Point get_point_on_Path(Path *path, int piece, double t)
+Geom::Point get_point_on_Path(Path *path, int piece, double t)
 {
-    NR::Point p;
+    Geom::Point p;
     path->PointAt(piece, t, p);
     return p;
 }
@@ -1871,4 +2159,4 @@ NR::Point get_point_on_Path(Path *path, int piece, double t)
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :