1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <sal/types.h>
21 #include <config_folders.h>
22
23 #include <algorithm>
24 #include <map>
25 #include <memory>
26 #include <mutex>
27 #include <set>
28 #include <string.h>
29 #include <svsys.h>
30 #include <vector>
31
32 #include <o3tl/lru_map.hxx>
33 #include <basegfx/matrix/b2dhommatrixtools.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <i18nlangtag/mslangid.hxx>
36 #include <osl/file.hxx>
37 #include <osl/process.h>
38 #include <rtl/bootstrap.hxx>
39 #include <rtl/tencinfo.h>
40 #include <sal/log.hxx>
41 #include <o3tl/char16_t2wchar_t.hxx>
42 #include <tools/helpers.hxx>
43 #include <tools/stream.hxx>
44 #include <tools/urlobj.hxx>
45 #include <unotools/fontcfg.hxx>
46 #include <vcl/settings.hxx>
47 #include <vcl/sysdata.hxx>
48 #include <vcl/metric.hxx>
49 #include <vcl/fontcharmap.hxx>
50 #include <comphelper/scopeguard.hxx>
51
52 #include <fontsubset.hxx>
53 #include <outdev.h>
54 #include <PhysicalFontCollection.hxx>
55 #include <PhysicalFontFace.hxx>
56 #include <sft.hxx>
57 #include <win/saldata.hxx>
58 #include <win/salgdi.h>
59 #include <win/winlayout.hxx>
60 #include <win/wingdiimpl.hxx>
61 #include <impfontcharmap.hxx>
62 #include <impfontmetricdata.hxx>
63 #include <impglyphitem.hxx>
64
65 #if HAVE_FEATURE_SKIA
66 #include <vcl/skia/SkiaHelper.hxx>
67 #include <skia/win/font.hxx>
68 #endif
69
70 using namespace vcl;
71
FixedFromDouble(double d)72 static FIXED FixedFromDouble( double d )
73 {
74 const tools::Long l = static_cast<tools::Long>( d * 65536. );
75 return *reinterpret_cast<FIXED const *>(&l);
76 }
77
IntTimes256FromFixed(FIXED f)78 static int IntTimes256FromFixed(FIXED f)
79 {
80 int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
81 return nFixedTimes256;
82 }
83
84 namespace {
85
86 // raw font data with a scoped lifetime
87 class RawFontData
88 {
89 public:
90 explicit RawFontData( HDC, DWORD nTableTag=0 );
get() const91 const unsigned char* get() const { return mpRawBytes.get(); }
steal()92 const unsigned char* steal() { return mpRawBytes.release(); }
size() const93 int size() const { return mnByteCount; }
94
95 private:
96 std::unique_ptr<unsigned char[]> mpRawBytes;
97 unsigned mnByteCount;
98 };
99
100 }
101
RawFontData(HDC hDC,DWORD nTableTag)102 RawFontData::RawFontData( HDC hDC, DWORD nTableTag )
103 : mnByteCount( 0 )
104 {
105 // get required size in bytes
106 mnByteCount = ::GetFontData( hDC, nTableTag, 0, nullptr, 0 );
107 if (mnByteCount == GDI_ERROR)
108 mnByteCount = 0;
109 if (!mnByteCount)
110 return;
111
112 // allocate the array
113 mpRawBytes.reset(new unsigned char[ mnByteCount ]);
114
115 // get raw data in chunks small enough for GetFontData()
116 unsigned nRawDataOfs = 0;
117 DWORD nMaxChunkSize = 0x100000;
118 for(;;)
119 {
120 // calculate remaining raw data to get
121 DWORD nFDGet = mnByteCount - nRawDataOfs;
122 if( nFDGet <= 0 )
123 break;
124 // #i56745# limit GetFontData requests
125 if( nFDGet > nMaxChunkSize )
126 nFDGet = nMaxChunkSize;
127 const DWORD nFDGot = ::GetFontData( hDC, nTableTag, nRawDataOfs,
128 mpRawBytes.get() + nRawDataOfs, nFDGet );
129 if( !nFDGot )
130 break;
131 else if( nFDGot != GDI_ERROR )
132 nRawDataOfs += nFDGot;
133 else
134 {
135 // was the chunk too big? reduce it
136 nMaxChunkSize /= 2;
137 if( nMaxChunkSize < 0x10000 )
138 break;
139 }
140 }
141
142 // cleanup if the raw data is incomplete
143 if( nRawDataOfs != mnByteCount )
144 {
145 mpRawBytes.reset();
146 // mnByteCount must correspond to mpRawBytes length
147 SAL_WARN( "vcl", "Raw data of font is incomplete: " << nRawDataOfs << " byte(s) found whereas " << mnByteCount << " byte(s) expected!" );
148 mnByteCount = 0;
149 }
150 }
151
152 // platform specific font substitution hooks for glyph fallback enhancement
153
154 namespace {
155
156 class WinPreMatchFontSubstititution
157 : public ImplPreMatchFontSubstitution
158 {
159 public:
160 bool FindFontSubstitute(FontSelectPattern&) const override;
161 };
162
163 class WinGlyphFallbackSubstititution
164 : public ImplGlyphFallbackFontSubstitution
165 {
166 public:
WinGlyphFallbackSubstititution()167 explicit WinGlyphFallbackSubstititution()
168 : mhDC(GetDC(nullptr))
169 {
170 };
171
~WinGlyphFallbackSubstititution()172 ~WinGlyphFallbackSubstititution() override
173 {
174 ReleaseDC(nullptr, mhDC);
175 };
176
177 bool FindFontSubstitute(FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
178 private:
179 HDC mhDC;
180 bool HasMissingChars(PhysicalFontFace*, OUString& rMissingChars) const;
181 };
182
183 }
184
185 // does a font face hold the given missing characters?
HasMissingChars(PhysicalFontFace * pFace,OUString & rMissingChars) const186 bool WinGlyphFallbackSubstititution::HasMissingChars(PhysicalFontFace* pFace, OUString& rMissingChars) const
187 {
188 WinFontFace* pWinFont = static_cast< WinFontFace* >(pFace);
189 FontCharMapRef xFontCharMap = pWinFont->GetFontCharMap();
190 if( !xFontCharMap.is() )
191 {
192 // construct a Size structure as the parameter of constructor of class FontSelectPattern
193 const Size aSize( pFace->GetWidth(), pFace->GetHeight() );
194 // create a FontSelectPattern object for getting s LOGFONT
195 const FontSelectPattern aFSD( *pFace, aSize, static_cast<float>(aSize.Height()), 0, false );
196 // construct log font
197 LOGFONTW aLogFont;
198 ImplGetLogFontFromFontSelect( aFSD, pFace, aLogFont );
199
200 // create HFONT from log font
201 HFONT hNewFont = ::CreateFontIndirectW( &aLogFont );
202 // select the new font into device
203 HFONT hOldFont = ::SelectFont( mhDC, hNewFont );
204
205 // read CMAP table to update their xFontCharMap
206 pWinFont->UpdateFromHDC( mhDC );
207
208 // cleanup temporary font
209 ::SelectFont( mhDC, hOldFont );
210 ::DeleteFont( hNewFont );
211
212 // get the new charmap
213 xFontCharMap = pWinFont->GetFontCharMap();
214 }
215
216 // avoid fonts with unknown CMAP subtables for glyph fallback
217 if( !xFontCharMap.is() || xFontCharMap->IsDefaultMap() )
218 return false;
219
220 int nMatchCount = 0;
221 std::vector<sal_UCS4> rRemainingCodes;
222 const sal_Int32 nStrLen = rMissingChars.getLength();
223 sal_Int32 nStrIdx = 0;
224 while (nStrIdx < nStrLen)
225 {
226 const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
227 if (xFontCharMap->HasChar(uChar))
228 nMatchCount++;
229 else
230 rRemainingCodes.push_back(uChar);
231 }
232
233 xFontCharMap = nullptr;
234
235 if (nMatchCount > 0)
236 rMissingChars = OUString(rRemainingCodes.data(), rRemainingCodes.size());
237
238 return nMatchCount > 0;
239 }
240
241 namespace
242 {
243 //used by 2-level font fallback
findDevFontListByLocale(const PhysicalFontCollection & rFontCollection,const LanguageTag & rLanguageTag)244 PhysicalFontFamily* findDevFontListByLocale(const PhysicalFontCollection &rFontCollection,
245 const LanguageTag& rLanguageTag )
246 {
247 // get the default font for a specified locale
248 const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
249 const OUString aDefault = rDefaults.getUserInterfaceFont(rLanguageTag);
250 return rFontCollection.FindFontFamilyByTokenNames(aDefault);
251 }
252 }
253
254 // These are Win 3.1 bitmap fonts using "FON" font format
255 // which is not supported with DirectWrite so let's substitute them
256 // with a font that is supported and always available.
257 // Based on:
258 // https://dxr.mozilla.org/mozilla-esr10/source/gfx/thebes/gfxDWriteFontList.cpp#1057
259 const std::map<OUString, OUString> aBitmapFontSubs =
260 {
261 { "MS Sans Serif", "Microsoft Sans Serif" },
262 { "MS Serif", "Times New Roman" },
263 { "Small Fonts", "Arial" },
264 { "Courier", "Courier New" },
265 { "Roman", "Times New Roman" },
266 { "Script", "Mistral" }
267 };
268
269 // TODO: See if Windows have API that we can use here to improve font fallback.
FindFontSubstitute(FontSelectPattern & rFontSelData) const270 bool WinPreMatchFontSubstititution::FindFontSubstitute(FontSelectPattern& rFontSelData) const
271 {
272 if (rFontSelData.IsSymbolFont() || IsStarSymbol(rFontSelData.maSearchName))
273 return false;
274
275 for (const auto& aSub : aBitmapFontSubs)
276 {
277 if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
278 {
279 rFontSelData.maSearchName = aSub.second;
280 return true;
281 }
282 }
283
284 return false;
285 }
286
287 // find a fallback font for missing characters
288 // TODO: should stylistic matches be searched and preferred?
FindFontSubstitute(FontSelectPattern & rFontSelData,LogicalFontInstance *,OUString & rMissingChars) const289 bool WinGlyphFallbackSubstititution::FindFontSubstitute(FontSelectPattern& rFontSelData, LogicalFontInstance* /*pLogicalFont*/, OUString& rMissingChars) const
290 {
291 // guess a locale matching to the missing chars
292 LanguageType eLang = rFontSelData.meLanguage;
293 LanguageTag aLanguageTag( eLang);
294
295 // fall back to default UI locale if the font language is inconclusive
296 if( eLang == LANGUAGE_DONTKNOW )
297 aLanguageTag = Application::GetSettings().GetUILanguageTag();
298
299 // first level fallback:
300 // try use the locale specific default fonts defined in VCL.xcu
301 const PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
302 PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag);
303 if( pFontFamily )
304 {
305 PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
306 if( HasMissingChars( pFace, rMissingChars ) )
307 {
308 rFontSelData.maSearchName = pFontFamily->GetSearchName();
309 return true;
310 }
311 }
312
313 // are the missing characters symbols?
314 pFontFamily = pFontCollection->FindFontFamilyByAttributes( ImplFontAttrs::Symbol,
315 rFontSelData.GetWeight(),
316 rFontSelData.GetWidthType(),
317 rFontSelData.GetItalic(),
318 rFontSelData.maSearchName );
319 if( pFontFamily )
320 {
321 PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
322 if( HasMissingChars( pFace, rMissingChars ) )
323 {
324 rFontSelData.maSearchName = pFontFamily->GetSearchName();
325 return true;
326 }
327 }
328
329 // last level fallback, check each font type face one by one
330 std::unique_ptr<ImplDeviceFontList> pTestFontList = pFontCollection->GetDeviceFontList();
331 // limit the count of fonts to be checked to prevent hangs
332 static const int MAX_GFBFONT_COUNT = 600;
333 int nTestFontCount = pTestFontList->Count();
334 if( nTestFontCount > MAX_GFBFONT_COUNT )
335 nTestFontCount = MAX_GFBFONT_COUNT;
336
337 bool bFound = false;
338 for( int i = 0; i < nTestFontCount; ++i )
339 {
340 PhysicalFontFace* pFace = pTestFontList->Get( i );
341 bFound = HasMissingChars( pFace, rMissingChars );
342 if( !bFound )
343 continue;
344 rFontSelData.maSearchName = pFace->GetFamilyName();
345 break;
346 }
347
348 return bFound;
349 }
350
351 namespace {
352
353 struct ImplEnumInfo
354 {
355 HDC mhDC;
356 PhysicalFontCollection* mpList;
357 OUString* mpName;
358 LOGFONTW* mpLogFont;
359 bool mbPrinter;
360 int mnFontCount;
361 };
362
363 }
364
ImplCharSetToSal(BYTE nCharSet)365 static rtl_TextEncoding ImplCharSetToSal( BYTE nCharSet )
366 {
367 rtl_TextEncoding eTextEncoding;
368
369 if ( nCharSet == OEM_CHARSET )
370 {
371 UINT nCP = static_cast<sal_uInt16>(GetOEMCP());
372 switch ( nCP )
373 {
374 // It is unclear why these two (undefined?) code page numbers are
375 // handled specially here:
376 case 1004: eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
377 case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
378 default:
379 eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
380 break;
381 }
382 }
383 else
384 {
385 if( nCharSet )
386 eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
387 else
388 eTextEncoding = RTL_TEXTENCODING_UNICODE;
389 }
390
391 return eTextEncoding;
392 }
393
ImplFamilyToSal(BYTE nFamily)394 static FontFamily ImplFamilyToSal( BYTE nFamily )
395 {
396 switch ( nFamily & 0xF0 )
397 {
398 case FF_DECORATIVE:
399 return FAMILY_DECORATIVE;
400
401 case FF_MODERN:
402 return FAMILY_MODERN;
403
404 case FF_ROMAN:
405 return FAMILY_ROMAN;
406
407 case FF_SCRIPT:
408 return FAMILY_SCRIPT;
409
410 case FF_SWISS:
411 return FAMILY_SWISS;
412
413 default:
414 break;
415 }
416
417 return FAMILY_DONTKNOW;
418 }
419
ImplFamilyToWin(FontFamily eFamily)420 static BYTE ImplFamilyToWin( FontFamily eFamily )
421 {
422 switch ( eFamily )
423 {
424 case FAMILY_DECORATIVE:
425 return FF_DECORATIVE;
426
427 case FAMILY_MODERN:
428 return FF_MODERN;
429
430 case FAMILY_ROMAN:
431 return FF_ROMAN;
432
433 case FAMILY_SCRIPT:
434 return FF_SCRIPT;
435
436 case FAMILY_SWISS:
437 return FF_SWISS;
438
439 case FAMILY_SYSTEM:
440 return FF_SWISS;
441
442 default:
443 break;
444 }
445
446 return FF_DONTCARE;
447 }
448
ImplWeightToSal(int nWeight)449 static FontWeight ImplWeightToSal( int nWeight )
450 {
451 if ( nWeight <= FW_THIN )
452 return WEIGHT_THIN;
453 else if ( nWeight <= FW_ULTRALIGHT )
454 return WEIGHT_ULTRALIGHT;
455 else if ( nWeight <= FW_LIGHT )
456 return WEIGHT_LIGHT;
457 else if ( nWeight < FW_MEDIUM )
458 return WEIGHT_NORMAL;
459 else if ( nWeight == FW_MEDIUM )
460 return WEIGHT_MEDIUM;
461 else if ( nWeight <= FW_SEMIBOLD )
462 return WEIGHT_SEMIBOLD;
463 else if ( nWeight <= FW_BOLD )
464 return WEIGHT_BOLD;
465 else if ( nWeight <= FW_ULTRABOLD )
466 return WEIGHT_ULTRABOLD;
467 else
468 return WEIGHT_BLACK;
469 }
470
ImplWeightToWin(FontWeight eWeight)471 static int ImplWeightToWin( FontWeight eWeight )
472 {
473 switch ( eWeight )
474 {
475 case WEIGHT_THIN:
476 return FW_THIN;
477
478 case WEIGHT_ULTRALIGHT:
479 return FW_ULTRALIGHT;
480
481 case WEIGHT_LIGHT:
482 return FW_LIGHT;
483
484 case WEIGHT_SEMILIGHT:
485 case WEIGHT_NORMAL:
486 return FW_NORMAL;
487
488 case WEIGHT_MEDIUM:
489 return FW_MEDIUM;
490
491 case WEIGHT_SEMIBOLD:
492 return FW_SEMIBOLD;
493
494 case WEIGHT_BOLD:
495 return FW_BOLD;
496
497 case WEIGHT_ULTRABOLD:
498 return FW_ULTRABOLD;
499
500 case WEIGHT_BLACK:
501 return FW_BLACK;
502
503 default:
504 break;
505 }
506
507 return 0;
508 }
509
ImplLogPitchToSal(BYTE nPitch)510 static FontPitch ImplLogPitchToSal( BYTE nPitch )
511 {
512 if ( nPitch & FIXED_PITCH )
513 return PITCH_FIXED;
514 else
515 return PITCH_VARIABLE;
516 }
517
ImplMetricPitchToSal(BYTE nPitch)518 static FontPitch ImplMetricPitchToSal( BYTE nPitch )
519 {
520 // Grrrr! See NT help
521 if ( !(nPitch & TMPF_FIXED_PITCH) )
522 return PITCH_FIXED;
523 else
524 return PITCH_VARIABLE;
525 }
526
ImplPitchToWin(FontPitch ePitch)527 static BYTE ImplPitchToWin( FontPitch ePitch )
528 {
529 if ( ePitch == PITCH_FIXED )
530 return FIXED_PITCH;
531 else if ( ePitch == PITCH_VARIABLE )
532 return VARIABLE_PITCH;
533 else
534 return DEFAULT_PITCH;
535 }
536
WinFont2DevFontAttributes(const ENUMLOGFONTEXW & rEnumFont,const NEWTEXTMETRICW & rMetric)537 static FontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
538 const NEWTEXTMETRICW& rMetric)
539 {
540 FontAttributes aDFA;
541
542 const LOGFONTW rLogFont = rEnumFont.elfLogFont;
543
544 // get font face attributes
545 aDFA.SetFamilyType(ImplFamilyToSal( rLogFont.lfPitchAndFamily ));
546 aDFA.SetWidthType(WIDTH_DONTKNOW);
547 aDFA.SetWeight(ImplWeightToSal( rLogFont.lfWeight ));
548 aDFA.SetItalic((rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE);
549 aDFA.SetPitch(ImplLogPitchToSal( rLogFont.lfPitchAndFamily ));
550 aDFA.SetSymbolFlag(rLogFont.lfCharSet == SYMBOL_CHARSET);
551
552 // get the font face name
553 aDFA.SetFamilyName(OUString(o3tl::toU(rLogFont.lfFaceName)));
554
555 // use the face's style name only if it looks reasonable
556 const wchar_t* pStyleName = rEnumFont.elfStyle;
557 const wchar_t* pEnd = pStyleName + sizeof(rEnumFont.elfStyle)/sizeof(*rEnumFont.elfStyle);
558 const wchar_t* p = pStyleName;
559 for(; *p && (p < pEnd); ++p )
560 if( *p < 0x0020 )
561 break;
562 if( p < pEnd )
563 aDFA.SetStyleName(OUString(o3tl::toU(pStyleName)));
564
565 // heuristics for font quality
566 // - opentypeTT > truetype
567 aDFA.SetQuality( 0 );
568 if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
569 aDFA.IncreaseQualityBy( 50 );
570 if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
571 aDFA.IncreaseQualityBy( 10 );
572
573 // TODO: add alias names
574 return aDFA;
575 }
576
577
ImplLogMetricToDevFontDataW(const ENUMLOGFONTEXW * pLogFont,const NEWTEXTMETRICW * pMetric)578 static rtl::Reference<WinFontFace> ImplLogMetricToDevFontDataW( const ENUMLOGFONTEXW* pLogFont,
579 const NEWTEXTMETRICW* pMetric)
580 {
581 rtl::Reference<WinFontFace> pData = new WinFontFace(
582 WinFont2DevFontAttributes(*pLogFont, *pMetric),
583 pLogFont->elfLogFont.lfCharSet,
584 pMetric->tmPitchAndFamily );
585
586 return pData;
587 }
588
ImplSalLogFontToFontW(HDC hDC,const LOGFONTW & rLogFont,Font & rFont)589 void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
590 {
591 OUString aFontName( o3tl::toU(rLogFont.lfFaceName) );
592 if (!aFontName.isEmpty())
593 {
594 rFont.SetFamilyName( aFontName );
595 rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
596 rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
597 rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
598 rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
599
600 tools::Long nFontHeight = rLogFont.lfHeight;
601 if ( nFontHeight < 0 )
602 nFontHeight = -nFontHeight;
603 tools::Long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
604 if( !nDPIY )
605 nDPIY = 600;
606 nFontHeight *= 72;
607 nFontHeight += nDPIY/2;
608 nFontHeight /= nDPIY;
609 rFont.SetFontSize( Size( 0, nFontHeight ) );
610 rFont.SetOrientation( Degree10(static_cast<sal_Int16>(rLogFont.lfEscapement)) );
611 if ( rLogFont.lfItalic )
612 rFont.SetItalic( ITALIC_NORMAL );
613 else
614 rFont.SetItalic( ITALIC_NONE );
615 if ( rLogFont.lfUnderline )
616 rFont.SetUnderline( LINESTYLE_SINGLE );
617 else
618 rFont.SetUnderline( LINESTYLE_NONE );
619 if ( rLogFont.lfStrikeOut )
620 rFont.SetStrikeout( STRIKEOUT_SINGLE );
621 else
622 rFont.SetStrikeout( STRIKEOUT_NONE );
623 }
624 }
625
WinFontFace(const FontAttributes & rDFS,BYTE eWinCharSet,BYTE nPitchAndFamily)626 WinFontFace::WinFontFace( const FontAttributes& rDFS,
627 BYTE eWinCharSet, BYTE nPitchAndFamily )
628 : PhysicalFontFace( rDFS ),
629 mnId( 0 ),
630 mbFontCapabilitiesRead( false ),
631 meWinCharSet( eWinCharSet ),
632 mnPitchAndFamily( nPitchAndFamily ),
633 mbAliasSymbolsHigh( false ),
634 mbAliasSymbolsLow( false )
635 {
636 if( eWinCharSet == SYMBOL_CHARSET )
637 {
638 if( (nPitchAndFamily & TMPF_TRUETYPE) != 0 )
639 {
640 // truetype fonts need their symbols as U+F0xx
641 mbAliasSymbolsHigh = true;
642 }
643 else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_DEVICE))
644 == (TMPF_VECTOR|TMPF_DEVICE) )
645 {
646 // scalable device fonts (e.g. builtin printer fonts)
647 // need their symbols as U+00xx
648 mbAliasSymbolsLow = true;
649 }
650 else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) == 0 )
651 {
652 // bitmap fonts need their symbols as U+F0xx
653 mbAliasSymbolsHigh = true;
654 }
655 }
656 }
657
~WinFontFace()658 WinFontFace::~WinFontFace()
659 {
660 mxUnicodeMap.clear();
661 }
662
GetFontId() const663 sal_IntPtr WinFontFace::GetFontId() const
664 {
665 return mnId;
666 }
667
CreateFontInstance(const FontSelectPattern & rFSD) const668 rtl::Reference<LogicalFontInstance> WinFontFace::CreateFontInstance(const FontSelectPattern& rFSD) const
669 {
670 #if HAVE_FEATURE_SKIA
671 if (SkiaHelper::isVCLSkiaEnabled())
672 return new SkiaWinFontInstance(*this, rFSD);
673 #endif
674 return new WinFontInstance(*this, rFSD);
675 }
676
CalcTag(const char p[5])677 static DWORD CalcTag( const char p[5]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }
678
UpdateFromHDC(HDC hDC) const679 void WinFontFace::UpdateFromHDC( HDC hDC ) const
680 {
681 // short circuit if already initialized
682 if( mxUnicodeMap.is() )
683 return;
684
685 ReadCmapTable( hDC );
686 GetFontCapabilities( hDC );
687 }
688
GetFontCharMap() const689 FontCharMapRef WinFontFace::GetFontCharMap() const
690 {
691 return mxUnicodeMap;
692 }
693
GetFontCapabilities(vcl::FontCapabilities & rFontCapabilities) const694 bool WinFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
695 {
696 rFontCapabilities = maFontCapabilities;
697 return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
698 }
699
ReadCmapTable(HDC hDC) const700 void WinFontFace::ReadCmapTable( HDC hDC ) const
701 {
702 if( mxUnicodeMap.is() )
703 return;
704
705 bool bIsSymbolFont = (meWinCharSet == SYMBOL_CHARSET);
706 // get the CMAP table from the font which is selected into the DC
707 const DWORD nCmapTag = CalcTag( "cmap" );
708 const RawFontData aRawFontData( hDC, nCmapTag );
709 // parse the CMAP table if available
710 if( aRawFontData.get() ) {
711 CmapResult aResult;
712 ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult );
713 aResult.mbSymbolic = bIsSymbolFont;
714 if( aResult.mnRangeCount > 0 )
715 {
716 FontCharMapRef pUnicodeMap(new FontCharMap(aResult));
717 mxUnicodeMap = pUnicodeMap;
718 }
719 }
720
721 if( !mxUnicodeMap.is() )
722 {
723 mxUnicodeMap = FontCharMap::GetDefaultMap( bIsSymbolFont );
724 }
725 }
726
GetFontCapabilities(HDC hDC) const727 void WinFontFace::GetFontCapabilities( HDC hDC ) const
728 {
729 // read this only once per font
730 if( mbFontCapabilitiesRead )
731 return;
732
733 mbFontCapabilitiesRead = true;
734
735 // OS/2 table
736 const DWORD OS2Tag = CalcTag( "OS/2" );
737 DWORD nLength = ::GetFontData( hDC, OS2Tag, 0, nullptr, 0 );
738 if( (nLength != GDI_ERROR) && nLength )
739 {
740 std::vector<unsigned char> aTable( nLength );
741 unsigned char* pTable = aTable.data();
742 ::GetFontData( hDC, OS2Tag, 0, pTable, nLength );
743 vcl::getTTCoverage(maFontCapabilities.oUnicodeRange, maFontCapabilities.oCodePageRange, pTable, nLength);
744 }
745 }
746
SetTextColor(Color nColor)747 void WinSalGraphics::SetTextColor( Color nColor )
748 {
749 COLORREF aCol = PALETTERGB( nColor.GetRed(),
750 nColor.GetGreen(),
751 nColor.GetBlue() );
752
753 if( !mbPrinter &&
754 GetSalData()->mhDitherPal &&
755 ImplIsSysColorEntry( nColor ) )
756 {
757 aCol = PALRGB_TO_RGB( aCol );
758 }
759
760 ::SetTextColor( getHDC(), aCol );
761 }
762
SalEnumQueryFontProcExW(const LOGFONTW *,const TEXTMETRICW *,DWORD,LPARAM lParam)763 static int CALLBACK SalEnumQueryFontProcExW( const LOGFONTW*, const TEXTMETRICW*, DWORD, LPARAM lParam )
764 {
765 *reinterpret_cast<bool*>(lParam) = true;
766 return 0;
767 }
768
ImplGetLogFontFromFontSelect(const FontSelectPattern & rFont,const PhysicalFontFace * pFontFace,LOGFONTW & rLogFont)769 void ImplGetLogFontFromFontSelect( const FontSelectPattern& rFont,
770 const PhysicalFontFace* pFontFace,
771 LOGFONTW& rLogFont )
772 {
773 OUString aName;
774 if (pFontFace)
775 aName = pFontFace->GetFamilyName();
776 else
777 aName = rFont.GetFamilyName().getToken( 0, ';' );
778
779 UINT nNameLen = aName.getLength();
780 if (nNameLen >= LF_FACESIZE)
781 nNameLen = LF_FACESIZE - 1;
782 memcpy( rLogFont.lfFaceName, aName.getStr(), nNameLen*sizeof( wchar_t ) );
783 rLogFont.lfFaceName[nNameLen] = 0;
784
785 if (pFontFace)
786 {
787 const WinFontFace* pWinFontData = static_cast<const WinFontFace*>(pFontFace);
788 rLogFont.lfCharSet = pWinFontData->GetCharSet();
789 rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
790 }
791 else
792 {
793 rLogFont.lfCharSet = rFont.IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
794 rLogFont.lfPitchAndFamily = ImplPitchToWin( rFont.GetPitch() )
795 | ImplFamilyToWin( rFont.GetFamilyType() );
796 }
797
798 rLogFont.lfWeight = ImplWeightToWin( rFont.GetWeight() );
799 rLogFont.lfHeight = static_cast<LONG>(-rFont.mnHeight);
800 rLogFont.lfWidth = static_cast<LONG>(rFont.mnWidth);
801 rLogFont.lfUnderline = 0;
802 rLogFont.lfStrikeOut = 0;
803 rLogFont.lfItalic = BYTE(rFont.GetItalic() != ITALIC_NONE);
804 rLogFont.lfEscapement = rFont.mnOrientation.get();
805 rLogFont.lfOrientation = rLogFont.lfEscapement;
806 rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
807 rLogFont.lfQuality = DEFAULT_QUALITY;
808 rLogFont.lfOutPrecision = OUT_TT_PRECIS;
809 if ( rFont.mnOrientation )
810 rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
811
812 // disable antialiasing if requested
813 if ( rFont.mbNonAntialiased )
814 rLogFont.lfQuality = NONANTIALIASED_QUALITY;
815
816 }
817
ImplDoSetFont(FontSelectPattern const & i_rFont,const PhysicalFontFace * i_pFontFace,HFONT & o_rOldFont)818 std::tuple<HFONT,bool,sal_Int32> WinSalGraphics::ImplDoSetFont(FontSelectPattern const & i_rFont,
819 const PhysicalFontFace * i_pFontFace,
820 HFONT& o_rOldFont)
821 {
822 HFONT hNewFont = nullptr;
823
824 LOGFONTW aLogFont;
825 ImplGetLogFontFromFontSelect( i_rFont, i_pFontFace, aLogFont );
826
827 bool bIsCJKVerticalFont = false;
828 // select vertical mode for printing if requested and available
829 if ( i_rFont.mbVertical && mbPrinter )
830 {
831 constexpr size_t nLen = sizeof(aLogFont.lfFaceName) - sizeof(aLogFont.lfFaceName[0]);
832 // vertical fonts start with an '@'
833 memmove( &aLogFont.lfFaceName[1], &aLogFont.lfFaceName[0], nLen );
834 aLogFont.lfFaceName[0] = '@';
835 aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
836
837 // check availability of vertical mode for this font
838 EnumFontFamiliesExW( getHDC(), &aLogFont, SalEnumQueryFontProcExW,
839 reinterpret_cast<LPARAM>(&bIsCJKVerticalFont), 0 );
840 if( !bIsCJKVerticalFont )
841 {
842 // restore non-vertical name if not vertical mode isn't available
843 memcpy( &aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1], nLen );
844 aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
845 }
846 }
847
848 hNewFont = ::CreateFontIndirectW( &aLogFont );
849
850 HDC hdcScreen = nullptr;
851 if( mbVirDev )
852 // only required for virtual devices, see below for details
853 hdcScreen = GetDC(nullptr);
854 if( hdcScreen )
855 {
856 // select font into screen hdc first to get an antialiased font
857 // and instantly restore the default font!
858 // see knowledge base article 305290:
859 // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
860 SelectFont( hdcScreen, SelectFont( hdcScreen , hNewFont ) );
861 }
862 o_rOldFont = ::SelectFont( getHDC(), hNewFont );
863
864 TEXTMETRICW aTextMetricW;
865 if( !::GetTextMetricsW( getHDC(), &aTextMetricW ) )
866 {
867 // the selected font doesn't work => try a replacement
868 // TODO: use its font fallback instead
869 lstrcpynW( aLogFont.lfFaceName, L"Courier New", 12 );
870 aLogFont.lfPitchAndFamily = FIXED_PITCH;
871 HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
872 SelectFont( getHDC(), hNewFont2 );
873 DeleteFont( hNewFont );
874 hNewFont = hNewFont2;
875 bIsCJKVerticalFont = false;
876 }
877
878 if( hdcScreen )
879 ::ReleaseDC( nullptr, hdcScreen );
880
881 return std::make_tuple(hNewFont, bIsCJKVerticalFont, static_cast<sal_Int32>(aTextMetricW.tmDescent));
882 }
883
SetFont(LogicalFontInstance * pFont,int nFallbackLevel)884 void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
885 {
886 // return early if there is no new font
887 if( !pFont )
888 {
889 if (!mpWinFontEntry[nFallbackLevel].is())
890 return;
891
892 // select original DC font
893 assert(mhDefFont);
894 ::SelectFont(getHDC(), mhDefFont);
895 mhDefFont = nullptr;
896
897 // release no longer referenced font handles
898 for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
899 mpWinFontEntry[i] = nullptr;
900 return;
901 }
902
903 WinFontInstance *pFontInstance = static_cast<WinFontInstance*>(pFont);
904 mpWinFontEntry[ nFallbackLevel ] = pFontInstance;
905
906 HFONT hOldFont = nullptr;
907 HFONT hNewFont = pFontInstance->GetHFONT();
908 if (!hNewFont)
909 {
910 pFontInstance->SetGraphics(this);
911 hNewFont = pFontInstance->GetHFONT();
912 }
913 hOldFont = ::SelectFont(getHDC(), hNewFont);
914
915 // keep default font
916 if( !mhDefFont )
917 mhDefFont = hOldFont;
918 else
919 {
920 // release no longer referenced font handles
921 for( int i = nFallbackLevel + 1; i < MAX_FALLBACK && mpWinFontEntry[i].is(); ++i )
922 mpWinFontEntry[i] = nullptr;
923 }
924
925 // now the font is live => update font face
926 const WinFontFace* pFontFace = pFontInstance->GetFontFace();
927 pFontFace->UpdateFromHDC(getHDC());
928 }
929
GetFontMetric(ImplFontMetricDataRef & rxFontMetric,int nFallbackLevel)930 void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel )
931 {
932 // temporarily change the HDC to the font in the fallback level
933 rtl::Reference<WinFontInstance> pFontInstance = mpWinFontEntry[nFallbackLevel];
934 const HFONT hOldFont = SelectFont(getHDC(), pFontInstance->GetHFONT());
935
936 wchar_t aFaceName[LF_FACESIZE+60];
937 if( GetTextFaceW( getHDC(), SAL_N_ELEMENTS(aFaceName), aFaceName ) )
938 rxFontMetric->SetFamilyName(OUString(o3tl::toU(aFaceName)));
939
940 rxFontMetric->SetMinKashida(pFontInstance->GetKashidaWidth());
941 rxFontMetric->ImplCalcLineSpacing(pFontInstance.get());
942
943 // get the font metric
944 OUTLINETEXTMETRICW aOutlineMetric;
945 const bool bOK = GetOutlineTextMetricsW(getHDC(), sizeof(aOutlineMetric), &aOutlineMetric);
946 // restore the HDC to the font in the base level
947 SelectFont( getHDC(), hOldFont );
948 if( !bOK )
949 return;
950
951 TEXTMETRICW aWinMetric = aOutlineMetric.otmTextMetrics;
952
953 // device independent font attributes
954 rxFontMetric->SetFamilyType(ImplFamilyToSal( aWinMetric.tmPitchAndFamily ));
955 rxFontMetric->SetSymbolFlag(aWinMetric.tmCharSet == SYMBOL_CHARSET);
956 rxFontMetric->SetWeight(ImplWeightToSal( aWinMetric.tmWeight ));
957 rxFontMetric->SetPitch(ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily ));
958 rxFontMetric->SetItalic(aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE);
959 rxFontMetric->SetSlant( 0 );
960
961 // transformation dependent font metrics
962 rxFontMetric->SetWidth(static_cast<int>(pFontInstance->GetScale() * aWinMetric.tmAveCharWidth));
963 }
964
GetFontCharMap() const965 FontCharMapRef WinSalGraphics::GetFontCharMap() const
966 {
967 if (!mpWinFontEntry[0])
968 {
969 return FontCharMapRef( new FontCharMap() );
970 }
971 return mpWinFontEntry[0]->GetFontFace()->GetFontCharMap();
972 }
973
GetFontCapabilities(vcl::FontCapabilities & rFontCapabilities) const974 bool WinSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
975 {
976 if (!mpWinFontEntry[0])
977 return false;
978 return mpWinFontEntry[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
979 }
980
SalEnumFontsProcExW(const LOGFONTW * lpelfe,const TEXTMETRICW * lpntme,DWORD nFontType,LPARAM lParam)981 static int CALLBACK SalEnumFontsProcExW( const LOGFONTW* lpelfe,
982 const TEXTMETRICW* lpntme,
983 DWORD nFontType, LPARAM lParam )
984 {
985 ENUMLOGFONTEXW const * pLogFont
986 = reinterpret_cast<ENUMLOGFONTEXW const *>(lpelfe);
987 NEWTEXTMETRICEXW const * pMetric
988 = reinterpret_cast<NEWTEXTMETRICEXW const *>(lpntme);
989 ImplEnumInfo* pInfo = reinterpret_cast<ImplEnumInfo*>(lParam);
990 if ( !pInfo->mpName )
991 {
992 // Ignore vertical fonts
993 if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
994 {
995 OUString aName(o3tl::toU(pLogFont->elfLogFont.lfFaceName));
996 pInfo->mpName = &aName;
997 memcpy(pInfo->mpLogFont->lfFaceName, pLogFont->elfLogFont.lfFaceName, (aName.getLength()+1)*sizeof(wchar_t));
998 pInfo->mpLogFont->lfCharSet = pLogFont->elfLogFont.lfCharSet;
999 EnumFontFamiliesExW(pInfo->mhDC, pInfo->mpLogFont, SalEnumFontsProcExW,
1000 reinterpret_cast<LPARAM>(pInfo), 0);
1001 pInfo->mpLogFont->lfFaceName[0] = '\0';
1002 pInfo->mpLogFont->lfCharSet = DEFAULT_CHARSET;
1003 pInfo->mpName = nullptr;
1004 }
1005 }
1006 else
1007 {
1008 // Ignore non-device fonts on printers.
1009 if (pInfo->mbPrinter)
1010 {
1011 if ((nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE))
1012 {
1013 SAL_INFO("vcl.fonts", "Unsupported printer font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
1014 return 1;
1015 }
1016 }
1017 // Only SFNT fonts are supported, ignore anything else.
1018 else if (!(nFontType & TRUETYPE_FONTTYPE) &&
1019 !(pMetric->ntmTm.ntmFlags & NTM_PS_OPENTYPE) &&
1020 !(pMetric->ntmTm.ntmFlags & NTM_TT_OPENTYPE))
1021 {
1022 SAL_INFO("vcl.fonts", "Unsupported font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
1023 return 1;
1024 }
1025
1026 rtl::Reference<WinFontFace> pData = ImplLogMetricToDevFontDataW(pLogFont, &(pMetric->ntmTm));
1027 pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
1028
1029 pInfo->mpList->Add( pData.get() );
1030 SAL_INFO("vcl.fonts", "SalEnumFontsProcExW: font added: " << pData->GetFamilyName() << " " << pData->GetStyleName());
1031 }
1032
1033 return 1;
1034 }
1035
1036 struct TempFontItem
1037 {
1038 OUString maFontResourcePath;
1039 TempFontItem* mpNextItem;
1040 };
1041
lcl_AddFontResource(SalData & rSalData,const OUString & rFontFileURL,bool bShared)1042 static int lcl_AddFontResource(SalData& rSalData, const OUString& rFontFileURL, bool bShared)
1043 {
1044 OUString aFontSystemPath;
1045 OSL_VERIFY(!osl::FileBase::getSystemPathFromFileURL(rFontFileURL, aFontSystemPath));
1046
1047 int nRet = AddFontResourceExW(o3tl::toW(aFontSystemPath.getStr()), FR_PRIVATE, nullptr);
1048 SAL_WARN_IF(nRet <= 0, "vcl.fonts", "AddFontResourceExW failed for " << rFontFileURL);
1049 if (nRet > 0)
1050 {
1051 TempFontItem* pNewItem = new TempFontItem;
1052 pNewItem->maFontResourcePath = aFontSystemPath;
1053 if (bShared)
1054 {
1055 pNewItem->mpNextItem = rSalData.mpSharedTempFontItem;
1056 rSalData.mpSharedTempFontItem = pNewItem;
1057 }
1058 else
1059 {
1060 pNewItem->mpNextItem = rSalData.mpOtherTempFontItem;
1061 rSalData.mpOtherTempFontItem = pNewItem;
1062 }
1063 }
1064 return nRet;
1065 }
1066
ImplReleaseTempFonts(SalData & rSalData,bool bAll)1067 void ImplReleaseTempFonts(SalData& rSalData, bool bAll)
1068 {
1069 while (TempFontItem* p = rSalData.mpOtherTempFontItem)
1070 {
1071 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
1072 rSalData.mpOtherTempFontItem = p->mpNextItem;
1073 delete p;
1074 }
1075
1076 if (!bAll)
1077 return;
1078
1079 while (TempFontItem* p = rSalData.mpSharedTempFontItem)
1080 {
1081 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
1082 rSalData.mpSharedTempFontItem = p->mpNextItem;
1083 delete p;
1084 }
1085 }
1086
lcl_GetFontFamilyName(const OUString & rFontFileURL)1087 static OUString lcl_GetFontFamilyName(const OUString& rFontFileURL)
1088 {
1089 // Create temporary file name
1090 OUString aTempFileURL;
1091 if (osl::File::E_None != osl::File::createTempFile(nullptr, nullptr, &aTempFileURL))
1092 return OUString();
1093 osl::File::remove(aTempFileURL);
1094 OUString aResSystemPath;
1095 osl::FileBase::getSystemPathFromFileURL(aTempFileURL, aResSystemPath);
1096
1097 // Create font resource file (.fot)
1098 // There is a limit of 127 characters for the full path passed via lpszFile, so we have to
1099 // split the font URL and pass it as two parameters. As a result we can't use
1100 // CreateScalableFontResource for renaming, as it now expects the font in the system path.
1101 // But it's still good to use it for family name extraction, we're currently after.
1102 // BTW: it doesn't help to prefix the lpszFile with \\?\ to support larger paths.
1103 // TODO: use TTLoadEmbeddedFont (needs an EOT as input, so we have to add a header to the TTF)
1104 // TODO: forward the EOT from the AddTempDevFont call side, if VCL supports it
1105 INetURLObject aTTFUrl(rFontFileURL);
1106 // GetBase() strips the extension
1107 OUString aFilename = aTTFUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
1108 if (!CreateScalableFontResourceW(0, o3tl::toW(aResSystemPath.getStr()),
1109 o3tl::toW(aFilename.getStr()), o3tl::toW(aTTFUrl.GetPath().getStr())))
1110 {
1111 sal_uInt32 nError = GetLastError();
1112 SAL_WARN("vcl.fonts", "CreateScalableFontResource failed for " << aResSystemPath << " "
1113 << aFilename << " " << aTTFUrl.GetPath() << " " << nError);
1114 return OUString();
1115 }
1116
1117 // Open and read the font resource file
1118 osl::File aFotFile(aTempFileURL);
1119 if (osl::FileBase::E_None != aFotFile.open(osl_File_OpenFlag_Read))
1120 return OUString();
1121
1122 sal_uInt64 nBytesRead = 0;
1123 char aBuffer[4096];
1124 aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead );
1125 // clean up temporary resource file
1126 aFotFile.close();
1127 osl::File::remove(aTempFileURL);
1128
1129 // retrieve font family name from byte offset 0x4F6
1130 static const sal_uInt64 nNameOfs = 0x4F6;
1131 sal_uInt64 nPos = nNameOfs;
1132 for (; (nPos < nBytesRead) && (aBuffer[nPos] != 0); nPos++);
1133 if (nPos >= nBytesRead || (nPos == nNameOfs))
1134 return OUString();
1135
1136 return OUString(aBuffer + nNameOfs, nPos - nNameOfs, osl_getThreadTextEncoding());
1137 }
1138
AddTempDevFont(PhysicalFontCollection * pFontCollection,const OUString & rFontFileURL,const OUString & rFontName)1139 bool WinSalGraphics::AddTempDevFont(PhysicalFontCollection* pFontCollection,
1140 const OUString& rFontFileURL, const OUString& rFontName)
1141 {
1142 OUString aFontFamily = lcl_GetFontFamilyName(rFontFileURL);
1143 if (aFontFamily.isEmpty())
1144 {
1145 SAL_WARN("vcl.fonts", "error extracting font family from " << rFontFileURL);
1146 return false;
1147 }
1148
1149 if (rFontName != aFontFamily)
1150 {
1151 SAL_WARN("vcl.fonts", "font family renaming not implemented; skipping embedded " << rFontName);
1152 return false;
1153 }
1154
1155 int nFonts = lcl_AddFontResource(*GetSalData(), rFontFileURL, false);
1156 if (nFonts <= 0)
1157 return false;
1158
1159 ImplEnumInfo aInfo;
1160 aInfo.mhDC = getHDC();
1161 aInfo.mpList = pFontCollection;
1162 aInfo.mpName = &aFontFamily;
1163 aInfo.mbPrinter = mbPrinter;
1164 aInfo.mnFontCount = pFontCollection->Count();
1165 const int nExpectedFontCount = aInfo.mnFontCount + nFonts;
1166
1167 LOGFONTW aLogFont = {};
1168 aLogFont.lfCharSet = DEFAULT_CHARSET;
1169 aInfo.mpLogFont = &aLogFont;
1170
1171 // add the font to the PhysicalFontCollection
1172 EnumFontFamiliesExW(getHDC(), &aLogFont,
1173 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0);
1174
1175 SAL_WARN_IF(nExpectedFontCount != pFontCollection->Count(), "vcl.fonts",
1176 "temp font was registered but is not in enumeration: " << rFontFileURL);
1177
1178 return true;
1179 }
1180
GetDevFontList(PhysicalFontCollection * pFontCollection)1181 void WinSalGraphics::GetDevFontList( PhysicalFontCollection* pFontCollection )
1182 {
1183 // make sure all LO shared fonts are registered temporarily
1184 static std::once_flag init;
1185 std::call_once(init, []()
1186 {
1187 auto registerFontsIn = [](const OUString& dir) {
1188 // collect fonts in font path that could not be registered
1189 osl::Directory aFontDir(dir);
1190 osl::FileBase::RC rcOSL = aFontDir.open();
1191 if (rcOSL == osl::FileBase::E_None)
1192 {
1193 osl::DirectoryItem aDirItem;
1194 SalData* pSalData = GetSalData();
1195 assert(pSalData);
1196
1197 while (aFontDir.getNextItem(aDirItem, 10) == osl::FileBase::E_None)
1198 {
1199 osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
1200 rcOSL = aDirItem.getFileStatus(aFileStatus);
1201 if (rcOSL == osl::FileBase::E_None)
1202 lcl_AddFontResource(*pSalData, aFileStatus.getFileURL(), true);
1203 }
1204 }
1205 };
1206
1207 // determine font path
1208 // since we are only interested in fonts that could not be
1209 // registered before because of missing administration rights
1210 // only the font path of the user installation is needed
1211 OUString aPath("$BRAND_BASE_DIR");
1212 rtl_bootstrap_expandMacros(&aPath.pData);
1213
1214 // internal font resources, required for normal operation, like OpenSymbol
1215 registerFontsIn(aPath + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts");
1216
1217 // collect fonts in font path that could not be registered
1218 registerFontsIn(aPath + "/" LIBO_SHARE_FOLDER "/fonts/truetype");
1219
1220 return true;
1221 });
1222
1223 ImplEnumInfo aInfo;
1224 aInfo.mhDC = getHDC();
1225 aInfo.mpList = pFontCollection;
1226 aInfo.mpName = nullptr;
1227 aInfo.mbPrinter = mbPrinter;
1228 aInfo.mnFontCount = 0;
1229
1230 LOGFONTW aLogFont = {};
1231 aLogFont.lfCharSet = DEFAULT_CHARSET;
1232 aInfo.mpLogFont = &aLogFont;
1233
1234 // fill the PhysicalFontCollection
1235 EnumFontFamiliesExW( getHDC(), &aLogFont,
1236 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0 );
1237
1238 // set glyph fallback hook
1239 static WinGlyphFallbackSubstititution aSubstFallback;
1240 static WinPreMatchFontSubstititution aPreMatchFont;
1241 pFontCollection->SetFallbackHook( &aSubstFallback );
1242 pFontCollection->SetPreMatchHook(&aPreMatchFont);
1243 }
1244
ClearDevFontCache()1245 void WinSalGraphics::ClearDevFontCache()
1246 {
1247 WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(GetImpl());
1248 assert(pImpl != nullptr);
1249 pImpl->ClearDevFontCache();
1250 ImplReleaseTempFonts(*GetSalData(), false);
1251 }
1252
ImplGetGlyphBoundRect(sal_GlyphId nId,tools::Rectangle & rRect,bool bIsVertical) const1253 bool WinFontInstance::ImplGetGlyphBoundRect(sal_GlyphId nId, tools::Rectangle& rRect, bool bIsVertical) const
1254 {
1255 assert(m_pGraphics);
1256 HDC hDC = m_pGraphics->getHDC();
1257 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1258 const HFONT hFont = GetHFONT();
1259 if (hFont != hOrigFont)
1260 SelectObject(hDC, hFont);
1261
1262 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1263 { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1264 const float fFontScale = GetScale();
1265
1266 // use unity matrix
1267 MAT2 aMat;
1268 const FontSelectPattern& rFSD = GetFontSelectPattern();
1269
1270 // Use identity matrix for fonts requested in horizontal
1271 // writing (LTR or RTL), or rotated glyphs in vertical writing.
1272 if (!rFSD.mbVertical || !bIsVertical)
1273 {
1274 aMat.eM11 = aMat.eM22 = FixedFromDouble(1.0);
1275 aMat.eM12 = aMat.eM21 = FixedFromDouble(0.0);
1276 }
1277 else
1278 {
1279 constexpr double nCos = 0.0;
1280 constexpr double nSin = 1.0;
1281 aMat.eM11 = FixedFromDouble(nCos);
1282 aMat.eM12 = FixedFromDouble(nSin);
1283 aMat.eM21 = FixedFromDouble(-nSin);
1284 aMat.eM22 = FixedFromDouble(nCos);
1285 }
1286
1287 UINT nGGOFlags = GGO_METRICS;
1288 nGGOFlags |= GGO_GLYPH_INDEX;
1289
1290 GLYPHMETRICS aGM;
1291 aGM.gmptGlyphOrigin.x = aGM.gmptGlyphOrigin.y = 0;
1292 aGM.gmBlackBoxX = aGM.gmBlackBoxY = 0;
1293 DWORD nSize = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGM, 0, nullptr, &aMat);
1294 if (nSize == GDI_ERROR)
1295 return false;
1296
1297 rRect = tools::Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
1298 Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
1299 rRect.SetLeft(static_cast<int>( fFontScale * rRect.Left() ));
1300 rRect.SetRight(static_cast<int>( fFontScale * rRect.Right() ) + 1);
1301 rRect.SetTop(static_cast<int>( fFontScale * rRect.Top() ));
1302 rRect.SetBottom(static_cast<int>( fFontScale * rRect.Bottom() ) + 1);
1303 return true;
1304 }
1305
GetGlyphOutline(sal_GlyphId nId,basegfx::B2DPolyPolygon & rB2DPolyPoly,bool) const1306 bool WinFontInstance::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rB2DPolyPoly, bool) const
1307 {
1308 rB2DPolyPoly.clear();
1309
1310 assert(m_pGraphics);
1311 HDC hDC = m_pGraphics->getHDC();
1312 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1313 const HFONT hFont = GetHFONT();
1314 if (hFont != hOrigFont)
1315 SelectObject(hDC, hFont);
1316
1317 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1318 { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1319
1320 // use unity matrix
1321 MAT2 aMat;
1322 aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1323 aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1324
1325 UINT nGGOFlags = GGO_NATIVE;
1326 nGGOFlags |= GGO_GLYPH_INDEX;
1327
1328 GLYPHMETRICS aGlyphMetrics;
1329 const DWORD nSize1 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGlyphMetrics, 0, nullptr, &aMat);
1330 if( !nSize1 ) // blank glyphs are ok
1331 return true;
1332 else if( nSize1 == GDI_ERROR )
1333 return false;
1334
1335 BYTE* pData = new BYTE[ nSize1 ];
1336 const DWORD nSize2 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags,
1337 &aGlyphMetrics, nSize1, pData, &aMat );
1338
1339 if( nSize1 != nSize2 )
1340 return false;
1341
1342 // TODO: avoid tools polygon by creating B2DPolygon directly
1343 int nPtSize = 512;
1344 Point* pPoints = new Point[ nPtSize ];
1345 PolyFlags* pFlags = new PolyFlags[ nPtSize ];
1346
1347 TTPOLYGONHEADER* pHeader = reinterpret_cast<TTPOLYGONHEADER*>(pData);
1348 while( reinterpret_cast<BYTE*>(pHeader) < pData+nSize2 )
1349 {
1350 // only outline data is interesting
1351 if( pHeader->dwType != TT_POLYGON_TYPE )
1352 break;
1353
1354 // get start point; next start points are end points
1355 // of previous segment
1356 sal_uInt16 nPnt = 0;
1357
1358 tools::Long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
1359 tools::Long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
1360 pPoints[ nPnt ] = Point( nX, nY );
1361 pFlags[ nPnt++ ] = PolyFlags::Normal;
1362
1363 bool bHasOfflinePoints = false;
1364 TTPOLYCURVE* pCurve = reinterpret_cast<TTPOLYCURVE*>( pHeader + 1 );
1365 pHeader = reinterpret_cast<TTPOLYGONHEADER*>( reinterpret_cast<BYTE*>(pHeader) + pHeader->cb );
1366 while( reinterpret_cast<BYTE*>(pCurve) < reinterpret_cast<BYTE*>(pHeader) )
1367 {
1368 int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
1369 if( nPtSize < nNeededSize )
1370 {
1371 Point* pOldPoints = pPoints;
1372 PolyFlags* pOldFlags = pFlags;
1373 nPtSize = 2 * nNeededSize;
1374 pPoints = new Point[ nPtSize ];
1375 pFlags = new PolyFlags[ nPtSize ];
1376 for( sal_uInt16 i = 0; i < nPnt; ++i )
1377 {
1378 pPoints[ i ] = pOldPoints[ i ];
1379 pFlags[ i ] = pOldFlags[ i ];
1380 }
1381 delete[] pOldPoints;
1382 delete[] pOldFlags;
1383 }
1384
1385 int i = 0;
1386 if( TT_PRIM_LINE == pCurve->wType )
1387 {
1388 while( i < pCurve->cpfx )
1389 {
1390 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1391 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1392 ++i;
1393 pPoints[ nPnt ] = Point( nX, nY );
1394 pFlags[ nPnt ] = PolyFlags::Normal;
1395 ++nPnt;
1396 }
1397 }
1398 else if( TT_PRIM_QSPLINE == pCurve->wType )
1399 {
1400 bHasOfflinePoints = true;
1401 while( i < pCurve->cpfx )
1402 {
1403 // get control point of quadratic bezier spline
1404 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1405 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1406 ++i;
1407 Point aControlP( nX, nY );
1408
1409 // calculate first cubic control point
1410 // P0 = 1/3 * (PBeg + 2 * PQControl)
1411 nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
1412 nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
1413 pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1414 pFlags[ nPnt+0 ] = PolyFlags::Control;
1415
1416 // calculate endpoint of segment
1417 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1418 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1419
1420 if ( i+1 >= pCurve->cpfx )
1421 {
1422 // endpoint is either last point in segment => advance
1423 ++i;
1424 }
1425 else
1426 {
1427 // or endpoint is the middle of two control points
1428 nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
1429 nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
1430 nX = (nX + 1) / 2;
1431 nY = (nY + 1) / 2;
1432 // no need to advance, because the current point
1433 // is the control point in next bezier spline
1434 }
1435
1436 pPoints[ nPnt+2 ] = Point( nX, nY );
1437 pFlags[ nPnt+2 ] = PolyFlags::Normal;
1438
1439 // calculate second cubic control point
1440 // P1 = 1/3 * (PEnd + 2 * PQControl)
1441 nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
1442 nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
1443 pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1444 pFlags[ nPnt+1 ] = PolyFlags::Control;
1445
1446 nPnt += 3;
1447 }
1448 }
1449
1450 // next curve segment
1451 pCurve = reinterpret_cast<TTPOLYCURVE*>(&pCurve->apfx[ i ]);
1452 }
1453
1454 // end point is start point for closed contour
1455 // disabled, because Polygon class closes the contour itself
1456 // pPoints[nPnt++] = pPoints[0];
1457 // #i35928#
1458 // Added again, but add only when not yet closed
1459 if(pPoints[nPnt - 1] != pPoints[0])
1460 {
1461 if( bHasOfflinePoints )
1462 pFlags[nPnt] = pFlags[0];
1463
1464 pPoints[nPnt++] = pPoints[0];
1465 }
1466
1467 // convert y-coordinates W32 -> VCL
1468 for( int i = 0; i < nPnt; ++i )
1469 pPoints[i].setY(-pPoints[i].Y());
1470
1471 // insert into polypolygon
1472 tools::Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : nullptr) );
1473 // convert to B2DPolyPolygon
1474 // TODO: get rid of the intermediate PolyPolygon
1475 rB2DPolyPoly.append( aPoly.getB2DPolygon() );
1476 }
1477
1478 delete[] pPoints;
1479 delete[] pFlags;
1480
1481 delete[] pData;
1482
1483 // rescaling needed for the tools::PolyPolygon conversion
1484 if( rB2DPolyPoly.count() )
1485 {
1486 const double fFactor(GetScale()/256);
1487 rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
1488 }
1489
1490 return true;
1491 }
1492
1493 class ScopedFont
1494 {
1495 public:
1496 explicit ScopedFont(WinSalGraphics & rData);
1497
1498 ~ScopedFont();
1499
1500 private:
1501 WinSalGraphics & m_rData;
1502 HFONT m_hOrigFont;
1503 };
1504
ScopedFont(WinSalGraphics & rData)1505 ScopedFont::ScopedFont(WinSalGraphics & rData): m_rData(rData), m_hOrigFont(nullptr)
1506 {
1507 if (m_rData.mpWinFontEntry[0])
1508 {
1509 m_hOrigFont = m_rData.mpWinFontEntry[0]->GetHFONT();
1510 m_rData.mpWinFontEntry[0]->SetHFONT(nullptr);
1511 }
1512 }
1513
~ScopedFont()1514 ScopedFont::~ScopedFont()
1515 {
1516 if( m_hOrigFont )
1517 {
1518 // restore original font, destroy temporary font
1519 HFONT hTempFont = m_rData.mpWinFontEntry[0]->GetHFONT();
1520 m_rData.mpWinFontEntry[0]->SetHFONT(m_hOrigFont);
1521 SelectObject( m_rData.getHDC(), m_hOrigFont );
1522 DeleteObject( hTempFont );
1523 }
1524 }
1525
1526 namespace {
1527
1528 class ScopedTrueTypeFont
1529 {
1530 public:
ScopedTrueTypeFont()1531 ScopedTrueTypeFont(): m_pFont(nullptr) {}
1532
1533 ~ScopedTrueTypeFont();
1534
1535 SFErrCodes open(void const * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum, const FontCharMapRef xCharMap = nullptr);
1536
get() const1537 TrueTypeFont * get() const { return m_pFont; }
1538
1539 private:
1540 TrueTypeFont * m_pFont;
1541 };
1542
1543 }
1544
~ScopedTrueTypeFont()1545 ScopedTrueTypeFont::~ScopedTrueTypeFont()
1546 {
1547 if (m_pFont != nullptr)
1548 CloseTTFont(m_pFont);
1549 }
1550
open(void const * pBuffer,sal_uInt32 nLen,sal_uInt32 nFaceNum,const FontCharMapRef xCharMap)1551 SFErrCodes ScopedTrueTypeFont::open(void const * pBuffer, sal_uInt32 nLen,
1552 sal_uInt32 nFaceNum, const FontCharMapRef xCharMap)
1553 {
1554 OSL_ENSURE(m_pFont == nullptr, "already open");
1555 return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont, xCharMap);
1556 }
1557
CreateFontSubset(const OUString & rToFile,const PhysicalFontFace * pFont,const sal_GlyphId * pGlyphIds,const sal_uInt8 * pEncoding,sal_Int32 * pGlyphWidths,int nGlyphCount,FontSubsetInfo & rInfo)1558 bool WinSalGraphics::CreateFontSubset( const OUString& rToFile,
1559 const PhysicalFontFace* pFont, const sal_GlyphId* pGlyphIds, const sal_uInt8* pEncoding,
1560 sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
1561 {
1562 // TODO: use more of the central font-subsetting code, move stuff there if needed
1563
1564 // create matching FontSelectPattern
1565 // we need just enough to get to the font file data
1566 // use height=1000 for easier debugging (to match psprint's font units)
1567 FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1568
1569 // TODO: much better solution: move SetFont and restoration of old font to caller
1570 ScopedFont aOldFont(*this);
1571 HFONT hOldFont = nullptr;
1572 ImplDoSetFont(aIFSD, pFont, hOldFont);
1573
1574 WinFontFace const * pWinFontData = static_cast<WinFontFace const *>(pFont);
1575
1576 #if OSL_DEBUG_LEVEL > 1
1577 // get font metrics
1578 TEXTMETRICW aWinMetric;
1579 if( !::GetTextMetricsW( getHDC(), &aWinMetric ) )
1580 return FALSE;
1581
1582 SAL_WARN_IF( (aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "vcl", "cannot subset device font" );
1583 SAL_WARN_IF( !(aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE), "vcl", "can only subset TT font" );
1584 #endif
1585
1586 OUString aSysPath;
1587 if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
1588 return false;
1589 const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
1590 const OString aToFile(OUStringToOString(aSysPath, aThreadEncoding));
1591
1592 // check if the font has a CFF-table
1593 const DWORD nCffTag = CalcTag( "CFF " );
1594 const RawFontData aRawCffData( getHDC(), nCffTag );
1595 if (aRawCffData.get())
1596 {
1597 pWinFontData->UpdateFromHDC( getHDC() );
1598 return SalGraphics::CreateCFFfontSubset(aRawCffData.get(), aRawCffData.size(), aToFile,
1599 pGlyphIds, pEncoding, pGlyphWidths, nGlyphCount,
1600 rInfo);
1601 }
1602
1603 // get raw font file data
1604 const RawFontData xRawFontData( getHDC(), 0 );
1605 if( !xRawFontData.get() )
1606 return false;
1607
1608 // open font file
1609 sal_uInt32 nFaceNum = 0;
1610 if( !*xRawFontData.get() ) // TTC candidate
1611 nFaceNum = ~0U; // indicate "TTC font extracts only"
1612
1613 ScopedTrueTypeFont aSftTTF;
1614 SFErrCodes nRC = aSftTTF.open( xRawFontData.get(), xRawFontData.size(), nFaceNum, pFont->GetFontCharMap());
1615 if( nRC != SFErrCodes::Ok )
1616 return false;
1617
1618 TTGlobalFontInfo aTTInfo;
1619 ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
1620 OUString aPSName = ImplSalGetUniString(aTTInfo.psname);
1621 FillFontSubsetInfo(aTTInfo, aPSName, rInfo);
1622
1623 // write subset into destination file
1624 return SalGraphics::CreateTTFfontSubset(*aSftTTF.get(), aToFile, aIFSD.mbVertical, pGlyphIds,
1625 pEncoding, pGlyphWidths, nGlyphCount);
1626 }
1627
GetEmbedFontData(const PhysicalFontFace * pFont,tools::Long * pDataLen)1628 const void* WinSalGraphics::GetEmbedFontData(const PhysicalFontFace* pFont, tools::Long* pDataLen)
1629 {
1630 // create matching FontSelectPattern
1631 // we need just enough to get to the font file data
1632 FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1633
1634 ScopedFont aOldFont(*this);
1635
1636 HFONT hOldFont = nullptr;
1637 ImplDoSetFont(aIFSD, pFont, hOldFont);
1638
1639 // get the raw font file data
1640 RawFontData aRawFontData( getHDC() );
1641 *pDataLen = aRawFontData.size();
1642 if( !aRawFontData.get() )
1643 return nullptr;
1644
1645 const unsigned char* pData = aRawFontData.steal();
1646 return pData;
1647 }
1648
FreeEmbedFontData(const void * pData,tools::Long)1649 void WinSalGraphics::FreeEmbedFontData( const void* pData, tools::Long /*nLen*/ )
1650 {
1651 delete[] static_cast<char const *>(pData);
1652 }
1653
GetGlyphWidths(const PhysicalFontFace * pFont,bool bVertical,std::vector<sal_Int32> & rWidths,Ucs2UIntMap & rUnicodeEnc)1654 void WinSalGraphics::GetGlyphWidths( const PhysicalFontFace* pFont,
1655 bool bVertical,
1656 std::vector< sal_Int32 >& rWidths,
1657 Ucs2UIntMap& rUnicodeEnc )
1658 {
1659 // create matching FontSelectPattern
1660 // we need just enough to get to the font file data
1661 FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1662
1663 // TODO: much better solution: move SetFont and restoration of old font to caller
1664 ScopedFont aOldFont(*this);
1665
1666 HFONT hOldFont = nullptr;
1667 ImplDoSetFont(aIFSD, pFont, hOldFont);
1668
1669 // get raw font file data
1670 const RawFontData xRawFontData( getHDC() );
1671 if( !xRawFontData.get() )
1672 return;
1673
1674 // open font file
1675 sal_uInt32 nFaceNum = 0;
1676 if( !*xRawFontData.get() ) // TTC candidate
1677 nFaceNum = ~0U; // indicate "TTC font extracts only"
1678
1679 ScopedTrueTypeFont aSftTTF;
1680 SFErrCodes nRC = aSftTTF.open(xRawFontData.get(), xRawFontData.size(), nFaceNum, pFont->GetFontCharMap());
1681 if( nRC != SFErrCodes::Ok )
1682 return;
1683
1684 SalGraphics::GetGlyphWidths(*aSftTTF.get(), *pFont, bVertical, rWidths, rUnicodeEnc);
1685 }
1686
1687 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1688