1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <o3tl/safeint.hxx>
23 #include <svx/svdotext.hxx>
24 #include <svx/svdoutl.hxx>
25 #include <basegfx/vector/b2dvector.hxx>
26 #include <sdr/primitive2d/sdrtextprimitive2d.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <basegfx/polygon/b2dpolygon.hxx>
30 #include <com/sun/star/i18n/BreakIterator.hpp>
31 #include <comphelper/processfactory.hxx>
32 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
33 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
34 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
35 #include <basegfx/color/bcolor.hxx>
37 // primitive decomposition helpers
38 #include <drawinglayer/attribute/strokeattribute.hxx>
39 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
40 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
41 #include <svx/unoapi.hxx>
42 #include <drawinglayer/geometry/viewinformation2d.hxx>
43 #include <sdr/attribute/sdrformtextoutlineattribute.hxx>
46 using namespace com::sun::star
;
48 // PathTextPortion helper
52 class impPathTextPortion
54 basegfx::B2DVector maOffset
;
56 sal_Int32 mnTextStart
;
57 sal_Int32 mnTextLength
;
58 sal_Int32 mnParagraph
;
60 ::std::vector
< double > maDblDXArray
; // double DXArray, font size independent -> unit coordinate system
61 ::std::vector
< sal_Bool
> maKashidaArray
;
62 lang::Locale maLocale
;
67 explicit impPathTextPortion(const DrawPortionInfo
& rInfo
)
68 : maOffset(rInfo
.mrStartPos
.X(), rInfo
.mrStartPos
.Y()),
70 mnTextStart(rInfo
.mnTextStart
),
71 mnTextLength(rInfo
.mnTextLen
),
72 mnParagraph(rInfo
.mnPara
),
74 maKashidaArray(rInfo
.mpKashidaArray
.begin(), rInfo
.mpKashidaArray
.end()),
75 maLocale(rInfo
.mpLocale
? *rInfo
.mpLocale
: lang::Locale()),
76 mbRTL(!rInfo
.mrFont
.IsVertical() && rInfo
.IsRTL())
78 if(mnTextLength
&& !rInfo
.mpDXArray
.empty())
80 maDblDXArray
.reserve(mnTextLength
);
82 for(sal_Int32 a
=0; a
< mnTextLength
; a
++)
84 maDblDXArray
.push_back(rInfo
.mpDXArray
[a
]);
90 bool operator<(const impPathTextPortion
& rComp
) const
92 if(mnParagraph
< rComp
.mnParagraph
)
97 if(maOffset
.getX() < rComp
.maOffset
.getX())
102 return (maOffset
.getY() < rComp
.maOffset
.getY());
105 const OUString
& getText() const { return maText
; }
106 sal_Int32
getTextStart() const { return mnTextStart
; }
107 sal_Int32
getTextLength() const { return mnTextLength
; }
108 sal_Int32
getParagraph() const { return mnParagraph
; }
109 const SvxFont
& getFont() const { return maFont
; }
110 bool isRTL() const { return mbRTL
; }
111 const ::std::vector
< double >& getDoubleDXArray() const { return maDblDXArray
; }
112 const ::std::vector
< sal_Bool
>& getKashidaArray() const { return maKashidaArray
; }
113 const lang::Locale
& getLocale() const { return maLocale
; }
115 sal_Int32
getPortionIndex(sal_Int32 nIndex
, sal_Int32 nLength
) const
119 return (mnTextStart
+ (mnTextLength
- (nIndex
+ nLength
)));
123 return (mnTextStart
+ nIndex
);
127 double getDisplayLength(sal_Int32 nIndex
, sal_Int32 nLength
) const
129 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter
;
132 if(maFont
.IsVertical())
134 fRetval
= aTextLayouter
.getTextHeight() * static_cast<double>(nLength
);
138 fRetval
= aTextLayouter
.getTextWidth(maText
, getPortionIndex(nIndex
, nLength
), nLength
);
144 } // end of anonymous namespace
147 // TextBreakup helper
151 class impTextBreakupHandler
153 SdrOutliner
& mrOutliner
;
154 ::std::vector
< impPathTextPortion
> maPathTextPortions
;
156 DECL_LINK(decompositionPathTextPrimitive
, DrawPortionInfo
*, void );
159 explicit impTextBreakupHandler(SdrOutliner
& rOutliner
)
160 : mrOutliner(rOutliner
)
164 const ::std::vector
< impPathTextPortion
>& decompositionPathTextPrimitive()
166 // strip portions to maPathTextPortions
167 mrOutliner
.SetDrawPortionHdl(LINK(this, impTextBreakupHandler
, decompositionPathTextPrimitive
));
168 mrOutliner
.StripPortions();
170 if(!maPathTextPortions
.empty())
172 // sort portions by paragraph, x and y
173 ::std::sort(maPathTextPortions
.begin(), maPathTextPortions
.end());
176 return maPathTextPortions
;
180 IMPL_LINK(impTextBreakupHandler
, decompositionPathTextPrimitive
, DrawPortionInfo
*, pInfo
, void)
182 maPathTextPortions
.emplace_back(*pInfo
);
184 } // end of anonymous namespace
187 // TextBreakup one poly and one paragraph helper
191 class impPolygonParagraphHandler
193 const drawinglayer::attribute::SdrFormTextAttribute maSdrFormTextAttribute
; // FormText parameters
194 drawinglayer::primitive2d::Primitive2DContainer
& mrDecomposition
; // destination primitive list
195 drawinglayer::primitive2d::Primitive2DContainer
& mrShadowDecomposition
; // destination primitive list for shadow
196 uno::Reference
<i18n::XBreakIterator
> mxBreak
; // break iterator
198 static double getParagraphTextLength(const ::std::vector
< const impPathTextPortion
* >& rTextPortions
)
200 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter
;
203 for(const impPathTextPortion
* pCandidate
: rTextPortions
)
205 if(pCandidate
&& pCandidate
->getTextLength())
207 aTextLayouter
.setFont(pCandidate
->getFont());
208 fRetval
+= pCandidate
->getDisplayLength(0, pCandidate
->getTextLength());
215 sal_Int32
getNextGlyphLen(const impPathTextPortion
* pCandidate
, sal_Int32 nPosition
, const lang::Locale
& rFontLocale
)
217 sal_Int32
nNextGlyphLen(1);
222 nNextGlyphLen
= mxBreak
->nextCharacters(pCandidate
->getText(), nPosition
,
223 rFontLocale
, i18n::CharacterIteratorMode::SKIPCELL
, 1, nDone
) - nPosition
;
226 return nNextGlyphLen
;
230 impPolygonParagraphHandler(
231 drawinglayer::attribute::SdrFormTextAttribute aSdrFormTextAttribute
,
232 drawinglayer::primitive2d::Primitive2DContainer
& rDecomposition
,
233 drawinglayer::primitive2d::Primitive2DContainer
& rShadowDecomposition
)
234 : maSdrFormTextAttribute(std::move(aSdrFormTextAttribute
)),
235 mrDecomposition(rDecomposition
),
236 mrShadowDecomposition(rShadowDecomposition
)
238 // prepare BreakIterator
239 const uno::Reference
<uno::XComponentContext
>& xContext
= ::comphelper::getProcessComponentContext();
240 mxBreak
= i18n::BreakIterator::create(xContext
);
243 void HandlePair(const basegfx::B2DPolygon
& rPolygonCandidate
, const ::std::vector
< const impPathTextPortion
* >& rTextPortions
)
245 // prepare polygon geometry, take into account as many parameters as possible
246 basegfx::B2DPolygon
aPolygonCandidate(rPolygonCandidate
);
247 const double fPolyLength(basegfx::utils::getLength(aPolygonCandidate
));
248 double fPolyEnd(fPolyLength
);
249 double fPolyStart(0.0);
250 double fAutosizeScaleFactor(1.0);
251 bool bAutosizeScale(false);
253 if(maSdrFormTextAttribute
.getFormTextMirror())
255 aPolygonCandidate
.flip();
258 if(maSdrFormTextAttribute
.getFormTextStart()
259 && (XFormTextAdjust::Left
== maSdrFormTextAttribute
.getFormTextAdjust()
260 || XFormTextAdjust::Right
== maSdrFormTextAttribute
.getFormTextAdjust()))
262 if(XFormTextAdjust::Left
== maSdrFormTextAttribute
.getFormTextAdjust())
264 fPolyStart
+= maSdrFormTextAttribute
.getFormTextStart();
266 if(fPolyStart
> fPolyEnd
)
268 fPolyStart
= fPolyEnd
;
273 fPolyEnd
-= maSdrFormTextAttribute
.getFormTextStart();
275 if(fPolyEnd
< fPolyStart
)
277 fPolyEnd
= fPolyStart
;
282 if(XFormTextAdjust::Left
!= maSdrFormTextAttribute
.getFormTextAdjust())
284 // calculate total text length of this paragraph, some layout needs to be done
285 const double fParagraphTextLength(getParagraphTextLength(rTextPortions
));
287 // check if text is too long for paragraph. If yes, handle as if left aligned (default),
288 // but still take care of XFormTextAdjust::AutoSize in that case
289 const bool bTextTooLong(fParagraphTextLength
> (fPolyEnd
- fPolyStart
));
291 if(XFormTextAdjust::Right
== maSdrFormTextAttribute
.getFormTextAdjust())
295 // if right aligned, add difference to polygon start
296 fPolyStart
+= ((fPolyEnd
- fPolyStart
) - fParagraphTextLength
);
299 else if(XFormTextAdjust::Center
== maSdrFormTextAttribute
.getFormTextAdjust())
303 // if centered, add half of difference to polygon start
304 fPolyStart
+= ((fPolyEnd
- fPolyStart
) - fParagraphTextLength
) / 2.0;
307 else if(XFormTextAdjust::AutoSize
== maSdrFormTextAttribute
.getFormTextAdjust())
309 // if scale, prepare scale factor between curve length and text length
310 if(0.0 != fParagraphTextLength
)
312 fAutosizeScaleFactor
= (fPolyEnd
- fPolyStart
) / fParagraphTextLength
;
313 bAutosizeScale
= true;
318 // handle text portions for this paragraph
319 for(auto a
= rTextPortions
.begin(); a
!= rTextPortions
.end() && fPolyStart
< fPolyEnd
; ++a
)
321 const impPathTextPortion
* pCandidate
= *a
;
322 basegfx::B2DVector aFontScaling
;
324 if(pCandidate
&& pCandidate
->getTextLength())
326 const drawinglayer::attribute::FontAttribute
aCandidateFontAttribute(
327 drawinglayer::primitive2d::getFontAttributeFromVclFont(
329 pCandidate
->getFont(),
333 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter
;
334 aTextLayouter
.setFont(pCandidate
->getFont());
335 sal_Int32
nUsedTextLength(0);
337 while(nUsedTextLength
< pCandidate
->getTextLength() && fPolyStart
< fPolyEnd
)
339 sal_Int32
nNextGlyphLen(getNextGlyphLen(pCandidate
, pCandidate
->getTextStart() + nUsedTextLength
, pCandidate
->getLocale()));
341 // prepare portion length. Takes RTL sections into account.
342 double fPortionLength(pCandidate
->getDisplayLength(nUsedTextLength
, nNextGlyphLen
));
346 // when autosize scaling, expand portion length
347 fPortionLength
*= fAutosizeScaleFactor
;
350 // create transformation
351 basegfx::B2DHomMatrix aNewTransformA
, aNewTransformB
, aNewShadowTransform
;
352 basegfx::B2DPoint
aStartPos(basegfx::utils::getPositionAbsolute(aPolygonCandidate
, fPolyStart
, fPolyLength
));
353 basegfx::B2DPoint
aEndPos(aStartPos
);
356 aNewTransformA
.scale(aFontScaling
.getX(), aFontScaling
.getY());
358 // prepare scaling of text primitive
361 // when autosize scaling, expand text primitive scaling to it
362 aNewTransformA
.scale(fAutosizeScaleFactor
, fAutosizeScaleFactor
);
365 // eventually create shadow primitives from aDecomposition and add to rDecomposition
366 const bool bShadow(XFormTextShadow::NONE
!= maSdrFormTextAttribute
.getFormTextShadow());
370 if(XFormTextShadow::Normal
== maSdrFormTextAttribute
.getFormTextShadow())
372 aNewShadowTransform
.translate(
373 maSdrFormTextAttribute
.getFormTextShdwXVal(),
374 -maSdrFormTextAttribute
.getFormTextShdwYVal());
376 else // XFormTextShadow::Slant
378 double fScaleValue(maSdrFormTextAttribute
.getFormTextShdwYVal() / 100.0);
379 double fShearValue(-basegfx::deg2rad
<10>(maSdrFormTextAttribute
.getFormTextShdwXVal()));
381 aNewShadowTransform
.scale(1.0, fScaleValue
);
382 aNewShadowTransform
.shearX(sin(fShearValue
));
383 aNewShadowTransform
.scale(1.0, cos(fShearValue
));
387 switch(maSdrFormTextAttribute
.getFormTextStyle())
389 case XFormTextStyle::Rotate
:
391 aEndPos
= basegfx::utils::getPositionAbsolute(aPolygonCandidate
, fPolyStart
+ fPortionLength
, fPolyLength
);
392 const basegfx::B2DVector
aDirection(aEndPos
- aStartPos
);
393 aNewTransformB
.rotate(atan2(aDirection
.getY(), aDirection
.getX()));
394 aNewTransformB
.translate(aStartPos
.getX(), aStartPos
.getY());
398 case XFormTextStyle::Upright
:
400 aNewTransformB
.translate(aStartPos
.getX() - (fPortionLength
/ 2.0), aStartPos
.getY());
404 case XFormTextStyle::SlantX
:
406 aEndPos
= basegfx::utils::getPositionAbsolute(aPolygonCandidate
, fPolyStart
+ fPortionLength
, fPolyLength
);
407 const basegfx::B2DVector
aDirection(aEndPos
- aStartPos
);
408 const double fShearValue(atan2(aDirection
.getY(), aDirection
.getX()));
409 const double fSin(sin(fShearValue
));
410 const double fCos(cos(fShearValue
));
412 aNewTransformB
.shearX(-fSin
);
414 // Scale may lead to objects without height since fCos == 0.0 is possible.
415 // Renderers need to handle that, it's not a forbidden value and does not
416 // need to be avoided
417 aNewTransformB
.scale(1.0, fCos
);
418 aNewTransformB
.translate(aStartPos
.getX() - (fPortionLength
/ 2.0), aStartPos
.getY());
422 case XFormTextStyle::SlantY
:
424 aEndPos
= basegfx::utils::getPositionAbsolute(aPolygonCandidate
, fPolyStart
+ fPortionLength
, fPolyLength
);
425 const basegfx::B2DVector
aDirection(aEndPos
- aStartPos
);
426 const double fShearValue(atan2(aDirection
.getY(), aDirection
.getX()));
427 const double fCos(cos(fShearValue
));
428 const double fTan(tan(fShearValue
));
430 // shear to 'stand' on the curve
431 aNewTransformB
.shearY(fTan
);
433 // scale in X to make as tight as needed. As with XFT_SLANT_X, this may
434 // lead to primitives without width which the renderers will handle
435 aNewTransformA
.scale(fCos
, 1.0);
437 aNewTransformB
.translate(aStartPos
.getX(), aStartPos
.getY());
441 default : break; // XFormTextStyle::NONE
444 // distance from path?
445 if(maSdrFormTextAttribute
.getFormTextDistance())
447 if(aEndPos
.equal(aStartPos
))
449 aEndPos
= basegfx::utils::getPositionAbsolute(aPolygonCandidate
, fPolyStart
+ fPortionLength
, fPolyLength
);
452 // use back vector (aStartPos - aEndPos) here to get mirrored perpendicular as in old stuff
453 const basegfx::B2DVector
aPerpendicular(
454 basegfx::getNormalizedPerpendicular(aStartPos
- aEndPos
) *
455 maSdrFormTextAttribute
.getFormTextDistance());
456 aNewTransformB
.translate(aPerpendicular
.getX(), aPerpendicular
.getY());
459 if(!pCandidate
->getText().isEmpty() && nNextGlyphLen
)
461 const sal_Int32
nPortionIndex(pCandidate
->getPortionIndex(nUsedTextLength
, nNextGlyphLen
));
462 ::std::vector
< double > aNewDXArray
;
464 if(nNextGlyphLen
> 1 && !pCandidate
->getDoubleDXArray().empty())
466 // copy DXArray for portion
469 pCandidate
->getDoubleDXArray().begin() + nPortionIndex
,
470 pCandidate
->getDoubleDXArray().begin() + (nPortionIndex
+ nNextGlyphLen
));
472 if(nPortionIndex
> 0)
474 // adapt to portion start
475 double fDXOffset
= *(pCandidate
->getDoubleDXArray().begin() + (nPortionIndex
- 1));
477 aNewDXArray
.begin(), aNewDXArray
.end(),
478 aNewDXArray
.begin(), [fDXOffset
](double x
) { return x
- fDXOffset
; });
483 // when autosize scaling, adapt to DXArray, too
485 aNewDXArray
.begin(), aNewDXArray
.end(),
486 aNewDXArray
.begin(), [fAutosizeScaleFactor
](double x
) { return x
* fAutosizeScaleFactor
; });
492 // shadow primitive creation
493 const Color
aShadowColor(maSdrFormTextAttribute
.getFormTextShdwColor());
494 const basegfx::BColor
aRGBShadowColor(aShadowColor
.getBColor());
496 mrShadowDecomposition
.push_back(
497 new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
498 aNewTransformB
* aNewShadowTransform
* aNewTransformA
,
499 pCandidate
->getText(),
502 std::vector(aNewDXArray
),
503 std::vector(pCandidate
->getKashidaArray()),
504 aCandidateFontAttribute
,
505 pCandidate
->getLocale(),
510 // primitive creation
511 const Color
aColor(pCandidate
->getFont().GetColor());
512 const basegfx::BColor
aRGBColor(aColor
.getBColor());
514 mrDecomposition
.push_back(
515 new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
516 aNewTransformB
* aNewTransformA
,
517 pCandidate
->getText(),
520 std::move(aNewDXArray
),
521 std::vector(pCandidate
->getKashidaArray()),
522 aCandidateFontAttribute
,
523 pCandidate
->getLocale(),
528 // consume from portion
529 nUsedTextLength
+= nNextGlyphLen
;
531 // consume from polygon
532 fPolyStart
+= fPortionLength
;
538 } // end of anonymous namespace
541 // primitive decomposition helpers
545 void impAddPolygonStrokePrimitives(
546 const basegfx::B2DPolyPolygonVector
& rB2DPolyPolyVector
,
547 const basegfx::B2DHomMatrix
& rTransform
,
548 const drawinglayer::attribute::LineAttribute
& rLineAttribute
,
549 const drawinglayer::attribute::StrokeAttribute
& rStrokeAttribute
,
550 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
)
552 for(const auto& rB2DPolyPolygon
: rB2DPolyPolyVector
)
554 // prepare PolyPolygons
555 basegfx::B2DPolyPolygon aB2DPolyPolygon
= rB2DPolyPolygon
;
556 aB2DPolyPolygon
.transform(rTransform
);
558 for(auto const& rPolygon
: std::as_const(aB2DPolyPolygon
))
560 // create one primitive per polygon
562 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
563 rPolygon
, rLineAttribute
, rStrokeAttribute
) );
568 drawinglayer::primitive2d::Primitive2DContainer
impAddPathTextOutlines(
569 const drawinglayer::primitive2d::Primitive2DContainer
& rSource
,
570 const drawinglayer::attribute::SdrFormTextOutlineAttribute
& rOutlineAttribute
)
572 drawinglayer::primitive2d::Primitive2DContainer aNewPrimitives
;
574 for(const drawinglayer::primitive2d::Primitive2DReference
& a
: rSource
)
576 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D
* pTextCandidate
= dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D
* >(a
.get());
580 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector
;
581 basegfx::B2DHomMatrix aPolygonTransform
;
583 // get text outlines and their object transformation
584 pTextCandidate
->getTextOutlinesAndTransformation(aB2DPolyPolyVector
, aPolygonTransform
);
586 if(!aB2DPolyPolyVector
.empty())
588 // create stroke primitives
589 drawinglayer::primitive2d::Primitive2DContainer aStrokePrimitives
;
590 impAddPolygonStrokePrimitives(
593 rOutlineAttribute
.getLineAttribute(),
594 rOutlineAttribute
.getStrokeAttribute(),
597 if(!aStrokePrimitives
.empty())
599 if(rOutlineAttribute
.getTransparence())
601 // create UnifiedTransparencePrimitive2D
602 aNewPrimitives
.push_back(
603 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
604 std::move(aStrokePrimitives
),
605 static_cast<double>(rOutlineAttribute
.getTransparence()) / 100.0) );
609 // add polygons to rDecomposition as polygonStrokePrimitives
610 aNewPrimitives
.append( std::move(aStrokePrimitives
) );
617 return aNewPrimitives
;
619 } // end of anonymous namespace
622 // primitive decomposition
624 void SdrTextObj::impDecomposePathTextPrimitive(
625 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
626 const drawinglayer::primitive2d::SdrPathTextPrimitive2D
& rSdrPathTextPrimitive
,
627 const drawinglayer::geometry::ViewInformation2D
& aViewInformation
) const
629 drawinglayer::primitive2d::Primitive2DContainer aRetvalA
;
630 drawinglayer::primitive2d::Primitive2DContainer aRetvalB
;
633 SdrOutliner
& rOutliner
= ImpGetDrawOutliner();
634 rOutliner
.SetUpdateLayout(true);
636 rOutliner
.SetPaperSize(Size(LONG_MAX
,LONG_MAX
));
637 rOutliner
.SetText(rSdrPathTextPrimitive
.getOutlinerParaObject());
639 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
640 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
642 // now break up to text portions
643 impTextBreakupHandler
aConverter(rOutliner
);
644 const ::std::vector
< impPathTextPortion
> rPathTextPortions
= aConverter
.decompositionPathTextPrimitive();
646 if(!rPathTextPortions
.empty())
648 // get FormText and polygon values
649 const drawinglayer::attribute::SdrFormTextAttribute
& rFormTextAttribute
= rSdrPathTextPrimitive
.getSdrFormTextAttribute();
650 const basegfx::B2DPolyPolygon
& rPathPolyPolygon(rSdrPathTextPrimitive
.getPathPolyPolygon());
653 sal_uInt32
nLoopCount(rPathPolyPolygon
.count());
655 if(o3tl::make_unsigned(rOutliner
.GetParagraphCount()) < nLoopCount
)
657 nLoopCount
= rOutliner
.GetParagraphCount();
662 // prepare common decomposition stuff
663 drawinglayer::primitive2d::Primitive2DContainer aRegularDecomposition
;
664 drawinglayer::primitive2d::Primitive2DContainer aShadowDecomposition
;
665 impPolygonParagraphHandler
aPolygonParagraphHandler(
667 aRegularDecomposition
,
668 aShadowDecomposition
);
671 for(a
= 0; a
< nLoopCount
; a
++)
673 // filter text portions for this paragraph
674 ::std::vector
< const impPathTextPortion
* > aParagraphTextPortions
;
676 for(const auto & rCandidate
: rPathTextPortions
)
678 if(static_cast<sal_uInt32
>(rCandidate
.getParagraph()) == a
)
680 aParagraphTextPortions
.push_back(&rCandidate
);
684 // handle data pair polygon/ParagraphTextPortions
685 if(!aParagraphTextPortions
.empty())
687 aPolygonParagraphHandler
.HandlePair(rPathPolyPolygon
.getB2DPolygon(a
), aParagraphTextPortions
);
691 const sal_uInt32
nShadowCount(aShadowDecomposition
.size());
692 const sal_uInt32
nRegularCount(aRegularDecomposition
.size());
696 // add shadow primitives to decomposition
698 // if necessary, add shadow outlines
699 if(rFormTextAttribute
.getFormTextOutline()
700 && !rFormTextAttribute
.getShadowOutline().isDefault())
702 aRetvalA
= aShadowDecomposition
;
703 const drawinglayer::primitive2d::Primitive2DContainer
aOutlines(
704 impAddPathTextOutlines(
705 aShadowDecomposition
,
706 rFormTextAttribute
.getShadowOutline()));
708 aRetvalA
.append(aOutlines
);
711 aRetvalA
= std::move(aShadowDecomposition
);
716 // add normal primitives to decomposition
718 // if necessary, add outlines
719 if(rFormTextAttribute
.getFormTextOutline()
720 && !rFormTextAttribute
.getOutline().isDefault())
722 aRetvalB
= aRegularDecomposition
;
723 const drawinglayer::primitive2d::Primitive2DContainer
aOutlines(
724 impAddPathTextOutlines(
725 aRegularDecomposition
,
726 rFormTextAttribute
.getOutline()));
728 aRetvalB
.append(aOutlines
);
731 aRetvalB
= std::move(aRegularDecomposition
);
737 rOutliner
.SetDrawPortionHdl(Link
<DrawPortionInfo
*,void>());
739 rOutliner
.setVisualizedPage(nullptr);
741 // concatenate all results
742 rTarget
.append(std::move(aRetvalA
));
743 rTarget
.append(std::move(aRetvalB
));
747 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */