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