static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item) { g_assert(SP_IS_ITEM(item)); g_assert(!SP_IS_ANCHOR(item)); SPDesktop *desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop"); g_return_if_fail(desktop != NULL); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); Inkscape::XML::Node *repr = xml_doc->createElement("svg:a"); SP_OBJECT_REPR(SP_OBJECT_PARENT(item))->addChild(repr, SP_OBJECT_REPR(item)); SPObject *object = SP_OBJECT_DOCUMENT(item)->getObjectByRepr(repr); g_return_if_fail(SP_IS_ANCHOR(object)); const char *id = SP_OBJECT_REPR(item)->attribute("id"); Inkscape::XML::Node *child = SP_OBJECT_REPR(item)->duplicate(xml_doc); SP_OBJECT(item)->deleteObject(false); repr->addChild(child, NULL); child->setAttribute("id", id); Inkscape::GC::release(repr); Inkscape::GC::release(child); sp_document_done(SP_OBJECT_DOCUMENT(object), SP_VERB_NONE, _("Create link")); sp_object_attributes_dialog(object, "SPAnchor"); sp_desktop_selection(desktop)->set(SP_ITEM(object)); }
/** * Writes the gradient's internal vector (whether from its own stops, or * inherited from refs) into the gradient repr as svg:stop elements. */ void sp_gradient_repr_write_vector(SPGradient *gr) { g_return_if_fail(gr != NULL); g_return_if_fail(SP_IS_GRADIENT(gr)); Inkscape::XML::Document *xml_doc = gr->document->getReprDoc(); Inkscape::XML::Node *repr = gr->getRepr(); /* We have to be careful, as vector may be our own, so construct repr list at first */ GSList *cl = NULL; for (guint i = 0; i < gr->vector.stops.size(); i++) { Inkscape::CSSOStringStream os; Inkscape::XML::Node *child = xml_doc->createElement("svg:stop"); sp_repr_set_css_double(child, "offset", gr->vector.stops[i].offset); /* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no * sense for offset proportions. */ os << "stop-color:" << gr->vector.stops[i].color.toString() << ";stop-opacity:" << gr->vector.stops[i].opacity; child->setAttribute("style", os.str().c_str()); /* Order will be reversed here */ cl = g_slist_prepend(cl, child); } sp_gradient_repr_clear_vector(gr); /* And insert new children from list */ while (cl) { Inkscape::XML::Node *child = static_cast<Inkscape::XML::Node *>(cl->data); repr->addChild(child, NULL); Inkscape::GC::release(child); cl = g_slist_remove(cl, child); } }
static void sp_box3d_drag(Box3DContext &bc, guint /*state*/) { SPDesktop *desktop = SP_EVENT_CONTEXT(&bc)->desktop; if (!bc.item) { if (Inkscape::have_viable_layer(desktop, bc._message_context) == false) { return; } /* Create object */ Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_EVENT_CONTEXT_DOCUMENT(&bc)); Inkscape::XML::Node *repr = xml_doc->createElement("svg:g"); repr->setAttribute("sodipodi:type", "inkscape:box3d"); /* Set style */ sp_desktop_apply_style_tool (desktop, repr, "/tools/shapes/3dbox", false); bc.item = (SPItem *) desktop->currentLayer()->appendChildRepr(repr); Inkscape::GC::release(repr); Inkscape::XML::Node *repr_side; // TODO: Incorporate this in box3d-side.cpp! for (int i = 0; i < 6; ++i) { repr_side = xml_doc->createElement("svg:path"); repr_side->setAttribute("sodipodi:type", "inkscape:box3dside"); repr->addChild(repr_side, NULL); Box3DSide *side = SP_BOX3D_SIDE(inkscape_active_document()->getObjectByRepr (repr_side)); guint desc = Box3D::int_to_face(i); Box3D::Axis plane = (Box3D::Axis) (desc & 0x7); plane = (Box3D::is_plane(plane) ? plane : Box3D::orth_plane_or_axis(plane)); side->dir1 = Box3D::extract_first_axis_direction(plane); side->dir2 = Box3D::extract_second_axis_direction(plane); side->front_or_rear = (Box3D::FrontOrRear) (desc & 0x8); /* Set style */ box3d_side_apply_style(side); SP_OBJECT(side)->updateRepr(); // calls box3d_side_write() and updates, e.g., the axes string description } box3d_set_z_orders(SP_BOX3D(bc.item)); bc.item->updateRepr(); // TODO: It would be nice to show the VPs during dragging, but since there is no selection // at this point (only after finishing the box), we must do this "manually" /* bc._vpdrag->updateDraggers(); */ desktop->canvas->force_full_redraw_after_interruptions(5); } g_assert(bc.item); SPBox3D *box = SP_BOX3D(bc.item); box->orig_corner0 = bc.drag_origin_proj; box->orig_corner7 = bc.drag_ptC_proj; box3d_check_for_swapped_coords(box); /* we need to call this from here (instead of from box3d_position_set(), for example) because z-order setting must not interfere with display updates during undo/redo */ box3d_set_z_orders (box); box3d_position_set(box); // status text bc._message_context->setF(Inkscape::NORMAL_MESSAGE, "%s", _("<b>3D Box</b>; with <b>Shift</b> to extrude along the Z axis")); }
SPItem *SPUse::unlink() { Inkscape::XML::Node *repr = this->getRepr(); if (!repr) { return NULL; } Inkscape::XML::Node *parent = repr->parent(); SPDocument *document = this->document; Inkscape::XML::Document *xml_doc = document->getReprDoc(); // Track the ultimate source of a chain of uses. SPItem *orig = this->root(); if (!orig) { return NULL; } // Calculate the accumulated transform, starting from the original. Geom::Affine t = this->get_root_transform(); Inkscape::XML::Node *copy = NULL; if (dynamic_cast<SPSymbol *>(orig)) { // make a group, copy children copy = xml_doc->createElement("svg:g"); for (Inkscape::XML::Node *child = orig->getRepr()->firstChild() ; child != NULL; child = child->next()) { Inkscape::XML::Node *newchild = child->duplicate(xml_doc); copy->appendChild(newchild); } } else { // just copy copy = orig->getRepr()->duplicate(xml_doc); } // Add the duplicate repr just after the existing one. parent->addChild(copy, repr); // Retrieve the SPItem of the resulting repr. SPObject *unlinked = document->getObjectByRepr(copy); // Merge style from the use. unlinked->style->merge( this->style ); unlinked->style->cascade( unlinked->parent->style ); unlinked->updateRepr(); // Hold onto our SPObject and repr for now. sp_object_ref(this, NULL); Inkscape::GC::anchor(repr); // Remove ourselves, not propagating delete events to avoid a // chain-reaction with other elements that might reference us. this->deleteObject(false); // Give the copy our old id and let go of our old repr. copy->setAttribute("id", repr->attribute("id")); Inkscape::GC::release(repr); // Remove tiled clone attrs. copy->setAttribute("inkscape:tiled-clone-of", NULL); copy->setAttribute("inkscape:tile-w", NULL); copy->setAttribute("inkscape:tile-h", NULL); copy->setAttribute("inkscape:tile-cx", NULL); copy->setAttribute("inkscape:tile-cy", NULL); // Establish the succession and let go of our object. this->setSuccessor(unlinked); sp_object_unref(this, NULL); SPItem *item = dynamic_cast<SPItem *>(unlinked); g_assert(item != NULL); // Set the accummulated transform. { Geom::Affine nomove(Geom::identity()); // Advertise ourselves as not moving. item->doWriteTransform(item->getRepr(), t, &nomove); } return item; }
SPDocument * sp_document_create(Inkscape::XML::Document *rdoc, gchar const *uri, gchar const *base, gchar const *name, unsigned int keepalive) { SPDocument *document; Inkscape::XML::Node *rroot; Inkscape::Version sodipodi_version; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); rroot = rdoc->root(); document = new SPDocument(); document->keepalive = keepalive; document->rdoc = rdoc; document->rroot = rroot; #ifndef WIN32 prepend_current_dir_if_relative(&(document->uri), uri); #else // FIXME: it may be that prepend_current_dir_if_relative works OK on windows too, test! document->uri = uri? g_strdup(uri) : NULL; #endif // base is simply the part of the path before filename; e.g. when running "inkscape ../file.svg" the base is "../" // which is why we use g_get_current_dir() in calculating the abs path above //This is NULL for a new document if (base) document->base = g_strdup(base); else document->base = NULL; document->name = g_strdup(name); document->root = sp_object_repr_build_tree(document, rroot); sodipodi_version = SP_ROOT(document->root)->version.sodipodi; /* fixme: Not sure about this, but lets assume ::build updates */ rroot->setAttribute("sodipodi:version", SODIPODI_VERSION); rroot->setAttribute("inkscape:version", Inkscape::version_string); /* fixme: Again, I moved these here to allow version determining in ::build (Lauris) */ /* Quick hack 2 - get default image size into document */ if (!rroot->attribute("width")) rroot->setAttribute("width", "100%"); if (!rroot->attribute("height")) rroot->setAttribute("height", "100%"); /* End of quick hack 2 */ /* Quick hack 3 - Set uri attributes */ if (uri) { rroot->setAttribute("sodipodi:docname", uri); } /* End of quick hack 3 */ /* Eliminate obsolete sodipodi:docbase, for privacy reasons */ rroot->setAttribute("sodipodi:docbase", NULL); /* Eliminate any claim to adhere to a profile, as we don't try to */ rroot->setAttribute("baseProfile", NULL); // creating namedview if (!sp_item_group_get_child_by_name((SPGroup *) document->root, NULL, "sodipodi:namedview")) { // if there's none in the document already, Inkscape::XML::Node *rnew = NULL; rnew = rdoc->createElement("sodipodi:namedview"); //rnew->setAttribute("id", "base"); // Add namedview data from the preferences // we can't use getAllEntries because this could produce non-SVG doubles Glib::ustring pagecolor = prefs->getString("/template/base/pagecolor"); if (!pagecolor.empty()) { rnew->setAttribute("pagecolor", pagecolor.data()); } Glib::ustring bordercolor = prefs->getString("/template/base/bordercolor"); if (!bordercolor.empty()) { rnew->setAttribute("bordercolor", bordercolor.data()); } sp_repr_set_svg_double(rnew, "borderopacity", prefs->getDouble("/template/base/borderopacity", 1.0)); sp_repr_set_svg_double(rnew, "objecttolerance", prefs->getDouble("/template/base/objecttolerance", 10.0)); sp_repr_set_svg_double(rnew, "gridtolerance", prefs->getDouble("/template/base/gridtolerance", 10.0)); sp_repr_set_svg_double(rnew, "guidetolerance", prefs->getDouble("/template/base/guidetolerance", 10.0)); sp_repr_set_svg_double(rnew, "inkscape:pageopacity", prefs->getDouble("/template/base/inkscape:pageopacity", 0.0)); sp_repr_set_int(rnew, "inkscape:pageshadow", prefs->getInt("/template/base/inkscape:pageshadow", 2)); sp_repr_set_int(rnew, "inkscape:window-width", prefs->getInt("/template/base/inkscape:window-width", 640)); sp_repr_set_int(rnew, "inkscape:window-height", prefs->getInt("/template/base/inkscape:window-height", 480)); // insert into the document rroot->addChild(rnew, NULL); // clean up Inkscape::GC::release(rnew); } /* Defs */ if (!SP_ROOT(document->root)->defs) { Inkscape::XML::Node *r; r = rdoc->createElement("svg:defs"); rroot->addChild(r, NULL); Inkscape::GC::release(r); g_assert(SP_ROOT(document->root)->defs); } /* Default RDF */ rdf_set_defaults( document ); if (keepalive) { inkscape_ref(); } // Remark: Here, we used to create a "currentpersp3d" element in the document defs. // But this is probably a bad idea since we need to adapt it for every change of selection, which will // completely clutter the undo history. Maybe rather save it to prefs on exit and re-read it on startup? document->current_persp3d = persp3d_document_first_persp(document); if (!document->current_persp3d) { document->current_persp3d = persp3d_create_xml_element (document); } sp_document_set_undo_sensitive(document, true); // reset undo key when selection changes, so that same-key actions on different objects are not coalesced if (!Inkscape::NSApplication::Application::getNewGui()) { g_signal_connect(G_OBJECT(INKSCAPE), "change_selection", G_CALLBACK(sp_document_reset_key), document); g_signal_connect(G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_document_reset_key), document); } else { document->_selection_changed_connection = Inkscape::NSApplication::Editor::connectSelectionChanged (sigc::mem_fun (*document, &SPDocument::reset_key)); document->_desktop_activated_connection = Inkscape::NSApplication::Editor::connectDesktopActivated (sigc::mem_fun (*document, &SPDocument::reset_key)); } return document; }
void text_put_on_path() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (!desktop) return; Inkscape::Selection *selection = sp_desktop_selection(desktop); SPItem *text = text_or_flowtext_in_selection(selection); SPItem *shape = shape_in_selection(selection); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); if (!text || !shape || g_slist_length((GSList *) selection->itemList()) != 2) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text and a path</b> to put text on path.")); return; } if (SP_IS_TEXT_TEXTPATH(text)) { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("This text object is <b>already put on a path</b>. Remove it from the path first. Use <b>Shift+D</b> to look up its path.")); return; } if (SP_IS_RECT(shape)) { // rect is the only SPShape which is not <path> yet, and thus SVG forbids us from putting text on it sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put text on a rectangle in this version. Convert rectangle to path first.")); return; } // if a flowed text is selected, convert it to a regular text object if (SP_IS_FLOWTEXT(text)) { if (!SP_FLOWTEXT(text)->layout.outputExists()) { sp_desktop_message_stack(desktop)-> flash(Inkscape::WARNING_MESSAGE, _("The flowed text(s) must be <b>visible</b> in order to be put on a path.")); } Inkscape::XML::Node *repr = SP_FLOWTEXT(text)->getAsText(); if (!repr) return; Inkscape::XML::Node *parent = SP_OBJECT_REPR(text)->parent(); parent->appendChild(repr); SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr); sp_item_write_transform(new_item, repr, text->transform); SP_OBJECT(new_item)->updateRepr(); Inkscape::GC::release(repr); text->deleteObject(); // delete the orignal flowtext sp_document_ensure_up_to_date(sp_desktop_document(desktop)); selection->clear(); text = new_item; // point to the new text } Inkscape::Text::Layout const *layout = te_get_layout(text); Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(layout->begin()); // remove transform from text, but recursively scale text's fontsize by the expansion SP_TEXT(text)->_adjustFontsizeRecursive (text, NR::expansion(SP_ITEM(text)->transform)); SP_OBJECT_REPR(text)->setAttribute("transform", NULL); // make a list of text children GSList *text_reprs = NULL; for (SPObject *o = SP_OBJECT(text)->children; o != NULL; o = o->next) { text_reprs = g_slist_prepend(text_reprs, SP_OBJECT_REPR(o)); } // create textPath and put it into the text Inkscape::XML::Node *textpath = xml_doc->createElement("svg:textPath"); // reference the shape textpath->setAttribute("xlink:href", g_strdup_printf("#%s", SP_OBJECT_REPR(shape)->attribute("id"))); if (text_alignment == Inkscape::Text::Layout::RIGHT) textpath->setAttribute("startOffset", "100%"); else if (text_alignment == Inkscape::Text::Layout::CENTER) textpath->setAttribute("startOffset", "50%"); SP_OBJECT_REPR(text)->addChild(textpath, NULL); for ( GSList *i = text_reprs ; i ; i = i->next ) { // Make a copy of each text child Inkscape::XML::Node *copy = ((Inkscape::XML::Node *) i->data)->duplicate(xml_doc); // We cannot have multiline in textpath, so remove line attrs from tspans if (!strcmp(copy->name(), "svg:tspan")) { copy->setAttribute("sodipodi:role", NULL); copy->setAttribute("x", NULL); copy->setAttribute("y", NULL); } // remove the old repr from under text SP_OBJECT_REPR(text)->removeChild((Inkscape::XML::Node *) i->data); // put its copy into under textPath textpath->addChild(copy, NULL); // fixme: copy id } // x/y are useless with textpath, and confuse Batik 1.5 SP_OBJECT_REPR(text)->setAttribute("x", NULL); SP_OBJECT_REPR(text)->setAttribute("y", NULL); sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, _("Put text on path")); g_slist_free(text_reprs); }
void flowtext_to_text() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>flowed text(s)</b> to convert.")); return; } bool did = false; GSList *reprs = NULL; GSList *items = g_slist_copy((GSList *) selection->itemList()); for (; items != NULL; items = items->next) { SPItem *item = (SPItem *) items->data; if (!SP_IS_FLOWTEXT(item)) continue; if (!SP_FLOWTEXT(item)->layout.outputExists()) { sp_desktop_message_stack(desktop)-> flash(Inkscape::WARNING_MESSAGE, _("The flowed text(s) must be <b>visible</b> in order to be converted.")); return; } Inkscape::XML::Node *repr = SP_FLOWTEXT(item)->getAsText(); if (!repr) break; did = true; Inkscape::XML::Node *parent = SP_OBJECT_REPR(item)->parent(); parent->addChild(repr, SP_OBJECT_REPR(item)); SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr); sp_item_write_transform(new_item, repr, item->transform); SP_OBJECT(new_item)->updateRepr(); Inkscape::GC::release(repr); item->deleteObject(); reprs = g_slist_prepend(reprs, repr); } g_slist_free(items); if (did) { sp_document_done(sp_desktop_document(desktop), SP_VERB_OBJECT_FLOWTEXT_TO_TEXT, _("Convert flowed text to text")); selection->setReprList(reprs); } else { sp_desktop_message_stack(desktop)-> flash(Inkscape::ERROR_MESSAGE, _("<b>No flowed text(s)</b> to convert in the selection.")); } g_slist_free(reprs); }
void text_unflow () { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (!desktop) return; SPDocument *doc = sp_desktop_document (desktop); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); Inkscape::Selection *selection = sp_desktop_selection(desktop); if (!flowtext_in_selection(selection) || g_slist_length((GSList *) selection->itemList()) < 1) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a flowed text</b> to unflow it.")); return; } GSList *new_objs = NULL; GSList *old_objs = NULL; for (GSList *items = g_slist_copy((GSList *) selection->itemList()); items != NULL; items = items->next) { if (!SP_IS_FLOWTEXT(SP_OBJECT(items->data))) { continue; } SPItem *flowtext = SP_ITEM(items->data); if (sp_te_get_string_multiline(flowtext) == NULL) { // flowtext is empty continue; } /* Create <text> */ Inkscape::XML::Node *rtext = xml_doc->createElement("svg:text"); rtext->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create /* Set style */ rtext->setAttribute("style", SP_OBJECT_REPR(flowtext)->attribute("style")); // fixme: transfer style attrs too; and from descendants NRRect bbox; sp_item_invoke_bbox(SP_ITEM(flowtext), &bbox, sp_item_i2doc_affine(SP_ITEM(flowtext)), TRUE); Geom::Point xy(bbox.x0, bbox.y0); if (xy[Geom::X] != 1e18 && xy[Geom::Y] != 1e18) { sp_repr_set_svg_double(rtext, "x", xy[Geom::X]); sp_repr_set_svg_double(rtext, "y", xy[Geom::Y]); } /* Create <tspan> */ Inkscape::XML::Node *rtspan = xml_doc->createElement("svg:tspan"); rtspan->setAttribute("sodipodi:role", "line"); // otherwise, why bother creating the tspan? rtext->addChild(rtspan, NULL); gchar *text_string = sp_te_get_string_multiline(flowtext); Inkscape::XML::Node *text_repr = xml_doc->createTextNode(text_string); // FIXME: transfer all formatting!!! free(text_string); rtspan->appendChild(text_repr); SP_OBJECT_REPR(SP_OBJECT_PARENT(flowtext))->appendChild(rtext); SPObject *text_object = doc->getObjectByRepr(rtext); new_objs = g_slist_prepend (new_objs, text_object); old_objs = g_slist_prepend (old_objs, flowtext); Inkscape::GC::release(rtext); Inkscape::GC::release(rtspan); Inkscape::GC::release(text_repr); } selection->clear(); selection->setList(new_objs); for (GSList *i = old_objs; i; i = i->next) { SP_OBJECT(i->data)->deleteObject (true); } g_slist_free (old_objs); g_slist_free (new_objs); sp_document_done(doc, SP_VERB_CONTEXT_TEXT, _("Unflow flowed text")); }
/** * Threaded method that does single bitmap--->path conversion */ void Tracer::traceThread() { //## Remember. NEVER leave this method without setting //## engine back to NULL //## Prepare our kill flag. We will watch this later to //## see if the main thread wants us to stop keepGoing = true; SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (!desktop) { g_warning("Trace: No active desktop\n"); return; } Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop); Inkscape::Selection *selection = sp_desktop_selection (desktop); if (!SP_ACTIVE_DOCUMENT) { char *msg = _("Trace: No active document"); msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); engine = NULL; return; } SPDocument *doc = SP_ACTIVE_DOCUMENT; sp_document_ensure_up_to_date(doc); SPImage *img = getSelectedSPImage(); if (!img) { engine = NULL; return; } Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(img->pixbuf, true); pixbuf = sioxProcessImage(img, pixbuf); if (!pixbuf) { char *msg = _("Trace: Image has no bitmap data"); msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); engine = NULL; return; } msgStack->flash(Inkscape::NORMAL_MESSAGE, _("Trace: Starting trace...")); desktop->updateCanvasNow(); std::vector<TracingEngineResult> results = engine->trace(pixbuf); //printf("nrPaths:%d\n", results.size()); int nrPaths = results.size(); //### Check if we should stop if (!keepGoing || nrPaths<1) { engine = NULL; return; } //### Get pointers to the <image> and its parent Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr; Inkscape::XML::Node *par = sp_repr_parent(imgRepr); //### Get some information for the new transform() double x = 0.0; double y = 0.0; double width = 0.0; double height = 0.0; double dval = 0.0; if (sp_repr_get_double(imgRepr, "x", &dval)) x = dval; if (sp_repr_get_double(imgRepr, "y", &dval)) y = dval; if (sp_repr_get_double(imgRepr, "width", &dval)) width = dval; if (sp_repr_get_double(imgRepr, "height", &dval)) height = dval; double iwidth = (double)pixbuf->get_width(); double iheight = (double)pixbuf->get_height(); double iwscale = width / iwidth; double ihscale = height / iheight; Geom::Translate trans(x, y); Geom::Scale scal(iwscale, ihscale); //# Convolve scale, translation, and the original transform Geom::Matrix tf(scal * trans); tf *= img->transform; //#OK. Now let's start making new nodes Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); Inkscape::XML::Node *groupRepr = NULL; //# if more than 1, make a <g>roup of <path>s if (nrPaths > 1) { groupRepr = xml_doc->createElement("svg:g"); par->addChild(groupRepr, imgRepr); } long totalNodeCount = 0L; for (unsigned int i=0 ; i<results.size() ; i++) { TracingEngineResult result = results[i]; totalNodeCount += result.getNodeCount(); Inkscape::XML::Node *pathRepr = xml_doc->createElement("svg:path"); pathRepr->setAttribute("style", result.getStyle().c_str()); pathRepr->setAttribute("d", result.getPathData().c_str()); if (nrPaths > 1) groupRepr->addChild(pathRepr, NULL); else par->addChild(pathRepr, imgRepr); //### Apply the transform from the image to the new shape SPObject *reprobj = doc->getObjectByRepr(pathRepr); if (reprobj) { SPItem *newItem = SP_ITEM(reprobj); sp_item_write_transform(newItem, pathRepr, tf, NULL); } if (nrPaths == 1) { selection->clear(); selection->add(pathRepr); } Inkscape::GC::release(pathRepr); } // If we have a group, then focus on, then forget it if (nrPaths > 1) { selection->clear(); selection->add(groupRepr); Inkscape::GC::release(groupRepr); } //## inform the document, so we can undo sp_document_done(doc, SP_VERB_SELECTION_TRACE, _("Trace bitmap")); engine = NULL; char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount); msgStack->flash(Inkscape::NORMAL_MESSAGE, msg); g_free(msg); }