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