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 <drawinglayer/primitive2d/textprimitive2d.hxx>
26 : #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
27 : #include <basegfx/range/b2drange.hxx>
28 : #include <editeng/editstat.hxx>
29 : #include <tools/helpers.hxx>
30 : #include <svx/sdtfchim.hxx>
31 : #include <svl/itemset.hxx>
32 : #include <basegfx/polygon/b2dpolygontools.hxx>
33 : #include <basegfx/polygon/b2dpolygon.hxx>
34 : #include <drawinglayer/animation/animationtiming.hxx>
35 : #include <basegfx/color/bcolor.hxx>
36 : #include <vcl/svapp.hxx>
37 : #include <editeng/eeitemid.hxx>
38 : #include <editeng/escpitem.hxx>
39 : #include <editeng/svxenum.hxx>
40 : #include <editeng/flditem.hxx>
41 : #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
42 : #include <vcl/metaact.hxx>
43 : #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
44 : #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
45 : #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
46 : #include <svx/unoapi.hxx>
47 : #include <drawinglayer/geometry/viewinformation2d.hxx>
48 : #include <editeng/outlobj.hxx>
49 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
50 :
51 : //////////////////////////////////////////////////////////////////////////////
52 : // helpers
53 :
54 : namespace
55 : {
56 534 : drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector)
57 : {
58 534 : const sal_Int32 nCount(rPrimitiveVector.size());
59 534 : drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount);
60 :
61 1201 : for(sal_Int32 a(0L); a < nCount; a++)
62 : {
63 667 : aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]);
64 : }
65 :
66 534 : return aRetval;
67 : }
68 :
69 109 : class impTextBreakupHandler
70 : {
71 : private:
72 : std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maTextPortionPrimitives;
73 : std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maLinePrimitives;
74 : std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maParagraphPrimitives;
75 :
76 : SdrOutliner& mrOutliner;
77 : basegfx::B2DHomMatrix maNewTransformA;
78 : basegfx::B2DHomMatrix maNewTransformB;
79 :
80 : // the visible area for contour text decomposition
81 : basegfx::B2DVector maScale;
82 :
83 : // ClipRange for BlockText decomposition; only text portions completely
84 : // inside are to be accepted, so this is different from geometric clipping
85 : // (which would allow e.g. upper parts of portions to remain). Only used for
86 : // BlockText (see there)
87 : basegfx::B2DRange maClipRange;
88 :
89 : DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* );
90 : DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* );
91 : DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* );
92 :
93 : DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* );
94 : DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* );
95 : DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* );
96 :
97 : bool impIsUnderlineAbove(const Font& rFont) const;
98 : void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
99 : drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const;
100 : void impFlushTextPortionPrimitivesToLinePrimitives();
101 : void impFlushLinePrimitivesToParagraphPrimitives();
102 : void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
103 : void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
104 :
105 : public:
106 109 : impTextBreakupHandler(SdrOutliner& rOutliner)
107 : : maTextPortionPrimitives(),
108 : maLinePrimitives(),
109 : maParagraphPrimitives(),
110 : mrOutliner(rOutliner),
111 : maNewTransformA(),
112 : maNewTransformB(),
113 : maScale(),
114 109 : maClipRange()
115 : {
116 109 : }
117 :
118 0 : void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
119 : {
120 0 : maScale = rScale;
121 0 : maNewTransformA = rNewTransformA;
122 0 : maNewTransformB = rNewTransformB;
123 0 : mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
124 0 : mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
125 0 : mrOutliner.StripPortions();
126 0 : mrOutliner.SetDrawPortionHdl(Link());
127 0 : mrOutliner.SetDrawBulletHdl(Link());
128 0 : }
129 :
130 109 : void decomposeBlockTextPrimitive(
131 : const basegfx::B2DHomMatrix& rNewTransformA,
132 : const basegfx::B2DHomMatrix& rNewTransformB,
133 : const basegfx::B2DRange& rClipRange)
134 : {
135 109 : maNewTransformA = rNewTransformA;
136 109 : maNewTransformB = rNewTransformB;
137 109 : maClipRange = rClipRange;
138 109 : mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
139 109 : mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
140 109 : mrOutliner.StripPortions();
141 109 : mrOutliner.SetDrawPortionHdl(Link());
142 109 : mrOutliner.SetDrawBulletHdl(Link());
143 109 : }
144 :
145 0 : void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
146 : {
147 0 : maNewTransformA = rNewTransformA;
148 0 : maNewTransformB = rNewTransformB;
149 0 : mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
150 0 : mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
151 0 : mrOutliner.StripPortions();
152 0 : mrOutliner.SetDrawPortionHdl(Link());
153 0 : mrOutliner.SetDrawBulletHdl(Link());
154 0 : }
155 :
156 : drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence();
157 : };
158 :
159 0 : bool impTextBreakupHandler::impIsUnderlineAbove(const Font& rFont) const
160 : {
161 0 : if(!rFont.IsVertical())
162 : {
163 0 : return false;
164 : }
165 :
166 0 : if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
167 : {
168 : // the underline is right for Japanese only
169 0 : return true;
170 : }
171 :
172 0 : return false;
173 : }
174 :
175 322 : void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
176 : {
177 322 : if(rInfo.mrText.Len() && rInfo.mnTextLen)
178 : {
179 242 : basegfx::B2DVector aFontScaling;
180 : drawinglayer::attribute::FontAttribute aFontAttribute(
181 : drawinglayer::primitive2d::getFontAttributeFromVclFont(
182 : aFontScaling,
183 : rInfo.mrFont,
184 242 : rInfo.IsRTL(),
185 242 : false));
186 242 : basegfx::B2DHomMatrix aNewTransform;
187 :
188 : // add font scale to new transform
189 242 : aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
190 :
191 : // look for proportional font scaling, if necessary, scale accordingly
192 242 : if(100 != rInfo.mrFont.GetPropr())
193 : {
194 0 : const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
195 0 : aNewTransform.scale(fFactor, fFactor);
196 : }
197 :
198 : // apply font rotate
199 242 : if(rInfo.mrFont.GetOrientation())
200 : {
201 0 : aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800);
202 : }
203 :
204 : // look for escapement, if necessary, translate accordingly
205 242 : if(rInfo.mrFont.GetEscapement())
206 : {
207 0 : sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
208 :
209 0 : if(DFLT_ESC_AUTO_SUPER == nEsc)
210 : {
211 0 : nEsc = 33;
212 : }
213 0 : else if(DFLT_ESC_AUTO_SUB == nEsc)
214 : {
215 0 : nEsc = -20;
216 : }
217 :
218 0 : if(nEsc > 100)
219 : {
220 0 : nEsc = 100;
221 : }
222 0 : else if(nEsc < -100)
223 : {
224 0 : nEsc = -100;
225 : }
226 :
227 0 : const double fEscapement(nEsc / -100.0);
228 0 : aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
229 : }
230 :
231 : // apply transformA
232 242 : aNewTransform *= maNewTransformA;
233 :
234 : // apply local offset
235 242 : aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
236 :
237 : // also apply embedding object's transform
238 242 : aNewTransform *= maNewTransformB;
239 :
240 : // prepare DXArray content. To make it independent from font size (and such from
241 : // the text transformation), scale it to unit coordinates
242 242 : ::std::vector< double > aDXArray;
243 : static bool bDisableTextArray(false);
244 :
245 242 : if(!bDisableTextArray && rInfo.mpDXArray && rInfo.mnTextLen)
246 : {
247 242 : aDXArray.reserve(rInfo.mnTextLen);
248 :
249 2073 : for(xub_StrLen a(0); a < rInfo.mnTextLen; a++)
250 : {
251 1831 : aDXArray.push_back((double)rInfo.mpDXArray[a]);
252 : }
253 : }
254 :
255 : // create complex text primitive and append
256 242 : const Color aFontColor(rInfo.mrFont.GetColor());
257 242 : const basegfx::BColor aBFontColor(aFontColor.getBColor());
258 :
259 : // prepare wordLineMode (for underline and strikeout)
260 : // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
261 : // to be split which would not look like the original
262 242 : const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
263 :
264 : // prepare new primitive
265 242 : drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = 0;
266 : const bool bDecoratedIsNeeded(
267 242 : UNDERLINE_NONE != rInfo.mrFont.GetOverline()
268 242 : || UNDERLINE_NONE != rInfo.mrFont.GetUnderline()
269 242 : || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
270 242 : || EMPHASISMARK_NONE != (rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
271 242 : || RELIEF_NONE != rInfo.mrFont.GetRelief()
272 242 : || rInfo.mrFont.IsShadow()
273 1452 : || bWordLineMode);
274 :
275 242 : if(bDecoratedIsNeeded)
276 : {
277 : // TextDecoratedPortionPrimitive2D needed, prepare some more data
278 : // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
279 0 : const Color aUnderlineColor(rInfo.maTextLineColor);
280 0 : const basegfx::BColor aBUnderlineColor((0xffffffff == aUnderlineColor.GetColor()) ? aBFontColor : aUnderlineColor.getBColor());
281 0 : const Color aOverlineColor(rInfo.maOverlineColor);
282 0 : const basegfx::BColor aBOverlineColor((0xffffffff == aOverlineColor.GetColor()) ? aBFontColor : aOverlineColor.getBColor());
283 :
284 : // prepare overline and underline data
285 : const drawinglayer::primitive2d::TextLine eFontOverline(
286 0 : drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetOverline()));
287 : const drawinglayer::primitive2d::TextLine eFontUnderline(
288 0 : drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetUnderline()));
289 :
290 : // check UnderlineAbove
291 : const bool bUnderlineAbove(
292 0 : drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont));
293 :
294 : // prepare strikeout data
295 : const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
296 0 : drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
297 :
298 : // prepare emphasis mark data
299 0 : drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
300 :
301 0 : switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
302 : {
303 0 : case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
304 0 : case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
305 0 : case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
306 0 : case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
307 : }
308 :
309 0 : const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
310 0 : const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
311 :
312 : // prepare font relief data
313 0 : drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
314 :
315 0 : switch(rInfo.mrFont.GetRelief())
316 : {
317 0 : case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
318 0 : case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
319 0 : default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
320 : }
321 :
322 : // prepare shadow/outline data
323 0 : const bool bShadow(rInfo.mrFont.IsShadow());
324 :
325 : // TextDecoratedPortionPrimitive2D is needed, create one
326 : pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
327 :
328 : // attributes for TextSimplePortionPrimitive2D
329 : aNewTransform,
330 : rInfo.mrText,
331 : rInfo.mnTextStart,
332 : rInfo.mnTextLen,
333 : aDXArray,
334 : aFontAttribute,
335 : rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
336 : aBFontColor,
337 :
338 : // attributes for TextDecoratedPortionPrimitive2D
339 : aBOverlineColor,
340 : aBUnderlineColor,
341 : eFontOverline,
342 : eFontUnderline,
343 : bUnderlineAbove,
344 : eTextStrikeout,
345 : bWordLineMode,
346 : eTextEmphasisMark,
347 : bEmphasisMarkAbove,
348 : bEmphasisMarkBelow,
349 : eTextRelief,
350 0 : bShadow);
351 : }
352 : else
353 : {
354 : // TextSimplePortionPrimitive2D is enough
355 : pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
356 : aNewTransform,
357 : rInfo.mrText,
358 : rInfo.mnTextStart,
359 : rInfo.mnTextLen,
360 : aDXArray,
361 : aFontAttribute,
362 : rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
363 : aBFontColor,
364 : rInfo.mbFilled,
365 242 : rInfo.mnWidthToFill);
366 : }
367 :
368 242 : if(rInfo.mbEndOfBullet)
369 : {
370 : // embed in TextHierarchyBulletPrimitive2D
371 28 : const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
372 28 : const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
373 28 : pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
374 : }
375 :
376 242 : if(rInfo.mpFieldData)
377 : {
378 44 : pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo);
379 : }
380 :
381 242 : maTextPortionPrimitives.push_back(pNewPrimitive);
382 :
383 : // support for WrongSpellVector. Create WrongSpellPrimitives as needed
384 242 : if(rInfo.mpWrongSpellVector && !aDXArray.empty())
385 : {
386 0 : const sal_uInt32 nSize(rInfo.mpWrongSpellVector->size());
387 0 : const sal_uInt32 nDXCount(aDXArray.size());
388 0 : const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
389 :
390 0 : for(sal_uInt32 a(0); a < nSize; a++)
391 : {
392 0 : const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
393 :
394 0 : if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
395 : {
396 0 : const sal_uInt32 nStart(rCandidate.nStart - rInfo.mnTextStart);
397 0 : const sal_uInt32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
398 0 : double fStart(0.0);
399 0 : double fEnd(0.0);
400 :
401 0 : if(nStart > 0 && nStart - 1 < nDXCount)
402 : {
403 0 : fStart = aDXArray[nStart - 1];
404 : }
405 :
406 0 : if(nEnd > 0 && nEnd - 1 < nDXCount)
407 : {
408 0 : fEnd = aDXArray[nEnd - 1];
409 : }
410 :
411 0 : if(!basegfx::fTools::equal(fStart, fEnd))
412 : {
413 0 : if(rInfo.IsRTL())
414 : {
415 : // #i98523#
416 : // When the portion is RTL, mirror the redlining using the
417 : // full portion width
418 0 : const double fTextWidth(aDXArray[aDXArray.size() - 1]);
419 :
420 0 : fStart = fTextWidth - fStart;
421 0 : fEnd = fTextWidth - fEnd;
422 : }
423 :
424 : // need to take FontScaling out of values; it's already part of
425 : // aNewTransform and would be double applied
426 0 : const double fFontScaleX(aFontScaling.getX());
427 :
428 0 : if(!basegfx::fTools::equal(fFontScaleX, 1.0)
429 0 : && !basegfx::fTools::equalZero(fFontScaleX))
430 : {
431 0 : fStart /= fFontScaleX;
432 0 : fEnd /= fFontScaleX;
433 : }
434 :
435 : maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
436 : aNewTransform,
437 : fStart,
438 : fEnd,
439 0 : aSpellColor));
440 : }
441 : }
442 0 : }
443 242 : }
444 : }
445 322 : }
446 :
447 44 : drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const
448 : {
449 44 : if(rInfo.mpFieldData)
450 : {
451 : // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
452 : // which holds the field type and, if applicable, the URL
453 44 : const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
454 44 : const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
455 :
456 : // embed current primitive to a sequence
457 44 : drawinglayer::primitive2d::Primitive2DSequence aSequence;
458 :
459 44 : if(pPrimitive)
460 : {
461 44 : aSequence.realloc(1);
462 44 : aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
463 : }
464 :
465 44 : if(pURLField)
466 : {
467 0 : pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
468 : }
469 44 : else if(pPageField)
470 : {
471 12 : pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, String());
472 : }
473 : else
474 : {
475 32 : pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, String());
476 44 : }
477 : }
478 :
479 44 : return pPrimitive;
480 : }
481 :
482 292 : void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
483 : {
484 : // only create a line primitive when we had content; there is no need for
485 : // empty line primitives (contrary to paragraphs, see below).
486 292 : if(!maTextPortionPrimitives.empty())
487 : {
488 212 : drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives));
489 212 : maTextPortionPrimitives.clear();
490 212 : maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence));
491 : }
492 292 : }
493 :
494 213 : void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives()
495 : {
496 : // ALWAYS create a paragraph primitive, even when no content was added. This is done to
497 : // have the correct paragraph count even with empty paragraphs. Those paragraphs will
498 : // have an empty sub-PrimitiveSequence.
499 213 : drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives));
500 213 : maLinePrimitives.clear();
501 213 : maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence));
502 213 : }
503 :
504 322 : void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
505 : {
506 322 : impCreateTextPortionPrimitive(rInfo);
507 :
508 322 : if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
509 : {
510 292 : impFlushTextPortionPrimitivesToLinePrimitives();
511 : }
512 :
513 322 : if(rInfo.mbEndOfParagraph)
514 : {
515 213 : impFlushLinePrimitivesToParagraphPrimitives();
516 : }
517 322 : }
518 :
519 0 : void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
520 : {
521 0 : basegfx::B2DHomMatrix aNewTransform;
522 :
523 : // add size to new transform
524 0 : aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
525 :
526 : // apply transformA
527 0 : aNewTransform *= maNewTransformA;
528 :
529 : // apply local offset
530 0 : aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
531 :
532 : // also apply embedding object's transform
533 0 : aNewTransform *= maNewTransformB;
534 :
535 : // prepare empty GraphicAttr
536 0 : const GraphicAttr aGraphicAttr;
537 :
538 : // create GraphicPrimitive2D
539 : const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
540 : aNewTransform,
541 : rInfo.maBulletGraphicObject,
542 0 : aGraphicAttr));
543 :
544 : // embed in TextHierarchyBulletPrimitive2D
545 0 : const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
546 0 : drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
547 :
548 : // add to output
549 0 : maTextPortionPrimitives.push_back(pNewPrimitive);
550 0 : }
551 :
552 0 : IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo)
553 : {
554 : // for contour text, ignore (clip away) all portions which are below
555 : // the visible area given by maScale
556 0 : if(pInfo && (double)pInfo->mrStartPos.Y() < maScale.getY())
557 : {
558 0 : impHandleDrawPortionInfo(*pInfo);
559 : }
560 :
561 0 : return 0;
562 : }
563 :
564 644 : IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo)
565 : {
566 322 : if(pInfo)
567 : {
568 : // Is clipping wanted? This is text clipping; only accept a portion
569 : // if it's completely in the range
570 322 : if(!maClipRange.isEmpty())
571 : {
572 : // Test start position first; this allows to not get the text range at
573 : // all if text is far outside
574 0 : const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
575 :
576 0 : if(!maClipRange.isInside(aStartPosition))
577 : {
578 0 : return 0;
579 : }
580 :
581 : // Start position is inside. Get TextBoundRect and TopLeft next
582 0 : drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
583 0 : aTextLayouterDevice.setFont(pInfo->mrFont);
584 :
585 : const basegfx::B2DRange aTextBoundRect(
586 : aTextLayouterDevice.getTextBoundRect(
587 0 : pInfo->mrText, pInfo->mnTextStart, pInfo->mnTextLen));
588 0 : const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
589 :
590 0 : if(!maClipRange.isInside(aTopLeft))
591 : {
592 0 : return 0;
593 : }
594 :
595 : // TopLeft is inside. Get BottomRight and check
596 0 : const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
597 :
598 0 : if(!maClipRange.isInside(aBottomRight))
599 : {
600 0 : return 0;
601 0 : }
602 :
603 : // all inside, clip was successful
604 : }
605 322 : impHandleDrawPortionInfo(*pInfo);
606 : }
607 :
608 322 : return 0;
609 : }
610 :
611 0 : IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo)
612 : {
613 0 : if(pInfo)
614 : {
615 0 : impHandleDrawPortionInfo(*pInfo);
616 : }
617 :
618 0 : return 0;
619 : }
620 :
621 0 : IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo)
622 : {
623 0 : if(pInfo)
624 : {
625 0 : impHandleDrawBulletInfo(*pInfo);
626 : }
627 :
628 0 : return 0;
629 : }
630 :
631 0 : IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo)
632 : {
633 0 : if(pInfo)
634 : {
635 0 : impHandleDrawBulletInfo(*pInfo);
636 : }
637 :
638 0 : return 0;
639 : }
640 :
641 0 : IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo)
642 : {
643 0 : if(pInfo)
644 : {
645 0 : impHandleDrawBulletInfo(*pInfo);
646 : }
647 :
648 0 : return 0;
649 : }
650 :
651 109 : drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence()
652 : {
653 109 : if(!maTextPortionPrimitives.empty())
654 : {
655 : // collect non-closed lines
656 0 : impFlushTextPortionPrimitivesToLinePrimitives();
657 : }
658 :
659 109 : if(!maLinePrimitives.empty())
660 : {
661 : // collect non-closed paragraphs
662 0 : impFlushLinePrimitivesToParagraphPrimitives();
663 : }
664 :
665 109 : return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives);
666 : }
667 : } // end of anonymous namespace
668 :
669 : //////////////////////////////////////////////////////////////////////////////
670 : // primitive decompositions
671 :
672 0 : void SdrTextObj::impDecomposeContourTextPrimitive(
673 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
674 : const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
675 : const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
676 : {
677 : // decompose matrix to have position and size of text
678 0 : basegfx::B2DVector aScale, aTranslate;
679 : double fRotate, fShearX;
680 0 : rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
681 :
682 : // prepare contour polygon, force to non-mirrored for laying out
683 0 : basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
684 0 : aPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
685 :
686 : // prepare outliner
687 0 : SdrOutliner& rOutliner = ImpGetDrawOutliner();
688 0 : const Size aNullSize;
689 0 : rOutliner.SetPaperSize(aNullSize);
690 0 : rOutliner.SetPolygon(aPolyPolygon);
691 0 : rOutliner.SetUpdateMode(true);
692 0 : rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
693 :
694 : // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
695 0 : rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
696 :
697 : // prepare matrices to apply to newly created primitives
698 0 : basegfx::B2DHomMatrix aNewTransformA;
699 :
700 : // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
701 : // move the null point which was top left to bottom right.
702 0 : const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
703 0 : const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
704 :
705 : // in-between the translations of the single primitives will take place. Afterwards,
706 : // the object's transformations need to be applied
707 : const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
708 : bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
709 0 : fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
710 :
711 : // now break up text primitives.
712 0 : impTextBreakupHandler aConverter(rOutliner);
713 0 : aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
714 :
715 : // cleanup outliner
716 0 : rOutliner.Clear();
717 0 : rOutliner.setVisualizedPage(0);
718 :
719 0 : rTarget = aConverter.getPrimitive2DSequence();
720 0 : }
721 :
722 2 : void SdrTextObj::impDecomposeAutoFitTextPrimitive(
723 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
724 : const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
725 : const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
726 : {
727 : // decompose matrix to have position and size of text
728 2 : basegfx::B2DVector aScale, aTranslate;
729 : double fRotate, fShearX;
730 2 : rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
731 :
732 : // use B2DRange aAnchorTextRange for calculations
733 2 : basegfx::B2DRange aAnchorTextRange(aTranslate);
734 2 : aAnchorTextRange.expand(aTranslate + aScale);
735 :
736 : // prepare outliner
737 2 : const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
738 2 : SdrOutliner& rOutliner = ImpGetDrawOutliner();
739 2 : SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
740 2 : SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
741 2 : const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
742 2 : const Size aNullSize;
743 :
744 : // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
745 2 : rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
746 :
747 2 : rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE|EE_CNTRL_STRETCHING);
748 2 : rOutliner.SetMinAutoPaperSize(aNullSize);
749 2 : rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
750 :
751 : // add one to rage sizes to get back to the old Rectangle and outliner measurements
752 2 : const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
753 2 : const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
754 2 : const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
755 : OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
756 2 : const bool bVerticalWritintg(pOutlinerParaObject->IsVertical());
757 2 : const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
758 :
759 2 : if((rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame()))
760 : {
761 2 : rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
762 : }
763 :
764 2 : if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg)
765 : {
766 2 : rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
767 : }
768 :
769 2 : if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg)
770 : {
771 0 : rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
772 : }
773 :
774 2 : rOutliner.SetPaperSize(aNullSize);
775 2 : rOutliner.SetUpdateMode(true);
776 2 : rOutliner.SetText(*pOutlinerParaObject);
777 2 : ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWritintg);
778 :
779 : // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
780 2 : rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
781 :
782 : // now get back the layouted text size from outliner
783 2 : const Size aOutlinerTextSiz(rOutliner.GetPaperSize());
784 2 : const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height());
785 2 : basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
786 :
787 : // correct horizontal translation using the now known text size
788 2 : if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
789 : {
790 0 : const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
791 :
792 0 : if(SDRTEXTHORZADJUST_CENTER == eHAdj)
793 : {
794 0 : aAdjustTranslate.setX(fFree / 2.0);
795 : }
796 :
797 0 : if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
798 : {
799 0 : aAdjustTranslate.setX(fFree);
800 : }
801 : }
802 :
803 : // correct vertical translation using the now known text size
804 2 : if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
805 : {
806 0 : const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
807 :
808 0 : if(SDRTEXTVERTADJUST_CENTER == eVAdj)
809 : {
810 0 : aAdjustTranslate.setY(fFree / 2.0);
811 : }
812 :
813 0 : if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
814 : {
815 0 : aAdjustTranslate.setY(fFree);
816 : }
817 : }
818 :
819 : // prepare matrices to apply to newly created primitives. aNewTransformA
820 : // will get coordinates in aOutlinerScale size and positive in X, Y.
821 2 : basegfx::B2DHomMatrix aNewTransformA;
822 2 : basegfx::B2DHomMatrix aNewTransformB;
823 :
824 : // translate relative to given primitive to get same rotation and shear
825 : // as the master shape we are working on. For vertical, use the top-right
826 : // corner
827 2 : const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
828 2 : aNewTransformA.translate(fStartInX, aAdjustTranslate.getY());
829 :
830 : // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
831 : // move the null point which was top left to bottom right.
832 2 : const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
833 2 : const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
834 2 : aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
835 :
836 : // in-between the translations of the single primitives will take place. Afterwards,
837 : // the object's transformations need to be applied
838 2 : aNewTransformB.shearX(fShearX);
839 2 : aNewTransformB.rotate(fRotate);
840 2 : aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
841 :
842 2 : basegfx::B2DRange aClipRange;
843 :
844 : // now break up text primitives.
845 2 : impTextBreakupHandler aConverter(rOutliner);
846 2 : aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
847 :
848 : // cleanup outliner
849 2 : rOutliner.Clear();
850 2 : rOutliner.setVisualizedPage(0);
851 2 : rOutliner.SetControlWord(nOriginalControlWord);
852 :
853 2 : rTarget = aConverter.getPrimitive2DSequence();
854 2 : }
855 :
856 107 : void SdrTextObj::impDecomposeBlockTextPrimitive(
857 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
858 : const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
859 : const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
860 : {
861 : // decompose matrix to have position and size of text
862 107 : basegfx::B2DVector aScale, aTranslate;
863 : double fRotate, fShearX;
864 107 : rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
865 :
866 : // use B2DRange aAnchorTextRange for calculations
867 107 : basegfx::B2DRange aAnchorTextRange(aTranslate);
868 107 : aAnchorTextRange.expand(aTranslate + aScale);
869 :
870 : // prepare outliner
871 107 : const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
872 107 : SdrOutliner& rOutliner = ImpGetDrawOutliner();
873 107 : SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
874 107 : SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
875 107 : const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
876 107 : const Size aNullSize;
877 :
878 : // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
879 107 : rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
880 107 : rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
881 107 : rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE);
882 107 : rOutliner.SetMinAutoPaperSize(aNullSize);
883 107 : rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
884 :
885 : // add one to rage sizes to get back to the old Rectangle and outliner measurements
886 107 : const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
887 107 : const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
888 107 : const bool bVerticalWritintg(rSdrBlockTextPrimitive.getOutlinerParaObject().IsVertical());
889 107 : const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
890 :
891 107 : if(bIsCell)
892 : {
893 : // cell text is formated neither like a text object nor like a object
894 : // text, so use a special setup here
895 0 : rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
896 :
897 : // #i106214# To work with an unchangeable PaperSize (CellSize in
898 : // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
899 : // #i106214# This was not completely correct; to still measure the real
900 : // text height to allow vertical adjust (and vice versa for VerticalWritintg)
901 : // only one aspect has to be set, but the other one to zero
902 0 : if(bVerticalWritintg)
903 : {
904 : // measure the horizontal text size
905 0 : rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
906 : }
907 : else
908 : {
909 : // measure the vertical text size
910 0 : rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
911 : }
912 :
913 0 : rOutliner.SetPaperSize(aAnchorTextSize);
914 0 : rOutliner.SetUpdateMode(true);
915 0 : rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
916 : }
917 : else
918 : {
919 : // check if block text is used (only one of them can be true)
920 107 : const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg);
921 107 : const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg);
922 :
923 : // set minimal paper size horizontally/vertically if needed
924 107 : if(bHorizontalIsBlock)
925 : {
926 79 : rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
927 : }
928 28 : else if(bVerticalIsBlock)
929 : {
930 0 : rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
931 : }
932 :
933 107 : if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
934 : {
935 : // #i103454# maximal paper size hor/ver needs to be limited to text
936 : // frame size. If it's block text, still allow the 'other' direction
937 : // to grow to get a correct real text size when using GetPaperSize().
938 : // When just using aAnchorTextSize as maximum, GetPaperSize()
939 : // would just return aAnchorTextSize again: this means, the wanted
940 : // 'measurement' of the real size of block text would not work
941 104 : Size aMaxAutoPaperSize(aAnchorTextSize);
942 :
943 104 : if(bHorizontalIsBlock)
944 : {
945 : // allow to grow vertical for horizontal blocks
946 79 : aMaxAutoPaperSize.setHeight(1000000);
947 : }
948 25 : else if(bVerticalIsBlock)
949 : {
950 : // allow to grow horizontal for vertical blocks
951 0 : aMaxAutoPaperSize.setWidth(1000000);
952 : }
953 :
954 104 : rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
955 : }
956 :
957 107 : rOutliner.SetPaperSize(aNullSize);
958 107 : rOutliner.SetUpdateMode(true);
959 107 : rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
960 : }
961 :
962 107 : rOutliner.SetControlWord(nOriginalControlWord);
963 :
964 : // now get back the layouted text size from outliner
965 107 : const Size aOutlinerTextSiz(rOutliner.GetPaperSize());
966 107 : const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height());
967 107 : basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
968 :
969 : // For draw objects containing text correct hor/ver alignment if text is bigger
970 : // than the object itself. Without that correction, the text would always be
971 : // formatted to the left edge (or top edge when vertical) of the draw object.
972 107 : if(!IsTextFrame() && !bIsCell)
973 : {
974 9 : if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg)
975 : {
976 : // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
977 : // else the alignment is wanted.
978 7 : if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
979 : {
980 6 : eHAdj = SDRTEXTHORZADJUST_CENTER;
981 : }
982 : }
983 :
984 9 : if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg)
985 : {
986 : // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
987 : // else the alignment is wanted.
988 0 : if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
989 : {
990 0 : eVAdj = SDRTEXTVERTADJUST_CENTER;
991 : }
992 : }
993 : }
994 :
995 : // correct horizontal translation using the now known text size
996 107 : if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
997 : {
998 9 : const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
999 :
1000 9 : if(SDRTEXTHORZADJUST_CENTER == eHAdj)
1001 : {
1002 9 : aAdjustTranslate.setX(fFree / 2.0);
1003 : }
1004 :
1005 9 : if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
1006 : {
1007 0 : aAdjustTranslate.setX(fFree);
1008 : }
1009 : }
1010 :
1011 : // correct vertical translation using the now known text size
1012 107 : if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1013 : {
1014 33 : const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
1015 :
1016 33 : if(SDRTEXTVERTADJUST_CENTER == eVAdj)
1017 : {
1018 17 : aAdjustTranslate.setY(fFree / 2.0);
1019 : }
1020 :
1021 33 : if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1022 : {
1023 16 : aAdjustTranslate.setY(fFree);
1024 : }
1025 : }
1026 :
1027 : // prepare matrices to apply to newly created primitives. aNewTransformA
1028 : // will get coordinates in aOutlinerScale size and positive in X, Y.
1029 : // Translate relative to given primitive to get same rotation and shear
1030 : // as the master shape we are working on. For vertical, use the top-right
1031 : // corner
1032 107 : const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
1033 107 : const basegfx::B2DTuple aAdjOffset(fStartInX, aAdjustTranslate.getY());
1034 107 : basegfx::B2DHomMatrix aNewTransformA(basegfx::tools::createTranslateB2DHomMatrix(aAdjOffset.getX(), aAdjOffset.getY()));
1035 :
1036 : // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1037 : // move the null point which was top left to bottom right.
1038 107 : const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1039 107 : const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1040 :
1041 : // in-between the translations of the single primitives will take place. Afterwards,
1042 : // the object's transformations need to be applied
1043 : const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1044 : bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1045 107 : fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1046 :
1047 : // create ClipRange (if needed)
1048 107 : basegfx::B2DRange aClipRange;
1049 :
1050 107 : if(rSdrBlockTextPrimitive.getClipOnBounds())
1051 : {
1052 0 : aClipRange.expand(-aAdjOffset);
1053 0 : aClipRange.expand(basegfx::B2DTuple(aAnchorTextSize.Width(), aAnchorTextSize.Height()) - aAdjOffset);
1054 : }
1055 :
1056 : // now break up text primitives.
1057 107 : impTextBreakupHandler aConverter(rOutliner);
1058 107 : aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
1059 :
1060 : // cleanup outliner
1061 107 : rOutliner.Clear();
1062 107 : rOutliner.setVisualizedPage(0);
1063 :
1064 107 : rTarget = aConverter.getPrimitive2DSequence();
1065 107 : }
1066 :
1067 0 : void SdrTextObj::impDecomposeStretchTextPrimitive(
1068 : drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1069 : const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
1070 : const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
1071 : {
1072 : // decompose matrix to have position and size of text
1073 0 : basegfx::B2DVector aScale, aTranslate;
1074 : double fRotate, fShearX;
1075 0 : rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
1076 :
1077 : // use non-mirrored B2DRange aAnchorTextRange for calculations
1078 0 : basegfx::B2DRange aAnchorTextRange(aTranslate);
1079 0 : aAnchorTextRange.expand(aTranslate + aScale);
1080 :
1081 : // prepare outliner
1082 0 : SdrOutliner& rOutliner = ImpGetDrawOutliner();
1083 0 : const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
1084 0 : const Size aNullSize;
1085 :
1086 0 : rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_STRETCHING|EE_CNTRL_AUTOPAGESIZE);
1087 0 : rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
1088 0 : rOutliner.SetMinAutoPaperSize(aNullSize);
1089 0 : rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
1090 0 : rOutliner.SetPaperSize(aNullSize);
1091 0 : rOutliner.SetUpdateMode(true);
1092 0 : rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
1093 :
1094 : // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1095 0 : rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1096 :
1097 : // now get back the laid out text size from outliner
1098 0 : const Size aOutlinerTextSiz(rOutliner.CalcTextSize());
1099 : const basegfx::B2DVector aOutlinerScale(
1100 0 : basegfx::fTools::equalZero(aOutlinerTextSiz.Width()) ? 1.0 : aOutlinerTextSiz.Width(),
1101 0 : basegfx::fTools::equalZero(aOutlinerTextSiz.Height()) ? 1.0 : aOutlinerTextSiz.Height());
1102 :
1103 : // prepare matrices to apply to newly created primitives
1104 0 : basegfx::B2DHomMatrix aNewTransformA;
1105 :
1106 : // #i101957# Check for vertical text. If used, aNewTransformA
1107 : // needs to translate the text initially around object width to orient
1108 : // it relative to the topper right instead of the topper left
1109 0 : const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsVertical());
1110 :
1111 0 : if(bVertical)
1112 : {
1113 0 : aNewTransformA.translate(aScale.getX(), 0.0);
1114 : }
1115 :
1116 : // calculate global char stretching scale parameters. Use non-mirrored sizes
1117 : // to layout without mirroring
1118 0 : const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
1119 0 : const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
1120 0 : rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0));
1121 :
1122 : // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1123 : // move the null point which was top left to bottom right.
1124 0 : const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1125 0 : const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1126 :
1127 : // in-between the translations of the single primitives will take place. Afterwards,
1128 : // the object's transformations need to be applied
1129 : const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1130 : bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1131 0 : fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1132 :
1133 : // now break up text primitives.
1134 0 : impTextBreakupHandler aConverter(rOutliner);
1135 0 : aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
1136 :
1137 : // cleanup outliner
1138 0 : rOutliner.SetControlWord(nOriginalControlWord);
1139 0 : rOutliner.Clear();
1140 0 : rOutliner.setVisualizedPage(0);
1141 :
1142 0 : rTarget = aConverter.getPrimitive2DSequence();
1143 0 : }
1144 :
1145 : //////////////////////////////////////////////////////////////////////////////
1146 : // timing generators
1147 : #define ENDLESS_LOOP (0xffffffff)
1148 : #define ENDLESS_TIME ((double)0xffffffff)
1149 : #define PIXEL_DPI (96.0)
1150 :
1151 0 : void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
1152 : {
1153 0 : if(SDRTEXTANI_BLINK == GetTextAniKind())
1154 : {
1155 : // get values
1156 0 : const SfxItemSet& rSet = GetObjectItemSet();
1157 0 : const sal_uInt32 nRepeat((sal_uInt32)((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1158 0 : bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1159 0 : double fDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1160 :
1161 0 : if(0.0 == fDelay)
1162 : {
1163 : // use default
1164 0 : fDelay = 250.0;
1165 : }
1166 :
1167 : // prepare loop and add
1168 0 : drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1169 0 : drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0);
1170 0 : aLoop.append(aStart);
1171 0 : drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0);
1172 0 : aLoop.append(aEnd);
1173 0 : rAnimList.append(aLoop);
1174 :
1175 : // add stopped state if loop is not endless
1176 0 : if(0L != nRepeat)
1177 : {
1178 0 : drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisisbleWhenStopped ? 0.0 : 1.0);
1179 0 : rAnimList.append(aStop);
1180 0 : }
1181 : }
1182 0 : }
1183 :
1184 0 : void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1185 : {
1186 0 : bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1187 0 : bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1188 0 : const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1189 :
1190 0 : if(bVisisbleWhenStarted)
1191 : {
1192 : // move from center to outside
1193 0 : drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1194 0 : rAnimList.append(aInOut);
1195 : }
1196 :
1197 : // loop. In loop, move through
1198 0 : if(nRepeat || 0L == nRepeat)
1199 : {
1200 0 : drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1201 0 : drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
1202 0 : aLoop.append(aThrough);
1203 0 : rAnimList.append(aLoop);
1204 : }
1205 :
1206 0 : if(0L != nRepeat && bVisisbleWhenStopped)
1207 : {
1208 : // move from outside to center
1209 0 : drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1210 0 : rAnimList.append(aOutIn);
1211 :
1212 : // add timing for staying at the end
1213 0 : drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1214 0 : rAnimList.append(aEnd);
1215 : }
1216 0 : }
1217 :
1218 0 : void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
1219 : {
1220 0 : if(basegfx::fTools::more(fRelativeTextLength, 0.5))
1221 : {
1222 : // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
1223 : // In that case, correct direction
1224 0 : bForward = !bForward;
1225 : }
1226 :
1227 0 : const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
1228 0 : const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
1229 0 : bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1230 0 : bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1231 0 : const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1232 :
1233 0 : if(!bVisisbleWhenStarted)
1234 : {
1235 : // move from outside to center
1236 0 : drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1237 0 : rAnimList.append(aOutIn);
1238 : }
1239 :
1240 : // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
1241 : // so use absolute value
1242 0 : const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
1243 0 : const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
1244 0 : const double fHalfInnerPath(fTimeForInnerPath * 0.5);
1245 0 : const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
1246 :
1247 0 : if(nDoubleRepeat || 0L == nRepeat)
1248 : {
1249 : // double forth and back loop
1250 0 : drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
1251 0 : drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1252 0 : aLoop.append(aTime0);
1253 0 : drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
1254 0 : aLoop.append(aTime1);
1255 0 : drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
1256 0 : aLoop.append(aTime2);
1257 0 : rAnimList.append(aLoop);
1258 : }
1259 :
1260 0 : if(nRepeat % 2L)
1261 : {
1262 : // repeat is uneven, so we need one more forth and back to center
1263 0 : drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1264 0 : rAnimList.append(aTime0);
1265 0 : drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
1266 0 : rAnimList.append(aTime1);
1267 : }
1268 :
1269 0 : if(0L != nRepeat)
1270 : {
1271 0 : if(bVisisbleWhenStopped)
1272 : {
1273 : // add timing for staying at the end
1274 0 : drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1275 0 : rAnimList.append(aEnd);
1276 : }
1277 : else
1278 : {
1279 : // move from center to outside
1280 0 : drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1281 0 : rAnimList.append(aInOut);
1282 : }
1283 : }
1284 0 : }
1285 :
1286 0 : void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1287 : {
1288 : // move in from outside, start outside
1289 0 : const double fStartPosition(bForward ? 0.0 : 1.0);
1290 0 : const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1291 :
1292 : // move from outside to center
1293 0 : drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1294 0 : rAnimList.append(aOutIn);
1295 :
1296 : // loop. In loop, move out and in again
1297 0 : if(nRepeat > 1L || 0L == nRepeat)
1298 : {
1299 0 : drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1L : ENDLESS_LOOP);
1300 0 : drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
1301 0 : aLoop.append(aTime0);
1302 0 : drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1303 0 : aLoop.append(aTime1);
1304 0 : rAnimList.append(aLoop);
1305 : }
1306 :
1307 : // always visible when stopped, so add timing for staying at the end when not endless
1308 0 : if(0L != nRepeat)
1309 : {
1310 0 : drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1311 0 : rAnimList.append(aEnd);
1312 0 : }
1313 0 : }
1314 :
1315 0 : void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
1316 : {
1317 0 : const SdrTextAniKind eAniKind(GetTextAniKind());
1318 :
1319 0 : if(SDRTEXTANI_SCROLL == eAniKind || SDRTEXTANI_ALTERNATE == eAniKind || SDRTEXTANI_SLIDE == eAniKind)
1320 : {
1321 : // get data. Goal is to calculate fTimeFullPath which is the time needed to
1322 : // move animation from (0.0) to (1.0) state
1323 0 : const SfxItemSet& rSet = GetObjectItemSet();
1324 0 : double fAnimationDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1325 0 : double fSingleStepWidth((double)((SdrTextAniAmountItem&)rSet.Get(SDRATTR_TEXT_ANIAMOUNT)).GetValue());
1326 0 : const SdrTextAniDirection eDirection(GetTextAniDirection());
1327 0 : const bool bForward(SDRTEXTANI_RIGHT == eDirection || SDRTEXTANI_DOWN == eDirection);
1328 :
1329 0 : if(basegfx::fTools::equalZero(fAnimationDelay))
1330 : {
1331 : // default to 1/20 second
1332 0 : fAnimationDelay = 50.0;
1333 : }
1334 :
1335 0 : if(basegfx::fTools::less(fSingleStepWidth, 0.0))
1336 : {
1337 : // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
1338 : // It makes no sense to keep the view-transformation centered
1339 : // definitions, so get rid of them here.
1340 0 : fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
1341 : }
1342 :
1343 0 : if(basegfx::fTools::equalZero(fSingleStepWidth))
1344 : {
1345 : // default to 1 millimeter
1346 0 : fSingleStepWidth = 100.0;
1347 : }
1348 :
1349 : // use the length of the full animation path and the number of steps
1350 : // to get the full path time
1351 0 : const double fFullPathLength(fFrameLength + fTextLength);
1352 0 : const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
1353 0 : double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
1354 :
1355 0 : if(fTimeFullPath < fAnimationDelay)
1356 : {
1357 0 : fTimeFullPath = fAnimationDelay;
1358 : }
1359 :
1360 0 : switch(eAniKind)
1361 : {
1362 : case SDRTEXTANI_SCROLL :
1363 : {
1364 0 : impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1365 0 : break;
1366 : }
1367 : case SDRTEXTANI_ALTERNATE :
1368 : {
1369 0 : double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
1370 0 : impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
1371 0 : break;
1372 : }
1373 : case SDRTEXTANI_SLIDE :
1374 : {
1375 0 : impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1376 0 : break;
1377 : }
1378 0 : default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK
1379 : }
1380 : }
1381 0 : }
1382 :
1383 :
1384 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|