Requirements for dependent shapes framework
Open, Needs TriagePublic

Description

We have multiple discussions on IRC about dependent shapes system in Krita. I feel really uncomfortable discussing that basing on a mere SVG standard, without real-world applications to that. So I decided to start a list of requirements that we want to fulfill by implementing such dependencies.

Requirements

  1. Text on path feature
    1. The user should be able to edit the path using the standard shape-editing tool
    2. The user should be able to edit custom handles provided by the text shape itself:
      • anchors
      • text offset from start of path (startOffset)
      • offset from the curve (dy on <text>)
      • change text position: below/above the path (side)
      • [TODO] what else
  1. Text in shape feature
    1. The user should be able to resize the parent shape. When resizing happens, the internal text shape undergoes a relayout
    2. The user should be able to transform the parent shape. In this case the internal text of the shape is not relayouted, but just scaled/transformed (like all the shape decorations, like stroke/pattern).
    3. The user should be able to edit custom handles provided by the text shape itself:
      • anchors
      • offset from the sides of the shape (shape-padding)
      • [TODO] what else
  1. SVG2 specification supports "referenced objects" feature, which behaves effectively like "cloned" shapes.
    • [TODO] Do we actually want to support this feature on-the-fly? Will painters have enough usefulness in it? Right now our policy is bake/detach all the lazy links on loading.
  1. "Enter group" feature
    1. The user should be able to edit the shapes inside a group without ungrouping them
      1. Select "Enter group" mode
      2. All shapes except of the ones belonging to a group are grayed-out
      3. The user can select and edit shapes inside the group
      4. Clicking on any (grayed-out) shape outside the group exits the "enter group" mode.

Questions to answer

  1. Are there any other usecases of dependent shapes framework?
  2. Should we support SVG2's "referenced object" feature? Personally, I believe that we shouldn't (and, afair, DOM-model doesn't actually keep runtime links for such objects, it detaches all references on loading, as per specification; though my info might be outdated)

The user should be able to edit custom handles provided by the text shape itself:

Can this include changing the text orientation from inside the path to outside the path (for example text outside the circle and text inside the circle path)

dkazakov updated the task description. (Show Details)Aug 29 2023, 7:53 AM

Hi, @kamathraghavendra!

Can this include changing the text orientation from inside the path to outside the path (for example text outside the circle and text inside the circle path)

I've added this point to the text-on-path feature

woltherav added a comment.EditedAug 30 2023, 3:42 PM

I think technically speaking the clip-paths are also a situation where there might be shape-dependency, though that can be resolved quite easily. Beyond that it's stuff like patterns and markers, but there it is also a lot more self-evident that we should just resolve, as they're resources and therefore suitably contained.

The user should be able to transform the parent shape. In this case the internal text of the shape is not relayouted, but just scaled/transformed (like all the shape decorations, like stroke/pattern).

FWIW:

Right now, for text path we are required to only apply 'local transforms' on the textpath: https://svgwg.org/svg2-draft/text.html#TextPathElementHrefAttribute

I've been interpreting the text-in-shape in the same way, and I suspect Inkscape works similarly, based off this discussion: https://github.com/w3c/svgwg/issues/877 (That is, original shape is not transformed, but the text is, so only the text rotates.

Similarly, right now, if you transform a shape and set it as the shape-inside of a text, it will layout in the transformed shape. If you want to rotate both at once, you'd need to rotate a group that contains both.

BTW:

  1. Deevad uses inkscape to typeset pepper and carrot, so he prolly has the most real-world experience with SVG 2 text-in-shape.
  2. We were at one point considering to have a special KoShape for comic balloons, that would keep track of the text shape, balloon shape and the balloon tail, such a KoShape could be a container for both that keeps track of the transforms if this turns out to be that important.
alvinhochun updated the task description. (Show Details)Aug 30 2023, 7:06 PM

Also need to consider flowing text in multiple shapes, and shape-subtract.

Right now, for text path we are required to only apply 'local transforms' on the textpath

Well, that is how our tools currently work. They have two modes of transformation, so, theoretically, we should implement (and test) them somehow. How we bake that into SVG is a separate topic :)

Btw, a new set of weird technical questions:

  1. Can a text shape belong to multiple (ungrouped) vector shapes? Or they should always be grouped first?
  2. Can a text shape have both, text-on-path and text-in-shape at the same time?
  3. Can a single "balloon-like" shape has multiple text chunks manipulated separately? E.g. one in the top-left corner, another in the bottom-right corner. SVG allows to have it in a single <text> block, but our tools do not support that natively afair. This question is necessary to evaluate design decision that "the contour shape belongs to a text shape, hence there can be only one text shape "inside" the balloon".

