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