LibreOffice Module sw (master)  1
fntcache.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <sal/config.h>
22 
23 #include <cstdint>
24 
25 #include <i18nlangtag/mslangid.hxx>
26 #include <officecfg/Office/Common.hxx>
27 #include <vcl/outdev.hxx>
28 #include <vcl/lineinfo.hxx>
29 #include <vcl/metric.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/lazydelete.hxx>
32 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
33 #include <com/sun/star/i18n/WordType.hpp>
34 #include <com/sun/star/i18n/XBreakIterator.hpp>
35 #include <breakit.hxx>
36 #include <paintfrm.hxx>
37 #include <viewsh.hxx>
38 #include <viewopt.hxx>
39 #include <fntcache.hxx>
41 #include <swfont.hxx>
42 #include <wrong.hxx>
43 #include <txtfrm.hxx>
44 #include <pagefrm.hxx>
45 #include <tgrditem.hxx>
46 #include <scriptinfo.hxx>
47 #include <editeng/brushitem.hxx>
48 #include <swmodule.hxx>
49 #include <accessibilityoptions.hxx>
52 #include <doc.hxx>
53 #include <editeng/fhgtitem.hxx>
54 #include <vcl/glyphitem.hxx>
55 #include <vcl/vcllayout.hxx>
56 #include <docsh.hxx>
57 #include <strings.hrc>
58 #include <fntcap.hxx>
60 
61 using namespace ::com::sun::star;
62 
63 // global variables declared in fntcache.hxx
64 // FontCache is created in txtinit.cxx TextInit_ and deleted in TextFinit
65 SwFntCache *pFntCache = nullptr;
66 // last Font set by ChgFntCache
67 SwFntObj *pLastFont = nullptr;
68 
69 constexpr Color gWaveCol(COL_GRAY);
70 
72 MapMode* SwFntObj::s_pPixMap = nullptr;
74 
80 {
82  OUString m_aText;
83  sal_Int32 m_nIndex;
84  sal_Int32 m_nLength;
85 
86 };
87 
92 {
94  tools::Long m_nTextWidth = -1; // -1 = not computed yet
95 };
96 
97 namespace
98 {
99 
100 tools::Long EvalGridWidthAdd( const SwTextGridItem *const pGrid, const SwDrawTextInfo &rInf )
101 {
102  SwDocShell* pDocShell = rInf.GetShell()->GetDoc()->GetDocShell();
103  SfxStyleSheetBasePool* pBasePool = pDocShell->GetStyleSheetPool();
104 
105  SfxStyleSheetBase* pStyle = pBasePool->Find(SwResId(STR_POOLCOLL_STANDARD), SfxStyleFamily::Para);
106  SfxItemSet& aTmpSet = pStyle->GetItemSet();
107  const SvxFontHeightItem &aDefaultFontItem = aTmpSet.Get(RES_CHRATR_CJK_FONTSIZE);
108 
109  const SwDoc* pDoc = rInf.GetShell()->GetDoc();
110  const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
111  const sal_uInt32 nFontHeight = aDefaultFontItem.GetHeight();
112  const tools::Long nGridWidthAdd = nGridWidth > nFontHeight ? nGridWidth - nFontHeight : 0;
113  if( SwFontScript::Latin == rInf.GetFont()->GetActual() )
114  return nGridWidthAdd / 2;
115 
116  return nGridWidthAdd;
117 }
118 
119 }
120 
121 bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r)
122 {
123  if (l.m_pOutputDevice.get() < r.m_pOutputDevice.get())
124  return true;
125  if (l.m_pOutputDevice.get() > r.m_pOutputDevice.get())
126  return false;
127  if (l.m_nIndex < r.m_nIndex)
128  return true;
129  if (l.m_nIndex > r.m_nIndex)
130  return false;
131  if (l.m_nLength < r.m_nLength)
132  return true;
133  if (l.m_nLength > r.m_nLength)
134  return false;
135 
136  // Comparing strings is expensive, so compare them:
137  // - only at the end of this function
138  // - only once
139  // - only the relevant substring (if the index/length is not out of bounds)
140  sal_Int32 nRet = 0;
141  if (l.m_nLength < 0 || l.m_nIndex < 0 || l.m_nIndex + l.m_nLength > l.m_aText.getLength())
142  nRet = l.m_aText.compareTo(r.m_aText);
143  else
144  nRet = memcmp(l.m_aText.getStr() + l.m_nIndex, r.m_aText.getStr() + r.m_nIndex,
145  l.m_nLength * sizeof(sal_Unicode));
146  if (nRet < 0)
147  return true;
148  if (nRet > 0)
149  return false;
150 
151  return false;
152 };
153 
155 {
156  if ( pLastFont )
157  {
158  pLastFont->Unlock();
159  pLastFont = nullptr;
160  }
161  SwCache::Flush( );
162 }
163 
164 SwFntObj::SwFntObj(const SwSubFont &rFont, std::uintptr_t nFontCacheId, SwViewShell const *pSh)
165  : SwCacheObj(reinterpret_cast<void *>(nFontCacheId))
166  , m_aFont(rFont)
167  , m_pScrFont(nullptr)
168  , m_pPrtFont(&m_aFont)
169  , m_pPrinter(nullptr)
170  , m_nGuessedLeading(USHRT_MAX)
171  , m_nExtLeading(USHRT_MAX)
172  , m_nScrAscent(0)
173  , m_nPrtAscent(USHRT_MAX)
174  , m_nScrHeight(0)
175  , m_nPrtHeight(USHRT_MAX)
176  , m_nPropWidth(rFont.GetPropWidth())
177 {
178  m_nZoom = pSh ? pSh->GetViewOptions()->GetZoom() : USHRT_MAX;
179  m_bSymbol = RTL_TEXTENCODING_SYMBOL == m_aFont.GetCharSet();
183  && !m_aFont.IsWordLineMode();
184  m_aFont.SetLanguage(rFont.GetLanguage());
185 }
186 
188 {
189  if ( m_pScrFont != m_pPrtFont )
190  delete m_pScrFont;
191  if ( m_pPrtFont != &m_aFont )
192  delete m_pPrtFont;
193 }
194 
196 {
197  if ( m_nPropWidth == 100 || m_pPrinter == &rPrt )
198  return;
199 
200  if( m_pScrFont != m_pPrtFont )
201  delete m_pScrFont;
202  if( m_pPrtFont != &m_aFont )
203  delete m_pPrtFont;
204 
205  const vcl::Font aOldFnt( rPrt.GetFont() );
206  const_cast<OutputDevice&>(rPrt).SetFont( m_aFont );
207  const FontMetric aWinMet( rPrt.GetFontMetric() );
208  const_cast<OutputDevice&>(rPrt).SetFont( aOldFnt );
209  auto nWidth = ( aWinMet.GetFontSize().Width() * m_nPropWidth ) / 100;
210 
211  if( !nWidth )
212  ++nWidth;
213  m_pPrtFont = new vcl::Font( m_aFont );
215  m_pScrFont = nullptr;
216 
217 }
218 
223 static SalLayoutGlyphs* lcl_CreateLayout(const SwTextGlyphsKey& rKey, std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it)
224 {
225  assert (!it->second.m_aTextGlyphs.IsValid());
226 
227  if (rKey.m_nIndex >= rKey.m_aText.getLength())
228  // Same as in OutputDevice::GetTextArray().
229  return nullptr;
230 
231  // Calculate glyph items.
232  std::unique_ptr<SalLayout> pLayout
233  = rKey.m_pOutputDevice->ImplLayout(rKey.m_aText, rKey.m_nIndex, rKey.m_nLength, Point(0, 0), 0,
234  nullptr, SalLayoutFlags::GlyphItemsOnly);
235  if (!pLayout)
236  return nullptr;
237 
238  // Remember the calculation result.
239  it->second.m_aTextGlyphs = pLayout->GetGlyphs();
240 
241  return &it->second.m_aTextGlyphs;
242 }
243 
245 {
246  std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it = m_aTextGlyphs.find(key);
247  if(it != m_aTextGlyphs.end())
248  {
249  if( it->second.m_aTextGlyphs.IsValid())
250  return &it->second.m_aTextGlyphs;
251  // Do not try to create the layout here. If a cache item exists, it's already
252  // been attempted and the layout was invalid (this happens with MultiSalLayout).
253  // So in that case this is a cached failure.
254  return nullptr;
255  }
256  it = m_aTextGlyphs.insert_or_assign( it, key, SwTextGlyphsData());
257  return lcl_CreateLayout(key, it);
258 }
259 
261 {
262  std::map<SwTextGlyphsKey, SwTextGlyphsData>::iterator it = m_aTextGlyphs.find(key);
263  if(it != m_aTextGlyphs.end() && it->second.m_nTextWidth >= 0)
264  return it->second.m_nTextWidth;
265  if(it == m_aTextGlyphs.end())
266  {
267  it = m_aTextGlyphs.insert_or_assign( it, key, SwTextGlyphsData());
268  lcl_CreateLayout(key, it);
269  }
270  it->second.m_nTextWidth = key.m_pOutputDevice->GetTextWidth(key.m_aText, key.m_nIndex, key.m_nLength, vclCache,
271  it->second.m_aTextGlyphs.IsValid() ? &it->second.m_aTextGlyphs : nullptr );
272  assert(it->second.m_nTextWidth >= 0);
273  return it->second.m_nTextWidth;
274 }
275 
277 {
278  m_aTextGlyphs.clear();
279 }
280 
281 /*
282  * returns whether we have to adjust the output font to resemble
283  * the formatting font
284  *
285  * _Not_ necessary if
286  *
287  * 1. RefDef == OutDev (text formatting, online layout...)
288  * 2. PDF export from online layout
289  * 3. Prospect/PagePreview printing
290  */
291 static bool lcl_IsFontAdjustNecessary( const vcl::RenderContext& rOutDev,
292  const vcl::RenderContext& rRefDev )
293 {
294  return &rRefDev != &rOutDev &&
295  OUTDEV_WINDOW != rRefDev.GetOutDevType() &&
296  ( OUTDEV_PRINTER != rRefDev.GetOutDevType() ||
297  OUTDEV_PRINTER != rOutDev.GetOutDevType() );
298 }
299 
300 namespace {
301 
302 struct CalcLinePosData
303 {
304  SwDrawTextInfo& rInf;
305  vcl::Font& rFont;
306  TextFrameIndex nCnt;
307  const bool bSwitchH2V;
308  const bool bSwitchH2VLRBT;
309  const bool bSwitchL2R;
310  tools::Long nHalfSpace;
311  tools::Long* pKernArray;
312  const bool bBidiPor;
313 
314  CalcLinePosData( SwDrawTextInfo& _rInf, vcl::Font& _rFont,
315  TextFrameIndex const _nCnt, const bool _bSwitchH2V, const bool _bSwitchH2VLRBT, const bool _bSwitchL2R,
316  tools::Long _nHalfSpace, tools::Long* _pKernArray, const bool _bBidiPor) :
317  rInf( _rInf ),
318  rFont( _rFont ),
319  nCnt( _nCnt ),
320  bSwitchH2V( _bSwitchH2V ),
321  bSwitchH2VLRBT( _bSwitchH2VLRBT ),
322  bSwitchL2R( _bSwitchL2R ),
323  nHalfSpace( _nHalfSpace ),
324  pKernArray( _pKernArray ),
325  bBidiPor( _bBidiPor )
326  {
327  }
328 };
329 
330 }
331 
332 // Computes the start and end position of an underline. This function is called
333 // from the DrawText-method (for underlining misspelled words or smarttag terms).
334 static void lcl_calcLinePos( const CalcLinePosData &rData,
335  Point &rStart, Point &rEnd, TextFrameIndex const nStart, TextFrameIndex const nWrLen)
336 {
337  tools::Long nBlank = 0;
338  const TextFrameIndex nEnd = nStart + nWrLen;
339  const tools::Long nTmpSpaceAdd = rData.rInf.GetSpace() / SPACING_PRECISION_FACTOR;
340 
341  if ( nEnd < rData.nCnt
342  && CH_BLANK == rData.rInf.GetText()[sal_Int32(rData.rInf.GetIdx() + nEnd)] )
343  {
344  if (nEnd + TextFrameIndex(1) == rData.nCnt)
345  nBlank -= nTmpSpaceAdd;
346  else
347  nBlank -= rData.nHalfSpace;
348  }
349 
350  // determine start, end and length of wave line
351  sal_Int32 nKernStart = nStart ? rData.pKernArray[sal_Int32(nStart) - 1] : 0;
352  sal_Int32 nKernEnd = rData.pKernArray[sal_Int32(nEnd) - 1];
353 
354  const Degree10 nDir = rData.bBidiPor ? 1800_deg10
355  : UnMapDirection(rData.rFont.GetOrientation(),
356  rData.bSwitchH2V, rData.bSwitchH2VLRBT);
357 
358  switch ( nDir.get() )
359  {
360  case 0 :
361  rStart.AdjustX(nKernStart );
362  rEnd.setX( nBlank + rData.rInf.GetPos().X() + nKernEnd );
363  rEnd.setY( rData.rInf.GetPos().Y() );
364  break;
365  case 900 :
366  rStart.AdjustY( -nKernStart );
367  rEnd.setX( rData.rInf.GetPos().X() );
368  rEnd.setY( nBlank + rData.rInf.GetPos().Y() - nKernEnd );
369  break;
370  case 1800 :
371  rStart.AdjustX( -nKernStart );
372  rEnd.setX( rData.rInf.GetPos().X() - nKernEnd - nBlank );
373  rEnd.setY( rData.rInf.GetPos().Y() );
374  break;
375  case 2700 :
376  rStart.AdjustY(nKernStart );
377  rEnd.setX( rData.rInf.GetPos().X() );
378  rEnd.setY( nBlank + rData.rInf.GetPos().Y() + nKernEnd );
379  break;
380  }
381 
382  if ( rData.bSwitchL2R )
383  {
384  rData.rInf.GetFrame()->SwitchLTRtoRTL( rStart );
385  rData.rInf.GetFrame()->SwitchLTRtoRTL( rEnd );
386  }
387 
388  if ( rData.bSwitchH2V )
389  {
390  rData.rInf.GetFrame()->SwitchHorizontalToVertical( rStart );
391  rData.rInf.GetFrame()->SwitchHorizontalToVertical( rEnd );
392  }
393 }
394 
395 // Returns the Ascent of the Font on the given output device;
396 // it may be necessary to create the screen font first.
397 sal_uInt16 SwFntObj::GetFontAscent( const SwViewShell *pSh, const OutputDevice& rOut )
398 {
399  sal_uInt16 nRet = 0;
400  const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
401 
402  if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
403  {
404  CreateScrFont( *pSh, rOut );
405  OSL_ENSURE( USHRT_MAX != m_nScrAscent, "nScrAscent is going berzerk" );
406  nRet = m_nScrAscent;
407  }
408  else
409  {
410  if (m_nPrtAscent == USHRT_MAX) // printer ascent unknown?
411  {
412  CreatePrtFont( rOut );
413  const vcl::Font aOldFnt( rRefDev.GetFont() );
414  const_cast<OutputDevice&>(rRefDev).SetFont( *m_pPrtFont );
415  const FontMetric aOutMet( rRefDev.GetFontMetric() );
416  m_nPrtAscent = o3tl::narrowing<sal_uInt16>(aOutMet.GetAscent());
417  const_cast<OutputDevice&>(rRefDev).SetFont( aOldFnt );
418  }
419 
420  nRet = m_nPrtAscent;
421  }
422 
423 #if !defined(MACOSX) // #i89844# extleading is below the line for Mac
424  // TODO: move extleading below the line for all platforms too
425  nRet += GetFontLeading( pSh, rRefDev );
426 #endif
427 
428  OSL_ENSURE( USHRT_MAX != nRet, "GetFontAscent returned USHRT_MAX" );
429  return nRet;
430 }
431 
432 // Returns the height of the Font on the given output device;
433 // it may be necessary to create the screen font first.
434 sal_uInt16 SwFntObj::GetFontHeight( const SwViewShell* pSh, const OutputDevice& rOut )
435 {
436  sal_uInt16 nRet = 0;
437  const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
438 
439  if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
440  {
441  CreateScrFont( *pSh, rOut );
442  OSL_ENSURE( USHRT_MAX != m_nScrHeight, "nScrHeight is going berzerk" );
443  nRet = m_nScrHeight + GetFontLeading( pSh, rRefDev );
444  }
445  else
446  {
447  if (m_nPrtHeight == USHRT_MAX) // printer height unknown?
448  {
449  CreatePrtFont( rOut );
450  const vcl::Font aOldFnt( rRefDev.GetFont() );
451  const_cast<OutputDevice&>(rRefDev).SetFont( *m_pPrtFont );
452  m_nPrtHeight = o3tl::narrowing<sal_uInt16>(rRefDev.GetTextHeight());
453 
454 #if OSL_DEBUG_LEVEL > 0
455  // Check if vcl did not change the meaning of GetTextHeight
456  const FontMetric aOutMet( rRefDev.GetFontMetric() );
457  tools::Long nTmpPrtHeight = o3tl::narrowing<sal_uInt16>(aOutMet.GetAscent()) + aOutMet.GetDescent();
458  // #i106098#: do not compare with == here due to rounding error
459  OSL_ENSURE( std::abs(nTmpPrtHeight - m_nPrtHeight) < 3,
460  "GetTextHeight != Ascent + Descent" );
461 #endif
462 
463  const_cast<OutputDevice&>(rRefDev).SetFont( aOldFnt );
464  }
465 
466  nRet = m_nPrtHeight + GetFontLeading( pSh, rRefDev );
467  }
468 
469  OSL_ENSURE( USHRT_MAX != nRet, "GetFontHeight returned USHRT_MAX" );
470  return nRet;
471 }
472 
473 sal_uInt16 SwFntObj::GetFontLeading( const SwViewShell *pSh, const OutputDevice& rOut )
474 {
475  sal_uInt16 nRet = 0;
476 
477  if ( pSh )
478  {
479  if ( USHRT_MAX == m_nGuessedLeading || USHRT_MAX == m_nExtLeading )
480  {
481  SolarMutexGuard aGuard;
482 
483  const vcl::Font aOldFnt( rOut.GetFont() );
484  const_cast<OutputDevice&>(rOut).SetFont( *m_pPrtFont );
485  const FontMetric aMet( rOut.GetFontMetric() );
486  const_cast<OutputDevice&>(rOut).SetFont( aOldFnt );
487  m_bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
488  GuessLeading( *pSh, aMet );
489  m_nExtLeading = o3tl::narrowing<sal_uInt16>(aMet.GetExternalLeading());
490  /* HACK: FIXME There is something wrong with Writer's bullet rendering, causing lines
491  with bullets to be higher than they should be. I think this is because
492  Writer uses font's external leading incorrect, as the vertical distance
493  added to every line instead of only a distance between multiple lines,
494  which means a single bullet has external leading added even though it
495  shouldn't, but frankly this is just an educated guess rather than understanding
496  Writer's layout (heh).
497  Symbol font in some documents is 'StarSymbol; Arial Unicode MS', and Windows
498  machines often do not have StarSymbol, falling back to Arial Unicode MS, which
499  has unusually high external leading. So just reset external leading for fonts
500  which are used to bullets, as those should not be used on multiple lines anyway,
501  so in correct rendering external leading should be irrelevant anyway.
502  Interestingly enough, bSymbol is false for 'StarSymbol; Arial Unicode MS', so
503  also check explicitly.
504  */
506  m_nExtLeading = 0;
507  }
508 
510  const bool bBrowse = ( pSh->GetWin() &&
511  pSh->GetViewOptions()->getBrowseMode() &&
512  !pSh->GetViewOptions()->IsPrtFormat() );
513 
514  if ( !bBrowse && rIDSA.get(DocumentSettingId::ADD_EXT_LEADING) )
515  nRet = m_nExtLeading;
516  else
517  nRet = m_nGuessedLeading;
518  }
519 
520  OSL_ENSURE( USHRT_MAX != nRet, "GetFontLeading returned USHRT_MAX" );
521  return nRet;
522 }
523 
524 // pOut is the output device, not the reference device
525 void SwFntObj::CreateScrFont( const SwViewShell& rSh, const OutputDevice& rOut )
526 {
527  if ( m_pScrFont )
528  return;
529 
530  // any changes to the output device are reset at the end of the function
531  OutputDevice* pOut = const_cast<OutputDevice*>(&rOut);
532 
533  // Save old font
534  vcl::Font aOldOutFont( pOut->GetFont() );
535 
536  m_nScrHeight = USHRT_MAX;
537 
538  // Condition for output font / refdev font adjustment
539  OutputDevice* pPrt = &rSh.GetRefDev();
540 
541  if( !rSh.GetWin() ||
542  !rSh.GetViewOptions()->getBrowseMode() ||
543  rSh.GetViewOptions()->IsPrtFormat() )
544  {
545  // After CreatePrtFont m_pPrtFont is the font which is actually used
546  // by the reference device
547  CreatePrtFont( *pPrt );
548  m_pPrinter = pPrt;
549 
550  // save old reference device font
551  vcl::Font aOldPrtFnt( pPrt->GetFont() );
552 
553  // set the font used at the reference device at the reference device
554  // and the output device
555  pPrt->SetFont( *m_pPrtFont );
556  pOut->SetFont( *m_pPrtFont );
557 
558  // This should be the default for pScrFont.
560 
561  FontMetric aMet = pPrt->GetFontMetric( );
562  // Don't lose "faked" properties of the logical font that don't truly
563  // exist in the physical font metrics which vcl which fake up for us
564  aMet.SetWeight(m_pScrFont->GetWeight());
565  aMet.SetItalic(m_pScrFont->GetItalic());
566 
567  m_bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
568 
569  if ( USHRT_MAX == m_nGuessedLeading )
570  GuessLeading( rSh, aMet );
571 
572  if ( USHRT_MAX == m_nExtLeading )
573  m_nExtLeading = o3tl::narrowing<sal_uInt16>(aMet.GetExternalLeading());
574 
575  // reset the original reference device font
576  pPrt->SetFont( aOldPrtFnt );
577  }
578  else
579  {
580  m_bSymbol = RTL_TEXTENCODING_SYMBOL == m_aFont.GetCharSet();
581  if ( m_nGuessedLeading == USHRT_MAX )
582  m_nGuessedLeading = 0;
583 
584  // no external leading in browse mode
585  if ( m_nExtLeading == USHRT_MAX )
586  m_nExtLeading = 0;
587 
589  }
590 
591  m_nScrAscent = o3tl::narrowing<sal_uInt16>(pOut->GetFontMetric().GetAscent());
592  if ( USHRT_MAX == m_nScrHeight )
593  m_nScrHeight = o3tl::narrowing<sal_uInt16>(pOut->GetTextHeight());
594 
595  // reset original output device font
596  pOut->SetFont( aOldOutFont );
597 }
598 
600 #if defined(_WIN32)
601  rSh
602 #endif
603  , const FontMetric& rMet )
604 {
605  // If leading >= 5, this seems to be enough leading.
606  // Nothing has to be done.
607  if ( rMet.GetInternalLeading() >= 5 )
608  {
609  m_nGuessedLeading = 0;
610  return;
611  }
612 
613 #if defined(_WIN32)
614  OutputDevice *pWin = rSh.GetWin() ?
615  rSh.GetWin()->GetOutDev() :
617  if ( pWin )
618  {
619  MapMode aTmpMap( MapUnit::MapTwip );
620  MapMode aOldMap = pWin->GetMapMode( );
621  pWin->SetMapMode( aTmpMap );
622  const vcl::Font aOldFnt( pWin->GetFont() );
623  pWin->SetFont( *m_pPrtFont );
624  const FontMetric aWinMet( pWin->GetFontMetric() );
625  const sal_uInt16 nWinHeight = sal_uInt16( aWinMet.GetFontSize().Height() );
626  if( m_pPrtFont->GetFamilyName().indexOf( aWinMet.GetFamilyName() ) != -1 )
627  {
628  // If the Leading on the Window is also 0, then it has to stay
629  // that way (see also StarMath).
630  tools::Long nTmpLeading = aWinMet.GetInternalLeading();
631  if( nTmpLeading <= 0 )
632  {
633  pWin->SetFont( rMet );
634  nTmpLeading = pWin->GetFontMetric().GetInternalLeading();
635  if( nTmpLeading < 0 )
636  m_nGuessedLeading = 0;
637  else
638  m_nGuessedLeading = sal_uInt16(nTmpLeading);
639  }
640  else
641  {
642  m_nGuessedLeading = sal_uInt16(nTmpLeading);
643  // Manta-Hack #50153#:
644  // Wer beim Leading luegt, luegt moeglicherweise auch beim
645  // Ascent/Descent, deshalb wird hier ggf. der Font ein wenig
646  // tiefergelegt, ohne dabei seine Hoehe zu aendern.
647  // (above original comment preserved for cultural reasons)
648  // Those who lie about their Leading, may lie about their
649  // Ascent/Descent as well, hence the Font will be lowered a
650  // little without changing its height.
651  tools::Long nDiff = std::min( rMet.GetDescent() - aWinMet.GetDescent(),
652  aWinMet.GetAscent() - rMet.GetAscent() - nTmpLeading );
653  if( nDiff > 0 )
654  {
655  OSL_ENSURE( m_nPrtAscent < USHRT_MAX, "GuessLeading: PrtAscent-Fault" );
656  if ( m_nPrtAscent < USHRT_MAX )
657  m_nPrtAscent = m_nPrtAscent + o3tl::narrowing<sal_uInt16>(( 2 * nDiff ) / 5);
658  }
659  }
660  }
661  else
662  {
663  // If all else fails, take 15% of the height, as empirically
664  // determined by CL
665  m_nGuessedLeading = (nWinHeight * 15) / 100;
666  }
667  pWin->SetFont( aOldFnt );
668  pWin->SetMapMode( aOldMap );
669  }
670  else
671  m_nGuessedLeading = 0;
672 #else
673  m_nGuessedLeading = 0;
674 #endif
675 }
676 
677 // Set the font at the given output device; for screens it may be
678 // necessary to do some adjustment first.
680 {
681  const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
682 
683  if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
684  {
685  CreateScrFont( *pSh, rOut );
686  if( !GetScrFont()->IsSameInstance( rOut.GetFont() ) )
687  rOut.SetFont( *m_pScrFont );
690  }
691  else
692  {
693  CreatePrtFont( rOut );
694  if( !m_pPrtFont->IsSameInstance( rOut.GetFont() ) )
695  rOut.SetFont( *m_pPrtFont );
696  }
697 
698  // Here, we actually do not need the leading values, but by calling
699  // GetFontLeading() we assure that the values are calculated for later use.
700  GetFontLeading( pSh, rRefDev );
701 }
702 
703 #define WRONG_SHOW_MIN 5
704 
705 /*
706  * Output text:
707  * on screen => DrawTextArray
708  * on printer, !Kerning => DrawText
709  * on printer + Kerning => DrawStretchText
710  */
712 {
713  if ( ( cChar < 0x3001 || cChar > 0x3002 ) &&
714  ( cChar < 0x3008 || cChar > 0x3011 ) &&
715  ( cChar < 0x3014 || cChar > 0x301F ) &&
716  0xFF62 != cChar && 0xFF63 != cChar )
717  // no punctuation
718  return SwScriptInfo::NONE;
719  else if ( 0x3001 == cChar || 0x3002 == cChar ||
720  0x3009 == cChar || 0x300B == cChar ||
721  0x300D == cChar || 0x300F == cChar ||
722  0x3011 == cChar || 0x3015 == cChar ||
723  0x3017 == cChar || 0x3019 == cChar ||
724  0x301B == cChar || 0x301E == cChar ||
725  0x301F == cChar || 0xFF63 == cChar )
726  // right punctuation
728 
730 }
731 
732 static bool lcl_IsMonoSpaceFont( const vcl::RenderContext& rOut )
733 {
734  const tools::Long nWidth1 = rOut.GetTextWidth( OUString( u'\x3008' ) );
735  const tools::Long nWidth2 = rOut.GetTextWidth( OUString( u'\x307C' ) );
736  return nWidth1 == nWidth2;
737 }
738 
739 static bool lcl_IsFullstopCentered( const vcl::RenderContext& rOut )
740 {
741  const FontMetric aMetric( rOut.GetFontMetric() );
742  return aMetric.IsFullstopCentered() ;
743 }
744 
745 /* This helper structure (SwForbidden) contains the already marked parts of the string
746  to avoid double lines (e.g grammar + spell check error) */
747 
748 typedef std::vector<std::pair<TextFrameIndex, TextFrameIndex>> SwForbidden;
749 
751  SwForbidden &rForbidden,
752  const SwDrawTextInfo &rInf,
753  sw::WrongListIterator *pWList,
754  const CalcLinePosData &rCalcLinePosData,
755  const Size &rPrtFontSize )
756 {
757  if (!pWList) return;
758 
759  TextFrameIndex nStart = rInf.GetIdx();
760  TextFrameIndex nWrLen = rInf.GetLen();
761 
762  // check if respective data is available in the current text range
763  if (!pWList->Check( nStart, nWrLen ))
764  {
765  return;
766  }
767 
768  tools::Long nHght = rInf.GetOut().LogicToPixel( rPrtFontSize ).Height();
769 
770  // Draw wavy lines for spell and grammar errors only if font is large enough.
771  // Lines for smart tags will always be drawn.
772  if (pWList != rInf.GetSmartTags() && WRONG_SHOW_MIN >= nHght)
773  {
774  return;
775  }
776 
777  SwForbidden::iterator pIter = rForbidden.begin();
778  if (rInf.GetOut().GetConnectMetaFile())
779  rInf.GetOut().Push();
780 
781  const Color aCol( rInf.GetOut().GetLineColor() );
782 
783  // iterate over all ranges stored in the respective SwWrongList
784  do
785  {
786  nStart -= rInf.GetIdx();
787 
788  const TextFrameIndex nEnd = nStart + nWrLen;
789  TextFrameIndex nNext = nStart;
790  while( nNext < nEnd )
791  {
792  while( pIter != rForbidden.end() && pIter->second <= nNext )
793  ++pIter;
794 
795  const TextFrameIndex nNextStart = nNext;
796  TextFrameIndex nNextEnd = nEnd;
797 
798  if( pIter == rForbidden.end() || nNextEnd <= pIter->first )
799  {
800  // No overlapping mark up found
801  rForbidden.insert(pIter, std::make_pair(nNextStart, nNextEnd));
802  pIter = rForbidden.begin();
803  nNext = nEnd;
804  }
805  else
806  {
807  nNext = pIter->second;
808  if( nNextStart < pIter->first )
809  {
810  nNextEnd = pIter->first;
811  pIter->first = nNextStart;
812  }
813  else
814  continue;
815  }
816  // determine line pos
817  Point aStart( rInf.GetPos() );
818  Point aEnd;
819  lcl_calcLinePos( rCalcLinePosData, aStart, aEnd, nNextStart, nNextEnd - nNextStart );
820 
821  SwWrongArea const*const wrongArea = pWList->GetWrongElement(nNextStart + rInf.GetIdx());
822  if (wrongArea != nullptr)
823  {
824  if (WRONGAREA_WAVE == wrongArea->mLineType)
825  {
826  vcl::ScopedAntialiasing a(rInf.GetOut(), true);
827  rInf.GetOut().SetLineColor( wrongArea->mColor );
828  rInf.GetOut().DrawWaveLine( aStart, aEnd, 1 );
829  }
830  else if (WRONGAREA_BOLDWAVE == wrongArea->mLineType)
831  {
832  vcl::ScopedAntialiasing a(rInf.GetOut(), true);
833  rInf.GetOut().SetLineColor( wrongArea->mColor );
834  rInf.GetOut().DrawWaveLine( aStart, aEnd, 2 );
835  }
836  else if (WRONGAREA_BOLD == wrongArea->mLineType)
837  {
838  rInf.GetOut().SetLineColor( wrongArea->mColor );
839 
840  aStart.AdjustY(30 );
841  aEnd.AdjustY(30 );
842 
843  LineInfo aLineInfo( LineStyle::Solid, 26 );
844 
845  rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo );
846  }
847  else if (WRONGAREA_DASHED == wrongArea->mLineType)
848  {
849  rInf.GetOut().SetLineColor( wrongArea->mColor );
850 
851  aStart.AdjustY(30 );
852  aEnd.AdjustY(30 );
853 
854  LineInfo aLineInfo( LineStyle::Dash );
855  aLineInfo.SetDistance( 40 );
856  aLineInfo.SetDashLen( 1 );
857  aLineInfo.SetDashCount(1);
858 
859  rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo );
860  }
861  }
862  }
863 
864  nStart = nEnd + rInf.GetIdx();
865  nWrLen = rInf.GetIdx() + rInf.GetLen() - nStart;
866  }
867  while (nWrLen && pWList->Check( nStart, nWrLen ));
868 
869  rInf.GetOut().SetLineColor( aCol );
870 
871  if (rInf.GetOut().GetConnectMetaFile())
872  rInf.GetOut().Pop();
873 }
874 
876 {
877  OSL_ENSURE( rInf.GetShell(), "SwFntObj::DrawText without shell" );
878 
879  OutputDevice& rRefDev = rInf.GetShell()->GetRefDev();
880  vcl::Window* pWin = rInf.GetShell()->GetWin();
881 
882  // true if pOut is the printer and the printer has been used for formatting
883  const bool bPrt = OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() &&
884  OUTDEV_PRINTER == rRefDev.GetOutDevType();
885  const bool bBrowse = ( pWin &&
886  rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
887  !rInf.GetShell()->GetViewOptions()->IsPrtFormat() &&
888  !rInf.GetBullet() &&
889  ( rInf.GetSpace() || !rInf.GetKern() ) &&
890  !rInf.GetWrong() &&
891  !rInf.GetGrammarCheck() &&
892  !rInf.GetSmartTags() &&
893  !rInf.GetGreyWave() );
894 
895  // bDirectPrint indicates that we can enter the branch which calls
896  // the DrawText functions instead of calling the DrawTextArray functions
897  const bool bDirectPrint = bPrt || bBrowse;
898 
899  // Condition for output font / refdev font adjustment
900  const bool bUseScrFont =
901  lcl_IsFontAdjustNecessary( rInf.GetOut(), rRefDev );
902 
903  vcl::Font* pTmpFont = bUseScrFont ? m_pScrFont : m_pPrtFont;
904 
905  // bDirectPrint and bUseScrFont should have these values:
906 
907  // Outdev / RefDef | Printer | VirtPrinter | Window
908 
909  // Printer | 1 - 0 | 0 - 1 | -
910 
911  // VirtPrinter/PDF | 0 - 1 | 0 - 1 | -
912 
913  // Window/VirtWindow| 0 - 1 | 0 - 1 | 1 - 0
914 
915  // Exception: During painting of a Writer OLE object, we do not have
916  // a window. Therefore bUseSrcFont is always 0 in this case.
917 
918 #if OSL_DEBUG_LEVEL > 0
919 
920  const bool bNoAdjust = bPrt ||
921  ( pWin &&
922  rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
923  !rInf.GetShell()->GetViewOptions()->IsPrtFormat() );
924 
925  if ( OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() )
926  {
927  // Printer output
928  if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
929  {
930  OSL_ENSURE( bNoAdjust && !bUseScrFont, "Outdev Check failed" );
931  }
932  else if ( rRefDev.IsVirtual() )
933  {
934  OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
935  }
936  else
937  {
938  OSL_FAIL( "Outdev Check failed" );
939  }
940  }
941  else if ( rInf.GetOut().IsVirtual() && ! pWin )
942  {
943  // PDF export
944  if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
945  {
946  OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
947  }
948  else if ( rRefDev.IsVirtual() )
949  {
950  OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
951  }
952  else
953  {
954  OSL_FAIL( "Outdev Check failed" );
955  }
956  }
957  else if ( OUTDEV_WINDOW == rInf.GetOut().GetOutDevType() ||
958  ( rInf.GetOut().IsVirtual() && pWin ) )
959  {
960  // Window or virtual window
961  if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
962  {
963  OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
964  }
965  else if ( rRefDev.IsVirtual() )
966  {
967  OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
968  }
969  else if ( OUTDEV_WINDOW == rRefDev.GetOutDevType() )
970  {
971  OSL_ENSURE( bNoAdjust && !bUseScrFont, "Outdev Check failed" );
972  }
973  else
974  {
975  OSL_FAIL( "Outdev Check failed" );
976  }
977  }
978  else
979  {
980  OSL_FAIL( "Outdev Check failed" );
981  }
982 
983 #endif
984 
985  // robust: better use the printer font instead of using no font at all
986  OSL_ENSURE( pTmpFont, "No screen or printer font?" );
987  if ( ! pTmpFont )
988  pTmpFont = m_pPrtFont;
989 
990  // HACK: LINESTYLE_WAVE must not be abused any more, hence the grey wave
991  // line of the ExtendedAttributeSets will appear in the font color first
992 
993  const bool bSwitchH2V = rInf.GetFrame() && rInf.GetFrame()->IsVertical();
994  const bool bSwitchH2VLRBT = rInf.GetFrame() && rInf.GetFrame()->IsVertLRBT();
995  const bool bSwitchL2R = rInf.GetFrame() && rInf.GetFrame()->IsRightToLeft() &&
996  ! rInf.IsIgnoreFrameRTL();
997  const ComplexTextLayoutFlags nMode = rInf.GetOut().GetLayoutMode();
998  const bool bBidiPor = ( bSwitchL2R !=
999  ( ComplexTextLayoutFlags::Default != ( ComplexTextLayoutFlags::BiDiRtl & nMode ) ) );
1000 
1001  // be sure to have the correct layout mode at the printer
1002  if ( m_pPrinter )
1003  {
1006  }
1007 
1008  Point aTextOriginPos( rInf.GetPos() );
1009  if( !bPrt )
1010  {
1011  if( rInf.GetpOut() != *s_pFntObjPixOut.get() || rInf.GetOut().GetMapMode() != *s_pPixMap )
1012  {
1013  *s_pPixMap = rInf.GetOut().GetMapMode();
1014  (*s_pFntObjPixOut.get()) = rInf.GetpOut();
1015  Size aTmp( 1, 1 );
1016  s_nPixWidth = rInf.GetOut().PixelToLogic( aTmp ).Width();
1017  }
1018 
1019  aTextOriginPos.AdjustX(rInf.GetFrame()->IsRightToLeft() ? 0 : s_nPixWidth );
1020  }
1021 
1022  Color aOldColor( pTmpFont->GetColor() );
1023  bool bChgColor = rInf.ApplyAutoColor( pTmpFont );
1024  if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
1025  rInf.GetOut().SetFont( *pTmpFont );
1026  if ( bChgColor )
1027  pTmpFont->SetColor( aOldColor );
1028 
1029  if (TextFrameIndex(COMPLETE_STRING) == rInf.GetLen())
1030  rInf.SetLen( TextFrameIndex(rInf.GetText().getLength()) );
1031 
1032  // ASIAN LINE AND CHARACTER GRID MODE START
1033 
1034  if ( rInf.GetFrame() && rInf.SnapToGrid() && rInf.GetFont() &&
1035  SwFontScript::CJK == rInf.GetFont()->GetActual() )
1036  {
1037  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
1038 
1039  // ASIAN LINE AND CHARACTER GRID MODE: Do we want to snap asian characters to the grid?
1040  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars())
1041  {
1042  //for textgrid refactor
1043  //const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
1044  const SwDoc* pDoc = rInf.GetShell()->GetDoc();
1045  const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
1046 
1047  // kerning array - gives the absolute position of end of each character
1048  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(rInf.GetLen())]);
1049 
1050  if ( m_pPrinter )
1051  m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(),
1052  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1053  else
1054  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
1055  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1056 
1057  // Change the average width per character to an appropriate grid width
1058  // basically get the ratio of the avg width to the grid unit width, then
1059  // multiple this ratio to give the new avg width - which in this case
1060  // gives a new grid width unit size
1061 
1062  tools::Long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen());
1063 
1064  const sal_uLong nRatioAvgWidthCharToGridWidth = nAvgWidthPerChar ?
1065  ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
1066  1;
1067 
1068  nAvgWidthPerChar = nRatioAvgWidthCharToGridWidth * nGridWidth;
1069 
1070  // the absolute end position of the first character is also its width
1071  tools::Long nCharWidth = pKernArray[ 0 ];
1072  sal_uLong nHalfWidth = nAvgWidthPerChar / 2;
1073 
1074  tools::Long nNextFix=0;
1075 
1076  // we work out the start position (origin) of the first character,
1077  // and we set the next "fix" offset to half the width of the char.
1078  // The exceptions are for punctuation characters that are not centered
1079  // so in these cases we just add half a regular "average" character width
1080  // to the first characters actual width to allow the next character to
1081  // be centered automatically
1082  // If the character is "special right", then the offset is correct already
1083  // so the fix offset is as normal - half the average character width
1084 
1085  sal_Unicode cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ];
1087  switch ( nType )
1088  {
1089  // centre character
1090  case SwScriptInfo::NONE :
1091  aTextOriginPos.AdjustX(( nAvgWidthPerChar - nCharWidth ) / 2 );
1092  nNextFix = nCharWidth / 2;
1093  break;
1095  nNextFix = nHalfWidth;
1096  break;
1097  // punctuation
1098  default:
1099  aTextOriginPos.AdjustX(nAvgWidthPerChar - nCharWidth );
1100  nNextFix = nCharWidth - nHalfWidth;
1101  }
1102 
1103  // calculate offsets
1104  for (sal_Int32 j = 1; j < sal_Int32(rInf.GetLen()); ++j)
1105  {
1106  tools::Long nCurrentCharWidth = pKernArray[ j ] - pKernArray[ j - 1 ];
1107  nNextFix += nAvgWidthPerChar;
1108 
1109  // almost the same as getting the offset for the first character:
1110  // punctuation characters are not centered, so just add half an
1111  // average character width minus the characters actual char width
1112  // to get the offset into the centre of the next character
1113 
1114  cChar = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + j ];
1115  nType = lcl_WhichPunctuation( cChar );
1116  switch ( nType )
1117  {
1118  case SwScriptInfo::NONE :
1119  pKernArray[ j - 1 ] = nNextFix - ( nCurrentCharWidth / 2 );
1120  break;
1122  pKernArray[ j - 1 ] = nNextFix - nHalfWidth;
1123  break;
1124  default:
1125  pKernArray[ j - 1 ] = nNextFix + nHalfWidth - nCurrentCharWidth;
1126  }
1127  }
1128 
1129  // the layout engine requires the total width of the output
1130  pKernArray[sal_Int32(rInf.GetLen()) - 1] = rInf.GetWidth() -
1131  aTextOriginPos.X() + rInf.GetPos().X() ;
1132 
1133  if ( bSwitchH2V )
1134  rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
1135 
1136  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1137  pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1138 
1139  return;
1140  }
1141  }
1142 
1143  // For text grid refactor
1144  // ASIAN LINE AND CHARACTER GRID MODE START: not snap to characters
1145 
1146  if ( rInf.GetFrame() && rInf.SnapToGrid() )
1147  {
1148  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
1149 
1150  // ASIAN LINE AND CHARACTER GRID MODE - do not snap to characters
1151  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
1152  {
1153  const tools::Long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
1154 
1155  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(rInf.GetLen())]);
1156 
1157  if ( m_pPrinter )
1158  m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(),
1159  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1160  else
1161  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
1162  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1163  if ( bSwitchH2V )
1164  rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
1165  if ( rInf.GetSpace() || rInf.GetKanaComp())
1166  {
1167  tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
1168  if ( rInf.GetFont() && rInf.GetLen() )
1169  {
1170  bool bSpecialJust = false;
1171  const SwScriptInfo* pSI = rInf.GetScriptInfo();
1172  const SwFontScript nActual = rInf.GetFont()->GetActual();
1174  if( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
1175  pSI && pSI->CountCompChg() &&
1176  lcl_IsMonoSpaceFont( *(rInf.GetpOut()) ) )
1177  {
1178  pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
1179  rInf.GetKanaComp(), o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ) , &aTextOriginPos );
1180  bSpecialJust = true;
1181  }
1183  if ( ( SwFontScript::CJK == nActual || SwFontScript::Latin == nActual ) && nSpaceAdd )
1184  {
1186  if (!MsLangId::isKorean(aLang))
1187  {
1188  tools::Long nSpaceSum = nSpaceAdd;
1189  for (sal_Int32 nI = 0; nI < sal_Int32(rInf.GetLen()); ++nI)
1190  {
1191  pKernArray[ nI ] += nSpaceSum;
1192  nSpaceSum += nSpaceAdd;
1193  }
1194  bSpecialJust = true;
1195  nSpaceAdd = 0;
1196  }
1197  }
1198  tools::Long nGridAddSum = nGridWidthAdd;
1199  for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++, nGridAddSum += nGridWidthAdd )
1200  {
1201  pKernArray[i] += nGridAddSum;
1202  }
1203  tools::Long nKernSum = rInf.GetKern();
1204  if ( bSpecialJust || rInf.GetKern() )
1205  {
1206  for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++, nKernSum += rInf.GetKern())
1207  {
1208  if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx())+i])
1209  nKernSum += nSpaceAdd;
1210  pKernArray[i] += nKernSum;
1211  }
1214  if( m_bPaintBlank && rInf.GetLen() && (CH_BLANK ==
1215  rInf.GetText()[sal_Int32(rInf.GetIdx() + rInf.GetLen()) - 1]))
1216  {
1219  if (TextFrameIndex(1) == rInf.GetLen())
1220  {
1221  pKernArray[0] = rInf.GetWidth() + nSpaceAdd;
1222  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1223  pKernArray.get(), sal_Int32(rInf.GetIdx()), 1);
1224  }
1225  else
1226  {
1227  pKernArray[sal_Int32(rInf.GetLen()) - 2] += nSpaceAdd;
1228  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1229  pKernArray.get(), sal_Int32(rInf.GetIdx()),
1230  sal_Int32(rInf.GetLen()));
1231  }
1232  }
1233  else
1234  {
1235  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1236  pKernArray.get(), sal_Int32(rInf.GetIdx()),
1237  sal_Int32(rInf.GetLen()));
1238  }
1239  }
1240  else
1241  {
1242  sal_Int32 i;
1243  tools::Long nSpaceSum = 0;
1244  for (i = 0; i < sal_Int32(rInf.GetLen()); i++)
1245  {
1246  if(CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
1247  nSpaceSum += nSpaceAdd + nKernSum;
1248 
1249  pKernArray[i] += nSpaceSum;
1250  }
1251 
1252  rInf.GetOut().DrawTextArray(aTextOriginPos,
1253  rInf.GetText(), pKernArray.get(),
1254  sal_Int32(rInf.GetIdx()),
1255  sal_Int32(rInf.GetLen()));
1256  }
1257  }
1258  }
1259  else
1260  {
1261  //long nKernAdd = rInf.GetKern();
1262  tools::Long nKernAdd = 0;
1263  tools::Long nGridAddSum = nGridWidthAdd + nKernAdd;
1264  for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen());
1265  i++, nGridAddSum += nGridWidthAdd + nKernAdd)
1266  {
1267  pKernArray[i] += nGridAddSum;
1268  }
1269  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1270  pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1271  }
1272  return;
1273  }
1274  }
1275 
1276  // DIRECT PAINTING WITHOUT SCREEN ADJUSTMENT
1277 
1278  if ( bDirectPrint )
1279  {
1280  const Fraction aTmp( 1, 1 );
1281  bool bStretch = rInf.GetWidth() && (rInf.GetLen() > TextFrameIndex(1)) && bPrt
1282  && ( aTmp != rInf.GetOut().GetMapMode().GetScaleX() );
1283 
1284  if ( bSwitchL2R )
1285  rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
1286 
1287  if ( bSwitchH2V )
1288  rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
1289 
1290  // In the good old days we used to have a simple DrawText if the
1291  // output device is the printer. Now we need a DrawTextArray if
1292  // 1. KanaCompression is enabled
1293  // 2. Justified alignment
1294  // Simple kerning is handled by DrawStretchText
1295  if( rInf.GetSpace() || rInf.GetKanaComp() )
1296  {
1297  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(rInf.GetLen())]);
1298  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
1299  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1300 
1301  if( bStretch )
1302  {
1303  sal_Int32 nZwi = sal_Int32(rInf.GetLen()) - 1;
1304  tools::Long nDiff = rInf.GetWidth() - pKernArray[ nZwi ]
1305  - sal_Int32(rInf.GetLen()) * rInf.GetKern();
1306  tools::Long nRest = nDiff % nZwi;
1307  tools::Long nAdd;
1308  if( nRest < 0 )
1309  {
1310  nAdd = -1;
1311  nRest += nZwi;
1312  }
1313  else
1314  {
1315  nAdd = +1;
1316  nRest = nZwi - nRest;
1317  }
1318  nDiff /= nZwi;
1319  tools::Long nSum = nDiff;
1320  for( sal_Int32 i = 0; i < nZwi; )
1321  {
1322  pKernArray[ i ] += nSum;
1323  if( ++i == nRest )
1324  nDiff += nAdd;
1325  nSum += nDiff;
1326  }
1327  }
1328 
1329  // Modify Array for special justifications
1330 
1331  tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
1332  bool bSpecialJust = false;
1333 
1334  if ( rInf.GetFont() && rInf.GetLen() )
1335  {
1336  const SwScriptInfo* pSI = rInf.GetScriptInfo();
1337  const SwFontScript nActual = rInf.GetFont()->GetActual();
1338 
1339  // Kana Compression
1340  if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
1341  pSI && pSI->CountCompChg() &&
1342  lcl_IsMonoSpaceFont( rInf.GetOut() ) )
1343  {
1344  pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
1345  rInf.GetKanaComp(),
1346  o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTextOriginPos );
1347  bSpecialJust = true;
1348  }
1349 
1350  // Asian Justification
1351  if ( SwFontScript::CJK == nActual && nSpaceAdd )
1352  {
1354 
1355  if (!MsLangId::isKorean(aLang))
1356  {
1357  SwScriptInfo::CJKJustify( rInf.GetText(), pKernArray.get(), nullptr,
1358  rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
1359 
1360  bSpecialJust = true;
1361  nSpaceAdd = 0;
1362  }
1363  }
1364 
1365  // Kashida Justification
1366  if ( SwFontScript::CTL == nActual && nSpaceAdd )
1367  {
1368  if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
1369  {
1370  if ( pSI && pSI->CountKashida() &&
1371  pSI->KashidaJustify( pKernArray.get(), nullptr, rInf.GetIdx(),
1372  rInf.GetLen(), nSpaceAdd ) != -1 )
1373  {
1374  bSpecialJust = true;
1375  nSpaceAdd = 0;
1376  }
1377  }
1378  }
1379 
1380  // Thai Justification
1381  if ( SwFontScript::CTL == nActual && nSpaceAdd )
1382  {
1384 
1385  if ( LANGUAGE_THAI == aLang )
1386  {
1387  // Use rInf.GetSpace() because it has more precision than
1388  // nSpaceAdd:
1389  SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray.get(), nullptr,
1390  rInf.GetIdx(), rInf.GetLen(),
1391  rInf.GetNumberOfBlanks(),
1392  rInf.GetSpace() );
1393 
1394  // adding space to blanks is already done
1395  bSpecialJust = true;
1396  nSpaceAdd = 0;
1397  }
1398  }
1399  }
1400 
1401  tools::Long nKernSum = rInf.GetKern();
1402 
1403  if ( bStretch || m_bPaintBlank || rInf.GetKern() || bSpecialJust )
1404  {
1405  for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++,
1406  nKernSum += rInf.GetKern() )
1407  {
1408  if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
1409  nKernSum += nSpaceAdd;
1410  pKernArray[i] += nKernSum;
1411  }
1412 
1413  // In case of underlined/strike-through justified text
1414  // a blank at the end requires special handling:
1415  if( m_bPaintBlank && rInf.GetLen() && ( CH_BLANK ==
1416  rInf.GetText()[sal_Int32(rInf.GetIdx() + rInf.GetLen())-1]))
1417  {
1418  // If it is a single underlined space, output 2 spaces:
1419  if (TextFrameIndex(1) == rInf.GetLen())
1420  {
1421  pKernArray[0] = rInf.GetWidth() + nSpaceAdd;
1422 
1423  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1424  pKernArray.get(), sal_Int32(rInf.GetIdx()), 1 );
1425  }
1426  else
1427  {
1428  pKernArray[ sal_Int32(rInf.GetLen()) - 2 ] += nSpaceAdd;
1429  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1430  pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1431  }
1432  }
1433  else
1434  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1435  pKernArray.get(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1436  }
1437  else
1438  {
1439  Point aTmpPos( aTextOriginPos );
1440  sal_Int32 j = 0;
1441  sal_Int32 i;
1442  for( i = 0; i < sal_Int32(rInf.GetLen()); i++ )
1443  {
1444  if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
1445  {
1446  nKernSum += nSpaceAdd;
1447  if( j < i )
1448  rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
1449  sal_Int32(rInf.GetIdx()) + j, i - j);
1450  j = i + 1;
1451  SwTwips nAdd = pKernArray[ i ] + nKernSum;
1452  if ( ( ComplexTextLayoutFlags::BiDiStrong | ComplexTextLayoutFlags::BiDiRtl ) == nMode )
1453  nAdd *= -1;
1454  aTmpPos.setX( aTextOriginPos.X() + nAdd );
1455  }
1456  }
1457  if( j < i )
1458  rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
1459  sal_Int32(rInf.GetIdx()) + j, i - j);
1460  }
1461  }
1462  else if( bStretch )
1463  {
1464  tools::Long nTmpWidth = rInf.GetWidth();
1465  if( rInf.GetKern() && rInf.GetLen() && nTmpWidth > rInf.GetKern() )
1466  nTmpWidth -= rInf.GetKern();
1467  rInf.GetOut().DrawStretchText( aTextOriginPos, nTmpWidth,
1468  rInf.GetText(),
1469  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1470  }
1471  else if( rInf.GetKern() )
1472  {
1473  const tools::Long nTmpWidth = GetTextSize( rInf ).Width();
1474 
1475  const Color aSaveColor( pTmpFont->GetColor() );
1476  const bool bColorChanged = rInf.ApplyAutoColor( pTmpFont );
1477 
1478  if( bColorChanged )
1479  {
1480  if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
1481  rInf.GetOut().SetFont( *pTmpFont );
1482  pTmpFont->SetColor( aSaveColor );
1483  }
1484 
1485  rInf.GetOut().DrawStretchText( aTextOriginPos, nTmpWidth,
1486  rInf.GetText(),
1487  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1488  }
1489  else
1490  rInf.GetOut().DrawText( aTextOriginPos, rInf.GetText(),
1491  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1492  }
1493 
1494  // PAINTING WITH FORMATTING DEVICE/SCREEN ADJUSTMENT
1495 
1496  else
1497  {
1498  const OUString* pStr = &rInf.GetText();
1499 
1500  OUString aStr;
1501  OUString aBulletOverlay;
1502  bool bBullet = rInf.GetBullet();
1503  if( m_bSymbol )
1504  bBullet = false;
1505  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(rInf.GetLen())]);
1506  CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
1507  tools::Long nScrPos;
1508 
1509  // get screen array
1510  std::unique_ptr<tools::Long[]> pScrArray(new tools::Long[sal_Int32(rInf.GetLen())]);
1511  SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) };
1512  SalLayoutGlyphs* pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey);
1513  rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(),
1514  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);
1515 
1516  // OLE: no printer available
1517  // OSL_ENSURE( pPrinter, "DrawText needs pPrinter" )
1518  if ( m_pPrinter )
1519  {
1520  // pTmpFont has already been set as current font for rInf.GetOut()
1521  if ( m_pPrinter.get() != rInf.GetpOut() || pTmpFont != m_pPrtFont )
1522  {
1525  }
1526  aGlyphsKey = SwTextGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) };
1527  pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey);
1528  m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(),
1529  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);
1530  }
1531  else
1532  {
1533  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
1534  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1535  }
1536 
1537  // Modify Printer and ScreenArrays for special justifications
1538 
1539  tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
1540  bool bNoHalfSpace = false;
1541 
1542  if ( rInf.GetFont() && rInf.GetLen() )
1543  {
1544  const SwFontScript nActual = rInf.GetFont()->GetActual();
1545  const SwScriptInfo* pSI = rInf.GetScriptInfo();
1546 
1547  // Kana Compression
1548  if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
1549  pSI && pSI->CountCompChg() &&
1550  lcl_IsMonoSpaceFont( rInf.GetOut() ) )
1551  {
1552  Point aTmpPos( aTextOriginPos );
1553  pSI->Compress( pScrArray.get(), rInf.GetIdx(), rInf.GetLen(),
1554  rInf.GetKanaComp(),
1555  o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTmpPos );
1556  pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
1557  rInf.GetKanaComp(),
1558  o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTextOriginPos );
1559  }
1560 
1561  // Asian Justification
1562  if ( SwFontScript::CJK == nActual && nSpaceAdd )
1563  {
1565 
1566  if (!MsLangId::isKorean(aLang))
1567  {
1568  SwScriptInfo::CJKJustify( rInf.GetText(), pKernArray.get(), pScrArray.get(),
1569  rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
1570 
1571  nSpaceAdd = 0;
1572  }
1573  }
1574 
1575  // Kashida Justification
1576  if ( SwFontScript::CTL == nActual && nSpaceAdd )
1577  {
1578  if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
1579  {
1580  if ( pSI && pSI->CountKashida() &&
1581  pSI->KashidaJustify( pKernArray.get(), pScrArray.get(), rInf.GetIdx(),
1582  rInf.GetLen(), nSpaceAdd ) != -1 )
1583  nSpaceAdd = 0;
1584  else
1585  bNoHalfSpace = true;
1586  }
1587  }
1588 
1589  // Thai Justification
1590  if ( SwFontScript::CTL == nActual && nSpaceAdd )
1591  {
1593 
1594  if ( LANGUAGE_THAI == aLang )
1595  {
1596  SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray.get(),
1597  pScrArray.get(), rInf.GetIdx(),
1598  rInf.GetLen(),
1599  rInf.GetNumberOfBlanks(),
1600  rInf.GetSpace() );
1601 
1602  // adding space to blanks is already done
1603  nSpaceAdd = 0;
1604  }
1605  }
1606  }
1607 
1608  nScrPos = pScrArray[ 0 ];
1609 
1610  if( bBullet )
1611  {
1612  // !!! HACK !!!
1613  // The Arabic layout engine requires some context of the string
1614  // which should be painted.
1615  sal_Int32 nCopyStart = sal_Int32(rInf.GetIdx());
1616  if ( nCopyStart )
1617  --nCopyStart;
1618 
1619  sal_Int32 nCopyLen = sal_Int32(rInf.GetLen());
1620  if ( nCopyStart + nCopyLen < rInf.GetText().getLength() )
1621  ++nCopyLen;
1622 
1623  aStr = rInf.GetText().copy( nCopyStart, nCopyLen );
1624  pStr = &aStr;
1625 
1626  aBulletOverlay = rInf.GetText().copy( nCopyStart, nCopyLen );
1627 
1628  for( sal_Int32 i = 0; i < aBulletOverlay.getLength(); ++i )
1629  if( CH_BLANK == aBulletOverlay[ i ] )
1630  {
1631  /* fdo#72488 Hack: try to see if the space is zero width
1632  * and don't bother with inserting a bullet in this case.
1633  */
1634  if ((i + nCopyStart + 1 >= sal_Int32(rInf.GetLen())) ||
1635  pKernArray[i + nCopyStart] != pKernArray[ i + nCopyStart + 1])
1636  {
1637  aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BULLET));
1638  }
1639  else
1640  {
1641  aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BLANK));
1642  }
1643  }
1644  else
1645  {
1646  aBulletOverlay = aBulletOverlay.replaceAt(i, 1, OUString(CH_BLANK));
1647  }
1648  }
1649 
1650  TextFrameIndex nCnt(rInf.GetText().getLength());
1651  if ( nCnt < rInf.GetIdx() )
1652  assert(false); // layout bug, not handled below
1653  else
1654  nCnt = nCnt - rInf.GetIdx();
1655  nCnt = std::min(nCnt, rInf.GetLen());
1656  tools::Long nKernSum = rInf.GetKern();
1657  sal_Unicode cChPrev = rInf.GetText()[sal_Int32(rInf.GetIdx())];
1658 
1659  // In case of a single underlined space in justified text,
1660  // have to output 2 spaces:
1661  if ((nCnt == TextFrameIndex(1)) && rInf.GetSpace() && (cChPrev == CH_BLANK))
1662  {
1663  pKernArray[0] = rInf.GetWidth() +
1664  rInf.GetKern() +
1665  ( rInf.GetSpace() / SPACING_PRECISION_FACTOR );
1666 
1667  if ( bSwitchL2R )
1668  rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
1669 
1670  if ( bSwitchH2V )
1671  rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
1672 
1673  rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
1674  pKernArray.get(), sal_Int32(rInf.GetIdx()), 1 );
1675  if( bBullet )
1676  rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(),
1677  rInf.GetIdx() ? 1 : 0, 1 );
1678  }
1679  else
1680  {
1681  sal_Unicode nCh;
1682 
1683  // In case of Pair Kerning the printer influence on the positioning
1684  // grows
1685  const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3;
1686  const int nDiv = nMul+1;
1687 
1688  // nSpaceSum contains the sum of the intermediate space distributed
1689  // among Spaces by the Justification.
1690  // The Spaces themselves will be positioned in the middle of the
1691  // intermediate space, hence the nSpace/2.
1692  // In case of word-by-word underlining they have to be positioned
1693  // at the beginning of the intermediate space, so that the space
1694  // is not underlined.
1695  // A Space at the beginning or end of the text must be positioned
1696  // before (resp. after) the whole intermediate space, otherwise
1697  // the underline/strike-through would have gaps.
1698  tools::Long nSpaceSum = 0;
1699  // in word line mode and for Arabic, we disable the half space trick:
1700  const tools::Long nHalfSpace = m_pPrtFont->IsWordLineMode() || bNoHalfSpace ? 0 : nSpaceAdd / 2;
1701  const tools::Long nOtherHalf = nSpaceAdd - nHalfSpace;
1702  if ( nSpaceAdd && ( cChPrev == CH_BLANK ) )
1703  nSpaceSum = nHalfSpace;
1704  for (sal_Int32 i = 1; i < sal_Int32(nCnt); ++i, nKernSum += rInf.GetKern())
1705  {
1706  nCh = rInf.GetText()[sal_Int32(rInf.GetIdx()) + i];
1707 
1708  OSL_ENSURE( pScrArray, "Where is the screen array?" );
1709  tools::Long nScr;
1710  nScr = pScrArray[ i ] - pScrArray[ i - 1 ];
1711 
1712  // If there is an (ex-)Space before us, position optimally,
1713  // i.e., our right margin to the 100% printer position;
1714  // if we _are_ an ex-Space, position us left-aligned to the
1715  // printer position.
1716  if ( nCh == CH_BLANK )
1717  {
1718  nScrPos = pKernArray[i-1] + nScr;
1719 
1720  if ( cChPrev == CH_BLANK )
1721  nSpaceSum += nOtherHalf;
1722  if (i + 1 == sal_Int32(nCnt))
1723  nSpaceSum += nSpaceAdd;
1724  else
1725  nSpaceSum += nHalfSpace;
1726  }
1727  else
1728  {
1729  if ( cChPrev == CH_BLANK )
1730  {
1731  nScrPos = pKernArray[i-1] + nScr;
1732  // no Pixel is lost:
1733  nSpaceSum += nOtherHalf;
1734  }
1735  else if ( cChPrev == '-' )
1736  nScrPos = pKernArray[i-1] + nScr;
1737  else
1738  {
1739  nScrPos += nScr;
1740  nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv;
1741  }
1742  }
1743  cChPrev = nCh;
1744  pKernArray[i-1] = nScrPos - nScr + nKernSum + nSpaceSum;
1745  // In word line mode and for Arabic, we disabled the half space trick. If a portion
1746  // ends with a blank, the full nSpaceAdd value has been added to the character in
1747  // front of the blank. This leads to painting artifacts, therefore we remove the
1748  // nSpaceAdd value again:
1749  if ((bNoHalfSpace || m_pPrtFont->IsWordLineMode()) && i+1 == sal_Int32(nCnt) && nCh == CH_BLANK)
1750  pKernArray[i-1] = pKernArray[i-1] - nSpaceAdd;
1751  }
1752 
1753  // the layout engine requires the total width of the output
1754  pKernArray[sal_Int32(rInf.GetLen()) - 1] += nKernSum + nSpaceSum;
1755 
1756  if( rInf.GetGreyWave() )
1757  {
1758  if( rInf.GetLen() )
1759  {
1760  tools::Long nHght = rInf.GetOut().LogicToPixel(
1761  m_pPrtFont->GetFontSize() ).Height();
1762  if( WRONG_SHOW_MIN < nHght )
1763  {
1764  if ( rInf.GetOut().GetConnectMetaFile() )
1765  rInf.GetOut().Push();
1766 
1767  Color aCol( rInf.GetOut().GetLineColor() );
1768  bool bColSave = aCol != gWaveCol;
1769  if ( bColSave )
1770  rInf.GetOut().SetLineColor( gWaveCol );
1771 
1772  Point aEnd;
1773  tools::Long nKernVal = pKernArray[sal_Int32(rInf.GetLen()) - 1];
1774 
1775  const Degree10 nDir = bBidiPor
1776  ? 1800_deg10
1777  : UnMapDirection(GetFont().GetOrientation(),
1778  bSwitchH2V, bSwitchH2VLRBT);
1779 
1780  switch ( nDir.get() )
1781  {
1782  case 0 :
1783  aEnd.setX( rInf.GetPos().X() + nKernVal );
1784  aEnd.setY( rInf.GetPos().Y() );
1785  break;
1786  case 900 :
1787  aEnd.setX( rInf.GetPos().X() );
1788  aEnd.setY( rInf.GetPos().Y() - nKernVal );
1789  break;
1790  case 1800 :
1791  aEnd.setX( rInf.GetPos().X() - nKernVal );
1792  aEnd.setY( rInf.GetPos().Y() );
1793  break;
1794  case 2700 :
1795  aEnd.setX( rInf.GetPos().X() );
1796  aEnd.setY( rInf.GetPos().Y() + nKernVal );
1797  break;
1798  }
1799 
1800  Point aCurrPos( rInf.GetPos() );
1801 
1802  if ( bSwitchL2R )
1803  {
1804  rInf.GetFrame()->SwitchLTRtoRTL( aCurrPos );
1805  rInf.GetFrame()->SwitchLTRtoRTL( aEnd );
1806  }
1807 
1808  if ( bSwitchH2V )
1809  {
1810  rInf.GetFrame()->SwitchHorizontalToVertical( aCurrPos );
1811  rInf.GetFrame()->SwitchHorizontalToVertical( aEnd );
1812  }
1813  {
1814  vcl::ScopedAntialiasing a(rInf.GetOut(), true);
1815  rInf.GetOut().DrawWaveLine( aCurrPos, aEnd );
1816  }
1817  if ( bColSave )
1818  rInf.GetOut().SetLineColor( aCol );
1819 
1820  if ( rInf.GetOut().GetConnectMetaFile() )
1821  rInf.GetOut().Pop();
1822  }
1823  }
1824  }
1825  else if( !m_bSymbol && rInf.GetLen() )
1826  {
1827  // anything to do?
1828  if (rInf.GetWrong() || rInf.GetGrammarCheck() || rInf.GetSmartTags())
1829  {
1830  CalcLinePosData aCalcLinePosData(rInf, GetFont(), nCnt, bSwitchH2V,
1831  bSwitchH2VLRBT, bSwitchL2R, nHalfSpace,
1832  pKernArray.get(), bBidiPor);
1833 
1834  SwForbidden aForbidden;
1835  // draw line for smart tag data
1836  lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetSmartTags(), aCalcLinePosData, Size() );
1837  // draw wave line for spell check errors
1838  // draw them BEFORE the grammar check lines to 'override' the latter in case of conflict.
1839  // reason: some grammar errors can only be found if spelling errors are fixed,
1840  // therefore we don't want the user to miss a spelling error.
1841  lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetWrong(), aCalcLinePosData, m_pPrtFont->GetFontSize() );
1842  // draw wave line for grammar check errors
1843  lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetGrammarCheck(), aCalcLinePosData, m_pPrtFont->GetFontSize() );
1844  }
1845  }
1846 
1847  sal_Int32 nLen = sal_Int32(rInf.GetLen());
1848 
1849  if( nLen > 0 )
1850  {
1851 
1852  if ( bSwitchL2R )
1853  rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
1854 
1855  if ( bSwitchH2V )
1856  rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
1857 
1858  // If we paint bullets instead of spaces, we use a copy of
1859  // the paragraph string. For the layout engine, the copy
1860  // of the string has to be an environment of the range which
1861  // is painted
1862  sal_Int32 nTmpIdx = bBullet
1863  ? (rInf.GetIdx() ? 1 : 0)
1864  : sal_Int32(rInf.GetIdx());
1865  aGlyphsKey = SwTextGlyphsKey{ &rInf.GetOut(), *pStr, nTmpIdx, nLen };
1866  pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey);
1867  rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(),
1868  nTmpIdx , nLen, SalLayoutFlags::NONE, pGlyphs );
1869  if (bBullet)
1870  {
1871  rInf.GetOut().Push();
1872  Color aPreviousColor = pTmpFont->GetColor();
1873 
1874  FontLineStyle aPreviousUnderline = pTmpFont->GetUnderline();
1875  FontLineStyle aPreviousOverline = pTmpFont->GetOverline();
1876  FontStrikeout aPreviousStrikeout = pTmpFont->GetStrikeout();
1877 
1878  pTmpFont->SetColor( NON_PRINTING_CHARACTER_COLOR );
1879  pTmpFont->SetUnderline(LINESTYLE_NONE);
1880  pTmpFont->SetOverline(LINESTYLE_NONE);
1881  pTmpFont->SetStrikeout(STRIKEOUT_NONE);
1882  rInf.GetOut().SetFont( *pTmpFont );
1883  tools::Long nShift = rInf.GetOut( ).GetFontMetric( ).GetBulletOffset( );
1884  if ( nShift )
1885  {
1886  tools::Long nAdd = 0;
1887 
1888  if (aBulletOverlay.getLength() > nTmpIdx &&
1889  aBulletOverlay[ nTmpIdx ] == CH_BULLET )
1890  {
1891  if (bSwitchH2V)
1892  aTextOriginPos.AdjustY(nShift ) ;
1893  else
1894  aTextOriginPos.AdjustX(nShift ) ;
1895  nAdd = nShift ;
1896  }
1897  for( sal_Int32 i = 1 ; i < nLen ; ++i )
1898  {
1899  if ( aBulletOverlay[ i + nTmpIdx ] == CH_BULLET )
1900  pKernArray [ i - 1 ] += nShift ;
1901  if ( nAdd )
1902  pKernArray [ i - 1 ] -= nAdd;
1903  }
1904  }
1905  rInf.GetOut().DrawTextArray( aTextOriginPos, aBulletOverlay, pKernArray.get(),
1906  nTmpIdx , nLen );
1907  pTmpFont->SetColor( aPreviousColor );
1908 
1909  pTmpFont->SetUnderline(aPreviousUnderline);
1910  pTmpFont->SetOverline(aPreviousOverline);
1911  pTmpFont->SetStrikeout(aPreviousStrikeout);
1912  rInf.GetOut().Pop();
1913  }
1914  }
1915  }
1916  }
1917 }
1918 
1920 {
1921  Size aTextSize;
1922  const TextFrameIndex nLn = (TextFrameIndex(COMPLETE_STRING) != rInf.GetLen())
1923  ? rInf.GetLen()
1924  : TextFrameIndex(rInf.GetText().getLength());
1925 
1926  // be sure to have the correct layout mode at the printer
1927  if ( m_pPrinter )
1928  {
1931  }
1932 
1933  if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() && rInf.GetFont() &&
1934  SwFontScript::CJK == rInf.GetFont()->GetActual() )
1935  {
1936  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
1937  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
1938  {
1939  const SwDoc* pDoc = rInf.GetShell()->GetDoc();
1940  const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
1941 
1942  OutputDevice* pOutDev;
1943 
1944  if ( m_pPrinter )
1945  {
1948  pOutDev = m_pPrinter;
1949  }
1950  else
1951  pOutDev = rInf.GetpOut();
1952 
1953  aTextSize.setWidth( pOutDev->GetTextWidth(rInf.GetText(),
1954  sal_Int32(rInf.GetIdx()), sal_Int32(nLn)) );
1955 
1956  OSL_ENSURE( !rInf.GetShell() ||
1957  ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExternalLeading() ),
1958  "Leading values should be already calculated" );
1959  aTextSize.setHeight( pOutDev->GetTextHeight() +
1960  GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
1961 
1962  tools::Long nAvgWidthPerChar = aTextSize.Width() / sal_Int32(nLn);
1963 
1964  const sal_uLong i = nAvgWidthPerChar ?
1965  ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
1966  1;
1967 
1968  aTextSize.setWidth(i * nGridWidth * sal_Int32(nLn));
1969  rInf.SetKanaDiff( 0 );
1970  return aTextSize;
1971  }
1972  }
1973 
1974  //for textgrid refactor
1975  if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() && rInf.GetFont() )
1976  {
1977  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
1978  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
1979  {
1980  const tools::Long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
1981  OutputDevice* pOutDev;
1982  if ( m_pPrinter )
1983  {
1986  pOutDev = m_pPrinter;
1987  }
1988  else
1989  pOutDev = rInf.GetpOut();
1990  aTextSize.setWidth(pOutDev->GetTextWidth(rInf.GetText(),
1991  sal_Int32(rInf.GetIdx()), sal_Int32(nLn)));
1992  aTextSize.setHeight( pOutDev->GetTextHeight() +
1993  GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
1994  aTextSize.AdjustWidth(sal_Int32(nLn) * nGridWidthAdd);
1995  //if ( rInf.GetKern() && nLn )
1996  // aTextSize.Width() += ( nLn ) * long( rInf.GetKern() );
1997 
1998  rInf.SetKanaDiff( 0 );
1999  return aTextSize;
2000  }
2001  }
2002 
2003  const bool bCompress = rInf.GetKanaComp() && nLn &&
2004  rInf.GetFont() &&
2005  SwFontScript::CJK == rInf.GetFont()->GetActual() &&
2006  rInf.GetScriptInfo() &&
2007  rInf.GetScriptInfo()->CountCompChg() &&
2008  lcl_IsMonoSpaceFont( rInf.GetOut() );
2009 
2010  OSL_ENSURE( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
2011  CountCompChg()), "Compression without info" );
2012 
2013  // This is the part used e.g., for cursor travelling
2014  // See condition for DrawText or DrawTextArray (bDirectPrint)
2015  if ( m_pPrinter && m_pPrinter.get() != rInf.GetpOut() )
2016  {
2019  aTextSize.setWidth( m_pPrinter->GetTextWidth( rInf.GetText(),
2020  sal_Int32(rInf.GetIdx()), sal_Int32(nLn)));
2021  aTextSize.setHeight( m_pPrinter->GetTextHeight() );
2022  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(nLn)]);
2023  CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
2024  if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) )
2025  rInf.GetOut().SetFont( *m_pScrFont );
2026  tools::Long nScrPos;
2027 
2028  m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(),
2029  sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
2030  if( bCompress )
2031  rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray.get(),
2032  rInf.GetIdx(), nLn, rInf.GetKanaComp(),
2033  o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()) ,lcl_IsFullstopCentered( rInf.GetOut() ) ) );
2034  else
2035  rInf.SetKanaDiff( 0 );
2036 
2037  if ( rInf.GetKanaDiff() )
2038  nScrPos = pKernArray[ sal_Int32(nLn) - 1 ];
2039  else
2040  {
2041  std::unique_ptr<tools::Long[]> pScrArray(new tools::Long[sal_Int32(rInf.GetLen())]);
2042  rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(),
2043  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
2044  nScrPos = pScrArray[ 0 ];
2045  TextFrameIndex nCnt(rInf.GetText().getLength());
2046  if ( nCnt < rInf.GetIdx() )
2047  nCnt = TextFrameIndex(0); // assert???
2048  else
2049  nCnt = nCnt - rInf.GetIdx();
2050  nCnt = std::min(nCnt, nLn);
2051  sal_Unicode nChPrev = rInf.GetText()[ sal_Int32(rInf.GetIdx()) ];
2052 
2053  sal_Unicode nCh;
2054 
2055  // In case of Pair Kerning the printer influence on the positioning
2056  // grows
2057  const int nMul = m_pPrtFont->GetKerning() != FontKerning::NONE ? 1 : 3;
2058  const int nDiv = nMul+1;
2059  for (sal_Int32 i = 1; i < sal_Int32(nCnt); i++)
2060  {
2061  nCh = rInf.GetText()[ sal_Int32(rInf.GetIdx()) + i ];
2062  tools::Long nScr;
2063  nScr = pScrArray[ i ] - pScrArray[ i - 1 ];
2064  if ( nCh == CH_BLANK )
2065  nScrPos = pKernArray[i-1]+nScr;
2066  else
2067  {
2068  if ( nChPrev == CH_BLANK || nChPrev == '-' )
2069  nScrPos = pKernArray[i-1]+nScr;
2070  else
2071  {
2072  nScrPos += nScr;
2073  nScrPos = ( nMul * nScrPos + pKernArray[i] ) / nDiv;
2074  }
2075  }
2076  nChPrev = nCh;
2077  pKernArray[i-1] = nScrPos - nScr;
2078  }
2079  }
2080 
2081  pKernArray.reset();
2082  aTextSize.setWidth( nScrPos );
2083  }
2084  else
2085  {
2086  if( !m_pPrtFont->IsSameInstance( rInf.GetOut().GetFont() ) )
2087  rInf.GetOut().SetFont( *m_pPrtFont );
2088  if( bCompress )
2089  {
2090  std::unique_ptr<tools::Long[]> pKernArray( new tools::Long[sal_Int32(nLn)] );
2091  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
2092  sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
2093  rInf.SetKanaDiff( rInf.GetScriptInfo()->Compress( pKernArray.get(),
2094  rInf.GetIdx(), nLn, rInf.GetKanaComp(),
2095  o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()) ,lcl_IsFullstopCentered( rInf.GetOut() ) ) );
2096  aTextSize.setWidth( pKernArray[sal_Int32(nLn) - 1] );
2097  }
2098  else
2099  {
2100  SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(nLn) };
2101  aTextSize.setWidth( GetCachedTextWidth(aGlyphsKey, rInf.GetVclCache()));
2102  rInf.SetKanaDiff( 0 );
2103  }
2104 
2105  aTextSize.setHeight( rInf.GetOut().GetTextHeight() );
2106  }
2107 
2108  if ( rInf.GetKern() && nLn )
2109  aTextSize.AdjustWidth((sal_Int32(nLn) - 1) * rInf.GetKern());
2110 
2111  OSL_ENSURE( !rInf.GetShell() ||
2112  ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExternalLeading() ),
2113  "Leading values should be already calculated" );
2114  aTextSize.AdjustHeight(GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
2115  return aTextSize;
2116 }
2117 
2119 {
2120  tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
2121  const tools::Long nSperren = -rInf.GetSperren() / SPACING_PRECISION_FACTOR;
2122  tools::Long nKern = rInf.GetKern();
2123 
2124  if( 0 != nSperren )
2125  nKern -= nSperren;
2126 
2127  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(rInf.GetLen())]);
2128 
2129  // be sure to have the correct layout mode at the printer
2130  if ( m_pPrinter )
2131  {
2134  SwTextGlyphsKey aGlyphsKey{ m_pPrinter, rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()) };
2135  SalLayoutGlyphs* pGlyphs = GetCachedSalLayoutGlyphs(aGlyphsKey);
2136  m_pPrinter->GetTextArray( rInf.GetText(), pKernArray.get(),
2137  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);
2138  }
2139  else
2140  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
2141  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
2142 
2143  const SwScriptInfo* pSI = rInf.GetScriptInfo();
2144  if ( rInf.GetFont() && rInf.GetLen() )
2145  {
2146  const SwFontScript nActual = rInf.GetFont()->GetActual();
2147 
2148  // Kana Compression
2149  if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
2150  pSI && pSI->CountCompChg() &&
2151  lcl_IsMonoSpaceFont( rInf.GetOut() ) )
2152  {
2153  pSI->Compress( pKernArray.get(), rInf.GetIdx(), rInf.GetLen(),
2154  rInf.GetKanaComp(),
2155  o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()),
2156  lcl_IsFullstopCentered( rInf.GetOut() ) );
2157  }
2158 
2159  // Asian Justification
2160  if ( SwFontScript::CJK == rInf.GetFont()->GetActual() )
2161  {
2163 
2164  if (!MsLangId::isKorean(aLang))
2165  {
2166  SwScriptInfo::CJKJustify( rInf.GetText(), pKernArray.get(), nullptr,
2167  rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
2168 
2169  nSpaceAdd = 0;
2170  }
2171 
2172  }
2173 
2174  // Kashida Justification
2175  if ( SwFontScript::CTL == nActual && rInf.GetSpace() )
2176  {
2177  if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
2178  {
2179  if ( pSI && pSI->CountKashida() &&
2180  pSI->KashidaJustify( pKernArray.get(), nullptr, rInf.GetIdx(), rInf.GetLen(),
2181  nSpaceAdd ) != -1 )
2182  nSpaceAdd = 0;
2183  }
2184  }
2185 
2186  // Thai Justification
2187  if ( SwFontScript::CTL == nActual && nSpaceAdd )
2188  {
2190 
2191  if ( LANGUAGE_THAI == aLang )
2192  {
2193  SwScriptInfo::ThaiJustify( rInf.GetText(), pKernArray.get(), nullptr,
2194  rInf.GetIdx(), rInf.GetLen(),
2195  rInf.GetNumberOfBlanks(),
2196  rInf.GetSpace() );
2197 
2198  // adding space to blanks is already done
2199  nSpaceAdd = 0;
2200  }
2201  }
2202  }
2203 
2204  tools::Long nLeft = 0;
2205  tools::Long nRight = 0;
2206  TextFrameIndex nCnt(0);
2207  tools::Long nSpaceSum = 0;
2208  tools::Long nKernSum = 0;
2209 
2210  if ( rInf.GetFrame() && rInf.GetLen() && rInf.SnapToGrid() &&
2211  rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() )
2212  {
2213  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
2214  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
2215  {
2216  const SwDoc* pDoc = rInf.GetShell()->GetDoc();
2217  const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
2218 
2219  tools::Long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen());
2220 
2221  sal_uLong i = nAvgWidthPerChar ?
2222  ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
2223  1;
2224 
2225  nAvgWidthPerChar = i * nGridWidth;
2226 
2227 // stupid CLANG
2228  nCnt = TextFrameIndex(rInf.GetOffset() / nAvgWidthPerChar);
2229  if (2 * (rInf.GetOffset() - sal_Int32(nCnt) * nAvgWidthPerChar) > nAvgWidthPerChar)
2230  ++nCnt;
2231 
2232  return nCnt;
2233  }
2234  }
2235 
2236  //for textgrid refactor
2237  if ( rInf.GetFrame() && rInf.GetLen() && rInf.SnapToGrid() )
2238  {
2239  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
2240  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
2241  {
2242 
2243  const tools::Long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
2244 
2245  for (TextFrameIndex j(0); j < rInf.GetLen(); j++)
2246  {
2247  tools::Long nScr = pKernArray[sal_Int32(j)] + (nSpaceAdd + nGridWidthAdd) * (sal_Int32(j) + 1);
2248  if( nScr >= rInf.GetOffset())
2249  {
2250  nCnt = j;
2251  break;
2252  }
2253  }
2254  return nCnt;
2255  }
2256  }
2257 
2258  sal_Int32 nDone = 0;
2259  TextFrameIndex nIdx = rInf.GetIdx();
2260  TextFrameIndex nLastIdx = nIdx;
2261  const TextFrameIndex nEnd = rInf.GetIdx() + rInf.GetLen();
2262 
2263  // #i105901#
2264  // skip character cells for all script types
2265  LanguageType aLang = rInf.GetFont()->GetLanguage();
2266 
2267  while ( ( nRight < tools::Long( rInf.GetOffset() ) ) && ( nIdx < nEnd ) )
2268  {
2269  if (nSpaceAdd && CH_BLANK == rInf.GetText()[ sal_Int32(nIdx)])
2270  nSpaceSum += nSpaceAdd;
2271 
2272  // go to next character (cell).
2273  nLastIdx = nIdx;
2274 
2275  nIdx = TextFrameIndex(g_pBreakIt->GetBreakIter()->nextCharacters(
2276  rInf.GetText(), sal_Int32(nIdx),
2277  g_pBreakIt->GetLocale( aLang ),
2278  i18n::CharacterIteratorMode::SKIPCELL, 1, nDone));
2279  if ( nIdx <= nLastIdx )
2280  break;
2281 
2282  nLeft = nRight;
2283  nRight = pKernArray[sal_Int32(nIdx - rInf.GetIdx()) - 1] + nKernSum + nSpaceSum;
2284 
2285  nKernSum += nKern;
2286  }
2287 
2288  // step back if position is before the middle of the character
2289  // or if we do not want to go to the next character
2290  if ( nIdx > rInf.GetIdx() &&
2291  ( rInf.IsPosMatchesBounds() ||
2292  ( ( nRight > tools::Long( rInf.GetOffset() ) ) &&
2293  ( nRight - rInf.GetOffset() > rInf.GetOffset() - nLeft ) ) ) )
2294  nCnt = nLastIdx - rInf.GetIdx(); // first half
2295  else
2296  nCnt = nIdx - rInf.GetIdx(); // second half
2297 
2298  if ( pSI )
2299  rInf.SetCursorBidiLevel( pSI->DirType( nLastIdx ) );
2300 
2301  return nCnt;
2302 }
2303 
2304 SwFntAccess::SwFntAccess( const void* & rnFontCacheId,
2305  sal_uInt16 &rIndex, const void *pOwn, SwViewShell const *pSh,
2306  bool bCheck ) :
2307  SwCacheAccess( *pFntCache, rnFontCacheId, rIndex ),
2308  m_pShell( pSh )
2309 {
2310  // the used ctor of SwCacheAccess searches for rnFontCacheId+rIndex in the cache
2311  if ( m_pObj )
2312  {
2313  // fast case: known Font (rnFontCacheId), no need to check printer and zoom
2314  if ( !bCheck )
2315  return;
2316 
2317  // Font is known, but has to be checked
2318  }
2319  else
2320  { // Font not known, must be searched
2321  bCheck = false;
2322  }
2323 
2324  {
2325  OutputDevice* pOut = nullptr;
2326  sal_uInt16 nZoom = USHRT_MAX;
2327 
2328  // Get the reference device
2329  if ( pSh )
2330  {
2331  pOut = &pSh->GetRefDev();
2332  nZoom = pSh->GetViewOptions()->GetZoom();
2333  }
2334 
2335  SwFntObj *pFntObj;
2336  if ( bCheck )
2337  {
2338  pFntObj = Get();
2339  if ( ( pFntObj->GetZoom( ) == nZoom ) &&
2340  ( pFntObj->m_pPrinter == pOut ) &&
2341  pFntObj->GetPropWidth() ==
2342  static_cast<SwSubFont const *>(pOwn)->GetPropWidth() )
2343  {
2344  return; // result of Check: Drucker+Zoom okay.
2345  }
2346  pFntObj->Unlock(); // forget this object, printer/zoom differs
2347  m_pObj = nullptr;
2348  }
2349 
2350  // Search by font comparison, quite expensive!
2351  // Look for same font and same printer
2352  pFntObj = pFntCache->First();
2353  while ( pFntObj && !( pFntObj->m_aFont == *static_cast<vcl::Font const *>(pOwn) &&
2354  pFntObj->GetZoom() == nZoom &&
2355  pFntObj->GetPropWidth() ==
2356  static_cast<SwSubFont const *>(pOwn)->GetPropWidth() &&
2357  ( !pFntObj->m_pPrinter || pFntObj->m_pPrinter == pOut ) ) )
2358  pFntObj = SwFntCache::Next( pFntObj );
2359 
2360  if( pFntObj && pFntObj->m_pPrinter.get() != pOut )
2361  {
2362  // found one without printer, let's see if there is one with
2363  // the same printer as well
2364  SwFntObj *pTmpObj = pFntObj;
2365  while( pTmpObj && !( pTmpObj->m_aFont == *static_cast<vcl::Font const *>(pOwn) &&
2366  pTmpObj->GetZoom()==nZoom && pTmpObj->m_pPrinter==pOut &&
2367  pTmpObj->GetPropWidth() ==
2368  static_cast<SwSubFont const *>(pOwn)->GetPropWidth() ) )
2369  pTmpObj = SwFntCache::Next( pTmpObj );
2370  if( pTmpObj )
2371  pFntObj = pTmpObj;
2372  }
2373 
2374  if ( !pFntObj ) // Font has not been found, create one
2375  {
2376  // Have to create new Object, hence Owner must be a SwFont, later
2377  // the Owner will be the "MagicNumber"
2378  SwCacheAccess::m_pOwner = pOwn;
2379  pFntObj = Get(); // will create via NewObj() and lock
2380  OSL_ENSURE(pFntObj, "No Font, no Fun.");
2381  }
2382  else // Font has been found, so we lock it.
2383  {
2384  pFntObj->Lock();
2385  if (pFntObj->m_pPrinter.get() != pOut) // if no printer is known by now
2386  {
2387  OSL_ENSURE( !pFntObj->m_pPrinter, "SwFntAccess: Printer Changed" );
2388  pFntObj->CreatePrtFont( *pOut );
2389  pFntObj->m_pPrinter = pOut;
2390  pFntObj->m_pScrFont = nullptr;
2391  pFntObj->m_nGuessedLeading = USHRT_MAX;
2392  pFntObj->m_nExtLeading = USHRT_MAX;
2393  pFntObj->m_nPrtAscent = USHRT_MAX;
2394  pFntObj->m_nPrtHeight = USHRT_MAX;
2395  }
2396  m_pObj = pFntObj;
2397  }
2398 
2399  // no matter if new or found, now the Owner of the Object is a
2400  // MagicNumber, and will be given to the SwFont, as well as the Index
2401  // for later direct access
2402  rnFontCacheId = reinterpret_cast<void*>(reinterpret_cast<sal_IntPtr>(pFntObj->GetOwner()));
2403  SwCacheAccess::m_pOwner = pFntObj->GetOwner();
2404  rIndex = pFntObj->GetCachePos();
2405  }
2406 }
2407 
2409 {
2410  // "MagicNumber" used to identify Fonts
2411  static std::uintptr_t fontCacheIdCounter = 0;
2412  // a new Font, a new "MagicNumber".
2413  return new SwFntObj( *static_cast<SwSubFont const *>(m_pOwner), ++fontCacheIdCounter, m_pShell );
2414 }
2415 
2417 {
2418  ChgFnt( rInf.GetShell(), rInf.GetOut() );
2419 
2420  const bool bCompress = rInf.GetKanaComp() && rInf.GetLen() &&
2422  rInf.GetScriptInfo() &&
2423  rInf.GetScriptInfo()->CountCompChg() &&
2424  lcl_IsMonoSpaceFont( rInf.GetOut() );
2425 
2426  OSL_ENSURE( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
2427  CountCompChg()), "Compression without info" );
2428 
2429  TextFrameIndex nTextBreak(0);
2430  tools::Long nKern = 0;
2431 
2433  ? TextFrameIndex(rInf.GetText().getLength()) : rInf.GetLen();
2434 
2435  if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() &&
2436  rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() )
2437  {
2438  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
2439  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars() )
2440  {
2441  const SwDoc* pDoc = rInf.GetShell()->GetDoc();
2442  const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
2443 
2444  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(rInf.GetLen())]);
2445  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
2446  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
2447 
2448  tools::Long nAvgWidthPerChar = pKernArray[sal_Int32(rInf.GetLen()) - 1] / sal_Int32(rInf.GetLen());
2449 
2450  const sal_uLong i = nAvgWidthPerChar ?
2451  ( nAvgWidthPerChar - 1 ) / nGridWidth + 1:
2452  1;
2453 
2454  nAvgWidthPerChar = i * nGridWidth;
2455  tools::Long nCurrPos = nAvgWidthPerChar;
2456 
2457  while( nTextBreak < rInf.GetLen() && nTextWidth >= nCurrPos )
2458  {
2459  nCurrPos += nAvgWidthPerChar;
2460  ++nTextBreak;
2461  }
2462 
2463  return nTextBreak + rInf.GetIdx();
2464  }
2465  }
2466 
2467  //for text grid enhancement
2468  if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() )
2469  {
2470  SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
2471  if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && !pGrid->IsSnapToChars() )
2472  {
2473  const tools::Long nGridWidthAdd = EvalGridWidthAdd( pGrid, rInf );
2474 
2475  std::unique_ptr<tools::Long[]> pKernArray(new tools::Long[sal_Int32(rInf.GetLen())] );
2476  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
2477  sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
2478  tools::Long nCurrPos = pKernArray[sal_Int32(nTextBreak)] + nGridWidthAdd;
2479  while (++nTextBreak < rInf.GetLen() && nTextWidth >= nCurrPos)
2480  {
2481  nCurrPos = pKernArray[sal_Int32(nTextBreak)] + nGridWidthAdd * (sal_Int32(nTextBreak) + 1);
2482  }
2483  return nTextBreak + rInf.GetIdx();
2484  }
2485  }
2486 
2487  if( m_aSub[m_nActual].IsCapital() && nLn )
2488  {
2489  nTextBreak = GetCapitalBreak( rInf.GetShell(), rInf.GetpOut(),
2490  rInf.GetScriptInfo(), rInf.GetText(), nTextWidth, rInf.GetIdx(),
2491  nLn );
2492  }
2493  else
2494  {
2495  nKern = CheckKerning();
2496 
2497  const OUString* pTmpText;
2498  OUString aTmpText;
2499  TextFrameIndex nTmpIdx;
2500  TextFrameIndex nTmpLen;
2501  bool bTextReplaced = false;
2502 
2503  if ( !m_aSub[m_nActual].IsCaseMap() )
2504  {
2505  pTmpText = &rInf.GetText();
2506  nTmpIdx = rInf.GetIdx();
2507  nTmpLen = nLn;
2508  }
2509  else
2510  {
2511  const OUString aSnippet(rInf.GetText().copy(sal_Int32(rInf.GetIdx()), sal_Int32(nLn)));
2512  aTmpText = m_aSub[m_nActual].CalcCaseMap( aSnippet );
2513  const bool bTitle = SvxCaseMap::Capitalize == m_aSub[m_nActual].GetCaseMap();
2514 
2515  // Uaaaaahhhh!!! In title case mode, we would get wrong results
2516  if ( bTitle && nLn )
2517  {
2518  // check if rInf.GetIdx() is begin of word
2519  if ( !g_pBreakIt->GetBreakIter()->isBeginWord(
2520  rInf.GetText(), sal_Int32(rInf.GetIdx()),
2521  g_pBreakIt->GetLocale( m_aSub[m_nActual].GetLanguage() ),
2522  i18n::WordType::ANYWORD_IGNOREWHITESPACES ) )
2523  {
2524  // In this case, the beginning of aTmpText is wrong.
2525  OUString aSnippetTmp(aSnippet.copy(0, 1));
2526  aSnippetTmp = m_aSub[m_nActual].CalcCaseMap( aSnippetTmp );
2527  aTmpText = aTmpText.replaceAt( 0, aSnippetTmp.getLength(), OUString(aSnippet[0]) );
2528  }
2529  }
2530 
2531  pTmpText = &aTmpText;
2532  nTmpIdx = TextFrameIndex(0);
2533  nTmpLen = TextFrameIndex(aTmpText.getLength());
2534  bTextReplaced = true;
2535  }
2536 
2537  if( rInf.GetHyphPos() ) {
2538  sal_Int32 nHyphPos = sal_Int32(*rInf.GetHyphPos());
2539  nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak(
2540  *pTmpText, nTextWidth,
2541  u'-', nHyphPos,
2542  sal_Int32(nTmpIdx), sal_Int32(nTmpLen),
2543  nKern, rInf.GetVclCache()));
2544  *rInf.GetHyphPos() = TextFrameIndex((nHyphPos == -1) ? COMPLETE_STRING : nHyphPos);
2545  }
2546  else
2547  {
2548  SwFntAccess aFntAccess(m_aSub[m_nActual].m_nFontCacheId, m_aSub[m_nActual].m_nFontIndex,
2549  &m_aSub[m_nActual], rInf.GetShell());
2550  SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), *pTmpText, sal_Int32(nTmpIdx), sal_Int32(nTmpLen) };
2551  SalLayoutGlyphs* pGlyphs = aFntAccess.Get()->GetCachedSalLayoutGlyphs(aGlyphsKey);
2552  nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak(
2553  *pTmpText, nTextWidth,
2554  sal_Int32(nTmpIdx), sal_Int32(nTmpLen),
2555  nKern, rInf.GetVclCache(), pGlyphs));
2556  }
2557 
2558  if (bTextReplaced && sal_Int32(nTextBreak) != -1)
2559  {
2560  if ( nTmpLen != nLn )
2561  nTextBreak = sw_CalcCaseMap( *this, rInf.GetText(),
2562  rInf.GetIdx(), nLn, nTextBreak );
2563  else
2564  nTextBreak = nTextBreak + rInf.GetIdx();
2565  }
2566  }
2567 
2568  TextFrameIndex nTextBreak2 = sal_Int32(nTextBreak) == -1
2570  : nTextBreak;
2571 
2572  // tdf112290 tdf136588 Break the line correctly only if there is an image inline,
2573  // and the image wider than the line...
2574  if (GetCaseMap() == SvxCaseMap::SmallCaps && TextFrameIndex(COMPLETE_STRING) == nTextBreak2 &&
2575  ! bCompress && nTextWidth == 0)
2576  // If nTextWidth == 0 means the line is full, we have to break it
2577  nTextBreak2 = TextFrameIndex(1);
2578 
2579  if ( ! bCompress )
2580  return nTextBreak2;
2581 
2582  nTextBreak2 = nTextBreak2 - rInf.GetIdx();
2583 
2584  if( nTextBreak2 < nLn )
2585  {
2586  if( !nTextBreak2 && nLn )
2587  nLn = TextFrameIndex(1);
2588  else if (nLn > nTextBreak2 + nTextBreak2)
2589  nLn = nTextBreak2 + nTextBreak2;
2590  std::unique_ptr<tools::Long[]> pKernArray( new tools::Long[sal_Int32(nLn)] );
2591  rInf.GetOut().GetTextArray( rInf.GetText(), pKernArray.get(),
2592  sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
2593  if( rInf.GetScriptInfo()->Compress( pKernArray.get(), rInf.GetIdx(), nLn,
2594  rInf.GetKanaComp(), o3tl::narrowing<sal_uInt16>(GetHeight( m_nActual )),
2595  lcl_IsFullstopCentered( rInf.GetOut() ) ) )
2596  {
2597  tools::Long nKernAdd = nKern;
2598  TextFrameIndex const nTmpBreak = nTextBreak2;
2599  if( nKern && nTextBreak2 )
2600  nKern *= sal_Int32(nTextBreak2) - 1;
2601  while (nTextBreak2 < nLn && nTextWidth >= pKernArray[sal_Int32(nTextBreak2)] + nKern)
2602  {
2603  nKern += nKernAdd;
2604  ++nTextBreak2;
2605  }
2606  if( rInf.GetHyphPos() )
2607  *rInf.GetHyphPos() += nTextBreak2 - nTmpBreak; // It's not perfect
2608  }
2609  }
2610  nTextBreak2 = nTextBreak2 + rInf.GetIdx();
2611 
2612  return nTextBreak2;
2613 }
2614 
2616 {
2617  const vcl::Font& rFnt = pFont ? *pFont : GetOut().GetFont();
2618  Color nNewColor = COL_BLACK;
2619  bool bChgFntColor = false;
2620  bool bChgLineColor = false;
2621 
2622  if (GetShell() && !GetShell()->GetWin() && GetShell()->GetViewOptions()->IsBlackFont())
2623  {
2624  if ( COL_BLACK != rFnt.GetColor() )
2625  bChgFntColor = true;
2626 
2627  if ( (COL_BLACK != GetOut().GetLineColor()) ||
2628  (COL_BLACK != GetOut().GetOverlineColor()) )
2629  bChgLineColor = true;
2630  }
2631  else
2632  {
2633  // FontColor has to be changed if:
2634  // 1. FontColor = AUTO or 2. IsAlwaysAutoColor is set
2635  // LineColor has to be changed if:
2636  // 1. IsAlwaysAutoColor is set
2637 
2638  bChgLineColor = GetShell() && GetShell()->GetWin() &&
2640 
2641  bChgFntColor = COL_AUTO == rFnt.GetColor() || bChgLineColor;
2642 
2643  if ( bChgFntColor )
2644  {
2645  // check if current background has a user defined setting
2646  std::optional<Color> pCol;
2647  if (GetFont())
2648  pCol = GetFont()->GetBackColor();
2649  if( ! pCol || COL_TRANSPARENT == *pCol )
2650  {
2651  const SvxBrushItem* pItem;
2652  SwRect aOrigBackRect;
2654 
2661  if( GetFrame()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/true ) )
2662  {
2663  if (aFillAttributes && aFillAttributes->isUsed())
2664  {
2665  // First see if fill attributes provide a color.
2666  pCol = Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
2667  }
2668 
2669  // If not, then fall back to the old brush item.
2670  if ( !pCol )
2671  {
2672  pCol = pItem->GetColor();
2673  }
2674 
2677  if ( *pCol == COL_TRANSPARENT)
2678  pCol.reset();
2679  }
2680  else
2681  pCol.reset();
2682  }
2683 
2684  // no user defined color at paragraph or font background
2685  if ( ! pCol )
2686  pCol = aGlobalRetoucheColor;
2687 
2688  if( GetShell() && GetShell()->GetWin() )
2689  {
2690  // here we determine the preferred window text color for painting
2691  const SwViewOption* pViewOption = GetShell()->GetViewOptions();
2692  if(pViewOption->IsPagePreview() &&
2694  nNewColor = COL_BLACK;
2695  else
2696  // we take the font color from the appearance page
2697  nNewColor = SwViewOption::GetFontColor();
2698  }
2699 
2700  // change painting color depending of dark/bright background
2701  Color aTmpColor( nNewColor );
2702  if ( pCol->IsDark() && aTmpColor.IsDark() )
2703  nNewColor = COL_WHITE;
2704  else if ( pCol->IsBright() && aTmpColor.IsBright() )
2705  nNewColor = COL_BLACK;
2706  }
2707  }
2708 
2709  if ( bChgFntColor || bChgLineColor )
2710  {
2711  Color aNewColor( nNewColor );
2712 
2713  if ( bChgFntColor )
2714  {
2715  if ( pFont && aNewColor != pFont->GetColor() )
2716  {
2717  // only set the new color at the font passed as argument
2718  pFont->SetColor( aNewColor );
2719  }
2720  else if ( aNewColor != GetOut().GetFont().GetColor() )
2721  {
2722  // set new font with new color at output device
2723  vcl::Font aFont( rFnt );
2724  aFont.SetColor( aNewColor );
2725  GetOut().SetFont( aFont );
2726  }
2727  }
2728 
2729  // the underline and overline colors have to be set separately
2730  if ( bChgLineColor )
2731  {
2732  // get current font color or color set at output device
2733  aNewColor = pFont ? pFont->GetColor() : GetOut().GetFont().GetColor();
2734  if ( aNewColor != GetOut().GetLineColor() )
2735  GetOut().SetLineColor( aNewColor );
2736  if ( aNewColor != GetOut().GetOverlineColor() )
2737  GetOut().SetOverlineColor( aNewColor );
2738  }
2739 
2740  return true;
2741  }
2742 
2743  return false;
2744 }
2745 
2747 {
2748  for (SwFntObj* pFntObj = pFntCache->First(); pFntObj; pFntObj = SwFntCache::Next(pFntObj))
2749  pFntObj->ClearCachedTextGlyphs();
2750 }
2751 
2752 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
const Fraction & GetScaleX() const
SwViewShell const * GetShell() const
Definition: drawfont.hxx:175
OutDevType GetOutDevType() const
sal_uInt16 m_nExtLeading
Definition: fntcache.hxx:78
void SetDigitLanguage(LanguageType)
sal_uInt16 GetCachePos() const
Definition: swcache.hxx:171
sw::WrongListIterator * GetGrammarCheck() const
Definition: drawfont.hxx:229
const SwWrongArea * GetWrongElement(TextFrameIndex nStart)
Definition: wrong.cxx:785
void CreateScrFont(const SwViewShell &rSh, const OutputDevice &rOut)
Definition: fntcache.cxx:525
void SetFontSize(const Size &)
short CheckKerning()
Definition: swfont.hxx:320
void SetDistance(double nDistance)
bool IsPagePreview() const
Definition: viewopt.hxx:635
virtual SwCacheObj * NewObj() override
Can be use in NewObj.
Definition: fntcache.cxx:2408
const OUString & GetFamilyName() const
SwFntAccess(const void *&rnFontCacheId, sal_uInt16 &rIndex, const void *pOwner, SwViewShell const *pShell, bool bCheck=false)
Definition: fntcache.cxx:2304
tools::Long Compress(tools::Long *pKernArray, TextFrameIndex nIdx, TextFrameIndex nLen, const sal_uInt16 nCompress, const sal_uInt16 nFontHeight, const bool bCentered, Point *pPoint=nullptr) const
Definition: porlay.cxx:2027
vcl::RenderContext * GetpOut() const
Definition: drawfont.hxx:185
bool m_bPaintBlank
Definition: fntcache.hxx:86
SwFont * GetFont() const
Definition: drawfont.hxx:250
void setWidth(tools::Long nWidth)
void Lock()
Definition: swcache.cxx:475
SwCacheObj * m_pObj
Definition: swcache.hxx:202
virtual bool IsVirtual() const
const SwAccessibilityOptions * GetAccessibilityOptions() const
Definition: viewsh.hxx:431
sal_uInt16 m_nPropWidth
Definition: fntcache.hxx:83
SwDocShell * GetDocShell()
Definition: doc.hxx:1352
sw::WrongListIterator * GetWrong() const
Definition: drawfont.hxx:221
sal_uInt16 GetFontHeight(const SwViewShell *pSh, const OutputDevice &rOut)
Definition: fntcache.cxx:434
bool m_bSymbol
Definition: fntcache.hxx:85
TextFrameIndex * GetHyphPos() const
Definition: drawfont.hxx:203
bool GetBullet() const
Definition: drawfont.hxx:346
FontLineStyle GetOverline() const
void SwitchHorizontalToVertical(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from horizontal to vertical layout...
Definition: txtfrm.cxx:475
sal_uIntPtr sal_uLong
long Long
constexpr::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
static void lcl_DrawLineForWrongListData(SwForbidden &rForbidden, const SwDrawTextInfo &rInf, sw::WrongListIterator *pWList, const CalcLinePosData &rCalcLinePosData, const Size &rPrtFontSize)
Definition: fntcache.cxx:750
TextFrameIndex GetModelPositionForViewPoint(SwDrawTextInfo &rInf)
Definition: fntcache.cxx:2118
constexpr::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
sal_Int32 GetTextBreak(const OUString &rStr, tools::Long nTextWidth, sal_Int32 nIndex, sal_Int32 nLen=-1, tools::Long nCharExtra=0, vcl::TextLayoutCache const *=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: doc.hxx:188
sal_uInt16 GetFontAscent(const SwViewShell *pSh, const OutputDevice &rOut)
Definition: fntcache.cxx:397
sal_uInt16 m_nScrHeight
Definition: fntcache.hxx:81
void ClearCachedTextGlyphs()
Definition: fntcache.cxx:276
void SetWeight(FontWeight)
sal_uInt16 GetGuessedLeading() const
Definition: fntcache.hxx:104
ComplexTextLayoutFlags GetLayoutMode() const
bool IsPosMatchesBounds() const
Definition: drawfont.hxx:387
const MapMode & GetMapMode() const
const IDocumentSettingAccess & getIDocumentSettingAccess() const
Provides access to the document setting interface.
Definition: viewsh.cxx:2662
WrongAreaLineType mLineType
Definition: wrong.hxx:76
sal_uInt16 GetWidth() const
Definition: drawfont.hxx:294
void SetLayoutMode(ComplexTextLayoutFlags nTextLayoutMode)
VclPtr< OutputDevice > m_pOutputDevice
Definition: fntcache.cxx:81
const sal_Unicode CH_BULLET
Definition: swfont.hxx:46
GDIMetaFile * GetConnectMetaFile() const
SalLayoutGlyphs m_aTextGlyphs
Definition: fntcache.cxx:93
virtual SfxItemSet & GetItemSet()
void SetDashCount(sal_uInt16 nDashCount)
Of course Writer needs its own rectangles.
Definition: swrect.hxx:34
static sal_uInt8 lcl_WhichPunctuation(sal_Unicode cChar)
Definition: fntcache.cxx:711
const void * m_pOwner
Definition: swcache.hxx:203
sal_uInt16 GetZoom() const
Definition: fntcache.hxx:115
void SetMapMode()
sal_uInt8 DirType(const TextFrameIndex nPos) const
Definition: porlay.cxx:1756
bool operator<(const SwTextGlyphsKey &l, const SwTextGlyphsKey &r)
Definition: fntcache.cxx:121
VclPtr< OutputDevice > m_pPrinter
Definition: fntcache.hxx:76
void DrawTextArray(const Point &rStartPt, const OUString &rStr, const tools::Long *pDXAry, sal_Int32 nIndex=0, sal_Int32 nLen=-1, SalLayoutFlags flags=SalLayoutFlags::NONE, const SalLayoutGlyphs *pLayoutCache=nullptr)
SalLayoutGlyphs * GetCachedSalLayoutGlyphs(const SwTextGlyphsKey &key)
Definition: fntcache.cxx:244
bool IsVertLRBT() const
Definition: frame.hxx:980
size_t CountCompChg() const
Definition: scriptinfo.hxx:162
tools::Long GetExternalLeading() const
vcl::Font & GetFont()
Definition: fntcache.hxx:101
const SwScriptInfo * GetScriptInfo() const
Definition: drawfont.hxx:190
static OutputDevice * GetDefaultDevice()
constexpr tools::Long Width() const
const Color & GetOverlineColor() const
void SetCursorBidiLevel(sal_uInt8 nNew)
Definition: drawfont.hxx:526
static SwFntObj * Next(SwFntObj *pFntObj)
Definition: fntcache.hxx:137
sal_uInt16 GetFontLeading(const SwViewShell *pSh, const OutputDevice &rOut)
Definition: fntcache.cxx:473
sal_uInt16 sal_Unicode
FontItalic GetItalic()
void DrawStretchText(const Point &rStartPt, sal_uLong nWidth, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1)
const SwTextFrame * GetFrame() const
Definition: drawfont.hxx:165
Color mColor
Definition: wrong.hxx:75
FontKerning GetKerning() const
LINESTYLE_NONE
tools::Long GetAscent() const
OUTDEV_WINDOW
bool getBrowseMode() const
Definition: viewopt.hxx:472
const vcl::Font & GetFont() const
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:33
void SetKanaDiff(tools::Long nNew)
Definition: drawfont.hxx:474
std::shared_ptr< SdrAllFillAttributesHelper > SdrAllFillAttributesHelperPtr
Definition: format.hxx:41
void Flush()
Definition: fntcache.cxx:154
oslFileHandle & pOut
SvxCaseMap GetCaseMap() const
Definition: swfont.hxx:276
o3tl::enumarray< SwFontScript, SwSubFont > m_aSub
Definition: swfont.hxx:134
FontMetric GetFontMetric() const
SwDoc * GetDoc() const
Definition: viewsh.hxx:281
const std::optional< Color > & GetBackColor() const
Definition: swfont.hxx:190
void DrawLine(const Point &rStartPt, const Point &rEndPt)
vcl::TextLayoutCache const * GetVclCache() const
Definition: drawfont.hxx:211
SwFntCache * pFntCache
Definition: fntcache.cxx:65
sal_uInt16 GetKanaComp() const
Definition: drawfont.hxx:307
void SetLanguage(LanguageType)
SwFontScript GetActual() const
Definition: swfont.hxx:182
void SetLen(TextFrameIndex const nNew)
Definition: drawfont.hxx:461
Defines a substring on a given output device, to be used as an std::map<> key.
Definition: fntcache.cxx:79
OUString m_aText
Definition: fntcache.cxx:82
static void CJKJustify(const OUString &rText, tools::Long *pKernArray, tools::Long *pScrArray, TextFrameIndex nStt, TextFrameIndex nLen, LanguageType aLang, tools::Long nSpaceAdd, bool bIsSpaceStop)
Definition: porlay.cxx:2742
const OUString & GetText() const
Definition: drawfont.hxx:216
sw::WrongListIterator * GetSmartTags() const
Definition: drawfont.hxx:237
vcl::Font * m_pScrFont
Definition: fntcache.hxx:74
vcl::RenderContext & GetRefDev() const
Definition: viewsh.cxx:2073
static bool IsArabicText(const OUString &rText, TextFrameIndex nStt, TextFrameIndex nLen)
Checks if text is Arabic text.
Definition: porlay.cxx:2229
void CreatePrtFont(const OutputDevice &rOut)
Definition: fntcache.cxx:195
vcl::Font * m_pPrtFont
Definition: fntcache.hxx:75
const Color & GetColor() const
OUTDEV_PRINTER
bool SnapToGrid() const
Definition: drawfont.hxx:377
void SetLineColor()
UNDERLYING_TYPE get() const
bool ApplyAutoColor(vcl::Font *pFnt=nullptr)
Definition: fntcache.cxx:2615
tools::Long GetKanaDiff() const
Definition: drawfont.hxx:286
void GuessLeading(const SwViewShell &rSh, const FontMetric &rMet)
Definition: fntcache.cxx:599
SwFontScript m_nActual
Definition: swfont.hxx:163
Font m_aFont
bool IsIgnoreFrameRTL() const
Definition: drawfont.hxx:382
int i
uno_Any a
SwViewShell const * m_pShell
Definition: fntcache.hxx:144
static bool lcl_IsFullstopCentered(const vcl::RenderContext &rOut)
Definition: fntcache.cxx:739
bool IsDark() const
bool IsFullstopCentered() const
ComplexTextLayoutFlags
SwPageFrame * FindPageFrame()
Definition: frame.hxx:679
SwTextGridItem const * GetGridItem(SwPageFrame const *const)
Definition: pagechg.cxx:2574
bool IsBright() const
const sal_Unicode CH_BLANK
Definition: swfont.hxx:42
TextFrameIndex GetCapitalBreak(SwViewShell const *pSh, const OutputDevice *pOut, const SwScriptInfo *pScript, const OUString &rText, tools::Long nTextWidth, TextFrameIndex nIdx, TextFrameIndex nLen)
Definition: fntcap.cxx:221
bool IsWordLineMode() const
static MapMode * s_pPixMap
Definition: fntcache.hxx:92
sal_uInt16 m_nZoom
Definition: fntcache.hxx:84
virtual SfxStyleSheetBasePool * GetStyleSheetPool() override
For Style PI.
Definition: docsh.cxx:1141
const Color & GetLineColor() const
TextFrameIndex sw_CalcCaseMap(const SwFont &rFnt, const OUString &rOrigString, TextFrameIndex nOfst, TextFrameIndex nLen, TextFrameIndex nIdx)
Definition: fntcap.cxx:60
#define WRONG_SHOW_MIN
Definition: fntcache.cxx:703
sal_uInt16 GetPropWidth() const
Definition: fntcache.hxx:116
float u
std::map< SwTextGlyphsKey, SwTextGlyphsData > m_aTextGlyphs
Cache of already calculated layout glyphs and text widths.
Definition: fntcache.hxx:89
static SalLayoutGlyphs * lcl_CreateLayout(const SwTextGlyphsKey &rKey, std::map< SwTextGlyphsKey, SwTextGlyphsData >::iterator it)
Pre-calculates glyph items for the rendered subset of rKey's text, assuming outdev state does not cha...
Definition: fntcache.cxx:223
Provides access to settings of a document.
const Size & GetFontSize() const
tools::Long m_nTextWidth
Definition: fntcache.cxx:94
#define NON_PRINTING_CHARACTER_COLOR
Definition: txtfrm.hxx:51
Glyphs and text width for the given SwTextGlyphsKey.
Definition: fntcache.cxx:91
FontStrikeout GetStrikeout() const
void SetOverlineColor()
Shell * m_pShell
void SetDashLen(double nDashLen)
tools::Long GetSperren() const
Definition: drawfont.hxx:312
static void lcl_calcLinePos(const CalcLinePosData &rData, Point &rStart, Point &rEnd, TextFrameIndex const nStart, TextFrameIndex const nWrLen)
Definition: fntcache.cxx:334
bool GetGreyWave() const
Definition: drawfont.hxx:367
tools::Long SwTwips
Definition: swtypes.hxx:50
void Flush()
Definition: swcache.cxx:149
static bool lcl_IsFontAdjustNecessary(const vcl::RenderContext &rOutDev, const vcl::RenderContext &rRefDev)
Definition: fntcache.cxx:291
std::unique_ptr< SalLayout > ImplLayout(const OUString &, sal_Int32 nIndex, sal_Int32 nLen, const Point &rLogicPos=Point(0, 0), tools::Long nLogicWidth=0, const tools::Long *pLogicDXArray=nullptr, SalLayoutFlags flags=SalLayoutFlags::NONE, vcl::TextLayoutCache const *=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
static TextFrameIndex ThaiJustify(const OUString &rText, tools::Long *pKernArray, tools::Long *pScrArray, TextFrameIndex nIdx, TextFrameIndex nLen, TextFrameIndex nNumberOfBlanks=TextFrameIndex(0), tools::Long nSpaceAdd=0)
Performs a thai justification on the kerning array.
Definition: porlay.cxx:2403
#define LANGUAGE_THAI
std::vector< std::pair< TextFrameIndex, TextFrameIndex > > SwForbidden
Definition: fntcache.cxx:748
void SetColor(const Color &)
bool Check(TextFrameIndex &rStart, TextFrameIndex &rLen)
Definition: wrong.cxx:672
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
css::uno::Reference< css::i18n::XBreakIterator > const & GetBreakIter() const
Definition: breakit.hxx:62
void SwitchLTRtoRTL(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from LTR to RTL layout.
Definition: txtfrm.cxx:685
sal_Int32 m_nIndex
Definition: fntcache.cxx:83
const css::lang::Locale & GetLocale(const LanguageType aLang)
Definition: breakit.hxx:67
vcl::Font * GetScrFont()
Definition: fntcache.hxx:100
void SetDevFont(const SwViewShell *pSh, OutputDevice &rOut)
Definition: fntcache.cxx:679
LanguageType GetLanguage() const
Definition: swfont.hxx:279
const Color & GetColor() const
SwFntObj * Get()
Definition: fntcache.hxx:152
tools::Long GetSpace() const
Definition: drawfont.hxx:325
sal_uInt32 GetHeight() const
sal_Int32 m_nLength
Definition: fntcache.cxx:84
static bool lcl_IsMonoSpaceFont(const vcl::RenderContext &rOut)
Definition: fntcache.cxx:732
virtual ~SwFntObj() override
Definition: fntcache.cxx:187
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
FontWeight GetWeight()
constexpr Color gWaveCol(COL_GRAY)
constexpr tools::Long Height() const
unsigned char sal_uInt8
TextFrameIndex GetNumberOfBlanks() const
Definition: drawfont.hxx:333
const SwViewOption * GetViewOptions() const
Definition: viewsh.hxx:423
vcl::Window * GetWin() const
Definition: viewsh.hxx:337
The Cache object base class Users of the Cache must derive a class from the SwCacheObj and store thei...
Definition: swcache.hxx:134
void SetFont(const vcl::Font &rNewFont)
const Point & GetPos() const
Definition: drawfont.hxx:195
LanguageType GetDigitLanguage() const
tools::Long GetCachedTextWidth(const SwTextGlyphsKey &key, const vcl::TextLayoutCache *vclCache)
Definition: fntcache.cxx:260
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
tools::Long GetTextHeight() const
TextFrameIndex GetLen() const
Definition: drawfont.hxx:268
sal_uInt16 m_nPrtAscent
Definition: fntcache.hxx:80
static Color & GetFontColor()
Definition: viewopt.cxx:456
SwFntObj * First()
Definition: fntcache.hxx:132
tools::Long AdjustWidth(tools::Long n)
tools::Long GetDescent() const
bool IsRightToLeft() const
Definition: frame.hxx:984
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
SwFntObj * pLastFont
Definition: fntcache.cxx:67
sal_uInt16 GetZoom() const
Definition: viewopt.hxx:505
tools::Long GetInternalLeading() const
QPRO_FUNC_TYPE nType
void SwClearFntCacheTextGlyphs()
Clears the pre-calculated text glyphs in all SwFntObj instances.
Definition: fntcache.cxx:2746
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
static bool isKorean(LanguageType nLang)
SwFntObj(const SwSubFont &rFont, std::uintptr_t nFontCacheId, SwViewShell const *pSh)
Definition: fntcache.cxx:164
bool IsPrtFormat() const
Definition: viewopt.hxx:541
SwFontScript
Definition: swfont.hxx:122
void DrawWaveLine(const Point &rStartPos, const Point &rEndPos, tools::Long nLineWidth=1)
sal_uInt16 GetGridWidth(SwTextGridItem const &, SwDoc const &)
Definition: pagechg.cxx:2588
tools::Long AdjustHeight(tools::Long n)
virtual bool get(DocumentSettingId id) const =0
Return the specified document setting.
static vcl::DeleteOnDeinit< VclPtr< OutputDevice > > s_pFntObjPixOut
Definition: fntcache.cxx:73
vcl::RenderContext & GetOut() const
Definition: drawfont.hxx:180
tools::Long GetKern() const
Definition: drawfont.hxx:320
basegfx::BColor getBColor() const
bool IsSnapToChars() const
Definition: tgrditem.hxx:100
TextFrameIndex GetTextBreak(SwDrawTextInfo const &rInf, tools::Long nTextWidth)
Definition: fntcache.cxx:2416
void Unlock()
Definition: swcache.cxx:481
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:56
bool IsSameInstance(const Font &) const
void SetItalic(FontItalic)
void setHeight(tools::Long nHeight)
FontLineStyle GetUnderline() const
size_t CountKashida() const
Definition: scriptinfo.hxx:151
sal_Int32 KashidaJustify(tools::Long *pKernArray, tools::Long *pScrArray, TextFrameIndex nStt, TextFrameIndex nLen, tools::Long nSpaceAdd=0) const
Performs a kashida justification on the kerning array.
Definition: porlay.cxx:2144
void DrawText(SwDrawTextInfo &rInf)
Definition: fntcache.cxx:875
bool IsVertical() const
Definition: frame.hxx:970
Degree10 UnMapDirection(Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT)
Definition: swfont.cxx:373
OutputDevice * get() const
tools::Long GetBulletOffset() const
#define SPACING_PRECISION_FACTOR
Definition: scriptinfo.hxx:40
Access class for the Cache.
Definition: swcache.hxx:195
TextFrameIndex GetIdx() const
Definition: drawfont.hxx:263
SwTextGrid GetGridType() const
Definition: tgrditem.hxx:81
bool IsSpaceStop() const
Definition: drawfont.hxx:372
sal_uInt16 m_nGuessedLeading
Definition: fntcache.hxx:77
STRIKEOUT_NONE
bool IsStarSymbol(const OUString &rFontName)
static tools::Long s_nPixWidth
Definition: fntcache.hxx:91
FontLineStyle
void ChgFnt(SwViewShell const *pSh, OutputDevice &rOut)
Definition: swfont.hxx:175
vcl::Font m_aFont
Definition: fntcache.hxx:73
OUString SwResId(std::string_view aId)
Definition: swmodule.cxx:165
constexpr OUStringLiteral first
void Push(PushFlags nFlags=PushFlags::ALL)
virtual SfxStyleSheetBase * Find(const OUString &, SfxStyleFamily eFam, SfxStyleSearchBits n=SfxStyleSearchBits::All)
sal_Int32 GetOffset() const
Definition: drawfont.hxx:273
o3tl::strong_int< sal_Int32, struct Tag_TextFrameIndex > TextFrameIndex
Denotes a character index in a text frame at a layout level, after extent mapping from a text node at...
void DrawText(const Point &rStartPt, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, std::vector< tools::Rectangle > *pVector=nullptr, OUString *pDisplayText=nullptr, const SalLayoutGlyphs *pLayoutCache=nullptr)
tools::Long GetTextArray(const OUString &rStr, tools::Long *pDXAry, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
constexpr TypedWhichId< SvxFontHeightItem > RES_CHRATR_CJK_FONTSIZE(23)
sal_uInt16 GetExternalLeading() const
Definition: fntcache.hxx:105
aStr
tools::Long GetHeight() const
Definition: swfont.hxx:280
FontStrikeout
Color aGlobalRetoucheColor
Definition: paintfrm.cxx:247
rtl_TextEncoding GetCharSet() const
const void * GetOwner() const
Definition: swcache.hxx:168
sal_uInt16 m_nScrAscent
Definition: fntcache.hxx:79
Size GetTextSize(SwDrawTextInfo &rInf)
determine the TextSize (of the printer)
Definition: fntcache.cxx:1919
sal_uInt16 m_nPrtHeight
Definition: fntcache.hxx:82
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo