1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_svgio.hxx"
24 
25 #include <svgio/svgreader/svgcharacternode.hxx>
26 #include <svgio/svgreader/svgstyleattributes.hxx>
27 #include <drawinglayer/attribute/fontattribute.hxx>
28 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
30 #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
31 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
33 
34 //////////////////////////////////////////////////////////////////////////////
35 
36 namespace svgio
37 {
38     namespace svgreader
39     {
SvgTextPositions()40         SvgTextPositions::SvgTextPositions()
41         :   maX(),
42             maY(),
43             maDx(),
44             maDy(),
45             maRotate(),
46             maTextLength(),
47             mbLengthAdjust(true)
48         {
49         }
50 
parseTextPositionAttributes(const rtl::OUString &,SVGToken aSVGToken,const rtl::OUString & aContent)51         void SvgTextPositions::parseTextPositionAttributes(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent)
52         {
53             // parse own
54             switch(aSVGToken)
55             {
56                 case SVGTokenX:
57                 {
58                     if(aContent.getLength())
59                     {
60                         SvgNumberVector aVector;
61 
62                         if(readSvgNumberVector(aContent, aVector))
63                         {
64                             setX(aVector);
65                         }
66                     }
67                     break;
68                 }
69                 case SVGTokenY:
70                 {
71                     if(aContent.getLength())
72                     {
73                         SvgNumberVector aVector;
74 
75                         if(readSvgNumberVector(aContent, aVector))
76                         {
77                             setY(aVector);
78                         }
79                     }
80                     break;
81                 }
82                 case SVGTokenDx:
83                 {
84                     if(aContent.getLength())
85                     {
86                         SvgNumberVector aVector;
87 
88                         if(readSvgNumberVector(aContent, aVector))
89                         {
90                             setDx(aVector);
91                         }
92                     }
93                     break;
94                 }
95                 case SVGTokenDy:
96                 {
97                     if(aContent.getLength())
98                     {
99                         SvgNumberVector aVector;
100 
101                         if(readSvgNumberVector(aContent, aVector))
102                         {
103                             setDy(aVector);
104                         }
105                     }
106                     break;
107                 }
108                 case SVGTokenRotate:
109                 {
110                     if(aContent.getLength())
111                     {
112                         SvgNumberVector aVector;
113 
114                         if(readSvgNumberVector(aContent, aVector))
115                         {
116                             setRotate(aVector);
117                         }
118                     }
119                     break;
120                 }
121                 case SVGTokenTextLength:
122                 {
123                     SvgNumber aNum;
124 
125                     if(readSingleNumber(aContent, aNum))
126                     {
127                         if(aNum.isPositive())
128                         {
129                             setTextLength(aNum);
130                         }
131                     }
132                     break;
133                 }
134                 case SVGTokenLengthAdjust:
135                 {
136                     if(aContent.getLength())
137                     {
138                         static rtl::OUString aStrSpacing(rtl::OUString::createFromAscii("spacing"));
139                         static rtl::OUString aStrSpacingAndGlyphs(rtl::OUString::createFromAscii("spacingAndGlyphs"));
140 
141                         if(aContent.match(aStrSpacing))
142                         {
143                             setLengthAdjust(true);
144                         }
145                         else if(aContent.match(aStrSpacingAndGlyphs))
146                         {
147                             setLengthAdjust(false);
148                         }
149                     }
150                     break;
151                 }
152                 default:
153                 {
154                     break;
155                 }
156             }
157         }
158 
159     } // end of namespace svgreader
160 } // end of namespace svgio
161 
162 //////////////////////////////////////////////////////////////////////////////
163 
164 namespace svgio
165 {
166     namespace svgreader
167     {
168         class localTextBreakupHelper : public drawinglayer::primitive2d::TextBreakupHelper
169         {
170         private:
171             SvgTextPosition&                    mrSvgTextPosition;
172 
173         protected:
174             /// allow user callback to allow changes to the new TextTransformation. Default
175             /// does nothing.
176             virtual bool allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength);
177 
178         public:
localTextBreakupHelper(const drawinglayer::primitive2d::TextSimplePortionPrimitive2D & rSource,SvgTextPosition & rSvgTextPosition)179             localTextBreakupHelper(
180                 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D& rSource,
181                 SvgTextPosition& rSvgTextPosition)
182             :   drawinglayer::primitive2d::TextBreakupHelper(rSource),
183                 mrSvgTextPosition(rSvgTextPosition)
184             {
185             }
186         };
187 
allowChange(sal_uInt32,basegfx::B2DHomMatrix & rNewTransform,sal_uInt32,sal_uInt32)188         bool localTextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/)
189         {
190             const double fRotation(mrSvgTextPosition.consumeRotation());
191 
192             if(0.0 != fRotation)
193             {
194                 const basegfx::B2DPoint aBasePoint(rNewTransform * basegfx::B2DPoint(0.0, 0.0));
195 
196                 rNewTransform.translate(-aBasePoint.getX(), -aBasePoint.getY());
197                 rNewTransform.rotate(fRotation);
198                 rNewTransform.translate(aBasePoint.getX(), aBasePoint.getY());
199             }
200 
201             return true;
202         }
203 
204     } // end of namespace svgreader
205 } // end of namespace svgio
206 
207 //////////////////////////////////////////////////////////////////////////////
208 
209 namespace svgio
210 {
211     namespace svgreader
212     {
SvgCharacterNode(SvgDocument & rDocument,SvgNode * pParent,const rtl::OUString & rText)213         SvgCharacterNode::SvgCharacterNode(
214             SvgDocument& rDocument,
215             SvgNode* pParent,
216             const rtl::OUString& rText)
217         :   SvgNode(SVGTokenCharacter, rDocument, pParent),
218             maText(rText)
219         {
220         }
221 
~SvgCharacterNode()222         SvgCharacterNode::~SvgCharacterNode()
223         {
224         }
225 
getSvgStyleAttributes() const226         const SvgStyleAttributes* SvgCharacterNode::getSvgStyleAttributes() const
227         {
228             // no own style, use parent's
229             if(getParent())
230             {
231                 return getParent()->getSvgStyleAttributes();
232             }
233             else
234             {
235                 return 0;
236             }
237         }
238 
createSimpleTextPrimitive(SvgTextPosition & rSvgTextPosition,const SvgStyleAttributes & rSvgStyleAttributes) const239         drawinglayer::primitive2d::TextSimplePortionPrimitive2D* SvgCharacterNode::createSimpleTextPrimitive(
240             SvgTextPosition& rSvgTextPosition,
241             const SvgStyleAttributes& rSvgStyleAttributes) const
242         {
243             // prepare retval, index and length
244             drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pRetval = 0;
245             sal_uInt32 nIndex(0);
246             sal_uInt32 nLength(getText().getLength());
247 
248             if(nLength)
249             {
250                 // prepare FontAttribute
251                 rtl::OUString aFontFamily = rSvgStyleAttributes.getFontFamily().empty() ?
252                     rtl::OUString(rtl::OUString::createFromAscii("Times New Roman")) :
253                     rSvgStyleAttributes.getFontFamily()[0];
254 
255                 // #122324# if the FontFamily name ends on ' embedded' it is probably a re-import
256                 // of a SVG export with fiont embedding. Remove this to make font matching work. This
257                 // is pretty safe since there should be no font family names ending on ' embedded'.
258                 // Remove again when FontEmbedding is implemented in SVG import
259                 if(aFontFamily.endsWithAsciiL(" embedded", 9))
260                 {
261                     aFontFamily = aFontFamily.copy(0, aFontFamily.getLength() - 9);
262                 }
263 
264                 const ::FontWeight nFontWeight(getVclFontWeight(rSvgStyleAttributes.getFontWeight()));
265                 bool bSymbol(false);
266                 bool bVertical(false);
267                 bool bItalic(FontStyle_italic == rSvgStyleAttributes.getFontStyle() || FontStyle_oblique == rSvgStyleAttributes.getFontStyle());
268                 bool bMonospaced(false);
269                 bool bOutline(false);
270                 bool bRTL(false);
271                 bool bBiDiStrong(false);
272 
273                 const drawinglayer::attribute::FontAttribute aFontAttribute(
274                     aFontFamily,
275                     rtl::OUString(),
276                     nFontWeight,
277                     bSymbol,
278                     bVertical,
279                     bItalic,
280                     bMonospaced,
281                     bOutline,
282                     bRTL,
283                     bBiDiStrong);
284 
285                 // prepare FontSize
286                 double fFontWidth(rSvgStyleAttributes.getFontSize().solve(*this, length));
287                 double fFontHeight(fFontWidth);
288 
289                 // prepare locale
290                 ::com::sun::star::lang::Locale aLocale;
291 
292                 // prepare TextLayouterDevice
293                 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
294                 aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale);
295 
296                 // prepare TextArray
297                 ::std::vector< double > aTextArray(rSvgTextPosition.getX());
298 
299                 if(!aTextArray.empty() && aTextArray.size() < nLength)
300                 {
301                     const sal_uInt32 nArray(aTextArray.size());
302 
303                     if(nArray < nLength)
304                     {
305                         double fStartX(0.0);
306 
307                         if(rSvgTextPosition.getParent() && rSvgTextPosition.getParent()->getAbsoluteX())
308                         {
309                             fStartX = rSvgTextPosition.getParent()->getPosition().getX();
310                         }
311                         else
312                         {
313                             fStartX = aTextArray[nArray - 1];
314                         }
315 
316                         ::std::vector< double > aExtendArray(aTextLayouterDevice.getTextArray(getText(), nArray, nLength - nArray));
317                         aTextArray.reserve(nLength);
318 
319                         for(sal_uInt32 a(0); a < aExtendArray.size(); a++)
320                         {
321                             aTextArray.push_back(aExtendArray[a] + fStartX);
322                         }
323                     }
324                 }
325 
326                 // get current TextPosition and TextWidth in units
327                 basegfx::B2DPoint aPosition(rSvgTextPosition.getPosition());
328                 double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength));
329 
330                 // check for user-given TextLength
331                 if(0.0 != rSvgTextPosition.getTextLength()
332                     && !basegfx::fTools::equal(fTextWidth, rSvgTextPosition.getTextLength()))
333                 {
334                     const double fFactor(rSvgTextPosition.getTextLength() / fTextWidth);
335 
336                     if(rSvgTextPosition.getLengthAdjust())
337                     {
338                         // spacing, need to create and expand TextArray
339                         if(aTextArray.empty())
340                         {
341                             aTextArray = aTextLayouterDevice.getTextArray(getText(), nIndex, nLength);
342                         }
343 
344                         for(sal_uInt32 a(0); a < aTextArray.size(); a++)
345                         {
346                             aTextArray[a] *= fFactor;
347                         }
348                     }
349                     else
350                     {
351                         // spacing and glyphs, just apply to FontWidth
352                         fFontWidth *= fFactor;
353                     }
354 
355                     fTextWidth = rSvgTextPosition.getTextLength();
356                 }
357 
358                 // get TextAlign
359                 TextAlign aTextAlign(rSvgStyleAttributes.getTextAlign());
360 
361                 // map TextAnchor to TextAlign, there seems not to be a difference
362                 if(TextAnchor_notset != rSvgStyleAttributes.getTextAnchor())
363                 {
364                     switch(rSvgStyleAttributes.getTextAnchor())
365                     {
366                         case TextAnchor_start:
367                         {
368                             aTextAlign = TextAlign_left;
369                             break;
370                         }
371                         case TextAnchor_middle:
372                         {
373                             aTextAlign = TextAlign_center;
374                             break;
375                         }
376                         case TextAnchor_end:
377                         {
378                             aTextAlign = TextAlign_right;
379                             break;
380                         }
381                         default:
382                         {
383                             break;
384                         }
385                     }
386                 }
387 
388                 // apply TextAlign
389                 switch(aTextAlign)
390                 {
391                     case TextAlign_right:
392                     {
393                         aPosition.setX(aPosition.getX() - fTextWidth);
394                         break;
395                     }
396                     case TextAlign_center:
397                     {
398                         aPosition.setX(aPosition.getX() - (fTextWidth * 0.5));
399                         break;
400                     }
401                     case TextAlign_notset:
402                     case TextAlign_left:
403                     case TextAlign_justify:
404                     {
405                         // TextAlign_notset, TextAlign_left: nothing to do
406                         // TextAlign_justify is not clear currently; handle as TextAlign_left
407                         break;
408                     }
409                 }
410 
411                 // get BaselineShift
412                 const BaselineShift aBaselineShift(rSvgStyleAttributes.getBaselineShift());
413 
414                 // apply BaselineShift
415                 switch(aBaselineShift)
416                 {
417                     case BaselineShift_Sub:
418                     {
419                         aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset());
420                         break;
421                     }
422                     case BaselineShift_Super:
423                     {
424                         aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset());
425                         break;
426                     }
427                     case BaselineShift_Percentage:
428                     case BaselineShift_Length:
429                     {
430                         const SvgNumber aNumber(rSvgStyleAttributes.getBaselineShiftNumber());
431                         const double mfBaselineShift(aNumber.solve(*this, length));
432 
433                         aPosition.setY(aPosition.getY() + mfBaselineShift);
434                         break;
435                     }
436                     default: // BaselineShift_Baseline
437                     {
438                         // nothing to do
439                         break;
440                     }
441                 }
442 
443                 // get fill color
444                 const basegfx::BColor aFill(rSvgStyleAttributes.getFill()
445                     ? *rSvgStyleAttributes.getFill()
446                     : basegfx::BColor(0.0, 0.0, 0.0));
447 
448                 // prepare TextTransformation
449                 basegfx::B2DHomMatrix aTextTransform;
450 
451                 aTextTransform.scale(fFontWidth, fFontHeight);
452                 aTextTransform.translate(aPosition.getX(), aPosition.getY());
453 
454                 // check TextDecoration and if TextDecoratedPortionPrimitive2D is needed
455                 const TextDecoration aDeco(rSvgStyleAttributes.getTextDecoration());
456 
457                 if(TextDecoration_underline == aDeco
458                     || TextDecoration_overline == aDeco
459                     || TextDecoration_line_through == aDeco)
460                 {
461                     // get the fill for decroation as described by SVG. We cannot
462                     // have different stroke colors/definitions for those, though
463                     const SvgStyleAttributes* pDecoDef = rSvgStyleAttributes.getTextDecorationDefiningSvgStyleAttributes();
464                     const basegfx::BColor aDecoColor(pDecoDef && pDecoDef->getFill() ? *pDecoDef->getFill() : aFill);
465 
466                     // create decorated text primitive
467                     pRetval = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
468                         aTextTransform,
469                         getText(),
470                         nIndex,
471                         nLength,
472                         aTextArray,
473                         aFontAttribute,
474                         aLocale,
475                         aFill,
476 
477                         // extra props for decorated
478                         aDecoColor,
479                         aDecoColor,
480                         TextDecoration_overline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
481                         TextDecoration_underline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
482                         false,
483                         TextDecoration_line_through == aDeco ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE,
484                         false,
485                         drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE,
486                         true,
487                         false,
488                         drawinglayer::primitive2d::TEXT_RELIEF_NONE,
489                         false);
490                 }
491                 else
492                 {
493                     // create text primitive
494                     pRetval = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
495                         aTextTransform,
496                         getText(),
497                         nIndex,
498                         nLength,
499                         aTextArray,
500                         aFontAttribute,
501                         aLocale,
502                         aFill);
503                 }
504 
505                 // advance current TextPosition
506                 rSvgTextPosition.setPosition(rSvgTextPosition.getPosition() + basegfx::B2DVector(fTextWidth, 0.0));
507             }
508 
509             return pRetval;
510         }
511 
decomposeTextWithStyle(drawinglayer::primitive2d::Primitive2DSequence & rTarget,SvgTextPosition & rSvgTextPosition,const SvgStyleAttributes & rSvgStyleAttributes) const512         void SvgCharacterNode::decomposeTextWithStyle(
513             drawinglayer::primitive2d::Primitive2DSequence& rTarget,
514             SvgTextPosition& rSvgTextPosition,
515             const SvgStyleAttributes& rSvgStyleAttributes) const
516         {
517             const drawinglayer::primitive2d::Primitive2DReference xRef(
518                 createSimpleTextPrimitive(
519                     rSvgTextPosition,
520                     rSvgStyleAttributes));
521 
522             if(xRef.is())
523             {
524                 if(!rSvgTextPosition.isRotated())
525                 {
526                     drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
527                 }
528                 else
529                 {
530                     // need to apply rotations to each character as given
531                     const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pCandidate =
532                         dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(xRef.get());
533 
534                     if(pCandidate)
535                     {
536                         const localTextBreakupHelper alocalTextBreakupHelper(*pCandidate, rSvgTextPosition);
537                         const drawinglayer::primitive2d::Primitive2DSequence aResult(
538                             alocalTextBreakupHelper.getResult(drawinglayer::primitive2d::BreakupUnit_character));
539 
540                         if(aResult.hasElements())
541                         {
542                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aResult);
543                         }
544 
545                         // also consume for the implied single space
546                         rSvgTextPosition.consumeRotation();
547                     }
548                     else
549                     {
550                         OSL_ENSURE(false, "Used primitive is not a text primitive (!)");
551                     }
552                 }
553             }
554         }
555 
whiteSpaceHandling()556         void SvgCharacterNode::whiteSpaceHandling()
557         {
558             if(XmlSpace_default == getXmlSpace())
559             {
560                 maText = whiteSpaceHandlingDefault(maText);
561             }
562             else
563             {
564                 maText = whiteSpaceHandlingPreserve(maText);
565             }
566         }
567 
addGap()568         void SvgCharacterNode::addGap()
569         {
570             maText += rtl::OUString(sal_Unicode(' '));
571         }
572 
concatenate(const rtl::OUString & rText)573         void SvgCharacterNode::concatenate(const rtl::OUString& rText)
574         {
575             maText += rText;
576         }
577 
decomposeText(drawinglayer::primitive2d::Primitive2DSequence & rTarget,SvgTextPosition & rSvgTextPosition) const578         void SvgCharacterNode::decomposeText(drawinglayer::primitive2d::Primitive2DSequence& rTarget, SvgTextPosition& rSvgTextPosition) const
579         {
580             if(getText().getLength())
581             {
582                 const SvgStyleAttributes* pSvgStyleAttributes = getSvgStyleAttributes();
583 
584                 if(pSvgStyleAttributes)
585                 {
586                     decomposeTextWithStyle(rTarget, rSvgTextPosition, *pSvgStyleAttributes);
587                 }
588             }
589         }
590 
591     } // end of namespace svgreader
592 } // end of namespace svgio
593 
594 //////////////////////////////////////////////////////////////////////////////
595 
596 namespace svgio
597 {
598     namespace svgreader
599     {
SvgTextPosition(SvgTextPosition * pParent,const InfoProvider & rInfoProvider,const SvgTextPositions & rSvgTextPositions)600         SvgTextPosition::SvgTextPosition(
601             SvgTextPosition* pParent,
602             const InfoProvider& rInfoProvider,
603             const SvgTextPositions& rSvgTextPositions)
604         :   mpParent(pParent),
605             maX(), // computed below
606             maY(), // computed below
607             maRotate(solveSvgNumberVector(rSvgTextPositions.getRotate(), rInfoProvider, length)),
608             mfTextLength(0.0),
609             maPosition(), // computed below
610             mnRotationIndex(0),
611             mbLengthAdjust(rSvgTextPositions.getLengthAdjust()),
612             mbAbsoluteX(false),
613             mbAbsoluteY(false)
614         {
615             // get TextLength if provided
616             if(rSvgTextPositions.getTextLength().isSet())
617             {
618                 mfTextLength = rSvgTextPositions.getTextLength().solve(rInfoProvider, length);
619             }
620 
621             // SVG does not really define in which units a �rotate� for Text/TSpan is given,
622             // but it seems to be degrees. Convert here to radians
623             if(!maRotate.empty())
624             {
625                 const double fFactor(F_PI / 180.0);
626 
627                 for(sal_uInt32 a(0); a < maRotate.size(); a++)
628                 {
629                     maRotate[a] *= fFactor;
630                 }
631             }
632 
633             // get text positions X
634             const sal_uInt32 nSizeX(rSvgTextPositions.getX().size());
635 
636             if(nSizeX)
637             {
638                 // we have absolute positions, get first one as current text position X
639                 maPosition.setX(rSvgTextPositions.getX()[0].solve(rInfoProvider, xcoordinate));
640                 mbAbsoluteX = true;
641 
642                 if(nSizeX > 1)
643                 {
644                     // fill deltas to maX
645                     maX.reserve(nSizeX);
646 
647                     for(sal_uInt32 a(1); a < nSizeX; a++)
648                     {
649                         maX.push_back(rSvgTextPositions.getX()[a].solve(rInfoProvider, xcoordinate) - maPosition.getX());
650                     }
651                 }
652             }
653             else
654             {
655                 // no absolute position, get from parent
656                 if(pParent)
657                 {
658                     maPosition.setX(pParent->getPosition().getX());
659                 }
660 
661                 const sal_uInt32 nSizeDx(rSvgTextPositions.getDx().size());
662 
663                 if(nSizeDx)
664                 {
665                     // relative positions given, translate position derived from parent
666                     maPosition.setX(maPosition.getX() + rSvgTextPositions.getDx()[0].solve(rInfoProvider, xcoordinate));
667 
668                     if(nSizeDx > 1)
669                     {
670                         // fill deltas to maX
671                         maX.reserve(nSizeDx);
672 
673                         for(sal_uInt32 a(1); a < nSizeDx; a++)
674                         {
675                             maX.push_back(rSvgTextPositions.getDx()[a].solve(rInfoProvider, xcoordinate));
676                         }
677                     }
678                 }
679             }
680 
681             // get text positions Y
682             const sal_uInt32 nSizeY(rSvgTextPositions.getY().size());
683 
684             if(nSizeY)
685             {
686                 // we have absolute positions, get first one as current text position Y
687                 maPosition.setY(rSvgTextPositions.getY()[0].solve(rInfoProvider, ycoordinate));
688                 mbAbsoluteX = true;
689 
690                 if(nSizeY > 1)
691                 {
692                     // fill deltas to maY
693                     maY.reserve(nSizeY);
694 
695                     for(sal_uInt32 a(1); a < nSizeY; a++)
696                     {
697                         maY.push_back(rSvgTextPositions.getY()[a].solve(rInfoProvider, ycoordinate) - maPosition.getY());
698                     }
699                 }
700             }
701             else
702             {
703                 // no absolute position, get from parent
704                 if(pParent)
705                 {
706                     maPosition.setY(pParent->getPosition().getY());
707                 }
708 
709                 const sal_uInt32 nSizeDy(rSvgTextPositions.getDy().size());
710 
711                 if(nSizeDy)
712                 {
713                     // relative positions given, translate position derived from parent
714                     maPosition.setY(maPosition.getY() + rSvgTextPositions.getDy()[0].solve(rInfoProvider, ycoordinate));
715 
716                     if(nSizeDy > 1)
717                     {
718                         // fill deltas to maY
719                         maY.reserve(nSizeDy);
720 
721                         for(sal_uInt32 a(1); a < nSizeDy; a++)
722                         {
723                             maY.push_back(rSvgTextPositions.getDy()[a].solve(rInfoProvider, ycoordinate));
724                         }
725                     }
726                 }
727             }
728         }
729 
isRotated() const730         bool SvgTextPosition::isRotated() const
731         {
732             if(maRotate.empty())
733             {
734                 if(getParent())
735                 {
736                     return getParent()->isRotated();
737                 }
738                 else
739                 {
740                     return false;
741                 }
742             }
743             else
744             {
745                 return true;
746             }
747         }
748 
consumeRotation()749         double SvgTextPosition::consumeRotation()
750         {
751             double fRetval(0.0);
752 
753             if(maRotate.empty())
754             {
755                 if(getParent())
756                 {
757                     fRetval = mpParent->consumeRotation();
758                 }
759                 else
760                 {
761                     fRetval = 0.0;
762                 }
763             }
764             else
765             {
766                 const sal_uInt32 nSize(maRotate.size());
767 
768                 if(mnRotationIndex < nSize)
769                 {
770                     fRetval = maRotate[mnRotationIndex++];
771                 }
772                 else
773                 {
774                     fRetval = maRotate[nSize - 1];
775                 }
776             }
777 
778             return fRetval;
779         }
780 
781     } // end of namespace svgreader
782 } // end of namespace svgio
783 
784 //////////////////////////////////////////////////////////////////////////////
785 // eof
786