tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / vcl / win / gdi / salfont.cxx
blob28a6dcf4dba5a01147f7b5572d63e3c37e4105e5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <sal/types.h>
23 #include <config_folders.h>
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>
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>
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>
72 #if HAVE_FEATURE_SKIA
73 #include <vcl/skia/SkiaHelper.hxx>
74 #include <skia/win/font.hxx>
75 #endif
77 using namespace vcl;
79 static FIXED FixedFromDouble( double d )
81 const tools::Long l = static_cast<tools::Long>( d * 65536. );
82 return *reinterpret_cast<FIXED const *>(&l);
85 static int IntTimes256FromFixed(FIXED f)
87 int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
88 return nFixedTimes256;
91 // platform specific font substitution hooks for glyph fallback enhancement
93 namespace {
95 class WinPreMatchFontSubstititution
96 : public vcl::font::PreMatchFontSubstitution
98 public:
99 bool FindFontSubstitute(vcl::font::FontSelectPattern&) const override;
102 class WinGlyphFallbackSubstititution
103 : public vcl::font::GlyphFallbackFontSubstitution
105 public:
106 bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
109 // does a font face hold the given missing characters?
110 bool HasMissingChars(vcl::font::PhysicalFontFace* pFace, OUString& rMissingChars)
112 FontCharMapRef xFontCharMap = pFace->GetFontCharMap();
114 // avoid fonts with unknown CMAP subtables for glyph fallback
115 if( !xFontCharMap.is() || xFontCharMap->IsDefaultMap() )
116 return false;
118 int nMatchCount = 0;
119 std::vector<sal_UCS4> rRemainingCodes;
120 const sal_Int32 nStrLen = rMissingChars.getLength();
121 sal_Int32 nStrIdx = 0;
122 while (nStrIdx < nStrLen)
124 const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
125 if (xFontCharMap->HasChar(uChar))
126 nMatchCount++;
127 else
128 rRemainingCodes.push_back(uChar);
131 xFontCharMap = nullptr;
133 if (nMatchCount > 0)
134 rMissingChars = OUString(rRemainingCodes.data(), rRemainingCodes.size());
136 return nMatchCount > 0;
139 //used by 2-level font fallback
140 vcl::font::PhysicalFontFamily* findDevFontListByLocale(const vcl::font::PhysicalFontCollection &rFontCollection,
141 const LanguageTag& rLanguageTag )
143 // get the default font for a specified locale
144 const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
145 const OUString aDefault = rDefaults.getUserInterfaceFont(rLanguageTag);
146 return rFontCollection.FindFontFamilyByTokenNames(aDefault);
150 // These are Win 3.1 bitmap fonts using "FON" font format
151 // which is not supported with DirectWrite so let's substitute them
152 // with a font that is supported and always available.
153 // Based on:
154 // https://dxr.mozilla.org/mozilla-esr10/source/gfx/thebes/gfxDWriteFontList.cpp#1057
155 const std::map<OUString, OUString> aBitmapFontSubs =
157 { "MS Sans Serif", "Microsoft Sans Serif" },
158 { "MS Serif", "Times New Roman" },
159 { "Small Fonts", "Arial" },
160 { "Courier", "Courier New" },
161 { "Roman", "Times New Roman" },
162 { "Script", "Mistral" }
165 // TODO: See if Windows have API that we can use here to improve font fallback.
166 bool WinPreMatchFontSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData) const
168 if (rFontSelData.IsMicrosoftSymbolEncoded() || IsOpenSymbol(rFontSelData.maSearchName))
169 return false;
171 for (const auto& aSub : aBitmapFontSubs)
173 if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
175 rFontSelData.maSearchName = aSub.second;
176 return true;
180 return false;
183 // find a fallback font for missing characters
184 // TODO: should stylistic matches be searched and preferred?
185 bool WinGlyphFallbackSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* /*pLogicalFont*/, OUString& rMissingChars) const
187 // guess a locale matching to the missing chars
188 LanguageType eLang = rFontSelData.meLanguage;
189 LanguageTag aLanguageTag( eLang);
191 // fall back to default UI locale if the font language is inconclusive
192 if( eLang == LANGUAGE_DONTKNOW )
193 aLanguageTag = Application::GetSettings().GetUILanguageTag();
195 // first level fallback:
196 // try use the locale specific default fonts defined in VCL.xcu
197 const vcl::font::PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
198 vcl::font::PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag);
199 if( pFontFamily )
201 vcl::font::PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
202 if( HasMissingChars( pFace, rMissingChars ) )
204 rFontSelData.maSearchName = pFontFamily->GetSearchName();
205 return true;
209 // are the missing characters symbols?
210 pFontFamily = pFontCollection->FindFontFamilyByAttributes( ImplFontAttrs::Symbol,
211 rFontSelData.GetWeight(),
212 rFontSelData.GetWidthType(),
213 rFontSelData.GetItalic(),
214 rFontSelData.maSearchName );
215 if( pFontFamily )
217 vcl::font::PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
218 if( HasMissingChars( pFace, rMissingChars ) )
220 rFontSelData.maSearchName = pFontFamily->GetSearchName();
221 return true;
225 // last level fallback, check each font type face one by one
226 std::unique_ptr<vcl::font::PhysicalFontFaceCollection> pTestFontList = pFontCollection->GetFontFaceCollection();
227 // limit the count of fonts to be checked to prevent hangs
228 static const int MAX_GFBFONT_COUNT = 600;
229 int nTestFontCount = pTestFontList->Count();
230 if( nTestFontCount > MAX_GFBFONT_COUNT )
231 nTestFontCount = MAX_GFBFONT_COUNT;
233 bool bFound = false;
234 for( int i = 0; i < nTestFontCount; ++i )
236 vcl::font::PhysicalFontFace* pFace = pTestFontList->Get( i );
237 bFound = HasMissingChars( pFace, rMissingChars );
238 if( !bFound )
239 continue;
240 rFontSelData.maSearchName = pFace->GetFamilyName();
241 break;
244 return bFound;
247 namespace {
249 struct ImplEnumInfo
251 HDC mhDC;
252 vcl::font::PhysicalFontCollection* mpList;
253 OUString* mpName;
254 LOGFONTW* mpLogFont;
255 bool mbPrinter;
256 int mnFontCount;
261 static rtl_TextEncoding ImplCharSetToSal( BYTE nCharSet )
263 rtl_TextEncoding eTextEncoding;
265 if ( nCharSet == OEM_CHARSET )
267 UINT nCP = static_cast<sal_uInt16>(GetOEMCP());
268 switch ( nCP )
270 // It is unclear why these two (undefined?) code page numbers are
271 // handled specially here:
272 case 1004: eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
273 case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
274 default:
275 eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
276 break;
279 else
281 if( nCharSet )
282 eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
283 else
284 eTextEncoding = RTL_TEXTENCODING_UNICODE;
287 return eTextEncoding;
290 static FontFamily ImplFamilyToSal( BYTE nFamily )
292 switch ( nFamily & 0xF0 )
294 case FF_DECORATIVE:
295 return FAMILY_DECORATIVE;
297 case FF_MODERN:
298 return FAMILY_MODERN;
300 case FF_ROMAN:
301 return FAMILY_ROMAN;
303 case FF_SCRIPT:
304 return FAMILY_SCRIPT;
306 case FF_SWISS:
307 return FAMILY_SWISS;
309 default:
310 break;
313 return FAMILY_DONTKNOW;
316 static BYTE ImplFamilyToWin( FontFamily eFamily )
318 switch ( eFamily )
320 case FAMILY_DECORATIVE:
321 return FF_DECORATIVE;
323 case FAMILY_MODERN:
324 return FF_MODERN;
326 case FAMILY_ROMAN:
327 return FF_ROMAN;
329 case FAMILY_SCRIPT:
330 return FF_SCRIPT;
332 case FAMILY_SWISS:
333 return FF_SWISS;
335 case FAMILY_SYSTEM:
336 return FF_SWISS;
338 default:
339 break;
342 return FF_DONTCARE;
345 static FontWeight ImplWeightToSal( int nWeight )
347 if ( nWeight <= FW_THIN )
348 return WEIGHT_THIN;
349 else if ( nWeight <= FW_ULTRALIGHT )
350 return WEIGHT_ULTRALIGHT;
351 else if ( nWeight <= FW_LIGHT )
352 return WEIGHT_LIGHT;
353 else if ( nWeight < FW_MEDIUM )
354 return WEIGHT_NORMAL;
355 else if ( nWeight == FW_MEDIUM )
356 return WEIGHT_MEDIUM;
357 else if ( nWeight <= FW_SEMIBOLD )
358 return WEIGHT_SEMIBOLD;
359 else if ( nWeight <= FW_BOLD )
360 return WEIGHT_BOLD;
361 else if ( nWeight <= FW_ULTRABOLD )
362 return WEIGHT_ULTRABOLD;
363 else
364 return WEIGHT_BLACK;
367 static int ImplWeightToWin( FontWeight eWeight )
369 switch ( eWeight )
371 case WEIGHT_THIN:
372 return FW_THIN;
374 case WEIGHT_ULTRALIGHT:
375 return FW_ULTRALIGHT;
377 case WEIGHT_LIGHT:
378 return FW_LIGHT;
380 case WEIGHT_SEMILIGHT:
381 case WEIGHT_NORMAL:
382 return FW_NORMAL;
384 case WEIGHT_MEDIUM:
385 return FW_MEDIUM;
387 case WEIGHT_SEMIBOLD:
388 return FW_SEMIBOLD;
390 case WEIGHT_BOLD:
391 return FW_BOLD;
393 case WEIGHT_ULTRABOLD:
394 return FW_ULTRABOLD;
396 case WEIGHT_BLACK:
397 return FW_BLACK;
399 default:
400 break;
403 return 0;
406 static FontPitch ImplLogPitchToSal( BYTE nPitch )
408 if ( nPitch & FIXED_PITCH )
409 return PITCH_FIXED;
410 else
411 return PITCH_VARIABLE;
414 static FontPitch ImplMetricPitchToSal( BYTE nPitch )
416 // Grrrr! See NT help
417 if ( !(nPitch & TMPF_FIXED_PITCH) )
418 return PITCH_FIXED;
419 else
420 return PITCH_VARIABLE;
423 static BYTE ImplPitchToWin( FontPitch ePitch )
425 if ( ePitch == PITCH_FIXED )
426 return FIXED_PITCH;
427 else if ( ePitch == PITCH_VARIABLE )
428 return VARIABLE_PITCH;
429 else
430 return DEFAULT_PITCH;
433 static FontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
434 const NEWTEXTMETRICW& rMetric)
436 FontAttributes aDFA;
438 const LOGFONTW rLogFont = rEnumFont.elfLogFont;
440 // get font face attributes
441 aDFA.SetFamilyType(ImplFamilyToSal( rLogFont.lfPitchAndFamily ));
442 aDFA.SetWidthType(WIDTH_DONTKNOW);
443 aDFA.SetWeight(ImplWeightToSal( rLogFont.lfWeight ));
444 aDFA.SetItalic((rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE);
445 aDFA.SetPitch(ImplLogPitchToSal( rLogFont.lfPitchAndFamily ));
446 aDFA.SetMicrosoftSymbolEncoded(rLogFont.lfCharSet == SYMBOL_CHARSET);
448 // get the font face name
449 aDFA.SetFamilyName(OUString(o3tl::toU(rLogFont.lfFaceName)));
451 // use the face's style name only if it looks reasonable
452 const wchar_t* pStyleName = rEnumFont.elfStyle;
453 const wchar_t* pEnd = pStyleName + sizeof(rEnumFont.elfStyle)/sizeof(*rEnumFont.elfStyle);
454 const wchar_t* p = pStyleName;
455 for(; *p && (p < pEnd); ++p )
456 if( *p < 0x0020 )
457 break;
458 if( p < pEnd )
459 aDFA.SetStyleName(OUString(o3tl::toU(pStyleName)));
461 // heuristics for font quality
462 // - opentypeTT > truetype
463 aDFA.SetQuality( 0 );
464 if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
465 aDFA.IncreaseQualityBy( 50 );
466 if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
467 aDFA.IncreaseQualityBy( 10 );
469 // TODO: add alias names
470 return aDFA;
473 void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
475 OUString aFontName( o3tl::toU(rLogFont.lfFaceName) );
476 if (!aFontName.isEmpty())
478 rFont.SetFamilyName( aFontName );
479 rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
480 rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
481 rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
482 rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
484 tools::Long nFontHeight = rLogFont.lfHeight;
485 if ( nFontHeight < 0 )
486 nFontHeight = -nFontHeight;
487 tools::Long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
488 if( !nDPIY )
489 nDPIY = 600;
490 nFontHeight *= 72;
491 nFontHeight += nDPIY/2;
492 nFontHeight /= nDPIY;
493 rFont.SetFontSize( Size( 0, nFontHeight ) );
494 rFont.SetOrientation( Degree10(static_cast<sal_Int16>(rLogFont.lfEscapement)) );
495 if ( rLogFont.lfItalic )
496 rFont.SetItalic( ITALIC_NORMAL );
497 else
498 rFont.SetItalic( ITALIC_NONE );
499 if ( rLogFont.lfUnderline )
500 rFont.SetUnderline( LINESTYLE_SINGLE );
501 else
502 rFont.SetUnderline( LINESTYLE_NONE );
503 if ( rLogFont.lfStrikeOut )
504 rFont.SetStrikeout( STRIKEOUT_SINGLE );
505 else
506 rFont.SetStrikeout( STRIKEOUT_NONE );
510 WinFontFace::WinFontFace(const ENUMLOGFONTEXW& rEnumFont, const NEWTEXTMETRICW& rMetric)
511 : vcl::font::PhysicalFontFace(WinFont2DevFontAttributes(rEnumFont, rMetric)),
512 mnId( 0 ),
513 meWinCharSet(rEnumFont.elfLogFont.lfCharSet),
514 mnPitchAndFamily(rMetric.tmPitchAndFamily),
515 maLogFont(rEnumFont.elfLogFont)
519 WinFontFace::~WinFontFace()
523 sal_IntPtr WinFontFace::GetFontId() const
525 return mnId;
528 rtl::Reference<LogicalFontInstance> WinFontFace::CreateFontInstance(const vcl::font::FontSelectPattern& rFSD) const
530 #if HAVE_FEATURE_SKIA
531 if (SkiaHelper::isVCLSkiaEnabled())
532 return new SkiaWinFontInstance(*this, rFSD);
533 #endif
534 return new WinFontInstance(*this, rFSD);
537 const std::vector<hb_variation_t>&
538 WinFontFace::GetVariations(const LogicalFontInstance& rFont) const
540 if (!mxVariations)
542 mxVariations.emplace();
543 auto pDWFontFace = static_cast<const WinFontInstance&>(rFont).GetDWFontFace();
544 if (pDWFontFace)
546 sal::systools::COMReference<IDWriteFontFace5> xDWFontFace5;
547 auto hr = pDWFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
548 reinterpret_cast<void**>(&xDWFontFace5));
549 if (SUCCEEDED(hr) && xDWFontFace5->HasVariations())
551 std::vector<DWRITE_FONT_AXIS_VALUE> aAxisValues(
552 xDWFontFace5->GetFontAxisValueCount());
553 hr = xDWFontFace5->GetFontAxisValues(aAxisValues.data(), aAxisValues.size());
554 if (SUCCEEDED(hr))
556 mxVariations->reserve(aAxisValues.size());
557 for (auto& rAxisValue : aAxisValues)
558 mxVariations->push_back(
559 { OSL_NETDWORD(rAxisValue.axisTag), rAxisValue.value });
565 return *mxVariations;
568 namespace
570 struct BlobReference
572 hb_blob_t* mpBlob;
573 BlobReference(hb_blob_t* pBlob)
574 : mpBlob(pBlob)
576 hb_blob_reference(mpBlob);
578 BlobReference(BlobReference&& other) noexcept
579 : mpBlob(other.mpBlob)
581 other.mpBlob = nullptr;
583 BlobReference& operator=(BlobReference&& other)
585 std::swap(mpBlob, other.mpBlob);
586 return *this;
588 BlobReference(const BlobReference& other) = delete;
589 BlobReference& operator=(BlobReference& other) = delete;
590 ~BlobReference() { hb_blob_destroy(mpBlob); }
594 using BlobCacheKey = std::pair<sal_IntPtr, hb_tag_t>;
596 namespace
598 struct BlobCacheKeyHash
600 std::size_t operator()(BlobCacheKey const& rKey) const
602 std::size_t seed = 0;
603 o3tl::hash_combine(seed, rKey.first);
604 o3tl::hash_combine(seed, rKey.second);
605 return seed;
610 hb_blob_t* WinFontFace::GetHbTable(hb_tag_t nTag) const
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())
617 hb_blob_reference(it->second.mpBlob);
618 return it->second.mpBlob;
621 sal_uLong nLength = 0;
622 unsigned char* pBuffer = nullptr;
624 HDC hDC(::GetDC(nullptr));
625 HFONT hFont = ::CreateFontIndirectW(&maLogFont);
626 HFONT hOldFont = ::SelectFont(hDC, hFont);
628 nLength = ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, nullptr, 0);
629 if (nLength > 0 && nLength != GDI_ERROR)
631 pBuffer = new unsigned char[nLength];
632 ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, pBuffer, nLength);
635 ::SelectFont(hDC, hOldFont);
636 ::DeleteFont(hFont);
637 ::ReleaseDC(nullptr, hDC);
639 hb_blob_t* pBlob = nullptr;
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); });
645 gCache.insert({ aCacheKey, BlobReference(pBlob) });
646 return pBlob;
649 void WinSalGraphics::SetTextColor( Color nColor )
651 COLORREF aCol = PALETTERGB( nColor.GetRed(),
652 nColor.GetGreen(),
653 nColor.GetBlue() );
655 if( !mbPrinter &&
656 GetSalData()->mhDitherPal &&
657 ImplIsSysColorEntry( nColor ) )
659 aCol = PALRGB_TO_RGB( aCol );
662 ::SetTextColor( getHDC(), aCol );
665 static int CALLBACK SalEnumQueryFontProcExW( const LOGFONTW*, const TEXTMETRICW*, DWORD, LPARAM lParam )
667 *reinterpret_cast<bool*>(lParam) = true;
668 return 0;
671 void ImplGetLogFontFromFontSelect( const vcl::font::FontSelectPattern& rFont,
672 const vcl::font::PhysicalFontFace* pFontFace,
673 LOGFONTW& rLogFont, bool bAntiAliased)
675 OUString aName;
676 if (pFontFace)
677 aName = pFontFace->GetFamilyName();
678 else
679 aName = rFont.GetFamilyName().getToken( 0, ';' );
681 UINT nNameLen = aName.getLength();
682 if (nNameLen >= LF_FACESIZE)
683 nNameLen = LF_FACESIZE - 1;
684 memcpy( rLogFont.lfFaceName, aName.getStr(), nNameLen*sizeof( wchar_t ) );
685 rLogFont.lfFaceName[nNameLen] = 0;
687 if (pFontFace)
689 const WinFontFace* pWinFontData = static_cast<const WinFontFace*>(pFontFace);
690 rLogFont.lfCharSet = pWinFontData->GetCharSet();
691 rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
693 else
695 rLogFont.lfCharSet = rFont.IsMicrosoftSymbolEncoded() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
696 rLogFont.lfPitchAndFamily = ImplPitchToWin( rFont.GetPitch() )
697 | ImplFamilyToWin( rFont.GetFamilyType() );
700 rLogFont.lfWeight = ImplWeightToWin( rFont.GetWeight() );
701 rLogFont.lfHeight = static_cast<LONG>(-rFont.mnHeight);
702 rLogFont.lfWidth = static_cast<LONG>(rFont.mnWidth);
703 rLogFont.lfUnderline = 0;
704 rLogFont.lfStrikeOut = 0;
705 rLogFont.lfItalic = BYTE(rFont.GetItalic() != ITALIC_NONE);
706 rLogFont.lfEscapement = rFont.mnOrientation.get();
707 rLogFont.lfOrientation = rLogFont.lfEscapement;
708 rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
709 rLogFont.lfOutPrecision = OUT_TT_PRECIS;
710 if ( rFont.mnOrientation )
711 rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
713 // disable antialiasing if requested
714 if ( rFont.mbNonAntialiased )
715 rLogFont.lfQuality = NONANTIALIASED_QUALITY;
716 else if (Application::GetSettings().GetStyleSettings().GetUseFontAAFromSystem())
717 rLogFont.lfQuality = DEFAULT_QUALITY; // for display on screen
718 else
719 rLogFont.lfQuality = bAntiAliased ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
722 std::tuple<HFONT,bool,sal_Int32> WinSalGraphics::ImplDoSetFont(HDC hDC, vcl::font::FontSelectPattern const & i_rFont,
723 const vcl::font::PhysicalFontFace * i_pFontFace,
724 HFONT& o_rOldFont)
726 HFONT hNewFont = nullptr;
728 LOGFONTW aLogFont;
729 ImplGetLogFontFromFontSelect( i_rFont, i_pFontFace, aLogFont, getAntiAlias());
731 bool bIsCJKVerticalFont = false;
732 // select vertical mode for printing if requested and available
733 if ( i_rFont.mbVertical && mbPrinter )
735 constexpr size_t nLen = sizeof(aLogFont.lfFaceName) - sizeof(aLogFont.lfFaceName[0]);
736 // vertical fonts start with an '@'
737 memmove( &aLogFont.lfFaceName[1], &aLogFont.lfFaceName[0], nLen );
738 aLogFont.lfFaceName[0] = '@';
739 aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
741 // check availability of vertical mode for this font
742 EnumFontFamiliesExW( getHDC(), &aLogFont, SalEnumQueryFontProcExW,
743 reinterpret_cast<LPARAM>(&bIsCJKVerticalFont), 0 );
744 if( !bIsCJKVerticalFont )
746 // restore non-vertical name if not vertical mode isn't available
747 memcpy( &aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1], nLen );
748 aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
752 hNewFont = ::CreateFontIndirectW( &aLogFont );
753 o_rOldFont = ::SelectFont(hDC, hNewFont);
755 TEXTMETRICW aTextMetricW;
756 if (!::GetTextMetricsW(hDC, &aTextMetricW))
758 // the selected font doesn't work => try a replacement
759 // TODO: use its font fallback instead
760 lstrcpynW( aLogFont.lfFaceName, L"Courier New", 12 );
761 aLogFont.lfPitchAndFamily = FIXED_PITCH;
762 HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
763 SelectFont(hDC, hNewFont2);
764 DeleteFont( hNewFont );
765 hNewFont = hNewFont2;
766 bIsCJKVerticalFont = false;
769 return std::make_tuple(hNewFont, bIsCJKVerticalFont, static_cast<sal_Int32>(aTextMetricW.tmDescent));
772 void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
774 assert(nFallbackLevel >= 0 && nFallbackLevel < MAX_FALLBACK);
776 // return early if there is no new font
777 if( !pFont )
779 if (!mpWinFontEntry[nFallbackLevel].is())
780 return;
782 // DeInitGraphics doesn't free the cached fonts, so mhDefFont might be nullptr
783 if (mhDefFont)
785 ::SelectFont(getHDC(), mhDefFont);
786 mhDefFont = nullptr;
789 // release no longer referenced font handles
790 for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
791 mpWinFontEntry[i] = nullptr;
792 return;
795 WinFontInstance *pFontInstance = static_cast<WinFontInstance*>(pFont);
796 mpWinFontEntry[ nFallbackLevel ] = pFontInstance;
798 HFONT hOldFont = nullptr;
799 HFONT hNewFont = pFontInstance->GetHFONT();
800 if (!hNewFont)
802 pFontInstance->SetGraphics(this);
803 hNewFont = pFontInstance->GetHFONT();
805 hOldFont = ::SelectFont(getHDC(), hNewFont);
807 // keep default font
808 if( !mhDefFont )
809 mhDefFont = hOldFont;
810 else
812 // release no longer referenced font handles
813 for( int i = nFallbackLevel + 1; i < MAX_FALLBACK && mpWinFontEntry[i].is(); ++i )
814 mpWinFontEntry[i] = nullptr;
818 void WinSalGraphics::GetFontMetric( FontMetricDataRef& rxFontMetric, int nFallbackLevel )
820 // temporarily change the HDC to the font in the fallback level
821 rtl::Reference<WinFontInstance> pFontInstance = mpWinFontEntry[nFallbackLevel];
822 const HFONT hOldFont = SelectFont(getHDC(), pFontInstance->GetHFONT());
824 wchar_t aFaceName[LF_FACESIZE+60];
825 if( GetTextFaceW( getHDC(), SAL_N_ELEMENTS(aFaceName), aFaceName ) )
826 rxFontMetric->SetFamilyName(OUString(o3tl::toU(aFaceName)));
828 rxFontMetric->SetMinKashida(pFontInstance->GetKashidaWidth());
829 rxFontMetric->ImplCalcLineSpacing(pFontInstance.get());
830 rxFontMetric->ImplInitBaselines(pFontInstance.get());
832 // get the font metric
833 OUTLINETEXTMETRICW aOutlineMetric;
834 const bool bOK = GetOutlineTextMetricsW(getHDC(), sizeof(aOutlineMetric), &aOutlineMetric);
835 // restore the HDC to the font in the base level
836 SelectFont( getHDC(), hOldFont );
837 if( !bOK )
838 return;
840 TEXTMETRICW aWinMetric = aOutlineMetric.otmTextMetrics;
842 // device independent font attributes
843 rxFontMetric->SetFamilyType(ImplFamilyToSal( aWinMetric.tmPitchAndFamily ));
844 rxFontMetric->SetMicrosoftSymbolEncoded(aWinMetric.tmCharSet == SYMBOL_CHARSET);
845 rxFontMetric->SetWeight(ImplWeightToSal( aWinMetric.tmWeight ));
846 rxFontMetric->SetPitch(ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily ));
847 rxFontMetric->SetItalic(aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE);
848 rxFontMetric->SetSlant( 0 );
850 // transformation dependent font metrics
851 rxFontMetric->SetWidth(aWinMetric.tmAveCharWidth);
854 FontCharMapRef WinSalGraphics::GetFontCharMap() const
856 if (!mpWinFontEntry[0])
858 return FontCharMapRef( new FontCharMap() );
860 return mpWinFontEntry[0]->GetFontFace()->GetFontCharMap();
863 bool WinSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
865 if (!mpWinFontEntry[0])
866 return false;
867 return mpWinFontEntry[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
870 static int CALLBACK SalEnumFontsProcExW( const LOGFONTW* lpelfe,
871 const TEXTMETRICW* lpntme,
872 DWORD nFontType, LPARAM lParam )
874 ENUMLOGFONTEXW const * pLogFont
875 = reinterpret_cast<ENUMLOGFONTEXW const *>(lpelfe);
876 NEWTEXTMETRICEXW const * pMetric
877 = reinterpret_cast<NEWTEXTMETRICEXW const *>(lpntme);
878 ImplEnumInfo* pInfo = reinterpret_cast<ImplEnumInfo*>(lParam);
879 if ( !pInfo->mpName )
881 // Ignore vertical fonts
882 if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
884 OUString aName(o3tl::toU(pLogFont->elfLogFont.lfFaceName));
885 pInfo->mpName = &aName;
886 memcpy(pInfo->mpLogFont->lfFaceName, pLogFont->elfLogFont.lfFaceName, (aName.getLength()+1)*sizeof(wchar_t));
887 pInfo->mpLogFont->lfCharSet = pLogFont->elfLogFont.lfCharSet;
888 EnumFontFamiliesExW(pInfo->mhDC, pInfo->mpLogFont, SalEnumFontsProcExW,
889 reinterpret_cast<LPARAM>(pInfo), 0);
890 pInfo->mpLogFont->lfFaceName[0] = '\0';
891 pInfo->mpLogFont->lfCharSet = DEFAULT_CHARSET;
892 pInfo->mpName = nullptr;
895 else
897 // Ignore non-device fonts on printers.
898 if (pInfo->mbPrinter)
900 if ((nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE))
902 SAL_INFO("vcl.fonts", "Unsupported printer font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
903 return 1;
906 // Only SFNT fonts are supported, ignore anything else.
907 else if (!(nFontType & TRUETYPE_FONTTYPE) &&
908 !(pMetric->ntmTm.ntmFlags & NTM_PS_OPENTYPE) &&
909 !(pMetric->ntmTm.ntmFlags & NTM_TT_OPENTYPE))
911 SAL_INFO("vcl.fonts", "Unsupported font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
912 return 1;
915 rtl::Reference<WinFontFace> pData = new WinFontFace(*pLogFont, pMetric->ntmTm);
916 pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
918 pInfo->mpList->Add( pData.get() );
919 SAL_INFO("vcl.fonts", "SalEnumFontsProcExW: font added: " << pData->GetFamilyName() << " " << pData->GetStyleName());
922 return 1;
925 struct TempFontItem
927 OUString maFontResourcePath;
928 TempFontItem* mpNextItem;
931 static int lcl_AddFontResource(SalData& rSalData, const OUString& rFontFileURL, bool bShared)
933 OUString aFontSystemPath;
934 OSL_VERIFY(!osl::FileBase::getSystemPathFromFileURL(rFontFileURL, aFontSystemPath));
936 int nRet = AddFontResourceExW(o3tl::toW(aFontSystemPath.getStr()), FR_PRIVATE, nullptr);
937 SAL_WARN_IF(nRet <= 0, "vcl.fonts", "AddFontResourceExW failed for " << rFontFileURL);
938 if (nRet > 0)
940 TempFontItem* pNewItem = new TempFontItem;
941 pNewItem->maFontResourcePath = aFontSystemPath;
942 if (bShared)
944 pNewItem->mpNextItem = rSalData.mpSharedTempFontItem;
945 rSalData.mpSharedTempFontItem = pNewItem;
947 else
949 pNewItem->mpNextItem = rSalData.mpOtherTempFontItem;
950 rSalData.mpOtherTempFontItem = pNewItem;
953 return nRet;
956 void ImplReleaseTempFonts(SalData& rSalData, bool bAll)
958 while (TempFontItem* p = rSalData.mpOtherTempFontItem)
960 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
961 rSalData.mpOtherTempFontItem = p->mpNextItem;
962 delete p;
965 if (!bAll)
966 return;
968 while (TempFontItem* p = rSalData.mpSharedTempFontItem)
970 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
971 rSalData.mpSharedTempFontItem = p->mpNextItem;
972 delete p;
976 static OUString lcl_GetFontFamilyName(std::u16string_view rFontFileURL)
978 // Create temporary file name
979 OUString aTempFileURL;
980 if (osl::File::E_None != osl::File::createTempFile(nullptr, nullptr, &aTempFileURL))
981 return OUString();
982 osl::File::remove(aTempFileURL);
983 OUString aResSystemPath;
984 osl::FileBase::getSystemPathFromFileURL(aTempFileURL, aResSystemPath);
986 // Create font resource file (.fot)
987 // There is a limit of 127 characters for the full path passed via lpszFile, so we have to
988 // split the font URL and pass it as two parameters. As a result we can't use
989 // CreateScalableFontResource for renaming, as it now expects the font in the system path.
990 // But it's still good to use it for family name extraction, we're currently after.
991 // BTW: it doesn't help to prefix the lpszFile with \\?\ to support larger paths.
992 // TODO: use TTLoadEmbeddedFont (needs an EOT as input, so we have to add a header to the TTF)
993 // TODO: forward the EOT from the AddTempDevFont call side, if VCL supports it
994 INetURLObject aTTFUrl(rFontFileURL);
995 // GetBase() strips the extension
996 OUString aFilename = aTTFUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
997 if (!CreateScalableFontResourceW(0, o3tl::toW(aResSystemPath.getStr()),
998 o3tl::toW(aFilename.getStr()), o3tl::toW(aTTFUrl.GetPath().getStr())))
1000 sal_uInt32 nError = GetLastError();
1001 SAL_WARN("vcl.fonts", "CreateScalableFontResource failed for " << aResSystemPath << " "
1002 << aFilename << " " << aTTFUrl.GetPath() << " " << nError);
1003 return OUString();
1006 // Open and read the font resource file
1007 osl::File aFotFile(aTempFileURL);
1008 if (osl::FileBase::E_None != aFotFile.open(osl_File_OpenFlag_Read))
1009 return OUString();
1011 sal_uInt64 nBytesRead = 0;
1012 char aBuffer[4096];
1013 aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead );
1014 // clean up temporary resource file
1015 aFotFile.close();
1016 osl::File::remove(aTempFileURL);
1018 // retrieve font family name from byte offset 0x4F6
1019 static const sal_uInt64 nNameOfs = 0x4F6;
1020 sal_uInt64 nPos = nNameOfs;
1021 for (; (nPos < nBytesRead) && (aBuffer[nPos] != 0); nPos++);
1022 if (nPos >= nBytesRead || (nPos == nNameOfs))
1023 return OUString();
1025 return OUString(aBuffer + nNameOfs, nPos - nNameOfs, osl_getThreadTextEncoding());
1028 bool WinSalGraphics::AddTempDevFont(vcl::font::PhysicalFontCollection* pFontCollection,
1029 const OUString& rFontFileURL, const OUString& rFontName)
1031 OUString aFontFamily = lcl_GetFontFamilyName(rFontFileURL);
1032 if (aFontFamily.isEmpty())
1034 SAL_WARN("vcl.fonts", "error extracting font family from " << rFontFileURL);
1035 return false;
1038 if (rFontName != aFontFamily)
1040 SAL_WARN("vcl.fonts", "font family renaming not implemented; skipping embedded " << rFontName);
1041 return false;
1044 int nFonts = lcl_AddFontResource(*GetSalData(), rFontFileURL, false);
1045 if (nFonts <= 0)
1046 return false;
1048 ImplEnumInfo aInfo;
1049 aInfo.mhDC = getHDC();
1050 aInfo.mpList = pFontCollection;
1051 aInfo.mpName = &aFontFamily;
1052 aInfo.mbPrinter = mbPrinter;
1053 aInfo.mnFontCount = pFontCollection->Count();
1054 const int nExpectedFontCount = aInfo.mnFontCount + nFonts;
1056 LOGFONTW aLogFont = {};
1057 aLogFont.lfCharSet = DEFAULT_CHARSET;
1058 aInfo.mpLogFont = &aLogFont;
1060 // add the font to the PhysicalFontCollection
1061 EnumFontFamiliesExW(getHDC(), &aLogFont,
1062 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0);
1064 SAL_WARN_IF(nExpectedFontCount != pFontCollection->Count(), "vcl.fonts",
1065 "temp font was registered but is not in enumeration: " << rFontFileURL);
1067 return true;
1070 void WinSalGraphics::GetDevFontList( vcl::font::PhysicalFontCollection* pFontCollection )
1072 // make sure all LO shared fonts are registered temporarily
1073 static std::once_flag init;
1074 std::call_once(init, []()
1076 auto registerFontsIn = [](const OUString& dir) {
1077 // collect fonts in font path that could not be registered
1078 osl::Directory aFontDir(dir);
1079 osl::FileBase::RC rcOSL = aFontDir.open();
1080 if (rcOSL == osl::FileBase::E_None)
1082 osl::DirectoryItem aDirItem;
1083 SalData* pSalData = GetSalData();
1084 assert(pSalData);
1086 while (aFontDir.getNextItem(aDirItem, 10) == osl::FileBase::E_None)
1088 osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
1089 rcOSL = aDirItem.getFileStatus(aFileStatus);
1090 if (rcOSL == osl::FileBase::E_None)
1091 lcl_AddFontResource(*pSalData, aFileStatus.getFileURL(), true);
1096 // determine font path
1097 // since we are only interested in fonts that could not be
1098 // registered before because of missing administration rights
1099 // only the font path of the user installation is needed
1100 OUString aPath("$BRAND_BASE_DIR");
1101 rtl_bootstrap_expandMacros(&aPath.pData);
1103 // internal font resources, required for normal operation, like OpenSymbol
1104 registerFontsIn(aPath + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts");
1106 // collect fonts in font path that could not be registered
1107 registerFontsIn(aPath + "/" LIBO_SHARE_FOLDER "/fonts/truetype");
1109 return true;
1112 ImplEnumInfo aInfo;
1113 aInfo.mhDC = getHDC();
1114 aInfo.mpList = pFontCollection;
1115 aInfo.mpName = nullptr;
1116 aInfo.mbPrinter = mbPrinter;
1117 aInfo.mnFontCount = 0;
1119 LOGFONTW aLogFont = {};
1120 aLogFont.lfCharSet = DEFAULT_CHARSET;
1121 aInfo.mpLogFont = &aLogFont;
1123 // fill the PhysicalFontCollection
1124 EnumFontFamiliesExW( getHDC(), &aLogFont,
1125 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0 );
1127 // set glyph fallback hook
1128 static WinGlyphFallbackSubstititution aSubstFallback;
1129 static WinPreMatchFontSubstititution aPreMatchFont;
1130 pFontCollection->SetFallbackHook( &aSubstFallback );
1131 pFontCollection->SetPreMatchHook(&aPreMatchFont);
1134 void WinSalGraphics::ClearDevFontCache()
1136 mWinSalGraphicsImplBase->ClearDevFontCache();
1137 ImplReleaseTempFonts(*GetSalData(), false);
1140 bool WinFontInstance::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rB2DPolyPoly, bool) const
1142 rB2DPolyPoly.clear();
1144 assert(m_pGraphics);
1145 HDC hDC = m_pGraphics->getHDC();
1146 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1147 const HFONT hFont = GetHFONT();
1148 if (hFont != hOrigFont)
1149 SelectObject(hDC, hFont);
1151 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1152 { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1154 // use unity matrix
1155 MAT2 aMat;
1156 aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1157 aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1159 UINT nGGOFlags = GGO_NATIVE;
1160 nGGOFlags |= GGO_GLYPH_INDEX;
1162 GLYPHMETRICS aGlyphMetrics;
1163 const DWORD nSize1 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGlyphMetrics, 0, nullptr, &aMat);
1164 if( !nSize1 ) // blank glyphs are ok
1165 return true;
1166 else if( nSize1 == GDI_ERROR )
1167 return false;
1169 BYTE* pData = new BYTE[ nSize1 ];
1170 const DWORD nSize2 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags,
1171 &aGlyphMetrics, nSize1, pData, &aMat );
1173 if( nSize1 != nSize2 )
1174 return false;
1176 // TODO: avoid tools polygon by creating B2DPolygon directly
1177 int nPtSize = 512;
1178 Point* pPoints = new Point[ nPtSize ];
1179 PolyFlags* pFlags = new PolyFlags[ nPtSize ];
1181 TTPOLYGONHEADER* pHeader = reinterpret_cast<TTPOLYGONHEADER*>(pData);
1182 while( reinterpret_cast<BYTE*>(pHeader) < pData+nSize2 )
1184 // only outline data is interesting
1185 if( pHeader->dwType != TT_POLYGON_TYPE )
1186 break;
1188 // get start point; next start points are end points
1189 // of previous segment
1190 sal_uInt16 nPnt = 0;
1192 tools::Long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
1193 tools::Long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
1194 pPoints[ nPnt ] = Point( nX, nY );
1195 pFlags[ nPnt++ ] = PolyFlags::Normal;
1197 bool bHasOfflinePoints = false;
1198 TTPOLYCURVE* pCurve = reinterpret_cast<TTPOLYCURVE*>( pHeader + 1 );
1199 pHeader = reinterpret_cast<TTPOLYGONHEADER*>( reinterpret_cast<BYTE*>(pHeader) + pHeader->cb );
1200 while( reinterpret_cast<BYTE*>(pCurve) < reinterpret_cast<BYTE*>(pHeader) )
1202 int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
1203 if( nPtSize < nNeededSize )
1205 Point* pOldPoints = pPoints;
1206 PolyFlags* pOldFlags = pFlags;
1207 nPtSize = 2 * nNeededSize;
1208 pPoints = new Point[ nPtSize ];
1209 pFlags = new PolyFlags[ nPtSize ];
1210 for( sal_uInt16 i = 0; i < nPnt; ++i )
1212 pPoints[ i ] = pOldPoints[ i ];
1213 pFlags[ i ] = pOldFlags[ i ];
1215 delete[] pOldPoints;
1216 delete[] pOldFlags;
1219 int i = 0;
1220 if( TT_PRIM_LINE == pCurve->wType )
1222 while( i < pCurve->cpfx )
1224 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1225 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1226 ++i;
1227 pPoints[ nPnt ] = Point( nX, nY );
1228 pFlags[ nPnt ] = PolyFlags::Normal;
1229 ++nPnt;
1232 else if( TT_PRIM_QSPLINE == pCurve->wType )
1234 bHasOfflinePoints = true;
1235 while( i < pCurve->cpfx )
1237 // get control point of quadratic bezier spline
1238 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1239 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1240 ++i;
1241 Point aControlP( nX, nY );
1243 // calculate first cubic control point
1244 // P0 = 1/3 * (PBeg + 2 * PQControl)
1245 nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
1246 nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
1247 pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1248 pFlags[ nPnt+0 ] = PolyFlags::Control;
1250 // calculate endpoint of segment
1251 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1252 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1254 if ( i+1 >= pCurve->cpfx )
1256 // endpoint is either last point in segment => advance
1257 ++i;
1259 else
1261 // or endpoint is the middle of two control points
1262 nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
1263 nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
1264 nX = (nX + 1) / 2;
1265 nY = (nY + 1) / 2;
1266 // no need to advance, because the current point
1267 // is the control point in next bezier spline
1270 pPoints[ nPnt+2 ] = Point( nX, nY );
1271 pFlags[ nPnt+2 ] = PolyFlags::Normal;
1273 // calculate second cubic control point
1274 // P1 = 1/3 * (PEnd + 2 * PQControl)
1275 nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
1276 nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
1277 pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1278 pFlags[ nPnt+1 ] = PolyFlags::Control;
1280 nPnt += 3;
1284 // next curve segment
1285 pCurve = reinterpret_cast<TTPOLYCURVE*>(&pCurve->apfx[ i ]);
1288 // end point is start point for closed contour
1289 // disabled, because Polygon class closes the contour itself
1290 // pPoints[nPnt++] = pPoints[0];
1291 // #i35928#
1292 // Added again, but add only when not yet closed
1293 if(pPoints[nPnt - 1] != pPoints[0])
1295 if( bHasOfflinePoints )
1296 pFlags[nPnt] = pFlags[0];
1298 pPoints[nPnt++] = pPoints[0];
1301 // convert y-coordinates W32 -> VCL
1302 for( int i = 0; i < nPnt; ++i )
1303 pPoints[i].setY(-pPoints[i].Y());
1305 // insert into polypolygon
1306 tools::Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : nullptr) );
1307 // convert to B2DPolyPolygon
1308 // TODO: get rid of the intermediate PolyPolygon
1309 rB2DPolyPoly.append( aPoly.getB2DPolygon() );
1312 delete[] pPoints;
1313 delete[] pFlags;
1315 delete[] pData;
1317 // rescaling needed for the tools::PolyPolygon conversion
1318 if( rB2DPolyPoly.count() )
1320 const double fFactor(1.0f/256);
1321 rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
1324 return true;
1327 IDWriteFontFace* WinFontInstance::GetDWFontFace() const
1329 if (!mxDWFontFace)
1331 assert(m_pGraphics);
1332 HDC hDC = m_pGraphics->getHDC();
1333 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1334 const HFONT hFont = GetHFONT();
1335 if (hFont != hOrigFont)
1336 SelectObject(hDC, hFont);
1338 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]() {
1339 if (hFont != hOrigFont)
1340 SelectObject(hDC, hOrigFont);
1343 IDWriteGdiInterop* pDWriteGdiInterop = WinSalGraphics::getDWriteGdiInterop();
1345 HRESULT hr = pDWriteGdiInterop->CreateFontFaceFromHdc(hDC, &mxDWFontFace);
1346 if (FAILED(hr))
1348 SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
1349 << WindowsErrorStringFromHRESULT(hr));
1350 mxDWFontFace = nullptr;
1354 return mxDWFontFace;
1357 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */