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