1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
24 #include <charfmt.hxx>
25 #include <viewopt.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>
39 #include <IDocumentSettingAccess.hxx>
41 using namespace ::com::sun::star::i18n
;
42 using namespace ::com::sun::star
;
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();
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();
70 SwTextPaintInfo
* pInf
;
77 explicit SwDropSave( const SwTextPaintInfo
&rInf
);
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
));
97 /// SwDropPortionPart DTor
98 SwDropPortionPart::~SwDropPortionPart()
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
),
116 SetWhichPor( PortionType::Drop
);
119 SwDropPortion::~SwDropPortion()
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
)
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();
144 case i18n::ScriptType::COMPLEX
:
145 eLanguage
= rAttrSet
.GetCTLLanguage().GetLanguage();
148 eLanguage
= rAttrSet
.GetLanguage().GetLanguage();
153 g_pBreakIt
->GetBreakIter()->getWordBoundary( GetText(), 0,
154 g_pBreakIt
->GetLocale( eLanguage
), WordType::DICTIONARY_WORD
, true );
156 nEnd
= aBound
.endPos
;
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
)) )
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
)
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();
191 case i18n::ScriptType::COMPLEX
:
192 eLanguage
= rAttrSet
.GetCTLLanguage().GetLanguage();
195 eLanguage
= rAttrSet
.GetLanguage().GetLanguage();
199 Boundary aBound
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
200 GetText(), 0, g_pBreakIt
->GetLocale(eLanguage
),
201 WordType::DICTIONARY_WORD
, true );
203 nEnd
= TextFrameIndex(aBound
.endPos
);
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
)
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);
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
231 bool SwTextNode::GetDropSize(int& rFontHeight
, int& rDropHeight
, int& rDropDescent
) const
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() ) )
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" );
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();
276 const SvxFontHeightItem
& rItem
= rSet
.Get(RES_CHRATR_FONTSIZE
);
277 rFontHeight
= rItem
.GetHeight();
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;
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();
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());
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 )
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
367 const_cast<SwTextPaintInfo
&>(rInf
).Y( aOutPos
.Y() + m_nDropHeight
);
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
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 ...
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
) )
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
))
427 // looks like shit, but what can we do?
428 rInf
.SetUnderflow( nullptr );
431 rInf
.SetLen( nOldInfLen
);
436 SwPosSize
SwDropPortion::GetTextSize( const SwTextSizeInfo
&rInf
) const
439 TextFrameIndex
nIdx(0);
441 const SwDropPortionPart
* pCurrPart
= GetPart();
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
);
459 const_cast<SwDropPortion
*>(this)->SetJoinBorderWithNext(pCurrPart
->GetJoinBorderWithNext());
460 const_cast<SwDropPortion
*>(this)->SetJoinBorderWithPrev(pCurrPart
->GetJoinBorderWithPrev());
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
);
472 const_cast<SwDropPortion
*>(this)->SetJoinBorderWithNext(false);
473 const_cast<SwDropPortion
*>(this)->SetJoinBorderWithPrev(false);
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;
490 sal_uInt16 nDropLns
= 0;
491 const bool bRegisterOld
= IsRegisterOn();
492 m_bRegisterOn
= false;
496 while( GetCurr()->IsDummy() )
502 // If we have only one line we return 0
503 if( GetNext() || GetDropLines() == 1 )
505 for( ; nDropLns
< nLines
; nDropLns
++ )
507 if ( GetCurr()->IsDummy() )
511 CalcAscentAndHeight( nAscent
, nHeight
);
512 nDropHght
= nDropHght
+ nHeight
;
513 m_bRegisterOn
= bRegisterOld
;
522 // We hit the line ascent when reaching the last line!
523 nDropHght
= nDropHght
- nHeight
;
524 nDropHght
= nDropHght
+ nAscent
;
527 m_bRegisterOn
= bRegisterOld
;
528 SetDropDescent( nHeight
- nAscent
);
529 SetDropHeight( nDropHght
);
530 SetDropLines( nDropLns
);
531 // Find old position!
532 while( pOldCurr
!= GetCurr() )
536 OSL_ENSURE( false, "SwTextFormatter::_CalcDropHeight: left Toulouse" );
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!" );
551 SetDropLines( nLines
);
552 if ( GetDropLines() > 1 )
555 CalcAscentAndHeight( nAscent
, nHeight
);
557 SetDropDescent( nHeight
- nAscent
);
558 SetDropHeight( nHeight
* nLines
- GetDropDescent() );
561 SwDropPortion
*SwTextFormatter::NewDropPortion( SwTextFormatInfo
&rInf
)
566 TextFrameIndex
nPorLen(m_pDropFormat
->GetWholeWord() ? 0 : m_pDropFormat
->GetChars());
567 nPorLen
= m_pFrame
->GetDropLen( nPorLen
);
574 SwDropPortion
*pDropPor
= nullptr;
576 // first or second round?
577 if ( !( GetDropHeight() || IsOnceMore() ) )
580 CalcDropHeight( m_pDropFormat
->GetLines() );
582 GuessDropHeight( m_pDropFormat
->GetLines() );
586 if( GetDropHeight() )
587 pDropPor
= new SwDropPortion( GetDropLines(), GetDropHeight(),
588 GetDropDescent(), m_pDropFormat
->GetDistance() );
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
598 if ( GetDropLines() < 2 )
600 SetPaintDrop( true );
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:
615 // the font is deleted in the destructor of the drop portion part
616 SwFont
* pTmpFnt
= new SwFont( *rInf
.GetFont() );
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
)
635 std::unique_ptr
<SwDropPortionPart
> pPart(
636 new SwDropPortionPart( *pTmpFnt
, nNextChg
- nTmpIdx
) );
637 auto pPartTemp
= pPart
.get();
640 pDropPor
->SetPart( std::move(pPart
) );
642 pCurrPart
->SetFollow( std::move(pPart
) );
644 pCurrPart
= pPartTemp
;
647 SetPaintDrop( true );
651 void SwTextPainter::PaintDropPortion()
653 const SwDropPortion
*pDrop
= GetInfo().GetParaPortion()->FindDropPortion();
654 OSL_ENSURE( pDrop
, "DrapCop-Portion not available." );
658 const SwTwips nOldY
= GetInfo().Y();
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();
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
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;
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;
733 SwFont
& rFnt
= pCurrPart
->GetFont();
734 rFnt
.CheckFontCacheId( rInf
.GetVsh(), rFnt
.GetActual() );
735 rFnt
.GetFontCacheId( nFntCacheId
, nTmpIdx
, rFnt
.GetActual() );
739 while( nTmpIdx
< DROP_CACHE_SIZE
&&
740 ( m_aText
[ nTmpIdx
] != aStr
|| m_aFontCacheId
[ nTmpIdx
] != nFntCacheId
||
741 m_aWishedHeight
[ nTmpIdx
] != pDrop
->GetDropHeight() ) )
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
)
752 m_nIndex
%= DROP_CACHE_SIZE
;
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;
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
;
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;
791 bool bWinUsed
= false;
793 MapMode
aOldMap( MapUnit::MapTwip
);
794 OutputDevice
* pOut
= rInf
.GetOut();
796 if( rInf
.GetVsh() && rInf
.GetVsh()->GetWin() )
797 pWin
= rInf
.GetVsh()->GetWin()->GetOutDev();
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;
808 // reset pCurrPart to first part
809 pCurrPart
= pDrop
->GetPart();
810 bool bFirstGlyphRect
= true;
811 tools::Rectangle aCommonRect
, aRect
;
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
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();
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 )
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
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
)
905 bFirstGlyphRect
= false;
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();
924 nDescent
= -nDescent
;
928 nDescent
= aCommonRect
.Bottom();
929 nAscent
= aCommonRect
.Top();
934 const tools::Long nHght
= nAscent
+ nDescent
;
937 if ( nHght
> nWishedHeight
)
942 m_aFactor
[ nTmpIdx
] = o3tl::narrowing
<sal_uInt16
>(nFactor
);
946 nFactor
= ( nFactor
* nWishedHeight
) / nHght
;
947 bGrow
= ( nFactor
> nMin
) && ( nFactor
< nMax
);
948 #if OSL_DEBUG_LEVEL > 1
952 nIdx
= rInf
.GetIdx();
960 // reset window if it has been used
961 pWin
->SetMapMode( aOldMap
);
962 pWin
->SetFont( aOldFnt
);
966 m_aDescent
[ nTmpIdx
] = -short( nDescent
);
969 pCurrPart
= pDrop
->GetPart();
971 // did made any new calculations or did we use the cache?
974 nFactor
= m_aFactor
[ nTmpIdx
];
975 nDescent
= m_aDescent
[ nTmpIdx
];
978 nDescent
= -nDescent
;
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
)
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();
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
);
1031 const SwTwips nTmpWidth
=
1032 ( InSpaceGrp() && rInf
.GetSpaceAdd() ) ?
1033 Width() + CalcSpacing( rInf
.GetSpaceAdd(), rInf
) :
1037 pCurrPart
->SetWidth( o3tl::narrowing
<sal_uInt16
>(nTmpWidth
) );
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
) );
1050 SetLen( rInf
.GetLen() );
1052 // Quit when Flys are overlapping
1054 bFull
= lcl_IsDropFlyInter( rInf
, Width(), m_nDropHeight
);
1058 // FormatText could have caused nHeight to be 0
1060 Height( rInf
.GetTextHeight() );
1062 // And now for another round
1063 m_nDropHeight
= m_nLines
= 0;
1066 // Meanwhile use normal formatting
1067 bFull
= SwTextPortion::Format( rInf
);
1070 rInf
.SetDropInit( true );
1072 Height( rInf
.GetTextHeight() );
1073 SetAscent( rInf
.GetAscent() );
1076 bFull
= SwTextPortion::Format( rInf
);
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
) )
1088 Width( Width() + m_nDistance
);
1093 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */