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