BR: Transform matrix incorrectly applied to SVG text elements

When a text element in an SVG file has its own transform matrix, then the text is drawn by Drawable methods (draw, drawAt, drawWithin) in wrong positions. Here is a simple example to test:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="239.889mm" height="310.444mm"
 viewBox="0 0 680 880"
 xmlns="" xmlns:xlink=""  version="1.2" baseProfile="tiny">
<title>SVG TEST</title>
<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >

<g fill="#000000" fill-opacity="1" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(0.46, 0, 0, 0.46, 365.76, 157.378)
<text fill="#000000" fill-opacity="1" stroke="none" xml:space="preserve" x="162.797" y="19.5938" font-family="Arial" font-size="20" font-weight="400" font-style="normal" 
 >SVG FONT TEST</text>


When I remove the transform matrix from the SVG and use the transformation applied directly, then everything is displayed correctly:

AffineTransform transform (0.46, 0, 365.76, 0, 0.46, 157.378);
g.addTransform (transform);
drawable->drawAt (g, 0, 0, 1);

So I assume there is some bug in the SVG parser or how the transform matrix is applied when drawing Drawable. Before I always changed texts to curves to eliminate this kind of problems but right now for some reasons Iā€™d prefer to draw texts directly. Could you please look into this?

I did some debugging and apparently the problem is caused by applying the same transform matrix twice to text SVG elements. Fonts themselves are scaled once and have correct sizes but after that positions of text rectangles are transformed again, so: text position = initial position correctly translated + (scale * translation). This can be checked in juce_DrawableComposite.cpp, line 56 (JUCE 6.1.5):

Rectangle<float> DrawableComposite::getDrawableBounds() const
    Rectangle<float> r;

    for (auto* c : getChildren())
        if (auto* d = dynamic_cast<const Drawable*> (c))
            r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform())
                                               : d->getDrawableBounds());

    return r;

For text elements this iteration returns drawable bounds translated twice.

Thank you for reporting this. A fix for this issue is now out on develop

1 Like

Thanks! I am just testing my app after the fix and it seems everything is ok now.