diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index c32fa93df60f4a3dee2772936529fed01ad383e2..3437c4ec162639877317dcb12e2f9a1892439d5d 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -1604,30 +1604,69 @@ void SPItem::set_item_transform(Geom::Affine const &transform_matrix) //} -Geom::Affine i2anc_affine(SPObject const *object, SPObject const *const ancestor) { - Geom::Affine ret(Geom::identity()); - g_return_val_if_fail(object != nullptr, ret); +/** + * Get the item-to-ancestor transformation. Return false if the path crosses + * a non-SPItem (`ret` will contain the transformation up to that point). + * @param object Start item (must not be NULL) + * @param ancestor Stop item (can be NULL for item-to-doc transformation) + * @param[out] ret Item to ancestor transformation + * @pre `ret` is the identity matrix + * @return True if `object` and all its parents up to `ancestor` are of type SPItem + */ +static bool _i2anc_affine(SPObject const *object, SPObject const *const ancestor, Geom::Affine &ret) +{ + g_return_val_if_fail(object != nullptr, false); /* stop at first non-renderable ancestor */ - while ( object != ancestor && dynamic_cast(object) ) { + while (object != ancestor) { + SPItem const *item = dynamic_cast(object); + if (!item) { + // item not directly renderable, e.g. inside + return false; + } SPRoot const *root = dynamic_cast(object); if (root) { ret *= root->c2p; } else { - SPItem const *item = dynamic_cast(object); g_assert(item != nullptr); ret *= item->transform; } object = object->parent; } + return true; +} + +Geom::Affine i2anc_affine(SPObject const *object, SPObject const *const ancestor) +{ + Geom::Affine ret; + _i2anc_affine(object, ancestor, ret); return ret; } +/** + * Get the item-to-item transformation if both objects are in "item" space + * (they and their ancestors up to their common ancestor are all of type SPItem). + * If `src` has a non-item ancestor, then assume it will be rendered in the viewport + * of `dest` and simply return `src->i2doc_affine()`. Vice versa if `dest` has + * a non-item ancestor. + * If both are not in "item" space, the result is undefined. + */ Geom::Affine i2i_affine(SPObject const *src, SPObject const *dest) { g_return_val_if_fail(src != nullptr && dest != nullptr, Geom::identity()); SPObject const *ancestor = src->nearestCommonAncestor(dest); - return i2anc_affine(src, ancestor) * i2anc_affine(dest, ancestor).inverse(); + + Geom::Affine src_affine; + if (!_i2anc_affine(src, ancestor, src_affine)) { + return src_affine; + } + + Geom::Affine dest_affine; + if (!_i2anc_affine(dest, ancestor, dest_affine)) { + return dest_affine.inverse(); + } + + return src_affine * dest_affine.inverse(); } Geom::Affine SPItem::getRelativeTransform(SPObject const *dest) const { diff --git a/src/object/sp-text.cpp b/src/object/sp-text.cpp index 8d64ee1dfc81b026af1f4a3c9aadc8240b7b8099..4ab9f82b84a1fbfebedb215d0fa187285832b27d 100644 --- a/src/object/sp-text.cpp +++ b/src/object/sp-text.cpp @@ -530,7 +530,7 @@ void SPText::_buildLayoutInit() if ( curve ) { Path *temp = new Path; Path *padded = new Path; - temp->LoadPathVector( curve->get_pathvector(), shape->transform, true ); + temp->LoadPathVector(curve->get_pathvector(), shape->getRelativeTransform(this), true ); if( style->shape_padding.set ) { // std::cout << " padding: " << style->shape_padding.computed << std::endl; temp->OutsideOutline ( padded, style->shape_padding.computed, join_round, butt_straight, 20.0 ); @@ -782,7 +782,7 @@ Shape* SPText::_buildExclusionShape() const if ( curve ) { Path *temp = new Path; Path *margin = new Path; - temp->LoadPathVector( curve->get_pathvector(), shape->transform, true ); + temp->LoadPathVector(curve->get_pathvector(), shape->getRelativeTransform(this), true ); if( shape->style->shape_margin.set ) { temp->OutsideOutline ( margin, -shape->style->shape_margin.computed, join_round, butt_straight, 20.0 );