xref: /trunk/main/svx/source/svdraw/svdotextpathdecomposition.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 
31 #include <svx/svdotext.hxx>
32 #include <svx/svdoutl.hxx>
33 #include <basegfx/vector/b2dvector.hxx>
34 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
35 #include <basegfx/range/b2drange.hxx>
36 #include <vcl/salbtype.hxx>
37 #include <svl/itemset.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/polygon/b2dpolygon.hxx>
40 #include <algorithm>
41 #include <svx/xtextit.hxx>
42 #include <svx/xftshtit.hxx>
43 #include <vcl/virdev.hxx>
44 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
45 #include <com/sun/star/i18n/ScriptType.hdl>
46 #include <com/sun/star/i18n/XBreakIterator.hpp>
47 #include <comphelper/processfactory.hxx>
48 #include <com/sun/star/i18n/CharacterIteratorMode.hdl>
49 #include <editeng/unolingu.hxx>
50 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
51 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
52 #include <basegfx/color/bcolor.hxx>
53 
54 //////////////////////////////////////////////////////////////////////////////
55 // primitive decomposition helpers
56 
57 #include <basegfx/polygon/b2dlinegeometry.hxx>
58 #include <drawinglayer/attribute/strokeattribute.hxx>
59 #include <svx/xlnclit.hxx>
60 #include <svx/xlntrit.hxx>
61 #include <svx/xlnwtit.hxx>
62 #include <svx/xlinjoit.hxx>
63 #include <svx/xlndsit.hxx>
64 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
65 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
66 #include <editeng/editstat.hxx>
67 #include <svx/unoapi.hxx>
68 #include <drawinglayer/geometry/viewinformation2d.hxx>
69 #include <svx/sdr/attribute/sdrformtextoutlineattribute.hxx>
70 
71 //////////////////////////////////////////////////////////////////////////////
72 
73 using namespace ::com::sun::star::uno;
74 using namespace ::com::sun::star::lang;
75 using namespace ::com::sun::star::i18n;
76 
77 //////////////////////////////////////////////////////////////////////////////
78 // PathTextPortion helper
79 
80 namespace
81 {
82     class impPathTextPortion
83     {
84         basegfx::B2DVector                          maOffset;
85         String                                      maText;
86         xub_StrLen                                  mnTextStart;
87         xub_StrLen                                  mnTextLength;
88         sal_uInt16                                  mnParagraph;
89         xub_StrLen                                  mnIndex;
90         SvxFont                                     maFont;
91         ::std::vector< double >                     maDblDXArray;   // double DXArray, font size independent -> unit coordinate system
92         ::com::sun::star::lang::Locale              maLocale;
93 
94         // bitfield
95         unsigned                                    mbRTL : 1;
96 
97     public:
98         impPathTextPortion(DrawPortionInfo& rInfo)
99         :   maOffset(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()),
100             maText(rInfo.mrText),
101             mnTextStart(rInfo.mnTextStart),
102             mnTextLength(rInfo.mnTextLen),
103             mnParagraph(rInfo.mnPara),
104             mnIndex(rInfo.mnIndex),
105             maFont(rInfo.mrFont),
106             maDblDXArray(),
107             maLocale(rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale()),
108             mbRTL(rInfo.mrFont.IsVertical() ? false : rInfo.IsRTL())
109         {
110             if(mnTextLength && rInfo.mpDXArray)
111             {
112                 maDblDXArray.reserve(mnTextLength);
113 
114                 for(xub_StrLen a(0); a < mnTextLength; a++)
115                 {
116                     maDblDXArray.push_back((double)rInfo.mpDXArray[a]);
117                 }
118             }
119         }
120 
121         // for ::std::sort
122         bool operator<(const impPathTextPortion& rComp) const
123         {
124             if(mnParagraph < rComp.mnParagraph)
125             {
126                 return true;
127             }
128 
129             if(maOffset.getX() < rComp.maOffset.getX())
130             {
131                 return true;
132             }
133 
134             return (maOffset.getY() < rComp.maOffset.getY());
135         }
136 
137         const basegfx::B2DVector& getOffset() const { return maOffset; }
138         const String& getText() const { return maText; }
139         xub_StrLen getTextStart() const { return mnTextStart; }
140         xub_StrLen getTextLength() const { return mnTextLength; }
141         sal_uInt16 getParagraph() const { return mnParagraph; }
142         xub_StrLen getIndex() const { return mnIndex; }
143         const SvxFont& getFont() const { return maFont; }
144         bool isRTL() const { return mbRTL; }
145         const ::std::vector< double >& getDoubleDXArray() const { return maDblDXArray; }
146         const ::com::sun::star::lang::Locale& getLocale() const { return maLocale; }
147 
148         xub_StrLen getPortionIndex(xub_StrLen nIndex, xub_StrLen nLength) const
149         {
150             if(mbRTL)
151             {
152                 return (mnTextStart + (mnTextLength - (nIndex + nLength)));
153             }
154             else
155             {
156                 return (mnTextStart + nIndex);
157             }
158         }
159 
160         double getDisplayLength(xub_StrLen nIndex, xub_StrLen nLength) const
161         {
162             drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
163             double fRetval(0.0);
164 
165             if(maFont.IsVertical())
166             {
167                 fRetval = aTextLayouter.getTextHeight() * (double)nLength;
168             }
169             else
170             {
171                 fRetval = aTextLayouter.getTextWidth(maText, getPortionIndex(nIndex, nLength), nLength);
172             }
173 
174             return fRetval;
175         }
176     };
177 } // end of anonymous namespace
178 
179 //////////////////////////////////////////////////////////////////////////////
180 // TextBreakup helper
181 
182 namespace
183 {
184     class impTextBreakupHandler
185     {
186         SdrOutliner&                                mrOutliner;
187         ::std::vector< impPathTextPortion >         maPathTextPortions;
188 
189         DECL_LINK(decompositionPathTextPrimitive, DrawPortionInfo* );
190 
191     public:
192         impTextBreakupHandler(SdrOutliner& rOutliner)
193         :   mrOutliner(rOutliner)
194         {
195         }
196 
197         const ::std::vector< impPathTextPortion >& decompositionPathTextPrimitive()
198         {
199             // strip portions to maPathTextPortions
200             mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decompositionPathTextPrimitive));
201             mrOutliner.StripPortions();
202 
203             if(!maPathTextPortions.empty())
204             {
205                 // sort portions by paragraph, x and y
206                 ::std::sort(maPathTextPortions.begin(), maPathTextPortions.end());
207             }
208 
209             return maPathTextPortions;
210         }
211     };
212 
213     IMPL_LINK(impTextBreakupHandler, decompositionPathTextPrimitive, DrawPortionInfo*, pInfo)
214     {
215         maPathTextPortions.push_back(impPathTextPortion(*pInfo));
216         return 0;
217     }
218 } // end of anonymous namespace
219 
220 //////////////////////////////////////////////////////////////////////////////
221 // TextBreakup one poly and one paragraph helper
222 
223 namespace
224 {
225     class impPolygonParagraphHandler
226     {
227         const drawinglayer::attribute::SdrFormTextAttribute         maSdrFormTextAttribute; // FormText parameters
228         std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& mrDecomposition;        // destination primitive list
229         std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& mrShadowDecomposition;  // destination primitive list for shadow
230         Reference < com::sun::star::i18n::XBreakIterator >          mxBreak;                // break iterator
231 
232         double getParagraphTextLength(const ::std::vector< const impPathTextPortion* >& rTextPortions)
233         {
234             drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
235             double fRetval(0.0);
236 
237             for(sal_uInt32 a(0L); a < rTextPortions.size(); a++)
238             {
239                 const impPathTextPortion* pCandidate = rTextPortions[a];
240 
241                 if(pCandidate && pCandidate->getTextLength())
242                 {
243                     aTextLayouter.setFont(pCandidate->getFont());
244                     fRetval += pCandidate->getDisplayLength(0L, pCandidate->getTextLength());
245                 }
246             }
247 
248             return fRetval;
249         }
250 
251         xub_StrLen getNextGlyphLen(const impPathTextPortion* pCandidate, xub_StrLen nPosition, const ::com::sun::star::lang::Locale& rFontLocale)
252         {
253             xub_StrLen nNextGlyphLen(1);
254 
255             if(mxBreak.is())
256             {
257                 sal_Int32 nDone(0L);
258                 nNextGlyphLen = (xub_StrLen)mxBreak->nextCharacters(pCandidate->getText(), nPosition,
259                     rFontLocale, CharacterIteratorMode::SKIPCELL, 1, nDone) - nPosition;
260             }
261 
262             return nNextGlyphLen;
263         }
264 
265     public:
266         impPolygonParagraphHandler(
267             const drawinglayer::attribute::SdrFormTextAttribute& rSdrFormTextAttribute,
268             std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rDecomposition,
269             std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rShadowDecomposition)
270         :   maSdrFormTextAttribute(rSdrFormTextAttribute),
271             mrDecomposition(rDecomposition),
272             mrShadowDecomposition(rShadowDecomposition)
273         {
274             // prepare BreakIterator
275             Reference < XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
276             Reference < XInterface > xInterface = xMSF->createInstance(::rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator"));
277 
278             if(xInterface.is())
279             {
280                 Any x = xInterface->queryInterface(::getCppuType((const Reference< XBreakIterator >*)0));
281                 x >>= mxBreak;
282             }
283         }
284 
285         void HandlePair(const basegfx::B2DPolygon rPolygonCandidate, const ::std::vector< const impPathTextPortion* >& rTextPortions)
286         {
287             // prepare polygon geometry, take into account as many parameters as possible
288             basegfx::B2DPolygon aPolygonCandidate(rPolygonCandidate);
289             const double fPolyLength(basegfx::tools::getLength(aPolygonCandidate));
290             double fPolyEnd(fPolyLength);
291             double fPolyStart(0.0);
292             double fAutosizeScaleFactor(1.0);
293             bool bAutosizeScale(false);
294 
295             if(maSdrFormTextAttribute.getFormTextMirror())
296             {
297                 aPolygonCandidate.flip();
298             }
299 
300             if(maSdrFormTextAttribute.getFormTextStart()
301                 && (XFT_LEFT == maSdrFormTextAttribute.getFormTextAdjust()
302                     || XFT_RIGHT == maSdrFormTextAttribute.getFormTextAdjust()))
303             {
304                 if(XFT_LEFT == maSdrFormTextAttribute.getFormTextAdjust())
305                 {
306                     fPolyStart += maSdrFormTextAttribute.getFormTextStart();
307 
308                     if(fPolyStart > fPolyEnd)
309                     {
310                         fPolyStart = fPolyEnd;
311                     }
312                 }
313                 else
314                 {
315                     fPolyEnd -= maSdrFormTextAttribute.getFormTextStart();
316 
317                     if(fPolyEnd < fPolyStart)
318                     {
319                         fPolyEnd = fPolyStart;
320                     }
321                 }
322             }
323 
324             if(XFT_LEFT != maSdrFormTextAttribute.getFormTextAdjust())
325             {
326                 // calculate total text length of this paragraph, some layout needs to be done
327                 const double fParagraphTextLength(getParagraphTextLength(rTextPortions));
328 
329                 // check if text is too long for paragraph. If yes, handle as if left aligned (default),
330                 // but still take care of XFT_AUTOSIZE in that case
331                 const bool bTextTooLong(fParagraphTextLength > (fPolyEnd - fPolyStart));
332 
333                 if(XFT_RIGHT == maSdrFormTextAttribute.getFormTextAdjust())
334                 {
335                     if(!bTextTooLong)
336                     {
337                         // if right aligned, add difference to polygon start
338                         fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength);
339                     }
340                 }
341                 else if(XFT_CENTER == maSdrFormTextAttribute.getFormTextAdjust())
342                 {
343                     if(!bTextTooLong)
344                     {
345                         // if centered, add half of difference to polygon start
346                         fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength) / 2.0;
347                     }
348                 }
349                 else if(XFT_AUTOSIZE == maSdrFormTextAttribute.getFormTextAdjust())
350                 {
351                     // if scale, prepare scale factor between curve length and text length
352                     if(0.0 != fParagraphTextLength)
353                     {
354                         fAutosizeScaleFactor = (fPolyEnd - fPolyStart) / fParagraphTextLength;
355                         bAutosizeScale = true;
356                     }
357                 }
358             }
359 
360             // handle text portions for this paragraph
361             for(sal_uInt32 a(0L); a < rTextPortions.size() && fPolyStart < fPolyEnd; a++)
362             {
363                 const impPathTextPortion* pCandidate = rTextPortions[a];
364                 basegfx::B2DVector aFontScaling;
365                 const drawinglayer::attribute::FontAttribute aCandidateFontAttribute(
366                     drawinglayer::primitive2d::getFontAttributeFromVclFont(
367                         aFontScaling,
368                         pCandidate->getFont(),
369                         pCandidate->isRTL(),
370                         false));
371 
372                 if(pCandidate && pCandidate->getTextLength())
373                 {
374                     drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
375                     aTextLayouter.setFont(pCandidate->getFont());
376                     xub_StrLen nUsedTextLength(0);
377 
378                     while(nUsedTextLength < pCandidate->getTextLength() && fPolyStart < fPolyEnd)
379                     {
380                         xub_StrLen nNextGlyphLen(getNextGlyphLen(pCandidate, pCandidate->getTextStart() + nUsedTextLength, pCandidate->getLocale()));
381 
382                         // prepare portion length. Takes RTL sections into account.
383                         double fPortionLength(pCandidate->getDisplayLength(nUsedTextLength, nNextGlyphLen));
384 
385                         if(bAutosizeScale)
386                         {
387                             // when autosize scaling, expand portion length
388                             fPortionLength *= fAutosizeScaleFactor;
389                         }
390 
391                         // create transformation
392                         basegfx::B2DHomMatrix aNewTransformA, aNewTransformB, aNewShadowTransform;
393                         basegfx::B2DPoint aStartPos(basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart, fPolyLength));
394                         basegfx::B2DPoint aEndPos(aStartPos);
395 
396                         // add font scaling
397                         aNewTransformA.scale(aFontScaling.getX(), aFontScaling.getY());
398 
399                         // prepare scaling of text primitive
400                         if(bAutosizeScale)
401                         {
402                             // when autosize scaling, expand text primitive scaling to it
403                             aNewTransformA.scale(fAutosizeScaleFactor, fAutosizeScaleFactor);
404                         }
405 
406                         // eventually create shadow primitives from aDecomposition and add to rDecomposition
407                         const bool bShadow(XFTSHADOW_NONE != maSdrFormTextAttribute.getFormTextShadow());
408 
409                         if(bShadow)
410                         {
411                             if(XFTSHADOW_NORMAL == maSdrFormTextAttribute.getFormTextShadow())
412                             {
413                                 aNewShadowTransform.translate(
414                                     maSdrFormTextAttribute.getFormTextShdwXVal(),
415                                     -maSdrFormTextAttribute.getFormTextShdwYVal());
416                             }
417                             else // XFTSHADOW_SLANT
418                             {
419                                 double fScaleValue(maSdrFormTextAttribute.getFormTextShdwYVal() / 100.0);
420                                 double fShearValue(-maSdrFormTextAttribute.getFormTextShdwXVal() * F_PI1800);
421 
422                                 aNewShadowTransform.scale(1.0, fScaleValue);
423                                 aNewShadowTransform.shearX(sin(fShearValue));
424                                 aNewShadowTransform.scale(1.0, cos(fShearValue));
425                             }
426                         }
427 
428                         switch(maSdrFormTextAttribute.getFormTextStyle())
429                         {
430                             case XFT_ROTATE :
431                             {
432                                 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
433                                 const basegfx::B2DVector aDirection(aEndPos - aStartPos);
434                                 aNewTransformB.rotate(atan2(aDirection.getY(), aDirection.getX()));
435                                 aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
436 
437                                 break;
438                             }
439                             case XFT_UPRIGHT :
440                             {
441                                 aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
442 
443                                 break;
444                             }
445                             case XFT_SLANTX :
446                             {
447                                 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
448                                 const basegfx::B2DVector aDirection(aEndPos - aStartPos);
449                                 const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
450                                 const double fSin(sin(fShearValue));
451                                 const double fCos(cos(fShearValue));
452 
453                                 aNewTransformB.shearX(-fSin);
454 
455                                 // Scale may lead to objects without height since fCos == 0.0 is possible.
456                                 // Renderers need to handle that, it's not a forbidden value and does not
457                                 // need to be avoided
458                                 aNewTransformB.scale(1.0, fCos);
459                                 aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
460 
461                                 break;
462                             }
463                             case XFT_SLANTY :
464                             {
465                                 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
466                                 const basegfx::B2DVector aDirection(aEndPos - aStartPos);
467                                 const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
468                                 const double fCos(cos(fShearValue));
469                                 const double fTan(tan(fShearValue));
470 
471                                 // shear to 'stand' on the curve
472                                 aNewTransformB.shearY(fTan);
473 
474                                 // scale in X to make as tight as needed. As with XFT_SLANT_X, this may
475                                 // lead to primitives without width which the renderers will handle
476                                 aNewTransformA.scale(fCos, 1.0);
477 
478                                 aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
479 
480                                 break;
481                             }
482                             default : break; // XFT_NONE
483                         }
484 
485                         // distance from path?
486                         if(maSdrFormTextAttribute.getFormTextDistance())
487                         {
488                             if(aEndPos.equal(aStartPos))
489                             {
490                                 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
491                             }
492 
493                             // use back vector (aStartPos - aEndPos) here to get mirrored perpendicular as in old stuff
494                             const basegfx::B2DVector aPerpendicular(
495                                 basegfx::getNormalizedPerpendicular(aStartPos - aEndPos) *
496                                 maSdrFormTextAttribute.getFormTextDistance());
497                             aNewTransformB.translate(aPerpendicular.getX(), aPerpendicular.getY());
498                         }
499 
500                         if(pCandidate->getText().Len() && nNextGlyphLen)
501                         {
502                             const xub_StrLen nPortionIndex(pCandidate->getPortionIndex(nUsedTextLength, nNextGlyphLen));
503                             ::std::vector< double > aNewDXArray;
504 
505                             if(nNextGlyphLen > 1 && pCandidate->getDoubleDXArray().size())
506                             {
507                                 // copy DXArray for portion
508                                 aNewDXArray.insert(
509                                     aNewDXArray.begin(),
510                                     pCandidate->getDoubleDXArray().begin() + nPortionIndex,
511                                     pCandidate->getDoubleDXArray().begin() + (nPortionIndex + nNextGlyphLen));
512 
513                                 if(nPortionIndex > 0)
514                                 {
515                                     // adapt to portion start
516                                     double fDXOffset= *(pCandidate->getDoubleDXArray().begin() + (nPortionIndex - 1));
517                                     ::std::transform(
518                                         aNewDXArray.begin(), aNewDXArray.end(),
519                                         aNewDXArray.begin(), ::std::bind2nd(::std::minus<double>(), fDXOffset));
520                                 }
521 
522                                 if(bAutosizeScale)
523                                 {
524                                     // when autosize scaling, adapt to DXArray, too
525                                     ::std::transform(
526                                         aNewDXArray.begin(), aNewDXArray.end(),
527                                         aNewDXArray.begin(), ::std::bind2nd(::std::multiplies<double>(), fAutosizeScaleFactor));
528                                 }
529                             }
530 
531                             if(bShadow)
532                             {
533                                 // shadow primitive creation
534                                 const Color aShadowColor(maSdrFormTextAttribute.getFormTextShdwColor());
535                                 const basegfx::BColor aRGBShadowColor(aShadowColor.getBColor());
536 
537                                 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pNew =
538                                     new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
539                                         aNewTransformB * aNewShadowTransform * aNewTransformA,
540                                         pCandidate->getText(),
541                                         nPortionIndex,
542                                         nNextGlyphLen,
543                                         aNewDXArray,
544                                         aCandidateFontAttribute,
545                                         pCandidate->getLocale(),
546                                         aRGBShadowColor);
547 
548                                 mrShadowDecomposition.push_back(pNew);
549                             }
550 
551                             {
552                                 // primitive creation
553                                 const Color aColor(pCandidate->getFont().GetColor());
554                                 const basegfx::BColor aRGBColor(aColor.getBColor());
555 
556                                 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pNew =
557                                     new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
558                                         aNewTransformB * aNewTransformA,
559                                         pCandidate->getText(),
560                                         nPortionIndex,
561                                         nNextGlyphLen,
562                                         aNewDXArray,
563                                         aCandidateFontAttribute,
564                                         pCandidate->getLocale(),
565                                         aRGBColor);
566 
567                                 mrDecomposition.push_back(pNew);
568                             }
569                         }
570 
571                         // consume from portion // no += here, xub_StrLen is sal_uInt16 and the compiler will gererate a warning here
572                         nUsedTextLength = nUsedTextLength + nNextGlyphLen;
573 
574                         // consume from polygon
575                         fPolyStart += fPortionLength;
576                     }
577                 }
578             }
579         }
580     };
581 } // end of anonymous namespace
582 
583 //////////////////////////////////////////////////////////////////////////////
584 // primitive decomposition helpers
585 
586 namespace
587 {
588     void impAddPolygonStrokePrimitives(
589         const basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
590         const basegfx::B2DHomMatrix& rTransform,
591         const drawinglayer::attribute::LineAttribute& rLineAttribute,
592         const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute,
593         std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rTarget)
594     {
595         for(basegfx::B2DPolyPolygonVector::const_iterator aPolygon(rB2DPolyPolyVector.begin()); aPolygon != rB2DPolyPolyVector.end(); aPolygon++)
596         {
597             // prepare PolyPolygons
598             basegfx::B2DPolyPolygon aB2DPolyPolygon = *aPolygon;
599             aB2DPolyPolygon.transform(rTransform);
600 
601             for(sal_uInt32 a(0L); a < aB2DPolyPolygon.count(); a++)
602             {
603                 // create one primitive per polygon
604                 drawinglayer::primitive2d::PolygonStrokePrimitive2D* pNew =
605                     new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
606                         aB2DPolyPolygon.getB2DPolygon(a), rLineAttribute, rStrokeAttribute);
607                 rTarget.push_back(pNew);
608             }
609         }
610     }
611 
612     drawinglayer::primitive2d::Primitive2DSequence impAddPathTextOutlines(
613         const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rSource,
614         const drawinglayer::attribute::SdrFormTextOutlineAttribute& rOutlineAttribute)
615     {
616         std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aNewPrimitives;
617 
618         for(sal_uInt32 a(0L); a < rSource.size(); a++)
619         {
620             const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pTextCandidate = dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(rSource[a]);
621 
622             if(pTextCandidate)
623             {
624                 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
625                 basegfx::B2DHomMatrix aPolygonTransform;
626 
627                 // get text outlines and their object transformation
628                 pTextCandidate->getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
629 
630                 if(!aB2DPolyPolyVector.empty())
631                 {
632                     // create stroke primitives
633                     std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aStrokePrimitives;
634                     impAddPolygonStrokePrimitives(
635                         aB2DPolyPolyVector,
636                         aPolygonTransform,
637                         rOutlineAttribute.getLineAttribute(),
638                         rOutlineAttribute.getStrokeAttribute(),
639                         aStrokePrimitives);
640                     const sal_uInt32 nStrokeCount(aStrokePrimitives.size());
641 
642                     if(nStrokeCount)
643                     {
644                         if(rOutlineAttribute.getTransparence())
645                         {
646                             // create UnifiedTransparencePrimitive2D
647                             drawinglayer::primitive2d::Primitive2DSequence aStrokePrimitiveSequence(nStrokeCount);
648 
649                             for(sal_uInt32 b(0L); b < nStrokeCount; b++)
650                             {
651                                 aStrokePrimitiveSequence[b] = drawinglayer::primitive2d::Primitive2DReference(aStrokePrimitives[b]);
652                             }
653 
654                             drawinglayer::primitive2d::UnifiedTransparencePrimitive2D* pNew2 =
655                                 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
656                                     aStrokePrimitiveSequence,
657                                     (double)rOutlineAttribute.getTransparence() / 100.0);
658                             aNewPrimitives.push_back(pNew2);
659                         }
660                         else
661                         {
662                             // add polygons to rDecomposition as polygonStrokePrimitives
663                             aNewPrimitives.insert(aNewPrimitives.end(), aStrokePrimitives.begin(), aStrokePrimitives.end());
664                         }
665                     }
666                 }
667             }
668         }
669 
670         const sal_uInt32 nNewCount(aNewPrimitives.size());
671 
672         if(nNewCount)
673         {
674             drawinglayer::primitive2d::Primitive2DSequence aRetval(nNewCount);
675 
676             for(sal_uInt32 a(0L); a < nNewCount; a++)
677             {
678                 aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(aNewPrimitives[a]);
679             }
680 
681             return aRetval;
682         }
683         else
684         {
685             return drawinglayer::primitive2d::Primitive2DSequence();
686         }
687     }
688 } // end of anonymous namespace
689 
690 //////////////////////////////////////////////////////////////////////////////
691 // primitive decomposition
692 
693 void SdrTextObj::impDecomposePathTextPrimitive(
694     drawinglayer::primitive2d::Primitive2DSequence& rTarget,
695     const drawinglayer::primitive2d::SdrPathTextPrimitive2D& rSdrPathTextPrimitive,
696     const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
697 {
698     drawinglayer::primitive2d::Primitive2DSequence aRetvalA;
699     drawinglayer::primitive2d::Primitive2DSequence aRetvalB;
700 
701     // prepare outliner
702     SdrOutliner& rOutliner = ImpGetDrawOutliner();
703     rOutliner.SetUpdateMode(true);
704     rOutliner.Clear();
705     rOutliner.SetPaperSize(Size(LONG_MAX,LONG_MAX));
706     rOutliner.SetText(rSdrPathTextPrimitive.getOutlinerParaObject());
707 
708     // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
709     rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
710 
711     // now break up to text portions
712     impTextBreakupHandler aConverter(rOutliner);
713     const ::std::vector< impPathTextPortion > rPathTextPortions = aConverter.decompositionPathTextPrimitive();
714 
715     if(!rPathTextPortions.empty())
716     {
717         // get FormText and polygon values
718         const drawinglayer::attribute::SdrFormTextAttribute& rFormTextAttribute = rSdrPathTextPrimitive.getSdrFormTextAttribute();
719         const basegfx::B2DPolyPolygon& rPathPolyPolygon(rSdrPathTextPrimitive.getPathPolyPolygon());
720 
721         // get loop count
722         sal_uInt32 nLoopCount(rPathPolyPolygon.count());
723 
724         if(rOutliner.GetParagraphCount() < nLoopCount)
725         {
726             nLoopCount = rOutliner.GetParagraphCount();
727         }
728 
729         if(nLoopCount)
730         {
731             // prepare common decomposition stuff
732             std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aRegularDecomposition;
733             std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aShadowDecomposition;
734             impPolygonParagraphHandler aPolygonParagraphHandler(
735                 rFormTextAttribute,
736                 aRegularDecomposition,
737                 aShadowDecomposition);
738             sal_uInt32 a;
739 
740             for(a = 0L; a < nLoopCount; a++)
741             {
742                 // filter text portions for this paragraph
743                 ::std::vector< const impPathTextPortion* > aParagraphTextPortions;
744 
745                 for(sal_uInt32 b(0L); b < rPathTextPortions.size(); b++)
746                 {
747                     const impPathTextPortion& rCandidate = rPathTextPortions[b];
748 
749                     if(rCandidate.getParagraph() == a)
750                     {
751                         aParagraphTextPortions.push_back(&rCandidate);
752                     }
753                 }
754 
755                 // handle data pair polygon/ParagraphTextPortions
756                 if(!aParagraphTextPortions.empty())
757                 {
758                     aPolygonParagraphHandler.HandlePair(rPathPolyPolygon.getB2DPolygon(a), aParagraphTextPortions);
759                 }
760             }
761 
762             const sal_uInt32 nShadowCount(aShadowDecomposition.size());
763             const sal_uInt32 nRegularCount(aRegularDecomposition.size());
764 
765             if(nShadowCount)
766             {
767                 // add shadow primitives to decomposition
768                 aRetvalA.realloc(nShadowCount);
769 
770                 for(a = 0L; a < nShadowCount; a++)
771                 {
772                     aRetvalA[a] = drawinglayer::primitive2d::Primitive2DReference(aShadowDecomposition[a]);
773                 }
774 
775                 // evtl. add shadow outlines
776                 if(rFormTextAttribute.getFormTextOutline()
777                     && !rFormTextAttribute.getShadowOutline().isDefault())
778                 {
779                     const drawinglayer::primitive2d::Primitive2DSequence aOutlines(
780                         impAddPathTextOutlines(
781                             aShadowDecomposition,
782                             rFormTextAttribute.getShadowOutline()));
783 
784                     drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(aRetvalA, aOutlines);
785                 }
786             }
787 
788             if(nRegularCount)
789             {
790                 // add normal primitives to decomposition
791                 aRetvalB.realloc(nRegularCount);
792 
793                 for(a = 0L; a < nRegularCount; a++)
794                 {
795                     aRetvalB[a] = drawinglayer::primitive2d::Primitive2DReference(aRegularDecomposition[a]);
796                 }
797 
798                 // evtl. add outlines
799                 if(rFormTextAttribute.getFormTextOutline()
800                     && !rFormTextAttribute.getOutline().isDefault())
801                 {
802                     const drawinglayer::primitive2d::Primitive2DSequence aOutlines(
803                         impAddPathTextOutlines(
804                             aRegularDecomposition,
805                             rFormTextAttribute.getOutline()));
806 
807                     drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(aRetvalB, aOutlines);
808                 }
809             }
810         }
811     }
812 
813     // cleanup outliner
814     rOutliner.SetDrawPortionHdl(Link());
815     rOutliner.Clear();
816     rOutliner.setVisualizedPage(0);
817 
818     // concatenate all results
819     drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aRetvalA);
820     drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aRetvalB);
821 }
822 
823 //////////////////////////////////////////////////////////////////////////////
824 // eof
825