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