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