SPItem *create_flowtext_with_internal_frame (SPDesktop *desktop, Geom::Point p0, Geom::Point p1)
{
    SPDocument *doc = sp_desktop_document (desktop);

    Inkscape::XML::Document *xml_doc = doc->getReprDoc();
    Inkscape::XML::Node *root_repr = xml_doc->createElement("svg:flowRoot");
    root_repr->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create
    SPItem *ft_item = SP_ITEM(desktop->currentLayer()->appendChildRepr(root_repr));
    SPObject *root_object = doc->getObjectByRepr(root_repr);
    g_assert(SP_IS_FLOWTEXT(root_object));

    Inkscape::XML::Node *region_repr = xml_doc->createElement("svg:flowRegion");
    root_repr->appendChild(region_repr);
    SPObject *region_object = doc->getObjectByRepr(region_repr);
    g_assert(SP_IS_FLOWREGION(region_object));

    Inkscape::XML::Node *rect_repr = xml_doc->createElement("svg:rect"); // FIXME: use path!!! after rects are converted to use path
    region_repr->appendChild(rect_repr);

    SPRect *rect = SP_RECT(doc->getObjectByRepr(rect_repr));

    p0 *= desktop->dt2doc();
    p1 *= desktop->dt2doc();
    using Geom::X;
    using Geom::Y;
    Geom::Coord const x0 = MIN(p0[X], p1[X]);
    Geom::Coord const y0 = MIN(p0[Y], p1[Y]);
    Geom::Coord const x1 = MAX(p0[X], p1[X]);
    Geom::Coord const y1 = MAX(p0[Y], p1[Y]);
    Geom::Coord const w  = x1 - x0;
    Geom::Coord const h  = y1 - y0;

    rect->setPosition(x0, y0, w, h);
    rect->updateRepr();

    Inkscape::XML::Node *para_repr = xml_doc->createElement("svg:flowPara");
    root_repr->appendChild(para_repr);
    SPObject *para_object = doc->getObjectByRepr(para_repr);
    g_assert(SP_IS_FLOWPARA(para_object));

    Inkscape::XML::Node *text = xml_doc->createTextNode("");
    para_repr->appendChild(text);

    Inkscape::GC::release(root_repr);
    Inkscape::GC::release(region_repr);
    Inkscape::GC::release(para_repr);
    Inkscape::GC::release(rect_repr);

    ft_item->transform = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse();

    return ft_item;
}
static void
sp_gradient_selector_add_vector_clicked (GtkWidget */*w*/, SPGradientSelector *sel)
{
    SPDocument *doc = sp_gradient_vector_selector_get_document (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors));

    if (!doc)
        return;

    SPGradient *gr = sp_gradient_vector_selector_get_gradient( SP_GRADIENT_VECTOR_SELECTOR (sel->vectors));
    Inkscape::XML::Document *xml_doc = doc->getReprDoc();

    Inkscape::XML::Node *repr = NULL;

    if (gr) {
        repr = gr->getRepr()->duplicate(xml_doc);
    } else {
        repr = xml_doc->createElement("svg:linearGradient");
        Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop");
        stop->setAttribute("offset", "0");
        stop->setAttribute("style", "stop-color:#000;stop-opacity:1;");
        repr->appendChild(stop);
        Inkscape::GC::release(stop);
        stop = xml_doc->createElement("svg:stop");
        stop->setAttribute("offset", "1");
        stop->setAttribute("style", "stop-color:#fff;stop-opacity:1;");
        repr->appendChild(stop);
        Inkscape::GC::release(stop);
    }

    doc->getDefs()->getRepr()->addChild(repr, NULL);

    Glib::ustring old_id = gr->getId();

    gr = SP_GRADIENT(doc->getObjectByRepr(repr));

    // Rename the new gradients id to be similar to the cloned gradients
    rename_id(gr, old_id);

    sp_gradient_vector_selector_set_gradient( SP_GRADIENT_VECTOR_SELECTOR (sel->vectors), doc, gr);

    sel->selectGradientInTree(gr);

    Inkscape::GC::release(repr);
}
void
sp_selected_path_combine(SPDesktop *desktop)
{
    Inkscape::Selection *selection = sp_desktop_selection(desktop);
    SPDocument *doc = sp_desktop_document(desktop);
    
    if (g_slist_length((GSList *) selection->itemList()) < 1) {
        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to combine."));
        return;
    }

    desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Combining paths..."));
    // set "busy" cursor
    desktop->setWaitingCursor();

    GSList *items = g_slist_copy((GSList *) selection->itemList());

    items = sp_degroup_list (items); // descend into any groups in selection

    GSList *to_paths = NULL;
    for (GSList *i = items; i != NULL; i = i->next) {
        SPItem *item = (SPItem *) i->data;
        if (!SP_IS_PATH(item) && !SP_IS_GROUP(item))
            to_paths = g_slist_prepend(to_paths, item);
    }
    GSList *converted = NULL;
    bool did = sp_item_list_to_curves(to_paths, &items, &converted);
    g_slist_free(to_paths);
    for (GSList *i = converted; i != NULL; i = i->next)
        items = g_slist_prepend(items, doc->getObjectByRepr((Inkscape::XML::Node*)(i->data)));

    items = sp_degroup_list (items); // converting to path may have added more groups, descend again

    items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position);
    items = g_slist_reverse(items);

    // remember the position, id, transform and style of the topmost path, they will be assigned to the combined one
    gint position = 0;
    char const *id = NULL;
    char const *transform = NULL;
    char const *style = NULL;
    char const *path_effect = NULL;

    SPCurve* curve = NULL;
    SPItem *first = NULL;
    Inkscape::XML::Node *parent = NULL; 

    if (did) {
        selection->clear();
    }

    for (GSList *i = items; i != NULL; i = i->next) {  // going from top to bottom

        SPItem *item = (SPItem *) i->data;
        if (!SP_IS_PATH(item)) {
            continue;
        }

        if (!did) {
            selection->clear();
            did = true;
        }

        SPCurve *c = SP_PATH(item)->get_curve_for_edit();
        if (first == NULL) {  // this is the topmost path
            first = item;
            parent = first->getRepr()->parent();
            position = first->getRepr()->position();
            id = first->getRepr()->attribute("id");
            transform = first->getRepr()->attribute("transform");
            // FIXME: merge styles of combined objects instead of using the first one's style
            style = first->getRepr()->attribute("style");
            path_effect = first->getRepr()->attribute("inkscape:path-effect");
            //c->transform(item->transform);
            curve = c;
        } else {
            c->transform(item->getRelativeTransform(first));
            curve->append(c, false);
            c->unref();

            // reduce position only if the same parent
            if (item->getRepr()->parent() == parent) {
                position--;
            }
            // delete the object for real, so that its clones can take appropriate action
            item->deleteObject();
        }
    }

    g_slist_free(items);

    if (did) {
        first->deleteObject(false);
        // delete the topmost.

        Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
        Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");

        // restore id, transform, path effect, and style
        repr->setAttribute("id", id);
        if (transform) {
            repr->setAttribute("transform", transform);
        }
        repr->setAttribute("style", style);

        repr->setAttribute("inkscape:path-effect", path_effect);

        // set path data corresponding to new curve
        gchar *dstring = sp_svg_write_path(curve->get_pathvector());
        curve->unref();
        if (path_effect) {
            repr->setAttribute("inkscape:original-d", dstring);
        } else {
            repr->setAttribute("d", dstring);
        }
        g_free(dstring);

        // add the new group to the parent of the topmost
        parent->appendChild(repr);

        // move to the position of the topmost, reduced by the number of deleted items
        repr->setPosition(position > 0 ? position : 0);

        DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_SELECTION_COMBINE, 
                           _("Combine"));

        selection->set(repr);

        Inkscape::GC::release(repr);

    } else {
        sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No path(s)</b> to combine in the selection."));
    }

    desktop->clearWaitingCursor();
}
bool
sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select, bool skip_all_lpeitems)
{
    bool did = false;
    
    for (;
         items != NULL;
         items = items->next) {

        SPItem *item = SP_ITEM(items->data);
        SPDocument *document = item->document;

        if ( skip_all_lpeitems &&
             SP_IS_LPE_ITEM(item) && 
             !SP_IS_GROUP(item) ) // also convert objects in an SPGroup when skip_all_lpeitems is set.
        { 
            continue;
        }

        if (SP_IS_PATH(item) && !SP_SHAPE(item)->_curve_before_lpe) {
            // remove connector attributes
            if (item->getAttribute("inkscape:connector-type") != NULL) {
                item->removeAttribute("inkscape:connection-start");
                item->removeAttribute("inkscape:connection-end");
                item->removeAttribute("inkscape:connector-type");
                item->removeAttribute("inkscape:connector-curvature");
                did = true;
            }
            continue; // already a path, and no path effect
        }

        if (SP_IS_BOX3D(item)) {
            // convert 3D box to ordinary group of paths; replace the old element in 'selected' with the new group
            Inkscape::XML::Node *repr = box3d_convert_to_group(SP_BOX3D(item))->getRepr();
            
            if (repr) {
                *to_select = g_slist_prepend (*to_select, repr);
                did = true;
                *selected = g_slist_remove (*selected, item);
            }

            continue;
        }
        
        if (SP_IS_GROUP(item)) {
            SP_LPE_ITEM(item)->removeAllPathEffects(true);
            GSList *item_list = sp_item_group_item_list(SP_GROUP(item));
            
            GSList *item_to_select = NULL;
            GSList *item_selected = NULL;
            
            if (sp_item_list_to_curves(item_list, &item_selected, &item_to_select))
                did = true;

            g_slist_free(item_list);
            g_slist_free(item_to_select);
            g_slist_free(item_selected);

            continue;
        }

        Inkscape::XML::Node *repr = sp_selected_item_to_curved_repr(item, 0);
        if (!repr)
            continue;

        did = true;
        *selected = g_slist_remove (*selected, item);

        // remember the position of the item
        gint pos = item->getRepr()->position();
        // remember parent
        Inkscape::XML::Node *parent = item->getRepr()->parent();
        // remember id
        char const *id = item->getRepr()->attribute("id");
        // remember title
        gchar *title = item->title();
        // remember description
        gchar *desc = item->desc();

        // It's going to resurrect, so we delete without notifying listeners.
        item->deleteObject(false);

        // restore id
        repr->setAttribute("id", id);
        // add the new repr to the parent
        parent->appendChild(repr);
        SPObject* newObj = document->getObjectByRepr(repr);
        if (title && newObj) {
            newObj->setTitle(title);
            g_free(title);
        }
        if (desc && newObj) {
            newObj->setDesc(desc);
            g_free(desc);
        }

        // move to the saved position
        repr->setPosition(pos > 0 ? pos : 0);

        /* Buglet: We don't re-add the (new version of the) object to the selection of any other
         * desktops where it was previously selected. */
        *to_select = g_slist_prepend (*to_select, repr);
        Inkscape::GC::release(repr);
    }
    
    return did;
}
/**
 * This function will create a new tspan element with the same attributes as
 * the tref had and add the same text as a child.  The tref is replaced in the
 * tree with the new tspan.
 * The code is based partially on sp_use_unlink
 */
