Files
opennurbs/opennurbs_apple_nsfont.cpp
2019-04-09 10:44:41 -07:00

1494 lines
47 KiB
C++

//
// Copyright (c) 1993-2018 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#include "opennurbs.h"
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
#if defined(ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
#include "opennurbs_internal_glyph.h"
#include "opennurbs_apple_nsfont.h"
void ON_ManagedFonts::Internal_GetAppleInstalledCTFonts(
ON_SimpleArray<const ON_Font*>& platform_font_list
)
{
CFDictionaryRef options = nullptr;
CTFontCollectionRef availableFontCollection = CTFontCollectionCreateFromAvailableFonts(options);
if (nullptr == availableFontCollection )
return;
CFArrayRef availableFontArray = CTFontCollectionCreateMatchingFontDescriptors(availableFontCollection);
if (nullptr == availableFontArray)
return;
const CFIndex count = CFArrayGetCount(availableFontArray);
for ( CFIndex idx = 0; idx < count; idx++)
{
CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(availableFontArray, idx);
if (nullptr == descriptor)
continue;
const CGAffineTransform *matrix = nullptr;
CGFloat size = 1000;
CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, size, matrix);
if (nullptr == font)
continue;
const unsigned int units_per_em = CTFontGetUnitsPerEm(font);
if ( units_per_em > 0 && (CGFloat)units_per_em != size )
{
CFRelease(font);
size = (CGFloat)units_per_em;
font = CTFontCreateWithFontDescriptor(descriptor, size, matrix);
if (nullptr == font)
continue;
}
ON_Font* platform_font = new ON_Font();
if (false == platform_font->SetFromAppleCTFont(font, false))
{
CFRelease(font);
delete platform_font;
continue;
}
platform_font->SetPointSize(0);
platform_font_list.Append(platform_font);
}
}
const ON_Font* ON_Font::GetManagedFontFromAppleCTFont(
CTFontRef apple_font,
bool bAnnotationFont
)
{
ON_Font font_characteristics;
if ( false == font_characteristics.SetFromAppleCTFont(apple_font,bAnnotationFont) )
return nullptr;
return font_characteristics.ManagedFont();
}
unsigned int ON_Font::AppleCTFontUnitsPerEm(CTFontRef apple_font)
{
const unsigned int units_per_em
= (nullptr != apple_font)
? CTFontGetUnitsPerEm(apple_font)
: 0;
return units_per_em;
}
CTFontRef ON_Font::AppleCTFontSetSize(
CTFontRef apple_font,
CGFloat size,
bool bReleaseOriginal
)
{
for(;;)
{
if (nullptr == apple_font)
break;
const unsigned int units_per_em = ON_Font::AppleCTFontUnitsPerEm(apple_font);
if ( units_per_em <= 0 )
break;
const CGFloat units_per_em_size = (CGFloat)units_per_em;
if ( !(units_per_em_size > 0.0) )
break;
const CGFloat size0 = CTFontGetSize(apple_font);
if ( fabs(size0 - units_per_em_size) <= 0.0001 )
break;
const CGAffineTransform *matrix = nullptr;
CTFontDescriptorRef attributes = nullptr;
CTFontRef apple_font1 = CTFontCreateCopyWithAttributes(apple_font, units_per_em_size, matrix, attributes);
if ( nullptr == apple_font1)
break;
const unsigned int units_per_em1 = CTFontGetUnitsPerEm(apple_font1);
if ( units_per_em1 != units_per_em)
{
CFRelease(apple_font1);
break;
}
if (bReleaseOriginal)
CFRelease(apple_font);
return apple_font1;
}
return apple_font;
}
ON_PANOSE1 ON_Font::AppleCTFontPANOSE1(CTFontRef apple_font)
{
ON_PANOSE1 panose1 = ON_PANOSE1::Zero;
CFDataRef os2Table = nullptr;
for(;;)
{
if (nullptr == apple_font)
break;
// The OS/2 table contains the PANOSE1 classification
os2Table = CTFontCopyTable(apple_font, kCTFontTableOS2, kCTFontTableOptionNoOptions);
if (nullptr == os2Table)
break;
// The PANOSE data is in bytes 32-42 of the table according to the TrueType and OpenType specs.
if (CFDataGetLength(os2Table) < 42)
{
// Truncated table?
break;
}
uint8_t panose[10] = {};
CFDataGetBytes(os2Table, (CFRange){ 32, 10 }, panose);
if (panose[0] > 5)
break; // invalid PANOSE1 family kind
panose1.SetTenBytes(panose);
break;
}
if (nullptr != os2Table)
CFRelease(os2Table);
return panose1;
}
bool ON_Font::SetFromAppleCTFont(CTFontRef apple_font, bool bAnnotationFont)
{
if (nullptr == apple_font)
return false;
if ( false == this->ModificationPermitted(OPENNURBS__FUNCTION__,__FILE__,__LINE__) )
return false;
*this = ON_Font::Unset;
const ON_wString postscript_name = ON_Font::AppleCTFontPostScriptName(apple_font);
const ON_wString family_name = ON_Font::AppleCTFontFamilyName(apple_font);
const ON_wString face_name = ON_Font::AppleCTFontFaceName(apple_font);
// Set Windows LOGFONT.lfFaceName to something not empty there is some hope this
// font might work in Rhino 5 for Windows too.
// https://mcneel.myjetbrains.com/youtrack/issue/RH-37074
const ON_wString windows_logfont_name = family_name;
const bool rc = postscript_name.IsNotEmpty() || family_name.IsNotEmpty();
if (rc)
{
m_loc_postscript_name = postscript_name;
m_en_postscript_name = postscript_name;
m_loc_family_name = family_name;
m_en_family_name = family_name;
m_loc_face_name = face_name;
m_en_face_name = face_name;
m_loc_windows_logfont_name = windows_logfont_name;
m_en_windows_logfont_name = windows_logfont_name;
// Can get font metrics from NSFontDescriptor
// https://developer.apple.com/library/content/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html
// defaultLineHeight(for theFont: NSFont)
// fd.xHeight, fd.ascender, fd.descender, fd.capHeight, fd.defaultLineHeightForFont
// Set weight - used if this font needs sustution on another computer
// https://mcneel.myjetbrains.com/youtrack/issue/RH-37075
double apple_font_weight_trait = ON_UNSET_VALUE;
double apple_font_width_trait = ON_UNSET_VALUE;
double apple_font_slant_trait = ON_UNSET_VALUE;
ON_Font::Weight weight = ON_Font::Weight::Normal;
ON_Font::Style style = ON_Font::Style::Upright;
ON_Font::Stretch stretch = ON_Font::Stretch::Medium;
CTFontDescriptorRef descriptor = CTFontCopyFontDescriptor(apple_font);
if (nullptr != descriptor)
{
CFDictionaryRef dict = (CFDictionaryRef)CTFontDescriptorCopyAttribute(descriptor, kCTFontTraitsAttribute);
if (nullptr != dict)
{
const CFNumberRef appleSymbolicTrait = (CFNumberRef)CFDictionaryGetValue(dict, kCTFontSymbolicTrait);
const CFNumberRef appleWeight = (CFNumberRef)CFDictionaryGetValue(dict, kCTFontWeightTrait);
const CFNumberRef appleWidth = (CFNumberRef)CFDictionaryGetValue(dict, kCTFontWidthTrait);
const CFNumberRef appleSlant = (CFNumberRef)CFDictionaryGetValue(dict, kCTFontSlantTrait);
if (nullptr != appleSymbolicTrait)
{
int i=0;
if ( CFNumberGetValue(appleSymbolicTrait, kCFNumberIntType, &i) )
{
// Use the kCTFontSymbolicTrait key to access the symbolic traits value from the font traits dictionary.
// The value is returned as a CFNumberRef.
if (0 != (kCTFontTraitItalic & i))
style = ON_Font::Style::Italic;
if (0 != (kCTFontTraitBold & i))
weight = ON_Font::Weight::Bold;
if (0 != (kCTFontTraitCondensed & i))
stretch = ON_Font::Stretch::Condensed;
else if (0 != (kCTFontTraitExpanded & i))
stretch = ON_Font::Stretch::Expanded;
}
}
if (nullptr != appleWeight)
{
double x = apple_font_weight_trait;
if ( CFNumberGetValue(appleWeight, kCFNumberFloat64Type, &x) && -1.0 <= x && x <= 1.0)
{
// Use the kCTFontWeightTrait key to access the normalized weight trait from the font traits dictionary.
// The value returned is a CFNumberRef representing a float value between -1.0 and 1.0 for normalized weight.
// The value of 0.0 corresponds to the regular or medium font weight.
apple_font_weight_trait = x;
ON_Font::WeightFromAppleFontWeightTrait(apple_font_weight_trait);
}
}
if (nullptr != appleWidth)
{
double x = apple_font_weight_trait;
if ( CFNumberGetValue(appleWidth, kCFNumberFloat64Type, &x) && -1.0 <= x && x <= 1.0 )
{
// Use the kCTFontWidthTrait key to access the normalized proportion trait from the font traits dictionary.
// This value corresponds to the relative inter-glyph spacing for a given font.
// The value returned is a CFNumberRef representing a float between -1.0 and 1.0.
// The value of 0.0 corresponds to regular glyph spacing while negative values represent condensed glyph spacing.
apple_font_width_trait = x;
if (-1.0 == apple_font_width_trait)
stretch = ON_Font::Stretch::Condensed;
else if (1.0 == apple_font_width_trait)
stretch = ON_Font::Stretch::Expanded;
}
}
if (nullptr != appleSlant)
{
double x = apple_font_weight_trait;
if ( CFNumberGetValue(appleSlant, kCFNumberFloat64Type, &x) && -1.0 <= x && x <= 1.0 )
{
// Use this key to access the normalized slant angle from the font traits dictionary.
// The value returned is a CFNumberRef representing a float value between -1.0 and 1.0 for normalized slant angle.
// The value or 0.0 corresponds to 0 degree clockwise rotation from the vertical and 1.0 corresponds to 30 degrees clockwise rotation.
apple_font_slant_trait = x;
if (1.0 == apple_font_slant_trait)
style = ON_Font::Style::Italic;
}
}
CFRelease(dict);
}
CFRelease(descriptor);
}
if (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0)
SetAppleFontWeightTrait(apple_font_weight_trait);
else
SetFontWeight(weight);
m_font_style = style;
m_font_stretch = stretch;
// Saving point size added January 2018.
const double point_size = (double)CTFontGetSize(apple_font);
m_point_size
= (false == bAnnotationFont && ON_Font::IsValidPointSize(point_size) && point_size < ((double)ON_Font::AnnotationFontApplePointSize))
? point_size
: 0.0; // indicates annotation size (units per em) will be used
m_logfont_charset = ON_Font::WindowsConstants::logfont_default_charset;
// do this again because some of the above calls can modify description
m_loc_postscript_name = postscript_name;
m_en_postscript_name = m_loc_postscript_name;
m_loc_family_name = family_name;
m_en_family_name = m_loc_family_name;
m_loc_face_name = face_name;
m_en_face_name = m_loc_face_name;
m_loc_windows_logfont_name = windows_logfont_name;
m_en_windows_logfont_name = m_loc_windows_logfont_name;
m_panose1 = ON_Font::AppleCTFontPANOSE1(apple_font);
SetFontOrigin(ON_Font::Origin::AppleFont);
}
return rc;
}
const ON_wString ON_Font::AppleCTFontPostScriptName(
CTFontRef apple_font
)
{
if (nullptr == apple_font)
return ON_wString::EmptyString;
CFStringRef applePostScriptName = CTFontCopyPostScriptName(apple_font);
ON_wString postscript_name(applePostScriptName);
if (nullptr != applePostScriptName)
CFRelease(applePostScriptName);
postscript_name.TrimLeftAndRight();
return postscript_name;
}
const ON_wString ON_Font::AppleCTFontFamilyName(
CTFontRef apple_font
)
{
CFStringRef appleFamilyName = CTFontCopyFamilyName(apple_font);
ON_wString family_name(appleFamilyName);
if (nullptr != appleFamilyName)
CFRelease(appleFamilyName);
family_name.TrimLeftAndRight();
return family_name;
}
const ON_wString ON_Font::AppleCTFontDisplayName(
CTFontRef apple_font
)
{
CFStringRef appleDisplayName = CTFontCopyDisplayName(apple_font);
ON_wString display_name(appleDisplayName);
if (nullptr != appleDisplayName)
CFRelease(appleDisplayName);
display_name.TrimLeftAndRight();
return display_name;
}
static const ON_wString Internal_FaceNameFromAppleDisplayAndFamilyName(
const ON_wString& appleFontDisplayName,
const ON_wString& appleFontFamilyName
)
{
if ( appleFontDisplayName.IsEmpty() || appleFontFamilyName.IsEmpty())
return ON_wString::EmptyString;
const wchar_t* family_and_face_str = static_cast<const wchar_t*>(appleFontDisplayName);
const wchar_t* family_str = static_cast<const wchar_t*>(appleFontFamilyName);
if (nullptr == family_and_face_str || nullptr == family_str)
return ON_wString::EmptyString;
// It is not always the case that the beginning of
// appleFontDisplayName exactly matches
// appleFontFamilyName.
// We need to ignore spaces and hyphens.
for (;;)
{
wchar_t a = *family_str;
while (ON_wString::Space == a || ON_wString::HyphenMinus == a)
a = *(++family_str);
wchar_t b = *family_and_face_str;
while (ON_wString::Space == b || ON_wString::HyphenMinus == b)
b = *(++family_and_face_str);
if (0 == a || 0 == b)
break;
a = ON_wString::MapCharacterOrdinal(ON_StringMapOrdinalType::MinimumOrdinal,a);
b = ON_wString::MapCharacterOrdinal(ON_StringMapOrdinalType::MinimumOrdinal,b);
if (a != b)
break;
family_str++;
family_and_face_str++;
}
for(wchar_t c = *family_and_face_str; ON_wString::Space == c || ON_wString::HyphenMinus == c; c = *family_and_face_str )
++family_and_face_str;
ON_wString face_name(family_and_face_str);
face_name.TrimRight();
if ( face_name.IsEmpty())
{
// This is what Apple MacOS uses in FontBook and other font selection UI.
// https://developer.apple.com/documentation/appkit/nsfontmanager/1462316-availablemembersoffontfamily?language=objc
// A more complicated approach that yields the same results is:
// https://developer.apple.com/documentation/coretext/1511240-ctfontcopyname?language=objc
// using kCTFontSubFamilyNameKey This retrieves the TrueType "subfamily" font name
// TrueType names [1]=family [2]=subfamily (which we call "face"), [6] = postscript.
// Reference: Section 6 of https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=IWS-Chapter08
//
// None of the approaches above is localized, so a hard coded English word works as
// well as using the Apple SDK.
face_name = L"Regular";
}
return face_name;
}
const ON_wString ON_Font::AppleCTFontFaceName(
CTFontRef apple_font
)
{
for(;;)
{
if (nullptr == apple_font)
break;
for(;;)
{
CFStringRef appleStyleName = CTFontCopyName(apple_font,kCTFontStyleNameKey);
if (nullptr == appleStyleName)
break;
ON_wString style_name(appleStyleName);
CFRelease(appleStyleName);
style_name.TrimRight();
if ( style_name.IsEmpty() )
break;
return style_name;
}
// Fallback - In Sep 12, 2018 tests of 603 faces, the no longer needed with the swtich to CTFont
// Leaving this here to handle unknown buggy fonts.
const ON_wString family_and_face_name = ON_Font::AppleCTFontDisplayName(apple_font);
const ON_wString family_name = ON_Font::AppleCTFontFamilyName(apple_font);
const ON_wString hack_style_name = Internal_FaceNameFromAppleDisplayAndFamilyName(family_and_face_name,family_name);
return hack_style_name;
}
return ON_wString::EmptyString;
}
CTFontRef ON_Font::AppleCTFont() const
{
// Using PointSize() added January 2018.
const double pointSize
= ON_Font::IsValidPointSize(m_point_size)
? m_point_size
: 0.0;
return ON_Font::AppleCTFont(pointSize);
}
CTFontRef ON_Font::AppleCTFont(
const wchar_t* name,
double pointSize
)
{
ON_wString local_name(name);
local_name.TrimLeftAndRight();
if (local_name.IsEmpty())
return nullptr;
const bool bHavePointSize = ( pointSize > 0.0 );
const CGFloat size = (CGFloat)(bHavePointSize ? pointSize : 1000.0 );
const CGAffineTransform *matrix = nullptr;
CFStringRef appleName = local_name.ToAppleCFString();
if (nullptr == appleName)
return nullptr;
CTFontRef appleFont = CTFontCreateWithName(appleName, size, matrix);
CFRelease(appleName);
if (nullptr == appleFont)
return nullptr;
if (false == bHavePointSize)
appleFont = ON_Font::AppleCTFontSetSize(appleFont, 0.0, true);
// DEBUGGING TEST
if (nullptr != appleFont)
{
ON_wString postscript_name = ON_Font::AppleCTFontPostScriptName(appleFont);
if ( false == ON_wString::EqualOrdinal(postscript_name,local_name,true) )
return appleFont;
}
return appleFont;
}
CTFontRef ON_Font::AppleCTFont(double pointSize) const
{
const bool bHavePointSize = ( pointSize > 0.0 );
const CGFloat size = (CGFloat)(bHavePointSize ? pointSize : 1000.0 );
CTFontRef appleCTFont = nullptr;
const ON_Font* installed_font = nullptr;
ON_wString tested_names[7];
for(int name_dex = 0; name_dex < 7; name_dex++)
{
ON_wString name;
if ( 2 == name_dex)
{
installed_font = InstalledFont(true);
if (nullptr == installed_font)
installed_font = &ON_Font::Default;
}
switch (name_dex)
{
case 0:
name = m_loc_postscript_name;
break;
case 1:
name = m_en_postscript_name;
break;
case 2:
name = installed_font->m_loc_postscript_name;
break;
case 3:
name = installed_font->m_en_postscript_name;
break;
case 4:
name = installed_font->m_loc_family_name;
break;
case 5:
name = installed_font->m_en_family_name;
break;
case 6:
name = installed_font->Description();
break;
default:
break;
}
if ( name.IsNotEmpty() )
{
for ( int i = 0; i < name_dex; i++ )
{
if (tested_names[name_dex].IsEmpty())
continue;
if ( ON_wString::EqualOrdinal(name,tested_names[name_dex],true) )
{
name = ON_wString::EmptyString;
break;
}
}
}
if ( name.IsEmpty() )
continue;
tested_names[name_dex] = name;
CTFontRef appleCTFontWithName = ON_Font::AppleCTFont(name,size);
if (nullptr == appleCTFontWithName)
continue;
const ON_wString postscript_name = ON_Font::AppleCTFontPostScriptName(appleCTFontWithName);
if ( ON_wString::EqualOrdinal(postscript_name,name,true))
{
if (nullptr != appleCTFont)
CFRelease(appleCTFont);
appleCTFont = appleCTFontWithName;
break;
}
if ( nullptr == appleCTFont )
appleCTFont = appleCTFontWithName;
else
CFRelease(appleCTFontWithName);
}
if ( nullptr != appleCTFont && false == bHavePointSize)
{
// make sure size = font_units_per_em
appleCTFont = ON_Font::AppleCTFontSetSize(appleCTFont, 0.0, true);
}
return appleCTFont;
}
static unsigned int Internal_AppleUnitsPerEm( CTFontRef appleFont, double* pointSizeToUPM )
{
unsigned int font_design_units_per_M = CTFontGetUnitsPerEm(appleFont);
if ( font_design_units_per_M <= 0 )
{
ON_ERROR("CTFontGetUnitsPerEm(appleFont) returned zero.");
font_design_units_per_M = 1000;
}
if (nullptr != pointSizeToUPM)
{
const double point_size = (double)CTFontGetSize(appleFont);
const double UPM = font_design_units_per_M;
// This scaling approach recovers correct values in font design units
// when compared with values from DWrite and FreeType.
*pointSizeToUPM = (point_size > 0.0 && fabs(point_size-UPM) > 0.001 ) ? (UPM/point_size) : 1.0;
}
return font_design_units_per_M;
}
void ON_AppleFontGetFontMetrics(
const ON_Font* font,
ON_FontMetrics& font_metrics
)
{
for (;;)
{
if (nullptr == font)
break;
font = font->ManagedFont();
if (nullptr == font)
break;
CTFontRef appleFont = font->AppleCTFont();
if (nullptr == appleFont)
break;
double pointSizeToUPM = 1.0;
const unsigned int font_design_units_per_M = Internal_AppleUnitsPerEm(appleFont,&pointSizeToUPM);
const double UPM = font_design_units_per_M;
const double leading = pointSizeToUPM*((double)CTFontGetLeading(appleFont));
const double ascent = pointSizeToUPM * ((double)CTFontGetAscent(appleFont));
const CGFloat ct_descent_sign = -1;
const double descent = pointSizeToUPM * ((double)(ct_descent_sign*CTFontGetDescent(appleFont)));
const double line_space1 = ascent - descent + leading;
const double line_space2
= (1.0 != pointSizeToUPM)
? (pointSizeToUPM*((double)(CTFontGetAscent(appleFont)-(ct_descent_sign*CTFontGetDescent(appleFont))+CTFontGetLeading(appleFont))))
: line_space1;
const double line_space = (line_space2>=line_space1) ? line_space2 : line_space1;
const double ascent_of_x = pointSizeToUPM*((double)CTFontGetXHeight(appleFont));
const double ascent_of_capital = pointSizeToUPM*((double)CTFontGetCapHeight(appleFont));
const double underscore_position = pointSizeToUPM*((double)CTFontGetUnderlinePosition(appleFont));
const double underscore_thickness = pointSizeToUPM*((double)CTFontGetUnderlineThickness(appleFont));
font_metrics = ON_FontMetrics::Unset;
font_metrics.SetHeights(
ascent,
descent,
UPM,
line_space
);
if (false == font_metrics.AscentDescentAndUPMAreValid())
break;
font_metrics.SetAscentOfx(ascent_of_x);
font_metrics.SetAscentOfCapital(ascent_of_capital);
font_metrics.SetUnderscore(underscore_position,underscore_thickness);
// Have to fake strikeout settings - not in NSFont API?
//int strikeout_position = ...;
//int strikeout_thickness = ...;
double h = (ascent_of_capital > 0) ? ascent_of_capital : ascent;
if (h > 0.0)
{
font_metrics.SetStrikeout(
(0.5*h),
(0.5*underscore_thickness)
);
}
if (font_metrics.AscentDescentAndUPMAreValid())
return;
break;
}
font_metrics = ON_FontMetrics::Unset;
return;
}
static int Internal_IntFloor(double x)
{
double f = floor(x);
double c = ceil(x);
if ( (1.0 == (c-f)) && ((c-x) <= 1.0e-4 ))
return (int)c;
return (int)f;
}
static int Internal_IntCeil(double x)
{
double f = floor(x);
double c = ceil(x);
if ( (1.0 == (c-f)) && ((x-f) <= 1.0e-4 ))
return (int)f;
return (int)c;
}
bool ON_AppleFontGetGlyphMetrics(
CTFontRef appleFont,
unsigned int glyphIndex,
class ON_TextBox& glyph_metrics
)
{
glyph_metrics = ON_TextBox::Unset;
for(;;)
{
if (0 == glyphIndex)
break;
if (nullptr == appleFont)
break;
const CGGlyph glyphs[2] = {(CGGlyph)glyphIndex,0};
if ( ((unsigned int)glyphs[0]) != glyphIndex )
break; // CGGlyph is a short and glyphIndex is too large.
const CFIndex count = 1;
CGRect boundingRects[2];
CGRect glyphRect = CTFontGetBoundingRectsForGlyphs(appleFont, kCTFontOrientationHorizontal, glyphs, boundingRects, count);
if ( boundingRects[0].size.height != 0.0f || boundingRects[0].size.width != 0.0f )
glyphRect = boundingRects[0];
CGSize advances[2];
double glyphAdvanceX = CTFontGetAdvancesForGlyphs(appleFont, kCTFontOrientationHorizontal, glyphs, advances, count);
if (advances[0].width != 0.0f)
glyphAdvanceX = advances[0].width;
double glyphAdvanceY = CTFontGetAdvancesForGlyphs(appleFont, kCTFontOrientationVertical, glyphs, advances, count);
if (advances[0].width != 0.0f)
glyphAdvanceY = advances[0].width; // width is correct - the vertical advance is returned in width.
//CGSize translations[2];
//CTFontGetVerticalTranslationsForGlyphs(appleFont, glyphs, translations, count);
double pointSizeToUPM = 1.0;
const unsigned int font_design_units_per_M = Internal_AppleUnitsPerEm(appleFont,&pointSizeToUPM);
if (0 == font_design_units_per_M)
pointSizeToUPM = 1.0;
glyph_metrics.m_bbmin.i = Internal_IntFloor(pointSizeToUPM*glyphRect.origin.x);
glyph_metrics.m_bbmin.j = Internal_IntFloor(pointSizeToUPM*glyphRect.origin.y);
glyph_metrics.m_bbmax.i = Internal_IntCeil(pointSizeToUPM*(glyphRect.origin.x + glyphRect.size.width));
glyph_metrics.m_bbmax.j = Internal_IntCeil(pointSizeToUPM*(glyphRect.origin.y + glyphRect.size.height));
glyph_metrics.m_advance.i = Internal_IntCeil(pointSizeToUPM*glyphAdvanceX);
glyph_metrics.m_advance.j = Internal_IntCeil(pointSizeToUPM*glyphAdvanceY);
return true;
}
return false;
}
class ON_AppleGlyphOutlineAccumlator : public ON_OutlineAccumulator
{
public:
ON_AppleGlyphOutlineAccumlator() = default;
~ON_AppleGlyphOutlineAccumlator() = default;
static void Internal_PathApplierFunction(
void *info,
const CGPathElement *element
);
private:
static const ON_2fPoint Internal_Point(CGPoint p);
private:
ON_AppleGlyphOutlineAccumlator(const ON_AppleGlyphOutlineAccumlator&) = delete;
ON_AppleGlyphOutlineAccumlator& operator=(const ON_AppleGlyphOutlineAccumlator&) = delete;
};
const ON_2fPoint ON_AppleGlyphOutlineAccumlator::Internal_Point(CGPoint p)
{
return ON_2fPoint((float)p.x, (float)p.y);
}
void ON_AppleGlyphOutlineAccumlator::Internal_PathApplierFunction(
void *info,
const CGPathElement *element
)
{
if (nullptr == info || nullptr == element)
return;
ON_AppleGlyphOutlineAccumlator* acc = (ON_AppleGlyphOutlineAccumlator*)info;
switch (element->type)
{
case kCGPathElementMoveToPoint:
if (nullptr != element->points)
{
acc->BeginFigure(
ON_OutlineFigurePoint::Type::BeginFigureUnknown,
ON_AppleGlyphOutlineAccumlator::Internal_Point(element->points[0])
);
}
break;
case kCGPathElementAddLineToPoint:
if (nullptr != element->points)
{
acc->AppendLine(
ON_AppleGlyphOutlineAccumlator::Internal_Point(element->points[0])
);
}
break;
case kCGPathElementAddQuadCurveToPoint:
if (nullptr != element->points)
{
acc->AppendQuadraticBezier(
ON_AppleGlyphOutlineAccumlator::Internal_Point(element->points[0]),
ON_AppleGlyphOutlineAccumlator::Internal_Point(element->points[1])
);
}
break;
case kCGPathElementAddCurveToPoint: // cubic bezier
if (nullptr != element->points)
{
acc->AppendCubicBezier(
ON_AppleGlyphOutlineAccumlator::Internal_Point(element->points[0]),
ON_AppleGlyphOutlineAccumlator::Internal_Point(element->points[1]),
ON_AppleGlyphOutlineAccumlator::Internal_Point(element->points[2])
);
}
break;
case kCGPathElementCloseSubpath:
acc->EndFigure(
ON_OutlineFigurePoint::Type::EndFigureClosed
);
break;
default:
break;
};
}
bool ON_AppleFontGetGlyphOutline(
CTFontRef appleFont,
unsigned int glyphIndex,
ON_OutlineFigure::Type figure_type,
class ON_Outline& outline
)
{
outline = ON_Outline::Unset;
if (nullptr == appleFont)
return false;
if ( glyphIndex <= 0 )
return false;
const CGGlyph appleGlyphIndex = (CGGlyph)glyphIndex;
if (((unsigned int)appleGlyphIndex) != glyphIndex)
{
// glyphIndex too large - currently CGGlyph is an unsigned short.
return false;
}
CGPathRef applePath = CTFontCreatePathForGlyph(appleFont, appleGlyphIndex, nullptr);
if (nullptr == applePath)
return false;
unsigned int font_design_units_per_M = CTFontGetUnitsPerEm(appleFont);
const CGFloat point_size = CTFontGetSize(appleFont);
if ( false == (point_size== (CGFloat)font_design_units_per_M))
{
ON_WARNING("CTFontGetSize() != CTFontGetUnitsPerEm()");
font_design_units_per_M =(unsigned int)ceil(point_size);
}
ON_AppleGlyphOutlineAccumlator acc;
acc.BeginGlyphOutline(font_design_units_per_M, figure_type, &outline);
CGPathApply(applePath, &acc, ON_AppleGlyphOutlineAccumlator::Internal_PathApplierFunction);
CGPathRelease(applePath);
acc.EndOutline();
// Add Glyph metrics to outline
ON_TextBox glyph_metrics_in_font_design_units;
bool bHaveGlyphMetrics = ON_AppleFontGetGlyphMetrics(appleFont, appleGlyphIndex, glyph_metrics_in_font_design_units);
if ( bHaveGlyphMetrics )
outline.SetGlyphMetrics(glyph_metrics_in_font_design_units);
else
outline.SetGlyphMetrics(ON_TextBox::Unset);
return true;
}
unsigned int ON_AppleFontGlyphIndex(
CTFontRef appleFont,
unsigned int unicode_code_point
)
{
unsigned int glyphIndex = 0;
for (;;)
{
if (false == ON_IsValidUnicodeCodePoint(unicode_code_point))
break;
ON__UINT16 utf16[3] = {};
const int utf16_count = ON_EncodeUTF16(unicode_code_point, utf16);
if (utf16_count < 1 || utf16_count > 2)
break;
// UniChar is used for Apple UTF-16 endcodings
const UniChar apple_utf16[3] = { (UniChar)utf16[0], (UniChar)utf16[1], (UniChar)0 };
// glyphs[] includes an element for the NULL terminator and an extra element
// in case CTFontGetGlyphsForCharacters is buggy when handling surrogate pairs.
CGGlyph glyphs[3] = {};
if (false == CTFontGetGlyphsForCharacters(appleFont, apple_utf16, glyphs, (NSInteger)utf16_count))
break;
if (0 == glyphs[0])
break;
if (0 != glyphs[1] || 0 != glyphs[2])
break;
glyphIndex = (unsigned int)glyphs[0];
break;
}
return glyphIndex;
}
unsigned int ON_AppleFontGetGlyphMetrics(
const ON_FontGlyph* glyph,
ON_TextBox& glyph_metrics
)
{
glyph_metrics = ON_TextBox::Unset;
if (nullptr == glyph)
return 0;
if (false == glyph->CodePointIsSet())
return 0;
const ON_Font* font = glyph->Font();
if (nullptr == font)
return 0;
CTFontRef appleFont = font->AppleCTFont();
if (nullptr == appleFont)
return 0;
const unsigned int glyph_index = ON_AppleFontGlyphIndex(appleFont, glyph->CodePoint());
if (0 == glyph_index)
return 0;
ON_AppleFontGetGlyphMetrics(appleFont, glyph_index, glyph_metrics);
return glyph_index;
}
bool ON_AppleFontGetGlyphOutline(
const ON_FontGlyph* glyph,
ON_OutlineFigure::Type figure_type,
class ON_Outline& outline
)
{
outline = ON_Outline::Unset;
if (nullptr == glyph)
return false;
if (false == glyph->CodePointIsSet())
return false;
const unsigned int glyphIndex = glyph->FontGlyphIndex();
if (0==glyphIndex)
return false;
const ON_Font* font = glyph->Font();
if (nullptr == font)
return false;
if (ON_OutlineFigure::Type::Unset == figure_type)
{
ON_OutlineFigure::Type font_figure_type = font->OutlineFigureType();
if (ON_OutlineFigure::Type::Unset != font_figure_type)
{
figure_type = font_figure_type;
}
}
#if defined(OPENNURBS_FREETYPE_SUPPORT)
if (
ON_OutlineFigure::Type::SingleStroke == figure_type
// NO // ON_OutlineFigure::Type::DoubleStroke == font_figure_type
)
{
// Apple's Mac OS CTFontCreatePathForGlyph() ignores subfigures that
// are lines. These are common in single stroke fonts. Typical results
// with singel stroke files are things like the R is missing it's right leg,
// h is missing is left side line, and so on.
// Freetype has many bugs, but it can parse some common single stroke files.
// Freetype is basically a file reading utility that can parse outline
// information in older formats of font files. It fails on newer formats
// and it also commonly fails to correctly map UNICODE code points to
// glyph indices. Freetype should only be used as a last resort.
const bool freetype_rc = ON_FreeTypeGetGlyphOutline(
glyph,
glyphIndex,
figure_type,
outline
);
if (freetype_rc)
return true;
}
#endif
CTFontRef appleFont = font->AppleCTFont();
if (nullptr == appleFont)
return false;
const bool rc = ON_AppleFontGetGlyphOutline(
appleFont,
glyphIndex,
figure_type,
outline
);
return rc;
}
void ON_Font::DumpCTFont(
CTFontRef apple_font,
ON_TextLog& text_log
)
{
if (nullptr == apple_font)
{
text_log.Print("CTFont = nullptr\n");
return;
}
text_log.Print("CTFont\n");
text_log.PushIndent();
const ON_wString postscript_name = ON_Font::AppleCTFontPostScriptName(apple_font);
text_log.Print(L"CTFont PostScriptName: \"%ls\"\n",static_cast<const wchar_t*>(postscript_name));
const ON_wString display_name = ON_Font::AppleCTFontDisplayName(apple_font);
text_log.Print(L"CTFont DisplayName: \"%ls\"\n",static_cast<const wchar_t*>(display_name));
const ON_wString family_name = ON_Font::AppleCTFontFamilyName(apple_font);
text_log.Print(L"CTFont FamilyName: \"%ls\"\n",static_cast<const wchar_t*>(family_name));
// Apple NSFont and MacOS do not have "face names" as a font attribute.
// This is the "typeface" name shown in Apple FontBook
const ON_wString fake_face_name = ON_Font::AppleCTFontFaceName(apple_font);
text_log.Print(L"CTFont FontBook typeface: \"%ls\"\n",static_cast<const wchar_t*>(fake_face_name));
const unsigned int UPM = ON_Font::AppleCTFontUnitsPerEm(apple_font);
text_log.Print("CTFont UnitsPerEm: %u\n",UPM);
const double point_size = (double)CTFontGetSize(apple_font);
text_log.Print("CTFont Size: %g points\n",point_size);
#if 0
text_log.Print(L"CTFont Names:\n");
text_log.PushIndent();
CFStringRef keys[] =
{
kCTFontCopyrightNameKey,
kCTFontFamilyNameKey,
kCTFontSubFamilyNameKey,
kCTFontStyleNameKey,
kCTFontUniqueNameKey, // Apple docs say this name is not actually unique
kCTFontFullNameKey,
kCTFontVersionNameKey,
kCTFontPostScriptNameKey,
kCTFontTrademarkNameKey,
kCTFontManufacturerNameKey,
kCTFontDesignerNameKey,
kCTFontDescriptionNameKey,
kCTFontVendorURLNameKey,
kCTFontDesignerURLNameKey,
kCTFontLicenseNameKey,
kCTFontLicenseURLNameKey,
kCTFontSampleTextNameKey,
kCTFontPostScriptCIDNameKey
};
const size_t key_count = sizeof(keys)/sizeof(keys[0]);
text_log.Print(L"CTFont Names:\n");
text_log.PushIndent();
for (size_t i = 0; i < key_count; i++)
{
CFStringRef appleName = CTFontCopyName(apple_font,keys[i]);
const ON_wString key(keys[i]);
const ON_wString name(appleName);
if (nullptr != appleName)
CFRelease(appleName);
if ( name.IsNotEmpty() )
{
text_log.Print(
L"\"%ls\" = CTFontCopyName(...,\"%ls\")\n",
static_cast<const wchar_t*>(name),
static_cast<const wchar_t*>(key)
);
}
}
text_log.PopIndent();
#endif
text_log.PopIndent();
}
#if defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE)
NSFont* ON_Font::AppleTollFreeNSFont(CTFontRef appleCTFont)
{
if (nullptr == appleCTFont)
return nullptr;
NSFont* appleNSFont = (__bridge NSFont*)(appleCTFont);
return appleNSFont;
}
CTFontRef AppleTollFreeCTFont(NSFont* appleNSFont)
{
if (nullptr == appleNSFont)
return nullptr;
CTFontRef appleCTFont = (__bridge CTFontRef)(appleNSFont);
return appleCTFont;
}
//bool ON_Font::SetFromAppleNSFont( NSFont* apple_font, bool bAnnotationFont )
//{
//
// if ( false == ON_FONT_MODIFICATION_PERMITTED )
// return false;
//
// *this = ON_Font::Unset;
//
// const ON_wString postscript_name = ON_Font::PostScriptNameFromAppleNSFont(apple_font);
// const ON_wString family_name = ON_Font::FamilyNameFromAppleNSFont(apple_font);
// const ON_wString face_name = ON_Font::FaceNameFromAppleNSFont(apple_font);
//
// // Set Windows LOGFONT.lfFaceName to something not empty there is some hope this
// // font might work in Rhino 5 for Windows too.
// // https://mcneel.myjetbrains.com/youtrack/issue/RH-37074
// const ON_wString windows_logfont_name = family_name;
//
// const bool rc = postscript_name.IsNotEmpty() || family_name.IsNotEmpty();
// if (rc)
// {
// m_loc_postscript_name = postscript_name;
// m_en_postscript_name = postscript_name;
// m_loc_family_name = family_name;
// m_en_family_name = family_name;
// m_loc_face_name = face_name;
// m_en_face_name = face_name;
// m_loc_windows_logfont_name = windows_logfont_name;
// m_en_windows_logfont_name = windows_logfont_name;
//
// // Can get font metrics from NSFontDescriptor
// // https://developer.apple.com/library/content/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html
// // defaultLineHeight(for theFont: NSFont)
// // fd.xHeight, fd.ascender, fd.descender, fd.capHeight, fd.defaultLineHeightForFont
//
// // 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);
// }
// else if ( 0 != (fd.symbolicTraits & NSFontBoldTrait) )
// SetFontWeight(ON_Font::Weight::Bold);
// else
// SetFontWeight(ON_Font::Weight::Normal);
//
// // Set style - used if this font needs sustution on another computer
// if ( 0 != (fd.symbolicTraits & NSFontItalicTrait) )
// m_font_style = ON_Font::Style::Italic;
// else
// m_font_style = ON_Font::Style::Upright;
//
// if ( 0 != (fd.symbolicTraits & NSFontExpandedTrait) )
// m_font_stretch = ON_Font::Stretch::Expanded;
// else if ( 0 != (fd.symbolicTraits & NSFontCondensedTrait) )
// m_font_stretch = ON_Font::Stretch::Condensed;
// else
// m_font_stretch = ON_Font::Stretch::Medium;
//
// // Saving point size added January 2018.
// const double point_size = (double)apple_font.pointSize;
// m_point_size
// = (false == bAnnotationFont && ON_Font::IsValidPointSize(point_size) && point_size < ((double)ON_Font::AnnotationFontApplePointSize))
// ? point_size
// : 0.0; // indicates annotation size (units per em) will be used
//
// m_logfont_charset = ON_Font::WindowsConstants::logfont_default_charset;
//
// // do this again because some of the above calls can modify description
// m_loc_postscript_name = postscript_name;
// m_en_postscript_name = m_loc_postscript_name;
// m_loc_family_name = family_name;
// m_en_family_name = m_loc_family_name;
// m_loc_face_name = face_name;
// m_en_face_name = m_loc_face_name;
// m_loc_windows_logfont_name = windows_logfont_name;
// m_en_windows_logfont_name = m_loc_windows_logfont_name;
//
// SetFontOrigin(ON_Font::Origin::AppleFont);
// }
//
// return rc;
//}
//const ON_wString ON_Font::PostScriptNameFromAppleNSFont(
// NSFont* apple_font
//)
//{
// if (nullptr == apple_font)
// return ON_wString::EmptyString;
// const ON_String utf8_postscript_name(apple_font.fontName.UTF8String);
// ON_wString postscript_name(utf8_postscript_name);
// postscript_name.TrimLeftAndRight();
// return postscript_name;
//}
//const ON_wString ON_Font::FamilyNameFromAppleNSFont(
// NSFont* apple_font
// )
//{
// if (nullptr == apple_font)
// return ON_wString::EmptyString;
// const ON_String utf8_family_name(apple_font.familyName.UTF8String);
// ON_wString family_name(utf8_family_name);
// family_name.TrimLeftAndRight();
// return family_name;
//}
//const ON_wString ON_Font::AppleDisplayNameFromAppleNSFont(
// NSFont* apple_font
//)
//{
// const ON_String utf8_display_name(apple_font.displayName.UTF8String);
// ON_wString display_name(utf8_display_name);
// display_name.TrimLeftAndRight();
// return display_name;
//}
//const ON_wString ON_Font::FaceNameFromAppleNSFont(
// NSFont* apple_font
// )
//{
// for(;;)
// {
// if (nullptr == apple_font)
// break;
// const ON_wString family_and_face_name = ON_Font::AppleDisplayNameFromAppleNSFont(apple_font);
// const ON_wString family_name = ON_Font::FamilyNameFromAppleNSFont(apple_font);
// return Internal_FaceNameFromAppleDisplayAndFamilyName(family_and_face_name,family_name);
// }
//
// return ON_wString::EmptyString;
//}
//NSFont* ON_Font::AppleNSFont() const
//{
// // Using PointSize() added January 2018.
// const double pointSize
// = ON_Font::IsValidPointSize(m_point_size)
// ? m_point_size
// : 1000.0; // common Apple units per em
//
// NSFont* appleFont = nullptr;
// for(;;)
// {
// appleFont = AppleNSFont(pointSize);
// if ( nullptr == appleFont)
// break;
//
// if ( m_point_size > 0.0 && pointSize == m_point_size)
// break;
//
// const unsigned int upm = CTFontGetUnitsPerEm((CTFontRef)appleFont);
// if (upm <= 0 || fabs(upm - pointSize) <= 0.001 )
// break;
//
// NSFont* appleFont2 = AppleNSFont(upm);
// if (nullptr != appleFont2)
// return appleFont2; // goal is to have point size = UPM so there is no scaling / rounding in metrics and glyph outlines.
//
// break;
// }
//
// return appleFont;
//}
//NSFont* ON_Font::AppleNSFont(double pointSize) const
//{
// const double annotation_font_point_size = (double)ON_Font::Constants::AnnotationFontApplePointSize;
// NSFont* userFont = nullptr;
//
// // TODO - Use ON_Font::InstalledFontFromNames(...) to search installed fonts instead of the following
//
// for (int pass = 0; pass < 2; pass++)
// {
// if ( 0 != pass)
// {
// if ( annotation_font_point_size == pointSize )
// continue; // already tried this point size
// pointSize = annotation_font_point_size;
// }
// if ( false ==(pointSize > 0.0) )
// continue;
//
// // TODO - replace the following with ON_Font::InstalledFontFromNames(....)
//
// for(int ps_loc = 0; ps_loc < 2; ps_loc++)
// {
// // NOTE WELL:
// // The post script name is NOT unique for simulated fonts
// // (Bahnschrift and other OpenType variable fonts being one common source of simulated fonts.)
// const ON_wString postscript_name
// = (0 == ps_loc)
// ? m_loc_postscript_name
// : m_en_postscript_name;
//
// if (ps_loc > 0 && postscript_name == m_loc_postscript_name)
// break;
//
// if (postscript_name.IsNotEmpty())
// {
// // m_apple_font name was a Mac OS font name (NSFont.fontName = Mac OS FontBook "PostScript" name) on some Apple platform device.
// // If the current computer has the same font, this will return the
// // highest fidelity match.
// const ON_String UTF8_postscript_name(postscript_name);
// NSString* fontPostscriptName = [NSString stringWithUTF8String : UTF8_postscript_name];
// userFont = [NSFont fontWithName : fontPostscriptName size : pointSize];
// if (userFont)
// break;
// }
// }
//
// // Try getting a NSFont by using NSFontManager
// NSFontTraitMask traitsStyleStretch = 0;
// if (IsItalic())
// traitsStyleStretch |= NSItalicFontMask;
// if (FontStretch() <= ON_Font::Stretch::Condensed)
// traitsStyleStretch |= NSCondensedFontMask;
// if (FontStretch() >= ON_Font::Stretch::Expanded)
// traitsStyleStretch |= NSExpandedFontMask;
//
// ON_String family_name = FamilyName(); // convert to UTF8
// NSString* fontFamilyName = [NSString stringWithUTF8String : family_name]; // font family name
//
// // https://developer.apple.com/documentation/appkit/nsfontmanager/1462332-fontwithfamily
// // weight
// // A hint for the weight desired, on a scale of 0 to 15:
// // a value of 5 indicates a normal or book weight,
// // and 9 or more a bold or heavier weight.
// // The weight is ignored if fontTraitMask includes NSBoldFontMask.
//
// int weightFromFontWeight;
// switch (FontWeight())
// {
// case ON_Font::Weight::Thin:
// weightFromFontWeight = 2;
// break;
// case ON_Font::Weight::Ultralight:
// weightFromFontWeight = 3;
// break;
// case ON_Font::Weight::Light:
// weightFromFontWeight = 4;
// break;
// case ON_Font::Weight::Normal:
// weightFromFontWeight = 5;
// break;
// case ON_Font::Weight::Medium:
// weightFromFontWeight = 6;
// break;
// case ON_Font::Weight::Semibold:
// weightFromFontWeight = 7;
// break;
// case ON_Font::Weight::Bold:
// weightFromFontWeight = 9;
// break;
// case ON_Font::Weight::Ultrabold:
// weightFromFontWeight = 12;
// break;
// case ON_Font::Weight::Heavy:
// weightFromFontWeight = 15;
// break;
// default:
// weightFromFontWeight = 5;
// break;
// }
// NSFontTraitMask traitsStyleStretchWeight
// = IsBoldInQuartet()
// ? (traitsStyleStretch | NSBoldFontMask)
// : (traitsStyleStretch | NSUnboldFontMask);
// userFont = [[NSFontManager sharedFontManager] fontWithFamily:fontFamilyName traits : traitsStyleStretchWeight weight : weightFromFontWeight size : pointSize];
// if (userFont)
// break;
//
// userFont = [[NSFontManager sharedFontManager] fontWithFamily:fontFamilyName traits : traitsStyleStretch weight : weightFromFontWeight size : pointSize];
// if (userFont)
// break;
//
// if ( 5 != weightFromFontWeight)
// {
// userFont = [[NSFontManager sharedFontManager] fontWithFamily:fontFamilyName traits : traitsStyleStretch weight : 5 size : pointSize];
// if (userFont)
// break;
// }
//
// // Try using just FontFaceName()
// userFont = [NSFont fontWithName : fontFamilyName size : pointSize];
// if (userFont)
// break;
//
// // Cannot find an equivalent font. Just use a system font.
// userFont = [NSFont userFontOfSize : pointSize];
// if (userFont)
// break;
//
// userFont = [NSFont systemFontOfSize : pointSize];
// if (userFont)
// break;
// }
//
// return userFont;
//}
//void ON_Font::DumpNSFont(
// NSFont* apple_font,
// ON_TextLog& text_log
//)
//{
// if (nullptr == apple_font)
// {
// text_log.Print("NSFont = nullptr\n");
// return;
// }
//
// text_log.Print("NSFont\n");
// text_log.PushIndent();
//
// const ON_wString postscript_name = ON_Font::PostScriptNameFromAppleNSFont(apple_font);
// text_log.Print(L"NSFont.fontName: \"%ls\"\n",static_cast<const wchar_t*>(postscript_name));
//
// const ON_wString display_name = ON_Font::AppleDisplayNameFromAppleNSFont(apple_font);
// text_log.Print(L"NSFont.displayName: \"%ls\"\n",static_cast<const wchar_t*>(display_name));
//
// const ON_wString family_name = ON_Font::FamilyNameFromAppleNSFont(apple_font);
// text_log.Print(L"NSFont.familyName: \"%ls\"\n",static_cast<const wchar_t*>(family_name));
//
// // Apple NSFont and MacOS do not have "face names" as a font attribute.
// // This is the "typeface" name shown in Apple FontBook
// const ON_wString fake_face_name = ON_Font::FaceNameFromAppleNSFont(apple_font);
// text_log.Print(L"NSFont FontBook typeface: \"%ls\"\n",static_cast<const wchar_t*>(fake_face_name));
//
// const double point_size = (double)apple_font.pointSize;
// text_log.Print("NSFont.pointSize: %g\n",point_size);
// text_log.PopIndent();
//}
#endif
#endif