Files
opennurbs/opennurbs_font.cpp
2018-09-10 17:39:40 -07:00

4623 lines
130 KiB
C++

//
// 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 <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#include "opennurbs.h"
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
#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<const ON_Font*> 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<ON_Font*>(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<ON_GlyphMap> 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<ON_wString&>(s);
clean.Destroy();
clean = static_cast<const wchar_t*>(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<ON_GlyphMap>();
managed_font->m_font_glyph_cache = std::shared_ptr<ON_FontGlyphCache>(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<unsigned int>(ON_Font::WindowsLogfontCharSetFromFaceName(face_name))
: static_cast<unsigned int>(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<unsigned int>(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<unsigned char>(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<int>(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<int>(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<unsigned int>(weight_a);
unsigned int b = static_cast<unsigned int>(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<unsigned int>(font_weight) },
{ 4U, static_cast<unsigned int>(font_style) },
{ 10U, static_cast<unsigned int>(font_stretch) },
// The static_cast<unsigned int> in b ? 1U : 0U is to work around a CLang compiler bug.
{ 2U, static_cast<unsigned int>(bUnderlined ? 1U : 0U) },
{ 2U, static_cast<unsigned int>(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<unsigned char>(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<const wchar_t*>(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<const wchar_t*>(local_font_description);
ON_wString local_apple_font_name(apple_font_name);
local_apple_font_name.TrimLeftAndRight();
apple_font_name = static_cast<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(local_face_name) + special_case_length;
break;
}
if (nullptr == characteristics)
characteristics = static_cast<const wchar_t*>(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<wchar_t*>(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<wchar_t*>(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<wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(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<unsigned int>(this->FontWeight())); break;
}
dump.Print("Weight = %ls\n", static_cast<const wchar_t*>(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<unsigned int>(this->FontStyle())); break;
}
dump.Print("Style = %ls\n", static_cast<const wchar_t*>(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<unsigned int>(this->FontStretch())); break;
}
dump.Print("Stretch = %ls\n", static_cast<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(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<int>(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<const wchar_t*>(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<const wchar_t*>(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<const wchar_t*>(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<unsigned char>(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<ON__UINT8>(m_font_weight));
sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_font_stretch));
sha1.AccumulateBool(m_font_bUnderlined);
sha1.AccumulateBool(m_font_bStrikethrough);
sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(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<int>(a.m_font_style);
j = static_cast<int>(b.m_font_style);
if ( i != j )
break;
i = static_cast<int>(a.m_font_stretch);
j = static_cast<int>(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<double>(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<int>(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<double>(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<double>(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<unsigned short>(floor(65535.0 * ffrac));
fx.value = static_cast<short>(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);
}