merge the formfield patch from ooo-build
[ooovba.git] / vcl / source / gdi / outdev3.cxx
blob3bd6031ac868d506c0ff0973d7fcf50222904665
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 #include <cstring>
31 #include <i18npool/mslangid.hxx>
33 #ifndef _SV_SVSYS_HXX
34 #include <svsys.h>
35 #endif
36 #include <vcl/salgdi.hxx>
37 #include <vcl/sallayout.hxx>
38 #include <rtl/tencinfo.h>
39 #include <tools/debug.hxx>
40 #include <vcl/svdata.hxx>
41 #include <vcl/metric.hxx>
42 #include <vcl/impfont.hxx>
43 #include <vcl/metaact.hxx>
44 #include <vcl/gdimtf.hxx>
45 #include <vcl/outdata.hxx>
46 #include <vcl/outfont.hxx>
47 #include <basegfx/polygon/b2dpolygon.hxx>
48 #include <basegfx/polygon/b2dpolypolygon.hxx>
49 #include <basegfx/matrix/b2dhommatrix.hxx>
50 #include <tools/poly.hxx>
51 #include <vcl/outdev.h>
52 #include <vcl/virdev.hxx>
53 #include <vcl/print.hxx>
54 #include <vcl/event.hxx>
55 #include <vcl/window.h>
56 #include <vcl/window.hxx>
57 #include <vcl/svapp.hxx>
58 #include <vcl/bmpacc.hxx>
59 #include <vcl/fontcvt.hxx>
60 #include <vcl/outdev.hxx>
61 #include <vcl/edit.hxx>
62 #include <vcl/fontcfg.hxx>
63 #include <vcl/sysdata.hxx>
64 #ifndef _OSL_FILE_H
65 #include <osl/file.h>
66 #endif
67 #ifdef ENABLE_GRAPHITE
68 #include <vcl/graphite_features.hxx>
69 #endif
71 #include <vcl/unohelp.hxx>
72 #include <pdfwriter_impl.hxx>
73 #include <vcl/controllayout.hxx>
74 #include <rtl/logfile.hxx>
76 #ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUES_HDL_
77 #include <com/sun/star/beans/PropertyValues.hdl>
78 #endif
79 #include <com/sun/star/i18n/XBreakIterator.hpp>
80 #include <com/sun/star/i18n/WordType.hpp>
81 #include <com/sun/star/linguistic2/XLinguServiceManager.hpp>
83 #if defined UNX
84 #define GLYPH_FONT_HEIGHT 128
85 #elif defined OS2
86 #define GLYPH_FONT_HEIGHT 176
87 #else
88 #define GLYPH_FONT_HEIGHT 256
89 #endif
91 #include <sal/alloca.h>
93 #include <cmath>
94 #include <cstring>
96 #include <memory>
97 #include <algorithm>
99 // =======================================================================
101 DBG_NAMEEX( OutputDevice )
102 DBG_NAMEEX( Font )
104 // =======================================================================
106 using namespace ::com::sun::star;
107 using namespace ::com::sun::star::uno;
108 using namespace ::rtl;
109 using namespace ::vcl;
111 // =======================================================================
113 #define TEXT_DRAW_ELLIPSIS (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS)
115 // =======================================================================
117 #define UNDERLINE_LAST UNDERLINE_BOLDWAVE
118 #define STRIKEOUT_LAST STRIKEOUT_X
120 // =======================================================================
122 static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY,
123 int nOrientation )
125 if ( (nOrientation >= 0) && !(nOrientation % 900) )
127 if ( (nOrientation >= 3600) )
128 nOrientation %= 3600;
130 if ( nOrientation )
132 rX -= nOriginX;
133 rY -= nOriginY;
135 if ( nOrientation == 900 )
137 long nTemp = rX;
138 rX = rY;
139 rY = -nTemp;
141 else if ( nOrientation == 1800 )
143 rX = -rX;
144 rY = -rY;
146 else /* ( nOrientation == 2700 ) */
148 long nTemp = rX;
149 rX = -rY;
150 rY = nTemp;
153 rX += nOriginX;
154 rY += nOriginY;
157 else
159 double nRealOrientation = nOrientation*F_PI1800;
160 double nCos = cos( nRealOrientation );
161 double nSin = sin( nRealOrientation );
163 // Translation...
164 long nX = rX-nOriginX;
165 long nY = rY-nOriginY;
167 // Rotation...
168 rX = +((long)(nCos*nX + nSin*nY)) + nOriginX;
169 rY = -((long)(nSin*nX - nCos*nY)) + nOriginY;
173 // =======================================================================
175 void OutputDevice::ImplUpdateFontData( bool bNewFontLists )
177 // the currently selected logical font is no longer needed
178 if ( mpFontEntry )
180 mpFontCache->Release( mpFontEntry );
181 mpFontEntry = NULL;
184 mbInitFont = true;
185 mbNewFont = true;
187 if ( bNewFontLists )
189 if ( mpGetDevFontList )
191 delete mpGetDevFontList;
192 mpGetDevFontList = NULL;
194 if ( mpGetDevSizeList )
196 delete mpGetDevSizeList;
197 mpGetDevSizeList = NULL;
200 // release all physically selected fonts on this device
201 if( ImplGetGraphics() )
202 mpGraphics->ReleaseFonts();
205 if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter )
207 ImplSVData* pSVData = ImplGetSVData();
209 if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
210 mpFontCache->Invalidate();
212 if ( bNewFontLists )
214 // we need a graphics
215 if ( ImplGetGraphics() )
217 if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList )
218 mpFontList->Clear();
220 if( mpPDFWriter )
222 if( mpFontList && mpFontList != pSVData->maGDIData.mpScreenFontList )
223 delete mpFontList;
224 if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
225 delete mpFontCache;
226 mpFontList = mpPDFWriter->filterDevFontList( pSVData->maGDIData.mpScreenFontList );
227 mpFontCache = new ImplFontCache( FALSE );
229 else
231 if( mpOutDevData )
232 mpOutDevData->maDevFontSubst.Clear();
233 mpGraphics->GetDevFontList( mpFontList );
234 mpGraphics->GetDevFontSubstList( this );
240 // also update child windows if needed
241 if ( GetOutDevType() == OUTDEV_WINDOW )
243 Window* pChild = ((Window*)this)->mpWindowImpl->mpFirstChild;
244 while ( pChild )
246 pChild->ImplUpdateFontData( true );
247 pChild = pChild->mpWindowImpl->mpNext;
252 // -----------------------------------------------------------------------
254 void OutputDevice::ImplUpdateAllFontData( bool bNewFontLists )
256 ImplSVData* pSVData = ImplGetSVData();
258 // update all windows
259 Window* pFrame = pSVData->maWinData.mpFirstFrame;
260 while ( pFrame )
262 pFrame->ImplUpdateFontData( bNewFontLists );
264 Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap;
265 while ( pSysWin )
267 pSysWin->ImplUpdateFontData( bNewFontLists );
268 pSysWin = pSysWin->mpWindowImpl->mpNextOverlap;
271 pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
274 // update all virtual devices
275 VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev;
276 while ( pVirDev )
278 pVirDev->ImplUpdateFontData( bNewFontLists );
279 pVirDev = pVirDev->mpNext;
282 // update all printers
283 Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter;
284 while ( pPrinter )
286 pPrinter->ImplUpdateFontData( bNewFontLists );
287 pPrinter = pPrinter->mpNext;
290 // clear global font lists to have them updated
291 pSVData->maGDIData.mpScreenFontCache->Invalidate();
292 if ( bNewFontLists )
294 pSVData->maGDIData.mpScreenFontList->Clear();
295 pFrame = pSVData->maWinData.mpFirstFrame;
296 if ( pFrame )
298 if ( pFrame->ImplGetGraphics() )
299 // MT: Stupid typecast here and somewhere ((OutputDevice*)&aVDev)->, because bug in .NET2002 compiler.
300 ((OutputDevice*)pFrame)->mpGraphics->GetDevFontList( pFrame->mpWindowImpl->mpFrameData->mpFontList );
305 // =======================================================================
307 struct ImplLocalizedFontName
309 const char* mpEnglishName;
310 const sal_Unicode* mpLocalizedNames;
313 static sal_Unicode const aBatang[] = { 0xBC14, 0xD0D5, 0, 0 };
314 static sal_Unicode const aBatangChe[] = { 0xBC14, 0xD0D5, 0xCCB4, 0, 0 };
315 static sal_Unicode const aGungsuh[] = { 0xAD81, 0xC11C, 0, 0 };
316 static sal_Unicode const aGungsuhChe[] = { 0xAD81, 0xC11C, 0xCCB4, 0, 0 };
317 static sal_Unicode const aGulim[] = { 0xAD74, 0xB9BC, 0, 0 };
318 static sal_Unicode const aGulimChe[] = { 0xAD74, 0xB9BC, 0xCCB4, 0, 0 };
319 static sal_Unicode const aDotum[] = { 0xB3CB, 0xC6C0, 0, 0 };
320 static sal_Unicode const aDotumChe[] = { 0xB3CB, 0xC6C0, 0xCCB4, 0, 0 };
321 static sal_Unicode const aSimSun[] = { 0x5B8B, 0x4F53, 0, 0 };
322 static sal_Unicode const aNSimSun[] = { 0x65B0, 0x5B8B, 0x4F53, 0, 0 };
323 static sal_Unicode const aSimHei[] = { 0x9ED1, 0x4F53, 0, 0 };
324 static sal_Unicode const aSimKai[] = { 0x6977, 0x4F53, 0, 0 };
325 static sal_Unicode const azycjkSun[] = { 0x4E2D, 0x6613, 0x5B8B, 0x4F53, 0, 0 };
326 static sal_Unicode const azycjkHei[] = { 0x4E2D, 0x6613, 0x9ED1, 0x4F53, 0, 0 };
327 static sal_Unicode const azycjkKai[] = { 0x4E2D, 0x6613, 0x6977, 0x4F53, 0, 0 };
328 static sal_Unicode const aFZHei[] = { 0x65B9, 0x6B63, 0x9ED1, 0x4F53, 0, 0 };
329 static sal_Unicode const aFZKai[] = { 0x65B9, 0x6B63, 0x6977, 0x4F53, 0, 0 };
330 static sal_Unicode const aFZSongYI[] = { 0x65B9, 0x6B63, 0x5B8B, 0x4E00, 0, 0 };
331 static sal_Unicode const aFZShuSong[] = { 0x65B9, 0x6B63, 0x4E66, 0x5B8B, 0, 0 };
332 static sal_Unicode const aFZFangSong[] = { 0x65B9, 0x6B63, 0x4EFF, 0x5B8B, 0, 0 };
333 // Attention: this fonts includes the wrong encoding vector - so we double the names with correct and wrong encoding
334 // First one is the GB-Encoding (we think the correct one), second is the big5 encoded name
335 static sal_Unicode const aMHei[] = { 'm', 0x7B80, 0x9ED1, 0, 'm', 0x6F60, 0x7AAA, 0, 0 };
336 static sal_Unicode const aMKai[] = { 'm', 0x7B80, 0x6977, 0x566C, 0, 'm', 0x6F60, 0x7FF1, 0x628E, 0, 0 };
337 static sal_Unicode const aMSong[] = { 'm', 0x7B80, 0x5B8B, 0, 'm', 0x6F60, 0x51BC, 0, 0 };
338 static sal_Unicode const aCFangSong[] = { 'm', 0x7B80, 0x592B, 0x5B8B, 0, 'm', 0x6F60, 0x6E98, 0x51BC, 0, 0 };
339 static sal_Unicode const aMingLiU[] = { 0x7D30, 0x660E, 0x9AD4, 0, 0 };
340 static sal_Unicode const aPMingLiU[] = { 0x65B0, 0x7D30, 0x660E, 0x9AD4, 0, 0 };
341 static sal_Unicode const aHei[] = { 0x6865, 0, 0 };
342 static sal_Unicode const aKai[] = { 0x6B61, 0, 0 };
343 static sal_Unicode const aMing[] = { 0x6D69, 0x6E67, 0, 0 };
344 static sal_Unicode const aMSGothic[] = { 'm','s', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
345 static sal_Unicode const aMSPGothic[] = { 'm','s','p', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
346 static sal_Unicode const aMSMincho[] = { 'm', 's', 0x660E, 0x671D, 0 };
347 static sal_Unicode const aMSPMincho[] = { 'm','s','p', 0x660E, 0x671D, 0 };
348 static sal_Unicode const aMeiryo[] = { 0x30e1, 0x30a4, 0x30ea, 0x30aa, 0 };
349 static sal_Unicode const aHGMinchoL[] = { 'h','g', 0x660E, 0x671D, 'l', 0, 0 };
350 static sal_Unicode const aHGGothicB[] = { 'h','g', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 'b', 0 };
351 static sal_Unicode const aHGPMinchoL[] = { 'h','g','p', 0x660E, 0x671D, 'l', 0 };
352 static sal_Unicode const aHGPGothicB[] = { 'h','g','p', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 'b', 0 };
353 static sal_Unicode const aHGMinchoLSun[] = { 'h','g', 0x660E, 0x671D, 'l', 's', 'u', 'n', 0 };
354 static sal_Unicode const aHGPMinchoLSun[] = { 'h','g','p', 0x660E, 0x671D, 'l', 's', 'u', 'n', 0 };
355 static sal_Unicode const aHGGothicBSun[] = { 'h', 'g', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 'b', 's', 'u', 'n', 0 };
356 static sal_Unicode const aHGPGothicBSun[] = { 'h', 'g', 'p', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 'b', 's', 'u', 'n', 0 };
357 static sal_Unicode const aHGHeiseiMin[] = { 'h', 'g', 0x5E73, 0x6210, 0x660E, 0x671D, 0x4F53, 0, 'h', 'g', 0x5E73, 0x6210, 0x660E, 0x671D, 0x4F53, 'w', '3', 'x', '1', '2', 0, 0 };
358 static sal_Unicode const aIPAMincho[] = { 'i', 'p', 'a', 0x660E, 0x671D, 0 };
359 static sal_Unicode const aIPAPMincho[] = { 'i', 'p', 'a', 'p', 0x660E, 0x671D, 0 };
360 static sal_Unicode const aIPAGothic[] = { 'i', 'p', 'a', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
361 static sal_Unicode const aIPAPGothic[] = { 'i', 'p', 'a', 'p', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
362 static sal_Unicode const aIPAUIGothic[] = { 'i', 'p', 'a', 'u', 'i', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
363 static sal_Unicode const aSazanamiMincho[] = { 0x3055, 0x3056, 0x306A, 0x307F, 0x660E, 0x671D, 0, 0 };
364 static sal_Unicode const aSazanamiGothic[] = { 0x3055, 0x3056, 0x306A, 0x307F, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
365 static sal_Unicode const aKochiMincho[] = { 0x6771, 0x98A8, 0x660E, 0x671D, 0, 0 };
366 static sal_Unicode const aKochiGothic[] = { 0x6771, 0x98A8, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
367 static sal_Unicode const aSunDotum[] = { 0xC36C, 0xB3CB, 0xC6C0, 0, 0 };
368 static sal_Unicode const aSunGulim[] = { 0xC36C, 0xAD74, 0xB9BC, 0, 0 };
369 static sal_Unicode const aSunBatang[] = { 0xC36C, 0xBC14, 0xD0D5, 0, 0 };
370 static sal_Unicode const aBaekmukDotum[] = { 0xBC31, 0xBB35, 0xB3CB, 0xC6C0, 0, 0 };
371 static sal_Unicode const aBaekmukGulim[] = { 0xBC31, 0xBB35, 0xAD74, 0xB9BC, 0, 0 };
372 static sal_Unicode const aBaekmukBatang[] = { 0xBC31, 0xBB35, 0xBC14, 0xD0D5, 0, 0 };
373 static sal_Unicode const aFzMingTi[] = { 0x65B9, 0x6B63, 0x660E, 0x9AD4, 0, 0 };
374 static sal_Unicode const aFzHeiTiTW[]= { 0x65B9, 0x6B63, 0x9ED1, 0x9AD4, 0, 0 };
375 static sal_Unicode const aFzKaiTiTW[]= { 0x65B9, 0x6B63, 0x6977, 0x9AD4, 0, 0 };
376 static sal_Unicode const aFzHeiTiCN[]= { 0x65B9, 0x6B63, 0x9ED1, 0x4F53, 0, 0 };
377 static sal_Unicode const aFzKaiTiCN[]= { 0x65B9, 0x6B63, 0x6977, 0x4F53, 0, 0 };
378 static sal_Unicode const aFzSongTi[] = { 0x65B9, 0x6B63, 0x5B8B, 0x4F53, 0, 0 };
379 static sal_Unicode const aHYMyeongJoExtra[] = { 'h', 'y', 0xACAC, 0xBA85, 0xC870, 0, 0 };
380 static sal_Unicode const aHYSinMyeongJoMedium[] = { 'h', 'y', 0xC2E0, 0xBA85, 0xC870, 0, 0 };
381 static sal_Unicode const aHYGothicMedium[] = { 'h', 'y', 0xC911, 0xACE0, 0xB515, 0, 0 };
382 static sal_Unicode const aHYGraphicMedium[] = { 'h', 'y', 0xADF8, 0xB798, 0xD53D, 'm', 0, 0 };
383 static sal_Unicode const aHYGraphic[] = { 'h', 'y', 0xADF8, 0xB798, 0xD53D, 0, 0 };
384 static sal_Unicode const aNewGulim[] = { 0xC0C8, 0xAD74, 0xB9BC, 0, 0 };
385 static sal_Unicode const aSunGungseo[] = { 0xC36C, 0xAD81, 0xC11C, 0, 0 };
386 static sal_Unicode const aHYGungSoBold[] = { 'h','y', 0xAD81, 0xC11C, 'b', 0, 0 };
387 static sal_Unicode const aHYGungSo[] = { 'h','y', 0xAD81, 0xC11C, 0, 0 };
388 static sal_Unicode const aSunHeadLine[] = { 0xC36C, 0xD5E4, 0xB4DC, 0xB77C, 0xC778, 0, 0 };
389 static sal_Unicode const aHYHeadLineMedium[] = { 'h', 'y', 0xD5E4, 0xB4DC, 0xB77C, 0xC778, 'm', 0, 0 };
390 static sal_Unicode const aHYHeadLine[] = { 'h', 'y', 0xD5E4, 0xB4DC, 0xB77C, 0xC778, 0, 0 };
391 static sal_Unicode const aYetR[] = { 0xD734, 0xBA3C, 0xC61B, 0xCCB4, 0, 0 };
392 static sal_Unicode const aHYGothicExtra[] = { 'h', 'y', 0xACAC, 0xACE0, 0xB515, 0, 0 };
393 static sal_Unicode const aSunMokPan[] = { 0xC36C, 0xBAA9, 0xD310, 0, 0 };
394 static sal_Unicode const aSunYeopseo[] = { 0xC36C, 0xC5FD, 0xC11C, 0, 0 };
395 static sal_Unicode const aSunBaekSong[] = { 0xC36C, 0xBC31, 0xC1A1, 0, 0 };
396 static sal_Unicode const aHYPostLight[] = { 'h', 'y', 0xC5FD, 0xC11C, 'l', 0, 0 };
397 static sal_Unicode const aHYPost[] = { 'h', 'y', 0xC5FD, 0xC11C, 0, 0 };
398 static sal_Unicode const aMagicR[] = { 0xD734, 0xBA3C, 0xB9E4, 0xC9C1, 0xCCB4, 0, 0 };
399 static sal_Unicode const aSunCrystal[] = { 0xC36C, 0xD06C, 0xB9AC, 0xC2A4, 0xD0C8, 0, 0 };
400 static sal_Unicode const aSunSaemmul[] = { 0xC36C, 0xC0D8, 0xBB3C, 0, 0 };
401 static sal_Unicode const aHaansoftBatang[] = { 0xD55C, 0xCEF4, 0xBC14, 0xD0D5, 0, 0 };
402 static sal_Unicode const aHaansoftDotum[] = { 0xD55C, 0xCEF4, 0xB3CB, 0xC6C0, 0, 0 };
403 static sal_Unicode const aHyhaeseo[] = { 0xD55C, 0xC591, 0xD574, 0xC11C, 0, 0 };
404 static sal_Unicode const aMDSol[] = { 'm', 'd', 0xC194, 0xCCB4, 0, 0 };
405 static sal_Unicode const aMDGaesung[] = { 'm', 'd', 0xAC1C, 0xC131, 0xCCB4, 0, 0 };
406 static sal_Unicode const aMDArt[] = { 'm', 'd', 0xC544, 0xD2B8, 0xCCB4, 0, 0 };
407 static sal_Unicode const aMDAlong[] = { 'm', 'd', 0xC544, 0xB871, 0xCCB4, 0, 0 };
408 static sal_Unicode const aMDEasop[] = { 'm', 'd', 0xC774, 0xC19D, 0xCCB4, 0, 0 };
409 static sal_Unicode const aHYShortSamulMedium[] = { 'h', 'y', 0xC595, 0xC740, 0xC0D8, 0xBB3C, 'm', 0 };
410 static sal_Unicode const aHYShortSamul[] = { 'h', 'y', 0xC595, 0xC740, 0xC0D8, 0xBB3C, 0 };
411 static sal_Unicode const aHGGothicE[] = { 'h','g', 0xFF7A, 0xFF9E, 0xFF7C, 0xFF6F, 0xFF78, 'e', 0 };
412 static sal_Unicode const aHGPGothicE[] = { 'h','g','p', 0xFF7A, 0xFF9E, 0xFF7C, 0xFF6F, 0xFF78, 'e', 0 };
413 static sal_Unicode const aHGSGothicE[] = { 'h','g','s', 0xFF7A, 0xFF9E, 0xFF7C, 0xFF6F, 0xFF78, 'e', 0 };
414 static sal_Unicode const aHGGothicM[] = { 'h','g', 0xFF7A, 0xFF9E, 0xFF7C, 0xFF6F, 0xFF78, 'm', 0 };
415 static sal_Unicode const aHGPGothicM[] = { 'h','g','p', 0xFF7A, 0xFF9E, 0xFF7C, 0xFF6F, 0xFF78, 'm', 0 };
416 static sal_Unicode const aHGSGothicM[] = { 'h','g','s', 0xFF7A, 0xFF9E, 0xFF7C, 0xFF6F, 0xFF78, 'm', 0 };
417 static sal_Unicode const aHGGyoshotai[] = { 'h','g', 0x884C, 0x66F8, 0x4F53, 0 };
418 static sal_Unicode const aHGPGyoshotai[] = { 'h','g','p', 0x884C, 0x66F8, 0x4F53, 0 };
419 static sal_Unicode const aHGSGyoshotai[] = { 'h','g','s', 0x884C, 0x66F8, 0x4F53, 0 };
420 static sal_Unicode const aHGKyokashotai[] = { 'h','g', 0x6559, 0x79D1, 0x66F8, 0x4F53, 0 };
421 static sal_Unicode const aHGPKyokashotai[] = { 'h','g','p', 0x6559, 0x79D1, 0x66F8, 0x4F53, 0 };
422 static sal_Unicode const aHGSKyokashotai[] = { 'h','g','s', 0x6559, 0x79D1, 0x66F8, 0x4F53, 0 };
423 static sal_Unicode const aHGMinchoB[] = { 'h','g', 0x660E, 0x671D, 'b', 0 };
424 static sal_Unicode const aHGPMinchoB[] = { 'h','g','p', 0x660E, 0x671D, 'b', 0 };
425 static sal_Unicode const aHGSMinchoB[] = { 'h','g','s', 0x660E, 0x671D, 'b', 0 };
426 static sal_Unicode const aHGMinchoE[] = { 'h','g', 0x660E, 0x671D, 'e', 0 };
427 static sal_Unicode const aHGPMinchoE[] = { 'h','g','p', 0x660E, 0x671D, 'e', 0 };
428 static sal_Unicode const aHGSMinchoE[] = { 'h','g','s', 0x660E, 0x671D, 'e', 0 };
429 static sal_Unicode const aHGSoeiKakupoptai[] = { 'h','g', 0x5275,0x82F1,0x89D2,0xFF8E,
430 0xFF9F,0xFF6F,0xFF8C,0xFF9F,0x4F53,0};
431 static sal_Unicode const aHGPSoeiKakupoptai[] = { 'h','g', 'p', 0x5275,0x82F1,0x89D2,0xFF8E,
432 0xFF9F,0xFF6F,0xFF8C,0xFF9F,0x4F53,0};
433 static sal_Unicode const aHGSSoeiKakupoptai[] = { 'h','g', 's', 0x5275,0x82F1,0x89D2,0xFF8E,
434 0xFF9F,0xFF6F,0xFF8C,0xFF9F,0x4F53,0};
435 static sal_Unicode const aHGSoeiPresenceEB[] = { 'h','g', 0x5275,0x82F1,0xFF8C,0xFF9F,
436 0xFF9A,0xFF7E,0xFF9E,0xFF9D,0xFF7D, 'e','b',0};
437 static sal_Unicode const aHGPSoeiPresenceEB[] = { 'h','g','p', 0x5275,0x82F1,0xFF8C,0xFF9F,
438 0xFF9A,0xFF7E,0xFF9E,0xFF9D,0xFF7D, 'e','b',0};
439 static sal_Unicode const aHGSSoeiPresenceEB[] = { 'h','g','s', 0x5275,0x82F1,0xFF8C,0xFF9F,
440 0xFF9A,0xFF7E,0xFF9E,0xFF9D,0xFF7D, 'e','b',0};
441 static sal_Unicode const aHGSoeiKakugothicUB[] = { 'h','g', 0x5275,0x82F1,0x89D2,0xFF7A,
442 0xFF9E,0xFF7C,0xFF6F,0xFF78,'u','b',0};
443 static sal_Unicode const aHGPSoeiKakugothicUB[] = { 'h','g','p', 0x5275,0x82F1,0x89D2,0xFF7A,
444 0xFF9E,0xFF7C,0xFF6F,0xFF78,'u','b',0};
445 static sal_Unicode const aHGSSoeiKakugothicUB[] = { 'h','g','s', 0x5275,0x82F1,0x89D2,0xFF7A,
446 0xFF9E,0xFF7C,0xFF6F,0xFF78,'u','b',0};
447 static sal_Unicode const aHGSeikaishotaiPRO[] = { 'h','g', 0x6B63,0x6977,0x66F8,0x4F53, '-','p','r','o',0};
448 static sal_Unicode const aHGMaruGothicMPRO[] = { 'h','g', 0x4E38,0xFF7A,0xFF9E,0xFF7C,0xFF6F,0xFF78, '-','p','r','o',0};
449 static sal_Unicode const aHiraginoMinchoPro[] = { 0x30D2, 0x30E9, 0x30AE, 0x30CE, 0x660E, 0x671D, 'p','r','o',0};
450 static sal_Unicode const aHiraginoMinchoProN[] = { 0x30D2, 0x30E9, 0x30AE, 0x30CE, 0x660E, 0x671D, 'p','r','o','n',0};
451 static sal_Unicode const aHiraginoKakuGothicPro[] = { 0x30D2, 0x30E9, 0x30AE, 0x30CE, 0x89D2, 0x30B4, 'p','r','o',0};
452 static sal_Unicode const aHiraginoKakuGothicProN[] = { 0x30D2, 0x30E9, 0x30AE, 0x30CE, 0x89D2, 0x30B4, 'p','r','o','n',0};
453 static sal_Unicode const aHiraginoMaruGothicPro[] = { 0x30D2, 0x30E9, 0x30AE, 0x30CE, 0x4E38, 0x30B4, 'p','r','o',0};
454 static sal_Unicode const aHiraginoMaruGothicProN[] = { 0x30D2, 0x30E9, 0x30AE, 0x30CE, 0x4E38, 0x30B4, 'p','r','o','n',0};
457 static ImplLocalizedFontName aImplLocalizedNamesList[] =
459 { "batang", aBatang },
460 { "batangche", aBatangChe },
461 { "gungshu", aGungsuh },
462 { "gungshuche", aGungsuhChe },
463 { "gulim", aGulim },
464 { "gulimche", aGulimChe },
465 { "dotum", aDotum },
466 { "dotumche", aDotumChe },
467 { "simsun", aSimSun },
468 { "nsimsun", aNSimSun },
469 { "simhei", aSimHei },
470 { "simkai", aSimKai },
471 { "zycjksun", azycjkSun },
472 { "zycjkhei", azycjkHei },
473 { "zycjkkai", azycjkKai },
474 { "fzhei", aFZHei },
475 { "fzkai", aFZKai },
476 { "fzsong", aFZSongYI },
477 { "fzshusong", aFZShuSong },
478 { "fzfangsong", aFZFangSong },
479 { "mhei", aMHei },
480 { "mkai", aMKai },
481 { "msong", aMSong },
482 { "cfangsong", aCFangSong },
483 { "mingliu", aMingLiU },
484 { "pmingliu", aPMingLiU },
485 { "hei", aHei },
486 { "kai", aKai },
487 { "ming", aMing },
488 { "msgothic", aMSGothic },
489 { "mspgothic", aMSPGothic },
490 { "msmincho", aMSMincho },
491 { "mspmincho", aMSPMincho },
492 { "meiryo", aMeiryo },
493 { "hgminchol", aHGMinchoL },
494 { "hggothicb", aHGGothicB },
495 { "hgpminchol", aHGPMinchoL },
496 { "hgpgothicb", aHGPGothicB },
497 { "hgmincholsun", aHGMinchoLSun },
498 { "hggothicbsun", aHGGothicBSun },
499 { "hgpmincholsun", aHGPMinchoLSun },
500 { "hgpgothicbsun", aHGPGothicBSun },
501 { "hgheiseimin", aHGHeiseiMin },
502 { "ipamincho", aIPAMincho },
503 { "ipapmincho", aIPAPMincho },
504 { "ipagothic", aIPAGothic },
505 { "ipapgothic", aIPAPGothic },
506 { "ipauigothic", aIPAUIGothic },
507 { "sazanamimincho", aSazanamiMincho },
508 { "sazanamigothic", aSazanamiGothic },
509 { "kochimincho", aKochiMincho },
510 { "kochigothic", aKochiGothic },
511 { "sundotum", aSunDotum },
512 { "sungulim", aSunGulim },
513 { "sunbatang", aSunBatang },
514 { "baekmukdotum", aBaekmukDotum },
515 { "baekmukgulim", aBaekmukGulim },
516 { "baekmukbatang", aBaekmukBatang },
517 { "fzheiti", aFzHeiTiCN },
518 { "fzheiti", aFzHeiTiTW },
519 { "fzkaiti", aFzKaiTiCN },
520 { "fzkaitib", aFzKaiTiTW },
521 { "fzmingtib", aFzMingTi },
522 { "fzsongti", aFzSongTi },
523 { "hymyeongjoextra", aHYMyeongJoExtra },
524 { "hysinmyeongjomedium", aHYSinMyeongJoMedium },
525 { "hygothicmedium", aHYGothicMedium },
526 { "hygraphicmedium", aHYGraphicMedium },
527 { "hygraphic", aHYGraphic },
528 { "newgulim", aNewGulim },
529 { "sungungseo", aSunGungseo },
530 { "hygungsobold", aHYGungSoBold },
531 { "hygungso", aHYGungSo },
532 { "sunheadline", aSunHeadLine },
533 { "hyheadlinemedium", aHYHeadLineMedium },
534 { "hyheadline", aHYHeadLine },
535 { "yetr", aYetR },
536 { "hygothicextra", aHYGothicExtra },
537 { "sunmokpan", aSunMokPan },
538 { "sunyeopseo", aSunYeopseo },
539 { "sunbaeksong", aSunBaekSong },
540 { "hypostlight", aHYPostLight },
541 { "hypost", aHYPost },
542 { "magicr", aMagicR },
543 { "suncrystal", aSunCrystal },
544 { "sunsaemmul", aSunSaemmul },
545 { "hyshortsamulmedium", aHYShortSamulMedium },
546 { "hyshortsamul", aHYShortSamul },
547 { "haansoftbatang", aHaansoftBatang },
548 { "haansoftdotum", aHaansoftDotum },
549 { "hyhaeseo", aHyhaeseo },
550 { "mdsol", aMDSol },
551 { "mdgaesung", aMDGaesung },
552 { "mdart", aMDArt },
553 { "mdalong", aMDAlong },
554 { "mdeasop", aMDEasop },
555 { "hggothice", aHGGothicE },
556 { "hgpgothice", aHGPGothicE },
557 { "hgpothice", aHGSGothicE },
558 { "hggothicm", aHGGothicM },
559 { "hgpgothicm", aHGPGothicM },
560 { "hgpgothicm", aHGSGothicM },
561 { "hggyoshotai", aHGGyoshotai },
562 { "hgpgyoshotai", aHGPGyoshotai },
563 { "hgsgyoshotai", aHGSGyoshotai },
564 { "hgkyokashotai", aHGKyokashotai },
565 { "hgpkyokashotai", aHGPKyokashotai },
566 { "hgskyokashotai", aHGSKyokashotai },
567 { "hgminchob", aHGMinchoB },
568 { "hgpminchob", aHGPMinchoB },
569 { "hgsminchob", aHGSMinchoB },
570 { "hgminchoe", aHGMinchoE },
571 { "hgpminchoe", aHGPMinchoE },
572 { "hgsminchoe", aHGSMinchoE },
573 { "hgsoeikakupoptai", aHGSoeiKakupoptai },
574 { "hgpsoeikakupopta", aHGPSoeiKakupoptai },
575 { "hgssoeikakupopta", aHGSSoeiKakupoptai },
576 { "hgsoeipresenceeb", aHGSoeiPresenceEB },
577 { "hgpsoeipresenceeb", aHGPSoeiPresenceEB },
578 { "hgssoeipresenceeb", aHGSSoeiPresenceEB },
579 { "hgsoeikakugothicub", aHGSoeiKakugothicUB },
580 { "hgpsoeikakugothicub", aHGPSoeiKakugothicUB },
581 { "hgssoeikakugothicub", aHGSSoeiKakugothicUB },
582 { "hgseikaishotaipro", aHGSeikaishotaiPRO },
583 { "hgmarugothicmpro", aHGMaruGothicMPRO },
584 { "hiraginominchopro", aHiraginoMinchoPro },
585 { "hiraginominchopron", aHiraginoMinchoProN },
586 { "hiraginokakugothicpro", aHiraginoKakuGothicPro },
587 { "hiraginokakugothicpron", aHiraginoKakuGothicProN },
588 { "hiraginomarugothicpro", aHiraginoMaruGothicPro },
589 { "hiraginomarugothicpron", aHiraginoMaruGothicProN },
590 { NULL, NULL },
593 // -----------------------------------------------------------------------
595 void ImplGetEnglishSearchFontName( String& rName )
597 bool bNeedTranslation = false;
598 xub_StrLen nLen = rName.Len();
600 // Remove trailing whitespaces
601 xub_StrLen i = nLen;
602 while ( i && (rName.GetChar( i-1 ) < 32) )
603 i--;
604 if ( i != nLen )
605 rName.Erase( i );
607 // Remove Script at the end
608 // Scriptname must be the last part of the fontname and
609 // looks like "fontname (scriptname)". So there can only be a
610 // script name at the and of the fontname, when the last char is ')'
611 if ( (nLen >= 3) && rName.GetChar( nLen-1 ) == ')' )
613 int nOpen = 1;
614 xub_StrLen nTempLen = nLen-2;
615 while ( nTempLen )
617 if ( rName.GetChar( nTempLen ) == '(' )
619 nOpen--;
620 if ( !nOpen )
622 // Remove Space at the end
623 if ( nTempLen && (rName.GetChar( nTempLen-1 ) == ' ') )
624 nTempLen--;
625 rName.Erase( nTempLen );
626 nLen = nTempLen;
627 break;
630 if ( rName.GetChar( nTempLen ) == ')' )
631 nOpen++;
632 nTempLen--;
636 // remove all whitespaces and converts to lower case ASCII
637 // TODO: better transliteration to ASCII e.g. all digits
638 i = 0;
639 while ( i < nLen )
641 sal_Unicode c = rName.GetChar( i );
642 if ( c > 127 )
644 // Translate to Lowercase-ASCII
645 // FullWidth-ASCII to half ASCII
646 if ( (c >= 0xFF00) && (c <= 0xFF5E) )
648 c -= 0xFF00-0x0020;
649 // Upper to Lower
650 if ( (c >= 'A') && (c <= 'Z') )
651 c += 'a' - 'A';
652 rName.SetChar( i, c );
654 else
656 // Only Fontnames with None-Ascii-Characters must be translated
657 bNeedTranslation = true;
660 // not lowercase Ascii
661 else if ( !((c >= 'a') && (c <= 'z')) )
663 // To Lowercase-Ascii
664 if ( (c >= 'A') && (c <= 'Z') )
666 c += 'a' - 'A';
667 rName.SetChar( i, c );
669 else if( ((c < '0') || (c > '9')) && (c != ';') ) // not 0-9 or semicolon
671 // Remove white spaces and special characters
672 rName.Erase( i, 1 );
673 nLen--;
674 continue;
678 i++;
681 // translate normalized localized name to its normalized English ASCII name
682 if( bNeedTranslation )
684 typedef std::hash_map<const String, const char*,FontNameHash> FontNameDictionary;
685 static FontNameDictionary aDictionary( sizeof(aImplLocalizedNamesList) / sizeof(*aImplLocalizedNamesList) );
686 // the font name dictionary needs to be intialized once
687 if( aDictionary.empty() )
689 // TODO: check if all dictionary entries are already normalized?
690 const ImplLocalizedFontName* pList = aImplLocalizedNamesList;
691 for(; pList->mpEnglishName; ++pList )
692 aDictionary[ pList->mpLocalizedNames ] = pList->mpEnglishName;
695 FontNameDictionary::const_iterator it = aDictionary.find( rName );
696 if( it != aDictionary.end() )
697 rName.AssignAscii( it->second );
701 // -----------------------------------------------------------------------
703 static String GetNextFontToken( const String& rTokenStr, xub_StrLen& rIndex )
705 // check for valid start index
706 int nStringLen = rTokenStr.Len();
707 if( rIndex >= nStringLen )
709 rIndex = STRING_NOTFOUND;
710 return String();
713 // find the next token delimiter and return the token substring
714 const sal_Unicode* pStr = rTokenStr.GetBuffer() + rIndex;
715 const sal_Unicode* pEnd = rTokenStr.GetBuffer() + nStringLen;
716 for(; pStr < pEnd; ++pStr )
717 if( (*pStr == ';') || (*pStr == ',') )
718 break;
720 xub_StrLen nTokenStart = rIndex;
721 xub_StrLen nTokenLen;
722 if( pStr < pEnd )
724 rIndex = sal::static_int_cast<xub_StrLen>(pStr - rTokenStr.GetBuffer());
725 nTokenLen = rIndex - nTokenStart;
726 ++rIndex; // skip over token separator
728 else
730 // no token delimiter found => handle last token
731 rIndex = STRING_NOTFOUND;
732 nTokenLen = STRING_LEN;
734 // optimize if the token string consists of just one token
735 if( !nTokenStart )
736 return rTokenStr;
739 return String( rTokenStr, nTokenStart, nTokenLen );
742 // TODO: get rid of this in another incompatible build with SW project.
743 // SW's WW8 and RTF filters still use this (from fontcvt.hxx)
744 String GetFontToken( const String& rTokenStr, xub_StrLen nToken, xub_StrLen& rIndex )
746 // skip nToken Tokens
747 for( xub_StrLen i = 0; (i < nToken) && (rIndex != STRING_NOTFOUND); ++i )
748 GetNextFontToken( rTokenStr, rIndex );
750 return GetNextFontToken( rTokenStr, rIndex );
753 // =======================================================================
755 // TODO: remove this method when the CWS-gfbfcfg dust has settled
756 void ImplFreeOutDevFontData()
759 // =======================================================================
761 void OutputDevice::BeginFontSubstitution()
763 ImplSVData* pSVData = ImplGetSVData();
764 pSVData->maGDIData.mbFontSubChanged = FALSE;
767 // -----------------------------------------------------------------------
769 void OutputDevice::EndFontSubstitution()
771 ImplSVData* pSVData = ImplGetSVData();
772 if ( pSVData->maGDIData.mbFontSubChanged )
774 ImplUpdateAllFontData( false );
776 Application* pApp = GetpApp();
777 DataChangedEvent aDCEvt( DATACHANGED_FONTSUBSTITUTION );
778 pApp->DataChanged( aDCEvt );
779 pApp->NotifyAllWindows( aDCEvt );
780 pSVData->maGDIData.mbFontSubChanged = FALSE;
784 // -----------------------------------------------------------------------
786 void OutputDevice::AddFontSubstitute( const XubString& rFontName,
787 const XubString& rReplaceFontName,
788 USHORT nFlags )
790 ImplDirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
791 if( !rpSubst )
792 rpSubst = new ImplDirectFontSubstitution();
793 rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags );
794 ImplGetSVData()->maGDIData.mbFontSubChanged = TRUE;
797 // -----------------------------------------------------------------------
799 void ImplDirectFontSubstitution::AddFontSubstitute( const String& rFontName,
800 const String& rSubstFontName, USHORT nFlags )
802 maFontSubstList.push_back( ImplFontSubstEntry( rFontName, rSubstFontName, nFlags ) );
805 // -----------------------------------------------------------------------
807 ImplFontSubstEntry::ImplFontSubstEntry( const String& rFontName,
808 const String& rSubstFontName, USHORT nSubstFlags )
809 : maName( rFontName )
810 , maReplaceName( rSubstFontName )
811 , mnFlags( nSubstFlags )
813 maSearchName = rFontName;
814 maSearchReplaceName = rSubstFontName;
815 ImplGetEnglishSearchFontName( maSearchName );
816 ImplGetEnglishSearchFontName( maSearchReplaceName );
819 // -----------------------------------------------------------------------
821 void OutputDevice::ImplAddDevFontSubstitute( const XubString& rFontName,
822 const XubString& rReplaceFontName,
823 USHORT nFlags )
825 ImplInitOutDevData();
826 mpOutDevData->maDevFontSubst.AddFontSubstitute( rFontName, rReplaceFontName, nFlags );
829 // -----------------------------------------------------------------------
831 void OutputDevice::RemoveFontSubstitute( USHORT n )
833 ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
834 if( pSubst )
835 pSubst->RemoveFontSubstitute( n );
838 // -----------------------------------------------------------------------
840 void ImplDirectFontSubstitution::RemoveFontSubstitute( int nIndex )
842 FontSubstList::iterator it = maFontSubstList.begin();
843 for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ;
844 if( it != maFontSubstList.end() )
845 maFontSubstList.erase( it );
848 // -----------------------------------------------------------------------
850 USHORT OutputDevice::GetFontSubstituteCount()
852 const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
853 if( !pSubst )
854 return 0;
855 int nCount = pSubst->GetFontSubstituteCount();
856 return (USHORT)nCount;
859 // -----------------------------------------------------------------------
861 void OutputDevice::GetFontSubstitute( USHORT n,
862 XubString& rFontName,
863 XubString& rReplaceFontName,
864 USHORT& rFlags )
866 const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
867 if( pSubst )
868 pSubst->GetFontSubstitute( n, rFontName, rReplaceFontName, rFlags );
871 // -----------------------------------------------------------------------
873 bool ImplDirectFontSubstitution::GetFontSubstitute( int nIndex,
874 String& rFontName, String& rSubstFontName, USHORT& rFlags ) const
876 FontSubstList::const_iterator it = maFontSubstList.begin();
877 for( int nCount = 0; (it != maFontSubstList.end()) && (nCount++ != nIndex); ++it ) ;
878 if( it == maFontSubstList.end() )
879 return false;
881 const ImplFontSubstEntry* pEntry = &(*it);
882 rFontName = pEntry->maName;
883 rSubstFontName = pEntry->maReplaceName;
884 rFlags = pEntry->mnFlags;
885 return true;
888 // -----------------------------------------------------------------------
890 bool ImplDirectFontSubstitution::FindFontSubstitute( String& rSubstName,
891 const String& rSearchName, USHORT nFlags ) const
893 // TODO: get rid of O(N) searches
894 FontSubstList::const_iterator it = maFontSubstList.begin();
895 for(; it != maFontSubstList.end(); ++it )
897 const ImplFontSubstEntry& rEntry = *it;
898 if( ((rEntry.mnFlags & nFlags) || !nFlags)
899 && (rEntry.maSearchName == rSearchName) )
901 rSubstName = rEntry.maSearchReplaceName;
902 return true;
906 return false;
909 // -----------------------------------------------------------------------
911 static void ImplFontSubstitute( String& rFontName,
912 USHORT nFlags, ImplDirectFontSubstitution* pDevSpecific )
914 #ifdef DBG_UTIL
915 String aTempName = rFontName;
916 ImplGetEnglishSearchFontName( aTempName );
917 DBG_ASSERT( aTempName == rFontName, "ImplFontSubstitute() called without a searchname" );
918 #endif
920 String aSubstFontName;
922 // apply user-configurable font replacement (eg, from the list in Tools->Options)
923 const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
924 if( pSubst && pSubst->FindFontSubstitute( aSubstFontName, rFontName, FONT_SUBSTITUTE_ALWAYS ) )
926 rFontName = aSubstFontName;
927 return;
930 // apply device specific font replacement (e.g. to use printer builtin fonts)
931 if( !pDevSpecific )
932 return;
934 if( pDevSpecific->FindFontSubstitute( aSubstFontName, rFontName, nFlags ) )
936 rFontName = aSubstFontName;
937 return;
941 // =======================================================================
943 static bool ImplIsFontToken( const String& rName, const String& rToken )
945 String aTempName;
946 xub_StrLen nIndex = 0;
949 aTempName = GetNextFontToken( rName, nIndex );
950 if ( rToken == aTempName )
951 return true;
953 while ( nIndex != STRING_NOTFOUND );
955 return false;
958 // -----------------------------------------------------------------------
960 static void ImplAppendFontToken( String& rName, const String& rNewToken )
962 if ( rName.Len() )
964 rName.Append( ';' );
965 rName.Append( rNewToken );
967 else
968 rName = rNewToken;
971 // -----------------------------------------------------------------------
973 static void ImplAddTokenFontName( String& rName, const String& rNewToken )
975 if ( !ImplIsFontToken( rName, rNewToken ) )
976 ImplAppendFontToken( rName, rNewToken );
979 // -----------------------------------------------------------------------
981 Font OutputDevice::GetDefaultFont( USHORT nType, LanguageType eLang,
982 ULONG nFlags, const OutputDevice* pOutDev )
984 DBG_TRACE( "OutputDevice::GetDefaultFont()" );
986 com::sun::star::lang::Locale aLocale;
987 if( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW )
989 aLocale = Application::GetSettings().GetUILocale();
991 else
993 MsLangId::convertLanguageToLocale( eLang, aLocale );
996 DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get();
997 String aSearch = rDefaults.getUserInterfaceFont( aLocale ); // ensure a fallback
998 String aDefault = rDefaults.getDefaultFont( aLocale, nType );
999 if( aDefault.Len() )
1000 aSearch = aDefault;
1002 int nDefaultHeight = 12;
1004 Font aFont;
1005 aFont.SetPitch( PITCH_VARIABLE );
1007 switch ( nType )
1009 case DEFAULTFONT_SANS_UNICODE:
1010 case DEFAULTFONT_UI_SANS:
1011 aFont.SetFamily( FAMILY_SWISS );
1012 break;
1014 case DEFAULTFONT_SANS:
1015 case DEFAULTFONT_LATIN_HEADING:
1016 case DEFAULTFONT_LATIN_SPREADSHEET:
1017 case DEFAULTFONT_LATIN_DISPLAY:
1018 aFont.SetFamily( FAMILY_SWISS );
1019 break;
1021 case DEFAULTFONT_SERIF:
1022 case DEFAULTFONT_LATIN_TEXT:
1023 case DEFAULTFONT_LATIN_PRESENTATION:
1024 aFont.SetFamily( FAMILY_ROMAN );
1025 break;
1027 case DEFAULTFONT_FIXED:
1028 case DEFAULTFONT_LATIN_FIXED:
1029 case DEFAULTFONT_UI_FIXED:
1030 aFont.SetPitch( PITCH_FIXED );
1031 aFont.SetFamily( FAMILY_MODERN );
1032 break;
1034 case DEFAULTFONT_SYMBOL:
1035 aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
1036 break;
1038 case DEFAULTFONT_CJK_TEXT:
1039 case DEFAULTFONT_CJK_PRESENTATION:
1040 case DEFAULTFONT_CJK_SPREADSHEET:
1041 case DEFAULTFONT_CJK_HEADING:
1042 case DEFAULTFONT_CJK_DISPLAY:
1043 aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
1044 break;
1046 case DEFAULTFONT_CTL_TEXT:
1047 case DEFAULTFONT_CTL_PRESENTATION:
1048 case DEFAULTFONT_CTL_SPREADSHEET:
1049 case DEFAULTFONT_CTL_HEADING:
1050 case DEFAULTFONT_CTL_DISPLAY:
1051 aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
1052 break;
1055 if ( aSearch.Len() )
1057 aFont.SetHeight( nDefaultHeight );
1058 aFont.SetWeight( WEIGHT_NORMAL );
1060 if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW )
1061 aFont.SetCharSet( gsl_getSystemTextEncoding() );
1063 // Should we only return available fonts on the given device
1064 if ( pOutDev )
1066 pOutDev->ImplInitFontList();
1068 // Search Font in the FontList
1069 String aName;
1070 String aSearchName;
1071 xub_StrLen nIndex = 0;
1074 aSearchName = GetNextFontToken( aSearch, nIndex );
1075 ImplGetEnglishSearchFontName( aSearchName );
1076 ImplDevFontListData* pFontFamily = pOutDev->mpFontList->ImplFindBySearchName( aSearchName );
1077 if( pFontFamily )
1079 ImplAddTokenFontName( aName, pFontFamily->GetFamilyName() );
1080 if( nFlags & DEFAULTFONT_FLAGS_ONLYONE )
1081 break;
1084 while ( nIndex != STRING_NOTFOUND );
1085 aFont.SetName( aName );
1088 // No Name, than set all names
1089 if ( !aFont.GetName().Len() )
1091 xub_StrLen nIndex = 0;
1092 if ( nFlags & DEFAULTFONT_FLAGS_ONLYONE )
1094 //aFont.SetName( aSearch.GetToken( 0, ';', nIndex ) );
1095 if( !pOutDev )
1096 pOutDev = (const OutputDevice *)ImplGetSVData()->mpDefaultWin;
1097 if( !pOutDev )
1098 aFont.SetName( aSearch.GetToken( 0, ';', nIndex ) );
1099 else
1101 pOutDev->ImplInitFontList();
1103 aFont.SetName( aSearch );
1105 // convert to pixel height
1106 Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetSize() );
1107 if ( !aSize.Height() )
1109 // use default pixel height only when logical height is zero
1110 if ( aFont.GetHeight() )
1111 aSize.Height() = 1;
1112 else
1113 aSize.Height() = (12*pOutDev->mnDPIY)/72;
1116 // use default width only when logical width is zero
1117 if( (0 == aSize.Width()) && (0 != aFont.GetSize().Width()) )
1118 aSize.Width() = 1;
1120 // get the name of the first available font
1121 float fExactHeight = static_cast<float>(aSize.Height());
1122 ImplFontEntry* pEntry = pOutDev->mpFontCache->GetFontEntry( pOutDev->mpFontList, aFont, aSize, fExactHeight, pOutDev->mpOutDevData ? &pOutDev->mpOutDevData->maDevFontSubst : NULL );
1123 if( pEntry->maFontSelData.mpFontData )
1124 aFont.SetName( pEntry->maFontSelData.mpFontData->maName );
1125 else
1126 aFont.SetName( pEntry->maFontSelData.maTargetName );
1129 else
1130 aFont.SetName( aSearch );
1134 #if OSL_DEBUG_LEVEL > 2
1135 const char* s = "DEFAULTFONT_SANS_UNKNOWN";
1136 switch ( nType )
1138 case DEFAULTFONT_SANS_UNICODE: s = "DEFAULTFONT_SANS_UNICODE"; break;
1139 case DEFAULTFONT_UI_SANS: s = "DEFAULTFONT_UI_SANS"; break;
1141 case DEFAULTFONT_SANS: s = "DEFAULTFONT_SANS"; break;
1142 case DEFAULTFONT_LATIN_HEADING: s = "DEFAULTFONT_LATIN_HEADING"; break;
1143 case DEFAULTFONT_LATIN_SPREADSHEET: s = "DEFAULTFONT_LATIN_SPREADSHEET"; break;
1144 case DEFAULTFONT_LATIN_DISPLAY: s = "DEFAULTFONT_LATIN_DISPLAY"; break;
1146 case DEFAULTFONT_SERIF: s = "DEFAULTFONT_SERIF"; break;
1147 case DEFAULTFONT_LATIN_TEXT: s = "DEFAULTFONT_LATIN_TEXT"; break;
1148 case DEFAULTFONT_LATIN_PRESENTATION: s = "DEFAULTFONT_LATIN_PRESENTATION"; break;
1150 case DEFAULTFONT_FIXED: s = "DEFAULTFONT_FIXED"; break;
1151 case DEFAULTFONT_LATIN_FIXED: s = "DEFAULTFONT_LATIN_FIXED"; break;
1152 case DEFAULTFONT_UI_FIXED: s = "DEFAULTFONT_UI_FIXED"; break;
1154 case DEFAULTFONT_SYMBOL: s = "DEFAULTFONT_SYMBOL"; break;
1156 case DEFAULTFONT_CJK_TEXT: s = "DEFAULTFONT_CJK_TEXT"; break;
1157 case DEFAULTFONT_CJK_PRESENTATION: s = "DEFAULTFONT_CJK_PRESENTATION"; break;
1158 case DEFAULTFONT_CJK_SPREADSHEET: s = "DEFAULTFONT_CJK_SPREADSHEET"; break;
1159 case DEFAULTFONT_CJK_HEADING: s = "DEFAULTFONT_CJK_HEADING"; break;
1160 case DEFAULTFONT_CJK_DISPLAY: s = "DEFAULTFONT_CJK_DISPLAY"; break;
1162 case DEFAULTFONT_CTL_TEXT: s = "DEFAULTFONT_CTL_TEXT"; break;
1163 case DEFAULTFONT_CTL_PRESENTATION: s = "DEFAULTFONT_CTL_PRESENTATION"; break;
1164 case DEFAULTFONT_CTL_SPREADSHEET: s = "DEFAULTFONT_CTL_SPREADSHEET"; break;
1165 case DEFAULTFONT_CTL_HEADING: s = "DEFAULTFONT_CTL_HEADING"; break;
1166 case DEFAULTFONT_CTL_DISPLAY: s = "DEFAULTFONT_CTL_DISPLAY"; break;
1168 fprintf( stderr, " OutputDevice::GetDefaultFont() Type=\"%s\" lang=%d flags=%ld FontName=\"%s\"\n",
1169 s, eLang, nFlags,
1170 OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr()
1172 #endif
1174 return aFont;
1177 // =======================================================================
1179 String GetSubsFontName( const String& rName, ULONG nFlags )
1181 String aName;
1183 xub_StrLen nIndex = 0;
1184 String aOrgName = GetNextFontToken( rName, nIndex );
1185 ImplGetEnglishSearchFontName( aOrgName );
1187 // #93662# do not try to replace StarSymbol with MS only font
1188 if( nFlags == (SUBSFONT_MS|SUBSFONT_ONLYONE)
1189 && ( aOrgName.EqualsAscii( "starsymbol" )
1190 || aOrgName.EqualsAscii( "opensymbol" ) ) )
1191 return aName;
1193 const FontNameAttr* pAttr = FontSubstConfiguration::get()->getSubstInfo( aOrgName );
1194 if ( pAttr )
1196 for( int i = 0; i < 3; i++ )
1198 const ::std::vector< String >* pVector = NULL;
1199 switch( i )
1201 case 0:
1202 if( nFlags & SUBSFONT_MS && pAttr->MSSubstitutions.size() )
1203 pVector = &pAttr->MSSubstitutions;
1204 break;
1205 case 1:
1206 if( nFlags & SUBSFONT_PS && pAttr->PSSubstitutions.size() )
1207 pVector = &pAttr->PSSubstitutions;
1208 break;
1209 case 2:
1210 if( nFlags & SUBSFONT_HTML && pAttr->HTMLSubstitutions.size() )
1211 pVector = &pAttr->HTMLSubstitutions;
1212 break;
1214 if( ! pVector )
1215 continue;
1216 for( ::std::vector< String >::const_iterator it = pVector->begin(); it != pVector->end(); ++it )
1217 if( ! ImplIsFontToken( rName, *it ) )
1219 ImplAppendFontToken( aName, *it );
1220 if( nFlags & SUBSFONT_ONLYONE )
1222 i = 4;
1223 break;
1229 return aName;
1232 // =======================================================================
1234 static unsigned ImplIsCJKFont( const String& rFontName )
1236 // Test, if Fontname includes CJK characters --> In this case we
1237 // mention that it is a CJK font
1238 const sal_Unicode* pStr = rFontName.GetBuffer();
1239 while ( *pStr )
1241 // japanese
1242 if ( ((*pStr >= 0x3040) && (*pStr <= 0x30FF)) ||
1243 ((*pStr >= 0x3190) && (*pStr <= 0x319F)) )
1244 return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_JP;
1246 // korean
1247 if ( ((*pStr >= 0xAC00) && (*pStr <= 0xD7AF)) ||
1248 ((*pStr >= 0x3130) && (*pStr <= 0x318F)) ||
1249 ((*pStr >= 0x1100) && (*pStr <= 0x11FF)) )
1250 return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_KR;
1252 // chinese
1253 if ( ((*pStr >= 0x3400) && (*pStr <= 0x9FFF)) )
1254 return IMPL_FONT_ATTR_CJK|IMPL_FONT_ATTR_CJK_TC|IMPL_FONT_ATTR_CJK_SC;
1256 // cjk
1257 if ( ((*pStr >= 0x3000) && (*pStr <= 0xD7AF)) ||
1258 ((*pStr >= 0xFF00) && (*pStr <= 0xFFEE)) )
1259 return IMPL_FONT_ATTR_CJK;
1261 pStr++;
1264 return 0;
1267 // -----------------------------------------------------------------------
1269 static void ImplCalcType( ULONG& rType, FontWeight& rWeight, FontWidth& rWidth,
1270 FontFamily eFamily, const FontNameAttr* pFontAttr )
1272 if ( eFamily != FAMILY_DONTKNOW )
1274 if ( eFamily == FAMILY_SWISS )
1275 rType |= IMPL_FONT_ATTR_SANSSERIF;
1276 else if ( eFamily == FAMILY_ROMAN )
1277 rType |= IMPL_FONT_ATTR_SERIF;
1278 else if ( eFamily == FAMILY_SCRIPT )
1279 rType |= IMPL_FONT_ATTR_SCRIPT;
1280 else if ( eFamily == FAMILY_MODERN )
1281 rType |= IMPL_FONT_ATTR_FIXED;
1282 else if ( eFamily == FAMILY_DECORATIVE )
1283 rType |= IMPL_FONT_ATTR_DECORATIVE;
1286 if ( pFontAttr )
1288 rType |= pFontAttr->Type;
1290 if ( ((rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL)) &&
1291 (pFontAttr->Weight != WEIGHT_DONTKNOW) )
1292 rWeight = pFontAttr->Weight;
1293 if ( ((rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL)) &&
1294 (pFontAttr->Width != WIDTH_DONTKNOW) )
1295 rWidth = pFontAttr->Width;
1299 // =======================================================================
1301 ImplFontData::ImplFontData( const ImplDevFontAttributes& rDFA, int nMagic )
1302 : ImplDevFontAttributes( rDFA ),
1303 mnWidth(0),
1304 mnHeight(0),
1305 mnMagic( nMagic ),
1306 mpNext( NULL )
1308 // StarSymbol is a unicode font, but it still deserves the symbol flag
1309 if( !mbSymbolFlag )
1310 if( 0 == GetFamilyName().CompareIgnoreCaseToAscii( "starsymbol", 10)
1311 || 0 == GetFamilyName().CompareIgnoreCaseToAscii( "opensymbol", 10) )
1312 mbSymbolFlag = true;
1315 // -----------------------------------------------------------------------
1317 StringCompare ImplFontData::CompareIgnoreSize( const ImplFontData& rOther ) const
1319 // compare their width, weight, italic and style name
1320 if( meWidthType < rOther.meWidthType )
1321 return COMPARE_LESS;
1322 else if( meWidthType > rOther.meWidthType )
1323 return COMPARE_GREATER;
1325 if( meWeight < rOther.meWeight )
1326 return COMPARE_LESS;
1327 else if( meWeight > rOther.meWeight )
1328 return COMPARE_GREATER;
1330 if( meItalic < rOther.meItalic )
1331 return COMPARE_LESS;
1332 else if( meItalic > rOther.meItalic )
1333 return COMPARE_GREATER;
1335 StringCompare eCompare = maName.CompareTo( rOther.maName );
1336 return eCompare;
1339 // -----------------------------------------------------------------------
1341 StringCompare ImplFontData::CompareWithSize( const ImplFontData& rOther ) const
1343 StringCompare eCompare = CompareIgnoreSize( rOther );
1344 if( eCompare != COMPARE_EQUAL )
1345 return eCompare;
1347 if( mnHeight < rOther.mnHeight )
1348 return COMPARE_LESS;
1349 else if( mnHeight > rOther.mnHeight )
1350 return COMPARE_GREATER;
1352 if( mnWidth < rOther.mnWidth )
1353 return COMPARE_LESS;
1354 else if( mnWidth > rOther.mnWidth )
1355 return COMPARE_GREATER;
1357 return COMPARE_EQUAL;
1360 // -----------------------------------------------------------------------
1362 struct FontMatchStatus
1364 public:
1365 int mnFaceMatch;
1366 int mnHeightMatch;
1367 int mnWidthMatch;
1368 const xub_Unicode* mpTargetStyleName;
1371 bool ImplFontData::IsBetterMatch( const ImplFontSelectData& rFSD, FontMatchStatus& rStatus ) const
1373 int nMatch = 0;
1375 const String& rFontName = rFSD.maTargetName;
1376 if( (rFontName == maName) || rFontName.EqualsIgnoreCaseAscii( maName ) )
1377 nMatch += 240000;
1379 if( rStatus.mpTargetStyleName
1380 && maStyleName.EqualsIgnoreCaseAscii( rStatus.mpTargetStyleName ) )
1381 nMatch += 120000;
1383 if( (rFSD.mePitch != PITCH_DONTKNOW) && (rFSD.mePitch == mePitch) )
1384 nMatch += 20000;
1386 // prefer NORMAL font width
1387 // TODO: change when the upper layers can tell their width preference
1388 if( meWidthType == WIDTH_NORMAL )
1389 nMatch += 400;
1390 else if( (meWidthType == WIDTH_SEMI_EXPANDED) || (meWidthType == WIDTH_SEMI_CONDENSED) )
1391 nMatch += 300;
1393 if( rFSD.meWeight != WEIGHT_DONTKNOW )
1395 // if not bold prefer light fonts to bold fonts
1396 int nReqWeight = (int)rFSD.meWeight;
1397 if ( rFSD.meWeight > WEIGHT_MEDIUM )
1398 nReqWeight += 100;
1400 int nGivenWeight = (int)meWeight;
1401 if( meWeight > WEIGHT_MEDIUM )
1402 nGivenWeight += 100;
1404 int nWeightDiff = nReqWeight - nGivenWeight;
1406 if ( nWeightDiff == 0 )
1407 nMatch += 1000;
1408 else if ( nWeightDiff == +1 || nWeightDiff == -1 )
1409 nMatch += 700;
1410 else if ( nWeightDiff < +50 && nWeightDiff > -50)
1411 nMatch += 200;
1413 else // requested weight == WEIGHT_DONTKNOW
1415 // prefer NORMAL font weight
1416 // TODO: change when the upper layers can tell their weight preference
1417 if( meWeight == WEIGHT_NORMAL )
1418 nMatch += 450;
1419 else if( meWeight == WEIGHT_MEDIUM )
1420 nMatch += 350;
1421 else if( (meWeight == WEIGHT_SEMILIGHT) || (meWeight == WEIGHT_SEMIBOLD) )
1422 nMatch += 200;
1423 else if( meWeight == WEIGHT_LIGHT )
1424 nMatch += 150;
1427 if ( rFSD.meItalic == ITALIC_NONE )
1429 if( meItalic == ITALIC_NONE )
1430 nMatch += 900;
1432 else
1434 if( rFSD.meItalic == meItalic )
1435 nMatch += 900;
1436 else if( meItalic != ITALIC_NONE )
1437 nMatch += 600;
1440 if( mbDevice )
1441 nMatch += 1;
1443 int nHeightMatch = 0;
1444 int nWidthMatch = 0;
1446 if( IsScalable() )
1448 if( rFSD.mnOrientation != 0 )
1449 nMatch += 80;
1450 else if( rFSD.mnWidth != 0 )
1451 nMatch += 25;
1452 else
1453 nMatch += 5;
1455 else
1457 if( rFSD.mnHeight == mnHeight )
1459 nMatch += 20;
1460 if( rFSD.mnWidth == mnWidth )
1461 nMatch += 10;
1463 else
1465 // for non-scalable fonts the size difference is very important
1466 // prefer the smaller font face because of clipping/overlapping issues
1467 int nHeightDiff = (rFSD.mnHeight - mnHeight) * 1000;
1468 nHeightMatch = (nHeightDiff >= 0) ? -nHeightDiff : 100+nHeightDiff;
1469 if( rFSD.mnHeight )
1470 nHeightMatch /= rFSD.mnHeight;
1472 if( (rFSD.mnWidth != 0) && (mnWidth != 0) && (rFSD.mnWidth != mnWidth) )
1474 int nWidthDiff = (rFSD.mnWidth - mnWidth) * 100;
1475 nWidthMatch = (nWidthDiff >= 0) ? -nWidthDiff : +nWidthDiff;
1480 if( rStatus.mnFaceMatch > nMatch )
1481 return false;
1482 else if( rStatus.mnFaceMatch < nMatch )
1484 rStatus.mnFaceMatch = nMatch;
1485 rStatus.mnHeightMatch = nHeightMatch;
1486 rStatus.mnWidthMatch = nWidthMatch;
1487 return true;
1490 // when two fonts are still competing prefer the
1491 // one with the best matching height
1492 if( rStatus.mnHeightMatch > nHeightMatch )
1493 return false;
1494 else if( rStatus.mnHeightMatch < nHeightMatch )
1496 rStatus.mnHeightMatch = nHeightMatch;
1497 rStatus.mnWidthMatch = nWidthMatch;
1498 return true;
1501 if( rStatus.mnWidthMatch > nWidthMatch )
1502 return false;
1504 rStatus.mnWidthMatch = nWidthMatch;
1505 return true;
1508 // =======================================================================
1510 ImplFontEntry::ImplFontEntry( const ImplFontSelectData& rFontSelData )
1511 : maFontSelData( rFontSelData ),
1512 maMetric( rFontSelData ),
1513 mpConversion( NULL ),
1514 mnRefCount( 1 ),
1515 mnSetFontFlags( 0 ),
1516 mnOwnOrientation( 0 ),
1517 mnOrientation( 0 ),
1518 mbInit( false ),
1519 mpUnicodeFallbackList( NULL )
1521 maFontSelData.mpFontEntry = this;
1524 // -----------------------------------------------------------------------
1526 ImplFontEntry::~ImplFontEntry()
1528 delete mpUnicodeFallbackList;
1531 // -----------------------------------------------------------------------
1533 inline void ImplFontEntry::AddFallbackForUnicode( sal_UCS4 cChar, const String& rFontName )
1535 if( !mpUnicodeFallbackList )
1536 mpUnicodeFallbackList = new UnicodeFallbackList;
1537 (*mpUnicodeFallbackList)[cChar] = rFontName;
1540 // -----------------------------------------------------------------------
1542 inline bool ImplFontEntry::GetFallbackForUnicode( sal_UCS4 cChar, String* pFontName ) const
1544 if( !mpUnicodeFallbackList )
1545 return false;
1547 UnicodeFallbackList::const_iterator it = mpUnicodeFallbackList->find( cChar );
1548 if( it == mpUnicodeFallbackList->end() )
1549 return false;
1551 *pFontName = (*it).second;
1552 return true;
1555 // -----------------------------------------------------------------------
1557 inline void ImplFontEntry::IgnoreFallbackForUnicode( sal_UCS4 cChar, const String& rFontName )
1559 // DBG_ASSERT( mpUnicodeFallbackList, "ImplFontEntry::IgnoreFallbackForUnicode no list" );
1560 UnicodeFallbackList::iterator it = mpUnicodeFallbackList->find( cChar );
1561 // DBG_ASSERT( it != mpUnicodeFallbackList->end(), "ImplFontEntry::IgnoreFallbackForUnicode no match" );
1562 if( it == mpUnicodeFallbackList->end() )
1563 return;
1564 if( (*it).second == rFontName )
1565 mpUnicodeFallbackList->erase( it );
1568 // =======================================================================
1570 ImplDevFontListData::ImplDevFontListData( const String& rSearchName )
1571 : mpFirst( NULL ),
1572 maSearchName( rSearchName ),
1573 mnTypeFaces( 0 ),
1574 mnMatchType( 0 ),
1575 meMatchWeight( WEIGHT_DONTKNOW ),
1576 meMatchWidth( WIDTH_DONTKNOW ),
1577 meFamily( FAMILY_DONTKNOW ),
1578 mePitch( PITCH_DONTKNOW ),
1579 mnMinQuality( -1 )
1582 // -----------------------------------------------------------------------
1584 ImplDevFontListData::~ImplDevFontListData()
1586 // release all physical font faces
1587 while( mpFirst )
1589 ImplFontData* pFace = mpFirst;
1590 mpFirst = pFace->GetNextFace();
1591 delete pFace;
1595 // -----------------------------------------------------------------------
1597 bool ImplDevFontListData::AddFontFace( ImplFontData* pNewData )
1599 pNewData->mpNext = NULL;
1601 if( !mpFirst )
1603 maName = pNewData->maName;
1604 maMapNames = pNewData->maMapNames;
1605 meFamily = pNewData->meFamily;
1606 mePitch = pNewData->mePitch;
1607 mnMinQuality = pNewData->mnQuality;
1609 else
1611 if( meFamily == FAMILY_DONTKNOW )
1612 meFamily = pNewData->meFamily;
1613 if( mePitch == PITCH_DONTKNOW )
1614 mePitch = pNewData->mePitch;
1615 if( mnMinQuality > pNewData->mnQuality )
1616 mnMinQuality = pNewData->mnQuality;
1619 // set attributes for attribute based font matching
1620 if( pNewData->IsScalable() )
1621 mnTypeFaces |= IMPL_DEVFONT_SCALABLE;
1623 if( pNewData->IsSymbolFont() )
1624 mnTypeFaces |= IMPL_DEVFONT_SYMBOL;
1625 else
1626 mnTypeFaces |= IMPL_DEVFONT_NONESYMBOL;
1628 if( pNewData->meWeight != WEIGHT_DONTKNOW )
1630 if( pNewData->meWeight >= WEIGHT_SEMIBOLD )
1631 mnTypeFaces |= IMPL_DEVFONT_BOLD;
1632 else if( pNewData->meWeight <= WEIGHT_SEMILIGHT )
1633 mnTypeFaces |= IMPL_DEVFONT_LIGHT;
1634 else
1635 mnTypeFaces |= IMPL_DEVFONT_NORMAL;
1638 if( pNewData->meItalic == ITALIC_NONE )
1639 mnTypeFaces |= IMPL_DEVFONT_NONEITALIC;
1640 else if( (pNewData->meItalic == ITALIC_NORMAL)
1641 || (pNewData->meItalic == ITALIC_OBLIQUE) )
1642 mnTypeFaces |= IMPL_DEVFONT_ITALIC;
1644 if( (meMatchWeight == WEIGHT_DONTKNOW)
1645 || (meMatchWidth == WIDTH_DONTKNOW)
1646 || (mnMatchType == 0) )
1648 // TODO: is it cheaper to calc matching attributes now or on demand?
1649 // calc matching attributes if other entries are already initialized
1651 // MT: Perform05: Do lazy, quite expensive, not needed in start-up!
1652 // const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
1653 // InitMatchData( rFontSubst, maSearchName );
1654 // mbMatchData=true; // Somewhere else???
1657 // reassign name (sharing saves memory)
1658 if( pNewData->maName == maName )
1659 pNewData->maName = maName;
1661 // insert new physical font face into linked list
1662 // TODO: get rid of linear search?
1663 ImplFontData* pData;
1664 ImplFontData** ppHere = &mpFirst;
1665 for(; (pData=*ppHere) != NULL; ppHere=&pData->mpNext )
1667 StringCompare eComp = pNewData->CompareWithSize( *pData );
1668 if( eComp == COMPARE_GREATER )
1669 continue;
1670 if( eComp == COMPARE_LESS )
1671 break;
1673 // ignore duplicate if its quality is worse
1674 if( pNewData->mnQuality < pData->mnQuality )
1675 return false;
1677 // keep the device font if its quality is good enough
1678 if( (pNewData->mnQuality == pData->mnQuality)
1679 && (pData->mbDevice || !pNewData->mbDevice) )
1680 return false;
1682 // replace existing font face with a better one
1683 pNewData->mpNext = pData->mpNext;
1684 *ppHere = pNewData;
1685 delete pData;
1686 return true;
1689 // insert into or append to list of physical font faces
1690 pNewData->mpNext = pData;
1691 *ppHere = pNewData;
1692 return true;
1695 // -----------------------------------------------------------------------
1697 // get font attributes using the normalized font family name
1698 void ImplDevFontListData::InitMatchData( const vcl::FontSubstConfiguration& rFontSubst,
1699 const String& rSearchName )
1701 String aShortName;
1702 // get font attributes from the decorated font name
1703 rFontSubst.getMapName( rSearchName, aShortName, maMatchFamilyName,
1704 meMatchWeight, meMatchWidth, mnMatchType );
1705 const FontNameAttr* pFontAttr = rFontSubst.getSubstInfo( rSearchName );
1706 // eventually use the stripped name
1707 if( !pFontAttr )
1708 if( aShortName != rSearchName )
1709 pFontAttr = rFontSubst.getSubstInfo( aShortName );
1710 ImplCalcType( mnMatchType, meMatchWeight, meMatchWidth, meFamily, pFontAttr );
1711 mnMatchType |= ImplIsCJKFont( maName );
1714 // -----------------------------------------------------------------------
1716 ImplFontData* ImplDevFontListData::FindBestFontFace( const ImplFontSelectData& rFSD ) const
1718 if( !mpFirst )
1719 return NULL;
1720 if( !mpFirst->GetNextFace() )
1721 return mpFirst;
1723 // FontName+StyleName should map to FamilyName+StyleName
1724 const String& rSearchName = rFSD.maTargetName;
1725 const xub_Unicode* pTargetStyleName = NULL;
1726 if( (rSearchName.Len() > maSearchName.Len())
1727 && rSearchName.Equals( maSearchName, 0, maSearchName.Len() ) )
1728 pTargetStyleName = rSearchName.GetBuffer() + maSearchName.Len() + 1;
1730 // linear search, TODO: improve?
1731 ImplFontData* pFontFace = mpFirst;
1732 ImplFontData* pBestFontFace = pFontFace;
1733 FontMatchStatus aFontMatchStatus = {0,0,0, pTargetStyleName};
1734 for(; pFontFace; pFontFace = pFontFace->GetNextFace() )
1735 if( pFontFace->IsBetterMatch( rFSD, aFontMatchStatus ) )
1736 pBestFontFace = pFontFace;
1738 return pBestFontFace;
1741 // -----------------------------------------------------------------------
1743 // update device font list with unique font faces, with uniqueness
1744 // meaning different font attributes, but not different fonts sizes
1745 void ImplDevFontListData::UpdateDevFontList( ImplGetDevFontList& rDevFontList ) const
1747 ImplFontData* pPrevFace = NULL;
1748 for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() )
1750 if( !pPrevFace || pFace->CompareIgnoreSize( *pPrevFace ) )
1751 rDevFontList.Add( pFace );
1752 pPrevFace = pFace;
1756 // -----------------------------------------------------------------------
1758 void ImplDevFontListData::GetFontHeights( std::set<int>& rHeights ) const
1760 // add all available font heights
1761 for( const ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() )
1762 rHeights.insert( pFace->GetHeight() );
1765 // -----------------------------------------------------------------------
1767 void ImplDevFontListData::UpdateCloneFontList( ImplDevFontList& rDevFontList,
1768 bool bScalable, bool bEmbeddable ) const
1770 for( ImplFontData* pFace = mpFirst; pFace; pFace = pFace->GetNextFace() )
1772 if( bScalable && !pFace->IsScalable() )
1773 continue;
1774 if( bEmbeddable && !pFace->IsEmbeddable() && !pFace->IsSubsettable() )
1775 continue;
1777 ImplFontData* pClonedFace = pFace->Clone();
1778 rDevFontList.Add( pClonedFace );
1782 // =======================================================================
1784 ImplDevFontList::ImplDevFontList()
1785 : mbMatchData( false )
1786 , mbMapNames( false )
1787 , mpPreMatchHook( NULL )
1788 , mpFallbackHook( NULL )
1789 , mpFallbackList( NULL )
1790 , mnFallbackCount( -1 )
1793 // -----------------------------------------------------------------------
1795 ImplDevFontList::~ImplDevFontList()
1797 Clear();
1800 // -----------------------------------------------------------------------
1802 void ImplDevFontList::SetPreMatchHook( ImplPreMatchFontSubstitution* pHook )
1804 mpPreMatchHook = pHook;
1807 // -----------------------------------------------------------------------
1809 void ImplDevFontList::SetFallbackHook( ImplGlyphFallbackFontSubstitution* pHook )
1811 mpFallbackHook = pHook;
1814 // -----------------------------------------------------------------------
1816 void ImplDevFontList::Clear()
1818 // remove fallback lists
1819 delete[] mpFallbackList;
1820 mpFallbackList = NULL;
1821 mnFallbackCount = -1;
1823 // clear all entries in the device font list
1824 DevFontList::iterator it = maDevFontList.begin();
1825 for(; it != maDevFontList.end(); ++it )
1827 ImplDevFontListData* pEntry = (*it).second;
1828 delete pEntry;
1831 maDevFontList.clear();
1833 // match data must be recalculated too
1834 mbMatchData = false;
1837 // -----------------------------------------------------------------------
1839 // TODO: use a more generic String hash
1840 int FontNameHash::operator()( const String& rStr ) const
1842 // this simple hash just has to be good enough for font names
1843 int nHash = 0;
1844 const int nLen = rStr.Len();
1845 const sal_Unicode* p = rStr.GetBuffer();
1846 switch( nLen )
1848 default: nHash = (p[0]<<16) - (p[1]<<8) + p[2];
1849 nHash += nLen;
1850 p += nLen - 3;
1851 // fall through
1852 case 3: nHash += (p[2]<<16); // fall through
1853 case 2: nHash += (p[1]<<8); // fall through
1854 case 1: nHash += p[0]; // fall through
1855 case 0: break;
1858 return nHash;
1861 // -----------------------------------------------------------------------
1863 void ImplDevFontList::InitGenericGlyphFallback( void ) const
1865 // normalized family names of fonts suited for glyph fallback
1866 // if a font is available related fonts can be ignored
1867 // TODO: implement dynamic lists
1868 static const char* aGlyphFallbackList[] = {
1869 // empty strings separate the names of unrelated fonts
1870 "eudc", "",
1871 "arialunicodems", "cyberbit", "code2000", "",
1872 "andalesansui", "",
1873 "starsymbol", "opensymbol", "",
1874 "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "",
1875 "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "",
1876 "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "",
1877 "tahoma", "dejavusans", "timesnewroman", "lucidatypewriter", "lucidasans", "nimbussansl", "",
1878 "shree", "mangal", "",
1879 "raavi", "shruti", "tunga", "",
1880 "latha", "gautami", "kartika", "vrinda", "",
1881 "shayyalmt", "naskmt", "",
1882 "david", "nachlieli", "lucidagrande", "",
1883 "norasi", "angsanaupc", "",
1884 "khmerossystem", "",
1885 "muktinarrow", "",
1886 "phetsarathot", "",
1887 "padauk", "pinlonmyanmar", "",
1891 bool bHasEudc = false;
1892 int nMaxLevel = 0;
1893 int nBestQuality = 0;
1894 ImplDevFontListData** pFallbackList = NULL;
1895 for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames )
1897 // advance to next sub-list when end-of-sublist marker
1898 if( !**ppNames ) // #i46456# check for empty string, i.e., deref string itself not only ptr to it
1900 if( nBestQuality > 0 )
1901 if( ++nMaxLevel >= MAX_FALLBACK )
1902 break;
1903 if( !ppNames[1] )
1904 break;
1905 nBestQuality = 0;
1906 continue;
1909 // test if the glyph fallback candidate font is available and scalable
1910 String aTokenName( *ppNames, RTL_TEXTENCODING_UTF8 );
1911 ImplDevFontListData* pFallbackFont = FindFontFamily( aTokenName );
1912 if( !pFallbackFont )
1913 continue;
1914 if( !pFallbackFont->IsScalable() )
1915 continue;
1917 // keep the best font of the glyph fallback sub-list
1918 if( nBestQuality < pFallbackFont->GetMinQuality() )
1920 nBestQuality = pFallbackFont->GetMinQuality();
1921 // store available glyph fallback fonts
1922 if( !pFallbackList )
1923 pFallbackList = new ImplDevFontListData*[ MAX_FALLBACK ];
1924 pFallbackList[ nMaxLevel ] = pFallbackFont;
1925 if( !bHasEudc && !nMaxLevel )
1926 bHasEudc = !strncmp( *ppNames, "eudc", 5 );
1930 // sort the list of fonts for glyph fallback by quality (highest first)
1931 // #i33947# keep the EUDC font at the front of the list
1932 // an insertion sort is good enough for this short list
1933 const int nSortStart = bHasEudc ? 1 : 0;
1934 for( int i = nSortStart+1, j; i < nMaxLevel; ++i )
1936 ImplDevFontListData* pTestFont = pFallbackList[ i ];
1937 int nTestQuality = pTestFont->GetMinQuality();
1938 for( j = i; --j >= nSortStart; )
1939 if( nTestQuality > pFallbackList[j]->GetMinQuality() )
1940 pFallbackList[ j+1 ] = pFallbackList[ j ];
1941 else
1942 break;
1943 pFallbackList[ j+1 ] = pTestFont;
1946 #if defined(HDU_DEBUG)
1947 for( int i = 0; i < nMaxLevel; ++i )
1949 ImplDevFontListData* pFont = pFallbackList[ i ];
1950 ByteString aFontName( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 );
1951 fprintf( stderr, "GlyphFallbackFont[%d] (quality=%05d): \"%s\"\n",
1952 i, pFont->GetMinQuality(), aFontName.GetBuffer() );
1954 #endif
1956 mnFallbackCount = nMaxLevel;
1957 mpFallbackList = pFallbackList;
1960 // -----------------------------------------------------------------------
1962 ImplDevFontListData* ImplDevFontList::GetGlyphFallbackFont( ImplFontSelectData& rFontSelData,
1963 rtl::OUString& rMissingCodes, int nFallbackLevel ) const
1965 ImplDevFontListData* pFallbackData = NULL;
1967 // find a matching font candidate for platform specific glyph fallback
1968 if( mpFallbackHook )
1970 // check cache for the first matching entry
1971 // to avoid calling the expensive fallback hook (#i83491#)
1972 sal_UCS4 cChar = 0;
1973 bool bCached = true;
1974 sal_Int32 nStrIndex = 0;
1975 while( nStrIndex < rMissingCodes.getLength() )
1977 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
1978 bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, &rFontSelData.maSearchName );
1979 // ignore entries which don't have a fallback
1980 if( !bCached || (rFontSelData.maSearchName.Len() != 0) )
1981 break;
1984 if( bCached )
1986 // there is a matching fallback in the cache
1987 // so update rMissingCodes with codepoints not yet resolved by this fallback
1988 int nRemainingLength = 0;
1989 sal_UCS4* pRemainingCodes = (sal_UCS4*)alloca( rMissingCodes.getLength() * sizeof(sal_UCS4) );
1990 String aFontName;
1991 while( nStrIndex < rMissingCodes.getLength() )
1993 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
1994 bCached = rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, &aFontName );
1995 if( !bCached || (rFontSelData.maSearchName != aFontName) )
1996 pRemainingCodes[ nRemainingLength++ ] = cChar;
1998 rMissingCodes = rtl::OUString( pRemainingCodes, nRemainingLength );
2000 else
2002 rtl::OUString aOldMissingCodes = rMissingCodes;
2003 // call the hook to query the best matching glyph fallback font
2004 if( mpFallbackHook->FindFontSubstitute( rFontSelData, rMissingCodes ) )
2005 // apply outdev3.cxx specific fontname normalization
2006 ImplGetEnglishSearchFontName( rFontSelData.maSearchName );
2007 else
2008 rFontSelData.maSearchName = String();
2010 // cache the result even if there was no match
2011 for(;;)
2013 if( !rFontSelData.mpFontEntry->GetFallbackForUnicode( cChar, &rFontSelData.maSearchName ) )
2014 rFontSelData.mpFontEntry->AddFallbackForUnicode( cChar, rFontSelData.maSearchName );
2015 if( nStrIndex >= aOldMissingCodes.getLength() )
2016 break;
2017 cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex );
2019 if( rFontSelData.maSearchName.Len() != 0 )
2021 // remove cache entries that were still not resolved
2022 for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
2024 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
2025 rFontSelData.mpFontEntry->IgnoreFallbackForUnicode( cChar, rFontSelData.maSearchName );
2030 // find the matching device font
2031 if( rFontSelData.maSearchName.Len() != 0 )
2032 pFallbackData = FindFontFamily( rFontSelData.maSearchName );
2035 // else find a matching font candidate for generic glyph fallback
2036 if( !pFallbackData )
2038 // initialize font candidates for generic glyph fallback if needed
2039 if( mnFallbackCount < 0 )
2040 InitGenericGlyphFallback();
2041 // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook
2042 if( nFallbackLevel < mnFallbackCount )
2043 pFallbackData = mpFallbackList[ nFallbackLevel ];
2046 return pFallbackData;
2049 // -----------------------------------------------------------------------
2051 void ImplDevFontList::Add( ImplFontData* pNewData )
2053 int nAliasQuality = pNewData->mnQuality - 100;
2054 String aMapNames = pNewData->maMapNames;
2055 pNewData->maMapNames = String();
2057 bool bKeepNewData = false;
2058 for( xub_StrLen nMapNameIndex = 0; nMapNameIndex != STRING_NOTFOUND; )
2060 String aSearchName = pNewData->maName;
2061 ImplGetEnglishSearchFontName( aSearchName );
2063 DevFontList::const_iterator it = maDevFontList.find( aSearchName );
2064 ImplDevFontListData* pFoundData = NULL;
2065 if( it != maDevFontList.end() )
2066 pFoundData = (*it).second;
2068 if( !pFoundData )
2070 pFoundData = new ImplDevFontListData( aSearchName );
2071 maDevFontList[ aSearchName ] = pFoundData;
2074 bKeepNewData = pFoundData->AddFontFace( pNewData );
2076 // add font alias if available
2077 // a font alias should never win against an original font with similar quality
2078 if( aMapNames.Len() >= nMapNameIndex )
2079 break;
2080 if( bKeepNewData ) // try to recycle obsoleted object
2081 pNewData = pNewData->CreateAlias();
2082 bKeepNewData = false;
2083 pNewData->mnQuality = nAliasQuality;
2084 pNewData->maName = GetNextFontToken( aMapNames, nMapNameIndex );
2087 if( !bKeepNewData )
2088 delete pNewData;
2091 // -----------------------------------------------------------------------
2093 // find the font from the normalized font family name
2094 ImplDevFontListData* ImplDevFontList::ImplFindBySearchName( const String& rSearchName ) const
2096 #ifdef DEBUG
2097 String aTempName = rSearchName;
2098 ImplGetEnglishSearchFontName( aTempName );
2099 DBG_ASSERT( aTempName == rSearchName, "ImplDevFontList::ImplFindBySearchName() called with non-normalized name" );
2100 #endif
2102 DevFontList::const_iterator it = maDevFontList.find( rSearchName );
2103 if( it == maDevFontList.end() )
2104 return NULL;
2106 ImplDevFontListData* pFoundData = (*it).second;
2107 return pFoundData;
2110 // -----------------------------------------------------------------------
2112 ImplDevFontListData* ImplDevFontList::ImplFindByAliasName( const String& rSearchName, const String& rShortName ) const
2114 // short circuit for impossible font name alias
2115 if( !rSearchName.Len() )
2116 return NULL;
2118 // short circuit if no alias names are available
2119 if( !mbMapNames )
2120 return NULL;
2122 // use the font's alias names to find the font
2123 // TODO: get rid of linear search
2124 DevFontList::const_iterator it = maDevFontList.begin();
2125 while( it != maDevFontList.end() )
2127 ImplDevFontListData* pData = (*it).second;
2128 if( !pData->maMapNames.Len() )
2129 continue;
2131 // if one alias name matches we found a matching font
2132 String aTempName;
2133 xub_StrLen nIndex = 0;
2136 aTempName = GetNextFontToken( pData->maMapNames, nIndex );
2137 // Test, if the Font name match with one of the mapping names
2138 if ( (aTempName == rSearchName) || (aTempName == rShortName) )
2139 return pData;
2141 while ( nIndex != STRING_NOTFOUND );
2144 return NULL;
2147 // -----------------------------------------------------------------------
2149 ImplDevFontListData* ImplDevFontList::FindFontFamily( const String& rFontName ) const
2151 // normalize the font fomily name and
2152 String aName = rFontName;
2153 ImplGetEnglishSearchFontName( aName );
2154 ImplDevFontListData* pFound = ImplFindBySearchName( aName );
2155 return pFound;
2158 // -----------------------------------------------------------------------
2160 ImplDevFontListData* ImplDevFontList::ImplFindByTokenNames( const String& rTokenStr ) const
2162 ImplDevFontListData* pFoundData = NULL;
2164 // use normalized font name tokens to find the font
2165 for( xub_StrLen nTokenPos = 0; nTokenPos != STRING_NOTFOUND; )
2167 String aSearchName = GetNextFontToken( rTokenStr, nTokenPos );
2168 if( !aSearchName.Len() )
2169 continue;
2170 ImplGetEnglishSearchFontName( aSearchName );
2171 pFoundData = ImplFindBySearchName( aSearchName );
2172 if( pFoundData )
2173 break;
2176 return pFoundData;
2179 // -----------------------------------------------------------------------
2181 ImplDevFontListData* ImplDevFontList::ImplFindBySubstFontAttr( const vcl::FontNameAttr& rFontAttr ) const
2183 ImplDevFontListData* pFoundData = NULL;
2185 // use the font substitutions suggested by the FontNameAttr to find the font
2186 ::std::vector< String >::const_iterator it = rFontAttr.Substitutions.begin();
2187 for(; it != rFontAttr.Substitutions.end(); ++it )
2189 String aSearchName( *it );
2190 ImplGetEnglishSearchFontName( aSearchName );
2192 pFoundData = ImplFindBySearchName( aSearchName );
2193 if( pFoundData )
2194 break;
2197 return pFoundData;
2200 // -----------------------------------------------------------------------
2202 void ImplDevFontList::InitMatchData() const
2204 // short circuit if already done
2205 if( mbMatchData )
2206 return;
2207 mbMatchData = true;
2209 // calculate MatchData for all entries
2210 const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
2212 DevFontList::const_iterator it = maDevFontList.begin();
2213 for(; it != maDevFontList.end(); ++it )
2215 const String& rSearchName = (*it).first;
2216 ImplDevFontListData* pEntry = (*it).second;
2218 pEntry->InitMatchData( rFontSubst, rSearchName );
2222 // -----------------------------------------------------------------------
2224 ImplDevFontListData* ImplDevFontList::ImplFindByAttributes( ULONG nSearchType,
2225 FontWeight eSearchWeight, FontWidth eSearchWidth, FontFamily /*eSearchFamily*/,
2226 FontItalic eSearchItalic, const String& rSearchFamilyName ) const
2228 if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) )
2229 nSearchType |= IMPL_FONT_ATTR_ITALIC;
2231 // don't bother to match attributes if the attributes aren't worth matching
2232 if( !nSearchType
2233 && ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL))
2234 && ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) )
2235 return NULL;
2237 InitMatchData();
2238 ImplDevFontListData* pFoundData = NULL;
2240 long nTestMatch;
2241 long nBestMatch = 40000;
2242 ULONG nBestType = 0;
2244 DevFontList::const_iterator it = maDevFontList.begin();
2245 for(; it != maDevFontList.end(); ++it )
2247 ImplDevFontListData* pData = (*it).second;
2249 // Get all information about the matching font
2250 ULONG nMatchType = pData->mnMatchType;
2251 FontWeight eMatchWeight= pData->meMatchWeight;
2252 FontWidth eMatchWidth = pData->meMatchWidth;
2254 // Calculate Match Value
2255 // 1000000000
2256 // 100000000
2257 // 10000000 CJK, CTL, None-Latin, Symbol
2258 // 1000000 FamilyName, Script, Fixed, -Special, -Decorative,
2259 // Titling, Capitals, Outline, Shadow
2260 // 100000 Match FamilyName, Serif, SansSerif, Italic,
2261 // Width, Weight
2262 // 10000 Scalable, Standard, Default,
2263 // full, Normal, Knownfont,
2264 // Otherstyle, +Special, +Decorative,
2265 // 1000 Typewriter, Rounded, Gothic, Schollbook
2266 // 100
2267 nTestMatch = 0;
2269 // test CJK script attributes
2270 if ( nSearchType & IMPL_FONT_ATTR_CJK )
2272 // Matching language
2273 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_CJK_ALLLANG) )
2274 nTestMatch += 10000000*3;
2275 if( nMatchType & IMPL_FONT_ATTR_CJK )
2276 nTestMatch += 10000000*2;
2277 if( nMatchType & IMPL_FONT_ATTR_FULL )
2278 nTestMatch += 10000000;
2280 else if ( nMatchType & IMPL_FONT_ATTR_CJK )
2281 nTestMatch -= 10000000;
2283 // test CTL script attributes
2284 if( nSearchType & IMPL_FONT_ATTR_CTL )
2286 if( nMatchType & IMPL_FONT_ATTR_CTL )
2287 nTestMatch += 10000000*2;
2288 if( nMatchType & IMPL_FONT_ATTR_FULL )
2289 nTestMatch += 10000000;
2291 else if ( nMatchType & IMPL_FONT_ATTR_CTL )
2292 nTestMatch -= 10000000;
2294 // test LATIN script attributes
2295 if( nSearchType & IMPL_FONT_ATTR_NONELATIN )
2297 if( nMatchType & IMPL_FONT_ATTR_NONELATIN )
2298 nTestMatch += 10000000*2;
2299 if( nMatchType & IMPL_FONT_ATTR_FULL )
2300 nTestMatch += 10000000;
2303 // test SYMBOL attributes
2304 if ( nSearchType & IMPL_FONT_ATTR_SYMBOL )
2306 const String& rSearchName = it->first;
2307 // prefer some special known symbol fonts
2308 if ( rSearchName.EqualsAscii( "starsymbol" ) )
2309 nTestMatch += 10000000*6+(10000*3);
2310 else if ( rSearchName.EqualsAscii( "opensymbol" ) )
2311 nTestMatch += 10000000*6;
2312 else if ( rSearchName.EqualsAscii( "starbats" )
2313 || rSearchName.EqualsAscii( "wingdings" )
2314 || rSearchName.EqualsAscii( "monotypesorts" )
2315 || rSearchName.EqualsAscii( "dingbats" )
2316 || rSearchName.EqualsAscii( "zapfdingbats" ) )
2317 nTestMatch += 10000000*5;
2318 else if ( pData->mnTypeFaces & IMPL_DEVFONT_SYMBOL )
2319 nTestMatch += 10000000*4;
2320 else
2322 if( nMatchType & IMPL_FONT_ATTR_SYMBOL )
2323 nTestMatch += 10000000*2;
2324 if( nMatchType & IMPL_FONT_ATTR_FULL )
2325 nTestMatch += 10000000;
2328 else if ( (pData->mnTypeFaces & (IMPL_DEVFONT_SYMBOL | IMPL_DEVFONT_NONESYMBOL)) == IMPL_DEVFONT_SYMBOL )
2329 nTestMatch -= 10000000;
2330 else if ( nMatchType & IMPL_FONT_ATTR_SYMBOL )
2331 nTestMatch -= 10000;
2333 // match stripped family name
2334 if( rSearchFamilyName.Len() && (rSearchFamilyName == pData->maMatchFamilyName) )
2335 nTestMatch += 1000000*3;
2337 // match ALLSCRIPT? attribute
2338 if( nSearchType & IMPL_FONT_ATTR_ALLSCRIPT )
2340 if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT )
2341 nTestMatch += 1000000*2;
2342 if( nSearchType & IMPL_FONT_ATTR_ALLSUBSCRIPT )
2344 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ALLSUBSCRIPT) )
2345 nTestMatch += 1000000*2;
2346 if( 0 != ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_BRUSHSCRIPT) )
2347 nTestMatch -= 1000000;
2350 else if( nMatchType & IMPL_FONT_ATTR_ALLSCRIPT )
2351 nTestMatch -= 1000000;
2353 // test MONOSPACE+TYPEWRITER attributes
2354 if( nSearchType & IMPL_FONT_ATTR_FIXED )
2356 if( nMatchType & IMPL_FONT_ATTR_FIXED )
2357 nTestMatch += 1000000*2;
2358 // a typewriter attribute is even better
2359 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) )
2360 nTestMatch += 10000*2;
2362 else if( nMatchType & IMPL_FONT_ATTR_FIXED )
2363 nTestMatch -= 1000000;
2365 // test SPECIAL attribute
2366 if( nSearchType & IMPL_FONT_ATTR_SPECIAL )
2368 if( nMatchType & IMPL_FONT_ATTR_SPECIAL )
2369 nTestMatch += 10000;
2370 else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) )
2372 if( nMatchType & IMPL_FONT_ATTR_SERIF )
2373 nTestMatch += 1000*2;
2374 else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
2375 nTestMatch += 1000;
2378 else if( (nMatchType & IMPL_FONT_ATTR_SPECIAL) && !(nSearchType & IMPL_FONT_ATTR_SYMBOL) )
2379 nTestMatch -= 1000000;
2381 // test DECORATIVE attribute
2382 if( nSearchType & IMPL_FONT_ATTR_DECORATIVE )
2384 if( nMatchType & IMPL_FONT_ATTR_DECORATIVE )
2385 nTestMatch += 10000;
2386 else if( !(nSearchType & IMPL_FONT_ATTR_ALLSERIFSTYLE) )
2388 if( nMatchType & IMPL_FONT_ATTR_SERIF )
2389 nTestMatch += 1000*2;
2390 else if ( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
2391 nTestMatch += 1000;
2394 else if( nMatchType & IMPL_FONT_ATTR_DECORATIVE )
2395 nTestMatch -= 1000000;
2397 // test TITLE+CAPITALS attributes
2398 if( nSearchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) )
2400 if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) )
2401 nTestMatch += 1000000*2;
2402 if( 0 == ((nSearchType^nMatchType) & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS)))
2403 nTestMatch += 1000000;
2404 else if( (nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS))
2405 && (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) )
2406 nTestMatch += 1000000;
2408 else if( nMatchType & (IMPL_FONT_ATTR_TITLING | IMPL_FONT_ATTR_CAPITALS) )
2409 nTestMatch -= 1000000;
2411 // test OUTLINE+SHADOW attributes
2412 if( nSearchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) )
2414 if( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) )
2415 nTestMatch += 1000000*2;
2416 if( 0 == ((nSearchType ^ nMatchType) & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW)) )
2417 nTestMatch += 1000000;
2418 else if( (nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW))
2419 && (nMatchType & (IMPL_FONT_ATTR_STANDARD | IMPL_FONT_ATTR_DEFAULT)) )
2420 nTestMatch += 1000000;
2422 else if ( nMatchType & (IMPL_FONT_ATTR_OUTLINE | IMPL_FONT_ATTR_SHADOW) )
2423 nTestMatch -= 1000000;
2425 // test font name substrings
2426 if( (rSearchFamilyName.Len() && pData->maMatchFamilyName.Len())
2427 && ((rSearchFamilyName.Search( pData->maMatchFamilyName ) != STRING_NOTFOUND)
2428 || (pData->maMatchFamilyName.Search( rSearchFamilyName ) != STRING_NOTFOUND)) )
2429 nTestMatch += 100000*2;
2431 // test SERIF attribute
2432 if( nSearchType & IMPL_FONT_ATTR_SERIF )
2434 if( nMatchType & IMPL_FONT_ATTR_SERIF )
2435 nTestMatch += 1000000*2;
2436 else if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
2437 nTestMatch -= 1000000;
2440 // test SANSERIF attribute
2441 if( nSearchType & IMPL_FONT_ATTR_SANSSERIF )
2443 if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
2444 nTestMatch += 1000000;
2445 else if ( nMatchType & IMPL_FONT_ATTR_SERIF )
2446 nTestMatch -= 1000000;
2449 // test ITALIC attribute
2450 if( nSearchType & IMPL_FONT_ATTR_ITALIC )
2452 if( pData->mnTypeFaces & IMPL_DEVFONT_ITALIC )
2453 nTestMatch += 1000000*3;
2454 if( nMatchType & IMPL_FONT_ATTR_ITALIC )
2455 nTestMatch += 1000000;
2457 else if( !(nSearchType & IMPL_FONT_ATTR_ALLSCRIPT)
2458 && ((nMatchType & IMPL_FONT_ATTR_ITALIC)
2459 || !(pData->mnTypeFaces & IMPL_DEVFONT_NONEITALIC)) )
2460 nTestMatch -= 1000000*2;
2462 // test WIDTH attribute
2463 if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) )
2465 if( eSearchWidth < WIDTH_NORMAL )
2467 if( eSearchWidth == eMatchWidth )
2468 nTestMatch += 1000000*3;
2469 else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) )
2470 nTestMatch += 1000000;
2472 else
2474 if( eSearchWidth == eMatchWidth )
2475 nTestMatch += 1000000*3;
2476 else if( eMatchWidth > WIDTH_NORMAL )
2477 nTestMatch += 1000000;
2480 else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) )
2481 nTestMatch -= 1000000;
2483 // test WEIGHT attribute
2484 if( (eSearchWeight != WEIGHT_DONTKNOW) && (eSearchWeight != WEIGHT_NORMAL) && (eSearchWeight != WEIGHT_MEDIUM) )
2486 if( eSearchWeight < WEIGHT_NORMAL )
2488 if( pData->mnTypeFaces & IMPL_DEVFONT_LIGHT )
2489 nTestMatch += 1000000;
2490 if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) )
2491 nTestMatch += 1000000;
2493 else
2495 if( pData->mnTypeFaces & IMPL_DEVFONT_BOLD )
2496 nTestMatch += 1000000;
2497 if( eMatchWeight > WEIGHT_BOLD )
2498 nTestMatch += 1000000;
2501 else if( ((eMatchWeight != WEIGHT_DONTKNOW) && (eMatchWeight != WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_MEDIUM))
2502 || !(pData->mnTypeFaces & IMPL_DEVFONT_NORMAL) )
2503 nTestMatch -= 1000000;
2505 // prefer scalable fonts
2506 if( pData->mnTypeFaces & IMPL_DEVFONT_SCALABLE )
2507 nTestMatch += 10000*4;
2508 else
2509 nTestMatch -= 10000*4;
2511 // test STANDARD+DEFAULT+FULL+NORMAL attributes
2512 if( nMatchType & IMPL_FONT_ATTR_STANDARD )
2513 nTestMatch += 10000*2;
2514 if( nMatchType & IMPL_FONT_ATTR_DEFAULT )
2515 nTestMatch += 10000;
2516 if( nMatchType & IMPL_FONT_ATTR_FULL )
2517 nTestMatch += 10000;
2518 if( nMatchType & IMPL_FONT_ATTR_NORMAL )
2519 nTestMatch += 10000;
2521 // test OTHERSTYLE attribute
2522 if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE )
2524 if( !(nMatchType & IMPL_FONT_ATTR_OTHERSTYLE) )
2525 nTestMatch -= 10000;
2527 else if( nMatchType & IMPL_FONT_ATTR_OTHERSTYLE )
2528 nTestMatch -= 10000;
2530 // test ROUNDED attribute
2531 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_ROUNDED) )
2532 nTestMatch += 1000;
2534 // test TYPEWRITER attribute
2535 if( 0 == ((nSearchType ^ nMatchType) & IMPL_FONT_ATTR_TYPEWRITER) )
2536 nTestMatch += 1000;
2538 // test GOTHIC attribute
2539 if( nSearchType & IMPL_FONT_ATTR_GOTHIC )
2541 if( nMatchType & IMPL_FONT_ATTR_GOTHIC )
2542 nTestMatch += 1000*3;
2543 if( nMatchType & IMPL_FONT_ATTR_SANSSERIF )
2544 nTestMatch += 1000*2;
2547 // test SCHOOLBOOK attribute
2548 if( nSearchType & IMPL_FONT_ATTR_SCHOOLBOOK )
2550 if( nMatchType & IMPL_FONT_ATTR_SCHOOLBOOK )
2551 nTestMatch += 1000*3;
2552 if( nMatchType & IMPL_FONT_ATTR_SERIF )
2553 nTestMatch += 1000*2;
2556 // compare with best matching font yet
2557 if ( nTestMatch > nBestMatch )
2559 pFoundData = pData;
2560 nBestMatch = nTestMatch;
2561 nBestType = nMatchType;
2563 else if( nTestMatch == nBestMatch )
2565 // some fonts are more suitable defaults
2566 if( nMatchType & IMPL_FONT_ATTR_DEFAULT )
2568 pFoundData = pData;
2569 nBestType = nMatchType;
2571 else if( (nMatchType & IMPL_FONT_ATTR_STANDARD) &&
2572 !(nBestType & IMPL_FONT_ATTR_DEFAULT) )
2574 pFoundData = pData;
2575 nBestType = nMatchType;
2580 return pFoundData;
2583 // -----------------------------------------------------------------------
2585 ImplDevFontListData* ImplDevFontList::FindDefaultFont() const
2587 // try to find one of the default fonts of the
2588 // UNICODE, SANSSERIF, SERIF or FIXED default font lists
2589 const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get();
2590 com::sun::star::lang::Locale aLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
2591 String aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS_UNICODE );
2592 ImplDevFontListData* pFoundData = ImplFindByTokenNames( aFontname );
2593 if( pFoundData )
2594 return pFoundData;
2596 aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS );
2597 pFoundData = ImplFindByTokenNames( aFontname );
2598 if( pFoundData )
2599 return pFoundData;
2601 aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SERIF );
2602 pFoundData = ImplFindByTokenNames( aFontname );
2603 if( pFoundData )
2604 return pFoundData;
2606 aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_FIXED );
2607 pFoundData = ImplFindByTokenNames( aFontname );
2608 if( pFoundData )
2609 return pFoundData;
2611 // now try to find a reasonable non-symbol font
2613 InitMatchData();
2615 DevFontList::const_iterator it = maDevFontList.begin();
2616 for(; it != maDevFontList.end(); ++it )
2618 ImplDevFontListData* pData = (*it).second;
2619 if( pData->mnMatchType & IMPL_FONT_ATTR_SYMBOL )
2620 continue;
2621 pFoundData = pData;
2622 if( pData->mnMatchType & (IMPL_FONT_ATTR_DEFAULT|IMPL_FONT_ATTR_STANDARD) )
2623 break;
2625 if( pFoundData )
2626 return pFoundData;
2628 // finding any font is better than finding no font at all
2629 it = maDevFontList.begin();
2630 if( it != maDevFontList.end() )
2631 pFoundData = (*it).second;
2633 return pFoundData;
2636 // -----------------------------------------------------------------------
2638 ImplDevFontList* ImplDevFontList::Clone( bool bScalable, bool bEmbeddable ) const
2640 ImplDevFontList* pClonedList = new ImplDevFontList;
2641 pClonedList->mbMatchData = mbMatchData;
2642 pClonedList->mbMapNames = mbMapNames;
2643 pClonedList->mpPreMatchHook = mpPreMatchHook;
2644 pClonedList->mpFallbackHook = mpFallbackHook;
2646 DevFontList::const_iterator it = maDevFontList.begin();
2647 for(; it != maDevFontList.end(); ++it )
2649 const ImplDevFontListData* pFontFace = (*it).second;
2650 pFontFace->UpdateCloneFontList( *pClonedList, bScalable, bEmbeddable );
2653 return pClonedList;
2656 // -----------------------------------------------------------------------
2658 ImplGetDevFontList* ImplDevFontList::GetDevFontList() const
2660 ImplGetDevFontList* pGetDevFontList = new ImplGetDevFontList;
2662 DevFontList::const_iterator it = maDevFontList.begin();
2663 for(; it != maDevFontList.end(); ++it )
2665 const ImplDevFontListData* pFontFamily = (*it).second;
2666 pFontFamily->UpdateDevFontList( *pGetDevFontList );
2669 return pGetDevFontList;
2672 // -----------------------------------------------------------------------
2674 ImplGetDevSizeList* ImplDevFontList::GetDevSizeList( const String& rFontName ) const
2676 ImplGetDevSizeList* pGetDevSizeList = new ImplGetDevSizeList( rFontName );
2678 ImplDevFontListData* pFontFamily = FindFontFamily( rFontName );
2679 if( pFontFamily != NULL )
2681 std::set<int> rHeights;
2682 pFontFamily->GetFontHeights( rHeights );
2684 std::set<int>::const_iterator it = rHeights.begin();
2685 for(; it != rHeights.begin(); ++it )
2686 pGetDevSizeList->Add( *it );
2689 return pGetDevSizeList;
2692 // =======================================================================
2694 ImplFontSelectData::ImplFontSelectData( const Font& rFont,
2695 const String& rSearchName, const Size& rSize, float fExactHeight)
2696 : maSearchName( rSearchName ),
2697 mnWidth( rSize.Width() ),
2698 mnHeight( rSize.Height() ),
2699 mfExactHeight( fExactHeight),
2700 mnOrientation( rFont.GetOrientation() ),
2701 meLanguage( rFont.GetLanguage() ),
2702 mbVertical( rFont.IsVertical() ),
2703 mbNonAntialiased( false ),
2704 mpFontData( NULL ),
2705 mpFontEntry( NULL )
2707 maTargetName = maName;
2709 rFont.GetFontAttributes( *this );
2711 // normalize orientation between 0 and 3600
2712 if( 3600 <= (unsigned)mnOrientation )
2714 if( mnOrientation >= 0 )
2715 mnOrientation %= 3600;
2716 else
2717 mnOrientation = 3600 - (-mnOrientation % 3600);
2720 // normalize width and height
2721 if( mnHeight < 0 )
2722 mnHeight = -mnHeight;
2723 if( mnWidth < 0 )
2724 mnWidth = -mnWidth;
2727 // -----------------------------------------------------------------------
2729 ImplFontSelectData::ImplFontSelectData( const ImplFontData& rFontData,
2730 const Size& rSize, float fExactHeight, int nOrientation, bool bVertical )
2731 : ImplFontAttributes( rFontData ),
2732 mnWidth( rSize.Width() ),
2733 mnHeight( rSize.Height() ),
2734 mfExactHeight( fExactHeight ),
2735 mnOrientation( nOrientation ),
2736 meLanguage( 0 ),
2737 mbVertical( bVertical ),
2738 mbNonAntialiased( false ),
2739 mpFontData( &rFontData ),
2740 mpFontEntry( NULL )
2742 maTargetName = maSearchName = maName;
2743 // NOTE: no normalization for width/height/orientation
2746 // =======================================================================
2748 size_t ImplFontCache::IFSD_Hash::operator()( const ImplFontSelectData& rFSD ) const
2750 // TODO: does it pay off to improve this hash function?
2751 static FontNameHash aFontNameHash;
2752 size_t nHash = aFontNameHash( rFSD.maSearchName );
2753 #ifdef ENABLE_GRAPHITE
2754 // check for features and generate a unique hash if necessary
2755 if (rFSD.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2756 != STRING_NOTFOUND)
2758 nHash = aFontNameHash( rFSD.maTargetName );
2760 #endif
2761 nHash += 11 * rFSD.mnHeight;
2762 nHash += 19 * rFSD.meWeight;
2763 nHash += 29 * rFSD.meItalic;
2764 nHash += 37 * rFSD.mnOrientation;
2765 nHash += 41 * rFSD.meLanguage;
2766 if( rFSD.mbVertical )
2767 nHash += 53;
2768 return nHash;
2771 // -----------------------------------------------------------------------
2773 bool ImplFontCache::IFSD_Equal::operator()(const ImplFontSelectData& rA, const ImplFontSelectData& rB) const
2775 // check normalized font family name
2776 if( rA.maSearchName != rB.maSearchName )
2777 return false;
2779 // check font transformation
2780 if( (rA.mnHeight != rB.mnHeight)
2781 || (rA.mnWidth != rB.mnWidth)
2782 || (rA.mnOrientation != rB.mnOrientation) )
2783 return false;
2785 // check mapping relevant attributes
2786 if( (rA.mbVertical != rB.mbVertical)
2787 || (rA.meLanguage != rB.meLanguage) )
2788 return false;
2790 // check font face attributes
2791 if( (rA.meWeight != rB.meWeight)
2792 || (rA.meItalic != rB.meItalic)
2793 // || (rA.meFamily != rB.meFamily) // TODO: remove this mostly obsolete member
2794 || (rA.mePitch != rB.mePitch) )
2795 return false;
2797 // check style name
2798 if( rA.maStyleName != rB.maStyleName)
2799 return false;
2801 // Symbol fonts may recode from one type to another So they are only
2802 // safely equivalent for equal targets
2803 if (
2804 (rA.mpFontData && rA.mpFontData->IsSymbolFont()) ||
2805 (rB.mpFontData && rB.mpFontData->IsSymbolFont())
2808 if (rA.maTargetName != rB.maTargetName)
2809 return false;
2812 #ifdef ENABLE_GRAPHITE
2813 // check for features
2814 if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2815 != STRING_NOTFOUND ||
2816 rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2817 != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
2818 return false;
2819 #endif
2821 return true;
2824 // -----------------------------------------------------------------------
2826 ImplFontCache::ImplFontCache( bool bPrinter )
2827 : mpFirstEntry( NULL ),
2828 mnRef0Count( 0 ),
2829 mbPrinter( bPrinter )
2832 // -----------------------------------------------------------------------
2834 ImplFontCache::~ImplFontCache()
2836 FontInstanceList::iterator it = maFontInstanceList.begin();
2837 for(; it != maFontInstanceList.end(); ++it )
2839 ImplFontEntry* pEntry = (*it).second;
2840 delete pEntry;
2844 // -----------------------------------------------------------------------
2846 ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
2847 const Font& rFont, const Size& rSize, float fExactHeight, ImplDirectFontSubstitution* pDevSpecific )
2849 String aSearchName = rFont.GetName();
2851 // TODO: also add device specific name caching
2852 if( !pDevSpecific )
2854 // check if the requested font name is already known
2855 // if it is already known get its normalized search name
2856 FontNameList::const_iterator it_name = maFontNameList.find( aSearchName );
2857 if( it_name != maFontNameList.end() )
2858 if( !(*it_name).second.EqualsAscii( "hg", 0, 2)
2859 #ifdef ENABLE_GRAPHITE
2860 && (aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
2861 == STRING_NOTFOUND)
2862 #endif
2864 aSearchName = (*it_name).second;
2867 // initialize internal font request object
2868 ImplFontSelectData aFontSelData( rFont, aSearchName, rSize, fExactHeight );
2869 return GetFontEntry( pFontList, aFontSelData, pDevSpecific );
2872 // -----------------------------------------------------------------------
2874 ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
2875 ImplFontSelectData& aFontSelData, ImplDirectFontSubstitution* pDevSpecific )
2877 // check if a directly matching logical font instance is already cached,
2878 // the most recently used font usually has a hit rate of >50%
2879 ImplFontEntry *pEntry = NULL;
2880 ImplDevFontListData* pFontFamily = NULL;
2881 IFSD_Equal aIFSD_Equal;
2882 if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) )
2883 pEntry = mpFirstEntry;
2884 else
2886 FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
2887 if( it != maFontInstanceList.end() )
2888 pEntry = (*it).second;
2891 if( !pEntry ) // no direct cache hit
2893 // find the best matching logical font family and update font selector accordingly
2894 pFontFamily = pFontList->ImplFindByFont( aFontSelData, mbPrinter, pDevSpecific );
2895 DBG_ASSERT( (pFontFamily != NULL), "ImplFontCache::Get() No logical font found!" );
2896 if( pFontFamily )
2897 aFontSelData.maSearchName = pFontFamily->GetSearchName();
2899 // check if an indirectly matching logical font instance is already cached
2900 FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
2901 if( it != maFontInstanceList.end() )
2903 // we have an indirect cache hit
2904 pEntry = (*it).second;
2905 // cache the requested and the selected font names
2906 // => next time there is a good chance for a direct cache hit
2907 // don't allow the cache to grow too big
2908 // TODO: implement some fancy LRU caching?
2909 if( maFontNameList.size() >= 4000 )
2910 maFontNameList.clear();
2911 // TODO: also add device specific name caching
2912 if( !pDevSpecific )
2913 if( aFontSelData.maName != aFontSelData.maSearchName )
2914 maFontNameList[ aFontSelData.maName ] = aFontSelData.maSearchName;
2918 if( pEntry ) // cache hit => use existing font instance
2920 // increase the font instance's reference count
2921 if( !pEntry->mnRefCount++ )
2922 --mnRef0Count;
2924 else // no cache hit => create a new font instance
2926 // find the best matching physical font face
2927 ImplFontData* pFontData = pFontFamily->FindBestFontFace( aFontSelData );
2928 aFontSelData.mpFontData = pFontData;
2930 // create a new logical font instance from this physical font face
2931 pEntry = pFontData->CreateFontInstance( aFontSelData );
2933 // if we found a different symbol font we need a symbol conversion table
2934 if( pFontData->IsSymbolFont() )
2935 if( aFontSelData.maTargetName != aFontSelData.maSearchName )
2936 pEntry->mpConversion = ImplGetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
2938 // add the new entry to the cache
2939 maFontInstanceList[ aFontSelData ] = pEntry;
2942 mpFirstEntry = pEntry;
2943 return pEntry;
2946 // -----------------------------------------------------------------------
2948 ImplDevFontListData* ImplDevFontList::ImplFindByFont( ImplFontSelectData& rFSD,
2949 bool bPrinter, ImplDirectFontSubstitution* pDevSpecific ) const
2951 // give up if no fonts are available
2952 if( !Count() )
2953 return NULL;
2955 // test if a font in the token list is available
2956 // substitute the font if this was requested
2957 USHORT nSubstFlags = FONT_SUBSTITUTE_ALWAYS;
2958 if ( bPrinter )
2959 nSubstFlags |= FONT_SUBSTITUTE_SCREENONLY;
2961 bool bMultiToken = false;
2962 xub_StrLen nTokenPos = 0;
2963 String& aSearchName = rFSD.maSearchName; // TODO: get rid of reference
2964 for(;;)
2966 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
2967 aSearchName = rFSD.maTargetName;
2969 #ifdef ENABLE_GRAPHITE
2970 // Until features are properly supported, they are appended to the
2971 // font name, so we need to strip them off so the font is found.
2972 xub_StrLen nFeat = aSearchName.Search(grutils::GrFeatureParser::FEAT_PREFIX);
2973 String aOrigName = rFSD.maTargetName;
2974 String aBaseFontName(aSearchName, 0, (nFeat != STRING_NOTFOUND)?nFeat:aSearchName.Len());
2975 if (nFeat != STRING_NOTFOUND && STRING_NOTFOUND !=
2976 aSearchName.Search(grutils::GrFeatureParser::FEAT_ID_VALUE_SEPARATOR, nFeat))
2978 aSearchName = aBaseFontName;
2979 rFSD.maTargetName = aBaseFontName;
2982 #endif
2984 ImplGetEnglishSearchFontName( aSearchName );
2985 ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
2986 // #114999# special emboldening for Ricoh fonts
2987 // TODO: smarter check for special cases by using PreMatch infrastructure?
2988 if( (rFSD.meWeight > WEIGHT_MEDIUM)
2989 && aSearchName.EqualsAscii( "hg", 0, 2) )
2991 String aBoldName;
2992 if( aSearchName.EqualsAscii( "hggothicb", 0, 9) )
2993 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hggothice"));
2994 else if( aSearchName.EqualsAscii( "hgpgothicb", 0, 10) )
2995 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpgothice"));
2996 else if( aSearchName.EqualsAscii( "hgminchol", 0, 9) )
2997 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchob"));
2998 else if( aSearchName.EqualsAscii( "hgpminchol", 0, 10) )
2999 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchob"));
3000 else if( aSearchName.EqualsAscii( "hgminchob" ) )
3001 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchoe"));
3002 else if( aSearchName.EqualsAscii( "hgpminchob" ) )
3003 aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchoe"));
3005 if( aBoldName.Len() && ImplFindBySearchName( aBoldName ) )
3007 // the other font is available => use it
3008 aSearchName = aBoldName;
3009 // prevent synthetic emboldening of bold version
3010 rFSD.meWeight = WEIGHT_DONTKNOW;
3014 #ifdef ENABLE_GRAPHITE
3015 // restore the features to make the font selection data unique
3016 rFSD.maTargetName = aOrigName;
3017 #endif
3018 // check if the current font name token or its substitute is valid
3019 ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
3020 if( pFoundData )
3021 return pFoundData;
3023 // some systems provide special customization
3024 // e.g. they suggest "serif" as UI-font, but this name cannot be used directly
3025 // because the system wants to map it to another font first, e.g. "Helvetica"
3026 #ifdef ENABLE_GRAPHITE
3027 // use the target name to search in the prematch hook
3028 rFSD.maTargetName = aBaseFontName;
3029 #endif
3030 if( mpPreMatchHook )
3031 if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
3032 ImplGetEnglishSearchFontName( aSearchName );
3033 #ifdef ENABLE_GRAPHITE
3034 // the prematch hook uses the target name to search, but we now need
3035 // to restore the features to make the font selection data unique
3036 rFSD.maTargetName = aOrigName;
3037 #endif
3038 pFoundData = ImplFindBySearchName( aSearchName );
3039 if( pFoundData )
3040 return pFoundData;
3042 // break after last font name token was checked unsuccessfully
3043 if( nTokenPos == STRING_NOTFOUND)
3044 break;
3045 bMultiToken = true;
3048 // if the first font was not available find the next available font in
3049 // the semicolon separated list of font names. A font is also considered
3050 // available when there is a matching entry in the Tools->Options->Fonts
3051 // dialog witho neither ALWAYS nor SCREENONLY flags set and the substitution
3052 // font is available
3053 for( nTokenPos = 0; nTokenPos != STRING_NOTFOUND; )
3055 if( bMultiToken )
3057 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
3058 aSearchName = rFSD.maTargetName;
3059 ImplGetEnglishSearchFontName( aSearchName );
3061 else
3062 nTokenPos = STRING_NOTFOUND;
3063 if( mpPreMatchHook )
3064 if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
3065 ImplGetEnglishSearchFontName( aSearchName );
3066 ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
3067 ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
3068 if( pFoundData )
3069 return pFoundData;
3072 // if no font with a directly matching name is available use the
3073 // first font name token and get its attributes to find a replacement
3074 if ( bMultiToken )
3076 nTokenPos = 0;
3077 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
3078 aSearchName = rFSD.maTargetName;
3079 ImplGetEnglishSearchFontName( aSearchName );
3082 String aSearchShortName;
3083 String aSearchFamilyName;
3084 FontWeight eSearchWeight = rFSD.meWeight;
3085 FontWidth eSearchWidth = rFSD.meWidthType;
3086 ULONG nSearchType = 0;
3087 FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName,
3088 eSearchWeight, eSearchWidth, nSearchType );
3090 // note: the search name was already translated to english (if possible)
3092 // use the font's shortened name if needed
3093 if ( aSearchShortName != aSearchName )
3095 ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchShortName );
3096 if( pFoundData )
3098 #ifdef UNX
3099 /* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is
3100 a korean bitmap font that is not suitable here. Use the font replacement table,
3101 that automatically leads to the desired "HG Mincho Light J". Same story for
3102 MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */
3103 static String aMS_Mincho( RTL_CONSTASCII_USTRINGPARAM("msmincho") );
3104 static String aMS_Gothic( RTL_CONSTASCII_USTRINGPARAM("msgothic") );
3105 if ((aSearchName != aMS_Mincho) && (aSearchName != aMS_Gothic))
3106 // TODO: add heuristic to only throw out the fake ms* fonts
3107 #endif
3109 return pFoundData;
3114 // use font fallback
3115 const FontNameAttr* pFontAttr = NULL;
3116 if( aSearchName.Len() )
3118 // get fallback info using FontSubstConfiguration and
3119 // the target name, it's shortened name and family name in that order
3120 const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
3121 pFontAttr = rFontSubst.getSubstInfo( aSearchName );
3122 if ( !pFontAttr && (aSearchShortName != aSearchName) )
3123 pFontAttr = rFontSubst.getSubstInfo( aSearchShortName );
3124 if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) )
3125 pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName );
3127 // try the font substitutions suggested by the fallback info
3128 if( pFontAttr )
3130 ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pFontAttr );
3131 if( pFoundData )
3132 return pFoundData;
3136 // if a target symbol font is not available use a default symbol font
3137 if( rFSD.IsSymbolFont() )
3139 com::sun::star::lang::Locale aDefaultLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
3140 aSearchName = DefaultFontConfiguration::get()->getDefaultFont( aDefaultLocale, DEFAULTFONT_SYMBOL );
3141 ImplDevFontListData* pFoundData = ImplFindByTokenNames( aSearchName );
3142 if( pFoundData )
3143 return pFoundData;
3146 // now try the other font name tokens
3147 while( nTokenPos != STRING_NOTFOUND )
3149 rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
3150 if( !rFSD.maTargetName.Len() )
3151 continue;
3153 aSearchName = rFSD.maTargetName;
3154 ImplGetEnglishSearchFontName( aSearchName );
3156 String aTempShortName;
3157 String aTempFamilyName;
3158 ULONG nTempType = 0;
3159 FontWeight eTempWeight = rFSD.meWeight;
3160 FontWidth eTempWidth = WIDTH_DONTKNOW;
3161 FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName,
3162 eTempWeight, eTempWidth, nTempType );
3164 // use a shortend token name if available
3165 if( aTempShortName != aSearchName )
3167 ImplDevFontListData* pFoundData = ImplFindBySearchName( aTempShortName );
3168 if( pFoundData )
3169 return pFoundData;
3172 // use a font name from font fallback list to determine font attributes
3174 // get fallback info using FontSubstConfiguration and
3175 // the target name, it's shortened name and family name in that order
3176 const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
3177 const FontNameAttr* pTempFontAttr = rFontSubst.getSubstInfo( aSearchName );
3178 if ( !pTempFontAttr && (aTempShortName != aSearchName) )
3179 pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName );
3180 if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) )
3181 pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName );
3183 // try the font substitutions suggested by the fallback info
3184 if( pTempFontAttr )
3186 ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pTempFontAttr );
3187 if( pFoundData )
3188 return pFoundData;
3189 if( !pFontAttr )
3190 pFontAttr = pTempFontAttr;
3194 // if still needed use the alias names of the installed fonts
3195 if( mbMapNames )
3197 ImplDevFontListData* pFoundData = ImplFindByAliasName( rFSD.maTargetName, aSearchShortName );
3198 if( pFoundData )
3199 return pFoundData;
3202 // if still needed use the font request's attributes to find a good match
3203 switch( rFSD.meLanguage )
3205 case LANGUAGE_CHINESE:
3206 case LANGUAGE_CHINESE_SIMPLIFIED:
3207 case LANGUAGE_CHINESE_SINGAPORE:
3208 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_SC;
3209 break;
3210 case LANGUAGE_CHINESE_TRADITIONAL:
3211 case LANGUAGE_CHINESE_HONGKONG:
3212 case LANGUAGE_CHINESE_MACAU:
3213 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_TC;
3214 break;
3215 case LANGUAGE_KOREAN:
3216 case LANGUAGE_KOREAN_JOHAB:
3217 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_KR;
3218 break;
3219 case LANGUAGE_JAPANESE:
3220 nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_JP;
3221 break;
3222 default:
3223 nSearchType |= ImplIsCJKFont( rFSD.maName );
3224 if( rFSD.IsSymbolFont() )
3225 nSearchType |= IMPL_FONT_ATTR_SYMBOL;
3226 break;
3229 ImplCalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.meFamily, pFontAttr );
3230 ImplDevFontListData* pFoundData = ImplFindByAttributes( nSearchType,
3231 eSearchWeight, eSearchWidth, rFSD.meFamily, rFSD.meItalic, aSearchFamilyName );
3233 if( pFoundData )
3235 // overwrite font selection attributes using info from the typeface flags
3236 if( (eSearchWeight >= WEIGHT_BOLD)
3237 && (eSearchWeight > rFSD.meWeight)
3238 && (pFoundData->mnTypeFaces & IMPL_DEVFONT_BOLD) )
3239 rFSD.meWeight = eSearchWeight;
3240 else if( (eSearchWeight < WEIGHT_NORMAL)
3241 && (eSearchWeight < rFSD.meWeight)
3242 && (eSearchWeight != WEIGHT_DONTKNOW)
3243 && (pFoundData->mnTypeFaces & IMPL_DEVFONT_LIGHT) )
3244 rFSD.meWeight = eSearchWeight;
3246 if( (nSearchType & IMPL_FONT_ATTR_ITALIC)
3247 && ((rFSD.meItalic == ITALIC_DONTKNOW) || (rFSD.meItalic == ITALIC_NONE))
3248 && (pFoundData->mnTypeFaces & IMPL_DEVFONT_ITALIC) )
3249 rFSD.meItalic = ITALIC_NORMAL;
3251 else
3253 // if still needed fall back to default fonts
3254 pFoundData = FindDefaultFont();
3257 return pFoundData;
3260 // -----------------------------------------------------------------------
3262 ImplFontEntry* ImplFontCache::GetGlyphFallbackFont( ImplDevFontList* pFontList,
3263 ImplFontSelectData& rFontSelData, int nFallbackLevel, rtl::OUString& rMissingCodes )
3265 // get a candidate font for glyph fallback
3266 // unless the previously selected font got a device specific substitution
3267 // e.g. PsPrint Arial->Helvetica for udiaeresis when Helvetica doesn't support it
3268 if( nFallbackLevel >= 1)
3270 ImplDevFontListData* pFallbackData = pFontList->GetGlyphFallbackFont(
3271 rFontSelData, rMissingCodes, nFallbackLevel-1 );
3272 // escape when there are no font candidates
3273 if( !pFallbackData )
3274 return NULL;
3275 // override the font name
3276 rFontSelData.maName = pFallbackData->GetFamilyName();
3277 // clear the cached normalized name
3278 rFontSelData.maSearchName = String();
3281 // get device font without doing device specific substitutions
3282 ImplFontEntry* pFallbackFont = GetFontEntry( pFontList, rFontSelData, NULL );
3283 return pFallbackFont;
3286 // -----------------------------------------------------------------------
3288 void ImplFontCache::Release( ImplFontEntry* pEntry )
3290 static const int FONTCACHE_MAX = 50;
3292 DBG_ASSERT( (pEntry->mnRefCount > 0), "ImplFontCache::Release() - font refcount underflow" );
3293 if( --pEntry->mnRefCount > 0 )
3294 return;
3296 if( ++mnRef0Count < FONTCACHE_MAX )
3297 return;
3299 // remove unused entries from font instance cache
3300 FontInstanceList::iterator it_next = maFontInstanceList.begin();
3301 while( it_next != maFontInstanceList.end() )
3303 FontInstanceList::iterator it = it_next++;
3304 ImplFontEntry* pFontEntry = (*it).second;
3305 if( pFontEntry->mnRefCount > 0 )
3306 continue;
3308 maFontInstanceList.erase( it );
3309 delete pFontEntry;
3310 --mnRef0Count;
3311 DBG_ASSERT( (mnRef0Count>=0), "ImplFontCache::Release() - refcount0 underflow" );
3313 if( mpFirstEntry == pFontEntry )
3314 mpFirstEntry = NULL;
3317 DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Release() - refcount0 mismatch" );
3320 // -----------------------------------------------------------------------
3322 void ImplFontCache::Invalidate()
3324 // delete unreferenced entries
3325 FontInstanceList::iterator it = maFontInstanceList.begin();
3326 for(; it != maFontInstanceList.end(); ++it )
3328 ImplFontEntry* pFontEntry = (*it).second;
3329 if( pFontEntry->mnRefCount > 0 )
3330 continue;
3332 delete pFontEntry;
3333 --mnRef0Count;
3336 // #112304# make sure the font cache is really clean
3337 mpFirstEntry = NULL;
3338 maFontInstanceList.clear();
3340 DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Invalidate() - mnRef0Count non-zero" );
3343 // =======================================================================
3345 ImplMultiTextLineInfo::ImplMultiTextLineInfo()
3347 mpLines = new PImplTextLineInfo[MULTITEXTLINEINFO_RESIZE];
3348 mnLines = 0;
3349 mnSize = MULTITEXTLINEINFO_RESIZE;
3353 ImplMultiTextLineInfo::~ImplMultiTextLineInfo()
3355 for ( xub_StrLen i = 0; i < mnLines; i++ )
3356 delete mpLines[i];
3357 delete [] mpLines;
3360 void ImplMultiTextLineInfo::AddLine( ImplTextLineInfo* pLine )
3362 if ( mnSize == mnLines )
3364 mnSize += MULTITEXTLINEINFO_RESIZE;
3365 PImplTextLineInfo* pNewLines = new PImplTextLineInfo[mnSize];
3366 memcpy( pNewLines, mpLines, mnLines*sizeof(PImplTextLineInfo) );
3367 mpLines = pNewLines;
3370 mpLines[mnLines] = pLine;
3371 mnLines++;
3374 void ImplMultiTextLineInfo::Clear()
3376 for ( xub_StrLen i = 0; i < mnLines; i++ )
3377 delete mpLines[i];
3378 mnLines = 0;
3381 // =======================================================================
3383 FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const Font& rFont )
3385 FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark();
3387 // If no Position is set, then calculate the default position, which
3388 // depends on the language
3389 if ( !(nEmphasisMark & (EMPHASISMARK_POS_ABOVE | EMPHASISMARK_POS_BELOW)) )
3391 LanguageType eLang = rFont.GetLanguage();
3392 // In Chinese Simplified the EmphasisMarks are below/left
3393 if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
3394 (eLang == LANGUAGE_CHINESE_SINGAPORE) )
3395 nEmphasisMark |= EMPHASISMARK_POS_BELOW;
3396 else
3398 eLang = rFont.GetCJKContextLanguage();
3399 // In Chinese Simplified the EmphasisMarks are below/left
3400 if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
3401 (eLang == LANGUAGE_CHINESE_SINGAPORE) )
3402 nEmphasisMark |= EMPHASISMARK_POS_BELOW;
3403 else
3404 nEmphasisMark |= EMPHASISMARK_POS_ABOVE;
3408 return nEmphasisMark;
3411 // -----------------------------------------------------------------------
3413 BOOL OutputDevice::ImplIsUnderlineAbove( const Font& rFont )
3415 if ( !rFont.IsVertical() )
3416 return FALSE;
3418 if( (LANGUAGE_JAPANESE == rFont.GetLanguage())
3419 || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) )
3420 // the underline is right for Japanese only
3421 return TRUE;
3423 return FALSE;
3426 // =======================================================================
3428 void OutputDevice::ImplInitFontList() const
3430 if( ! mpFontList->Count() )
3432 if( mpGraphics || ImplGetGraphics() )
3434 RTL_LOGFILE_CONTEXT( aLog, "OutputDevice::ImplInitFontList()" );
3435 mpGraphics->GetDevFontList( mpFontList );
3440 // =======================================================================
3442 void OutputDevice::ImplInitFont() const
3444 DBG_TESTSOLARMUTEX();
3446 if ( mbInitFont )
3448 if ( meOutDevType != OUTDEV_PRINTER )
3450 // decide if antialiasing is appropriate
3451 bool bNonAntialiased = (GetAntialiasing() & ANTIALIASING_DISABLE_TEXT) != 0;
3452 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
3453 bNonAntialiased |= ((rStyleSettings.GetDisplayOptions() & DISPLAY_OPTION_AA_DISABLE) != 0);
3454 bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontEntry->maFontSelData.mnHeight);
3455 mpFontEntry->maFontSelData.mbNonAntialiased = bNonAntialiased;
3458 if( !mpPDFWriter || !mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) )
3460 // select font in the device layers
3461 mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData), 0 );
3463 mbInitFont = false;
3467 // -----------------------------------------------------------------------
3469 void OutputDevice::ImplInitTextColor()
3471 DBG_TESTSOLARMUTEX();
3473 if ( mbInitTextColor )
3475 mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) );
3476 mbInitTextColor = FALSE;
3480 // -----------------------------------------------------------------------
3482 bool OutputDevice::ImplNewFont() const
3484 DBG_TESTSOLARMUTEX();
3486 // get correct font list on the PDF writer if necessary
3487 if( mpPDFWriter )
3489 const ImplSVData* pSVData = ImplGetSVData();
3490 if( mpFontList == pSVData->maGDIData.mpScreenFontList
3491 || mpFontCache == pSVData->maGDIData.mpScreenFontCache )
3492 const_cast<OutputDevice&>(*this).ImplUpdateFontData( true );
3495 if ( !mbNewFont )
3496 return true;
3498 // we need a graphics
3499 if ( !mpGraphics && !ImplGetGraphics() )
3500 return false;
3501 SalGraphics* pGraphics = mpGraphics;
3502 ImplInitFontList();
3504 // convert to pixel height
3505 // TODO: replace integer based aSize completely with subpixel accurate type
3506 float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetHeight()) );
3507 Size aSize = ImplLogicToDevicePixel( maFont.GetSize() );
3508 if ( !aSize.Height() )
3510 // use default pixel height only when logical height is zero
3511 if ( maFont.GetSize().Height() )
3512 aSize.Height() = 1;
3513 else
3514 aSize.Height() = (12*mnDPIY)/72;
3515 fExactHeight = static_cast<float>(aSize.Height());
3518 // select the default width only when logical width is zero
3519 if( (0 == aSize.Width()) && (0 != maFont.GetSize().Width()) )
3520 aSize.Width() = 1;
3522 // get font entry
3523 ImplDirectFontSubstitution* pDevSpecificSubst = NULL;
3524 if( mpOutDevData )
3525 pDevSpecificSubst = &mpOutDevData->maDevFontSubst;
3526 ImplFontEntry* pOldEntry = mpFontEntry;
3527 mpFontEntry = mpFontCache->GetFontEntry( mpFontList, maFont, aSize, fExactHeight, pDevSpecificSubst );
3528 if( pOldEntry )
3529 mpFontCache->Release( pOldEntry );
3531 ImplFontEntry* pFontEntry = mpFontEntry;
3532 // mark when lower layers need to get involved
3533 mbNewFont = FALSE;
3534 if( pFontEntry != pOldEntry )
3535 mbInitFont = TRUE;
3537 // select font when it has not been initialized yet
3538 if ( !pFontEntry->mbInit )
3540 ImplInitFont();
3542 // get metric data from device layers
3543 if ( pGraphics )
3545 pFontEntry->mbInit = true;
3547 pFontEntry->maMetric.mnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation);
3548 if( mpPDFWriter && mpPDFWriter->isBuiltinFont( pFontEntry->maFontSelData.mpFontData ) )
3549 mpPDFWriter->getFontMetric( &pFontEntry->maFontSelData, &(pFontEntry->maMetric) );
3550 else
3551 pGraphics->GetFontMetric( &(pFontEntry->maMetric) );
3553 pFontEntry->maMetric.ImplInitTextLineSize( this );
3554 pFontEntry->maMetric.ImplInitAboveTextLineSize();
3556 pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent + pFontEntry->maMetric.mnDescent;
3558 if( pFontEntry->maFontSelData.mnOrientation
3559 && !pFontEntry->maMetric.mnOrientation
3560 && (meOutDevType != OUTDEV_PRINTER) )
3562 pFontEntry->mnOwnOrientation = sal::static_int_cast<short>(pFontEntry->maFontSelData.mnOrientation);
3563 pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation;
3565 else
3566 pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation;
3570 // enable kerning array if requested
3571 if ( maFont.GetKerning() & KERNING_FONTSPECIFIC )
3573 // TODO: test if physical font supports kerning and disable if not
3574 if( pFontEntry->maMetric.mbKernableFont )
3575 mbKerning = true;
3577 else
3578 mbKerning = false;
3579 if ( maFont.GetKerning() & KERNING_ASIAN )
3580 mbKerning = true;
3582 // calculate EmphasisArea
3583 mnEmphasisAscent = 0;
3584 mnEmphasisDescent = 0;
3585 if ( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
3587 FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
3588 long nEmphasisHeight = (pFontEntry->mnLineHeight*250)/1000;
3589 if ( nEmphasisHeight < 1 )
3590 nEmphasisHeight = 1;
3591 if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
3592 mnEmphasisDescent = nEmphasisHeight;
3593 else
3594 mnEmphasisAscent = nEmphasisHeight;
3597 // calculate text offset depending on TextAlignment
3598 TextAlign eAlign = maFont.GetAlign();
3599 if ( eAlign == ALIGN_BASELINE )
3601 mnTextOffX = 0;
3602 mnTextOffY = 0;
3604 else if ( eAlign == ALIGN_TOP )
3606 mnTextOffX = 0;
3607 mnTextOffY = +pFontEntry->maMetric.mnAscent + mnEmphasisAscent;
3608 if ( pFontEntry->mnOrientation )
3609 ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
3611 else // eAlign == ALIGN_BOTTOM
3613 mnTextOffX = 0;
3614 mnTextOffY = -pFontEntry->maMetric.mnDescent + mnEmphasisDescent;
3615 if ( pFontEntry->mnOrientation )
3616 ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
3619 mbTextLines = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) ||
3620 ((maFont.GetOverline() != UNDERLINE_NONE) && (maFont.GetOverline() != UNDERLINE_DONTKNOW)) ||
3621 ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW));
3622 mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() ||
3623 (maFont.GetRelief() != RELIEF_NONE);
3625 // #95414# fix for OLE objects which use scale factors very creatively
3626 if( mbMap && !aSize.Width() )
3628 int nOrigWidth = pFontEntry->maMetric.mnWidth;
3629 float fStretch = (float)maMapRes.mnMapScNumX * maMapRes.mnMapScDenomY;
3630 fStretch /= (float)maMapRes.mnMapScNumY * maMapRes.mnMapScDenomX;
3631 int nNewWidth = (int)(nOrigWidth * fStretch + 0.5);
3632 if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) )
3634 Size aOrigSize = maFont.GetSize();
3635 const_cast<Font&>(maFont).SetSize( Size( nNewWidth, aSize.Height() ) );
3636 mbMap = FALSE;
3637 mbNewFont = TRUE;
3638 ImplNewFont(); // recurse once using stretched width
3639 mbMap = TRUE;
3640 const_cast<Font&>(maFont).SetSize( aOrigSize );
3644 return true;
3647 // -----------------------------------------------------------------------
3649 long OutputDevice::ImplGetTextWidth( const SalLayout& rSalLayout ) const
3651 long nWidth = rSalLayout.GetTextWidth();
3652 nWidth /= rSalLayout.GetUnitsPerPixel();
3653 return nWidth;
3656 // -----------------------------------------------------------------------
3658 void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY,
3659 long nX, long nY, long nWidth, long nHeight )
3661 short nOrientation = mpFontEntry->mnOrientation;
3662 if ( nOrientation )
3664 // Rotate rect without rounding problems for 90 degree rotations
3665 if ( !(nOrientation % 900) )
3667 nX -= nBaseX;
3668 nY -= nBaseY;
3670 if ( nOrientation == 900 )
3672 long nTemp = nX;
3673 nX = nY;
3674 nY = -nTemp;
3675 nTemp = nWidth;
3676 nWidth = nHeight;
3677 nHeight = nTemp;
3678 nY -= nHeight;
3680 else if ( nOrientation == 1800 )
3682 nX = -nX;
3683 nY = -nY;
3684 nX -= nWidth;
3685 nY -= nHeight;
3687 else /* ( nOrientation == 2700 ) */
3689 long nTemp = nX;
3690 nX = -nY;
3691 nY = nTemp;
3692 nTemp = nWidth;
3693 nWidth = nHeight;
3694 nHeight = nTemp;
3695 nX -= nWidth;
3698 nX += nBaseX;
3699 nY += nBaseY;
3701 else
3703 // inflate because polygons are drawn smaller
3704 Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
3705 Polygon aPoly( aRect );
3706 aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
3707 ImplDrawPolygon( aPoly );
3708 return;
3712 mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this );
3715 // -----------------------------------------------------------------------
3717 void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout )
3719 const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
3720 const Point aBase = rSalLayout.DrawBase();
3721 const long nX = aBase.X();
3722 const long nY = aBase.Y();
3724 if ( mbLineColor || mbInitLineColor )
3726 mpGraphics->SetLineColor();
3727 mbInitLineColor = TRUE;
3729 mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) );
3730 mbInitFillColor = TRUE;
3732 ImplDrawTextRect( nX, nY, nX, nY-mpFontEntry->maMetric.mnAscent-mnEmphasisAscent,
3733 nWidth,
3734 mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent );
3737 // -----------------------------------------------------------------------
3739 Rectangle OutputDevice::ImplGetTextBoundRect( const SalLayout& rSalLayout )
3741 Point aPoint = rSalLayout.GetDrawPosition();
3742 long nX = aPoint.X();
3743 long nY = aPoint.Y();
3745 long nWidth = rSalLayout.GetTextWidth();
3746 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
3748 nY -= mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
3750 if ( mpFontEntry->mnOrientation )
3752 long nBaseX = nX, nBaseY = nY;
3753 if ( !(mpFontEntry->mnOrientation % 900) )
3755 long nX2 = nX+nWidth;
3756 long nY2 = nY+nHeight;
3757 ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation );
3758 ImplRotatePos( nBaseX, nBaseY, nX2, nY2, mpFontEntry->mnOrientation );
3759 nWidth = nX2-nX;
3760 nHeight = nY2-nY;
3762 else
3764 // inflate by +1+1 because polygons are drawn smaller
3765 Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
3766 Polygon aPoly( aRect );
3767 aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
3768 return aPoly.GetBoundRect();
3772 return Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) );
3775 // -----------------------------------------------------------------------
3777 void OutputDevice::ImplInitTextLineSize()
3779 mpFontEntry->maMetric.ImplInitTextLineSize( this );
3782 // -----------------------------------------------------------------------
3784 void OutputDevice::ImplInitAboveTextLineSize()
3786 mpFontEntry->maMetric.ImplInitAboveTextLineSize();
3789 // -----------------------------------------------------------------------
3791 ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData )
3792 : ImplFontAttributes( rFontSelData )
3794 // initialize the members provided by the font request
3795 mnWidth = rFontSelData.mnWidth;
3796 mnOrientation = sal::static_int_cast<short>(rFontSelData.mnOrientation);
3798 // intialize the used font name
3799 if( rFontSelData.mpFontData )
3801 maName = rFontSelData.mpFontData->maName;
3802 maStyleName= rFontSelData.mpFontData->maStyleName;
3803 mbDevice = rFontSelData.mpFontData->mbDevice;
3804 mbKernableFont = true;
3806 else
3808 xub_StrLen nTokenPos = 0;
3809 maName = GetNextFontToken( rFontSelData.maName, nTokenPos );
3810 maStyleName= rFontSelData.maStyleName;
3811 mbDevice = false;
3812 mbKernableFont = false;
3815 // reset metrics that are usually measured for the font instance
3816 mnAscent = 0;
3817 mnDescent = 0;
3818 mnIntLeading = 0;
3819 mnExtLeading = 0;
3820 mnSlant = 0;
3821 mnMinKashida = 0;
3823 // reset metrics that are usually derived from the measurements
3824 mnUnderlineSize = 0;
3825 mnUnderlineOffset = 0;
3826 mnBUnderlineSize = 0;
3827 mnBUnderlineOffset = 0;
3828 mnDUnderlineSize = 0;
3829 mnDUnderlineOffset1 = 0;
3830 mnDUnderlineOffset2 = 0;
3831 mnWUnderlineSize = 0;
3832 mnWUnderlineOffset = 0;
3833 mnAboveUnderlineSize = 0;
3834 mnAboveUnderlineOffset = 0;
3835 mnAboveBUnderlineSize = 0;
3836 mnAboveBUnderlineOffset = 0;
3837 mnAboveDUnderlineSize = 0;
3838 mnAboveDUnderlineOffset1 = 0;
3839 mnAboveDUnderlineOffset2 = 0;
3840 mnAboveWUnderlineSize = 0;
3841 mnAboveWUnderlineOffset = 0;
3842 mnStrikeoutSize = 0;
3843 mnStrikeoutOffset = 0;
3844 mnBStrikeoutSize = 0;
3845 mnBStrikeoutOffset = 0;
3846 mnDStrikeoutSize = 0;
3847 mnDStrikeoutOffset1 = 0;
3848 mnDStrikeoutOffset2 = 0;
3851 // -----------------------------------------------------------------------
3853 void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev )
3855 long nDescent = mnDescent;
3856 if ( nDescent <= 0 )
3858 nDescent = mnAscent / 10;
3859 if ( !nDescent )
3860 nDescent = 1;
3863 // #i55341# for some fonts it is not a good idea to calculate
3864 // their text line metrics from the real font descent
3865 // => work around this problem just for these fonts
3866 if( 3*nDescent > mnAscent )
3867 nDescent = mnAscent / 3;
3869 long nLineHeight = ((nDescent*25)+50) / 100;
3870 if ( !nLineHeight )
3871 nLineHeight = 1;
3872 long nLineHeight2 = nLineHeight / 2;
3873 if ( !nLineHeight2 )
3874 nLineHeight2 = 1;
3876 long nBLineHeight = ((nDescent*50)+50) / 100;
3877 if ( nBLineHeight == nLineHeight )
3878 nBLineHeight++;
3879 long nBLineHeight2 = nBLineHeight/2;
3880 if ( !nBLineHeight2 )
3881 nBLineHeight2 = 1;
3883 long n2LineHeight = ((nDescent*16)+50) / 100;
3884 if ( !n2LineHeight )
3885 n2LineHeight = 1;
3886 long n2LineDY = n2LineHeight;
3887 /* #117909#
3888 * add some pixels to minimum double line distance on higher resolution devices
3890 long nMin2LineDY = 1 + pDev->ImplGetDPIY()/150;
3891 if ( n2LineDY < nMin2LineDY )
3892 n2LineDY = nMin2LineDY;
3893 long n2LineDY2 = n2LineDY/2;
3894 if ( !n2LineDY2 )
3895 n2LineDY2 = 1;
3897 long nUnderlineOffset = mnDescent/2 + 1;
3898 long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3);
3900 mnUnderlineSize = nLineHeight;
3901 mnUnderlineOffset = nUnderlineOffset - nLineHeight2;
3903 mnBUnderlineSize = nBLineHeight;
3904 mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2;
3906 mnDUnderlineSize = n2LineHeight;
3907 mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight;
3908 mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;
3910 long nWCalcSize = mnDescent;
3911 if ( nWCalcSize < 6 )
3913 if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
3914 mnWUnderlineSize = nWCalcSize;
3915 else
3916 mnWUnderlineSize = 3;
3918 else
3919 mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;
3921 // #109280# the following line assures that wavelnes are never placed below the descent, however
3922 // for most fonts the waveline then is drawn into the text, so we better keep the old solution
3923 // pFontEntry->maMetric.mnWUnderlineOffset = pFontEntry->maMetric.mnDescent + 1 - pFontEntry->maMetric.mnWUnderlineSize;
3924 mnWUnderlineOffset = nUnderlineOffset;
3926 mnStrikeoutSize = nLineHeight;
3927 mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2;
3929 mnBStrikeoutSize = nBLineHeight;
3930 mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2;
3932 mnDStrikeoutSize = n2LineHeight;
3933 mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
3934 mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;
3937 // -----------------------------------------------------------------------
3939 void ImplFontMetricData::ImplInitAboveTextLineSize()
3941 long nIntLeading = mnIntLeading;
3942 // TODO: assess usage of nLeading below (changed in extleading CWS)
3943 // if no leading is available, we assume 15% of the ascent
3944 if ( nIntLeading <= 0 )
3946 nIntLeading = mnAscent*15/100;
3947 if ( !nIntLeading )
3948 nIntLeading = 1;
3951 long nLineHeight = ((nIntLeading*25)+50) / 100;
3952 if ( !nLineHeight )
3953 nLineHeight = 1;
3955 long nBLineHeight = ((nIntLeading*50)+50) / 100;
3956 if ( nBLineHeight == nLineHeight )
3957 nBLineHeight++;
3959 long n2LineHeight = ((nIntLeading*16)+50) / 100;
3960 if ( !n2LineHeight )
3961 n2LineHeight = 1;
3963 long nCeiling = -mnAscent;
3965 mnAboveUnderlineSize = nLineHeight;
3966 mnAboveUnderlineOffset = nCeiling + (nIntLeading - nLineHeight + 1) / 2;
3968 mnAboveBUnderlineSize = nBLineHeight;
3969 mnAboveBUnderlineOffset = nCeiling + (nIntLeading - nBLineHeight + 1) / 2;
3971 mnAboveDUnderlineSize = n2LineHeight;
3972 mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2;
3973 mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + n2LineHeight + 1) / 2;
3975 long nWCalcSize = nIntLeading;
3976 if ( nWCalcSize < 6 )
3978 if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
3979 mnAboveWUnderlineSize = nWCalcSize;
3980 else
3981 mnAboveWUnderlineSize = 3;
3983 else
3984 mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100;
3986 mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
3989 // -----------------------------------------------------------------------
3991 static void ImplDrawWavePixel( long nOriginX, long nOriginY,
3992 long nCurX, long nCurY,
3993 short nOrientation,
3994 SalGraphics* pGraphics,
3995 OutputDevice* pOutDev,
3996 BOOL bDrawPixAsRect,
3998 long nPixWidth, long nPixHeight )
4000 if ( nOrientation )
4001 ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation );
4003 if ( bDrawPixAsRect )
4006 pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev );
4008 else
4010 pGraphics->DrawPixel( nCurX, nCurY, pOutDev );
4014 // -----------------------------------------------------------------------
4016 void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY,
4017 long nStartX, long nStartY,
4018 long nWidth, long nHeight,
4019 long nLineWidth, short nOrientation,
4020 const Color& rColor )
4022 if ( !nHeight )
4023 return;
4025 // Bei Hoehe von 1 Pixel reicht es, eine Linie auszugeben
4026 if ( (nLineWidth == 1) && (nHeight == 1) )
4028 mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
4029 mbInitLineColor = TRUE;
4031 long nEndX = nStartX+nWidth;
4032 long nEndY = nStartY;
4033 if ( nOrientation )
4035 ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation );
4036 ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation );
4038 mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this );
4041 else
4043 long nCurX = nStartX;
4044 long nCurY = nStartY;
4045 long nDiffX = 2;
4046 long nDiffY = nHeight-1;
4047 long nCount = nWidth;
4048 long nOffY = -1;
4049 long nFreq;
4050 long i;
4051 long nPixWidth;
4052 long nPixHeight;
4053 BOOL bDrawPixAsRect;
4054 // Auf Druckern die Pixel per DrawRect() ausgeben
4055 if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) )
4057 if ( mbLineColor || mbInitLineColor )
4059 mpGraphics->SetLineColor();
4060 mbInitLineColor = TRUE;
4062 mpGraphics->SetFillColor( ImplColorToSal( rColor ) );
4063 mbInitFillColor = TRUE;
4064 bDrawPixAsRect = TRUE;
4065 nPixWidth = nLineWidth;
4066 nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
4068 else
4070 mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
4071 mbInitLineColor = TRUE;
4072 nPixWidth = 1;
4073 nPixHeight = 1;
4074 bDrawPixAsRect = FALSE;
4077 if ( !nDiffY )
4079 while ( nWidth )
4081 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
4082 mpGraphics, this,
4083 bDrawPixAsRect, nPixWidth, nPixHeight );
4084 nCurX++;
4085 nWidth--;
4088 else
4090 nCurY += nDiffY;
4091 nFreq = nCount / (nDiffX+nDiffY);
4092 while ( nFreq-- )
4094 for( i = nDiffY; i; --i )
4096 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
4097 mpGraphics, this,
4098 bDrawPixAsRect, nPixWidth, nPixHeight );
4099 nCurX++;
4100 nCurY += nOffY;
4102 for( i = nDiffX; i; --i )
4104 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
4105 mpGraphics, this,
4106 bDrawPixAsRect, nPixWidth, nPixHeight );
4107 nCurX++;
4109 nOffY = -nOffY;
4111 nFreq = nCount % (nDiffX+nDiffY);
4112 if ( nFreq )
4114 for( i = nDiffY; i && nFreq; --i, --nFreq )
4116 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
4117 mpGraphics, this,
4118 bDrawPixAsRect, nPixWidth, nPixHeight );
4119 nCurX++;
4120 nCurY += nOffY;
4123 for( i = nDiffX; i && nFreq; --i, --nFreq )
4125 ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
4126 mpGraphics, this,
4127 bDrawPixAsRect, nPixWidth, nPixHeight );
4128 nCurX++;
4136 // -----------------------------------------------------------------------
4138 void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY,
4139 long nX, long nY, long nWidth,
4140 FontUnderline eTextLine,
4141 Color aColor,
4142 BOOL bIsAbove )
4144 ImplFontEntry* pFontEntry = mpFontEntry;
4145 long nLineHeight;
4146 long nLinePos;
4148 if ( bIsAbove )
4150 nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize;
4151 nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset;
4153 else
4155 nLineHeight = pFontEntry->maMetric.mnWUnderlineSize;
4156 nLinePos = pFontEntry->maMetric.mnWUnderlineOffset;
4158 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
4159 nLineHeight = 3;
4160 long nLineWidth = (mnDPIX/300);
4161 if ( !nLineWidth )
4162 nLineWidth = 1;
4163 if ( eTextLine == UNDERLINE_BOLDWAVE )
4164 nLineWidth *= 2;
4165 nLinePos += nY - (nLineHeight / 2);
4166 long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
4167 if ( eTextLine == UNDERLINE_DOUBLEWAVE )
4169 long nOrgLineHeight = nLineHeight;
4170 nLineHeight /= 3;
4171 if ( nLineHeight < 2 )
4173 if ( nOrgLineHeight > 1 )
4174 nLineHeight = 2;
4175 else
4176 nLineHeight = 1;
4178 long nLineDY = nOrgLineHeight-(nLineHeight*2);
4179 if ( nLineDY < nLineWidthHeight )
4180 nLineDY = nLineWidthHeight;
4181 long nLineDY2 = nLineDY/2;
4182 if ( !nLineDY2 )
4183 nLineDY2 = 1;
4185 nLinePos -= nLineWidthHeight-nLineDY2;
4186 ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
4187 nLineWidth, mpFontEntry->mnOrientation, aColor );
4188 nLinePos += nLineWidthHeight+nLineDY;
4189 ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
4190 nLineWidth, mpFontEntry->mnOrientation, aColor );
4192 else
4194 nLinePos -= nLineWidthHeight/2;
4195 ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
4196 nLineWidth, mpFontEntry->mnOrientation, aColor );
4200 // -----------------------------------------------------------------------
4202 void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY,
4203 long nX, long nY, long nWidth,
4204 FontUnderline eTextLine,
4205 Color aColor,
4206 BOOL bIsAbove )
4208 ImplFontEntry* pFontEntry = mpFontEntry;
4209 long nLineHeight = 0;
4210 long nLinePos = 0;
4211 long nLinePos2 = 0;
4213 if ( eTextLine > UNDERLINE_LAST )
4214 eTextLine = UNDERLINE_SINGLE;
4216 switch ( eTextLine )
4218 case UNDERLINE_SINGLE:
4219 case UNDERLINE_DOTTED:
4220 case UNDERLINE_DASH:
4221 case UNDERLINE_LONGDASH:
4222 case UNDERLINE_DASHDOT:
4223 case UNDERLINE_DASHDOTDOT:
4224 if ( bIsAbove )
4226 nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize;
4227 nLinePos = nY + pFontEntry->maMetric.mnAboveUnderlineOffset;
4229 else
4231 nLineHeight = pFontEntry->maMetric.mnUnderlineSize;
4232 nLinePos = nY + pFontEntry->maMetric.mnUnderlineOffset;
4234 break;
4235 case UNDERLINE_BOLD:
4236 case UNDERLINE_BOLDDOTTED:
4237 case UNDERLINE_BOLDDASH:
4238 case UNDERLINE_BOLDLONGDASH:
4239 case UNDERLINE_BOLDDASHDOT:
4240 case UNDERLINE_BOLDDASHDOTDOT:
4241 if ( bIsAbove )
4243 nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize;
4244 nLinePos = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset;
4246 else
4248 nLineHeight = pFontEntry->maMetric.mnBUnderlineSize;
4249 nLinePos = nY + pFontEntry->maMetric.mnBUnderlineOffset;
4251 break;
4252 case UNDERLINE_DOUBLE:
4253 if ( bIsAbove )
4255 nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize;
4256 nLinePos = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1;
4257 nLinePos2 = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2;
4259 else
4261 nLineHeight = pFontEntry->maMetric.mnDUnderlineSize;
4262 nLinePos = nY + pFontEntry->maMetric.mnDUnderlineOffset1;
4263 nLinePos2 = nY + pFontEntry->maMetric.mnDUnderlineOffset2;
4265 break;
4266 default:
4267 break;
4270 if ( nLineHeight )
4272 if ( mbLineColor || mbInitLineColor )
4274 mpGraphics->SetLineColor();
4275 mbInitLineColor = TRUE;
4277 mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
4278 mbInitFillColor = TRUE;
4280 long nLeft = nX;
4282 switch ( eTextLine )
4284 case UNDERLINE_SINGLE:
4285 case UNDERLINE_BOLD:
4286 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
4287 break;
4288 case UNDERLINE_DOUBLE:
4289 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
4290 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
4291 break;
4292 case UNDERLINE_DOTTED:
4293 case UNDERLINE_BOLDDOTTED:
4295 long nDotWidth = nLineHeight*mnDPIY;
4296 nDotWidth += mnDPIY/2;
4297 nDotWidth /= mnDPIY;
4298 long nTempWidth = nDotWidth;
4299 long nEnd = nLeft+nWidth;
4300 while ( nLeft < nEnd )
4302 if ( nLeft+nTempWidth > nEnd )
4303 nTempWidth = nEnd-nLeft;
4304 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
4305 nLeft += nDotWidth*2;
4308 break;
4309 case UNDERLINE_DASH:
4310 case UNDERLINE_LONGDASH:
4311 case UNDERLINE_BOLDDASH:
4312 case UNDERLINE_BOLDLONGDASH:
4314 long nDotWidth = nLineHeight*mnDPIY;
4315 nDotWidth += mnDPIY/2;
4316 nDotWidth /= mnDPIY;
4317 long nMinDashWidth;
4318 long nMinSpaceWidth;
4319 long nSpaceWidth;
4320 long nDashWidth;
4321 if ( (eTextLine == UNDERLINE_LONGDASH) ||
4322 (eTextLine == UNDERLINE_BOLDLONGDASH) )
4324 nMinDashWidth = nDotWidth*6;
4325 nMinSpaceWidth = nDotWidth*2;
4326 nDashWidth = 200;
4327 nSpaceWidth = 100;
4329 else
4331 nMinDashWidth = nDotWidth*4;
4332 nMinSpaceWidth = (nDotWidth*150)/100;
4333 nDashWidth = 100;
4334 nSpaceWidth = 50;
4336 nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540;
4337 nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540;
4338 // DashWidth wird gegebenenfalls verbreitert, wenn
4339 // die dicke der Linie im Verhaeltnis zur Laenge
4340 // zu dick wird
4341 if ( nDashWidth < nMinDashWidth )
4342 nDashWidth = nMinDashWidth;
4343 if ( nSpaceWidth < nMinSpaceWidth )
4344 nSpaceWidth = nMinSpaceWidth;
4345 long nTempWidth = nDashWidth;
4346 long nEnd = nLeft+nWidth;
4347 while ( nLeft < nEnd )
4349 if ( nLeft+nTempWidth > nEnd )
4350 nTempWidth = nEnd-nLeft;
4351 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
4352 nLeft += nDashWidth+nSpaceWidth;
4355 break;
4356 case UNDERLINE_DASHDOT:
4357 case UNDERLINE_BOLDDASHDOT:
4359 long nDotWidth = nLineHeight*mnDPIY;
4360 nDotWidth += mnDPIY/2;
4361 nDotWidth /= mnDPIY;
4362 long nDashWidth = ((100*mnDPIX)+1270)/2540;
4363 long nMinDashWidth = nDotWidth*4;
4364 // DashWidth wird gegebenenfalls verbreitert, wenn
4365 // die dicke der Linie im Verhaeltnis zur Laenge
4366 // zu dick wird
4367 if ( nDashWidth < nMinDashWidth )
4368 nDashWidth = nMinDashWidth;
4369 long nTempDotWidth = nDotWidth;
4370 long nTempDashWidth = nDashWidth;
4371 long nEnd = nLeft+nWidth;
4372 while ( nLeft < nEnd )
4374 if ( nLeft+nTempDotWidth > nEnd )
4375 nTempDotWidth = nEnd-nLeft;
4376 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
4377 nLeft += nDotWidth*2;
4378 if ( nLeft > nEnd )
4379 break;
4380 if ( nLeft+nTempDashWidth > nEnd )
4381 nTempDashWidth = nEnd-nLeft;
4382 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
4383 nLeft += nDashWidth+nDotWidth;
4386 break;
4387 case UNDERLINE_DASHDOTDOT:
4388 case UNDERLINE_BOLDDASHDOTDOT:
4390 long nDotWidth = nLineHeight*mnDPIY;
4391 nDotWidth += mnDPIY/2;
4392 nDotWidth /= mnDPIY;
4393 long nDashWidth = ((100*mnDPIX)+1270)/2540;
4394 long nMinDashWidth = nDotWidth*4;
4395 // DashWidth wird gegebenenfalls verbreitert, wenn
4396 // die dicke der Linie im Verhaeltnis zur Laenge
4397 // zu dick wird
4398 if ( nDashWidth < nMinDashWidth )
4399 nDashWidth = nMinDashWidth;
4400 long nTempDotWidth = nDotWidth;
4401 long nTempDashWidth = nDashWidth;
4402 long nEnd = nLeft+nWidth;
4403 while ( nLeft < nEnd )
4405 if ( nLeft+nTempDotWidth > nEnd )
4406 nTempDotWidth = nEnd-nLeft;
4407 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
4408 nLeft += nDotWidth*2;
4409 if ( nLeft > nEnd )
4410 break;
4411 if ( nLeft+nTempDotWidth > nEnd )
4412 nTempDotWidth = nEnd-nLeft;
4413 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
4414 nLeft += nDotWidth*2;
4415 if ( nLeft > nEnd )
4416 break;
4417 if ( nLeft+nTempDashWidth > nEnd )
4418 nTempDashWidth = nEnd-nLeft;
4419 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
4420 nLeft += nDashWidth+nDotWidth;
4423 break;
4424 default:
4425 break;
4430 // -----------------------------------------------------------------------
4432 void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY,
4433 long nX, long nY, long nWidth,
4434 FontStrikeout eStrikeout,
4435 Color aColor )
4437 ImplFontEntry* pFontEntry = mpFontEntry;
4438 long nLineHeight = 0;
4439 long nLinePos = 0;
4440 long nLinePos2 = 0;
4442 if ( eStrikeout > STRIKEOUT_LAST )
4443 eStrikeout = STRIKEOUT_SINGLE;
4445 switch ( eStrikeout )
4447 case STRIKEOUT_SINGLE:
4448 nLineHeight = pFontEntry->maMetric.mnStrikeoutSize;
4449 nLinePos = nY + pFontEntry->maMetric.mnStrikeoutOffset;
4450 break;
4451 case STRIKEOUT_BOLD:
4452 nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize;
4453 nLinePos = nY + pFontEntry->maMetric.mnBStrikeoutOffset;
4454 break;
4455 case STRIKEOUT_DOUBLE:
4456 nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize;
4457 nLinePos = nY + pFontEntry->maMetric.mnDStrikeoutOffset1;
4458 nLinePos2 = nY + pFontEntry->maMetric.mnDStrikeoutOffset2;
4459 break;
4460 default:
4461 break;
4464 if ( nLineHeight )
4466 if ( mbLineColor || mbInitLineColor )
4468 mpGraphics->SetLineColor();
4469 mbInitLineColor = TRUE;
4471 mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
4472 mbInitFillColor = TRUE;
4474 long nLeft = nX;
4476 switch ( eStrikeout )
4478 case STRIKEOUT_SINGLE:
4479 case STRIKEOUT_BOLD:
4480 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
4481 break;
4482 case STRIKEOUT_DOUBLE:
4483 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
4484 ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
4485 break;
4486 default:
4487 break;
4492 // -----------------------------------------------------------------------
4494 void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY,
4495 long nX, long nY, long nWidth,
4496 FontStrikeout eStrikeout,
4497 Color aColor )
4499 // PDF-export does its own strikeout drawing... why again?
4500 if( mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) )
4501 return;
4503 // prepare string for strikeout measurement
4504 static char cStrikeoutChar;
4505 if ( eStrikeout == STRIKEOUT_SLASH )
4506 cStrikeoutChar = '/';
4507 else // ( eStrikeout == STRIKEOUT_X )
4508 cStrikeoutChar = 'X';
4509 static const int nTestStrLen = 4;
4510 static const int nMaxStrikeStrLen = 2048;
4511 xub_Unicode aChars[ nMaxStrikeStrLen +1]; // +1 for valgrind...
4512 for( int i = 0; i < nTestStrLen; ++i)
4513 aChars[i] = cStrikeoutChar;
4514 const String aStrikeoutTest( aChars, nTestStrLen );
4516 // calculate approximation of strikeout atom size
4517 long nStrikeoutWidth = nWidth;
4518 SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, nTestStrLen );
4519 if( pLayout )
4521 nStrikeoutWidth = (pLayout->GetTextWidth() +nTestStrLen/2) / (nTestStrLen * pLayout->GetUnitsPerPixel());
4522 pLayout->Release();
4524 if( nStrikeoutWidth <= 0 ) // sanity check
4525 return;
4527 // calculate acceptable strikeout length
4528 // allow the strikeout to be one pixel larger than the text it strikes out
4529 long nMaxWidth = nStrikeoutWidth / 2;
4530 if ( nMaxWidth < 2 )
4531 nMaxWidth = 2;
4532 nMaxWidth += nWidth + 1;
4534 int nStrikeStrLen = (nMaxWidth + nStrikeoutWidth - 1) / nStrikeoutWidth;
4535 // if the text width is smaller than the strikeout text, then do not
4536 // strike out at all. This case requires user interaction, e.g. adding
4537 // a space to the text
4538 if( nStrikeStrLen <= 0 )
4539 return;
4540 if( nStrikeStrLen > nMaxStrikeStrLen )
4541 nStrikeStrLen = nMaxStrikeStrLen;
4543 // build the strikeout string
4544 for( int i = nTestStrLen; i < nStrikeStrLen; ++i)
4545 aChars[i] = cStrikeoutChar;
4546 const String aStrikeoutText( aChars, xub_StrLen(nStrikeStrLen) );
4548 if( mpFontEntry->mnOrientation )
4549 ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation );
4551 // strikeout text has to be left aligned
4552 ULONG nOrigTLM = mnTextLayoutMode;
4553 mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED;
4554 pLayout = ImplLayout( aStrikeoutText, 0, STRING_LEN );
4555 mnTextLayoutMode = nOrigTLM;
4557 if( !pLayout )
4558 return;
4560 // draw the strikeout text
4561 const Color aOldColor = GetTextColor();
4562 SetTextColor( aColor );
4563 ImplInitTextColor();
4565 pLayout->DrawBase() = Point( nX+mnTextOffX, nY+mnTextOffY );
4566 pLayout->DrawText( *mpGraphics );
4567 pLayout->Release();
4569 SetTextColor( aOldColor );
4570 ImplInitTextColor();
4573 // -----------------------------------------------------------------------
4575 void OutputDevice::ImplDrawTextLine( long nBaseX,
4576 long nX, long nY, long nWidth,
4577 FontStrikeout eStrikeout,
4578 FontUnderline eUnderline,
4579 FontUnderline eOverline,
4580 BOOL bUnderlineAbove )
4582 if ( !nWidth )
4583 return;
4585 Color aStrikeoutColor = GetTextColor();
4586 Color aUnderlineColor = GetTextLineColor();
4587 Color aOverlineColor = GetOverlineColor();
4588 BOOL bStrikeoutDone = FALSE;
4589 BOOL bUnderlineDone = FALSE;
4590 BOOL bOverlineDone = FALSE;
4592 // TODO: fix rotated text
4593 if ( IsRTLEnabled() )
4594 // --- RTL --- mirror at basex
4595 nX = nBaseX - nWidth - (nX - nBaseX - 1);
4597 if ( !IsTextLineColor() )
4598 aUnderlineColor = GetTextColor();
4600 if ( !IsOverlineColor() )
4601 aOverlineColor = GetTextColor();
4603 if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
4604 (eUnderline == UNDERLINE_WAVE) ||
4605 (eUnderline == UNDERLINE_DOUBLEWAVE) ||
4606 (eUnderline == UNDERLINE_BOLDWAVE) )
4608 ImplDrawWaveTextLine( nBaseX, nY, nX, nY, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
4609 bUnderlineDone = TRUE;
4611 if ( (eOverline == UNDERLINE_SMALLWAVE) ||
4612 (eOverline == UNDERLINE_WAVE) ||
4613 (eOverline == UNDERLINE_DOUBLEWAVE) ||
4614 (eOverline == UNDERLINE_BOLDWAVE) )
4616 ImplDrawWaveTextLine( nBaseX, nY, nX, nY, nWidth, eOverline, aOverlineColor, TRUE );
4617 bOverlineDone = TRUE;
4620 if ( (eStrikeout == STRIKEOUT_SLASH) ||
4621 (eStrikeout == STRIKEOUT_X) )
4623 ImplDrawStrikeoutChar( nBaseX, nY, nX, nY, nWidth, eStrikeout, aStrikeoutColor );
4624 bStrikeoutDone = TRUE;
4627 if ( !bUnderlineDone )
4628 ImplDrawStraightTextLine( nBaseX, nY, nX, nY, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
4630 if ( !bOverlineDone )
4631 ImplDrawStraightTextLine( nBaseX, nY, nX, nY, nWidth, eOverline, aOverlineColor, TRUE );
4633 if ( !bStrikeoutDone )
4634 ImplDrawStrikeoutLine( nBaseX, nY, nX, nY, nWidth, eStrikeout, aStrikeoutColor );
4637 // -----------------------------------------------------------------------
4639 void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout,
4640 FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, BOOL bWordLine, BOOL bUnderlineAbove )
4642 if( bWordLine )
4644 Point aPos, aStartPt;
4645 sal_Int32 nWidth = 0, nAdvance=0;
4646 for( int nStart = 0;;)
4648 sal_GlyphId nGlyphIndex;
4649 if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
4650 break;
4652 if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
4654 if( !nWidth )
4656 aStartPt = aPos;//rSalLayout.DrawBase() - (aPos - rSalLayout.DrawOffset());
4659 nWidth += nAdvance;
4661 else if( nWidth > 0 )
4663 ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
4664 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
4665 nWidth = 0;
4669 if( nWidth > 0 )
4671 ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
4672 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
4675 else
4677 Point aStartPt = rSalLayout.GetDrawPosition();
4678 int nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
4679 ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
4680 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
4684 // -----------------------------------------------------------------------
4686 void OutputDevice::ImplDrawMnemonicLine( long nX, long nY, long nWidth )
4688 long nBaseX = nX;
4689 if( /*ImplHasMirroredGraphics() &&*/ IsRTLEnabled() )
4691 // --- RTL ---
4692 // add some strange offset
4693 nX += 2;
4694 // revert the hack that will be done later in ImplDrawTextLine
4695 nX = nBaseX - nWidth - (nX - nBaseX - 1);
4698 ImplDrawTextLine( nBaseX, nX, nY, nWidth, STRIKEOUT_NONE, UNDERLINE_SINGLE, UNDERLINE_NONE, FALSE );
4701 // -----------------------------------------------------------------------
4703 void OutputDevice::ImplGetEmphasisMark( PolyPolygon& rPolyPoly, BOOL& rPolyLine,
4704 Rectangle& rRect1, Rectangle& rRect2,
4705 long& rYOff, long& rWidth,
4706 FontEmphasisMark eEmphasis,
4707 long nHeight, short /*nOrient*/ )
4709 static const BYTE aAccentPolyFlags[24] =
4711 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 0, 2, 0, 2, 2
4714 static const long aAccentPos[48] =
4716 78, 0,
4717 348, 79,
4718 599, 235,
4719 843, 469,
4720 938, 574,
4721 990, 669,
4722 990, 773,
4723 990, 843,
4724 964, 895,
4725 921, 947,
4726 886, 982,
4727 860, 999,
4728 825, 999,
4729 764, 999,
4730 721, 964,
4731 686, 895,
4732 625, 791,
4733 556, 660,
4734 469, 504,
4735 400, 400,
4736 261, 252,
4737 61, 61,
4738 0, 27,
4739 9, 0
4742 rWidth = 0;
4743 rYOff = 0;
4744 rPolyLine = FALSE;
4746 if ( !nHeight )
4747 return;
4749 FontEmphasisMark nEmphasisStyle = eEmphasis & EMPHASISMARK_STYLE;
4750 long nDotSize = 0;
4751 switch ( nEmphasisStyle )
4753 case EMPHASISMARK_DOT:
4754 // Dot has 55% of the height
4755 nDotSize = (nHeight*550)/1000;
4756 if ( !nDotSize )
4757 nDotSize = 1;
4758 if ( nDotSize <= 2 )
4759 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4760 else
4762 long nRad = nDotSize/2;
4763 Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
4764 rPolyPoly.Insert( aPoly );
4766 rYOff = ((nHeight*250)/1000)/2; // Center to the anthoer EmphasisMarks
4767 rWidth = nDotSize;
4768 break;
4770 case EMPHASISMARK_CIRCLE:
4771 // Dot has 80% of the height
4772 nDotSize = (nHeight*800)/1000;
4773 if ( !nDotSize )
4774 nDotSize = 1;
4775 if ( nDotSize <= 2 )
4776 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4777 else
4779 long nRad = nDotSize/2;
4780 Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
4781 rPolyPoly.Insert( aPoly );
4782 // BorderWidth is 15%
4783 long nBorder = (nDotSize*150)/1000;
4784 if ( nBorder <= 1 )
4785 rPolyLine = TRUE;
4786 else
4788 Polygon aPoly2( Point( nRad, nRad ),
4789 nRad-nBorder, nRad-nBorder );
4790 rPolyPoly.Insert( aPoly2 );
4793 rWidth = nDotSize;
4794 break;
4796 case EMPHASISMARK_DISC:
4797 // Dot has 80% of the height
4798 nDotSize = (nHeight*800)/1000;
4799 if ( !nDotSize )
4800 nDotSize = 1;
4801 if ( nDotSize <= 2 )
4802 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4803 else
4805 long nRad = nDotSize/2;
4806 Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
4807 rPolyPoly.Insert( aPoly );
4809 rWidth = nDotSize;
4810 break;
4812 case EMPHASISMARK_ACCENT:
4813 // Dot has 80% of the height
4814 nDotSize = (nHeight*800)/1000;
4815 if ( !nDotSize )
4816 nDotSize = 1;
4817 if ( nDotSize <= 2 )
4819 if ( nDotSize == 1 )
4821 rRect1 = Rectangle( Point(), Size( nDotSize, nDotSize ) );
4822 rWidth = nDotSize;
4824 else
4826 rRect1 = Rectangle( Point(), Size( 1, 1 ) );
4827 rRect2 = Rectangle( Point( 1, 1 ), Size( 1, 1 ) );
4830 else
4832 Polygon aPoly( sizeof( aAccentPos ) / sizeof( long ) / 2,
4833 (const Point*)aAccentPos,
4834 aAccentPolyFlags );
4835 double dScale = ((double)nDotSize)/1000.0;
4836 aPoly.Scale( dScale, dScale );
4837 Polygon aTemp;
4838 aPoly.GetSimple( aTemp );
4839 Rectangle aBoundRect = aTemp.GetBoundRect();
4840 rWidth = aBoundRect.GetWidth();
4841 nDotSize = aBoundRect.GetHeight();
4842 rPolyPoly.Insert( aTemp );
4844 break;
4847 // calculate position
4848 long nOffY = 1+(mnDPIY/300); // one visible pixel space
4849 long nSpaceY = nHeight-nDotSize;
4850 if ( nSpaceY >= nOffY*2 )
4851 rYOff += nOffY;
4852 if ( !(eEmphasis & EMPHASISMARK_POS_BELOW) )
4853 rYOff += nDotSize;
4856 // -----------------------------------------------------------------------
4858 void OutputDevice::ImplDrawEmphasisMark( long nBaseX, long nX, long nY,
4859 const PolyPolygon& rPolyPoly, BOOL bPolyLine,
4860 const Rectangle& rRect1, const Rectangle& rRect2 )
4862 // TODO: pass nWidth as width of this mark
4863 long nWidth = 0;
4865 if( IsRTLEnabled() )
4866 // --- RTL --- mirror at basex
4867 nX = nBaseX - nWidth - (nX - nBaseX - 1);
4869 nX -= mnOutOffX;
4870 nY -= mnOutOffY;
4872 if ( rPolyPoly.Count() )
4874 if ( bPolyLine )
4876 Polygon aPoly = rPolyPoly.GetObject( 0 );
4877 aPoly.Move( nX, nY );
4878 DrawPolyLine( aPoly );
4880 else
4882 PolyPolygon aPolyPoly = rPolyPoly;
4883 aPolyPoly.Move( nX, nY );
4884 DrawPolyPolygon( aPolyPoly );
4888 if ( !rRect1.IsEmpty() )
4890 Rectangle aRect( Point( nX+rRect1.Left(),
4891 nY+rRect1.Top() ), rRect1.GetSize() );
4892 DrawRect( aRect );
4895 if ( !rRect2.IsEmpty() )
4897 Rectangle aRect( Point( nX+rRect2.Left(),
4898 nY+rRect2.Top() ), rRect2.GetSize() );
4900 DrawRect( aRect );
4904 // -----------------------------------------------------------------------
4906 void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout )
4908 Color aOldColor = GetTextColor();
4909 Color aOldLineColor = GetLineColor();
4910 Color aOldFillColor = GetFillColor();
4911 BOOL bOldMap = mbMap;
4912 GDIMetaFile* pOldMetaFile = mpMetaFile;
4913 mpMetaFile = NULL;
4914 EnableMapMode( FALSE );
4916 FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
4917 PolyPolygon aPolyPoly;
4918 Rectangle aRect1;
4919 Rectangle aRect2;
4920 long nEmphasisYOff;
4921 long nEmphasisWidth;
4922 long nEmphasisHeight;
4923 BOOL bPolyLine;
4925 if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
4926 nEmphasisHeight = mnEmphasisDescent;
4927 else
4928 nEmphasisHeight = mnEmphasisAscent;
4930 ImplGetEmphasisMark( aPolyPoly, bPolyLine,
4931 aRect1, aRect2,
4932 nEmphasisYOff, nEmphasisWidth,
4933 nEmphasisMark,
4934 nEmphasisHeight, mpFontEntry->mnOrientation );
4936 if ( bPolyLine )
4938 SetLineColor( GetTextColor() );
4939 SetFillColor();
4941 else
4943 SetLineColor();
4944 SetFillColor( GetTextColor() );
4947 Point aOffset = Point(0,0);
4949 if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
4950 aOffset.Y() += mpFontEntry->maMetric.mnDescent + nEmphasisYOff;
4951 else
4952 aOffset.Y() -= mpFontEntry->maMetric.mnAscent + nEmphasisYOff;
4954 long nEmphasisWidth2 = nEmphasisWidth / 2;
4955 long nEmphasisHeight2 = nEmphasisHeight / 2;
4956 aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );
4958 Point aOutPoint;
4959 Rectangle aRectangle;
4960 for( int nStart = 0;;)
4962 sal_GlyphId nGlyphIndex;
4963 if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aOutPoint, nStart ) )
4964 break;
4966 if( !mpGraphics->GetGlyphBoundRect( nGlyphIndex, aRectangle ) )
4967 continue;
4969 if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
4971 Point aAdjPoint = aOffset;
4972 aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2;
4973 if ( mpFontEntry->mnOrientation )
4974 ImplRotatePos( 0, 0, aAdjPoint.X(), aAdjPoint.Y(), mpFontEntry->mnOrientation );
4975 aOutPoint += aAdjPoint;
4976 aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 );
4977 ImplDrawEmphasisMark( rSalLayout.DrawBase().X(),
4978 aOutPoint.X(), aOutPoint.Y(),
4979 aPolyPoly, bPolyLine, aRect1, aRect2 );
4983 SetLineColor( aOldLineColor );
4984 SetFillColor( aOldFillColor );
4985 EnableMapMode( bOldMap );
4986 mpMetaFile = pOldMetaFile;
4989 // -----------------------------------------------------------------------
4991 bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout )
4993 int nX = rSalLayout.DrawBase().X();
4994 int nY = rSalLayout.DrawBase().Y();
4996 Rectangle aBoundRect;
4997 rSalLayout.DrawBase() = Point( 0, 0 );
4998 rSalLayout.DrawOffset() = Point( 0, 0 );
4999 if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) )
5001 // guess vertical text extents if GetBoundRect failed
5002 int nRight = rSalLayout.GetTextWidth();
5003 int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
5004 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
5005 aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop );
5008 // cache virtual device for rotation
5009 if ( !mpOutDevData )
5010 ImplInitOutDevData();
5011 if ( !mpOutDevData->mpRotateDev )
5012 mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 );
5013 VirtualDevice* pVDev = mpOutDevData->mpRotateDev;
5015 // size it accordingly
5016 if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) )
5017 return false;
5019 Font aFont( GetFont() );
5020 aFont.SetOrientation( 0 );
5021 aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
5022 pVDev->SetFont( aFont );
5023 pVDev->SetTextColor( Color( COL_BLACK ) );
5024 pVDev->SetTextFillColor();
5025 pVDev->ImplNewFont();
5026 pVDev->ImplInitFont();
5027 pVDev->ImplInitTextColor();
5029 // draw text into upper left corner
5030 rSalLayout.DrawBase() -= aBoundRect.TopLeft();
5031 rSalLayout.DrawText( *((OutputDevice*)pVDev)->mpGraphics );
5033 Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() );
5034 if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) )
5035 return false;
5037 // calculate rotation offset
5038 Polygon aPoly( aBoundRect );
5039 aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation );
5040 Point aPoint = aPoly.GetBoundRect().TopLeft();
5041 aPoint += Point( nX, nY );
5043 // mask output with text colored bitmap
5044 GDIMetaFile* pOldMetaFile = mpMetaFile;
5045 long nOldOffX = mnOutOffX;
5046 long nOldOffY = mnOutOffY;
5047 BOOL bOldMap = mbMap;
5049 mnOutOffX = 0L;
5050 mnOutOffY = 0L;
5051 mpMetaFile = NULL;
5052 EnableMapMode( FALSE );
5054 DrawMask( aPoint, aBmp, GetTextColor() );
5056 EnableMapMode( bOldMap );
5057 mnOutOffX = nOldOffX;
5058 mnOutOffY = nOldOffY;
5059 mpMetaFile = pOldMetaFile;
5061 return true;
5064 // -----------------------------------------------------------------------
5066 void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, BOOL bTextLines )
5068 if( mpFontEntry->mnOwnOrientation )
5069 if( ImplDrawRotateText( rSalLayout ) )
5070 return;
5072 long nOldX = rSalLayout.DrawBase().X();
5073 if( ! (mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) )
5075 if( ImplHasMirroredGraphics() )
5077 long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
5078 long x = rSalLayout.DrawBase().X();
5079 rSalLayout.DrawBase().X() = w - 1 - x;
5080 if( !IsRTLEnabled() )
5082 OutputDevice *pOutDevRef = (OutputDevice *)this;
5083 // mirror this window back
5084 long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
5085 rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ;
5088 else if( IsRTLEnabled() )
5090 //long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
5091 //long x = rSalLayout.DrawBase().X();
5092 OutputDevice *pOutDevRef = (OutputDevice *)this;
5093 // mirror this window back
5094 long devX = pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
5095 rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX;
5098 rSalLayout.DrawText( *mpGraphics );
5101 rSalLayout.DrawBase().X() = nOldX;
5103 if( bTextLines )
5104 ImplDrawTextLines( rSalLayout,
5105 maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(),
5106 maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) );
5108 // emphasis marks
5109 if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
5110 ImplDrawEmphasisMarks( rSalLayout );
5113 // -----------------------------------------------------------------------
5115 void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout )
5117 Color aOldColor = GetTextColor();
5118 Color aOldTextLineColor = GetTextLineColor();
5119 Color aOldOverlineColor = GetOverlineColor();
5120 FontRelief eRelief = maFont.GetRelief();
5122 Point aOrigPos = rSalLayout.DrawBase();
5123 if ( eRelief != RELIEF_NONE )
5125 Color aReliefColor( COL_LIGHTGRAY );
5126 Color aTextColor( aOldColor );
5128 Color aTextLineColor( aOldTextLineColor );
5129 Color aOverlineColor( aOldOverlineColor );
5131 // we don't have a automatic color, so black is always drawn on white
5132 if ( aTextColor.GetColor() == COL_BLACK )
5133 aTextColor = Color( COL_WHITE );
5134 if ( aTextLineColor.GetColor() == COL_BLACK )
5135 aTextLineColor = Color( COL_WHITE );
5136 if ( aOverlineColor.GetColor() == COL_BLACK )
5137 aOverlineColor = Color( COL_WHITE );
5139 // relief-color is black for white text, in all other cases
5140 // we set this to LightGray
5141 if ( aTextColor.GetColor() == COL_WHITE )
5142 aReliefColor = Color( COL_BLACK );
5143 SetTextLineColor( aReliefColor );
5144 SetOverlineColor( aReliefColor );
5145 SetTextColor( aReliefColor );
5146 ImplInitTextColor();
5148 // calculate offset - for high resolution printers the offset
5149 // should be greater so that the effect is visible
5150 long nOff = 1;
5151 nOff += mnDPIX/300;
5153 if ( eRelief == RELIEF_ENGRAVED )
5154 nOff = -nOff;
5155 rSalLayout.DrawOffset() += Point( nOff, nOff);
5156 ImplDrawTextDirect( rSalLayout, mbTextLines );
5157 rSalLayout.DrawOffset() -= Point( nOff, nOff);
5159 SetTextLineColor( aTextLineColor );
5160 SetOverlineColor( aOverlineColor );
5161 SetTextColor( aTextColor );
5162 ImplInitTextColor();
5163 ImplDrawTextDirect( rSalLayout, mbTextLines );
5165 SetTextLineColor( aOldTextLineColor );
5166 SetOverlineColor( aOldOverlineColor );
5168 if ( aTextColor != aOldColor )
5170 SetTextColor( aOldColor );
5171 ImplInitTextColor();
5174 else
5176 if ( maFont.IsShadow() )
5178 long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24);
5179 if ( maFont.IsOutline() )
5180 nOff++;
5181 SetTextLineColor();
5182 SetOverlineColor();
5183 if ( (GetTextColor().GetColor() == COL_BLACK)
5184 || (GetTextColor().GetLuminance() < 8) )
5185 SetTextColor( Color( COL_LIGHTGRAY ) );
5186 else
5187 SetTextColor( Color( COL_BLACK ) );
5188 ImplInitTextColor();
5189 rSalLayout.DrawBase() += Point( nOff, nOff );
5190 ImplDrawTextDirect( rSalLayout, mbTextLines );
5191 rSalLayout.DrawBase() -= Point( nOff, nOff );
5192 SetTextColor( aOldColor );
5193 SetTextLineColor( aOldTextLineColor );
5194 SetOverlineColor( aOldOverlineColor );
5195 ImplInitTextColor();
5197 if ( !maFont.IsOutline() )
5198 ImplDrawTextDirect( rSalLayout, mbTextLines );
5201 if ( maFont.IsOutline() )
5203 rSalLayout.DrawBase() = aOrigPos + Point(-1,-1);
5204 ImplDrawTextDirect( rSalLayout, mbTextLines );
5205 rSalLayout.DrawBase() = aOrigPos + Point(+1,+1);
5206 ImplDrawTextDirect( rSalLayout, mbTextLines );
5207 rSalLayout.DrawBase() = aOrigPos + Point(-1,+0);
5208 ImplDrawTextDirect( rSalLayout, mbTextLines );
5209 rSalLayout.DrawBase() = aOrigPos + Point(-1,+1);
5210 ImplDrawTextDirect( rSalLayout, mbTextLines );
5211 rSalLayout.DrawBase() = aOrigPos + Point(+0,+1);
5212 ImplDrawTextDirect( rSalLayout, mbTextLines );
5213 rSalLayout.DrawBase() = aOrigPos + Point(+0,-1);
5214 ImplDrawTextDirect( rSalLayout, mbTextLines );
5215 rSalLayout.DrawBase() = aOrigPos + Point(+1,-1);
5216 ImplDrawTextDirect( rSalLayout, mbTextLines );
5217 rSalLayout.DrawBase() = aOrigPos + Point(+1,+0);
5218 ImplDrawTextDirect( rSalLayout, mbTextLines );
5219 rSalLayout.DrawBase() = aOrigPos;
5221 SetTextColor( Color( COL_WHITE ) );
5222 SetTextLineColor( Color( COL_WHITE ) );
5223 SetOverlineColor( Color( COL_WHITE ) );
5224 ImplInitTextColor();
5225 ImplDrawTextDirect( rSalLayout, mbTextLines );
5226 SetTextColor( aOldColor );
5227 SetTextLineColor( aOldTextLineColor );
5228 SetOverlineColor( aOldOverlineColor );
5229 ImplInitTextColor();
5234 // -----------------------------------------------------------------------
5236 void OutputDevice::ImplDrawText( SalLayout& rSalLayout )
5238 if( mbInitClipRegion )
5239 ImplInitClipRegion();
5240 if( mbOutputClipped )
5241 return;
5242 if( mbInitTextColor )
5243 ImplInitTextColor();
5245 rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY );
5247 if( IsTextFillColor() )
5248 ImplDrawTextBackground( rSalLayout );
5250 if( mbTextSpecial )
5251 ImplDrawSpecialText( rSalLayout );
5252 else
5253 ImplDrawTextDirect( rSalLayout, mbTextLines );
5256 // -----------------------------------------------------------------------
5258 long OutputDevice::ImplGetTextLines( ImplMultiTextLineInfo& rLineInfo,
5259 long nWidth, const XubString& rStr,
5260 USHORT nStyle ) const
5262 DBG_ASSERTWARNING( nWidth >= 0, "ImplGetTextLines: nWidth <= 0!" );
5264 if ( nWidth <= 0 )
5265 nWidth = 1;
5267 long nMaxLineWidth = 0;
5268 rLineInfo.Clear();
5269 if ( rStr.Len() && (nWidth > 0) )
5271 ::rtl::OUString aText( rStr );
5272 uno::Reference < i18n::XBreakIterator > xBI;
5273 // get service provider
5274 uno::Reference< lang::XMultiServiceFactory > xSMgr( unohelper::GetMultiServiceFactory() );
5276 uno::Reference< linguistic2::XHyphenator > xHyph;
5277 if( xSMgr.is() )
5279 uno::Reference< linguistic2::XLinguServiceManager> xLinguMgr(xSMgr->createInstance(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.linguistic2.LinguServiceManager"))),uno::UNO_QUERY);
5280 if ( xLinguMgr.is() )
5282 xHyph = xLinguMgr->getHyphenator();
5286 i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, uno::Sequence <beans::PropertyValue>(), 1 );
5287 i18n::LineBreakUserOptions aUserOptions;
5289 xub_StrLen nPos = 0;
5290 xub_StrLen nLen = rStr.Len();
5291 while ( nPos < nLen )
5293 xub_StrLen nBreakPos = nPos;
5295 while ( ( nBreakPos < nLen ) && ( rStr.GetChar( nBreakPos ) != _CR ) && ( rStr.GetChar( nBreakPos ) != _LF ) )
5296 nBreakPos++;
5298 long nLineWidth = GetTextWidth( rStr, nPos, nBreakPos-nPos );
5299 if ( ( nLineWidth > nWidth ) && ( nStyle & TEXT_DRAW_WORDBREAK ) )
5301 if ( !xBI.is() )
5302 xBI = vcl::unohelper::CreateBreakIterator();
5304 if ( xBI.is() )
5306 const com::sun::star::lang::Locale& rDefLocale(Application::GetSettings().GetUILocale());
5307 xub_StrLen nSoftBreak = GetTextBreak( rStr, nWidth, nPos, nBreakPos - nPos );
5308 DBG_ASSERT( nSoftBreak < nBreakPos, "Break?!" );
5309 //aHyphOptions.hyphenIndex = nSoftBreak;
5310 i18n::LineBreakResults aLBR = xBI->getLineBreak( aText, nSoftBreak, rDefLocale, nPos, aHyphOptions, aUserOptions );
5311 nBreakPos = (xub_StrLen)aLBR.breakIndex;
5312 if ( nBreakPos <= nPos )
5313 nBreakPos = nSoftBreak;
5314 if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION )
5316 // Egal ob Trenner oder nicht: Das Wort nach dem Trenner durch
5317 // die Silbentrennung jagen...
5318 // nMaxBreakPos ist das letzte Zeichen was in die Zeile passt,
5319 // nBreakPos ist der Wort-Anfang
5320 // Ein Problem gibt es, wenn das Dok so schmal ist, dass ein Wort
5321 // auf mehr als Zwei Zeilen gebrochen wird...
5322 if ( xHyph.is() )
5324 sal_Unicode cAlternateReplChar = 0;
5325 sal_Unicode cAlternateExtraChar = 0;
5326 i18n::Boundary aBoundary = xBI->getWordBoundary( aText, nBreakPos, rDefLocale, ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True );
5327 // sal_uInt16 nWordStart = nBreakPos;
5328 // sal_uInt16 nBreakPos_OLD = nBreakPos;
5329 sal_uInt16 nWordStart = nPos;
5330 sal_uInt16 nWordEnd = (USHORT) aBoundary.endPos;
5331 DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
5333 USHORT nWordLen = nWordEnd - nWordStart;
5334 if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) )
5336 // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
5337 // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" );
5338 String aWord( aText, nWordStart, nWordLen );
5339 sal_uInt16 nMinTrail = static_cast<sal_uInt16>(nWordEnd-nSoftBreak+1); //+1: Vor dem angeknacksten Buchstaben
5340 uno::Reference< linguistic2::XHyphenatedWord > xHyphWord;
5341 if (xHyph.is())
5342 xHyphWord = xHyph->hyphenate( aWord, rDefLocale, aWord.Len() - nMinTrail, uno::Sequence< beans::PropertyValue >() );
5343 if (xHyphWord.is())
5345 sal_Bool bAlternate = xHyphWord->isAlternativeSpelling();
5346 sal_uInt16 _nWordLen = 1 + xHyphWord->getHyphenPos();
5348 if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= ( 2 ) ) )
5350 if ( !bAlternate )
5352 nBreakPos = nWordStart + _nWordLen;
5354 else
5356 String aAlt( xHyphWord->getHyphenatedWord() );
5358 // Wir gehen von zwei Faellen aus, die nun
5359 // vorliegen koennen:
5360 // 1) packen wird zu pak-ken
5361 // 2) Schiffahrt wird zu Schiff-fahrt
5362 // In Fall 1 muss ein Zeichen ersetzt werden,
5363 // in Fall 2 wird ein Zeichen hinzugefuegt.
5364 // Die Identifikation wird erschwert durch Worte wie
5365 // "Schiffahrtsbrennesseln", da der Hyphenator alle
5366 // Position des Wortes auftrennt und "Schifffahrtsbrennnesseln"
5367 // ermittelt. Wir koennen also eigentlich nicht unmittelbar vom
5368 // Index des AlternativWord auf aWord schliessen.
5370 // Das ganze geraffel wird durch eine Funktion am
5371 // Hyphenator vereinfacht werden, sobald AMA sie einbaut...
5372 sal_uInt16 nAltStart = _nWordLen - 1;
5373 sal_uInt16 nTxtStart = nAltStart - (aAlt.Len() - aWord.Len());
5374 sal_uInt16 nTxtEnd = nTxtStart;
5375 sal_uInt16 nAltEnd = nAltStart;
5377 // Die Bereiche zwischen den nStart und nEnd ist
5378 // die Differenz zwischen Alternativ- und OriginalString.
5379 while( nTxtEnd < aWord.Len() && nAltEnd < aAlt.Len() &&
5380 aWord.GetChar(nTxtEnd) != aAlt.GetChar(nAltEnd) )
5382 ++nTxtEnd;
5383 ++nAltEnd;
5386 // Wenn ein Zeichen hinzugekommen ist, dann bemerken wir es jetzt:
5387 if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
5388 aWord.GetChar( nTxtEnd ) == aAlt.GetChar(nAltEnd) )
5390 ++nAltEnd;
5391 ++nTxtStart;
5392 ++nTxtEnd;
5395 DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Falsche Annahme!" );
5397 if ( nTxtEnd > nTxtStart )
5398 cAlternateReplChar = aAlt.GetChar( nAltStart );
5399 else
5400 cAlternateExtraChar = aAlt.GetChar( nAltStart );
5402 nBreakPos = nWordStart + nTxtStart;
5403 if ( cAlternateReplChar )
5404 nBreakPos++;
5406 } // if (xHyphWord.is())
5407 } // if ( ( nWordEnd >= nSoftBreak ) && ( nWordLen > 3 ) )
5408 } // if ( xHyph.is() )
5409 } // if ( (nStyle & TEXT_DRAW_WORDBREAK_HYPHENATION) == TEXT_DRAW_WORDBREAK_HYPHENATION )
5411 nLineWidth = GetTextWidth( rStr, nPos, nBreakPos-nPos );
5413 else
5415 // fallback to something really simple
5416 USHORT nSpacePos = STRING_LEN;
5417 long nW = 0;
5420 nSpacePos = rStr.SearchBackward( sal_Unicode(' '), nSpacePos );
5421 if( nSpacePos != STRING_NOTFOUND )
5423 if( nSpacePos > nPos )
5424 nSpacePos--;
5425 nW = GetTextWidth( rStr, nPos, nSpacePos-nPos );
5427 } while( nW > nWidth );
5429 if( nSpacePos != STRING_NOTFOUND )
5431 nBreakPos = nSpacePos;
5432 nLineWidth = GetTextWidth( rStr, nPos, nBreakPos-nPos );
5433 if( nBreakPos < rStr.Len()-1 )
5434 nBreakPos++;
5439 if ( nLineWidth > nMaxLineWidth )
5440 nMaxLineWidth = nLineWidth;
5442 rLineInfo.AddLine( new ImplTextLineInfo( nLineWidth, nPos, nBreakPos-nPos ) );
5444 if ( nBreakPos == nPos )
5445 nBreakPos++;
5446 nPos = nBreakPos;
5448 if ( ( rStr.GetChar( nPos ) == _CR ) || ( rStr.GetChar( nPos ) == _LF ) )
5450 nPos++;
5451 // CR/LF?
5452 if ( ( nPos < nLen ) && ( rStr.GetChar( nPos ) == _LF ) && ( rStr.GetChar( nPos-1 ) == _CR ) )
5453 nPos++;
5457 #ifdef DBG_UTIL
5458 for ( USHORT nL = 0; nL < rLineInfo.Count(); nL++ )
5460 ImplTextLineInfo* pLine = rLineInfo.GetLine( nL );
5461 String aLine( rStr, pLine->GetIndex(), pLine->GetLen() );
5462 DBG_ASSERT( aLine.Search( _CR ) == STRING_NOTFOUND, "ImplGetTextLines - Found CR!" );
5463 DBG_ASSERT( aLine.Search( _LF ) == STRING_NOTFOUND, "ImplGetTextLines - Found LF!" );
5465 #endif
5467 return nMaxLineWidth;
5470 // =======================================================================
5472 void OutputDevice::SetAntialiasing( USHORT nMode )
5474 if ( mnAntialiasing != nMode )
5476 mnAntialiasing = nMode;
5477 mbInitFont = TRUE;
5479 if(mpGraphics)
5481 mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW);
5485 if( mpAlphaVDev )
5486 mpAlphaVDev->SetAntialiasing( nMode );
5489 // -----------------------------------------------------------------------
5491 void OutputDevice::SetFont( const Font& rNewFont )
5493 DBG_TRACE( "OutputDevice::SetFont()" );
5494 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5495 DBG_CHKOBJ( &rNewFont, Font, NULL );
5497 Font aFont( rNewFont );
5498 aFont.SetLanguage(rNewFont.GetLanguage());
5499 if ( mnDrawMode & (DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT | DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT | DRAWMODE_SETTINGSTEXT |
5500 DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL | DRAWMODE_GRAYFILL | DRAWMODE_NOFILL |
5501 DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) )
5503 Color aTextColor( aFont.GetColor() );
5505 if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5506 aTextColor = Color( COL_BLACK );
5507 else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5508 aTextColor = Color( COL_WHITE );
5509 else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5511 const UINT8 cLum = aTextColor.GetLuminance();
5512 aTextColor = Color( cLum, cLum, cLum );
5514 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5515 aTextColor = GetSettings().GetStyleSettings().GetFontColor();
5517 if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT )
5519 aTextColor = Color( (aTextColor.GetRed() >> 1 ) | 0x80,
5520 (aTextColor.GetGreen() >> 1 ) | 0x80,
5521 (aTextColor.GetBlue() >> 1 ) | 0x80 );
5524 aFont.SetColor( aTextColor );
5526 BOOL bTransFill = aFont.IsTransparent();
5527 if ( !bTransFill )
5529 Color aTextFillColor( aFont.GetFillColor() );
5531 if ( mnDrawMode & DRAWMODE_BLACKFILL )
5532 aTextFillColor = Color( COL_BLACK );
5533 else if ( mnDrawMode & DRAWMODE_WHITEFILL )
5534 aTextFillColor = Color( COL_WHITE );
5535 else if ( mnDrawMode & DRAWMODE_GRAYFILL )
5537 const UINT8 cLum = aTextFillColor.GetLuminance();
5538 aTextFillColor = Color( cLum, cLum, cLum );
5540 else if( mnDrawMode & DRAWMODE_SETTINGSFILL )
5541 aTextFillColor = GetSettings().GetStyleSettings().GetWindowColor();
5542 else if ( mnDrawMode & DRAWMODE_NOFILL )
5544 aTextFillColor = Color( COL_TRANSPARENT );
5545 bTransFill = TRUE;
5548 if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) )
5550 aTextFillColor = Color( (aTextFillColor.GetRed() >> 1) | 0x80,
5551 (aTextFillColor.GetGreen() >> 1) | 0x80,
5552 (aTextFillColor.GetBlue() >> 1) | 0x80 );
5555 aFont.SetFillColor( aTextFillColor );
5559 if ( mpMetaFile )
5561 mpMetaFile->AddAction( new MetaFontAction( aFont ) );
5562 // the color and alignment actions don't belong here
5563 // TODO: get rid of them without breaking anything...
5564 mpMetaFile->AddAction( new MetaTextAlignAction( aFont.GetAlign() ) );
5565 mpMetaFile->AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) );
5568 #if (OSL_DEBUG_LEVEL > 2) || defined (HDU_DEBUG)
5569 fprintf( stderr, " OutputDevice::SetFont( name=\"%s\", h=%ld)\n",
5570 OUStringToOString( aFont.GetName(), RTL_TEXTENCODING_UTF8 ).getStr(),
5571 aFont.GetSize().Height() );
5572 #endif
5574 if ( !maFont.IsSameInstance( aFont ) )
5576 // Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color,
5577 // because SetTextColor() is used for this.
5578 // #i28759# maTextColor might have been changed behind our back, commit then, too.
5579 if( aFont.GetColor() != COL_TRANSPARENT
5580 && (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) )
5582 maTextColor = aFont.GetColor();
5583 mbInitTextColor = TRUE;
5584 if( mpMetaFile )
5585 mpMetaFile->AddAction( new MetaTextColorAction( aFont.GetColor() ) );
5587 maFont = aFont;
5588 mbNewFont = TRUE;
5590 if( mpAlphaVDev )
5592 // #i30463#
5593 // Since SetFont might change the text color, apply that only
5594 // selectively to alpha vdev (which normally paints opaque text
5595 // with COL_BLACK)
5596 if( aFont.GetColor() != COL_TRANSPARENT )
5598 mpAlphaVDev->SetTextColor( COL_BLACK );
5599 aFont.SetColor( COL_TRANSPARENT );
5602 mpAlphaVDev->SetFont( aFont );
5607 // -----------------------------------------------------------------------
5609 void OutputDevice::SetLayoutMode( ULONG nTextLayoutMode )
5611 DBG_TRACE( "OutputDevice::SetTextLayoutMode()" );
5613 if( mpMetaFile )
5614 mpMetaFile->AddAction( new MetaLayoutModeAction( nTextLayoutMode ) );
5616 mnTextLayoutMode = nTextLayoutMode;
5618 if( mpAlphaVDev )
5619 mpAlphaVDev->SetLayoutMode( nTextLayoutMode );
5622 // -----------------------------------------------------------------------
5624 void OutputDevice::SetDigitLanguage( LanguageType eTextLanguage )
5626 DBG_TRACE( "OutputDevice::SetTextLanguage()" );
5628 if( mpMetaFile )
5629 mpMetaFile->AddAction( new MetaTextLanguageAction( eTextLanguage ) );
5631 meTextLanguage = eTextLanguage;
5633 if( mpAlphaVDev )
5634 mpAlphaVDev->SetDigitLanguage( eTextLanguage );
5637 // -----------------------------------------------------------------------
5639 void OutputDevice::SetTextColor( const Color& rColor )
5641 DBG_TRACE( "OutputDevice::SetTextColor()" );
5642 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5644 Color aColor( rColor );
5646 if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
5647 DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
5648 DRAWMODE_SETTINGSTEXT ) )
5650 if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5651 aColor = Color( COL_BLACK );
5652 else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5653 aColor = Color( COL_WHITE );
5654 else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5656 const UINT8 cLum = aColor.GetLuminance();
5657 aColor = Color( cLum, cLum, cLum );
5659 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5660 aColor = GetSettings().GetStyleSettings().GetFontColor();
5662 if ( mnDrawMode & DRAWMODE_GHOSTEDTEXT )
5664 aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5665 (aColor.GetGreen() >> 1) | 0x80,
5666 (aColor.GetBlue() >> 1) | 0x80 );
5670 if ( mpMetaFile )
5671 mpMetaFile->AddAction( new MetaTextColorAction( aColor ) );
5673 if ( maTextColor != aColor )
5675 maTextColor = aColor;
5676 mbInitTextColor = TRUE;
5679 if( mpAlphaVDev )
5680 mpAlphaVDev->SetTextColor( COL_BLACK );
5683 // -----------------------------------------------------------------------
5685 void OutputDevice::SetTextFillColor()
5687 DBG_TRACE( "OutputDevice::SetTextFillColor()" );
5688 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5690 if ( mpMetaFile )
5691 mpMetaFile->AddAction( new MetaTextFillColorAction( Color(), FALSE ) );
5693 if ( maFont.GetColor() != Color( COL_TRANSPARENT ) )
5694 maFont.SetFillColor( Color( COL_TRANSPARENT ) );
5695 if ( !maFont.IsTransparent() )
5696 maFont.SetTransparent( TRUE );
5698 if( mpAlphaVDev )
5699 mpAlphaVDev->SetTextFillColor();
5702 // -----------------------------------------------------------------------
5704 void OutputDevice::SetTextFillColor( const Color& rColor )
5706 DBG_TRACE( "OutputDevice::SetTextFillColor()" );
5707 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5709 Color aColor( rColor );
5710 BOOL bTransFill = ImplIsColorTransparent( aColor ) ? TRUE : FALSE;
5712 if ( !bTransFill )
5714 if ( mnDrawMode & ( DRAWMODE_BLACKFILL | DRAWMODE_WHITEFILL |
5715 DRAWMODE_GRAYFILL | DRAWMODE_NOFILL |
5716 DRAWMODE_GHOSTEDFILL | DRAWMODE_SETTINGSFILL ) )
5718 if ( mnDrawMode & DRAWMODE_BLACKFILL )
5719 aColor = Color( COL_BLACK );
5720 else if ( mnDrawMode & DRAWMODE_WHITEFILL )
5721 aColor = Color( COL_WHITE );
5722 else if ( mnDrawMode & DRAWMODE_GRAYFILL )
5724 const UINT8 cLum = aColor.GetLuminance();
5725 aColor = Color( cLum, cLum, cLum );
5727 else if( mnDrawMode & DRAWMODE_SETTINGSFILL )
5728 aColor = GetSettings().GetStyleSettings().GetWindowColor();
5729 else if ( mnDrawMode & DRAWMODE_NOFILL )
5731 aColor = Color( COL_TRANSPARENT );
5732 bTransFill = TRUE;
5735 if ( !bTransFill && (mnDrawMode & DRAWMODE_GHOSTEDFILL) )
5737 aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5738 (aColor.GetGreen() >> 1) | 0x80,
5739 (aColor.GetBlue() >> 1) | 0x80 );
5744 if ( mpMetaFile )
5745 mpMetaFile->AddAction( new MetaTextFillColorAction( aColor, TRUE ) );
5747 if ( maFont.GetFillColor() != aColor )
5748 maFont.SetFillColor( aColor );
5749 if ( maFont.IsTransparent() != bTransFill )
5750 maFont.SetTransparent( bTransFill );
5752 if( mpAlphaVDev )
5753 mpAlphaVDev->SetTextFillColor( COL_BLACK );
5756 // -----------------------------------------------------------------------
5758 Color OutputDevice::GetTextFillColor() const
5760 if ( maFont.IsTransparent() )
5761 return Color( COL_TRANSPARENT );
5762 else
5763 return maFont.GetFillColor();
5766 // -----------------------------------------------------------------------
5768 void OutputDevice::SetTextLineColor()
5770 DBG_TRACE( "OutputDevice::SetTextLineColor()" );
5771 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5773 if ( mpMetaFile )
5774 mpMetaFile->AddAction( new MetaTextLineColorAction( Color(), FALSE ) );
5776 maTextLineColor = Color( COL_TRANSPARENT );
5778 if( mpAlphaVDev )
5779 mpAlphaVDev->SetTextLineColor();
5782 // -----------------------------------------------------------------------
5784 void OutputDevice::SetTextLineColor( const Color& rColor )
5786 DBG_TRACE( "OutputDevice::SetTextLineColor()" );
5787 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5789 Color aColor( rColor );
5791 if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
5792 DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
5793 DRAWMODE_SETTINGSTEXT ) )
5795 if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5796 aColor = Color( COL_BLACK );
5797 else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5798 aColor = Color( COL_WHITE );
5799 else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5801 const UINT8 cLum = aColor.GetLuminance();
5802 aColor = Color( cLum, cLum, cLum );
5804 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5805 aColor = GetSettings().GetStyleSettings().GetFontColor();
5807 if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT)
5808 && (aColor.GetColor() != COL_TRANSPARENT) )
5810 aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5811 (aColor.GetGreen() >> 1) | 0x80,
5812 (aColor.GetBlue() >> 1) | 0x80 );
5816 if ( mpMetaFile )
5817 mpMetaFile->AddAction( new MetaTextLineColorAction( aColor, TRUE ) );
5819 maTextLineColor = aColor;
5821 if( mpAlphaVDev )
5822 mpAlphaVDev->SetTextLineColor( COL_BLACK );
5825 // -----------------------------------------------------------------------
5827 void OutputDevice::SetOverlineColor()
5829 DBG_TRACE( "OutputDevice::SetOverlineColor()" );
5830 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5832 if ( mpMetaFile )
5833 mpMetaFile->AddAction( new MetaOverlineColorAction( Color(), FALSE ) );
5835 maOverlineColor = Color( COL_TRANSPARENT );
5837 if( mpAlphaVDev )
5838 mpAlphaVDev->SetOverlineColor();
5841 // -----------------------------------------------------------------------
5843 void OutputDevice::SetOverlineColor( const Color& rColor )
5845 DBG_TRACE( "OutputDevice::SetOverlineColor()" );
5846 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5848 Color aColor( rColor );
5850 if ( mnDrawMode & ( DRAWMODE_BLACKTEXT | DRAWMODE_WHITETEXT |
5851 DRAWMODE_GRAYTEXT | DRAWMODE_GHOSTEDTEXT |
5852 DRAWMODE_SETTINGSTEXT ) )
5854 if ( mnDrawMode & DRAWMODE_BLACKTEXT )
5855 aColor = Color( COL_BLACK );
5856 else if ( mnDrawMode & DRAWMODE_WHITETEXT )
5857 aColor = Color( COL_WHITE );
5858 else if ( mnDrawMode & DRAWMODE_GRAYTEXT )
5860 const UINT8 cLum = aColor.GetLuminance();
5861 aColor = Color( cLum, cLum, cLum );
5863 else if ( mnDrawMode & DRAWMODE_SETTINGSTEXT )
5864 aColor = GetSettings().GetStyleSettings().GetFontColor();
5866 if( (mnDrawMode & DRAWMODE_GHOSTEDTEXT)
5867 && (aColor.GetColor() != COL_TRANSPARENT) )
5869 aColor = Color( (aColor.GetRed() >> 1) | 0x80,
5870 (aColor.GetGreen() >> 1) | 0x80,
5871 (aColor.GetBlue() >> 1) | 0x80 );
5875 if ( mpMetaFile )
5876 mpMetaFile->AddAction( new MetaOverlineColorAction( aColor, TRUE ) );
5878 maOverlineColor = aColor;
5880 if( mpAlphaVDev )
5881 mpAlphaVDev->SetOverlineColor( COL_BLACK );
5884 // -----------------------------------------------------------------------
5887 void OutputDevice::SetTextAlign( TextAlign eAlign )
5889 DBG_TRACE( "OutputDevice::SetTextAlign()" );
5890 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5892 if ( mpMetaFile )
5893 mpMetaFile->AddAction( new MetaTextAlignAction( eAlign ) );
5895 if ( maFont.GetAlign() != eAlign )
5897 maFont.SetAlign( eAlign );
5898 mbNewFont = TRUE;
5901 if( mpAlphaVDev )
5902 mpAlphaVDev->SetTextAlign( eAlign );
5905 // -----------------------------------------------------------------------
5907 void OutputDevice::DrawTextLine( const Point& rPos, long nWidth,
5908 FontStrikeout eStrikeout,
5909 FontUnderline eUnderline,
5910 FontUnderline eOverline,
5911 BOOL bUnderlineAbove )
5913 DBG_TRACE( "OutputDevice::DrawTextLine()" );
5914 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5916 if ( mpMetaFile )
5917 mpMetaFile->AddAction( new MetaTextLineAction( rPos, nWidth, eStrikeout, eUnderline, eOverline ) );
5919 if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) &&
5920 ((eOverline == UNDERLINE_NONE) || (eOverline == UNDERLINE_DONTKNOW)) &&
5921 ((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
5922 return;
5924 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
5925 return;
5927 // we need a graphics
5928 if( !mpGraphics && !ImplGetGraphics() )
5929 return;
5930 if( mbInitClipRegion )
5931 ImplInitClipRegion();
5932 if( mbOutputClipped )
5933 return;
5935 // initialize font if needed to get text offsets
5936 // TODO: only needed for mnTextOff!=(0,0)
5937 if( mbNewFont )
5938 if( !ImplNewFont() )
5939 return;
5940 if( mbInitFont )
5941 ImplInitFont();
5943 Point aPos = ImplLogicToDevicePixel( rPos );
5944 nWidth = ImplLogicWidthToDevicePixel( nWidth );
5945 aPos += Point( mnTextOffX, mnTextOffY );
5946 ImplDrawTextLine( aPos.X(), aPos.X(), aPos.Y(), nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
5948 if( mpAlphaVDev )
5949 mpAlphaVDev->DrawTextLine( rPos, nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
5952 // ------------------------------------------------------------------------
5954 BOOL OutputDevice::IsTextUnderlineAbove( const Font& rFont )
5956 return ImplIsUnderlineAbove( rFont );
5959 // ------------------------------------------------------------------------
5961 void OutputDevice::DrawWaveLine( const Point& rStartPos, const Point& rEndPos,
5962 USHORT nStyle )
5964 DBG_TRACE( "OutputDevice::DrawWaveLine()" );
5965 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
5967 if ( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
5968 return;
5970 // we need a graphics
5971 if( !mpGraphics )
5972 if( !ImplGetGraphics() )
5973 return;
5975 if ( mbInitClipRegion )
5976 ImplInitClipRegion();
5977 if ( mbOutputClipped )
5978 return;
5980 if( mbNewFont )
5981 if( !ImplNewFont() )
5982 return;
5984 Point aStartPt = ImplLogicToDevicePixel( rStartPos );
5985 Point aEndPt = ImplLogicToDevicePixel( rEndPos );
5986 long nStartX = aStartPt.X();
5987 long nStartY = aStartPt.Y();
5988 long nEndX = aEndPt.X();
5989 long nEndY = aEndPt.Y();
5990 short nOrientation = 0;
5992 // when rotated
5993 if ( (nStartY != nEndY) || (nStartX > nEndX) )
5995 long nDX = nEndX - nStartX;
5996 double nO = atan2( -nEndY + nStartY, ((nDX == 0L) ? 0.000000001 : nDX) );
5997 nO /= F_PI1800;
5998 nOrientation = (short)nO;
5999 ImplRotatePos( nStartX, nStartY, nEndX, nEndY, -nOrientation );
6002 long nWaveHeight;
6003 if ( nStyle == WAVE_NORMAL )
6005 nWaveHeight = 3;
6006 nStartY++;
6007 nEndY++;
6009 else if( nStyle == WAVE_SMALL )
6011 nWaveHeight = 2;
6012 nStartY++;
6013 nEndY++;
6015 else // WAVE_FLAT
6016 nWaveHeight = 1;
6018 // #109280# make sure the waveline does not exceed the descent to avoid paint problems
6019 ImplFontEntry* pFontEntry = mpFontEntry;
6020 if( nWaveHeight > pFontEntry->maMetric.mnWUnderlineSize )
6021 nWaveHeight = pFontEntry->maMetric.mnWUnderlineSize;
6023 ImplDrawWaveLine( nStartX, nStartY, nStartX, nStartY,
6024 nEndX-nStartX, nWaveHeight, 1,
6025 nOrientation, GetLineColor() );
6026 if( mpAlphaVDev )
6027 mpAlphaVDev->DrawWaveLine( rStartPos, rEndPos, nStyle );
6030 // -----------------------------------------------------------------------
6032 void OutputDevice::DrawText( const Point& rStartPt, const String& rStr,
6033 xub_StrLen nIndex, xub_StrLen nLen,
6034 MetricVector* pVector, String* pDisplayText
6037 if( mpOutDevData && mpOutDevData->mpRecordLayout )
6039 pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
6040 pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
6043 DBG_TRACE( "OutputDevice::DrawText()" );
6044 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6046 #if OSL_DEBUG_LEVEL > 2
6047 fprintf( stderr, " OutputDevice::DrawText(\"%s\")\n",
6048 OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ).getStr() );
6049 #endif
6051 if ( mpMetaFile )
6052 mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
6053 if( pVector )
6055 Region aClip( GetClipRegion() );
6056 if( meOutDevType == OUTDEV_WINDOW )
6057 aClip.Intersect( Rectangle( Point(), GetOutputSize() ) );
6058 if( mpOutDevData && mpOutDevData->mpRecordLayout )
6060 mpOutDevData->mpRecordLayout->m_aLineIndices.push_back( mpOutDevData->mpRecordLayout->m_aDisplayText.Len() );
6061 aClip.Intersect( mpOutDevData->maRecordRect );
6063 if( ! aClip.IsNull() )
6065 MetricVector aTmp;
6066 GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp );
6068 bool bInserted = false;
6069 for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ )
6071 bool bAppend = false;
6073 if( aClip.IsOver( *it ) )
6074 bAppend = true;
6075 else if( rStr.GetChar( nIndex ) == ' ' && bInserted )
6077 MetricVector::const_iterator next = it;
6078 ++next;
6079 if( next != aTmp.end() && aClip.IsOver( *next ) )
6080 bAppend = true;
6083 if( bAppend )
6085 pVector->push_back( *it );
6086 if( pDisplayText )
6087 pDisplayText->Append( rStr.GetChar( nIndex ) );
6088 bInserted = true;
6092 else
6094 GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector );
6095 if( pDisplayText )
6096 pDisplayText->Append( rStr.Copy( nIndex, nLen ) );
6100 if ( !IsDeviceOutputNecessary() || pVector )
6101 return;
6103 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, NULL, true );
6104 if( pSalLayout )
6106 ImplDrawText( *pSalLayout );
6107 pSalLayout->Release();
6110 if( mpAlphaVDev )
6111 mpAlphaVDev->DrawText( rStartPt, rStr, nIndex, nLen, pVector, pDisplayText );
6114 // -----------------------------------------------------------------------
6116 long OutputDevice::GetTextWidth( const String& rStr,
6117 xub_StrLen nIndex, xub_StrLen nLen ) const
6119 DBG_TRACE( "OutputDevice::GetTextWidth()" );
6120 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6122 long nWidth = GetTextArray( rStr, NULL, nIndex, nLen );
6123 return nWidth;
6126 // -----------------------------------------------------------------------
6128 long OutputDevice::GetTextHeight() const
6130 DBG_TRACE( "OutputDevice::GetTextHeight()" );
6131 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6133 if( mbNewFont )
6134 if( !ImplNewFont() )
6135 return 0;
6136 if( mbInitFont )
6137 if( !ImplNewFont() )
6138 return 0;
6140 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
6142 if ( mbMap )
6143 nHeight = ImplDevicePixelToLogicHeight( nHeight );
6145 return nHeight;
6148 // -----------------------------------------------------------------------
6150 void OutputDevice::DrawTextArray( const Point& rStartPt, const String& rStr,
6151 const sal_Int32* pDXAry,
6152 xub_StrLen nIndex, xub_StrLen nLen )
6154 DBG_TRACE( "OutputDevice::DrawTextArray()" );
6155 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6157 if ( mpMetaFile )
6158 mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
6160 if ( !IsDeviceOutputNecessary() )
6161 return;
6163 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true );
6164 if( pSalLayout )
6166 ImplDrawText( *pSalLayout );
6167 pSalLayout->Release();
6170 if( mpAlphaVDev )
6171 mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen );
6174 // -----------------------------------------------------------------------
6176 long OutputDevice::GetTextArray( const String& rStr, sal_Int32* pDXAry,
6177 xub_StrLen nIndex, xub_StrLen nLen ) const
6179 DBG_TRACE( "OutputDevice::GetTextArray()" );
6180 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6182 if( nIndex >= rStr.Len() )
6183 return 0;
6184 if( (ULONG)nIndex+nLen >= rStr.Len() )
6185 nLen = rStr.Len() - nIndex;
6187 // do layout
6188 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
6189 if( !pSalLayout )
6190 return 0;
6192 long nWidth = pSalLayout->FillDXArray( pDXAry );
6193 int nWidthFactor = pSalLayout->GetUnitsPerPixel();
6194 pSalLayout->Release();
6196 // convert virtual char widths to virtual absolute positions
6197 if( pDXAry )
6198 for( int i = 1; i < nLen; ++i )
6199 pDXAry[ i ] += pDXAry[ i-1 ];
6201 // convert from font units to logical units
6202 if( mbMap )
6204 if( pDXAry )
6205 for( int i = 0; i < nLen; ++i )
6206 pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] );
6207 nWidth = ImplDevicePixelToLogicWidth( nWidth );
6210 if( nWidthFactor > 1 )
6212 if( pDXAry )
6213 for( int i = 0; i < nLen; ++i )
6214 pDXAry[i] /= nWidthFactor;
6215 nWidth /= nWidthFactor;
6218 return nWidth;
6221 // -----------------------------------------------------------------------
6223 bool OutputDevice::GetCaretPositions( const XubString& rStr, sal_Int32* pCaretXArray,
6224 xub_StrLen nIndex, xub_StrLen nLen,
6225 sal_Int32* pDXAry, long nLayoutWidth,
6226 BOOL bCellBreaking ) const
6228 DBG_TRACE( "OutputDevice::GetCaretPositions()" );
6229 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6231 if( nIndex >= rStr.Len() )
6232 return false;
6233 if( (ULONG)nIndex+nLen >= rStr.Len() )
6234 nLen = rStr.Len() - nIndex;
6236 // layout complex text
6237 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen,
6238 Point(0,0), nLayoutWidth, pDXAry );
6239 if( !pSalLayout )
6240 return false;
6242 int nWidthFactor = pSalLayout->GetUnitsPerPixel();
6243 pSalLayout->GetCaretPositions( 2*nLen, pCaretXArray );
6244 long nWidth = pSalLayout->GetTextWidth();
6245 pSalLayout->Release();
6247 // fixup unknown caret positions
6248 int i;
6249 for( i = 0; i < 2 * nLen; ++i )
6250 if( pCaretXArray[ i ] >= 0 )
6251 break;
6252 long nXPos = pCaretXArray[ i ];
6253 for( i = 0; i < 2 * nLen; ++i )
6255 if( pCaretXArray[ i ] >= 0 )
6256 nXPos = pCaretXArray[ i ];
6257 else
6258 pCaretXArray[ i ] = nXPos;
6261 // handle window mirroring
6262 if( IsRTLEnabled() )
6264 for( i = 0; i < 2 * nLen; ++i )
6265 pCaretXArray[i] = nWidth - pCaretXArray[i] - 1;
6268 // convert from font units to logical units
6269 if( mbMap )
6271 for( i = 0; i < 2*nLen; ++i )
6272 pCaretXArray[i] = ImplDevicePixelToLogicWidth( pCaretXArray[i] );
6275 if( nWidthFactor != 1 )
6277 for( i = 0; i < 2*nLen; ++i )
6278 pCaretXArray[i] /= nWidthFactor;
6281 // if requested move caret position to cell limits
6282 if( bCellBreaking )
6284 ; // TODO
6287 return true;
6290 // -----------------------------------------------------------------------
6292 void OutputDevice::DrawStretchText( const Point& rStartPt, ULONG nWidth,
6293 const String& rStr,
6294 xub_StrLen nIndex, xub_StrLen nLen )
6296 DBG_TRACE( "OutputDevice::DrawStretchText()" );
6297 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6299 if ( mpMetaFile )
6300 mpMetaFile->AddAction( new MetaStretchTextAction( rStartPt, nWidth, rStr, nIndex, nLen ) );
6302 if ( !IsDeviceOutputNecessary() )
6303 return;
6305 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, nWidth, NULL, true );
6306 if( pSalLayout )
6308 ImplDrawText( *pSalLayout );
6309 pSalLayout->Release();
6312 if( mpAlphaVDev )
6313 mpAlphaVDev->DrawStretchText( rStartPt, nWidth, rStr, nIndex, nLen );
6316 // -----------------------------------------------------------------------
6318 ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( String& rStr,
6319 xub_StrLen nMinIndex, xub_StrLen nLen,
6320 long nPixelWidth, const sal_Int32* pDXArray ) const
6322 // get string length for calculating extents
6323 xub_StrLen nEndIndex = rStr.Len();
6324 if( (ULONG)nMinIndex + nLen < nEndIndex )
6325 nEndIndex = nMinIndex + nLen;
6327 // don't bother if there is nothing to do
6328 if( nEndIndex < nMinIndex )
6329 nEndIndex = nMinIndex;
6331 int nLayoutFlags = 0;
6332 if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL )
6333 nLayoutFlags |= SAL_LAYOUT_BIDI_RTL;
6334 if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG )
6335 nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
6336 else if( 0 == (mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) )
6338 // disable Bidi if no RTL hint and no RTL codes used
6339 const xub_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
6340 const xub_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
6341 for( ; pStr < pEnd; ++pStr )
6342 if( ((*pStr >= 0x0580) && (*pStr < 0x0800)) // middle eastern scripts
6343 || ((*pStr >= 0xFB18) && (*pStr < 0xFE00)) // hebrew + arabic A presentation forms
6344 || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B
6345 break;
6346 if( pStr >= pEnd )
6347 nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
6350 if( mbKerning )
6351 nLayoutFlags |= SAL_LAYOUT_KERNING_PAIRS;
6352 if( maFont.GetKerning() & KERNING_ASIAN )
6353 nLayoutFlags |= SAL_LAYOUT_KERNING_ASIAN;
6354 if( maFont.IsVertical() )
6355 nLayoutFlags |= SAL_LAYOUT_VERTICAL;
6357 if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES )
6358 nLayoutFlags |= SAL_LAYOUT_ENABLE_LIGATURES;
6359 else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED )
6360 nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
6361 else
6363 // disable CTL for non-CTL text
6364 const sal_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
6365 const sal_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
6366 for( ; pStr < pEnd; ++pStr )
6367 if( ((*pStr >= 0x0300) && (*pStr < 0x0370)) // diacritical marks
6368 || ((*pStr >= 0x0590) && (*pStr < 0x10A0)) // many CTL scripts
6369 || ((*pStr >= 0x1100) && (*pStr < 0x1200)) // hangul jamo
6370 || ((*pStr >= 0x1700) && (*pStr < 0x1900)) // many CTL scripts
6371 || ((*pStr >= 0xFB1D) && (*pStr < 0xFE00)) // middle east presentation
6372 || ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation B
6373 break;
6374 if( pStr >= pEnd )
6375 nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
6378 if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS)
6380 // disable character localization when no digits used
6381 const sal_Unicode* pBase = rStr.GetBuffer();
6382 const sal_Unicode* pStr = pBase + nMinIndex;
6383 const sal_Unicode* pEnd = pBase + nEndIndex;
6384 for( ; pStr < pEnd; ++pStr )
6386 // TODO: are there non-digit localizations?
6387 if( (*pStr >= '0') && (*pStr <= '9') )
6389 // translate characters to local preference
6390 sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
6391 if( cChar != *pStr )
6392 // TODO: are the localized digit surrogates?
6393 rStr.SetChar( static_cast<USHORT>(pStr - pBase),
6394 static_cast<sal_Unicode>(cChar) );
6399 // right align for RTL text, DRAWPOS_REVERSED, RTL window style
6400 bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0);
6401 if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT )
6402 bRightAlign = false;
6403 else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT )
6404 bRightAlign = true;
6405 // SSA: hack for western office, ie text get right aligned
6406 // for debugging purposes of mirrored UI
6407 //static const char* pEnv = getenv( "SAL_RTL_MIRRORTEXT" );
6408 bool bRTLWindow = IsRTLEnabled();
6409 bRightAlign ^= bRTLWindow;
6410 if( bRightAlign )
6411 nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN;
6413 // set layout options
6414 ImplLayoutArgs aLayoutArgs( rStr.GetBuffer(), rStr.Len(), nMinIndex, nEndIndex, nLayoutFlags );
6416 int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0;
6417 aLayoutArgs.SetOrientation( nOrientation );
6419 aLayoutArgs.SetLayoutWidth( nPixelWidth );
6420 aLayoutArgs.SetDXArray( pDXArray );
6422 return aLayoutArgs;
6425 // -----------------------------------------------------------------------
6427 SalLayout* OutputDevice::ImplLayout( const String& rOrigStr,
6428 xub_StrLen nMinIndex,
6429 xub_StrLen nLen,
6430 const Point& rLogicalPos,
6431 long nLogicalWidth,
6432 const sal_Int32* pDXArray,
6433 bool bFilter ) const
6435 // we need a graphics
6436 if( !mpGraphics )
6437 if( !ImplGetGraphics() )
6438 return NULL;
6440 // initialize font if needed
6441 if( mbNewFont )
6442 if( !ImplNewFont() )
6443 return NULL;
6444 if( mbInitFont )
6445 ImplInitFont();
6447 // check string index and length
6448 String aStr = rOrigStr;
6449 if( (ULONG)nMinIndex + nLen >= aStr.Len() )
6451 if( nMinIndex < aStr.Len() )
6452 nLen = aStr.Len() - nMinIndex;
6453 else
6454 return NULL;
6457 // filter out special markers
6458 if( bFilter )
6460 xub_StrLen nCutStart, nCutStop, nOrgLen = nLen;
6461 bool bFiltered = mpGraphics->filterText( rOrigStr, aStr, nMinIndex, nLen, nCutStart, nCutStop );
6462 if( !nLen )
6463 return NULL;
6465 if( bFiltered && nCutStop != nCutStart && pDXArray )
6467 if( !nLen )
6468 pDXArray = NULL;
6469 else
6471 sal_Int32* pAry = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen);
6472 if( nCutStart > nMinIndex )
6473 memcpy( pAry, pDXArray, sizeof(sal_Int32)*(nCutStart-nMinIndex) );
6474 // note: nCutStart will never be smaller than nMinIndex
6475 memcpy( pAry+nCutStart-nMinIndex,
6476 pDXArray + nOrgLen - (nCutStop-nMinIndex),
6477 sizeof(sal_Int32)*(nLen - (nCutStart-nMinIndex)) );
6478 pDXArray = pAry;
6483 // convert from logical units to physical units
6484 // recode string if needed
6485 if( mpFontEntry->mpConversion )
6486 ImplRecodeString( mpFontEntry->mpConversion, aStr, 0, aStr.Len() );
6488 long nPixelWidth = nLogicalWidth;
6489 if( nLogicalWidth && mbMap )
6490 nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth );
6491 if( pDXArray && mbMap )
6493 // convert from logical units to font units using a temporary array
6494 sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );
6495 // using base position for better rounding a.k.a. "dancing characters"
6496 int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() );
6497 for( int i = 0; i < nLen; ++i )
6498 pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs;
6500 pDXArray = pTempDXAry;
6503 ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray );
6505 // get matching layout object for base font
6506 SalLayout* pSalLayout = NULL;
6507 if( mpPDFWriter )
6508 pSalLayout = mpPDFWriter->GetTextLayout( aLayoutArgs, &mpFontEntry->maFontSelData );
6510 if( !pSalLayout )
6511 pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );
6513 // layout text
6514 if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) )
6516 pSalLayout->Release();
6517 pSalLayout = NULL;
6520 if( !pSalLayout )
6521 return NULL;
6523 // do glyph fallback if needed
6524 // #105768# avoid fallback for very small font sizes
6525 if( aLayoutArgs.NeedFallback() )
6526 if( mpFontEntry && (mpFontEntry->maFontSelData.mnHeight >= 3) )
6527 pSalLayout = ImplGlyphFallbackLayout( pSalLayout, aLayoutArgs );
6529 // position, justify, etc. the layout
6530 pSalLayout->AdjustLayout( aLayoutArgs );
6531 pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos );
6532 // adjust to right alignment if necessary
6533 if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN )
6535 long nRTLOffset;
6536 if( pDXArray )
6537 nRTLOffset = pDXArray[ nLen - 1 ];
6538 else if( nPixelWidth )
6539 nRTLOffset = nPixelWidth;
6540 else
6541 nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
6542 pSalLayout->DrawOffset().X() = 1 - nRTLOffset;
6545 return pSalLayout;
6548 // -----------------------------------------------------------------------
6550 SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const
6552 // prepare multi level glyph fallback
6553 MultiSalLayout* pMultiSalLayout = NULL;
6554 ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
6555 rLayoutArgs.PrepareFallback();
6556 rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
6558 #if defined(HDU_DEBUG)
6560 int nCharPos = -1;
6561 bool bRTL = false;
6562 fprintf(stderr,"OD:ImplLayout Glyph Fallback for");
6563 for( int i=0; i<8 && rLayoutArgs.GetNextPos( &nCharPos, &bRTL); ++i )
6564 fprintf(stderr," U+%04X", rLayoutArgs.mpStr[ nCharPos ] );
6565 fprintf(stderr,"\n");
6566 rLayoutArgs.ResetPos();
6568 #endif
6569 // get list of unicodes that need glyph fallback
6570 int nCharPos = -1;
6571 bool bRTL = false;
6572 rtl::OUStringBuffer aMissingCodeBuf;
6573 while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) )
6574 aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] );
6575 rLayoutArgs.ResetPos();
6576 rtl::OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();
6578 ImplFontSelectData aFontSelData = mpFontEntry->maFontSelData;
6579 // when device specific font substitution may have been performed for
6580 // the originally selected font then make sure that a fallback to that
6581 // font is performed first
6582 int nDevSpecificFallback = 0;
6583 if( mpOutDevData && !mpOutDevData->maDevFontSubst.Empty() )
6584 nDevSpecificFallback = 1;
6586 // try if fallback fonts support the missing unicodes
6587 for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
6589 // find a font family suited for glyph fallback
6590 #ifndef FONTFALLBACK_HOOKS_DISABLED
6591 // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry
6592 // if the system-specific glyph fallback is active
6593 aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level
6594 #endif
6595 ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontList,
6596 aFontSelData, nFallbackLevel-nDevSpecificFallback, aMissingCodes );
6597 if( !pFallbackFont )
6598 break;
6600 aFontSelData.mpFontEntry = pFallbackFont;
6601 aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData;
6602 if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1)
6604 // ignore fallback font if it is the same as the original font
6605 if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData )
6607 mpFontCache->Release( pFallbackFont );
6608 continue;
6612 #if defined(HDU_DEBUG)
6614 ByteString aOrigFontName( maFont.GetName(), RTL_TEXTENCODING_UTF8);
6615 ByteString aFallbackName( aFontSelData.mpFontData->GetFamilyName(),
6616 RTL_TEXTENCODING_UTF8);
6617 fprintf(stderr,"\tGlyphFallback[lvl=%d] \"%s\" -> \"%s\" (q=%d)\n",
6618 nFallbackLevel, aOrigFontName.GetBuffer(), aFallbackName.GetBuffer(),
6619 aFontSelData.mpFontData->GetQuality());
6621 #endif
6623 pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel );
6625 // create and add glyph fallback layout to multilayout
6626 rLayoutArgs.ResetPos();
6627 SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel );
6628 if( pFallback )
6630 if( pFallback->LayoutText( rLayoutArgs ) )
6632 if( !pMultiSalLayout )
6633 pMultiSalLayout = new MultiSalLayout( *pSalLayout );
6634 pMultiSalLayout->AddFallback( *pFallback,
6635 rLayoutArgs.maRuns, aFontSelData.mpFontData );
6636 if (nFallbackLevel == MAX_FALLBACK-1)
6637 pMultiSalLayout->SetInComplete();
6639 else
6641 // there is no need for a font that couldn't resolve anything
6642 pFallback->Release();
6646 mpFontCache->Release( pFallbackFont );
6648 // break when this fallback was sufficient
6649 if( !rLayoutArgs.PrepareFallback() )
6650 break;
6653 if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) )
6654 pSalLayout = pMultiSalLayout;
6656 // restore orig font settings
6657 pSalLayout->InitFont();
6658 rLayoutArgs.maRuns = aLayoutRuns;
6660 return pSalLayout;
6663 // -----------------------------------------------------------------------
6665 BOOL OutputDevice::GetTextIsRTL(
6666 const String& rString,
6667 xub_StrLen nIndex, xub_StrLen nLen ) const
6669 String aStr( rString );
6670 ImplLayoutArgs aArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
6671 bool bRTL = false;
6672 int nCharPos = -1;
6673 aArgs.GetNextPos( &nCharPos, &bRTL );
6674 return (nCharPos != nIndex) ? TRUE : FALSE;
6677 // -----------------------------------------------------------------------
6679 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth,
6680 xub_StrLen nIndex, xub_StrLen nLen,
6681 long nCharExtra, BOOL /*TODO: bCellBreaking*/ ) const
6683 DBG_TRACE( "OutputDevice::GetTextBreak()" );
6684 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6686 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
6687 xub_StrLen nRetVal = STRING_LEN;
6688 if( pSalLayout )
6690 // convert logical widths into layout units
6691 // NOTE: be very careful to avoid rounding errors for nCharExtra case
6692 // problem with rounding errors especially for small nCharExtras
6693 // TODO: remove when layout units have subpixel granularity
6694 long nWidthFactor = pSalLayout->GetUnitsPerPixel();
6695 long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
6696 nTextWidth *= nWidthFactor * nSubPixelFactor;
6697 long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
6698 long nExtraPixelWidth = 0;
6699 if( nCharExtra != 0 )
6701 nCharExtra *= nWidthFactor * nSubPixelFactor;
6702 nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
6704 nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6706 pSalLayout->Release();
6709 return nRetVal;
6712 // -----------------------------------------------------------------------
6714 xub_StrLen OutputDevice::GetTextBreak( const String& rStr, long nTextWidth,
6715 sal_Unicode nHyphenatorChar, xub_StrLen& rHyphenatorPos,
6716 xub_StrLen nIndex, xub_StrLen nLen,
6717 long nCharExtra ) const
6719 DBG_TRACE( "OutputDevice::GetTextBreak()" );
6720 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
6722 rHyphenatorPos = STRING_LEN;
6724 SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
6725 if( !pSalLayout )
6726 return STRING_LEN;
6728 // convert logical widths into layout units
6729 // NOTE: be very careful to avoid rounding errors for nCharExtra case
6730 // problem with rounding errors especially for small nCharExtras
6731 // TODO: remove when layout units have subpixel granularity
6732 long nWidthFactor = pSalLayout->GetUnitsPerPixel();
6733 long nSubPixelFactor = (nWidthFactor < 64 ) ? 64 : 1;
6735 nTextWidth *= nWidthFactor * nSubPixelFactor;
6736 long nTextPixelWidth = ImplLogicWidthToDevicePixel( nTextWidth );
6737 long nExtraPixelWidth = 0;
6738 if( nCharExtra != 0 )
6740 nCharExtra *= nWidthFactor * nSubPixelFactor;
6741 nExtraPixelWidth = ImplLogicWidthToDevicePixel( nCharExtra );
6744 // calculate un-hyphenated break position
6745 xub_StrLen nRetVal = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6747 // calculate hyphenated break position
6748 String aHyphenatorStr( &nHyphenatorChar, 1 );
6749 xub_StrLen nTempLen = 1;
6750 SalLayout* pHyphenatorLayout = ImplLayout( aHyphenatorStr, 0, nTempLen );
6751 if( pHyphenatorLayout )
6753 // calculate subpixel width of hyphenation character
6754 long nHyphenatorPixelWidth = pHyphenatorLayout->GetTextWidth() * nSubPixelFactor;
6755 pHyphenatorLayout->Release();
6757 // calculate hyphenated break position
6758 nTextPixelWidth -= nHyphenatorPixelWidth;
6759 if( nExtraPixelWidth > 0 )
6760 nTextPixelWidth -= nExtraPixelWidth;
6762 rHyphenatorPos = sal::static_int_cast<xub_StrLen>(pSalLayout->GetTextBreak( nTextPixelWidth, nExtraPixelWidth, nSubPixelFactor ));
6764 if( rHyphenatorPos > nRetVal )
6765 rHyphenatorPos = nRetVal;
6768 pSalLayout->Release();
6769 return nRetVal;
6772 // -----------------------------------------------------------------------
6774 void OutputDevice::ImplDrawText( const Rectangle& rRect,
6775 const String& rOrigStr, USHORT nStyle,
6776 MetricVector* pVector, String* pDisplayText )
6778 Color aOldTextColor;
6779 Color aOldTextFillColor;
6780 BOOL bRestoreFillColor = false;
6781 if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector )
6783 BOOL bHighContrastBlack = FALSE;
6784 BOOL bHighContrastWhite = FALSE;
6785 const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
6786 if( rStyleSettings.GetHighContrastMode() )
6788 Color aCol;
6789 if( IsBackground() )
6790 aCol = GetBackground().GetColor();
6791 else
6792 // best guess is the face color here
6793 // but it may be totally wrong. the background color
6794 // was typically already reset
6795 aCol = rStyleSettings.GetFaceColor();
6797 bHighContrastBlack = aCol.IsDark();
6798 bHighContrastWhite = aCol.IsBright();
6801 aOldTextColor = GetTextColor();
6802 if ( IsTextFillColor() )
6804 bRestoreFillColor = TRUE;
6805 aOldTextFillColor = GetTextFillColor();
6807 if( bHighContrastBlack )
6808 SetTextColor( COL_GREEN );
6809 else if( bHighContrastWhite )
6810 SetTextColor( COL_LIGHTGREEN );
6811 else
6813 // draw disabled text always without shadow
6814 // as it fits better with native look
6816 SetTextColor( GetSettings().GetStyleSettings().GetLightColor() );
6817 Rectangle aRect = rRect;
6818 aRect.Move( 1, 1 );
6819 DrawText( aRect, rOrigStr, nStyle & ~TEXT_DRAW_DISABLE );
6821 SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
6825 long nWidth = rRect.GetWidth();
6826 long nHeight = rRect.GetHeight();
6828 if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) )
6829 return;
6831 Point aPos = rRect.TopLeft();
6833 long nTextHeight = GetTextHeight();
6834 TextAlign eAlign = GetTextAlign();
6835 xub_StrLen nMnemonicPos = STRING_NOTFOUND;
6837 String aStr = rOrigStr;
6838 if ( nStyle & TEXT_DRAW_MNEMONIC )
6839 aStr = GetNonMnemonicString( aStr, nMnemonicPos );
6841 // Mehrzeiligen Text behandeln wir anders
6842 if ( nStyle & TEXT_DRAW_MULTILINE )
6845 XubString aLastLine;
6846 ImplMultiTextLineInfo aMultiLineInfo;
6847 ImplTextLineInfo* pLineInfo;
6848 long nMaxTextWidth;
6849 xub_StrLen i;
6850 xub_StrLen nLines;
6851 xub_StrLen nFormatLines;
6853 if ( nTextHeight )
6855 nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle );
6856 nLines = (xub_StrLen)(nHeight/nTextHeight);
6857 nFormatLines = aMultiLineInfo.Count();
6858 if ( !nLines )
6859 nLines = 1;
6860 if ( nFormatLines > nLines )
6862 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
6864 // Letzte Zeile zusammenbauen und kuerzen
6865 nFormatLines = nLines-1;
6867 pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
6868 aLastLine = aStr.Copy( pLineInfo->GetIndex() );
6869 aLastLine.ConvertLineEnd( LINEEND_LF );
6870 // Alle LineFeed's durch Spaces ersetzen
6871 xub_StrLen nLastLineLen = aLastLine.Len();
6872 for ( i = 0; i < nLastLineLen; i++ )
6874 if ( aLastLine.GetChar( i ) == _LF )
6875 aLastLine.SetChar( i, ' ' );
6877 aLastLine = GetEllipsisString( aLastLine, nWidth, nStyle );
6878 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
6879 nStyle |= TEXT_DRAW_TOP;
6882 else
6884 if ( nMaxTextWidth <= nWidth )
6885 nStyle &= ~TEXT_DRAW_CLIP;
6888 // Muss in der Hoehe geclippt werden?
6889 if ( nFormatLines*nTextHeight > nHeight )
6890 nStyle |= TEXT_DRAW_CLIP;
6892 // Clipping setzen
6893 if ( nStyle & TEXT_DRAW_CLIP )
6895 Push( PUSH_CLIPREGION );
6896 IntersectClipRegion( rRect );
6899 // Vertikales Alignment
6900 if ( nStyle & TEXT_DRAW_BOTTOM )
6901 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
6902 else if ( nStyle & TEXT_DRAW_VCENTER )
6903 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
6905 // Font Alignment
6906 if ( eAlign == ALIGN_BOTTOM )
6907 aPos.Y() += nTextHeight;
6908 else if ( eAlign == ALIGN_BASELINE )
6909 aPos.Y() += GetFontMetric().GetAscent();
6911 // Alle Zeilen ausgeben, bis auf die letzte
6912 for ( i = 0; i < nFormatLines; i++ )
6914 pLineInfo = aMultiLineInfo.GetLine( i );
6915 if ( nStyle & TEXT_DRAW_RIGHT )
6916 aPos.X() += nWidth-pLineInfo->GetWidth();
6917 else if ( nStyle & TEXT_DRAW_CENTER )
6918 aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
6919 xub_StrLen nIndex = pLineInfo->GetIndex();
6920 xub_StrLen nLineLen = pLineInfo->GetLen();
6921 DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
6922 if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
6924 if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
6926 long nMnemonicX;
6927 long nMnemonicY;
6928 long nMnemonicWidth;
6930 sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen );
6931 /*BOOL bRet =*/ GetCaretPositions( aStr, pCaretXArray,
6932 nIndex, nLineLen);
6933 long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
6934 long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
6935 nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
6937 Point aTempPos = LogicToPixel( aPos );
6938 nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( Min( lc_x1, lc_x2 ) );
6939 nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() );
6940 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
6943 aPos.Y() += nTextHeight;
6944 aPos.X() = rRect.Left();
6948 // Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben,
6949 // da die Zeile gekuerzt wurde
6950 if ( aLastLine.Len() )
6951 DrawText( aPos, aLastLine, 0, STRING_LEN, pVector, pDisplayText );
6953 // Clipping zuruecksetzen
6954 if ( nStyle & TEXT_DRAW_CLIP )
6955 Pop();
6958 else
6960 long nTextWidth = GetTextWidth( aStr );
6962 // Evt. Text kuerzen
6963 if ( nTextWidth > nWidth )
6965 if ( nStyle & TEXT_DRAW_ELLIPSIS )
6967 aStr = GetEllipsisString( aStr, nWidth, nStyle );
6968 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
6969 nStyle |= TEXT_DRAW_LEFT;
6970 nTextWidth = GetTextWidth( aStr );
6973 else
6975 if ( nTextHeight <= nHeight )
6976 nStyle &= ~TEXT_DRAW_CLIP;
6979 // horizontal text alignment
6980 if ( nStyle & TEXT_DRAW_RIGHT )
6981 aPos.X() += nWidth-nTextWidth;
6982 else if ( nStyle & TEXT_DRAW_CENTER )
6983 aPos.X() += (nWidth-nTextWidth)/2;
6985 // vertical font alignment
6986 if ( eAlign == ALIGN_BOTTOM )
6987 aPos.Y() += nTextHeight;
6988 else if ( eAlign == ALIGN_BASELINE )
6989 aPos.Y() += GetFontMetric().GetAscent();
6991 if ( nStyle & TEXT_DRAW_BOTTOM )
6992 aPos.Y() += nHeight-nTextHeight;
6993 else if ( nStyle & TEXT_DRAW_VCENTER )
6994 aPos.Y() += (nHeight-nTextHeight)/2;
6996 long nMnemonicX = 0;
6997 long nMnemonicY = 0;
6998 long nMnemonicWidth = 0;
6999 if ( nMnemonicPos != STRING_NOTFOUND )
7001 sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.Len() );
7002 /*BOOL bRet =*/ GetCaretPositions( aStr, pCaretXArray, 0, aStr.Len() );
7003 long lc_x1 = pCaretXArray[2*(nMnemonicPos)];
7004 long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1];
7005 nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
7007 Point aTempPos = LogicToPixel( aPos );
7008 nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( Min(lc_x1, lc_x2) );
7009 nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() );
7012 if ( nStyle & TEXT_DRAW_CLIP )
7014 Push( PUSH_CLIPREGION );
7015 IntersectClipRegion( rRect );
7016 DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
7017 if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7019 if ( nMnemonicPos != STRING_NOTFOUND )
7020 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7022 Pop();
7024 else
7026 DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
7027 if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7029 if ( nMnemonicPos != STRING_NOTFOUND )
7030 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7035 if ( nStyle & TEXT_DRAW_DISABLE && !pVector )
7037 SetTextColor( aOldTextColor );
7038 if ( bRestoreFillColor )
7039 SetTextFillColor( aOldTextFillColor );
7043 // -----------------------------------------------------------------------
7045 void OutputDevice::AddTextRectActions( const Rectangle& rRect,
7046 const String& rOrigStr,
7047 USHORT nStyle,
7048 GDIMetaFile& rMtf )
7050 DBG_TRACE( "OutputDevice::AddTextRectActions( const Rectangle& )" );
7051 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7053 if ( !rOrigStr.Len() || rRect.IsEmpty() )
7054 return;
7056 // we need a graphics
7057 if( !mpGraphics && !ImplGetGraphics() )
7058 return;
7059 if( mbInitClipRegion )
7060 ImplInitClipRegion();
7062 // temporarily swap in passed mtf for action generation, and
7063 // disable output generation.
7064 const BOOL bOutputEnabled( IsOutputEnabled() );
7065 GDIMetaFile* pMtf = mpMetaFile;
7067 mpMetaFile = &rMtf;
7068 EnableOutput( FALSE );
7070 // #i47157# Factored out to ImplDrawTextRect(), to be shared
7071 // between us and DrawText()
7072 ImplDrawText( rRect, rOrigStr, nStyle, NULL, NULL );
7074 // and restore again
7075 EnableOutput( bOutputEnabled );
7076 mpMetaFile = pMtf;
7079 // -----------------------------------------------------------------------
7081 void OutputDevice::DrawText( const Rectangle& rRect,
7082 const String& rOrigStr, USHORT nStyle,
7083 MetricVector* pVector, String* pDisplayText )
7086 if( mpOutDevData && mpOutDevData->mpRecordLayout )
7088 pVector = &mpOutDevData->mpRecordLayout->m_aUnicodeBoundRects;
7089 pDisplayText = &mpOutDevData->mpRecordLayout->m_aDisplayText;
7092 DBG_TRACE( "OutputDevice::DrawText( const Rectangle& )" );
7093 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7095 if ( mpMetaFile )
7096 mpMetaFile->AddAction( new MetaTextRectAction( rRect, rOrigStr, nStyle ) );
7098 if ( ( !IsDeviceOutputNecessary() && ! pVector ) || !rOrigStr.Len() || rRect.IsEmpty() )
7099 return;
7101 // we need a graphics
7102 if( !mpGraphics && !ImplGetGraphics() )
7103 return;
7104 if( mbInitClipRegion )
7105 ImplInitClipRegion();
7106 if( mbOutputClipped )
7107 return;
7109 // temporarily disable mtf action generation (ImplDrawText _does_
7110 // create META_TEXT_ACTIONs otherwise)
7111 GDIMetaFile* pMtf = mpMetaFile;
7112 mpMetaFile = NULL;
7114 // #i47157# Factored out to ImplDrawTextRect(), to be used also
7115 // from AddTextRectActions()
7116 ImplDrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
7118 // and enable again
7119 mpMetaFile = pMtf;
7121 if( mpAlphaVDev )
7122 mpAlphaVDev->DrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
7125 // -----------------------------------------------------------------------
7127 Rectangle OutputDevice::GetTextRect( const Rectangle& rRect,
7128 const String& rOrigStr, USHORT nStyle,
7129 TextRectInfo* pInfo ) const
7131 DBG_TRACE( "OutputDevice::GetTextRect()" );
7132 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7134 Rectangle aRect = rRect;
7135 xub_StrLen nLines;
7136 long nWidth = rRect.GetWidth();
7137 long nMaxWidth;
7138 long nTextHeight = GetTextHeight();
7140 String aStr = rOrigStr;
7141 if ( nStyle & TEXT_DRAW_MNEMONIC )
7142 aStr = GetNonMnemonicString( aStr );
7144 if ( nStyle & TEXT_DRAW_MULTILINE )
7146 ImplMultiTextLineInfo aMultiLineInfo;
7147 ImplTextLineInfo* pLineInfo;
7148 xub_StrLen nFormatLines;
7149 xub_StrLen i;
7151 nMaxWidth = 0;
7152 ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle );
7153 nFormatLines = aMultiLineInfo.Count();
7154 if ( !nTextHeight )
7155 nTextHeight = 1;
7156 nLines = (USHORT)(aRect.GetHeight()/nTextHeight);
7157 if ( pInfo )
7158 pInfo->mnLineCount = nFormatLines;
7159 if ( !nLines )
7160 nLines = 1;
7161 if ( nFormatLines <= nLines )
7162 nLines = nFormatLines;
7163 else
7165 if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) )
7166 nLines = nFormatLines;
7167 else
7169 if ( pInfo )
7170 pInfo->mbEllipsis = TRUE;
7171 nMaxWidth = nWidth;
7174 if ( pInfo )
7176 BOOL bMaxWidth = nMaxWidth == 0;
7177 pInfo->mnMaxWidth = 0;
7178 for ( i = 0; i < nLines; i++ )
7180 pLineInfo = aMultiLineInfo.GetLine( i );
7181 if ( bMaxWidth && (pLineInfo->GetWidth() > nMaxWidth) )
7182 nMaxWidth = pLineInfo->GetWidth();
7183 if ( pLineInfo->GetWidth() > pInfo->mnMaxWidth )
7184 pInfo->mnMaxWidth = pLineInfo->GetWidth();
7187 else if ( !nMaxWidth )
7189 for ( i = 0; i < nLines; i++ )
7191 pLineInfo = aMultiLineInfo.GetLine( i );
7192 if ( pLineInfo->GetWidth() > nMaxWidth )
7193 nMaxWidth = pLineInfo->GetWidth();
7197 else
7199 nLines = 1;
7200 nMaxWidth = GetTextWidth( aStr );
7202 if ( pInfo )
7204 pInfo->mnLineCount = 1;
7205 pInfo->mnMaxWidth = nMaxWidth;
7208 if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ELLIPSIS) )
7210 if ( pInfo )
7211 pInfo->mbEllipsis = TRUE;
7212 nMaxWidth = nWidth;
7216 if ( nStyle & TEXT_DRAW_RIGHT )
7217 aRect.Left() = aRect.Right()-nMaxWidth+1;
7218 else if ( nStyle & TEXT_DRAW_CENTER )
7220 aRect.Left() += (nWidth-nMaxWidth)/2;
7221 aRect.Right() = aRect.Left()+nMaxWidth-1;
7223 else
7224 aRect.Right() = aRect.Left()+nMaxWidth-1;
7226 if ( nStyle & TEXT_DRAW_BOTTOM )
7227 aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1;
7228 else if ( nStyle & TEXT_DRAW_VCENTER )
7230 aRect.Top() += (aRect.GetHeight()-(nTextHeight*nLines))/2;
7231 aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
7233 else
7234 aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
7236 aRect.Right()++; // #99188# get rid of rounding problems when using this rect later
7237 return aRect;
7240 // -----------------------------------------------------------------------
7242 static BOOL ImplIsCharIn( xub_Unicode c, const sal_Char* pStr )
7244 while ( *pStr )
7246 if ( *pStr == c )
7247 return TRUE;
7248 pStr++;
7251 return FALSE;
7254 // -----------------------------------------------------------------------
7256 String OutputDevice::GetEllipsisString( const String& rOrigStr, long nMaxWidth,
7257 USHORT nStyle ) const
7259 DBG_TRACE( "OutputDevice::GetEllipsisString()" );
7260 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7262 String aStr = rOrigStr;
7263 xub_StrLen nIndex = GetTextBreak( aStr, nMaxWidth );
7266 if ( nIndex != STRING_LEN )
7268 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7270 aStr.Erase( nIndex );
7271 if ( nIndex > 1 )
7273 aStr.AppendAscii( "..." );
7274 while ( aStr.Len() && (GetTextWidth( aStr ) > nMaxWidth) )
7276 if ( (nIndex > 1) || (nIndex == aStr.Len()) )
7277 nIndex--;
7278 aStr.Erase( nIndex, 1 );
7282 if ( !aStr.Len() && (nStyle & TEXT_DRAW_CLIP) )
7283 aStr += rOrigStr.GetChar( 0 );
7285 else if ( nStyle & TEXT_DRAW_PATHELLIPSIS )
7287 rtl::OUString aPath( rOrigStr );
7288 rtl::OUString aAbbreviatedPath;
7289 osl_abbreviateSystemPath( aPath.pData, &aAbbreviatedPath.pData, nIndex, NULL );
7290 aStr = aAbbreviatedPath;
7292 else if ( nStyle & TEXT_DRAW_NEWSELLIPSIS )
7294 static sal_Char const pSepChars[] = ".";
7295 // Letztes Teilstueck ermitteln
7296 xub_StrLen nLastContent = aStr.Len();
7297 while ( nLastContent )
7299 nLastContent--;
7300 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) )
7301 break;
7303 while ( nLastContent &&
7304 ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) )
7305 nLastContent--;
7307 XubString aLastStr( aStr, nLastContent, aStr.Len() );
7308 XubString aTempLastStr1( RTL_CONSTASCII_USTRINGPARAM( "..." ) );
7309 aTempLastStr1 += aLastStr;
7310 if ( GetTextWidth( aTempLastStr1 ) > nMaxWidth )
7311 aStr = GetEllipsisString( aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS );
7312 else
7314 USHORT nFirstContent = 0;
7315 while ( nFirstContent < nLastContent )
7317 nFirstContent++;
7318 if ( ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) )
7319 break;
7321 while ( (nFirstContent < nLastContent) &&
7322 ImplIsCharIn( aStr.GetChar( nFirstContent ), pSepChars ) )
7323 nFirstContent++;
7325 if ( nFirstContent >= nLastContent )
7326 aStr = GetEllipsisString( aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS );
7327 else
7329 if ( nFirstContent > 4 )
7330 nFirstContent = 4;
7331 XubString aFirstStr( aStr, 0, nFirstContent );
7332 aFirstStr.AppendAscii( "..." );
7333 XubString aTempStr = aFirstStr;
7334 aTempStr += aLastStr;
7335 if ( GetTextWidth( aTempStr ) > nMaxWidth )
7336 aStr = GetEllipsisString( aStr, nMaxWidth, nStyle | TEXT_DRAW_ENDELLIPSIS );
7337 else
7341 aStr = aTempStr;
7342 if( nLastContent > aStr.Len() )
7343 nLastContent = aStr.Len();
7344 while ( nFirstContent < nLastContent )
7346 nLastContent--;
7347 if ( ImplIsCharIn( aStr.GetChar( nLastContent ), pSepChars ) )
7348 break;
7351 while ( (nFirstContent < nLastContent) &&
7352 ImplIsCharIn( aStr.GetChar( nLastContent-1 ), pSepChars ) )
7353 nLastContent--;
7355 if ( nFirstContent < nLastContent )
7357 XubString aTempLastStr( aStr, nLastContent, aStr.Len() );
7358 aTempStr = aFirstStr;
7359 aTempStr += aTempLastStr;
7360 if ( GetTextWidth( aTempStr ) > nMaxWidth )
7361 break;
7364 while ( nFirstContent < nLastContent );
7371 return aStr;
7374 // -----------------------------------------------------------------------
7376 void OutputDevice::DrawCtrlText( const Point& rPos, const XubString& rStr,
7377 xub_StrLen nIndex, xub_StrLen nLen,
7378 USHORT nStyle, MetricVector* pVector, String* pDisplayText )
7380 DBG_TRACE( "OutputDevice::DrawCtrlText()" );
7381 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7383 if ( !IsDeviceOutputNecessary() || (nIndex >= rStr.Len()) )
7384 return;
7386 // better get graphics here because ImplDrawMnemonicLine() will not
7387 // we need a graphics
7388 if( !mpGraphics && !ImplGetGraphics() )
7389 return;
7390 if( mbInitClipRegion )
7391 ImplInitClipRegion();
7392 if ( mbOutputClipped )
7393 return;
7395 if( nIndex >= rStr.Len() )
7396 return;
7397 if( (ULONG)nIndex+nLen >= rStr.Len() )
7398 nLen = rStr.Len() - nIndex;
7400 XubString aStr = rStr;
7401 xub_StrLen nMnemonicPos = STRING_NOTFOUND;
7403 long nMnemonicX = 0;
7404 long nMnemonicY = 0;
7405 long nMnemonicWidth = 0;
7406 if ( (nStyle & TEXT_DRAW_MNEMONIC) && nLen > 1 )
7408 aStr = GetNonMnemonicString( aStr, nMnemonicPos );
7409 if ( nMnemonicPos != STRING_NOTFOUND )
7411 if( nMnemonicPos < nIndex )
7412 --nIndex;
7413 else if( nLen < STRING_LEN )
7415 if( nMnemonicPos < (nIndex+nLen) )
7416 --nLen;
7417 DBG_ASSERT( nMnemonicPos < (nIndex+nLen), "Mnemonic underline marker after last character" );
7419 BOOL bInvalidPos = FALSE;
7421 if( nMnemonicPos >= nLen )
7423 // #106952#
7424 // may occur in BiDi-Strings: the '~' is sometimes found behind the last char
7425 // due to some strange BiDi text editors
7426 // ->place the underline behind the string to indicate a failure
7427 bInvalidPos = TRUE;
7428 nMnemonicPos = nLen-1;
7431 sal_Int32* pCaretXArray = (sal_Int32*)alloca( 2 * sizeof(sal_Int32) * nLen );
7432 /*BOOL bRet =*/ GetCaretPositions( aStr, pCaretXArray, nIndex, nLen );
7433 long lc_x1 = pCaretXArray[ 2*(nMnemonicPos - nIndex) ];
7434 long lc_x2 = pCaretXArray[ 2*(nMnemonicPos - nIndex)+1 ];
7435 nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
7437 Point aTempPos( Min(lc_x1,lc_x2), GetFontMetric().GetAscent() );
7438 if( bInvalidPos ) // #106952#, place behind the (last) character
7439 aTempPos = Point( Max(lc_x1,lc_x2), GetFontMetric().GetAscent() );
7441 aTempPos += rPos;
7442 aTempPos = LogicToPixel( aTempPos );
7443 nMnemonicX = mnOutOffX + aTempPos.X();
7444 nMnemonicY = mnOutOffY + aTempPos.Y();
7448 if ( nStyle & TEXT_DRAW_DISABLE && ! pVector )
7450 Color aOldTextColor;
7451 Color aOldTextFillColor;
7452 BOOL bRestoreFillColor;
7453 BOOL bHighContrastBlack = FALSE;
7454 BOOL bHighContrastWhite = FALSE;
7455 const StyleSettings& rStyleSettings( GetSettings().GetStyleSettings() );
7456 if( rStyleSettings.GetHighContrastMode() )
7458 if( IsBackground() )
7460 Wallpaper aWall = GetBackground();
7461 Color aCol = aWall.GetColor();
7462 bHighContrastBlack = aCol.IsDark();
7463 bHighContrastWhite = aCol.IsBright();
7467 aOldTextColor = GetTextColor();
7468 if ( IsTextFillColor() )
7470 bRestoreFillColor = TRUE;
7471 aOldTextFillColor = GetTextFillColor();
7473 else
7474 bRestoreFillColor = FALSE;
7476 if( bHighContrastBlack )
7477 SetTextColor( COL_GREEN );
7478 else if( bHighContrastWhite )
7479 SetTextColor( COL_LIGHTGREEN );
7480 else
7481 SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
7483 DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
7484 if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7486 if ( nMnemonicPos != STRING_NOTFOUND )
7487 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7489 SetTextColor( aOldTextColor );
7490 if ( bRestoreFillColor )
7491 SetTextFillColor( aOldTextFillColor );
7493 else
7495 DrawText( rPos, aStr, nIndex, nLen, pVector, pDisplayText );
7496 if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
7498 if ( nMnemonicPos != STRING_NOTFOUND )
7499 ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
7503 if( mpAlphaVDev )
7504 mpAlphaVDev->DrawCtrlText( rPos, rStr, nIndex, nLen, nStyle, pVector, pDisplayText );
7507 // -----------------------------------------------------------------------
7509 long OutputDevice::GetCtrlTextWidth( const String& rStr,
7510 xub_StrLen nIndex, xub_StrLen nLen,
7511 USHORT nStyle ) const
7513 DBG_TRACE( "OutputDevice::GetCtrlTextSize()" );
7514 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7516 if ( nStyle & TEXT_DRAW_MNEMONIC )
7518 xub_StrLen nMnemonicPos;
7519 XubString aStr = GetNonMnemonicString( rStr, nMnemonicPos );
7520 if ( nMnemonicPos != STRING_NOTFOUND )
7522 if ( nMnemonicPos < nIndex )
7523 nIndex--;
7524 else if ( (nLen < STRING_LEN) &&
7525 (nMnemonicPos >= nIndex) && (nMnemonicPos < (ULONG)(nIndex+nLen)) )
7526 nLen--;
7528 return GetTextWidth( aStr, nIndex, nLen );
7530 else
7531 return GetTextWidth( rStr, nIndex, nLen );
7534 // -----------------------------------------------------------------------
7536 String OutputDevice::GetNonMnemonicString( const String& rStr, xub_StrLen& rMnemonicPos )
7538 String aStr = rStr;
7539 xub_StrLen nLen = aStr.Len();
7540 xub_StrLen i = 0;
7542 rMnemonicPos = STRING_NOTFOUND;
7543 while ( i < nLen )
7545 if ( aStr.GetChar( i ) == '~' )
7547 if ( aStr.GetChar( i+1 ) != '~' )
7549 if ( rMnemonicPos == STRING_NOTFOUND )
7550 rMnemonicPos = i;
7551 aStr.Erase( i, 1 );
7552 nLen--;
7554 else
7556 aStr.Erase( i, 1 );
7557 nLen--;
7558 i++;
7561 else
7562 i++;
7565 return aStr;
7568 // -----------------------------------------------------------------------
7570 int OutputDevice::GetDevFontCount() const
7572 DBG_TRACE( "OutputDevice::GetDevFontCount()" );
7573 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7575 if( !mpGetDevFontList )
7576 mpGetDevFontList = mpFontList->GetDevFontList();
7577 return mpGetDevFontList->Count();
7580 // -----------------------------------------------------------------------
7582 FontInfo OutputDevice::GetDevFont( int nDevFontIndex ) const
7584 DBG_TRACE( "OutputDevice::GetDevFont()" );
7585 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7587 FontInfo aFontInfo;
7589 ImplInitFontList();
7591 int nCount = GetDevFontCount();
7592 if( nDevFontIndex < nCount )
7594 const ImplFontData& rData = *mpGetDevFontList->Get( nDevFontIndex );
7595 aFontInfo.SetName( rData.maName );
7596 aFontInfo.SetStyleName( rData.maStyleName );
7597 aFontInfo.SetCharSet( rData.mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
7598 aFontInfo.SetFamily( rData.meFamily );
7599 aFontInfo.SetPitch( rData.mePitch );
7600 aFontInfo.SetWeight( rData.meWeight );
7601 aFontInfo.SetItalic( rData.meItalic );
7602 aFontInfo.SetWidthType( rData.meWidthType );
7603 if( rData.IsScalable() )
7604 aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
7605 if( rData.mbDevice )
7606 aFontInfo.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
7609 return aFontInfo;
7612 // -----------------------------------------------------------------------
7614 BOOL OutputDevice::AddTempDevFont( const String& rFileURL, const String& rFontName )
7616 DBG_TRACE( "OutputDevice::AddTempDevFont()" );
7617 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7619 ImplInitFontList();
7621 if( !mpGraphics && !ImplGetGraphics() )
7622 return FALSE;
7624 bool bRC = mpGraphics->AddTempDevFont( mpFontList, rFileURL, rFontName );
7625 if( !bRC )
7626 return FALSE;
7628 if( mpAlphaVDev )
7629 mpAlphaVDev->AddTempDevFont( rFileURL, rFontName );
7631 mpFontCache->Invalidate();
7632 return TRUE;
7635 // -----------------------------------------------------------------------
7637 int OutputDevice::GetDevFontSizeCount( const Font& rFont ) const
7639 DBG_TRACE( "OutputDevice::GetDevFontSizeCount()" );
7640 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7642 delete mpGetDevSizeList;
7644 ImplInitFontList();
7645 mpGetDevSizeList = mpFontList->GetDevSizeList( rFont.GetName() );
7646 return mpGetDevSizeList->Count();
7649 // -----------------------------------------------------------------------
7651 Size OutputDevice::GetDevFontSize( const Font& rFont, int nSizeIndex ) const
7653 DBG_TRACE( "OutputDevice::GetDevFontSize()" );
7654 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7656 // check range
7657 int nCount = GetDevFontSizeCount( rFont );
7658 if ( nSizeIndex >= nCount )
7659 return Size();
7661 // when mapping is enabled round to .5 points
7662 Size aSize( 0, mpGetDevSizeList->Get( nSizeIndex ) );
7663 if ( mbMap )
7665 aSize.Height() *= 10;
7666 MapMode aMap( MAP_10TH_INCH, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) );
7667 aSize = PixelToLogic( aSize, aMap );
7668 aSize.Height() += 5;
7669 aSize.Height() /= 10;
7670 long nRound = aSize.Height() % 5;
7671 if ( nRound >= 3 )
7672 aSize.Height() += (5-nRound);
7673 else
7674 aSize.Height() -= nRound;
7675 aSize.Height() *= 10;
7676 aSize = LogicToPixel( aSize, aMap );
7677 aSize = PixelToLogic( aSize );
7678 aSize.Height() += 5;
7679 aSize.Height() /= 10;
7681 return aSize;
7684 // -----------------------------------------------------------------------
7686 BOOL OutputDevice::IsFontAvailable( const String& rFontName ) const
7688 DBG_TRACE( "OutputDevice::IsFontAvailable()" );
7689 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7691 ImplDevFontListData* pFound = mpFontList->FindFontFamily( rFontName );
7692 return (pFound != NULL);
7695 // -----------------------------------------------------------------------
7697 FontMetric OutputDevice::GetFontMetric() const
7699 DBG_TRACE( "OutputDevice::GetFontMetric()" );
7700 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7702 FontMetric aMetric;
7703 if( mbNewFont && !ImplNewFont() )
7704 return aMetric;
7706 ImplFontEntry* pEntry = mpFontEntry;
7707 ImplFontMetricData* pMetric = &(pEntry->maMetric);
7709 // prepare metric
7710 aMetric.Font::operator=( maFont );
7712 // set aMetric with info from font
7713 aMetric.SetName( maFont.GetName() );
7714 aMetric.SetStyleName( pMetric->maStyleName );
7715 aMetric.SetSize( PixelToLogic( Size( pMetric->mnWidth, pMetric->mnAscent+pMetric->mnDescent-pMetric->mnIntLeading ) ) );
7716 aMetric.SetCharSet( pMetric->mbSymbolFlag ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
7717 aMetric.SetFamily( pMetric->meFamily );
7718 aMetric.SetPitch( pMetric->mePitch );
7719 aMetric.SetWeight( pMetric->meWeight );
7720 aMetric.SetItalic( pMetric->meItalic );
7721 aMetric.SetWidthType( pMetric->meWidthType );
7722 if ( pEntry->mnOwnOrientation )
7723 aMetric.SetOrientation( pEntry->mnOwnOrientation );
7724 else
7725 aMetric.SetOrientation( pMetric->mnOrientation );
7726 if( !pEntry->maMetric.mbKernableFont )
7727 aMetric.SetKerning( maFont.GetKerning() & ~KERNING_FONTSPECIFIC );
7729 // set remaining metric fields
7730 aMetric.mpImplMetric->mnMiscFlags = 0;
7731 if( pMetric->mbDevice )
7732 aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::DEVICE_FLAG;
7733 if( pMetric->mbScalableFont )
7734 aMetric.mpImplMetric->mnMiscFlags |= ImplFontMetric::SCALABLE_FLAG;
7735 aMetric.mpImplMetric->mnAscent = ImplDevicePixelToLogicHeight( pMetric->mnAscent+mnEmphasisAscent );
7736 aMetric.mpImplMetric->mnDescent = ImplDevicePixelToLogicHeight( pMetric->mnDescent+mnEmphasisDescent );
7737 aMetric.mpImplMetric->mnIntLeading = ImplDevicePixelToLogicHeight( pMetric->mnIntLeading+mnEmphasisAscent );
7738 aMetric.mpImplMetric->mnExtLeading = ImplDevicePixelToLogicHeight( pMetric->mnExtLeading );
7739 aMetric.mpImplMetric->mnLineHeight = ImplDevicePixelToLogicHeight( pMetric->mnAscent+pMetric->mnDescent+mnEmphasisAscent+mnEmphasisDescent );
7740 aMetric.mpImplMetric->mnSlant = ImplDevicePixelToLogicHeight( pMetric->mnSlant );
7742 #ifdef UNX
7743 // backwards compatible line metrics after fixing #i60945#
7744 if( (meOutDevType == OUTDEV_VIRDEV)
7745 && static_cast<const VirtualDevice*>(this)->ForceZeroExtleadBug() )
7746 aMetric.mpImplMetric->mnExtLeading = 0;
7747 #endif
7749 return aMetric;
7752 // -----------------------------------------------------------------------
7754 FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const
7756 // select font, query metrics, select original font again
7757 Font aOldFont = GetFont();
7758 const_cast<OutputDevice*>(this)->SetFont( rFont );
7759 FontMetric aMetric( GetFontMetric() );
7760 const_cast<OutputDevice*>(this)->SetFont( aOldFont );
7761 return aMetric;
7764 // -----------------------------------------------------------------------
7766 /** OutputDevice::GetSysFontData
7768 * @param nFallbacklevel Fallback font level (0 = best matching font)
7770 * Retrieve detailed font information in platform independent structure
7772 * @return SystemFontData
7774 SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const
7776 SystemFontData aSysFontData;
7777 aSysFontData.nSize = sizeof(aSysFontData);
7779 if (!mpGraphics) ImplGetGraphics();
7780 if (mpGraphics) aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel);
7782 return aSysFontData;
7786 // -----------------------------------------------------------------------
7788 /** OutputDevice::GetSysTextLayoutData
7790 * @param rStartPt Start point of the text
7791 * @param rStr Text string that will be transformed into layout of glyphs
7792 * @param nIndex Position in the string from where layout will be done
7793 * @param nLen Length of the string
7794 * @param pDXAry Custom layout adjustment data
7796 * Export finalized glyph layout data as platform independent SystemTextLayoutData
7797 * (see vcl/inc/vcl/sysdata.hxx)
7799 * Only parameters rStartPt and rStr are mandatory, the rest is optional
7800 * (default values will be used)
7802 * @return SystemTextLayoutData
7804 SystemTextLayoutData OutputDevice::GetSysTextLayoutData(const Point& rStartPt, const XubString& rStr, xub_StrLen nIndex, xub_StrLen nLen,
7805 const sal_Int32* pDXAry) const
7807 DBG_TRACE( "OutputDevice::GetSysTextLayoutData()" );
7808 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7810 SystemTextLayoutData aSysLayoutData;
7811 aSysLayoutData.nSize = sizeof(aSysLayoutData);
7812 aSysLayoutData.rGlyphData.reserve( 256 );
7814 if ( mpMetaFile ) {
7815 if (pDXAry)
7816 mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) );
7817 else
7818 mpMetaFile->AddAction( new MetaTextAction( rStartPt, rStr, nIndex, nLen ) );
7821 if ( !IsDeviceOutputNecessary() ) return aSysLayoutData;
7823 SalLayout* rLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true );
7825 // setup glyphs
7826 Point aPos;
7827 sal_GlyphId aGlyphId;
7828 int nFallbacklevel = 0;
7829 for( int nStart = 0; rLayout->GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); )
7831 // NOTE: Windows backend is producing unicode chars (ucs4), so on windows,
7832 // ETO_GLYPH_INDEX is unusable, unless extra glyph conversion is made.
7834 SystemGlyphData aGlyph;
7835 aGlyph.index = static_cast<unsigned long> (aGlyphId & GF_IDXMASK);
7836 aGlyph.x = aPos.X();
7837 aGlyph.y = aPos.Y();
7838 aSysLayoutData.rGlyphData.push_back(aGlyph);
7840 int nLevel = (aGlyphId & GF_FONTMASK) >> GF_FONTSHIFT;
7841 if (nLevel > nFallbacklevel && nLevel < MAX_FALLBACK)
7842 nFallbacklevel = nLevel;
7845 // Get font data
7846 aSysLayoutData.aSysFontData = GetSysFontData(nFallbacklevel);
7847 aSysLayoutData.orientation = rLayout->GetOrientation();
7849 rLayout->Release();
7851 return aSysLayoutData;
7854 // -----------------------------------------------------------------------
7857 long OutputDevice::GetMinKashida() const
7859 DBG_TRACE( "OutputDevice::GetMinKashida()" );
7860 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7861 if( mbNewFont && !ImplNewFont() )
7862 return 0;
7864 ImplFontEntry* pEntry = mpFontEntry;
7865 ImplFontMetricData* pMetric = &(pEntry->maMetric);
7866 return ImplDevicePixelToLogicWidth( pMetric->mnMinKashida );
7868 // -----------------------------------------------------------------------
7870 long OutputDevice::GetMinKashida( const Font& rFont ) const
7872 // select font, query Kashida, select original font again
7873 Font aOldFont = GetFont();
7874 const_cast<OutputDevice*>(this)->SetFont( rFont );
7875 long aKashida = GetMinKashida();
7876 const_cast<OutputDevice*>(this)->SetFont( aOldFont );
7877 return aKashida;
7880 // -----------------------------------------------------------------------
7881 xub_StrLen OutputDevice::ValidateKashidas ( const String& rTxt,
7882 xub_StrLen nIdx, xub_StrLen nLen,
7883 xub_StrLen nKashCount,
7884 const xub_StrLen* pKashidaPos,
7885 xub_StrLen* pKashidaPosDropped ) const
7887 // do layout
7888 SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen );
7889 if( !pSalLayout )
7890 return 0;
7891 xub_StrLen nDropped = 0;
7892 for( int i = 0; i < nKashCount; ++i )
7894 if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] ))
7896 pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ];
7897 ++nDropped;
7900 pSalLayout->Release();
7901 return nDropped;
7906 // -----------------------------------------------------------------------
7909 // TODO: best is to get rid of this method completely
7910 ULONG OutputDevice::GetKerningPairCount() const
7912 DBG_TRACE( "OutputDevice::GetKerningPairCount()" );
7913 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7915 if( mbNewFont && !ImplNewFont() )
7916 return 0;
7917 if( mbInitFont )
7918 ImplInitFont();
7920 if( mpPDFWriter && mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) )
7921 return 0;
7923 // get the kerning pair count from the device layer
7924 int nKernPairs = mpGraphics->GetKernPairs( 0, NULL );
7925 return nKernPairs;
7928 // -----------------------------------------------------------------------
7930 inline bool CmpKernData( const KerningPair& a, const KerningPair& b )
7932 return (a.nChar1 < b.nChar1) || ((a.nChar1 == a.nChar2) && (a.nChar2 < a.nChar2));
7935 // TODO: best is to get rid of this method completely
7936 void OutputDevice::GetKerningPairs( ULONG nRequestedPairs, KerningPair* pKernPairs ) const
7938 DBG_TRACE( "OutputDevice::GetKerningPairs()" );
7939 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7941 if( mbNewFont && !ImplNewFont() )
7942 return;
7943 if( mbInitFont )
7944 ImplInitFont();
7946 if( mpPDFWriter && mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) )
7947 return;
7949 // get the kerning pairs directly from the device layer
7950 int nKernPairs = mpGraphics->GetKernPairs( nRequestedPairs, (ImplKernPairData*)pKernPairs );
7952 // sort kerning pairs
7953 std::sort( pKernPairs, pKernPairs+nKernPairs, CmpKernData );
7956 // -----------------------------------------------------------------------
7958 BOOL OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const String& rStr,
7959 int nIndex, int nLen, int nBase, MetricVector& rVector )
7961 DBG_TRACE( "OutputDevice::GetGlyphBoundRect_CTL()" );
7962 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7964 rVector.clear();
7966 if( nLen == STRING_LEN )
7967 nLen = rStr.Len() - nIndex;
7969 Rectangle aRect;
7970 for( int i = 0; i < nLen; i++ )
7972 if( !GetTextBoundRect( aRect, rStr, sal::static_int_cast<xub_StrLen>(nBase), sal::static_int_cast<xub_StrLen>(nIndex+i), 1 ) )
7973 break;
7974 aRect.Move( rOrigin.X(), rOrigin.Y() );
7975 rVector.push_back( aRect );
7978 return (nLen == (int)rVector.size());
7981 // -----------------------------------------------------------------------
7983 BOOL OutputDevice::GetTextBoundRect( Rectangle& rRect,
7984 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
7985 ULONG nLayoutWidth, const sal_Int32* pDXAry ) const
7987 DBG_TRACE( "OutputDevice::GetTextBoundRect()" );
7988 DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
7990 BOOL bRet = FALSE;
7991 rRect.SetEmpty();
7993 SalLayout* pSalLayout = NULL;
7994 const Point aPoint;
7995 // calculate offset when nBase!=nIndex
7996 long nXOffset = 0;
7997 if( nBase != nIndex )
7999 xub_StrLen nStart = Min( nBase, nIndex );
8000 xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
8001 pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry );
8002 if( pSalLayout )
8004 nXOffset = pSalLayout->GetTextWidth();
8005 nXOffset /= pSalLayout->GetUnitsPerPixel();
8006 pSalLayout->Release();
8007 // TODO: fix offset calculation for Bidi case
8008 if( nBase < nIndex)
8009 nXOffset = -nXOffset;
8013 pSalLayout = ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
8014 Rectangle aPixelRect;
8015 if( pSalLayout )
8017 bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect );
8019 if( bRet )
8021 int nWidthFactor = pSalLayout->GetUnitsPerPixel();
8023 if( nWidthFactor > 1 )
8025 double fFactor = 1.0 / nWidthFactor;
8026 aPixelRect.Left()
8027 = static_cast< long >(aPixelRect.Left() * fFactor);
8028 aPixelRect.Right()
8029 = static_cast< long >(aPixelRect.Right() * fFactor);
8030 aPixelRect.Top()
8031 = static_cast< long >(aPixelRect.Top() * fFactor);
8032 aPixelRect.Bottom()
8033 = static_cast< long >(aPixelRect.Bottom() * fFactor);
8036 Point aRotatedOfs( mnTextOffX, mnTextOffY );
8037 aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
8038 aPixelRect += aRotatedOfs;
8039 rRect = PixelToLogic( aPixelRect );
8040 if( mbMap )
8041 rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
8044 pSalLayout->Release();
8047 if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
8048 return bRet;
8050 // fall back to bitmap method to get the bounding rectangle,
8051 // so we need a monochrome virtual device with matching font
8052 VirtualDevice aVDev( 1 );
8053 Font aFont( GetFont() );
8054 aFont.SetShadow( FALSE );
8055 aFont.SetOutline( FALSE );
8056 aFont.SetRelief( RELIEF_NONE );
8057 aFont.SetOrientation( 0 );
8058 aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
8059 aVDev.SetFont( aFont );
8060 aVDev.SetTextAlign( ALIGN_TOP );
8062 // layout the text on the virtual device
8063 pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry );
8064 if( !pSalLayout )
8065 return false;
8067 // make the bitmap big enough
8068 // TODO: use factors when it would get too big
8069 long nWidth = pSalLayout->GetTextWidth();
8070 long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
8071 Point aOffset( nWidth/2, 8 );
8072 Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() );
8073 if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) )
8074 return false;
8076 // draw text in black
8077 pSalLayout->DrawBase() = aOffset;
8078 aVDev.SetTextColor( Color( COL_BLACK ) );
8079 aVDev.SetTextFillColor();
8080 aVDev.ImplInitTextColor();
8081 aVDev.ImplDrawText( *pSalLayout );
8082 pSalLayout->Release();
8084 // find extents using the bitmap
8085 Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize );
8086 BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
8087 if( !pAcc )
8088 return FALSE;
8089 const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
8090 const long nW = pAcc->Width();
8091 const long nH = pAcc->Height();
8092 long nLeft = 0;
8093 long nRight = 0;
8095 // find top left point
8096 long nTop = 0;
8097 for(; nTop < nH; ++nTop )
8099 for( nLeft = 0; nLeft < nW; ++nLeft )
8100 if( pAcc->GetPixel( nTop, nLeft ) == aBlack )
8101 break;
8102 if( nLeft < nW )
8103 break;
8106 // find bottom right point
8107 long nBottom = nH;
8108 while( --nBottom >= nTop )
8110 for( nRight = nW; --nRight >= 0; )
8111 if( pAcc->GetPixel( nBottom, nRight ) == aBlack )
8112 break;
8113 if( nRight >= 0 )
8114 break;
8116 if( nRight < nLeft )
8118 long nX = nRight;
8119 nRight = nLeft;
8120 nLeft = nX;
8123 for( long nY = nTop; nY <= nBottom; ++nY )
8125 // find leftmost point
8126 long nX;
8127 for( nX = 0; nX < nLeft; ++nX )
8128 if( pAcc->GetPixel( nY, nX ) == aBlack )
8129 break;
8130 nLeft = nX;
8132 // find rightmost point
8133 for( nX = nW; --nX > nRight; )
8134 if( pAcc->GetPixel( nY, nX ) == aBlack )
8135 break;
8136 nRight = nX;
8139 aBmp.ReleaseAccess( pAcc );
8141 if( nTop <= nBottom )
8143 Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 );
8144 Point aTopLeft( nLeft, nTop );
8145 aTopLeft -= aOffset;
8146 // adjust to text alignment
8147 aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent);
8148 // convert to logical coordinates
8149 aSize = PixelToLogic( aSize );
8150 aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() );
8151 aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() );
8152 rRect = Rectangle( aTopLeft, aSize );
8153 return TRUE;
8156 return FALSE;
8159 // -----------------------------------------------------------------------
8161 BOOL OutputDevice::GetTextOutlines( ::basegfx::B2DPolyPolygonVector& rVector,
8162 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
8163 BOOL bOptimize, ULONG nTWidth, const sal_Int32* pDXArray ) const
8165 // the fonts need to be initialized
8166 if( mbNewFont )
8167 ImplNewFont();
8168 if( mbInitFont )
8169 ImplInitFont();
8170 if( !mpFontEntry )
8171 return FALSE;
8173 BOOL bRet = FALSE;
8174 rVector.clear();
8175 if( nLen == STRING_LEN )
8176 nLen = rStr.Len() - nIndex;
8177 rVector.reserve( nLen );
8179 // we want to get the Rectangle in logical units, so to
8180 // avoid rounding errors we just size the font in logical units
8181 BOOL bOldMap = mbMap;
8182 if( bOldMap )
8184 const_cast<OutputDevice&>(*this).mbMap = FALSE;
8185 const_cast<OutputDevice&>(*this).mbNewFont = TRUE;
8188 SalLayout* pSalLayout = NULL;
8190 // calculate offset when nBase!=nIndex
8191 long nXOffset = 0;
8192 if( nBase != nIndex )
8194 xub_StrLen nStart = Min( nBase, nIndex );
8195 xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
8196 pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nTWidth, pDXArray );
8197 if( pSalLayout )
8199 nXOffset = pSalLayout->GetTextWidth();
8200 pSalLayout->Release();
8201 // TODO: fix offset calculation for Bidi case
8202 if( nBase > nIndex)
8203 nXOffset = -nXOffset;
8207 pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
8208 if( pSalLayout )
8210 bRet = pSalLayout->GetOutline( *mpGraphics, rVector );
8211 if( bRet )
8213 // transform polygon to pixel units
8214 ::basegfx::B2DHomMatrix aMatrix;
8216 int nWidthFactor = pSalLayout->GetUnitsPerPixel();
8217 if( nXOffset | mnTextOffX | mnTextOffY )
8219 Point aRotatedOfs( mnTextOffX*nWidthFactor, mnTextOffY*nWidthFactor );
8220 aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
8221 aMatrix.translate( aRotatedOfs.X(), aRotatedOfs.Y() );
8224 if( nWidthFactor > 1 )
8226 double fFactor = 1.0 / nWidthFactor;
8227 aMatrix.scale( fFactor, fFactor );
8230 if( !aMatrix.isIdentity() )
8232 ::basegfx::B2DPolyPolygonVector::iterator aIt = rVector.begin();
8233 for(; aIt != rVector.end(); ++aIt )
8234 (*aIt).transform( aMatrix );
8238 pSalLayout->Release();
8241 if( bOldMap )
8243 // restore original font size and map mode
8244 const_cast<OutputDevice&>(*this).mbMap = bOldMap;
8245 const_cast<OutputDevice&>(*this).mbNewFont = TRUE;
8248 if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
8249 return bRet;
8251 // fall back to bitmap conversion ------------------------------------------
8253 // Here, we can savely assume that the mapping between characters and glyphs
8254 // is one-to-one. This is most probably valid for the old bitmap fonts.
8256 // fall back to bitmap method to get the bounding rectangle,
8257 // so we need a monochrome virtual device with matching font
8258 pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
8259 if (pSalLayout == 0)
8260 return false;
8261 long nOrgWidth = pSalLayout->GetTextWidth();
8262 long nOrgHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent
8263 + mnEmphasisDescent;
8264 pSalLayout->Release();
8266 VirtualDevice aVDev(1);
8268 Font aFont(GetFont());
8269 aFont.SetShadow(false);
8270 aFont.SetOutline(false);
8271 aFont.SetRelief(RELIEF_NONE);
8272 aFont.SetOrientation(0);
8273 if( bOptimize )
8275 aFont.SetSize( Size( 0, GLYPH_FONT_HEIGHT ) );
8276 aVDev.SetMapMode( MAP_PIXEL );
8278 aVDev.SetFont( aFont );
8279 aVDev.SetTextAlign( ALIGN_TOP );
8280 aVDev.SetTextColor( Color(COL_BLACK) );
8281 aVDev.SetTextFillColor();
8283 pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen, Point(0,0), nTWidth, pDXArray );
8284 if (pSalLayout == 0)
8285 return false;
8286 long nWidth = pSalLayout->GetTextWidth();
8287 long nHeight = ((OutputDevice*)&aVDev)->mpFontEntry->mnLineHeight + ((OutputDevice*)&aVDev)->mnEmphasisAscent
8288 + ((OutputDevice*)&aVDev)->mnEmphasisDescent;
8289 pSalLayout->Release();
8291 if( !nWidth || !nHeight )
8292 return TRUE;
8293 double fScaleX = static_cast< double >(nOrgWidth) / nWidth;
8294 double fScaleY = static_cast< double >(nOrgHeight) / nHeight;
8296 // calculate offset when nBase!=nIndex
8297 // TODO: fix offset calculation for Bidi case
8298 nXOffset = 0;
8299 if( nBase != nIndex )
8301 xub_StrLen nStart = ((nBase < nIndex) ? nBase : nIndex);
8302 xub_StrLen nLength = ((nBase > nIndex) ? nBase : nIndex) - nStart;
8303 pSalLayout = aVDev.ImplLayout( rStr, nStart, nLength, Point(0,0), nTWidth, pDXArray );
8304 if( pSalLayout )
8306 nXOffset = pSalLayout->GetTextWidth();
8307 pSalLayout->Release();
8308 if( nBase > nIndex)
8309 nXOffset = -nXOffset;
8313 bRet = true;
8314 bool bRTL = false;
8315 String aStr( rStr ); // prepare for e.g. localized digits
8316 ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nIndex, nLen, 0, NULL );
8317 for( int nCharPos = -1; aLayoutArgs.GetNextPos( &nCharPos, &bRTL);)
8319 bool bSuccess = false;
8321 // draw character into virtual device
8322 pSalLayout = aVDev.ImplLayout( rStr, static_cast< xub_StrLen >(nCharPos), 1, Point(0,0), nTWidth, pDXArray );
8323 if (pSalLayout == 0)
8324 return false;
8325 long nCharWidth = pSalLayout->GetTextWidth();
8327 Point aOffset(nCharWidth / 2, 8);
8328 Size aSize(nCharWidth + 2 * aOffset.X(), nHeight + 2 * aOffset.Y());
8329 bSuccess = (bool)aVDev.SetOutputSizePixel(aSize);
8330 if( bSuccess )
8332 // draw glyph into virtual device
8333 aVDev.Erase();
8334 pSalLayout->DrawBase() += aOffset;
8335 pSalLayout->DrawBase() += Point( ((OutputDevice*)&aVDev)->mnTextOffX, ((OutputDevice*)&aVDev)->mnTextOffY );
8336 pSalLayout->DrawText( *((OutputDevice*)&aVDev)->mpGraphics );
8337 pSalLayout->Release();
8339 // convert character image into outline
8340 Bitmap aBmp( aVDev.GetBitmap(Point(0, 0), aSize));
8342 PolyPolygon aPolyPoly;
8343 bool bVectorized = aBmp.Vectorize(aPolyPoly, BMP_VECTORIZE_OUTER | BMP_VECTORIZE_REDUCE_EDGES);
8344 if( !bVectorized )
8345 bSuccess = false;
8346 else
8348 // convert units to logical width
8349 for (USHORT j = 0; j < aPolyPoly.Count(); ++j)
8351 Polygon& rPoly = aPolyPoly[j];
8352 for (USHORT k = 0; k < rPoly.GetSize(); ++k)
8354 Point& rPt = rPoly[k];
8355 rPt -= aOffset;
8356 int nPixelX = rPt.X() - ((OutputDevice&)aVDev).mnTextOffX + nXOffset;
8357 int nPixelY = rPt.Y() - ((OutputDevice&)aVDev).mnTextOffY;
8358 rPt.X() = ImplDevicePixelToLogicWidth( nPixelX );
8359 rPt.Y() = ImplDevicePixelToLogicHeight( nPixelY );
8364 // ignore "empty" glyphs:
8365 if( aPolyPoly.Count() > 0 )
8367 // convert to B2DPolyPolygon
8368 // TODO: get rid of intermediate tool's PolyPolygon
8369 ::basegfx::B2DPolyPolygon aB2DPolyPoly = aPolyPoly.getB2DPolyPolygon();
8370 ::basegfx::B2DHomMatrix aMatrix;
8371 aMatrix.scale( fScaleX, fScaleY );
8372 int nAngle = GetFont().GetOrientation();
8373 if( nAngle )
8374 aMatrix.rotate( nAngle * F_PI1800 );
8375 aB2DPolyPoly.transform( aMatrix );
8376 rVector.push_back( aB2DPolyPoly );
8381 nXOffset += nCharWidth;
8382 bRet = bRet && bSuccess;
8385 return bRet;
8388 // -----------------------------------------------------------------------
8390 BOOL OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector,
8391 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex,
8392 xub_StrLen nLen, BOOL bOptimize, ULONG nTWidth, const sal_Int32* pDXArray ) const
8394 rResultVector.clear();
8396 // get the basegfx polypolygon vector
8397 ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
8398 if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
8399 bOptimize, nTWidth, pDXArray ) )
8400 return FALSE;
8402 // convert to a tool polypolygon vector
8403 rResultVector.reserve( aB2DPolyPolyVector.size() );
8404 ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
8405 for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
8406 rResultVector.push_back(PolyPolygon(*aIt)); // #i76339#
8408 return TRUE;
8411 // -----------------------------------------------------------------------
8413 BOOL OutputDevice::GetTextOutline( PolyPolygon& rPolyPoly,
8414 const String& rStr, xub_StrLen nBase, xub_StrLen nIndex, xub_StrLen nLen,
8415 BOOL bOptimize, ULONG nTWidth, const sal_Int32* pDXArray ) const
8417 rPolyPoly.Clear();
8419 // get the basegfx polypolygon vector
8420 ::basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
8421 if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen,
8422 bOptimize, nTWidth, pDXArray ) )
8423 return FALSE;
8425 // convert and merge into a tool polypolygon
8426 ::basegfx::B2DPolyPolygonVector::const_iterator aIt = aB2DPolyPolyVector.begin();
8427 for(; aIt != aB2DPolyPolyVector.end(); ++aIt )
8428 for( unsigned int i = 0; i < aIt->count(); ++i )
8429 rPolyPoly.Insert(Polygon((*aIt).getB2DPolygon( i ))); // #i76339#
8431 return TRUE;
8434 // -----------------------------------------------------------------------
8436 BOOL OutputDevice::GetFontCharMap( FontCharMap& rFontCharMap ) const
8438 rFontCharMap.Reset();
8440 // we need a graphics
8441 if( !mpGraphics && !ImplGetGraphics() )
8442 return FALSE;
8444 if( mbNewFont )
8445 ImplNewFont();
8446 if( mbInitFont )
8447 ImplInitFont();
8448 if( !mpFontEntry )
8449 return FALSE;
8451 // a little font charmap cache helps considerably
8452 static const int NMAXITEMS = 16;
8453 static int nUsedItems = 0, nCurItem = 0;
8455 struct CharMapCacheItem { const ImplFontData* mpFontData; FontCharMap maCharMap; };
8456 static CharMapCacheItem aCache[ NMAXITEMS ];
8458 const ImplFontData* pFontData = mpFontEntry->maFontSelData.mpFontData;
8460 int i;
8461 for( i = nUsedItems; --i >= 0; )
8462 if( pFontData == aCache[i].mpFontData )
8463 break;
8464 if( i >= 0 ) // found in cache
8466 rFontCharMap.Reset( aCache[i].maCharMap.mpImpl );
8468 else // need to cache
8470 ImplFontCharMap* pNewMap = mpGraphics->GetImplFontCharMap();
8471 rFontCharMap.Reset( pNewMap );
8473 // manage cache round-robin and insert data
8474 CharMapCacheItem& rItem = aCache[ nCurItem ];
8475 rItem.mpFontData = pFontData;
8476 rItem.maCharMap.Reset( pNewMap );
8478 if( ++nCurItem >= NMAXITEMS )
8479 nCurItem = 0;
8481 if( ++nUsedItems >= NMAXITEMS )
8482 nUsedItems = NMAXITEMS;
8485 if( rFontCharMap.IsDefaultMap() )
8486 return FALSE;
8487 return TRUE;
8490 // -----------------------------------------------------------------------
8492 xub_StrLen OutputDevice::HasGlyphs( const Font& rTempFont, const String& rStr,
8493 xub_StrLen nIndex, xub_StrLen nLen ) const
8495 if( nIndex >= rStr.Len() )
8496 return nIndex;
8497 xub_StrLen nEnd = nIndex + nLen;
8498 if( (ULONG)nIndex+nLen > rStr.Len() )
8499 nEnd = rStr.Len();
8501 DBG_ASSERT( nIndex < nEnd, "StartPos >= EndPos?" );
8502 DBG_ASSERT( nEnd <= rStr.Len(), "String too short" );
8504 // to get the map temporarily set font
8505 const Font aOrigFont = GetFont();
8506 const_cast<OutputDevice&>(*this).SetFont( rTempFont );
8507 FontCharMap aFontCharMap;
8508 BOOL bRet = GetFontCharMap( aFontCharMap );
8509 const_cast<OutputDevice&>(*this).SetFont( aOrigFont );
8511 // if fontmap is unknown assume it doesn't have the glyphs
8512 if( bRet == FALSE )
8513 return nIndex;
8515 const sal_Unicode* pStr = rStr.GetBuffer();
8516 for( pStr += nIndex; nIndex < nEnd; ++pStr, ++nIndex )
8517 if( ! aFontCharMap.HasChar( *pStr ) )
8518 return nIndex;
8520 return STRING_LEN;
8523 // -----------------------------------------------------------------------