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