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