mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 19:46:08 +08:00
2463 lines
88 KiB
C++
2463 lines
88 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 ON_wString preferedLocaleDirty,
|
|
IDWriteLocalizedStrings* pIDWriteLocalizedStrings,
|
|
ON_wString& defaultLocaleString,
|
|
ON_wString& defaultLocale,
|
|
ON_wString& enLocaleString,
|
|
ON_wString& enLocale
|
|
)
|
|
{
|
|
defaultLocaleString = ON_wString::EmptyString;
|
|
defaultLocale = ON_wString::EmptyString;
|
|
enLocaleString = ON_wString::EmptyString;
|
|
enLocale = ON_wString::EmptyString;
|
|
|
|
|
|
// get a clean preferedLocale with reliable storage for the string.
|
|
ON_wString preferedLocale(preferedLocaleDirty.Duplicate());
|
|
preferedLocale.TrimLeftAndRight();
|
|
if (ON_wString::EqualOrdinal(preferedLocale, L"GetUserDefaultLocaleName", true))
|
|
{
|
|
// Get the default locale for this user.
|
|
wchar_t userDefaultLocale[LOCALE_NAME_MAX_LENGTH + 1] = {};
|
|
userDefaultLocale[LOCALE_NAME_MAX_LENGTH] = 0;
|
|
if (0 == ::GetUserDefaultLocaleName(userDefaultLocale, LOCALE_NAME_MAX_LENGTH))
|
|
userDefaultLocale[0];
|
|
userDefaultLocale[LOCALE_NAME_MAX_LENGTH] = 0;
|
|
preferedLocale = ON_wString(userDefaultLocale);
|
|
preferedLocale.TrimLeftAndRight();
|
|
}
|
|
|
|
const wchar_t* localeNames[] =
|
|
{
|
|
static_cast<const wchar_t*>(preferedLocale),
|
|
|
|
// The preferred English dialect should be listed next
|
|
L"en-us", // United States
|
|
|
|
// Other known English dialects are list below
|
|
L"en-au", // Australia
|
|
L"en-bz", // Belize
|
|
L"en-ca", // Canada
|
|
L"en-cb", // Caribbean
|
|
L"en-gb", // Great Britain
|
|
L"en-in", // India
|
|
L"en-ie", // Ireland
|
|
L"en-jm", // Jamaica
|
|
L"en-nz", // New Zealand
|
|
L"en-ph", // Philippines
|
|
L"en-tt", // Trinidad
|
|
L"en-za", // Southern Africa
|
|
};
|
|
|
|
const int localNamesCount = (int)(sizeof(localeNames)/sizeof(localeNames[0]));
|
|
|
|
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 enLocaleIndex = ON_UNSET_UINT_INDEX;
|
|
for (int i = 0; i < localNamesCount; i++)
|
|
{
|
|
const wchar_t* s = localeNames[i];
|
|
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)
|
|
{
|
|
enLocaleIndex = idx;
|
|
enLocale = s;
|
|
}
|
|
else
|
|
{
|
|
// preferred locale
|
|
localeIndex = idx;
|
|
if (
|
|
0 != s[0] && 0 != s[1] && 0 != s[2]
|
|
&& ON_wString::EqualOrdinal(L"en-", 3, s, 3, true)
|
|
)
|
|
{
|
|
// preferred local is the preferred English dialect too.
|
|
enLocaleIndex = idx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int pass = 0; pass < 3; pass++)
|
|
{
|
|
if (pass > 1)
|
|
{
|
|
if (defaultLocaleString.IsNotEmpty() || enLocaleString.IsNotEmpty())
|
|
break;
|
|
}
|
|
|
|
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 == enLocaleIndex || enLocaleIndex == localeIndex )
|
|
continue;
|
|
i0 = enLocaleIndex;
|
|
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;
|
|
if (i == localeIndex)
|
|
strLocaleName = preferedLocale;
|
|
else if (i == enLocaleIndex)
|
|
strLocaleName = enLocale;
|
|
for (int value = 0; value < (strLocaleName.IsEmpty()?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;
|
|
defaultLocale = strLocaleName;
|
|
}
|
|
|
|
if (i == localeIndex)
|
|
{
|
|
defaultLocaleString = strStringValue;
|
|
defaultLocale = strLocaleName;
|
|
}
|
|
|
|
if (i == enLocaleIndex)
|
|
{
|
|
enLocaleString = strStringValue;
|
|
enLocale = strLocaleName;
|
|
}
|
|
|
|
if (
|
|
defaultLocaleString.IsNotEmpty()
|
|
&& enLocaleString.IsNotEmpty()
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (defaultLocaleString.IsEmpty())
|
|
{
|
|
defaultLocaleString = enLocaleString;
|
|
defaultLocale = enLocale;
|
|
}
|
|
|
|
return defaultLocaleString.IsNotEmpty();
|
|
}
|
|
|
|
|
|
static bool Internal_GetLocalizeStrings(
|
|
const wchar_t* preferedLocale,
|
|
IDWriteLocalizedStrings* pIDWriteLocalizedStrings,
|
|
ON_wString& defaultLocaleString,
|
|
ON_wString& enLocaleString
|
|
)
|
|
{
|
|
ON_wString defaultLocale;
|
|
ON_wString enusLocale;
|
|
return Internal_GetLocalizeStrings(
|
|
preferedLocale,
|
|
pIDWriteLocalizedStrings,
|
|
defaultLocaleString, defaultLocale,
|
|
enLocaleString, enusLocale
|
|
);
|
|
}
|
|
|
|
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[] =
|
|
{
|
|
// field 4
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_FULL_NAME,L"DWrite full name"),
|
|
|
|
// field 6
|
|
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"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME,L"DWrite weight-stretch-style model family name"),
|
|
|
|
// other fields - useful when trying to compare fonts / bugs from different computers
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE,L"DWrite field 0 copyright"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS,L"DWrite field 5 version"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_TRADEMARK,L"DWrite field 7 trademark"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_MANUFACTURER,L"DWrite field 8 manufacturer"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_DESIGNER,L"DWrite field 9 designer/foundary"),
|
|
|
|
// DWRITE_INFORMATIONAL_STRING_DESCRIPTION gets field 10 from the ttf file.
|
|
// Opennurbs searches the description saved in field 10 of the name table
|
|
// for the strings "Engraving - single stroke" / "Engraving - double stroke" / "Engraving"
|
|
// to identify fonts that are desgned for engraving (and which tend to render poorly when
|
|
// used to dispaly text devices like screens, monitors, and printers).
|
|
// The SLF (single line fonts) are examples of fonts that have Engraving in field 10.
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_DESCRIPTION,L"DWrite field 10 description"),
|
|
|
|
// other fields - useful when trying to compare fonts / bugs from different computers
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL,L"DWrite field 11 vendor URL"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_DESIGNER_URL,L"DWrite field 12 designer URL"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION,L"DWrite field 13 license"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL,L"DWrite field 14 license URL"),
|
|
Internal_DWriteFontInformation(DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME,L"Dwrite field 20 PostScript CID findfont 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));
|
|
|
|
// Opennurbs searches the description saved in field 10 of the name table
|
|
// for the strings "Engraving - single stroke" / "Engraving - double stroke" / "Engraving"
|
|
// to identify fonts that are desgned for engraving (and which tend to render poorly when
|
|
// used to dispaly text devices like screens, monitors, and printers).
|
|
// The SLF (single line fonts) are examples of fonts that have Engraving in field 10.
|
|
const ON_wString field_10_description = ON_Font::Field_10_DescriptionFromWindowsDWriteFont(dwrite_font, preferedLocale);
|
|
text_log.Print(L"ON_Font field 10 description = \"%ls\"\n", static_cast<const wchar_t*>(field_10_description));
|
|
|
|
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.IsNotEmpty() ? loc_PostScriptName : en_PostScriptName;
|
|
}
|
|
return ON_wString::EmptyString;
|
|
}
|
|
|
|
static const ON_wString Internal_InfoStringFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
enum DWRITE_INFORMATIONAL_STRING_ID info_string_id,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == dwrite_font)
|
|
break;
|
|
BOOL exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> info_string = nullptr;
|
|
HRESULT hr = dwrite_font->GetInformationalStrings(info_string_id, &info_string, &exists);
|
|
if (FAILED(hr))
|
|
break;
|
|
ON_wString loc_description;
|
|
ON_wString en_description;
|
|
if (exists)
|
|
Internal_GetLocalizeStrings(preferedLocale, info_string.Get(), loc_description, en_description);
|
|
|
|
return loc_description.IsNotEmpty() ? loc_description : en_description;
|
|
}
|
|
return ON_wString::EmptyString;
|
|
}
|
|
|
|
const ON_wString ON_Font::WeightStretchStyleModelFamilyNameFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_0_CopyrightFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_5_VersionFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_7_TrademarkFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_TRADEMARK, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_8_ManufacturerFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_MANUFACTURER, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_9_DesignerFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_DESIGNER, preferedLocale);
|
|
}
|
|
|
|
// Returns the desription saved in field 10.
|
|
// Opennurbs searches the description saved in field 10 of the name table
|
|
// for the strings "Engraving - single stroke" / "Engraving - double stroke" / "Engraving"
|
|
// to identify fonts that are desgned for engraving (and which tend to render poorly when
|
|
// used to dispaly text devices like screens, monitors, and printers).
|
|
// The SLF (single line fonts) are examples of fonts that have Engraving in field 10.
|
|
const ON_wString ON_Font::Field_10_DescriptionFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font,DWRITE_INFORMATIONAL_STRING_DESCRIPTION, preferedLocale);
|
|
}
|
|
|
|
|
|
const ON_wString ON_Font::Field_11_VendorURLFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_12_DesignerURLFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_DESIGNER_URL, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_13_LicenseFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_14_LicenseURLFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL, preferedLocale);
|
|
}
|
|
|
|
const ON_wString ON_Font::Field_20_PostScriptCIDNameFromWindowsDWriteFont(
|
|
struct IDWriteFont* dwrite_font,
|
|
const wchar_t* preferedLocale
|
|
)
|
|
{
|
|
return Internal_InfoStringFromWindowsDWriteFont(dwrite_font, DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME, preferedLocale);
|
|
}
|
|
|
|
void ON_WindowsDWriteFontInformation::Dump(ON_TextLog& text_log) const
|
|
{
|
|
text_log.Print("IDWriteFont:\n");
|
|
|
|
const bool bTerse = text_log.LevelOfDetailIsAtLeast(ON_TextLog::LevelOfDetail::Medium);
|
|
const bool bChatty = text_log.LevelOfDetailIsAtLeast(ON_TextLog::LevelOfDetail::Medium);
|
|
const bool bVerbose = text_log.LevelOfDetailIsAtLeast(ON_TextLog::LevelOfDetail::Maximum);
|
|
|
|
ON_TextLogIndent indent1(text_log);
|
|
if ( bVerbose )
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
ON_wString family_name = m_loc_family_name;
|
|
ON_wString face_name = m_loc_face_name;
|
|
ON_wString postscript_name = m_loc_postscript_name;
|
|
ON_wString logfont_name = m_loc_gdi_family_name;
|
|
if (false == bChatty)
|
|
{
|
|
if (family_name.IsEmpty())
|
|
family_name = m_en_family_name;
|
|
if (face_name.IsEmpty())
|
|
face_name = m_en_face_name;
|
|
if (postscript_name.IsEmpty())
|
|
postscript_name = m_en_postscript_name;
|
|
if (logfont_name.IsEmpty())
|
|
logfont_name = m_en_gdi_family_name;
|
|
}
|
|
|
|
if (bChatty)
|
|
text_log.Print(L"Localized Names:\n");
|
|
else
|
|
text_log.Print(L"Names:\n");
|
|
|
|
text_log.PushIndent();
|
|
|
|
text_log.Print(L"Family name = \"%ls\"\n", static_cast<const wchar_t*>(family_name));
|
|
text_log.Print(L"Face name = \"%ls\"\n", static_cast<const wchar_t*>(face_name));
|
|
text_log.Print(L"PostScript name = \"%ls\"\n", static_cast<const wchar_t*>(postscript_name));
|
|
text_log.Print(L"GDI LOGFONT name = \"%ls\"\n", static_cast<const wchar_t*>(logfont_name));
|
|
|
|
if ( bChatty )
|
|
{
|
|
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));
|
|
if ( bVerbose )
|
|
{
|
|
text_log.Print(L"Direct Write Weight-Stretch-Style Model Name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_weight_stretch_style_model_name));
|
|
|
|
text_log.Print(L"Field 0 Copyright = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_0_copyright));
|
|
text_log.Print(L"Field 5 Version = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_5_version));
|
|
text_log.Print(L"Field 7 Trademark = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_7_trademark));
|
|
text_log.Print(L"Field 8 Manufacturer = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_8_manufacturer));
|
|
text_log.Print(L"Field 9 Designer = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_9_designer));
|
|
text_log.Print(L"Field 10 Description = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_10_description));
|
|
text_log.Print(L"Field 11 Vendor URL = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_11_vendor_URL));
|
|
text_log.Print(L"Field 12 Designer_URL = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_12_designer_URL));
|
|
text_log.Print(L"Field 13 License = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_13_license));
|
|
text_log.Print(L"Field 14 License URL = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_14_license_URL));
|
|
text_log.Print(L"Field 20 PostScript CID Name = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_20_postscript_cid));
|
|
}
|
|
}
|
|
|
|
if ( false == bVerbose )
|
|
{
|
|
if (ON_OutlineFigure::Type::Unset != ON_OutlineFigure::FigureTypeFromField10Description(m_loc_field_10_description))
|
|
text_log.Print(L"Field 10 Description = \"%ls\"\n", static_cast<const wchar_t*>(m_loc_field_10_description));
|
|
else if (ON_OutlineFigure::Type::Unset != ON_OutlineFigure::FigureTypeFromField10Description(m_en_field_10_description))
|
|
text_log.Print(L"Field 10 Description = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_10_description));
|
|
}
|
|
|
|
text_log.PopIndent();
|
|
|
|
if ( bChatty )
|
|
{
|
|
text_log.Print(L"English Names:\n");
|
|
ON_TextLogIndent indent2(text_log);
|
|
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));
|
|
|
|
if ( bVerbose )
|
|
{
|
|
text_log.Print(L"Direct Write Weight-Stretch-Style Model Name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_weight_stretch_style_model_name));
|
|
|
|
text_log.Print(L"Field 0 Copyright = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_0_copyright));
|
|
text_log.Print(L"Field 5 Version = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_5_version));
|
|
text_log.Print(L"Field 7 Trademark = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_7_trademark));
|
|
text_log.Print(L"Field 8 Manufacturer = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_8_manufacturer));
|
|
text_log.Print(L"Field 9 Designer = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_9_designer));
|
|
text_log.Print(L"Field 10 Description = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_10_description));
|
|
text_log.Print(L"Field 11 Vendor URL = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_11_vendor_URL));
|
|
text_log.Print(L"Field 12 Designer_URL = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_12_designer_URL));
|
|
text_log.Print(L"Field 13 License = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_13_license));
|
|
text_log.Print(L"Field 14 License URL = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_14_license_URL));
|
|
text_log.Print(L"Field 20 PostScript CID Name = \"%ls\"\n", static_cast<const wchar_t*>(m_en_field_20_postscript_cid));
|
|
}
|
|
}
|
|
|
|
if (bChatty)
|
|
{
|
|
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));
|
|
}
|
|
|
|
if ( bVerbose )
|
|
{
|
|
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);
|
|
}
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME, ... )
|
|
fi.m_en_weight_stretch_style_model_name = ON_Font::WeightStretchStyleModelFamilyNameFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_weight_stretch_style_model_name = ON_Font::WeightStretchStyleModelFamilyNameFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE, ... )
|
|
fi.m_loc_field_0_copyright = ON_Font::Field_0_CopyrightFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_0_copyright = ON_Font::Field_0_CopyrightFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS, ... )
|
|
fi.m_loc_field_5_version = ON_Font::Field_5_VersionFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_5_version = ON_Font::Field_5_VersionFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_TRADEMARK, ... )
|
|
fi.m_loc_field_7_trademark = ON_Font::Field_7_TrademarkFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_7_trademark = ON_Font::Field_7_TrademarkFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_MANUFACTURER, ... )
|
|
fi.m_loc_field_8_manufacturer = ON_Font::Field_8_ManufacturerFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_8_manufacturer = ON_Font::Field_8_ManufacturerFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_DESIGNER, ... )
|
|
fi.m_loc_field_9_designer = ON_Font::Field_9_DesignerFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_9_designer = ON_Font::Field_9_DesignerFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// Field 10 Description
|
|
// Opennurbs searches the description saved in field 10 of the name table
|
|
// for the strings "Engraving - single stroke" / "Engraving - double stroke" / "Engraving"
|
|
// to identify fonts that are desgned for engraving (and which tend to render poorly when
|
|
// used to dispaly text devices like screens, monitors, and printers).
|
|
// The SLF (single line fonts) are examples of fonts that have Engraving in field 10.
|
|
fi.m_loc_field_10_description = ON_Font::Field_10_DescriptionFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_10_description = ON_Font::Field_10_DescriptionFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
fi.m_loc_field_11_vendor_URL = ON_Font::Field_11_VendorURLFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_11_vendor_URL = ON_Font::Field_11_VendorURLFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_DESIGNER_URL, ... )
|
|
fi.m_loc_field_12_designer_URL = ON_Font::Field_12_DesignerURLFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_12_designer_URL = ON_Font::Field_12_DesignerURLFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION, ... )
|
|
fi.m_loc_field_13_license = ON_Font::Field_13_LicenseFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_13_license = ON_Font::Field_13_LicenseFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL, ... )
|
|
fi.m_loc_field_14_license_URL = ON_Font::Field_14_LicenseURLFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_14_license_URL = ON_Font::Field_14_LicenseURLFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
// from IDWriteFont.GetInformationalStrings( DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME, ... )
|
|
fi.m_loc_field_20_postscript_cid = ON_Font::Field_20_PostScriptCIDNameFromWindowsDWriteFont(dwriteFont.Get(), preferedLocale);
|
|
fi.m_en_field_20_postscript_cid = ON_Font::Field_20_PostScriptCIDNameFromWindowsDWriteFont(dwriteFont.Get(), englishLocale);
|
|
|
|
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.
|
|
// ...
|
|
// Dale Lear 7 Jan 2019.
|
|
// It appears Turbo Tax 2018 installs 4 faces of Avenir LT with
|
|
// empty family name and empty logfont name. It has non-empty PostScript names.
|
|
//
|
|
// C:\PROGRAM FILES (X86)\TURBOTAX\DELUXE 2018\32BIT\LOCAL\FUEGO\HOST\AVENIR\
|
|
// Avenir LT 35 Light: AVENIRLT-LIGHT.TTF
|
|
// Avenir LT 55 Roman: AVENIRLT-ROMAN.TTF
|
|
// Avenir LT 65 Medium: AVENIRLT-MEDIUM.TTF
|
|
// Avenir LT 85 Heavy: AVENIRLT-HEAVY.TTF
|
|
//
|
|
// These fonts never appear in Windows Notepad, WordPad, or Word font UI.
|
|
// The Windows Setting font list leaves the name of this font blank as well.
|
|
// So I'm going to comment out the call to ON_ERROR.
|
|
//
|
|
////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;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ON_OutlineFigure::Type::Unset == fi.m_outline_figure_type)
|
|
{
|
|
fi.m_outline_figure_type = ON_OutlineFigure::FigureTypeFromField10Description(fi.m_loc_field_10_description);
|
|
if (ON_OutlineFigure::Type::Unset == fi.m_outline_figure_type)
|
|
fi.m_outline_figure_type = ON_OutlineFigure::FigureTypeFromField10Description(fi.m_loc_field_10_description);
|
|
|
|
if (ON_OutlineFigure::Type::Unset == fi.m_outline_figure_type)
|
|
fi.m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(fi.m_loc_family_name);
|
|
if (ON_OutlineFigure::Type::Unset == fi.m_outline_figure_type
|
|
&& false == fi.m_en_family_name.EqualOrdinal(fi.m_loc_family_name, true)
|
|
)
|
|
fi.m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(fi.m_en_family_name);
|
|
|
|
if (ON_OutlineFigure::Type::Unset == fi.m_outline_figure_type)
|
|
fi.m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(fi.m_loc_postscript_name);
|
|
if (ON_OutlineFigure::Type::Unset == fi.m_outline_figure_type
|
|
&& false == fi.m_en_postscript_name.EqualOrdinal(fi.m_loc_postscript_name, true)
|
|
)
|
|
fi.m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(fi.m_en_postscript_name);
|
|
|
|
if (ON_OutlineFigure::Type::Unset == fi.m_outline_figure_type)
|
|
fi.m_outline_figure_type = ON_OutlineFigure::Type::Unknown;
|
|
}
|
|
|
|
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_index = 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.
|
|
ON_OutlineFigure::Type figure_type = ON_OutlineFigure::FigureTypeFromField10Description(a[i].m_loc_field_10_description);
|
|
if (ON_OutlineFigure::Type::Unset == figure_type )
|
|
figure_type = ON_OutlineFigure::FigureTypeFromField10Description(a[i].m_en_field_10_description);
|
|
if (ON_OutlineFigure::Type::Unset == figure_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_Outline::DefaultOuterOrientation);
|
|
|
|
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;
|
|
}
|
|
|
|
unsigned int 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;
|
|
|
|
unsigned int glyph_index = 0;
|
|
for(;;)
|
|
{
|
|
glyph_index
|
|
= glyph->FontGlyphIndexIsSet()
|
|
? glyph->FontGlyphIndex()
|
|
: 0;
|
|
if (glyph_index > 0)
|
|
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;
|
|
|
|
glyph_index = glyphIndices[0];
|
|
break;
|
|
}
|
|
|
|
const bool rc
|
|
= (0 != glyph_index)
|
|
? ON_WindowsDWriteGetGlyphMetrics(dwriteFont, glyph_index, glyph_metrics)
|
|
: false;
|
|
|
|
dwriteFont->Release();
|
|
|
|
return rc ? glyph_index : 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;
|
|
|
|
const unsigned int dwriteGlyphIndex = glyph->FontGlyphIndex();
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Opennurbs searches the description saved in field 10 of the name table
|
|
// for the strings "Engraving - single stroke" / "Engraving - double stroke" / "Engraving"
|
|
// to identify fonts that are desgned for engraving (and which tend to render poorly when
|
|
// used to dispaly text devices like screens, monitors, and printers).
|
|
// The SLF (single line fonts) are examples of fonts that have Engraving in field 10.
|
|
ON_wString loc_field_10_description = ON_Font::Field_10_DescriptionFromWindowsDWriteFont(dwrite_font, preferedLocale);
|
|
ON_wString en_field_10_description = ON_Font::Field_10_DescriptionFromWindowsDWriteFont(dwrite_font, L"en-us");
|
|
if (ON_OutlineFigure::Type::Unset == m_outline_figure_type)
|
|
m_outline_figure_type = ON_OutlineFigure::FigureTypeFromField10Description(loc_field_10_description);
|
|
if (ON_OutlineFigure::Type::Unset == m_outline_figure_type)
|
|
m_outline_figure_type = ON_OutlineFigure::FigureTypeFromField10Description(en_field_10_description);
|
|
|
|
/*
|
|
Look for known engraving fonts by name
|
|
*/
|
|
if (ON_OutlineFigure::Type::Unset == m_outline_figure_type)
|
|
m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(m_loc_postscript_name);
|
|
if (ON_OutlineFigure::Type::Unset == m_outline_figure_type
|
|
&& false == m_loc_postscript_name.EqualOrdinal(m_en_postscript_name, true)
|
|
)
|
|
m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(m_en_postscript_name);
|
|
|
|
if (ON_OutlineFigure::Type::Unset == m_outline_figure_type)
|
|
m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(m_loc_family_name);
|
|
if (ON_OutlineFigure::Type::Unset == m_outline_figure_type
|
|
&& false == m_loc_family_name.EqualOrdinal(m_en_family_name, true)
|
|
)
|
|
m_outline_figure_type = ON_OutlineFigure::FigureTypeFromFontName(m_en_family_name);
|
|
|
|
// If it's still unset, we won't do any better in the future,
|
|
// so set m_outline_figure_type to unknow.
|
|
if (ON_OutlineFigure::Type::Unset == m_outline_figure_type)
|
|
m_outline_figure_type = ON_OutlineFigure::Type::Unknown;
|
|
|
|
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;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
//
|
|
|
|
static bool Internal_GetDWriteFamilyName(
|
|
const IDWriteFont* dwrite_font,
|
|
const ON_wString preferedLocaleDirty,
|
|
ON_wString& familyName,
|
|
ON_wString& locale
|
|
)
|
|
{
|
|
familyName = ON_wString::EmptyString;
|
|
locale = ON_wString::EmptyString;
|
|
if (nullptr == dwrite_font)
|
|
return false;
|
|
|
|
// The name id are list in decreasing order of reliability.
|
|
DWRITE_INFORMATIONAL_STRING_ID name_id[] =
|
|
{
|
|
/// <summary>
|
|
/// Family name for the weight-stretch-style model.
|
|
/// </summary>
|
|
DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME,
|
|
|
|
// Typographic family name preferred by the designer. This enables font designers to group more than four fonts in a single family without losing compatibility with
|
|
// GDI. This name is typically only present if it differs from the GDI-compatible family name.
|
|
DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES,
|
|
|
|
// GDI-compatible family name. Because GDI allows a maximum of four fonts per family, fonts in the same family may have different GDI-compatible family names
|
|
// (e.g., "Arial", "Arial Narrow", "Arial Black").
|
|
DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES,
|
|
};
|
|
|
|
const size_t name_id_count = sizeof(name_id) / sizeof(name_id[0]);
|
|
|
|
ON_wString preferedLocale(preferedLocaleDirty);
|
|
preferedLocale.TrimLeftAndRight();
|
|
if (preferedLocale.IsEmpty())
|
|
preferedLocale = ON_wString(L"GetUserDefaultLocaleName");
|
|
|
|
for (size_t i = 0; i < name_id_count; ++i)
|
|
{
|
|
// iterate possible family names finding the best one to return.
|
|
BOOL exists = false;
|
|
Microsoft::WRL::ComPtr<IDWriteLocalizedStrings> localizedFamilynames = nullptr;
|
|
HRESULT hr = const_cast<IDWriteFont*>(dwrite_font)->GetInformationalStrings(name_id[i], &localizedFamilynames, &exists);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
IDWriteLocalizedStrings* familyNames = localizedFamilynames.Get();
|
|
if (nullptr == familyNames)
|
|
continue;
|
|
|
|
ON_wString defaultName;
|
|
ON_wString defaultLocale;
|
|
ON_wString enName;
|
|
ON_wString enLocale;
|
|
if (exists)
|
|
{
|
|
if (Internal_GetLocalizeStrings(
|
|
preferedLocale,
|
|
familyNames,
|
|
defaultName,
|
|
defaultLocale,
|
|
enName,
|
|
enLocale
|
|
))
|
|
{
|
|
if (defaultName.IsNotEmpty() && defaultLocale.IsNotEmpty())
|
|
{
|
|
familyName = defaultName;
|
|
locale = defaultLocale;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return familyName.IsNotEmpty();
|
|
}
|
|
|
|
static IDWriteTextFormat* Internal_TextFormat(const IDWriteFont* dwriteFont)
|
|
{
|
|
if (nullptr == dwriteFont)
|
|
return nullptr;
|
|
|
|
ON_wString familyName;
|
|
ON_wString locale;
|
|
const bool bHaveFamilyName = Internal_GetDWriteFamilyName(dwriteFont,ON_wString::EmptyString,familyName,locale);
|
|
if (false == bHaveFamilyName || familyName.IsEmpty() || locale.IsEmpty())
|
|
return nullptr;
|
|
|
|
IDWriteFactory* dwiteFactory = ON_IDWrite::Factory();
|
|
if (nullptr == dwiteFactory)
|
|
return nullptr;
|
|
|
|
DWRITE_FONT_WEIGHT weight = const_cast<IDWriteFont*>(dwriteFont)->GetWeight();
|
|
DWRITE_FONT_STYLE style = const_cast<IDWriteFont*>(dwriteFont)->GetStyle();
|
|
DWRITE_FONT_STRETCH stretch = const_cast<IDWriteFont*>(dwriteFont)->GetStretch();
|
|
|
|
float logical_size_dip = 96.0; // 96 DIP ("device-independent pixel") = one inch
|
|
|
|
IDWriteTextFormat* dwriteTextFormat = nullptr;
|
|
|
|
HRESULT hr = dwiteFactory->CreateTextFormat(
|
|
static_cast<const wchar_t*>(familyName),
|
|
nullptr, // use System font collection
|
|
weight, style, stretch,
|
|
logical_size_dip,
|
|
static_cast<const wchar_t*>(locale),
|
|
&dwriteTextFormat
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = dwriteTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
|
|
|
|
// Center align (vertically) the text.
|
|
if (SUCCEEDED(hr))
|
|
hr = dwriteTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
|
|
|
|
if (SUCCEEDED(hr))
|
|
return dwriteTextFormat;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
//static
|
|
IDWriteTextLayout* Internal_TextLayout(
|
|
ON_wString textString,
|
|
const ON_Font* font,
|
|
double height
|
|
)
|
|
{
|
|
if (nullptr == font)
|
|
return nullptr;
|
|
|
|
if (textString.IsEmpty())
|
|
return nullptr;
|
|
|
|
const UINT32 textStringLength = (UINT32)textString.Length();
|
|
|
|
// sanity check textStringLength
|
|
if (textStringLength <= 0 || textStringLength > 1024*1024)
|
|
return nullptr;
|
|
|
|
IDWriteFactory* dwiteFactory = ON_IDWrite::Factory();
|
|
if (nullptr == dwiteFactory)
|
|
return nullptr;
|
|
|
|
HRESULT hr = -1;
|
|
IDWriteTextFormat* dwriteTextFormat = nullptr;
|
|
IDWriteTextLayout* dwriteTextLayout = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
// dwriteFont is managed by opennurbs
|
|
const IDWriteFont* dwriteFont = font->WindowsDWriteFont();
|
|
if (nullptr == dwriteFont)
|
|
break;
|
|
|
|
dwriteTextFormat = Internal_TextFormat(dwriteFont);
|
|
if (nullptr == dwriteTextFormat)
|
|
break;
|
|
|
|
FLOAT maxWidthInPixels = 16384.0f; // The width of the layout box.
|
|
FLOAT maxHeightInPixels = 128.0f; // The height of the layout box.
|
|
|
|
hr = dwiteFactory->CreateTextLayout(
|
|
static_cast<const wchar_t*>(textString),
|
|
textStringLength,
|
|
dwriteTextFormat,
|
|
maxWidthInPixels,
|
|
maxHeightInPixels,
|
|
&dwriteTextLayout
|
|
);
|
|
if (nullptr == dwriteTextLayout)
|
|
break;
|
|
|
|
FLOAT fontSizeInDIPs = 96.0; // The font size in DIP units to be set for text in the range specified by textRange.
|
|
|
|
const DWRITE_TEXT_RANGE textRange = { 0, textStringLength };
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = dwriteTextLayout->SetFontSize(fontSizeInDIPs, textRange);
|
|
|
|
if (SUCCEEDED(hr) && font->IsUnderlined())
|
|
hr = dwriteTextLayout->SetUnderline(TRUE, textRange);
|
|
|
|
if (SUCCEEDED(hr) && font->IsStrikethrough())
|
|
hr = dwriteTextLayout->SetStrikethrough(TRUE, textRange);
|
|
|
|
break;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
return dwriteTextLayout;
|
|
|
|
// delete stuff here
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#endif
|