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