From 77b97b8e5c7ab80075bdfd95254ff3d609b6cdb2 Mon Sep 17 00:00:00 2001 From: Thomas Holder Date: Thu, 30 Apr 2020 08:51:15 +0200 Subject: [PATCH] Use shape-to-text transform with `shape-inside` For text with `shape-inside` layout ("Flow into Frame"), use `shape->getRelativeTransform(text)` instead of simply `shape->transform` when applying the curve. This means that text will flow exactly into the curve that's visible on screen, independent of groups and parent transformations. --- src/object/sp-item.cpp | 51 +++++++++++++++++++++++++++++++++++++----- src/object/sp-text.cpp | 4 ++-- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index c32fa93df6..3437c4ec16 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 8d64ee1dfc..4ab9f82b84 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 ); -- GitLab