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