//
// 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.
          // ...
          // 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;
        }

        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.
        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_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);
      }
    }
    
    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
