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 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
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( !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
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 * 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();
~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 controll
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
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