1ddde725dSArmin Le Grand /**************************************************************
2ddde725dSArmin Le Grand  *
3ddde725dSArmin Le Grand  * Licensed to the Apache Software Foundation (ASF) under one
4ddde725dSArmin Le Grand  * or more contributor license agreements.  See the NOTICE file
5ddde725dSArmin Le Grand  * distributed with this work for additional information
6ddde725dSArmin Le Grand  * regarding copyright ownership.  The ASF licenses this file
7ddde725dSArmin Le Grand  * to you under the Apache License, Version 2.0 (the
8ddde725dSArmin Le Grand  * "License"); you may not use this file except in compliance
9ddde725dSArmin Le Grand  * with the License.  You may obtain a copy of the License at
10ddde725dSArmin Le Grand  *
11ddde725dSArmin Le Grand  *   http://www.apache.org/licenses/LICENSE-2.0
12ddde725dSArmin Le Grand  *
13ddde725dSArmin Le Grand  * Unless required by applicable law or agreed to in writing,
14ddde725dSArmin Le Grand  * software distributed under the License is distributed on an
15ddde725dSArmin Le Grand  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16ddde725dSArmin Le Grand  * KIND, either express or implied.  See the License for the
17ddde725dSArmin Le Grand  * specific language governing permissions and limitations
18ddde725dSArmin Le Grand  * under the License.
19ddde725dSArmin Le Grand  *
20ddde725dSArmin Le Grand  *************************************************************/
21ddde725dSArmin Le Grand 
22ddde725dSArmin Le Grand // MARKER(update_precomp.py): autogen include statement, do not remove
23ddde725dSArmin Le Grand #include "precompiled_svgio.hxx"
24ddde725dSArmin Le Grand 
25ddde725dSArmin Le Grand #include <svgio/svgreader/svgtextpathnode.hxx>
26ddde725dSArmin Le Grand #include <svgio/svgreader/svgstyleattributes.hxx>
27ddde725dSArmin Le Grand #include <svgio/svgreader/svgpathnode.hxx>
28ddde725dSArmin Le Grand #include <svgio/svgreader/svgdocument.hxx>
29ddde725dSArmin Le Grand #include <svgio/svgreader/svgtrefnode.hxx>
30ddde725dSArmin Le Grand #include <basegfx/polygon/b2dpolygon.hxx>
31ddde725dSArmin Le Grand #include <basegfx/polygon/b2dpolygontools.hxx>
32ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
33ddde725dSArmin Le Grand #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
34ddde725dSArmin Le Grand #include <basegfx/curve/b2dcubicbezier.hxx>
35ddde725dSArmin Le Grand #include <basegfx/curve/b2dbeziertools.hxx>
36ddde725dSArmin Le Grand 
37ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
38ddde725dSArmin Le Grand 
39ddde725dSArmin Le Grand namespace svgio
40ddde725dSArmin Le Grand {
41ddde725dSArmin Le Grand     namespace svgreader
42ddde725dSArmin Le Grand     {
43ddde725dSArmin Le Grand         class pathTextBreakupHelper : public drawinglayer::primitive2d::TextBreakupHelper
44ddde725dSArmin Le Grand         {
45ddde725dSArmin Le Grand         private:
46ddde725dSArmin Le Grand             const basegfx::B2DPolygon&      mrPolygon;
47ddde725dSArmin Le Grand             const double                    mfBasegfxPathLength;
48ddde725dSArmin Le Grand             const double                    mfUserToBasegfx;
49ddde725dSArmin Le Grand             double                          mfPosition;
50ddde725dSArmin Le Grand             const basegfx::B2DPoint&        mrTextStart;
51ddde725dSArmin Le Grand 
52ddde725dSArmin Le Grand             const sal_uInt32                mnMaxIndex;
53ddde725dSArmin Le Grand             sal_uInt32                      mnIndex;
54ddde725dSArmin Le Grand             basegfx::B2DCubicBezier         maCurrentSegment;
55ddde725dSArmin Le Grand             basegfx::B2DCubicBezierHelper*  mpB2DCubicBezierHelper;
56ddde725dSArmin Le Grand             double                          mfCurrentSegmentLength;
57ddde725dSArmin Le Grand             double                          mfSegmentStartPosition;
58ddde725dSArmin Le Grand 
59ddde725dSArmin Le Grand         protected:
60ddde725dSArmin Le Grand             /// allow user callback to allow changes to the new TextTransformation. Default
61ddde725dSArmin Le Grand             /// does nothing.
62ddde725dSArmin Le Grand             virtual bool allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength);
63ddde725dSArmin Le Grand 
64ddde725dSArmin Le Grand             void freeB2DCubicBezierHelper();
65ddde725dSArmin Le Grand             basegfx::B2DCubicBezierHelper* getB2DCubicBezierHelper();
66ddde725dSArmin Le Grand             void advanceToPosition(double fNewPosition);
67ddde725dSArmin Le Grand 
68ddde725dSArmin Le Grand         public:
69ddde725dSArmin Le Grand             pathTextBreakupHelper(
70693be7f6SArmin Le Grand                 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D& rSource,
71ddde725dSArmin Le Grand                 const basegfx::B2DPolygon& rPolygon,
72ddde725dSArmin Le Grand                 const double fBasegfxPathLength,
73ddde725dSArmin Le Grand                 const double fUserToBasegfx,
74ddde725dSArmin Le Grand                 double fPosition,
75ddde725dSArmin Le Grand                 const basegfx::B2DPoint& rTextStart);
76ddde725dSArmin Le Grand             virtual ~pathTextBreakupHelper();
77ddde725dSArmin Le Grand 
78ddde725dSArmin Le Grand             // read access to evtl. advanced position
getPosition() const79ddde725dSArmin Le Grand             double getPosition() const { return mfPosition; }
80ddde725dSArmin Le Grand 
81ddde725dSArmin Le Grand             // get length of given text
82ddde725dSArmin Le Grand             double getLength(const rtl::OUString& rText) const;
83ddde725dSArmin Le Grand         };
84ddde725dSArmin Le Grand 
getLength(const rtl::OUString & rText) const85ddde725dSArmin Le Grand         double pathTextBreakupHelper::getLength(const rtl::OUString& rText) const
86ddde725dSArmin Le Grand         {
87ddde725dSArmin Le Grand             const sal_uInt32 nLength(rText.getLength());
88ddde725dSArmin Le Grand 
89ddde725dSArmin Le Grand             if(nLength)
90ddde725dSArmin Le Grand             {
91ddde725dSArmin Le Grand                 return getTextLayouter().getTextWidth(rText, 0, nLength);
92ddde725dSArmin Le Grand             }
93ddde725dSArmin Le Grand 
94ddde725dSArmin Le Grand             return 0.0;
95ddde725dSArmin Le Grand         }
96ddde725dSArmin Le Grand 
freeB2DCubicBezierHelper()97ddde725dSArmin Le Grand         void pathTextBreakupHelper::freeB2DCubicBezierHelper()
98ddde725dSArmin Le Grand         {
99ddde725dSArmin Le Grand             if(mpB2DCubicBezierHelper)
100ddde725dSArmin Le Grand             {
101ddde725dSArmin Le Grand                 delete mpB2DCubicBezierHelper;
102ddde725dSArmin Le Grand                 mpB2DCubicBezierHelper = 0;
103ddde725dSArmin Le Grand             }
104ddde725dSArmin Le Grand         }
105ddde725dSArmin Le Grand 
getB2DCubicBezierHelper()106ddde725dSArmin Le Grand         basegfx::B2DCubicBezierHelper* pathTextBreakupHelper::getB2DCubicBezierHelper()
107ddde725dSArmin Le Grand         {
108ddde725dSArmin Le Grand             if(!mpB2DCubicBezierHelper && maCurrentSegment.isBezier())
109ddde725dSArmin Le Grand             {
110ddde725dSArmin Le Grand                 mpB2DCubicBezierHelper = new basegfx::B2DCubicBezierHelper(maCurrentSegment);
111ddde725dSArmin Le Grand             }
112ddde725dSArmin Le Grand 
113ddde725dSArmin Le Grand             return mpB2DCubicBezierHelper;
114ddde725dSArmin Le Grand         }
115ddde725dSArmin Le Grand 
advanceToPosition(double fNewPosition)116ddde725dSArmin Le Grand         void pathTextBreakupHelper::advanceToPosition(double fNewPosition)
117ddde725dSArmin Le Grand         {
118ddde725dSArmin Le Grand             while(mfSegmentStartPosition + mfCurrentSegmentLength < fNewPosition && mnIndex < mnMaxIndex)
119ddde725dSArmin Le Grand             {
120ddde725dSArmin Le Grand                 mfSegmentStartPosition += mfCurrentSegmentLength;
121ddde725dSArmin Le Grand                 mnIndex++;
122ddde725dSArmin Le Grand 
123ddde725dSArmin Le Grand                 if(mnIndex < mnMaxIndex)
124ddde725dSArmin Le Grand                 {
125ddde725dSArmin Le Grand                     freeB2DCubicBezierHelper();
126ddde725dSArmin Le Grand                     mrPolygon.getBezierSegment(mnIndex % mrPolygon.count(), maCurrentSegment);
127ddde725dSArmin Le Grand                     maCurrentSegment.testAndSolveTrivialBezier();
128ddde725dSArmin Le Grand                     mfCurrentSegmentLength = getB2DCubicBezierHelper()
129ddde725dSArmin Le Grand                         ? getB2DCubicBezierHelper()->getLength()
130ddde725dSArmin Le Grand                         : maCurrentSegment.getLength();
131ddde725dSArmin Le Grand                 }
132ddde725dSArmin Le Grand             }
133ddde725dSArmin Le Grand 
134ddde725dSArmin Le Grand             mfPosition = fNewPosition;
135ddde725dSArmin Le Grand         }
136ddde725dSArmin Le Grand 
pathTextBreakupHelper(const drawinglayer::primitive2d::TextSimplePortionPrimitive2D & rSource,const basegfx::B2DPolygon & rPolygon,const double fBasegfxPathLength,const double fUserToBasegfx,double fPosition,const basegfx::B2DPoint & rTextStart)137ddde725dSArmin Le Grand         pathTextBreakupHelper::pathTextBreakupHelper(
138693be7f6SArmin Le Grand             const drawinglayer::primitive2d::TextSimplePortionPrimitive2D& rSource,
139ddde725dSArmin Le Grand             const basegfx::B2DPolygon& rPolygon,
140ddde725dSArmin Le Grand             const double fBasegfxPathLength,
141ddde725dSArmin Le Grand             const double fUserToBasegfx,
142ddde725dSArmin Le Grand             double fPosition,
143ddde725dSArmin Le Grand             const basegfx::B2DPoint& rTextStart)
144693be7f6SArmin Le Grand         :   drawinglayer::primitive2d::TextBreakupHelper(rSource),
145ddde725dSArmin Le Grand             mrPolygon(rPolygon),
146ddde725dSArmin Le Grand             mfBasegfxPathLength(fBasegfxPathLength),
147ddde725dSArmin Le Grand             mfUserToBasegfx(fUserToBasegfx),
148ddde725dSArmin Le Grand             mfPosition(0.0),
149ddde725dSArmin Le Grand             mrTextStart(rTextStart),
150ddde725dSArmin Le Grand             mnMaxIndex(rPolygon.isClosed() ? rPolygon.count() : rPolygon.count() - 1),
151ddde725dSArmin Le Grand             mnIndex(0),
152ddde725dSArmin Le Grand             maCurrentSegment(),
153ddde725dSArmin Le Grand             mpB2DCubicBezierHelper(0),
154ddde725dSArmin Le Grand             mfCurrentSegmentLength(0.0),
155ddde725dSArmin Le Grand             mfSegmentStartPosition(0.0)
156ddde725dSArmin Le Grand         {
157ddde725dSArmin Le Grand             mrPolygon.getBezierSegment(mnIndex % mrPolygon.count(), maCurrentSegment);
158ddde725dSArmin Le Grand             mfCurrentSegmentLength = maCurrentSegment.getLength();
159ddde725dSArmin Le Grand 
160ddde725dSArmin Le Grand             advanceToPosition(fPosition);
161ddde725dSArmin Le Grand         }
162ddde725dSArmin Le Grand 
~pathTextBreakupHelper()163ddde725dSArmin Le Grand         pathTextBreakupHelper::~pathTextBreakupHelper()
164ddde725dSArmin Le Grand         {
165ddde725dSArmin Le Grand             freeB2DCubicBezierHelper();
166ddde725dSArmin Le Grand         }
167ddde725dSArmin Le Grand 
allowChange(sal_uInt32,basegfx::B2DHomMatrix & rNewTransform,sal_uInt32 nIndex,sal_uInt32 nLength)168e2bf1e9dSArmin Le Grand         bool pathTextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength)
169ddde725dSArmin Le Grand         {
170ddde725dSArmin Le Grand             bool bRetval(false);
171ddde725dSArmin Le Grand 
172693be7f6SArmin Le Grand             if(mfPosition < mfBasegfxPathLength && nLength && mnIndex < mnMaxIndex)
173ddde725dSArmin Le Grand             {
174ddde725dSArmin Le Grand                 const double fSnippetWidth(
175ddde725dSArmin Le Grand                     getTextLayouter().getTextWidth(
176693be7f6SArmin Le Grand                         getSource().getText(),
177ddde725dSArmin Le Grand                         nIndex,
178ddde725dSArmin Le Grand                         nLength));
179ddde725dSArmin Le Grand 
180ddde725dSArmin Le Grand                 if(basegfx::fTools::more(fSnippetWidth, 0.0))
181ddde725dSArmin Le Grand                 {
182693be7f6SArmin Le Grand                     const ::rtl::OUString aText(getSource().getText());
183ddde725dSArmin Le Grand                     const ::rtl::OUString aTrimmedChars(aText.copy(nIndex, nLength).trim());
184ddde725dSArmin Le Grand                     const double fEndPos(mfPosition + fSnippetWidth);
185ddde725dSArmin Le Grand 
186ddde725dSArmin Le Grand                     if(aTrimmedChars.getLength() && (mfPosition < mfBasegfxPathLength || fEndPos > 0.0))
187ddde725dSArmin Le Grand                     {
188ddde725dSArmin Le Grand                         const double fHalfSnippetWidth(fSnippetWidth * 0.5);
189ddde725dSArmin Le Grand 
190ddde725dSArmin Le Grand                         advanceToPosition(mfPosition + fHalfSnippetWidth);
191ddde725dSArmin Le Grand 
192ddde725dSArmin Le Grand                         // create representation for this snippet
193ddde725dSArmin Le Grand                         bRetval = true;
194ddde725dSArmin Le Grand 
195ddde725dSArmin Le Grand                         // get target position and tangent in that pint
196ddde725dSArmin Le Grand                         basegfx::B2DPoint aPosition(0.0, 0.0);
197ddde725dSArmin Le Grand                         basegfx::B2DVector aTangent(0.0, 1.0);
198ddde725dSArmin Le Grand 
199ddde725dSArmin Le Grand                         if(mfPosition < 0.0)
200ddde725dSArmin Le Grand                         {
201ddde725dSArmin Le Grand                             // snippet center is left of first segment, but right edge is on it (SVG allows that)
202ddde725dSArmin Le Grand                             aTangent = maCurrentSegment.getTangent(0.0);
203ddde725dSArmin Le Grand                             aTangent.normalize();
204ddde725dSArmin Le Grand                             aPosition = maCurrentSegment.getStartPoint() + (aTangent * (mfPosition - mfSegmentStartPosition));
205ddde725dSArmin Le Grand                         }
206ddde725dSArmin Le Grand                         else if(mfPosition > mfBasegfxPathLength)
207ddde725dSArmin Le Grand                         {
208ddde725dSArmin Le Grand                             // snippet center is right of last segment, but left edge is on it (SVG allows that)
209ddde725dSArmin Le Grand                             aTangent = maCurrentSegment.getTangent(1.0);
210ddde725dSArmin Le Grand                             aTangent.normalize();
211ddde725dSArmin Le Grand                             aPosition = maCurrentSegment.getEndPoint() + (aTangent * (mfPosition - mfSegmentStartPosition));
212ddde725dSArmin Le Grand                         }
213ddde725dSArmin Le Grand                         else
214ddde725dSArmin Le Grand                         {
215ddde725dSArmin Le Grand                             // snippet center inside segment, interpolate
216ddde725dSArmin Le Grand                             double fBezierDistance(mfPosition - mfSegmentStartPosition);
217ddde725dSArmin Le Grand 
218ddde725dSArmin Le Grand                             if(getB2DCubicBezierHelper())
219ddde725dSArmin Le Grand                             {
220ddde725dSArmin Le Grand                                 // use B2DCubicBezierHelper to bridge the non-linear gap between
221ddde725dSArmin Le Grand                                 // length and bezier distances (if it's a bezier segment)
222ddde725dSArmin Le Grand                                 fBezierDistance = getB2DCubicBezierHelper()->distanceToRelative(fBezierDistance);
223ddde725dSArmin Le Grand                             }
224ddde725dSArmin Le Grand                             else
225ddde725dSArmin Le Grand                             {
226ddde725dSArmin Le Grand                                 // linear relationship, make relative to segment length
227ddde725dSArmin Le Grand                                 fBezierDistance = fBezierDistance / mfCurrentSegmentLength;
228ddde725dSArmin Le Grand                             }
229ddde725dSArmin Le Grand 
230ddde725dSArmin Le Grand                             aPosition = maCurrentSegment.interpolatePoint(fBezierDistance);
231ddde725dSArmin Le Grand                             aTangent = maCurrentSegment.getTangent(fBezierDistance);
232ddde725dSArmin Le Grand                             aTangent.normalize();
233ddde725dSArmin Le Grand                         }
234ddde725dSArmin Le Grand 
235ddde725dSArmin Le Grand                         // detect evtl. hor/ver translations (depends on text direction)
236ddde725dSArmin Le Grand                         const basegfx::B2DPoint aBasePoint(rNewTransform * basegfx::B2DPoint(0.0, 0.0));
237ddde725dSArmin Le Grand                         const basegfx::B2DVector aOffset(aBasePoint - mrTextStart);
238ddde725dSArmin Le Grand 
239ddde725dSArmin Le Grand                         if(!basegfx::fTools::equalZero(aOffset.getY()))
240ddde725dSArmin Le Grand                         {
241ddde725dSArmin Le Grand                             // ...and apply
242ddde725dSArmin Le Grand                             aPosition.setY(aPosition.getY() + aOffset.getY());
243ddde725dSArmin Le Grand                         }
244ddde725dSArmin Le Grand 
245ddde725dSArmin Le Grand                         // move target position from snippet center to left text start
246ddde725dSArmin Le Grand                         aPosition -= fHalfSnippetWidth * aTangent;
247ddde725dSArmin Le Grand 
248ddde725dSArmin Le Grand                         // remove current translation
249ddde725dSArmin Le Grand                         rNewTransform.translate(-aBasePoint.getX(), -aBasePoint.getY());
250ddde725dSArmin Le Grand 
251ddde725dSArmin Le Grand                         // rotate due to tangent
252ddde725dSArmin Le Grand                         rNewTransform.rotate(atan2(aTangent.getY(), aTangent.getX()));
253ddde725dSArmin Le Grand 
254ddde725dSArmin Le Grand                         // add new translation
255ddde725dSArmin Le Grand                         rNewTransform.translate(aPosition.getX(), aPosition.getY());
256ddde725dSArmin Le Grand                     }
257ddde725dSArmin Le Grand 
258ddde725dSArmin Le Grand                     // advance to end
259ddde725dSArmin Le Grand                     advanceToPosition(fEndPos);
260ddde725dSArmin Le Grand                 }
261ddde725dSArmin Le Grand             }
262ddde725dSArmin Le Grand 
263ddde725dSArmin Le Grand             return bRetval;
264ddde725dSArmin Le Grand         }
265ddde725dSArmin Le Grand 
266ddde725dSArmin Le Grand     } // end of namespace svgreader
267ddde725dSArmin Le Grand } // end of namespace svgio
268ddde725dSArmin Le Grand 
269ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
270ddde725dSArmin Le Grand 
271ddde725dSArmin Le Grand namespace svgio
272ddde725dSArmin Le Grand {
273ddde725dSArmin Le Grand     namespace svgreader
274ddde725dSArmin Le Grand     {
SvgTextPathNode(SvgDocument & rDocument,SvgNode * pParent)275ddde725dSArmin Le Grand         SvgTextPathNode::SvgTextPathNode(
276ddde725dSArmin Le Grand             SvgDocument& rDocument,
277ddde725dSArmin Le Grand             SvgNode* pParent)
278ddde725dSArmin Le Grand         :   SvgNode(SVGTokenTextPath, rDocument, pParent),
279ddde725dSArmin Le Grand             maSvgStyleAttributes(*this),
280ddde725dSArmin Le Grand             maXLink(),
281ddde725dSArmin Le Grand             maStartOffset(),
282ddde725dSArmin Le Grand             mbMethod(true),
283ddde725dSArmin Le Grand             mbSpacing(false)
284ddde725dSArmin Le Grand         {
285ddde725dSArmin Le Grand         }
286ddde725dSArmin Le Grand 
~SvgTextPathNode()287ddde725dSArmin Le Grand         SvgTextPathNode::~SvgTextPathNode()
288ddde725dSArmin Le Grand         {
289ddde725dSArmin Le Grand         }
290ddde725dSArmin Le Grand 
getSvgStyleAttributes() const291ddde725dSArmin Le Grand         const SvgStyleAttributes* SvgTextPathNode::getSvgStyleAttributes() const
292ddde725dSArmin Le Grand         {
293ddde725dSArmin Le Grand             return &maSvgStyleAttributes;
294ddde725dSArmin Le Grand         }
295ddde725dSArmin Le Grand 
parseAttribute(const rtl::OUString & rTokenName,SVGToken aSVGToken,const rtl::OUString & aContent)296ddde725dSArmin Le Grand         void SvgTextPathNode::parseAttribute(const rtl::OUString& rTokenName, SVGToken aSVGToken, const rtl::OUString& aContent)
297ddde725dSArmin Le Grand         {
298ddde725dSArmin Le Grand             // call parent
299ddde725dSArmin Le Grand             SvgNode::parseAttribute(rTokenName, aSVGToken, aContent);
300ddde725dSArmin Le Grand 
301ddde725dSArmin Le Grand             // read style attributes
302*52cb04b8SArmin Le Grand             maSvgStyleAttributes.parseStyleAttribute(rTokenName, aSVGToken, aContent, false);
303ddde725dSArmin Le Grand 
304ddde725dSArmin Le Grand             // parse own
305ddde725dSArmin Le Grand             switch(aSVGToken)
306ddde725dSArmin Le Grand             {
307ddde725dSArmin Le Grand                 case SVGTokenStyle:
308ddde725dSArmin Le Grand                 {
3099d01bcdeSArmin Le Grand                     readLocalCssStyle(aContent);
310ddde725dSArmin Le Grand                     break;
311ddde725dSArmin Le Grand                 }
312ddde725dSArmin Le Grand                 case SVGTokenStartOffset:
313ddde725dSArmin Le Grand                 {
314ddde725dSArmin Le Grand                     SvgNumber aNum;
315ddde725dSArmin Le Grand 
316ddde725dSArmin Le Grand                     if(readSingleNumber(aContent, aNum))
317ddde725dSArmin Le Grand                     {
318ddde725dSArmin Le Grand                         if(aNum.isPositive())
319ddde725dSArmin Le Grand                         {
320ddde725dSArmin Le Grand                             setStartOffset(aNum);
321ddde725dSArmin Le Grand                         }
322ddde725dSArmin Le Grand                     }
323ddde725dSArmin Le Grand                     break;
324ddde725dSArmin Le Grand                 }
325ddde725dSArmin Le Grand                 case SVGTokenMethod:
326ddde725dSArmin Le Grand                 {
327ddde725dSArmin Le Grand                     if(aContent.getLength())
328ddde725dSArmin Le Grand                     {
329ddde725dSArmin Le Grand                         static rtl::OUString aStrAlign(rtl::OUString::createFromAscii("align"));
330ddde725dSArmin Le Grand                         static rtl::OUString aStrStretch(rtl::OUString::createFromAscii("stretch"));
331ddde725dSArmin Le Grand 
332ddde725dSArmin Le Grand                         if(aContent.match(aStrAlign))
333ddde725dSArmin Le Grand                         {
334ddde725dSArmin Le Grand                             setMethod(true);
335ddde725dSArmin Le Grand                         }
336ddde725dSArmin Le Grand                         else if(aContent.match(aStrStretch))
337ddde725dSArmin Le Grand                         {
338ddde725dSArmin Le Grand                             setMethod(false);
339ddde725dSArmin Le Grand                         }
340ddde725dSArmin Le Grand                     }
341ddde725dSArmin Le Grand                     break;
342ddde725dSArmin Le Grand                 }
343ddde725dSArmin Le Grand                 case SVGTokenSpacing:
344ddde725dSArmin Le Grand                 {
345ddde725dSArmin Le Grand                     if(aContent.getLength())
346ddde725dSArmin Le Grand                     {
347ddde725dSArmin Le Grand                         static rtl::OUString aStrAuto(rtl::OUString::createFromAscii("auto"));
348ddde725dSArmin Le Grand                         static rtl::OUString aStrExact(rtl::OUString::createFromAscii("exact"));
349ddde725dSArmin Le Grand 
350ddde725dSArmin Le Grand                         if(aContent.match(aStrAuto))
351ddde725dSArmin Le Grand                         {
352ddde725dSArmin Le Grand                             setSpacing(true);
353ddde725dSArmin Le Grand                         }
354ddde725dSArmin Le Grand                         else if(aContent.match(aStrExact))
355ddde725dSArmin Le Grand                         {
356ddde725dSArmin Le Grand                             setSpacing(false);
357ddde725dSArmin Le Grand                         }
358ddde725dSArmin Le Grand                     }
359ddde725dSArmin Le Grand                     break;
360ddde725dSArmin Le Grand                 }
361ddde725dSArmin Le Grand                 case SVGTokenXlinkHref:
362ddde725dSArmin Le Grand                 {
363ddde725dSArmin Le Grand                     const sal_Int32 nLen(aContent.getLength());
364ddde725dSArmin Le Grand 
365ddde725dSArmin Le Grand                     if(nLen && sal_Unicode('#') == aContent[0])
366ddde725dSArmin Le Grand                     {
367ddde725dSArmin Le Grand                         maXLink = aContent.copy(1);
368ddde725dSArmin Le Grand                     }
369ddde725dSArmin Le Grand                     break;
370ddde725dSArmin Le Grand                 }
371e2bf1e9dSArmin Le Grand                 default:
372e2bf1e9dSArmin Le Grand                 {
373e2bf1e9dSArmin Le Grand                     break;
374e2bf1e9dSArmin Le Grand                 }
375ddde725dSArmin Le Grand             }
376ddde725dSArmin Le Grand         }
377ddde725dSArmin Le Grand 
isValid() const378ddde725dSArmin Le Grand         bool SvgTextPathNode::isValid() const
379ddde725dSArmin Le Grand         {
380ddde725dSArmin Le Grand             const SvgPathNode* pSvgPathNode = dynamic_cast< const SvgPathNode* >(getDocument().findSvgNodeById(maXLink));
381ddde725dSArmin Le Grand 
382ddde725dSArmin Le Grand             if(!pSvgPathNode)
383ddde725dSArmin Le Grand             {
384ddde725dSArmin Le Grand                 return false;
385ddde725dSArmin Le Grand             }
386ddde725dSArmin Le Grand 
387ddde725dSArmin Le Grand             const basegfx::B2DPolyPolygon* pPolyPolyPath = pSvgPathNode->getPath();
388ddde725dSArmin Le Grand 
389ddde725dSArmin Le Grand             if(!pPolyPolyPath || !pPolyPolyPath->count())
390ddde725dSArmin Le Grand             {
391ddde725dSArmin Le Grand                 return false;
392ddde725dSArmin Le Grand             }
393ddde725dSArmin Le Grand 
394ddde725dSArmin Le Grand             const basegfx::B2DPolygon aPolygon(pPolyPolyPath->getB2DPolygon(0));
395ddde725dSArmin Le Grand 
396ddde725dSArmin Le Grand             if(!aPolygon.count())
397ddde725dSArmin Le Grand             {
398ddde725dSArmin Le Grand                 return false;
399ddde725dSArmin Le Grand             }
400ddde725dSArmin Le Grand 
401ddde725dSArmin Le Grand             const double fBasegfxPathLength(basegfx::tools::getLength(aPolygon));
402ddde725dSArmin Le Grand 
403ddde725dSArmin Le Grand             if(basegfx::fTools::equalZero(fBasegfxPathLength))
404ddde725dSArmin Le Grand             {
405ddde725dSArmin Le Grand                 return false;
406ddde725dSArmin Le Grand             }
407ddde725dSArmin Le Grand 
408ddde725dSArmin Le Grand             return true;
409ddde725dSArmin Le Grand         }
410ddde725dSArmin Le Grand 
decomposePathNode(const drawinglayer::primitive2d::Primitive2DSequence & rPathContent,drawinglayer::primitive2d::Primitive2DSequence & rTarget,const basegfx::B2DPoint & rTextStart) const411ddde725dSArmin Le Grand         void SvgTextPathNode::decomposePathNode(
412ddde725dSArmin Le Grand             const drawinglayer::primitive2d::Primitive2DSequence& rPathContent,
413ddde725dSArmin Le Grand             drawinglayer::primitive2d::Primitive2DSequence& rTarget,
414ddde725dSArmin Le Grand             const basegfx::B2DPoint& rTextStart) const
415ddde725dSArmin Le Grand         {
416ddde725dSArmin Le Grand             if(rPathContent.hasElements())
417ddde725dSArmin Le Grand             {
418ddde725dSArmin Le Grand                 const SvgPathNode* pSvgPathNode = dynamic_cast< const SvgPathNode* >(getDocument().findSvgNodeById(maXLink));
419ddde725dSArmin Le Grand 
420ddde725dSArmin Le Grand                 if(pSvgPathNode)
421ddde725dSArmin Le Grand                 {
422ddde725dSArmin Le Grand                     const basegfx::B2DPolyPolygon* pPolyPolyPath = pSvgPathNode->getPath();
423ddde725dSArmin Le Grand 
424ddde725dSArmin Le Grand                     if(pPolyPolyPath && pPolyPolyPath->count())
425ddde725dSArmin Le Grand                     {
426ddde725dSArmin Le Grand                         basegfx::B2DPolygon aPolygon(pPolyPolyPath->getB2DPolygon(0));
427ddde725dSArmin Le Grand 
428ddde725dSArmin Le Grand                         if(pSvgPathNode->getTransform())
429ddde725dSArmin Le Grand                         {
430ddde725dSArmin Le Grand                             aPolygon.transform(*pSvgPathNode->getTransform());
431ddde725dSArmin Le Grand                         }
432ddde725dSArmin Le Grand 
433ddde725dSArmin Le Grand                         const double fBasegfxPathLength(basegfx::tools::getLength(aPolygon));
434ddde725dSArmin Le Grand 
435ddde725dSArmin Le Grand                         if(!basegfx::fTools::equalZero(fBasegfxPathLength))
436ddde725dSArmin Le Grand                         {
437ddde725dSArmin Le Grand                             double fUserToBasegfx(1.0); // multiply: user->basegfx, divide: basegfx->user
438ddde725dSArmin Le Grand 
439ddde725dSArmin Le Grand                             if(pSvgPathNode->getPathLength().isSet())
440ddde725dSArmin Le Grand                             {
441ddde725dSArmin Le Grand                                 const double fUserLength(pSvgPathNode->getPathLength().solve(*this, length));
442ddde725dSArmin Le Grand 
443ddde725dSArmin Le Grand                                 if(fUserLength > 0.0 && !basegfx::fTools::equal(fUserLength, fBasegfxPathLength))
444ddde725dSArmin Le Grand                                 {
445ddde725dSArmin Le Grand                                     fUserToBasegfx = fUserLength / fBasegfxPathLength;
446ddde725dSArmin Le Grand                                 }
447ddde725dSArmin Le Grand                             }
448ddde725dSArmin Le Grand 
449ddde725dSArmin Le Grand                             double fPosition(0.0);
450ddde725dSArmin Le Grand 
451ddde725dSArmin Le Grand                             if(getStartOffset().isSet())
452ddde725dSArmin Le Grand                             {
453ddde725dSArmin Le Grand                                 if(Unit_percent == getStartOffset().getUnit())
454ddde725dSArmin Le Grand                                 {
455ddde725dSArmin Le Grand                                     // percent are relative to path length
456ddde725dSArmin Le Grand                                     fPosition = getStartOffset().getNumber() * 0.01 * fBasegfxPathLength;
457ddde725dSArmin Le Grand                                 }
458ddde725dSArmin Le Grand                                 else
459ddde725dSArmin Le Grand                                 {
460ddde725dSArmin Le Grand                                     fPosition = getStartOffset().solve(*this, length) * fUserToBasegfx;
461ddde725dSArmin Le Grand                                 }
462ddde725dSArmin Le Grand                             }
463ddde725dSArmin Le Grand 
464ddde725dSArmin Le Grand                             if(fPosition >= 0.0)
465ddde725dSArmin Le Grand                             {
466ddde725dSArmin Le Grand                                 const sal_Int32 nLength(rPathContent.getLength());
467ddde725dSArmin Le Grand                                 sal_Int32 nCurrent(0);
468ddde725dSArmin Le Grand 
469ddde725dSArmin Le Grand                                 while(fPosition < fBasegfxPathLength && nCurrent < nLength)
470ddde725dSArmin Le Grand                                 {
471ddde725dSArmin Le Grand                                     const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pCandidate = 0;
472ddde725dSArmin Le Grand                                     const drawinglayer::primitive2d::Primitive2DReference xReference(rPathContent[nCurrent]);
473ddde725dSArmin Le Grand 
474ddde725dSArmin Le Grand                                     if(xReference.is())
475ddde725dSArmin Le Grand                                     {
476ddde725dSArmin Le Grand                                         pCandidate = dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(xReference.get());
477ddde725dSArmin Le Grand                                     }
478ddde725dSArmin Le Grand 
479ddde725dSArmin Le Grand                                     if(pCandidate)
480ddde725dSArmin Le Grand                                     {
481693be7f6SArmin Le Grand                                         const pathTextBreakupHelper aPathTextBreakupHelper(
482693be7f6SArmin Le Grand                                             *pCandidate,
483ddde725dSArmin Le Grand                                             aPolygon,
484ddde725dSArmin Le Grand                                             fBasegfxPathLength,
485ddde725dSArmin Le Grand                                             fUserToBasegfx,
486ddde725dSArmin Le Grand                                             fPosition,
487ddde725dSArmin Le Grand                                             rTextStart);
488ddde725dSArmin Le Grand 
489ddde725dSArmin Le Grand                                         const drawinglayer::primitive2d::Primitive2DSequence aResult(
490ddde725dSArmin Le Grand                                             aPathTextBreakupHelper.getResult(drawinglayer::primitive2d::BreakupUnit_character));
491ddde725dSArmin Le Grand 
492ddde725dSArmin Le Grand                                         if(aResult.hasElements())
493ddde725dSArmin Le Grand                                         {
494ddde725dSArmin Le Grand                                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aResult);
495ddde725dSArmin Le Grand                                         }
496ddde725dSArmin Le Grand 
497ddde725dSArmin Le Grand                                         // advance position to consumed
498ddde725dSArmin Le Grand                                         fPosition = aPathTextBreakupHelper.getPosition();
499ddde725dSArmin Le Grand                                     }
500ddde725dSArmin Le Grand 
501ddde725dSArmin Le Grand                                     nCurrent++;
502ddde725dSArmin Le Grand                                 }
503ddde725dSArmin Le Grand                             }
504ddde725dSArmin Le Grand                         }
505ddde725dSArmin Le Grand                     }
506ddde725dSArmin Le Grand                 }
507ddde725dSArmin Le Grand             }
508ddde725dSArmin Le Grand         }
509ddde725dSArmin Le Grand 
510ddde725dSArmin Le Grand     } // end of namespace svgreader
511ddde725dSArmin Le Grand } // end of namespace svgio
512ddde725dSArmin Le Grand 
513ddde725dSArmin Le Grand //////////////////////////////////////////////////////////////////////////////
514ddde725dSArmin Le Grand // eof
515