diff options
Diffstat (limited to 'svgio')
-rw-r--r-- | svgio/inc/svgcharacternode.hxx | 7 | ||||
-rw-r--r-- | svgio/qa/cppunit/SvgImportTest.cxx | 86 | ||||
-rw-r--r-- | svgio/qa/cppunit/data/tdf151103.svg | 18 | ||||
-rw-r--r-- | svgio/source/svgreader/svgcharacternode.cxx | 6 | ||||
-rw-r--r-- | svgio/source/svgreader/svgdocumenthandler.cxx | 91 |
5 files changed, 203 insertions, 5 deletions
diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx index 8861055f8e65..c74f881df468 100644 --- a/svgio/inc/svgcharacternode.hxx +++ b/svgio/inc/svgcharacternode.hxx @@ -127,6 +127,9 @@ namespace svgio::svgreader // keep a copy of string data before space handling OUString maTextBeforeSpaceHandling; + // The whole text line of which this SvgCharacterNode is parted of + OUString maWholeTextLine; + /// local helpers rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> createSimpleTextPrimitive( SvgTextPosition& rSvgTextPosition, @@ -153,6 +156,10 @@ namespace svgio::svgreader const OUString& getText() const { return maText; } const OUString& getTextBeforeSpaceHandling() const { return maTextBeforeSpaceHandling; } + + void setWholeTextLine(const OUString& rWholeTextLine) { maWholeTextLine = rWholeTextLine; } + + const OUString& getWholeTextLine() const { return maWholeTextLine; } }; } // end of namespace svgio::svgreader diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx index 24a05ea27075..80234e8b1f56 100644 --- a/svgio/qa/cppunit/SvgImportTest.cxx +++ b/svgio/qa/cppunit/SvgImportTest.cxx @@ -1458,6 +1458,92 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf95400) assertXPathNoAttribute(pDocument, "/primitive2D/transform/textsimpleportion[2]", "dx0"); } +CPPUNIT_TEST_FIXTURE(Test, testTextAnchor) +{ + Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/tdf151103.svg"); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(aSequence.getLength())); + + drawinglayer::Primitive2dXmlDump dumper; + xmlDocUniquePtr pDocument = dumper.dumpAndParse(Primitive2DContainer(aSequence)); + + CPPUNIT_ASSERT (pDocument); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "x", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "y", "40"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "x", "43"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "y", "50"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "x", "26"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "y", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", "x", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", "y", "40"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", "x", "43"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", "y", "50"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", "x", "26"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", "y", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]", "x", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]", "y", "40"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[7]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]", "x", "43"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]", "y", "50"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[8]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]", "x", "26"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]", "y", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[9]", "text", "ABC"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[10]", "x", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[10]", "y", "40"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[10]", "text", "A"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[11]", "x", "72"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[11]", "y", "40"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[11]", "text", "B"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[12]", "x", "83"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[12]", "y", "40"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[12]", "text", "C"); + + // Without the fix in place, this test would have failed with + // - Expected: 43 + // - Actual : 54 + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[13]", "x", "43"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[13]", "y", "50"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[13]", "text", "A"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[14]", "x", "55"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[14]", "y", "50"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[14]", "text", "B"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[15]", "x", "66"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[15]", "y", "50"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[15]", "text", "C"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[16]", "x", "26"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[16]", "y", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[16]", "text", "A"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[17]", "x", "38"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[17]", "y", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[17]", "text", "B"); + + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[18]", "x", "49"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[18]", "y", "60"); + assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[18]", "text", "C"); +} + CPPUNIT_TEST_FIXTURE(Test, testTdf156577) { Primitive2DSequence aSequence = parseSvg(u"/svgio/qa/cppunit/data/tdf156577.svg"); diff --git a/svgio/qa/cppunit/data/tdf151103.svg b/svgio/qa/cppunit/data/tdf151103.svg new file mode 100644 index 000000000000..664253f8df06 --- /dev/null +++ b/svgio/qa/cppunit/data/tdf151103.svg @@ -0,0 +1,18 @@ +<svg viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg"> + <text text-anchor="start" x="60" y="40">ABC</text> + <text text-anchor="middle" x="60" y="50">ABC</text> + <text text-anchor="end" x="60" y="60">ABC</text> + + <text><tspan text-anchor="start" x="60" y="40">ABC</tspan></text> + <text><tspan text-anchor="middle" x="60" y="50">ABC</tspan></text> + <text><tspan text-anchor="end" x="60" y="60">ABC</tspan></text> + + <text text-anchor="start" x="60" y="40"><tspan>ABC</tspan></text> + <text text-anchor="middle" x="60" y="50"><tspan>ABC</tspan></text> + <text text-anchor="end" x="60" y="60"><tspan>ABC</tspan></text> + + <text text-anchor="start" x="60" y="40">A<tspan>B</tspan>C</text> + <text text-anchor="middle" x="60" y="50">A<tspan>B</tspan>C</text> + <text text-anchor="end" x="60" y="60">A<tspan>B</tspan>C</text> +</svg> + diff --git a/svgio/source/svgreader/svgcharacternode.cxx b/svgio/source/svgreader/svgcharacternode.cxx index 8a6610c91d25..bfd17c6e0d58 100644 --- a/svgio/source/svgreader/svgcharacternode.cxx +++ b/svgio/source/svgreader/svgcharacternode.cxx @@ -359,17 +359,19 @@ namespace svgio::svgreader } } + // Use the whole text line to calculate the align position + double fWholeTextLineWidth(aTextLayouterDevice.getTextWidth(getWholeTextLine(), 0, getWholeTextLine().getLength())); // apply TextAlign switch(aTextAlign) { case TextAlign::right: { - aPosition.setX(aPosition.getX() - fTextWidth); + aPosition.setX(aPosition.getX() - fWholeTextLineWidth); break; } case TextAlign::center: { - aPosition.setX(aPosition.getX() - (fTextWidth * 0.5)); + aPosition.setX(aPosition.getX() - (fWholeTextLineWidth * 0.5)); break; } case TextAlign::notset: diff --git a/svgio/source/svgreader/svgdocumenthandler.cxx b/svgio/source/svgreader/svgdocumenthandler.cxx index 5c600a86dce1..b0bdfbabe7fb 100644 --- a/svgio/source/svgreader/svgdocumenthandler.cxx +++ b/svgio/source/svgreader/svgdocumenthandler.cxx @@ -145,6 +145,87 @@ namespace return pLast; } + + OUString getWholeTextLine(svgio::svgreader::SvgNode const * pNode) + { + OUString sText; + if (pNode) + { + const auto& rChilds = pNode->getChildren(); + const sal_uInt32 nCount(rChilds.size()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + svgio::svgreader::SvgNode* pCandidate = rChilds[a].get(); + + if(pCandidate) + { + switch(pCandidate->getType()) + { + case SVGToken::Character: + { + svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate); + sText += pCharNode->getText(); + break; + } + case SVGToken::Tspan: + case SVGToken::TextPath: + case SVGToken::Tref: + { + sText += getWholeTextLine(pCandidate); + break; + } + default: + { + OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)"); + break; + } + } + } + } + } + return sText; + } + + void setWholeTextLine(svgio::svgreader::SvgNode const * pNode, const OUString& rText) + { + if (pNode) + { + const auto& rChilds = pNode->getChildren(); + const sal_uInt32 nCount(rChilds.size()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + svgio::svgreader::SvgNode* pCandidate = rChilds[a].get(); + + if(pCandidate) + { + switch(pCandidate->getType()) + { + case SVGToken::Character: + { + svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate); + pCharNode->setWholeTextLine(rText); + break; + } + case SVGToken::Tspan: + case SVGToken::TextPath: + case SVGToken::Tref: + { + setWholeTextLine(pCandidate, rText); + break; + } + default: + { + OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)"); + break; + } + } + } + } + } + } + } // end anonymous namespace SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath) @@ -468,7 +549,7 @@ namespace return; const SVGToken aSVGToken(StrToSVGToken(aName, false)); - SvgNode* pWhitespaceCheck(SVGToken::Text == aSVGToken ? mpTarget : nullptr); + SvgNode* pTextNode(SVGToken::Text == aSVGToken ? mpTarget : nullptr); SvgStyleNode* pCssStyle(SVGToken::Style == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : nullptr); SvgTitleDescNode* pSvgTitleDescNode(SVGToken::Title == aSVGToken || SVGToken::Desc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : nullptr); @@ -597,10 +678,14 @@ namespace } } - if(pWhitespaceCheck) + if(pTextNode) { // cleanup read strings - whiteSpaceHandling(pWhitespaceCheck, nullptr); + whiteSpaceHandling(pTextNode, nullptr); + + // Iterate over all the nodes in this text element to get the whole text line + OUString sWholeTextLine = getWholeTextLine(pTextNode); + setWholeTextLine(pTextNode, sWholeTextLine); } } |