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 double nWidth = rSalLayout.GetTextWidth();
173 const basegfx::B2DPoint 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 basegfx::B2DPoint aPoint = rSalLayout.GetDrawPosition();
193 tools::Long nX = aPoint.getX();
194 tools::Long nY = aPoint.getY();
195
196 double 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() = basegfx::B2DPoint( 0, 0 );
235 rSalLayout.DrawOffset() = Point( 0, 0 );
236 if (!rSalLayout.GetBoundRect(aBoundRect))
237 {
238 // guess vertical text extents if GetBoundRect failed
239 double 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 basegfx::B2DPoint 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() += basegfx::B2DPoint( nOff, nOff );
420 ImplDrawTextDirect( rSalLayout, mbTextLines );
421 rSalLayout.DrawBase() -= basegfx::B2DPoint( 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 + basegfx::B2DPoint(-1,-1);
434 ImplDrawTextDirect( rSalLayout, mbTextLines );
435 rSalLayout.DrawBase() = aOrigPos + basegfx::B2DPoint(+1,+1);
436 ImplDrawTextDirect( rSalLayout, mbTextLines );
437 rSalLayout.DrawBase() = aOrigPos + basegfx::B2DPoint(-1,+0);
438 ImplDrawTextDirect( rSalLayout, mbTextLines );
439 rSalLayout.DrawBase() = aOrigPos + basegfx::B2DPoint(-1,+1);
440 ImplDrawTextDirect( rSalLayout, mbTextLines );
441 rSalLayout.DrawBase() = aOrigPos + basegfx::B2DPoint(+0,+1);
442 ImplDrawTextDirect( rSalLayout, mbTextLines );
443 rSalLayout.DrawBase() = aOrigPos + basegfx::B2DPoint(+0,-1);
444 ImplDrawTextDirect( rSalLayout, mbTextLines );
445 rSalLayout.DrawBase() = aOrigPos + basegfx::B2DPoint(+1,-1);
446 ImplDrawTextDirect( rSalLayout, mbTextLines );
447 rSalLayout.DrawBase() = aOrigPos + basegfx::B2DPoint(+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
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_ALPHA_OPAQUE );
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_ALPHA_OPAQUE );
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 std::unique_ptr<std::vector<double>> xDXPixelArray;
993 if(pDXAry)
994 {
995 xDXPixelArray.reset(new std::vector<double>(nLen));
996 }
997 std::vector<double>* pDXPixelArray = xDXPixelArray.get();
998 double nWidth = pSalLayout->FillDXArray(pDXPixelArray, bCaret ? rStr : OUString());
999
1000 // convert virtual char widths to virtual absolute positions
1001 if( pDXPixelArray )
1002 {
1003 for( int i = 1; i < nLen; ++i )
1004 {
1005 (*pDXPixelArray)[i] += (*pDXPixelArray)[i - 1];
1006 }
1007 }
1008
1009 // convert from font units to logical units
1010 if (pDXPixelArray)
1011 {
1012 int nSubPixelFactor = pKernArray->get_factor();
1013 if (mbMap)
1014 {
1015 for (int i = 0; i < nLen; ++i)
1016 (*pDXPixelArray)[i] = ImplDevicePixelToLogicWidth((*pDXPixelArray)[i] * nSubPixelFactor);
1017 }
1018 else if (nSubPixelFactor)
1019 {
1020 for (int i = 0; i < nLen; ++i)
1021 (*pDXPixelArray)[i] *= nSubPixelFactor;
1022 }
1023 }
1024
1025 if (pDXAry)
1026 {
1027 pDXAry->resize(nLen);
1028 for (int i = 0; i < nLen; ++i)
1029 (*pDXAry)[i] = basegfx::fround((*pDXPixelArray)[i]);
1030 }
1031
1032 if (mbMap)
1033 nWidth = ImplDevicePixelToLogicWidth( nWidth );
1034
1035 return basegfx::fround(nWidth);
1036}
1037
1038void OutputDevice::GetCaretPositions( const OUString& rStr, KernArray& rCaretXArray,
1039 sal_Int32 nIndex, sal_Int32 nLen,
1040 const SalLayoutGlyphs* pGlyphs ) const
1041{
1042
1043 if( nIndex >= rStr.getLength() )
1044 return;
1045 if( nIndex+nLen >= rStr.getLength() )
1046 nLen = rStr.getLength() - nIndex;
1047
1048 sal_Int32 nCaretPos = nLen * 2;
1049 std::vector<sal_Int32>& rCaretPos = rCaretXArray.get_subunit_array();
1050 rCaretPos.resize(nCaretPos);
1051
1052 // do layout
1053 std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, Point(0, 0), 0, {}, {},
1054 eDefaultLayout, nullptr, pGlyphs);
1055 if( !pSalLayout )
1056 {
1057 std::fill(rCaretPos.begin(), rCaretPos.end(), -1);
1058 return;
1059 }
1060
1061 std::vector<double> aCaretPixelPos;
1062 pSalLayout->GetCaretPositions(aCaretPixelPos, rStr);
1063
1064 // fixup unknown caret positions
1065 int i;
1066 for (i = 0; i < nCaretPos; ++i)
1067 if (aCaretPixelPos[i] >= 0)
1068 break;
1069 tools::Long nXPos = (i < nCaretPos) ? aCaretPixelPos[i] : -1;
1070 for (i = 0; i < nCaretPos; ++i)
1071 {
1072 if (aCaretPixelPos[i] >= 0)
1073 nXPos = aCaretPixelPos[i];
1074 else
1075 aCaretPixelPos[i] = nXPos;
1076 }
1077
1078 // handle window mirroring
1079 if( IsRTLEnabled() )
1080 {
1081 double nWidth = pSalLayout->GetTextWidth();
1082 for (i = 0; i < nCaretPos; ++i)
1083 aCaretPixelPos[i] = nWidth - aCaretPixelPos[i] - 1;
1084 }
1085
1086 int nSubPixelFactor = rCaretXArray.get_factor();
1087 // convert from font units to logical units
1088 if( mbMap )
1089 {
1090 for (i = 0; i < nCaretPos; ++i)
1091 aCaretPixelPos[i] = ImplDevicePixelToLogicWidth(aCaretPixelPos[i] * nSubPixelFactor);
1092 }
1093 else if (nSubPixelFactor)
1094 {
1095 for (i = 0; i < nCaretPos; ++i)
1096 aCaretPixelPos[i] *= nSubPixelFactor;
1097 }
1098
1099 for (i = 0; i < nCaretPos; ++i)
1100 rCaretPos[i] = basegfx::fround(aCaretPixelPos[i]);
1101}
1102
1103void OutputDevice::DrawStretchText( const Point& rStartPt, sal_Int32 nWidth,
1104 const OUString& rStr,
1105 sal_Int32 nIndex, sal_Int32 nLen)
1106{
1107 assert(!is_double_buffered_window());
1108
1109 if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
1110 {
1111 nLen = rStr.getLength() - nIndex;
1112 }
1113
1114 if ( mpMetaFile )
1115 mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) );
1116
1117 if ( !IsDeviceOutputNecessary() )
1118 return;
1119
1120 std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, nWidth);
1121 if( pSalLayout )
1122 {
1123 ImplDrawText( *pSalLayout );
1124 }
1125
1126 if( mpAlphaVDev )
1127 mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen );
1128}
1129
1131 const sal_Int32 nMinIndex, const sal_Int32 nLen,
1132 double nPixelWidth,
1133 SalLayoutFlags nLayoutFlags,
1134 vcl::text::TextLayoutCache const*const pLayoutCache) const
1135{
1136 assert(nMinIndex >= 0);
1137 assert(nLen >= 0);
1138
1139 // get string length for calculating extents
1140 sal_Int32 nEndIndex = rStr.getLength();
1141 if( nMinIndex + nLen < nEndIndex )
1142 nEndIndex = nMinIndex + nLen;
1143
1144 // don't bother if there is nothing to do
1145 if( nEndIndex < nMinIndex )
1146 nEndIndex = nMinIndex;
1147
1148 nLayoutFlags |= GetBiDiLayoutFlags( rStr, nMinIndex, nEndIndex );
1149
1150 if( !maFont.IsKerning() )
1151 nLayoutFlags |= SalLayoutFlags::DisableKerning;
1153 nLayoutFlags |= SalLayoutFlags::KerningAsian;
1154 if( maFont.IsVertical() )
1155 nLayoutFlags |= SalLayoutFlags::Vertical;
1156 if( maFont.IsFixKerning() ||
1157 ( mpFontInstance && mpFontInstance->GetFontSelectPattern().GetPitch() == PITCH_FIXED ) )
1158 nLayoutFlags |= SalLayoutFlags::DisableLigatures;
1159
1160 if( meTextLanguage ) //TODO: (mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::SubstituteDigits)
1161 {
1162 // disable character localization when no digits used
1163 const sal_Unicode* pBase = rStr.getStr();
1164 const sal_Unicode* pStr = pBase + nMinIndex;
1165 const sal_Unicode* pEnd = pBase + nEndIndex;
1166 std::optional<OUStringBuffer> xTmpStr;
1167 for( ; pStr < pEnd; ++pStr )
1168 {
1169 // TODO: are there non-digit localizations?
1170 if( (*pStr >= '0') && (*pStr <= '9') )
1171 {
1172 // translate characters to local preference
1173 sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
1174 if( cChar != *pStr )
1175 {
1176 if (!xTmpStr)
1177 xTmpStr = OUStringBuffer(rStr);
1178 // TODO: are the localized digit surrogates?
1179 (*xTmpStr)[pStr - pBase] = cChar;
1180 }
1181 }
1182 }
1183 if (xTmpStr)
1184 rStr = (*xTmpStr).makeStringAndClear();
1185 }
1186
1187 // right align for RTL text, DRAWPOS_REVERSED, RTL window style
1190 bRightAlign = false;
1192 bRightAlign = true;
1193 // SSA: hack for western office, ie text get right aligned
1194 // for debugging purposes of mirrored UI
1195 bool bRTLWindow = IsRTLEnabled();
1196 bRightAlign ^= bRTLWindow;
1197 if( bRightAlign )
1198 nLayoutFlags |= SalLayoutFlags::RightAlign;
1199
1200 // set layout options
1201 vcl::text::ImplLayoutArgs aLayoutArgs(rStr, nMinIndex, nEndIndex, nLayoutFlags, maFont.GetLanguageTag(), pLayoutCache);
1202
1203 Degree10 nOrientation = mpFontInstance ? mpFontInstance->mnOrientation : 0_deg10;
1204 aLayoutArgs.SetOrientation( nOrientation );
1205
1206 aLayoutArgs.SetLayoutWidth( nPixelWidth );
1207
1208 return aLayoutArgs;
1209}
1210
1212 const sal_Int32 nMinIndex,
1213 const sal_Int32 nEndIndex ) const
1214{
1215 SalLayoutFlags nLayoutFlags = SalLayoutFlags::NONE;
1217 nLayoutFlags |= SalLayoutFlags::BiDiRtl;
1219 nLayoutFlags |= SalLayoutFlags::BiDiStrong;
1221 {
1222 // Disable Bidi if no RTL hint and only known LTR codes used.
1223 bool bAllLtr = true;
1224 for (sal_Int32 i = nMinIndex; i < nEndIndex; i++)
1225 {
1226 // [0x0000, 0x052F] are Latin, Greek and Cyrillic.
1227 // [0x0370, 0x03FF] has a few holes as if Unicode 10.0.0, but
1228 // hopefully no RTL character will be encoded there.
1229 if (rStr[i] > 0x052F)
1230 {
1231 bAllLtr = false;
1232 break;
1233 }
1234 }
1235 if (bAllLtr)
1236 nLayoutFlags |= SalLayoutFlags::BiDiStrong;
1237 }
1238 return nLayoutFlags;
1239}
1240
1242
1243static inline bool IsTrackingFontMappingUse()
1244{
1245 return fontMappingUseData != nullptr;
1246}
1247
1248static void TrackFontMappingUse( const vcl::Font& originalFont, const SalLayout* salLayout)
1249{
1250 assert(fontMappingUseData);
1251 OUString originalName = originalFont.GetStyleName().isEmpty()
1252 ? originalFont.GetFamilyName()
1253 : originalFont.GetFamilyName() + "/" + originalFont.GetStyleName();
1254 std::vector<OUString> usedFontNames;
1255 SalLayoutGlyphs glyphs = salLayout->GetGlyphs(); // includes all font fallbacks
1256 int level = 0;
1257 while( const SalLayoutGlyphsImpl* impl = glyphs.Impl(level++))
1258 {
1259 const vcl::font::PhysicalFontFace* face = impl->GetFont()->GetFontFace();
1260 OUString name = face->GetStyleName().isEmpty()
1261 ? face->GetFamilyName()
1262 : face->GetFamilyName() + "/" + face->GetStyleName();
1263 usedFontNames.push_back( name );
1264 }
1266 {
1267 if( item.mOriginalFont == originalName && item.mUsedFonts == usedFontNames )
1268 {
1269 ++item.mCount;
1270 return;
1271 }
1272 }
1273 fontMappingUseData->push_back( { originalName, usedFontNames, 1 } );
1274}
1275
1277{
1278 delete fontMappingUseData;
1280}
1281
1283{
1285 return {};
1286 FontMappingUseData ret = std::move( *fontMappingUseData );
1287 delete fontMappingUseData;
1288 fontMappingUseData = nullptr;
1289 return ret;
1290}
1291
1292std::unique_ptr<SalLayout> OutputDevice::ImplLayout(const OUString& rOrigStr,
1293 sal_Int32 nMinIndex, sal_Int32 nLen,
1294 const Point& rLogicalPos, tools::Long nLogicalWidth,
1295 KernArraySpan pDXArray,
1296 o3tl::span<const sal_Bool> pKashidaArray,
1297 SalLayoutFlags flags,
1298 vcl::text::TextLayoutCache const* pLayoutCache,
1299 const SalLayoutGlyphs* pGlyphs) const
1300{
1301 if (pGlyphs && !pGlyphs->IsValid())
1302 {
1303 SAL_WARN("vcl", "Trying to setup invalid cached glyphs - falling back to relayout!");
1304 pGlyphs = nullptr;
1305 }
1306#ifdef DBG_UTIL
1307 if (pGlyphs)
1308 {
1309 for( int level = 0;; ++level )
1310 {
1311 SalLayoutGlyphsImpl* glyphsImpl = pGlyphs->Impl(level);
1312 if(glyphsImpl == nullptr)
1313 break;
1314 // It is allowed to reuse only glyphs created with SalLayoutFlags::GlyphItemsOnly.
1315 // If the glyphs have already been used, the AdjustLayout() call below might have
1316 // altered them (MultiSalLayout::ImplAdjustMultiLayout() drops glyphs that need
1317 // fallback from the base layout, but then GenericSalLayout::LayoutText()
1318 // would not know to call SetNeedFallback()).
1319 assert(glyphsImpl->GetFlags() & SalLayoutFlags::GlyphItemsOnly);
1320 }
1321 }
1322#endif
1323
1324 if (!InitFont())
1325 return nullptr;
1326
1327 // check string index and length
1328 if( -1 == nLen || nMinIndex + nLen > rOrigStr.getLength() )
1329 {
1330 const sal_Int32 nNewLen = rOrigStr.getLength() - nMinIndex;
1331 if( nNewLen <= 0 )
1332 return nullptr;
1333 nLen = nNewLen;
1334 }
1335
1336 OUString aStr = rOrigStr;
1337
1338 // recode string if needed
1339 if( mpFontInstance->mpConversion ) {
1340 mpFontInstance->mpConversion->RecodeString( aStr, 0, aStr.getLength() );
1341 pLayoutCache = nullptr; // don't use cache with modified string!
1342 pGlyphs = nullptr;
1343 }
1344
1345 double nPixelWidth = nLogicalWidth;
1346 if( nLogicalWidth && mbMap )
1347 {
1348 // convert from logical units to physical units
1349 nPixelWidth = ImplLogicWidthToDeviceSubPixel(nLogicalWidth);
1350 }
1351
1352 vcl::text::ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen,
1353 nPixelWidth, flags, pLayoutCache);
1354
1355 double nEndGlyphCoord(0);
1356 std::unique_ptr<double[]> xDXPixelArray;
1357 if( !pDXArray.empty() )
1358 {
1359 xDXPixelArray.reset(new double[nLen]);
1360
1361 if (mbMap)
1362 {
1363 // convert from logical units to font units without rounding,
1364 // keeping accuracy for lower levels
1365 int nSubPixels = pDXArray.get_factor();
1366 for (int i = 0; i < nLen; ++i)
1367 xDXPixelArray[i] = ImplLogicWidthToDeviceSubPixel(pDXArray.get_subunit(i)) / nSubPixels;
1368 nEndGlyphCoord = xDXPixelArray[nLen - 1];
1369 }
1370 else
1371 {
1372 for(int i = 0; i < nLen; ++i)
1373 xDXPixelArray[i] = pDXArray.get(i);
1374 nEndGlyphCoord = std::lround(xDXPixelArray[nLen - 1]);
1375 }
1376
1377 aLayoutArgs.SetDXArray(xDXPixelArray.get());
1378 }
1379
1380 if (!pKashidaArray.empty())
1381 aLayoutArgs.SetKashidaArray(pKashidaArray.data());
1382
1383 // get matching layout object for base font
1384 std::unique_ptr<SalLayout> pSalLayout = mpGraphics->GetTextLayout(0);
1385
1386 if (pSalLayout)
1387 pSalLayout->SetSubpixelPositioning(mbMap);
1388
1389 // layout text
1390 if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs, pGlyphs ? pGlyphs->Impl(0) : nullptr ) )
1391 {
1392 pSalLayout.reset();
1393 }
1394
1395 if( !pSalLayout )
1396 return nullptr;
1397
1398 // do glyph fallback if needed
1399 // #105768# avoid fallback for very small font sizes
1400 if (aLayoutArgs.HasFallbackRun() && mpFontInstance->GetFontSelectPattern().mnHeight >= 3)
1401 pSalLayout = ImplGlyphFallbackLayout(std::move(pSalLayout), aLayoutArgs, pGlyphs);
1402
1404 // Return glyph items only after fallback handling. Otherwise they may
1405 // contain invalid glyph IDs.
1406 return pSalLayout;
1407
1408 // position, justify, etc. the layout
1409 pSalLayout->AdjustLayout( aLayoutArgs );
1410
1411 // default to on for pdf export, which uses SubPixelToLogic to convert back to
1412 // the logical coord space, of if we are scaling/mapping
1413 if (mbMap || meOutDevType == OUTDEV_PDF)
1414 pSalLayout->DrawBase() = ImplLogicToDeviceSubPixel(rLogicalPos);
1415 else
1416 {
1417 Point aDevicePos = ImplLogicToDevicePixel(rLogicalPos);
1418 pSalLayout->DrawBase() = basegfx::B2DPoint(aDevicePos.X(), aDevicePos.Y());
1419 }
1420
1421 // adjust to right alignment if necessary
1422 if( aLayoutArgs.mnFlags & SalLayoutFlags::RightAlign )
1423 {
1424 double nRTLOffset;
1425 if (!pDXArray.empty())
1426 nRTLOffset = nEndGlyphCoord;
1427 else if( nPixelWidth )
1428 nRTLOffset = nPixelWidth;
1429 else
1430 nRTLOffset = pSalLayout->GetTextWidth();
1431 pSalLayout->DrawOffset().setX( 1 - nRTLOffset );
1432 }
1433
1435 TrackFontMappingUse(GetFont(), pSalLayout.get());
1436
1437 return pSalLayout;
1438}
1439
1440std::shared_ptr<const vcl::text::TextLayoutCache> OutputDevice::CreateTextLayoutCache(
1441 OUString const& rString)
1442{
1443 return vcl::text::TextLayoutCache::Create(rString);
1444}
1445
1446bool OutputDevice::GetTextIsRTL( const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen ) const
1447{
1448 OUString aStr( rString );
1450 bool bRTL = false;
1451 int nCharPos = -1;
1452 if (!aArgs.GetNextPos(&nCharPos, &bRTL))
1453 return false;
1454 return (nCharPos != nIndex);
1455}
1456
1457sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, tools::Long nTextWidth,
1458 sal_Int32 nIndex, sal_Int32 nLen,
1459 tools::Long nCharExtra,
1460 vcl::text::TextLayoutCache const*const pLayoutCache,
1461 const SalLayoutGlyphs* pGlyphs) const
1462{
1463 std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rStr, nIndex, nLen,
1464 Point(0,0), 0, {}, {}, eDefaultLayout, pLayoutCache, pGlyphs);
1465 sal_Int32 nRetVal = -1;
1466 if( pSalLayout )
1467 {
1468 // convert logical widths into layout units
1469 // NOTE: be very careful to avoid rounding errors for nCharExtra case
1470 // problem with rounding errors especially for small nCharExtras
1471 // TODO: remove when layout units have subpixel granularity
1472 tools::Long nSubPixelFactor = 1;
1473 if (!mbMap)
1474 nSubPixelFactor = 64;
1475 double nTextPixelWidth = ImplLogicWidthToDeviceSubPixel(nTextWidth * nSubPixelFactor);
1476 double nExtraPixelWidth = 0;
1477 if( nCharExtra != 0 )
1478 nExtraPixelWidth = ImplLogicWidthToDeviceSubPixel(nCharExtra * nSubPixelFactor);
1479 nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
1480 }
1481
1482 return nRetVal;
1483}
1484
1485sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, tools::Long nTextWidth,
1486 sal_Unicode nHyphenChar, sal_Int32& rHyphenPos,
1487 sal_Int32 nIndex, sal_Int32 nLen,
1488 tools::Long nCharExtra,
1489 vcl::text::TextLayoutCache const*const pLayoutCache,
1490 const SalLayoutGlyphs* pGlyphs) const
1491{
1492 rHyphenPos = -1;
1493
1494 std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rStr, nIndex, nLen,
1495 Point(0,0), 0, {}, {}, eDefaultLayout, pLayoutCache, pGlyphs);
1496 sal_Int32 nRetVal = -1;
1497 if( pSalLayout )
1498 {
1499 // convert logical widths into layout units
1500 // NOTE: be very careful to avoid rounding errors for nCharExtra case
1501 // problem with rounding errors especially for small nCharExtras
1502 // TODO: remove when layout units have subpixel granularity
1503 tools::Long nSubPixelFactor = 1;
1504 if (!mbMap)
1505 nSubPixelFactor = 64;
1506
1507 double nTextPixelWidth = ImplLogicWidthToDeviceSubPixel(nTextWidth * nSubPixelFactor);
1508 double nExtraPixelWidth = 0;
1509 if( nCharExtra != 0 )
1510 nExtraPixelWidth = ImplLogicWidthToDeviceSubPixel(nCharExtra * nSubPixelFactor);
1511
1512 // calculate un-hyphenated break position
1513 nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
1514
1515 // calculate hyphenated break position
1516 OUString aHyphenStr(nHyphenChar);
1517 std::unique_ptr<SalLayout> pHyphenLayout = ImplLayout( aHyphenStr, 0, 1 );
1518 if( pHyphenLayout )
1519 {
1520 // calculate subpixel width of hyphenation character
1521 double nHyphenPixelWidth = pHyphenLayout->GetTextWidth() * nSubPixelFactor;
1522
1523 // calculate hyphenated break position
1524 nTextPixelWidth -= nHyphenPixelWidth;
1525 if( nExtraPixelWidth > 0 )
1526 nTextPixelWidth -= nExtraPixelWidth;
1527
1528 rHyphenPos = pSalLayout->GetTextBreak(nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor);
1529
1530 if( rHyphenPos > nRetVal )
1531 rHyphenPos = nRetVal;
1532 }
1533 }
1534
1535 return nRetVal;
1536}
1537
1539 const OUString& rOrigStr, DrawTextFlags nStyle,
1540 std::vector< tools::Rectangle >* pVector, OUString* pDisplayText,
1541 vcl::ITextLayout& _rLayout )
1542{
1543
1544 Color aOldTextColor;
1545 Color aOldTextFillColor;
1546 bool bRestoreFillColor = false;
1547 if ( (nStyle & DrawTextFlags::Disable) && ! pVector )
1548 {
1549 bool bHighContrastBlack = false;
1550 bool bHighContrastWhite = false;
1551 const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() );
1552 if( rStyleSettings.GetHighContrastMode() )
1553 {
1554 Color aCol;
1555 if( rTargetDevice.IsBackground() )
1556 aCol = rTargetDevice.GetBackground().GetColor();
1557 else
1558 // best guess is the face color here
1559 // but it may be totally wrong. the background color
1560 // was typically already reset
1561 aCol = rStyleSettings.GetFaceColor();
1562
1563 bHighContrastBlack = aCol.IsDark();
1564 bHighContrastWhite = aCol.IsBright();
1565 }
1566
1567 aOldTextColor = rTargetDevice.GetTextColor();
1568 if ( rTargetDevice.IsTextFillColor() )
1569 {
1570 bRestoreFillColor = true;
1571 aOldTextFillColor = rTargetDevice.GetTextFillColor();
1572 }
1573 if( bHighContrastBlack )
1574 rTargetDevice.SetTextColor( COL_GREEN );
1575 else if( bHighContrastWhite )
1576 rTargetDevice.SetTextColor( COL_LIGHTGREEN );
1577 else
1578 {
1579 // draw disabled text always without shadow
1580 // as it fits better with native look
1581 rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() );
1582 }
1583 }
1584
1585 tools::Long nWidth = rRect.GetWidth();
1586 tools::Long nHeight = rRect.GetHeight();
1587
1588 if (nWidth <= 0 || nHeight <= 0)
1589 {
1590 if (nStyle & DrawTextFlags::Clip)
1591 return;
1592 static bool bFuzzing = utl::ConfigManager::IsFuzzing();
1593 SAL_WARN_IF(bFuzzing, "vcl", "skipping negative rectangle of: " << nWidth << " x " << nHeight);
1594 if (bFuzzing)
1595 return;
1596 }
1597
1598 Point aPos = rRect.TopLeft();
1599
1600 tools::Long nTextHeight = rTargetDevice.GetTextHeight();
1601 TextAlign eAlign = rTargetDevice.GetTextAlign();
1602 sal_Int32 nMnemonicPos = -1;
1603
1604 OUString aStr = rOrigStr;
1605 if ( nStyle & DrawTextFlags::Mnemonic )
1606 aStr = removeMnemonicFromString( aStr, nMnemonicPos );
1607
1608 const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics) && !pVector;
1609
1610 // We treat multiline text differently
1611 if ( nStyle & DrawTextFlags::MultiLine )
1612 {
1613
1614 ImplMultiTextLineInfo aMultiLineInfo;
1615 sal_Int32 i;
1616 sal_Int32 nFormatLines;
1617
1618 if ( nTextHeight )
1619 {
1620 tools::Long nMaxTextWidth = ImplGetTextLines( rRect, nTextHeight, aMultiLineInfo, nWidth, aStr, nStyle, _rLayout );
1621 sal_Int32 nLines = static_cast<sal_Int32>(nHeight/nTextHeight);
1622 OUString aLastLine;
1623 nFormatLines = aMultiLineInfo.Count();
1624 if (nLines <= 0)
1625 nLines = 1;
1626 if ( nFormatLines > nLines )
1627 {
1628 if ( nStyle & DrawTextFlags::EndEllipsis )
1629 {
1630 // Create last line and shorten it
1631 nFormatLines = nLines-1;
1632
1633 ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( nFormatLines );
1634 aLastLine = convertLineEnd(aStr.copy(rLineInfo.GetIndex()), LINEEND_LF);
1635 // Replace all LineFeeds with Spaces
1636 OUStringBuffer aLastLineBuffer(aLastLine);
1637 sal_Int32 nLastLineLen = aLastLineBuffer.getLength();
1638 for ( i = 0; i < nLastLineLen; i++ )
1639 {
1640 if ( aLastLineBuffer[ i ] == '\n' )
1641 aLastLineBuffer[ i ] = ' ';
1642 }
1643 aLastLine = aLastLineBuffer.makeStringAndClear();
1644 aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout );
1646 nStyle |= DrawTextFlags::Top;
1647 }
1648 }
1649 else
1650 {
1651 if ( nMaxTextWidth <= nWidth )
1652 nStyle &= ~DrawTextFlags::Clip;
1653 }
1654
1655 // Do we need to clip the height?
1656 if ( nFormatLines*nTextHeight > nHeight )
1657 nStyle |= DrawTextFlags::Clip;
1658
1659 // Set clipping
1660 if ( nStyle & DrawTextFlags::Clip )
1661 {
1662 rTargetDevice.Push( vcl::PushFlags::CLIPREGION );
1663 rTargetDevice.IntersectClipRegion( rRect );
1664 }
1665
1666 // Vertical alignment
1667 if ( nStyle & DrawTextFlags::Bottom )
1668 aPos.AdjustY(nHeight-(nFormatLines*nTextHeight) );
1669 else if ( nStyle & DrawTextFlags::VCenter )
1670 aPos.AdjustY((nHeight-(nFormatLines*nTextHeight))/2 );
1671
1672 // Font alignment
1673 if ( eAlign == ALIGN_BOTTOM )
1674 aPos.AdjustY(nTextHeight );
1675 else if ( eAlign == ALIGN_BASELINE )
1676 aPos.AdjustY(rTargetDevice.GetFontMetric().GetAscent() );
1677
1678 // Output all lines except for the last one
1679 for ( i = 0; i < nFormatLines; i++ )
1680 {
1681 ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( i );
1682 if ( nStyle & DrawTextFlags::Right )
1683 aPos.AdjustX(nWidth-rLineInfo.GetWidth() );
1684 else if ( nStyle & DrawTextFlags::Center )
1685 aPos.AdjustX((nWidth-rLineInfo.GetWidth())/2 );
1686 sal_Int32 nIndex = rLineInfo.GetIndex();
1687 sal_Int32 nLineLen = rLineInfo.GetLen();
1688 _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
1689 if ( bDrawMnemonics )
1690 {
1691 if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
1692 {
1693 tools::Long nMnemonicX;
1694 tools::Long nMnemonicY;
1695
1696 KernArray aDXArray;
1697 _rLayout.GetTextArray(aStr, &aDXArray, nIndex, nLineLen, true);
1698 sal_Int32 nPos = nMnemonicPos - nIndex;
1699 sal_Int32 lc_x1 = nPos ? aDXArray[nPos - 1] : 0;
1700 sal_Int32 lc_x2 = aDXArray[nPos];
1701 double nMnemonicWidth = rTargetDevice.ImplLogicWidthToDeviceSubPixel(std::abs(lc_x1 - lc_x2));
1702
1703 Point aTempPos = rTargetDevice.LogicToPixel( aPos );
1704 nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min( lc_x1, lc_x2 ) );
1705 nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
1706 rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
1707 }
1708 }
1709 aPos.AdjustY(nTextHeight );
1710 aPos.setX( rRect.Left() );
1711 }
1712
1713 // If there still is a last line, we output it left-aligned as the line would be clipped
1714 if ( !aLastLine.isEmpty() )
1715 _rLayout.DrawText( aPos, aLastLine, 0, aLastLine.getLength(), pVector, pDisplayText );
1716
1717 // Reset clipping
1718 if ( nStyle & DrawTextFlags::Clip )
1719 rTargetDevice.Pop();
1720 }
1721 }
1722 else
1723 {
1724 tools::Long nTextWidth = _rLayout.GetTextWidth( aStr, 0, -1 );
1725
1726 // Clip text if needed
1727 if ( nTextWidth > nWidth )
1728 {
1729 if ( nStyle & TEXT_DRAW_ELLIPSIS )
1730 {
1731 aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout );
1733 nStyle |= DrawTextFlags::Left;
1734 nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.getLength() );
1735 }
1736 }
1737 else
1738 {
1739 if ( nTextHeight <= nHeight )
1740 nStyle &= ~DrawTextFlags::Clip;
1741 }
1742
1743 // horizontal text alignment
1744 if ( nStyle & DrawTextFlags::Right )
1745 aPos.AdjustX(nWidth-nTextWidth );
1746 else if ( nStyle & DrawTextFlags::Center )
1747 aPos.AdjustX((nWidth-nTextWidth)/2 );
1748
1749 // vertical font alignment
1750 if ( eAlign == ALIGN_BOTTOM )
1751 aPos.AdjustY(nTextHeight );
1752 else if ( eAlign == ALIGN_BASELINE )
1753 aPos.AdjustY(rTargetDevice.GetFontMetric().GetAscent() );
1754
1755 if ( nStyle & DrawTextFlags::Bottom )
1756 aPos.AdjustY(nHeight-nTextHeight );
1757 else if ( nStyle & DrawTextFlags::VCenter )
1758 aPos.AdjustY((nHeight-nTextHeight)/2 );
1759
1760 tools::Long nMnemonicX = 0;
1761 tools::Long nMnemonicY = 0;
1762 double nMnemonicWidth = 0;
1763 if (nMnemonicPos != -1 && nMnemonicPos < aStr.getLength())
1764 {
1765 KernArray aDXArray;
1766 _rLayout.GetTextArray(aStr, &aDXArray, 0, aStr.getLength(), true);
1767 tools::Long lc_x1 = nMnemonicPos? aDXArray[nMnemonicPos - 1] : 0;
1768 tools::Long lc_x2 = aDXArray[nMnemonicPos];
1769 nMnemonicWidth = rTargetDevice.ImplLogicWidthToDeviceSubPixel(std::abs(lc_x1 - lc_x2));
1770
1771 Point aTempPos = rTargetDevice.LogicToPixel( aPos );
1772 nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min(lc_x1, lc_x2) );
1773 nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
1774 }
1775
1776 if ( nStyle & DrawTextFlags::Clip )
1777 {
1778 rTargetDevice.Push( vcl::PushFlags::CLIPREGION );
1779 rTargetDevice.IntersectClipRegion( rRect );
1780 _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
1781 if ( bDrawMnemonics && nMnemonicPos != -1 )
1782 rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
1783 rTargetDevice.Pop();
1784 }
1785 else
1786 {
1787 _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
1788 if ( bDrawMnemonics && nMnemonicPos != -1 )
1789 rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
1790 }
1791 }
1792
1793 if ( nStyle & DrawTextFlags::Disable && !pVector )
1794 {
1795 rTargetDevice.SetTextColor( aOldTextColor );
1796 if ( bRestoreFillColor )
1797 rTargetDevice.SetTextFillColor( aOldTextFillColor );
1798 }
1799}
1800
1802 const OUString& rOrigStr,
1803 DrawTextFlags nStyle,
1804 GDIMetaFile& rMtf )
1805{
1806
1807 if ( rOrigStr.isEmpty() || rRect.IsEmpty() )
1808 return;
1809
1810 // we need a graphics
1811 if( !mpGraphics && !AcquireGraphics() )
1812 return;
1813 assert(mpGraphics);
1814 if( mbInitClipRegion )
1816
1817 // temporarily swap in passed mtf for action generation, and
1818 // disable output generation.
1819 const bool bOutputEnabled( IsOutputEnabled() );
1820 GDIMetaFile* pMtf = mpMetaFile;
1821
1822 mpMetaFile = &rMtf;
1823 EnableOutput( false );
1824
1825 // #i47157# Factored out to ImplDrawTextRect(), to be shared
1826 // between us and DrawText()
1827 vcl::DefaultTextLayout aLayout( *this );
1828 ImplDrawText( *this, rRect, rOrigStr, nStyle, nullptr, nullptr, aLayout );
1829
1830 // and restore again
1831 EnableOutput( bOutputEnabled );
1832 mpMetaFile = pMtf;
1833}
1834
1835void OutputDevice::DrawText( const tools::Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle,
1836 std::vector< tools::Rectangle >* pVector, OUString* pDisplayText,
1837 vcl::ITextLayout* _pTextLayout )
1838{
1839 assert(!is_double_buffered_window());
1840
1841 if (mpOutDevData->mpRecordLayout)
1842 {
1843 pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
1844 pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
1845 }
1846
1847 bool bDecomposeTextRectAction = ( _pTextLayout != nullptr ) && _pTextLayout->DecomposeTextRectAction();
1848 if ( mpMetaFile && !bDecomposeTextRectAction )
1849 mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) );
1850
1851 if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || rOrigStr.isEmpty() || rRect.IsEmpty() )
1852 return;
1853
1854 // we need a graphics
1855 if( !mpGraphics && !AcquireGraphics() )
1856 return;
1857 assert(mpGraphics);
1858 if( mbInitClipRegion )
1860 if (mbOutputClipped && !bDecomposeTextRectAction && !pDisplayText)
1861 return;
1862
1863 // temporarily disable mtf action generation (ImplDrawText _does_
1864 // create MetaActionType::TEXTs otherwise)
1865 GDIMetaFile* pMtf = mpMetaFile;
1866 if ( !bDecomposeTextRectAction )
1867 mpMetaFile = nullptr;
1868
1869 // #i47157# Factored out to ImplDrawText(), to be used also
1870 // from AddTextRectActions()
1871 vcl::DefaultTextLayout aDefaultLayout( *this );
1872 ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout );
1873
1874 // and enable again
1875 mpMetaFile = pMtf;
1876
1877 if( mpAlphaVDev )
1878 mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
1879}
1880
1882 const OUString& rStr, DrawTextFlags nStyle,
1883 TextRectInfo* pInfo,
1884 const vcl::ITextLayout* _pTextLayout ) const
1885{
1886
1887 tools::Rectangle aRect = rRect;
1888 sal_Int32 nLines;
1889 tools::Long nWidth = rRect.GetWidth();
1890 tools::Long nMaxWidth;
1891 tools::Long nTextHeight = GetTextHeight();
1892
1893 OUString aStr = rStr;
1894 if ( nStyle & DrawTextFlags::Mnemonic )
1896
1897 if ( nStyle & DrawTextFlags::MultiLine )
1898 {
1899 ImplMultiTextLineInfo aMultiLineInfo;
1900 sal_Int32 nFormatLines;
1901 sal_Int32 i;
1902
1903 nMaxWidth = 0;
1904 vcl::DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) );
1905 ImplGetTextLines( rRect, nTextHeight, aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout );
1906 nFormatLines = aMultiLineInfo.Count();
1907 if ( !nTextHeight )
1908 nTextHeight = 1;
1909 nLines = static_cast<sal_uInt16>(aRect.GetHeight()/nTextHeight);
1910 if ( pInfo )
1911 pInfo->mnLineCount = nFormatLines;
1912 if ( !nLines )
1913 nLines = 1;
1914 if ( nFormatLines <= nLines )
1915 nLines = nFormatLines;
1916 else
1917 {
1918 if ( !(nStyle & DrawTextFlags::EndEllipsis) )
1919 nLines = nFormatLines;
1920 else
1921 {
1922 if ( pInfo )
1923 pInfo->mbEllipsis = true;
1924 nMaxWidth = nWidth;
1925 }
1926 }
1927 if ( pInfo )
1928 {
1929 bool bMaxWidth = nMaxWidth == 0;
1930 pInfo->mnMaxWidth = 0;
1931 for ( i = 0; i < nLines; i++ )
1932 {
1933 ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( i );
1934 if ( bMaxWidth && (rLineInfo.GetWidth() > nMaxWidth) )
1935 nMaxWidth = rLineInfo.GetWidth();
1936 if ( rLineInfo.GetWidth() > pInfo->mnMaxWidth )
1937 pInfo->mnMaxWidth = rLineInfo.GetWidth();
1938 }
1939 }
1940 else if ( !nMaxWidth )
1941 {
1942 for ( i = 0; i < nLines; i++ )
1943 {
1944 ImplTextLineInfo& rLineInfo = aMultiLineInfo.GetLine( i );
1945 if ( rLineInfo.GetWidth() > nMaxWidth )
1946 nMaxWidth = rLineInfo.GetWidth();
1947 }
1948 }
1949 }
1950 else
1951 {
1952 nLines = 1;
1953 nMaxWidth = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.getLength() ) : GetTextWidth( aStr );
1954
1955 if ( pInfo )
1956 {
1957 pInfo->mnLineCount = 1;
1958 pInfo->mnMaxWidth = nMaxWidth;
1959 }
1960
1961 if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) )
1962 {
1963 if ( pInfo )
1964 pInfo->mbEllipsis = true;
1965 nMaxWidth = nWidth;
1966 }
1967 }
1968
1969 if ( nStyle & DrawTextFlags::Right )
1970 aRect.SetLeft( aRect.Right()-nMaxWidth+1 );
1971 else if ( nStyle & DrawTextFlags::Center )
1972 {
1973 aRect.AdjustLeft((nWidth-nMaxWidth)/2 );
1974 aRect.SetRight( aRect.Left()+nMaxWidth-1 );
1975 }
1976 else
1977 aRect.SetRight( aRect.Left()+nMaxWidth-1 );
1978
1979 if ( nStyle & DrawTextFlags::Bottom )
1980 aRect.SetTop( aRect.Bottom()-(nTextHeight*nLines)+1 );
1981 else if ( nStyle & DrawTextFlags::VCenter )
1982 {
1983 aRect.AdjustTop((aRect.GetHeight()-(nTextHeight*nLines))/2 );
1984 aRect.SetBottom( aRect.Top()+(nTextHeight*nLines)-1 );
1985 }
1986 else
1987 aRect.SetBottom( aRect.Top()+(nTextHeight*nLines)-1 );
1988
1989 // #99188# get rid of rounding problems when using this rect later
1990 if (nStyle & DrawTextFlags::Right)
1991 aRect.AdjustLeft( -1 );
1992 else
1993 aRect.AdjustRight( 1 );
1994
1995 if (maFont.GetOrientation() != 0_deg10)
1996 {
1997 tools::Polygon aRotatedPolygon(aRect);
1998 aRotatedPolygon.Rotate(Point(aRect.GetWidth() / 2, aRect.GetHeight() / 2), maFont.GetOrientation());
1999 return aRotatedPolygon.GetBoundRect();
2000 }
2001
2002 return aRect;
2003}
2004
2005static bool ImplIsCharIn( sal_Unicode c, const char* pStr )
2006{
2007 while ( *pStr )
2008 {
2009 if ( *pStr == c )
2010 return true;
2011 pStr++;
2012 }
2013
2014 return false;
2015}
2016
2017OUString OutputDevice::GetEllipsisString( const OUString& rOrigStr, tools::Long nMaxWidth,
2018 DrawTextFlags nStyle ) const
2019{
2020 vcl::DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) );
2021 return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout );
2022}
2023
2024OUString OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const OUString& rOrigStr, tools::Long nMaxWidth,
2025 DrawTextFlags nStyle, const vcl::ITextLayout& _rLayout )
2026{
2027 OUString aStr = rOrigStr;
2028 sal_Int32 nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.getLength() );
2029
2030 if ( nIndex != -1 )
2031 {
2033 {
2034 OUStringBuffer aTmpStr( aStr );
2035 // speed it up by removing all but 1.33x as many as the break pos.
2036 sal_Int32 nEraseChars = std::max<sal_Int32>(4, aStr.getLength() - (nIndex*4)/3);
2037 while( nEraseChars < aStr.getLength() && _rLayout.GetTextWidth( aTmpStr.toString(), 0, aTmpStr.getLength() ) > nMaxWidth )
2038 {
2039 aTmpStr = aStr;
2040 sal_Int32 i = (aTmpStr.getLength() - nEraseChars)/2;
2041 aTmpStr.remove(i, nEraseChars++);
2042 aTmpStr.insert(i, "...");
2043 }
2044 aStr = aTmpStr.makeStringAndClear();
2045 }
2046 else if ( nStyle & DrawTextFlags::EndEllipsis )
2047 {
2048 aStr = aStr.copy(0, nIndex);
2049 if ( nIndex > 1 )
2050 {
2051 aStr += "...";
2052 while ( !aStr.isEmpty() && (_rLayout.GetTextWidth( aStr, 0, aStr.getLength() ) > nMaxWidth) )
2053 {
2054 if ( (nIndex > 1) || (nIndex == aStr.getLength()) )
2055 nIndex--;
2056 aStr = aStr.replaceAt( nIndex, 1, u"");
2057 }
2058 }
2059
2060 if ( aStr.isEmpty() && (nStyle & DrawTextFlags::Clip) )
2061 aStr += OUStringChar(rOrigStr[ 0 ]);
2062 }
2063 else if ( nStyle & DrawTextFlags::PathEllipsis )
2064 {
2065 OUString aPath( rOrigStr );
2066 OUString aAbbreviatedPath;
2067 osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, nullptr );
2068 aStr = aAbbreviatedPath;
2069 }
2070 else if ( nStyle & DrawTextFlags::NewsEllipsis )
2071 {
2072 static char const pSepChars[] = ".";
2073 // Determine last section
2074 sal_Int32 nLastContent = aStr.getLength();
2075 while ( nLastContent )
2076 {
2077 nLastContent--;
2078 if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
2079 break;
2080 }
2081 while ( nLastContent &&
2082 ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
2083 nLastContent--;
2084
2085 OUString aLastStr = aStr.copy(nLastContent);
2086 OUString aTempLastStr1 = "..." + aLastStr;
2087 if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.getLength() ) > nMaxWidth )
2088 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
2089 else
2090 {
2091 sal_Int32 nFirstContent = 0;
2092 while ( nFirstContent < nLastContent )
2093 {
2094 nFirstContent++;
2095 if ( ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
2096 break;
2097 }
2098 while ( (nFirstContent < nLastContent) &&
2099 ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
2100 nFirstContent++;
2101 // MEM continue here
2102 if ( nFirstContent >= nLastContent )
2103 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
2104 else
2105 {
2106 if ( nFirstContent > 4 )
2107 nFirstContent = 4;
2108 OUString aFirstStr = OUString::Concat(aStr.subView( 0, nFirstContent )) + "...";
2109 OUString aTempStr = aFirstStr + aLastStr;
2110 if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
2111 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
2112 else
2113 {
2114 do
2115 {
2116 aStr = aTempStr;
2117 if( nLastContent > aStr.getLength() )
2118 nLastContent = aStr.getLength();
2119 while ( nFirstContent < nLastContent )
2120 {
2121 nLastContent--;
2122 if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
2123 break;
2124
2125 }
2126 while ( (nFirstContent < nLastContent) &&
2127 ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
2128 nLastContent--;
2129
2130 if ( nFirstContent < nLastContent )
2131 {
2132 std::u16string_view aTempLastStr = aStr.subView( nLastContent );
2133 aTempStr = aFirstStr + aTempLastStr;
2134
2135 if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
2136 break;
2137 }
2138 }
2139 while ( nFirstContent < nLastContent );
2140 }
2141 }
2142 }
2143 }
2144 }
2145
2146 return aStr;
2147}
2148
2149void OutputDevice::DrawCtrlText( const Point& rPos, const OUString& rStr,
2150 sal_Int32 nIndex, sal_Int32 nLen,
2151 DrawTextFlags nStyle, std::vector< tools::Rectangle >* pVector, OUString* pDisplayText,
2152 const SalLayoutGlyphs* pGlyphs )
2153{
2154 assert(!is_double_buffered_window());
2155
2156 if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
2157 {
2158 nLen = rStr.getLength() - nIndex;
2159 }
2160
2161 if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.getLength()) )
2162 return;
2163
2164 // better get graphics here because ImplDrawMnemonicLine() will not
2165 // we need a graphics
2166 if( !mpGraphics && !AcquireGraphics() )
2167 return;
2168 assert(mpGraphics);
2169 if( mbInitClipRegion )
2171 if ( mbOutputClipped )
2172 return;
2173
2174 if( nIndex >= rStr.getLength() )
2175 return;
2176
2177 if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
2178 {
2179 nLen = rStr.getLength() - nIndex;
2180 }
2181 OUString aStr = rStr;
2182 sal_Int32 nMnemonicPos = -1;
2183
2184 tools::Long nMnemonicX = 0;
2185 tools::Long nMnemonicY = 0;
2186 tools::Long nMnemonicWidth = 0;
2187 if ( (nStyle & DrawTextFlags::Mnemonic) && nLen > 1 )
2188 {
2189 aStr = removeMnemonicFromString( aStr, nMnemonicPos );
2190 if ( nMnemonicPos != -1 )
2191 {
2192 if( nMnemonicPos < nIndex )
2193 {
2194 --nIndex;
2195 }
2196 else
2197 {
2198 if( nMnemonicPos < (nIndex+nLen) )
2199 --nLen;
2200 SAL_WARN_IF( nMnemonicPos >= (nIndex+nLen), "vcl", "Mnemonic underline marker after last character" );
2201 }
2202 bool bInvalidPos = false;
2203
2204 if( nMnemonicPos >= nLen )
2205 {
2206 // may occur in BiDi-Strings: the '~' is sometimes found behind the last char
2207 // due to some strange BiDi text editors
2208 // -> place the underline behind the string to indicate a failure
2209 bInvalidPos = true;
2210 nMnemonicPos = nLen-1;
2211 }
2212
2213 KernArray aDXArray;
2214 GetTextArray(aStr, &aDXArray, nIndex, nLen, true, nullptr, pGlyphs);
2215 sal_Int32 nPos = nMnemonicPos - nIndex;
2216 sal_Int32 lc_x1 = nPos ? aDXArray[nPos - 1] : 0;
2217 sal_Int32 lc_x2 = aDXArray[nPos];
2218 nMnemonicWidth = std::abs(lc_x1 - lc_x2);
2219
2220 Point aTempPos( std::min(lc_x1,lc_x2), GetFontMetric().GetAscent() );
2221 if( bInvalidPos ) // #106952#, place behind the (last) character
2222 aTempPos = Point( std::max(lc_x1,lc_x2), GetFontMetric().GetAscent() );
2223
2224 aTempPos += rPos;
2225 aTempPos = LogicToPixel( aTempPos );
2226 nMnemonicX = mnOutOffX + aTempPos.X();
2227 nMnemonicY = mnOutOffY + aTempPos.Y();
2228 }
2229 }
2230
2231 if ( nStyle & DrawTextFlags::Disable && ! pVector )
2232 {
2233 Color aOldTextColor;
2234 Color aOldTextFillColor;
2235 bool bRestoreFillColor;
2236 bool bHighContrastBlack = false;
2237 bool bHighContrastWhite = false;
2238 const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
2239 if( rStyleSettings.GetHighContrastMode() )
2240 {
2241 if( IsBackground() )
2242 {
2243 Wallpaper aWall = GetBackground();
2244 Color aCol = aWall.GetColor();
2245 bHighContrastBlack = aCol.IsDark();
2246 bHighContrastWhite = aCol.IsBright();
2247 }
2248 }
2249
2250 aOldTextColor = GetTextColor();
2251 if ( IsTextFillColor() )
2252 {
2253 bRestoreFillColor = true;
2254 aOldTextFillColor = GetTextFillColor();
2255 }
2256 else
2257 bRestoreFillColor = false;
2258
2259 if( bHighContrastBlack )
2261 else if( bHighContrastWhite )
2263 else
2264 SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
2265
2266 DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
2267 if (!(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics))
2268 {
2269 if ( nMnemonicPos != -1 )
2270 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
2271 }
2272 SetTextColor( aOldTextColor );
2273 if ( bRestoreFillColor )
2274 SetTextFillColor( aOldTextFillColor );
2275 }
2276 else
2277 {
2278 DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText, pGlyphs );
2279 if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics) && !pVector )
2280 {
2281 if ( nMnemonicPos != -1 )
2282 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
2283 }
2284 }
2285
2286 if( mpAlphaVDev )
2287 mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText );
2288}
2289
2290tools::Long OutputDevice::GetCtrlTextWidth( const OUString& rStr, const SalLayoutGlyphs* pGlyphs ) const
2291{
2292 sal_Int32 nLen = rStr.getLength();
2293 sal_Int32 nIndex = 0;
2294
2295 sal_Int32 nMnemonicPos;
2296 OUString aStr = removeMnemonicFromString( rStr, nMnemonicPos );
2297 if ( nMnemonicPos != -1 )
2298 {
2299 if ( nMnemonicPos < nIndex )
2300 nIndex--;
2301 else if (static_cast<sal_uLong>(nMnemonicPos) < static_cast<sal_uLong>(nIndex+nLen))
2302 nLen--;
2303 }
2304 return GetTextWidth( aStr, nIndex, nLen, nullptr, pGlyphs );
2305}
2306
2308 const OUString& rStr, sal_Int32 nBase,
2309 sal_Int32 nIndex, sal_Int32 nLen,
2310 sal_uLong nLayoutWidth, KernArraySpan pDXAry,
2311 o3tl::span<const sal_Bool> pKashidaAry,
2312 const SalLayoutGlyphs* pGlyphs ) const
2313{
2314 bool bRet = false;
2315 rRect.SetEmpty();
2316
2317 std::unique_ptr<SalLayout> pSalLayout;
2318 const Point aPoint;
2319 // calculate offset when nBase!=nIndex
2320 double nXOffset = 0;
2321 if( nBase != nIndex )
2322 {
2323 sal_Int32 nStart = std::min( nBase, nIndex );
2324 sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
2325 pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry, pKashidaAry );
2326 if( pSalLayout )
2327 {
2328 nXOffset = pSalLayout->GetTextWidth();
2329 // TODO: fix offset calculation for Bidi case
2330 if( nBase < nIndex)
2331 nXOffset = -nXOffset;
2332 }
2333 }
2334
2335 pSalLayout = ImplLayout(rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry, pKashidaAry, eDefaultLayout,
2336 nullptr, pGlyphs);
2337 if( pSalLayout )
2338 {
2339 tools::Rectangle aPixelRect;
2340 bRet = pSalLayout->GetBoundRect(aPixelRect);
2341
2342 if( bRet )
2343 {
2344 Point aRotatedOfs( mnTextOffX, mnTextOffY );
2345 basegfx::B2DPoint aPos = pSalLayout->GetDrawPosition(basegfx::B2DPoint(nXOffset, 0));
2346 aRotatedOfs -= Point(aPos.getX(), aPos.getY());
2347 aPixelRect += aRotatedOfs;
2348 rRect = PixelToLogic( aPixelRect );
2349 if( mbMap )
2351 }
2352 }
2353
2354 return bRet;
2355}
2356
2358 const OUString& rStr, sal_Int32 nBase,
2359 sal_Int32 nIndex, sal_Int32 nLen,
2360 sal_uLong nLayoutWidth,
2361 KernArraySpan pDXArray,
2362 o3tl::span<const sal_Bool> pKashidaArray ) const
2363{
2364 if (!InitFont())
2365 return false;
2366
2367 bool bRet = false;
2368 rVector.clear();
2369 if( nLen < 0 )
2370 {
2371 nLen = rStr.getLength() - nIndex;
2372 }
2373 rVector.reserve( nLen );
2374
2375 // we want to get the Rectangle in logical units, so to
2376 // avoid rounding errors we just size the font in logical units
2377 bool bOldMap = mbMap;
2378 if( bOldMap )
2379 {
2380 const_cast<OutputDevice&>(*this).mbMap = false;
2381 const_cast<OutputDevice&>(*this).mbNewFont = true;
2382 }
2383
2384 std::unique_ptr<SalLayout> pSalLayout;
2385
2386 // calculate offset when nBase!=nIndex
2387 double nXOffset = 0;
2388 if( nBase != nIndex )
2389 {
2390 sal_Int32 nStart = std::min( nBase, nIndex );
2391 sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
2392 pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nLayoutWidth, pDXArray, pKashidaArray);
2393 if( pSalLayout )
2394 {
2395 nXOffset = pSalLayout->GetTextWidth();
2396 pSalLayout.reset();
2397 // TODO: fix offset calculation for Bidi case
2398 if( nBase > nIndex)
2399 nXOffset = -nXOffset;
2400 }
2401 }
2402
2403 pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray, pKashidaArray );
2404 if( pSalLayout )
2405 {
2406 bRet = pSalLayout->GetOutline(rVector);
2407 if( bRet )
2408 {
2409 // transform polygon to pixel units
2410 basegfx::B2DHomMatrix aMatrix;
2411
2412 if (nXOffset || mnTextOffX || mnTextOffY)
2413 {
2415 aRotatedOfs -= pSalLayout->GetDrawPosition(basegfx::B2DPoint(nXOffset, 0));
2416 aMatrix.translate( aRotatedOfs.getX(), aRotatedOfs.getY() );
2417 }
2418
2419 if( !aMatrix.isIdentity() )
2420 {
2421 for (auto & elem : rVector)
2422 elem.transform( aMatrix );
2423 }
2424 }
2425
2426 pSalLayout.reset();
2427 }
2428
2429 if( bOldMap )
2430 {
2431 // restore original font size and map mode
2432 const_cast<OutputDevice&>(*this).mbMap = bOldMap;
2433 const_cast<OutputDevice&>(*this).mbNewFont = true;
2434 }
2435
2436 return bRet;
2437}
2438
2440 const OUString& rStr, sal_Int32 nBase,
2441 sal_Int32 nIndex, sal_Int32 nLen,
2442 sal_uLong nLayoutWidth, KernArraySpan pDXArray,
2443 o3tl::span<const sal_Bool> pKashidaArray ) const
2444{
2445 rResultVector.clear();
2446
2447 // get the basegfx polypolygon vector
2448 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
2449 if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
2450 nLayoutWidth, pDXArray, pKashidaArray ) )
2451 return false;
2452
2453 // convert to a tool polypolygon vector
2454 rResultVector.reserve( aB2DPolyPolyVector.size() );
2455 for (auto const& elem : aB2DPolyPolyVector)
2456 rResultVector.emplace_back(elem); // #i76339#
2457
2458 return true;
2459}
2460
2461bool OutputDevice::GetTextOutline( tools::PolyPolygon& rPolyPoly, const OUString& rStr ) const
2462{
2463 rPolyPoly.Clear();
2464
2465 // get the basegfx polypolygon vector
2466 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
2467 if( !GetTextOutlines( aB2DPolyPolyVector, rStr, 0/*nBase*/, 0/*nIndex*/, /*nLen*/-1,
2468 /*nLayoutWidth*/0, /*pDXArray*/{} ) )
2469 return false;
2470
2471 // convert and merge into a tool polypolygon
2472 for (auto const& elem : aB2DPolyPolyVector)
2473 for(auto const& rB2DPolygon : elem)
2474 rPolyPoly.Insert(tools::Polygon(rB2DPolygon)); // #i76339#
2475
2476 return true;
2477}
2478
2480{
2481 if (nFlags & SystemTextColorFlags::Mono)
2482 {
2484 }
2485 else
2486 {
2487 if (!bEnabled)
2488 {
2489 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
2490 SetTextColor(rStyleSettings.GetDisableColor());
2491 }
2492 }
2493}
2494
2495/* 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:638
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:585
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
SAL_DLLPRIVATE basegfx::B2DPoint ImplLogicToDeviceSubPixel(const Point &rLogicPt) const
Definition: map.cxx:1855
void GetCaretPositions(const OUString &, KernArray &rCaretXArray, sal_Int32 nIndex, sal_Int32 nLen, const SalLayoutGlyphs *pGlyphs=nullptr) const
Definition: text.cxx:1038
tools::Long GetCtrlTextWidth(const OUString &rStr, const SalLayoutGlyphs *pLayoutCache=nullptr) const
Definition: text.cxx:2290
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:1282
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:589
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:2149
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:1457
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:304
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:334
bool GetTextOutline(tools::PolyPolygon &, const OUString &rStr) const
Definition: text.cxx:2461
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:1110
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:2479
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:2307
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:2017
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:280
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:1211
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:1801
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:2024
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:1837
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_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:879
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:1881
SAL_DLLPRIVATE tools::Long ImplDevicePixelToLogicWidth(tools::Long nWidth) const
Convert device pixels to a width in logical units.
Definition: map.cxx:296
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
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:1276
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:1538
bool IsBackground() const
Definition: outdev.hxx:526
bool GetTextIsRTL(const OUString &, sal_Int32 nIndex, sal_Int32 nLen) const
Definition: text.cxx:1446
void Push(vcl::PushFlags nFlags=vcl::PushFlags::ALL)
Definition: stack.cxx:32
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:1292
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:2439
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:919
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:1440
void IntersectClipRegion(const tools::Rectangle &rRect)
SAL_DLLPRIVATE void ImplDrawMnemonicLine(tools::Long nX, tools::Long nY, tools::Long nWidth)
Definition: textline.cxx:882
SAL_DLLPRIVATE vcl::text::ImplLayoutArgs ImplPrepareLayoutArgs(OUString &, const sal_Int32 nIndex, const sal_Int32 nLen, double nPixelWidth, SalLayoutFlags flags=SalLayoutFlags::NONE, vcl::text::TextLayoutCache const *=nullptr) const
Definition: text.cxx:1130
const Color & GetTextLineColor() const
Definition: outdev.hxx:1012
void SetTextLineColor()
Definition: textline.cxx:894
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:1103
virtual bool IsVirtual() const
Definition: outdev.cxx:185
const OutDevType meOutDevType
Definition: outdev.hxx:223
constexpr tools::Long Y() const
void setX(tools::Long nX)
void RotateAround(tools::Long &rX, tools::Long &rY, Degree10 nOrientation) const
tools::Long AdjustY(tools::Long nVertMove)
tools::Long AdjustX(tools::Long nHorzMove)
constexpr tools::Long X() const
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
virtual double GetTextWidth() const
Definition: vcllayout.hxx:98
basegfx::B2DPoint GetDrawPosition(const basegfx::B2DPoint &rRelative=basegfx::B2DPoint(0, 0)) const
Definition: sallayout.cxx:144
bool GetBoundRect(tools::Rectangle &) const
Definition: sallayout.cxx:217
basegfx::B2DPoint & DrawBase()
Definition: vcllayout.hxx:72
Point & DrawOffset()
Definition: vcllayout.hxx:74
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, bool bAlphaMaskTransparent=false)
Definition: virdev.cxx:405
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 tools::Long GetTextArray(const OUString &_rText, KernArray *_pDXArray, sal_Int32 _nStartIndex, sal_Int32 _nLength, bool bCaret=false) const =0
virtual sal_Int32 GetTextBreak(const OUString &_rText, tools::Long _nMaxTextWidth, 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
void SetLayoutWidth(double nWidth)
bool GetNextPos(int *nCharPos, bool *bRTL)
void SetOrientation(Degree10 nOrientation)
void SetKashidaArray(const sal_Bool *pKashidaArray)
void SetDXArray(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()
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
OUString removeMnemonicFromString(OUString const &rStr)
static OutputDevice::FontMappingUseData * fontMappingUseData
Definition: text.cxx:1241
#define TEXT_DRAW_ELLIPSIS
Definition: text.cxx:58
static bool ImplIsCharIn(sal_Unicode c, const char *pStr)
Definition: text.cxx:2005
static void TrackFontMappingUse(const vcl::Font &originalFont, const SalLayout *salLayout)
Definition: text.cxx:1248
static bool IsTrackingFontMappingUse()
Definition: text.cxx:1243
const SalLayoutFlags eDefaultLayout
Definition: text.cxx:795
sal_uInt16 sal_Unicode
sal_uInt32 sal_UCS4
Definition: vclenum.hxx:160