calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / win / gdi / salfont.cxx
blob196932982c288cc2a549affe5b9b91eb2b731b87
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 // Currently, we build with _WIN32_WINNT=0x0601 (Windows 7), which means newer
37 // declarations in dwrite_3.h will not be visible.
38 #if WINVER < 0x0A00
39 # include "dw-extra.h"
40 #endif
42 #include <o3tl/lru_map.hxx>
43 #include <basegfx/matrix/b2dhommatrixtools.hxx>
44 #include <basegfx/polygon/b2dpolygon.hxx>
45 #include <i18nlangtag/mslangid.hxx>
46 #include <osl/file.hxx>
47 #include <osl/process.h>
48 #include <rtl/bootstrap.hxx>
49 #include <rtl/tencinfo.h>
50 #include <sal/log.hxx>
51 #include <o3tl/char16_t2wchar_t.hxx>
52 #include <tools/helpers.hxx>
53 #include <tools/stream.hxx>
54 #include <tools/urlobj.hxx>
55 #include <unotools/fontcfg.hxx>
56 #include <vcl/settings.hxx>
57 #include <vcl/sysdata.hxx>
58 #include <vcl/metric.hxx>
59 #include <vcl/fontcharmap.hxx>
60 #include <comphelper/scopeguard.hxx>
61 #include <comphelper/windowserrorstring.hxx>
63 #include <font/FontSelectPattern.hxx>
64 #include <font/PhysicalFontCollection.hxx>
65 #include <font/PhysicalFontFaceCollection.hxx>
66 #include <font/PhysicalFontFace.hxx>
67 #include <font/fontsubstitution.hxx>
68 #include <sft.hxx>
69 #include <win/saldata.hxx>
70 #include <win/salgdi.h>
71 #include <win/winlayout.hxx>
72 #include <win/wingdiimpl.hxx>
73 #include <impfontcharmap.hxx>
74 #include <impfontmetricdata.hxx>
75 #include <impglyphitem.hxx>
77 #if HAVE_FEATURE_SKIA
78 #include <vcl/skia/SkiaHelper.hxx>
79 #include <skia/win/font.hxx>
80 #endif
82 using namespace vcl;
84 static FIXED FixedFromDouble( double d )
86 const tools::Long l = static_cast<tools::Long>( d * 65536. );
87 return *reinterpret_cast<FIXED const *>(&l);
90 static int IntTimes256FromFixed(FIXED f)
92 int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
93 return nFixedTimes256;
96 // platform specific font substitution hooks for glyph fallback enhancement
98 namespace {
100 class WinPreMatchFontSubstititution
101 : public vcl::font::PreMatchFontSubstitution
103 public:
104 bool FindFontSubstitute(vcl::font::FontSelectPattern&) const override;
107 class WinGlyphFallbackSubstititution
108 : public vcl::font::GlyphFallbackFontSubstitution
110 public:
111 bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
114 // does a font face hold the given missing characters?
115 bool HasMissingChars(vcl::font::PhysicalFontFace* pFace, OUString& rMissingChars)
117 FontCharMapRef xFontCharMap = pFace->GetFontCharMap();
119 // avoid fonts with unknown CMAP subtables for glyph fallback
120 if( !xFontCharMap.is() || xFontCharMap->IsDefaultMap() )
121 return false;
123 int nMatchCount = 0;
124 std::vector<sal_UCS4> rRemainingCodes;
125 const sal_Int32 nStrLen = rMissingChars.getLength();
126 sal_Int32 nStrIdx = 0;
127 while (nStrIdx < nStrLen)
129 const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
130 if (xFontCharMap->HasChar(uChar))
131 nMatchCount++;
132 else
133 rRemainingCodes.push_back(uChar);
136 xFontCharMap = nullptr;
138 if (nMatchCount > 0)
139 rMissingChars = OUString(rRemainingCodes.data(), rRemainingCodes.size());
141 return nMatchCount > 0;
144 //used by 2-level font fallback
145 vcl::font::PhysicalFontFamily* findDevFontListByLocale(const vcl::font::PhysicalFontCollection &rFontCollection,
146 const LanguageTag& rLanguageTag )
148 // get the default font for a specified locale
149 const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
150 const OUString aDefault = rDefaults.getUserInterfaceFont(rLanguageTag);
151 return rFontCollection.FindFontFamilyByTokenNames(aDefault);
155 // These are Win 3.1 bitmap fonts using "FON" font format
156 // which is not supported with DirectWrite so let's substitute them
157 // with a font that is supported and always available.
158 // Based on:
159 // https://dxr.mozilla.org/mozilla-esr10/source/gfx/thebes/gfxDWriteFontList.cpp#1057
160 const std::map<OUString, OUString> aBitmapFontSubs =
162 { "MS Sans Serif", "Microsoft Sans Serif" },
163 { "MS Serif", "Times New Roman" },
164 { "Small Fonts", "Arial" },
165 { "Courier", "Courier New" },
166 { "Roman", "Times New Roman" },
167 { "Script", "Mistral" }
170 // TODO: See if Windows have API that we can use here to improve font fallback.
171 bool WinPreMatchFontSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData) const
173 if (rFontSelData.IsMicrosoftSymbolEncoded() || IsOpenSymbol(rFontSelData.maSearchName))
174 return false;
176 for (const auto& aSub : aBitmapFontSubs)
178 if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
180 rFontSelData.maSearchName = aSub.second;
181 return true;
185 return false;
188 // find a fallback font for missing characters
189 // TODO: should stylistic matches be searched and preferred?
190 bool WinGlyphFallbackSubstititution::FindFontSubstitute(vcl::font::FontSelectPattern& rFontSelData, LogicalFontInstance* /*pLogicalFont*/, OUString& rMissingChars) const
192 // guess a locale matching to the missing chars
193 LanguageType eLang = rFontSelData.meLanguage;
194 LanguageTag aLanguageTag( eLang);
196 // fall back to default UI locale if the font language is inconclusive
197 if( eLang == LANGUAGE_DONTKNOW )
198 aLanguageTag = Application::GetSettings().GetUILanguageTag();
200 // first level fallback:
201 // try use the locale specific default fonts defined in VCL.xcu
202 const vcl::font::PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
203 vcl::font::PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag);
204 if( pFontFamily )
206 vcl::font::PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
207 if( HasMissingChars( pFace, rMissingChars ) )
209 rFontSelData.maSearchName = pFontFamily->GetSearchName();
210 return true;
214 // are the missing characters symbols?
215 pFontFamily = pFontCollection->FindFontFamilyByAttributes( ImplFontAttrs::Symbol,
216 rFontSelData.GetWeight(),
217 rFontSelData.GetWidthType(),
218 rFontSelData.GetItalic(),
219 rFontSelData.maSearchName );
220 if( pFontFamily )
222 vcl::font::PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
223 if( HasMissingChars( pFace, rMissingChars ) )
225 rFontSelData.maSearchName = pFontFamily->GetSearchName();
226 return true;
230 // last level fallback, check each font type face one by one
231 std::unique_ptr<vcl::font::PhysicalFontFaceCollection> pTestFontList = pFontCollection->GetFontFaceCollection();
232 // limit the count of fonts to be checked to prevent hangs
233 static const int MAX_GFBFONT_COUNT = 600;
234 int nTestFontCount = pTestFontList->Count();
235 if( nTestFontCount > MAX_GFBFONT_COUNT )
236 nTestFontCount = MAX_GFBFONT_COUNT;
238 bool bFound = false;
239 for( int i = 0; i < nTestFontCount; ++i )
241 vcl::font::PhysicalFontFace* pFace = pTestFontList->Get( i );
242 bFound = HasMissingChars( pFace, rMissingChars );
243 if( !bFound )
244 continue;
245 rFontSelData.maSearchName = pFace->GetFamilyName();
246 break;
249 return bFound;
252 namespace {
254 struct ImplEnumInfo
256 HDC mhDC;
257 vcl::font::PhysicalFontCollection* mpList;
258 OUString* mpName;
259 LOGFONTW* mpLogFont;
260 bool mbPrinter;
261 int mnFontCount;
266 static rtl_TextEncoding ImplCharSetToSal( BYTE nCharSet )
268 rtl_TextEncoding eTextEncoding;
270 if ( nCharSet == OEM_CHARSET )
272 UINT nCP = static_cast<sal_uInt16>(GetOEMCP());
273 switch ( nCP )
275 // It is unclear why these two (undefined?) code page numbers are
276 // handled specially here:
277 case 1004: eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
278 case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
279 default:
280 eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
281 break;
284 else
286 if( nCharSet )
287 eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
288 else
289 eTextEncoding = RTL_TEXTENCODING_UNICODE;
292 return eTextEncoding;
295 static FontFamily ImplFamilyToSal( BYTE nFamily )
297 switch ( nFamily & 0xF0 )
299 case FF_DECORATIVE:
300 return FAMILY_DECORATIVE;
302 case FF_MODERN:
303 return FAMILY_MODERN;
305 case FF_ROMAN:
306 return FAMILY_ROMAN;
308 case FF_SCRIPT:
309 return FAMILY_SCRIPT;
311 case FF_SWISS:
312 return FAMILY_SWISS;
314 default:
315 break;
318 return FAMILY_DONTKNOW;
321 static BYTE ImplFamilyToWin( FontFamily eFamily )
323 switch ( eFamily )
325 case FAMILY_DECORATIVE:
326 return FF_DECORATIVE;
328 case FAMILY_MODERN:
329 return FF_MODERN;
331 case FAMILY_ROMAN:
332 return FF_ROMAN;
334 case FAMILY_SCRIPT:
335 return FF_SCRIPT;
337 case FAMILY_SWISS:
338 return FF_SWISS;
340 case FAMILY_SYSTEM:
341 return FF_SWISS;
343 default:
344 break;
347 return FF_DONTCARE;
350 static FontWeight ImplWeightToSal( int nWeight )
352 if ( nWeight <= FW_THIN )
353 return WEIGHT_THIN;
354 else if ( nWeight <= FW_ULTRALIGHT )
355 return WEIGHT_ULTRALIGHT;
356 else if ( nWeight <= FW_LIGHT )
357 return WEIGHT_LIGHT;
358 else if ( nWeight < FW_MEDIUM )
359 return WEIGHT_NORMAL;
360 else if ( nWeight == FW_MEDIUM )
361 return WEIGHT_MEDIUM;
362 else if ( nWeight <= FW_SEMIBOLD )
363 return WEIGHT_SEMIBOLD;
364 else if ( nWeight <= FW_BOLD )
365 return WEIGHT_BOLD;
366 else if ( nWeight <= FW_ULTRABOLD )
367 return WEIGHT_ULTRABOLD;
368 else
369 return WEIGHT_BLACK;
372 static int ImplWeightToWin( FontWeight eWeight )
374 switch ( eWeight )
376 case WEIGHT_THIN:
377 return FW_THIN;
379 case WEIGHT_ULTRALIGHT:
380 return FW_ULTRALIGHT;
382 case WEIGHT_LIGHT:
383 return FW_LIGHT;
385 case WEIGHT_SEMILIGHT:
386 case WEIGHT_NORMAL:
387 return FW_NORMAL;
389 case WEIGHT_MEDIUM:
390 return FW_MEDIUM;
392 case WEIGHT_SEMIBOLD:
393 return FW_SEMIBOLD;
395 case WEIGHT_BOLD:
396 return FW_BOLD;
398 case WEIGHT_ULTRABOLD:
399 return FW_ULTRABOLD;
401 case WEIGHT_BLACK:
402 return FW_BLACK;
404 default:
405 break;
408 return 0;
411 static FontPitch ImplLogPitchToSal( BYTE nPitch )
413 if ( nPitch & FIXED_PITCH )
414 return PITCH_FIXED;
415 else
416 return PITCH_VARIABLE;
419 static FontPitch ImplMetricPitchToSal( BYTE nPitch )
421 // Grrrr! See NT help
422 if ( !(nPitch & TMPF_FIXED_PITCH) )
423 return PITCH_FIXED;
424 else
425 return PITCH_VARIABLE;
428 static BYTE ImplPitchToWin( FontPitch ePitch )
430 if ( ePitch == PITCH_FIXED )
431 return FIXED_PITCH;
432 else if ( ePitch == PITCH_VARIABLE )
433 return VARIABLE_PITCH;
434 else
435 return DEFAULT_PITCH;
438 static FontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
439 const NEWTEXTMETRICW& rMetric)
441 FontAttributes aDFA;
443 const LOGFONTW rLogFont = rEnumFont.elfLogFont;
445 // get font face attributes
446 aDFA.SetFamilyType(ImplFamilyToSal( rLogFont.lfPitchAndFamily ));
447 aDFA.SetWidthType(WIDTH_DONTKNOW);
448 aDFA.SetWeight(ImplWeightToSal( rLogFont.lfWeight ));
449 aDFA.SetItalic((rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE);
450 aDFA.SetPitch(ImplLogPitchToSal( rLogFont.lfPitchAndFamily ));
451 aDFA.SetMicrosoftSymbolEncoded(rLogFont.lfCharSet == SYMBOL_CHARSET);
453 // get the font face name
454 aDFA.SetFamilyName(OUString(o3tl::toU(rLogFont.lfFaceName)));
456 // use the face's style name only if it looks reasonable
457 const wchar_t* pStyleName = rEnumFont.elfStyle;
458 const wchar_t* pEnd = pStyleName + sizeof(rEnumFont.elfStyle)/sizeof(*rEnumFont.elfStyle);
459 const wchar_t* p = pStyleName;
460 for(; *p && (p < pEnd); ++p )
461 if( *p < 0x0020 )
462 break;
463 if( p < pEnd )
464 aDFA.SetStyleName(OUString(o3tl::toU(pStyleName)));
466 // heuristics for font quality
467 // - opentypeTT > truetype
468 aDFA.SetQuality( 0 );
469 if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
470 aDFA.IncreaseQualityBy( 50 );
471 if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
472 aDFA.IncreaseQualityBy( 10 );
474 // TODO: add alias names
475 return aDFA;
478 void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
480 OUString aFontName( o3tl::toU(rLogFont.lfFaceName) );
481 if (!aFontName.isEmpty())
483 rFont.SetFamilyName( aFontName );
484 rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
485 rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
486 rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
487 rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
489 tools::Long nFontHeight = rLogFont.lfHeight;
490 if ( nFontHeight < 0 )
491 nFontHeight = -nFontHeight;
492 tools::Long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
493 if( !nDPIY )
494 nDPIY = 600;
495 nFontHeight *= 72;
496 nFontHeight += nDPIY/2;
497 nFontHeight /= nDPIY;
498 rFont.SetFontSize( Size( 0, nFontHeight ) );
499 rFont.SetOrientation( Degree10(static_cast<sal_Int16>(rLogFont.lfEscapement)) );
500 if ( rLogFont.lfItalic )
501 rFont.SetItalic( ITALIC_NORMAL );
502 else
503 rFont.SetItalic( ITALIC_NONE );
504 if ( rLogFont.lfUnderline )
505 rFont.SetUnderline( LINESTYLE_SINGLE );
506 else
507 rFont.SetUnderline( LINESTYLE_NONE );
508 if ( rLogFont.lfStrikeOut )
509 rFont.SetStrikeout( STRIKEOUT_SINGLE );
510 else
511 rFont.SetStrikeout( STRIKEOUT_NONE );
515 WinFontFace::WinFontFace(const ENUMLOGFONTEXW& rEnumFont, const NEWTEXTMETRICW& rMetric)
516 : vcl::font::PhysicalFontFace(WinFont2DevFontAttributes(rEnumFont, rMetric)),
517 mnId( 0 ),
518 meWinCharSet(rEnumFont.elfLogFont.lfCharSet),
519 mnPitchAndFamily(rMetric.tmPitchAndFamily),
520 maLogFont(rEnumFont.elfLogFont)
524 WinFontFace::~WinFontFace()
528 sal_IntPtr WinFontFace::GetFontId() const
530 return mnId;
533 rtl::Reference<LogicalFontInstance> WinFontFace::CreateFontInstance(const vcl::font::FontSelectPattern& rFSD) const
535 #if HAVE_FEATURE_SKIA
536 if (SkiaHelper::isVCLSkiaEnabled())
537 return new SkiaWinFontInstance(*this, rFSD);
538 #endif
539 return new WinFontInstance(*this, rFSD);
542 const std::vector<hb_variation_t>&
543 WinFontFace::GetVariations(const LogicalFontInstance& rFont) const
545 if (!mxVariations)
547 mxVariations.emplace();
548 auto pDWFontFace = static_cast<const WinFontInstance&>(rFont).GetDWFontFace();
549 if (pDWFontFace)
551 sal::systools::COMReference<IDWriteFontFace5> xDWFontFace5;
552 auto hr = pDWFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
553 reinterpret_cast<void**>(&xDWFontFace5));
554 if (SUCCEEDED(hr) && xDWFontFace5->HasVariations())
556 std::vector<DWRITE_FONT_AXIS_VALUE> aAxisValues(
557 xDWFontFace5->GetFontAxisValueCount());
558 hr = xDWFontFace5->GetFontAxisValues(aAxisValues.data(), aAxisValues.size());
559 if (SUCCEEDED(hr))
561 mxVariations->reserve(aAxisValues.size());
562 for (auto& rAxisValue : aAxisValues)
563 mxVariations->push_back(
564 { OSL_NETDWORD(rAxisValue.axisTag), rAxisValue.value });
570 return *mxVariations;
573 namespace
575 struct BlobReference
577 hb_blob_t* mpBlob;
578 BlobReference(hb_blob_t* pBlob)
579 : mpBlob(pBlob)
581 hb_blob_reference(mpBlob);
583 BlobReference(BlobReference&& other) noexcept
584 : mpBlob(other.mpBlob)
586 other.mpBlob = nullptr;
588 BlobReference& operator=(BlobReference&& other)
590 std::swap(mpBlob, other.mpBlob);
591 return *this;
593 BlobReference(const BlobReference& other) = delete;
594 BlobReference& operator=(BlobReference& other) = delete;
595 ~BlobReference() { hb_blob_destroy(mpBlob); }
599 using BlobCacheKey = std::pair<sal_IntPtr, hb_tag_t>;
601 namespace
603 struct BlobCacheKeyHash
605 std::size_t operator()(BlobCacheKey const& rKey) const
607 std::size_t seed = 0;
608 o3tl::hash_combine(seed, rKey.first);
609 o3tl::hash_combine(seed, rKey.second);
610 return seed;
615 hb_blob_t* WinFontFace::GetHbTable(hb_tag_t nTag) const
617 static o3tl::lru_map<BlobCacheKey, BlobReference, BlobCacheKeyHash> gCache(50);
618 BlobCacheKey aCacheKey{ GetFontId(), nTag };
619 auto it = gCache.find(aCacheKey);
620 if (it != gCache.end())
622 hb_blob_reference(it->second.mpBlob);
623 return it->second.mpBlob;
626 sal_uLong nLength = 0;
627 unsigned char* pBuffer = nullptr;
629 HDC hDC(::GetDC(nullptr));
630 HFONT hFont = ::CreateFontIndirectW(&maLogFont);
631 HFONT hOldFont = ::SelectFont(hDC, hFont);
633 nLength = ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, nullptr, 0);
634 if (nLength > 0 && nLength != GDI_ERROR)
636 pBuffer = new unsigned char[nLength];
637 ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, pBuffer, nLength);
640 ::SelectFont(hDC, hOldFont);
641 ::DeleteFont(hFont);
642 ::ReleaseDC(nullptr, hDC);
644 hb_blob_t* pBlob = nullptr;
646 if (pBuffer)
647 pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY,
648 pBuffer, [](void* data) { delete[] static_cast<unsigned char*>(data); });
650 gCache.insert({ aCacheKey, BlobReference(pBlob) });
651 return pBlob;
654 void WinSalGraphics::SetTextColor( Color nColor )
656 COLORREF aCol = PALETTERGB( nColor.GetRed(),
657 nColor.GetGreen(),
658 nColor.GetBlue() );
660 if( !mbPrinter &&
661 GetSalData()->mhDitherPal &&
662 ImplIsSysColorEntry( nColor ) )
664 aCol = PALRGB_TO_RGB( aCol );
667 ::SetTextColor( getHDC(), aCol );
670 static int CALLBACK SalEnumQueryFontProcExW( const LOGFONTW*, const TEXTMETRICW*, DWORD, LPARAM lParam )
672 *reinterpret_cast<bool*>(lParam) = true;
673 return 0;
676 void ImplGetLogFontFromFontSelect( const vcl::font::FontSelectPattern& rFont,
677 const vcl::font::PhysicalFontFace* pFontFace,
678 LOGFONTW& rLogFont )
680 OUString aName;
681 if (pFontFace)
682 aName = pFontFace->GetFamilyName();
683 else
684 aName = rFont.GetFamilyName().getToken( 0, ';' );
686 UINT nNameLen = aName.getLength();
687 if (nNameLen >= LF_FACESIZE)
688 nNameLen = LF_FACESIZE - 1;
689 memcpy( rLogFont.lfFaceName, aName.getStr(), nNameLen*sizeof( wchar_t ) );
690 rLogFont.lfFaceName[nNameLen] = 0;
692 if (pFontFace)
694 const WinFontFace* pWinFontData = static_cast<const WinFontFace*>(pFontFace);
695 rLogFont.lfCharSet = pWinFontData->GetCharSet();
696 rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
698 else
700 rLogFont.lfCharSet = rFont.IsMicrosoftSymbolEncoded() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
701 rLogFont.lfPitchAndFamily = ImplPitchToWin( rFont.GetPitch() )
702 | ImplFamilyToWin( rFont.GetFamilyType() );
705 rLogFont.lfWeight = ImplWeightToWin( rFont.GetWeight() );
706 rLogFont.lfHeight = static_cast<LONG>(-rFont.mnHeight);
707 rLogFont.lfWidth = static_cast<LONG>(rFont.mnWidth);
708 rLogFont.lfUnderline = 0;
709 rLogFont.lfStrikeOut = 0;
710 rLogFont.lfItalic = BYTE(rFont.GetItalic() != ITALIC_NONE);
711 rLogFont.lfEscapement = rFont.mnOrientation.get();
712 rLogFont.lfOrientation = rLogFont.lfEscapement;
713 rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
714 rLogFont.lfQuality = DEFAULT_QUALITY;
715 rLogFont.lfOutPrecision = OUT_TT_PRECIS;
716 if ( rFont.mnOrientation )
717 rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
719 // disable antialiasing if requested
720 if ( rFont.mbNonAntialiased )
721 rLogFont.lfQuality = NONANTIALIASED_QUALITY;
725 std::tuple<HFONT,bool,sal_Int32> WinSalGraphics::ImplDoSetFont(HDC hDC, vcl::font::FontSelectPattern const & i_rFont,
726 const vcl::font::PhysicalFontFace * i_pFontFace,
727 HFONT& o_rOldFont)
729 HFONT hNewFont = nullptr;
731 LOGFONTW aLogFont;
732 ImplGetLogFontFromFontSelect( i_rFont, i_pFontFace, aLogFont );
734 bool bIsCJKVerticalFont = false;
735 // select vertical mode for printing if requested and available
736 if ( i_rFont.mbVertical && mbPrinter )
738 constexpr size_t nLen = sizeof(aLogFont.lfFaceName) - sizeof(aLogFont.lfFaceName[0]);
739 // vertical fonts start with an '@'
740 memmove( &aLogFont.lfFaceName[1], &aLogFont.lfFaceName[0], nLen );
741 aLogFont.lfFaceName[0] = '@';
742 aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
744 // check availability of vertical mode for this font
745 EnumFontFamiliesExW( getHDC(), &aLogFont, SalEnumQueryFontProcExW,
746 reinterpret_cast<LPARAM>(&bIsCJKVerticalFont), 0 );
747 if( !bIsCJKVerticalFont )
749 // restore non-vertical name if not vertical mode isn't available
750 memcpy( &aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1], nLen );
751 aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
755 hNewFont = ::CreateFontIndirectW( &aLogFont );
757 HDC hdcScreen = nullptr;
758 if( mbVirDev )
759 // only required for virtual devices, see below for details
760 hdcScreen = GetDC(nullptr);
761 if( hdcScreen )
763 // select font into screen hdc first to get an antialiased font
764 // and instantly restore the default font!
765 // see knowledge base article 305290:
766 // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
767 SelectFont( hdcScreen, SelectFont( hdcScreen , hNewFont ) );
769 o_rOldFont = ::SelectFont(hDC, hNewFont);
771 TEXTMETRICW aTextMetricW;
772 if (!::GetTextMetricsW(hDC, &aTextMetricW))
774 // the selected font doesn't work => try a replacement
775 // TODO: use its font fallback instead
776 lstrcpynW( aLogFont.lfFaceName, L"Courier New", 12 );
777 aLogFont.lfPitchAndFamily = FIXED_PITCH;
778 HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
779 SelectFont(hDC, hNewFont2);
780 DeleteFont( hNewFont );
781 hNewFont = hNewFont2;
782 bIsCJKVerticalFont = false;
785 if( hdcScreen )
786 ::ReleaseDC( nullptr, hdcScreen );
788 return std::make_tuple(hNewFont, bIsCJKVerticalFont, static_cast<sal_Int32>(aTextMetricW.tmDescent));
791 void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
793 assert(nFallbackLevel >= 0 && nFallbackLevel < MAX_FALLBACK);
795 // return early if there is no new font
796 if( !pFont )
798 if (!mpWinFontEntry[nFallbackLevel].is())
799 return;
801 // DeInitGraphics doesn't free the cached fonts, so mhDefFont might be nullptr
802 if (mhDefFont)
804 ::SelectFont(getHDC(), mhDefFont);
805 mhDefFont = nullptr;
808 // release no longer referenced font handles
809 for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
810 mpWinFontEntry[i] = nullptr;
811 return;
814 WinFontInstance *pFontInstance = static_cast<WinFontInstance*>(pFont);
815 mpWinFontEntry[ nFallbackLevel ] = pFontInstance;
817 HFONT hOldFont = nullptr;
818 HFONT hNewFont = pFontInstance->GetHFONT();
819 if (!hNewFont)
821 pFontInstance->SetGraphics(this);
822 hNewFont = pFontInstance->GetHFONT();
824 hOldFont = ::SelectFont(getHDC(), hNewFont);
826 // keep default font
827 if( !mhDefFont )
828 mhDefFont = hOldFont;
829 else
831 // release no longer referenced font handles
832 for( int i = nFallbackLevel + 1; i < MAX_FALLBACK && mpWinFontEntry[i].is(); ++i )
833 mpWinFontEntry[i] = nullptr;
837 void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel )
839 // temporarily change the HDC to the font in the fallback level
840 rtl::Reference<WinFontInstance> pFontInstance = mpWinFontEntry[nFallbackLevel];
841 const HFONT hOldFont = SelectFont(getHDC(), pFontInstance->GetHFONT());
843 wchar_t aFaceName[LF_FACESIZE+60];
844 if( GetTextFaceW( getHDC(), SAL_N_ELEMENTS(aFaceName), aFaceName ) )
845 rxFontMetric->SetFamilyName(OUString(o3tl::toU(aFaceName)));
847 rxFontMetric->SetMinKashida(pFontInstance->GetKashidaWidth());
848 rxFontMetric->ImplCalcLineSpacing(pFontInstance.get());
849 rxFontMetric->ImplInitBaselines(pFontInstance.get());
851 // get the font metric
852 OUTLINETEXTMETRICW aOutlineMetric;
853 const bool bOK = GetOutlineTextMetricsW(getHDC(), sizeof(aOutlineMetric), &aOutlineMetric);
854 // restore the HDC to the font in the base level
855 SelectFont( getHDC(), hOldFont );
856 if( !bOK )
857 return;
859 TEXTMETRICW aWinMetric = aOutlineMetric.otmTextMetrics;
861 // device independent font attributes
862 rxFontMetric->SetFamilyType(ImplFamilyToSal( aWinMetric.tmPitchAndFamily ));
863 rxFontMetric->SetMicrosoftSymbolEncoded(aWinMetric.tmCharSet == SYMBOL_CHARSET);
864 rxFontMetric->SetWeight(ImplWeightToSal( aWinMetric.tmWeight ));
865 rxFontMetric->SetPitch(ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily ));
866 rxFontMetric->SetItalic(aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE);
867 rxFontMetric->SetSlant( 0 );
869 // transformation dependent font metrics
870 rxFontMetric->SetWidth(static_cast<int>(pFontInstance->GetScale() * aWinMetric.tmAveCharWidth));
873 FontCharMapRef WinSalGraphics::GetFontCharMap() const
875 if (!mpWinFontEntry[0])
877 return FontCharMapRef( new FontCharMap() );
879 return mpWinFontEntry[0]->GetFontFace()->GetFontCharMap();
882 bool WinSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
884 if (!mpWinFontEntry[0])
885 return false;
886 return mpWinFontEntry[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
889 static int CALLBACK SalEnumFontsProcExW( const LOGFONTW* lpelfe,
890 const TEXTMETRICW* lpntme,
891 DWORD nFontType, LPARAM lParam )
893 ENUMLOGFONTEXW const * pLogFont
894 = reinterpret_cast<ENUMLOGFONTEXW const *>(lpelfe);
895 NEWTEXTMETRICEXW const * pMetric
896 = reinterpret_cast<NEWTEXTMETRICEXW const *>(lpntme);
897 ImplEnumInfo* pInfo = reinterpret_cast<ImplEnumInfo*>(lParam);
898 if ( !pInfo->mpName )
900 // Ignore vertical fonts
901 if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
903 OUString aName(o3tl::toU(pLogFont->elfLogFont.lfFaceName));
904 pInfo->mpName = &aName;
905 memcpy(pInfo->mpLogFont->lfFaceName, pLogFont->elfLogFont.lfFaceName, (aName.getLength()+1)*sizeof(wchar_t));
906 pInfo->mpLogFont->lfCharSet = pLogFont->elfLogFont.lfCharSet;
907 EnumFontFamiliesExW(pInfo->mhDC, pInfo->mpLogFont, SalEnumFontsProcExW,
908 reinterpret_cast<LPARAM>(pInfo), 0);
909 pInfo->mpLogFont->lfFaceName[0] = '\0';
910 pInfo->mpLogFont->lfCharSet = DEFAULT_CHARSET;
911 pInfo->mpName = nullptr;
914 else
916 // Ignore non-device fonts on printers.
917 if (pInfo->mbPrinter)
919 if ((nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE))
921 SAL_INFO("vcl.fonts", "Unsupported printer font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
922 return 1;
925 // Only SFNT fonts are supported, ignore anything else.
926 else if (!(nFontType & TRUETYPE_FONTTYPE) &&
927 !(pMetric->ntmTm.ntmFlags & NTM_PS_OPENTYPE) &&
928 !(pMetric->ntmTm.ntmFlags & NTM_TT_OPENTYPE))
930 SAL_INFO("vcl.fonts", "Unsupported font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
931 return 1;
934 rtl::Reference<WinFontFace> pData = new WinFontFace(*pLogFont, pMetric->ntmTm);
935 pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
937 pInfo->mpList->Add( pData.get() );
938 SAL_INFO("vcl.fonts", "SalEnumFontsProcExW: font added: " << pData->GetFamilyName() << " " << pData->GetStyleName());
941 return 1;
944 struct TempFontItem
946 OUString maFontResourcePath;
947 TempFontItem* mpNextItem;
950 static int lcl_AddFontResource(SalData& rSalData, const OUString& rFontFileURL, bool bShared)
952 OUString aFontSystemPath;
953 OSL_VERIFY(!osl::FileBase::getSystemPathFromFileURL(rFontFileURL, aFontSystemPath));
955 int nRet = AddFontResourceExW(o3tl::toW(aFontSystemPath.getStr()), FR_PRIVATE, nullptr);
956 SAL_WARN_IF(nRet <= 0, "vcl.fonts", "AddFontResourceExW failed for " << rFontFileURL);
957 if (nRet > 0)
959 TempFontItem* pNewItem = new TempFontItem;
960 pNewItem->maFontResourcePath = aFontSystemPath;
961 if (bShared)
963 pNewItem->mpNextItem = rSalData.mpSharedTempFontItem;
964 rSalData.mpSharedTempFontItem = pNewItem;
966 else
968 pNewItem->mpNextItem = rSalData.mpOtherTempFontItem;
969 rSalData.mpOtherTempFontItem = pNewItem;
972 return nRet;
975 void ImplReleaseTempFonts(SalData& rSalData, bool bAll)
977 while (TempFontItem* p = rSalData.mpOtherTempFontItem)
979 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
980 rSalData.mpOtherTempFontItem = p->mpNextItem;
981 delete p;
984 if (!bAll)
985 return;
987 while (TempFontItem* p = rSalData.mpSharedTempFontItem)
989 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
990 rSalData.mpSharedTempFontItem = p->mpNextItem;
991 delete p;
995 static OUString lcl_GetFontFamilyName(std::u16string_view rFontFileURL)
997 // Create temporary file name
998 OUString aTempFileURL;
999 if (osl::File::E_None != osl::File::createTempFile(nullptr, nullptr, &aTempFileURL))
1000 return OUString();
1001 osl::File::remove(aTempFileURL);
1002 OUString aResSystemPath;
1003 osl::FileBase::getSystemPathFromFileURL(aTempFileURL, aResSystemPath);
1005 // Create font resource file (.fot)
1006 // There is a limit of 127 characters for the full path passed via lpszFile, so we have to
1007 // split the font URL and pass it as two parameters. As a result we can't use
1008 // CreateScalableFontResource for renaming, as it now expects the font in the system path.
1009 // But it's still good to use it for family name extraction, we're currently after.
1010 // BTW: it doesn't help to prefix the lpszFile with \\?\ to support larger paths.
1011 // TODO: use TTLoadEmbeddedFont (needs an EOT as input, so we have to add a header to the TTF)
1012 // TODO: forward the EOT from the AddTempDevFont call side, if VCL supports it
1013 INetURLObject aTTFUrl(rFontFileURL);
1014 // GetBase() strips the extension
1015 OUString aFilename = aTTFUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
1016 if (!CreateScalableFontResourceW(0, o3tl::toW(aResSystemPath.getStr()),
1017 o3tl::toW(aFilename.getStr()), o3tl::toW(aTTFUrl.GetPath().getStr())))
1019 sal_uInt32 nError = GetLastError();
1020 SAL_WARN("vcl.fonts", "CreateScalableFontResource failed for " << aResSystemPath << " "
1021 << aFilename << " " << aTTFUrl.GetPath() << " " << nError);
1022 return OUString();
1025 // Open and read the font resource file
1026 osl::File aFotFile(aTempFileURL);
1027 if (osl::FileBase::E_None != aFotFile.open(osl_File_OpenFlag_Read))
1028 return OUString();
1030 sal_uInt64 nBytesRead = 0;
1031 char aBuffer[4096];
1032 aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead );
1033 // clean up temporary resource file
1034 aFotFile.close();
1035 osl::File::remove(aTempFileURL);
1037 // retrieve font family name from byte offset 0x4F6
1038 static const sal_uInt64 nNameOfs = 0x4F6;
1039 sal_uInt64 nPos = nNameOfs;
1040 for (; (nPos < nBytesRead) && (aBuffer[nPos] != 0); nPos++);
1041 if (nPos >= nBytesRead || (nPos == nNameOfs))
1042 return OUString();
1044 return OUString(aBuffer + nNameOfs, nPos - nNameOfs, osl_getThreadTextEncoding());
1047 bool WinSalGraphics::AddTempDevFont(vcl::font::PhysicalFontCollection* pFontCollection,
1048 const OUString& rFontFileURL, const OUString& rFontName)
1050 OUString aFontFamily = lcl_GetFontFamilyName(rFontFileURL);
1051 if (aFontFamily.isEmpty())
1053 SAL_WARN("vcl.fonts", "error extracting font family from " << rFontFileURL);
1054 return false;
1057 if (rFontName != aFontFamily)
1059 SAL_WARN("vcl.fonts", "font family renaming not implemented; skipping embedded " << rFontName);
1060 return false;
1063 int nFonts = lcl_AddFontResource(*GetSalData(), rFontFileURL, false);
1064 if (nFonts <= 0)
1065 return false;
1067 ImplEnumInfo aInfo;
1068 aInfo.mhDC = getHDC();
1069 aInfo.mpList = pFontCollection;
1070 aInfo.mpName = &aFontFamily;
1071 aInfo.mbPrinter = mbPrinter;
1072 aInfo.mnFontCount = pFontCollection->Count();
1073 const int nExpectedFontCount = aInfo.mnFontCount + nFonts;
1075 LOGFONTW aLogFont = {};
1076 aLogFont.lfCharSet = DEFAULT_CHARSET;
1077 aInfo.mpLogFont = &aLogFont;
1079 // add the font to the PhysicalFontCollection
1080 EnumFontFamiliesExW(getHDC(), &aLogFont,
1081 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0);
1083 SAL_WARN_IF(nExpectedFontCount != pFontCollection->Count(), "vcl.fonts",
1084 "temp font was registered but is not in enumeration: " << rFontFileURL);
1086 return true;
1089 void WinSalGraphics::GetDevFontList( vcl::font::PhysicalFontCollection* pFontCollection )
1091 // make sure all LO shared fonts are registered temporarily
1092 static std::once_flag init;
1093 std::call_once(init, []()
1095 auto registerFontsIn = [](const OUString& dir) {
1096 // collect fonts in font path that could not be registered
1097 osl::Directory aFontDir(dir);
1098 osl::FileBase::RC rcOSL = aFontDir.open();
1099 if (rcOSL == osl::FileBase::E_None)
1101 osl::DirectoryItem aDirItem;
1102 SalData* pSalData = GetSalData();
1103 assert(pSalData);
1105 while (aFontDir.getNextItem(aDirItem, 10) == osl::FileBase::E_None)
1107 osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
1108 rcOSL = aDirItem.getFileStatus(aFileStatus);
1109 if (rcOSL == osl::FileBase::E_None)
1110 lcl_AddFontResource(*pSalData, aFileStatus.getFileURL(), true);
1115 // determine font path
1116 // since we are only interested in fonts that could not be
1117 // registered before because of missing administration rights
1118 // only the font path of the user installation is needed
1119 OUString aPath("$BRAND_BASE_DIR");
1120 rtl_bootstrap_expandMacros(&aPath.pData);
1122 // internal font resources, required for normal operation, like OpenSymbol
1123 registerFontsIn(aPath + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts");
1125 // collect fonts in font path that could not be registered
1126 registerFontsIn(aPath + "/" LIBO_SHARE_FOLDER "/fonts/truetype");
1128 return true;
1131 ImplEnumInfo aInfo;
1132 aInfo.mhDC = getHDC();
1133 aInfo.mpList = pFontCollection;
1134 aInfo.mpName = nullptr;
1135 aInfo.mbPrinter = mbPrinter;
1136 aInfo.mnFontCount = 0;
1138 LOGFONTW aLogFont = {};
1139 aLogFont.lfCharSet = DEFAULT_CHARSET;
1140 aInfo.mpLogFont = &aLogFont;
1142 // fill the PhysicalFontCollection
1143 EnumFontFamiliesExW( getHDC(), &aLogFont,
1144 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0 );
1146 // set glyph fallback hook
1147 static WinGlyphFallbackSubstititution aSubstFallback;
1148 static WinPreMatchFontSubstititution aPreMatchFont;
1149 pFontCollection->SetFallbackHook( &aSubstFallback );
1150 pFontCollection->SetPreMatchHook(&aPreMatchFont);
1153 void WinSalGraphics::ClearDevFontCache()
1155 mWinSalGraphicsImplBase->ClearDevFontCache();
1156 ImplReleaseTempFonts(*GetSalData(), false);
1159 bool WinFontInstance::ImplGetGlyphBoundRect(sal_GlyphId nId, tools::Rectangle& rRect, bool bIsVertical) const
1161 assert(m_pGraphics);
1162 HDC hDC = m_pGraphics->getHDC();
1163 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1164 const HFONT hFont = GetHFONT();
1165 if (hFont != hOrigFont)
1166 SelectObject(hDC, hFont);
1168 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1169 { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1170 const float fFontScale = GetScale();
1172 // use unity matrix
1173 MAT2 aMat;
1174 const vcl::font::FontSelectPattern& rFSD = GetFontSelectPattern();
1176 // Use identity matrix for fonts requested in horizontal
1177 // writing (LTR or RTL), or rotated glyphs in vertical writing.
1178 if (!rFSD.mbVertical || !bIsVertical)
1180 aMat.eM11 = aMat.eM22 = FixedFromDouble(1.0);
1181 aMat.eM12 = aMat.eM21 = FixedFromDouble(0.0);
1183 else
1185 constexpr double nCos = 0.0;
1186 constexpr double nSin = 1.0;
1187 aMat.eM11 = FixedFromDouble(nCos);
1188 aMat.eM12 = FixedFromDouble(nSin);
1189 aMat.eM21 = FixedFromDouble(-nSin);
1190 aMat.eM22 = FixedFromDouble(nCos);
1193 UINT nGGOFlags = GGO_METRICS;
1194 nGGOFlags |= GGO_GLYPH_INDEX;
1196 GLYPHMETRICS aGM;
1197 aGM.gmptGlyphOrigin.x = aGM.gmptGlyphOrigin.y = 0;
1198 aGM.gmBlackBoxX = aGM.gmBlackBoxY = 0;
1199 DWORD nSize = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGM, 0, nullptr, &aMat);
1200 if (nSize == GDI_ERROR)
1201 return false;
1203 rRect = tools::Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
1204 Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
1205 rRect.SetLeft(static_cast<int>( fFontScale * rRect.Left() ));
1206 rRect.SetRight(static_cast<int>( fFontScale * rRect.Right() ) + 1);
1207 rRect.SetTop(static_cast<int>( fFontScale * rRect.Top() ));
1208 rRect.SetBottom(static_cast<int>( fFontScale * rRect.Bottom() ) + 1);
1209 return true;
1212 bool WinFontInstance::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rB2DPolyPoly, bool) const
1214 rB2DPolyPoly.clear();
1216 assert(m_pGraphics);
1217 HDC hDC = m_pGraphics->getHDC();
1218 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1219 const HFONT hFont = GetHFONT();
1220 if (hFont != hOrigFont)
1221 SelectObject(hDC, hFont);
1223 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1224 { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1226 // use unity matrix
1227 MAT2 aMat;
1228 aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1229 aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1231 UINT nGGOFlags = GGO_NATIVE;
1232 nGGOFlags |= GGO_GLYPH_INDEX;
1234 GLYPHMETRICS aGlyphMetrics;
1235 const DWORD nSize1 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGlyphMetrics, 0, nullptr, &aMat);
1236 if( !nSize1 ) // blank glyphs are ok
1237 return true;
1238 else if( nSize1 == GDI_ERROR )
1239 return false;
1241 BYTE* pData = new BYTE[ nSize1 ];
1242 const DWORD nSize2 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags,
1243 &aGlyphMetrics, nSize1, pData, &aMat );
1245 if( nSize1 != nSize2 )
1246 return false;
1248 // TODO: avoid tools polygon by creating B2DPolygon directly
1249 int nPtSize = 512;
1250 Point* pPoints = new Point[ nPtSize ];
1251 PolyFlags* pFlags = new PolyFlags[ nPtSize ];
1253 TTPOLYGONHEADER* pHeader = reinterpret_cast<TTPOLYGONHEADER*>(pData);
1254 while( reinterpret_cast<BYTE*>(pHeader) < pData+nSize2 )
1256 // only outline data is interesting
1257 if( pHeader->dwType != TT_POLYGON_TYPE )
1258 break;
1260 // get start point; next start points are end points
1261 // of previous segment
1262 sal_uInt16 nPnt = 0;
1264 tools::Long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
1265 tools::Long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
1266 pPoints[ nPnt ] = Point( nX, nY );
1267 pFlags[ nPnt++ ] = PolyFlags::Normal;
1269 bool bHasOfflinePoints = false;
1270 TTPOLYCURVE* pCurve = reinterpret_cast<TTPOLYCURVE*>( pHeader + 1 );
1271 pHeader = reinterpret_cast<TTPOLYGONHEADER*>( reinterpret_cast<BYTE*>(pHeader) + pHeader->cb );
1272 while( reinterpret_cast<BYTE*>(pCurve) < reinterpret_cast<BYTE*>(pHeader) )
1274 int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
1275 if( nPtSize < nNeededSize )
1277 Point* pOldPoints = pPoints;
1278 PolyFlags* pOldFlags = pFlags;
1279 nPtSize = 2 * nNeededSize;
1280 pPoints = new Point[ nPtSize ];
1281 pFlags = new PolyFlags[ nPtSize ];
1282 for( sal_uInt16 i = 0; i < nPnt; ++i )
1284 pPoints[ i ] = pOldPoints[ i ];
1285 pFlags[ i ] = pOldFlags[ i ];
1287 delete[] pOldPoints;
1288 delete[] pOldFlags;
1291 int i = 0;
1292 if( TT_PRIM_LINE == pCurve->wType )
1294 while( i < pCurve->cpfx )
1296 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1297 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1298 ++i;
1299 pPoints[ nPnt ] = Point( nX, nY );
1300 pFlags[ nPnt ] = PolyFlags::Normal;
1301 ++nPnt;
1304 else if( TT_PRIM_QSPLINE == pCurve->wType )
1306 bHasOfflinePoints = true;
1307 while( i < pCurve->cpfx )
1309 // get control point of quadratic bezier spline
1310 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1311 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1312 ++i;
1313 Point aControlP( nX, nY );
1315 // calculate first cubic control point
1316 // P0 = 1/3 * (PBeg + 2 * PQControl)
1317 nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
1318 nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
1319 pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1320 pFlags[ nPnt+0 ] = PolyFlags::Control;
1322 // calculate endpoint of segment
1323 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1324 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1326 if ( i+1 >= pCurve->cpfx )
1328 // endpoint is either last point in segment => advance
1329 ++i;
1331 else
1333 // or endpoint is the middle of two control points
1334 nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
1335 nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
1336 nX = (nX + 1) / 2;
1337 nY = (nY + 1) / 2;
1338 // no need to advance, because the current point
1339 // is the control point in next bezier spline
1342 pPoints[ nPnt+2 ] = Point( nX, nY );
1343 pFlags[ nPnt+2 ] = PolyFlags::Normal;
1345 // calculate second cubic control point
1346 // P1 = 1/3 * (PEnd + 2 * PQControl)
1347 nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
1348 nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
1349 pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1350 pFlags[ nPnt+1 ] = PolyFlags::Control;
1352 nPnt += 3;
1356 // next curve segment
1357 pCurve = reinterpret_cast<TTPOLYCURVE*>(&pCurve->apfx[ i ]);
1360 // end point is start point for closed contour
1361 // disabled, because Polygon class closes the contour itself
1362 // pPoints[nPnt++] = pPoints[0];
1363 // #i35928#
1364 // Added again, but add only when not yet closed
1365 if(pPoints[nPnt - 1] != pPoints[0])
1367 if( bHasOfflinePoints )
1368 pFlags[nPnt] = pFlags[0];
1370 pPoints[nPnt++] = pPoints[0];
1373 // convert y-coordinates W32 -> VCL
1374 for( int i = 0; i < nPnt; ++i )
1375 pPoints[i].setY(-pPoints[i].Y());
1377 // insert into polypolygon
1378 tools::Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : nullptr) );
1379 // convert to B2DPolyPolygon
1380 // TODO: get rid of the intermediate PolyPolygon
1381 rB2DPolyPoly.append( aPoly.getB2DPolygon() );
1384 delete[] pPoints;
1385 delete[] pFlags;
1387 delete[] pData;
1389 // rescaling needed for the tools::PolyPolygon conversion
1390 if( rB2DPolyPoly.count() )
1392 const double fFactor(GetScale()/256);
1393 rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
1396 return true;
1399 IDWriteFontFace* WinFontInstance::GetDWFontFace() const
1401 if (!mxDWFontFace)
1403 assert(m_pGraphics);
1404 HDC hDC = m_pGraphics->getHDC();
1405 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1406 const HFONT hFont = GetHFONT();
1407 if (hFont != hOrigFont)
1408 SelectObject(hDC, hFont);
1410 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]() {
1411 if (hFont != hOrigFont)
1412 SelectObject(hDC, hOrigFont);
1415 IDWriteGdiInterop* pDWriteGdiInterop;
1416 WinSalGraphics::getDWriteFactory(nullptr, &pDWriteGdiInterop);
1418 HRESULT hr = pDWriteGdiInterop->CreateFontFaceFromHdc(hDC, &mxDWFontFace);
1419 if (FAILED(hr))
1421 SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
1422 << WindowsErrorStringFromHRESULT(hr));
1423 mxDWFontFace = nullptr;
1427 return mxDWFontFace;
1430 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */