summaryrefslogtreecommitdiff
path: root/svgio
diff options
context:
space:
mode:
authorXisco Fauli <xiscofauli@libreoffice.org>2023-08-03 17:47:36 +0200
committerXisco Fauli <xiscofauli@libreoffice.org>2023-08-03 18:36:56 +0200
commit2795a230464aea3a792e67b5625fce2a0c01d547 (patch)
tree905bb5b808270eb1c43c5d796171fc1940bd98a7 /svgio
parent490cf5393d30dd2e9ab201286ef863b3c266a8b4 (diff)
tdf#151103: Use the whole text line to calculate the align position
Change-Id: I7ecd41c422afbf028101924972c47a510834ba5a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155314 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Diffstat (limited to 'svgio')
-rw-r--r--svgio/inc/svgcharacternode.hxx7
-rw-r--r--svgio/qa/cppunit/SvgImportTest.cxx86
-rw-r--r--svgio/qa/cppunit/data/tdf151103.svg18
-rw-r--r--svgio/source/svgreader/svgcharacternode.cxx6
-rw-r--r--svgio/source/svgreader/svgdocumenthandler.cxx91
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);
}
}