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