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 "i18nlangtag/mslangid.hxx"
21 : #include "i18nlangtag/languagetag.hxx"
22 :
23 : #include "rtl/tencinfo.h"
24 : #include "rtl/logfile.hxx"
25 :
26 : #include "tools/debug.hxx"
27 : #include "tools/poly.hxx"
28 :
29 : #include "basegfx/polygon/b2dpolygon.hxx"
30 : #include "basegfx/polygon/b2dpolypolygon.hxx"
31 : #include "basegfx/matrix/b2dhommatrix.hxx"
32 :
33 : #include "vcl/metric.hxx"
34 : #include "vcl/metaact.hxx"
35 : #include "vcl/gdimtf.hxx"
36 : #include "vcl/virdev.hxx"
37 : #include "vcl/print.hxx"
38 : #include "vcl/event.hxx"
39 : #include "vcl/window.hxx"
40 : #include "vcl/svapp.hxx"
41 : #include "vcl/bmpacc.hxx"
42 : #include "vcl/outdev.hxx"
43 : #include "vcl/edit.hxx"
44 : #include <vcl/settings.hxx>
45 : // declare system types in sysdata.hxx
46 : #include <svsys.h>
47 : #include "vcl/sysdata.hxx"
48 : #include "vcl/unohelp.hxx"
49 : #include "vcl/controllayout.hxx"
50 :
51 : #include "salgdi.hxx"
52 : #include "sallayout.hxx"
53 : #include "svdata.hxx"
54 : #include "impfont.hxx"
55 : #include "outdata.hxx"
56 : #include "outfont.hxx"
57 : #include "outdev.h"
58 : #include "PhysicalFontCollection.hxx"
59 : #include "PhysicalFontFace.hxx"
60 : #include "PhysicalFontFamily.hxx"
61 :
62 : #include "textlayout.hxx"
63 : #include "svids.hrc"
64 : #include "window.h"
65 :
66 : #include "unotools/fontcvt.hxx"
67 : #include "unotools/fontcfg.hxx"
68 :
69 : #include "osl/file.h"
70 :
71 : #include <config_graphite.h>
72 : #if ENABLE_GRAPHITE
73 : #include "graphite_features.hxx"
74 : #endif
75 :
76 : #include "pdfwriter_impl.hxx"
77 :
78 : #include "com/sun/star/beans/PropertyValues.hpp"
79 : #include "com/sun/star/i18n/XBreakIterator.hpp"
80 : #include "com/sun/star/i18n/WordType.hpp"
81 : #include "com/sun/star/linguistic2/LinguServiceManager.hpp"
82 : #include <comphelper/processfactory.hxx>
83 :
84 : #if defined UNX
85 : #define GLYPH_FONT_HEIGHT 128
86 : #else
87 : #define GLYPH_FONT_HEIGHT 256
88 : #endif
89 :
90 : #include "sal/alloca.h"
91 :
92 : #include <cmath>
93 : #include <cstring>
94 :
95 : #include <memory>
96 : #include <algorithm>
97 :
98 : using namespace ::com::sun::star;
99 : using namespace ::com::sun::star::uno;
100 : using namespace ::rtl;
101 : using namespace ::vcl;
102 : using namespace ::utl;
103 :
104 : #define TEXT_DRAW_ELLIPSIS (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS)
105 :
106 : #define UNDERLINE_LAST UNDERLINE_BOLDWAVE
107 : #define STRIKEOUT_LAST STRIKEOUT_X
108 :
109 0 : static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY,
110 : int nOrientation )
111 : {
112 0 : if ( (nOrientation >= 0) && !(nOrientation % 900) )
113 : {
114 0 : if ( (nOrientation >= 3600) )
115 0 : nOrientation %= 3600;
116 :
117 0 : if ( nOrientation )
118 : {
119 0 : rX -= nOriginX;
120 0 : rY -= nOriginY;
121 :
122 0 : if ( nOrientation == 900 )
123 : {
124 0 : long nTemp = rX;
125 0 : rX = rY;
126 0 : rY = -nTemp;
127 : }
128 0 : else if ( nOrientation == 1800 )
129 : {
130 0 : rX = -rX;
131 0 : rY = -rY;
132 : }
133 : else /* ( nOrientation == 2700 ) */
134 : {
135 0 : long nTemp = rX;
136 0 : rX = -rY;
137 0 : rY = nTemp;
138 : }
139 :
140 0 : rX += nOriginX;
141 0 : rY += nOriginY;
142 0 : }
143 : }
144 : else
145 : {
146 0 : double nRealOrientation = nOrientation*F_PI1800;
147 0 : double nCos = cos( nRealOrientation );
148 0 : double nSin = sin( nRealOrientation );
149 :
150 : // Translation...
151 0 : long nX = rX-nOriginX;
152 0 : long nY = rY-nOriginY;
153 :
154 : // Rotation...
155 0 : rX = +((long)(nCos*nX + nSin*nY)) + nOriginX;
156 0 : rY = -((long)(nSin*nX - nCos*nY)) + nOriginY;
157 : }
158 0 : }
159 :
160 0 : void OutputDevice::ImplClearFontData( const bool bNewFontLists )
161 : {
162 : // the currently selected logical font is no longer needed
163 0 : if ( mpFontEntry )
164 : {
165 0 : mpFontCache->Release( mpFontEntry );
166 0 : mpFontEntry = NULL;
167 : }
168 :
169 0 : mbInitFont = true;
170 0 : mbNewFont = true;
171 :
172 0 : if ( bNewFontLists )
173 : {
174 0 : if ( mpGetDevFontList )
175 : {
176 0 : delete mpGetDevFontList;
177 0 : mpGetDevFontList = NULL;
178 : }
179 0 : if ( mpGetDevSizeList )
180 : {
181 0 : delete mpGetDevSizeList;
182 0 : mpGetDevSizeList = NULL;
183 : }
184 :
185 : // release all physically selected fonts on this device
186 0 : if( ImplGetGraphics() )
187 0 : mpGraphics->ReleaseFonts();
188 : }
189 :
190 : // if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter )
191 : {
192 0 : ImplSVData* pSVData = ImplGetSVData();
193 :
194 0 : if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
195 0 : mpFontCache->Invalidate();
196 :
197 0 : if ( bNewFontLists )
198 : {
199 : // we need a graphics
200 0 : if ( ImplGetGraphics() )
201 : {
202 0 : if( mpFontCollection && mpFontCollection != pSVData->maGDIData.mpScreenFontList )
203 0 : mpFontCollection->Clear();
204 :
205 0 : if( mpPDFWriter )
206 : {
207 0 : if( mpFontCollection && mpFontCollection != pSVData->maGDIData.mpScreenFontList )
208 0 : delete mpFontCollection;
209 0 : if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
210 0 : delete mpFontCache;
211 0 : mpFontCollection = 0;
212 0 : mpFontCache = 0;
213 : }
214 : }
215 : }
216 : }
217 :
218 : // also update child windows if needed
219 0 : if ( GetOutDevType() == OUTDEV_WINDOW )
220 : {
221 0 : Window* pChild = ((Window*)this)->mpWindowImpl->mpFirstChild;
222 0 : while ( pChild )
223 : {
224 0 : pChild->ImplClearFontData( true );
225 0 : pChild = pChild->mpWindowImpl->mpNext;
226 : }
227 : }
228 0 : }
229 :
230 0 : void OutputDevice::ImplRefreshFontData( const bool bNewFontLists )
231 : {
232 : // if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter )
233 : {
234 0 : ImplSVData* pSVData = ImplGetSVData();
235 :
236 0 : if ( bNewFontLists )
237 : {
238 : // we need a graphics
239 0 : if ( ImplGetGraphics() )
240 : {
241 0 : if( mpPDFWriter )
242 : {
243 0 : mpFontCollection = pSVData->maGDIData.mpScreenFontList->Clone( true, true );
244 0 : mpFontCache = new ImplFontCache();
245 : }
246 : else
247 : {
248 0 : mpGraphics->GetDevFontList( mpFontCollection );
249 : }
250 : }
251 : }
252 : }
253 :
254 : // also update child windows if needed
255 0 : if ( GetOutDevType() == OUTDEV_WINDOW )
256 : {
257 0 : Window* pChild = ((Window*)this)->mpWindowImpl->mpFirstChild;
258 0 : while ( pChild )
259 : {
260 0 : pChild->ImplRefreshFontData( true );
261 0 : pChild = pChild->mpWindowImpl->mpNext;
262 : }
263 : }
264 0 : }
265 :
266 0 : void OutputDevice::ImplUpdateFontData( bool bNewFontLists )
267 : {
268 0 : ImplClearFontData( bNewFontLists );
269 0 : ImplRefreshFontData( bNewFontLists );
270 0 : }
271 :
272 0 : void OutputDevice::ImplUpdateAllFontData( bool bNewFontLists )
273 : {
274 0 : ImplSVData* pSVData = ImplGetSVData();
275 :
276 0 : ImplUpdateFontDataForAllFrames( &OutputDevice::ImplClearFontData, bNewFontLists );
277 :
278 : // clear global font lists to have them updated
279 0 : pSVData->maGDIData.mpScreenFontCache->Invalidate();
280 0 : if ( bNewFontLists )
281 : {
282 0 : pSVData->maGDIData.mpScreenFontList->Clear();
283 0 : Window * pFrame = pSVData->maWinData.mpFirstFrame;
284 0 : if ( pFrame )
285 : {
286 0 : if ( pFrame->ImplGetGraphics() )
287 : {
288 : // Stupid typecast here and somewhere ((OutputDevice*)&aVDev)->, because bug in .NET2002 compiler
289 0 : OutputDevice *pDevice = (OutputDevice*)pFrame;
290 0 : pDevice->mpGraphics->ClearDevFontCache();
291 0 : pDevice->mpGraphics->GetDevFontList(pFrame->mpWindowImpl->mpFrameData->mpFontCollection);
292 : }
293 : }
294 : }
295 :
296 0 : ImplUpdateFontDataForAllFrames( &OutputDevice::ImplRefreshFontData, bNewFontLists );
297 0 : }
298 :
299 0 : void OutputDevice::ImplUpdateFontDataForAllFrames( const FontUpdateHandler_t pHdl, const bool bNewFontLists )
300 : {
301 0 : ImplSVData* const pSVData = ImplGetSVData();
302 :
303 : // update all windows
304 0 : Window* pFrame = pSVData->maWinData.mpFirstFrame;
305 0 : while ( pFrame )
306 : {
307 0 : ( pFrame->*pHdl )( bNewFontLists );
308 :
309 0 : Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap;
310 0 : while ( pSysWin )
311 : {
312 0 : ( pSysWin->*pHdl )( bNewFontLists );
313 0 : pSysWin = pSysWin->mpWindowImpl->mpNextOverlap;
314 : }
315 :
316 0 : pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
317 : }
318 :
319 : // update all virtual devices
320 0 : VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev;
321 0 : while ( pVirDev )
322 : {
323 0 : ( pVirDev->*pHdl )( bNewFontLists );
324 0 : pVirDev = pVirDev->mpNext;
325 : }
326 :
327 : // update all printers
328 0 : Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter;
329 0 : while ( pPrinter )
330 : {
331 0 : ( pPrinter->*pHdl )( bNewFontLists );
332 0 : pPrinter = pPrinter->mpNext;
333 : }
334 0 : }
335 :
336 0 : void OutputDevice::BeginFontSubstitution()
337 : {
338 0 : ImplSVData* pSVData = ImplGetSVData();
339 0 : pSVData->maGDIData.mbFontSubChanged = false;
340 0 : }
341 :
342 0 : void OutputDevice::EndFontSubstitution()
343 : {
344 0 : ImplSVData* pSVData = ImplGetSVData();
345 0 : if ( pSVData->maGDIData.mbFontSubChanged )
346 : {
347 0 : ImplUpdateAllFontData( false );
348 :
349 0 : Application* pApp = GetpApp();
350 0 : DataChangedEvent aDCEvt( DATACHANGED_FONTSUBSTITUTION );
351 0 : pApp->DataChanged( aDCEvt );
352 0 : pApp->NotifyAllWindows( aDCEvt );
353 0 : pSVData->maGDIData.mbFontSubChanged = false;
354 : }
355 0 : }
356 :
357 0 : void OutputDevice::AddFontSubstitute( const OUString& rFontName,
358 : const OUString& rReplaceFontName,
359 : sal_uInt16 nFlags )
360 : {
361 0 : ImplDirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
362 0 : if( !rpSubst )
363 0 : rpSubst = new ImplDirectFontSubstitution();
364 0 : rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags );
365 0 : ImplGetSVData()->maGDIData.mbFontSubChanged = true;
366 0 : }
367 :
368 0 : void ImplDirectFontSubstitution::AddFontSubstitute( const OUString& rFontName,
369 : const OUString& rSubstFontName, sal_uInt16 nFlags )
370 : {
371 0 : maFontSubstList.push_back( ImplFontSubstEntry( rFontName, rSubstFontName, nFlags ) );
372 0 : }
373 :
374 0 : ImplFontSubstEntry::ImplFontSubstEntry( const OUString& rFontName,
375 : const OUString& rSubstFontName, sal_uInt16 nSubstFlags )
376 : : maName( rFontName )
377 : , maReplaceName( rSubstFontName )
378 0 : , mnFlags( nSubstFlags )
379 : {
380 0 : maSearchName = rFontName;
381 0 : maSearchReplaceName = rSubstFontName;
382 0 : GetEnglishSearchFontName( maSearchName );
383 0 : GetEnglishSearchFontName( maSearchReplaceName );
384 0 : }
385 :
386 0 : void OutputDevice::RemoveFontSubstitute( sal_uInt16 n )
387 : {
388 0 : ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
389 0 : if( pSubst )
390 0 : pSubst->RemoveFontSubstitute( n );
391 0 : }
392 :
393 0 : void ImplDirectFontSubstitution::RemoveFontSubstitute( int nIndex )
394 : {
395 0 : FontSubstList::iterator it = maFontSubstList.begin();
396 0 : for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ;
397 0 : if( it != maFontSubstList.end() )
398 0 : maFontSubstList.erase( it );
399 0 : }
400 :
401 0 : sal_uInt16 OutputDevice::GetFontSubstituteCount()
402 : {
403 0 : const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
404 0 : if( !pSubst )
405 0 : return 0;
406 0 : int nCount = pSubst->GetFontSubstituteCount();
407 0 : return (sal_uInt16)nCount;
408 : }
409 :
410 0 : bool ImplDirectFontSubstitution::FindFontSubstitute( OUString& rSubstName,
411 : const OUString& rSearchName, sal_uInt16 nFlags ) const
412 : {
413 : // TODO: get rid of O(N) searches
414 0 : FontSubstList::const_iterator it = maFontSubstList.begin();
415 0 : for(; it != maFontSubstList.end(); ++it )
416 : {
417 0 : const ImplFontSubstEntry& rEntry = *it;
418 0 : if( ((rEntry.mnFlags & nFlags) || !nFlags)
419 0 : && (rEntry.maSearchName == rSearchName) )
420 : {
421 0 : rSubstName = rEntry.maSearchReplaceName;
422 0 : return true;
423 : }
424 : }
425 :
426 0 : return false;
427 : }
428 :
429 0 : void ImplFontSubstitute( OUString& rFontName )
430 : {
431 : #ifdef DBG_UTIL
432 : OUString aTempName = rFontName;
433 : GetEnglishSearchFontName( aTempName );
434 : DBG_ASSERT( aTempName == rFontName, "ImplFontSubstitute() called without a searchname" );
435 : #endif
436 :
437 0 : OUString aSubstFontName;
438 :
439 : // apply user-configurable font replacement (eg, from the list in Tools->Options)
440 0 : const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
441 0 : if( pSubst && pSubst->FindFontSubstitute( aSubstFontName, rFontName, FONT_SUBSTITUTE_ALWAYS ) )
442 : {
443 0 : rFontName = aSubstFontName;
444 0 : return;
445 0 : }
446 : }
447 :
448 : //hidpi TODO: This routine has hard-coded font-sizes that break places such as DialControl
449 0 : Font OutputDevice::GetDefaultFont( sal_uInt16 nType, LanguageType eLang,
450 : sal_uLong nFlags, const OutputDevice* pOutDev )
451 : {
452 0 : if (!pOutDev) // default is NULL
453 0 : pOutDev = Application::GetDefaultDevice();
454 :
455 : LanguageTag aLanguageTag(
456 0 : ( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW ) ?
457 0 : Application::GetSettings().GetUILanguageTag() :
458 0 : LanguageTag( eLang ));
459 :
460 0 : utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
461 0 : OUString aDefault = rDefaults.getDefaultFont( aLanguageTag, nType );
462 0 : OUString aSearch;
463 :
464 0 : if( !aDefault.isEmpty() )
465 0 : aSearch = aDefault;
466 : else
467 0 : aSearch = rDefaults.getUserInterfaceFont( aLanguageTag ); // use the UI font as a fallback
468 :
469 0 : Font aFont;
470 0 : aFont.SetPitch( PITCH_VARIABLE );
471 :
472 0 : switch ( nType )
473 : {
474 : case DEFAULTFONT_SANS_UNICODE:
475 : case DEFAULTFONT_UI_SANS:
476 0 : aFont.SetFamily( FAMILY_SWISS );
477 0 : break;
478 :
479 : case DEFAULTFONT_SANS:
480 : case DEFAULTFONT_LATIN_HEADING:
481 : case DEFAULTFONT_LATIN_SPREADSHEET:
482 : case DEFAULTFONT_LATIN_DISPLAY:
483 0 : aFont.SetFamily( FAMILY_SWISS );
484 0 : break;
485 :
486 : case DEFAULTFONT_SERIF:
487 : case DEFAULTFONT_LATIN_TEXT:
488 : case DEFAULTFONT_LATIN_PRESENTATION:
489 0 : aFont.SetFamily( FAMILY_ROMAN );
490 0 : break;
491 :
492 : case DEFAULTFONT_FIXED:
493 : case DEFAULTFONT_LATIN_FIXED:
494 : case DEFAULTFONT_UI_FIXED:
495 0 : aFont.SetPitch( PITCH_FIXED );
496 0 : aFont.SetFamily( FAMILY_MODERN );
497 0 : break;
498 :
499 : case DEFAULTFONT_SYMBOL:
500 0 : aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
501 0 : break;
502 :
503 : case DEFAULTFONT_CJK_TEXT:
504 : case DEFAULTFONT_CJK_PRESENTATION:
505 : case DEFAULTFONT_CJK_SPREADSHEET:
506 : case DEFAULTFONT_CJK_HEADING:
507 : case DEFAULTFONT_CJK_DISPLAY:
508 0 : aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
509 0 : break;
510 :
511 : case DEFAULTFONT_CTL_TEXT:
512 : case DEFAULTFONT_CTL_PRESENTATION:
513 : case DEFAULTFONT_CTL_SPREADSHEET:
514 : case DEFAULTFONT_CTL_HEADING:
515 : case DEFAULTFONT_CTL_DISPLAY:
516 0 : aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
517 0 : break;
518 : }
519 :
520 0 : if ( !aSearch.isEmpty() )
521 : {
522 0 : aFont.SetHeight( 12 ); // corresponds to nDefaultHeight
523 0 : aFont.SetWeight( WEIGHT_NORMAL );
524 0 : aFont.SetLanguage( eLang );
525 :
526 0 : if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW )
527 0 : aFont.SetCharSet( osl_getThreadTextEncoding() );
528 :
529 : // Should we only return available fonts on the given device
530 0 : if ( pOutDev )
531 : {
532 0 : pOutDev->ImplInitFontList();
533 :
534 : // Search Font in the FontList
535 0 : OUString aName;
536 0 : OUString aSearchName;
537 0 : sal_Int32 nIndex = 0;
538 0 : do
539 : {
540 0 : aSearchName = GetNextFontToken( aSearch, nIndex );
541 0 : GetEnglishSearchFontName( aSearchName );
542 0 : PhysicalFontFamily* pFontFamily = pOutDev->mpFontCollection->ImplFindBySearchName( aSearchName );
543 0 : if( pFontFamily )
544 : {
545 0 : AddTokenFontName( aName, pFontFamily->GetFamilyName() );
546 0 : if( nFlags & DEFAULTFONT_FLAGS_ONLYONE )
547 0 : break;
548 : }
549 : }
550 0 : while ( nIndex != -1 );
551 0 : aFont.SetName( aName );
552 : }
553 :
554 : // No Name, than set all names
555 0 : if ( aFont.GetName().isEmpty() )
556 : {
557 0 : if ( nFlags & DEFAULTFONT_FLAGS_ONLYONE )
558 : {
559 0 : if( !pOutDev )
560 : {
561 : SAL_WARN ("vcl.gdi", "No default window has been set for the application - we really shouldn't be able to get here");
562 0 : sal_Int32 nIndex = 0;
563 0 : aFont.SetName( aSearch.getToken( 0, ';', nIndex ) );
564 : }
565 : else
566 : {
567 0 : pOutDev->ImplInitFontList();
568 :
569 0 : aFont.SetName( aSearch );
570 :
571 : // convert to pixel height
572 0 : Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetSize() );
573 0 : if ( !aSize.Height() )
574 : {
575 : // use default pixel height only when logical height is zero
576 0 : if ( aFont.GetHeight() )
577 0 : aSize.Height() = 1;
578 : else
579 0 : aSize.Height() = (12*pOutDev->mnDPIY)/72;
580 : }
581 :
582 : // use default width only when logical width is zero
583 0 : if( (0 == aSize.Width()) && (0 != aFont.GetSize().Width()) )
584 0 : aSize.Width() = 1;
585 :
586 : // get the name of the first available font
587 0 : float fExactHeight = static_cast<float>(aSize.Height());
588 0 : ImplFontEntry* pEntry = pOutDev->mpFontCache->GetFontEntry( pOutDev->mpFontCollection, aFont, aSize, fExactHeight );
589 0 : if (pEntry)
590 : {
591 0 : if( pEntry->maFontSelData.mpFontData )
592 0 : aFont.SetName( pEntry->maFontSelData.mpFontData->GetFamilyName() );
593 : else
594 0 : aFont.SetName( pEntry->maFontSelData.maTargetName );
595 : }
596 : }
597 : }
598 : else
599 0 : aFont.SetName( aSearch );
600 : }
601 : }
602 :
603 : #if OSL_DEBUG_LEVEL > 2
604 : const char* s = "DEFAULTFONT_SANS_UNKNOWN";
605 : switch ( nType )
606 : {
607 : case DEFAULTFONT_SANS_UNICODE: s = "DEFAULTFONT_SANS_UNICODE"; break;
608 : case DEFAULTFONT_UI_SANS: s = "DEFAULTFONT_UI_SANS"; break;
609 :
610 : case DEFAULTFONT_SANS: s = "DEFAULTFONT_SANS"; break;
611 : case DEFAULTFONT_LATIN_HEADING: s = "DEFAULTFONT_LATIN_HEADING"; break;
612 : case DEFAULTFONT_LATIN_SPREADSHEET: s = "DEFAULTFONT_LATIN_SPREADSHEET"; break;
613 : case DEFAULTFONT_LATIN_DISPLAY: s = "DEFAULTFONT_LATIN_DISPLAY"; break;
614 :
615 : case DEFAULTFONT_SERIF: s = "DEFAULTFONT_SERIF"; break;
616 : case DEFAULTFONT_LATIN_TEXT: s = "DEFAULTFONT_LATIN_TEXT"; break;
617 : case DEFAULTFONT_LATIN_PRESENTATION: s = "DEFAULTFONT_LATIN_PRESENTATION"; break;
618 :
619 : case DEFAULTFONT_FIXED: s = "DEFAULTFONT_FIXED"; break;
620 : case DEFAULTFONT_LATIN_FIXED: s = "DEFAULTFONT_LATIN_FIXED"; break;
621 : case DEFAULTFONT_UI_FIXED: s = "DEFAULTFONT_UI_FIXED"; break;
622 :
623 : case DEFAULTFONT_SYMBOL: s = "DEFAULTFONT_SYMBOL"; break;
624 :
625 : case DEFAULTFONT_CJK_TEXT: s = "DEFAULTFONT_CJK_TEXT"; break;
626 : case DEFAULTFONT_CJK_PRESENTATION: s = "DEFAULTFONT_CJK_PRESENTATION"; break;
627 : case DEFAULTFONT_CJK_SPREADSHEET: s = "DEFAULTFONT_CJK_SPREADSHEET"; break;
628 : case DEFAULTFONT_CJK_HEADING: s = "DEFAULTFONT_CJK_HEADING"; break;
629 : case DEFAULTFONT_CJK_DISPLAY: s = "DEFAULTFONT_CJK_DISPLAY"; break;
630 :
631 : case DEFAULTFONT_CTL_TEXT: s = "DEFAULTFONT_CTL_TEXT"; break;
632 : case DEFAULTFONT_CTL_PRESENTATION: s = "DEFAULTFONT_CTL_PRESENTATION"; break;
633 : case DEFAULTFONT_CTL_SPREADSHEET: s = "DEFAULTFONT_CTL_SPREADSHEET"; break;
634 : case DEFAULTFONT_CTL_HEADING: s = "DEFAULTFONT_CTL_HEADING"; break;
635 : case DEFAULTFONT_CTL_DISPLAY: s = "DEFAULTFONT_CTL_DISPLAY"; break;
636 : }
637 : fprintf( stderr, " OutputDevice::GetDefaultFont() Type=\"%s\" lang=%d flags=%ld FontName=\"%s\"\n",
638 : s, eLang, nFlags,
639 : OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr()
640 : );
641 : #endif
642 :
643 0 : return aFont;
644 : }
645 :
646 0 : ImplFontEntry::ImplFontEntry( const FontSelectPattern& rFontSelData )
647 : : maFontSelData( rFontSelData )
648 : , maMetric( rFontSelData )
649 : , mpConversion( NULL )
650 : , mnLineHeight( 0 )
651 : , mnRefCount( 1 )
652 : , mnSetFontFlags( 0 )
653 : , mnOwnOrientation( 0 )
654 : , mnOrientation( 0 )
655 : , mbInit( false )
656 0 : , mpUnicodeFallbackList( NULL )
657 : {
658 0 : maFontSelData.mpFontEntry = this;
659 0 : }
660 :
661 0 : ImplFontEntry::~ImplFontEntry()
662 : {
663 0 : delete mpUnicodeFallbackList;
664 0 : }
665 :
666 0 : size_t ImplFontEntry::GFBCacheKey_Hash::operator()( const GFBCacheKey& rData ) const
667 : {
668 : boost::hash<sal_UCS4> a;
669 : boost::hash<int > b;
670 0 : return a(rData.first) ^ b(rData.second);
671 : }
672 :
673 0 : void ImplFontEntry::AddFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const OUString& rFontName )
674 : {
675 0 : if( !mpUnicodeFallbackList )
676 0 : mpUnicodeFallbackList = new UnicodeFallbackList;
677 0 : (*mpUnicodeFallbackList)[ GFBCacheKey(cChar,eWeight) ] = rFontName;
678 0 : }
679 :
680 0 : bool ImplFontEntry::GetFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, OUString* pFontName ) const
681 : {
682 0 : if( !mpUnicodeFallbackList )
683 0 : return false;
684 :
685 0 : UnicodeFallbackList::const_iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) );
686 0 : if( it == mpUnicodeFallbackList->end() )
687 0 : return false;
688 :
689 0 : *pFontName = (*it).second;
690 0 : return true;
691 : }
692 :
693 0 : void ImplFontEntry::IgnoreFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const OUString& rFontName )
694 : {
695 : // DBG_ASSERT( mpUnicodeFallbackList, "ImplFontEntry::IgnoreFallbackForUnicode no list" );
696 0 : UnicodeFallbackList::iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) );
697 : // DBG_ASSERT( it != mpUnicodeFallbackList->end(), "ImplFontEntry::IgnoreFallbackForUnicode no match" );
698 0 : if( it == mpUnicodeFallbackList->end() )
699 0 : return;
700 0 : if( (*it).second == rFontName )
701 0 : mpUnicodeFallbackList->erase( it );
702 : }
703 :
704 0 : FontSelectPatternAttributes::FontSelectPatternAttributes( const Font& rFont,
705 : const OUString& rSearchName, const Size& rSize, float fExactHeight )
706 : : maSearchName( rSearchName )
707 0 : , mnWidth( rSize.Width() )
708 0 : , mnHeight( rSize.Height() )
709 : , mfExactHeight( fExactHeight)
710 0 : , mnOrientation( rFont.GetOrientation() )
711 0 : , meLanguage( rFont.GetLanguage() )
712 0 : , mbVertical( rFont.IsVertical() )
713 : , mbNonAntialiased( false )
714 0 : , mbEmbolden( false )
715 : {
716 0 : maTargetName = GetFamilyName();
717 :
718 0 : rFont.GetFontAttributes( *this );
719 :
720 : // normalize orientation between 0 and 3600
721 0 : if( 3600 <= (unsigned)mnOrientation )
722 : {
723 0 : if( mnOrientation >= 0 )
724 0 : mnOrientation %= 3600;
725 : else
726 0 : mnOrientation = 3600 - (-mnOrientation % 3600);
727 : }
728 :
729 : // normalize width and height
730 0 : if( mnHeight < 0 )
731 0 : mnHeight = -mnHeight;
732 0 : if( mnWidth < 0 )
733 0 : mnWidth = -mnWidth;
734 0 : }
735 :
736 0 : FontSelectPattern::FontSelectPattern( const Font& rFont,
737 : const OUString& rSearchName, const Size& rSize, float fExactHeight)
738 : : FontSelectPatternAttributes(rFont, rSearchName, rSize, fExactHeight)
739 : , mpFontData( NULL )
740 0 : , mpFontEntry( NULL )
741 : {
742 0 : }
743 :
744 : // NOTE: this ctor is still used on Windows. Do not remove.
745 : #ifdef WNT
746 : FontSelectPatternAttributes::FontSelectPatternAttributes( const PhysicalFontFace& rFontData,
747 : const Size& rSize, float fExactHeight, int nOrientation, bool bVertical )
748 : : ImplFontAttributes( rFontData )
749 : , mnWidth( rSize.Width() )
750 : , mnHeight( rSize.Height() )
751 : , mfExactHeight( fExactHeight )
752 : , mnOrientation( nOrientation )
753 : , meLanguage( 0 )
754 : , mbVertical( bVertical )
755 : , mbNonAntialiased( false )
756 : , mbEmbolden( false )
757 : {
758 : maTargetName = maSearchName = GetFamilyName();
759 : // NOTE: no normalization for width/height/orientation
760 : }
761 :
762 : FontSelectPattern::FontSelectPattern( const PhysicalFontFace& rFontData,
763 : const Size& rSize, float fExactHeight, int nOrientation, bool bVertical )
764 : : FontSelectPatternAttributes(rFontData, rSize, fExactHeight, nOrientation, bVertical)
765 : , mpFontData( &rFontData )
766 : , mpFontEntry( NULL )
767 : {
768 : }
769 : #endif
770 :
771 0 : void FontSelectPattern::copyAttributes(const FontSelectPatternAttributes &rAttributes)
772 : {
773 0 : static_cast<FontSelectPatternAttributes&>(*this) = rAttributes;
774 0 : }
775 :
776 0 : size_t ImplFontCache::IFSD_Hash::operator()( const FontSelectPattern& rFSD ) const
777 : {
778 0 : return rFSD.hashCode();
779 : }
780 :
781 0 : size_t FontSelectPatternAttributes::hashCode() const
782 : {
783 : // TODO: does it pay off to improve this hash function?
784 : static FontNameHash aFontNameHash;
785 0 : size_t nHash = aFontNameHash( maSearchName );
786 : #if ENABLE_GRAPHITE
787 : // check for features and generate a unique hash if necessary
788 0 : if (maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
789 : != -1)
790 : {
791 0 : nHash = aFontNameHash( maTargetName );
792 : }
793 : #endif
794 0 : nHash += 11 * mnHeight;
795 0 : nHash += 19 * GetWeight();
796 0 : nHash += 29 * GetSlant();
797 0 : nHash += 37 * mnOrientation;
798 0 : nHash += 41 * meLanguage;
799 0 : if( mbVertical )
800 0 : nHash += 53;
801 0 : return nHash;
802 : }
803 :
804 0 : bool FontSelectPatternAttributes::operator==(const FontSelectPatternAttributes& rOther) const
805 : {
806 0 : if (static_cast<const ImplFontAttributes&>(*this) != static_cast<const ImplFontAttributes&>(rOther))
807 0 : return false;
808 :
809 0 : if (maTargetName != rOther.maTargetName)
810 0 : return false;
811 :
812 0 : if (maSearchName != rOther.maSearchName)
813 0 : return false;
814 :
815 0 : if (mnWidth != rOther.mnWidth)
816 0 : return false;
817 :
818 0 : if (mnHeight != rOther.mnHeight)
819 0 : return false;
820 :
821 0 : if (mfExactHeight != rOther.mfExactHeight)
822 0 : return false;
823 :
824 0 : if (mnOrientation != rOther.mnOrientation)
825 0 : return false;
826 :
827 0 : if (meLanguage != rOther.meLanguage)
828 0 : return false;
829 :
830 0 : if (mbVertical != rOther.mbVertical)
831 0 : return false;
832 :
833 0 : if (mbNonAntialiased != rOther.mbNonAntialiased)
834 0 : return false;
835 :
836 0 : if (mbEmbolden != rOther.mbEmbolden)
837 0 : return false;
838 :
839 0 : if (maItalicMatrix != rOther.maItalicMatrix)
840 0 : return false;
841 :
842 0 : return true;
843 : }
844 :
845 0 : bool ImplFontCache::IFSD_Equal::operator()(const FontSelectPattern& rA, const FontSelectPattern& rB) const
846 : {
847 : // check normalized font family name
848 0 : if( rA.maSearchName != rB.maSearchName )
849 0 : return false;
850 :
851 : // check font transformation
852 0 : if( (rA.mnHeight != rB.mnHeight)
853 0 : || (rA.mnWidth != rB.mnWidth)
854 0 : || (rA.mnOrientation != rB.mnOrientation) )
855 0 : return false;
856 :
857 : // check mapping relevant attributes
858 0 : if( (rA.mbVertical != rB.mbVertical)
859 0 : || (rA.meLanguage != rB.meLanguage) )
860 0 : return false;
861 :
862 : // check font face attributes
863 0 : if( (rA.GetWeight() != rB.GetWeight())
864 0 : || (rA.GetSlant() != rB.GetSlant())
865 : // || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
866 0 : || (rA.GetPitch() != rB.GetPitch()) )
867 0 : return false;
868 :
869 : // check style name
870 0 : if( rA.GetStyleName() != rB.GetStyleName() )
871 0 : return false;
872 :
873 : // Symbol fonts may recode from one type to another So they are only
874 : // safely equivalent for equal targets
875 0 : if (
876 0 : (rA.mpFontData && rA.mpFontData->IsSymbolFont()) ||
877 0 : (rB.mpFontData && rB.mpFontData->IsSymbolFont())
878 : )
879 : {
880 0 : if (rA.maTargetName != rB.maTargetName)
881 0 : return false;
882 : }
883 :
884 : #if ENABLE_GRAPHITE
885 : // check for features
886 0 : if ((rA.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
887 0 : != -1 ||
888 0 : rB.maTargetName.indexOf(grutils::GrFeatureParser::FEAT_PREFIX)
889 0 : != -1) && rA.maTargetName != rB.maTargetName)
890 0 : return false;
891 : #endif
892 :
893 0 : if (rA.mbEmbolden != rB.mbEmbolden)
894 0 : return false;
895 :
896 0 : if (rA.maItalicMatrix != rB.maItalicMatrix)
897 0 : return false;
898 :
899 0 : return true;
900 : }
901 :
902 1 : ImplFontCache::ImplFontCache()
903 : : mpFirstEntry( NULL ),
904 1 : mnRef0Count( 0 )
905 1 : {}
906 :
907 2 : ImplFontCache::~ImplFontCache()
908 : {
909 1 : FontInstanceList::iterator it = maFontInstanceList.begin();
910 1 : for(; it != maFontInstanceList.end(); ++it )
911 : {
912 0 : ImplFontEntry* pEntry = (*it).second;
913 0 : delete pEntry;
914 : }
915 1 : }
916 :
917 0 : ImplFontEntry* ImplFontCache::GetFontEntry( PhysicalFontCollection* pFontList,
918 : const Font& rFont, const Size& rSize, float fExactHeight )
919 : {
920 0 : OUString aSearchName = rFont.GetName();
921 :
922 : // initialize internal font request object
923 0 : FontSelectPattern aFontSelData( rFont, aSearchName, rSize, fExactHeight );
924 0 : return GetFontEntry( pFontList, aFontSelData );
925 : }
926 :
927 0 : ImplFontEntry* ImplFontCache::GetFontEntry( PhysicalFontCollection* pFontList,
928 : FontSelectPattern& aFontSelData )
929 : {
930 : // check if a directly matching logical font instance is already cached,
931 : // the most recently used font usually has a hit rate of >50%
932 0 : ImplFontEntry *pEntry = NULL;
933 0 : PhysicalFontFamily* pFontFamily = NULL;
934 : IFSD_Equal aIFSD_Equal;
935 0 : if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) )
936 0 : pEntry = mpFirstEntry;
937 : else
938 : {
939 0 : FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
940 0 : if( it != maFontInstanceList.end() )
941 0 : pEntry = (*it).second;
942 : }
943 :
944 0 : if( !pEntry ) // no direct cache hit
945 : {
946 : // find the best matching logical font family and update font selector accordingly
947 0 : pFontFamily = pFontList->ImplFindByFont( aFontSelData );
948 : DBG_ASSERT( (pFontFamily != NULL), "ImplFontCache::Get() No logical font found!" );
949 0 : if( pFontFamily )
950 0 : aFontSelData.maSearchName = pFontFamily->GetSearchName();
951 :
952 : // check if an indirectly matching logical font instance is already cached
953 0 : FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
954 0 : if( it != maFontInstanceList.end() )
955 : {
956 : // we have an indirect cache hit
957 0 : pEntry = (*it).second;
958 : }
959 : }
960 :
961 0 : PhysicalFontFace* pFontData = NULL;
962 :
963 0 : if (!pEntry && pFontFamily)// no cache hit => find the best matching physical font face
964 : {
965 0 : bool bOrigWasSymbol = aFontSelData.mpFontData && aFontSelData.mpFontData->IsSymbolFont();
966 0 : pFontData = pFontFamily->FindBestFontFace( aFontSelData );
967 0 : aFontSelData.mpFontData = pFontData;
968 0 : bool bNewIsSymbol = aFontSelData.mpFontData && aFontSelData.mpFontData->IsSymbolFont();
969 :
970 0 : if (bNewIsSymbol != bOrigWasSymbol)
971 : {
972 : // it is possible, though generally unlikely, that at this point we
973 : // will attempt to use a symbol font as a last-ditch fallback for a
974 : // non-symbol font request or vice versa, and by changing
975 : // aFontSelData.mpFontData to/from a symbol font we may now find
976 : // something in the cache that can be reused which previously
977 : // wasn't a candidate
978 0 : FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
979 0 : if( it != maFontInstanceList.end() )
980 0 : pEntry = (*it).second;
981 : }
982 : }
983 :
984 0 : if( pEntry ) // cache hit => use existing font instance
985 : {
986 : // increase the font instance's reference count
987 0 : if( !pEntry->mnRefCount++ )
988 0 : --mnRef0Count;
989 : }
990 :
991 0 : if (!pEntry && pFontData)// still no cache hit => create a new font instance
992 : {
993 : // create a new logical font instance from this physical font face
994 0 : pEntry = pFontData->CreateFontInstance( aFontSelData );
995 :
996 : // if we're subtituting from or to a symbol font we may need a symbol
997 : // conversion table
998 0 : if( pFontData->IsSymbolFont() || aFontSelData.IsSymbolFont() )
999 : {
1000 0 : if( aFontSelData.maTargetName != aFontSelData.maSearchName )
1001 0 : pEntry->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
1002 : }
1003 :
1004 : #ifdef MACOSX
1005 : //It might be better to dig out the font version of the target font
1006 : //to see if it's a modern re-coded apple symbol font in case that
1007 : //font shows up on a different platform
1008 : if (!pEntry->mpConversion &&
1009 : aFontSelData.maTargetName.equalsIgnoreAsciiCase("symbol") &&
1010 : aFontSelData.maSearchName.equalsIgnoreAsciiCase("symbol"))
1011 : {
1012 : pEntry->mpConversion = ConvertChar::GetRecodeData( OUString("Symbol"), OUString("AppleSymbol") );
1013 : }
1014 : #endif
1015 :
1016 : // add the new entry to the cache
1017 0 : maFontInstanceList[ aFontSelData ] = pEntry;
1018 : }
1019 :
1020 0 : mpFirstEntry = pEntry;
1021 0 : return pEntry;
1022 : }
1023 :
1024 0 : ImplFontEntry* ImplFontCache::GetGlyphFallbackFont( PhysicalFontCollection* pFontCollection,
1025 : FontSelectPattern& rFontSelData, int nFallbackLevel, OUString& rMissingCodes )
1026 : {
1027 : // get a candidate font for glyph fallback
1028 : // unless the previously selected font got a device specific substitution
1029 : // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
1030 0 : if( nFallbackLevel >= 1)
1031 : {
1032 0 : PhysicalFontFamily* pFallbackData = NULL;
1033 :
1034 : //fdo#33898 If someone has EUDC installed then they really want that to
1035 : //be used as the first-choice glyph fallback seeing as it's filled with
1036 : //private area codes with don't make any sense in any other font so
1037 : //prioritise it here if it's available. Ideally we would remove from
1038 : //rMissingCodes all the glyphs which it is able to resolve as an
1039 : //optimization, but that's tricky to achieve cross-platform without
1040 : //sufficient heavy-weight code that's likely to undo the value of the
1041 : //optimization
1042 0 : if (nFallbackLevel == 1)
1043 0 : pFallbackData = pFontCollection->FindFontFamily(OUString("EUDC"));
1044 0 : if (!pFallbackData)
1045 0 : pFallbackData = pFontCollection->GetGlyphFallbackFont(rFontSelData, rMissingCodes, nFallbackLevel-1);
1046 : // escape when there are no font candidates
1047 0 : if( !pFallbackData )
1048 0 : return NULL;
1049 : // override the font name
1050 0 : rFontSelData.SetFamilyName( pFallbackData->GetFamilyName() );
1051 : // clear the cached normalized name
1052 0 : rFontSelData.maSearchName = "";
1053 : }
1054 :
1055 0 : ImplFontEntry* pFallbackFont = GetFontEntry( pFontCollection, rFontSelData );
1056 0 : return pFallbackFont;
1057 : }
1058 :
1059 0 : void ImplFontCache::Release( ImplFontEntry* pEntry )
1060 : {
1061 : static const int FONTCACHE_MAX = 50;
1062 :
1063 : DBG_ASSERT( (pEntry->mnRefCount > 0), "ImplFontCache::Release() - font refcount underflow" );
1064 0 : if( --pEntry->mnRefCount > 0 )
1065 0 : return;
1066 :
1067 0 : if( ++mnRef0Count < FONTCACHE_MAX )
1068 0 : return;
1069 :
1070 : // remove unused entries from font instance cache
1071 0 : FontInstanceList::iterator it_next = maFontInstanceList.begin();
1072 0 : while( it_next != maFontInstanceList.end() )
1073 : {
1074 0 : FontInstanceList::iterator it = it_next++;
1075 0 : ImplFontEntry* pFontEntry = (*it).second;
1076 0 : if( pFontEntry->mnRefCount > 0 )
1077 0 : continue;
1078 :
1079 0 : maFontInstanceList.erase( it );
1080 0 : delete pFontEntry;
1081 0 : --mnRef0Count;
1082 : DBG_ASSERT( (mnRef0Count>=0), "ImplFontCache::Release() - refcount0 underflow" );
1083 :
1084 0 : if( mpFirstEntry == pFontEntry )
1085 0 : mpFirstEntry = NULL;
1086 : }
1087 :
1088 : DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Release() - refcount0 mismatch" );
1089 : }
1090 :
1091 0 : void ImplFontCache::Invalidate()
1092 : {
1093 : // delete unreferenced entries
1094 0 : FontInstanceList::iterator it = maFontInstanceList.begin();
1095 0 : for(; it != maFontInstanceList.end(); ++it )
1096 : {
1097 0 : ImplFontEntry* pFontEntry = (*it).second;
1098 0 : if( pFontEntry->mnRefCount > 0 )
1099 0 : continue;
1100 :
1101 0 : delete pFontEntry;
1102 0 : --mnRef0Count;
1103 : }
1104 :
1105 : // #112304# make sure the font cache is really clean
1106 0 : mpFirstEntry = NULL;
1107 0 : maFontInstanceList.clear();
1108 :
1109 : DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Invalidate() - mnRef0Count non-zero" );
1110 0 : }
1111 :
1112 0 : ImplMultiTextLineInfo::ImplMultiTextLineInfo()
1113 : {
1114 0 : mpLines = new PImplTextLineInfo[MULTITEXTLINEINFO_RESIZE];
1115 0 : mnLines = 0;
1116 0 : mnSize = MULTITEXTLINEINFO_RESIZE;
1117 0 : }
1118 :
1119 0 : ImplMultiTextLineInfo::~ImplMultiTextLineInfo()
1120 : {
1121 0 : for( sal_Int32 i = 0; i < mnLines; i++ )
1122 0 : delete mpLines[i];
1123 0 : delete [] mpLines;
1124 0 : }
1125 :
1126 0 : void ImplMultiTextLineInfo::AddLine( ImplTextLineInfo* pLine )
1127 : {
1128 0 : if ( mnSize == mnLines )
1129 : {
1130 0 : mnSize += MULTITEXTLINEINFO_RESIZE;
1131 0 : PImplTextLineInfo* pNewLines = new PImplTextLineInfo[mnSize];
1132 0 : memcpy( pNewLines, mpLines, mnLines*sizeof(PImplTextLineInfo) );
1133 0 : mpLines = pNewLines;
1134 : }
1135 :
1136 0 : mpLines[mnLines] = pLine;
1137 0 : mnLines++;
1138 0 : }
1139 :
1140 0 : void ImplMultiTextLineInfo::Clear()
1141 : {
1142 0 : for( sal_Int32 i = 0; i < mnLines; i++ )
1143 0 : delete mpLines[i];
1144 0 : mnLines = 0;
1145 0 : }
1146 :
1147 0 : FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const Font& rFont )
1148 : {
1149 0 : FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark();
1150 :
1151 : // If no Position is set, then calculate the default position, which
1152 : // depends on the language
1153 0 : if ( !(nEmphasisMark & (EMPHASISMARK_POS_ABOVE | EMPHASISMARK_POS_BELOW)) )
1154 : {
1155 0 : LanguageType eLang = rFont.GetLanguage();
1156 : // In Chinese Simplified the EmphasisMarks are below/left
1157 0 : if (MsLangId::isSimplifiedChinese(eLang))
1158 0 : nEmphasisMark |= EMPHASISMARK_POS_BELOW;
1159 : else
1160 : {
1161 0 : eLang = rFont.GetCJKContextLanguage();
1162 : // In Chinese Simplified the EmphasisMarks are below/left
1163 0 : if (MsLangId::isSimplifiedChinese(eLang))
1164 0 : nEmphasisMark |= EMPHASISMARK_POS_BELOW;
1165 : else
1166 0 : nEmphasisMark |= EMPHASISMARK_POS_ABOVE;
1167 : }
1168 : }
1169 :
1170 0 : return nEmphasisMark;
1171 : }
1172 :
1173 0 : bool OutputDevice::ImplIsUnderlineAbove( const Font& rFont )
1174 : {
1175 0 : if ( !rFont.IsVertical() )
1176 0 : return false;
1177 :
1178 0 : if( (LANGUAGE_JAPANESE == rFont.GetLanguage())
1179 0 : || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) )
1180 : // the underline is right for Japanese only
1181 0 : return true;
1182 :
1183 0 : return false;
1184 : }
1185 :
1186 0 : void OutputDevice::ImplInitFontList() const
1187 : {
1188 0 : if( !mpFontCollection->Count() )
1189 : {
1190 0 : if( mpGraphics || ImplGetGraphics() )
1191 : {
1192 : SAL_INFO( "vcl.gdi", "OutputDevice::ImplInitFontList()" );
1193 0 : mpGraphics->GetDevFontList( mpFontCollection );
1194 :
1195 : // There is absolutely no way there should be no fonts available on the device
1196 0 : if( !mpFontCollection->Count() )
1197 : {
1198 0 : OUString aError( "Application error: no fonts and no vcl resource found on your system" );
1199 0 : ResMgr* pMgr = ImplGetResMgr();
1200 0 : if( pMgr )
1201 : {
1202 0 : OUString aResStr(ResId(SV_ACCESSERROR_NO_FONTS, *pMgr).toString());
1203 0 : if( !aResStr.isEmpty() )
1204 0 : aError = aResStr;
1205 : }
1206 0 : Application::Abort( aError );
1207 : }
1208 : }
1209 : }
1210 0 : }
1211 :
1212 0 : void OutputDevice::ImplInitFont() const
1213 : {
1214 : DBG_TESTSOLARMUTEX();
1215 :
1216 0 : if (!mpFontEntry)
1217 0 : return;
1218 :
1219 0 : if ( mbInitFont )
1220 : {
1221 0 : if ( meOutDevType != OUTDEV_PRINTER )
1222 : {
1223 : // decide if antialiasing is appropriate
1224 0 : bool bNonAntialiased = (GetAntialiasing() & ANTIALIASING_DISABLE_TEXT) != 0;
1225 0 : const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1226 0 : bNonAntialiased |= ((rStyleSettings.GetDisplayOptions() & DISPLAY_OPTION_AA_DISABLE) != 0);
1227 0 : bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontEntry->maFontSelData.mnHeight);
1228 0 : mpFontEntry->maFontSelData.mbNonAntialiased = bNonAntialiased;
1229 : }
1230 :
1231 : // select font in the device layers
1232 0 : mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData), 0 );
1233 0 : mbInitFont = false;
1234 : }
1235 : }
1236 :
1237 0 : void OutputDevice::ImplInitTextColor()
1238 : {
1239 : DBG_TESTSOLARMUTEX();
1240 :
1241 0 : if ( mbInitTextColor )
1242 : {
1243 0 : mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) );
1244 0 : mbInitTextColor = false;
1245 : }
1246 0 : }
1247 :
1248 0 : bool OutputDevice::ImplNewFont() const
1249 : {
1250 : DBG_TESTSOLARMUTEX();
1251 :
1252 : // get correct font list on the PDF writer if necessary
1253 0 : if( mpPDFWriter )
1254 : {
1255 0 : const ImplSVData* pSVData = ImplGetSVData();
1256 0 : if( mpFontCollection == pSVData->maGDIData.mpScreenFontList
1257 0 : || mpFontCache == pSVData->maGDIData.mpScreenFontCache )
1258 0 : const_cast<OutputDevice&>(*this).ImplUpdateFontData( true );
1259 : }
1260 :
1261 0 : if ( !mbNewFont )
1262 0 : return true;
1263 :
1264 : // we need a graphics
1265 0 : if ( !mpGraphics && !ImplGetGraphics() )
1266 0 : return false;
1267 0 : SalGraphics* pGraphics = mpGraphics;
1268 0 : ImplInitFontList();
1269 :
1270 : // convert to pixel height
1271 : // TODO: replace integer based aSize completely with subpixel accurate type
1272 0 : float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetHeight()) );
1273 0 : Size aSize = ImplLogicToDevicePixel( maFont.GetSize() );
1274 0 : if ( !aSize.Height() )
1275 : {
1276 : // use default pixel height only when logical height is zero
1277 0 : if ( maFont.GetSize().Height() )
1278 0 : aSize.Height() = 1;
1279 : else
1280 0 : aSize.Height() = (12*mnDPIY)/72;
1281 0 : fExactHeight = static_cast<float>(aSize.Height());
1282 : }
1283 :
1284 : // select the default width only when logical width is zero
1285 0 : if( (0 == aSize.Width()) && (0 != maFont.GetSize().Width()) )
1286 0 : aSize.Width() = 1;
1287 :
1288 : // get font entry
1289 0 : ImplFontEntry* pOldEntry = mpFontEntry;
1290 0 : mpFontEntry = mpFontCache->GetFontEntry( mpFontCollection, maFont, aSize, fExactHeight );
1291 0 : if( pOldEntry )
1292 0 : mpFontCache->Release( pOldEntry );
1293 :
1294 0 : ImplFontEntry* pFontEntry = mpFontEntry;
1295 :
1296 0 : if (!pFontEntry)
1297 0 : return false;
1298 :
1299 : // mark when lower layers need to get involved
1300 0 : mbNewFont = false;
1301 0 : if( pFontEntry != pOldEntry )
1302 0 : mbInitFont = true;
1303 :
1304 : // select font when it has not been initialized yet
1305 0 : if ( !pFontEntry->mbInit )
1306 : {
1307 0 : ImplInitFont();
1308 :
1309 : // get metric data from device layers
1310 0 : if ( pGraphics )
1311 : {
1312 0 : pFontEntry->mbInit = true;
1313 :
1314 0 : pFontEntry->maMetric.mnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation);
1315 0 : pGraphics->GetFontMetric( &(pFontEntry->maMetric) );
1316 :
1317 0 : pFontEntry->maMetric.ImplInitTextLineSize( this );
1318 0 : pFontEntry->maMetric.ImplInitAboveTextLineSize();
1319 :
1320 0 : pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent + pFontEntry->maMetric.mnDescent;
1321 :
1322 0 : if( pFontEntry->maFontSelData.mnOrientation
1323 0 : && !pFontEntry->maMetric.mnOrientation
1324 0 : && (meOutDevType != OUTDEV_PRINTER) )
1325 : {
1326 0 : pFontEntry->mnOwnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation);
1327 0 : pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation;
1328 : }
1329 : else
1330 0 : pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation;
1331 : }
1332 : }
1333 :
1334 : // enable kerning array if requested
1335 0 : if ( maFont.GetKerning() & KERNING_FONTSPECIFIC )
1336 : {
1337 : // TODO: test if physical font supports kerning and disable if not
1338 0 : if( pFontEntry->maMetric.mbKernableFont )
1339 0 : mbKerning = true;
1340 : }
1341 : else
1342 0 : mbKerning = false;
1343 0 : if ( maFont.GetKerning() & KERNING_ASIAN )
1344 0 : mbKerning = true;
1345 :
1346 : // calculate EmphasisArea
1347 0 : mnEmphasisAscent = 0;
1348 0 : mnEmphasisDescent = 0;
1349 0 : if ( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
1350 : {
1351 0 : FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
1352 0 : long nEmphasisHeight = (pFontEntry->mnLineHeight*250)/1000;
1353 0 : if ( nEmphasisHeight < 1 )
1354 0 : nEmphasisHeight = 1;
1355 0 : if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
1356 0 : mnEmphasisDescent = nEmphasisHeight;
1357 : else
1358 0 : mnEmphasisAscent = nEmphasisHeight;
1359 : }
1360 :
1361 : // calculate text offset depending on TextAlignment
1362 0 : TextAlign eAlign = maFont.GetAlign();
1363 0 : if ( eAlign == ALIGN_BASELINE )
1364 : {
1365 0 : mnTextOffX = 0;
1366 0 : mnTextOffY = 0;
1367 : }
1368 0 : else if ( eAlign == ALIGN_TOP )
1369 : {
1370 0 : mnTextOffX = 0;
1371 0 : mnTextOffY = +pFontEntry->maMetric.mnAscent + mnEmphasisAscent;
1372 0 : if ( pFontEntry->mnOrientation )
1373 0 : ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
1374 : }
1375 : else // eAlign == ALIGN_BOTTOM
1376 : {
1377 0 : mnTextOffX = 0;
1378 0 : mnTextOffY = -pFontEntry->maMetric.mnDescent + mnEmphasisDescent;
1379 0 : if ( pFontEntry->mnOrientation )
1380 0 : ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
1381 : }
1382 :
1383 0 : mbTextLines = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) ||
1384 0 : ((maFont.GetOverline() != UNDERLINE_NONE) && (maFont.GetOverline() != UNDERLINE_DONTKNOW)) ||
1385 0 : ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW));
1386 0 : mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() ||
1387 0 : (maFont.GetRelief() != RELIEF_NONE);
1388 :
1389 : // #95414# fix for OLE objects which use scale factors very creatively
1390 0 : if( mbMap && !aSize.Width() )
1391 : {
1392 0 : int nOrigWidth = pFontEntry->maMetric.mnWidth;
1393 0 : float fStretch = (float)maMapRes.mnMapScNumX * maMapRes.mnMapScDenomY;
1394 0 : fStretch /= (float)maMapRes.mnMapScNumY * maMapRes.mnMapScDenomX;
1395 0 : int nNewWidth = (int)(nOrigWidth * fStretch + 0.5);
1396 0 : if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) )
1397 : {
1398 0 : Size aOrigSize = maFont.GetSize();
1399 0 : const_cast<Font&>(maFont).SetSize( Size( nNewWidth, aSize.Height() ) );
1400 0 : mbMap = false;
1401 0 : mbNewFont = true;
1402 0 : ImplNewFont(); // recurse once using stretched width
1403 0 : mbMap = true;
1404 0 : const_cast<Font&>(maFont).SetSize( aOrigSize );
1405 : }
1406 : }
1407 :
1408 0 : return true;
1409 : }
1410 :
1411 0 : void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY,
1412 : long nDistX, long nDistY, long nWidth, long nHeight )
1413 : {
1414 0 : long nX = nDistX;
1415 0 : long nY = nDistY;
1416 :
1417 0 : short nOrientation = mpFontEntry->mnOrientation;
1418 0 : if ( nOrientation )
1419 : {
1420 : // Rotate rect without rounding problems for 90 degree rotations
1421 0 : if ( !(nOrientation % 900) )
1422 : {
1423 0 : if ( nOrientation == 900 )
1424 : {
1425 0 : long nTemp = nX;
1426 0 : nX = nY;
1427 0 : nY = -nTemp;
1428 0 : nTemp = nWidth;
1429 0 : nWidth = nHeight;
1430 0 : nHeight = nTemp;
1431 0 : nY -= nHeight;
1432 : }
1433 0 : else if ( nOrientation == 1800 )
1434 : {
1435 0 : nX = -nX;
1436 0 : nY = -nY;
1437 0 : nX -= nWidth;
1438 0 : nY -= nHeight;
1439 : }
1440 : else /* ( nOrientation == 2700 ) */
1441 : {
1442 0 : long nTemp = nX;
1443 0 : nX = -nY;
1444 0 : nY = nTemp;
1445 0 : nTemp = nWidth;
1446 0 : nWidth = nHeight;
1447 0 : nHeight = nTemp;
1448 0 : nX -= nWidth;
1449 : }
1450 : }
1451 : else
1452 : {
1453 0 : nX += nBaseX;
1454 0 : nY += nBaseY;
1455 : // inflate because polygons are drawn smaller
1456 0 : Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
1457 0 : Polygon aPoly( aRect );
1458 0 : aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
1459 0 : ImplDrawPolygon( aPoly );
1460 0 : return;
1461 : }
1462 : }
1463 :
1464 0 : nX += nBaseX;
1465 0 : nY += nBaseY;
1466 0 : mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this );
1467 : }
1468 :
1469 0 : void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout )
1470 : {
1471 0 : const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
1472 0 : const Point aBase = rSalLayout.DrawBase();
1473 0 : const long nX = aBase.X();
1474 0 : const long nY = aBase.Y();
1475 :
1476 0 : if ( mbLineColor || mbInitLineColor )
1477 : {
1478 0 : mpGraphics->SetLineColor();
1479 0 : mbInitLineColor = true;
1480 : }
1481 0 : mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) );
1482 0 : mbInitFillColor = true;
1483 :
1484 0 : ImplDrawTextRect( nX, nY, 0, -(mpFontEntry->maMetric.mnAscent + mnEmphasisAscent),
1485 : nWidth,
1486 0 : mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent );
1487 0 : }
1488 :
1489 0 : Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout )
1490 : {
1491 0 : Point aPoint = rSalLayout.GetDrawPosition();
1492 0 : long nX = aPoint.X();
1493 0 : long nY = aPoint.Y();
1494 :
1495 0 : long nWidth = rSalLayout.GetTextWidth();
1496 0 : long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
1497 :
1498 0 : nY -= mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
1499 :
1500 0 : if ( mpFontEntry->mnOrientation )
1501 : {
1502 0 : long nBaseX = nX, nBaseY = nY;
1503 0 : if ( !(mpFontEntry->mnOrientation % 900) )
1504 : {
1505 0 : long nX2 = nX+nWidth;
1506 0 : long nY2 = nY+nHeight;
1507 0 : ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation );
1508 0 : ImplRotatePos( nBaseX, nBaseY, nX2, nY2, mpFontEntry->mnOrientation );
1509 0 : nWidth = nX2-nX;
1510 0 : nHeight = nY2-nY;
1511 : }
1512 : else
1513 : {
1514 : // inflate by +1+1 because polygons are drawn smaller
1515 0 : Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
1516 0 : Polygon aPoly( aRect );
1517 0 : aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
1518 0 : return aPoly.GetBoundRect();
1519 : }
1520 : }
1521 :
1522 0 : return Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) );
1523 : }
1524 :
1525 0 : void OutputDevice::ImplInitTextLineSize()
1526 : {
1527 0 : mpFontEntry->maMetric.ImplInitTextLineSize( this );
1528 0 : }
1529 :
1530 0 : void OutputDevice::ImplInitAboveTextLineSize()
1531 : {
1532 0 : mpFontEntry->maMetric.ImplInitAboveTextLineSize();
1533 0 : }
1534 :
1535 0 : bool ImplFontAttributes::operator==(const ImplFontAttributes& rOther) const
1536 : {
1537 0 : if (maName != rOther.maName)
1538 0 : return false;
1539 :
1540 0 : if (maStyleName != rOther.maStyleName)
1541 0 : return false;
1542 :
1543 0 : if (meWeight != rOther.meWeight)
1544 0 : return false;
1545 :
1546 0 : if (meItalic != rOther.meItalic)
1547 0 : return false;
1548 :
1549 0 : if (meFamily != rOther.meFamily)
1550 0 : return false;
1551 :
1552 0 : if (mePitch != rOther.mePitch)
1553 0 : return false;
1554 :
1555 0 : if (meWidthType != rOther.meWidthType)
1556 0 : return false;
1557 :
1558 0 : if (mbSymbolFlag != rOther.mbSymbolFlag)
1559 0 : return false;
1560 :
1561 0 : return true;
1562 : }
1563 :
1564 0 : ImplFontMetricData::ImplFontMetricData( const FontSelectPattern& rFontSelData )
1565 : : ImplFontAttributes( rFontSelData )
1566 : , mnWidth ( rFontSelData.mnWidth)
1567 : , mnOrientation( (short)(rFontSelData.mnOrientation))
1568 : , mnAscent( 0 )
1569 : , mnDescent( 0 )
1570 : , mnIntLeading( 0 )
1571 : , mnExtLeading( 0 )
1572 : , mnSlant( 0 )
1573 : , mnMinKashida( 0 )
1574 : , mnUnderlineSize( 0 )
1575 : , mnUnderlineOffset( 0 )
1576 : , mnBUnderlineSize( 0 )
1577 : , mnBUnderlineOffset( 0 )
1578 : , mnDUnderlineSize( 0 )
1579 : , mnDUnderlineOffset1( 0 )
1580 : , mnDUnderlineOffset2( 0 )
1581 : , mnWUnderlineSize( 0 )
1582 : , mnWUnderlineOffset( 0 )
1583 : , mnAboveUnderlineSize( 0 )
1584 : , mnAboveUnderlineOffset( 0 )
1585 : , mnAboveBUnderlineSize( 0 )
1586 : , mnAboveBUnderlineOffset( 0 )
1587 : , mnAboveDUnderlineSize( 0 )
1588 : , mnAboveDUnderlineOffset1( 0 )
1589 : , mnAboveDUnderlineOffset2( 0 )
1590 : , mnAboveWUnderlineSize( 0 )
1591 : , mnAboveWUnderlineOffset( 0 )
1592 : , mnStrikeoutSize( 0 )
1593 : , mnStrikeoutOffset( 0 )
1594 : , mnBStrikeoutSize( 0 )
1595 : , mnBStrikeoutOffset( 0 )
1596 : , mnDStrikeoutSize( 0 )
1597 : , mnDStrikeoutOffset1( 0 )
1598 0 : , mnDStrikeoutOffset2( 0 )
1599 : {
1600 : // intialize the used font name
1601 0 : if( rFontSelData.mpFontData )
1602 : {
1603 0 : SetFamilyName( rFontSelData.mpFontData->GetFamilyName() );
1604 0 : SetStyleName( rFontSelData.mpFontData->GetStyleName() );
1605 0 : mbDevice = rFontSelData.mpFontData->mbDevice;
1606 0 : mbKernableFont = true;
1607 : }
1608 : else
1609 : {
1610 0 : sal_Int32 nTokenPos = 0;
1611 0 : SetFamilyName( GetNextFontToken( rFontSelData.GetFamilyName(), nTokenPos ) );
1612 0 : SetStyleName( rFontSelData.GetStyleName() );
1613 0 : mbDevice = false;
1614 0 : mbKernableFont = false;
1615 : }
1616 0 : }
1617 :
1618 0 : void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev )
1619 : {
1620 0 : long nDescent = mnDescent;
1621 0 : if ( nDescent <= 0 )
1622 : {
1623 0 : nDescent = mnAscent / 10;
1624 0 : if ( !nDescent )
1625 0 : nDescent = 1;
1626 : }
1627 :
1628 : // #i55341# for some fonts it is not a good idea to calculate
1629 : // their text line metrics from the real font descent
1630 : // => work around this problem just for these fonts
1631 0 : if( 3*nDescent > mnAscent )
1632 0 : nDescent = mnAscent / 3;
1633 :
1634 0 : long nLineHeight = ((nDescent*25)+50) / 100;
1635 0 : if ( !nLineHeight )
1636 0 : nLineHeight = 1;
1637 0 : long nLineHeight2 = nLineHeight / 2;
1638 0 : if ( !nLineHeight2 )
1639 0 : nLineHeight2 = 1;
1640 :
1641 0 : long nBLineHeight = ((nDescent*50)+50) / 100;
1642 0 : if ( nBLineHeight == nLineHeight )
1643 0 : nBLineHeight++;
1644 0 : long nBLineHeight2 = nBLineHeight/2;
1645 0 : if ( !nBLineHeight2 )
1646 0 : nBLineHeight2 = 1;
1647 :
1648 0 : long n2LineHeight = ((nDescent*16)+50) / 100;
1649 0 : if ( !n2LineHeight )
1650 0 : n2LineHeight = 1;
1651 0 : long n2LineDY = n2LineHeight;
1652 : /* #117909#
1653 : * add some pixels to minimum double line distance on higher resolution devices
1654 : */
1655 0 : long nMin2LineDY = 1 + pDev->ImplGetDPIY()/150;
1656 0 : if ( n2LineDY < nMin2LineDY )
1657 0 : n2LineDY = nMin2LineDY;
1658 0 : long n2LineDY2 = n2LineDY/2;
1659 0 : if ( !n2LineDY2 )
1660 0 : n2LineDY2 = 1;
1661 :
1662 0 : long nUnderlineOffset = mnDescent/2 + 1;
1663 0 : long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3);
1664 :
1665 0 : mnUnderlineSize = nLineHeight;
1666 0 : mnUnderlineOffset = nUnderlineOffset - nLineHeight2;
1667 :
1668 0 : mnBUnderlineSize = nBLineHeight;
1669 0 : mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2;
1670 :
1671 0 : mnDUnderlineSize = n2LineHeight;
1672 0 : mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight;
1673 0 : mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;
1674 :
1675 0 : long nWCalcSize = mnDescent;
1676 0 : if ( nWCalcSize < 6 )
1677 : {
1678 0 : if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
1679 0 : mnWUnderlineSize = nWCalcSize;
1680 : else
1681 0 : mnWUnderlineSize = 3;
1682 : }
1683 : else
1684 0 : mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;
1685 :
1686 : // #109280# the following line assures that wavelnes are never placed below the descent, however
1687 : // for most fonts the waveline then is drawn into the text, so we better keep the old solution
1688 : // pFontEntry->maMetric.mnWUnderlineOffset = pFontEntry->maMetric.mnDescent + 1 - pFontEntry->maMetric.mnWUnderlineSize;
1689 0 : mnWUnderlineOffset = nUnderlineOffset;
1690 :
1691 0 : mnStrikeoutSize = nLineHeight;
1692 0 : mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2;
1693 :
1694 0 : mnBStrikeoutSize = nBLineHeight;
1695 0 : mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2;
1696 :
1697 0 : mnDStrikeoutSize = n2LineHeight;
1698 0 : mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
1699 0 : mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;
1700 0 : }
1701 :
1702 0 : void ImplFontMetricData::ImplInitAboveTextLineSize()
1703 : {
1704 0 : long nIntLeading = mnIntLeading;
1705 : // TODO: assess usage of nLeading below (changed in extleading CWS)
1706 : // if no leading is available, we assume 15% of the ascent
1707 0 : if ( nIntLeading <= 0 )
1708 : {
1709 0 : nIntLeading = mnAscent*15/100;
1710 0 : if ( !nIntLeading )
1711 0 : nIntLeading = 1;
1712 : }
1713 :
1714 0 : long nLineHeight = ((nIntLeading*25)+50) / 100;
1715 0 : if ( !nLineHeight )
1716 0 : nLineHeight = 1;
1717 :
1718 0 : long nBLineHeight = ((nIntLeading*50)+50) / 100;
1719 0 : if ( nBLineHeight == nLineHeight )
1720 0 : nBLineHeight++;
1721 :
1722 0 : long n2LineHeight = ((nIntLeading*16)+50) / 100;
1723 0 : if ( !n2LineHeight )
1724 0 : n2LineHeight = 1;
1725 :
1726 0 : long nCeiling = -mnAscent;
1727 :
1728 0 : mnAboveUnderlineSize = nLineHeight;
1729 0 : mnAboveUnderlineOffset = nCeiling + (nIntLeading - nLineHeight + 1) / 2;
1730 :
1731 0 : mnAboveBUnderlineSize = nBLineHeight;
1732 0 : mnAboveBUnderlineOffset = nCeiling + (nIntLeading - nBLineHeight + 1) / 2;
1733 :
1734 0 : mnAboveDUnderlineSize = n2LineHeight;
1735 0 : mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2;
1736 0 : mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + n2LineHeight + 1) / 2;
1737 :
1738 0 : long nWCalcSize = nIntLeading;
1739 0 : if ( nWCalcSize < 6 )
1740 : {
1741 0 : if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
1742 0 : mnAboveWUnderlineSize = nWCalcSize;
1743 : else
1744 0 : mnAboveWUnderlineSize = 3;
1745 : }
1746 : else
1747 0 : mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100;
1748 :
1749 0 : mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
1750 0 : }
1751 :
1752 0 : static void ImplDrawWavePixel( long nOriginX, long nOriginY,
1753 : long nCurX, long nCurY,
1754 : short nOrientation,
1755 : SalGraphics* pGraphics,
1756 : OutputDevice* pOutDev,
1757 : bool bDrawPixAsRect,
1758 :
1759 : long nPixWidth, long nPixHeight )
1760 : {
1761 0 : if ( nOrientation )
1762 0 : ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation );
1763 :
1764 0 : if ( bDrawPixAsRect )
1765 : {
1766 :
1767 0 : pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev );
1768 : }
1769 : else
1770 : {
1771 0 : pGraphics->DrawPixel( nCurX, nCurY, pOutDev );
1772 : }
1773 0 : }
1774 :
1775 0 : void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY,
1776 : long nDistX, long nDistY,
1777 : long nWidth, long nHeight,
1778 : long nLineWidth, short nOrientation,
1779 : const Color& rColor )
1780 : {
1781 0 : if ( !nHeight )
1782 0 : return;
1783 :
1784 0 : long nStartX = nBaseX + nDistX;
1785 0 : long nStartY = nBaseY + nDistY;
1786 :
1787 : // If the height is 1 pixel, it's enough ouput a line
1788 0 : if ( (nLineWidth == 1) && (nHeight == 1) )
1789 : {
1790 0 : mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
1791 0 : mbInitLineColor = true;
1792 :
1793 0 : long nEndX = nStartX+nWidth;
1794 0 : long nEndY = nStartY;
1795 0 : if ( nOrientation )
1796 : {
1797 0 : ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation );
1798 0 : ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation );
1799 : }
1800 0 : mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this );
1801 : }
1802 : else
1803 : {
1804 0 : long nCurX = nStartX;
1805 0 : long nCurY = nStartY;
1806 0 : long nDiffX = 2;
1807 0 : long nDiffY = nHeight-1;
1808 0 : long nCount = nWidth;
1809 0 : long nOffY = -1;
1810 : long nFreq;
1811 : long i;
1812 : long nPixWidth;
1813 : long nPixHeight;
1814 : bool bDrawPixAsRect;
1815 : // On printers that ouput pixel via DrawRect()
1816 0 : if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) )
1817 : {
1818 0 : if ( mbLineColor || mbInitLineColor )
1819 : {
1820 0 : mpGraphics->SetLineColor();
1821 0 : mbInitLineColor = true;
1822 : }
1823 0 : mpGraphics->SetFillColor( ImplColorToSal( rColor ) );
1824 0 : mbInitFillColor = true;
1825 0 : bDrawPixAsRect = true;
1826 0 : nPixWidth = nLineWidth;
1827 0 : nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
1828 : }
1829 : else
1830 : {
1831 0 : mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
1832 0 : mbInitLineColor = true;
1833 0 : nPixWidth = 1;
1834 0 : nPixHeight = 1;
1835 0 : bDrawPixAsRect = false;
1836 : }
1837 :
1838 0 : if ( !nDiffY )
1839 : {
1840 0 : while ( nWidth )
1841 : {
1842 : ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
1843 : mpGraphics, this,
1844 0 : bDrawPixAsRect, nPixWidth, nPixHeight );
1845 0 : nCurX++;
1846 0 : nWidth--;
1847 : }
1848 : }
1849 : else
1850 : {
1851 0 : nCurY += nDiffY;
1852 0 : nFreq = nCount / (nDiffX+nDiffY);
1853 0 : while ( nFreq-- )
1854 : {
1855 0 : for( i = nDiffY; i; --i )
1856 : {
1857 : ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
1858 : mpGraphics, this,
1859 0 : bDrawPixAsRect, nPixWidth, nPixHeight );
1860 0 : nCurX++;
1861 0 : nCurY += nOffY;
1862 : }
1863 0 : for( i = nDiffX; i; --i )
1864 : {
1865 : ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
1866 : mpGraphics, this,
1867 0 : bDrawPixAsRect, nPixWidth, nPixHeight );
1868 0 : nCurX++;
1869 : }
1870 0 : nOffY = -nOffY;
1871 : }
1872 0 : nFreq = nCount % (nDiffX+nDiffY);
1873 0 : if ( nFreq )
1874 : {
1875 0 : for( i = nDiffY; i && nFreq; --i, --nFreq )
1876 : {
1877 : ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
1878 : mpGraphics, this,
1879 0 : bDrawPixAsRect, nPixWidth, nPixHeight );
1880 0 : nCurX++;
1881 0 : nCurY += nOffY;
1882 :
1883 : }
1884 0 : for( i = nDiffX; i && nFreq; --i, --nFreq )
1885 : {
1886 : ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
1887 : mpGraphics, this,
1888 0 : bDrawPixAsRect, nPixWidth, nPixHeight );
1889 0 : nCurX++;
1890 : }
1891 : }
1892 : }
1893 :
1894 : }
1895 : }
1896 :
1897 0 : void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY,
1898 : long nDistX, long nDistY, long nWidth,
1899 : FontUnderline eTextLine,
1900 : Color aColor,
1901 : bool bIsAbove )
1902 : {
1903 0 : ImplFontEntry* pFontEntry = mpFontEntry;
1904 : long nLineHeight;
1905 : long nLinePos;
1906 :
1907 0 : if ( bIsAbove )
1908 : {
1909 0 : nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize;
1910 0 : nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset;
1911 : }
1912 : else
1913 : {
1914 0 : nLineHeight = pFontEntry->maMetric.mnWUnderlineSize;
1915 0 : nLinePos = pFontEntry->maMetric.mnWUnderlineOffset;
1916 : }
1917 0 : if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
1918 0 : nLineHeight = 3;
1919 0 : long nLineWidth = (mnDPIX/300);
1920 0 : if ( !nLineWidth )
1921 0 : nLineWidth = 1;
1922 0 : if ( eTextLine == UNDERLINE_BOLDWAVE )
1923 0 : nLineWidth *= 2;
1924 0 : nLinePos += nDistY - (nLineHeight / 2);
1925 0 : long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
1926 0 : if ( eTextLine == UNDERLINE_DOUBLEWAVE )
1927 : {
1928 0 : long nOrgLineHeight = nLineHeight;
1929 0 : nLineHeight /= 3;
1930 0 : if ( nLineHeight < 2 )
1931 : {
1932 0 : if ( nOrgLineHeight > 1 )
1933 0 : nLineHeight = 2;
1934 : else
1935 0 : nLineHeight = 1;
1936 : }
1937 0 : long nLineDY = nOrgLineHeight-(nLineHeight*2);
1938 0 : if ( nLineDY < nLineWidthHeight )
1939 0 : nLineDY = nLineWidthHeight;
1940 0 : long nLineDY2 = nLineDY/2;
1941 0 : if ( !nLineDY2 )
1942 0 : nLineDY2 = 1;
1943 :
1944 0 : nLinePos -= nLineWidthHeight-nLineDY2;
1945 : ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
1946 0 : nLineWidth, mpFontEntry->mnOrientation, aColor );
1947 0 : nLinePos += nLineWidthHeight+nLineDY;
1948 : ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
1949 0 : nLineWidth, mpFontEntry->mnOrientation, aColor );
1950 : }
1951 : else
1952 : {
1953 0 : nLinePos -= nLineWidthHeight/2;
1954 : ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
1955 0 : nLineWidth, mpFontEntry->mnOrientation, aColor );
1956 : }
1957 0 : }
1958 :
1959 0 : void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY,
1960 : long nDistX, long nDistY, long nWidth,
1961 : FontUnderline eTextLine,
1962 : Color aColor,
1963 : bool bIsAbove )
1964 : {
1965 0 : ImplFontEntry* pFontEntry = mpFontEntry;
1966 0 : long nLineHeight = 0;
1967 0 : long nLinePos = 0;
1968 0 : long nLinePos2 = 0;
1969 :
1970 0 : const long nY = nDistY;
1971 :
1972 0 : if ( eTextLine > UNDERLINE_LAST )
1973 0 : eTextLine = UNDERLINE_SINGLE;
1974 :
1975 0 : switch ( eTextLine )
1976 : {
1977 : case UNDERLINE_SINGLE:
1978 : case UNDERLINE_DOTTED:
1979 : case UNDERLINE_DASH:
1980 : case UNDERLINE_LONGDASH:
1981 : case UNDERLINE_DASHDOT:
1982 : case UNDERLINE_DASHDOTDOT:
1983 0 : if ( bIsAbove )
1984 : {
1985 0 : nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize;
1986 0 : nLinePos = nY + pFontEntry->maMetric.mnAboveUnderlineOffset;
1987 : }
1988 : else
1989 : {
1990 0 : nLineHeight = pFontEntry->maMetric.mnUnderlineSize;
1991 0 : nLinePos = nY + pFontEntry->maMetric.mnUnderlineOffset;
1992 : }
1993 0 : break;
1994 : case UNDERLINE_BOLD:
1995 : case UNDERLINE_BOLDDOTTED:
1996 : case UNDERLINE_BOLDDASH:
1997 : case UNDERLINE_BOLDLONGDASH:
1998 : case UNDERLINE_BOLDDASHDOT:
1999 : case UNDERLINE_BOLDDASHDOTDOT:
2000 0 : if ( bIsAbove )
2001 : {
2002 0 : nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize;
2003 0 : nLinePos = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset;
2004 : }
2005 : else
2006 : {
2007 0 : nLineHeight = pFontEntry->maMetric.mnBUnderlineSize;
2008 0 : nLinePos = nY + pFontEntry->maMetric.mnBUnderlineOffset;
2009 : }
2010 0 : break;
2011 : case UNDERLINE_DOUBLE:
2012 0 : if ( bIsAbove )
2013 : {
2014 0 : nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize;
2015 0 : nLinePos = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1;
2016 0 : nLinePos2 = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2;
2017 : }
2018 : else
2019 : {
2020 0 : nLineHeight = pFontEntry->maMetric.mnDUnderlineSize;
2021 0 : nLinePos = nY + pFontEntry->maMetric.mnDUnderlineOffset1;
2022 0 : nLinePos2 = nY + pFontEntry->maMetric.mnDUnderlineOffset2;
2023 : }
2024 0 : break;
2025 : default:
2026 0 : break;
2027 : }
2028 :
2029 0 : if ( nLineHeight )
2030 : {
2031 0 : if ( mbLineColor || mbInitLineColor )
2032 : {
2033 0 : mpGraphics->SetLineColor();
2034 0 : mbInitLineColor = true;
2035 : }
2036 0 : mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
2037 0 : mbInitFillColor = true;
2038 :
2039 0 : long nLeft = nDistX;
2040 :
2041 0 : switch ( eTextLine )
2042 : {
2043 : case UNDERLINE_SINGLE:
2044 : case UNDERLINE_BOLD:
2045 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
2046 0 : break;
2047 : case UNDERLINE_DOUBLE:
2048 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
2049 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
2050 0 : break;
2051 : case UNDERLINE_DOTTED:
2052 : case UNDERLINE_BOLDDOTTED:
2053 : {
2054 0 : long nDotWidth = nLineHeight*mnDPIY;
2055 0 : nDotWidth += mnDPIY/2;
2056 0 : nDotWidth /= mnDPIY;
2057 0 : long nTempWidth = nDotWidth;
2058 0 : long nEnd = nLeft+nWidth;
2059 0 : while ( nLeft < nEnd )
2060 : {
2061 0 : if ( nLeft+nTempWidth > nEnd )
2062 0 : nTempWidth = nEnd-nLeft;
2063 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
2064 0 : nLeft += nDotWidth*2;
2065 : }
2066 : }
2067 0 : break;
2068 : case UNDERLINE_DASH:
2069 : case UNDERLINE_LONGDASH:
2070 : case UNDERLINE_BOLDDASH:
2071 : case UNDERLINE_BOLDLONGDASH:
2072 : {
2073 0 : long nDotWidth = nLineHeight*mnDPIY;
2074 0 : nDotWidth += mnDPIY/2;
2075 0 : nDotWidth /= mnDPIY;
2076 : long nMinDashWidth;
2077 : long nMinSpaceWidth;
2078 : long nSpaceWidth;
2079 : long nDashWidth;
2080 0 : if ( (eTextLine == UNDERLINE_LONGDASH) ||
2081 : (eTextLine == UNDERLINE_BOLDLONGDASH) )
2082 : {
2083 0 : nMinDashWidth = nDotWidth*6;
2084 0 : nMinSpaceWidth = nDotWidth*2;
2085 0 : nDashWidth = 200;
2086 0 : nSpaceWidth = 100;
2087 : }
2088 : else
2089 : {
2090 0 : nMinDashWidth = nDotWidth*4;
2091 0 : nMinSpaceWidth = (nDotWidth*150)/100;
2092 0 : nDashWidth = 100;
2093 0 : nSpaceWidth = 50;
2094 : }
2095 0 : nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540;
2096 0 : nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540;
2097 : // DashWidth will be increased if the line is getting too thick
2098 : // in proportion to the line's length
2099 0 : if ( nDashWidth < nMinDashWidth )
2100 0 : nDashWidth = nMinDashWidth;
2101 0 : if ( nSpaceWidth < nMinSpaceWidth )
2102 0 : nSpaceWidth = nMinSpaceWidth;
2103 0 : long nTempWidth = nDashWidth;
2104 0 : long nEnd = nLeft+nWidth;
2105 0 : while ( nLeft < nEnd )
2106 : {
2107 0 : if ( nLeft+nTempWidth > nEnd )
2108 0 : nTempWidth = nEnd-nLeft;
2109 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
2110 0 : nLeft += nDashWidth+nSpaceWidth;
2111 : }
2112 : }
2113 0 : break;
2114 : case UNDERLINE_DASHDOT:
2115 : case UNDERLINE_BOLDDASHDOT:
2116 : {
2117 0 : long nDotWidth = nLineHeight*mnDPIY;
2118 0 : nDotWidth += mnDPIY/2;
2119 0 : nDotWidth /= mnDPIY;
2120 0 : long nDashWidth = ((100*mnDPIX)+1270)/2540;
2121 0 : long nMinDashWidth = nDotWidth*4;
2122 : // DashWidth will be increased if the line is getting too thick
2123 : // in proportion to the line's length
2124 0 : if ( nDashWidth < nMinDashWidth )
2125 0 : nDashWidth = nMinDashWidth;
2126 0 : long nTempDotWidth = nDotWidth;
2127 0 : long nTempDashWidth = nDashWidth;
2128 0 : long nEnd = nLeft+nWidth;
2129 0 : while ( nLeft < nEnd )
2130 : {
2131 0 : if ( nLeft+nTempDotWidth > nEnd )
2132 0 : nTempDotWidth = nEnd-nLeft;
2133 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
2134 0 : nLeft += nDotWidth*2;
2135 0 : if ( nLeft > nEnd )
2136 0 : break;
2137 0 : if ( nLeft+nTempDashWidth > nEnd )
2138 0 : nTempDashWidth = nEnd-nLeft;
2139 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
2140 0 : nLeft += nDashWidth+nDotWidth;
2141 : }
2142 : }
2143 0 : break;
2144 : case UNDERLINE_DASHDOTDOT:
2145 : case UNDERLINE_BOLDDASHDOTDOT:
2146 : {
2147 0 : long nDotWidth = nLineHeight*mnDPIY;
2148 0 : nDotWidth += mnDPIY/2;
2149 0 : nDotWidth /= mnDPIY;
2150 0 : long nDashWidth = ((100*mnDPIX)+1270)/2540;
2151 0 : long nMinDashWidth = nDotWidth*4;
2152 : // DashWidth will be increased if the line is getting too thick
2153 : // in proportion to the line's length
2154 0 : if ( nDashWidth < nMinDashWidth )
2155 0 : nDashWidth = nMinDashWidth;
2156 0 : long nTempDotWidth = nDotWidth;
2157 0 : long nTempDashWidth = nDashWidth;
2158 0 : long nEnd = nLeft+nWidth;
2159 0 : while ( nLeft < nEnd )
2160 : {
2161 0 : if ( nLeft+nTempDotWidth > nEnd )
2162 0 : nTempDotWidth = nEnd-nLeft;
2163 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
2164 0 : nLeft += nDotWidth*2;
2165 0 : if ( nLeft > nEnd )
2166 0 : break;
2167 0 : if ( nLeft+nTempDotWidth > nEnd )
2168 0 : nTempDotWidth = nEnd-nLeft;
2169 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
2170 0 : nLeft += nDotWidth*2;
2171 0 : if ( nLeft > nEnd )
2172 0 : break;
2173 0 : if ( nLeft+nTempDashWidth > nEnd )
2174 0 : nTempDashWidth = nEnd-nLeft;
2175 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
2176 0 : nLeft += nDashWidth+nDotWidth;
2177 : }
2178 : }
2179 0 : break;
2180 : default:
2181 0 : break;
2182 : }
2183 : }
2184 0 : }
2185 :
2186 0 : void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY,
2187 : long nDistX, long nDistY, long nWidth,
2188 : FontStrikeout eStrikeout,
2189 : Color aColor )
2190 : {
2191 0 : ImplFontEntry* pFontEntry = mpFontEntry;
2192 0 : long nLineHeight = 0;
2193 0 : long nLinePos = 0;
2194 0 : long nLinePos2 = 0;
2195 :
2196 0 : long nY = nDistY;
2197 :
2198 0 : if ( eStrikeout > STRIKEOUT_LAST )
2199 0 : eStrikeout = STRIKEOUT_SINGLE;
2200 :
2201 0 : switch ( eStrikeout )
2202 : {
2203 : case STRIKEOUT_SINGLE:
2204 0 : nLineHeight = pFontEntry->maMetric.mnStrikeoutSize;
2205 0 : nLinePos = nY + pFontEntry->maMetric.mnStrikeoutOffset;
2206 0 : break;
2207 : case STRIKEOUT_BOLD:
2208 0 : nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize;
2209 0 : nLinePos = nY + pFontEntry->maMetric.mnBStrikeoutOffset;
2210 0 : break;
2211 : case STRIKEOUT_DOUBLE:
2212 0 : nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize;
2213 0 : nLinePos = nY + pFontEntry->maMetric.mnDStrikeoutOffset1;
2214 0 : nLinePos2 = nY + pFontEntry->maMetric.mnDStrikeoutOffset2;
2215 0 : break;
2216 : default:
2217 0 : break;
2218 : }
2219 :
2220 0 : if ( nLineHeight )
2221 : {
2222 0 : if ( mbLineColor || mbInitLineColor )
2223 : {
2224 0 : mpGraphics->SetLineColor();
2225 0 : mbInitLineColor = true;
2226 : }
2227 0 : mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
2228 0 : mbInitFillColor = true;
2229 :
2230 0 : const long& nLeft = nDistX;
2231 :
2232 0 : switch ( eStrikeout )
2233 : {
2234 : case STRIKEOUT_SINGLE:
2235 : case STRIKEOUT_BOLD:
2236 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
2237 0 : break;
2238 : case STRIKEOUT_DOUBLE:
2239 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
2240 0 : ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
2241 0 : break;
2242 : default:
2243 0 : break;
2244 : }
2245 : }
2246 0 : }
2247 :
2248 0 : void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY,
2249 : long nDistX, long nDistY, long nWidth,
2250 : FontStrikeout eStrikeout,
2251 : Color aColor )
2252 : {
2253 : // See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
2254 : // to tweak this
2255 0 : if (!nWidth)
2256 0 : return;
2257 :
2258 : // prepare string for strikeout measurement
2259 : static char cStrikeoutChar;
2260 0 : if ( eStrikeout == STRIKEOUT_SLASH )
2261 0 : cStrikeoutChar = '/';
2262 : else // ( eStrikeout == STRIKEOUT_X )
2263 0 : cStrikeoutChar = 'X';
2264 : static const int nTestStrLen = 4;
2265 : static const int nMaxStrikeStrLen = 2048;
2266 : sal_Unicode aChars[nMaxStrikeStrLen+1]; // +1 for valgrind...
2267 0 : for( int i = 0; i < nTestStrLen; ++i)
2268 0 : aChars[i] = cStrikeoutChar;
2269 0 : const OUString aStrikeoutTest(aChars, nTestStrLen);
2270 :
2271 : // calculate approximation of strikeout atom size
2272 0 : long nStrikeoutWidth = 0;
2273 0 : SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen );
2274 0 : if( pLayout )
2275 : {
2276 0 : nStrikeoutWidth = pLayout->GetTextWidth() / (nTestStrLen * pLayout->GetUnitsPerPixel());
2277 0 : pLayout->Release();
2278 : }
2279 0 : if( nStrikeoutWidth <= 0 ) // sanity check
2280 0 : return;
2281 :
2282 0 : int nStrikeStrLen = (nWidth+(nStrikeoutWidth-1)) / nStrikeoutWidth;
2283 0 : if( nStrikeStrLen > nMaxStrikeStrLen )
2284 0 : nStrikeStrLen = nMaxStrikeStrLen;
2285 :
2286 : // build the strikeout string
2287 0 : for( int i = nTestStrLen; i < nStrikeStrLen; ++i)
2288 0 : aChars[i] = cStrikeoutChar;
2289 0 : const OUString aStrikeoutText(aChars, nStrikeStrLen);
2290 :
2291 0 : if( mpFontEntry->mnOrientation )
2292 0 : ImplRotatePos( 0, 0, nDistX, nDistY, mpFontEntry->mnOrientation );
2293 0 : nBaseX += nDistX;
2294 0 : nBaseY += nDistY;
2295 :
2296 : // strikeout text has to be left aligned
2297 0 : sal_uLong nOrigTLM = mnTextLayoutMode;
2298 0 : mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED;
2299 0 : pLayout = ImplLayout( aStrikeoutText, 0, aStrikeoutText.getLength() );
2300 0 : mnTextLayoutMode = nOrigTLM;
2301 :
2302 0 : if( !pLayout )
2303 0 : return;
2304 :
2305 : // draw the strikeout text
2306 0 : const Color aOldColor = GetTextColor();
2307 0 : SetTextColor( aColor );
2308 0 : ImplInitTextColor();
2309 :
2310 0 : pLayout->DrawBase() = Point( nBaseX+mnTextOffX, nBaseY+mnTextOffY );
2311 :
2312 0 : Rectangle aPixelRect;
2313 0 : aPixelRect.Left() = nBaseX+mnTextOffX;
2314 0 : aPixelRect.Right() = aPixelRect.Left()+nWidth;
2315 0 : aPixelRect.Bottom() = nBaseY+mpFontEntry->maMetric.mnDescent;
2316 0 : aPixelRect.Top() = nBaseY-mpFontEntry->maMetric.mnAscent;
2317 :
2318 0 : if (mpFontEntry->mnOrientation)
2319 : {
2320 0 : Polygon aPoly( aPixelRect );
2321 0 : aPoly.Rotate( Point(nBaseX+mnTextOffX, nBaseY+mnTextOffY), mpFontEntry->mnOrientation);
2322 0 : aPixelRect = aPoly.GetBoundRect();
2323 : }
2324 :
2325 0 : Push( PUSH_CLIPREGION );
2326 0 : IntersectClipRegion( PixelToLogic(aPixelRect) );
2327 0 : if( mbInitClipRegion )
2328 0 : ImplInitClipRegion();
2329 :
2330 0 : pLayout->DrawText( *mpGraphics );
2331 :
2332 0 : pLayout->Release();
2333 0 : Pop();
2334 :
2335 0 : SetTextColor( aOldColor );
2336 0 : ImplInitTextColor();
2337 : }
2338 :
2339 0 : void OutputDevice::ImplDrawTextLine( long nX, long nY,
2340 : long nDistX, long nWidth,
2341 : FontStrikeout eStrikeout,
2342 : FontUnderline eUnderline,
2343 : FontUnderline eOverline,
2344 : bool bUnderlineAbove )
2345 : {
2346 0 : if ( !nWidth )
2347 0 : return;
2348 :
2349 0 : Color aStrikeoutColor = GetTextColor();
2350 0 : Color aUnderlineColor = GetTextLineColor();
2351 0 : Color aOverlineColor = GetOverlineColor();
2352 0 : bool bStrikeoutDone = false;
2353 0 : bool bUnderlineDone = false;
2354 0 : bool bOverlineDone = false;
2355 :
2356 0 : if ( IsRTLEnabled() )
2357 : {
2358 : // --- RTL --- mirror at basex
2359 0 : long nXAdd = nWidth - nDistX;
2360 0 : if( mpFontEntry->mnOrientation )
2361 0 : nXAdd = FRound( nXAdd * cos( mpFontEntry->mnOrientation * F_PI1800 ) );
2362 0 : nX += nXAdd - 1;
2363 : }
2364 :
2365 0 : if ( !IsTextLineColor() )
2366 0 : aUnderlineColor = GetTextColor();
2367 :
2368 0 : if ( !IsOverlineColor() )
2369 0 : aOverlineColor = GetTextColor();
2370 :
2371 0 : if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
2372 0 : (eUnderline == UNDERLINE_WAVE) ||
2373 0 : (eUnderline == UNDERLINE_DOUBLEWAVE) ||
2374 : (eUnderline == UNDERLINE_BOLDWAVE) )
2375 : {
2376 0 : ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
2377 0 : bUnderlineDone = true;
2378 : }
2379 0 : if ( (eOverline == UNDERLINE_SMALLWAVE) ||
2380 0 : (eOverline == UNDERLINE_WAVE) ||
2381 0 : (eOverline == UNDERLINE_DOUBLEWAVE) ||
2382 : (eOverline == UNDERLINE_BOLDWAVE) )
2383 : {
2384 0 : ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true );
2385 0 : bOverlineDone = true;
2386 : }
2387 :
2388 0 : if ( (eStrikeout == STRIKEOUT_SLASH) ||
2389 : (eStrikeout == STRIKEOUT_X) )
2390 : {
2391 0 : ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
2392 0 : bStrikeoutDone = true;
2393 : }
2394 :
2395 0 : if ( !bUnderlineDone )
2396 0 : ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
2397 :
2398 0 : if ( !bOverlineDone )
2399 0 : ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, true );
2400 :
2401 0 : if ( !bStrikeoutDone )
2402 0 : ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
2403 : }
2404 :
2405 0 : void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout,
2406 : FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bWordLine, bool bUnderlineAbove )
2407 : {
2408 0 : if( bWordLine )
2409 : {
2410 : // draw everything relative to the layout base point
2411 0 : const Point aStartPt = rSalLayout.DrawBase();
2412 :
2413 : // calculate distance of each word from the base point
2414 0 : Point aPos;
2415 0 : sal_Int32 nDist = 0, nWidth = 0, nAdvance=0;
2416 0 : for( int nStart = 0;;)
2417 : {
2418 : // iterate through the layouted glyphs
2419 : sal_GlyphId aGlyphId;
2420 0 : if( !rSalLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
2421 0 : break;
2422 :
2423 : // calculate the boundaries of each word
2424 0 : if( !rSalLayout.IsSpacingGlyph( aGlyphId ) )
2425 : {
2426 0 : if( !nWidth )
2427 : {
2428 : // get the distance to the base point (as projected to baseline)
2429 0 : nDist = aPos.X() - aStartPt.X();
2430 0 : if( mpFontEntry->mnOrientation )
2431 : {
2432 0 : const long nDY = aPos.Y() - aStartPt.Y();
2433 0 : const double fRad = mpFontEntry->mnOrientation * F_PI1800;
2434 0 : nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) );
2435 : }
2436 : }
2437 :
2438 : // update the length of the textline
2439 0 : nWidth += nAdvance;
2440 : }
2441 0 : else if( nWidth > 0 )
2442 : {
2443 : // draw the textline for each word
2444 : ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth,
2445 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
2446 0 : nWidth = 0;
2447 : }
2448 0 : }
2449 :
2450 : // draw textline for the last word
2451 0 : if( nWidth > 0 )
2452 : {
2453 : ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth,
2454 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
2455 : }
2456 : }
2457 : else
2458 : {
2459 0 : Point aStartPt = rSalLayout.GetDrawPosition();
2460 0 : int nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
2461 0 : ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), 0, nWidth,
2462 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
2463 : }
2464 0 : }
2465 :
2466 0 : void OutputDevice::ImplDrawMnemonicLine( long nX, long nY, long nWidth )
2467 : {
2468 0 : long nBaseX = nX;
2469 0 : if( /*HasMirroredGraphics() &&*/ IsRTLEnabled() )
2470 : {
2471 : // --- RTL ---
2472 : // add some strange offset
2473 0 : nX += 2;
2474 : // revert the hack that will be done later in ImplDrawTextLine
2475 0 : nX = nBaseX - nWidth - (nX - nBaseX - 1);
2476 : }
2477 :
2478 0 : ImplDrawTextLine( nX, nY, 0, nWidth, STRIKEOUT_NONE, UNDERLINE_SINGLE, UNDERLINE_NONE, false );
2479 0 : }
2480 :
2481 0 : void OutputDevice::ImplGetEmphasisMark( PolyPolygon& rPolyPoly, bool& rPolyLine,
2482 : Rectangle& rRect1, Rectangle& rRect2,
2483 : long& rYOff, long& rWidth,
2484 : FontEmphasisMark eEmphasis,
2485 : long nHeight, short /*nOrient*/ )
2486 : {
2487 : static const sal_uInt8 aAccentPolyFlags[24] =
2488 : {
2489 : 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 0, 2, 0, 2, 2
2490 : };
2491 :
2492 : static const long aAccentPos[48] =
2493 : {
2494 : 78, 0,
2495 : 348, 79,
2496 : 599, 235,
2497 : 843, 469,
2498 : 938, 574,
2499 : 990, 669,
2500 : 990, 773,
2501 : 990, 843,
2502 : 964, 895,
2503 : 921, 947,
2504 : 886, 982,
2505 : 860, 999,
2506 : 825, 999,
2507 : 764, 999,
2508 : 721, 964,
2509 : 686, 895,
2510 : 625, 791,
2511 : 556, 660,
2512 : 469, 504,
2513 : 400, 400,
2514 : 261, 252,
2515 : 61, 61,
2516 : 0, 27,
2517 : 9, 0
2518 : };
2519 :
2520 0 : rWidth = 0;
2521 0 : rYOff = 0;
2522 0 : rPolyLine = false;
2523 :
2524 0 : if ( !nHeight )
2525 0 : return;
2526 :
2527 0 : FontEmphasisMark nEmphasisStyle = eEmphasis & EMPHASISMARK_STYLE;
2528 0 : long nDotSize = 0;
2529 0 : switch ( nEmphasisStyle )
2530 : {
2531 : case EMPHASISMARK_DOT:
2532 : // Dot has 55% of the height
2533 0 : nDotSize = (nHeight*550)/1000;
2534 0 : if ( !nDotSize )
2535 0 : nDotSize = 1;
2536 0 : if ( nDotSize <= 2 )
2537 0 : rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
2538 : else
2539 : {
2540 0 : long nRad = nDotSize/2;
2541 0 : Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
2542 0 : rPolyPoly.Insert( aPoly );
2543 : }
2544 0 : rYOff = ((nHeight*250)/1000)/2; // Center to the another EmphasisMarks
2545 0 : rWidth = nDotSize;
2546 0 : break;
2547 :
2548 : case EMPHASISMARK_CIRCLE:
2549 : // Dot has 80% of the height
2550 0 : nDotSize = (nHeight*800)/1000;
2551 0 : if ( !nDotSize )
2552 0 : nDotSize = 1;
2553 0 : if ( nDotSize <= 2 )
2554 0 : rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
2555 : else
2556 : {
2557 0 : long nRad = nDotSize/2;
2558 0 : Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
2559 0 : rPolyPoly.Insert( aPoly );
2560 : // BorderWidth is 15%
2561 0 : long nBorder = (nDotSize*150)/1000;
2562 0 : if ( nBorder <= 1 )
2563 0 : rPolyLine = true;
2564 : else
2565 : {
2566 : Polygon aPoly2( Point( nRad, nRad ),
2567 0 : nRad-nBorder, nRad-nBorder );
2568 0 : rPolyPoly.Insert( aPoly2 );
2569 0 : }
2570 : }
2571 0 : rWidth = nDotSize;
2572 0 : break;
2573 :
2574 : case EMPHASISMARK_DISC:
2575 : // Dot has 80% of the height
2576 0 : nDotSize = (nHeight*800)/1000;
2577 0 : if ( !nDotSize )
2578 0 : nDotSize = 1;
2579 0 : if ( nDotSize <= 2 )
2580 0 : rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
2581 : else
2582 : {
2583 0 : long nRad = nDotSize/2;
2584 0 : Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
2585 0 : rPolyPoly.Insert( aPoly );
2586 : }
2587 0 : rWidth = nDotSize;
2588 0 : break;
2589 :
2590 : case EMPHASISMARK_ACCENT:
2591 : // Dot has 80% of the height
2592 0 : nDotSize = (nHeight*800)/1000;
2593 0 : if ( !nDotSize )
2594 0 : nDotSize = 1;
2595 0 : if ( nDotSize <= 2 )
2596 : {
2597 0 : if ( nDotSize == 1 )
2598 : {
2599 0 : rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
2600 0 : rWidth = nDotSize;
2601 : }
2602 : else
2603 : {
2604 0 : rRect1 = Rectangle( Point(), Size( 1, 1 ) );
2605 0 : rRect2 = Rectangle( Point( 1, 1 ), Size( 1, 1 ) );
2606 : }
2607 : }
2608 : else
2609 : {
2610 0 : Polygon aPoly( sizeof( aAccentPos ) / sizeof( long ) / 2,
2611 : (const Point*)aAccentPos,
2612 0 : aAccentPolyFlags );
2613 0 : double dScale = ((double)nDotSize)/1000.0;
2614 0 : aPoly.Scale( dScale, dScale );
2615 0 : Polygon aTemp;
2616 0 : aPoly.AdaptiveSubdivide( aTemp );
2617 0 : Rectangle aBoundRect = aTemp.GetBoundRect();
2618 0 : rWidth = aBoundRect.GetWidth();
2619 0 : nDotSize = aBoundRect.GetHeight();
2620 0 : rPolyPoly.Insert( aTemp );
2621 : }
2622 0 : break;
2623 : }
2624 :
2625 : // calculate position
2626 0 : long nOffY = 1+(mnDPIY/300); // one visible pixel space
2627 0 : long nSpaceY = nHeight-nDotSize;
2628 0 : if ( nSpaceY >= nOffY*2 )
2629 0 : rYOff += nOffY;
2630 0 : if ( !(eEmphasis & EMPHASISMARK_POS_BELOW) )
2631 0 : rYOff += nDotSize;
2632 : }
2633 :
2634 0 : void OutputDevice::ImplDrawEmphasisMark( long nBaseX, long nX, long nY,
2635 : const PolyPolygon& rPolyPoly, bool bPolyLine,
2636 : const Rectangle& rRect1, const Rectangle& rRect2 )
2637 : {
2638 0 : if( IsRTLEnabled() )
2639 : // --- RTL --- mirror at basex
2640 0 : nX = nBaseX - (nX - nBaseX - 1);
2641 :
2642 0 : nX -= mnOutOffX;
2643 0 : nY -= mnOutOffY;
2644 :
2645 0 : if ( rPolyPoly.Count() )
2646 : {
2647 0 : if ( bPolyLine )
2648 : {
2649 0 : Polygon aPoly = rPolyPoly.GetObject( 0 );
2650 0 : aPoly.Move( nX, nY );
2651 0 : DrawPolyLine( aPoly );
2652 : }
2653 : else
2654 : {
2655 0 : PolyPolygon aPolyPoly = rPolyPoly;
2656 0 : aPolyPoly.Move( nX, nY );
2657 0 : DrawPolyPolygon( aPolyPoly );
2658 : }
2659 : }
2660 :
2661 0 : if ( !rRect1.IsEmpty() )
2662 : {
2663 0 : Rectangle aRect( Point( nX+rRect1.Left(),
2664 0 : nY+rRect1.Top() ), rRect1.GetSize() );
2665 0 : DrawRect( aRect );
2666 : }
2667 :
2668 0 : if ( !rRect2.IsEmpty() )
2669 : {
2670 0 : Rectangle aRect( Point( nX+rRect2.Left(),
2671 0 : nY+rRect2.Top() ), rRect2.GetSize() );
2672 :
2673 0 : DrawRect( aRect );
2674 : }
2675 0 : }
2676 :
2677 0 : void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout )
2678 : {
2679 0 : Color aOldLineColor = GetLineColor();
2680 0 : Color aOldFillColor = GetFillColor();
2681 0 : bool bOldMap = mbMap;
2682 0 : GDIMetaFile* pOldMetaFile = mpMetaFile;
2683 0 : mpMetaFile = NULL;
2684 0 : EnableMapMode( false );
2685 :
2686 0 : FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
2687 0 : PolyPolygon aPolyPoly;
2688 0 : Rectangle aRect1;
2689 0 : Rectangle aRect2;
2690 : long nEmphasisYOff;
2691 : long nEmphasisWidth;
2692 : long nEmphasisHeight;
2693 : bool bPolyLine;
2694 :
2695 0 : if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
2696 0 : nEmphasisHeight = mnEmphasisDescent;
2697 : else
2698 0 : nEmphasisHeight = mnEmphasisAscent;
2699 :
2700 : ImplGetEmphasisMark( aPolyPoly, bPolyLine,
2701 : aRect1, aRect2,
2702 : nEmphasisYOff, nEmphasisWidth,
2703 : nEmphasisMark,
2704 0 : nEmphasisHeight, mpFontEntry->mnOrientation );
2705 :
2706 0 : if ( bPolyLine )
2707 : {
2708 0 : SetLineColor( GetTextColor() );
2709 0 : SetFillColor();
2710 : }
2711 : else
2712 : {
2713 0 : SetLineColor();
2714 0 : SetFillColor( GetTextColor() );
2715 : }
2716 :
2717 0 : Point aOffset = Point(0,0);
2718 :
2719 0 : if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
2720 0 : aOffset.Y() += mpFontEntry->maMetric.mnDescent + nEmphasisYOff;
2721 : else
2722 0 : aOffset.Y() -= mpFontEntry->maMetric.mnAscent + nEmphasisYOff;
2723 :
2724 0 : long nEmphasisWidth2 = nEmphasisWidth / 2;
2725 0 : long nEmphasisHeight2 = nEmphasisHeight / 2;
2726 0 : aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );
2727 :
2728 0 : Point aOutPoint;
2729 0 : Rectangle aRectangle;
2730 0 : for( int nStart = 0;;)
2731 : {
2732 : sal_GlyphId aGlyphId;
2733 0 : if( !rSalLayout.GetNextGlyphs( 1, &aGlyphId, aOutPoint, nStart ) )
2734 0 : break;
2735 :
2736 0 : if( !mpGraphics->GetGlyphBoundRect( aGlyphId, aRectangle ) )
2737 0 : continue;
2738 :
2739 0 : if( !rSalLayout.IsSpacingGlyph( aGlyphId ) )
2740 : {
2741 0 : Point aAdjPoint = aOffset;
2742 0 : aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2;
2743 0 : if ( mpFontEntry->mnOrientation )
2744 0 : ImplRotatePos( 0, 0, aAdjPoint.X(), aAdjPoint.Y(), mpFontEntry->mnOrientation );
2745 0 : aOutPoint += aAdjPoint;
2746 0 : aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 );
2747 0 : ImplDrawEmphasisMark( rSalLayout.DrawBase().X(),
2748 0 : aOutPoint.X(), aOutPoint.Y(),
2749 0 : aPolyPoly, bPolyLine, aRect1, aRect2 );
2750 : }
2751 0 : }
2752 :
2753 0 : SetLineColor( aOldLineColor );
2754 0 : SetFillColor( aOldFillColor );
2755 0 : EnableMapMode( bOldMap );
2756 0 : mpMetaFile = pOldMetaFile;
2757 0 : }
2758 :
2759 0 : bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout )
2760 : {
2761 0 : int nX = rSalLayout.DrawBase().X();
2762 0 : int nY = rSalLayout.DrawBase().Y();
2763 :
2764 0 : Rectangle aBoundRect;
2765 0 : rSalLayout.DrawBase() = Point( 0, 0 );
2766 0 : rSalLayout.DrawOffset() = Point( 0, 0 );
2767 0 : if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) )
2768 : {
2769 : // guess vertical text extents if GetBoundRect failed
2770 0 : int nRight = rSalLayout.GetTextWidth();
2771 0 : int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
2772 0 : long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
2773 0 : aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop );
2774 : }
2775 :
2776 : // cache virtual device for rotation
2777 0 : if ( !mpOutDevData )
2778 0 : ImplInitOutDevData();
2779 0 : if ( !mpOutDevData->mpRotateDev )
2780 0 : mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 );
2781 0 : VirtualDevice* pVDev = mpOutDevData->mpRotateDev;
2782 :
2783 : // size it accordingly
2784 0 : if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) )
2785 0 : return false;
2786 :
2787 0 : Font aFont( GetFont() );
2788 0 : aFont.SetOrientation( 0 );
2789 0 : aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
2790 0 : pVDev->SetFont( aFont );
2791 0 : pVDev->SetTextColor( Color( COL_BLACK ) );
2792 0 : pVDev->SetTextFillColor();
2793 0 : pVDev->ImplNewFont();
2794 0 : pVDev->ImplInitFont();
2795 0 : pVDev->ImplInitTextColor();
2796 :
2797 : // draw text into upper left corner
2798 0 : rSalLayout.DrawBase() -= aBoundRect.TopLeft();
2799 0 : rSalLayout.DrawText( *((OutputDevice*)pVDev)->mpGraphics );
2800 :
2801 0 : Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() );
2802 0 : if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) )
2803 0 : return false;
2804 :
2805 : // calculate rotation offset
2806 0 : Polygon aPoly( aBoundRect );
2807 0 : aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation );
2808 0 : Point aPoint = aPoly.GetBoundRect().TopLeft();
2809 0 : aPoint += Point( nX, nY );
2810 :
2811 : // mask output with text colored bitmap
2812 0 : GDIMetaFile* pOldMetaFile = mpMetaFile;
2813 0 : long nOldOffX = mnOutOffX;
2814 0 : long nOldOffY = mnOutOffY;
2815 0 : bool bOldMap = mbMap;
2816 :
2817 0 : mnOutOffX = 0L;
2818 0 : mnOutOffY = 0L;
2819 0 : mpMetaFile = NULL;
2820 0 : EnableMapMode( false );
2821 :
2822 0 : DrawMask( aPoint, aBmp, GetTextColor() );
2823 :
2824 0 : EnableMapMode( bOldMap );
2825 0 : mnOutOffX = nOldOffX;
2826 0 : mnOutOffY = nOldOffY;
2827 0 : mpMetaFile = pOldMetaFile;
2828 :
2829 0 : return true;
2830 : }
2831 :
2832 0 : bool OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, bool bTextLines, sal_uInt32 flags )
2833 : {
2834 0 : if( mpFontEntry->mnOwnOrientation )
2835 0 : if( ImplDrawRotateText( rSalLayout ) )
2836 0 : return true;
2837 :
2838 0 : long nOldX = rSalLayout.DrawBase().X();
2839 0 : if( HasMirroredGraphics() )
2840 : {
2841 0 : long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
2842 0 : long x = rSalLayout.DrawBase().X();
2843 0 : rSalLayout.DrawBase().X() = w - 1 - x;
2844 0 : if( !IsRTLEnabled() )
2845 : {
2846 0 : OutputDevice *pOutDevRef = (OutputDevice *)this;
2847 : // mirror this window back
2848 0 : long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
2849 0 : rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ;
2850 : }
2851 : }
2852 0 : else if( IsRTLEnabled() )
2853 : {
2854 0 : OutputDevice *pOutDevRef = (OutputDevice *)this;
2855 :
2856 : // mirror this window back
2857 0 : long devX = pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
2858 0 : rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX;
2859 : }
2860 :
2861 0 : if(flags)
2862 : {
2863 0 : if( ! rSalLayout.DrawTextSpecial( *mpGraphics, flags ))
2864 : {
2865 0 : rSalLayout.DrawBase().X() = nOldX;
2866 0 : return false;
2867 : }
2868 : }
2869 : else
2870 : {
2871 0 : rSalLayout.DrawText( *mpGraphics );
2872 : }
2873 0 : rSalLayout.DrawBase().X() = nOldX;
2874 :
2875 0 : if( bTextLines )
2876 : ImplDrawTextLines( rSalLayout,
2877 : maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(),
2878 0 : maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) );
2879 :
2880 : // emphasis marks
2881 0 : if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
2882 0 : ImplDrawEmphasisMarks( rSalLayout );
2883 :
2884 0 : return true;
2885 : }
2886 :
2887 0 : void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout )
2888 : {
2889 0 : Color aOldColor = GetTextColor();
2890 0 : Color aOldTextLineColor = GetTextLineColor();
2891 0 : Color aOldOverlineColor = GetOverlineColor();
2892 0 : FontRelief eRelief = maFont.GetRelief();
2893 :
2894 0 : Point aOrigPos = rSalLayout.DrawBase();
2895 0 : if ( eRelief != RELIEF_NONE )
2896 : {
2897 0 : Color aReliefColor( COL_LIGHTGRAY );
2898 0 : Color aTextColor( aOldColor );
2899 :
2900 0 : Color aTextLineColor( aOldTextLineColor );
2901 0 : Color aOverlineColor( aOldOverlineColor );
2902 :
2903 : // we don't have a automatic color, so black is always drawn on white
2904 0 : if ( aTextColor.GetColor() == COL_BLACK )
2905 0 : aTextColor = Color( COL_WHITE );
2906 0 : if ( aTextLineColor.GetColor() == COL_BLACK )
2907 0 : aTextLineColor = Color( COL_WHITE );
2908 0 : if ( aOverlineColor.GetColor() == COL_BLACK )
2909 0 : aOverlineColor = Color( COL_WHITE );
2910 :
2911 : // relief-color is black for white text, in all other cases
2912 : // we set this to LightGray
2913 0 : if ( aTextColor.GetColor() == COL_WHITE )
2914 0 : aReliefColor = Color( COL_BLACK );
2915 0 : SetTextLineColor( aReliefColor );
2916 0 : SetOverlineColor( aReliefColor );
2917 0 : SetTextColor( aReliefColor );
2918 0 : ImplInitTextColor();
2919 :
2920 : // calculate offset - for high resolution printers the offset
2921 : // should be greater so that the effect is visible
2922 0 : long nOff = 1;
2923 0 : nOff += mnDPIX/300;
2924 :
2925 0 : if ( eRelief == RELIEF_ENGRAVED )
2926 0 : nOff = -nOff;
2927 0 : rSalLayout.DrawOffset() += Point( nOff, nOff);
2928 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2929 0 : rSalLayout.DrawOffset() -= Point( nOff, nOff);
2930 :
2931 0 : SetTextLineColor( aTextLineColor );
2932 0 : SetOverlineColor( aOverlineColor );
2933 0 : SetTextColor( aTextColor );
2934 0 : ImplInitTextColor();
2935 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2936 :
2937 0 : SetTextLineColor( aOldTextLineColor );
2938 0 : SetOverlineColor( aOldOverlineColor );
2939 :
2940 0 : if ( aTextColor != aOldColor )
2941 : {
2942 0 : SetTextColor( aOldColor );
2943 0 : ImplInitTextColor();
2944 : }
2945 : }
2946 : else
2947 : {
2948 0 : if ( maFont.IsShadow() )
2949 : {
2950 0 : long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24);
2951 0 : if ( maFont.IsOutline() )
2952 0 : nOff++;
2953 0 : SetTextLineColor();
2954 0 : SetOverlineColor();
2955 0 : if ( (GetTextColor().GetColor() == COL_BLACK)
2956 0 : || (GetTextColor().GetLuminance() < 8) )
2957 0 : SetTextColor( Color( COL_LIGHTGRAY ) );
2958 : else
2959 0 : SetTextColor( Color( COL_BLACK ) );
2960 0 : ImplInitTextColor();
2961 0 : rSalLayout.DrawBase() += Point( nOff, nOff );
2962 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2963 0 : rSalLayout.DrawBase() -= Point( nOff, nOff );
2964 0 : SetTextColor( aOldColor );
2965 0 : SetTextLineColor( aOldTextLineColor );
2966 0 : SetOverlineColor( aOldOverlineColor );
2967 0 : ImplInitTextColor();
2968 :
2969 0 : if ( !maFont.IsOutline() )
2970 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2971 : }
2972 :
2973 0 : if ( maFont.IsOutline() )
2974 : {
2975 0 : if(! ImplDrawTextDirect( rSalLayout, mbTextLines, DRAWTEXT_F_OUTLINE))
2976 : {
2977 0 : rSalLayout.DrawBase() = aOrigPos + Point(-1,-1);
2978 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2979 0 : rSalLayout.DrawBase() = aOrigPos + Point(+1,+1);
2980 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2981 0 : rSalLayout.DrawBase() = aOrigPos + Point(-1,+0);
2982 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2983 0 : rSalLayout.DrawBase() = aOrigPos + Point(-1,+1);
2984 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2985 0 : rSalLayout.DrawBase() = aOrigPos + Point(+0,+1);
2986 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2987 0 : rSalLayout.DrawBase() = aOrigPos + Point(+0,-1);
2988 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2989 0 : rSalLayout.DrawBase() = aOrigPos + Point(+1,-1);
2990 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2991 0 : rSalLayout.DrawBase() = aOrigPos + Point(+1,+0);
2992 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
2993 0 : rSalLayout.DrawBase() = aOrigPos;
2994 :
2995 0 : SetTextColor( Color( COL_WHITE ) );
2996 0 : SetTextLineColor( Color( COL_WHITE ) );
2997 0 : SetOverlineColor( Color( COL_WHITE ) );
2998 0 : ImplInitTextColor();
2999 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
3000 0 : SetTextColor( aOldColor );
3001 0 : SetTextLineColor( aOldTextLineColor );
3002 0 : SetOverlineColor( aOldOverlineColor );
3003 0 : ImplInitTextColor();
3004 : }
3005 : }
3006 : }
3007 0 : }
3008 :
3009 0 : void OutputDevice::ImplDrawText( SalLayout& rSalLayout )
3010 : {
3011 0 : if( mbInitClipRegion )
3012 0 : ImplInitClipRegion();
3013 0 : if( mbOutputClipped )
3014 0 : return;
3015 0 : if( mbInitTextColor )
3016 0 : ImplInitTextColor();
3017 :
3018 0 : rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY );
3019 :
3020 0 : if( IsTextFillColor() )
3021 0 : ImplDrawTextBackground( rSalLayout );
3022 :
3023 0 : if( mbTextSpecial )
3024 0 : ImplDrawSpecialText( rSalLayout );
3025 : else
3026 0 : ImplDrawTextDirect( rSalLayout, mbTextLines );
3027 : }
3028 :
3029 0 : long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo,
3030 : long nWidth, const OUString& rStr,
3031 : sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout )
3032 : {
3033 : DBG_ASSERTWARNING( nWidth >= 0, "ImplGetTextLines: nWidth <= 0!" );
3034 :
3035 0 : if ( nWidth <= 0 )
3036 0 : nWidth = 1;
3037 :
3038 0 : long nMaxLineWidth = 0;
3039 0 : rLineInfo.Clear();
3040 0 : if ( !rStr.isEmpty() && (nWidth > 0) )
3041 : {
3042 0 : uno::Reference < i18n::XBreakIterator > xBI;
3043 : // get service provider
3044 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
3045 :
3046 0 : bool bHyphenate = (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION)
3047 0 : == TEXT_DRAW_WORDBREAK_HYPHENATION;
3048 0 : uno::Reference< linguistic2::XHyphenator > xHyph;
3049 0 : if ( bHyphenate )
3050 : {
3051 0 : uno::Reference< linguistic2::XLinguServiceManager2> xLinguMgr = linguistic2::LinguServiceManager::create(xContext);
3052 0 : xHyph = xLinguMgr->getHyphenator();
3053 : }
3054 :
3055 0 : sal_Int32 nPos = 0;
3056 0 : sal_Int32 nLen = rStr.getLength();
3057 0 : while ( nPos < nLen )
3058 : {
3059 0 : sal_Int32 nBreakPos = nPos;
3060 :
3061 0 : while ( ( nBreakPos < nLen ) && ( rStr[ nBreakPos ] != '\r' ) && ( rStr[ nBreakPos ] != '\n' ) )
3062 0 : nBreakPos++;
3063 :
3064 0 : long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
3065 0 : if ( ( nLineWidth > nWidth ) && ( nStyle & TEXT_DRAW_WORDBREAK ) )
3066 : {
3067 0 : if ( !xBI.is() )
3068 0 : xBI = vcl::unohelper::CreateBreakIterator();
3069 :
3070 0 : if ( xBI.is() )
3071 : {
3072 0 : const com::sun::star::lang::Locale& rDefLocale(Application::GetSettings().GetUILanguageTag().getLocale());
3073 0 : sal_Int32 nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos );
3074 : DBG_ASSERT( nSoftBreak < nBreakPos, "Break?!" );
3075 0 : i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, uno::Sequence <beans::PropertyValue>(), 1 );
3076 0 : i18n::LineBreakUserOptions aUserOptions;
3077 0 : i18n::LineBreakResults aLBR = xBI->getLineBreak( rStr, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions );
3078 0 : nBreakPos = aLBR.breakIndex;
3079 0 : if ( nBreakPos <= nPos )
3080 0 : nBreakPos = nSoftBreak;
3081 0 : if ( bHyphenate )
3082 : {
3083 : // Whether hyphen or not: Put the word after the hyphen through
3084 : // word boundary.
3085 :
3086 : // nMaxBreakPos the last char that fits into the line
3087 : // nBreakPos is the word's start
3088 :
3089 : // We run into a problem if the doc is so narrow, that a word
3090 : // is broken into more than two lines ...
3091 0 : if ( xHyph.is() )
3092 : {
3093 0 : sal_Unicode cAlternateReplChar = 0;
3094 0 : i18n::Boundary aBoundary = xBI->getWordBoundary( rStr, nBreakPos, rDefLocale, ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True );
3095 0 : sal_Int32 nWordStart = nPos;
3096 0 : sal_Int32 nWordEnd = aBoundary.endPos;
3097 : DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
3098 :
3099 0 : sal_Int32 nWordLen = nWordEnd - nWordStart;
3100 0 : if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) )
3101 : {
3102 : // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
3103 : // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" );
3104 0 : OUString aWord = rStr.copy( nWordStart, nWordLen );
3105 0 : sal_Int32 nMinTrail = nWordEnd-nSoftBreak+1; //+1: Before the "broken off" char
3106 0 : uno::Reference< linguistic2::XHyphenatedWord > xHyphWord;
3107 0 : if (xHyph.is())
3108 0 : xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.getLength() - nMinTrail, uno::Sequence< beans::PropertyValue >() );
3109 0 : if (xHyphWord.is())
3110 : {
3111 0 : bool bAlternate = xHyphWord->isAlternativeSpelling();
3112 0 : sal_Int32 _nWordLen = 1 + xHyphWord->getHyphenPos();
3113 :
3114 0 : if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= ( 2 ) ) )
3115 : {
3116 0 : if ( !bAlternate )
3117 : {
3118 0 : nBreakPos = nWordStart + _nWordLen;
3119 : }
3120 : else
3121 : {
3122 0 : OUString aAlt( xHyphWord->getHyphenatedWord() );
3123 :
3124 : // We can have two cases:
3125 : // 1) "packen" turns into "pak-ken"
3126 : // 2) "Schiffahrt" turns into "Schiff-fahrt"
3127 :
3128 : // In case 1 we need to replace a char
3129 : // In case 2 we add a char
3130 :
3131 : // Correct recognition is made harder by words such as
3132 : // "Schiffahrtsbrennesseln", as the Hyphenator splits all
3133 : // positions of the word and comes up with "Schifffahrtsbrennnesseln"
3134 : // Thus, we cannot infer the aWord from the AlternativWord's
3135 : // index.
3136 : // TODO: The whole junk will be made easier by a function in
3137 : // the Hyphenator, as soon as AMA adds it.
3138 0 : sal_Int32 nAltStart = _nWordLen - 1;
3139 0 : sal_Int32 nTxtStart = nAltStart - (aAlt.getLength() - aWord.getLength());
3140 0 : sal_Int32 nTxtEnd = nTxtStart;
3141 0 : sal_Int32 nAltEnd = nAltStart;
3142 :
3143 : // The area between nStart and nEnd is the difference
3144 : // between AlternativString and OriginalString
3145 0 : while( nTxtEnd < aWord.getLength() && nAltEnd < aAlt.getLength() &&
3146 0 : aWord[nTxtEnd] != aAlt[nAltEnd] )
3147 : {
3148 0 : ++nTxtEnd;
3149 0 : ++nAltEnd;
3150 : }
3151 :
3152 : // If a char was added, we notice it now:
3153 0 : if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
3154 0 : aWord[ nTxtEnd ] == aAlt[nAltEnd] )
3155 : {
3156 0 : ++nAltEnd;
3157 0 : ++nTxtStart;
3158 0 : ++nTxtEnd;
3159 : }
3160 :
3161 : DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Wrong assumption!" );
3162 :
3163 0 : if ( nTxtEnd > nTxtStart )
3164 0 : cAlternateReplChar = aAlt[ nAltStart ];
3165 :
3166 0 : nBreakPos = nWordStart + nTxtStart;
3167 0 : if ( cAlternateReplChar )
3168 0 : nBreakPos++;
3169 : }
3170 : }
3171 0 : }
3172 : }
3173 : }
3174 : }
3175 0 : nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
3176 : }
3177 : else
3178 : {
3179 : // fallback to something really simple
3180 0 : sal_Int32 nSpacePos = rStr.getLength();
3181 0 : long nW = 0;
3182 0 : do
3183 : {
3184 0 : nSpacePos = rStr.lastIndexOf( ' ', nSpacePos );
3185 0 : if( nSpacePos != -1 )
3186 : {
3187 0 : if( nSpacePos > nPos )
3188 0 : nSpacePos--;
3189 0 : nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos );
3190 : }
3191 : } while( nW > nWidth );
3192 :
3193 0 : if( nSpacePos != -1 )
3194 : {
3195 0 : nBreakPos = nSpacePos;
3196 0 : nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
3197 0 : if( nBreakPos < rStr.getLength()-1 )
3198 0 : nBreakPos++;
3199 : }
3200 : }
3201 : }
3202 :
3203 0 : if ( nLineWidth > nMaxLineWidth )
3204 0 : nMaxLineWidth = nLineWidth;
3205 :
3206 0 : rLineInfo.AddLine( new ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) );
3207 :
3208 0 : if ( nBreakPos == nPos )
3209 0 : nBreakPos++;
3210 0 : nPos = nBreakPos;
3211 :
3212 0 : if ( nPos < nLen && ( ( rStr[ nPos ] == '\r' ) || ( rStr[ nPos ] == '\n' ) ) )
3213 : {
3214 0 : nPos++;
3215 : // CR/LF?
3216 0 : if ( ( nPos < nLen ) && ( rStr[ nPos ] == '\n' ) && ( rStr[ nPos-1 ] == '\r' ) )
3217 0 : nPos++;
3218 : }
3219 0 : }
3220 : }
3221 : #ifdef DBG_UTIL
3222 : for ( sal_uInt16 nL = 0; nL < rLineInfo.Count(); nL++ )
3223 : {
3224 : ImplTextLineInfo* pLine = rLineInfo.GetLine( nL );
3225 : OUString aLine = rStr.copy( pLine->GetIndex(), pLine->GetLen() );
3226 : DBG_ASSERT( aLine.indexOf( '\r' ) == -1, "ImplGetTextLines - Found CR!" );
3227 : DBG_ASSERT( aLine.indexOf( '\n' ) == -1, "ImplGetTextLines - Found LF!" );
3228 : }
3229 : #endif
3230 :
3231 0 : return nMaxLineWidth;
3232 : }
3233 :
3234 0 : void OutputDevice::SetAntialiasing( sal_uInt16 nMode )
3235 : {
3236 0 : if ( mnAntialiasing != nMode )
3237 : {
3238 0 : mnAntialiasing = nMode;
3239 0 : mbInitFont = true;
3240 :
3241 0 : if(mpGraphics)
3242 : {
3243 0 : mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW);
3244 : }
3245 : }
3246 :
3247 0 : if( mpAlphaVDev )
3248 0 : mpAlphaVDev->SetAntialiasing( nMode );
3249 0 : }
3250 :
3251 0 : void OutputDevice::SetFont( const Font& rNewFont )
3252 : {
3253 :
3254 0 : Font aFont( rNewFont );
3255 0 : aFont.SetLanguage(rNewFont.GetLanguage());
3256 0 : if ( mnDrawMode & (DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | DRAWMODE_SETTINGSTEXT |
3257 : DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | DRAWMODE_GRAYFILL | DRAWMODE_NOFILL |
3258 : DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) )
3259 : {
3260 0 : Color aTextColor( aFont.GetColor() );
3261 :
3262 0 : if ( mnDrawMode & DRAWMODE_BLACKTEXT )
3263 0 : aTextColor = Color( COL_BLACK );
3264 0 : else if ( mnDrawMode & DRAWMODE_WHITETEXT )
3265 0 : aTextColor = Color( COL_WHITE );
3266 0 : else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
3267 : {
3268 0 : const sal_uInt8 cLum = aTextColor.GetLuminance();
3269 0 : aTextColor = Color( cLum, cLum, cLum );
3270 : }
3271 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
3272 0 : aTextColor = GetSettings().GetStyleSettings().GetFontColor();
3273 :
3274 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT )
3275 : {
3276 0 : aTextColor = Color( (aTextColor.GetRed() >> 1 ) | 0x80,
3277 0 : (aTextColor.GetGreen() >> 1 ) | 0x80,
3278 0 : (aTextColor.GetBlue() >> 1 ) | 0x80 );
3279 : }
3280 :
3281 0 : aFont.SetColor( aTextColor );
3282 :
3283 0 : bool bTransFill = aFont.IsTransparent();
3284 0 : if ( !bTransFill )
3285 : {
3286 0 : Color aTextFillColor( aFont.GetFillColor() );
3287 :
3288 0 : if ( mnDrawMode & DRAWMODE_BLACKFILL )
3289 0 : aTextFillColor = Color( COL_BLACK );
3290 0 : else if ( mnDrawMode & DRAWMODE_WHITEFILL )
3291 0 : aTextFillColor = Color( COL_WHITE );
3292 0 : else if ( mnDrawMode & DRAWMODE_GRAYFILL )
3293 : {
3294 0 : const sal_uInt8 cLum = aTextFillColor.GetLuminance();
3295 0 : aTextFillColor = Color( cLum, cLum, cLum );
3296 : }
3297 0 : else if( mnDrawMode & DRAWMODE_SETTINGSFILL )
3298 0 : aTextFillColor = GetSettings().GetStyleSettings().GetWindowColor();
3299 0 : else if ( mnDrawMode & DRAWMODE_NOFILL )
3300 : {
3301 0 : aTextFillColor = Color( COL_TRANSPARENT );
3302 0 : bTransFill = true;
3303 : }
3304 :
3305 0 : if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) )
3306 : {
3307 0 : aTextFillColor = Color( (aTextFillColor.GetRed() >> 1) | 0x80,
3308 0 : (aTextFillColor.GetGreen() >> 1) | 0x80,
3309 0 : (aTextFillColor.GetBlue() >> 1) | 0x80 );
3310 : }
3311 :
3312 0 : aFont.SetFillColor( aTextFillColor );
3313 : }
3314 : }
3315 :
3316 0 : if ( mpMetaFile )
3317 : {
3318 0 : mpMetaFile->AddAction( new MetaFontAction( aFont ) );
3319 : // the color and alignment actions don't belong here
3320 : // TODO: get rid of them without breaking anything...
3321 0 : mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlign() ) );
3322 0 : mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) );
3323 : }
3324 :
3325 0 : if ( !maFont.IsSameInstance( aFont ) )
3326 : {
3327 : // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color,
3328 : // because SetTextColor() is used for this.
3329 : // #i28759# maTextColor might have been changed behind our back, commit then, too.
3330 0 : if( aFont.GetColor() != COL_TRANSPARENT
3331 0 : && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) )
3332 : {
3333 0 : maTextColor = aFont.GetColor();
3334 0 : mbInitTextColor = true;
3335 0 : if( mpMetaFile )
3336 0 : mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) );
3337 : }
3338 0 : maFont = aFont;
3339 0 : mbNewFont = true;
3340 :
3341 0 : if( mpAlphaVDev )
3342 : {
3343 : // #i30463#
3344 : // Since SetFont might change the text color, apply that only
3345 : // selectively to alpha vdev (which normally paints opaque text
3346 : // with COL_BLACK)
3347 0 : if( aFont.GetColor() != COL_TRANSPARENT )
3348 : {
3349 0 : mpAlphaVDev->SetTextColor( COL_BLACK );
3350 0 : aFont.SetColor( COL_TRANSPARENT );
3351 : }
3352 :
3353 0 : mpAlphaVDev->SetFont( aFont );
3354 : }
3355 0 : }
3356 0 : }
3357 :
3358 0 : void OutputDevice::SetLayoutMode( sal_uLong nTextLayoutMode )
3359 : {
3360 0 : if( mpMetaFile )
3361 0 : mpMetaFile->AddAction( new MetaLayoutModeAction( nTextLayoutMode ) );
3362 :
3363 0 : mnTextLayoutMode = nTextLayoutMode;
3364 :
3365 0 : if( mpAlphaVDev )
3366 0 : mpAlphaVDev->SetLayoutMode( nTextLayoutMode );
3367 0 : }
3368 :
3369 0 : void OutputDevice::SetDigitLanguage( LanguageType eTextLanguage )
3370 : {
3371 0 : if( mpMetaFile )
3372 0 : mpMetaFile->AddAction( new MetaTextLanguageAction( eTextLanguage ) );
3373 :
3374 0 : meTextLanguage = eTextLanguage;
3375 :
3376 0 : if( mpAlphaVDev )
3377 0 : mpAlphaVDev->SetDigitLanguage( eTextLanguage );
3378 0 : }
3379 :
3380 0 : void OutputDevice::SetTextColor( const Color& rColor )
3381 : {
3382 :
3383 0 : Color aColor( rColor );
3384 :
3385 0 : if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
3386 : DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
3387 : DRAWMODE_SETTINGSTEXT ) )
3388 : {
3389 0 : if ( mnDrawMode & DRAWMODE_BLACKTEXT )
3390 0 : aColor = Color( COL_BLACK );
3391 0 : else if ( mnDrawMode & DRAWMODE_WHITETEXT )
3392 0 : aColor = Color( COL_WHITE );
3393 0 : else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
3394 : {
3395 0 : const sal_uInt8 cLum = aColor.GetLuminance();
3396 0 : aColor = Color( cLum, cLum, cLum );
3397 : }
3398 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
3399 0 : aColor = GetSettings().GetStyleSettings().GetFontColor();
3400 :
3401 0 : if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT )
3402 : {
3403 0 : aColor = Color( (aColor.GetRed() >> 1) | 0x80,
3404 0 : (aColor.GetGreen() >> 1) | 0x80,
3405 0 : (aColor.GetBlue() >> 1) | 0x80 );
3406 : }
3407 : }
3408 :
3409 0 : if ( mpMetaFile )
3410 0 : mpMetaFile->AddAction( new MetaTextColorAction( aColor ) );
3411 :
3412 0 : if ( maTextColor != aColor )
3413 : {
3414 0 : maTextColor = aColor;
3415 0 : mbInitTextColor = true;
3416 : }
3417 :
3418 0 : if( mpAlphaVDev )
3419 0 : mpAlphaVDev->SetTextColor( COL_BLACK );
3420 0 : }
3421 :
3422 0 : void OutputDevice::SetTextFillColor()
3423 : {
3424 :
3425 0 : if ( mpMetaFile )
3426 0 : mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), false ) );
3427 :
3428 0 : if ( maFont.GetColor() != Color( COL_TRANSPARENT ) )
3429 0 : maFont.SetFillColor( Color( COL_TRANSPARENT ) );
3430 0 : if ( !maFont.IsTransparent() )
3431 0 : maFont.SetTransparent( true );
3432 :
3433 0 : if( mpAlphaVDev )
3434 0 : mpAlphaVDev->SetTextFillColor();
3435 0 : }
3436 :
3437 0 : void OutputDevice::SetTextFillColor( const Color& rColor )
3438 : {
3439 :
3440 0 : Color aColor( rColor );
3441 0 : bool bTransFill = ImplIsColorTransparent( aColor ) ? sal_True : sal_False;
3442 :
3443 0 : if ( !bTransFill )
3444 : {
3445 0 : if ( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL |
3446 : DRAWMODE_GRAYFILL | DRAWMODE_NOFILL |
3447 : DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) )
3448 : {
3449 0 : if ( mnDrawMode & DRAWMODE_BLACKFILL )
3450 0 : aColor = Color( COL_BLACK );
3451 0 : else if ( mnDrawMode & DRAWMODE_WHITEFILL )
3452 0 : aColor = Color( COL_WHITE );
3453 0 : else if ( mnDrawMode & DRAWMODE_GRAYFILL )
3454 : {
3455 0 : const sal_uInt8 cLum = aColor.GetLuminance();
3456 0 : aColor = Color( cLum, cLum, cLum );
3457 : }
3458 0 : else if( mnDrawMode & DRAWMODE_SETTINGSFILL )
3459 0 : aColor = GetSettings().GetStyleSettings().GetWindowColor();
3460 0 : else if ( mnDrawMode & DRAWMODE_NOFILL )
3461 : {
3462 0 : aColor = Color( COL_TRANSPARENT );
3463 0 : bTransFill = true;
3464 : }
3465 :
3466 0 : if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) )
3467 : {
3468 0 : aColor = Color( (aColor.GetRed() >> 1) | 0x80,
3469 0 : (aColor.GetGreen() >> 1) | 0x80,
3470 0 : (aColor.GetBlue() >> 1) | 0x80 );
3471 : }
3472 : }
3473 : }
3474 :
3475 0 : if ( mpMetaFile )
3476 0 : mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, true ) );
3477 :
3478 0 : if ( maFont.GetFillColor() != aColor )
3479 0 : maFont.SetFillColor( aColor );
3480 0 : if ( maFont.IsTransparent() != bTransFill )
3481 0 : maFont.SetTransparent( bTransFill );
3482 :
3483 0 : if( mpAlphaVDev )
3484 0 : mpAlphaVDev->SetTextFillColor( COL_BLACK );
3485 0 : }
3486 :
3487 0 : Color OutputDevice::GetTextFillColor() const
3488 : {
3489 0 : if ( maFont.IsTransparent() )
3490 0 : return Color( COL_TRANSPARENT );
3491 : else
3492 0 : return maFont.GetFillColor();
3493 : }
3494 :
3495 0 : void OutputDevice::SetTextLineColor()
3496 : {
3497 :
3498 0 : if ( mpMetaFile )
3499 0 : mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), false ) );
3500 :
3501 0 : maTextLineColor = Color( COL_TRANSPARENT );
3502 :
3503 0 : if( mpAlphaVDev )
3504 0 : mpAlphaVDev->SetTextLineColor();
3505 0 : }
3506 :
3507 0 : void OutputDevice::SetTextLineColor( const Color& rColor )
3508 : {
3509 :
3510 0 : Color aColor( rColor );
3511 :
3512 0 : if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
3513 : DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
3514 : DRAWMODE_SETTINGSTEXT ) )
3515 : {
3516 0 : if ( mnDrawMode & DRAWMODE_BLACKTEXT )
3517 0 : aColor = Color( COL_BLACK );
3518 0 : else if ( mnDrawMode & DRAWMODE_WHITETEXT )
3519 0 : aColor = Color( COL_WHITE );
3520 0 : else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
3521 : {
3522 0 : const sal_uInt8 cLum = aColor.GetLuminance();
3523 0 : aColor = Color( cLum, cLum, cLum );
3524 : }
3525 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
3526 0 : aColor = GetSettings().GetStyleSettings().GetFontColor();
3527 :
3528 0 : if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT)
3529 0 : && (aColor.GetColor() != COL_TRANSPARENT) )
3530 : {
3531 0 : aColor = Color( (aColor.GetRed() >> 1) | 0x80,
3532 0 : (aColor.GetGreen() >> 1) | 0x80,
3533 0 : (aColor.GetBlue() >> 1) | 0x80 );
3534 : }
3535 : }
3536 :
3537 0 : if ( mpMetaFile )
3538 0 : mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, true ) );
3539 :
3540 0 : maTextLineColor = aColor;
3541 :
3542 0 : if( mpAlphaVDev )
3543 0 : mpAlphaVDev->SetTextLineColor( COL_BLACK );
3544 0 : }
3545 :
3546 0 : void OutputDevice::SetOverlineColor()
3547 : {
3548 :
3549 0 : if ( mpMetaFile )
3550 0 : mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), false ) );
3551 :
3552 0 : maOverlineColor = Color( COL_TRANSPARENT );
3553 :
3554 0 : if( mpAlphaVDev )
3555 0 : mpAlphaVDev->SetOverlineColor();
3556 0 : }
3557 :
3558 0 : void OutputDevice::SetOverlineColor( const Color& rColor )
3559 : {
3560 :
3561 0 : Color aColor( rColor );
3562 :
3563 0 : if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
3564 : DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
3565 : DRAWMODE_SETTINGSTEXT ) )
3566 : {
3567 0 : if ( mnDrawMode & DRAWMODE_BLACKTEXT )
3568 0 : aColor = Color( COL_BLACK );
3569 0 : else if ( mnDrawMode & DRAWMODE_WHITETEXT )
3570 0 : aColor = Color( COL_WHITE );
3571 0 : else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
3572 : {
3573 0 : const sal_uInt8 cLum = aColor.GetLuminance();
3574 0 : aColor = Color( cLum, cLum, cLum );
3575 : }
3576 0 : else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
3577 0 : aColor = GetSettings().GetStyleSettings().GetFontColor();
3578 :
3579 0 : if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT)
3580 0 : && (aColor.GetColor() != COL_TRANSPARENT) )
3581 : {
3582 0 : aColor = Color( (aColor.GetRed() >> 1) | 0x80,
3583 0 : (aColor.GetGreen() >> 1) | 0x80,
3584 0 : (aColor.GetBlue() >> 1) | 0x80 );
3585 : }
3586 : }
3587 :
3588 0 : if ( mpMetaFile )
3589 0 : mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, true ) );
3590 :
3591 0 : maOverlineColor = aColor;
3592 :
3593 0 : if( mpAlphaVDev )
3594 0 : mpAlphaVDev->SetOverlineColor( COL_BLACK );
3595 0 : }
3596 :
3597 0 : void OutputDevice::SetTextAlign( TextAlign eAlign )
3598 : {
3599 :
3600 0 : if ( mpMetaFile )
3601 0 : mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) );
3602 :
3603 0 : if ( maFont.GetAlign() != eAlign )
3604 : {
3605 0 : maFont.SetAlign( eAlign );
3606 0 : mbNewFont = true;
3607 : }
3608 :
3609 0 : if( mpAlphaVDev )
3610 0 : mpAlphaVDev->SetTextAlign( eAlign );
3611 0 : }
3612 :
3613 0 : void OutputDevice::DrawTextLine( const Point& rPos, long nWidth,
3614 : FontStrikeout eStrikeout,
3615 : FontUnderline eUnderline,
3616 : FontUnderline eOverline,
3617 : bool bUnderlineAbove )
3618 : {
3619 :
3620 0 : if ( mpMetaFile )
3621 0 : mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) );
3622 :
3623 0 : if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) &&
3624 0 : ((eOverline == UNDERLINE_NONE) || (eOverline == UNDERLINE_DONTKNOW)) &&
3625 0 : ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
3626 0 : return;
3627 :
3628 0 : if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
3629 0 : return;
3630 :
3631 : // we need a graphics
3632 0 : if( !mpGraphics && !ImplGetGraphics() )
3633 0 : return;
3634 0 : if( mbInitClipRegion )
3635 0 : ImplInitClipRegion();
3636 0 : if( mbOutputClipped )
3637 0 : return;
3638 :
3639 : // initialize font if needed to get text offsets
3640 : // TODO: only needed for mnTextOff!=(0,0)
3641 0 : if( mbNewFont )
3642 0 : if( !ImplNewFont() )
3643 0 : return;
3644 0 : if( mbInitFont )
3645 0 : ImplInitFont();
3646 :
3647 0 : Point aPos = ImplLogicToDevicePixel( rPos );
3648 0 : nWidth = ImplLogicWidthToDevicePixel( nWidth );
3649 0 : aPos += Point( mnTextOffX, mnTextOffY );
3650 0 : ImplDrawTextLine( aPos.X(), aPos.X(), 0, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
3651 :
3652 0 : if( mpAlphaVDev )
3653 0 : mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
3654 : }
3655 :
3656 0 : void OutputDevice::DrawWaveLine( const Point& rStartPos, const Point& rEndPos )
3657 : {
3658 :
3659 0 : if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
3660 0 : return;
3661 :
3662 : // we need a graphics
3663 0 : if( !mpGraphics )
3664 0 : if( !ImplGetGraphics() )
3665 0 : return;
3666 :
3667 0 : if ( mbInitClipRegion )
3668 0 : ImplInitClipRegion();
3669 0 : if ( mbOutputClipped )
3670 0 : return;
3671 :
3672 0 : if( mbNewFont )
3673 0 : if( !ImplNewFont() )
3674 0 : return;
3675 :
3676 0 : Point aStartPt = ImplLogicToDevicePixel( rStartPos );
3677 0 : Point aEndPt = ImplLogicToDevicePixel( rEndPos );
3678 0 : long nStartX = aStartPt.X();
3679 0 : long nStartY = aStartPt.Y();
3680 0 : long nEndX = aEndPt.X();
3681 0 : long nEndY = aEndPt.Y();
3682 0 : short nOrientation = 0;
3683 :
3684 : // when rotated
3685 0 : if ( (nStartY != nEndY) || (nStartX > nEndX) )
3686 : {
3687 0 : long nDX = nEndX - nStartX;
3688 0 : double nO = atan2( -nEndY + nStartY, ((nDX == 0L) ? 0.000000001 : nDX) );
3689 0 : nO /= F_PI1800;
3690 0 : nOrientation = (short)nO;
3691 0 : ImplRotatePos( nStartX, nStartY, nEndX, nEndY, -nOrientation );
3692 : }
3693 :
3694 : long nWaveHeight;
3695 :
3696 0 : nWaveHeight = 3;
3697 0 : nStartY++;
3698 0 : nEndY++;
3699 :
3700 0 : if (mnDPIScaleFactor > 1)
3701 : {
3702 0 : nWaveHeight *= mnDPIScaleFactor;
3703 :
3704 0 : nStartY += mnDPIScaleFactor - 1; // Shift down additional pixel(s) to create more visual separation.
3705 :
3706 : // odd heights look better than even
3707 0 : if (mnDPIScaleFactor % 2 == 0)
3708 : {
3709 0 : nWaveHeight--;
3710 : }
3711 : }
3712 :
3713 : // #109280# make sure the waveline does not exceed the descent to avoid paint problems
3714 0 : ImplFontEntry* pFontEntry = mpFontEntry;
3715 0 : if( nWaveHeight > pFontEntry->maMetric.mnWUnderlineSize )
3716 0 : nWaveHeight = pFontEntry->maMetric.mnWUnderlineSize;
3717 :
3718 : ImplDrawWaveLine(nStartX, nStartY, 0, 0,
3719 : nEndX-nStartX, nWaveHeight,
3720 0 : mnDPIScaleFactor, nOrientation, GetLineColor());
3721 :
3722 0 : if( mpAlphaVDev )
3723 0 : mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos );
3724 : }
3725 :
3726 0 : void OutputDevice::DrawText( const Point& rStartPt, const OUString& rStr,
3727 : sal_Int32 nIndex, sal_Int32 nLen,
3728 : MetricVector* pVector, OUString* pDisplayText
3729 : )
3730 : {
3731 0 : if(nLen == 0x0FFFF)
3732 : {
3733 : SAL_INFO("sal.rtl.xub",
3734 : "GetTextOutlines Suspicious arguments nLen:" << nLen);
3735 : }
3736 0 : if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
3737 : {
3738 0 : nLen = rStr.getLength() - nIndex;
3739 : }
3740 :
3741 0 : if( mpOutDevData && mpOutDevData->mpRecordLayout )
3742 : {
3743 0 : pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
3744 0 : pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
3745 : }
3746 :
3747 : #if OSL_DEBUG_LEVEL > 2
3748 : fprintf( stderr, " OutputDevice::DrawText(\"%s\")\n",
3749 : OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ).getStr() );
3750 : #endif
3751 :
3752 0 : if ( mpMetaFile )
3753 0 : mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
3754 0 : if( pVector )
3755 : {
3756 0 : Region aClip( GetClipRegion() );
3757 0 : if( meOutDevType == OUTDEV_WINDOW )
3758 0 : aClip.Intersect( Rectangle( Point(), GetOutputSize() ) );
3759 0 : if( mpOutDevData && mpOutDevData->mpRecordLayout )
3760 : {
3761 0 : mpOutDevData->mpRecordLayout->m_aLineIndices.push_back( mpOutDevData->mpRecordLayout->m_aDisplayText.getLength() );
3762 0 : aClip.Intersect( mpOutDevData->maRecordRect );
3763 : }
3764 0 : if( ! aClip.IsNull() )
3765 : {
3766 0 : MetricVector aTmp;
3767 0 : GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp );
3768 :
3769 0 : bool bInserted = false;
3770 0 : for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ )
3771 : {
3772 0 : bool bAppend = false;
3773 :
3774 0 : if( aClip.IsOver( *it ) )
3775 0 : bAppend = true;
3776 0 : else if( rStr[ nIndex ] == ' ' && bInserted )
3777 : {
3778 0 : MetricVector::const_iterator next = it;
3779 0 : ++next;
3780 0 : if( next != aTmp.end() && aClip.IsOver( *next ) )
3781 0 : bAppend = true;
3782 : }
3783 :
3784 0 : if( bAppend )
3785 : {
3786 0 : pVector->push_back( *it );
3787 0 : if( pDisplayText )
3788 0 : *pDisplayText += OUString(rStr[ nIndex ]);
3789 0 : bInserted = true;
3790 : }
3791 0 : }
3792 : }
3793 : else
3794 : {
3795 0 : GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector );
3796 0 : if( pDisplayText )
3797 0 : *pDisplayText += rStr.copy( nIndex, nLen );
3798 0 : }
3799 : }
3800 :
3801 0 : if ( !IsDeviceOutputNecessary() || pVector )
3802 0 : return;
3803 :
3804 0 : SalLayout* pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, NULL);
3805 0 : if( pSalLayout )
3806 : {
3807 0 : ImplDrawText( *pSalLayout );
3808 0 : pSalLayout->Release();
3809 : }
3810 :
3811 0 : if( mpAlphaVDev )
3812 0 : mpAlphaVDev->DrawText( rStartPt, rStr, nIndex, nLen, pVector, pDisplayText );
3813 : }
3814 :
3815 0 : long OutputDevice::GetTextWidth( const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen ) const
3816 : {
3817 :
3818 0 : long nWidth = GetTextArray( rStr, NULL, nIndex, nLen );
3819 :
3820 0 : return nWidth;
3821 : }
3822 :
3823 0 : long OutputDevice::GetTextHeight() const
3824 : {
3825 :
3826 0 : if( mbNewFont )
3827 0 : if( !ImplNewFont() )
3828 0 : return 0;
3829 0 : if( mbInitFont )
3830 0 : if( !ImplNewFont() )
3831 0 : return 0;
3832 :
3833 0 : long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
3834 :
3835 0 : if ( mbMap )
3836 0 : nHeight = ImplDevicePixelToLogicHeight( nHeight );
3837 :
3838 0 : return nHeight;
3839 : }
3840 :
3841 0 : float OutputDevice::approximate_char_width() const
3842 : {
3843 0 : return GetTextWidth("aemnnxEM") / 8.0;
3844 : }
3845 :
3846 0 : void OutputDevice::DrawTextArray( const Point& rStartPt, const OUString& rStr,
3847 : const sal_Int32* pDXAry,
3848 : sal_Int32 nIndex, sal_Int32 nLen )
3849 : {
3850 0 : if(nLen == 0x0FFFF)
3851 : {
3852 : SAL_INFO("sal.rtl.xub",
3853 : "DrawTextArray Suspicious arguments nLen:" << nLen);
3854 : }
3855 0 : if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
3856 : {
3857 0 : nLen = rStr.getLength() - nIndex;
3858 : }
3859 0 : if ( mpMetaFile )
3860 0 : mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
3861 :
3862 0 : if ( !IsDeviceOutputNecessary() )
3863 0 : return;
3864 0 : if( !mpGraphics && !ImplGetGraphics() )
3865 0 : return;
3866 0 : if( mbInitClipRegion )
3867 0 : ImplInitClipRegion();
3868 0 : if( mbOutputClipped )
3869 0 : return;
3870 :
3871 0 : SalLayout* pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, pDXAry);
3872 0 : if( pSalLayout )
3873 : {
3874 0 : ImplDrawText( *pSalLayout );
3875 0 : pSalLayout->Release();
3876 : }
3877 :
3878 0 : if( mpAlphaVDev )
3879 0 : mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen );
3880 : }
3881 :
3882 0 : long OutputDevice::GetTextArray( const OUString& rStr, sal_Int32* pDXAry,
3883 : sal_Int32 nIndex, sal_Int32 nLen ) const
3884 : {
3885 0 : if(nLen == 0x0FFFF)
3886 : {
3887 : SAL_INFO("sal.rtl.xub",
3888 : "GetTextArray Suspicious arguments nLen:" << nLen);
3889 : }
3890 :
3891 0 : if( nIndex >= rStr.getLength() )
3892 0 : return 0;
3893 :
3894 0 : if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
3895 : {
3896 0 : nLen = rStr.getLength() - nIndex;
3897 : }
3898 : // do layout
3899 0 : SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
3900 0 : if( !pSalLayout )
3901 0 : return 0;
3902 :
3903 0 : long nWidth = pSalLayout->FillDXArray( pDXAry );
3904 0 : int nWidthFactor = pSalLayout->GetUnitsPerPixel();
3905 0 : pSalLayout->Release();
3906 :
3907 : // convert virtual char widths to virtual absolute positions
3908 0 : if( pDXAry )
3909 0 : for( int i = 1; i < nLen; ++i )
3910 0 : pDXAry[ i ] += pDXAry[ i-1 ];
3911 :
3912 : // convert from font units to logical units
3913 0 : if( mbMap )
3914 : {
3915 0 : if( pDXAry )
3916 0 : for( int i = 0; i < nLen; ++i )
3917 0 : pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] );
3918 0 : nWidth = ImplDevicePixelToLogicWidth( nWidth );
3919 : }
3920 :
3921 0 : if( nWidthFactor > 1 )
3922 : {
3923 0 : if( pDXAry )
3924 0 : for( int i = 0; i < nLen; ++i )
3925 0 : pDXAry[i] /= nWidthFactor;
3926 0 : nWidth /= nWidthFactor;
3927 : }
3928 :
3929 0 : return nWidth;
3930 : }
3931 :
3932 0 : bool OutputDevice::GetCaretPositions( const OUString& rStr, sal_Int32* pCaretXArray,
3933 : sal_Int32 nIndex, sal_Int32 nLen,
3934 : sal_Int32* pDXAry, long nLayoutWidth,
3935 : bool bCellBreaking ) const
3936 : {
3937 :
3938 0 : if( nIndex >= rStr.getLength() )
3939 0 : return false;
3940 0 : if( nIndex+nLen >= rStr.getLength() )
3941 0 : nLen = rStr.getLength() - nIndex;
3942 :
3943 : // layout complex text
3944 : SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen,
3945 0 : Point(0,0), nLayoutWidth, pDXAry );
3946 0 : if( !pSalLayout )
3947 0 : return false;
3948 :
3949 0 : int nWidthFactor = pSalLayout->GetUnitsPerPixel();
3950 0 : pSalLayout->GetCaretPositions( 2*nLen, pCaretXArray );
3951 0 : long nWidth = pSalLayout->GetTextWidth();
3952 0 : pSalLayout->Release();
3953 :
3954 : // fixup unknown caret positions
3955 : int i;
3956 0 : for( i = 0; i < 2 * nLen; ++i )
3957 0 : if( pCaretXArray[ i ] >= 0 )
3958 0 : break;
3959 0 : long nXPos = pCaretXArray[ i ];
3960 0 : for( i = 0; i < 2 * nLen; ++i )
3961 : {
3962 0 : if( pCaretXArray[ i ] >= 0 )
3963 0 : nXPos = pCaretXArray[ i ];
3964 : else
3965 0 : pCaretXArray[ i ] = nXPos;
3966 : }
3967 :
3968 : // handle window mirroring
3969 0 : if( IsRTLEnabled() )
3970 : {
3971 0 : for( i = 0; i < 2 * nLen; ++i )
3972 0 : pCaretXArray[i] = nWidth - pCaretXArray[i] - 1;
3973 : }
3974 :
3975 : // convert from font units to logical units
3976 0 : if( mbMap )
3977 : {
3978 0 : for( i = 0; i < 2*nLen; ++i )
3979 0 : pCaretXArray[i] = ImplDevicePixelToLogicWidth( pCaretXArray[i] );
3980 : }
3981 :
3982 0 : if( nWidthFactor != 1 )
3983 : {
3984 0 : for( i = 0; i < 2*nLen; ++i )
3985 0 : pCaretXArray[i] /= nWidthFactor;
3986 : }
3987 :
3988 : // if requested move caret position to cell limits
3989 : if( bCellBreaking )
3990 : {
3991 : ; // FIXME
3992 : }
3993 :
3994 0 : return true;
3995 : }
3996 :
3997 0 : void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth,
3998 : const OUString& rStr,
3999 : sal_Int32 nIndex, sal_Int32 nLen )
4000 : {
4001 0 : if(nIndex < 0 || nIndex == 0x0FFFF || nLen == 0x0FFFF)
4002 : {
4003 : SAL_INFO("sal.rtl.xub",
4004 : "DrawStretchText Suspicious arguments nIndex:" << nIndex << " nLen:" << nLen);
4005 : }
4006 0 : if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
4007 : {
4008 0 : nLen = rStr.getLength() - nIndex;
4009 : }
4010 :
4011 0 : if ( mpMetaFile )
4012 0 : mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) );
4013 :
4014 0 : if ( !IsDeviceOutputNecessary() )
4015 0 : return;
4016 :
4017 0 : SalLayout* pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, nWidth, NULL);
4018 0 : if( pSalLayout )
4019 : {
4020 0 : ImplDrawText( *pSalLayout );
4021 0 : pSalLayout->Release();
4022 : }
4023 :
4024 0 : if( mpAlphaVDev )
4025 0 : mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen );
4026 : }
4027 :
4028 0 : ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr,
4029 : const sal_Int32 nMinIndex, const sal_Int32 nLen,
4030 : long nPixelWidth, const sal_Int32* pDXArray ) const
4031 : {
4032 : assert(nMinIndex >= 0);
4033 : assert(nLen >= 0);
4034 :
4035 : // get string length for calculating extents
4036 0 : sal_Int32 nEndIndex = rStr.getLength();
4037 0 : if( nMinIndex + nLen < nEndIndex )
4038 0 : nEndIndex = nMinIndex + nLen;
4039 :
4040 : // don't bother if there is nothing to do
4041 0 : if( nEndIndex < nMinIndex )
4042 0 : nEndIndex = nMinIndex;
4043 :
4044 0 : int nLayoutFlags = 0;
4045 0 : if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL )
4046 0 : nLayoutFlags |= SAL_LAYOUT_BIDI_RTL;
4047 0 : if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG )
4048 0 : nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
4049 0 : else if( 0 == (mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) )
4050 : {
4051 : // disable Bidi if no RTL hint and no RTL codes used
4052 0 : const sal_Unicode* pStr = rStr.getStr() + nMinIndex;
4053 0 : const sal_Unicode* pEnd = rStr.getStr() + nEndIndex;
4054 0 : for( ; pStr < pEnd; ++pStr )
4055 0 : if( ((*pStr >= 0x0580) && (*pStr < 0x0800)) // middle eastern scripts
4056 0 : || ((*pStr >= 0xFB18) && (*pStr < 0xFE00)) // hebrew + arabic A presentation forms
4057 0 : || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B
4058 : break;
4059 0 : if( pStr >= pEnd )
4060 0 : nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
4061 : }
4062 :
4063 0 : if( mbKerning )
4064 0 : nLayoutFlags |= SAL_LAYOUT_KERNING_PAIRS;
4065 0 : if( maFont.GetKerning() & KERNING_ASIAN )
4066 0 : nLayoutFlags |= SAL_LAYOUT_KERNING_ASIAN;
4067 0 : if( maFont.IsVertical() )
4068 0 : nLayoutFlags |= SAL_LAYOUT_VERTICAL;
4069 :
4070 0 : if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES )
4071 0 : nLayoutFlags |= SAL_LAYOUT_ENABLE_LIGATURES;
4072 0 : else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED )
4073 0 : nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
4074 : else
4075 : {
4076 : // disable CTL for non-CTL text
4077 0 : const sal_Unicode* pStr = rStr.getStr() + nMinIndex;
4078 0 : const sal_Unicode* pEnd = rStr.getStr() + nEndIndex;
4079 0 : for( ; pStr < pEnd; ++pStr )
4080 0 : if( ((*pStr >= 0x0300) && (*pStr < 0x0370)) // diacritical marks
4081 0 : || ((*pStr >= 0x0590) && (*pStr < 0x10A0)) // many CTL scripts
4082 0 : || ((*pStr >= 0x1100) && (*pStr < 0x1200)) // hangul jamo
4083 0 : || ((*pStr >= 0x1700) && (*pStr < 0x1900)) // many CTL scripts
4084 0 : || ((*pStr >= 0xFB1D) && (*pStr < 0xFE00)) // middle east presentation
4085 0 : || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) // arabic presentation B
4086 0 : || ((*pStr >= 0xFE00) && (*pStr < 0xFE10)) // variation selectors in BMP
4087 0 : || ((pStr + 1 < pEnd) && (pStr[0] == 0xDB40) && (0xDD00 <= pStr[1]) && (pStr[1] < 0xDEF0)) // variation selector supplement
4088 : )
4089 : break;
4090 0 : if( pStr >= pEnd )
4091 0 : nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
4092 : }
4093 :
4094 0 : if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS)
4095 : {
4096 : // disable character localization when no digits used
4097 0 : const sal_Unicode* pBase = rStr.getStr();
4098 0 : const sal_Unicode* pStr = pBase + nMinIndex;
4099 0 : const sal_Unicode* pEnd = pBase + nEndIndex;
4100 0 : OUStringBuffer sTmpStr(rStr);
4101 0 : for( ; pStr < pEnd; ++pStr )
4102 : {
4103 : // TODO: are there non-digit localizations?
4104 0 : if( (*pStr >= '0') && (*pStr <= '9') )
4105 : {
4106 : // translate characters to local preference
4107 0 : sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
4108 0 : if( cChar != *pStr )
4109 : // TODO: are the localized digit surrogates?
4110 0 : sTmpStr[pStr - pBase] = cChar;
4111 : }
4112 : }
4113 0 : rStr = sTmpStr.makeStringAndClear();
4114 : }
4115 :
4116 : // right align for RTL text, DRAWPOS_REVERSED, RTL window style
4117 0 : bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0);
4118 0 : if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT )
4119 0 : bRightAlign = false;
4120 0 : else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT )
4121 0 : bRightAlign = true;
4122 : // SSA: hack for western office, ie text get right aligned
4123 : // for debugging purposes of mirrored UI
4124 0 : bool bRTLWindow = IsRTLEnabled();
4125 0 : bRightAlign ^= bRTLWindow;
4126 0 : if( bRightAlign )
4127 0 : nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN;
4128 :
4129 : // set layout options
4130 0 : ImplLayoutArgs aLayoutArgs( rStr.getStr(), rStr.getLength(), nMinIndex, nEndIndex, nLayoutFlags, maFont.GetLanguageTag() );
4131 :
4132 0 : int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0;
4133 0 : aLayoutArgs.SetOrientation( nOrientation );
4134 :
4135 0 : aLayoutArgs.SetLayoutWidth( nPixelWidth );
4136 0 : aLayoutArgs.SetDXArray( pDXArray );
4137 :
4138 0 : return aLayoutArgs;
4139 : }
4140 :
4141 0 : SalLayout* OutputDevice::ImplLayout(const OUString& rOrigStr,
4142 : sal_Int32 nMinIndex, sal_Int32 nLen,
4143 : const Point& rLogicalPos, long nLogicalWidth,
4144 : const sal_Int32* pDXArray) const
4145 : {
4146 : // we need a graphics
4147 0 : if( !mpGraphics )
4148 0 : if( !ImplGetGraphics() )
4149 0 : return NULL;
4150 :
4151 : // initialize font if needed
4152 0 : if( mbNewFont )
4153 0 : if( !ImplNewFont() )
4154 0 : return NULL;
4155 0 : if( mbInitFont )
4156 0 : ImplInitFont();
4157 :
4158 : // check string index and length
4159 0 : if( -1 == nLen || nMinIndex + nLen > rOrigStr.getLength() )
4160 : {
4161 0 : const sal_Int32 nNewLen = rOrigStr.getLength() - nMinIndex;
4162 0 : if( nNewLen <= 0 )
4163 0 : return NULL;
4164 0 : nLen = nNewLen;
4165 : }
4166 :
4167 0 : OUString aStr = rOrigStr;
4168 :
4169 : // convert from logical units to physical units
4170 : // recode string if needed
4171 0 : if( mpFontEntry->mpConversion ) {
4172 0 : mpFontEntry->mpConversion->RecodeString( aStr, 0, aStr.getLength() );
4173 : }
4174 :
4175 0 : long nPixelWidth = nLogicalWidth;
4176 0 : if( nLogicalWidth && mbMap )
4177 0 : nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth );
4178 0 : if( pDXArray && mbMap )
4179 : {
4180 : // convert from logical units to font units using a temporary array
4181 0 : sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );
4182 : // using base position for better rounding a.k.a. "dancing characters"
4183 0 : int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() );
4184 0 : for( int i = 0; i < nLen; ++i )
4185 0 : pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs;
4186 :
4187 0 : pDXArray = pTempDXAry;
4188 : }
4189 :
4190 0 : ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray );
4191 :
4192 : #if defined(MACOSX) || defined(IOS)
4193 : // CoreText layouts are immutable and already contain the text color
4194 : // so we need to provide the color already for the layout request
4195 : // even if this layout will never be drawn
4196 : if( mbInitTextColor )
4197 : const_cast<OutputDevice&>(*this).ImplInitTextColor();
4198 : #endif
4199 :
4200 : // get matching layout object for base font
4201 0 : SalLayout* pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );
4202 :
4203 : // layout text
4204 0 : if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) )
4205 : {
4206 0 : pSalLayout->Release();
4207 0 : pSalLayout = NULL;
4208 : }
4209 :
4210 0 : if( !pSalLayout )
4211 0 : return NULL;
4212 :
4213 : // do glyph fallback if needed
4214 : // #105768# avoid fallback for very small font sizes
4215 0 : if (aLayoutArgs.NeedFallback() && mpFontEntry->maFontSelData.mnHeight >= 3)
4216 0 : pSalLayout = ImplGlyphFallbackLayout(pSalLayout, aLayoutArgs);
4217 :
4218 : // position, justify, etc. the layout
4219 0 : pSalLayout->AdjustLayout( aLayoutArgs );
4220 0 : pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos );
4221 : // adjust to right alignment if necessary
4222 0 : if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN )
4223 : {
4224 : long nRTLOffset;
4225 0 : if( pDXArray )
4226 0 : nRTLOffset = pDXArray[ nLen - 1 ];
4227 0 : else if( nPixelWidth )
4228 0 : nRTLOffset = nPixelWidth;
4229 : else
4230 0 : nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
4231 0 : pSalLayout->DrawOffset().X() = 1 - nRTLOffset;
4232 : }
4233 :
4234 0 : return pSalLayout;
4235 : }
4236 :
4237 0 : SalLayout* OutputDevice::getFallbackFont(ImplFontEntry &rFallbackFont,
4238 : FontSelectPattern &rFontSelData, int nFallbackLevel,
4239 : ImplLayoutArgs& rLayoutArgs) const
4240 : {
4241 0 : rFallbackFont.mnSetFontFlags = mpGraphics->SetFont( &rFontSelData, nFallbackLevel );
4242 :
4243 0 : rLayoutArgs.ResetPos();
4244 0 : SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel );
4245 :
4246 0 : if (!pFallback)
4247 0 : return NULL;
4248 :
4249 0 : if (!pFallback->LayoutText(rLayoutArgs))
4250 : {
4251 : // there is no need for a font that couldn't resolve anything
4252 0 : pFallback->Release();
4253 0 : return NULL;
4254 : }
4255 :
4256 0 : pFallback->AdjustLayout( rLayoutArgs );
4257 :
4258 0 : return pFallback;
4259 : }
4260 :
4261 0 : SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const
4262 : {
4263 : // prepare multi level glyph fallback
4264 0 : MultiSalLayout* pMultiSalLayout = NULL;
4265 0 : ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
4266 0 : rLayoutArgs.PrepareFallback();
4267 0 : rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
4268 :
4269 : // get list of unicodes that need glyph fallback
4270 0 : int nCharPos = -1;
4271 0 : bool bRTL = false;
4272 0 : OUStringBuffer aMissingCodeBuf;
4273 0 : while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) )
4274 0 : aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] );
4275 0 : rLayoutArgs.ResetPos();
4276 0 : OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();
4277 :
4278 0 : FontSelectPattern aFontSelData = mpFontEntry->maFontSelData;
4279 :
4280 : // try if fallback fonts support the missing unicodes
4281 0 : for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
4282 : {
4283 : // find a font family suited for glyph fallback
4284 : #ifndef FONTFALLBACK_HOOKS_DISABLED
4285 : // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry
4286 : // if the system-specific glyph fallback is active
4287 0 : aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level
4288 : #endif
4289 : ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontCollection,
4290 0 : aFontSelData, nFallbackLevel, aMissingCodes );
4291 0 : if( !pFallbackFont )
4292 0 : break;
4293 :
4294 0 : aFontSelData.mpFontEntry = pFallbackFont;
4295 0 : aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData;
4296 0 : if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1)
4297 : {
4298 : // ignore fallback font if it is the same as the original font
4299 0 : if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData )
4300 : {
4301 0 : mpFontCache->Release( pFallbackFont );
4302 0 : continue;
4303 : }
4304 : }
4305 :
4306 : // create and add glyph fallback layout to multilayout
4307 : SalLayout* pFallback = getFallbackFont(*pFallbackFont, aFontSelData,
4308 0 : nFallbackLevel, rLayoutArgs);
4309 0 : if (pFallback)
4310 : {
4311 0 : if( !pMultiSalLayout )
4312 0 : pMultiSalLayout = new MultiSalLayout( *pSalLayout );
4313 : pMultiSalLayout->AddFallback( *pFallback,
4314 0 : rLayoutArgs.maRuns, aFontSelData.mpFontData );
4315 0 : if (nFallbackLevel == MAX_FALLBACK-1)
4316 0 : pMultiSalLayout->SetInComplete();
4317 : }
4318 :
4319 0 : mpFontCache->Release( pFallbackFont );
4320 :
4321 : // break when this fallback was sufficient
4322 0 : if( !rLayoutArgs.PrepareFallback() )
4323 0 : break;
4324 : }
4325 :
4326 0 : if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) )
4327 0 : pSalLayout = pMultiSalLayout;
4328 :
4329 : // restore orig font settings
4330 0 : pSalLayout->InitFont();
4331 0 : rLayoutArgs.maRuns = aLayoutRuns;
4332 :
4333 0 : return pSalLayout;
4334 : }
4335 :
4336 0 : bool OutputDevice::GetTextIsRTL( const OUString& rString, sal_Int32 nIndex, sal_Int32 nLen ) const
4337 : {
4338 0 : OUString aStr( rString );
4339 0 : ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
4340 0 : bool bRTL = false;
4341 0 : int nCharPos = -1;
4342 0 : aArgs.GetNextPos( &nCharPos, &bRTL );
4343 0 : return (nCharPos != nIndex) ? sal_True : sal_False;
4344 : }
4345 :
4346 0 : sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, long nTextWidth,
4347 : sal_Int32 nIndex, sal_Int32 nLen,
4348 : long nCharExtra ) const
4349 : {
4350 0 : SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
4351 0 : sal_Int32 nRetVal = -1;
4352 0 : if( pSalLayout )
4353 : {
4354 : // convert logical widths into layout units
4355 : // NOTE: be very careful to avoid rounding errors for nCharExtra case
4356 : // problem with rounding errors especially for small nCharExtras
4357 : // TODO: remove when layout units have subpixel granularity
4358 0 : long nWidthFactor = pSalLayout->GetUnitsPerPixel();
4359 0 : long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
4360 0 : nTextWidth *= nWidthFactor * nSubPixelFactor;
4361 0 : long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
4362 0 : long nExtraPixelWidth = 0;
4363 0 : if( nCharExtra != 0 )
4364 : {
4365 0 : nCharExtra *= nWidthFactor * nSubPixelFactor;
4366 0 : nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
4367 : }
4368 0 : nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
4369 :
4370 0 : pSalLayout->Release();
4371 : }
4372 :
4373 0 : return nRetVal;
4374 : }
4375 :
4376 0 : sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, long nTextWidth,
4377 : sal_Unicode nHyphenChar, sal_Int32& rHyphenPos,
4378 : sal_Int32 nIndex, sal_Int32 nLen,
4379 : long nCharExtra ) const
4380 : {
4381 0 : rHyphenPos = -1;
4382 :
4383 0 : SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
4384 0 : sal_Int32 nRetVal = -1;
4385 0 : if( pSalLayout )
4386 : {
4387 : // convert logical widths into layout units
4388 : // NOTE: be very careful to avoid rounding errors for nCharExtra case
4389 : // problem with rounding errors especially for small nCharExtras
4390 : // TODO: remove when layout units have subpixel granularity
4391 0 : long nWidthFactor = pSalLayout->GetUnitsPerPixel();
4392 0 : long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
4393 :
4394 0 : nTextWidth *= nWidthFactor * nSubPixelFactor;
4395 0 : long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
4396 0 : long nExtraPixelWidth = 0;
4397 0 : if( nCharExtra != 0 )
4398 : {
4399 0 : nCharExtra *= nWidthFactor * nSubPixelFactor;
4400 0 : nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
4401 : }
4402 :
4403 : // calculate un-hyphenated break position
4404 0 : nRetVal = pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor );
4405 :
4406 : // calculate hyphenated break position
4407 0 : OUString aHyphenStr(nHyphenChar);
4408 0 : sal_Int32 nTempLen = 1;
4409 0 : SalLayout* pHyphenLayout = ImplLayout( aHyphenStr, 0, nTempLen );
4410 0 : if( pHyphenLayout )
4411 : {
4412 : // calculate subpixel width of hyphenation character
4413 0 : long nHyphenPixelWidth = pHyphenLayout->GetTextWidth() * nSubPixelFactor;
4414 0 : pHyphenLayout->Release();
4415 :
4416 : // calculate hyphenated break position
4417 0 : nTextPixelWidth -= nHyphenPixelWidth;
4418 0 : if( nExtraPixelWidth > 0 )
4419 0 : nTextPixelWidth -= nExtraPixelWidth;
4420 :
4421 0 : rHyphenPos = pSalLayout->GetTextBreak(nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor);
4422 :
4423 0 : if( rHyphenPos > nRetVal )
4424 0 : rHyphenPos = nRetVal;
4425 : }
4426 :
4427 0 : pSalLayout->Release();
4428 : }
4429 :
4430 0 : return nRetVal;
4431 : }
4432 :
4433 0 : void OutputDevice::ImplDrawText( OutputDevice& rTargetDevice, const Rectangle& rRect,
4434 : const OUString& rOrigStr, sal_uInt16 nStyle,
4435 : MetricVector* pVector, OUString* pDisplayText,
4436 : ::vcl::ITextLayout& _rLayout )
4437 : {
4438 0 : Color aOldTextColor;
4439 0 : Color aOldTextFillColor;
4440 0 : bool bRestoreFillColor = false;
4441 0 : if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector )
4442 : {
4443 0 : bool bHighContrastBlack = false;
4444 0 : bool bHighContrastWhite = false;
4445 0 : const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() );
4446 0 : if( rStyleSettings.GetHighContrastMode() )
4447 : {
4448 0 : Color aCol;
4449 0 : if( rTargetDevice.IsBackground() )
4450 0 : aCol = rTargetDevice.GetBackground().GetColor();
4451 : else
4452 : // best guess is the face color here
4453 : // but it may be totally wrong. the background color
4454 : // was typically already reset
4455 0 : aCol = rStyleSettings.GetFaceColor();
4456 :
4457 0 : bHighContrastBlack = aCol.IsDark();
4458 0 : bHighContrastWhite = aCol.IsBright();
4459 : }
4460 :
4461 0 : aOldTextColor = rTargetDevice.GetTextColor();
4462 0 : if ( rTargetDevice.IsTextFillColor() )
4463 : {
4464 0 : bRestoreFillColor = true;
4465 0 : aOldTextFillColor = rTargetDevice.GetTextFillColor();
4466 : }
4467 0 : if( bHighContrastBlack )
4468 0 : rTargetDevice.SetTextColor( COL_GREEN );
4469 0 : else if( bHighContrastWhite )
4470 0 : rTargetDevice.SetTextColor( COL_LIGHTGREEN );
4471 : else
4472 : {
4473 : // draw disabled text always without shadow
4474 : // as it fits better with native look
4475 0 : rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() );
4476 : }
4477 : }
4478 :
4479 0 : long nWidth = rRect.GetWidth();
4480 0 : long nHeight = rRect.GetHeight();
4481 :
4482 0 : if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) )
4483 0 : return;
4484 :
4485 0 : Point aPos = rRect.TopLeft();
4486 :
4487 0 : long nTextHeight = rTargetDevice.GetTextHeight();
4488 0 : TextAlign eAlign = rTargetDevice.GetTextAlign();
4489 0 : sal_Int32 nMnemonicPos = -1;
4490 :
4491 0 : OUString aStr = rOrigStr;
4492 0 : if ( nStyle & TEXT_DRAW_MNEMONIC )
4493 0 : aStr = GetNonMnemonicString( aStr, nMnemonicPos );
4494 :
4495 0 : const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector;
4496 :
4497 : // We treat multiline text differently
4498 0 : if ( nStyle & TEXT_DRAW_MULTILINE )
4499 : {
4500 :
4501 0 : OUString aLastLine;
4502 0 : ImplMultiTextLineInfo aMultiLineInfo;
4503 : ImplTextLineInfo* pLineInfo;
4504 : sal_Int32 i;
4505 : sal_Int32 nLines;
4506 : sal_Int32 nFormatLines;
4507 :
4508 0 : if ( nTextHeight )
4509 : {
4510 0 : long nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _rLayout );
4511 0 : nLines = (sal_Int32)(nHeight/nTextHeight);
4512 0 : nFormatLines = aMultiLineInfo.Count();
4513 0 : if ( !nLines )
4514 0 : nLines = 1;
4515 0 : if ( nFormatLines > nLines )
4516 : {
4517 0 : if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
4518 : {
4519 : // Create last line and shorten it
4520 0 : nFormatLines = nLines-1;
4521 :
4522 0 : pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
4523 0 : aLastLine = convertLineEnd(aStr.copy(pLineInfo->GetIndex()), LINEEND_LF);
4524 : // Replace all LineFeeds with Spaces
4525 0 : OUStringBuffer aLastLineBuffer(aLastLine);
4526 0 : sal_Int32 nLastLineLen = aLastLineBuffer.getLength();
4527 0 : for ( i = 0; i < nLastLineLen; i++ )
4528 : {
4529 0 : if ( aLastLineBuffer[ i ] == '\n' )
4530 0 : aLastLineBuffer[ i ] = ' ';
4531 : }
4532 0 : aLastLine = aLastLineBuffer.makeStringAndClear();
4533 0 : aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout );
4534 0 : nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
4535 0 : nStyle |= TEXT_DRAW_TOP;
4536 : }
4537 : }
4538 : else
4539 : {
4540 0 : if ( nMaxTextWidth <= nWidth )
4541 0 : nStyle &= ~TEXT_DRAW_CLIP;
4542 : }
4543 :
4544 : // Do we need to clip the height?
4545 0 : if ( nFormatLines*nTextHeight > nHeight )
4546 0 : nStyle |= TEXT_DRAW_CLIP;
4547 :
4548 : // Set clipping
4549 0 : if ( nStyle & TEXT_DRAW_CLIP )
4550 : {
4551 0 : rTargetDevice.Push( PUSH_CLIPREGION );
4552 0 : rTargetDevice.IntersectClipRegion( rRect );
4553 : }
4554 :
4555 : // Vertical alignment
4556 0 : if ( nStyle & TEXT_DRAW_BOTTOM )
4557 0 : aPos.Y() += nHeight-(nFormatLines*nTextHeight);
4558 0 : else if ( nStyle & TEXT_DRAW_VCENTER )
4559 0 : aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
4560 :
4561 : // Font alignment
4562 0 : if ( eAlign == ALIGN_BOTTOM )
4563 0 : aPos.Y() += nTextHeight;
4564 0 : else if ( eAlign == ALIGN_BASELINE )
4565 0 : aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
4566 :
4567 : // Output all lines except for the last one
4568 0 : for ( i = 0; i < nFormatLines; i++ )
4569 : {
4570 0 : pLineInfo = aMultiLineInfo.GetLine( i );
4571 0 : if ( nStyle & TEXT_DRAW_RIGHT )
4572 0 : aPos.X() += nWidth-pLineInfo->GetWidth();
4573 0 : else if ( nStyle & TEXT_DRAW_CENTER )
4574 0 : aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
4575 0 : sal_Int32 nIndex = pLineInfo->GetIndex();
4576 0 : sal_Int32 nLineLen = pLineInfo->GetLen();
4577 0 : _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
4578 0 : if ( bDrawMnemonics )
4579 : {
4580 0 : if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
4581 : {
4582 : long nMnemonicX;
4583 : long nMnemonicY;
4584 : long nMnemonicWidth;
4585 :
4586 0 : sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen );
4587 : /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray,
4588 0 : nIndex, nLineLen );
4589 0 : long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
4590 0 : long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
4591 0 : nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) );
4592 :
4593 0 : Point aTempPos = rTargetDevice.LogicToPixel( aPos );
4594 0 : nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min( lc_x1, lc_x2 ) );
4595 0 : nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
4596 0 : rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
4597 : }
4598 : }
4599 0 : aPos.Y() += nTextHeight;
4600 0 : aPos.X() = rRect.Left();
4601 : }
4602 :
4603 : // If there still is a last line, we output it left-aligned as the line would be clipped
4604 0 : if ( !aLastLine.isEmpty() )
4605 0 : _rLayout.DrawText( aPos, aLastLine, 0, aLastLine.getLength(), pVector, pDisplayText );
4606 :
4607 : // Reset clipping
4608 0 : if ( nStyle & TEXT_DRAW_CLIP )
4609 0 : rTargetDevice.Pop();
4610 0 : }
4611 : }
4612 : else
4613 : {
4614 0 : long nTextWidth = _rLayout.GetTextWidth( aStr, 0, -1 );
4615 :
4616 : // Clip text if needed
4617 0 : if ( nTextWidth > nWidth )
4618 : {
4619 0 : if ( nStyle & TEXT_DRAW_ELLIPSIS )
4620 : {
4621 0 : aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout );
4622 0 : nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
4623 0 : nStyle |= TEXT_DRAW_LEFT;
4624 0 : nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.getLength() );
4625 : }
4626 : }
4627 : else
4628 : {
4629 0 : if ( nTextHeight <= nHeight )
4630 0 : nStyle &= ~TEXT_DRAW_CLIP;
4631 : }
4632 :
4633 : // horizontal text alignment
4634 0 : if ( nStyle & TEXT_DRAW_RIGHT )
4635 0 : aPos.X() += nWidth-nTextWidth;
4636 0 : else if ( nStyle & TEXT_DRAW_CENTER )
4637 0 : aPos.X() += (nWidth-nTextWidth)/2;
4638 :
4639 : // vertical font alignment
4640 0 : if ( eAlign == ALIGN_BOTTOM )
4641 0 : aPos.Y() += nTextHeight;
4642 0 : else if ( eAlign == ALIGN_BASELINE )
4643 0 : aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
4644 :
4645 0 : if ( nStyle & TEXT_DRAW_BOTTOM )
4646 0 : aPos.Y() += nHeight-nTextHeight;
4647 0 : else if ( nStyle & TEXT_DRAW_VCENTER )
4648 0 : aPos.Y() += (nHeight-nTextHeight)/2;
4649 :
4650 0 : long nMnemonicX = 0;
4651 0 : long nMnemonicY = 0;
4652 0 : long nMnemonicWidth = 0;
4653 0 : if ( nMnemonicPos != -1 )
4654 : {
4655 0 : sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.getLength() );
4656 0 : /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, 0, aStr.getLength() );
4657 0 : long lc_x1 = pCaretXArray[2*(nMnemonicPos)];
4658 0 : long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1];
4659 0 : nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) );
4660 :
4661 0 : Point aTempPos = rTargetDevice.LogicToPixel( aPos );
4662 0 : nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( std::min(lc_x1, lc_x2) );
4663 0 : nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
4664 : }
4665 :
4666 0 : if ( nStyle & TEXT_DRAW_CLIP )
4667 : {
4668 0 : rTargetDevice.Push( PUSH_CLIPREGION );
4669 0 : rTargetDevice.IntersectClipRegion( rRect );
4670 0 : _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
4671 0 : if ( bDrawMnemonics )
4672 : {
4673 0 : if ( nMnemonicPos != -1 )
4674 0 : rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
4675 : }
4676 0 : rTargetDevice.Pop();
4677 : }
4678 : else
4679 : {
4680 0 : _rLayout.DrawText( aPos, aStr, 0, aStr.getLength(), pVector, pDisplayText );
4681 0 : if ( bDrawMnemonics )
4682 : {
4683 0 : if ( nMnemonicPos != -1 )
4684 0 : rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
4685 : }
4686 : }
4687 : }
4688 :
4689 0 : if ( nStyle & TEXT_DRAW_DISABLE && !pVector )
4690 : {
4691 0 : rTargetDevice.SetTextColor( aOldTextColor );
4692 0 : if ( bRestoreFillColor )
4693 0 : rTargetDevice.SetTextFillColor( aOldTextFillColor );
4694 0 : }
4695 : }
4696 :
4697 0 : void OutputDevice::AddTextRectActions( const Rectangle& rRect,
4698 : const OUString& rOrigStr,
4699 : sal_uInt16 nStyle,
4700 : GDIMetaFile& rMtf )
4701 : {
4702 :
4703 0 : if ( rOrigStr.isEmpty() || rRect.IsEmpty() )
4704 0 : return;
4705 :
4706 : // we need a graphics
4707 0 : if( !mpGraphics && !ImplGetGraphics() )
4708 0 : return;
4709 0 : if( mbInitClipRegion )
4710 0 : ImplInitClipRegion();
4711 :
4712 : // temporarily swap in passed mtf for action generation, and
4713 : // disable output generation.
4714 0 : const bool bOutputEnabled( IsOutputEnabled() );
4715 0 : GDIMetaFile* pMtf = mpMetaFile;
4716 :
4717 0 : mpMetaFile = &rMtf;
4718 0 : EnableOutput( false );
4719 :
4720 : // #i47157# Factored out to ImplDrawTextRect(), to be shared
4721 : // between us and DrawText()
4722 0 : DefaultTextLayout aLayout( *this );
4723 0 : ImplDrawText( *this, rRect, rOrigStr, nStyle, NULL, NULL, aLayout );
4724 :
4725 : // and restore again
4726 0 : EnableOutput( bOutputEnabled );
4727 0 : mpMetaFile = pMtf;
4728 : }
4729 :
4730 0 : void OutputDevice::DrawText( const Rectangle& rRect, const OUString& rOrigStr, sal_uInt16 nStyle,
4731 : MetricVector* pVector, OUString* pDisplayText,
4732 : ::vcl::ITextLayout* _pTextLayout )
4733 : {
4734 0 : if( mpOutDevData && mpOutDevData->mpRecordLayout )
4735 : {
4736 0 : pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
4737 0 : pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
4738 : }
4739 :
4740 0 : bool bDecomposeTextRectAction = ( _pTextLayout != NULL ) && _pTextLayout->DecomposeTextRectAction();
4741 0 : if ( mpMetaFile && !bDecomposeTextRectAction )
4742 0 : mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) );
4743 :
4744 0 : if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || rOrigStr.isEmpty() || rRect.IsEmpty() )
4745 0 : return;
4746 :
4747 : // we need a graphics
4748 0 : if( !mpGraphics && !ImplGetGraphics() )
4749 0 : return;
4750 0 : if( mbInitClipRegion )
4751 0 : ImplInitClipRegion();
4752 0 : if( mbOutputClipped && !bDecomposeTextRectAction )
4753 0 : return;
4754 :
4755 : // temporarily disable mtf action generation (ImplDrawText _does_
4756 : // create META_TEXT_ACTIONs otherwise)
4757 0 : GDIMetaFile* pMtf = mpMetaFile;
4758 0 : if ( !bDecomposeTextRectAction )
4759 0 : mpMetaFile = NULL;
4760 :
4761 : // #i47157# Factored out to ImplDrawText(), to be used also
4762 : // from AddTextRectActions()
4763 0 : DefaultTextLayout aDefaultLayout( *this );
4764 0 : ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout );
4765 :
4766 : // and enable again
4767 0 : mpMetaFile = pMtf;
4768 :
4769 0 : if( mpAlphaVDev )
4770 0 : mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
4771 : }
4772 :
4773 0 : Rectangle OutputDevice::GetTextRect( const Rectangle& rRect,
4774 : const OUString& rStr, sal_uInt16 nStyle,
4775 : TextRectInfo* pInfo,
4776 : const ::vcl::ITextLayout* _pTextLayout ) const
4777 : {
4778 :
4779 0 : Rectangle aRect = rRect;
4780 : sal_Int32 nLines;
4781 0 : long nWidth = rRect.GetWidth();
4782 : long nMaxWidth;
4783 0 : long nTextHeight = GetTextHeight();
4784 :
4785 0 : OUString aStr = rStr;
4786 0 : if ( nStyle & TEXT_DRAW_MNEMONIC )
4787 0 : aStr = GetNonMnemonicString( aStr );
4788 :
4789 0 : if ( nStyle & TEXT_DRAW_MULTILINE )
4790 : {
4791 0 : ImplMultiTextLineInfo aMultiLineInfo;
4792 : ImplTextLineInfo* pLineInfo;
4793 : sal_Int32 nFormatLines;
4794 : sal_Int32 i;
4795 :
4796 0 : nMaxWidth = 0;
4797 0 : DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) );
4798 0 : ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout );
4799 0 : nFormatLines = aMultiLineInfo.Count();
4800 0 : if ( !nTextHeight )
4801 0 : nTextHeight = 1;
4802 0 : nLines = (sal_uInt16)(aRect.GetHeight()/nTextHeight);
4803 0 : if ( pInfo )
4804 0 : pInfo->mnLineCount = nFormatLines;
4805 0 : if ( !nLines )
4806 0 : nLines = 1;
4807 0 : if ( nFormatLines <= nLines )
4808 0 : nLines = nFormatLines;
4809 : else
4810 : {
4811 0 : if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) )
4812 0 : nLines = nFormatLines;
4813 : else
4814 : {
4815 0 : if ( pInfo )
4816 0 : pInfo->mbEllipsis = true;
4817 0 : nMaxWidth = nWidth;
4818 : }
4819 : }
4820 0 : if ( pInfo )
4821 : {
4822 0 : bool bMaxWidth = nMaxWidth == 0;
4823 0 : pInfo->mnMaxWidth = 0;
4824 0 : for ( i = 0; i < nLines; i++ )
4825 : {
4826 0 : pLineInfo = aMultiLineInfo.GetLine( i );
4827 0 : if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) )
4828 0 : nMaxWidth = pLineInfo->GetWidth();
4829 0 : if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth )
4830 0 : pInfo->mnMaxWidth = pLineInfo->GetWidth();
4831 : }
4832 : }
4833 0 : else if ( !nMaxWidth )
4834 : {
4835 0 : for ( i = 0; i < nLines; i++ )
4836 : {
4837 0 : pLineInfo = aMultiLineInfo.GetLine( i );
4838 0 : if ( pLineInfo->GetWidth() > nMaxWidth )
4839 0 : nMaxWidth = pLineInfo->GetWidth();
4840 : }
4841 0 : }
4842 : }
4843 : else
4844 : {
4845 0 : nLines = 1;
4846 0 : nMaxWidth = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.getLength() ) : GetTextWidth( aStr );
4847 :
4848 0 : if ( pInfo )
4849 : {
4850 0 : pInfo->mnLineCount = 1;
4851 0 : pInfo->mnMaxWidth = nMaxWidth;
4852 : }
4853 :
4854 0 : if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) )
4855 : {
4856 0 : if ( pInfo )
4857 0 : pInfo->mbEllipsis = true;
4858 0 : nMaxWidth = nWidth;
4859 : }
4860 : }
4861 :
4862 0 : if ( nStyle & TEXT_DRAW_RIGHT )
4863 0 : aRect.Left() = aRect.Right()-nMaxWidth+1;
4864 0 : else if ( nStyle & TEXT_DRAW_CENTER )
4865 : {
4866 0 : aRect.Left() += (nWidth-nMaxWidth)/2;
4867 0 : aRect.Right() = aRect.Left()+nMaxWidth-1;
4868 : }
4869 : else
4870 0 : aRect.Right() = aRect.Left()+nMaxWidth-1;
4871 :
4872 0 : if ( nStyle & TEXT_DRAW_BOTTOM )
4873 0 : aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1;
4874 0 : else if ( nStyle & TEXT_DRAW_VCENTER )
4875 : {
4876 0 : aRect.Top() += (aRect.GetHeight()-(nTextHeight*nLines))/2;
4877 0 : aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
4878 : }
4879 : else
4880 0 : aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
4881 :
4882 : // #99188# get rid of rounding problems when using this rect later
4883 0 : if (nStyle & TEXT_DRAW_RIGHT)
4884 0 : aRect.Left()--;
4885 : else
4886 0 : aRect.Right()++;
4887 0 : return aRect;
4888 : }
4889 :
4890 0 : static bool ImplIsCharIn( sal_Unicode c, const sal_Char* pStr )
4891 : {
4892 0 : while ( *pStr )
4893 : {
4894 0 : if ( *pStr == c )
4895 0 : return true;
4896 0 : pStr++;
4897 : }
4898 :
4899 0 : return false;
4900 : }
4901 :
4902 0 : OUString OutputDevice::GetEllipsisString( const OUString& rOrigStr, long nMaxWidth,
4903 : sal_uInt16 nStyle ) const
4904 : {
4905 0 : DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) );
4906 0 : return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout );
4907 : }
4908 :
4909 0 : OUString OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const OUString& rOrigStr, long nMaxWidth,
4910 : sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout )
4911 : {
4912 0 : OUString aStr = rOrigStr;
4913 0 : sal_Int32 nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.getLength() );
4914 :
4915 0 : if ( nIndex != -1 )
4916 : {
4917 0 : if( (nStyle & TEXT_DRAW_CENTERELLIPSIS) == TEXT_DRAW_CENTERELLIPSIS )
4918 : {
4919 0 : OUStringBuffer aTmpStr( aStr );
4920 0 : sal_Int32 nEraseChars = 4;
4921 0 : while( nEraseChars < aStr.getLength() && _rLayout.GetTextWidth( aTmpStr.toString(), 0, aTmpStr.getLength() ) > nMaxWidth )
4922 : {
4923 0 : aTmpStr = OUStringBuffer(aStr);
4924 0 : sal_Int32 i = (aTmpStr.getLength() - nEraseChars)/2;
4925 0 : aTmpStr.remove(i, nEraseChars++);
4926 0 : aTmpStr.insert(i, "...");
4927 : }
4928 0 : aStr = aTmpStr.makeStringAndClear();
4929 : }
4930 0 : else if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
4931 : {
4932 0 : aStr = aStr.copy(0, nIndex);
4933 0 : if ( nIndex > 1 )
4934 : {
4935 0 : aStr += "...";
4936 0 : while ( !aStr.isEmpty() && (_rLayout.GetTextWidth( aStr, 0, aStr.getLength() ) > nMaxWidth) )
4937 : {
4938 0 : if ( (nIndex > 1) || (nIndex == aStr.getLength()) )
4939 0 : nIndex--;
4940 0 : aStr = aStr.replaceAt( nIndex, 1, "");
4941 : }
4942 : }
4943 :
4944 0 : if ( aStr.isEmpty() && (nStyle & TEXT_DRAW_CLIP) )
4945 0 : aStr += OUString(rOrigStr[ 0 ]);
4946 : }
4947 0 : else if ( nStyle & TEXT_DRAW_PATHELLIPSIS )
4948 : {
4949 0 : OUString aPath( rOrigStr );
4950 0 : OUString aAbbreviatedPath;
4951 0 : osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, NULL );
4952 0 : aStr = aAbbreviatedPath;
4953 : }
4954 0 : else if ( nStyle & TEXT_DRAW_NEWSELLIPSIS )
4955 : {
4956 : static sal_Char const pSepChars[] = ".";
4957 : // Determine last section
4958 0 : sal_Int32 nLastContent = aStr.getLength();
4959 0 : while ( nLastContent )
4960 : {
4961 0 : nLastContent--;
4962 0 : if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
4963 0 : break;
4964 : }
4965 0 : while ( nLastContent &&
4966 0 : ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
4967 0 : nLastContent--;
4968 :
4969 0 : OUString aLastStr = aStr.copy(nLastContent);
4970 0 : OUString aTempLastStr1( "..." );
4971 0 : aTempLastStr1 += aLastStr;
4972 0 : if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.getLength() ) > nMaxWidth )
4973 0 : aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
4974 : else
4975 : {
4976 0 : sal_Int32 nFirstContent = 0;
4977 0 : while ( nFirstContent < nLastContent )
4978 : {
4979 0 : nFirstContent++;
4980 0 : if ( ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
4981 0 : break;
4982 : }
4983 0 : while ( (nFirstContent < nLastContent) &&
4984 0 : ImplIsCharIn( aStr[ nFirstContent ], pSepChars ) )
4985 0 : nFirstContent++;
4986 : // MEM continue here
4987 0 : if ( nFirstContent >= nLastContent )
4988 0 : aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
4989 : else
4990 : {
4991 0 : if ( nFirstContent > 4 )
4992 0 : nFirstContent = 4;
4993 0 : OUString aFirstStr = aStr.copy( 0, nFirstContent );
4994 0 : aFirstStr += "...";
4995 0 : OUString aTempStr = aFirstStr + aLastStr;
4996 0 : if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
4997 0 : aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
4998 : else
4999 : {
5000 0 : do
5001 : {
5002 0 : aStr = aTempStr;
5003 0 : if( nLastContent > aStr.getLength() )
5004 0 : nLastContent = aStr.getLength();
5005 0 : while ( nFirstContent < nLastContent )
5006 : {
5007 0 : nLastContent--;
5008 0 : if ( ImplIsCharIn( aStr[ nLastContent ], pSepChars ) )
5009 0 : break;
5010 :
5011 : }
5012 0 : while ( (nFirstContent < nLastContent) &&
5013 0 : ImplIsCharIn( aStr[ nLastContent-1 ], pSepChars ) )
5014 0 : nLastContent--;
5015 :
5016 0 : if ( nFirstContent < nLastContent )
5017 : {
5018 0 : OUString aTempLastStr = aStr.copy( nLastContent );
5019 0 : aTempStr = aFirstStr + aTempLastStr;
5020 :
5021 0 : if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.getLength() ) > nMaxWidth )
5022 0 : break;
5023 : }
5024 : }
5025 : while ( nFirstContent < nLastContent );
5026 0 : }
5027 : }
5028 0 : }
5029 : }
5030 : }
5031 :
5032 0 : return aStr;
5033 : }
5034 :
5035 0 : void OutputDevice::DrawCtrlText( const Point& rPos, const OUString& rStr,
5036 : sal_Int32 nIndex, sal_Int32 nLen,
5037 : sal_uInt16 nStyle, MetricVector* pVector, OUString* pDisplayText )
5038 : {
5039 :
5040 0 : if(nLen == 0x0FFFF)
5041 : {
5042 : SAL_INFO("sal.rtl.xub",
5043 : "DrawCtrlText Suspicious arguments nLen:" << nLen);
5044 : }
5045 0 : if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
5046 : {
5047 0 : nLen = rStr.getLength() - nIndex;
5048 : }
5049 :
5050 0 : if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.getLength()) )
5051 0 : return;
5052 :
5053 : // better get graphics here because ImplDrawMnemonicLine() will not
5054 : // we need a graphics
5055 0 : if( !mpGraphics && !ImplGetGraphics() )
5056 0 : return;
5057 0 : if( mbInitClipRegion )
5058 0 : ImplInitClipRegion();
5059 0 : if ( mbOutputClipped )
5060 0 : return;
5061 :
5062 0 : if( nIndex >= rStr.getLength() )
5063 0 : return;
5064 :
5065 0 : if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
5066 : {
5067 0 : nLen = rStr.getLength() - nIndex;
5068 : }
5069 0 : OUString aStr = rStr;
5070 0 : sal_Int32 nMnemonicPos = -1;
5071 :
5072 0 : long nMnemonicX = 0;
5073 0 : long nMnemonicY = 0;
5074 0 : long nMnemonicWidth = 0;
5075 0 : if ( (nStyle & TEXT_DRAW_MNEMONIC) && nLen > 1 )
5076 : {
5077 0 : aStr = GetNonMnemonicString( aStr, nMnemonicPos );
5078 0 : if ( nMnemonicPos != -1 )
5079 : {
5080 0 : if( nMnemonicPos < nIndex )
5081 : {
5082 0 : --nIndex;
5083 : }
5084 : else
5085 : {
5086 0 : if( nMnemonicPos < (nIndex+nLen) )
5087 0 : --nLen;
5088 : DBG_ASSERT( nMnemonicPos < (nIndex+nLen), "Mnemonic underline marker after last character" );
5089 : }
5090 0 : bool bInvalidPos = false;
5091 :
5092 0 : if( nMnemonicPos >= nLen )
5093 : {
5094 : // #106952#
5095 : // may occur in BiDi-Strings: the '~' is sometimes found behind the last char
5096 : // due to some strange BiDi text editors
5097 : // -> place the underline behind the string to indicate a failure
5098 0 : bInvalidPos = true;
5099 0 : nMnemonicPos = nLen-1;
5100 : }
5101 :
5102 0 : sal_Int32* pCaretXArray = (sal_Int32*)alloca( 2 * sizeof(sal_Int32) * nLen );
5103 0 : /*sal_Bool bRet =*/ GetCaretPositions( aStr, pCaretXArray, nIndex, nLen );
5104 0 : long lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ];
5105 0 : long lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ];
5106 0 : nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
5107 :
5108 0 : Point aTempPos( std::min(lc_x1,lc_x2), GetFontMetric().GetAscent() );
5109 0 : if( bInvalidPos ) // #106952#, place behind the (last) character
5110 0 : aTempPos = Point( std::max(lc_x1,lc_x2), GetFontMetric().GetAscent() );
5111 :
5112 0 : aTempPos += rPos;
5113 0 : aTempPos = LogicToPixel( aTempPos );
5114 0 : nMnemonicX = mnOutOffX + aTempPos.X();
5115 0 : nMnemonicY = mnOutOffY + aTempPos.Y();
5116 : }
5117 : }
5118 :
5119 0 : if ( nStyle & TEXT_DRAW_DISABLE && ! pVector )
5120 : {
5121 0 : Color aOldTextColor;
5122 0 : Color aOldTextFillColor;
5123 : bool bRestoreFillColor;
5124 0 : bool bHighContrastBlack = false;
5125 0 : bool bHighContrastWhite = false;
5126 0 : const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
5127 0 : if( rStyleSettings.GetHighContrastMode() )
5128 : {
5129 0 : if( IsBackground() )
5130 : {
5131 0 : Wallpaper aWall = GetBackground();
5132 0 : Color aCol = aWall.GetColor();
5133 0 : bHighContrastBlack = aCol.IsDark();
5134 0 : bHighContrastWhite = aCol.IsBright();
5135 : }
5136 : }
5137 :
5138 0 : aOldTextColor = GetTextColor();
5139 0 : if ( IsTextFillColor() )
5140 : {
5141 0 : bRestoreFillColor = true;
5142 0 : aOldTextFillColor = GetTextFillColor();
5143 : }
5144 : else
5145 0 : bRestoreFillColor = false;
5146 :
5147 0 : if( bHighContrastBlack )
5148 0 : SetTextColor( COL_GREEN );
5149 0 : else if( bHighContrastWhite )
5150 0 : SetTextColor( COL_LIGHTGREEN );
5151 : else
5152 0 : SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
5153 :
5154 0 : DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
5155 0 : if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
5156 : {
5157 0 : if ( nMnemonicPos != -1 )
5158 0 : ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
5159 : }
5160 0 : SetTextColor( aOldTextColor );
5161 0 : if ( bRestoreFillColor )
5162 0 : SetTextFillColor( aOldTextFillColor );
5163 : }
5164 : else
5165 : {
5166 0 : DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
5167 0 : if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
5168 : {
5169 0 : if ( nMnemonicPos != -1 )
5170 0 : ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
5171 : }
5172 : }
5173 :
5174 0 : if( mpAlphaVDev )
5175 0 : mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText );
5176 : }
5177 :
5178 0 : long OutputDevice::GetCtrlTextWidth( const OUString& rStr,
5179 : sal_Int32 nIndex, sal_Int32 nLen,
5180 : sal_uInt16 nStyle ) const
5181 : {
5182 0 : if(nLen == 0x0FFFF)
5183 : {
5184 : SAL_INFO("sal.rtl.xub",
5185 : "GetCtrlTextWidth Suspicious arguments nLen:" << nLen);
5186 : }
5187 : /* defensive code */
5188 0 : if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
5189 : {
5190 0 : nLen = rStr.getLength() - nIndex;
5191 : }
5192 :
5193 0 : if ( nStyle & TEXT_DRAW_MNEMONIC )
5194 : {
5195 : sal_Int32 nMnemonicPos;
5196 0 : OUString aStr = GetNonMnemonicString( rStr, nMnemonicPos );
5197 0 : if ( nMnemonicPos != -1 )
5198 : {
5199 0 : if ( nMnemonicPos < nIndex )
5200 0 : nIndex--;
5201 0 : else if ( (nMnemonicPos >= nIndex) && ((sal_uLong)nMnemonicPos < (sal_uLong)(nIndex+nLen)) )
5202 0 : nLen--;
5203 : }
5204 0 : return GetTextWidth( aStr, nIndex, nLen );
5205 : }
5206 : else
5207 0 : return GetTextWidth( rStr, nIndex, nLen );
5208 : }
5209 :
5210 0 : OUString OutputDevice::GetNonMnemonicString( const OUString& rStr, sal_Int32& rMnemonicPos )
5211 : {
5212 0 : OUString aStr = rStr;
5213 0 : sal_Int32 nLen = aStr.getLength();
5214 0 : sal_Int32 i = 0;
5215 :
5216 0 : rMnemonicPos = -1;
5217 0 : while ( i < nLen )
5218 : {
5219 0 : if ( aStr[ i ] == '~' )
5220 : {
5221 0 : if ( nLen <= i+1 )
5222 0 : break;
5223 :
5224 0 : if ( aStr[ i+1 ] != '~' )
5225 : {
5226 0 : if ( rMnemonicPos == -1 )
5227 0 : rMnemonicPos = i;
5228 0 : aStr = aStr.replaceAt( i, 1, "" );
5229 0 : nLen--;
5230 : }
5231 : else
5232 : {
5233 0 : aStr = aStr.replaceAt( i, 1, "" );
5234 0 : nLen--;
5235 0 : i++;
5236 : }
5237 : }
5238 : else
5239 0 : i++;
5240 : }
5241 :
5242 0 : return aStr;
5243 : }
5244 :
5245 0 : int OutputDevice::GetDevFontCount() const
5246 : {
5247 :
5248 0 : if( !mpGetDevFontList )
5249 0 : mpGetDevFontList = mpFontCollection->GetDevFontList();
5250 0 : return mpGetDevFontList->Count();
5251 : }
5252 :
5253 0 : FontInfo OutputDevice::GetDevFont( int nDevFontIndex ) const
5254 : {
5255 :
5256 0 : FontInfo aFontInfo;
5257 :
5258 0 : ImplInitFontList();
5259 :
5260 0 : int nCount = GetDevFontCount();
5261 0 : if( nDevFontIndex < nCount )
5262 : {
5263 0 : const PhysicalFontFace& rData = *mpGetDevFontList->Get( nDevFontIndex );
5264 0 : aFontInfo.SetName( rData.GetFamilyName() );
5265 0 : aFontInfo.SetStyleName( rData.GetStyleName() );
5266 0 : aFontInfo.SetCharSet( rData.IsSymbolFont() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
5267 0 : aFontInfo.SetFamily( rData.GetFamilyType() );
5268 0 : aFontInfo.SetPitch( rData.GetPitch() );
5269 0 : aFontInfo.SetWeight( rData.GetWeight() );
5270 0 : aFontInfo.SetItalic( rData.GetSlant() );
5271 0 : aFontInfo.SetWidthType( rData.GetWidthType() );
5272 0 : if( rData.IsScalable() )
5273 0 : aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
5274 0 : if( rData.mbDevice )
5275 0 : aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
5276 : }
5277 :
5278 0 : return aFontInfo;
5279 : }
5280 :
5281 0 : bool OutputDevice::AddTempDevFont( const OUString& rFileURL, const OUString& rFontName )
5282 : {
5283 :
5284 0 : ImplInitFontList();
5285 :
5286 0 : if( !mpGraphics && !ImplGetGraphics() )
5287 0 : return false;
5288 :
5289 0 : bool bRC = mpGraphics->AddTempDevFont( mpFontCollection, rFileURL, rFontName );
5290 0 : if( !bRC )
5291 0 : return false;
5292 :
5293 0 : if( mpAlphaVDev )
5294 0 : mpAlphaVDev->AddTempDevFont( rFileURL, rFontName );
5295 :
5296 0 : mpFontCache->Invalidate();
5297 0 : return true;
5298 : }
5299 :
5300 0 : int OutputDevice::GetDevFontSizeCount( const Font& rFont ) const
5301 : {
5302 :
5303 0 : delete mpGetDevSizeList;
5304 :
5305 0 : ImplInitFontList();
5306 0 : mpGetDevSizeList = mpFontCollection->GetDevSizeList( rFont.GetName() );
5307 0 : return mpGetDevSizeList->Count();
5308 : }
5309 :
5310 0 : Size OutputDevice::GetDevFontSize( const Font& rFont, int nSizeIndex ) const
5311 : {
5312 :
5313 : // check range
5314 0 : int nCount = GetDevFontSizeCount( rFont );
5315 0 : if ( nSizeIndex >= nCount )
5316 0 : return Size();
5317 :
5318 : // when mapping is enabled round to .5 points
5319 0 : Size aSize( 0, mpGetDevSizeList->Get( nSizeIndex ) );
5320 0 : if ( mbMap )
5321 : {
5322 0 : aSize.Height() *= 10;
5323 0 : MapMode aMap( MAP_10TH_INCH, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) );
5324 0 : aSize = PixelToLogic( aSize, aMap );
5325 0 : aSize.Height() += 5;
5326 0 : aSize.Height() /= 10;
5327 0 : long nRound = aSize.Height() % 5;
5328 0 : if ( nRound >= 3 )
5329 0 : aSize.Height() += (5-nRound);
5330 : else
5331 0 : aSize.Height() -= nRound;
5332 0 : aSize.Height() *= 10;
5333 0 : aSize = LogicToPixel( aSize, aMap );
5334 0 : aSize = PixelToLogic( aSize );
5335 0 : aSize.Height() += 5;
5336 0 : aSize.Height() /= 10;
5337 : }
5338 0 : return aSize;
5339 : }
5340 :
5341 0 : bool OutputDevice::IsFontAvailable( const OUString& rFontName ) const
5342 : {
5343 :
5344 0 : PhysicalFontFamily* pFound = mpFontCollection->FindFontFamily( rFontName );
5345 0 : return (pFound != NULL);
5346 : }
5347 :
5348 0 : FontMetric OutputDevice::GetFontMetric() const
5349 : {
5350 :
5351 0 : FontMetric aMetric;
5352 0 : if( mbNewFont && !ImplNewFont() )
5353 0 : return aMetric;
5354 :
5355 0 : ImplFontEntry* pEntry = mpFontEntry;
5356 0 : ImplFontMetricData* pMetric = &(pEntry->maMetric);
5357 :
5358 : // prepare metric
5359 0 : aMetric.Font::operator=( maFont );
5360 :
5361 : // set aMetric with info from font
5362 0 : aMetric.SetName( maFont.GetName() );
5363 0 : aMetric.SetStyleName( pMetric->GetStyleName() );
5364 0 : aMetric.SetSize( PixelToLogic( Size( pMetric->mnWidth, pMetric->mnAscent+pMetric->mnDescent-pMetric->mnIntLeading ) ) );
5365 0 : aMetric.SetCharSet( pMetric->IsSymbolFont() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
5366 0 : aMetric.SetFamily( pMetric->GetFamilyType() );
5367 0 : aMetric.SetPitch( pMetric->GetPitch() );
5368 0 : aMetric.SetWeight( pMetric->GetWeight() );
5369 0 : aMetric.SetItalic( pMetric->GetSlant() );
5370 0 : aMetric.SetWidthType( pMetric->GetWidthType() );
5371 0 : if ( pEntry->mnOwnOrientation )
5372 0 : aMetric.SetOrientation( pEntry->mnOwnOrientation );
5373 : else
5374 0 : aMetric.SetOrientation( pMetric->mnOrientation );
5375 0 : if( !pEntry->maMetric.mbKernableFont )
5376 0 : aMetric.SetKerning( maFont.GetKerning() & ~KERNING_FONTSPECIFIC );
5377 :
5378 : // set remaining metric fields
5379 0 : aMetric.mpImplMetric->mnMiscFlags = 0;
5380 0 : if( pMetric->mbDevice )
5381 0 : aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
5382 0 : if( pMetric->mbScalableFont )
5383 0 : aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
5384 0 : aMetric.mpImplMetric->mnAscent = ImplDevicePixelToLogicHeight( pMetric->mnAscent+mnEmphasisAscent );
5385 0 : aMetric.mpImplMetric->mnDescent = ImplDevicePixelToLogicHeight( pMetric->mnDescent+mnEmphasisDescent );
5386 0 : aMetric.mpImplMetric->mnIntLeading = ImplDevicePixelToLogicHeight( pMetric->mnIntLeading+mnEmphasisAscent );
5387 0 : aMetric.mpImplMetric->mnExtLeading = ImplDevicePixelToLogicHeight( pMetric->mnExtLeading );
5388 0 : aMetric.mpImplMetric->mnLineHeight = ImplDevicePixelToLogicHeight( pMetric->mnAscent+pMetric->mnDescent+mnEmphasisAscent+mnEmphasisDescent );
5389 0 : aMetric.mpImplMetric->mnSlant = ImplDevicePixelToLogicHeight( pMetric->mnSlant );
5390 :
5391 : #ifdef UNX
5392 : // backwards compatible line metrics after fixing #i60945#
5393 0 : if( (meOutDevType == OUTDEV_VIRDEV)
5394 0 : && static_cast<const VirtualDevice*>(this)->ForceZeroExtleadBug() )
5395 0 : aMetric.mpImplMetric->mnExtLeading = 0;
5396 : #endif
5397 :
5398 0 : return aMetric;
5399 : }
5400 :
5401 0 : FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const
5402 : {
5403 : // select font, query metrics, select original font again
5404 0 : Font aOldFont = GetFont();
5405 0 : const_cast<OutputDevice*>(this)->SetFont( rFont );
5406 0 : FontMetric aMetric( GetFontMetric() );
5407 0 : const_cast<OutputDevice*>(this)->SetFont( aOldFont );
5408 0 : return aMetric;
5409 : }
5410 :
5411 : /** OutputDevice::GetSysFontData
5412 : *
5413 : * @param nFallbacklevel Fallback font level (0 = best matching font)
5414 : *
5415 : * Retrieve detailed font information in platform independent structure
5416 : *
5417 : * @return SystemFontData
5418 : **/
5419 0 : SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const
5420 : {
5421 0 : SystemFontData aSysFontData;
5422 0 : aSysFontData.nSize = sizeof(aSysFontData);
5423 :
5424 0 : if (!mpGraphics) ImplGetGraphics();
5425 0 : if (mpGraphics) aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel);
5426 :
5427 0 : return aSysFontData;
5428 : }
5429 :
5430 : /** OutputDevice::GetSysTextLayoutData
5431 : *
5432 : * @param rStartPt Start point of the text
5433 : * @param rStr Text string that will be transformed into layout of glyphs
5434 : * @param nIndex Position in the string from where layout will be done
5435 : * @param nLen Length of the string
5436 : * @param pDXAry Custom layout adjustment data
5437 : *
5438 : * Export finalized glyph layout data as platform independent SystemTextLayoutData
5439 : * (see vcl/inc/vcl/sysdata.hxx)
5440 : *
5441 : * Only parameters rStartPt and rStr are mandatory, the rest is optional
5442 : * (default values will be used)
5443 : *
5444 : * @return SystemTextLayoutData
5445 : **/
5446 0 : SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen,
5447 : const sal_Int32* pDXAry) const
5448 : {
5449 0 : if(nLen == 0x0FFFF)
5450 : {
5451 : SAL_INFO("sal.rtl.xub",
5452 : "GetSysTextLayoutData Suspicious arguments nLen:" << nLen);
5453 : }
5454 0 : if( (nLen < 0) || (nIndex + nLen >= rStr.getLength()))
5455 : {
5456 0 : nLen = rStr.getLength() - nIndex;
5457 : }
5458 :
5459 0 : SystemTextLayoutData aSysLayoutData;
5460 0 : aSysLayoutData.nSize = sizeof(aSysLayoutData);
5461 0 : aSysLayoutData.rGlyphData.reserve( 256 );
5462 0 : aSysLayoutData.orientation = 0;
5463 :
5464 0 : if ( mpMetaFile )
5465 : {
5466 0 : if (pDXAry)
5467 0 : mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
5468 : else
5469 0 : mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
5470 : }
5471 :
5472 0 : if ( !IsDeviceOutputNecessary() ) return aSysLayoutData;
5473 :
5474 0 : SalLayout* pLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, pDXAry);
5475 :
5476 0 : if ( !pLayout ) return aSysLayoutData;
5477 :
5478 : // setup glyphs
5479 0 : Point aPos;
5480 : sal_GlyphId aGlyphId;
5481 0 : for( int nStart = 0; pLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); )
5482 : {
5483 : // NOTE: Windows backend is producing unicode chars (ucs4), so on windows,
5484 : // ETO_GLYPH_INDEX is unusable, unless extra glyph conversion is made.
5485 :
5486 : SystemGlyphData aGlyph;
5487 0 : aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK);
5488 0 : aGlyph.x = aPos.X();
5489 0 : aGlyph.y = aPos.Y();
5490 0 : int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
5491 0 : aGlyph.fallbacklevel = nLevel < MAX_FALLBACK ? nLevel : 0;
5492 0 : aSysLayoutData.rGlyphData.push_back(aGlyph);
5493 : }
5494 :
5495 : // Get font data
5496 0 : aSysLayoutData.orientation = pLayout->GetOrientation();
5497 :
5498 0 : pLayout->Release();
5499 :
5500 0 : return aSysLayoutData;
5501 : }
5502 :
5503 0 : long OutputDevice::GetMinKashida() const
5504 : {
5505 0 : if( mbNewFont && !ImplNewFont() )
5506 0 : return 0;
5507 :
5508 0 : ImplFontEntry* pEntry = mpFontEntry;
5509 0 : ImplFontMetricData* pMetric = &(pEntry->maMetric);
5510 0 : return ImplDevicePixelToLogicWidth( pMetric->mnMinKashida );
5511 : }
5512 :
5513 0 : sal_Int32 OutputDevice::ValidateKashidas ( const OUString& rTxt,
5514 : sal_Int32 nIdx, sal_Int32 nLen,
5515 : sal_Int32 nKashCount,
5516 : const sal_Int32* pKashidaPos,
5517 : sal_Int32* pKashidaPosDropped ) const
5518 : {
5519 : // do layout
5520 0 : SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen );
5521 0 : if( !pSalLayout )
5522 0 : return 0;
5523 0 : sal_Int32 nDropped = 0;
5524 0 : for( int i = 0; i < nKashCount; ++i )
5525 : {
5526 0 : if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] ))
5527 : {
5528 0 : pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ];
5529 0 : ++nDropped;
5530 : }
5531 : }
5532 0 : pSalLayout->Release();
5533 0 : return nDropped;
5534 : }
5535 :
5536 0 : bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr,
5537 : int nIndex, int nLen, int nBase, MetricVector& rVector )
5538 : {
5539 :
5540 0 : rVector.clear();
5541 :
5542 0 : if(nLen == 0x0FFFF)
5543 : {
5544 : SAL_INFO("sal.rtl.xub",
5545 : "GetGlyphBoundRects Suspicious arguments nLen:" << nLen);
5546 : }
5547 :
5548 0 : if( nIndex >= rStr.getLength() )
5549 0 : return false;
5550 :
5551 0 : if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
5552 : {
5553 0 : nLen = rStr.getLength() - nIndex;
5554 : }
5555 :
5556 0 : Rectangle aRect;
5557 0 : for( int i = 0; i < nLen; i++ )
5558 : {
5559 0 : if( !GetTextBoundRect( aRect, rStr, nBase, nIndex + i, 1 ) )
5560 0 : break;
5561 0 : aRect.Move( rOrigin.X(), rOrigin.Y() );
5562 0 : rVector.push_back( aRect );
5563 : }
5564 :
5565 0 : return (nLen == (int)rVector.size());
5566 : }
5567 :
5568 0 : bool OutputDevice::GetTextBoundRect( Rectangle& rRect,
5569 : const OUString& rStr, sal_Int32 nBase,
5570 : sal_Int32 nIndex, sal_Int32 nLen,
5571 : sal_uLong nLayoutWidth, const sal_Int32* pDXAry ) const
5572 : {
5573 0 : if(nLen == 0x0FFFF)
5574 : {
5575 : SAL_INFO("sal.rtl.xub",
5576 : "GetTextBoundRect Suspicious arguments nLen:" << nLen);
5577 : }
5578 :
5579 0 : bool bRet = false;
5580 0 : rRect.SetEmpty();
5581 :
5582 0 : SalLayout* pSalLayout = NULL;
5583 0 : const Point aPoint;
5584 : // calculate offset when nBase!=nIndex
5585 0 : long nXOffset = 0;
5586 0 : if( nBase != nIndex )
5587 : {
5588 0 : sal_Int32 nStart = std::min( nBase, nIndex );
5589 0 : sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
5590 0 : pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry );
5591 0 : if( pSalLayout )
5592 : {
5593 0 : nXOffset = pSalLayout->GetTextWidth();
5594 0 : nXOffset /= pSalLayout->GetUnitsPerPixel();
5595 0 : pSalLayout->Release();
5596 : // TODO: fix offset calculation for Bidi case
5597 0 : if( nBase < nIndex)
5598 0 : nXOffset = -nXOffset;
5599 : }
5600 : }
5601 :
5602 0 : pSalLayout = ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
5603 0 : Rectangle aPixelRect;
5604 0 : if( pSalLayout )
5605 : {
5606 0 : bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect );
5607 :
5608 0 : if( bRet )
5609 : {
5610 0 : int nWidthFactor = pSalLayout->GetUnitsPerPixel();
5611 :
5612 0 : if( nWidthFactor > 1 )
5613 : {
5614 0 : double fFactor = 1.0 / nWidthFactor;
5615 0 : aPixelRect.Left()
5616 0 : = static_cast< long >(aPixelRect.Left() * fFactor);
5617 0 : aPixelRect.Right()
5618 0 : = static_cast< long >(aPixelRect.Right() * fFactor);
5619 0 : aPixelRect.Top()
5620 0 : = static_cast< long >(aPixelRect.Top() * fFactor);
5621 0 : aPixelRect.Bottom()
5622 0 : = static_cast< long >(aPixelRect.Bottom() * fFactor);
5623 : }
5624 :
5625 0 : Point aRotatedOfs( mnTextOffX, mnTextOffY );
5626 0 : aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
5627 0 : aPixelRect += aRotatedOfs;
5628 0 : rRect = PixelToLogic( aPixelRect );
5629 0 : if( mbMap )
5630 0 : rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
5631 : }
5632 :
5633 0 : pSalLayout->Release();
5634 : }
5635 :
5636 0 : if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
5637 0 : return bRet;
5638 :
5639 : // fall back to bitmap method to get the bounding rectangle,
5640 : // so we need a monochrome virtual device with matching font
5641 0 : VirtualDevice aVDev( 1 );
5642 0 : Font aFont( GetFont() );
5643 0 : aFont.SetShadow( false );
5644 0 : aFont.SetOutline( false );
5645 0 : aFont.SetRelief( RELIEF_NONE );
5646 0 : aFont.SetOrientation( 0 );
5647 0 : aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
5648 0 : aVDev.SetFont( aFont );
5649 0 : aVDev.SetTextAlign( ALIGN_TOP );
5650 :
5651 : // layout the text on the virtual device
5652 0 : pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
5653 0 : if( !pSalLayout )
5654 0 : return false;
5655 :
5656 : // make the bitmap big enough
5657 : // TODO: use factors when it would get too big
5658 0 : long nWidth = pSalLayout->GetTextWidth();
5659 0 : long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
5660 0 : Point aOffset( nWidth/2, 8 );
5661 0 : Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() );
5662 0 : if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) )
5663 0 : return false;
5664 :
5665 : // draw text in black
5666 0 : pSalLayout->DrawBase() = aOffset;
5667 0 : aVDev.SetTextColor( Color( COL_BLACK ) );
5668 0 : aVDev.SetTextFillColor();
5669 0 : aVDev.ImplInitTextColor();
5670 0 : aVDev.ImplDrawText( *pSalLayout );
5671 0 : pSalLayout->Release();
5672 :
5673 : // find extents using the bitmap
5674 0 : Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize );
5675 0 : BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
5676 0 : if( !pAcc )
5677 0 : return false;
5678 0 : const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
5679 0 : const long nW = pAcc->Width();
5680 0 : const long nH = pAcc->Height();
5681 0 : long nLeft = 0;
5682 0 : long nRight = 0;
5683 :
5684 : // find top left point
5685 0 : long nTop = 0;
5686 0 : for(; nTop < nH; ++nTop )
5687 : {
5688 0 : for( nLeft = 0; nLeft < nW; ++nLeft )
5689 0 : if( pAcc->GetPixel( nTop, nLeft ) == aBlack )
5690 0 : break;
5691 0 : if( nLeft < nW )
5692 0 : break;
5693 : }
5694 :
5695 : // find bottom right point
5696 0 : long nBottom = nH;
5697 0 : while( --nBottom >= nTop )
5698 : {
5699 0 : for( nRight = nW; --nRight >= 0; )
5700 0 : if( pAcc->GetPixel( nBottom, nRight ) == aBlack )
5701 0 : break;
5702 0 : if( nRight >= 0 )
5703 0 : break;
5704 : }
5705 0 : if( nRight < nLeft )
5706 : {
5707 0 : long nX = nRight;
5708 0 : nRight = nLeft;
5709 0 : nLeft = nX;
5710 : }
5711 :
5712 0 : for( long nY = nTop; nY <= nBottom; ++nY )
5713 : {
5714 : // find leftmost point
5715 : long nX;
5716 0 : for( nX = 0; nX < nLeft; ++nX )
5717 0 : if( pAcc->GetPixel( nY, nX ) == aBlack )
5718 0 : break;
5719 0 : nLeft = nX;
5720 :
5721 : // find rightmost point
5722 0 : for( nX = nW; --nX > nRight; )
5723 0 : if( pAcc->GetPixel( nY, nX ) == aBlack )
5724 0 : break;
5725 0 : nRight = nX;
5726 : }
5727 :
5728 0 : aBmp.ReleaseAccess( pAcc );
5729 :
5730 0 : if( nTop <= nBottom )
5731 : {
5732 0 : Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 );
5733 0 : Point aTopLeft( nLeft, nTop );
5734 0 : aTopLeft -= aOffset;
5735 : // adjust to text alignment
5736 0 : aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent);
5737 : // convert to logical coordinates
5738 0 : aSize = PixelToLogic( aSize );
5739 0 : aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() );
5740 0 : aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() );
5741 0 : rRect = Rectangle( aTopLeft, aSize );
5742 0 : return true;
5743 : }
5744 :
5745 0 : return false;
5746 : }
5747 :
5748 0 : bool OutputDevice::GetTextOutlines( ::basegfx::B2DPolyPolygonVector& rVector,
5749 : const OUString& rStr, sal_Int32 nBase,
5750 : sal_Int32 nIndex, sal_Int32 nLen,
5751 : bool bOptimize, sal_uLong nLayoutWidth, const sal_Int32* pDXArray ) const
5752 : {
5753 0 : if(nLen == 0x0FFFF)
5754 : {
5755 : SAL_INFO("sal.rtl.xub",
5756 : "GetTextOutlines Suspicious arguments nLen:" << nLen);
5757 : }
5758 : // the fonts need to be initialized
5759 0 : if( mbNewFont )
5760 0 : ImplNewFont();
5761 0 : if( mbInitFont )
5762 0 : ImplInitFont();
5763 0 : if( !mpFontEntry )
5764 0 : return false;
5765 :
5766 0 : bool bRet = false;
5767 0 : rVector.clear();
5768 0 : if( nLen < 0 )
5769 : {
5770 0 : nLen = rStr.getLength() - nIndex;
5771 : }
5772 0 : rVector.reserve( nLen );
5773 :
5774 : // we want to get the Rectangle in logical units, so to
5775 : // avoid rounding errors we just size the font in logical units
5776 0 : bool bOldMap = mbMap;
5777 0 : if( bOldMap )
5778 : {
5779 0 : const_cast<OutputDevice&>(*this).mbMap = false;
5780 0 : const_cast<OutputDevice&>(*this).mbNewFont = true;
5781 : }
5782 :
5783 0 : SalLayout* pSalLayout = NULL;
5784 :
5785 : // calculate offset when nBase!=nIndex
5786 0 : long nXOffset = 0;
5787 0 : if( nBase != nIndex )
5788 : {
5789 0 : sal_Int32 nStart = std::min( nBase, nIndex );
5790 0 : sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart;
5791 0 : pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nLayoutWidth, pDXArray );
5792 0 : if( pSalLayout )
5793 : {
5794 0 : nXOffset = pSalLayout->GetTextWidth();
5795 0 : pSalLayout->Release();
5796 : // TODO: fix offset calculation for Bidi case
5797 0 : if( nBase > nIndex)
5798 0 : nXOffset = -nXOffset;
5799 : }
5800 : }
5801 :
5802 0 : pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray );
5803 0 : if( pSalLayout )
5804 : {
5805 0 : bRet = pSalLayout->GetOutline( *mpGraphics, rVector );
5806 0 : if( bRet )
5807 : {
5808 : // transform polygon to pixel units
5809 0 : ::basegfx::B2DHomMatrix aMatrix;
5810 :
5811 0 : int nWidthFactor = pSalLayout->GetUnitsPerPixel();
5812 0 : if( nXOffset | mnTextOffX | mnTextOffY )
5813 : {
5814 0 : Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor );
5815 0 : aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
5816 0 : aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() );
5817 : }
5818 :
5819 0 : if( nWidthFactor > 1 )
5820 : {
5821 0 : double fFactor = 1.0 / nWidthFactor;
5822 0 : aMatrix.scale( fFactor, fFactor );
5823 : }
5824 :
5825 0 : if( !aMatrix.isIdentity() )
5826 : {
5827 0 : ::basegfx::B2DPolyPolygonVector::iterator aIt = rVector.begin();
5828 0 : for(; aIt != rVector.end(); ++aIt )
5829 0 : (*aIt).transform( aMatrix );
5830 0 : }
5831 : }
5832 :
5833 0 : pSalLayout->Release();
5834 : }
5835 :
5836 0 : if( bOldMap )
5837 : {
5838 : // restore original font size and map mode
5839 0 : const_cast<OutputDevice&>(*this).mbMap = bOldMap;
5840 0 : const_cast<OutputDevice&>(*this).mbNewFont = true;
5841 : }
5842 :
5843 0 : if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
5844 0 : return bRet;
5845 :
5846 : // fall back to bitmap conversion
5847 : // Here, we can savely assume that the mapping between characters and glyphs
5848 : // is one-to-one. This is most probably valid for the old bitmap fonts.
5849 : // fall back to bitmap method to get the bounding rectangle,
5850 : // so we need a monochrome virtual device with matching font
5851 0 : pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray );
5852 0 : if (pSalLayout == 0)
5853 0 : return false;
5854 0 : long nOrgWidth = pSalLayout->GetTextWidth();
5855 0 : long nOrgHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent
5856 0 : + mnEmphasisDescent;
5857 0 : pSalLayout->Release();
5858 :
5859 0 : VirtualDevice aVDev(1);
5860 :
5861 0 : Font aFont(GetFont());
5862 0 : aFont.SetShadow(false);
5863 0 : aFont.SetOutline(false);
5864 0 : aFont.SetRelief(RELIEF_NONE);
5865 0 : aFont.SetOrientation(0);
5866 0 : if( bOptimize )
5867 : {
5868 0 : aFont.SetSize( Size( 0, GLYPH_FONT_HEIGHT ) );
5869 0 : aVDev.SetMapMode( MAP_PIXEL );
5870 : }
5871 0 : aVDev.SetFont( aFont );
5872 0 : aVDev.SetTextAlign( ALIGN_TOP );
5873 0 : aVDev.SetTextColor( Color(COL_BLACK) );
5874 0 : aVDev.SetTextFillColor();
5875 :
5876 0 : pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray );
5877 0 : if (pSalLayout == 0)
5878 0 : return false;
5879 0 : long nWidth = pSalLayout->GetTextWidth();
5880 0 : long nHeight = ((OutputDevice*)&aVDev)->mpFontEntry->mnLineHeight + ((OutputDevice*)&aVDev)->mnEmphasisAscent
5881 0 : + ((OutputDevice*)&aVDev)->mnEmphasisDescent;
5882 0 : pSalLayout->Release();
5883 :
5884 0 : if( !nWidth || !nHeight )
5885 0 : return true;
5886 0 : double fScaleX = static_cast< double >(nOrgWidth) / nWidth;
5887 0 : double fScaleY = static_cast< double >(nOrgHeight) / nHeight;
5888 :
5889 : // calculate offset when nBase!=nIndex
5890 : // TODO: fix offset calculation for Bidi case
5891 0 : nXOffset = 0;
5892 0 : if( nBase != nIndex )
5893 : {
5894 0 : sal_Int32 nStart = ((nBase < nIndex) ? nBase : nIndex);
5895 0 : sal_Int32 nLength = ((nBase > nIndex) ? nBase : nIndex) - nStart;
5896 0 : pSalLayout = aVDev.ImplLayout( rStr, nStart, nLength, Point(0,0), nLayoutWidth, pDXArray );
5897 0 : if( pSalLayout )
5898 : {
5899 0 : nXOffset = pSalLayout->GetTextWidth();
5900 0 : pSalLayout->Release();
5901 0 : if( nBase > nIndex)
5902 0 : nXOffset = -nXOffset;
5903 : }
5904 : }
5905 :
5906 0 : bRet = true;
5907 0 : bool bRTL = false;
5908 0 : OUString aStr( rStr ); // prepare for e.g. localized digits
5909 0 : sal_Int32 nIndex2 = nIndex; // only needed until nIndex is sal_Int32
5910 0 : sal_Int32 nLen2 = nLen; // only needed until nLen is sal_Int32
5911 0 : ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nIndex2, nLen2, 0, NULL );
5912 0 : for( int nCharPos = -1; aLayoutArgs.GetNextPos( &nCharPos, &bRTL);)
5913 : {
5914 0 : bool bSuccess = false;
5915 :
5916 : // draw character into virtual device
5917 0 : pSalLayout = aVDev.ImplLayout( rStr, nCharPos, 1, Point(0,0), nLayoutWidth, pDXArray );
5918 0 : if (pSalLayout == 0)
5919 0 : return false;
5920 0 : long nCharWidth = pSalLayout->GetTextWidth();
5921 :
5922 0 : Point aOffset(nCharWidth / 2, 8);
5923 0 : Size aSize(nCharWidth + 2 * aOffset.X(), nHeight + 2 * aOffset.Y());
5924 0 : bSuccess = (bool)aVDev.SetOutputSizePixel(aSize);
5925 0 : if( bSuccess )
5926 : {
5927 : // draw glyph into virtual device
5928 0 : aVDev.Erase();
5929 0 : pSalLayout->DrawBase() += aOffset;
5930 0 : pSalLayout->DrawBase() += Point( ((OutputDevice*)&aVDev)->mnTextOffX, ((OutputDevice*)&aVDev)->mnTextOffY );
5931 0 : pSalLayout->DrawText( *((OutputDevice*)&aVDev)->mpGraphics );
5932 0 : pSalLayout->Release();
5933 :
5934 : // convert character image into outline
5935 0 : Bitmap aBmp( aVDev.GetBitmap(Point(0, 0), aSize));
5936 :
5937 0 : PolyPolygon aPolyPoly;
5938 0 : bool bVectorized = aBmp.Vectorize(aPolyPoly, BMP_VECTORIZE_OUTER | BMP_VECTORIZE_REDUCE_EDGES);
5939 0 : if( !bVectorized )
5940 0 : bSuccess = false;
5941 : else
5942 : {
5943 : // convert units to logical width
5944 0 : for (sal_uInt16 j = 0; j < aPolyPoly.Count(); ++j)
5945 : {
5946 0 : Polygon& rPoly = aPolyPoly[j];
5947 0 : for (sal_uInt16 k = 0; k < rPoly.GetSize(); ++k)
5948 : {
5949 0 : Point& rPt = rPoly[k];
5950 0 : rPt -= aOffset;
5951 0 : int nPixelX = rPt.X() - ((OutputDevice&)aVDev).mnTextOffX + nXOffset;
5952 0 : int nPixelY = rPt.Y() - ((OutputDevice&)aVDev).mnTextOffY;
5953 0 : rPt.X() = ImplDevicePixelToLogicWidth( nPixelX );
5954 0 : rPt.Y() = ImplDevicePixelToLogicHeight( nPixelY );
5955 : }
5956 : }
5957 :
5958 : // ignore "empty" glyphs:
5959 0 : if( aPolyPoly.Count() > 0 )
5960 : {
5961 : // convert to B2DPolyPolygon
5962 : // TODO: get rid of intermediate tool's PolyPolygon
5963 0 : ::basegfx::B2DPolyPolygon aB2DPolyPoly = aPolyPoly.getB2DPolyPolygon();
5964 0 : ::basegfx::B2DHomMatrix aMatrix;
5965 0 : aMatrix.scale( fScaleX, fScaleY );
5966 0 : int nAngle = GetFont().GetOrientation();
5967 0 : if( nAngle )
5968 0 : aMatrix.rotate( nAngle * F_PI1800 );
5969 0 : aB2DPolyPoly.transform( aMatrix );
5970 0 : rVector.push_back( aB2DPolyPoly );
5971 : }
5972 0 : }
5973 : }
5974 :
5975 0 : nXOffset += nCharWidth;
5976 0 : bRet = bRet && bSuccess;
5977 : }
5978 :
5979 0 : return bRet;
5980 : }
5981 :
5982 0 : bool OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector,
5983 : const OUString& rStr, sal_Int32 nBase,
5984 : sal_Int32 nIndex, sal_Int32 nLen, bool bOptimize,
5985 : sal_uLong nTWidth, const sal_Int32* pDXArray ) const
5986 : {
5987 0 : if(nLen == 0x0FFFF)
5988 : {
5989 : SAL_INFO("sal.rtl.xub",
5990 : "GetTextOutlines Suspicious arguments nLen:" << nLen);
5991 : }
5992 :
5993 0 : rResultVector.clear();
5994 :
5995 : // get the basegfx polypolygon vector
5996 0 : ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
5997 0 : if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
5998 0 : bOptimize, nTWidth, pDXArray ) )
5999 0 : return false;
6000 :
6001 : // convert to a tool polypolygon vector
6002 0 : rResultVector.reserve( aB2DPolyPolyVector.size() );
6003 0 : ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
6004 0 : for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
6005 0 : rResultVector.push_back(PolyPolygon(*aIt)); // #i76339#
6006 :
6007 0 : return true;
6008 : }
6009 :
6010 0 : bool OutputDevice::GetTextOutline( PolyPolygon& rPolyPoly, const OUString& rStr,
6011 : sal_Int32 nBase, sal_Int32 nIndex, sal_Int32 nLen,
6012 : bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const
6013 : {
6014 0 : if(nLen == 0x0FFFF)
6015 : {
6016 : SAL_INFO("sal.rtl.xub",
6017 : "GetTextOutline Suspicious arguments nLen:" << nLen);
6018 : }
6019 0 : rPolyPoly.Clear();
6020 :
6021 : // get the basegfx polypolygon vector
6022 0 : ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
6023 0 : if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
6024 0 : bOptimize, nTWidth, pDXArray ) )
6025 0 : return false;
6026 :
6027 : // convert and merge into a tool polypolygon
6028 0 : ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
6029 0 : for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
6030 0 : for( unsigned int i = 0; i < aIt->count(); ++i )
6031 0 : rPolyPoly.Insert(Polygon((*aIt).getB2DPolygon( i ))); // #i76339#
6032 :
6033 0 : return true;
6034 : }
6035 :
6036 0 : bool OutputDevice::GetFontCapabilities( FontCapabilities& rFontCapabilities ) const
6037 : {
6038 : // we need a graphics
6039 0 : if( !mpGraphics && !ImplGetGraphics() )
6040 0 : return false;
6041 :
6042 0 : if( mbNewFont )
6043 0 : ImplNewFont();
6044 0 : if( mbInitFont )
6045 0 : ImplInitFont();
6046 0 : if( !mpFontEntry )
6047 0 : return false;
6048 :
6049 0 : return mpGraphics->GetImplFontCapabilities(rFontCapabilities);
6050 : }
6051 :
6052 0 : bool OutputDevice::GetFontCharMap( FontCharMap& rFontCharMap ) const
6053 : {
6054 0 : rFontCharMap.Reset();
6055 :
6056 : // we need a graphics
6057 0 : if( !mpGraphics && !ImplGetGraphics() )
6058 0 : return false;
6059 :
6060 0 : if( mbNewFont )
6061 0 : ImplNewFont();
6062 0 : if( mbInitFont )
6063 0 : ImplInitFont();
6064 0 : if( !mpFontEntry )
6065 0 : return false;
6066 :
6067 : #ifdef ENABLE_IFC_CACHE // a little font charmap cache helps considerably
6068 : static const int NMAXITEMS = 16;
6069 : static int nUsedItems = 0, nCurItem = 0;
6070 :
6071 : struct CharMapCacheItem { const PhysicalFontFace* mpFontData; FontCharMap maCharMap; };
6072 : static CharMapCacheItem aCache[ NMAXITEMS ];
6073 :
6074 : const PhysicalFontFace* pFontData = mpFontEntry->maFontSelData.mpFontData;
6075 :
6076 : int i;
6077 : for( i = nUsedItems; --i >= 0; )
6078 : if( pFontData == aCache[i].mpFontData )
6079 : break;
6080 : if( i >= 0 ) // found in cache
6081 : {
6082 : rFontCharMap.Reset( aCache[i].maCharMap.mpImpl );
6083 : }
6084 : else // need to cache
6085 : #endif // ENABLE_IFC_CACHE
6086 : {
6087 0 : const ImplFontCharMap* pNewMap = mpGraphics->GetImplFontCharMap();
6088 0 : rFontCharMap.Reset( pNewMap );
6089 :
6090 : #ifdef ENABLE_IFC_CACHE
6091 : // manage cache round-robin and insert data
6092 : CharMapCacheItem& rItem = aCache[ nCurItem ];
6093 : rItem.mpFontData = pFontData;
6094 : rItem.maCharMap.Reset( pNewMap );
6095 :
6096 : if( ++nCurItem >= NMAXITEMS )
6097 : nCurItem = 0;
6098 :
6099 : if( ++nUsedItems >= NMAXITEMS )
6100 : nUsedItems = NMAXITEMS;
6101 : #endif // ENABLE_IFC_CACHE
6102 : }
6103 :
6104 0 : if( rFontCharMap.IsDefaultMap() )
6105 0 : return false;
6106 0 : return true;
6107 : }
6108 :
6109 0 : sal_Int32 OutputDevice::HasGlyphs( const Font& rTempFont, const OUString& rStr,
6110 : sal_Int32 nIndex, sal_Int32 nLen ) const
6111 : {
6112 0 : if( nIndex >= rStr.getLength() )
6113 0 : return nIndex;
6114 : sal_Int32 nEnd;
6115 0 : if( nLen == -1 )
6116 0 : nEnd = rStr.getLength();
6117 : else
6118 0 : nEnd = std::min( rStr.getLength(), nIndex + nLen );
6119 :
6120 : DBG_ASSERT( nIndex < nEnd, "StartPos >= EndPos?" );
6121 : DBG_ASSERT( nEnd <= rStr.getLength(), "String too short" );
6122 :
6123 : // to get the map temporarily set font
6124 0 : const Font aOrigFont = GetFont();
6125 0 : const_cast<OutputDevice&>(*this).SetFont( rTempFont );
6126 0 : FontCharMap aFontCharMap;
6127 0 : bool bRet = GetFontCharMap( aFontCharMap );
6128 0 : const_cast<OutputDevice&>(*this).SetFont( aOrigFont );
6129 :
6130 : // if fontmap is unknown assume it doesn't have the glyphs
6131 0 : if( !bRet )
6132 0 : return nIndex;
6133 :
6134 0 : for( sal_Int32 i = nIndex; nIndex < nEnd; ++i, ++nIndex )
6135 0 : if( ! aFontCharMap.HasChar( rStr[i] ) )
6136 0 : return nIndex;
6137 :
6138 0 : return -1;
6139 3 : }
6140 :
6141 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|