From ebc15c877cb20c6fd94d9da433a0605925ad8bf0 Mon Sep 17 00:00:00 2001 From: attila Date: Thu, 21 Aug 2025 20:46:27 +0200 Subject: [PATCH] Fix drawing some SVG text elements that contain tspan elements This commit mainly aims to fix some regressions caused by 20afcb5bf3fdf78a49d302f51b4ec6e940c73a40, but it also fixes cases that were rendered incorrectly even before. --- .../drawables/juce_SVGParser.cpp | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index 56ce6b2bbd..6ef3fa9f4a 100644 --- a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -1127,10 +1127,24 @@ private: return false; } + bool getPreviousTokenEndedWithSpace() const + { + return previousTokenEndedWithSpace; + } + + void setPreviousTokenEndedWithSpace (bool endedWithSpace) + { + previousTokenEndedWithSpace = endedWithSpace; + + if (parent != nullptr) + parent->setPreviousTokenEndedWithSpace (endedWithSpace); + } + private: StringLayoutState* parent = nullptr; Point nextStartingPos; Array xCoords, yCoords; + bool previousTokenEndedWithSpace = false; }; Drawable* parseText (const XmlPath& xml, bool shouldParseTransform, @@ -1163,11 +1177,46 @@ private: auto dc = new DrawableComposite(); setCommonAttributes (*dc, xml); - for (auto* e : xml->getChildIterator()) + const auto children = xml->getChildIterator(); + + for (auto childIt = children.begin(); childIt != children.end(); ++childIt) { + const auto firstChild = childIt == children.begin(); + const auto lastChild = std::next (childIt) == children.end(); + auto* e = *childIt; + if (e->isTextElement()) { - auto fullText = e->getText(); + auto fullText = e->getText().replace ("\r\n", " ").replace ("\n", " "); + + if (layoutState.getPreviousTokenEndedWithSpace()) + fullText = fullText.trimStart(); + + if (xml->hasTagName ("text")) + { + if (firstChild) + fullText = fullText.trimStart(); + else if (lastChild) + fullText = fullText.trimEnd(); + } + + const auto collapseSpaces = [] (const String& s) + { + auto tokens = StringArray::fromTokens (s, false); + tokens.removeEmptyStrings(); + auto collapsed = tokens.joinIntoString (" "); + + if (s.startsWithChar (' ')) + collapsed = " " + collapsed; + + if (s.endsWithChar (' ')) + collapsed += " "; + + return collapsed; + }; + + fullText = collapseSpaces (fullText); + layoutState.setPreviousTokenEndedWithSpace (fullText.endsWithChar (' ')); const auto subtextElements = [&] { @@ -1189,6 +1238,7 @@ private: auto dt = new DrawableText(); dc->addAndMakeVisible (dt); + dt->setPreserveWhitespace (true); dt->setText (text); dt->setFont (font, true);