svgio/inc/svgstyleattributes.hxx | 12 ++++++++ svgio/qa/cppunit/SvgImportTest.cxx | 8 +++++ svgio/qa/cppunit/data/RTLtext.svg | 7 +++++ svgio/source/svgreader/svgcharacternode.cxx | 3 +- svgio/source/svgreader/svgstyleattributes.cxx | 35 +++++++++++++++++++++++++- 5 files changed, 63 insertions(+), 2 deletions(-)
New commits: commit ae88cf4bc2b69282509128663f54e8c836e15c01 Author: Xisco Fauli <xiscofa...@libreoffice.org> AuthorDate: Mon Sep 15 16:53:37 2025 +0200 Commit: Adolfo Jayme Barrientos <fit...@ubuntu.com> CommitDate: Wed Sep 17 16:46:53 2025 +0200 tdf#168421: support RTL text direction in svg Change-Id: Iba8853d131bf88be3cc94003a73ffe545cf62004 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190978 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> (cherry picked from commit 4efae74abc8e7cefe2d7d0c3c55653dc269b8727) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191004 Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com> diff --git a/svgio/inc/svgstyleattributes.hxx b/svgio/inc/svgstyleattributes.hxx index 9a9eccb55105..52c08d2ad4c6 100644 --- a/svgio/inc/svgstyleattributes.hxx +++ b/svgio/inc/svgstyleattributes.hxx @@ -115,6 +115,13 @@ namespace svgio::svgreader lighter, }; + enum class FontDirection + { + notset, + LTR, + RTL, + }; + FontWeight getBolder(FontWeight aSource); FontWeight getLighter(FontWeight aSource); ::FontWeight getVclFontWeight(FontWeight aSource); @@ -209,6 +216,7 @@ namespace svgio::svgreader FontStretch maFontStretch; FontStyle maFontStyle; FontWeight maFontWeight; + FontDirection maFontDirection; TextAlign maTextAlign; TextDecoration maTextDecoration; TextAnchor maTextAnchor; @@ -416,6 +424,10 @@ namespace svgio::svgreader FontWeight getFontWeight() const; void setFontWeight(const FontWeight aFontWeight) { maFontWeight = aFontWeight; } + /// FontDirection content + FontDirection getFontDirection() const; + void setFontDirection(const FontDirection aFontDirection) { maFontDirection = aFontDirection; } + /// TextAlign content TextAlign getTextAlign() const; void setTextAlign(const TextAlign aTextAlign) { maTextAlign = aTextAlign; } diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index 529ac1e9381e..24eeb8f562a9 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -2126,6 +2126,14 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf149880) "/primitive2D/transform/mask/unhandled/mask/transform/transform/bitmap", 28); } +CPPUNIT_TEST_FIXTURE(Test, testRTLtext) +{ + xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/RTLtext.svg"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "text", u"داستان SVG 1.1 SE طولا ني است."); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "rtl", u"true"); +} + CPPUNIT_TEST_FIXTURE(Test, testCssClassRedefinition) { // Tests for svg css class redefinition behavior diff --git a/svgio/qa/cppunit/data/RTLtext.svg b/svgio/qa/cppunit/data/RTLtext.svg new file mode 100644 index 000000000000..f360fc559dc6 --- /dev/null +++ b/svgio/qa/cppunit/data/RTLtext.svg @@ -0,0 +1,7 @@ +<svg + viewBox="0 0 600 72" + xmlns="http://www.w3.org/2000/svg" + direction="rtl" + font-family="DejaVu Sans"> + <text x="300" y="50" text-anchor="middle">داستان SVG 1.1 SE طولا ني است.</text> +</svg> diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx index ca1f91ebb99a..e0ef204c4452 100644 --- a/svgio/source/svgreader/svgcharacternode.cxx +++ b/svgio/source/svgreader/svgcharacternode.cxx @@ -118,6 +118,7 @@ namespace svgio::svgreader const ::FontWeight nFontWeight(getVclFontWeight(rSvgStyleAttributes.getFontWeight())); bool bItalic(FontStyle::italic == rSvgStyleAttributes.getFontStyle() || FontStyle::oblique == rSvgStyleAttributes.getFontStyle()); + bool bRTL(FontDirection::RTL == rSvgStyleAttributes.getFontDirection()); return drawinglayer::attribute::FontAttribute( aFontFamily, @@ -128,7 +129,7 @@ namespace svgio::svgreader bItalic, false/*bMonospaced*/, false/*bOutline*/, - false/*bRTL*/, + bRTL, false/*bBiDiStrong*/); } diff --git a/svgio/source/svgreader/svgstyleattributes.cxx b/svgio/source/svgreader/svgstyleattributes.cxx index 219c0a20161e..6a9a20b56386 100644 --- a/svgio/source/svgreader/svgstyleattributes.cxx +++ b/svgio/source/svgreader/svgstyleattributes.cxx @@ -1391,6 +1391,7 @@ namespace svgio::svgreader maFontStretch(FontStretch::notset), maFontStyle(FontStyle::notset), maFontWeight(FontWeight::notset), + maFontDirection(FontDirection::notset), maTextAlign(TextAlign::notset), maTextDecoration(TextDecoration::notset), maTextAnchor(TextAnchor::notset), @@ -1401,7 +1402,7 @@ namespace svgio::svgreader maBaselineShift(BaselineShift::Baseline), maBaselineShiftNumber(0), maDominantBaseline(DominantBaseline::Auto), - maResolvingParent(34, 0), + maResolvingParent(35, 0), mbStrokeDasharraySet(false), mbUseFillFromContextFill(false), mbUseFillFromContextStroke(false), @@ -1840,6 +1841,17 @@ namespace svgio::svgreader } case SVGToken::Direction: { + if(!aContent.isEmpty()) + { + if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"ltr")) + { + setFontDirection(FontDirection::LTR); + } + else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"rtl")) + { + setFontDirection(FontDirection::RTL); + } + } break; } case SVGToken::LetterSpacing: @@ -2936,6 +2948,27 @@ namespace svgio::svgreader return FontWeight::N400; } + FontDirection SvgStyleAttributes::getFontDirection() const + { + if(maFontDirection != FontDirection::notset) + { + return maFontDirection; + } + + const SvgStyleAttributes* pSvgStyleAttributes = getCssStyleOrParentStyle(); + if (pSvgStyleAttributes && maResolvingParent[34] < nStyleDepthLimit) + { + ++maResolvingParent[34]; + auto ret = pSvgStyleAttributes->getFontDirection(); + --maResolvingParent[34]; + + return ret; + } + + // default is LTR + return FontDirection::LTR; + } + TextAlign SvgStyleAttributes::getTextAlign() const { if(maTextAlign != TextAlign::notset)