xref: /aoo42x/main/vcl/source/gdi/outdev3.cxx (revision e26449d3)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include "i18npool/mslangid.hxx"
28 
29 #include "rtl/tencinfo.h"
30 #include "rtl/logfile.hxx"
31 
32 #include "tools/debug.hxx"
33 #include "tools/poly.hxx"
34 
35 #include "basegfx/polygon/b2dpolygon.hxx"
36 #include "basegfx/polygon/b2dpolypolygon.hxx"
37 #include "basegfx/matrix/b2dhommatrix.hxx"
38 
39 #include "vcl/metric.hxx"
40 #include "vcl/metaact.hxx"
41 #include "vcl/gdimtf.hxx"
42 #include "vcl/virdev.hxx"
43 #include "vcl/print.hxx"
44 #include "vcl/event.hxx"
45 #include "vcl/window.hxx"
46 #include "vcl/svapp.hxx"
47 #include "vcl/bmpacc.hxx"
48 #include "vcl/outdev.hxx"
49 #include "vcl/edit.hxx"
50 // declare system types in sysdata.hxx
51 #include <svsys.h>
52 #include "vcl/sysdata.hxx"
53 #include "vcl/unohelp.hxx"
54 #include "vcl/controllayout.hxx"
55 
56 #include "salgdi.hxx"
57 #include "sallayout.hxx"
58 #include "svdata.hxx"
59 #include "impfont.hxx"
60 #include "outdata.hxx"
61 #include "outfont.hxx"
62 #include "outdev.h"
63 #include "textlayout.hxx"
64 #include "svids.hrc"
65 #include "window.h"
66 
67 #include "unotools/fontcvt.hxx"
68 #include "unotools/fontcfg.hxx"
69 
70 #include "osl/file.h"
71 
72 #ifdef ENABLE_GRAPHITE
73 #include "graphite_features.hxx"
74 #endif
75 #ifdef USE_BUILTIN_RASTERIZER
76 #include "glyphcache.hxx"
77 #endif
78 
79 #include "pdfwriter_impl.hxx"
80 
81 #include "com/sun/star/beans/PropertyValues.hpp"
82 #include "com/sun/star/i18n/XBreakIterator.hpp"
83 #include "com/sun/star/i18n/WordType.hpp"
84 #include "com/sun/star/linguistic2/XLinguServiceManager.hpp"
85 
86 #if defined UNX
87 #define GLYPH_FONT_HEIGHT   128
88 #elif defined OS2
89 #define GLYPH_FONT_HEIGHT   176
90 #else
91 #define GLYPH_FONT_HEIGHT   256
92 #endif
93 
94 #include "sal/alloca.h"
95 
96 #include <cmath>
97 #include <cstring>
98 
99 #include <memory>
100 #include <algorithm>
101 
102 
103 // =======================================================================
104 
105 DBG_NAMEEX( OutputDevice )
106 DBG_NAMEEX( Font )
107 
108 // =======================================================================
109 
110 using namespace ::com::sun::star;
111 using namespace ::com::sun::star::uno;
112 using namespace ::rtl;
113 using namespace ::vcl;
114 using namespace ::utl;
115 
116 // =======================================================================
117 
118 #define TEXT_DRAW_ELLIPSIS  (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS)
119 
120 // =======================================================================
121 
122 #define UNDERLINE_LAST      UNDERLINE_BOLDWAVE
123 #define STRIKEOUT_LAST      STRIKEOUT_X
124 
125 // =======================================================================
126 
127 static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY,
128                            int nOrientation )
129 {
130     if ( (nOrientation >= 0) && !(nOrientation % 900) )
131     {
132         if ( (nOrientation >= 3600) )
133             nOrientation %= 3600;
134 
135         if ( nOrientation )
136         {
137             rX -= nOriginX;
138             rY -= nOriginY;
139 
140             if ( nOrientation == 900 )
141             {
142                 long nTemp = rX;
143                 rX = rY;
144                 rY = -nTemp;
145             }
146             else if ( nOrientation == 1800 )
147             {
148                 rX = -rX;
149                 rY = -rY;
150             }
151             else /* ( nOrientation == 2700 ) */
152             {
153                 long nTemp = rX;
154                 rX = -rY;
155                 rY = nTemp;
156             }
157 
158             rX += nOriginX;
159             rY += nOriginY;
160         }
161     }
162     else
163     {
164         double nRealOrientation = nOrientation*F_PI1800;
165         double nCos = cos( nRealOrientation );
166         double nSin = sin( nRealOrientation );
167 
168         // Translation...
169         long nX = rX-nOriginX;
170         long nY = rY-nOriginY;
171 
172         // Rotation...
173         rX = +((long)(nCos*nX + nSin*nY)) + nOriginX;
174         rY = -((long)(nSin*nX - nCos*nY)) + nOriginY;
175     }
176 }
177 
178 // =======================================================================
179 
180 void OutputDevice::ImplUpdateFontData( bool bNewFontLists )
181 {
182     // the currently selected logical font is no longer needed
183     if ( mpFontEntry )
184     {
185         mpFontCache->Release( mpFontEntry );
186         mpFontEntry = NULL;
187     }
188 
189     mbInitFont = true;
190     mbNewFont = true;
191 
192     if ( bNewFontLists )
193     {
194         if ( mpGetDevFontList )
195         {
196             delete mpGetDevFontList;
197             mpGetDevFontList = NULL;
198         }
199         if ( mpGetDevSizeList )
200         {
201             delete mpGetDevSizeList;
202             mpGetDevSizeList = NULL;
203         }
204 
205         // release all physically selected fonts on this device
206 	if( ImplGetGraphics() )
207 	     mpGraphics->ReleaseFonts();
208     }
209 
210     if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter )
211     {
212         ImplSVData* pSVData = ImplGetSVData();
213 
214         if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
215             mpFontCache->Invalidate();
216 
217         if ( bNewFontLists )
218         {
219             // we need a graphics
220             if ( ImplGetGraphics() )
221             {
222                 if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList )
223                     mpFontList->Clear();
224 
225                 if( mpPDFWriter )
226                 {
227                     if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList )
228                         delete mpFontList;
229                     if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
230                         delete mpFontCache;
231                     mpFontList = mpPDFWriter->filterDevFontList( pSVData->maGDIData.mpScreenFontList );
232                     mpFontCache = new ImplFontCache( sal_False );
233                 }
234                 else
235                 {
236                     if( mpOutDevData )
237                         mpOutDevData->maDevFontSubst.Clear();
238                     mpGraphics->GetDevFontList( mpFontList );
239                     mpGraphics->GetDevFontSubstList( this );
240                 }
241             }
242         }
243     }
244 
245     // also update child windows if needed
246     if ( GetOutDevType() == OUTDEV_WINDOW )
247     {
248         Window* pChild = ((Window*)this)->mpWindowImpl->mpFirstChild;
249         while ( pChild )
250         {
251             pChild->ImplUpdateFontData( true );
252             pChild = pChild->mpWindowImpl->mpNext;
253         }
254     }
255 }
256 
257 // -----------------------------------------------------------------------
258 
259 void OutputDevice::ImplUpdateAllFontData( bool bNewFontLists )
260 {
261     ImplSVData* pSVData = ImplGetSVData();
262 
263     // update all windows
264     Window* pFrame = pSVData->maWinData.mpFirstFrame;
265     while ( pFrame )
266     {
267         pFrame->ImplUpdateFontData( bNewFontLists );
268 
269         Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap;
270         while ( pSysWin )
271         {
272             pSysWin->ImplUpdateFontData( bNewFontLists );
273             pSysWin = pSysWin->mpWindowImpl->mpNextOverlap;
274         }
275 
276         pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
277     }
278 
279     // update all virtual devices
280     VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev;
281     while ( pVirDev )
282     {
283         pVirDev->ImplUpdateFontData( bNewFontLists );
284         pVirDev = pVirDev->mpNext;
285     }
286 
287     // update all printers
288     Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter;
289     while ( pPrinter )
290     {
291         pPrinter->ImplUpdateFontData( bNewFontLists );
292         pPrinter = pPrinter->mpNext;
293     }
294 
295     // clear global font lists to have them updated
296     pSVData->maGDIData.mpScreenFontCache->Invalidate();
297     if ( bNewFontLists )
298     {
299         pSVData->maGDIData.mpScreenFontList->Clear();
300         pFrame = pSVData->maWinData.mpFirstFrame;
301         if ( pFrame )
302         {
303             if ( pFrame->ImplGetGraphics() )
304                 // MT: Stupid typecast here and somewhere ((OutputDevice*)&aVDev)->, because bug in .NET2002 compiler.
305                 ((OutputDevice*)pFrame)->mpGraphics->GetDevFontList( pFrame->mpWindowImpl->mpFrameData->mpFontList );
306         }
307     }
308 }
309 
310 // =======================================================================
311 
312 
313 // =======================================================================
314 
315 // TODO: remove this method when the CWS-gfbfcfg dust has settled
316 void ImplFreeOutDevFontData()
317 {}
318 
319 // =======================================================================
320 
321 void OutputDevice::BeginFontSubstitution()
322 {
323     ImplSVData* pSVData = ImplGetSVData();
324     pSVData->maGDIData.mbFontSubChanged = sal_False;
325 }
326 
327 // -----------------------------------------------------------------------
328 
329 void OutputDevice::EndFontSubstitution()
330 {
331     ImplSVData* pSVData = ImplGetSVData();
332     if ( pSVData->maGDIData.mbFontSubChanged )
333     {
334         ImplUpdateAllFontData( false );
335 
336         Application* pApp = GetpApp();
337         DataChangedEvent aDCEvt( DATACHANGED_FONTSUBSTITUTION );
338         pApp->DataChanged( aDCEvt );
339         pApp->NotifyAllWindows( aDCEvt );
340         pSVData->maGDIData.mbFontSubChanged = sal_False;
341     }
342 }
343 
344 // -----------------------------------------------------------------------
345 
346 void OutputDevice::AddFontSubstitute( const XubString& rFontName,
347                                       const XubString& rReplaceFontName,
348                                       sal_uInt16 nFlags )
349 {
350     ImplDirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
351     if( !rpSubst )
352         rpSubst = new ImplDirectFontSubstitution();
353     rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags );
354     ImplGetSVData()->maGDIData.mbFontSubChanged = sal_True;
355 }
356 
357 // -----------------------------------------------------------------------
358 
359 void ImplDirectFontSubstitution::AddFontSubstitute( const String& rFontName,
360 	const String& rSubstFontName, sal_uInt16 nFlags )
361 {
362     maFontSubstList.push_back( ImplFontSubstEntry( rFontName, rSubstFontName, nFlags ) );
363 }
364 
365 // -----------------------------------------------------------------------
366 
367 ImplFontSubstEntry::ImplFontSubstEntry( const String& rFontName,
368     const String& rSubstFontName, sal_uInt16 nSubstFlags )
369 :   maName( rFontName )
370 ,   maReplaceName( rSubstFontName )
371 ,   mnFlags( nSubstFlags )
372 {
373     maSearchName        = rFontName;
374     maSearchReplaceName = rSubstFontName;
375     GetEnglishSearchFontName( maSearchName );
376     GetEnglishSearchFontName( maSearchReplaceName );
377 }
378 
379 // -----------------------------------------------------------------------
380 
381 void OutputDevice::ImplAddDevFontSubstitute( const XubString& rFontName,
382                                              const XubString& rReplaceFontName,
383                                              sal_uInt16 nFlags )
384 {
385     ImplInitOutDevData();
386     mpOutDevData->maDevFontSubst.AddFontSubstitute( rFontName, rReplaceFontName, nFlags );
387 }
388 
389 // -----------------------------------------------------------------------
390 
391 void OutputDevice::RemoveFontSubstitute( sal_uInt16 n )
392 {
393     ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
394     if( pSubst )
395         pSubst->RemoveFontSubstitute( n );
396 }
397 
398 // -----------------------------------------------------------------------
399 
400 void ImplDirectFontSubstitution::RemoveFontSubstitute( int nIndex )
401 {
402     FontSubstList::iterator it = maFontSubstList.begin();
403     for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ;
404     if( it != maFontSubstList.end() )
405         maFontSubstList.erase( it );
406 }
407 
408 // -----------------------------------------------------------------------
409 
410 sal_uInt16 OutputDevice::GetFontSubstituteCount()
411 {
412     const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
413     if( !pSubst )
414 	return 0;
415     int nCount =  pSubst->GetFontSubstituteCount();
416     return (sal_uInt16)nCount;
417 }
418 
419 // -----------------------------------------------------------------------
420 
421 void OutputDevice::GetFontSubstitute( sal_uInt16 n,
422                                       XubString& rFontName,
423                                       XubString& rReplaceFontName,
424                                       sal_uInt16& rFlags )
425 {
426     const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
427     if( pSubst )
428         pSubst->GetFontSubstitute( n, rFontName, rReplaceFontName, rFlags );
429 }
430 
431 // -----------------------------------------------------------------------
432 
433 bool ImplDirectFontSubstitution::GetFontSubstitute( int nIndex,
434     String& rFontName, String& rSubstFontName, sal_uInt16& rFlags ) const
435 {
436     FontSubstList::const_iterator it = maFontSubstList.begin();
437 	for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ;
438 	if( it == maFontSubstList.end() )
439 	    return false;
440 
441     const ImplFontSubstEntry* pEntry = &(*it);
442     rFontName       = pEntry->maName;
443     rSubstFontName  = pEntry->maReplaceName;
444     rFlags          = pEntry->mnFlags;
445     return true;
446 }
447 
448 // -----------------------------------------------------------------------
449 
450 bool ImplDirectFontSubstitution::FindFontSubstitute( String& rSubstName,
451     const String& rSearchName, sal_uInt16 nFlags ) const
452 {
453     // TODO: get rid of O(N) searches
454     FontSubstList::const_iterator it = maFontSubstList.begin();
455     for(; it != maFontSubstList.end(); ++it )
456     {
457         const ImplFontSubstEntry& rEntry = *it;
458         if( ((rEntry.mnFlags & nFlags) || !nFlags)
459         &&   (rEntry.maSearchName == rSearchName) )
460         {
461             rSubstName = rEntry.maSearchReplaceName;
462             return true;
463         }
464     }
465 
466     return false;
467 }
468 
469 // -----------------------------------------------------------------------
470 
471 static void ImplFontSubstitute( String& rFontName,
472     sal_uInt16 nFlags, ImplDirectFontSubstitution* pDevSpecific )
473 {
474 #ifdef DBG_UTIL
475     String aTempName = rFontName;
476     GetEnglishSearchFontName( aTempName );
477     DBG_ASSERT( aTempName == rFontName, "ImplFontSubstitute() called without a searchname" );
478 #endif
479 
480     String aSubstFontName;
481 
482     // apply user-configurable font replacement (eg, from the list in Tools->Options)
483     const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
484     if( pSubst && pSubst->FindFontSubstitute( aSubstFontName, rFontName, FONT_SUBSTITUTE_ALWAYS ) )
485     {
486         rFontName = aSubstFontName;
487         return;
488     }
489 
490     // apply device specific font replacement (e.g. to use printer builtin fonts)
491     if( !pDevSpecific )
492         return;
493 
494     if( pDevSpecific->FindFontSubstitute( aSubstFontName, rFontName, nFlags ) )
495     {
496         rFontName = aSubstFontName;
497         return;
498     }
499 }
500 
501 // -----------------------------------------------------------------------
502 
503 Font OutputDevice::GetDefaultFont( sal_uInt16 nType, LanguageType eLang,
504                                    sal_uLong nFlags, const OutputDevice* pOutDev )
505 {
506     DBG_TRACE( "OutputDevice::GetDefaultFont()" );
507 
508     com::sun::star::lang::Locale aLocale;
509     if( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW )
510     {
511         aLocale = Application::GetSettings().GetUILocale();
512     }
513     else
514     {
515         MsLangId::convertLanguageToLocale( eLang, aLocale );
516     }
517 
518     utl::DefaultFontConfiguration& rDefaults = *utl::DefaultFontConfiguration::get();
519     String aSearch = rDefaults.getUserInterfaceFont( aLocale ); // ensure a fallback
520     String aDefault = rDefaults.getDefaultFont( aLocale, nType );
521     if( aDefault.Len() )
522         aSearch = aDefault;
523 
524     int nDefaultHeight = 12;
525 
526 	Font aFont;
527     aFont.SetPitch( PITCH_VARIABLE );
528 
529     switch ( nType )
530     {
531         case DEFAULTFONT_SANS_UNICODE:
532         case DEFAULTFONT_UI_SANS:
533             aFont.SetFamily( FAMILY_SWISS );
534             break;
535 
536         case DEFAULTFONT_SANS:
537         case DEFAULTFONT_LATIN_HEADING:
538         case DEFAULTFONT_LATIN_SPREADSHEET:
539         case DEFAULTFONT_LATIN_DISPLAY:
540             aFont.SetFamily( FAMILY_SWISS );
541             break;
542 
543         case DEFAULTFONT_SERIF:
544         case DEFAULTFONT_LATIN_TEXT:
545         case DEFAULTFONT_LATIN_PRESENTATION:
546             aFont.SetFamily( FAMILY_ROMAN );
547             break;
548 
549         case DEFAULTFONT_FIXED:
550         case DEFAULTFONT_LATIN_FIXED:
551         case DEFAULTFONT_UI_FIXED:
552             aFont.SetPitch( PITCH_FIXED );
553             aFont.SetFamily( FAMILY_MODERN );
554             break;
555 
556         case DEFAULTFONT_SYMBOL:
557             aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
558             break;
559 
560         case DEFAULTFONT_CJK_TEXT:
561         case DEFAULTFONT_CJK_PRESENTATION:
562         case DEFAULTFONT_CJK_SPREADSHEET:
563         case DEFAULTFONT_CJK_HEADING:
564         case DEFAULTFONT_CJK_DISPLAY:
565             aFont.SetFamily( FAMILY_SYSTEM );	// don't care, but don't use font subst config later...
566             break;
567 
568         case DEFAULTFONT_CTL_TEXT:
569         case DEFAULTFONT_CTL_PRESENTATION:
570         case DEFAULTFONT_CTL_SPREADSHEET:
571         case DEFAULTFONT_CTL_HEADING:
572         case DEFAULTFONT_CTL_DISPLAY:
573             aFont.SetFamily( FAMILY_SYSTEM );	// don't care, but don't use font subst config later...
574             break;
575     }
576 
577     if ( aSearch.Len() )
578     {
579         aFont.SetHeight( nDefaultHeight );
580         aFont.SetWeight( WEIGHT_NORMAL );
581         aFont.SetLanguage( eLang );
582 
583 		if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW )
584             aFont.SetCharSet( gsl_getSystemTextEncoding() );
585 
586         // Should we only return available fonts on the given device
587         if ( pOutDev )
588         {
589             pOutDev->ImplInitFontList();
590 
591             // Search Font in the FontList
592             String      aName;
593             String      aSearchName;
594             xub_StrLen  nIndex = 0;
595             do
596             {
597                 aSearchName = GetNextFontToken( aSearch, nIndex );
598                 GetEnglishSearchFontName( aSearchName );
599                 ImplDevFontListData* pFontFamily = pOutDev->mpFontList->ImplFindBySearchName( aSearchName );
600                 if( pFontFamily )
601                 {
602                     AddTokenFontName( aName, pFontFamily->GetFamilyName() );
603                     if( nFlags & DEFAULTFONT_FLAGS_ONLYONE )
604                         break;
605                 }
606             }
607             while ( nIndex != STRING_NOTFOUND );
608             aFont.SetName( aName );
609         }
610 
611         // No Name, than set all names
612         if ( !aFont.GetName().Len() )
613         {
614             xub_StrLen nIndex = 0;
615             if ( nFlags & DEFAULTFONT_FLAGS_ONLYONE )
616             {
617                 //aFont.SetName( aSearch.GetToken( 0, ';', nIndex ) );
618                 if( !pOutDev )
619                     pOutDev = (const OutputDevice *)ImplGetSVData()->mpDefaultWin;
620                 if( !pOutDev )
621                     aFont.SetName( aSearch.GetToken( 0, ';', nIndex ) );
622                 else
623                 {
624                     pOutDev->ImplInitFontList();
625 
626                     aFont.SetName( aSearch );
627 
628                     // convert to pixel height
629                     Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetSize() );
630                     if ( !aSize.Height() )
631                     {
632                         // use default pixel height only when logical height is zero
633                         if ( aFont.GetHeight() )
634                             aSize.Height() = 1;
635                         else
636                             aSize.Height() = (12*pOutDev->mnDPIY)/72;
637                     }
638 
639                     // use default width only when logical width is zero
640                     if( (0 == aSize.Width()) && (0 != aFont.GetSize().Width()) )
641                         aSize.Width() = 1;
642 
643                     // get the name of the first available font
644                     float fExactHeight = static_cast<float>(aSize.Height());
645                     ImplFontEntry* pEntry = pOutDev->mpFontCache->GetFontEntry( pOutDev->mpFontList, aFont, aSize, fExactHeight, pOutDev->mpOutDevData ? &pOutDev->mpOutDevData->maDevFontSubst : NULL );
646                     if( pEntry->maFontSelData.mpFontData )
647                         aFont.SetName( pEntry->maFontSelData.mpFontData->maName );
648                     else
649                         aFont.SetName( pEntry->maFontSelData.maTargetName );
650                 }
651             }
652             else
653                 aFont.SetName( aSearch );
654         }
655     }
656 
657 #if OSL_DEBUG_LEVEL > 2
658     const char* s = "DEFAULTFONT_SANS_UNKNOWN";
659     switch ( nType )
660     {
661 	case DEFAULTFONT_SANS_UNICODE:	s = "DEFAULTFONT_SANS_UNICODE"; break;
662 	case DEFAULTFONT_UI_SANS:	s = "DEFAULTFONT_UI_SANS"; break;
663 
664 	case DEFAULTFONT_SANS:	s = "DEFAULTFONT_SANS"; break;
665 	case DEFAULTFONT_LATIN_HEADING:	s = "DEFAULTFONT_LATIN_HEADING"; break;
666 	case DEFAULTFONT_LATIN_SPREADSHEET:	s = "DEFAULTFONT_LATIN_SPREADSHEET"; break;
667 	case DEFAULTFONT_LATIN_DISPLAY:	s = "DEFAULTFONT_LATIN_DISPLAY"; break;
668 
669 	case DEFAULTFONT_SERIF:	s = "DEFAULTFONT_SERIF"; break;
670 	case DEFAULTFONT_LATIN_TEXT:	s = "DEFAULTFONT_LATIN_TEXT"; break;
671 	case DEFAULTFONT_LATIN_PRESENTATION:	s = "DEFAULTFONT_LATIN_PRESENTATION"; break;
672 
673 	case DEFAULTFONT_FIXED:	s = "DEFAULTFONT_FIXED"; break;
674 	case DEFAULTFONT_LATIN_FIXED:	s = "DEFAULTFONT_LATIN_FIXED"; break;
675 	case DEFAULTFONT_UI_FIXED:	s = "DEFAULTFONT_UI_FIXED"; break;
676 
677 	case DEFAULTFONT_SYMBOL:	s = "DEFAULTFONT_SYMBOL"; break;
678 
679 	case DEFAULTFONT_CJK_TEXT:	s = "DEFAULTFONT_CJK_TEXT"; break;
680 	case DEFAULTFONT_CJK_PRESENTATION:	s = "DEFAULTFONT_CJK_PRESENTATION"; break;
681 	case DEFAULTFONT_CJK_SPREADSHEET:	s = "DEFAULTFONT_CJK_SPREADSHEET"; break;
682 	case DEFAULTFONT_CJK_HEADING:	s = "DEFAULTFONT_CJK_HEADING"; break;
683 	case DEFAULTFONT_CJK_DISPLAY:	s = "DEFAULTFONT_CJK_DISPLAY"; break;
684 
685 	case DEFAULTFONT_CTL_TEXT:	s = "DEFAULTFONT_CTL_TEXT"; break;
686 	case DEFAULTFONT_CTL_PRESENTATION:	s = "DEFAULTFONT_CTL_PRESENTATION"; break;
687 	case DEFAULTFONT_CTL_SPREADSHEET:	s = "DEFAULTFONT_CTL_SPREADSHEET"; break;
688 	case DEFAULTFONT_CTL_HEADING:	s = "DEFAULTFONT_CTL_HEADING"; break;
689 	case DEFAULTFONT_CTL_DISPLAY:	s = "DEFAULTFONT_CTL_DISPLAY"; break;
690     }
691     fprintf( stderr, "   OutputDevice::GetDefaultFont() Type=\"%s\" lang=%d flags=%ld FontName=\"%s\"\n",
692 	     s, eLang, nFlags,
693 	     OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr()
694 	     );
695 #endif
696 
697     return aFont;
698 }
699 
700 // =======================================================================
701 
702 static unsigned ImplIsCJKFont( const String& rFontName )
703 {
704     // Test, if Fontname includes CJK characters --> In this case we
705     // mention that it is a CJK font
706     const sal_Unicode* pStr = rFontName.GetBuffer();
707     while ( *pStr )
708     {
709         // japanese
710         if ( ((*pStr >= 0x3040) && (*pStr <= 0x30FF)) ||
711              ((*pStr >= 0x3190) && (*pStr <= 0x319F)) )
712             return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_JP;
713 
714         // korean
715         if ( ((*pStr >= 0xAC00) && (*pStr <= 0xD7AF)) ||
716              ((*pStr >= 0x3130) && (*pStr <= 0x318F)) ||
717              ((*pStr >= 0x1100) && (*pStr <= 0x11FF)) )
718             return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_KR;
719 
720         // chinese
721         if ( ((*pStr >= 0x3400) && (*pStr <= 0x9FFF)) )
722             return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_TC|IMPL_FONT_ATTR_CJK_SC;
723 
724         // cjk
725         if ( ((*pStr >= 0x3000) && (*pStr <= 0xD7AF)) ||
726              ((*pStr >= 0xFF00) && (*pStr <= 0xFFEE)) )
727             return IMPL_FONT_ATTR_CJK;
728 
729         pStr++;
730     }
731 
732     return 0;
733 }
734 
735 // -----------------------------------------------------------------------
736 
737 static void ImplCalcType( sal_uLong& rType, FontWeight& rWeight, FontWidth& rWidth,
738                           FontFamily eFamily, const FontNameAttr* pFontAttr )
739 {
740     if ( eFamily != FAMILY_DONTKNOW )
741     {
742         if ( eFamily == FAMILY_SWISS )
743             rType |= IMPL_FONT_ATTR_SANSSERIF;
744         else if ( eFamily == FAMILY_ROMAN )
745             rType |= IMPL_FONT_ATTR_SERIF;
746         else if ( eFamily == FAMILY_SCRIPT )
747             rType |= IMPL_FONT_ATTR_SCRIPT;
748         else if ( eFamily == FAMILY_MODERN )
749             rType |= IMPL_FONT_ATTR_FIXED;
750         else if ( eFamily == FAMILY_DECORATIVE )
751             rType |= IMPL_FONT_ATTR_DECORATIVE;
752     }
753 
754     if ( pFontAttr )
755     {
756         rType |= pFontAttr->Type;
757 
758         if ( ((rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL)) &&
759              (pFontAttr->Weight != WEIGHT_DONTKNOW) )
760             rWeight = pFontAttr->Weight;
761         if ( ((rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL)) &&
762              (pFontAttr->Width != WIDTH_DONTKNOW) )
763             rWidth = pFontAttr->Width;
764     }
765 }
766 
767 // =======================================================================
768 
769 ImplFontData::ImplFontData( const ImplDevFontAttributes& rDFA, int nMagic )
770 :   ImplDevFontAttributes( rDFA ),
771     mnWidth(0),
772     mnHeight(0),
773     mnMagic( nMagic ),
774     mpNext( NULL )
775 {
776     // StarSymbol is a unicode font, but it still deserves the symbol flag
777     if( !mbSymbolFlag )
778         if( 0 == GetFamilyName().CompareIgnoreCaseToAscii( "starsymbol", 10)
779         ||  0 == GetFamilyName().CompareIgnoreCaseToAscii( "opensymbol", 10) )
780             mbSymbolFlag = true;
781 }
782 
783 // -----------------------------------------------------------------------
784 
785 StringCompare ImplFontData::CompareIgnoreSize( const ImplFontData& rOther ) const
786 {
787     // compare their width, weight, italic and style name
788     if( meWidthType < rOther.meWidthType )
789         return COMPARE_LESS;
790     else if( meWidthType > rOther.meWidthType )
791         return COMPARE_GREATER;
792 
793     if( meWeight < rOther.meWeight )
794         return COMPARE_LESS;
795     else if( meWeight > rOther.meWeight )
796         return COMPARE_GREATER;
797 
798     if( meItalic < rOther.meItalic )
799         return COMPARE_LESS;
800     else if( meItalic > rOther.meItalic )
801         return COMPARE_GREATER;
802 
803     StringCompare eCompare = maName.CompareTo( rOther.maName );
804     return eCompare;
805 }
806 
807 // -----------------------------------------------------------------------
808 
809 StringCompare ImplFontData::CompareWithSize( const ImplFontData& rOther ) const
810 {
811     StringCompare eCompare = CompareIgnoreSize( rOther );
812     if( eCompare != COMPARE_EQUAL )
813         return eCompare;
814 
815     if( mnHeight < rOther.mnHeight )
816         return COMPARE_LESS;
817     else if( mnHeight > rOther.mnHeight )
818         return COMPARE_GREATER;
819 
820     if( mnWidth < rOther.mnWidth )
821         return COMPARE_LESS;
822     else if( mnWidth > rOther.mnWidth )
823         return COMPARE_GREATER;
824 
825     return COMPARE_EQUAL;
826 }
827 
828 // -----------------------------------------------------------------------
829 
830 struct FontMatchStatus
831 {
832 public:
833     int                 mnFaceMatch;
834     int                 mnHeightMatch;
835     int                 mnWidthMatch;
836     const xub_Unicode*  mpTargetStyleName;
837 };
838 
839 bool ImplFontData::IsBetterMatch( const ImplFontSelectData& rFSD, FontMatchStatus& rStatus ) const
840 {
841     int nMatch = 0;
842 
843     const String& rFontName = rFSD.maTargetName;
844     if( (rFontName == maName) || rFontName.EqualsIgnoreCaseAscii( maName ) )
845         nMatch += 240000;
846 
847     if( rStatus.mpTargetStyleName
848     &&  maStyleName.EqualsIgnoreCaseAscii( rStatus.mpTargetStyleName ) )
849         nMatch += 120000;
850 
851     if( (rFSD.mePitch != PITCH_DONTKNOW) && (rFSD.mePitch == mePitch) )
852         nMatch += 20000;
853 
854     // prefer NORMAL font width
855     // TODO: change when the upper layers can tell their width preference
856     if( meWidthType == WIDTH_NORMAL )
857         nMatch += 400;
858     else if( (meWidthType == WIDTH_SEMI_EXPANDED) || (meWidthType == WIDTH_SEMI_CONDENSED) )
859         nMatch += 300;
860 
861     if( rFSD.meWeight != WEIGHT_DONTKNOW )
862     {
863         // if not bold prefer light fonts to bold fonts
864         int nReqWeight = (int)rFSD.meWeight;
865         if ( rFSD.meWeight > WEIGHT_MEDIUM )
866             nReqWeight += 100;
867 
868         int nGivenWeight = (int)meWeight;
869         if( meWeight > WEIGHT_MEDIUM )
870             nGivenWeight += 100;
871 
872         int nWeightDiff = nReqWeight - nGivenWeight;
873 
874         if ( nWeightDiff == 0 )
875             nMatch += 1000;
876         else if ( nWeightDiff == +1 || nWeightDiff == -1 )
877             nMatch += 700;
878         else if ( nWeightDiff < +50 && nWeightDiff > -50)
879             nMatch += 200;
880     }
881     else // requested weight == WEIGHT_DONTKNOW
882     {
883         // prefer NORMAL font weight
884         // TODO: change when the upper layers can tell their weight preference
885         if( meWeight == WEIGHT_NORMAL )
886             nMatch += 450;
887         else if( meWeight == WEIGHT_MEDIUM )
888             nMatch += 350;
889         else if( (meWeight == WEIGHT_SEMILIGHT) || (meWeight == WEIGHT_SEMIBOLD) )
890             nMatch += 200;
891         else if( meWeight == WEIGHT_LIGHT )
892             nMatch += 150;
893     }
894 
895     if ( rFSD.meItalic == ITALIC_NONE )
896     {
897         if( meItalic == ITALIC_NONE )
898             nMatch += 900;
899     }
900     else
901     {
902         if( rFSD.meItalic == meItalic )
903             nMatch += 900;
904         else if( meItalic != ITALIC_NONE )
905             nMatch += 600;
906     }
907 
908     if( mbDevice )
909         nMatch += 1;
910 
911     int nHeightMatch = 0;
912     int nWidthMatch = 0;
913 
914     if( IsScalable() )
915     {
916         if( rFSD.mnOrientation != 0 )
917             nMatch += 80;
918         else if( rFSD.mnWidth != 0 )
919             nMatch += 25;
920         else
921             nMatch += 5;
922     }
923     else
924     {
925         if( rFSD.mnHeight == mnHeight )
926         {
927             nMatch += 20;
928             if( rFSD.mnWidth == mnWidth )
929                 nMatch += 10;
930         }
931         else
932         {
933             // for non-scalable fonts the size difference is very important
934             // prefer the smaller font face because of clipping/overlapping issues
935             int nHeightDiff = (rFSD.mnHeight - mnHeight) * 1000;
936             nHeightMatch = (nHeightDiff >= 0) ? -nHeightDiff : 100+nHeightDiff;
937             if( rFSD.mnHeight )
938                 nHeightMatch /= rFSD.mnHeight;
939 
940             if( (rFSD.mnWidth != 0) && (mnWidth != 0) && (rFSD.mnWidth != mnWidth) )
941             {
942                 int nWidthDiff = (rFSD.mnWidth - mnWidth) * 100;
943                 nWidthMatch = (nWidthDiff >= 0) ? -nWidthDiff : +nWidthDiff;
944             }
945         }
946     }
947 
948     if( rStatus.mnFaceMatch > nMatch )
949         return false;
950     else if( rStatus.mnFaceMatch < nMatch )
951     {
952         rStatus.mnFaceMatch      = nMatch;
953         rStatus.mnHeightMatch    = nHeightMatch;
954         rStatus.mnWidthMatch     = nWidthMatch;
955         return true;
956     }
957 
958     // when two fonts are still competing prefer the
959     // one with the best matching height
960     if( rStatus.mnHeightMatch > nHeightMatch )
961         return false;
962     else if( rStatus.mnHeightMatch < nHeightMatch )
963     {
964         rStatus.mnHeightMatch    = nHeightMatch;
965         rStatus.mnWidthMatch     = nWidthMatch;
966         return true;
967     }
968 
969     if( rStatus.mnWidthMatch > nWidthMatch )
970         return false;
971 
972     rStatus.mnWidthMatch = nWidthMatch;
973     return true;
974 }
975 
976 // =======================================================================
977 
978 ImplFontEntry::ImplFontEntry( const ImplFontSelectData& rFontSelData )
979 :   maFontSelData( rFontSelData ),
980     maMetric( rFontSelData ),
981     mpConversion( NULL ),
982     mnRefCount( 1 ),
983     mnSetFontFlags( 0 ),
984     mnOwnOrientation( 0 ),
985     mnOrientation( 0 ),
986     mbInit( false ),
987     mpUnicodeFallbackList( NULL )
988 {
989     maFontSelData.mpFontEntry = this;
990 }
991 
992 // -----------------------------------------------------------------------
993 
994 ImplFontEntry::~ImplFontEntry()
995 {
996     delete mpUnicodeFallbackList;
997 }
998 
999 // -----------------------------------------------------------------------
1000 
1001 size_t ImplFontEntry::GFBCacheKey_Hash::operator()( const GFBCacheKey& rData ) const
1002 {
1003     std::hash<sal_UCS4> a;
1004     std::hash<int > b;
1005     return a(rData.first) ^ b(rData.second);
1006 }
1007 
1008 inline void ImplFontEntry::AddFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const String& rFontName )
1009 {
1010     if( !mpUnicodeFallbackList )
1011         mpUnicodeFallbackList = new UnicodeFallbackList;
1012     (*mpUnicodeFallbackList)[ GFBCacheKey(cChar,eWeight) ] = rFontName;
1013 }
1014 
1015 // -----------------------------------------------------------------------
1016 
1017 inline bool ImplFontEntry::GetFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, String* pFontName ) const
1018 {
1019     if( !mpUnicodeFallbackList )
1020         return false;
1021 
1022     UnicodeFallbackList::const_iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) );
1023     if( it == mpUnicodeFallbackList->end() )
1024         return false;
1025 
1026     *pFontName = (*it).second;
1027     return true;
1028 }
1029 
1030 // -----------------------------------------------------------------------
1031 
1032 inline void ImplFontEntry::IgnoreFallbackForUnicode( sal_UCS4 cChar, FontWeight eWeight, const String& rFontName )
1033 {
1034 //  DBG_ASSERT( mpUnicodeFallbackList, "ImplFontEntry::IgnoreFallbackForUnicode no list" );
1035     UnicodeFallbackList::iterator it = mpUnicodeFallbackList->find( GFBCacheKey(cChar,eWeight) );
1036 //  DBG_ASSERT( it != mpUnicodeFallbackList->end(), "ImplFontEntry::IgnoreFallbackForUnicode no match" );
1037     if( it == mpUnicodeFallbackList->end() )
1038         return;
1039     if( (*it).second == rFontName )
1040         mpUnicodeFallbackList->erase( it );
1041 }
1042 
1043 // =======================================================================
1044 
1045 ImplDevFontListData::ImplDevFontListData( const String& rSearchName )
1046 :   mpFirst( NULL ),
1047     maSearchName( rSearchName ),
1048     mnTypeFaces( 0 ),
1049     mnMatchType( 0 ),
1050     meMatchWeight( WEIGHT_DONTKNOW ),
1051     meMatchWidth( WIDTH_DONTKNOW ),
1052     meFamily( FAMILY_DONTKNOW ),
1053     mePitch( PITCH_DONTKNOW ),
1054     mnMinQuality( -1 )
1055 {}
1056 
1057 // -----------------------------------------------------------------------
1058 
1059 ImplDevFontListData::~ImplDevFontListData()
1060 {
1061     // release all physical font faces
1062     while( mpFirst )
1063     {
1064         ImplFontData* pFace = mpFirst;
1065         mpFirst = pFace->GetNextFace();
1066         delete pFace;
1067     }
1068 }
1069 
1070 // -----------------------------------------------------------------------
1071 
1072 bool ImplDevFontListData::AddFontFace( ImplFontData* pNewData )
1073 {
1074     pNewData->mpNext = NULL;
1075 
1076     if( !mpFirst )
1077     {
1078         maName         = pNewData->maName;
1079         maMapNames     = pNewData->maMapNames;
1080         meFamily       = pNewData->meFamily;
1081         mePitch        = pNewData->mePitch;
1082         mnMinQuality   = pNewData->mnQuality;
1083     }
1084     else
1085     {
1086         if( meFamily == FAMILY_DONTKNOW )
1087             meFamily = pNewData->meFamily;
1088         if( mePitch == PITCH_DONTKNOW )
1089             mePitch = pNewData->mePitch;
1090         if( mnMinQuality > pNewData->mnQuality )
1091             mnMinQuality = pNewData->mnQuality;
1092     }
1093 
1094     // set attributes for attribute based font matching
1095     if( pNewData->IsScalable() )
1096         mnTypeFaces |= IMPL_DEVFONT_SCALABLE;
1097 
1098     if( pNewData->IsSymbolFont() )
1099         mnTypeFaces |= IMPL_DEVFONT_SYMBOL;
1100     else
1101         mnTypeFaces |= IMPL_DEVFONT_NONESYMBOL;
1102 
1103     if( pNewData->meWeight != WEIGHT_DONTKNOW )
1104     {
1105         if( pNewData->meWeight >= WEIGHT_SEMIBOLD )
1106             mnTypeFaces |= IMPL_DEVFONT_BOLD;
1107         else if( pNewData->meWeight <= WEIGHT_SEMILIGHT )
1108             mnTypeFaces |= IMPL_DEVFONT_LIGHT;
1109         else
1110             mnTypeFaces |= IMPL_DEVFONT_NORMAL;
1111     }
1112 
1113     if( pNewData->meItalic == ITALIC_NONE )
1114         mnTypeFaces |= IMPL_DEVFONT_NONEITALIC;
1115     else if( (pNewData->meItalic == ITALIC_NORMAL)
1116          ||  (pNewData->meItalic == ITALIC_OBLIQUE) )
1117         mnTypeFaces |= IMPL_DEVFONT_ITALIC;
1118 
1119     if( (meMatchWeight == WEIGHT_DONTKNOW)
1120     ||  (meMatchWidth  == WIDTH_DONTKNOW)
1121     ||  (mnMatchType   == 0) )
1122     {
1123         // TODO: is it cheaper to calc matching attributes now or on demand?
1124         // calc matching attributes if other entries are already initialized
1125 
1126         // MT: Perform05: Do lazy, quite expensive, not needed in start-up!
1127         // const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
1128         // InitMatchData( rFontSubst, maSearchName );
1129 		// mbMatchData=true; // Somewhere else???
1130     }
1131 
1132     // reassign name (sharing saves memory)
1133     if( pNewData->maName == maName )
1134         pNewData->maName = maName;
1135 
1136     // insert new physical font face into linked list
1137     // TODO: get rid of linear search?
1138     ImplFontData* pData;
1139     ImplFontData** ppHere = &mpFirst;
1140     for(; (pData=*ppHere) != NULL; ppHere=&pData->mpNext )
1141     {
1142         StringCompare eComp = pNewData->CompareWithSize( *pData );
1143         if( eComp == COMPARE_GREATER )
1144             continue;
1145         if( eComp == COMPARE_LESS )
1146             break;
1147 
1148         // ignore duplicate if its quality is worse
1149         if( pNewData->mnQuality < pData->mnQuality )
1150             return false;
1151 
1152         // keep the device font if its quality is good enough
1153         if( (pNewData->mnQuality == pData->mnQuality)
1154         &&  (pData->mbDevice || !pNewData->mbDevice) )
1155             return false;
1156 
1157         // replace existing font face with a better one
1158         pNewData->mpNext = pData->mpNext;
1159         *ppHere = pNewData;
1160         delete pData;
1161         return true;
1162     }
1163 
1164     // insert into or append to list of physical font faces
1165     pNewData->mpNext = pData;
1166     *ppHere = pNewData;
1167     return true;
1168 }
1169 
1170 // -----------------------------------------------------------------------
1171 
1172 // get font attributes using the normalized font family name
1173 void ImplDevFontListData::InitMatchData( const utl::FontSubstConfiguration& rFontSubst,
1174     const String& rSearchName )
1175 {
1176     String aShortName;
1177     // get font attributes from the decorated font name
1178     rFontSubst.getMapName( rSearchName, aShortName, maMatchFamilyName,
1179                             meMatchWeight, meMatchWidth, mnMatchType );
1180     const FontNameAttr* pFontAttr = rFontSubst.getSubstInfo( rSearchName );
1181     // eventually use the stripped name
1182     if( !pFontAttr )
1183         if( aShortName != rSearchName )
1184             pFontAttr = rFontSubst.getSubstInfo( aShortName );
1185     ImplCalcType( mnMatchType, meMatchWeight, meMatchWidth, meFamily, pFontAttr );
1186     mnMatchType |= ImplIsCJKFont( maName );
1187 }
1188 
1189 // -----------------------------------------------------------------------
1190 
1191 ImplFontData* ImplDevFontListData::FindBestFontFace( const ImplFontSelectData& rFSD ) const
1192 {
1193     if( !mpFirst )
1194         return NULL;
1195     if( !mpFirst->GetNextFace() )
1196         return mpFirst;
1197 
1198     // FontName+StyleName should map to FamilyName+StyleName
1199     const String& rSearchName = rFSD.maTargetName;
1200     const xub_Unicode* pTargetStyleName = NULL;
1201     if( (rSearchName.Len() > maSearchName.Len())
1202     &&   rSearchName.Equals( maSearchName, 0, maSearchName.Len() ) )
1203         pTargetStyleName = rSearchName.GetBuffer() + maSearchName.Len() + 1;
1204 
1205     // linear search, TODO: improve?
1206     ImplFontData* pFontFace = mpFirst;
1207     ImplFontData* pBestFontFace = pFontFace;
1208     FontMatchStatus aFontMatchStatus = {0,0,0, pTargetStyleName};
1209     for(; pFontFace; pFontFace = pFontFace->GetNextFace() )
1210         if( pFontFace->IsBetterMatch( rFSD, aFontMatchStatus ) )
1211             pBestFontFace = pFontFace;
1212 
1213     return pBestFontFace;
1214 }
1215 
1216 // -----------------------------------------------------------------------
1217 
1218 // update device font list with unique font faces, with uniqueness
1219 // meaning different font attributes, but not different fonts sizes
1220 void ImplDevFontListData::UpdateDevFontList( ImplGetDevFontList& rDevFontList ) const
1221 {
1222     ImplFontData* pPrevFace = NULL;
1223     for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() )
1224     {
1225         if( !pPrevFace || pFace->CompareIgnoreSize( *pPrevFace ) )
1226             rDevFontList.Add( pFace );
1227         pPrevFace = pFace;
1228     }
1229 }
1230 
1231 // -----------------------------------------------------------------------
1232 
1233 void ImplDevFontListData::GetFontHeights( std::set<int>& rHeights ) const
1234 {
1235     // add all available font heights
1236     for( const ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() )
1237         rHeights.insert( pFace->GetHeight() );
1238 }
1239 
1240 // -----------------------------------------------------------------------
1241 
1242 void ImplDevFontListData::UpdateCloneFontList( ImplDevFontList& rDevFontList,
1243     bool bScalable, bool bEmbeddable ) const
1244 {
1245     for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() )
1246     {
1247         if( bScalable && !pFace->IsScalable() )
1248             continue;
1249         if( bEmbeddable && !pFace->IsEmbeddable() && !pFace->IsSubsettable() )
1250             continue;
1251 
1252         ImplFontData* pClonedFace = pFace->Clone();
1253         rDevFontList.Add( pClonedFace );
1254     }
1255 }
1256 
1257 // =======================================================================
1258 
1259 ImplDevFontList::ImplDevFontList()
1260 :   mbMatchData( false )
1261 ,   mbMapNames( false )
1262 ,   mpPreMatchHook( NULL )
1263 ,   mpFallbackHook( NULL )
1264 ,   mpFallbackList( NULL )
1265 ,   mnFallbackCount( -1 )
1266 {}
1267 
1268 // -----------------------------------------------------------------------
1269 
1270 ImplDevFontList::~ImplDevFontList()
1271 {
1272     Clear();
1273 }
1274 
1275 // -----------------------------------------------------------------------
1276 
1277 void ImplDevFontList::SetPreMatchHook( ImplPreMatchFontSubstitution* pHook )
1278 {
1279     mpPreMatchHook = pHook;
1280 }
1281 
1282 // -----------------------------------------------------------------------
1283 
1284 void ImplDevFontList::SetFallbackHook( ImplGlyphFallbackFontSubstitution* pHook )
1285 {
1286     mpFallbackHook = pHook;
1287 }
1288 
1289 // -----------------------------------------------------------------------
1290 
1291 void ImplDevFontList::Clear()
1292 {
1293     // remove fallback lists
1294     delete[] mpFallbackList;
1295     mpFallbackList = NULL;
1296     mnFallbackCount = -1;
1297 
1298     // clear all entries in the device font list
1299     DevFontList::iterator it = maDevFontList.begin();
1300     for(; it != maDevFontList.end(); ++it )
1301     {
1302         ImplDevFontListData* pEntry = (*it).second;
1303         delete pEntry;
1304     }
1305 
1306     maDevFontList.clear();
1307 
1308     // match data must be recalculated too
1309     mbMatchData = false;
1310 }
1311 
1312 
1313 // -----------------------------------------------------------------------
1314 
1315 void ImplDevFontList::InitGenericGlyphFallback( void ) const
1316 {
1317     // normalized family names of fonts suited for glyph fallback
1318     // if a font is available related fonts can be ignored
1319     // TODO: implement dynamic lists
1320     static const char* aGlyphFallbackList[] = {
1321         // empty strings separate the names of unrelated fonts
1322         "eudc", "",
1323         "arialunicodems", "cyberbit", "code2000", "",
1324         "andalesansui", "",
1325         "starsymbol", "opensymbol", "",
1326         "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "",
1327         "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "",
1328         "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "",
1329         "tahoma", "dejavusans", "timesnewroman", "liberationsans", "",
1330         "shree", "mangal", "",
1331         "raavi", "shruti", "tunga", "",
1332         "latha", "gautami", "kartika", "vrinda", "",
1333         "shayyalmt", "naskmt", "scheherazade", "",
1334         "david", "nachlieli", "lucidagrande", "",
1335         "norasi", "angsanaupc", "",
1336         "khmerossystem", "",
1337         "muktinarrow", "",
1338         "phetsarathot", "",
1339         "padauk", "pinlonmyanmar", "",
1340         "iskoolapota", "lklug", "",
1341         0
1342     };
1343 
1344     bool bHasEudc = false;
1345     int nMaxLevel = 0;
1346     int nBestQuality = 0;
1347     ImplDevFontListData** pFallbackList = NULL;
1348     for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames )
1349     {
1350         // advance to next sub-list when end-of-sublist marker
1351         if( !**ppNames )    // #i46456# check for empty string, i.e., deref string itself not only ptr to it
1352         {
1353             if( nBestQuality > 0 )
1354                 if( ++nMaxLevel >= MAX_FALLBACK )
1355                     break;
1356             if( !ppNames[1] )
1357                 break;
1358             nBestQuality = 0;
1359             continue;
1360         }
1361 
1362         // test if the glyph fallback candidate font is available and scalable
1363         String aTokenName( *ppNames, RTL_TEXTENCODING_UTF8 );
1364         ImplDevFontListData* pFallbackFont = FindFontFamily( aTokenName );
1365         if( !pFallbackFont )
1366             continue;
1367         if( !pFallbackFont->IsScalable() )
1368             continue;
1369 
1370         // keep the best font of the glyph fallback sub-list
1371         if( nBestQuality < pFallbackFont->GetMinQuality() )
1372         {
1373             nBestQuality = pFallbackFont->GetMinQuality();
1374             // store available glyph fallback fonts
1375             if( !pFallbackList )
1376                 pFallbackList = new ImplDevFontListData*[ MAX_FALLBACK ];
1377             pFallbackList[ nMaxLevel ] = pFallbackFont;
1378             if( !bHasEudc && !nMaxLevel )
1379                 bHasEudc = !strncmp( *ppNames, "eudc", 5 );
1380         }
1381     }
1382 
1383 #ifdef SAL_FONTENUM_STABLE_ON_PLATFORM // #i113472#
1384     // sort the list of fonts for glyph fallback by quality (highest first)
1385     // #i33947# keep the EUDC font at the front of the list
1386     // an insertion sort is good enough for this short list
1387     const int nSortStart = bHasEudc ? 1 : 0;
1388     for( int i = nSortStart+1, j; i < nMaxLevel; ++i )
1389     {
1390         ImplDevFontListData* pTestFont = pFallbackList[ i ];
1391         int nTestQuality = pTestFont->GetMinQuality();
1392         for( j = i; --j >= nSortStart; )
1393             if( nTestQuality > pFallbackList[j]->GetMinQuality() )
1394                 pFallbackList[ j+1 ] = pFallbackList[ j ];
1395             else
1396                 break;
1397         pFallbackList[ j+1 ] = pTestFont;
1398     }
1399 #endif
1400 
1401 #if defined(HDU_DEBUG)
1402     for( int i = 0; i < nMaxLevel; ++i )
1403     {
1404         ImplDevFontListData* pFont = pFallbackList[ i ];
1405         ByteString aFontName( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 );
1406         fprintf( stderr, "GlyphFallbackFont[%d] (quality=%05d): \"%s\"\n",
1407             i, pFont->GetMinQuality(), aFontName.GetBuffer() );
1408     }
1409 #endif
1410 
1411     mnFallbackCount = nMaxLevel;
1412     mpFallbackList  = pFallbackList;
1413 }
1414 
1415 // -----------------------------------------------------------------------
1416 
1417 ImplDevFontListData* ImplDevFontList::GetGlyphFallbackFont( ImplFontSelectData& rFontSelData,
1418     rtl::OUString& rMissingCodes, int nFallbackLevel ) const
1419 {
1420     ImplDevFontListData* pFallbackData = NULL;
1421 
1422     // find a matching font candidate for platform specific glyph fallback
1423     if( mpFallbackHook )
1424     {
1425         // check cache for the first matching entry
1426         // to avoid calling the expensive fallback hook (#i83491#)
1427         sal_UCS4 cChar = 0;
1428         bool bCached = true;
1429         sal_Int32 nStrIndex = 0;
1430         while( nStrIndex < rMissingCodes.getLength() )
1431         {
1432             cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
1433             bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName );
1434             // ignore entries which don't have a fallback
1435             if( !bCached || (rFontSelData.maSearchName.Len() != 0) )
1436                 break;
1437         }
1438 
1439         if( bCached )
1440         {
1441             // there is a matching fallback in the cache
1442             // so update rMissingCodes with codepoints not yet resolved by this fallback
1443             int nRemainingLength = 0;
1444             sal_UCS4* pRemainingCodes = (sal_UCS4*)alloca( rMissingCodes.getLength() * sizeof(sal_UCS4) );
1445             String aFontName;
1446             while( nStrIndex < rMissingCodes.getLength() )
1447             {
1448                 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
1449                 bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &aFontName );
1450                 if( !bCached || (rFontSelData.maSearchName != aFontName) )
1451                     pRemainingCodes[ nRemainingLength++ ] = cChar;
1452             }
1453             rMissingCodes = rtl::OUString( pRemainingCodes, nRemainingLength );
1454         }
1455         else
1456         {
1457             rtl::OUString aOldMissingCodes = rMissingCodes;
1458             // call the hook to query the best matching glyph fallback font
1459             if( mpFallbackHook->FindFontSubstitute( rFontSelData, rMissingCodes ) )
1460                 // apply outdev3.cxx specific fontname normalization
1461                 GetEnglishSearchFontName( rFontSelData.maSearchName );
1462             else
1463                 rFontSelData.maSearchName = String();
1464 
1465             // cache the result even if there was no match
1466             for(;;)
1467             {
1468                  if( !rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ) )
1469                      rFontSelData.mpFontEntry->AddFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName );
1470                  if( nStrIndex >= aOldMissingCodes.getLength() )
1471                      break;
1472                  cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex );
1473             }
1474             if( rFontSelData.maSearchName.Len() != 0 )
1475             {
1476                 // remove cache entries that were still not resolved
1477                 for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
1478                 {
1479                     cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
1480                     rFontSelData.mpFontEntry->IgnoreFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName );
1481                 }
1482             }
1483         }
1484 
1485         // find the matching device font
1486         if( rFontSelData.maSearchName.Len() != 0 )
1487             pFallbackData = FindFontFamily( rFontSelData.maSearchName );
1488     }
1489 
1490     // else find a matching font candidate for generic glyph fallback
1491     if( !pFallbackData )
1492     {
1493         // initialize font candidates for generic glyph fallback if needed
1494         if( mnFallbackCount < 0 )
1495             InitGenericGlyphFallback();
1496         // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook
1497         if( nFallbackLevel < mnFallbackCount )
1498             pFallbackData = mpFallbackList[ nFallbackLevel ];
1499     }
1500 
1501     return pFallbackData;
1502 }
1503 
1504 // -----------------------------------------------------------------------
1505 
1506 void ImplDevFontList::Add( ImplFontData* pNewData )
1507 {
1508     int nAliasQuality = pNewData->mnQuality - 100;
1509     String aMapNames = pNewData->maMapNames;
1510     pNewData->maMapNames = String();
1511 
1512     bool bKeepNewData = false;
1513     for( xub_StrLen nMapNameIndex = 0; nMapNameIndex != STRING_NOTFOUND; )
1514     {
1515         String aSearchName = pNewData->maName;
1516         GetEnglishSearchFontName( aSearchName );
1517 
1518         DevFontList::const_iterator it = maDevFontList.find( aSearchName );
1519         ImplDevFontListData* pFoundData = NULL;
1520         if( it != maDevFontList.end() )
1521             pFoundData = (*it).second;
1522 
1523         if( !pFoundData )
1524         {
1525             pFoundData = new ImplDevFontListData( aSearchName );
1526             maDevFontList[ aSearchName ] = pFoundData;
1527         }
1528 
1529         bKeepNewData = pFoundData->AddFontFace( pNewData );
1530 
1531         // add font alias if available
1532         // a font alias should never win against an original font with similar quality
1533         if( aMapNames.Len() <= nMapNameIndex )
1534             break;
1535         if( bKeepNewData ) // try to recycle obsoleted object
1536             pNewData = pNewData->CreateAlias();
1537         bKeepNewData = false;
1538         pNewData->mnQuality = nAliasQuality;
1539         pNewData->maName = GetNextFontToken( aMapNames, nMapNameIndex );
1540     }
1541 
1542     if( !bKeepNewData )
1543         delete pNewData;
1544 }
1545 
1546 // -----------------------------------------------------------------------
1547 
1548 // find the font from the normalized font family name
1549 ImplDevFontListData* ImplDevFontList::ImplFindBySearchName( const String& rSearchName ) const
1550 {
1551 #ifdef DEBUG
1552     String aTempName = rSearchName;
1553     GetEnglishSearchFontName( aTempName );
1554     DBG_ASSERT( aTempName == rSearchName, "ImplDevFontList::ImplFindBySearchName() called with non-normalized name" );
1555 #endif
1556 
1557     DevFontList::const_iterator it = maDevFontList.find( rSearchName );
1558     if( it == maDevFontList.end() )
1559         return NULL;
1560 
1561     ImplDevFontListData* pFoundData = (*it).second;
1562     return pFoundData;
1563 }
1564 
1565 // -----------------------------------------------------------------------
1566 
1567 ImplDevFontListData* ImplDevFontList::ImplFindByAliasName( const String& rSearchName, const String& rShortName ) const
1568 {
1569     // short circuit for impossible font name alias
1570     if( !rSearchName.Len() )
1571         return NULL;
1572 
1573     // short circuit if no alias names are available
1574     if( !mbMapNames )
1575         return NULL;
1576 
1577     // use the font's alias names to find the font
1578     // TODO: get rid of linear search
1579     DevFontList::const_iterator it = maDevFontList.begin();
1580     while( it != maDevFontList.end() )
1581     {
1582         ImplDevFontListData* pData = (*it).second;
1583         if( !pData->maMapNames.Len() )
1584             continue;
1585 
1586         // if one alias name matches we found a matching font
1587         String aTempName;
1588         xub_StrLen nIndex = 0;
1589         do
1590         {
1591            aTempName = GetNextFontToken( pData->maMapNames, nIndex );
1592            // Test, if the Font name match with one of the mapping names
1593            if ( (aTempName == rSearchName) || (aTempName == rShortName) )
1594               return pData;
1595         }
1596         while ( nIndex != STRING_NOTFOUND );
1597      }
1598 
1599      return NULL;
1600 }
1601 
1602 // -----------------------------------------------------------------------
1603 
1604 ImplDevFontListData* ImplDevFontList::FindFontFamily( const String& rFontName ) const
1605 {
1606     // normalize the font fomily name and
1607     String aName = rFontName;
1608     GetEnglishSearchFontName( aName );
1609     ImplDevFontListData* pFound = ImplFindBySearchName( aName );
1610     return pFound;
1611 }
1612 
1613 // -----------------------------------------------------------------------
1614 
1615 ImplDevFontListData* ImplDevFontList::ImplFindByTokenNames( const String& rTokenStr ) const
1616 {
1617     ImplDevFontListData* pFoundData = NULL;
1618 
1619     // use normalized font name tokens to find the font
1620     for( xub_StrLen nTokenPos = 0; nTokenPos != STRING_NOTFOUND; )
1621     {
1622         String aSearchName = GetNextFontToken( rTokenStr, nTokenPos );
1623         if( !aSearchName.Len() )
1624             continue;
1625         GetEnglishSearchFontName( aSearchName );
1626         pFoundData = ImplFindBySearchName( aSearchName );
1627         if( pFoundData )
1628             break;
1629     }
1630 
1631     return pFoundData;
1632 }
1633 
1634 // -----------------------------------------------------------------------
1635 
1636 ImplDevFontListData* ImplDevFontList::ImplFindBySubstFontAttr( const utl::FontNameAttr& rFontAttr ) const
1637 {
1638     ImplDevFontListData* pFoundData = NULL;
1639 
1640     // use the font substitutions suggested by the FontNameAttr to find the font
1641     ::std::vector< String >::const_iterator it = rFontAttr.Substitutions.begin();
1642     for(; it != rFontAttr.Substitutions.end(); ++it )
1643     {
1644         String aSearchName( *it );
1645         GetEnglishSearchFontName( aSearchName );
1646 
1647         pFoundData = ImplFindBySearchName( aSearchName );
1648         if( pFoundData )
1649             return pFoundData;
1650     }
1651 
1652 	// use known attributes from the configuration to find a matching substitute
1653 	const sal_uLong nSearchType = rFontAttr.Type;
1654 	if( nSearchType != 0 )
1655 	{
1656 		const FontWeight eSearchWeight = rFontAttr.Weight;
1657 		const FontWidth  eSearchWidth  = rFontAttr.Width;
1658 		const FontItalic eSearchSlant  = ITALIC_DONTKNOW;
1659 		const FontFamily eSearchFamily = FAMILY_DONTKNOW;
1660 		const String aSearchName;
1661 		pFoundData = ImplFindByAttributes( nSearchType,
1662 			eSearchWeight, eSearchWidth, eSearchFamily, eSearchSlant, aSearchName );
1663 		if( pFoundData )
1664 			return pFoundData;
1665 	}
1666 
1667 	return NULL;
1668 }
1669 
1670 // -----------------------------------------------------------------------
1671 
1672 void ImplDevFontList::InitMatchData() const
1673 {
1674     // short circuit if already done
1675     if( mbMatchData )
1676         return;
1677     mbMatchData = true;
1678 
1679     // calculate MatchData for all entries
1680     const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
1681 
1682     DevFontList::const_iterator it = maDevFontList.begin();
1683     for(; it != maDevFontList.end(); ++it )
1684     {
1685         const String& rSearchName = (*it).first;
1686         ImplDevFontListData* pEntry = (*it).second;
1687 
1688         pEntry->InitMatchData( rFontSubst, rSearchName );
1689     }
1690 }
1691 
1692 //----------------------------------------------------------------------------
1693 ImplDevFontListData* ImplDevFontList::ImplFindByLocale( com::sun::star::lang::Locale& rLocale ) const
1694 {
1695     // get the default font for a specified locale
1696 	const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get();
1697 	const String aDefault = rDefaults.getUserInterfaceFont( rLocale );
1698     ImplDevFontListData* pFontData = ImplFindByTokenNames( aDefault );
1699     if( pFontData )
1700         return pFontData;
1701 	return NULL;
1702 }
1703 
1704 // -----------------------------------------------------------------------
1705 
1706 ImplDevFontListData* ImplDevFontList::ImplFindByAttributes( sal_uLong nSearchType,
1707     FontWeight eSearchWeight, FontWidth eSearchWidth, FontFamily /*eSearchFamily*/,
1708     FontItalic eSearchItalic, const String& rSearchFamilyName ) const
1709 {
1710     if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) )
1711         nSearchType |= IMPL_FONT_ATTR_ITALIC;
1712 
1713     // don't bother to match attributes if the attributes aren't worth matching
1714     if( !nSearchType
1715     && ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL))
1716     && ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) )
1717         return NULL;
1718 
1719     InitMatchData();
1720     ImplDevFontListData* pFoundData = NULL;
1721 
1722     long    nTestMatch;
1723     long    nBestMatch = 40000;
1724     sal_uLong   nBestType = 0;
1725 
1726     DevFontList::const_iterator it = maDevFontList.begin();
1727     for(; it != maDevFontList.end(); ++it )
1728     {
1729         ImplDevFontListData* pData = (*it).second;
1730 
1731         // Get all information about the matching font
1732         sal_uLong       nMatchType  = pData->mnMatchType;
1733         FontWeight  eMatchWeight= pData->meMatchWeight;
1734         FontWidth   eMatchWidth = pData->meMatchWidth;
1735 
1736         // Calculate Match Value
1737         // 1000000000
1738         //  100000000
1739         //   10000000   CJK, CTL, None-Latin, Symbol
1740         //    1000000   FamilyName, Script, Fixed, -Special, -Decorative,
1741         //              Titling, Capitals, Outline, Shadow
1742         //     100000   Match FamilyName, Serif, SansSerif, Italic,
1743         //              Width, Weight
1744         //      10000   Scalable, Standard, Default,
1745         //              full, Normal, Knownfont,
1746         //              Otherstyle, +Special, +Decorative,
1747         //       1000   Typewriter, Rounded, Gothic, Schollbook
1748         //        100
1749         nTestMatch = 0;
1750 
1751         // test CJK script attributes
1752         if ( nSearchType & IMPL_FONT_ATTR_CJK )
1753         {
1754             // Matching language
1755             if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_CJK_ALLLANG) )
1756                 nTestMatch += 10000000*3;
1757             if( nMatchType & IMPL_FONT_ATTR_CJK )
1758                 nTestMatch += 10000000*2;
1759             if( nMatchType & IMPL_FONT_ATTR_FULL )
1760                 nTestMatch += 10000000;
1761         }
1762         else if ( nMatchType & IMPL_FONT_ATTR_CJK )
1763             nTestMatch -= 10000000;
1764 
1765         // test CTL script attributes
1766         if( nSearchType & IMPL_FONT_ATTR_CTL )
1767         {
1768             if( nMatchType & IMPL_FONT_ATTR_CTL )
1769                 nTestMatch += 10000000*2;
1770             if( nMatchType & IMPL_FONT_ATTR_FULL )
1771                 nTestMatch += 10000000;
1772         }
1773         else if ( nMatchType & IMPL_FONT_ATTR_CTL )
1774             nTestMatch -= 10000000;
1775 
1776         // test LATIN script attributes
1777         if( nSearchType & IMPL_FONT_ATTR_NONELATIN )
1778         {
1779             if( nMatchType & IMPL_FONT_ATTR_NONELATIN )
1780                 nTestMatch += 10000000*2;
1781             if( nMatchType & IMPL_FONT_ATTR_FULL )
1782                 nTestMatch += 10000000;
1783         }
1784 
1785         // test SYMBOL attributes
1786         if ( nSearchType & IMPL_FONT_ATTR_SYMBOL )
1787         {
1788             const String& rSearchName = it->first;
1789             // prefer some special known symbol fonts
1790             if ( rSearchName.EqualsAscii( "starsymbol" ) )
1791                 nTestMatch += 10000000*6+(10000*3);
1792             else if ( rSearchName.EqualsAscii( "opensymbol" ) )
1793                 nTestMatch += 10000000*6;
1794             else if ( rSearchName.EqualsAscii( "starbats" )
1795             ||        rSearchName.EqualsAscii( "wingdings" )
1796             ||        rSearchName.EqualsAscii( "monotypesorts" )
1797             ||        rSearchName.EqualsAscii( "dingbats" )
1798             ||        rSearchName.EqualsAscii( "zapfdingbats" ) )
1799                 nTestMatch += 10000000*5;
1800             else if ( pData->mnTypeFaces & IMPL_DEVFONT_SYMBOL )
1801                 nTestMatch += 10000000*4;
1802             else
1803             {
1804                 if( nMatchType & IMPL_FONT_ATTR_SYMBOL )
1805                     nTestMatch += 10000000*2;
1806                 if( nMatchType & IMPL_FONT_ATTR_FULL )
1807                     nTestMatch += 10000000;
1808             }
1809         }
1810         else if ( (pData->mnTypeFaces & (IMPL_DEVFONT_SYMBOL | IMPL_DEVFONT_NONESYMBOL)) == IMPL_DEVFONT_SYMBOL )
1811             nTestMatch -= 10000000;
1812         else if ( nMatchType & IMPL_FONT_ATTR_SYMBOL )
1813             nTestMatch -= 10000;
1814 
1815         // match stripped family name
1816         if( rSearchFamilyName.Len() && (rSearchFamilyName == pData->maMatchFamilyName) )
1817             nTestMatch += 1000000*3;
1818 
1819         // match ALLSCRIPT? attribute
1820         if( nSearchType & IMPL_FONT_ATTR_ALLSCRIPT )
1821         {
1822             if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT )
1823                 nTestMatch += 1000000*2;
1824             if( nSearchType & IMPL_FONT_ATTR_ALLSUBSCRIPT )
1825             {
1826                 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ALLSUBSCRIPT) )
1827                     nTestMatch += 1000000*2;
1828                 if( 0 != ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_BRUSHSCRIPT) )
1829                     nTestMatch -= 1000000;
1830             }
1831         }
1832         else if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT )
1833             nTestMatch -= 1000000;
1834 
1835         // test MONOSPACE+TYPEWRITER attributes
1836         if( nSearchType & IMPL_FONT_ATTR_FIXED )
1837         {
1838             if( nMatchType & IMPL_FONT_ATTR_FIXED )
1839                 nTestMatch += 1000000*2;
1840             // a typewriter attribute is even better
1841             if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) )
1842                 nTestMatch += 10000*2;
1843         }
1844         else if( nMatchType & IMPL_FONT_ATTR_FIXED )
1845             nTestMatch -= 1000000;
1846 
1847         // test SPECIAL attribute
1848         if( nSearchType & IMPL_FONT_ATTR_SPECIAL )
1849         {
1850             if( nMatchType & IMPL_FONT_ATTR_SPECIAL )
1851                 nTestMatch += 10000;
1852             else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) )
1853             {
1854                  if( nMatchType & IMPL_FONT_ATTR_SERIF )
1855                      nTestMatch += 1000*2;
1856                  else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
1857                      nTestMatch += 1000;
1858              }
1859         }
1860         else if( (nMatchType & IMPL_FONT_ATTR_SPECIAL) && !(nSearchType & IMPL_FONT_ATTR_SYMBOL) )
1861             nTestMatch -= 1000000;
1862 
1863         // test DECORATIVE attribute
1864         if( nSearchType & IMPL_FONT_ATTR_DECORATIVE )
1865         {
1866             if( nMatchType & IMPL_FONT_ATTR_DECORATIVE )
1867                 nTestMatch += 10000;
1868             else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) )
1869             {
1870                 if( nMatchType & IMPL_FONT_ATTR_SERIF )
1871                     nTestMatch += 1000*2;
1872                 else if ( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
1873                     nTestMatch += 1000;
1874             }
1875         }
1876         else if( nMatchType & IMPL_FONT_ATTR_DECORATIVE )
1877             nTestMatch -= 1000000;
1878 
1879         // test TITLE+CAPITALS attributes
1880         if( nSearchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) )
1881         {
1882             if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) )
1883                 nTestMatch += 1000000*2;
1884             if( 0 == ((nSearchType^nMatchType) & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS)))
1885                 nTestMatch += 1000000;
1886             else if( (nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS))
1887             &&       (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) )
1888                 nTestMatch += 1000000;
1889         }
1890         else if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) )
1891             nTestMatch -= 1000000;
1892 
1893         // test OUTLINE+SHADOW attributes
1894         if( nSearchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) )
1895         {
1896             if( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) )
1897                 nTestMatch += 1000000*2;
1898             if( 0 == ((nSearchType ^ nMatchType) & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) )
1899                 nTestMatch += 1000000;
1900             else if( (nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW))
1901             &&       (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) )
1902                 nTestMatch += 1000000;
1903         }
1904         else if ( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) )
1905             nTestMatch -= 1000000;
1906 
1907         // test font name substrings
1908 	// TODO: calculate name matching score using e.g. Levenstein distance
1909         if( (rSearchFamilyName.Len() >= 4) && (pData->maMatchFamilyName.Len() >= 4)
1910         &&    ((rSearchFamilyName.Search( pData->maMatchFamilyName ) != STRING_NOTFOUND)
1911             || (pData->maMatchFamilyName.Search( rSearchFamilyName ) != STRING_NOTFOUND)) )
1912                     nTestMatch += 5000;
1913 
1914         // test SERIF attribute
1915         if( nSearchType & IMPL_FONT_ATTR_SERIF )
1916         {
1917             if( nMatchType & IMPL_FONT_ATTR_SERIF )
1918                 nTestMatch += 1000000*2;
1919             else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
1920                 nTestMatch -= 1000000;
1921         }
1922 
1923         // test SANSERIF attribute
1924         if( nSearchType & IMPL_FONT_ATTR_SANSSERIF )
1925         {
1926             if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
1927                 nTestMatch += 1000000;
1928             else if ( nMatchType & IMPL_FONT_ATTR_SERIF )
1929                 nTestMatch -= 1000000;
1930         }
1931 
1932         // test ITALIC attribute
1933         if( nSearchType & IMPL_FONT_ATTR_ITALIC )
1934         {
1935             if( pData->mnTypeFaces & IMPL_DEVFONT_ITALIC )
1936                 nTestMatch += 1000000*3;
1937             if( nMatchType & IMPL_FONT_ATTR_ITALIC )
1938                 nTestMatch += 1000000;
1939         }
1940         else if( !(nSearchType & IMPL_FONT_ATTR_ALLSCRIPT)
1941             &&  ((nMatchType & IMPL_FONT_ATTR_ITALIC)
1942                 || !(pData->mnTypeFaces & IMPL_DEVFONT_NONEITALIC)) )
1943             nTestMatch -= 1000000*2;
1944 
1945         // test WIDTH attribute
1946         if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) )
1947         {
1948             if( eSearchWidth < WIDTH_NORMAL )
1949             {
1950                 if( eSearchWidth == eMatchWidth )
1951                     nTestMatch += 1000000*3;
1952                 else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) )
1953                     nTestMatch += 1000000;
1954             }
1955             else
1956             {
1957                 if( eSearchWidth == eMatchWidth )
1958                     nTestMatch += 1000000*3;
1959                 else if( eMatchWidth > WIDTH_NORMAL )
1960                     nTestMatch += 1000000;
1961             }
1962         }
1963         else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) )
1964             nTestMatch -= 1000000;
1965 
1966         // test WEIGHT attribute
1967         if( (eSearchWeight != WEIGHT_DONTKNOW) && (eSearchWeight != WEIGHT_NORMAL) && (eSearchWeight != WEIGHT_MEDIUM) )
1968         {
1969             if( eSearchWeight < WEIGHT_NORMAL )
1970             {
1971                 if( pData->mnTypeFaces & IMPL_DEVFONT_LIGHT )
1972                     nTestMatch += 1000000;
1973                 if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) )
1974                     nTestMatch += 1000000;
1975             }
1976             else
1977             {
1978                 if( pData->mnTypeFaces & IMPL_DEVFONT_BOLD )
1979                     nTestMatch += 1000000;
1980                 if( eMatchWeight > WEIGHT_BOLD )
1981                     nTestMatch += 1000000;
1982             }
1983         }
1984         else if( ((eMatchWeight != WEIGHT_DONTKNOW) && (eMatchWeight != WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_MEDIUM))
1985             || !(pData->mnTypeFaces & IMPL_DEVFONT_NORMAL) )
1986             nTestMatch -= 1000000;
1987 
1988         // prefer scalable fonts
1989         if( pData->mnTypeFaces & IMPL_DEVFONT_SCALABLE )
1990             nTestMatch += 10000*4;
1991         else
1992             nTestMatch -= 10000*4;
1993 
1994         // test STANDARD+DEFAULT+FULL+NORMAL attributes
1995         if( nMatchType & IMPL_FONT_ATTR_STANDARD )
1996             nTestMatch += 10000*2;
1997         if( nMatchType & IMPL_FONT_ATTR_DEFAULT )
1998             nTestMatch += 10000;
1999         if( nMatchType & IMPL_FONT_ATTR_FULL )
2000             nTestMatch += 10000;
2001         if( nMatchType & IMPL_FONT_ATTR_NORMAL )
2002             nTestMatch += 10000;
2003 
2004         // test OTHERSTYLE attribute
2005         if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE )
2006         {
2007             if( !(nMatchType & IMPL_FONT_ATTR_OTHERSTYLE) )
2008                 nTestMatch -= 10000;
2009         }
2010         else if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE )
2011             nTestMatch -= 10000;
2012 
2013         // test ROUNDED attribute
2014         if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ROUNDED) )
2015             nTestMatch += 1000;
2016 
2017         // test TYPEWRITER attribute
2018         if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) )
2019             nTestMatch += 1000;
2020 
2021         // test GOTHIC attribute
2022         if( nSearchType & IMPL_FONT_ATTR_GOTHIC )
2023         {
2024             if( nMatchType & IMPL_FONT_ATTR_GOTHIC )
2025                 nTestMatch += 1000*3;
2026             if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
2027                 nTestMatch += 1000*2;
2028         }
2029 
2030         // test SCHOOLBOOK attribute
2031         if( nSearchType & IMPL_FONT_ATTR_SCHOOLBOOK )
2032         {
2033             if( nMatchType & IMPL_FONT_ATTR_SCHOOLBOOK )
2034                 nTestMatch += 1000*3;
2035             if( nMatchType & IMPL_FONT_ATTR_SERIF )
2036                 nTestMatch += 1000*2;
2037         }
2038 
2039         // compare with best matching font yet
2040         if ( nTestMatch > nBestMatch )
2041         {
2042             pFoundData  = pData;
2043             nBestMatch  = nTestMatch;
2044             nBestType   = nMatchType;
2045         }
2046         else if( nTestMatch == nBestMatch )
2047         {
2048             // some fonts are more suitable defaults
2049             if( nMatchType & IMPL_FONT_ATTR_DEFAULT )
2050             {
2051                 pFoundData  = pData;
2052                 nBestType   = nMatchType;
2053             }
2054             else if( (nMatchType & IMPL_FONT_ATTR_STANDARD) &&
2055                     !(nBestType & IMPL_FONT_ATTR_DEFAULT) )
2056             {
2057                  pFoundData  = pData;
2058                  nBestType   = nMatchType;
2059             }
2060         }
2061     }
2062 
2063     return pFoundData;
2064 }
2065 
2066 // -----------------------------------------------------------------------
2067 
2068 ImplDevFontListData* ImplDevFontList::FindDefaultFont() const
2069 {
2070     // try to find one of the default fonts of the
2071     // UNICODE, SANSSERIF, SERIF or FIXED default font lists
2072     const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get();
2073     com::sun::star::lang::Locale aLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
2074     String aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS_UNICODE );
2075     ImplDevFontListData* pFoundData = ImplFindByTokenNames( aFontname );
2076     if( pFoundData )
2077         return pFoundData;
2078 
2079     aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS );
2080     pFoundData = ImplFindByTokenNames( aFontname );
2081     if( pFoundData )
2082         return pFoundData;
2083 
2084     aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SERIF );
2085     pFoundData = ImplFindByTokenNames( aFontname );
2086     if( pFoundData )
2087         return pFoundData;
2088 
2089     aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_FIXED );
2090     pFoundData = ImplFindByTokenNames( aFontname );
2091     if( pFoundData )
2092         return pFoundData;
2093 
2094     // now try to find a reasonable non-symbol font
2095 
2096     InitMatchData();
2097 
2098     DevFontList::const_iterator it = maDevFontList.begin();
2099     for(; it !=  maDevFontList.end(); ++it )
2100     {
2101         ImplDevFontListData* pData = (*it).second;
2102         if( pData->mnMatchType & IMPL_FONT_ATTR_SYMBOL )
2103             continue;
2104         pFoundData = pData;
2105         if( pData->mnMatchType & (IMPL_FONT_ATTR_DEFAULT|IMPL_FONT_ATTR_STANDARD) )
2106             break;
2107     }
2108     if( pFoundData )
2109         return pFoundData;
2110 
2111     // finding any font is better than finding no font at all
2112     it = maDevFontList.begin();
2113     if( it !=  maDevFontList.end() )
2114         pFoundData = (*it).second;
2115 
2116     return pFoundData;
2117 }
2118 
2119 // -----------------------------------------------------------------------
2120 
2121 ImplDevFontList* ImplDevFontList::Clone( bool bScalable, bool bEmbeddable ) const
2122 {
2123     ImplDevFontList* pClonedList = new ImplDevFontList;
2124 //  pClonedList->mbMatchData    = mbMatchData;
2125     pClonedList->mbMapNames     = mbMapNames;
2126     pClonedList->mpPreMatchHook = mpPreMatchHook;
2127     pClonedList->mpFallbackHook = mpFallbackHook;
2128 
2129     // TODO: clone the config-font attributes too?
2130     pClonedList->mbMatchData    = false;
2131 
2132     DevFontList::const_iterator it = maDevFontList.begin();
2133     for(; it != maDevFontList.end(); ++it )
2134     {
2135         const ImplDevFontListData* pFontFace = (*it).second;
2136         pFontFace->UpdateCloneFontList( *pClonedList, bScalable, bEmbeddable );
2137     }
2138 
2139     return pClonedList;
2140 }
2141 
2142 // -----------------------------------------------------------------------
2143 
2144 ImplGetDevFontList* ImplDevFontList::GetDevFontList() const
2145 {
2146     ImplGetDevFontList* pGetDevFontList = new ImplGetDevFontList;
2147 
2148     DevFontList::const_iterator it = maDevFontList.begin();
2149     for(; it != maDevFontList.end(); ++it )
2150     {
2151         const ImplDevFontListData* pFontFamily = (*it).second;
2152         pFontFamily->UpdateDevFontList( *pGetDevFontList );
2153     }
2154 
2155     return pGetDevFontList;
2156 }
2157 
2158 // -----------------------------------------------------------------------
2159 
2160 ImplGetDevSizeList* ImplDevFontList::GetDevSizeList( const String& rFontName ) const
2161 {
2162     ImplGetDevSizeList* pGetDevSizeList = new ImplGetDevSizeList( rFontName );
2163 
2164     ImplDevFontListData* pFontFamily = FindFontFamily( rFontName );
2165     if( pFontFamily != NULL )
2166     {
2167         std::set<int> rHeights;
2168         pFontFamily->GetFontHeights( rHeights );
2169 
2170         std::set<int>::const_iterator it = rHeights.begin();
2171         for(; it != rHeights.begin(); ++it )
2172             pGetDevSizeList->Add( *it );
2173     }
2174 
2175     return pGetDevSizeList;
2176 }
2177 
2178 // =======================================================================
2179 
2180 ImplFontSelectData::ImplFontSelectData( const Font& rFont,
2181     const String& rSearchName, const Size& rSize, float fExactHeight)
2182 :   maSearchName( rSearchName ),
2183     mnWidth( rSize.Width() ),
2184     mnHeight( rSize.Height() ),
2185     mfExactHeight( fExactHeight),
2186     mnOrientation( rFont.GetOrientation() ),
2187     meLanguage( rFont.GetLanguage() ),
2188     mbVertical( rFont.IsVertical() ),
2189     mbNonAntialiased( false ),
2190     mpFontData( NULL ),
2191     mpFontEntry( NULL )
2192 {
2193     maTargetName = maName;
2194 
2195     rFont.GetFontAttributes( *this );
2196 
2197     // normalize orientation between 0 and 3600
2198     if( 3600 <= (unsigned)mnOrientation )
2199     {
2200         if( mnOrientation >= 0 )
2201             mnOrientation %= 3600;
2202         else
2203             mnOrientation = 3600 - (-mnOrientation % 3600);
2204     }
2205 
2206     // normalize width and height
2207     if( mnHeight < 0 )
2208         mnHeight = -mnHeight;
2209     if( mnWidth < 0 )
2210         mnWidth = -mnWidth;
2211 }
2212 
2213 // -----------------------------------------------------------------------
2214 
2215 ImplFontSelectData::ImplFontSelectData( const ImplFontData& rFontData,
2216     const Size& rSize, float fExactHeight, int nOrientation, bool bVertical )
2217 :   ImplFontAttributes( rFontData ),
2218     mnWidth( rSize.Width() ),
2219     mnHeight( rSize.Height() ),
2220     mfExactHeight( fExactHeight ),
2221     mnOrientation( nOrientation ),
2222     meLanguage( 0 ),
2223     mbVertical( bVertical ),
2224     mbNonAntialiased( false ),
2225     mpFontData( &rFontData ),
2226     mpFontEntry( NULL )
2227 {
2228     maTargetName = maSearchName = maName;
2229     // NOTE: no normalization for width/height/orientation
2230 }
2231 
2232 // =======================================================================
2233 
2234 size_t ImplFontCache::IFSD_Hash::operator()( const ImplFontSelectData& rFSD ) const
2235 {
2236     // TODO: does it pay off to improve this hash function?
2237     static FontNameHash aFontNameHash;
2238     size_t nHash = aFontNameHash( rFSD.maSearchName );
2239 #ifdef ENABLE_GRAPHITE
2240     // check for features and generate a unique hash if necessary
2241     if (rFSD.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2242         != STRING_NOTFOUND)
2243     {
2244         nHash = aFontNameHash( rFSD.maTargetName );
2245     }
2246 #endif
2247     nHash += 11 * rFSD.mnHeight;
2248     nHash += 19 * rFSD.meWeight;
2249     nHash += 29 * rFSD.meItalic;
2250     nHash += 37 * rFSD.mnOrientation;
2251     nHash += 41 * rFSD.meLanguage;
2252     if( rFSD.mbVertical )
2253         nHash += 53;
2254     return nHash;
2255 }
2256 
2257 // -----------------------------------------------------------------------
2258 
2259 bool ImplFontCache::IFSD_Equal::operator()(const ImplFontSelectData& rA, const ImplFontSelectData& rB) const
2260 {
2261     // check normalized font family name
2262     if( rA.maSearchName != rB.maSearchName )
2263         return false;
2264 
2265     // check font transformation
2266     if( (rA.mnHeight       != rB.mnHeight)
2267     ||  (rA.mnWidth        != rB.mnWidth)
2268     ||  (rA.mnOrientation  != rB.mnOrientation) )
2269         return false;
2270 
2271     // check mapping relevant attributes
2272     if( (rA.mbVertical     != rB.mbVertical)
2273     ||  (rA.meLanguage     != rB.meLanguage) )
2274         return false;
2275 
2276     // check font face attributes
2277     if( (rA.meWeight       != rB.meWeight)
2278     ||  (rA.meItalic       != rB.meItalic)
2279 //    ||  (rA.meFamily       != rB.meFamily) // TODO: remove this mostly obsolete member
2280     ||  (rA.mePitch        != rB.mePitch) )
2281         return false;
2282 
2283     // check style name
2284     if( rA.maStyleName != rB.maStyleName)
2285         return false;
2286 
2287     // Symbol fonts may recode from one type to another So they are only
2288     // safely equivalent for equal targets
2289     if (
2290         (rA.mpFontData && rA.mpFontData->IsSymbolFont()) ||
2291         (rB.mpFontData && rB.mpFontData->IsSymbolFont())
2292        )
2293     {
2294         if (rA.maTargetName != rB.maTargetName)
2295             return false;
2296     }
2297 
2298 #ifdef ENABLE_GRAPHITE
2299     // check for features
2300     if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2301          != STRING_NOTFOUND ||
2302          rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2303          != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
2304         return false;
2305 #endif
2306 
2307     return true;
2308 }
2309 
2310 // -----------------------------------------------------------------------
2311 
2312 ImplFontCache::ImplFontCache( bool bPrinter )
2313 :   mpFirstEntry( NULL ),
2314     mnRef0Count( 0 ),
2315     mbPrinter( bPrinter )
2316 {}
2317 
2318 // -----------------------------------------------------------------------
2319 
2320 ImplFontCache::~ImplFontCache()
2321 {
2322     FontInstanceList::iterator it = maFontInstanceList.begin();
2323     for(; it != maFontInstanceList.end(); ++it )
2324     {
2325         ImplFontEntry* pEntry = (*it).second;
2326         delete pEntry;
2327     }
2328 }
2329 
2330 // -----------------------------------------------------------------------
2331 
2332 ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
2333     const Font& rFont, const Size& rSize, float fExactHeight, ImplDirectFontSubstitution* pDevSpecific )
2334 {
2335     String aSearchName = rFont.GetName();
2336 
2337     // TODO: also add device specific name caching
2338     if( !pDevSpecific )
2339     {
2340         // check if the requested font name is already known
2341         // if it is already known get its normalized search name
2342         FontNameList::const_iterator it_name = maFontNameList.find( aSearchName );
2343         if( it_name != maFontNameList.end() )
2344             if( !(*it_name).second.EqualsAscii( "hg", 0, 2)
2345 #ifdef ENABLE_GRAPHITE
2346                 && (aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2347                     == STRING_NOTFOUND)
2348 #endif
2349             )
2350                 aSearchName = (*it_name).second;
2351     }
2352 
2353     // initialize internal font request object
2354     ImplFontSelectData aFontSelData( rFont, aSearchName, rSize, fExactHeight );
2355     return GetFontEntry( pFontList, aFontSelData, pDevSpecific );
2356 }
2357 
2358 // -----------------------------------------------------------------------
2359 
2360 ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
2361     ImplFontSelectData& aFontSelData, ImplDirectFontSubstitution* pDevSpecific )
2362 {
2363     // check if a directly matching logical font instance is already cached,
2364     // the most recently used font usually has a hit rate of >50%
2365     ImplFontEntry *pEntry = NULL;
2366     ImplDevFontListData* pFontFamily = NULL;
2367     IFSD_Equal aIFSD_Equal;
2368     if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) )
2369         pEntry = mpFirstEntry;
2370     else
2371     {
2372         FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
2373         if( it != maFontInstanceList.end() )
2374             pEntry = (*it).second;
2375     }
2376 
2377     if( !pEntry ) // no direct cache hit
2378     {
2379         // find the best matching logical font family and update font selector accordingly
2380         pFontFamily = pFontList->ImplFindByFont( aFontSelData, mbPrinter, pDevSpecific );
2381         DBG_ASSERT( (pFontFamily != NULL), "ImplFontCache::Get() No logical font found!" );
2382         if( pFontFamily )
2383             aFontSelData.maSearchName = pFontFamily->GetSearchName();
2384 
2385         // check if an indirectly matching logical font instance is already cached
2386         FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
2387         if( it != maFontInstanceList.end() )
2388         {
2389             // we have an indirect cache hit
2390             pEntry = (*it).second;
2391             // cache the requested and the selected font names
2392             // => next time there is a good chance for a direct cache hit
2393             // don't allow the cache to grow too big
2394             // TODO: implement some fancy LRU caching?
2395             if( maFontNameList.size() >= 4000 )
2396                 maFontNameList.clear();
2397             // TODO: also add device specific name caching
2398             if( !pDevSpecific )
2399                 if( aFontSelData.maName != aFontSelData.maSearchName )
2400                     maFontNameList[ aFontSelData.maName ] = aFontSelData.maSearchName;
2401         }
2402     }
2403 
2404     if( pEntry ) // cache hit => use existing font instance
2405     {
2406         // increase the font instance's reference count
2407         if( !pEntry->mnRefCount++ )
2408             --mnRef0Count;
2409     }
2410     else // no cache hit => create a new font instance
2411     {
2412         // find the best matching physical font face
2413         ImplFontData* pFontData = pFontFamily->FindBestFontFace( aFontSelData );
2414         aFontSelData.mpFontData = pFontData;
2415 
2416         // create a new logical font instance from this physical font face
2417         pEntry = pFontData->CreateFontInstance( aFontSelData );
2418 
2419         // if we found a different symbol font we need a symbol conversion table
2420         if( pFontData->IsSymbolFont() )
2421             if( aFontSelData.maTargetName != aFontSelData.maSearchName )
2422                 pEntry->mpConversion = ConvertChar::GetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
2423 
2424         // add the new entry to the cache
2425         maFontInstanceList[ aFontSelData ] = pEntry;
2426     }
2427 
2428     mpFirstEntry = pEntry;
2429     return pEntry;
2430 }
2431 
2432 // -----------------------------------------------------------------------
2433 
2434 ImplDevFontListData* ImplDevFontList::ImplFindByFont( ImplFontSelectData& rFSD,
2435     bool bPrinter, ImplDirectFontSubstitution* pDevSpecific ) const
2436 {
2437     // give up if no fonts are available
2438     if( !Count() )
2439         return NULL;
2440 
2441     // test if a font in the token list is available
2442     // substitute the font if this was requested
2443     sal_uInt16 nSubstFlags = FONT_SUBSTITUTE_ALWAYS;
2444     if ( bPrinter )
2445         nSubstFlags |= FONT_SUBSTITUTE_SCREENONLY;
2446 
2447     bool bMultiToken = false;
2448     xub_StrLen nTokenPos = 0;
2449     String& aSearchName = rFSD.maSearchName; // TODO: get rid of reference
2450     for(;;)
2451     {
2452         rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
2453         aSearchName = rFSD.maTargetName;
2454 
2455 #ifdef ENABLE_GRAPHITE
2456         // Until features are properly supported, they are appended to the
2457         // font name, so we need to strip them off so the font is found.
2458         xub_StrLen nFeat = aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX);
2459         String aOrigName = rFSD.maTargetName;
2460         String aBaseFontName(aSearchName, 0, (nFeat != STRING_NOTFOUND)?nFeat:aSearchName.Len());
2461         if (nFeat != STRING_NOTFOUND && STRING_NOTFOUND !=
2462             aSearchName.Search(grutils::GrFeatureParser::FEAT_ID_VALUE_SEPARATOR, nFeat))
2463         {
2464             aSearchName = aBaseFontName;
2465             rFSD.maTargetName = aBaseFontName;
2466         }
2467 
2468 #endif
2469 
2470         GetEnglishSearchFontName( aSearchName );
2471         ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
2472         // #114999# special emboldening for Ricoh fonts
2473         // TODO: smarter check for special cases by using PreMatch infrastructure?
2474         if( (rFSD.meWeight > WEIGHT_MEDIUM)
2475         &&  aSearchName.EqualsAscii( "hg", 0, 2) )
2476         {
2477             String aBoldName;
2478             if( aSearchName.EqualsAscii( "hggothicb", 0, 9) )
2479                 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hggothice"));
2480             else if( aSearchName.EqualsAscii( "hgpgothicb", 0, 10) )
2481                 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpgothice"));
2482             else if( aSearchName.EqualsAscii( "hgminchol", 0, 9) )
2483                 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchob"));
2484             else if( aSearchName.EqualsAscii( "hgpminchol", 0, 10) )
2485                 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchob"));
2486             else if( aSearchName.EqualsAscii( "hgminchob" ) )
2487                 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchoe"));
2488             else if( aSearchName.EqualsAscii( "hgpminchob" ) )
2489                 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchoe"));
2490 
2491             if( aBoldName.Len() && ImplFindBySearchName( aBoldName ) )
2492             {
2493                 // the other font is available => use it
2494                 aSearchName = aBoldName;
2495                 // prevent synthetic emboldening of bold version
2496                 rFSD.meWeight = WEIGHT_DONTKNOW;
2497             }
2498         }
2499 
2500 #ifdef ENABLE_GRAPHITE
2501 		// restore the features to make the font selection data unique
2502 		rFSD.maTargetName = aOrigName;
2503 #endif
2504         // check if the current font name token or its substitute is valid
2505         ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
2506         if( pFoundData )
2507             return pFoundData;
2508 
2509         // some systems provide special customization
2510         // e.g. they suggest "serif" as UI-font, but this name cannot be used directly
2511         //      because the system wants to map it to another font first, e.g. "Helvetica"
2512 #ifdef ENABLE_GRAPHITE
2513 		// use the target name to search in the prematch hook
2514 		rFSD.maTargetName = aBaseFontName;
2515 #endif
2516         if( mpPreMatchHook )
2517             if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
2518                 GetEnglishSearchFontName( aSearchName );
2519 #ifdef ENABLE_GRAPHITE
2520 	    // the prematch hook uses the target name to search, but we now need
2521 	    // to restore the features to make the font selection data unique
2522 	    rFSD.maTargetName = aOrigName;
2523 #endif
2524 		pFoundData = ImplFindBySearchName( aSearchName );
2525 		if( pFoundData )
2526 			return pFoundData;
2527 
2528         // break after last font name token was checked unsuccessfully
2529         if( nTokenPos == STRING_NOTFOUND)
2530             break;
2531         bMultiToken = true;
2532     }
2533 
2534     // if the first font was not available find the next available font in
2535     // the semicolon separated list of font names. A font is also considered
2536     // available when there is a matching entry in the Tools->Options->Fonts
2537     // dialog witho neither ALWAYS nor SCREENONLY flags set and the substitution
2538     // font is available
2539     for( nTokenPos = 0; nTokenPos != STRING_NOTFOUND; )
2540     {
2541         if( bMultiToken )
2542         {
2543             rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
2544             aSearchName = rFSD.maTargetName;
2545             GetEnglishSearchFontName( aSearchName );
2546         }
2547         else
2548             nTokenPos = STRING_NOTFOUND;
2549         if( mpPreMatchHook )
2550             if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
2551                 GetEnglishSearchFontName( aSearchName );
2552         ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
2553         ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
2554         if( pFoundData )
2555             return pFoundData;
2556     }
2557 
2558     // if no font with a directly matching name is available use the
2559     // first font name token and get its attributes to find a replacement
2560     if ( bMultiToken )
2561     {
2562         nTokenPos = 0;
2563         rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
2564         aSearchName = rFSD.maTargetName;
2565         GetEnglishSearchFontName( aSearchName );
2566     }
2567 
2568     String      aSearchShortName;
2569     String      aSearchFamilyName;
2570     FontWeight  eSearchWeight   = rFSD.meWeight;
2571     FontWidth   eSearchWidth    = rFSD.meWidthType;
2572     sal_uLong       nSearchType     = 0;
2573     FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName,
2574                                         eSearchWeight, eSearchWidth, nSearchType );
2575 
2576     // note: the search name was already translated to english (if possible)
2577 
2578     // use the font's shortened name if needed
2579     if ( aSearchShortName != aSearchName )
2580     {
2581        ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchShortName );
2582        if( pFoundData )
2583        {
2584 #ifdef UNX
2585             /* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is
2586             a korean bitmap font that is not suitable here. Use the font replacement table,
2587             that automatically leads to the desired "HG Mincho Light J". Same story for
2588             MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */
2589             static String aMS_Mincho( RTL_CONSTASCII_USTRINGPARAM("msmincho") );
2590             static String aMS_Gothic( RTL_CONSTASCII_USTRINGPARAM("msgothic") );
2591             if ((aSearchName != aMS_Mincho) && (aSearchName != aMS_Gothic))
2592                 // TODO: add heuristic to only throw out the fake ms* fonts
2593 #endif
2594             {
2595                 return pFoundData;
2596             }
2597         }
2598     }
2599 
2600     // use font fallback
2601     const FontNameAttr* pFontAttr = NULL;
2602     if( aSearchName.Len() )
2603     {
2604         // get fallback info using FontSubstConfiguration and
2605         // the target name, it's shortened name and family name in that order
2606         const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
2607         pFontAttr = rFontSubst.getSubstInfo( aSearchName );
2608         if ( !pFontAttr && (aSearchShortName != aSearchName) )
2609             pFontAttr = rFontSubst.getSubstInfo( aSearchShortName );
2610         if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) )
2611             pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName );
2612 
2613         // try the font substitutions suggested by the fallback info
2614         if( pFontAttr )
2615         {
2616             ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pFontAttr );
2617             if( pFoundData )
2618                 return pFoundData;
2619         }
2620     }
2621 
2622     // if a target symbol font is not available use a default symbol font
2623     if( rFSD.IsSymbolFont() )
2624     {
2625         com::sun::star::lang::Locale aDefaultLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
2626         aSearchName = DefaultFontConfiguration::get()->getDefaultFont( aDefaultLocale, DEFAULTFONT_SYMBOL );
2627         ImplDevFontListData* pFoundData = ImplFindByTokenNames( aSearchName );
2628         if( pFoundData )
2629             return pFoundData;
2630     }
2631 
2632     // now try the other font name tokens
2633     while( nTokenPos != STRING_NOTFOUND )
2634     {
2635         rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
2636         if( !rFSD.maTargetName.Len() )
2637             continue;
2638 
2639         aSearchName = rFSD.maTargetName;
2640         GetEnglishSearchFontName( aSearchName );
2641 
2642         String      aTempShortName;
2643         String      aTempFamilyName;
2644         sal_uLong       nTempType   = 0;
2645         FontWeight  eTempWeight = rFSD.meWeight;
2646         FontWidth   eTempWidth  = WIDTH_DONTKNOW;
2647         FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName,
2648                                             eTempWeight, eTempWidth, nTempType );
2649 
2650         // use a shortend token name if available
2651         if( aTempShortName != aSearchName )
2652         {
2653             ImplDevFontListData* pFoundData = ImplFindBySearchName( aTempShortName );
2654             if( pFoundData )
2655                 return pFoundData;
2656         }
2657 
2658         // use a font name from font fallback list to determine font attributes
2659 
2660         // get fallback info using FontSubstConfiguration and
2661         // the target name, it's shortened name and family name in that order
2662         const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
2663         const FontNameAttr* pTempFontAttr = rFontSubst.getSubstInfo( aSearchName );
2664         if ( !pTempFontAttr && (aTempShortName != aSearchName) )
2665             pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName );
2666         if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) )
2667             pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName );
2668 
2669         // try the font substitutions suggested by the fallback info
2670         if( pTempFontAttr )
2671         {
2672             ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pTempFontAttr );
2673             if( pFoundData )
2674                 return pFoundData;
2675             if( !pFontAttr )
2676                 pFontAttr = pTempFontAttr;
2677         }
2678     }
2679 
2680     // if still needed use the alias names of the installed fonts
2681     if( mbMapNames )
2682     {
2683         ImplDevFontListData* pFoundData = ImplFindByAliasName( rFSD.maTargetName, aSearchShortName );
2684         if( pFoundData )
2685             return pFoundData;
2686     }
2687 
2688     // if still needed use the font request's attributes to find a good match
2689     switch( rFSD.meLanguage )
2690     {
2691         case LANGUAGE_CHINESE:
2692         case LANGUAGE_CHINESE_SIMPLIFIED:
2693         case LANGUAGE_CHINESE_SINGAPORE:
2694             nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_SC;
2695             break;
2696         case LANGUAGE_CHINESE_TRADITIONAL:
2697         case LANGUAGE_CHINESE_HONGKONG:
2698         case LANGUAGE_CHINESE_MACAU:
2699             nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_TC;
2700             break;
2701         case LANGUAGE_KOREAN:
2702         case LANGUAGE_KOREAN_JOHAB:
2703             nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_KR;
2704             break;
2705         case LANGUAGE_JAPANESE:
2706             nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_JP;
2707             break;
2708         default:
2709             nSearchType |= ImplIsCJKFont( rFSD.maName );
2710             if( rFSD.IsSymbolFont() )
2711                 nSearchType |= IMPL_FONT_ATTR_SYMBOL;
2712             break;
2713     }
2714 
2715     ImplCalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.meFamily, pFontAttr );
2716     ImplDevFontListData* pFoundData = ImplFindByAttributes( nSearchType,
2717         eSearchWeight, eSearchWidth, rFSD.meFamily, rFSD.meItalic, aSearchFamilyName );
2718 
2719     if( pFoundData )
2720     {
2721         // overwrite font selection attributes using info from the typeface flags
2722         if( (eSearchWeight >= WEIGHT_BOLD)
2723         &&  (eSearchWeight > rFSD.meWeight)
2724         &&  (pFoundData->mnTypeFaces & IMPL_DEVFONT_BOLD) )
2725             rFSD.meWeight = eSearchWeight;
2726         else if( (eSearchWeight < WEIGHT_NORMAL)
2727         &&  (eSearchWeight < rFSD.meWeight)
2728         &&  (eSearchWeight != WEIGHT_DONTKNOW)
2729         &&  (pFoundData->mnTypeFaces & IMPL_DEVFONT_LIGHT) )
2730             rFSD.meWeight = eSearchWeight;
2731 
2732         if( (nSearchType & IMPL_FONT_ATTR_ITALIC)
2733         &&  ((rFSD.meItalic == ITALIC_DONTKNOW) || (rFSD.meItalic == ITALIC_NONE))
2734         &&  (pFoundData->mnTypeFaces & IMPL_DEVFONT_ITALIC) )
2735             rFSD.meItalic = ITALIC_NORMAL;
2736     }
2737     else
2738     {
2739         // if still needed fall back to default fonts
2740         pFoundData = FindDefaultFont();
2741     }
2742 
2743     return pFoundData;
2744 }
2745 
2746 // -----------------------------------------------------------------------
2747 
2748 ImplFontEntry* ImplFontCache::GetGlyphFallbackFont( ImplDevFontList* pFontList,
2749     ImplFontSelectData& rFontSelData, int nFallbackLevel, rtl::OUString& rMissingCodes )
2750 {
2751     // get a candidate font for glyph fallback
2752     // unless the previously selected font got a device specific substitution
2753     // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
2754     if( nFallbackLevel >= 1)
2755     {
2756         ImplDevFontListData* pFallbackData = pFontList->GetGlyphFallbackFont(
2757             rFontSelData, rMissingCodes, nFallbackLevel-1 );
2758         // escape when there are no font candidates
2759         if( !pFallbackData  )
2760             return NULL;
2761         // override the font name
2762         rFontSelData.maName = pFallbackData->GetFamilyName();
2763         // clear the cached normalized name
2764         rFontSelData.maSearchName = String();
2765     }
2766 
2767     // get device font without doing device specific substitutions
2768     ImplFontEntry* pFallbackFont = GetFontEntry( pFontList, rFontSelData, NULL );
2769     return pFallbackFont;
2770 }
2771 
2772 // -----------------------------------------------------------------------
2773 
2774 void ImplFontCache::Release( ImplFontEntry* pEntry )
2775 {
2776     static const int FONTCACHE_MAX = 50;
2777 
2778     DBG_ASSERT( (pEntry->mnRefCount > 0), "ImplFontCache::Release() - font refcount underflow" );
2779     if( --pEntry->mnRefCount > 0 )
2780         return;
2781 
2782     if( ++mnRef0Count < FONTCACHE_MAX )
2783         return;
2784 
2785     // remove unused entries from font instance cache
2786     FontInstanceList::iterator it_next = maFontInstanceList.begin();
2787     while( it_next != maFontInstanceList.end() )
2788     {
2789         FontInstanceList::iterator it = it_next++;
2790         ImplFontEntry* pFontEntry = (*it).second;
2791         if( pFontEntry->mnRefCount > 0 )
2792             continue;
2793 
2794         maFontInstanceList.erase( it );
2795         delete pFontEntry;
2796         --mnRef0Count;
2797         DBG_ASSERT( (mnRef0Count>=0), "ImplFontCache::Release() - refcount0 underflow" );
2798 
2799         if( mpFirstEntry == pFontEntry )
2800             mpFirstEntry = NULL;
2801     }
2802 
2803     DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Release() - refcount0 mismatch" );
2804 }
2805 
2806 // -----------------------------------------------------------------------
2807 
2808 void ImplFontCache::Invalidate()
2809 {
2810     // delete unreferenced entries
2811     FontInstanceList::iterator it = maFontInstanceList.begin();
2812     for(; it != maFontInstanceList.end(); ++it )
2813     {
2814         ImplFontEntry* pFontEntry = (*it).second;
2815         if( pFontEntry->mnRefCount > 0 )
2816             continue;
2817 
2818         delete pFontEntry;
2819         --mnRef0Count;
2820     }
2821 
2822     // #112304# make sure the font cache is really clean
2823     mpFirstEntry = NULL;
2824     maFontInstanceList.clear();
2825 
2826     DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Invalidate() - mnRef0Count non-zero" );
2827 
2828 #ifdef USE_BUILTIN_RASTERIZER
2829     // TODO: eventually move into SalGraphics layer
2830     GlyphCache::GetInstance().InvalidateAllGlyphs();
2831 #endif
2832 }
2833 
2834 // =======================================================================
2835 
2836 ImplMultiTextLineInfo::ImplMultiTextLineInfo()
2837 {
2838     mpLines = new PImplTextLineInfo[MULTITEXTLINEINFO_RESIZE];
2839     mnLines = 0;
2840     mnSize  = MULTITEXTLINEINFO_RESIZE;
2841 }
2842 
2843 
2844 ImplMultiTextLineInfo::~ImplMultiTextLineInfo()
2845 {
2846     for ( xub_StrLen i = 0; i < mnLines; i++ )
2847         delete mpLines[i];
2848     delete [] mpLines;
2849 }
2850 
2851 void ImplMultiTextLineInfo::AddLine( ImplTextLineInfo* pLine )
2852 {
2853     if ( mnSize == mnLines )
2854     {
2855         mnSize += MULTITEXTLINEINFO_RESIZE;
2856         PImplTextLineInfo* pNewLines = new PImplTextLineInfo[mnSize];
2857         memcpy( pNewLines, mpLines, mnLines*sizeof(PImplTextLineInfo) );
2858         mpLines = pNewLines;
2859     }
2860 
2861     mpLines[mnLines] = pLine;
2862     mnLines++;
2863 }
2864 
2865 void ImplMultiTextLineInfo::Clear()
2866 {
2867     for ( xub_StrLen i = 0; i < mnLines; i++ )
2868         delete mpLines[i];
2869     mnLines = 0;
2870 }
2871 
2872 // =======================================================================
2873 
2874 FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const Font& rFont )
2875 {
2876     FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark();
2877 
2878     // If no Position is set, then calculate the default position, which
2879     // depends on the language
2880     if ( !(nEmphasisMark & (EMPHASISMARK_POS_ABOVE | EMPHASISMARK_POS_BELOW)) )
2881     {
2882         LanguageType eLang = rFont.GetLanguage();
2883         // In Chinese Simplified the EmphasisMarks are below/left
2884         if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
2885              (eLang == LANGUAGE_CHINESE_SINGAPORE) )
2886             nEmphasisMark |= EMPHASISMARK_POS_BELOW;
2887         else
2888         {
2889             eLang = rFont.GetCJKContextLanguage();
2890             // In Chinese Simplified the EmphasisMarks are below/left
2891             if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
2892                  (eLang == LANGUAGE_CHINESE_SINGAPORE) )
2893                 nEmphasisMark |= EMPHASISMARK_POS_BELOW;
2894             else
2895                 nEmphasisMark |= EMPHASISMARK_POS_ABOVE;
2896         }
2897     }
2898 
2899     return nEmphasisMark;
2900 }
2901 
2902 // -----------------------------------------------------------------------
2903 
2904 sal_Bool OutputDevice::ImplIsUnderlineAbove( const Font& rFont )
2905 {
2906     if ( !rFont.IsVertical() )
2907         return sal_False;
2908 
2909     if( (LANGUAGE_JAPANESE == rFont.GetLanguage())
2910     ||  (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) )
2911         // the underline is right for Japanese only
2912         return sal_True;
2913 
2914     return sal_False;
2915 }
2916 
2917 // =======================================================================
2918 
2919 void OutputDevice::ImplInitFontList() const
2920 {
2921     if( ! mpFontList->Count() )
2922     {
2923         if( mpGraphics || ImplGetGraphics() )
2924         {
2925 			RTL_LOGFILE_CONTEXT( aLog, "OutputDevice::ImplInitFontList()" );
2926             mpGraphics->GetDevFontList( mpFontList );
2927         }
2928     }
2929     if( meOutDevType == OUTDEV_WINDOW && ! mpFontList->Count() )
2930     {
2931         String aError( RTL_CONSTASCII_USTRINGPARAM( "Application error: no fonts and no vcl resource found on your system" ) );
2932         ResMgr* pMgr = ImplGetResMgr();
2933         if( pMgr )
2934         {
2935             String aResStr( ResId( SV_ACCESSERROR_NO_FONTS, *pMgr ) );
2936             if( aResStr.Len() )
2937                 aError = aResStr;
2938         }
2939         Application::Abort( aError );
2940     }
2941 }
2942 
2943 // =======================================================================
2944 
2945 void OutputDevice::ImplInitFont() const
2946 {
2947     DBG_TESTSOLARMUTEX();
2948 
2949     if ( mbInitFont )
2950     {
2951         if ( meOutDevType != OUTDEV_PRINTER )
2952         {
2953             // decide if antialiasing is appropriate
2954             bool bNonAntialiased = (GetAntialiasing() & ANTIALIASING_DISABLE_TEXT) != 0;
2955             const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
2956             bNonAntialiased |= ((rStyleSettings.GetDisplayOptions() & DISPLAY_OPTION_AA_DISABLE) != 0);
2957             bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontEntry->maFontSelData.mnHeight);
2958             mpFontEntry->maFontSelData.mbNonAntialiased = bNonAntialiased;
2959         }
2960 
2961         if( !mpPDFWriter || !mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) )
2962         {
2963             // select font in the device layers
2964             mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData), 0 );
2965         }
2966         mbInitFont = false;
2967     }
2968 }
2969 
2970 // -----------------------------------------------------------------------
2971 
2972 void OutputDevice::ImplInitTextColor()
2973 {
2974     DBG_TESTSOLARMUTEX();
2975 
2976     if ( mbInitTextColor )
2977     {
2978         mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) );
2979         mbInitTextColor = sal_False;
2980     }
2981 }
2982 
2983 // -----------------------------------------------------------------------
2984 
2985 bool OutputDevice::ImplNewFont() const
2986 {
2987     DBG_TESTSOLARMUTEX();
2988 
2989     // get correct font list on the PDF writer if necessary
2990     if( mpPDFWriter )
2991     {
2992         const ImplSVData* pSVData = ImplGetSVData();
2993         if( mpFontList == pSVData->maGDIData.mpScreenFontList
2994         ||  mpFontCache == pSVData->maGDIData.mpScreenFontCache )
2995             const_cast<OutputDevice&>(*this).ImplUpdateFontData( true );
2996     }
2997 
2998     if ( !mbNewFont )
2999         return true;
3000 
3001     // we need a graphics
3002     if ( !mpGraphics && !ImplGetGraphics() )
3003         return false;
3004     SalGraphics* pGraphics = mpGraphics;
3005     ImplInitFontList();
3006 
3007     // convert to pixel height
3008     // TODO: replace integer based aSize completely with subpixel accurate type
3009     float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetHeight()) );
3010     Size aSize = ImplLogicToDevicePixel( maFont.GetSize() );
3011     if ( !aSize.Height() )
3012     {
3013         // use default pixel height only when logical height is zero
3014         if ( maFont.GetSize().Height() )
3015             aSize.Height() = 1;
3016         else
3017             aSize.Height() = (12*mnDPIY)/72;
3018         fExactHeight =  static_cast<float>(aSize.Height());
3019     }
3020 
3021     // select the default width only when logical width is zero
3022     if( (0 == aSize.Width()) && (0 != maFont.GetSize().Width()) )
3023         aSize.Width() = 1;
3024 
3025     // get font entry
3026     ImplDirectFontSubstitution* pDevSpecificSubst = NULL;
3027     if( mpOutDevData )
3028         pDevSpecificSubst = &mpOutDevData->maDevFontSubst;
3029     ImplFontEntry* pOldEntry = mpFontEntry;
3030     mpFontEntry = mpFontCache->GetFontEntry( mpFontList, maFont, aSize, fExactHeight, pDevSpecificSubst );
3031     if( pOldEntry )
3032         mpFontCache->Release( pOldEntry );
3033 
3034     ImplFontEntry* pFontEntry = mpFontEntry;
3035     // mark when lower layers need to get involved
3036     mbNewFont = sal_False;
3037     if( pFontEntry != pOldEntry )
3038         mbInitFont = sal_True;
3039 
3040     // select font when it has not been initialized yet
3041     if ( !pFontEntry->mbInit )
3042     {
3043         ImplInitFont();
3044 
3045         // get metric data from device layers
3046         if ( pGraphics )
3047         {
3048             pFontEntry->mbInit = true;
3049 
3050             pFontEntry->maMetric.mnOrientation  = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation);
3051             if( mpPDFWriter && mpPDFWriter->isBuiltinFont( pFontEntry->maFontSelData.mpFontData ) )
3052                 mpPDFWriter->getFontMetric( &pFontEntry->maFontSelData, &(pFontEntry->maMetric) );
3053             else
3054                 pGraphics->GetFontMetric( &(pFontEntry->maMetric) );
3055 
3056             pFontEntry->maMetric.ImplInitTextLineSize( this );
3057             pFontEntry->maMetric.ImplInitAboveTextLineSize();
3058 
3059             pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent + pFontEntry->maMetric.mnDescent;
3060 
3061             if( pFontEntry->maFontSelData.mnOrientation
3062             && !pFontEntry->maMetric.mnOrientation
3063             && (meOutDevType != OUTDEV_PRINTER) )
3064             {
3065                 pFontEntry->mnOwnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation);
3066                 pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation;
3067             }
3068             else
3069                 pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation;
3070         }
3071     }
3072 
3073     // enable kerning array if requested
3074     if ( maFont.GetKerning() & KERNING_FONTSPECIFIC )
3075     {
3076         // TODO: test if physical font supports kerning and disable if not
3077         if( pFontEntry->maMetric.mbKernableFont )
3078             mbKerning = true;
3079     }
3080     else
3081         mbKerning = false;
3082     if ( maFont.GetKerning() & KERNING_ASIAN )
3083         mbKerning = true;
3084 
3085     // calculate EmphasisArea
3086     mnEmphasisAscent = 0;
3087     mnEmphasisDescent = 0;
3088     if ( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
3089     {
3090         FontEmphasisMark    nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
3091         long                nEmphasisHeight = (pFontEntry->mnLineHeight*250)/1000;
3092         if ( nEmphasisHeight < 1 )
3093             nEmphasisHeight = 1;
3094         if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
3095             mnEmphasisDescent = nEmphasisHeight;
3096         else
3097             mnEmphasisAscent = nEmphasisHeight;
3098     }
3099 
3100     // calculate text offset depending on TextAlignment
3101     TextAlign eAlign = maFont.GetAlign();
3102     if ( eAlign == ALIGN_BASELINE )
3103     {
3104         mnTextOffX = 0;
3105         mnTextOffY = 0;
3106     }
3107     else if ( eAlign == ALIGN_TOP )
3108     {
3109         mnTextOffX = 0;
3110         mnTextOffY = +pFontEntry->maMetric.mnAscent + mnEmphasisAscent;
3111         if ( pFontEntry->mnOrientation )
3112             ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
3113     }
3114     else // eAlign == ALIGN_BOTTOM
3115     {
3116         mnTextOffX = 0;
3117         mnTextOffY = -pFontEntry->maMetric.mnDescent + mnEmphasisDescent;
3118         if ( pFontEntry->mnOrientation )
3119             ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
3120     }
3121 
3122     mbTextLines     = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) ||
3123                       ((maFont.GetOverline()  != UNDERLINE_NONE) && (maFont.GetOverline()  != UNDERLINE_DONTKNOW)) ||
3124                       ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW));
3125     mbTextSpecial   = maFont.IsShadow() || maFont.IsOutline() ||
3126                       (maFont.GetRelief() != RELIEF_NONE);
3127 
3128     // #95414# fix for OLE objects which use scale factors very creatively
3129     if( mbMap && !aSize.Width() )
3130     {
3131         int nOrigWidth = pFontEntry->maMetric.mnWidth;
3132         float fStretch = (float)maMapRes.mnMapScNumX * maMapRes.mnMapScDenomY;
3133         fStretch /= (float)maMapRes.mnMapScNumY * maMapRes.mnMapScDenomX;
3134         int nNewWidth = (int)(nOrigWidth * fStretch + 0.5);
3135         if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) )
3136         {
3137             Size aOrigSize = maFont.GetSize();
3138             const_cast<Font&>(maFont).SetSize( Size( nNewWidth, aSize.Height() ) );
3139             mbMap = sal_False;
3140             mbNewFont = sal_True;
3141             ImplNewFont();  // recurse once using stretched width
3142             mbMap = sal_True;
3143             const_cast<Font&>(maFont).SetSize( aOrigSize );
3144         }
3145     }
3146 
3147     return true;
3148 }
3149 
3150 // -----------------------------------------------------------------------
3151 
3152 long OutputDevice::ImplGetTextWidth( const SalLayout& rSalLayout ) const
3153 {
3154     long nWidth = rSalLayout.GetTextWidth();
3155     nWidth /= rSalLayout.GetUnitsPerPixel();
3156     return nWidth;
3157 }
3158 
3159 // -----------------------------------------------------------------------
3160 
3161 void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY,
3162                                      long nDistX, long nDistY, long nWidth, long nHeight )
3163 {
3164     long nX = nDistX;
3165     long nY = nDistY;
3166 
3167     short nOrientation = mpFontEntry->mnOrientation;
3168     if ( nOrientation )
3169     {
3170         // Rotate rect without rounding problems for 90 degree rotations
3171         if ( !(nOrientation % 900) )
3172         {
3173             if ( nOrientation == 900 )
3174             {
3175                 long nTemp = nX;
3176                 nX = nY;
3177                 nY = -nTemp;
3178                 nTemp = nWidth;
3179                 nWidth = nHeight;
3180                 nHeight = nTemp;
3181                 nY -= nHeight;
3182             }
3183             else if ( nOrientation == 1800 )
3184             {
3185                 nX = -nX;
3186                 nY = -nY;
3187                 nX -= nWidth;
3188                 nY -= nHeight;
3189             }
3190             else /* ( nOrientation == 2700 ) */
3191             {
3192                 long nTemp = nX;
3193                 nX = -nY;
3194                 nY = nTemp;
3195                 nTemp = nWidth;
3196                 nWidth = nHeight;
3197                 nHeight = nTemp;
3198                 nX -= nWidth;
3199             }
3200         }
3201         else
3202         {
3203             nX += nBaseX;
3204             nY += nBaseY;
3205             // inflate because polygons are drawn smaller
3206             Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
3207             Polygon   aPoly( aRect );
3208             aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
3209             ImplDrawPolygon( aPoly );
3210             return;
3211         }
3212     }
3213 
3214     nX += nBaseX;
3215     nY += nBaseY;
3216     mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this );
3217 }
3218 
3219 // -----------------------------------------------------------------------
3220 
3221 void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout )
3222 {
3223     const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
3224     const Point aBase = rSalLayout.DrawBase();
3225     const long nX = aBase.X();
3226     const long nY = aBase.Y();
3227 
3228     if ( mbLineColor || mbInitLineColor )
3229     {
3230         mpGraphics->SetLineColor();
3231         mbInitLineColor = sal_True;
3232     }
3233     mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) );
3234     mbInitFillColor = sal_True;
3235 
3236     ImplDrawTextRect( nX, nY, 0, -(mpFontEntry->maMetric.mnAscent + mnEmphasisAscent),
3237                       nWidth,
3238                       mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent );
3239 }
3240 
3241 // -----------------------------------------------------------------------
3242 
3243 Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout )
3244 {
3245     Point aPoint = rSalLayout.GetDrawPosition();
3246     long nX = aPoint.X();
3247     long nY = aPoint.Y();
3248 
3249     long nWidth = rSalLayout.GetTextWidth();
3250     long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
3251 
3252     nY -= mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
3253 
3254     if ( mpFontEntry->mnOrientation )
3255     {
3256         long nBaseX = nX, nBaseY = nY;
3257         if ( !(mpFontEntry->mnOrientation % 900) )
3258         {
3259             long nX2 = nX+nWidth;
3260             long nY2 = nY+nHeight;
3261             ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation );
3262             ImplRotatePos( nBaseX, nBaseY, nX2, nY2, mpFontEntry->mnOrientation );
3263             nWidth = nX2-nX;
3264             nHeight = nY2-nY;
3265         }
3266         else
3267         {
3268             // inflate by +1+1 because polygons are drawn smaller
3269             Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
3270             Polygon   aPoly( aRect );
3271             aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
3272             return aPoly.GetBoundRect();
3273         }
3274     }
3275 
3276     return Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) );
3277 }
3278 
3279 // -----------------------------------------------------------------------
3280 
3281 void OutputDevice::ImplInitTextLineSize()
3282 {
3283     mpFontEntry->maMetric.ImplInitTextLineSize( this );
3284 }
3285 
3286 // -----------------------------------------------------------------------
3287 
3288 void OutputDevice::ImplInitAboveTextLineSize()
3289 {
3290     mpFontEntry->maMetric.ImplInitAboveTextLineSize();
3291 }
3292 
3293 // -----------------------------------------------------------------------
3294 
3295 ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData )
3296 :   ImplFontAttributes( rFontSelData )
3297 {
3298 	// initialize the members provided by the font request
3299     mnWidth        = rFontSelData.mnWidth;
3300     mnSlant        = rFontSelData.GetSlant();
3301     mnOrientation  = sal::static_int_cast<short>(rFontSelData.mnOrientation);
3302 
3303 	// intialize the used font name
3304     if( rFontSelData.mpFontData )
3305     {
3306         maName     = rFontSelData.mpFontData->maName;
3307         maStyleName= rFontSelData.mpFontData->maStyleName;
3308         mbDevice   = rFontSelData.mpFontData->mbDevice;
3309         mbKernableFont = true;
3310     }
3311     else
3312     {
3313         xub_StrLen nTokenPos = 0;
3314         maName     = GetNextFontToken( rFontSelData.maName, nTokenPos );
3315         maStyleName= rFontSelData.maStyleName;
3316         mbDevice   = false;
3317         mbKernableFont = false;
3318     }
3319 
3320 	// reset metrics that are usually measured for the font instance
3321     mnAscent       = 0;
3322     mnDescent      = 0;
3323     mnIntLeading   = 0;
3324     mnExtLeading   = 0;
3325     mnMinKashida   = 0;
3326 
3327     // reset metrics that are usually derived from the measurements
3328     mnUnderlineSize            = 0;
3329     mnUnderlineOffset          = 0;
3330     mnBUnderlineSize           = 0;
3331     mnBUnderlineOffset         = 0;
3332     mnDUnderlineSize           = 0;
3333     mnDUnderlineOffset1        = 0;
3334     mnDUnderlineOffset2        = 0;
3335     mnWUnderlineSize           = 0;
3336     mnWUnderlineOffset         = 0;
3337     mnAboveUnderlineSize       = 0;
3338     mnAboveUnderlineOffset     = 0;
3339     mnAboveBUnderlineSize      = 0;
3340     mnAboveBUnderlineOffset    = 0;
3341     mnAboveDUnderlineSize      = 0;
3342     mnAboveDUnderlineOffset1   = 0;
3343     mnAboveDUnderlineOffset2   = 0;
3344     mnAboveWUnderlineSize      = 0;
3345     mnAboveWUnderlineOffset    = 0;
3346     mnStrikeoutSize            = 0;
3347     mnStrikeoutOffset          = 0;
3348     mnBStrikeoutSize           = 0;
3349     mnBStrikeoutOffset         = 0;
3350     mnDStrikeoutSize           = 0;
3351     mnDStrikeoutOffset1        = 0;
3352     mnDStrikeoutOffset2        = 0;
3353 }
3354 
3355 // -----------------------------------------------------------------------
3356 
3357 void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev )
3358 {
3359     long nDescent = mnDescent;
3360     if ( nDescent <= 0 )
3361     {
3362         nDescent = mnAscent / 10;
3363         if ( !nDescent )
3364             nDescent = 1;
3365     }
3366 
3367     // #i55341# for some fonts it is not a good idea to calculate
3368     // their text line metrics from the real font descent
3369     // => work around this problem just for these fonts
3370     if( 3*nDescent > mnAscent )
3371         nDescent = mnAscent / 3;
3372 
3373     long nLineHeight = ((nDescent*25)+50) / 100;
3374     if ( !nLineHeight )
3375         nLineHeight = 1;
3376     long nLineHeight2 = nLineHeight / 2;
3377     if ( !nLineHeight2 )
3378         nLineHeight2 = 1;
3379 
3380     long nBLineHeight = ((nDescent*50)+50) / 100;
3381     if ( nBLineHeight == nLineHeight )
3382         nBLineHeight++;
3383     long nBLineHeight2 = nBLineHeight/2;
3384     if ( !nBLineHeight2 )
3385         nBLineHeight2 = 1;
3386 
3387     long n2LineHeight = ((nDescent*16)+50) / 100;
3388     if ( !n2LineHeight )
3389         n2LineHeight = 1;
3390     long n2LineDY = n2LineHeight;
3391      /* #117909#
3392       * add some pixels to minimum double line distance on higher resolution devices
3393       */
3394     long nMin2LineDY = 1 + pDev->ImplGetDPIY()/150;
3395     if ( n2LineDY < nMin2LineDY )
3396         n2LineDY = nMin2LineDY;
3397     long n2LineDY2 = n2LineDY/2;
3398     if ( !n2LineDY2 )
3399         n2LineDY2 = 1;
3400 
3401     long nUnderlineOffset = mnDescent/2 + 1;
3402     long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3);
3403 
3404     mnUnderlineSize        = nLineHeight;
3405     mnUnderlineOffset      = nUnderlineOffset - nLineHeight2;
3406 
3407     mnBUnderlineSize       = nBLineHeight;
3408     mnBUnderlineOffset     = nUnderlineOffset - nBLineHeight2;
3409 
3410     mnDUnderlineSize       = n2LineHeight;
3411     mnDUnderlineOffset1    = nUnderlineOffset - n2LineDY2 - n2LineHeight;
3412     mnDUnderlineOffset2    = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;
3413 
3414     long nWCalcSize = mnDescent;
3415     if ( nWCalcSize < 6 )
3416     {
3417         if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
3418             mnWUnderlineSize = nWCalcSize;
3419         else
3420             mnWUnderlineSize = 3;
3421     }
3422     else
3423         mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;
3424 
3425     // #109280# the following line assures that wavelnes are never placed below the descent, however
3426     // for most fonts the waveline then is drawn into the text, so we better keep the old solution
3427     // pFontEntry->maMetric.mnWUnderlineOffset     = pFontEntry->maMetric.mnDescent + 1 - pFontEntry->maMetric.mnWUnderlineSize;
3428     mnWUnderlineOffset     = nUnderlineOffset;
3429 
3430     mnStrikeoutSize        = nLineHeight;
3431     mnStrikeoutOffset      = nStrikeoutOffset - nLineHeight2;
3432 
3433     mnBStrikeoutSize       = nBLineHeight;
3434     mnBStrikeoutOffset     = nStrikeoutOffset - nBLineHeight2;
3435 
3436     mnDStrikeoutSize       = n2LineHeight;
3437     mnDStrikeoutOffset1    = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
3438     mnDStrikeoutOffset2    = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;
3439 }
3440 
3441 // -----------------------------------------------------------------------
3442 
3443 void ImplFontMetricData::ImplInitAboveTextLineSize()
3444 {
3445     long nIntLeading = mnIntLeading;
3446     // TODO: assess usage of nLeading below (changed in extleading CWS)
3447     // if no leading is available, we assume 15% of the ascent
3448     if ( nIntLeading <= 0 )
3449     {
3450         nIntLeading = mnAscent*15/100;
3451         if ( !nIntLeading )
3452             nIntLeading = 1;
3453     }
3454 
3455     long nLineHeight = ((nIntLeading*25)+50) / 100;
3456     if ( !nLineHeight )
3457         nLineHeight = 1;
3458 
3459     long nBLineHeight = ((nIntLeading*50)+50) / 100;
3460     if ( nBLineHeight == nLineHeight )
3461         nBLineHeight++;
3462 
3463     long n2LineHeight = ((nIntLeading*16)+50) / 100;
3464     if ( !n2LineHeight )
3465         n2LineHeight = 1;
3466 
3467     long nCeiling = -mnAscent;
3468 
3469     mnAboveUnderlineSize       = nLineHeight;
3470     mnAboveUnderlineOffset     = nCeiling + (nIntLeading - nLineHeight + 1) / 2;
3471 
3472     mnAboveBUnderlineSize      = nBLineHeight;
3473     mnAboveBUnderlineOffset    = nCeiling + (nIntLeading - nBLineHeight + 1) / 2;
3474 
3475     mnAboveDUnderlineSize      = n2LineHeight;
3476     mnAboveDUnderlineOffset1   = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2;
3477     mnAboveDUnderlineOffset2   = nCeiling + (nIntLeading +   n2LineHeight + 1) / 2;
3478 
3479     long nWCalcSize = nIntLeading;
3480     if ( nWCalcSize < 6 )
3481     {
3482         if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
3483             mnAboveWUnderlineSize = nWCalcSize;
3484         else
3485             mnAboveWUnderlineSize = 3;
3486     }
3487     else
3488         mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100;
3489 
3490     mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
3491 }
3492 
3493 // -----------------------------------------------------------------------
3494 
3495 static void ImplDrawWavePixel( long nOriginX, long nOriginY,
3496                                long nCurX, long nCurY,
3497                                short nOrientation,
3498                                SalGraphics* pGraphics,
3499                                OutputDevice* pOutDev,
3500                                sal_Bool bDrawPixAsRect,
3501 
3502                                long nPixWidth, long nPixHeight )
3503 {
3504     if ( nOrientation )
3505         ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation );
3506 
3507     if ( bDrawPixAsRect )
3508     {
3509 
3510         pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev );
3511     }
3512     else
3513     {
3514         pGraphics->DrawPixel( nCurX, nCurY, pOutDev );
3515     }
3516 }
3517 
3518 // -----------------------------------------------------------------------
3519 
3520 void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY,
3521                                      long nDistX, long nDistY,
3522                                      long nWidth, long nHeight,
3523                                      long nLineWidth, short nOrientation,
3524                                      const Color& rColor )
3525 {
3526     if ( !nHeight )
3527         return;
3528 
3529     long nStartX = nBaseX + nDistX;
3530     long nStartY = nBaseY + nDistY;
3531 
3532     // Bei Hoehe von 1 Pixel reicht es, eine Linie auszugeben
3533     if ( (nLineWidth == 1) && (nHeight == 1) )
3534     {
3535         mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
3536         mbInitLineColor = sal_True;
3537 
3538         long nEndX = nStartX+nWidth;
3539         long nEndY = nStartY;
3540         if ( nOrientation )
3541         {
3542             ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation );
3543             ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation );
3544         }
3545         mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this );
3546     }
3547     else
3548     {
3549         long    nCurX = nStartX;
3550         long    nCurY = nStartY;
3551         long    nDiffX = 2;
3552         long    nDiffY = nHeight-1;
3553         long    nCount = nWidth;
3554         long    nOffY = -1;
3555         long    nFreq;
3556         long    i;
3557         long    nPixWidth;
3558         long    nPixHeight;
3559         sal_Bool    bDrawPixAsRect;
3560         // Auf Druckern die Pixel per DrawRect() ausgeben
3561         if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) )
3562         {
3563             if ( mbLineColor || mbInitLineColor )
3564             {
3565                 mpGraphics->SetLineColor();
3566                 mbInitLineColor = sal_True;
3567             }
3568             mpGraphics->SetFillColor( ImplColorToSal( rColor ) );
3569             mbInitFillColor = sal_True;
3570             bDrawPixAsRect  = sal_True;
3571             nPixWidth       = nLineWidth;
3572             nPixHeight      = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
3573         }
3574         else
3575         {
3576             mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
3577             mbInitLineColor = sal_True;
3578             nPixWidth       = 1;
3579             nPixHeight      = 1;
3580             bDrawPixAsRect  = sal_False;
3581         }
3582 
3583         if ( !nDiffY )
3584         {
3585             while ( nWidth )
3586             {
3587                 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
3588                                    mpGraphics, this,
3589                                    bDrawPixAsRect, nPixWidth, nPixHeight );
3590                 nCurX++;
3591                 nWidth--;
3592             }
3593         }
3594         else
3595         {
3596             nCurY += nDiffY;
3597             nFreq = nCount / (nDiffX+nDiffY);
3598             while ( nFreq-- )
3599             {
3600                 for( i = nDiffY; i; --i )
3601                 {
3602                     ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
3603                                        mpGraphics, this,
3604                                        bDrawPixAsRect, nPixWidth, nPixHeight );
3605                     nCurX++;
3606                     nCurY += nOffY;
3607                 }
3608                 for( i = nDiffX; i; --i )
3609                 {
3610                     ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
3611                                        mpGraphics, this,
3612                                        bDrawPixAsRect, nPixWidth, nPixHeight );
3613                     nCurX++;
3614                 }
3615                 nOffY = -nOffY;
3616             }
3617             nFreq = nCount % (nDiffX+nDiffY);
3618             if ( nFreq )
3619             {
3620                 for( i = nDiffY; i && nFreq; --i, --nFreq )
3621                 {
3622                     ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
3623                                        mpGraphics, this,
3624                                        bDrawPixAsRect, nPixWidth, nPixHeight );
3625                     nCurX++;
3626                     nCurY += nOffY;
3627 
3628                 }
3629                 for( i = nDiffX; i && nFreq; --i, --nFreq )
3630                 {
3631                     ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
3632                                        mpGraphics, this,
3633                                        bDrawPixAsRect, nPixWidth, nPixHeight );
3634                     nCurX++;
3635                 }
3636             }
3637         }
3638 
3639     }
3640 }
3641 
3642 // -----------------------------------------------------------------------
3643 
3644 void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY,
3645                                          long nDistX, long nDistY, long nWidth,
3646                                          FontUnderline eTextLine,
3647                                          Color aColor,
3648                                          sal_Bool bIsAbove )
3649 {
3650     ImplFontEntry*  pFontEntry = mpFontEntry;
3651     long            nLineHeight;
3652     long            nLinePos;
3653 
3654     if ( bIsAbove )
3655     {
3656         nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize;
3657         nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset;
3658     }
3659     else
3660     {
3661         nLineHeight = pFontEntry->maMetric.mnWUnderlineSize;
3662         nLinePos = pFontEntry->maMetric.mnWUnderlineOffset;
3663     }
3664     if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
3665         nLineHeight = 3;
3666     long nLineWidth = (mnDPIX/300);
3667     if ( !nLineWidth )
3668         nLineWidth = 1;
3669     if ( eTextLine == UNDERLINE_BOLDWAVE )
3670         nLineWidth *= 2;
3671     nLinePos += nDistY - (nLineHeight / 2);
3672     long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
3673     if ( eTextLine == UNDERLINE_DOUBLEWAVE )
3674     {
3675         long nOrgLineHeight = nLineHeight;
3676         nLineHeight /= 3;
3677         if ( nLineHeight < 2 )
3678         {
3679             if ( nOrgLineHeight > 1 )
3680                 nLineHeight = 2;
3681             else
3682                 nLineHeight = 1;
3683         }
3684         long nLineDY = nOrgLineHeight-(nLineHeight*2);
3685         if ( nLineDY < nLineWidthHeight )
3686             nLineDY = nLineWidthHeight;
3687         long nLineDY2 = nLineDY/2;
3688         if ( !nLineDY2 )
3689             nLineDY2 = 1;
3690 
3691         nLinePos -= nLineWidthHeight-nLineDY2;
3692         ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
3693                           nLineWidth, mpFontEntry->mnOrientation, aColor );
3694         nLinePos += nLineWidthHeight+nLineDY;
3695         ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
3696                           nLineWidth, mpFontEntry->mnOrientation, aColor );
3697     }
3698     else
3699     {
3700         nLinePos -= nLineWidthHeight/2;
3701         ImplDrawWaveLine( nBaseX, nBaseY, nDistX, nLinePos, nWidth, nLineHeight,
3702                           nLineWidth, mpFontEntry->mnOrientation, aColor );
3703     }
3704 }
3705 
3706 // -----------------------------------------------------------------------
3707 
3708 void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY,
3709                                              long nDistX, long nDistY, long nWidth,
3710                                              FontUnderline eTextLine,
3711                                              Color aColor,
3712                                              sal_Bool bIsAbove )
3713 {
3714     ImplFontEntry*  pFontEntry = mpFontEntry;
3715     long            nLineHeight = 0;
3716     long            nLinePos  = 0;
3717     long            nLinePos2 = 0;
3718 
3719 	const long nY = nDistY;
3720 
3721     if ( eTextLine > UNDERLINE_LAST )
3722         eTextLine = UNDERLINE_SINGLE;
3723 
3724     switch ( eTextLine )
3725     {
3726         case UNDERLINE_SINGLE:
3727         case UNDERLINE_DOTTED:
3728         case UNDERLINE_DASH:
3729         case UNDERLINE_LONGDASH:
3730         case UNDERLINE_DASHDOT:
3731         case UNDERLINE_DASHDOTDOT:
3732             if ( bIsAbove )
3733             {
3734                 nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize;
3735                 nLinePos    = nY + pFontEntry->maMetric.mnAboveUnderlineOffset;
3736             }
3737             else
3738             {
3739                 nLineHeight = pFontEntry->maMetric.mnUnderlineSize;
3740                 nLinePos    = nY + pFontEntry->maMetric.mnUnderlineOffset;
3741             }
3742             break;
3743         case UNDERLINE_BOLD:
3744         case UNDERLINE_BOLDDOTTED:
3745         case UNDERLINE_BOLDDASH:
3746         case UNDERLINE_BOLDLONGDASH:
3747         case UNDERLINE_BOLDDASHDOT:
3748         case UNDERLINE_BOLDDASHDOTDOT:
3749             if ( bIsAbove )
3750             {
3751                 nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize;
3752                 nLinePos    = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset;
3753             }
3754             else
3755             {
3756                 nLineHeight = pFontEntry->maMetric.mnBUnderlineSize;
3757                 nLinePos    = nY + pFontEntry->maMetric.mnBUnderlineOffset;
3758             }
3759             break;
3760         case UNDERLINE_DOUBLE:
3761             if ( bIsAbove )
3762             {
3763                 nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize;
3764                 nLinePos    = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1;
3765                 nLinePos2   = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2;
3766             }
3767             else
3768             {
3769                 nLineHeight = pFontEntry->maMetric.mnDUnderlineSize;
3770                 nLinePos    = nY + pFontEntry->maMetric.mnDUnderlineOffset1;
3771                 nLinePos2   = nY + pFontEntry->maMetric.mnDUnderlineOffset2;
3772             }
3773             break;
3774         default:
3775             break;
3776     }
3777 
3778     if ( nLineHeight )
3779     {
3780         if ( mbLineColor || mbInitLineColor )
3781         {
3782             mpGraphics->SetLineColor();
3783             mbInitLineColor = sal_True;
3784         }
3785         mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
3786         mbInitFillColor = sal_True;
3787 
3788         long nLeft = nDistX;
3789 
3790         switch ( eTextLine )
3791         {
3792             case UNDERLINE_SINGLE:
3793             case UNDERLINE_BOLD:
3794                 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
3795                 break;
3796             case UNDERLINE_DOUBLE:
3797                 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos,  nWidth, nLineHeight );
3798                 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
3799                 break;
3800             case UNDERLINE_DOTTED:
3801             case UNDERLINE_BOLDDOTTED:
3802                 {
3803                     long nDotWidth = nLineHeight*mnDPIY;
3804                     nDotWidth += mnDPIY/2;
3805                     nDotWidth /= mnDPIY;
3806                     long nTempWidth = nDotWidth;
3807                     long nEnd = nLeft+nWidth;
3808                     while ( nLeft < nEnd )
3809                     {
3810                         if ( nLeft+nTempWidth > nEnd )
3811                             nTempWidth = nEnd-nLeft;
3812                         ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
3813                         nLeft += nDotWidth*2;
3814                     }
3815                 }
3816                 break;
3817             case UNDERLINE_DASH:
3818             case UNDERLINE_LONGDASH:
3819             case UNDERLINE_BOLDDASH:
3820             case UNDERLINE_BOLDLONGDASH:
3821                 {
3822                     long nDotWidth = nLineHeight*mnDPIY;
3823                     nDotWidth += mnDPIY/2;
3824                     nDotWidth /= mnDPIY;
3825                     long nMinDashWidth;
3826                     long nMinSpaceWidth;
3827                     long nSpaceWidth;
3828                     long nDashWidth;
3829                     if ( (eTextLine == UNDERLINE_LONGDASH) ||
3830                          (eTextLine == UNDERLINE_BOLDLONGDASH) )
3831                     {
3832                         nMinDashWidth = nDotWidth*6;
3833                         nMinSpaceWidth = nDotWidth*2;
3834                         nDashWidth = 200;
3835                         nSpaceWidth = 100;
3836                     }
3837                     else
3838                     {
3839                         nMinDashWidth = nDotWidth*4;
3840                         nMinSpaceWidth = (nDotWidth*150)/100;
3841                         nDashWidth = 100;
3842                         nSpaceWidth = 50;
3843                     }
3844                     nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540;
3845                     nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540;
3846                     // DashWidth wird gegebenenfalls verbreitert, wenn
3847                     // die dicke der Linie im Verhaeltnis zur Laenge
3848                     // zu dick wird
3849                     if ( nDashWidth < nMinDashWidth )
3850                         nDashWidth = nMinDashWidth;
3851                     if ( nSpaceWidth < nMinSpaceWidth )
3852                         nSpaceWidth = nMinSpaceWidth;
3853                     long nTempWidth = nDashWidth;
3854                     long nEnd = nLeft+nWidth;
3855                     while ( nLeft < nEnd )
3856                     {
3857                         if ( nLeft+nTempWidth > nEnd )
3858                             nTempWidth = nEnd-nLeft;
3859                         ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
3860                         nLeft += nDashWidth+nSpaceWidth;
3861                     }
3862                 }
3863                 break;
3864             case UNDERLINE_DASHDOT:
3865             case UNDERLINE_BOLDDASHDOT:
3866                 {
3867                     long nDotWidth = nLineHeight*mnDPIY;
3868                     nDotWidth += mnDPIY/2;
3869                     nDotWidth /= mnDPIY;
3870                     long nDashWidth = ((100*mnDPIX)+1270)/2540;
3871                     long nMinDashWidth = nDotWidth*4;
3872                     // DashWidth wird gegebenenfalls verbreitert, wenn
3873                     // die dicke der Linie im Verhaeltnis zur Laenge
3874                     // zu dick wird
3875                     if ( nDashWidth < nMinDashWidth )
3876                         nDashWidth = nMinDashWidth;
3877                     long nTempDotWidth = nDotWidth;
3878                     long nTempDashWidth = nDashWidth;
3879                     long nEnd = nLeft+nWidth;
3880                     while ( nLeft < nEnd )
3881                     {
3882                         if ( nLeft+nTempDotWidth > nEnd )
3883                             nTempDotWidth = nEnd-nLeft;
3884                         ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
3885                         nLeft += nDotWidth*2;
3886                         if ( nLeft > nEnd )
3887                             break;
3888                         if ( nLeft+nTempDashWidth > nEnd )
3889                             nTempDashWidth = nEnd-nLeft;
3890                         ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
3891                         nLeft += nDashWidth+nDotWidth;
3892                     }
3893                 }
3894                 break;
3895             case UNDERLINE_DASHDOTDOT:
3896             case UNDERLINE_BOLDDASHDOTDOT:
3897                 {
3898                     long nDotWidth = nLineHeight*mnDPIY;
3899                     nDotWidth += mnDPIY/2;
3900                     nDotWidth /= mnDPIY;
3901                     long nDashWidth = ((100*mnDPIX)+1270)/2540;
3902                     long nMinDashWidth = nDotWidth*4;
3903                     // DashWidth wird gegebenenfalls verbreitert, wenn
3904                     // die dicke der Linie im Verhaeltnis zur Laenge
3905                     // zu dick wird
3906                     if ( nDashWidth < nMinDashWidth )
3907                         nDashWidth = nMinDashWidth;
3908                     long nTempDotWidth = nDotWidth;
3909                     long nTempDashWidth = nDashWidth;
3910                     long nEnd = nLeft+nWidth;
3911                     while ( nLeft < nEnd )
3912                     {
3913                         if ( nLeft+nTempDotWidth > nEnd )
3914                             nTempDotWidth = nEnd-nLeft;
3915                         ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
3916                         nLeft += nDotWidth*2;
3917                         if ( nLeft > nEnd )
3918                             break;
3919                         if ( nLeft+nTempDotWidth > nEnd )
3920                             nTempDotWidth = nEnd-nLeft;
3921                         ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
3922                         nLeft += nDotWidth*2;
3923                         if ( nLeft > nEnd )
3924                             break;
3925                         if ( nLeft+nTempDashWidth > nEnd )
3926                             nTempDashWidth = nEnd-nLeft;
3927                         ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
3928                         nLeft += nDashWidth+nDotWidth;
3929                     }
3930                 }
3931                 break;
3932             default:
3933                 break;
3934         }
3935     }
3936 }
3937 
3938 // -----------------------------------------------------------------------
3939 
3940 void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY,
3941                                           long nDistX, long nDistY, long nWidth,
3942                                           FontStrikeout eStrikeout,
3943                                           Color aColor )
3944 {
3945     ImplFontEntry*  pFontEntry = mpFontEntry;
3946     long            nLineHeight = 0;
3947     long            nLinePos  = 0;
3948     long            nLinePos2 = 0;
3949 
3950 	long nY = nDistY;
3951 
3952     if ( eStrikeout > STRIKEOUT_LAST )
3953         eStrikeout = STRIKEOUT_SINGLE;
3954 
3955     switch ( eStrikeout )
3956     {
3957         case STRIKEOUT_SINGLE:
3958             nLineHeight = pFontEntry->maMetric.mnStrikeoutSize;
3959             nLinePos    = nY + pFontEntry->maMetric.mnStrikeoutOffset;
3960             break;
3961         case STRIKEOUT_BOLD:
3962             nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize;
3963             nLinePos    = nY + pFontEntry->maMetric.mnBStrikeoutOffset;
3964             break;
3965         case STRIKEOUT_DOUBLE:
3966             nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize;
3967             nLinePos    = nY + pFontEntry->maMetric.mnDStrikeoutOffset1;
3968             nLinePos2   = nY + pFontEntry->maMetric.mnDStrikeoutOffset2;
3969             break;
3970         default:
3971             break;
3972     }
3973 
3974     if ( nLineHeight )
3975     {
3976         if ( mbLineColor || mbInitLineColor )
3977         {
3978             mpGraphics->SetLineColor();
3979             mbInitLineColor = sal_True;
3980         }
3981         mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
3982         mbInitFillColor = sal_True;
3983 
3984         const long& nLeft = nDistX;
3985 
3986         switch ( eStrikeout )
3987         {
3988             case STRIKEOUT_SINGLE:
3989             case STRIKEOUT_BOLD:
3990                 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
3991                 break;
3992             case STRIKEOUT_DOUBLE:
3993                 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
3994                 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
3995                 break;
3996             default:
3997                 break;
3998         }
3999     }
4000 }
4001 
4002 // -----------------------------------------------------------------------
4003 
4004 void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY,
4005                                           long nDistX, long nDistY, long nWidth,
4006                                           FontStrikeout eStrikeout,
4007                                           Color aColor )
4008 {
4009     // PDF-export does its own strikeout drawing... why again?
4010     if( mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) )
4011         return;
4012 
4013     // prepare string for strikeout measurement
4014     static char cStrikeoutChar;
4015     if ( eStrikeout == STRIKEOUT_SLASH )
4016         cStrikeoutChar = '/';
4017     else // ( eStrikeout == STRIKEOUT_X )
4018         cStrikeoutChar = 'X';
4019 	static const int nTestStrLen = 4;
4020     static const int nMaxStrikeStrLen = 2048;
4021     xub_Unicode aChars[ nMaxStrikeStrLen +1]; // +1 for valgrind...
4022     for( int i = 0; i < nTestStrLen; ++i)
4023         aChars[i] = cStrikeoutChar;
4024     const String aStrikeoutTest( aChars, nTestStrLen );
4025 
4026     // calculate approximation of strikeout atom size
4027     long nStrikeoutWidth = nWidth;
4028     SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen );
4029     if( pLayout )
4030     {
4031         nStrikeoutWidth = (pLayout->GetTextWidth() +nTestStrLen/2) / (nTestStrLen * pLayout->GetUnitsPerPixel());
4032         pLayout->Release();
4033     }
4034     if( nStrikeoutWidth <= 0 ) // sanity check
4035         return;
4036 
4037     // calculate acceptable strikeout length
4038     // allow the strikeout to be one pixel larger than the text it strikes out
4039     long nMaxWidth = nStrikeoutWidth * 3 / 4;
4040     if ( nMaxWidth < 2 )
4041         nMaxWidth = 2;
4042     nMaxWidth += nWidth + 1;
4043 
4044     int nStrikeStrLen = (nMaxWidth - 1) / nStrikeoutWidth;
4045     // if the text width is smaller than the strikeout text, then do not
4046     // strike out at all. This case requires user interaction, e.g. adding
4047     // a space to the text
4048     if( nStrikeStrLen <= 0 )
4049         return;
4050     if( nStrikeStrLen > nMaxStrikeStrLen )
4051         nStrikeStrLen = nMaxStrikeStrLen;
4052 
4053     // build the strikeout string
4054     for( int i = nTestStrLen; i < nStrikeStrLen; ++i)
4055         aChars[i] = cStrikeoutChar;
4056     const String aStrikeoutText( aChars, xub_StrLen(nStrikeStrLen) );
4057 
4058     if( mpFontEntry->mnOrientation )
4059         ImplRotatePos( 0, 0, nDistX, nDistY, mpFontEntry->mnOrientation );
4060     nBaseX += nDistX;
4061     nBaseY += nDistY;
4062 
4063     // strikeout text has to be left aligned
4064     sal_uLong nOrigTLM = mnTextLayoutMode;
4065     mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED;
4066     pLayout = ImplLayout( aStrikeoutText, 0, STRING_LEN );
4067     mnTextLayoutMode = nOrigTLM;
4068 
4069     if( !pLayout )
4070         return;
4071 
4072     // draw the strikeout text
4073     const Color aOldColor = GetTextColor();
4074     SetTextColor( aColor );
4075     ImplInitTextColor();
4076 
4077     pLayout->DrawBase() = Point( nBaseX+mnTextOffX, nBaseY+mnTextOffY );
4078     pLayout->DrawText( *mpGraphics );
4079     pLayout->Release();
4080 
4081     SetTextColor( aOldColor );
4082     ImplInitTextColor();
4083 }
4084 
4085 // -----------------------------------------------------------------------
4086 
4087 void OutputDevice::ImplDrawTextLine( long nX, long nY,
4088                                      long nDistX, long nWidth,
4089                                      FontStrikeout eStrikeout,
4090                                      FontUnderline eUnderline,
4091                                      FontUnderline eOverline,
4092                                      sal_Bool bUnderlineAbove )
4093 {
4094     if ( !nWidth )
4095         return;
4096 
4097     Color           aStrikeoutColor = GetTextColor();
4098     Color           aUnderlineColor = GetTextLineColor();
4099     Color           aOverlineColor  = GetOverlineColor();
4100     sal_Bool            bStrikeoutDone = sal_False;
4101     sal_Bool            bUnderlineDone = sal_False;
4102     sal_Bool            bOverlineDone  = sal_False;
4103 
4104     if ( IsRTLEnabled() )
4105     {
4106         // --- RTL --- mirror at basex
4107         long nXAdd = nWidth - nDistX;
4108         if( mpFontEntry->mnOrientation )
4109             nXAdd = FRound( nXAdd * cos( mpFontEntry->mnOrientation * F_PI1800 ) );
4110         nX += nXAdd - 1;
4111     }
4112 
4113     if ( !IsTextLineColor() )
4114         aUnderlineColor = GetTextColor();
4115 
4116     if ( !IsOverlineColor() )
4117         aOverlineColor = GetTextColor();
4118 
4119     if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
4120          (eUnderline == UNDERLINE_WAVE) ||
4121          (eUnderline == UNDERLINE_DOUBLEWAVE) ||
4122          (eUnderline == UNDERLINE_BOLDWAVE) )
4123     {
4124         ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
4125         bUnderlineDone = sal_True;
4126     }
4127     if ( (eOverline == UNDERLINE_SMALLWAVE) ||
4128          (eOverline == UNDERLINE_WAVE) ||
4129          (eOverline == UNDERLINE_DOUBLEWAVE) ||
4130          (eOverline == UNDERLINE_BOLDWAVE) )
4131     {
4132         ImplDrawWaveTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, sal_True );
4133         bOverlineDone = sal_True;
4134     }
4135 
4136     if ( (eStrikeout == STRIKEOUT_SLASH) ||
4137          (eStrikeout == STRIKEOUT_X) )
4138     {
4139         ImplDrawStrikeoutChar( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
4140         bStrikeoutDone = sal_True;
4141     }
4142 
4143     if ( !bUnderlineDone )
4144         ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
4145 
4146     if ( !bOverlineDone )
4147         ImplDrawStraightTextLine( nX, nY, nDistX, 0, nWidth, eOverline, aOverlineColor, sal_True );
4148 
4149     if ( !bStrikeoutDone )
4150         ImplDrawStrikeoutLine( nX, nY, nDistX, 0, nWidth, eStrikeout, aStrikeoutColor );
4151 }
4152 
4153 // -----------------------------------------------------------------------
4154 
4155 void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout,
4156     FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, sal_Bool bWordLine, sal_Bool bUnderlineAbove )
4157 {
4158     if( bWordLine )
4159     {
4160         // draw everything relative to the layout base point
4161 	 const Point aStartPt = rSalLayout.DrawBase();
4162 	 // calculate distance of each word from the base point
4163         Point aPos;
4164         sal_Int32 nDist = 0, nWidth = 0, nAdvance=0;
4165         for( int nStart = 0;;)
4166         {
4167             // iterate through the layouted glyphs
4168             sal_GlyphId aGlyphId;
4169             if( !rSalLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
4170                 break;
4171 
4172             // calculate the boundaries of each word
4173             if( !rSalLayout.IsSpacingGlyph( aGlyphId ) )
4174             {
4175                 if( !nWidth )
4176                 {
4177                     // get the distance to the base point (as projected to baseline)
4178                     nDist = aPos.X() - aStartPt.X();
4179                     if( mpFontEntry->mnOrientation )
4180                     {
4181                         const long nDY = aPos.Y() - aStartPt.Y();
4182                         const double fRad = mpFontEntry->mnOrientation * F_PI1800;
4183                         nDist = FRound( nDist*cos(fRad) - nDY*sin(fRad) );
4184                     }
4185                 }
4186 
4187                 // update the length of the textline
4188                 nWidth += nAdvance;
4189             }
4190             else if( nWidth > 0 )
4191             {
4192 	         // draw the textline for each word
4193                 ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth,
4194                     eStrikeout, eUnderline, eOverline, bUnderlineAbove );
4195                 nWidth = 0;
4196             }
4197         }
4198 
4199         // draw textline for the last word
4200         if( nWidth > 0 )
4201         {
4202             ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), nDist, nWidth,
4203                 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
4204         }
4205     }
4206     else
4207     {
4208         Point aStartPt = rSalLayout.GetDrawPosition();
4209         int nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
4210         ImplDrawTextLine( aStartPt.X(), aStartPt.Y(), 0, nWidth,
4211             eStrikeout, eUnderline, eOverline, bUnderlineAbove );
4212     }
4213 }
4214 
4215 // -----------------------------------------------------------------------
4216 
4217 void OutputDevice::ImplDrawMnemonicLine( long nX, long nY, long nWidth )
4218 {
4219     long nBaseX = nX;
4220     if( /*ImplHasMirroredGraphics() &&*/ IsRTLEnabled() )
4221     {
4222         // --- RTL ---
4223         // add some strange offset
4224         nX += 2;
4225         // revert the hack that will be done later in ImplDrawTextLine
4226         nX = nBaseX - nWidth - (nX - nBaseX - 1);
4227     }
4228 
4229     ImplDrawTextLine( nX, nY, 0, nWidth, STRIKEOUT_NONE, UNDERLINE_SINGLE, UNDERLINE_NONE, sal_False );
4230 }
4231 
4232 // -----------------------------------------------------------------------
4233 
4234 void OutputDevice::ImplGetEmphasisMark( PolyPolygon& rPolyPoly, sal_Bool& rPolyLine,
4235                                         Rectangle& rRect1, Rectangle& rRect2,
4236                                         long& rYOff, long& rWidth,
4237                                         FontEmphasisMark eEmphasis,
4238                                         long nHeight, short /*nOrient*/ )
4239 {
4240     static const sal_uInt8 aAccentPolyFlags[24] =
4241     {
4242         0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 0, 2, 0, 2, 2
4243     };
4244 
4245     static const long aAccentPos[48] =
4246     {
4247          78,      0,
4248         348,     79,
4249         599,    235,
4250         843,    469,
4251         938,    574,
4252         990,    669,
4253         990,    773,
4254         990,    843,
4255         964,    895,
4256         921,    947,
4257         886,    982,
4258         860,    999,
4259         825,    999,
4260         764,    999,
4261         721,    964,
4262         686,    895,
4263         625,    791,
4264         556,    660,
4265         469,    504,
4266         400,    400,
4267         261,    252,
4268          61,     61,
4269           0,     27,
4270           9,      0
4271     };
4272 
4273     rWidth      = 0;
4274     rYOff       = 0;
4275     rPolyLine   = sal_False;
4276 
4277     if ( !nHeight )
4278         return;
4279 
4280     FontEmphasisMark    nEmphasisStyle = eEmphasis & EMPHASISMARK_STYLE;
4281     long                nDotSize = 0;
4282     switch ( nEmphasisStyle )
4283     {
4284         case EMPHASISMARK_DOT:
4285             // Dot has 55% of the height
4286             nDotSize = (nHeight*550)/1000;
4287             if ( !nDotSize )
4288                 nDotSize = 1;
4289             if ( nDotSize <= 2 )
4290                 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4291             else
4292             {
4293                 long nRad = nDotSize/2;
4294                 Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
4295                 rPolyPoly.Insert( aPoly );
4296             }
4297             rYOff = ((nHeight*250)/1000)/2; // Center to the anthoer EmphasisMarks
4298             rWidth = nDotSize;
4299             break;
4300 
4301         case EMPHASISMARK_CIRCLE:
4302             // Dot has 80% of the height
4303             nDotSize = (nHeight*800)/1000;
4304             if ( !nDotSize )
4305                 nDotSize = 1;
4306             if ( nDotSize <= 2 )
4307                 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4308             else
4309             {
4310                 long nRad = nDotSize/2;
4311                 Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
4312                 rPolyPoly.Insert( aPoly );
4313                 // BorderWidth is 15%
4314                 long nBorder = (nDotSize*150)/1000;
4315                 if ( nBorder <= 1 )
4316                     rPolyLine = sal_True;
4317                 else
4318                 {
4319                     Polygon aPoly2( Point( nRad, nRad ),
4320                                     nRad-nBorder, nRad-nBorder );
4321                     rPolyPoly.Insert( aPoly2 );
4322                 }
4323             }
4324             rWidth = nDotSize;
4325             break;
4326 
4327         case EMPHASISMARK_DISC:
4328             // Dot has 80% of the height
4329             nDotSize = (nHeight*800)/1000;
4330             if ( !nDotSize )
4331                 nDotSize = 1;
4332             if ( nDotSize <= 2 )
4333                 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4334             else
4335             {
4336                 long nRad = nDotSize/2;
4337                 Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
4338                 rPolyPoly.Insert( aPoly );
4339             }
4340             rWidth = nDotSize;
4341             break;
4342 
4343         case EMPHASISMARK_ACCENT:
4344             // Dot has 80% of the height
4345             nDotSize = (nHeight*800)/1000;
4346             if ( !nDotSize )
4347                 nDotSize = 1;
4348             if ( nDotSize <= 2 )
4349             {
4350                 if ( nDotSize == 1 )
4351                 {
4352                     rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4353                     rWidth = nDotSize;
4354                 }
4355                 else
4356                 {
4357                     rRect1 = Rectangle( Point(), Size( 1, 1 ) );
4358                     rRect2 = Rectangle( Point( 1, 1 ), Size( 1, 1 ) );
4359                 }
4360             }
4361             else
4362             {
4363                 Polygon aPoly( sizeof( aAccentPos ) / sizeof( long ) / 2,
4364                                (const Point*)aAccentPos,
4365                                aAccentPolyFlags );
4366                 double dScale = ((double)nDotSize)/1000.0;
4367                 aPoly.Scale( dScale, dScale );
4368                 Polygon aTemp;
4369                 aPoly.AdaptiveSubdivide( aTemp );
4370                 Rectangle aBoundRect = aTemp.GetBoundRect();
4371                 rWidth = aBoundRect.GetWidth();
4372                 nDotSize = aBoundRect.GetHeight();
4373                 rPolyPoly.Insert( aTemp );
4374             }
4375             break;
4376     }
4377 
4378     // calculate position
4379     long nOffY = 1+(mnDPIY/300); // one visible pixel space
4380     long nSpaceY = nHeight-nDotSize;
4381     if ( nSpaceY >= nOffY*2 )
4382         rYOff += nOffY;
4383     if ( !(eEmphasis & EMPHASISMARK_POS_BELOW) )
4384         rYOff += nDotSize;
4385 }
4386 
4387 // -----------------------------------------------------------------------
4388 
4389 void OutputDevice::ImplDrawEmphasisMark( long nBaseX, long nX, long nY,
4390                                          const PolyPolygon& rPolyPoly, sal_Bool bPolyLine,
4391                                          const Rectangle& rRect1, const Rectangle& rRect2 )
4392 {
4393     // TODO: pass nWidth as width of this mark
4394     long nWidth = 0;
4395 
4396     if( IsRTLEnabled() )
4397         // --- RTL --- mirror at basex
4398         nX = nBaseX - nWidth - (nX - nBaseX - 1);
4399 
4400     nX -= mnOutOffX;
4401     nY -= mnOutOffY;
4402 
4403     if ( rPolyPoly.Count() )
4404     {
4405         if ( bPolyLine )
4406         {
4407             Polygon aPoly = rPolyPoly.GetObject( 0 );
4408             aPoly.Move( nX, nY );
4409             DrawPolyLine( aPoly );
4410         }
4411         else
4412         {
4413             PolyPolygon aPolyPoly = rPolyPoly;
4414             aPolyPoly.Move( nX, nY );
4415             DrawPolyPolygon( aPolyPoly );
4416         }
4417     }
4418 
4419     if ( !rRect1.IsEmpty() )
4420     {
4421         Rectangle aRect( Point( nX+rRect1.Left(),
4422                                 nY+rRect1.Top() ), rRect1.GetSize() );
4423         DrawRect( aRect );
4424     }
4425 
4426     if ( !rRect2.IsEmpty() )
4427     {
4428         Rectangle aRect( Point( nX+rRect2.Left(),
4429                                 nY+rRect2.Top() ), rRect2.GetSize() );
4430 
4431         DrawRect( aRect );
4432     }
4433 }
4434 
4435 // -----------------------------------------------------------------------
4436 
4437 void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout )
4438 {
4439     Color               aOldColor       = GetTextColor();
4440     Color               aOldLineColor   = GetLineColor();
4441     Color               aOldFillColor   = GetFillColor();
4442     sal_Bool                bOldMap         = mbMap;
4443     GDIMetaFile*        pOldMetaFile    = mpMetaFile;
4444     mpMetaFile = NULL;
4445     EnableMapMode( sal_False );
4446 
4447     FontEmphasisMark    nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
4448     PolyPolygon         aPolyPoly;
4449     Rectangle           aRect1;
4450     Rectangle           aRect2;
4451     long                nEmphasisYOff;
4452     long                nEmphasisWidth;
4453     long                nEmphasisHeight;
4454     sal_Bool                bPolyLine;
4455 
4456     if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
4457         nEmphasisHeight = mnEmphasisDescent;
4458     else
4459         nEmphasisHeight = mnEmphasisAscent;
4460 
4461     ImplGetEmphasisMark( aPolyPoly, bPolyLine,
4462                          aRect1, aRect2,
4463                          nEmphasisYOff, nEmphasisWidth,
4464                          nEmphasisMark,
4465                          nEmphasisHeight, mpFontEntry->mnOrientation );
4466 
4467     if ( bPolyLine )
4468     {
4469         SetLineColor( GetTextColor() );
4470         SetFillColor();
4471     }
4472     else
4473     {
4474         SetLineColor();
4475         SetFillColor( GetTextColor() );
4476     }
4477 
4478     Point aOffset = Point(0,0);
4479 
4480     if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
4481         aOffset.Y() += mpFontEntry->maMetric.mnDescent + nEmphasisYOff;
4482     else
4483         aOffset.Y() -= mpFontEntry->maMetric.mnAscent + nEmphasisYOff;
4484 
4485     long nEmphasisWidth2  = nEmphasisWidth / 2;
4486     long nEmphasisHeight2 = nEmphasisHeight / 2;
4487     aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );
4488 
4489     Point aOutPoint;
4490     Rectangle aRectangle;
4491     for( int nStart = 0;;)
4492     {
4493         sal_GlyphId aGlyphId;
4494         if( !rSalLayout.GetNextGlyphs( 1, &aGlyphId, aOutPoint, nStart ) )
4495             break;
4496 
4497         if( !mpGraphics->GetGlyphBoundRect( aGlyphId, aRectangle ) )
4498             continue;
4499 
4500         if( !rSalLayout.IsSpacingGlyph( aGlyphId ) )
4501         {
4502             Point aAdjPoint = aOffset;
4503             aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2;
4504             if ( mpFontEntry->mnOrientation )
4505                 ImplRotatePos( 0, 0, aAdjPoint.X(), aAdjPoint.Y(), mpFontEntry->mnOrientation );
4506             aOutPoint += aAdjPoint;
4507             aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 );
4508             ImplDrawEmphasisMark( rSalLayout.DrawBase().X(),
4509                                   aOutPoint.X(), aOutPoint.Y(),
4510                                   aPolyPoly, bPolyLine, aRect1, aRect2 );
4511         }
4512     }
4513 
4514     SetLineColor( aOldLineColor );
4515     SetFillColor( aOldFillColor );
4516     EnableMapMode( bOldMap );
4517     mpMetaFile = pOldMetaFile;
4518 }
4519 
4520 // -----------------------------------------------------------------------
4521 
4522 bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout )
4523 {
4524     int nX = rSalLayout.DrawBase().X();
4525     int nY = rSalLayout.DrawBase().Y();
4526 
4527     Rectangle aBoundRect;
4528     rSalLayout.DrawBase() = Point( 0, 0 );
4529     rSalLayout.DrawOffset() = Point( 0, 0 );
4530     if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) )
4531     {
4532         // guess vertical text extents if GetBoundRect failed
4533         int nRight = rSalLayout.GetTextWidth();
4534         int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
4535         long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
4536         aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop );
4537     }
4538 
4539     // cache virtual device for rotation
4540     if ( !mpOutDevData )
4541         ImplInitOutDevData();
4542     if ( !mpOutDevData->mpRotateDev )
4543         mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 );
4544     VirtualDevice* pVDev = mpOutDevData->mpRotateDev;
4545 
4546     // size it accordingly
4547     if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) )
4548         return false;
4549 
4550     Font aFont( GetFont() );
4551     aFont.SetOrientation( 0 );
4552     aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
4553     pVDev->SetFont( aFont );
4554     pVDev->SetTextColor( Color( COL_BLACK ) );
4555     pVDev->SetTextFillColor();
4556     pVDev->ImplNewFont();
4557     pVDev->ImplInitFont();
4558     pVDev->ImplInitTextColor();
4559 
4560     // draw text into upper left corner
4561     rSalLayout.DrawBase() -= aBoundRect.TopLeft();
4562     rSalLayout.DrawText( *((OutputDevice*)pVDev)->mpGraphics );
4563 
4564     Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() );
4565     if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) )
4566         return false;
4567 
4568     // calculate rotation offset
4569     Polygon aPoly( aBoundRect );
4570     aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation );
4571     Point aPoint = aPoly.GetBoundRect().TopLeft();
4572     aPoint += Point( nX, nY );
4573 
4574     // mask output with text colored bitmap
4575     GDIMetaFile* pOldMetaFile = mpMetaFile;
4576     long nOldOffX = mnOutOffX;
4577     long nOldOffY = mnOutOffY;
4578     sal_Bool bOldMap = mbMap;
4579 
4580     mnOutOffX   = 0L;
4581     mnOutOffY   = 0L;
4582     mpMetaFile  = NULL;
4583     EnableMapMode( sal_False );
4584 
4585     DrawMask( aPoint, aBmp, GetTextColor() );
4586 
4587     EnableMapMode( bOldMap );
4588     mnOutOffX   = nOldOffX;
4589     mnOutOffY   = nOldOffY;
4590     mpMetaFile  = pOldMetaFile;
4591 
4592     return true;
4593 }
4594 
4595 // -----------------------------------------------------------------------
4596 
4597 void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, sal_Bool bTextLines )
4598 {
4599     if( mpFontEntry->mnOwnOrientation )
4600         if( ImplDrawRotateText( rSalLayout ) )
4601             return;
4602 
4603     long nOldX = rSalLayout.DrawBase().X();
4604     if( ! (mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) )
4605     {
4606         if( ImplHasMirroredGraphics() )
4607         {
4608             long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
4609             long x = rSalLayout.DrawBase().X();
4610        		rSalLayout.DrawBase().X() = w - 1 - x;
4611             if( !IsRTLEnabled() )
4612             {
4613                 OutputDevice *pOutDevRef = (OutputDevice *)this;
4614                 // mirror this window back
4615                 long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX;   // re-mirrored mnOutOffX
4616                 rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ;
4617             }
4618         }
4619         else if( IsRTLEnabled() )
4620         {
4621             //long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
4622             //long x = rSalLayout.DrawBase().X();
4623             OutputDevice *pOutDevRef = (OutputDevice *)this;
4624             // mirror this window back
4625             long devX = pOutDevRef->mnOutOffX;   // re-mirrored mnOutOffX
4626             rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX;
4627         }
4628 
4629         rSalLayout.DrawText( *mpGraphics );
4630     }
4631 
4632     rSalLayout.DrawBase().X() = nOldX;
4633 
4634     if( bTextLines )
4635         ImplDrawTextLines( rSalLayout,
4636             maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(),
4637             maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) );
4638 
4639     // emphasis marks
4640     if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
4641         ImplDrawEmphasisMarks( rSalLayout );
4642 }
4643 
4644 // -----------------------------------------------------------------------
4645 
4646 void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout )
4647 {
4648     Color       aOldColor           = GetTextColor();
4649     Color       aOldTextLineColor   = GetTextLineColor();
4650     Color       aOldOverlineColor   = GetOverlineColor();
4651     FontRelief  eRelief             = maFont.GetRelief();
4652 
4653     Point aOrigPos = rSalLayout.DrawBase();
4654     if ( eRelief != RELIEF_NONE )
4655     {
4656         Color   aReliefColor( COL_LIGHTGRAY );
4657         Color   aTextColor( aOldColor );
4658 
4659         Color   aTextLineColor( aOldTextLineColor );
4660         Color   aOverlineColor( aOldOverlineColor );
4661 
4662         // we don't have a automatic color, so black is always drawn on white
4663         if ( aTextColor.GetColor() == COL_BLACK )
4664             aTextColor = Color( COL_WHITE );
4665         if ( aTextLineColor.GetColor() == COL_BLACK )
4666             aTextLineColor = Color( COL_WHITE );
4667         if ( aOverlineColor.GetColor() == COL_BLACK )
4668             aOverlineColor = Color( COL_WHITE );
4669 
4670         // relief-color is black for white text, in all other cases
4671         // we set this to LightGray
4672         if ( aTextColor.GetColor() == COL_WHITE )
4673             aReliefColor = Color( COL_BLACK );
4674         SetTextLineColor( aReliefColor );
4675         SetOverlineColor( aReliefColor );
4676         SetTextColor( aReliefColor );
4677         ImplInitTextColor();
4678 
4679         // calculate offset - for high resolution printers the offset
4680         // should be greater so that the effect is visible
4681         long nOff = 1;
4682         nOff += mnDPIX/300;
4683 
4684         if ( eRelief == RELIEF_ENGRAVED )
4685             nOff = -nOff;
4686         rSalLayout.DrawOffset() += Point( nOff, nOff);
4687         ImplDrawTextDirect( rSalLayout, mbTextLines );
4688         rSalLayout.DrawOffset() -= Point( nOff, nOff);
4689 
4690         SetTextLineColor( aTextLineColor );
4691         SetOverlineColor( aOverlineColor );
4692         SetTextColor( aTextColor );
4693         ImplInitTextColor();
4694         ImplDrawTextDirect( rSalLayout, mbTextLines );
4695 
4696         SetTextLineColor( aOldTextLineColor );
4697         SetOverlineColor( aOldOverlineColor );
4698 
4699         if ( aTextColor != aOldColor )
4700         {
4701             SetTextColor( aOldColor );
4702             ImplInitTextColor();
4703         }
4704     }
4705     else
4706     {
4707         if ( maFont.IsShadow() )
4708         {
4709             long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24);
4710             if ( maFont.IsOutline() )
4711                 nOff++;
4712             SetTextLineColor();
4713             SetOverlineColor();
4714             if ( (GetTextColor().GetColor() == COL_BLACK)
4715             ||   (GetTextColor().GetLuminance() < 8) )
4716                 SetTextColor( Color( COL_LIGHTGRAY ) );
4717             else
4718                 SetTextColor( Color( COL_BLACK ) );
4719             ImplInitTextColor();
4720             rSalLayout.DrawBase() += Point( nOff, nOff );
4721             ImplDrawTextDirect( rSalLayout, mbTextLines );
4722             rSalLayout.DrawBase() -= Point( nOff, nOff );
4723             SetTextColor( aOldColor );
4724             SetTextLineColor( aOldTextLineColor );
4725             SetOverlineColor( aOldOverlineColor );
4726             ImplInitTextColor();
4727 
4728             if ( !maFont.IsOutline() )
4729                 ImplDrawTextDirect( rSalLayout, mbTextLines );
4730         }
4731 
4732         if ( maFont.IsOutline() )
4733         {
4734             rSalLayout.DrawBase() = aOrigPos + Point(-1,-1);
4735             ImplDrawTextDirect( rSalLayout, mbTextLines );
4736             rSalLayout.DrawBase() = aOrigPos + Point(+1,+1);
4737             ImplDrawTextDirect( rSalLayout, mbTextLines );
4738             rSalLayout.DrawBase() = aOrigPos + Point(-1,+0);
4739             ImplDrawTextDirect( rSalLayout, mbTextLines );
4740             rSalLayout.DrawBase() = aOrigPos + Point(-1,+1);
4741             ImplDrawTextDirect( rSalLayout, mbTextLines );
4742             rSalLayout.DrawBase() = aOrigPos + Point(+0,+1);
4743             ImplDrawTextDirect( rSalLayout, mbTextLines );
4744             rSalLayout.DrawBase() = aOrigPos + Point(+0,-1);
4745             ImplDrawTextDirect( rSalLayout, mbTextLines );
4746             rSalLayout.DrawBase() = aOrigPos + Point(+1,-1);
4747             ImplDrawTextDirect( rSalLayout, mbTextLines );
4748             rSalLayout.DrawBase() = aOrigPos + Point(+1,+0);
4749             ImplDrawTextDirect( rSalLayout, mbTextLines );
4750             rSalLayout.DrawBase() = aOrigPos;
4751 
4752             SetTextColor( Color( COL_WHITE ) );
4753             SetTextLineColor( Color( COL_WHITE ) );
4754             SetOverlineColor( Color( COL_WHITE ) );
4755             ImplInitTextColor();
4756             ImplDrawTextDirect( rSalLayout, mbTextLines );
4757             SetTextColor( aOldColor );
4758             SetTextLineColor( aOldTextLineColor );
4759             SetOverlineColor( aOldOverlineColor );
4760             ImplInitTextColor();
4761         }
4762     }
4763 }
4764 
4765 // -----------------------------------------------------------------------
4766 
4767 void OutputDevice::ImplDrawText( SalLayout& rSalLayout )
4768 {
4769     if( mbInitClipRegion )
4770         ImplInitClipRegion();
4771     if( mbOutputClipped )
4772         return;
4773     if( mbInitTextColor )
4774         ImplInitTextColor();
4775 
4776     rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY );
4777 
4778     if( IsTextFillColor() )
4779         ImplDrawTextBackground( rSalLayout );
4780 
4781     if( mbTextSpecial )
4782         ImplDrawSpecialText( rSalLayout );
4783     else
4784         ImplDrawTextDirect( rSalLayout, mbTextLines );
4785 }
4786 
4787 // -----------------------------------------------------------------------
4788 
4789 long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo,
4790                                      long nWidth, const XubString& rStr,
4791                                      sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout )
4792 {
4793     DBG_ASSERTWARNING( nWidth >= 0, "ImplGetTextLines: nWidth <= 0!" );
4794 
4795     if ( nWidth <= 0 )
4796         nWidth = 1;
4797 
4798     long nMaxLineWidth  = 0;
4799     rLineInfo.Clear();
4800     if ( rStr.Len() && (nWidth > 0) )
4801     {
4802         ::rtl::OUString aText( rStr );
4803         uno::Reference < i18n::XBreakIterator > xBI;
4804         // get service provider
4805         uno::Reference< lang::XMultiServiceFactory > xSMgr( unohelper::GetMultiServiceFactory() );
4806 
4807         uno::Reference< linguistic2::XHyphenator > xHyph;
4808         if( xSMgr.is() )
4809         {
4810             uno::Reference< linguistic2::XLinguServiceManager> xLinguMgr(xSMgr->createInstance(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.linguistic2.LinguServiceManager"))),uno::UNO_QUERY);
4811             if ( xLinguMgr.is() )
4812             {
4813                 xHyph = xLinguMgr->getHyphenator();
4814             }
4815         }
4816 
4817         i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, uno::Sequence <beans::PropertyValue>(), 1 );
4818         i18n::LineBreakUserOptions aUserOptions;
4819 
4820         xub_StrLen nPos = 0;
4821         xub_StrLen nLen = rStr.Len();
4822         while ( nPos < nLen )
4823         {
4824             xub_StrLen nBreakPos = nPos;
4825 
4826             while ( ( nBreakPos < nLen ) && ( rStr.GetChar( nBreakPos ) != _CR ) && ( rStr.GetChar( nBreakPos ) != _LF ) )
4827                 nBreakPos++;
4828 
4829             long nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
4830             if ( ( nLineWidth > nWidth ) && ( nStyle & TEXT_DRAW_WORDBREAK ) )
4831             {
4832                 if ( !xBI.is() )
4833                     xBI = vcl::unohelper::CreateBreakIterator();
4834 
4835                 if ( xBI.is() )
4836                 {
4837                     const com::sun::star::lang::Locale& rDefLocale(Application::GetSettings().GetUILocale());
4838                     xub_StrLen nSoftBreak = _rLayout.GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos );
4839                     DBG_ASSERT( nSoftBreak < nBreakPos, "Break?!" );
4840                     //aHyphOptions.hyphenIndex = nSoftBreak;
4841                     i18n::LineBreakResults aLBR = xBI->getLineBreak( aText, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions );
4842                     nBreakPos = (xub_StrLen)aLBR.breakIndex;
4843                     if ( nBreakPos <= nPos )
4844                         nBreakPos = nSoftBreak;
4845                     if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION )
4846                     {
4847                         // Egal ob Trenner oder nicht: Das Wort nach dem Trenner durch
4848 	                    // die Silbentrennung jagen...
4849 	                    // nMaxBreakPos ist das letzte Zeichen was in die Zeile passt,
4850 	                    // nBreakPos ist der Wort-Anfang
4851 	                    // Ein Problem gibt es, wenn das Dok so schmal ist, dass ein Wort
4852 	                    // auf mehr als Zwei Zeilen gebrochen wird...
4853 	                    if ( xHyph.is() )
4854 	                    {
4855                             sal_Unicode cAlternateReplChar = 0;
4856 	                        sal_Unicode cAlternateExtraChar = 0;
4857 			                i18n::Boundary aBoundary = xBI->getWordBoundary( aText, nBreakPos, rDefLocale, ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True );
4858                 //		    sal_uInt16 nWordStart = nBreakPos;
4859                 //		    sal_uInt16 nBreakPos_OLD = nBreakPos;
4860 		                    sal_uInt16 nWordStart = nPos;
4861                             sal_uInt16 nWordEnd = (sal_uInt16) aBoundary.endPos;
4862                             DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
4863 
4864                             sal_uInt16 nWordLen = nWordEnd - nWordStart;
4865 		                    if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) )
4866 		                    {
4867                                 // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
4868 			                    // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" );
4869 		                        String aWord( aText, nWordStart, nWordLen );
4870 			                    sal_uInt16 nMinTrail = static_cast<sal_uInt16>(nWordEnd-nSoftBreak+1); 	//+1: Vor dem angeknacksten Buchstaben
4871                                 uno::Reference< linguistic2::XHyphenatedWord > xHyphWord;
4872 			                    if (xHyph.is())
4873                                     xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.Len() - nMinTrail, uno::Sequence< beans::PropertyValue >() );
4874 			                    if (xHyphWord.is())
4875 			                    {
4876 				                    sal_Bool bAlternate = xHyphWord->isAlternativeSpelling();
4877 				                    sal_uInt16 _nWordLen = 1 + xHyphWord->getHyphenPos();
4878 
4879 				                    if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= ( 2 ) ) )
4880 				                    {
4881 					                    if ( !bAlternate )
4882 					                    {
4883 						                    nBreakPos = nWordStart + _nWordLen;
4884 					                    }
4885 					                    else
4886 					                    {
4887 						                    String aAlt( xHyphWord->getHyphenatedWord() );
4888 
4889 						                    // Wir gehen von zwei Faellen aus, die nun
4890 						                    // vorliegen koennen:
4891 						                    // 1) packen wird zu pak-ken
4892 						                    // 2) Schiffahrt wird zu Schiff-fahrt
4893 						                    // In Fall 1 muss ein Zeichen ersetzt werden,
4894 						                    // in Fall 2 wird ein Zeichen hinzugefuegt.
4895 						                    // Die Identifikation wird erschwert durch Worte wie
4896 						                    // "Schiffahrtsbrennesseln", da der Hyphenator alle
4897 						                    // Position des Wortes auftrennt und "Schifffahrtsbrennnesseln"
4898 						                    // ermittelt. Wir koennen also eigentlich nicht unmittelbar vom
4899 						                    // Index des AlternativWord auf aWord schliessen.
4900 
4901 						                    // Das ganze geraffel wird durch eine Funktion am
4902 						                    // Hyphenator vereinfacht werden, sobald AMA sie einbaut...
4903 						                    sal_uInt16 nAltStart = _nWordLen - 1;
4904 						                    sal_uInt16 nTxtStart = nAltStart - (aAlt.Len() - aWord.Len());
4905 						                    sal_uInt16 nTxtEnd = nTxtStart;
4906 						                    sal_uInt16 nAltEnd = nAltStart;
4907 
4908 						                    // Die Bereiche zwischen den nStart und nEnd ist
4909 						                    // die Differenz zwischen Alternativ- und OriginalString.
4910 						                    while( nTxtEnd < aWord.Len() && nAltEnd < aAlt.Len() &&
4911 							                       aWord.GetChar(nTxtEnd) != aAlt.GetChar(nAltEnd) )
4912 						                    {
4913 							                    ++nTxtEnd;
4914 							                    ++nAltEnd;
4915 						                    }
4916 
4917 						                    // Wenn ein Zeichen hinzugekommen ist, dann bemerken wir es jetzt:
4918 						                    if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
4919 							                    aWord.GetChar( nTxtEnd ) == aAlt.GetChar(nAltEnd) )
4920 						                    {
4921 							                    ++nAltEnd;
4922 							                    ++nTxtStart;
4923 							                    ++nTxtEnd;
4924 						                    }
4925 
4926 						                    DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Falsche Annahme!" );
4927 
4928 						                    if ( nTxtEnd > nTxtStart )
4929 							                    cAlternateReplChar = aAlt.GetChar( nAltStart );
4930 						                    else
4931 							                    cAlternateExtraChar = aAlt.GetChar( nAltStart );
4932 
4933 						                    nBreakPos = nWordStart + nTxtStart;
4934 						                    if ( cAlternateReplChar )
4935 							                    nBreakPos++;
4936 					                    }
4937 			                        } // if (xHyphWord.is())
4938 	                            } // if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) )
4939                             } // if ( xHyph.is() )
4940                         } // if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION )
4941 	                }
4942                     nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
4943                 }
4944                 else
4945                 {
4946                     // fallback to something really simple
4947                     sal_uInt16 nSpacePos = STRING_LEN;
4948                     long nW = 0;
4949                     do
4950                     {
4951                         nSpacePos = rStr.SearchBackward( sal_Unicode(' '), nSpacePos );
4952                         if( nSpacePos != STRING_NOTFOUND )
4953                         {
4954                             if( nSpacePos > nPos )
4955                                 nSpacePos--;
4956                             nW = _rLayout.GetTextWidth( rStr, nPos, nSpacePos-nPos );
4957                         }
4958                     } while( nW > nWidth );
4959 
4960                     if( nSpacePos != STRING_NOTFOUND )
4961                     {
4962                         nBreakPos = nSpacePos;
4963                         nLineWidth = _rLayout.GetTextWidth( rStr, nPos, nBreakPos-nPos );
4964                         if( nBreakPos < rStr.Len()-1 )
4965                             nBreakPos++;
4966                     }
4967                 }
4968             }
4969 
4970             if ( nLineWidth > nMaxLineWidth )
4971                 nMaxLineWidth = nLineWidth;
4972 
4973             rLineInfo.AddLine( new ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) );
4974 
4975             if ( nBreakPos == nPos )
4976                 nBreakPos++;
4977             nPos = nBreakPos;
4978 
4979             if ( ( rStr.GetChar( nPos ) == _CR ) || ( rStr.GetChar( nPos ) == _LF ) )
4980             {
4981                 nPos++;
4982                 // CR/LF?
4983                 if ( ( nPos < nLen ) && ( rStr.GetChar( nPos ) == _LF ) && ( rStr.GetChar( nPos-1 ) == _CR ) )
4984                     nPos++;
4985             }
4986         }
4987     }
4988 #ifdef DBG_UTIL
4989     for ( sal_uInt16 nL = 0; nL < rLineInfo.Count(); nL++ )
4990     {
4991         ImplTextLineInfo* pLine = rLineInfo.GetLine( nL );
4992         String aLine( rStr, pLine->GetIndex(), pLine->GetLen() );
4993         DBG_ASSERT( aLine.Search( _CR ) == STRING_NOTFOUND, "ImplGetTextLines - Found CR!" );
4994         DBG_ASSERT( aLine.Search( _LF ) == STRING_NOTFOUND, "ImplGetTextLines - Found LF!" );
4995     }
4996 #endif
4997 
4998     return nMaxLineWidth;
4999 }
5000 
5001 // =======================================================================
5002 
5003 void OutputDevice::SetAntialiasing( sal_uInt16 nMode )
5004 {
5005     if ( mnAntialiasing != nMode )
5006     {
5007         mnAntialiasing = nMode;
5008         mbInitFont = sal_True;
5009 
5010         if(mpGraphics)
5011         {
5012             mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW);
5013         }
5014     }
5015 
5016     if( mpAlphaVDev )
5017         mpAlphaVDev->SetAntialiasing( nMode );
5018 }
5019 
5020 // -----------------------------------------------------------------------
5021 
5022 void OutputDevice::SetFont( const Font& rNewFont )
5023 {
5024     DBG_TRACE( "OutputDevice::SetFont()" );
5025     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5026     DBG_CHKOBJ( &rNewFont, Font, NULL );
5027 
5028     Font aFont( rNewFont );
5029     aFont.SetLanguage(rNewFont.GetLanguage());
5030     if ( mnDrawMode & (DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | DRAWMODE_SETTINGSTEXT |
5031                        DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | DRAWMODE_GRAYFILL | DRAWMODE_NOFILL |
5032                        DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) )
5033     {
5034         Color aTextColor( aFont.GetColor() );
5035 
5036         if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5037             aTextColor = Color( COL_BLACK );
5038         else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5039             aTextColor = Color( COL_WHITE );
5040         else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5041         {
5042             const sal_uInt8 cLum = aTextColor.GetLuminance();
5043             aTextColor = Color( cLum, cLum, cLum );
5044         }
5045         else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5046             aTextColor = GetSettings().GetStyleSettings().GetFontColor();
5047 
5048         if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT )
5049         {
5050             aTextColor = Color( (aTextColor.GetRed() >> 1 ) | 0x80,
5051                                 (aTextColor.GetGreen() >> 1 ) | 0x80,
5052                                 (aTextColor.GetBlue() >> 1 ) | 0x80 );
5053         }
5054 
5055         aFont.SetColor( aTextColor );
5056 
5057         sal_Bool bTransFill = aFont.IsTransparent();
5058         if ( !bTransFill )
5059         {
5060             Color aTextFillColor( aFont.GetFillColor() );
5061 
5062             if ( mnDrawMode & DRAWMODE_BLACKFILL )
5063                 aTextFillColor = Color( COL_BLACK );
5064             else if ( mnDrawMode & DRAWMODE_WHITEFILL )
5065                 aTextFillColor = Color( COL_WHITE );
5066             else if ( mnDrawMode & DRAWMODE_GRAYFILL )
5067             {
5068                 const sal_uInt8 cLum = aTextFillColor.GetLuminance();
5069                 aTextFillColor = Color( cLum, cLum, cLum );
5070             }
5071             else if( mnDrawMode & DRAWMODE_SETTINGSFILL )
5072                 aTextFillColor = GetSettings().GetStyleSettings().GetWindowColor();
5073             else if ( mnDrawMode & DRAWMODE_NOFILL )
5074             {
5075                 aTextFillColor = Color( COL_TRANSPARENT );
5076                 bTransFill = sal_True;
5077             }
5078 
5079             if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) )
5080             {
5081                 aTextFillColor = Color( (aTextFillColor.GetRed() >> 1) | 0x80,
5082                                         (aTextFillColor.GetGreen() >> 1) | 0x80,
5083                                         (aTextFillColor.GetBlue() >> 1) | 0x80 );
5084             }
5085 
5086             aFont.SetFillColor( aTextFillColor );
5087         }
5088     }
5089 
5090     if ( mpMetaFile )
5091     {
5092         mpMetaFile->AddAction( new MetaFontAction( aFont ) );
5093         // the color and alignment actions don't belong here
5094         // TODO: get rid of them without breaking anything...
5095         mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlign() ) );
5096         mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) );
5097     }
5098 
5099 #if (OSL_DEBUG_LEVEL > 2) || defined (HDU_DEBUG)
5100     fprintf( stderr, "   OutputDevice::SetFont( name=\"%s\", h=%ld)\n",
5101          OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr(),
5102          aFont.GetSize().Height() );
5103 #endif
5104 
5105     if ( !maFont.IsSameInstance( aFont ) )
5106     {
5107         // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color,
5108         // because SetTextColor() is used for this.
5109         // #i28759# maTextColor might have been changed behind our back, commit then, too.
5110         if( aFont.GetColor() != COL_TRANSPARENT
5111         && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) )
5112         {
5113             maTextColor = aFont.GetColor();
5114             mbInitTextColor = sal_True;
5115             if( mpMetaFile )
5116                 mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) );
5117         }
5118         maFont      = aFont;
5119         mbNewFont   = sal_True;
5120 
5121         if( mpAlphaVDev )
5122         {
5123             // #i30463#
5124             // Since SetFont might change the text color, apply that only
5125             // selectively to alpha vdev (which normally paints opaque text
5126             // with COL_BLACK)
5127             if( aFont.GetColor() != COL_TRANSPARENT )
5128             {
5129                 mpAlphaVDev->SetTextColor( COL_BLACK );
5130                 aFont.SetColor( COL_TRANSPARENT );
5131             }
5132 
5133             mpAlphaVDev->SetFont( aFont );
5134         }
5135     }
5136 }
5137 
5138 // -----------------------------------------------------------------------
5139 
5140 void OutputDevice::SetLayoutMode( sal_uLong nTextLayoutMode )
5141 {
5142     DBG_TRACE( "OutputDevice::SetTextLayoutMode()" );
5143 
5144     if( mpMetaFile )
5145         mpMetaFile->AddAction( new MetaLayoutModeAction( nTextLayoutMode ) );
5146 
5147     mnTextLayoutMode = nTextLayoutMode;
5148 
5149     if( mpAlphaVDev )
5150         mpAlphaVDev->SetLayoutMode( nTextLayoutMode );
5151 }
5152 
5153 // -----------------------------------------------------------------------
5154 
5155 void OutputDevice::SetDigitLanguage( LanguageType eTextLanguage )
5156 {
5157     DBG_TRACE( "OutputDevice::SetTextLanguage()" );
5158 
5159     if( mpMetaFile )
5160         mpMetaFile->AddAction( new MetaTextLanguageAction( eTextLanguage ) );
5161 
5162     meTextLanguage = eTextLanguage;
5163 
5164     if( mpAlphaVDev )
5165         mpAlphaVDev->SetDigitLanguage( eTextLanguage );
5166 }
5167 
5168 // -----------------------------------------------------------------------
5169 
5170 void OutputDevice::SetTextColor( const Color& rColor )
5171 {
5172     DBG_TRACE( "OutputDevice::SetTextColor()" );
5173     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5174 
5175     Color aColor( rColor );
5176 
5177     if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
5178                         DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
5179                         DRAWMODE_SETTINGSTEXT ) )
5180     {
5181         if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5182             aColor = Color( COL_BLACK );
5183         else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5184             aColor = Color( COL_WHITE );
5185         else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5186         {
5187             const sal_uInt8 cLum = aColor.GetLuminance();
5188             aColor = Color( cLum, cLum, cLum );
5189         }
5190         else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5191             aColor = GetSettings().GetStyleSettings().GetFontColor();
5192 
5193         if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT )
5194         {
5195             aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5196                             (aColor.GetGreen() >> 1) | 0x80,
5197                             (aColor.GetBlue() >> 1) | 0x80 );
5198         }
5199     }
5200 
5201     if ( mpMetaFile )
5202         mpMetaFile->AddAction( new MetaTextColorAction( aColor ) );
5203 
5204     if ( maTextColor != aColor )
5205     {
5206         maTextColor = aColor;
5207         mbInitTextColor = sal_True;
5208     }
5209 
5210     if( mpAlphaVDev )
5211         mpAlphaVDev->SetTextColor( COL_BLACK );
5212 }
5213 
5214 // -----------------------------------------------------------------------
5215 
5216 void OutputDevice::SetTextFillColor()
5217 {
5218     DBG_TRACE( "OutputDevice::SetTextFillColor()" );
5219     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5220 
5221     if ( mpMetaFile )
5222         mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), sal_False ) );
5223 
5224     if ( maFont.GetColor() != Color( COL_TRANSPARENT ) )
5225         maFont.SetFillColor( Color( COL_TRANSPARENT ) );
5226     if ( !maFont.IsTransparent() )
5227         maFont.SetTransparent( sal_True );
5228 
5229     if( mpAlphaVDev )
5230         mpAlphaVDev->SetTextFillColor();
5231 }
5232 
5233 // -----------------------------------------------------------------------
5234 
5235 void OutputDevice::SetTextFillColor( const Color& rColor )
5236 {
5237     DBG_TRACE( "OutputDevice::SetTextFillColor()" );
5238     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5239 
5240     Color aColor( rColor );
5241     sal_Bool bTransFill = ImplIsColorTransparent( aColor ) ? sal_True : sal_False;
5242 
5243     if ( !bTransFill )
5244     {
5245         if ( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL |
5246                             DRAWMODE_GRAYFILL | DRAWMODE_NOFILL |
5247                             DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) )
5248         {
5249             if ( mnDrawMode & DRAWMODE_BLACKFILL )
5250                 aColor = Color( COL_BLACK );
5251             else if ( mnDrawMode & DRAWMODE_WHITEFILL )
5252                 aColor = Color( COL_WHITE );
5253             else if ( mnDrawMode & DRAWMODE_GRAYFILL )
5254             {
5255                 const sal_uInt8 cLum = aColor.GetLuminance();
5256                 aColor = Color( cLum, cLum, cLum );
5257             }
5258             else if( mnDrawMode & DRAWMODE_SETTINGSFILL )
5259                 aColor = GetSettings().GetStyleSettings().GetWindowColor();
5260             else if ( mnDrawMode & DRAWMODE_NOFILL )
5261             {
5262                 aColor = Color( COL_TRANSPARENT );
5263                 bTransFill = sal_True;
5264             }
5265 
5266             if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) )
5267             {
5268                 aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5269                                 (aColor.GetGreen() >> 1) | 0x80,
5270                                 (aColor.GetBlue() >> 1) | 0x80 );
5271             }
5272         }
5273     }
5274 
5275     if ( mpMetaFile )
5276         mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, sal_True ) );
5277 
5278     if ( maFont.GetFillColor() != aColor )
5279         maFont.SetFillColor( aColor );
5280     if ( maFont.IsTransparent() != bTransFill )
5281         maFont.SetTransparent( bTransFill );
5282 
5283     if( mpAlphaVDev )
5284         mpAlphaVDev->SetTextFillColor( COL_BLACK );
5285 }
5286 
5287 // -----------------------------------------------------------------------
5288 
5289 Color OutputDevice::GetTextFillColor() const
5290 {
5291     if ( maFont.IsTransparent() )
5292         return Color( COL_TRANSPARENT );
5293     else
5294         return maFont.GetFillColor();
5295 }
5296 
5297 // -----------------------------------------------------------------------
5298 
5299 void OutputDevice::SetTextLineColor()
5300 {
5301     DBG_TRACE( "OutputDevice::SetTextLineColor()" );
5302     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5303 
5304     if ( mpMetaFile )
5305         mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), sal_False ) );
5306 
5307     maTextLineColor = Color( COL_TRANSPARENT );
5308 
5309     if( mpAlphaVDev )
5310         mpAlphaVDev->SetTextLineColor();
5311 }
5312 
5313 // -----------------------------------------------------------------------
5314 
5315 void OutputDevice::SetTextLineColor( const Color& rColor )
5316 {
5317     DBG_TRACE( "OutputDevice::SetTextLineColor()" );
5318     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5319 
5320     Color aColor( rColor );
5321 
5322     if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
5323                         DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
5324                         DRAWMODE_SETTINGSTEXT ) )
5325     {
5326         if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5327             aColor = Color( COL_BLACK );
5328         else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5329             aColor = Color( COL_WHITE );
5330         else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5331         {
5332             const sal_uInt8 cLum = aColor.GetLuminance();
5333             aColor = Color( cLum, cLum, cLum );
5334         }
5335         else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5336             aColor = GetSettings().GetStyleSettings().GetFontColor();
5337 
5338         if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT)
5339         &&  (aColor.GetColor() != COL_TRANSPARENT) )
5340         {
5341             aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5342                             (aColor.GetGreen() >> 1) | 0x80,
5343                             (aColor.GetBlue() >> 1) | 0x80 );
5344         }
5345     }
5346 
5347     if ( mpMetaFile )
5348         mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, sal_True ) );
5349 
5350     maTextLineColor = aColor;
5351 
5352     if( mpAlphaVDev )
5353         mpAlphaVDev->SetTextLineColor( COL_BLACK );
5354 }
5355 
5356 // -----------------------------------------------------------------------
5357 
5358 void OutputDevice::SetOverlineColor()
5359 {
5360     DBG_TRACE( "OutputDevice::SetOverlineColor()" );
5361     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5362 
5363     if ( mpMetaFile )
5364         mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), sal_False ) );
5365 
5366     maOverlineColor = Color( COL_TRANSPARENT );
5367 
5368     if( mpAlphaVDev )
5369         mpAlphaVDev->SetOverlineColor();
5370 }
5371 
5372 // -----------------------------------------------------------------------
5373 
5374 void OutputDevice::SetOverlineColor( const Color& rColor )
5375 {
5376     DBG_TRACE( "OutputDevice::SetOverlineColor()" );
5377     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5378 
5379     Color aColor( rColor );
5380 
5381     if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
5382                         DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
5383                         DRAWMODE_SETTINGSTEXT ) )
5384     {
5385         if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5386             aColor = Color( COL_BLACK );
5387         else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5388             aColor = Color( COL_WHITE );
5389         else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5390         {
5391             const sal_uInt8 cLum = aColor.GetLuminance();
5392             aColor = Color( cLum, cLum, cLum );
5393         }
5394         else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5395             aColor = GetSettings().GetStyleSettings().GetFontColor();
5396 
5397         if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT)
5398         &&  (aColor.GetColor() != COL_TRANSPARENT) )
5399         {
5400             aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5401                             (aColor.GetGreen() >> 1) | 0x80,
5402                             (aColor.GetBlue() >> 1) | 0x80 );
5403         }
5404     }
5405 
5406     if ( mpMetaFile )
5407         mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, sal_True ) );
5408 
5409     maOverlineColor = aColor;
5410 
5411     if( mpAlphaVDev )
5412         mpAlphaVDev->SetOverlineColor( COL_BLACK );
5413 }
5414 
5415 // -----------------------------------------------------------------------
5416 
5417 
5418 void OutputDevice::SetTextAlign( TextAlign eAlign )
5419 {
5420     DBG_TRACE( "OutputDevice::SetTextAlign()" );
5421     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5422 
5423     if ( mpMetaFile )
5424         mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) );
5425 
5426     if ( maFont.GetAlign() != eAlign )
5427     {
5428         maFont.SetAlign( eAlign );
5429         mbNewFont = sal_True;
5430     }
5431 
5432     if( mpAlphaVDev )
5433         mpAlphaVDev->SetTextAlign( eAlign );
5434 }
5435 
5436 // -----------------------------------------------------------------------
5437 
5438 void OutputDevice::DrawTextLine( const Point& rPos, long nWidth,
5439                                  FontStrikeout eStrikeout,
5440                                  FontUnderline eUnderline,
5441                                  FontUnderline eOverline,
5442                                  sal_Bool bUnderlineAbove )
5443 {
5444     DBG_TRACE( "OutputDevice::DrawTextLine()" );
5445     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5446 
5447     if ( mpMetaFile )
5448         mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) );
5449 
5450     if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) &&
5451          ((eOverline  == UNDERLINE_NONE) || (eOverline  == UNDERLINE_DONTKNOW)) &&
5452          ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
5453         return;
5454 
5455     if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
5456         return;
5457 
5458     // we need a graphics
5459     if( !mpGraphics && !ImplGetGraphics() )
5460         return;
5461     if( mbInitClipRegion )
5462         ImplInitClipRegion();
5463     if( mbOutputClipped )
5464         return;
5465 
5466     // initialize font if needed to get text offsets
5467     // TODO: only needed for mnTextOff!=(0,0)
5468     if( mbNewFont )
5469         if( !ImplNewFont() )
5470             return;
5471     if( mbInitFont )
5472         ImplInitFont();
5473 
5474     Point aPos = ImplLogicToDevicePixel( rPos );
5475     nWidth = ImplLogicWidthToDevicePixel( nWidth );
5476     aPos += Point( mnTextOffX, mnTextOffY );
5477     ImplDrawTextLine( aPos.X(), aPos.X(), 0, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
5478 
5479     if( mpAlphaVDev )
5480         mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
5481 }
5482 
5483 // ------------------------------------------------------------------------
5484 
5485 sal_Bool OutputDevice::IsTextUnderlineAbove( const Font& rFont )
5486 {
5487     return ImplIsUnderlineAbove( rFont );
5488 }
5489 
5490 // ------------------------------------------------------------------------
5491 
5492 void OutputDevice::DrawWaveLine( const Point& rStartPos, const Point& rEndPos,
5493                                  sal_uInt16 nStyle )
5494 {
5495     DBG_TRACE( "OutputDevice::DrawWaveLine()" );
5496     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5497 
5498     if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
5499         return;
5500 
5501     // we need a graphics
5502     if( !mpGraphics )
5503         if( !ImplGetGraphics() )
5504             return;
5505 
5506     if ( mbInitClipRegion )
5507         ImplInitClipRegion();
5508     if ( mbOutputClipped )
5509         return;
5510 
5511     if( mbNewFont )
5512         if( !ImplNewFont() )
5513             return;
5514 
5515     Point   aStartPt = ImplLogicToDevicePixel( rStartPos );
5516     Point   aEndPt = ImplLogicToDevicePixel( rEndPos );
5517     long    nStartX = aStartPt.X();
5518     long    nStartY = aStartPt.Y();
5519     long    nEndX = aEndPt.X();
5520     long    nEndY = aEndPt.Y();
5521     short   nOrientation = 0;
5522 
5523     // when rotated
5524     if ( (nStartY != nEndY) || (nStartX > nEndX) )
5525     {
5526         long nDX = nEndX - nStartX;
5527         double nO = atan2( -nEndY + nStartY, ((nDX == 0L) ? 0.000000001 : nDX) );
5528         nO /= F_PI1800;
5529         nOrientation = (short)nO;
5530         ImplRotatePos( nStartX, nStartY, nEndX, nEndY, -nOrientation );
5531     }
5532 
5533     long nWaveHeight;
5534     if ( nStyle == WAVE_NORMAL )
5535     {
5536         nWaveHeight = 3;
5537         nStartY++;
5538         nEndY++;
5539     }
5540     else if( nStyle == WAVE_SMALL )
5541     {
5542         nWaveHeight = 2;
5543         nStartY++;
5544         nEndY++;
5545     }
5546     else // WAVE_FLAT
5547         nWaveHeight = 1;
5548 
5549      // #109280# make sure the waveline does not exceed the descent to avoid paint problems
5550      ImplFontEntry* pFontEntry = mpFontEntry;
5551      if( nWaveHeight > pFontEntry->maMetric.mnWUnderlineSize )
5552          nWaveHeight = pFontEntry->maMetric.mnWUnderlineSize;
5553 
5554      ImplDrawWaveLine( nStartX, nStartY, 0, 0,
5555                       nEndX-nStartX, nWaveHeight, 1,
5556                       nOrientation, GetLineColor() );
5557     if( mpAlphaVDev )
5558         mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos, nStyle );
5559 }
5560 
5561 // -----------------------------------------------------------------------
5562 
5563 void OutputDevice::DrawText( const Point& rStartPt, const String& rStr,
5564                              xub_StrLen nIndex, xub_StrLen nLen,
5565                              MetricVector* pVector, String* pDisplayText
5566                              )
5567 {
5568     if( mpOutDevData && mpOutDevData->mpRecordLayout )
5569     {
5570         pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
5571         pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
5572     }
5573 
5574     DBG_TRACE( "OutputDevice::DrawText()" );
5575     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5576 
5577 #if OSL_DEBUG_LEVEL > 2
5578     fprintf( stderr, "   OutputDevice::DrawText(\"%s\")\n",
5579 	     OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ).getStr() );
5580 #endif
5581 
5582     if ( mpMetaFile )
5583         mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
5584     if( pVector )
5585     {
5586         Region aClip( GetClipRegion() );
5587         if( meOutDevType == OUTDEV_WINDOW )
5588             aClip.Intersect( Rectangle( Point(), GetOutputSize() ) );
5589         if( mpOutDevData && mpOutDevData->mpRecordLayout )
5590 		{
5591             mpOutDevData->mpRecordLayout->m_aLineIndices.push_back( mpOutDevData->mpRecordLayout->m_aDisplayText.Len() );
5592 			aClip.Intersect( mpOutDevData->maRecordRect );
5593 		}
5594         if( ! aClip.IsNull() )
5595         {
5596             MetricVector aTmp;
5597             GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp );
5598 
5599             bool bInserted = false;
5600             for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ )
5601             {
5602                 bool bAppend = false;
5603 
5604                 if( aClip.IsOver( *it ) )
5605                     bAppend = true;
5606                 else if( rStr.GetChar( nIndex ) == ' ' && bInserted )
5607                 {
5608                     MetricVector::const_iterator next = it;
5609                     ++next;
5610                     if( next != aTmp.end() && aClip.IsOver( *next ) )
5611                         bAppend = true;
5612                 }
5613 
5614                 if( bAppend )
5615                 {
5616                     pVector->push_back( *it );
5617                     if( pDisplayText )
5618                         pDisplayText->Append( rStr.GetChar( nIndex ) );
5619                     bInserted = true;
5620                 }
5621             }
5622         }
5623         else
5624         {
5625             GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector );
5626             if( pDisplayText )
5627                 pDisplayText->Append( rStr.Copy( nIndex, nLen ) );
5628         }
5629     }
5630 
5631     if ( !IsDeviceOutputNecessary() || pVector )
5632         return;
5633 
5634     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, NULL, true );
5635     if( pSalLayout )
5636     {
5637         ImplDrawText( *pSalLayout );
5638         pSalLayout->Release();
5639     }
5640 
5641     if( mpAlphaVDev )
5642         mpAlphaVDev->DrawText( rStartPt, rStr, nIndex, nLen, pVector, pDisplayText );
5643 }
5644 
5645 // -----------------------------------------------------------------------
5646 
5647 long OutputDevice::GetTextWidth( const String& rStr,
5648                                  xub_StrLen nIndex, xub_StrLen nLen ) const
5649 {
5650     DBG_TRACE( "OutputDevice::GetTextWidth()" );
5651     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5652 
5653     long nWidth = GetTextArray( rStr, NULL, nIndex, nLen );
5654     return nWidth;
5655 }
5656 
5657 // -----------------------------------------------------------------------
5658 
5659 long OutputDevice::GetTextHeight() const
5660 {
5661     DBG_TRACE( "OutputDevice::GetTextHeight()" );
5662     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5663 
5664     if( mbNewFont )
5665         if( !ImplNewFont() )
5666             return 0;
5667     if( mbInitFont )
5668         if( !ImplNewFont() )
5669             return 0;
5670 
5671     long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
5672 
5673     if ( mbMap )
5674         nHeight = ImplDevicePixelToLogicHeight( nHeight );
5675 
5676     return nHeight;
5677 }
5678 
5679 // -----------------------------------------------------------------------
5680 
5681 void OutputDevice::DrawTextArray( const Point& rStartPt, const String& rStr,
5682                                   const sal_Int32* pDXAry,
5683                                   xub_StrLen nIndex, xub_StrLen nLen )
5684 {
5685     DBG_TRACE( "OutputDevice::DrawTextArray()" );
5686     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5687 
5688     if ( mpMetaFile )
5689         mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
5690 
5691     if ( !IsDeviceOutputNecessary() )
5692         return;
5693     if( !mpGraphics && !ImplGetGraphics() )
5694         return;
5695     if( mbInitClipRegion )
5696         ImplInitClipRegion();
5697     if( mbOutputClipped )
5698         return;
5699 
5700     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true );
5701     if( pSalLayout )
5702     {
5703         ImplDrawText( *pSalLayout );
5704         pSalLayout->Release();
5705     }
5706 
5707     if( mpAlphaVDev )
5708         mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen );
5709 }
5710 
5711 // -----------------------------------------------------------------------
5712 
5713 long OutputDevice::GetTextArray( const String& rStr, sal_Int32* pDXAry,
5714                                  xub_StrLen nIndex, xub_StrLen nLen ) const
5715 {
5716     DBG_TRACE( "OutputDevice::GetTextArray()" );
5717     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5718 
5719     if( nIndex >= rStr.Len() )
5720         return 0;
5721     if( (sal_uLong)nIndex+nLen >= rStr.Len() )
5722         nLen = rStr.Len() - nIndex;
5723 
5724     // do layout
5725     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
5726     if( !pSalLayout )
5727         return 0;
5728 
5729     long nWidth = pSalLayout->FillDXArray( pDXAry );
5730     int nWidthFactor = pSalLayout->GetUnitsPerPixel();
5731     pSalLayout->Release();
5732 
5733     // convert virtual char widths to virtual absolute positions
5734     if( pDXAry )
5735         for( int i = 1; i < nLen; ++i )
5736             pDXAry[ i ] += pDXAry[ i-1 ];
5737 
5738     // convert from font units to logical units
5739     if( mbMap )
5740     {
5741         if( pDXAry )
5742             for( int i = 0; i < nLen; ++i )
5743                 pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] );
5744         nWidth = ImplDevicePixelToLogicWidth( nWidth );
5745     }
5746 
5747     if( nWidthFactor > 1 )
5748     {
5749         if( pDXAry )
5750             for( int i = 0; i < nLen; ++i )
5751                 pDXAry[i] /= nWidthFactor;
5752         nWidth /= nWidthFactor;
5753     }
5754 
5755     return nWidth;
5756 }
5757 
5758 // -----------------------------------------------------------------------
5759 
5760 bool OutputDevice::GetCaretPositions( const XubString& rStr, sal_Int32* pCaretXArray,
5761     xub_StrLen nIndex, xub_StrLen nLen,
5762     sal_Int32* pDXAry, long nLayoutWidth,
5763     sal_Bool bCellBreaking ) const
5764 {
5765     DBG_TRACE( "OutputDevice::GetCaretPositions()" );
5766     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5767 
5768     if( nIndex >= rStr.Len() )
5769         return false;
5770     if( (sal_uLong)nIndex+nLen >= rStr.Len() )
5771         nLen = rStr.Len() - nIndex;
5772 
5773     // layout complex text
5774     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen,
5775         Point(0,0), nLayoutWidth, pDXAry );
5776     if( !pSalLayout )
5777         return false;
5778 
5779     int nWidthFactor = pSalLayout->GetUnitsPerPixel();
5780     pSalLayout->GetCaretPositions( 2*nLen, pCaretXArray );
5781     long nWidth = pSalLayout->GetTextWidth();
5782     pSalLayout->Release();
5783 
5784     // fixup unknown caret positions
5785     int i;
5786     for( i = 0; i < 2 * nLen; ++i )
5787         if( pCaretXArray[ i ] >= 0 )
5788             break;
5789     long nXPos = pCaretXArray[ i ];
5790     for( i = 0; i < 2 * nLen; ++i )
5791     {
5792         if( pCaretXArray[ i ] >= 0 )
5793             nXPos = pCaretXArray[ i ];
5794         else
5795             pCaretXArray[ i ] = nXPos;
5796     }
5797 
5798     // handle window mirroring
5799     if( IsRTLEnabled() )
5800     {
5801         for( i = 0; i < 2 * nLen; ++i )
5802             pCaretXArray[i] = nWidth - pCaretXArray[i] - 1;
5803     }
5804 
5805     // convert from font units to logical units
5806     if( mbMap )
5807     {
5808         for( i = 0; i < 2*nLen; ++i )
5809             pCaretXArray[i] = ImplDevicePixelToLogicWidth( pCaretXArray[i] );
5810     }
5811 
5812     if( nWidthFactor != 1 )
5813     {
5814         for( i = 0; i < 2*nLen; ++i )
5815             pCaretXArray[i] /= nWidthFactor;
5816     }
5817 
5818     // if requested move caret position to cell limits
5819     if( bCellBreaking )
5820     {
5821         ; // TODO
5822     }
5823 
5824     return true;
5825 }
5826 
5827 // -----------------------------------------------------------------------
5828 
5829 void OutputDevice::DrawStretchText( const Point& rStartPt, sal_uLong nWidth,
5830                                     const String& rStr,
5831                                     xub_StrLen nIndex, xub_StrLen nLen )
5832 {
5833     DBG_TRACE( "OutputDevice::DrawStretchText()" );
5834     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5835 
5836     if ( mpMetaFile )
5837         mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) );
5838 
5839     if ( !IsDeviceOutputNecessary() )
5840         return;
5841 
5842     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, nWidth, NULL, true );
5843     if( pSalLayout )
5844     {
5845         ImplDrawText( *pSalLayout );
5846         pSalLayout->Release();
5847     }
5848 
5849     if( mpAlphaVDev )
5850         mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen );
5851 }
5852 
5853 // -----------------------------------------------------------------------
5854 
5855 ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( String& rStr,
5856                                        xub_StrLen nMinIndex, xub_StrLen nLen,
5857                                        long nPixelWidth, const sal_Int32* pDXArray ) const
5858 {
5859     // get string length for calculating extents
5860     xub_StrLen nEndIndex = rStr.Len();
5861     if( (sal_uLong)nMinIndex + nLen < nEndIndex )
5862         nEndIndex = nMinIndex + nLen;
5863 
5864     // don't bother if there is nothing to do
5865     if( nEndIndex < nMinIndex )
5866         nEndIndex = nMinIndex;
5867 
5868     int nLayoutFlags = 0;
5869     if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL )
5870         nLayoutFlags |= SAL_LAYOUT_BIDI_RTL;
5871     if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG )
5872         nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
5873     else if( 0 == (mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) )
5874     {
5875         // disable Bidi if no RTL hint and no RTL codes used
5876         const xub_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
5877         const xub_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
5878         for( ; pStr < pEnd; ++pStr )
5879             if( ((*pStr >= 0x0580) && (*pStr < 0x0800))   // middle eastern scripts
5880             ||  ((*pStr >= 0xFB18) && (*pStr < 0xFE00))   // hebrew + arabic A presentation forms
5881             ||  ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B
5882                 break;
5883         if( pStr >= pEnd )
5884             nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
5885     }
5886 
5887     if( mbKerning )
5888         nLayoutFlags |= SAL_LAYOUT_KERNING_PAIRS;
5889     if( maFont.GetKerning() & KERNING_ASIAN )
5890         nLayoutFlags |= SAL_LAYOUT_KERNING_ASIAN;
5891     if( maFont.IsVertical() )
5892         nLayoutFlags |= SAL_LAYOUT_VERTICAL;
5893 
5894     if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES )
5895         nLayoutFlags |= SAL_LAYOUT_ENABLE_LIGATURES;
5896     else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED )
5897         nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
5898     else
5899     {
5900         // disable CTL for non-CTL text
5901         const sal_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
5902         const sal_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
5903         for( ; pStr < pEnd; ++pStr )
5904             if( ((*pStr >= 0x0300) && (*pStr < 0x0370))   // diacritical marks
5905             ||  ((*pStr >= 0x0590) && (*pStr < 0x10A0))   // many CTL scripts
5906             ||  ((*pStr >= 0x1100) && (*pStr < 0x1200))   // hangul jamo
5907             ||  ((*pStr >= 0x1700) && (*pStr < 0x1900))   // many CTL scripts
5908             ||  ((*pStr >= 0xFB1D) && (*pStr < 0xFE00))   // middle east presentation
5909             ||  ((*pStr >= 0xFE70) && (*pStr < 0xFEFF))   // arabic presentation B
5910             ||  ((*pStr >= 0xFE00) && (*pStr < 0xFE10))   // variation selectors in BMP
5911             ||  ((pStr + 1 < pEnd) && (pStr[0] == 0xDB40) && (0xDD00 <= pStr[1]) && (pStr[1] < 0xDEF0)) // variation selector supplement
5912 			)
5913                 break;
5914         if( pStr >= pEnd )
5915             nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
5916     }
5917 
5918     if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS)
5919     {
5920         // disable character localization when no digits used
5921         const sal_Unicode* pBase = rStr.GetBuffer();
5922         const sal_Unicode* pStr = pBase + nMinIndex;
5923         const sal_Unicode* pEnd = pBase + nEndIndex;
5924         for( ; pStr < pEnd; ++pStr )
5925         {
5926             // TODO: are there non-digit localizations?
5927             if( (*pStr >= '0') && (*pStr <= '9') )
5928             {
5929                 // translate characters to local preference
5930                 sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
5931                 if( cChar != *pStr )
5932                     // TODO: are the localized digit surrogates?
5933                     rStr.SetChar( static_cast<sal_uInt16>(pStr - pBase),
5934                                  static_cast<sal_Unicode>(cChar) );
5935             }
5936         }
5937     }
5938 
5939     // right align for RTL text, DRAWPOS_REVERSED, RTL window style
5940     bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0);
5941     if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT )
5942         bRightAlign = false;
5943     else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT )
5944         bRightAlign = true;
5945     // SSA: hack for western office, ie text get right aligned
5946     //      for debugging purposes of mirrored UI
5947     //static const char* pEnv = getenv( "SAL_RTL_MIRRORTEXT" );
5948     bool bRTLWindow = IsRTLEnabled();
5949     bRightAlign ^= bRTLWindow;
5950     if( bRightAlign )
5951         nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN;
5952 
5953     // set layout options
5954     ImplLayoutArgs aLayoutArgs( rStr.GetBuffer(), rStr.Len(), nMinIndex, nEndIndex, nLayoutFlags );
5955 
5956     int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0;
5957     aLayoutArgs.SetOrientation( nOrientation );
5958 
5959     aLayoutArgs.SetLayoutWidth( nPixelWidth );
5960     aLayoutArgs.SetDXArray( pDXArray );
5961 
5962     return aLayoutArgs;
5963 }
5964 
5965 // -----------------------------------------------------------------------
5966 
5967 SalLayout* OutputDevice::ImplLayout( const String& rOrigStr,
5968                                      xub_StrLen nMinIndex,
5969                                      xub_StrLen nLen,
5970                                      const Point& rLogicalPos,
5971                                      long nLogicalWidth,
5972                                      const sal_Int32* pDXArray,
5973                                      bool bFilter ) const
5974 {
5975     // we need a graphics
5976     if( !mpGraphics )
5977         if( !ImplGetGraphics() )
5978             return NULL;
5979 
5980     // initialize font if needed
5981     if( mbNewFont )
5982         if( !ImplNewFont() )
5983             return NULL;
5984     if( mbInitFont )
5985         ImplInitFont();
5986 
5987     // check string index and length
5988     if( (unsigned)nMinIndex + nLen > rOrigStr.Len() )
5989     {
5990         const int nNewLen = (int)rOrigStr.Len() - nMinIndex;
5991         if( nNewLen <= 0 )
5992             return NULL;
5993         nLen = static_cast<xub_StrLen>(nNewLen);
5994     }
5995 
5996     String aStr = rOrigStr;
5997 
5998     // filter out special markers
5999     if( bFilter )
6000     {
6001         xub_StrLen nCutStart, nCutStop, nOrgLen = nLen;
6002         bool bFiltered = mpGraphics->filterText( rOrigStr, aStr, nMinIndex, nLen, nCutStart, nCutStop );
6003         if( !nLen )
6004             return NULL;
6005 
6006         if( bFiltered && nCutStop != nCutStart && pDXArray )
6007         {
6008             if( !nLen )
6009                 pDXArray = NULL;
6010             else
6011             {
6012                 sal_Int32* pAry = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen);
6013                 if( nCutStart > nMinIndex )
6014                     memcpy( pAry, pDXArray, sizeof(sal_Int32)*(nCutStart-nMinIndex) );
6015                 // note: nCutStart will never be smaller than nMinIndex
6016                 memcpy( pAry+nCutStart-nMinIndex,
6017                         pDXArray + nOrgLen - (nCutStop-nMinIndex),
6018                         sizeof(sal_Int32)*(nLen - (nCutStart-nMinIndex)) );
6019                 pDXArray = pAry;
6020             }
6021         }
6022     }
6023 
6024     // convert from logical units to physical units
6025     // recode string if needed
6026     if( mpFontEntry->mpConversion )
6027         mpFontEntry->mpConversion->RecodeString( aStr, 0, aStr.Len() );
6028 
6029     long nPixelWidth = nLogicalWidth;
6030     if( nLogicalWidth && mbMap )
6031         nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth );
6032     if( pDXArray && mbMap )
6033     {
6034         // convert from logical units to font units using a temporary array
6035         sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );
6036         // using base position for better rounding a.k.a. "dancing characters"
6037         int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() );
6038         for( int i = 0; i < nLen; ++i )
6039             pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs;
6040 
6041         pDXArray = pTempDXAry;
6042     }
6043 
6044     ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray );
6045 
6046 #ifdef MACOSX
6047     // CoreText layouts are immutable and already contain the text color
6048     // so we need to provide the color already for the layout request
6049     // even if this layout will never be drawn
6050     if( mbInitTextColor )
6051         const_cast<OutputDevice&>(*this).ImplInitTextColor();
6052 #endif
6053 
6054     // get matching layout object for base font
6055     SalLayout* pSalLayout = NULL;
6056     if( mpPDFWriter )
6057         pSalLayout = mpPDFWriter->GetTextLayout( aLayoutArgs, &mpFontEntry->maFontSelData );
6058 
6059     if( !pSalLayout )
6060         pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );
6061 
6062     // layout text
6063     if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) )
6064     {
6065         pSalLayout->Release();
6066         pSalLayout = NULL;
6067     }
6068 
6069     if( !pSalLayout )
6070         return NULL;
6071 
6072     // do glyph fallback if needed
6073     // #105768# avoid fallback for very small font sizes
6074     if( aLayoutArgs.NeedFallback() )
6075         if( mpFontEntry && (mpFontEntry->maFontSelData.mnHeight >= 3) )
6076             pSalLayout = ImplGlyphFallbackLayout( pSalLayout, aLayoutArgs );
6077 
6078     // position, justify, etc. the layout
6079     pSalLayout->AdjustLayout( aLayoutArgs );
6080     pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos );
6081     // adjust to right alignment if necessary
6082     if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN )
6083     {
6084         long nRTLOffset;
6085         if( pDXArray )
6086             nRTLOffset = pDXArray[ nLen - 1 ];
6087         else if( nPixelWidth )
6088             nRTLOffset = nPixelWidth;
6089         else
6090             nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
6091         pSalLayout->DrawOffset().X() = 1 - nRTLOffset;
6092     }
6093 
6094     return pSalLayout;
6095 }
6096 
6097 // -----------------------------------------------------------------------
6098 
6099 SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const
6100 {
6101     // prepare multi level glyph fallback
6102     MultiSalLayout* pMultiSalLayout = NULL;
6103     ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
6104     rLayoutArgs.PrepareFallback();
6105     rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
6106 
6107 #if defined(HDU_DEBUG)
6108     {
6109         int nCharPos = -1;
6110         bool bRTL = false;
6111         fprintf(stderr,"OD:ImplLayout Glyph Fallback for");
6112         for( int i=0; i<8 && rLayoutArgs.GetNextPos( &nCharPos, &bRTL); ++i )
6113             fprintf(stderr," U+%04X", rLayoutArgs.mpStr[ nCharPos ] );
6114         fprintf(stderr,"\n");
6115         rLayoutArgs.ResetPos();
6116     }
6117 #endif
6118     // get list of unicodes that need glyph fallback
6119     int nCharPos = -1;
6120     bool bRTL = false;
6121     rtl::OUStringBuffer aMissingCodeBuf;
6122     while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) )
6123         aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] );
6124     rLayoutArgs.ResetPos();
6125     rtl::OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();
6126 
6127     ImplFontSelectData aFontSelData = mpFontEntry->maFontSelData;
6128 
6129     ImplFontMetricData aOrigMetric( aFontSelData );
6130     // TODO: use cached metric in fontentry
6131     mpGraphics->GetFontMetric( &aOrigMetric );
6132 
6133     // when device specific font substitution may have been performed for
6134     // the originally selected font then make sure that a fallback to that
6135     // font is performed first
6136     int nDevSpecificFallback = 0;
6137     if( mpOutDevData && !mpOutDevData->maDevFontSubst.Empty() )
6138         nDevSpecificFallback = 1;
6139 
6140     // try if fallback fonts support the missing unicodes
6141     for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
6142     {
6143         // find a font family suited for glyph fallback
6144 #ifndef FONTFALLBACK_HOOKS_DISABLED
6145         // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry
6146         // if the system-specific glyph fallback is active
6147         aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level
6148 #endif
6149         ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontList,
6150             aFontSelData, nFallbackLevel-nDevSpecificFallback, aMissingCodes );
6151         if( !pFallbackFont )
6152             break;
6153 
6154         aFontSelData.mpFontEntry = pFallbackFont;
6155         aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData;
6156         if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1)
6157         {
6158             // ignore fallback font if it is the same as the original font
6159             if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData )
6160             {
6161                 mpFontCache->Release( pFallbackFont );
6162                 continue;
6163             }
6164         }
6165 
6166 #if defined(HDU_DEBUG)
6167         {
6168             ByteString aOrigFontName( maFont.GetName(), RTL_TEXTENCODING_UTF8);
6169             ByteString aFallbackName( aFontSelData.mpFontData->GetFamilyName(),
6170                 RTL_TEXTENCODING_UTF8);
6171             fprintf(stderr,"\tGlyphFallback[lvl=%d] \"%s\" -> \"%s\" (q=%d)\n",
6172                 nFallbackLevel, aOrigFontName.GetBuffer(), aFallbackName.GetBuffer(),
6173                 aFontSelData.mpFontData->GetQuality());
6174         }
6175 #endif
6176 
6177         // TODO: try to get the metric data from the GFB's mpFontEntry
6178         ImplFontMetricData aSubstituteMetric( aFontSelData );
6179         pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel );
6180         mpGraphics->GetFontMetric( &aSubstituteMetric, nFallbackLevel );
6181 
6182         const long nOriginalHeight = aOrigMetric.mnAscent + aOrigMetric.mnDescent;
6183         const long nSubstituteHeight = aSubstituteMetric.mnAscent + aSubstituteMetric.mnDescent;
6184         // Too tall, shrink it a bit. Need a better calculation to include extra
6185         // factors and any extra wriggle room we might have available?
6186 	// TODO: should we scale by max-ascent/max-descent instead of design height?
6187         if( nSubstituteHeight > nOriginalHeight )
6188         {
6189             const float fScale = nOriginalHeight / (float)nSubstituteHeight;
6190             const float fOrigHeight = aFontSelData.mfExactHeight;
6191             const int nOrigHeight = aFontSelData.mnHeight;
6192             aFontSelData.mfExactHeight *= fScale;
6193             aFontSelData.mnHeight = static_cast<int>(aFontSelData.mfExactHeight);
6194             pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel );
6195             aFontSelData.mnHeight = nOrigHeight;
6196             aFontSelData.mfExactHeight = fOrigHeight;
6197         }
6198 
6199         // create and add glyph fallback layout to multilayout
6200         rLayoutArgs.ResetPos();
6201         SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel );
6202         if( pFallback )
6203         {
6204             if( pFallback->LayoutText( rLayoutArgs ) )
6205             {
6206                 if( !pMultiSalLayout )
6207                     pMultiSalLayout = new MultiSalLayout( *pSalLayout );
6208                 pMultiSalLayout->AddFallback( *pFallback,
6209                     rLayoutArgs.maRuns, aFontSelData.mpFontData );
6210                 if (nFallbackLevel == MAX_FALLBACK-1)
6211                     pMultiSalLayout->SetInComplete();
6212             }
6213             else
6214             {
6215                 // there is no need for a font that couldn't resolve anything
6216                 pFallback->Release();
6217             }
6218         }
6219 
6220         mpFontCache->Release( pFallbackFont );
6221 
6222         // break when this fallback was sufficient
6223         if( !rLayoutArgs.PrepareFallback() )
6224             break;
6225     }
6226 
6227     if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) )
6228         pSalLayout = pMultiSalLayout;
6229 
6230     // restore orig font settings
6231     pSalLayout->InitFont();
6232     rLayoutArgs.maRuns = aLayoutRuns;
6233 
6234     return pSalLayout;
6235 }
6236 
6237 // -----------------------------------------------------------------------
6238 
6239 sal_Bool OutputDevice::GetTextIsRTL(
6240             const String& rString,
6241             xub_StrLen nIndex, xub_StrLen nLen ) const
6242 {
6243     String aStr( rString );
6244     ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
6245     bool bRTL = false;
6246     int nCharPos = -1;
6247     aArgs.GetNextPos( &nCharPos, &bRTL );
6248     return (nCharPos != nIndex) ? sal_True : sal_False;
6249 }
6250 
6251 // -----------------------------------------------------------------------
6252 
6253 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth,
6254                                        xub_StrLen nIndex, xub_StrLen nLen,
6255                                        long nCharExtra, sal_Bool /*TODO: bCellBreaking*/ ) const
6256 {
6257     DBG_TRACE( "OutputDevice::GetTextBreak()" );
6258     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6259 
6260     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
6261     xub_StrLen nRetVal = STRING_LEN;
6262     if( pSalLayout )
6263     {
6264         // convert logical widths into layout units
6265         // NOTE: be very careful to avoid rounding errors for nCharExtra case
6266         // problem with rounding errors especially for small nCharExtras
6267         // TODO: remove when layout units have subpixel granularity
6268         long nWidthFactor = pSalLayout->GetUnitsPerPixel();
6269         long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
6270         nTextWidth *= nWidthFactor * nSubPixelFactor;
6271         long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
6272         long nExtraPixelWidth = 0;
6273         if( nCharExtra != 0 )
6274         {
6275             nCharExtra *= nWidthFactor * nSubPixelFactor;
6276             nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
6277         }
6278         nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6279 
6280         pSalLayout->Release();
6281     }
6282 
6283     return nRetVal;
6284 }
6285 
6286 // -----------------------------------------------------------------------
6287 
6288 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth,
6289                                        sal_Unicode nHyphenatorChar, xub_StrLen& rHyphenatorPos,
6290                                        xub_StrLen nIndex, xub_StrLen nLen,
6291                                        long nCharExtra ) const
6292 {
6293     DBG_TRACE( "OutputDevice::GetTextBreak()" );
6294     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6295 
6296     rHyphenatorPos = STRING_LEN;
6297 
6298     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
6299     if( !pSalLayout )
6300         return STRING_LEN;
6301 
6302     // convert logical widths into layout units
6303     // NOTE: be very careful to avoid rounding errors for nCharExtra case
6304     // problem with rounding errors especially for small nCharExtras
6305     // TODO: remove when layout units have subpixel granularity
6306     long nWidthFactor = pSalLayout->GetUnitsPerPixel();
6307     long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
6308 
6309     nTextWidth *= nWidthFactor * nSubPixelFactor;
6310     long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
6311     long nExtraPixelWidth = 0;
6312     if( nCharExtra != 0 )
6313     {
6314         nCharExtra *= nWidthFactor * nSubPixelFactor;
6315         nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
6316     }
6317 
6318     // calculate un-hyphenated break position
6319     xub_StrLen nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6320 
6321     // calculate hyphenated break position
6322     String aHyphenatorStr( &nHyphenatorChar, 1 );
6323     xub_StrLen nTempLen = 1;
6324     SalLayout* pHyphenatorLayout = ImplLayout( aHyphenatorStr, 0, nTempLen );
6325     if( pHyphenatorLayout )
6326     {
6327         // calculate subpixel width of hyphenation character
6328         long nHyphenatorPixelWidth = pHyphenatorLayout->GetTextWidth() * nSubPixelFactor;
6329         pHyphenatorLayout->Release();
6330 
6331         // calculate hyphenated break position
6332         nTextPixelWidth -= nHyphenatorPixelWidth;
6333         if( nExtraPixelWidth > 0 )
6334             nTextPixelWidth -= nExtraPixelWidth;
6335 
6336         rHyphenatorPos = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6337 
6338         if( rHyphenatorPos > nRetVal )
6339             rHyphenatorPos = nRetVal;
6340     }
6341 
6342     pSalLayout->Release();
6343     return nRetVal;
6344 }
6345 
6346 // -----------------------------------------------------------------------
6347 
6348 void OutputDevice::ImplDrawText( OutputDevice& rTargetDevice, const Rectangle& rRect,
6349                                  const String& rOrigStr, sal_uInt16 nStyle,
6350                                  MetricVector* pVector, String* pDisplayText,
6351                                  ::vcl::ITextLayout& _rLayout )
6352 {
6353     Color aOldTextColor;
6354     Color aOldTextFillColor;
6355     sal_Bool  bRestoreFillColor = false;
6356     if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector )
6357     {
6358         sal_Bool  bHighContrastBlack = sal_False;
6359         sal_Bool  bHighContrastWhite = sal_False;
6360         const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() );
6361         if( rStyleSettings.GetHighContrastMode() )
6362         {
6363             Color aCol;
6364             if( rTargetDevice.IsBackground() )
6365                 aCol = rTargetDevice.GetBackground().GetColor();
6366             else
6367                 // best guess is the face color here
6368                 // but it may be totally wrong. the background color
6369                 // was typically already reset
6370                 aCol = rStyleSettings.GetFaceColor();
6371 
6372             bHighContrastBlack = aCol.IsDark();
6373             bHighContrastWhite = aCol.IsBright();
6374         }
6375 
6376         aOldTextColor = rTargetDevice.GetTextColor();
6377         if ( rTargetDevice.IsTextFillColor() )
6378         {
6379             bRestoreFillColor = sal_True;
6380             aOldTextFillColor = rTargetDevice.GetTextFillColor();
6381         }
6382         if( bHighContrastBlack )
6383             rTargetDevice.SetTextColor( COL_GREEN );
6384         else if( bHighContrastWhite )
6385             rTargetDevice.SetTextColor( COL_LIGHTGREEN );
6386         else
6387         {
6388             // draw disabled text always without shadow
6389             // as it fits better with native look
6390             /*
6391             SetTextColor( GetSettings().GetStyleSettings().GetLightColor() );
6392             Rectangle aRect = rRect;
6393             aRect.Move( 1, 1 );
6394             DrawText( aRect, rOrigStr, nStyle & ~TEXT_DRAW_DISABLE );
6395             */
6396             rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() );
6397         }
6398     }
6399 
6400     long        nWidth          = rRect.GetWidth();
6401     long        nHeight         = rRect.GetHeight();
6402 
6403     if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) )
6404         return;
6405 
6406     Point       aPos            = rRect.TopLeft();
6407 
6408     long        nTextHeight     = rTargetDevice.GetTextHeight();
6409     TextAlign   eAlign          = rTargetDevice.GetTextAlign();
6410     xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;
6411 
6412     String aStr = rOrigStr;
6413     if ( nStyle & TEXT_DRAW_MNEMONIC )
6414         aStr = GetNonMnemonicString( aStr, nMnemonicPos );
6415 
6416     const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector;
6417 
6418     // Mehrzeiligen Text behandeln wir anders
6419     if ( nStyle & TEXT_DRAW_MULTILINE )
6420     {
6421 
6422         XubString               aLastLine;
6423         ImplMultiTextLineInfo   aMultiLineInfo;
6424         ImplTextLineInfo*       pLineInfo;
6425         long                    nMaxTextWidth;
6426         xub_StrLen              i;
6427         xub_StrLen              nLines;
6428         xub_StrLen              nFormatLines;
6429 
6430         if ( nTextHeight )
6431         {
6432             nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _rLayout );
6433             nLines = (xub_StrLen)(nHeight/nTextHeight);
6434             nFormatLines = aMultiLineInfo.Count();
6435             if ( !nLines )
6436                 nLines = 1;
6437             if ( nFormatLines > nLines )
6438             {
6439                 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
6440                 {
6441                     // Letzte Zeile zusammenbauen und kuerzen
6442                     nFormatLines = nLines-1;
6443 
6444                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
6445                     aLastLine = aStr.Copy( pLineInfo->GetIndex() );
6446                     aLastLine.ConvertLineEnd( LINEEND_LF );
6447                     // Alle LineFeed's durch Spaces ersetzen
6448                     xub_StrLen nLastLineLen = aLastLine.Len();
6449                     for ( i = 0; i < nLastLineLen; i++ )
6450                     {
6451                         if ( aLastLine.GetChar( i ) == _LF )
6452                             aLastLine.SetChar( i, ' ' );
6453                     }
6454                     aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout );
6455                     nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
6456                     nStyle |= TEXT_DRAW_TOP;
6457                 }
6458             }
6459             else
6460             {
6461                 if ( nMaxTextWidth <= nWidth )
6462                     nStyle &= ~TEXT_DRAW_CLIP;
6463             }
6464 
6465             // Muss in der Hoehe geclippt werden?
6466             if ( nFormatLines*nTextHeight > nHeight )
6467                 nStyle |= TEXT_DRAW_CLIP;
6468 
6469             // Clipping setzen
6470             if ( nStyle & TEXT_DRAW_CLIP )
6471             {
6472                 rTargetDevice.Push( PUSH_CLIPREGION );
6473                 rTargetDevice.IntersectClipRegion( rRect );
6474             }
6475 
6476             // Vertikales Alignment
6477             if ( nStyle & TEXT_DRAW_BOTTOM )
6478                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
6479             else if ( nStyle & TEXT_DRAW_VCENTER )
6480                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
6481 
6482             // Font Alignment
6483             if ( eAlign == ALIGN_BOTTOM )
6484                 aPos.Y() += nTextHeight;
6485             else if ( eAlign == ALIGN_BASELINE )
6486                 aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
6487 
6488             // Alle Zeilen ausgeben, bis auf die letzte
6489             for ( i = 0; i < nFormatLines; i++ )
6490             {
6491                 pLineInfo = aMultiLineInfo.GetLine( i );
6492                 if ( nStyle & TEXT_DRAW_RIGHT )
6493                     aPos.X() += nWidth-pLineInfo->GetWidth();
6494                 else if ( nStyle & TEXT_DRAW_CENTER )
6495                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
6496                 xub_StrLen nIndex   = pLineInfo->GetIndex();
6497                 xub_StrLen nLineLen = pLineInfo->GetLen();
6498                 _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
6499                 if ( bDrawMnemonics )
6500                 {
6501                     if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
6502                     {
6503                         long        nMnemonicX;
6504                         long        nMnemonicY;
6505                         long        nMnemonicWidth;
6506 
6507                         sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen );
6508                         /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray,
6509                                                 nIndex, nLineLen );
6510                         long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
6511                         long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
6512                         nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) );
6513 
6514                         Point       aTempPos = rTargetDevice.LogicToPixel( aPos );
6515                         nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min( lc_x1, lc_x2 ) );
6516                         nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
6517                         rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
6518                     }
6519                 }
6520                 aPos.Y() += nTextHeight;
6521                 aPos.X() = rRect.Left();
6522             }
6523 
6524 
6525             // Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben,
6526             // da die Zeile gekuerzt wurde
6527             if ( aLastLine.Len() )
6528                 _rLayout.DrawText( aPos, aLastLine, 0, STRING_LEN, pVector, pDisplayText );
6529 
6530             // Clipping zuruecksetzen
6531             if ( nStyle & TEXT_DRAW_CLIP )
6532                 rTargetDevice.Pop();
6533         }
6534     }
6535     else
6536     {
6537         long nTextWidth = _rLayout.GetTextWidth( aStr, 0, STRING_LEN );
6538 
6539         // Evt. Text kuerzen
6540         if ( nTextWidth > nWidth )
6541         {
6542             if ( nStyle & TEXT_DRAW_ELLIPSIS )
6543             {
6544                 aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout );
6545                 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
6546                 nStyle |= TEXT_DRAW_LEFT;
6547                 nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.Len() );
6548             }
6549         }
6550         else
6551         {
6552             if ( nTextHeight <= nHeight )
6553                 nStyle &= ~TEXT_DRAW_CLIP;
6554         }
6555 
6556         // horizontal text alignment
6557         if ( nStyle & TEXT_DRAW_RIGHT )
6558             aPos.X() += nWidth-nTextWidth;
6559         else if ( nStyle & TEXT_DRAW_CENTER )
6560             aPos.X() += (nWidth-nTextWidth)/2;
6561 
6562         // vertical font alignment
6563         if ( eAlign == ALIGN_BOTTOM )
6564             aPos.Y() += nTextHeight;
6565         else if ( eAlign == ALIGN_BASELINE )
6566             aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
6567 
6568         if ( nStyle & TEXT_DRAW_BOTTOM )
6569             aPos.Y() += nHeight-nTextHeight;
6570         else if ( nStyle & TEXT_DRAW_VCENTER )
6571             aPos.Y() += (nHeight-nTextHeight)/2;
6572 
6573         long        nMnemonicX = 0;
6574         long        nMnemonicY = 0;
6575         long        nMnemonicWidth = 0;
6576         if ( nMnemonicPos != STRING_NOTFOUND )
6577         {
6578             sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.Len() );
6579             /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, 0, aStr.Len() );
6580             long lc_x1 = pCaretXArray[2*(nMnemonicPos)];
6581             long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1];
6582             nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) );
6583 
6584             Point aTempPos = rTargetDevice.LogicToPixel( aPos );
6585             nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min(lc_x1, lc_x2) );
6586             nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
6587         }
6588 
6589         if ( nStyle & TEXT_DRAW_CLIP )
6590         {
6591             rTargetDevice.Push( PUSH_CLIPREGION );
6592             rTargetDevice.IntersectClipRegion( rRect );
6593             _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
6594             if ( bDrawMnemonics )
6595             {
6596                 if ( nMnemonicPos != STRING_NOTFOUND )
6597                     rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
6598             }
6599             rTargetDevice.Pop();
6600         }
6601         else
6602         {
6603             _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
6604             if ( bDrawMnemonics )
6605             {
6606                 if ( nMnemonicPos != STRING_NOTFOUND )
6607                     rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
6608             }
6609         }
6610     }
6611 
6612     if ( nStyle & TEXT_DRAW_DISABLE && !pVector )
6613     {
6614         rTargetDevice.SetTextColor( aOldTextColor );
6615         if ( bRestoreFillColor )
6616             rTargetDevice.SetTextFillColor( aOldTextFillColor );
6617     }
6618 }
6619 
6620 // -----------------------------------------------------------------------
6621 
6622 void OutputDevice::AddTextRectActions( const Rectangle& rRect,
6623                                        const String&    rOrigStr,
6624                                        sal_uInt16           nStyle,
6625                                        GDIMetaFile&     rMtf )
6626 {
6627     DBG_TRACE( "OutputDevice::AddTextRectActions( const Rectangle& )" );
6628     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6629 
6630     if ( !rOrigStr.Len() || rRect.IsEmpty() )
6631         return;
6632 
6633     // we need a graphics
6634     if( !mpGraphics && !ImplGetGraphics() )
6635         return;
6636     if( mbInitClipRegion )
6637         ImplInitClipRegion();
6638 
6639     // temporarily swap in passed mtf for action generation, and
6640     // disable output generation.
6641     const sal_Bool bOutputEnabled( IsOutputEnabled() );
6642     GDIMetaFile* pMtf = mpMetaFile;
6643 
6644     mpMetaFile = &rMtf;
6645     EnableOutput( sal_False );
6646 
6647     // #i47157# Factored out to ImplDrawTextRect(), to be shared
6648     // between us and DrawText()
6649     DefaultTextLayout aLayout( *this );
6650     ImplDrawText( *this, rRect, rOrigStr, nStyle, NULL, NULL, aLayout );
6651 
6652     // and restore again
6653     EnableOutput( bOutputEnabled );
6654     mpMetaFile = pMtf;
6655 }
6656 
6657 // -----------------------------------------------------------------------
6658 
6659 void OutputDevice::DrawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle,
6660                              MetricVector* pVector, String* pDisplayText,
6661                              ::vcl::ITextLayout* _pTextLayout )
6662 {
6663     if( mpOutDevData && mpOutDevData->mpRecordLayout )
6664     {
6665         pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
6666         pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
6667     }
6668 
6669     DBG_TRACE( "OutputDevice::DrawText( const Rectangle& )" );
6670     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6671 
6672     bool bDecomposeTextRectAction = ( _pTextLayout != NULL ) && _pTextLayout->DecomposeTextRectAction();
6673     if ( mpMetaFile && !bDecomposeTextRectAction )
6674         mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) );
6675 
6676     if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || !rOrigStr.Len() || rRect.IsEmpty() )
6677         return;
6678 
6679     // we need a graphics
6680     if( !mpGraphics && !ImplGetGraphics() )
6681         return;
6682     if( mbInitClipRegion )
6683         ImplInitClipRegion();
6684     if( mbOutputClipped && !bDecomposeTextRectAction )
6685         return;
6686 
6687     // temporarily disable mtf action generation (ImplDrawText _does_
6688     // create META_TEXT_ACTIONs otherwise)
6689     GDIMetaFile* pMtf = mpMetaFile;
6690     if ( !bDecomposeTextRectAction )
6691         mpMetaFile = NULL;
6692 
6693     // #i47157# Factored out to ImplDrawText(), to be used also
6694     // from AddTextRectActions()
6695     DefaultTextLayout aDefaultLayout( *this );
6696     ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout );
6697 
6698     // and enable again
6699     mpMetaFile = pMtf;
6700 
6701     if( mpAlphaVDev )
6702         mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
6703 }
6704 
6705 // -----------------------------------------------------------------------
6706 
6707 Rectangle OutputDevice::GetTextRect( const Rectangle& rRect,
6708                                      const XubString& rStr, sal_uInt16 nStyle,
6709                                      TextRectInfo* pInfo,
6710                                      const ::vcl::ITextLayout* _pTextLayout ) const
6711 {
6712     DBG_TRACE( "OutputDevice::GetTextRect()" );
6713     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6714 
6715     Rectangle           aRect = rRect;
6716     xub_StrLen          nLines;
6717     long                nWidth = rRect.GetWidth();
6718     long                nMaxWidth;
6719     long                nTextHeight = GetTextHeight();
6720 
6721     String aStr = rStr;
6722     if ( nStyle & TEXT_DRAW_MNEMONIC )
6723         aStr = GetNonMnemonicString( aStr );
6724 
6725     if ( nStyle & TEXT_DRAW_MULTILINE )
6726     {
6727         ImplMultiTextLineInfo   aMultiLineInfo;
6728         ImplTextLineInfo*       pLineInfo;
6729         xub_StrLen              nFormatLines;
6730         xub_StrLen              i;
6731 
6732         nMaxWidth = 0;
6733         DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) );
6734         ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout );
6735         nFormatLines = aMultiLineInfo.Count();
6736         if ( !nTextHeight )
6737             nTextHeight = 1;
6738         nLines = (sal_uInt16)(aRect.GetHeight()/nTextHeight);
6739         if ( pInfo )
6740             pInfo->mnLineCount = nFormatLines;
6741         if ( !nLines )
6742             nLines = 1;
6743         if ( nFormatLines <= nLines )
6744             nLines = nFormatLines;
6745         else
6746         {
6747             if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) )
6748                 nLines = nFormatLines;
6749             else
6750             {
6751                 if ( pInfo )
6752                     pInfo->mbEllipsis = sal_True;
6753                 nMaxWidth = nWidth;
6754             }
6755         }
6756         if ( pInfo )
6757         {
6758             sal_Bool bMaxWidth = nMaxWidth == 0;
6759             pInfo->mnMaxWidth = 0;
6760             for ( i = 0; i < nLines; i++ )
6761             {
6762                 pLineInfo = aMultiLineInfo.GetLine( i );
6763                 if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) )
6764                     nMaxWidth = pLineInfo->GetWidth();
6765                 if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth )
6766                     pInfo->mnMaxWidth = pLineInfo->GetWidth();
6767             }
6768         }
6769         else if ( !nMaxWidth )
6770         {
6771             for ( i = 0; i < nLines; i++ )
6772             {
6773                 pLineInfo = aMultiLineInfo.GetLine( i );
6774                 if ( pLineInfo->GetWidth() > nMaxWidth )
6775                     nMaxWidth = pLineInfo->GetWidth();
6776             }
6777         }
6778     }
6779     else
6780     {
6781         nLines      = 1;
6782         nMaxWidth   = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.Len() ) : GetTextWidth( aStr );
6783 
6784         if ( pInfo )
6785         {
6786             pInfo->mnLineCount  = 1;
6787             pInfo->mnMaxWidth   = nMaxWidth;
6788         }
6789 
6790         if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) )
6791         {
6792             if ( pInfo )
6793                 pInfo->mbEllipsis = sal_True;
6794             nMaxWidth = nWidth;
6795         }
6796     }
6797 
6798     if ( nStyle & TEXT_DRAW_RIGHT )
6799         aRect.Left() = aRect.Right()-nMaxWidth+1;
6800     else if ( nStyle & TEXT_DRAW_CENTER )
6801     {
6802         aRect.Left() += (nWidth-nMaxWidth)/2;
6803         aRect.Right() = aRect.Left()+nMaxWidth-1;
6804     }
6805     else
6806         aRect.Right() = aRect.Left()+nMaxWidth-1;
6807 
6808     if ( nStyle & TEXT_DRAW_BOTTOM )
6809         aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1;
6810     else if ( nStyle & TEXT_DRAW_VCENTER )
6811     {
6812         aRect.Top()   += (aRect.GetHeight()-(nTextHeight*nLines))/2;
6813         aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
6814     }
6815     else
6816         aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
6817 
6818     aRect.Right()++; // #99188# get rid of rounding problems when using this rect later
6819     return aRect;
6820 }
6821 
6822 // -----------------------------------------------------------------------
6823 
6824 static sal_Bool ImplIsCharIn( xub_Unicode c, const sal_Char* pStr )
6825 {
6826     while ( *pStr )
6827     {
6828         if ( *pStr == c )
6829             return sal_True;
6830         pStr++;
6831     }
6832 
6833     return sal_False;
6834 }
6835 
6836 // -----------------------------------------------------------------------
6837 
6838 String OutputDevice::GetEllipsisString( const String& rOrigStr, long nMaxWidth,
6839                                         sal_uInt16 nStyle ) const
6840 {
6841     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6842     DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) );
6843     return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout );
6844 }
6845 
6846 // -----------------------------------------------------------------------
6847 
6848 String OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const XubString& rOrigStr, long nMaxWidth,
6849                                                sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout )
6850 {
6851     DBG_TRACE( "OutputDevice::ImplGetEllipsisString()" );
6852 
6853     String aStr = rOrigStr;
6854     xub_StrLen nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.Len() );
6855 
6856 
6857     if ( nIndex != STRING_LEN )
6858     {
6859         if( (nStyle & TEXT_DRAW_CENTERELLIPSIS) == TEXT_DRAW_CENTERELLIPSIS )
6860         {
6861             String aTmpStr( aStr );
6862             xub_StrLen nEraseChars = 4;
6863             while( nEraseChars < aStr.Len() && _rLayout.GetTextWidth( aTmpStr, 0, aTmpStr.Len() ) > nMaxWidth )
6864             {
6865                 aTmpStr = aStr;
6866                 xub_StrLen i = (aTmpStr.Len() - nEraseChars)/2;
6867                 aTmpStr.Erase( i, nEraseChars++ );
6868                 aTmpStr.InsertAscii( "...", i );
6869             }
6870             aStr = aTmpStr;
6871         }
6872         else if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
6873         {
6874             aStr.Erase( nIndex );
6875             if ( nIndex > 1 )
6876             {
6877                 aStr.AppendAscii( "..." );
6878                 while ( aStr.Len() && (_rLayout.GetTextWidth( aStr, 0, aStr.Len() ) > nMaxWidth) )
6879                 {
6880                     if ( (nIndex > 1) || (nIndex == aStr.Len()) )
6881                         nIndex--;
6882                     aStr.Erase( nIndex, 1 );
6883                 }
6884             }
6885 
6886             if ( !aStr.Len() && (nStyle & TEXT_DRAW_CLIP) )
6887                 aStr += rOrigStr.GetChar( 0 );
6888         }
6889         else if ( nStyle & TEXT_DRAW_PATHELLIPSIS )
6890         {
6891             rtl::OUString aPath( rOrigStr );
6892             rtl::OUString aAbbreviatedPath;
6893             osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, NULL );
6894             aStr = aAbbreviatedPath;
6895         }
6896         else if ( nStyle & TEXT_DRAW_NEWSELLIPSIS )
6897         {
6898             static sal_Char const   pSepChars[] = ".";
6899             // Letztes Teilstueck ermitteln
6900             xub_StrLen nLastContent = aStr.Len();
6901             while ( nLastContent )
6902             {
6903                 nLastContent--;
6904                 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) )
6905                     break;
6906             }
6907             while ( nLastContent &&
6908                     ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) )
6909                 nLastContent--;
6910 
6911             XubString aLastStr( aStr, nLastContent, aStr.Len() );
6912             XubString aTempLastStr1( RTL_CONSTASCII_USTRINGPARAM( "..." ) );
6913             aTempLastStr1 += aLastStr;
6914             if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.Len() ) > nMaxWidth )
6915                 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
6916             else
6917             {
6918                 sal_uInt16 nFirstContent = 0;
6919                 while ( nFirstContent < nLastContent )
6920                 {
6921                     nFirstContent++;
6922                     if ( ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) )
6923                         break;
6924                 }
6925                 while ( (nFirstContent < nLastContent) &&
6926                         ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) )
6927                     nFirstContent++;
6928 
6929                 if ( nFirstContent >= nLastContent )
6930                     aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
6931                 else
6932                 {
6933                     if ( nFirstContent > 4 )
6934                         nFirstContent = 4;
6935                     XubString aFirstStr( aStr, 0, nFirstContent );
6936                     aFirstStr.AppendAscii( "..." );
6937                     XubString aTempStr = aFirstStr;
6938                     aTempStr += aLastStr;
6939                     if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth )
6940                         aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
6941                     else
6942                     {
6943                         do
6944                         {
6945                             aStr = aTempStr;
6946                             if( nLastContent > aStr.Len() )
6947                                 nLastContent = aStr.Len();
6948                             while ( nFirstContent < nLastContent )
6949                             {
6950                                 nLastContent--;
6951                                 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) )
6952                                     break;
6953 
6954                             }
6955                             while ( (nFirstContent < nLastContent) &&
6956                                     ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) )
6957                                 nLastContent--;
6958 
6959                             if ( nFirstContent < nLastContent )
6960                             {
6961                                 XubString aTempLastStr( aStr, nLastContent, aStr.Len() );
6962                                 aTempStr = aFirstStr;
6963                                 aTempStr += aTempLastStr;
6964                                 if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth )
6965                                     break;
6966                             }
6967                         }
6968                         while ( nFirstContent < nLastContent );
6969                     }
6970                 }
6971             }
6972         }
6973     }
6974 
6975     return aStr;
6976 }
6977 
6978 // -----------------------------------------------------------------------
6979 
6980 void OutputDevice::DrawCtrlText( const Point& rPos, const XubString& rStr,
6981                                  xub_StrLen nIndex, xub_StrLen nLen,
6982                                  sal_uInt16 nStyle, MetricVector* pVector, String* pDisplayText )
6983 {
6984     DBG_TRACE( "OutputDevice::DrawCtrlText()" );
6985     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6986 
6987     if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.Len()) )
6988         return;
6989 
6990     // better get graphics here because ImplDrawMnemonicLine() will not
6991     // we need a graphics
6992     if( !mpGraphics && !ImplGetGraphics() )
6993         return;
6994     if( mbInitClipRegion )
6995         ImplInitClipRegion();
6996     if ( mbOutputClipped )
6997         return;
6998 
6999     if( nIndex >= rStr.Len() )
7000         return;
7001     if( (sal_uLong)nIndex+nLen >= rStr.Len() )
7002         nLen = rStr.Len() - nIndex;
7003 
7004     XubString   aStr = rStr;
7005     xub_StrLen  nMnemonicPos = STRING_NOTFOUND;
7006 
7007     long        nMnemonicX = 0;
7008     long        nMnemonicY = 0;
7009     long        nMnemonicWidth = 0;
7010     if ( (nStyle & TEXT_DRAW_MNEMONIC) && nLen > 1 )
7011     {
7012         aStr = GetNonMnemonicString( aStr, nMnemonicPos );
7013         if ( nMnemonicPos != STRING_NOTFOUND )
7014         {
7015             if( nMnemonicPos < nIndex )
7016                 --nIndex;
7017             else if( nLen < STRING_LEN )
7018             {
7019                 if( nMnemonicPos < (nIndex+nLen) )
7020                     --nLen;
7021                 DBG_ASSERT( nMnemonicPos < (nIndex+nLen), "Mnemonic underline marker after last character" );
7022             }
7023             sal_Bool bInvalidPos = sal_False;
7024 
7025             if( nMnemonicPos >= nLen )
7026             {
7027                 // #106952#
7028                 // may occur in BiDi-Strings: the '~' is sometimes found behind the last char
7029                 // due to some strange BiDi text editors
7030                 // ->place the underline behind the string to indicate a failure
7031                 bInvalidPos = sal_True;
7032                 nMnemonicPos = nLen-1;
7033             }
7034 
7035             sal_Int32* pCaretXArray = (sal_Int32*)alloca( 2 * sizeof(sal_Int32) * nLen );
7036             /*sal_Bool bRet =*/ GetCaretPositions( aStr, pCaretXArray, nIndex, nLen );
7037             long lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ];
7038             long lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ];
7039             nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
7040 
7041             Point aTempPos( Min(lc_x1,lc_x2), GetFontMetric().GetAscent() );
7042             if( bInvalidPos )  // #106952#, place behind the (last) character
7043                 aTempPos = Point( Max(lc_x1,lc_x2), GetFontMetric().GetAscent() );
7044 
7045 			aTempPos += rPos;
7046             aTempPos = LogicToPixel( aTempPos );
7047             nMnemonicX = mnOutOffX + aTempPos.X();
7048             nMnemonicY = mnOutOffY + aTempPos.Y();
7049         }
7050     }
7051 
7052     if ( nStyle & TEXT_DRAW_DISABLE && ! pVector )
7053     {
7054         Color aOldTextColor;
7055         Color aOldTextFillColor;
7056         sal_Bool  bRestoreFillColor;
7057         sal_Bool  bHighContrastBlack = sal_False;
7058         sal_Bool  bHighContrastWhite = sal_False;
7059         const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
7060         if( rStyleSettings.GetHighContrastMode() )
7061         {
7062             if( IsBackground() )
7063             {
7064                 Wallpaper aWall = GetBackground();
7065                 Color aCol = aWall.GetColor();
7066                 bHighContrastBlack = aCol.IsDark();
7067                 bHighContrastWhite = aCol.IsBright();
7068             }
7069         }
7070 
7071         aOldTextColor = GetTextColor();
7072         if ( IsTextFillColor() )
7073         {
7074             bRestoreFillColor = sal_True;
7075             aOldTextFillColor = GetTextFillColor();
7076         }
7077         else
7078             bRestoreFillColor = sal_False;
7079 
7080         if( bHighContrastBlack )
7081             SetTextColor( COL_GREEN );
7082         else if( bHighContrastWhite )
7083             SetTextColor( COL_LIGHTGREEN );
7084         else
7085             SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
7086 
7087         DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
7088         if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7089         {
7090             if ( nMnemonicPos != STRING_NOTFOUND )
7091                 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7092         }
7093         SetTextColor( aOldTextColor );
7094         if ( bRestoreFillColor )
7095             SetTextFillColor( aOldTextFillColor );
7096     }
7097     else
7098     {
7099         DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
7100         if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7101         {
7102             if ( nMnemonicPos != STRING_NOTFOUND )
7103                 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7104         }
7105     }
7106 
7107     if( mpAlphaVDev )
7108         mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText );
7109 }
7110 
7111 // -----------------------------------------------------------------------
7112 
7113 long OutputDevice::GetCtrlTextWidth( const String& rStr,
7114                                      xub_StrLen nIndex, xub_StrLen nLen,
7115                                      sal_uInt16 nStyle ) const
7116 {
7117     DBG_TRACE( "OutputDevice::GetCtrlTextSize()" );
7118     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7119 
7120     if ( nStyle & TEXT_DRAW_MNEMONIC )
7121     {
7122         xub_StrLen  nMnemonicPos;
7123         XubString   aStr = GetNonMnemonicString( rStr, nMnemonicPos );
7124         if ( nMnemonicPos != STRING_NOTFOUND )
7125         {
7126             if ( nMnemonicPos < nIndex )
7127                 nIndex--;
7128             else if ( (nLen < STRING_LEN) &&
7129                       (nMnemonicPos >= nIndex) && (nMnemonicPos < (sal_uLong)(nIndex+nLen)) )
7130                 nLen--;
7131         }
7132         return GetTextWidth( aStr, nIndex, nLen );
7133     }
7134     else
7135         return GetTextWidth( rStr, nIndex, nLen );
7136 }
7137 
7138 // -----------------------------------------------------------------------
7139 
7140 String OutputDevice::GetNonMnemonicString( const String& rStr, xub_StrLen& rMnemonicPos )
7141 {
7142     String   aStr    = rStr;
7143     xub_StrLen  nLen    = aStr.Len();
7144     xub_StrLen  i       = 0;
7145 
7146     rMnemonicPos = STRING_NOTFOUND;
7147     while ( i < nLen )
7148     {
7149         if ( aStr.GetChar( i ) == '~' )
7150         {
7151             if ( aStr.GetChar( i+1 ) != '~' )
7152             {
7153                 if ( rMnemonicPos == STRING_NOTFOUND )
7154                     rMnemonicPos = i;
7155                 aStr.Erase( i, 1 );
7156                 nLen--;
7157             }
7158             else
7159             {
7160                 aStr.Erase( i, 1 );
7161                 nLen--;
7162                 i++;
7163             }
7164         }
7165         else
7166             i++;
7167     }
7168 
7169     return aStr;
7170 }
7171 
7172 // -----------------------------------------------------------------------
7173 
7174 int OutputDevice::GetDevFontCount() const
7175 {
7176     DBG_TRACE( "OutputDevice::GetDevFontCount()" );
7177     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7178 
7179     if( !mpGetDevFontList )
7180         mpGetDevFontList = mpFontList->GetDevFontList();
7181     return mpGetDevFontList->Count();
7182 }
7183 
7184 // -----------------------------------------------------------------------
7185 
7186 FontInfo OutputDevice::GetDevFont( int nDevFontIndex ) const
7187 {
7188     DBG_TRACE( "OutputDevice::GetDevFont()" );
7189     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7190 
7191     FontInfo aFontInfo;
7192 
7193     ImplInitFontList();
7194 
7195     int nCount = GetDevFontCount();
7196     if( nDevFontIndex < nCount )
7197     {
7198         const ImplFontData& rData = *mpGetDevFontList->Get( nDevFontIndex );
7199         aFontInfo.SetName( rData.maName );
7200         aFontInfo.SetStyleName( rData.maStyleName );
7201         aFontInfo.SetCharSet( rData.mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
7202         aFontInfo.SetFamily( rData.meFamily );
7203         aFontInfo.SetPitch( rData.mePitch );
7204         aFontInfo.SetWeight( rData.meWeight );
7205         aFontInfo.SetItalic( rData.meItalic );
7206         aFontInfo.SetWidthType( rData.meWidthType );
7207         if( rData.IsScalable() )
7208             aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
7209         if( rData.mbDevice )
7210             aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
7211     }
7212 
7213     return aFontInfo;
7214 }
7215 
7216 // -----------------------------------------------------------------------
7217 
7218 sal_Bool OutputDevice::AddTempDevFont( const String& rFileURL, const String& rFontName )
7219 {
7220     DBG_TRACE( "OutputDevice::AddTempDevFont()" );
7221     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7222 
7223     ImplInitFontList();
7224 
7225     if( !mpGraphics && !ImplGetGraphics() )
7226         return sal_False;
7227 
7228     bool bRC = mpGraphics->AddTempDevFont( mpFontList, rFileURL, rFontName );
7229     if( !bRC )
7230         return sal_False;
7231 
7232     if( mpAlphaVDev )
7233         mpAlphaVDev->AddTempDevFont( rFileURL, rFontName );
7234 
7235     mpFontCache->Invalidate();
7236     return sal_True;
7237 }
7238 
7239 // -----------------------------------------------------------------------
7240 
7241 int OutputDevice::GetDevFontSizeCount( const Font& rFont ) const
7242 {
7243     DBG_TRACE( "OutputDevice::GetDevFontSizeCount()" );
7244     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7245 
7246     delete mpGetDevSizeList;
7247 
7248     ImplInitFontList();
7249     mpGetDevSizeList = mpFontList->GetDevSizeList( rFont.GetName() );
7250     return mpGetDevSizeList->Count();
7251 }
7252 
7253 // -----------------------------------------------------------------------
7254 
7255 Size OutputDevice::GetDevFontSize( const Font& rFont, int nSizeIndex ) const
7256 {
7257     DBG_TRACE( "OutputDevice::GetDevFontSize()" );
7258     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7259 
7260     // check range
7261     int nCount = GetDevFontSizeCount( rFont );
7262     if ( nSizeIndex >= nCount )
7263         return Size();
7264 
7265     // when mapping is enabled round to .5 points
7266     Size aSize( 0, mpGetDevSizeList->Get( nSizeIndex ) );
7267     if ( mbMap )
7268     {
7269         aSize.Height() *= 10;
7270         MapMode aMap( MAP_10TH_INCH, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) );
7271         aSize = PixelToLogic( aSize, aMap );
7272         aSize.Height() += 5;
7273         aSize.Height() /= 10;
7274         long nRound = aSize.Height() % 5;
7275         if ( nRound >= 3 )
7276             aSize.Height() += (5-nRound);
7277         else
7278             aSize.Height() -= nRound;
7279         aSize.Height() *= 10;
7280         aSize = LogicToPixel( aSize, aMap );
7281         aSize = PixelToLogic( aSize );
7282         aSize.Height() += 5;
7283         aSize.Height() /= 10;
7284     }
7285     return aSize;
7286 }
7287 
7288 // -----------------------------------------------------------------------
7289 
7290 sal_Bool OutputDevice::IsFontAvailable( const String& rFontName ) const
7291 {
7292     DBG_TRACE( "OutputDevice::IsFontAvailable()" );
7293     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7294 
7295     ImplDevFontListData* pFound = mpFontList->FindFontFamily( rFontName );
7296     return (pFound != NULL);
7297 }
7298 
7299 // -----------------------------------------------------------------------
7300 
7301 FontMetric OutputDevice::GetFontMetric() const
7302 {
7303     DBG_TRACE( "OutputDevice::GetFontMetric()" );
7304     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7305 
7306     FontMetric aMetric;
7307     if( mbNewFont && !ImplNewFont() )
7308         return aMetric;
7309 
7310     ImplFontEntry*      pEntry = mpFontEntry;
7311     ImplFontMetricData* pMetric = &(pEntry->maMetric);
7312 
7313     // prepare metric
7314     aMetric.Font::operator=( maFont );
7315 
7316     // set aMetric with info from font
7317     aMetric.SetName( maFont.GetName() );
7318     aMetric.SetStyleName( pMetric->maStyleName );
7319     aMetric.SetSize( PixelToLogic( Size( pMetric->mnWidth, pMetric->mnAscent+pMetric->mnDescent-pMetric->mnIntLeading ) ) );
7320     aMetric.SetCharSet( pMetric->mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
7321     aMetric.SetFamily( pMetric->meFamily );
7322     aMetric.SetPitch( pMetric->mePitch );
7323     aMetric.SetWeight( pMetric->meWeight );
7324     aMetric.SetItalic( pMetric->meItalic );
7325     aMetric.SetWidthType( pMetric->meWidthType );
7326     if ( pEntry->mnOwnOrientation )
7327         aMetric.SetOrientation( pEntry->mnOwnOrientation );
7328     else
7329         aMetric.SetOrientation( pMetric->mnOrientation );
7330     if( !pEntry->maMetric.mbKernableFont )
7331          aMetric.SetKerning( maFont.GetKerning() & ~KERNING_FONTSPECIFIC );
7332 
7333     // set remaining metric fields
7334     aMetric.mpImplMetric->mnMiscFlags   = 0;
7335     if( pMetric->mbDevice )
7336             aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
7337     if( pMetric->mbScalableFont )
7338             aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
7339     aMetric.mpImplMetric->mnAscent      = ImplDevicePixelToLogicHeight( pMetric->mnAscent+mnEmphasisAscent );
7340     aMetric.mpImplMetric->mnDescent     = ImplDevicePixelToLogicHeight( pMetric->mnDescent+mnEmphasisDescent );
7341     aMetric.mpImplMetric->mnIntLeading  = ImplDevicePixelToLogicHeight( pMetric->mnIntLeading+mnEmphasisAscent );
7342     aMetric.mpImplMetric->mnExtLeading  = ImplDevicePixelToLogicHeight( pMetric->mnExtLeading );
7343     aMetric.mpImplMetric->mnLineHeight  = ImplDevicePixelToLogicHeight( pMetric->mnAscent+pMetric->mnDescent+mnEmphasisAscent+mnEmphasisDescent );
7344     aMetric.mpImplMetric->mnSlant       = ImplDevicePixelToLogicHeight( pMetric->mnSlant );
7345 
7346 #ifdef UNX
7347     // backwards compatible line metrics after fixing #i60945#
7348     if( (meOutDevType == OUTDEV_VIRDEV)
7349     &&  static_cast<const VirtualDevice*>(this)->ForceZeroExtleadBug() )
7350         aMetric.mpImplMetric->mnExtLeading = 0;
7351 #endif
7352 
7353     return aMetric;
7354 }
7355 
7356 // -----------------------------------------------------------------------
7357 
7358 FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const
7359 {
7360     // select font, query metrics, select original font again
7361     Font aOldFont = GetFont();
7362     const_cast<OutputDevice*>(this)->SetFont( rFont );
7363     FontMetric aMetric( GetFontMetric() );
7364     const_cast<OutputDevice*>(this)->SetFont( aOldFont );
7365     return aMetric;
7366 }
7367 
7368 // -----------------------------------------------------------------------
7369 
7370 /** OutputDevice::GetSysFontData
7371  *
7372  * @param nFallbacklevel Fallback font level (0 = best matching font)
7373  *
7374  * Retrieve detailed font information in platform independent structure
7375  *
7376  * @return SystemFontData
7377  **/
7378 SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const
7379 {
7380     SystemFontData aSysFontData;
7381     aSysFontData.nSize = sizeof(aSysFontData);
7382 
7383     if (!mpGraphics) ImplGetGraphics();
7384     if (mpGraphics) aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel);
7385 
7386     return aSysFontData;
7387 }
7388 
7389 
7390 // -----------------------------------------------------------------------
7391 
7392 /** OutputDevice::GetSysTextLayoutData
7393  *
7394  * @param rStartPt Start point of the text
7395  * @param rStr Text string that will be transformed into layout of glyphs
7396  * @param nIndex Position in the string from where layout will be done
7397  * @param nLen Length of the string
7398  * @param pDXAry Custom layout adjustment data
7399  *
7400  * Export finalized glyph layout data as platform independent SystemTextLayoutData
7401  * (see vcl/inc/vcl/sysdata.hxx)
7402  *
7403  * Only parameters rStartPt and rStr are mandatory, the rest is optional
7404  * (default values will be used)
7405  *
7406  * @return SystemTextLayoutData
7407  **/
7408 SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, const XubString& rStr, xub_StrLen nIndex, xub_StrLen nLen,
7409                                                         const sal_Int32* pDXAry) const
7410 {
7411     DBG_TRACE( "OutputDevice::GetSysTextLayoutData()" );
7412 	DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7413 
7414     SystemTextLayoutData aSysLayoutData;
7415     aSysLayoutData.nSize = sizeof(aSysLayoutData);
7416     aSysLayoutData.rGlyphData.reserve( 256 );
7417 
7418 	if ( mpMetaFile ) {
7419         if (pDXAry)
7420             mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
7421         else
7422             mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
7423 	}
7424 
7425 	if ( !IsDeviceOutputNecessary() ) return aSysLayoutData;
7426 
7427 	SalLayout* rLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true );
7428 
7429     // setup glyphs
7430     Point aPos;
7431     sal_GlyphId aGlyphId;
7432     for( int nStart = 0; rLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); )
7433     {
7434         // NOTE: Windows backend is producing unicode chars (ucs4), so on windows,
7435         //       ETO_GLYPH_INDEX is unusable, unless extra glyph conversion is made.
7436 
7437         SystemGlyphData aGlyph;
7438         aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK);
7439         aGlyph.x = aPos.X();
7440         aGlyph.y = aPos.Y();
7441         int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
7442         aGlyph.fallbacklevel = nLevel < MAX_FALLBACK ? nLevel : 0;
7443         aSysLayoutData.rGlyphData.push_back(aGlyph);
7444     }
7445 
7446     // Get font data
7447     aSysLayoutData.orientation = rLayout->GetOrientation();
7448 
7449     rLayout->Release();
7450 
7451     return aSysLayoutData;
7452 }
7453 
7454 // -----------------------------------------------------------------------
7455 
7456 
7457 long OutputDevice::GetMinKashida() const
7458 {
7459     DBG_TRACE( "OutputDevice::GetMinKashida()" );
7460     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7461     if( mbNewFont && !ImplNewFont() )
7462         return 0;
7463 
7464     ImplFontEntry*      pEntry = mpFontEntry;
7465     ImplFontMetricData* pMetric = &(pEntry->maMetric);
7466     return ImplDevicePixelToLogicWidth( pMetric->mnMinKashida );
7467 }
7468 // -----------------------------------------------------------------------
7469 
7470 long OutputDevice::GetMinKashida( const Font& rFont ) const
7471 {
7472     // select font, query Kashida, select original font again
7473     Font aOldFont = GetFont();
7474     const_cast<OutputDevice*>(this)->SetFont( rFont );
7475     long aKashida = GetMinKashida();
7476     const_cast<OutputDevice*>(this)->SetFont( aOldFont );
7477     return aKashida;
7478 }
7479 
7480 // -----------------------------------------------------------------------
7481 xub_StrLen OutputDevice::ValidateKashidas ( const String& rTxt,
7482 											xub_StrLen nIdx, xub_StrLen nLen,
7483 											xub_StrLen nKashCount,
7484 											const xub_StrLen* pKashidaPos,
7485 											xub_StrLen* pKashidaPosDropped ) const
7486 {
7487    // do layout
7488     SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen );
7489     if( !pSalLayout )
7490         return 0;
7491 	xub_StrLen nDropped = 0;
7492 	for( int i = 0; i < nKashCount; ++i )
7493 	{
7494 		if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] ))
7495 		{
7496 			pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ];
7497 			++nDropped;
7498 		}
7499 	}
7500 	pSalLayout->Release();
7501 	return nDropped;
7502 }
7503 
7504 // -----------------------------------------------------------------------
7505 
7506 sal_Bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const String& rStr,
7507     int nIndex, int nLen, int nBase, MetricVector& rVector )
7508 {
7509     DBG_TRACE( "OutputDevice::GetGlyphBoundRect_CTL()" );
7510     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7511 
7512     rVector.clear();
7513 
7514     if( nLen == STRING_LEN )
7515         nLen = rStr.Len() - nIndex;
7516 
7517     Rectangle aRect;
7518     for( int i = 0; i < nLen; i++ )
7519     {
7520         if( !GetTextBoundRect( aRect, rStr, sal::static_int_cast<xub_StrLen>(nBase), sal::static_int_cast<xub_StrLen>(nIndex+i), 1 ) )
7521             break;
7522         aRect.Move( rOrigin.X(), rOrigin.Y() );
7523         rVector.push_back( aRect );
7524     }
7525 
7526     return (nLen == (int)rVector.size());
7527 }
7528 
7529 // -----------------------------------------------------------------------
7530 
7531 sal_Bool OutputDevice::GetTextBoundRect( Rectangle& rRect,
7532     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
7533 	sal_uLong nLayoutWidth, const sal_Int32* pDXAry ) const
7534 {
7535     DBG_TRACE( "OutputDevice::GetTextBoundRect()" );
7536     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7537 
7538     sal_Bool bRet = sal_False;
7539     rRect.SetEmpty();
7540 
7541     SalLayout* pSalLayout = NULL;
7542 	const Point aPoint;
7543     // calculate offset when nBase!=nIndex
7544     long nXOffset = 0;
7545     if( nBase != nIndex )
7546     {
7547         xub_StrLen nStart = Min( nBase, nIndex );
7548         xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
7549         pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry );
7550         if( pSalLayout )
7551         {
7552             nXOffset = pSalLayout->GetTextWidth();
7553             nXOffset /= pSalLayout->GetUnitsPerPixel();
7554             pSalLayout->Release();
7555             // TODO: fix offset calculation for Bidi case
7556             if( nBase < nIndex)
7557                 nXOffset = -nXOffset;
7558         }
7559     }
7560 
7561     pSalLayout = ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
7562     Rectangle aPixelRect;
7563     if( pSalLayout )
7564     {
7565         bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect );
7566 
7567         if( bRet )
7568         {
7569             int nWidthFactor = pSalLayout->GetUnitsPerPixel();
7570 
7571             if( nWidthFactor > 1 )
7572             {
7573                 double fFactor = 1.0 / nWidthFactor;
7574                 aPixelRect.Left()
7575                     = static_cast< long >(aPixelRect.Left() * fFactor);
7576                 aPixelRect.Right()
7577                     = static_cast< long >(aPixelRect.Right() * fFactor);
7578                 aPixelRect.Top()
7579                     = static_cast< long >(aPixelRect.Top() * fFactor);
7580                 aPixelRect.Bottom()
7581                     = static_cast< long >(aPixelRect.Bottom() * fFactor);
7582             }
7583 
7584             Point aRotatedOfs( mnTextOffX, mnTextOffY );
7585             aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
7586             aPixelRect += aRotatedOfs;
7587             rRect = PixelToLogic( aPixelRect );
7588             if( mbMap )
7589                 rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
7590         }
7591 
7592         pSalLayout->Release();
7593     }
7594 
7595     if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
7596         return bRet;
7597 
7598     // fall back to bitmap method to get the bounding rectangle,
7599     // so we need a monochrome virtual device with matching font
7600     VirtualDevice aVDev( 1 );
7601     Font aFont( GetFont() );
7602     aFont.SetShadow( sal_False );
7603     aFont.SetOutline( sal_False );
7604     aFont.SetRelief( RELIEF_NONE );
7605     aFont.SetOrientation( 0 );
7606     aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
7607     aVDev.SetFont( aFont );
7608     aVDev.SetTextAlign( ALIGN_TOP );
7609 
7610     // layout the text on the virtual device
7611     pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
7612     if( !pSalLayout )
7613         return false;
7614 
7615     // make the bitmap big enough
7616     // TODO: use factors when it would get too big
7617     long nWidth = pSalLayout->GetTextWidth();
7618     long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
7619     Point aOffset( nWidth/2, 8 );
7620     Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() );
7621     if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) )
7622         return false;
7623 
7624     // draw text in black
7625     pSalLayout->DrawBase() = aOffset;
7626     aVDev.SetTextColor( Color( COL_BLACK ) );
7627     aVDev.SetTextFillColor();
7628     aVDev.ImplInitTextColor();
7629     aVDev.ImplDrawText( *pSalLayout );
7630     pSalLayout->Release();
7631 
7632     // find extents using the bitmap
7633     Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize );
7634     BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
7635     if( !pAcc )
7636         return sal_False;
7637     const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
7638     const long nW = pAcc->Width();
7639     const long nH = pAcc->Height();
7640     long nLeft = 0;
7641     long nRight = 0;
7642 
7643     // find top left point
7644     long nTop = 0;
7645     for(; nTop < nH; ++nTop )
7646     {
7647         for( nLeft = 0; nLeft < nW; ++nLeft )
7648             if( pAcc->GetPixel( nTop, nLeft ) == aBlack )
7649                 break;
7650         if( nLeft < nW )
7651             break;
7652     }
7653 
7654     // find bottom right point
7655     long nBottom = nH;
7656     while( --nBottom >= nTop )
7657     {
7658         for( nRight = nW; --nRight >= 0; )
7659             if( pAcc->GetPixel( nBottom, nRight ) == aBlack )
7660                 break;
7661         if( nRight >= 0 )
7662             break;
7663     }
7664     if( nRight < nLeft )
7665     {
7666         long nX = nRight;
7667         nRight = nLeft;
7668         nLeft  = nX;
7669     }
7670 
7671     for( long nY = nTop; nY <= nBottom; ++nY )
7672     {
7673         // find leftmost point
7674         long nX;
7675         for( nX = 0; nX < nLeft; ++nX )
7676             if( pAcc->GetPixel( nY, nX ) == aBlack )
7677                 break;
7678         nLeft = nX;
7679 
7680         // find rightmost point
7681         for( nX = nW; --nX > nRight; )
7682             if( pAcc->GetPixel( nY, nX ) == aBlack )
7683                 break;
7684         nRight = nX;
7685     }
7686 
7687     aBmp.ReleaseAccess( pAcc );
7688 
7689     if( nTop <= nBottom )
7690     {
7691         Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 );
7692         Point aTopLeft( nLeft, nTop );
7693         aTopLeft -= aOffset;
7694         // adjust to text alignment
7695         aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent);
7696         // convert to logical coordinates
7697         aSize = PixelToLogic( aSize );
7698         aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() );
7699         aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() );
7700         rRect = Rectangle( aTopLeft, aSize );
7701         return sal_True;
7702     }
7703 
7704     return sal_False;
7705 }
7706 
7707 // -----------------------------------------------------------------------
7708 
7709 sal_Bool OutputDevice::GetTextOutlines( ::basegfx::B2DPolyPolygonVector& rVector,
7710     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
7711     sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const
7712 {
7713     // the fonts need to be initialized
7714     if( mbNewFont )
7715         ImplNewFont();
7716     if( mbInitFont )
7717         ImplInitFont();
7718     if( !mpFontEntry )
7719         return sal_False;
7720 
7721     sal_Bool bRet = sal_False;
7722     rVector.clear();
7723     if( nLen == STRING_LEN )
7724         nLen = rStr.Len() - nIndex;
7725     rVector.reserve( nLen );
7726 
7727     // we want to get the Rectangle in logical units, so to
7728     // avoid rounding errors we just size the font in logical units
7729     sal_Bool bOldMap = mbMap;
7730     if( bOldMap )
7731     {
7732         const_cast<OutputDevice&>(*this).mbMap = sal_False;
7733         const_cast<OutputDevice&>(*this).mbNewFont = sal_True;
7734     }
7735 
7736     SalLayout* pSalLayout = NULL;
7737 
7738     // calculate offset when nBase!=nIndex
7739     long nXOffset = 0;
7740     if( nBase != nIndex )
7741     {
7742         xub_StrLen nStart = Min( nBase, nIndex );
7743         xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
7744         pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nTWidth, pDXArray );
7745         if( pSalLayout )
7746         {
7747             nXOffset = pSalLayout->GetTextWidth();
7748             pSalLayout->Release();
7749             // TODO: fix offset calculation for Bidi case
7750             if( nBase > nIndex)
7751                 nXOffset = -nXOffset;
7752         }
7753     }
7754 
7755     pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
7756     if( pSalLayout )
7757     {
7758         bRet = pSalLayout->GetOutline( *mpGraphics, rVector );
7759         if( bRet )
7760         {
7761             // transform polygon to pixel units
7762             ::basegfx::B2DHomMatrix aMatrix;
7763 
7764             int nWidthFactor = pSalLayout->GetUnitsPerPixel();
7765             if( nXOffset | mnTextOffX | mnTextOffY )
7766             {
7767                 Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor );
7768                 aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
7769                 aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() );
7770             }
7771 
7772             if( nWidthFactor > 1 )
7773             {
7774                 double fFactor = 1.0 / nWidthFactor;
7775                 aMatrix.scale( fFactor, fFactor );
7776             }
7777 
7778             if( !aMatrix.isIdentity() )
7779             {
7780                 ::basegfx::B2DPolyPolygonVector::iterator aIt = rVector.begin();
7781                 for(; aIt != rVector.end(); ++aIt )
7782                     (*aIt).transform( aMatrix );
7783             }
7784         }
7785 
7786         pSalLayout->Release();
7787     }
7788 
7789     if( bOldMap )
7790     {
7791         // restore original font size and map mode
7792         const_cast<OutputDevice&>(*this).mbMap = bOldMap;
7793         const_cast<OutputDevice&>(*this).mbNewFont = sal_True;
7794     }
7795 
7796     if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
7797         return bRet;
7798 
7799     // fall back to bitmap conversion ------------------------------------------
7800 
7801     // Here, we can savely assume that the mapping between characters and glyphs
7802     // is one-to-one. This is most probably valid for the old bitmap fonts.
7803 
7804     // fall back to bitmap method to get the bounding rectangle,
7805     // so we need a monochrome virtual device with matching font
7806     pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
7807     if (pSalLayout == 0)
7808         return false;
7809     long nOrgWidth = pSalLayout->GetTextWidth();
7810     long nOrgHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent
7811         + mnEmphasisDescent;
7812     pSalLayout->Release();
7813 
7814     VirtualDevice aVDev(1);
7815 
7816     Font aFont(GetFont());
7817     aFont.SetShadow(false);
7818     aFont.SetOutline(false);
7819     aFont.SetRelief(RELIEF_NONE);
7820     aFont.SetOrientation(0);
7821     if( bOptimize )
7822     {
7823         aFont.SetSize( Size( 0, GLYPH_FONT_HEIGHT ) );
7824         aVDev.SetMapMode( MAP_PIXEL );
7825     }
7826     aVDev.SetFont( aFont );
7827     aVDev.SetTextAlign( ALIGN_TOP );
7828     aVDev.SetTextColor( Color(COL_BLACK) );
7829     aVDev.SetTextFillColor();
7830 
7831     pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
7832     if (pSalLayout == 0)
7833         return false;
7834     long nWidth = pSalLayout->GetTextWidth();
7835     long nHeight = ((OutputDevice*)&aVDev)->mpFontEntry->mnLineHeight + ((OutputDevice*)&aVDev)->mnEmphasisAscent
7836         + ((OutputDevice*)&aVDev)->mnEmphasisDescent;
7837     pSalLayout->Release();
7838 
7839     if( !nWidth || !nHeight )
7840         return sal_True;
7841     double fScaleX = static_cast< double >(nOrgWidth) / nWidth;
7842     double fScaleY = static_cast< double >(nOrgHeight) / nHeight;
7843 
7844     // calculate offset when nBase!=nIndex
7845     // TODO: fix offset calculation for Bidi case
7846     nXOffset = 0;
7847     if( nBase != nIndex )
7848     {
7849         xub_StrLen nStart  = ((nBase < nIndex) ? nBase : nIndex);
7850         xub_StrLen nLength = ((nBase > nIndex) ? nBase : nIndex) - nStart;
7851         pSalLayout = aVDev.ImplLayout( rStr, nStart, nLength, Point(0,0), nTWidth, pDXArray );
7852         if( pSalLayout )
7853         {
7854             nXOffset = pSalLayout->GetTextWidth();
7855             pSalLayout->Release();
7856             if( nBase > nIndex)
7857                 nXOffset = -nXOffset;
7858         }
7859     }
7860 
7861     bRet = true;
7862     bool bRTL = false;
7863     String aStr( rStr ); // prepare for e.g. localized digits
7864     ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
7865     for( int nCharPos = -1; aLayoutArgs.GetNextPos( &nCharPos, &bRTL);)
7866     {
7867         bool bSuccess = false;
7868 
7869         // draw character into virtual device
7870         pSalLayout = aVDev.ImplLayout( rStr, static_cast< xub_StrLen >(nCharPos), 1, Point(0,0), nTWidth, pDXArray );
7871         if (pSalLayout == 0)
7872             return false;
7873         long nCharWidth = pSalLayout->GetTextWidth();
7874 
7875         Point aOffset(nCharWidth / 2, 8);
7876         Size aSize(nCharWidth + 2 * aOffset.X(), nHeight + 2 * aOffset.Y());
7877         bSuccess = (bool)aVDev.SetOutputSizePixel(aSize);
7878         if( bSuccess )
7879         {
7880             // draw glyph into virtual device
7881             aVDev.Erase();
7882             pSalLayout->DrawBase() += aOffset;
7883             pSalLayout->DrawBase() += Point( ((OutputDevice*)&aVDev)->mnTextOffX, ((OutputDevice*)&aVDev)->mnTextOffY );
7884             pSalLayout->DrawText( *((OutputDevice*)&aVDev)->mpGraphics );
7885             pSalLayout->Release();
7886 
7887             // convert character image into outline
7888             Bitmap aBmp( aVDev.GetBitmap(Point(0, 0), aSize));
7889 
7890             PolyPolygon aPolyPoly;
7891             bool bVectorized = aBmp.Vectorize(aPolyPoly, BMP_VECTORIZE_OUTER | BMP_VECTORIZE_REDUCE_EDGES);
7892             if( !bVectorized )
7893                 bSuccess = false;
7894             else
7895             {
7896                 // convert units to logical width
7897                 for (sal_uInt16 j = 0; j < aPolyPoly.Count(); ++j)
7898                 {
7899                     Polygon& rPoly = aPolyPoly[j];
7900                     for (sal_uInt16 k = 0; k < rPoly.GetSize(); ++k)
7901                     {
7902                         Point& rPt = rPoly[k];
7903                         rPt -= aOffset;
7904                         int nPixelX = rPt.X() - ((OutputDevice&)aVDev).mnTextOffX + nXOffset;
7905                         int nPixelY = rPt.Y() - ((OutputDevice&)aVDev).mnTextOffY;
7906                         rPt.X() = ImplDevicePixelToLogicWidth( nPixelX );
7907                         rPt.Y() = ImplDevicePixelToLogicHeight( nPixelY );
7908                     }
7909                 }
7910 
7911 
7912                 // ignore "empty" glyphs:
7913                 if( aPolyPoly.Count() > 0 )
7914                 {
7915                     // convert	to B2DPolyPolygon
7916                     // TODO: get rid of intermediate tool's PolyPolygon
7917                     ::basegfx::B2DPolyPolygon aB2DPolyPoly = aPolyPoly.getB2DPolyPolygon();
7918                     ::basegfx::B2DHomMatrix aMatrix;
7919                     aMatrix.scale( fScaleX, fScaleY );
7920                     int nAngle = GetFont().GetOrientation();
7921                     if( nAngle )
7922                         aMatrix.rotate( nAngle * F_PI1800 );
7923                     aB2DPolyPoly.transform( aMatrix );
7924                     rVector.push_back( aB2DPolyPoly );
7925                 }
7926             }
7927         }
7928 
7929         nXOffset += nCharWidth;
7930         bRet = bRet && bSuccess;
7931     }
7932 
7933     return bRet;
7934 }
7935 
7936 // -----------------------------------------------------------------------
7937 
7938 sal_Bool OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector,
7939     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex,
7940     xub_StrLen nLen, sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const
7941 {
7942     rResultVector.clear();
7943 
7944     // get the basegfx polypolygon vector
7945     ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
7946     if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
7947                          bOptimize, nTWidth, pDXArray ) )
7948     return sal_False;
7949 
7950     // convert to a tool polypolygon vector
7951     rResultVector.reserve( aB2DPolyPolyVector.size() );
7952     ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
7953     for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
7954         rResultVector.push_back(PolyPolygon(*aIt)); // #i76339#
7955 
7956     return sal_True;
7957 }
7958 
7959 // -----------------------------------------------------------------------
7960 
7961 sal_Bool OutputDevice::GetTextOutline( PolyPolygon& rPolyPoly,
7962     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
7963     sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const
7964 {
7965     rPolyPoly.Clear();
7966 
7967     // get the basegfx polypolygon vector
7968     ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
7969     if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
7970                          bOptimize, nTWidth, pDXArray ) )
7971     return sal_False;
7972 
7973     // convert and merge into a tool polypolygon
7974     ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
7975     for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
7976         for( unsigned int i = 0; i < aIt->count(); ++i )
7977             rPolyPoly.Insert(Polygon((*aIt).getB2DPolygon( i ))); // #i76339#
7978 
7979     return sal_True;
7980 }
7981 
7982 // -----------------------------------------------------------------------
7983 
7984 sal_Bool OutputDevice::GetFontCharMap( FontCharMap& rFontCharMap ) const
7985 {
7986     rFontCharMap.Reset();
7987 
7988     // we need a graphics
7989     if( !mpGraphics && !ImplGetGraphics() )
7990         return sal_False;
7991 
7992     if( mbNewFont )
7993         ImplNewFont();
7994     if( mbInitFont )
7995         ImplInitFont();
7996     if( !mpFontEntry )
7997         return sal_False;
7998 
7999 #ifdef ENABLE_IFC_CACHE    // a little font charmap cache helps considerably
8000     static const int NMAXITEMS = 16;
8001     static int nUsedItems = 0, nCurItem = 0;
8002 
8003     struct CharMapCacheItem { const ImplFontData* mpFontData; FontCharMap maCharMap; };
8004     static CharMapCacheItem aCache[ NMAXITEMS ];
8005 
8006     const ImplFontData* pFontData = mpFontEntry->maFontSelData.mpFontData;
8007 
8008     int i;
8009     for( i = nUsedItems; --i >= 0; )
8010         if( pFontData == aCache[i].mpFontData )
8011             break;
8012     if( i >= 0 )	// found in cache
8013     {
8014         rFontCharMap.Reset( aCache[i].maCharMap.mpImpl );
8015     }
8016     else            // need to cache
8017 #endif // ENABLE_IFC_CACHE
8018     {
8019         const ImplFontCharMap* pNewMap = mpGraphics->GetImplFontCharMap();
8020         rFontCharMap.Reset( pNewMap );
8021 
8022 #ifdef ENABLE_IFC_CACHE
8023         // manage cache round-robin and insert data
8024         CharMapCacheItem& rItem = aCache[ nCurItem ];
8025         rItem.mpFontData = pFontData;
8026         rItem.maCharMap.Reset( pNewMap );
8027 
8028         if( ++nCurItem >= NMAXITEMS )
8029             nCurItem = 0;
8030 
8031         if( ++nUsedItems >= NMAXITEMS )
8032             nUsedItems = NMAXITEMS;
8033 #endif // ENABLE_IFC_CACHE
8034     }
8035 
8036     if( rFontCharMap.IsDefaultMap() )
8037         return sal_False;
8038     return sal_True;
8039 }
8040 
8041 // -----------------------------------------------------------------------
8042 
8043 xub_StrLen OutputDevice::HasGlyphs( const Font& rTempFont, const String& rStr,
8044     xub_StrLen nIndex, xub_StrLen nLen ) const
8045 {
8046     if( nIndex >= rStr.Len() )
8047         return nIndex;
8048     xub_StrLen nEnd = nIndex + nLen;
8049     if( (sal_uLong)nIndex+nLen > rStr.Len() )
8050         nEnd = rStr.Len();
8051 
8052     DBG_ASSERT( nIndex < nEnd, "StartPos >= EndPos?" );
8053     DBG_ASSERT( nEnd <= rStr.Len(), "String too short" );
8054 
8055     // to get the map temporarily set font
8056     const Font aOrigFont = GetFont();
8057     const_cast<OutputDevice&>(*this).SetFont( rTempFont );
8058     FontCharMap aFontCharMap;
8059     sal_Bool bRet = GetFontCharMap( aFontCharMap );
8060     const_cast<OutputDevice&>(*this).SetFont( aOrigFont );
8061 
8062     // if fontmap is unknown assume it doesn't have the glyphs
8063     if( bRet == sal_False )
8064         return nIndex;
8065 
8066     const sal_Unicode* pStr = rStr.GetBuffer();
8067     for( pStr += nIndex; nIndex < nEnd; ++pStr, ++nIndex )
8068         if( ! aFontCharMap.HasChar( *pStr ) )
8069             return nIndex;
8070 
8071     return STRING_LEN;
8072 }
8073 
8074 // -----------------------------------------------------------------------
8075