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