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 : #include <hintids.hxx>
21 : #include <vcl/metric.hxx>
22 : #include <vcl/window.hxx>
23 : #include <vcl/svapp.hxx>
24 : #include <paratr.hxx>
25 : #include <txtfrm.hxx>
26 : #include <charfmt.hxx>
27 : #include <viewopt.hxx>
28 : #include <viewsh.hxx>
29 : #include <pordrop.hxx>
30 : #include <itrform2.hxx>
31 : #include <txtpaint.hxx>
32 : #include <blink.hxx>
33 : #include <breakit.hxx>
34 : #include <com/sun/star/i18n/ScriptType.hpp>
35 : #include <com/sun/star/i18n/WordType.hpp>
36 : #include <editeng/langitem.hxx>
37 : #include <charatr.hxx>
38 : #include <editeng/fhgtitem.hxx>
39 : #include <calbck.hxx>
40 :
41 : using namespace ::com::sun::star::i18n;
42 : using namespace ::com::sun::star;
43 :
44 : /**
45 : * Calculates if a drop caps portion intersects with a fly
46 : * The width and height of the drop caps portion are passed as arguments,
47 : * the position is calculated from the values in rInf
48 : */
49 18 : static bool lcl_IsDropFlyInter( const SwTextFormatInfo &rInf,
50 : sal_uInt16 nWidth, sal_uInt16 nHeight )
51 : {
52 18 : const SwTextFly& rTextFly = rInf.GetTextFly();
53 18 : if( rTextFly.IsOn() )
54 : {
55 0 : SwRect aRect( rInf.GetTextFrm()->Frm().Pos(), Size( nWidth, nHeight) );
56 0 : aRect.Pos() += rInf.GetTextFrm()->Prt().Pos();
57 0 : aRect.Pos().X() += rInf.X();
58 0 : aRect.Pos().Y() = rInf.Y();
59 0 : aRect = rTextFly.GetFrm( aRect );
60 0 : return aRect.HasArea();
61 : }
62 :
63 18 : return false;
64 : }
65 :
66 : class SwDropSave
67 : {
68 : SwTextPaintInfo* pInf;
69 : sal_Int32 nIdx;
70 : sal_Int32 nLen;
71 : long nX;
72 : long nY;
73 :
74 : public:
75 : explicit SwDropSave( const SwTextPaintInfo &rInf );
76 : ~SwDropSave();
77 : };
78 :
79 6 : SwDropSave::SwDropSave( const SwTextPaintInfo &rInf ) :
80 6 : pInf( const_cast<SwTextPaintInfo*>(&rInf) ), nIdx( rInf.GetIdx() ),
81 12 : nLen( rInf.GetLen() ), nX( rInf.X() ), nY( rInf.Y() )
82 : {
83 6 : }
84 :
85 6 : SwDropSave::~SwDropSave()
86 : {
87 6 : pInf->SetIdx( nIdx );
88 6 : pInf->SetLen( nLen );
89 6 : pInf->X( nX );
90 6 : pInf->Y( nY );
91 6 : }
92 :
93 : /// SwDropPortionPart DTor
94 6 : SwDropPortionPart::~SwDropPortionPart()
95 : {
96 6 : delete pFollow;
97 6 : delete pFnt;
98 6 : }
99 :
100 : /// SwDropPortion CTor, DTor
101 12 : SwDropPortion::SwDropPortion( const sal_uInt16 nLineCnt,
102 : const sal_uInt16 nDrpHeight,
103 : const sal_uInt16 nDrpDescent,
104 : const sal_uInt16 nDist )
105 : : pPart( 0 ),
106 : nLines( nLineCnt ),
107 : nDropHeight(nDrpHeight),
108 : nDropDescent(nDrpDescent),
109 : nDistance(nDist),
110 : nFix(0),
111 : nX(0),
112 12 : nY(0)
113 : {
114 12 : SetWhichPor( POR_DROP );
115 12 : }
116 :
117 36 : SwDropPortion::~SwDropPortion()
118 : {
119 12 : delete pPart;
120 12 : if( pBlink )
121 0 : pBlink->Delete( this );
122 24 : }
123 :
124 : /// nWishLen = 0 indicates that we want a whole word
125 12 : sal_Int32 SwTextNode::GetDropLen( sal_Int32 nWishLen ) const
126 : {
127 12 : sal_Int32 nEnd = GetText().getLength();
128 12 : if( nWishLen && nWishLen < nEnd )
129 0 : nEnd = nWishLen;
130 :
131 12 : if ( ! nWishLen && g_pBreakIt->GetBreakIter().is() )
132 : {
133 : // find first word
134 0 : const SwAttrSet& rAttrSet = GetSwAttrSet();
135 0 : const sal_uInt16 nTextScript = g_pBreakIt->GetRealScriptOfText( GetText(), 0 );
136 :
137 : LanguageType eLanguage;
138 :
139 0 : switch ( nTextScript )
140 : {
141 : case i18n::ScriptType::ASIAN :
142 0 : eLanguage = rAttrSet.GetCJKLanguage().GetLanguage();
143 0 : break;
144 : case i18n::ScriptType::COMPLEX :
145 0 : eLanguage = rAttrSet.GetCTLLanguage().GetLanguage();
146 0 : break;
147 : default :
148 0 : eLanguage = rAttrSet.GetLanguage().GetLanguage();
149 0 : break;
150 : }
151 :
152 : Boundary aBound =
153 0 : g_pBreakIt->GetBreakIter()->getWordBoundary( GetText(), 0,
154 0 : g_pBreakIt->GetLocale( eLanguage ), WordType::DICTIONARY_WORD, true );
155 :
156 0 : nEnd = aBound.endPos;
157 : }
158 :
159 12 : sal_Int32 i = 0;
160 24 : for( ; i < nEnd; ++i )
161 : {
162 12 : sal_Unicode const cChar = GetText()[i];
163 12 : if( CH_TAB == cChar || CH_BREAK == cChar ||
164 12 : (( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar )
165 0 : && SwTextSizeInfo::_HasHint( this, i ) ) )
166 0 : break;
167 : }
168 12 : return i;
169 : }
170 :
171 : /**
172 : * If a dropcap is found the return value is true otherwise false. The
173 : * drop cap sizes passed back by reference are font height, drop height
174 : * and drop descent.
175 : */
176 0 : bool SwTextNode::GetDropSize(int& rFontHeight, int& rDropHeight, int& rDropDescent) const
177 : {
178 0 : rFontHeight = 0;
179 0 : rDropHeight = 0;
180 0 : rDropDescent =0;
181 :
182 0 : const SwAttrSet& rSet = GetSwAttrSet();
183 0 : const SwFormatDrop& rDrop = rSet.GetDrop();
184 :
185 : // Return (0,0) if there is no drop cap at this paragraph
186 0 : if( 1 >= rDrop.GetLines() ||
187 0 : ( !rDrop.GetChars() && !rDrop.GetWholeWord() ) )
188 : {
189 0 : return false;
190 : }
191 :
192 : // get text frame
193 0 : SwIterator<SwTextFrm,SwTextNode> aIter( *this );
194 0 : for( SwTextFrm* pLastFrm = aIter.First(); pLastFrm; pLastFrm = aIter.Next() )
195 : {
196 : // Only (master-) text frames can have a drop cap.
197 0 : if ( !pLastFrm->IsFollow() )
198 : {
199 :
200 0 : if( !pLastFrm->HasPara() )
201 0 : pLastFrm->GetFormatted();
202 :
203 0 : if ( !pLastFrm->IsEmpty() )
204 : {
205 0 : const SwParaPortion* pPara = pLastFrm->GetPara();
206 : OSL_ENSURE( pPara, "GetDropSize could not find the ParaPortion, I'll guess the drop cap size" );
207 :
208 0 : if ( pPara )
209 : {
210 0 : const SwLinePortion* pFirstPor = pPara->GetFirstPortion();
211 0 : if (pFirstPor && pFirstPor->IsDropPortion())
212 : {
213 0 : const SwDropPortion* pDrop = static_cast<const SwDropPortion*>(pFirstPor);
214 0 : rDropHeight = pDrop->GetDropHeight();
215 0 : rDropDescent = pDrop->GetDropDescent();
216 0 : if (const SwFont *pFont = pDrop->GetFnt())
217 0 : rFontHeight = pFont->GetSize(pFont->GetActual()).Height();
218 : else
219 : {
220 0 : const SvxFontHeightItem& rItem = static_cast<const SvxFontHeightItem&>(rSet.Get(RES_CHRATR_FONTSIZE));
221 0 : rFontHeight = rItem.GetHeight();
222 : }
223 : }
224 : }
225 : }
226 0 : break;
227 : }
228 : }
229 :
230 0 : if (rFontHeight==0 && rDropHeight==0 && rDropDescent==0)
231 : {
232 0 : const sal_uInt16 nLines = rDrop.GetLines();
233 :
234 0 : const SvxFontHeightItem& rItem = static_cast<const SvxFontHeightItem&>(rSet.Get( RES_CHRATR_FONTSIZE ));
235 0 : rFontHeight = rItem.GetHeight();
236 0 : rDropHeight = nLines * rFontHeight;
237 0 : rDropDescent = rFontHeight / 5;
238 0 : return false;
239 : }
240 :
241 0 : return true;
242 : }
243 :
244 : /// Manipulate the width, otherwise the chars are being stretched
245 0 : void SwDropPortion::PaintText( const SwTextPaintInfo &rInf ) const
246 : {
247 : OSL_ENSURE( nDropHeight && pPart && nLines != 1, "Drop Portion painted twice" );
248 :
249 0 : const SwDropPortionPart* pCurrPart = GetPart();
250 0 : const sal_Int32 nOldLen = GetLen();
251 0 : const sal_uInt16 nOldWidth = Width();
252 0 : const sal_uInt16 nOldAscent = GetAscent();
253 :
254 0 : const SwTwips nBasePosY = rInf.Y();
255 0 : const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY + nY );
256 0 : const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent + nY );
257 0 : SwDropSave aSave( rInf );
258 : // for text inside drop portions we let vcl handle the text directions
259 0 : SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
260 0 : aLayoutModeModifier.SetAuto();
261 :
262 0 : while ( pCurrPart )
263 : {
264 0 : const_cast<SwDropPortion*>(this)->SetLen( pCurrPart->GetLen() );
265 0 : const_cast<SwDropPortion*>(this)->Width( pCurrPart->GetWidth() );
266 0 : const_cast<SwTextPaintInfo&>(rInf).SetLen( pCurrPart->GetLen() );
267 0 : SwFontSave aFontSave( rInf, &pCurrPart->GetFont() );
268 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
269 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
270 :
271 0 : if ( rInf.OnWin() &&
272 0 : !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() &&
273 0 : (!pCurrPart->GetFont().GetBackColor() || *pCurrPart->GetFont().GetBackColor() == Color(COL_TRANSPARENT)) )
274 : {
275 0 : rInf.DrawBackground( *this );
276 : }
277 :
278 0 : SwTextPortion::Paint( rInf );
279 :
280 0 : const_cast<SwTextPaintInfo&>(rInf).SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
281 0 : const_cast<SwTextPaintInfo&>(rInf).X( rInf.X() + pCurrPart->GetWidth() );
282 0 : pCurrPart = pCurrPart->GetFollow();
283 0 : }
284 :
285 0 : const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY );
286 0 : const_cast<SwDropPortion*>(this)->Width( nOldWidth );
287 0 : const_cast<SwDropPortion*>(this)->SetLen( nOldLen );
288 0 : const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
289 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
290 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
291 0 : }
292 :
293 0 : void SwDropPortion::PaintDrop( const SwTextPaintInfo &rInf ) const
294 : {
295 : // normal output is being done during the normal painting
296 0 : if( ! nDropHeight || ! pPart || nLines == 1 )
297 0 : return;
298 :
299 : // set the lying values
300 0 : const sal_uInt16 nOldHeight = Height();
301 0 : const sal_uInt16 nOldWidth = Width();
302 0 : const sal_uInt16 nOldAscent = GetAscent();
303 0 : const SwTwips nOldPosY = rInf.Y();
304 0 : const SwTwips nOldPosX = rInf.X();
305 0 : const SwParaPortion *pPara = rInf.GetParaPortion();
306 0 : const Point aOutPos( nOldPosX + nX, nOldPosY - pPara->GetAscent()
307 0 : - pPara->GetRealHeight() + pPara->Height() );
308 : // make good for retouching
309 :
310 : // Set baseline
311 0 : const_cast<SwTextPaintInfo&>(rInf).Y( aOutPos.Y() + nDropHeight );
312 :
313 : // for background
314 0 : const_cast<SwDropPortion*>(this)->Height( nDropHeight + nDropDescent );
315 0 : const_cast<SwDropPortion*>(this)->Width( Width() - nX );
316 0 : const_cast<SwDropPortion*>(this)->SetAscent( nDropHeight );
317 :
318 : // Always adapt Clipregion to us, never set it off using the existing ClipRect
319 : // as that could be set for the line
320 0 : SwRect aClipRect;
321 0 : if ( rInf.OnWin() )
322 : {
323 0 : aClipRect = SwRect( aOutPos, SvLSize() );
324 0 : aClipRect.Intersection( rInf.GetPaintRect() );
325 : }
326 0 : SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
327 0 : aClip.ChgClip( aClipRect, rInf.GetTextFrm() );
328 :
329 : // Just do, what we always do ...
330 0 : PaintText( rInf );
331 :
332 : // save old values
333 0 : const_cast<SwDropPortion*>(this)->Height( nOldHeight );
334 0 : const_cast<SwDropPortion*>(this)->Width( nOldWidth );
335 0 : const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
336 0 : const_cast<SwTextPaintInfo&>(rInf).Y( nOldPosY );
337 : }
338 :
339 2 : void SwDropPortion::Paint( const SwTextPaintInfo &rInf ) const
340 : {
341 : // normal output is being done here
342 2 : if( ! nDropHeight || ! pPart || 1 == nLines )
343 : {
344 4 : if ( rInf.OnWin() &&
345 2 : !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
346 0 : rInf.DrawBackground( *this );
347 :
348 : // make sure that font is not rotated
349 2 : SwFont* pTmpFont = 0;
350 2 : if ( rInf.GetFont()->GetOrientation( rInf.GetTextFrm()->IsVertical() ) )
351 : {
352 0 : pTmpFont = new SwFont( *rInf.GetFont() );
353 0 : pTmpFont->SetVertical( 0, rInf.GetTextFrm()->IsVertical() );
354 : }
355 :
356 2 : SwFontSave aFontSave( rInf, pTmpFont );
357 : // for text inside drop portions we let vcl handle the text directions
358 4 : SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
359 2 : aLayoutModeModifier.SetAuto();
360 :
361 2 : SwTextPortion::Paint( rInf );
362 4 : delete pTmpFont;
363 : }
364 2 : }
365 :
366 6 : bool SwDropPortion::FormatText( SwTextFormatInfo &rInf )
367 : {
368 6 : const sal_Int32 nOldLen = GetLen();
369 6 : const sal_Int32 nOldInfLen = rInf.GetLen();
370 6 : if (!SwTextPortion::Format( rInf ))
371 6 : return false;
372 :
373 : // looks like shit, but what can we do?
374 0 : rInf.SetUnderflow( 0 );
375 0 : Truncate();
376 0 : SetLen( nOldLen );
377 0 : rInf.SetLen( nOldInfLen );
378 :
379 0 : return true;
380 : }
381 :
382 0 : SwPosSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
383 : {
384 0 : sal_uInt16 nMyX = 0;
385 0 : sal_Int32 nIdx = 0;
386 :
387 0 : const SwDropPortionPart* pCurrPart = GetPart();
388 :
389 : // skip parts
390 0 : while ( pCurrPart && nIdx + pCurrPart->GetLen() < rInf.GetLen() )
391 : {
392 0 : nMyX = nMyX + pCurrPart->GetWidth();
393 0 : nIdx = nIdx + pCurrPart->GetLen();
394 0 : pCurrPart = pCurrPart->GetFollow();
395 : }
396 :
397 0 : sal_Int32 nOldIdx = rInf.GetIdx();
398 0 : sal_Int32 nOldLen = rInf.GetLen();
399 :
400 0 : const_cast<SwTextSizeInfo&>(rInf).SetIdx( nIdx );
401 0 : const_cast<SwTextSizeInfo&>(rInf).SetLen( rInf.GetLen() - nIdx );
402 :
403 0 : if( pCurrPart )
404 : {
405 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
406 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
407 : }
408 :
409 : // robust
410 0 : SwFontSave aFontSave( rInf, pCurrPart ? &pCurrPart->GetFont() : 0 );
411 0 : SwPosSize aPosSize( SwTextPortion::GetTextSize( rInf ) );
412 0 : aPosSize.Width( aPosSize.Width() + nMyX );
413 :
414 0 : const_cast<SwTextSizeInfo&>(rInf).SetIdx( nOldIdx );
415 0 : const_cast<SwTextSizeInfo&>(rInf).SetLen( nOldLen );
416 0 : if( pCurrPart )
417 : {
418 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
419 0 : const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
420 : }
421 :
422 0 : return aPosSize;
423 : }
424 :
425 0 : sal_Int32 SwDropPortion::GetCrsrOfst( const sal_uInt16 ) const
426 : {
427 0 : return 0;
428 : }
429 :
430 12 : void SwTextFormatter::CalcDropHeight( const sal_uInt16 nLines )
431 : {
432 12 : const SwLinePortion *const pOldCurr = GetCurr();
433 12 : sal_uInt16 nDropHght = 0;
434 12 : sal_uInt16 nAscent = 0;
435 12 : sal_uInt16 nHeight = 0;
436 12 : sal_uInt16 nDropLns = 0;
437 12 : const bool bRegisterOld = IsRegisterOn();
438 12 : bRegisterOn = false;
439 :
440 12 : Top();
441 :
442 24 : while( GetCurr()->IsDummy() )
443 : {
444 6 : if ( !Next() )
445 6 : break;
446 : }
447 :
448 : // If we have only one line we return 0
449 12 : if( GetNext() || GetDropLines() == 1 )
450 : {
451 0 : for( ; nDropLns < nLines; nDropLns++ )
452 : {
453 0 : if ( GetCurr()->IsDummy() )
454 0 : break;
455 : else
456 : {
457 0 : CalcAscentAndHeight( nAscent, nHeight );
458 0 : nDropHght = nDropHght + nHeight;
459 0 : bRegisterOn = bRegisterOld;
460 : }
461 0 : if ( !Next() )
462 : {
463 0 : nDropLns++;
464 0 : break;
465 : }
466 : }
467 :
468 : // We hit the line ascent when reaching the last line!
469 0 : nDropHght = nDropHght - nHeight;
470 0 : nDropHght = nDropHght + nAscent;
471 0 : Top();
472 : }
473 12 : bRegisterOn = bRegisterOld;
474 12 : SetDropDescent( nHeight - nAscent );
475 12 : SetDropHeight( nDropHght );
476 12 : SetDropLines( nDropLns );
477 : // Alte Stelle wiederfinden!
478 12 : while( pOldCurr != GetCurr() )
479 : {
480 0 : if( !Next() )
481 : {
482 : OSL_ENSURE( false, "SwTextFormatter::_CalcDropHeight: left Toulouse" );
483 0 : break;
484 : }
485 : }
486 12 : }
487 :
488 : /**
489 : * We assume hat the font height doesn't change and that at first there
490 : * are at least as many lines, as the DropCap-setting claims
491 : */
492 6 : void SwTextFormatter::GuessDropHeight( const sal_uInt16 nLines )
493 : {
494 : OSL_ENSURE( nLines, "GuessDropHeight: Give me more Lines!" );
495 6 : sal_uInt16 nAscent = 0;
496 6 : sal_uInt16 nHeight = 0;
497 6 : SetDropLines( nLines );
498 6 : if ( GetDropLines() > 1 )
499 : {
500 6 : CalcRealHeight();
501 6 : CalcAscentAndHeight( nAscent, nHeight );
502 : }
503 6 : SetDropDescent( nHeight - nAscent );
504 6 : SetDropHeight( nHeight * nLines - GetDropDescent() );
505 6 : }
506 :
507 12 : SwDropPortion *SwTextFormatter::NewDropPortion( SwTextFormatInfo &rInf )
508 : {
509 12 : if( !pDropFormat )
510 0 : return 0;
511 :
512 12 : sal_Int32 nPorLen = pDropFormat->GetWholeWord() ? 0 : pDropFormat->GetChars();
513 12 : nPorLen = pFrm->GetTextNode()->GetDropLen( nPorLen );
514 12 : if( !nPorLen )
515 : {
516 0 : static_cast<SwTextFormatter*>(this)->ClearDropFormat();
517 0 : return 0;
518 : }
519 :
520 12 : SwDropPortion *pDropPor = 0;
521 :
522 : // first or second round?
523 12 : if ( !( GetDropHeight() || IsOnceMore() ) )
524 : {
525 6 : if ( GetNext() )
526 0 : CalcDropHeight( pDropFormat->GetLines() );
527 : else
528 6 : GuessDropHeight( pDropFormat->GetLines() );
529 : }
530 :
531 : // the DropPortion
532 12 : if( GetDropHeight() )
533 12 : pDropPor = new SwDropPortion( GetDropLines(), GetDropHeight(),
534 12 : GetDropDescent(), pDropFormat->GetDistance() );
535 : else
536 6 : pDropPor = new SwDropPortion( 0,0,0,pDropFormat->GetDistance() );
537 :
538 12 : pDropPor->SetLen( nPorLen );
539 :
540 : // If it was not possible to create a proper drop cap portion
541 : // due to avoiding endless loops. We return a drop cap portion
542 : // with an empty SwDropCapPart. For these portions the current
543 : // font is used.
544 12 : if ( GetDropLines() < 2 )
545 : {
546 6 : static_cast<SwTextFormatter*>(this)->SetPaintDrop( true );
547 6 : return pDropPor;
548 : }
549 :
550 : // build DropPortionParts:
551 : OSL_ENSURE( ! rInf.GetIdx(), "Drop Portion not at 0 position!" );
552 6 : sal_Int32 nNextChg = 0;
553 6 : const SwCharFormat* pFormat = pDropFormat->GetCharFormat();
554 6 : SwDropPortionPart* pCurrPart = 0;
555 :
556 18 : while ( nNextChg < nPorLen )
557 : {
558 : // check for attribute changes and if the portion has to split:
559 6 : Seek( nNextChg );
560 :
561 : // the font is deleted in the destructor of the drop portion part
562 6 : SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
563 6 : if ( pFormat )
564 : {
565 6 : const SwAttrSet& rSet = pFormat->GetAttrSet();
566 6 : pTmpFnt->SetDiffFnt( &rSet, pFrm->GetTextNode()->getIDocumentSettingAccess() );
567 : }
568 :
569 : // we do not allow a vertical font for the drop portion
570 6 : pTmpFnt->SetVertical( 0, rInf.GetTextFrm()->IsVertical() );
571 :
572 : // find next attribute change / script change
573 6 : const sal_Int32 nTmpIdx = nNextChg;
574 6 : sal_Int32 nNextAttr = std::min( static_cast<sal_Int32>(GetNextAttr()), rInf.GetText().getLength() );
575 6 : nNextChg = pScriptInfo->NextScriptChg( nTmpIdx );
576 6 : if( nNextChg > nNextAttr )
577 0 : nNextChg = nNextAttr;
578 6 : if ( nNextChg > nPorLen )
579 0 : nNextChg = nPorLen;
580 :
581 : SwDropPortionPart* pPart =
582 6 : new SwDropPortionPart( *pTmpFnt, nNextChg - nTmpIdx );
583 :
584 6 : if ( ! pCurrPart )
585 6 : pDropPor->SetPart( pPart );
586 : else
587 0 : pCurrPart->SetFollow( pPart );
588 :
589 6 : pCurrPart = pPart;
590 : }
591 :
592 6 : static_cast<SwTextFormatter*>(this)->SetPaintDrop( true );
593 6 : return pDropPor;
594 : }
595 :
596 0 : void SwTextPainter::PaintDropPortion()
597 : {
598 0 : const SwDropPortion *pDrop = GetInfo().GetParaPortion()->FindDropPortion();
599 : OSL_ENSURE( pDrop, "DrapCop-Portion not available." );
600 0 : if( !pDrop )
601 0 : return;
602 :
603 0 : const SwTwips nOldY = GetInfo().Y();
604 :
605 0 : Top();
606 :
607 0 : GetInfo().SetpSpaceAdd( pCurr->GetpLLSpaceAdd() );
608 0 : GetInfo().ResetSpaceIdx();
609 0 : GetInfo().SetKanaComp( pCurr->GetpKanaComp() );
610 0 : GetInfo().ResetKanaIdx();
611 :
612 : // 8047: Drops and Dummies
613 0 : while( !pCurr->GetLen() && Next() )
614 : ;
615 :
616 : // MarginPortion und Adjustment!
617 0 : const SwLinePortion *pPor = pCurr->GetFirstPortion();
618 0 : long nX = 0;
619 0 : while( pPor && !pPor->IsDropPortion() )
620 : {
621 0 : nX = nX + pPor->Width();
622 0 : pPor = pPor->GetPortion();
623 : }
624 0 : Point aLineOrigin( GetTopLeft() );
625 :
626 0 : aLineOrigin.X() += nX;
627 : sal_uInt16 nTmpAscent, nTmpHeight;
628 0 : CalcAscentAndHeight( nTmpAscent, nTmpHeight );
629 0 : aLineOrigin.Y() += nTmpAscent;
630 0 : GetInfo().SetIdx( GetStart() );
631 0 : GetInfo().SetPos( aLineOrigin );
632 0 : GetInfo().SetLen( pDrop->GetLen() );
633 :
634 0 : pDrop->PaintDrop( GetInfo() );
635 :
636 0 : GetInfo().Y( nOldY );
637 : }
638 :
639 : // Since the calculation of the font size is expensive, this is being
640 : // channeled through a DropCapCache
641 : #define DROP_CACHE_SIZE 10
642 :
643 : class SwDropCapCache
644 : {
645 : long aMagicNo[ DROP_CACHE_SIZE ];
646 : OUString aText[ DROP_CACHE_SIZE ];
647 : sal_uInt16 aFactor[ DROP_CACHE_SIZE ];
648 : sal_uInt16 aWishedHeight[ DROP_CACHE_SIZE ];
649 : short aDescent[ DROP_CACHE_SIZE ];
650 : sal_uInt16 nIndex;
651 : public:
652 : SwDropCapCache();
653 1 : ~SwDropCapCache(){}
654 : void CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf );
655 : };
656 :
657 : // SwDropCapCache Ctor / Dtor
658 1 : SwDropCapCache::SwDropCapCache() : nIndex( 0 )
659 : {
660 1 : memset( &aMagicNo, 0, sizeof(aMagicNo) );
661 1 : memset( &aWishedHeight, 0, sizeof(aWishedHeight) );
662 1 : }
663 :
664 59 : void SwDropPortion::DeleteDropCapCache()
665 : {
666 59 : delete pDropCapCache;
667 59 : }
668 :
669 6 : void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf )
670 : {
671 6 : const void* pFntNo = 0;
672 6 : sal_uInt16 nTmpIdx = 0;
673 :
674 : OSL_ENSURE( pDrop->GetPart(),"DropPortion without part during font calculation");
675 :
676 6 : SwDropPortionPart* pCurrPart = pDrop->GetPart();
677 6 : const bool bUseCache = ! pCurrPart->GetFollow() && !pCurrPart->GetFont().HasBorder();
678 6 : sal_Int32 nIdx = rInf.GetIdx();
679 6 : OUString aStr(rInf.GetText().copy(nIdx, pCurrPart->GetLen()));
680 :
681 6 : long nAscent = 0;
682 6 : long nDescent = 0;
683 6 : long nFactor = -1;
684 :
685 6 : if ( bUseCache )
686 : {
687 6 : SwFont& rFnt = pCurrPart->GetFont();
688 6 : rFnt.ChkMagic( rInf.GetVsh(), rFnt.GetActual() );
689 6 : rFnt.GetMagic( pFntNo, nTmpIdx, rFnt.GetActual() );
690 :
691 6 : nTmpIdx = 0;
692 :
693 123 : while( nTmpIdx < DROP_CACHE_SIZE &&
694 51 : ( aText[ nTmpIdx ] != aStr || aMagicNo[ nTmpIdx ] != sal_IntPtr(pFntNo) ||
695 3 : aWishedHeight[ nTmpIdx ] != pDrop->GetDropHeight() ) )
696 36 : ++nTmpIdx;
697 : }
698 :
699 : // we have to calculate a new font scaling factor if
700 : // 1. we did not find a scaling factor in the cache or
701 : // 2. we are not allowed to use the cache because the drop portion
702 : // consists of more than one part
703 6 : if( nTmpIdx >= DROP_CACHE_SIZE || ! bUseCache )
704 : {
705 3 : ++nIndex;
706 3 : nIndex %= DROP_CACHE_SIZE;
707 3 : nTmpIdx = nIndex;
708 :
709 3 : long nWishedHeight = pDrop->GetDropHeight();
710 :
711 : // find out biggest font size for initial scaling factor
712 3 : long nMaxFontHeight = 1;
713 9 : while ( pCurrPart )
714 : {
715 3 : const SwFont& rFnt = pCurrPart->GetFont();
716 3 : const long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
717 3 : if ( nCurrHeight > nMaxFontHeight )
718 3 : nMaxFontHeight = nCurrHeight;
719 :
720 3 : pCurrPart = pCurrPart->GetFollow();
721 : }
722 :
723 3 : nFactor = ( 1000 * nWishedHeight ) / nMaxFontHeight;
724 :
725 3 : if ( bUseCache )
726 : {
727 : // save keys for cache
728 3 : aMagicNo[ nTmpIdx ] = sal_IntPtr(pFntNo);
729 3 : aText[ nTmpIdx ] = aStr;
730 3 : aWishedHeight[ nTmpIdx ] = sal_uInt16(nWishedHeight);
731 : // save initial scaling factor
732 3 : aFactor[ nTmpIdx ] = (sal_uInt16)nFactor;
733 : }
734 :
735 3 : bool bGrow = ( pDrop->GetLen() != 0 );
736 :
737 : // for growing control
738 3 : long nMax = USHRT_MAX;
739 3 : long nMin = 0;
740 : #if OSL_DEBUG_LEVEL > 1
741 : long nGrow = 0;
742 : #endif
743 :
744 3 : bool bWinUsed = false;
745 3 : vcl::Font aOldFnt;
746 6 : MapMode aOldMap( MAP_TWIP );
747 3 : OutputDevice* pOut = rInf.GetOut();
748 : OutputDevice* pWin;
749 3 : if( rInf.GetVsh() && rInf.GetVsh()->GetWin() )
750 3 : pWin = rInf.GetVsh()->GetWin();
751 : else
752 0 : pWin = Application::GetDefaultDevice();
753 :
754 18 : while( bGrow )
755 : {
756 : // reset pCurrPart to first part
757 12 : pCurrPart = pDrop->GetPart();
758 12 : bool bFirstGlyphRect = true;
759 12 : Rectangle aCommonRect, aRect;
760 :
761 36 : while ( pCurrPart )
762 : {
763 : // current font
764 12 : SwFont& rFnt = pCurrPart->GetFont();
765 :
766 : // Get height including proportion
767 12 : const long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
768 :
769 : // Get without proportion
770 12 : const sal_uInt8 nOldProp = rFnt.GetPropr();
771 12 : rFnt.SetProportion( 100 );
772 12 : Size aOldSize = Size( 0, rFnt.GetHeight( rFnt.GetActual() ) );
773 :
774 12 : Size aNewSize( 0, ( nFactor * nCurrHeight ) / 1000 );
775 12 : rFnt.SetSize( aNewSize, rFnt.GetActual() );
776 12 : rFnt.ChgPhysFnt( rInf.GetVsh(), *pOut );
777 :
778 12 : nAscent = rFnt.GetAscent( rInf.GetVsh(), *pOut );
779 :
780 : // we get the rectangle that covers all chars
781 12 : bool bHaveGlyphRect = pOut->GetTextBoundRect( aRect, rInf.GetText(), 0,
782 36 : nIdx, pCurrPart->GetLen() ) &&
783 24 : ! aRect.IsEmpty();
784 :
785 12 : if ( ! bHaveGlyphRect )
786 : {
787 : // getting glyph boundaries failed for some reason,
788 : // we take the window for calculating sizes
789 0 : if ( pWin )
790 : {
791 0 : if ( ! bWinUsed )
792 : {
793 0 : bWinUsed = true;
794 0 : aOldMap = pWin->GetMapMode( );
795 0 : pWin->SetMapMode( MapMode( MAP_TWIP ) );
796 0 : aOldFnt = pWin->GetFont();
797 : }
798 0 : pWin->SetFont( rFnt.GetActualFont() );
799 :
800 0 : bHaveGlyphRect = pWin->GetTextBoundRect( aRect, rInf.GetText(), 0,
801 0 : nIdx, pCurrPart->GetLen() ) &&
802 0 : ! aRect.IsEmpty();
803 : }
804 0 : if (!bHaveGlyphRect)
805 : {
806 : // We do not have a window or our window could not
807 : // give us glyph boundaries.
808 0 : aRect = Rectangle( Point( 0, 0 ), Size( 0, nAscent ) );
809 : }
810 : }
811 :
812 : // Now we (hopefully) have a bounding rectangle for the
813 : // glyphs of the current portion and the ascent of the current
814 : // font
815 :
816 : // reset font size and proportion
817 12 : rFnt.SetSize( aOldSize, rFnt.GetActual() );
818 12 : rFnt.SetProportion( nOldProp );
819 :
820 : // Modify the bounding rectangle with the borders
821 : // Robust: If the padding is so big as drop cap letter has no enough space than
822 : // remove all padding.
823 12 : if( rFnt.GetTopBorderSpace() + rFnt.GetBottomBorderSpace() >= nWishedHeight )
824 : {
825 0 : rFnt.SetTopBorderDist(0);
826 0 : rFnt.SetBottomBorderDist(0);
827 0 : rFnt.SetRightBorderDist(0);
828 0 : rFnt.SetLeftBorderDist(0);
829 : }
830 :
831 12 : if( rFnt.GetTopBorder() )
832 : {
833 0 : aRect.setHeight(aRect.GetHeight() + rFnt.GetTopBorderSpace());
834 0 : aRect.setY(aRect.getY() - rFnt.GetTopBorderSpace());
835 : }
836 :
837 12 : if( rFnt.GetBottomBorder() )
838 : {
839 0 : aRect.setHeight(aRect.GetHeight() + rFnt.GetBottomBorderSpace());
840 : }
841 :
842 12 : if ( bFirstGlyphRect )
843 : {
844 12 : aCommonRect = aRect;
845 12 : bFirstGlyphRect = false;
846 : }
847 : else
848 0 : aCommonRect.Union( aRect );
849 :
850 12 : nIdx = nIdx + pCurrPart->GetLen();
851 12 : pCurrPart = pCurrPart->GetFollow();
852 : }
853 :
854 : // now we have a union ( aCommonRect ) of all glyphs with
855 : // respect to a common baseline : 0
856 :
857 : // get descent and ascent from union
858 12 : if ( rInf.GetTextFrm()->IsVertical() )
859 : {
860 0 : nDescent = aCommonRect.Left();
861 0 : nAscent = aCommonRect.Right();
862 :
863 0 : if ( nDescent < 0 )
864 0 : nDescent = -nDescent;
865 : }
866 : else
867 : {
868 12 : nDescent = aCommonRect.Bottom();
869 12 : nAscent = aCommonRect.Top();
870 : }
871 12 : if ( nAscent < 0 )
872 12 : nAscent = -nAscent;
873 :
874 12 : const long nHght = nAscent + nDescent;
875 12 : if ( nHght )
876 : {
877 12 : if ( nHght > nWishedHeight )
878 6 : nMax = nFactor;
879 : else
880 : {
881 6 : if ( bUseCache )
882 6 : aFactor[ nTmpIdx ] = (sal_uInt16)nFactor;
883 6 : nMin = nFactor;
884 : }
885 :
886 12 : nFactor = ( nFactor * nWishedHeight ) / nHght;
887 12 : bGrow = ( nFactor > nMin ) && ( nFactor < nMax );
888 : #if OSL_DEBUG_LEVEL > 1
889 : if ( bGrow )
890 : nGrow++;
891 : #endif
892 12 : nIdx = rInf.GetIdx();
893 : }
894 : else
895 0 : bGrow = false;
896 : }
897 :
898 3 : if ( bWinUsed )
899 : {
900 : // reset window if it has been used
901 0 : pWin->SetMapMode( aOldMap );
902 0 : pWin->SetFont( aOldFnt );
903 : }
904 :
905 3 : if ( bUseCache )
906 6 : aDescent[ nTmpIdx ] = -short( nDescent );
907 : }
908 :
909 6 : pCurrPart = pDrop->GetPart();
910 :
911 : // did made any new calculations or did we use the cache?
912 6 : if ( -1 == nFactor )
913 : {
914 3 : nFactor = aFactor[ nTmpIdx ];
915 3 : nDescent = aDescent[ nTmpIdx ];
916 : }
917 : else
918 3 : nDescent = -nDescent;
919 :
920 18 : while ( pCurrPart )
921 : {
922 : // scale current font
923 6 : SwFont& rFnt = pCurrPart->GetFont();
924 6 : Size aNewSize( 0, ( nFactor * rFnt.GetHeight( rFnt.GetActual() ) ) / 1000 );
925 :
926 6 : const sal_uInt8 nOldProp = rFnt.GetPropr();
927 6 : rFnt.SetProportion( 100 );
928 6 : rFnt.SetSize( aNewSize, rFnt.GetActual() );
929 6 : rFnt.SetProportion( nOldProp );
930 :
931 6 : pCurrPart = pCurrPart->GetFollow();
932 : }
933 6 : pDrop->SetY( (short)nDescent );
934 6 : }
935 :
936 12 : bool SwDropPortion::Format( SwTextFormatInfo &rInf )
937 : {
938 12 : bool bFull = false;
939 12 : Fix( (sal_uInt16)rInf.X() );
940 :
941 12 : SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
942 12 : aLayoutModeModifier.SetAuto();
943 :
944 12 : if( nDropHeight && pPart && nLines!=1 )
945 : {
946 6 : if( !pDropCapCache )
947 1 : pDropCapCache = new SwDropCapCache();
948 :
949 : // adjust font sizes to fit into the rectangle
950 6 : pDropCapCache->CalcFontSize( this, rInf );
951 :
952 6 : const long nOldX = rInf.X();
953 : {
954 6 : SwDropSave aSave( rInf );
955 6 : SwDropPortionPart* pCurrPart = pPart;
956 :
957 18 : while ( pCurrPart )
958 : {
959 6 : rInf.SetLen( pCurrPart->GetLen() );
960 6 : SwFont& rFnt = pCurrPart->GetFont();
961 : {
962 6 : SwFontSave aFontSave( rInf, &rFnt );
963 6 : SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
964 6 : SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
965 6 : bFull = FormatText( rInf );
966 :
967 6 : if ( bFull )
968 0 : break;
969 : }
970 :
971 : const SwTwips nTmpWidth =
972 12 : ( InSpaceGrp() && rInf.GetSpaceAdd() ) ?
973 0 : Width() + CalcSpacing( rInf.GetSpaceAdd(), rInf ) :
974 6 : Width();
975 :
976 : // set values
977 6 : pCurrPart->SetWidth( (sal_uInt16)nTmpWidth );
978 :
979 : // Move
980 6 : rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
981 6 : rInf.X( rInf.X() + nTmpWidth );
982 6 : pCurrPart = pCurrPart->GetFollow();
983 : }
984 6 : SetJoinBorderWithNext(false);
985 6 : SetJoinBorderWithPrev(false);
986 6 : Width( (sal_uInt16)(rInf.X() - nOldX) );
987 : }
988 :
989 : // reset my length
990 6 : SetLen( rInf.GetLen() );
991 :
992 : // Quit when Flys are overlapping
993 6 : if( ! bFull )
994 6 : bFull = lcl_IsDropFlyInter( rInf, Width(), nDropHeight );
995 :
996 6 : if( bFull )
997 : {
998 : // FormatText could have caused nHeight to be 0
999 0 : if ( !Height() )
1000 0 : Height( rInf.GetTextHeight() );
1001 :
1002 : // And now for another round
1003 0 : nDropHeight = nLines = 0;
1004 0 : delete pPart;
1005 0 : pPart = NULL;
1006 :
1007 : // Meanwhile use normal formatting
1008 0 : bFull = SwTextPortion::Format( rInf );
1009 : }
1010 : else
1011 6 : rInf.SetDropInit( true );
1012 :
1013 6 : Height( rInf.GetTextHeight() );
1014 6 : SetAscent( rInf.GetAscent() );
1015 : }
1016 : else
1017 6 : bFull = SwTextPortion::Format( rInf );
1018 :
1019 12 : if( bFull )
1020 0 : nDistance = 0;
1021 : else
1022 : {
1023 12 : const sal_uInt16 nWant = Width() + GetDistance();
1024 12 : const sal_uInt16 nRest = (sal_uInt16)(rInf.Width() - rInf.X());
1025 24 : if( ( nWant > nRest ) ||
1026 12 : lcl_IsDropFlyInter( rInf, Width() + GetDistance(), nDropHeight ) )
1027 0 : nDistance = 0;
1028 :
1029 12 : Width( Width() + nDistance );
1030 : }
1031 12 : return bFull;
1032 177 : }
1033 :
1034 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|