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