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