android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / inftxt.cxx
blob32b8b2609df03e6d0dc1c499463443200c6a4e75
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/linguistic2/XHyphenator.hpp>
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/splwrap.hxx>
33 #include <editeng/pgrditem.hxx>
34 #include <editeng/tstpitem.hxx>
35 #include <editeng/shaditem.hxx>
37 #include <SwSmartTagMgr.hxx>
38 #include <breakit.hxx>
39 #include <editeng/forbiddenruleitem.hxx>
40 #include <swmodule.hxx>
41 #include <vcl/svapp.hxx>
42 #include <viewsh.hxx>
43 #include <viewopt.hxx>
44 #include <frmtool.hxx>
45 #include <IDocumentSettingAccess.hxx>
46 #include <IDocumentDeviceAccess.hxx>
47 #include <IDocumentMarkAccess.hxx>
48 #include <paratr.hxx>
49 #include <rootfrm.hxx>
50 #include "inftxt.hxx"
51 #include <noteurl.hxx>
52 #include "porftn.hxx"
53 #include "porrst.hxx"
54 #include "itratr.hxx"
55 #include "portab.hxx"
56 #include <wrong.hxx>
57 #include <doc.hxx>
58 #include <pam.hxx>
59 #include <numrule.hxx>
60 #include <EnhancedPDFExportHelper.hxx>
61 #include <docsh.hxx>
62 #include <strings.hrc>
63 #include <o3tl/deleter.hxx>
64 #include <vcl/gdimtf.hxx>
65 #include <vcl/virdev.hxx>
66 #include <vcl/gradient.hxx>
67 #include <i18nlangtag/mslangid.hxx>
68 #include <formatlinebreak.hxx>
70 #include <view.hxx>
71 #include <wrtsh.hxx>
72 #include <com/sun/star/text/XTextRange.hpp>
73 #include <unotextrange.hxx>
74 #include <SwStyleNameMapper.hxx>
75 #include <unoprnms.hxx>
76 #include <editeng/unoprnms.hxx>
77 #include <unomap.hxx>
78 #include <com/sun/star/awt/FontSlant.hpp>
80 using namespace ::com::sun::star;
81 using namespace ::com::sun::star::linguistic2;
82 using namespace ::com::sun::star::uno;
83 using namespace ::com::sun::star::beans;
85 #define CHAR_UNDERSCORE u'_'
86 #define CHAR_LEFT_ARROW u'\x25C0'
87 #define CHAR_RIGHT_ARROW u'\x25B6'
88 #define CHAR_TAB u'\x2192'
89 #define CHAR_TAB_RTL u'\x2190'
90 #define CHAR_LINEBREAK u'\x21B5'
91 #define CHAR_LINEBREAK_RTL u'\x21B3'
93 #define DRAW_SPECIAL_OPTIONS_CENTER 1
94 #define DRAW_SPECIAL_OPTIONS_ROTATE 2
96 SwLineInfo::SwLineInfo()
97 : m_pSpace( nullptr ),
98 m_nVertAlign( SvxParaVertAlignItem::Align::Automatic ),
99 m_nDefTabStop( 0 ),
100 m_bListTabStopIncluded( false ),
101 m_nListTabStopPosition( 0 )
105 SwLineInfo::~SwLineInfo()
109 void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
110 const SwTextNode& rTextNode )
112 m_oRuler.emplace( rAttrSet.GetTabStops() );
113 if ( rTextNode.GetListTabStopPosition( m_nListTabStopPosition ) )
115 m_bListTabStopIncluded = true;
117 // insert the list tab stop into SvxTabItem instance <pRuler>
118 const SvxTabStop aListTabStop( m_nListTabStopPosition,
119 SvxTabAdjust::Left );
120 m_oRuler->Insert( aListTabStop );
122 // remove default tab stops, which are before the inserted list tab stop
123 for ( sal_uInt16 i = 0; i < m_oRuler->Count(); i++ )
125 if ( (*m_oRuler)[i].GetTabPos() < m_nListTabStopPosition &&
126 (*m_oRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
128 m_oRuler->Remove(i);
129 continue;
134 if ( !rTextNode.getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
136 // remove default tab stop at position 0
137 for ( sal_uInt16 i = 0; i < m_oRuler->Count(); i++ )
139 if ( (*m_oRuler)[i].GetTabPos() == 0 &&
140 (*m_oRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
142 m_oRuler->Remove(i);
143 break;
148 m_pSpace = &rAttrSet.GetLineSpacing();
149 m_nVertAlign = rAttrSet.GetParaVertAlign().GetValue();
150 m_nDefTabStop = USHRT_MAX;
153 void SwTextInfo::CtorInitTextInfo( SwTextFrame *pFrame )
155 m_pPara = pFrame->GetPara();
156 m_nTextStart = pFrame->GetOffset();
157 if (!m_pPara)
159 SAL_WARN("sw.core", "+SwTextInfo::CTOR: missing paragraph information");
160 pFrame->Format(pFrame->getRootFrame()->GetCurrShell()->GetOut());
161 m_pPara = pFrame->GetPara();
165 SwTextInfo::SwTextInfo( const SwTextInfo &rInf )
166 : m_pPara( const_cast<SwTextInfo&>(rInf).GetParaPortion() )
167 , m_nTextStart( rInf.GetTextStart() )
170 #if OSL_DEBUG_LEVEL > 0
172 static void ChkOutDev( const SwTextSizeInfo &rInf )
174 if ( !rInf.GetVsh() )
175 return;
177 const OutputDevice* pOut = rInf.GetOut();
178 const OutputDevice* pRef = rInf.GetRefDev();
179 OSL_ENSURE( pOut && pRef, "ChkOutDev: invalid output devices" );
181 #endif
183 static TextFrameIndex GetMinLen( const SwTextSizeInfo &rInf )
185 const TextFrameIndex nTextLen(rInf.GetText().getLength());
186 if (rInf.GetLen() == TextFrameIndex(COMPLETE_STRING))
187 return nTextLen;
188 const TextFrameIndex nInfLen = rInf.GetIdx() + rInf.GetLen();
189 return std::min(nTextLen, nInfLen);
192 SwTextSizeInfo::SwTextSizeInfo()
193 : m_pKanaComp(nullptr)
194 , m_pVsh(nullptr)
195 , m_pOut(nullptr)
196 , m_pRef(nullptr)
197 , m_pFnt(nullptr)
198 , m_pUnderFnt(nullptr)
199 , m_pFrame(nullptr)
200 , m_pOpt(nullptr)
201 , m_pText(nullptr)
202 , m_nIdx(0)
203 , m_nLen(0)
204 , m_nMeasureLen(COMPLETE_STRING)
205 , m_nKanaIdx(0)
206 , m_bOnWin (false)
207 , m_bNotEOL (false)
208 , m_bURLNotify(false)
209 , m_bStopUnderflow(false)
210 , m_bFootnoteInside(false)
211 , m_bOtherThanFootnoteInside(false)
212 , m_bMulti(false)
213 , m_bFirstMulti(false)
214 , m_bRuby(false)
215 , m_bHanging(false)
216 , m_bScriptSpace(false)
217 , m_bForbiddenChars(false)
218 , m_bSnapToGrid(false)
219 , m_nDirection(0)
222 SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
223 : SwTextInfo( rNew ),
224 m_pKanaComp(rNew.GetpKanaComp()),
225 m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
226 m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
227 m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
228 m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
229 m_pUnderFnt(rNew.GetUnderFnt()),
230 m_pFrame(rNew.m_pFrame),
231 m_pOpt(&rNew.GetOpt()),
232 m_pText(&rNew.GetText()),
233 m_nIdx(rNew.GetIdx()),
234 m_nLen(rNew.GetLen()),
235 m_nMeasureLen(rNew.GetMeasureLen()),
236 m_nKanaIdx( rNew.GetKanaIdx() ),
237 m_bOnWin( rNew.OnWin() ),
238 m_bNotEOL( rNew.NotEOL() ),
239 m_bURLNotify( rNew.URLNotify() ),
240 m_bStopUnderflow( rNew.StopUnderflow() ),
241 m_bFootnoteInside( rNew.IsFootnoteInside() ),
242 m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
243 m_bMulti( rNew.IsMulti() ),
244 m_bFirstMulti( rNew.IsFirstMulti() ),
245 m_bRuby( rNew.IsRuby() ),
246 m_bHanging( rNew.IsHanging() ),
247 m_bScriptSpace( rNew.HasScriptSpace() ),
248 m_bForbiddenChars( rNew.HasForbiddenChars() ),
249 m_bSnapToGrid( rNew.SnapToGrid() ),
250 m_nDirection( rNew.GetDirection() )
252 #if OSL_DEBUG_LEVEL > 0
253 ChkOutDev( *this );
254 #endif
257 void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame,
258 TextFrameIndex const nNewIdx)
260 m_pKanaComp = nullptr;
261 m_nKanaIdx = 0;
262 m_pFrame = pFrame;
263 CtorInitTextInfo( m_pFrame );
264 SwDoc const& rDoc(m_pFrame->GetDoc());
265 m_pVsh = m_pFrame->getRootFrame()->GetCurrShell();
267 // Get the output and reference device
268 if ( m_pVsh )
270 m_pOut = pRenderContext;
271 m_pRef = &m_pVsh->GetRefDev();
272 m_bOnWin = m_pVsh->GetWin() || OUTDEV_WINDOW == m_pOut->GetOutDevType() || m_pVsh->isOutputToWindow();
274 else
276 // Access via StarONE. We do not need a Shell or an active one.
277 if (rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE))
279 // We can only pick the AppWin here? (there's nothing better to pick?)
280 m_pOut = Application::GetDefaultDevice();
282 else
283 m_pOut = rDoc.getIDocumentDeviceAccess().getPrinter(false);
285 m_pRef = m_pOut;
288 #if OSL_DEBUG_LEVEL > 0
289 ChkOutDev( *this );
290 #endif
292 // Set default layout mode ( LTR or RTL ).
293 if ( m_pFrame->IsRightToLeft() )
295 m_pOut->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
296 m_pRef->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
297 m_nDirection = DIR_RIGHT2LEFT;
299 else
301 m_pOut->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong );
302 m_pRef->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong );
303 m_nDirection = DIR_LEFT2RIGHT;
306 // The Options
308 m_pOpt = m_pVsh ?
309 m_pVsh->GetViewOptions() :
310 SW_MOD()->GetViewOption(rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)); // Options from Module, due to StarONE
312 // bURLNotify is set if MakeGraphic prepares it
313 // TODO: Unwind
314 m_bURLNotify = pNoteURL && !m_bOnWin;
316 SetSnapToGrid( m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue() &&
317 m_pFrame->IsInDocBody() );
319 m_pFnt = nullptr;
320 m_pUnderFnt = nullptr;
321 m_pText = &m_pFrame->GetText();
323 m_nIdx = nNewIdx;
324 m_nLen = m_nMeasureLen = TextFrameIndex(COMPLETE_STRING);
325 m_bNotEOL = false;
326 m_bStopUnderflow = m_bFootnoteInside = m_bOtherThanFootnoteInside = false;
327 m_bMulti = m_bFirstMulti = m_bRuby = m_bHanging = m_bScriptSpace =
328 m_bForbiddenChars = false;
330 SetLen( GetMinLen( *this ) );
333 SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew, const OUString* pText,
334 TextFrameIndex const nIndex)
335 : SwTextInfo( rNew ),
336 m_pKanaComp(rNew.GetpKanaComp()),
337 m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
338 m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
339 m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
340 m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
341 m_pUnderFnt(rNew.GetUnderFnt()),
342 m_pFrame( rNew.m_pFrame ),
343 m_pOpt(&rNew.GetOpt()),
344 m_pText(pText),
345 m_nIdx(nIndex),
346 m_nLen(COMPLETE_STRING),
347 m_nMeasureLen(COMPLETE_STRING),
348 m_nKanaIdx( rNew.GetKanaIdx() ),
349 m_bOnWin( rNew.OnWin() ),
350 m_bNotEOL( rNew.NotEOL() ),
351 m_bURLNotify( rNew.URLNotify() ),
352 m_bStopUnderflow( rNew.StopUnderflow() ),
353 m_bFootnoteInside( rNew.IsFootnoteInside() ),
354 m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
355 m_bMulti( rNew.IsMulti() ),
356 m_bFirstMulti( rNew.IsFirstMulti() ),
357 m_bRuby( rNew.IsRuby() ),
358 m_bHanging( rNew.IsHanging() ),
359 m_bScriptSpace( rNew.HasScriptSpace() ),
360 m_bForbiddenChars( rNew.HasForbiddenChars() ),
361 m_bSnapToGrid( rNew.SnapToGrid() ),
362 m_nDirection( rNew.GetDirection() )
364 #if OSL_DEBUG_LEVEL > 0
365 ChkOutDev( *this );
366 #endif
367 SetLen( GetMinLen( *this ) );
370 SwTextSizeInfo::SwTextSizeInfo(SwTextFrame *const pTextFrame,
371 TextFrameIndex const nIndex)
372 : m_bOnWin(false)
374 CtorInitTextSizeInfo( pTextFrame->getRootFrame()->GetCurrShell()->GetOut(), pTextFrame, nIndex );
377 void SwTextSizeInfo::SelectFont()
379 // The path needs to go via ChgPhysFnt or the FontMetricCache gets confused.
380 // In this case pLastMet has it's old value.
381 // Wrong: GetOut()->SetFont( GetFont()->GetFnt() );
382 GetFont()->Invalidate();
383 GetFont()->ChgPhysFnt( m_pVsh, *GetOut() );
386 void SwTextSizeInfo::NoteAnimation() const
388 if( OnWin() )
389 SwRootFrame::FlushVout();
391 OSL_ENSURE( m_pOut == m_pVsh->GetOut(),
392 "SwTextSizeInfo::NoteAnimation() changed m_pOut" );
395 SwPosSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
396 const SwScriptInfo* pSI,
397 const OUString& rText,
398 const TextFrameIndex nIndex,
399 const TextFrameIndex nLength) const
401 SwDrawTextInfo aDrawInf( m_pVsh, *pOutDev, pSI, rText, nIndex, nLength );
402 aDrawInf.SetFrame( m_pFrame );
403 aDrawInf.SetFont( m_pFnt );
404 aDrawInf.SetSnapToGrid( SnapToGrid() );
405 aDrawInf.SetKanaComp( 0 );
406 return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
409 SwPosSize SwTextSizeInfo::GetTextSize() const
411 const SwScriptInfo& rSI =
412 const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
414 // in some cases, compression is not allowed or suppressed for
415 // performance reasons
416 sal_uInt16 nComp =( SwFontScript::CJK == GetFont()->GetActual() &&
417 rSI.CountCompChg() &&
418 ! IsMulti() ) ?
419 GetKanaComp() :
422 SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen );
423 aDrawInf.SetMeasureLen( m_nMeasureLen );
424 aDrawInf.SetFrame( m_pFrame );
425 aDrawInf.SetFont( m_pFnt );
426 aDrawInf.SetSnapToGrid( SnapToGrid() );
427 aDrawInf.SetKanaComp( nComp );
428 return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
431 void SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI, const TextFrameIndex nIndex,
432 const TextFrameIndex nLength, const sal_uInt16 nComp,
433 sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
434 vcl::text::TextLayoutCache const*const pCache) const
436 SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength,
437 0, false, pCache);
438 aDrawInf.SetFrame( m_pFrame );
439 aDrawInf.SetFont( m_pFnt );
440 aDrawInf.SetSnapToGrid( SnapToGrid() );
441 aDrawInf.SetKanaComp( nComp );
442 SwPosSize aSize( m_pFnt->GetTextSize_( aDrawInf ) );
443 nMaxSizeDiff = o3tl::narrowing<sal_uInt16>(aDrawInf.GetKanaDiff());
444 nMinSize = aSize.Width();
447 TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
448 const TextFrameIndex nMaxLen,
449 const sal_uInt16 nComp,
450 vcl::text::TextLayoutCache const*const pCache) const
452 const SwScriptInfo& rScriptInfo =
453 const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
455 OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
456 SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
457 *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
458 aDrawInf.SetFrame( m_pFrame );
459 aDrawInf.SetFont( m_pFnt );
460 aDrawInf.SetSnapToGrid( SnapToGrid() );
461 aDrawInf.SetKanaComp( nComp );
462 aDrawInf.SetHyphPos( nullptr );
464 return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
467 TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
468 const TextFrameIndex nMaxLen,
469 const sal_uInt16 nComp,
470 TextFrameIndex& rExtraCharPos,
471 vcl::text::TextLayoutCache const*const pCache) const
473 const SwScriptInfo& rScriptInfo =
474 const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
476 OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
477 SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
478 *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
479 aDrawInf.SetFrame( m_pFrame );
480 aDrawInf.SetFont( m_pFnt );
481 aDrawInf.SetSnapToGrid( SnapToGrid() );
482 aDrawInf.SetKanaComp( nComp );
483 aDrawInf.SetHyphPos( &rExtraCharPos );
485 return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
488 bool SwTextSizeInfo::HasHint(TextFrameIndex const nPos) const
490 std::pair<SwTextNode const*, sal_Int32> const pos(m_pFrame->MapViewToModel(nPos));
491 return pos.first->GetTextAttrForCharAt(pos.second);
494 void SwTextPaintInfo::CtorInitTextPaintInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame, const SwRect &rPaint )
496 CtorInitTextSizeInfo( pRenderContext, pFrame, TextFrameIndex(0) );
497 m_aTextFly.CtorInitTextFly( pFrame );
498 m_aPaintRect = rPaint;
499 m_nSpaceIdx = 0;
500 m_pSpaceAdd = nullptr;
501 m_pWrongList = nullptr;
502 m_pGrammarCheckList = nullptr;
503 m_pSmartTags = nullptr;
504 m_pBrushItem = nullptr;
507 SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf, const OUString* pText )
508 : SwTextSizeInfo( rInf, pText )
509 , m_pWrongList( rInf.GetpWrongList() )
510 , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
511 , m_pSmartTags( rInf.GetSmartTags() )
512 , m_pSpaceAdd( rInf.GetpSpaceAdd() ),
513 m_pBrushItem( rInf.GetBrushItem() ),
514 m_aTextFly( rInf.GetTextFly() ),
515 m_aPos( rInf.GetPos() ),
516 m_aPaintRect( rInf.GetPaintRect() ),
517 m_nSpaceIdx( rInf.GetSpaceIdx() )
520 SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf )
521 : SwTextSizeInfo( rInf )
522 , m_pWrongList( rInf.GetpWrongList() )
523 , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
524 , m_pSmartTags( rInf.GetSmartTags() )
525 , m_pSpaceAdd( rInf.GetpSpaceAdd() ),
526 m_pBrushItem( rInf.GetBrushItem() ),
527 m_aTextFly( rInf.GetTextFly() ),
528 m_aPos( rInf.GetPos() ),
529 m_aPaintRect( rInf.GetPaintRect() ),
530 m_nSpaceIdx( rInf.GetSpaceIdx() )
533 SwTextPaintInfo::SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint )
535 CtorInitTextPaintInfo( pFrame->getRootFrame()->GetCurrShell()->GetOut(), pFrame, rPaint );
538 namespace
541 * Context class that captures the draw operations on rDrawInf's output device for transparency
542 * purposes.
544 class SwTransparentTextGuard
546 ScopedVclPtrInstance<VirtualDevice> m_aContentVDev;
547 GDIMetaFile m_aContentMetafile;
548 MapMode m_aNewMapMode;
549 SwRect m_aPorRect;
550 SwTextPaintInfo& m_rPaintInf;
551 SwDrawTextInfo& m_rDrawInf;
553 public:
554 SwTransparentTextGuard(const SwLinePortion& rPor, SwTextPaintInfo& rPaintInf,
555 SwDrawTextInfo& rDrawInf);
556 ~SwTransparentTextGuard();
559 SwTransparentTextGuard::SwTransparentTextGuard(const SwLinePortion& rPor,
560 SwTextPaintInfo& rPaintInf, SwDrawTextInfo& rDrawInf)
561 : m_aNewMapMode(rPaintInf.GetOut()->GetMapMode())
562 , m_rPaintInf(rPaintInf)
563 , m_rDrawInf(rDrawInf)
565 rPaintInf.CalcRect(rPor, &m_aPorRect);
566 rDrawInf.SetOut(*m_aContentVDev);
567 m_aContentVDev->SetMapMode(rPaintInf.GetOut()->GetMapMode());
568 m_aContentMetafile.Record(m_aContentVDev.get());
569 m_aContentVDev->SetLineColor(rPaintInf.GetOut()->GetLineColor());
570 m_aContentVDev->SetFillColor(rPaintInf.GetOut()->GetFillColor());
571 m_aContentVDev->SetFont(rPaintInf.GetOut()->GetFont());
572 m_aContentVDev->SetDrawMode(rPaintInf.GetOut()->GetDrawMode());
573 m_aContentVDev->SetSettings(rPaintInf.GetOut()->GetSettings());
574 m_aContentVDev->SetRefPoint(rPaintInf.GetOut()->GetRefPoint());
577 SwTransparentTextGuard::~SwTransparentTextGuard()
579 m_aContentMetafile.Stop();
580 m_aContentMetafile.WindStart();
581 m_aNewMapMode.SetOrigin(m_aPorRect.TopLeft());
582 m_aContentMetafile.SetPrefMapMode(m_aNewMapMode);
583 m_aContentMetafile.SetPrefSize(m_aPorRect.SSize());
584 m_rDrawInf.SetOut(*m_rPaintInf.GetOut());
585 Gradient aVCLGradient;
586 sal_uInt8 nTransPercentVcl = 255 - m_rPaintInf.GetFont()->GetColor().GetAlpha();
587 const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
588 aVCLGradient.SetStyle(css::awt::GradientStyle_LINEAR);
589 aVCLGradient.SetStartColor(aTransColor);
590 aVCLGradient.SetEndColor(aTransColor);
591 aVCLGradient.SetAngle(0_deg10);
592 aVCLGradient.SetBorder(0);
593 aVCLGradient.SetOfsX(0);
594 aVCLGradient.SetOfsY(0);
595 aVCLGradient.SetStartIntensity(100);
596 aVCLGradient.SetEndIntensity(100);
597 aVCLGradient.SetSteps(2);
598 m_rPaintInf.GetOut()->DrawTransparent(m_aContentMetafile, m_aPorRect.TopLeft(),
599 m_aPorRect.SSize(), aVCLGradient);
603 void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPor,
604 TextFrameIndex const nStart, TextFrameIndex const nLength,
605 const bool bKern, const bool bWrong,
606 const bool bSmartTag,
607 const bool bGrammarCheck )
609 if( !nLength )
610 return;
612 // The SwScriptInfo is useless if we are inside a field portion
613 SwScriptInfo* pSI = nullptr;
614 if ( ! rPor.InFieldGrp() )
615 pSI = &GetParaPortion()->GetScriptInfo();
617 // in some cases, kana compression is not allowed or suppressed for
618 // performance reasons
619 sal_uInt16 nComp = 0;
620 if ( ! IsMulti() )
621 nComp = GetKanaComp();
623 bool bCfgIsAutoGrammar = false;
624 SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bCfgIsAutoGrammar;
625 const bool bBullet = OnWin() && GetOpt().IsBlank() && IsNoSymbol();
626 const bool bTmpWrong = bWrong && OnWin() && GetOpt().IsOnlineSpell();
627 const bool bTmpGrammarCheck = bGrammarCheck && OnWin() && bCfgIsAutoGrammar && GetOpt().IsOnlineSpell();
628 const bool bTmpSmart = bSmartTag && OnWin() && !GetOpt().IsPagePreview() && SwSmartTagMgr::Get().IsSmartTagsEnabled();
630 OSL_ENSURE( GetParaPortion(), "No paragraph!");
631 SwDrawTextInfo aDrawInf( m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart, nLength,
632 rPor.Width(), bBullet );
634 aDrawInf.SetUnderFnt( m_pUnderFnt );
636 const tools::Long nSpaceAdd = ( rPor.IsBlankPortion() || rPor.IsDropPortion() ||
637 rPor.InNumberGrp() ) ? 0 : GetSpaceAdd();
638 if ( nSpaceAdd )
640 TextFrameIndex nCharCnt(0);
641 // #i41860# Thai justified alignment needs some
642 // additional information:
643 aDrawInf.SetNumberOfBlanks( rPor.InTextGrp() ?
644 static_cast<const SwTextPortion&>(rPor).GetSpaceCnt( *this, nCharCnt ) :
645 TextFrameIndex(0) );
648 aDrawInf.SetSpace( nSpaceAdd );
649 aDrawInf.SetKanaComp( nComp );
651 // the font is used to identify the current script via nActual
652 aDrawInf.SetFont( m_pFnt );
653 // the frame is used to identify the orientation
654 aDrawInf.SetFrame( GetTextFrame() );
655 // we have to know if the paragraph should snap to grid
656 aDrawInf.SetSnapToGrid( SnapToGrid() );
657 // for underlining we must know when not to add extra space behind
658 // a character in justified mode
659 aDrawInf.SetSpaceStop( ! rPor.GetNextPortion() ||
660 rPor.GetNextPortion()->InFixMargGrp() ||
661 rPor.GetNextPortion()->IsHolePortion() );
663 // Draw text next to the left border
664 Point aFontPos(m_aPos);
665 if( m_pFnt->GetLeftBorder() && rPor.InTextGrp() && !static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev() )
667 const sal_uInt16 nLeftBorderSpace = m_pFnt->GetLeftBorderSpace();
668 if ( GetTextFrame()->IsRightToLeft() )
670 aFontPos.AdjustX( -nLeftBorderSpace );
672 else
674 switch( m_pFnt->GetOrientation(GetTextFrame()->IsVertical()).get() )
676 case 0 :
677 aFontPos.AdjustX(nLeftBorderSpace );
678 break;
679 case 900 :
680 aFontPos.AdjustY( -nLeftBorderSpace );
681 break;
682 case 1800 :
683 aFontPos.AdjustX( -nLeftBorderSpace );
684 break;
685 case 2700 :
686 aFontPos.AdjustY(nLeftBorderSpace );
687 break;
690 if( aFontPos.X() < 0 )
691 aFontPos.setX( 0 );
692 if( aFontPos.Y() < 0 )
693 aFontPos.setY( 0 );
696 // Handle semi-transparent text if necessary.
697 std::unique_ptr<SwTransparentTextGuard, o3tl::default_delete<SwTransparentTextGuard>> pTransparentText;
698 if (m_pFnt->GetColor() != COL_AUTO && m_pFnt->GetColor().IsTransparent())
700 pTransparentText.reset(new SwTransparentTextGuard(rPor, *this, aDrawInf));
703 if( GetTextFly().IsOn() )
705 // aPos needs to be the TopLeft, because we cannot calculate the
706 // ClipRects otherwise
707 const Point aPoint( aFontPos.X(), aFontPos.Y() - rPor.GetAscent() );
708 const Size aSize( rPor.Width(), rPor.Height() );
709 aDrawInf.SetPos( aPoint );
710 aDrawInf.SetSize( aSize );
711 aDrawInf.SetAscent( rPor.GetAscent() );
712 aDrawInf.SetKern( bKern ? rPor.Width() : 0 );
713 aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
714 aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
715 aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
716 GetTextFly().DrawTextOpaque( aDrawInf );
718 else
720 aDrawInf.SetPos( aFontPos );
721 if( bKern )
722 m_pFnt->DrawStretchText_( aDrawInf );
723 else
725 aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
726 aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
727 aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
728 m_pFnt->DrawText_( aDrawInf );
733 void SwTextPaintInfo::CalcRect( const SwLinePortion& rPor,
734 SwRect* pRect, SwRect* pIntersect,
735 const bool bInsideBox ) const
737 Size aSize( rPor.Width(), rPor.Height() );
738 if( rPor.IsHangingPortion() )
739 aSize.setWidth( static_cast<const SwHangingPortion&>(rPor).GetInnerWidth() );
740 if( rPor.InSpaceGrp() && GetSpaceAdd() )
742 SwTwips nAdd = rPor.CalcSpacing( GetSpaceAdd(), *this );
743 if( rPor.InFieldGrp() && GetSpaceAdd() < 0 && nAdd )
744 nAdd += GetSpaceAdd() / SPACING_PRECISION_FACTOR;
745 aSize.AdjustWidth(nAdd );
748 Point aPoint;
750 if( IsRotated() )
752 tools::Long nTmp = aSize.Width();
753 aSize.setWidth( aSize.Height() );
754 aSize.setHeight( nTmp );
755 if ( 1 == GetDirection() )
757 aPoint.setX( X() - rPor.GetAscent() );
758 aPoint.setY( Y() - aSize.Height() );
760 else
762 aPoint.setX( X() - rPor.Height() + rPor.GetAscent() );
763 aPoint.setY( Y() );
766 else
768 aPoint.setX( X() );
769 if (GetTextFrame()->IsVertLR() && !GetTextFrame()->IsVertLRBT())
770 aPoint.setY( Y() - rPor.Height() + rPor.GetAscent() );
771 else
772 aPoint.setY( Y() - rPor.GetAscent() );
775 // Adjust x coordinate if we are inside a bidi portion
776 const bool bFrameDir = GetTextFrame()->IsRightToLeft();
777 const bool bCounterDir = ( !bFrameDir && DIR_RIGHT2LEFT == GetDirection() ) ||
778 ( bFrameDir && DIR_LEFT2RIGHT == GetDirection() );
780 if ( bCounterDir )
781 aPoint.AdjustX( -(aSize.Width()) );
783 SwRect aRect( aPoint, aSize );
785 if ( GetTextFrame()->IsRightToLeft() )
786 GetTextFrame()->SwitchLTRtoRTL( aRect );
788 if ( GetTextFrame()->IsVertical() )
789 GetTextFrame()->SwitchHorizontalToVertical( aRect );
791 if( bInsideBox && rPor.InTextGrp() )
793 const bool bJoinWithPrev =
794 static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev();
795 const bool bJoinWithNext =
796 static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithNext();
797 const bool bIsVert = GetTextFrame()->IsVertical();
798 const bool bIsVertLRBT = GetTextFrame()->IsVertLRBT();
799 aRect.AddTop( GetFont()->CalcShadowSpace(SvxShadowItemSide::TOP, bIsVert, bIsVertLRBT,
800 bJoinWithPrev, bJoinWithNext));
801 aRect.AddBottom( - GetFont()->CalcShadowSpace(SvxShadowItemSide::BOTTOM, bIsVert, bIsVertLRBT,
802 bJoinWithPrev, bJoinWithNext));
803 aRect.AddLeft( GetFont()->CalcShadowSpace(SvxShadowItemSide::LEFT, bIsVert, bIsVertLRBT,
804 bJoinWithPrev, bJoinWithNext));
805 aRect.AddRight( - GetFont()->CalcShadowSpace(SvxShadowItemSide::RIGHT, bIsVert, bIsVertLRBT,
806 bJoinWithPrev, bJoinWithNext));
809 if ( pRect )
810 *pRect = aRect;
812 if( aRect.HasArea() && pIntersect )
814 ::SwAlignRect( aRect, GetVsh(), GetOut() );
816 if ( GetOut()->IsClipRegion() )
818 SwRect aClip( GetOut()->GetClipRegion().GetBoundRect() );
819 aRect.Intersection( aClip );
822 *pIntersect = aRect;
827 * Draws a special portion
828 * E.g.: line break portion, tab portion
830 * @param rPor The portion
831 * @param rRect The rectangle surrounding the character
832 * @param rCol Specify a color for the character
833 * @param bCenter Draw the character centered, otherwise left aligned
834 * @param bRotate Rotate the character if character rotation is set
836 static void lcl_DrawSpecial( const SwTextPaintInfo& rTextPaintInfo, const SwLinePortion& rPor,
837 SwRect& rRect, const Color& rCol, sal_Unicode cChar,
838 sal_uInt8 nOptions )
840 bool bCenter = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_CENTER );
841 bool bRotate = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_ROTATE );
843 // rRect is given in absolute coordinates
844 if ( rTextPaintInfo.GetTextFrame()->IsRightToLeft() )
845 rTextPaintInfo.GetTextFrame()->SwitchRTLtoLTR( rRect );
846 if ( rTextPaintInfo.GetTextFrame()->IsVertical() )
847 rTextPaintInfo.GetTextFrame()->SwitchVerticalToHorizontal( rRect );
849 const SwFont* pOldFnt = rTextPaintInfo.GetFont();
851 // Font is generated only once:
852 static SwFont s_aFnt = [&]()
854 SwFont tmp( *pOldFnt );
855 tmp.SetFamily( FAMILY_DONTKNOW, tmp.GetActual() );
856 tmp.SetName( numfunc::GetDefBulletFontname(), tmp.GetActual() );
857 tmp.SetStyleName(OUString(), tmp.GetActual());
858 tmp.SetCharSet( RTL_TEXTENCODING_SYMBOL, tmp.GetActual() );
859 return tmp;
860 }();
862 // Some of the current values are set at the font:
863 if ( ! bRotate )
864 s_aFnt.SetVertical( 0_deg10, rTextPaintInfo.GetTextFrame()->IsVertical() );
865 else
866 s_aFnt.SetVertical( pOldFnt->GetOrientation() );
868 s_aFnt.SetColor(rCol);
870 Size aFontSize( 0, SPECIAL_FONT_HEIGHT );
871 s_aFnt.SetSize( aFontSize, s_aFnt.GetActual() );
873 SwTextPaintInfo& rNonConstTextPaintInfo = const_cast<SwTextPaintInfo&>(rTextPaintInfo);
875 rNonConstTextPaintInfo.SetFont( &s_aFnt );
877 // The maximum width depends on the current orientation
878 const Degree10 nDir = s_aFnt.GetOrientation( rTextPaintInfo.GetTextFrame()->IsVertical() );
879 SwTwips nMaxWidth;
880 if (nDir == 900_deg10 || nDir == 2700_deg10)
881 nMaxWidth = rRect.Height();
882 else
884 assert(nDir == 0_deg10); //Unknown direction set at font
885 nMaxWidth = rRect.Width();
888 // check if char fits into rectangle
889 const OUString aTmp( cChar );
890 aFontSize = rTextPaintInfo.GetTextSize( aTmp ).SvLSize();
891 while ( aFontSize.Width() > nMaxWidth )
893 SwTwips nFactor = ( 100 * aFontSize.Width() ) / nMaxWidth;
894 const SwTwips nOldWidth = aFontSize.Width();
896 // new height for font
897 const SwFontScript nAct = s_aFnt.GetActual();
898 aFontSize.setHeight( ( 100 * s_aFnt.GetSize( nAct ).Height() ) / nFactor );
899 aFontSize.setWidth( ( 100 * s_aFnt.GetSize( nAct).Width() ) / nFactor );
901 if ( !aFontSize.Width() && !aFontSize.Height() )
902 break;
904 s_aFnt.SetSize( aFontSize, nAct );
906 aFontSize = rTextPaintInfo.GetTextSize( aTmp ).SvLSize();
908 if ( aFontSize.Width() >= nOldWidth )
909 break;
912 const Point aOldPos( rTextPaintInfo.GetPos() );
914 // adjust values so that tab is vertically and horizontally centered
915 SwTwips nX = rRect.Left();
916 SwTwips nY = rRect.Top();
917 switch ( nDir.get() )
919 case 0 :
920 if ( bCenter )
921 nX += ( rRect.Width() - aFontSize.Width() ) / 2;
922 nY += ( rRect.Height() - aFontSize.Height() ) / 2 + rTextPaintInfo.GetAscent();
923 break;
924 case 900 :
925 if ( bCenter )
926 nX += ( rRect.Width() - aFontSize.Height() ) / 2 + rTextPaintInfo.GetAscent();
927 nY += ( rRect.Height() + aFontSize.Width() ) / 2;
928 break;
929 case 2700 :
930 if ( bCenter )
931 nX += ( rRect.Width() + aFontSize.Height() ) / 2 - rTextPaintInfo.GetAscent();
932 nY += ( rRect.Height() - aFontSize.Width() ) / 2;
933 break;
936 Point aTmpPos( nX, nY );
937 rNonConstTextPaintInfo.SetPos( aTmpPos );
938 sal_uInt16 nOldWidth = rPor.Width();
939 const_cast<SwLinePortion&>(rPor).Width( o3tl::narrowing<sal_uInt16>(aFontSize.Width()) );
940 rTextPaintInfo.DrawText( aTmp, rPor );
941 const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
942 rNonConstTextPaintInfo.SetFont( const_cast<SwFont*>(pOldFnt) );
943 rNonConstTextPaintInfo.SetPos( aOldPos );
946 void SwTextPaintInfo::DrawRect( const SwRect &rRect, bool bRetouche ) const
948 if ( OnWin() || !bRetouche )
950 if( m_aTextFly.IsOn() )
951 const_cast<SwTextPaintInfo*>(this)->GetTextFly().
952 DrawFlyRect( m_pOut, rRect );
953 else
954 m_pOut->DrawRect( rRect.SVRect() );
958 void SwTextPaintInfo::DrawTab( const SwLinePortion &rPor ) const
960 if( !OnWin() )
961 return;
963 SwRect aRect;
964 CalcRect( rPor, &aRect );
966 if ( ! aRect.HasArea() )
967 return;
969 const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ? CHAR_TAB_RTL : CHAR_TAB;
970 const sal_uInt8 nOptions = DRAW_SPECIAL_OPTIONS_CENTER | DRAW_SPECIAL_OPTIONS_ROTATE;
972 lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
975 void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
977 if( !OnWin() )
978 return;
980 SwLineBreakClear eClear = SwLineBreakClear::NONE;
981 if (rPor.IsBreakPortion())
983 const auto& rBreakPortion = static_cast<const SwBreakPortion&>(rPor);
984 eClear = rBreakPortion.GetClear();
987 sal_uInt16 nOldWidth = rPor.Width();
988 const_cast<SwLinePortion&>(rPor).Width( LINE_BREAK_WIDTH );
990 SwRect aRect;
991 CalcRect( rPor, &aRect );
993 if( aRect.HasArea() )
995 const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ?
996 CHAR_LINEBREAK_RTL : CHAR_LINEBREAK;
997 const sal_uInt8 nOptions = 0;
999 SwRect aTextRect(aRect);
1000 if (eClear == SwLineBreakClear::LEFT || eClear == SwLineBreakClear::ALL)
1001 aTextRect.AddLeft(30);
1002 if (eClear == SwLineBreakClear::RIGHT || eClear == SwLineBreakClear::ALL)
1003 aTextRect.AddRight(-30);
1004 lcl_DrawSpecial( *this, rPor, aTextRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
1006 if (eClear != SwLineBreakClear::NONE)
1008 // Paint indicator if this clear is left/right/all.
1009 m_pOut->Push(vcl::PushFlags::LINECOLOR);
1010 m_pOut->SetLineColor(NON_PRINTING_CHARACTER_COLOR);
1011 if (eClear != SwLineBreakClear::RIGHT)
1012 m_pOut->DrawLine(aRect.BottomLeft(), aRect.TopLeft());
1013 if (eClear != SwLineBreakClear::LEFT)
1014 m_pOut->DrawLine(aRect.BottomRight(), aRect.TopRight());
1015 m_pOut->Pop();
1019 const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
1022 void SwTextPaintInfo::DrawRedArrow( const SwLinePortion &rPor ) const
1024 Size aSize( SPECIAL_FONT_HEIGHT, SPECIAL_FONT_HEIGHT );
1025 SwRect aRect( static_cast<const SwArrowPortion&>(rPor).GetPos(), aSize );
1026 sal_Unicode cChar;
1027 if( static_cast<const SwArrowPortion&>(rPor).IsLeft() )
1029 aRect.Pos().AdjustY(20 - GetAscent() );
1030 aRect.Pos().AdjustX(20 );
1031 if( aSize.Height() > rPor.Height() )
1032 aRect.Height( rPor.Height() );
1033 cChar = CHAR_LEFT_ARROW;
1035 else
1037 if( aSize.Height() > rPor.Height() )
1038 aRect.Height( rPor.Height() );
1039 aRect.Pos().AdjustY( -(aRect.Height() + 20) );
1040 aRect.Pos().AdjustX( -(aRect.Width() + 20) );
1041 cChar = CHAR_RIGHT_ARROW;
1044 if ( GetTextFrame()->IsVertical() )
1045 GetTextFrame()->SwitchHorizontalToVertical( aRect );
1047 if( aRect.HasArea() )
1049 const sal_uInt8 nOptions = 0;
1050 lcl_DrawSpecial( *this, rPor, aRect, COL_LIGHTRED, cChar, nOptions );
1054 void SwTextPaintInfo::DrawPostIts( bool bScript ) const
1056 if( !OnWin() || !m_pOpt->IsPostIts() )
1057 return;
1059 Size aSize;
1060 Point aTmp;
1062 const sal_uInt16 nPostItsWidth = SwViewOption::GetPostItsWidth( GetOut() );
1063 const sal_uInt16 nFontHeight = m_pFnt->GetHeight( m_pVsh, *GetOut() );
1064 const sal_uInt16 nFontAscent = m_pFnt->GetAscent( m_pVsh, *GetOut() );
1066 switch ( m_pFnt->GetOrientation( GetTextFrame()->IsVertical() ).get() )
1068 case 0 :
1069 aSize.setWidth( nPostItsWidth );
1070 aSize.setHeight( nFontHeight );
1071 aTmp.setX( m_aPos.X() );
1072 aTmp.setY( m_aPos.Y() - nFontAscent );
1073 break;
1074 case 900 :
1075 aSize.setHeight( nPostItsWidth );
1076 aSize.setWidth( nFontHeight );
1077 aTmp.setX( m_aPos.X() - nFontAscent );
1078 aTmp.setY( m_aPos.Y() );
1079 break;
1080 case 2700 :
1081 aSize.setHeight( nPostItsWidth );
1082 aSize.setWidth( nFontHeight );
1083 aTmp.setX( m_aPos.X() - nFontHeight +
1084 nFontAscent );
1085 aTmp.setY( m_aPos.Y() );
1086 break;
1089 SwRect aTmpRect( aTmp, aSize );
1091 if ( GetTextFrame()->IsRightToLeft() )
1092 GetTextFrame()->SwitchLTRtoRTL( aTmpRect );
1094 if ( GetTextFrame()->IsVertical() )
1095 GetTextFrame()->SwitchHorizontalToVertical( aTmpRect );
1097 GetOpt().PaintPostIts( const_cast<OutputDevice*>(GetOut()), aTmpRect, bScript );
1101 void SwTextPaintInfo::DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) const
1103 SwRect aIntersect;
1104 CalcRect( rPor, &aIntersect );
1105 if ( !aIntersect.HasArea() )
1106 return;
1108 if (OnWin() && GetOpt().IsFieldShadings() &&
1109 !GetOpt().IsPagePreview())
1111 OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
1112 pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1113 pOut->SetFillColor( GetOpt().GetFieldShadingsColor() );
1114 pOut->SetLineColor();
1115 pOut->DrawRect( aIntersect.SVRect() );
1116 pOut->Pop();
1118 const int delta=10;
1119 tools::Rectangle r(aIntersect.Left()+delta, aIntersect.Top()+delta, aIntersect.Right()-delta, aIntersect.Bottom()-delta);
1120 m_pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1121 m_pOut->SetLineColor( Color(0, 0, 0));
1122 m_pOut->SetFillColor();
1123 m_pOut->DrawRect( r );
1124 if (bChecked)
1126 m_pOut->DrawLine(r.TopLeft(), r.BottomRight());
1127 m_pOut->DrawLine(r.TopRight(), r.BottomLeft());
1129 m_pOut->Pop();
1132 void SwTextPaintInfo::DrawBackground( const SwLinePortion &rPor, const Color *pColor ) const
1134 OSL_ENSURE( OnWin(), "SwTextPaintInfo::DrawBackground: printer pollution ?" );
1136 SwRect aIntersect;
1137 CalcRect( rPor, nullptr, &aIntersect, true );
1139 if ( !aIntersect.HasArea() )
1140 return;
1142 OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
1143 pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1145 if ( pColor )
1146 pOut->SetFillColor( *pColor );
1147 else
1148 pOut->SetFillColor( GetOpt().GetFieldShadingsColor() );
1150 pOut->SetLineColor();
1152 DrawRect( aIntersect, true );
1153 pOut->Pop();
1156 void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
1159 SwRect aIntersect;
1160 CalcRect( rPor, &aIntersect, nullptr, true );
1161 if(aIntersect.HasArea())
1163 SwPosition const aPosition(m_pFrame->MapViewToModelPos(GetIdx()));
1164 const ::sw::mark::IMark* pFieldmark =
1165 m_pFrame->GetDoc().getIDocumentMarkAccess()->getInnerFieldmarkFor(aPosition);
1166 bool bIsStartMark = (TextFrameIndex(1) == GetLen()
1167 && CH_TXT_ATR_FIELDSTART == GetText()[sal_Int32(GetIdx())]);
1168 if(pFieldmark) {
1169 SAL_INFO("sw.core", "Found Fieldmark " << pFieldmark->ToString());
1171 if(bIsStartMark)
1172 SAL_INFO("sw.core", "Found StartMark");
1173 if (OnWin() && (pFieldmark!=nullptr || bIsStartMark) &&
1174 GetOpt().IsFieldShadings() &&
1175 !GetOpt().IsPagePreview())
1177 OutputDevice* pOutDev = const_cast<OutputDevice*>(GetOut());
1178 pOutDev->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1179 pOutDev->SetFillColor( GetOpt().GetFieldShadingsColor() );
1180 pOutDev->SetLineColor( );
1181 pOutDev->DrawRect( aIntersect.SVRect() );
1182 pOutDev->Pop();
1187 SwRect aIntersect;
1188 CalcRect( rPor, nullptr, &aIntersect, true );
1190 if ( !aIntersect.HasArea() )
1191 return;
1193 OutputDevice* pTmpOut = const_cast<OutputDevice*>(GetOut());
1195 // #i16816# tagged pdf support
1196 SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pTmpOut );
1198 Color aFillColor;
1200 if( m_pFnt->GetHighlightColor() != COL_TRANSPARENT )
1202 aFillColor = m_pFnt->GetHighlightColor();
1204 else
1206 if( !m_pFnt->GetBackColor() )
1207 return;
1208 aFillColor = *m_pFnt->GetBackColor();
1211 // tdf#104349 do not highlight portions of space chars before end of line if the compatibility option is enabled
1212 // for LTR mode only
1213 if ( !GetTextFrame()->IsRightToLeft() )
1215 if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
1217 bool draw = false;
1218 bool full = false;
1219 const sal_Int32 nMaxLen = GetText().getLength();
1220 const sal_Int32 nCurrPorEnd(GetIdx() + rPor.GetLen());
1221 const SwLinePortion* pPos = &rPor;
1222 TextFrameIndex nIdx = GetIdx();
1226 const sal_Int32 nEndPos = std::min(sal_Int32(nIdx + pPos->GetLen()), nMaxLen);
1227 for (sal_Int32 i = sal_Int32(nIdx); i < nEndPos; ++i)
1229 if (i < nMaxLen && i >= nCurrPorEnd && GetText()[i] == CH_TXTATR_NEWLINE)
1230 goto drawcontinue;
1232 if (i == nMaxLen || GetText()[i] != CH_BLANK)
1234 draw = true;
1235 if (i >= nCurrPorEnd)
1237 full = true;
1238 goto drawcontinue;
1242 nIdx += pPos->GetLen();
1243 pPos = pPos->GetNextPortion();
1244 } while ( pPos );
1246 drawcontinue:
1248 if ( !draw )
1249 return;
1251 if ( !full )
1253 const sal_Int32 nLastPos = std::min(nCurrPorEnd, nMaxLen) - 1;
1254 for (sal_Int32 i = nLastPos; TextFrameIndex(i) >= GetIdx(); --i)
1256 if (GetText()[i] == CH_TXTATR_NEWLINE)
1257 continue;
1259 if (GetText()[i] != CH_BLANK)
1261 const sal_uInt16 nOldWidth = rPor.Width();
1262 const sal_uInt16 nExcessWidth
1263 = GetTextSize(m_pOut, nullptr, GetText(), TextFrameIndex(i + 1),
1264 TextFrameIndex(nLastPos - i)).Width();
1265 const_cast<SwLinePortion&>(rPor).Width(nOldWidth - nExcessWidth);
1266 CalcRect( rPor, nullptr, &aIntersect, true );
1267 const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
1269 if ( !aIntersect.HasArea() )
1270 return;
1272 break;
1279 pTmpOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
1281 pTmpOut->SetFillColor(aFillColor);
1282 pTmpOut->SetLineColor();
1284 DrawRect( aIntersect, false );
1286 pTmpOut->Pop();
1289 void SwTextPaintInfo::DrawBorder( const SwLinePortion &rPor ) const
1291 SwRect aDrawArea;
1292 CalcRect( rPor, &aDrawArea );
1293 if ( aDrawArea.HasArea() )
1295 PaintCharacterBorder(*m_pFnt, aDrawArea, GetTextFrame()->IsVertical(),
1296 GetTextFrame()->IsVertLRBT(), rPor.GetJoinBorderWithPrev(),
1297 rPor.GetJoinBorderWithNext());
1301 namespace {
1303 bool HasValidPropertyValue(const uno::Any& rAny)
1305 if (bool bValue; rAny >>= bValue)
1307 return true;
1309 else if (OUString aValue; (rAny >>= aValue) && !(aValue.isEmpty()))
1311 return true;
1313 else if (awt::FontSlant eValue; rAny >>= eValue)
1315 return true;
1317 else if (tools::Long nValueLong; rAny >>= nValueLong)
1319 return true;
1321 else if (double fValue; rAny >>= fValue)
1323 return true;
1325 else if (short nValueShort; rAny >>= nValueShort)
1327 return true;
1329 else
1330 return false;
1334 void SwTextPaintInfo::DrawCSDFHighlighting(const SwLinePortion &rPor) const
1336 // Don't use GetActiveView() as it does not work as expected when there are multiple open
1337 // documents.
1338 SwView* pView = SwTextFrame::GetView();
1339 if (!pView)
1340 return;
1342 StylesHighlighterColorMap& rCharStylesColorMap = pView->GetStylesHighlighterCharColorMap();
1344 if (rCharStylesColorMap.empty() && !pView->IsHighlightCharDF())
1345 return;
1347 SwRect aRect;
1348 CalcRect(rPor, &aRect, nullptr, true);
1349 if(!aRect.HasArea())
1350 return;
1352 SwTextFrame* pFrame = const_cast<SwTextFrame*>(GetTextFrame());
1353 if (!pFrame)
1354 return;
1356 SwPosition aPosition(pFrame->MapViewToModelPos(GetIdx()));
1357 SwPosition aMarkPosition(pFrame->MapViewToModelPos(GetIdx() + GetLen()));
1359 uno::Reference<text::XTextRange> xRange(
1360 SwXTextRange::CreateXTextRange(pFrame->GetDoc(), aPosition, &aMarkPosition));
1361 uno::Reference<beans::XPropertySet> xPropertiesSet(xRange, uno::UNO_QUERY_THROW);
1363 OUString sCurrentCharStyle;
1364 xPropertiesSet->getPropertyValue("CharStyleName") >>= sCurrentCharStyle;
1366 std::optional<OUString> sCSNumberOrDF; // CS number or "df" or not used
1367 std::optional<Color> aFillColor;
1369 // check for CS formatting, if not CS formatted check for direct character formatting
1370 if (!sCurrentCharStyle.isEmpty())
1372 if (!rCharStylesColorMap.empty())
1374 OUString sCharStyleDisplayName;
1375 sCharStyleDisplayName = SwStyleNameMapper::GetUIName(sCurrentCharStyle,
1376 SwGetPoolIdFromName::ChrFmt);
1377 if (!sCharStyleDisplayName.isEmpty()
1378 && rCharStylesColorMap.find(sCharStyleDisplayName)
1379 != rCharStylesColorMap.end())
1381 aFillColor = rCharStylesColorMap[sCharStyleDisplayName].first;
1382 sCSNumberOrDF = OUString::number(rCharStylesColorMap[sCharStyleDisplayName].second);
1386 // not character style formatted
1387 else if (pView->IsHighlightCharDF())
1389 const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID,
1390 UNO_NAME_PARA_IS_NUMBERING_RESTART,
1391 UNO_NAME_PARA_STYLE_NAME,
1392 UNO_NAME_PARA_CONDITIONAL_STYLE_NAME,
1393 UNO_NAME_PAGE_STYLE_NAME,
1394 UNO_NAME_NUMBERING_START_VALUE,
1395 UNO_NAME_NUMBERING_IS_NUMBER,
1396 UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE,
1397 UNO_NAME_CHAR_STYLE_NAME,
1398 UNO_NAME_NUMBERING_LEVEL,
1399 UNO_NAME_SORTED_TEXT_ID,
1400 UNO_NAME_PARRSID,
1401 UNO_NAME_CHAR_COLOR_THEME,
1402 UNO_NAME_CHAR_COLOR_TINT_OR_SHADE };
1404 SfxItemPropertySet const& rPropSet(
1405 *aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
1406 SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
1409 uno::Reference<beans::XPropertyState> xPropertiesState(xRange, uno::UNO_QUERY_THROW);
1410 const uno::Sequence<beans::Property> aProperties
1411 = xPropertiesSet->getPropertySetInfo()->getProperties();
1413 for (const beans::Property& rProperty : aProperties)
1415 const OUString& rPropName = rProperty.Name;
1417 if (!rMap.hasPropertyByName(rPropName))
1418 continue;
1420 if (std::find(aHiddenProperties.begin(), aHiddenProperties.end(), rPropName)
1421 != aHiddenProperties.end())
1422 continue;
1424 if (xPropertiesState->getPropertyState(rPropName) == beans::PropertyState_DIRECT_VALUE)
1426 const uno::Any aAny = xPropertiesSet->getPropertyValue(rPropName);
1427 if (HasValidPropertyValue(aAny))
1429 sCSNumberOrDF = SwResId(STR_CHARACTER_DIRECT_FORMATTING_TAG);
1430 aFillColor = COL_LIGHTGRAY;
1431 break;
1436 if (sCSNumberOrDF)
1438 OutputDevice* pTmpOut = const_cast<OutputDevice*>(GetOut());
1439 pTmpOut->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR
1440 | vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::FONT);
1442 // draw a filled rectangle at the formatted CS or DF text
1443 pTmpOut->SetFillColor(aFillColor.value());
1444 pTmpOut->SetLineColor(aFillColor.value());
1445 tools::Rectangle aSVRect(aRect.SVRect());
1446 pTmpOut->DrawRect(aSVRect);
1448 // calculate size and position for the CS number or "df" text and rectangle
1449 tools::Long nWidth = pTmpOut->GetTextWidth(sCSNumberOrDF.value());
1450 tools::Long nHeight = pTmpOut->GetTextHeight();
1451 aSVRect.SetSize(Size(nWidth, nHeight));
1452 aSVRect.Move(-(nWidth / 1.5), -(nHeight / 1.5));
1454 vcl::Font aFont(pTmpOut->GetFont());
1455 aFont.SetOrientation(Degree10(0));
1456 pTmpOut->SetFont(aFont);
1458 pTmpOut->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::TextOriginLeft);
1459 //pTmpOut->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::BiDiStrong);
1461 pTmpOut->SetTextFillColor(aFillColor.value());
1462 pTmpOut->DrawText(aSVRect, sCSNumberOrDF.value(), DrawTextFlags::NONE);
1464 pTmpOut->Pop();
1468 void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor,
1469 PortionType nWhich, const Color *pColor ) const
1471 if( !OnWin() || IsMulti() )
1472 return;
1474 bool bDraw = false;
1475 switch( nWhich )
1477 case PortionType::Footnote:
1478 case PortionType::QuoVadis:
1479 case PortionType::Number:
1480 case PortionType::Field:
1481 case PortionType::Hidden:
1482 case PortionType::Tox:
1483 case PortionType::Ref:
1484 case PortionType::Meta:
1485 case PortionType::ContentControl:
1486 case PortionType::ControlChar:
1487 if ( !GetOpt().IsPagePreview()
1488 && !GetOpt().IsReadonly()
1489 && GetOpt().IsFieldShadings()
1490 && ( PortionType::Number != nWhich
1491 || m_pFrame->GetTextNodeForParaProps()->HasMarkedLabel())) // #i27615#
1493 bDraw = PortionType::Footnote != nWhich || m_pFrame->IsFootnoteAllowed();
1495 break;
1496 case PortionType::Bookmark:
1497 // no shading
1498 break;
1499 case PortionType::InputField:
1500 // input field shading also in read-only mode
1501 if ( !GetOpt().IsPagePreview()
1502 && GetOpt().IsFieldShadings() )
1504 bDraw = true;
1506 break;
1507 case PortionType::Tab:
1508 if ( GetOpt().IsTab() ) bDraw = true;
1509 break;
1510 case PortionType::SoftHyphen:
1511 if ( GetOpt().IsSoftHyph() )bDraw = true;
1512 break;
1513 case PortionType::Blank:
1514 if ( GetOpt().IsHardBlank())bDraw = true;
1515 break;
1516 default:
1518 OSL_ENSURE( false, "SwTextPaintInfo::DrawViewOpt: don't know how to draw this" );
1519 break;
1522 if ( bDraw )
1523 DrawBackground( rPor, pColor );
1526 static void lcl_InitHyphValues( PropertyValues &rVals,
1527 sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
1528 bool bNoCapsHyphenation, bool bNoLastWordHyphenation,
1529 sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone )
1531 sal_Int32 nLen = rVals.getLength();
1533 if (0 == nLen) // yet to be initialized?
1535 rVals.realloc( 6 );
1536 PropertyValue *pVal = rVals.getArray();
1538 pVal[0].Name = UPN_HYPH_MIN_LEADING;
1539 pVal[0].Handle = UPH_HYPH_MIN_LEADING;
1540 pVal[0].Value <<= nMinLeading;
1542 pVal[1].Name = UPN_HYPH_MIN_TRAILING;
1543 pVal[1].Handle = UPH_HYPH_MIN_TRAILING;
1544 pVal[1].Value <<= nMinTrailing;
1546 pVal[2].Name = UPN_HYPH_NO_CAPS;
1547 pVal[2].Handle = UPH_HYPH_NO_CAPS;
1548 pVal[2].Value <<= bNoCapsHyphenation;
1550 pVal[3].Name = UPN_HYPH_NO_LAST_WORD;
1551 pVal[3].Handle = UPH_HYPH_NO_LAST_WORD;
1552 pVal[3].Value <<= bNoLastWordHyphenation;
1554 pVal[4].Name = UPN_HYPH_MIN_WORD_LENGTH;
1555 pVal[4].Handle = UPH_HYPH_MIN_WORD_LENGTH;
1556 pVal[4].Value <<= nMinWordLength;
1558 pVal[5].Name = UPN_HYPH_ZONE;
1559 pVal[5].Handle = UPH_HYPH_ZONE;
1560 pVal[5].Value <<= nTextHyphZone;
1562 else if (6 == nLen) // already initialized once?
1564 PropertyValue *pVal = rVals.getArray();
1565 pVal[0].Value <<= nMinLeading;
1566 pVal[1].Value <<= nMinTrailing;
1567 pVal[2].Value <<= bNoCapsHyphenation;
1568 pVal[3].Value <<= bNoLastWordHyphenation;
1569 pVal[4].Value <<= nMinWordLength;
1570 pVal[5].Value <<= nTextHyphZone;
1572 else {
1573 OSL_FAIL( "unexpected size of sequence" );
1577 const PropertyValues & SwTextFormatInfo::GetHyphValues() const
1579 OSL_ENSURE( 6 == m_aHyphVals.getLength(),
1580 "hyphenation values not yet initialized" );
1581 return m_aHyphVals;
1584 bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
1586 const SwAttrSet& rAttrSet = GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet();
1587 SetHanging( rAttrSet.GetHangingPunctuation().GetValue() );
1588 SetScriptSpace( rAttrSet.GetScriptSpace().GetValue() );
1589 SetForbiddenChars( rAttrSet.GetForbiddenRule().GetValue() );
1590 const SvxHyphenZoneItem &rAttr = rAttrSet.GetHyphenZone();
1591 MaxHyph() = rAttr.GetMaxHyphens();
1592 const bool bAuto = bAutoHyphen || rAttr.IsHyphen();
1593 if( bAuto || m_bInterHyph )
1595 const sal_Int16 nMinimalLeading = std::max(rAttr.GetMinLead(), sal_uInt8(2));
1596 const sal_Int16 nMinimalTrailing = rAttr.GetMinTrail();
1597 const sal_Int16 nMinimalWordLength = rAttr.GetMinWordLength();
1598 const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation();
1599 const bool bNoLastWordHyphenation = rAttr.IsNoLastWordHyphenation();
1600 const sal_Int16 nTextHyphZone = rAttr.GetTextHyphenZone();
1601 lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
1602 bNoCapsHyphenation, bNoLastWordHyphenation,
1603 nMinimalWordLength, nTextHyphZone );
1605 return bAuto;
1608 void SwTextFormatInfo::CtorInitTextFormatInfo( OutputDevice* pRenderContext, SwTextFrame *pNewFrame, const bool bNewInterHyph,
1609 const bool bNewQuick, const bool bTst )
1611 CtorInitTextPaintInfo( pRenderContext, pNewFrame, SwRect() );
1613 m_bQuick = bNewQuick;
1614 m_bInterHyph = bNewInterHyph;
1616 //! needs to be done in this order
1617 m_bAutoHyph = InitHyph();
1619 m_bIgnoreFly = false;
1620 m_bFakeLineStart = false;
1621 m_bShift = false;
1622 m_bDropInit = false;
1623 m_bTestFormat = bTst;
1624 m_nLeft = 0;
1625 m_nRight = 0;
1626 m_nFirst = 0;
1627 m_nRealWidth = 0;
1628 m_nForcedLeftMargin = 0;
1629 m_pRest = nullptr;
1630 m_nLineHeight = 0;
1631 m_nLineNetHeight = 0;
1632 SetLineStart(TextFrameIndex(0));
1634 SvtCTLOptions::TextNumerals const nTextNumerals(
1635 SvtCTLOptions::GetCTLTextNumerals());
1636 // cannot cache for NUMERALS_CONTEXT because we need to know the string
1637 // for the whole paragraph now
1638 if (nTextNumerals != SvtCTLOptions::NUMERALS_CONTEXT)
1640 // set digit mode to what will be used later to get same results
1641 SwDigitModeModifier const m(*m_pRef, LANGUAGE_NONE /*dummy*/);
1642 assert(m_pRef->GetDigitLanguage() != LANGUAGE_NONE);
1643 SetCachedVclData(OutputDevice::CreateTextLayoutCache(*m_pText));
1646 Init();
1650 * If the Hyphenator returns ERROR or the language is set to NOLANGUAGE
1651 * we do not hyphenate.
1652 * Else, we always hyphenate if we do interactive hyphenation.
1653 * If we do not do interactive hyphenation, we only hyphenate if ParaFormat is
1654 * set to automatic hyphenation.
1656 bool SwTextFormatInfo::IsHyphenate() const
1658 if( !m_bInterHyph && !m_bAutoHyph )
1659 return false;
1661 LanguageType eTmp = GetFont()->GetLanguage();
1662 // TODO: check for more ideographic langs w/o hyphenation as a concept
1663 if ( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == eTmp
1664 || !MsLangId::usesHyphenation(eTmp) )
1665 return false;
1667 uno::Reference< XHyphenator > xHyph = ::GetHyphenator();
1668 if (!xHyph.is())
1669 return false;
1671 if (m_bInterHyph)
1672 SvxSpellWrapper::CheckHyphLang( xHyph, eTmp );
1674 if (!xHyph->hasLocale(g_pBreakIt->GetLocale(eTmp)))
1676 SfxObjectShell* pShell = m_pFrame->GetDoc().GetDocShell();
1677 if (pShell)
1679 pShell->AppendInfoBarWhenReady(
1680 "hyphenationmissing", SwResId(STR_HYPH_MISSING),
1681 SwResId(STR_HYPH_MISSING_DETAIL)
1682 .replaceFirst("%1", LanguageTag::convertToBcp47( g_pBreakIt->GetLocale(eTmp))),
1683 InfobarType::WARNING);
1687 return xHyph->hasLocale( g_pBreakIt->GetLocale(eTmp) );
1690 const SwFormatDrop *SwTextFormatInfo::GetDropFormat() const
1692 const SwFormatDrop *pDrop = &GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetDrop();
1693 if( 1 >= pDrop->GetLines() ||
1694 ( !pDrop->GetChars() && !pDrop->GetWholeWord() ) )
1695 pDrop = nullptr;
1696 return pDrop;
1699 void SwTextFormatInfo::Init()
1701 // Not initialized: pRest, nLeft, nRight, nFirst, nRealWidth
1702 X(0);
1703 m_bArrowDone = m_bFull = m_bFootnoteDone = m_bErgoDone = m_bNumDone = m_bNoEndHyph =
1704 m_bNoMidHyph = m_bStop = m_bNewLine = m_bUnderflow = m_bTabOverflow = false;
1706 // generally we do not allow number portions in follows, except...
1707 if ( GetTextFrame()->IsFollow() )
1709 const SwTextFrame* pMaster = GetTextFrame()->FindMaster();
1710 OSL_ENSURE(pMaster, "pTextFrame without Master");
1711 const SwLinePortion* pTmpPara = pMaster ? pMaster->GetPara() : nullptr;
1713 // there is a master for this follow and the master does not have
1714 // any contents (especially it does not have a number portion)
1715 m_bNumDone = ! pTmpPara ||
1716 ! static_cast<const SwParaPortion*>(pTmpPara)->GetFirstPortion()->IsFlyPortion();
1719 m_pRoot = nullptr;
1720 m_pLast = nullptr;
1721 m_pFly = nullptr;
1722 m_pLastTab = nullptr;
1723 m_pUnderflow = nullptr;
1724 m_cTabDecimal = 0;
1725 m_nWidth = m_nRealWidth;
1726 m_nForcedLeftMargin = 0;
1727 m_nSoftHyphPos = TextFrameIndex(0);
1728 m_nUnderScorePos = TextFrameIndex(COMPLETE_STRING);
1729 m_nLastBookmarkPos = TextFrameIndex(-1);
1730 m_cHookChar = 0;
1731 SetIdx(TextFrameIndex(0));
1732 SetLen(TextFrameIndex(GetText().getLength()));
1733 SetPaintOfst(0);
1736 SwTextFormatInfo::SwTextFormatInfo(OutputDevice* pRenderContext, SwTextFrame *pFrame, const bool bInterHyphL,
1737 const bool bQuickL, const bool bTst)
1739 CtorInitTextFormatInfo(pRenderContext, pFrame, bInterHyphL, bQuickL, bTst);
1743 * There are a few differences between a copy constructor
1744 * and the following constructor for multi-line formatting.
1745 * The root is the first line inside the multi-portion,
1746 * the line start is the actual position in the text,
1747 * the line width is the rest width from the surrounding line
1748 * and the bMulti and bFirstMulti-flag has to be set correctly.
1750 SwTextFormatInfo::SwTextFormatInfo( const SwTextFormatInfo& rInf,
1751 SwLineLayout& rLay, SwTwips nActWidth ) :
1752 SwTextPaintInfo( rInf ),
1753 m_pRoot(&rLay),
1754 m_pLast(&rLay),
1755 m_pFly(nullptr),
1756 m_pUnderflow(nullptr),
1757 m_pRest(nullptr),
1758 m_pLastTab(nullptr),
1759 m_nSoftHyphPos(TextFrameIndex(0)),
1760 m_nLineStart(rInf.GetIdx()),
1761 m_nUnderScorePos(TextFrameIndex(COMPLETE_STRING)),
1762 m_nLeft(rInf.m_nLeft),
1763 m_nRight(rInf.m_nRight),
1764 m_nFirst(rInf.m_nLeft),
1765 m_nRealWidth(sal_uInt16(nActWidth)),
1766 m_nWidth(m_nRealWidth),
1767 m_nLineHeight(0),
1768 m_nLineNetHeight(0),
1769 m_nForcedLeftMargin(0),
1770 m_bFull(false),
1771 m_bFootnoteDone(true),
1772 m_bErgoDone(true),
1773 m_bNumDone(true),
1774 m_bArrowDone(true),
1775 m_bStop(false),
1776 m_bNewLine(true),
1777 m_bShift(false),
1778 m_bUnderflow(false),
1779 m_bInterHyph(false),
1780 m_bAutoHyph(false),
1781 m_bDropInit(false),
1782 m_bQuick(rInf.m_bQuick),
1783 m_bNoEndHyph(false),
1784 m_bNoMidHyph(false),
1785 m_bIgnoreFly(false),
1786 m_bFakeLineStart(false),
1787 m_bTabOverflow( false ),
1788 m_bTestFormat(rInf.m_bTestFormat),
1789 m_cTabDecimal(0),
1790 m_cHookChar(0),
1791 m_nMaxHyph(0)
1793 SetMulti( true );
1794 SetFirstMulti( rInf.IsFirstMulti() );
1797 bool SwTextFormatInfo::CheckFootnotePortion_( SwLineLayout const * pCurr )
1799 const sal_uInt16 nHeight = pCurr->GetRealHeight();
1800 for( SwLinePortion *pPor = pCurr->GetNextPortion(); pPor; pPor = pPor->GetNextPortion() )
1802 if( pPor->IsFootnotePortion() && nHeight > static_cast<SwFootnotePortion*>(pPor)->Orig() )
1804 SetLineHeight( nHeight );
1805 SetLineNetHeight( pCurr->Height() );
1806 return true;
1809 return false;
1812 TextFrameIndex SwTextFormatInfo::ScanPortionEnd(TextFrameIndex const nStart,
1813 TextFrameIndex const nEnd)
1815 m_cHookChar = 0;
1816 TextFrameIndex i = nStart;
1818 // Used for decimal tab handling:
1819 const sal_Unicode cTabDec = GetLastTab() ? GetTabDecimal() : 0;
1820 const sal_Unicode cThousandSep = ',' == cTabDec ? '.' : ',';
1822 // #i45951# German (Switzerland) uses ' as thousand separator
1823 const sal_Unicode cThousandSep2 = ',' == cTabDec ? '.' : '\'';
1825 bool bNumFound = false;
1826 const bool bTabCompat = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
1828 for( ; i < nEnd; ++i )
1830 const sal_Unicode cPos = GetChar( i );
1831 switch( cPos )
1833 case CH_TXTATR_BREAKWORD:
1834 case CH_TXTATR_INWORD:
1835 if( !HasHint( i ))
1836 break;
1837 [[fallthrough]];
1839 case CHAR_SOFTHYPHEN:
1840 case CHAR_HARDHYPHEN:
1841 case CHAR_HARDBLANK:
1842 case CH_TAB:
1843 case CH_BREAK:
1844 case CHAR_ZWSP :
1845 case CHAR_WJ :
1846 m_cHookChar = cPos;
1847 return i;
1849 case CHAR_UNDERSCORE:
1850 if (TextFrameIndex(COMPLETE_STRING) == m_nUnderScorePos)
1851 m_nUnderScorePos = i;
1852 break;
1854 default:
1855 if ( cTabDec )
1857 if( cTabDec == cPos )
1859 OSL_ENSURE( cPos, "Unexpected end of string" );
1860 if( cPos ) // robust
1862 m_cHookChar = cPos;
1863 return i;
1867 // Compatibility: First non-digit character behind a
1868 // a digit character becomes the hook character
1869 if ( bTabCompat )
1871 if ( ( 0x2F < cPos && cPos < 0x3A ) ||
1872 ( bNumFound && ( cPos == cThousandSep || cPos == cThousandSep2 ) ) )
1874 bNumFound = true;
1876 else
1878 if ( bNumFound )
1880 m_cHookChar = cPos;
1881 SetTabDecimal( cPos );
1882 return i;
1890 // Check if character *behind* the portion has
1891 // to become the hook:
1892 if (i == nEnd && i < TextFrameIndex(GetText().getLength()) && bNumFound)
1894 const sal_Unicode cPos = GetChar( i );
1895 if ( cPos != cTabDec && cPos != cThousandSep && cPos !=cThousandSep2 && ( 0x2F >= cPos || cPos >= 0x3A ) )
1897 m_cHookChar = GetChar( i );
1898 SetTabDecimal( m_cHookChar );
1902 return i;
1905 bool SwTextFormatInfo::LastKernPortion()
1907 if( GetLast() )
1909 if( GetLast()->IsKernPortion() )
1910 return true;
1911 if( GetLast()->Width() || ( GetLast()->GetLen() &&
1912 !GetLast()->IsHolePortion() ) )
1913 return false;
1915 SwLinePortion* pPor = GetRoot();
1916 SwLinePortion *pKern = nullptr;
1917 while( pPor )
1919 if( pPor->IsKernPortion() )
1920 pKern = pPor;
1921 else if( pPor->Width() || ( pPor->GetLen() && !pPor->IsHolePortion() ) )
1922 pKern = nullptr;
1923 pPor = pPor->GetNextPortion();
1925 if( pKern )
1927 SetLast( pKern );
1928 return true;
1930 return false;
1933 SwTwips SwTextFormatInfo::GetLineWidth()
1935 SwTwips nLineWidth = Width() - X();
1937 const bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
1938 DocumentSettingId::TAB_OVER_MARGIN);
1939 const bool bTabOverSpacing = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
1940 DocumentSettingId::TAB_OVER_SPACING);
1941 if (!bTabOverMargin && !bTabOverSpacing)
1942 return nLineWidth;
1944 SwTabPortion* pLastTab = GetLastTab();
1945 if (!pLastTab)
1946 return nLineWidth;
1948 // Consider tab portions over the printing bounds of the text frame.
1949 if (pLastTab->GetTabPos() <= Width())
1950 return nLineWidth;
1952 // Calculate the width that starts at the left (or in case of first line:
1953 // first) margin, but ends after the right paragraph margin:
1955 // +--------------------+
1956 // |LL| |RR|
1957 // +--------------------+
1958 // ^ m_nLeftMargin (absolute)
1959 // ^ nLeftMarginWidth (relative to m_nLeftMargin), X() is relative to this
1960 // ^ right margin
1961 // ^ paragraph right
1962 // <--------------------> is GetTextFrame()->getFrameArea().Width()
1963 // <--------------> is Width()
1964 // <-----------------> is what we need to be able to compare to X() (nTextFrameWidth)
1965 SwTwips nLeftMarginWidth = m_nLeftMargin - GetTextFrame()->getFrameArea().Left();
1966 SwTwips nTextFrameWidth = GetTextFrame()->getFrameArea().Width() - nLeftMarginWidth;
1968 // If there is one such tab portion, then text is allowed to use the full
1969 // text frame area to the right (RR above, but not LL).
1970 nLineWidth = nTextFrameWidth - X();
1972 if (!bTabOverMargin) // thus bTabOverSpacing only
1974 // right, center, decimal can back-fill all the available space - same as TabOverMargin
1975 if (pLastTab->GetWhichPor() == PortionType::TabLeft)
1976 nLineWidth = nTextFrameWidth - pLastTab->GetTabPos();
1978 return nLineWidth;
1981 SwTextSlot::SwTextSlot(
1982 const SwTextSizeInfo *pNew,
1983 const SwLinePortion *pPor,
1984 bool bTextLen,
1985 bool bExgLists,
1986 OUString const & rCh )
1987 : pOldText(nullptr)
1988 , m_pOldSmartTagList(nullptr)
1989 , m_pOldGrammarCheckList(nullptr)
1990 , nIdx(0)
1991 , nLen(0)
1992 , nMeasureLen(0)
1993 , pInf(nullptr)
1995 if( rCh.isEmpty() )
1997 bOn = pPor->GetExpText( *pNew, aText );
1999 else
2001 aText = rCh;
2002 bOn = true;
2005 // The text is replaced ...
2006 if( !bOn )
2007 return;
2009 pInf = const_cast<SwTextSizeInfo*>(pNew);
2010 nIdx = pInf->GetIdx();
2011 nLen = pInf->GetLen();
2012 nMeasureLen = pInf->GetMeasureLen();
2013 pOldText = &(pInf->GetText());
2014 m_pOldCachedVclData = pInf->GetCachedVclData();
2015 pInf->SetText( aText );
2016 pInf->SetIdx(TextFrameIndex(0));
2017 pInf->SetLen(bTextLen ? TextFrameIndex(pInf->GetText().getLength()) : pPor->GetLen());
2018 if (nMeasureLen != TextFrameIndex(COMPLETE_STRING))
2019 pInf->SetMeasureLen(TextFrameIndex(COMPLETE_STRING));
2021 pInf->SetCachedVclData(nullptr);
2023 // ST2
2024 if ( !bExgLists )
2025 return;
2027 m_pOldSmartTagList = static_cast<SwTextPaintInfo*>(pInf)->GetSmartTags();
2028 if (m_pOldSmartTagList)
2030 std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
2031 SwWrongList const*const pSmartTags(pos.first->GetSmartTags());
2032 if (pSmartTags)
2034 const sal_uInt16 nPos = pSmartTags->GetWrongPos(pos.second);
2035 const sal_Int32 nListPos = pSmartTags->Pos(nPos);
2036 if (nListPos == pos.second && pSmartTags->SubList(nPos) != nullptr)
2038 m_pTempIter.reset(new sw::WrongListIterator(*pSmartTags->SubList(nPos)));
2039 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
2041 else if (!m_pTempList && nPos < pSmartTags->Count()
2042 && nListPos < pos.second && !aText.isEmpty())
2044 m_pTempList.reset(new SwWrongList( WRONGLIST_SMARTTAG ));
2045 m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
2046 m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
2047 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
2049 else
2050 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
2052 else
2053 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
2055 m_pOldGrammarCheckList = static_cast<SwTextPaintInfo*>(pInf)->GetGrammarCheckList();
2056 if (!m_pOldGrammarCheckList)
2057 return;
2059 std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
2060 SwWrongList const*const pGrammar(pos.first->GetGrammarCheck());
2061 if (pGrammar)
2063 const sal_uInt16 nPos = pGrammar->GetWrongPos(pos.second);
2064 const sal_Int32 nListPos = pGrammar->Pos(nPos);
2065 if (nListPos == pos.second && pGrammar->SubList(nPos) != nullptr)
2067 m_pTempIter.reset(new sw::WrongListIterator(*pGrammar->SubList(nPos)));
2068 static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
2070 else if (!m_pTempList && nPos < pGrammar->Count()
2071 && nListPos < pos.second && !aText.isEmpty())
2073 m_pTempList.reset(new SwWrongList( WRONGLIST_GRAMMAR ));
2074 m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
2075 m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
2076 static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
2078 else
2079 static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
2081 else
2082 static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
2085 SwTextSlot::~SwTextSlot()
2087 if( !bOn )
2088 return;
2090 pInf->SetCachedVclData(m_pOldCachedVclData);
2091 pInf->SetText( *pOldText );
2092 pInf->SetIdx( nIdx );
2093 pInf->SetLen( nLen );
2094 pInf->SetMeasureLen( nMeasureLen );
2096 // ST2
2097 // Restore old smart tag list
2098 if (m_pOldSmartTagList)
2099 static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pOldSmartTagList);
2100 if (m_pOldGrammarCheckList)
2101 static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pOldGrammarCheckList);
2104 SwFontSave::SwFontSave(const SwTextSizeInfo &rInf, SwFont *pNew,
2105 SwAttrIter* pItr)
2106 : pInf(nullptr)
2107 , pFnt(pNew ? const_cast<SwTextSizeInfo&>(rInf).GetFont() : nullptr)
2108 , pIter(nullptr)
2110 if( !pFnt )
2111 return;
2113 pInf = &const_cast<SwTextSizeInfo&>(rInf);
2114 // In these cases we temporarily switch to the new font:
2115 // 1. the fonts have a different magic number
2116 // 2. they have different script types
2117 // 3. their background colors differ (this is not covered by 1.)
2118 if( pFnt->DifferentFontCacheId( pNew, pFnt->GetActual() ) ||
2119 pNew->GetActual() != pFnt->GetActual() ||
2120 ( ! pNew->GetBackColor() && pFnt->GetBackColor() ) ||
2121 ( pNew->GetBackColor() && ! pFnt->GetBackColor() ) ||
2122 ( pNew->GetBackColor() && pFnt->GetBackColor() &&
2123 ( *pNew->GetBackColor() != *pFnt->GetBackColor() ) ) )
2125 pNew->SetTransparent( true );
2126 pNew->SetAlign( ALIGN_BASELINE );
2127 pInf->SetFont( pNew );
2129 else
2130 pFnt = nullptr;
2131 pNew->Invalidate();
2132 pNew->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );
2133 if( pItr && pItr->GetFnt() == pFnt )
2135 pIter = pItr;
2136 pIter->SetFnt( pNew );
2140 SwFontSave::~SwFontSave()
2142 if( pFnt )
2144 // Reset SwFont
2145 pFnt->Invalidate();
2146 pInf->SetFont( pFnt );
2147 if( pIter )
2149 pIter->SetFnt( pFnt );
2150 pIter->m_nPosition = COMPLETE_STRING;
2155 bool SwTextFormatInfo::ChgHyph( const bool bNew )
2157 const bool bOld = m_bAutoHyph;
2158 if( m_bAutoHyph != bNew )
2160 m_bAutoHyph = bNew;
2161 InitHyph( bNew );
2162 // Set language in the Hyphenator
2163 if( m_pFnt )
2164 m_pFnt->ChgPhysFnt( m_pVsh, *m_pOut );
2166 return bOld;
2170 bool SwTextFormatInfo::CheckCurrentPosBookmark()
2172 if (m_nLastBookmarkPos != GetIdx())
2174 m_nLastBookmarkPos = GetIdx();
2175 return true;
2177 else
2179 return false;
2183 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */