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