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