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