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