Btw, a new set of weird technical questions:

  1. Can a text shape belong to multiple (ungrouped) vector shapes? Or they should always be grouped first?

A text shape can flow into multiple shapes, within SVG these are just linked, so both grouped and ungrouped is possible within SVG.

  1. Can a text shape have both, text-on-path and text-in-shape at the same time?

In terms of the data, yes, but in the layout, the text-in-shape or inline-size prevents text-in-path from being interpreted.

So...

<svg width="460px" height="270px"
    xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="2.0">
<defs>
<path id="bubble" d="M60 0C90 0 120 30 120 60C120 90 90 120 60 120C30 120 0 90 0 60C0 30 30 0 60 0Z" />
<path id="figure-of-eight" d="M50 0C75 0 100 25 100 50C100 100 0 100 0 150C0 175 25 200 50 200C75 200 100 175 100 150C100 100 0 100 0 50C0 25 25 0 50 0Z" />
</defs>

<text style="shape-inside: url(#bubble);" transform="translate(20, 20)">
    <textPath xlink:href="#figure-of-eight">The quick brown fox jumps over the lazy dog</textPath>
</text>
</svg>

Krita will load the above as only a text-in-shape, Firefox will load only the textPath, Inkscape is bugged because it loads both, the official pseudo algorithm is pretty clear about ignoring textPath when using shape-inside or inline-size.

  1. Can a single "balloon-like" shape has multiple text chunks manipulated separately? E.g. one in the top-left corner, another in the bottom-right corner. SVG allows to have it in a single <text> block, but our tools do not support that natively afair. This question is necessary to evaluate design decision that "the contour shape belongs to a text shape, hence there can be only one text shape "inside" the balloon".

No, wrapped text has a single anchor/alignment and does not allow for x, y, dx, dy and rotate to have effect (Basically, if text is soft-wrapping, it skips the unique SVG 1.1 features).

A text shape can flow into multiple shapes, within SVG these are just linked, so both grouped and ungrouped is possible within SVG.

Then the design of the shapes being owned by the text shape may become a bit weird in some corner cases. E.g. we might end up with a configuration like that:

  • shapeA
  • groupShapeB
    • shapeC
    • shapeD
  • textShapeE -> flows inside shapeA and shapeD

As far as I can tell this configuration is not possible to implement within the current design.

In terms of the data, yes, but in the layout, the text-in-shape or inline-size prevents text-in-path from being interpreted.

That makes things a bit easier got the text-on-path, because text-on-path can actually use the embed-the-path design.

No, wrapped text has a single anchor/alignment and does not allow for x, y, dx, dy and rotate to have effect (Basically, if text is soft-wrapping, it skips the unique SVG 1.1 features).

It means that we have a strict one-to-many relationship. One "text object" is related to one or more "contour objects". Parent-children/ownership is one example of such relationships, though it doesn't seem to fit properly, because it conflicts with the existing grouping relationship.

We need to think about that a bit more.

A text shape can flow into multiple shapes, within SVG these are just linked, so both grouped and ungrouped is possible within SVG.

Then the design of the shapes being owned by the text shape may become a bit weird in some corner cases. E.g. we might end up with a configuration like that:

  • shapeA
  • groupShapeB
    • shapeC
    • shapeD
  • textShapeE -> flows inside shapeA and shapeD

    As far as I can tell this configuration is not possible to implement within the current design.

So, basically, I had originally based the design on how the old artistic text shape 'owned' paths: https://invent.kde.org/graphics/krita/-/blob/f851b98745100a17ce2f3f680a8f11a15d46bacf/plugins/flake/artistictextshape/ArtisticTextShape.cpp#L577

It uses KoShape::addDependee to listen to changes on the linked shape. We could then, upon saving the textshape, check if the shape has a parent, and if not, save the shape to the <defs/>, and otherwise let that parent handle the saving (though, the text shape should still be told which ID it needs to save).

However, the big wrinkle with that is that the document cloning for background saving was implemented after the artistic text shape got removed, so the addDependee system is not taken into account (leading this link to break). This why @alvinhochun was originally looking into the ID system (and was also able to fix some bugs there earlier this month), because he was hoping to find a way to keep this link intact upon cloning.

I agree with the rest that you mentioned. For clarification we have 4 of these inform/ownership mechanisms:

  1. Parent-child.
  2. TextOnShapeContainer
  3. ShapeChangeListener.
  4. AddDependee (basically a shape change listener uniquely for KoShapes).