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