SPObject *
sp_tref_convert_to_tspan(SPObject *obj)
{
    SPObject * new_tspan = NULL;

    ////////////////////
    // BASE CASE
    ////////////////////
    if (SP_IS_TREF(obj)) {

        SPTRef *tref = SP_TREF(obj);

        if (tref && tref->stringChild) {
            Inkscape::XML::Node *tref_repr = tref->getRepr();
            Inkscape::XML::Node *tref_parent = tref_repr->parent();

            SPDocument *document = tref->document;
            Inkscape::XML::Document *xml_doc = document->getReprDoc();

            Inkscape::XML::Node *new_tspan_repr = xml_doc->createElement("svg:tspan");

            // Add the new tspan element just after the current tref
            tref_parent->addChild(new_tspan_repr, tref_repr);
            Inkscape::GC::release(new_tspan_repr);

            new_tspan = document->getObjectByRepr(new_tspan_repr);

            // Create a new string child for the tspan
            Inkscape::XML::Node *new_string_repr = tref->stringChild->getRepr()->duplicate(xml_doc);
            new_tspan_repr->addChild(new_string_repr, NULL);

            //SPObject * new_string_child = document->getObjectByRepr(new_string_repr);

            // Merge style from the tref
            SPStyle *new_tspan_sty = new_tspan->style;
            SPStyle const *tref_sty = tref->style;
            sp_style_merge_from_dying_parent(new_tspan_sty, tref_sty);
            sp_style_merge_from_parent(new_tspan_sty, new_tspan->parent->style);


            new_tspan->updateRepr();

            // Hold onto our SPObject and repr for now.
            sp_object_ref(tref, NULL);
            Inkscape::GC::anchor(tref_repr);

            // Remove ourselves, not propagating delete events to avoid a
            // chain-reaction with other elements that might reference us.
            tref->deleteObject(false);

            // Give the copy our old id and let go of our old repr.
            new_tspan_repr->setAttribute("id", tref_repr->attribute("id"));
            Inkscape::GC::release(tref_repr);

            // Establish the succession and let go of our object.
            tref->setSuccessor(new_tspan);
            sp_object_unref(tref, NULL);
        }
    }
    ////////////////////
    // RECURSIVE CASE
    ////////////////////
    else {
        GSList *l = NULL;
        for (SPObject *child = obj->firstChild() ; child != NULL ; child = child->getNext() ) {
            sp_object_ref(child, obj);
            l = g_slist_prepend (l, child);
        }
        l = g_slist_reverse (l);
        while (l) {
            SPObject *child = reinterpret_cast<SPObject *>(l->data); // We just built this list, so cast is safe.
            l = g_slist_remove (l, child);

            // Note that there may be more than one conversion happening here, so if it's not a
            // tref being passed into this function, the returned value can't be specifically known
            new_tspan = sp_tref_convert_to_tspan(child);

            sp_object_unref(child, obj);
        }
    }

    return new_tspan;
}
Exemple #6
0
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;
}
void
sp_selected_path_combine(SPDesktop *desktop)
{
    Inkscape::Selection *selection = desktop->getSelection();
    SPDocument *doc = desktop->getDocument();

    std::vector<SPItem*> items(selection->itemList());
    
    if (items.size() < 1) {
        desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to combine."));
        return;
    }

    desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Combining paths..."));
    // set "busy" cursor
    desktop->setWaitingCursor();

    items = sp_degroup_list (items); // descend into any groups in selection

    std::vector<SPItem*> to_paths;
    for (std::vector<SPItem*>::const_reverse_iterator i = items.rbegin(); i != items.rend(); ++i) {
        if (!dynamic_cast<SPPath *>(*i) && !dynamic_cast<SPGroup *>(*i)) {
            to_paths.push_back(*i);
        }
    }
    std::vector<Inkscape::XML::Node*> converted;
    bool did = sp_item_list_to_curves(to_paths, items, converted);
    for (std::vector<Inkscape::XML::Node*>::const_iterator i = converted.begin(); i != converted.end(); ++i)
        items.push_back((SPItem*)doc->getObjectByRepr(*i));

    items = sp_degroup_list (items); // converting to path may have added more groups, descend again

    sort(items.begin(),items.end(),less_than_items);
    assert(!items.empty()); // 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;
    char const *id = NULL;
    char const *transform = NULL;
    char const *style = NULL;
    char const *path_effect = NULL;

    SPCurve* curve = NULL;
    SPItem *first = NULL;
    Inkscape::XML::Node *parent = NULL; 

    if (did) {
        selection->clear();
    }

    for (std::vector<SPItem*>::const_reverse_iterator i = items.rbegin(); i != items.rend(); ++i){

        SPItem *item = *i;
        SPPath *path = dynamic_cast<SPPath *>(item);
        if (!path) {
            continue;
        }

        if (!did) {
            selection->clear();
            did = true;
        }

        SPCurve *c = path->get_curve_for_edit();
        if (first == NULL) {  // this is the topmost path
            first = item;
            parent = first->getRepr()->parent();
            position = first->getRepr()->position();
            id = first->getRepr()->attribute("id");
            transform = first->getRepr()->attribute("transform");
            // FIXME: merge styles of combined objects instead of using the first one's style
            style = first->getRepr()->attribute("style");
            path_effect = first->getRepr()->attribute("inkscape:path-effect");
            //c->transform(item->transform);
            curve = c;
        } else {
            c->transform(item->getRelativeTransform(first));
            curve->append(c, false);
            c->unref();

            // reduce position only if the same parent
            if (item->getRepr()->parent() == parent) {
                position--;
            }
            // delete the object for real, so that its clones can take appropriate action
            item->deleteObject();
        }
    }


    if (did) {
        first->deleteObject(false);
        // delete the topmost.

        Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
        Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");

        // restore id, transform, path effect, and style
        repr->setAttribute("id", id);
        if (transform) {
            repr->setAttribute("transform", transform);
        }
        repr->setAttribute("style", style);

        repr->setAttribute("inkscape:path-effect", path_effect);

        // set path data corresponding to new curve
        gchar *dstring = sp_svg_write_path(curve->get_pathvector());
        curve->unref();
        if (path_effect) {
            repr->setAttribute("inkscape:original-d", dstring);
        } else {
            repr->setAttribute("d", dstring);
        }
        g_free(dstring);

        // add the new group to the parent of the topmost
        parent->appendChild(repr);

        // move to the position of the topmost, reduced by the number of deleted items
        repr->setPosition(position > 0 ? position : 0);

        DocumentUndo::done(desktop->getDocument(), SP_VERB_SELECTION_COMBINE, 
                           _("Combine"));

        selection->set(repr);

        Inkscape::GC::release(repr);

    } else {
        desktop->getMessageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No path(s)</b> to combine in the selection."));
    }

    desktop->clearWaitingCursor();
}
bool
sp_item_list_to_curves(const std::vector<SPItem*> &items, std::vector<SPItem*>& selected, std::vector<Inkscape::XML::Node*> &to_select, bool skip_all_lpeitems)
{
    bool did = false;
    for (std::vector<SPItem*>::const_iterator i = items.begin(); i != items.end(); ++i){
        SPItem *item = *i;
        g_assert(item != NULL);
        SPDocument *document = item->document;

        SPGroup *group = dynamic_cast<SPGroup *>(item);
        if ( skip_all_lpeitems &&
             dynamic_cast<SPLPEItem *>(item) && 
             !group ) // also convert objects in an SPGroup when skip_all_lpeitems is set.
        { 
            continue;
        }

        SPPath *path = dynamic_cast<SPPath *>(item);
        if (path && !path->_curve_before_lpe) {
            // remove connector attributes
            if (item->getAttribute("inkscape:connector-type") != NULL) {
                item->removeAttribute("inkscape:connection-start");
                item->removeAttribute("inkscape:connection-end");
                item->removeAttribute("inkscape:connector-type");
                item->removeAttribute("inkscape:connector-curvature");
                did = true;
            }
            continue; // already a path, and no path effect
        }

        SPBox3D *box = dynamic_cast<SPBox3D *>(item);
        if (box) {
            // convert 3D box to ordinary group of paths; replace the old element in 'selected' with the new group
            Inkscape::XML::Node *repr = box3d_convert_to_group(box)->getRepr();
            
            if (repr) {
                to_select.insert(to_select.begin(),repr);
                did = true;
                selected.erase(remove(selected.begin(), selected.end(), item), selected.end());
            }

            continue;
        }
        
        if (group) {
            group->removeAllPathEffects(true);
            std::vector<SPItem*> item_list = sp_item_group_item_list(group);
            
            std::vector<Inkscape::XML::Node*> item_to_select;
            std::vector<SPItem*> item_selected;
            
            if (sp_item_list_to_curves(item_list, item_selected, item_to_select))
                did = true;


            continue;
        }

        Inkscape::XML::Node *repr = sp_selected_item_to_curved_repr(item, 0);
        if (!repr)
            continue;

        did = true;
        selected.erase(remove(selected.begin(), selected.end(), item), selected.end());

        // remember the position of the item
        gint pos = item->getRepr()->position();
        // remember parent
        Inkscape::XML::Node *parent = item->getRepr()->parent();
        // remember id
        char const *id = item->getRepr()->attribute("id");
        // remember title
        gchar *title = item->title();
        // remember description
        gchar *desc = item->desc();
        // remember highlight color
        guint32 highlight_color = 0;
        if (item->isHighlightSet())
            highlight_color = item->highlight_color();

        // It's going to resurrect, so we delete without notifying listeners.
        item->deleteObject(false);

        // restore id
        repr->setAttribute("id", id);
        // add the new repr to the parent
        parent->appendChild(repr);
        SPObject* newObj = document->getObjectByRepr(repr);
        if (title && newObj) {
            newObj->setTitle(title);
            g_free(title);
        }
        if (desc && newObj) {
            newObj->setDesc(desc);
            g_free(desc);
        }
        if (highlight_color && newObj) {
                SP_ITEM(newObj)->setHighlightColor( highlight_color );
        }

        // move to the saved position
        repr->setPosition(pos > 0 ? pos : 0);

        /* Buglet: We don't re-add the (new version of the) object to the selection of any other
         * desktops where it was previously selected. */
        to_select.insert(to_select.begin(),repr);
        Inkscape::GC::release(repr);
    }
    
    return did;
}
Exemple #9
0
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"));
}
Exemple #10
0
void
text_flow_into_shape()
{
    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);

    SPItem *text = text_or_flowtext_in_selection(selection);
    SPItem *shape = shape_in_selection(selection);

    if (!text || !shape || g_slist_length((GSList *) selection->itemList()) < 2) {
        sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text</b> and one or more <b>paths or shapes</b> to flow text into frame."));
        return;
    }

    if (SP_IS_TEXT(text)) {
      // 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);
    }

    Inkscape::XML::Node *root_repr = xml_doc->createElement("svg:flowRoot");
    root_repr->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create
    root_repr->setAttribute("style", SP_OBJECT_REPR(text)->attribute("style")); // fixme: transfer style attrs too
    SP_OBJECT_REPR(SP_OBJECT_PARENT(shape))->appendChild(root_repr);
    SPObject *root_object = doc->getObjectByRepr(root_repr);
    g_return_if_fail(SP_IS_FLOWTEXT(root_object));

    Inkscape::XML::Node *region_repr = xml_doc->createElement("svg:flowRegion");
    root_repr->appendChild(region_repr);
    SPObject *object = doc->getObjectByRepr(region_repr);
    g_return_if_fail(SP_IS_FLOWREGION(object));

    /* Add clones */
    for (GSList *items = (GSList *) selection->itemList();
         items != NULL;
         items = items->next) {
        SPItem *item = SP_ITEM(items->data);
        if (SP_IS_SHAPE(item)){
            Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
            clone->setAttribute("x", "0");
            clone->setAttribute("y", "0");
            clone->setAttribute("xlink:href", g_strdup_printf("#%s", SP_OBJECT_REPR(item)->attribute("id")));

            // add the new clone to the region
            region_repr->appendChild(clone);
        }
    }

    if (SP_IS_TEXT(text)) { // flow from text, as string
        Inkscape::XML::Node *para_repr = xml_doc->createElement("svg:flowPara");
        root_repr->appendChild(para_repr);
        object = doc->getObjectByRepr(para_repr);
        g_return_if_fail(SP_IS_FLOWPARA(object));

        Inkscape::Text::Layout const *layout = te_get_layout(text);
        Glib::ustring text_ustring = sp_te_get_string_multiline(text, layout->begin(), layout->end());

        Inkscape::XML::Node *text_repr = xml_doc->createTextNode(text_ustring.c_str()); // FIXME: transfer all formatting! and convert newlines into flowParas!
        para_repr->appendChild(text_repr);

        Inkscape::GC::release(para_repr);
        Inkscape::GC::release(text_repr);

    } else { // reflow an already flowed text, preserving paras
        for (SPObject *o = SP_OBJECT(text)->children; o != NULL; o = o->next) {
            if (SP_IS_FLOWPARA(o)) {
                Inkscape::XML::Node *para_repr = SP_OBJECT_REPR(o)->duplicate(xml_doc);
                root_repr->appendChild(para_repr);
                object = doc->getObjectByRepr(para_repr);
                g_return_if_fail(SP_IS_FLOWPARA(object));
                Inkscape::GC::release(para_repr);
            }
        }
    }

    SP_OBJECT(text)->deleteObject (true);

    sp_document_done(doc, SP_VERB_CONTEXT_TEXT,
                     _("Flow text into shape"));

    sp_desktop_selection(desktop)->set(SP_ITEM(root_object));

    Inkscape::GC::release(root_repr);
    Inkscape::GC::release(region_repr);
}
Exemple #11
0
/**
 *  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);

}
Exemple #12
0
void
sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done)
{
    g_return_if_fail (group != NULL);
    g_return_if_fail (SP_IS_GROUP (group));

    SPDocument *doc = group->document;
    SPRoot *root = doc->getRoot();
    SPObject *defs = root->defs;

    SPItem *gitem = group;
    Inkscape::XML::Node *grepr = gitem->getRepr();

    g_return_if_fail (!strcmp (grepr->name(), "svg:g") || !strcmp (grepr->name(), "svg:a") || !strcmp (grepr->name(), "svg:switch"));

    // this converts the gradient/pattern fill/stroke on the group, if any, to userSpaceOnUse
    gitem->adjust_paint_recursive (Geom::identity(), Geom::identity(), false);

    SPItem *pitem = SP_ITEM(gitem->parent);
    Inkscape::XML::Node *prepr = pitem->getRepr();

	if (SP_IS_BOX3D(gitem)) {
		group = box3d_convert_to_group(SP_BOX3D(gitem));
		gitem = group;
	}

	sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(group), false);

    /* Step 1 - generate lists of children objects */
    GSList *items = NULL;
    GSList *objects = NULL;
    for (SPObject *child = group->firstChild() ; child; child = child->getNext() ) {

        if (SP_IS_ITEM (child)) {

            SPItem *citem = SP_ITEM (child);

            /* Merging of style */
            // this converts the gradient/pattern fill/stroke, if any, to userSpaceOnUse; we need to do
            // it here _before_ the new transform is set, so as to use the pre-transform bbox
            citem->adjust_paint_recursive (Geom::identity(), Geom::identity(), false);

            sp_style_merge_from_dying_parent(child->style, gitem->style);
            /*
             * fixme: We currently make no allowance for the case where child is cloned
             * and the group has any style settings.
             *
             * (This should never occur with documents created solely with the current
             * version of inkscape without using the XML editor: we usually apply group
             * style changes to children rather than to the group itself.)
             *
             * If the group has no style settings, then
             * sp_style_merge_from_dying_parent should be a no-op.  Otherwise (i.e. if
             * we change the child's style to compensate for its parent going away)
             * then those changes will typically be reflected in any clones of child,
             * whereas we'd prefer for Ungroup not to affect the visual appearance.
             *
             * The only way of preserving styling appearance in general is for child to
             * be put into a new group -- a somewhat surprising response to an Ungroup
             * command.  We could add a new groupmode:transparent that would mostly
             * hide the existence of such groups from the user (i.e. editing behaves as
             * if the transparent group's children weren't in a group), though that's
             * extra complication & maintenance burden and this case is rare.
             */

            child->updateRepr();

            Inkscape::XML::Node *nrepr = child->getRepr()->duplicate(prepr->document());

            // Merging transform
            Geom::Affine ctrans;
            Geom::Affine const g(gitem->transform);
            if (SP_IS_USE(citem) && sp_use_get_original (SP_USE(citem)) &&
                sp_use_get_original( SP_USE(citem) )->parent == SP_OBJECT(group)) {
                // make sure a clone's effective transform is the same as was under group
                ctrans = g.inverse() * citem->transform * g;
            } else {
                // We should not apply the group's transformation to both a linked offset AND to its source
                if (SP_IS_OFFSET(citem)) { // Do we have an offset at hand (whether it's dynamic or linked)?
                    SPItem *source = sp_offset_get_source(SP_OFFSET(citem));
                    // When dealing with a chain of linked offsets, the transformation of an offset will be
                    // tied to the transformation of the top-most source, not to any of the intermediate
                    // offsets. So let's find the top-most source
                    while (source != NULL && SP_IS_OFFSET(source)) {
                        source = sp_offset_get_source(SP_OFFSET(source));
                    }
                    if (source != NULL && // If true then we must be dealing with a linked offset ...
                        group->isAncestorOf(source) == false) { // ... of which the source is not in the same group
                        ctrans = citem->transform * g; // then we should apply the transformation of the group to the offset
                    } else {
                        ctrans = citem->transform;
                    }
                } else {
                    ctrans = citem->transform * g;
                }
            }

            // FIXME: constructing a transform that would fully preserve the appearance of a
            // textpath if it is ungrouped with its path seems to be impossible in general
            // case. E.g. if the group was squeezed, to keep the ungrouped textpath squeezed
            // as well, we'll need to relink it to some "virtual" path which is inversely
            // stretched relative to the actual path, and then squeeze the textpath back so it
            // would both fit the actual path _and_ be squeezed as before. It's a bummer.

            // This is just a way to temporarily remember the transform in repr. When repr is
            // reattached outside of the group, the transform will be written more properly
            // (i.e. optimized into the object if the corresponding preference is set)
            gchar *affinestr=sp_svg_transform_write(ctrans);
            nrepr->setAttribute("transform", affinestr);
            g_free(affinestr);

            items = g_slist_prepend (items, nrepr);

        } else {
            Inkscape::XML::Node *nrepr = child->getRepr()->duplicate(prepr->document());
            objects = g_slist_prepend (objects, nrepr);
        }
    }

    /* Step 2 - clear group */
    // remember the position of the group
    gint pos = group->getRepr()->position();

    // the group is leaving forever, no heir, clones should take note; its children however are going to reemerge
    group->deleteObject(true, false);

    /* Step 3 - add nonitems */
    if (objects) {
        Inkscape::XML::Node *last_def = defs->getRepr()->lastChild();
        while (objects) {
            Inkscape::XML::Node *repr = (Inkscape::XML::Node *) objects->data;
            if (!sp_repr_is_meta_element(repr)) {
                defs->getRepr()->addChild(repr, last_def);
            }
            Inkscape::GC::release(repr);
            objects = g_slist_remove (objects, objects->data);
        }
    }

    /* Step 4 - add items */
    while (items) {
        Inkscape::XML::Node *repr = (Inkscape::XML::Node *) items->data;
        // add item
        prepr->appendChild(repr);
        // restore position; since the items list was prepended (i.e. reverse), we now add
        // all children at the same pos, which inverts the order once again
        repr->setPosition(pos > 0 ? pos : 0);

        // fill in the children list if non-null
        SPItem *item = static_cast<SPItem *>(doc->getObjectByRepr(repr));

        item->doWriteTransform(repr, item->transform, NULL, false);

        Inkscape::GC::release(repr);
        if (children && SP_IS_ITEM (item))
            *children = g_slist_prepend (*children, item);

        items = g_slist_remove (items, items->data);
    }

    if (do_done) {
        DocumentUndo::done(doc, SP_VERB_NONE, _("Ungroup"));
    }
}
Exemple #13
0
static bool sp_spray_recursive(SPDesktop *desktop,
                               Inkscape::Selection *selection,
                               SPItem *item,
                               Geom::Point p,
                               Geom::Point /*vector*/,
                               gint mode,
                               double radius,
                               double /*force*/,
                               double population,
                               double &scale,
                               double scale_variation,
                               bool /*reverse*/,
                               double mean,
                               double standard_deviation,
                               double ratio,
                               double tilt,
                               double rotation_variation,
                               gint _distrib)
{
    bool did = false;
    
    if (SP_IS_BOX3D(item) ) {
        // convert 3D boxes to ordinary groups before spraying their shapes
        item = box3d_convert_to_group(SP_BOX3D(item));
        selection->add(item);
    }

    double _fid = g_random_double_range(0, 1);
    double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI );
    double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 );
    double dr; double dp;
    random_position( dr, dp, mean, standard_deviation, _distrib );
    dr=dr*radius;

    if (mode == SPRAY_MODE_COPY) {
        Geom::OptRect a = item->documentVisualBounds();
        if (a) {
            SPItem *item_copied;
            if(_fid <= population)
            {
                // duplicate
                SPDocument *doc = item->document;
                Inkscape::XML::Document* xml_doc = doc->getReprDoc();
                Inkscape::XML::Node *old_repr = item->getRepr();
                Inkscape::XML::Node *parent = old_repr->parent();
                Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
                parent->appendChild(copy);

                SPObject *new_obj = doc->getObjectByRepr(copy);
                item_copied = (SPItem *) new_obj;   //convertion object->item
                Geom::Point center=item->getCenter();
                sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(_scale,_scale));
                sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(scale,scale));

                sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
                //Move the cursor p
                Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio), -sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
                sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
                did = true;
            }
        }
    } else if (mode == SPRAY_MODE_SINGLE_PATH) {

        SPItem *father = NULL;         //initial Object
        SPItem *item_copied = NULL;    //Projected Object
        SPItem *unionResult = NULL;    //previous union
        SPItem *son = NULL;            //father copy

        int i=1;
        for (GSList *items = g_slist_copy((GSList *) selection->itemList());
                items != NULL;
                items = items->next) {

            SPItem *item1 = (SPItem *) items->data;
            if (i == 1) {
                father = item1;
            }
            if (i == 2) {
                unionResult = item1;
            }
            i++;
        }
        SPDocument *doc = father->document;
        Inkscape::XML::Document* xml_doc = doc->getReprDoc();
        Inkscape::XML::Node *old_repr = father->getRepr();
        Inkscape::XML::Node *parent = old_repr->parent();

        Geom::OptRect a = father->documentVisualBounds();
        if (a) {
            if (i == 2) {
                Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
                parent->appendChild(copy1);
                SPObject *new_obj1 = doc->getObjectByRepr(copy1);
                son = (SPItem *) new_obj1;   // conversion object->item
                unionResult = son;
                Inkscape::GC::release(copy1);
            }

            if (_fid <= population) { // Rules the population of objects sprayed
                // duplicates the father
                Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
                parent->appendChild(copy2);
                SPObject *new_obj2 = doc->getObjectByRepr(copy2);
                item_copied = (SPItem *) new_obj2;

                // Move around the cursor
                Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio), -sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint()); 

                Geom::Point center=father->getCenter();
                sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(_scale, _scale));
                sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(scale, scale));
                sp_spray_rotate_rel(center, desktop, item_copied, Geom::Rotate(angle));
                sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));

                // union and duplication
                selection->clear();
                selection->add(item_copied);
                selection->add(unionResult);
                sp_selected_path_union_skip_undo(selection->desktop());
                selection->add(father);
                Inkscape::GC::release(copy2);
                did = true;
            }
        }
    } else if (mode == SPRAY_MODE_CLONE) {
        Geom::OptRect a = item->documentVisualBounds();
        if (a) {
            if(_fid <= population) {
                SPItem *item_copied;
                SPDocument *doc = item->document;
                Inkscape::XML::Document* xml_doc = doc->getReprDoc();
                Inkscape::XML::Node *old_repr = item->getRepr();
                Inkscape::XML::Node *parent = old_repr->parent();

                // Creation of the clone
                Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
                // Ad the clone to the list of the father's sons
                parent->appendChild(clone);
                // Generates the link between father and son attributes
                clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false); 

                SPObject *clone_object = doc->getObjectByRepr(clone);
                // conversion object->item
                item_copied = (SPItem *) clone_object;
                Geom::Point center = item->getCenter();
                sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(_scale, _scale));
                sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(scale, scale));
                sp_spray_rotate_rel(center, desktop, item_copied, Geom::Rotate(angle));
                Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio), -sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
                sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));

                Inkscape::GC::release(clone);

                did = true;
            }
        }
    }

    return did;
}