1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <i18nlangtag/mslangid.hxx>
23 #include <officecfg/Office/Common.hxx>
24 #include <vcl/outdev.hxx>
25 #include <vcl/kernarray.hxx>
26 #include <vcl/lineinfo.hxx>
27 #include <vcl/metric.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/lazydelete.hxx>
30 #include <vcl/glyphitemcache.hxx>
31 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
32 #include <com/sun/star/i18n/WordType.hpp>
33 #include <com/sun/star/i18n/XBreakIterator.hpp>
34 #include <breakit.hxx>
35 #include <paintfrm.hxx>
37 #include <viewopt.hxx>
38 #include <fntcache.hxx>
39 #include <IDocumentSettingAccess.hxx>
43 #include <pagefrm.hxx>
44 #include <tgrditem.hxx>
45 #include <scriptinfo.hxx>
46 #include <editeng/brushitem.hxx>
47 #include <accessibilityoptions.hxx>
48 #include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
50 #include <editeng/fhgtitem.hxx>
51 #include <vcl/glyphitem.hxx>
52 #include <vcl/vcllayout.hxx>
54 #include <strings.hrc>
56 #include <vcl/outdev/ScopedStates.hxx>
57 #include <o3tl/hash_combine.hxx>
60 #include "justify.hxx"
62 using namespace ::com::sun::star
;
64 // global variables declared in fntcache.hxx
65 // FontCache is created in txtinit.cxx TextInit_ and deleted in TextFinit
66 SwFntCache
*pFntCache
= nullptr;
67 // last Font set by ChgFntCache
68 SwFntObj
*pLastFont
= nullptr;
70 constexpr Color
gWaveCol(COL_GRAY
);
72 tools::Long
SwFntObj::s_nPixWidth
;
73 MapMode
* SwFntObj::s_pPixMap
= nullptr;
74 static vcl::DeleteOnDeinit
< VclPtr
<OutputDevice
> > s_pFntObjPixOut
{};
76 void SwFntCache::Flush( )
86 SwFntObj::SwFntObj(const SwSubFont
&rFont
, std::uintptr_t nFontCacheId
, SwViewShell
const *pSh
)
87 : SwCacheObj(reinterpret_cast<void *>(nFontCacheId
))
90 , m_pPrtFont(&m_aFont
)
92 , m_nGuessedLeading(USHRT_MAX
)
93 , m_nExtLeading(USHRT_MAX
)
95 , m_nPrtAscent(USHRT_MAX
)
97 , m_nPrtHeight(USHRT_MAX
)
98 , m_nPropWidth(rFont
.GetPropWidth())
99 , m_nScrHangingBaseline(0)
100 , m_nPrtHangingBaseline(0)
102 m_nZoom
= pSh
? pSh
->GetViewOptions()->GetZoom() : USHRT_MAX
;
103 m_bSymbol
= RTL_TEXTENCODING_SYMBOL
== m_aFont
.GetCharSet();
104 m_bPaintBlank
= ( LINESTYLE_NONE
!= m_aFont
.GetUnderline()
105 || LINESTYLE_NONE
!= m_aFont
.GetOverline()
106 || STRIKEOUT_NONE
!= m_aFont
.GetStrikeout() )
107 && !m_aFont
.IsWordLineMode();
108 m_aFont
.SetLanguage(rFont
.GetLanguage());
111 SwFntObj::~SwFntObj()
113 if ( m_pScrFont
!= m_pPrtFont
)
115 if ( m_pPrtFont
!= &m_aFont
)
119 void SwFntObj::CreatePrtFont( const OutputDevice
& rPrt
)
121 if ( m_nPropWidth
== 100 || m_pPrinter
== &rPrt
)
124 if( m_pScrFont
!= m_pPrtFont
)
126 if( m_pPrtFont
!= &m_aFont
)
129 const vcl::Font
aOldFnt( rPrt
.GetFont() );
130 const_cast<OutputDevice
&>(rPrt
).SetFont( m_aFont
);
131 const FontMetric
aWinMet( rPrt
.GetFontMetric() );
132 const_cast<OutputDevice
&>(rPrt
).SetFont( aOldFnt
);
133 auto nWidth
= ( aWinMet
.GetFontSize().Width() * m_nPropWidth
) / 100;
137 m_pPrtFont
= new vcl::Font( m_aFont
);
138 m_pPrtFont
->SetFontSize( Size( nWidth
, m_aFont
.GetFontSize().Height() ) );
139 m_pScrFont
= nullptr;
144 * returns whether we have to adjust the output font to resemble
145 * the formatting font
149 * 1. RefDef == OutDev (text formatting, online layout...)
150 * 2. PDF export from online layout
151 * 3. Prospect/PagePreview printing
153 static bool lcl_IsFontAdjustNecessary( const vcl::RenderContext
& rOutDev
,
154 const vcl::RenderContext
& rRefDev
)
156 return &rRefDev
!= &rOutDev
&&
157 OUTDEV_WINDOW
!= rRefDev
.GetOutDevType() &&
158 ( OUTDEV_PRINTER
!= rRefDev
.GetOutDevType() ||
159 OUTDEV_PRINTER
!= rOutDev
.GetOutDevType() );
164 struct CalcLinePosData
166 SwDrawTextInfo
& rInf
;
169 const bool bSwitchH2V
;
170 const bool bSwitchH2VLRBT
;
171 const bool bSwitchL2R
;
172 tools::Long nHalfSpace
;
173 KernArray
& rKernArray
;
176 CalcLinePosData( SwDrawTextInfo
& _rInf
, vcl::Font
& _rFont
,
177 TextFrameIndex
const _nCnt
, const bool _bSwitchH2V
, const bool _bSwitchH2VLRBT
, const bool _bSwitchL2R
,
178 tools::Long _nHalfSpace
, KernArray
& _rKernArray
, const bool _bBidiPor
) :
182 bSwitchH2V( _bSwitchH2V
),
183 bSwitchH2VLRBT( _bSwitchH2VLRBT
),
184 bSwitchL2R( _bSwitchL2R
),
185 nHalfSpace( _nHalfSpace
),
186 rKernArray( _rKernArray
),
187 bBidiPor( _bBidiPor
)
194 // Computes the start and end position of an underline. This function is called
195 // from the DrawText-method (for underlining misspelled words or smarttag terms).
196 static void lcl_calcLinePos( const CalcLinePosData
&rData
,
197 Point
&rStart
, Point
&rEnd
, TextFrameIndex
const nStart
, TextFrameIndex
const nWrLen
)
199 tools::Long nBlank
= 0;
200 const TextFrameIndex nEnd
= nStart
+ nWrLen
;
201 const tools::Long nTmpSpaceAdd
= rData
.rInf
.GetSpace() / SPACING_PRECISION_FACTOR
;
203 if ( nEnd
< rData
.nCnt
204 && CH_BLANK
== rData
.rInf
.GetText()[sal_Int32(rData
.rInf
.GetIdx() + nEnd
)] )
206 if (nEnd
+ TextFrameIndex(1) == rData
.nCnt
)
207 nBlank
-= nTmpSpaceAdd
;
209 nBlank
-= rData
.nHalfSpace
;
212 // determine start, end and length of wave line
213 sal_Int32 nKernStart
= nStart
? rData
.rKernArray
[sal_Int32(nStart
) - 1] : 0;
214 sal_Int32 nKernEnd
= rData
.rKernArray
[sal_Int32(nEnd
) - 1];
216 const Degree10 nDir
= rData
.bBidiPor
? 1800_deg10
217 : UnMapDirection(rData
.rFont
.GetOrientation(),
218 rData
.bSwitchH2V
, rData
.bSwitchH2VLRBT
);
220 switch ( nDir
.get() )
223 rStart
.AdjustX(nKernStart
);
224 rEnd
.setX( nBlank
+ rData
.rInf
.GetPos().X() + nKernEnd
);
225 rEnd
.setY( rData
.rInf
.GetPos().Y() );
228 rStart
.AdjustY( -nKernStart
);
229 rEnd
.setX( rData
.rInf
.GetPos().X() );
230 rEnd
.setY( nBlank
+ rData
.rInf
.GetPos().Y() - nKernEnd
);
233 rStart
.AdjustX( -nKernStart
);
234 rEnd
.setX( rData
.rInf
.GetPos().X() - nKernEnd
- nBlank
);
235 rEnd
.setY( rData
.rInf
.GetPos().Y() );
238 rStart
.AdjustY(nKernStart
);
239 rEnd
.setX( rData
.rInf
.GetPos().X() );
240 rEnd
.setY( nBlank
+ rData
.rInf
.GetPos().Y() + nKernEnd
);
245 // if start < end, OutputDevice::DrawWaveLine() will think it is a rotated
246 // line, so we swap nStart and nEnd to avoid this.
247 if ( rData
.bBidiPor
)
248 std::swap(rStart
, rEnd
);
250 if ( rData
.bSwitchL2R
)
252 rData
.rInf
.GetFrame()->SwitchLTRtoRTL( rStart
);
253 rData
.rInf
.GetFrame()->SwitchLTRtoRTL( rEnd
);
256 // We need to do this here as well for LTR text in a RTL paragraph.
257 std::swap(rStart
, rEnd
);
260 if ( rData
.bSwitchH2V
)
262 rData
.rInf
.GetFrame()->SwitchHorizontalToVertical( rStart
);
263 rData
.rInf
.GetFrame()->SwitchHorizontalToVertical( rEnd
);
267 // Returns the Ascent of the Font on the given output device;
268 // it may be necessary to create the screen font first.
269 sal_uInt16
SwFntObj::GetFontAscent( const SwViewShell
*pSh
, const OutputDevice
& rOut
)
272 const OutputDevice
& rRefDev
= pSh
? pSh
->GetRefDev() : rOut
;
274 if ( pSh
&& lcl_IsFontAdjustNecessary( rOut
, rRefDev
) )
276 CreateScrFont( *pSh
, rOut
);
277 OSL_ENSURE( USHRT_MAX
!= m_nScrAscent
, "nScrAscent is going berzerk" );
282 if (m_nPrtAscent
== USHRT_MAX
) // printer ascent unknown?
284 CreatePrtFont( rOut
);
285 const vcl::Font
aOldFnt( rRefDev
.GetFont() );
286 const_cast<OutputDevice
&>(rRefDev
).SetFont( *m_pPrtFont
);
287 const FontMetric
aOutMet( rRefDev
.GetFontMetric() );
288 m_nPrtAscent
= o3tl::narrowing
<sal_uInt16
>(aOutMet
.GetAscent());
289 m_nPrtHangingBaseline
= o3tl::narrowing
<sal_uInt16
>(aOutMet
.GetHangingBaseline());
290 const_cast<OutputDevice
&>(rRefDev
).SetFont( aOldFnt
);
296 #if !defined(MACOSX) // #i89844# extleading is below the line for Mac
297 // TODO: move extleading below the line for all platforms too
298 nRet
+= GetFontLeading( pSh
, rRefDev
);
301 OSL_ENSURE( USHRT_MAX
!= nRet
, "GetFontAscent returned USHRT_MAX" );
305 // Returns the height of the Font on the given output device;
306 // it may be necessary to create the screen font first.
307 sal_uInt16
SwFntObj::GetFontHeight( const SwViewShell
* pSh
, const OutputDevice
& rOut
)
310 const OutputDevice
& rRefDev
= pSh
? pSh
->GetRefDev() : rOut
;
312 if ( pSh
&& lcl_IsFontAdjustNecessary( rOut
, rRefDev
) )
314 CreateScrFont( *pSh
, rOut
);
315 OSL_ENSURE( USHRT_MAX
!= m_nScrHeight
, "nScrHeight is going berzerk" );
316 nRet
= m_nScrHeight
+ GetFontLeading( pSh
, rRefDev
);
320 if (m_nPrtHeight
== USHRT_MAX
) // printer height unknown?
322 CreatePrtFont( rOut
);
323 const vcl::Font
aOldFnt( rRefDev
.GetFont() );
324 const_cast<OutputDevice
&>(rRefDev
).SetFont( *m_pPrtFont
);
325 m_nPrtHeight
= o3tl::narrowing
<sal_uInt16
>(rRefDev
.GetTextHeight());
327 #if OSL_DEBUG_LEVEL > 0
328 // Check if vcl did not change the meaning of GetTextHeight
329 const FontMetric
aOutMet( rRefDev
.GetFontMetric() );
330 tools::Long nTmpPrtHeight
= o3tl::narrowing
<sal_uInt16
>(aOutMet
.GetAscent()) + aOutMet
.GetDescent();
331 // #i106098#: do not compare with == here due to rounding error
332 OSL_ENSURE( std::abs(nTmpPrtHeight
- m_nPrtHeight
) < 3,
333 "GetTextHeight != Ascent + Descent" );
336 const_cast<OutputDevice
&>(rRefDev
).SetFont( aOldFnt
);
339 nRet
= m_nPrtHeight
+ GetFontLeading( pSh
, rRefDev
);
342 OSL_ENSURE( USHRT_MAX
!= nRet
, "GetFontHeight returned USHRT_MAX" );
346 sal_uInt16
SwFntObj::GetFontLeading( const SwViewShell
*pSh
, const OutputDevice
& rOut
)
352 if ( USHRT_MAX
== m_nGuessedLeading
|| USHRT_MAX
== m_nExtLeading
)
354 SolarMutexGuard aGuard
;
356 const vcl::Font
aOldFnt( rOut
.GetFont() );
357 const_cast<OutputDevice
&>(rOut
).SetFont( *m_pPrtFont
);
358 const FontMetric
aMet( rOut
.GetFontMetric() );
359 const_cast<OutputDevice
&>(rOut
).SetFont( aOldFnt
);
360 m_bSymbol
= RTL_TEXTENCODING_SYMBOL
== aMet
.GetCharSet();
361 GuessLeading( *pSh
, aMet
);
362 m_nExtLeading
= o3tl::narrowing
<sal_uInt16
>(aMet
.GetExternalLeading());
363 /* HACK: FIXME There is something wrong with Writer's bullet rendering, causing lines
364 with bullets to be higher than they should be. I think this is because
365 Writer uses font's external leading incorrect, as the vertical distance
366 added to every line instead of only a distance between multiple lines,
367 which means a single bullet has external leading added even though it
368 shouldn't, but frankly this is just an educated guess rather than understanding
369 Writer's layout (heh).
370 Symbol font in some documents is 'StarSymbol; Arial Unicode MS', and Windows
371 machines often do not have StarSymbol, falling back to Arial Unicode MS, which
372 has unusually high external leading. So just reset external leading for fonts
373 which are used to bullets, as those should not be used on multiple lines anyway,
374 so in correct rendering external leading should be irrelevant anyway.
375 Interestingly enough, bSymbol is false for 'StarSymbol; Arial Unicode MS', so
376 also check explicitly.
378 if( m_bSymbol
|| IsOpenSymbol( m_pPrtFont
->GetFamilyName()))
382 const IDocumentSettingAccess
& rIDSA
= pSh
->getIDocumentSettingAccess();
383 const bool bBrowse
= ( pSh
->GetWin() &&
384 pSh
->GetViewOptions()->getBrowseMode() &&
385 !pSh
->GetViewOptions()->IsPrtFormat() );
387 if ( !bBrowse
&& rIDSA
.get(DocumentSettingId::ADD_EXT_LEADING
) )
388 nRet
= m_nExtLeading
;
390 nRet
= m_nGuessedLeading
;
393 OSL_ENSURE( USHRT_MAX
!= nRet
, "GetFontLeading returned USHRT_MAX" );
397 sal_uInt16
SwFntObj::GetFontHangingBaseline( const SwViewShell
* pSh
, const OutputDevice
& rOut
)
400 const OutputDevice
& rRefDev
= pSh
? pSh
->GetRefDev() : rOut
;
402 GetFontAscent(pSh
, rOut
);
404 if ( pSh
&& lcl_IsFontAdjustNecessary( rOut
, rRefDev
) )
405 nRet
= m_nScrHangingBaseline
;
407 nRet
= m_nPrtHangingBaseline
;
412 // pOut is the output device, not the reference device
413 void SwFntObj::CreateScrFont( const SwViewShell
& rSh
, const OutputDevice
& rOut
)
418 // any changes to the output device are reset at the end of the function
419 OutputDevice
* pOut
= const_cast<OutputDevice
*>(&rOut
);
422 vcl::Font
aOldOutFont( pOut
->GetFont() );
424 m_nScrHeight
= USHRT_MAX
;
426 // Condition for output font / refdev font adjustment
427 OutputDevice
* pPrt
= &rSh
.GetRefDev();
430 !rSh
.GetViewOptions()->getBrowseMode() ||
431 rSh
.GetViewOptions()->IsPrtFormat() )
433 // After CreatePrtFont m_pPrtFont is the font which is actually used
434 // by the reference device
435 CreatePrtFont( *pPrt
);
438 // save old reference device font
439 vcl::Font
aOldPrtFnt( pPrt
->GetFont() );
441 // set the font used at the reference device at the reference device
442 // and the output device
443 pPrt
->SetFont( *m_pPrtFont
);
444 pOut
->SetFont( *m_pPrtFont
);
446 // This should be the default for pScrFont.
447 m_pScrFont
= m_pPrtFont
;
449 FontMetric aMet
= pPrt
->GetFontMetric( );
450 // Don't lose "faked" properties of the logical font that don't truly
451 // exist in the physical font metrics which vcl which fake up for us
452 aMet
.SetWeight(m_pScrFont
->GetWeight());
453 aMet
.SetItalic(m_pScrFont
->GetItalic());
455 m_bSymbol
= RTL_TEXTENCODING_SYMBOL
== aMet
.GetCharSet();
457 if ( USHRT_MAX
== m_nGuessedLeading
)
458 GuessLeading( rSh
, aMet
);
460 if ( USHRT_MAX
== m_nExtLeading
)
461 m_nExtLeading
= o3tl::narrowing
<sal_uInt16
>(aMet
.GetExternalLeading());
463 // reset the original reference device font
464 pPrt
->SetFont( aOldPrtFnt
);
468 m_bSymbol
= RTL_TEXTENCODING_SYMBOL
== m_aFont
.GetCharSet();
469 if ( m_nGuessedLeading
== USHRT_MAX
)
470 m_nGuessedLeading
= 0;
472 // no external leading in browse mode
473 if ( m_nExtLeading
== USHRT_MAX
)
476 m_pScrFont
= m_pPrtFont
;
479 m_nScrAscent
= o3tl::narrowing
<sal_uInt16
>(pOut
->GetFontMetric().GetAscent());
480 m_nScrHangingBaseline
= o3tl::narrowing
<sal_uInt16
>(pOut
->GetFontMetric().GetHangingBaseline());
481 if ( USHRT_MAX
== m_nScrHeight
)
482 m_nScrHeight
= o3tl::narrowing
<sal_uInt16
>(pOut
->GetTextHeight());
484 // reset original output device font
485 pOut
->SetFont( aOldOutFont
);
488 void SwFntObj::GuessLeading( const SwViewShell
&
492 , const FontMetric
& rMet
)
494 // If leading >= 5, this seems to be enough leading.
495 // Nothing has to be done.
496 if ( rMet
.GetInternalLeading() >= 5 )
498 m_nGuessedLeading
= 0;
503 OutputDevice
*pWin
= rSh
.GetWin() ?
504 rSh
.GetWin()->GetOutDev() :
505 Application::GetDefaultDevice();
508 MapMode
aTmpMap( MapUnit::MapTwip
);
509 MapMode aOldMap
= pWin
->GetMapMode( );
510 pWin
->SetMapMode( aTmpMap
);
511 const vcl::Font
aOldFnt( pWin
->GetFont() );
512 pWin
->SetFont( *m_pPrtFont
);
513 const FontMetric
aWinMet( pWin
->GetFontMetric() );
514 const sal_uInt16 nWinHeight
= sal_uInt16( aWinMet
.GetFontSize().Height() );
515 if( m_pPrtFont
->GetFamilyName().indexOf( aWinMet
.GetFamilyName() ) != -1 )
517 // If the Leading on the Window is also 0, then it has to stay
518 // that way (see also StarMath).
519 tools::Long nTmpLeading
= aWinMet
.GetInternalLeading();
520 if( nTmpLeading
<= 0 )
522 pWin
->SetFont( rMet
);
523 nTmpLeading
= pWin
->GetFontMetric().GetInternalLeading();
524 if( nTmpLeading
< 0 )
525 m_nGuessedLeading
= 0;
527 m_nGuessedLeading
= sal_uInt16(nTmpLeading
);
531 m_nGuessedLeading
= sal_uInt16(nTmpLeading
);
532 // Manta-Hack #50153#:
533 // Wer beim Leading luegt, luegt moeglicherweise auch beim
534 // Ascent/Descent, deshalb wird hier ggf. der Font ein wenig
535 // tiefergelegt, ohne dabei seine Hoehe zu aendern.
536 // (above original comment preserved for cultural reasons)
537 // Those who lie about their Leading, may lie about their
538 // Ascent/Descent as well, hence the Font will be lowered a
539 // little without changing its height.
540 tools::Long nDiff
= std::min( rMet
.GetDescent() - aWinMet
.GetDescent(),
541 aWinMet
.GetAscent() - rMet
.GetAscent() - nTmpLeading
);
544 OSL_ENSURE( m_nPrtAscent
< USHRT_MAX
, "GuessLeading: PrtAscent-Fault" );
545 if ( m_nPrtAscent
< USHRT_MAX
)
546 m_nPrtAscent
= m_nPrtAscent
+ o3tl::narrowing
<sal_uInt16
>(( 2 * nDiff
) / 5);
552 // If all else fails, take 15% of the height, as empirically
554 m_nGuessedLeading
= (nWinHeight
* 15) / 100;
556 pWin
->SetFont( aOldFnt
);
557 pWin
->SetMapMode( aOldMap
);
560 m_nGuessedLeading
= 0;
562 m_nGuessedLeading
= 0;
566 // Set the font at the given output device; for screens it may be
567 // necessary to do some adjustment first.
568 void SwFntObj::SetDevFont( const SwViewShell
*pSh
, OutputDevice
& rOut
)
570 const OutputDevice
& rRefDev
= pSh
? pSh
->GetRefDev() : rOut
;
572 if ( pSh
&& lcl_IsFontAdjustNecessary( rOut
, rRefDev
) )
574 CreateScrFont( *pSh
, rOut
);
575 if( !GetScrFont()->IsSameInstance( rOut
.GetFont() ) )
576 rOut
.SetFont( *m_pScrFont
);
577 if( m_pPrinter
&& ( !m_pPrtFont
->IsSameInstance( m_pPrinter
->GetFont() ) ) )
578 m_pPrinter
->SetFont( *m_pPrtFont
);
582 CreatePrtFont( rOut
);
583 if( !m_pPrtFont
->IsSameInstance( rOut
.GetFont() ) )
584 rOut
.SetFont( *m_pPrtFont
);
587 // Here, we actually do not need the leading values, but by calling
588 // GetFontLeading() we assure that the values are calculated for later use.
589 GetFontLeading( pSh
, rRefDev
);
592 #define WRONG_SHOW_MIN 5
596 * on screen => DrawTextArray
597 * on printer, !Kerning => DrawText
598 * on printer + Kerning => DrawStretchText
600 static bool lcl_IsMonoSpaceFont( const vcl::RenderContext
& rOut
)
602 const tools::Long nWidth1
= rOut
.GetTextWidth( OUString( u
'\x3008' ) );
603 const tools::Long nWidth2
= rOut
.GetTextWidth( OUString( u
'\x307C' ) );
604 return nWidth1
== nWidth2
;
607 static bool lcl_IsFullstopCentered( const vcl::RenderContext
& rOut
)
609 const FontMetric
aMetric( rOut
.GetFontMetric() );
610 return aMetric
.IsFullstopCentered() ;
613 /* This helper structure (SwForbidden) contains the already marked parts of the string
614 to avoid double lines (e.g grammar + spell check error) */
616 typedef std::vector
<std::pair
<TextFrameIndex
, TextFrameIndex
>> SwForbidden
;
618 static void lcl_DrawLineForWrongListData(
619 SwForbidden
&rForbidden
,
620 const SwDrawTextInfo
&rInf
,
621 sw::WrongListIterator
*pWList
,
622 const CalcLinePosData
&rCalcLinePosData
,
623 const Size
&rPrtFontSize
)
627 TextFrameIndex nStart
= rInf
.GetIdx();
628 TextFrameIndex nWrLen
= rInf
.GetLen();
630 // check if respective data is available in the current text range
631 if (!pWList
->Check( nStart
, nWrLen
))
636 tools::Long nHght
= rInf
.GetOut().LogicToPixel( rPrtFontSize
).Height();
638 // Draw wavy lines for spell and grammar errors only if font is large enough.
639 // Lines for smart tags will always be drawn.
640 if (pWList
!= rInf
.GetSmartTags() && WRONG_SHOW_MIN
>= nHght
)
645 SwForbidden::iterator pIter
= rForbidden
.begin();
646 if (rInf
.GetOut().GetConnectMetaFile())
647 rInf
.GetOut().Push();
649 const Color
aCol( rInf
.GetOut().GetLineColor() );
651 // iterate over all ranges stored in the respective SwWrongList
654 nStart
-= rInf
.GetIdx();
656 const TextFrameIndex nEnd
= nStart
+ nWrLen
;
657 TextFrameIndex nNext
= nStart
;
658 while( nNext
< nEnd
)
660 while( pIter
!= rForbidden
.end() && pIter
->second
<= nNext
)
663 const TextFrameIndex nNextStart
= nNext
;
664 TextFrameIndex nNextEnd
= nEnd
;
666 if( pIter
== rForbidden
.end() || nNextEnd
<= pIter
->first
)
668 // No overlapping mark up found
669 rForbidden
.insert(pIter
, std::make_pair(nNextStart
, nNextEnd
));
670 pIter
= rForbidden
.begin();
675 nNext
= pIter
->second
;
676 if( nNextStart
< pIter
->first
)
678 nNextEnd
= pIter
->first
;
679 pIter
->first
= nNextStart
;
684 // determine line pos
685 Point
aStart( rInf
.GetPos() );
687 lcl_calcLinePos( rCalcLinePosData
, aStart
, aEnd
, nNextStart
, nNextEnd
- nNextStart
);
689 SwWrongArea
const*const wrongArea
= pWList
->GetWrongElement(nNextStart
+ rInf
.GetIdx());
690 if (wrongArea
!= nullptr)
692 const SwViewShell
* pShell
= rInf
.GetShell();
693 sal_uInt16 nZoom
= pShell
? round(pShell
->GetViewOptions()->GetZoom()/100) : 1;
694 if (WRONGAREA_WAVE
== wrongArea
->mLineType
)
696 vcl::ScopedAntialiasing
a(rInf
.GetOut(), true);
697 rInf
.GetOut().SetLineColor( wrongArea
->mColor
);
698 rInf
.GetOut().DrawWaveLine( aStart
, aEnd
, 1 + nZoom
, 3 + nZoom
);
700 else if (WRONGAREA_BOLDWAVE
== wrongArea
->mLineType
)
702 vcl::ScopedAntialiasing
a(rInf
.GetOut(), true);
703 rInf
.GetOut().SetLineColor( wrongArea
->mColor
);
704 rInf
.GetOut().DrawWaveLine( aStart
, aEnd
, 2 + nZoom
, 4 + nZoom
);
706 else if (WRONGAREA_BOLD
== wrongArea
->mLineType
)
708 rInf
.GetOut().SetLineColor( wrongArea
->mColor
);
713 LineInfo
aLineInfo( LineStyle::Solid
, 26 );
715 rInf
.GetOut().DrawLine( aStart
, aEnd
, aLineInfo
);
717 else if (WRONGAREA_DASHED
== wrongArea
->mLineType
)
719 rInf
.GetOut().SetLineColor( wrongArea
->mColor
);
724 LineInfo
aLineInfo( LineStyle::Dash
);
725 aLineInfo
.SetDistance( 40 );
726 aLineInfo
.SetDashLen( 1 );
727 aLineInfo
.SetDashCount(1);
729 rInf
.GetOut().DrawLine( aStart
, aEnd
, aLineInfo
);
734 nStart
= nEnd
+ rInf
.GetIdx();
735 nWrLen
= rInf
.GetIdx() + rInf
.GetLen() - nStart
;
737 while (nWrLen
&& pWList
->Check( nStart
, nWrLen
));
739 rInf
.GetOut().SetLineColor( aCol
);
741 if (rInf
.GetOut().GetConnectMetaFile())
745 static void GetTextArray(const OutputDevice
& rDevice
, const OUString
& rStr
, KernArray
& rDXAry
,
746 sal_Int32 nIndex
, sal_Int32 nLen
, bool bCaret
= false,
747 const vcl::text::TextLayoutCache
* layoutCache
= nullptr)
749 const SalLayoutGlyphs
* pLayoutCache
= SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rDevice
, rStr
, nIndex
, nLen
,
751 rDevice
.GetTextArray(rStr
, &rDXAry
, nIndex
, nLen
, bCaret
, layoutCache
, pLayoutCache
);
754 static void GetTextArray(const OutputDevice
& rOutputDevice
, const SwDrawTextInfo
& rInf
, KernArray
& rDXAry
,
757 return GetTextArray(rOutputDevice
, rInf
.GetText(), rDXAry
, rInf
.GetIdx().get(), rInf
.GetLen().get(),
758 bCaret
, rInf
.GetVclCache());
761 static void GetTextArray(const OutputDevice
& rOutputDevice
, const SwDrawTextInfo
& rInf
, KernArray
& rDXAry
,
762 sal_Int32 nLen
, bool bCaret
= false)
764 // Substring is fine.
765 assert( nLen
<= rInf
.GetLen().get());
766 return GetTextArray(rOutputDevice
, rInf
.GetText(), rDXAry
, rInf
.GetIdx().get(), nLen
, bCaret
, rInf
.GetVclCache());
769 void SwFntObj::DrawText( SwDrawTextInfo
&rInf
)
771 OSL_ENSURE( rInf
.GetShell(), "SwFntObj::DrawText without shell" );
773 OutputDevice
& rRefDev
= rInf
.GetShell()->GetRefDev();
774 vcl::Window
* pWin
= rInf
.GetShell()->GetWin();
776 // true if pOut is the printer and the printer has been used for formatting
777 const bool bPrt
= OUTDEV_PRINTER
== rInf
.GetOut().GetOutDevType() &&
778 OUTDEV_PRINTER
== rRefDev
.GetOutDevType();
779 const bool bBrowse
= ( pWin
&&
780 rInf
.GetShell()->GetViewOptions()->getBrowseMode() &&
781 !rInf
.GetShell()->GetViewOptions()->IsPrtFormat() &&
783 ( rInf
.GetSpace() || !rInf
.GetKern() ) &&
785 !rInf
.GetGrammarCheck() &&
786 !rInf
.GetSmartTags() &&
787 !rInf
.GetGreyWave() );
789 // bDirectPrint indicates that we can enter the branch which calls
790 // the DrawText functions instead of calling the DrawTextArray functions
791 const bool bDirectPrint
= bPrt
|| bBrowse
;
793 // Condition for output font / refdev font adjustment
794 const bool bUseScrFont
=
795 lcl_IsFontAdjustNecessary( rInf
.GetOut(), rRefDev
);
797 vcl::Font
* pTmpFont
= bUseScrFont
? m_pScrFont
: m_pPrtFont
;
799 // bDirectPrint and bUseScrFont should have these values:
801 // Outdev / RefDef | Printer | VirtPrinter | Window
803 // Printer | 1 - 0 | 0 - 1 | -
805 // VirtPrinter/PDF | 0 - 1 | 0 - 1 | -
807 // Window/VirtWindow| 0 - 1 | 0 - 1 | 1 - 0
809 // Exception: During painting of a Writer OLE object, we do not have
810 // a window. Therefore bUseSrcFont is always 0 in this case.
812 #if OSL_DEBUG_LEVEL > 0
814 const bool bNoAdjust
= bPrt
||
816 rInf
.GetShell()->GetViewOptions()->getBrowseMode() &&
817 !rInf
.GetShell()->GetViewOptions()->IsPrtFormat() );
819 if ( OUTDEV_PRINTER
== rInf
.GetOut().GetOutDevType() )
822 if ( OUTDEV_PRINTER
== rRefDev
.GetOutDevType() )
824 OSL_ENSURE( bNoAdjust
&& !bUseScrFont
, "Outdev Check failed" );
826 else if ( rRefDev
.IsVirtual() )
828 OSL_ENSURE( !bNoAdjust
&& bUseScrFont
, "Outdev Check failed" );
832 OSL_FAIL( "Outdev Check failed" );
835 else if ( rInf
.GetOut().IsVirtual() && ! pWin
)
838 if ( OUTDEV_PRINTER
== rRefDev
.GetOutDevType() )
840 OSL_ENSURE( !bNoAdjust
&& bUseScrFont
, "Outdev Check failed" );
842 else if ( rRefDev
.IsVirtual() )
844 OSL_ENSURE( !bNoAdjust
&& bUseScrFont
, "Outdev Check failed" );
848 OSL_FAIL( "Outdev Check failed" );
851 else if ( OUTDEV_WINDOW
== rInf
.GetOut().GetOutDevType() ||
852 ( rInf
.GetOut().IsVirtual() && pWin
) )
854 // Window or virtual window
855 if ( OUTDEV_PRINTER
== rRefDev
.GetOutDevType() )
857 OSL_ENSURE( !bNoAdjust
&& bUseScrFont
, "Outdev Check failed" );
859 else if ( rRefDev
.IsVirtual() )
861 OSL_ENSURE( !bNoAdjust
&& bUseScrFont
, "Outdev Check failed" );
863 else if ( OUTDEV_WINDOW
== rRefDev
.GetOutDevType() )
865 OSL_ENSURE( bNoAdjust
&& !bUseScrFont
, "Outdev Check failed" );
869 OSL_FAIL( "Outdev Check failed" );
874 OSL_FAIL( "Outdev Check failed" );
879 // robust: better use the printer font instead of using no font at all
880 OSL_ENSURE( pTmpFont
, "No screen or printer font?" );
882 pTmpFont
= m_pPrtFont
;
884 // HACK: LINESTYLE_WAVE must not be abused any more, hence the grey wave
885 // line of the ExtendedAttributeSets will appear in the font color first
887 const bool bSwitchH2V
= rInf
.GetFrame() && rInf
.GetFrame()->IsVertical();
888 const bool bSwitchH2VLRBT
= rInf
.GetFrame() && rInf
.GetFrame()->IsVertLRBT();
889 const bool bSwitchL2R
= rInf
.GetFrame() && rInf
.GetFrame()->IsRightToLeft() &&
890 ! rInf
.IsIgnoreFrameRTL();
891 const vcl::text::ComplexTextLayoutFlags nMode
= rInf
.GetOut().GetLayoutMode();
892 const bool bBidiPor
= ( bSwitchL2R
!=
893 ( vcl::text::ComplexTextLayoutFlags::Default
!= ( vcl::text::ComplexTextLayoutFlags::BiDiRtl
& nMode
) ) );
895 // be sure to have the correct layout mode at the printer
898 m_pPrinter
->SetLayoutMode( rInf
.GetOut().GetLayoutMode() );
899 m_pPrinter
->SetDigitLanguage( rInf
.GetOut().GetDigitLanguage() );
902 Point
aTextOriginPos( rInf
.GetPos() );
905 if( rInf
.GetpOut() != *s_pFntObjPixOut
.get() || rInf
.GetOut().GetMapMode() != *s_pPixMap
)
907 *s_pPixMap
= rInf
.GetOut().GetMapMode();
908 (*s_pFntObjPixOut
.get()) = rInf
.GetpOut();
910 s_nPixWidth
= rInf
.GetOut().PixelToLogic( aTmp
).Width();
913 aTextOriginPos
.AdjustX(rInf
.GetFrame()->IsRightToLeft() ? 0 : s_nPixWidth
);
916 Color
aOldColor( pTmpFont
->GetColor() );
917 bool bChgColor
= rInf
.ApplyAutoColor( pTmpFont
);
918 if( !pTmpFont
->IsSameInstance( rInf
.GetOut().GetFont() ) )
919 rInf
.GetOut().SetFont( *pTmpFont
);
921 pTmpFont
->SetColor( aOldColor
);
923 if (TextFrameIndex(COMPLETE_STRING
) == rInf
.GetLen())
924 rInf
.SetLen( TextFrameIndex(rInf
.GetText().getLength()) );
926 // ASIAN LINE AND CHARACTER GRID MODE START
928 if ( rInf
.GetFrame() && rInf
.SnapToGrid() && rInf
.GetFont() &&
929 SwFontScript::CJK
== rInf
.GetFont()->GetActual() )
931 SwTextGridItem
const*const pGrid(GetGridItem(rInf
.GetFrame()->FindPageFrame()));
933 // ASIAN LINE AND CHARACTER GRID MODE
934 if ( pGrid
&& GRID_LINES_CHARS
== pGrid
->GetGridType() )
936 //for textgrid refactor
937 const SwDoc
* pDoc
= rInf
.GetShell()->GetDoc();
938 const tools::Long nGridWidth
= GetGridWidth(*pGrid
, *pDoc
);
939 tools::Long nSpaceAdd
= rInf
.GetSpace() / SPACING_PRECISION_FACTOR
;
941 // kerning array - gives the absolute position of end of each character
942 KernArray aKernArray
;
945 GetTextArray(*m_pPrinter
, rInf
, aKernArray
);
947 GetTextArray(rInf
.GetOut(), rInf
, aKernArray
);
949 tools::Long nDelta
= 0;
951 if (pGrid
->IsSnapToChars())
953 nDelta
= sw::Justify::SnapToGrid(aKernArray
, rInf
.GetText(), sal_Int32(rInf
.GetIdx()),
954 sal_Int32(rInf
.GetLen()), nGridWidth
, false);
958 sw::Justify::SnapToGridEdge(aKernArray
, sal_Int32(rInf
.GetLen()), nGridWidth
,
959 nSpaceAdd
, rInf
.GetKern());
963 aTextOriginPos
.AdjustX(nDelta
);
966 rInf
.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos
);
968 rInf
.GetOut().DrawTextArray( aTextOriginPos
, rInf
.GetText(),
969 aKernArray
, {}, sal_Int32(rInf
.GetIdx()), sal_Int32(rInf
.GetLen()));
975 // DIRECT PAINTING WITHOUT SCREEN ADJUSTMENT
979 const Fraction
aTmp( 1, 1 );
980 bool bStretch
= rInf
.GetWidth() && (rInf
.GetLen() > TextFrameIndex(1)) && bPrt
981 && ( aTmp
!= rInf
.GetOut().GetMapMode().GetScaleX() );
984 rInf
.GetFrame()->SwitchLTRtoRTL( aTextOriginPos
);
987 rInf
.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos
);
989 // In the good old days we used to have a simple DrawText if the
990 // output device is the printer. Now we need a DrawTextArray if
991 // 1. KanaCompression is enabled
992 // 2. Justified alignment
993 // Simple kerning is handled by DrawStretchText
994 if( rInf
.GetSpace() || rInf
.GetKanaComp() )
996 KernArray aKernArray
;
997 GetTextArray(rInf
.GetOut(), rInf
, aKernArray
);
998 std::vector
<sal_Bool
> aKashidaArray
;
1002 sal_Int32 nZwi
= sal_Int32(rInf
.GetLen()) - 1;
1003 tools::Long nDiff
= rInf
.GetWidth() - aKernArray
[ nZwi
]
1004 - sal_Int32(rInf
.GetLen()) * rInf
.GetKern();
1005 tools::Long nRest
= nDiff
% nZwi
;
1015 nRest
= nZwi
- nRest
;
1018 tools::Long nSum
= nDiff
;
1019 for( sal_Int32 i
= 0; i
< nZwi
; )
1021 aKernArray
.adjust(i
, nSum
);
1028 // Modify Array for special justifications
1030 tools::Long nSpaceAdd
= rInf
.GetSpace() / SPACING_PRECISION_FACTOR
;
1031 bool bSpecialJust
= false;
1033 if ( rInf
.GetFont() && rInf
.GetLen() )
1035 const SwScriptInfo
* pSI
= rInf
.GetScriptInfo();
1036 const SwFontScript nActual
= rInf
.GetFont()->GetActual();
1039 if ( SwFontScript::CJK
== nActual
&& rInf
.GetKanaComp() &&
1040 pSI
&& pSI
->CountCompChg() &&
1041 lcl_IsMonoSpaceFont( rInf
.GetOut() ) )
1043 pSI
->Compress( aKernArray
, rInf
.GetIdx(), rInf
.GetLen(),
1045 o3tl::narrowing
<sal_uInt16
>(m_aFont
.GetFontSize().Height()), lcl_IsFullstopCentered( rInf
.GetOut() ), &aTextOriginPos
);
1046 bSpecialJust
= true;
1049 // Asian Justification
1050 if ( SwFontScript::CJK
== nActual
&& nSpaceAdd
)
1052 LanguageType aLang
= rInf
.GetFont()->GetLanguage( SwFontScript::CJK
);
1054 if (!MsLangId::isKorean(aLang
))
1056 SwScriptInfo::CJKJustify( rInf
.GetText(), aKernArray
,
1057 rInf
.GetIdx(), rInf
.GetLen(), aLang
, nSpaceAdd
, rInf
.IsSpaceStop() );
1059 bSpecialJust
= true;
1064 // Kashida Justification
1065 if ( SwFontScript::CTL
== nActual
&& nSpaceAdd
)
1067 if ( SwScriptInfo::IsArabicText( rInf
.GetText(), rInf
.GetIdx(), rInf
.GetLen() ) )
1069 aKashidaArray
.resize(aKernArray
.size(), false);
1070 if ( pSI
&& pSI
->CountKashida() &&
1071 pSI
->KashidaJustify( &aKernArray
, aKashidaArray
.data(), rInf
.GetIdx(),
1072 rInf
.GetLen(), nSpaceAdd
) != -1 )
1074 bSpecialJust
= true;
1078 aKashidaArray
.clear();
1082 // Thai Justification
1083 if ( SwFontScript::CTL
== nActual
&& nSpaceAdd
)
1085 LanguageType aLang
= rInf
.GetFont()->GetLanguage( SwFontScript::CTL
);
1087 if ( LANGUAGE_THAI
== aLang
)
1089 // Use rInf.GetSpace() because it has more precision than
1091 SwScriptInfo::ThaiJustify( rInf
.GetText(), &aKernArray
,
1092 rInf
.GetIdx(), rInf
.GetLen(),
1093 rInf
.GetNumberOfBlanks(),
1096 // adding space to blanks is already done
1097 bSpecialJust
= true;
1103 tools::Long nKernSum
= rInf
.GetKern();
1105 if ( bStretch
|| m_bPaintBlank
|| rInf
.GetKern() || bSpecialJust
)
1107 for (sal_Int32 i
= 0; i
< sal_Int32(rInf
.GetLen()); i
++,
1108 nKernSum
+= rInf
.GetKern() )
1110 if (CH_BLANK
== rInf
.GetText()[sal_Int32(rInf
.GetIdx()) + i
])
1111 nKernSum
+= nSpaceAdd
;
1112 aKernArray
.adjust(i
, nKernSum
);
1115 // In case of underlined/strike-through justified text
1116 // a blank at the end requires special handling:
1117 if( m_bPaintBlank
&& rInf
.GetLen() && ( CH_BLANK
==
1118 rInf
.GetText()[sal_Int32(rInf
.GetIdx() + rInf
.GetLen())-1]))
1120 // If it is a single underlined space, output 2 spaces:
1121 if (TextFrameIndex(1) == rInf
.GetLen())
1123 aKernArray
.set(0, rInf
.GetWidth() + nSpaceAdd
);
1125 rInf
.GetOut().DrawTextArray( aTextOriginPos
, rInf
.GetText(),
1126 aKernArray
, aKashidaArray
, sal_Int32(rInf
.GetIdx()), 1 );
1130 sal_Int32
nIndex(sal_Int32(rInf
.GetLen()) - 2);
1131 aKernArray
.adjust(nIndex
, nSpaceAdd
);
1132 rInf
.GetOut().DrawTextArray( aTextOriginPos
, rInf
.GetText(),
1133 aKernArray
, aKashidaArray
, sal_Int32(rInf
.GetIdx()), sal_Int32(rInf
.GetLen()));
1137 rInf
.GetOut().DrawTextArray( aTextOriginPos
, rInf
.GetText(),
1138 aKernArray
, aKashidaArray
, sal_Int32(rInf
.GetIdx()), sal_Int32(rInf
.GetLen()));
1142 Point
aTmpPos( aTextOriginPos
);
1145 for( i
= 0; i
< sal_Int32(rInf
.GetLen()); i
++ )
1147 if (CH_BLANK
== rInf
.GetText()[sal_Int32(rInf
.GetIdx()) + i
])
1149 nKernSum
+= nSpaceAdd
;
1151 rInf
.GetOut().DrawText( aTmpPos
, rInf
.GetText(),
1152 sal_Int32(rInf
.GetIdx()) + j
, i
- j
);
1154 SwTwips nAdd
= aKernArray
[ i
] + nKernSum
;
1155 if ( ( vcl::text::ComplexTextLayoutFlags::BiDiStrong
| vcl::text::ComplexTextLayoutFlags::BiDiRtl
) == nMode
)
1157 aTmpPos
.setX( aTextOriginPos
.X() + nAdd
);
1161 rInf
.GetOut().DrawText( aTmpPos
, rInf
.GetText(),
1162 sal_Int32(rInf
.GetIdx()) + j
, i
- j
);
1167 tools::Long nTmpWidth
= rInf
.GetWidth();
1168 if( rInf
.GetKern() && rInf
.GetLen() && nTmpWidth
> rInf
.GetKern() )
1169 nTmpWidth
-= rInf
.GetKern();
1170 rInf
.GetOut().DrawStretchText( aTextOriginPos
, nTmpWidth
,
1172 sal_Int32(rInf
.GetIdx()), sal_Int32(rInf
.GetLen()));
1174 else if( rInf
.GetKern() )
1176 const tools::Long nTmpWidth
= GetTextSize( rInf
).Width();
1178 const Color
aSaveColor( pTmpFont
->GetColor() );
1179 const bool bColorChanged
= rInf
.ApplyAutoColor( pTmpFont
);
1183 if( !pTmpFont
->IsSameInstance( rInf
.GetOut().GetFont() ) )
1184 rInf
.GetOut().SetFont( *pTmpFont
);
1185 pTmpFont
->SetColor( aSaveColor
);
1188 rInf
.GetOut().DrawStretchText( aTextOriginPos
, nTmpWidth
,
1190 sal_Int32(rInf
.GetIdx()), sal_Int32(rInf
.GetLen()));
1193 rInf
.GetOut().DrawText( aTextOriginPos
, rInf
.GetText(),
1194 sal_Int32(rInf
.GetIdx()), sal_Int32(rInf
.GetLen()));
1197 // PAINTING WITH FORMATTING DEVICE/SCREEN ADJUSTMENT
1201 OUString aBulletOverlay
;
1202 bool bBullet
= rInf
.GetBullet();
1205 CreateScrFont( *rInf
.GetShell(), rInf
.GetOut() );
1207 VclPtr
<OutputDevice
> xFormattingDevice
;
1209 // OLE: no printer available
1210 // OSL_ENSURE( pPrinter, "DrawText needs pPrinter" )
1213 // pTmpFont has already been set as current font for rInf.GetOut()
1214 if ( m_pPrinter
.get() != rInf
.GetpOut() || pTmpFont
!= m_pPrtFont
)
1216 if( !m_pPrtFont
->IsSameInstance( m_pPrinter
->GetFont() ) )
1217 m_pPrinter
->SetFont( *m_pPrtFont
);
1219 xFormattingDevice
= m_pPrinter
;
1223 xFormattingDevice
= &rInf
.GetOut();
1226 //tdf#152094 see if we can retain a subpixel factor
1228 MapMode
aMapMode(xFormattingDevice
->GetMapMode());
1229 if (aMapMode
.IsSimple() && aMapMode
.GetMapUnit() == MapUnit::MapTwip
)
1231 if (xFormattingDevice
->GetDPIX() == xFormattingDevice
->GetDPIY())
1233 int nRatio
= xFormattingDevice
->GetDPIX() / 1440;
1234 if (nRatio
* 1440 == xFormattingDevice
->GetDPIX())
1235 nSubPixels
= nRatio
;
1238 KernArray
aKernArray(nSubPixels
);
1239 GetTextArray(*xFormattingDevice
, rInf
, aKernArray
);
1241 std::vector
<sal_Bool
> aKashidaArray
;
1243 // Modify Printer and ScreenArrays for special justifications
1245 tools::Long nSpaceAdd
= rInf
.GetSpace() / SPACING_PRECISION_FACTOR
;
1246 bool bNoHalfSpace
= false;
1248 if ( rInf
.GetFont() && rInf
.GetLen() )
1250 const SwFontScript nActual
= rInf
.GetFont()->GetActual();
1251 const SwScriptInfo
* pSI
= rInf
.GetScriptInfo();
1254 if ( SwFontScript::CJK
== nActual
&& rInf
.GetKanaComp() &&
1255 pSI
&& pSI
->CountCompChg() &&
1256 lcl_IsMonoSpaceFont( rInf
.GetOut() ) )
1258 pSI
->Compress( aKernArray
, rInf
.GetIdx(), rInf
.GetLen(),
1260 o3tl::narrowing
<sal_uInt16
>(m_aFont
.GetFontSize().Height()), lcl_IsFullstopCentered( rInf
.GetOut() ), &aTextOriginPos
);
1263 // Asian Justification
1264 if ( SwFontScript::CJK
== nActual
&& nSpaceAdd
)
1266 LanguageType aLang
= rInf
.GetFont()->GetLanguage( SwFontScript::CJK
);
1268 if (!MsLangId::isKorean(aLang
))
1270 SwScriptInfo::CJKJustify( rInf
.GetText(), aKernArray
,
1271 rInf
.GetIdx(), rInf
.GetLen(), aLang
, nSpaceAdd
, rInf
.IsSpaceStop() );
1277 // Kashida Justification
1278 if ( SwFontScript::CTL
== nActual
&& nSpaceAdd
)
1280 if ( SwScriptInfo::IsArabicText( rInf
.GetText(), rInf
.GetIdx(), rInf
.GetLen() ) )
1282 aKashidaArray
.resize(aKernArray
.size(), false);
1283 if ( pSI
&& pSI
->CountKashida() &&
1284 pSI
->KashidaJustify( &aKernArray
, aKashidaArray
.data(), rInf
.GetIdx(),
1285 rInf
.GetLen(), nSpaceAdd
) != -1 )
1289 aKashidaArray
.clear();
1290 bNoHalfSpace
= true;
1295 // Thai Justification
1296 if ( SwFontScript::CTL
== nActual
&& nSpaceAdd
)
1298 LanguageType aLang
= rInf
.GetFont()->GetLanguage( SwFontScript::CTL
);
1300 if ( LANGUAGE_THAI
== aLang
)
1302 SwScriptInfo::ThaiJustify( rInf
.GetText(), &aKernArray
,
1305 rInf
.GetNumberOfBlanks(),
1308 // adding space to blanks is already done
1316 // Copy the substring that will be painted, and replace spaces with
1317 // bullets, and everything else with space.
1318 sal_Int32 nCopyStart
= sal_Int32(rInf
.GetIdx());
1319 sal_Int32 nCopyLen
= sal_Int32(rInf
.GetLen());
1321 aBulletOverlay
= rInf
.GetText().copy( nCopyStart
, nCopyLen
);
1323 for( sal_Int32 i
= 0; i
< aBulletOverlay
.getLength(); ++i
)
1324 if( CH_BLANK
== aBulletOverlay
[ i
] )
1326 /* fdo#72488 Hack: try to see if the space is zero width
1327 * and don't bother with inserting a bullet in this case.
1329 if ((i
+ nCopyStart
+ 1 >= sal_Int32(rInf
.GetLen())) ||
1330 aKernArray
[i
+ nCopyStart
] != aKernArray
[ i
+ nCopyStart
+ 1])
1332 aBulletOverlay
= aBulletOverlay
.replaceAt(i
, 1, rtl::OUStringChar(CH_BULLET
));
1336 aBulletOverlay
= aBulletOverlay
.replaceAt(i
, 1, rtl::OUStringChar(CH_BLANK
));
1341 aBulletOverlay
= aBulletOverlay
.replaceAt(i
, 1, rtl::OUStringChar(CH_BLANK
));
1345 TextFrameIndex
nCnt(rInf
.GetText().getLength());
1346 if ( nCnt
< rInf
.GetIdx() )
1347 assert(false); // layout bug, not handled below
1349 nCnt
= nCnt
- rInf
.GetIdx();
1350 nCnt
= std::min(nCnt
, rInf
.GetLen());
1351 sal_Unicode cChPrev
= rInf
.GetText()[sal_Int32(rInf
.GetIdx())];
1353 // In case of a single underlined space in justified text,
1354 // have to output 2 spaces:
1355 if ((nCnt
== TextFrameIndex(1)) && rInf
.GetSpace() && (cChPrev
== CH_BLANK
))
1357 aKernArray
.set(0, rInf
.GetWidth() +
1359 (rInf
.GetSpace() / SPACING_PRECISION_FACTOR
));
1362 rInf
.GetFrame()->SwitchLTRtoRTL( aTextOriginPos
);
1365 rInf
.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos
);
1367 rInf
.GetOut().DrawTextArray( aTextOriginPos
, rInf
.GetText(),
1368 aKernArray
, aKashidaArray
, sal_Int32(rInf
.GetIdx()), 1 );
1370 rInf
.GetOut().DrawTextArray( aTextOriginPos
, rInf
.GetText(), aKernArray
, {},
1371 sal_Int32(rInf
.GetIdx()), 1 );
1375 if (m_pPrtFont
->IsWordLineMode())
1376 bNoHalfSpace
= true;
1378 sw::Justify::SpaceDistribution(aKernArray
, rInf
.GetText(), sal_Int32(rInf
.GetIdx()),
1379 sal_Int32(nCnt
), nSpaceAdd
, rInf
.GetKern(), bNoHalfSpace
);
1381 if( rInf
.GetGreyWave() )
1385 tools::Long nHght
= rInf
.GetOut().LogicToPixel(
1386 m_pPrtFont
->GetFontSize() ).Height();
1387 if( WRONG_SHOW_MIN
< nHght
)
1389 if ( rInf
.GetOut().GetConnectMetaFile() )
1390 rInf
.GetOut().Push();
1392 Color
aCol( rInf
.GetOut().GetLineColor() );
1393 bool bColSave
= aCol
!= gWaveCol
;
1395 rInf
.GetOut().SetLineColor( gWaveCol
);
1398 tools::Long nKernVal
= aKernArray
[sal_Int32(rInf
.GetLen()) - 1];
1400 const Degree10 nDir
= bBidiPor
1402 : UnMapDirection(GetFont().GetOrientation(),
1403 bSwitchH2V
, bSwitchH2VLRBT
);
1405 switch ( nDir
.get() )
1408 aEnd
.setX( rInf
.GetPos().X() + nKernVal
);
1409 aEnd
.setY( rInf
.GetPos().Y() );
1412 aEnd
.setX( rInf
.GetPos().X() );
1413 aEnd
.setY( rInf
.GetPos().Y() - nKernVal
);
1416 aEnd
.setX( rInf
.GetPos().X() - nKernVal
);
1417 aEnd
.setY( rInf
.GetPos().Y() );
1420 aEnd
.setX( rInf
.GetPos().X() );
1421 aEnd
.setY( rInf
.GetPos().Y() + nKernVal
);
1425 Point
aCurrPos( rInf
.GetPos() );
1429 rInf
.GetFrame()->SwitchLTRtoRTL( aCurrPos
);
1430 rInf
.GetFrame()->SwitchLTRtoRTL( aEnd
);
1435 rInf
.GetFrame()->SwitchHorizontalToVertical( aCurrPos
);
1436 rInf
.GetFrame()->SwitchHorizontalToVertical( aEnd
);
1439 vcl::ScopedAntialiasing
a(rInf
.GetOut(), true);
1440 rInf
.GetOut().DrawWaveLine( aCurrPos
, aEnd
);
1443 rInf
.GetOut().SetLineColor( aCol
);
1445 if ( rInf
.GetOut().GetConnectMetaFile() )
1446 rInf
.GetOut().Pop();
1450 else if( !m_bSymbol
&& rInf
.GetLen() )
1453 if (rInf
.GetWrong() || rInf
.GetGrammarCheck() || rInf
.GetSmartTags())
1455 const tools::Long nHalfSpace
= bNoHalfSpace
? 0 : nSpaceAdd
/ 2;
1456 CalcLinePosData
aCalcLinePosData(rInf
, GetFont(), nCnt
, bSwitchH2V
,
1457 bSwitchH2VLRBT
, bSwitchL2R
, nHalfSpace
,
1458 aKernArray
, bBidiPor
);
1460 SwForbidden aForbidden
;
1461 // draw line for smart tag data
1462 lcl_DrawLineForWrongListData( aForbidden
, rInf
, rInf
.GetSmartTags(), aCalcLinePosData
, Size() );
1463 // draw wave line for spell check errors
1464 // draw them BEFORE the grammar check lines to 'override' the latter in case of conflict.
1465 // reason: some grammar errors can only be found if spelling errors are fixed,
1466 // therefore we don't want the user to miss a spelling error.
1467 lcl_DrawLineForWrongListData( aForbidden
, rInf
, rInf
.GetWrong(), aCalcLinePosData
, m_pPrtFont
->GetFontSize() );
1468 // draw wave line for grammar check errors
1469 lcl_DrawLineForWrongListData( aForbidden
, rInf
, rInf
.GetGrammarCheck(), aCalcLinePosData
, m_pPrtFont
->GetFontSize() );
1473 sal_Int32 nLen
= sal_Int32(rInf
.GetLen());
1479 rInf
.GetFrame()->SwitchLTRtoRTL( aTextOriginPos
);
1482 rInf
.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos
);
1484 sal_Int32 nIdx
= sal_Int32(rInf
.GetIdx());
1485 const SalLayoutGlyphs
* pGlyphs
= SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rInf
.GetOut(),
1486 rInf
.GetText(), nIdx
, nLen
);
1487 rInf
.GetOut().DrawTextArray( aTextOriginPos
, rInf
.GetText(), aKernArray
, aKashidaArray
,
1488 nIdx
, nLen
, SalLayoutFlags::NONE
, pGlyphs
);
1491 rInf
.GetOut().Push();
1492 Color aPreviousColor
= pTmpFont
->GetColor();
1494 FontLineStyle aPreviousUnderline
= pTmpFont
->GetUnderline();
1495 FontLineStyle aPreviousOverline
= pTmpFont
->GetOverline();
1496 FontStrikeout aPreviousStrikeout
= pTmpFont
->GetStrikeout();
1498 pTmpFont
->SetColor( NON_PRINTING_CHARACTER_COLOR
);
1499 pTmpFont
->SetUnderline(LINESTYLE_NONE
);
1500 pTmpFont
->SetOverline(LINESTYLE_NONE
);
1501 pTmpFont
->SetStrikeout(STRIKEOUT_NONE
);
1502 rInf
.GetOut().SetFont( *pTmpFont
);
1503 tools::Long nShift
= rInf
.GetOut( ).GetFontMetric( ).GetBulletOffset( );
1506 tools::Long nAdd
= 0;
1508 if ( aBulletOverlay
[ 0 ] == CH_BULLET
)
1511 aTextOriginPos
.AdjustY(nShift
) ;
1513 aTextOriginPos
.AdjustX(nShift
) ;
1516 for( sal_Int32 i
= 1 ; i
< nLen
; ++i
)
1518 if ( aBulletOverlay
[ i
] == CH_BULLET
)
1519 aKernArray
.adjust(i
- 1, nShift
);
1521 aKernArray
.adjust(i
- 1, -nAdd
);
1524 rInf
.GetOut().DrawTextArray( aTextOriginPos
, aBulletOverlay
, aKernArray
,
1526 pTmpFont
->SetColor( aPreviousColor
);
1528 pTmpFont
->SetUnderline(aPreviousUnderline
);
1529 pTmpFont
->SetOverline(aPreviousOverline
);
1530 pTmpFont
->SetStrikeout(aPreviousStrikeout
);
1531 rInf
.GetOut().Pop();
1538 Size
SwFntObj::GetTextSize( SwDrawTextInfo
& rInf
)
1541 const TextFrameIndex nLn
= (TextFrameIndex(COMPLETE_STRING
) != rInf
.GetLen())
1543 : TextFrameIndex(rInf
.GetText().getLength());
1545 const TextFrameIndex nMsrLn
= (TextFrameIndex(COMPLETE_STRING
) != rInf
.GetMeasureLen())
1546 ? rInf
.GetMeasureLen()
1549 // If the measure length is different from the length, then we are
1550 // measuring substring width for caret positioning, see SetMeasureLength()
1551 // use in TextCursor::GetCharRect_().
1552 bool bCaret(nMsrLn
!= nLn
);
1554 // be sure to have the correct layout mode at the printer
1557 m_pPrinter
->SetLayoutMode( rInf
.GetOut().GetLayoutMode() );
1558 m_pPrinter
->SetDigitLanguage( rInf
.GetOut().GetDigitLanguage() );
1561 if ( rInf
.GetFrame() && nLn
&& rInf
.SnapToGrid() && rInf
.GetFont() &&
1562 SwFontScript::CJK
== rInf
.GetFont()->GetActual() )
1564 SwTextGridItem
const*const pGrid(GetGridItem(rInf
.GetFrame()->FindPageFrame()));
1565 if ( pGrid
&& GRID_LINES_CHARS
== pGrid
->GetGridType() )
1567 const SwDoc
* pDoc
= rInf
.GetShell()->GetDoc();
1568 const sal_uInt16 nGridWidth
= GetGridWidth(*pGrid
, *pDoc
);
1570 OutputDevice
* pOutDev
;
1574 if( !m_pPrtFont
->IsSameInstance( m_pPrinter
->GetFont() ) )
1575 m_pPrinter
->SetFont(*m_pPrtFont
);
1576 pOutDev
= m_pPrinter
;
1579 pOutDev
= rInf
.GetpOut();
1581 aTextSize
.setWidth( pOutDev
->GetTextWidth(rInf
.GetText(),
1582 sal_Int32(rInf
.GetIdx()), sal_Int32(nLn
)) );
1584 OSL_ENSURE( !rInf
.GetShell() ||
1585 ( USHRT_MAX
!= GetGuessedLeading() && USHRT_MAX
!= GetExternalLeading() ),
1586 "Leading values should be already calculated" );
1587 aTextSize
.setHeight( pOutDev
->GetTextHeight() +
1588 GetFontLeading( rInf
.GetShell(), rInf
.GetOut() ) );
1590 KernArray aKernArray
;
1591 GetTextArray(*pOutDev
, rInf
, aKernArray
, sal_Int32(nLn
), bCaret
);
1592 if (pGrid
->IsSnapToChars())
1594 sw::Justify::SnapToGrid(aKernArray
, rInf
.GetText(), sal_Int32(rInf
.GetIdx()),
1595 sal_Int32(rInf
.GetLen()), nGridWidth
, true);
1599 // use 0 to calculate raw width without rInf.GetSpace().
1600 sw::Justify::SnapToGridEdge(aKernArray
, sal_Int32(rInf
.GetLen()), nGridWidth
, 0,
1604 aTextSize
.setWidth(aKernArray
[sal_Int32(nMsrLn
) - 1]);
1605 rInf
.SetKanaDiff( 0 );
1610 const bool bCompress
= rInf
.GetKanaComp() && nLn
&&
1612 SwFontScript::CJK
== rInf
.GetFont()->GetActual() &&
1613 rInf
.GetScriptInfo() &&
1614 rInf
.GetScriptInfo()->CountCompChg() &&
1615 lcl_IsMonoSpaceFont( rInf
.GetOut() );
1617 OSL_ENSURE( !bCompress
|| ( rInf
.GetScriptInfo() && rInf
.GetScriptInfo()->
1618 CountCompChg()), "Compression without info" );
1620 // This is the part used e.g., for cursor travelling
1621 // See condition for DrawText or DrawTextArray (bDirectPrint)
1622 KernArray aKernArray
;
1623 if ( m_pPrinter
&& m_pPrinter
.get() != rInf
.GetpOut() )
1625 if( !m_pPrtFont
->IsSameInstance( m_pPrinter
->GetFont() ) )
1626 m_pPrinter
->SetFont(*m_pPrtFont
);
1627 aTextSize
.setHeight( m_pPrinter
->GetTextHeight() );
1629 CreateScrFont( *rInf
.GetShell(), rInf
.GetOut() );
1630 if( !GetScrFont()->IsSameInstance( rInf
.GetOut().GetFont() ) )
1631 rInf
.GetOut().SetFont( *m_pScrFont
);
1633 GetTextArray(*m_pPrinter
, rInf
.GetText(), aKernArray
,
1634 sal_Int32(rInf
.GetIdx()), sal_Int32(nLn
), bCaret
);
1638 if( !m_pPrtFont
->IsSameInstance( rInf
.GetOut().GetFont() ) )
1639 rInf
.GetOut().SetFont( *m_pPrtFont
);
1640 aTextSize
.setHeight( rInf
.GetOut().GetTextHeight() );
1642 GetTextArray(rInf
.GetOut(), rInf
, aKernArray
, nLn
.get(), bCaret
);
1647 rInf
.SetKanaDiff(rInf
.GetScriptInfo()->Compress(aKernArray
, rInf
.GetIdx(), nLn
, rInf
.GetKanaComp(),
1648 o3tl::narrowing
<sal_uInt16
>(m_aFont
.GetFontSize().Height()), lcl_IsFullstopCentered(rInf
.GetOut())));
1651 rInf
.SetKanaDiff( 0 );
1655 aTextSize
.setWidth(aKernArray
[sal_Int32(nMsrLn
) - 1]);
1657 // Note that we can't simply use sal_Int(nMsrLn) - 1 as nSpaceCount
1658 // because a glyph may be made up of more than one characters.
1659 sal_Int32 nSpaceCount
= 0;
1660 tools::Long nOldValue
= aKernArray
[0];
1662 for(sal_Int32 i
= 1; i
< sal_Int32(nMsrLn
); ++i
)
1664 if (nOldValue
!= aKernArray
[i
])
1667 nOldValue
= aKernArray
[i
];
1672 aTextSize
.AdjustWidth(nSpaceCount
* rInf
.GetKern());
1675 OSL_ENSURE( !rInf
.GetShell() ||
1676 ( USHRT_MAX
!= GetGuessedLeading() && USHRT_MAX
!= GetExternalLeading() ),
1677 "Leading values should be already calculated" );
1678 aTextSize
.AdjustHeight(GetFontLeading( rInf
.GetShell(), rInf
.GetOut() ) );
1682 TextFrameIndex
SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo
&rInf
)
1684 tools::Long nSpaceAdd
= rInf
.GetSpace() / SPACING_PRECISION_FACTOR
;
1685 const tools::Long nCharacterSpacing
= -rInf
.GetCharacterSpacing() / SPACING_PRECISION_FACTOR
;
1686 tools::Long nKern
= rInf
.GetKern();
1688 if( 0 != nCharacterSpacing
)
1689 nKern
-= nCharacterSpacing
;
1691 KernArray aKernArray
;
1693 // be sure to have the correct layout mode at the printer
1696 m_pPrinter
->SetLayoutMode( rInf
.GetOut().GetLayoutMode() );
1697 m_pPrinter
->SetDigitLanguage( rInf
.GetOut().GetDigitLanguage() );
1698 GetTextArray(*m_pPrinter
, rInf
, aKernArray
);
1702 GetTextArray(rInf
.GetOut(), rInf
, aKernArray
);
1705 if ( rInf
.GetFrame() && rInf
.GetLen() && rInf
.SnapToGrid() &&
1706 rInf
.GetFont() && SwFontScript::CJK
== rInf
.GetFont()->GetActual() )
1708 SwTextGridItem
const*const pGrid(GetGridItem(rInf
.GetFrame()->FindPageFrame()));
1709 if ( pGrid
&& GRID_LINES_CHARS
== pGrid
->GetGridType() )
1711 const SwDoc
* pDoc
= rInf
.GetShell()->GetDoc();
1712 const sal_uInt16 nGridWidth
= GetGridWidth(*pGrid
, *pDoc
);
1714 if (pGrid
->IsSnapToChars())
1716 sw::Justify::SnapToGrid(aKernArray
, rInf
.GetText(), sal_Int32(rInf
.GetIdx()),
1717 sal_Int32(rInf
.GetLen()), nGridWidth
, true);
1721 sw::Justify::SnapToGridEdge(aKernArray
, sal_Int32(rInf
.GetLen()), nGridWidth
,
1722 nSpaceAdd
, rInf
.GetKern());
1725 return TextFrameIndex(sw::Justify::GetModelPosition(aKernArray
, sal_Int32(rInf
.GetLen()),
1730 const SwScriptInfo
* pSI
= rInf
.GetScriptInfo();
1731 if ( rInf
.GetFont() && rInf
.GetLen() )
1733 const SwFontScript nActual
= rInf
.GetFont()->GetActual();
1736 if ( SwFontScript::CJK
== nActual
&& rInf
.GetKanaComp() &&
1737 pSI
&& pSI
->CountCompChg() &&
1738 lcl_IsMonoSpaceFont( rInf
.GetOut() ) )
1740 pSI
->Compress( aKernArray
, rInf
.GetIdx(), rInf
.GetLen(),
1742 o3tl::narrowing
<sal_uInt16
>(m_aFont
.GetFontSize().Height()),
1743 lcl_IsFullstopCentered( rInf
.GetOut() ) );
1746 // Asian Justification
1747 if ( SwFontScript::CJK
== rInf
.GetFont()->GetActual() )
1749 LanguageType aLang
= rInf
.GetFont()->GetLanguage( SwFontScript::CJK
);
1751 if (!MsLangId::isKorean(aLang
))
1753 SwScriptInfo::CJKJustify( rInf
.GetText(), aKernArray
,
1754 rInf
.GetIdx(), rInf
.GetLen(), aLang
, nSpaceAdd
, rInf
.IsSpaceStop() );
1761 // Kashida Justification
1762 if ( SwFontScript::CTL
== nActual
&& rInf
.GetSpace() )
1764 if ( SwScriptInfo::IsArabicText( rInf
.GetText(), rInf
.GetIdx(), rInf
.GetLen() ) )
1766 if ( pSI
&& pSI
->CountKashida() &&
1767 pSI
->KashidaJustify( &aKernArray
, nullptr, rInf
.GetIdx(), rInf
.GetLen(),
1773 // Thai Justification
1774 if ( SwFontScript::CTL
== nActual
&& nSpaceAdd
)
1776 LanguageType aLang
= rInf
.GetFont()->GetLanguage( SwFontScript::CTL
);
1778 if ( LANGUAGE_THAI
== aLang
)
1780 SwScriptInfo::ThaiJustify( rInf
.GetText(), &aKernArray
,
1781 rInf
.GetIdx(), rInf
.GetLen(),
1782 rInf
.GetNumberOfBlanks(),
1785 // adding space to blanks is already done
1791 tools::Long nLeft
= 0;
1792 tools::Long nRight
= 0;
1793 TextFrameIndex
nCnt(0);
1794 tools::Long nSpaceSum
= 0;
1795 tools::Long nKernSum
= 0;
1797 sal_Int32 nDone
= 0;
1798 TextFrameIndex nIdx
= rInf
.GetIdx();
1799 TextFrameIndex nLastIdx
= nIdx
;
1800 const TextFrameIndex nEnd
= rInf
.GetIdx() + rInf
.GetLen();
1803 // skip character cells for all script types
1804 LanguageType aLang
= rInf
.GetFont()->GetLanguage();
1806 while ( ( nRight
< tools::Long( rInf
.GetOffset() ) ) && ( nIdx
< nEnd
) )
1808 if (nSpaceAdd
&& CH_BLANK
== rInf
.GetText()[ sal_Int32(nIdx
)])
1809 nSpaceSum
+= nSpaceAdd
;
1811 // go to next character (cell).
1814 nIdx
= TextFrameIndex(g_pBreakIt
->GetBreakIter()->nextCharacters(
1815 rInf
.GetText(), sal_Int32(nIdx
),
1816 g_pBreakIt
->GetLocale( aLang
),
1817 i18n::CharacterIteratorMode::SKIPCELL
, 1, nDone
));
1818 if ( nIdx
<= nLastIdx
)
1821 // the next character might be outside the layout range (e.g tdf124116-1.odt)
1826 nRight
= aKernArray
[sal_Int32(nIdx
- rInf
.GetIdx()) - 1] + nKernSum
+ nSpaceSum
;
1831 // step back if position is before the middle of the character
1832 // or if we do not want to go to the next character
1833 if ( nIdx
> rInf
.GetIdx() &&
1834 ( rInf
.IsPosMatchesBounds() ||
1835 ( ( nRight
> tools::Long( rInf
.GetOffset() ) ) &&
1836 ( nRight
- rInf
.GetOffset() > rInf
.GetOffset() - nLeft
) ) ) )
1837 nCnt
= nLastIdx
- rInf
.GetIdx(); // first half
1839 nCnt
= nIdx
- rInf
.GetIdx(); // second half
1842 rInf
.SetCursorBidiLevel( pSI
->DirType( nLastIdx
) );
1847 SwFntAccess::SwFntAccess( const void* & rnFontCacheId
,
1848 sal_uInt16
&rIndex
, const void *pOwn
, SwViewShell
const *pSh
,
1850 SwCacheAccess( *pFntCache
, rnFontCacheId
, rIndex
),
1853 // the used ctor of SwCacheAccess searches for rnFontCacheId+rIndex in the cache
1856 // fast case: known Font (rnFontCacheId), no need to check printer and zoom
1860 // Font is known, but has to be checked
1863 { // Font not known, must be searched
1868 OutputDevice
* pOut
= nullptr;
1869 sal_uInt16 nZoom
= USHRT_MAX
;
1871 // Get the reference device
1874 pOut
= &pSh
->GetRefDev();
1875 nZoom
= pSh
->GetViewOptions()->GetZoom();
1882 if ( ( pFntObj
->GetZoom( ) == nZoom
) &&
1883 ( pFntObj
->m_pPrinter
== pOut
) &&
1884 pFntObj
->GetPropWidth() ==
1885 static_cast<SwSubFont
const *>(pOwn
)->GetPropWidth() )
1887 return; // result of Check: Drucker+Zoom okay.
1889 pFntObj
->Unlock(); // forget this object, printer/zoom differs
1893 // Search by font comparison, quite expensive!
1894 // Look for same font and same printer
1895 pFntObj
= pFntCache
->First();
1896 while ( pFntObj
&& !( pFntObj
->m_aFont
== *static_cast<vcl::Font
const *>(pOwn
) &&
1897 pFntObj
->GetZoom() == nZoom
&&
1898 pFntObj
->GetPropWidth() ==
1899 static_cast<SwSubFont
const *>(pOwn
)->GetPropWidth() &&
1900 ( !pFntObj
->m_pPrinter
|| pFntObj
->m_pPrinter
== pOut
) ) )
1901 pFntObj
= SwFntCache::Next( pFntObj
);
1903 if( pFntObj
&& pFntObj
->m_pPrinter
.get() != pOut
)
1905 // found one without printer, let's see if there is one with
1906 // the same printer as well
1907 SwFntObj
*pTmpObj
= pFntObj
;
1908 while( pTmpObj
&& !( pTmpObj
->m_aFont
== *static_cast<vcl::Font
const *>(pOwn
) &&
1909 pTmpObj
->GetZoom()==nZoom
&& pTmpObj
->m_pPrinter
==pOut
&&
1910 pTmpObj
->GetPropWidth() ==
1911 static_cast<SwSubFont
const *>(pOwn
)->GetPropWidth() ) )
1912 pTmpObj
= SwFntCache::Next( pTmpObj
);
1917 if ( !pFntObj
) // Font has not been found, create one
1919 // Have to create new Object, hence Owner must be a SwFont, later
1920 // the Owner will be the "MagicNumber"
1921 SwCacheAccess::m_pOwner
= pOwn
;
1922 pFntObj
= Get(); // will create via NewObj() and lock
1923 OSL_ENSURE(pFntObj
, "No Font, no Fun.");
1925 else // Font has been found, so we lock it.
1928 if (pFntObj
->m_pPrinter
.get() != pOut
) // if no printer is known by now
1930 OSL_ENSURE( !pFntObj
->m_pPrinter
, "SwFntAccess: Printer Changed" );
1931 pFntObj
->CreatePrtFont( *pOut
);
1932 pFntObj
->m_pPrinter
= pOut
;
1933 pFntObj
->m_pScrFont
= nullptr;
1934 pFntObj
->m_nGuessedLeading
= USHRT_MAX
;
1935 pFntObj
->m_nExtLeading
= USHRT_MAX
;
1936 pFntObj
->m_nPrtAscent
= USHRT_MAX
;
1937 pFntObj
->m_nPrtHeight
= USHRT_MAX
;
1942 // no matter if new or found, now the Owner of the Object is a
1943 // MagicNumber, and will be given to the SwFont, as well as the Index
1944 // for later direct access
1945 rnFontCacheId
= reinterpret_cast<void*>(reinterpret_cast<sal_IntPtr
>(pFntObj
->GetOwner()));
1946 SwCacheAccess::m_pOwner
= pFntObj
->GetOwner();
1947 rIndex
= pFntObj
->GetCachePos();
1951 SwCacheObj
*SwFntAccess::NewObj( )
1953 // "MagicNumber" used to identify Fonts
1954 static std::uintptr_t fontCacheIdCounter
= 0;
1955 // a new Font, a new "MagicNumber".
1956 return new SwFntObj( *static_cast<SwSubFont
const *>(m_pOwner
), ++fontCacheIdCounter
, m_pShell
);
1959 TextFrameIndex
SwFont::GetTextBreak(SwDrawTextInfo
const & rInf
, tools::Long nTextWidth
)
1961 ChgFnt( rInf
.GetShell(), rInf
.GetOut() );
1963 const bool bCompress
= rInf
.GetKanaComp() && rInf
.GetLen() &&
1964 SwFontScript::CJK
== GetActual() &&
1965 rInf
.GetScriptInfo() &&
1966 rInf
.GetScriptInfo()->CountCompChg() &&
1967 lcl_IsMonoSpaceFont( rInf
.GetOut() );
1969 OSL_ENSURE( !bCompress
|| ( rInf
.GetScriptInfo() && rInf
.GetScriptInfo()->
1970 CountCompChg()), "Compression without info" );
1972 TextFrameIndex
nTextBreak(0);
1973 tools::Long nKern
= 0;
1975 TextFrameIndex nLn
= rInf
.GetLen() == TextFrameIndex(COMPLETE_STRING
)
1976 ? TextFrameIndex(rInf
.GetText().getLength()) : rInf
.GetLen();
1978 if ( rInf
.GetFrame() && nLn
&& rInf
.SnapToGrid() &&
1979 rInf
.GetFont() && SwFontScript::CJK
== rInf
.GetFont()->GetActual() )
1981 SwTextGridItem
const*const pGrid(GetGridItem(rInf
.GetFrame()->FindPageFrame()));
1982 if ( pGrid
&& GRID_LINES_CHARS
== pGrid
->GetGridType() )
1984 const SwDoc
* pDoc
= rInf
.GetShell()->GetDoc();
1985 const sal_uInt16 nGridWidth
= GetGridWidth(*pGrid
, *pDoc
);
1987 KernArray aKernArray
;
1988 GetTextArray( rInf
.GetOut(), rInf
.GetText(), aKernArray
,
1989 sal_Int32(rInf
.GetIdx()), sal_Int32(rInf
.GetLen()));
1991 if (pGrid
->IsSnapToChars())
1993 sw::Justify::SnapToGrid(aKernArray
, rInf
.GetText(), sal_Int32(rInf
.GetIdx()),
1994 sal_Int32(rInf
.GetLen()), nGridWidth
, true);
1998 // use 0 to calculate raw width without rInf.GetSpace().
1999 sw::Justify::SnapToGridEdge(aKernArray
, sal_Int32(rInf
.GetLen()), nGridWidth
,
2003 while(nTextBreak
< rInf
.GetLen() && aKernArray
[sal_Int32(nTextBreak
)] <= nTextWidth
)
2006 return nTextBreak
+ rInf
.GetIdx();
2010 if( m_aSub
[m_nActual
].IsCapital() && nLn
)
2012 nTextBreak
= GetCapitalBreak( rInf
.GetShell(), rInf
.GetpOut(),
2013 rInf
.GetScriptInfo(), rInf
.GetText(), nTextWidth
, rInf
.GetIdx(),
2018 nKern
= CheckKerning();
2020 const OUString
* pTmpText
;
2022 TextFrameIndex nTmpIdx
;
2023 TextFrameIndex nTmpLen
;
2024 bool bTextReplaced
= false;
2026 if ( !m_aSub
[m_nActual
].IsCaseMap() )
2028 pTmpText
= &rInf
.GetText();
2029 nTmpIdx
= rInf
.GetIdx();
2034 const OUString
aSnippet(rInf
.GetText().copy(sal_Int32(rInf
.GetIdx()), sal_Int32(nLn
)));
2035 aTmpText
= m_aSub
[m_nActual
].CalcCaseMap( aSnippet
);
2036 const bool bTitle
= SvxCaseMap::Capitalize
== m_aSub
[m_nActual
].GetCaseMap();
2038 // Uaaaaahhhh!!! In title case mode, we would get wrong results
2039 if ( bTitle
&& nLn
)
2041 // check if rInf.GetIdx() is begin of word
2042 if ( !g_pBreakIt
->GetBreakIter()->isBeginWord(
2043 rInf
.GetText(), sal_Int32(rInf
.GetIdx()),
2044 g_pBreakIt
->GetLocale( m_aSub
[m_nActual
].GetLanguage() ),
2045 i18n::WordType::ANYWORD_IGNOREWHITESPACES
) )
2047 // In this case, the beginning of aTmpText is wrong.
2048 OUString
aSnippetTmp(aSnippet
.copy(0, 1));
2049 aSnippetTmp
= m_aSub
[m_nActual
].CalcCaseMap( aSnippetTmp
);
2050 aTmpText
= aTmpText
.replaceAt( 0, aSnippetTmp
.getLength(), rtl::OUStringChar(aSnippet
[0]) );
2054 pTmpText
= &aTmpText
;
2055 nTmpIdx
= TextFrameIndex(0);
2056 nTmpLen
= TextFrameIndex(aTmpText
.getLength());
2057 bTextReplaced
= true;
2060 if( rInf
.GetHyphPos() ) {
2061 sal_Int32 nHyphPos
= sal_Int32(*rInf
.GetHyphPos());
2062 const SalLayoutGlyphs
* pGlyphs
= SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
2063 &rInf
.GetOut(), *pTmpText
, nTmpIdx
.get(), nTmpLen
.get(), 0, rInf
.GetVclCache());
2064 nTextBreak
= TextFrameIndex(rInf
.GetOut().GetTextBreak(
2065 *pTmpText
, nTextWidth
,
2067 sal_Int32(nTmpIdx
), sal_Int32(nTmpLen
),
2068 nKern
, rInf
.GetVclCache(), pGlyphs
));
2069 *rInf
.GetHyphPos() = TextFrameIndex((nHyphPos
== -1) ? COMPLETE_STRING
: nHyphPos
);
2073 SwFntAccess
aFntAccess(m_aSub
[m_nActual
].m_nFontCacheId
, m_aSub
[m_nActual
].m_nFontIndex
,
2074 &m_aSub
[m_nActual
], rInf
.GetShell());
2075 const SalLayoutGlyphs
* pGlyphs
= SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rInf
.GetOut(),
2076 *pTmpText
, nTmpIdx
.get(), nTmpLen
.get(), 0, rInf
.GetVclCache());
2077 nTextBreak
= TextFrameIndex(rInf
.GetOut().GetTextBreak(
2078 *pTmpText
, nTextWidth
,
2079 sal_Int32(nTmpIdx
), sal_Int32(nTmpLen
),
2080 nKern
, rInf
.GetVclCache(), pGlyphs
));
2083 if (bTextReplaced
&& sal_Int32(nTextBreak
) != -1)
2085 if ( nTmpLen
!= nLn
)
2086 nTextBreak
= sw_CalcCaseMap( *this, rInf
.GetText(),
2087 rInf
.GetIdx(), nLn
, nTextBreak
);
2089 nTextBreak
= nTextBreak
+ rInf
.GetIdx();
2093 TextFrameIndex nTextBreak2
= sal_Int32(nTextBreak
) == -1
2094 ? TextFrameIndex(COMPLETE_STRING
)
2097 // tdf112290 tdf136588 Break the line correctly only if there is an image inline,
2098 // and the image wider than the line...
2099 if (GetCaseMap() == SvxCaseMap::SmallCaps
&& TextFrameIndex(COMPLETE_STRING
) == nTextBreak2
&&
2100 ! bCompress
&& nTextWidth
== 0)
2101 // If nTextWidth == 0 means the line is full, we have to break it
2102 nTextBreak2
= TextFrameIndex(1);
2107 nTextBreak2
= nTextBreak2
- rInf
.GetIdx();
2109 if( nTextBreak2
< nLn
)
2111 if( !nTextBreak2
&& nLn
)
2112 nLn
= TextFrameIndex(1);
2113 else if (nLn
> nTextBreak2
+ nTextBreak2
)
2114 nLn
= nTextBreak2
+ nTextBreak2
;
2115 KernArray aKernArray
;
2116 GetTextArray( rInf
.GetOut(), rInf
.GetText(), aKernArray
,
2117 sal_Int32(rInf
.GetIdx()), sal_Int32(nLn
));
2118 if( rInf
.GetScriptInfo()->Compress( aKernArray
, rInf
.GetIdx(), nLn
,
2119 rInf
.GetKanaComp(), o3tl::narrowing
<sal_uInt16
>(GetHeight( m_nActual
)),
2120 lcl_IsFullstopCentered( rInf
.GetOut() ) ) )
2122 tools::Long nKernAdd
= nKern
;
2123 TextFrameIndex
const nTmpBreak
= nTextBreak2
;
2124 if( nKern
&& nTextBreak2
)
2125 nKern
*= sal_Int32(nTextBreak2
) - 1;
2126 while (nTextBreak2
< nLn
&& nTextWidth
>= aKernArray
[sal_Int32(nTextBreak2
)] + nKern
)
2131 if( rInf
.GetHyphPos() )
2132 *rInf
.GetHyphPos() += nTextBreak2
- nTmpBreak
; // It's not perfect
2135 nTextBreak2
= nTextBreak2
+ rInf
.GetIdx();
2140 bool SwDrawTextInfo::ApplyAutoColor( vcl::Font
* pFont
)
2142 const vcl::Font
& rFnt
= pFont
? *pFont
: GetOut().GetFont();
2143 Color nNewColor
= COL_BLACK
;
2144 bool bChgFntColor
= false;
2145 bool bChgLineColor
= false;
2147 const SwViewShell
*pVSh
= GetShell();
2148 const bool bOutputToWindow(pVSh
&& (pVSh
->GetWin() || pVSh
->isOutputToWindow()));
2150 if (pVSh
&& !bOutputToWindow
&& pVSh
->GetViewOptions()->IsBlackFont())
2152 if ( COL_BLACK
!= rFnt
.GetColor() )
2153 bChgFntColor
= true;
2155 if ( (COL_BLACK
!= GetOut().GetLineColor()) ||
2156 (COL_BLACK
!= GetOut().GetOverlineColor()) )
2157 bChgLineColor
= true;
2161 // FontColor has to be changed if:
2162 // 1. FontColor = AUTO or 2. IsAlwaysAutoColor is set
2163 // LineColor has to be changed if:
2164 // 1. IsAlwaysAutoColor is set
2166 bChgLineColor
= pVSh
&& bOutputToWindow
&&
2167 pVSh
->GetAccessibilityOptions()->IsAlwaysAutoColor();
2169 bChgFntColor
= COL_AUTO
== rFnt
.GetColor() || bChgLineColor
;
2173 // check if current background has a user defined setting
2174 std::optional
<Color
> pCol
;
2176 pCol
= GetFont()->GetBackColor();
2177 if( ! pCol
|| COL_TRANSPARENT
== *pCol
)
2179 const SvxBrushItem
* pItem
;
2180 SwRect aOrigBackRect
;
2181 drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes
;
2184 /// consider, that [GetBackgroundBrush(...)] can set <pCol>
2185 /// - see implementation in /core/layout/paintfrm.cxx
2186 /// OD 21.08.2002 #99657#
2187 /// There is a user defined setting for the background, if there
2188 /// is a background brush and its color is *not* "no fill"/"auto fill".
2189 if( GetFrame()->GetBackgroundBrush( aFillAttributes
, pItem
, pCol
, aOrigBackRect
, false, /*bConsiderTextBox=*/true ) )
2191 if (aFillAttributes
&& aFillAttributes
->isUsed())
2193 // First see if fill attributes provide a color.
2194 pCol
= Color(aFillAttributes
->getAverageColor(aGlobalRetoucheColor
.getBColor()));
2197 // If not, then fall back to the old brush item.
2200 pCol
= pItem
->GetColor();
2203 /// OD 30.08.2002 #99657#
2204 /// determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it.
2205 if ( *pCol
== COL_TRANSPARENT
)
2212 // no user defined color at paragraph or font background
2214 pCol
= aGlobalRetoucheColor
;
2216 if (pVSh
&& bOutputToWindow
)
2218 // here we determine the preferred window text color for painting
2219 const SwViewOption
* pViewOption
= pVSh
->GetViewOptions();
2220 if(pViewOption
->IsPagePreview() &&
2221 !officecfg::Office::Common::Accessibility::IsForPagePreviews::get())
2222 nNewColor
= COL_BLACK
;
2224 // we take the font color from the appearance page
2225 nNewColor
= pViewOption
->GetFontColor();
2228 // change painting color depending of dark/bright background
2229 Color
aTmpColor( nNewColor
);
2230 if ( pCol
->IsDark() && aTmpColor
.IsDark() )
2231 nNewColor
= COL_WHITE
;
2232 else if ( pCol
->IsBright() && aTmpColor
.IsBright() )
2233 nNewColor
= COL_BLACK
;
2237 if ( bChgFntColor
|| bChgLineColor
)
2239 Color
aNewColor( nNewColor
);
2243 if ( pFont
&& aNewColor
!= pFont
->GetColor() )
2245 // only set the new color at the font passed as argument
2246 pFont
->SetColor( aNewColor
);
2248 else if ( aNewColor
!= GetOut().GetFont().GetColor() )
2250 // set new font with new color at output device
2251 vcl::Font
aFont( rFnt
);
2252 aFont
.SetColor( aNewColor
);
2253 GetOut().SetFont( aFont
);
2257 // the underline and overline colors have to be set separately
2258 if ( bChgLineColor
)
2260 // get current font color or color set at output device
2261 aNewColor
= pFont
? pFont
->GetColor() : GetOut().GetFont().GetColor();
2262 if ( aNewColor
!= GetOut().GetLineColor() )
2263 GetOut().SetLineColor( aNewColor
);
2264 if ( aNewColor
!= GetOut().GetOverlineColor() )
2265 GetOut().SetOverlineColor( aNewColor
);
2274 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */