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