mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 19:46:08 +08:00
1820 lines
60 KiB
C++
1820 lines
60 KiB
C++
//
|
|
// Copyright (c) 1993-2018 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
|
|
#include "opennurbs_internal_glyph.h"
|
|
#include "opennurbs_win_dwrite.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Global
|
|
// IDWriteFactory
|
|
// IDWriteGdiInterop
|
|
//
|
|
|
|
class ON_IDWrite
|
|
{
|
|
public:
|
|
static IDWriteFactory* Factory();
|
|
static IDWriteGdiInterop* GdiInterop();
|
|
|
|
private:
|
|
ON_IDWrite() = default;
|
|
~ON_IDWrite() = default;
|
|
ON_IDWrite(const ON_IDWrite&) = delete;
|
|
ON_IDWrite& operator=(const ON_IDWrite&) = delete;
|
|
|
|
private:
|
|
static IDWriteFactory* the_dwiteFactory;
|
|
static IDWriteGdiInterop* the_dwriteGdiInterop;
|
|
};
|
|
|
|
IDWriteFactory* ON_IDWrite::the_dwiteFactory = nullptr;
|
|
IDWriteGdiInterop* ON_IDWrite::the_dwriteGdiInterop = nullptr;
|
|
|
|
IDWriteFactory* ON_IDWrite::Factory()
|
|
{
|
|
if (nullptr == ON_IDWrite::the_dwiteFactory)
|
|
{
|
|
IDWriteFactory* dwiteFactory = nullptr;
|
|
HRESULT hr = DWriteCreateFactory(
|
|
DWRITE_FACTORY_TYPE_SHARED,
|
|
__uuidof(IDWriteFactory),
|
|
reinterpret_cast<IUnknown**>(&dwiteFactory)
|
|
);
|
|
if (FAILED(hr) || nullptr == dwiteFactory)
|
|
{
|
|
ON_ERROR("DWriteCreateFactory() failed.");
|
|
return nullptr;
|
|
}
|
|
dwiteFactory->AddRef();
|
|
ON_IDWrite::the_dwiteFactory = dwiteFactory;
|
|
}
|
|
return ON_IDWrite::the_dwiteFactory;
|
|
}
|
|
|
|
IDWriteGdiInterop* ON_IDWrite::GdiInterop()
|
|
{
|
|
if (nullptr == ON_IDWrite::the_dwriteGdiInterop)
|
|
{
|
|
IDWriteFactory* dwiteFactory = ON_IDWrite::Factory();
|
|
if (nullptr == dwiteFactory)
|
|
return nullptr;
|
|
|
|
IDWriteGdiInterop* dwriteGdiInterop = nullptr;
|
|
HRESULT hr = dwiteFactory->GetGdiInterop(&dwriteGdiInterop);
|
|
if (FAILED(hr) || nullptr == dwriteGdiInterop)
|
|
{
|
|
ON_ERROR("dwiteFactory->GetGdiInterop() failed.");
|
|
return nullptr;
|
|
}
|
|
|
|
dwriteGdiInterop->AddRef();
|
|
ON_IDWrite::the_dwriteGdiInterop = dwriteGdiInterop;
|
|
}
|
|
return ON_IDWrite::the_dwriteGdiInterop;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
|
|
static bool Internal_GetLocalizeStrings(
|
|
const wchar_t* preferedLocale,
|
|
IDWriteLocalizedStrings* pIDWriteLocalizedStrings,
|
|
ON_wString& defaultLocaleString,
|
|
ON_wString& enusLocaleString
|
|
)
|
|
{
|
|
defaultLocaleString = ON_wString::EmptyString;
|
|
enusLocaleString = ON_wString::EmptyString;
|
|
|
|
wchar_t userDefaultLocale[LOCALE_NAME_MAX_LENGTH + 1] = {};
|
|
if (ON_wString::EqualOrdinal(preferedLocale, L"GetUserDefaultLocaleName", true))
|
|
{
|
|
// Get the default locale for this user.
|
|
userDefaultLocale[LOCALE_NAME_MAX_LENGTH] = 0;
|
|
if (0 == ::GetUserDefaultLocaleName(userDefaultLocale, LOCALE_NAME_MAX_LENGTH))
|
|
userDefaultLocale[0];
|
|
userDefaultLocale[LOCALE_NAME_MAX_LENGTH] = 0;
|
|
preferedLocale = userDefaultLocale;
|
|
}
|
|
|
|
const wchar_t* enusLocaleName = L"en-us";
|
|
|
|
HRESULT hr;
|
|
|
|
if (nullptr == pIDWriteLocalizedStrings)
|
|
return ON_wString::EmptyString;
|
|
|
|
const UINT32 count = pIDWriteLocalizedStrings->GetCount();
|
|
if (count < 1)
|
|
return ON_wString::EmptyString;
|
|
|
|
UINT32 localeIndex = ON_UNSET_UINT_INDEX;
|
|
UINT32 enusLocaleIndex = ON_UNSET_UINT_INDEX;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
const wchar_t* s = (i > 0) ? enusLocaleName : preferedLocale;
|
|
if (nullptr == s || 0 == s[0])
|
|
continue;
|
|
BOOL exists = 0;
|
|
UINT32 idx = ON_UNSET_UINT_INDEX;
|
|
hr = pIDWriteLocalizedStrings->FindLocaleName(s, &idx, &exists);
|
|
if (FAILED(hr))
|
|
continue;
|
|
if (false == exists)
|
|
continue;
|
|
if (idx >= count)
|
|
continue;
|
|
if (i > 0)
|
|
{
|
|
enusLocaleIndex = idx;
|
|
}
|
|
else
|
|
{
|
|
localeIndex = idx;
|
|
if (ON_wString::EqualOrdinal(enusLocaleName, -1, s, -1, true))
|
|
{
|
|
enusLocaleIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int pass = 0; pass < 3; pass++)
|
|
{
|
|
if (pass > 1)
|
|
{
|
|
if (defaultLocaleString.IsNotEmpty() || enusLocaleString.IsNotEmpty())
|
|
break;
|
|
}
|
|
bool bGetLocaleName = false;
|
|
UINT32 i0, i1;
|
|
if (0 == pass)
|
|
{
|
|
if (ON_UNSET_UINT_INDEX == localeIndex)
|
|
continue;
|
|
i0 = localeIndex;
|
|
i1 = i0 + 1;
|
|
}
|
|
else if (1 == pass)
|
|
{
|
|
|
|
if (ON_UNSET_UINT_INDEX == enusLocaleIndex || enusLocaleIndex == localeIndex )
|
|
continue;
|
|
i0 = enusLocaleIndex;
|
|
i1 = i0 + 1;
|
|
}
|
|
else
|
|
{
|
|
if (defaultLocaleString.IsNotEmpty())
|
|
break;
|
|
i0 = 0;
|
|
i1 = count;
|
|
}
|
|
|
|
for (UINT32 i = i0; i < i1; i++)
|
|
{
|
|
ON_wString strStringValue;
|
|
ON_wString strLocaleName;
|
|
for (int value = 0; value < (bGetLocaleName?2:1); value++)
|
|
{
|
|
const bool bStringValuePass = (0 == value);
|
|
UINT32 slen = 0;
|
|
hr = bStringValuePass
|
|
? pIDWriteLocalizedStrings->GetStringLength(i, &slen)
|
|
: pIDWriteLocalizedStrings->GetLocaleNameLength(i, &slen)
|
|
;
|
|
if (FAILED(hr))
|
|
continue;
|
|
if (slen < 1)
|
|
continue;
|
|
if (slen > 1024 * 1024)
|
|
continue; // sanity check
|
|
ON_wString& s = bStringValuePass ? strStringValue : strLocaleName;
|
|
s.ReserveArray(slen + 2);
|
|
wchar_t* buffer = s.Array();
|
|
buffer[0] = 0;
|
|
buffer[slen] = 0;
|
|
buffer[slen + 1] = 0;
|
|
hr = bStringValuePass
|
|
? pIDWriteLocalizedStrings->GetString(i, buffer, slen + 1)
|
|
: pIDWriteLocalizedStrings->GetLocaleName(i, buffer, slen + 1);
|
|
if (SUCCEEDED(hr) && 0 != buffer[0] && 0 == buffer[slen + 1])
|
|
{
|
|
s.SetLength(ON_wString::Length(buffer));
|
|
s.TrimLeftAndRight();
|
|
}
|
|
else
|
|
s = ON_wString::EmptyString;
|
|
if (bStringValuePass)
|
|
{
|
|
if (s.IsEmpty())
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (strStringValue.IsEmpty())
|
|
continue;
|
|
|
|
if ( pass >= 2 && defaultLocaleString.IsEmpty() )
|
|
{
|
|
defaultLocaleString = strStringValue;
|
|
}
|
|
|
|
if (i == localeIndex)
|
|
defaultLocaleString = strStringValue;
|
|
|
|
if (i == enusLocaleIndex)
|
|
enusLocaleString = strStringValue;
|
|
|
|
if (
|
|
defaultLocaleString.IsNotEmpty()
|
|
&& enusLocaleString.IsNotEmpty()
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (defaultLocaleString.IsEmpty())
|
|
defaultLocaleString = enusLocaleString;
|
|
|
|
return defaultLocaleString.IsNotEmpty();
|
|
}
|
|
|
|
static void Internal_PrintLocalizedNames(
|
|
const ON_wString stringDescription,
|
|
const wchar_t* preferedLocale,
|
|
IDWriteLocalizedStrings* pDWriteLocalizedStrings,
|
|
ON_TextLog& text_log
|
|
)
|
|
{
|
|
ON_ClassArray<ON_wString> localizedStrings;
|
|
ON_wString preferedString;
|
|
ON_wString enusString;
|
|
Internal_GetLocalizeStrings(preferedLocale, pDWriteLocalizedStrings, preferedString, enusString);
|
|
text_log.Print(L"%ls: \"%ls\"\n", static_cast<const wchar_t*>(stringDescription), static_cast<const wchar_t*>(preferedString));
|
|
if (enusString.IsNotEmpty() && enusString != preferedString)
|
|
{
|
|
text_log.PushIndent();
|
|
text_log.Print(
|
|
L"locale \"en-us\": \"%ls\"\n",
|
|
static_cast<const wchar_t*>(enusString)
|
|
);
|
|
text_log.PopIndent();
|
|
}
|
|
}
|
|
|
|
class Internal_DWriteFontInformation
|
|
{
|
|
public:
|
|
Internal_DWriteFontInformation(
|
|
DWRITE_INFORMATIONAL_STRING_ID id,
|
|
const wchar_t* description
|
|
)
|
|
: m_id(id)
|
|
, m_description(description)
|
|
{}
|
|
DWRITE_INFORMATIONAL_STRING_ID m_id = DWRITE_INFORMATIONAL_STRING_NONE;
|
|
ON_wString m_description;
|
|
};
|
|
|
|
static const ON_wString Internal_DWRITE_FONT_WEIGHT_ToString(DWRITE_FONT_WEIGHT dwrite_weight)
|
|
{
|
|
ON_wString s;
|
|
switch (dwrite_weight)
|
|
{
|
|
case DWRITE_FONT_WEIGHT_THIN: s = L"THIN"; break;// 100
|
|
|
|
//case DWRITE_FONT_WEIGHT_EXTRA_LIGHT: s = L""; break; // 200
|
|
case DWRITE_FONT_WEIGHT_ULTRA_LIGHT: s = L"ULTRA_LIGHT"; break;// 200,
|
|
|
|
case DWRITE_FONT_WEIGHT_LIGHT: s = L"LIGHT"; break; // 300,
|
|
case DWRITE_FONT_WEIGHT_SEMI_LIGHT: s = L"SEMI_LIGHT"; break; // 350,
|
|
|
|
case DWRITE_FONT_WEIGHT_NORMAL: s = L"NORMAL"; break; // 400,
|
|
//case DWRITE_FONT_WEIGHT_REGULAR: s = L""; break; // 400,
|
|
|
|
case DWRITE_FONT_WEIGHT_MEDIUM: s = L"MEDIUM"; break; // 500,
|
|
|
|
//case DWRITE_FONT_WEIGHT_DEMI_BOLD: s = L""; break; // 600,
|
|
case DWRITE_FONT_WEIGHT_SEMI_BOLD: s = L"SEMI_BOLD"; break; // 600,
|
|
|
|
case DWRITE_FONT_WEIGHT_BOLD: s = L"BOLD"; break; // 700,
|
|
|
|
//case DWRITE_FONT_WEIGHT_EXTRA_BOLD: s = L""; break; // 800,
|
|
case DWRITE_FONT_WEIGHT_ULTRA_BOLD: s = L"ULTRA_BOLD"; break; // 800,
|
|
|
|
//case DWRITE_FONT_WEIGHT_BLACK: s = L""; break; // 900,
|
|
case DWRITE_FONT_WEIGHT_HEAVY: s = L"HEAVY"; break; // 900,
|
|
|
|
//case DWRITE_FONT_WEIGHT_EXTRA_BLACK: s = L""; break; // 950,
|
|
case DWRITE_FONT_WEIGHT_ULTRA_BLACK: s = L"ULTRA_BLACK"; break; // 950
|
|
|
|
default:
|
|
s = ON_wString::FromNumber((unsigned int)dwrite_weight); break;
|
|
};
|
|
return s;
|
|
}
|
|
|
|
static const ON_wString Internal_DWRITE_FONT_STRETCH_ToString(DWRITE_FONT_STRETCH dwrite_stretch)
|
|
{
|
|
ON_wString s;
|
|
switch (dwrite_stretch)
|
|
{
|
|
case DWRITE_FONT_STRETCH_UNDEFINED: s = L"UNDEFINED"; break; // 0,
|
|
case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: s = L"ULTRA_CONDENSED"; break; // 1,
|
|
case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: s = L"EXTRA_CONDENSED"; break; // 2,
|
|
case DWRITE_FONT_STRETCH_CONDENSED: s = L"CONDENSED"; break; // 3,
|
|
case DWRITE_FONT_STRETCH_SEMI_CONDENSED: s = L"CONDENSED"; break; // 4,
|
|
case DWRITE_FONT_STRETCH_NORMAL: s = L"NORMAL"; break; // 5,
|
|
//case DWRITE_FONT_STRETCH_MEDIUM: s = L"MEDIUM"; break; // 5,
|
|
case DWRITE_FONT_STRETCH_SEMI_EXPANDED: s = L"SEMI_EXPANDED"; break; // 6,
|
|
case DWRITE_FONT_STRETCH_EXPANDED: s = L"EXPANDED"; break; // 7,
|
|
case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: s = L"EXTRA_EXPANDED"; break; // 8,
|
|
case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: s = L"ULTRA_EXPANDED"; break; // 9 }; break;
|
|
default:
|
|
s = ON_wString::FromNumber((unsigned int)dwrite_stretch); break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static const ON_wString Internal_DWRITE_FONT_STYLE_ToString(DWRITE_FONT_STYLE dwrite_style)
|
|
{
|
|
ON_wString s;
|
|
switch (dwrite_style)
|
|
{
|
|
case DWRITE_FONT_STYLE_NORMAL: s = L"NORMAL"; break;
|
|
case DWRITE_FONT_STYLE_OBLIQUE: s = L"OBLIQUE"; break;
|
|
case DWRITE_FONT_STYLE_ITALIC: s = L"ITALIC"; break;
|
|
default:
|
|
s = ON_wString::FromNumber((unsigned int)dwrite_style); break;
|
|
};
|
|
|
|
return s;
|
|
}
|
|
|
|
void ON_Font::DumpWindowsDWriteFont(
|
|
IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale,
|
|
ON_TextLog& text_log
|
|
)
|
|
{
|
|
if (nullptr == dwrite_font)
|
|
{
|
|
text_log.Print("IDWriteFont = nullptr\n");
|
|
return;
|
|
}
|
|
|
|
const DWRITE_FONT_SIMULATIONS simulation = dwrite_font->GetSimulations();
|
|
if (DWRITE_FONT_SIMULATIONS_NONE != simulation)
|
|
text_log.Print("IDWriteFont SIMULATED:\n");
|
|
else
|
|
text_log.Print("IDWriteFont:\n");
|
|
|
|
ON_TextLogIndent indent1(text_log);
|
|
|
|
HRESULT hr;
|
|
|
|
// Family name
|
|
Microsoft::WRL::ComPtr<IDWriteFontFamily> pIDWriteFontFamily = nullptr;
|
|
hr = dwrite_font->GetFontFamily(&pIDWriteFontFamily);
|
|
if ((SUCCEEDED(hr)) && nullptr != pIDWriteFontFamily)
|
|
{
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> familyNames = nullptr;
|
|
hr = pIDWriteFontFamily->GetFamilyNames(&familyNames);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Internal_PrintLocalizedNames(L"Family name", preferedLocale, familyNames.Get(), text_log);
|
|
}
|
|
}
|
|
|
|
// Face name
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> faceNames = nullptr;
|
|
hr = dwrite_font->GetFaceNames(&faceNames);
|
|
if (SUCCEEDED(hr))
|
|
Internal_PrintLocalizedNames(L"Face name", preferedLocale, faceNames.Get(), text_log);
|
|
|
|
const DWRITE_FONT_WEIGHT dwrite_weight = dwrite_font->GetWeight();
|
|
const ON_wString weightString = Internal_DWRITE_FONT_WEIGHT_ToString(dwrite_weight);
|
|
text_log.Print("Weight = %ls\n", static_cast<const wchar_t*>(weightString));
|
|
|
|
const DWRITE_FONT_STYLE dwrite_style = dwrite_font->GetStyle();
|
|
const ON_wString styleString = Internal_DWRITE_FONT_STYLE_ToString(dwrite_style);
|
|
text_log.Print("Style = %ls\n", static_cast<const wchar_t*>(styleString));
|
|
|
|
const DWRITE_FONT_STRETCH dwrite_stretch = dwrite_font->GetStretch();
|
|
const ON_wString stretchString = Internal_DWRITE_FONT_STRETCH_ToString(dwrite_stretch);
|
|
text_log.Print("Stretch = %ls\n", static_cast<const wchar_t*>(stretchString));
|
|
|
|
const bool bIsSymbolFont = (dwrite_font->IsSymbolFont() ? true : false);
|
|
text_log.Print("IsSymbolFont = %s.\n",(bIsSymbolFont?"true":"false"));
|
|
|
|
if (DWRITE_FONT_SIMULATIONS_NONE != simulation)
|
|
{
|
|
const bool bBold = ((ON__UINT32)DWRITE_FONT_SIMULATIONS_BOLD) & ((ON__UINT32)simulation);
|
|
const bool bOblique = ((ON__UINT32)DWRITE_FONT_SIMULATIONS_OBLIQUE) & ((ON__UINT32)simulation);
|
|
text_log.Print("Simulations:");
|
|
if (bBold)
|
|
text_log.Print(" BOLD");
|
|
if (bOblique)
|
|
text_log.Print(" OBLIQUE");
|
|
ON__UINT32 s = 0;
|
|
if (bBold)
|
|
s |= (ON__UINT32)DWRITE_FONT_SIMULATIONS_BOLD;
|
|
if (bOblique)
|
|
s |= (ON__UINT32)DWRITE_FONT_SIMULATIONS_OBLIQUE;
|
|
if ( s != (ON__UINT32)simulation )
|
|
text_log.Print(" other");
|
|
text_log.PrintNewLine();
|
|
}
|
|
|
|
// Other names:
|
|
Internal_DWriteFontInformation info_strings[] =
|
|
{
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_FULL_NAME,L"DWrite full name"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME,L"DWrite PostScript name"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES,L"GDI family name"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES,L"GDI sub-family name")
|
|
};
|
|
const size_t info_strings_count = sizeof(info_strings) / sizeof(info_strings[0]);
|
|
for (size_t i = 0; i < info_strings_count; i++)
|
|
{
|
|
BOOL exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> fontNames = nullptr;
|
|
hr = dwrite_font->GetInformationalStrings(info_strings[i].m_id,&fontNames,&exists);
|
|
if (SUCCEEDED(hr) && exists)
|
|
Internal_PrintLocalizedNames(info_strings[i].m_description, preferedLocale, fontNames.Get(), text_log);
|
|
}
|
|
|
|
const ON_wString postscriptName = ON_Font::PostScriptNameFromWindowsDWriteFont(dwrite_font, preferedLocale);
|
|
text_log.Print(L"ON_Font PostScript name = \"%ls\"\n", static_cast<const wchar_t*>(postscriptName));
|
|
|
|
bool bGotLogfont = false;
|
|
for (;;)
|
|
{
|
|
IDWriteGdiInterop* dwriteGdiInterop = ON_IDWrite::GdiInterop();
|
|
if (nullptr == dwriteGdiInterop)
|
|
break;
|
|
|
|
BOOL isSystemFont = false;
|
|
LOGFONTW logfont;
|
|
memset(&logfont, 0, sizeof(logfont));
|
|
hr = dwriteGdiInterop->ConvertFontToLOGFONT(dwrite_font, &logfont, &isSystemFont);
|
|
if (FAILED(hr))
|
|
break;
|
|
text_log.Print("GDI interop IsSystemFont = %s\n", (isSystemFont ? "true" : "false"));
|
|
text_log.Print("GDI interop ");
|
|
ON_Font::DumpLogfont(&logfont, text_log);
|
|
bGotLogfont = true;
|
|
break;
|
|
}
|
|
|
|
if (false == bGotLogfont)
|
|
{
|
|
text_log.Print("GDI interop LOGFONT - FAILED.\n");
|
|
}
|
|
}
|
|
|
|
ON_Font::Stretch ON_Font::FontStretchFromDWriteStretch(
|
|
unsigned int dwrite_stretch,
|
|
ON_Font::Stretch undefined_result
|
|
)
|
|
{
|
|
ON_Font::Stretch font_stretch;
|
|
|
|
switch (dwrite_stretch)
|
|
{
|
|
case DWRITE_FONT_STRETCH_UNDEFINED:
|
|
font_stretch = undefined_result;
|
|
break;
|
|
|
|
case DWRITE_FONT_STRETCH_ULTRA_CONDENSED:
|
|
font_stretch = ON_Font::Stretch::Ultracondensed;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_EXTRA_CONDENSED:
|
|
font_stretch = ON_Font::Stretch::Extracondensed;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_CONDENSED:
|
|
font_stretch = ON_Font::Stretch::Condensed;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_SEMI_CONDENSED:
|
|
font_stretch = ON_Font::Stretch::Semicondensed;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_MEDIUM:
|
|
font_stretch = ON_Font::Stretch::Medium;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_SEMI_EXPANDED:
|
|
font_stretch = ON_Font::Stretch::Semiexpanded;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_EXPANDED:
|
|
font_stretch = ON_Font::Stretch::Expanded;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_EXTRA_EXPANDED:
|
|
font_stretch = ON_Font::Stretch::Extraexpanded;
|
|
break;
|
|
case DWRITE_FONT_STRETCH_ULTRA_EXPANDED:
|
|
font_stretch = ON_Font::Stretch::Ultraexpanded;
|
|
break;
|
|
|
|
default:
|
|
font_stretch = undefined_result;
|
|
break;
|
|
}
|
|
|
|
return font_stretch;
|
|
}
|
|
|
|
const ON_wString ON_Font::PostScriptNameFromWindowsDWriteFont(
|
|
IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == dwrite_font)
|
|
break;
|
|
BOOL exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> postscriptNames = nullptr;
|
|
HRESULT hr = dwrite_font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME, &postscriptNames, &exists);
|
|
if (FAILED(hr))
|
|
break;
|
|
ON_wString loc_PostScriptName;
|
|
ON_wString en_PostScriptName;
|
|
if (exists)
|
|
Internal_GetLocalizeStrings(preferedLocale, postscriptNames.Get(), loc_PostScriptName, en_PostScriptName);
|
|
|
|
// This was wrong.
|
|
// Bahnschrift is an OpenType variable font and all faces of variable fonts
|
|
// end up with the same PostScript name. In short, a PostScript name property
|
|
// cannot be used as a "unique id" for a font face.
|
|
|
|
////if (ON_wString::EqualOrdinal(L"Bahnschrift", postscriptName, true))
|
|
////{
|
|
//// // All faces of Bahnshrift (Win 10 en-us Feb 2018) return the same PostScriptName ( "Bahnschrift" ) A bug in the font file?
|
|
//// ON_wString suffix;
|
|
//// const DWRITE_FONT_WEIGHT weight = dwrite_font->GetWeight();
|
|
//// switch (weight)
|
|
//// {
|
|
//// case DWRITE_FONT_WEIGHT_THIN: suffix += L"Thin"; break; //; // 100
|
|
////
|
|
//// //case DWRITE_FONT_WEIGHT_EXTRA_LIGHT: suffix += L"Bold"; break; //; // 200
|
|
//// case DWRITE_FONT_WEIGHT_ULTRA_LIGHT: suffix += L"UltraLight"; break; //; // 200,
|
|
////
|
|
//// case DWRITE_FONT_WEIGHT_LIGHT: suffix += L"Light"; break; //; // 300,
|
|
//// case DWRITE_FONT_WEIGHT_SEMI_LIGHT: suffix += L"Semilight"; break; //; // 350,
|
|
////
|
|
//// case DWRITE_FONT_WEIGHT_NORMAL: break;
|
|
////
|
|
//// case DWRITE_FONT_WEIGHT_MEDIUM: suffix += L"Medium"; break; //; // 500,
|
|
////
|
|
//// //case DWRITE_FONT_WEIGHT_DEMI_BOLD: suffix += L"Bold"; break; //; // 600,
|
|
//// case DWRITE_FONT_WEIGHT_SEMI_BOLD: suffix += L"Semibold"; break; //; // 600,
|
|
////
|
|
//// case DWRITE_FONT_WEIGHT_BOLD: suffix += L"Bold"; break; //; // 700,
|
|
////
|
|
//// //case DWRITE_FONT_WEIGHT_EXTRA_BOLD: suffix += L"Bold"; break; //; // 800,
|
|
//// case DWRITE_FONT_WEIGHT_ULTRA_BOLD: suffix += L"UltraBold"; break; //; // 800,
|
|
////
|
|
//// //case DWRITE_FONT_WEIGHT_BLACK: suffix += L"Bold"; break; //; // 900,
|
|
//// case DWRITE_FONT_WEIGHT_HEAVY: suffix += L"Heavy"; break; //; // 900,
|
|
////
|
|
//// //case DWRITE_FONT_WEIGHT_EXTRA_BLACK: suffix += L"Bold"; break; //; // 950,
|
|
//// case DWRITE_FONT_WEIGHT_ULTRA_BLACK: suffix += L"UltraBlack"; break; //; // 950
|
|
////
|
|
//// default:
|
|
//// break;
|
|
//// }
|
|
////
|
|
//// const DWRITE_FONT_STYLE style = dwrite_font->GetStyle();
|
|
//// switch (style)
|
|
//// {
|
|
//// case DWRITE_FONT_STYLE_NORMAL: break;
|
|
//// case DWRITE_FONT_STYLE_OBLIQUE: suffix += L"Oblique"; break;
|
|
//// case DWRITE_FONT_STYLE_ITALIC: suffix += L"Italic"; break;
|
|
//// default:
|
|
//// break;
|
|
//// }
|
|
////
|
|
//// if (suffix.IsNotEmpty())
|
|
//// {
|
|
//// postscriptName += ON_wString::HyphenMinus;
|
|
//// postscriptName += suffix;
|
|
//// }
|
|
////}
|
|
return loc_PostScriptName;
|
|
}
|
|
return ON_wString::EmptyString;
|
|
}
|
|
|
|
void ON_WindowsDWriteFontInformation::Dump(ON_TextLog& text_log) const
|
|
{
|
|
text_log.Print("IDWriteFont:\n");
|
|
ON_TextLogIndent indent1(text_log);
|
|
if (m_prefered_locale.IsNotEmpty())
|
|
text_log.Print("Prefered locale = \"%ls\"\n", static_cast<const wchar_t*>(m_prefered_locale));
|
|
if (FamilyName().IsNotEmpty())
|
|
{
|
|
if ( FaceName().IsNotEmpty())
|
|
text_log.Print("Iteration index = family[%d].font[%u]\n", m_family_index, m_family_font_index);
|
|
else
|
|
text_log.Print("Iteration index = family[%d]\n", m_family_index);
|
|
}
|
|
|
|
text_log.Print(L"Localized Names:\n");
|
|
text_log.PushIndent();
|
|
text_log.Print(L"Family name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_family_name));
|
|
text_log.Print(L"Face name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_face_name));
|
|
text_log.Print(L"PostScript name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_postscript_name));
|
|
text_log.Print(L"GDI LOGFONT name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_gdi_family_name));
|
|
text_log.Print(L"GDI subfamily name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_gdi_subfamily_name));
|
|
text_log.Print(L"Full name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_full_name));
|
|
text_log.PopIndent();
|
|
|
|
text_log.Print(L"English Names:\n");
|
|
text_log.PushIndent();
|
|
text_log.Print(L"Family name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_family_name));
|
|
text_log.Print(L"Face name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_face_name));
|
|
text_log.Print(L"PostScript name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_postscript_name));
|
|
text_log.Print(L"GDI LOGFONT name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_gdi_family_name));
|
|
text_log.Print(L"GDI subfamily name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_gdi_subfamily_name));
|
|
text_log.Print(L"Full name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_full_name));
|
|
text_log.PopIndent();
|
|
|
|
if (m_bSimulatedBold || m_bSimulatedOblique || m_bSimulatedOther)
|
|
{
|
|
text_log.Print("Simuilations =");
|
|
if (m_bSimulatedBold)
|
|
text_log.Print(" BOLD");
|
|
if (m_bSimulatedOblique)
|
|
text_log.Print(" OBLIQUE");
|
|
if (m_bSimulatedOther)
|
|
text_log.Print(" other");
|
|
text_log.PrintNewLine();
|
|
}
|
|
|
|
const DWRITE_FONT_WEIGHT dwrite_weight = (DWRITE_FONT_WEIGHT)m_weight;
|
|
const ON_wString weightString = Internal_DWRITE_FONT_WEIGHT_ToString(dwrite_weight);
|
|
text_log.Print(L"Weight = %ls\n", static_cast<const wchar_t*>(weightString));
|
|
|
|
const DWRITE_FONT_STYLE dwrite_style = (DWRITE_FONT_STYLE)m_style;
|
|
const ON_wString styleString = Internal_DWRITE_FONT_STYLE_ToString(dwrite_style);
|
|
text_log.Print(L"Style = %ls\n", static_cast<const wchar_t*>(styleString));
|
|
|
|
const DWRITE_FONT_STRETCH dwrite_stretch = (DWRITE_FONT_STRETCH)m_stretch;
|
|
const ON_wString stretchString = Internal_DWRITE_FONT_STRETCH_ToString(dwrite_stretch);
|
|
text_log.Print(L"Stretch = %ls\n", static_cast<const wchar_t*>(stretchString));
|
|
|
|
text_log.Print("IsSymbolFont = %s.\n",(m_bIsSymbolFont?"true":"false"));
|
|
|
|
if (0 != m_gdi_interop_logfont.lfFaceName[0])
|
|
{
|
|
text_log.Print("GDI interop IsSystemFont = %s\n", (m_gdi_interop_logfont_bIsSystemFont ? "true" : "false"));
|
|
text_log.Print("GDI interop ");
|
|
ON_Font::DumpLogfont(&m_gdi_interop_logfont, text_log);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DWRITE_FONT_METRICS to ON_FontMetrics
|
|
//
|
|
|
|
const ON_FontMetrics ON_FontMetrics::CreateFromDWriteFontMetrics(const struct DWRITE_FONT_METRICS* dwrite_font_metrics)
|
|
{
|
|
if (nullptr == dwrite_font_metrics)
|
|
return ON_FontMetrics::Unset;
|
|
|
|
ON_FontMetrics fm;
|
|
const int ascent = ((int)dwrite_font_metrics->ascent);
|
|
|
|
// dwrite_font_metrics->descent is unsigned and hence >= 0
|
|
const int signed_descent = -((int)dwrite_font_metrics->descent);
|
|
|
|
// line_gap is signed and can be positive or negative.
|
|
const int line_gap = ((int)dwrite_font_metrics->lineGap);
|
|
|
|
const int line_space = ascent - signed_descent + line_gap;
|
|
|
|
fm.SetHeights(
|
|
dwrite_font_metrics->ascent,
|
|
signed_descent,
|
|
dwrite_font_metrics->designUnitsPerEm,
|
|
line_space
|
|
);
|
|
|
|
const int cap_height = ((int)dwrite_font_metrics->capHeight);
|
|
fm.SetAscentOfCapital(cap_height);
|
|
|
|
const int x_height = ((int)dwrite_font_metrics->xHeight);
|
|
fm.SetAscentOfx(x_height);
|
|
|
|
fm.SetStrikeout(
|
|
(int)(dwrite_font_metrics->strikethroughPosition),
|
|
(int)(dwrite_font_metrics->strikethroughThickness)
|
|
);
|
|
|
|
fm.SetUnderscore(
|
|
(int)(dwrite_font_metrics->underlinePosition),
|
|
(int)(dwrite_font_metrics->underlineThickness)
|
|
);
|
|
|
|
return fm;
|
|
}
|
|
|
|
const ON_FontMetrics ON_FontMetrics::CreateFromDWriteFont(struct IDWriteFont* dwrite_font)
|
|
{
|
|
if (nullptr == dwrite_font)
|
|
return ON_FontMetrics::Unset;
|
|
|
|
DWRITE_FONT_METRICS dwriteFontMetrics;
|
|
memset(&dwriteFontMetrics, 0, sizeof(dwriteFontMetrics));
|
|
dwrite_font->GetMetrics(&dwriteFontMetrics);
|
|
return ON_FontMetrics::CreateFromDWriteFontMetrics(&dwriteFontMetrics);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Get list of fonts installed on this Windows device
|
|
//
|
|
|
|
static int Internal_CompareDWriteFontInformationSort0(
|
|
const ON_WindowsDWriteFontInformation* lhs,
|
|
const ON_WindowsDWriteFontInformation* rhs
|
|
)
|
|
{
|
|
if (lhs == rhs)
|
|
return 0;
|
|
if (nullptr == lhs)
|
|
return 1;
|
|
if (nullptr == rhs)
|
|
return -1;
|
|
|
|
const ON_wString lhs_family = lhs->FamilyName();
|
|
const ON_wString rhs_family = rhs->FamilyName();
|
|
int rc = ON_wString::CompareOrdinal(lhs_family, rhs_family, true);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
const ON_wString lhs_logfont = lhs->WindowsLogfontName();
|
|
const ON_wString rhs_logfont = rhs->WindowsLogfontName();
|
|
rc = ON_wString::CompareOrdinal(lhs_logfont, rhs_logfont, true);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ((int)lhs->m_weight) - ((int)rhs->m_weight);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ((int)lhs->m_stretch) - ((int)rhs->m_stretch);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
const int lhs_style
|
|
= (lhs->m_bSimulatedOblique && DWRITE_FONT_STYLE_OBLIQUE == lhs->m_style)
|
|
? DWRITE_FONT_STYLE_ITALIC
|
|
: lhs->m_style;
|
|
const int rhs_style
|
|
= (rhs->m_bSimulatedOblique && DWRITE_FONT_STYLE_OBLIQUE == rhs->m_style)
|
|
? DWRITE_FONT_STYLE_ITALIC
|
|
: rhs->m_style;
|
|
rc = lhs_style - rhs_style;
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool Internal_DWriteFontIsSimulated(
|
|
const ON_WindowsDWriteFontInformation* f
|
|
)
|
|
{
|
|
return (f->m_bSimulatedBold || f->m_bSimulatedOblique || f->m_bSimulatedOther);
|
|
}
|
|
|
|
static int Internal_CompareDWriteFontInformationSort1(
|
|
const ON_WindowsDWriteFontInformation* lhs,
|
|
const ON_WindowsDWriteFontInformation* rhs
|
|
)
|
|
{
|
|
int rc = Internal_CompareDWriteFontInformationSort0(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
if (nullptr == lhs)
|
|
return 1;
|
|
if (nullptr == rhs)
|
|
return -1;
|
|
|
|
const int lhs_sim = Internal_DWriteFontIsSimulated(lhs) ? 1 : 0;
|
|
const int rhs_sim = Internal_DWriteFontIsSimulated(rhs) ? 1 : 0;
|
|
rc = (lhs_sim - rhs_sim);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//// Used to test ability to fallback to English file name when
|
|
//// files from a different local are read.
|
|
//static const ON_wString Internal_FakeLocaleTest(
|
|
// const ON_wString loc_str
|
|
//)
|
|
//{
|
|
// if (loc_str.IsEmpty())
|
|
// return ON_wString::EmptyString;
|
|
// const ON_wString fake_loc_str = ON_wString(L"FAKE") + loc_str;
|
|
// return fake_loc_str;
|
|
//}
|
|
//
|
|
//static void Internal_FakeLocaleTest(
|
|
// ON_WindowsDWriteFontInformation& fi
|
|
//)
|
|
//{
|
|
// fi.m_prefered_locale = L"fa-ke";
|
|
// fi.m_loc_family_name = Internal_FakeLocaleTest(fi.m_loc_family_name);
|
|
// fi.m_loc_face_name = Internal_FakeLocaleTest(fi.m_loc_face_name);
|
|
// fi.m_loc_postscript_name = Internal_FakeLocaleTest(fi.m_loc_postscript_name);
|
|
// fi.m_loc_gdi_family_name = Internal_FakeLocaleTest(fi.m_loc_gdi_family_name);
|
|
// fi.m_loc_gdi_subfamily_name = Internal_FakeLocaleTest(fi.m_loc_gdi_subfamily_name);
|
|
// ON_wString fake_logfont = Internal_FakeLocaleTest(fi.m_gdi_interop_logfont.lfFaceName);
|
|
// const size_t sz_lfFacename = sizeof(fi.m_gdi_interop_logfont.lfFaceName);
|
|
// const size_t sz_fakeFaceName = fake_logfont.Length() * sizeof(wchar_t);
|
|
// if (sz_fakeFaceName < sz_lfFacename)
|
|
// {
|
|
// wchar_t* a = fake_logfont.Array();
|
|
// memset(fi.m_gdi_interop_logfont.lfFaceName, 0, sz_lfFacename);
|
|
// memcpy(fi.m_gdi_interop_logfont.lfFaceName, a, sz_fakeFaceName);
|
|
// }
|
|
//}
|
|
|
|
unsigned int ON_Font::GetInstalledWindowsDWriteFonts(
|
|
const wchar_t* preferedLocale,
|
|
bool bIncludeSimulatedFontFaces,
|
|
bool bKeepDWriteFont,
|
|
ON_SimpleArray<ON_WindowsDWriteFontInformation>& dwrite_font_list
|
|
)
|
|
{
|
|
dwrite_font_list.SetCount(0);
|
|
|
|
const wchar_t* englishLocale = L"en-us";
|
|
|
|
for (;;)
|
|
{
|
|
IDWriteFactory* dwriteFactory = ON_IDWrite::Factory();
|
|
if (nullptr == dwriteFactory)
|
|
break;
|
|
|
|
BOOL exists;
|
|
HRESULT hr;
|
|
|
|
// Get the system font collection.
|
|
Microsoft::WRL::ComPtr<IDWriteFontCollection> dwriteFontCollection = nullptr;
|
|
hr = dwriteFactory->GetSystemFontCollection(&dwriteFontCollection);
|
|
if (FAILED(hr))
|
|
break;
|
|
if (nullptr == dwriteFontCollection || nullptr == dwriteFontCollection.Get())
|
|
break;
|
|
const UINT32 familyCount = dwriteFontCollection->GetFontFamilyCount();
|
|
if (familyCount < 1)
|
|
break;
|
|
|
|
wchar_t userDefaultLocale[LOCALE_NAME_MAX_LENGTH + 1] = {};
|
|
if (ON_wString::EqualOrdinal(preferedLocale, L"GetUserDefaultLocaleName", true))
|
|
{
|
|
// Get the default locale for this user.
|
|
userDefaultLocale[LOCALE_NAME_MAX_LENGTH] = 0;
|
|
if (0 == ::GetUserDefaultLocaleName(userDefaultLocale, LOCALE_NAME_MAX_LENGTH))
|
|
userDefaultLocale[0];
|
|
userDefaultLocale[LOCALE_NAME_MAX_LENGTH] = 0;
|
|
preferedLocale = userDefaultLocale;
|
|
}
|
|
|
|
IDWriteGdiInterop* dwriteGdiInterop = ON_IDWrite::GdiInterop();
|
|
if (nullptr == dwriteGdiInterop)
|
|
break;
|
|
|
|
dwrite_font_list.Reserve(5 * familyCount);
|
|
|
|
for (UINT32 familyIndex = 0; familyIndex < familyCount; ++familyIndex)
|
|
{
|
|
Microsoft::WRL::ComPtr<IDWriteFontFamily> dwriteFontFamily = nullptr;
|
|
hr = dwriteFontCollection->GetFontFamily(familyIndex, &dwriteFontFamily);
|
|
if (FAILED(hr))
|
|
continue;
|
|
if (nullptr == dwriteFontFamily || nullptr == dwriteFontFamily.Get())
|
|
continue;
|
|
|
|
// Family name
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> familyNames = nullptr;
|
|
hr = dwriteFontFamily->GetFamilyNames(&familyNames);
|
|
ON_wString loc_family_name;
|
|
ON_wString en_family_name;
|
|
Internal_GetLocalizeStrings(preferedLocale, familyNames.Get(), loc_family_name, en_family_name);
|
|
|
|
const UINT32 fontCount = dwriteFontFamily->GetFontCount();
|
|
for (UINT32 fontIndex = 0; fontIndex < fontCount; fontIndex++)
|
|
{
|
|
Microsoft::WRL::ComPtr<IDWriteFont> dwriteFont = nullptr;
|
|
hr = dwriteFontFamily->GetFont(fontIndex, &dwriteFont);
|
|
if (FAILED(hr))
|
|
continue;
|
|
if (nullptr == dwriteFont || nullptr == dwriteFont.Get())
|
|
continue;
|
|
|
|
ON_WindowsDWriteFontInformation fi;
|
|
fi.m_prefered_locale = preferedLocale;
|
|
memset(&fi.m_gdi_interop_logfont, 0, sizeof(fi.m_gdi_interop_logfont));
|
|
|
|
const DWRITE_FONT_SIMULATIONS simulations = dwriteFont->GetSimulations();
|
|
if (DWRITE_FONT_SIMULATIONS_NONE != simulations)
|
|
{
|
|
if (false == bIncludeSimulatedFontFaces)
|
|
continue;
|
|
DWRITE_FONT_SIMULATIONS s = DWRITE_FONT_SIMULATIONS_NONE;
|
|
if (0 != (DWRITE_FONT_SIMULATIONS_BOLD & simulations))
|
|
{
|
|
s |= DWRITE_FONT_SIMULATIONS_BOLD;
|
|
fi.m_bSimulatedBold = true;
|
|
}
|
|
if (0 != (DWRITE_FONT_SIMULATIONS_OBLIQUE & simulations))
|
|
{
|
|
s |= DWRITE_FONT_SIMULATIONS_OBLIQUE;
|
|
fi.m_bSimulatedOblique = true;
|
|
}
|
|
if (s != simulations)
|
|
fi.m_bSimulatedOther = true;
|
|
}
|
|
|
|
// Face name
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> faceNames = nullptr;
|
|
hr = dwriteFont->GetFaceNames(&faceNames);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Internal_GetLocalizeStrings(preferedLocale, faceNames.Get(), fi.m_loc_face_name, fi.m_en_face_name);
|
|
}
|
|
|
|
fi.m_weight = dwriteFont->GetWeight();
|
|
fi.m_style = dwriteFont->GetStyle();
|
|
fi.m_stretch = dwriteFont->GetStretch();
|
|
fi.m_bIsSymbolFont = (dwriteFont->IsSymbolFont() ? true : false);
|
|
|
|
// Full name
|
|
exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> fullNames = nullptr;
|
|
hr = dwriteFont->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_FULL_NAME, &fullNames, &exists);
|
|
if (SUCCEEDED(hr) && exists)
|
|
{
|
|
Internal_GetLocalizeStrings(preferedLocale, fullNames.Get(), fi.m_loc_full_name, fi.m_en_full_name );
|
|
}
|
|
|
|
// PostScript name
|
|
fi.m_loc_postscript_name = ON_Font::PostScriptNameFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_postscript_name = ON_Font::PostScriptNameFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// GDI family name
|
|
exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> gdiFamilyNames = nullptr;
|
|
hr = dwriteFont->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &gdiFamilyNames, &exists);
|
|
if (SUCCEEDED(hr) && exists)
|
|
{
|
|
Internal_GetLocalizeStrings(preferedLocale, gdiFamilyNames.Get(), fi.m_loc_gdi_family_name, fi.m_en_gdi_family_name);
|
|
}
|
|
|
|
// GDI sub-family name
|
|
exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> gdiSubfamilyNames = nullptr;
|
|
hr = dwriteFont->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, &gdiSubfamilyNames, &exists);
|
|
if (SUCCEEDED(hr) && exists)
|
|
{
|
|
Internal_GetLocalizeStrings(preferedLocale, gdiSubfamilyNames.Get(), fi.m_loc_gdi_subfamily_name, fi.m_en_gdi_subfamily_name);
|
|
}
|
|
|
|
BOOL isSystemFont = false;
|
|
LOGFONTW logfont;
|
|
memset(&logfont, 0, sizeof(logfont));
|
|
hr = dwriteGdiInterop->ConvertFontToLOGFONT(dwriteFont.Get(), &logfont, &isSystemFont);
|
|
if (SUCCEEDED(hr) && 0 != logfont.lfFaceName[0])
|
|
{
|
|
logfont.lfHeight = 0;
|
|
if (0 != logfont.lfItalic) logfont.lfItalic = 1;
|
|
if (0 != logfont.lfUnderline) logfont.lfUnderline = 1;
|
|
if (0 != logfont.lfStrikeOut) logfont.lfStrikeOut = 1;
|
|
if (ON_Font::WindowsConstants::logfont_symbol_charset != logfont.lfCharSet)
|
|
logfont.lfCharSet = ON_Font::WindowsConstants::logfont_default_charset;
|
|
logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis;
|
|
logfont.lfClipPrecision = 0;
|
|
logfont.lfQuality = ON_Font::WindowsConstants::logfont_quality;
|
|
logfont.lfPitchAndFamily = ON_Font::WindowsConstants::logfont_pitch_and_family;
|
|
fi.m_gdi_interop_logfont = logfont;
|
|
fi.m_gdi_interop_logfont_bIsSystemFont = isSystemFont ? true : false;
|
|
}
|
|
|
|
if (0 == fi.m_gdi_interop_logfont.lfFaceName[0])
|
|
{
|
|
// During testing on Windows 10, this never occured.
|
|
ON_ERROR("Empty LOGFONT lfFaceName");
|
|
continue;
|
|
}
|
|
|
|
fi.m_font_metrics = ON_FontMetrics::CreateFromDWriteFont(dwriteFont.Get());
|
|
|
|
for (;;)
|
|
{
|
|
IDWriteFont1* dwriteFont1 = nullptr;
|
|
hr = dwriteFont.Get()->QueryInterface(__uuidof(IDWriteFont1), (void**)&dwriteFont1);
|
|
if (FAILED(hr))
|
|
break; // On some versions of Windows 7, IDWriteFont1 is not available
|
|
if (nullptr == dwriteFont1)
|
|
break;
|
|
DWRITE_PANOSE dwritePANOSE;
|
|
memset(&dwritePANOSE, 0, sizeof(dwritePANOSE));
|
|
dwriteFont1->GetPanose(&dwritePANOSE);
|
|
ON_PANOSE1 panose1;
|
|
fi.m_panose1.SetTenBytes(dwritePANOSE.values);
|
|
dwriteFont1->Release();
|
|
break;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
Microsoft::WRL::ComPtr<IDWriteFontFace> dwriteFontFace = nullptr;
|
|
hr = dwriteFont->CreateFontFace(&dwriteFontFace);
|
|
if (FAILED(hr))
|
|
break;
|
|
if (nullptr == dwriteFontFace || nullptr == dwriteFontFace.Get())
|
|
break;
|
|
|
|
UINT32 codepoints[] = {ON_wString::Space, 'H', 'I', 'x'};
|
|
UINT32 codepointCount = ((UINT32)sizeof(codepoints) / sizeof(codepoints[0]));
|
|
UINT16 glyphIndices[sizeof(codepoints) / sizeof(codepoints[0])] = {};
|
|
hr = dwriteFontFace.Get()->GetGlyphIndices(codepoints, codepointCount, glyphIndices);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
for (UINT32 k = 0; k < codepointCount; k++)
|
|
{
|
|
UINT16 glyphIndex = glyphIndices[k];
|
|
if (0 == glyphIndex)
|
|
continue;
|
|
DWRITE_GLYPH_METRICS glyphMetric;
|
|
memset(&glyphMetric, 0, sizeof(glyphMetric));
|
|
BOOL isSideways = false;
|
|
hr = dwriteFontFace.Get()->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetric, isSideways);
|
|
if (FAILED(hr))
|
|
break;
|
|
ON_FontGlyph glyph_box;
|
|
glyph_box.m_code_point = codepoints[k];
|
|
glyph_box.m_font_glyph_id = glyphIndex;
|
|
|
|
// In font design units
|
|
glyph_box.m_font_unit_glyph_bbox = ON_TextBox::CreateFromDWriteGlyphMetrics(&glyphMetric);
|
|
|
|
// NOT applicable
|
|
glyph_box.m_font_unit_glyph_bbox.m_max_basepoint.i = 0;
|
|
glyph_box.m_font_unit_glyph_bbox.m_max_basepoint.j = 0;
|
|
|
|
if (fi.m_font_metrics.UPM() > 0)
|
|
{
|
|
double scale = ((double)ON_Font::AnnotationFontCellHeight) / ((double)fi.m_font_metrics.UPM());
|
|
glyph_box.m_normalized_glyph_bbox = ON_TextBox::Scale(glyph_box.m_font_unit_glyph_bbox, scale);
|
|
}
|
|
|
|
switch (codepoints[k])
|
|
{
|
|
case ON_UnicodeCodePoint::ON_Space:
|
|
fi.m_Spacebox = glyph_box;
|
|
break;
|
|
case 'H':
|
|
fi.m_Hbox = glyph_box;
|
|
break;
|
|
case 'I':
|
|
fi.m_Ibox = glyph_box;
|
|
break;
|
|
case 'x':
|
|
fi.m_xbox = glyph_box;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
fi.m_family_index = familyIndex;
|
|
fi.m_family_font_index = fontIndex;
|
|
fi.m_loc_family_name = loc_family_name;
|
|
fi.m_en_family_name = en_family_name;
|
|
fi.m_prefered_locale = preferedLocale;
|
|
|
|
if (bKeepDWriteFont)
|
|
{
|
|
fi.m_dwrite_font = dwriteFont.Detach();
|
|
}
|
|
|
|
//Internal_FakeLocaleTest(fi);
|
|
dwrite_font_list.AppendNew() = fi;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Removed redundant simulated fonts
|
|
//
|
|
// Example:
|
|
// KEEP Arial+Narrow Italic [normal-condensed-italic] (no simuliations)
|
|
// DISCARD: Arial+Narrow Obliqued [normal-condensed-italic(simulated)]
|
|
//
|
|
// KEEP Arial+Narrow Bold Italic [bold-condensed-italic] (no simuliations)
|
|
// DISCARD: Arial+Narrow Bold Obliqued [(bold-condensed-italic(simulated)]
|
|
//
|
|
// KEEP Arial+Black Italic [heavy-medium-italic(simulated)]
|
|
// because there is not a non-simulated version of Arial Black Italic
|
|
//
|
|
dwrite_font_list.QuickSort(Internal_CompareDWriteFontInformationSort1);
|
|
const unsigned int count0 = dwrite_font_list.Count();
|
|
ON_WindowsDWriteFontInformation* a = dwrite_font_list.Array();
|
|
ON_WindowsDWriteFontInformation* a0 = nullptr;
|
|
unsigned int count1 = 0;
|
|
for ( unsigned int i = 0; i < count0; i++ )
|
|
{
|
|
if ( Internal_DWriteFontIsSimulated(a + i) )
|
|
{
|
|
if ( 0 == Internal_CompareDWriteFontInformationSort0(a0, a + i) )
|
|
continue; // simulation duplicates previous font
|
|
if (a[i].m_bSimulatedBold)
|
|
{
|
|
// skip engraving fonts with simulated bold.
|
|
const ON_OutlineFigure::Type figure_type = ON_OutlineFigure::FigureTypeFromFontName(a[i].FamilyName());
|
|
if (ON_OutlineFigure::Type::SingleStroke == figure_type)
|
|
continue;
|
|
if (ON_OutlineFigure::Type::DoubleStroke == figure_type)
|
|
continue;
|
|
}
|
|
}
|
|
a0 = a + i;
|
|
if ( count1 < i)
|
|
a[count1] = a[i];
|
|
count1++;
|
|
}
|
|
dwrite_font_list.SetCount(count1);
|
|
|
|
return dwrite_font_list.UnsignedCount();
|
|
}
|
|
|
|
const ON_TextBox ON_TextBox::CreateFromDWriteGlyphMetrics(const struct DWRITE_GLYPH_METRICS* dwrite_glyph_metrics)
|
|
{
|
|
if (nullptr == dwrite_glyph_metrics)
|
|
return ON_TextBox::Unset;
|
|
|
|
ON_TextBox glyph_box;
|
|
// See image of Q
|
|
// https://msdn.microsoft.com/en-us/library/system.windows.media.charactermetrics.blackboxheight(v=vs.85)?cs-save-lang=1&cs-lang=vb
|
|
//const int blackBoxWidth = ((int)glyphMetric.advanceWidth) - ((int)glyphMetric.leftSideBearing) - ((int)glyphMetric.rightSideBearing);
|
|
//const int blackBoxHeight = ((int)glyphMetric.advanceHeight) - ((int)glyphMetric.bottomSideBearing) - ((int)glyphMetric.topSideBearing);
|
|
|
|
// In font design units
|
|
glyph_box.m_advance.i = (int)(dwrite_glyph_metrics->advanceWidth);
|
|
glyph_box.m_advance.j = (int)(dwrite_glyph_metrics->advanceHeight);
|
|
|
|
glyph_box.m_bbmin.i = (int)(dwrite_glyph_metrics->leftSideBearing);
|
|
glyph_box.m_bbmax.i = ((int)dwrite_glyph_metrics->advanceWidth) - ((int)dwrite_glyph_metrics->rightSideBearing);
|
|
|
|
glyph_box.m_bbmin.j = ((int)dwrite_glyph_metrics->verticalOriginY) - ((int)dwrite_glyph_metrics->advanceHeight) + ((int)dwrite_glyph_metrics->bottomSideBearing);
|
|
glyph_box.m_bbmax.j = ((int)dwrite_glyph_metrics->verticalOriginY) - ((int)dwrite_glyph_metrics->topSideBearing);
|
|
|
|
return glyph_box;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Glyph outline
|
|
//
|
|
|
|
class ON_IDWriteGlyphOutlineAccumlator : public IDWriteGeometrySink
|
|
{
|
|
public:
|
|
ON_IDWriteGlyphOutlineAccumlator() = default;
|
|
virtual ~ON_IDWriteGlyphOutlineAccumlator()
|
|
{};
|
|
|
|
private:
|
|
ON_IDWriteGlyphOutlineAccumlator(const ON_IDWriteGlyphOutlineAccumlator&) = delete;
|
|
ON_IDWriteGlyphOutlineAccumlator& operator=(const ON_IDWriteGlyphOutlineAccumlator&) = delete;
|
|
|
|
public:
|
|
ON_OutlineAccumulator * m_accumulator = nullptr;
|
|
|
|
D2D1_FILL_MODE m_fill_mode = D2D1_FILL_MODE::D2D1_FILL_MODE_ALTERNATE;
|
|
D2D1_PATH_SEGMENT m_segment_flags = D2D1_PATH_SEGMENT::D2D1_PATH_SEGMENT_NONE;
|
|
|
|
private:
|
|
long m_IDUnknown_reference_count = 0;
|
|
|
|
public:
|
|
// IUnknown overrides
|
|
unsigned long STDMETHODCALLTYPE AddRef() override
|
|
{
|
|
return ++m_IDUnknown_reference_count;
|
|
}
|
|
|
|
unsigned long STDMETHODCALLTYPE Release() override
|
|
{
|
|
return --m_IDUnknown_reference_count;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE QueryInterface(
|
|
IID const& riid,
|
|
void** ppvObject
|
|
) override
|
|
{
|
|
if (nullptr == ppvObject)
|
|
return E_POINTER;
|
|
|
|
if (
|
|
__uuidof(IUnknown) == riid
|
|
|| __uuidof(IDWriteGeometrySink) == riid
|
|
)
|
|
{
|
|
*ppvObject = this;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
public:
|
|
// IDWriteGeometrySink overrides
|
|
void STDMETHODCALLTYPE AddBeziers(
|
|
const D2D1_BEZIER_SEGMENT *beziers,
|
|
UINT32 beziersCount
|
|
) override
|
|
{
|
|
if (nullptr == m_accumulator)
|
|
return;
|
|
for (UINT32 i = 0; i < beziersCount; i++)
|
|
{
|
|
m_accumulator->AppendCubicBezier(
|
|
*((const ON_2fPoint*)&(beziers[i].point1)),
|
|
*((const ON_2fPoint*)&(beziers[i].point2)),
|
|
*((const ON_2fPoint*)&(beziers[i].point3))
|
|
);
|
|
}
|
|
}
|
|
|
|
void STDMETHODCALLTYPE AddLines(
|
|
const D2D1_POINT_2F *points,
|
|
UINT32 pointsCount
|
|
) override
|
|
{
|
|
if (nullptr == m_accumulator)
|
|
return;
|
|
for (UINT32 i = 0; i < pointsCount; i++)
|
|
{
|
|
m_accumulator->AppendLine(*((const ON_2fPoint*)&(points[i])));
|
|
}
|
|
}
|
|
|
|
void STDMETHODCALLTYPE BeginFigure(
|
|
D2D1_POINT_2F startPoint,
|
|
D2D1_FIGURE_BEGIN figureBegin
|
|
) override
|
|
{
|
|
if (nullptr == m_accumulator)
|
|
return;
|
|
|
|
ON_OutlineFigurePoint::Type point_type
|
|
= ON_OutlineFigurePoint::Type::BeginFigureUnknown;
|
|
|
|
// Turns out the figureBegin parameter has no meaning when getting glyph outlines.
|
|
// In tests of many fonts and glyphs, it is always 0.
|
|
|
|
////if (D2D1_FIGURE_BEGIN::D2D1_FIGURE_BEGIN_FILLED == figureBegin)
|
|
//// point_type = ON_OutlineFigurePoint::Type::BeginFigureFilled;
|
|
////else if (D2D1_FIGURE_BEGIN::D2D1_FIGURE_BEGIN_HOLLOW == figureBegin)
|
|
//// point_type = ON_OutlineFigurePoint::Type::BeginFigureHollow;
|
|
////else
|
|
////{
|
|
//// ON_ERROR("Invalid D2D1_FIGURE_BEGIN figureBegin parameter");
|
|
//// m_accumulator->AbandonFigure();
|
|
//// return;
|
|
////}
|
|
|
|
const ON_2fPoint point(startPoint.x, startPoint.y);
|
|
m_accumulator->BeginFigure( point_type, point );
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE Close() override
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
void STDMETHODCALLTYPE EndFigure(D2D1_FIGURE_END figureEnd) override
|
|
{
|
|
if (nullptr == m_accumulator)
|
|
return;
|
|
|
|
ON_OutlineFigurePoint::Type point_type;
|
|
if ( D2D1_FIGURE_END::D2D1_FIGURE_END_OPEN == figureEnd)
|
|
point_type = ON_OutlineFigurePoint::Type::EndFigureOpen;
|
|
else if ( D2D1_FIGURE_END::D2D1_FIGURE_END_CLOSED == figureEnd)
|
|
point_type = ON_OutlineFigurePoint::Type::EndFigureClosed;
|
|
else
|
|
{
|
|
ON_ERROR("Invalid D2D1_FIGURE_END figureEnd parameter");
|
|
m_accumulator->AbandonCurrentFigure();
|
|
return;
|
|
}
|
|
|
|
m_accumulator->EndFigure(point_type);
|
|
}
|
|
|
|
void STDMETHODCALLTYPE SetFillMode(
|
|
D2D1_FILL_MODE fillMode
|
|
) override
|
|
{
|
|
// From Microsoft Docs:
|
|
// The fill mode defaults to D2D1_FILL_MODE_ALTERNATE.
|
|
// To set the fill mode, call SetFillMode before the first call to BeginFigure.
|
|
// Not doing will put the geometry sink in an error state.
|
|
//D2D1_FILL_MODE::D2D1_FILL_MODE_ALTERNATE = 0,
|
|
//D2D1_FILL_MODE::D2D1_FILL_MODE_WINDING = 1,
|
|
if (m_fill_mode != fillMode)
|
|
{
|
|
m_fill_mode = fillMode;
|
|
}
|
|
}
|
|
|
|
void STDMETHODCALLTYPE SetSegmentFlags(
|
|
D2D1_PATH_SEGMENT vertexFlags
|
|
) override
|
|
{
|
|
// From Microsoft Docs:
|
|
// After this method is called, the specified segment flags are applied to each segment subsequently added to the sink.
|
|
// The segment flags are applied to every additional segment until this method is called again and a different set of
|
|
// segment flags is specified.
|
|
//D2D1_PATH_SEGMENT::D2D1_PATH_SEGMENT_NONE = 0,
|
|
//D2D1_PATH_SEGMENT::D2D1_PATH_SEGMENT_FORCE_UNSTROKED = 1,
|
|
//D2D1_PATH_SEGMENT::D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN = 2,
|
|
if (m_segment_flags != vertexFlags)
|
|
{
|
|
m_segment_flags = vertexFlags;
|
|
}
|
|
}
|
|
};
|
|
|
|
bool ON_WindowsDWriteGetGlyphOutline(
|
|
struct IDWriteFont* dwriteFont,
|
|
unsigned int dwriteGlyphIndex,
|
|
ON_OutlineFigure::Type figure_type,
|
|
class ON_Outline& outline
|
|
)
|
|
{
|
|
outline = ON_Outline::Unset;
|
|
|
|
if (nullptr == dwriteFont)
|
|
return false;
|
|
|
|
if ( dwriteGlyphIndex <= 0 || dwriteGlyphIndex > 0xFFFF )
|
|
return false;
|
|
|
|
DWRITE_FONT_METRICS dwriteFontMetrics;
|
|
memset(&dwriteFontMetrics, 0, sizeof(dwriteFontMetrics));
|
|
dwriteFont->GetMetrics(&dwriteFontMetrics);
|
|
|
|
|
|
Microsoft::WRL::ComPtr<IDWriteFontFace> dwriteFontFace = nullptr;
|
|
HRESULT hr = dwriteFont->CreateFontFace(&dwriteFontFace);
|
|
if (FAILED(hr))
|
|
return false;
|
|
if (nullptr == dwriteFontFace || nullptr == dwriteFontFace.Get())
|
|
return false;
|
|
|
|
ON_OutlineAccumulator accumulator;
|
|
accumulator.BeginGlyphOutline(
|
|
dwriteFontMetrics.designUnitsPerEm,
|
|
figure_type,
|
|
&outline
|
|
);
|
|
|
|
ON_IDWriteGlyphOutlineAccumlator dwrite_accumulator;
|
|
|
|
dwrite_accumulator.m_accumulator = &accumulator;
|
|
|
|
const UINT32 glyphCount = 1;
|
|
const BOOL isSideways = 0; // FALSE - We want the single glyph with respect to the English baseline
|
|
const BOOL isRightToLeft = 0; // FALSE - We want the single glyph positioned for LTR rendering (RTL adjustments made elsewhere)
|
|
const FLOAT emSize = dwriteFontMetrics.designUnitsPerEm; //96.0f; // in DIP units 1 DIP = 1/96 inch
|
|
const UINT16 glyphIndices[2] = { (UINT16)dwriteGlyphIndex, 0 };
|
|
|
|
dwriteFontFace->GetGlyphRunOutline(
|
|
emSize,
|
|
glyphIndices,
|
|
nullptr, // OPTIONAL - FLOAT glyphAdvances[]
|
|
nullptr, // OPTIONAL - DWRITE_GLYPH_OFFSET glyphOffsets[]
|
|
glyphCount,
|
|
isSideways,
|
|
isRightToLeft, // FALSE - We want the single glyph positioned for LTR rendering (RTL adjustments made elsewhere)
|
|
&dwrite_accumulator
|
|
);
|
|
|
|
const bool bNegatePointY = true;
|
|
// NOTE WELL:
|
|
// OpenType fonts can have glyphs with either orientation.
|
|
// We have to specify ON_OutlineFigure::Orientation::Clockwise
|
|
// to get consistent results. ON_OutlineFigure::Orientation::Clockwise
|
|
// is the default "TrueType" orientation. The Klavika OpenType font is
|
|
// an example where the outlines returned by DirectWrite are "backwards"
|
|
// from those returned by
|
|
accumulator.EndOutline(bNegatePointY,ON_OutlineFigure::Orientation::Clockwise);
|
|
|
|
DWRITE_GLYPH_METRICS glyphMetrics[2] = {};
|
|
|
|
hr = dwriteFontFace->GetDesignGlyphMetrics(
|
|
glyphIndices,
|
|
glyphCount,
|
|
glyphMetrics,
|
|
isSideways
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
outline.SetGlyphMetrics( ON_TextBox::CreateFromDWriteGlyphMetrics(&glyphMetrics[0]) );
|
|
}
|
|
else
|
|
{
|
|
outline.SetGlyphMetrics( ON_TextBox::Unset );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_WindowsDWriteGetGlyphMetrics(
|
|
struct IDWriteFont* dwriteFont,
|
|
unsigned int dwriteGlyphIndex,
|
|
class ON_TextBox& glyph_metrics
|
|
)
|
|
{
|
|
glyph_metrics = ON_TextBox::Unset;
|
|
|
|
if (nullptr == dwriteFont)
|
|
return false;
|
|
|
|
if ( dwriteGlyphIndex <= 0 || dwriteGlyphIndex > 0xFFFF )
|
|
return false;
|
|
|
|
Microsoft::WRL::ComPtr<IDWriteFontFace> dwriteFontFace = nullptr;
|
|
HRESULT hr = dwriteFont->CreateFontFace(&dwriteFontFace);
|
|
if (FAILED(hr))
|
|
return false;
|
|
if (nullptr == dwriteFontFace || nullptr == dwriteFontFace.Get())
|
|
return false;
|
|
|
|
const UINT32 glyphCount = 1;
|
|
const BOOL isSideways = 0;
|
|
const BOOL isRightToLeft = 0;
|
|
const UINT16 glyphIndices[2] = { (UINT16)dwriteGlyphIndex, 0 };
|
|
|
|
DWRITE_GLYPH_METRICS glyphMetrics[2] = {};
|
|
|
|
hr = dwriteFontFace->GetDesignGlyphMetrics(
|
|
glyphIndices,
|
|
glyphCount,
|
|
glyphMetrics,
|
|
isSideways
|
|
);
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
glyph_metrics = ON_TextBox::CreateFromDWriteGlyphMetrics(&glyphMetrics[0]);
|
|
return true;
|
|
}
|
|
|
|
void ON_WindowsDWriteGetFontMetrics(
|
|
const ON_Font* font,
|
|
ON_FontMetrics& font_metrics
|
|
)
|
|
{
|
|
font_metrics = ON_FontMetrics::Unset;
|
|
|
|
for (;;)
|
|
{
|
|
if (nullptr == font)
|
|
break;
|
|
|
|
IDWriteFont* dwriteFont = font->WindowsDWriteFont();
|
|
if (nullptr == dwriteFont)
|
|
break;
|
|
|
|
font_metrics = ON_FontMetrics::CreateFromDWriteFont(dwriteFont);
|
|
|
|
dwriteFont->Release();
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ON__UINT_PTR ON_WindowsDWriteGetGlyphMetrics(
|
|
const ON_FontGlyph* glyph,
|
|
ON_TextBox& glyph_metrics
|
|
)
|
|
{
|
|
glyph_metrics = ON_TextBox::Unset;
|
|
|
|
if (nullptr == glyph)
|
|
return 0;
|
|
|
|
if (false == glyph->CodePointIsSet())
|
|
return 0;
|
|
|
|
const ON_Font* font = glyph->Font();
|
|
if (nullptr == font)
|
|
return 0;
|
|
|
|
IDWriteFont* dwriteFont = font->WindowsDWriteFont();
|
|
if (nullptr == dwriteFont)
|
|
return 0;
|
|
|
|
ON__UINT_PTR glpyh_id = 0;
|
|
for(;;)
|
|
{
|
|
glpyh_id
|
|
= glyph->FontGlyphIdIsSet()
|
|
? glyph->FontGlyphId()
|
|
: 0;
|
|
if (glpyh_id > 0 && glpyh_id < 0xFFFFFFFF)
|
|
break;
|
|
|
|
Microsoft::WRL::ComPtr<IDWriteFontFace> dwriteFontFace = nullptr;
|
|
HRESULT hr = dwriteFont->CreateFontFace(&dwriteFontFace);
|
|
if (FAILED(hr))
|
|
break;
|
|
if (nullptr == dwriteFontFace || nullptr == dwriteFontFace.Get())
|
|
break;
|
|
|
|
const UINT32 codepoints[] = {glyph->CodePoint(), 0 };
|
|
const UINT32 codepointCount = 1;
|
|
UINT16 glyphIndices[sizeof(codepoints) / sizeof(codepoints[0])] = {};
|
|
hr = dwriteFontFace.Get()->GetGlyphIndices(codepoints, codepointCount, glyphIndices);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
glpyh_id = glyphIndices[0];
|
|
break;
|
|
}
|
|
|
|
const bool rc
|
|
= (0 != glpyh_id)
|
|
? ON_WindowsDWriteGetGlyphMetrics(dwriteFont, (unsigned int)glpyh_id, glyph_metrics)
|
|
: false;
|
|
|
|
dwriteFont->Release();
|
|
|
|
return rc ? glpyh_id : 0;
|
|
}
|
|
|
|
bool ON_WindowsDWriteGetGlyphOutline(
|
|
const ON_FontGlyph* glyph,
|
|
ON_OutlineFigure::Type figure_type,
|
|
class ON_Outline& outline
|
|
)
|
|
{
|
|
outline = ON_Outline::Unset;
|
|
|
|
if (nullptr == glyph)
|
|
return false;
|
|
|
|
if (false == glyph->CodePointIsSet())
|
|
return false;
|
|
|
|
ON__UINT64 glpyh_id = (ON__UINT64)glyph->FontGlyphId();
|
|
if (glpyh_id > 0xFFFFFFFF)
|
|
return false;
|
|
const unsigned int dwriteGlyphIndex = (unsigned int)glpyh_id;
|
|
|
|
const ON_Font* font = glyph->Font();
|
|
if (nullptr == font)
|
|
return false;
|
|
|
|
IDWriteFont* dwriteFont = font->WindowsDWriteFont();
|
|
if (nullptr == dwriteFont)
|
|
return false;
|
|
|
|
if (ON_OutlineFigure::Type::Unset == figure_type)
|
|
{
|
|
ON_OutlineFigure::Type font_figure_type = font->OutlineFigureType();
|
|
if (ON_OutlineFigure::Type::Unset != font_figure_type)
|
|
{
|
|
figure_type = font_figure_type;
|
|
}
|
|
}
|
|
|
|
const bool rc = ON_WindowsDWriteGetGlyphOutline(
|
|
dwriteFont,
|
|
dwriteGlyphIndex,
|
|
figure_type,
|
|
outline
|
|
);
|
|
|
|
dwriteFont->Release();
|
|
|
|
return rc;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_Font <-> IDWriteFont
|
|
//
|
|
|
|
#define ON_FONT_MODIFICATION_PERMITTED this->ModificationPermitted(OPENNURBS__FUNCTION__,__FILE__,__LINE__)
|
|
|
|
bool ON_Font::SetFromWindowsDWriteFont(
|
|
IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
if (false == ON_FONT_MODIFICATION_PERMITTED)
|
|
return false;
|
|
|
|
*this = ON_Font::Unset;
|
|
for (;;)
|
|
{
|
|
if (nullptr == dwrite_font)
|
|
break;
|
|
|
|
IDWriteGdiInterop* dwriteGdiInterop = ON_IDWrite::GdiInterop();
|
|
if (nullptr == dwriteGdiInterop)
|
|
break;
|
|
|
|
BOOL isSystemFont = false;
|
|
LOGFONTW logfont;
|
|
memset(&logfont, 0, sizeof(logfont));
|
|
HRESULT hr = dwriteGdiInterop->ConvertFontToLOGFONT(dwrite_font, &logfont, &isSystemFont);
|
|
if (FAILED(hr))
|
|
break;
|
|
if (0 == logfont.lfFaceName[0])
|
|
break;
|
|
if (SYMBOL_CHARSET != logfont.lfCharSet && DEFAULT_CHARSET != logfont.lfCharSet )
|
|
logfont.lfCharSet = dwrite_font->IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
|
|
logfont.lfHeight = 0;
|
|
if (false == SetFromWindowsLogFont(0, nullptr, logfont))
|
|
break;
|
|
|
|
// LOGFONT lfFaceName
|
|
{
|
|
BOOL exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> gdiLogfontNames = nullptr;
|
|
hr = dwrite_font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &gdiLogfontNames, &exists);
|
|
if (SUCCEEDED(hr) && exists && nullptr != gdiLogfontNames)
|
|
{
|
|
Internal_GetLocalizeStrings(preferedLocale, gdiLogfontNames.Get(), m_loc_windows_logfont_name, m_en_windows_logfont_name);
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
// For simulated fonts, the DWrite GDI family name is sometimes too long for a valid logfont name.
|
|
if (
|
|
m_loc_windows_logfont_name.IsNotEmpty() && m_loc_windows_logfont_name.Length() <= 32
|
|
&& m_en_windows_logfont_name.IsNotEmpty() && m_en_windows_logfont_name.Length() <= 32
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
if (0 == logfont.lfFaceName[0])
|
|
break;
|
|
// logfont_name[] = logfont lfFaceName we are cetain is null terminated.
|
|
wchar_t logfont_name[1 + (sizeof(logfont.lfFaceName) / sizeof(logfont.lfFaceName[0]))];
|
|
const size_t logfont_name_capacity = (sizeof(logfont_name)/sizeof(logfont_name[0])) - 1;
|
|
memcpy(logfont_name, logfont.lfFaceName, logfont_name_capacity*sizeof(logfont_name[0]));
|
|
logfont_name[logfont_name_capacity] = 0;
|
|
if (m_en_windows_logfont_name.IsEmpty() || m_en_windows_logfont_name.Length() > 32)
|
|
m_en_windows_logfont_name = logfont_name;
|
|
if (m_loc_windows_logfont_name.IsEmpty() || m_loc_windows_logfont_name.Length() > 32)
|
|
m_loc_windows_logfont_name = logfont_name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ON_wString name;
|
|
// PostScript name
|
|
name = ON_Font::PostScriptNameFromWindowsDWriteFont(dwrite_font, preferedLocale);
|
|
if ( name.IsNotEmpty() )
|
|
m_loc_postscript_name = name;
|
|
name = ON_Font::PostScriptNameFromWindowsDWriteFont(dwrite_font, L"en-us");
|
|
if ( name.IsNotEmpty() )
|
|
m_en_postscript_name = name;
|
|
|
|
// Family name
|
|
for (;;)
|
|
{
|
|
Microsoft::WRL::ComPtr<IDWriteFontFamily> pIDWriteFontFamily = nullptr;
|
|
hr = dwrite_font->GetFontFamily(&pIDWriteFontFamily);
|
|
if ((SUCCEEDED(hr)) && nullptr != pIDWriteFontFamily && nullptr != pIDWriteFontFamily.Get())
|
|
{
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> familyNames = nullptr;
|
|
hr = pIDWriteFontFamily->GetFamilyNames(&familyNames);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Internal_GetLocalizeStrings(preferedLocale, familyNames.Get(), m_loc_family_name, m_en_family_name);
|
|
}
|
|
}
|
|
|
|
if (m_loc_family_name.IsNotEmpty() && m_en_family_name.IsNotEmpty())
|
|
break;
|
|
|
|
BOOL exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> preferedFamilyNames = nullptr;
|
|
hr = dwrite_font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES, &preferedFamilyNames, &exists);
|
|
if (SUCCEEDED(hr) && exists && nullptr != preferedFamilyNames)
|
|
{
|
|
ON_wString locName;
|
|
ON_wString enName;
|
|
Internal_GetLocalizeStrings(preferedLocale, preferedFamilyNames.Get(), locName, enName);
|
|
if (m_loc_family_name.IsEmpty())
|
|
m_loc_family_name = locName;
|
|
if ( m_en_family_name.IsEmpty())
|
|
m_en_family_name = enName;
|
|
if (m_loc_family_name.IsNotEmpty() && m_en_family_name.IsNotEmpty())
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Face name
|
|
{
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> faceNames = nullptr;
|
|
hr = dwrite_font->GetFaceNames(&faceNames);
|
|
if (SUCCEEDED(hr) && nullptr != faceNames)
|
|
{
|
|
Internal_GetLocalizeStrings(preferedLocale, faceNames.Get(), m_loc_face_name, m_en_face_name);
|
|
}
|
|
}
|
|
|
|
m_font_origin = ON_Font::Origin::WindowsFont;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
IDWriteFont* ON_Font::WindowsDWriteFont() const
|
|
{
|
|
for (;;)
|
|
{
|
|
IDWriteGdiInterop* dwriteGdiInterop = ON_IDWrite::GdiInterop();
|
|
if (nullptr == dwriteGdiInterop)
|
|
break;
|
|
|
|
LOGFONTW logfont = WindowsLogFont(0,nullptr);
|
|
if (0 == logfont.lfFaceName[0])
|
|
break;
|
|
|
|
IDWriteFont* dwriteFont = nullptr;
|
|
HRESULT hr = dwriteGdiInterop->CreateFontFromLOGFONT(&logfont, &dwriteFont);
|
|
if (FAILED(hr))
|
|
break;
|
|
if (nullptr == dwriteFont)
|
|
break;
|
|
|
|
return dwriteFont;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
|
|
#endif
|