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