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-15 19:01:16 +0200
commit00871b79164d7dfb50820cdcae05b9050639a014 (patch)
treef2ec6f99782aeeeef0eb77e8b02f857ca8503a06 /svgio
parent2a432db2201f602496389bb165fd87aaaf5f7b54 (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> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> This commit also contains the following commits: svgio: remove recurrent checks Change-Id: I26c37e6b58e7c54e2bdc2c77543896daceb638d3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155520 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> svgio: get rid of SvgTextPositions and make SvgText inherit from SvgTspan Change-Id: Ief25e52ba2a493936f82f1674f73168ed5647278 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155521 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> svgio: avoid dynamic_cast Change-Id: I9a2e2c4341476a59ffb001c42e7812cb8249c856 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155548 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> svgio: handle addGap internally inside SvgCharacterNode Also add the gap at the beginning of the current node, not at the end of the previous one Change-Id: I6583059b4a7418010ac2af459e00fb0d02d39605 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155552 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> svgio: move SvgTextPosition to its own file In order to avoid a circular dependency in a follow-up commit Change-Id: Ib7b16e73282dfa6f3ca87aab1044cb92df72b6bf Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155555 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> related: tdf#151103: simplify code Keep the text line in the SvgTextNode and not in each SvgCharacterNode Change-Id: Ia33e46cc974a39a915e7b933337b4c529e6eeca5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155558 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> cid#1539807 Uninitialized pointer field Change-Id: I500c5d9f15c6a57622a28ea7cbf3b5f90761b5c0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155582 Tested-by: Caolán McNamara <caolan.mcnamara@collabora.com> Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com> Signed-off-by: Xisco Fauli <xiscofauli@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155656 Tested-by: Jenkins
Diffstat (limited to 'svgio')
-rw-r--r--svgio/Library_svgio.mk1
-rw-r--r--svgio/inc/svgcharacternode.hxx98
-rw-r--r--svgio/inc/svgtextnode.hxx18
-rw-r--r--svgio/inc/svgtextposition.hxx72
-rw-r--r--svgio/inc/svgtspannode.hxx44
-rw-r--r--svgio/qa/cppunit/SvgImportTest.cxx102
-rw-r--r--svgio/qa/cppunit/data/tdf151103.svg18
-rw-r--r--svgio/source/svgreader/svgcharacternode.cxx320
-rw-r--r--svgio/source/svgreader/svgdocumenthandler.cxx80
-rw-r--r--svgio/source/svgreader/svgtextnode.cxx25
-rw-r--r--svgio/source/svgreader/svgtextposition.cxx200
-rw-r--r--svgio/source/svgreader/svgtspannode.cxx85
12 files changed, 586 insertions, 477 deletions
diff --git a/svgio/Library_svgio.mk b/svgio/Library_svgio.mk
index c25077ed94d3..edd83ed57251 100644
--- a/svgio/Library_svgio.mk
+++ b/svgio/Library_svgio.mk
@@ -85,6 +85,7 @@ $(eval $(call gb_Library_add_exception_objects,svgio,\
svgio/source/svgreader/svgsvgnode \
svgio/source/svgreader/svgsymbolnode \
svgio/source/svgreader/svgtextnode \
+ svgio/source/svgreader/svgtextposition \
svgio/source/svgreader/svgtitledescnode \
svgio/source/svgreader/svgtoken \
svgio/source/svgreader/svgtrefnode \
diff --git a/svgio/inc/svgcharacternode.hxx b/svgio/inc/svgcharacternode.hxx
index 8861055f8e65..391c4029e46c 100644
--- a/svgio/inc/svgcharacternode.hxx
+++ b/svgio/inc/svgcharacternode.hxx
@@ -24,100 +24,13 @@
#include <string_view>
-#include "svgnode.hxx"
+#include "svgtextnode.hxx"
+#include "svgtextposition.hxx"
namespace drawinglayer::primitive2d { class TextSimplePortionPrimitive2D; }
namespace svgio::svgreader
{
- class SvgTextPositions
- {
- private:
- SvgNumberVector maX;
- SvgNumberVector maY;
- SvgNumberVector maDx;
- SvgNumberVector maDy;
- SvgNumberVector maRotate;
- SvgNumber maTextLength;
-
- bool mbLengthAdjust : 1; // true = spacing, false = spacingAndGlyphs
-
- public:
- SvgTextPositions();
-
- void parseTextPositionAttributes(SVGToken aSVGToken, std::u16string_view aContent);
-
- /// X content
- const SvgNumberVector& getX() const { return maX; }
- void setX(SvgNumberVector&& aX) { maX = std::move(aX); }
-
- /// Y content
- const SvgNumberVector& getY() const { return maY; }
- void setY(SvgNumberVector&& aY) { maY = std::move(aY); }
-
- /// Dx content
- const SvgNumberVector& getDx() const { return maDx; }
- void setDx(SvgNumberVector&& aDx) { maDx = std::move(aDx); }
-
- /// Dy content
- const SvgNumberVector& getDy() const { return maDy; }
- void setDy(SvgNumberVector&& aDy) { maDy = std::move(aDy); }
-
- /// Rotate content
- const SvgNumberVector& getRotate() const { return maRotate; }
- void setRotate(SvgNumberVector&& aRotate) { maRotate = std::move(aRotate); }
-
- /// TextLength content
- const SvgNumber& getTextLength() const { return maTextLength; }
- void setTextLength(const SvgNumber& rTextLength) { maTextLength = rTextLength; }
-
- /// LengthAdjust content
- bool getLengthAdjust() const { return mbLengthAdjust; }
- void setLengthAdjust(bool bNew) { mbLengthAdjust = bNew; }
- };
-
- class SvgTextPosition
- {
- private:
- SvgTextPosition* mpParent;
- ::std::vector< double > maX;
- ::std::vector< double > maY;
- ::std::vector< double > maDx;
- ::std::vector< double > maRotate;
- double mfTextLength;
-
- // absolute, current, advancing position
- basegfx::B2DPoint maPosition;
-
- // advancing rotation index
- sal_uInt32 mnRotationIndex;
-
- bool mbLengthAdjust : 1; // true = spacing, false = spacingAndGlyphs
- bool mbAbsoluteX : 1;
-
- public:
- SvgTextPosition(
- SvgTextPosition* pParent,
- const InfoProvider& rInfoProvider,
- const SvgTextPositions& rSvgTextPositions);
-
- // data read access
- const SvgTextPosition* getParent() const { return mpParent; }
- const ::std::vector< double >& getX() const { return maX; }
- const ::std::vector< double >& getDx() const { return maDx; }
- double getTextLength() const { return mfTextLength; }
- bool getLengthAdjust() const { return mbLengthAdjust; }
- bool getAbsoluteX() const { return mbAbsoluteX; }
-
- // get/set absolute, current, advancing position
- const basegfx::B2DPoint& getPosition() const { return maPosition; }
- void setPosition(const basegfx::B2DPoint& rNew) { maPosition = rNew; }
-
- // rotation handling
- bool isRotated() const;
- double consumeRotation();
- };
-
class SvgCharacterNode final : public SvgNode
{
private:
@@ -127,6 +40,8 @@ namespace svgio::svgreader
// keep a copy of string data before space handling
OUString maTextBeforeSpaceHandling;
+ SvgTextNode* mpTextParent;
+
/// local helpers
rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> createSimpleTextPrimitive(
SvgTextPosition& rSvgTextPosition,
@@ -144,15 +59,16 @@ namespace svgio::svgreader
virtual ~SvgCharacterNode() override;
virtual const SvgStyleAttributes* getSvgStyleAttributes() const override;
+
void decomposeText(drawinglayer::primitive2d::Primitive2DContainer& rTarget, SvgTextPosition& rSvgTextPosition) const;
void whiteSpaceHandling();
- void addGap();
+ SvgCharacterNode* addGap(SvgCharacterNode* pPreviousCharacterNode);
void concatenate(std::u16string_view rText);
/// Text content
const OUString& getText() const { return maText; }
- const OUString& getTextBeforeSpaceHandling() const { return maTextBeforeSpaceHandling; }
+ void setTextParent(SvgTextNode* pTextParent) { mpTextParent = pTextParent; }
};
} // end of namespace svgio::svgreader
diff --git a/svgio/inc/svgtextnode.hxx b/svgio/inc/svgtextnode.hxx
index 0cc78f130aed..2d5f98ec18fc 100644
--- a/svgio/inc/svgtextnode.hxx
+++ b/svgio/inc/svgtextnode.hxx
@@ -19,23 +19,23 @@
#pragma once
-#include "svgnode.hxx"
#include "svgstyleattributes.hxx"
-#include "svgcharacternode.hxx"
+#include "svgtextposition.hxx"
+#include "svgtspannode.hxx"
#include <basegfx/matrix/b2dhommatrix.hxx>
namespace svgio::svgreader
{
- class SvgTextNode final : public SvgNode
+ class SvgTextNode final : public SvgTspanNode
{
private:
- /// use styles
- SvgStyleAttributes maSvgStyleAttributes;
-
/// variable scan values, dependent of given XAttributeList
std::optional<basegfx::B2DHomMatrix>
mpaTransform;
- SvgTextPositions maSvgTextPositions;
+
+ // The text line composed by the different SvgCharacterNode children
+ // it will be used to calculate their alignment
+ OUString maTextLine;
/// local helpers
void DecomposeChild(
@@ -53,13 +53,15 @@ namespace svgio::svgreader
SvgNode* pParent);
virtual ~SvgTextNode() override;
- virtual const SvgStyleAttributes* getSvgStyleAttributes() const override;
virtual void parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent) override;
virtual void decomposeSvgNode(drawinglayer::primitive2d::Primitive2DContainer& rTarget, bool bReferenced) const override;
/// transform content, set if found in current context
const std::optional<basegfx::B2DHomMatrix>& getTransform() const { return mpaTransform; }
void setTransform(const std::optional<basegfx::B2DHomMatrix>& pMatrix) { mpaTransform = pMatrix; }
+
+ void concatenateTextLine(std::u16string_view rText) {maTextLine += rText;}
+ const OUString& getTextLine() const { return maTextLine; }
};
} // end of namespace svgio::svgreader
diff --git a/svgio/inc/svgtextposition.hxx b/svgio/inc/svgtextposition.hxx
new file mode 100644
index 000000000000..df6adc16ab1e
--- /dev/null
+++ b/svgio/inc/svgtextposition.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include <rtl/ref.hxx>
+
+#include <string_view>
+
+#include "svgtspannode.hxx"
+
+namespace svgio::svgreader
+{
+class SvgTextPosition
+{
+private:
+ SvgTextPosition* mpParent;
+ ::std::vector<double> maX;
+ ::std::vector<double> maY;
+ ::std::vector<double> maDx;
+ ::std::vector<double> maRotate;
+ double mfTextLength;
+
+ // absolute, current, advancing position
+ basegfx::B2DPoint maPosition;
+
+ // advancing rotation index
+ sal_uInt32 mnRotationIndex;
+
+ bool mbLengthAdjust : 1; // true = spacing, false = spacingAndGlyphs
+ bool mbAbsoluteX : 1;
+
+public:
+ SvgTextPosition(SvgTextPosition* pParent, const SvgTspanNode& rSvgCharacterNode);
+
+ // data read access
+ const SvgTextPosition* getParent() const { return mpParent; }
+ const ::std::vector<double>& getX() const { return maX; }
+ const ::std::vector<double>& getDx() const { return maDx; }
+ double getTextLength() const { return mfTextLength; }
+ bool getLengthAdjust() const { return mbLengthAdjust; }
+ bool getAbsoluteX() const { return mbAbsoluteX; }
+
+ // get/set absolute, current, advancing position
+ const basegfx::B2DPoint& getPosition() const { return maPosition; }
+ void setPosition(const basegfx::B2DPoint& rNew) { maPosition = rNew; }
+
+ // rotation handling
+ bool isRotated() const;
+ double consumeRotation();
+};
+
+} // end of namespace svgio::svgreader
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svgio/inc/svgtspannode.hxx b/svgio/inc/svgtspannode.hxx
index 10a7b7ee16a9..d5d86c5a7c1a 100644
--- a/svgio/inc/svgtspannode.hxx
+++ b/svgio/inc/svgtspannode.hxx
@@ -19,22 +19,29 @@
#pragma once
-#include "svgcharacternode.hxx"
+#include "svgnode.hxx"
#include "svgstyleattributes.hxx"
namespace svgio::svgreader
{
- class SvgTspanNode final : public SvgNode
+ class SvgTspanNode : public SvgNode
{
private:
/// use styles
SvgStyleAttributes maSvgStyleAttributes;
- /// variable scan values, dependent of given XAttributeList
- SvgTextPositions maSvgTextPositions;
+ SvgNumberVector maX;
+ SvgNumberVector maY;
+ SvgNumberVector maDx;
+ SvgNumberVector maDy;
+ SvgNumberVector maRotate;
+ SvgNumber maTextLength;
+
+ bool mbLengthAdjust : 1; // true = spacing, false = spacingAndGlyphs
public:
SvgTspanNode(
+ SVGToken aType,
SvgDocument& rDocument,
SvgNode* pParent);
virtual ~SvgTspanNode() override;
@@ -44,8 +51,33 @@ namespace svgio::svgreader
double getCurrentFontSize() const;
- /// access to SvgTextPositions
- const SvgTextPositions& getSvgTextPositions() const { return maSvgTextPositions; }
+ /// X content
+ const SvgNumberVector& getX() const { return maX; }
+ void setX(SvgNumberVector&& aX) { maX = std::move(aX); }
+
+ /// Y content
+ const SvgNumberVector& getY() const { return maY; }
+ void setY(SvgNumberVector&& aY) { maY = std::move(aY); }
+
+ /// Dx content
+ const SvgNumberVector& getDx() const { return maDx; }
+ void setDx(SvgNumberVector&& aDx) { maDx = std::move(aDx); }
+
+ /// Dy content
+ const SvgNumberVector& getDy() const { return maDy; }
+ void setDy(SvgNumberVector&& aDy) { maDy = std::move(aDy); }
+
+ /// Rotate content
+ const SvgNumberVector& getRotate() const { return maRotate; }
+ void setRotate(SvgNumberVector&& aRotate) { maRotate = std::move(aRotate); }
+
+ /// TextLength content
+ const SvgNumber& getTextLength() const { return maTextLength; }
+ void setTextLength(const SvgNumber& rTextLength) { maTextLength = rTextLength; }
+
+ /// LengthAdjust content
+ bool getLengthAdjust() const { return mbLengthAdjust; }
+ void setLengthAdjust(bool bNew) { mbLengthAdjust = bNew; }
};
} // end of namespace svgio::svgreader
diff --git a/svgio/qa/cppunit/SvgImportTest.cxx b/svgio/qa/cppunit/SvgImportTest.cxx
index 7a8ba6e82ec0..c52e20594b10 100644
--- a/svgio/qa/cppunit/SvgImportTest.cxx
+++ b/svgio/qa/cppunit/SvgImportTest.cxx
@@ -733,11 +733,11 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf85770)
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "height", "11");
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "familyname", "Times New Roman");
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "fontcolor", "#000000");
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "text", "Start ");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "text", "Start");
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "height", "11");
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "familyname", "Times New Roman");
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "fontcolor", "#000000");
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "text", "End");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "text", " End");
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "height", "11");
assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "familyname", "Times New Roman");
@@ -1159,12 +1159,12 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf156251)
// Without the fix in place, this test would have failed with
// - Expected: 'You are '
// - Actual : 'You are'
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "text", "You are ");
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "text", "not ");
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "text", "a banana!");
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", "text", "You are ");
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", "text", "not ");
- assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", "text", "a banana!");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[1]", "text", "You are");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[2]", "text", " not");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[3]", "text", " a banana!");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[4]", "text", "You are");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[5]", "text", " not");
+ assertXPath(pDocument, "/primitive2D/transform/textsimpleportion[6]", "text", " a banana!");
}
CPPUNIT_TEST_FIXTURE(Test, testMaskText)
@@ -1454,6 +1454,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..2b88944aa8d0 100644
--- a/svgio/source/svgreader/svgcharacternode.cxx
+++ b/svgio/source/svgreader/svgcharacternode.cxx
@@ -33,116 +33,6 @@ using namespace drawinglayer::primitive2d;
namespace svgio::svgreader
{
- SvgTextPositions::SvgTextPositions()
- : mbLengthAdjust(true)
- {
- }
-
- void SvgTextPositions::parseTextPositionAttributes(SVGToken aSVGToken, std::u16string_view aContent)
- {
- // parse own
- switch(aSVGToken)
- {
- case SVGToken::X:
- {
- if(!aContent.empty())
- {
- SvgNumberVector aVector;
-
- if(readSvgNumberVector(aContent, aVector))
- {
- setX(std::move(aVector));
- }
- }
- break;
- }
- case SVGToken::Y:
- {
- if(!aContent.empty())
- {
- SvgNumberVector aVector;
-
- if(readSvgNumberVector(aContent, aVector))
- {
- setY(std::move(aVector));
- }
- }
- break;
- }
- case SVGToken::Dx:
- {
- if(!aContent.empty())
- {
- SvgNumberVector aVector;
-
- if(readSvgNumberVector(aContent, aVector))
- {
- setDx(std::move(aVector));
- }
- }
- break;
- }
- case SVGToken::Dy:
- {
- if(!aContent.empty())
- {
- SvgNumberVector aVector;
-
- if(readSvgNumberVector(aContent, aVector))
- {
- setDy(std::move(aVector));
- }
- }
- break;
- }
- case SVGToken::Rotate:
- {
- if(!aContent.empty())
- {
- SvgNumberVector aVector;
-
- if(readSvgNumberVector(aContent, aVector))
- {
- setRotate(std::move(aVector));
- }
- }
- break;
- }
- case SVGToken::TextLength:
- {
- SvgNumber aNum;
-
- if(readSingleNumber(aContent, aNum))
- {
- if(aNum.isPositive())
- {
- setTextLength(aNum);
- }
- }
- break;
- }
- case SVGToken::LengthAdjust:
- {
- if(!aContent.empty())
- {
- if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"spacing"))
- {
- setLengthAdjust(true);
- }
- else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"spacingAndGlyphs"))
- {
- setLengthAdjust(false);
- }
- }
- break;
- }
- default:
- {
- break;
- }
- }
- }
-
namespace {
class localTextBreakupHelper : public TextBreakupHelper
@@ -188,7 +78,8 @@ namespace svgio::svgreader
SvgNode* pParent,
OUString aText)
: SvgNode(SVGToken::Character, rDocument, pParent),
- maText(std::move(aText))
+ maText(std::move(aText)),
+ mpTextParent(nullptr)
{
}
@@ -359,17 +250,19 @@ namespace svgio::svgreader
}
}
+ // Use the whole text line to calculate the align position
+ double fWholeTextLineWidth(aTextLayouterDevice.getTextWidth(mpTextParent->getTextLine(), 0, mpTextParent->getTextLine().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:
@@ -573,197 +466,64 @@ namespace svgio::svgreader
}
}
- void SvgCharacterNode::addGap()
- {
- maText += " ";
- }
-
- void SvgCharacterNode::concatenate(std::u16string_view rText)
- {
- maText += rText;
- }
-
- void SvgCharacterNode::decomposeText(Primitive2DContainer& rTarget, SvgTextPosition& rSvgTextPosition) const
+ SvgCharacterNode* SvgCharacterNode::addGap(SvgCharacterNode* pPreviousCharacterNode)
{
- if(!getText().isEmpty())
+ // maText may have lost all text. If that's the case, ignore as invalid character node
+ // Also ignore if maTextBeforeSpaceHandling just have spaces
+ if(!maText.isEmpty() && !o3tl::trim(maTextBeforeSpaceHandling).empty())
{
- const SvgStyleAttributes* pSvgStyleAttributes = getSvgStyleAttributes();
-
- if(pSvgStyleAttributes)
+ if(pPreviousCharacterNode)
{
- decomposeTextWithStyle(rTarget, rSvgTextPosition, *pSvgStyleAttributes);
- }
- }
- }
+ bool bAddGap(true);
+ // Do not add a gap if last node doesn't end with a space and
+ // current note doesn't start with a space
+ const sal_uInt32 nLastLength(pPreviousCharacterNode->maTextBeforeSpaceHandling.getLength());
+ if(pPreviousCharacterNode->maTextBeforeSpaceHandling[nLastLength - 1] != ' ' && maTextBeforeSpaceHandling[0] != ' ')
+ bAddGap = false;
- SvgTextPosition::SvgTextPosition(
- SvgTextPosition* pParent,
- const InfoProvider& rInfoProvider,
- const SvgTextPositions& rSvgTextPositions)
- : mpParent(pParent),
- maRotate(solveSvgNumberVector(rSvgTextPositions.getRotate(), rInfoProvider)),
- mfTextLength(0.0),
- mnRotationIndex(0),
- mbLengthAdjust(rSvgTextPositions.getLengthAdjust()),
- mbAbsoluteX(false)
- {
- // get TextLength if provided
- if(rSvgTextPositions.getTextLength().isSet())
- {
- mfTextLength = rSvgTextPositions.getTextLength().solve(rInfoProvider);
- }
-
- // SVG does not really define in which units a \91rotate\92 for Text/TSpan is given,
- // but it seems to be degrees. Convert here to radians
- if(!maRotate.empty())
- {
- for (double& f : maRotate)
- {
- f = basegfx::deg2rad(f);
- }
- }
-
- // get text positions X
- const sal_uInt32 nSizeX(rSvgTextPositions.getX().size());
-
- if(nSizeX)
- {
- // we have absolute positions, get first one as current text position X
- maPosition.setX(rSvgTextPositions.getX()[0].solve(rInfoProvider, NumberType::xcoordinate));
- mbAbsoluteX = true;
- }
- else
- {
- // no absolute position, get from parent
- if(pParent)
- {
- maPosition.setX(pParent->getPosition().getX());
- }
- }
-
- const sal_uInt32 nSizeDx(rSvgTextPositions.getDx().size());
- if(nSizeDx)
- {
- // relative positions given, translate position derived from parent
- maPosition.setX(maPosition.getX() + rSvgTextPositions.getDx()[0].solve(rInfoProvider, NumberType::xcoordinate));
- }
-
- // fill deltas to maX
- maX.reserve(nSizeX);
+ // With this option a baseline shift between two char parts ('words')
+ // will not add a space 'gap' to the end of the (non-last) word. This
+ // seems to be the standard behaviour, see last bugdoc attached #122524#
+ const SvgStyleAttributes* pStyleLast = pPreviousCharacterNode->getSvgStyleAttributes();
+ const SvgStyleAttributes* pStyleCurrent = getSvgStyleAttributes();
- for(sal_uInt32 a(1); a < std::max(nSizeX, nSizeDx); ++a)
- {
- if (a < nSizeX)
- {
- double nPos = rSvgTextPositions.getX()[a].solve(rInfoProvider, NumberType::xcoordinate) - maPosition.getX();
-
- if(a < nSizeDx)
+ if(pStyleLast && pStyleCurrent && pStyleLast->getBaselineShift() != pStyleCurrent->getBaselineShift())
{
- nPos += rSvgTextPositions.getDx()[a].solve(rInfoProvider, NumberType::xcoordinate);
+ bAddGap = false;
}
- maX.push_back(nPos);
- }
- else
- {
- // Apply them later since it also needs the character width to calculate
- // the final character position
- maDx.push_back(rSvgTextPositions.getDx()[a].solve(rInfoProvider, NumberType::xcoordinate));
+ // add in-between whitespace (single space) to last
+ // known character node
+ if(bAddGap)
+ {
+ maText = " " + maText;
+ }
}
- }
-
- // get text positions Y
- const sal_uInt32 nSizeY(rSvgTextPositions.getY().size());
- if(nSizeY)
- {
- // we have absolute positions, get first one as current text position Y
- maPosition.setY(rSvgTextPositions.getY()[0].solve(rInfoProvider, NumberType::ycoordinate));
- mbAbsoluteX = true;
+ // this becomes the previous character node
+ return this;
}
- else
- {
- // no absolute position, get from parent
- if(pParent)
- {
- maPosition.setY(pParent->getPosition().getY());
- }
- }
-
- const sal_uInt32 nSizeDy(rSvgTextPositions.getDy().size());
- if(nSizeDy)
- {
- // relative positions given, translate position derived from parent
- maPosition.setY(maPosition.getY() + rSvgTextPositions.getDy()[0].solve(rInfoProvider, NumberType::ycoordinate));
- }
-
- // fill deltas to maY
- maY.reserve(nSizeY);
-
- for(sal_uInt32 a(1); a < nSizeY; a++)
- {
- double nPos = rSvgTextPositions.getY()[a].solve(rInfoProvider, NumberType::ycoordinate) - maPosition.getY();
-
- if(a < nSizeDy)
- {
- nPos += rSvgTextPositions.getDy()[a].solve(rInfoProvider, NumberType::ycoordinate);
- }
-
- maY.push_back(nPos);
- }
+ return pPreviousCharacterNode;
}
- bool SvgTextPosition::isRotated() const
+ void SvgCharacterNode::concatenate(std::u16string_view rText)
{
- if(maRotate.empty())
- {
- if(getParent())
- {
- return getParent()->isRotated();
- }
- else
- {
- return false;
- }
- }
- else
- {
- return true;
- }
+ maText += rText;
}
- double SvgTextPosition::consumeRotation()
+ void SvgCharacterNode::decomposeText(Primitive2DContainer& rTarget, SvgTextPosition& rSvgTextPosition) const
{
- double fRetval(0.0);
-
- if(maRotate.empty())
- {
- if(getParent())
- {
- fRetval = mpParent->consumeRotation();
- }
- else
- {
- fRetval = 0.0;
- }
- }
- else
+ if(!getText().isEmpty())
{
- const sal_uInt32 nSize(maRotate.size());
+ const SvgStyleAttributes* pSvgStyleAttributes = getSvgStyleAttributes();
- if(mnRotationIndex < nSize)
- {
- fRetval = maRotate[mnRotationIndex++];
- }
- else
+ if(pSvgStyleAttributes)
{
- fRetval = maRotate[nSize - 1];
+ decomposeTextWithStyle(rTarget, rSvgTextPosition, *pSvgStyleAttributes);
}
}
-
- return fRetval;
}
} // end of namespace svgio
diff --git a/svgio/source/svgreader/svgdocumenthandler.cxx b/svgio/source/svgreader/svgdocumenthandler.cxx
index 2e7785f2626d..45213997144c 100644
--- a/svgio/source/svgreader/svgdocumenthandler.cxx
+++ b/svgio/source/svgreader/svgdocumenthandler.cxx
@@ -54,7 +54,6 @@
#include <svgtitledescnode.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
-#include <o3tl/string_view.hxx>
using namespace com::sun::star;
@@ -63,7 +62,7 @@ namespace svgio::svgreader
namespace
{
- svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, svgio::svgreader::SvgCharacterNode* pLast)
+ svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode const * pNode, svgio::svgreader::SvgTextNode* pText, svgio::svgreader::SvgCharacterNode* pLast)
{
if(pNode)
{
@@ -84,45 +83,10 @@ namespace
svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
pCharNode->whiteSpaceHandling();
+ pLast = pCharNode->addGap(pLast);
- // pCharNode may have lost all text. If that's the case, ignore
- // as invalid character node
- // Also ignore if textBeforeSpaceHandling just have spaces
- if(!pCharNode->getText().isEmpty() && !o3tl::trim(pCharNode->getTextBeforeSpaceHandling()).empty())
- {
- if(pLast)
- {
- bool bAddGap(true);
-
- // Do not add a gap if last node doesn't end with a space and
- // current note doesn't start with a space
- const sal_uInt32 nLastLength(pLast->getTextBeforeSpaceHandling().getLength());
- if(pLast->getTextBeforeSpaceHandling()[nLastLength - 1] != ' ' && pCharNode->getTextBeforeSpaceHandling()[0] != ' ')
- bAddGap = false;
-
- // With this option a baseline shift between two char parts ('words')
- // will not add a space 'gap' to the end of the (non-last) word. This
- // seems to be the standard behaviour, see last bugdoc attached #122524#
- const svgio::svgreader::SvgStyleAttributes* pStyleLast = pLast->getSvgStyleAttributes();
- const svgio::svgreader::SvgStyleAttributes* pStyleCurrent = pCandidate->getSvgStyleAttributes();
-
- if(pStyleLast && pStyleCurrent && pStyleLast->getBaselineShift() != pStyleCurrent->getBaselineShift())
- {
- bAddGap = false;
- }
-
- // add in-between whitespace (single space) to last
- // known character node
- if(bAddGap)
- {
- pLast->addGap();
- }
- }
-
- // remember new last corrected character node
- pLast = pCharNode;
- }
-
+ pCharNode->setTextParent(pText);
+ pText->concatenateTextLine(pCharNode->getText());
break;
}
case SVGToken::Tspan:
@@ -130,7 +94,7 @@ namespace
case SVGToken::Tref:
{
// recursively clean whitespaces in subhierarchy
- pLast = whiteSpaceHandling(pCandidate, pLast);
+ pLast = whiteSpaceHandling(pCandidate, pText, pLast);
break;
}
default:
@@ -145,6 +109,7 @@ namespace
return pLast;
}
+
} // end anonymous namespace
SvgDocHdl::SvgDocHdl(const OUString& aAbsolutePath)
@@ -323,7 +288,7 @@ namespace
}
case SVGToken::Tspan:
{
- mpTarget = new SvgTspanNode(maDocument, mpTarget);
+ mpTarget = new SvgTspanNode(aSVGToken, maDocument, mpTarget);
mpTarget->parseAttributes(xAttribs);
break;
}
@@ -463,7 +428,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);
@@ -584,10 +549,10 @@ namespace
}
}
- if(pWhitespaceCheck)
+ if(pTextNode)
{
// cleanup read strings
- whiteSpaceHandling(pWhitespaceCheck, nullptr);
+ whiteSpaceHandling(pTextNode, static_cast< SvgTextNode*>(pTextNode), nullptr);
}
}
@@ -605,24 +570,23 @@ namespace
case SVGToken::TextPath:
{
const auto& rChilds = mpTarget->getChildren();
- SvgCharacterNode* pTarget = nullptr;
if(!rChilds.empty())
{
- pTarget = dynamic_cast< SvgCharacterNode* >(rChilds[rChilds.size() - 1].get());
- }
+ SvgNode* pChild = rChilds[rChilds.size() - 1].get();
+ if ( pChild->getType() == SVGToken::Character )
+ {
+ SvgCharacterNode& rSvgCharacterNode = static_cast< SvgCharacterNode& >(*pChild);
- if(pTarget)
- {
- // concatenate to current character span
- pTarget->concatenate(aChars);
- }
- else
- {
- // add character span as simplified tspan (no arguments)
- // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
- new SvgCharacterNode(maDocument, mpTarget, aChars);
+ // concatenate to current character span
+ rSvgCharacterNode.concatenate(aChars);
+ break;
+ }
}
+
+ // add character span as simplified tspan (no arguments)
+ // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
+ new SvgCharacterNode(maDocument, mpTarget, aChars);
break;
}
case SVGToken::Style:
diff --git a/svgio/source/svgreader/svgtextnode.cxx b/svgio/source/svgreader/svgtextnode.cxx
index 5b8cc3187070..bd8a334c5e11 100644
--- a/svgio/source/svgreader/svgtextnode.cxx
+++ b/svgio/source/svgreader/svgtextnode.cxx
@@ -30,8 +30,7 @@ namespace svgio::svgreader
SvgTextNode::SvgTextNode(
SvgDocument& rDocument,
SvgNode* pParent)
- : SvgNode(SVGToken::Text, rDocument, pParent),
- maSvgStyleAttributes(*this)
+ : SvgTspanNode(SVGToken::Text, rDocument, pParent)
{
}
@@ -39,30 +38,14 @@ namespace svgio::svgreader
{
}
- const SvgStyleAttributes* SvgTextNode::getSvgStyleAttributes() const
- {
- return checkForCssStyle(maSvgStyleAttributes);
- }
-
void SvgTextNode::parseAttribute(const OUString& rTokenName, SVGToken aSVGToken, const OUString& aContent)
{
// call parent
- SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
-
- // read style attributes
- maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent);
-
- // read text position attributes
- maSvgTextPositions.parseTextPositionAttributes(aSVGToken, aContent);
+ SvgTspanNode::parseAttribute(rTokenName, aSVGToken, aContent);
// parse own
switch(aSVGToken)
{
- case SVGToken::Style:
- {
- readLocalCssStyle(aContent);
- break;
- }
case SVGToken::Transform:
{
const basegfx::B2DHomMatrix aMatrix(readTransform(aContent, *this));
@@ -159,7 +142,7 @@ namespace svgio::svgreader
if(nCount)
{
- SvgTextPosition aSvgTextPosition(&rSvgTextPosition, rSvgTspanNode, rSvgTspanNode.getSvgTextPositions());
+ SvgTextPosition aSvgTextPosition(&rSvgTextPosition, rSvgTspanNode);
drawinglayer::primitive2d::Primitive2DContainer aNewTarget;
for(sal_uInt32 a(0); a < nCount; a++)
@@ -229,7 +212,7 @@ namespace svgio::svgreader
if(fOpacity <= 0.0)
return;
- SvgTextPosition aSvgTextPosition(nullptr, *this, maSvgTextPositions);
+ SvgTextPosition aSvgTextPosition(nullptr, *this);
drawinglayer::primitive2d::Primitive2DContainer aNewTarget;
const auto& rChildren = getChildren();
const sal_uInt32 nCount(rChildren.size());
diff --git a/svgio/source/svgreader/svgtextposition.cxx b/svgio/source/svgreader/svgtextposition.cxx
new file mode 100644
index 000000000000..50a896ba2204
--- /dev/null
+++ b/svgio/source/svgreader/svgtextposition.cxx
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svgtextposition.hxx>
+
+using namespace drawinglayer::primitive2d;
+
+namespace svgio::svgreader
+{
+SvgTextPosition::SvgTextPosition(SvgTextPosition* pParent, const SvgTspanNode& rSvgTspanNode)
+ : mpParent(pParent)
+ , maRotate(solveSvgNumberVector(rSvgTspanNode.getRotate(), rSvgTspanNode))
+ , mfTextLength(0.0)
+ , mnRotationIndex(0)
+ , mbLengthAdjust(rSvgTspanNode.getLengthAdjust())
+ , mbAbsoluteX(false)
+{
+ const InfoProvider& rInfoProvider(rSvgTspanNode);
+
+ // get TextLength if provided
+ if (rSvgTspanNode.getTextLength().isSet())
+ {
+ mfTextLength = rSvgTspanNode.getTextLength().solve(rInfoProvider);
+ }
+
+ // SVG does not really define in which units a \91rotate\92 for Text/TSpan is given,
+ // but it seems to be degrees. Convert here to radians
+ if (!maRotate.empty())
+ {
+ for (double& f : maRotate)
+ {
+ f = basegfx::deg2rad(f);
+ }
+ }
+
+ // get text positions X
+ const sal_uInt32 nSizeX(rSvgTspanNode.getX().size());
+
+ if (nSizeX)
+ {
+ // we have absolute positions, get first one as current text position X
+ maPosition.setX(rSvgTspanNode.getX()[0].solve(rInfoProvider, NumberType::xcoordinate));
+ mbAbsoluteX = true;
+ }
+ else
+ {
+ // no absolute position, get from parent
+ if (pParent)
+ {
+ maPosition.setX(pParent->getPosition().getX());
+ }
+ }
+
+ const sal_uInt32 nSizeDx(rSvgTspanNode.getDx().size());
+ if (nSizeDx)
+ {
+ // relative positions given, translate position derived from parent
+ maPosition.setX(maPosition.getX()
+ + rSvgTspanNode.getDx()[0].solve(rInfoProvider, NumberType::xcoordinate));
+ }
+
+ // fill deltas to maX
+ maX.reserve(nSizeX);
+
+ for (sal_uInt32 a(1); a < std::max(nSizeX, nSizeDx); ++a)
+ {
+ if (a < nSizeX)
+ {
+ double nPos = rSvgTspanNode.getX()[a].solve(rInfoProvider, NumberType::xcoordinate)
+ - maPosition.getX();
+
+ if (a < nSizeDx)
+ {
+ nPos += rSvgTspanNode.getDx()[a].solve(rInfoProvider, NumberType::xcoordinate);
+ }
+
+ maX.push_back(nPos);
+ }
+ else
+ {
+ // Apply them later since it also needs the character width to calculate
+ // the final character position
+ maDx.push_back(rSvgTspanNode.getDx()[a].solve(rInfoProvider, NumberType::xcoordinate));
+ }
+ }
+
+ // get text positions Y
+ const sal_uInt32 nSizeY(rSvgTspanNode.getY().size());
+
+ if (nSizeY)
+ {
+ // we have absolute positions, get first one as current text position Y
+ maPosition.setY(rSvgTspanNode.getY()[0].solve(rInfoProvider, NumberType::ycoordinate));
+ mbAbsoluteX = true;
+ }
+ else
+ {
+ // no absolute position, get from parent
+ if (pParent)
+ {
+ maPosition.setY(pParent->getPosition().getY());
+ }
+ }
+
+ const sal_uInt32 nSizeDy(rSvgTspanNode.getDy().size());
+
+ if (nSizeDy)
+ {
+ // relative positions given, translate position derived from parent
+ maPosition.setY(maPosition.getY()
+ + rSvgTspanNode.getDy()[0].solve(rInfoProvider, NumberType::ycoordinate));
+ }
+
+ // fill deltas to maY
+ maY.reserve(nSizeY);
+
+ for (sal_uInt32 a(1); a < nSizeY; a++)
+ {
+ double nPos = rSvgTspanNode.getY()[a].solve(rInfoProvider, NumberType::ycoordinate)
+ - maPosition.getY();
+
+ if (a < nSizeDy)
+ {
+ nPos += rSvgTspanNode.getDy()[a].solve(rInfoProvider, NumberType::ycoordinate);
+ }
+
+ maY.push_back(nPos);
+ }
+}
+
+bool SvgTextPosition::isRotated() const
+{
+ if (maRotate.empty())
+ {
+ if (getParent())
+ {
+ return getParent()->isRotated();
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return true;
+ }
+}
+
+double SvgTextPosition::consumeRotation()
+{
+ double fRetval(0.0);
+
+ if (maRotate.empty())
+ {
+ if (getParent())
+ {
+ fRetval = mpParent->consumeRotation();
+ }
+ else
+ {
+ fRetval = 0.0;
+ }
+ }
+ else
+ {
+ const sal_uInt32 nSize(maRotate.size());
+
+ if (mnRotationIndex < nSize)
+ {
+ fRetval = maRotate[mnRotationIndex++];
+ }
+ else
+ {
+ fRetval = maRotate[nSize - 1];
+ }
+ }
+
+ return fRetval;
+}
+
+} // end of namespace svgio
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svgio/source/svgreader/svgtspannode.cxx b/svgio/source/svgreader/svgtspannode.cxx
index df5e440080f8..4472b88ab3ad 100644
--- a/svgio/source/svgreader/svgtspannode.cxx
+++ b/svgio/source/svgreader/svgtspannode.cxx
@@ -18,14 +18,17 @@
*/
#include <svgtspannode.hxx>
+#include <o3tl/string_view.hxx>
namespace svgio::svgreader
{
SvgTspanNode::SvgTspanNode(
+ SVGToken aType,
SvgDocument& rDocument,
SvgNode* pParent)
- : SvgNode(SVGToken::Tspan, rDocument, pParent),
- maSvgStyleAttributes(*this)
+ : SvgNode(aType, rDocument, pParent),
+ maSvgStyleAttributes(*this),
+ mbLengthAdjust(true)
{
}
@@ -47,9 +50,6 @@ namespace svgio::svgreader
// read style attributes
maSvgStyleAttributes.parseStyleAttribute(aSVGToken, aContent);
- // read text position attributes
- maSvgTextPositions.parseTextPositionAttributes(aSVGToken, aContent);
-
// parse own
switch(aSVGToken)
{
@@ -58,6 +58,81 @@ namespace svgio::svgreader
readLocalCssStyle(aContent);
break;
}
+ case SVGToken::X:
+ {
+ SvgNumberVector aVector;
+
+ if(readSvgNumberVector(aContent, aVector))
+ {
+ setX(std::move(aVector));
+ }
+ break;
+ }
+ case SVGToken::Y:
+ {
+ SvgNumberVector aVector;
+
+ if(readSvgNumberVector(aContent, aVector))
+ {
+ setY(std::move(aVector));
+ }
+ break;
+ }
+ case SVGToken::Dx:
+ {
+ SvgNumberVector aVector;
+
+ if(readSvgNumberVector(aContent, aVector))
+ {
+ setDx(std::move(aVector));
+ }
+ break;
+ }
+ case SVGToken::Dy:
+ {
+ SvgNumberVector aVector;
+
+ if(readSvgNumberVector(aContent, aVector))
+ {
+ setDy(std::move(aVector));
+ }
+ break;
+ }
+ case SVGToken::Rotate:
+ {
+ SvgNumberVector aVector;
+
+ if(readSvgNumberVector(aContent, aVector))
+ {
+ setRotate(std::move(aVector));
+ }
+ break;
+ }
+ case SVGToken::TextLength:
+ {
+ SvgNumber aNum;
+
+ if(readSingleNumber(aContent, aNum))
+ {
+ if(aNum.isPositive())
+ {
+ setTextLength(aNum);
+ }
+ }
+ break;
+ }
+ case SVGToken::LengthAdjust:
+ {
+ if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"spacing"))
+ {
+ setLengthAdjust(true);
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"spacingAndGlyphs"))
+ {
+ setLengthAdjust(false);
+ }
+ break;
+ }
default:
{
break;