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