// // 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" 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 m_ascent_of_I; } 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)AscentOfI(); return (text_height > 0.0 && y > 0.0) ? (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::HeightsAreValid() const { if (m_ascent <= m_descent) return false; if (m_line_space < m_ascent - m_descent) return false; if (m_ascent_of_I > m_ascent) return false; if (m_UPM <= 0) return false; return true; } 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; } void ON_FontMetrics::SetAscentOfI( int ascent_of_I ) { m_ascent_of_I = (ascent_of_I > 0 && ascent_of_I < -ON_UNSET_INT_INDEX) ? ascent_of_I : 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_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); if (font_metrics.m_line_space == Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,font_metrics.m_ascent_of_I)) scaled_font_metrics.m_line_space = Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,scaled_font_metrics.m_ascent_of_I); else scaled_font_metrics.m_line_space = Internal_ScaleInt(scale, scaled_font_metrics.m_line_space); scaled_font_metrics.m_ascent_of_I = Internal_ScaleInt(scale, scaled_font_metrics.m_ascent_of_I); 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; } // ON_GlyphMap::GlyphPool needs to be allocated before ON_ManagedFonts::List. // This is so the pool is still around when the ON_ManagedFonts::List // is being destroyed. ON_Internal_FontGlyphPool ON_Internal_FontGlyphPool::theGlyphItemPool; ON_ManagedFonts ON_ManagedFonts::List = ON_ManagedFonts(); ON_ManagedFonts::~ON_ManagedFonts() { ON_SimpleArray managed_fonts(m_managed_fonts_by_serial_number); m_managed_fonts.Destroy(); m_managed_fonts_by_serial_number.Destroy(); // 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]); // 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(); if (managed_font->m_runtime_serial_number >= 2) delete managed_font; } } class ON_FontGlyphCache { public: // 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; ON_FontMetrics m_font_unit_metrics; ON_FontMetrics m_normalized_metrics; // Array of glyphs with sizes and display info std::shared_ptr m_glyphmap; }; static int CompareManagedFontCharacteristics( const void* a, const void* b ) { const ON_Font* a_font = *((const ON_Font*const*)a); const ON_Font* b_font = *((const ON_Font*const*)b); return ON_Font::CompareFontCharacteristics(*a_font,*b_font); } const ON_Font* BinarySearchForManagedFontCharacteristics( const ON_Font& key, const ON_Font*const* base, size_t nel ) { if (nel > 0 && nullptr != base ) { size_t i; const ON_Font* font; int c; while ( nel > 0 ) { i = nel/2; font = base[i]; c = ON_Font::CompareFontCharacteristics(key,*font); if ( c < 0 ) { nel = i; } else if ( c > 0 ) { i++; base += i; nel -= i; } else { return font; } } } return nullptr; } static int CompareManagedFontSerialNumber( const void* a, const void* b ) { const unsigned int a_sn = (*((const ON_Font*const*)a))->RuntimeSerialNumber(); const unsigned int b_sn = (*((const ON_Font*const*)b))->RuntimeSerialNumber(); if (a_sn < b_sn) return -1; if (a_sn > b_sn) return 1; return 0; } const ON_Font* 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 ( managed_font_serial_number == ON_Font::Default.RuntimeSerialNumber() ) return &ON_Font::Default; const ON_Font* const * managed_fonts = m_managed_fonts_by_serial_number.Array(); const unsigned int font_count = m_managed_fonts_by_serial_number.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]; } unsigned int& sorted_count = m_sorted_by_serial_number_count; if (font_count > sorted_count + 4) { ON_qsort((void*)managed_fonts, font_count, sizeof(managed_fonts[0]), CompareManagedFontSerialNumber ); sorted_count = font_count; } else { // most recently added font will be in the unsorted section. for (unsigned int i = sorted_count; i < font_count; i++) { if (managed_font_serial_number == managed_fonts[i]->RuntimeSerialNumber()) return managed_fonts[i]; } } return (sorted_count > 0) ? BinarySearchForManagedFontSerialNumber(managed_font_serial_number, managed_fonts, sorted_count) : nullptr; } const ON_Font* ON_ManagedFonts::GetFromFontCharacteristics( const ON_Font& font_characteristics, bool bCreateIfNotFound ) { if (0 == m_managed_fonts.UnsignedCount()) { // Put ON_Font::Default as the first entry in this list. Internal_AddManagedFont(&ON_Font::Default); } const ON_Font* const * managed_fonts = m_managed_fonts.Array(); const unsigned int font_count = m_managed_fonts.UnsignedCount(); unsigned int& sorted_count = m_sorted_count; if (font_count > sorted_count + 4) { ON_qsort((void*)managed_fonts, font_count, sizeof(managed_fonts[0]), CompareManagedFontCharacteristics ); sorted_count = font_count; } else { // most recently added font will be in the unsorted section. for (unsigned int i = sorted_count; i < font_count; i++) { if (0 == ON_Font::CompareFontCharacteristics( font_characteristics, *managed_fonts[i] ) ) return managed_fonts[i]; } } const ON_Font* managed_font = BinarySearchForManagedFontCharacteristics(font_characteristics,m_managed_fonts.Array(),m_sorted_count); if (nullptr == managed_font && bCreateIfNotFound) { if (font_characteristics.FontDescription().IsEmpty() && font_characteristics.AppleFontName().IsEmpty() && 0 == font_characteristics.FontFaceName()[0] ) { managed_font = &ON_Font::Default; } else { // 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); managed_font = Internal_AddManagedFont(new ON_Font(2, font_characteristics)); } } return managed_font; } const ON_Font* ON_ManagedFonts::GetFromAppleFontName( const wchar_t* apple_font_name, bool bCreateIfNotFound ) { if (0 == m_managed_fonts.UnsignedCount()) { // Put ON_Font::Default as the first entry in this list. Internal_AddManagedFont(&ON_Font::Default); } const ON_Font* const * managed_fonts = m_managed_fonts.Array(); const unsigned int font_count = m_managed_fonts.UnsignedCount(); for (unsigned int i = 0; i < font_count; i++) { if (0 == ON_StringCompareOrdinalWideChar(apple_font_name, -1, managed_fonts[i]->m_apple_font_name, -1, true) ) return managed_fonts[i]; } const ON_Font* managed_font = nullptr; if (bCreateIfNotFound) { ON_Font font_characteristics; font_characteristics.SetFromAppleFontName(apple_font_name); // The managed font constructor does not copy user data, m_font_index, m_font_id, m_gonna_change_font_cache managed_font = Internal_AddManagedFont(new ON_Font(2,font_characteristics)); } return managed_font; } static void Internal_AddManagedFontCleanString( 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 ) { // All memory allocated for managed fonts is permanent app workspace memory. ON_MemoryAllocationTracking disable_tracking(false); ///////////////////// // // Put the cached glyph information here so we only have one set for each font // ON_FontGlyphCache* font_cache = new ON_FontGlyphCache(); font_cache->m_glyphmap = std::make_shared(); managed_font->m_font_glyph_cache = std::shared_ptr(font_cache); if (true) { ON_FontMetrics font_unit_metrics; ON_ManagedFonts::GetFontMetrics( managed_font, font_unit_metrics ); const int ascent = font_unit_metrics.Ascent(); const int descent = font_unit_metrics.Descent(); if (font_unit_metrics.UPM() <= 0) { const int line_space = font_unit_metrics.LineSpace(); if (ascent > descent) font_unit_metrics.SetHeights(ascent, descent, ascent - descent, line_space); } const int UPM = font_unit_metrics.UPM(); if (UPM > 0 ) { int ascent_of_I = font_unit_metrics.AscentOfI(); int line_space = font_unit_metrics.LineSpace(); if (ascent_of_I <= 0) { // Get 'I' glyph height. // Do not use glyph cache or per glyph substuted fonts here. // This call is used only to set the value of // font_cache->m_unnormalized_metrics.m_height_of_I // and that value needs to come from the font. ON_TextBox unnormalized_glyph_box = ON_TextBox::Unset; if (0 != ON_ManagedFonts::GetGlyphMetrics(managed_font, ON_Font::Constants::MetricsGlyphCodePoint, unnormalized_glyph_box)) { if (unnormalized_glyph_box.IsSet() && unnormalized_glyph_box.m_bbmax.j > 0) ascent_of_I = unnormalized_glyph_box.m_bbmax.j; } if (ascent_of_I <= 0) { if (ascent_of_I <= 0 && line_space > ascent - descent) { ascent_of_I = (int)ceil(line_space / ON_FontMetrics::DefaultLineFeedRatio); } if (ascent_of_I <= 0 && &ON_Font::Default != managed_font) { const ON_FontMetrics default_font_unit_metrics = ON_Font::Default.FontUnitFontMetrics(); if (default_font_unit_metrics.AscentOfI() > 0 && default_font_unit_metrics.UPM() > 0) { const double scale = ((double)UPM) / ((double)default_font_unit_metrics.UPM()); ascent_of_I = (int)ceil(scale*default_font_unit_metrics.AscentOfI()); } } if (ascent > 0 && ascent_of_I > ascent) ascent_of_I = ascent; } } if (line_space <= 0 && ascent_of_I > 0 ) line_space = (int)ceil(ON_FontMetrics::DefaultLineFeedRatio*ascent_of_I); font_unit_metrics.SetHeights(ascent, descent, UPM, line_space); font_unit_metrics.SetAscentOfI(ascent_of_I); font_cache->m_font_unit_metrics = font_unit_metrics; font_cache->m_normalized_to_font_unit_scale = ((double)UPM) / ((double)ON_Font::Constants::AnnotationFontCellHeight); font_cache->m_font_unit_to_normalized_scale = ((double)ON_Font::Constants::AnnotationFontCellHeight) / ((double)UPM); font_cache->m_normalized_metrics = (font_cache->m_font_unit_to_normalized_scale > 0.0 && 1.0 != font_cache->m_font_unit_to_normalized_scale) ? ON_FontMetrics::Scale(font_cache->m_font_unit_metrics, font_cache->m_font_unit_to_normalized_scale) : font_cache->m_font_unit_metrics; } } if ( false == font_cache->m_font_unit_metrics.HeightsAreValid() ) { ON_ERROR("Unable to get useful font metrics."); // continue and save what we have } const unsigned int font_count0 = m_managed_fonts.UnsignedCount(); if (font_count0 > 0) { if ( font_count0 == m_sorted_by_serial_number_count && m_managed_fonts_by_serial_number[font_count0 - 1]->RuntimeSerialNumber() < managed_font->RuntimeSerialNumber() ) { m_sorted_by_serial_number_count++; } } m_managed_fonts.Append(managed_font); m_managed_fonts_by_serial_number.Append(managed_font); Internal_AddManagedFontCleanString(managed_font->m_apple_font_name); Internal_AddManagedFontCleanString(managed_font->m_font_description); return managed_font; } unsigned int ON_ManagedFonts::GetList( ON_SimpleArray< const ON_Font* >& managed_fonts ) { const unsigned int font_count = m_managed_fonts_by_serial_number.UnsignedCount(); if (m_sorted_by_serial_number_count < font_count) { ON_qsort((void*)m_managed_fonts_by_serial_number.Array(), font_count, sizeof(managed_fonts[0]), CompareManagedFontSerialNumber); m_sorted_by_serial_number_count = font_count; } managed_fonts = m_managed_fonts_by_serial_number; return managed_fonts.UnsignedCount(); } #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 // ////////////////////////////////////////////////////////////////////////// bool ON_Font::ModificationPermitted( const char* function_name, const char* file_name, int line_number ) const { if ( IsManagedFont() ) { // ON_Font::Default and managed fonts can never be modified if ( this == &ON_Font::Default ) ON_ErrorEx(file_name, line_number, function_name, "ON_Font::Default cannot be modified."); else ON_ErrorEx(file_name, line_number, function_name, "Managed fonts cannot be modified."); return false; } // 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; } #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; } #if defined (ON_RUNTIME_APPLE) const ON_Font* font = ON_ManagedFonts::List.GetFromAppleFontName(font_characteristics.m_apple_font_name,bCreateIfNotFound); if (nullptr != font) return font; #endif 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 ) { return ON_ManagedFonts::List.GetList(managed_fonts); } bool ON_Font::IsManagedFont() const { return ( 0 != m_runtime_serial_number ); } 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 ) { unsigned int logfont_charset = (nullptr != face_name && 0 != face_name[0]) ? static_cast(ON_Font::WindowsLogfontCharSetFromFaceName(face_name)) : static_cast(ON_Font::WindowsConstants::logfont_default_charset); 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 wchar_t* apple_font_name ) { ON_Font font_characteristics; if ( false == font_characteristics.SetFromAppleFontName(apple_font_name) ) return nullptr; return font_characteristics.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::Default.m_font_weight; 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::CopyHelper(const ON_Font& src) { 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; memset(m_face_name, 0, sizeof(m_face_name)); for (int i = 0; i < ON_Font::face_name_capacity && 0 != src.m_face_name[i]; i++) { m_face_name[i] = src.m_face_name[i]; } m_font_description = src.m_font_description; m_apple_font_name = src.m_apple_font_name; if (0 == m_runtime_serial_number) { // destination font is not managed m_font_glyph_cache = src.m_font_glyph_cache; } m_point_size = src.m_point_size; m_font_characteristics_hash = ON_SHA1_Hash::ZeroDigest; } ON_Font::ON_Font() { memset(m_face_name, 0, sizeof(m_face_name)); } ON_Font::ON_Font( unsigned char managed_status, const ON_Font& src ) : m_runtime_serial_number((1 == managed_status || 2 == managed_status) ? (++ON_Font::__runtime_serial_number_generator) : 0) { CopyHelper(src); } ON_Font::ON_Font(const ON_Font& src) : m_runtime_serial_number(0) { memset(m_face_name, 0, sizeof(m_face_name)); CopyHelper(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 { CopyHelper(src); } } return *this; } bool ON_Font::SetFontCharacteristics( const wchar_t* face_name, bool bBold, bool bItalic, bool bUnderlined, bool bStrikethrough ) { return SetFontCharacteristics( 0.0, face_name, bBold, bItalic, bUnderlined, bStrikethrough ); } bool ON_Font::SetFontCharacteristics( double point_size, const wchar_t * face_name, bool bBold, bool bItalic, bool bUnderlined, bool bStrikethrough ) { if (nullptr == face_name || 0 == face_name[0] ) face_name = ON_Font::Default.m_face_name; return SetFontCharacteristics( point_size, face_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(face_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 < ON_Font::face_name_capacity && 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. 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; } 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; } 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; } bool ON_Font::Internal_SetFontCharacteristicsFromUnsigned( unsigned int font_characteristics_as_unsigned ) { 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 ON_Font::Weight font_weight = (1U == u_one && u_font_weight > 0) ? ON_Font::FontWeightFromUnsigned(u_font_weight) : ON_Font::Default.FontWeight(); ON_Font::Style font_style = (1U == u_one) ? ON_Font::FontStyleFromUnsigned(u_font_style) : ON_Font::Default.FontStyle(); ON_Font::Stretch font_stretch = (1U == u_one) ? ON_Font::FontStretchFromUnsigned(u_font_stretch) : ON_Font::Default.FontStretch(); bool bUnderlined = (1U == u_one) ? (1 == u_bUnderlined) : ON_Font::Default.IsUnderlined(); bool bStrikethrough = (1U == u_one) ? (1 == u_bStrikethrough) : ON_Font::Default.IsStrikethrough(); return SetFontCharacteristics( m_face_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough ); } unsigned int ON_Font::CRC32( bool bIgnoreFaceNameOrdinalCase ) const { unsigned int u = FontCharacteristicsAsUnsigned(); wchar_t mapped_face_name[ON_Font::face_name_capacity+1]; int element_count = 0; while (element_count < ON_Font::face_name_capacity && 0 != m_face_name[element_count] ) element_count++; const wchar_t* face_name = m_face_name; if (bIgnoreFaceNameOrdinalCase) { ON_wString::MapStringOrdinal( ON_StringMapOrdinalType::MinimumOrdinal, m_face_name, element_count, mapped_face_name, ON_Font::face_name_capacity ); face_name = mapped_face_name; } ON__UINT32 hash = ON_CRC32(0, sizeof(u), &u ); hash = ON_CRC32(hash,element_count*sizeof(face_name[0]),face_name); #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* face_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, bool bUnderlined, bool bStrikethrough ) { return SetFontCharacteristics( 0.0, face_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough ); } bool ON_Font::SetFontCharacteristics( 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 ) { const unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(face_name); double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio; return SetFontCharacteristics( point_size, face_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough, linefeed_ratio, logfont_charset ); } bool ON_Font::SetFontCharacteristics( 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 SetFontCharacteristics( 0.0, face_name, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough, linefeed_ratio, logfont_charset ); } bool ON_Font::SetFontCharacteristics( 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 ) { if (this == &ON_Font::Default) return false; if (false == ON_Font::IsValidFaceName(face_name)) return false; if (logfont_charset >= 256) return false; ON_Font new_characteristics; for (int i = 0; i < ON_Font::face_name_capacity && 0 != face_name[i]; i++) new_characteristics.m_face_name[i] = face_name[i]; 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; if (ON_Font::logfont_symbol_charset == logfont_charset) { // verify this is correct. logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(new_characteristics.m_face_name); } new_characteristics.m_logfont_charset = (unsigned char)logfont_charset; if ( // 3 fast checks to avoid time consuming hash calculation 0 != memcmp(m_face_name,new_characteristics.m_face_name,sizeof(m_face_name)) || m_font_weight != new_characteristics.m_font_weight || m_font_style != new_characteristics.m_font_style || FontCharacteristicsHash() != new_characteristics.FontCharacteristicsHash() ) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; CopyHelper(new_characteristics); if (0 == m_runtime_serial_number) { // destination font is not managed m_font_glyph_cache = nullptr; } Internal_SetFontDescription(); } return true; } #if defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) bool ON_Font::SetFromAppleFont (NSFont* apple_font) { const char* sAppleFontName = apple_font.fontName.UTF8String; const ON_wString apple_font_name = sAppleFontName; bool rc = SetFromAppleFontName(apple_font_name); if (rc) { const ON_wString saved_apple_font_name = m_apple_font_name; const ON_wString saved_font_description = m_font_description; // Set face name -- used if this font needs sustution on another computer // https://mcneel.myjetbrains.com/youtrack/issue/RH-37074 const char* sAppleFontFamilyName = apple_font.familyName.UTF8String; const ON_wString face_name = sAppleFontFamilyName; // UTF-8 to wchar_t conversion SetFontFaceName(face_name); // Set weight - used if this font needs sustution on another computer // https://mcneel.myjetbrains.com/youtrack/issue/RH-37075 NSFontDescriptor* fd = apple_font.fontDescriptor; NSDictionary* traits = [fd objectForKey: NSFontTraitsAttribute]; NSNumber* weightValue = [traits objectForKey: NSFontWeightTrait]; if (weightValue) { const double apple_font_weight_trait = weightValue.doubleValue; SetAppleFontWeightTrait(apple_font_weight_trait); } // Set style - used if this font needs sustution on another computer const ON_Font::Style font_style = ( 0 != (fd.symbolicTraits & NSFontItalicTrait) ) ? ON_Font::Style::Italic : ON_Font::Style::Upright; SetFontStyle(font_style); // Restore m_apple_font_name and m_font_description to the values set in SetFromAppleFontName(). // These values may get modified by the SetFontFaceName() or SetFontStyle() calls. m_apple_font_name = saved_apple_font_name; m_font_description = saved_font_description; } return rc; } NSFont* ON_Font::AppleFont() const { NSFont* userFont = nullptr; double pointSize = (double)FontMetrics().LineSpace() / 96.0 * 72.0; ON_String font_name = m_apple_font_name; // convert to UTF8 NSString* fullFontName = [NSString stringWithUTF8String: font_name]; // first try full font name userFont = [NSFont fontWithName: fullFontName size: pointSize]; if (userFont) return userFont; // Try getting a NSFont by using NSFontManager NSFontTraitMask traits = 0; if (IsItalic()) traits |= NSItalicFontMask; if (FontStretch() <= ON_Font::Stretch::Condensed) traits |= NSCondensedFontMask; if (FontStretch() >= ON_Font::Stretch::Expanded) traits |= NSExpandedFontMask; // AppleWeightOfFontFromWeight returns a value from 0 to 9. The fontWithFamily:traits:weight:size: method needs weights in the range 0 to 15. int onWeight = ON_Font::AppleWeightOfFontFromWeight (FontWeight()); int weight = fmax(fmin(onWeight * 1.5, 15.0), 0.0); ON_String family_name = FontFaceName(); // convert to UTF8 NSString* fontFamilyName = [NSString stringWithUTF8String: family_name]; // font family name userFont = [[NSFontManager sharedFontManager] fontWithFamily: fontFamilyName traits: traits weight: weight size: pointSize]; if (userFont) return userFont; // Try using just FontFaceName() userFont = [NSFont fontWithName: fontFamilyName size: pointSize]; if (userFont) return userFont; // Cannot find an equivalent font. Just use a system font. userFont = [NSFont userFontOfSize: pointSize]; if (userFont) return userFont; userFont = [NSFont systemFontOfSize: pointSize]; return userFont; } #endif bool ON_Font::SetFromAppleFontName( const wchar_t* apple_font_name ) { ON_wString local_apple_font_name(apple_font_name); local_apple_font_name.TrimLeftAndRight(); apple_font_name = static_cast(local_apple_font_name); const bool rc = SetFromFontDescription(apple_font_name,apple_font_name); // unconditionally set m_font_description and m_apple_font_name m_font_description = local_apple_font_name; m_apple_font_name = local_apple_font_name; return rc; } const wchar_t* ON_Font::AppleFontNameAsPointer() const { return m_apple_font_name; } const ON_wString& ON_Font::AppleFontName() const { return m_apple_font_name; } static bool IsAtoZ( const wchar_t* s ) { return (nullptr != s && ((s[0] >= 'A' && s[0] <= 'Z') || (s[0] >= 'a' && s[0] <= 'z'))); } static const unsigned int ParseToken( const wchar_t*& s, size_t count, const wchar_t*const* token, const unsigned int* token_rc, unsigned int token_not_found_rc ) { if (IsAtoZ(s)) { for (size_t i = 0; i < count; i++) { const int len = (int)ON_wString::Length(token[i]); if (ON_wString::EqualOrdinal(token[i], len, s, len, true)) { s += len; return token_rc[i]; } } } return token_not_found_rc; } static bool SkipSeparator(bool bSkipSpace, const wchar_t*& s) { if (nullptr == s) return false; bool rc = false; switch (s[0]) { case '-': case '_': case ',': case ';': case '.': rc = true; break; default: if (bSkipSpace && ON_wString::Space == s[0]) rc = true; break; } if (rc) s++; return rc; } static bool ParseStretch( const wchar_t*& s, ON_Font::Stretch& font_stretch ) { if (false == IsAtoZ(s)) return false; const wchar_t* s1 = s; const wchar_t* prefix_token[] = { L"SEMI", L"DEMI", L"EXTRA", L"ULTRA" }; const size_t prefix_count = sizeof(prefix_token) / sizeof(prefix_token[0]); unsigned int prefix_rc[prefix_count] = { 1, 1, 2, 3 }; const unsigned int prefix_id = ParseToken(s1, prefix_count, prefix_token, prefix_rc, 0); if ( 0 != prefix_id ) SkipSeparator(true,s1); const unsigned int medium_rc = (0 != prefix_id) ? 0 : 2; const wchar_t* name_token[] = { L"CONDENSED", L"MEDIUM", L"NORMAL", L"EXPANDED", }; const size_t name_count = sizeof(name_token) / sizeof(name_token[0]); unsigned int name_rc[name_count] = { 1,medium_rc,medium_rc,3 }; const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0); if (0 == name_id) return false; bool rc = false; switch (name_id) { case 1: // Condensed switch (prefix_id) { case 0: // no prefix font_stretch = ON_Font::Stretch::Condensed; rc = true; break; case 1: // semi font_stretch = ON_Font::Stretch::Semicondensed; rc = true; break; case 2: // extra font_stretch = ON_Font::Stretch::Extracondensed; rc = true; break; case 3: // ultra font_stretch = ON_Font::Stretch::Ultracondensed; rc = true; break; } break; case 2: // Medium switch (prefix_id) { case 0: // no prefix font_stretch = ON_Font::Stretch::Medium; rc = true; break; } break; case 3: // Expanded switch (prefix_id) { case 0: // no prefix font_stretch = ON_Font::Stretch::Expanded; rc = true; break; case 1: // semi font_stretch = ON_Font::Stretch::Semiexpanded; rc = true; break; case 2: // extra font_stretch = ON_Font::Stretch::Extraexpanded; rc = true; break; case 3: // ultra font_stretch = ON_Font::Stretch::Ultraexpanded; rc = true; break; } break; } if (rc) { s = s1; return true; } return false; } static bool ParseWeight( const wchar_t*& s, ON_Font::Weight& font_weight ) { if (false == IsAtoZ(s)) return false; const wchar_t* s1 = s; const wchar_t* prefix_token[] = { L"SEMI", L"DEMI", L"EXTRA", L"ULTRA" }; const size_t prefix_count = sizeof(prefix_token) / sizeof(prefix_token[0]); unsigned int prefix_rc[prefix_count] = { 1, 1, 2, 2 }; const unsigned int prefix_id = ParseToken(s1, prefix_count, prefix_token, prefix_rc, 0); if ( prefix_id > 0 ) SkipSeparator(true,s1); const unsigned int medium_rc = (0 != prefix_id) ? 0 : 3; const unsigned int normal_rc = (0 != prefix_id) ? 0 : 4; const wchar_t* name_token[] = { L"THIN", L"LIGHT", L"NORMAL", L"REGULAR", L"MEDIUM", L"BOLD", L"HEAVY", L"BLACK" }; const size_t name_count = sizeof(name_token) / sizeof(name_token[0]); unsigned int name_rc[name_count] = { 1, 2, normal_rc, normal_rc, medium_rc, 5, 6, 6 }; const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0); if (0 == name_id) return false; bool rc = false; switch (name_id) { case 1: // Thin font_weight = ON_Font::Weight::Thin; rc = true; break; case 2: // Light switch (prefix_id) { case 0: font_weight = ON_Font::Weight::Light; rc = true; case 1: // Semi font_weight = ON_Font::Weight::Light; rc = true; case 2: // Ultra font_weight = ON_Font::Weight::Ultralight; rc = true; break; } break; case 3: // Normal font_weight = ON_Font::Weight::Normal; rc = true; break; case 4: // Medium font_weight = ON_Font::Weight::Medium; rc = true; break; case 5: // Bold switch (prefix_id) { case 0: font_weight = ON_Font::Weight::Bold; rc = true; case 1: // Semi font_weight = ON_Font::Weight::Semibold; rc = true; case 2: // Ultra font_weight = ON_Font::Weight::Ultrabold; rc = true; break; } break; case 6: // Heavy font_weight = ON_Font::Weight::Heavy; rc = true; break; } if (rc) { s = s1; return true; } return false; } static bool ParseStyle( const wchar_t*& s, ON_Font::Style& font_style ) { if (false == IsAtoZ(s)) return false; const wchar_t* s1 = s; const wchar_t* name_token[] = { L"UPRIGHT", L"ROMAN", L"ITALIC", L"OBLIQUE" }; const size_t name_count = sizeof(name_token) / sizeof(name_token[0]); unsigned int name_rc[name_count] = { 1,1,2,3 }; const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0); if (0 == name_id) return false; bool rc = false; switch (name_id) { case 1: // Upright font_style = ON_Font::Style::Upright; rc = true; break; case 2: // Italic font_style = ON_Font::Style::Italic; rc = true; break; case 3: // Oblique font_style = ON_Font::Style::Oblique; rc = true; break; } if (rc) { s = s1; return true; } return false; } bool ON_Font::SetFromFontDescription( const wchar_t* font_description ) { const wchar_t* apple_font_name = nullptr; return SetFromFontDescription( font_description, apple_font_name ); } bool ON_Font::SetFromFontDescription( const wchar_t* font_description, const wchar_t* apple_font_name ) { ON_wString local_font_description(font_description); local_font_description.TrimLeftAndRight(); font_description = static_cast(local_font_description); ON_wString local_apple_font_name(apple_font_name); local_apple_font_name.TrimLeftAndRight(); apple_font_name = static_cast(local_apple_font_name); if (nullptr == font_description || font_description[0] <= ON_wString::Space) { font_description = apple_font_name; local_font_description = local_apple_font_name; if (nullptr == font_description || font_description[0] <= ON_wString::Space) return false; } // As names are discovered that do not work in the code below, // add a special case here. These are typically fonts that have // words like Upright, Italic, Oblique, Regular, Semi, Demi, Extra, Ultra, Medium // Black, Heavy, ... as part of the face name and those words must not // be parsed as possible weight, style or stretch characteristics applied // to a root face name. const wchar_t* special_cases[] = { // Apple's "Times New Roman" and "Times New Roman Bold Italic" is an example of why // special cases are required. The face name is "Times New Roman" and "Roman" no // longer indicates an upright style. // However, Apple's "Avenir Roman" and "Avenir Oblique" fonts provide an example // where "Roman" is style and "Avenir" is a face name. The default parsing // handles Avenir and any other font names that are using Roman as a style. L"Times New Roman", // Put new special case names above this nullptr which terminates the special_cases[] list. nullptr }; // NOTE WELL: // It is important that local_face_name be created from a pointer // so that it's string buffer is not shared with other ON_wStrings. // wchar_t values in local_face_name are modifed via const_cast<> below. ON_wString local_face_name = static_cast(font_description); const wchar_t* characteristics = nullptr; int face_name_length = local_face_name.Length(); for (int i = 0; nullptr != special_cases[i]; i++) { const int special_case_length = ON_wString::Length(special_cases[i]); if (special_case_length > face_name_length) continue; if (special_case_length < face_name_length && local_face_name[special_case_length] > ON_wString::Space ) continue; if (false == ON_wString::EqualOrdinal(special_cases[i], special_case_length, font_description, special_case_length, true)) continue; characteristics = static_cast(local_face_name) + special_case_length; break; } if (nullptr == characteristics) characteristics = static_cast(local_face_name) + 1; const wchar_t x = (wchar_t)1; ON_Font::Weight font_weight = ON_Font::Default.m_font_weight; for (wchar_t* s0 = const_cast(characteristics); 0 != *s0; s0++) { const wchar_t* s1 = s0; if (ParseWeight(s1, font_weight)) { while ( s0 < s1 ) *s0++ = x; // NOTE - modifies local_face_name content break; } } ON_Font::Style font_style = ON_Font::Default.m_font_style; for (wchar_t* s0 = const_cast(characteristics); 0 != *s0; s0++) { const wchar_t* s1 = s0; if (ParseStyle(s1, font_style)) { while ( s0 < s1 ) *s0++ = x; // NOTE - modifies local_face_name content break; } } ON_Font::Stretch font_stretch = ON_Font::Default.m_font_stretch; for (wchar_t* s0 = const_cast(characteristics); 0 != *s0; s0++) { const wchar_t* s1 = s0; if (ParseStretch(s1, font_stretch)) { while ( s0 < s1 ) *s0++ = x; // NOTE - modifies local_face_name content break; } } face_name_length = 0; bool bCopyFontDescription = false; for (const wchar_t* s = static_cast(local_face_name); 0 != *s; s++) { if (*s >= ON_wString::Space) face_name_length++; else { while (*s > 0 && *s <= ON_wString::Space) s++; if (0 == *s) { // all text after the face name was converted to a characteristic // Using a copy preserves word order so input descriptions like // "Avenir Next Condensed Heavy" don't gert reordered to "Avenir Next Heavy Condensed" bCopyFontDescription = true; } break; } } local_face_name.SetLength(face_name_length); local_face_name.TrimLeftAndRight(); const wchar_t* face_name = static_cast(local_face_name); bool rc = SetFontCharacteristics( face_name, font_weight, font_style, font_stretch, false, false, ON_FontMetrics::DefaultLineFeedRatio, ON_Font::Default.m_logfont_charset ); if (rc) { if (bCopyFontDescription) m_font_description = local_font_description; m_apple_font_name = local_apple_font_name; } return rc; } ////////////////////////////////////////////////////////////////////// // // ON_Object overrides bool ON_Font::IsValid(ON_TextLog* text_log) const { return (0 == m_face_name[ON_Font::face_name_capacity] && ON_Font::IsValidFaceName(m_face_name)); } void ON_Font::Dump(ON_TextLog& dump) const { const bool bTextHash = dump.IsTextHash();; if (bTextHash) { dump.Print("Font face name = ...\n"); dump.PushIndent(); dump.Print( "The font face name and other properties depend on the platform \n" "or the fonts installed on a particular computer. Information like \n" "this is omitted from dumps used for SHA-1 hash caluculations so \n" "hash values from different platforms and computers can be compared. \n" ); } else { dump.Print("Font face name = \"%ls\"\n", FontFaceName()); dump.PushIndent(); if (FontDescription().IsNotEmpty()) dump.Print("Descripton = %ls\n", FontDescriptionAsPointer()); if (AppleFontName().IsNotEmpty()) dump.Print("Apple font name = %ls\n", AppleFontNameAsPointer()); } ON_wString s; if (m_point_size > 0.0) { dump.Print("PointSize = %g\n", m_point_size); } else { dump.Print("PointSize = ON_Font::Constants::AnnotationFontCellHeight (%d)\n", ON_Font::Constants::AnnotationFontCellHeight); } switch (this->FontWeight()) { case ON_Font::Weight::Unset: s = "Unset"; break; case ON_Font::Weight::Thin: s = "Light-Thin"; break; case ON_Font::Weight::Ultralight: s = "Light-Ultralight"; break; case ON_Font::Weight::Light: s = "Light"; break; case ON_Font::Weight::Normal: s = "Normal"; break; case ON_Font::Weight::Medium: s = "Normal-Medium"; break; case ON_Font::Weight::Semibold: s = "Bold-Semibold"; break; case ON_Font::Weight::Bold: s = "Bold"; break; case ON_Font::Weight::Ultrabold: s = "Bold-Ultrabold"; break; case ON_Font::Weight::Heavy: s = "Bold-Heavy"; break; default: s = ON_wString::FormatToString(L"%u", static_cast(this->FontWeight())); break; } dump.Print("Weight = %ls\n", static_cast(s)); switch (this->FontStyle()) { case ON_Font::Style::Unset: s = "Unset"; break; case ON_Font::Style::Upright: s = "Upright"; break; case ON_Font::Style::Italic: s = "Italic"; break; case ON_Font::Style::Oblique: s = "Oblique"; break; default: s = ON_wString::FormatToString(L"%u", static_cast(this->FontStyle())); break; } dump.Print("Style = %ls\n", static_cast(s)); switch (this->FontStretch()) { case ON_Font::Stretch::Unset: s = "Unset"; break; case ON_Font::Stretch::Ultracondensed: s = "Ultracondensed"; break; case ON_Font::Stretch::Extracondensed: s = "Extracondensed"; break; case ON_Font::Stretch::Condensed: s = "Condensed"; break; case ON_Font::Stretch::Semicondensed: s = "Semicondensed"; break; case ON_Font::Stretch::Medium: s = "Medium"; break; case ON_Font::Stretch::Semiexpanded: s = "Semiexpanded"; break; case ON_Font::Stretch::Expanded: s = "Expanded"; break; case ON_Font::Stretch::Extraexpanded: s = "Extraexpanded"; break; case ON_Font::Stretch::Ultraexpanded: s = "Ultraexpanded"; break; default: s = ON_wString::FormatToString(L"%u", static_cast(this->FontStretch())); break; } dump.Print("Stretch = %ls\n", static_cast(s)); dump.Print("Underlined = %ls\n", this->IsUnderlined() ? "true" : "false"); dump.Print("Strikethrough = %ls\n", this->IsStrikethrough() ? "true" : "false"); if (false == bTextHash) { const ON_wString characteristics_hash = FontCharacteristicsHash().ToString(true); dump.Print(L"Font characteristics SHA-1 hash = %ls\n", static_cast(characteristics_hash)); unsigned int runtime_sn = RuntimeSerialNumber(); if (runtime_sn >= 1) { if (this == &ON_Font::Default) dump.Print("Managed font <%u> (ON_Font::Default)\n", runtime_sn); else dump.Print("Managed font <%u>\n", runtime_sn); } #if defined(ON_OS_WINDOWS_GDI) // LOGFONT details dump.Print("LOGFONT\n"); const LOGFONT logfont = this->WindowsLogFont(0, nullptr); ON_Font::DumpLogfont(&logfont, dump); #endif // Free Type font details DumpFreeType(dump); } dump.PopIndent(); } #if defined(ON_OS_WINDOWS_GDI) void ON_Font::DumpLogfont( const LOGFONT* logfont, ON_TextLog& text_log ) { if (nullptr == logfont) { text_log.Print("LOGFONT = nullptr\n"); return; } text_log.Print("LOGFONT\n"); text_log.PushIndent(); ON_wString s = logfont->lfFaceName; text_log.Print("lfFaceName = %ls\n",static_cast(s)); text_log.Print("Height = %d\n", logfont->lfHeight); text_log.Print("Width = %d\n", logfont->lfWidth); text_log.Print("Escapement = %d.%d degrees\n", (logfont->lfEscapement)/10, (logfont->lfEscapement)%10); text_log.Print("Orientation = %d.%d degrees\n", (logfont->lfOrientation)/10, (logfont->lfOrientation)%10); switch (logfont->lfWeight) { case FW_DONTCARE: s = "FW_DONTCARE"; break; case FW_THIN: s = "FW_THIN"; break; case FW_EXTRALIGHT: s = "FW_EXTRALIGHT = FW_ULTRALIGHT"; break; case FW_LIGHT: s = "FW_LIGHT"; break; case FW_NORMAL: s = "FW_NORMAL = FW_REGULAR"; break; case FW_MEDIUM: s = "FW_MEDIUM"; break; case FW_SEMIBOLD: s = "FW_SEMIBOLD = FW_DEMIBOLD"; break; case FW_BOLD: s = "FW_BOLD"; break; case FW_EXTRABOLD: s = "FW_EXTRABOLD = FW_ULTRABOLD"; break; case FW_HEAVY: s = "FW_HEAVY = FW_BLACK"; break; default: s = ON_wString::EmptyString; break; } if (s.IsNotEmpty()) s += " = "; s += ON_wString::FormatToString(L"%d", logfont->lfWeight); text_log.Print("Weight = %ls\n", static_cast(s)); text_log.Print("Italic = %ls\n", logfont->lfItalic ? L"true" : L"false"); text_log.Print("Underline = %ls\n", logfont->lfUnderline ? L"true" : L"false"); text_log.Print("StrikeOut = %ls\n", logfont->lfStrikeOut ? L"true" : L"false"); switch (logfont->lfCharSet) { case ANSI_CHARSET: s = "ANSI_CHARSET"; break; case DEFAULT_CHARSET: s = "DEFAULT_CHARSET"; break; case SYMBOL_CHARSET: s = "SYMBOL_CHARSET"; break; case SHIFTJIS_CHARSET: s = "SHIFTJIS_CHARSET"; break; case HANGEUL_CHARSET: s = "HANGEUL_CHARSET"; break; case GB2312_CHARSET: s = "GB2312_CHARSET"; break; case CHINESEBIG5_CHARSET: s = "CHINESEBIG5_CHARSET"; break; case OEM_CHARSET: s = "OEM_CHARSET"; break; #if(WINVER >= 0x0400) case JOHAB_CHARSET: s = "JOHAB_CHARSET"; break; case HEBREW_CHARSET: s = "HEBREW_CHARSET"; break; case ARABIC_CHARSET: s = "ARABIC_CHARSET"; break; case GREEK_CHARSET: s = "GREEK_CHARSET"; break; case TURKISH_CHARSET: s = "TURKISH_CHARSET"; break; case VIETNAMESE_CHARSET: s = "VIETNAMESE_CHARSET"; break; case THAI_CHARSET: s = "THAI_CHARSET"; break; case EASTEUROPE_CHARSET: s = "EASTEUROPE_CHARSET"; break; case RUSSIAN_CHARSET: s = "RUSSIAN_CHARSET"; break; case MAC_CHARSET: s = "MAC_CHARSET"; break; case BALTIC_CHARSET: s = "BALTIC_CHARSET"; break; #endif default: s = ON_wString::EmptyString; break; } if (s.IsNotEmpty()) s += L" = "; s += ON_wString::FormatToString(L"%d", (unsigned int)logfont->lfCharSet); text_log.Print("CharSet = %ls\n", static_cast(s)); switch (logfont->lfOutPrecision) { case OUT_DEFAULT_PRECIS: s = "OUT_DEFAULT_PRECIS"; break; case OUT_STRING_PRECIS: s = "OUT_STRING_PRECIS"; break; case OUT_CHARACTER_PRECIS: s = "OUT_CHARACTER_PRECIS"; break; case OUT_STROKE_PRECIS: s = "OUT_STROKE_PRECIS"; break; case OUT_TT_PRECIS: s = "OUT_TT_PRECIS"; break; case OUT_DEVICE_PRECIS: s = "OUT_DEVICE_PRECIS"; break; case OUT_RASTER_PRECIS: s = "OUT_RASTER_PRECIS"; break; case OUT_TT_ONLY_PRECIS: s = "OUT_TT_ONLY_PRECIS"; break; case OUT_OUTLINE_PRECIS: s = "OUT_OUTLINE_PRECIS"; break; case OUT_SCREEN_OUTLINE_PRECIS: s = "OUT_SCREEN_OUTLINE_PRECIS"; break; case OUT_PS_ONLY_PRECIS: s = "OUT_PS_ONLY_PRECIS"; break; }; if (s.IsNotEmpty()) s += L" = "; s += ON_wString::FormatToString(L"%d", (unsigned int)logfont->lfOutPrecision); text_log.Print("OutPrecision = %ls\n", static_cast(s)); text_log.Print("ClipPrecision = 0x%02x\n", logfont->lfOutPrecision); text_log.Print("Quality = 0x%02x\n", logfont->lfQuality); const unsigned int pitch = (logfont->lfPitchAndFamily & 0x03); const unsigned int family = (logfont->lfPitchAndFamily & 0xFC); switch (pitch) { case DEFAULT_PITCH: s = "DEFAULT_PITCH"; break; case FIXED_PITCH: s = "FIXED_PITCH"; break; case VARIABLE_PITCH: s = "VARIABLE_PITCH"; break; default: s = ON_wString::FormatToString(L"0x%02x", pitch); }; s += " | "; switch (family) { case FF_DONTCARE: s += "FF_DONTCARE"; break; case FF_ROMAN: s += "FF_ROMAN"; break; case FF_SWISS: s += "FF_SWISS"; break; case FF_MODERN: s += "FF_MODERN"; break; case FF_SCRIPT: s += "FF_SCRIPT"; break; case FF_DECORATIVE: s += "FF_DECORATIVE"; break; default: s += ON_wString::FormatToString(L"0x%02x", family); }; if (s.IsNotEmpty()) s += L" = "; s += ON_wString::FormatToString(L"0x%02x", (unsigned int)logfont->lfPitchAndFamily); text_log.Print("PitchAndFamily = %ls\n", static_cast(s)); text_log.PopIndent(); } #endif bool ON_Font::Write( ON_BinaryArchive& file // serialize definition to binary archive ) const { if (file.Archive3dmVersion() < 60 || file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version ) { ON_WARNING("This font should probably be an ON_TextStyle."); return WriteV5( RuntimeSerialNumber(), ON_nil_uuid, file ); } if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3)) return false; bool rc = false; for (;;) { // version 1.0 unsigned int fc = FontCharacteristicsAsUnsigned(); if (!file.WriteInt(fc)) break; int face_name_length = 0; while (face_name_length < ON_Font::face_name_capacity) { if ( 0 == m_face_name[face_name_length] ) break; face_name_length++; } if ( !file.WriteWideString(m_face_name,face_name_length) ) break; if (!file.WriteString(m_apple_font_name)) break; // version 1.1 added font_description August 2016 if (!file.WriteString(m_font_description)) break; // version 1.2 added m_windows_logfont_weight and m_apple_font_weight_trait if (!file.WriteInt(m_windows_logfont_weight)) break; if (!file.WriteDouble(m_apple_font_weight_trait)) break; // version 1.3 added additional m_point_size and m_LOGFONT_* values. if (!file.WriteDouble(m_point_size)) break; const bool bOBSOLETEBool = false; if (!file.WriteBool(bOBSOLETEBool)) break; rc = true; break; } if (!file.EndWrite3dmChunk()) rc = false; return rc; } bool ON_Font::WriteV5( int V5_font_index, ON_UUID V5_font_id, ON_BinaryArchive& file // serialize definition to binary archive ) const { bool rc = file.Write3dmChunkVersion(1, 2); while(rc) { rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::TextStyle,V5_font_index); if(!rc) break; // Mac Rhino 5 uses the V5 "m_font_description" field to store Apple font names because // there was no other appropriate place to save the information in a V5 file format. const ON_wString font_description = (m_font_description.IsEmpty() || (ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment() && m_apple_font_name.IsNotEmpty()) ) ? m_apple_font_name : m_font_description; rc = file.WriteString(font_description); if(!rc) break; { // 18 October 2002 Dale Lear: // Lowell, wcha:r_t has different sizes on different OSs. // When writing a wchar_t string, you should use one // of the WriteString functions. This function must continue // to use WriteShort(64,...) so old files will remain valid. unsigned short sh[64]; memset(sh, 0, sizeof(sh)); int i; for(i = 0; i < 64 && i < ON_Font::face_name_capacity; i++) sh[i] = m_face_name[i]; rc = file.WriteShort(64, sh); if(!rc) break; } // 1.1 additions int windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); if (file.Archive3dmVersion() < 60) { // V5 and earlier files had 4 permitted weights // Light = 300 // Normal = 400 // Medium = 500 // Bold = 700 if ( windows_logfont_weight <= 0 ) windows_logfont_weight = 400; if ( windows_logfont_weight < 350 ) windows_logfont_weight = 300; else if ( windows_logfont_weight < 450 ) windows_logfont_weight = 400; else if ( windows_logfont_weight < 600 ) windows_logfont_weight = 500; else if ( windows_logfont_weight <= 1000 ) windows_logfont_weight = 700; else windows_logfont_weight = 400; } rc = file.WriteInt(static_cast(windows_logfont_weight)); if(!rc) break; rc = file.WriteInt(ON_Font::Style::Italic == m_font_style); if(!rc) break; rc = file.WriteDouble(ON_FontMetrics::DefaultLineFeedRatio); if(!rc) break; // 1.2 addition rc = file.WriteUuid(V5_font_id); if(!rc) break; break; } return rc; } bool ON_Font::Read( ON_BinaryArchive& file // restore definition from binary archive ) { // On September 16, 2015 the "V5" ON_Font was split into // ON_TextStyle (a document object) and // ON_Font (a current runtime resource) *this = ON_Font::Default; ON__UINT32 typecode = 0; ON__INT64 big_value = 0; if (file.Archive3dmVersion() < 60 || file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version || (file.PeekAt3dmBigChunkType(&typecode,&big_value) && 1 == typecode) ) { ON_WARNING("Should probably be reading an ON_TextStyle"); int font_index = -1; ON_UUID font_id = ON_nil_uuid; return ReadV5( file, &font_index, &font_id ); } int major_verision = 0; int minor_verision = 0; if (!file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_verision,&minor_verision)) return false; bool rc = false; for (;;) { if ( 1 != major_verision ) break; unsigned int fc = 0; if (!file.ReadInt(&fc)) break; ON_wString face_name; if ( !file.ReadWideString(face_name) ) break; SetFontFaceName(static_cast(face_name)); Internal_SetFontCharacteristicsFromUnsigned(fc); if (!file.ReadString(m_apple_font_name)) break; if (ON::RuntimeEnvironment::Windows == file.ArchiveRuntimeEnvironment()) { // Dale Lear - August 16, 2016. // The value of m_apple_font_name is damaged in many archives written by Windows Rhino. // It has values like "Font 01". So, I'm going to clean it // during read and we will get it right going forward. // This value is not saved in V5 files. const unsigned int broken_apple_font_name_version = ON_VersionNumberConstruct(6, 0, 2016, 8, 18, 0); const unsigned int archive_version = file.Archive3dmVersion(); const unsigned int archive_opennurbs_version = file.ArchiveOpenNURBSVersion(); if ( archive_version < 60 || (60 == archive_version && archive_opennurbs_version <= broken_apple_font_name_version) ) { m_apple_font_name = ON_wString::EmptyString; } } if (minor_verision <= 0) { Internal_SetFontDescription(); rc = true; break; } // version 1.1 added m_font_description August 2016 if ( !file.ReadString(m_font_description) ) break; if (minor_verision <= 1) { // m_windows_logfont_weight and m_apple_font_weight_trait // are set above in the call to SetFontCharacteristicsFromUnsigned(). rc = true; break; } // version 1.2 added m_windows_logfont_weight and m_apple_font_weight_trait if (!file.ReadInt(&m_windows_logfont_weight)) break; if (!file.ReadDouble(&m_apple_font_weight_trait)) break; if (minor_verision <= 2) { rc = true; break; } // version 1.3 added additional m_point_size and m_LOGFONT_* values. if (!file.ReadDouble(&m_point_size)) break; bool bOBSOLETE_Bool = false; if (!file.ReadBool(&bOBSOLETE_Bool)) break; if (bOBSOLETE_Bool) { unsigned char obsolete_c = 0; if (!file.ReadChar(&obsolete_c)) break; if (!file.ReadChar(&obsolete_c)) break; if (!file.ReadChar(&obsolete_c)) break; if (!file.ReadChar(&obsolete_c)) break; int obsolete_i = 0; if (!file.ReadInt(&obsolete_i)) break; if (!file.ReadInt(&obsolete_i)) break; if (!file.ReadInt(&obsolete_i)) break; if (!file.ReadInt(&obsolete_i)) break; } rc = true; break; } if (!file.EndRead3dmChunk()) rc = false; return rc; } bool ON_Font::ReadV5( ON_BinaryArchive& file, // restore definition from binary archive int* V5_font_index, ON_UUID* V5_font_id ) { *this = ON_Font::Default; if ( nullptr != V5_font_index ) *V5_font_index = -1; if ( nullptr != V5_font_id ) *V5_font_id = ON_nil_uuid; int major_version = 0; int minor_version = 0; if (!file.Read3dmChunkVersion(&major_version, &minor_version)) return false; bool rc = false; ON_wString apple_font_name; for (;;) { if ( 1 != major_version ) break; int i; if (!file.ReadInt(&i)) break; if ( nullptr != V5_font_index ) *V5_font_index = i; ON_wString corrupt_information_font_description; if (!file.ReadString(corrupt_information_font_description)) break; if (ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment() && false == ON_Font::IsNotAppleFontName(corrupt_information_font_description) ) { // Files written by Mac Rhino 5 for Mac have the Apple font name stored in this string. apple_font_name = corrupt_information_font_description; } // 18 October 2002 Dale Lear: // Lowell, wchar_t has different sizes on different OSs. // When writing a wchar_t string, you should use one // of the WriteString functions. This function must continue // to use ReadShort(64,...) so old files will remain valid. unsigned short sh[64]; if (!file.ReadShort(64, sh)) break; wchar_t facename[65]; for(i = 0; i < 64; i++) facename[i] = sh[i]; facename[64] = 0; SetFontFaceName(facename); m_logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(m_face_name); if (minor_version >= 1) { int logfont_weight = 0; if (!file.ReadInt(&logfont_weight)) break; if (logfont_weight >= 100 && logfont_weight <= 1000) { Internal_SetFontWeightTrio( ON_Font::WeightFromWindowsLogfontWeight(logfont_weight), logfont_weight, ON_UNSET_VALUE, false ); } int bItalic = 0; if (!file.ReadInt(&bItalic)) break; if ( 0 != bItalic ) m_font_style = ON_Font::Style::Italic; double obsolete_linefeed_ratio = 1.6; if (!file.ReadDouble(&obsolete_linefeed_ratio)) break; if (minor_version >= 2) { ON_UUID uuid = ON_nil_uuid; if (!file.ReadUuid(uuid)) break; if ( nullptr != V5_font_id ) *V5_font_id = uuid; } } rc = true; break; } if (apple_font_name.IsNotEmpty()) { m_font_description = apple_font_name; } else if (m_face_name[0] > ON_wString::Space) { Internal_SetFontDescription(); } else { m_font_description = ON_wString::EmptyString; } m_apple_font_name = apple_font_name; return rc; } unsigned int ON_Font::RuntimeSerialNumber() const { return m_runtime_serial_number; } unsigned int ON_Font::ManagedFontSerialNumber() const { if (0 != m_runtime_serial_number) return m_runtime_serial_number; const ON_Font* mananged_font = this->ManagedFont(); return (nullptr == mananged_font) ? 0 : mananged_font->RuntimeSerialNumber(); } #if defined(ON_OS_WINDOWS_GDI) static int CALLBACK ON__IsSymbolCharSetFontFaceNameHelper(ENUMLOGFONTEX* lf, NEWTEXTMETRICEX* tm, DWORD font_type, LPARAM) { // If the fontname in the logfont structure has // a corresponding symbol font on the system, // set the lfCharSet member to SYMBOL_CHARSET, // otherwise DEFAULT_CHARSET // The input logfont structure may be modified. return 7; } #endif unsigned char ON_Font::WindowsLogfontCharSetFromFaceName( const wchar_t* face_name ) { unsigned char logfont_charset = ON_Font::WindowsConstants::logfont_default_charset; #if defined(ON_OS_WINDOWS_GDI) if( nullptr != face_name && face_name[0] > ON_wString::Space ) { HDC hdc = ::GetDC(nullptr); if(hdc) { // See if there is a font with this facename that has the symbol charset LOGFONT logfont; memset(&logfont, 0, sizeof(logfont)); for(int i = 0; i < LF_FACESIZE && face_name[i]; i++) logfont.lfFaceName[i] = face_name[i]; // Dale lear - set logfont.lfOutPrecision May 2017 logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis; // Is it a SYMBOL_CHARSET font? logfont.lfCharSet = ON_Font::WindowsConstants::logfont_symbol_charset; if (7 == ::EnumFontFamiliesEx(hdc, &logfont, (FONTENUMPROC)ON__IsSymbolCharSetFontFaceNameHelper, 0, 0)) { // Yes, this facename is a "symbol font" logfont_charset = ON_Font::WindowsConstants::logfont_symbol_charset; } ::ReleaseDC(nullptr, hdc); } } #endif return logfont_charset; } const ON_wString& ON_Font::FontDescription() const { return m_font_description; } const wchar_t* ON_Font::FontDescriptionAsPointer() const { return static_cast(m_font_description); } void ON_Font::Internal_SetFontDescription() { ON_wString local_font_description = m_face_name; m_font_description = ON_wString::EmptyString; #if defined (ON_RUNTIME_APPLE) if (m_apple_font_name.Length() > 0) { local_font_description = m_apple_font_name; m_font_description = local_font_description; return; } #endif const double point_size = PointSize(); if (point_size > 0.0) { local_font_description += ON_wString::FormatToString(L" %g point",point_size); } switch (FontWeight()) { case ON_Font::Weight::Unset: //local_font_description += L" Unsetweight"; break; case ON_Font::Weight::Thin: local_font_description += L" Thin"; break; case ON_Font::Weight::Ultralight: local_font_description += L" Ultralight"; break; case ON_Font::Weight::Light: local_font_description += L" Light"; break; case ON_Font::Weight::Normal: //local_font_description += L" Normal"; break; case ON_Font::Weight::Medium: local_font_description += L" Medium"; break; case ON_Font::Weight::Semibold: local_font_description += L" Semibold"; break; case ON_Font::Weight::Bold: local_font_description += L" Bold"; break; case ON_Font::Weight::Ultrabold: local_font_description += L" Ultrabold"; break; case ON_Font::Weight::Heavy: local_font_description += L" Heavy"; break; default: break; } switch (FontStyle()) { case ON_Font::Style::Upright: //local_font_description += L" Upright"; break; case ON_Font::Style::Italic: local_font_description += L" Italic"; break; case ON_Font::Style::Oblique: local_font_description += L" Oblique"; break; default: break; } switch (m_font_stretch) { case ON_Font::Stretch::Unset: //local_font_description += L" Unsetstretch"; break; case ON_Font::Stretch::Ultracondensed: local_font_description += L" Ultracondensed"; break; case ON_Font::Stretch::Extracondensed: local_font_description += L" Extracondensed"; break; case ON_Font::Stretch::Condensed: local_font_description += L" Condensed"; break; case ON_Font::Stretch::Semicondensed: local_font_description += L" Semicondensed"; break; case ON_Font::Stretch::Medium: //local_font_description += L" Medium"; break; case ON_Font::Stretch::Semiexpanded: local_font_description += L" Semiexpanded"; break; case ON_Font::Stretch::Expanded: local_font_description += L" Expanded"; break; case ON_Font::Stretch::Extraexpanded: local_font_description += L" Extraexpanded"; break; case ON_Font::Stretch::Ultraexpanded: local_font_description += L" Ultraexpanded"; break; default: break; }; if(IsUnderlined()) local_font_description += L" Underlined"; if(IsStrikethrough()) local_font_description += L" Strikethrough"; local_font_description.TrimLeft(); m_font_description = local_font_description; } double ON_Font::LinefeedRatio() const { return ON_FontMetrics::DefaultLineFeedRatio; } bool ON_Font::SetFontFaceName( const wchar_t* face_name ) { if (false == ON_Font::IsValidFaceName(face_name)) return false; if ( ON_wString::EqualOrdinal(face_name,m_face_name,false) ) return true; if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; memset(m_face_name, 0, sizeof(m_face_name)); for (int i = 0; i < ON_Font::face_name_capacity && 0 != face_name[i]; i++) m_face_name[i] = face_name[i]; if (0 == m_logfont_charset || ON_Font::logfont_symbol_charset == m_logfont_charset ) m_logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(m_face_name); Internal_SetFontDescription(); return true; } const wchar_t* ON_Font::FontFaceName() const { return static_cast(m_face_name); } ON_Font::Weight ON_Font::FontWeight() const { return m_font_weight; } bool ON_Font::IsLight() const { const int font_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); const int normal_weight = ON_Font::WindowsLogfontWeightFromWeight(ON_Font::Weight::Normal); return (font_weight < normal_weight && m_font_weight != ON_Font::Weight::Unset); } bool ON_Font::IsNormalWeight() const { return (ON_Font::Weight::Normal == m_font_weight || ON_Font::Weight::Medium == m_font_weight); } bool ON_Font::IsBold() const { const int font_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); const int bold_threshhold_weight = ON_Font::WindowsLogfontWeightFromWeight(ON_Font::Weight::Semibold); return (font_weight >= bold_threshhold_weight && m_font_weight != ON_Font::Weight::Unset); } bool ON_Font::IsItalic() const { return (ON_Font::Style::Italic == m_font_style); } bool ON_Font::IsUpright() const { return (ON_Font::Style::Upright == m_font_style); } bool ON_Font::IsOblique() { return (ON_Font::Style::Oblique == m_font_style); } bool ON_Font::Internal_SetFontWeightTrio( ON_Font::Weight font_weight, int windows_logfont_weight, double apple_font_weight_trait, bool bUpdateFontDescription ) { font_weight = ON_Font::FontWeightFromUnsigned((unsigned int)static_cast(font_weight)); if (ON_Font::Weight::Unset == font_weight) return false; if (false == (1 <= windows_logfont_weight && windows_logfont_weight <= 1000)) windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(font_weight); if (false == (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) ) apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(font_weight); if ( font_weight != m_font_weight || false == (apple_font_weight_trait == m_apple_font_weight_trait) // handles nans correctly || windows_logfont_weight != m_windows_logfont_weight ) { if (false == ON_FONT_MODIFICATION_PERMITTED) return false; if (font_weight != m_font_weight) m_font_weight = font_weight; if (false == (apple_font_weight_trait == m_apple_font_weight_trait)) m_apple_font_weight_trait = apple_font_weight_trait; if (windows_logfont_weight != m_windows_logfont_weight) m_windows_logfont_weight = windows_logfont_weight; if ( bUpdateFontDescription ) Internal_SetFontDescription(); } return true; } bool ON_Font::SetFontWeight(ON_Font::Weight font_weight) { return Internal_SetFontWeightTrio( font_weight, -1, ON_UNSET_VALUE, font_weight != m_font_weight ); } bool ON_Font::SetWindowsLogfontWeight( int windows_logfont_weight ) { const ON_Font::Weight font_weight = ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight); return Internal_SetFontWeightTrio( font_weight, windows_logfont_weight, ON_UNSET_VALUE, windows_logfont_weight != m_windows_logfont_weight ); } int ON_Font::WindowsLogfontWeight() const { return (100 <= m_windows_logfont_weight && m_windows_logfont_weight <= 1000) ? m_windows_logfont_weight : ON_Font::WindowsLogfontWeightFromWeight(FontWeight()); } bool ON_Font::SetAppleWeightOfFont( int apple_weight_of_font ) { const bool bUpdateFontDescription = m_apple_font_name.IsEmpty() || m_apple_font_name != m_font_description; const ON_Font::Weight font_weight = ON_Font::WeightFromAppleWeightOfFont(apple_weight_of_font); double apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(font_weight); if (0 <= apple_weight_of_font && apple_weight_of_font <= 9) { apple_font_weight_trait = (1.0 - apple_weight_of_font) / 7.5; if (apple_font_weight_trait < -1.0) apple_font_weight_trait = -1.0; else if (apple_font_weight_trait > 1.0) apple_font_weight_trait = 1.0; } return Internal_SetFontWeightTrio( font_weight, -1, apple_font_weight_trait, bUpdateFontDescription ); } int ON_Font::AppleWeightOfFont() const { return WindowsLogfontWeight() / 100; } bool ON_Font::SetAppleFontWeightTrait( double apple_font_weight_trait ) { if (false == ON_IsValid(apple_font_weight_trait)) return false; // nan or unset value const ON_Font::Weight font_weight = ON_Font::WeightFromAppleFontWeightTrait(apple_font_weight_trait); const bool bUpdateFontDescription = m_apple_font_name.IsEmpty() || m_apple_font_name != m_font_description; return Internal_SetFontWeightTrio( font_weight, -1, apple_font_weight_trait, bUpdateFontDescription ); } double ON_Font::AppleFontWeightTrait() const { return (m_apple_font_weight_trait >= -1.0 && m_apple_font_weight_trait <= 1.0) ? m_apple_font_weight_trait : ON_Font::AppleFontWeightTraitFromWeight(this->FontWeight()); } double ON_Font::PointSize() const { return m_point_size; } bool ON_Font::SetPointSize( double point_size ) { double x = (point_size > 0.0) ? point_size : 0.0; if (!(m_point_size == x)) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; m_point_size = x; Internal_SetFontDescription(); } return true; } ON_Font::Style ON_Font::FontStyle() const { return m_font_style; } bool ON_Font::SetFontStyle( ON_Font::Style font_style ) { if (m_font_style != font_style) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; m_font_style = font_style; Internal_SetFontDescription(); } return true; } ON_Font::Stretch ON_Font::FontStretch() const { return m_font_stretch; } bool ON_Font::SetFontStretch( ON_Font::Stretch font_stretch ) { if (m_font_stretch != font_stretch) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; m_font_stretch = font_stretch; Internal_SetFontDescription(); } return true; } bool ON_Font::IsUnderlined() const { return m_font_bUnderlined; } bool ON_Font::SetUnderlined(bool bUnderlined) { if( m_font_bUnderlined != (bUnderlined?true:false)) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; m_font_bUnderlined = bUnderlined; } return true; } bool ON_Font::IsStrikethrough() const { return m_font_bStrikethrough; } bool ON_Font::SetStrikethrough(bool bStrikethrough) { if(m_font_bStrikethrough != (bStrikethrough?true:false)) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; m_font_bStrikethrough = bStrikethrough; } return true; } unsigned char ON_Font::LogfontCharSet() const { return m_logfont_charset; } bool ON_Font::SetLogfontCharSet(unsigned char logfont_charset) { if(logfont_charset != m_logfont_charset) { if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; m_logfont_charset = logfont_charset; Internal_SetFontDescription(); } return true; } const ON_FontMetrics& ON_Font::FontMetrics() const { class ON_FontGlyphCache* font_cache = FontGlyphCache(true); if (nullptr != font_cache) return font_cache->m_normalized_metrics; return ON_FontMetrics::Unset; } const ON_FontMetrics& ON_Font::FontUnitFontMetrics() const { class ON_FontGlyphCache* font_cache = FontGlyphCache(true); if (nullptr != font_cache) return font_cache->m_font_unit_metrics; return ON_FontMetrics::Unset; } double ON_Font::FontUnitToNormalizedScale() const { class ON_FontGlyphCache* font_cache = FontGlyphCache(true); if (nullptr != font_cache) return font_cache->m_font_unit_to_normalized_scale; return 1.0; } double ON_Font::NormalizedToFontUnitScale() const { class ON_FontGlyphCache* font_cache = FontGlyphCache(true); if (nullptr != font_cache) return font_cache->m_normalized_to_font_unit_scale; return 1.0; } int ON_Font::HeightOfI() const { return FontMetrics().AscentOfI(); } int ON_Font::HeightOfLinefeed() const { return FontMetrics().LineSpace(); } double ON_Font::HeightScale(double text_height) const { return FontMetrics().GlyphScale(text_height); } int ON_Font::GetStrikeoutSize() const { return FontMetrics().StrikeoutThickness(); } int ON_Font::GetStrikeoutPosition() const { return FontMetrics().StrikeoutPosition(); } int ON_Font::GetUnderscoreSize() const { return FontMetrics().UnderscoreThickness(); } int ON_Font::GetUnderscorePosition() const { return FontMetrics().UnderscorePosition(); } const class ON_SHA1_Hash& ON_Font::FontCharacteristicsHash() const { if (m_font_characteristics_hash.IsZeroDigest()) { ON_SHA1 sha1; sha1.AccumulateUnsigned64(sizeof(*this)); sha1.AccumulateString(m_face_name, sizeof(m_face_name)/sizeof(m_face_name[0]), ON_StringMapOrdinalType::Identity); sha1.AccumulateUnsigned8(static_cast(m_font_weight)); sha1.AccumulateUnsigned8(static_cast(m_font_stretch)); sha1.AccumulateBool(m_font_bUnderlined); sha1.AccumulateBool(m_font_bStrikethrough); sha1.AccumulateUnsigned8(static_cast(m_logfont_charset)); ON_SHA1_Hash string_hash; if (m_font_description.IsNotEmpty()) { string_hash = ON_SHA1_Hash::StringHash(this->m_font_description); sha1.AccumulateSubHash(string_hash); } if (m_point_size > 0) { sha1.AccumulateDouble(m_point_size); } #if defined(ON_RUNTIME_WIN) sha1.AccumulateInteger32(m_windows_logfont_weight); #endif #if defined(ON_RUNTIME_APPLE) sha1.AccumulateDouble(m_apple_font_weight_trait); if (m_apple_font_name.IsNotEmpty()) { string_hash = ON_SHA1_Hash::StringHash(m_apple_font_name); sha1.AccumulateSubHash(string_hash); } #endif m_font_characteristics_hash = sha1.Hash(); } return m_font_characteristics_hash; } int ON_Font::CompareFontCharacteristics( const ON_Font& a, const ON_Font& b ) { int i = ON_wString::CompareOrdinal(a.m_face_name, ON_Font::face_name_capacity, b.m_face_name, ON_Font::face_name_capacity, true); int j = 0; for (;;) { if ( i != j ) break; i = ON_Font::WindowsLogfontWeightFromWeight(a.m_font_weight); j = ON_Font::WindowsLogfontWeightFromWeight(b.m_font_weight); if ( i != j ) break; i = static_cast(a.m_font_style); j = static_cast(b.m_font_style); if ( i != j ) break; i = static_cast(a.m_font_stretch); j = static_cast(b.m_font_stretch); if ( i != j ) break; i = a.m_font_bUnderlined ? 1 : 0; j = b.m_font_bUnderlined ? 1 : 0; if ( i != j ) break; i = a.m_font_bStrikethrough ? 1 : 0; j = b.m_font_bStrikethrough ? 1 : 0; if ( i != j ) break; j = 0; if (a.m_logfont_charset < b.m_logfont_charset) i = -1; else if (a.m_logfont_charset > b.m_logfont_charset) i = 1; else { #if defined(ON_RUNTIME_APPLE) i = ON_wString::CompareOrdinal(a.m_apple_font_name, b.m_apple_font_name, false); if (0 == i) { if (a.m_apple_font_weight_trait < b.m_apple_font_weight_trait) i = -1; else if (a.m_apple_font_weight_trait > b.m_apple_font_weight_trait) i = 1; } #else i = 0; #endif } if (i == j) { j = 0; if (a.m_point_size < b.m_point_size) i = -1; else if (a.m_point_size > b.m_point_size) i = 1; else i = 0; } break; } if (i < j) return -1; if (i > j) return 1; return 0; } bool ON_Font::EqualFontCharacteristics( const ON_Font& a, const ON_Font& b ) { return (0 == ON_Font::CompareFontCharacteristics(a,b)); } #if defined(ON_RUNTIME_WIN) static int ON_InternalVerticalPixelScale( HDC hdc, bool bDCtoLP, int y0 ) { int y1 = -1; for (;;) { POINT origin{ 0,0 }; POINT pt{ 0, y0 }; if (bDCtoLP) { if (0 == ::DPtoLP(hdc, &pt, 1)) break; if (0 == ::DPtoLP(hdc, &origin, 1)) break; } else { if (0 == ::LPtoDP(hdc, &pt, 1)) break; if (0 == ::LPtoDP(hdc, &origin, 1)) break; } y1 = std::abs(pt.y - origin.y); break; } return y1; } bool ON_Font::GetWindowsDeviceToLogicalHeightScales( HDC hdc, double* device_to_logical_scale, double* logical_to_device_scale ) { if (nullptr != device_to_logical_scale) *device_to_logical_scale = 1.0; if (nullptr != logical_to_device_scale) *logical_to_device_scale = 1.0; int device_coordinate = 0; int logical_coordinate = 0; // see which is larger, device or logical const int thousand = 1000; const int device_to_logical_1000x = ON_InternalVerticalPixelScale(hdc, true, thousand); const int logical_to_device_1000x = ON_InternalVerticalPixelScale(hdc, false, thousand); if (device_to_logical_1000x <= 0 && logical_to_device_1000x <= 0) { ON_ERROR("DPtoLP(hdc, ...) and LPtoDP(hdc, ...) failed."); return false; } if (thousand == device_to_logical_1000x && thousand == logical_to_device_1000x) { // vertical logical pixels are the same size as device pixels device_coordinate = 1; logical_coordinate = 1; } else { // bDCtoLP = true if logical pixels are smaller than device pixels const bool bDCtoLP = (device_to_logical_1000x >= logical_to_device_1000x); const int s1000 = bDCtoLP ? device_to_logical_1000x : logical_to_device_1000x; int y0 = 0; int y1 = 0; const int s1024 = ON_InternalVerticalPixelScale(hdc, bDCtoLP, 1024); for (y0 = 1; y0 <= 100; y0++) { y1 = ON_InternalVerticalPixelScale(hdc, bDCtoLP, y0); if (y0*s1000 == y1*thousand || y0*s1024 == y1*1024) { break; } y1 = 0; } if (0 == y1) { y0 = thousand; y1 = s1000; } if (bDCtoLP) { device_coordinate = y0; logical_coordinate = y1; } else { device_coordinate = y1; logical_coordinate = y0; } } if (nullptr != device_to_logical_scale) { *device_to_logical_scale = ((double)logical_coordinate) / ((double)device_coordinate); } if (nullptr != logical_to_device_scale) { *logical_to_device_scale = ((double)device_coordinate) / ((double)logical_coordinate); } return true; } HDC ON_Font::Internal_CreateWindowsLogfontDeviceContext() { return ::CreateCompatibleDC(nullptr); } void ON_Font::Internal_DeleteWindowsLogfontDeviceContext( HDC hdc ) { if ( nullptr != hdc ) ::DeleteDC(hdc); } bool ON_Font::GetWindowsTextMetrics( int map_mode, HDC hdc, const LOGFONT& logfont, TEXTMETRIC& textmetric ) { bool rc = false; TEXTMETRIC tm; memset(&tm, 0, sizeof(tm)); textmetric = tm; // need to get TEXTMETRICS tmInternalLeading const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX); const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode); HDC screen_dc = nullptr; const int savedDC = (nullptr != hdc) ? ::SaveDC(hdc) : 0; HFONT hfont = 0; for (;;) { if (nullptr == hdc) { screen_dc = ON_Font::Internal_CreateWindowsLogfontDeviceContext(); hdc = screen_dc; if (nullptr == hdc) break; } const int hdc_mm = ::GetMapMode(hdc); const int mm = bValidMapModeParameter ? map_mode : ((nullptr != hdc) ? hdc_mm : 0); if (mm < MM_MIN || mm > MM_MAX) { ON_ERROR("GetMapMode(hdc) failed."); break; } if (hdc_mm != mm) { ::SetMapMode(hdc, mm); } hfont = ::CreateFontIndirect(&logfont); if (0 == hfont) break; ::SelectObject(hdc, hfont); if (0 == ::GetTextMetrics(hdc, &tm)) { ON_ERROR("GetTextMetrics(hdc,...) failed."); break; } textmetric = tm; rc = true; break; } if (nullptr != screen_dc) ON_Font::Internal_DeleteWindowsLogfontDeviceContext(screen_dc); else if ( nullptr != hdc) ::RestoreDC(hdc, savedDC); if (0 != hfont) ::DeleteObject(hfont); return rc; } int ON_Font::WindowsLogfontCharacterHeight( int map_mode, HDC hdc, const LOGFONT& logfont ) { if (logfont.lfHeight <= 0) return logfont.lfHeight; int logfont_lfHeight = 0; for (;;) { TEXTMETRIC tm; memset(&tm, 0, sizeof(tm)); bool bHaveTextMetric = ON_Font::GetWindowsTextMetrics( map_mode, hdc, logfont, tm ); if (false == bHaveTextMetric) { ON_ERROR("ON_Font::GetWindowsTextMetrics...) failed."); break; } const int cellHeight = tm.tmAscent + tm.tmDescent; const int characterHeight = cellHeight - tm.tmInternalLeading; if (cellHeight <= 0 || characterHeight <= 0) { ON_ERROR("tm.tmInternalLeading >= cellHeight."); break; } // logfont_lfHeight is negative to indicate it is character height. // (positive values are cell height) logfont_lfHeight = -characterHeight; break; } return logfont_lfHeight; } int ON_Font::WindowsLogfontCellHeight( int map_mode, HDC hdc, const LOGFONT& logfont ) { if (logfont.lfHeight >= 0) return logfont.lfHeight; int logfont_lfHeight = 0; for (;;) { TEXTMETRIC tm; memset(&tm, 0, sizeof(tm)); bool bHaveTextMetric = ON_Font::GetWindowsTextMetrics( map_mode, hdc, logfont, tm ); if (false == bHaveTextMetric) { ON_ERROR("ON_Font::GetWindowsTextMetrics...) failed."); break; } const int cellHeight = tm.tmAscent + tm.tmDescent; if (cellHeight <= 0) { ON_ERROR("tm.tmAscent + tm.tmDescent < 0."); break; } // logfont_lfHeight is positive to indicate it is cell height. // (negative values are character height) logfont_lfHeight = cellHeight; break; } return logfont_lfHeight; } int ON_Font::WindowsLogfontCharacterHeightFromPointSize( int map_mode, HDC hdc, double point_size ) { int logfont_lfHeight = 0; const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX); const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode); HDC screen_dc = nullptr; for (;;) { if (0 == point_size) break; if (!(point_size > 0.0 && point_size < 21474836.47)) { ON_ERROR("Invalid point_size parameter."); break; } if (bNeedDeviceContext && nullptr == hdc) { screen_dc = ON_Font::Internal_CreateWindowsLogfontDeviceContext(); hdc = screen_dc; if (nullptr == hdc) break; } const int mm = bValidMapModeParameter ? map_mode : ((nullptr != hdc) ? ::GetMapMode(hdc) : 0); if (mm < MM_MIN || mm > MM_MAX) { ON_ERROR("GetMapMode(hdc) failed."); break; } double points_to_lfHeight = 0.0; switch (mm) { case MM_TEXT: // Each logical unit is mapped to one device pixel. Positive x is to the right; positive y is down. if(nullptr != hdc) { const int device_pixels_per_logical_inch = ::GetDeviceCaps(hdc, LOGPIXELSY); if (device_pixels_per_logical_inch <= 0) { ON_ERROR("GetDeviceCaps(hdc, LOGPIXELSY) failed."); break; } double device_to_logical_scale = 1.0; if (false == ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, &device_to_logical_scale, nullptr) ) { ON_ERROR("ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, ...) failed."); break; } // MM_TEXT points_to_lfHeight units // = ((device pixel height)/(logical inch)) * (logical pixel height) / ( (points/inch)*(device pixel height) ) // = ( (logical pixel height) / points ) * (inch / (logical inch)) points_to_lfHeight = ((static_cast(device_pixels_per_logical_inch)*device_to_logical_scale)) / 72.0; } break; case MM_LOMETRIC: // Each logical unit is mapped to 0.1 millimeter. Positive x is to the right; positive y is up. // (254 tenths of a mm/inch)/(72 points/inch) points_to_lfHeight = 254.0/72.0; break; case MM_HIMETRIC: // Each logical unit is mapped to 0.01 millimeter. Positive x is to the right; positive y is up. // (2540 hundredths of a mm/inch)/(72 points/inch) points_to_lfHeight = 2540.0/72.0; break; case MM_LOENGLISH: // Each logical unit is mapped to 0.01 inch. Positive x is to the right; positive y is up. // (100 hundredths of a inch/inch)/(72 points/inch) points_to_lfHeight = 1.0/0.72; break; case MM_HIENGLISH: // Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up. // (1000 thousandths of a inch/inch)/(72 points/inch) points_to_lfHeight = 1.0/0.072; break; case MM_TWIPS: // Each logical unit is mapped to one twentieth of a printer's point (1/1440 inch, also called a "twip"). Positive x is to the right; positive y is up. // 20 twips / point points_to_lfHeight = 20.0; break; case MM_ISOTROPIC: // Logical units are mapped to arbitrary units with equally scaled axes; // that is, one unit along the x-axis is equal to one unit along the y-axis. // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units // and the orientation of the axes. Graphics device interface makes adjustments // as necessary to ensure the x and y units remain the same size. // (When the windows extent is set, the viewport will be adjusted to keep // the units isotropic). ON_ERROR("GetMapMode(hdc) returned MM_ISOTROPIC mapping mode."); break; case MM_ANISOTROPIC: // Logical units are mapped to arbitrary units with arbitrarily scaled axes. // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units, // orientation, and scaling required. ON_ERROR("GetMapMode(hdc) returned MM_ANISOTROPIC mapping mode."); break; default: ON_ERROR("GetMapMode(hdc) returned undocumented mapping mode."); } if (0 != points_to_lfHeight) { logfont_lfHeight = -static_cast(fabs(points_to_lfHeight*point_size + 0.5)); if (0 == logfont_lfHeight) logfont_lfHeight = -1; } break; } if (0 != screen_dc) { ON_Font::Internal_DeleteWindowsLogfontDeviceContext(screen_dc); } return logfont_lfHeight; } double ON_Font::PointSizeFromWindowsLogfontCharacterHeight( int map_mode, HDC hdc, int logfont_character_height ) { double point_size = 0.0; const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX); const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode); HDC screen_dc = 0; // Windows LOGFONT.lfHeight is negative when // the value is a character height. This // function allow the user to pass in a positive value. const int logfont_lfHeight = (logfont_character_height <= 0) ? logfont_character_height : -logfont_character_height; for (;;) { if (logfont_lfHeight >= 0) break; if (bNeedDeviceContext && nullptr == hdc ) { screen_dc = ON_Font::Internal_CreateWindowsLogfontDeviceContext(); hdc = screen_dc; if (nullptr == hdc) break; } const int mm = bValidMapModeParameter ? map_mode : ((nullptr != hdc) ? ::GetMapMode(hdc) : 0); if (mm < MM_MIN || mm > MM_MAX) { ON_ERROR("GetMapMode(hdc) failed."); break; } double lfHeight_to_points = 0.0; switch (mm) { case MM_TEXT: // Each logical unit is mapped to one device pixel. Positive x is to the right; positive y is down. if(nullptr != hdc) { const int device_pixels_per_logical_inch = ::GetDeviceCaps(hdc, LOGPIXELSY); if (device_pixels_per_logical_inch <= 0) { ON_ERROR("GetDeviceCaps(hdc, LOGPIXELSY) failed."); break; } double logical_to_device_scale = 1.0; if (false == ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, nullptr, &logical_to_device_scale)) { ON_ERROR("ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, ...) failed."); break; } // MM_TEXT lfHeight_to_points units // = ( (points/inch) * (device pixel height) ) / ( ((device pixel height)/(logical inch)) * (logical pixel height) ) // = ( points / (logical pixel height) ) * ( (logical inch)/inch ) lfHeight_to_points = (72.0*logical_to_device_scale) / static_cast(device_pixels_per_logical_inch); } break; case MM_LOMETRIC: // Each logical unit is mapped to 0.1 millimeter. Positive x is to the right; positive y is up. // 72 points/inch / (254 tenths of a mm/inch) lfHeight_to_points = 72.0 / 254.0; break; case MM_HIMETRIC: // Each logical unit is mapped to 0.01 millimeter. Positive x is to the right; positive y is up. // 72 points/inch / (2540 hundredths of a mm/inch) lfHeight_to_points = 72.0 / 2540.0; break; case MM_LOENGLISH: // Each logical unit is mapped to 0.01 inch. Positive x is to the right; positive y is up. // 72 points/inch / (100 hundredths of a inch/inch) lfHeight_to_points = 0.72; break; case MM_HIENGLISH: // Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up. // 72 points/inch / (1000 thousandths of a inch/inch) lfHeight_to_points = 0.072; break; case MM_TWIPS: // Each logical unit is mapped to one twentieth of a printer's point (1/1440 inch, also called a "twip"). Positive x is to the right; positive y is up. // 1 point / 20 twips lfHeight_to_points = 1.0/20.0; break; case MM_ISOTROPIC: // Logical units are mapped to arbitrary units with equally scaled axes; // that is, one unit along the x-axis is equal to one unit along the y-axis. // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units // and the orientation of the axes. Graphics device interface makes adjustments // as necessary to ensure the x and y units remain the same size. // (When the windows extent is set, the viewport will be adjusted to keep // the units isotropic). ON_ERROR("GetMapMode(hdc) returned MM_ISOTROPIC mapping mode."); break; case MM_ANISOTROPIC: // Logical units are mapped to arbitrary units with arbitrarily scaled axes. // Use the SetWindowExtEx and SetViewportExtEx functions to specify the units, // orientation, and scaling required. ON_ERROR("GetMapMode(hdc) returned MM_ANISOTROPIC mapping mode."); break; default: ON_ERROR("GetMapMode(hdc) returned undocumented mapping mode."); } point_size = fabs(lfHeight_to_points*static_cast(logfont_lfHeight)); for (;;) { // Because LOGFONT lfHeight is an integer and point_size is typically // an integer and sometimes a multiple of 0.5, the following rouding // is performed so point_size will round trip. double i0 = floor(point_size); if (i0 == point_size) break; if (lfHeight_to_points < 0.5 && 2.0*point_size == floor(2.0*point_size)) break; double i1 = ceil(point_size); if (i1 >= 1.0) { if (i0 >= 1.0 && fabs(i0 - point_size) < fabs(i1 - point_size) && fabs(i0 - point_size) < lfHeight_to_points) { if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, i0)) { point_size = i0; break; } } else if (fabs(i1 - point_size) < fabs(i0 - point_size) && fabs(i1 - point_size) < lfHeight_to_points) { if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, i1)) { point_size = i1; break; } } if (lfHeight_to_points < 0.25 && fabs(point_size - 0.5*(i0 + i1)) < lfHeight_to_points) { if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, 0.5*(i0 + i1))) { point_size = 0.5*(i0 + i1); break; } } } break; } break; } if (0 != screen_dc) { ON_Font::Internal_DeleteWindowsLogfontDeviceContext(screen_dc); } return point_size; } bool ON_Font::SetFromWindowsLogFont( int map_mode, HDC hdc, const LOGFONT& logfont ) { const ON_Font::Weight font_weight = ON_Font::WeightFromWindowsLogfontWeight(logfont.lfWeight); const ON_Font::Style font_style = (0 != logfont.lfItalic) ? ON_Font::Style::Italic : ON_Font::Default.m_font_style; const ON_Font::Stretch font_stretch = ON_Font::Default.m_font_stretch; const bool bUnderlined = (0 != logfont.lfUnderline) ? true : false; const bool bStrikethrough = (0 != logfont.lfStrikeOut) ? true : false; const double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio; // If logfont.lfHeight > 0, +lfHeight = cell height. // If logfont.lfHeight < 0, -lfHeight = character height = cell height - TEXTMETRIC.tmInternalLeading. // PointSize() calculations require a character height. const int logfont_lfHeight = (0 != logfont.lfHeight) ? ON_Font::WindowsLogfontCharacterHeight(map_mode, hdc, logfont) : 0; const double point_size = (logfont_lfHeight < 0) ? ON_Font::PointSizeFromWindowsLogfontCharacterHeight(map_mode, hdc, logfont_lfHeight) : 0.0; unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(logfont.lfFaceName); if ( ON_Font::WindowsConstants::logfont_symbol_charset != logfont_charset && ON_Font::WindowsConstants::logfont_symbol_charset != logfont.lfCharSet ) { // Use the input value of logfont.lfCharSet. // // The problem: // // Many callers memset logfont to zero and don't correctly and consciously // set logfont.lfCharSet. The value 0 = ANSI_CHARSET is a valid value for // logfont.lfCharSet. // // At this point, we cannot tell if 0 == logfont.lfCharSet occurs because // the caller didn't set it or because the caller really wants ANSI_CHARSET // for this particular font. // // If the caller failed to correctly and consciously set logfont.lfCharSet // and the font is being used to render text for a locale other than // US English, DEFAULT_CHARSET (=1) would be a much better choice. // logfont_charset = logfont.lfCharSet; } const bool rc = SetFontCharacteristics( point_size, logfont.lfFaceName, font_weight, font_style, font_stretch, bUnderlined, bStrikethrough, linefeed_ratio, logfont_charset ); if (rc) { return Internal_SetFontWeightTrio( font_weight, logfont.lfWeight, ON_UNSET_VALUE, false ); } return rc; } const LOGFONT ON_Font::WindowsLogFont( int map_mode, HDC hdc ) const { LOGFONT logfont; memset(&logfont, 0, sizeof(LOGFONT)); logfont.lfHeight = (m_point_size > 0.0) ? ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode,hdc,m_point_size) : ON_Font::Constants::AnnotationFontCellHeight; logfont.lfWidth = 0; logfont.lfEscapement = 0; logfont.lfOrientation = 0; logfont.lfWeight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight); logfont.lfItalic = (ON_Font::Style::Italic == m_font_style) ? 1 : 0; logfont.lfUnderline = m_font_bUnderlined ? 1 : 0; logfont.lfStrikeOut = m_font_bStrikethrough ? 1 : 0; logfont.lfCharSet = m_logfont_charset; logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis; logfont.lfClipPrecision = 0; logfont.lfQuality = ON_Font::WindowsConstants::logfont_quality; logfont.lfPitchAndFamily = ON_Font::WindowsConstants::logfont_pitch_and_family; for(int i = 0; i <= ON_Font::face_name_capacity && i < LF_FACESIZE; i++) logfont.lfFaceName[i] = m_face_name[i]; return logfont; } const MAT2 ON_Font::WindowsFontMat2() const { 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; double eM11 = 1.0; switch (FontStretch()) { case ON_Font::Stretch::Ultracondensed: eM11 = 0.2; break; case ON_Font::Stretch::Extracondensed: eM11 = 0.3; break; case ON_Font::Stretch::Condensed: eM11 = 0.5; break; case ON_Font::Stretch::Semicondensed: eM11 = 0.75; break; case ON_Font::Stretch::Medium: eM11 = 1.0; break; case ON_Font::Stretch::Semiexpanded: eM11 = 1.25; break; case ON_Font::Stretch::Expanded: eM11 = 1.5; break; case ON_Font::Stretch::Extraexpanded: eM11 = 2.0; break; case ON_Font::Stretch::Ultraexpanded: eM11 = 3.0; break; } if (eM11 > 0.0 && 1.0 != eM11) { FIXED fx = { 1, 0 }; double ffrac = fmod(eM11, 1.0); if (ffrac != ffrac) ffrac = 0.0; fx.fract = static_cast(floor(65535.0 * ffrac)); fx.value = static_cast(eM11 - ffrac); mat2.eM11 = fx; } return mat2; } #endif void ON_Font::DestroyFontGlyphCache() { m_font_glyph_cache.reset(); } class ON_FontGlyphCache* ON_Font::FontGlyphCache( bool bCreateIfMissing ) const { ON_FontGlyphCache* font_cache = m_font_glyph_cache.get(); if(nullptr == font_cache) { // get the static ON_Font that matches this one const ON_Font* managed_font = this->ManagedFont(); if(nullptr != managed_font) { // The first call to ON_Font::GetManagedFont() adds // &ON_Font::Default and calculates its font metric // and glyph information. The this != managed_font // text below prevents a silly copy in this case // and in other unanticipated cases. if ( this != managed_font) m_font_glyph_cache = managed_font->m_font_glyph_cache; font_cache = m_font_glyph_cache.get(); } } return font_cache; } // static void ON_Font::GetRunBounds( const ON_Font& font, const wchar_t* text, double font_height_pixels, ON::TextHorizontalAlignment horizontal_alignment, ON::TextVerticalAlignment vertical_alignment, ON_2dPoint& bounds_min, ON_2dPoint& bounds_max, int& line_count ) { line_count = 0; bounds_min = ON_2dPoint::Origin; bounds_max = ON_2dPoint::Origin; ON_wString run = text; int textlength = run.Length(); if(textlength < 1) return; const ON_Font* managed_font = font.ManagedFont(); if (nullptr == managed_font) managed_font = &ON_Font::Default; const ON_FontMetrics& fm = managed_font->FontMetrics(); const int height_of_I = fm.AscentOfI(); const int height_of_LF = fm.LineSpace(); ON_TextBox text_box; line_count = ON_FontGlyph::GetGlyphListBoundingBox(text, managed_font, text_box); if ( line_count < 0 || false == text_box.IsSet()) return; bounds_min.x = text_box.m_bbmin.i; bounds_min.y = text_box.m_bbmin.j; bounds_max.x = text_box.m_bbmax.i; bounds_max.y = text_box.m_bbmax.j; // in text_box coordinate system (0.0) = left most baseline point of first line. const ON_2dPoint first_line_basepoint = ON_2dPoint::Origin; const ON_2dPoint last_line_basepoint(first_line_basepoint.x, -(line_count - 1)*height_of_LF); ON_2dVector offset = ON_2dVector::ZeroVector; switch (horizontal_alignment) { case ON::TextHorizontalAlignment::Left: offset.x = -first_line_basepoint.x; break; case ON::TextHorizontalAlignment::Center: // Text command uses m_max_basepoint (not bbmax) offset.x = -first_line_basepoint.x - 0.5*text_box.m_max_basepoint.i; break; case ON::TextHorizontalAlignment::Right: // Text command uses m_max_basepoint (not bbmax) offset.x = -first_line_basepoint.x - text_box.m_max_basepoint.i; break; default: break; } switch (vertical_alignment) { case ON::TextVerticalAlignment::Top: // (*,0) is at the top of an "I" on the first line. offset.y = -first_line_basepoint.y - height_of_I; break; case ON::TextVerticalAlignment::MiddleOfTop: // (*,0) is at the middle of an "I" on the first line. offset.y = -first_line_basepoint.y - 0.5*height_of_I; break; case ON::TextVerticalAlignment::BottomOfTop: // (*,0) is at the baseline of the first line. offset.y = -first_line_basepoint.y; break; case ON::TextVerticalAlignment::Middle: { // (*,0) is the vertical middle of lines (not vertical middle of glyph bounding box) double h = (line_count - 1)*height_of_LF + height_of_I; offset.y = -last_line_basepoint.y + 0.5*h; } break; case ON::TextVerticalAlignment::MiddleOfBottom: // (*,0) is at the middle of an "I" on the last line. offset.y = -last_line_basepoint.y - 0.5*height_of_I; break; case ON::TextVerticalAlignment::Bottom: // (*,0) is at the basline of the last line. offset.y = -last_line_basepoint.y; break; case ON::TextVerticalAlignment::BottomOfBoundingBox: // (*,0) is on the bottom the the glyphs' bounding box offset.y = -bounds_min.y; break; default: break; } bounds_min += offset; bounds_max += offset; // scale is applied last. const double scale = font_height_pixels / (double)ON_Font::Constants::AnnotationFontCellHeight; if (scale > 0.0) { bounds_min.x = scale * text_box.m_bbmin.i; bounds_max.x = scale * text_box.m_bbmax.i; bounds_min.y = scale * text_box.m_bbmin.j; bounds_max.y = scale * text_box.m_bbmax.j; } } const ON_FontGlyph* ON_Font::Internal_ManagedCodePointGlyph( ON__UINT32 unicode_code_point, bool bCreateIfMissing, bool bFindSubstitutes ) const { if ( false == ON_IsValidUnicodeCodePoint(unicode_code_point) ) { // valid UNICODE codepoint values are <= 0x10FFFF ON_ERROR("invalid codepoint parameter."); return nullptr; } if (false == IsManagedFont()) { ON_ERROR("this->IsManagedFont() must be true."); return nullptr; } // First see if we already have the glyph in the cache ON_FontGlyphCache* font_cache = FontGlyphCache(true); ON_GlyphMap* glyph_map = (nullptr == font_cache) ? nullptr : font_cache->m_glyphmap.get(); if (nullptr == glyph_map) { ON_ERROR("Unable to create ON_FontGlyphCache."); return nullptr; } const ON_FontGlyph* managed_glyph = glyph_map->FindGlyph(unicode_code_point); if (nullptr != managed_glyph) return managed_glyph; if (false == bCreateIfMissing) return nullptr; ON_FontGlyph glyph; glyph.SetCodePoint(this, unicode_code_point); // Call external function to get glyph details ON_TextBox font_unit_glyph_box = ON_TextBox::Unset; ON__UINT_PTR font_glyph_id = ON_ManagedFonts::GetGlyphMetrics(this, unicode_code_point, font_unit_glyph_box); if (0 != font_glyph_id) { if (font_unit_glyph_box.IsSet()) { glyph.m_font_unit_glyph_bbox = font_unit_glyph_box; // Save a normalized box as well. const double normalize_scale = this->FontUnitToNormalizedScale(); ON_TextBox normalized_glyph_box = font_unit_glyph_box; if (normalize_scale > 0.0) normalized_glyph_box = ON_TextBox::Scale(font_unit_glyph_box, normalize_scale); glyph.m_normalized_glyph_bbox = normalized_glyph_box; } glyph.Internal_SetFontGlyphId(font_glyph_id); bFindSubstitutes = false; } return glyph_map->InsertGlyph(glyph); } #if defined(ON_RUNTIME_WIN) static bool ON_Internal_WindowsGetGlyphMetricsEx( const ON_Font* font, int logfont_height, 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 (nullptr == font) return false; font = font->ManagedFont(); if (nullptr == font) 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 hdc = nullptr; HFONT hfont = nullptr; HGDIOBJ hfont0 = nullptr; for (;;) { hdc = ::CreateCompatibleDC(0); if (nullptr == hdc) break; LOGFONT lf = font->WindowsLogFont(0,nullptr); lf.lfHeight = logfont_height; hfont = ::CreateFontIndirect(&lf); if (nullptr == hfont) break; hfont0 = ::SelectObject(hdc, hfont); ON__UINT16 glyphindex[sizeof(w)/sizeof(w[0])] = { 0 }; const DWORD gicount = ::GetGlyphIndices(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; } GLYPHMETRICS glm; memset(&glm, 0, sizeof(glm)); MAT2 mat2 = font->WindowsFontMat2(); const DWORD cb_size = ::GetGlyphOutlineW(hdc, glyphindex[0], GGO_NATIVE | GGO_GLYPH_INDEX, &glm, 0, nullptr, &mat2); if (GDI_ERROR == cb_size) break; ////bool ul = false, so = false; ////int ulsize = 0, ulpos = 0, sosize = 0, sopos = 0; ////HGDIOBJ hfont = GetCurrentObject(hdc, OBJ_FONT); ////LOGFONT lf = { 0 }; ////if (sizeof(lf) == GetObject(hfont, sizeof(lf), &lf)) ////{ //// ul = lf.lfUnderline ? true : false; //// so = lf.lfStrikeOut ? true : false; ////} ////if (ul || so) ////{ //// OUTLINETEXTMETRIC otm = { 0 }; //// if (0 != ::GetOutlineTextMetrics(hdc, sizeof(OUTLINETEXTMETRIC), &otm)) //// { //// sosize = otm.otmsStrikeoutSize; //// sopos = otm.otmsStrikeoutPosition; //// ulsize = otm.otmsUnderscoreSize; //// ulpos = otm.otmsUnderscorePosition; //// } ////} 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 != hdc) { if (nullptr != hfont) { ::SelectObject(hdc, hfont0); ::DeleteObject(hfont); } ::DeleteDC(hdc); } return font_unit_glyph_box.IsSet(); } bool ON_WindowsGetGlyphMetrics( const ON_Font* font, ON__UINT32 unicode_code_point, class ON_TextBox& font_unit_glyph_box ) { return ON_Internal_WindowsGetGlyphMetricsEx(font, 0, unicode_code_point, font_unit_glyph_box); } #endif ON_Font::ON_GetGlyphMetricsFuncType ON_Font::Internal_CustomGetGlyphMetricsFunc = nullptr; ON_Font::ON_GetFontMetricsFuncType ON_Font::Internal_CustomGetFontMetricsFunc = nullptr; void ON_Font::SetCustomMeasurementFunctions( ON_GetGlyphMetricsFuncType measureGlyphFunc, ON_GetFontMetricsFuncType metricsFunc ) { ON_Font::Internal_CustomGetGlyphMetricsFunc = measureGlyphFunc; ON_Font::Internal_CustomGetFontMetricsFunc = metricsFunc; } ON__UINT_PTR ON_ManagedFonts::GetGlyphMetrics( const class ON_Font* font, ON__UINT32 unicode_code_point, class ON_TextBox& font_unit_glyph_box ) { return (nullptr != ON_Font::Internal_CustomGetGlyphMetricsFunc) ? ON_Font::Internal_CustomGetGlyphMetricsFunc(font, unicode_code_point, font_unit_glyph_box) : ON_FreeTypeGetGlyphMetrics(font, unicode_code_point, font_unit_glyph_box); } void ON_ManagedFonts::GetFontMetrics( const ON_Font* font, ON_FontMetrics& font_unit_font_metrics ) { return (nullptr != ON_Font::Internal_CustomGetFontMetricsFunc) ? ON_Font::Internal_CustomGetFontMetricsFunc(font, font_unit_font_metrics) : ON_FreeTypeGetFontMetrics(font, font_unit_font_metrics); }