mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-04 14:09:41 +08:00
1494 lines
47 KiB
C++
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
|