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