xref: /trunk/main/vcl/source/gdi/outdev3.cxx (revision 9f62ea84)
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 nGlyphIndex;
4169             if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
4170                 break;
4171 
4172             // calculate the boundaries of each word
4173             if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
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 nGlyphIndex;
4494         if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aOutPoint, nStart ) )
4495             break;
4496 
4497         if( !mpGraphics->GetGlyphBoundRect( nGlyphIndex, aRectangle ) )
4498             continue;
4499 
4500         if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
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                 break;
5911         if( pStr >= pEnd )
5912             nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
5913     }
5914 
5915     if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS)
5916     {
5917         // disable character localization when no digits used
5918         const sal_Unicode* pBase = rStr.GetBuffer();
5919         const sal_Unicode* pStr = pBase + nMinIndex;
5920         const sal_Unicode* pEnd = pBase + nEndIndex;
5921         for( ; pStr < pEnd; ++pStr )
5922         {
5923             // TODO: are there non-digit localizations?
5924             if( (*pStr >= '0') && (*pStr <= '9') )
5925             {
5926                 // translate characters to local preference
5927                 sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
5928                 if( cChar != *pStr )
5929                     // TODO: are the localized digit surrogates?
5930                     rStr.SetChar( static_cast<sal_uInt16>(pStr - pBase),
5931                                  static_cast<sal_Unicode>(cChar) );
5932             }
5933         }
5934     }
5935 
5936     // right align for RTL text, DRAWPOS_REVERSED, RTL window style
5937     bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0);
5938     if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT )
5939         bRightAlign = false;
5940     else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT )
5941         bRightAlign = true;
5942     // SSA: hack for western office, ie text get right aligned
5943     //      for debugging purposes of mirrored UI
5944     //static const char* pEnv = getenv( "SAL_RTL_MIRRORTEXT" );
5945     bool bRTLWindow = IsRTLEnabled();
5946     bRightAlign ^= bRTLWindow;
5947     if( bRightAlign )
5948         nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN;
5949 
5950     // set layout options
5951     ImplLayoutArgs aLayoutArgs( rStr.GetBuffer(), rStr.Len(), nMinIndex, nEndIndex, nLayoutFlags );
5952 
5953     int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0;
5954     aLayoutArgs.SetOrientation( nOrientation );
5955 
5956     aLayoutArgs.SetLayoutWidth( nPixelWidth );
5957     aLayoutArgs.SetDXArray( pDXArray );
5958 
5959     return aLayoutArgs;
5960 }
5961 
5962 // -----------------------------------------------------------------------
5963 
5964 SalLayout* OutputDevice::ImplLayout( const String& rOrigStr,
5965                                      xub_StrLen nMinIndex,
5966                                      xub_StrLen nLen,
5967                                      const Point& rLogicalPos,
5968                                      long nLogicalWidth,
5969                                      const sal_Int32* pDXArray,
5970                                      bool bFilter ) const
5971 {
5972     // we need a graphics
5973     if( !mpGraphics )
5974         if( !ImplGetGraphics() )
5975             return NULL;
5976 
5977     // initialize font if needed
5978     if( mbNewFont )
5979         if( !ImplNewFont() )
5980             return NULL;
5981     if( mbInitFont )
5982         ImplInitFont();
5983 
5984     // check string index and length
5985     if( (unsigned)nMinIndex + nLen > rOrigStr.Len() )
5986     {
5987         const int nNewLen = (int)rOrigStr.Len() - nMinIndex;
5988         if( nNewLen <= 0 )
5989             return NULL;
5990         nLen = static_cast<xub_StrLen>(nNewLen);
5991     }
5992 
5993     String aStr = rOrigStr;
5994 
5995     // filter out special markers
5996     if( bFilter )
5997     {
5998         xub_StrLen nCutStart, nCutStop, nOrgLen = nLen;
5999         bool bFiltered = mpGraphics->filterText( rOrigStr, aStr, nMinIndex, nLen, nCutStart, nCutStop );
6000         if( !nLen )
6001             return NULL;
6002 
6003         if( bFiltered && nCutStop != nCutStart && pDXArray )
6004         {
6005             if( !nLen )
6006                 pDXArray = NULL;
6007             else
6008             {
6009                 sal_Int32* pAry = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen);
6010                 if( nCutStart > nMinIndex )
6011                     memcpy( pAry, pDXArray, sizeof(sal_Int32)*(nCutStart-nMinIndex) );
6012                 // note: nCutStart will never be smaller than nMinIndex
6013                 memcpy( pAry+nCutStart-nMinIndex,
6014                         pDXArray + nOrgLen - (nCutStop-nMinIndex),
6015                         sizeof(sal_Int32)*(nLen - (nCutStart-nMinIndex)) );
6016                 pDXArray = pAry;
6017             }
6018         }
6019     }
6020 
6021     // convert from logical units to physical units
6022     // recode string if needed
6023     if( mpFontEntry->mpConversion )
6024         mpFontEntry->mpConversion->RecodeString( aStr, 0, aStr.Len() );
6025 
6026     long nPixelWidth = nLogicalWidth;
6027     if( nLogicalWidth && mbMap )
6028         nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth );
6029     if( pDXArray && mbMap )
6030     {
6031         // convert from logical units to font units using a temporary array
6032         sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );
6033         // using base position for better rounding a.k.a. "dancing characters"
6034         int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() );
6035         for( int i = 0; i < nLen; ++i )
6036             pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs;
6037 
6038         pDXArray = pTempDXAry;
6039     }
6040 
6041     ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray );
6042 
6043     // get matching layout object for base font
6044     SalLayout* pSalLayout = NULL;
6045     if( mpPDFWriter )
6046         pSalLayout = mpPDFWriter->GetTextLayout( aLayoutArgs, &mpFontEntry->maFontSelData );
6047 
6048     if( !pSalLayout )
6049         pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );
6050 
6051     // layout text
6052     if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) )
6053     {
6054         pSalLayout->Release();
6055         pSalLayout = NULL;
6056     }
6057 
6058     if( !pSalLayout )
6059         return NULL;
6060 
6061     // do glyph fallback if needed
6062     // #105768# avoid fallback for very small font sizes
6063     if( aLayoutArgs.NeedFallback() )
6064         if( mpFontEntry && (mpFontEntry->maFontSelData.mnHeight >= 3) )
6065             pSalLayout = ImplGlyphFallbackLayout( pSalLayout, aLayoutArgs );
6066 
6067     // position, justify, etc. the layout
6068     pSalLayout->AdjustLayout( aLayoutArgs );
6069     pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos );
6070     // adjust to right alignment if necessary
6071     if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN )
6072     {
6073         long nRTLOffset;
6074         if( pDXArray )
6075             nRTLOffset = pDXArray[ nLen - 1 ];
6076         else if( nPixelWidth )
6077             nRTLOffset = nPixelWidth;
6078         else
6079             nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
6080         pSalLayout->DrawOffset().X() = 1 - nRTLOffset;
6081     }
6082 
6083     return pSalLayout;
6084 }
6085 
6086 // -----------------------------------------------------------------------
6087 
6088 SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const
6089 {
6090     // prepare multi level glyph fallback
6091     MultiSalLayout* pMultiSalLayout = NULL;
6092     ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
6093     rLayoutArgs.PrepareFallback();
6094     rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
6095 
6096 #if defined(HDU_DEBUG)
6097     {
6098         int nCharPos = -1;
6099         bool bRTL = false;
6100         fprintf(stderr,"OD:ImplLayout Glyph Fallback for");
6101         for( int i=0; i<8 && rLayoutArgs.GetNextPos( &nCharPos, &bRTL); ++i )
6102             fprintf(stderr," U+%04X", rLayoutArgs.mpStr[ nCharPos ] );
6103         fprintf(stderr,"\n");
6104         rLayoutArgs.ResetPos();
6105     }
6106 #endif
6107     // get list of unicodes that need glyph fallback
6108     int nCharPos = -1;
6109     bool bRTL = false;
6110     rtl::OUStringBuffer aMissingCodeBuf;
6111     while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) )
6112         aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] );
6113     rLayoutArgs.ResetPos();
6114     rtl::OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();
6115 
6116     ImplFontSelectData aFontSelData = mpFontEntry->maFontSelData;
6117 
6118     ImplFontMetricData aOrigMetric( aFontSelData );
6119     // TODO: use cached metric in fontentry
6120     mpGraphics->GetFontMetric( &aOrigMetric );
6121 
6122     // when device specific font substitution may have been performed for
6123     // the originally selected font then make sure that a fallback to that
6124     // font is performed first
6125     int nDevSpecificFallback = 0;
6126     if( mpOutDevData && !mpOutDevData->maDevFontSubst.Empty() )
6127         nDevSpecificFallback = 1;
6128 
6129     // try if fallback fonts support the missing unicodes
6130     for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
6131     {
6132         // find a font family suited for glyph fallback
6133 #ifndef FONTFALLBACK_HOOKS_DISABLED
6134         // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry
6135         // if the system-specific glyph fallback is active
6136         aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level
6137 #endif
6138         ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontList,
6139             aFontSelData, nFallbackLevel-nDevSpecificFallback, aMissingCodes );
6140         if( !pFallbackFont )
6141             break;
6142 
6143         aFontSelData.mpFontEntry = pFallbackFont;
6144         aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData;
6145         if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1)
6146         {
6147             // ignore fallback font if it is the same as the original font
6148             if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData )
6149             {
6150                 mpFontCache->Release( pFallbackFont );
6151                 continue;
6152             }
6153         }
6154 
6155 #if defined(HDU_DEBUG)
6156         {
6157             ByteString aOrigFontName( maFont.GetName(), RTL_TEXTENCODING_UTF8);
6158             ByteString aFallbackName( aFontSelData.mpFontData->GetFamilyName(),
6159                 RTL_TEXTENCODING_UTF8);
6160             fprintf(stderr,"\tGlyphFallback[lvl=%d] \"%s\" -> \"%s\" (q=%d)\n",
6161                 nFallbackLevel, aOrigFontName.GetBuffer(), aFallbackName.GetBuffer(),
6162                 aFontSelData.mpFontData->GetQuality());
6163         }
6164 #endif
6165 
6166         // TODO: try to get the metric data from the GFB's mpFontEntry
6167         ImplFontMetricData aSubstituteMetric( aFontSelData );
6168         pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel );
6169         mpGraphics->GetFontMetric( &aSubstituteMetric, nFallbackLevel );
6170 
6171         const long nOriginalHeight = aOrigMetric.mnAscent + aOrigMetric.mnDescent;
6172         const long nSubstituteHeight = aSubstituteMetric.mnAscent + aSubstituteMetric.mnDescent;
6173         // Too tall, shrink it a bit. Need a better calculation to include extra
6174         // factors and any extra wriggle room we might have available?
6175 	// TODO: should we scale by max-ascent/max-descent instead of design height?
6176         if( nSubstituteHeight > nOriginalHeight )
6177         {
6178             const float fScale = nOriginalHeight / (float)nSubstituteHeight;
6179             const float fOrigHeight = aFontSelData.mfExactHeight;
6180             const int nOrigHeight = aFontSelData.mnHeight;
6181             aFontSelData.mfExactHeight *= fScale;
6182             aFontSelData.mnHeight = static_cast<int>(aFontSelData.mfExactHeight);
6183             pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel );
6184             aFontSelData.mnHeight = nOrigHeight;
6185             aFontSelData.mfExactHeight = fOrigHeight;
6186         }
6187 
6188         // create and add glyph fallback layout to multilayout
6189         rLayoutArgs.ResetPos();
6190         SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel );
6191         if( pFallback )
6192         {
6193             if( pFallback->LayoutText( rLayoutArgs ) )
6194             {
6195                 if( !pMultiSalLayout )
6196                     pMultiSalLayout = new MultiSalLayout( *pSalLayout );
6197                 pMultiSalLayout->AddFallback( *pFallback,
6198                     rLayoutArgs.maRuns, aFontSelData.mpFontData );
6199                 if (nFallbackLevel == MAX_FALLBACK-1)
6200                     pMultiSalLayout->SetInComplete();
6201             }
6202             else
6203             {
6204                 // there is no need for a font that couldn't resolve anything
6205                 pFallback->Release();
6206             }
6207         }
6208 
6209         mpFontCache->Release( pFallbackFont );
6210 
6211         // break when this fallback was sufficient
6212         if( !rLayoutArgs.PrepareFallback() )
6213             break;
6214     }
6215 
6216     if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) )
6217         pSalLayout = pMultiSalLayout;
6218 
6219     // restore orig font settings
6220     pSalLayout->InitFont();
6221     rLayoutArgs.maRuns = aLayoutRuns;
6222 
6223     return pSalLayout;
6224 }
6225 
6226 // -----------------------------------------------------------------------
6227 
6228 sal_Bool OutputDevice::GetTextIsRTL(
6229             const String& rString,
6230             xub_StrLen nIndex, xub_StrLen nLen ) const
6231 {
6232     String aStr( rString );
6233     ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
6234     bool bRTL = false;
6235     int nCharPos = -1;
6236     aArgs.GetNextPos( &nCharPos, &bRTL );
6237     return (nCharPos != nIndex) ? sal_True : sal_False;
6238 }
6239 
6240 // -----------------------------------------------------------------------
6241 
6242 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth,
6243                                        xub_StrLen nIndex, xub_StrLen nLen,
6244                                        long nCharExtra, sal_Bool /*TODO: bCellBreaking*/ ) const
6245 {
6246     DBG_TRACE( "OutputDevice::GetTextBreak()" );
6247     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6248 
6249     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
6250     xub_StrLen nRetVal = STRING_LEN;
6251     if( pSalLayout )
6252     {
6253         // convert logical widths into layout units
6254         // NOTE: be very careful to avoid rounding errors for nCharExtra case
6255         // problem with rounding errors especially for small nCharExtras
6256         // TODO: remove when layout units have subpixel granularity
6257         long nWidthFactor = pSalLayout->GetUnitsPerPixel();
6258         long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
6259         nTextWidth *= nWidthFactor * nSubPixelFactor;
6260         long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
6261         long nExtraPixelWidth = 0;
6262         if( nCharExtra != 0 )
6263         {
6264             nCharExtra *= nWidthFactor * nSubPixelFactor;
6265             nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
6266         }
6267         nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6268 
6269         pSalLayout->Release();
6270     }
6271 
6272     return nRetVal;
6273 }
6274 
6275 // -----------------------------------------------------------------------
6276 
6277 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth,
6278                                        sal_Unicode nHyphenatorChar, xub_StrLen& rHyphenatorPos,
6279                                        xub_StrLen nIndex, xub_StrLen nLen,
6280                                        long nCharExtra ) const
6281 {
6282     DBG_TRACE( "OutputDevice::GetTextBreak()" );
6283     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6284 
6285     rHyphenatorPos = STRING_LEN;
6286 
6287     SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
6288     if( !pSalLayout )
6289         return STRING_LEN;
6290 
6291     // convert logical widths into layout units
6292     // NOTE: be very careful to avoid rounding errors for nCharExtra case
6293     // problem with rounding errors especially for small nCharExtras
6294     // TODO: remove when layout units have subpixel granularity
6295     long nWidthFactor = pSalLayout->GetUnitsPerPixel();
6296     long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
6297 
6298     nTextWidth *= nWidthFactor * nSubPixelFactor;
6299     long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
6300     long nExtraPixelWidth = 0;
6301     if( nCharExtra != 0 )
6302     {
6303         nCharExtra *= nWidthFactor * nSubPixelFactor;
6304         nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
6305     }
6306 
6307     // calculate un-hyphenated break position
6308     xub_StrLen nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6309 
6310     // calculate hyphenated break position
6311     String aHyphenatorStr( &nHyphenatorChar, 1 );
6312     xub_StrLen nTempLen = 1;
6313     SalLayout* pHyphenatorLayout = ImplLayout( aHyphenatorStr, 0, nTempLen );
6314     if( pHyphenatorLayout )
6315     {
6316         // calculate subpixel width of hyphenation character
6317         long nHyphenatorPixelWidth = pHyphenatorLayout->GetTextWidth() * nSubPixelFactor;
6318         pHyphenatorLayout->Release();
6319 
6320         // calculate hyphenated break position
6321         nTextPixelWidth -= nHyphenatorPixelWidth;
6322         if( nExtraPixelWidth > 0 )
6323             nTextPixelWidth -= nExtraPixelWidth;
6324 
6325         rHyphenatorPos = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6326 
6327         if( rHyphenatorPos > nRetVal )
6328             rHyphenatorPos = nRetVal;
6329     }
6330 
6331     pSalLayout->Release();
6332     return nRetVal;
6333 }
6334 
6335 // -----------------------------------------------------------------------
6336 
6337 void OutputDevice::ImplDrawText( OutputDevice& rTargetDevice, const Rectangle& rRect,
6338                                  const String& rOrigStr, sal_uInt16 nStyle,
6339                                  MetricVector* pVector, String* pDisplayText,
6340                                  ::vcl::ITextLayout& _rLayout )
6341 {
6342     Color aOldTextColor;
6343     Color aOldTextFillColor;
6344     sal_Bool  bRestoreFillColor = false;
6345     if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector )
6346     {
6347         sal_Bool  bHighContrastBlack = sal_False;
6348         sal_Bool  bHighContrastWhite = sal_False;
6349         const StyleSettings& rStyleSettings( rTargetDevice.GetSettings().GetStyleSettings() );
6350         if( rStyleSettings.GetHighContrastMode() )
6351         {
6352             Color aCol;
6353             if( rTargetDevice.IsBackground() )
6354                 aCol = rTargetDevice.GetBackground().GetColor();
6355             else
6356                 // best guess is the face color here
6357                 // but it may be totally wrong. the background color
6358                 // was typically already reset
6359                 aCol = rStyleSettings.GetFaceColor();
6360 
6361             bHighContrastBlack = aCol.IsDark();
6362             bHighContrastWhite = aCol.IsBright();
6363         }
6364 
6365         aOldTextColor = rTargetDevice.GetTextColor();
6366         if ( rTargetDevice.IsTextFillColor() )
6367         {
6368             bRestoreFillColor = sal_True;
6369             aOldTextFillColor = rTargetDevice.GetTextFillColor();
6370         }
6371         if( bHighContrastBlack )
6372             rTargetDevice.SetTextColor( COL_GREEN );
6373         else if( bHighContrastWhite )
6374             rTargetDevice.SetTextColor( COL_LIGHTGREEN );
6375         else
6376         {
6377             // draw disabled text always without shadow
6378             // as it fits better with native look
6379             /*
6380             SetTextColor( GetSettings().GetStyleSettings().GetLightColor() );
6381             Rectangle aRect = rRect;
6382             aRect.Move( 1, 1 );
6383             DrawText( aRect, rOrigStr, nStyle & ~TEXT_DRAW_DISABLE );
6384             */
6385             rTargetDevice.SetTextColor( rTargetDevice.GetSettings().GetStyleSettings().GetDisableColor() );
6386         }
6387     }
6388 
6389     long        nWidth          = rRect.GetWidth();
6390     long        nHeight         = rRect.GetHeight();
6391 
6392     if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) )
6393         return;
6394 
6395     Point       aPos            = rRect.TopLeft();
6396 
6397     long        nTextHeight     = rTargetDevice.GetTextHeight();
6398     TextAlign   eAlign          = rTargetDevice.GetTextAlign();
6399     xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;
6400 
6401     String aStr = rOrigStr;
6402     if ( nStyle & TEXT_DRAW_MNEMONIC )
6403         aStr = GetNonMnemonicString( aStr, nMnemonicPos );
6404 
6405     const bool bDrawMnemonics = !(rTargetDevice.GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector;
6406 
6407     // Mehrzeiligen Text behandeln wir anders
6408     if ( nStyle & TEXT_DRAW_MULTILINE )
6409     {
6410 
6411         XubString               aLastLine;
6412         ImplMultiTextLineInfo   aMultiLineInfo;
6413         ImplTextLineInfo*       pLineInfo;
6414         long                    nMaxTextWidth;
6415         xub_StrLen              i;
6416         xub_StrLen              nLines;
6417         xub_StrLen              nFormatLines;
6418 
6419         if ( nTextHeight )
6420         {
6421             nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _rLayout );
6422             nLines = (xub_StrLen)(nHeight/nTextHeight);
6423             nFormatLines = aMultiLineInfo.Count();
6424             if ( !nLines )
6425                 nLines = 1;
6426             if ( nFormatLines > nLines )
6427             {
6428                 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
6429                 {
6430                     // Letzte Zeile zusammenbauen und kuerzen
6431                     nFormatLines = nLines-1;
6432 
6433                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
6434                     aLastLine = aStr.Copy( pLineInfo->GetIndex() );
6435                     aLastLine.ConvertLineEnd( LINEEND_LF );
6436                     // Alle LineFeed's durch Spaces ersetzen
6437                     xub_StrLen nLastLineLen = aLastLine.Len();
6438                     for ( i = 0; i < nLastLineLen; i++ )
6439                     {
6440                         if ( aLastLine.GetChar( i ) == _LF )
6441                             aLastLine.SetChar( i, ' ' );
6442                     }
6443                     aLastLine = ImplGetEllipsisString( rTargetDevice, aLastLine, nWidth, nStyle, _rLayout );
6444                     nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
6445                     nStyle |= TEXT_DRAW_TOP;
6446                 }
6447             }
6448             else
6449             {
6450                 if ( nMaxTextWidth <= nWidth )
6451                     nStyle &= ~TEXT_DRAW_CLIP;
6452             }
6453 
6454             // Muss in der Hoehe geclippt werden?
6455             if ( nFormatLines*nTextHeight > nHeight )
6456                 nStyle |= TEXT_DRAW_CLIP;
6457 
6458             // Clipping setzen
6459             if ( nStyle & TEXT_DRAW_CLIP )
6460             {
6461                 rTargetDevice.Push( PUSH_CLIPREGION );
6462                 rTargetDevice.IntersectClipRegion( rRect );
6463             }
6464 
6465             // Vertikales Alignment
6466             if ( nStyle & TEXT_DRAW_BOTTOM )
6467                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
6468             else if ( nStyle & TEXT_DRAW_VCENTER )
6469                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
6470 
6471             // Font Alignment
6472             if ( eAlign == ALIGN_BOTTOM )
6473                 aPos.Y() += nTextHeight;
6474             else if ( eAlign == ALIGN_BASELINE )
6475                 aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
6476 
6477             // Alle Zeilen ausgeben, bis auf die letzte
6478             for ( i = 0; i < nFormatLines; i++ )
6479             {
6480                 pLineInfo = aMultiLineInfo.GetLine( i );
6481                 if ( nStyle & TEXT_DRAW_RIGHT )
6482                     aPos.X() += nWidth-pLineInfo->GetWidth();
6483                 else if ( nStyle & TEXT_DRAW_CENTER )
6484                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
6485                 xub_StrLen nIndex   = pLineInfo->GetIndex();
6486                 xub_StrLen nLineLen = pLineInfo->GetLen();
6487                 _rLayout.DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
6488                 if ( bDrawMnemonics )
6489                 {
6490                     if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
6491                     {
6492                         long        nMnemonicX;
6493                         long        nMnemonicY;
6494                         long        nMnemonicWidth;
6495 
6496                         sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen );
6497                         /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray,
6498                                                 nIndex, nLineLen );
6499                         long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
6500                         long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
6501                         nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) );
6502 
6503                         Point       aTempPos = rTargetDevice.LogicToPixel( aPos );
6504                         nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min( lc_x1, lc_x2 ) );
6505                         nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
6506                         rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
6507                     }
6508                 }
6509                 aPos.Y() += nTextHeight;
6510                 aPos.X() = rRect.Left();
6511             }
6512 
6513 
6514             // Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben,
6515             // da die Zeile gekuerzt wurde
6516             if ( aLastLine.Len() )
6517                 _rLayout.DrawText( aPos, aLastLine, 0, STRING_LEN, pVector, pDisplayText );
6518 
6519             // Clipping zuruecksetzen
6520             if ( nStyle & TEXT_DRAW_CLIP )
6521                 rTargetDevice.Pop();
6522         }
6523     }
6524     else
6525     {
6526         long nTextWidth = _rLayout.GetTextWidth( aStr, 0, STRING_LEN );
6527 
6528         // Evt. Text kuerzen
6529         if ( nTextWidth > nWidth )
6530         {
6531             if ( nStyle & TEXT_DRAW_ELLIPSIS )
6532             {
6533                 aStr = ImplGetEllipsisString( rTargetDevice, aStr, nWidth, nStyle, _rLayout );
6534                 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
6535                 nStyle |= TEXT_DRAW_LEFT;
6536                 nTextWidth = _rLayout.GetTextWidth( aStr, 0, aStr.Len() );
6537             }
6538         }
6539         else
6540         {
6541             if ( nTextHeight <= nHeight )
6542                 nStyle &= ~TEXT_DRAW_CLIP;
6543         }
6544 
6545         // horizontal text alignment
6546         if ( nStyle & TEXT_DRAW_RIGHT )
6547             aPos.X() += nWidth-nTextWidth;
6548         else if ( nStyle & TEXT_DRAW_CENTER )
6549             aPos.X() += (nWidth-nTextWidth)/2;
6550 
6551         // vertical font alignment
6552         if ( eAlign == ALIGN_BOTTOM )
6553             aPos.Y() += nTextHeight;
6554         else if ( eAlign == ALIGN_BASELINE )
6555             aPos.Y() += rTargetDevice.GetFontMetric().GetAscent();
6556 
6557         if ( nStyle & TEXT_DRAW_BOTTOM )
6558             aPos.Y() += nHeight-nTextHeight;
6559         else if ( nStyle & TEXT_DRAW_VCENTER )
6560             aPos.Y() += (nHeight-nTextHeight)/2;
6561 
6562         long        nMnemonicX = 0;
6563         long        nMnemonicY = 0;
6564         long        nMnemonicWidth = 0;
6565         if ( nMnemonicPos != STRING_NOTFOUND )
6566         {
6567             sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.Len() );
6568             /*sal_Bool bRet =*/ _rLayout.GetCaretPositions( aStr, pCaretXArray, 0, aStr.Len() );
6569             long lc_x1 = pCaretXArray[2*(nMnemonicPos)];
6570             long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1];
6571             nMnemonicWidth = rTargetDevice.ImplLogicWidthToDevicePixel( ::abs((int)(lc_x1 - lc_x2)) );
6572 
6573             Point aTempPos = rTargetDevice.LogicToPixel( aPos );
6574             nMnemonicX = rTargetDevice.GetOutOffXPixel() + aTempPos.X() + rTargetDevice.ImplLogicWidthToDevicePixel( Min(lc_x1, lc_x2) );
6575             nMnemonicY = rTargetDevice.GetOutOffYPixel() + aTempPos.Y() + rTargetDevice.ImplLogicWidthToDevicePixel( rTargetDevice.GetFontMetric().GetAscent() );
6576         }
6577 
6578         if ( nStyle & TEXT_DRAW_CLIP )
6579         {
6580             rTargetDevice.Push( PUSH_CLIPREGION );
6581             rTargetDevice.IntersectClipRegion( rRect );
6582             _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
6583             if ( bDrawMnemonics )
6584             {
6585                 if ( nMnemonicPos != STRING_NOTFOUND )
6586                     rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
6587             }
6588             rTargetDevice.Pop();
6589         }
6590         else
6591         {
6592             _rLayout.DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
6593             if ( bDrawMnemonics )
6594             {
6595                 if ( nMnemonicPos != STRING_NOTFOUND )
6596                     rTargetDevice.ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
6597             }
6598         }
6599     }
6600 
6601     if ( nStyle & TEXT_DRAW_DISABLE && !pVector )
6602     {
6603         rTargetDevice.SetTextColor( aOldTextColor );
6604         if ( bRestoreFillColor )
6605             rTargetDevice.SetTextFillColor( aOldTextFillColor );
6606     }
6607 }
6608 
6609 // -----------------------------------------------------------------------
6610 
6611 void OutputDevice::AddTextRectActions( const Rectangle& rRect,
6612                                        const String&    rOrigStr,
6613                                        sal_uInt16           nStyle,
6614                                        GDIMetaFile&     rMtf )
6615 {
6616     DBG_TRACE( "OutputDevice::AddTextRectActions( const Rectangle& )" );
6617     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6618 
6619     if ( !rOrigStr.Len() || rRect.IsEmpty() )
6620         return;
6621 
6622     // we need a graphics
6623     if( !mpGraphics && !ImplGetGraphics() )
6624         return;
6625     if( mbInitClipRegion )
6626         ImplInitClipRegion();
6627 
6628     // temporarily swap in passed mtf for action generation, and
6629     // disable output generation.
6630     const sal_Bool bOutputEnabled( IsOutputEnabled() );
6631     GDIMetaFile* pMtf = mpMetaFile;
6632 
6633     mpMetaFile = &rMtf;
6634     EnableOutput( sal_False );
6635 
6636     // #i47157# Factored out to ImplDrawTextRect(), to be shared
6637     // between us and DrawText()
6638     DefaultTextLayout aLayout( *this );
6639     ImplDrawText( *this, rRect, rOrigStr, nStyle, NULL, NULL, aLayout );
6640 
6641     // and restore again
6642     EnableOutput( bOutputEnabled );
6643     mpMetaFile = pMtf;
6644 }
6645 
6646 // -----------------------------------------------------------------------
6647 
6648 void OutputDevice::DrawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle,
6649                              MetricVector* pVector, String* pDisplayText,
6650                              ::vcl::ITextLayout* _pTextLayout )
6651 {
6652     if( mpOutDevData && mpOutDevData->mpRecordLayout )
6653     {
6654         pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
6655         pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
6656     }
6657 
6658     DBG_TRACE( "OutputDevice::DrawText( const Rectangle& )" );
6659     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6660 
6661     bool bDecomposeTextRectAction = ( _pTextLayout != NULL ) && _pTextLayout->DecomposeTextRectAction();
6662     if ( mpMetaFile && !bDecomposeTextRectAction )
6663         mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) );
6664 
6665     if ( ( !IsDeviceOutputNecessary() && !pVector && !bDecomposeTextRectAction ) || !rOrigStr.Len() || rRect.IsEmpty() )
6666         return;
6667 
6668     // we need a graphics
6669     if( !mpGraphics && !ImplGetGraphics() )
6670         return;
6671     if( mbInitClipRegion )
6672         ImplInitClipRegion();
6673     if( mbOutputClipped && !bDecomposeTextRectAction )
6674         return;
6675 
6676     // temporarily disable mtf action generation (ImplDrawText _does_
6677     // create META_TEXT_ACTIONs otherwise)
6678     GDIMetaFile* pMtf = mpMetaFile;
6679     if ( !bDecomposeTextRectAction )
6680         mpMetaFile = NULL;
6681 
6682     // #i47157# Factored out to ImplDrawText(), to be used also
6683     // from AddTextRectActions()
6684     DefaultTextLayout aDefaultLayout( *this );
6685     ImplDrawText( *this, rRect, rOrigStr, nStyle, pVector, pDisplayText, _pTextLayout ? *_pTextLayout : aDefaultLayout );
6686 
6687     // and enable again
6688     mpMetaFile = pMtf;
6689 
6690     if( mpAlphaVDev )
6691         mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
6692 }
6693 
6694 // -----------------------------------------------------------------------
6695 
6696 Rectangle OutputDevice::GetTextRect( const Rectangle& rRect,
6697                                      const XubString& rStr, sal_uInt16 nStyle,
6698                                      TextRectInfo* pInfo,
6699                                      const ::vcl::ITextLayout* _pTextLayout ) const
6700 {
6701     DBG_TRACE( "OutputDevice::GetTextRect()" );
6702     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6703 
6704     Rectangle           aRect = rRect;
6705     xub_StrLen          nLines;
6706     long                nWidth = rRect.GetWidth();
6707     long                nMaxWidth;
6708     long                nTextHeight = GetTextHeight();
6709 
6710     String aStr = rStr;
6711     if ( nStyle & TEXT_DRAW_MNEMONIC )
6712         aStr = GetNonMnemonicString( aStr );
6713 
6714     if ( nStyle & TEXT_DRAW_MULTILINE )
6715     {
6716         ImplMultiTextLineInfo   aMultiLineInfo;
6717         ImplTextLineInfo*       pLineInfo;
6718         xub_StrLen              nFormatLines;
6719         xub_StrLen              i;
6720 
6721         nMaxWidth = 0;
6722         DefaultTextLayout aDefaultLayout( *const_cast< OutputDevice* >( this ) );
6723         ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, _pTextLayout ? *_pTextLayout : aDefaultLayout );
6724         nFormatLines = aMultiLineInfo.Count();
6725         if ( !nTextHeight )
6726             nTextHeight = 1;
6727         nLines = (sal_uInt16)(aRect.GetHeight()/nTextHeight);
6728         if ( pInfo )
6729             pInfo->mnLineCount = nFormatLines;
6730         if ( !nLines )
6731             nLines = 1;
6732         if ( nFormatLines <= nLines )
6733             nLines = nFormatLines;
6734         else
6735         {
6736             if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) )
6737                 nLines = nFormatLines;
6738             else
6739             {
6740                 if ( pInfo )
6741                     pInfo->mbEllipsis = sal_True;
6742                 nMaxWidth = nWidth;
6743             }
6744         }
6745         if ( pInfo )
6746         {
6747             sal_Bool bMaxWidth = nMaxWidth == 0;
6748             pInfo->mnMaxWidth = 0;
6749             for ( i = 0; i < nLines; i++ )
6750             {
6751                 pLineInfo = aMultiLineInfo.GetLine( i );
6752                 if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) )
6753                     nMaxWidth = pLineInfo->GetWidth();
6754                 if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth )
6755                     pInfo->mnMaxWidth = pLineInfo->GetWidth();
6756             }
6757         }
6758         else if ( !nMaxWidth )
6759         {
6760             for ( i = 0; i < nLines; i++ )
6761             {
6762                 pLineInfo = aMultiLineInfo.GetLine( i );
6763                 if ( pLineInfo->GetWidth() > nMaxWidth )
6764                     nMaxWidth = pLineInfo->GetWidth();
6765             }
6766         }
6767     }
6768     else
6769     {
6770         nLines      = 1;
6771         nMaxWidth   = _pTextLayout ? _pTextLayout->GetTextWidth( aStr, 0, aStr.Len() ) : GetTextWidth( aStr );
6772 
6773         if ( pInfo )
6774         {
6775             pInfo->mnLineCount  = 1;
6776             pInfo->mnMaxWidth   = nMaxWidth;
6777         }
6778 
6779         if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) )
6780         {
6781             if ( pInfo )
6782                 pInfo->mbEllipsis = sal_True;
6783             nMaxWidth = nWidth;
6784         }
6785     }
6786 
6787     if ( nStyle & TEXT_DRAW_RIGHT )
6788         aRect.Left() = aRect.Right()-nMaxWidth+1;
6789     else if ( nStyle & TEXT_DRAW_CENTER )
6790     {
6791         aRect.Left() += (nWidth-nMaxWidth)/2;
6792         aRect.Right() = aRect.Left()+nMaxWidth-1;
6793     }
6794     else
6795         aRect.Right() = aRect.Left()+nMaxWidth-1;
6796 
6797     if ( nStyle & TEXT_DRAW_BOTTOM )
6798         aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1;
6799     else if ( nStyle & TEXT_DRAW_VCENTER )
6800     {
6801         aRect.Top()   += (aRect.GetHeight()-(nTextHeight*nLines))/2;
6802         aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
6803     }
6804     else
6805         aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
6806 
6807     aRect.Right()++; // #99188# get rid of rounding problems when using this rect later
6808     return aRect;
6809 }
6810 
6811 // -----------------------------------------------------------------------
6812 
6813 static sal_Bool ImplIsCharIn( xub_Unicode c, const sal_Char* pStr )
6814 {
6815     while ( *pStr )
6816     {
6817         if ( *pStr == c )
6818             return sal_True;
6819         pStr++;
6820     }
6821 
6822     return sal_False;
6823 }
6824 
6825 // -----------------------------------------------------------------------
6826 
6827 String OutputDevice::GetEllipsisString( const String& rOrigStr, long nMaxWidth,
6828                                         sal_uInt16 nStyle ) const
6829 {
6830     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6831     DefaultTextLayout aTextLayout( *const_cast< OutputDevice* >( this ) );
6832     return ImplGetEllipsisString( *this, rOrigStr, nMaxWidth, nStyle, aTextLayout );
6833 }
6834 
6835 // -----------------------------------------------------------------------
6836 
6837 String OutputDevice::ImplGetEllipsisString( const OutputDevice& rTargetDevice, const XubString& rOrigStr, long nMaxWidth,
6838                                                sal_uInt16 nStyle, const ::vcl::ITextLayout& _rLayout )
6839 {
6840     DBG_TRACE( "OutputDevice::ImplGetEllipsisString()" );
6841 
6842     String aStr = rOrigStr;
6843     xub_StrLen nIndex = _rLayout.GetTextBreak( aStr, nMaxWidth, 0, aStr.Len() );
6844 
6845 
6846     if ( nIndex != STRING_LEN )
6847     {
6848         if( (nStyle & TEXT_DRAW_CENTERELLIPSIS) == TEXT_DRAW_CENTERELLIPSIS )
6849         {
6850             String aTmpStr( aStr );
6851             xub_StrLen nEraseChars = 4;
6852             while( nEraseChars < aStr.Len() && _rLayout.GetTextWidth( aTmpStr, 0, aTmpStr.Len() ) > nMaxWidth )
6853             {
6854                 aTmpStr = aStr;
6855                 xub_StrLen i = (aTmpStr.Len() - nEraseChars)/2;
6856                 aTmpStr.Erase( i, nEraseChars++ );
6857                 aTmpStr.InsertAscii( "...", i );
6858             }
6859             aStr = aTmpStr;
6860         }
6861         else if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
6862         {
6863             aStr.Erase( nIndex );
6864             if ( nIndex > 1 )
6865             {
6866                 aStr.AppendAscii( "..." );
6867                 while ( aStr.Len() && (_rLayout.GetTextWidth( aStr, 0, aStr.Len() ) > nMaxWidth) )
6868                 {
6869                     if ( (nIndex > 1) || (nIndex == aStr.Len()) )
6870                         nIndex--;
6871                     aStr.Erase( nIndex, 1 );
6872                 }
6873             }
6874 
6875             if ( !aStr.Len() && (nStyle & TEXT_DRAW_CLIP) )
6876                 aStr += rOrigStr.GetChar( 0 );
6877         }
6878         else if ( nStyle & TEXT_DRAW_PATHELLIPSIS )
6879         {
6880             rtl::OUString aPath( rOrigStr );
6881             rtl::OUString aAbbreviatedPath;
6882             osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, NULL );
6883             aStr = aAbbreviatedPath;
6884         }
6885         else if ( nStyle & TEXT_DRAW_NEWSELLIPSIS )
6886         {
6887             static sal_Char const   pSepChars[] = ".";
6888             // Letztes Teilstueck ermitteln
6889             xub_StrLen nLastContent = aStr.Len();
6890             while ( nLastContent )
6891             {
6892                 nLastContent--;
6893                 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) )
6894                     break;
6895             }
6896             while ( nLastContent &&
6897                     ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) )
6898                 nLastContent--;
6899 
6900             XubString aLastStr( aStr, nLastContent, aStr.Len() );
6901             XubString aTempLastStr1( RTL_CONSTASCII_USTRINGPARAM( "..." ) );
6902             aTempLastStr1 += aLastStr;
6903             if ( _rLayout.GetTextWidth( aTempLastStr1, 0, aTempLastStr1.Len() ) > nMaxWidth )
6904                 aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
6905             else
6906             {
6907                 sal_uInt16 nFirstContent = 0;
6908                 while ( nFirstContent < nLastContent )
6909                 {
6910                     nFirstContent++;
6911                     if ( ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) )
6912                         break;
6913                 }
6914                 while ( (nFirstContent < nLastContent) &&
6915                         ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) )
6916                     nFirstContent++;
6917 
6918                 if ( nFirstContent >= nLastContent )
6919                     aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
6920                 else
6921                 {
6922                     if ( nFirstContent > 4 )
6923                         nFirstContent = 4;
6924                     XubString aFirstStr( aStr, 0, nFirstContent );
6925                     aFirstStr.AppendAscii( "..." );
6926                     XubString aTempStr = aFirstStr;
6927                     aTempStr += aLastStr;
6928                     if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth )
6929                         aStr = OutputDevice::ImplGetEllipsisString( rTargetDevice, aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS, _rLayout );
6930                     else
6931                     {
6932                         do
6933                         {
6934                             aStr = aTempStr;
6935                             if( nLastContent > aStr.Len() )
6936                                 nLastContent = aStr.Len();
6937                             while ( nFirstContent < nLastContent )
6938                             {
6939                                 nLastContent--;
6940                                 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) )
6941                                     break;
6942 
6943                             }
6944                             while ( (nFirstContent < nLastContent) &&
6945                                     ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) )
6946                                 nLastContent--;
6947 
6948                             if ( nFirstContent < nLastContent )
6949                             {
6950                                 XubString aTempLastStr( aStr, nLastContent, aStr.Len() );
6951                                 aTempStr = aFirstStr;
6952                                 aTempStr += aTempLastStr;
6953                                 if ( _rLayout.GetTextWidth( aTempStr, 0, aTempStr.Len() ) > nMaxWidth )
6954                                     break;
6955                             }
6956                         }
6957                         while ( nFirstContent < nLastContent );
6958                     }
6959                 }
6960             }
6961         }
6962     }
6963 
6964     return aStr;
6965 }
6966 
6967 // -----------------------------------------------------------------------
6968 
6969 void OutputDevice::DrawCtrlText( const Point& rPos, const XubString& rStr,
6970                                  xub_StrLen nIndex, xub_StrLen nLen,
6971                                  sal_uInt16 nStyle, MetricVector* pVector, String* pDisplayText )
6972 {
6973     DBG_TRACE( "OutputDevice::DrawCtrlText()" );
6974     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6975 
6976     if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.Len()) )
6977         return;
6978 
6979     // better get graphics here because ImplDrawMnemonicLine() will not
6980     // we need a graphics
6981     if( !mpGraphics && !ImplGetGraphics() )
6982         return;
6983     if( mbInitClipRegion )
6984         ImplInitClipRegion();
6985     if ( mbOutputClipped )
6986         return;
6987 
6988     if( nIndex >= rStr.Len() )
6989         return;
6990     if( (sal_uLong)nIndex+nLen >= rStr.Len() )
6991         nLen = rStr.Len() - nIndex;
6992 
6993     XubString   aStr = rStr;
6994     xub_StrLen  nMnemonicPos = STRING_NOTFOUND;
6995 
6996     long        nMnemonicX = 0;
6997     long        nMnemonicY = 0;
6998     long        nMnemonicWidth = 0;
6999     if ( (nStyle & TEXT_DRAW_MNEMONIC) && nLen > 1 )
7000     {
7001         aStr = GetNonMnemonicString( aStr, nMnemonicPos );
7002         if ( nMnemonicPos != STRING_NOTFOUND )
7003         {
7004             if( nMnemonicPos < nIndex )
7005                 --nIndex;
7006             else if( nLen < STRING_LEN )
7007             {
7008                 if( nMnemonicPos < (nIndex+nLen) )
7009                     --nLen;
7010                 DBG_ASSERT( nMnemonicPos < (nIndex+nLen), "Mnemonic underline marker after last character" );
7011             }
7012             sal_Bool bInvalidPos = sal_False;
7013 
7014             if( nMnemonicPos >= nLen )
7015             {
7016                 // #106952#
7017                 // may occur in BiDi-Strings: the '~' is sometimes found behind the last char
7018                 // due to some strange BiDi text editors
7019                 // ->place the underline behind the string to indicate a failure
7020                 bInvalidPos = sal_True;
7021                 nMnemonicPos = nLen-1;
7022             }
7023 
7024             sal_Int32* pCaretXArray = (sal_Int32*)alloca( 2 * sizeof(sal_Int32) * nLen );
7025             /*sal_Bool bRet =*/ GetCaretPositions( aStr, pCaretXArray, nIndex, nLen );
7026             long lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ];
7027             long lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ];
7028             nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
7029 
7030             Point aTempPos( Min(lc_x1,lc_x2), GetFontMetric().GetAscent() );
7031             if( bInvalidPos )  // #106952#, place behind the (last) character
7032                 aTempPos = Point( Max(lc_x1,lc_x2), GetFontMetric().GetAscent() );
7033 
7034 			aTempPos += rPos;
7035             aTempPos = LogicToPixel( aTempPos );
7036             nMnemonicX = mnOutOffX + aTempPos.X();
7037             nMnemonicY = mnOutOffY + aTempPos.Y();
7038         }
7039     }
7040 
7041     if ( nStyle & TEXT_DRAW_DISABLE && ! pVector )
7042     {
7043         Color aOldTextColor;
7044         Color aOldTextFillColor;
7045         sal_Bool  bRestoreFillColor;
7046         sal_Bool  bHighContrastBlack = sal_False;
7047         sal_Bool  bHighContrastWhite = sal_False;
7048         const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
7049         if( rStyleSettings.GetHighContrastMode() )
7050         {
7051             if( IsBackground() )
7052             {
7053                 Wallpaper aWall = GetBackground();
7054                 Color aCol = aWall.GetColor();
7055                 bHighContrastBlack = aCol.IsDark();
7056                 bHighContrastWhite = aCol.IsBright();
7057             }
7058         }
7059 
7060         aOldTextColor = GetTextColor();
7061         if ( IsTextFillColor() )
7062         {
7063             bRestoreFillColor = sal_True;
7064             aOldTextFillColor = GetTextFillColor();
7065         }
7066         else
7067             bRestoreFillColor = sal_False;
7068 
7069         if( bHighContrastBlack )
7070             SetTextColor( COL_GREEN );
7071         else if( bHighContrastWhite )
7072             SetTextColor( COL_LIGHTGREEN );
7073         else
7074             SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
7075 
7076         DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
7077         if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7078         {
7079             if ( nMnemonicPos != STRING_NOTFOUND )
7080                 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7081         }
7082         SetTextColor( aOldTextColor );
7083         if ( bRestoreFillColor )
7084             SetTextFillColor( aOldTextFillColor );
7085     }
7086     else
7087     {
7088         DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
7089         if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7090         {
7091             if ( nMnemonicPos != STRING_NOTFOUND )
7092                 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7093         }
7094     }
7095 
7096     if( mpAlphaVDev )
7097         mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText );
7098 }
7099 
7100 // -----------------------------------------------------------------------
7101 
7102 long OutputDevice::GetCtrlTextWidth( const String& rStr,
7103                                      xub_StrLen nIndex, xub_StrLen nLen,
7104                                      sal_uInt16 nStyle ) const
7105 {
7106     DBG_TRACE( "OutputDevice::GetCtrlTextSize()" );
7107     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7108 
7109     if ( nStyle & TEXT_DRAW_MNEMONIC )
7110     {
7111         xub_StrLen  nMnemonicPos;
7112         XubString   aStr = GetNonMnemonicString( rStr, nMnemonicPos );
7113         if ( nMnemonicPos != STRING_NOTFOUND )
7114         {
7115             if ( nMnemonicPos < nIndex )
7116                 nIndex--;
7117             else if ( (nLen < STRING_LEN) &&
7118                       (nMnemonicPos >= nIndex) && (nMnemonicPos < (sal_uLong)(nIndex+nLen)) )
7119                 nLen--;
7120         }
7121         return GetTextWidth( aStr, nIndex, nLen );
7122     }
7123     else
7124         return GetTextWidth( rStr, nIndex, nLen );
7125 }
7126 
7127 // -----------------------------------------------------------------------
7128 
7129 String OutputDevice::GetNonMnemonicString( const String& rStr, xub_StrLen& rMnemonicPos )
7130 {
7131     String   aStr    = rStr;
7132     xub_StrLen  nLen    = aStr.Len();
7133     xub_StrLen  i       = 0;
7134 
7135     rMnemonicPos = STRING_NOTFOUND;
7136     while ( i < nLen )
7137     {
7138         if ( aStr.GetChar( i ) == '~' )
7139         {
7140             if ( aStr.GetChar( i+1 ) != '~' )
7141             {
7142                 if ( rMnemonicPos == STRING_NOTFOUND )
7143                     rMnemonicPos = i;
7144                 aStr.Erase( i, 1 );
7145                 nLen--;
7146             }
7147             else
7148             {
7149                 aStr.Erase( i, 1 );
7150                 nLen--;
7151                 i++;
7152             }
7153         }
7154         else
7155             i++;
7156     }
7157 
7158     return aStr;
7159 }
7160 
7161 // -----------------------------------------------------------------------
7162 
7163 int OutputDevice::GetDevFontCount() const
7164 {
7165     DBG_TRACE( "OutputDevice::GetDevFontCount()" );
7166     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7167 
7168     if( !mpGetDevFontList )
7169         mpGetDevFontList = mpFontList->GetDevFontList();
7170     return mpGetDevFontList->Count();
7171 }
7172 
7173 // -----------------------------------------------------------------------
7174 
7175 FontInfo OutputDevice::GetDevFont( int nDevFontIndex ) const
7176 {
7177     DBG_TRACE( "OutputDevice::GetDevFont()" );
7178     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7179 
7180     FontInfo aFontInfo;
7181 
7182     ImplInitFontList();
7183 
7184     int nCount = GetDevFontCount();
7185     if( nDevFontIndex < nCount )
7186     {
7187         const ImplFontData& rData = *mpGetDevFontList->Get( nDevFontIndex );
7188         aFontInfo.SetName( rData.maName );
7189         aFontInfo.SetStyleName( rData.maStyleName );
7190         aFontInfo.SetCharSet( rData.mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
7191         aFontInfo.SetFamily( rData.meFamily );
7192         aFontInfo.SetPitch( rData.mePitch );
7193         aFontInfo.SetWeight( rData.meWeight );
7194         aFontInfo.SetItalic( rData.meItalic );
7195         aFontInfo.SetWidthType( rData.meWidthType );
7196         if( rData.IsScalable() )
7197             aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
7198         if( rData.mbDevice )
7199             aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
7200     }
7201 
7202     return aFontInfo;
7203 }
7204 
7205 // -----------------------------------------------------------------------
7206 
7207 sal_Bool OutputDevice::AddTempDevFont( const String& rFileURL, const String& rFontName )
7208 {
7209     DBG_TRACE( "OutputDevice::AddTempDevFont()" );
7210     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7211 
7212     ImplInitFontList();
7213 
7214     if( !mpGraphics && !ImplGetGraphics() )
7215         return sal_False;
7216 
7217     bool bRC = mpGraphics->AddTempDevFont( mpFontList, rFileURL, rFontName );
7218     if( !bRC )
7219         return sal_False;
7220 
7221     if( mpAlphaVDev )
7222         mpAlphaVDev->AddTempDevFont( rFileURL, rFontName );
7223 
7224     mpFontCache->Invalidate();
7225     return sal_True;
7226 }
7227 
7228 // -----------------------------------------------------------------------
7229 
7230 int OutputDevice::GetDevFontSizeCount( const Font& rFont ) const
7231 {
7232     DBG_TRACE( "OutputDevice::GetDevFontSizeCount()" );
7233     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7234 
7235     delete mpGetDevSizeList;
7236 
7237     ImplInitFontList();
7238     mpGetDevSizeList = mpFontList->GetDevSizeList( rFont.GetName() );
7239     return mpGetDevSizeList->Count();
7240 }
7241 
7242 // -----------------------------------------------------------------------
7243 
7244 Size OutputDevice::GetDevFontSize( const Font& rFont, int nSizeIndex ) const
7245 {
7246     DBG_TRACE( "OutputDevice::GetDevFontSize()" );
7247     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7248 
7249     // check range
7250     int nCount = GetDevFontSizeCount( rFont );
7251     if ( nSizeIndex >= nCount )
7252         return Size();
7253 
7254     // when mapping is enabled round to .5 points
7255     Size aSize( 0, mpGetDevSizeList->Get( nSizeIndex ) );
7256     if ( mbMap )
7257     {
7258         aSize.Height() *= 10;
7259         MapMode aMap( MAP_10TH_INCH, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) );
7260         aSize = PixelToLogic( aSize, aMap );
7261         aSize.Height() += 5;
7262         aSize.Height() /= 10;
7263         long nRound = aSize.Height() % 5;
7264         if ( nRound >= 3 )
7265             aSize.Height() += (5-nRound);
7266         else
7267             aSize.Height() -= nRound;
7268         aSize.Height() *= 10;
7269         aSize = LogicToPixel( aSize, aMap );
7270         aSize = PixelToLogic( aSize );
7271         aSize.Height() += 5;
7272         aSize.Height() /= 10;
7273     }
7274     return aSize;
7275 }
7276 
7277 // -----------------------------------------------------------------------
7278 
7279 sal_Bool OutputDevice::IsFontAvailable( const String& rFontName ) const
7280 {
7281     DBG_TRACE( "OutputDevice::IsFontAvailable()" );
7282     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7283 
7284     ImplDevFontListData* pFound = mpFontList->FindFontFamily( rFontName );
7285     return (pFound != NULL);
7286 }
7287 
7288 // -----------------------------------------------------------------------
7289 
7290 FontMetric OutputDevice::GetFontMetric() const
7291 {
7292     DBG_TRACE( "OutputDevice::GetFontMetric()" );
7293     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7294 
7295     FontMetric aMetric;
7296     if( mbNewFont && !ImplNewFont() )
7297         return aMetric;
7298 
7299     ImplFontEntry*      pEntry = mpFontEntry;
7300     ImplFontMetricData* pMetric = &(pEntry->maMetric);
7301 
7302     // prepare metric
7303     aMetric.Font::operator=( maFont );
7304 
7305     // set aMetric with info from font
7306     aMetric.SetName( maFont.GetName() );
7307     aMetric.SetStyleName( pMetric->maStyleName );
7308     aMetric.SetSize( PixelToLogic( Size( pMetric->mnWidth, pMetric->mnAscent+pMetric->mnDescent-pMetric->mnIntLeading ) ) );
7309     aMetric.SetCharSet( pMetric->mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
7310     aMetric.SetFamily( pMetric->meFamily );
7311     aMetric.SetPitch( pMetric->mePitch );
7312     aMetric.SetWeight( pMetric->meWeight );
7313     aMetric.SetItalic( pMetric->meItalic );
7314     aMetric.SetWidthType( pMetric->meWidthType );
7315     if ( pEntry->mnOwnOrientation )
7316         aMetric.SetOrientation( pEntry->mnOwnOrientation );
7317     else
7318         aMetric.SetOrientation( pMetric->mnOrientation );
7319     if( !pEntry->maMetric.mbKernableFont )
7320          aMetric.SetKerning( maFont.GetKerning() & ~KERNING_FONTSPECIFIC );
7321 
7322     // set remaining metric fields
7323     aMetric.mpImplMetric->mnMiscFlags   = 0;
7324     if( pMetric->mbDevice )
7325             aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
7326     if( pMetric->mbScalableFont )
7327             aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
7328     aMetric.mpImplMetric->mnAscent      = ImplDevicePixelToLogicHeight( pMetric->mnAscent+mnEmphasisAscent );
7329     aMetric.mpImplMetric->mnDescent     = ImplDevicePixelToLogicHeight( pMetric->mnDescent+mnEmphasisDescent );
7330     aMetric.mpImplMetric->mnIntLeading  = ImplDevicePixelToLogicHeight( pMetric->mnIntLeading+mnEmphasisAscent );
7331     aMetric.mpImplMetric->mnExtLeading  = ImplDevicePixelToLogicHeight( pMetric->mnExtLeading );
7332     aMetric.mpImplMetric->mnLineHeight  = ImplDevicePixelToLogicHeight( pMetric->mnAscent+pMetric->mnDescent+mnEmphasisAscent+mnEmphasisDescent );
7333     aMetric.mpImplMetric->mnSlant       = ImplDevicePixelToLogicHeight( pMetric->mnSlant );
7334 
7335 #ifdef UNX
7336     // backwards compatible line metrics after fixing #i60945#
7337     if( (meOutDevType == OUTDEV_VIRDEV)
7338     &&  static_cast<const VirtualDevice*>(this)->ForceZeroExtleadBug() )
7339         aMetric.mpImplMetric->mnExtLeading = 0;
7340 #endif
7341 
7342     return aMetric;
7343 }
7344 
7345 // -----------------------------------------------------------------------
7346 
7347 FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const
7348 {
7349     // select font, query metrics, select original font again
7350     Font aOldFont = GetFont();
7351     const_cast<OutputDevice*>(this)->SetFont( rFont );
7352     FontMetric aMetric( GetFontMetric() );
7353     const_cast<OutputDevice*>(this)->SetFont( aOldFont );
7354     return aMetric;
7355 }
7356 
7357 // -----------------------------------------------------------------------
7358 
7359 /** OutputDevice::GetSysFontData
7360  *
7361  * @param nFallbacklevel Fallback font level (0 = best matching font)
7362  *
7363  * Retrieve detailed font information in platform independent structure
7364  *
7365  * @return SystemFontData
7366  **/
7367 SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const
7368 {
7369     SystemFontData aSysFontData;
7370     aSysFontData.nSize = sizeof(aSysFontData);
7371 
7372     if (!mpGraphics) ImplGetGraphics();
7373     if (mpGraphics) aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel);
7374 
7375     return aSysFontData;
7376 }
7377 
7378 
7379 // -----------------------------------------------------------------------
7380 
7381 /** OutputDevice::GetSysTextLayoutData
7382  *
7383  * @param rStartPt Start point of the text
7384  * @param rStr Text string that will be transformed into layout of glyphs
7385  * @param nIndex Position in the string from where layout will be done
7386  * @param nLen Length of the string
7387  * @param pDXAry Custom layout adjustment data
7388  *
7389  * Export finalized glyph layout data as platform independent SystemTextLayoutData
7390  * (see vcl/inc/vcl/sysdata.hxx)
7391  *
7392  * Only parameters rStartPt and rStr are mandatory, the rest is optional
7393  * (default values will be used)
7394  *
7395  * @return SystemTextLayoutData
7396  **/
7397 SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, const XubString& rStr, xub_StrLen nIndex, xub_StrLen nLen,
7398                                                         const sal_Int32* pDXAry) const
7399 {
7400     DBG_TRACE( "OutputDevice::GetSysTextLayoutData()" );
7401 	DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7402 
7403     SystemTextLayoutData aSysLayoutData;
7404     aSysLayoutData.nSize = sizeof(aSysLayoutData);
7405     aSysLayoutData.rGlyphData.reserve( 256 );
7406 
7407 	if ( mpMetaFile ) {
7408         if (pDXAry)
7409             mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
7410         else
7411             mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
7412 	}
7413 
7414 	if ( !IsDeviceOutputNecessary() ) return aSysLayoutData;
7415 
7416 	SalLayout* rLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true );
7417 
7418     // setup glyphs
7419     Point aPos;
7420     sal_GlyphId aGlyphId;
7421     for( int nStart = 0; rLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); )
7422     {
7423         // NOTE: Windows backend is producing unicode chars (ucs4), so on windows,
7424         //       ETO_GLYPH_INDEX is unusable, unless extra glyph conversion is made.
7425 
7426         SystemGlyphData aGlyph;
7427         aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK);
7428         aGlyph.x = aPos.X();
7429         aGlyph.y = aPos.Y();
7430         int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
7431         aGlyph.fallbacklevel = nLevel < MAX_FALLBACK ? nLevel : 0;
7432         aSysLayoutData.rGlyphData.push_back(aGlyph);
7433     }
7434 
7435     // Get font data
7436     aSysLayoutData.orientation = rLayout->GetOrientation();
7437 
7438     rLayout->Release();
7439 
7440     return aSysLayoutData;
7441 }
7442 
7443 // -----------------------------------------------------------------------
7444 
7445 
7446 long OutputDevice::GetMinKashida() const
7447 {
7448     DBG_TRACE( "OutputDevice::GetMinKashida()" );
7449     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7450     if( mbNewFont && !ImplNewFont() )
7451         return 0;
7452 
7453     ImplFontEntry*      pEntry = mpFontEntry;
7454     ImplFontMetricData* pMetric = &(pEntry->maMetric);
7455     return ImplDevicePixelToLogicWidth( pMetric->mnMinKashida );
7456 }
7457 // -----------------------------------------------------------------------
7458 
7459 long OutputDevice::GetMinKashida( const Font& rFont ) const
7460 {
7461     // select font, query Kashida, select original font again
7462     Font aOldFont = GetFont();
7463     const_cast<OutputDevice*>(this)->SetFont( rFont );
7464     long aKashida = GetMinKashida();
7465     const_cast<OutputDevice*>(this)->SetFont( aOldFont );
7466     return aKashida;
7467 }
7468 
7469 // -----------------------------------------------------------------------
7470 xub_StrLen OutputDevice::ValidateKashidas ( const String& rTxt,
7471 											xub_StrLen nIdx, xub_StrLen nLen,
7472 											xub_StrLen nKashCount,
7473 											const xub_StrLen* pKashidaPos,
7474 											xub_StrLen* pKashidaPosDropped ) const
7475 {
7476    // do layout
7477     SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen );
7478     if( !pSalLayout )
7479         return 0;
7480 	xub_StrLen nDropped = 0;
7481 	for( int i = 0; i < nKashCount; ++i )
7482 	{
7483 		if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] ))
7484 		{
7485 			pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ];
7486 			++nDropped;
7487 		}
7488 	}
7489 	pSalLayout->Release();
7490 	return nDropped;
7491 }
7492 
7493 // -----------------------------------------------------------------------
7494 
7495 sal_Bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const String& rStr,
7496     int nIndex, int nLen, int nBase, MetricVector& rVector )
7497 {
7498     DBG_TRACE( "OutputDevice::GetGlyphBoundRect_CTL()" );
7499     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7500 
7501     rVector.clear();
7502 
7503     if( nLen == STRING_LEN )
7504         nLen = rStr.Len() - nIndex;
7505 
7506     Rectangle aRect;
7507     for( int i = 0; i < nLen; i++ )
7508     {
7509         if( !GetTextBoundRect( aRect, rStr, sal::static_int_cast<xub_StrLen>(nBase), sal::static_int_cast<xub_StrLen>(nIndex+i), 1 ) )
7510             break;
7511         aRect.Move( rOrigin.X(), rOrigin.Y() );
7512         rVector.push_back( aRect );
7513     }
7514 
7515     return (nLen == (int)rVector.size());
7516 }
7517 
7518 // -----------------------------------------------------------------------
7519 
7520 sal_Bool OutputDevice::GetTextBoundRect( Rectangle& rRect,
7521     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
7522 	sal_uLong nLayoutWidth, const sal_Int32* pDXAry ) const
7523 {
7524     DBG_TRACE( "OutputDevice::GetTextBoundRect()" );
7525     DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7526 
7527     sal_Bool bRet = sal_False;
7528     rRect.SetEmpty();
7529 
7530     SalLayout* pSalLayout = NULL;
7531 	const Point aPoint;
7532     // calculate offset when nBase!=nIndex
7533     long nXOffset = 0;
7534     if( nBase != nIndex )
7535     {
7536         xub_StrLen nStart = Min( nBase, nIndex );
7537         xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
7538         pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry );
7539         if( pSalLayout )
7540         {
7541             nXOffset = pSalLayout->GetTextWidth();
7542             nXOffset /= pSalLayout->GetUnitsPerPixel();
7543             pSalLayout->Release();
7544             // TODO: fix offset calculation for Bidi case
7545             if( nBase < nIndex)
7546                 nXOffset = -nXOffset;
7547         }
7548     }
7549 
7550     pSalLayout = ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
7551     Rectangle aPixelRect;
7552     if( pSalLayout )
7553     {
7554         bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect );
7555 
7556         if( bRet )
7557         {
7558             int nWidthFactor = pSalLayout->GetUnitsPerPixel();
7559 
7560             if( nWidthFactor > 1 )
7561             {
7562                 double fFactor = 1.0 / nWidthFactor;
7563                 aPixelRect.Left()
7564                     = static_cast< long >(aPixelRect.Left() * fFactor);
7565                 aPixelRect.Right()
7566                     = static_cast< long >(aPixelRect.Right() * fFactor);
7567                 aPixelRect.Top()
7568                     = static_cast< long >(aPixelRect.Top() * fFactor);
7569                 aPixelRect.Bottom()
7570                     = static_cast< long >(aPixelRect.Bottom() * fFactor);
7571             }
7572 
7573             Point aRotatedOfs( mnTextOffX, mnTextOffY );
7574             aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
7575             aPixelRect += aRotatedOfs;
7576             rRect = PixelToLogic( aPixelRect );
7577             if( mbMap )
7578                 rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
7579         }
7580 
7581         pSalLayout->Release();
7582     }
7583 
7584     if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
7585         return bRet;
7586 
7587     // fall back to bitmap method to get the bounding rectangle,
7588     // so we need a monochrome virtual device with matching font
7589     VirtualDevice aVDev( 1 );
7590     Font aFont( GetFont() );
7591     aFont.SetShadow( sal_False );
7592     aFont.SetOutline( sal_False );
7593     aFont.SetRelief( RELIEF_NONE );
7594     aFont.SetOrientation( 0 );
7595     aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
7596     aVDev.SetFont( aFont );
7597     aVDev.SetTextAlign( ALIGN_TOP );
7598 
7599     // layout the text on the virtual device
7600     pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
7601     if( !pSalLayout )
7602         return false;
7603 
7604     // make the bitmap big enough
7605     // TODO: use factors when it would get too big
7606     long nWidth = pSalLayout->GetTextWidth();
7607     long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
7608     Point aOffset( nWidth/2, 8 );
7609     Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() );
7610     if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) )
7611         return false;
7612 
7613     // draw text in black
7614     pSalLayout->DrawBase() = aOffset;
7615     aVDev.SetTextColor( Color( COL_BLACK ) );
7616     aVDev.SetTextFillColor();
7617     aVDev.ImplInitTextColor();
7618     aVDev.ImplDrawText( *pSalLayout );
7619     pSalLayout->Release();
7620 
7621     // find extents using the bitmap
7622     Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize );
7623     BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
7624     if( !pAcc )
7625         return sal_False;
7626     const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
7627     const long nW = pAcc->Width();
7628     const long nH = pAcc->Height();
7629     long nLeft = 0;
7630     long nRight = 0;
7631 
7632     // find top left point
7633     long nTop = 0;
7634     for(; nTop < nH; ++nTop )
7635     {
7636         for( nLeft = 0; nLeft < nW; ++nLeft )
7637             if( pAcc->GetPixel( nTop, nLeft ) == aBlack )
7638                 break;
7639         if( nLeft < nW )
7640             break;
7641     }
7642 
7643     // find bottom right point
7644     long nBottom = nH;
7645     while( --nBottom >= nTop )
7646     {
7647         for( nRight = nW; --nRight >= 0; )
7648             if( pAcc->GetPixel( nBottom, nRight ) == aBlack )
7649                 break;
7650         if( nRight >= 0 )
7651             break;
7652     }
7653     if( nRight < nLeft )
7654     {
7655         long nX = nRight;
7656         nRight = nLeft;
7657         nLeft  = nX;
7658     }
7659 
7660     for( long nY = nTop; nY <= nBottom; ++nY )
7661     {
7662         // find leftmost point
7663         long nX;
7664         for( nX = 0; nX < nLeft; ++nX )
7665             if( pAcc->GetPixel( nY, nX ) == aBlack )
7666                 break;
7667         nLeft = nX;
7668 
7669         // find rightmost point
7670         for( nX = nW; --nX > nRight; )
7671             if( pAcc->GetPixel( nY, nX ) == aBlack )
7672                 break;
7673         nRight = nX;
7674     }
7675 
7676     aBmp.ReleaseAccess( pAcc );
7677 
7678     if( nTop <= nBottom )
7679     {
7680         Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 );
7681         Point aTopLeft( nLeft, nTop );
7682         aTopLeft -= aOffset;
7683         // adjust to text alignment
7684         aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent);
7685         // convert to logical coordinates
7686         aSize = PixelToLogic( aSize );
7687         aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() );
7688         aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() );
7689         rRect = Rectangle( aTopLeft, aSize );
7690         return sal_True;
7691     }
7692 
7693     return sal_False;
7694 }
7695 
7696 // -----------------------------------------------------------------------
7697 
7698 sal_Bool OutputDevice::GetTextOutlines( ::basegfx::B2DPolyPolygonVector& rVector,
7699     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
7700     sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const
7701 {
7702     // the fonts need to be initialized
7703     if( mbNewFont )
7704         ImplNewFont();
7705     if( mbInitFont )
7706         ImplInitFont();
7707     if( !mpFontEntry )
7708         return sal_False;
7709 
7710     sal_Bool bRet = sal_False;
7711     rVector.clear();
7712     if( nLen == STRING_LEN )
7713         nLen = rStr.Len() - nIndex;
7714     rVector.reserve( nLen );
7715 
7716     // we want to get the Rectangle in logical units, so to
7717     // avoid rounding errors we just size the font in logical units
7718     sal_Bool bOldMap = mbMap;
7719     if( bOldMap )
7720     {
7721         const_cast<OutputDevice&>(*this).mbMap = sal_False;
7722         const_cast<OutputDevice&>(*this).mbNewFont = sal_True;
7723     }
7724 
7725     SalLayout* pSalLayout = NULL;
7726 
7727     // calculate offset when nBase!=nIndex
7728     long nXOffset = 0;
7729     if( nBase != nIndex )
7730     {
7731         xub_StrLen nStart = Min( nBase, nIndex );
7732         xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
7733         pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nTWidth, pDXArray );
7734         if( pSalLayout )
7735         {
7736             nXOffset = pSalLayout->GetTextWidth();
7737             pSalLayout->Release();
7738             // TODO: fix offset calculation for Bidi case
7739             if( nBase > nIndex)
7740                 nXOffset = -nXOffset;
7741         }
7742     }
7743 
7744     pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
7745     if( pSalLayout )
7746     {
7747         bRet = pSalLayout->GetOutline( *mpGraphics, rVector );
7748         if( bRet )
7749         {
7750             // transform polygon to pixel units
7751             ::basegfx::B2DHomMatrix aMatrix;
7752 
7753             int nWidthFactor = pSalLayout->GetUnitsPerPixel();
7754             if( nXOffset | mnTextOffX | mnTextOffY )
7755             {
7756                 Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor );
7757                 aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
7758                 aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() );
7759             }
7760 
7761             if( nWidthFactor > 1 )
7762             {
7763                 double fFactor = 1.0 / nWidthFactor;
7764                 aMatrix.scale( fFactor, fFactor );
7765             }
7766 
7767             if( !aMatrix.isIdentity() )
7768             {
7769                 ::basegfx::B2DPolyPolygonVector::iterator aIt = rVector.begin();
7770                 for(; aIt != rVector.end(); ++aIt )
7771                     (*aIt).transform( aMatrix );
7772             }
7773         }
7774 
7775         pSalLayout->Release();
7776     }
7777 
7778     if( bOldMap )
7779     {
7780         // restore original font size and map mode
7781         const_cast<OutputDevice&>(*this).mbMap = bOldMap;
7782         const_cast<OutputDevice&>(*this).mbNewFont = sal_True;
7783     }
7784 
7785     if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
7786         return bRet;
7787 
7788     // fall back to bitmap conversion ------------------------------------------
7789 
7790     // Here, we can savely assume that the mapping between characters and glyphs
7791     // is one-to-one. This is most probably valid for the old bitmap fonts.
7792 
7793     // fall back to bitmap method to get the bounding rectangle,
7794     // so we need a monochrome virtual device with matching font
7795     pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
7796     if (pSalLayout == 0)
7797         return false;
7798     long nOrgWidth = pSalLayout->GetTextWidth();
7799     long nOrgHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent
7800         + mnEmphasisDescent;
7801     pSalLayout->Release();
7802 
7803     VirtualDevice aVDev(1);
7804 
7805     Font aFont(GetFont());
7806     aFont.SetShadow(false);
7807     aFont.SetOutline(false);
7808     aFont.SetRelief(RELIEF_NONE);
7809     aFont.SetOrientation(0);
7810     if( bOptimize )
7811     {
7812         aFont.SetSize( Size( 0, GLYPH_FONT_HEIGHT ) );
7813         aVDev.SetMapMode( MAP_PIXEL );
7814     }
7815     aVDev.SetFont( aFont );
7816     aVDev.SetTextAlign( ALIGN_TOP );
7817     aVDev.SetTextColor( Color(COL_BLACK) );
7818     aVDev.SetTextFillColor();
7819 
7820     pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
7821     if (pSalLayout == 0)
7822         return false;
7823     long nWidth = pSalLayout->GetTextWidth();
7824     long nHeight = ((OutputDevice*)&aVDev)->mpFontEntry->mnLineHeight + ((OutputDevice*)&aVDev)->mnEmphasisAscent
7825         + ((OutputDevice*)&aVDev)->mnEmphasisDescent;
7826     pSalLayout->Release();
7827 
7828     if( !nWidth || !nHeight )
7829         return sal_True;
7830     double fScaleX = static_cast< double >(nOrgWidth) / nWidth;
7831     double fScaleY = static_cast< double >(nOrgHeight) / nHeight;
7832 
7833     // calculate offset when nBase!=nIndex
7834     // TODO: fix offset calculation for Bidi case
7835     nXOffset = 0;
7836     if( nBase != nIndex )
7837     {
7838         xub_StrLen nStart  = ((nBase < nIndex) ? nBase : nIndex);
7839         xub_StrLen nLength = ((nBase > nIndex) ? nBase : nIndex) - nStart;
7840         pSalLayout = aVDev.ImplLayout( rStr, nStart, nLength, Point(0,0), nTWidth, pDXArray );
7841         if( pSalLayout )
7842         {
7843             nXOffset = pSalLayout->GetTextWidth();
7844             pSalLayout->Release();
7845             if( nBase > nIndex)
7846                 nXOffset = -nXOffset;
7847         }
7848     }
7849 
7850     bRet = true;
7851     bool bRTL = false;
7852     String aStr( rStr ); // prepare for e.g. localized digits
7853     ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
7854     for( int nCharPos = -1; aLayoutArgs.GetNextPos( &nCharPos, &bRTL);)
7855     {
7856         bool bSuccess = false;
7857 
7858         // draw character into virtual device
7859         pSalLayout = aVDev.ImplLayout( rStr, static_cast< xub_StrLen >(nCharPos), 1, Point(0,0), nTWidth, pDXArray );
7860         if (pSalLayout == 0)
7861             return false;
7862         long nCharWidth = pSalLayout->GetTextWidth();
7863 
7864         Point aOffset(nCharWidth / 2, 8);
7865         Size aSize(nCharWidth + 2 * aOffset.X(), nHeight + 2 * aOffset.Y());
7866         bSuccess = (bool)aVDev.SetOutputSizePixel(aSize);
7867         if( bSuccess )
7868         {
7869             // draw glyph into virtual device
7870             aVDev.Erase();
7871             pSalLayout->DrawBase() += aOffset;
7872             pSalLayout->DrawBase() += Point( ((OutputDevice*)&aVDev)->mnTextOffX, ((OutputDevice*)&aVDev)->mnTextOffY );
7873             pSalLayout->DrawText( *((OutputDevice*)&aVDev)->mpGraphics );
7874             pSalLayout->Release();
7875 
7876             // convert character image into outline
7877             Bitmap aBmp( aVDev.GetBitmap(Point(0, 0), aSize));
7878 
7879             PolyPolygon aPolyPoly;
7880             bool bVectorized = aBmp.Vectorize(aPolyPoly, BMP_VECTORIZE_OUTER | BMP_VECTORIZE_REDUCE_EDGES);
7881             if( !bVectorized )
7882                 bSuccess = false;
7883             else
7884             {
7885                 // convert units to logical width
7886                 for (sal_uInt16 j = 0; j < aPolyPoly.Count(); ++j)
7887                 {
7888                     Polygon& rPoly = aPolyPoly[j];
7889                     for (sal_uInt16 k = 0; k < rPoly.GetSize(); ++k)
7890                     {
7891                         Point& rPt = rPoly[k];
7892                         rPt -= aOffset;
7893                         int nPixelX = rPt.X() - ((OutputDevice&)aVDev).mnTextOffX + nXOffset;
7894                         int nPixelY = rPt.Y() - ((OutputDevice&)aVDev).mnTextOffY;
7895                         rPt.X() = ImplDevicePixelToLogicWidth( nPixelX );
7896                         rPt.Y() = ImplDevicePixelToLogicHeight( nPixelY );
7897                     }
7898                 }
7899 
7900 
7901                 // ignore "empty" glyphs:
7902                 if( aPolyPoly.Count() > 0 )
7903                 {
7904                     // convert	to B2DPolyPolygon
7905                     // TODO: get rid of intermediate tool's PolyPolygon
7906                     ::basegfx::B2DPolyPolygon aB2DPolyPoly = aPolyPoly.getB2DPolyPolygon();
7907                     ::basegfx::B2DHomMatrix aMatrix;
7908                     aMatrix.scale( fScaleX, fScaleY );
7909                     int nAngle = GetFont().GetOrientation();
7910                     if( nAngle )
7911                         aMatrix.rotate( nAngle * F_PI1800 );
7912                     aB2DPolyPoly.transform( aMatrix );
7913                     rVector.push_back( aB2DPolyPoly );
7914                 }
7915             }
7916         }
7917 
7918         nXOffset += nCharWidth;
7919         bRet = bRet && bSuccess;
7920     }
7921 
7922     return bRet;
7923 }
7924 
7925 // -----------------------------------------------------------------------
7926 
7927 sal_Bool OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector,
7928     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex,
7929     xub_StrLen nLen, sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const
7930 {
7931     rResultVector.clear();
7932 
7933     // get the basegfx polypolygon vector
7934     ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
7935     if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
7936                          bOptimize, nTWidth, pDXArray ) )
7937     return sal_False;
7938 
7939     // convert to a tool polypolygon vector
7940     rResultVector.reserve( aB2DPolyPolyVector.size() );
7941     ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
7942     for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
7943         rResultVector.push_back(PolyPolygon(*aIt)); // #i76339#
7944 
7945     return sal_True;
7946 }
7947 
7948 // -----------------------------------------------------------------------
7949 
7950 sal_Bool OutputDevice::GetTextOutline( PolyPolygon& rPolyPoly,
7951     const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
7952     sal_Bool bOptimize, sal_uLong nTWidth, const sal_Int32* pDXArray ) const
7953 {
7954     rPolyPoly.Clear();
7955 
7956     // get the basegfx polypolygon vector
7957     ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
7958     if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
7959                          bOptimize, nTWidth, pDXArray ) )
7960     return sal_False;
7961 
7962     // convert and merge into a tool polypolygon
7963     ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
7964     for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
7965         for( unsigned int i = 0; i < aIt->count(); ++i )
7966             rPolyPoly.Insert(Polygon((*aIt).getB2DPolygon( i ))); // #i76339#
7967 
7968     return sal_True;
7969 }
7970 
7971 // -----------------------------------------------------------------------
7972 
7973 sal_Bool OutputDevice::GetFontCharMap( FontCharMap& rFontCharMap ) const
7974 {
7975     rFontCharMap.Reset();
7976 
7977     // we need a graphics
7978     if( !mpGraphics && !ImplGetGraphics() )
7979         return sal_False;
7980 
7981     if( mbNewFont )
7982         ImplNewFont();
7983     if( mbInitFont )
7984         ImplInitFont();
7985     if( !mpFontEntry )
7986         return sal_False;
7987 
7988 #ifdef ENABLE_IFC_CACHE    // a little font charmap cache helps considerably
7989     static const int NMAXITEMS = 16;
7990     static int nUsedItems = 0, nCurItem = 0;
7991 
7992     struct CharMapCacheItem { const ImplFontData* mpFontData; FontCharMap maCharMap; };
7993     static CharMapCacheItem aCache[ NMAXITEMS ];
7994 
7995     const ImplFontData* pFontData = mpFontEntry->maFontSelData.mpFontData;
7996 
7997     int i;
7998     for( i = nUsedItems; --i >= 0; )
7999         if( pFontData == aCache[i].mpFontData )
8000             break;
8001     if( i >= 0 )	// found in cache
8002     {
8003         rFontCharMap.Reset( aCache[i].maCharMap.mpImpl );
8004     }
8005     else            // need to cache
8006 #endif // ENABLE_IFC_CACHE
8007     {
8008         const ImplFontCharMap* pNewMap = mpGraphics->GetImplFontCharMap();
8009         rFontCharMap.Reset( pNewMap );
8010 
8011 #ifdef ENABLE_IFC_CACHE
8012         // manage cache round-robin and insert data
8013         CharMapCacheItem& rItem = aCache[ nCurItem ];
8014         rItem.mpFontData = pFontData;
8015         rItem.maCharMap.Reset( pNewMap );
8016 
8017         if( ++nCurItem >= NMAXITEMS )
8018             nCurItem = 0;
8019 
8020         if( ++nUsedItems >= NMAXITEMS )
8021             nUsedItems = NMAXITEMS;
8022 #endif // ENABLE_IFC_CACHE
8023     }
8024 
8025     if( rFontCharMap.IsDefaultMap() )
8026         return sal_False;
8027     return sal_True;
8028 }
8029 
8030 // -----------------------------------------------------------------------
8031 
8032 xub_StrLen OutputDevice::HasGlyphs( const Font& rTempFont, const String& rStr,
8033     xub_StrLen nIndex, xub_StrLen nLen ) const
8034 {
8035     if( nIndex >= rStr.Len() )
8036         return nIndex;
8037     xub_StrLen nEnd = nIndex + nLen;
8038     if( (sal_uLong)nIndex+nLen > rStr.Len() )
8039         nEnd = rStr.Len();
8040 
8041     DBG_ASSERT( nIndex < nEnd, "StartPos >= EndPos?" );
8042     DBG_ASSERT( nEnd <= rStr.Len(), "String too short" );
8043 
8044     // to get the map temporarily set font
8045     const Font aOrigFont = GetFont();
8046     const_cast<OutputDevice&>(*this).SetFont( rTempFont );
8047     FontCharMap aFontCharMap;
8048     sal_Bool bRet = GetFontCharMap( aFontCharMap );
8049     const_cast<OutputDevice&>(*this).SetFont( aOrigFont );
8050 
8051     // if fontmap is unknown assume it doesn't have the glyphs
8052     if( bRet == sal_False )
8053         return nIndex;
8054 
8055     const sal_Unicode* pStr = rStr.GetBuffer();
8056     for( pStr += nIndex; nIndex < nEnd; ++pStr, ++nIndex )
8057         if( ! aFontCharMap.HasChar( *pStr ) )
8058             return nIndex;
8059 
8060     return STRING_LEN;
8061 }
8062 
8063 // -----------------------------------------------------------------------
8064