xref: /trunk/main/sw/source/core/text/txtdrop.cxx (revision 3436fcd2)
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 
lcl_IsDropFlyInter(const SwTxtFormatInfo & rInf,sal_uInt16 nWidth,sal_uInt16 nHeight)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 
SwDropSave(const SwTxtPaintInfo & rInf)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 
~SwDropSave()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 
~SwDropPortionPart()112 SwDropPortionPart::~SwDropPortionPart()
113 {
114 	if ( pFollow )
115 		delete pFollow;
116 	delete pFnt;
117 }
118 
119 /*************************************************************************
120  * SwDropPortion CTor, DTor
121  *************************************************************************/
122 
SwDropPortion(const MSHORT nLineCnt,const KSHORT nDrpHeight,const KSHORT nDrpDescent,const KSHORT nDist)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 
~SwDropPortion()138 SwDropPortion::~SwDropPortion()
139 {
140 	delete pPart;
141 	if( pBlink )
142 		pBlink->Delete( this );
143 }
144 
_HasHint(const SwTxtNode * pTxtNode,xub_StrLen nPos)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 
GetDropLen(MSHORT nWishLen) const156 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  *************************************************************************/
GetDropSize(int & rFontHeight,int & rDropHeight,int & rDropDescent) const209 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 
PaintTxt(const SwTxtPaintInfo & rInf) const283 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 
PaintDrop(const SwTxtPaintInfo & rInf) const322 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 
Paint(const SwTxtPaintInfo & rInf) const373 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 
FormatTxt(SwTxtFormatInfo & rInf)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 nicht gut 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 
GetTxtSize(const SwTxtSizeInfo & rInf) const426 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 
GetCrsrOfst(const KSHORT) const462 xub_StrLen SwDropPortion::GetCrsrOfst( const KSHORT ) const
463 {
464 	return 0;
465 }
466 
467 /*************************************************************************
468  * SwTxtFormatter::CalcDropHeight()
469  *************************************************************************/
470 
CalcDropHeight(const MSHORT nLines)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( sal_False, "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 
GuessDropHeight(const MSHORT nLines)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 
NewDropPortion(SwTxtFormatInfo & rInf)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 
PaintDropPortion()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  * class 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();
~SwDropCapCache()726 	~SwDropCapCache(){}
727 	void CalcFontSize( SwDropPortion* pDrop, SwTxtFormatInfo &rInf );
728 };
729 
730 /*************************************************************************
731  * SwDropCapCache Ctor / Dtor
732  *************************************************************************/
733 
SwDropCapCache()734 SwDropCapCache::SwDropCapCache() : nIndex( 0 )
735 {
736 	memset( &aMagicNo, 0, sizeof(aMagicNo) );
737 	memset( &aWishedHeight, 0, sizeof(aWishedHeight) );
738 }
739 
DeleteDropCapCache()740 void SwDropPortion::DeleteDropCapCache()
741 {
742 	delete pDropCapCache;
743 }
744 
745 /*************************************************************************
746  * SwDropCapCache::CalcFontSize
747  *************************************************************************/
748 
CalcFontSize(SwDropPortion * pDrop,SwTxtFormatInfo & rInf)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 we 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 
Format(SwTxtFormatInfo & rInf)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 /* vim: set noet sw=4 ts=4: */
1099