android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / txtdrop.cxx
blobafbe3b1470eb6891eb699363aaf483d7c47482ad
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <hintids.hxx>
21 #include <vcl/svapp.hxx>
22 #include <paratr.hxx>
23 #include <txtfrm.hxx>
24 #include <charfmt.hxx>
25 #include <viewopt.hxx>
26 #include <viewsh.hxx>
27 #include "pordrop.hxx"
28 #include "itrform2.hxx"
29 #include "txtpaint.hxx"
30 #include <breakit.hxx>
31 #include <com/sun/star/i18n/ScriptType.hpp>
32 #include <com/sun/star/i18n/WordType.hpp>
33 #include <com/sun/star/i18n/XBreakIterator.hpp>
34 #include <editeng/langitem.hxx>
35 #include <charatr.hxx>
36 #include <editeng/fhgtitem.hxx>
37 #include <calbck.hxx>
38 #include <doc.hxx>
39 #include <IDocumentSettingAccess.hxx>
41 using namespace ::com::sun::star::i18n;
42 using namespace ::com::sun::star;
44 /**
45 * Calculates if a drop caps portion intersects with a fly
46 * The width and height of the drop caps portion are passed as arguments,
47 * the position is calculated from the values in rInf
49 static bool lcl_IsDropFlyInter( const SwTextFormatInfo &rInf,
50 sal_uInt16 nWidth, sal_uInt16 nHeight )
52 const SwTextFly& rTextFly = rInf.GetTextFly();
53 if( rTextFly.IsOn() )
55 SwRect aRect( rInf.GetTextFrame()->getFrameArea().Pos(), Size( nWidth, nHeight) );
56 aRect.Pos() += rInf.GetTextFrame()->getFramePrintArea().Pos();
57 aRect.Pos().AdjustX(rInf.X() );
58 aRect.Pos().setY( rInf.Y() );
59 aRect = rTextFly.GetFrame( aRect );
60 return aRect.HasArea();
63 return false;
66 namespace {
68 class SwDropSave
70 SwTextPaintInfo* pInf;
71 sal_Int32 nIdx;
72 sal_Int32 nLen;
73 tools::Long nX;
74 tools::Long nY;
76 public:
77 explicit SwDropSave( const SwTextPaintInfo &rInf );
78 ~SwDropSave();
83 SwDropSave::SwDropSave( const SwTextPaintInfo &rInf ) :
84 pInf( const_cast<SwTextPaintInfo*>(&rInf) ), nIdx( rInf.GetIdx() ),
85 nLen( rInf.GetLen() ), nX( rInf.X() ), nY( rInf.Y() )
89 SwDropSave::~SwDropSave()
91 pInf->SetIdx(TextFrameIndex(nIdx));
92 pInf->SetLen(TextFrameIndex(nLen));
93 pInf->X( nX );
94 pInf->Y( nY );
97 /// SwDropPortionPart DTor
98 SwDropPortionPart::~SwDropPortionPart()
100 m_pFollow.reset();
101 m_pFnt.reset();
104 /// SwDropPortion CTor, DTor
105 SwDropPortion::SwDropPortion( const sal_uInt16 nLineCnt,
106 const sal_uInt16 nDrpHeight,
107 const sal_uInt16 nDrpDescent,
108 const sal_uInt16 nDist )
109 : m_nLines( nLineCnt ),
110 m_nDropHeight(nDrpHeight),
111 m_nDropDescent(nDrpDescent),
112 m_nDistance(nDist),
113 m_nFix(0),
114 m_nY(0)
116 SetWhichPor( PortionType::Drop );
119 SwDropPortion::~SwDropPortion()
121 m_pPart.reset();
124 /// nWishLen = 0 indicates that we want a whole word
125 sal_Int32 SwTextNode::GetDropLen( sal_Int32 nWishLen ) const
127 sal_Int32 nEnd = GetText().getLength();
128 if( nWishLen && nWishLen < nEnd )
129 nEnd = nWishLen;
131 if (! nWishLen)
133 // find first word
134 const SwAttrSet& rAttrSet = GetSwAttrSet();
135 const sal_uInt16 nTextScript = g_pBreakIt->GetRealScriptOfText( GetText(), 0 );
137 LanguageType eLanguage;
139 switch ( nTextScript )
141 case i18n::ScriptType::ASIAN :
142 eLanguage = rAttrSet.GetCJKLanguage().GetLanguage();
143 break;
144 case i18n::ScriptType::COMPLEX :
145 eLanguage = rAttrSet.GetCTLLanguage().GetLanguage();
146 break;
147 default :
148 eLanguage = rAttrSet.GetLanguage().GetLanguage();
149 break;
152 Boundary aBound =
153 g_pBreakIt->GetBreakIter()->getWordBoundary( GetText(), 0,
154 g_pBreakIt->GetLocale( eLanguage ), WordType::DICTIONARY_WORD, true );
156 nEnd = aBound.endPos;
159 sal_Int32 i = 0;
160 for( ; i < nEnd; ++i )
162 sal_Unicode const cChar = GetText()[i];
163 if( CH_TAB == cChar || CH_BREAK == cChar ||
164 (( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar )
165 && GetTextAttrForCharAt(i)) )
166 break;
168 return i;
171 /// nWishLen = 0 indicates that we want a whole word
172 TextFrameIndex SwTextFrame::GetDropLen(TextFrameIndex const nWishLen) const
174 TextFrameIndex nEnd(GetText().getLength());
175 if (nWishLen && nWishLen < nEnd)
176 nEnd = nWishLen;
178 if (! nWishLen)
180 // find first word
181 const SwAttrSet& rAttrSet = GetTextNodeForParaProps()->GetSwAttrSet();
182 const sal_uInt16 nTextScript = g_pBreakIt->GetRealScriptOfText(GetText(), 0);
184 LanguageType eLanguage;
186 switch ( nTextScript )
188 case i18n::ScriptType::ASIAN :
189 eLanguage = rAttrSet.GetCJKLanguage().GetLanguage();
190 break;
191 case i18n::ScriptType::COMPLEX :
192 eLanguage = rAttrSet.GetCTLLanguage().GetLanguage();
193 break;
194 default :
195 eLanguage = rAttrSet.GetLanguage().GetLanguage();
196 break;
199 Boundary aBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
200 GetText(), 0, g_pBreakIt->GetLocale(eLanguage),
201 WordType::DICTIONARY_WORD, true );
203 nEnd = TextFrameIndex(aBound.endPos);
206 TextFrameIndex i(0);
207 for ( ; i < nEnd; ++i)
209 sal_Unicode const cChar = GetText()[sal_Int32(i)];
210 if (CH_TAB == cChar || CH_BREAK == cChar ||
211 CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
213 #ifndef NDEBUG
214 if (CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
216 std::pair<SwTextNode const*, sal_Int32> const pos(MapViewToModel(i));
217 assert(pos.first->GetTextAttrForCharAt(pos.second) != nullptr);
219 #endif
220 break;
223 return i;
227 * If a dropcap is found the return value is true otherwise false. The
228 * drop cap sizes passed back by reference are font height, drop height
229 * and drop descent.
231 bool SwTextNode::GetDropSize(int& rFontHeight, int& rDropHeight, int& rDropDescent) const
233 rFontHeight = 0;
234 rDropHeight = 0;
235 rDropDescent =0;
237 const SwAttrSet& rSet = GetSwAttrSet();
238 const SwFormatDrop& rDrop = rSet.GetDrop();
240 // Return (0,0) if there is no drop cap at this paragraph
241 if( 1 >= rDrop.GetLines() ||
242 ( !rDrop.GetChars() && !rDrop.GetWholeWord() ) )
244 return false;
247 // get text frame
248 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
249 for( SwTextFrame* pLastFrame = aIter.First(); pLastFrame; pLastFrame = aIter.Next() )
251 // Only (master-) text frames can have a drop cap.
252 if (!pLastFrame->IsFollow() &&
253 pLastFrame->GetTextNodeForFirstText() == this)
256 if( !pLastFrame->HasPara() )
257 pLastFrame->GetFormatted();
259 if ( !pLastFrame->IsEmpty() )
261 const SwParaPortion* pPara = pLastFrame->GetPara();
262 OSL_ENSURE( pPara, "GetDropSize could not find the ParaPortion, I'll guess the drop cap size" );
264 if ( pPara )
266 const SwLinePortion* pFirstPor = pPara->GetFirstPortion();
267 if (pFirstPor && pFirstPor->IsDropPortion())
269 const SwDropPortion* pDrop = static_cast<const SwDropPortion*>(pFirstPor);
270 rDropHeight = pDrop->GetDropHeight();
271 rDropDescent = pDrop->GetDropDescent();
272 if (const SwFont *pFont = pDrop->GetFnt())
273 rFontHeight = pFont->GetSize(pFont->GetActual()).Height();
274 else
276 const SvxFontHeightItem& rItem = rSet.Get(RES_CHRATR_FONTSIZE);
277 rFontHeight = rItem.GetHeight();
282 break;
286 if (rFontHeight==0 && rDropHeight==0 && rDropDescent==0)
288 const sal_uInt16 nLines = rDrop.GetLines();
290 const SvxFontHeightItem& rItem = rSet.Get( RES_CHRATR_FONTSIZE );
291 rFontHeight = rItem.GetHeight();
292 rDropHeight = nLines * rFontHeight;
293 rDropDescent = rFontHeight / 5;
294 return false;
297 return true;
300 /// Manipulate the width, otherwise the chars are being stretched
301 void SwDropPortion::PaintText( const SwTextPaintInfo &rInf ) const
303 OSL_ENSURE( m_nDropHeight && m_pPart && m_nLines != 1, "Drop Portion painted twice" );
305 const SwDropPortionPart* pCurrPart = GetPart();
306 const TextFrameIndex nOldLen = GetLen();
307 const sal_uInt16 nOldWidth = Width();
308 const sal_uInt16 nOldAscent = GetAscent();
310 const SwTwips nBasePosY = rInf.Y();
311 const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY + m_nY );
312 const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent + m_nY );
313 SwDropSave aSave( rInf );
314 // for text inside drop portions we let vcl handle the text directions
315 SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
316 aLayoutModeModifier.SetAuto();
318 while ( pCurrPart )
320 const_cast<SwDropPortion*>(this)->SetLen( pCurrPart->GetLen() );
321 const_cast<SwDropPortion*>(this)->Width( pCurrPart->GetWidth() );
322 const_cast<SwTextPaintInfo&>(rInf).SetLen( pCurrPart->GetLen() );
323 SwFontSave aFontSave( rInf, &pCurrPart->GetFont() );
324 const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
325 const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
327 if ( rInf.OnWin() &&
328 !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() &&
329 (!pCurrPart->GetFont().GetBackColor() || *pCurrPart->GetFont().GetBackColor() == COL_TRANSPARENT) )
331 rInf.DrawBackground( *this );
334 SwTextPortion::Paint( rInf );
336 const_cast<SwTextPaintInfo&>(rInf).SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
337 const_cast<SwTextPaintInfo&>(rInf).X( rInf.X() + pCurrPart->GetWidth() );
338 pCurrPart = pCurrPart->GetFollow();
341 const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY );
342 const_cast<SwDropPortion*>(this)->Width( nOldWidth );
343 const_cast<SwDropPortion*>(this)->SetLen( nOldLen );
344 const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
345 const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
346 const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
349 void SwDropPortion::PaintDrop( const SwTextPaintInfo &rInf ) const
351 // normal output is being done during the normal painting
352 if( ! m_nDropHeight || ! m_pPart || m_nLines == 1 )
353 return;
355 // set the lying values
356 const sal_uInt16 nOldHeight = Height();
357 const sal_uInt16 nOldWidth = Width();
358 const sal_uInt16 nOldAscent = GetAscent();
359 const SwTwips nOldPosY = rInf.Y();
360 const SwTwips nOldPosX = rInf.X();
361 const SwParaPortion *pPara = rInf.GetParaPortion();
362 const Point aOutPos( nOldPosX, nOldPosY - pPara->GetAscent()
363 - pPara->GetRealHeight() + pPara->Height() );
364 // make good for retouching
366 // Set baseline
367 const_cast<SwTextPaintInfo&>(rInf).Y( aOutPos.Y() + m_nDropHeight );
369 // for background
370 const_cast<SwDropPortion*>(this)->Height( m_nDropHeight + m_nDropDescent );
371 const_cast<SwDropPortion*>(this)->SetAscent( m_nDropHeight );
373 // Always adapt Clipregion to us, never set it off using the existing ClipRect
374 // as that could be set for the line
375 SwRect aClipRect;
376 if ( rInf.OnWin() )
378 aClipRect = SwRect( aOutPos, SvLSize() );
379 aClipRect.Intersection( rInf.GetPaintRect() );
381 SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
382 aClip.ChgClip( aClipRect, rInf.GetTextFrame() );
384 // Just do, what we always do ...
385 PaintText( rInf );
387 // save old values
388 const_cast<SwDropPortion*>(this)->Height( nOldHeight );
389 const_cast<SwDropPortion*>(this)->Width( nOldWidth );
390 const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
391 const_cast<SwTextPaintInfo&>(rInf).Y( nOldPosY );
394 void SwDropPortion::Paint( const SwTextPaintInfo &rInf ) const
396 // normal output is being done here
397 if( !(! m_nDropHeight || ! m_pPart || 1 == m_nLines) )
398 return;
400 if ( rInf.OnWin() &&
401 !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && rInf.GetOpt().IsFieldShadings() )
402 rInf.DrawBackground( *this );
404 // make sure that font is not rotated
405 std::unique_ptr<SwFont> pTmpFont;
406 if ( rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() ) )
408 pTmpFont.reset(new SwFont( *rInf.GetFont() ));
409 pTmpFont->SetVertical( 0_deg10, rInf.GetTextFrame()->IsVertical() );
412 SwFontSave aFontSave( rInf, pTmpFont.get() );
413 // for text inside drop portions we let vcl handle the text directions
414 SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
415 aLayoutModeModifier.SetAuto();
417 SwTextPortion::Paint( rInf );
420 bool SwDropPortion::FormatText( SwTextFormatInfo &rInf )
422 const TextFrameIndex nOldLen = GetLen();
423 const TextFrameIndex nOldInfLen = rInf.GetLen();
424 if (!SwTextPortion::Format( rInf ))
425 return false;
427 // looks like shit, but what can we do?
428 rInf.SetUnderflow( nullptr );
429 Truncate();
430 SetLen( nOldLen );
431 rInf.SetLen( nOldInfLen );
433 return true;
436 SwPosSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
438 sal_uInt16 nMyX = 0;
439 TextFrameIndex nIdx(0);
441 const SwDropPortionPart* pCurrPart = GetPart();
443 // skip parts
444 while ( pCurrPart && nIdx + pCurrPart->GetLen() < rInf.GetLen() )
446 nMyX = nMyX + pCurrPart->GetWidth();
447 nIdx = nIdx + pCurrPart->GetLen();
448 pCurrPart = pCurrPart->GetFollow();
451 TextFrameIndex const nOldIdx = rInf.GetIdx();
452 TextFrameIndex const nOldLen = rInf.GetLen();
454 const_cast<SwTextSizeInfo&>(rInf).SetIdx( nIdx );
455 const_cast<SwTextSizeInfo&>(rInf).SetLen( rInf.GetLen() - nIdx );
457 if( pCurrPart )
459 const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
460 const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
463 // robust
464 SwFontSave aFontSave( rInf, pCurrPart ? &pCurrPart->GetFont() : nullptr );
465 SwPosSize aPosSize( SwTextPortion::GetTextSize( rInf ) );
466 aPosSize.Width( aPosSize.Width() + nMyX );
468 const_cast<SwTextSizeInfo&>(rInf).SetIdx( nOldIdx );
469 const_cast<SwTextSizeInfo&>(rInf).SetLen( nOldLen );
470 if( pCurrPart )
472 const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
473 const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
476 return aPosSize;
479 TextFrameIndex SwDropPortion::GetModelPositionForViewPoint(const sal_uInt16) const
481 return TextFrameIndex(0);
484 void SwTextFormatter::CalcDropHeight( const sal_uInt16 nLines )
486 const SwLinePortion *const pOldCurr = GetCurr();
487 sal_uInt16 nDropHght = 0;
488 SwTwips nAscent = 0;
489 SwTwips nHeight = 0;
490 sal_uInt16 nDropLns = 0;
491 const bool bRegisterOld = IsRegisterOn();
492 m_bRegisterOn = false;
494 Top();
496 while( GetCurr()->IsDummy() )
498 if ( !Next() )
499 break;
502 // If we have only one line we return 0
503 if( GetNext() || GetDropLines() == 1 )
505 for( ; nDropLns < nLines; nDropLns++ )
507 if ( GetCurr()->IsDummy() )
508 break;
509 else
511 CalcAscentAndHeight( nAscent, nHeight );
512 nDropHght = nDropHght + nHeight;
513 m_bRegisterOn = bRegisterOld;
515 if ( !Next() )
517 nDropLns++;
518 break;
522 // We hit the line ascent when reaching the last line!
523 nDropHght = nDropHght - nHeight;
524 nDropHght = nDropHght + nAscent;
525 Top();
527 m_bRegisterOn = bRegisterOld;
528 SetDropDescent( nHeight - nAscent );
529 SetDropHeight( nDropHght );
530 SetDropLines( nDropLns );
531 // Find old position!
532 while( pOldCurr != GetCurr() )
534 if( !Next() )
536 OSL_ENSURE( false, "SwTextFormatter::_CalcDropHeight: left Toulouse" );
537 break;
543 * We assume that the font height doesn't change and that at first there
544 * are at least as many lines, as the DropCap-setting claims
546 void SwTextFormatter::GuessDropHeight( const sal_uInt16 nLines )
548 OSL_ENSURE( nLines, "GuessDropHeight: Give me more Lines!" );
549 SwTwips nAscent = 0;
550 SwTwips nHeight = 0;
551 SetDropLines( nLines );
552 if ( GetDropLines() > 1 )
554 CalcRealHeight();
555 CalcAscentAndHeight( nAscent, nHeight );
557 SetDropDescent( nHeight - nAscent );
558 SetDropHeight( nHeight * nLines - GetDropDescent() );
561 SwDropPortion *SwTextFormatter::NewDropPortion( SwTextFormatInfo &rInf )
563 if( !m_pDropFormat )
564 return nullptr;
566 TextFrameIndex nPorLen(m_pDropFormat->GetWholeWord() ? 0 : m_pDropFormat->GetChars());
567 nPorLen = m_pFrame->GetDropLen( nPorLen );
568 if( !nPorLen )
570 ClearDropFormat();
571 return nullptr;
574 SwDropPortion *pDropPor = nullptr;
576 // first or second round?
577 if ( !( GetDropHeight() || IsOnceMore() ) )
579 if ( GetNext() )
580 CalcDropHeight( m_pDropFormat->GetLines() );
581 else
582 GuessDropHeight( m_pDropFormat->GetLines() );
585 // the DropPortion
586 if( GetDropHeight() )
587 pDropPor = new SwDropPortion( GetDropLines(), GetDropHeight(),
588 GetDropDescent(), m_pDropFormat->GetDistance() );
589 else
590 pDropPor = new SwDropPortion( 0,0,0,m_pDropFormat->GetDistance() );
592 pDropPor->SetLen( nPorLen );
594 // If it was not possible to create a proper drop cap portion
595 // due to avoiding endless loops. We return a drop cap portion
596 // with an empty SwDropCapPart. For these portions the current
597 // font is used.
598 if ( GetDropLines() < 2 )
600 SetPaintDrop( true );
601 return pDropPor;
604 // build DropPortionParts:
605 OSL_ENSURE( ! rInf.GetIdx(), "Drop Portion not at 0 position!" );
606 TextFrameIndex nNextChg(0);
607 const SwCharFormat* pFormat = m_pDropFormat->GetCharFormat();
608 SwDropPortionPart* pCurrPart = nullptr;
610 while ( nNextChg < nPorLen )
612 // check for attribute changes and if the portion has to split:
613 Seek( nNextChg );
615 // the font is deleted in the destructor of the drop portion part
616 SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
617 if ( pFormat )
619 const SwAttrSet& rSet = pFormat->GetAttrSet();
620 pTmpFnt->SetDiffFnt(&rSet, &m_pFrame->GetDoc().getIDocumentSettingAccess());
623 // we do not allow a vertical font for the drop portion
624 pTmpFnt->SetVertical( 0_deg10, rInf.GetTextFrame()->IsVertical() );
626 // find next attribute change / script change
627 const TextFrameIndex nTmpIdx = nNextChg;
628 TextFrameIndex nNextAttr = GetNextAttr();
629 nNextChg = m_pScriptInfo->NextScriptChg( nTmpIdx );
630 if( nNextChg > nNextAttr )
631 nNextChg = nNextAttr;
632 if ( nNextChg > nPorLen )
633 nNextChg = nPorLen;
635 std::unique_ptr<SwDropPortionPart> pPart(
636 new SwDropPortionPart( *pTmpFnt, nNextChg - nTmpIdx ) );
637 auto pPartTemp = pPart.get();
639 if ( ! pCurrPart )
640 pDropPor->SetPart( std::move(pPart) );
641 else
642 pCurrPart->SetFollow( std::move(pPart) );
644 pCurrPart = pPartTemp;
647 SetPaintDrop( true );
648 return pDropPor;
651 void SwTextPainter::PaintDropPortion()
653 const SwDropPortion *pDrop = GetInfo().GetParaPortion()->FindDropPortion();
654 OSL_ENSURE( pDrop, "DrapCop-Portion not available." );
655 if( !pDrop )
656 return;
658 const SwTwips nOldY = GetInfo().Y();
660 Top();
662 GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
663 GetInfo().ResetSpaceIdx();
664 GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
665 GetInfo().ResetKanaIdx();
667 // 8047: Drops and Dummies
668 while( !m_pCurr->GetLen() && Next() )
671 // MarginPortion and Adjustment!
672 const SwLinePortion *pPor = m_pCurr->GetFirstPortion();
673 tools::Long nX = 0;
674 while( pPor && !pPor->IsDropPortion() )
676 nX = nX + pPor->Width();
677 pPor = pPor->GetNextPortion();
679 Point aLineOrigin( GetTopLeft() );
681 aLineOrigin.AdjustX(nX );
682 SwTwips nTmpAscent, nTmpHeight;
683 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
684 aLineOrigin.AdjustY(nTmpAscent );
685 GetInfo().SetIdx( GetStart() );
686 GetInfo().SetPos( aLineOrigin );
687 GetInfo().SetLen( pDrop->GetLen() );
689 pDrop->PaintDrop( GetInfo() );
691 GetInfo().Y( nOldY );
694 // Since the calculation of the font size is expensive, this is being
695 // channeled through a DropCapCache
696 #define DROP_CACHE_SIZE 10
698 class SwDropCapCache
700 const void* m_aFontCacheId[ DROP_CACHE_SIZE ] = {};
701 OUString m_aText[ DROP_CACHE_SIZE ];
702 sal_uInt16 m_aFactor[ DROP_CACHE_SIZE ];
703 sal_uInt16 m_aWishedHeight[ DROP_CACHE_SIZE ] = {};
704 short m_aDescent[ DROP_CACHE_SIZE ];
705 sal_uInt16 m_nIndex = 0;
706 public:
707 SwDropCapCache() = default;
708 void CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf );
711 void SwDropPortion::DeleteDropCapCache()
713 delete pDropCapCache;
716 void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf )
718 const void* nFntCacheId = nullptr;
719 sal_uInt16 nTmpIdx = 0;
721 OSL_ENSURE( pDrop->GetPart(),"DropPortion without part during font calculation");
723 SwDropPortionPart* pCurrPart = pDrop->GetPart();
724 const bool bUseCache = ! pCurrPart->GetFollow() && !pCurrPart->GetFont().HasBorder();
725 TextFrameIndex nIdx = rInf.GetIdx();
726 OUString aStr(rInf.GetText().copy(sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen())));
728 tools::Long nDescent = 0;
729 tools::Long nFactor = -1;
731 if ( bUseCache )
733 SwFont& rFnt = pCurrPart->GetFont();
734 rFnt.CheckFontCacheId( rInf.GetVsh(), rFnt.GetActual() );
735 rFnt.GetFontCacheId( nFntCacheId, nTmpIdx, rFnt.GetActual() );
737 nTmpIdx = 0;
739 while( nTmpIdx < DROP_CACHE_SIZE &&
740 ( m_aText[ nTmpIdx ] != aStr || m_aFontCacheId[ nTmpIdx ] != nFntCacheId ||
741 m_aWishedHeight[ nTmpIdx ] != pDrop->GetDropHeight() ) )
742 ++nTmpIdx;
745 // we have to calculate a new font scaling factor if
746 // 1. we did not find a scaling factor in the cache or
747 // 2. we are not allowed to use the cache because the drop portion
748 // consists of more than one part
749 if( nTmpIdx >= DROP_CACHE_SIZE || ! bUseCache )
751 ++m_nIndex;
752 m_nIndex %= DROP_CACHE_SIZE;
753 nTmpIdx = m_nIndex;
755 tools::Long nWishedHeight = pDrop->GetDropHeight();
756 tools::Long nAscent = 0;
758 // find out biggest font size for initial scaling factor
759 tools::Long nMaxFontHeight = 1;
760 while ( pCurrPart )
762 const SwFont& rFnt = pCurrPart->GetFont();
763 const tools::Long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
764 if ( nCurrHeight > nMaxFontHeight )
765 nMaxFontHeight = nCurrHeight;
767 pCurrPart = pCurrPart->GetFollow();
770 nFactor = ( 1000 * nWishedHeight ) / nMaxFontHeight;
772 if ( bUseCache )
774 // save keys for cache
775 m_aFontCacheId[ nTmpIdx ] = nFntCacheId;
776 m_aText[ nTmpIdx ] = aStr;
777 m_aWishedHeight[ nTmpIdx ] = sal_uInt16(nWishedHeight);
778 // save initial scaling factor
779 m_aFactor[ nTmpIdx ] = o3tl::narrowing<sal_uInt16>(nFactor);
782 bool bGrow = (pDrop->GetLen() != TextFrameIndex(0));
784 // for growing control
785 tools::Long nMax = USHRT_MAX;
786 tools::Long nMin = 0;
787 #if OSL_DEBUG_LEVEL > 1
788 tools::Long nGrow = 0;
789 #endif
791 bool bWinUsed = false;
792 vcl::Font aOldFnt;
793 MapMode aOldMap( MapUnit::MapTwip );
794 OutputDevice* pOut = rInf.GetOut();
795 OutputDevice* pWin;
796 if( rInf.GetVsh() && rInf.GetVsh()->GetWin() )
797 pWin = rInf.GetVsh()->GetWin()->GetOutDev();
798 else
799 pWin = Application::GetDefaultDevice();
801 // adjust punctuation?
802 bool bKeepBaseline = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess()
803 .get(DocumentSettingId::DROP_CAP_PUNCTUATION) &&
804 !rInf.GetDropFormat()->GetWholeWord(); // && rInf.GetDropFormat()->GetChars() == 1;
806 while( bGrow )
808 // reset pCurrPart to first part
809 pCurrPart = pDrop->GetPart();
810 bool bFirstGlyphRect = true;
811 tools::Rectangle aCommonRect, aRect;
813 while ( pCurrPart )
815 // current font
816 SwFont& rFnt = pCurrPart->GetFont();
818 // Get height including proportion
819 const tools::Long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
821 // Get without proportion
822 const sal_uInt8 nOldProp = rFnt.GetPropr();
823 rFnt.SetProportion( 100 );
824 Size aOldSize( 0, rFnt.GetHeight( rFnt.GetActual() ) );
826 Size aNewSize( 0, ( nFactor * nCurrHeight ) / 1000 );
827 rFnt.SetSize( aNewSize, rFnt.GetActual() );
828 rFnt.ChgPhysFnt( rInf.GetVsh(), *pOut );
830 nAscent = rFnt.GetAscent( rInf.GetVsh(), *pOut );
832 // we get the rectangle that covers all chars
833 bool bHaveGlyphRect = pOut->GetTextBoundRect( aRect, rInf.GetText(), 0,
834 sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen()))
835 && ! aRect.IsEmpty();
837 if ( ! bHaveGlyphRect )
839 // getting glyph boundaries failed for some reason,
840 // we take the window for calculating sizes
841 if ( pWin )
843 if ( ! bWinUsed )
845 bWinUsed = true;
846 aOldMap = pWin->GetMapMode( );
847 pWin->SetMapMode( MapMode( MapUnit::MapTwip ) );
848 aOldFnt = pWin->GetFont();
850 pWin->SetFont( rFnt.GetActualFont() );
852 bHaveGlyphRect = pWin->GetTextBoundRect( aRect, rInf.GetText(), 0,
853 sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen()))
854 && ! aRect.IsEmpty();
856 if (!bHaveGlyphRect)
858 // We do not have a window or our window could not
859 // give us glyph boundaries.
860 aRect = tools::Rectangle( Point( 0, 0 ), Size( 0, nAscent ) );
864 // extend rectangle to the baseline to avoid of giant dashes,
865 // quotation marks, bullet, asterisks etc.
866 if ( bKeepBaseline && aRect.Top() < 0 )
868 aRect.SetBottom(0);
869 aRect.SetTop(aRect.Top() - nAscent/60);
872 // Now we (hopefully) have a bounding rectangle for the
873 // glyphs of the current portion and the ascent of the current
874 // font
876 // reset font size and proportion
877 rFnt.SetSize( aOldSize, rFnt.GetActual() );
878 rFnt.SetProportion( nOldProp );
880 // Modify the bounding rectangle with the borders
881 // Robust: If the padding is so big as drop cap letter has no enough space than
882 // remove all padding.
883 if( rFnt.GetTopBorderSpace() + rFnt.GetBottomBorderSpace() >= nWishedHeight )
885 rFnt.SetTopBorderDist(0);
886 rFnt.SetBottomBorderDist(0);
887 rFnt.SetRightBorderDist(0);
888 rFnt.SetLeftBorderDist(0);
891 if( rFnt.GetTopBorder() )
893 aRect.setHeight(aRect.GetHeight() + rFnt.GetTopBorderSpace());
894 aRect.SetPosY(aRect.Top() - rFnt.GetTopBorderSpace());
897 if( rFnt.GetBottomBorder() )
899 aRect.setHeight(aRect.GetHeight() + rFnt.GetBottomBorderSpace());
902 if ( bFirstGlyphRect )
904 aCommonRect = aRect;
905 bFirstGlyphRect = false;
907 else
908 aCommonRect.Union( aRect );
910 nIdx = nIdx + pCurrPart->GetLen();
911 pCurrPart = pCurrPart->GetFollow();
914 // now we have a union ( aCommonRect ) of all glyphs with
915 // respect to a common baseline : 0
917 // get descent and ascent from union
918 if ( rInf.GetTextFrame()->IsVertical() )
920 nDescent = aCommonRect.Left();
921 nAscent = aCommonRect.Right();
923 if ( nDescent < 0 )
924 nDescent = -nDescent;
926 else
928 nDescent = aCommonRect.Bottom();
929 nAscent = aCommonRect.Top();
931 if ( nAscent < 0 )
932 nAscent = -nAscent;
934 const tools::Long nHght = nAscent + nDescent;
935 if ( nHght )
937 if ( nHght > nWishedHeight )
938 nMax = nFactor;
939 else
941 if ( bUseCache )
942 m_aFactor[ nTmpIdx ] = o3tl::narrowing<sal_uInt16>(nFactor);
943 nMin = nFactor;
946 nFactor = ( nFactor * nWishedHeight ) / nHght;
947 bGrow = ( nFactor > nMin ) && ( nFactor < nMax );
948 #if OSL_DEBUG_LEVEL > 1
949 if ( bGrow )
950 nGrow++;
951 #endif
952 nIdx = rInf.GetIdx();
954 else
955 bGrow = false;
958 if ( bWinUsed )
960 // reset window if it has been used
961 pWin->SetMapMode( aOldMap );
962 pWin->SetFont( aOldFnt );
965 if ( bUseCache )
966 m_aDescent[ nTmpIdx ] = -short( nDescent );
969 pCurrPart = pDrop->GetPart();
971 // did made any new calculations or did we use the cache?
972 if ( -1 == nFactor )
974 nFactor = m_aFactor[ nTmpIdx ];
975 nDescent = m_aDescent[ nTmpIdx ];
977 else
978 nDescent = -nDescent;
980 while ( pCurrPart )
982 // scale current font
983 SwFont& rFnt = pCurrPart->GetFont();
984 Size aNewSize( 0, ( nFactor * rFnt.GetHeight( rFnt.GetActual() ) ) / 1000 );
986 const sal_uInt8 nOldProp = rFnt.GetPropr();
987 rFnt.SetProportion( 100 );
988 rFnt.SetSize( aNewSize, rFnt.GetActual() );
989 rFnt.SetProportion( nOldProp );
991 pCurrPart = pCurrPart->GetFollow();
993 pDrop->SetY( static_cast<short>(nDescent) );
996 bool SwDropPortion::Format( SwTextFormatInfo &rInf )
998 bool bFull = false;
999 m_nFix = o3tl::narrowing<sal_uInt16>(rInf.X());
1001 SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
1002 aLayoutModeModifier.SetAuto();
1004 if( m_nDropHeight && m_pPart && m_nLines!=1 )
1006 if( !pDropCapCache )
1007 pDropCapCache = new SwDropCapCache;
1009 // adjust font sizes to fit into the rectangle
1010 pDropCapCache->CalcFontSize( this, rInf );
1012 const tools::Long nOldX = rInf.X();
1014 SwDropSave aSave( rInf );
1015 SwDropPortionPart* pCurrPart = m_pPart.get();
1017 while ( pCurrPart )
1019 rInf.SetLen( pCurrPart->GetLen() );
1020 SwFont& rFnt = pCurrPart->GetFont();
1022 SwFontSave aFontSave( rInf, &rFnt );
1023 SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
1024 SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
1025 bFull = FormatText( rInf );
1027 if ( bFull )
1028 break;
1031 const SwTwips nTmpWidth =
1032 ( InSpaceGrp() && rInf.GetSpaceAdd() ) ?
1033 Width() + CalcSpacing( rInf.GetSpaceAdd(), rInf ) :
1034 Width();
1036 // set values
1037 pCurrPart->SetWidth( o3tl::narrowing<sal_uInt16>(nTmpWidth) );
1039 // Move
1040 rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
1041 rInf.X( rInf.X() + nTmpWidth );
1042 pCurrPart = pCurrPart->GetFollow();
1044 SetJoinBorderWithNext(false);
1045 SetJoinBorderWithPrev(false);
1046 Width( o3tl::narrowing<sal_uInt16>(rInf.X() - nOldX) );
1049 // reset my length
1050 SetLen( rInf.GetLen() );
1052 // Quit when Flys are overlapping
1053 if( ! bFull )
1054 bFull = lcl_IsDropFlyInter( rInf, Width(), m_nDropHeight );
1056 if( bFull )
1058 // FormatText could have caused nHeight to be 0
1059 if ( !Height() )
1060 Height( rInf.GetTextHeight() );
1062 // And now for another round
1063 m_nDropHeight = m_nLines = 0;
1064 m_pPart.reset();
1066 // Meanwhile use normal formatting
1067 bFull = SwTextPortion::Format( rInf );
1069 else
1070 rInf.SetDropInit( true );
1072 Height( rInf.GetTextHeight() );
1073 SetAscent( rInf.GetAscent() );
1075 else
1076 bFull = SwTextPortion::Format( rInf );
1078 if( bFull )
1079 m_nDistance = 0;
1080 else
1082 const sal_uInt16 nWant = Width() + GetDistance();
1083 const sal_uInt16 nRest = o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X());
1084 if( ( nWant > nRest ) ||
1085 lcl_IsDropFlyInter( rInf, Width() + GetDistance(), m_nDropHeight ) )
1086 m_nDistance = 0;
1088 Width( Width() + m_nDistance );
1090 return bFull;
1093 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */