LibreOffice Module vcl (master)  1
text.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 <sal/config.h>
21 
22 #include <memory>
24 
25 #include <com/sun/star/i18n/WordType.hpp>
26 #include <com/sun/star/i18n/XBreakIterator.hpp>
27 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
28 
30 #include <osl/file.h>
31 #include <rtl/ustrbuf.hxx>
32 #include <sal/log.hxx>
33 #include <tools/lineend.hxx>
34 #include <tools/debug.hxx>
35 #include <vcl/ctrl.hxx>
36 #include <vcl/gdimtf.hxx>
37 #include <vcl/metaact.hxx>
38 #include <vcl/metric.hxx>
39 #include <vcl/textrectinfo.hxx>
40 #include <vcl/virdev.hxx>
41 #include <vcl/sysdata.hxx>
42 #include <vcl/unohelp.hxx>
43 
44 #include <ImplLayoutArgs.hxx>
45 #include <ImplOutDevData.hxx>
46 #include <drawmode.hxx>
47 #include <salgdi.hxx>
48 #include <svdata.hxx>
49 #include <textlayout.hxx>
50 #include <textlineinfo.hxx>
51 #include <impglyphitem.hxx>
52 #include <optional>
53 #include <TextLayoutCache.hxx>
55 
56 #define TEXT_DRAW_ELLIPSIS (DrawTextFlags::EndEllipsis | DrawTextFlags::PathEllipsis | DrawTextFlags::NewsEllipsis)
57 
59 {
60  if( mpMetaFile )
61  mpMetaFile->AddAction( new MetaLayoutModeAction( nTextLayoutMode ) );
62 
63  mnTextLayoutMode = nTextLayoutMode;
64 
65  if( mpAlphaVDev )
66  mpAlphaVDev->SetLayoutMode( nTextLayoutMode );
67 }
68 
70 {
71  if( mpMetaFile )
72  mpMetaFile->AddAction( new MetaTextLanguageAction( eTextLanguage ) );
73 
74  meTextLanguage = eTextLanguage;
75 
76  if( mpAlphaVDev )
77  mpAlphaVDev->SetDigitLanguage( eTextLanguage );
78 }
79 
81 {
82 }
83 
85 {
86 }
87 
89 {
90  mvLines.push_back(rLine);
91 }
92 
94 {
95  mvLines.clear();
96 }
97 
99 {
101 
102  if ( mbInitTextColor )
103  {
105  mbInitTextColor = false;
106  }
107 }
108 
110  tools::Long nDistX, tools::Long nDistY, tools::Long nWidth, tools::Long nHeight )
111 {
112  tools::Long nX = nDistX;
113  tools::Long nY = nDistY;
114 
115  Degree10 nOrientation = mpFontInstance->mnOrientation;
116  if ( nOrientation )
117  {
118  // Rotate rect without rounding problems for 90 degree rotations
119  if ( !(nOrientation % 900_deg10) )
120  {
121  if ( nOrientation == 900_deg10 )
122  {
123  tools::Long nTemp = nX;
124  nX = nY;
125  nY = -nTemp;
126  nTemp = nWidth;
127  nWidth = nHeight;
128  nHeight = nTemp;
129  nY -= nHeight;
130  }
131  else if ( nOrientation == 1800_deg10 )
132  {
133  nX = -nX;
134  nY = -nY;
135  nX -= nWidth;
136  nY -= nHeight;
137  }
138  else /* ( nOrientation == 2700 ) */
139  {
140  tools::Long nTemp = nX;
141  nX = -nY;
142  nY = nTemp;
143  nTemp = nWidth;
144  nWidth = nHeight;
145  nHeight = nTemp;
146  nX -= nWidth;
147  }
148  }
149  else
150  {
151  nX += nBaseX;
152  nY += nBaseY;
153  // inflate because polygons are drawn smaller
154  tools::Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
155  tools::Polygon aPoly( aRect );
156  aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontInstance->mnOrientation );
157  ImplDrawPolygon( aPoly );
158  return;
159  }
160  }
161 
162  nX += nBaseX;
163  nY += nBaseY;
164  mpGraphics->DrawRect( nX, nY, nWidth, nHeight, *this ); // original code
165 
166 }
167 
169 {
170  const tools::Long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
171  const DevicePoint aBase = rSalLayout.DrawBase();
172  const tools::Long nX = aBase.getX();
173  const tools::Long nY = aBase.getY();
174 
175  if ( mbLineColor || mbInitLineColor )
176  {
178  mbInitLineColor = true;
179  }
181  mbInitFillColor = true;
182 
183  ImplDrawTextRect( nX, nY, 0, -(mpFontInstance->mxFontMetric->GetAscent() + mnEmphasisAscent),
184  nWidth,
186 }
187 
189 {
190  DevicePoint aPoint = rSalLayout.GetDrawPosition();
191  tools::Long nX = aPoint.getX();
192  tools::Long nY = aPoint.getY();
193 
194  tools::Long nWidth = rSalLayout.GetTextWidth();
195  tools::Long nHeight = mpFontInstance->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
196 
197  nY -= mpFontInstance->mxFontMetric->GetAscent() + mnEmphasisAscent;
198 
199  if ( mpFontInstance->mnOrientation )
200  {
201  tools::Long nBaseX = nX, nBaseY = nY;
202  if ( !(mpFontInstance->mnOrientation % 900_deg10) )
203  {
204  tools::Long nX2 = nX+nWidth;
205  tools::Long nY2 = nY+nHeight;
206 
207  Point aBasePt( nBaseX, nBaseY );
208  aBasePt.RotateAround( nX, nY, mpFontInstance->mnOrientation );
209  aBasePt.RotateAround( nX2, nY2, mpFontInstance->mnOrientation );
210  nWidth = nX2-nX;
211  nHeight = nY2-nY;
212  }
213  else
214  {
215  // inflate by +1+1 because polygons are drawn smaller
216  tools::Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
217  tools::Polygon aPoly( aRect );
218  aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontInstance->mnOrientation );
219  return aPoly.GetBoundRect();
220  }
221  }
222 
223  return tools::Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) );
224 }
225 
227 {
228  tools::Long nX = rSalLayout.DrawBase().getX();
229  tools::Long nY = rSalLayout.DrawBase().getY();
230 
231  tools::Rectangle aBoundRect;
232  rSalLayout.DrawBase() = DevicePoint( 0, 0 );
233  rSalLayout.DrawOffset() = Point( 0, 0 );
234  if (!rSalLayout.GetBoundRect(aBoundRect))
235  {
236  // guess vertical text extents if GetBoundRect failed
237  tools::Long nRight = rSalLayout.GetTextWidth();
238  tools::Long nTop = mpFontInstance->mxFontMetric->GetAscent() + mnEmphasisAscent;
239  tools::Long nHeight = mpFontInstance->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
240  aBoundRect = tools::Rectangle( 0, -nTop, nRight, nHeight - nTop );
241  }
242 
243  // cache virtual device for rotation
244  if (!mpOutDevData->mpRotateDev)
245  mpOutDevData->mpRotateDev = VclPtr<VirtualDevice>::Create(*this);
246  VirtualDevice* pVDev = mpOutDevData->mpRotateDev;
247 
248  // size it accordingly
249  if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) )
250  return false;
251 
252  const vcl::font::FontSelectPattern& rPattern = mpFontInstance->GetFontSelectPattern();
253  vcl::Font aFont( GetFont() );
254  aFont.SetOrientation( 0_deg10 );
255  aFont.SetFontSize( Size( rPattern.mnWidth, rPattern.mnHeight ) );
256  pVDev->SetFont( aFont );
257  pVDev->SetTextColor( COL_BLACK );
258  pVDev->SetTextFillColor();
259  if (!pVDev->InitFont())
260  return false;
261  pVDev->ImplInitTextColor();
262 
263  // draw text into upper left corner
264  rSalLayout.DrawBase().adjustX(-aBoundRect.Left());
265  rSalLayout.DrawBase().adjustY(-aBoundRect.Top());
266  rSalLayout.DrawText( *pVDev->mpGraphics );
267 
268  Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() );
269  if ( aBmp.IsEmpty() || !aBmp.Rotate( mpFontInstance->mnOwnOrientation, COL_WHITE ) )
270  return false;
271 
272  // calculate rotation offset
273  tools::Polygon aPoly( aBoundRect );
274  aPoly.Rotate( Point(), mpFontInstance->mnOwnOrientation );
275  Point aPoint = aPoly.GetBoundRect().TopLeft();
276  aPoint += Point( nX, nY );
277 
278  // mask output with text colored bitmap
279  GDIMetaFile* pOldMetaFile = mpMetaFile;
280  tools::Long nOldOffX = mnOutOffX;
281  tools::Long nOldOffY = mnOutOffY;
282  bool bOldMap = mbMap;
283 
284  mnOutOffX = 0;
285  mnOutOffY = 0;
286  mpMetaFile = nullptr;
287  EnableMapMode( false );
288 
289  DrawMask( aPoint, aBmp, GetTextColor() );
290 
291  EnableMapMode( bOldMap );
292  mnOutOffX = nOldOffX;
293  mnOutOffY = nOldOffY;
294  mpMetaFile = pOldMetaFile;
295 
296  return true;
297 }
298 
300  bool bTextLines)
301 {
302  if( mpFontInstance->mnOwnOrientation )
303  if( ImplDrawRotateText( rSalLayout ) )
304  return;
305 
306  auto nOldX = rSalLayout.DrawBase().getX();
307  if( HasMirroredGraphics() )
308  {
310  auto x = rSalLayout.DrawBase().getX();
311  rSalLayout.DrawBase().setX( w - 1 - x );
312  if( !IsRTLEnabled() )
313  {
314  OutputDevice *pOutDevRef = this;
315  // mirror this window back
316  tools::Long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
317  rSalLayout.DrawBase().setX( devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().getX() - devX) ) ) ;
318  }
319  }
320  else if( IsRTLEnabled() )
321  {
322  OutputDevice *pOutDevRef = this;
323 
324  // mirror this window back
325  tools::Long devX = pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
326  rSalLayout.DrawBase().setX( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().getX() - devX) + devX );
327  }
328 
329  rSalLayout.DrawText( *mpGraphics );
330  rSalLayout.DrawBase().setX( nOldX );
331 
332  if( bTextLines )
333  ImplDrawTextLines( rSalLayout,
336 
337  // emphasis marks
338  if( maFont.GetEmphasisMark() & FontEmphasisMark::Style )
339  ImplDrawEmphasisMarks( rSalLayout );
340 }
341 
343 {
344  Color aOldColor = GetTextColor();
345  Color aOldTextLineColor = GetTextLineColor();
346  Color aOldOverlineColor = GetOverlineColor();
347  FontRelief eRelief = maFont.GetRelief();
348 
349  DevicePoint aOrigPos = rSalLayout.DrawBase();
350  if ( eRelief != FontRelief::NONE )
351  {
352  Color aReliefColor( COL_LIGHTGRAY );
353  Color aTextColor( aOldColor );
354 
355  Color aTextLineColor( aOldTextLineColor );
356  Color aOverlineColor( aOldOverlineColor );
357 
358  // we don't have an automatic color, so black is always drawn on white
359  if ( aTextColor == COL_BLACK )
360  aTextColor = COL_WHITE;
361  if ( aTextLineColor == COL_BLACK )
362  aTextLineColor = COL_WHITE;
363  if ( aOverlineColor == COL_BLACK )
364  aOverlineColor = COL_WHITE;
365 
366  // relief-color is black for white text, in all other cases
367  // we set this to LightGray
368  // coverity[copy_paste_error: FALSE] - this is intentional
369  if ( aTextColor == COL_WHITE )
370  aReliefColor = COL_BLACK;
371  SetTextLineColor( aReliefColor );
372  SetOverlineColor( aReliefColor );
373  SetTextColor( aReliefColor );
375 
376  // calculate offset - for high resolution printers the offset
377  // should be greater so that the effect is visible
378  tools::Long nOff = 1;
379  nOff += mnDPIX/300;
380 
381  if ( eRelief == FontRelief::Engraved )
382  nOff = -nOff;
383  rSalLayout.DrawOffset() += Point( nOff, nOff);
384  ImplDrawTextDirect( rSalLayout, mbTextLines );
385  rSalLayout.DrawOffset() -= Point( nOff, nOff);
386 
387  SetTextLineColor( aTextLineColor );
388  SetOverlineColor( aOverlineColor );
389  SetTextColor( aTextColor );
391  ImplDrawTextDirect( rSalLayout, mbTextLines );
392 
393  SetTextLineColor( aOldTextLineColor );
394  SetOverlineColor( aOldOverlineColor );
395 
396  if ( aTextColor != aOldColor )
397  {
398  SetTextColor( aOldColor );
400  }
401  }
402  else
403  {
404  if ( maFont.IsShadow() )
405  {
406  tools::Long nOff = 1 + ((mpFontInstance->mnLineHeight-24)/24);
407  if ( maFont.IsOutline() )
408  nOff++;
411  if ( (GetTextColor() == COL_BLACK)
412  || (GetTextColor().GetLuminance() < 8) )
414  else
417  rSalLayout.DrawBase() += DevicePoint( nOff, nOff );
418  ImplDrawTextDirect( rSalLayout, mbTextLines );
419  rSalLayout.DrawBase() -= DevicePoint( nOff, nOff );
420  SetTextColor( aOldColor );
421  SetTextLineColor( aOldTextLineColor );
422  SetOverlineColor( aOldOverlineColor );
424 
425  if ( !maFont.IsOutline() )
426  ImplDrawTextDirect( rSalLayout, mbTextLines );
427  }
428 
429  if ( maFont.IsOutline() )
430  {
431  rSalLayout.DrawBase() = aOrigPos + DevicePoint(-1,-1);
432  ImplDrawTextDirect( rSalLayout, mbTextLines );
433  rSalLayout.DrawBase() = aOrigPos + DevicePoint(+1,+1);
434  ImplDrawTextDirect( rSalLayout, mbTextLines );
435  rSalLayout.DrawBase() = aOrigPos + DevicePoint(-1,+0);
436  ImplDrawTextDirect( rSalLayout, mbTextLines );
437  rSalLayout.DrawBase() = aOrigPos + DevicePoint(-1,+1);
438  ImplDrawTextDirect( rSalLayout, mbTextLines );
439  rSalLayout.DrawBase() = aOrigPos + DevicePoint(+0,+1);
440  ImplDrawTextDirect( rSalLayout, mbTextLines );
441  rSalLayout.DrawBase() = aOrigPos + DevicePoint(+0,-1);
442  ImplDrawTextDirect( rSalLayout, mbTextLines );
443  rSalLayout.DrawBase() = aOrigPos + DevicePoint(+1,-1);
444  ImplDrawTextDirect( rSalLayout, mbTextLines );
445  rSalLayout.DrawBase() = aOrigPos + DevicePoint(+1,+0);
446  ImplDrawTextDirect( rSalLayout, mbTextLines );
447  rSalLayout.DrawBase() = aOrigPos;
448 
453  ImplDrawTextDirect( rSalLayout, mbTextLines );
454  SetTextColor( aOldColor );
455  SetTextLineColor( aOldTextLineColor );
456  SetOverlineColor( aOldOverlineColor );
458  }
459  }
460 }
461 
463 {
464 
465  if( mbInitClipRegion )
466  InitClipRegion();
467  if( mbOutputClipped )
468  return;
469  if( mbInitTextColor )
471 
472  rSalLayout.DrawBase() += DevicePoint(mnTextOffX, mnTextOffY);
473 
474  if( IsTextFillColor() )
475  ImplDrawTextBackground( rSalLayout );
476 
477  if( mbTextSpecial )
478  ImplDrawSpecialText( rSalLayout );
479  else
480  ImplDrawTextDirect( rSalLayout, mbTextLines );
481 }
482 
484  ImplMultiTextLineInfo& rLineInfo,
485  tools::Long nWidth, const OUString& rStr,
486  DrawTextFlags nStyle, const vcl::ITextLayout& _rLayout )
487 {
488  SAL_WARN_IF( nWidth <= 0, "vcl", "ImplGetTextLines: nWidth <= 0!" );
489 
490  if ( nWidth <= 0 )
491  nWidth = 1;
492 
493  rLineInfo.Clear();
494  if (rStr.isEmpty())
495  return 0;
496 
497  const bool bClipping = (nStyle & DrawTextFlags::Clip) && !(nStyle & DrawTextFlags::EndEllipsis);
498 
499  tools::Long nMaxLineWidth = 0;
500  const bool bHyphenate = (nStyle & DrawTextFlags::WordBreakHyphenation) == DrawTextFlags::WordBreakHyphenation;
501  css::uno::Reference< css::linguistic2::XHyphenator > xHyph;
502  if (bHyphenate)
503  {
504  // get service provider
505  css::uno::Reference<css::uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
506  css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLinguMgr = css::linguistic2::LinguServiceManager::create(xContext);
507  xHyph = xLinguMgr->getHyphenator();
508  }
509 
510  css::uno::Reference<css::i18n::XBreakIterator> xBI;
511  sal_Int32 nPos = 0;
512  sal_Int32 nLen = rStr.getLength();
513  sal_Int32 nCurrentTextY = 0;
514  while ( nPos < nLen )
515  {
516  sal_Int32 nBreakPos = nPos;
517 
518  while ( ( nBreakPos < nLen ) && ( rStr[ nBreakPos ] != '\r' ) && ( rStr[ nBreakPos ] != '\n' ) )
519  nBreakPos++;
520 
521  tools::Long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
522  if ( ( nLineWidth > nWidth ) && ( nStyle & DrawTextFlags::WordBreak ) )
523  {
524  if ( !xBI.is() )
526 
527  if ( xBI.is() )
528  {
529  nBreakPos = ImplBreakLinesWithIterator(nWidth, rStr, _rLayout, xHyph, xBI, bHyphenate, nPos, nBreakPos);
530  nLineWidth = _rLayout.GetTextWidth(rStr, nPos, nBreakPos - nPos);
531  }
532  else
533  // fallback to something really simple
534  nBreakPos = ImplBreakLinesSimple(nWidth, rStr, _rLayout, nPos, nBreakPos, nLineWidth);
535  }
536 
537  if ( nLineWidth > nMaxLineWidth )
538  nMaxLineWidth = nLineWidth;
539 
540  rLineInfo.AddLine( ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) );
541 
542  if ( nBreakPos == nPos )
543  nBreakPos++;
544  nPos = nBreakPos;
545 
546  if ( nPos < nLen && ( ( rStr[ nPos ] == '\r' ) || ( rStr[ nPos ] == '\n' ) ) )
547  {
548  nPos++;
549  // CR/LF?
550  if ( ( nPos < nLen ) && ( rStr[ nPos ] == '\n' ) && ( rStr[ nPos-1 ] == '\r' ) )
551  nPos++;
552  }
553  nCurrentTextY += nTextHeight;
554  if (bClipping && nCurrentTextY > rRect.GetHeight())
555  break;
556  }
557 
558 #ifdef DBG_UTIL
559  for ( sal_Int32 nL = 0; nL < rLineInfo.Count(); nL++ )
560  {
561  ImplTextLineInfo& rLine = rLineInfo.GetLine( nL );
562  OUString aLine = rStr.copy( rLine.GetIndex(), rLine.GetLen() );
563  SAL_WARN_IF( aLine.indexOf( '\r' ) != -1, "vcl", "ImplGetTextLines - Found CR!" );
564  SAL_WARN_IF( aLine.indexOf( '\n' ) != -1, "vcl", "ImplGetTextLines - Found LF!" );
565  }
566 #endif
567 
568  return nMaxLineWidth;
569 }
570 
571 sal_Int32 OutputDevice::ImplBreakLinesWithIterator(const tools::Long nWidth, const OUString& rStr, const vcl::ITextLayout& _rLayout,
572  const css::uno::Reference< css::linguistic2::XHyphenator >& xHyph,
573  const css::uno::Reference<css::i18n::XBreakIterator>& xBI,
574  const bool bHyphenate,
575  const sal_Int32 nPos, sal_Int32 nBreakPos)
576 {
577  const css::lang::Locale& rDefLocale(Application::GetSettings().GetUILanguageTag().getLocale());
578  sal_Int32 nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos );
579  if (nSoftBreak == -1)
580  {
581  nSoftBreak = nPos;
582  }
583  SAL_WARN_IF( nSoftBreak >= nBreakPos, "vcl", "Break?!" );
584  css::i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, css::uno::Sequence <css::beans::PropertyValue>(), 1 );
585  css::i18n::LineBreakUserOptions aUserOptions;
586  css::i18n::LineBreakResults aLBR = xBI->getLineBreak( rStr, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions );
587  nBreakPos = aLBR.breakIndex;
588  if ( nBreakPos <= nPos )
589  nBreakPos = nSoftBreak;
590  if ( !bHyphenate )
591  return nBreakPos;
592 
593  // Whether hyphen or not: Put the word after the hyphen through
594  // word boundary.
595 
596  // nMaxBreakPos the last char that fits into the line
597  // nBreakPos is the word's start
598 
599  // We run into a problem if the doc is so narrow, that a word
600  // is broken into more than two lines ...
601  if ( !xHyph.is() )
602  return nBreakPos;
603 
604  css::i18n::Boundary aBoundary = xBI->getWordBoundary( rStr, nBreakPos, rDefLocale, css::i18n::WordType::DICTIONARY_WORD, true );
605  sal_Int32 nWordStart = nPos;
606  sal_Int32 nWordEnd = aBoundary.endPos;
607  SAL_WARN_IF( nWordEnd <= nWordStart, "vcl", "ImpBreakLine: Start >= End?" );
608 
609  sal_Int32 nWordLen = nWordEnd - nWordStart;
610  if ( ( nWordEnd < nSoftBreak ) || ( nWordLen <= 3 ) )
611  return nBreakPos;
612 
613  // #104415# May happen, because getLineBreak may differ from getWordBoundary with DICTIONARY_WORD
614  // SAL_WARN_IF( nWordEnd < nMaxBreakPos, "vcl", "Hyph: Break?" );
615  OUString aWord = rStr.copy( nWordStart, nWordLen );
616  sal_Int32 nMinTrail = nWordEnd-nSoftBreak+1; //+1: Before the "broken off" char
617  css::uno::Reference< css::linguistic2::XHyphenatedWord > xHyphWord;
618  if (xHyph.is())
619  xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.getLength() - nMinTrail, css::uno::Sequence< css::beans::PropertyValue >() );
620  if (!xHyphWord.is())
621  return nBreakPos;
622 
623  bool bAlternate = xHyphWord->isAlternativeSpelling();
624  sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos();
625 
626  if ( ( _nWordLen < 2 ) || ( (nWordStart+_nWordLen) < 2 ) )
627  return nBreakPos;
628 
629  if ( bAlternate )
630  {
631  nBreakPos = nWordStart + _nWordLen;
632  return nBreakPos;
633  }
634 
635 
636  OUString aAlt( xHyphWord->getHyphenatedWord() );
637 
638  // We can have two cases:
639  // 1) "packen" turns into "pak-ken"
640  // 2) "Schiffahrt" turns into "Schiff-fahrt"
641 
642  // In case 1 we need to replace a char
643  // In case 2 we add a char
644 
645  // Correct recognition is made harder by words such as
646  // "Schiffahrtsbrennesseln", as the Hyphenator splits all
647  // positions of the word and comes up with "Schifffahrtsbrennnesseln"
648  // Thus, we cannot infer the aWord from the AlternativeWord's
649  // index.
650  // TODO: The whole junk will be made easier by a function in
651  // the Hyphenator, as soon as AMA adds it.
652  sal_Int32 nAltStart = _nWordLen - 1;
653  sal_Int32 nTxtStart = nAltStart - (aAlt.getLength() - aWord.getLength());
654  sal_Int32 nTxtEnd = nTxtStart;
655  sal_Int32 nAltEnd = nAltStart;
656 
657  // The area between nStart and nEnd is the difference
658  // between AlternativeString and OriginalString
659  while( nTxtEnd < aWord.getLength() && nAltEnd < aAlt.getLength() &&
660  aWord[nTxtEnd] != aAlt[nAltEnd] )
661  {
662  ++nTxtEnd;
663  ++nAltEnd;
664  }
665 
666  // If a char was added, we notice it now:
667  if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
668  aWord[ nTxtEnd ] == aAlt[nAltEnd] )
669  {
670  ++nAltEnd;
671  ++nTxtStart;
672  ++nTxtEnd;
673  }
674 
675  SAL_WARN_IF( ( nAltEnd - nAltStart ) != 1, "vcl", "Alternate: Wrong assumption!" );
676 
677  sal_Unicode cAlternateReplChar = 0;
678  if ( nTxtEnd > nTxtStart )
679  cAlternateReplChar = aAlt[ nAltStart ];
680 
681  nBreakPos = nWordStart + nTxtStart;
682  if ( cAlternateReplChar )
683  nBreakPos++;
684  return nBreakPos;
685 }
686 
687 sal_Int32 OutputDevice::ImplBreakLinesSimple( const tools::Long nWidth, const OUString& rStr,
688  const vcl::ITextLayout& _rLayout, const sal_Int32 nPos, sal_Int32 nBreakPos, tools::Long& nLineWidth )
689 {
690  sal_Int32 nSpacePos = rStr.getLength();
691  tools::Long nW = 0;
692  do
693  {
694  nSpacePos = rStr.lastIndexOf( ' ', nSpacePos );
695  if( nSpacePos != -1 )
696  {
697  if( nSpacePos > nPos )
698  nSpacePos--;
699  nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos );
700  }
701  } while( nW > nWidth );
702 
703  if( nSpacePos != -1 )
704  {
705  nBreakPos = nSpacePos;
706  nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
707  if( nBreakPos < rStr.getLength()-1 )
708  nBreakPos++;
709  }
710  return nBreakPos;
711 }
712 
713 
714 void OutputDevice::SetTextColor( const Color& rColor )
715 {
716 
717  Color aColor(vcl::drawmode::GetTextColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings()));
718 
719  if ( mpMetaFile )
720  mpMetaFile->AddAction( new MetaTextColorAction( aColor ) );
721 
722  if ( maTextColor != aColor )
723  {
724  maTextColor = aColor;
725  mbInitTextColor = true;
726  }
727 
728  if( mpAlphaVDev )
729  mpAlphaVDev->SetTextColor( COL_BLACK );
730 }
731 
732 void OutputDevice::SetTextFillColor()
733 {
734 
735  if ( mpMetaFile )
736  mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), false ) );
737 
738  if ( maFont.GetColor() != COL_TRANSPARENT ) {
739  maFont.SetFillColor( COL_TRANSPARENT );
740  }
741  if ( !maFont.IsTransparent() )
742  maFont.SetTransparent( true );
743 
744  if( mpAlphaVDev )
745  mpAlphaVDev->SetTextFillColor();
746 }
747 
748 void OutputDevice::SetTextFillColor( const Color& rColor )
749 {
750  Color aColor(vcl::drawmode::GetFillColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings()));
751 
752  if ( mpMetaFile )
753  mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, true ) );
754 
755  if ( maFont.GetFillColor() != aColor )
756  maFont.SetFillColor( aColor );
757  if ( maFont.IsTransparent() != rColor.IsTransparent() )
758  maFont.SetTransparent( rColor.IsTransparent() );
759 
760  if( mpAlphaVDev )
761  mpAlphaVDev->SetTextFillColor( COL_BLACK );
762 }
763 
764 Color OutputDevice::GetTextFillColor() const
765 {
766  if ( maFont.IsTransparent() )
767  return COL_TRANSPARENT;
768  else
769  return maFont.GetFillColor();
770 }
771 
772 void OutputDevice::SetTextAlign( TextAlign eAlign )
773 {
774 
775  if ( mpMetaFile )
776  mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) );
777 
778  if ( maFont.GetAlignment() != eAlign )
779  {
780  maFont.SetAlignment( eAlign );
781  mbNewFont = true;
782  }
783 
784  if( mpAlphaVDev )
785  mpAlphaVDev->SetTextAlign( eAlign );
786 }
787 
788 vcl::Region OutputDevice::GetOutputBoundsClipRegion() const
789 {
790  return GetClipRegion();
791 }
792 
793 const SalLayoutFlags eDefaultLayout = SalLayoutFlags::NONE;
794 
795 void OutputDevice::DrawText( const Point& rStartPt, const OUString& rStr,
796  sal_Int32 nIndex, sal_Int32 nLen,
797  std::vector< tools::Rectangle >* pVector, OUString* pDisplayText,
798  const SalLayoutGlyphs* pLayoutCache
799  )
800 {
801  assert(!is_double_buffered_window());
802 
803  if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
804  {
805  nLen = rStr.getLength() - nIndex;
806  }
807 
808  if (mpOutDevData->mpRecordLayout)
809  {
810  pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
811  pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
812  }
813 
814 #if OSL_DEBUG_LEVEL > 2
815  SAL_INFO("vcl.gdi", "OutputDevice::DrawText(\"" << rStr << "\")");
816 #endif
817 
818  if ( mpMetaFile )
819  mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
820  if( pVector )
821  {
823 
824  if (mpOutDevData->mpRecordLayout)
825  {
826  mpOutDevData->mpRecordLayout->m_aLineIndices.push_back( mpOutDevData->mpRecordLayout->m_aDisplayText.getLength() );
827  aClip.Intersect( mpOutDevData->maRecordRect );
828  }
829  if( ! aClip.IsNull() )
830  {
831  std::vector< tools::Rectangle > aTmp;
832  GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, aTmp );
833 
834  bool bInserted = false;
835  for( std::vector< tools::Rectangle >::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ )
836  {
837  bool bAppend = false;
838 
839  if( aClip.Overlaps( *it ) )
840  bAppend = true;
841  else if( rStr[ nIndex ] == ' ' && bInserted )
842  {
843  std::vector< tools::Rectangle >::const_iterator next = it;
844  ++next;
845  if( next != aTmp.end() && aClip.Overlaps( *next ) )
846  bAppend = true;
847  }
848 
849  if( bAppend )
850  {
851  pVector->push_back( *it );
852  if( pDisplayText )
853  *pDisplayText += OUStringChar(rStr[ nIndex ]);
854  bInserted = true;
855  }
856  }
857  }
858  else
859  {
860  GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, *pVector );
861  if( pDisplayText )
862  *pDisplayText += rStr.subView( nIndex, nLen );
863  }
864  }
865 
866  if ( !IsDeviceOutputNecessary() || pVector )
867  return;
868 
869  if(mpFontInstance)
870  // do not use cache with modified string
871  if(mpFontInstance->mpConversion)
872  pLayoutCache = nullptr;
873 
874  std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, {}, eDefaultLayout, nullptr, pLayoutCache);
875  if(pSalLayout)
876  {
877  ImplDrawText( *pSalLayout );
878  }
879 
880  if( mpAlphaVDev )
881  mpAlphaVDev->DrawText( rStartPt, rStr, nIndex, nLen, pVector, pDisplayText );
882 }
883 
884 tools::Long OutputDevice::GetTextWidth( const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen,
885  vcl::text::TextLayoutCache const*const pLayoutCache,
886  SalLayoutGlyphs const*const pSalLayoutCache) const
887 {
888 
889  tools::Long nWidth = GetTextArray( rStr, nullptr, nIndex,
890  nLen, pLayoutCache, pSalLayoutCache );
891 
892  return nWidth;
893 }
894 
896 {
897  if (!InitFont())
898  return 0;
899 
900  tools::Long nHeight = mpFontInstance->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
901 
902  if ( mbMap )
903  nHeight = ImplDevicePixelToLogicHeight( nHeight );
904 
905  return nHeight;
906 }
907 
909 {
910  //note pango uses "The quick brown fox jumps over the lazy dog." for english
911  //and has a bunch of per-language strings which corresponds somewhat with
912  //makeRepresentativeText in include/svtools/sampletext.hxx
913  return GetTextWidth("aemnnxEM") / 8.0;
914 }
915 
917 {
918  return GetTextWidth("0123456789") / 10.0;
919 }
920 
921 void OutputDevice::DrawTextArray( const Point& rStartPt, const OUString& rStr,
923  sal_Int32 nIndex, sal_Int32 nLen, SalLayoutFlags flags,
924  const SalLayoutGlyphs* pSalLayoutCache )
925 {
926  assert(!is_double_buffered_window());
927 
928  if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
929  {
930  nLen = rStr.getLength() - nIndex;
931  }
932  if ( mpMetaFile )
933  mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
934 
935  if ( !IsDeviceOutputNecessary() )
936  return;
937  if( !mpGraphics && !AcquireGraphics() )
938  return;
939  assert(mpGraphics);
940  if( mbInitClipRegion )
941  InitClipRegion();
942  if( mbOutputClipped )
943  return;
944 
945  std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, pDXAry, flags, nullptr, pSalLayoutCache);
946  if( pSalLayout )
947  {
948  ImplDrawText( *pSalLayout );
949  }
950 
951  if( mpAlphaVDev )
952  mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen, flags );
953 }
954 
955 tools::Long OutputDevice::GetTextArray( const OUString& rStr, std::vector<sal_Int32>* pDXAry,
956  sal_Int32 nIndex, sal_Int32 nLen,
957  vcl::text::TextLayoutCache const*const pLayoutCache,
958  SalLayoutGlyphs const*const pSalLayoutCache) const
959 {
960  if( nIndex >= rStr.getLength() )
961  return 0; // TODO: this looks like a buggy caller?
962 
963  if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
964  {
965  nLen = rStr.getLength() - nIndex;
966  }
967 
968  // do layout
969  std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen,
970  Point(0,0), 0, {}, eDefaultLayout, pLayoutCache, pSalLayoutCache);
971  if( !pSalLayout )
972  {
973  // The caller expects this to init the elements of pDXAry.
974  // Adapting all the callers to check that GetTextArray succeeded seems
975  // too much work.
976  // Init here to 0 only in the (rare) error case, so that any missing
977  // element init in the happy case will still be found by tools,
978  // and hope that is sufficient.
979  if (pDXAry)
980  std::fill(pDXAry->begin(), pDXAry->end(), 0);
981  return 0;
982  }
983 
984 #if VCL_FLOAT_DEVICE_PIXEL
985  std::unique_ptr<std::vector<DeviceCoordinate>> xDXPixelArray;
986  if(pDXAry)
987  {
988  xDXPixelArray.reset(new std::vector<DeviceCoordinate>(nLen));
989  }
990  std::vector<DeviceCoordinate>* pDXPixelArray = xDXPixelArray.get();
991  DeviceCoordinate nWidth = pSalLayout->FillDXArray(pDXPixelArray);
992  int nWidthFactor = pSalLayout->GetUnitsPerPixel();
993 
994  // convert virtual char widths to virtual absolute positions
995  if( pDXPixelArray )
996  {
997  for( int i = 1; i < nLen; ++i )
998  {
999  (*pDXPixelArray)[i] += (*pDXPixelArray)[i - 1];
1000  }
1001  }
1002  if( mbMap )
1003  {
1004  if( pDXPixelArray )
1005  {
1006  for( int i = 0; i < nLen; ++i )
1007  {
1008  (*pDXPixelArray)[i] = ImplDevicePixelToLogicWidth((*pDXPixelArray)[i]);
1009  }
1010  }
1011  nWidth = ImplDevicePixelToLogicWidth( nWidth );
1012  }
1013  if( nWidthFactor > 1 )
1014  {
1015  if( pDXPixelArray )
1016  {
1017  for( int i = 0; i < nLen; ++i )
1018  {
1019  (*pDXPixelArray)[i] /= nWidthFactor;
1020  }
1021  }
1022  nWidth /= nWidthFactor;
1023  }
1024  if(pDXAry)
1025  {
1026  pDXAry->resize(nLen);
1027  for( int i = 0; i < nLen; ++i )
1028  {
1029  (*pDXAry)[i] = basegfx::fround((*pDXPixelArray)[i]);
1030  }
1031  }
1032  return basegfx::fround(nWidth);
1033 
1034 #else /* ! VCL_FLOAT_DEVICE_PIXEL */
1035 
1036  tools::Long nWidth = pSalLayout->FillDXArray( pDXAry );
1037  int nWidthFactor = pSalLayout->GetUnitsPerPixel();
1038 
1039  // convert virtual char widths to virtual absolute positions
1040  if( pDXAry )
1041  for( int i = 1; i < nLen; ++i )
1042  (*pDXAry)[ i ] += (*pDXAry)[ i-1 ];
1043 
1044  // convert from font units to logical units
1045  if( mbMap )
1046  {
1047  if( pDXAry )
1048  for( int i = 0; i < nLen; ++i )
1049  (*pDXAry)[i] = ImplDevicePixelToLogicWidth( (*pDXAry)[i] );
1050  nWidth = ImplDevicePixelToLogicWidth( nWidth );
1051  }
1052 
1053  if( nWidthFactor > 1 )
1054  {
1055  if( pDXAry )
1056  for( int i = 0; i < nLen; ++i )
1057  (*pDXAry)[i] /= nWidthFactor;
1058  nWidth /= nWidthFactor;
1059  }
1060  return nWidth;
1061 #endif /* VCL_FLOAT_DEVICE_PIXEL */
1062 }
1063 
1064 void OutputDevice::GetCaretPositions( const OUString& rStr, sal_Int32* pCaretXArray,
1065  sal_Int32 nIndex, sal_Int32 nLen,
1066  const SalLayoutGlyphs* pGlyphs ) const
1067 {
1068 
1069  if( nIndex >= rStr.getLength() )
1070  return;
1071  if( nIndex+nLen >= rStr.getLength() )
1072  nLen = rStr.getLength() - nIndex;
1073 
1074  // layout complex text
1075  std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, Point(0, 0), 0, {},
1076  eDefaultLayout, nullptr, pGlyphs);
1077  if( !pSalLayout )
1078  {
1079  std::fill(pCaretXArray, pCaretXArray + nLen * 2, -1);
1080  return;
1081  }
1082 
1083  int nWidthFactor = pSalLayout->GetUnitsPerPixel();
1084  pSalLayout->GetCaretPositions( 2*nLen, pCaretXArray );
1085  tools::Long nWidth = pSalLayout->GetTextWidth();
1086 
1087  // fixup unknown caret positions
1088  int i;
1089  for( i = 0; i < 2 * nLen; ++i )
1090  if( pCaretXArray[ i ] >= 0 )
1091  break;
1092  tools::Long nXPos = (i < 2 * nLen) ? pCaretXArray[i] : -1;
1093  for( i = 0; i < 2 * nLen; ++i )
1094  {
1095  if( pCaretXArray[ i ] >= 0 )
1096  nXPos = pCaretXArray[ i ];
1097  else
1098  pCaretXArray[ i ] = nXPos;
1099  }
1100 
1101  // handle window mirroring
1102  if( IsRTLEnabled() )
1103  {
1104  for( i = 0; i < 2 * nLen; ++i )
1105  pCaretXArray[i] = nWidth - pCaretXArray[i] - 1;
1106  }
1107 
1108  // convert from font units to logical units
1109  if( mbMap )
1110  {
1111  for( i = 0; i < 2*nLen; ++i )
1112  pCaretXArray[i] = ImplDevicePixelToLogicWidth( pCaretXArray[i] );
1113  }
1114 
1115  if( nWidthFactor != 1 )
1116  {
1117  for( i = 0; i < 2*nLen; ++i )
1118  pCaretXArray[i] /= nWidthFactor;
1119  }
1120 }
1121 
1122 void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth,
1123  const OUString& rStr,
1124  sal_Int32 nIndex, sal_Int32 nLen)
1125 {
1126  assert(!is_double_buffered_window());
1127 
1128  if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
1129  {
1130  nLen = rStr.getLength() - nIndex;
1131  }
1132 
1133  if ( mpMetaFile )
1134  mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) );
1135 
1136  if ( !IsDeviceOutputNecessary() )
1137  return;
1138 
1139  std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, nWidth);
1140  if( pSalLayout )
1141  {
1142  ImplDrawText( *pSalLayout );
1143  }
1144 
1145  if( mpAlphaVDev )
1146  mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen );
1147 }
1148 
1150  const sal_Int32 nMinIndex, const sal_Int32 nLen,
1151  DeviceCoordinate nPixelWidth,
1152  SalLayoutFlags nLayoutFlags,
1153  vcl::text::TextLayoutCache const*const pLayoutCache) const
1154 {
1155  assert(nMinIndex >= 0);
1156  assert(nLen >= 0);
1157 
1158  // get string length for calculating extents
1159  sal_Int32 nEndIndex = rStr.getLength();
1160  if( nMinIndex + nLen < nEndIndex )
1161  nEndIndex = nMinIndex + nLen;
1162 
1163  // don't bother if there is nothing to do
1164  if( nEndIndex < nMinIndex )
1165  nEndIndex = nMinIndex;
1166 
1167  nLayoutFlags |= GetBiDiLayoutFlags( rStr, nMinIndex, nEndIndex );
1168 
1169  if( !maFont.IsKerning() )
1170  nLayoutFlags |= SalLayoutFlags::DisableKerning;
1172  nLayoutFlags |= SalLayoutFlags::KerningAsian;
1173  if( maFont.IsVertical() )
1174  nLayoutFlags |= SalLayoutFlags::Vertical;
1175 
1176  if( meTextLanguage ) //TODO: (mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::SubstituteDigits)
1177  {
1178  // disable character localization when no digits used
1179  const sal_Unicode* pBase = rStr.getStr();
1180  const sal_Unicode* pStr = pBase + nMinIndex;
1181  const sal_Unicode* pEnd = pBase + nEndIndex;
1182  std::optional<OUStringBuffer> xTmpStr;
1183  for( ; pStr < pEnd; ++pStr )
1184  {
1185  // TODO: are there non-digit localizations?
1186  if( (*pStr >= '0') && (*pStr <= '9') )
1187  {
1188  // translate characters to local preference
1189  sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
1190  if( cChar != *pStr )
1191  {
1192  if (!xTmpStr)
1193  xTmpStr = OUStringBuffer(rStr);
1194  // TODO: are the localized digit surrogates?
1195  (*xTmpStr)[pStr - pBase] = cChar;
1196  }
1197  }
1198  }
1199  if (xTmpStr)
1200  rStr = (*xTmpStr).makeStringAndClear();
1201  }
1202 
1203  // right align for RTL text, DRAWPOS_REVERSED, RTL window style
1206  bRightAlign = false;
1208  bRightAlign = true;
1209  // SSA: hack for western office, ie text get right aligned
1210  // for debugging purposes of mirrored UI
1211  bool bRTLWindow = IsRTLEnabled();
1212  bRightAlign ^= bRTLWindow;
1213  if( bRightAlign )
1214  nLayoutFlags |= SalLayoutFlags::RightAlign;
1215 
1216  // set layout options
1217  vcl::text::ImplLayoutArgs aLayoutArgs(rStr, nMinIndex, nEndIndex, nLayoutFlags, maFont.GetLanguageTag(), pLayoutCache);
1218 
1219  Degree10 nOrientation = mpFontInstance ? mpFontInstance->mnOrientation : 0_deg10;
1220  aLayoutArgs.SetOrientation( nOrientation );
1221 
1222  aLayoutArgs.SetLayoutWidth( nPixelWidth );
1223 
1224  return aLayoutArgs;
1225 }
1226 
1228  const sal_Int32 nMinIndex,
1229  const sal_Int32 nEndIndex ) const
1230 {
1231  SalLayoutFlags nLayoutFlags = SalLayoutFlags::NONE;
1233  nLayoutFlags |= SalLayoutFlags::BiDiRtl;
1235  nLayoutFlags |= SalLayoutFlags::BiDiStrong;
1236  else if( !(mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::BiDiRtl) )
1237  {
1238  // Disable Bidi if no RTL hint and only known LTR codes used.
1239  bool bAllLtr = true;
1240  for (sal_Int32 i = nMinIndex; i < nEndIndex; i++)
1241  {
1242  // [0x0000, 0x052F] are Latin, Greek and Cyrillic.
1243  // [0x0370, 0x03FF] has a few holes as if Unicode 10.0.0, but
1244  // hopefully no RTL character will be encoded there.
1245  if (rStr[i] > 0x052F)
1246  {
1247  bAllLtr = false;
1248  break;
1249  }
1250  }
1251  if (bAllLtr)
1252  nLayoutFlags |= SalLayoutFlags::BiDiStrong;
1253  }
1254  return nLayoutFlags;
1255 }
1256 
1258 
1259 static inline bool IsTrackingFontMappingUse()
1260 {
1261  return fontMappingUseData != nullptr;
1262 }
1263 
1264 static void TrackFontMappingUse( const vcl::Font& originalFont, const SalLayout* salLayout)
1265 {
1266  assert(fontMappingUseData);
1267  OUString originalName = originalFont.GetStyleName().isEmpty()
1268  ? originalFont.GetFamilyName()
1269  : originalFont.GetFamilyName() + "/" + originalFont.GetStyleName();
1270  std::vector<OUString> usedFontNames;
1271  SalLayoutGlyphs glyphs = salLayout->GetGlyphs(); // includes all font fallbacks
1272  int level = 0;
1273  while( const SalLayoutGlyphsImpl* impl = glyphs.Impl(level++))
1274  {
1275  const vcl::font::PhysicalFontFace* face = impl->GetFont()->GetFontFace();
1276  OUString name = face->GetStyleName().isEmpty()
1277  ? face->GetFamilyName()
1278  : face->GetFamilyName() + "/" + face->GetStyleName();
1279  usedFontNames.push_back( name );
1280  }
1281  for( OutputDevice::FontMappingUseItem& item : *fontMappingUseData )
1282  {
1283  if( item.mOriginalFont == originalName && item.mUsedFonts == usedFontNames )
1284  {
1285  ++item.mCount;
1286  return;
1287  }
1288  }
1289  fontMappingUseData->push_back( { originalName, usedFontNames, 1 } );
1290 }
1291 
1293 {
1294  delete fontMappingUseData;
1295  fontMappingUseData = new FontMappingUseData;
1296 }
1297 
1299 {
1300  if(!fontMappingUseData)
1301  return {};
1302  FontMappingUseData ret = std::move( *fontMappingUseData );
1303  delete fontMappingUseData;
1304  fontMappingUseData = nullptr;
1305  return ret;
1306 }
1307 
1308 std::unique_ptr<SalLayout> OutputDevice::ImplLayout(const OUString& rOrigStr,
1309  sal_Int32 nMinIndex, sal_Int32 nLen,
1310  const Point& rLogicalPos, tools::Long nLogicalWidth,
1312  vcl::text::TextLayoutCache const* pLayoutCache,
1313  const SalLayoutGlyphs* pGlyphs) const
1314 {
1315  if (pGlyphs && !pGlyphs->IsValid())
1316  {
1317  SAL_WARN("vcl", "Trying to setup invalid cached glyphs - falling back to relayout!");
1318  pGlyphs = nullptr;
1319  }
1320 #ifdef DBG_UTIL
1321  if (pGlyphs)
1322  {
1323  for( int level = 0;; ++level )
1324  {
1325  SalLayoutGlyphsImpl* glyphsImpl = pGlyphs->Impl(level);
1326  if(glyphsImpl == nullptr)
1327  break;
1328  // It is allowed to reuse only glyphs created with SalLayoutFlags::GlyphItemsOnly.
1329  // If the glyphs have already been used, the AdjustLayout() call below might have
1330  // altered them (MultiSalLayout::ImplAdjustMultiLayout() drops glyphs that need
1331  // fallback from the base layout, but then GenericSalLayout::LayoutText()
1332  // would not know to call SetNeedFallback()).
1333  assert(glyphsImpl->GetFlags() & SalLayoutFlags::GlyphItemsOnly);
1334  }
1335  }
1336 #endif
1337 
1338  if (!InitFont())
1339  return nullptr;
1340 
1341  // check string index and length
1342  if( -1 == nLen || nMinIndex + nLen > rOrigStr.getLength() )
1343  {
1344  const sal_Int32 nNewLen = rOrigStr.getLength() - nMinIndex;
1345  if( nNewLen <= 0 )
1346  return nullptr;
1347  nLen = nNewLen;
1348  }
1349 
1350  OUString aStr = rOrigStr;
1351 
1352  // convert from logical units to physical units
1353  // recode string if needed
1354  if( mpFontInstance->mpConversion ) {
1355  mpFontInstance->mpConversion->RecodeString( aStr, 0, aStr.getLength() );
1356  pLayoutCache = nullptr; // don't use cache with modified string!
1357  pGlyphs = nullptr;
1358  }
1359 
1360  DeviceCoordinate nPixelWidth = static_cast<DeviceCoordinate>(nLogicalWidth);
1361  if( nLogicalWidth && mbMap )
1362  {
1363  nPixelWidth = LogicWidthToDeviceCoordinate( nLogicalWidth );
1364  }
1365 
1366  vcl::text::ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen,
1367  nPixelWidth, flags, pLayoutCache);
1368 
1369  bool bTextRenderModeForResolutionIndependentLayout(false);
1370  DeviceCoordinate nEndGlyphCoord(0);
1371  std::unique_ptr<DeviceCoordinate[]> xDXPixelArray;
1372  std::unique_ptr<double[]> xNaturalDXPixelArray;
1373  if( !pDXArray.empty() )
1374  {
1375  DeviceCoordinate* pDXPixelArray(nullptr);
1376  if (mbMap)
1377  {
1379  {
1380  bTextRenderModeForResolutionIndependentLayout = true;
1381 
1382  // convert from logical units to font units using a temporary array
1383  xNaturalDXPixelArray.reset(new double[nLen]);
1384 
1385  for (int i = 0; i < nLen; ++i)
1386  xNaturalDXPixelArray[i] = ImplLogicWidthToDeviceFontWidth(pDXArray[i]);
1387 
1388  aLayoutArgs.SetAltNaturalDXArray(xNaturalDXPixelArray.get());
1389  nEndGlyphCoord = std::lround(xNaturalDXPixelArray[nLen - 1]);
1390  }
1391  else
1392  {
1393  // convert from logical units to font units using a temporary array
1394  xDXPixelArray.reset(new DeviceCoordinate[nLen]);
1395  pDXPixelArray = xDXPixelArray.get();
1396  // using base position for better rounding a.k.a. "dancing characters"
1397  DeviceCoordinate nPixelXOfs2 = LogicWidthToDeviceCoordinate(rLogicalPos.X() * 2);
1398  for( int i = 0; i < nLen; ++i )
1399  {
1400  pDXPixelArray[i] = (LogicWidthToDeviceCoordinate((rLogicalPos.X() + pDXArray[i]) * 2) - nPixelXOfs2) / 2;
1401  }
1402 
1403  aLayoutArgs.SetDXArray(pDXPixelArray);
1404  nEndGlyphCoord = pDXPixelArray[nLen - 1];
1405  }
1406 
1407  }
1408  else
1409  {
1410 #if VCL_FLOAT_DEVICE_PIXEL
1411  xDXPixelArray.reset(new DeviceCoordinate[nLen]);
1412  pDXPixelArray = xDXPixelArray.get();
1413  for( int i = 0; i < nLen; ++i )
1414  {
1415  pDXPixelArray[i] = pDXArray[i];
1416  }
1417 #else /* !VCL_FLOAT_DEVICE_PIXEL */
1418  pDXPixelArray = const_cast<DeviceCoordinate*>(pDXArray.data());
1419 #endif /* !VCL_FLOAT_DEVICE_PIXEL */
1420 
1421  aLayoutArgs.SetDXArray(pDXPixelArray);
1422  nEndGlyphCoord = pDXPixelArray[nLen - 1];
1423  }
1424  }
1425 
1426  // get matching layout object for base font
1427  std::unique_ptr<SalLayout> pSalLayout = mpGraphics->GetTextLayout(0);
1428 
1429  // layout text
1430  if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs, pGlyphs ? pGlyphs->Impl(0) : nullptr ) )
1431  {
1432  pSalLayout.reset();
1433  }
1434 
1435  if( !pSalLayout )
1436  return nullptr;
1437 
1438  pSalLayout->SetTextRenderModeForResolutionIndependentLayout(bTextRenderModeForResolutionIndependentLayout);
1439 
1440  // do glyph fallback if needed
1441  // #105768# avoid fallback for very small font sizes
1442  if (aLayoutArgs.HasFallbackRun() && mpFontInstance->GetFontSelectPattern().mnHeight >= 3)
1443  pSalLayout = ImplGlyphFallbackLayout(std::move(pSalLayout), aLayoutArgs, pGlyphs);
1444 
1445  if (flags & SalLayoutFlags::GlyphItemsOnly)
1446  // Return glyph items only after fallback handling. Otherwise they may
1447  // contain invalid glyph IDs.
1448  return pSalLayout;
1449 
1450  // position, justify, etc. the layout
1451  pSalLayout->AdjustLayout( aLayoutArgs );
1452 
1453  if (bTextRenderModeForResolutionIndependentLayout)
1454  pSalLayout->DrawBase() = ImplLogicToDeviceFontCoordinate(rLogicalPos);
1455  else
1456  {
1457  Point aDevicePos = ImplLogicToDevicePixel(rLogicalPos);
1458  pSalLayout->DrawBase() = DevicePoint(aDevicePos.X(), aDevicePos.Y());
1459  }
1460 
1461  // adjust to right alignment if necessary
1462  if( aLayoutArgs.mnFlags & SalLayoutFlags::RightAlign )
1463  {
1464  DeviceCoordinate nRTLOffset;
1465  if (!pDXArray.empty())
1466  nRTLOffset = nEndGlyphCoord;
1467  else if( nPixelWidth )
1468  nRTLOffset = nPixelWidth;
1469  else
1470  nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
1471  pSalLayout->DrawOffset().setX( 1 - nRTLOffset );
1472  }
1473 
1475  TrackFontMappingUse(GetFont(), pSalLayout.get());
1476 
1477  return pSalLayout;
1478 }
1479 
1480 std::shared_ptr<const vcl::text::TextLayoutCache> OutputDevice::CreateTextLayoutCache(
1481  OUString const& rString)
1482 {
1483  return vcl::text::TextLayoutCache::Create(rString);
1484 }
1485 
1486 bool OutputDevice::GetTextIsRTL( const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen ) const
1487 {
1488  OUString aStr( rString );
1489  vcl::text::ImplLayoutArgs aArgs = ImplPrepareLayoutArgs(aStr, nIndex, nLen, 0);
1490  bool bRTL = false;
1491  int nCharPos = -1;
1492  if (!aArgs.GetNextPos(&nCharPos, &bRTL))
1493  return false;
1494  return (nCharPos != nIndex);
1495 }
1496 
1497 sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, tools::Long nTextWidth,
1498  sal_Int32 nIndex, sal_Int32 nLen,
1499  tools::Long nCharExtra,
1500  vcl::text::TextLayoutCache const*const pLayoutCache,
1501  const SalLayoutGlyphs* pGlyphs) const
1502 {
1503  std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rStr, nIndex, nLen,
1504  Point(0,0), 0, {}, eDefaultLayout, pLayoutCache, pGlyphs);
1505  sal_Int32 nRetVal = -1;
1506  if( pSalLayout )
1507  {
1508  // convert logical widths into layout units
1509  // NOTE: be very careful to avoid rounding errors for nCharExtra case
1510  // problem with rounding errors especially for small nCharExtras
1511  // TODO: remove when layout units have subpixel granularity
1512  tools::Long nWidthFactor = pSalLayout->GetUnitsPerPixel();
1513  tools::Long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
1514  nTextWidth *= nWidthFactor * nSubPixelFactor;
1515  DeviceCoordinate nTextPixelWidth = LogicWidthToDeviceCoordinate( nTextWidth );
1516  DeviceCoordinate nExtraPixelWidth = 0;
1517  if( nCharExtra != 0 )
1518  {
1519  nCharExtra *= nWidthFactor * nSubPixelFactor;
1520  nExtraPixelWidth = LogicWidthToDeviceCoordinate( nCharExtra );
1521  }
1522  nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
1523  }
1524 
1525  return nRetVal;
1526 }
1527 
1528 sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, tools::Long nTextWidth,
1529  sal_Unicode nHyphenChar, sal_Int32& rHyphenPos,
1530  sal_Int32 nIndex, sal_Int32 nLen,
1531  tools::Long nCharExtra,
1532  vcl::text::TextLayoutCache const*const pLayoutCache,
1533  const SalLayoutGlyphs* pGlyphs) const
1534 {
1535  rHyphenPos = -1;
1536 
1537  std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rStr, nIndex, nLen,
1538  Point(0,0), 0, {}, eDefaultLayout, pLayoutCache, pGlyphs);
1539  sal_Int32 nRetVal = -1;
1540  if( pSalLayout )
1541  {
1542  // convert logical widths into layout units
1543  // NOTE: be very careful to avoid rounding errors for nCharExtra case
1544  // problem with rounding errors especially for small nCharExtras
1545  // TODO: remove when layout units have subpixel granularity
1546  tools::Long nWidthFactor = pSalLayout->GetUnitsPerPixel();
1547  tools::Long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
1548 
1549  nTextWidth *= nWidthFactor * nSubPixelFactor;
1550  DeviceCoordinate nTextPixelWidth = LogicWidthToDeviceCoordinate( nTextWidth );
1551  DeviceCoordinate nExtraPixelWidth = 0;
1552  if( nCharExtra != 0 )
1553  {
1554  nCharExtra *= nWidthFactor * nSubPixelFactor;
1555  nExtraPixelWidth = LogicWidthToDeviceCoordinate( nCharExtra );
1556  }
1557 
1558  // calculate un-hyphenated break position
1559  nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
1560 
1561  // calculate hyphenated break position
1562  OUString aHyphenStr(nHyphenChar);
1563  std::unique_ptr<SalLayout> pHyphenLayout = ImplLayout( aHyphenStr, 0, 1 );
1564  if( pHyphenLayout )
1565  {
1566  // calculate subpixel width of hyphenation character
1567  tools::Long nHyphenPixelWidth = pHyphenLayout->GetTextWidth() * nSubPixelFactor;
1568 
1569  // calculate hyphenated break position
1570  nTextPixelWidth -= nHyphenPixelWidth;
1571  if( nExtraPixelWidth > 0 )
1572  nTextPixelWidth -= nExtraPixelWidth;
1573 
1574  rHyphenPos = pSalLayout->GetTextBreak(nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor);
1575 
1576  if( rHyphenPos > nRetVal )
1577  rHyphenPos = nRetVal;
1578  }
1579  }
1580 
1581  return nRetVal;
1582 }
1583 
1584 void OutputDevice::ImplDrawText( OutputDevice& rTargetDevice, const tools::Rectangle& rRect,
1585  const OUString& rOrigStr, DrawTextFlags nStyle,
1586  std::vector< tools::Rectangle >* pVector, OUString* pDisplayText,
1587  vcl::ITextLayout& _rLayout )
1588 {
1589 
1590  Color aOldTextColor;
1591  Color aOldTextFillColor;
1592  bool bRestoreFillColor = false;
1593  if ( (nStyle & DrawTextFlags::Disable) && ! pVector )
1594  {
1595  bool bHighContrastBlack = false;
1596  bool bHighContrastWhite = false;
1597  const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() );
1598  if( rStyleSettings.GetHighContrastMode() )
1599  {
1600  Color aCol;
1601  if( rTargetDevice.IsBackground() )
1602  aCol = rTargetDevice.GetBackground().GetColor();
1603  else
1604  // best guess is the face color here
1605  // but it may be totally wrong. the background color
1606  // was typically already reset
1607  aCol = rStyleSettings.GetFaceColor();
1608 
1609  bHighContrastBlack = aCol.IsDark();
1610  bHighContrastWhite = aCol.IsBright();
1611  }
1612 
1613  aOldTextColor = rTargetDevice.GetTextColor();
1614  if ( rTargetDevice.IsTextFillColor() )
1615  {
1616  bRestoreFillColor = true;
1617  aOldTextFillColor = rTargetDevice.GetTextFillColor();
1618  }
1619  if( bHighContrastBlack )
1620  rTargetDevice.SetTextColor( COL_GREEN );
1621  else if( bHighContrastWhite )
1622  rTargetDevice.SetTextColor( COL_LIGHTGREEN );
1623  else
1624  {
1625  // draw disabled text always without shadow
1626  // as it fits better with native look
1627  rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() );
1628  }
1629  }
1630 
1631  tools::Long nWidth = rRect.GetWidth();
1632  tools::Long nHeight = rRect.GetHeight();
1633 
1634  if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & DrawTextFlags::Clip) )
1635  return;
1636 
1637  Point aPos = rRect.TopLeft();
1638 
1639  tools::Long nTextHeight = rTargetDevice.GetTextHeight();
1640  TextAlign eAlign = rTargetDevice.GetTextAlign();
1641  sal_Int32 nMnemonicPos = -1;
1642 
1643  OUString aStr = rOrigStr;
1644  if ( nStyle & DrawTextFlags::Mnemonic )
1645  aStr = GetNonMnemonicString( aStr, nMnemonicPos );
1646 
1647  const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics) && !pVector;
1648 
1649  // We treat multiline text differently
1650  if ( nStyle & DrawTextFlags::MultiLine )
1651  {
1652 
1653  ImplMultiTextLineInfo aMultiLineInfo;
1654  sal_Int32 i;
1655  sal_Int32 nFormatLines;
1656 
1657  if ( nTextHeight )
1658  {
1659  tools::Long nMaxTextWidth = ImplGetTextLines( rRect, nTextHeight, aMultiLineInfo, nWidth, aStr, nStyle, _rLayout );
1660  sal_Int32 nLines = static_cast<sal_Int32>(nHeight/nTextHeight);
1661  OUString aLastLine;
1662  nFormatLines = aMultiLineInfo.Count();
1663  if (nLines <= 0)
1664  nLines = 1;
1665  if ( nFormatLines > nLines )
1666  {
1667  if ( nStyle & DrawTextFlags::EndEllipsis )
1668  {
1669  // Create last line and shorten it
1670  nFormatLines = nLines-1;
1671 
1672  ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( nFormatLines );
1673  aLastLine = convertLineEnd(aStr.copy(rLineInfo.GetIndex()), LINEEND_LF);
1674  // Replace all LineFeeds with Spaces
1675  OUStringBuffer aLastLineBuffer(aLastLine);
1676  sal_Int32 nLastLineLen = aLastLineBuffer.getLength();
1677  for ( i = 0; i < nLastLineLen; i++ )
1678  {
1679  if ( aLastLineBuffer[ i ] == '\n' )
1680  aLastLineBuffer[ i ] = ' ';
1681  }
1682  aLastLine = aLastLineBuffer.makeStringAndClear();
1683  aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout );
1685  nStyle |= DrawTextFlags::Top;
1686  }
1687  }
1688  else
1689  {
1690  if ( nMaxTextWidth <= nWidth )
1691  nStyle &= ~DrawTextFlags::Clip;
1692  }
1693 
1694  // Do we need to clip the height?
1695  if ( nFormatLines*nTextHeight > nHeight )
1696  nStyle |= DrawTextFlags::Clip;
1697 
1698  // Set clipping
1699  if ( nStyle & DrawTextFlags::Clip )
1700  {
1701  rTargetDevice.Push( vcl::PushFlags::CLIPREGION );
1702  rTargetDevice.IntersectClipRegion( rRect );
1703  }
1704 
1705  // Vertical alignment
1706  if ( nStyle & DrawTextFlags::Bottom )
1707  aPos.AdjustY(nHeight-(nFormatLines*nTextHeight) );
1708  else if ( nStyle & DrawTextFlags::VCenter )
1709  aPos.AdjustY((nHeight-(nFormatLines*nTextHeight))/2 );
1710 
1711  // Font alignment
1712  if ( eAlign == ALIGN_BOTTOM )
1713  aPos.AdjustY(nTextHeight );
1714  else if ( eAlign == ALIGN_BASELINE )
1715  aPos.AdjustY(rTargetDevice.GetFontMetric().GetAscent() );
1716 
1717  // Output all lines except for the last one
1718  for ( i = 0; i < nFormatLines; i++ )
1719  {
1720  ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( i );
1721  if ( nStyle & DrawTextFlags::Right )
1722  aPos.AdjustX(nWidth-rLineInfo.GetWidth() );
1723  else if ( nStyle & DrawTextFlags::Center )
1724  aPos.AdjustX((nWidth-rLineInfo.GetWidth())/2 );
1725  sal_Int32 nIndex = rLineInfo.GetIndex();
1726  sal_Int32 nLineLen = rLineInfo.GetLen();
1727  _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
1728  if ( bDrawMnemonics )
1729  {
1730  if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
1731  {
1732  tools::Long nMnemonicX;
1733  tools::Long nMnemonicY;
1734  DeviceCoordinate nMnemonicWidth;
1735 
1736  std::unique_ptr<sal_Int32[]> const pCaretXArray(new sal_Int32[2 * nLineLen]);
1737  /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray.get(),
1738  nIndex, nLineLen );
1739  sal_Int32 lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
1740  sal_Int32 lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
1741  nMnemonicWidth = rTargetDevice.LogicWidthToDeviceCoordinate( std::abs(lc_x1 - lc_x2) );
1742 
1743  Point aTempPos = rTargetDevice.LogicToPixel( aPos );
1744  nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min( lc_x1, lc_x2 ) );
1745  nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
1746  rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
1747  }
1748  }
1749  aPos.AdjustY(nTextHeight );
1750  aPos.setX( rRect.Left() );
1751  }
1752 
1753  // If there still is a last line, we output it left-aligned as the line would be clipped
1754  if ( !aLastLine.isEmpty() )
1755  _rLayout.DrawText( aPos, aLastLine, 0, aLastLine.getLength(), pVector, pDisplayText );
1756 
1757  // Reset clipping
1758  if ( nStyle & DrawTextFlags::Clip )
1759  rTargetDevice.Pop();
1760  }
1761  }
1762  else
1763  {
1764  tools::Long nTextWidth = _rLayout.GetTextWidth( aStr, 0, -1 );
1765 
1766  // Clip text if needed
1767  if ( nTextWidth > nWidth )
1768  {
1769  if ( nStyle & TEXT_DRAW_ELLIPSIS )
1770  {
1771  aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout );
1773  nStyle |= DrawTextFlags::Left;
1774  nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.getLength() );
1775  }
1776  }
1777  else
1778  {
1779  if ( nTextHeight <= nHeight )
1780  nStyle &= ~DrawTextFlags::Clip;
1781  }
1782 
1783  // horizontal text alignment
1784  if ( nStyle & DrawTextFlags::Right )
1785  aPos.AdjustX(nWidth-nTextWidth );
1786  else if ( nStyle & DrawTextFlags::Center )
1787  aPos.AdjustX((nWidth-nTextWidth)/2 );
1788 
1789  // vertical font alignment
1790  if ( eAlign == ALIGN_BOTTOM )
1791  aPos.AdjustY(nTextHeight );
1792  else if ( eAlign == ALIGN_BASELINE )
1793  aPos.AdjustY(rTargetDevice.GetFontMetric().GetAscent() );
1794 
1795  if ( nStyle & DrawTextFlags::Bottom )
1796  aPos.AdjustY(nHeight-nTextHeight );
1797  else if ( nStyle & DrawTextFlags::VCenter )
1798  aPos.AdjustY((nHeight-nTextHeight)/2 );
1799 
1800  tools::Long nMnemonicX = 0;
1801  tools::Long nMnemonicY = 0;
1802  DeviceCoordinate nMnemonicWidth = 0;
1803  if (nMnemonicPos != -1 && nMnemonicPos < aStr.getLength())
1804  {
1805  std::unique_ptr<sal_Int32[]> const pCaretXArray(new sal_Int32[2 * aStr.getLength()]);
1806  /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray.get(), 0, aStr.getLength() );
1807  sal_Int32 lc_x1 = pCaretXArray[2*nMnemonicPos];
1808  sal_Int32 lc_x2 = pCaretXArray[2*nMnemonicPos+1];
1809  nMnemonicWidth = rTargetDevice.LogicWidthToDeviceCoordinate( std::abs(lc_x1 - lc_x2) );
1810 
1811  Point aTempPos = rTargetDevice.LogicToPixel( aPos );
1812  nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min(lc_x1, lc_x2) );
1813  nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
1814  }
1815 
1816  if ( nStyle & DrawTextFlags::Clip )
1817  {
1818  rTargetDevice.Push( vcl::PushFlags::CLIPREGION );
1819  rTargetDevice.IntersectClipRegion( rRect );
1820  _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
1821  if ( bDrawMnemonics && nMnemonicPos != -1 )
1822  rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
1823  rTargetDevice.Pop();
1824  }
1825  else
1826  {
1827  _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
1828  if ( bDrawMnemonics && nMnemonicPos != -1 )
1829  rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
1830  }
1831  }
1832 
1833  if ( nStyle & DrawTextFlags::Disable && !pVector )
1834  {
1835  rTargetDevice.SetTextColor( aOldTextColor );
1836  if ( bRestoreFillColor )
1837  rTargetDevice.SetTextFillColor( aOldTextFillColor );
1838  }
1839 }
1840 
1842  const OUString& rOrigStr,
1843  DrawTextFlags nStyle,
1844  GDIMetaFile& rMtf )
1845 {
1846 
1847  if ( rOrigStr.isEmpty() || rRect.IsEmpty() )
1848  return;
1849 
1850  // we need a graphics
1851  if( !mpGraphics && !AcquireGraphics() )
1852  return;
1853  assert(mpGraphics);
1854  if( mbInitClipRegion )
1855  InitClipRegion();
1856 
1857  // temporarily swap in passed mtf for action generation, and
1858  // disable output generation.
1859  const bool bOutputEnabled( IsOutputEnabled() );
1860  GDIMetaFile* pMtf = mpMetaFile;
1861 
1862  mpMetaFile = &rMtf;
1863  EnableOutput( false );
1864 
1865  // #i47157# Factored out to ImplDrawTextRect(), to be shared
1866  // between us and DrawText()
1867  vcl::DefaultTextLayout aLayout( *this );
1868  ImplDrawText( *this, rRect, rOrigStr, nStyle, nullptr, nullptr, aLayout );
1869 
1870  // and restore again
1871  EnableOutput( bOutputEnabled );
1872  mpMetaFile = pMtf;
1873 }
1874 
1875 void OutputDevice::DrawText( const tools::Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle,
1876  std::vector< tools::Rectangle >* pVector, OUString* pDisplayText,
1877  vcl::ITextLayout* _pTextLayout )
1878 {
1879  assert(!is_double_buffered_window());
1880 
1881  if (mpOutDevData->mpRecordLayout)
1882  {
1883  pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
1884  pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
1885  }
1886 
1887  bool bDecomposeTextRectAction = ( _pTextLayout != nullptr ) && _pTextLayout->DecomposeTextRectAction();
1888  if ( mpMetaFile && !bDecomposeTextRectAction )
1889  mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) );
1890 
1891  if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || rOrigStr.isEmpty() || rRect.IsEmpty() )
1892  return;
1893 
1894  // we need a graphics
1895  if( !mpGraphics && !AcquireGraphics() )
1896  return;
1897  assert(mpGraphics);
1898  if( mbInitClipRegion )
1899  InitClipRegion();
1900  if (mbOutputClipped && !bDecomposeTextRectAction && !pDisplayText)
1901  return;
1902 
1903  // temporarily disable mtf action generation (ImplDrawText _does_
1904  // create MetaActionType::TEXTs otherwise)
1905  GDIMetaFile* pMtf = mpMetaFile;
1906  if ( !bDecomposeTextRectAction )
1907  mpMetaFile = nullptr;
1908 
1909  // #i47157# Factored out to ImplDrawText(), to be used also
1910  // from AddTextRectActions()
1911  vcl::DefaultTextLayout aDefaultLayout( *this );
1912  ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout );
1913 
1914  // and enable again
1915  mpMetaFile = pMtf;
1916 
1917  if( mpAlphaVDev )
1918  mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
1919 }
1920 
1922  const OUString& rStr, DrawTextFlags nStyle,
1923  TextRectInfo* pInfo,
1924  const vcl::ITextLayout* _pTextLayout ) const
1925 {
1926 
1927  tools::Rectangle aRect = rRect;
1928  sal_Int32 nLines;
1929  tools::Long nWidth = rRect.GetWidth();
1930  tools::Long nMaxWidth;
1931  tools::Long nTextHeight = GetTextHeight();
1932 
1933  OUString aStr = rStr;
1934  if ( nStyle & DrawTextFlags::Mnemonic )
1935  aStr = GetNonMnemonicString( aStr );
1936 
1937  if ( nStyle & DrawTextFlags::MultiLine )
1938  {
1939  ImplMultiTextLineInfo aMultiLineInfo;
1940  sal_Int32 nFormatLines;
1941  sal_Int32 i;
1942 
1943  nMaxWidth = 0;
1944  vcl::DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) );
1945  ImplGetTextLines( rRect, nTextHeight, aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout );
1946  nFormatLines = aMultiLineInfo.Count();
1947  if ( !nTextHeight )
1948  nTextHeight = 1;
1949  nLines = static_cast<sal_uInt16>(aRect.GetHeight()/nTextHeight);
1950  if ( pInfo )
1951  pInfo->mnLineCount = nFormatLines;
1952  if ( !nLines )
1953  nLines = 1;
1954  if ( nFormatLines <= nLines )
1955  nLines = nFormatLines;
1956  else
1957  {
1958  if ( !(nStyle & DrawTextFlags::EndEllipsis) )
1959  nLines = nFormatLines;
1960  else
1961  {
1962  if ( pInfo )
1963  pInfo->mbEllipsis = true;
1964  nMaxWidth = nWidth;
1965  }
1966  }
1967  if ( pInfo )
1968  {
1969  bool bMaxWidth = nMaxWidth == 0;
1970  pInfo->mnMaxWidth = 0;
1971  for ( i = 0; i < nLines; i++ )
1972  {
1973  ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( i );
1974  if ( bMaxWidth && (rLineInfo.GetWidth() > nMaxWidth) )
1975  nMaxWidth = rLineInfo.GetWidth();
1976  if ( rLineInfo.GetWidth() > pInfo->mnMaxWidth )
1977  pInfo->mnMaxWidth = rLineInfo.GetWidth();
1978  }
1979  }
1980  else if ( !nMaxWidth )
1981  {
1982  for ( i = 0; i < nLines; i++ )
1983  {
1984  ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( i );
1985  if ( rLineInfo.GetWidth() > nMaxWidth )
1986  nMaxWidth = rLineInfo.GetWidth();
1987  }
1988  }
1989  }
1990  else
1991  {
1992  nLines = 1;
1993  nMaxWidth = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.getLength() ) : GetTextWidth( aStr );
1994 
1995  if ( pInfo )
1996  {
1997  pInfo->mnLineCount = 1;
1998  pInfo->mnMaxWidth = nMaxWidth;
1999  }
2000 
2001  if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) )
2002  {
2003  if ( pInfo )
2004  pInfo->mbEllipsis = true;
2005  nMaxWidth = nWidth;
2006  }
2007  }
2008 
2009  if ( nStyle & DrawTextFlags::Right )
2010  aRect.SetLeft( aRect.Right()-nMaxWidth+1 );
2011  else if ( nStyle & DrawTextFlags::Center )
2012  {
2013  aRect.AdjustLeft((nWidth-nMaxWidth)/2 );
2014  aRect.SetRight( aRect.Left()+nMaxWidth-1 );
2015  }
2016  else
2017  aRect.SetRight( aRect.Left()+nMaxWidth-1 );
2018 
2019  if ( nStyle & DrawTextFlags::Bottom )
2020  aRect.SetTop( aRect.Bottom()-(nTextHeight*nLines)+1 );
2021  else if ( nStyle & DrawTextFlags::VCenter )
2022  {
2023  aRect.AdjustTop((aRect.GetHeight()-(nTextHeight*nLines))/2 );
2024  aRect.SetBottom( aRect.Top()+(nTextHeight*nLines)-1 );
2025  }
2026  else
2027  aRect.SetBottom( aRect.Top()+(nTextHeight*nLines)-1 );
2028 
2029  // #99188# get rid of rounding problems when using this rect later
2030  if (nStyle & DrawTextFlags::Right)
2031  aRect.AdjustLeft( -1 );
2032  else
2033  aRect.AdjustRight( 1 );
2034  return aRect;
2035 }
2036 
2037 static bool ImplIsCharIn( sal_Unicode c, const char* pStr )
2038 {
2039  while ( *pStr )
2040  {
2041  if ( *pStr == c )
2042  return true;
2043  pStr++;
2044  }
2045 
2046  return false;
2047 }
2048 
2049 OUString OutputDevice::GetEllipsisString( const OUString& rOrigStr, tools::Long nMaxWidth,
2050  DrawTextFlags nStyle ) const
2051 {
2052  vcl::DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) );
2053  return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout );
2054 }
2055 
2056 OUString OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const OUString& rOrigStr, tools::Long nMaxWidth,
2057  DrawTextFlags nStyle, const vcl::ITextLayout& _rLayout )
2058 {
2059  OUString aStr = rOrigStr;
2060  sal_Int32 nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.getLength() );
2061 
2062  if ( nIndex != -1 )
2063  {
2065  {
2066  OUStringBuffer aTmpStr( aStr );
2067  // speed it up by removing all but 1.33x as many as the break pos.
2068  sal_Int32 nEraseChars = std::max<sal_Int32>(4, aStr.getLength() - (nIndex*4)/3);
2069  while( nEraseChars < aStr.getLength() && _rLayout.GetTextWidth( aTmpStr.toString(), 0, aTmpStr.getLength() ) > nMaxWidth )
2070  {
2071  aTmpStr = aStr;
2072  sal_Int32 i = (aTmpStr.getLength() - nEraseChars)/2;
2073  aTmpStr.remove(i, nEraseChars++);
2074  aTmpStr.insert(i, "...");
2075  }
2076  aStr = aTmpStr.makeStringAndClear();
2077  }
2078  else if ( nStyle & DrawTextFlags::EndEllipsis )
2079  {
2080  aStr = aStr.copy(0, nIndex);
2081  if ( nIndex > 1 )
2082  {
2083  aStr += "...";
2084  while ( !aStr.isEmpty() && (_rLayout.GetTextWidth( aStr, 0, aStr.getLength() ) > nMaxWidth) )
2085  {
2086  if ( (nIndex > 1) || (nIndex == aStr.getLength()) )
2087  nIndex--;
2088  aStr = aStr.replaceAt( nIndex, 1, u"");
2089  }
2090  }
2091 
2092  if ( aStr.isEmpty() && (nStyle & DrawTextFlags::Clip) )
2093  aStr += OUStringChar(rOrigStr[ 0 ]);
2094  }
2095  else if ( nStyle & DrawTextFlags::PathEllipsis )
2096  {
2097  OUString aPath( rOrigStr );
2098  OUString aAbbreviatedPath;
2099  osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, nullptr );
2100  aStr = aAbbreviatedPath;
2101  }
2102  else if ( nStyle & DrawTextFlags::NewsEllipsis )
2103  {
2104  static char const pSepChars[] = ".";
2105  // Determine last section
2106  sal_Int32 nLastContent = aStr.getLength();
2107  while ( nLastContent )
2108  {
2109  nLastContent--;
2110  if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
2111  break;
2112  }
2113  while ( nLastContent &&
2114  ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
2115  nLastContent--;
2116 
2117  OUString aLastStr = aStr.copy(nLastContent);
2118  OUString aTempLastStr1 = "..." + aLastStr;
2119  if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.getLength() ) > nMaxWidth )
2120  aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
2121  else
2122  {
2123  sal_Int32 nFirstContent = 0;
2124  while ( nFirstContent < nLastContent )
2125  {
2126  nFirstContent++;
2127  if ( ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
2128  break;
2129  }
2130  while ( (nFirstContent < nLastContent) &&
2131  ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
2132  nFirstContent++;
2133  // MEM continue here
2134  if ( nFirstContent >= nLastContent )
2135  aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
2136  else
2137  {
2138  if ( nFirstContent > 4 )
2139  nFirstContent = 4;
2140  OUString aFirstStr = OUString::Concat(aStr.subView( 0, nFirstContent )) + "...";
2141  OUString aTempStr = aFirstStr + aLastStr;
2142  if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
2143  aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
2144  else
2145  {
2146  do
2147  {
2148  aStr = aTempStr;
2149  if( nLastContent > aStr.getLength() )
2150  nLastContent = aStr.getLength();
2151  while ( nFirstContent < nLastContent )
2152  {
2153  nLastContent--;
2154  if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
2155  break;
2156 
2157  }
2158  while ( (nFirstContent < nLastContent) &&
2159  ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
2160  nLastContent--;
2161 
2162  if ( nFirstContent < nLastContent )
2163  {
2164  std::u16string_view aTempLastStr = aStr.subView( nLastContent );
2165  aTempStr = aFirstStr + aTempLastStr;
2166 
2167  if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
2168  break;
2169  }
2170  }
2171  while ( nFirstContent < nLastContent );
2172  }
2173  }
2174  }
2175  }
2176  }
2177 
2178  return aStr;
2179 }
2180 
2181 void OutputDevice::DrawCtrlText( const Point& rPos, const OUString& rStr,
2182  sal_Int32 nIndex, sal_Int32 nLen,
2183  DrawTextFlags nStyle, std::vector< tools::Rectangle >* pVector, OUString* pDisplayText,
2184  const SalLayoutGlyphs* pGlyphs )
2185 {
2186  assert(!is_double_buffered_window());
2187 
2188  if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
2189  {
2190  nLen = rStr.getLength() - nIndex;
2191  }
2192 
2193  if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.getLength()) )
2194  return;
2195 
2196  // better get graphics here because ImplDrawMnemonicLine() will not
2197  // we need a graphics
2198  if( !mpGraphics && !AcquireGraphics() )
2199  return;
2200  assert(mpGraphics);
2201  if( mbInitClipRegion )
2202  InitClipRegion();
2203  if ( mbOutputClipped )
2204  return;
2205 
2206  if( nIndex >= rStr.getLength() )
2207  return;
2208 
2209  if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
2210  {
2211  nLen = rStr.getLength() - nIndex;
2212  }
2213  OUString aStr = rStr;
2214  sal_Int32 nMnemonicPos = -1;
2215 
2216  tools::Long nMnemonicX = 0;
2217  tools::Long nMnemonicY = 0;
2218  tools::Long nMnemonicWidth = 0;
2219  if ( (nStyle & DrawTextFlags::Mnemonic) && nLen > 1 )
2220  {
2221  aStr = GetNonMnemonicString( aStr, nMnemonicPos );
2222  if ( nMnemonicPos != -1 )
2223  {
2224  if( nMnemonicPos < nIndex )
2225  {
2226  --nIndex;
2227  }
2228  else
2229  {
2230  if( nMnemonicPos < (nIndex+nLen) )
2231  --nLen;
2232  SAL_WARN_IF( nMnemonicPos >= (nIndex+nLen), "vcl", "Mnemonic underline marker after last character" );
2233  }
2234  bool bInvalidPos = false;
2235 
2236  if( nMnemonicPos >= nLen )
2237  {
2238  // may occur in BiDi-Strings: the '~' is sometimes found behind the last char
2239  // due to some strange BiDi text editors
2240  // -> place the underline behind the string to indicate a failure
2241  bInvalidPos = true;
2242  nMnemonicPos = nLen-1;
2243  }
2244 
2245  std::unique_ptr<sal_Int32[]> const pCaretXArray(new sal_Int32[2 * nLen]);
2246  /*sal_Bool bRet =*/ GetCaretPositions( aStr, pCaretXArray.get(), nIndex, nLen, pGlyphs );
2247  sal_Int32 lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ];
2248  sal_Int32 lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ];
2249  nMnemonicWidth = ::abs(static_cast<int>(lc_x1 - lc_x2));
2250 
2251  Point aTempPos( std::min(lc_x1,lc_x2), GetFontMetric().GetAscent() );
2252  if( bInvalidPos ) // #106952#, place behind the (last) character
2253  aTempPos = Point( std::max(lc_x1,lc_x2), GetFontMetric().GetAscent() );
2254 
2255  aTempPos += rPos;
2256  aTempPos = LogicToPixel( aTempPos );
2257  nMnemonicX = mnOutOffX + aTempPos.X();
2258  nMnemonicY = mnOutOffY + aTempPos.Y();
2259  }
2260  }
2261 
2262  bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
2263 
2264  if ( nStyle & DrawTextFlags::Disable && ! pVector )
2265  {
2266  Color aOldTextColor;
2267  Color aOldTextFillColor;
2268  bool bRestoreFillColor;
2269  bool bHighContrastBlack = false;
2270  bool bHighContrastWhite = false;
2271  const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
2272  if( rStyleSettings.GetHighContrastMode() )
2273  {
2274  if( IsBackground() )
2275  {
2276  Wallpaper aWall = GetBackground();
2277  Color aCol = aWall.GetColor();
2278  bHighContrastBlack = aCol.IsDark();
2279  bHighContrastWhite = aCol.IsBright();
2280  }
2281  }
2282 
2283  aOldTextColor = GetTextColor();
2284  if ( IsTextFillColor() )
2285  {
2286  bRestoreFillColor = true;
2287  aOldTextFillColor = GetTextFillColor();
2288  }
2289  else
2290  bRestoreFillColor = false;
2291 
2292  if( bHighContrastBlack )
2294  else if( bHighContrastWhite )
2296  else
2297  SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
2298 
2299  DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
2300  if (!(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics)
2301  && (!autoacc || !(nStyle & DrawTextFlags::HideMnemonic)) )
2302  {
2303  if ( nMnemonicPos != -1 )
2304  ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
2305  }
2306  SetTextColor( aOldTextColor );
2307  if ( bRestoreFillColor )
2308  SetTextFillColor( aOldTextFillColor );
2309  }
2310  else
2311  {
2312  DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText, pGlyphs );
2313  if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics) && !pVector
2314  && (!autoacc || !(nStyle & DrawTextFlags::HideMnemonic)) )
2315  {
2316  if ( nMnemonicPos != -1 )
2317  ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
2318  }
2319  }
2320 
2321  if( mpAlphaVDev )
2322  mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText );
2323 }
2324 
2325 tools::Long OutputDevice::GetCtrlTextWidth( const OUString& rStr, const SalLayoutGlyphs* pGlyphs ) const
2326 {
2327  sal_Int32 nLen = rStr.getLength();
2328  sal_Int32 nIndex = 0;
2329 
2330  sal_Int32 nMnemonicPos;
2331  OUString aStr = GetNonMnemonicString( rStr, nMnemonicPos );
2332  if ( nMnemonicPos != -1 )
2333  {
2334  if ( nMnemonicPos < nIndex )
2335  nIndex--;
2336  else if (static_cast<sal_uLong>(nMnemonicPos) < static_cast<sal_uLong>(nIndex+nLen))
2337  nLen--;
2338  }
2339  return GetTextWidth( aStr, nIndex, nLen, nullptr, pGlyphs );
2340 }
2341 
2342 OUString OutputDevice::GetNonMnemonicString( const OUString& rStr, sal_Int32& rMnemonicPos )
2343 {
2344  OUString aStr = rStr;
2345  sal_Int32 nLen = aStr.getLength();
2346  sal_Int32 i = 0;
2347 
2348  rMnemonicPos = -1;
2349  while ( i < nLen )
2350  {
2351  if ( aStr[ i ] == '~' )
2352  {
2353  if ( nLen <= i+1 )
2354  break;
2355 
2356  if ( aStr[ i+1 ] != '~' )
2357  {
2358  if ( rMnemonicPos == -1 )
2359  rMnemonicPos = i;
2360  aStr = aStr.replaceAt( i, 1, u"" );
2361  nLen--;
2362  }
2363  else
2364  {
2365  aStr = aStr.replaceAt( i, 1, u"" );
2366  nLen--;
2367  i++;
2368  }
2369  }
2370  else
2371  i++;
2372  }
2373 
2374  return aStr;
2375 }
2376 
2378  const OUString& rStr, sal_Int32 nBase,
2379  sal_Int32 nIndex, sal_Int32 nLen,
2380  sal_uLong nLayoutWidth, o3tl::span<const sal_Int32> pDXAry,
2381  const SalLayoutGlyphs* pGlyphs ) const
2382 {
2383  bool bRet = false;
2384  rRect.SetEmpty();
2385 
2386  std::unique_ptr<SalLayout> pSalLayout;
2387  const Point aPoint;
2388  // calculate offset when nBase!=nIndex
2389  tools::Long nXOffset = 0;
2390  if( nBase != nIndex )
2391  {
2392  sal_Int32 nStart = std::min( nBase, nIndex );
2393  sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
2394  pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry );
2395  if( pSalLayout )
2396  {
2397  nXOffset = pSalLayout->GetTextWidth();
2398  nXOffset /= pSalLayout->GetUnitsPerPixel();
2399  // TODO: fix offset calculation for Bidi case
2400  if( nBase < nIndex)
2401  nXOffset = -nXOffset;
2402  }
2403  }
2404 
2405  pSalLayout = ImplLayout(rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry, eDefaultLayout,
2406  nullptr, pGlyphs);
2407  if( pSalLayout )
2408  {
2409  tools::Rectangle aPixelRect;
2410  bRet = pSalLayout->GetBoundRect(aPixelRect);
2411 
2412  if( bRet )
2413  {
2414  int nWidthFactor = pSalLayout->GetUnitsPerPixel();
2415 
2416  if( nWidthFactor > 1 )
2417  {
2418  double fFactor = 1.0 / nWidthFactor;
2419  aPixelRect.SetLeft(
2420  static_cast< tools::Long >(aPixelRect.Left() * fFactor) );
2421  aPixelRect.SetRight(
2422  static_cast< tools::Long >(aPixelRect.Right() * fFactor) );
2423  aPixelRect.SetTop(
2424  static_cast< tools::Long >(aPixelRect.Top() * fFactor) );
2425  aPixelRect.SetBottom(
2426  static_cast< tools::Long >(aPixelRect.Bottom() * fFactor) );
2427  }
2428 
2429  Point aRotatedOfs( mnTextOffX, mnTextOffY );
2430  DevicePoint aPos = pSalLayout->GetDrawPosition(DevicePoint(nXOffset, 0));
2431  aRotatedOfs -= Point(aPos.getX(), aPos.getY());
2432  aPixelRect += aRotatedOfs;
2433  rRect = PixelToLogic( aPixelRect );
2434  if( mbMap )
2435  rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
2436  }
2437  }
2438 
2439  return bRet;
2440 }
2441 
2443  const OUString& rStr, sal_Int32 nBase,
2444  sal_Int32 nIndex, sal_Int32 nLen,
2445  sal_uLong nLayoutWidth, o3tl::span<const sal_Int32> pDXArray ) const
2446 {
2447  if (!InitFont())
2448  return false;
2449 
2450  bool bRet = false;
2451  rVector.clear();
2452  if( nLen < 0 )
2453  {
2454  nLen = rStr.getLength() - nIndex;
2455  }
2456  rVector.reserve( nLen );
2457 
2458  // we want to get the Rectangle in logical units, so to
2459  // avoid rounding errors we just size the font in logical units
2460  bool bOldMap = mbMap;
2461  if( bOldMap )
2462  {
2463  const_cast<OutputDevice&>(*this).mbMap = false;
2464  const_cast<OutputDevice&>(*this).mbNewFont = true;
2465  }
2466 
2467  std::unique_ptr<SalLayout> pSalLayout;
2468 
2469  // calculate offset when nBase!=nIndex
2470  tools::Long nXOffset = 0;
2471  if( nBase != nIndex )
2472  {
2473  sal_Int32 nStart = std::min( nBase, nIndex );
2474  sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
2475  pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nLayoutWidth, pDXArray );
2476  if( pSalLayout )
2477  {
2478  nXOffset = pSalLayout->GetTextWidth();
2479  pSalLayout.reset();
2480  // TODO: fix offset calculation for Bidi case
2481  if( nBase > nIndex)
2482  nXOffset = -nXOffset;
2483  }
2484  }
2485 
2486  pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray );
2487  if( pSalLayout )
2488  {
2489  bRet = pSalLayout->GetOutline(rVector);
2490  if( bRet )
2491  {
2492  // transform polygon to pixel units
2493  basegfx::B2DHomMatrix aMatrix;
2494 
2495  int nWidthFactor = pSalLayout->GetUnitsPerPixel();
2496  if( nXOffset | mnTextOffX | mnTextOffY )
2497  {
2498  DevicePoint aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor );
2499  aRotatedOfs -= pSalLayout->GetDrawPosition(DevicePoint(nXOffset, 0));
2500  aMatrix.translate( aRotatedOfs.getX(), aRotatedOfs.getY() );
2501  }
2502 
2503  if( nWidthFactor > 1 )
2504  {
2505  double fFactor = 1.0 / nWidthFactor;
2506  aMatrix.scale( fFactor, fFactor );
2507  }
2508 
2509  if( !aMatrix.isIdentity() )
2510  {
2511  for (auto & elem : rVector)
2512  elem.transform( aMatrix );
2513  }
2514  }
2515 
2516  pSalLayout.reset();
2517  }
2518 
2519  if( bOldMap )
2520  {
2521  // restore original font size and map mode
2522  const_cast<OutputDevice&>(*this).mbMap = bOldMap;
2523  const_cast<OutputDevice&>(*this).mbNewFont = true;
2524  }
2525 
2526  return bRet;
2527 }
2528 
2530  const OUString& rStr, sal_Int32 nBase,
2531  sal_Int32 nIndex, sal_Int32 nLen,
2532  sal_uLong nLayoutWidth, o3tl::span<const sal_Int32> pDXArray ) const
2533 {
2534  rResultVector.clear();
2535 
2536  // get the basegfx polypolygon vector
2537  basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
2538  if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
2539  nLayoutWidth, pDXArray ) )
2540  return false;
2541 
2542  // convert to a tool polypolygon vector
2543  rResultVector.reserve( aB2DPolyPolyVector.size() );
2544  for (auto const& elem : aB2DPolyPolyVector)
2545  rResultVector.emplace_back(elem); // #i76339#
2546 
2547  return true;
2548 }
2549 
2550 bool OutputDevice::GetTextOutline( tools::PolyPolygon& rPolyPoly, const OUString& rStr ) const
2551 {
2552  rPolyPoly.Clear();
2553 
2554  // get the basegfx polypolygon vector
2555  basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
2556  if( !GetTextOutlines( aB2DPolyPolyVector, rStr, 0/*nBase*/, 0/*nIndex*/, /*nLen*/-1,
2557  /*nLayoutWidth*/0, /*pDXArray*/{} ) )
2558  return false;
2559 
2560  // convert and merge into a tool polypolygon
2561  for (auto const& elem : aB2DPolyPolyVector)
2562  for(auto const& rB2DPolygon : elem)
2563  rPolyPoly.Insert(tools::Polygon(rB2DPolygon)); // #i76339#
2564 
2565  return true;
2566 }
2567 
2569 {
2570  if (nFlags & SystemTextColorFlags::Mono)
2571  {
2573  }
2574  else
2575  {
2576  if (!bEnabled)
2577  {
2578  const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
2579  SetTextColor(rStyleSettings.GetDisableColor());
2580  }
2581  }
2582 }
2583 
2584 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void EnableOutput(bool bEnable=true)
Definition: outdev.cxx:343
DevicePoint GetDrawPosition(const DevicePoint &rRelative=DevicePoint(0, 0)) const
Definition: sallayout.cxx:152
const Color & GetTextColor() const
Definition: outdev.hxx:1016
double getY() const
void SetDigitLanguage(LanguageType)
Definition: text.cxx:69
SAL_DLLPRIVATE float approximate_char_width() const
Definition: text.cxx:908
SAL_DLLPRIVATE DeviceCoordinate LogicWidthToDeviceCoordinate(tools::Long nWidth) const
Definition: map.cxx:1876
sal_uInt16 mnLineCount
static bool IsTrackingFontMappingUse()
Definition: text.cxx:1259
const Wallpaper & GetBackground() const
Definition: outdev.hxx:534
sal_Int32 nIndex
void SetFontSize(const Size &)
Definition: font/font.cxx:148
const OUString & GetStyleName() const
TOOLS_DLLPUBLIC OString convertLineEnd(const OString &rIn, LineEnd eLineEnd)
tools::Long mnTextOffX
font specific text alignment offsets in pixel units
Definition: outdev.hxx:222
constexpr bool empty() const noexcept
const OUString & GetFamilyName() const
Definition: font/font.cxx:878
int GetUnitsPerPixel() const
Definition: vcllayout.hxx:82
bool IsNull() const
Definition: region.hxx:99
virtual vcl::Region GetOutputBoundsClipRegion() const
Definition: text.cxx:788
static SAL_DLLPRIVATE sal_Int32 ImplBreakLinesSimple(const tools::Long nWidth, const OUString &rStr, const vcl::ITextLayout &_rLayout, const sal_Int32 nPos, sal_Int32 nBreakPos, tools::Long &nLineWidth)
Definition: text.cxx:687
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:193
virtual bool IsVirtual() const
Definition: outdev.cxx:187
const OUString & GetStyleName() const
Definition: font/font.cxx:879
const Color & GetTextLineColor() const
Definition: outdev.hxx:1025
tools::Rectangle ImplGetTextBoundRect(const SalLayout &) const
Definition: text.cxx:188
constexpr tools::Long Left() const
bool IsOutline() const
Definition: font/font.cxx:915
bool mbTextSpecial
Definition: outdev.hxx:263
FontLineStyle GetOverline() const
Definition: font/font.cxx:919
bool IsBackground() const
Definition: outdev.hxx:537
sal_uIntPtr sal_uLong
long Long
const StyleSettings & GetStyleSettings() const
TextAlign GetTextAlign() const
Definition: outdev.hxx:1034
static const AllSettings & GetSettings()
Gets the application's settings.
Definition: svapp.cxx:760
void Push(vcl::PushFlags nFlags=vcl::PushFlags::ALL)
Definition: stack.cxx:33
static SAL_DLLPRIVATE OUString ImplGetEllipsisString(const OutputDevice &rTargetDevice, const OUString &rStr, tools::Long nMaxWidth, DrawTextFlags nStyle, const vcl::ITextLayout &_rLayout)
Definition: text.cxx:2056
bool IsOutputEnabled() const
Definition: outdev.hxx:487
const SalLayoutFlags eDefaultLayout
Definition: text.cxx:793
SAL_DLLPRIVATE double ImplLogicWidthToDeviceFontWidth(tools::Long nWidth) const
Definition: map.cxx:1888
tools::Long GetOutOffXPixel() const
Definition: outdev.hxx:325
ImplSVNWFData maNWFData
Definition: svdata.hxx:404
static std::shared_ptr< const vcl::text::TextLayoutCache > CreateTextLayoutCache(OUString const &)
Definition: text.cxx:1480
virtual DeviceCoordinate GetTextWidth() const
Definition: vcllayout.hxx:93
bool mbOutputClipped
Definition: outdev.hxx:252
static void ImplDrawText(OutputDevice &rTargetDevice, const tools::Rectangle &rRect, const OUString &rOrigStr, DrawTextFlags nStyle, std::vector< tools::Rectangle > *pVector, OUString *pDisplayText, vcl::ITextLayout &_rLayout)
Definition: text.cxx:1584
tools::Long mnOutOffY
Output offset for device output in pixel (pseudo window offset within window system's frames) ...
Definition: outdev.hxx:215
SAL_DLLPRIVATE void ImplInitTextColor()
Definition: text.cxx:98
bool IsKerning() const
Definition: font/font.cxx:244
SAL_DLLPRIVATE bool is_double_buffered_window() const
void SetTextFillColor()
Definition: text.cxx:732
void IntersectClipRegion(const tools::Rectangle &rRect)
bool GetTextRenderModeForResolutionIndependentLayout() const
Definition: outdev.hxx:495
static bool ImplIsCharIn(sal_Unicode c, const char *pStr)
Definition: text.cxx:2037
sal_Int32 mnDPIX
Definition: outdev.hxx:218
SAL_DLLPRIVATE void ImplDrawSpecialText(SalLayout &)
Definition: text.cxx:342
tools::Long GetTextArray(const OUString &rStr, std::vector< sal_Int32 > *pDXAry, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
Definition: text.cxx:955
constexpr::Color COL_LIGHTGRAY(0xC0, 0xC0, 0xC0)
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:641
void DrawMask(const Point &rDestPt, const Bitmap &rBitmap, const Color &rMaskColor)
Definition: mask.cxx:30
bool mbMap
Definition: outdev.hxx:247
sal_Int32 DeviceCoordinate
SAL_DLLPRIVATE SalLayoutFlags GetBiDiLayoutFlags(std::u16string_view rStr, const sal_Int32 nMinIndex, const sal_Int32 nEndIndex) const
Definition: text.cxx:1227
virtual bool DecomposeTextRectAction() const =0
float x
sal_Int32 GetTextBreak(const OUString &rStr, tools::Long nTextWidth, sal_Int32 nIndex, sal_Int32 nLen=-1, tools::Long nCharExtra=0, vcl::text::TextLayoutCache const *=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: text.cxx:1497
tools::Long mnEmphasisDescent
Definition: outdev.hxx:225
DrawTextFlags
virtual std::unique_ptr< GenericSalLayout > GetTextLayout(int nFallbackLevel)=0
void setX(double fX)
const Color & GetOverlineColor() const
Definition: outdev.hxx:1030
void ImplDrawTextBackground(const SalLayout &)
Definition: text.cxx:168
tools::Rectangle GetTextRect(const tools::Rectangle &rRect, const OUString &rStr, DrawTextFlags nStyle=DrawTextFlags::WordBreak, TextRectInfo *pInfo=nullptr, const vcl::ITextLayout *_pTextLayout=nullptr) const
Definition: text.cxx:1921
Color GetTextFillColor() const
Definition: text.cxx:764
sal_uInt16 sal_Unicode
void DrawStretchText(const Point &rStartPt, sal_uLong nWidth, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1)
Definition: text.cxx:1122
bool IsVertical() const
Definition: font/font.cxx:895
FontKerning GetKerning() const
Definition: font/font.cxx:896
sal_Int32 GetLen() const
virtual void SetSystemTextColor(SystemTextColorFlags nFlags, bool bEnabled)
Definition: text.cxx:2568
tools::Long GetAscent() const
Definition: metric.hxx:43
is an implementation of the ITextLayout interface which simply delegates its calls to the respective ...
Definition: textlayout.hxx:46
const vcl::Font & GetFont() const
Definition: outdev.hxx:540
vcl::text::ComplexTextLayoutFlags mnTextLayoutMode
Definition: outdev.hxx:227
void AddLine(const ImplTextLineInfo &)
Definition: text.cxx:88
void SetLayoutMode(vcl::text::ComplexTextLayoutFlags nTextLayoutMode)
Definition: text.cxx:58
OUString GetEllipsisString(const OUString &rStr, tools::Long nMaxWidth, DrawTextFlags nStyle=DrawTextFlags::EndEllipsis) const
Definition: text.cxx:2049
bool GetTextOutlines(PolyPolyVector &, const OUString &rStr, sal_Int32 nBase=0, sal_Int32 nIndex=0, sal_Int32 nLen=-1, sal_uLong nLayoutWidth=0, o3tl::span< const sal_Int32 > pDXArray={}) const
Definition: text.cxx:2529
bool GetTextOutline(tools::PolyPolygon &, const OUString &rStr) const
Definition: text.cxx:2550
bool GetHighContrastMode() const
virtual Bitmap GetBitmap(const Point &rSrcPt, const Size &rSize) const
constexpr tools::Long GetWidth() const
SystemTextColorFlags
void Pop()
Definition: stack.cxx:92
bool GetTextBoundRect(tools::Rectangle &rRect, const OUString &rStr, sal_Int32 nBase=0, sal_Int32 nIndex=0, sal_Int32 nLen=-1, sal_uLong nLayoutWidth=0, o3tl::span< const sal_Int32 > pDXArray={}, const SalLayoutGlyphs *pGlyphs=nullptr) const
Return the exact bounding rectangle of rStr.
Definition: text.cxx:2377
const LanguageTag & GetLanguageTag() const
Definition: font/font.cxx:889
void Insert(const tools::Polygon &rPoly, sal_uInt16 nPos=POLYPOLY_APPEND)
FontMetric GetFontMetric() const
tools::Long GetCtrlTextWidth(const OUString &rStr, const SalLayoutGlyphs *pLayoutCache=nullptr) const
Definition: text.cxx:2325
sal_Int32 GetIndex() const
bool IsValid() const
tools::Long GetWidth() const
B2IRange fround(const B2DRange &rRange)
bool GetBoundRect(tools::Rectangle &) const
Definition: sallayout.cxx:225
FontEmphasisMark GetEmphasisMark() const
Definition: font/font.cxx:921
void SetTextLineColor()
Definition: textline.cxx:883
bool mbAutoAccel
Definition: svdata.hxx:340
SAL_DLLPRIVATE void ImplDrawMnemonicLine(tools::Long nX, tools::Long nY, tools::Long nWidth)
Definition: textline.cxx:869
constexpr bool IsEmpty() const
void Rotate(const Point &rCenter, double fSin, double fCos)
void DrawTextArray(const Point &rStartPt, const OUString &rStr, o3tl::span< const sal_Int32 > pDXAry, sal_Int32 nIndex=0, sal_Int32 nLen=-1, SalLayoutFlags flags=SalLayoutFlags::NONE, const SalLayoutGlyphs *pLayoutCache=nullptr)
Definition: text.cxx:921
constexpr void SetLeft(tools::Long v)
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:188
bool IsTextFillColor() const
Definition: outdev.hxx:1021
void DrawRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, const OutputDevice &rOutDev)
virtual void SetFillColor()=0
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:76
sal_Int32 Count() const
const Color & GetDisableColor() const
ALIGN_BASELINE
LINEEND_LF
bool IsRTLEnabled() const
Definition: outdev.hxx:1284
SAL_DLLPRIVATE vcl::text::ImplLayoutArgs ImplPrepareLayoutArgs(OUString &, const sal_Int32 nIndex, const sal_Int32 nLen, DeviceCoordinate nPixelWidth, SalLayoutFlags flags=SalLayoutFlags::NONE, vcl::text::TextLayoutCache const *=nullptr) const
Definition: text.cxx:1149
int i
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
Width of the text.
Definition: text.cxx:884
ComplexTextLayoutFlags
Definition: State.hxx:75
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
virtual void SetLineColor()=0
bool IsDark() const
SalLayoutFlags GetFlags() const
float approximate_digit_width() const
Definition: text.cxx:916
static OutputDevice::FontMappingUseData * fontMappingUseData
Definition: text.cxx:1257
virtual void DrawText(SalGraphics &) const =0
void SetOrientation(Degree10 nLineOrientation)
Definition: font/font.cxx:226
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
bool SetOutputSizePixel(const Size &rNewSize, bool bErase=true)
Definition: virdev.cxx:407
std::vector< ImplTextLineInfo > mvLines
bool IsBright() const
SAL_DLLPRIVATE void ImplDrawTextDirect(SalLayout &, bool bTextLines)
Definition: text.cxx:299
DevicePoint & DrawBase()
Definition: vcllayout.hxx:71
static SAL_DLLPRIVATE tools::Long ImplGetTextLines(const tools::Rectangle &rRect, tools::Long nTextHeight, ImplMultiTextLineInfo &rLineInfo, tools::Long nWidth, const OUString &rStr, DrawTextFlags nStyle, const vcl::ITextLayout &_rLayout)
Definition: text.cxx:483
const Color & GetColor() const
Definition: wall.hxx:71
bool IsWordLineMode() const
Definition: font/font.cxx:922
virtual bool HasMirroredGraphics() const
Definition: outdev.cxx:719
bool mbInitLineColor
Definition: outdev.hxx:255
static void TrackFontMappingUse(const vcl::Font &originalFont, const SalLayout *salLayout)
Definition: text.cxx:1264
SAL_DLLPRIVATE void ImplDrawTextRect(tools::Long nBaseX, tools::Long nBaseY, tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight)
Definition: text.cxx:109
constexpr tools::Long Right() const
bool mbNewFont
Definition: outdev.hxx:261
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:175
SAL_DLLPRIVATE tools::Long ImplLogicWidthToDevicePixel(tools::Long nWidth) const
Convert a logical width to a width in units of device pixels.
Definition: map.cxx:324
bool mbTextLines
Definition: outdev.hxx:262
float u
void SetTextColor(const Color &rColor)
Definition: text.cxx:714
void scale(double fX, double fY)
bool IsShadow() const
Definition: font/font.cxx:916
sal_Int32 w
constexpr tools::Long Top() const
std::vector< tools::PolyPolygon > PolyPolyVector
void Intersect(const tools::Rectangle &rRegion)
Definition: region.cxx:582
ALIGN_BOTTOM
vcl::Font maFont
Definition: outdev.hxx:234
virtual tools::Long GetGraphicsWidth() const =0
bool ImplDrawRotateText(SalLayout &)
Definition: text.cxx:226
bool mbLineColor
Definition: outdev.hxx:253
FontStrikeout GetStrikeout() const
Definition: font/font.cxx:920
void SetOverlineColor()
Definition: textline.cxx:908
constexpr void SetRight(tools::Long v)
bool isIdentity() const
tools::Long mnMapOfsX
Offset in X direction.
Definition: ImplMapRes.hxx:28
virtual tools::Long GetTextWidth(const OUString &_rText, sal_Int32 _nStartIndex, sal_Int32 _nLength) const =0
const AllSettings & GetSettings() const
Definition: outdev.hxx:295
constexpr void SetBottom(tools::Long v)
bool mbInitTextColor
Definition: outdev.hxx:258
void DrawCtrlText(const Point &rPos, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, DrawTextFlags nStyle=DrawTextFlags::Mnemonic, std::vector< tools::Rectangle > *pVector=nullptr, OUString *pDisplayText=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr)
Definition: text.cxx:2181
virtual SalLayoutGlyphs GetGlyphs() const
Definition: sallayout.cxx:254
tools::Long mnMapOfsY
Offset in Y direction.
Definition: ImplMapRes.hxx:29
sal_Int32 nLineWidth
constexpr void SetTop(tools::Long v)
tools::Long mnEmphasisAscent
Definition: outdev.hxx:224
TextAlign
const ImplTextLineInfo & GetLine(sal_Int32 nLine) const
tools::Long GetOutOffYPixel() const
Definition: outdev.hxx:326
constexpr Point TopLeft() const
StyleSettingsOptions GetOptions() const
#define TEXT_DRAW_ELLIPSIS
Definition: text.cxx:56
tools::Long AdjustTop(tools::Long nVertMoveDelta)
static FontMappingUseData FinishTrackingFontMappingUse()
Definition: text.cxx:1298
abstract base class for physical font faces
bool IsUnderlineAbove() const
virtual sal_Int32 GetTextBreak(const OUString &_rText, tools::Long _nMaxTextWidth, sal_Int32 _nStartIndex, sal_Int32 _nLength) const =0
constexpr tools::Long Bottom() const
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1164
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:933
const LanguageTag & getLocale()
basegfx::B2DPoint DevicePoint
void adjustY(double fY)
static SAL_DLLPRIVATE sal_Int32 ImplBreakLinesWithIterator(const tools::Long nWidth, const OUString &rStr, const vcl::ITextLayout &_rLayout, const css::uno::Reference< css::linguistic2::XHyphenator > &xHyph, const css::uno::Reference< css::i18n::XBreakIterator > &xBI, const bool bHyphenate, const sal_Int32 nPos, sal_Int32 nBreakPos)
Definition: text.cxx:571
FontRelief GetRelief() const
Definition: font/font.cxx:917
std::vector< FontMappingUseItem > FontMappingUseData
Definition: outdev.hxx:1278
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:202
constexpr Size GetSize() const
bool Overlaps(const tools::Rectangle &rRect) const
Definition: region.cxx:1374
virtual void GetCaretPositions(const OUString &_rText, sal_Int32 *_pCaretXArray, sal_Int32 _nStartIndex, sal_Int32 _nLength) const =0
ImplMapRes maMapRes
Definition: outdev.hxx:228
#define SAL_WARN_IF(condition, area, stream)
static void StartTrackingFontMappingUse()
Definition: text.cxx:1292
SalLayoutGlyphsImpl * Impl(unsigned int nLevel) const
constexpr::Color COL_GREEN(0x00, 0x80, 0x00)
void SetFont(const vcl::Font &rNewFont)
Definition: outdev/font.cxx:53
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:581
bool mbInitClipRegion
Definition: outdev.hxx:259
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
tools::Long GetTextHeight() const
Height where any character of the current font fits; in logic coordinates.
Definition: text.cxx:895
SAL_DLLPRIVATE void ImplDrawEmphasisMarks(SalLayout &)
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_LIGHTGREEN
if(aStr!=aBuf) UpdateName_Impl(m_xFollowLb.get()
static VclPtr< reference_type > Create(Arg &&...arg)
A construction helper for VclPtr.
Definition: vclptr.hxx:127
sal_UCS4 GetLocalizedChar(sal_UCS4 nChar, LanguageType eLang)
Definition: sallayout.cxx:60
virtual void DrawText(const Point &_rStartPoint, const OUString &_rText, sal_Int32 _nStartIndex, sal_Int32 _nLength, std::vector< tools::Rectangle > *_pVector, OUString *_pDisplayText)=0
virtual void InitClipRegion()
bool IsEmpty() const
Reference< XComponentContext > getProcessComponentContext()
const char * name
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
bool GetTextIsRTL(const OUString &, sal_Int32 nIndex, sal_Int32 nLen) const
Definition: text.cxx:1486
rtl::Reference< LogicalFontInstance > mpFontInstance
Definition: outdev.hxx:192
void translate(double fX, double fY)
bool GetNextPos(int *nCharPos, bool *bRTL)
void AddTextRectActions(const tools::Rectangle &rRect, const OUString &rOrigStr, DrawTextFlags nStyle, GDIMetaFile &rMtf)
Generate MetaTextActions for the text rect.
Definition: text.cxx:1841
void SetAltNaturalDXArray(const double *pDXArray)
bool mbInitFillColor
Definition: outdev.hxx:256
tools::Long mnOutWidth
Definition: outdev.hxx:216
SAL_DLLPRIVATE tools::Rectangle ImplLogicToDevicePixel(const tools::Rectangle &rLogicRect) const
Convert a logical rectangle to a rectangle in physical device pixel units.
Definition: map.cxx:386
FontLineStyle GetUnderline() const
Definition: font/font.cxx:918
constexpr pointer data() const noexcept
tools::Long mnOutOffX
Output offset for device output in pixel (pseudo window offset within window system's frames) ...
Definition: outdev.hxx:213
tools::Long AdjustLeft(tools::Long nHorzMoveDelta)
#define SAL_WARN(area, stream)
std::unique_ptr< ImplOutDevData > mpOutDevData
Definition: outdev.hxx:195
void GetCaretPositions(const OUString &, sal_Int32 *pCaretXArray, sal_Int32 nIndex, sal_Int32 nLen, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: text.cxx:1064
double getX() const
tools::Long mnMaxWidth
std::unique_ptr< SalLayout > ImplLayout(const OUString &, sal_Int32 nIndex, sal_Int32 nLen, const Point &rLogicPos=Point(0, 0), tools::Long nLogicWidth=0, o3tl::span< const sal_Int32 > pLogicDXArray={}, SalLayoutFlags flags=SalLayoutFlags::NONE, vcl::text::TextLayoutCache const *=nullptr, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: text.cxx:1308
static std::shared_ptr< const vcl::text::TextLayoutCache > Create(OUString const &)
#define DBG_TESTSOLARMUTEX()
tools::Rectangle GetBoundRect() const
void SetDXArray(const DeviceCoordinate *pDXArray)
SAL_DLLPRIVATE DevicePoint ImplLogicToDeviceFontCoordinate(const Point &rLogicPt) const
Definition: map.cxx:1897
VCL_DLLPUBLIC css::uno::Reference< css::i18n::XBreakIterator > CreateBreakIterator()
Definition: unohelp.cxx:37
SAL_DLLPRIVATE tools::Long ImplDevicePixelToLogicWidth(tools::Long nWidth) const
Convert device pixels to a width in logical units.
Definition: map.cxx:348
static OUString GetNonMnemonicString(const OUString &rStr, sal_Int32 &rMnemonicPos)
Definition: text.cxx:2342
const OUString & GetFamilyName() const
void ImplDrawTextLines(SalLayout &, FontStrikeout eStrikeout, FontLineStyle eUnderline, FontLineStyle eOverline, bool bWordLine, bool bUnderlineAbove)
Definition: textline.cxx:809
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:488
Point & DrawOffset()
Definition: vcllayout.hxx:73
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)
Definition: text.cxx:795
virtual void SetTextColor(Color nColor)=0
SAL_DLLPRIVATE tools::Long ImplDevicePixelToLogicHeight(tools::Long nHeight) const
Convert device pixels to a height in logical units.
Definition: map.cxx:356
aStr
tools::Long mnTextOffY
Definition: outdev.hxx:223
SAL_DLLPRIVATE std::unique_ptr< SalLayout > ImplGlyphFallbackLayout(std::unique_ptr< SalLayout >, vcl::text::ImplLayoutArgs &, const SalLayoutGlyphs *) const
bool GetGlyphBoundRects(const Point &rOrigin, const OUString &rStr, int nIndex, int nLen, std::vector< tools::Rectangle > &rVector) const
FontRelief
Definition: fntstyle.hxx:26
sal_uInt16 nPos
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:191
void adjustX(double fX)
SAL_DLLPRIVATE void ImplDrawPolygon(const tools::Polygon &rPoly, const tools::PolyPolygon *pClipPolyPoly=nullptr)
Definition: polygon.cxx:445
constexpr tools::Long GetHeight() const
::std::vector< B2DPolyPolygon > B2DPolyPolygonVector
SAL_DLLPRIVATE bool InitFont() const
SalLayoutFlags
LanguageType meTextLanguage
Definition: outdev.hxx:244