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 <com/sun/star/linguistic2/XHyphenator.hpp>
21 
22 #include <unotools/linguprops.hxx>
23 #include <unotools/lingucfg.hxx>
24 #include <hintids.hxx>
25 #include <svl/ctloptions.hxx>
26 #include <sfx2/infobar.hxx>
27 #include <sfx2/printer.hxx>
28 #include <sal/log.hxx>
29 #include <editeng/hyphenzoneitem.hxx>
30 #include <editeng/hngpnctitem.hxx>
31 #include <editeng/scriptspaceitem.hxx>
32 #include <editeng/brushitem.hxx>
33 #include <editeng/splwrap.hxx>
34 #include <editeng/pgrditem.hxx>
35 #include <editeng/tstpitem.hxx>
36 #include <editeng/shaditem.hxx>
37 
38 #include <SwSmartTagMgr.hxx>
39 #include <breakit.hxx>
40 #include <editeng/forbiddenruleitem.hxx>
41 #include <paintfrm.hxx>
42 #include <swmodule.hxx>
43 #include <vcl/svapp.hxx>
44 #include <viewsh.hxx>
45 #include <viewopt.hxx>
46 #include <frmtool.hxx>
47 #include <IDocumentSettingAccess.hxx>
48 #include <IDocumentDeviceAccess.hxx>
49 #include <IDocumentMarkAccess.hxx>
50 #include <paratr.hxx>
51 #include <rootfrm.hxx>
52 #include "inftxt.hxx"
53 #include <noteurl.hxx>
54 #include "porftn.hxx"
55 #include "porrst.hxx"
56 #include "itratr.hxx"
57 #include "portab.hxx"
58 #include <wrong.hxx>
59 #include <doc.hxx>
60 #include <pam.hxx>
61 #include <numrule.hxx>
62 #include <EnhancedPDFExportHelper.hxx>
63 #include <docsh.hxx>
64 #include <strings.hrc>
65 #include <o3tl/deleter.hxx>
66 #include <vcl/gdimtf.hxx>
67 #include <vcl/virdev.hxx>
68 #include <vcl/gradient.hxx>
69 
70 using namespace ::com::sun::star;
71 using namespace ::com::sun::star::linguistic2;
72 using namespace ::com::sun::star::uno;
73 using namespace ::com::sun::star::beans;
74 
75 #define CHAR_UNDERSCORE u'_'
76 #define CHAR_LEFT_ARROW u'\x25C0'
77 #define CHAR_RIGHT_ARROW u'\x25B6'
78 #define CHAR_TAB u'\x2192'
79 #define CHAR_TAB_RTL u'\x2190'
80 #define CHAR_LINEBREAK u'\x21B5'
81 #define CHAR_LINEBREAK_RTL u'\x21B3'
82 
83 #define DRAW_SPECIAL_OPTIONS_CENTER 1
84 #define DRAW_SPECIAL_OPTIONS_ROTATE 2
85 
SwLineInfo()86 SwLineInfo::SwLineInfo()
87     : m_pSpace( nullptr ),
88       m_nVertAlign( SvxParaVertAlignItem::Align::Automatic ),
89       m_nDefTabStop( 0 ),
90       m_bListTabStopIncluded( false ),
91       m_nListTabStopPosition( 0 )
92 {
93 }
94 
~SwLineInfo()95 SwLineInfo::~SwLineInfo()
96 {
97 }
98 
CtorInitLineInfo(const SwAttrSet & rAttrSet,const SwTextNode & rTextNode)99 void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
100                                    const SwTextNode& rTextNode )
101 {
102     m_pRuler.reset( new SvxTabStopItem( rAttrSet.GetTabStops() ) );
103     if ( rTextNode.GetListTabStopPosition( m_nListTabStopPosition ) )
104     {
105         m_bListTabStopIncluded = true;
106 
107         // insert the list tab stop into SvxTabItem instance <pRuler>
108         const SvxTabStop aListTabStop( m_nListTabStopPosition,
109                                        SvxTabAdjust::Left );
110         m_pRuler->Insert( aListTabStop );
111 
112         // remove default tab stops, which are before the inserted list tab stop
113         for ( sal_uInt16 i = 0; i < m_pRuler->Count(); i++ )
114         {
115             if ( (*m_pRuler)[i].GetTabPos() < m_nListTabStopPosition &&
116                  (*m_pRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
117             {
118                 m_pRuler->Remove(i);
119                 continue;
120             }
121         }
122     }
123 
124     if ( !rTextNode.getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
125     {
126         // remove default tab stop at position 0
127         for ( sal_uInt16 i = 0; i < m_pRuler->Count(); i++ )
128         {
129             if ( (*m_pRuler)[i].GetTabPos() == 0 &&
130                  (*m_pRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
131             {
132                 m_pRuler->Remove(i);
133                 break;
134             }
135         }
136     }
137 
138     m_pSpace = &rAttrSet.GetLineSpacing();
139     m_nVertAlign = rAttrSet.GetParaVertAlign().GetValue();
140     m_nDefTabStop = USHRT_MAX;
141 }
142 
CtorInitTextInfo(SwTextFrame * pFrame)143 void SwTextInfo::CtorInitTextInfo( SwTextFrame *pFrame )
144 {
145     m_pPara = pFrame->GetPara();
146     m_nTextStart = pFrame->GetOffset();
147     if (!m_pPara)
148     {
149         SAL_WARN("sw.core", "+SwTextInfo::CTOR: missing paragraph information");
150         pFrame->Format(pFrame->getRootFrame()->GetCurrShell()->GetOut());
151         m_pPara = pFrame->GetPara();
152     }
153 }
154 
SwTextInfo(const SwTextInfo & rInf)155 SwTextInfo::SwTextInfo( const SwTextInfo &rInf )
156     : m_pPara( const_cast<SwTextInfo&>(rInf).GetParaPortion() )
157     , m_nTextStart( rInf.GetTextStart() )
158 { }
159 
160 #if OSL_DEBUG_LEVEL > 0
161 
ChkOutDev(const SwTextSizeInfo & rInf)162 static void ChkOutDev( const SwTextSizeInfo &rInf )
163 {
164     if ( !rInf.GetVsh() )
165         return;
166 
167     const OutputDevice* pOut = rInf.GetOut();
168     const OutputDevice* pRef = rInf.GetRefDev();
169     OSL_ENSURE( pOut && pRef, "ChkOutDev: invalid output devices" );
170 }
171 #endif
172 
GetMinLen(const SwTextSizeInfo & rInf)173 static TextFrameIndex GetMinLen( const SwTextSizeInfo &rInf )
174 {
175     const TextFrameIndex nTextLen(rInf.GetText().getLength());
176     if (rInf.GetLen() == TextFrameIndex(COMPLETE_STRING))
177         return nTextLen;
178     const TextFrameIndex nInfLen = rInf.GetIdx() + rInf.GetLen();
179     return std::min(nTextLen, nInfLen);
180 }
181 
SwTextSizeInfo()182 SwTextSizeInfo::SwTextSizeInfo()
183 : m_pKanaComp(nullptr)
184 , m_pVsh(nullptr)
185 , m_pOut(nullptr)
186 , m_pRef(nullptr)
187 , m_pFnt(nullptr)
188 , m_pUnderFnt(nullptr)
189 , m_pFrame(nullptr)
190 , m_pOpt(nullptr)
191 , m_pText(nullptr)
192 , m_nIdx(0)
193 , m_nLen(0)
194 , m_nKanaIdx(0)
195 , m_bOnWin    (false)
196 , m_bNotEOL   (false)
197 , m_bURLNotify(false)
198 , m_bStopUnderflow(false)
199 , m_bFootnoteInside(false)
200 , m_bOtherThanFootnoteInside(false)
201 , m_bMulti(false)
202 , m_bFirstMulti(false)
203 , m_bRuby(false)
204 , m_bHanging(false)
205 , m_bScriptSpace(false)
206 , m_bForbiddenChars(false)
207 , m_bSnapToGrid(false)
208 , m_nDirection(0)
209 {}
210 
SwTextSizeInfo(const SwTextSizeInfo & rNew)211 SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
212     : SwTextInfo( rNew ),
213       m_pKanaComp(rNew.GetpKanaComp()),
214       m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
215       m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
216       m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
217       m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
218       m_pUnderFnt(rNew.GetUnderFnt()),
219       m_pFrame(rNew.m_pFrame),
220       m_pOpt(&rNew.GetOpt()),
221       m_pText(&rNew.GetText()),
222       m_nIdx(rNew.GetIdx()),
223       m_nLen(rNew.GetLen()),
224       m_nKanaIdx( rNew.GetKanaIdx() ),
225       m_bOnWin( rNew.OnWin() ),
226       m_bNotEOL( rNew.NotEOL() ),
227       m_bURLNotify( rNew.URLNotify() ),
228       m_bStopUnderflow( rNew.StopUnderflow() ),
229       m_bFootnoteInside( rNew.IsFootnoteInside() ),
230       m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
231       m_bMulti( rNew.IsMulti() ),
232       m_bFirstMulti( rNew.IsFirstMulti() ),
233       m_bRuby( rNew.IsRuby() ),
234       m_bHanging( rNew.IsHanging() ),
235       m_bScriptSpace( rNew.HasScriptSpace() ),
236       m_bForbiddenChars( rNew.HasForbiddenChars() ),
237       m_bSnapToGrid( rNew.SnapToGrid() ),
238       m_nDirection( rNew.GetDirection() )
239 {
240 #if OSL_DEBUG_LEVEL > 0
241     ChkOutDev( *this );
242 #endif
243 }
244 
CtorInitTextSizeInfo(OutputDevice * pRenderContext,SwTextFrame * pFrame,TextFrameIndex const nNewIdx)245 void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame,
246            TextFrameIndex const nNewIdx)
247 {
248     m_pKanaComp = nullptr;
249     m_nKanaIdx = 0;
250     m_pFrame = pFrame;
251     CtorInitTextInfo( m_pFrame );
252     SwDoc const& rDoc(m_pFrame->GetDoc());
253     m_pVsh = m_pFrame->getRootFrame()->GetCurrShell();
254 
255     // Get the output and reference device
256     if ( m_pVsh )
257     {
258         m_pOut = pRenderContext;
259         m_pRef = &m_pVsh->GetRefDev();
260         m_bOnWin = m_pVsh->GetWin() || OUTDEV_WINDOW == m_pOut->GetOutDevType() || m_pVsh->isOutputToWindow();
261     }
262     else
263     {
264         // Access via StarONE. We do not need a Shell or an active one.
265         if (rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE))
266         {
267             // We can only pick the AppWin here? (there's nothing better to pick?)
268             m_pOut = Application::GetDefaultDevice();
269         }
270         else
271             m_pOut = rDoc.getIDocumentDeviceAccess().getPrinter(false);
272 
273         m_pRef = m_pOut;
274     }
275 
276 #if OSL_DEBUG_LEVEL > 0
277     ChkOutDev( *this );
278 #endif
279 
280     // Set default layout mode ( LTR or RTL ).
281     if ( m_pFrame->IsRightToLeft() )
282     {
283         m_pOut->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl );
284         m_pRef->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl );
285         m_nDirection = DIR_RIGHT2LEFT;
286     }
287     else
288     {
289         m_pOut->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong );
290         m_pRef->SetLayoutMode( ComplexTextLayoutFlags::BiDiStrong );
291         m_nDirection = DIR_LEFT2RIGHT;
292     }
293 
294     // The Options
295 
296     m_pOpt = m_pVsh ?
297            m_pVsh->GetViewOptions() :
298            SW_MOD()->GetViewOption(rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)); // Options from Module, due to StarONE
299 
300     // bURLNotify is set if MakeGraphic prepares it
301     // TODO: Unwind
302     m_bURLNotify = pNoteURL && !m_bOnWin;
303 
304     SetSnapToGrid( m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue() &&
305                    m_pFrame->IsInDocBody() );
306 
307     m_pFnt = nullptr;
308     m_pUnderFnt = nullptr;
309     m_pText = &m_pFrame->GetText();
310 
311     m_nIdx = nNewIdx;
312     m_nLen = TextFrameIndex(COMPLETE_STRING);
313     m_bNotEOL = false;
314     m_bStopUnderflow = m_bFootnoteInside = m_bOtherThanFootnoteInside = false;
315     m_bMulti = m_bFirstMulti = m_bRuby = m_bHanging = m_bScriptSpace =
316         m_bForbiddenChars = false;
317 
318     SetLen( GetMinLen( *this ) );
319 }
320 
SwTextSizeInfo(const SwTextSizeInfo & rNew,const OUString * pText,TextFrameIndex const nIndex)321 SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew, const OUString* pText,
322               TextFrameIndex const nIndex)
323     : SwTextInfo( rNew ),
324       m_pKanaComp(rNew.GetpKanaComp()),
325       m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
326       m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
327       m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
328       m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
329       m_pUnderFnt(rNew.GetUnderFnt()),
330       m_pFrame( rNew.m_pFrame ),
331       m_pOpt(&rNew.GetOpt()),
332       m_pText(pText),
333       m_nIdx(nIndex),
334       m_nLen(COMPLETE_STRING),
335       m_nKanaIdx( rNew.GetKanaIdx() ),
336       m_bOnWin( rNew.OnWin() ),
337       m_bNotEOL( rNew.NotEOL() ),
338       m_bURLNotify( rNew.URLNotify() ),
339       m_bStopUnderflow( rNew.StopUnderflow() ),
340       m_bFootnoteInside( rNew.IsFootnoteInside() ),
341       m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
342       m_bMulti( rNew.IsMulti() ),
343       m_bFirstMulti( rNew.IsFirstMulti() ),
344       m_bRuby( rNew.IsRuby() ),
345       m_bHanging( rNew.IsHanging() ),
346       m_bScriptSpace( rNew.HasScriptSpace() ),
347       m_bForbiddenChars( rNew.HasForbiddenChars() ),
348       m_bSnapToGrid( rNew.SnapToGrid() ),
349       m_nDirection( rNew.GetDirection() )
350 {
351 #if OSL_DEBUG_LEVEL > 0
352     ChkOutDev( *this );
353 #endif
354     SetLen( GetMinLen( *this ) );
355 }
356 
SwTextSizeInfo(SwTextFrame * const pTextFrame,TextFrameIndex const nIndex)357 SwTextSizeInfo::SwTextSizeInfo(SwTextFrame *const pTextFrame,
358             TextFrameIndex const nIndex)
359     : m_bOnWin(false)
360 {
361     CtorInitTextSizeInfo( pTextFrame->getRootFrame()->GetCurrShell()->GetOut(), pTextFrame, nIndex );
362 }
363 
SelectFont()364 void SwTextSizeInfo::SelectFont()
365 {
366      // The path needs to go via ChgPhysFnt or the FontMetricCache gets confused.
367      // In this case pLastMet has it's old value.
368      // Wrong: GetOut()->SetFont( GetFont()->GetFnt() );
369     GetFont()->Invalidate();
370     GetFont()->ChgPhysFnt( m_pVsh, *GetOut() );
371 }
372 
NoteAnimation() const373 void SwTextSizeInfo::NoteAnimation() const
374 {
375     if( OnWin() )
376         SwRootFrame::FlushVout();
377 
378     OSL_ENSURE( m_pOut == m_pVsh->GetOut(),
379             "SwTextSizeInfo::NoteAnimation() changed m_pOut" );
380 }
381 
GetTextSize(OutputDevice * pOutDev,const SwScriptInfo * pSI,const OUString & rText,const TextFrameIndex nIndex,const TextFrameIndex nLength) const382 SwPosSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
383                                      const SwScriptInfo* pSI,
384                                      const OUString& rText,
385                                      const TextFrameIndex nIndex,
386                                      const TextFrameIndex nLength) const
387 {
388     SwDrawTextInfo aDrawInf( m_pVsh, *pOutDev, pSI, rText, nIndex, nLength );
389     aDrawInf.SetFrame( m_pFrame );
390     aDrawInf.SetFont( m_pFnt );
391     aDrawInf.SetSnapToGrid( SnapToGrid() );
392     aDrawInf.SetKanaComp( 0 );
393     return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
394 }
395 
GetTextSize() const396 SwPosSize SwTextSizeInfo::GetTextSize() const
397 {
398     const SwScriptInfo& rSI =
399                      const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
400 
401     // in some cases, compression is not allowed or suppressed for
402     // performance reasons
403     sal_uInt16 nComp =( SwFontScript::CJK == GetFont()->GetActual() &&
404                     rSI.CountCompChg() &&
405                     ! IsMulti() ) ?
406                     GetKanaComp() :
407                                 0 ;
408 
409     SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen );
410     aDrawInf.SetFrame( m_pFrame );
411     aDrawInf.SetFont( m_pFnt );
412     aDrawInf.SetSnapToGrid( SnapToGrid() );
413     aDrawInf.SetKanaComp( nComp );
414     return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
415 }
416 
GetTextSize(const SwScriptInfo * pSI,const TextFrameIndex nIndex,const TextFrameIndex nLength,const sal_uInt16 nComp,sal_uInt16 & nMinSize,sal_uInt16 & nMaxSizeDiff,vcl::TextLayoutCache const * const pCache) const417 void SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI, const TextFrameIndex nIndex,
418                                 const TextFrameIndex nLength, const sal_uInt16 nComp,
419                                 sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
420                                 vcl::TextLayoutCache const*const pCache) const
421 {
422     SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength,
423             0, false, pCache);
424     aDrawInf.SetFrame( m_pFrame );
425     aDrawInf.SetFont( m_pFnt );
426     aDrawInf.SetSnapToGrid( SnapToGrid() );
427     aDrawInf.SetKanaComp( nComp );
428     SwPosSize aSize( m_pFnt->GetTextSize_( aDrawInf ) );
429     nMaxSizeDiff = static_cast<sal_uInt16>(aDrawInf.GetKanaDiff());
430     nMinSize = aSize.Width();
431 }
432 
GetTextBreak(const tools::Long nLineWidth,const TextFrameIndex nMaxLen,const sal_uInt16 nComp,vcl::TextLayoutCache const * const pCache) const433 TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
434                                        const TextFrameIndex nMaxLen,
435                                        const sal_uInt16 nComp,
436                                        vcl::TextLayoutCache const*const pCache) const
437 {
438     const SwScriptInfo& rScriptInfo =
439                      const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
440 
441     OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
442     SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
443                              *m_pText, GetIdx(), nMaxLen,  0, false, pCache );
444     aDrawInf.SetFrame( m_pFrame );
445     aDrawInf.SetFont( m_pFnt );
446     aDrawInf.SetSnapToGrid( SnapToGrid() );
447     aDrawInf.SetKanaComp( nComp );
448     aDrawInf.SetHyphPos( nullptr );
449 
450     return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
451 }
452 
GetTextBreak(const tools::Long nLineWidth,const TextFrameIndex nMaxLen,const sal_uInt16 nComp,TextFrameIndex & rExtraCharPos,vcl::TextLayoutCache const * const pCache) const453 TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
454                                        const TextFrameIndex nMaxLen,
455                                        const sal_uInt16 nComp,
456                                        TextFrameIndex& rExtraCharPos,
457                                        vcl::TextLayoutCache const*const pCache) const
458 {
459     const SwScriptInfo& rScriptInfo =
460                      const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
461 
462     OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
463     SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
464                              *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
465     aDrawInf.SetFrame( m_pFrame );
466     aDrawInf.SetFont( m_pFnt );
467     aDrawInf.SetSnapToGrid( SnapToGrid() );
468     aDrawInf.SetKanaComp( nComp );
469     aDrawInf.SetHyphPos( &rExtraCharPos );
470 
471     return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
472 }
473 
HasHint(TextFrameIndex const nPos) const474 bool SwTextSizeInfo::HasHint(TextFrameIndex const nPos) const
475 {
476     std::pair<SwTextNode const*, sal_Int32> const pos(m_pFrame->MapViewToModel(nPos));
477     return pos.first->GetTextAttrForCharAt(pos.second);
478 }
479 
CtorInitTextPaintInfo(OutputDevice * pRenderContext,SwTextFrame * pFrame,const SwRect & rPaint)480 void SwTextPaintInfo::CtorInitTextPaintInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame, const SwRect &rPaint )
481 {
482     CtorInitTextSizeInfo( pRenderContext, pFrame, TextFrameIndex(0) );
483     m_aTextFly.CtorInitTextFly( pFrame );
484     m_aPaintRect = rPaint;
485     m_nSpaceIdx = 0;
486     m_pSpaceAdd = nullptr;
487     m_pWrongList = nullptr;
488     m_pGrammarCheckList = nullptr;
489     m_pSmartTags = nullptr;
490     m_pBrushItem = nullptr;
491 }
492 
SwTextPaintInfo(const SwTextPaintInfo & rInf,const OUString * pText)493 SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf, const OUString* pText )
494     : SwTextSizeInfo( rInf, pText )
495     , m_pWrongList( rInf.GetpWrongList() )
496     , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
497     , m_pSmartTags( rInf.GetSmartTags() )
498     , m_pSpaceAdd( rInf.GetpSpaceAdd() ),
499       m_pBrushItem( rInf.GetBrushItem() ),
500       m_aTextFly( rInf.GetTextFly() ),
501       m_aPos( rInf.GetPos() ),
502       m_aPaintRect( rInf.GetPaintRect() ),
503       m_nSpaceIdx( rInf.GetSpaceIdx() )
504 { }
505 
SwTextPaintInfo(const SwTextPaintInfo & rInf)506 SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf )
507     : SwTextSizeInfo( rInf )
508     , m_pWrongList( rInf.GetpWrongList() )
509     , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
510     , m_pSmartTags( rInf.GetSmartTags() )
511     , m_pSpaceAdd( rInf.GetpSpaceAdd() ),
512       m_pBrushItem( rInf.GetBrushItem() ),
513       m_aTextFly( rInf.GetTextFly() ),
514       m_aPos( rInf.GetPos() ),
515       m_aPaintRect( rInf.GetPaintRect() ),
516       m_nSpaceIdx( rInf.GetSpaceIdx() )
517 { }
518 
SwTextPaintInfo(SwTextFrame * pFrame,const SwRect & rPaint)519 SwTextPaintInfo::SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint )
520 {
521     CtorInitTextPaintInfo( pFrame->getRootFrame()->GetCurrShell()->GetOut(), pFrame, rPaint );
522 }
523 
524 /// Returns if the current background color is dark.
lcl_IsDarkBackground(const SwTextPaintInfo & rInf)525 static bool lcl_IsDarkBackground( const SwTextPaintInfo& rInf )
526 {
527     std::optional<Color> pCol = rInf.GetFont()->GetBackColor();
528     if( ! pCol || COL_TRANSPARENT == *pCol )
529     {
530         const SvxBrushItem* pItem;
531         SwRect aOrigBackRect;
532         drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
533 
534         // Consider, that [GetBackgroundBrush(...)] can set <pCol>
535         // See implementation in /core/layout/paintfrm.cxx
536         // There is a background color, if there is a background brush and
537         // its color is *not* "no fill"/"auto fill".
538         if( rInf.GetTextFrame()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false ) )
539         {
540             if ( !pCol )
541                 pCol = pItem->GetColor();
542 
543             // Determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it.
544             if ( *pCol == COL_TRANSPARENT)
545                 pCol.reset();
546         }
547         else
548             pCol.reset();
549     }
550 
551     if( !pCol )
552         pCol = aGlobalRetoucheColor;
553 
554     return pCol->IsDark();
555 }
556 
557 namespace
558 {
559 /**
560  * Context class that captures the draw operations on rDrawInf's output device for transparency
561  * purposes.
562  */
563 class SwTransparentTextGuard
564 {
565     ScopedVclPtrInstance<VirtualDevice> m_aContentVDev;
566     GDIMetaFile m_aContentMetafile;
567     MapMode m_aNewMapMode;
568     SwRect m_aPorRect;
569     SwTextPaintInfo& m_rPaintInf;
570     SwDrawTextInfo& m_rDrawInf;
571 
572 public:
573     SwTransparentTextGuard(const SwLinePortion& rPor, SwTextPaintInfo& rPaintInf,
574                            SwDrawTextInfo& rDrawInf);
575     ~SwTransparentTextGuard();
576 };
577 
SwTransparentTextGuard(const SwLinePortion & rPor,SwTextPaintInfo & rPaintInf,SwDrawTextInfo & rDrawInf)578 SwTransparentTextGuard::SwTransparentTextGuard(const SwLinePortion& rPor,
579                                                SwTextPaintInfo& rPaintInf, SwDrawTextInfo& rDrawInf)
580     : m_aNewMapMode(rPaintInf.GetOut()->GetMapMode())
581     , m_rPaintInf(rPaintInf)
582     , m_rDrawInf(rDrawInf)
583 {
584     rPaintInf.CalcRect(rPor, &m_aPorRect);
585     rDrawInf.SetOut(*m_aContentVDev);
586     m_aContentVDev->SetMapMode(rPaintInf.GetOut()->GetMapMode());
587     m_aContentMetafile.Record(m_aContentVDev.get());
588     m_aContentVDev->SetLineColor(rPaintInf.GetOut()->GetLineColor());
589     m_aContentVDev->SetFillColor(rPaintInf.GetOut()->GetFillColor());
590     m_aContentVDev->SetFont(rPaintInf.GetOut()->GetFont());
591     m_aContentVDev->SetDrawMode(rPaintInf.GetOut()->GetDrawMode());
592     m_aContentVDev->SetSettings(rPaintInf.GetOut()->GetSettings());
593     m_aContentVDev->SetRefPoint(rPaintInf.GetOut()->GetRefPoint());
594 }
595 
~SwTransparentTextGuard()596 SwTransparentTextGuard::~SwTransparentTextGuard()
597 {
598     m_aContentMetafile.Stop();
599     m_aContentMetafile.WindStart();
600     m_aNewMapMode.SetOrigin(m_aPorRect.TopLeft());
601     m_aContentMetafile.SetPrefMapMode(m_aNewMapMode);
602     m_aContentMetafile.SetPrefSize(m_aPorRect.SSize());
603     m_rDrawInf.SetOut(*m_rPaintInf.GetOut());
604     Gradient aVCLGradient;
605     sal_uInt8 nTransPercentVcl = 255 - m_rPaintInf.GetFont()->GetColor().GetAlpha();
606     const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
607     aVCLGradient.SetStyle(GradientStyle::Linear);
608     aVCLGradient.SetStartColor(aTransColor);
609     aVCLGradient.SetEndColor(aTransColor);
610     aVCLGradient.SetAngle(0_deg10);
611     aVCLGradient.SetBorder(0);
612     aVCLGradient.SetOfsX(0);
613     aVCLGradient.SetOfsY(0);
614     aVCLGradient.SetStartIntensity(100);
615     aVCLGradient.SetEndIntensity(100);
616     aVCLGradient.SetSteps(2);
617     m_rPaintInf.GetOut()->DrawTransparent(m_aContentMetafile, m_aPorRect.TopLeft(),
618                                           m_aPorRect.SSize(), aVCLGradient);
619 }
620 }
621 
DrawText_(const OUString & rText,const SwLinePortion & rPor,TextFrameIndex const nStart,TextFrameIndex const nLength,const bool bKern,const bool bWrong,const bool bSmartTag,const bool bGrammarCheck)622 void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPor,
623                                 TextFrameIndex const nStart, TextFrameIndex const nLength,
624                                 const bool bKern, const bool bWrong,
625                                 const bool bSmartTag,
626                                 const bool bGrammarCheck )
627 {
628     if( !nLength )
629         return;
630 
631     // The SwScriptInfo is useless if we are inside a field portion
632     SwScriptInfo* pSI = nullptr;
633     if ( ! rPor.InFieldGrp() )
634         pSI = &GetParaPortion()->GetScriptInfo();
635 
636     // in some cases, kana compression is not allowed or suppressed for
637     // performance reasons
638     sal_uInt16 nComp = 0;
639     if ( ! IsMulti() )
640         nComp = GetKanaComp();
641 
642     bool bCfgIsAutoGrammar = false;
643     SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bCfgIsAutoGrammar;
644     const bool bBullet = OnWin() && GetOpt().IsBlank() && IsNoSymbol();
645     const bool bTmpWrong = bWrong && OnWin() && GetOpt().IsOnlineSpell();
646     const bool bTmpGrammarCheck = bGrammarCheck && OnWin() && bCfgIsAutoGrammar && GetOpt().IsOnlineSpell();
647     const bool bTmpSmart = bSmartTag && OnWin() && !GetOpt().IsPagePreview() && SwSmartTagMgr::Get().IsSmartTagsEnabled();
648 
649     OSL_ENSURE( GetParaPortion(), "No paragraph!");
650     SwDrawTextInfo aDrawInf( m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart, nLength,
651                              rPor.Width(), bBullet );
652 
653     aDrawInf.SetUnderFnt( m_pUnderFnt );
654 
655     const tools::Long nSpaceAdd = ( rPor.IsBlankPortion() || rPor.IsDropPortion() ||
656                              rPor.InNumberGrp() ) ? 0 : GetSpaceAdd();
657     if ( nSpaceAdd )
658     {
659         TextFrameIndex nCharCnt(0);
660         // #i41860# Thai justified alignment needs some
661         // additional information:
662         aDrawInf.SetNumberOfBlanks( rPor.InTextGrp() ?
663                                     static_cast<const SwTextPortion&>(rPor).GetSpaceCnt( *this, nCharCnt ) :
664                                     TextFrameIndex(0) );
665     }
666 
667     aDrawInf.SetSpace( nSpaceAdd );
668     aDrawInf.SetKanaComp( nComp );
669 
670     // the font is used to identify the current script via nActual
671     aDrawInf.SetFont( m_pFnt );
672     // the frame is used to identify the orientation
673     aDrawInf.SetFrame( GetTextFrame() );
674     // we have to know if the paragraph should snap to grid
675     aDrawInf.SetSnapToGrid( SnapToGrid() );
676     // for underlining we must know when not to add extra space behind
677     // a character in justified mode
678     aDrawInf.SetSpaceStop( ! rPor.GetNextPortion() ||
679                              rPor.GetNextPortion()->InFixMargGrp() ||
680                              rPor.GetNextPortion()->IsHolePortion() );
681 
682     // Draw text next to the left border
683     Point aFontPos(m_aPos);
684     if( m_pFnt->GetLeftBorder() && !static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev() )
685     {
686         const sal_uInt16 nLeftBorderSpace = m_pFnt->GetLeftBorderSpace();
687         if ( GetTextFrame()->IsRightToLeft() )
688         {
689             aFontPos.AdjustX( -nLeftBorderSpace );
690         }
691         else
692         {
693             switch( m_pFnt->GetOrientation(GetTextFrame()->IsVertical()).get() )
694             {
695                 case 0 :
696                     aFontPos.AdjustX(nLeftBorderSpace );
697                     break;
698                 case 900 :
699                     aFontPos.AdjustY( -nLeftBorderSpace );
700                     break;
701                 case 1800 :
702                     aFontPos.AdjustX( -nLeftBorderSpace );
703                     break;
704                 case 2700 :
705                     aFontPos.AdjustY(nLeftBorderSpace );
706                     break;
707             }
708         }
709         if( aFontPos.X() < 0 )
710             aFontPos.setX( 0 );
711         if( aFontPos.Y() < 0 )
712             aFontPos.setY( 0 );
713     }
714 
715     // Handle semi-transparent text if necessary.
716     std::unique_ptr<SwTransparentTextGuard, o3tl::default_delete<SwTransparentTextGuard>> pTransparentText;
717     if (m_pFnt->GetColor() != COL_AUTO && m_pFnt->GetColor().IsTransparent())
718     {
719         pTransparentText.reset(new SwTransparentTextGuard(rPor, *this, aDrawInf));
720     }
721 
722     if( GetTextFly().IsOn() )
723     {
724         // aPos needs to be the TopLeft, because we cannot calculate the
725         // ClipRects otherwise
726         const Point aPoint( aFontPos.X(), aFontPos.Y() - rPor.GetAscent() );
727         const Size aSize( rPor.Width(), rPor.Height() );
728         aDrawInf.SetPos( aPoint );
729         aDrawInf.SetSize( aSize );
730         aDrawInf.SetAscent( rPor.GetAscent() );
731         aDrawInf.SetKern( bKern ? rPor.Width() : 0 );
732         aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
733         aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
734         aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
735         GetTextFly().DrawTextOpaque( aDrawInf );
736     }
737     else
738     {
739         aDrawInf.SetPos( aFontPos );
740         if( bKern )
741             m_pFnt->DrawStretchText_( aDrawInf );
742         else
743         {
744             aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
745             aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
746             aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
747             m_pFnt->DrawText_( aDrawInf );
748         }
749     }
750 }
751 
CalcRect(const SwLinePortion & rPor,SwRect * pRect,SwRect * pIntersect,const bool bInsideBox) const752 void SwTextPaintInfo::CalcRect( const SwLinePortion& rPor,
753                                SwRect* pRect, SwRect* pIntersect,
754                                const bool bInsideBox ) const
755 {
756     Size aSize( rPor.Width(), rPor.Height() );
757     if( rPor.IsHangingPortion() )
758         aSize.setWidth( static_cast<const SwHangingPortion&>(rPor).GetInnerWidth() );
759     if( rPor.InSpaceGrp() && GetSpaceAdd() )
760     {
761         SwTwips nAdd = rPor.CalcSpacing( GetSpaceAdd(), *this );
762         if( rPor.InFieldGrp() && GetSpaceAdd() < 0 && nAdd )
763             nAdd += GetSpaceAdd() / SPACING_PRECISION_FACTOR;
764         aSize.AdjustWidth(nAdd );
765     }
766 
767     Point aPoint;
768 
769     if( IsRotated() )
770     {
771         tools::Long nTmp = aSize.Width();
772         aSize.setWidth( aSize.Height() );
773         aSize.setHeight( nTmp );
774         if ( 1 == GetDirection() )
775         {
776             aPoint.setX( X() - rPor.GetAscent() );
777             aPoint.setY( Y() - aSize.Height() );
778         }
779         else
780         {
781             aPoint.setX( X() - rPor.Height() + rPor.GetAscent() );
782             aPoint.setY( Y() );
783         }
784     }
785     else
786     {
787         aPoint.setX( X() );
788         if (GetTextFrame()->IsVertLR() && !GetTextFrame()->IsVertLRBT())
789             aPoint.setY( Y() - rPor.Height() + rPor.GetAscent() );
790         else
791             aPoint.setY( Y() - rPor.GetAscent() );
792     }
793 
794     // Adjust x coordinate if we are inside a bidi portion
795     const bool bFrameDir = GetTextFrame()->IsRightToLeft();
796     const bool bCounterDir = ( !bFrameDir && DIR_RIGHT2LEFT == GetDirection() ) ||
797                              (  bFrameDir && DIR_LEFT2RIGHT == GetDirection() );
798 
799     if ( bCounterDir )
800         aPoint.AdjustX( -(aSize.Width()) );
801 
802     SwRect aRect( aPoint, aSize );
803 
804     if ( GetTextFrame()->IsRightToLeft() )
805         GetTextFrame()->SwitchLTRtoRTL( aRect );
806 
807     if ( GetTextFrame()->IsVertical() )
808         GetTextFrame()->SwitchHorizontalToVertical( aRect );
809 
810     if( bInsideBox && rPor.InTextGrp() )
811     {
812         const bool bJoinWithPrev =
813             static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev();
814         const bool bJoinWithNext =
815             static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithNext();
816         const bool bIsVert = GetTextFrame()->IsVertical();
817         const bool bIsVertLRBT = GetTextFrame()->IsVertLRBT();
818         aRect.AddTop( GetFont()->CalcShadowSpace(SvxShadowItemSide::TOP, bIsVert, bIsVertLRBT,
819                                                bJoinWithPrev, bJoinWithNext));
820         aRect.AddBottom( - GetFont()->CalcShadowSpace(SvxShadowItemSide::BOTTOM, bIsVert, bIsVertLRBT,
821                                                   bJoinWithPrev, bJoinWithNext));
822         aRect.AddLeft( GetFont()->CalcShadowSpace(SvxShadowItemSide::LEFT, bIsVert, bIsVertLRBT,
823                                                 bJoinWithPrev, bJoinWithNext));
824         aRect.AddRight( - GetFont()->CalcShadowSpace(SvxShadowItemSide::RIGHT, bIsVert, bIsVertLRBT,
825                                                  bJoinWithPrev, bJoinWithNext));
826     }
827 
828     if ( pRect )
829         *pRect = aRect;
830 
831     if( aRect.HasArea() && pIntersect )
832     {
833         ::SwAlignRect( aRect, GetVsh(), GetOut() );
834 
835         if ( GetOut()->IsClipRegion() )
836         {
837             SwRect aClip( GetOut()->GetClipRegion().GetBoundRect() );
838             aRect.Intersection( aClip );
839         }
840 
841         *pIntersect = aRect;
842     }
843 }
844 
845 /**
846  * Draws a special portion
847  * E.g.: line break portion, tab portion
848  *
849  * @param rPor The portion
850  * @param rRect The rectangle surrounding the character
851  * @param rCol Specify a color for the character
852  * @param bCenter Draw the character centered, otherwise left aligned
853  * @param bRotate Rotate the character if character rotation is set
854  */
lcl_DrawSpecial(const SwTextPaintInfo & rTextPaintInfo,const SwLinePortion & rPor,SwRect & rRect,const Color & rCol,sal_Unicode cChar,sal_uInt8 nOptions)855 static void lcl_DrawSpecial( const SwTextPaintInfo& rTextPaintInfo, const SwLinePortion& rPor,
856                       SwRect& rRect, const Color& rCol, sal_Unicode cChar,
857                       sal_uInt8 nOptions )
858 {
859     bool bCenter = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_CENTER );
860     bool bRotate = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_ROTATE );
861 
862     // rRect is given in absolute coordinates
863     if ( rTextPaintInfo.GetTextFrame()->IsRightToLeft() )
864         rTextPaintInfo.GetTextFrame()->SwitchRTLtoLTR( rRect );
865     if ( rTextPaintInfo.GetTextFrame()->IsVertical() )
866         rTextPaintInfo.GetTextFrame()->SwitchVerticalToHorizontal( rRect );
867 
868     const SwFont* pOldFnt = rTextPaintInfo.GetFont();
869 
870     // Font is generated only once:
871     static SwFont s_aFnt = [&]()
872     {
873         SwFont tmp( *pOldFnt );
874         tmp.SetFamily( FAMILY_DONTKNOW, tmp.GetActual() );
875         tmp.SetName( numfunc::GetDefBulletFontname(), tmp.GetActual() );
876         tmp.SetStyleName(OUString(), tmp.GetActual());
877         tmp.SetCharSet( RTL_TEXTENCODING_SYMBOL, tmp.GetActual() );
878         return tmp;
879     }();
880 
881     // Some of the current values are set at the font:
882     if ( ! bRotate )
883         s_aFnt.SetVertical( 0_deg10, rTextPaintInfo.GetTextFrame()->IsVertical() );
884     else
885         s_aFnt.SetVertical( pOldFnt->GetOrientation() );
886 
887     s_aFnt.SetColor(rCol);
888 
889     Size aFontSize( 0, SPECIAL_FONT_HEIGHT );
890     s_aFnt.SetSize( aFontSize, s_aFnt.GetActual() );
891 
892     SwTextPaintInfo& rNonConstTextPaintInfo = const_cast<SwTextPaintInfo&>(rTextPaintInfo);
893 
894     rNonConstTextPaintInfo.SetFont( &s_aFnt );
895 
896     // The maximum width depends on the current orientation
897     const Degree10 nDir = s_aFnt.GetOrientation( rTextPaintInfo.GetTextFrame()->IsVertical() );
898     SwTwips nMaxWidth;
899     if (nDir == 900_deg10 || nDir == 2700_deg10)
900         nMaxWidth = rRect.Height();
901     else
902     {
903         assert(nDir == 0_deg10); //Unknown direction set at font
904         nMaxWidth = rRect.Width();
905     }
906 
907     // check if char fits into rectangle
908     const OUString aTmp( cChar );
909     aFontSize = rTextPaintInfo.GetTextSize( aTmp ).SvLSize();
910     while ( aFontSize.Width() > nMaxWidth )
911     {
912         SwTwips nFactor = ( 100 * aFontSize.Width() ) / nMaxWidth;
913         const SwTwips nOldWidth = aFontSize.Width();
914 
915         // new height for font
916         const SwFontScript nAct = s_aFnt.GetActual();
917         aFontSize.setHeight( ( 100 * s_aFnt.GetSize( nAct ).Height() ) / nFactor );
918         aFontSize.setWidth( ( 100 * s_aFnt.GetSize( nAct).Width() ) / nFactor );
919 
920         if ( !aFontSize.Width() && !aFontSize.Height() )
921             break;
922 
923         s_aFnt.SetSize( aFontSize, nAct );
924 
925         aFontSize = rTextPaintInfo.GetTextSize( aTmp ).SvLSize();
926 
927         if ( aFontSize.Width() >= nOldWidth )
928             break;
929     }
930 
931     const Point aOldPos( rTextPaintInfo.GetPos() );
932 
933     // adjust values so that tab is vertically and horizontally centered
934     SwTwips nX = rRect.Left();
935     SwTwips nY = rRect.Top();
936     switch ( nDir.get() )
937     {
938     case 0 :
939         if ( bCenter )
940             nX += ( rRect.Width() - aFontSize.Width() ) / 2;
941         nY += ( rRect.Height() - aFontSize.Height() ) / 2 + rTextPaintInfo.GetAscent();
942         break;
943     case 900 :
944         if ( bCenter )
945             nX += ( rRect.Width() - aFontSize.Height() ) / 2 + rTextPaintInfo.GetAscent();
946         nY += ( rRect.Height() + aFontSize.Width() ) / 2;
947         break;
948     case 2700 :
949         if ( bCenter )
950             nX += ( rRect.Width() + aFontSize.Height() ) / 2 - rTextPaintInfo.GetAscent();
951         nY += ( rRect.Height() - aFontSize.Width() ) / 2;
952         break;
953     }
954 
955     Point aTmpPos( nX, nY );
956     rNonConstTextPaintInfo.SetPos( aTmpPos );
957     sal_uInt16 nOldWidth = rPor.Width();
958     const_cast<SwLinePortion&>(rPor).Width( static_cast<sal_uInt16>(aFontSize.Width()) );
959     rTextPaintInfo.DrawText( aTmp, rPor );
960     const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
961     rNonConstTextPaintInfo.SetFont( const_cast<SwFont*>(pOldFnt) );
962     rNonConstTextPaintInfo.SetPos( aOldPos );
963 }
964 
DrawRect(const SwRect & rRect,bool bRetouche) const965 void SwTextPaintInfo::DrawRect( const SwRect &rRect, bool bRetouche ) const
966 {
967     if ( OnWin() || !bRetouche )
968     {
969         if( m_aTextFly.IsOn() )
970             const_cast<SwTextPaintInfo*>(this)->GetTextFly().
971                 DrawFlyRect( m_pOut, rRect );
972         else
973             m_pOut->DrawRect( rRect.SVRect() );
974     }
975 }
976 
DrawTab(const SwLinePortion & rPor) const977 void SwTextPaintInfo::DrawTab( const SwLinePortion &rPor ) const
978 {
979     if( !OnWin() )
980         return;
981 
982     SwRect aRect;
983     CalcRect( rPor, &aRect );
984 
985     if ( ! aRect.HasArea() )
986         return;
987 
988     const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ? CHAR_TAB_RTL : CHAR_TAB;
989     const sal_uInt8 nOptions = DRAW_SPECIAL_OPTIONS_CENTER | DRAW_SPECIAL_OPTIONS_ROTATE;
990 
991     lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
992 }
993 
DrawLineBreak(const SwLinePortion & rPor) const994 void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
995 {
996     if( !OnWin() )
997         return;
998 
999     sal_uInt16 nOldWidth = rPor.Width();
1000     const_cast<SwLinePortion&>(rPor).Width( LINE_BREAK_WIDTH );
1001 
1002     SwRect aRect;
1003     CalcRect( rPor, &aRect );
1004 
1005     if( aRect.HasArea() )
1006     {
1007         const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ?
1008                                   CHAR_LINEBREAK_RTL : CHAR_LINEBREAK;
1009         const sal_uInt8 nOptions = 0;
1010 
1011         lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
1012     }
1013 
1014     const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
1015 }
1016 
DrawRedArrow(const SwLinePortion & rPor) const1017 void SwTextPaintInfo::DrawRedArrow( const SwLinePortion &rPor ) const
1018 {
1019     Size aSize( SPECIAL_FONT_HEIGHT, SPECIAL_FONT_HEIGHT );
1020     SwRect aRect( static_cast<const SwArrowPortion&>(rPor).GetPos(), aSize );
1021     sal_Unicode cChar;
1022     if( static_cast<const SwArrowPortion&>(rPor).IsLeft() )
1023     {
1024         aRect.Pos().AdjustY(20 - GetAscent() );
1025         aRect.Pos().AdjustX(20 );
1026         if( aSize.Height() > rPor.Height() )
1027             aRect.Height( rPor.Height() );
1028         cChar = CHAR_LEFT_ARROW;
1029     }
1030     else
1031     {
1032         if( aSize.Height() > rPor.Height() )
1033             aRect.Height( rPor.Height() );
1034         aRect.Pos().AdjustY( -(aRect.Height() + 20) );
1035         aRect.Pos().AdjustX( -(aRect.Width() + 20) );
1036         cChar = CHAR_RIGHT_ARROW;
1037     }
1038 
1039     if ( GetTextFrame()->IsVertical() )
1040         GetTextFrame()->SwitchHorizontalToVertical( aRect );
1041 
1042     if( aRect.HasArea() )
1043     {
1044         const sal_uInt8 nOptions = 0;
1045         lcl_DrawSpecial( *this, rPor, aRect, COL_LIGHTRED, cChar, nOptions );
1046     }
1047 }
1048 
DrawPostIts(bool bScript) const1049 void SwTextPaintInfo::DrawPostIts( bool bScript ) const
1050 {
1051     if( !OnWin() || !m_pOpt->IsPostIts() )
1052         return;
1053 
1054     Size aSize;
1055     Point aTmp;
1056 
1057     const sal_uInt16 nPostItsWidth = SwViewOption::GetPostItsWidth( GetOut() );
1058     const sal_uInt16 nFontHeight = m_pFnt->GetHeight( m_pVsh, *GetOut() );
1059     const sal_uInt16 nFontAscent = m_pFnt->GetAscent( m_pVsh, *GetOut() );
1060 
1061     switch ( m_pFnt->GetOrientation( GetTextFrame()->IsVertical() ).get() )
1062     {
1063     case 0 :
1064         aSize.setWidth( nPostItsWidth );
1065         aSize.setHeight( nFontHeight );
1066         aTmp.setX( m_aPos.X() );
1067         aTmp.setY( m_aPos.Y() - nFontAscent );
1068         break;
1069     case 900 :
1070         aSize.setHeight( nPostItsWidth );
1071         aSize.setWidth( nFontHeight );
1072         aTmp.setX( m_aPos.X() - nFontAscent );
1073         aTmp.setY( m_aPos.Y() );
1074         break;
1075     case 2700 :
1076         aSize.setHeight( nPostItsWidth );
1077         aSize.setWidth( nFontHeight );
1078         aTmp.setX( m_aPos.X() - nFontHeight +
1079                               nFontAscent );
1080         aTmp.setY( m_aPos.Y() );
1081         break;
1082     }
1083 
1084     SwRect aTmpRect( aTmp, aSize );
1085 
1086     if ( GetTextFrame()->IsRightToLeft() )
1087         GetTextFrame()->SwitchLTRtoRTL( aTmpRect );
1088 
1089     if ( GetTextFrame()->IsVertical() )
1090         GetTextFrame()->SwitchHorizontalToVertical( aTmpRect );
1091 
1092     SwViewOption::PaintPostIts( const_cast<OutputDevice*>(GetOut()), aTmpRect, bScript );
1093 
1094 }
1095 
DrawCheckBox(const SwFieldFormCheckboxPortion & rPor,bool bChecked) const1096 void SwTextPaintInfo::DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) const
1097 {
1098     SwRect aIntersect;
1099     CalcRect( rPor, &aIntersect );
1100     if ( !aIntersect.HasArea() )
1101         return;
1102 
1103     if (OnWin() && SwViewOption::IsFieldShadings() &&
1104             !GetOpt().IsPagePreview())
1105     {
1106         OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
1107         pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1108         pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
1109         pOut->SetLineColor();
1110         pOut->DrawRect( aIntersect.SVRect() );
1111         pOut->Pop();
1112     }
1113     const int delta=10;
1114     tools::Rectangle r(aIntersect.Left()+delta, aIntersect.Top()+delta, aIntersect.Right()-delta, aIntersect.Bottom()-delta);
1115     m_pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1116     m_pOut->SetLineColor( Color(0, 0, 0));
1117     m_pOut->SetFillColor();
1118     m_pOut->DrawRect( r );
1119     if (bChecked)
1120     {
1121         m_pOut->DrawLine(r.TopLeft(), r.BottomRight());
1122         m_pOut->DrawLine(r.TopRight(), r.BottomLeft());
1123     }
1124     m_pOut->Pop();
1125 }
1126 
DrawBackground(const SwLinePortion & rPor,const Color * pColor) const1127 void SwTextPaintInfo::DrawBackground( const SwLinePortion &rPor, const Color *pColor ) const
1128 {
1129     OSL_ENSURE( OnWin(), "SwTextPaintInfo::DrawBackground: printer pollution ?" );
1130 
1131     SwRect aIntersect;
1132     CalcRect( rPor, nullptr, &aIntersect, true );
1133 
1134     if ( !aIntersect.HasArea() )
1135         return;
1136 
1137     OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
1138     pOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1139 
1140     // For dark background we do not want to have a filled rectangle
1141     if ( GetVsh() && GetVsh()->GetWin() && lcl_IsDarkBackground( *this ) )
1142     {
1143         pOut->SetLineColor( SwViewOption::GetFontColor() );
1144     }
1145     else
1146     {
1147         if ( pColor )
1148             pOut->SetFillColor( *pColor );
1149         else
1150             pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
1151 
1152         pOut->SetLineColor();
1153     }
1154 
1155     DrawRect( aIntersect, true );
1156     pOut->Pop();
1157 }
1158 
DrawBackBrush(const SwLinePortion & rPor) const1159 void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
1160 {
1161     {
1162         SwRect aIntersect;
1163         CalcRect( rPor, &aIntersect, nullptr, true );
1164         if(aIntersect.HasArea())
1165         {
1166             SwPosition const aPosition(m_pFrame->MapViewToModelPos(GetIdx()));
1167             const ::sw::mark::IMark* pFieldmark =
1168                 m_pFrame->GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
1169             bool bIsStartMark = (TextFrameIndex(1) == GetLen()
1170                     && CH_TXT_ATR_FIELDSTART == GetText()[sal_Int32(GetIdx())]);
1171             if(pFieldmark) {
1172                 SAL_INFO("sw.core", "Found Fieldmark " << pFieldmark->ToString());
1173             }
1174             if(bIsStartMark)
1175                 SAL_INFO("sw.core", "Found StartMark");
1176             if (OnWin() && (pFieldmark!=nullptr || bIsStartMark) &&
1177                     SwViewOption::IsFieldShadings() &&
1178                     !GetOpt().IsPagePreview())
1179             {
1180                 OutputDevice* pOutDev = const_cast<OutputDevice*>(GetOut());
1181                 pOutDev->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1182                 pOutDev->SetFillColor( SwViewOption::GetFieldShadingsColor() );
1183                 pOutDev->SetLineColor( );
1184                 pOutDev->DrawRect( aIntersect.SVRect() );
1185                 pOutDev->Pop();
1186             }
1187         }
1188     }
1189 
1190     SwRect aIntersect;
1191     CalcRect( rPor, nullptr, &aIntersect, true );
1192 
1193     if ( !aIntersect.HasArea() )
1194         return;
1195 
1196     OutputDevice* pTmpOut = const_cast<OutputDevice*>(GetOut());
1197 
1198     // #i16816# tagged pdf support
1199     SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pTmpOut );
1200 
1201     Color aFillColor;
1202 
1203     if( m_pFnt->GetHighlightColor() != COL_TRANSPARENT )
1204     {
1205         aFillColor = m_pFnt->GetHighlightColor();
1206     }
1207     else
1208     {
1209         if( !m_pFnt->GetBackColor() )
1210             return;
1211         aFillColor = *m_pFnt->GetBackColor();
1212     }
1213 
1214     // tdf#104349 do not highlight portions of space chars before end of line if the compatibility option is enabled
1215     // for LTR mode only
1216     if ( !GetTextFrame()->IsRightToLeft() )
1217     {
1218         if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
1219         {
1220             bool           draw = false;
1221             bool           full = false;
1222             SwLinePortion *pPos = const_cast<SwLinePortion *>(&rPor);
1223             TextFrameIndex nIdx = GetIdx();
1224             TextFrameIndex nLen;
1225 
1226             do
1227             {
1228                 nLen = pPos->GetLen();
1229                 for (TextFrameIndex i = nIdx; i < (nIdx + nLen); ++i)
1230                 {
1231                     if (i < TextFrameIndex(GetText().getLength())
1232                         && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
1233                     {
1234                         if ( i >= (GetIdx() + rPor.GetLen()) )
1235                         {
1236                             goto drawcontinue;
1237                         }
1238                     }
1239                     if (i >= TextFrameIndex(GetText().getLength())
1240                         || GetText()[sal_Int32(i)] != CH_BLANK)
1241                     {
1242                         draw = true;
1243                         if ( i >= (GetIdx() + rPor.GetLen()) )
1244                         {
1245                             full = true;
1246                             goto drawcontinue;
1247                         }
1248                     }
1249                 }
1250                 nIdx += nLen;
1251                 pPos = pPos->GetNextPortion();
1252             } while ( pPos );
1253 
1254         drawcontinue:
1255 
1256             if ( !draw )
1257                 return;
1258 
1259             if ( !full )
1260             {
1261                 pPos = const_cast<SwLinePortion *>(&rPor);
1262                 nIdx = GetIdx();
1263 
1264                 nLen = pPos->GetLen();
1265                 for (TextFrameIndex i = nIdx + nLen - TextFrameIndex(1);
1266                         i >= nIdx; --i)
1267                 {
1268                     if (i < TextFrameIndex(GetText().getLength())
1269                         && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
1270                     {
1271                         continue;
1272                     }
1273                     if (i >= TextFrameIndex(GetText().getLength())
1274                         || GetText()[sal_Int32(i)] != CH_BLANK)
1275                     {
1276                         sal_uInt16 nOldWidth = rPor.Width();
1277                         sal_uInt16 nNewWidth = GetTextSize(m_pOut, nullptr,
1278                             GetText(), nIdx, (i + TextFrameIndex(1) - nIdx)).Width();
1279 
1280                         const_cast<SwLinePortion&>(rPor).Width( nNewWidth );
1281                         CalcRect( rPor, nullptr, &aIntersect, true );
1282                         const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
1283 
1284                         if ( !aIntersect.HasArea() )
1285                         {
1286                             return;
1287                         }
1288 
1289                         break;
1290                     }
1291                 }
1292             }
1293         }
1294     }
1295 
1296     pTmpOut->Push( PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
1297 
1298     pTmpOut->SetFillColor(aFillColor);
1299     pTmpOut->SetLineColor();
1300 
1301     DrawRect( aIntersect, false );
1302 
1303     pTmpOut->Pop();
1304 }
1305 
DrawBorder(const SwLinePortion & rPor) const1306 void SwTextPaintInfo::DrawBorder( const SwLinePortion &rPor ) const
1307 {
1308     SwRect aDrawArea;
1309     CalcRect( rPor, &aDrawArea );
1310     if ( aDrawArea.HasArea() )
1311     {
1312         PaintCharacterBorder(*m_pFnt, aDrawArea, GetTextFrame()->IsVertical(),
1313                              GetTextFrame()->IsVertLRBT(), rPor.GetJoinBorderWithPrev(),
1314                              rPor.GetJoinBorderWithNext());
1315     }
1316 }
1317 
DrawViewOpt(const SwLinePortion & rPor,PortionType nWhich,const Color * pColor) const1318 void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor,
1319                                    PortionType nWhich, const Color *pColor ) const
1320 {
1321     if( !OnWin() || IsMulti() )
1322         return;
1323 
1324     bool bDraw = false;
1325     switch( nWhich )
1326     {
1327     case PortionType::Footnote:
1328     case PortionType::QuoVadis:
1329     case PortionType::Number:
1330     case PortionType::Field:
1331     case PortionType::Hidden:
1332     case PortionType::Tox:
1333     case PortionType::Ref:
1334     case PortionType::Meta:
1335     case PortionType::ControlChar:
1336         if ( !GetOpt().IsPagePreview()
1337              && !GetOpt().IsReadonly()
1338              && SwViewOption::IsFieldShadings()
1339              && ( PortionType::Number != nWhich
1340                   || m_pFrame->GetTextNodeForParaProps()->HasMarkedLabel())) // #i27615#
1341         {
1342             bDraw = PortionType::Footnote != nWhich || m_pFrame->IsFootnoteAllowed();
1343         }
1344         break;
1345     case PortionType::Bookmark:
1346         // no shading
1347         break;
1348     case PortionType::InputField:
1349         // input field shading also in read-only mode
1350         if ( !GetOpt().IsPagePreview()
1351              && SwViewOption::IsFieldShadings() )
1352         {
1353             bDraw = true;
1354         }
1355         break;
1356     case PortionType::Table:
1357         if ( GetOpt().IsTab() )     bDraw = true;
1358         break;
1359     case PortionType::SoftHyphen:
1360         if ( GetOpt().IsSoftHyph() )bDraw = true;
1361         break;
1362     case PortionType::Blank:
1363         if ( GetOpt().IsHardBlank())bDraw = true;
1364         break;
1365     default:
1366         {
1367             OSL_ENSURE( false, "SwTextPaintInfo::DrawViewOpt: don't know how to draw this" );
1368             break;
1369         }
1370     }
1371     if ( bDraw )
1372         DrawBackground( rPor, pColor );
1373 }
1374 
lcl_InitHyphValues(PropertyValues & rVals,sal_Int16 nMinLeading,sal_Int16 nMinTrailing,bool bNoCapsHyphenation)1375 static void lcl_InitHyphValues( PropertyValues &rVals,
1376             sal_Int16 nMinLeading, sal_Int16 nMinTrailing, bool bNoCapsHyphenation )
1377 {
1378     sal_Int32 nLen = rVals.getLength();
1379 
1380     if (0 == nLen)  // yet to be initialized?
1381     {
1382         rVals.realloc( 3 );
1383         PropertyValue *pVal = rVals.getArray();
1384 
1385         pVal[0].Name    = UPN_HYPH_MIN_LEADING;
1386         pVal[0].Handle  = UPH_HYPH_MIN_LEADING;
1387         pVal[0].Value   <<= nMinLeading;
1388 
1389         pVal[1].Name    = UPN_HYPH_MIN_TRAILING;
1390         pVal[1].Handle  = UPH_HYPH_MIN_TRAILING;
1391         pVal[1].Value   <<= nMinTrailing;
1392 
1393         pVal[2].Name    = UPN_HYPH_NO_CAPS;
1394         pVal[2].Handle  = UPH_HYPH_NO_CAPS;
1395         pVal[2].Value   <<= bNoCapsHyphenation;
1396     }
1397     else if (3 == nLen) // already initialized once?
1398     {
1399         PropertyValue *pVal = rVals.getArray();
1400         pVal[0].Value <<= nMinLeading;
1401         pVal[1].Value <<= nMinTrailing;
1402         pVal[2].Value <<= bNoCapsHyphenation;
1403     }
1404     else {
1405         OSL_FAIL( "unexpected size of sequence" );
1406     }
1407 }
1408 
GetHyphValues() const1409 const PropertyValues & SwTextFormatInfo::GetHyphValues() const
1410 {
1411     OSL_ENSURE( 3 == m_aHyphVals.getLength(),
1412             "hyphenation values not yet initialized" );
1413     return m_aHyphVals;
1414 }
1415 
InitHyph(const bool bAutoHyphen)1416 bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
1417 {
1418     const SwAttrSet& rAttrSet = GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet();
1419     SetHanging( rAttrSet.GetHangingPunctuation().GetValue() );
1420     SetScriptSpace( rAttrSet.GetScriptSpace().GetValue() );
1421     SetForbiddenChars( rAttrSet.GetForbiddenRule().GetValue() );
1422     const SvxHyphenZoneItem &rAttr = rAttrSet.GetHyphenZone();
1423     MaxHyph() = rAttr.GetMaxHyphens();
1424     const bool bAuto = bAutoHyphen || rAttr.IsHyphen();
1425     if( bAuto || m_bInterHyph )
1426     {
1427         const sal_Int16 nMinimalLeading  = std::max(rAttr.GetMinLead(), sal_uInt8(2));
1428         const sal_Int16 nMinimalTrailing = rAttr.GetMinTrail();
1429         const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation();
1430         lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing, bNoCapsHyphenation);
1431     }
1432     return bAuto;
1433 }
1434 
CtorInitTextFormatInfo(OutputDevice * pRenderContext,SwTextFrame * pNewFrame,const bool bNewInterHyph,const bool bNewQuick,const bool bTst)1435 void SwTextFormatInfo::CtorInitTextFormatInfo( OutputDevice* pRenderContext, SwTextFrame *pNewFrame, const bool bNewInterHyph,
1436                                 const bool bNewQuick, const bool bTst )
1437 {
1438     CtorInitTextPaintInfo( pRenderContext, pNewFrame, SwRect() );
1439 
1440     m_bQuick = bNewQuick;
1441     m_bInterHyph = bNewInterHyph;
1442 
1443     //! needs to be done in this order
1444     m_bAutoHyph = InitHyph();
1445 
1446     m_bIgnoreFly = false;
1447     m_bFakeLineStart = false;
1448     m_bShift = false;
1449     m_bDropInit = false;
1450     m_bTestFormat = bTst;
1451     m_nLeft = 0;
1452     m_nRight = 0;
1453     m_nFirst = 0;
1454     m_nRealWidth = 0;
1455     m_nForcedLeftMargin = 0;
1456     m_pRest = nullptr;
1457     m_nLineHeight = 0;
1458     m_nLineNetHeight = 0;
1459     SetLineStart(TextFrameIndex(0));
1460 
1461     SvtCTLOptions::TextNumerals const nTextNumerals(
1462             SW_MOD()->GetCTLOptions().GetCTLTextNumerals());
1463     // cannot cache for NUMERALS_CONTEXT because we need to know the string
1464     // for the whole paragraph now
1465     if (nTextNumerals != SvtCTLOptions::NUMERALS_CONTEXT)
1466     {
1467         // set digit mode to what will be used later to get same results
1468         SwDigitModeModifier const m(*m_pRef, LANGUAGE_NONE /*dummy*/);
1469         assert(m_pRef->GetDigitLanguage() != LANGUAGE_NONE);
1470         SetCachedVclData(OutputDevice::CreateTextLayoutCache(*m_pText));
1471     }
1472 
1473     Init();
1474 }
1475 
1476 /**
1477  * If the Hyphenator returns ERROR or the language is set to NOLANGUAGE
1478  * we do not hyphenate.
1479  * Else, we always hyphenate if we do interactive hyphenation.
1480  * If we do not do interactive hyphenation, we only hyphenate if ParaFormat is
1481  * set to automatic hyphenation.
1482  */
IsHyphenate() const1483 bool SwTextFormatInfo::IsHyphenate() const
1484 {
1485     if( !m_bInterHyph && !m_bAutoHyph )
1486         return false;
1487 
1488     LanguageType eTmp = GetFont()->GetLanguage();
1489     // TODO: check for more ideographic langs w/o hyphenation as a concept
1490     if ( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == eTmp || LANGUAGE_JAPANESE == eTmp )
1491         return false;
1492 
1493     uno::Reference< XHyphenator > xHyph = ::GetHyphenator();
1494     if (!xHyph.is())
1495         return false;
1496 
1497     if (m_bInterHyph)
1498         SvxSpellWrapper::CheckHyphLang( xHyph, eTmp );
1499 
1500     if (!xHyph->hasLocale(g_pBreakIt->GetLocale(eTmp)))
1501     {
1502         SfxObjectShell* pShell = m_pFrame->GetDoc().GetDocShell();
1503         if (pShell)
1504         {
1505             pShell->AppendInfoBarWhenReady(
1506                 "hyphenationmissing", SwResId(STR_HYPH_MISSING),
1507                 SwResId(STR_HYPH_MISSING_DETAIL)
1508                     .replaceFirst("%1", LanguageTag::convertToBcp47( g_pBreakIt->GetLocale(eTmp))),
1509                 InfobarType::WARNING);
1510         }
1511     }
1512 
1513     return xHyph->hasLocale( g_pBreakIt->GetLocale(eTmp) );
1514 }
1515 
GetDropFormat() const1516 const SwFormatDrop *SwTextFormatInfo::GetDropFormat() const
1517 {
1518     const SwFormatDrop *pDrop = &GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetDrop();
1519     if( 1 >= pDrop->GetLines() ||
1520         ( !pDrop->GetChars() && !pDrop->GetWholeWord() ) )
1521         pDrop = nullptr;
1522     return pDrop;
1523 }
1524 
Init()1525 void SwTextFormatInfo::Init()
1526 {
1527     // Not initialized: pRest, nLeft, nRight, nFirst, nRealWidth
1528     X(0);
1529     m_bArrowDone = m_bFull = m_bFootnoteDone = m_bErgoDone = m_bNumDone = m_bNoEndHyph =
1530         m_bNoMidHyph = m_bStop = m_bNewLine = m_bUnderflow = m_bTabOverflow = false;
1531 
1532     // generally we do not allow number portions in follows, except...
1533     if ( GetTextFrame()->IsFollow() )
1534     {
1535         const SwTextFrame* pMaster = GetTextFrame()->FindMaster();
1536         OSL_ENSURE(pMaster, "pTextFrame without Master");
1537         const SwLinePortion* pTmpPara = pMaster ? pMaster->GetPara() : nullptr;
1538 
1539         // there is a master for this follow and the master does not have
1540         // any contents (especially it does not have a number portion)
1541         m_bNumDone = ! pTmpPara ||
1542                    ! static_cast<const SwParaPortion*>(pTmpPara)->GetFirstPortion()->IsFlyPortion();
1543     }
1544 
1545     m_pRoot = nullptr;
1546     m_pLast = nullptr;
1547     m_pFly = nullptr;
1548     m_pLastTab = nullptr;
1549     m_pUnderflow = nullptr;
1550     m_cTabDecimal = 0;
1551     m_nWidth = m_nRealWidth;
1552     m_nForcedLeftMargin = 0;
1553     m_nSoftHyphPos = TextFrameIndex(0);
1554     m_nUnderScorePos = TextFrameIndex(COMPLETE_STRING);
1555     m_nLastBookmarkPos = TextFrameIndex(-1);
1556     m_cHookChar = 0;
1557     SetIdx(TextFrameIndex(0));
1558     SetLen(TextFrameIndex(GetText().getLength()));
1559     SetPaintOfst(0);
1560 }
1561 
SwTextFormatInfo(OutputDevice * pRenderContext,SwTextFrame * pFrame,const bool bInterHyphL,const bool bQuickL,const bool bTst)1562 SwTextFormatInfo::SwTextFormatInfo(OutputDevice* pRenderContext, SwTextFrame *pFrame, const bool bInterHyphL,
1563                                    const bool bQuickL, const bool bTst)
1564 {
1565     CtorInitTextFormatInfo(pRenderContext, pFrame, bInterHyphL, bQuickL, bTst);
1566 }
1567 
1568 /**
1569  * There are a few differences between a copy constructor
1570  * and the following constructor for multi-line formatting.
1571  * The root is the first line inside the multi-portion,
1572  * the line start is the actual position in the text,
1573  * the line width is the rest width from the surrounding line
1574  * and the bMulti and bFirstMulti-flag has to be set correctly.
1575  */
SwTextFormatInfo(const SwTextFormatInfo & rInf,SwLineLayout & rLay,SwTwips nActWidth)1576 SwTextFormatInfo::SwTextFormatInfo( const SwTextFormatInfo& rInf,
1577     SwLineLayout& rLay, SwTwips nActWidth ) :
1578     SwTextPaintInfo( rInf ),
1579     m_pRoot(&rLay),
1580     m_pLast(&rLay),
1581     m_pFly(nullptr),
1582     m_pUnderflow(nullptr),
1583     m_pRest(nullptr),
1584     m_pLastTab(nullptr),
1585     m_nSoftHyphPos(TextFrameIndex(0)),
1586     m_nLineStart(rInf.GetIdx()),
1587     m_nUnderScorePos(TextFrameIndex(COMPLETE_STRING)),
1588     m_nLeft(rInf.m_nLeft),
1589     m_nRight(rInf.m_nRight),
1590     m_nFirst(rInf.m_nLeft),
1591     m_nRealWidth(sal_uInt16(nActWidth)),
1592     m_nWidth(m_nRealWidth),
1593     m_nLineHeight(0),
1594     m_nLineNetHeight(0),
1595     m_nForcedLeftMargin(0),
1596     m_bFull(false),
1597     m_bFootnoteDone(true),
1598     m_bErgoDone(true),
1599     m_bNumDone(true),
1600     m_bArrowDone(true),
1601     m_bStop(false),
1602     m_bNewLine(true),
1603     m_bShift(false),
1604     m_bUnderflow(false),
1605     m_bInterHyph(false),
1606     m_bAutoHyph(false),
1607     m_bDropInit(false),
1608     m_bQuick(rInf.m_bQuick),
1609     m_bNoEndHyph(false),
1610     m_bNoMidHyph(false),
1611     m_bIgnoreFly(false),
1612     m_bFakeLineStart(false),
1613     m_bTabOverflow( false ),
1614     m_bTestFormat(rInf.m_bTestFormat),
1615     m_cTabDecimal(0),
1616     m_cHookChar(0),
1617     m_nMaxHyph(0)
1618 {
1619     SetMulti( true );
1620     SetFirstMulti( rInf.IsFirstMulti() );
1621 }
1622 
CheckFootnotePortion_(SwLineLayout const * pCurr)1623 bool SwTextFormatInfo::CheckFootnotePortion_( SwLineLayout const * pCurr )
1624 {
1625     const sal_uInt16 nHeight = pCurr->GetRealHeight();
1626     for( SwLinePortion *pPor = pCurr->GetNextPortion(); pPor; pPor = pPor->GetNextPortion() )
1627     {
1628         if( pPor->IsFootnotePortion() && nHeight > static_cast<SwFootnotePortion*>(pPor)->Orig() )
1629         {
1630             SetLineHeight( nHeight );
1631             SetLineNetHeight( pCurr->Height() );
1632             return true;
1633         }
1634     }
1635     return false;
1636 }
1637 
ScanPortionEnd(TextFrameIndex const nStart,TextFrameIndex const nEnd)1638 TextFrameIndex SwTextFormatInfo::ScanPortionEnd(TextFrameIndex const nStart,
1639                                                 TextFrameIndex const nEnd)
1640 {
1641     m_cHookChar = 0;
1642     TextFrameIndex i = nStart;
1643 
1644     // Used for decimal tab handling:
1645     const sal_Unicode cTabDec = GetLastTab() ? GetTabDecimal() : 0;
1646     const sal_Unicode cThousandSep  = ',' == cTabDec ? '.' : ',';
1647 
1648     // #i45951# German (Switzerland) uses ' as thousand separator
1649     const sal_Unicode cThousandSep2 = ',' == cTabDec ? '.' : '\'';
1650 
1651     bool bNumFound = false;
1652     const bool bTabCompat = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
1653 
1654     for( ; i < nEnd; ++i )
1655     {
1656         const sal_Unicode cPos = GetChar( i );
1657         switch( cPos )
1658         {
1659         case CH_TXTATR_BREAKWORD:
1660         case CH_TXTATR_INWORD:
1661             if( !HasHint( i ))
1662                 break;
1663             [[fallthrough]];
1664 
1665         case CHAR_SOFTHYPHEN:
1666         case CHAR_HARDHYPHEN:
1667         case CHAR_HARDBLANK:
1668         case CH_TAB:
1669         case CH_BREAK:
1670         case CHAR_ZWSP :
1671         case CHAR_WJ :
1672             m_cHookChar = cPos;
1673             return i;
1674 
1675         case CHAR_UNDERSCORE:
1676             if (TextFrameIndex(COMPLETE_STRING) == m_nUnderScorePos)
1677                 m_nUnderScorePos = i;
1678             break;
1679 
1680         default:
1681             if ( cTabDec )
1682             {
1683                 if( cTabDec == cPos )
1684                 {
1685                     OSL_ENSURE( cPos, "Unexpected end of string" );
1686                     if( cPos ) // robust
1687                     {
1688                         m_cHookChar = cPos;
1689                         return i;
1690                     }
1691                 }
1692 
1693                 // Compatibility: First non-digit character behind a
1694                 // a digit character becomes the hook character
1695                 if ( bTabCompat )
1696                 {
1697                     if ( ( 0x2F < cPos && cPos < 0x3A ) ||
1698                          ( bNumFound && ( cPos == cThousandSep || cPos == cThousandSep2 ) ) )
1699                     {
1700                         bNumFound = true;
1701                     }
1702                     else
1703                     {
1704                         if ( bNumFound )
1705                         {
1706                             m_cHookChar = cPos;
1707                             SetTabDecimal( cPos );
1708                             return i;
1709                         }
1710                     }
1711                 }
1712             }
1713         }
1714     }
1715 
1716     // Check if character *behind* the portion has
1717     // to become the hook:
1718     if (i == nEnd && i < TextFrameIndex(GetText().getLength()) && bNumFound)
1719     {
1720         const sal_Unicode cPos = GetChar( i );
1721         if ( cPos != cTabDec && cPos != cThousandSep && cPos !=cThousandSep2 && ( 0x2F >= cPos || cPos >= 0x3A ) )
1722         {
1723             m_cHookChar = GetChar( i );
1724             SetTabDecimal( m_cHookChar );
1725         }
1726     }
1727 
1728     return i;
1729 }
1730 
LastKernPortion()1731 bool SwTextFormatInfo::LastKernPortion()
1732 {
1733     if( GetLast() )
1734     {
1735         if( GetLast()->IsKernPortion() )
1736             return true;
1737         if( GetLast()->Width() || ( GetLast()->GetLen() &&
1738             !GetLast()->IsHolePortion() ) )
1739             return false;
1740     }
1741     SwLinePortion* pPor = GetRoot();
1742     SwLinePortion *pKern = nullptr;
1743     while( pPor )
1744     {
1745         if( pPor->IsKernPortion() )
1746             pKern = pPor;
1747         else if( pPor->Width() || ( pPor->GetLen() && !pPor->IsHolePortion() ) )
1748             pKern = nullptr;
1749         pPor = pPor->GetNextPortion();
1750     }
1751     if( pKern )
1752     {
1753         SetLast( pKern );
1754         return true;
1755     }
1756     return false;
1757 }
1758 
GetLineWidth()1759 SwTwips SwTextFormatInfo::GetLineWidth()
1760 {
1761     SwTwips nLineWidth = Width() - X();
1762 
1763     const bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
1764         DocumentSettingId::TAB_OVER_MARGIN);
1765     const bool bTabOverSpacing = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
1766         DocumentSettingId::TAB_OVER_SPACING);
1767     if (!bTabOverMargin && !bTabOverSpacing)
1768         return nLineWidth;
1769 
1770     SwTabPortion* pLastTab = GetLastTab();
1771     if (!pLastTab)
1772         return nLineWidth;
1773 
1774     // Consider tab portions over the printing bounds of the text frame.
1775     if (pLastTab->GetTabPos() <= Width())
1776         return nLineWidth;
1777 
1778     // Calculate the width that starts at the left (or in case of first line:
1779     // first) margin, but ends after the right paragraph margin:
1780     //
1781     // +--------------------+
1782     // |LL|              |RR|
1783     // +--------------------+
1784     // ^ m_nLeftMargin (absolute)
1785     //    ^ nLeftMarginWidth (relative to m_nLeftMargin), X() is relative to this
1786     //                   ^ right margin
1787     //                      ^ paragraph right
1788     // <--------------------> is GetTextFrame()->getFrameArea().Width()
1789     //    <-------------->    is Width()
1790     //    <-----------------> is what we need to be able to compare to X() (nTextFrameWidth)
1791     SwTwips nLeftMarginWidth = m_nLeftMargin - GetTextFrame()->getFrameArea().Left();
1792     SwTwips nTextFrameWidth = GetTextFrame()->getFrameArea().Width() - nLeftMarginWidth;
1793 
1794     // If there is one such tab portion, then text is allowed to use the full
1795     // text frame area to the right (RR above, but not LL).
1796     nLineWidth = nTextFrameWidth - X();
1797 
1798     if (!bTabOverMargin) // thus bTabOverSpacing only
1799     {
1800         // right, center, decimal can back-fill all the available space - same as TabOverMargin
1801         if (pLastTab->GetWhichPor() == PortionType::TabLeft)
1802             nLineWidth = nTextFrameWidth - pLastTab->GetTabPos();
1803     }
1804     return nLineWidth;
1805 }
1806 
SwTextSlot(const SwTextSizeInfo * pNew,const SwLinePortion * pPor,bool bTextLen,bool bExgLists,OUString const & rCh)1807 SwTextSlot::SwTextSlot(
1808     const SwTextSizeInfo *pNew,
1809     const SwLinePortion *pPor,
1810     bool bTextLen,
1811     bool bExgLists,
1812     OUString const & rCh )
1813     : pOldText(nullptr)
1814     , m_pOldSmartTagList(nullptr)
1815     , m_pOldGrammarCheckList(nullptr)
1816     , nIdx(0)
1817     , nLen(0)
1818     , pInf(nullptr)
1819 {
1820     if( rCh.isEmpty() )
1821     {
1822         bOn = pPor->GetExpText( *pNew, aText );
1823     }
1824     else
1825     {
1826         aText = rCh;
1827         bOn = true;
1828     }
1829 
1830     // The text is replaced ...
1831     if( !bOn )
1832         return;
1833 
1834     pInf = const_cast<SwTextSizeInfo*>(pNew);
1835     nIdx = pInf->GetIdx();
1836     nLen = pInf->GetLen();
1837     pOldText = &(pInf->GetText());
1838     m_pOldCachedVclData = pInf->GetCachedVclData();
1839     pInf->SetText( aText );
1840     pInf->SetIdx(TextFrameIndex(0));
1841     pInf->SetLen(bTextLen ? TextFrameIndex(pInf->GetText().getLength()) : pPor->GetLen());
1842     pInf->SetCachedVclData(nullptr);
1843 
1844     // ST2
1845     if ( !bExgLists )
1846         return;
1847 
1848     m_pOldSmartTagList = static_cast<SwTextPaintInfo*>(pInf)->GetSmartTags();
1849     if (m_pOldSmartTagList)
1850     {
1851         std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
1852         SwWrongList const*const pSmartTags(pos.first->GetSmartTags());
1853         if (pSmartTags)
1854         {
1855             const sal_uInt16 nPos = pSmartTags->GetWrongPos(pos.second);
1856             const sal_Int32 nListPos = pSmartTags->Pos(nPos);
1857             if (nListPos == pos.second && pSmartTags->SubList(nPos) != nullptr)
1858             {
1859                 m_pTempIter.reset(new sw::WrongListIterator(*pSmartTags->SubList(nPos)));
1860                 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
1861             }
1862             else if (!m_pTempList && nPos < pSmartTags->Count()
1863                         && nListPos < pos.second && !aText.isEmpty())
1864             {
1865                 m_pTempList.reset(new SwWrongList( WRONGLIST_SMARTTAG ));
1866                 m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
1867                 m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
1868                 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
1869             }
1870             else
1871                 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
1872         }
1873         else
1874             static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
1875     }
1876     m_pOldGrammarCheckList = static_cast<SwTextPaintInfo*>(pInf)->GetGrammarCheckList();
1877     if (!m_pOldGrammarCheckList)
1878         return;
1879 
1880     std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
1881     SwWrongList const*const pGrammar(pos.first->GetGrammarCheck());
1882     if (pGrammar)
1883     {
1884         const sal_uInt16 nPos = pGrammar->GetWrongPos(pos.second);
1885         const sal_Int32 nListPos = pGrammar->Pos(nPos);
1886         if (nListPos == pos.second && pGrammar->SubList(nPos) != nullptr)
1887         {
1888             m_pTempIter.reset(new sw::WrongListIterator(*pGrammar->SubList(nPos)));
1889             static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
1890         }
1891         else if (!m_pTempList && nPos < pGrammar->Count()
1892                     && nListPos < pos.second && !aText.isEmpty())
1893         {
1894             m_pTempList.reset(new SwWrongList( WRONGLIST_GRAMMAR ));
1895             m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
1896             m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
1897             static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
1898         }
1899         else
1900             static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
1901     }
1902     else
1903         static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
1904 }
1905 
~SwTextSlot()1906 SwTextSlot::~SwTextSlot()
1907 {
1908     if( !bOn )
1909         return;
1910 
1911     pInf->SetCachedVclData(m_pOldCachedVclData);
1912     pInf->SetText( *pOldText );
1913     pInf->SetIdx( nIdx );
1914     pInf->SetLen( nLen );
1915 
1916     // ST2
1917     // Restore old smart tag list
1918     if (m_pOldSmartTagList)
1919         static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pOldSmartTagList);
1920     if (m_pOldGrammarCheckList)
1921         static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pOldGrammarCheckList);
1922 }
1923 
SwFontSave(const SwTextSizeInfo & rInf,SwFont * pNew,SwAttrIter * pItr)1924 SwFontSave::SwFontSave(const SwTextSizeInfo &rInf, SwFont *pNew,
1925         SwAttrIter* pItr)
1926     : pInf(nullptr)
1927     , pFnt(pNew ? const_cast<SwTextSizeInfo&>(rInf).GetFont() : nullptr)
1928     , pIter(nullptr)
1929 {
1930     if( !pFnt )
1931         return;
1932 
1933     pInf = &const_cast<SwTextSizeInfo&>(rInf);
1934     // In these cases we temporarily switch to the new font:
1935     // 1. the fonts have a different magic number
1936     // 2. they have different script types
1937     // 3. their background colors differ (this is not covered by 1.)
1938     if( pFnt->DifferentFontCacheId( pNew, pFnt->GetActual() ) ||
1939         pNew->GetActual() != pFnt->GetActual() ||
1940         ( ! pNew->GetBackColor() && pFnt->GetBackColor() ) ||
1941         ( pNew->GetBackColor() && ! pFnt->GetBackColor() ) ||
1942         ( pNew->GetBackColor() && pFnt->GetBackColor() &&
1943           ( *pNew->GetBackColor() != *pFnt->GetBackColor() ) ) )
1944     {
1945         pNew->SetTransparent( true );
1946         pNew->SetAlign( ALIGN_BASELINE );
1947         pInf->SetFont( pNew );
1948     }
1949     else
1950         pFnt = nullptr;
1951     pNew->Invalidate();
1952     pNew->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );
1953     if( pItr && pItr->GetFnt() == pFnt )
1954     {
1955         pIter = pItr;
1956         pIter->SetFnt( pNew );
1957     }
1958 }
1959 
~SwFontSave()1960 SwFontSave::~SwFontSave()
1961 {
1962     if( pFnt )
1963     {
1964         // Reset SwFont
1965         pFnt->Invalidate();
1966         pInf->SetFont( pFnt );
1967         if( pIter )
1968         {
1969             pIter->SetFnt( pFnt );
1970             pIter->m_nPosition = COMPLETE_STRING;
1971         }
1972     }
1973 }
1974 
ChgHyph(const bool bNew)1975 bool SwTextFormatInfo::ChgHyph( const bool bNew )
1976 {
1977     const bool bOld = m_bAutoHyph;
1978     if( m_bAutoHyph != bNew )
1979     {
1980         m_bAutoHyph = bNew;
1981         InitHyph( bNew );
1982         // Set language in the Hyphenator
1983         if( m_pFnt )
1984             m_pFnt->ChgPhysFnt( m_pVsh, *m_pOut );
1985     }
1986     return bOld;
1987 }
1988 
1989 
CheckCurrentPosBookmark()1990 bool SwTextFormatInfo::CheckCurrentPosBookmark()
1991 {
1992     if (m_nLastBookmarkPos != GetIdx())
1993     {
1994         m_nLastBookmarkPos = GetIdx();
1995         return true;
1996     }
1997     else
1998     {
1999         return false;
2000     }
2001 }
2002 
2003 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2004