// // Copyright (c) 1993-2015 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 . // //////////////////////////////////////////////////////////////// #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 #include "opennurbs_internal_glyph.h" #include "opennurbs_win_dwrite.h" #include "opennurbs_apple_nsfont.h" ON_PANOSE1::FamilyKind ON_PANOSE1::FamilyKindFromUnsigned( unsigned int unsigned_panose_family_kind ) { switch (unsigned_panose_family_kind) { ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::Any); ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::NoFit); ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinText); ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinScript); ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinDecorative); ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinSymbol); default: break; } ON_ERROR("unsigned_pannos1_classification is not valid"); return ON_PANOSE1::FamilyKind::Any; } const wchar_t* ON_PANOSE1::FamilyKindToWideString( ON_PANOSE1::FamilyKind family_kind ) { const wchar_t* s; switch (family_kind) { case ON_PANOSE1::FamilyKind::Any: s = L"Any"; break; case ON_PANOSE1::FamilyKind::NoFit: s = L"No fit"; break; case ON_PANOSE1::FamilyKind::LatinText: s = L"Latin Text"; break; case ON_PANOSE1::FamilyKind::LatinScript: s = L"Latin Script"; break; case ON_PANOSE1::FamilyKind::LatinDecorative: s = L"Latin Decorative"; break; case ON_PANOSE1::FamilyKind::LatinSymbol: s = L"Latin Symbol"; break; default: s = L""; break; } return s; } bool ON_PANOSE1::IsZero() const { const ON__UINT8* p = TenBytes(); for (int i = 0; i < 10; i++) { if (0 != p[i]) return false; } return true; } bool ON_PANOSE1::IsZeroOrOne() const { const ON__UINT8* p = TenBytes(); for (int i = 0; i < 10; i++) { if (p[i] > 1) return false; } return true; } bool ON_PANOSE1::IsSet() const { return IsZeroOrOne() ? false : true; } ON_PANOSE1::FamilyKind ON_PANOSE1::PANOSE1FamilyKind() const { return m_family_kind; } const ON__UINT8* ON_PANOSE1::TenBytes() const { return (const ON__UINT8*)(&m_family_kind); } void ON_PANOSE1::SetTenBytes(const ON__UINT8* panose1_ten_bytes) { SetNineBytes(ON_PANOSE1::FamilyKindFromUnsigned(panose1_ten_bytes[0]), panose1_ten_bytes + 1); } void ON_PANOSE1::SetNineBytes( ON_PANOSE1::FamilyKind family_kind, const ON__UINT8* panose1_properties_bytes ) { const ON__UINT8 b0 = static_cast(family_kind); if ( b0 > 5) { *this = ON_PANOSE1::Zero; } else { m_family_kind = family_kind; m_prop1 = panose1_properties_bytes[0]; m_prop2 = panose1_properties_bytes[1]; m_prop3 = panose1_properties_bytes[2]; m_prop4 = panose1_properties_bytes[3]; m_prop5 = panose1_properties_bytes[4]; m_prop6 = panose1_properties_bytes[5]; m_prop7 = panose1_properties_bytes[6]; m_prop8 = panose1_properties_bytes[7]; m_prop9 = panose1_properties_bytes[8]; } } void ON_PANOSE1::Dump( class ON_TextLog& text_log ) const { if (IsZero()) { text_log.Print(L"PANOSE1::Zero\n"); } else { ON_wString family = FamilyKindToWideString(m_family_kind); if (family.IsEmpty()) family = ON_wString::FormatToString(L"%u", (unsigned int)m_family_kind); text_log.Print(L"PANOSE1: %ls (%u, %u, %u, %u, %u, %u, %u, %u, %u)\n", static_cast(family), (unsigned int)m_prop1, (unsigned int)m_prop2, (unsigned int)m_prop3, (unsigned int)m_prop4, (unsigned int)m_prop5, (unsigned int)m_prop6, (unsigned int)m_prop7, (unsigned int)m_prop8, (unsigned int)m_prop9 ); } } bool ON_PANOSE1::Write( class ON_BinaryArchive& archive ) const { if (false == archive.BeginWrite3dmAnonymousChunk(1)) return false; bool rc = false; for(;;) { if (!archive.WriteByte(1, &m_family_kind)) break; if (!archive.WriteByte(1, &m_prop1)) break; if (!archive.WriteByte(1, &m_prop2)) break; if (!archive.WriteByte(1, &m_prop3)) break; if (!archive.WriteByte(1, &m_prop4)) break; if (!archive.WriteByte(1, &m_prop5)) break; if (!archive.WriteByte(1, &m_prop6)) break; if (!archive.WriteByte(1, &m_prop7)) break; if (!archive.WriteByte(1, &m_prop8)) break; if (!archive.WriteByte(1, &m_prop9)) break; rc = true; break; } if (!archive.EndWrite3dmChunk()) rc = false; return rc; } bool ON_PANOSE1::Read( class ON_BinaryArchive& archive ) { int version = 0; if (false == archive.BeginRead3dmAnonymousChunk(&version)) return false; bool rc = false; for (;;) { if (version < 1) break; if (!archive.ReadByte(1, &m_family_kind)) break; if (!archive.ReadByte(1, &m_prop1)) break; if (!archive.ReadByte(1, &m_prop2)) break; if (!archive.ReadByte(1, &m_prop3)) break; if (!archive.ReadByte(1, &m_prop4)) break; if (!archive.ReadByte(1, &m_prop5)) break; if (!archive.ReadByte(1, &m_prop6)) break; if (!archive.ReadByte(1, &m_prop7)) break; if (!archive.ReadByte(1, &m_prop8)) break; if (!archive.ReadByte(1, &m_prop9)) break; if (version <= 1) { rc = true; break; } rc = true; break; } if (!archive.EndRead3dmChunk()) rc = false; return rc; } int ON_FontMetrics::Ascent() const { return m_ascent; } int ON_FontMetrics::Descent() const { return m_descent; } int ON_FontMetrics::LineSpace() const { return m_line_space; } int ON_FontMetrics::UPM() const { return m_UPM; } int ON_FontMetrics::AscentOfI() const { return AscentOfCapital(); } int ON_FontMetrics::AscentOfCapital() const { return (int)m_ascent_of_capital; } int ON_FontMetrics::AscentOfx() const { return (int)m_ascent_of_x; } double ON_FontMetrics::GlyphScale(double text_height) const { // Please consult Dale Lear and the ON_FontGlyph // bounding box, outline, and advance calculation code // before making any modifications to this function. const double y = (double)AscentOfCapital(); return (text_height > 0.0 && text_height < ON_UNSET_POSITIVE_FLOAT && y > 0.0 && y < ON_UNSET_POSITIVE_FLOAT ) ? (text_height / y) : 1.0; } int ON_FontMetrics::StrikeoutThickness() const { return m_strikeout_thickness; } int ON_FontMetrics::StrikeoutPosition() const { return m_strikeout_position; } int ON_FontMetrics::UnderscoreThickness() const { return m_underscore_thickness; } int ON_FontMetrics::UnderscorePosition() const { return m_underscore_position; } bool ON_FontMetrics::AscentDescentAndUPMAreValid() const { const int max_valid = 0xFFFF; const int min_valid = -max_valid; // basic validation if (m_UPM <= 0 || m_UPM >= max_valid) return false; if (0 == m_ascent && 0 == m_descent) return false; if (m_ascent <= min_valid || m_ascent >= max_valid) return false; if (m_descent <= min_valid || m_descent >= max_valid) return false; if (m_ascent <= m_descent) return false; return true; } bool ON_FontMetrics::HeightsAreValid() const { // basic validation if (false == AscentDescentAndUPMAreValid() ) return false; // sanity check if (m_line_space < m_ascent - m_descent) return false; if (((int)m_ascent_of_capital) > m_ascent) return false; if (((int)m_ascent_of_x) > m_ascent) return false; return true; } bool ON_FontMetrics::IsSetAndValid() const { return (m_ascent_of_capital > 0 && HeightsAreValid()); } void ON_FontMetrics::SetHeights( int ascent, int descent, int UPM, int line_space ) { if (ON_UNSET_INT_INDEX < descent && descent < ascent && ascent < -ON_UNSET_INT_INDEX) { m_ascent = ascent; m_descent = descent; } else { m_ascent = 0; m_descent = 0; } m_UPM = (UPM > 0 && UPM < -ON_UNSET_INT_INDEX) ? UPM : 0; m_line_space = (line_space > 0 && line_space < -ON_UNSET_INT_INDEX)? line_space : 0; if ( m_line_space > 0 && m_ascent > 0 && m_descent <= m_ascent && m_line_space < m_ascent - m_descent && m_line_space >= m_ascent - m_descent - 1 ) { // Probably sloppy rounding - just make it work m_line_space = m_ascent - m_descent; } } void ON_FontMetrics::SetAscentOfI( int ascent_of_capital ) { SetAscentOfCapital(ascent_of_capital); } void ON_FontMetrics::SetAscentOfCapital( int ascent_of_capital ) { m_ascent_of_capital = (ascent_of_capital > 0 && ascent_of_capital <= 0xFFFF) ? ((unsigned short)ascent_of_capital) : 0; } void ON_FontMetrics::SetAscentOfx( int ascent_of_x ) { m_ascent_of_x = (ascent_of_x > 0 && ascent_of_x <= 0xFFFF) ? ((unsigned short)ascent_of_x) : 0; } void ON_FontMetrics::SetStrikeout( int strikeout_position, int strikeout_thickness ) { m_strikeout_position = strikeout_position; m_strikeout_thickness = strikeout_thickness; } void ON_FontMetrics::SetUnderscore( int underscore_position, int underscore_thickness ) { m_underscore_position = underscore_position; m_underscore_thickness = underscore_thickness; } static int Internal_FontMetricCeil( double x ) { const double m = (double)0xFFFFFF; if (x >= -m && x <= m) { double i = ceil(x); return (int)(((i - x) > 0.9375) ? (i - 1.0) : i); } return 0; } static int Internal_FontMetricFloor( double x ) { const double m = (double)0xFFFFFF; if (x >= -m && x <= m) { double i = floor(x); return (int)(((x - i) > 0.9375) ? (i + 1.0) : i); } return 0; } static int Internal_FontMetricNearest( double x ) { const double m = (double)0xFFFFFF; if (x >= -m && x <= m) { double i = floor(x); return (int)(((x - i) > 0.5) ? (i + 1.0) : i); } return 0; } void ON_FontMetrics::SetHeights( double ascent, double descent, double UPM, double line_space ) { int iascent = Internal_FontMetricCeil(ascent); int idescent = Internal_FontMetricFloor(descent); int iUPM = Internal_FontMetricCeil(UPM); int iline_space = Internal_FontMetricCeil(line_space); if ( iascent > 0 && idescent <= iascent && iline_space < iascent - idescent && line_space >= (ascent - descent - 1.0) ) { iline_space = iascent - idescent; } SetHeights(iascent, idescent, iUPM, iline_space); } void ON_FontMetrics::SetAscentOfCapital( double ascent_of_capital ) { int iascent_of_capital = Internal_FontMetricCeil(ascent_of_capital); if (m_ascent < 0 && iascent_of_capital > m_ascent && iascent_of_capital <= m_ascent - 1) iascent_of_capital = m_ascent; SetAscentOfCapital(iascent_of_capital); } void ON_FontMetrics::SetAscentOfx( double ascent_of_x ) { int iascent_of_x = Internal_FontMetricCeil(ascent_of_x); if (m_ascent < 0 && iascent_of_x > m_ascent && iascent_of_x <= m_ascent - 1) iascent_of_x = m_ascent; SetAscentOfx(iascent_of_x); } void ON_FontMetrics::SetStrikeout( double strikeout_position, double strikeout_thickness ) { int istrikeout_thickness = (strikeout_thickness > 0.0) ? Internal_FontMetricCeil(strikeout_thickness) : 0; if (0 == istrikeout_thickness && strikeout_thickness > 0.0) istrikeout_thickness = 1; SetStrikeout(Internal_FontMetricNearest(strikeout_position), istrikeout_thickness); } void ON_FontMetrics::SetUnderscore( double underscore_position, double underscore_thickness ) { int iunderscore_thickness = (underscore_thickness > 0.0) ? Internal_FontMetricCeil(underscore_thickness) : 0; if (0 == iunderscore_thickness && underscore_thickness > 0.0) iunderscore_thickness = 1; SetUnderscore(Internal_FontMetricFloor(underscore_position), iunderscore_thickness); } static int Internal_ScaleInt(double scale, int i) { return (int)((i >= 0) ? ceil(scale*i) : floor(scale*i)); } const ON_FontMetrics ON_FontMetrics::Scale( const ON_FontMetrics& font_metrics, double scale ) { ON_FontMetrics scaled_font_metrics = font_metrics; if (scale > 0.0 && 1.0 != scale) { scaled_font_metrics.m_UPM = Internal_ScaleInt(scale, scaled_font_metrics.m_UPM); scaled_font_metrics.m_ascent = Internal_ScaleInt(scale, scaled_font_metrics.m_ascent); scaled_font_metrics.m_descent = Internal_ScaleInt(scale, scaled_font_metrics.m_descent); scaled_font_metrics.SetAscentOfCapital(Internal_ScaleInt(scale, scaled_font_metrics.m_ascent_of_capital)); scaled_font_metrics.SetAscentOfx(Internal_ScaleInt(scale, scaled_font_metrics.m_ascent_of_x)); if (font_metrics.m_line_space == Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,font_metrics.AscentOfCapital())) scaled_font_metrics.m_line_space = Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,scaled_font_metrics.AscentOfCapital()); else scaled_font_metrics.m_line_space = Internal_ScaleInt(scale, scaled_font_metrics.m_line_space); scaled_font_metrics.m_strikeout_thickness = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_thickness); scaled_font_metrics.m_strikeout_position = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_position); scaled_font_metrics.m_underscore_thickness = Internal_ScaleInt(scale, scaled_font_metrics.m_underscore_thickness); scaled_font_metrics.m_underscore_position = Internal_ScaleInt(scale, scaled_font_metrics.m_underscore_position); } return scaled_font_metrics; } const ON_FontMetrics ON_FontMetrics::Normalize( const ON_FontMetrics& font_metrics ) { if (font_metrics.m_UPM == ON_Font::AnnotationFontCellHeight) return font_metrics; if (font_metrics.m_UPM <= 0) return ON_FontMetrics::Unset; const double scale = ((double)ON_Font::AnnotationFontCellHeight / ((double)font_metrics.m_UPM)); return ON_FontMetrics::Scale(font_metrics, scale); } ON_ManagedFonts::ON_ManagedFonts() : m_managed_fonts(true) , m_installed_fonts(false) {} ON_ManagedFonts::~ON_ManagedFonts() { ON_SimpleArray managed_fonts(m_installed_fonts.m_by_index); managed_fonts.Append(m_managed_fonts.m_by_index.Count(),m_managed_fonts.m_by_index.Array()); m_installed_fonts.Internal_EmptyLists(); m_managed_fonts.Internal_EmptyLists(); // last created first deleted - it shouldn't really matter. for(int i = managed_fonts.Count()-1; i >= 0; i--) { ON_Font* managed_font = const_cast(managed_fonts[i]); if (nullptr == managed_font) continue; if (m_default_font_ptr == (ON__UINT_PTR)managed_font) { // ON_Font::Default is constructed after ON_ManagedFonts::List // and destructed before the destruction of ON_ManagedFonts::List. // This continue prevents calling delete on continue; } if (1 == managed_font->m_runtime_serial_number) { // ON_Font::Default.m_runtime_serial_number = 1 and it is the only instance of a font // with m_runtime_serial_number = 1. // However, the managed_font pointer points to ON_Font::Default, which was destroyed a few miliseconds ago. // See opennurbs_statics.cpp and observe that construction order is // ..., ON_ManagedFonts::List, ON_Font::Unset, ON_Font::Default, ... // and destruction order is // ..., ON_Font::Default, ON_Font::Unset, ON_ManagedFonts::List, ... ON_ERROR("The m_default_font_ptr test above should have detected ON_Font::Default."); continue; } // The reset needs to happen for all fonts, including ON_Font::Default // Otherwise we get unpredictable crashes when closing because the // order in which ON_Font::Default, ON_GlyphMap::GlyphPool and ON_ManagedFonts::List // is not predictable. managed_font->m_font_glyph_cache.reset(); delete managed_font; } } class ON_FontGlyphCache { public: static ON_FontGlyphCache* New(); void SetFontMetrics( const ON_FontMetrics& font_unit_metrics ); #if defined(ON_OS_WINDOWS_GDI) // primary installed font used to get glyphs // When the current (Windows) device has a font that // matches the ON_Font, this is matching installed font. struct IDWriteFont* m_dwrite_font = 0; #endif // See ON_Font.FontMetrics() documentation for a discussion // of normalized and unnormalized metrics and scales. // Both scale values are stored to reduce rounding errors. double m_font_unit_to_normalized_scale = 1.0; double m_normalized_to_font_unit_scale = 1.0; // Font metrics in the units from the system font defintion. // UPM = font design cell height (often 1000 for PostScript, 2014 for TrueType, ...) ON_FontMetrics m_font_unit_metrics; // Font metrics normalized so UPM = ON_Font::Constants::AnnotationFontCellHeight ON_FontMetrics m_normalized_metrics; // Array of glyphs with sizes and display info std::shared_ptr m_glyphmap; }; ON_FontGlyphCache* ON_FontGlyphCache::New() { ON_FontGlyphCache* font_cache = new ON_FontGlyphCache(); font_cache->m_glyphmap = std::make_shared(); return font_cache; } void ON_FontGlyphCache::SetFontMetrics( const ON_FontMetrics& font_unit_metrics ) { m_font_unit_metrics = font_unit_metrics; m_normalized_to_font_unit_scale = font_unit_metrics.UPM() > 0 ? (((double)font_unit_metrics.UPM()) / ((double)ON_Font::Constants::AnnotationFontCellHeight)) : 0.0; m_font_unit_to_normalized_scale = m_normalized_to_font_unit_scale > 0.0 ? (((double)ON_Font::Constants::AnnotationFontCellHeight) / ((double)font_unit_metrics.UPM())) : 0.0; m_normalized_metrics = (m_font_unit_to_normalized_scale > 0.0 && 1.0 != m_font_unit_to_normalized_scale) ? ON_FontMetrics::Scale(m_font_unit_metrics, m_font_unit_to_normalized_scale) : m_font_unit_metrics; } static const ON_Font* Internal_BinarySearchForManagedFontSerialNumber( unsigned int key, const ON_Font*const* base, size_t nel ) { if (nel > 0 && nullptr != base ) { size_t i; const ON_Font* font; unsigned int d; // The end tests are not necessary, but they // seem to provide overall speed improvement // for the types of searches that call this // function. font = base[0]; d = font->RuntimeSerialNumber(); if ( key < d ) return nullptr; if ( key == d ) return font; font = base[nel-1]; d = font->RuntimeSerialNumber(); if ( key > d ) return nullptr; if ( key == d ) return font; while ( nel > 0 ) { i = nel/2; font = base[i]; d = font->RuntimeSerialNumber(); if ( key < d ) { nel = i; } else if ( key > d ) { i++; base += i; nel -= i; } else { return font; } } } return nullptr; } const ON_Font* ON_ManagedFonts::GetFromSerialNumber( unsigned int managed_font_serial_number ) { if (0 == m_managed_fonts.Count()) { // Put ON_Font::Default as the first entry in this list. Internal_AddManagedFont(&ON_Font::Default, nullptr); } if (managed_font_serial_number < 1) { ON_ERROR("managed_font_serial_number parameter must be >= 1"); return nullptr; } if ( managed_font_serial_number == ON_Font::Default.RuntimeSerialNumber() ) return &ON_Font::Default; const ON_Font* const * managed_fonts = m_managed_fonts.m_by_index.Array(); const unsigned int font_count = m_managed_fonts.m_by_index.UnsignedCount(); if (managed_font_serial_number <= font_count && managed_font_serial_number == managed_fonts[managed_font_serial_number - 1]->RuntimeSerialNumber()) { // This test should always find the managed font as long as the current numbering scheme is used. return managed_fonts[managed_font_serial_number - 1]; } return (font_count > 0) ? Internal_BinarySearchForManagedFontSerialNumber(managed_font_serial_number, managed_fonts, font_count) : nullptr; } const ON_Font* ON_ManagedFonts::GetFromFontCharacteristics( const ON_Font& font_characteristics, bool bCreateIfNotFound ) { if ( 0 == m_managed_fonts.Count() ) { // Put ON_Font::Default as the first entry in this list. Internal_AddManagedFont( &ON_Font::Default, nullptr ); } if (font_characteristics.IsManagedFont()) return &font_characteristics; const bool bIsUnderlined = font_characteristics.IsUnderlined(); const bool bIsStrikethrough = font_characteristics.IsStrikethrough(); const double point_size = ON_Font::IsValidPointSize(font_characteristics.PointSize()) ? font_characteristics.PointSize() : 0.0; const ON_Font* set_font_characteristics = &font_characteristics; std::unique_ptr< ON_Font > fup; if (false == font_characteristics.IsInstalledFont()) { bool bHaveName = font_characteristics.PostScriptName().IsNotEmpty() || font_characteristics.WindowsLogfontName().IsNotEmpty() || font_characteristics.FamilyName().IsNotEmpty(); if ( false == bHaveName || font_characteristics.HasUnsetProperties(false, false) ) { fup = std::make_unique< ON_Font >(font_characteristics); ON_Font* temporary_font = fup.get(); if (nullptr != temporary_font) { if (false == bHaveName) { *temporary_font = ON_Font::Default; } else { temporary_font->SetUnsetProperties(ON_Font::Default, true); } temporary_font->SetUnderlined(bIsUnderlined); temporary_font->SetStrikethrough(bIsStrikethrough); temporary_font->SetPointSize(point_size); set_font_characteristics = temporary_font; } } if (false == set_font_characteristics->IsValid()) return &ON_Font::Default; } ON_Font::NameLocale name_locale = ON_Font::NameLocale::LocalizedFirst; for (;;) { // quick test for default font that occurs often enough to warrent the special checking if (bIsUnderlined) break; if (bIsStrikethrough) break; if (0.0 != point_size && point_size != ON_Font::Default.m_point_size) break; if (false == ON_Font::EqualWeightStretchStyle(&ON_Font::Default, set_font_characteristics, false)) break; // there are multiple weight-stretch-style settings for the same family name and same LOGFONT.lfFaceName. ON_wString name = set_font_characteristics->PostScriptName(name_locale); if (name.IsNotEmpty()) { if (ON_wString::EqualOrdinal(name, ON_Font::Default.PostScriptName(name_locale), true)) { return &ON_Font::Default; } break; // Different PostScript names } name = set_font_characteristics->WindowsLogfontName(name_locale); if (name.IsNotEmpty()) { if (ON_wString::EqualOrdinal(name, ON_Font::Default.WindowsLogfontName(name_locale), true)) { return &ON_Font::Default; } break; // Different LOGFONT settings } name = set_font_characteristics->FamilyName(name_locale); if (name.IsNotEmpty()) { if (ON_wString::EqualOrdinal(name, ON_Font::Default.FamilyName(name_locale), true)) { name = set_font_characteristics->FaceName(name_locale); if ( name.IsEmpty() || ON_wString::EqualOrdinal(name, ON_Font::Default.FaceName(name_locale), true) ) { return &ON_Font::Default; } } break; // Different faces } break; } const ON_Font* managed_font = m_managed_fonts.FromFontProperties( set_font_characteristics, true, true, bIsUnderlined, bIsStrikethrough, point_size ); unsigned int managed_font_wss_dev = ON_Font::WeightStretchStyleDeviation( set_font_characteristics, managed_font ); if ( nullptr != managed_font && 0 == managed_font_wss_dev && point_size == managed_font->PointSize() ) { return managed_font; } for(;;) { if (set_font_characteristics->IsInstalledFont()) break; const ON_Font* installed_font = m_installed_fonts.FromFontProperties( set_font_characteristics, true, true ); if (nullptr == installed_font) break; set_font_characteristics = installed_font; if (bIsUnderlined || bIsStrikethrough || point_size > 0.0) { // Need a copy to set underlined/strikethrough/point_size fup = std::make_unique< ON_Font >(*installed_font); ON_Font* temporary_font = fup.get(); if (nullptr != temporary_font) { temporary_font->SetUnderlined(bIsUnderlined); temporary_font->SetStrikethrough(bIsStrikethrough); temporary_font->SetPointSize(point_size); set_font_characteristics = temporary_font; } } // Look again now that we have settings known to be correct. managed_font = m_managed_fonts.FromFontProperties( set_font_characteristics, true, true, bIsUnderlined, bIsStrikethrough, point_size ); managed_font_wss_dev = ON_Font::WeightStretchStyleDeviation( set_font_characteristics->FontWeight(), set_font_characteristics->FontStretch(), set_font_characteristics->FontStyle(), managed_font ); if (nullptr != managed_font && 0 == managed_font_wss_dev) return managed_font; break; } if (false == bCreateIfNotFound) return nullptr; // The managed font constructor does not copy user data, m_font_index, m_font_id, m_gonna_change_font_cache ON_MemoryAllocationTracking disable_tracking(false); const ON_FontMetrics* font_metrics = nullptr; const ON_FontGlyphCache* font_glyph_cache = set_font_characteristics->m_font_glyph_cache.get(); if (nullptr != font_glyph_cache) font_metrics = &font_glyph_cache->m_font_unit_metrics; managed_font = Internal_AddManagedFont( new ON_Font( ON_Font::FontType::ManagedFont, *set_font_characteristics), font_metrics ); return managed_font; } static void Internal_AddManagedFontSingleRefCountString( const ON_wString& s ) { const ON_wString dirty(s); ON_wString& clean = const_cast(s); clean.Destroy(); clean = static_cast(dirty); } const ON_Font* ON_ManagedFonts::Internal_AddManagedFont( const ON_Font* managed_font, const ON_FontMetrics* managed_font_metrics_in_font_design_units ) { // All memory allocated for managed fonts is permanent app workspace memory. ON_MemoryAllocationTracking disable_tracking(false); if (0 == m_default_font_ptr && 1 == managed_font->m_runtime_serial_number) { // ON_Font::Default is constructed after ON_ManagedFonts::List and // destroyed before ON_ManagedFonts::List. Therefore ~ON_ManagedFonts() // has a pointer to ON_Font::Default that must be ignored during // destruction. m_default_font_ptr = (ON__UINT_PTR)managed_font; } const ON_Font* installed_font = InstalledFonts().FromFontProperties(managed_font, true, true); if (nullptr != installed_font) managed_font->m_managed_face_is_installed = 1; else managed_font->m_managed_face_is_installed = 2; ON_FontGlyphCache* font_cache = managed_font->m_font_glyph_cache.get(); if (nullptr == font_cache) { ///////////////////// // // Put the cached glyph information here so we only have one set for each font // font_cache = ON_FontGlyphCache::New(); managed_font->m_font_glyph_cache = std::shared_ptr(font_cache); } if (nullptr != font_cache) { if (false == font_cache->m_font_unit_metrics.HeightsAreValid()) { ON_FontMetrics font_metrics_in_font_design_units; if ( nullptr != managed_font_metrics_in_font_design_units && managed_font_metrics_in_font_design_units->HeightsAreValid() ) { font_metrics_in_font_design_units = *managed_font_metrics_in_font_design_units; } else { ON_ManagedFonts::GetFontMetricsInFontDesignUnits( managed_font, font_metrics_in_font_design_units ); } font_cache->SetFontMetrics(font_metrics_in_font_design_units); } } // Insulate strings in managed fonts from outside damage. Internal_AddManagedFontSingleRefCountString(managed_font->m_locale_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_postscript_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_en_postscript_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_family_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_en_family_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_face_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_en_face_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_windows_logfont_name); Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_windows_logfont_name); m_managed_fonts.AddFont(managed_font,false); return managed_font; } #define ON_MANAGED_FONT_CHECK(falure_return_value) {if (0 != m_managed_font) (ON_ERROR("Cannot modify managed fonts."); return (falure_return_value);}} // // END list of managed ON_Fonts // ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // BEGIN list of platform ON_Fonts // #if defined(ON_RUNTIME_WIN) ////static bool ON_Internal_WindowsGetGlyphMetricsEx( //// LOGFONT& logfont, //// int logfont_height, //// //MAT2 mat2, //// ON__UINT32 unicode_code_point, //// class ON_TextBox& font_unit_glyph_box ////) ////{ //// font_unit_glyph_box = ON_TextBox::Unset; //// //// if (logfont_height <= 0) //// logfont_height = ON_Font::Constants::AnnotationFontCellHeight; //// //// if (false == ON_IsValidUnicodeCodePoint(unicode_code_point) ) //// return false; //// //// if (0 == logfont.lfFaceName[0] ) //// return false; //// //// wchar_t w[8] = { 0 }; //// const size_t w_capacity = (sizeof(w) / sizeof(w[0])) - 1; //// const int w_count = ON_EncodeWideChar(unicode_code_point, w_capacity, w); //// if ( 0 == w[0] || w_count <= 0 || w_count > (int)w_capacity ) //// return false; //// //// HDC font_hdc = nullptr; //// HFONT hfont = nullptr; //// HGDIOBJ hfont0 = nullptr; //// //// for (;;) //// { //// font_hdc = ON_Font::CreateWindowsLogfontDeviceContext(); //// if (nullptr == font_hdc) //// break; //// //// LOGFONT lf = logfont; //// lf.lfHeight = logfont_height; //// hfont = ::CreateFontIndirect(&lf); //// if (nullptr == hfont) //// break; //// hfont0 = ::SelectObject(font_hdc, hfont); //// //// ON__UINT16 glyphindex[sizeof(w)/sizeof(w[0])] = { 0 }; //// const DWORD gicount = ::GetGlyphIndices(font_hdc, w, w_count, glyphindex, GGI_MARK_NONEXISTING_GLYPHS); //// if (GDI_ERROR == gicount) //// break; //// if (0xffff == glyphindex[0] || 0xffff == glyphindex[1]) //// { //// // March 2017 Dale Lear //// // https://mcneel.myjetbrains.com/youtrack/issue/RH-38377 //// // //// // The GGI_MARK_NONEXISTING_GLYPHS flag causes GetGlyphIndices() //// // to set glyphindex[] values to 0xFFFF for missing glyphs. //// // //// // GetGlyphIndices() is not capable of getting this glyph in the hdc font. //// // This often happes for surrogate pair encodings, even when the //// // glyph does exist in the font. //// // //// // GetGlyphIndices cannot find glyph in the font //// // Often a surrogate pair and we probably need to be using //// // Windows newer Uniscribe API instead of the tired old //// // GetGlyphIndices + GetGlyphOutlineW stuff. //// break; //// } //// //// MAT2 mat2; //// memset(&mat2, 0, sizeof(mat2)); //// mat2.eM11.fract = 0; //// mat2.eM11.value = 1; //// mat2.eM12.fract = 0; //// mat2.eM12.value = 0; //// mat2.eM21.fract = 0; //// mat2.eM21.value = 0; //// mat2.eM22.fract = 0; //// mat2.eM22.value = 1; //// //// GLYPHMETRICS glm; //// memset(&glm, 0, sizeof(glm)); //// const DWORD cb_size = ::GetGlyphOutlineW(font_hdc, glyphindex[0], GGO_NATIVE | GGO_GLYPH_INDEX, &glm, 0, nullptr, &mat2); //// if (GDI_ERROR == cb_size) //// break; //// //// font_unit_glyph_box.m_advance.i = glm.gmCellIncX; //// font_unit_glyph_box.m_advance.j = glm.gmCellIncY; //// if (cb_size == 0) //// { //// // Non-printing char - nothing to draw //// font_unit_glyph_box.m_bbmin.i = 0; //// font_unit_glyph_box.m_bbmin.j = 0; //// font_unit_glyph_box.m_bbmax.i = font_unit_glyph_box.m_advance.i; //// font_unit_glyph_box.m_bbmax.j = font_unit_glyph_box.m_advance.j; //// } //// else //// { //// font_unit_glyph_box.m_bbmin.i = glm.gmptGlyphOrigin.x; //// font_unit_glyph_box.m_bbmin.j = glm.gmptGlyphOrigin.y - (int)glm.gmBlackBoxY; //// font_unit_glyph_box.m_bbmax.i = (int)glm.gmBlackBoxX + glm.gmptGlyphOrigin.x; //// font_unit_glyph_box.m_bbmax.j = glm.gmptGlyphOrigin.y; //// } //// //// break; //// } //// //// if (nullptr != font_hdc) //// { //// if (nullptr != hfont) //// { //// ::SelectObject(font_hdc, hfont0); //// ::DeleteObject(hfont); //// } //// ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc); //// } //// //// return font_unit_glyph_box.IsSet(); ////} class ON_InternalLogFontAndTextMetric { public: ON_InternalLogFontAndTextMetric() = default; ~ON_InternalLogFontAndTextMetric() = default; ON_InternalLogFontAndTextMetric(const ON_InternalLogFontAndTextMetric&) = default; ON_InternalLogFontAndTextMetric& operator=(const ON_InternalLogFontAndTextMetric&) = default; static const ON_InternalLogFontAndTextMetric Unset; public: ON_SHA1_Hash m_font_hash = ON_SHA1_Hash::ZeroDigest; LOGFONT m_lfX; TEXTMETRIC m_tmX; }; ON_InternalLogFontAndTextMetric Internal_InitUnset() { ON_InternalLogFontAndTextMetric lftm; memset(&lftm, 0, sizeof(lftm)); return lftm; } const ON_InternalLogFontAndTextMetric ON_InternalLogFontAndTextMetric::Unset(Internal_InitUnset()); ////class ON_Font_Windows_LFTM_UserData : public ON_UserData ////{ ////public: //// static void Set( //// const LOGFONT& lf, //// const TEXTMETRIC& tm, //// ON_Font* font //// ) //// { //// if (nullptr == font || 0 == lf.lfFaceName[0]) //// return; //// ON_UserData* ud = font->GetUserData(ON_Font_Windows_LFTM_UserData::Id); //// ON_Font_Windows_LFTM_UserData* lftm = ON_Font_Windows_LFTM_UserData::Cast(ud); //// if (nullptr == lftm) //// { //// if (nullptr != ud) //// delete ud; //// ud = nullptr; //// lftm = new ON_Font_Windows_LFTM_UserData(); //// } //// lftm->m_font_hash = font->FontCharacteristicsHash(); //// lftm->m_lf = lf; //// lftm->m_lftm = tm; //// if (nullptr == ud) //// { //// if (false == font->AttachUserData(lftm)) //// delete lftm; //// } //// return; //// } //// //// static ON_InternalLogFontAndTextMetric Get( //// const ON_Font* font //// ); //// //// ON_Font_Windows_LFTM_UserData() //// { //// m_userdata_uuid = ON_Font_Windows_LFTM_UserData::Id; //// m_userdata_copycount = 1; //// } //// ~ON_Font_Windows_LFTM_UserData() = default; //// ON_Font_Windows_LFTM_UserData(const ON_Font_Windows_LFTM_UserData&) = default; //// ON_Font_Windows_LFTM_UserData& operator=(const ON_Font_Windows_LFTM_UserData&) = default; //// //// ON_OBJECT_DECLARE(ON_Font_Windows_LFTM_UserData); //// ////private: ////// {452ADA1A-49F0-4FAF-8E14-73DABE21FC5C} //// static const ON_UUID Id; //// ////private: //// ON_SHA1_Hash m_font_hash = ON_SHA1_Hash::ZeroDigest; //// ON_InternalLogFontAndTextMetric m_lftm = ON_InternalLogFontAndTextMetric::Unset; ////}; //// ////// {452ADA1A-49F0-4FAF-8E14-73DABE21FC5C} ////const ON_UUID ON_Font_Windows_LFTM_UserData::Id = { 0x452ada1a, 0x49f0, 0x4faf, { 0x8e, 0x14, 0x73, 0xda, 0xbe, 0x21, 0xfc, 0x5c } }; ////ON_OBJECT_IMPLEMENT(ON_Font_Windows_LFTM_UserData, ON_UserData, "452ADA1A-49F0-4FAF-8E14-73DABE21FC5C"); //// ////void ON_Font_Windows_LFTM_UserData::Set( //// const LOGFONT& lf, //// const TEXTMETRIC& tf, //// ON_Font* font ////) ////{ ////} //// ////ON_InternalLogFontAndTextMetric ON_Font_Windows_LFTM_UserData::Get( //// const ON_Font* font ////) ////{ ////} static int CALLBACK Internal_GetPlatformWindowsLogfontFontList_CallbackProc( const LOGFONT *lf, const TEXTMETRIC *ignored_tm, DWORD font_type, LPARAM lParam ) { for (;;) { if (nullptr == lf ) break; if (font_type != TRUETYPE_FONTTYPE) break; if (0 == lParam) break; if (L'@' == lf->lfFaceName[0]) break; ((ON_SimpleArray*)lParam)->AppendNew() = *lf; break; } return 1; } static void Internal_CleanLOGFONT( LOGFONT& lf, bool bZero ) { lf.lfHeight = 0; lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; if (bZero) { lf.lfWeight = 0; lf.lfItalic = 0; lf.lfUnderline = 0; lf.lfStrikeOut = 0; lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = 0; lf.lfClipPrecision = 0; lf.lfQuality = 0; lf.lfPitchAndFamily = 0; } else { // do not change lfWeight if (0 != lf.lfItalic) lf.lfItalic = 1; if (0 != lf.lfUnderline) lf.lfUnderline = 1; if (0 != lf.lfStrikeOut) lf.lfStrikeOut = 1; if (SYMBOL_CHARSET != lf.lfCharSet) lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = ON_Font::WindowsConstants::logfont_quality; lf.lfPitchAndFamily = ON_Font::WindowsConstants::logfont_pitch_and_family; } } static int Internal_CompareLogfont(const LOGFONT* lhs, const LOGFONT* rhs) { if (lhs == rhs) return 0; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; // compare font family name const size_t face_name_capacity = sizeof(lhs->lfFaceName) / sizeof(lhs->lfFaceName[0]); int rc = ON_wString::CompareOrdinal( lhs->lfFaceName, ON_wString::Length(lhs->lfFaceName,face_name_capacity), rhs->lfFaceName, ON_wString::Length(rhs->lfFaceName,face_name_capacity), true ); if (0 != rc) return rc; // Compare style (upright, italic, ...) const int lhs_italic = (0 != lhs->lfItalic) ? 1 : 0; const int rhs_italic = (0 != rhs->lfItalic) ? 1 : 0; rc = lhs_italic - rhs_italic; if (0 != rc) return rc; // compare weight (normal, bold, ...) if (lhs->lfWeight < rhs->lfWeight) return -1; if (lhs->lfWeight > rhs->lfWeight) return 1; // compare charset last - SYMBOL_CHARSET require special treatment const int lhs_charset = (SYMBOL_CHARSET == lhs->lfCharSet) ? 1 : 0; const int rhs_charset = (SYMBOL_CHARSET == rhs->lfCharSet) ? 1 : 0; rc = lhs_charset - rhs_charset; if (0 != rc) return rc; return 0; } unsigned int ON_Font::GetInstalledWindowsLogFonts( ON_SimpleArray& logfont_list ) { logfont_list.SetCount(0); // https://en.wikipedia.org/wiki/List_of_typefaces_included_with_Microsoft_Windows HDC font_hdc = ON_Font::CreateWindowsLogfontDeviceContext(); if (nullptr == font_hdc) return 0; const DWORD flags = 0; // To enumerate all installed Windows LOGFONT values // Get list of font family names LOGFONT lf; memset(&lf, 0, sizeof(lf)); lf.lfCharSet = DEFAULT_CHARSET; ON_SimpleArray font_family_list(1000); ::EnumFontFamiliesEx( font_hdc, &lf, Internal_GetPlatformWindowsLogfontFontList_CallbackProc, (LPARAM)(&font_family_list), flags ); // remove duplicates for (int i = 0; i < font_family_list.Count(); i++) { Internal_CleanLOGFONT(font_family_list[i], true); } font_family_list.QuickSort(Internal_CompareLogfont); memset(&lf, 0, sizeof(lf)); int font_family_count = 0; for (int i = 0; i < font_family_list.Count(); i++) { if (0 != Internal_CompareLogfont(&lf, &font_family_list[i])) { lf = font_family_list[i]; font_family_list[font_family_count++] = lf; } } font_family_list.SetCount(font_family_count); // Get complete list of fonts - family and face (..., Arial(Normal , Bold, Italic, Bold Italic, ...), ... logfont_list.Reserve(3 * font_family_count); for (int i = 0; i < font_family_count; i++) { // Append a list of all faces in this font family lf = font_family_list[i]; if (0 == lf.lfFaceName[0]) continue; int count0 = logfont_list.Count(); bool bAddFamilyLOGFONT = true; lf.lfCharSet = DEFAULT_CHARSET; ::EnumFontFamiliesEx( font_hdc, &lf, Internal_GetPlatformWindowsLogfontFontList_CallbackProc, (LPARAM)(&logfont_list), flags ); // remove duplicates const int count1 = logfont_list.Count(); for (int j = count0; j < count1; j++) { Internal_CleanLOGFONT(logfont_list[j], false); } ON_qsort((void*)(logfont_list.Array() + count0), count1 - count0, sizeof(LOGFONT), (int (*)(const void*,const void*))Internal_CompareLogfont); memset(&lf, 0, sizeof(lf)); for (int j = count0; j < count1; j++) { LOGFONT lfj = logfont_list[j]; if (0 == Internal_CompareLogfont(&lf, &lfj)) continue; // Add logfont_list[j] to the culled list fonts. lf = logfont_list[j]; logfont_list[count0++] = lf; bAddFamilyLOGFONT = false; } if (bAddFamilyLOGFONT) { logfont_list[count0++] = font_family_list[i]; } logfont_list.SetCount(count0); } ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc); font_hdc = nullptr; return logfont_list.UnsignedCount(); } void ON_ManagedFonts::Internal_GetWindowsInstalledFonts( ON_SimpleArray& platform_font_list ) { // As of May 2016, Rhino 6 requires WIndows 7 SP2 and DWrite works better. // ON_Font::GetInstalledWindowsLogFonts() uses the 2 pass EnumFontFamiliesEx() technique. // It has at least 5 failures // It fails to mark "HoloLens MDL Assets" as a symbol font (lfCharSet = SYMBOL_CHARSET) // It fails to mark "Segoe MDL Assets" as a symbol font (lfCharSet = SYMBOL_CHARSET) // It fails to include "Source Sans Pro Black" // It fails to include simulated fonts. // It fails to get PostScript font names. ////ON_SimpleArray logfont_list; ////ON_Font::GetInstalledWindowsLogFonts(logfont_list); ////for (int i = 0; i < logfont_list.Count(); i++) ////{ //// LOGFONT lf = logfont_list[i]; //// lf.lfHeight = 0; //// ON_Font* font = new ON_Font(); //// if (false == font->SetFromWindowsLogFont( 0, nullptr, lf ) ) //// { //// delete font; //// continue; //// } //// platform_font_list.Append(font); ////} platform_font_list.SetCount(0); ON_SimpleArray dwrite_font_list; // freetype cannot support simulated fonts. If freetype is not used, we can include simulated fonts. const bool bIncludeSimulatedFontFaces = true; const bool bKeepDWriteFont = true; ON_Font::GetInstalledWindowsDWriteFonts( L"GetUserDefaultLocaleName", bIncludeSimulatedFontFaces, true, dwrite_font_list ); platform_font_list.Reserve(dwrite_font_list.Count()); for (int i = 0; i < dwrite_font_list.Count(); i++) { //const bool bSimulated // = dwrite_font_list[i].m_bSimulatedBold // || dwrite_font_list[i].m_bSimulatedOblique // || dwrite_font_list[i].m_bSimulatedOther; //if (bSimulated) //{ // if (false == bIncludeSimulatedFontFaces) // continue; // if (ON_Font::IsSingleStrokeFontName(dwrite_font_list[i].m_postscript_name)) // continue; //} struct IDWriteFont* dwrite_font = dwrite_font_list[i].m_dwrite_font; if (nullptr == dwrite_font) continue; for (;;) { ON_Font* installed_font = new ON_Font(ON_Font::FontType::InstalledFont, dwrite_font_list[i]); platform_font_list.Append(installed_font); break; } dwrite_font_list[i].m_dwrite_font = nullptr; dwrite_font->Release(); } } #endif ON_FontFaceQuartet::ON_FontFaceQuartet( const wchar_t* quartet_name, const ON_Font* regular, const ON_Font* bold, const ON_Font* italic, const ON_Font* bold_italic ) : m_quartet_name(quartet_name) , m_regular(regular) , m_bold(bold) , m_italic(italic) , m_bold_italic(bold_italic) { m_quartet_name.TrimLeftAndRight(); } int ON_FontFaceQuartet::CompareQuartetName( const ON_FontFaceQuartet* lhs, const ON_FontFaceQuartet* rhs ) { if (lhs == rhs) return 0; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; return ON_wString::CompareOrdinal(lhs->m_quartet_name, rhs->m_quartet_name, true); } bool ON_FontFaceQuartet::HasRegularFace() const { return (nullptr != RegularFace()); } bool ON_FontFaceQuartet::HasBoldFace() const { return (nullptr != BoldFace()); } bool ON_FontFaceQuartet::HasItalicFace() const { return (nullptr != ItalicFace()); } bool ON_FontFaceQuartet::HasBoldItalicFace() const { return (nullptr != BoldItalicFace()); } bool ON_FontFaceQuartet::HasAllFaces() const { return HasRegularFace() && HasBoldFace() && HasItalicFace() && HasBoldItalicFace(); } bool ON_FontFaceQuartet::IsEmpty() const { return (false==HasRegularFace() && false==HasBoldFace() && false==HasItalicFace() && false==HasBoldItalicFace()); } const ON_wString ON_FontFaceQuartet::QuartetName() const { return m_quartet_name; } const ON_Font* ON_FontFaceQuartet::RegularFace() const { return m_regular; } const ON_Font* ON_FontFaceQuartet::BoldFace() const { return m_bold; } const ON_Font* ON_FontFaceQuartet::ItalicFace() const { return m_italic; } const ON_Font* ON_FontFaceQuartet::BoldItalicFace() const { return m_bold_italic; } const ON_Font* ON_FontFaceQuartet::Face( bool bBold, bool bItalic ) const { return (bItalic) ? (bBold ? BoldItalicFace() : ItalicFace()) : (bBold ? BoldFace() : RegularFace()); } unsigned int ON_FontFaceQuartet::FaceCount() const { unsigned int face_count = 0; if (nullptr != RegularFace()) face_count++; if (nullptr != BoldFace()) face_count++; if (nullptr != ItalicFace()) face_count++; if (nullptr != BoldItalicFace()) face_count++; return face_count; } void ON_FontFaceQuartet::Dump(ON_TextLog& text_log) const { text_log.Print(L"Quartet Name: %ls\n", static_cast(m_quartet_name)); text_log.PushIndent(); const wchar_t* quartet_face[4] = { L"Regular",L"Bold",L"Italic",L"Bold-Italic" }; const ON_Font* quartet_font[4] = { RegularFace(),BoldFace(),ItalicFace(),BoldItalicFace() }; for (int i = 0; i < 4; i++) { const ON_Font* font = quartet_font[i]; if (nullptr == font) text_log.Print(L"%ls: \n", quartet_face[i]); else text_log.Print( L"%ls: %ls %ls (%ls) Weight = %ls Slope = %ls \n", quartet_face[i], static_cast(font->FamilyName()), static_cast(font->FaceName()), static_cast(font->PostScriptName()), ON_Font::WeightToWideString(font->FontWeight()), ON_Font::StyleToWideString(font->FontStyle()) ); } text_log.PopIndent(); } #define ON_ManagedFonts_CompareFontPointer(lhs,rhs) \ if (lhs == rhs) return 0; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; \ const ON_Font* lhs_font = *lhs; const ON_Font* rhs_font = *rhs; \ if (lhs_font == rhs_font) return 0; if (nullptr == lhs_font) return 1; if (nullptr == rhs_font) return -1 int ON_ManagedFonts::CompareFontPointer(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return 0; } int ON_FontList::ComparePostScriptName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_wString::CompareOrdinal( lhs_font->PostScriptName(ON_Font::NameLocale::LocalizedFirst), rhs_font->PostScriptName(ON_Font::NameLocale::LocalizedFirst), true ); } int ON_FontList::CompareFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_wString::CompareOrdinal( lhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst), rhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst), true ); } int ON_FontList::CompareFamilyAndFaceName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); int rc = ON_wString::CompareOrdinal( lhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst), rhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst), true ); if ( 0 == rc ) rc = ON_wString::CompareOrdinal( lhs_font->FaceName(ON_Font::NameLocale::LocalizedFirst), rhs_font->FaceName(ON_Font::NameLocale::LocalizedFirst), true ); return rc; } int ON_FontList::CompareWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_wString::CompareOrdinal( lhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst), rhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst), true ); } int ON_FontList::CompareEnglishPostScriptName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_wString::CompareOrdinal( lhs_font->PostScriptName(ON_Font::NameLocale::English), rhs_font->PostScriptName(ON_Font::NameLocale::English), true ); } int ON_FontList::CompareEnglishFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_wString::CompareOrdinal( lhs_font->FamilyName(ON_Font::NameLocale::English), rhs_font->FamilyName(ON_Font::NameLocale::English), true ); } int ON_FontList::CompareEnglishFamilyAndFaceName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); int rc = ON_wString::CompareOrdinal( lhs_font->FamilyName(ON_Font::NameLocale::English), rhs_font->FamilyName(ON_Font::NameLocale::English), true ); if ( 0 == rc ) rc = ON_wString::CompareOrdinal( lhs_font->FaceName(ON_Font::NameLocale::English), rhs_font->FaceName(ON_Font::NameLocale::English), true ); return rc; } int ON_FontList::CompareEnglishWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_wString::CompareOrdinal( lhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst), rhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst), true ); } int ON_FontList::CompareQuartetName(ON_Font const* const* lhs, ON_Font const* const* rhs) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_wString::CompareOrdinal( lhs_font->QuartetName(), rhs_font->QuartetName(), true ); } int ON_FontList::CompareWeightStretchStyle( ON_Font const* const* lhs, ON_Font const* const* rhs ) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); int rc; // Upright first, Italic 2nd, Oblique last // slope const int lhs_font_style = (int)static_cast(lhs_font->FontStyle()); const int rhs_font_style = (int)static_cast(rhs_font->FontStyle()); rc = (lhs_font_style - rhs_font_style); if (0 != rc) return rc; // width const int lhs_font_stretch = (int)static_cast(lhs_font->FontStretch()); const int rhs_font_stretch = (int)static_cast(rhs_font->FontStretch()); rc = (lhs_font_stretch - rhs_font_stretch); if (0 != rc) return rc; // weight const int lhs_font_weight = lhs_font->WindowsLogfontWeight(); const int rhs_font_weight = rhs_font->WindowsLogfontWeight(); rc = (lhs_font_weight - rhs_font_weight); if (0 != rc) return rc; return 0; } int ON_FontList::CompareUnderlinedStrikethroughPointSize( ON_Font const* const* lhs, ON_Font const* const* rhs ) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); // underlined const int lhs_font_underlined = lhs_font->IsUnderlined() ? 1 : 0; const int rhs_font_underlined = rhs_font->IsUnderlined() ? 1 : 0; int rc = (lhs_font_underlined - rhs_font_underlined); if (0 != rc) return rc; // underlined const int lhs_font_strikethrough = lhs_font->IsStrikethrough() ? 1 : 0; const int rhs_font_strikethrough = rhs_font->IsStrikethrough() ? 1 : 0; rc = (lhs_font_strikethrough - rhs_font_strikethrough); if (0 != rc) return rc; const double lhs_font_point_size = lhs_font->PointSize(); const double rhs_font_point_size = rhs_font->PointSize(); if (lhs_font_point_size < rhs_font_point_size) return -1; if (lhs_font_point_size > rhs_font_point_size) return 1; return 0; } int ON_ManagedFonts::CompareFontCharacteristicsHash( ON_Font const* const* lhs, ON_Font const* const* rhs ) { ON_ManagedFonts_CompareFontPointer(lhs, rhs); return ON_SHA1_Hash::Compare(lhs_font->FontCharacteristicsHash(), rhs_font->FontCharacteristicsHash()); } // // END list of platform ON_Fonts // ////////////////////////////////////////////////////////////////////////// bool ON_Font::ModificationPermitted( const char* function_name, const char* file_name, int line_number ) const { if (this == &ON_Font::Default) ON_ErrorEx(file_name, line_number, function_name, "ON_Font::Default cannot be modified."); else if (this == &ON_Font::Unset) ON_ErrorEx(file_name, line_number, function_name, "ON_Font::Unset cannot be modified."); else if (IsManagedFont()) ON_ErrorEx(file_name, line_number, function_name, "Managed fonts cannot be modified."); else { // Modificaton of this font means the managed information it references // will not be valid. A reference to the correct cached information // will be generated when it is actually needed. m_font_glyph_cache.reset(); return true; } return false; } #define ON_FONT_MODIFICATION_PERMITTED this->ModificationPermitted(OPENNURBS__FUNCTION__,__FILE__,__LINE__) //// V6 files 4F0F51FB-35D0-4865-9998-6D2C6A99721D is the class id for ON_TextStyle ////ON_OBJECT_IMPLEMENT( ON_Font, ON_Object, "4F0F51FB-35D0-4865-9998-6D2C6A99721D" ); //ON_OBJECT_IMPLEMENT( ON_Font, ON_Object, "5F7476D1-798A-4953-B359-4A37699CD6F4" ); const ON_Font* ON_Font::ManagedFont() const { return IsManagedFont() ? this : ON_Font::GetManagedFont(*this, true); } const ON_FontGlyph* ON_Font::CodePointGlyph( ON__UINT32 unicode_codepoint ) const { const ON_Font* managed_font = ManagedFont(); if (nullptr == managed_font) return nullptr; bool bCreateIfMissing = true; bool bFindSubstitutes = true; return managed_font->Internal_ManagedCodePointGlyph(unicode_codepoint,bCreateIfMissing,bFindSubstitutes); } const ON_Font* ON_Font::GetManagedFont( const ON_Font& font_characteristics, bool bCreateIfNotFound ) { if (font_characteristics.IsManagedFont()) { // No need to look it up and return itself. return &font_characteristics; } return ON_ManagedFonts::List.GetFromFontCharacteristics(font_characteristics,bCreateIfNotFound); } const ON_Font* ON_Font::GetManagedFontFromSerialNumber( unsigned int managed_font_runtime_serial_number ) { return ON_ManagedFonts::List.GetFromSerialNumber(managed_font_runtime_serial_number); } unsigned int ON_Font::GetManagedFontList( ON_SimpleArray< const ON_Font* >& managed_fonts ) { const ON_SimpleArray< const ON_Font* >& a = ON_ManagedFonts::ManagedFonts().ByIndex(); if (&a != &managed_fonts) managed_fonts = a; return managed_fonts.UnsignedCount(); } unsigned int ON_Font::GetInstalledFontList( ON_SimpleArray< const ON_Font* >& installed_fonts ) { const ON_SimpleArray< const ON_Font* >& a = ON_ManagedFonts::InstalledFonts().ByFamilyName(); if (&a != &installed_fonts) installed_fonts = a; return installed_fonts.UnsignedCount(); } unsigned int ON_Font::GetInstalledFontFamily( const wchar_t* font_family_name, ON_SimpleArray< const ON_Font* >& installed_fonts ) { installed_fonts.SetCount(0); if (nullptr == font_family_name || 0 == font_family_name[0]) return 0; const ON_FontList& installed_font_list = ON_ManagedFonts::InstalledFonts(); if (0 == installed_font_list.FontListFromNames( nullptr, nullptr, font_family_name, nullptr, installed_fonts) ) installed_font_list.FontListFromNames(font_family_name, font_family_name, nullptr, nullptr, installed_fonts); return installed_fonts.UnsignedCount(); } static const wchar_t* Internal_NameOverLapSkipNoise( const wchar_t* s, bool bNoiseFilter ) { if (false == bNoiseFilter) return s; if (nullptr == s) return nullptr; while (0 != *s) { if (*s >= 'A' && *s <= 'Z') return s; if (*s >= 'a' && *s <= 'z') return s; if (*s >= 0x80) return s; s++; } return s; } static const ON_wString Internal_NameOverlapCleanName( const wchar_t* s, bool bNoiseFilter ) { if (nullptr == s || 0 == s[0]) return ON_wString::EmptyString; ON_wString clean_name; wchar_t buffer[128]; int buffer_count = 0; for (wchar_t c = *(s = Internal_NameOverLapSkipNoise(s,bNoiseFilter)); 0 != c; c = *(s = Internal_NameOverLapSkipNoise(++s,bNoiseFilter))) { buffer[buffer_count++] = ON_wString::MapCharacterOrdinal(ON_StringMapOrdinalType::MinimumOrdinal, c); if (127 == buffer_count) { buffer[buffer_count] = 0; clean_name += buffer; buffer_count = 0; } } buffer[buffer_count] = 0; clean_name += buffer; return clean_name; } static int Internal_NameOverLap( const ON_wString& clean_name, const wchar_t* s, bool bNoiseFilter ) { if (nullptr == s) return 0; const wchar_t* clean_s = static_cast(clean_name); int overlap_count = 0; for ( s = Internal_NameOverLapSkipNoise(s,bNoiseFilter); 0 != *clean_s && *clean_s == ON_wString::MapCharacterOrdinal(ON_StringMapOrdinalType::LowerOrdinal,*s); s = Internal_NameOverLapSkipNoise(s,bNoiseFilter) ) { overlap_count++; clean_s++; s++; } return overlap_count; } static bool Internal_EqualLogfontName( const ON_wString& clean_logfont_name, const ON_Font* candidate_font ) { if (nullptr == candidate_font) return false; if (clean_logfont_name.IsEmpty()) return false; const ON_wString installed_font_logfont_name = Internal_NameOverlapCleanName(candidate_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),true); const bool bEqualLogfontName = ON_wString::EqualOrdinal(clean_logfont_name, installed_font_logfont_name, true); return bEqualLogfontName; } static bool Internal_EqualFamilyName( const ON_wString& clean_family_name, const ON_Font* installed_font, ON_wString& installed_font_family_name ) { if (nullptr == installed_font) return false; installed_font_family_name = Internal_NameOverlapCleanName(installed_font->FamilyName(),true); const bool bEqualFamileName = ON_wString::EqualOrdinal(clean_family_name, installed_font_family_name, true); return bEqualFamileName; } class Internal_FontDelta { // Internal_FontDelta is a simple minded attempt at measuring the difference between a target font and a candidate font. // It is probably a poor substitued for something fancier like PANOSE information. // Unfortunately, we generally do not have PANOSE information about the target font. public: Internal_FontDelta() = default; ~Internal_FontDelta() = default; Internal_FontDelta(const Internal_FontDelta&) = default; Internal_FontDelta& operator= (const Internal_FontDelta&) = default; // Initialize this to be a measure of the difference between target_font and candidate_font // The target_clean_*_name values take some time to compute, so they are pre-calculated and // passed in. There are generally multiple candidate fonts. Internal_FontDelta( const ON_Font& target_font, const ON_wString& target_clean_logfont_name, const ON_wString& target_clean_family_name, const ON_Font* candidate_font ) : m_candidate_font(candidate_font) { if (nullptr == m_candidate_font) { m_hash_delta = 6; return; } // When we have identical PostSript names, we assume they // are correct and assume font family, weight, stretch, and style // are exact matches. const ON_wString target_postscript_name = target_font.PostScriptName(); const bool bEqualPostScriptName = target_postscript_name.IsNotEmpty() && ON_wString::EqualOrdinal(target_postscript_name, m_candidate_font->PostScriptName(), true); const bool bEqualLogfontName = bEqualPostScriptName || ( ON_Font::Origin::WindowsFont == target_font.FontOrigin() && ON_Font::Origin::WindowsFont == candidate_font->FontOrigin() && target_clean_logfont_name.IsNotEmpty() && Internal_EqualLogfontName(target_clean_logfont_name, candidate_font) ); m_weight_delta = bEqualPostScriptName ? 0 : abs(((int)static_cast(target_font.FontWeight())) - ((int)static_cast(candidate_font->FontWeight()))); m_stretch_delta = bEqualPostScriptName ? 0 : abs(((int)static_cast(target_font.FontStretch())) - ((int)static_cast(candidate_font->FontStretch()))); m_style_delta = bEqualPostScriptName ? 0 : abs(((int)static_cast(target_font.FontStyle())) - ((int)static_cast(candidate_font->FontStyle()))); ON_wString candidate_clean_family_name; const bool bEqualFamilyName = bEqualLogfontName || Internal_EqualFamilyName(target_clean_family_name, candidate_font, candidate_clean_family_name); if (target_font.IsUnderlined() != candidate_font->IsUnderlined()) m_underline_and_strikethrough_delta++; if (target_font.IsStrikethrough() != candidate_font->IsStrikethrough()) m_underline_and_strikethrough_delta++; m_family_name_delta = 0; if ( bEqualFamilyName && 0 == m_weight_delta && 0 == m_stretch_delta && 0 == m_style_delta ) { // This is either an exact match (m_hash_delta = 0) or a very close match (m_hash_delta = 1). m_hash_delta = (0 == m_underline_and_strikethrough_delta && target_font.FontCharacteristicsHash() == candidate_font->FontCharacteristicsHash()) ? 0 : 1; return; } // If PANOSE information were available for target_font and candidate_font, it would be useful here. if ( bEqualLogfontName && 0 == m_stretch_delta && m_style_delta <= 1 ) { // There are at most 4 "faces" with the same WINDOWS LOGFONT.lfFaceName and the differences // among the four "faces" are almost always in weight (normal or bold) or style (upright or italic). // Fonts with larger differences in weight or style or any significant difference in stretch // typically have different values of LOGFONT.lfFaceName. // (Underlined and Strikethrough are not "faces"). m_hash_delta = 2; return; } if (bEqualFamilyName) { // A family, like "Arial", can have manu faces with a wide variety of weights, stretches, and styles. // The LOGFONTs with lfFaceName = "Arial", "Arial Narrow", "Arial Black", ... are all in the "Arial" family. m_hash_delta = 3; return; } // Differences below here (m_hash_delta >= 4) are generally visually significant. // Calculate font family name overlap int name_overlap = Internal_NameOverLap(target_clean_family_name, candidate_clean_family_name, true); m_family_name_delta = target_clean_family_name.Length() - name_overlap; if (0 == m_family_name_delta) { // clean_font_family_name.Length() = name_overlap means that // clean_font_family_name.Length() <= installed_font_family_name.Length() // and installed_font_family_name begins with clean_font_family_name. // If installed_font_family_name.Length() > clean_font_family_name.Length(), // m_name_delta < 0 and it measures how much tail was not matched m_family_name_delta = target_clean_family_name.Length() - candidate_clean_family_name.Length(); } m_hash_delta = (0 == m_style_delta || ON_Font::Style::Upright != target_font.FontStyle()) ? 4 : 5; } bool IsExactMatch() const { return (nullptr != m_candidate_font && 0 == m_hash_delta ); } // If Compare(lhs,rhs) < 0, then lhs is a better match for the target font passed to the constructor. static int Compare(const Internal_FontDelta& lhs, const Internal_FontDelta& rhs) { if (lhs.m_candidate_font == rhs.m_candidate_font) return 0; if (nullptr == rhs.m_candidate_font) return -1; // lhs is better because it has a candidate font if (nullptr == lhs.m_candidate_font) return 1; // rhs is better because it has a candidate font // m_hash_delta >= 0 and smaller values of m_hash_delta indicate better matches int rc = (lhs.m_hash_delta - rhs.m_hash_delta); if (0 != rc) return rc; rc = (lhs.m_family_name_delta - rhs.m_family_name_delta); if (0 != rc) { if (0 == lhs.m_family_name_delta) return -1; // lhs is in the same family if (0 == rhs.m_family_name_delta) return 1; // rhs is in the same family if (lhs.m_family_name_delta < 0 && rhs.m_family_name_delta < 0) { // When both values of m_name_delta are negative, it // means their font family names both began with the // family name we are searching for. In this // case the shortest tail is best. // The length of that tail is -m_name_delta, // so the least negative m_delta is the best match. return -rc; } // If at least one value of m_delta > 0, then the // smallest value of m_delta is the best match. return rc; } rc = (lhs.m_style_delta - rhs.m_style_delta); if (0 != rc) return rc; rc = (lhs.m_weight_delta - rhs.m_weight_delta); if (0 != rc) return rc; rc = (lhs.m_stretch_delta - rhs.m_stretch_delta); if (0 != rc) return rc; rc = (lhs.m_underline_and_strikethrough_delta - rhs.m_underline_and_strikethrough_delta); if (0 != rc) return rc; return 0; } // Points to a cadidate for matching the original font const ON_Font* m_candidate_font = nullptr; // 0: exact match // 1: same family, weight, stretch, style match - different hash // 2: same LOGFONT.lfName - different weight,stretch,style // 3: same family name - different LOGFONT.lfFaceName, weight,stretch,style // 4: some overlap in family name // 5: least favoriable match int m_hash_delta = 0; // m_family_name_delta // <0: partial overlap // 0: exact match // >0: installed font family name is longer but exact match with start int m_family_name_delta = 0; // m_logfont_name_delta // <0: partial overlap // 0: exact match // >0: installed font LOGFONT.lfFaceName name is longer but exact match with start int m_logfont_name_delta = 0; // Weight delta = absolute value of difference between weights. int m_weight_delta = 0; // Stretch (width) delta = absolute value of difference between stretch. int m_stretch_delta = 0; // Style (slope) delta = absolute value of difference between font styles. int m_style_delta = 0; // Underlined and strikethrough delta = number of different bools. int m_underline_and_strikethrough_delta = 0; }; const ON_Font* ON_Font::BestMatch( ON_Font const*const* candidate_font_list, size_t candidate_font_count ) const { if (nullptr == candidate_font_list || candidate_font_count < 1) return nullptr; if (1 == candidate_font_count) return candidate_font_list[0]; const ON_wString target_clean_logfont_name = Internal_NameOverlapCleanName(WindowsLogfontName(),true); const ON_wString target_clean_family_name = Internal_NameOverlapCleanName(FamilyName(),true); Internal_FontDelta best_match; for (size_t i = 0; i < candidate_font_count; i++) { const ON_Font* candidate_font = candidate_font_list[i]; if (nullptr == candidate_font) continue; const Internal_FontDelta delta( *this, target_clean_logfont_name, target_clean_family_name, candidate_font ); if (delta.IsExactMatch()) return candidate_font; if (nullptr == best_match.m_candidate_font || Internal_FontDelta::Compare(delta, best_match) < 0 ) best_match = delta; } return best_match.m_candidate_font; } unsigned int ON_Font::WeightStretchStyleDeviation( ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, ON_Font::Weight available_weight, ON_Font::Stretch available_stretch, ON_Font::Style available_style ) { if (ON_Font::Weight::Unset == prefered_weight) prefered_weight = available_weight; if (ON_Font::Stretch::Unset == prefered_stretch) prefered_stretch = available_stretch; if (ON_Font::Style::Unset == prefered_style) prefered_style = available_style; const unsigned int weight_delta = abs(((int)prefered_weight) - ((int)available_weight)); const unsigned int stretch_delta = abs(((int)prefered_stretch) - ((int)available_stretch)); const unsigned int style_delta = abs(((int)prefered_style) - ((int)available_style)); return 4*(1000U * style_delta + 20U * weight_delta + stretch_delta); } unsigned int ON_Font::WeightStretchStyleDeviation( ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, const ON_Font* available_font ) { if (nullptr == available_font) return 0xFFFFFFF; return ON_Font::WeightStretchStyleDeviation( prefered_weight, prefered_stretch, prefered_style, available_font->FontWeight(), available_font->FontStretch(), available_font->FontStyle() ); } unsigned int ON_Font::WeightStretchStyleDeviation( const ON_Font* prefered_weight_stretch_style, const ON_Font* available_font ) { if (nullptr == prefered_weight_stretch_style) prefered_weight_stretch_style = &ON_Font::Default; ON_Font::Weight prefered_weight = prefered_weight_stretch_style->FontWeight(); ON_Font::Stretch prefered_stretch = prefered_weight_stretch_style->FontStretch(); ON_Font::Style prefered_style = prefered_weight_stretch_style->FontStyle(); return ON_Font::WeightStretchStyleDeviation(prefered_weight, prefered_stretch, prefered_style, available_font); } unsigned int ON_Font::UnderlinedStrikethroughDeviation( bool bPreferedUnderline, bool bPreferedStrikethrough, bool bAvailableUnderline, bool bAvailableStrikethrough ) { const unsigned int underlined_dev = (unsigned int)abs((int)(bPreferedUnderline ? 1 : 0) - (int)(bAvailableUnderline ? 1 : 0)); const unsigned int strikethrough_dev = (unsigned int)abs((int)(bPreferedStrikethrough ? 1 : 0) - (int)(bAvailableStrikethrough ? 1 : 0)); return 2 * underlined_dev + strikethrough_dev; } unsigned int ON_Font::UnderlinedStrikethroughDeviation( bool bPreferedUnderline, bool bPreferedStrikethrough, const ON_Font* available_font ) { return ON_Font::UnderlinedStrikethroughDeviation( bPreferedUnderline, bPreferedStrikethrough, (nullptr == available_font) ? false : available_font->IsUnderlined(), (nullptr == available_font) ? false : available_font->IsStrikethrough() ); } unsigned int ON_Font::UnderlinedStrikethroughDeviation( const ON_Font* prefered_underlined_strikethrough, const ON_Font* available_font ) { return ON_Font::UnderlinedStrikethroughDeviation( (nullptr == prefered_underlined_strikethrough) ? false : prefered_underlined_strikethrough->IsUnderlined(), (nullptr == prefered_underlined_strikethrough) ? false : prefered_underlined_strikethrough->IsStrikethrough(), (nullptr == available_font) ? false : available_font->IsUnderlined(), (nullptr == available_font) ? false : available_font->IsStrikethrough() ); } unsigned int ON_Font::RichTextPropertyDeviation( bool bPreferedRtfBold, bool bPreferedItalic, bool bPreferedUnderline, bool bPreferedStrikethrough, const ON_Font* available_font ) { if (nullptr == available_font) return 0xFFFFFFFF; return ON_Font::RichTextPropertyDeviation( bPreferedRtfBold, bPreferedItalic, bPreferedUnderline, bPreferedStrikethrough, available_font->IsBold(), available_font->IsItalic(), available_font->IsUnderlined(), available_font->IsStrikethrough() ); } unsigned int ON_Font::RichTextPropertyDeviation( bool bPreferedRtfBold, bool bPreferedItalic, bool bPreferedUnderline, bool bPreferedStrikethrough, bool bAvailableRtfBold, bool bAvailableItalic, bool bAvailableUnderline, bool bAvailableStrikethrough ) { const ON_Font::Weight prefered_weight = bPreferedRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal; const ON_Font::Weight available_weight = bAvailableRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal; const ON_Font::Style prefered_style = bPreferedItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright; const ON_Font::Style available_style = bPreferedItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright; const unsigned int wss_dev = ON_Font::WeightStretchStyleDeviation( prefered_weight, ON_Font::Stretch::Medium, prefered_style, available_weight, ON_Font::Stretch::Medium, available_style ); const unsigned int us_dev = ON_Font::UnderlinedStrikethroughDeviation( bPreferedUnderline, bPreferedStrikethrough, bAvailableUnderline, bAvailableStrikethrough ); return wss_dev + us_dev; } const ON_Font* ON_Font::Internal_BestMatchWeightStretchStyle( ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, ON_Font const * const * font_list, size_t font_count ) { if (nullptr == font_list || font_count <= 0) return nullptr; const ON_Font* best_font = nullptr; unsigned int best_delta = 0xFFFFFFFF; for (size_t i = 0; i < font_count; i++) { const ON_Font* font = font_list[i]; if (nullptr == font) continue; const unsigned int delta = ON_Font::WeightStretchStyleDeviation( prefered_weight, prefered_stretch, prefered_style, font ); if (0 == delta) return font; if (nullptr == best_font || delta < best_delta) { best_font = font; best_delta = delta; } } return best_font; } const ON_Font* ON_Font::BestMatch( const ON_SimpleArray< const ON_Font* >& font_list ) const { return BestMatch( font_list.Array(), font_list.UnsignedCount() ); } const ON_FontList& ON_Font::ManagedFontList() { return ON_ManagedFonts::ManagedFonts(); } const ON_FontList& ON_Font::InstalledFontList() { return ON_ManagedFonts::InstalledFonts(); } const ON_Font* ON_Font::InstalledFontFromRichTextProperties( const wchar_t* rtf_font_name, bool bRtfBold, bool bRtfItalic ) { ON_wString s(rtf_font_name); s.TrimLeftAndRight(); if (s.IsEmpty()) s = ON_Font::DefaultFamilyName(); rtf_font_name = s; ON_Font font(ON_Font::Default); if (bRtfBold) font.SetFontWeight(ON_Font::Weight::Bold); if (bRtfItalic) font.SetFontStyle(ON_Font::Style::Italic); font.Internal_ClearAllNames(); font.m_loc_family_name = rtf_font_name; font.m_en_family_name = font.m_loc_family_name; font.m_loc_postscript_name = rtf_font_name; font.m_en_postscript_name = font.m_loc_postscript_name; font.m_loc_windows_logfont_name = rtf_font_name; font.m_en_windows_logfont_name = font.m_loc_windows_logfont_name; return ON_Font::InstalledFontList().FromFontProperties(&font,false,true); } const ON_wString ON_Font::RichTextPropertiesToString( bool bRtfBold, bool bRtfItalic, bool bRtfUnderlined, bool bRtfStrikethrough ) { return ON_wString::FormatToString( L"%ls%ls%ls", (bRtfBold ? (bRtfItalic ? L"Bold Italic" : L"Bold") : (bRtfItalic ? L"Italic" : L"Regular")), (bRtfUnderlined ? L" Underlined" : L""), (bRtfStrikethrough ? L" Strikethrough" : L"") ); } const ON_wString ON_Font::RichTextPropertiesToString( ON_Font::Weight rtf_weight, ON_Font::Style rtf_style, bool bRtfUnderlined, bool bRtfStrikethrough ) { return RichTextPropertiesToString( ON_Font::IsBoldWeight(rtf_weight), ON_Font::Style::Italic == rtf_style, bRtfUnderlined, bRtfStrikethrough ); } const ON_wString ON_Font::RichTextPropertiesToString( const ON_Font* font ) { if (font == nullptr) font = &ON_Font::Default; return RichTextPropertiesToString( font->FontWeight(), font->FontStyle(), font->IsUnderlined(), font->IsStrikethrough() ); } static unsigned int Internal_RtfDeviation( const ON_Font* font, bool bRtfBold, bool bRtfItalic, bool bRftUnderlined, bool bRftStrikethrough ) { if (nullptr == font) return 0xFFFFFFFF; unsigned int bold_dev = (unsigned int)abs((int)(font->IsBold()?1:0) - (int)(bRtfBold?1:0)); unsigned int italic_dev = (unsigned int)abs((int)(font->IsItalic()?1:0) - (int)(bRtfItalic?1:0)); unsigned int undelined_dev = (unsigned int)abs((int)(font->IsUnderlined()?1:0) - (int)(bRftUnderlined?1:0)); unsigned int strikethrough_dev = (unsigned int)abs((int)(font->IsStrikethrough()?1:0) - (int)(bRftStrikethrough?1:0)); return (8 * italic_dev + 4 * bold_dev + 2 * undelined_dev + 1 * strikethrough_dev); } const ON_Font* ON_Font::ManagedFontFromRichTextProperties( const wchar_t* rtf_font_name, bool bRtfBold, bool bRtfItalic, bool bRftUnderlined, bool bRftStrikethrough ) { ON_wString s(rtf_font_name); s.TrimLeftAndRight(); if (s.IsEmpty()) s = ON_Font::DefaultFamilyName(); rtf_font_name = s; // insure exat true/false settings so we can compare bRtfBold = bRtfBold ? true : false; bRftStrikethrough = bRftStrikethrough ? true : false; const ON_Font* managed_font = ManagedFontList().FromRichTextProperties(rtf_font_name, bRtfBold, bRtfItalic, bRftUnderlined, bRftStrikethrough); unsigned int managed_font_dev = Internal_RtfDeviation(managed_font, bRtfBold, bRtfItalic, bRftUnderlined, bRftStrikethrough); if (nullptr != managed_font && managed_font_dev <= 3) { if (managed_font_dev > 0) { // add underlined and strikethrough settings ON_Font font(*managed_font); font.SetUnderlined(bRftUnderlined); font.SetStrikethrough(bRftStrikethrough); managed_font = font.ManagedFont(); } return managed_font; } const ON_Font* installed_font = ON_Font::InstalledFontFromRichTextProperties(rtf_font_name, bRtfBold, bRtfItalic); unsigned int installed_font_dev = Internal_RtfDeviation(installed_font, bRtfBold, bRtfItalic, bRftUnderlined, bRftStrikethrough); if (nullptr != installed_font && installed_font_dev <= 3) { if (installed_font_dev > 0) { ON_Font font(*installed_font); font.SetUnderlined(bRftUnderlined); font.SetStrikethrough(bRftStrikethrough); managed_font = font.ManagedFont(); } else { managed_font = installed_font->ManagedFont(); } return managed_font; } if (nullptr != managed_font && managed_font_dev <= installed_font_dev) return managed_font; // found something in the "rtf family/face" on this device or from a recently read model. if (nullptr != installed_font) return installed_font->ManagedFont(); // found something in the "rtf family/face" on this device const ON_wString loc_family_name( (nullptr != installed_font && installed_font->FamilyName().IsNotEmpty()) ? installed_font->FamilyName() : ON_wString(rtf_font_name) ); const ON_wString en_family_name( (nullptr != installed_font && installed_font->m_en_family_name.IsNotEmpty()) ? installed_font->m_en_family_name : loc_family_name ); // There is not font is not installed on this device with any type of name that is equal to rtf_font_name ON_Font font((nullptr != installed_font) ? (*installed_font) : ON_Font::Default); if (bRtfBold != font.IsBold()) font.SetFontWeight(bRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal); if (bRtfItalic!= font.IsItalic()) font.SetFontStyle(bRtfItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright); if (bRftUnderlined) font.SetUnderlined(bRftUnderlined); if (bRftStrikethrough) font.SetUnderlined(bRftStrikethrough); font.Internal_ClearAllNames(); ON_wString postscript_name = rtf_font_name; ON_wString face_name; if (bRtfBold && bRtfItalic) { postscript_name += L"-BoldItalic"; face_name = L"Bold Italic"; } else if (bRtfBold) { postscript_name += L"-Bold"; face_name = L"Bold"; } else if (bRtfItalic) { postscript_name += L"-Bold"; face_name = L"Italic"; } else if (bRtfItalic) { face_name = L"Regular"; } font.m_loc_family_name = loc_family_name; font.m_en_family_name = en_family_name; // Best guess face name font.m_loc_face_name = face_name; font.m_en_face_name = font.m_loc_face_name; // Best guess PostScript name. font.m_loc_postscript_name = postscript_name; font.m_en_postscript_name = font.m_loc_postscript_name; // This is not correct, but works better than anything else, especially when saving as V5. font.m_loc_windows_logfont_name = rtf_font_name; font.m_en_windows_logfont_name = font.m_loc_windows_logfont_name; return font.ManagedFont(); } const ON_2dex ON_FontList::Internal_SearchSortedList( const ON_Font* key, ON_FontPtrCompareFunc compare_func, const ON_SimpleArray< const ON_Font* >& sorted_font_list ) { for (;;) { if (nullptr == key || nullptr == compare_func) break; const int sorted_count = sorted_font_list.Count(); if (sorted_count <= 0) break; const int k = sorted_font_list.BinarySearch(&key, compare_func); if (k < 0 || k >= sorted_count) break; int i0 = k; while (i0 > 0 && 0 == compare_func(&key, &(sorted_font_list[i0 - 1]) )) i0--; int i1 = k+1; while (i1 < sorted_count && 0 == compare_func(&key, &(sorted_font_list[i1]) )) i1++; return ON_2dex(i0, i1); } return ON_2dex(ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX); } void ON_FontList::Internal_EmptyLists() { m_by_index.SetCount(0); m_unsorted.SetCount(0); m_by_postscript_name.SetCount(0); m_by_windows_logfont_name.SetCount(0); m_by_family_name.SetCount(0); m_by_english_postscript_name.SetCount(0); m_by_english_windows_logfont_name.SetCount(0); m_by_english_family_name.SetCount(0); m_by_quartet_name.SetCount(0); m_quartet_list.Destroy(); } static int Internal_CompareLogfontNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs) { int rc = ON_FontList::CompareWindowsLogfontName(lhs, rhs); if (0 != rc) return rc; rc = ON_FontList::CompareWeightStretchStyle(lhs, rhs); if (0 != rc) return rc; rc = ON_FontList::CompareUnderlinedStrikethroughPointSize(lhs, rhs); if (0 != rc) return rc; return ON_ManagedFonts::CompareFontCharacteristicsHash(lhs, rhs); } static int Internal_CompareFamilyNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs) { int rc = ON_FontList::CompareFamilyAndFaceName(lhs, rhs); if (0 != rc) return rc; return Internal_CompareLogfontNameEtc(lhs, rhs); } static int Internal_ComparePostScriptNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs) { int rc = ON_FontList::ComparePostScriptName(lhs, rhs); if (0 != rc) return rc; return Internal_CompareFamilyNameEtc(lhs, rhs); } static int Internal_CompareEnglishLogfontNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs) { int rc = ON_FontList::CompareEnglishWindowsLogfontName(lhs, rhs); if (0 != rc) return rc; rc = ON_FontList::CompareWeightStretchStyle(lhs, rhs); if (0 != rc) return rc; rc = ON_FontList::CompareUnderlinedStrikethroughPointSize(lhs, rhs); if (0 != rc) return rc; return ON_ManagedFonts::CompareFontCharacteristicsHash(lhs, rhs); } static int Internal_CompareEnglishFamilyNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs) { int rc = ON_FontList::CompareEnglishFamilyAndFaceName(lhs, rhs); if (0 != rc) return rc; return Internal_CompareEnglishLogfontNameEtc(lhs, rhs); } static int Internal_CompareEnglishPostScriptNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs) { int rc = ON_FontList::CompareEnglishPostScriptName(lhs, rhs); if (0 != rc) return rc; return Internal_CompareEnglishFamilyNameEtc(lhs, rhs); } static int Internal_CompareQuartetNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs) { int rc = ON_FontList::CompareQuartetName(lhs, rhs); if (0 != rc) return rc; rc = ON_FontList::CompareWeightStretchStyle(lhs, rhs); if (0 != rc) return rc; rc = ON_FontList::CompareUnderlinedStrikethroughPointSize(lhs, rhs); if (0 != rc) return rc; return ON_ManagedFonts::CompareFontCharacteristicsHash(lhs, rhs); } void ON_FontList::Internal_UpdateSortedLists() const { const int unsorted_count = m_unsorted.Count(); if (unsorted_count <= 0) return; ON_SimpleArray< const ON_Font* >* sorted_lists[7] = { &m_by_postscript_name, &m_by_windows_logfont_name, &m_by_family_name, &m_by_english_postscript_name, &m_by_english_windows_logfont_name, &m_by_english_family_name, &m_by_quartet_name }; ON_FontPtrCompareFunc compare_funcs[7] = { Internal_ComparePostScriptNameEtc, Internal_CompareLogfontNameEtc, Internal_CompareFamilyNameEtc, Internal_CompareEnglishPostScriptNameEtc, Internal_CompareEnglishLogfontNameEtc, Internal_CompareEnglishFamilyNameEtc, Internal_CompareQuartetNameEtc }; const int array_dex_max = (int)(sizeof(sorted_lists) / sizeof(sorted_lists[0])); for (int array_dex = 0; array_dex < array_dex_max; array_dex++) { ON_SimpleArray< const ON_Font* >& sorted_list = *(sorted_lists[array_dex]); bool bNeedSort = false; sorted_list.Reserve(sorted_list.Count() + unsorted_count); for (int j = 0; j < unsorted_count; j++) { const ON_Font* font = m_unsorted[j]; if (nullptr == font) continue; if (0 == array_dex) { if (font->PostScriptName(m_name_locale).IsEmpty()) continue; } else if (1 == array_dex) { if (font->WindowsLogfontName(m_name_locale).IsEmpty()) continue; } else if (2 == array_dex) { if (font->FamilyName(m_name_locale).IsEmpty()) continue; } else if (3 == array_dex) { const ON_wString en = font->PostScriptName(ON_Font::NameLocale::English); if (en.IsEmpty()) continue; if (ON_wString::EqualOrdinal(en, font->PostScriptName(ON_Font::NameLocale::English), true)) continue; } else if (4 == array_dex) { const ON_wString en = font->WindowsLogfontName(ON_Font::NameLocale::English); if (en.IsEmpty()) continue; if (ON_wString::EqualOrdinal(en, font->WindowsLogfontName(ON_Font::NameLocale::English), true)) continue; } else if (5 == array_dex) { const ON_wString en = font->FamilyName(ON_Font::NameLocale::English); if (en.IsEmpty()) continue; if (ON_wString::EqualOrdinal(en, font->FamilyName(ON_Font::NameLocale::English), true)) continue; } else if (6 == array_dex) { const ON_wString qname = font->QuartetName(); if (qname.IsEmpty()) continue; // m_quartet_list[] will get remade when it's needed m_quartet_list.SetCount(0); } sorted_list.Append(font); bNeedSort = true; } if ( bNeedSort ) sorted_list.QuickSort(compare_funcs[array_dex]); } m_unsorted.SetCount(0); } ON_FontList::ON_FontList( bool bMatchUnderlineAndStrikethrough ) : m_bMatchUnderlineStrikethroughAndPointSize(bMatchUnderlineAndStrikethrough) {} unsigned int ON_FontList::Count() const { return m_by_index.UnsignedCount(); } ON_Font::NameLocale ON_FontList::NameLocale() const { return m_name_locale; } const ON_Font* ON_FontList::FromPostScriptName( const wchar_t* postscript_name ) const { return FromPostScriptName(postscript_name, ON_Font::Weight::Normal, ON_Font::Stretch::Condensed, ON_Font::Style::Upright, false, false); } const ON_Font* ON_FontList::FromPostScriptName( const wchar_t* postscript_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style ) const { return FromNames( postscript_name, nullptr, nullptr, nullptr, prefered_weight, prefered_stretch, prefered_style, false, false ); } const ON_Font* ON_FontList::FromPostScriptName( const wchar_t* postscript_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, bool bUnderlined, bool bStrikethrough ) const { return FromNames( postscript_name, nullptr, nullptr, nullptr, prefered_weight, prefered_stretch, prefered_style, false, false, bUnderlined, bStrikethrough, 0.0 ); } const ON_Font* ON_FontList::FromWindowsLogfontName( const wchar_t* windows_logfont_name ) const { return FromWindowsLogfontName(windows_logfont_name, ON_Font::Weight::Normal, ON_Font::Stretch::Condensed, ON_Font::Style::Upright); } const ON_Font* ON_FontList::FromWindowsLogfontName( const wchar_t* windows_logfont_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style ) const { return FromNames( nullptr, windows_logfont_name, nullptr, nullptr, prefered_weight, prefered_stretch, prefered_style, false, false ); } const ON_Font* ON_FontList::FromWindowsLogfontName( const wchar_t* windows_logfont_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, bool bUnderlined, bool bStrikethrough ) const { return FromNames( nullptr, windows_logfont_name, nullptr, nullptr, prefered_weight, prefered_stretch, prefered_style, false, false, bUnderlined, bStrikethrough, 0.0 ); } const ON_Font* ON_FontList::FromFamilyName( const wchar_t* family_name, const wchar_t* prefered_face_name ) const { return FromFamilyName(family_name, prefered_face_name, ON_Font::Weight::Normal, ON_Font::Stretch::Medium, ON_Font::Style::Upright); } const ON_Font* ON_FontList::FromFamilyName( const wchar_t* family_name, const wchar_t* prefered_face_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style ) const { return FromNames( nullptr, nullptr, family_name, prefered_face_name, prefered_weight, prefered_stretch, prefered_style, false, false ); } const ON_Font* ON_FontList::FromFamilyName( const wchar_t* family_name, const wchar_t* prefered_face_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, bool bUnderlined, bool bStrikethrough ) const { return FromNames( nullptr, nullptr, family_name, prefered_face_name, prefered_weight, prefered_stretch, prefered_style, false, false, bUnderlined, bStrikethrough, 0.0 ); } const ON_Font* ON_FontList::FromRichTextProperties( const wchar_t* rtf_font_name, bool bRtfBold, bool bRtfItalic, bool bUnderlined, bool bStrikethrough ) const { // ballpark weight and stretch values. Closest match is returned. const ON_Font::Weight prefered_weight = bRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal; const ON_Font::Stretch prefered_stretch = ON_Font::Stretch::Medium; const ON_Font::Style prefered_style = bRtfItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright; bool bRequireFaceMatch = false; bool bRequireStyleMatch = true; return FromNames( rtf_font_name, // PostScript guess rtf_font_name, // Windows LOGFONT.lfFaceName guess rtf_font_name, // Family name guess nullptr, prefered_weight, prefered_stretch, prefered_style, bRequireFaceMatch, bRequireStyleMatch, bUnderlined, bStrikethrough, 0.0 ); } const ON_Font* ON_FontList::FromNames( const wchar_t* postscript_name, const wchar_t* windows_logfont_name, const wchar_t* family_name, const wchar_t* prefered_face_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, bool bRequireFaceMatch, bool bRequireStyleMatch ) const { const bool bMatchUnderlineStrikethroughAndPointSize = false; return Internal_FromNames( postscript_name, windows_logfont_name, family_name, prefered_face_name, prefered_weight, prefered_stretch, prefered_style, bRequireFaceMatch, bRequireStyleMatch, bMatchUnderlineStrikethroughAndPointSize, false, false, 0.0 ); } const ON_Font* ON_FontList::FromNames( const wchar_t* postscript_name, const wchar_t* windows_logfont_name, const wchar_t* family_name, const wchar_t* prefered_face_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, bool bRequireFaceMatch, bool bRequireStyleMatch, bool bUnderlined, bool bStrikethrough, double point_size ) const { bool bMatchUnderlineStrikethroughAndPointSize = true; return Internal_FromNames( postscript_name, windows_logfont_name, family_name, prefered_face_name, prefered_weight, prefered_stretch, prefered_style, bRequireFaceMatch, bRequireStyleMatch, bMatchUnderlineStrikethroughAndPointSize, bUnderlined, bStrikethrough, point_size ); } const ON_Font* ON_FontList::Internal_FromNames( const wchar_t* postscript_name, const wchar_t* windows_logfont_name, const wchar_t* family_name, const wchar_t* prefered_face_name, ON_Font::Weight prefered_weight, ON_Font::Stretch prefered_stretch, ON_Font::Style prefered_style, bool bRequireFaceMatch, bool bRequireStyleMatch, bool bMatchUnderlineStrikethroughAndPointSize, bool bUnderlined, bool bStrikethrough, double point_size ) const { if (ON_Font::Stretch::Unset == prefered_stretch) bRequireStyleMatch = false; if (bUnderlined) bUnderlined = true; if (bStrikethrough) bStrikethrough = true; if (!(point_size > 0.0 && point_size < ON_UNSET_POSITIVE_FLOAT)) point_size = 0.0; if (false == m_bMatchUnderlineStrikethroughAndPointSize) bMatchUnderlineStrikethroughAndPointSize = false; const ON_SimpleArray< const ON_Font* > * sorted_lists[16] = {}; ON_FontPtrCompareFunc compare_funcs[16] = { 0 }; ON_Font key; key.m_loc_postscript_name = postscript_name; key.m_loc_postscript_name.TrimLeftAndRight(); key.m_en_postscript_name = key.m_loc_postscript_name; key.m_loc_windows_logfont_name = windows_logfont_name; key.m_loc_windows_logfont_name.TrimLeftAndRight(); key.m_en_windows_logfont_name = key.m_loc_windows_logfont_name; key.m_loc_family_name = family_name; key.m_loc_family_name.TrimLeftAndRight(); key.m_en_family_name = key.m_loc_family_name; key.m_loc_face_name = prefered_face_name; key.m_loc_face_name.TrimLeftAndRight(); key.m_en_face_name = key.m_loc_face_name; key.m_font_weight = prefered_weight; key.m_font_stretch = prefered_stretch; key.m_font_style = prefered_style; const bool bKeyHasFamilyAndFace = key.m_loc_family_name.IsNotEmpty() && key.m_loc_face_name.IsNotEmpty(); if (false == bKeyHasFamilyAndFace) bRequireFaceMatch = false; int pass_count = 0; // First compare family AND face name. In general, there will not be multiple // fonts with the same family and face name combination. if (key.m_loc_family_name.IsNotEmpty() && key.m_loc_face_name.IsNotEmpty()) { sorted_lists[pass_count] = &m_by_family_name; compare_funcs[pass_count] = ON_FontList::CompareFamilyAndFaceName; pass_count++; sorted_lists[pass_count] = &m_by_english_family_name; compare_funcs[pass_count] = ON_FontList::CompareEnglishFamilyAndFaceName; pass_count++; } #if defined(ON_RUNTIME_WIN) // On Windows, check LOGFONT.lfFaceName before PostScript // It is common for 4 distinct faces to have the same LOGFONT lfFaceName. if (key.m_loc_windows_logfont_name.IsNotEmpty()) { sorted_lists[pass_count] = &m_by_windows_logfont_name; compare_funcs[pass_count] = ON_FontList::CompareWindowsLogfontName; pass_count++; sorted_lists[pass_count] = &m_by_english_windows_logfont_name; compare_funcs[pass_count] = ON_FontList::CompareEnglishWindowsLogfontName; pass_count++; } #endif // Check PostScript name // On Windows, when simulated fonts or OpenType variable face fonts are in use, // it is very common for distict faces to have the same PostScript font name. // It is less common in MacOS. if (key.m_loc_postscript_name.IsNotEmpty()) { sorted_lists[pass_count] = &m_by_postscript_name; compare_funcs[pass_count] = ON_FontList::ComparePostScriptName; pass_count++; sorted_lists[pass_count] = &m_by_english_postscript_name; compare_funcs[pass_count] = ON_FontList::CompareEnglishPostScriptName; pass_count++; } #if !defined(ON_RUNTIME_WIN) // Windows LOGFONT.lfFaceName checked after PostScript // It is common for 4 distinct faces to have the same LOGFONT lfFaceName. if (key.m_loc_windows_logfont_name.IsNotEmpty()) { sorted_lists[pass_count] = &m_by_windows_logfont_name; compare_funcs[pass_count] = ON_FontList::CompareWindowsLogfontName; pass_count++; sorted_lists[pass_count] = &m_by_english_windows_logfont_name; compare_funcs[pass_count] = ON_FontList::CompareEnglishWindowsLogfontName; pass_count++; } #endif // The final passes search by Family name. // It is generally the case that multiple faces have the same family name. if (key.m_loc_family_name.IsNotEmpty()) { sorted_lists[pass_count] = &m_by_family_name; compare_funcs[pass_count] = ON_FontList::CompareFamilyName; pass_count++; sorted_lists[pass_count] = &m_by_english_family_name; compare_funcs[pass_count] = ON_FontList::CompareEnglishFamilyName; pass_count++; } if (pass_count <= 0) return nullptr; // Move any unsorted fonts into the sorted lists. Internal_UpdateSortedLists(); const ON_Font* font = nullptr; unsigned int font_dev = 0xFFFFFFFF; const ON_Font* pkey = &key; for (int pass = 0; pass < pass_count; pass++) { ON_FontPtrCompareFunc compare_func = compare_funcs[pass]; if (nullptr == compare_func) continue; const ON_Font* candidate = nullptr; unsigned int candidate_dev = 0xFFFFFFFF; for (int list_dex = 0; list_dex < 2; list_dex++) { if (1 == list_dex) { if (nullptr == sorted_lists[pass]) continue; } const ON_SimpleArray< const ON_Font* >& sorted_list = (1==list_dex) ? *(sorted_lists[pass]) : m_unsorted; const ON_2dex subset = (1==list_dex) ? ON_FontList::Internal_SearchSortedList(&key, compare_func, sorted_list) : ON_2dex(0,m_unsorted.Count()) ; if (subset.i < 0 || subset.j <= 0) continue; for (int i = subset.i; i < subset.j; i++) { candidate = sorted_list[i]; if (nullptr == candidate) continue; if (0 == list_dex) { if (0 != compare_func(&pkey, &candidate)) continue; } if (bMatchUnderlineStrikethroughAndPointSize) { if (candidate->IsUnderlined() != bUnderlined) continue; if (candidate->IsStrikethrough() != bStrikethrough) continue; if (candidate->PointSize() != point_size) continue; } if (bRequireStyleMatch && prefered_style != candidate->FontStyle()) continue; const bool bCandidateFamilyAndFaceMatch = bKeyHasFamilyAndFace && (ON_Font::EqualFontFamilyAndFace(&key, candidate) || 0 == ON_FontList::CompareEnglishFamilyAndFaceName(&pkey,&candidate)); if (bRequireFaceMatch && candidate->FamilyName().IsNotEmpty() && false == bCandidateFamilyAndFaceMatch) continue; const bool bFontFamilyAndFaceMatch = bKeyHasFamilyAndFace && (nullptr != font) && (ON_Font::EqualFontFamilyAndFace(&key, font) || 0 == ON_FontList::CompareEnglishFamilyAndFaceName(&pkey,&candidate)); if (bFontFamilyAndFaceMatch && false == bCandidateFamilyAndFaceMatch) continue; candidate_dev = ON_Font::WeightStretchStyleDeviation(prefered_weight, prefered_stretch, prefered_style, candidate); if (0 == candidate_dev) { if ( bCandidateFamilyAndFaceMatch || false == bKeyHasFamilyAndFace) return candidate; } if ( nullptr == font || (bCandidateFamilyAndFaceMatch && false == bFontFamilyAndFaceMatch) || candidate_dev < font_dev ) { font = candidate; font_dev = candidate_dev; } } } } return font; } unsigned int ON_FontList::FontListFromNames( const wchar_t* postscript_name, const wchar_t* windows_logfont_name, const wchar_t* family_name, const wchar_t* face_name, ON_SimpleArray< const ON_Font* >& font_list ) const { const unsigned int font_list_count0 = font_list.UnsignedCount(); ON_Font key; key.m_loc_postscript_name = postscript_name; key.m_loc_postscript_name.TrimLeftAndRight(); key.m_en_postscript_name = key.m_loc_postscript_name; key.m_loc_windows_logfont_name = windows_logfont_name; key.m_loc_windows_logfont_name.TrimLeftAndRight(); key.m_en_windows_logfont_name = key.m_loc_windows_logfont_name; key.m_loc_family_name = family_name; key.m_loc_family_name.TrimLeftAndRight(); key.m_en_family_name = key.m_loc_family_name; Internal_UpdateSortedLists(); for (int pass = 0; pass < 3; pass++) { ON_SimpleArray< const ON_Font* >* sorted_list = nullptr; ON_2dex subset(-1, -1); switch (pass) { case 0: if (key.m_loc_postscript_name.IsEmpty()) continue; sorted_list = &m_by_postscript_name; subset = Internal_SearchSortedList(&key, ON_FontList::ComparePostScriptName, *sorted_list); break; case 1: if (key.m_loc_windows_logfont_name.IsEmpty()) continue; sorted_list = &m_by_windows_logfont_name; subset = Internal_SearchSortedList(&key, ON_FontList::CompareWindowsLogfontName, *sorted_list); break; case 2: if (key.m_loc_family_name.IsEmpty()) continue; sorted_list = &m_by_family_name; subset = Internal_SearchSortedList(&key, ON_FontList::CompareFamilyName, *sorted_list); break; } if (subset.j <= 0) break; const ON_Font* pkey = &key; for (int i = subset.i; i < subset.j; i++) { const ON_Font* font = (*sorted_list)[i]; if ( pass < 1 && key.m_loc_windows_logfont_name.IsNotEmpty() && 0 != ON_FontList::CompareWindowsLogfontName(&pkey, &font) ) continue; if (key.m_loc_family_name.IsNotEmpty()) { if (0 != ON_FontList::CompareFamilyName(&pkey, &font)) continue; if (key.m_loc_face_name.IsNotEmpty() && false == ON_wString::EqualOrdinal(key.FaceName(m_name_locale), font->FaceName(m_name_locale), true) ) continue; } font_list.Append(font); } break; } return font_list.UnsignedCount() - font_list_count0; } const ON_Font* ON_FontList::FromFontProperties( const ON_Font* font_properties, bool bRequireFaceMatch, bool bRequireStyleMatch ) const { return FromNames( font_properties->PostScriptName(m_name_locale), font_properties->WindowsLogfontName(m_name_locale), font_properties->FamilyName(m_name_locale), font_properties->FaceName(m_name_locale), font_properties->FontWeight(), font_properties->FontStretch(), font_properties->FontStyle(), bRequireFaceMatch, bRequireStyleMatch ); } const ON_Font* ON_FontList::FromFontProperties( const ON_Font* font_properties, bool bRequireFaceMatch, bool bRequireStyleMatch, bool bUnderlined, bool bStrikethrough, double point_size ) const { return FromNames( font_properties->PostScriptName(m_name_locale), font_properties->WindowsLogfontName(m_name_locale), font_properties->FamilyName(m_name_locale), font_properties->FaceName(m_name_locale), font_properties->FontWeight(), font_properties->FontStretch(), font_properties->FontStyle(), bRequireFaceMatch, bRequireStyleMatch, bUnderlined, bStrikethrough, point_size ); } const ON_Font* ON_FontList::FamilyMemberWithWeightStretchStyle( const wchar_t* family_name, ON_Font::Weight desired_weight, ON_Font::Stretch desired_stretch, ON_Font::Style desired_style ) const { ON_Font key; key.m_loc_family_name = family_name; key.m_loc_family_name.TrimLeftAndRight(); if (key.m_loc_family_name.IsEmpty()) key.m_loc_family_name = ON_Font::DefaultFamilyName(); else key.m_en_family_name = key.m_loc_family_name; key.m_font_weight = desired_weight; key.m_font_stretch = desired_stretch; key.m_font_style = desired_style; const ON_2dex subdex = Internal_SearchSortedList(&key, ON_FontList::CompareFamilyName, m_by_family_name); if (subdex.j <= 0) return nullptr; const ON_Font* candidate = nullptr; unsigned int candidate_dev = 0xFFFFFFFF; for (int i = subdex.i; i < subdex.j; i++) { const ON_Font* font = m_by_family_name[i]; if (nullptr == font) continue; unsigned int font_dev = ON_Font::WeightStretchStyleDeviation(&key, font); if (nullptr == candidate || font_dev < candidate_dev) { candidate = font; candidate_dev = font_dev; } } return candidate; } const ON_Font* ON_FontList::FamilyMemberWithWeightStretchStyle( const ON_Font* font, ON_Font::Weight desired_weight, ON_Font::Stretch desired_stretch, ON_Font::Style desired_style ) const { if (nullptr == font) font = &ON_Font::Default; else if ( font->FamilyName().IsEmpty() && font->FaceName().IsEmpty() && font->WindowsLogfontName().IsEmpty() ) font = &ON_Font::Default; if (ON_Font::Weight::Unset == desired_weight) desired_weight = font->FontWeight(); if (ON_Font::Stretch::Unset == desired_stretch) desired_stretch = font->FontStretch(); if (ON_Font::Style::Unset == desired_style) desired_style = font->FontStyle(); ON_wString family_name = font->FamilyName(); if (family_name.IsEmpty()) { const ON_SimpleArray< const ON_Font* > * sorted_lists[2] = {&m_by_windows_logfont_name,&m_by_postscript_name}; ON_FontPtrCompareFunc compare_funcs[2] = {ON_FontList::CompareWindowsLogfontName,ON_FontList::ComparePostScriptName}; const bool bNameIsEmpty[2] = { font->WindowsLogfontName().IsEmpty(),font->PostScriptName().IsEmpty() }; const int k1 = (int)(sizeof(bNameIsEmpty) / sizeof(bNameIsEmpty[0])); for (int k = 0; k < k1 && family_name.IsEmpty(); k++) { if (bNameIsEmpty[k]) continue; const ON_SimpleArray< const ON_Font* >& sorted_list = *sorted_lists[k]; const ON_2dex subdex = Internal_SearchSortedList(font, compare_funcs[k], sorted_list); if (subdex.j <= 0) continue; for (int i = subdex.i; i < subdex.j && family_name.IsEmpty(); i++) { const ON_Font* f = sorted_list[i]; if (nullptr == f) continue; family_name = f->FamilyName(); if (family_name.IsNotEmpty()) break; } } } const ON_Font* family_member = FamilyMemberWithWeightStretchStyle(family_name, desired_weight, desired_stretch, desired_style); return family_member; } const ON_Font* ON_Font::InstalledFamilyMemberWithWeightStretchStyle( ON_Font::Weight desired_weight, ON_Font::Stretch desired_stretch, ON_Font::Style desired_style ) const { return ON_Font::InstalledFontList().FamilyMemberWithWeightStretchStyle( this, desired_weight, desired_stretch, desired_style ); } const ON_Font* ON_Font::ManagedFamilyMemberWithRichTextProperties( bool bBold, bool bItalic, bool bUnderlined, bool bStrikethrough ) const { // Get clean boolean values for safe comparisons. bBold = bBold ? true : false; bItalic = bItalic ? true : false; bUnderlined = bUnderlined ? true : false; bStrikethrough = bStrikethrough ? true : false; const ON_Font::Weight desired_weight = (bBold != IsBoldInQuartet()) ? (bBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal) : FontWeight(); const ON_Font::Style desired_style = (bItalic != IsItalic()) ? (bItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright) : FontStyle(); return ManagedFamilyMemberWithWeightStretchStyle( desired_weight, FontStretch(), desired_style, bUnderlined, bStrikethrough ); } const ON_Font* ON_Font::ManagedFamilyMemberWithWeightStretchStyle( ON_Font::Weight desired_weight, ON_Font::Stretch desired_stretch, ON_Font::Style desired_style, bool bUnderlined, bool bStrikethrough ) const { if (ON_Font::Weight::Unset == desired_weight) desired_weight = FontWeight(); if (ON_Font::Stretch::Unset == desired_stretch) desired_stretch = FontStretch(); if (ON_Font::Style::Unset == desired_style) desired_style = FontStyle(); bool bChangeWeight = (desired_weight != FontWeight()); bool bChangeStretch = (desired_stretch != FontStretch()); bool bChangeStyle = (desired_style != FontStyle()); bool bChangeUnderlined = ((bUnderlined ? true : false) != IsUnderlined()); bool bChangeStrikethrough = ((bStrikethrough ? true : false) != IsStrikethrough()); bool bChangeSomething = bChangeWeight || bChangeStretch || bChangeStyle || bChangeUnderlined || bChangeStrikethrough; const ON_Font* font = this; if ( bChangeWeight || bChangeStretch || bChangeStyle ) { const ON_Font* installed_font = InstalledFamilyMemberWithWeightStretchStyle(desired_weight, desired_stretch, desired_style); if (nullptr != installed_font) { font = installed_font; bChangeWeight = false; bChangeStretch = false; bChangeStyle = false; bChangeUnderlined = (bUnderlined ? true : false) != installed_font->IsUnderlined(); bChangeStrikethrough = ((bStrikethrough ? true : false) != installed_font->IsStrikethrough()); bChangeSomething = bChangeWeight || bChangeStretch || bChangeStyle || bChangeUnderlined || bChangeStrikethrough; } } if (false == bChangeSomething) return font->ManagedFont(); ON_Font changed_font(*font); if (bChangeWeight && ON_Font::Weight::Unset != desired_weight) changed_font.SetFontWeight(desired_weight); if (bChangeStretch && ON_Font::Stretch::Unset != desired_stretch) changed_font.SetFontStretch(desired_stretch); if (bChangeStyle && ON_Font::Style::Unset != desired_style) changed_font.SetFontStyle(desired_style); if (bChangeUnderlined) changed_font.SetUnderlined(bUnderlined); if (bChangeStrikethrough) changed_font.SetStrikethrough(bStrikethrough); return changed_font.ManagedFont(); } const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByPostScriptName() const { Internal_UpdateSortedLists(); return m_by_postscript_name; } const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByWindowsLogfontName() const { Internal_UpdateSortedLists(); return m_by_windows_logfont_name; } const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByFamilyName() const { Internal_UpdateSortedLists(); return m_by_family_name; } const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByQuartetName() const { Internal_UpdateSortedLists(); return m_by_quartet_name; } const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByIndex() const { return m_by_index; } const ON_FontFaceQuartet ON_FontList::QuartetFromQuartetName( const wchar_t* quartet_name ) const { for (;;) { const ON_FontFaceQuartet qname(quartet_name, nullptr, nullptr, nullptr, nullptr); if (qname.QuartetName().IsEmpty()) break; const ON_ClassArray< ON_FontFaceQuartet >& quartet_list = ON_FontList::QuartetList(); const int quartet_list_count = quartet_list.Count(); int i = quartet_list.BinarySearch(&qname, ON_FontFaceQuartet::CompareQuartetName); if (i < 0 || i >= quartet_list_count) break; while (i > 0 && 0 == ON_FontFaceQuartet::CompareQuartetName(&qname, &quartet_list[i - 1])) i--; return quartet_list[i]; } return ON_FontFaceQuartet::Empty; } const ON_Font* ON_FontList::FontFromQuartetProperties( const wchar_t* quartet_name, bool bBold, bool bItalic ) const { const ON_FontFaceQuartet qname(quartet_name, nullptr, nullptr, nullptr, nullptr); if (qname.QuartetName().IsEmpty()) return nullptr; const ON_ClassArray< ON_FontFaceQuartet >& quartet_list = ON_FontList::QuartetList(); const int quartet_list_count = quartet_list.Count(); int i = quartet_list.BinarySearch(&qname, ON_FontFaceQuartet::CompareQuartetName); if (i < 0 || i >= quartet_list_count) return nullptr; while (i > 0 && 0 == ON_FontFaceQuartet::CompareQuartetName(&qname, &quartet_list[i - 1])) i--; do { const ON_FontFaceQuartet& q = quartet_list[i]; if (0 != ON_FontFaceQuartet::CompareQuartetName(&qname, &q)) break; const ON_Font* font = q.Face(bBold, bItalic); if (nullptr != font) return font; i++; } while (i < quartet_list_count); return nullptr; } const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const { // call ByQuartetName() first to insure m_quartet_list[] is set correctly. const ON_SimpleArray& a = this->ByQuartetName(); if (m_quartet_list.Count() > 0) return m_quartet_list; const unsigned int font_count = a.UnsignedCount(); m_quartet_list.Reserve(32 + font_count / 4); const ON_Font* f = nullptr; const ON_Font* upright6[6] = {}; const ON_Font* italic6[6] = {}; for (unsigned int i = 0; i < font_count; i++) { f = a[i]; if (nullptr == f) continue; const ON_wString quartet_name = f->QuartetName(); if (quartet_name.IsEmpty()) continue; memset(upright6, 0, sizeof(upright6)); memset(italic6, 0, sizeof(italic6)); const ON_Font** sextet = (f->IsItalic()) ? italic6 : upright6; if (ON_Font::Weight::Normal == f->FontWeight()) sextet[2] = f; else if (ON_Font::Weight::Medium == f->FontWeight()) sextet[3] = f; else if (f->IsBold()) sextet[4] = f; else if (f->IsLight()) sextet[1] = f; else continue; while(i+1 < font_count) { f = a[i + 1]; if (nullptr == f) break; if (false == quartet_name.EqualOrdinal(f->QuartetName(), true)) break; i++; const unsigned int f_weight = static_cast(f->FontWeight()); sextet = (f->IsItalic()) ? italic6 : upright6; if (ON_Font::Weight::Normal == f->FontWeight()) sextet[2] = f; else if (ON_Font::Weight::Medium == f->FontWeight()) sextet[3] = f; else if (f->IsBold()) { if (nullptr == sextet[4]) sextet[4] = f; else if (f_weight < static_cast(sextet[4]->FontWeight())) { if (nullptr == sextet[5]) sextet[5] = sextet[4]; sextet[4] = f; } else if (nullptr != sextet[5] && f_weight > static_cast(sextet[5]->FontWeight())) sextet[5] = f; } else if (f->IsLight()) { if (nullptr == sextet[1]) sextet[1] = f; else if (f_weight > static_cast(sextet[1]->FontWeight())) { if ( nullptr == sextet[0]) sextet[0] = sextet[1]; sextet[1] = f; } else if (nullptr != sextet[0] && f_weight < static_cast(sextet[0]->FontWeight())) sextet[0] = f; } } for (int sex_dex = 0; sex_dex < 2; sex_dex++) { sextet = (1==sex_dex) ? italic6 : upright6; if (nullptr == sextet[2]) { sextet[2] = sextet[3]; sextet[3] = nullptr; } else if (nullptr == sextet[4]) { sextet[4] = sextet[3]; sextet[3] = nullptr; } if (nullptr != sextet[2]) { if (nullptr != sextet[5]) sextet[4] = sextet[5]; continue; } if (nullptr != sextet[1] && nullptr != sextet[4]) { sextet[2] = sextet[1]; continue; } if (nullptr != sextet[1]) { if (nullptr != sextet[0]) { sextet[2] = sextet[0]; sextet[4] = sextet[1]; } else { sextet[2] = sextet[1]; } continue; } if (nullptr != sextet[4]) { sextet[2] = sextet[4]; sextet[4] = sextet[5]; continue; } } ON_FontFaceQuartet q(quartet_name,upright6[2],upright6[4],italic6[2],italic6[4]); if (q.IsEmpty()) continue; m_quartet_list.Append(q); } return m_quartet_list; } unsigned int ON_FontList::AddFont( const ON_Font* font, bool bCheckforDuplicates ) { if (nullptr == font) return false; if ( font->PostScriptName(m_name_locale).IsEmpty() && font->WindowsLogfontName(m_name_locale).IsEmpty() && font->FamilyName(m_name_locale).IsEmpty() ) return false; if ( ON_Font::Weight::Unset == font->FontWeight() || ON_Font::Stretch::Unset == font->FontStretch() || ON_Font::Style::Unset == font->FontStyle() ) return false; if (bCheckforDuplicates) { const ON_Font* f = FromFontProperties(font, true, true); if ( nullptr != f && f->FontWeight() == font->FontWeight() && f->FontStretch() == font->FontStretch() && f->FontStyle() == font->FontStyle() && f->IsUnderlined() == font->IsUnderlined() && f->IsStrikethrough() == font->IsStrikethrough() && ON_wString::EqualOrdinal(f->PostScriptName(m_name_locale), font->PostScriptName(m_name_locale), true) && ON_wString::EqualOrdinal(f->WindowsLogfontName(m_name_locale), font->WindowsLogfontName(m_name_locale), true) && ON_wString::EqualOrdinal(f->FamilyName(m_name_locale), font->FamilyName(m_name_locale), true) && ON_wString::EqualOrdinal(f->FaceName(m_name_locale), font->FaceName(m_name_locale), true) ) { return false; } } m_by_index.Append(font); int count = m_by_index.Count(); if (count > 1) { for (int i = count - 2; i <= 0; i--) { // font = m_by_index[i+1] const ON_Font* a = m_by_index[i]; if (a->RuntimeSerialNumber() <= font->RuntimeSerialNumber()) break; // almost never happens so bubble insertion is ok m_by_index[i] = font; m_by_index[i + 1] = a; } } m_unsorted.Append(font); m_quartet_list.SetCount(0); return 1; } unsigned int ON_FontList::AddFonts( const ON_SimpleArray< const ON_Font* >& fonts ) { const size_t font_count = fonts.UnsignedCount(); const ON_Font * const * font_list = fonts.Array(); return AddFonts(font_count,font_list); } unsigned int ON_FontList::AddFonts( size_t font_count, const ON_Font * const * font_list ) { if (nullptr == font_list || font_count <= 0) return 0; unsigned int rc = 0; for (size_t i = 0; i < font_count; i++) { rc += AddFont(font_list[i],false); } return rc; } const ON_Font* ON_Font::InstalledFont( bool bAllowBestMatch ) const { return IsInstalledFont() ? this : ON_ManagedFonts::InstalledFonts().FromFontProperties(this, !bAllowBestMatch, !bAllowBestMatch); } bool ON_Font::IsManagedFont() const { return ( 0 != m_runtime_serial_number ); } bool ON_Font::IsInstalledFont() const { bool rc; switch (m_font_type) { case ON_Font::FontType::InstalledFont: rc = true; break; case ON_Font::FontType::ManagedFont: rc = (1 == m_managed_face_is_installed); break; default: rc = false; break; } return rc; } const ON_Font* ON_Font::GetManagedFont( const wchar_t* face_name ) { return ON_Font::GetManagedFont(0.0, face_name); } const ON_Font* ON_Font::GetManagedFont( double point_size, const wchar_t* face_name ) { return ON_Font::GetManagedFont( point_size, face_name, ON_Font::Default.m_font_weight, ON_Font::Default.m_font_style ); } const ON_Font* ON_Font::GetManagedFont( const wchar_t* face_name, bool bBold ) { return ON_Font::GetManagedFont(0.0, face_name, bBold); } const ON_Font* ON_Font::GetManagedFont( double point_size, const wchar_t* face_name, bool bBold ) { const bool bItalic = false; return ON_Font::GetManagedFont( point_size, face_name, bBold, bItalic ); } const ON_Font* ON_Font::GetManagedFont( const wchar_t* face_name, bool bBold, bool bItalic ) { return ON_Font::GetManagedFont(0.0, face_name, bBold, bItalic); } const ON_Font* ON_Font::GetManagedFont( double point_size, const wchar_t* face_name, bool bBold, bool bItalic ) { return ON_Font::GetManagedFont( point_size, face_name, bBold ? ON_Font::Weight::Bold : ON_Font::Default.FontWeight(), bItalic ? ON_Font::Style::Italic : ON_Font::Default.FontStyle() ); } const ON_Font* ON_Font::GetManagedFont( const wchar_t* face_name, ON_Font::Weight font_weight, ON_Font::Style font_style ) { return ON_Font::GetManagedFont(0.0, face_name, font_weight, font_style); } const ON_Font* ON_Font::GetManagedFont( double point_size, const wchar_t* face_name, ON_Font::Weight font_weight, ON_Font::Style font_style ) { const unsigned int logfont_charset = static_cast(ON_Font::WindowsLogfontCharSetFromFaceName(face_name)); return ON_Font::GetManagedFont( point_size, face_name, font_weight, font_style, ON_Font::Default.m_font_stretch, ON_Font::Default.m_font_bUnderlined, ON_Font::Default.m_font_bStrikethrough, ON_FontMetrics::DefaultLineFeedRatio, logfont_charset ); } const ON_Font* ON_Font::GetManagedFont( const wchar_t* face_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough, double linefeed_ratio, unsigned int logfont_charset ) { return ON_Font::GetManagedFont( 0.0, // point_size face_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough, linefeed_ratio, logfont_charset ); } const ON_Font* ON_Font::GetManagedFont( double point_size, const wchar_t* face_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough, double linefeed_ratio, unsigned int logfont_charset ) { ON_Font font_characteristics; if ( false == font_characteristics.SetFontCharacteristics( point_size, face_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough, linefeed_ratio, logfont_charset )) return nullptr; return font_characteristics.ManagedFont(); } #if defined(ON_OS_WINDOWS_GDI) const ON_Font* ON_Font::GetManagedFontFromWindowsLogfont( int map_mode, HDC hdc, const LOGFONT& logfont ) { ON_Font font_characteristics; if (false == font_characteristics.SetFromWindowsLogFont(map_mode, hdc,logfont)) return nullptr; return font_characteristics.ManagedFont(); } #endif const ON_Font* ON_Font::GetManagedFontFromFontDescription( const wchar_t* font_description ) { ON_Font font_characteristics; if ( false == font_characteristics.SetFromFontDescription(font_description) ) return nullptr; return font_characteristics.ManagedFont(); } bool ON_Font::IsNotAppleFontName( const wchar_t* font_description ) { if (nullptr == font_description || 0 == font_description[0]) return true; if ( ON_wString::EqualOrdinal(L"Default",font_description,true) ) return true; // In RH-35535 Marlin reports that Arial is shipped with OS X. //if ( ON_wString::EqualOrdinal(L"Arial",font_description,true) ) // return true; return false; } const ON_Font* ON_Font::GetManagedFontFromAppleFontName( const char* postscript_name ) { return ON_Font::GetManagedFontFromPostScriptName(postscript_name); } const ON_Font* ON_Font::GetManagedFontFromAppleFontName( const wchar_t* postscript_name ) { return ON_Font::GetManagedFontFromPostScriptName(postscript_name); } const ON_Font* ON_Font::GetManagedFontFromPostScriptName( const char* postscript_name ) { const ON_wString wide_string_postscript_name(postscript_name); return ON_Font::GetManagedFontFromPostScriptName(wide_string_postscript_name); } const ON_Font* ON_Font::GetManagedFontFromPostScriptName( const wchar_t* postscript_name ) { ON_wString buffer(postscript_name); buffer.TrimLeftAndRight(); postscript_name = buffer; if (nullptr == postscript_name || 0 == postscript_name[0]) return &ON_Font::Default; const ON_Font* managed_font = ON_Font::ManagedFontList().FromPostScriptName(postscript_name); if (nullptr != managed_font) return managed_font; const ON_Font* installed_font = ON_Font::InstalledFontList().FromPostScriptName(postscript_name); if (nullptr != installed_font) return installed_font->ManagedFont(); // This font is not installed. ON_Font font(ON_Font::Unset); // prefered weight/stretch/style since this font is not installed font.SetFontWeight(ON_Font::Weight::Normal); font.SetFontStretch(ON_Font::Stretch::Medium); font.SetFontStyle(ON_Font::Style::Upright); font.m_loc_postscript_name = postscript_name; font.m_en_postscript_name = font.m_loc_postscript_name; return font.ManagedFont(); } int ON_Font::WindowsLogfontWeightFromWeight( ON_Font::Weight font_weight ) { int logfont_weight = (int)(100U*static_cast(font_weight)); if ( logfont_weight < 50 ) logfont_weight = 400; if ( logfont_weight < 150 ) logfont_weight = 100; else if ( logfont_weight >= 850 ) logfont_weight = 900; else if (0 != logfont_weight % 100) { int delta = logfont_weight %100; if (delta < 50) logfont_weight -= delta; else logfont_weight += (100-delta); } return logfont_weight; } int ON_Font::AppleWeightOfFontFromWeight( ON_Font::Weight font_weight ) { return ON_Font::WindowsLogfontWeightFromWeight(font_weight)/100; } double ON_Font::AppleFontWeightTraitFromWeight( ON_Font::Weight font_weight ) { // These values are selected to optimize conversion of font weights between Windows and Apple platforms. // https://mcneel.myjetbrains.com/youtrack/issue/RH-37075 const double default_apple_font_weight_trait = 0.0; double w = ((double)((int)static_cast(font_weight)) - 400.0) / 750.0; if (w < -1.0) w = -1.0; else if (w > 1.0) w = 1.0; if (!(-1.0 <= w && w < 1.0)) w = default_apple_font_weight_trait; double apple_font_weight_trait; switch (font_weight) { case ON_Font::Weight::Unset: apple_font_weight_trait = default_apple_font_weight_trait; break; case ON_Font::Weight::Thin: apple_font_weight_trait = -0.4; break; case ON_Font::Weight::Ultralight: apple_font_weight_trait = w; break; case ON_Font::Weight::Light: apple_font_weight_trait = w; break; case ON_Font::Weight::Normal: apple_font_weight_trait = 0.0; break; case ON_Font::Weight::Medium: apple_font_weight_trait = w; break; case ON_Font::Weight::Semibold: apple_font_weight_trait = w; break; case ON_Font::Weight::Bold: apple_font_weight_trait = 0.4; break; case ON_Font::Weight::Ultrabold: apple_font_weight_trait = w; break; case ON_Font::Weight::Heavy: apple_font_weight_trait = w; break; default: apple_font_weight_trait = default_apple_font_weight_trait; break; } // The valid value range is from -1.0 to 1.0. The value of 0.0 corresponds to the regular or medium font weight. return (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) ? apple_font_weight_trait : default_apple_font_weight_trait; } ON_Font::Weight ON_Font::WeightFromWindowsLogfontWeight( int windows_logfont_weight ) { if ( windows_logfont_weight <= 0 || windows_logfont_weight > 1000 ) return ON_Font::Weight::Normal; if ( windows_logfont_weight < 150 ) return ON_Font::Weight::Thin; if ( windows_logfont_weight >= 850 ) return ON_Font::Weight::Heavy; const ON_Font::Weight weights[] = { ON_Font::Weight::Thin, // = 1 ON_Font::Weight::Ultralight, // = 2 ON_Font::Weight::Light, // = 3 ON_Font::Weight::Normal, // = 4 ON_Font::Weight::Medium, // = 5 ON_Font::Weight::Semibold, // = 6 ON_Font::Weight::Bold, // = 7 ON_Font::Weight::Ultrabold, // = 8 ON_Font::Weight::Heavy, // = 9 }; const size_t weight_count = sizeof(weights) / sizeof(weights[0]); ON_Font::Weight font_weight = ON_Font::Weight::Normal; int delta = std::abs(static_cast(ON_Font::WindowsLogfontWeightFromWeight(font_weight)) - windows_logfont_weight); for (size_t i = 0; 0 != delta && i < weight_count; i++) { // look for a closer match int d = std::abs(static_cast(ON_Font::WindowsLogfontWeightFromWeight(weights[i])) - windows_logfont_weight); if (d < delta) { font_weight = weights[i]; delta = d; } } return font_weight; } ON_Font::Weight ON_Font::WeightFromAppleWeightOfFont( int apple_weight_of_font ) { return ON_Font::WeightFromWindowsLogfontWeight(apple_weight_of_font*100); } ON_Font::Weight ON_Font::WeightFromAppleFontWeightTrait( double apple_font_weight_trait ) { if (false == ON_IsValid(apple_font_weight_trait)) return ON_Font::Weight::Unset; const double x = (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) ? apple_font_weight_trait : 0.0; int windows_logfont_weight = (int)(400.0 + 750.0*x); if (windows_logfont_weight < 1) windows_logfont_weight = 1; else if (windows_logfont_weight > 1000) windows_logfont_weight = 1000; return ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight); } void ON_Font::Internal_CopyFrom( const ON_Font& src ) { if ( 0 == ((ON__UINT_PTR)(&src)) ) { ON_ERROR("nullptr is target of ON_Font copy ctor or operator=. Crash is imminent or already happened."); return; } const bool bThisIsManagedFont = (ON_Font::FontType::ManagedFont == m_font_type); if ( bThisIsManagedFont ) { if (0 == m_runtime_serial_number) { ON_ERROR("Invalid parameters: true == bThisIsManagedFont and 0 == m_runtime_serial_number."); return; } } else { if (0 != m_runtime_serial_number && ON_Font::FontType::ManagedFont != m_font_type) { ON_ERROR("Invalid parameters: false == bThisIsManagedFont and 0 != m_runtime_serial_number."); return; } } if ( ((ON__UINT_PTR)(&src)) <= ON_PTR_SEMAPHORE_MAX ) { // If 8 == ((ON__UINT_PTR)(&src)), Initializing ON_Font::Unset (and m_runtime_serial_number is 0 ) // If 16 == ((ON__UINT_PTR)(&src)), Initializing ON_Font::Default (and m_runtime_serial_number is already set to 1) // (Multiples of 8 are used so that runtime pointer alignment checkers don't get alarmed.) const bool bDefaultFont = (m_runtime_serial_number > 0); m_font_weight = bDefaultFont ? ON_Font::Weight::Normal : ON_Font::Weight::Unset; m_font_stretch = bDefaultFont ? ON_Font::Stretch::Medium : ON_Font::Stretch::Unset; m_font_style = bDefaultFont ? ON_Font::Style::Upright : ON_Font::Style::Unset; m_loc_family_name = (bDefaultFont ? ON_Font::DefaultFamilyName() : L""); m_en_family_name = (bDefaultFont ? ON_Font::DefaultFamilyName() : L""); m_loc_face_name = (bDefaultFont ? ON_Font::DefaultFaceName() : L""); m_en_face_name = (bDefaultFont ? ON_Font::DefaultFaceName() : L""); m_loc_windows_logfont_name = (bDefaultFont ? ON_Font::DefaultWindowsLogfontName() : L""); m_en_windows_logfont_name = (bDefaultFont ? ON_Font::DefaultWindowsLogfontName() : L""); m_loc_postscript_name = (bDefaultFont ? ON_Font::DefaultPostScriptName() : L""); m_en_postscript_name = (bDefaultFont ? ON_Font::DefaultPostScriptName() : L""); m_font_bUnderlined = false; m_font_bStrikethrough = false; m_apple_font_weight_trait = 0.0; m_windows_logfont_weight = 400; m_logfont_charset = ON_Font::WindowsConstants::logfont_default_charset; m_point_size = 0.0; m_font_origin = bDefaultFont ? #if defined(ON_RUNTIME_WIN) ON_Font::Origin::WindowsFont #elif defined(ON_RUNTIME_APPLE) ON_Font::Origin::AppleFont #else ON_Font::Origin::Unknown #endif : ON_Font::Origin::Unset; const ON_Font* installed_font = bDefaultFont ? ON_Font::InstalledFontList().FromFontProperties(this, true, true) : nullptr; if ( nullptr != installed_font && ON_Font::EqualFontFamily(this, installed_font) && m_font_style == installed_font->FontStyle() ) { // Set stretch from installed font. m_font_stretch = installed_font->FontStretch(); // want an exact name match if ( installed_font->m_loc_postscript_name.IsNotEmpty() ) m_loc_postscript_name = installed_font->m_loc_postscript_name; if ( installed_font->m_en_postscript_name.IsNotEmpty() ) m_en_postscript_name = installed_font->m_en_postscript_name; if ( installed_font->m_loc_family_name.IsNotEmpty() ) m_loc_family_name = installed_font->m_loc_family_name; if ( installed_font->m_en_family_name.IsNotEmpty() ) m_en_family_name = installed_font->m_en_family_name; #if defined(ON_RUNTIME_WIN) if ( installed_font->m_loc_face_name.IsNotEmpty() ) m_loc_face_name = installed_font->m_loc_face_name; if ( installed_font->m_en_face_name.IsNotEmpty() ) m_en_face_name = installed_font->m_en_face_name; if ( installed_font->m_loc_windows_logfont_name.IsNotEmpty() ) m_loc_windows_logfont_name = installed_font->m_loc_windows_logfont_name; if ( installed_font->m_en_windows_logfont_name.IsNotEmpty() ) m_en_windows_logfont_name = installed_font->m_en_windows_logfont_name; m_logfont_charset = installed_font->m_logfont_charset; #endif // exact platform weight match m_windows_logfont_weight = installed_font->m_windows_logfont_weight; m_apple_font_weight_trait = installed_font->m_apple_font_weight_trait; m_panose1 = installed_font->m_panose1; // share glyph cache m_font_glyph_cache = installed_font->m_font_glyph_cache; } } else { m_font_weight = src.m_font_weight; m_windows_logfont_weight = src.m_windows_logfont_weight; m_apple_font_weight_trait = src.m_apple_font_weight_trait; m_font_style = src.m_font_style; m_font_stretch = src.m_font_stretch; m_font_bUnderlined = src.m_font_bUnderlined; m_font_bStrikethrough = src.m_font_bStrikethrough; m_logfont_charset = src.m_logfont_charset; m_locale_name = src.m_locale_name; m_loc_postscript_name = src.m_loc_postscript_name; m_en_postscript_name = src.m_en_postscript_name; m_loc_family_name = src.m_loc_family_name; m_en_family_name = src.m_en_family_name; m_loc_face_name = src.m_loc_face_name; m_en_face_name = src.m_en_face_name; m_loc_windows_logfont_name = src.m_loc_windows_logfont_name; m_en_windows_logfont_name = src.m_en_windows_logfont_name; bool bCopyCache = (0 == m_runtime_serial_number && ON_Font::FontType::Unset == m_font_type); if ( false == bCopyCache && ON_Font::FontType::ManagedFont == m_font_type && ON_Font::FontType::InstalledFont == src.m_font_type && nullptr != src.m_font_glyph_cache.get() && nullptr == m_font_glyph_cache.get() ) { // destination font is managed and src font is installed bCopyCache = true; } if (bCopyCache) m_font_glyph_cache = src.m_font_glyph_cache; m_point_size = src.m_point_size; m_font_origin = src.m_font_origin; m_panose1 = src.m_panose1; m_simulated = src.m_simulated; } m_font_characteristics_hash = ON_SHA1_Hash::ZeroDigest; } ON_Font::ON_Font() { /* //sz = 192 //offsets[0] 0 const unsigned __int64 //this //offsets[1] 0 const unsigned __int64 //m_runtime_serial_number //offsets[2] 4 const unsigned __int64 //m_windows_logfont_weight //offsets[3] 8 const unsigned __int64 //m_point_size //offsets[4] 16 const unsigned __int64 //m_apple_font_weight_trait //offsets[5] 24 const unsigned __int64 //m_font_weight //offsets[6] 25 const unsigned __int64 //offsets[7] 26 const unsigned __int64 //offsets[8] 27 const unsigned __int64 //offsets[9] 28 const unsigned __int64 //offsets[10] 29 const unsigned __int64 //offsets[11] 30 const unsigned __int64 //offsets[12] 31 const unsigned __int64 //m_font_type //offsets[13] 32 const unsigned __int64 //m_locale_name //offsets[14] 40 const unsigned __int64 //offsets[15] 48 const unsigned __int64 //offsets[16] 56 const unsigned __int64 //offsets[17] 64 const unsigned __int64 //offsets[18] 72 const unsigned __int64 //m_loc_face_name //offsets[19] 80 const unsigned __int64 //offsets[20] 88 const unsigned __int64 //offsets[21] 96 const unsigned __int64 //m_en_windows_logfont_name //offsets[22] 104 const unsigned __int64 //m_simulated //offsets[23] 105 const unsigned __int64 //m_reserved1 //offsets[24] 106 const unsigned __int64 //m_panose1 //offsets[25] 116 const unsigned __int64 //m_font_characteristics_hash //offsets[26] 136 const unsigned __int64 //m_reserved2 //offsets[27] 144 const unsigned __int64 //m_reserved3 //offsets[28] 152 const unsigned __int64 //m_reserved4 //offsets[29] 160 const unsigned __int64 //m_font_glyph_cache //offsets[30] 176 const unsigned __int64 //m_free_type_face //offsets[31] 184 const unsigned __int64 //m_reserved5 //offsets[32] 192 const unsigned __int64 //(this+1) */ ////const size_t sz = sizeof(*this); ////const char* p[33] = ////{ //// (const char*)this, //// (const char*)&m_runtime_serial_number, //// (const char*)&m_windows_logfont_weight, //// (const char*)&m_point_size, //// (const char*)&m_apple_font_weight_trait, //// (const char*)&m_font_weight, //// (const char*)&m_font_style, //// (const char*)&m_font_stretch, //// (const char*)&m_font_bUnderlined, //// (const char*)&m_font_bStrikethrough, //// (const char*)&m_logfont_charset, //// (const char*)&m_font_origin, //// (const char*)&m_font_type, //// (const char*)&m_locale_name, //// (const char*)&m_loc_postscript_name, //// (const char*)&m_en_postscript_name, //// (const char*)&m_loc_family_name, //// (const char*)&m_en_family_name, //// (const char*)&m_loc_face_name, //// (const char*)&m_en_face_name, //// (const char*)&m_loc_windows_logfont_name, //// (const char*)&m_en_windows_logfont_name, //// (const char*)&m_simulated, //// (const char*)&m_reserved1, //// (const char*)&m_panose1, //// (const char*)&m_font_characteristics_hash, //// (const char*)&m_reserved2, //// (const char*)&m_reserved3, //// (const char*)&m_reserved4, //// (const char*)&m_font_glyph_cache, //// (const char*)&m_free_type_face, //// (const char*)&m_reserved5, //// (const char*)(this+1), ////}; ////const size_t offsets[sizeof(p)/sizeof(p[0])] = { //// 0, //// (size_t)(p[1] - p[0]), //// (size_t)(p[2] - p[0]), //// (size_t)(p[3] - p[0]), //// (size_t)(p[4] - p[0]), //// (size_t)(p[5] - p[0]), //// (size_t)(p[6] - p[0]), //// (size_t)(p[7] - p[0]), //// (size_t)(p[8] - p[0]), //// (size_t)(p[9] - p[0]), //// (size_t)(p[10] - p[0]), //// (size_t)(p[11] - p[0]), //// (size_t)(p[12] - p[0]), //// (size_t)(p[13] - p[0]), //// (size_t)(p[14] - p[0]), //// (size_t)(p[15] - p[0]), //// (size_t)(p[16] - p[0]), //// (size_t)(p[17] - p[0]), //// (size_t)(p[18] - p[0]), //// (size_t)(p[19] - p[0]), //// (size_t)(p[20] - p[0]), //// (size_t)(p[21] - p[0]), //// (size_t)(p[22] - p[0]), //// (size_t)(p[23] - p[0]), //// (size_t)(p[24] - p[0]), //// (size_t)(p[25] - p[0]), //// (size_t)(p[26] - p[0]), //// (size_t)(p[27] - p[0]), //// (size_t)(p[28] - p[0]), //// (size_t)(p[29] - p[0]), //// (size_t)(p[30] - p[0]), //// (size_t)(p[31] - p[0]), //// (size_t)(p[32] - p[0]) ////}; ////int breakpointhere = 99; } ON_Font::ON_Font( ON_Font::FontType font_type, const ON_Font& src ) : m_runtime_serial_number( (ON_Font::FontType::ManagedFont == font_type) ? (++ON_Font::__runtime_serial_number_generator) // only managed fonts have non-zero m_runtime_serial_number : 0 ) , m_font_type(font_type) { // This is a private constructor used to create managed fonts. Internal_CopyFrom(src); } ON_Font::ON_Font(const ON_Font& src) : m_runtime_serial_number( (ON_PTR_SEMAPHORE2 == ((ON__UINT_PTR)&src)) ? 1 : 0) , m_font_type( (ON_PTR_SEMAPHORE2 == ((ON__UINT_PTR)&src)) ? ON_Font::FontType::ManagedFont : ON_Font::FontType::Unset) { // (ON_PTR_SEMAPHORE1 == ((ON__UINT_PTR)&src)) means we are constructing ON_Font::Unset. // (ON_PTR_SEMAPHORE2 == ((ON__UINT_PTR)&src)) means we are constructing ON_Font::Default. // Otherwise "this" should be an ordinary and unmanaged font. // See opennurbs_statics.cpp and ON_Font::CopyHelper() for more information. Internal_CopyFrom(src); } ON_Font& ON_Font::operator=(const ON_Font& src) { if (this != &src) { if (IsManagedFont() ) { // managed fonts can never be modified if ( false == ON_Font::EqualFontCharacteristics(*this, src) ) { ON_ERROR("Attempt to modify a managed font"); } } else { Internal_CopyFrom(src); } } return *this; } bool ON_Font::SetFontCharacteristics( const wchar_t* gdi_logfont_name, bool bBold, bool bItalic, bool bUnderlined, bool bStrikethrough ) { return SetFontCharacteristics( 0.0, gdi_logfont_name, bBold, bItalic, bUnderlined, bStrikethrough ); } bool ON_Font::SetFontCharacteristics( double point_size, const wchar_t * gdi_logfont_name, bool bBold, bool bItalic, bool bUnderlined, bool bStrikethrough ) { if (nullptr == gdi_logfont_name || 0 == gdi_logfont_name[0]) { gdi_logfont_name = ON_Font::Default.m_loc_windows_logfont_name; } return SetFontCharacteristics( point_size, gdi_logfont_name, (bBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal), (bItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright), ON_Font::Default.m_font_stretch, bUnderlined, bStrikethrough, ON_FontMetrics::DefaultLineFeedRatio, ON_Font::WindowsLogfontCharSetFromFaceName(gdi_logfont_name) ); } bool ON_Font::IsValidFaceName( const wchar_t* face_name ) { if ( nullptr == face_name || 0 == face_name[0] || ON_wString::Space == face_name[0]) return false; int i = 0; while (i < 32 && 0 != face_name[i]) { if (face_name[i] < ON_wString::Space ) return false; switch (face_name[i]) { case ';': case '"': case '\'': case '`': case '=': case '#': // lots more return false; //case '@': - There are valid fonts like @Gulim with '@' in the name. // case '.' - Many apple font names begin with a period // case '-' - A hyphen is common in PostScript names // case ' ' - A space is common in many names default: break; } i++; } if (0 != face_name[i]) return false; return true; } ON_Font::Weight ON_Font::FontWeightFromUnsigned( unsigned int unsigned_font_weight ) { switch (unsigned_font_weight) { ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Thin); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Ultralight); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Light); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Normal); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Medium); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Semibold); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Bold); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Ultrabold); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Heavy); default: break; } ON_ERROR("unsigned_font_weight is not valid"); return ON_Font::Weight::Unset; } bool ON_Font::EqualWeightStretchStyle( const ON_Font* lhs, const ON_Font* rhs, bool bUnsetIsEqual ) { return ON_Font::EqualWeight(lhs, rhs, bUnsetIsEqual) && ON_Font::EqualStretch(lhs, rhs, bUnsetIsEqual) && ON_Font::EqualStyle(lhs, rhs, bUnsetIsEqual); } bool ON_Font::EqualWeight( const ON_Font* lhs, const ON_Font* rhs, bool bUnsetIsEqual ) { if (nullptr == lhs || nullptr == rhs) return false; if (lhs->m_font_weight != rhs->m_font_weight) { if (false == bUnsetIsEqual) return false; if (ON_Font::Weight::Unset != lhs->m_font_weight && ON_Font::Weight::Unset != rhs->m_font_weight) return false; } return true; } bool ON_Font::EqualStretch( const ON_Font* lhs, const ON_Font* rhs, bool bUnsetIsEqual ) { if (nullptr == lhs || nullptr == rhs) return false; if (lhs->m_font_stretch != rhs->m_font_stretch) { if (false == bUnsetIsEqual) return false; if (ON_Font::Stretch::Unset != lhs->m_font_stretch && ON_Font::Stretch::Unset != rhs->m_font_stretch) return false; } return true; } bool ON_Font::EqualStyle( const ON_Font* lhs, const ON_Font* rhs, bool bUnsetIsEqual ) { if (nullptr == lhs || nullptr == rhs) return false; if (lhs->m_font_style != rhs->m_font_style) { if (false == bUnsetIsEqual) return false; if (ON_Font::Style::Unset != lhs->m_font_style && ON_Font::Style::Unset != rhs->m_font_style) return false; } return true; } int ON_Font::CompareWeight( ON_Font::Weight weight_a, ON_Font::Weight weight_b ) { unsigned int a = static_cast(weight_a); unsigned int b = static_cast(weight_b); if ( a < b ) return -1; if ( a < b ) return 1; return 0; } ON_Font::Style ON_Font::FontStyleFromUnsigned( unsigned int unsigned_font_style ) { switch (unsigned_font_style) { ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Upright); ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Italic); ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Oblique); default: break; } ON_ERROR("unsigned_font_style is not valid"); return ON_Font::Style::Upright; } bool ON_Font::IsBoldWeight( ON_Font::Weight weight ) { return (static_cast(weight) >= static_cast(ON_Font::Weight::Semibold)); } const wchar_t* ON_Font::WeightToWideString( ON_Font::Weight font_weight ) { const wchar_t* s; switch (font_weight) { case ON_Font::Weight::Unset: s = L"Unsetweight"; break; case ON_Font::Weight::Thin: s = L"Thin"; break; case ON_Font::Weight::Ultralight: s = L"Ultralight"; break; case ON_Font::Weight::Light: s = L"Light"; break; case ON_Font::Weight::Normal: s = L"Normal"; break; case ON_Font::Weight::Medium: s = L"Medium"; break; case ON_Font::Weight::Semibold: s = L"Semibold"; break; case ON_Font::Weight::Bold: s = L"Bold"; break; case ON_Font::Weight::Ultrabold: s = L"Ultrabold"; break; case ON_Font::Weight::Heavy: s = L"Heavy"; break; default: s = L""; break; } return s; } const wchar_t* ON_Font::StretchToWideString( ON_Font::Stretch font_stretch ) { const wchar_t* s; switch (font_stretch) { case ON_Font::Stretch::Unset: s = L"Unsetstretch"; break; case ON_Font::Stretch::Ultracondensed: s = L"Ultracondensed"; break; case ON_Font::Stretch::Extracondensed: s = L"Extracondensed"; break; case ON_Font::Stretch::Condensed: s = L"Condensed"; break; case ON_Font::Stretch::Semicondensed: s = L"Semicondensed"; break; case ON_Font::Stretch::Medium: s = L"Medium"; break; case ON_Font::Stretch::Semiexpanded: s = L"Semiexpanded"; break; case ON_Font::Stretch::Expanded: s = L"Expanded"; break; case ON_Font::Stretch::Extraexpanded: s = L"Extraexpanded"; break; case ON_Font::Stretch::Ultraexpanded: s = L"Ultraexpanded"; break; default: s = L""; break; } return s; } const wchar_t* ON_Font::StyleToWideString( ON_Font::Style font_style ) { const wchar_t* s; switch (font_style) { case ON_Font::Style::Unset: s = L"Unsetstyle"; break; case ON_Font::Style::Upright: s = L"Upright"; break; case ON_Font::Style::Italic: s = L"Italic"; break; case ON_Font::Style::Oblique: s = L"Oblique"; break; default: s = L""; break; } return s; } ON_Font::Stretch ON_Font::FontStretchFromUnsigned( unsigned int unsigned_font_stretch ) { switch (unsigned_font_stretch) { ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Unset); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Ultracondensed); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Extracondensed); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Condensed); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Semicondensed); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Medium); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Semiexpanded); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Expanded); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Extraexpanded); ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Ultraexpanded); default: break; } ON_ERROR("unsigned_font_stretch is not valid"); return ON_Font::Stretch::Unset; } unsigned int ON_Font::FontCharacteristicsAsUnsigned() const { return ON_Font::Internal_FontCharacteristicsAsUnsigned( FontWeight(), FontStyle(), FontStretch(), m_font_bUnderlined, m_font_bStrikethrough ); } unsigned int ON_Font::Internal_FontCharacteristicsAsUnsigned( ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough ) { unsigned int a[][2] = { { 2U, 1U }, // insures 0 is not a valid FontCharacteristicsAsUnsigned() value. { 10U, static_cast(font_weight) }, { 4U, static_cast(font_style) }, { 10U, static_cast(font_stretch) }, // The static_cast in b ? 1U : 0U is to work around a CLang compiler bug. { 2U, static_cast(bUnderlined ? 1U : 0U) }, { 2U, static_cast(bStrikethrough ? 1U : 0U) } // insert new information below this line. }; const int count = (int)(sizeof(a) / sizeof(a[0])); int i = count-1; unsigned int u = a[i][1] % a[i][0]; for (i--; i >= 0; i--) { u = (u * a[i][0]) + (a[i][1] % a[i][0]); } return u; } void ON_Font::Internal_GetFontCharacteristicsFromUnsigned( unsigned int font_characteristics_as_unsigned, ON_Font::Weight& font_weight, ON_Font::Stretch& font_stretch, ON_Font::Style& font_style, bool& bUnderlined, bool& bStrikethrough ) { unsigned int u = font_characteristics_as_unsigned; const unsigned int u_one = u % 2; u /= 2; const unsigned int u_font_weight = u % 10; u /= 10; const unsigned int u_font_style = u % 4; u /= 4; const unsigned int u_font_stretch = u % 10; u /= 10; const unsigned int u_bUnderlined = u % 2; u /= 2; const unsigned int u_bStrikethrough = u % 2; u /= 2; // extract new information below this line font_weight = (1U == u_one && u_font_weight > 0) ? ON_Font::FontWeightFromUnsigned(u_font_weight) : ON_Font::Default.FontWeight(); font_style = (1U == u_one) ? ON_Font::FontStyleFromUnsigned(u_font_style) : ON_Font::Default.FontStyle(); font_stretch = (1U == u_one) ? ON_Font::FontStretchFromUnsigned(u_font_stretch) : ON_Font::Default.FontStretch(); bUnderlined = (1U == u_one) ? (1 == u_bUnderlined) : ON_Font::Default.IsUnderlined(); bStrikethrough = (1U == u_one) ? (1 == u_bStrikethrough) : ON_Font::Default.IsStrikethrough(); } bool ON_Font::Internal_SetFontCharacteristicsFromUnsigned( unsigned int font_characteristics_as_unsigned ) { ON_Font::Weight font_weight = ON_Font::Weight::Normal; ON_Font::Stretch font_stretch = ON_Font::Stretch::Medium; ON_Font::Style font_style = ON_Font::Style::Upright; bool bUnderlined = false; bool bStrikethrough = false; ON_Font::Internal_GetFontCharacteristicsFromUnsigned( font_characteristics_as_unsigned, font_weight, font_stretch, font_style, bUnderlined, bStrikethrough ); const ON_wString windows_logfont_name = WindowsLogfontName(); bool rc = SetFontCharacteristics( windows_logfont_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough ); return rc; } unsigned int ON_Font::CRC32( bool bIgnoreNameOrdinalCase ) const { unsigned int u = FontCharacteristicsAsUnsigned(); const ON_wString windows_logfont_name = WindowsLogfontName(); const ON_wString windows_logfont_name_crc = bIgnoreNameOrdinalCase ? windows_logfont_name.MapStringOrdinal(ON_StringMapOrdinalType::MinimumOrdinal) : windows_logfont_name; // SHould probably include PostScript name as well. ON__UINT32 hash = ON_CRC32(0, sizeof(u), &u ); hash = ON_CRC32(hash,windows_logfont_name_crc.Length()*sizeof(wchar_t),static_cast(windows_logfont_name_crc)); #if defined(ON_RUNTIME_WIN) if ( m_point_size > 0.0 ) hash = ON_CRC32(hash, sizeof(m_point_size), &m_point_size); #endif return hash; } bool ON_Font::SetFontCharacteristics( const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough ) { return SetFontCharacteristics( 0.0, gdi_logfont_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough ); } bool ON_Font::SetFontCharacteristics( double point_size, const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough ) { const unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(gdi_logfont_name); double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio; return SetFontCharacteristics( point_size, gdi_logfont_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough, linefeed_ratio, logfont_charset ); } bool ON_Font::SetFontCharacteristics( const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough, double linefeed_ratio, unsigned int logfont_charset ) { return SetFontCharacteristics( 0.0, gdi_logfont_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough, linefeed_ratio, logfont_charset ); } bool ON_Font::SetFontCharacteristics( double point_size, const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough, double linefeed_ratio, unsigned int logfont_charset ) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; // Save face_name - it often points to this->m_face_name // and *this = ON_Font::Unset will set this->m_face_name to the empty string. ON_wString local_face_name(gdi_logfont_name); local_face_name.TrimLeftAndRight(); gdi_logfont_name = static_cast(local_face_name); *this = ON_Font::Unset; if (false == ON_Font::IsValidFaceName(gdi_logfont_name)) return false; if (logfont_charset >= 256) return false; ON_Font new_characteristics(ON_Font::Unset); new_characteristics.m_loc_windows_logfont_name = gdi_logfont_name; new_characteristics.m_en_windows_logfont_name = new_characteristics.m_loc_windows_logfont_name; new_characteristics.m_font_weight = ON_Font::FontWeightFromUnsigned(static_cast(font_weight)); new_characteristics.m_point_size = (point_size > 0.0 && point_size < 2147483640.0) ? point_size : 0.0; new_characteristics.m_windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(new_characteristics.m_font_weight); new_characteristics.m_apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(new_characteristics.m_font_weight); new_characteristics.m_font_style = font_style; new_characteristics.m_font_stretch = font_stretch; new_characteristics.m_font_bUnderlined = bUnderlined ? true : false; new_characteristics.m_font_bStrikethrough = bStrikethrough; new_characteristics.m_loc_family_name = ON_Font::FamilyNameFromDirtyName(gdi_logfont_name); new_characteristics.m_en_family_name = new_characteristics.m_loc_family_name; if ( ON_wString::EqualOrdinal(L"CityBlueprint", -1, gdi_logfont_name, -1, true) || ON_wString::EqualOrdinal(L"CountryBlueprint", -1, gdi_logfont_name, -1, true) ) { // These two fonts were didstrubuted with ACAD for decades. // Thy have several errors in their defintion including being // marked as SYMBOL_CHARSET rather than ANSI_CHARSET. logfont_charset = ON_Font::WindowsConstants::logfont_symbol_charset; } else if (ON_Font::logfont_symbol_charset == logfont_charset) { // verify this is correct. logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(new_characteristics.WindowsLogfontName()); } else { logfont_charset = ON_Font::WindowsConstants::logfont_default_charset; } new_characteristics.m_logfont_charset = (ON_Font::WindowsConstants::logfont_symbol_charset == (unsigned char)logfont_charset) ? ON_Font::WindowsConstants::logfont_symbol_charset : ON_Font::WindowsConstants::logfont_default_charset; if ( // 3 fast checks to avoid time consuming hash calculation false == ON_wString::EqualOrdinal(WindowsLogfontName(),new_characteristics.WindowsLogfontName(),false) || false == ON_wString::EqualOrdinal(PostScriptName(),new_characteristics.PostScriptName(),false) || false == ON_wString::EqualOrdinal(FamilyName(),new_characteristics.FamilyName(),false) || false == ON_wString::EqualOrdinal(FaceName(),new_characteristics.FaceName(),false) || m_font_weight != new_characteristics.m_font_weight || m_font_stretch != new_characteristics.m_font_stretch || m_font_style != new_characteristics.m_font_style || FontCharacteristicsHash() != new_characteristics.FontCharacteristicsHash() ) { Internal_CopyFrom(new_characteristics); if (0 == m_runtime_serial_number) { // destination font is not managed m_font_glyph_cache = nullptr; } if ( ON_Font::Stretch::Medium == m_font_stretch && (ON_Font::Weight::Normal == m_font_weight || ON_Font::Weight::Bold == m_font_weight) && (ON_Font::Style::Upright == m_font_style || ON_Font::Style::Italic == m_font_style) ) { const ON_wString logfontName = WindowsLogfontName(); ON_wString postscript_suffix; bool bSetMakePostScriptName = false; if ( ON_wString::EqualOrdinal(L"Arial", logfontName, true) ) { // Because Arial is common and its PostScript name has "MT" appended, // the post script name is set here so the font will readily work // in situation that key off the PostScript name. bSetMakePostScriptName = true; postscript_suffix = L"MT"; } else if (ON_wString::EqualOrdinal(L"Segoe Print", logfontName, true)) { bSetMakePostScriptName = true; } else if (ON_wString::EqualOrdinal(L"Segoe Script", logfontName, true)) { bSetMakePostScriptName = true; } else if (ON_wString::EqualOrdinal(L"Segoe UI", logfontName, true)) { bSetMakePostScriptName = true; } else if (ON_wString::EqualOrdinal(L"Microsoft YaHei", logfontName, true)) { bSetMakePostScriptName = true; } else if (ON_wString::EqualOrdinal(L"Microsoft YaHei UI", logfontName, true)) { bSetMakePostScriptName = true; } for (;;) { if (false == bSetMakePostScriptName) break; // The fonts listed above all remove spaces from m_face_name to get // the beginning of the postscript name. ON_wString postscript_name = logfontName; postscript_name.TrimLeftAndRight(); postscript_name.Remove(ON_wString::Space); if (postscript_name.IsEmpty()) break; // add -