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     {
40         SvgTextPositions::SvgTextPositions()
41         :   maX(),
42             maY(),
43             maDx(),
44             maDy(),
45             maRotate(),
46             maTextLength(),
47             mbLengthAdjust(true)
48         {
49         }
50 
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:
179             localTextBreakupHelper(
180                 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D& rSource,
181                 SvgTextPosition& rSvgTextPosition)
182             :   drawinglayer::primitive2d::TextBreakupHelper(rSource),
183                 mrSvgTextPosition(rSvgTextPosition)
184             {
185             }
186         };
187 
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     {
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 
222         SvgCharacterNode::~SvgCharacterNode()
223         {
224         }
225 
226         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 
239         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 fill color
412                 const basegfx::BColor aFill(rSvgStyleAttributes.getFill()
413                     ? *rSvgStyleAttributes.getFill()
414                     : basegfx::BColor(0.0, 0.0, 0.0));
415 
416                 // prepare TextTransformation
417                 basegfx::B2DHomMatrix aTextTransform;
418 
419                 aTextTransform.scale(fFontWidth, fFontHeight);
420                 aTextTransform.translate(aPosition.getX(), aPosition.getY());
421 
422                 // check TextDecoration and if TextDecoratedPortionPrimitive2D is needed
423                 const TextDecoration aDeco(rSvgStyleAttributes.getTextDecoration());
424 
425                 if(TextDecoration_underline == aDeco
426                     || TextDecoration_overline == aDeco
427                     || TextDecoration_line_through == aDeco)
428                 {
429                     // get the fill for decroation as described by SVG. We cannot
430                     // have different stroke colors/definitions for those, though
431                     const SvgStyleAttributes* pDecoDef = rSvgStyleAttributes.getTextDecorationDefiningSvgStyleAttributes();
432                     const basegfx::BColor aDecoColor(pDecoDef && pDecoDef->getFill() ? *pDecoDef->getFill() : aFill);
433 
434                     // create decorated text primitive
435                     pRetval = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
436                         aTextTransform,
437                         getText(),
438                         nIndex,
439                         nLength,
440                         aTextArray,
441                         aFontAttribute,
442                         aLocale,
443                         aFill,
444 
445                         // extra props for decorated
446                         aDecoColor,
447                         aDecoColor,
448                         TextDecoration_overline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
449                         TextDecoration_underline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
450                         false,
451                         TextDecoration_line_through == aDeco ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE,
452                         false,
453                         drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE,
454                         true,
455                         false,
456                         drawinglayer::primitive2d::TEXT_RELIEF_NONE,
457                         false);
458                 }
459                 else
460                 {
461                     // create text primitive
462                     pRetval = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
463                         aTextTransform,
464                         getText(),
465                         nIndex,
466                         nLength,
467                         aTextArray,
468                         aFontAttribute,
469                         aLocale,
470                         aFill);
471                 }
472 
473                 // advance current TextPosition
474                 rSvgTextPosition.setPosition(rSvgTextPosition.getPosition() + basegfx::B2DVector(fTextWidth, 0.0));
475             }
476 
477             return pRetval;
478         }
479 
480         void SvgCharacterNode::decomposeTextWithStyle(
481             drawinglayer::primitive2d::Primitive2DSequence& rTarget,
482             SvgTextPosition& rSvgTextPosition,
483             const SvgStyleAttributes& rSvgStyleAttributes) const
484         {
485             const drawinglayer::primitive2d::Primitive2DReference xRef(
486                 createSimpleTextPrimitive(
487                     rSvgTextPosition,
488                     rSvgStyleAttributes));
489 
490             if(xRef.is())
491             {
492                 if(!rSvgTextPosition.isRotated())
493                 {
494                     drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef);
495                 }
496                 else
497                 {
498                     // need to apply rotations to each character as given
499                     const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pCandidate =
500                         dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(xRef.get());
501 
502                     if(pCandidate)
503                     {
504                         const localTextBreakupHelper alocalTextBreakupHelper(*pCandidate, rSvgTextPosition);
505                         const drawinglayer::primitive2d::Primitive2DSequence aResult(
506                             alocalTextBreakupHelper.getResult(drawinglayer::primitive2d::BreakupUnit_character));
507 
508                         if(aResult.hasElements())
509                         {
510                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aResult);
511                         }
512 
513                         // also consume for the implied single space
514                         rSvgTextPosition.consumeRotation();
515                     }
516                     else
517                     {
518                         OSL_ENSURE(false, "Used primitive is not a text primitive (!)");
519                     }
520                 }
521             }
522         }
523 
524         void SvgCharacterNode::whiteSpaceHandling()
525         {
526             if(XmlSpace_default == getXmlSpace())
527             {
528                 maText = whiteSpaceHandlingDefault(maText);
529             }
530             else
531             {
532                 maText = whiteSpaceHandlingPreserve(maText);
533             }
534         }
535 
536         void SvgCharacterNode::addGap()
537         {
538             maText += rtl::OUString(sal_Unicode(' '));
539         }
540 
541         void SvgCharacterNode::concatenate(const rtl::OUString& rText)
542         {
543             maText += rText;
544         }
545 
546         void SvgCharacterNode::decomposeText(drawinglayer::primitive2d::Primitive2DSequence& rTarget, SvgTextPosition& rSvgTextPosition) const
547         {
548             if(getText().getLength())
549             {
550                 const SvgStyleAttributes* pSvgStyleAttributes = getSvgStyleAttributes();
551 
552                 if(pSvgStyleAttributes)
553                 {
554                     decomposeTextWithStyle(rTarget, rSvgTextPosition, *pSvgStyleAttributes);
555                 }
556             }
557         }
558 
559     } // end of namespace svgreader
560 } // end of namespace svgio
561 
562 //////////////////////////////////////////////////////////////////////////////
563 
564 namespace svgio
565 {
566     namespace svgreader
567     {
568         SvgTextPosition::SvgTextPosition(
569             SvgTextPosition* pParent,
570             const InfoProvider& rInfoProvider,
571             const SvgTextPositions& rSvgTextPositions)
572         :   mpParent(pParent),
573             maX(), // computed below
574             maY(), // computed below
575             maRotate(solveSvgNumberVector(rSvgTextPositions.getRotate(), rInfoProvider, length)),
576             mfTextLength(0.0),
577             maPosition(), // computed below
578             mnRotationIndex(0),
579             mbLengthAdjust(rSvgTextPositions.getLengthAdjust()),
580             mbAbsoluteX(false),
581             mbAbsoluteY(false)
582         {
583             // get TextLength if provided
584             if(rSvgTextPositions.getTextLength().isSet())
585             {
586                 mfTextLength = rSvgTextPositions.getTextLength().solve(rInfoProvider, length);
587             }
588 
589             // SVG does not really define in which units a �rotate� for Text/TSpan is given,
590             // but it seems to be degrees. Convert here to radians
591             if(!maRotate.empty())
592             {
593                 const double fFactor(F_PI / 180.0);
594 
595                 for(sal_uInt32 a(0); a < maRotate.size(); a++)
596                 {
597                     maRotate[a] *= fFactor;
598                 }
599             }
600 
601             // get text positions X
602             const sal_uInt32 nSizeX(rSvgTextPositions.getX().size());
603 
604             if(nSizeX)
605             {
606                 // we have absolute positions, get first one as current text position X
607                 maPosition.setX(rSvgTextPositions.getX()[0].solve(rInfoProvider, xcoordinate));
608                 mbAbsoluteX = true;
609 
610                 if(nSizeX > 1)
611                 {
612                     // fill deltas to maX
613                     maX.reserve(nSizeX);
614 
615                     for(sal_uInt32 a(1); a < nSizeX; a++)
616                     {
617                         maX.push_back(rSvgTextPositions.getX()[a].solve(rInfoProvider, xcoordinate) - maPosition.getX());
618                     }
619                 }
620             }
621             else
622             {
623                 // no absolute position, get from parent
624                 if(pParent)
625                 {
626                     maPosition.setX(pParent->getPosition().getX());
627                 }
628 
629                 const sal_uInt32 nSizeDx(rSvgTextPositions.getDx().size());
630 
631                 if(nSizeDx)
632                 {
633                     // relative positions given, translate position derived from parent
634                     maPosition.setX(maPosition.getX() + rSvgTextPositions.getDx()[0].solve(rInfoProvider, xcoordinate));
635 
636                     if(nSizeDx > 1)
637                     {
638                         // fill deltas to maX
639                         maX.reserve(nSizeDx);
640 
641                         for(sal_uInt32 a(1); a < nSizeDx; a++)
642                         {
643                             maX.push_back(rSvgTextPositions.getDx()[a].solve(rInfoProvider, xcoordinate));
644                         }
645                     }
646                 }
647             }
648 
649             // get text positions Y
650             const sal_uInt32 nSizeY(rSvgTextPositions.getY().size());
651 
652             if(nSizeY)
653             {
654                 // we have absolute positions, get first one as current text position Y
655                 maPosition.setY(rSvgTextPositions.getY()[0].solve(rInfoProvider, ycoordinate));
656                 mbAbsoluteX = true;
657 
658                 if(nSizeY > 1)
659                 {
660                     // fill deltas to maY
661                     maY.reserve(nSizeY);
662 
663                     for(sal_uInt32 a(1); a < nSizeY; a++)
664                     {
665                         maY.push_back(rSvgTextPositions.getY()[a].solve(rInfoProvider, ycoordinate) - maPosition.getY());
666                     }
667                 }
668             }
669             else
670             {
671                 // no absolute position, get from parent
672                 if(pParent)
673                 {
674                     maPosition.setY(pParent->getPosition().getY());
675                 }
676 
677                 const sal_uInt32 nSizeDy(rSvgTextPositions.getDy().size());
678 
679                 if(nSizeDy)
680                 {
681                     // relative positions given, translate position derived from parent
682                     maPosition.setY(maPosition.getY() + rSvgTextPositions.getDy()[0].solve(rInfoProvider, ycoordinate));
683 
684                     if(nSizeDy > 1)
685                     {
686                         // fill deltas to maY
687                         maY.reserve(nSizeDy);
688 
689                         for(sal_uInt32 a(1); a < nSizeDy; a++)
690                         {
691                             maY.push_back(rSvgTextPositions.getDy()[a].solve(rInfoProvider, ycoordinate));
692                         }
693                     }
694                 }
695             }
696         }
697 
698         bool SvgTextPosition::isRotated() const
699         {
700             if(maRotate.empty())
701             {
702                 if(getParent())
703                 {
704                     return getParent()->isRotated();
705                 }
706                 else
707                 {
708                     return false;
709                 }
710             }
711             else
712             {
713                 return true;
714             }
715         }
716 
717         double SvgTextPosition::consumeRotation()
718         {
719             double fRetval(0.0);
720 
721             if(maRotate.empty())
722             {
723                 if(getParent())
724                 {
725                     fRetval = mpParent->consumeRotation();
726                 }
727                 else
728                 {
729                     fRetval = 0.0;
730                 }
731             }
732             else
733             {
734                 const sal_uInt32 nSize(maRotate.size());
735 
736                 if(mnRotationIndex < nSize)
737                 {
738                     fRetval = maRotate[mnRotationIndex++];
739                 }
740                 else
741                 {
742                     fRetval = maRotate[nSize - 1];
743                 }
744             }
745 
746             return fRetval;
747         }
748 
749     } // end of namespace svgreader
750 } // end of namespace svgio
751 
752 //////////////////////////////////////////////////////////////////////////////
753 // eof
754