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