1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
31 #include <string_view>
36 // Currently, we build with _WIN32_WINNT=0x0601 (Windows 7), which means newer
37 // declarations in dwrite_3.h will not be visible.
39 # include "dw-extra.h"
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>
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>
78 #include <vcl/skia/SkiaHelper.hxx>
79 #include <skia/win/font.hxx>
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
100 class WinPreMatchFontSubstititution
101 : public vcl::font::PreMatchFontSubstitution
104 bool FindFontSubstitute(vcl::font::FontSelectPattern
&) const override
;
107 class WinGlyphFallbackSubstititution
108 : public vcl::font::GlyphFallbackFontSubstitution
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() )
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
))
133 rRemainingCodes
.push_back(uChar
);
136 xFontCharMap
= nullptr;
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.
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
))
176 for (const auto& aSub
: aBitmapFontSubs
)
178 if (rFontSelData
.maSearchName
== GetEnglishSearchFontName(aSub
.first
))
180 rFontSelData
.maSearchName
= aSub
.second
;
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
);
206 vcl::font::PhysicalFontFace
* pFace
= pFontFamily
->FindBestFontFace( rFontSelData
);
207 if( HasMissingChars( pFace
, rMissingChars
) )
209 rFontSelData
.maSearchName
= pFontFamily
->GetSearchName();
214 // are the missing characters symbols?
215 pFontFamily
= pFontCollection
->FindFontFamilyByAttributes( ImplFontAttrs::Symbol
,
216 rFontSelData
.GetWeight(),
217 rFontSelData
.GetWidthType(),
218 rFontSelData
.GetItalic(),
219 rFontSelData
.maSearchName
);
222 vcl::font::PhysicalFontFace
* pFace
= pFontFamily
->FindBestFontFace( rFontSelData
);
223 if( HasMissingChars( pFace
, rMissingChars
) )
225 rFontSelData
.maSearchName
= pFontFamily
->GetSearchName();
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
;
239 for( int i
= 0; i
< nTestFontCount
; ++i
)
241 vcl::font::PhysicalFontFace
* pFace
= pTestFontList
->Get( i
);
242 bFound
= HasMissingChars( pFace
, rMissingChars
);
245 rFontSelData
.maSearchName
= pFace
->GetFamilyName();
257 vcl::font::PhysicalFontCollection
* mpList
;
266 static rtl_TextEncoding
ImplCharSetToSal( BYTE nCharSet
)
268 rtl_TextEncoding eTextEncoding
;
270 if ( nCharSet
== OEM_CHARSET
)
272 UINT nCP
= static_cast<sal_uInt16
>(GetOEMCP());
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;
280 eTextEncoding
= rtl_getTextEncodingFromWindowsCodePage(nCP
);
287 eTextEncoding
= rtl_getTextEncodingFromWindowsCharset( nCharSet
);
289 eTextEncoding
= RTL_TEXTENCODING_UNICODE
;
292 return eTextEncoding
;
295 static FontFamily
ImplFamilyToSal( BYTE nFamily
)
297 switch ( nFamily
& 0xF0 )
300 return FAMILY_DECORATIVE
;
303 return FAMILY_MODERN
;
309 return FAMILY_SCRIPT
;
318 return FAMILY_DONTKNOW
;
321 static BYTE
ImplFamilyToWin( FontFamily eFamily
)
325 case FAMILY_DECORATIVE
:
326 return FF_DECORATIVE
;
350 static FontWeight
ImplWeightToSal( int nWeight
)
352 if ( nWeight
<= FW_THIN
)
354 else if ( nWeight
<= FW_ULTRALIGHT
)
355 return WEIGHT_ULTRALIGHT
;
356 else if ( nWeight
<= FW_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
)
366 else if ( nWeight
<= FW_ULTRABOLD
)
367 return WEIGHT_ULTRABOLD
;
372 static int ImplWeightToWin( FontWeight eWeight
)
379 case WEIGHT_ULTRALIGHT
:
380 return FW_ULTRALIGHT
;
385 case WEIGHT_SEMILIGHT
:
392 case WEIGHT_SEMIBOLD
:
398 case WEIGHT_ULTRABOLD
:
411 static FontPitch
ImplLogPitchToSal( BYTE nPitch
)
413 if ( nPitch
& FIXED_PITCH
)
416 return PITCH_VARIABLE
;
419 static FontPitch
ImplMetricPitchToSal( BYTE nPitch
)
421 // Grrrr! See NT help
422 if ( !(nPitch
& TMPF_FIXED_PITCH
) )
425 return PITCH_VARIABLE
;
428 static BYTE
ImplPitchToWin( FontPitch ePitch
)
430 if ( ePitch
== PITCH_FIXED
)
432 else if ( ePitch
== PITCH_VARIABLE
)
433 return VARIABLE_PITCH
;
435 return DEFAULT_PITCH
;
438 static FontAttributes
WinFont2DevFontAttributes( const ENUMLOGFONTEXW
& rEnumFont
,
439 const NEWTEXTMETRICW
& rMetric
)
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
)
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
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
);
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
);
503 rFont
.SetItalic( ITALIC_NONE
);
504 if ( rLogFont
.lfUnderline
)
505 rFont
.SetUnderline( LINESTYLE_SINGLE
);
507 rFont
.SetUnderline( LINESTYLE_NONE
);
508 if ( rLogFont
.lfStrikeOut
)
509 rFont
.SetStrikeout( STRIKEOUT_SINGLE
);
511 rFont
.SetStrikeout( STRIKEOUT_NONE
);
515 WinFontFace::WinFontFace(const ENUMLOGFONTEXW
& rEnumFont
, const NEWTEXTMETRICW
& rMetric
)
516 : vcl::font::PhysicalFontFace(WinFont2DevFontAttributes(rEnumFont
, rMetric
)),
518 meWinCharSet(rEnumFont
.elfLogFont
.lfCharSet
),
519 mnPitchAndFamily(rMetric
.tmPitchAndFamily
),
520 maLogFont(rEnumFont
.elfLogFont
)
524 WinFontFace::~WinFontFace()
528 sal_IntPtr
WinFontFace::GetFontId() const
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
);
539 return new WinFontInstance(*this, rFSD
);
542 const std::vector
<hb_variation_t
>&
543 WinFontFace::GetVariations(const LogicalFontInstance
& rFont
) const
547 mxVariations
.emplace();
548 auto pDWFontFace
= static_cast<const WinFontInstance
&>(rFont
).GetDWFontFace();
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());
561 mxVariations
->reserve(aAxisValues
.size());
562 for (auto& rAxisValue
: aAxisValues
)
563 mxVariations
->push_back(
564 { OSL_NETDWORD(rAxisValue
.axisTag
), rAxisValue
.value
});
570 return *mxVariations
;
578 BlobReference(hb_blob_t
* 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
);
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
>;
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
);
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
);
642 ::ReleaseDC(nullptr, hDC
);
644 hb_blob_t
* pBlob
= nullptr;
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
) });
654 void WinSalGraphics::SetTextColor( Color nColor
)
656 COLORREF aCol
= PALETTERGB( nColor
.GetRed(),
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;
676 void ImplGetLogFontFromFontSelect( const vcl::font::FontSelectPattern
& rFont
,
677 const vcl::font::PhysicalFontFace
* pFontFace
,
682 aName
= pFontFace
->GetFamilyName();
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;
694 const WinFontFace
* pWinFontData
= static_cast<const WinFontFace
*>(pFontFace
);
695 rLogFont
.lfCharSet
= pWinFontData
->GetCharSet();
696 rLogFont
.lfPitchAndFamily
= pWinFontData
->GetPitchAndFamily();
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
,
729 HFONT hNewFont
= nullptr;
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;
759 // only required for virtual devices, see below for details
760 hdcScreen
= GetDC(nullptr);
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;
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
798 if (!mpWinFontEntry
[nFallbackLevel
].is())
801 // DeInitGraphics doesn't free the cached fonts, so mhDefFont might be nullptr
804 ::SelectFont(getHDC(), mhDefFont
);
808 // release no longer referenced font handles
809 for( int i
= nFallbackLevel
; i
< MAX_FALLBACK
; ++i
)
810 mpWinFontEntry
[i
] = nullptr;
814 WinFontInstance
*pFontInstance
= static_cast<WinFontInstance
*>(pFont
);
815 mpWinFontEntry
[ nFallbackLevel
] = pFontInstance
;
817 HFONT hOldFont
= nullptr;
818 HFONT hNewFont
= pFontInstance
->GetHFONT();
821 pFontInstance
->SetGraphics(this);
822 hNewFont
= pFontInstance
->GetHFONT();
824 hOldFont
= ::SelectFont(getHDC(), hNewFont
);
828 mhDefFont
= hOldFont
;
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
);
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])
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;
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
)));
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
)));
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());
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
);
959 TempFontItem
* pNewItem
= new TempFontItem
;
960 pNewItem
->maFontResourcePath
= aFontSystemPath
;
963 pNewItem
->mpNextItem
= rSalData
.mpSharedTempFontItem
;
964 rSalData
.mpSharedTempFontItem
= pNewItem
;
968 pNewItem
->mpNextItem
= rSalData
.mpOtherTempFontItem
;
969 rSalData
.mpOtherTempFontItem
= pNewItem
;
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
;
987 while (TempFontItem
* p
= rSalData
.mpSharedTempFontItem
)
989 RemoveFontResourceExW(o3tl::toW(p
->maFontResourcePath
.getStr()), FR_PRIVATE
, nullptr);
990 rSalData
.mpSharedTempFontItem
= p
->mpNextItem
;
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
))
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
);
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
))
1030 sal_uInt64 nBytesRead
= 0;
1032 aFotFile
.read( aBuffer
, sizeof( aBuffer
), nBytesRead
);
1033 // clean up temporary resource file
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
))
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
);
1057 if (rFontName
!= aFontFamily
)
1059 SAL_WARN("vcl.fonts", "font family renaming not implemented; skipping embedded " << rFontName
);
1063 int nFonts
= lcl_AddFontResource(*GetSalData(), rFontFileURL
, false);
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
);
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();
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");
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();
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);
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
;
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
)
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);
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
); });
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
1238 else if( nSize1
== GDI_ERROR
)
1241 BYTE
* pData
= new BYTE
[ nSize1
];
1242 const DWORD nSize2
= ::GetGlyphOutlineW(hDC
, nId
, nGGOFlags
,
1243 &aGlyphMetrics
, nSize1
, pData
, &aMat
);
1245 if( nSize1
!= nSize2
)
1248 // TODO: avoid tools polygon by creating B2DPolygon directly
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
)
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
;
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
);
1299 pPoints
[ nPnt
] = Point( nX
, nY
);
1300 pFlags
[ nPnt
] = PolyFlags::Normal
;
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
);
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
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
);
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
;
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];
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() );
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
));
1399 IDWriteFontFace
* WinFontInstance::GetDWFontFace() const
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
);
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: */