Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / vcl / win / gdi / salfont.cxx
blob04f3b46ce6e6814f838c65c01fda7196e55c46f0
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/types.h>
21 #include <config_folders.h>
23 #include <algorithm>
24 #include <map>
25 #include <memory>
26 #include <mutex>
27 #include <set>
28 #include <string.h>
29 #include <svsys.h>
30 #include <vector>
32 #include <o3tl/lru_map.hxx>
33 #include <basegfx/matrix/b2dhommatrixtools.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <i18nlangtag/mslangid.hxx>
36 #include <osl/file.hxx>
37 #include <osl/process.h>
38 #include <rtl/bootstrap.hxx>
39 #include <rtl/tencinfo.h>
40 #include <sal/log.hxx>
41 #include <o3tl/char16_t2wchar_t.hxx>
42 #include <tools/helpers.hxx>
43 #include <tools/stream.hxx>
44 #include <tools/urlobj.hxx>
45 #include <unotools/fontcfg.hxx>
46 #include <vcl/settings.hxx>
47 #include <vcl/sysdata.hxx>
48 #include <vcl/metric.hxx>
49 #include <vcl/fontcharmap.hxx>
50 #include <vcl/opengl/OpenGLWrapper.hxx>
51 #include <comphelper/scopeguard.hxx>
53 #include <fontsubset.hxx>
54 #include <outdev.h>
55 #include <PhysicalFontCollection.hxx>
56 #include <PhysicalFontFace.hxx>
57 #include <sft.hxx>
58 #include <win/saldata.hxx>
59 #include <win/salgdi.h>
60 #include <win/winlayout.hxx>
61 #include <win/wingdiimpl.hxx>
62 #include <impfontcharmap.hxx>
63 #include <impfontmetricdata.hxx>
64 #include <impglyphitem.hxx>
66 using namespace vcl;
68 static FIXED FixedFromDouble( double d )
70 const tools::Long l = static_cast<tools::Long>( d * 65536. );
71 return *reinterpret_cast<FIXED const *>(&l);
74 static int IntTimes256FromFixed(FIXED f)
76 int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
77 return nFixedTimes256;
80 namespace {
82 // raw font data with a scoped lifetime
83 class RawFontData
85 public:
86 explicit RawFontData( HDC, DWORD nTableTag=0 );
87 const unsigned char* get() const { return mpRawBytes.get(); }
88 const unsigned char* steal() { return mpRawBytes.release(); }
89 int size() const { return mnByteCount; }
91 private:
92 std::unique_ptr<unsigned char[]> mpRawBytes;
93 unsigned mnByteCount;
98 RawFontData::RawFontData( HDC hDC, DWORD nTableTag )
99 : mnByteCount( 0 )
101 // get required size in bytes
102 mnByteCount = ::GetFontData( hDC, nTableTag, 0, nullptr, 0 );
103 if (mnByteCount == GDI_ERROR)
104 mnByteCount = 0;
105 if (!mnByteCount)
106 return;
108 // allocate the array
109 mpRawBytes.reset(new unsigned char[ mnByteCount ]);
111 // get raw data in chunks small enough for GetFontData()
112 unsigned nRawDataOfs = 0;
113 DWORD nMaxChunkSize = 0x100000;
114 for(;;)
116 // calculate remaining raw data to get
117 DWORD nFDGet = mnByteCount - nRawDataOfs;
118 if( nFDGet <= 0 )
119 break;
120 // #i56745# limit GetFontData requests
121 if( nFDGet > nMaxChunkSize )
122 nFDGet = nMaxChunkSize;
123 const DWORD nFDGot = ::GetFontData( hDC, nTableTag, nRawDataOfs,
124 mpRawBytes.get() + nRawDataOfs, nFDGet );
125 if( !nFDGot )
126 break;
127 else if( nFDGot != GDI_ERROR )
128 nRawDataOfs += nFDGot;
129 else
131 // was the chunk too big? reduce it
132 nMaxChunkSize /= 2;
133 if( nMaxChunkSize < 0x10000 )
134 break;
138 // cleanup if the raw data is incomplete
139 if( nRawDataOfs != mnByteCount )
141 mpRawBytes.reset();
142 // mnByteCount must correspond to mpRawBytes length
143 SAL_WARN( "vcl", "Raw data of font is incomplete: " << nRawDataOfs << " byte(s) found whereas " << mnByteCount << " byte(s) expected!" );
144 mnByteCount = 0;
148 // platform specific font substitution hooks for glyph fallback enhancement
150 namespace {
152 class WinPreMatchFontSubstititution
153 : public ImplPreMatchFontSubstitution
155 public:
156 bool FindFontSubstitute(FontSelectPattern&) const override;
159 class WinGlyphFallbackSubstititution
160 : public ImplGlyphFallbackFontSubstitution
162 public:
163 explicit WinGlyphFallbackSubstititution()
164 : mhDC(GetDC(nullptr))
168 ~WinGlyphFallbackSubstititution() override
170 ReleaseDC(nullptr, mhDC);
173 bool FindFontSubstitute(FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
174 private:
175 HDC mhDC;
176 bool HasMissingChars(PhysicalFontFace*, OUString& rMissingChars) const;
181 // does a font face hold the given missing characters?
182 bool WinGlyphFallbackSubstititution::HasMissingChars(PhysicalFontFace* pFace, OUString& rMissingChars) const
184 WinFontFace* pWinFont = static_cast< WinFontFace* >(pFace);
185 FontCharMapRef xFontCharMap = pWinFont->GetFontCharMap();
186 if( !xFontCharMap.is() )
188 // construct a Size structure as the parameter of constructor of class FontSelectPattern
189 const Size aSize( pFace->GetWidth(), pFace->GetHeight() );
190 // create a FontSelectPattern object for getting s LOGFONT
191 const FontSelectPattern aFSD( *pFace, aSize, static_cast<float>(aSize.Height()), 0, false );
192 // construct log font
193 LOGFONTW aLogFont;
194 ImplGetLogFontFromFontSelect( mhDC, aFSD, pFace, aLogFont );
196 // create HFONT from log font
197 HFONT hNewFont = ::CreateFontIndirectW( &aLogFont );
198 // select the new font into device
199 HFONT hOldFont = ::SelectFont( mhDC, hNewFont );
201 // read CMAP table to update their xFontCharMap
202 pWinFont->UpdateFromHDC( mhDC );
204 // cleanup temporary font
205 ::SelectFont( mhDC, hOldFont );
206 ::DeleteFont( hNewFont );
208 // get the new charmap
209 xFontCharMap = pWinFont->GetFontCharMap();
212 // avoid fonts with unknown CMAP subtables for glyph fallback
213 if( !xFontCharMap.is() || xFontCharMap->IsDefaultMap() )
214 return false;
216 int nMatchCount = 0;
217 std::vector<sal_UCS4> rRemainingCodes;
218 const sal_Int32 nStrLen = rMissingChars.getLength();
219 sal_Int32 nStrIdx = 0;
220 while (nStrIdx < nStrLen)
222 const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
223 if (xFontCharMap->HasChar(uChar))
224 nMatchCount++;
225 else
226 rRemainingCodes.push_back(uChar);
229 xFontCharMap = nullptr;
231 if (nMatchCount > 0)
232 rMissingChars = OUString(rRemainingCodes.data(), rRemainingCodes.size());
234 return nMatchCount > 0;
237 namespace
239 //used by 2-level font fallback
240 PhysicalFontFamily* findDevFontListByLocale(const PhysicalFontCollection &rFontCollection,
241 const LanguageTag& rLanguageTag )
243 // get the default font for a specified locale
244 const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
245 const OUString aDefault = rDefaults.getUserInterfaceFont(rLanguageTag);
246 return rFontCollection.FindFontFamilyByTokenNames(aDefault);
250 // These are Win 3.1 bitmap fonts using "FON" font format
251 // which is not supported with DirectWrite so let's substitute them
252 // with a font that is supported and always available.
253 // Based on:
254 // https://dxr.mozilla.org/mozilla-esr10/source/gfx/thebes/gfxDWriteFontList.cpp#1057
255 const std::map<OUString, OUString> aBitmapFontSubs =
257 { "MS Sans Serif", "Microsoft Sans Serif" },
258 { "MS Serif", "Times New Roman" },
259 { "Small Fonts", "Arial" },
260 { "Courier", "Courier New" },
261 { "Roman", "Times New Roman" },
262 { "Script", "Mistral" }
265 // TODO: See if Windows have API that we can use here to improve font fallback.
266 bool WinPreMatchFontSubstititution::FindFontSubstitute(FontSelectPattern& rFontSelData) const
268 if (rFontSelData.IsSymbolFont() || IsStarSymbol(rFontSelData.maSearchName))
269 return false;
271 for (const auto& aSub : aBitmapFontSubs)
273 if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
275 rFontSelData.maSearchName = aSub.second;
276 return true;
280 return false;
283 // find a fallback font for missing characters
284 // TODO: should stylistic matches be searched and preferred?
285 bool WinGlyphFallbackSubstititution::FindFontSubstitute(FontSelectPattern& rFontSelData, LogicalFontInstance* /*pLogicalFont*/, OUString& rMissingChars) const
287 // guess a locale matching to the missing chars
288 LanguageType eLang = rFontSelData.meLanguage;
289 LanguageTag aLanguageTag( eLang);
291 // fall back to default UI locale if the font language is inconclusive
292 if( eLang == LANGUAGE_DONTKNOW )
293 aLanguageTag = Application::GetSettings().GetUILanguageTag();
295 // first level fallback:
296 // try use the locale specific default fonts defined in VCL.xcu
297 const PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
298 PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag);
299 if( pFontFamily )
301 PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
302 if( HasMissingChars( pFace, rMissingChars ) )
304 rFontSelData.maSearchName = pFontFamily->GetSearchName();
305 return true;
309 // are the missing characters symbols?
310 pFontFamily = pFontCollection->FindFontFamilyByAttributes( ImplFontAttrs::Symbol,
311 rFontSelData.GetWeight(),
312 rFontSelData.GetWidthType(),
313 rFontSelData.GetItalic(),
314 rFontSelData.maSearchName );
315 if( pFontFamily )
317 PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
318 if( HasMissingChars( pFace, rMissingChars ) )
320 rFontSelData.maSearchName = pFontFamily->GetSearchName();
321 return true;
325 // last level fallback, check each font type face one by one
326 std::unique_ptr<ImplDeviceFontList> pTestFontList = pFontCollection->GetDeviceFontList();
327 // limit the count of fonts to be checked to prevent hangs
328 static const int MAX_GFBFONT_COUNT = 600;
329 int nTestFontCount = pTestFontList->Count();
330 if( nTestFontCount > MAX_GFBFONT_COUNT )
331 nTestFontCount = MAX_GFBFONT_COUNT;
333 bool bFound = false;
334 for( int i = 0; i < nTestFontCount; ++i )
336 PhysicalFontFace* pFace = pTestFontList->Get( i );
337 bFound = HasMissingChars( pFace, rMissingChars );
338 if( !bFound )
339 continue;
340 rFontSelData.maSearchName = pFace->GetFamilyName();
341 break;
344 return bFound;
347 namespace {
349 struct ImplEnumInfo
351 HDC mhDC;
352 PhysicalFontCollection* mpList;
353 OUString* mpName;
354 LOGFONTW* mpLogFont;
355 bool mbPrinter;
356 int mnFontCount;
361 static rtl_TextEncoding ImplCharSetToSal( BYTE nCharSet )
363 rtl_TextEncoding eTextEncoding;
365 if ( nCharSet == OEM_CHARSET )
367 UINT nCP = static_cast<sal_uInt16>(GetOEMCP());
368 switch ( nCP )
370 // It is unclear why these two (undefined?) code page numbers are
371 // handled specially here:
372 case 1004: eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
373 case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
374 default:
375 eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
376 break;
379 else
381 if( nCharSet )
382 eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
383 else
384 eTextEncoding = RTL_TEXTENCODING_UNICODE;
387 return eTextEncoding;
390 static FontFamily ImplFamilyToSal( BYTE nFamily )
392 switch ( nFamily & 0xF0 )
394 case FF_DECORATIVE:
395 return FAMILY_DECORATIVE;
397 case FF_MODERN:
398 return FAMILY_MODERN;
400 case FF_ROMAN:
401 return FAMILY_ROMAN;
403 case FF_SCRIPT:
404 return FAMILY_SCRIPT;
406 case FF_SWISS:
407 return FAMILY_SWISS;
409 default:
410 break;
413 return FAMILY_DONTKNOW;
416 static BYTE ImplFamilyToWin( FontFamily eFamily )
418 switch ( eFamily )
420 case FAMILY_DECORATIVE:
421 return FF_DECORATIVE;
423 case FAMILY_MODERN:
424 return FF_MODERN;
426 case FAMILY_ROMAN:
427 return FF_ROMAN;
429 case FAMILY_SCRIPT:
430 return FF_SCRIPT;
432 case FAMILY_SWISS:
433 return FF_SWISS;
435 case FAMILY_SYSTEM:
436 return FF_SWISS;
438 default:
439 break;
442 return FF_DONTCARE;
445 static FontWeight ImplWeightToSal( int nWeight )
447 if ( nWeight <= FW_THIN )
448 return WEIGHT_THIN;
449 else if ( nWeight <= FW_ULTRALIGHT )
450 return WEIGHT_ULTRALIGHT;
451 else if ( nWeight <= FW_LIGHT )
452 return WEIGHT_LIGHT;
453 else if ( nWeight < FW_MEDIUM )
454 return WEIGHT_NORMAL;
455 else if ( nWeight == FW_MEDIUM )
456 return WEIGHT_MEDIUM;
457 else if ( nWeight <= FW_SEMIBOLD )
458 return WEIGHT_SEMIBOLD;
459 else if ( nWeight <= FW_BOLD )
460 return WEIGHT_BOLD;
461 else if ( nWeight <= FW_ULTRABOLD )
462 return WEIGHT_ULTRABOLD;
463 else
464 return WEIGHT_BLACK;
467 static int ImplWeightToWin( FontWeight eWeight )
469 switch ( eWeight )
471 case WEIGHT_THIN:
472 return FW_THIN;
474 case WEIGHT_ULTRALIGHT:
475 return FW_ULTRALIGHT;
477 case WEIGHT_LIGHT:
478 return FW_LIGHT;
480 case WEIGHT_SEMILIGHT:
481 case WEIGHT_NORMAL:
482 return FW_NORMAL;
484 case WEIGHT_MEDIUM:
485 return FW_MEDIUM;
487 case WEIGHT_SEMIBOLD:
488 return FW_SEMIBOLD;
490 case WEIGHT_BOLD:
491 return FW_BOLD;
493 case WEIGHT_ULTRABOLD:
494 return FW_ULTRABOLD;
496 case WEIGHT_BLACK:
497 return FW_BLACK;
499 default:
500 break;
503 return 0;
506 static FontPitch ImplLogPitchToSal( BYTE nPitch )
508 if ( nPitch & FIXED_PITCH )
509 return PITCH_FIXED;
510 else
511 return PITCH_VARIABLE;
514 static FontPitch ImplMetricPitchToSal( BYTE nPitch )
516 // Grrrr! See NT help
517 if ( !(nPitch & TMPF_FIXED_PITCH) )
518 return PITCH_FIXED;
519 else
520 return PITCH_VARIABLE;
523 static BYTE ImplPitchToWin( FontPitch ePitch )
525 if ( ePitch == PITCH_FIXED )
526 return FIXED_PITCH;
527 else if ( ePitch == PITCH_VARIABLE )
528 return VARIABLE_PITCH;
529 else
530 return DEFAULT_PITCH;
533 static FontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
534 const NEWTEXTMETRICW& rMetric)
536 FontAttributes aDFA;
538 const LOGFONTW rLogFont = rEnumFont.elfLogFont;
540 // get font face attributes
541 aDFA.SetFamilyType(ImplFamilyToSal( rLogFont.lfPitchAndFamily ));
542 aDFA.SetWidthType(WIDTH_DONTKNOW);
543 aDFA.SetWeight(ImplWeightToSal( rLogFont.lfWeight ));
544 aDFA.SetItalic((rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE);
545 aDFA.SetPitch(ImplLogPitchToSal( rLogFont.lfPitchAndFamily ));
546 aDFA.SetSymbolFlag(rLogFont.lfCharSet == SYMBOL_CHARSET);
548 // get the font face name
549 aDFA.SetFamilyName(OUString(o3tl::toU(rLogFont.lfFaceName)));
551 // use the face's style name only if it looks reasonable
552 const wchar_t* pStyleName = rEnumFont.elfStyle;
553 const wchar_t* pEnd = pStyleName + sizeof(rEnumFont.elfStyle)/sizeof(*rEnumFont.elfStyle);
554 const wchar_t* p = pStyleName;
555 for(; *p && (p < pEnd); ++p )
556 if( *p < 0x0020 )
557 break;
558 if( p < pEnd )
559 aDFA.SetStyleName(OUString(o3tl::toU(pStyleName)));
561 // heuristics for font quality
562 // - opentypeTT > truetype
563 aDFA.SetQuality( 0 );
564 if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
565 aDFA.IncreaseQualityBy( 50 );
566 if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
567 aDFA.IncreaseQualityBy( 10 );
569 // TODO: add alias names
570 return aDFA;
574 static rtl::Reference<WinFontFace> ImplLogMetricToDevFontDataW( const ENUMLOGFONTEXW* pLogFont,
575 const NEWTEXTMETRICW* pMetric)
577 rtl::Reference<WinFontFace> pData = new WinFontFace(
578 WinFont2DevFontAttributes(*pLogFont, *pMetric),
579 pLogFont->elfLogFont.lfCharSet,
580 pMetric->tmPitchAndFamily );
582 return pData;
585 void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
587 OUString aFontName( o3tl::toU(rLogFont.lfFaceName) );
588 if (!aFontName.isEmpty())
590 rFont.SetFamilyName( aFontName );
591 rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
592 rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
593 rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
594 rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
596 tools::Long nFontHeight = rLogFont.lfHeight;
597 if ( nFontHeight < 0 )
598 nFontHeight = -nFontHeight;
599 tools::Long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
600 if( !nDPIY )
601 nDPIY = 600;
602 nFontHeight *= 72;
603 nFontHeight += nDPIY/2;
604 nFontHeight /= nDPIY;
605 rFont.SetFontSize( Size( 0, nFontHeight ) );
606 rFont.SetOrientation( Degree10(static_cast<sal_Int16>(rLogFont.lfEscapement)) );
607 if ( rLogFont.lfItalic )
608 rFont.SetItalic( ITALIC_NORMAL );
609 else
610 rFont.SetItalic( ITALIC_NONE );
611 if ( rLogFont.lfUnderline )
612 rFont.SetUnderline( LINESTYLE_SINGLE );
613 else
614 rFont.SetUnderline( LINESTYLE_NONE );
615 if ( rLogFont.lfStrikeOut )
616 rFont.SetStrikeout( STRIKEOUT_SINGLE );
617 else
618 rFont.SetStrikeout( STRIKEOUT_NONE );
622 WinFontFace::WinFontFace( const FontAttributes& rDFS,
623 BYTE eWinCharSet, BYTE nPitchAndFamily )
624 : PhysicalFontFace( rDFS ),
625 mnId( 0 ),
626 mbFontCapabilitiesRead( false ),
627 meWinCharSet( eWinCharSet ),
628 mnPitchAndFamily( nPitchAndFamily ),
629 mbAliasSymbolsHigh( false ),
630 mbAliasSymbolsLow( false )
632 if( eWinCharSet == SYMBOL_CHARSET )
634 if( (nPitchAndFamily & TMPF_TRUETYPE) != 0 )
636 // truetype fonts need their symbols as U+F0xx
637 mbAliasSymbolsHigh = true;
639 else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_DEVICE))
640 == (TMPF_VECTOR|TMPF_DEVICE) )
642 // scalable device fonts (e.g. builtin printer fonts)
643 // need their symbols as U+00xx
644 mbAliasSymbolsLow = true;
646 else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) == 0 )
648 // bitmap fonts need their symbols as U+F0xx
649 mbAliasSymbolsHigh = true;
654 WinFontFace::~WinFontFace()
656 mxUnicodeMap.clear();
659 sal_IntPtr WinFontFace::GetFontId() const
661 return mnId;
664 rtl::Reference<LogicalFontInstance> WinFontFace::CreateFontInstance(const FontSelectPattern& rFSD) const
666 return new WinFontInstance(*this, rFSD);
669 static DWORD CalcTag( const char p[5]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }
671 void WinFontFace::UpdateFromHDC( HDC hDC ) const
673 // short circuit if already initialized
674 if( mxUnicodeMap.is() )
675 return;
677 ReadCmapTable( hDC );
678 GetFontCapabilities( hDC );
681 FontCharMapRef WinFontFace::GetFontCharMap() const
683 return mxUnicodeMap;
686 bool WinFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
688 rFontCapabilities = maFontCapabilities;
689 return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
692 void WinFontFace::ReadCmapTable( HDC hDC ) const
694 if( mxUnicodeMap.is() )
695 return;
697 bool bIsSymbolFont = (meWinCharSet == SYMBOL_CHARSET);
698 // get the CMAP table from the font which is selected into the DC
699 const DWORD nCmapTag = CalcTag( "cmap" );
700 const RawFontData aRawFontData( hDC, nCmapTag );
701 // parse the CMAP table if available
702 if( aRawFontData.get() ) {
703 CmapResult aResult;
704 ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult );
705 aResult.mbSymbolic = bIsSymbolFont;
706 if( aResult.mnRangeCount > 0 )
708 FontCharMapRef pUnicodeMap(new FontCharMap(aResult));
709 mxUnicodeMap = pUnicodeMap;
713 if( !mxUnicodeMap.is() )
715 mxUnicodeMap = FontCharMap::GetDefaultMap( bIsSymbolFont );
719 void WinFontFace::GetFontCapabilities( HDC hDC ) const
721 // read this only once per font
722 if( mbFontCapabilitiesRead )
723 return;
725 mbFontCapabilitiesRead = true;
727 // OS/2 table
728 const DWORD OS2Tag = CalcTag( "OS/2" );
729 DWORD nLength = ::GetFontData( hDC, OS2Tag, 0, nullptr, 0 );
730 if( (nLength != GDI_ERROR) && nLength )
732 std::vector<unsigned char> aTable( nLength );
733 unsigned char* pTable = aTable.data();
734 ::GetFontData( hDC, OS2Tag, 0, pTable, nLength );
735 vcl::getTTCoverage(maFontCapabilities.oUnicodeRange, maFontCapabilities.oCodePageRange, pTable, nLength);
739 void WinSalGraphics::SetTextColor( Color nColor )
741 COLORREF aCol = PALETTERGB( nColor.GetRed(),
742 nColor.GetGreen(),
743 nColor.GetBlue() );
745 if( !mbPrinter &&
746 GetSalData()->mhDitherPal &&
747 ImplIsSysColorEntry( nColor ) )
749 aCol = PALRGB_TO_RGB( aCol );
752 ::SetTextColor( getHDC(), aCol );
755 static int CALLBACK SalEnumQueryFontProcExW( const LOGFONTW*,
756 const TEXTMETRICW*,
757 DWORD, LPARAM lParam )
759 *reinterpret_cast<bool*>(lParam) = true;
760 return 0;
763 void ImplGetLogFontFromFontSelect( HDC hDC,
764 const FontSelectPattern& rFont,
765 const PhysicalFontFace* pFontFace,
766 LOGFONTW& rLogFont )
768 OUString aName;
769 if (pFontFace)
770 aName = pFontFace->GetFamilyName();
771 else
772 aName = rFont.GetFamilyName().getToken( 0, ';' );
774 UINT nNameLen = aName.getLength();
775 if (nNameLen >= LF_FACESIZE)
776 nNameLen = LF_FACESIZE - 1;
777 memcpy( rLogFont.lfFaceName, aName.getStr(), nNameLen*sizeof( wchar_t ) );
778 rLogFont.lfFaceName[nNameLen] = 0;
780 if (pFontFace)
782 const WinFontFace* pWinFontData = static_cast<const WinFontFace*>(pFontFace);
783 rLogFont.lfCharSet = pWinFontData->GetCharSet();
784 rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
786 else
788 rLogFont.lfCharSet = rFont.IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
789 rLogFont.lfPitchAndFamily = ImplPitchToWin( rFont.GetPitch() )
790 | ImplFamilyToWin( rFont.GetFamilyType() );
793 static BYTE nDefaultQuality = NONANTIALIASED_QUALITY;
794 if (nDefaultQuality == NONANTIALIASED_QUALITY)
796 if (OpenGLWrapper::isVCLOpenGLEnabled())
797 nDefaultQuality = ANTIALIASED_QUALITY;
798 else
799 nDefaultQuality = DEFAULT_QUALITY;
802 rLogFont.lfWeight = ImplWeightToWin( rFont.GetWeight() );
803 rLogFont.lfHeight = static_cast<LONG>(-rFont.mnHeight);
804 rLogFont.lfWidth = static_cast<LONG>(rFont.mnWidth);
805 rLogFont.lfUnderline = 0;
806 rLogFont.lfStrikeOut = 0;
807 rLogFont.lfItalic = BYTE(rFont.GetItalic() != ITALIC_NONE);
808 rLogFont.lfEscapement = rFont.mnOrientation.get();
809 rLogFont.lfOrientation = rLogFont.lfEscapement;
810 rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
811 rLogFont.lfQuality = nDefaultQuality;
812 rLogFont.lfOutPrecision = OUT_TT_PRECIS;
813 if ( rFont.mnOrientation )
814 rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
816 // disable antialiasing if requested
817 if ( rFont.mbNonAntialiased )
818 rLogFont.lfQuality = NONANTIALIASED_QUALITY;
820 // select vertical mode if requested and available
821 if ( rFont.mbVertical && nNameLen )
823 // vertical fonts start with an '@'
824 memmove( &rLogFont.lfFaceName[1], &rLogFont.lfFaceName[0],
825 sizeof(rLogFont.lfFaceName)-sizeof(rLogFont.lfFaceName[0]) );
826 rLogFont.lfFaceName[0] = '@';
828 // check availability of vertical mode for this font
829 bool bAvailable = false;
830 EnumFontFamiliesExW( hDC, &rLogFont, SalEnumQueryFontProcExW,
831 reinterpret_cast<LPARAM>(&bAvailable), 0 );
833 if( !bAvailable )
835 // restore non-vertical name if not vertical mode isn't available
836 memcpy( &rLogFont.lfFaceName[0], aName.getStr(), nNameLen*sizeof(wchar_t) );
837 rLogFont.lfFaceName[nNameLen] = '\0';
838 // keep it upright and create the font for sideway glyphs later.
839 rLogFont.lfEscapement = rLogFont.lfEscapement - 2700;
840 rLogFont.lfOrientation = rLogFont.lfEscapement;
845 HFONT WinSalGraphics::ImplDoSetFont(FontSelectPattern const & i_rFont,
846 const PhysicalFontFace * i_pFontFace,
847 HFONT& o_rOldFont)
849 HFONT hNewFont = nullptr;
851 LOGFONTW aLogFont;
852 ImplGetLogFontFromFontSelect( getHDC(), i_rFont, i_pFontFace, aLogFont );
854 hNewFont = ::CreateFontIndirectW( &aLogFont );
856 HDC hdcScreen = nullptr;
857 if( mbVirDev )
858 // only required for virtual devices, see below for details
859 hdcScreen = GetDC(nullptr);
860 if( hdcScreen )
862 // select font into screen hdc first to get an antialiased font
863 // and instantly restore the default font!
864 // see knowledge base article 305290:
865 // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
866 SelectFont( hdcScreen, SelectFont( hdcScreen , hNewFont ) );
868 o_rOldFont = ::SelectFont( getHDC(), hNewFont );
870 TEXTMETRICW aTextMetricW;
871 if( !::GetTextMetricsW( getHDC(), &aTextMetricW ) )
873 // the selected font doesn't work => try a replacement
874 // TODO: use its font fallback instead
875 lstrcpynW( aLogFont.lfFaceName, L"Courier New", 12 );
876 aLogFont.lfPitchAndFamily = FIXED_PITCH;
877 HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
878 SelectFont( getHDC(), hNewFont2 );
879 DeleteFont( hNewFont );
880 hNewFont = hNewFont2;
883 if( hdcScreen )
884 ::ReleaseDC( nullptr, hdcScreen );
886 return hNewFont;
889 void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
891 // return early if there is no new font
892 if( !pFont )
894 if (!mpWinFontEntry[nFallbackLevel].is())
895 return;
897 // select original DC font
898 assert(mhDefFont);
899 ::SelectFont(getHDC(), mhDefFont);
900 mhDefFont = nullptr;
902 // release no longer referenced font handles
903 for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
904 mpWinFontEntry[i] = nullptr;
905 return;
908 WinFontInstance *pFontInstance = static_cast<WinFontInstance*>(pFont);
909 mpWinFontEntry[ nFallbackLevel ] = pFontInstance;
911 HFONT hOldFont = nullptr;
912 HFONT hNewFont = pFontInstance->GetHFONT();
913 if (!hNewFont)
915 pFontInstance->SetGraphics(this);
916 hNewFont = pFontInstance->GetHFONT();
918 hOldFont = ::SelectFont(getHDC(), hNewFont);
920 // keep default font
921 if( !mhDefFont )
922 mhDefFont = hOldFont;
923 else
925 // release no longer referenced font handles
926 for( int i = nFallbackLevel + 1; i < MAX_FALLBACK && mpWinFontEntry[i].is(); ++i )
927 mpWinFontEntry[i] = nullptr;
930 // now the font is live => update font face
931 const WinFontFace* pFontFace = pFontInstance->GetFontFace();
932 pFontFace->UpdateFromHDC(getHDC());
935 void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel )
937 // temporarily change the HDC to the font in the fallback level
938 rtl::Reference<WinFontInstance> pFontInstance = mpWinFontEntry[nFallbackLevel];
939 const HFONT hOldFont = SelectFont(getHDC(), pFontInstance->GetHFONT());
941 wchar_t aFaceName[LF_FACESIZE+60];
942 if( GetTextFaceW( getHDC(), SAL_N_ELEMENTS(aFaceName), aFaceName ) )
943 rxFontMetric->SetFamilyName(OUString(o3tl::toU(aFaceName)));
945 rxFontMetric->SetMinKashida(pFontInstance->GetKashidaWidth());
946 rxFontMetric->ImplCalcLineSpacing(pFontInstance.get());
948 // get the font metric
949 OUTLINETEXTMETRICW aOutlineMetric;
950 const bool bOK = GetOutlineTextMetricsW(getHDC(), sizeof(aOutlineMetric), &aOutlineMetric);
951 // restore the HDC to the font in the base level
952 SelectFont( getHDC(), hOldFont );
953 if( !bOK )
954 return;
956 TEXTMETRICW aWinMetric = aOutlineMetric.otmTextMetrics;
958 // device independent font attributes
959 rxFontMetric->SetFamilyType(ImplFamilyToSal( aWinMetric.tmPitchAndFamily ));
960 rxFontMetric->SetSymbolFlag(aWinMetric.tmCharSet == SYMBOL_CHARSET);
961 rxFontMetric->SetWeight(ImplWeightToSal( aWinMetric.tmWeight ));
962 rxFontMetric->SetPitch(ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily ));
963 rxFontMetric->SetItalic(aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE);
964 rxFontMetric->SetSlant( 0 );
966 // transformation dependent font metrics
967 rxFontMetric->SetWidth(static_cast<int>(pFontInstance->GetScale() * aWinMetric.tmAveCharWidth));
970 FontCharMapRef WinSalGraphics::GetFontCharMap() const
972 if (!mpWinFontEntry[0])
974 return FontCharMapRef( new FontCharMap() );
976 return mpWinFontEntry[0]->GetFontFace()->GetFontCharMap();
979 bool WinSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
981 if (!mpWinFontEntry[0])
982 return false;
983 return mpWinFontEntry[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
986 static int CALLBACK SalEnumFontsProcExW( const LOGFONTW* lpelfe,
987 const TEXTMETRICW* lpntme,
988 DWORD nFontType, LPARAM lParam )
990 ENUMLOGFONTEXW const * pLogFont
991 = reinterpret_cast<ENUMLOGFONTEXW const *>(lpelfe);
992 NEWTEXTMETRICEXW const * pMetric
993 = reinterpret_cast<NEWTEXTMETRICEXW const *>(lpntme);
994 ImplEnumInfo* pInfo = reinterpret_cast<ImplEnumInfo*>(lParam);
995 if ( !pInfo->mpName )
997 // Ignore vertical fonts
998 if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
1000 OUString aName(o3tl::toU(pLogFont->elfLogFont.lfFaceName));
1001 pInfo->mpName = &aName;
1002 memcpy(pInfo->mpLogFont->lfFaceName, pLogFont->elfLogFont.lfFaceName, (aName.getLength()+1)*sizeof(wchar_t));
1003 pInfo->mpLogFont->lfCharSet = pLogFont->elfLogFont.lfCharSet;
1004 EnumFontFamiliesExW(pInfo->mhDC, pInfo->mpLogFont, SalEnumFontsProcExW,
1005 reinterpret_cast<LPARAM>(pInfo), 0);
1006 pInfo->mpLogFont->lfFaceName[0] = '\0';
1007 pInfo->mpLogFont->lfCharSet = DEFAULT_CHARSET;
1008 pInfo->mpName = nullptr;
1011 else
1013 // Ignore non-device fonts on printers.
1014 if (pInfo->mbPrinter)
1016 if ((nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE))
1018 SAL_INFO("vcl.fonts", "Unsupported printer font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
1019 return 1;
1022 // Only SFNT fonts are supported, ignore anything else.
1023 else if (!(nFontType & TRUETYPE_FONTTYPE) &&
1024 !(pMetric->ntmTm.ntmFlags & NTM_PS_OPENTYPE) &&
1025 !(pMetric->ntmTm.ntmFlags & NTM_TT_OPENTYPE))
1027 SAL_INFO("vcl.fonts", "Unsupported font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
1028 return 1;
1031 rtl::Reference<WinFontFace> pData = ImplLogMetricToDevFontDataW(pLogFont, &(pMetric->ntmTm));
1032 pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
1034 pInfo->mpList->Add( pData.get() );
1035 SAL_INFO("vcl.fonts", "SalEnumFontsProcExW: font added: " << pData->GetFamilyName() << " " << pData->GetStyleName());
1038 return 1;
1041 struct TempFontItem
1043 OUString maFontResourcePath;
1044 TempFontItem* mpNextItem;
1047 static int lcl_AddFontResource(SalData& rSalData, const OUString& rFontFileURL, bool bShared)
1049 OUString aFontSystemPath;
1050 OSL_VERIFY(!osl::FileBase::getSystemPathFromFileURL(rFontFileURL, aFontSystemPath));
1052 int nRet = AddFontResourceExW(o3tl::toW(aFontSystemPath.getStr()), FR_PRIVATE, nullptr);
1053 SAL_WARN_IF(nRet <= 0, "vcl.fonts", "AddFontResourceExW failed for " << rFontFileURL);
1054 if (nRet > 0)
1056 TempFontItem* pNewItem = new TempFontItem;
1057 pNewItem->maFontResourcePath = aFontSystemPath;
1058 if (bShared)
1060 pNewItem->mpNextItem = rSalData.mpSharedTempFontItem;
1061 rSalData.mpSharedTempFontItem = pNewItem;
1063 else
1065 pNewItem->mpNextItem = rSalData.mpOtherTempFontItem;
1066 rSalData.mpOtherTempFontItem = pNewItem;
1069 return nRet;
1072 void ImplReleaseTempFonts(SalData& rSalData, bool bAll)
1074 while (TempFontItem* p = rSalData.mpOtherTempFontItem)
1076 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
1077 rSalData.mpOtherTempFontItem = p->mpNextItem;
1078 delete p;
1081 if (!bAll)
1082 return;
1084 while (TempFontItem* p = rSalData.mpSharedTempFontItem)
1086 RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
1087 rSalData.mpSharedTempFontItem = p->mpNextItem;
1088 delete p;
1092 static OUString lcl_GetFontFamilyName(const OUString& rFontFileURL)
1094 // Create temporary file name
1095 OUString aTempFileURL;
1096 if (osl::File::E_None != osl::File::createTempFile(nullptr, nullptr, &aTempFileURL))
1097 return OUString();
1098 osl::File::remove(aTempFileURL);
1099 OUString aResSystemPath;
1100 osl::FileBase::getSystemPathFromFileURL(aTempFileURL, aResSystemPath);
1102 // Create font resource file (.fot)
1103 // There is a limit of 127 characters for the full path passed via lpszFile, so we have to
1104 // split the font URL and pass it as two parameters. As a result we can't use
1105 // CreateScalableFontResource for renaming, as it now expects the font in the system path.
1106 // But it's still good to use it for family name extraction, we're currently after.
1107 // BTW: it doesn't help to prefix the lpszFile with \\?\ to support larger paths.
1108 // TODO: use TTLoadEmbeddedFont (needs an EOT as input, so we have to add a header to the TTF)
1109 // TODO: forward the EOT from the AddTempDevFont call side, if VCL supports it
1110 INetURLObject aTTFUrl(rFontFileURL);
1111 // GetBase() strips the extension
1112 OUString aFilename = aTTFUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
1113 if (!CreateScalableFontResourceW(0, o3tl::toW(aResSystemPath.getStr()),
1114 o3tl::toW(aFilename.getStr()), o3tl::toW(aTTFUrl.GetPath().getStr())))
1116 sal_uInt32 nError = GetLastError();
1117 SAL_WARN("vcl.fonts", "CreateScalableFontResource failed for " << aResSystemPath << " "
1118 << aFilename << " " << aTTFUrl.GetPath() << " " << nError);
1119 return OUString();
1122 // Open and read the font resource file
1123 osl::File aFotFile(aTempFileURL);
1124 if (osl::FileBase::E_None != aFotFile.open(osl_File_OpenFlag_Read))
1125 return OUString();
1127 sal_uInt64 nBytesRead = 0;
1128 char aBuffer[4096];
1129 aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead );
1130 // clean up temporary resource file
1131 aFotFile.close();
1132 osl::File::remove(aTempFileURL);
1134 // retrieve font family name from byte offset 0x4F6
1135 static const sal_uInt64 nNameOfs = 0x4F6;
1136 sal_uInt64 nPos = nNameOfs;
1137 for (; (nPos < nBytesRead) && (aBuffer[nPos] != 0); nPos++);
1138 if (nPos >= nBytesRead || (nPos == nNameOfs))
1139 return OUString();
1141 return OUString(aBuffer + nNameOfs, nPos - nNameOfs, osl_getThreadTextEncoding());
1144 bool WinSalGraphics::AddTempDevFont(PhysicalFontCollection* pFontCollection,
1145 const OUString& rFontFileURL, const OUString& rFontName)
1147 OUString aFontFamily = lcl_GetFontFamilyName(rFontFileURL);
1148 if (aFontFamily.isEmpty())
1150 SAL_WARN("vcl.fonts", "error extracting font family from " << rFontFileURL);
1151 return false;
1154 if (rFontName != aFontFamily)
1156 SAL_WARN("vcl.fonts", "font family renaming not implemented; skipping embedded " << rFontName);
1157 return false;
1160 int nFonts = lcl_AddFontResource(*GetSalData(), rFontFileURL, false);
1161 if (nFonts <= 0)
1162 return false;
1164 ImplEnumInfo aInfo;
1165 aInfo.mhDC = getHDC();
1166 aInfo.mpList = pFontCollection;
1167 aInfo.mpName = &aFontFamily;
1168 aInfo.mbPrinter = mbPrinter;
1169 aInfo.mnFontCount = pFontCollection->Count();
1170 const int nExpectedFontCount = aInfo.mnFontCount + nFonts;
1172 LOGFONTW aLogFont = {};
1173 aLogFont.lfCharSet = DEFAULT_CHARSET;
1174 aInfo.mpLogFont = &aLogFont;
1176 // add the font to the PhysicalFontCollection
1177 EnumFontFamiliesExW(getHDC(), &aLogFont,
1178 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0);
1180 SAL_WARN_IF(nExpectedFontCount != pFontCollection->Count(), "vcl.fonts",
1181 "temp font was registered but is not in enumeration: " << rFontFileURL);
1183 return true;
1186 void WinSalGraphics::GetDevFontList( PhysicalFontCollection* pFontCollection )
1188 // make sure all LO shared fonts are registered temporarily
1189 static std::once_flag init;
1190 std::call_once(init, []()
1192 auto registerFontsIn = [](const OUString& dir) {
1193 // collect fonts in font path that could not be registered
1194 osl::Directory aFontDir(dir);
1195 osl::FileBase::RC rcOSL = aFontDir.open();
1196 if (rcOSL == osl::FileBase::E_None)
1198 osl::DirectoryItem aDirItem;
1199 SalData* pSalData = GetSalData();
1200 assert(pSalData);
1202 while (aFontDir.getNextItem(aDirItem, 10) == osl::FileBase::E_None)
1204 osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
1205 rcOSL = aDirItem.getFileStatus(aFileStatus);
1206 if (rcOSL == osl::FileBase::E_None)
1207 lcl_AddFontResource(*pSalData, aFileStatus.getFileURL(), true);
1212 // determine font path
1213 // since we are only interested in fonts that could not be
1214 // registered before because of missing administration rights
1215 // only the font path of the user installation is needed
1216 OUString aPath("$BRAND_BASE_DIR");
1217 rtl_bootstrap_expandMacros(&aPath.pData);
1219 // internal font resources, required for normal operation, like OpenSymbol
1220 registerFontsIn(aPath + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts");
1222 // collect fonts in font path that could not be registered
1223 registerFontsIn(aPath + "/" LIBO_SHARE_FOLDER "/fonts/truetype");
1225 return true;
1228 ImplEnumInfo aInfo;
1229 aInfo.mhDC = getHDC();
1230 aInfo.mpList = pFontCollection;
1231 aInfo.mpName = nullptr;
1232 aInfo.mbPrinter = mbPrinter;
1233 aInfo.mnFontCount = 0;
1235 LOGFONTW aLogFont = {};
1236 aLogFont.lfCharSet = DEFAULT_CHARSET;
1237 aInfo.mpLogFont = &aLogFont;
1239 // fill the PhysicalFontCollection
1240 EnumFontFamiliesExW( getHDC(), &aLogFont,
1241 SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0 );
1243 // set glyph fallback hook
1244 static WinGlyphFallbackSubstititution aSubstFallback;
1245 static WinPreMatchFontSubstititution aPreMatchFont;
1246 pFontCollection->SetFallbackHook( &aSubstFallback );
1247 pFontCollection->SetPreMatchHook(&aPreMatchFont);
1250 void WinSalGraphics::ClearDevFontCache()
1252 WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(GetImpl());
1253 assert(pImpl != nullptr);
1254 pImpl->ClearDevFontCache();
1255 ImplReleaseTempFonts(*GetSalData(), false);
1258 bool WinFontInstance::ImplGetGlyphBoundRect(sal_GlyphId nId, tools::Rectangle& rRect, bool) const
1260 assert(m_pGraphics);
1261 HDC hDC = m_pGraphics->getHDC();
1262 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1263 const HFONT hFont = GetHFONT();
1264 if (hFont != hOrigFont)
1265 SelectObject(hDC, hFont);
1267 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1268 { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1269 const float fFontScale = GetScale();
1271 // use unity matrix
1272 MAT2 aMat;
1273 aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1274 aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1276 UINT nGGOFlags = GGO_METRICS;
1277 nGGOFlags |= GGO_GLYPH_INDEX;
1279 GLYPHMETRICS aGM;
1280 aGM.gmptGlyphOrigin.x = aGM.gmptGlyphOrigin.y = 0;
1281 aGM.gmBlackBoxX = aGM.gmBlackBoxY = 0;
1282 DWORD nSize = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGM, 0, nullptr, &aMat);
1283 if (nSize == GDI_ERROR)
1284 return false;
1286 rRect = tools::Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
1287 Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
1288 rRect.SetLeft(static_cast<int>( fFontScale * rRect.Left() ));
1289 rRect.SetRight(static_cast<int>( fFontScale * rRect.Right() ) + 1);
1290 rRect.SetTop(static_cast<int>( fFontScale * rRect.Top() ));
1291 rRect.SetBottom(static_cast<int>( fFontScale * rRect.Bottom() ) + 1);
1292 return true;
1295 bool WinFontInstance::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rB2DPolyPoly, bool) const
1297 rB2DPolyPoly.clear();
1299 assert(m_pGraphics);
1300 HDC hDC = m_pGraphics->getHDC();
1301 const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
1302 const HFONT hFont = GetHFONT();
1303 if (hFont != hOrigFont)
1304 SelectObject(hDC, hFont);
1306 const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
1307 { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
1309 // use unity matrix
1310 MAT2 aMat;
1311 aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
1312 aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
1314 UINT nGGOFlags = GGO_NATIVE;
1315 nGGOFlags |= GGO_GLYPH_INDEX;
1317 GLYPHMETRICS aGlyphMetrics;
1318 const DWORD nSize1 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGlyphMetrics, 0, nullptr, &aMat);
1319 if( !nSize1 ) // blank glyphs are ok
1320 return true;
1321 else if( nSize1 == GDI_ERROR )
1322 return false;
1324 BYTE* pData = new BYTE[ nSize1 ];
1325 const DWORD nSize2 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags,
1326 &aGlyphMetrics, nSize1, pData, &aMat );
1328 if( nSize1 != nSize2 )
1329 return false;
1331 // TODO: avoid tools polygon by creating B2DPolygon directly
1332 int nPtSize = 512;
1333 Point* pPoints = new Point[ nPtSize ];
1334 PolyFlags* pFlags = new PolyFlags[ nPtSize ];
1336 TTPOLYGONHEADER* pHeader = reinterpret_cast<TTPOLYGONHEADER*>(pData);
1337 while( reinterpret_cast<BYTE*>(pHeader) < pData+nSize2 )
1339 // only outline data is interesting
1340 if( pHeader->dwType != TT_POLYGON_TYPE )
1341 break;
1343 // get start point; next start points are end points
1344 // of previous segment
1345 sal_uInt16 nPnt = 0;
1347 tools::Long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
1348 tools::Long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
1349 pPoints[ nPnt ] = Point( nX, nY );
1350 pFlags[ nPnt++ ] = PolyFlags::Normal;
1352 bool bHasOfflinePoints = false;
1353 TTPOLYCURVE* pCurve = reinterpret_cast<TTPOLYCURVE*>( pHeader + 1 );
1354 pHeader = reinterpret_cast<TTPOLYGONHEADER*>( reinterpret_cast<BYTE*>(pHeader) + pHeader->cb );
1355 while( reinterpret_cast<BYTE*>(pCurve) < reinterpret_cast<BYTE*>(pHeader) )
1357 int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
1358 if( nPtSize < nNeededSize )
1360 Point* pOldPoints = pPoints;
1361 PolyFlags* pOldFlags = pFlags;
1362 nPtSize = 2 * nNeededSize;
1363 pPoints = new Point[ nPtSize ];
1364 pFlags = new PolyFlags[ nPtSize ];
1365 for( sal_uInt16 i = 0; i < nPnt; ++i )
1367 pPoints[ i ] = pOldPoints[ i ];
1368 pFlags[ i ] = pOldFlags[ i ];
1370 delete[] pOldPoints;
1371 delete[] pOldFlags;
1374 int i = 0;
1375 if( TT_PRIM_LINE == pCurve->wType )
1377 while( i < pCurve->cpfx )
1379 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1380 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1381 ++i;
1382 pPoints[ nPnt ] = Point( nX, nY );
1383 pFlags[ nPnt ] = PolyFlags::Normal;
1384 ++nPnt;
1387 else if( TT_PRIM_QSPLINE == pCurve->wType )
1389 bHasOfflinePoints = true;
1390 while( i < pCurve->cpfx )
1392 // get control point of quadratic bezier spline
1393 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1394 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1395 ++i;
1396 Point aControlP( nX, nY );
1398 // calculate first cubic control point
1399 // P0 = 1/3 * (PBeg + 2 * PQControl)
1400 nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
1401 nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
1402 pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1403 pFlags[ nPnt+0 ] = PolyFlags::Control;
1405 // calculate endpoint of segment
1406 nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
1407 nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
1409 if ( i+1 >= pCurve->cpfx )
1411 // endpoint is either last point in segment => advance
1412 ++i;
1414 else
1416 // or endpoint is the middle of two control points
1417 nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
1418 nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
1419 nX = (nX + 1) / 2;
1420 nY = (nY + 1) / 2;
1421 // no need to advance, because the current point
1422 // is the control point in next bezier spline
1425 pPoints[ nPnt+2 ] = Point( nX, nY );
1426 pFlags[ nPnt+2 ] = PolyFlags::Normal;
1428 // calculate second cubic control point
1429 // P1 = 1/3 * (PEnd + 2 * PQControl)
1430 nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
1431 nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
1432 pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
1433 pFlags[ nPnt+1 ] = PolyFlags::Control;
1435 nPnt += 3;
1439 // next curve segment
1440 pCurve = reinterpret_cast<TTPOLYCURVE*>(&pCurve->apfx[ i ]);
1443 // end point is start point for closed contour
1444 // disabled, because Polygon class closes the contour itself
1445 // pPoints[nPnt++] = pPoints[0];
1446 // #i35928#
1447 // Added again, but add only when not yet closed
1448 if(pPoints[nPnt - 1] != pPoints[0])
1450 if( bHasOfflinePoints )
1451 pFlags[nPnt] = pFlags[0];
1453 pPoints[nPnt++] = pPoints[0];
1456 // convert y-coordinates W32 -> VCL
1457 for( int i = 0; i < nPnt; ++i )
1458 pPoints[i].setY(-pPoints[i].Y());
1460 // insert into polypolygon
1461 tools::Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : nullptr) );
1462 // convert to B2DPolyPolygon
1463 // TODO: get rid of the intermediate PolyPolygon
1464 rB2DPolyPoly.append( aPoly.getB2DPolygon() );
1467 delete[] pPoints;
1468 delete[] pFlags;
1470 delete[] pData;
1472 // rescaling needed for the tools::PolyPolygon conversion
1473 if( rB2DPolyPoly.count() )
1475 const double fFactor(GetScale()/256);
1476 rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
1479 return true;
1482 class ScopedFont
1484 public:
1485 explicit ScopedFont(WinSalGraphics & rData);
1487 ~ScopedFont();
1489 private:
1490 WinSalGraphics & m_rData;
1491 HFONT m_hOrigFont;
1494 ScopedFont::ScopedFont(WinSalGraphics & rData): m_rData(rData), m_hOrigFont(nullptr)
1496 if (m_rData.mpWinFontEntry[0])
1498 m_hOrigFont = m_rData.mpWinFontEntry[0]->GetHFONT();
1499 m_rData.mpWinFontEntry[0]->SetHFONT(nullptr);
1503 ScopedFont::~ScopedFont()
1505 if( m_hOrigFont )
1507 // restore original font, destroy temporary font
1508 HFONT hTempFont = m_rData.mpWinFontEntry[0]->GetHFONT();
1509 m_rData.mpWinFontEntry[0]->SetHFONT(m_hOrigFont);
1510 SelectObject( m_rData.getHDC(), m_hOrigFont );
1511 DeleteObject( hTempFont );
1515 namespace {
1517 class ScopedTrueTypeFont
1519 public:
1520 ScopedTrueTypeFont(): m_pFont(nullptr) {}
1522 ~ScopedTrueTypeFont();
1524 SFErrCodes open(void const * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum, const FontCharMapRef xCharMap = nullptr);
1526 TrueTypeFont * get() const { return m_pFont; }
1528 private:
1529 TrueTypeFont * m_pFont;
1534 ScopedTrueTypeFont::~ScopedTrueTypeFont()
1536 if (m_pFont != nullptr)
1537 CloseTTFont(m_pFont);
1540 SFErrCodes ScopedTrueTypeFont::open(void const * pBuffer, sal_uInt32 nLen,
1541 sal_uInt32 nFaceNum, const FontCharMapRef xCharMap)
1543 OSL_ENSURE(m_pFont == nullptr, "already open");
1544 return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont, xCharMap);
1547 bool WinSalGraphics::CreateFontSubset( const OUString& rToFile,
1548 const PhysicalFontFace* pFont, const sal_GlyphId* pGlyphIds, const sal_uInt8* pEncoding,
1549 sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
1551 // TODO: use more of the central font-subsetting code, move stuff there if needed
1553 // create matching FontSelectPattern
1554 // we need just enough to get to the font file data
1555 // use height=1000 for easier debugging (to match psprint's font units)
1556 FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1558 // TODO: much better solution: move SetFont and restoration of old font to caller
1559 ScopedFont aOldFont(*this);
1560 HFONT hOldFont = nullptr;
1561 ImplDoSetFont(aIFSD, pFont, hOldFont);
1563 WinFontFace const * pWinFontData = static_cast<WinFontFace const *>(pFont);
1565 #if OSL_DEBUG_LEVEL > 1
1566 // get font metrics
1567 TEXTMETRICW aWinMetric;
1568 if( !::GetTextMetricsW( getHDC(), &aWinMetric ) )
1569 return FALSE;
1571 SAL_WARN_IF( (aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "vcl", "cannot subset device font" );
1572 SAL_WARN_IF( !(aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE), "vcl", "can only subset TT font" );
1573 #endif
1575 OUString aSysPath;
1576 if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
1577 return false;
1578 const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
1579 const OString aToFile(OUStringToOString(aSysPath, aThreadEncoding));
1581 // check if the font has a CFF-table
1582 const DWORD nCffTag = CalcTag( "CFF " );
1583 const RawFontData aRawCffData( getHDC(), nCffTag );
1584 if (aRawCffData.get())
1586 pWinFontData->UpdateFromHDC( getHDC() );
1587 return SalGraphics::CreateCFFfontSubset(aRawCffData.get(), aRawCffData.size(), aToFile,
1588 pGlyphIds, pEncoding, pGlyphWidths, nGlyphCount,
1589 rInfo);
1592 // get raw font file data
1593 const RawFontData xRawFontData( getHDC(), 0 );
1594 if( !xRawFontData.get() )
1595 return false;
1597 // open font file
1598 sal_uInt32 nFaceNum = 0;
1599 if( !*xRawFontData.get() ) // TTC candidate
1600 nFaceNum = ~0U; // indicate "TTC font extracts only"
1602 ScopedTrueTypeFont aSftTTF;
1603 SFErrCodes nRC = aSftTTF.open( xRawFontData.get(), xRawFontData.size(), nFaceNum, pFont->GetFontCharMap());
1604 if( nRC != SFErrCodes::Ok )
1605 return false;
1607 TTGlobalFontInfo aTTInfo;
1608 ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
1609 OUString aPSName = ImplSalGetUniString(aTTInfo.psname);
1610 FillFontSubsetInfo(aTTInfo, aPSName, rInfo);
1612 // write subset into destination file
1613 return SalGraphics::CreateTTFfontSubset(*aSftTTF.get(), aToFile, aIFSD.mbVertical, pGlyphIds,
1614 pEncoding, pGlyphWidths, nGlyphCount);
1617 const void* WinSalGraphics::GetEmbedFontData(const PhysicalFontFace* pFont, tools::Long* pDataLen)
1619 // create matching FontSelectPattern
1620 // we need just enough to get to the font file data
1621 FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1623 ScopedFont aOldFont(*this);
1625 HFONT hOldFont = nullptr;
1626 ImplDoSetFont(aIFSD, pFont, hOldFont);
1628 // get the raw font file data
1629 RawFontData aRawFontData( getHDC() );
1630 *pDataLen = aRawFontData.size();
1631 if( !aRawFontData.get() )
1632 return nullptr;
1634 const unsigned char* pData = aRawFontData.steal();
1635 return pData;
1638 void WinSalGraphics::FreeEmbedFontData( const void* pData, tools::Long /*nLen*/ )
1640 delete[] static_cast<char const *>(pData);
1643 void WinSalGraphics::GetGlyphWidths( const PhysicalFontFace* pFont,
1644 bool bVertical,
1645 std::vector< sal_Int32 >& rWidths,
1646 Ucs2UIntMap& rUnicodeEnc )
1648 // create matching FontSelectPattern
1649 // we need just enough to get to the font file data
1650 FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
1652 // TODO: much better solution: move SetFont and restoration of old font to caller
1653 ScopedFont aOldFont(*this);
1655 HFONT hOldFont = nullptr;
1656 ImplDoSetFont(aIFSD, pFont, hOldFont);
1658 // get raw font file data
1659 const RawFontData xRawFontData( getHDC() );
1660 if( !xRawFontData.get() )
1661 return;
1663 // open font file
1664 sal_uInt32 nFaceNum = 0;
1665 if( !*xRawFontData.get() ) // TTC candidate
1666 nFaceNum = ~0U; // indicate "TTC font extracts only"
1668 ScopedTrueTypeFont aSftTTF;
1669 SFErrCodes nRC = aSftTTF.open(xRawFontData.get(), xRawFontData.size(), nFaceNum, pFont->GetFontCharMap());
1670 if( nRC != SFErrCodes::Ok )
1671 return;
1673 SalGraphics::GetGlyphWidths(*aSftTTF.get(), *pFont, bVertical, rWidths, rUnicodeEnc);
1676 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */