xref: /core/vcl/win/gdi/salfont.cxx (revision c77a53433d9cfb0f03a3ebd04707ddccb11a3cca)
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/config.h>
21 
22 #include <sal/types.h>
23 #include <config_folders.h>
24 
25 #include <algorithm>
26 #include <map>
27 #include <memory>
28 #include <mutex>
29 #include <set>
30 #include <string.h>
31 #include <string_view>
32 #include <svsys.h>
33 #include <vector>
34 
35 #include <dwrite_3.h>
36 #include <o3tl/lru_map.hxx>
37 #include <basegfx/matrix/b2dhommatrixtools.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <i18nlangtag/mslangid.hxx>
40 #include <osl/diagnose.h>
41 #include <osl/file.hxx>
42 #include <osl/process.h>
43 #include <rtl/bootstrap.hxx>
44 #include <rtl/tencinfo.h>
45 #include <sal/log.hxx>
46 #include <o3tl/char16_t2wchar_t.hxx>
47 #include <tools/helpers.hxx>
48 #include <tools/stream.hxx>
49 #include <tools/urlobj.hxx>
50 #include <unotools/fontcfg.hxx>
51 #include <vcl/settings.hxx>
52 #include <vcl/sysdata.hxx>
53 #include <vcl/metric.hxx>
54 #include <vcl/fontcharmap.hxx>
55 #include <comphelper/scopeguard.hxx>
56 #include <comphelper/windowserrorstring.hxx>
57 
58 #include <font/FontSelectPattern.hxx>
59 #include <font/PhysicalFontCollection.hxx>
60 #include <font/PhysicalFontFaceCollection.hxx>
61 #include <font/PhysicalFontFace.hxx>
62 #include <font/fontsubstitution.hxx>
63 #include <sft.hxx>
64 #include <win/saldata.hxx>
65 #include <win/salgdi.h>
66 #include <win/winlayout.hxx>
67 #include <win/wingdiimpl.hxx>
68 #include <impfontcharmap.hxx>
69 #include <font/FontMetricData.hxx>
70 #include <impglyphitem.hxx>
71 
72 #include <vcl/skia/SkiaHelper.hxx>
73 #include <skia/win/font.hxx>
74 
75 using namespace vcl;
76 
FixedFromDouble(double d)77 static FIXED FixedFromDouble( double d )
78 {
79     const tools::Long l = static_cast<tools::Long>( d * 65536. );
80     return *reinterpret_cast<FIXED const *>(&l);
81 }
82 
IntTimes256FromFixed(FIXED f)83 static int IntTimes256FromFixed(FIXED f)
84 {
85     int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
86     return nFixedTimes256;
87 }
88 
89 // platform specific font substitution hooks for glyph fallback enhancement
90 
91 namespace {
92 
93 class WinPreMatchFontSubstititution
94 :    public vcl::font::PreMatchFontSubstitution
95 {
96 public:
97     bool FindFontSubstitute(vcl::font::FontSelectPattern&) const override;
98 };
99 
100 class WinGlyphFallbackSubstititution
101 :    public vcl::font::GlyphFallbackFontSubstitution
102 {
103 public:
104     bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
105 };
106 
107 // does a font face hold the given missing characters?
HasMissingChars(vcl::font::PhysicalFontFace * pFace,OUString & rMissingChars)108 bool HasMissingChars(vcl::font::PhysicalFontFace* pFace, OUString& rMissingChars)
109 {
110     FontCharMapRef xFontCharMap = pFace->GetFontCharMap();
111 
112     // avoid fonts with unknown CMAP subtables for glyph fallback
113     if( !xFontCharMap.is() || xFontCharMap->IsDefaultMap() )
114         return false;
115 
116     int nMatchCount = 0;
117     std::vector<sal_UCS4> rRemainingCodes;
118     const sal_Int32 nStrLen = rMissingChars.getLength();
119     sal_Int32 nStrIdx = 0;
120     while (nStrIdx < nStrLen)
121     {
122         const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
123         if (xFontCharMap->HasChar(uChar))
124             nMatchCount++;
125         else
126             rRemainingCodes.push_back(uChar);
127     }
128 
129     xFontCharMap = nullptr;
130 
131     if (nMatchCount > 0)
132         rMissingChars = OUString(rRemainingCodes.data(), rRemainingCodes.size());
133 
134     return nMatchCount > 0;
135 }
136 
137     //used by 2-level font fallback
findDevFontListByLocale(const vcl::font::PhysicalFontCollection & rFontCollection,const LanguageTag & rLanguageTag)138     vcl::font::PhysicalFontFamily* findDevFontListByLocale(const vcl::font::PhysicalFontCollection &rFontCollection,
139                                                 const LanguageTag& rLanguageTag )
140     {
141         // get the default font for a specified locale
142         const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
143         const OUString aDefault = rDefaults.getUserInterfaceFont(rLanguageTag);
144         return rFontCollection.FindFontFamilyByTokenNames(aDefault);
145     }
146 }
147 
148 // These are Win 3.1 bitmap fonts using "FON" font format
149 // which is not supported with DirectWrite so let's substitute them
150 // with a font that is supported and always available.
151 // Based on:
152 // https://dxr.mozilla.org/mozilla-esr10/source/gfx/thebes/gfxDWriteFontList.cpp#1057
153 const std::map<OUString, OUString> aBitmapFontSubs =
154 {
155     { "MS Sans Serif", "Microsoft Sans Serif" },
156     { "MS Serif",      "Times New Roman" },
157     { "Small Fonts",   "Arial" },
158     { "Courier",       "Courier New" },
159     { "Roman",         "Times New Roman" },
160     { "Script",        "Mistral" }
161 };
162 
163 // TODO: See if Windows have API that we can use here to improve font fallback.
FindFontSubstitute(vcl::font::FontSelectPattern & rFontSelData) const164 bool WinPreMatchFontSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData) const
165 {
166     if (rFontSelData.IsMicrosoftSymbolEncoded() || IsOpenSymbol(rFontSelData.maSearchName))
167         return false;
168 
169     for (const auto& aSub : aBitmapFontSubs)
170     {
171         if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
172         {
173             rFontSelData.maSearchName = aSub.second;
174             return true;
175         }
176     }
177 
178     return false;
179 }
180 
181 // find a fallback font for missing characters
182 // TODO: should stylistic matches be searched and preferred?
FindFontSubstitute(vcl::font::FontSelectPattern & rFontSelData,LogicalFontInstance *,OUString & rMissingChars) const183 bool WinGlyphFallbackSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* /*pLogicalFont*/, OUString& rMissingChars) const
184 {
185     // guess a locale matching to the missing chars
186     LanguageType eLang = rFontSelData.meLanguage;
187     LanguageTag aLanguageTag( eLang);
188 
189     // fall back to default UI locale if the font language is inconclusive
190     if( eLang == LANGUAGE_DONTKNOW )
191         aLanguageTag = Application::GetSettings().GetUILanguageTag();
192 
193     // first level fallback:
194     // try use the locale specific default fonts defined in VCL.xcu
195     const vcl::font::PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
196     vcl::font::PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag);
197     if( pFontFamily )
198     {
199         vcl::font::PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
200         if( HasMissingChars( pFace, rMissingChars ) )
201         {
202             rFontSelData.maSearchName = pFontFamily->GetSearchName();
203             return true;
204         }
205     }
206 
207     // are the missing characters symbols?
208     pFontFamily = pFontCollection->FindFontFamilyByAttributes( ImplFontAttrs::Symbol,
209                                                      rFontSelData.GetWeight(),
210                                                      rFontSelData.GetWidthType(),
211                                                      rFontSelData.GetItalic(),
212                                                      rFontSelData.maSearchName );
213     if( pFontFamily )
214     {
215         vcl::font::PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
216         if( HasMissingChars( pFace, rMissingChars ) )
217         {
218             rFontSelData.maSearchName = pFontFamily->GetSearchName();
219             return true;
220         }
221     }
222 
223     // last level fallback, check each font type face one by one
224     std::unique_ptr<vcl::font::PhysicalFontFaceCollection> pTestFontList = pFontCollection->GetFontFaceCollection();
225     // limit the count of fonts to be checked to prevent hangs
226     static const int MAX_GFBFONT_COUNT = 600;
227     int nTestFontCount = pTestFontList->Count();
228     if( nTestFontCount > MAX_GFBFONT_COUNT )
229         nTestFontCount = MAX_GFBFONT_COUNT;
230 
231     bool bFound = false;
232     for( int i = 0; i < nTestFontCount; ++i )
233     {
234         vcl::font::PhysicalFontFace* pFace = pTestFontList->Get( i );
235         bFound = HasMissingChars( pFace, rMissingChars );
236         if( !bFound )
237             continue;
238         rFontSelData.maSearchName = pFace->GetFamilyName();
239         break;
240     }
241 
242     return bFound;
243 }
244 
245 namespace {
246 
247 struct ImplEnumInfo
248 {
249     HDC                 mhDC;
250     vcl::font::PhysicalFontCollection* mpList;
251     OUString*           mpName;
252     bool                mbPrinter;
253 };
254 
255 }
256 
ImplCharSetToSal(BYTE nCharSet)257 static rtl_TextEncoding ImplCharSetToSal( BYTE nCharSet )
258 {
259     rtl_TextEncoding eTextEncoding;
260 
261     if ( nCharSet == OEM_CHARSET )
262     {
263         UINT nCP = static_cast<sal_uInt16>(GetOEMCP());
264         switch ( nCP )
265         {
266             // It is unclear why these two (undefined?) code page numbers are
267             // handled specially here. They are mentioned briefly in
268             // https://github.com/osfree-project/osfree/blob/165b89a685030c2b875aa0c7c5db420afc8db5f6/docs/dos/dos.txt
269             case 1004:  eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
270             case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
271             default:
272                 eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
273                 break;
274         }
275     }
276     else
277     {
278         if( nCharSet )
279             eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
280         else
281             eTextEncoding = RTL_TEXTENCODING_UNICODE;
282     }
283 
284     return eTextEncoding;
285 }
286 
ImplFamilyToSal(BYTE nFamily)287 static FontFamily ImplFamilyToSal( BYTE nFamily )
288 {
289     switch ( nFamily & 0xF0 )
290     {
291         case FF_DECORATIVE:
292             return FAMILY_DECORATIVE;
293 
294         case FF_MODERN:
295             return FAMILY_MODERN;
296 
297         case FF_ROMAN:
298             return FAMILY_ROMAN;
299 
300         case FF_SCRIPT:
301             return FAMILY_SCRIPT;
302 
303         case FF_SWISS:
304             return FAMILY_SWISS;
305 
306         default:
307             break;
308     }
309 
310     return FAMILY_DONTKNOW;
311 }
312 
ImplFamilyToWin(FontFamily eFamily)313 static BYTE ImplFamilyToWin( FontFamily eFamily )
314 {
315     switch ( eFamily )
316     {
317         case FAMILY_DECORATIVE:
318             return FF_DECORATIVE;
319 
320         case FAMILY_MODERN:
321             return FF_MODERN;
322 
323         case FAMILY_ROMAN:
324             return FF_ROMAN;
325 
326         case FAMILY_SCRIPT:
327             return FF_SCRIPT;
328 
329         case FAMILY_SWISS:
330             return FF_SWISS;
331 
332         case FAMILY_SYSTEM:
333             return FF_SWISS;
334 
335         default:
336             break;
337     }
338 
339     return FF_DONTCARE;
340 }
341 
ImplWeightToSal(int nWeight)342 static FontWeight ImplWeightToSal( int nWeight )
343 {
344     if ( nWeight <= FW_THIN )
345         return WEIGHT_THIN;
346     else if ( nWeight <= FW_ULTRALIGHT )
347         return WEIGHT_ULTRALIGHT;
348     else if ( nWeight <= FW_LIGHT )
349         return WEIGHT_LIGHT;
350     else if ( nWeight < FW_MEDIUM )
351         return WEIGHT_NORMAL;
352     else if ( nWeight == FW_MEDIUM )
353         return WEIGHT_MEDIUM;
354     else if ( nWeight <= FW_SEMIBOLD )
355         return WEIGHT_SEMIBOLD;
356     else if ( nWeight <= FW_BOLD )
357         return WEIGHT_BOLD;
358     else if ( nWeight <= FW_ULTRABOLD )
359         return WEIGHT_ULTRABOLD;
360     else
361         return WEIGHT_BLACK;
362 }
363 
ImplWeightToWin(FontWeight eWeight)364 static int ImplWeightToWin( FontWeight eWeight )
365 {
366     switch ( eWeight )
367     {
368         case WEIGHT_THIN:
369             return FW_THIN;
370 
371         case WEIGHT_ULTRALIGHT:
372             return FW_ULTRALIGHT;
373 
374         case WEIGHT_LIGHT:
375             return FW_LIGHT;
376 
377         case WEIGHT_SEMILIGHT:
378         case WEIGHT_NORMAL:
379             return FW_NORMAL;
380 
381         case WEIGHT_MEDIUM:
382             return FW_MEDIUM;
383 
384         case WEIGHT_SEMIBOLD:
385             return FW_SEMIBOLD;
386 
387         case WEIGHT_BOLD:
388             return FW_BOLD;
389 
390         case WEIGHT_ULTRABOLD:
391             return FW_ULTRABOLD;
392 
393         case WEIGHT_BLACK:
394             return FW_BLACK;
395 
396         default:
397             break;
398     }
399 
400     return 0;
401 }
402 
ImplLogPitchToSal(BYTE nPitch)403 static FontPitch ImplLogPitchToSal( BYTE nPitch )
404 {
405     if ( nPitch & FIXED_PITCH )
406         return PITCH_FIXED;
407     else
408         return PITCH_VARIABLE;
409 }
410 
ImplMetricPitchToSal(BYTE nPitch)411 static FontPitch ImplMetricPitchToSal( BYTE nPitch )
412 {
413     // Grrrr! See NT help
414     if ( !(nPitch & TMPF_FIXED_PITCH) )
415         return PITCH_FIXED;
416     else
417         return PITCH_VARIABLE;
418 }
419 
ImplPitchToWin(FontPitch ePitch)420 static BYTE ImplPitchToWin( FontPitch ePitch )
421 {
422     if ( ePitch == PITCH_FIXED )
423         return FIXED_PITCH;
424     else if ( ePitch == PITCH_VARIABLE )
425         return VARIABLE_PITCH;
426     else
427         return DEFAULT_PITCH;
428 }
429 
WinFont2DevFontAttributes(const ENUMLOGFONTEXW & rEnumFont,const NEWTEXTMETRICW & rMetric)430 static FontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
431     const NEWTEXTMETRICW& rMetric)
432 {
433     FontAttributes aDFA;
434 
435     const LOGFONTW rLogFont = rEnumFont.elfLogFont;
436 
437     // get font face attributes
438     aDFA.SetFamilyType(ImplFamilyToSal( rLogFont.lfPitchAndFamily ));
439     aDFA.SetWidthType(WIDTH_DONTKNOW);
440     aDFA.SetWeight(ImplWeightToSal( rLogFont.lfWeight ));
441     aDFA.SetItalic((rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE);
442     aDFA.SetPitch(ImplLogPitchToSal( rLogFont.lfPitchAndFamily ));
443     aDFA.SetMicrosoftSymbolEncoded(rLogFont.lfCharSet == SYMBOL_CHARSET);
444 
445     // get the font face name
446     aDFA.SetFamilyName(OUString(o3tl::toU(rLogFont.lfFaceName)));
447 
448     // use the face's style name only if it looks reasonable
449     const wchar_t* pStyleName = rEnumFont.elfStyle;
450     const wchar_t* pEnd = std::end(rEnumFont.elfStyle);
451     const wchar_t* p = pStyleName;
452     for(; *p && (p < pEnd); ++p )
453         if( *p < 0x0020 )
454             break;
455     if( p < pEnd )
456         aDFA.SetStyleName(OUString(o3tl::toU(pStyleName)));
457 
458     // heuristics for font quality
459     // -   opentypeTT > truetype
460     aDFA.SetQuality( 0 );
461     if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
462         aDFA.IncreaseQualityBy( 50 );
463     if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
464         aDFA.IncreaseQualityBy( 10 );
465 
466     // TODO: add alias names
467     return aDFA;
468 }
469 
ImplSalLogFontToFontW(HDC hDC,const LOGFONTW & rLogFont,Font & rFont)470 void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
471 {
472     OUString aFontName( o3tl::toU(rLogFont.lfFaceName) );
473     if (aFontName.isEmpty())
474         return;
475 
476     rFont.SetFamilyName( aFontName );
477     rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
478     rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
479     rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
480     rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
481 
482     tools::Long nFontHeight = rLogFont.lfHeight;
483     if ( nFontHeight < 0 )
484         nFontHeight = -nFontHeight;
485     tools::Long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
486     if( !nDPIY )
487         nDPIY = 600;
488     nFontHeight *= 72;
489     nFontHeight += nDPIY/2;
490     nFontHeight /= nDPIY;
491     rFont.SetFontSize( Size( 0, nFontHeight ) );
492     rFont.SetOrientation( Degree10(static_cast<sal_Int16>(rLogFont.lfEscapement)) );
493     if ( rLogFont.lfItalic )
494         rFont.SetItalic( ITALIC_NORMAL );
495     else
496         rFont.SetItalic( ITALIC_NONE );
497     if ( rLogFont.lfUnderline )
498         rFont.SetUnderline( LINESTYLE_SINGLE );
499     else
500         rFont.SetUnderline( LINESTYLE_NONE );
501     if ( rLogFont.lfStrikeOut )
502         rFont.SetStrikeout( STRIKEOUT_SINGLE );
503     else
504         rFont.SetStrikeout( STRIKEOUT_NONE );
505 }
506 
getNextFontId()507 static sal_IntPtr getNextFontId()
508 {
509     static sal_IntPtr id;
510     return ++id;
511 }
512 
WinFontFace(const ENUMLOGFONTEXW & rEnumFont,const NEWTEXTMETRICW & rMetric)513 WinFontFace::WinFontFace(const ENUMLOGFONTEXW& rEnumFont, const NEWTEXTMETRICW& rMetric)
514 :   vcl::font::PhysicalFontFace(WinFont2DevFontAttributes(rEnumFont, rMetric)),
515     mnId(getNextFontId()),
516     meWinCharSet(rEnumFont.elfLogFont.lfCharSet),
517     mnPitchAndFamily(rMetric.tmPitchAndFamily),
518     maLogFont(rEnumFont.elfLogFont)
519 {
520 }
521 
~WinFontFace()522 WinFontFace::~WinFontFace()
523 {
524 }
525 
GetFontId() const526 sal_IntPtr WinFontFace::GetFontId() const
527 {
528     return mnId;
529 }
530 
CreateFontInstance(const vcl::font::FontSelectPattern & rFSD) const531 rtl::Reference<LogicalFontInstance> WinFontFace::CreateFontInstance(const vcl::font::FontSelectPattern& rFSD) const
532 {
533     assert(SkiaHelper::isVCLSkiaEnabled() && "Windows requires skia");
534     return new SkiaWinFontInstance(*this, rFSD);
535 }
536 
537 const std::vector<hb_variation_t>&
GetVariations(const LogicalFontInstance & rFont) const538 WinFontFace::GetVariations(const LogicalFontInstance& rFont) const
539 {
540     if (!mxVariations)
541     {
542         mxVariations.emplace();
543         auto pDWFontFace = static_cast<const WinFontInstance&>(rFont).GetDWFontFace();
544         if (pDWFontFace)
545         {
546             sal::systools::COMReference<IDWriteFontFace5> xDWFontFace5;
547             auto hr = pDWFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
548                                                   reinterpret_cast<void**>(&xDWFontFace5));
549             if (SUCCEEDED(hr) && xDWFontFace5->HasVariations())
550             {
551                 std::vector<DWRITE_FONT_AXIS_VALUE> aAxisValues(
552                     xDWFontFace5->GetFontAxisValueCount());
553                 hr = xDWFontFace5->GetFontAxisValues(aAxisValues.data(), aAxisValues.size());
554                 if (SUCCEEDED(hr))
555                 {
556                     mxVariations->reserve(aAxisValues.size());
557                     for (auto& rAxisValue : aAxisValues)
558                         mxVariations->push_back(
559                             { OSL_NETDWORD(rAxisValue.axisTag), rAxisValue.value });
560                 }
561             }
562         }
563     }
564 
565     return *mxVariations;
566 }
567 
568 namespace
569 {
570 struct BlobReference
571 {
572     hb_blob_t* mpBlob;
BlobReference__anon20ff48010311::BlobReference573     BlobReference(hb_blob_t* pBlob)
574         : mpBlob(pBlob)
575     {
576         hb_blob_reference(mpBlob);
577     }
BlobReference__anon20ff48010311::BlobReference578     BlobReference(BlobReference&& other) noexcept
579         : mpBlob(other.mpBlob)
580     {
581         other.mpBlob = nullptr;
582     }
operator =__anon20ff48010311::BlobReference583     BlobReference& operator=(BlobReference&& other)
584     {
585         std::swap(mpBlob, other.mpBlob);
586         return *this;
587     }
588     BlobReference(const BlobReference& other) = delete;
589     BlobReference& operator=(BlobReference& other) = delete;
~BlobReference__anon20ff48010311::BlobReference590     ~BlobReference() { hb_blob_destroy(mpBlob); }
591 };
592 }
593 
594 using BlobCacheKey = std::pair<sal_IntPtr, hb_tag_t>;
595 
596 namespace
597 {
598 struct BlobCacheKeyHash
599 {
operator ()__anon20ff48010411::BlobCacheKeyHash600     std::size_t operator()(BlobCacheKey const& rKey) const
601     {
602         std::size_t seed = 0;
603         o3tl::hash_combine(seed, rKey.first);
604         o3tl::hash_combine(seed, rKey.second);
605         return seed;
606     }
607 };
608 }
609 
GetHbTable(hb_tag_t nTag) const610 hb_blob_t* WinFontFace::GetHbTable(hb_tag_t nTag) const
611 {
612     static o3tl::lru_map<BlobCacheKey, BlobReference, BlobCacheKeyHash> gCache(50);
613     BlobCacheKey aCacheKey{ GetFontId(), nTag };
614     auto it = gCache.find(aCacheKey);
615     if (it != gCache.end())
616     {
617         hb_blob_reference(it->second.mpBlob);
618         return it->second.mpBlob;
619     }
620 
621     sal_uLong nLength = 0;
622     unsigned char* pBuffer = nullptr;
623 
624     HDC hDC(::GetDC(nullptr));
625     HFONT hFont = ::CreateFontIndirectW(&maLogFont);
626     HFONT hOldFont = ::SelectFont(hDC, hFont);
627 
628     nLength = ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, nullptr, 0);
629     if (nLength > 0 && nLength != GDI_ERROR)
630     {
631         pBuffer = new unsigned char[nLength];
632         ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, pBuffer, nLength);
633     }
634 
635     ::SelectFont(hDC, hOldFont);
636     ::DeleteFont(hFont);
637     ::ReleaseDC(nullptr, hDC);
638 
639     hb_blob_t* pBlob = nullptr;
640 
641     if (pBuffer)
642         pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY,
643                                pBuffer, [](void* data) { delete[] static_cast<unsigned char*>(data); });
644 
645     gCache.insert({ aCacheKey, BlobReference(pBlob) });
646     return pBlob;
647 }
648 
SetTextColor(Color nColor)649 void WinSalGraphics::SetTextColor( Color nColor )
650 {
651     COLORREF aCol = RGB( nColor.GetRed(),
652                          nColor.GetGreen(),
653                          nColor.GetBlue() );
654 
655     ::SetTextColor( getHDC(), aCol );
656 }
657 
SalEnumQueryFontProcExW(const LOGFONTW *,const TEXTMETRICW *,DWORD,LPARAM lParam)658 static int CALLBACK SalEnumQueryFontProcExW( const LOGFONTW*, const TEXTMETRICW*, DWORD, LPARAM lParam )
659 {
660     *reinterpret_cast<bool*>(lParam) = true;
661     return 0;
662 }
663 
ImplGetLogFontFromFontSelect(const vcl::font::FontSelectPattern & rFont,const vcl::font::PhysicalFontFace * pFontFace,LOGFONTW & rLogFont,bool bAntiAliased)664 void ImplGetLogFontFromFontSelect( const vcl::font::FontSelectPattern& rFont,
665                                    const vcl::font::PhysicalFontFace* pFontFace,
666                                    LOGFONTW& rLogFont, bool bAntiAliased)
667 {
668     OUString aName;
669     if (pFontFace)
670         aName = pFontFace->GetFamilyName();
671     else
672         aName = rFont.GetFamilyName().getToken( 0, ';' );
673 
674     UINT nNameLen = aName.getLength();
675     if (nNameLen >= LF_FACESIZE)
676         nNameLen = LF_FACESIZE - 1;
677     memcpy( rLogFont.lfFaceName, aName.getStr(), nNameLen*sizeof( wchar_t ) );
678     rLogFont.lfFaceName[nNameLen] = 0;
679 
680     if  (pFontFace)
681     {
682         const WinFontFace* pWinFontData = static_cast<const WinFontFace*>(pFontFace);
683         rLogFont.lfCharSet = pWinFontData->GetCharSet();
684         rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
685     }
686     else
687     {
688         rLogFont.lfCharSet = rFont.IsMicrosoftSymbolEncoded() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
689         rLogFont.lfPitchAndFamily = ImplPitchToWin( rFont.GetPitch() )
690                                   | ImplFamilyToWin( rFont.GetFamilyType() );
691     }
692 
693     rLogFont.lfWeight          = ImplWeightToWin( rFont.GetWeight() );
694     rLogFont.lfHeight          = static_cast<LONG>(-rFont.mnHeight);
695     rLogFont.lfWidth           = static_cast<LONG>(rFont.mnWidth);
696     rLogFont.lfUnderline       = 0;
697     rLogFont.lfStrikeOut       = 0;
698     rLogFont.lfItalic          = BYTE(rFont.GetItalic() != ITALIC_NONE);
699     rLogFont.lfEscapement      = rFont.mnOrientation.get();
700     rLogFont.lfOrientation     = rLogFont.lfEscapement;
701     rLogFont.lfClipPrecision   = CLIP_DEFAULT_PRECIS;
702     rLogFont.lfOutPrecision    = OUT_TT_PRECIS;
703     if ( rFont.mnOrientation )
704         rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
705 
706     // disable antialiasing if requested
707     if ( rFont.mbNonAntialiased )
708         rLogFont.lfQuality = NONANTIALIASED_QUALITY;
709     else if (Application::GetSettings().GetStyleSettings().GetUseFontAAFromSystem())
710         rLogFont.lfQuality = DEFAULT_QUALITY; // for display on screen
711     else
712         rLogFont.lfQuality = bAntiAliased ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
713 }
714 
715 std::tuple<HFONT, HFONT, sal_Int32>
ImplDoSetFont(HDC hDC,vcl::font::FontSelectPattern const & i_rFont,const vcl::font::PhysicalFontFace * i_pFontFace,HFONT & o_rOldFont)716 WinSalGraphics::ImplDoSetFont(HDC hDC, vcl::font::FontSelectPattern const& i_rFont,
717                               const vcl::font::PhysicalFontFace* i_pFontFace, HFONT& o_rOldFont)
718 {
719     HFONT hNewFont = nullptr;
720 
721     LOGFONTW aLogFont;
722     ImplGetLogFontFromFontSelect( i_rFont, i_pFontFace, aLogFont, getAntiAlias());
723 
724     hNewFont = ::CreateFontIndirectW( &aLogFont );
725     o_rOldFont = ::SelectFont(hDC, hNewFont);
726 
727     TEXTMETRICW aTextMetricW;
728     if (!::GetTextMetricsW(hDC, &aTextMetricW))
729     {
730         // the selected font doesn't work => try a replacement
731         // TODO: use its font fallback instead
732         lstrcpynW( aLogFont.lfFaceName, L"Courier New", 12 );
733         aLogFont.lfPitchAndFamily = FIXED_PITCH;
734         HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
735         SelectFont(hDC, hNewFont2);
736         DeleteFont( hNewFont );
737         hNewFont = hNewFont2;
738     }
739 
740     // Optionally create a secondary font for non-rotated CJK glyphs in vertical context
741     HFONT hNewVerticalFont = nullptr;
742     if (i_rFont.mbVertical && mbPrinter)
743     {
744         aLogFont.lfEscapement = 0;
745         aLogFont.lfOrientation = 0;
746         hNewVerticalFont = ::CreateFontIndirectW(&aLogFont);
747     }
748 
749     return std::make_tuple(hNewFont, hNewVerticalFont,
750                            static_cast<sal_Int32>(aTextMetricW.tmDescent));
751 }
752 
SetFont(LogicalFontInstance * pFont,int nFallbackLevel)753 void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
754 {
755     assert(nFallbackLevel >= 0 && nFallbackLevel < MAX_FALLBACK);
756 
757     // return early if there is no new font
758     if( !pFont )
759     {
760         if (!mpWinFontEntry[nFallbackLevel].is())
761             return;
762 
763         // DeInitGraphics doesn't free the cached fonts, so mhDefFont might be nullptr
764         if (mhDefFont)
765         {
766             ::SelectFont(getHDC(), mhDefFont);
767             mhDefFont = nullptr;
768         }
769 
770         // release no longer referenced font handles
771         for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
772             mpWinFontEntry[i] = nullptr;
773         return;
774     }
775 
776     WinFontInstance *pFontInstance = static_cast<WinFontInstance*>(pFont);
777     mpWinFontEntry[ nFallbackLevel ] = pFontInstance;
778 
779     HFONT hOldFont = nullptr;
780     HFONT hNewFont = pFontInstance->GetHFONT();
781     if (!hNewFont)
782     {
783         pFontInstance->SetGraphics(this);
784         hNewFont = pFontInstance->GetHFONT();
785     }
786     hOldFont = ::SelectFont(getHDC(), hNewFont);
787 
788     // keep default font
789     if( !mhDefFont )
790         mhDefFont = hOldFont;
791     else
792     {
793         // release no longer referenced font handles
794         for( int i = nFallbackLevel + 1; i < MAX_FALLBACK && mpWinFontEntry[i].is(); ++i )
795             mpWinFontEntry[i] = nullptr;
796     }
797 }
798 
GetFontMetric(FontMetricDataRef & rxFontMetric,int nFallbackLevel)799 void WinSalGraphics::GetFontMetric( FontMetricDataRef& rxFontMetric, int nFallbackLevel )
800 {
801     // temporarily change the HDC to the font in the fallback level
802     rtl::Reference<WinFontInstance> pFontInstance = mpWinFontEntry[nFallbackLevel];
803     const HFONT hOldFont = SelectFont(getHDC(), pFontInstance->GetHFONT());
804 
805     wchar_t aFaceName[LF_FACESIZE+60];
806     if( GetTextFaceW( getHDC(), SAL_N_ELEMENTS(aFaceName), aFaceName ) )
807         rxFontMetric->SetFamilyName(OUString(o3tl::toU(aFaceName)));
808 
809     rxFontMetric->SetMinKashida(pFontInstance->GetKashidaWidth());
810     rxFontMetric->ImplCalcLineSpacing(pFontInstance.get());
811     rxFontMetric->ImplInitBaselines(pFontInstance.get());
812 
813     // get the font metric
814     OUTLINETEXTMETRICW aOutlineMetric;
815     const bool bOK = GetOutlineTextMetricsW(getHDC(), sizeof(aOutlineMetric), &aOutlineMetric);
816     // restore the HDC to the font in the base level
817     SelectFont( getHDC(), hOldFont );
818     if( !bOK )
819         return;
820 
821     TEXTMETRICW aWinMetric = aOutlineMetric.otmTextMetrics;
822 
823     // device independent font attributes
824     rxFontMetric->SetFamilyType(ImplFamilyToSal( aWinMetric.tmPitchAndFamily ));
825     rxFontMetric->SetMicrosoftSymbolEncoded(aWinMetric.tmCharSet == SYMBOL_CHARSET);
826     rxFontMetric->SetWeight(ImplWeightToSal( aWinMetric.tmWeight ));
827     rxFontMetric->SetPitch(ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily ));
828     rxFontMetric->SetItalic(aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE);
829     rxFontMetric->SetSlant( 0 );
830 
831     // transformation dependent font metrics
832     rxFontMetric->SetWidth(aWinMetric.tmAveCharWidth);
833 }
834 
GetFontCharMap() const835 FontCharMapRef WinSalGraphics::GetFontCharMap() const
836 {
837     if (!mpWinFontEntry[0])
838     {
839         return FontCharMapRef( new FontCharMap() );
840     }
841     return mpWinFontEntry[0]->GetFontFace()->GetFontCharMap();
842 }
843 
GetFontCapabilities(vcl::FontCapabilities & rFontCapabilities) const844 bool WinSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
845 {
846     if (!mpWinFontEntry[0])
847         return false;
848     return mpWinFontEntry[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
849 }
850 
SalEnumFontsProcExW(const LOGFONTW * lpelfe,const TEXTMETRICW * lpntme,DWORD nFontType,LPARAM lParam)851 static int CALLBACK SalEnumFontsProcExW( const LOGFONTW* lpelfe,
852                                   const TEXTMETRICW* lpntme,
853                                   DWORD nFontType, LPARAM lParam )
854 {
855     ImplEnumInfo* pInfo = reinterpret_cast<ImplEnumInfo*>(lParam);
856     if ( !pInfo->mpName )
857     {
858         // Ignore vertical fonts
859         if (lpelfe->lfFaceName[0] != '@')
860         {
861             OUString aName(o3tl::toU(lpelfe->lfFaceName));
862             pInfo->mpName = &aName;
863             LOGFONTW aLogFont{ .lfCharSet = lpelfe->lfCharSet };
864             std::copy_n(lpelfe->lfFaceName, std::size(lpelfe->lfFaceName), aLogFont.lfFaceName);
865             EnumFontFamiliesExW(pInfo->mhDC, &aLogFont, SalEnumFontsProcExW,
866                                 reinterpret_cast<LPARAM>(pInfo), 0);
867             pInfo->mpName = nullptr;
868         }
869     }
870     else
871     {
872         NEWTEXTMETRICW const* pMetric = reinterpret_cast<NEWTEXTMETRICW const*>(lpntme);
873         // Ignore non-device fonts on printers.
874         if (pInfo->mbPrinter)
875         {
876             if ((nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE))
877             {
878                 SAL_INFO("vcl.fonts", "Unsupported printer font ignored: " << OUString(o3tl::toU(lpelfe->lfFaceName)));
879                 return 1;
880             }
881         }
882         // Only SFNT fonts are supported, ignore anything else.
883         else if (!(nFontType & TRUETYPE_FONTTYPE) &&
884                  !(pMetric->ntmFlags & NTM_PS_OPENTYPE) &&
885                  !(pMetric->ntmFlags & NTM_TT_OPENTYPE))
886         {
887             SAL_INFO("vcl.fonts", "Unsupported font ignored: " << OUString(o3tl::toU(lpelfe->lfFaceName)));
888             return 1;
889         }
890 
891         rtl::Reference<WinFontFace> pData
892             = new WinFontFace(*reinterpret_cast<ENUMLOGFONTEXW const*>(lpelfe), *pMetric);
893 
894         pInfo->mpList->Add( pData.get() );
895         SAL_INFO("vcl.fonts", "SalEnumFontsProcExW: font added: " << pData->GetFamilyName() << " " << pData->GetStyleName());
896     }
897 
898     return 1;
899 }
900 
901 struct TempFontItem
902 {
903     OUString maFontResourcePath;
904     TempFontItem* mpNextItem;
905 };
906 
lcl_AddFontResource(SalData & rSalData,const OUString & rFontFileURL)907 static int lcl_AddFontResource(SalData& rSalData, const OUString& rFontFileURL)
908 {
909     OUString aFontSystemPath;
910     OSL_VERIFY(!osl::FileBase::getSystemPathFromFileURL(rFontFileURL, aFontSystemPath));
911 
912     int nRet = AddFontResourceExW(o3tl::toW(aFontSystemPath.getStr()), FR_PRIVATE, nullptr);
913     SAL_WARN_IF(nRet <= 0, "vcl.fonts", "AddFontResourceExW failed for " << rFontFileURL);
914     if (nRet <= 0)
915         return nRet;
916 
917     TempFontItem* pNewItem = new TempFontItem;
918     pNewItem->maFontResourcePath = aFontSystemPath;
919 
920     pNewItem->mpNextItem = rSalData.mpTempFontItem;
921     rSalData.mpTempFontItem = pNewItem;
922 
923     return nRet;
924 }
925 
ImplReleaseTempFonts(SalData & rSalData)926 void ImplReleaseTempFonts(SalData& rSalData)
927 {
928     while (TempFontItem* p = rSalData.mpTempFontItem)
929     {
930         RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
931         rSalData.mpTempFontItem = p->mpNextItem;
932         delete p;
933     }
934 }
935 
AddTempDevFont(vcl::font::PhysicalFontCollection * pFontCollection,const OUString & rFontFileURL,const OUString & rFontName)936 bool WinSalGraphics::AddTempDevFont(vcl::font::PhysicalFontCollection* pFontCollection,
937                                     const OUString& rFontFileURL, const OUString& rFontName)
938 {
939     OUString aFontFamily = getFontFamilyNameFromTTF(rFontFileURL);
940     if (aFontFamily.isEmpty())
941     {
942         SAL_WARN("vcl.fonts", "error extracting font family from " << rFontFileURL);
943         return false;
944     }
945 
946     if (rFontName != aFontFamily)
947     {
948         SAL_WARN("vcl.fonts", "font family renaming not implemented; skipping embedded " << rFontName);
949         return false;
950     }
951 
952     int nFonts = lcl_AddFontResource(*GetSalData(), rFontFileURL);
953     if (nFonts <= 0)
954         return false;
955 
956     ImplEnumInfo aInfo{ .mhDC = getHDC(),
957                         .mpList = pFontCollection,
958                         .mpName = &aFontFamily,
959                         .mbPrinter = mbPrinter };
960     const int nExpectedFontCount = pFontCollection->Count() + nFonts;
961 
962     LOGFONTW aLogFont = { .lfCharSet = DEFAULT_CHARSET };
963 
964     // add the font to the PhysicalFontCollection
965     EnumFontFamiliesExW(getHDC(), &aLogFont,
966         SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0);
967 
968     SAL_WARN_IF(nExpectedFontCount != pFontCollection->Count() && !pFontCollection->FindFontFamily(aFontFamily),
969                 "vcl.fonts",
970                 "temp font was registered but is not in enumeration: " << rFontFileURL);
971 
972     return true;
973 }
974 
RemoveTempDevFont(const OUString & rFileURL,const OUString &)975 bool WinSalGraphics::RemoveTempDevFont(const OUString& rFileURL, const OUString& /*rFontName*/)
976 {
977     OUString path;
978     osl::FileBase::getSystemPathFromFileURL(rFileURL, path);
979     auto pSalData = GetSalData();
980     for (TempFontItem** pp = &pSalData->mpTempFontItem; *pp; pp = &(*pp)->mpNextItem)
981     {
982         if ((*pp)->maFontResourcePath == path)
983         {
984             RemoveFontResourceExW(o3tl::toW(path.getStr()), FR_PRIVATE, nullptr);
985             auto p = *pp;
986             *pp = p->mpNextItem;
987             delete p;
988             return true;
989         }
990     }
991     SAL_WARN("vcl.fonts", "Trying to unregister an embedded font that wasn't registered?");
992     return true; // It's still safe to delete the font file: we don't use it
993 }
994 
GetDevFontList(vcl::font::PhysicalFontCollection * pFontCollection)995 void WinSalGraphics::GetDevFontList( vcl::font::PhysicalFontCollection* pFontCollection )
996 {
997     // make sure all LO shared fonts are registered temporarily
998     static std::once_flag init;
999     std::call_once(init, []()
1000     {
1001         auto registerFontsIn = [](const OUString& dir) {
1002             // collect fonts in font path that could not be registered
1003             osl::Directory aFontDir(dir);
1004             osl::FileBase::RC rcOSL = aFontDir.open();
1005             if (rcOSL == osl::FileBase::E_None)
1006             {
1007                 osl::DirectoryItem aDirItem;
1008                 SalData* pSalData = GetSalData();
1009                 assert(pSalData);
1010 
1011                 while (aFontDir.getNextItem(aDirItem, 10) == osl::FileBase::E_None)
1012                 {
1013                     osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
1014                     rcOSL = aDirItem.getFileStatus(aFileStatus);
1015                     if (rcOSL == osl::FileBase::E_None)
1016                         lcl_AddFontResource(*pSalData, aFileStatus.getFileURL());
1017                 }
1018             }
1019         };
1020 
1021         // determine font path
1022         // since we are only interested in fonts that could not be
1023         // registered before because of missing administration rights
1024         // only the font path of the user installation is needed
1025         OUString aPath("$BRAND_BASE_DIR");
1026         rtl_bootstrap_expandMacros(&aPath.pData);
1027 
1028         // internal font resources, required for normal operation, like OpenSymbol
1029         registerFontsIn(aPath + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts");
1030 
1031         // collect fonts in font path that could not be registered
1032         registerFontsIn(aPath + "/" LIBO_SHARE_FOLDER "/fonts/truetype");
1033 
1034         return true;
1035     });
1036 
1037     ImplEnumInfo aInfo{ .mhDC = getHDC(),
1038                         .mpList = pFontCollection,
1039                         .mpName = nullptr,
1040                         .mbPrinter = mbPrinter };
1041 
1042     LOGFONTW aLogFont = { .lfCharSet = DEFAULT_CHARSET };
1043 
1044     // fill the PhysicalFontCollection
1045     EnumFontFamiliesExW( getHDC(), &aLogFont,
1046         SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0 );
1047 
1048     // set glyph fallback hook
1049     static WinGlyphFallbackSubstititution aSubstFallback;
1050     static WinPreMatchFontSubstititution aPreMatchFont;
1051     pFontCollection->SetFallbackHook( &aSubstFallback );
1052     pFontCollection->SetPreMatchHook(&aPreMatchFont);
1053 }
1054 
ClearDevFontCache()1055 void WinSalGraphics::ClearDevFontCache()
1056 {
1057     mWinSalGraphicsImplBase->ClearDevFontCache();
1058 }
1059 
GetGlyphOutline(sal_GlyphId nId,basegfx::B2DPolyPolygon & rB2DPolyPoly,bool) const1060 bool WinFontInstance::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rB2DPolyPoly, bool) const
1061 {
1062     rB2DPolyPoly.clear();
1063 
1064     assert(m_pGraphics);
1065     HDC hDC = m_pGraphics->getHDC();
1066     const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1067     const HFONT hFont = GetHFONT();
1068     if (hFont != hOrigFont)
1069         SelectObject(hDC, hFont);
1070 
1071     const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1072         { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1073 
1074     // use unity matrix
1075     MAT2 aMat;
1076     aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1077     aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1078 
1079     UINT nGGOFlags = GGO_NATIVE;
1080     nGGOFlags |= GGO_GLYPH_INDEX;
1081 
1082     GLYPHMETRICS aGlyphMetrics;
1083     const DWORD nSize1 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGlyphMetrics, 0, nullptr, &aMat);
1084     if( !nSize1 )       // blank glyphs are ok
1085         return true;
1086     else if( nSize1 == GDI_ERROR )
1087         return false;
1088 
1089     BYTE* pData = new BYTE[ nSize1 ];
1090     const DWORD nSize2 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags,
1091               &aGlyphMetrics, nSize1, pData, &aMat );
1092 
1093     if( nSize1 != nSize2 )
1094         return false;
1095 
1096     // TODO: avoid tools polygon by creating B2DPolygon directly
1097     int     nPtSize = 512;
1098     Point*  pPoints = new Point[ nPtSize ];
1099     PolyFlags* pFlags = new PolyFlags[ nPtSize ];
1100 
1101     TTPOLYGONHEADER* pHeader = reinterpret_cast<TTPOLYGONHEADER*>(pData);
1102     while( reinterpret_cast<BYTE*>(pHeader) < pData+nSize2 )
1103     {
1104         // only outline data is interesting
1105         if( pHeader->dwType != TT_POLYGON_TYPE )
1106             break;
1107 
1108         // get start point; next start points are end points
1109         // of previous segment
1110         sal_uInt16 nPnt = 0;
1111 
1112         tools::Long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
1113         tools::Long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
1114         pPoints[ nPnt ] = Point( nX, nY );
1115         pFlags[ nPnt++ ] = PolyFlags::Normal;
1116 
1117         bool bHasOfflinePoints = false;
1118         TTPOLYCURVE* pCurve = reinterpret_cast<TTPOLYCURVE*>( pHeader + 1 );
1119         pHeader = reinterpret_cast<TTPOLYGONHEADER*>( reinterpret_cast<BYTE*>(pHeader) + pHeader->cb );
1120         while( reinterpret_cast<BYTE*>(pCurve) < reinterpret_cast<BYTE*>(pHeader) )
1121         {
1122             int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
1123             if( nPtSize < nNeededSize )
1124             {
1125                 Point* pOldPoints = pPoints;
1126                 PolyFlags* pOldFlags = pFlags;
1127                 nPtSize = 2 * nNeededSize;
1128                 pPoints = new Point[ nPtSize ];
1129                 pFlags = new PolyFlags[ nPtSize ];
1130                 for( sal_uInt16 i = 0; i < nPnt; ++i )
1131                 {
1132                     pPoints[ i ] = pOldPoints[ i ];
1133                     pFlags[ i ] = pOldFlags[ i ];
1134                 }
1135                 delete[] pOldPoints;
1136                 delete[] pOldFlags;
1137             }
1138 
1139             int i = 0;
1140             if( TT_PRIM_LINE == pCurve->wType )
1141             {
1142                 while( i < pCurve->cpfx )
1143                 {
1144                     nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1145                     nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1146                     ++i;
1147                     pPoints[ nPnt ] = Point( nX, nY );
1148                     pFlags[ nPnt ] = PolyFlags::Normal;
1149                     ++nPnt;
1150                 }
1151             }
1152             else if( TT_PRIM_QSPLINE == pCurve->wType )
1153             {
1154                 bHasOfflinePoints = true;
1155                 while( i < pCurve->cpfx )
1156                 {
1157                     // get control point of quadratic bezier spline
1158                     nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1159                     nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1160                     ++i;
1161                     Point aControlP( nX, nY );
1162 
1163                     // calculate first cubic control point
1164                     // P0 = 1/3 * (PBeg + 2 * PQControl)
1165                     nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
1166                     nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
1167                     pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1168                     pFlags[ nPnt+0 ] = PolyFlags::Control;
1169 
1170                     // calculate endpoint of segment
1171                     nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1172                     nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1173 
1174                     if ( i+1 >= pCurve->cpfx )
1175                     {
1176                         // endpoint is either last point in segment => advance
1177                         ++i;
1178                     }
1179                     else
1180                     {
1181                         // or endpoint is the middle of two control points
1182                         nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
1183                         nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
1184                         nX = (nX + 1) / 2;
1185                         nY = (nY + 1) / 2;
1186                         // no need to advance, because the current point
1187                         // is the control point in next bezier spline
1188                     }
1189 
1190                     pPoints[ nPnt+2 ] = Point( nX, nY );
1191                     pFlags[ nPnt+2 ] = PolyFlags::Normal;
1192 
1193                     // calculate second cubic control point
1194                     // P1 = 1/3 * (PEnd + 2 * PQControl)
1195                     nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
1196                     nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
1197                     pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1198                     pFlags[ nPnt+1 ] = PolyFlags::Control;
1199 
1200                     nPnt += 3;
1201                 }
1202             }
1203 
1204             // next curve segment
1205             pCurve = reinterpret_cast<TTPOLYCURVE*>(&pCurve->apfx[ i ]);
1206         }
1207 
1208         // end point is start point for closed contour
1209         // disabled, because Polygon class closes the contour itself
1210         // pPoints[nPnt++] = pPoints[0];
1211         // #i35928#
1212         // Added again, but add only when not yet closed
1213         if(pPoints[nPnt - 1] != pPoints[0])
1214         {
1215             if( bHasOfflinePoints )
1216                 pFlags[nPnt] = pFlags[0];
1217 
1218             pPoints[nPnt++] = pPoints[0];
1219         }
1220 
1221         // convert y-coordinates W32 -> VCL
1222         for( int i = 0; i < nPnt; ++i )
1223             pPoints[i].setY(-pPoints[i].Y());
1224 
1225         // insert into polypolygon
1226         tools::Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : nullptr) );
1227         // convert to B2DPolyPolygon
1228         // TODO: get rid of the intermediate PolyPolygon
1229         rB2DPolyPoly.append( aPoly.getB2DPolygon() );
1230     }
1231 
1232     delete[] pPoints;
1233     delete[] pFlags;
1234 
1235     delete[] pData;
1236 
1237     // rescaling needed for the tools::PolyPolygon conversion
1238     if( rB2DPolyPoly.count() )
1239     {
1240         const double fFactor(1.0f/256);
1241         rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
1242     }
1243 
1244     return true;
1245 }
1246 
GetDWFontFace() const1247 IDWriteFontFace* WinFontInstance::GetDWFontFace() const
1248 {
1249     if (!mxDWFontFace)
1250     {
1251         assert(m_pGraphics);
1252         HDC hDC = m_pGraphics->getHDC();
1253         const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1254         const HFONT hFont = GetHFONT();
1255         if (hFont != hOrigFont)
1256             SelectObject(hDC, hFont);
1257 
1258         const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]() {
1259             if (hFont != hOrigFont)
1260                 SelectObject(hDC, hOrigFont);
1261         });
1262 
1263         IDWriteGdiInterop* pDWriteGdiInterop = WinSalGraphics::getDWriteGdiInterop();
1264 
1265         HRESULT hr = pDWriteGdiInterop->CreateFontFaceFromHdc(hDC, &mxDWFontFace);
1266         if (FAILED(hr))
1267         {
1268             SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
1269                                                << comphelper::WindowsErrorStringFromHRESULT(hr));
1270             mxDWFontFace = nullptr;
1271         }
1272     }
1273 
1274     return mxDWFontFace;
1275 }
1276 
1277 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
1278