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