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