LCOV - code coverage report
Current view: top level - vcl/source/outdev - text.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 906 1555 58.3 %
Date: 2015-06-13 12:38:46 Functions: 42 49 85.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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 <cmath>
      23             : 
      24             : #include <sal/types.h>
      25             : #include <sal/alloca.h>
      26             : 
      27             : #include <basegfx/matrix/b2dhommatrix.hxx>
      28             : 
      29             : #include <com/sun/star/i18n/WordType.hpp>
      30             : #include <com/sun/star/i18n/XBreakIterator.hpp>
      31             : #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
      32             : 
      33             : #include <comphelper/processfactory.hxx>
      34             : 
      35             : #include <vcl/outdev.hxx>
      36             : #include <vcl/virdev.hxx>
      37             : #include <vcl/bmpacc.hxx>
      38             : #include <vcl/settings.hxx>
      39             : #include <vcl/sysdata.hxx>
      40             : #include <vcl/unohelp.hxx>
      41             : #include <vcl/controllayout.hxx>
      42             : 
      43             : #include <outdata.hxx>
      44             : #include <outdev.h>
      45             : #include <salgdi.hxx>
      46             : #include <svdata.hxx>
      47             : #include <textlayout.hxx>
      48             : 
      49             : #include <config_graphite.h>
      50             : #if ENABLE_GRAPHITE
      51             : #include "graphite_features.hxx"
      52             : #endif
      53             : 
      54             : 
      55             : #define TEXT_DRAW_ELLIPSIS  (DrawTextFlags::EndEllipsis | DrawTextFlags::PathEllipsis | DrawTextFlags::NewsEllipsis)
      56             : 
      57        9710 : ImplMultiTextLineInfo::ImplMultiTextLineInfo()
      58             : {
      59        9710 :     mpLines = new PImplTextLineInfo[MULTITEXTLINEINFO_RESIZE];
      60        9710 :     mnLines = 0;
      61        9710 :     mnSize  = MULTITEXTLINEINFO_RESIZE;
      62        9710 : }
      63             : 
      64        9710 : ImplMultiTextLineInfo::~ImplMultiTextLineInfo()
      65             : {
      66       19569 :     for( sal_Int32 i = 0; i < mnLines; i++ )
      67        9859 :         delete mpLines[i];
      68        9710 :     delete [] mpLines;
      69        9710 : }
      70             : 
      71        9859 : void ImplMultiTextLineInfo::AddLine( ImplTextLineInfo* pLine )
      72             : {
      73        9859 :     if ( mnSize == mnLines )
      74             :     {
      75           0 :         mnSize += MULTITEXTLINEINFO_RESIZE;
      76           0 :         PImplTextLineInfo* pNewLines = new PImplTextLineInfo[mnSize];
      77           0 :         memcpy( pNewLines, mpLines, mnLines*sizeof(PImplTextLineInfo) );
      78           0 :         mpLines = pNewLines;
      79             :     }
      80             : 
      81        9859 :     mpLines[mnLines] = pLine;
      82        9859 :     mnLines++;
      83        9859 : }
      84             : 
      85        9710 : void ImplMultiTextLineInfo::Clear()
      86             : {
      87        9710 :     for( sal_Int32 i = 0; i < mnLines; i++ )
      88           0 :         delete mpLines[i];
      89        9710 :     mnLines = 0;
      90        9710 : }
      91             : 
      92       53107 : void OutputDevice::ImplInitTextColor()
      93             : {
      94             :     DBG_TESTSOLARMUTEX();
      95             : 
      96       53107 :     if ( mbInitTextColor )
      97             :     {
      98       49116 :         mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) );
      99       49116 :         mbInitTextColor = false;
     100             :     }
     101       53107 : }
     102             : 
     103      111154 : void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY,
     104             :                                      long nDistX, long nDistY, long nWidth, long nHeight )
     105             : {
     106      111154 :     long nX = nDistX;
     107      111154 :     long nY = nDistY;
     108             : 
     109      111154 :     short nOrientation = mpFontEntry->mnOrientation;
     110      111154 :     if ( nOrientation )
     111             :     {
     112             :         // Rotate rect without rounding problems for 90 degree rotations
     113       38607 :         if ( !(nOrientation % 900) )
     114             :         {
     115       30994 :             if ( nOrientation == 900 )
     116             :             {
     117           0 :                 long nTemp = nX;
     118           0 :                 nX = nY;
     119           0 :                 nY = -nTemp;
     120           0 :                 nTemp = nWidth;
     121           0 :                 nWidth = nHeight;
     122           0 :                 nHeight = nTemp;
     123           0 :                 nY -= nHeight;
     124             :             }
     125       30994 :             else if ( nOrientation == 1800 )
     126             :             {
     127           0 :                 nX = -nX;
     128           0 :                 nY = -nY;
     129           0 :                 nX -= nWidth;
     130           0 :                 nY -= nHeight;
     131             :             }
     132             :             else /* ( nOrientation == 2700 ) */
     133             :             {
     134       30994 :                 long nTemp = nX;
     135       30994 :                 nX = -nY;
     136       30994 :                 nY = nTemp;
     137       30994 :                 nTemp = nWidth;
     138       30994 :                 nWidth = nHeight;
     139       30994 :                 nHeight = nTemp;
     140       30994 :                 nX -= nWidth;
     141             :             }
     142             :         }
     143             :         else
     144             :         {
     145        7613 :             nX += nBaseX;
     146        7613 :             nY += nBaseY;
     147             :             // inflate because polygons are drawn smaller
     148        7613 :             Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
     149        7613 :             Polygon   aPoly( aRect );
     150        7613 :             aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
     151        7613 :             ImplDrawPolygon( aPoly );
     152      118767 :             return;
     153             :         }
     154             :     }
     155             : 
     156      103541 :     nX += nBaseX;
     157      103541 :     nY += nBaseY;
     158      103541 :     mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this ); // original code
     159             : 
     160             : }
     161             : 
     162       11301 : void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout )
     163             : {
     164       11301 :     const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
     165       11301 :     const Point aBase = rSalLayout.DrawBase();
     166       11301 :     const long nX = aBase.X();
     167       11301 :     const long nY = aBase.Y();
     168             : 
     169       11301 :     if ( mbLineColor || mbInitLineColor )
     170             :     {
     171       11218 :         mpGraphics->SetLineColor();
     172       11218 :         mbInitLineColor = true;
     173             :     }
     174       11301 :     mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) );
     175       11301 :     mbInitFillColor = true;
     176             : 
     177       11301 :     ImplDrawTextRect( nX, nY, 0, -(mpFontEntry->maMetric.mnAscent + mnEmphasisAscent),
     178             :                       nWidth,
     179       22602 :                       mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent );
     180       11301 : }
     181             : 
     182           0 : Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout )
     183             : {
     184           0 :     Point aPoint = rSalLayout.GetDrawPosition();
     185           0 :     long nX = aPoint.X();
     186           0 :     long nY = aPoint.Y();
     187             : 
     188           0 :     long nWidth = rSalLayout.GetTextWidth();
     189           0 :     long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
     190             : 
     191           0 :     nY -= mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
     192             : 
     193           0 :     if ( mpFontEntry->mnOrientation )
     194             :     {
     195           0 :         long nBaseX = nX, nBaseY = nY;
     196           0 :         if ( !(mpFontEntry->mnOrientation % 900) )
     197             :         {
     198           0 :             long nX2 = nX+nWidth;
     199           0 :             long nY2 = nY+nHeight;
     200             : 
     201           0 :             Point aBasePt( nBaseX, nBaseY );
     202           0 :             aBasePt.RotateAround( nX, nY, mpFontEntry->mnOrientation );
     203           0 :             aBasePt.RotateAround( nX2, nY2, mpFontEntry->mnOrientation );
     204           0 :             nWidth = nX2-nX;
     205           0 :             nHeight = nY2-nY;
     206             :         }
     207             :         else
     208             :         {
     209             :             // inflate by +1+1 because polygons are drawn smaller
     210           0 :             Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
     211           0 :             Polygon   aPoly( aRect );
     212           0 :             aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
     213           0 :             return aPoly.GetBoundRect();
     214             :         }
     215             :     }
     216             : 
     217           0 :     return Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) );
     218             : }
     219             : 
     220           0 : bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout )
     221             : {
     222           0 :     int nX = rSalLayout.DrawBase().X();
     223           0 :     int nY = rSalLayout.DrawBase().Y();
     224             : 
     225           0 :     Rectangle aBoundRect;
     226           0 :     rSalLayout.DrawBase() = Point( 0, 0 );
     227           0 :     rSalLayout.DrawOffset() = Point( 0, 0 );
     228           0 :     if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) )
     229             :     {
     230             :         // guess vertical text extents if GetBoundRect failed
     231           0 :         int nRight = rSalLayout.GetTextWidth();
     232           0 :         int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
     233           0 :         long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
     234           0 :         aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop );
     235             :     }
     236             : 
     237             :     // cache virtual device for rotation
     238           0 :     if (!mpOutDevData->mpRotateDev)
     239           0 :         mpOutDevData->mpRotateDev = VclPtr<VirtualDevice>::Create( *this, 1 );
     240           0 :     VirtualDevice* pVDev = mpOutDevData->mpRotateDev;
     241             : 
     242             :     // size it accordingly
     243           0 :     if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) )
     244           0 :         return false;
     245             : 
     246           0 :     vcl::Font aFont( GetFont() );
     247           0 :     aFont.SetOrientation( 0 );
     248           0 :     aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
     249           0 :     pVDev->SetFont( aFont );
     250           0 :     pVDev->SetTextColor( Color( COL_BLACK ) );
     251           0 :     pVDev->SetTextFillColor();
     252           0 :     pVDev->ImplNewFont();
     253           0 :     pVDev->InitFont();
     254           0 :     pVDev->ImplInitTextColor();
     255             : 
     256             :     // draw text into upper left corner
     257           0 :     rSalLayout.DrawBase() -= aBoundRect.TopLeft();
     258           0 :     rSalLayout.DrawText( *static_cast<OutputDevice*>(pVDev)->mpGraphics );
     259             : 
     260           0 :     Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() );
     261           0 :     if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) )
     262           0 :         return false;
     263             : 
     264             :     // calculate rotation offset
     265           0 :     Polygon aPoly( aBoundRect );
     266           0 :     aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation );
     267           0 :     Point aPoint = aPoly.GetBoundRect().TopLeft();
     268           0 :     aPoint += Point( nX, nY );
     269             : 
     270             :     // mask output with text colored bitmap
     271           0 :     GDIMetaFile* pOldMetaFile = mpMetaFile;
     272           0 :     long nOldOffX = mnOutOffX;
     273           0 :     long nOldOffY = mnOutOffY;
     274           0 :     bool bOldMap = mbMap;
     275             : 
     276           0 :     mnOutOffX   = 0L;
     277           0 :     mnOutOffY   = 0L;
     278           0 :     mpMetaFile  = NULL;
     279           0 :     EnableMapMode( false );
     280             : 
     281           0 :     DrawMask( aPoint, aBmp, GetTextColor() );
     282             : 
     283           0 :     EnableMapMode( bOldMap );
     284           0 :     mnOutOffX   = nOldOffX;
     285           0 :     mnOutOffY   = nOldOffY;
     286           0 :     mpMetaFile  = pOldMetaFile;
     287             : 
     288           0 :     return true;
     289             : }
     290             : 
     291      244981 : bool OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout,
     292             :                                        bool bTextLines,
     293             :                                        sal_uInt32 flags )
     294             : {
     295      244981 :     if( mpFontEntry->mnOwnOrientation )
     296           0 :         if( ImplDrawRotateText( rSalLayout ) )
     297           0 :             return true;
     298             : 
     299             : 
     300             : 
     301             : 
     302      244981 :     long nOldX = rSalLayout.DrawBase().X();
     303      244981 :     if( HasMirroredGraphics() )
     304             :     {
     305         654 :         long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
     306         654 :         long x = rSalLayout.DrawBase().X();
     307         654 :            rSalLayout.DrawBase().X() = w - 1 - x;
     308         654 :         if( !IsRTLEnabled() )
     309             :         {
     310           0 :             OutputDevice *pOutDevRef = this;
     311             :             // mirror this window back
     312           0 :             long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX;   // re-mirrored mnOutOffX
     313           0 :             rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ;
     314             :         }
     315             :     }
     316      244327 :     else if( IsRTLEnabled() )
     317             :     {
     318           0 :         OutputDevice *pOutDevRef = this;
     319             : 
     320             :         // mirror this window back
     321           0 :         long devX = pOutDevRef->mnOutOffX;   // re-mirrored mnOutOffX
     322           0 :         rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX;
     323             :     }
     324             : 
     325      244981 :     if(flags)
     326             :     {
     327         285 :         if( ! rSalLayout.DrawTextSpecial( *mpGraphics, flags ))
     328             :         {
     329         285 :             rSalLayout.DrawBase().X() = nOldX;
     330         285 :             return false;
     331             :         }
     332             :     }
     333             :     else
     334             :     {
     335      244696 :         rSalLayout.DrawText( *mpGraphics );
     336             :     }
     337      244696 :     rSalLayout.DrawBase().X() = nOldX;
     338             : 
     339      244696 :     if( bTextLines )
     340             :         ImplDrawTextLines( rSalLayout,
     341             :             maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(),
     342       14306 :             maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) );
     343             : 
     344             : 
     345             :     // emphasis marks
     346      244696 :     if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
     347       11976 :         ImplDrawEmphasisMarks( rSalLayout );
     348             : 
     349      244696 :     return true;
     350             : }
     351             : 
     352        5517 : void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout )
     353             : {
     354        5517 :     Color       aOldColor           = GetTextColor();
     355        5517 :     Color       aOldTextLineColor   = GetTextLineColor();
     356        5517 :     Color       aOldOverlineColor   = GetOverlineColor();
     357        5517 :     FontRelief  eRelief             = maFont.GetRelief();
     358             : 
     359        5517 :     Point aOrigPos = rSalLayout.DrawBase();
     360        5517 :     if ( eRelief != RELIEF_NONE )
     361             :     {
     362        5004 :         Color   aReliefColor( COL_LIGHTGRAY );
     363        5004 :         Color   aTextColor( aOldColor );
     364             : 
     365        5004 :         Color   aTextLineColor( aOldTextLineColor );
     366        5004 :         Color   aOverlineColor( aOldOverlineColor );
     367             : 
     368             :         // we don't have a automatic color, so black is always drawn on white
     369        5004 :         if ( aTextColor.GetColor() == COL_BLACK )
     370        1355 :             aTextColor = Color( COL_WHITE );
     371        5004 :         if ( aTextLineColor.GetColor() == COL_BLACK )
     372        1052 :             aTextLineColor = Color( COL_WHITE );
     373        5004 :         if ( aOverlineColor.GetColor() == COL_BLACK )
     374        1052 :             aOverlineColor = Color( COL_WHITE );
     375             : 
     376             :         // relief-color is black for white text, in all other cases
     377             :         // we set this to LightGray
     378        5004 :         if ( aTextColor.GetColor() == COL_WHITE )
     379        1355 :             aReliefColor = Color( COL_BLACK );
     380        5004 :         SetTextLineColor( aReliefColor );
     381        5004 :         SetOverlineColor( aReliefColor );
     382        5004 :         SetTextColor( aReliefColor );
     383        5004 :         ImplInitTextColor();
     384             : 
     385             :         // calculate offset - for high resolution printers the offset
     386             :         // should be greater so that the effect is visible
     387        5004 :         long nOff = 1;
     388        5004 :         nOff += mnDPIX/300;
     389             : 
     390        5004 :         if ( eRelief == RELIEF_ENGRAVED )
     391        1697 :             nOff = -nOff;
     392        5004 :         rSalLayout.DrawOffset() += Point( nOff, nOff);
     393        5004 :         ImplDrawTextDirect( rSalLayout, mbTextLines );
     394        5004 :         rSalLayout.DrawOffset() -= Point( nOff, nOff);
     395             : 
     396        5004 :         SetTextLineColor( aTextLineColor );
     397        5004 :         SetOverlineColor( aOverlineColor );
     398        5004 :         SetTextColor( aTextColor );
     399        5004 :         ImplInitTextColor();
     400        5004 :         ImplDrawTextDirect( rSalLayout, mbTextLines );
     401             : 
     402        5004 :         SetTextLineColor( aOldTextLineColor );
     403        5004 :         SetOverlineColor( aOldOverlineColor );
     404             : 
     405        5004 :         if ( aTextColor != aOldColor )
     406             :         {
     407        1355 :             SetTextColor( aOldColor );
     408        1355 :             ImplInitTextColor();
     409             :         }
     410             :     }
     411             :     else
     412             :     {
     413         513 :         if ( maFont.IsShadow() )
     414             :         {
     415         418 :             long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24);
     416         418 :             if ( maFont.IsOutline() )
     417         190 :                 nOff++;
     418         418 :             SetTextLineColor();
     419         418 :             SetOverlineColor();
     420         836 :             if ( (GetTextColor().GetColor() == COL_BLACK)
     421         418 :             ||   (GetTextColor().GetLuminance() < 8) )
     422         418 :                 SetTextColor( Color( COL_LIGHTGRAY ) );
     423             :             else
     424           0 :                 SetTextColor( Color( COL_BLACK ) );
     425         418 :             ImplInitTextColor();
     426         418 :             rSalLayout.DrawBase() += Point( nOff, nOff );
     427         418 :             ImplDrawTextDirect( rSalLayout, mbTextLines );
     428         418 :             rSalLayout.DrawBase() -= Point( nOff, nOff );
     429         418 :             SetTextColor( aOldColor );
     430         418 :             SetTextLineColor( aOldTextLineColor );
     431         418 :             SetOverlineColor( aOldOverlineColor );
     432         418 :             ImplInitTextColor();
     433             : 
     434         418 :             if ( !maFont.IsOutline() )
     435         228 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     436             :         }
     437             : 
     438         513 :         if ( maFont.IsOutline() )
     439             :         {
     440         285 :             if(! ImplDrawTextDirect( rSalLayout, mbTextLines, DRAWTEXT_F_OUTLINE))
     441             :             {
     442         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(-1,-1);
     443         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     444         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(+1,+1);
     445         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     446         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(-1,+0);
     447         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     448         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(-1,+1);
     449         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     450         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(+0,+1);
     451         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     452         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(+0,-1);
     453         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     454         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(+1,-1);
     455         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     456         285 :                 rSalLayout.DrawBase() = aOrigPos + Point(+1,+0);
     457         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     458         285 :                 rSalLayout.DrawBase() = aOrigPos;
     459             : 
     460         285 :                 SetTextColor( Color( COL_WHITE ) );
     461         285 :                 SetTextLineColor( Color( COL_WHITE ) );
     462         285 :                 SetOverlineColor( Color( COL_WHITE ) );
     463         285 :                 ImplInitTextColor();
     464         285 :                 ImplDrawTextDirect( rSalLayout, mbTextLines );
     465         285 :                 SetTextColor( aOldColor );
     466         285 :                 SetTextLineColor( aOldTextLineColor );
     467         285 :                 SetOverlineColor( aOldOverlineColor );
     468         285 :                 ImplInitTextColor();
     469             :             }
     470             :         }
     471             :     }
     472        5517 : }
     473             : 
     474      240227 : void OutputDevice::ImplDrawText( SalLayout& rSalLayout )
     475             : {
     476             : 
     477      240227 :     if( mbInitClipRegion )
     478        1584 :         InitClipRegion();
     479      240227 :     if( mbOutputClipped )
     480      243460 :         return;
     481      236994 :     if( mbInitTextColor )
     482       37702 :         ImplInitTextColor();
     483             : 
     484      236994 :     rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY );
     485             : 
     486      236994 :     if( IsTextFillColor() )
     487       11301 :         ImplDrawTextBackground( rSalLayout );
     488             : 
     489      236994 :     if( mbTextSpecial )
     490        5517 :         ImplDrawSpecialText( rSalLayout );
     491             :     else
     492      231477 :         ImplDrawTextDirect( rSalLayout, mbTextLines );
     493             : }
     494             : 
     495        9710 : long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo,
     496             :                                      long nWidth, const OUString& rStr,
     497             :                                      DrawTextFlags nStyle, const vcl::ITextLayout& _rLayout )
     498             : {
     499             :     DBG_ASSERTWARNING( nWidth >= 0, "ImplGetTextLines: nWidth <= 0!" );
     500             : 
     501        9710 :     if ( nWidth <= 0 )
     502           0 :         nWidth = 1;
     503             : 
     504        9710 :     long nMaxLineWidth  = 0;
     505        9710 :     rLineInfo.Clear();
     506        9710 :     if ( !rStr.isEmpty() && (nWidth > 0) )
     507             :     {
     508        9347 :         css::uno::Reference < css::i18n::XBreakIterator > xBI;
     509             :         // get service provider
     510       18694 :         css::uno::Reference< css::uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
     511             : 
     512       18694 :         bool bHyphenate = (nStyle & DrawTextFlags::WordBreakHyphenation)
     513        9347 :             == DrawTextFlags::WordBreakHyphenation;
     514       18694 :         css::uno::Reference< css::linguistic2::XHyphenator > xHyph;
     515        9347 :         if ( bHyphenate )
     516             :         {
     517           0 :             css::uno::Reference< css::linguistic2::XLinguServiceManager2> xLinguMgr = css::linguistic2::LinguServiceManager::create(xContext);
     518           0 :             xHyph = xLinguMgr->getHyphenator();
     519             :         }
     520             : 
     521        9347 :         sal_Int32 nPos = 0;
     522        9347 :         sal_Int32 nLen = rStr.getLength();
     523       28553 :         while ( nPos < nLen )
     524             :         {
     525        9859 :             sal_Int32 nBreakPos = nPos;
     526             : 
     527      143025 :             while ( ( nBreakPos < nLen ) && ( rStr[ nBreakPos ] != '\r' ) && ( rStr[ nBreakPos ] != '\n' ) )
     528      123307 :                 nBreakPos++;
     529             : 
     530        9859 :             long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
     531        9859 :             if ( ( nLineWidth > nWidth ) && ( nStyle & DrawTextFlags::WordBreak ) )
     532             :             {
     533         512 :                 if ( !xBI.is() )
     534         256 :                     xBI = vcl::unohelper::CreateBreakIterator();
     535             : 
     536         512 :                 if ( xBI.is() )
     537             :                 {
     538         512 :                     const css::lang::Locale& rDefLocale(Application::GetSettings().GetUILanguageTag().getLocale());
     539         512 :                     sal_Int32 nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos );
     540         512 :                     if (nSoftBreak == -1)
     541             :                     {
     542           0 :                         nSoftBreak = nPos;
     543             :                     }
     544             :                     DBG_ASSERT( nSoftBreak < nBreakPos, "Break?!" );
     545         512 :                     css::i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, css::uno::Sequence <css::beans::PropertyValue>(), 1 );
     546        1024 :                     css::i18n::LineBreakUserOptions aUserOptions;
     547        1024 :                     css::i18n::LineBreakResults aLBR = xBI->getLineBreak( rStr, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions );
     548         512 :                     nBreakPos = aLBR.breakIndex;
     549         512 :                     if ( nBreakPos <= nPos )
     550           0 :                         nBreakPos = nSoftBreak;
     551         512 :                     if ( bHyphenate )
     552             :                     {
     553             :                         // Whether hyphen or not: Put the word after the hyphen through
     554             :                         // word boundary.
     555             : 
     556             :                         // nMaxBreakPos the last char that fits into the line
     557             :                         // nBreakPos is the word's start
     558             : 
     559             :                         // We run into a problem if the doc is so narrow, that a word
     560             :                         // is broken into more than two lines ...
     561           0 :                         if ( xHyph.is() )
     562             :                         {
     563           0 :                             sal_Unicode cAlternateReplChar = 0;
     564           0 :                             css::i18n::Boundary aBoundary = xBI->getWordBoundary( rStr, nBreakPos, rDefLocale, css::i18n::WordType::DICTIONARY_WORD, sal_True );
     565           0 :                             sal_Int32 nWordStart = nPos;
     566           0 :                             sal_Int32 nWordEnd = aBoundary.endPos;
     567             :                             DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
     568             : 
     569           0 :                             sal_Int32 nWordLen = nWordEnd - nWordStart;
     570           0 :                             if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) )
     571             :                             {
     572             :                                 // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
     573             :                                 // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" );
     574           0 :                                 OUString aWord = rStr.copy( nWordStart, nWordLen );
     575           0 :                                 sal_Int32 nMinTrail = nWordEnd-nSoftBreak+1;  //+1: Before the "broken off" char
     576           0 :                                 css::uno::Reference< css::linguistic2::XHyphenatedWord > xHyphWord;
     577           0 :                                 if (xHyph.is())
     578           0 :                                     xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.getLength() - nMinTrail, css::uno::Sequence< css::beans::PropertyValue >() );
     579           0 :                                 if (xHyphWord.is())
     580             :                                 {
     581           0 :                                     bool bAlternate = xHyphWord->isAlternativeSpelling();
     582           0 :                                     sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos();
     583             : 
     584           0 :                                     if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= ( 2 ) ) )
     585             :                                     {
     586           0 :                                         if ( !bAlternate )
     587             :                                         {
     588           0 :                                             nBreakPos = nWordStart + _nWordLen;
     589             :                                         }
     590             :                                         else
     591             :                                         {
     592           0 :                                             OUString aAlt( xHyphWord->getHyphenatedWord() );
     593             : 
     594             :                                             // We can have two cases:
     595             :                                             // 1) "packen" turns into "pak-ken"
     596             :                                             // 2) "Schiffahrt" turns into "Schiff-fahrt"
     597             : 
     598             :                                             // In case 1 we need to replace a char
     599             :                                             // In case 2 we add a char
     600             : 
     601             :                                             // Correct recognition is made harder by words such as
     602             :                                             // "Schiffahrtsbrennesseln", as the Hyphenator splits all
     603             :                                             // positions of the word and comes up with "Schifffahrtsbrennnesseln"
     604             :                                             // Thus, we cannot infer the aWord from the AlternativWord's
     605             :                                             // index.
     606             :                                             // TODO: The whole junk will be made easier by a function in
     607             :                                             // the Hyphenator, as soon as AMA adds it.
     608           0 :                                             sal_Int32 nAltStart = _nWordLen - 1;
     609           0 :                                             sal_Int32 nTxtStart = nAltStart - (aAlt.getLength() - aWord.getLength());
     610           0 :                                             sal_Int32 nTxtEnd = nTxtStart;
     611           0 :                                             sal_Int32 nAltEnd = nAltStart;
     612             : 
     613             :                                             // The area between nStart and nEnd is the difference
     614             :                                             // between AlternativString and OriginalString
     615           0 :                                             while( nTxtEnd < aWord.getLength() && nAltEnd < aAlt.getLength() &&
     616           0 :                                                    aWord[nTxtEnd] != aAlt[nAltEnd] )
     617             :                                             {
     618           0 :                                                 ++nTxtEnd;
     619           0 :                                                 ++nAltEnd;
     620             :                                             }
     621             : 
     622             :                                             // If a char was added, we notice it now:
     623           0 :                                             if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
     624           0 :                                                 aWord[ nTxtEnd ] == aAlt[nAltEnd] )
     625             :                                             {
     626           0 :                                                 ++nAltEnd;
     627           0 :                                                 ++nTxtStart;
     628           0 :                                                 ++nTxtEnd;
     629             :                                             }
     630             : 
     631             :                                             DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Wrong assumption!" );
     632             : 
     633           0 :                                             if ( nTxtEnd > nTxtStart )
     634           0 :                                                 cAlternateReplChar = aAlt[ nAltStart ];
     635             : 
     636           0 :                                             nBreakPos = nWordStart + nTxtStart;
     637           0 :                                             if ( cAlternateReplChar )
     638           0 :                                                 nBreakPos++;
     639             :                                         }
     640             :                                     }
     641           0 :                                 }
     642             :                             }
     643             :                         }
     644             :                     }
     645        1024 :                     nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
     646             :                 }
     647             :                 else
     648             :                 {
     649             :                     // fallback to something really simple
     650           0 :                     sal_Int32 nSpacePos = rStr.getLength();
     651           0 :                     long nW = 0;
     652           0 :                     do
     653             :                     {
     654           0 :                         nSpacePos = rStr.lastIndexOf( ' ', nSpacePos );
     655           0 :                         if( nSpacePos != -1 )
     656             :                         {
     657           0 :                             if( nSpacePos > nPos )
     658           0 :                                 nSpacePos--;
     659           0 :                             nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos );
     660             :                         }
     661             :                     } while( nW > nWidth );
     662             : 
     663           0 :                     if( nSpacePos != -1 )
     664             :                     {
     665           0 :                         nBreakPos = nSpacePos;
     666           0 :                         nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
     667           0 :                         if( nBreakPos < rStr.getLength()-1 )
     668           0 :                             nBreakPos++;
     669             :                     }
     670             :                 }
     671             :             }
     672             : 
     673        9859 :             if ( nLineWidth > nMaxLineWidth )
     674        9603 :                 nMaxLineWidth = nLineWidth;
     675             : 
     676        9859 :             rLineInfo.AddLine( new ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) );
     677             : 
     678        9859 :             if ( nBreakPos == nPos )
     679           0 :                 nBreakPos++;
     680        9859 :             nPos = nBreakPos;
     681             : 
     682        9859 :             if ( nPos < nLen && ( ( rStr[ nPos ] == '\r' ) || ( rStr[ nPos ] == '\n' ) ) )
     683             :             {
     684           0 :                 nPos++;
     685             :                 // CR/LF?
     686           0 :                 if ( ( nPos < nLen ) && ( rStr[ nPos ] == '\n' ) && ( rStr[ nPos-1 ] == '\r' ) )
     687           0 :                     nPos++;
     688             :             }
     689        9347 :         }
     690             :     }
     691             : #ifdef DBG_UTIL
     692             :     for ( sal_Int32 nL = 0; nL < rLineInfo.Count(); nL++ )
     693             :     {
     694             :         ImplTextLineInfo* pLine = rLineInfo.GetLine( nL );
     695             :         OUString aLine = rStr.copy( pLine->GetIndex(), pLine->GetLen() );
     696             :         DBG_ASSERT( aLine.indexOf( '\r' ) == -1, "ImplGetTextLines - Found CR!" );
     697             :         DBG_ASSERT( aLine.indexOf( '\n' ) == -1, "ImplGetTextLines - Found LF!" );
     698             :     }
     699             : #endif
     700             : 
     701        9710 :     return nMaxLineWidth;
     702             : }
     703             : 
     704      764992 : void OutputDevice::SetTextColor( const Color& rColor )
     705             : {
     706             : 
     707      764992 :     Color aColor( rColor );
     708             : 
     709      764992 :     if ( mnDrawMode & ( DrawModeFlags::BlackText | DrawModeFlags::WhiteText |
     710             :                         DrawModeFlags::GrayText | DrawModeFlags::GhostedText |
     711      764992 :                         DrawModeFlags::SettingsText ) )
     712             :     {
     713           9 :         if ( mnDrawMode & DrawModeFlags::BlackText )
     714           0 :             aColor = Color( COL_BLACK );
     715           9 :         else if ( mnDrawMode & DrawModeFlags::WhiteText )
     716           9 :             aColor = Color( COL_WHITE );
     717           0 :         else if ( mnDrawMode & DrawModeFlags::GrayText )
     718             :         {
     719           0 :             const sal_uInt8 cLum = aColor.GetLuminance();
     720           0 :             aColor = Color( cLum, cLum, cLum );
     721             :         }
     722           0 :         else if ( mnDrawMode & DrawModeFlags::SettingsText )
     723           0 :             aColor = GetSettings().GetStyleSettings().GetFontColor();
     724             : 
     725           9 :         if ( mnDrawMode & DrawModeFlags::GhostedText )
     726             :         {
     727           0 :             aColor = Color( (aColor.GetRed() >> 1) | 0x80,
     728           0 :                             (aColor.GetGreen() >> 1) | 0x80,
     729           0 :                             (aColor.GetBlue() >> 1) | 0x80 );
     730             :         }
     731             :     }
     732             : 
     733      764992 :     if ( mpMetaFile )
     734       54277 :         mpMetaFile->AddAction( new MetaTextColorAction( aColor ) );
     735             : 
     736      764992 :     if ( maTextColor != aColor )
     737             :     {
     738       26471 :         maTextColor = aColor;
     739       26471 :         mbInitTextColor = true;
     740             :     }
     741             : 
     742      764992 :     if( mpAlphaVDev )
     743       33052 :         mpAlphaVDev->SetTextColor( COL_BLACK );
     744      764992 : }
     745             : 
     746      334129 : void OutputDevice::SetTextFillColor()
     747             : {
     748             : 
     749      334129 :     if ( mpMetaFile )
     750       13208 :         mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), false ) );
     751             : 
     752      334129 :     if ( maFont.GetColor() != Color( COL_TRANSPARENT ) ) {
     753        3768 :         maFont.SetFillColor( Color( COL_TRANSPARENT ) );
     754             :     }
     755      334129 :     if ( !maFont.IsTransparent() )
     756           0 :         maFont.SetTransparent( true );
     757             : 
     758      334129 :     if( mpAlphaVDev )
     759       39206 :         mpAlphaVDev->SetTextFillColor();
     760      334129 : }
     761             : 
     762       20370 : void OutputDevice::SetTextFillColor( const Color& rColor )
     763             : {
     764             : 
     765       20370 :     Color aColor( rColor );
     766       20370 :     bool bTransFill = ImplIsColorTransparent( aColor );
     767             : 
     768       20370 :     if ( !bTransFill )
     769             :     {
     770       14456 :         if ( mnDrawMode & ( DrawModeFlags::BlackFill | DrawModeFlags::WhiteFill |
     771             :                             DrawModeFlags::GrayFill | DrawModeFlags::NoFill |
     772       14456 :                             DrawModeFlags::GhostedFill | DrawModeFlags::SettingsFill ) )
     773             :         {
     774           0 :             if ( mnDrawMode & DrawModeFlags::BlackFill )
     775           0 :                 aColor = Color( COL_BLACK );
     776           0 :             else if ( mnDrawMode & DrawModeFlags::WhiteFill )
     777           0 :                 aColor = Color( COL_WHITE );
     778           0 :             else if ( mnDrawMode & DrawModeFlags::GrayFill )
     779             :             {
     780           0 :                 const sal_uInt8 cLum = aColor.GetLuminance();
     781           0 :                 aColor = Color( cLum, cLum, cLum );
     782             :             }
     783           0 :             else if( mnDrawMode & DrawModeFlags::SettingsFill )
     784           0 :                 aColor = GetSettings().GetStyleSettings().GetWindowColor();
     785           0 :             else if ( mnDrawMode & DrawModeFlags::NoFill )
     786             :             {
     787           0 :                 aColor = Color( COL_TRANSPARENT );
     788           0 :                 bTransFill = true;
     789             :             }
     790             : 
     791           0 :             if ( !bTransFill && (mnDrawMode & DrawModeFlags::GhostedFill) )
     792             :             {
     793           0 :                 aColor = Color( (aColor.GetRed() >> 1) | 0x80,
     794           0 :                                 (aColor.GetGreen() >> 1) | 0x80,
     795           0 :                                 (aColor.GetBlue() >> 1) | 0x80 );
     796             :             }
     797             :         }
     798             :     }
     799             : 
     800       20370 :     if ( mpMetaFile )
     801        3294 :         mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, true ) );
     802             : 
     803       20370 :     if ( maFont.GetFillColor() != aColor )
     804       11745 :         maFont.SetFillColor( aColor );
     805       20370 :     if ( maFont.IsTransparent() != bTransFill )
     806       11305 :         maFont.SetTransparent( bTransFill );
     807             : 
     808       20370 :     if( mpAlphaVDev )
     809        3670 :         mpAlphaVDev->SetTextFillColor( COL_BLACK );
     810       20370 : }
     811             : 
     812       46828 : Color OutputDevice::GetTextFillColor() const
     813             : {
     814       46828 :     if ( maFont.IsTransparent() )
     815       34259 :         return Color( COL_TRANSPARENT );
     816             :     else
     817       12569 :         return maFont.GetFillColor();
     818             : }
     819             : 
     820      165040 : void OutputDevice::SetTextAlign( TextAlign eAlign )
     821             : {
     822             : 
     823      165040 :     if ( mpMetaFile )
     824       12983 :         mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) );
     825             : 
     826      165040 :     if ( maFont.GetAlign() != eAlign )
     827             :     {
     828         939 :         maFont.SetAlign( eAlign );
     829         939 :         mbNewFont = true;
     830             :     }
     831             : 
     832      165040 :     if( mpAlphaVDev )
     833       39372 :         mpAlphaVDev->SetTextAlign( eAlign );
     834      165040 : }
     835             : 
     836      203829 : void OutputDevice::DrawText( const Point& rStartPt, const OUString& rStr,
     837             :                              sal_Int32 nIndex, sal_Int32 nLen,
     838             :                              MetricVector* pVector, OUString* pDisplayText
     839             :                              )
     840             : {
     841      203829 :     assert_if_double_buffered_window();
     842             : 
     843      203829 :     if(nLen == 0x0FFFF)
     844             :     {
     845             :         SAL_INFO("sal.rtl.xub",
     846             :                  "GetTextOutlines Suspicious arguments nLen:" << nLen);
     847             :     }
     848      203829 :     if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
     849             :     {
     850      203561 :         nLen = rStr.getLength() - nIndex;
     851             :     }
     852             : 
     853             : 
     854      203829 :     if (mpOutDevData->mpRecordLayout)
     855             :     {
     856        2460 :         pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
     857        2460 :         pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
     858             :     }
     859             : 
     860             : #if OSL_DEBUG_LEVEL > 2
     861             :     fprintf( stderr, "   OutputDevice::DrawText(\"%s\")\n",
     862             :          OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ).getStr() );
     863             : #endif
     864             : 
     865      203829 :     if ( mpMetaFile )
     866        4419 :         mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
     867      203829 :     if( pVector )
     868             :     {
     869        2492 :         vcl::Region aClip( GetClipRegion() );
     870        2492 :         if( meOutDevType == OUTDEV_WINDOW )
     871        2492 :             aClip.Intersect( Rectangle( Point(), GetOutputSize() ) );
     872        2492 :         if (mpOutDevData->mpRecordLayout)
     873             :         {
     874        2460 :             mpOutDevData->mpRecordLayout->m_aLineIndices.push_back( mpOutDevData->mpRecordLayout->m_aDisplayText.getLength() );
     875        2460 :             aClip.Intersect( mpOutDevData->maRecordRect );
     876             :         }
     877        2492 :         if( ! aClip.IsNull() )
     878             :         {
     879        2492 :             MetricVector aTmp;
     880        2492 :             GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp );
     881             : 
     882        2492 :             bool bInserted = false;
     883       15508 :             for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ )
     884             :             {
     885       13016 :                 bool bAppend = false;
     886             : 
     887       13016 :                 if( aClip.IsOver( *it ) )
     888        3944 :                     bAppend = true;
     889        9072 :                 else if( rStr[ nIndex ] == ' ' && bInserted )
     890             :                 {
     891         625 :                     MetricVector::const_iterator next = it;
     892         625 :                     ++next;
     893         625 :                     if( next != aTmp.end() && aClip.IsOver( *next ) )
     894         625 :                         bAppend = true;
     895             :                 }
     896             : 
     897       13016 :                 if( bAppend )
     898             :                 {
     899        4569 :                     pVector->push_back( *it );
     900        4569 :                     if( pDisplayText )
     901        4569 :                         *pDisplayText += OUString(rStr[ nIndex ]);
     902        4569 :                     bInserted = true;
     903             :                 }
     904        2492 :             }
     905             :         }
     906             :         else
     907             :         {
     908           0 :             GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector );
     909           0 :             if( pDisplayText )
     910           0 :                 *pDisplayText += rStr.copy( nIndex, nLen );
     911        2492 :         }
     912             :     }
     913             : 
     914      203829 :     if ( !IsDeviceOutputNecessary() || pVector )
     915      207718 :         return;
     916             : 
     917      199940 :     SalLayout* pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, NULL);
     918      199940 :     if( pSalLayout )
     919             :     {
     920      199940 :         ImplDrawText( *pSalLayout );
     921      199940 :         pSalLayout->Release();
     922             :     }
     923             : 
     924      199940 :     if( mpAlphaVDev )
     925        4098 :         mpAlphaVDev->DrawText( rStartPt, rStr, nIndex, nLen, pVector, pDisplayText );
     926             : }
     927             : 
     928      904658 : long OutputDevice::GetTextWidth( const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen,
     929             :      vcl::TextLayoutCache const*const pLayoutCache) const
     930             : {
     931             : 
     932      904658 :     long nWidth = GetTextArray( rStr, NULL, nIndex, nLen, pLayoutCache );
     933             : 
     934      904658 :     return nWidth;
     935             : }
     936             : 
     937      891208 : long OutputDevice::GetTextHeight() const
     938             : {
     939             : 
     940      891208 :     if( mbNewFont )
     941      461178 :         if( !ImplNewFont() )
     942           0 :             return 0;
     943      891208 :     if( mbInitFont )
     944      426163 :         if( !ImplNewFont() )
     945           0 :             return 0;
     946             : 
     947      891208 :     long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
     948             : 
     949      891208 :     if ( mbMap )
     950      622137 :         nHeight = ImplDevicePixelToLogicHeight( nHeight );
     951             : 
     952      891208 :     return nHeight;
     953             : }
     954             : 
     955         379 : float OutputDevice::approximate_char_width() const
     956             : {
     957         379 :     return GetTextWidth("aemnnxEM") / 8.0;
     958             : }
     959             : 
     960       40093 : void OutputDevice::DrawTextArray( const Point& rStartPt, const OUString& rStr,
     961             :                                   const long* pDXAry,
     962             :                                   sal_Int32 nIndex, sal_Int32 nLen, SalLayoutFlags flags )
     963             : {
     964       40093 :     assert_if_double_buffered_window();
     965             : 
     966       40093 :     if(nLen == 0x0FFFF)
     967             :     {
     968             :         SAL_INFO("sal.rtl.xub",
     969             :                  "DrawTextArray Suspicious arguments nLen:" << nLen);
     970             :     }
     971       40093 :     if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
     972             :     {
     973       24514 :         nLen = rStr.getLength() - nIndex;
     974             :     }
     975       40093 :     if ( mpMetaFile )
     976        1802 :         mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
     977             : 
     978       40093 :     if ( !IsDeviceOutputNecessary() )
     979        1369 :         return;
     980       38724 :     if( !mpGraphics && !AcquireGraphics() )
     981           0 :         return;
     982       38724 :     if( mbInitClipRegion )
     983        5761 :         InitClipRegion();
     984       38724 :     if( mbOutputClipped )
     985         525 :         return;
     986             : 
     987       38199 :     SalLayout* pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, pDXAry, flags);
     988       38199 :     if( pSalLayout )
     989             :     {
     990       38199 :         ImplDrawText( *pSalLayout );
     991       38199 :         pSalLayout->Release();
     992             :     }
     993             : 
     994       38199 :     if( mpAlphaVDev )
     995         433 :         mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen, flags );
     996             : }
     997             : 
     998     1185328 : long OutputDevice::GetTextArray( const OUString& rStr, long* pDXAry,
     999             :                                  sal_Int32 nIndex, sal_Int32 nLen,
    1000             :                                  vcl::TextLayoutCache const*const pLayoutCache) const
    1001             : {
    1002     1185328 :     if(nLen == 0x0FFFF)
    1003             :     {
    1004             :         SAL_INFO("sal.rtl.xub",
    1005             :                  "GetTextArray Suspicious arguments nLen:" << nLen);
    1006             :     }
    1007             : 
    1008     1185328 :     if( nIndex >= rStr.getLength() )
    1009      282673 :         return 0;
    1010             : 
    1011      902655 :     if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
    1012             :     {
    1013      799873 :         nLen = rStr.getLength() - nIndex;
    1014             :     }
    1015             :     // do layout
    1016             :     SalLayout *const pSalLayout = ImplLayout(rStr, nIndex, nLen,
    1017      902655 :             Point(0,0), 0, nullptr, SalLayoutFlags::NONE, pLayoutCache);
    1018      902655 :     if( !pSalLayout )
    1019           0 :         return 0;
    1020             : #if VCL_FLOAT_DEVICE_PIXEL
    1021             :     DeviceCoordinate* pDXPixelArray = NULL;
    1022             :     if(pDXAry)
    1023             :     {
    1024             :         pDXPixelArray = static_cast<DeviceCoordinate*>(alloca(nLen * sizeof(DeviceCoordinate)));
    1025             :     }
    1026             :     DeviceCoordinate nWidth = pSalLayout->FillDXArray( pDXPixelArray );
    1027             :     int nWidthFactor = pSalLayout->GetUnitsPerPixel();
    1028             :     pSalLayout->Release();
    1029             : 
    1030             :     // convert virtual char widths to virtual absolute positions
    1031             :     if( pDXPixelArray )
    1032             :     {
    1033             :         for( int i = 1; i < nLen; ++i )
    1034             :         {
    1035             :             pDXPixelArray[ i ] += pDXPixelArray[ i-1 ];
    1036             :         }
    1037             :     }
    1038             :     if( mbMap )
    1039             :     {
    1040             :         if( pDXPixelArray )
    1041             :         {
    1042             :             for( int i = 0; i < nLen; ++i )
    1043             :             {
    1044             :                 pDXPixelArray[i] = ImplDevicePixelToLogicWidth( pDXPixelArray[i] );
    1045             :             }
    1046             :         }
    1047             :         nWidth = ImplDevicePixelToLogicWidth( nWidth );
    1048             :     }
    1049             :     if( nWidthFactor > 1 )
    1050             :     {
    1051             :         if( pDXPixelArray )
    1052             :         {
    1053             :             for( int i = 0; i < nLen; ++i )
    1054             :             {
    1055             :                 pDXPixelArray[i] /= nWidthFactor;
    1056             :             }
    1057             :         }
    1058             :         nWidth /= nWidthFactor;
    1059             :     }
    1060             :     if(pDXAry)
    1061             :     {
    1062             :         for( int i = 0; i < nLen; ++i )
    1063             :         {
    1064             :             pDXAry[i] = basegfx::fround(pDXPixelArray[i]);
    1065             :         }
    1066             :     }
    1067             :     return basegfx::fround(nWidth);
    1068             : 
    1069             : #else /* ! VCL_FLOAT_DEVICE_PIXEL */
    1070             : 
    1071      902655 :     long nWidth = pSalLayout->FillDXArray( pDXAry );
    1072      902655 :     int nWidthFactor = pSalLayout->GetUnitsPerPixel();
    1073      902655 :     pSalLayout->Release();
    1074             : 
    1075             :     // convert virtual char widths to virtual absolute positions
    1076      902655 :     if( pDXAry )
    1077     3354228 :         for( int i = 1; i < nLen; ++i )
    1078     3081505 :             pDXAry[ i ] += pDXAry[ i-1 ];
    1079             : 
    1080             :     // convert from font units to logical units
    1081      902655 :     if( mbMap )
    1082             :     {
    1083      405124 :         if( pDXAry )
    1084     3621769 :             for( int i = 0; i < nLen; ++i )
    1085     3350141 :                 pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] );
    1086      405124 :         nWidth = ImplDevicePixelToLogicWidth( nWidth );
    1087             :     }
    1088             : 
    1089      902655 :     if( nWidthFactor > 1 )
    1090             :     {
    1091          45 :         if( pDXAry )
    1092        2093 :             for( int i = 0; i < nLen; ++i )
    1093        2050 :                 pDXAry[i] /= nWidthFactor;
    1094          45 :         nWidth /= nWidthFactor;
    1095             :     }
    1096      902655 :     return nWidth;
    1097             : #endif /* VCL_FLOAT_DEVICE_PIXEL */
    1098             : }
    1099             : 
    1100       19412 : bool OutputDevice::GetCaretPositions( const OUString& rStr, long* pCaretXArray,
    1101             :                                       sal_Int32 nIndex, sal_Int32 nLen,
    1102             :                                       long* pDXAry, long nLayoutWidth,
    1103             :                                       bool bCellBreaking ) const
    1104             : {
    1105             : 
    1106       19412 :     if( nIndex >= rStr.getLength() )
    1107           0 :         return false;
    1108       19412 :     if( nIndex+nLen >= rStr.getLength() )
    1109       19412 :         nLen = rStr.getLength() - nIndex;
    1110             : 
    1111             :     // layout complex text
    1112             :     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen,
    1113       19412 :                                         Point(0,0), nLayoutWidth, pDXAry );
    1114       19412 :     if( !pSalLayout )
    1115           0 :         return false;
    1116             : 
    1117       19412 :     int nWidthFactor = pSalLayout->GetUnitsPerPixel();
    1118       19412 :     pSalLayout->GetCaretPositions( 2*nLen, pCaretXArray );
    1119       19412 :     long nWidth = pSalLayout->GetTextWidth();
    1120       19412 :     pSalLayout->Release();
    1121             : 
    1122             :     // fixup unknown caret positions
    1123             :     int i;
    1124       19850 :     for( i = 0; i < 2 * nLen; ++i )
    1125       19816 :         if( pCaretXArray[ i ] >= 0 )
    1126       19378 :             break;
    1127       19412 :     long nXPos = pCaretXArray[ i ];
    1128      238996 :     for( i = 0; i < 2 * nLen; ++i )
    1129             :     {
    1130      219584 :         if( pCaretXArray[ i ] >= 0 )
    1131      219138 :             nXPos = pCaretXArray[ i ];
    1132             :         else
    1133         446 :             pCaretXArray[ i ] = nXPos;
    1134             :     }
    1135             : 
    1136             :     // handle window mirroring
    1137       19412 :     if( IsRTLEnabled() )
    1138             :     {
    1139         548 :         for( i = 0; i < 2 * nLen; ++i )
    1140         448 :             pCaretXArray[i] = nWidth - pCaretXArray[i] - 1;
    1141             :     }
    1142             : 
    1143             :     // convert from font units to logical units
    1144       19412 :     if( mbMap )
    1145             :     {
    1146           0 :         for( i = 0; i < 2*nLen; ++i )
    1147           0 :             pCaretXArray[i] = ImplDevicePixelToLogicWidth( pCaretXArray[i] );
    1148             :     }
    1149             : 
    1150       19412 :     if( nWidthFactor != 1 )
    1151             :     {
    1152           0 :         for( i = 0; i < 2*nLen; ++i )
    1153           0 :             pCaretXArray[i] /= nWidthFactor;
    1154             :     }
    1155             : 
    1156             :     // if requested move caret position to cell limits
    1157             :     if( bCellBreaking )
    1158             :     {
    1159             :         ; // FIXME
    1160             :     }
    1161             : 
    1162       19412 :     return true;
    1163             : }
    1164             : 
    1165       37672 : void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth,
    1166             :                                     const OUString& rStr,
    1167             :                                     sal_Int32 nIndex, sal_Int32 nLen)
    1168             : {
    1169       37672 :     assert_if_double_buffered_window();
    1170             : 
    1171       37672 :     if(nIndex < 0 || nIndex == 0x0FFFF || nLen == 0x0FFFF)
    1172             :     {
    1173             :         SAL_INFO("sal.rtl.xub",
    1174             :                  "DrawStretchText Suspicious arguments nIndex:" << nIndex << " nLen:" << nLen);
    1175             :     }
    1176       37672 :     if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
    1177             :     {
    1178       37672 :         nLen = rStr.getLength() - nIndex;
    1179             :     }
    1180             : 
    1181       37672 :     if ( mpMetaFile )
    1182       35584 :         mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) );
    1183             : 
    1184       37672 :     if ( !IsDeviceOutputNecessary() )
    1185       73256 :         return;
    1186             : 
    1187        2088 :     SalLayout* pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, nWidth, NULL);
    1188        2088 :     if( pSalLayout )
    1189             :     {
    1190        2088 :         ImplDrawText( *pSalLayout );
    1191        2088 :         pSalLayout->Release();
    1192             :     }
    1193             : 
    1194        2088 :     if( mpAlphaVDev )
    1195           0 :         mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen );
    1196             : }
    1197             : 
    1198     1584868 : ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr,
    1199             :                                                     const sal_Int32 nMinIndex, const sal_Int32 nLen,
    1200             :                                                     DeviceCoordinate nPixelWidth, const DeviceCoordinate* pDXArray,
    1201             :                                                     SalLayoutFlags nLayoutFlags,
    1202             :          vcl::TextLayoutCache const*const pLayoutCache) const
    1203             : {
    1204             :     assert(nMinIndex >= 0);
    1205             :     assert(nLen >= 0);
    1206             : 
    1207             :     // get string length for calculating extents
    1208     1584868 :     sal_Int32 nEndIndex = rStr.getLength();
    1209     1584868 :     if( nMinIndex + nLen < nEndIndex )
    1210      171782 :         nEndIndex = nMinIndex + nLen;
    1211             : 
    1212             :     // don't bother if there is nothing to do
    1213     1584868 :     if( nEndIndex < nMinIndex )
    1214           0 :         nEndIndex = nMinIndex;
    1215             : 
    1216     1584868 :     if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL )
    1217        4160 :         nLayoutFlags |= SalLayoutFlags::BiDiRtl;
    1218     1584868 :     if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG )
    1219      480607 :         nLayoutFlags |= SalLayoutFlags::BiDiStrong;
    1220     1104261 :     else if( !(mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) )
    1221             :     {
    1222             :         // disable Bidi if no RTL hint and no RTL codes used
    1223     1100163 :         const sal_Unicode* pStr = rStr.getStr() + nMinIndex;
    1224     1100163 :         const sal_Unicode* pEnd = rStr.getStr() + nEndIndex;
    1225     9221401 :         for( ; pStr < pEnd; ++pStr )
    1226     8121258 :             if( ((*pStr >= 0x0580) && (*pStr < 0x0800))   // middle eastern scripts
    1227     8121238 :             ||  ((*pStr >= 0xFB18) && (*pStr < 0xFE00))   // hebrew + arabic A presentation forms
    1228     8121238 :             ||  ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B
    1229             :                 break;
    1230     1100163 :         if( pStr >= pEnd )
    1231     1100143 :             nLayoutFlags |= SalLayoutFlags::BiDiStrong;
    1232             :     }
    1233             : 
    1234     1584868 :     if( mbKerning )
    1235      245124 :         nLayoutFlags |= SalLayoutFlags::KerningPairs;
    1236     1584868 :     if( maFont.GetKerning() & FontKerning::Asian )
    1237         522 :         nLayoutFlags |= SalLayoutFlags::KerningAsian;
    1238     1584868 :     if( maFont.IsVertical() )
    1239          41 :         nLayoutFlags |= SalLayoutFlags::Vertical;
    1240             : 
    1241     1584868 :     if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES )
    1242           0 :         nLayoutFlags |= SalLayoutFlags::EnableLigatures;
    1243     1584868 :     else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED )
    1244      233506 :         nLayoutFlags |= SalLayoutFlags::ComplexDisabled;
    1245             :     else
    1246             :     {
    1247             :         // disable CTL for non-CTL text
    1248     1351362 :         const sal_Unicode* pStr = rStr.getStr() + nMinIndex;
    1249     1351362 :         const sal_Unicode* pEnd = rStr.getStr() + nEndIndex;
    1250    34772912 :         for( ; pStr < pEnd; ++pStr )
    1251    33421609 :             if( ((*pStr >= 0x0300) && (*pStr < 0x0370))   // diacritical marks
    1252    33421603 :             ||  ((*pStr >= 0x0590) && (*pStr < 0x10A0))   // many CTL scripts
    1253    33421550 :             ||  ((*pStr >= 0x1100) && (*pStr < 0x1200))   // hangul jamo
    1254    33421550 :             ||  ((*pStr >= 0x1700) && (*pStr < 0x1900))   // many CTL scripts
    1255    33421550 :             ||  ((*pStr >= 0xFB1D) && (*pStr < 0xFE00))   // middle east presentation
    1256    33421550 :             ||  ((*pStr >= 0xFE70) && (*pStr < 0xFEFF))   // arabic presentation B
    1257    33421550 :             ||  ((*pStr >= 0xFE00) && (*pStr < 0xFE10))   // variation selectors in BMP
    1258    33421550 :             ||  ((pStr + 1 < pEnd) && (pStr[0] == 0xDB40) && (0xDD00 <= pStr[1]) && (pStr[1] < 0xDEF0)) // variation selector supplement
    1259             :             )
    1260             :                 break;
    1261     1351362 :         if( pStr >= pEnd )
    1262     1351303 :             nLayoutFlags |= SalLayoutFlags::ComplexDisabled;
    1263             :     }
    1264             : 
    1265     1584868 :     if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS)
    1266             :     {
    1267             :         // disable character localization when no digits used
    1268      578665 :         const sal_Unicode* pBase = rStr.getStr();
    1269      578665 :         const sal_Unicode* pStr = pBase + nMinIndex;
    1270      578665 :         const sal_Unicode* pEnd = pBase + nEndIndex;
    1271      578665 :         OUStringBuffer sTmpStr(rStr);
    1272    25831846 :         for( ; pStr < pEnd; ++pStr )
    1273             :         {
    1274             :             // TODO: are there non-digit localizations?
    1275    25253181 :             if( (*pStr >= '0') && (*pStr <= '9') )
    1276             :             {
    1277             :                 // translate characters to local preference
    1278    12983612 :                 sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
    1279    12983612 :                 if( cChar != *pStr )
    1280             :                     // TODO: are the localized digit surrogates?
    1281           0 :                     sTmpStr[pStr - pBase] = cChar;
    1282             :             }
    1283             :         }
    1284      578665 :         rStr = sTmpStr.makeStringAndClear();
    1285             :     }
    1286             : 
    1287             :     // right align for RTL text, DRAWPOS_REVERSED, RTL window style
    1288     1584868 :     bool bRightAlign = bool(mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL);
    1289     1584868 :     if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT )
    1290      178277 :         bRightAlign = false;
    1291     1406591 :     else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT )
    1292           0 :         bRightAlign = true;
    1293             :     // SSA: hack for western office, ie text get right aligned
    1294             :     //      for debugging purposes of mirrored UI
    1295     1584868 :     bool bRTLWindow = IsRTLEnabled();
    1296     1584868 :     bRightAlign ^= bRTLWindow;
    1297     1584868 :     if( bRightAlign )
    1298        1382 :         nLayoutFlags |= SalLayoutFlags::RightAlign;
    1299             : 
    1300             :     // set layout options
    1301     1584868 :     ImplLayoutArgs aLayoutArgs( rStr.getStr(), rStr.getLength(), nMinIndex, nEndIndex, nLayoutFlags, maFont.GetLanguageTag(), pLayoutCache );
    1302             : 
    1303     1584868 :     int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0;
    1304     1584868 :     aLayoutArgs.SetOrientation( nOrientation );
    1305             : 
    1306     1584868 :     aLayoutArgs.SetLayoutWidth( nPixelWidth );
    1307     1584868 :     aLayoutArgs.SetDXArray( pDXArray );
    1308             : 
    1309     1584868 :     return aLayoutArgs;
    1310             : }
    1311             : 
    1312     1529050 : SalLayout* OutputDevice::ImplLayout(const OUString& rOrigStr,
    1313             :                                     sal_Int32 nMinIndex, sal_Int32 nLen,
    1314             :                                     const Point& rLogicalPos, long nLogicalWidth,
    1315             :                                     const long* pDXArray, SalLayoutFlags flags,
    1316             :          vcl::TextLayoutCache const* pLayoutCache) const
    1317             : {
    1318             :     // we need a graphics
    1319     1529050 :     if( !mpGraphics )
    1320         337 :         if( !AcquireGraphics() )
    1321           0 :             return NULL;
    1322             : 
    1323             :     // initialize font if needed
    1324     1529050 :     if( mbNewFont )
    1325      202799 :         if( !ImplNewFont() )
    1326           0 :             return NULL;
    1327     1529050 :     if( mbInitFont )
    1328      269199 :         InitFont();
    1329             : 
    1330             :     // check string index and length
    1331     1529050 :     if( -1 == nLen || nMinIndex + nLen > rOrigStr.getLength() )
    1332             :     {
    1333      261538 :         const sal_Int32 nNewLen = rOrigStr.getLength() - nMinIndex;
    1334      261538 :         if( nNewLen <= 0 )
    1335           0 :             return NULL;
    1336      261538 :         nLen = nNewLen;
    1337             :     }
    1338             : 
    1339     1529050 :     OUString aStr = rOrigStr;
    1340             : 
    1341             :     // convert from logical units to physical units
    1342             :     // recode string if needed
    1343     1529050 :     if( mpFontEntry->mpConversion ) {
    1344        2007 :         mpFontEntry->mpConversion->RecodeString( aStr, 0, aStr.getLength() );
    1345        2007 :         pLayoutCache = nullptr; // don't use cache with modified string!
    1346             :     }
    1347     1529050 :     DeviceCoordinate nPixelWidth = (DeviceCoordinate)nLogicalWidth;
    1348     1529050 :     DeviceCoordinate* pDXPixelArray = NULL;
    1349     1529050 :     if( nLogicalWidth && mbMap )
    1350             :     {
    1351        2085 :         nPixelWidth = LogicWidthToDeviceCoordinate( nLogicalWidth );
    1352             :     }
    1353             : 
    1354     1529050 :     if( pDXArray)
    1355             :     {
    1356       38418 :         if(mbMap)
    1357             :         {
    1358             :             // convert from logical units to font units using a temporary array
    1359       24076 :             pDXPixelArray = static_cast<DeviceCoordinate*>(alloca( nLen * sizeof(DeviceCoordinate) ));
    1360             :             // using base position for better rounding a.k.a. "dancing characters"
    1361       24076 :             DeviceCoordinate nPixelXOfs = LogicWidthToDeviceCoordinate( rLogicalPos.X() );
    1362      929950 :             for( int i = 0; i < nLen; ++i )
    1363             :             {
    1364      905874 :                 pDXPixelArray[i] = LogicWidthToDeviceCoordinate( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs;
    1365             :             }
    1366             :         }
    1367             :         else
    1368             :         {
    1369             : #if VCL_FLOAT_DEVICE_PIXEL
    1370             :             pDXPixelArray = static_cast<DeviceCoordinate*>(alloca( nLen * sizeof(DeviceCoordinate) ));
    1371             :             for( int i = 0; i < nLen; ++i )
    1372             :             {
    1373             :                 pDXPixelArray[i] = pDXArray[i];
    1374             :             }
    1375             : #else /* !VCL_FLOAT_DEVICE_PIXEL */
    1376       14342 :             pDXPixelArray = const_cast<DeviceCoordinate*>(pDXArray);
    1377             : #endif /* !VCL_FLOAT_DEVICE_PIXEL */
    1378             :         }
    1379             :     }
    1380             : 
    1381             :     ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen,
    1382     3058100 :             nPixelWidth, pDXPixelArray, flags, pLayoutCache);
    1383             : 
    1384             :     // get matching layout object for base font
    1385     1529050 :     SalLayout* pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );
    1386             : 
    1387             :     // layout text
    1388     1529050 :     if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) )
    1389             :     {
    1390           0 :         pSalLayout->Release();
    1391           0 :         pSalLayout = NULL;
    1392             :     }
    1393             : 
    1394     1529050 :     if( !pSalLayout )
    1395           0 :         return NULL;
    1396             : 
    1397             :     // do glyph fallback if needed
    1398             :     // #105768# avoid fallback for very small font sizes
    1399     1529050 :     if (aLayoutArgs.NeedFallback() && mpFontEntry->maFontSelData.mnHeight >= 3)
    1400        3098 :         pSalLayout = ImplGlyphFallbackLayout(pSalLayout, aLayoutArgs);
    1401             : 
    1402             :     // position, justify, etc. the layout
    1403     1529050 :     pSalLayout->AdjustLayout( aLayoutArgs );
    1404     1529050 :     pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos );
    1405             :     // adjust to right alignment if necessary
    1406     1529050 :     if( aLayoutArgs.mnFlags & SalLayoutFlags::RightAlign )
    1407             :     {
    1408             :         DeviceCoordinate nRTLOffset;
    1409        1333 :         if( pDXPixelArray )
    1410          42 :             nRTLOffset = pDXPixelArray[ nLen - 1 ];
    1411        1291 :         else if( nPixelWidth )
    1412           0 :             nRTLOffset = nPixelWidth;
    1413             :         else
    1414        1291 :             nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
    1415        1333 :         pSalLayout->DrawOffset().X() = 1 - nRTLOffset;
    1416             :     }
    1417             : 
    1418     3058100 :     return pSalLayout;
    1419             : }
    1420             : 
    1421       55817 : std::shared_ptr<vcl::TextLayoutCache> OutputDevice::CreateTextLayoutCache(
    1422             :         OUString const& rString) const
    1423             : {
    1424       55817 :     if (!mpGraphics) // can happen in e.g Insert Index/Table dialog
    1425           0 :         return nullptr;
    1426       55817 :     OUString copyBecausePrepareModifiesIt(rString);
    1427             :     ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs(copyBecausePrepareModifiesIt,
    1428      111634 :             0, rString.getLength(), 0, nullptr, SalLayoutFlags::NONE, nullptr);
    1429             : 
    1430       55817 :     SalLayout *const pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );
    1431       55817 :     if (!pSalLayout)
    1432        1197 :         return nullptr;
    1433             :     std::shared_ptr<vcl::TextLayoutCache> const ret(
    1434      109240 :             pSalLayout->CreateTextLayoutCache(copyBecausePrepareModifiesIt));
    1435       54620 :     pSalLayout->Release();
    1436      110437 :     return ret;
    1437             : }
    1438             : 
    1439           1 : bool OutputDevice::GetTextIsRTL( const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen ) const
    1440             : {
    1441           1 :     OUString aStr( rString );
    1442           2 :     ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
    1443           1 :     bool bRTL = false;
    1444           1 :     int nCharPos = -1;
    1445           1 :     if (!aArgs.GetNextPos(&nCharPos, &bRTL))
    1446           0 :         return false;
    1447           2 :     return (nCharPos != nIndex);
    1448             : }
    1449             : 
    1450       51635 : sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, long nTextWidth,
    1451             :                                        sal_Int32 nIndex, sal_Int32 nLen,
    1452             :                                        long nCharExtra,
    1453             :          vcl::TextLayoutCache const*const pLayoutCache) const
    1454             : {
    1455             :     SalLayout *const pSalLayout = ImplLayout( rStr, nIndex, nLen,
    1456       51635 :             Point(0,0), 0, nullptr, SalLayoutFlags::NONE, pLayoutCache);
    1457       51635 :     sal_Int32 nRetVal = -1;
    1458       51635 :     if( pSalLayout )
    1459             :     {
    1460             :         // convert logical widths into layout units
    1461             :         // NOTE: be very careful to avoid rounding errors for nCharExtra case
    1462             :         // problem with rounding errors especially for small nCharExtras
    1463             :         // TODO: remove when layout units have subpixel granularity
    1464       51635 :         long nWidthFactor = pSalLayout->GetUnitsPerPixel();
    1465       51635 :         long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
    1466       51635 :         nTextWidth *= nWidthFactor * nSubPixelFactor;
    1467       51635 :         DeviceCoordinate nTextPixelWidth = LogicWidthToDeviceCoordinate( nTextWidth );
    1468       51635 :         DeviceCoordinate nExtraPixelWidth = 0;
    1469       51635 :         if( nCharExtra != 0 )
    1470             :         {
    1471          73 :             nCharExtra *= nWidthFactor * nSubPixelFactor;
    1472          73 :             nExtraPixelWidth = LogicWidthToDeviceCoordinate( nCharExtra );
    1473             :         }
    1474       51635 :         nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
    1475             : 
    1476       51635 :         pSalLayout->Release();
    1477             :     }
    1478             : 
    1479       51635 :     return nRetVal;
    1480             : }
    1481             : 
    1482           0 : sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, long nTextWidth,
    1483             :                                        sal_Unicode nHyphenChar, sal_Int32& rHyphenPos,
    1484             :                                        sal_Int32 nIndex, sal_Int32 nLen,
    1485             :                                        long nCharExtra,
    1486             :          vcl::TextLayoutCache const*const pLayoutCache) const
    1487             : {
    1488           0 :     rHyphenPos = -1;
    1489             : 
    1490             :     SalLayout *const pSalLayout = ImplLayout( rStr, nIndex, nLen,
    1491           0 :             Point(0,0), 0, nullptr, SalLayoutFlags::NONE, pLayoutCache);
    1492           0 :     sal_Int32 nRetVal = -1;
    1493           0 :     if( pSalLayout )
    1494             :     {
    1495             :         // convert logical widths into layout units
    1496             :         // NOTE: be very careful to avoid rounding errors for nCharExtra case
    1497             :         // problem with rounding errors especially for small nCharExtras
    1498             :         // TODO: remove when layout units have subpixel granularity
    1499           0 :         long nWidthFactor = pSalLayout->GetUnitsPerPixel();
    1500           0 :         long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
    1501             : 
    1502           0 :         nTextWidth *= nWidthFactor * nSubPixelFactor;
    1503           0 :         DeviceCoordinate nTextPixelWidth = LogicWidthToDeviceCoordinate( nTextWidth );
    1504           0 :         DeviceCoordinate nExtraPixelWidth = 0;
    1505           0 :         if( nCharExtra != 0 )
    1506             :         {
    1507           0 :             nCharExtra *= nWidthFactor * nSubPixelFactor;
    1508           0 :             nExtraPixelWidth = LogicWidthToDeviceCoordinate( nCharExtra );
    1509             :         }
    1510             : 
    1511             :         // calculate un-hyphenated break position
    1512           0 :         nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
    1513             : 
    1514             :         // calculate hyphenated break position
    1515           0 :         OUString aHyphenStr(nHyphenChar);
    1516           0 :         sal_Int32 nTempLen = 1;
    1517           0 :         SalLayout* pHyphenLayout = ImplLayout( aHyphenStr, 0, nTempLen );
    1518           0 :         if( pHyphenLayout )
    1519             :         {
    1520             :             // calculate subpixel width of hyphenation character
    1521           0 :             long nHyphenPixelWidth = pHyphenLayout->GetTextWidth() * nSubPixelFactor;
    1522           0 :             pHyphenLayout->Release();
    1523             : 
    1524             :             // calculate hyphenated break position
    1525           0 :             nTextPixelWidth -= nHyphenPixelWidth;
    1526           0 :             if( nExtraPixelWidth > 0 )
    1527           0 :                 nTextPixelWidth -= nExtraPixelWidth;
    1528             : 
    1529           0 :             rHyphenPos = pSalLayout->GetTextBreak(nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor);
    1530             : 
    1531           0 :             if( rHyphenPos > nRetVal )
    1532           0 :                 rHyphenPos = nRetVal;
    1533             :         }
    1534             : 
    1535           0 :         pSalLayout->Release();
    1536             :     }
    1537             : 
    1538           0 :     return nRetVal;
    1539             : }
    1540             : 
    1541        5857 : void OutputDevice::ImplDrawText( OutputDevice& rTargetDevice, const Rectangle& rRect,
    1542             :                                  const OUString& rOrigStr, DrawTextFlags nStyle,
    1543             :                                  MetricVector* pVector, OUString* pDisplayText,
    1544             :                                  vcl::ITextLayout& _rLayout )
    1545             : {
    1546             : 
    1547        5857 :     Color aOldTextColor;
    1548        5857 :     Color aOldTextFillColor;
    1549        5857 :     bool  bRestoreFillColor = false;
    1550        5857 :     if ( (nStyle & DrawTextFlags::Disable) && ! pVector )
    1551             :     {
    1552        1481 :         bool  bHighContrastBlack = false;
    1553        1481 :         bool  bHighContrastWhite = false;
    1554        1481 :         const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() );
    1555        1481 :         if( rStyleSettings.GetHighContrastMode() )
    1556             :         {
    1557           0 :             Color aCol;
    1558           0 :             if( rTargetDevice.IsBackground() )
    1559           0 :                 aCol = rTargetDevice.GetBackground().GetColor();
    1560             :             else
    1561             :                 // best guess is the face color here
    1562             :                 // but it may be totally wrong. the background color
    1563             :                 // was typically already reset
    1564           0 :                 aCol = rStyleSettings.GetFaceColor();
    1565             : 
    1566           0 :             bHighContrastBlack = aCol.IsDark();
    1567           0 :             bHighContrastWhite = aCol.IsBright();
    1568             :         }
    1569             : 
    1570        1481 :         aOldTextColor = rTargetDevice.GetTextColor();
    1571        1481 :         if ( rTargetDevice.IsTextFillColor() )
    1572             :         {
    1573           0 :             bRestoreFillColor = true;
    1574           0 :             aOldTextFillColor = rTargetDevice.GetTextFillColor();
    1575             :         }
    1576        1481 :         if( bHighContrastBlack )
    1577           0 :             rTargetDevice.SetTextColor( COL_GREEN );
    1578        1481 :         else if( bHighContrastWhite )
    1579           0 :             rTargetDevice.SetTextColor( COL_LIGHTGREEN );
    1580             :         else
    1581             :         {
    1582             :             // draw disabled text always without shadow
    1583             :             // as it fits better with native look
    1584        1481 :             rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() );
    1585             :         }
    1586             :     }
    1587             : 
    1588        5857 :     long        nWidth          = rRect.GetWidth();
    1589        5857 :     long        nHeight         = rRect.GetHeight();
    1590             : 
    1591        5857 :     if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & DrawTextFlags::Clip) )
    1592        5857 :         return;
    1593             : 
    1594        5857 :     Point       aPos            = rRect.TopLeft();
    1595             : 
    1596        5857 :     long        nTextHeight     = rTargetDevice.GetTextHeight();
    1597        5857 :     TextAlign   eAlign          = rTargetDevice.GetTextAlign();
    1598        5857 :     sal_Int32   nMnemonicPos    = -1;
    1599             : 
    1600        5857 :     OUString aStr = rOrigStr;
    1601        5857 :     if ( nStyle & DrawTextFlags::Mnemonic )
    1602        4205 :         aStr = GetNonMnemonicString( aStr, nMnemonicPos );
    1603             : 
    1604        5857 :     const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics) && !pVector;
    1605             : 
    1606             :     // We treat multiline text differently
    1607        5857 :     if ( nStyle & DrawTextFlags::MultiLine )
    1608             :     {
    1609             : 
    1610        4207 :         OUString                aLastLine;
    1611        8414 :         ImplMultiTextLineInfo   aMultiLineInfo;
    1612             :         ImplTextLineInfo*       pLineInfo;
    1613             :         sal_Int32               i;
    1614             :         sal_Int32               nLines;
    1615             :         sal_Int32               nFormatLines;
    1616             : 
    1617        4207 :         if ( nTextHeight )
    1618             :         {
    1619        4207 :             long nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _rLayout );
    1620        4207 :             nLines = (sal_Int32)(nHeight/nTextHeight);
    1621        4207 :             nFormatLines = aMultiLineInfo.Count();
    1622        4207 :             if (nLines <= 0)
    1623         214 :                 nLines = 1;
    1624        4207 :             if ( nFormatLines > nLines )
    1625             :             {
    1626           0 :                 if ( nStyle & DrawTextFlags::EndEllipsis )
    1627             :                 {
    1628             :                     // Create last line and shorten it
    1629           0 :                     nFormatLines = nLines-1;
    1630             : 
    1631           0 :                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
    1632           0 :                     aLastLine = convertLineEnd(aStr.copy(pLineInfo->GetIndex()), LINEEND_LF);
    1633             :                     // Replace all LineFeeds with Spaces
    1634           0 :                     OUStringBuffer aLastLineBuffer(aLastLine);
    1635           0 :                     sal_Int32 nLastLineLen = aLastLineBuffer.getLength();
    1636           0 :                     for ( i = 0; i < nLastLineLen; i++ )
    1637             :                     {
    1638           0 :                         if ( aLastLineBuffer[ i ] == '\n' )
    1639           0 :                             aLastLineBuffer[ i ] = ' ';
    1640             :                     }
    1641           0 :                     aLastLine = aLastLineBuffer.makeStringAndClear();
    1642           0 :                     aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout );
    1643           0 :                     nStyle &= ~DrawTextFlags(DrawTextFlags::VCenter | DrawTextFlags::Bottom);
    1644           0 :                     nStyle |= DrawTextFlags::Top;
    1645             :                 }
    1646             :             }
    1647             :             else
    1648             :             {
    1649        4207 :                 if ( nMaxTextWidth <= nWidth )
    1650        4189 :                     nStyle &= ~DrawTextFlags::Clip;
    1651             :             }
    1652             : 
    1653             :             // Do we need to clip the height?
    1654        4207 :             if ( nFormatLines*nTextHeight > nHeight )
    1655         214 :                 nStyle |= DrawTextFlags::Clip;
    1656             : 
    1657             :             // Set clipping
    1658        4207 :             if ( nStyle & DrawTextFlags::Clip )
    1659             :             {
    1660         214 :                 rTargetDevice.Push( PushFlags::CLIPREGION );
    1661         214 :                 rTargetDevice.IntersectClipRegion( rRect );
    1662             :             }
    1663             : 
    1664             :             // Vertical alignment
    1665        4207 :             if ( nStyle & DrawTextFlags::Bottom )
    1666           0 :                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
    1667        4207 :             else if ( nStyle & DrawTextFlags::VCenter )
    1668        3772 :                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
    1669             : 
    1670             :             // Font alignment
    1671        4207 :             if ( eAlign == ALIGN_BOTTOM )
    1672           0 :                 aPos.Y() += nTextHeight;
    1673        4207 :             else if ( eAlign == ALIGN_BASELINE )
    1674           0 :                 aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
    1675             : 
    1676             :             // Output all lines except for the last one
    1677        8670 :             for ( i = 0; i < nFormatLines; i++ )
    1678             :             {
    1679        4463 :                 pLineInfo = aMultiLineInfo.GetLine( i );
    1680        4463 :                 if ( nStyle & DrawTextFlags::Right )
    1681           0 :                     aPos.X() += nWidth-pLineInfo->GetWidth();
    1682        4463 :                 else if ( nStyle & DrawTextFlags::Center )
    1683        2789 :                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
    1684        4463 :                 sal_Int32 nIndex   = pLineInfo->GetIndex();
    1685        4463 :                 sal_Int32 nLineLen = pLineInfo->GetLen();
    1686        4463 :                 _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
    1687        4463 :                 if ( bDrawMnemonics )
    1688             :                 {
    1689        4462 :                     if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
    1690             :                     {
    1691             :                         long        nMnemonicX;
    1692             :                         long        nMnemonicY;
    1693             :                         DeviceCoordinate nMnemonicWidth;
    1694             : 
    1695          58 :                         long* pCaretXArray = static_cast<long*>(alloca( 2 * sizeof(long) * nLineLen ));
    1696             :                         /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray,
    1697          58 :                                                 nIndex, nLineLen );
    1698          58 :                         long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
    1699          58 :                         long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
    1700          58 :                         nMnemonicWidth = rTargetDevice.LogicWidthToDeviceCoordinate( std::abs(lc_x1 - lc_x2) );
    1701             : 
    1702          58 :                         Point       aTempPos = rTargetDevice.LogicToPixel( aPos );
    1703          58 :                         nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min( lc_x1, lc_x2 ) );
    1704          58 :                         nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
    1705          58 :                         rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
    1706             :                     }
    1707             :                 }
    1708        4463 :                 aPos.Y() += nTextHeight;
    1709        4463 :                 aPos.X() = rRect.Left();
    1710             :             }
    1711             : 
    1712             :             // If there still is a last line, we output it left-aligned as the line would be clipped
    1713        4207 :             if ( !aLastLine.isEmpty() )
    1714           0 :                 _rLayout.DrawText( aPos, aLastLine, 0, aLastLine.getLength(), pVector, pDisplayText );
    1715             : 
    1716             :             // Reset clipping
    1717        4207 :             if ( nStyle & DrawTextFlags::Clip )
    1718         214 :                 rTargetDevice.Pop();
    1719        4207 :         }
    1720             :     }
    1721             :     else
    1722             :     {
    1723        1650 :         long nTextWidth = _rLayout.GetTextWidth( aStr, 0, -1 );
    1724             : 
    1725             :         // Clip text if needed
    1726        1650 :         if ( nTextWidth > nWidth )
    1727             :         {
    1728           1 :             if ( nStyle & TEXT_DRAW_ELLIPSIS )
    1729             :             {
    1730           0 :                 aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout );
    1731           0 :                 nStyle &= ~DrawTextFlags(DrawTextFlags::Center | DrawTextFlags::Right);
    1732           0 :                 nStyle |= DrawTextFlags::Left;
    1733           0 :                 nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.getLength() );
    1734             :             }
    1735             :         }
    1736             :         else
    1737             :         {
    1738        1649 :             if ( nTextHeight <= nHeight )
    1739        1649 :                 nStyle &= ~DrawTextFlags::Clip;
    1740             :         }
    1741             : 
    1742             :         // horizontal text alignment
    1743        1650 :         if ( nStyle & DrawTextFlags::Right )
    1744         320 :             aPos.X() += nWidth-nTextWidth;
    1745        1330 :         else if ( nStyle & DrawTextFlags::Center )
    1746         126 :             aPos.X() += (nWidth-nTextWidth)/2;
    1747             : 
    1748             :         // vertical font alignment
    1749        1650 :         if ( eAlign == ALIGN_BOTTOM )
    1750           0 :             aPos.Y() += nTextHeight;
    1751        1650 :         else if ( eAlign == ALIGN_BASELINE )
    1752           0 :             aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
    1753             : 
    1754        1650 :         if ( nStyle & DrawTextFlags::Bottom )
    1755           0 :             aPos.Y() += nHeight-nTextHeight;
    1756        1650 :         else if ( nStyle & DrawTextFlags::VCenter )
    1757        1426 :             aPos.Y() += (nHeight-nTextHeight)/2;
    1758             : 
    1759        1650 :         long nMnemonicX = 0;
    1760        1650 :         long nMnemonicY = 0;
    1761        1650 :         DeviceCoordinate nMnemonicWidth = 0;
    1762        1650 :         if ( nMnemonicPos != -1 )
    1763             :         {
    1764           0 :             long* pCaretXArray = static_cast<long*>(alloca( 2 * sizeof(long) * aStr.getLength() ));
    1765           0 :             /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, 0, aStr.getLength() );
    1766           0 :             long lc_x1 = pCaretXArray[2*(nMnemonicPos)];
    1767           0 :             long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1];
    1768           0 :             nMnemonicWidth = rTargetDevice.LogicWidthToDeviceCoordinate( std::abs(lc_x1 - lc_x2) );
    1769             : 
    1770           0 :             Point aTempPos = rTargetDevice.LogicToPixel( aPos );
    1771           0 :             nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min(lc_x1, lc_x2) );
    1772           0 :             nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
    1773             :         }
    1774             : 
    1775        1650 :         if ( nStyle & DrawTextFlags::Clip )
    1776             :         {
    1777           1 :             rTargetDevice.Push( PushFlags::CLIPREGION );
    1778           1 :             rTargetDevice.IntersectClipRegion( rRect );
    1779           1 :             _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
    1780           1 :             if ( bDrawMnemonics )
    1781             :             {
    1782           1 :                 if ( nMnemonicPos != -1 )
    1783           0 :                     rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
    1784             :             }
    1785           1 :             rTargetDevice.Pop();
    1786             :         }
    1787             :         else
    1788             :         {
    1789        1649 :             _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
    1790        1649 :             if ( bDrawMnemonics )
    1791             :             {
    1792        1649 :                 if ( nMnemonicPos != -1 )
    1793           0 :                     rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
    1794             :             }
    1795             :         }
    1796             :     }
    1797             : 
    1798        5857 :     if ( nStyle & DrawTextFlags::Disable && !pVector )
    1799             :     {
    1800        1481 :         rTargetDevice.SetTextColor( aOldTextColor );
    1801        1481 :         if ( bRestoreFillColor )
    1802           0 :             rTargetDevice.SetTextFillColor( aOldTextFillColor );
    1803        5857 :     }
    1804             : }
    1805             : 
    1806           0 : void OutputDevice::AddTextRectActions( const Rectangle& rRect,
    1807             :                                        const OUString&  rOrigStr,
    1808             :                                        DrawTextFlags    nStyle,
    1809             :                                        GDIMetaFile&     rMtf )
    1810             : {
    1811             : 
    1812           0 :     if ( rOrigStr.isEmpty() || rRect.IsEmpty() )
    1813           0 :         return;
    1814             : 
    1815             :     // we need a graphics
    1816           0 :     if( !mpGraphics && !AcquireGraphics() )
    1817           0 :         return;
    1818           0 :     if( mbInitClipRegion )
    1819           0 :         InitClipRegion();
    1820             : 
    1821             :     // temporarily swap in passed mtf for action generation, and
    1822             :     // disable output generation.
    1823           0 :     const bool bOutputEnabled( IsOutputEnabled() );
    1824           0 :     GDIMetaFile* pMtf = mpMetaFile;
    1825             : 
    1826           0 :     mpMetaFile = &rMtf;
    1827           0 :     EnableOutput( false );
    1828             : 
    1829             :     // #i47157# Factored out to ImplDrawTextRect(), to be shared
    1830             :     // between us and DrawText()
    1831           0 :     vcl::DefaultTextLayout aLayout( *this );
    1832           0 :     ImplDrawText( *this, rRect, rOrigStr, nStyle, NULL, NULL, aLayout );
    1833             : 
    1834             :     // and restore again
    1835           0 :     EnableOutput( bOutputEnabled );
    1836           0 :     mpMetaFile = pMtf;
    1837             : }
    1838             : 
    1839        8170 : void OutputDevice::DrawText( const Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle,
    1840             :                              MetricVector* pVector, OUString* pDisplayText,
    1841             :                              vcl::ITextLayout* _pTextLayout )
    1842             : {
    1843        8170 :     assert_if_double_buffered_window();
    1844             : 
    1845        8170 :     if (mpOutDevData->mpRecordLayout)
    1846             :     {
    1847           0 :         pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
    1848           0 :         pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
    1849             :     }
    1850             : 
    1851        8170 :     bool bDecomposeTextRectAction = ( _pTextLayout != NULL ) && _pTextLayout->DecomposeTextRectAction();
    1852        8170 :     if ( mpMetaFile && !bDecomposeTextRectAction )
    1853        1666 :         mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) );
    1854             : 
    1855        8170 :     if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || rOrigStr.isEmpty() || rRect.IsEmpty() )
    1856        3548 :         return;
    1857             : 
    1858             :     // we need a graphics
    1859        6935 :     if( !mpGraphics && !AcquireGraphics() )
    1860           0 :         return;
    1861        6935 :     if( mbInitClipRegion )
    1862        2611 :         InitClipRegion();
    1863        6935 :     if( mbOutputClipped && !bDecomposeTextRectAction )
    1864        1078 :         return;
    1865             : 
    1866             :     // temporarily disable mtf action generation (ImplDrawText _does_
    1867             :     // create MetaActionType::TEXTs otherwise)
    1868        5857 :     GDIMetaFile* pMtf = mpMetaFile;
    1869        5857 :     if ( !bDecomposeTextRectAction )
    1870        5406 :         mpMetaFile = NULL;
    1871             : 
    1872             :     // #i47157# Factored out to ImplDrawText(), to be used also
    1873             :     // from AddTextRectActions()
    1874        5857 :     vcl::DefaultTextLayout aDefaultLayout( *this );
    1875        5857 :     ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout );
    1876             : 
    1877             :     // and enable again
    1878        5857 :     mpMetaFile = pMtf;
    1879             : 
    1880        5857 :     if( mpAlphaVDev )
    1881        1076 :         mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
    1882             : }
    1883             : 
    1884        6048 : Rectangle OutputDevice::GetTextRect( const Rectangle& rRect,
    1885             :                                      const OUString& rStr, DrawTextFlags nStyle,
    1886             :                                      TextRectInfo* pInfo,
    1887             :                                      const vcl::ITextLayout* _pTextLayout ) const
    1888             : {
    1889             : 
    1890        6048 :     Rectangle           aRect = rRect;
    1891             :     sal_Int32           nLines;
    1892        6048 :     long                nWidth = rRect.GetWidth();
    1893             :     long                nMaxWidth;
    1894        6048 :     long                nTextHeight = GetTextHeight();
    1895             : 
    1896        6048 :     OUString aStr = rStr;
    1897        6048 :     if ( nStyle & DrawTextFlags::Mnemonic )
    1898        5882 :         aStr = GetNonMnemonicString( aStr );
    1899             : 
    1900        6048 :     if ( nStyle & DrawTextFlags::MultiLine )
    1901             :     {
    1902        5503 :         ImplMultiTextLineInfo   aMultiLineInfo;
    1903             :         ImplTextLineInfo*       pLineInfo;
    1904             :         sal_Int32               nFormatLines;
    1905             :         sal_Int32               i;
    1906             : 
    1907        5503 :         nMaxWidth = 0;
    1908       11006 :         vcl::DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) );
    1909        5503 :         ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout );
    1910        5503 :         nFormatLines = aMultiLineInfo.Count();
    1911        5503 :         if ( !nTextHeight )
    1912           0 :             nTextHeight = 1;
    1913        5503 :         nLines = (sal_uInt16)(aRect.GetHeight()/nTextHeight);
    1914        5503 :         if ( pInfo )
    1915           0 :             pInfo->mnLineCount = nFormatLines;
    1916        5503 :         if ( !nLines )
    1917          21 :             nLines = 1;
    1918        5503 :         if ( nFormatLines <= nLines )
    1919        5503 :             nLines = nFormatLines;
    1920             :         else
    1921             :         {
    1922           0 :             if ( !(nStyle & DrawTextFlags::EndEllipsis) )
    1923           0 :                 nLines = nFormatLines;
    1924             :             else
    1925             :             {
    1926           0 :                 if ( pInfo )
    1927           0 :                     pInfo->mbEllipsis = true;
    1928           0 :                 nMaxWidth = nWidth;
    1929             :             }
    1930             :         }
    1931        5503 :         if ( pInfo )
    1932             :         {
    1933           0 :             bool bMaxWidth = nMaxWidth == 0;
    1934           0 :             pInfo->mnMaxWidth = 0;
    1935           0 :             for ( i = 0; i < nLines; i++ )
    1936             :             {
    1937           0 :                 pLineInfo = aMultiLineInfo.GetLine( i );
    1938           0 :                 if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) )
    1939           0 :                     nMaxWidth = pLineInfo->GetWidth();
    1940           0 :                 if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth )
    1941           0 :                     pInfo->mnMaxWidth = pLineInfo->GetWidth();
    1942             :             }
    1943             :         }
    1944        5503 :         else if ( !nMaxWidth )
    1945             :         {
    1946       10899 :             for ( i = 0; i < nLines; i++ )
    1947             :             {
    1948        5396 :                 pLineInfo = aMultiLineInfo.GetLine( i );
    1949        5396 :                 if ( pLineInfo->GetWidth() > nMaxWidth )
    1950        5268 :                     nMaxWidth = pLineInfo->GetWidth();
    1951             :             }
    1952        5503 :         }
    1953             :     }
    1954             :     else
    1955             :     {
    1956         545 :         nLines      = 1;
    1957         545 :         nMaxWidth   = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.getLength() ) : GetTextWidth( aStr );
    1958             : 
    1959         545 :         if ( pInfo )
    1960             :         {
    1961           0 :             pInfo->mnLineCount  = 1;
    1962           0 :             pInfo->mnMaxWidth   = nMaxWidth;
    1963             :         }
    1964             : 
    1965         545 :         if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) )
    1966             :         {
    1967           0 :             if ( pInfo )
    1968           0 :                 pInfo->mbEllipsis = true;
    1969           0 :             nMaxWidth = nWidth;
    1970             :         }
    1971             :     }
    1972             : 
    1973        6048 :     if ( nStyle & DrawTextFlags::Right )
    1974           0 :         aRect.Left() = aRect.Right()-nMaxWidth+1;
    1975        6048 :     else if ( nStyle & DrawTextFlags::Center )
    1976             :     {
    1977        2647 :         aRect.Left() += (nWidth-nMaxWidth)/2;
    1978        2647 :         aRect.Right() = aRect.Left()+nMaxWidth-1;
    1979             :     }
    1980             :     else
    1981        3401 :         aRect.Right() = aRect.Left()+nMaxWidth-1;
    1982             : 
    1983        6048 :     if ( nStyle & DrawTextFlags::Bottom )
    1984           0 :         aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1;
    1985        6048 :     else if ( nStyle & DrawTextFlags::VCenter )
    1986             :     {
    1987        5412 :         aRect.Top()   += (aRect.GetHeight()-(nTextHeight*nLines))/2;
    1988        5412 :         aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
    1989             :     }
    1990             :     else
    1991         636 :         aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
    1992             : 
    1993             :     // #99188# get rid of rounding problems when using this rect later
    1994        6048 :     if (nStyle & DrawTextFlags::Right)
    1995           0 :         aRect.Left()--;
    1996             :     else
    1997        6048 :         aRect.Right()++;
    1998        6048 :     return aRect;
    1999             : }
    2000             : 
    2001           0 : static bool ImplIsCharIn( sal_Unicode c, const sal_Char* pStr )
    2002             : {
    2003           0 :     while ( *pStr )
    2004             :     {
    2005           0 :         if ( *pStr == c )
    2006           0 :             return true;
    2007           0 :         pStr++;
    2008             :     }
    2009             : 
    2010           0 :     return false;
    2011             : }
    2012             : 
    2013       10810 : OUString OutputDevice::GetEllipsisString( const OUString& rOrigStr, long nMaxWidth,
    2014             :                                         DrawTextFlags nStyle ) const
    2015             : {
    2016       10810 :     vcl::DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) );
    2017       10810 :     return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout );
    2018             : }
    2019             : 
    2020       10810 : OUString OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const OUString& rOrigStr, long nMaxWidth,
    2021             :                                                DrawTextFlags nStyle, const vcl::ITextLayout& _rLayout )
    2022             : {
    2023       10810 :     OUString aStr = rOrigStr;
    2024       10810 :     sal_Int32 nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.getLength() );
    2025             : 
    2026       10810 :     if ( nIndex != -1 )
    2027             :     {
    2028           0 :         if( (nStyle & DrawTextFlags::CenterEllipsis) == DrawTextFlags::CenterEllipsis )
    2029             :         {
    2030           0 :             OUStringBuffer aTmpStr( aStr );
    2031             :             // speed it up by removing all but 1.33x as many as the break pos.
    2032           0 :             sal_Int32 nEraseChars = std::max<sal_Int32>(4, aStr.getLength() - (nIndex*4)/3);
    2033           0 :             while( nEraseChars < aStr.getLength() && _rLayout.GetTextWidth( aTmpStr.toString(), 0, aTmpStr.getLength() ) > nMaxWidth )
    2034             :             {
    2035           0 :                 aTmpStr = OUStringBuffer(aStr);
    2036           0 :                 sal_Int32 i = (aTmpStr.getLength() - nEraseChars)/2;
    2037           0 :                 aTmpStr.remove(i, nEraseChars++);
    2038           0 :                 aTmpStr.insert(i, "...");
    2039             :             }
    2040           0 :             aStr = aTmpStr.makeStringAndClear();
    2041             :         }
    2042           0 :         else if ( nStyle & DrawTextFlags::EndEllipsis )
    2043             :         {
    2044           0 :             aStr = aStr.copy(0, nIndex);
    2045           0 :             if ( nIndex > 1 )
    2046             :             {
    2047           0 :                 aStr += "...";
    2048           0 :                 while ( !aStr.isEmpty() && (_rLayout.GetTextWidth( aStr, 0, aStr.getLength() ) > nMaxWidth) )
    2049             :                 {
    2050           0 :                     if ( (nIndex > 1) || (nIndex == aStr.getLength()) )
    2051           0 :                         nIndex--;
    2052           0 :                     aStr = aStr.replaceAt( nIndex, 1, "");
    2053             :                 }
    2054             :             }
    2055             : 
    2056           0 :             if ( aStr.isEmpty() && (nStyle & DrawTextFlags::Clip) )
    2057           0 :                 aStr += OUString(rOrigStr[ 0 ]);
    2058             :         }
    2059           0 :         else if ( nStyle & DrawTextFlags::PathEllipsis )
    2060             :         {
    2061           0 :             OUString aPath( rOrigStr );
    2062           0 :             OUString aAbbreviatedPath;
    2063           0 :             osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, NULL );
    2064           0 :             aStr = aAbbreviatedPath;
    2065             :         }
    2066           0 :         else if ( nStyle & DrawTextFlags::NewsEllipsis )
    2067             :         {
    2068             :             static sal_Char const   pSepChars[] = ".";
    2069             :             // Determine last section
    2070           0 :             sal_Int32 nLastContent = aStr.getLength();
    2071           0 :             while ( nLastContent )
    2072             :             {
    2073           0 :                 nLastContent--;
    2074           0 :                 if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
    2075           0 :                     break;
    2076             :             }
    2077           0 :             while ( nLastContent &&
    2078           0 :                     ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
    2079           0 :                 nLastContent--;
    2080             : 
    2081           0 :             OUString aLastStr = aStr.copy(nLastContent);
    2082           0 :             OUString aTempLastStr1( "..." );
    2083           0 :             aTempLastStr1 += aLastStr;
    2084           0 :             if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.getLength() ) > nMaxWidth )
    2085           0 :                 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
    2086             :             else
    2087             :             {
    2088           0 :                 sal_Int32 nFirstContent = 0;
    2089           0 :                 while ( nFirstContent < nLastContent )
    2090             :                 {
    2091           0 :                     nFirstContent++;
    2092           0 :                     if ( ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
    2093           0 :                         break;
    2094             :                 }
    2095           0 :                 while ( (nFirstContent < nLastContent) &&
    2096           0 :                         ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
    2097           0 :                     nFirstContent++;
    2098             :                 // MEM continue here
    2099           0 :                 if ( nFirstContent >= nLastContent )
    2100           0 :                     aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
    2101             :                 else
    2102             :                 {
    2103           0 :                     if ( nFirstContent > 4 )
    2104           0 :                         nFirstContent = 4;
    2105           0 :                     OUString aFirstStr = aStr.copy( 0, nFirstContent );
    2106           0 :                     aFirstStr += "...";
    2107           0 :                     OUString aTempStr = aFirstStr + aLastStr;
    2108           0 :                     if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
    2109           0 :                         aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | DrawTextFlags::EndEllipsis, _rLayout );
    2110             :                     else
    2111             :                     {
    2112           0 :                         do
    2113             :                         {
    2114           0 :                             aStr = aTempStr;
    2115           0 :                             if( nLastContent > aStr.getLength() )
    2116           0 :                                 nLastContent = aStr.getLength();
    2117           0 :                             while ( nFirstContent < nLastContent )
    2118             :                             {
    2119           0 :                                 nLastContent--;
    2120           0 :                                 if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
    2121           0 :                                     break;
    2122             : 
    2123             :                             }
    2124           0 :                             while ( (nFirstContent < nLastContent) &&
    2125           0 :                                     ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
    2126           0 :                                 nLastContent--;
    2127             : 
    2128           0 :                             if ( nFirstContent < nLastContent )
    2129             :                             {
    2130           0 :                                 OUString aTempLastStr = aStr.copy( nLastContent );
    2131           0 :                                 aTempStr = aFirstStr + aTempLastStr;
    2132             : 
    2133           0 :                                 if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
    2134           0 :                                     break;
    2135             :                             }
    2136             :                         }
    2137             :                         while ( nFirstContent < nLastContent );
    2138           0 :                     }
    2139             :                 }
    2140           0 :             }
    2141             :         }
    2142             :     }
    2143             : 
    2144       10810 :     return aStr;
    2145             : }
    2146             : 
    2147       11068 : void OutputDevice::DrawCtrlText( const Point& rPos, const OUString& rStr,
    2148             :                                  sal_Int32 nIndex, sal_Int32 nLen,
    2149             :                                  DrawTextFlags nStyle, MetricVector* pVector, OUString* pDisplayText )
    2150             : {
    2151       11068 :     assert_if_double_buffered_window();
    2152             : 
    2153       11068 :     if(nLen == 0x0FFFF)
    2154             :     {
    2155             :         SAL_INFO("sal.rtl.xub",
    2156             :                  "DrawCtrlText Suspicious arguments nLen:" << nLen);
    2157             :     }
    2158       11068 :     if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
    2159             :     {
    2160       11068 :         nLen = rStr.getLength() - nIndex;
    2161             :     }
    2162             : 
    2163       11068 :     if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.getLength()) )
    2164           0 :         return;
    2165             : 
    2166             :     // better get graphics here because ImplDrawMnemonicLine() will not
    2167             :     // we need a graphics
    2168       11068 :     if( !mpGraphics && !AcquireGraphics() )
    2169           0 :         return;
    2170       11068 :     if( mbInitClipRegion )
    2171        1416 :         InitClipRegion();
    2172       11068 :     if ( mbOutputClipped )
    2173           0 :         return;
    2174             : 
    2175       11068 :     if( nIndex >= rStr.getLength() )
    2176           0 :         return;
    2177             : 
    2178       11068 :     if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
    2179             :     {
    2180       11068 :         nLen = rStr.getLength() - nIndex;
    2181             :     }
    2182       11068 :     OUString   aStr = rStr;
    2183       11068 :     sal_Int32  nMnemonicPos = -1;
    2184             : 
    2185       11068 :     long        nMnemonicX = 0;
    2186       11068 :     long        nMnemonicY = 0;
    2187       11068 :     long        nMnemonicWidth = 0;
    2188       11068 :     if ( (nStyle & DrawTextFlags::Mnemonic) && nLen > 1 )
    2189             :     {
    2190       10810 :         aStr = GetNonMnemonicString( aStr, nMnemonicPos );
    2191       10810 :         if ( nMnemonicPos != -1 )
    2192             :         {
    2193       10792 :             if( nMnemonicPos < nIndex )
    2194             :             {
    2195           0 :                 --nIndex;
    2196             :             }
    2197             :             else
    2198             :             {
    2199       10792 :                 if( nMnemonicPos < (nIndex+nLen) )
    2200       10792 :                     --nLen;
    2201             :                 DBG_ASSERT( nMnemonicPos < (nIndex+nLen), "Mnemonic underline marker after last character" );
    2202             :             }
    2203       10792 :             bool bInvalidPos = false;
    2204             : 
    2205       10792 :             if( nMnemonicPos >= nLen )
    2206             :             {
    2207             :                 // #106952#
    2208             :                 // may occur in BiDi-Strings: the '~' is sometimes found behind the last char
    2209             :                 // due to some strange BiDi text editors
    2210             :                 // -> place the underline behind the string to indicate a failure
    2211           0 :                 bInvalidPos = true;
    2212           0 :                 nMnemonicPos = nLen-1;
    2213             :             }
    2214             : 
    2215       10792 :             long* pCaretXArray = static_cast<long*>(alloca( 2 * sizeof(long) * nLen ));
    2216       10792 :             /*sal_Bool bRet =*/ GetCaretPositions( aStr, pCaretXArray, nIndex, nLen );
    2217       10792 :             long lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ];
    2218       10792 :             long lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ];
    2219       10792 :             nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
    2220             : 
    2221       10792 :             Point aTempPos( std::min(lc_x1,lc_x2), GetFontMetric().GetAscent() );
    2222       10792 :             if( bInvalidPos )  // #106952#, place behind the (last) character
    2223           0 :                 aTempPos = Point( std::max(lc_x1,lc_x2), GetFontMetric().GetAscent() );
    2224             : 
    2225       10792 :             aTempPos += rPos;
    2226       10792 :             aTempPos = LogicToPixel( aTempPos );
    2227       10792 :             nMnemonicX = mnOutOffX + aTempPos.X();
    2228       10792 :             nMnemonicY = mnOutOffY + aTempPos.Y();
    2229             :         }
    2230             :     }
    2231             : 
    2232       11068 :     if ( nStyle & DrawTextFlags::Disable && ! pVector )
    2233             :     {
    2234         101 :         Color aOldTextColor;
    2235         101 :         Color aOldTextFillColor;
    2236             :         bool  bRestoreFillColor;
    2237         101 :         bool  bHighContrastBlack = false;
    2238         101 :         bool  bHighContrastWhite = false;
    2239         101 :         const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
    2240         101 :         if( rStyleSettings.GetHighContrastMode() )
    2241             :         {
    2242           0 :             if( IsBackground() )
    2243             :             {
    2244           0 :                 Wallpaper aWall = GetBackground();
    2245           0 :                 Color aCol = aWall.GetColor();
    2246           0 :                 bHighContrastBlack = aCol.IsDark();
    2247           0 :                 bHighContrastWhite = aCol.IsBright();
    2248             :             }
    2249             :         }
    2250             : 
    2251         101 :         aOldTextColor = GetTextColor();
    2252         101 :         if ( IsTextFillColor() )
    2253             :         {
    2254           0 :             bRestoreFillColor = true;
    2255           0 :             aOldTextFillColor = GetTextFillColor();
    2256             :         }
    2257             :         else
    2258         101 :             bRestoreFillColor = false;
    2259             : 
    2260         101 :         if( bHighContrastBlack )
    2261           0 :             SetTextColor( COL_GREEN );
    2262         101 :         else if( bHighContrastWhite )
    2263           0 :             SetTextColor( COL_LIGHTGREEN );
    2264             :         else
    2265         101 :             SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
    2266             : 
    2267         101 :         DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
    2268         101 :         if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics) && !pVector )
    2269             :         {
    2270         101 :             if ( nMnemonicPos != -1 )
    2271          56 :                 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
    2272             :         }
    2273         101 :         SetTextColor( aOldTextColor );
    2274         101 :         if ( bRestoreFillColor )
    2275           0 :             SetTextFillColor( aOldTextFillColor );
    2276             :     }
    2277             :     else
    2278             :     {
    2279       10967 :         DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
    2280       10967 :         if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::NoMnemonics) && !pVector )
    2281             :         {
    2282       10936 :             if ( nMnemonicPos != -1 )
    2283       10705 :                 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
    2284             :         }
    2285             :     }
    2286             : 
    2287       11068 :     if( mpAlphaVDev )
    2288           0 :         mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText );
    2289             : }
    2290             : 
    2291       38571 : long OutputDevice::GetCtrlTextWidth( const OUString& rStr,
    2292             :                                      sal_Int32 nIndex, sal_Int32 nLen,
    2293             :                                      DrawTextFlags nStyle ) const
    2294             : {
    2295       38571 :     if(nLen == 0x0FFFF)
    2296             :     {
    2297             :         SAL_INFO("sal.rtl.xub",
    2298             :                  "GetCtrlTextWidth Suspicious arguments nLen:" << nLen);
    2299             :     }
    2300             :     /* defensive code */
    2301       38571 :     if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
    2302             :     {
    2303       38571 :         nLen = rStr.getLength() - nIndex;
    2304             :     }
    2305             : 
    2306       38571 :     if ( nStyle & DrawTextFlags::Mnemonic )
    2307             :     {
    2308             :         sal_Int32  nMnemonicPos;
    2309       38571 :         OUString   aStr = GetNonMnemonicString( rStr, nMnemonicPos );
    2310       38571 :         if ( nMnemonicPos != -1 )
    2311             :         {
    2312        6095 :             if ( nMnemonicPos < nIndex )
    2313           0 :                 nIndex--;
    2314        6095 :             else if ( (nMnemonicPos >= nIndex) && ((sal_uLong)nMnemonicPos < (sal_uLong)(nIndex+nLen)) )
    2315        6095 :                 nLen--;
    2316             :         }
    2317       38571 :         return GetTextWidth( aStr, nIndex, nLen );
    2318             :     }
    2319             :     else
    2320           0 :         return GetTextWidth( rStr, nIndex, nLen );
    2321             : }
    2322             : 
    2323       86402 : OUString OutputDevice::GetNonMnemonicString( const OUString& rStr, sal_Int32& rMnemonicPos )
    2324             : {
    2325       86402 :     OUString   aStr    = rStr;
    2326       86402 :     sal_Int32  nLen    = aStr.getLength();
    2327       86402 :     sal_Int32  i       = 0;
    2328             : 
    2329       86402 :     rMnemonicPos = -1;
    2330     1374328 :     while ( i < nLen )
    2331             :     {
    2332     1201524 :         if ( aStr[ i ] == '~' )
    2333             :         {
    2334       30215 :             if ( nLen <= i+1 )
    2335           0 :                 break;
    2336             : 
    2337       30215 :             if ( aStr[ i+1 ] != '~' )
    2338             :             {
    2339       30215 :                 if ( rMnemonicPos == -1 )
    2340       30215 :                     rMnemonicPos = i;
    2341       30215 :                 aStr = aStr.replaceAt( i, 1, "" );
    2342       30215 :                 nLen--;
    2343             :             }
    2344             :             else
    2345             :             {
    2346           0 :                 aStr = aStr.replaceAt( i, 1, "" );
    2347           0 :                 nLen--;
    2348           0 :                 i++;
    2349             :             }
    2350             :         }
    2351             :         else
    2352     1171309 :             i++;
    2353             :     }
    2354             : 
    2355       86402 :     return aStr;
    2356             : }
    2357             : 
    2358             : /** OutputDevice::GetSysTextLayoutData
    2359             :  *
    2360             :  * @param rStartPt Start point of the text
    2361             :  * @param rStr Text string that will be transformed into layout of glyphs
    2362             :  * @param nIndex Position in the string from where layout will be done
    2363             :  * @param nLen Length of the string
    2364             :  * @param pDXAry Custom layout adjustment data
    2365             :  *
    2366             :  * Export finalized glyph layout data as platform independent SystemTextLayoutData
    2367             :  * (see vcl/inc/vcl/sysdata.hxx)
    2368             :  *
    2369             :  * Only parameters rStartPt and rStr are mandatory, the rest is optional
    2370             :  * (default values will be used)
    2371             :  *
    2372             :  * @return SystemTextLayoutData
    2373             :  **/
    2374           0 : SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen,
    2375             :                                                         const long* pDXAry) const
    2376             : {
    2377           0 :     if(nLen == 0x0FFFF)
    2378             :     {
    2379             :         SAL_INFO("sal.rtl.xub",
    2380             :                  "GetSysTextLayoutData Suspicious arguments nLen:" << nLen);
    2381             :     }
    2382           0 :     if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
    2383             :     {
    2384           0 :         nLen = rStr.getLength() - nIndex;
    2385             :     }
    2386             : 
    2387           0 :     SystemTextLayoutData aSysLayoutData;
    2388           0 :     aSysLayoutData.nSize = sizeof(aSysLayoutData);
    2389           0 :     aSysLayoutData.rGlyphData.reserve( 256 );
    2390           0 :     aSysLayoutData.orientation = 0;
    2391             : 
    2392           0 :     if ( mpMetaFile )
    2393             :     {
    2394           0 :         if (pDXAry)
    2395           0 :             mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
    2396             :         else
    2397           0 :             mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
    2398             :     }
    2399             : 
    2400           0 :     if ( !IsDeviceOutputNecessary() ) return aSysLayoutData;
    2401             : 
    2402           0 :     SalLayout* pLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, pDXAry);
    2403             : 
    2404           0 :     if ( !pLayout ) return aSysLayoutData;
    2405             : 
    2406             :     // setup glyphs
    2407           0 :     Point aPos;
    2408             :     sal_GlyphId aGlyphId;
    2409           0 :     for( int nStart = 0; pLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); )
    2410             :     {
    2411             :         // NOTE: Windows backend is producing unicode chars (ucs4), so on windows,
    2412             :         //       ETO_GLYPH_INDEX is unusable, unless extra glyph conversion is made.
    2413             : 
    2414             :         SystemGlyphData aGlyph;
    2415           0 :         aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK);
    2416           0 :         aGlyph.x = aPos.X();
    2417           0 :         aGlyph.y = aPos.Y();
    2418           0 :         int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
    2419           0 :         aGlyph.fallbacklevel = nLevel < MAX_FALLBACK ? nLevel : 0;
    2420           0 :         aSysLayoutData.rGlyphData.push_back(aGlyph);
    2421             :     }
    2422             : 
    2423             :     // Get font data
    2424           0 :     aSysLayoutData.orientation = pLayout->GetOrientation();
    2425             : 
    2426           0 :     pLayout->Release();
    2427             : 
    2428           0 :     return aSysLayoutData;
    2429             : }
    2430             : 
    2431      299367 : bool OutputDevice::GetTextBoundRect( Rectangle& rRect,
    2432             :                                          const OUString& rStr, sal_Int32 nBase,
    2433             :                                          sal_Int32 nIndex, sal_Int32 nLen,
    2434             :                                          sal_uLong nLayoutWidth, const long* pDXAry ) const
    2435             : {
    2436      299367 :     if(nLen == 0x0FFFF)
    2437             :     {
    2438             :         SAL_INFO("sal.rtl.xub",
    2439             :                  "GetTextBoundRect Suspicious arguments nLen:" << nLen);
    2440             :     }
    2441             : 
    2442      299367 :     bool bRet = false;
    2443      299367 :     rRect.SetEmpty();
    2444             : 
    2445      299367 :     SalLayout* pSalLayout = NULL;
    2446      299367 :     const Point aPoint;
    2447             :     // calculate offset when nBase!=nIndex
    2448      299367 :     long nXOffset = 0;
    2449      299367 :     if( nBase != nIndex )
    2450             :     {
    2451       11959 :         sal_Int32 nStart = std::min( nBase, nIndex );
    2452       11959 :         sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
    2453       11959 :         pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry );
    2454       11959 :         if( pSalLayout )
    2455             :         {
    2456       11959 :             nXOffset = pSalLayout->GetTextWidth();
    2457       11959 :             nXOffset /= pSalLayout->GetUnitsPerPixel();
    2458       11959 :             pSalLayout->Release();
    2459             :             // TODO: fix offset calculation for Bidi case
    2460       11959 :             if( nBase < nIndex)
    2461       11959 :                 nXOffset = -nXOffset;
    2462             :         }
    2463             :     }
    2464             : 
    2465      299367 :     pSalLayout = ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
    2466      299367 :     Rectangle aPixelRect;
    2467      299367 :     if( pSalLayout )
    2468             :     {
    2469      299367 :         bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect );
    2470             : 
    2471      299367 :         if( bRet )
    2472             :         {
    2473      299367 :             int nWidthFactor = pSalLayout->GetUnitsPerPixel();
    2474             : 
    2475      299367 :             if( nWidthFactor > 1 )
    2476             :             {
    2477           0 :                 double fFactor = 1.0 / nWidthFactor;
    2478           0 :                 aPixelRect.Left()
    2479           0 :                     = static_cast< long >(aPixelRect.Left() * fFactor);
    2480           0 :                 aPixelRect.Right()
    2481           0 :                     = static_cast< long >(aPixelRect.Right() * fFactor);
    2482           0 :                 aPixelRect.Top()
    2483           0 :                     = static_cast< long >(aPixelRect.Top() * fFactor);
    2484           0 :                 aPixelRect.Bottom()
    2485           0 :                     = static_cast< long >(aPixelRect.Bottom() * fFactor);
    2486             :             }
    2487             : 
    2488      299367 :             Point aRotatedOfs( mnTextOffX, mnTextOffY );
    2489      299367 :             aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
    2490      299367 :             aPixelRect += aRotatedOfs;
    2491      299367 :             rRect = PixelToLogic( aPixelRect );
    2492      299367 :             if( mbMap )
    2493        9564 :                 rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
    2494             :         }
    2495             : 
    2496      299367 :         pSalLayout->Release();
    2497             :     }
    2498             : 
    2499      299367 :     if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
    2500      299367 :         return bRet;
    2501             : 
    2502             :     // fall back to bitmap method to get the bounding rectangle,
    2503             :     // so we need a monochrome virtual device with matching font
    2504           0 :     ScopedVclPtrInstance< VirtualDevice > aVDev(  1  );
    2505           0 :     vcl::Font aFont( GetFont() );
    2506           0 :     aFont.SetShadow( false );
    2507           0 :     aFont.SetOutline( false );
    2508           0 :     aFont.SetRelief( RELIEF_NONE );
    2509           0 :     aFont.SetOrientation( 0 );
    2510           0 :     aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
    2511           0 :     aVDev->SetFont( aFont );
    2512           0 :     aVDev->SetTextAlign( ALIGN_TOP );
    2513             : 
    2514             :     // layout the text on the virtual device
    2515           0 :     pSalLayout = aVDev->ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
    2516           0 :     if( !pSalLayout )
    2517           0 :         return false;
    2518             : 
    2519             :     // make the bitmap big enough
    2520             :     // TODO: use factors when it would get too big
    2521           0 :     long nWidth = pSalLayout->GetTextWidth();
    2522           0 :     long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
    2523           0 :     Point aOffset( nWidth/2, 8 );
    2524           0 :     Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() );
    2525           0 :     if( !nWidth || !aVDev->SetOutputSizePixel( aOutSize ) )
    2526             :     {
    2527           0 :         pSalLayout->Release();
    2528           0 :         return false;
    2529             :     }
    2530             : 
    2531             :     // draw text in black
    2532           0 :     pSalLayout->DrawBase() = aOffset;
    2533           0 :     aVDev->SetTextColor( Color( COL_BLACK ) );
    2534           0 :     aVDev->SetTextFillColor();
    2535           0 :     aVDev->ImplInitTextColor();
    2536           0 :     aVDev->ImplDrawText( *pSalLayout );
    2537           0 :     pSalLayout->Release();
    2538             : 
    2539             :     // find extents using the bitmap
    2540           0 :     Bitmap aBmp = aVDev->GetBitmap( Point(), aOutSize );
    2541           0 :     BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
    2542           0 :     if( !pAcc )
    2543           0 :         return false;
    2544           0 :     const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
    2545           0 :     const long nW = pAcc->Width();
    2546           0 :     const long nH = pAcc->Height();
    2547           0 :     long nLeft = 0;
    2548           0 :     long nRight = 0;
    2549             : 
    2550             :     // find top left point
    2551           0 :     long nTop = 0;
    2552           0 :     for(; nTop < nH; ++nTop )
    2553             :     {
    2554           0 :         for( nLeft = 0; nLeft < nW; ++nLeft )
    2555           0 :             if( pAcc->GetPixel( nTop, nLeft ) == aBlack )
    2556           0 :                 break;
    2557           0 :         if( nLeft < nW )
    2558           0 :             break;
    2559             :     }
    2560             : 
    2561             :     // find bottom right point
    2562           0 :     long nBottom = nH;
    2563           0 :     while( --nBottom >= nTop )
    2564             :     {
    2565           0 :         for( nRight = nW; --nRight >= 0; )
    2566           0 :             if( pAcc->GetPixel( nBottom, nRight ) == aBlack )
    2567           0 :                 break;
    2568           0 :         if( nRight >= 0 )
    2569           0 :             break;
    2570             :     }
    2571           0 :     if( nRight < nLeft )
    2572             :     {
    2573           0 :         long nX = nRight;
    2574           0 :         nRight = nLeft;
    2575           0 :         nLeft  = nX;
    2576             :     }
    2577             : 
    2578           0 :     for( long nY = nTop; nY <= nBottom; ++nY )
    2579             :     {
    2580             :         // find leftmost point
    2581             :         long nX;
    2582           0 :         for( nX = 0; nX < nLeft; ++nX )
    2583           0 :             if( pAcc->GetPixel( nY, nX ) == aBlack )
    2584           0 :                 break;
    2585           0 :         nLeft = nX;
    2586             : 
    2587             :         // find rightmost point
    2588           0 :         for( nX = nW; --nX > nRight; )
    2589           0 :             if( pAcc->GetPixel( nY, nX ) == aBlack )
    2590           0 :                 break;
    2591           0 :         nRight = nX;
    2592             :     }
    2593             : 
    2594           0 :     Bitmap::ReleaseAccess( pAcc );
    2595             : 
    2596           0 :     if( nTop <= nBottom )
    2597             :     {
    2598           0 :         Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 );
    2599           0 :         Point aTopLeft( nLeft, nTop );
    2600           0 :         aTopLeft -= aOffset;
    2601             :         // adjust to text alignment
    2602           0 :         aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent);
    2603             :         // convert to logical coordinates
    2604           0 :         aSize = PixelToLogic( aSize );
    2605           0 :         aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() );
    2606           0 :         aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() );
    2607           0 :         rRect = Rectangle( aTopLeft, aSize );
    2608           0 :         return true;
    2609             :     }
    2610             : 
    2611           0 :     return false;
    2612             : }
    2613             : 
    2614        1119 : bool OutputDevice::GetTextOutlines( ::basegfx::B2DPolyPolygonVector& rVector,
    2615             :                                         const OUString& rStr, sal_Int32 nBase,
    2616             :                                         sal_Int32 nIndex, sal_Int32 nLen,
    2617             :                                         bool bOptimize, sal_uLong nLayoutWidth, const long* pDXArray ) const
    2618             : {
    2619        1119 :     if(nLen == 0x0FFFF)
    2620             :     {
    2621             :         SAL_INFO("sal.rtl.xub",
    2622             :                  "GetTextOutlines Suspicious arguments nLen:" << nLen);
    2623             :     }
    2624             :     // the fonts need to be initialized
    2625        1119 :     if( mbNewFont )
    2626        1011 :         ImplNewFont();
    2627        1119 :     if( mbInitFont )
    2628         333 :         InitFont();
    2629        1119 :     if( !mpFontEntry )
    2630           0 :         return false;
    2631             : 
    2632        1119 :     bool bRet = false;
    2633        1119 :     rVector.clear();
    2634        1119 :     if( nLen < 0 )
    2635             :     {
    2636         196 :         nLen = rStr.getLength() - nIndex;
    2637             :     }
    2638        1119 :     rVector.reserve( nLen );
    2639             : 
    2640             :     // we want to get the Rectangle in logical units, so to
    2641             :     // avoid rounding errors we just size the font in logical units
    2642        1119 :     bool bOldMap = mbMap;
    2643        1119 :     if( bOldMap )
    2644             :     {
    2645         196 :         const_cast<OutputDevice&>(*this).mbMap = false;
    2646         196 :         const_cast<OutputDevice&>(*this).mbNewFont = true;
    2647             :     }
    2648             : 
    2649        1119 :     SalLayout* pSalLayout = NULL;
    2650             : 
    2651             :     // calculate offset when nBase!=nIndex
    2652        1119 :     long nXOffset = 0;
    2653        1119 :     if( nBase != nIndex )
    2654             :     {
    2655           0 :         sal_Int32 nStart = std::min( nBase, nIndex );
    2656           0 :         sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
    2657           0 :         pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nLayoutWidth, pDXArray );
    2658           0 :         if( pSalLayout )
    2659             :         {
    2660           0 :             nXOffset = pSalLayout->GetTextWidth();
    2661           0 :             pSalLayout->Release();
    2662             :             // TODO: fix offset calculation for Bidi case
    2663           0 :             if( nBase > nIndex)
    2664           0 :                 nXOffset = -nXOffset;
    2665             :         }
    2666             :     }
    2667             : 
    2668        1119 :     pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray );
    2669        1119 :     if( pSalLayout )
    2670             :     {
    2671        1119 :         bRet = pSalLayout->GetOutline( *mpGraphics, rVector );
    2672        1119 :         if( bRet )
    2673             :         {
    2674             :             // transform polygon to pixel units
    2675        1119 :             ::basegfx::B2DHomMatrix aMatrix;
    2676             : 
    2677        1119 :             int nWidthFactor = pSalLayout->GetUnitsPerPixel();
    2678        1119 :             if( nXOffset | mnTextOffX | mnTextOffY )
    2679             :             {
    2680          74 :                 Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor );
    2681          74 :                 aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
    2682          74 :                 aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() );
    2683             :             }
    2684             : 
    2685        1119 :             if( nWidthFactor > 1 )
    2686             :             {
    2687           0 :                 double fFactor = 1.0 / nWidthFactor;
    2688           0 :                 aMatrix.scale( fFactor, fFactor );
    2689             :             }
    2690             : 
    2691        1119 :             if( !aMatrix.isIdentity() )
    2692             :             {
    2693          74 :                 ::basegfx::B2DPolyPolygonVector::iterator aIt = rVector.begin();
    2694         900 :                 for(; aIt != rVector.end(); ++aIt )
    2695         826 :                     (*aIt).transform( aMatrix );
    2696        1119 :             }
    2697             :         }
    2698             : 
    2699        1119 :         pSalLayout->Release();
    2700             :     }
    2701             : 
    2702        1119 :     if( bOldMap )
    2703             :     {
    2704             :         // restore original font size and map mode
    2705         196 :         const_cast<OutputDevice&>(*this).mbMap = bOldMap;
    2706         196 :         const_cast<OutputDevice&>(*this).mbNewFont = true;
    2707             :     }
    2708             : 
    2709        1119 :     if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
    2710        1119 :         return bRet;
    2711             : 
    2712             :     // reset work done (tdf#81876)
    2713           0 :     rVector.clear();
    2714             : 
    2715             :     // fall back to bitmap conversion
    2716             :     // Here, we can savely assume that the mapping between characters and glyphs
    2717             :     // is one-to-one. This is most probably valid for the old bitmap fonts.
    2718             :     // fall back to bitmap method to get the bounding rectangle,
    2719             :     // so we need a monochrome virtual device with matching font
    2720           0 :     pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray );
    2721           0 :     if (pSalLayout == 0)
    2722           0 :         return false;
    2723           0 :     long nOrgWidth = pSalLayout->GetTextWidth();
    2724           0 :     long nOrgHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent
    2725           0 :         + mnEmphasisDescent;
    2726           0 :     pSalLayout->Release();
    2727             : 
    2728           0 :     ScopedVclPtrInstance< VirtualDevice > aVDev( 1 );
    2729             : 
    2730           0 :     vcl::Font aFont(GetFont());
    2731           0 :     aFont.SetShadow(false);
    2732           0 :     aFont.SetOutline(false);
    2733           0 :     aFont.SetRelief(RELIEF_NONE);
    2734           0 :     aFont.SetOrientation(0);
    2735           0 :     if( bOptimize )
    2736             :     {
    2737           0 :         aFont.SetSize( Size( 0, GLYPH_FONT_HEIGHT ) );
    2738           0 :         aVDev->SetMapMode( MAP_PIXEL );
    2739             :     }
    2740           0 :     aVDev->SetFont( aFont );
    2741           0 :     aVDev->SetTextAlign( ALIGN_TOP );
    2742           0 :     aVDev->SetTextColor( Color(COL_BLACK) );
    2743           0 :     aVDev->SetTextFillColor();
    2744             : 
    2745           0 :     pSalLayout = aVDev->ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray );
    2746           0 :     if (pSalLayout == 0)
    2747           0 :         return false;
    2748           0 :     long nWidth = pSalLayout->GetTextWidth();
    2749           0 :     long nHeight = aVDev->mpFontEntry->mnLineHeight + aVDev->mnEmphasisAscent +
    2750           0 :                    aVDev->mnEmphasisDescent;
    2751           0 :     pSalLayout->Release();
    2752             : 
    2753           0 :     if( !nWidth || !nHeight )
    2754           0 :         return true;
    2755           0 :     double fScaleX = static_cast< double >(nOrgWidth) / nWidth;
    2756           0 :     double fScaleY = static_cast< double >(nOrgHeight) / nHeight;
    2757             : 
    2758             :     // calculate offset when nBase!=nIndex
    2759             :     // TODO: fix offset calculation for Bidi case
    2760           0 :     nXOffset = 0;
    2761           0 :     if( nBase != nIndex )
    2762             :     {
    2763           0 :         sal_Int32 nStart  = ((nBase < nIndex) ? nBase : nIndex);
    2764           0 :         sal_Int32 nLength = ((nBase > nIndex) ? nBase : nIndex) - nStart;
    2765           0 :         pSalLayout = aVDev->ImplLayout( rStr, nStart, nLength, Point(0,0), nLayoutWidth, pDXArray );
    2766           0 :         if( pSalLayout )
    2767             :         {
    2768           0 :             nXOffset = pSalLayout->GetTextWidth();
    2769           0 :             pSalLayout->Release();
    2770           0 :             if( nBase > nIndex)
    2771           0 :                 nXOffset = -nXOffset;
    2772             :         }
    2773             :     }
    2774             : 
    2775           0 :     bRet = true;
    2776           0 :     bool bRTL = false;
    2777           0 :     OUString aStr( rStr ); // prepare for e.g. localized digits
    2778           0 :     sal_Int32 nIndex2 = nIndex; // only needed until nIndex is sal_Int32
    2779           0 :     sal_Int32 nLen2 = nLen; // only needed until nLen is sal_Int32
    2780           0 :     ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nIndex2, nLen2, 0, NULL );
    2781           0 :     for( int nCharPos = -1; aLayoutArgs.GetNextPos( &nCharPos, &bRTL);)
    2782             :     {
    2783           0 :         bool bSuccess = false;
    2784             : 
    2785             :         // draw character into virtual device
    2786           0 :         pSalLayout = aVDev->ImplLayout( rStr, nCharPos, 1, Point(0,0), nLayoutWidth, pDXArray );
    2787           0 :         if (pSalLayout == 0)
    2788           0 :             return false;
    2789           0 :         long nCharWidth = pSalLayout->GetTextWidth();
    2790             : 
    2791           0 :         Point aOffset(nCharWidth / 2, 8);
    2792           0 :         Size aSize(nCharWidth + 2 * aOffset.X(), nHeight + 2 * aOffset.Y());
    2793           0 :         bSuccess = (bool)aVDev->SetOutputSizePixel(aSize);
    2794           0 :         if( bSuccess )
    2795             :         {
    2796             :             // draw glyph into virtual device
    2797           0 :             aVDev->Erase();
    2798           0 :             pSalLayout->DrawBase() += aOffset;
    2799           0 :             pSalLayout->DrawBase() += Point( aVDev->mnTextOffX, aVDev->mnTextOffY );
    2800           0 :             pSalLayout->DrawText( *aVDev->mpGraphics );
    2801           0 :             pSalLayout->Release();
    2802             : 
    2803             :             // convert character image into outline
    2804           0 :             Bitmap aBmp( aVDev->GetBitmap(Point(0, 0), aSize));
    2805             : 
    2806           0 :             tools::PolyPolygon aPolyPoly;
    2807           0 :             bool bVectorized = aBmp.Vectorize(aPolyPoly, BmpVectorizeFlags::Outer | BmpVectorizeFlags::ReduceEdges);
    2808           0 :             if( !bVectorized )
    2809           0 :                 bSuccess = false;
    2810             :             else
    2811             :             {
    2812             :                 // convert units to logical width
    2813           0 :                 for (sal_uInt16 j = 0; j < aPolyPoly.Count(); ++j)
    2814             :                 {
    2815           0 :                     Polygon& rPoly = aPolyPoly[j];
    2816           0 :                     for (sal_uInt16 k = 0; k < rPoly.GetSize(); ++k)
    2817             :                     {
    2818           0 :                         Point& rPt = rPoly[k];
    2819           0 :                         rPt -= aOffset;
    2820           0 :                         int nPixelX = rPt.X() - static_cast<OutputDevice*>(aVDev.get())->mnTextOffX + nXOffset;
    2821           0 :                         int nPixelY = rPt.Y() - static_cast<OutputDevice*>(aVDev.get())->mnTextOffY;
    2822           0 :                         rPt.X() = ImplDevicePixelToLogicWidth( nPixelX );
    2823           0 :                         rPt.Y() = ImplDevicePixelToLogicHeight( nPixelY );
    2824             :                     }
    2825             :                 }
    2826             : 
    2827             :                 // ignore "empty" glyphs:
    2828           0 :                 if( aPolyPoly.Count() > 0 )
    2829             :                 {
    2830             :                     // convert  to B2DPolyPolygon
    2831             :                     // TODO: get rid of intermediate tool's PolyPolygon
    2832           0 :                     ::basegfx::B2DPolyPolygon aB2DPolyPoly = aPolyPoly.getB2DPolyPolygon();
    2833           0 :                     ::basegfx::B2DHomMatrix aMatrix;
    2834           0 :                     aMatrix.scale( fScaleX, fScaleY );
    2835           0 :                     int nAngle = GetFont().GetOrientation();
    2836           0 :                     if( nAngle )
    2837           0 :                         aMatrix.rotate( nAngle * F_PI1800 );
    2838           0 :                     aB2DPolyPoly.transform( aMatrix );
    2839           0 :                     rVector.push_back( aB2DPolyPoly );
    2840             :                 }
    2841           0 :             }
    2842             :         }
    2843             : 
    2844           0 :         nXOffset += nCharWidth;
    2845           0 :         bRet = bRet && bSuccess;
    2846             :     }
    2847             : 
    2848           0 :     return bRet;
    2849             : }
    2850             : 
    2851         196 : bool OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector,
    2852             :                                         const OUString& rStr, sal_Int32 nBase,
    2853             :                                         sal_Int32 nIndex, sal_Int32 nLen, bool bOptimize,
    2854             :                                         sal_uLong nTWidth, const long* pDXArray ) const
    2855             : {
    2856         196 :     if(nLen == 0x0FFFF)
    2857             :     {
    2858             :         SAL_INFO("sal.rtl.xub",
    2859             :                  "GetTextOutlines Suspicious arguments  nLen:" << nLen);
    2860             :     }
    2861             : 
    2862         196 :     rResultVector.clear();
    2863             : 
    2864             :     // get the basegfx polypolygon vector
    2865         196 :     ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
    2866         196 :     if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
    2867         196 :                          bOptimize, nTWidth, pDXArray ) )
    2868           0 :         return false;
    2869             : 
    2870             :     // convert to a tool polypolygon vector
    2871         196 :     rResultVector.reserve( aB2DPolyPolyVector.size() );
    2872         196 :     ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
    2873        1144 :     for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
    2874         948 :         rResultVector.push_back(tools::PolyPolygon(*aIt)); // #i76339#
    2875             : 
    2876         196 :     return true;
    2877             : }
    2878             : 
    2879           0 : bool OutputDevice::GetTextOutline( tools::PolyPolygon& rPolyPoly, const OUString& rStr,
    2880             :                                        sal_Int32 nBase, sal_Int32 nIndex, sal_Int32 nLen,
    2881             :                                        bool bOptimize, sal_uLong nTWidth, const long* pDXArray ) const
    2882             : {
    2883           0 :     if(nLen == 0x0FFFF)
    2884             :     {
    2885             :         SAL_INFO("sal.rtl.xub",
    2886             :                  "GetTextOutline Suspicious arguments nLen:" << nLen);
    2887             :     }
    2888           0 :     rPolyPoly.Clear();
    2889             : 
    2890             :     // get the basegfx polypolygon vector
    2891           0 :     ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
    2892           0 :     if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
    2893           0 :                          bOptimize, nTWidth, pDXArray ) )
    2894           0 :         return false;
    2895             : 
    2896             :     // convert and merge into a tool polypolygon
    2897           0 :     ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
    2898           0 :     for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
    2899           0 :         for( unsigned int i = 0; i < aIt->count(); ++i )
    2900           0 :             rPolyPoly.Insert(Polygon((*aIt).getB2DPolygon( i ))); // #i76339#
    2901             : 
    2902           0 :     return true;
    2903         801 : }
    2904             : 
    2905             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11