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