/** * Get the number of dereferences or calls to get_original() needed to get an object * which is not an svg:use. Returns -1 if there is no original object. */ int SPUse::cloneDepth() const { unsigned depth = 1; SPItem *orig = this->child; while (orig && SP_IS_USE(orig)) { ++depth; orig = SP_USE(orig)->child; } if (!orig) return -1; return depth; }
/** * Returns the ultimate original of a SPUse (i.e. the first object in the chain of its originals * which is not an SPUse). If no original is found, NULL is returned (it is the responsibility * of the caller to make sure that this is handled correctly). * * Note that the returned is the clone object, i.e. the child of an SPUse (of the argument one for * the trivial case) and not the "true original". */ SPItem *SPUse::root() { SPItem *orig = this->child; while (orig && SP_IS_USE(orig)) { orig = SP_USE(orig)->child; } if (!orig) { return NULL; } return orig; }
/** * Returns the effective transform that goes from the ultimate original to given SPUse, both ends * included. */ Geom::Affine SPUse::get_root_transform() { //track the ultimate source of a chain of uses SPObject *orig = this->child; GSList *chain = NULL; chain = g_slist_prepend(chain, this); while (SP_IS_USE(orig)) { chain = g_slist_prepend(chain, orig); orig = SP_USE(orig)->child; } chain = g_slist_prepend(chain, orig); // calculate the accummulated transform, starting from the original Geom::Affine t(Geom::identity()); for (GSList *i = chain; i != NULL; i = i->next) { SPItem *i_tem = SP_ITEM(i->data); // "An additional transformation translate(x,y) is appended to the end (i.e., // right-side) of the transform attribute on the generated 'g', where x and y // represent the values of the x and y attributes on the 'use' element." - http://www.w3.org/TR/SVG11/struct.html#UseElement if (SP_IS_USE(i_tem)) { SPUse *i_use = SP_USE(i_tem); if ((i_use->x._set && i_use->x.computed != 0) || (i_use->y._set && i_use->y.computed != 0)) { t = t * Geom::Translate(i_use->x._set ? i_use->x.computed : 0, i_use->y._set ? i_use->y.computed : 0); } } t *= i_tem->transform; } g_slist_free(chain); return t; }
static void GetDest(SPObject* child,Shape **computed) { if ( child == NULL ) return; SPCurve *curve=NULL; Geom::Matrix tr_mat; SPObject* u_child=child; if ( SP_IS_USE(u_child) ) { u_child=SP_USE(u_child)->child; tr_mat = SP_ITEM(u_child)->getRelativeTransform(SP_OBJECT_PARENT(child)); } else { tr_mat = SP_ITEM(u_child)->transform; } if ( SP_IS_SHAPE (u_child) ) { curve = sp_shape_get_curve (SP_SHAPE (u_child)); } else if ( SP_IS_TEXT (u_child) ) { curve = SP_TEXT (u_child)->getNormalizedBpath (); } if ( curve ) { Path* temp=new Path; temp->LoadPathVector(curve->get_pathvector(), tr_mat, true); Shape* n_shp=new Shape; temp->Convert(0.25); temp->Fill(n_shp,0); Shape* uncross=new Shape; SPStyle* style=SP_OBJECT_STYLE(u_child); if ( style && style->fill_rule.computed == SP_WIND_RULE_EVENODD ) { uncross->ConvertToShape(n_shp,fill_oddEven); } else { uncross->ConvertToShape(n_shp,fill_nonZero); } UnionShape(computed, uncross); delete uncross; delete n_shp; delete temp; curve->unref(); } else { // printf("no curve\n"); } }
bool item_type_match (SPItem *item, GtkWidget *widget) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (SP_IS_RECT(item)) { return (type_checkbox (widget, "shapes") || type_checkbox (widget, "rects")); } else if (SP_IS_GENERICELLIPSE(item) || SP_IS_ELLIPSE(item) || SP_IS_ARC(item) || SP_IS_CIRCLE(item)) { return (type_checkbox (widget, "shapes") || type_checkbox (widget, "ellipses")); } else if (SP_IS_STAR(item) || SP_IS_POLYGON(item)) { return (type_checkbox (widget, "shapes") || type_checkbox (widget, "stars")); } else if (SP_IS_SPIRAL(item)) { return (type_checkbox (widget, "shapes") || type_checkbox (widget, "spirals")); } else if (SP_IS_PATH(item) || SP_IS_LINE(item) || SP_IS_POLYLINE(item)) { return (type_checkbox (widget, "paths")); } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_TREF(item) || SP_IS_STRING(item)) { return (type_checkbox (widget, "texts")); } else if (SP_IS_GROUP(item) && !desktop->isLayer(item) ) { // never select layers! return (type_checkbox (widget, "groups")); } else if (SP_IS_USE(item)) { return (type_checkbox (widget, "clones")); } else if (SP_IS_IMAGE(item)) { return (type_checkbox (widget, "images")); } else if (SP_IS_OFFSET(item)) { return (type_checkbox (widget, "offsets")); } return false; }
cairo_status_t SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/, unsigned long glyph, cairo_t *cr, cairo_text_extents_t */*metrics*/) { // This method does the actual rendering of glyphs. // We have glyphs.size() glyphs and possibly one missing-glyph declared on this SVG document // The id of the missing-glyph is always equal to glyphs.size() // All the other glyphs have ids ranging from 0 to glyphs.size()-1 if (glyph > this->glyphs.size()) return CAIRO_STATUS_SUCCESS;//TODO: this is an error! SPObject* node; if (glyph == this->glyphs.size()){ if (!this->missingglyph) return CAIRO_STATUS_SUCCESS; node = (SPObject*) this->missingglyph; } else { node = (SPObject*) this->glyphs[glyph]; } if (!SP_IS_GLYPH(node) && !SP_IS_MISSING_GLYPH(node)) { return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return? } SPFont* spfont = (SPFont*) node->parent; if (!spfont) { return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return? } //glyphs can be described by arbitrary SVG declared in the childnodes of a glyph node // or using the d attribute of a glyph node. // pathv stores the path description from the d attribute: Geom::PathVector pathv; if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) { pathv = sp_svg_read_pathv(((SPGlyph*)node)->d); pathv = flip_coordinate_system(spfont, pathv); this->render_glyph_path(cr, &pathv); } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) { pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d); pathv = flip_coordinate_system(spfont, pathv); this->render_glyph_path(cr, &pathv); } if (node->hasChildren()){ //render the SVG described on this glyph's child nodes. for(node = node->children; node; node=node->next){ if (SP_IS_PATH(node)){ pathv = ((SPShape*)node)->_curve->get_pathvector(); pathv = flip_coordinate_system(spfont, pathv); this->render_glyph_path(cr, &pathv); } if (SP_IS_OBJECTGROUP(node)){ g_warning("TODO: svgfonts: render OBJECTGROUP"); } if (SP_IS_USE(node)){ SPItem* item = SP_USE(node)->ref->getObject(); if (SP_IS_PATH(item)){ pathv = ((SPShape*)item)->_curve->get_pathvector(); pathv = flip_coordinate_system(spfont, pathv); this->render_glyph_path(cr, &pathv); } glyph_modified_connection = ((SPObject*) item)->connectModified(sigc::mem_fun(*this, &SvgFont::glyph_modified)); } } } return CAIRO_STATUS_SUCCESS; }
void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *selection) { GSList const *items = selection->itemList(); if (!items) { // no items _context.set(Inkscape::NORMAL_MESSAGE, _when_nothing); } else { SPItem *item = SP_ITEM(items->data); SPObject *layer = selection->desktop()->layerForObject(item); SPObject *root = selection->desktop()->currentRoot(); // Layer name gchar *layer_name; if (layer == root) { layer_name = g_strdup(_("root")); } else { char const *layer_label; bool is_label = false; if (layer->label()) { layer_label = layer->label(); is_label = true; } else { layer_label = layer->defaultLabel(); } char *quoted_layer_label = xml_quote_strdup(layer_label); if (is_label) { layer_name = g_strdup_printf(_("layer <b>%s</b>"), quoted_layer_label); } else { layer_name = g_strdup_printf(_("layer <b><i>%s</i></b>"), quoted_layer_label); } g_free(quoted_layer_label); } // Parent name SPObject *parent = item->parent; gchar const *parent_label = parent->getId(); char *quoted_parent_label = xml_quote_strdup(parent_label); gchar *parent_name = g_strdup_printf(_("<i>%s</i>"), quoted_parent_label); g_free(quoted_parent_label); gchar *in_phrase; guint num_layers = selection->numberOfLayers(); guint num_parents = selection->numberOfParents(); if (num_layers == 1) { if (num_parents == 1) { if (layer == parent) in_phrase = g_strdup_printf(_(" in %s"), layer_name); else in_phrase = g_strdup_printf(_(" in group %s (%s)"), parent_name, layer_name); } else { in_phrase = g_strdup_printf(ngettext(" in <b>%i</b> parents (%s)", " in <b>%i</b> parents (%s)", num_parents), num_parents, layer_name); } } else { in_phrase = g_strdup_printf(ngettext(" in <b>%i</b> layers", " in <b>%i</b> layers", num_layers), num_layers); } g_free (layer_name); g_free (parent_name); if (!items->next) { // one item char *item_desc = item->description(); if (SP_IS_USE(item) && SP_IS_SYMBOL(item->firstChild())) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, _("Convert symbol to group to edit"), _when_selected); } else if (SP_IS_USE(item) || (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref)) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, _("Use <b>Shift+D</b> to look up original"), _when_selected); } else if (SP_IS_TEXT_TEXTPATH(item)) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, _("Use <b>Shift+D</b> to look up path"), _when_selected); } else if (SP_IS_FLOWTEXT(item) && !SP_FLOWTEXT(item)->has_internal_frame()) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, _("Use <b>Shift+D</b> to look up frame"), _when_selected); } else { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s.", item_desc, in_phrase, _when_selected); } g_free(item_desc); } else { // multiple items int object_count = g_slist_length((GSList *)items); gchar *objects_str = NULL; GSList *terms = collect_terms ((GSList *)items); int n_terms = g_slist_length(terms); if (n_terms == 0) { objects_str = g_strdup_printf ( // this is only used with 2 or more objects ngettext("<b>%i</b> object selected", "<b>%i</b> objects selected", object_count), object_count); } else if (n_terms == 1) { objects_str = g_strdup_printf ( // this is only used with 2 or more objects ngettext("<b>%i</b> object of type <b>%s</b>", "<b>%i</b> objects of type <b>%s</b>", object_count), object_count, (gchar *) terms->data); } else if (n_terms == 2) { objects_str = g_strdup_printf ( // this is only used with 2 or more objects ngettext("<b>%i</b> object of types <b>%s</b>, <b>%s</b>", "<b>%i</b> objects of types <b>%s</b>, <b>%s</b>", object_count), object_count, (gchar *) terms->data, (gchar *) terms->next->data); } else if (n_terms == 3) { objects_str = g_strdup_printf ( // this is only used with 2 or more objects ngettext("<b>%i</b> object of types <b>%s</b>, <b>%s</b>, <b>%s</b>", "<b>%i</b> objects of types <b>%s</b>, <b>%s</b>, <b>%s</b>", object_count), object_count, (gchar *) terms->data, (gchar *) terms->next->data, (gchar *) terms->next->next->data); } else { objects_str = g_strdup_printf ( // this is only used with 2 or more objects ngettext("<b>%i</b> object of <b>%i</b> types", "<b>%i</b> objects of <b>%i</b> types", object_count), object_count, n_terms); } g_slist_free (terms); // indicate all, some, or none filtered gchar *filt_str = NULL; int n_filt = count_filtered((GSList *)items); //all filtered if (n_filt) { filt_str = g_strdup_printf(ngettext("; <i>%d filtered object</i> ", "; <i>%d filtered objects</i> ", n_filt), n_filt); } else { filt_str = g_strdup_printf("%s", ""); } _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s%s. %s.", objects_str, filt_str, in_phrase, _when_selected); if (objects_str) { g_free(objects_str); objects_str = 0; } if (filt_str) { g_free(filt_str); objects_str = 0; } } g_free(in_phrase); } }
/// @todo investigate why Geom::Point p is passed in but ignored. void Inkscape::ObjectSnapper::_collectPaths(Geom::Point /*p*/, SnapSourceType const source_type, bool const &first_point) const { // Now, let's first collect all paths to snap to. If we have a whole bunch of points to snap, // e.g. when translating an item using the selector tool, then we will only do this for the // first point and store the collection for later use. This significantly improves the performance if (first_point) { _clear_paths(); // Determine the type of bounding box we should snap to SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX; bool p_is_a_node = source_type & SNAPSOURCE_NODE_CATEGORY; bool p_is_a_bbox = source_type & SNAPSOURCE_BBOX_CATEGORY; bool p_is_other = (source_type & SNAPSOURCE_OTHERS_CATEGORY) || (source_type & SNAPSOURCE_DATUMS_CATEGORY); if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_EDGE)) { Preferences *prefs = Preferences::get(); int prefs_bbox = prefs->getBool("/tools/bounding_box", 0); bbox_type = !prefs_bbox ? SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX; } // Consider the page border for snapping if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PAGE_BORDER) && _snapmanager->snapprefs.isAnyCategorySnappable()) { Geom::PathVector *border_path = _getBorderPathv(); if (border_path != NULL) { _paths_to_snap_to->push_back(SnapCandidatePath(border_path, SNAPTARGET_PAGE_BORDER, Geom::OptRect())); } } for (std::vector<SnapCandidateItem>::const_iterator i = _candidates->begin(); i != _candidates->end(); ++i) { /* Transform the requested snap point to this item's coordinates */ Geom::Affine i2doc(Geom::identity()); SPItem *root_item = NULL; /* We might have a clone at hand, so make sure we get the root item */ if (SP_IS_USE((*i).item)) { i2doc = SP_USE((*i).item)->get_root_transform(); root_item = SP_USE((*i).item)->root(); g_return_if_fail(root_item); } else { i2doc = (*i).item->i2doc_affine(); root_item = (*i).item; } //Build a list of all paths considered for snapping to //Add the item's path to snap to if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH, SNAPTARGET_PATH_INTERSECTION, SNAPTARGET_TEXT_BASELINE)) { if (p_is_other || p_is_a_node || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_bbox)) { if (SP_IS_TEXT(root_item) || SP_IS_FLOWTEXT(root_item)) { if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_TEXT_BASELINE)) { // Snap to the text baseline Text::Layout const *layout = te_get_layout(static_cast<SPItem *>(root_item)); if (layout != NULL && layout->outputExists()) { Geom::PathVector *pv = new Geom::PathVector(); pv->push_back(layout->baseline() * root_item->i2dt_affine() * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt()); _paths_to_snap_to->push_back(SnapCandidatePath(pv, SNAPTARGET_TEXT_BASELINE, Geom::OptRect())); } } } else { // Snapping for example to a traced bitmap is very stressing for // the CPU, so we'll only snap to paths having no more than 500 nodes // This also leads to a lag of approx. 500 msec (in my lousy test set-up). bool very_complex_path = false; if (SP_IS_PATH(root_item)) { very_complex_path = SP_PATH(root_item)->nodesInPath() > 500; } if (!very_complex_path && root_item && _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH, SNAPTARGET_PATH_INTERSECTION)) { SPCurve *curve = NULL; if (SP_IS_SHAPE(root_item)) { curve = SP_SHAPE(root_item)->getCurve(); }/* else if (SP_IS_TEXT(root_item) || SP_IS_FLOWTEXT(root_item)) { curve = te_get_layout(root_item)->convertToCurves(); }*/ if (curve) { // We will get our own copy of the pathvector, which must be freed at some point // Geom::PathVector *pv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine); Geom::PathVector *pv = new Geom::PathVector(curve->get_pathvector()); (*pv) *= root_item->i2dt_affine() * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt(); // (_edit_transform * _i2d_transform); _paths_to_snap_to->push_back(SnapCandidatePath(pv, SNAPTARGET_PATH, Geom::OptRect())); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it. curve->unref(); } } } } } //Add the item's bounding box to snap to if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_EDGE)) { if (p_is_other || p_is_a_bbox || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node)) { // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox // of the item AND the bbox of the clipping path at the same time if (!(*i).clip_or_mask) { Geom::OptRect rect = root_item->bounds(bbox_type, i2doc); if (rect) { Geom::PathVector *path = _getPathvFromRect(*rect); rect = root_item->desktopBounds(bbox_type); _paths_to_snap_to->push_back(SnapCandidatePath(path, SNAPTARGET_BBOX_EDGE, rect)); } } } } } } }
void Inkscape::ObjectSnapper::_collectNodes(SnapSourceType const &t, bool const &first_point) const { // Now, let's first collect all points to snap to. If we have a whole bunch of points to snap, // e.g. when translating an item using the selector tool, then we will only do this for the // first point and store the collection for later use. This significantly improves the performance if (first_point) { _points_to_snap_to->clear(); // Determine the type of bounding box we should snap to SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX; bool p_is_a_node = t & SNAPSOURCE_NODE_CATEGORY; bool p_is_a_bbox = t & SNAPSOURCE_BBOX_CATEGORY; bool p_is_other = (t & SNAPSOURCE_OTHERS_CATEGORY) || (t & SNAPSOURCE_DATUMS_CATEGORY); // A point considered for snapping should be either a node, a bbox corner or a guide/other. Pick only ONE! if (((p_is_a_node && p_is_a_bbox) || (p_is_a_bbox && p_is_other) || (p_is_a_node && p_is_other))) { g_warning("Snap warning: node type is ambiguous"); } if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_CORNER, SNAPTARGET_BBOX_EDGE_MIDPOINT, SNAPTARGET_BBOX_MIDPOINT)) { Preferences *prefs = Preferences::get(); bool prefs_bbox = prefs->getBool("/tools/bounding_box"); bbox_type = !prefs_bbox ? SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX; } // Consider the page border for snapping to if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PAGE_CORNER)) { _getBorderNodes(_points_to_snap_to); } for (std::vector<SnapCandidateItem>::const_iterator i = _candidates->begin(); i != _candidates->end(); ++i) { //Geom::Affine i2doc(Geom::identity()); SPItem *root_item = (*i).item; if (SP_IS_USE((*i).item)) { root_item = SP_USE((*i).item)->root(); } g_return_if_fail(root_item); //Collect all nodes so we can snap to them if (p_is_a_node || p_is_other || (p_is_a_bbox && !_snapmanager->snapprefs.getStrictSnapping())) { // Note: there are two ways in which intersections are considered: // Method 1: Intersections are calculated for each shape individually, for both the // snap source and snap target (see sp_shape_snappoints) // Method 2: Intersections are calculated for each curve or line that we've snapped to, i.e. only for // the target (see the intersect() method in the SnappedCurve and SnappedLine classes) // Some differences: // - Method 1 doesn't find intersections within a set of multiple objects // - Method 2 only works for targets // When considering intersections as snap targets: // - Method 1 only works when snapping to nodes, whereas // - Method 2 only works when snapping to paths // - There will be performance differences too! // If both methods are being used simultaneously, then this might lead to duplicate targets! // Well, here we will be looking for snap TARGETS. Both methods can therefore be used. // When snapping to paths, we will get a collection of snapped lines and snapped curves. findBestSnap() will // go hunting for intersections (but only when asked to in the prefs of course). In that case we can just // temporarily block the intersections in sp_item_snappoints, we don't need duplicates. If we're not snapping to // paths though but only to item nodes then we should still look for the intersections in sp_item_snappoints() bool old_pref = _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH_INTERSECTION); if (_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_PATH)) { // So if we snap to paths, then findBestSnap will find the intersections // and therefore we temporarily disable SNAPTARGET_PATH_INTERSECTION, which will // avoid root_item->getSnappoints() below from returning intersections _snapmanager->snapprefs.setTargetSnappable(SNAPTARGET_PATH_INTERSECTION, false); } // We should not snap a transformation center to any of the centers of the items in the // current selection (see the comment in SelTrans::centerRequest()) bool old_pref2 = _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_ROTATION_CENTER); if (old_pref2) { for ( GSList const *itemlist = _snapmanager->getRotationCenterSource(); itemlist != NULL; itemlist = g_slist_next(itemlist) ) { if ((*i).item == reinterpret_cast<SPItem*>(itemlist->data)) { // don't snap to this item's rotation center _snapmanager->snapprefs.setTargetSnappable(SNAPTARGET_ROTATION_CENTER, false); break; } } } root_item->getSnappoints(*_points_to_snap_to, &_snapmanager->snapprefs); // restore the original snap preferences _snapmanager->snapprefs.setTargetSnappable(SNAPTARGET_PATH_INTERSECTION, old_pref); _snapmanager->snapprefs.setTargetSnappable(SNAPTARGET_ROTATION_CENTER, old_pref2); } //Collect the bounding box's corners so we can snap to them if (p_is_a_bbox || (!_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node) || p_is_other) { // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox // of the item AND the bbox of the clipping path at the same time if (!(*i).clip_or_mask) { Geom::OptRect b = root_item->desktopBounds(bbox_type); getBBoxPoints(b, _points_to_snap_to, true, _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_CORNER), _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_EDGE_MIDPOINT), _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_BBOX_MIDPOINT)); } } } } }