mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 11:36:09 +08:00
11367 lines
322 KiB
C++
11367 lines
322 KiB
C++
//
|
|
// Copyright (c) 1993-2015 Robert McNeel & Associates. All rights reserved.
|
|
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
|
|
// McNeel & Associates.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
|
|
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
|
|
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
|
|
//
|
|
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#include "opennurbs.h"
|
|
|
|
#if !defined(ON_COMPILING_OPENNURBS)
|
|
// This check is included in all opennurbs source .c and .cpp files to insure
|
|
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
|
|
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
|
|
// and the opennurbs .h files alter what is declared and how it is declared.
|
|
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
|
|
#endif
|
|
|
|
#include "opennurbs_internal_glyph.h"
|
|
#include "opennurbs_win_dwrite.h"
|
|
#include "opennurbs_apple_nsfont.h"
|
|
|
|
ON_PANOSE1::FamilyKind ON_PANOSE1::FamilyKindFromUnsigned(
|
|
unsigned int unsigned_panose_family_kind
|
|
)
|
|
{
|
|
switch (unsigned_panose_family_kind)
|
|
{
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::Any);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::NoFit);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinText);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinScript);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinDecorative);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_PANOSE1::FamilyKind::LatinSymbol);
|
|
default:
|
|
break;
|
|
}
|
|
ON_ERROR("unsigned_pannos1_classification is not valid");
|
|
return ON_PANOSE1::FamilyKind::Any;
|
|
}
|
|
const wchar_t* ON_PANOSE1::FamilyKindToWideString(
|
|
ON_PANOSE1::FamilyKind family_kind
|
|
)
|
|
{
|
|
const wchar_t* s;
|
|
switch (family_kind)
|
|
{
|
|
case ON_PANOSE1::FamilyKind::Any:
|
|
s = L"Any";
|
|
break;
|
|
case ON_PANOSE1::FamilyKind::NoFit:
|
|
s = L"No fit";
|
|
break;
|
|
case ON_PANOSE1::FamilyKind::LatinText:
|
|
s = L"Latin Text";
|
|
break;
|
|
case ON_PANOSE1::FamilyKind::LatinScript:
|
|
s = L"Latin Script";
|
|
break;
|
|
case ON_PANOSE1::FamilyKind::LatinDecorative:
|
|
s = L"Latin Decorative";
|
|
break;
|
|
case ON_PANOSE1::FamilyKind::LatinSymbol:
|
|
s = L"Latin Symbol";
|
|
break;
|
|
default:
|
|
s = L"";
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
bool ON_PANOSE1::IsZero() const
|
|
{
|
|
const ON__UINT8* p = TenBytes();
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
if (0 != p[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_PANOSE1::IsZeroOrOne() const
|
|
{
|
|
const ON__UINT8* p = TenBytes();
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
if (p[i] > 1)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_PANOSE1::IsSet() const
|
|
{
|
|
return IsZeroOrOne() ? false : true;
|
|
}
|
|
|
|
ON_PANOSE1::FamilyKind ON_PANOSE1::PANOSE1FamilyKind() const
|
|
{
|
|
return m_family_kind;
|
|
}
|
|
|
|
const ON__UINT8* ON_PANOSE1::TenBytes() const
|
|
{
|
|
return (const ON__UINT8*)(&m_family_kind);
|
|
}
|
|
|
|
void ON_PANOSE1::SetTenBytes(const ON__UINT8* panose1_ten_bytes)
|
|
{
|
|
SetNineBytes(ON_PANOSE1::FamilyKindFromUnsigned(panose1_ten_bytes[0]), panose1_ten_bytes + 1);
|
|
}
|
|
|
|
void ON_PANOSE1::SetNineBytes(
|
|
ON_PANOSE1::FamilyKind family_kind,
|
|
const ON__UINT8* panose1_properties_bytes
|
|
)
|
|
{
|
|
const ON__UINT8 b0 = static_cast<ON__UINT8>(family_kind);
|
|
if ( b0 > 5)
|
|
{
|
|
*this = ON_PANOSE1::Zero;
|
|
}
|
|
else
|
|
{
|
|
m_family_kind = family_kind;
|
|
m_prop1 = panose1_properties_bytes[0];
|
|
m_prop2 = panose1_properties_bytes[1];
|
|
m_prop3 = panose1_properties_bytes[2];
|
|
m_prop4 = panose1_properties_bytes[3];
|
|
m_prop5 = panose1_properties_bytes[4];
|
|
m_prop6 = panose1_properties_bytes[5];
|
|
m_prop7 = panose1_properties_bytes[6];
|
|
m_prop8 = panose1_properties_bytes[7];
|
|
m_prop9 = panose1_properties_bytes[8];
|
|
}
|
|
}
|
|
|
|
void ON_PANOSE1::Dump(
|
|
class ON_TextLog& text_log
|
|
) const
|
|
{
|
|
if (IsZero())
|
|
{
|
|
text_log.Print(L"PANOSE1::Zero\n");
|
|
}
|
|
else
|
|
{
|
|
ON_wString family = FamilyKindToWideString(m_family_kind);
|
|
if (family.IsEmpty())
|
|
family = ON_wString::FormatToString(L"%u", (unsigned int)m_family_kind);
|
|
text_log.Print(L"PANOSE1: %ls (%u, %u, %u, %u, %u, %u, %u, %u, %u)\n",
|
|
static_cast<const wchar_t*>(family),
|
|
(unsigned int)m_prop1,
|
|
(unsigned int)m_prop2,
|
|
(unsigned int)m_prop3,
|
|
(unsigned int)m_prop4,
|
|
(unsigned int)m_prop5,
|
|
(unsigned int)m_prop6,
|
|
(unsigned int)m_prop7,
|
|
(unsigned int)m_prop8,
|
|
(unsigned int)m_prop9
|
|
);
|
|
}
|
|
}
|
|
|
|
bool ON_PANOSE1::Write(
|
|
class ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
if (false == archive.BeginWrite3dmAnonymousChunk(1))
|
|
return false;
|
|
|
|
bool rc = false;
|
|
for(;;)
|
|
{
|
|
if (!archive.WriteByte(1, &m_family_kind))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop1))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop2))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop3))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop4))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop5))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop6))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop7))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop8))
|
|
break;
|
|
if (!archive.WriteByte(1, &m_prop9))
|
|
break;
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
return rc;
|
|
}
|
|
|
|
bool ON_PANOSE1::Read(
|
|
class ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
int version = 0;
|
|
if (false == archive.BeginRead3dmAnonymousChunk(&version))
|
|
return false;
|
|
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
if (version < 1)
|
|
break;
|
|
if (!archive.ReadByte(1, &m_family_kind))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop1))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop2))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop3))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop4))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop5))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop6))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop7))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop8))
|
|
break;
|
|
if (!archive.ReadByte(1, &m_prop9))
|
|
break;
|
|
if (version <= 1)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (!archive.EndRead3dmChunk())
|
|
rc = false;
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
int ON_FontMetrics::Ascent() const
|
|
{
|
|
return m_ascent;
|
|
}
|
|
|
|
int ON_FontMetrics::Descent() const
|
|
{
|
|
return m_descent;
|
|
}
|
|
|
|
int ON_FontMetrics::LineSpace() const
|
|
{
|
|
return m_line_space;
|
|
}
|
|
|
|
int ON_FontMetrics::UPM() const
|
|
{
|
|
return m_UPM;
|
|
}
|
|
|
|
int ON_FontMetrics::AscentOfI() const
|
|
{
|
|
return AscentOfCapital();
|
|
}
|
|
|
|
int ON_FontMetrics::AscentOfCapital() const
|
|
{
|
|
return (int)m_ascent_of_capital;
|
|
}
|
|
|
|
int ON_FontMetrics::AscentOfx() const
|
|
{
|
|
return (int)m_ascent_of_x;
|
|
}
|
|
|
|
double ON_FontMetrics::GlyphScale(double text_height) const
|
|
{
|
|
// Please consult Dale Lear and the ON_FontGlyph
|
|
// bounding box, outline, and advance calculation code
|
|
// before making any modifications to this function.
|
|
const double y = (double)AscentOfCapital();
|
|
return (text_height > 0.0
|
|
&& text_height < ON_UNSET_POSITIVE_FLOAT
|
|
&& y > 0.0
|
|
&& y < ON_UNSET_POSITIVE_FLOAT
|
|
)
|
|
? (text_height / y)
|
|
: 1.0;
|
|
}
|
|
|
|
int ON_FontMetrics::StrikeoutThickness() const
|
|
{
|
|
return m_strikeout_thickness;
|
|
}
|
|
|
|
int ON_FontMetrics::StrikeoutPosition() const
|
|
{
|
|
return m_strikeout_position;
|
|
}
|
|
|
|
int ON_FontMetrics::UnderscoreThickness() const
|
|
{
|
|
return m_underscore_thickness;
|
|
}
|
|
|
|
int ON_FontMetrics::UnderscorePosition() const
|
|
{
|
|
return m_underscore_position;
|
|
}
|
|
|
|
bool ON_FontMetrics::AscentDescentAndUPMAreValid() const
|
|
{
|
|
const int max_valid = 0xFFFF;
|
|
const int min_valid = -max_valid;
|
|
// basic validation
|
|
if (m_UPM <= 0 || m_UPM >= max_valid)
|
|
return false;
|
|
if (0 == m_ascent && 0 == m_descent)
|
|
return false;
|
|
if (m_ascent <= min_valid || m_ascent >= max_valid)
|
|
return false;
|
|
if (m_descent <= min_valid || m_descent >= max_valid)
|
|
return false;
|
|
if (m_ascent <= m_descent)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ON_FontMetrics::HeightsAreValid() const
|
|
{
|
|
// basic validation
|
|
if (false == AscentDescentAndUPMAreValid() )
|
|
return false;
|
|
|
|
// sanity check
|
|
if (m_line_space < m_ascent - m_descent)
|
|
return false;
|
|
if (((int)m_ascent_of_capital) > m_ascent)
|
|
return false;
|
|
if (((int)m_ascent_of_x) > m_ascent)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_FontMetrics::IsSetAndValid() const
|
|
{
|
|
return (m_ascent_of_capital > 0 && HeightsAreValid());
|
|
}
|
|
|
|
void ON_FontMetrics::SetHeights(
|
|
int ascent,
|
|
int descent,
|
|
int UPM,
|
|
int line_space
|
|
)
|
|
{
|
|
if (ON_UNSET_INT_INDEX < descent && descent < ascent && ascent < -ON_UNSET_INT_INDEX)
|
|
{
|
|
m_ascent = ascent;
|
|
m_descent = descent;
|
|
}
|
|
else
|
|
{
|
|
m_ascent = 0;
|
|
m_descent = 0;
|
|
}
|
|
m_UPM = (UPM > 0 && UPM < -ON_UNSET_INT_INDEX) ? UPM : 0;
|
|
m_line_space = (line_space > 0 && line_space < -ON_UNSET_INT_INDEX)? line_space : 0;
|
|
if (
|
|
m_line_space > 0
|
|
&& m_ascent > 0
|
|
&& m_descent <= m_ascent
|
|
&& m_line_space < m_ascent - m_descent
|
|
&& m_line_space >= m_ascent - m_descent - 1
|
|
)
|
|
{
|
|
// Probably sloppy rounding - just make it work
|
|
m_line_space = m_ascent - m_descent;
|
|
}
|
|
}
|
|
|
|
void ON_FontMetrics::SetAscentOfI(
|
|
int ascent_of_capital
|
|
)
|
|
{
|
|
SetAscentOfCapital(ascent_of_capital);
|
|
}
|
|
|
|
void ON_FontMetrics::SetAscentOfCapital(
|
|
int ascent_of_capital
|
|
)
|
|
{
|
|
m_ascent_of_capital = (ascent_of_capital > 0 && ascent_of_capital <= 0xFFFF) ? ((unsigned short)ascent_of_capital) : 0;
|
|
}
|
|
|
|
void ON_FontMetrics::SetAscentOfx(
|
|
int ascent_of_x
|
|
)
|
|
{
|
|
m_ascent_of_x = (ascent_of_x > 0 && ascent_of_x <= 0xFFFF) ? ((unsigned short)ascent_of_x) : 0;
|
|
}
|
|
|
|
void ON_FontMetrics::SetStrikeout(
|
|
int strikeout_position,
|
|
int strikeout_thickness
|
|
)
|
|
{
|
|
m_strikeout_position = strikeout_position;
|
|
m_strikeout_thickness = strikeout_thickness;
|
|
}
|
|
|
|
void ON_FontMetrics::SetUnderscore(
|
|
int underscore_position,
|
|
int underscore_thickness
|
|
)
|
|
{
|
|
m_underscore_position = underscore_position;
|
|
m_underscore_thickness = underscore_thickness;
|
|
}
|
|
|
|
static int Internal_FontMetricCeil(
|
|
double x
|
|
)
|
|
{
|
|
const double m = (double)0xFFFFFF;
|
|
if (x >= -m && x <= m)
|
|
{
|
|
double i = ceil(x);
|
|
return (int)(((i - x) > 0.9375) ? (i - 1.0) : i);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int Internal_FontMetricFloor(
|
|
double x
|
|
)
|
|
{
|
|
const double m = (double)0xFFFFFF;
|
|
if (x >= -m && x <= m)
|
|
{
|
|
double i = floor(x);
|
|
return (int)(((x - i) > 0.9375) ? (i + 1.0) : i);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int Internal_FontMetricNearest(
|
|
double x
|
|
)
|
|
{
|
|
const double m = (double)0xFFFFFF;
|
|
if (x >= -m && x <= m)
|
|
{
|
|
double i = floor(x);
|
|
return (int)(((x - i) > 0.5) ? (i + 1.0) : i);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ON_FontMetrics::SetHeights(
|
|
double ascent,
|
|
double descent,
|
|
double UPM,
|
|
double line_space
|
|
)
|
|
{
|
|
int iascent = Internal_FontMetricCeil(ascent);
|
|
int idescent = Internal_FontMetricFloor(descent);
|
|
int iUPM = Internal_FontMetricCeil(UPM);
|
|
int iline_space = Internal_FontMetricCeil(line_space);
|
|
if (
|
|
iascent > 0
|
|
&& idescent <= iascent
|
|
&& iline_space < iascent - idescent
|
|
&& line_space >= (ascent - descent - 1.0)
|
|
)
|
|
{
|
|
iline_space = iascent - idescent;
|
|
}
|
|
SetHeights(iascent, idescent, iUPM, iline_space);
|
|
}
|
|
|
|
void ON_FontMetrics::SetAscentOfCapital(
|
|
double ascent_of_capital
|
|
)
|
|
{
|
|
int iascent_of_capital = Internal_FontMetricCeil(ascent_of_capital);
|
|
if (m_ascent < 0 && iascent_of_capital > m_ascent && iascent_of_capital <= m_ascent - 1)
|
|
iascent_of_capital = m_ascent;
|
|
SetAscentOfCapital(iascent_of_capital);
|
|
}
|
|
|
|
void ON_FontMetrics::SetAscentOfx(
|
|
double ascent_of_x
|
|
)
|
|
{
|
|
int iascent_of_x = Internal_FontMetricCeil(ascent_of_x);
|
|
if (m_ascent < 0 && iascent_of_x > m_ascent && iascent_of_x <= m_ascent - 1)
|
|
iascent_of_x = m_ascent;
|
|
SetAscentOfx(iascent_of_x);
|
|
}
|
|
|
|
void ON_FontMetrics::SetStrikeout(
|
|
double strikeout_position,
|
|
double strikeout_thickness
|
|
)
|
|
{
|
|
int istrikeout_thickness =
|
|
(strikeout_thickness > 0.0)
|
|
? Internal_FontMetricCeil(strikeout_thickness)
|
|
: 0;
|
|
if (0 == istrikeout_thickness && strikeout_thickness > 0.0)
|
|
istrikeout_thickness = 1;
|
|
SetStrikeout(Internal_FontMetricNearest(strikeout_position), istrikeout_thickness);
|
|
}
|
|
|
|
void ON_FontMetrics::SetUnderscore(
|
|
double underscore_position,
|
|
double underscore_thickness
|
|
)
|
|
{
|
|
int iunderscore_thickness =
|
|
(underscore_thickness > 0.0)
|
|
? Internal_FontMetricCeil(underscore_thickness)
|
|
: 0;
|
|
if (0 == iunderscore_thickness && underscore_thickness > 0.0)
|
|
iunderscore_thickness = 1;
|
|
SetUnderscore(Internal_FontMetricFloor(underscore_position), iunderscore_thickness);
|
|
}
|
|
|
|
static int Internal_ScaleInt(double scale, int i)
|
|
{
|
|
return (int)((i >= 0) ? ceil(scale*i) : floor(scale*i));
|
|
}
|
|
|
|
const ON_FontMetrics ON_FontMetrics::Scale(
|
|
const ON_FontMetrics& font_metrics,
|
|
double scale
|
|
)
|
|
{
|
|
ON_FontMetrics scaled_font_metrics = font_metrics;
|
|
|
|
if (scale > 0.0 && 1.0 != scale)
|
|
{
|
|
scaled_font_metrics.m_UPM = Internal_ScaleInt(scale, scaled_font_metrics.m_UPM);
|
|
scaled_font_metrics.m_ascent = Internal_ScaleInt(scale, scaled_font_metrics.m_ascent);
|
|
scaled_font_metrics.m_descent = Internal_ScaleInt(scale, scaled_font_metrics.m_descent);
|
|
|
|
scaled_font_metrics.SetAscentOfCapital(Internal_ScaleInt(scale, scaled_font_metrics.m_ascent_of_capital));
|
|
scaled_font_metrics.SetAscentOfx(Internal_ScaleInt(scale, scaled_font_metrics.m_ascent_of_x));
|
|
|
|
if (font_metrics.m_line_space == Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,font_metrics.AscentOfCapital()))
|
|
scaled_font_metrics.m_line_space = Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,scaled_font_metrics.AscentOfCapital());
|
|
else
|
|
scaled_font_metrics.m_line_space = Internal_ScaleInt(scale, scaled_font_metrics.m_line_space);
|
|
|
|
scaled_font_metrics.m_strikeout_thickness = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_thickness);
|
|
scaled_font_metrics.m_strikeout_position = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_position);
|
|
|
|
scaled_font_metrics.m_underscore_thickness = Internal_ScaleInt(scale, scaled_font_metrics.m_underscore_thickness);
|
|
scaled_font_metrics.m_underscore_position = Internal_ScaleInt(scale, scaled_font_metrics.m_underscore_position);
|
|
}
|
|
|
|
return scaled_font_metrics;
|
|
}
|
|
|
|
const ON_FontMetrics ON_FontMetrics::Normalize(
|
|
const ON_FontMetrics& font_metrics
|
|
)
|
|
{
|
|
if (font_metrics.m_UPM == ON_Font::AnnotationFontCellHeight)
|
|
return font_metrics;
|
|
|
|
if (font_metrics.m_UPM <= 0)
|
|
return ON_FontMetrics::Unset;
|
|
|
|
const double scale
|
|
= ((double)ON_Font::AnnotationFontCellHeight / ((double)font_metrics.m_UPM));
|
|
return ON_FontMetrics::Scale(font_metrics, scale);
|
|
}
|
|
|
|
ON_ManagedFonts::ON_ManagedFonts()
|
|
: m_managed_fonts(true)
|
|
, m_installed_fonts(false)
|
|
{}
|
|
|
|
ON_ManagedFonts::~ON_ManagedFonts()
|
|
{
|
|
|
|
ON_SimpleArray<const ON_Font*> managed_fonts(m_installed_fonts.m_by_index);
|
|
managed_fonts.Append(m_managed_fonts.m_by_index.Count(),m_managed_fonts.m_by_index.Array());
|
|
|
|
m_installed_fonts.Internal_EmptyLists();
|
|
m_managed_fonts.Internal_EmptyLists();
|
|
|
|
// last created first deleted - it shouldn't really matter.
|
|
for(int i = managed_fonts.Count()-1; i >= 0; i--)
|
|
{
|
|
ON_Font* managed_font = const_cast<ON_Font*>(managed_fonts[i]);
|
|
if (nullptr == managed_font)
|
|
continue;
|
|
|
|
if (m_default_font_ptr == (ON__UINT_PTR)managed_font)
|
|
{
|
|
// ON_Font::Default is constructed after ON_ManagedFonts::List
|
|
// and destructed before the destruction of ON_ManagedFonts::List.
|
|
// This continue prevents calling delete on
|
|
continue;
|
|
}
|
|
if (1 == managed_font->m_runtime_serial_number)
|
|
{
|
|
// ON_Font::Default.m_runtime_serial_number = 1 and it is the only instance of a font
|
|
// with m_runtime_serial_number = 1.
|
|
// However, the managed_font pointer points to ON_Font::Default, which was destroyed a few miliseconds ago.
|
|
// See opennurbs_statics.cpp and observe that construction order is
|
|
// ..., ON_ManagedFonts::List, ON_Font::Unset, ON_Font::Default, ...
|
|
// and destruction order is
|
|
// ..., ON_Font::Default, ON_Font::Unset, ON_ManagedFonts::List, ...
|
|
ON_ERROR("The m_default_font_ptr test above should have detected ON_Font::Default.");
|
|
continue;
|
|
}
|
|
|
|
// The reset needs to happen for all fonts, including ON_Font::Default
|
|
// Otherwise we get unpredictable crashes when closing because the
|
|
// order in which ON_Font::Default, ON_GlyphMap::GlyphPool and ON_ManagedFonts::List
|
|
// is not predictable.
|
|
managed_font->m_font_glyph_cache.reset();
|
|
|
|
|
|
delete managed_font;
|
|
}
|
|
}
|
|
|
|
class ON_FontGlyphCache
|
|
{
|
|
public:
|
|
static ON_FontGlyphCache* New();
|
|
|
|
void SetFontMetrics(
|
|
const ON_FontMetrics& font_unit_metrics
|
|
);
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
// primary installed font used to get glyphs
|
|
// When the current (Windows) device has a font that
|
|
// matches the ON_Font, this is matching installed font.
|
|
struct IDWriteFont* m_dwrite_font = 0;
|
|
#endif
|
|
|
|
// See ON_Font.FontMetrics() documentation for a discussion
|
|
// of normalized and unnormalized metrics and scales.
|
|
// Both scale values are stored to reduce rounding errors.
|
|
double m_font_unit_to_normalized_scale = 1.0;
|
|
double m_normalized_to_font_unit_scale = 1.0;
|
|
|
|
// Font metrics in the units from the system font defintion.
|
|
// UPM = font design cell height (often 1000 for PostScript, 2014 for TrueType, ...)
|
|
ON_FontMetrics m_font_unit_metrics;
|
|
|
|
// Font metrics normalized so UPM = ON_Font::Constants::AnnotationFontCellHeight
|
|
ON_FontMetrics m_normalized_metrics;
|
|
|
|
// Array of glyphs with sizes and display info
|
|
std::shared_ptr<ON_GlyphMap> m_glyphmap;
|
|
};
|
|
|
|
ON_FontGlyphCache* ON_FontGlyphCache::New()
|
|
{
|
|
ON_FontGlyphCache* font_cache = new ON_FontGlyphCache();
|
|
font_cache->m_glyphmap = std::make_shared<ON_GlyphMap>();
|
|
return font_cache;
|
|
}
|
|
|
|
void ON_FontGlyphCache::SetFontMetrics(
|
|
const ON_FontMetrics& font_unit_metrics
|
|
)
|
|
{
|
|
m_font_unit_metrics = font_unit_metrics;
|
|
|
|
m_normalized_to_font_unit_scale
|
|
= font_unit_metrics.UPM() > 0
|
|
? (((double)font_unit_metrics.UPM()) / ((double)ON_Font::Constants::AnnotationFontCellHeight))
|
|
: 0.0;
|
|
m_font_unit_to_normalized_scale
|
|
= m_normalized_to_font_unit_scale > 0.0
|
|
? (((double)ON_Font::Constants::AnnotationFontCellHeight) / ((double)font_unit_metrics.UPM()))
|
|
: 0.0;
|
|
|
|
m_normalized_metrics
|
|
= (m_font_unit_to_normalized_scale > 0.0 && 1.0 != m_font_unit_to_normalized_scale)
|
|
? ON_FontMetrics::Scale(m_font_unit_metrics, m_font_unit_to_normalized_scale)
|
|
: m_font_unit_metrics;
|
|
}
|
|
|
|
static const ON_Font* Internal_BinarySearchForManagedFontSerialNumber( unsigned int key, const ON_Font*const* base, size_t nel )
|
|
{
|
|
if (nel > 0 && nullptr != base )
|
|
{
|
|
size_t i;
|
|
const ON_Font* font;
|
|
unsigned int d;
|
|
|
|
// The end tests are not necessary, but they
|
|
// seem to provide overall speed improvement
|
|
// for the types of searches that call this
|
|
// function.
|
|
font = base[0];
|
|
d = font->RuntimeSerialNumber();
|
|
if ( key < d )
|
|
return nullptr;
|
|
if ( key == d )
|
|
return font;
|
|
|
|
font = base[nel-1];
|
|
d = font->RuntimeSerialNumber();
|
|
if ( key > d )
|
|
return nullptr;
|
|
if ( key == d )
|
|
return font;
|
|
|
|
while ( nel > 0 )
|
|
{
|
|
i = nel/2;
|
|
font = base[i];
|
|
d = font->RuntimeSerialNumber();
|
|
if ( key < d )
|
|
{
|
|
nel = i;
|
|
}
|
|
else if ( key > d )
|
|
{
|
|
i++;
|
|
base += i;
|
|
nel -= i;
|
|
}
|
|
else
|
|
{
|
|
return font;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const ON_Font* ON_ManagedFonts::GetFromSerialNumber(
|
|
unsigned int managed_font_serial_number
|
|
)
|
|
{
|
|
if (0 == m_managed_fonts.Count())
|
|
{
|
|
// Put ON_Font::Default as the first entry in this list.
|
|
Internal_AddManagedFont(&ON_Font::Default, nullptr);
|
|
}
|
|
if (managed_font_serial_number < 1)
|
|
{
|
|
ON_ERROR("managed_font_serial_number parameter must be >= 1");
|
|
return nullptr;
|
|
}
|
|
|
|
if ( managed_font_serial_number == ON_Font::Default.RuntimeSerialNumber() )
|
|
return &ON_Font::Default;
|
|
|
|
const ON_Font* const * managed_fonts = m_managed_fonts.m_by_index.Array();
|
|
const unsigned int font_count = m_managed_fonts.m_by_index.UnsignedCount();
|
|
|
|
if (managed_font_serial_number <= font_count
|
|
&& managed_font_serial_number == managed_fonts[managed_font_serial_number - 1]->RuntimeSerialNumber())
|
|
{
|
|
// This test should always find the managed font as long as the current numbering scheme is used.
|
|
return managed_fonts[managed_font_serial_number - 1];
|
|
}
|
|
|
|
return
|
|
(font_count > 0)
|
|
? Internal_BinarySearchForManagedFontSerialNumber(managed_font_serial_number, managed_fonts, font_count)
|
|
: nullptr;
|
|
}
|
|
|
|
const ON_Font* ON_ManagedFonts::GetFromFontCharacteristics(
|
|
const ON_Font& font_characteristics,
|
|
bool bCreateIfNotFound
|
|
)
|
|
{
|
|
if ( 0 == m_managed_fonts.Count() )
|
|
{
|
|
// Put ON_Font::Default as the first entry in this list.
|
|
Internal_AddManagedFont( &ON_Font::Default, nullptr );
|
|
}
|
|
|
|
if (font_characteristics.IsManagedFont())
|
|
return &font_characteristics;
|
|
|
|
|
|
const bool bIsUnderlined = font_characteristics.IsUnderlined();
|
|
const bool bIsStrikethrough = font_characteristics.IsStrikethrough();
|
|
const double point_size
|
|
= ON_Font::IsValidPointSize(font_characteristics.PointSize())
|
|
? font_characteristics.PointSize()
|
|
: 0.0;
|
|
|
|
const ON_Font* set_font_characteristics = &font_characteristics;
|
|
|
|
std::unique_ptr< ON_Font > fup;
|
|
if (false == font_characteristics.IsInstalledFont())
|
|
{
|
|
bool bHaveName
|
|
= font_characteristics.PostScriptName().IsNotEmpty()
|
|
|| font_characteristics.WindowsLogfontName().IsNotEmpty()
|
|
|| font_characteristics.FamilyName().IsNotEmpty();
|
|
if ( false == bHaveName || font_characteristics.HasUnsetProperties(false, false) )
|
|
{
|
|
fup = std::make_unique< ON_Font >(font_characteristics);
|
|
ON_Font* temporary_font = fup.get();
|
|
if (nullptr != temporary_font)
|
|
{
|
|
if (false == bHaveName)
|
|
{
|
|
*temporary_font = ON_Font::Default;
|
|
}
|
|
else
|
|
{
|
|
temporary_font->SetUnsetProperties(ON_Font::Default, true);
|
|
}
|
|
temporary_font->SetUnderlined(bIsUnderlined);
|
|
temporary_font->SetStrikethrough(bIsStrikethrough);
|
|
temporary_font->SetPointSize(point_size);
|
|
set_font_characteristics = temporary_font;
|
|
}
|
|
}
|
|
|
|
if (false == set_font_characteristics->IsValid())
|
|
return &ON_Font::Default;
|
|
}
|
|
|
|
ON_Font::NameLocale name_locale = ON_Font::NameLocale::LocalizedFirst;
|
|
|
|
for (;;)
|
|
{
|
|
// quick test for default font that occurs often enough to warrent the special checking
|
|
if (bIsUnderlined)
|
|
break;
|
|
if (bIsStrikethrough)
|
|
break;
|
|
if (0.0 != point_size && point_size != ON_Font::Default.m_point_size)
|
|
break;
|
|
|
|
if (false == ON_Font::EqualWeightStretchStyle(&ON_Font::Default, set_font_characteristics, false))
|
|
break; // there are multiple weight-stretch-style settings for the same family name and same LOGFONT.lfFaceName.
|
|
|
|
ON_wString name = set_font_characteristics->PostScriptName(name_locale);
|
|
if (name.IsNotEmpty())
|
|
{
|
|
if (ON_wString::EqualOrdinal(name, ON_Font::Default.PostScriptName(name_locale), true))
|
|
{
|
|
return &ON_Font::Default;
|
|
}
|
|
break; // Different PostScript names
|
|
}
|
|
|
|
name = set_font_characteristics->WindowsLogfontName(name_locale);
|
|
if (name.IsNotEmpty())
|
|
{
|
|
if (ON_wString::EqualOrdinal(name, ON_Font::Default.WindowsLogfontName(name_locale), true))
|
|
{
|
|
return &ON_Font::Default;
|
|
}
|
|
break; // Different LOGFONT settings
|
|
}
|
|
|
|
name = set_font_characteristics->FamilyName(name_locale);
|
|
if (name.IsNotEmpty())
|
|
{
|
|
if (ON_wString::EqualOrdinal(name, ON_Font::Default.FamilyName(name_locale), true))
|
|
{
|
|
name = set_font_characteristics->FaceName(name_locale);
|
|
if (
|
|
name.IsEmpty()
|
|
|| ON_wString::EqualOrdinal(name, ON_Font::Default.FaceName(name_locale), true)
|
|
)
|
|
{
|
|
return &ON_Font::Default;
|
|
}
|
|
}
|
|
break; // Different faces
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
const ON_Font* managed_font = m_managed_fonts.FromFontProperties(
|
|
set_font_characteristics,
|
|
true,
|
|
true,
|
|
bIsUnderlined,
|
|
bIsStrikethrough,
|
|
point_size
|
|
);
|
|
|
|
unsigned int managed_font_wss_dev = ON_Font::WeightStretchStyleDeviation(
|
|
set_font_characteristics,
|
|
managed_font
|
|
);
|
|
|
|
if (
|
|
nullptr != managed_font
|
|
&& 0 == managed_font_wss_dev
|
|
&& point_size == managed_font->PointSize()
|
|
)
|
|
{
|
|
return managed_font;
|
|
}
|
|
|
|
|
|
for(;;)
|
|
{
|
|
if (set_font_characteristics->IsInstalledFont())
|
|
break;
|
|
|
|
const ON_Font* installed_font
|
|
= m_installed_fonts.FromFontProperties(
|
|
set_font_characteristics,
|
|
true,
|
|
true
|
|
);
|
|
if (nullptr == installed_font)
|
|
break;
|
|
|
|
set_font_characteristics = installed_font;
|
|
if (bIsUnderlined || bIsStrikethrough || point_size > 0.0)
|
|
{
|
|
// Need a copy to set underlined/strikethrough/point_size
|
|
fup = std::make_unique< ON_Font >(*installed_font);
|
|
ON_Font* temporary_font = fup.get();
|
|
if (nullptr != temporary_font)
|
|
{
|
|
temporary_font->SetUnderlined(bIsUnderlined);
|
|
temporary_font->SetStrikethrough(bIsStrikethrough);
|
|
temporary_font->SetPointSize(point_size);
|
|
set_font_characteristics = temporary_font;
|
|
}
|
|
}
|
|
|
|
// Look again now that we have settings known to be correct.
|
|
managed_font = m_managed_fonts.FromFontProperties(
|
|
set_font_characteristics,
|
|
true,
|
|
true,
|
|
bIsUnderlined,
|
|
bIsStrikethrough,
|
|
point_size
|
|
);
|
|
|
|
managed_font_wss_dev = ON_Font::WeightStretchStyleDeviation(
|
|
set_font_characteristics->FontWeight(),
|
|
set_font_characteristics->FontStretch(),
|
|
set_font_characteristics->FontStyle(),
|
|
managed_font
|
|
);
|
|
|
|
if (nullptr != managed_font && 0 == managed_font_wss_dev)
|
|
return managed_font;
|
|
|
|
break;
|
|
}
|
|
|
|
if (false == bCreateIfNotFound)
|
|
return nullptr;
|
|
|
|
// The managed font constructor does not copy user data, m_font_index, m_font_id, m_gonna_change_font_cache
|
|
ON_MemoryAllocationTracking disable_tracking(false);
|
|
const ON_FontMetrics* font_metrics = nullptr;
|
|
const ON_FontGlyphCache* font_glyph_cache = set_font_characteristics->m_font_glyph_cache.get();
|
|
if (nullptr != font_glyph_cache)
|
|
font_metrics = &font_glyph_cache->m_font_unit_metrics;
|
|
managed_font = Internal_AddManagedFont(
|
|
new ON_Font( ON_Font::FontType::ManagedFont, *set_font_characteristics),
|
|
font_metrics
|
|
);
|
|
|
|
return managed_font;
|
|
}
|
|
|
|
static void Internal_AddManagedFontSingleRefCountString(
|
|
const ON_wString& s
|
|
)
|
|
{
|
|
const ON_wString dirty(s);
|
|
ON_wString& clean = const_cast<ON_wString&>(s);
|
|
clean.Destroy();
|
|
clean = static_cast<const wchar_t*>(dirty);
|
|
}
|
|
|
|
const ON_Font* ON_ManagedFonts::Internal_AddManagedFont(
|
|
const ON_Font* managed_font,
|
|
const ON_FontMetrics* managed_font_metrics_in_font_design_units
|
|
)
|
|
{
|
|
// All memory allocated for managed fonts is permanent app workspace memory.
|
|
ON_MemoryAllocationTracking disable_tracking(false);
|
|
|
|
if (0 == m_default_font_ptr && 1 == managed_font->m_runtime_serial_number)
|
|
{
|
|
// ON_Font::Default is constructed after ON_ManagedFonts::List and
|
|
// destroyed before ON_ManagedFonts::List. Therefore ~ON_ManagedFonts()
|
|
// has a pointer to ON_Font::Default that must be ignored during
|
|
// destruction.
|
|
m_default_font_ptr = (ON__UINT_PTR)managed_font;
|
|
}
|
|
|
|
const ON_Font* installed_font = InstalledFonts().FromFontProperties(managed_font, true, true);
|
|
if (nullptr != installed_font)
|
|
managed_font->m_managed_face_is_installed = 1;
|
|
else
|
|
managed_font->m_managed_face_is_installed = 2;
|
|
|
|
ON_FontGlyphCache* font_cache = managed_font->m_font_glyph_cache.get();
|
|
if (nullptr == font_cache)
|
|
{
|
|
/////////////////////
|
|
//
|
|
// Put the cached glyph information here so we only have one set for each font
|
|
//
|
|
font_cache = ON_FontGlyphCache::New();
|
|
managed_font->m_font_glyph_cache = std::shared_ptr<ON_FontGlyphCache>(font_cache);
|
|
}
|
|
|
|
if (nullptr != font_cache)
|
|
{
|
|
if (false == font_cache->m_font_unit_metrics.HeightsAreValid())
|
|
{
|
|
ON_FontMetrics font_metrics_in_font_design_units;
|
|
|
|
if (
|
|
nullptr != managed_font_metrics_in_font_design_units
|
|
&& managed_font_metrics_in_font_design_units->HeightsAreValid()
|
|
)
|
|
{
|
|
font_metrics_in_font_design_units = *managed_font_metrics_in_font_design_units;
|
|
}
|
|
else
|
|
{
|
|
ON_ManagedFonts::GetFontMetricsInFontDesignUnits(
|
|
managed_font,
|
|
font_metrics_in_font_design_units
|
|
);
|
|
}
|
|
|
|
font_cache->SetFontMetrics(font_metrics_in_font_design_units);
|
|
}
|
|
}
|
|
|
|
// Insulate strings in managed fonts from outside damage.
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_locale_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_postscript_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_en_postscript_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_family_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_en_family_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_face_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_en_face_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_windows_logfont_name);
|
|
Internal_AddManagedFontSingleRefCountString(managed_font->m_loc_windows_logfont_name);
|
|
|
|
m_managed_fonts.AddFont(managed_font,false);
|
|
|
|
return managed_font;
|
|
}
|
|
|
|
#define ON_MANAGED_FONT_CHECK(falure_return_value) {if (0 != m_managed_font) (ON_ERROR("Cannot modify managed fonts."); return (falure_return_value);}}
|
|
|
|
//
|
|
// END list of managed ON_Fonts
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// BEGIN list of platform ON_Fonts
|
|
//
|
|
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
|
|
////static bool ON_Internal_WindowsGetGlyphMetricsEx(
|
|
//// LOGFONT& logfont,
|
|
//// int logfont_height,
|
|
//// //MAT2 mat2,
|
|
//// ON__UINT32 unicode_code_point,
|
|
//// class ON_TextBox& font_unit_glyph_box
|
|
////)
|
|
////{
|
|
//// font_unit_glyph_box = ON_TextBox::Unset;
|
|
////
|
|
//// if (logfont_height <= 0)
|
|
//// logfont_height = ON_Font::Constants::AnnotationFontCellHeight;
|
|
////
|
|
//// if (false == ON_IsValidUnicodeCodePoint(unicode_code_point) )
|
|
//// return false;
|
|
////
|
|
//// if (0 == logfont.lfFaceName[0] )
|
|
//// return false;
|
|
////
|
|
//// wchar_t w[8] = { 0 };
|
|
//// const size_t w_capacity = (sizeof(w) / sizeof(w[0])) - 1;
|
|
//// const int w_count = ON_EncodeWideChar(unicode_code_point, w_capacity, w);
|
|
//// if ( 0 == w[0] || w_count <= 0 || w_count > (int)w_capacity )
|
|
//// return false;
|
|
////
|
|
//// HDC font_hdc = nullptr;
|
|
//// HFONT hfont = nullptr;
|
|
//// HGDIOBJ hfont0 = nullptr;
|
|
////
|
|
//// for (;;)
|
|
//// {
|
|
//// font_hdc = ON_Font::CreateWindowsLogfontDeviceContext();
|
|
//// if (nullptr == font_hdc)
|
|
//// break;
|
|
////
|
|
//// LOGFONT lf = logfont;
|
|
//// lf.lfHeight = logfont_height;
|
|
//// hfont = ::CreateFontIndirect(&lf);
|
|
//// if (nullptr == hfont)
|
|
//// break;
|
|
//// hfont0 = ::SelectObject(font_hdc, hfont);
|
|
////
|
|
//// ON__UINT16 glyphindex[sizeof(w)/sizeof(w[0])] = { 0 };
|
|
//// const DWORD gicount = ::GetGlyphIndices(font_hdc, w, w_count, glyphindex, GGI_MARK_NONEXISTING_GLYPHS);
|
|
//// if (GDI_ERROR == gicount)
|
|
//// break;
|
|
//// if (0xffff == glyphindex[0] || 0xffff == glyphindex[1])
|
|
//// {
|
|
//// // March 2017 Dale Lear
|
|
//// // https://mcneel.myjetbrains.com/youtrack/issue/RH-38377
|
|
//// //
|
|
//// // The GGI_MARK_NONEXISTING_GLYPHS flag causes GetGlyphIndices()
|
|
//// // to set glyphindex[] values to 0xFFFF for missing glyphs.
|
|
//// //
|
|
//// // GetGlyphIndices() is not capable of getting this glyph in the hdc font.
|
|
//// // This often happes for surrogate pair encodings, even when the
|
|
//// // glyph does exist in the font.
|
|
//// //
|
|
//// // GetGlyphIndices cannot find glyph in the font
|
|
//// // Often a surrogate pair and we probably need to be using
|
|
//// // Windows newer Uniscribe API instead of the tired old
|
|
//// // GetGlyphIndices + GetGlyphOutlineW stuff.
|
|
//// break;
|
|
//// }
|
|
////
|
|
//// MAT2 mat2;
|
|
//// memset(&mat2, 0, sizeof(mat2));
|
|
//// mat2.eM11.fract = 0;
|
|
//// mat2.eM11.value = 1;
|
|
//// mat2.eM12.fract = 0;
|
|
//// mat2.eM12.value = 0;
|
|
//// mat2.eM21.fract = 0;
|
|
//// mat2.eM21.value = 0;
|
|
//// mat2.eM22.fract = 0;
|
|
//// mat2.eM22.value = 1;
|
|
////
|
|
//// GLYPHMETRICS glm;
|
|
//// memset(&glm, 0, sizeof(glm));
|
|
//// const DWORD cb_size = ::GetGlyphOutlineW(font_hdc, glyphindex[0], GGO_NATIVE | GGO_GLYPH_INDEX, &glm, 0, nullptr, &mat2);
|
|
//// if (GDI_ERROR == cb_size)
|
|
//// break;
|
|
////
|
|
//// font_unit_glyph_box.m_advance.i = glm.gmCellIncX;
|
|
//// font_unit_glyph_box.m_advance.j = glm.gmCellIncY;
|
|
//// if (cb_size == 0)
|
|
//// {
|
|
//// // Non-printing char - nothing to draw
|
|
//// font_unit_glyph_box.m_bbmin.i = 0;
|
|
//// font_unit_glyph_box.m_bbmin.j = 0;
|
|
//// font_unit_glyph_box.m_bbmax.i = font_unit_glyph_box.m_advance.i;
|
|
//// font_unit_glyph_box.m_bbmax.j = font_unit_glyph_box.m_advance.j;
|
|
//// }
|
|
//// else
|
|
//// {
|
|
//// font_unit_glyph_box.m_bbmin.i = glm.gmptGlyphOrigin.x;
|
|
//// font_unit_glyph_box.m_bbmin.j = glm.gmptGlyphOrigin.y - (int)glm.gmBlackBoxY;
|
|
//// font_unit_glyph_box.m_bbmax.i = (int)glm.gmBlackBoxX + glm.gmptGlyphOrigin.x;
|
|
//// font_unit_glyph_box.m_bbmax.j = glm.gmptGlyphOrigin.y;
|
|
//// }
|
|
////
|
|
//// break;
|
|
//// }
|
|
////
|
|
//// if (nullptr != font_hdc)
|
|
//// {
|
|
//// if (nullptr != hfont)
|
|
//// {
|
|
//// ::SelectObject(font_hdc, hfont0);
|
|
//// ::DeleteObject(hfont);
|
|
//// }
|
|
//// ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc);
|
|
//// }
|
|
////
|
|
//// return font_unit_glyph_box.IsSet();
|
|
////}
|
|
|
|
|
|
|
|
class ON_InternalLogFontAndTextMetric
|
|
{
|
|
public:
|
|
ON_InternalLogFontAndTextMetric() = default;
|
|
~ON_InternalLogFontAndTextMetric() = default;
|
|
ON_InternalLogFontAndTextMetric(const ON_InternalLogFontAndTextMetric&) = default;
|
|
ON_InternalLogFontAndTextMetric& operator=(const ON_InternalLogFontAndTextMetric&) = default;
|
|
|
|
static const ON_InternalLogFontAndTextMetric Unset;
|
|
|
|
public:
|
|
ON_SHA1_Hash m_font_hash = ON_SHA1_Hash::ZeroDigest;
|
|
LOGFONT m_lfX;
|
|
TEXTMETRIC m_tmX;
|
|
};
|
|
|
|
ON_InternalLogFontAndTextMetric Internal_InitUnset()
|
|
{
|
|
ON_InternalLogFontAndTextMetric lftm;
|
|
memset(&lftm, 0, sizeof(lftm));
|
|
return lftm;
|
|
}
|
|
|
|
const ON_InternalLogFontAndTextMetric ON_InternalLogFontAndTextMetric::Unset(Internal_InitUnset());
|
|
|
|
////class ON_Font_Windows_LFTM_UserData : public ON_UserData
|
|
////{
|
|
////public:
|
|
//// static void Set(
|
|
//// const LOGFONT& lf,
|
|
//// const TEXTMETRIC& tm,
|
|
//// ON_Font* font
|
|
//// )
|
|
//// {
|
|
//// if (nullptr == font || 0 == lf.lfFaceName[0])
|
|
//// return;
|
|
//// ON_UserData* ud = font->GetUserData(ON_Font_Windows_LFTM_UserData::Id);
|
|
//// ON_Font_Windows_LFTM_UserData* lftm = ON_Font_Windows_LFTM_UserData::Cast(ud);
|
|
//// if (nullptr == lftm)
|
|
//// {
|
|
//// if (nullptr != ud)
|
|
//// delete ud;
|
|
//// ud = nullptr;
|
|
//// lftm = new ON_Font_Windows_LFTM_UserData();
|
|
//// }
|
|
//// lftm->m_font_hash = font->FontCharacteristicsHash();
|
|
//// lftm->m_lf = lf;
|
|
//// lftm->m_lftm = tm;
|
|
//// if (nullptr == ud)
|
|
//// {
|
|
//// if (false == font->AttachUserData(lftm))
|
|
//// delete lftm;
|
|
//// }
|
|
//// return;
|
|
//// }
|
|
////
|
|
//// static ON_InternalLogFontAndTextMetric Get(
|
|
//// const ON_Font* font
|
|
//// );
|
|
////
|
|
//// ON_Font_Windows_LFTM_UserData()
|
|
//// {
|
|
//// m_userdata_uuid = ON_Font_Windows_LFTM_UserData::Id;
|
|
//// m_userdata_copycount = 1;
|
|
//// }
|
|
//// ~ON_Font_Windows_LFTM_UserData() = default;
|
|
//// ON_Font_Windows_LFTM_UserData(const ON_Font_Windows_LFTM_UserData&) = default;
|
|
//// ON_Font_Windows_LFTM_UserData& operator=(const ON_Font_Windows_LFTM_UserData&) = default;
|
|
////
|
|
//// ON_OBJECT_DECLARE(ON_Font_Windows_LFTM_UserData);
|
|
////
|
|
////private:
|
|
////// {452ADA1A-49F0-4FAF-8E14-73DABE21FC5C}
|
|
//// static const ON_UUID Id;
|
|
////
|
|
////private:
|
|
//// ON_SHA1_Hash m_font_hash = ON_SHA1_Hash::ZeroDigest;
|
|
//// ON_InternalLogFontAndTextMetric m_lftm = ON_InternalLogFontAndTextMetric::Unset;
|
|
////};
|
|
////
|
|
////// {452ADA1A-49F0-4FAF-8E14-73DABE21FC5C}
|
|
////const ON_UUID ON_Font_Windows_LFTM_UserData::Id = { 0x452ada1a, 0x49f0, 0x4faf, { 0x8e, 0x14, 0x73, 0xda, 0xbe, 0x21, 0xfc, 0x5c } };
|
|
////ON_OBJECT_IMPLEMENT(ON_Font_Windows_LFTM_UserData, ON_UserData, "452ADA1A-49F0-4FAF-8E14-73DABE21FC5C");
|
|
////
|
|
////void ON_Font_Windows_LFTM_UserData::Set(
|
|
//// const LOGFONT& lf,
|
|
//// const TEXTMETRIC& tf,
|
|
//// ON_Font* font
|
|
////)
|
|
////{
|
|
////}
|
|
////
|
|
////ON_InternalLogFontAndTextMetric ON_Font_Windows_LFTM_UserData::Get(
|
|
//// const ON_Font* font
|
|
////)
|
|
////{
|
|
////}
|
|
|
|
static int CALLBACK Internal_GetPlatformWindowsLogfontFontList_CallbackProc(
|
|
const LOGFONT *lf,
|
|
const TEXTMETRIC *ignored_tm,
|
|
DWORD font_type,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == lf )
|
|
break;
|
|
if (font_type != TRUETYPE_FONTTYPE)
|
|
break;
|
|
if (0 == lParam)
|
|
break;
|
|
if (L'@' == lf->lfFaceName[0])
|
|
break;
|
|
|
|
((ON_SimpleArray<LOGFONT>*)lParam)->AppendNew() = *lf;
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void Internal_CleanLOGFONT(
|
|
LOGFONT& lf,
|
|
bool bZero
|
|
)
|
|
{
|
|
lf.lfHeight = 0;
|
|
lf.lfWidth = 0;
|
|
lf.lfEscapement = 0;
|
|
lf.lfOrientation = 0;
|
|
if (bZero)
|
|
{
|
|
lf.lfWeight = 0;
|
|
lf.lfItalic = 0;
|
|
lf.lfUnderline = 0;
|
|
lf.lfStrikeOut = 0;
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
lf.lfOutPrecision = 0;
|
|
lf.lfClipPrecision = 0;
|
|
lf.lfQuality = 0;
|
|
lf.lfPitchAndFamily = 0;
|
|
}
|
|
else
|
|
{
|
|
// do not change lfWeight
|
|
if (0 != lf.lfItalic)
|
|
lf.lfItalic = 1;
|
|
if (0 != lf.lfUnderline)
|
|
lf.lfUnderline = 1;
|
|
if (0 != lf.lfStrikeOut)
|
|
lf.lfStrikeOut = 1;
|
|
if (SYMBOL_CHARSET != lf.lfCharSet)
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
lf.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis;
|
|
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
lf.lfQuality = ON_Font::WindowsConstants::logfont_quality;
|
|
lf.lfPitchAndFamily = ON_Font::WindowsConstants::logfont_pitch_and_family;
|
|
}
|
|
}
|
|
|
|
static int Internal_CompareLogfont(const LOGFONT* lhs, const LOGFONT* rhs)
|
|
{
|
|
if (lhs == rhs)
|
|
return 0;
|
|
if (nullptr == lhs)
|
|
return 1;
|
|
if (nullptr == rhs)
|
|
return -1;
|
|
|
|
// compare font family name
|
|
const size_t face_name_capacity = sizeof(lhs->lfFaceName) / sizeof(lhs->lfFaceName[0]);
|
|
int rc = ON_wString::CompareOrdinal(
|
|
lhs->lfFaceName,
|
|
ON_wString::Length(lhs->lfFaceName,face_name_capacity),
|
|
rhs->lfFaceName,
|
|
ON_wString::Length(rhs->lfFaceName,face_name_capacity),
|
|
true
|
|
);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
// Compare style (upright, italic, ...)
|
|
const int lhs_italic = (0 != lhs->lfItalic) ? 1 : 0;
|
|
const int rhs_italic = (0 != rhs->lfItalic) ? 1 : 0;
|
|
rc = lhs_italic - rhs_italic;
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
// compare weight (normal, bold, ...)
|
|
if (lhs->lfWeight < rhs->lfWeight)
|
|
return -1;
|
|
if (lhs->lfWeight > rhs->lfWeight)
|
|
return 1;
|
|
|
|
// compare charset last - SYMBOL_CHARSET require special treatment
|
|
const int lhs_charset = (SYMBOL_CHARSET == lhs->lfCharSet) ? 1 : 0;
|
|
const int rhs_charset = (SYMBOL_CHARSET == rhs->lfCharSet) ? 1 : 0;
|
|
rc = lhs_charset - rhs_charset;
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
unsigned int ON_Font::GetInstalledWindowsLogFonts(
|
|
ON_SimpleArray<LOGFONT>& logfont_list
|
|
)
|
|
{
|
|
logfont_list.SetCount(0);
|
|
// https://en.wikipedia.org/wiki/List_of_typefaces_included_with_Microsoft_Windows
|
|
|
|
HDC font_hdc = ON_Font::CreateWindowsLogfontDeviceContext();
|
|
if (nullptr == font_hdc)
|
|
return 0;
|
|
const DWORD flags = 0;
|
|
|
|
// To enumerate all installed Windows LOGFONT values
|
|
|
|
// Get list of font family names
|
|
LOGFONT lf;
|
|
memset(&lf, 0, sizeof(lf));
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
ON_SimpleArray<LOGFONT> font_family_list(1000);
|
|
::EnumFontFamiliesEx(
|
|
font_hdc,
|
|
&lf,
|
|
Internal_GetPlatformWindowsLogfontFontList_CallbackProc,
|
|
(LPARAM)(&font_family_list),
|
|
flags
|
|
);
|
|
|
|
// remove duplicates
|
|
for (int i = 0; i < font_family_list.Count(); i++)
|
|
{
|
|
Internal_CleanLOGFONT(font_family_list[i], true);
|
|
}
|
|
font_family_list.QuickSort(Internal_CompareLogfont);
|
|
memset(&lf, 0, sizeof(lf));
|
|
int font_family_count = 0;
|
|
for (int i = 0; i < font_family_list.Count(); i++)
|
|
{
|
|
if (0 != Internal_CompareLogfont(&lf, &font_family_list[i]))
|
|
{
|
|
lf = font_family_list[i];
|
|
font_family_list[font_family_count++] = lf;
|
|
}
|
|
}
|
|
font_family_list.SetCount(font_family_count);
|
|
|
|
// Get complete list of fonts - family and face (..., Arial(Normal , Bold, Italic, Bold Italic, ...), ...
|
|
logfont_list.Reserve(3 * font_family_count);
|
|
for (int i = 0; i < font_family_count; i++)
|
|
{
|
|
|
|
// Append a list of all faces in this font family
|
|
lf = font_family_list[i];
|
|
if (0 == lf.lfFaceName[0])
|
|
continue;
|
|
int count0 = logfont_list.Count();
|
|
bool bAddFamilyLOGFONT = true;
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
::EnumFontFamiliesEx(
|
|
font_hdc,
|
|
&lf,
|
|
Internal_GetPlatformWindowsLogfontFontList_CallbackProc,
|
|
(LPARAM)(&logfont_list),
|
|
flags
|
|
);
|
|
|
|
// remove duplicates
|
|
const int count1 = logfont_list.Count();
|
|
for (int j = count0; j < count1; j++)
|
|
{
|
|
Internal_CleanLOGFONT(logfont_list[j], false);
|
|
}
|
|
ON_qsort((void*)(logfont_list.Array() + count0), count1 - count0, sizeof(LOGFONT), (int (*)(const void*,const void*))Internal_CompareLogfont);
|
|
memset(&lf, 0, sizeof(lf));
|
|
for (int j = count0; j < count1; j++)
|
|
{
|
|
LOGFONT lfj = logfont_list[j];
|
|
if (0 == Internal_CompareLogfont(&lf, &lfj))
|
|
continue;
|
|
// Add logfont_list[j] to the culled list fonts.
|
|
lf = logfont_list[j];
|
|
logfont_list[count0++] = lf;
|
|
bAddFamilyLOGFONT = false;
|
|
}
|
|
|
|
if (bAddFamilyLOGFONT)
|
|
{
|
|
logfont_list[count0++] = font_family_list[i];
|
|
}
|
|
logfont_list.SetCount(count0);
|
|
}
|
|
|
|
ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc);
|
|
font_hdc = nullptr;
|
|
|
|
return logfont_list.UnsignedCount();
|
|
}
|
|
|
|
|
|
void ON_ManagedFonts::Internal_GetWindowsInstalledFonts(
|
|
ON_SimpleArray<const ON_Font*>& platform_font_list
|
|
)
|
|
{
|
|
// As of May 2016, Rhino 6 requires WIndows 7 SP2 and DWrite works better.
|
|
|
|
// ON_Font::GetInstalledWindowsLogFonts() uses the 2 pass EnumFontFamiliesEx() technique.
|
|
// It has at least 5 failures
|
|
// It fails to mark "HoloLens MDL Assets" as a symbol font (lfCharSet = SYMBOL_CHARSET)
|
|
// It fails to mark "Segoe MDL Assets" as a symbol font (lfCharSet = SYMBOL_CHARSET)
|
|
// It fails to include "Source Sans Pro Black"
|
|
// It fails to include simulated fonts.
|
|
// It fails to get PostScript font names.
|
|
////ON_SimpleArray<LOGFONT> logfont_list;
|
|
////ON_Font::GetInstalledWindowsLogFonts(logfont_list);
|
|
////for (int i = 0; i < logfont_list.Count(); i++)
|
|
////{
|
|
//// LOGFONT lf = logfont_list[i];
|
|
//// lf.lfHeight = 0;
|
|
//// ON_Font* font = new ON_Font();
|
|
//// if (false == font->SetFromWindowsLogFont( 0, nullptr, lf ) )
|
|
//// {
|
|
//// delete font;
|
|
//// continue;
|
|
//// }
|
|
//// platform_font_list.Append(font);
|
|
////}
|
|
|
|
platform_font_list.SetCount(0);
|
|
|
|
ON_SimpleArray<ON_WindowsDWriteFontInformation> dwrite_font_list;
|
|
// freetype cannot support simulated fonts. If freetype is not used, we can include simulated fonts.
|
|
const bool bIncludeSimulatedFontFaces = true;
|
|
const bool bKeepDWriteFont = true;
|
|
ON_Font::GetInstalledWindowsDWriteFonts(
|
|
L"GetUserDefaultLocaleName",
|
|
bIncludeSimulatedFontFaces,
|
|
true,
|
|
dwrite_font_list
|
|
);
|
|
|
|
platform_font_list.Reserve(dwrite_font_list.Count());
|
|
|
|
for (int i = 0; i < dwrite_font_list.Count(); i++)
|
|
{
|
|
//const bool bSimulated
|
|
// = dwrite_font_list[i].m_bSimulatedBold
|
|
// || dwrite_font_list[i].m_bSimulatedOblique
|
|
// || dwrite_font_list[i].m_bSimulatedOther;
|
|
//if (bSimulated)
|
|
//{
|
|
// if (false == bIncludeSimulatedFontFaces)
|
|
// continue;
|
|
// if (ON_Font::IsSingleStrokeFontName(dwrite_font_list[i].m_postscript_name))
|
|
// continue;
|
|
//}
|
|
|
|
struct IDWriteFont* dwrite_font = dwrite_font_list[i].m_dwrite_font;
|
|
if (nullptr == dwrite_font)
|
|
continue;
|
|
|
|
for (;;)
|
|
{
|
|
ON_Font* installed_font = new ON_Font(ON_Font::FontType::InstalledFont, dwrite_font_list[i]);
|
|
platform_font_list.Append(installed_font);
|
|
break;
|
|
}
|
|
dwrite_font_list[i].m_dwrite_font = nullptr;
|
|
dwrite_font->Release();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ON_FontFaceQuartet::ON_FontFaceQuartet(
|
|
const wchar_t* quartet_name,
|
|
const ON_Font* regular,
|
|
const ON_Font* bold,
|
|
const ON_Font* italic,
|
|
const ON_Font* bold_italic
|
|
)
|
|
: m_quartet_name(quartet_name)
|
|
, m_regular(regular)
|
|
, m_bold(bold)
|
|
, m_italic(italic)
|
|
, m_bold_italic(bold_italic)
|
|
{
|
|
m_quartet_name.TrimLeftAndRight();
|
|
}
|
|
|
|
int ON_FontFaceQuartet::CompareQuartetName(
|
|
const ON_FontFaceQuartet* lhs,
|
|
const ON_FontFaceQuartet* rhs
|
|
)
|
|
{
|
|
if (lhs == rhs)
|
|
return 0;
|
|
if (nullptr == lhs)
|
|
return 1;
|
|
if (nullptr == rhs)
|
|
return -1;
|
|
return ON_wString::CompareOrdinal(lhs->m_quartet_name, rhs->m_quartet_name, true);
|
|
}
|
|
|
|
bool ON_FontFaceQuartet::HasRegularFace() const
|
|
{
|
|
return (nullptr != RegularFace());
|
|
}
|
|
|
|
bool ON_FontFaceQuartet::HasBoldFace() const
|
|
{
|
|
return (nullptr != BoldFace());
|
|
}
|
|
|
|
bool ON_FontFaceQuartet::HasItalicFace() const
|
|
{
|
|
return (nullptr != ItalicFace());
|
|
}
|
|
|
|
bool ON_FontFaceQuartet::HasBoldItalicFace() const
|
|
{
|
|
return (nullptr != BoldItalicFace());
|
|
}
|
|
|
|
bool ON_FontFaceQuartet::HasAllFaces() const
|
|
{
|
|
return HasRegularFace() && HasBoldFace() && HasItalicFace() && HasBoldItalicFace();
|
|
}
|
|
|
|
bool ON_FontFaceQuartet::IsEmpty() const
|
|
{
|
|
return (false==HasRegularFace() && false==HasBoldFace() && false==HasItalicFace() && false==HasBoldItalicFace());
|
|
}
|
|
|
|
const ON_wString ON_FontFaceQuartet::QuartetName() const
|
|
{
|
|
return m_quartet_name;
|
|
}
|
|
|
|
const ON_Font* ON_FontFaceQuartet::RegularFace() const
|
|
{
|
|
return m_regular;
|
|
}
|
|
|
|
const ON_Font* ON_FontFaceQuartet::BoldFace() const
|
|
{
|
|
return m_bold;
|
|
}
|
|
|
|
const ON_Font* ON_FontFaceQuartet::ItalicFace() const
|
|
{
|
|
return m_italic;
|
|
}
|
|
|
|
const ON_Font* ON_FontFaceQuartet::BoldItalicFace() const
|
|
{
|
|
return m_bold_italic;
|
|
}
|
|
|
|
const ON_Font* ON_FontFaceQuartet::Face(
|
|
bool bBold,
|
|
bool bItalic
|
|
) const
|
|
{
|
|
return (bItalic)
|
|
? (bBold ? BoldItalicFace() : ItalicFace())
|
|
: (bBold ? BoldFace() : RegularFace());
|
|
}
|
|
|
|
const ON_Font* ON_FontFaceQuartet::ClosestFace(
|
|
bool bPreferedBold,
|
|
bool bPreferedItalic
|
|
) const
|
|
{
|
|
const ON_Font* f = Face(bPreferedBold, bPreferedItalic);
|
|
if (nullptr != f)
|
|
return f;
|
|
|
|
if (IsEmpty())
|
|
return nullptr;
|
|
|
|
if (nullptr == m_bold && nullptr == m_bold_italic)
|
|
bPreferedBold = false;
|
|
else if (nullptr == m_regular && nullptr == m_italic)
|
|
bPreferedBold = true;
|
|
|
|
if (nullptr == m_italic && nullptr == m_bold_italic)
|
|
bPreferedItalic = false;
|
|
else if (nullptr == m_regular && nullptr == m_bold)
|
|
bPreferedItalic = true;
|
|
|
|
f = Face(bPreferedBold, bPreferedItalic);
|
|
if (nullptr != f)
|
|
return f;
|
|
if (nullptr != m_regular)
|
|
return m_regular;
|
|
if (nullptr != m_bold)
|
|
return m_bold;
|
|
if (nullptr != m_italic)
|
|
return m_italic;
|
|
return m_bold_italic;
|
|
}
|
|
|
|
|
|
unsigned int ON_FontFaceQuartet::FaceCount() const
|
|
{
|
|
unsigned int face_count = 0;
|
|
if (nullptr != RegularFace())
|
|
face_count++;
|
|
if (nullptr != BoldFace())
|
|
face_count++;
|
|
if (nullptr != ItalicFace())
|
|
face_count++;
|
|
if (nullptr != BoldItalicFace())
|
|
face_count++;
|
|
return face_count;
|
|
}
|
|
|
|
void ON_FontFaceQuartet::Dump(ON_TextLog& text_log) const
|
|
{
|
|
text_log.Print(L"Quartet Name: %ls\n", static_cast<const wchar_t*>(m_quartet_name));
|
|
text_log.PushIndent();
|
|
const wchar_t* quartet_face[4] = { L"Regular",L"Bold",L"Italic",L"Bold-Italic" };
|
|
const ON_Font* quartet_font[4] = { RegularFace(),BoldFace(),ItalicFace(),BoldItalicFace() };
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
const ON_Font* font = quartet_font[i];
|
|
if (nullptr == font)
|
|
text_log.Print(L"%ls: <not available>\n", quartet_face[i]);
|
|
else
|
|
text_log.Print(
|
|
L"%ls: %ls %ls (%ls) Weight = %ls Slope = %ls \n",
|
|
quartet_face[i],
|
|
static_cast<const wchar_t*>(font->FamilyName()),
|
|
static_cast<const wchar_t*>(font->FaceName()),
|
|
static_cast<const wchar_t*>(font->PostScriptName()),
|
|
ON_Font::WeightToWideString(font->FontWeight()),
|
|
ON_Font::StyleToWideString(font->FontStyle())
|
|
);
|
|
}
|
|
text_log.PopIndent();
|
|
}
|
|
|
|
|
|
#define ON_ManagedFonts_CompareFontPointer(lhs,rhs) \
|
|
if (lhs == rhs) return 0; if (nullptr == lhs) return 1; if (nullptr == rhs) return -1; \
|
|
const ON_Font* lhs_font = *lhs; const ON_Font* rhs_font = *rhs; \
|
|
if (lhs_font == rhs_font) return 0; if (nullptr == lhs_font) return 1; if (nullptr == rhs_font) return -1
|
|
|
|
int ON_ManagedFonts::CompareFontPointer(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return 0;
|
|
}
|
|
|
|
int ON_FontList::ComparePostScriptName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_wString::CompareOrdinal(
|
|
lhs_font->PostScriptName(ON_Font::NameLocale::LocalizedFirst),
|
|
rhs_font->PostScriptName(ON_Font::NameLocale::LocalizedFirst),
|
|
true
|
|
);
|
|
}
|
|
|
|
int ON_FontList::CompareFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_wString::CompareOrdinal(
|
|
lhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst),
|
|
rhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst),
|
|
true
|
|
);
|
|
}
|
|
|
|
int ON_FontList::CompareFamilyAndFaceName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
int rc = ON_wString::CompareOrdinal(
|
|
lhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst),
|
|
rhs_font->FamilyName(ON_Font::NameLocale::LocalizedFirst),
|
|
true
|
|
);
|
|
if ( 0 == rc )
|
|
rc = ON_wString::CompareOrdinal(
|
|
lhs_font->FaceName(ON_Font::NameLocale::LocalizedFirst),
|
|
rhs_font->FaceName(ON_Font::NameLocale::LocalizedFirst),
|
|
true
|
|
);
|
|
return rc;
|
|
}
|
|
|
|
int ON_FontList::CompareWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_wString::CompareOrdinal(
|
|
lhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),
|
|
rhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),
|
|
true
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
int ON_FontList::CompareEnglishPostScriptName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_wString::CompareOrdinal(
|
|
lhs_font->PostScriptName(ON_Font::NameLocale::English),
|
|
rhs_font->PostScriptName(ON_Font::NameLocale::English),
|
|
true
|
|
);
|
|
}
|
|
|
|
int ON_FontList::CompareEnglishFamilyName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_wString::CompareOrdinal(
|
|
lhs_font->FamilyName(ON_Font::NameLocale::English),
|
|
rhs_font->FamilyName(ON_Font::NameLocale::English),
|
|
true
|
|
);
|
|
}
|
|
|
|
int ON_FontList::CompareEnglishFamilyAndFaceName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
int rc = ON_wString::CompareOrdinal(
|
|
lhs_font->FamilyName(ON_Font::NameLocale::English),
|
|
rhs_font->FamilyName(ON_Font::NameLocale::English),
|
|
true
|
|
);
|
|
if ( 0 == rc )
|
|
rc = ON_wString::CompareOrdinal(
|
|
lhs_font->FaceName(ON_Font::NameLocale::English),
|
|
rhs_font->FaceName(ON_Font::NameLocale::English),
|
|
true
|
|
);
|
|
return rc;
|
|
}
|
|
|
|
int ON_FontList::CompareEnglishWindowsLogfontName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_wString::CompareOrdinal(
|
|
lhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),
|
|
rhs_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),
|
|
true
|
|
);
|
|
}
|
|
|
|
int ON_FontList::CompareQuartetName(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_wString::CompareOrdinal(
|
|
lhs_font->QuartetName(),
|
|
rhs_font->QuartetName(),
|
|
true
|
|
);
|
|
}
|
|
|
|
int ON_FontList::CompareWeightStretchStyle(
|
|
ON_Font const* const* lhs,
|
|
ON_Font const* const* rhs
|
|
)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
|
|
int rc;
|
|
|
|
// Upright first, Italic 2nd, Oblique last
|
|
|
|
// slope
|
|
const int lhs_font_style = (int)static_cast<unsigned char>(lhs_font->FontStyle());
|
|
const int rhs_font_style = (int)static_cast<unsigned char>(rhs_font->FontStyle());
|
|
rc = (lhs_font_style - rhs_font_style);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
// width
|
|
const int lhs_font_stretch = (int)static_cast<unsigned char>(lhs_font->FontStretch());
|
|
const int rhs_font_stretch = (int)static_cast<unsigned char>(rhs_font->FontStretch());
|
|
rc = (lhs_font_stretch - rhs_font_stretch);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
// weight
|
|
const int lhs_font_weight = lhs_font->WindowsLogfontWeight();
|
|
const int rhs_font_weight = rhs_font->WindowsLogfontWeight();
|
|
rc = (lhs_font_weight - rhs_font_weight);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ON_FontList::CompareUnderlinedStrikethroughPointSize(
|
|
ON_Font const* const* lhs,
|
|
ON_Font const* const* rhs
|
|
)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
|
|
// underlined
|
|
const int lhs_font_underlined = lhs_font->IsUnderlined() ? 1 : 0;
|
|
const int rhs_font_underlined = rhs_font->IsUnderlined() ? 1 : 0;
|
|
int rc = (lhs_font_underlined - rhs_font_underlined);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
// underlined
|
|
const int lhs_font_strikethrough = lhs_font->IsStrikethrough() ? 1 : 0;
|
|
const int rhs_font_strikethrough = rhs_font->IsStrikethrough() ? 1 : 0;
|
|
rc = (lhs_font_strikethrough - rhs_font_strikethrough);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
const double lhs_font_point_size = lhs_font->PointSize();
|
|
const double rhs_font_point_size = rhs_font->PointSize();
|
|
if (lhs_font_point_size < rhs_font_point_size)
|
|
return -1;
|
|
if (lhs_font_point_size > rhs_font_point_size)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
int ON_ManagedFonts::CompareFontCharacteristicsHash(
|
|
ON_Font const* const* lhs,
|
|
ON_Font const* const* rhs
|
|
)
|
|
{
|
|
ON_ManagedFonts_CompareFontPointer(lhs, rhs);
|
|
return ON_SHA1_Hash::Compare(lhs_font->FontCharacteristicsHash(), rhs_font->FontCharacteristicsHash());
|
|
}
|
|
|
|
//
|
|
// END list of platform ON_Fonts
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
bool ON_Font::ModificationPermitted(
|
|
const char* function_name,
|
|
const char* file_name,
|
|
int line_number
|
|
) const
|
|
{
|
|
if (this == &ON_Font::Default)
|
|
ON_ErrorEx(file_name, line_number, function_name, "ON_Font::Default cannot be modified.");
|
|
else if (this == &ON_Font::Unset)
|
|
ON_ErrorEx(file_name, line_number, function_name, "ON_Font::Unset cannot be modified.");
|
|
else if (IsManagedFont())
|
|
ON_ErrorEx(file_name, line_number, function_name, "Managed fonts cannot be modified.");
|
|
else
|
|
{
|
|
// Modificaton of this font means the managed information it references
|
|
// will not be valid. A reference to the correct cached information
|
|
// will be generated when it is actually needed.
|
|
m_font_glyph_cache.reset();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#define ON_FONT_MODIFICATION_PERMITTED this->ModificationPermitted(OPENNURBS__FUNCTION__,__FILE__,__LINE__)
|
|
|
|
//// V6 files 4F0F51FB-35D0-4865-9998-6D2C6A99721D is the class id for ON_TextStyle
|
|
////ON_OBJECT_IMPLEMENT( ON_Font, ON_Object, "4F0F51FB-35D0-4865-9998-6D2C6A99721D" );
|
|
//ON_OBJECT_IMPLEMENT( ON_Font, ON_Object, "5F7476D1-798A-4953-B359-4A37699CD6F4" );
|
|
|
|
const ON_Font* ON_Font::ManagedFont() const
|
|
{
|
|
return
|
|
IsManagedFont()
|
|
? this
|
|
: ON_Font::GetManagedFont(*this, true);
|
|
}
|
|
|
|
const ON_FontGlyph* ON_Font::CodePointGlyph(
|
|
ON__UINT32 unicode_codepoint
|
|
) const
|
|
{
|
|
const ON_Font* managed_font = ManagedFont();
|
|
if (nullptr == managed_font)
|
|
return nullptr;
|
|
|
|
bool bCreateIfMissing = true;
|
|
bool bFindSubstitutes = true;
|
|
return managed_font->Internal_ManagedCodePointGlyph(unicode_codepoint,bCreateIfMissing,bFindSubstitutes);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
const ON_Font& font_characteristics,
|
|
bool bCreateIfNotFound
|
|
)
|
|
{
|
|
if (font_characteristics.IsManagedFont())
|
|
{
|
|
// No need to look it up and return itself.
|
|
return &font_characteristics;
|
|
}
|
|
|
|
return ON_ManagedFonts::List.GetFromFontCharacteristics(font_characteristics,bCreateIfNotFound);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFontFromSerialNumber(
|
|
unsigned int managed_font_runtime_serial_number
|
|
)
|
|
{
|
|
return ON_ManagedFonts::List.GetFromSerialNumber(managed_font_runtime_serial_number);
|
|
}
|
|
|
|
unsigned int ON_Font::GetManagedFontList(
|
|
ON_SimpleArray< const ON_Font* >& managed_fonts
|
|
)
|
|
{
|
|
const ON_SimpleArray< const ON_Font* >& a = ON_ManagedFonts::ManagedFonts().ByIndex();
|
|
if (&a != &managed_fonts)
|
|
managed_fonts = a;
|
|
return managed_fonts.UnsignedCount();
|
|
}
|
|
|
|
unsigned int ON_Font::GetInstalledFontList(
|
|
ON_SimpleArray< const ON_Font* >& installed_fonts
|
|
)
|
|
{
|
|
const ON_SimpleArray< const ON_Font* >& a = ON_ManagedFonts::InstalledFonts().ByFamilyName();
|
|
if (&a != &installed_fonts)
|
|
installed_fonts = a;
|
|
return installed_fonts.UnsignedCount();
|
|
}
|
|
|
|
unsigned int ON_Font::GetInstalledFontFamily(
|
|
const wchar_t* font_family_name,
|
|
ON_SimpleArray< const ON_Font* >& installed_fonts
|
|
)
|
|
{
|
|
installed_fonts.SetCount(0);
|
|
if (nullptr == font_family_name || 0 == font_family_name[0])
|
|
return 0;
|
|
|
|
const ON_FontList& installed_font_list = ON_ManagedFonts::InstalledFonts();
|
|
|
|
if (0 == installed_font_list.FontListFromNames( nullptr, nullptr, font_family_name, nullptr, installed_fonts) )
|
|
installed_font_list.FontListFromNames(font_family_name, font_family_name, nullptr, nullptr, installed_fonts);
|
|
|
|
return installed_fonts.UnsignedCount();
|
|
}
|
|
|
|
static const wchar_t* Internal_NameOverLapSkipNoise(
|
|
const wchar_t* s,
|
|
bool bNoiseFilter
|
|
)
|
|
{
|
|
if (false == bNoiseFilter)
|
|
return s;
|
|
|
|
if (nullptr == s)
|
|
return nullptr;
|
|
|
|
while (0 != *s)
|
|
{
|
|
if (*s >= 'A' && *s <= 'Z')
|
|
return s;
|
|
if (*s >= 'a' && *s <= 'z')
|
|
return s;
|
|
if (*s >= 0x80)
|
|
return s;
|
|
s++;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static const ON_wString Internal_NameOverlapCleanName(
|
|
const wchar_t* s,
|
|
bool bNoiseFilter
|
|
)
|
|
{
|
|
if (nullptr == s || 0 == s[0])
|
|
return ON_wString::EmptyString;
|
|
|
|
ON_wString clean_name;
|
|
wchar_t buffer[128];
|
|
int buffer_count = 0;
|
|
for (wchar_t c = *(s = Internal_NameOverLapSkipNoise(s,bNoiseFilter)); 0 != c; c = *(s = Internal_NameOverLapSkipNoise(++s,bNoiseFilter)))
|
|
{
|
|
buffer[buffer_count++] = ON_wString::MapCharacterOrdinal(ON_StringMapOrdinalType::MinimumOrdinal, c);
|
|
if (127 == buffer_count)
|
|
{
|
|
buffer[buffer_count] = 0;
|
|
clean_name += buffer;
|
|
buffer_count = 0;
|
|
}
|
|
}
|
|
buffer[buffer_count] = 0;
|
|
clean_name += buffer;
|
|
return clean_name;
|
|
}
|
|
|
|
static int Internal_NameOverLap(
|
|
const ON_wString& clean_name,
|
|
const wchar_t* s,
|
|
bool bNoiseFilter
|
|
)
|
|
{
|
|
if (nullptr == s)
|
|
return 0;
|
|
const wchar_t* clean_s = static_cast<const wchar_t*>(clean_name);
|
|
int overlap_count = 0;
|
|
for (
|
|
s = Internal_NameOverLapSkipNoise(s,bNoiseFilter);
|
|
0 != *clean_s && *clean_s == ON_wString::MapCharacterOrdinal(ON_StringMapOrdinalType::LowerOrdinal,*s);
|
|
s = Internal_NameOverLapSkipNoise(s,bNoiseFilter)
|
|
)
|
|
{
|
|
overlap_count++;
|
|
clean_s++;
|
|
s++;
|
|
}
|
|
return overlap_count;
|
|
}
|
|
|
|
static bool Internal_EqualLogfontName(
|
|
const ON_wString& clean_logfont_name,
|
|
const ON_Font* candidate_font
|
|
)
|
|
{
|
|
if (nullptr == candidate_font)
|
|
return false;
|
|
if (clean_logfont_name.IsEmpty())
|
|
return false;
|
|
const ON_wString installed_font_logfont_name = Internal_NameOverlapCleanName(candidate_font->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),true);
|
|
const bool bEqualLogfontName = ON_wString::EqualOrdinal(clean_logfont_name, installed_font_logfont_name, true);
|
|
return bEqualLogfontName;
|
|
}
|
|
|
|
static bool Internal_EqualFamilyName(
|
|
const ON_wString& clean_family_name,
|
|
const ON_Font* installed_font,
|
|
ON_wString& installed_font_family_name
|
|
)
|
|
{
|
|
if (nullptr == installed_font)
|
|
return false;
|
|
installed_font_family_name = Internal_NameOverlapCleanName(installed_font->FamilyName(),true);
|
|
const bool bEqualFamileName = ON_wString::EqualOrdinal(clean_family_name, installed_font_family_name, true);
|
|
return bEqualFamileName;
|
|
}
|
|
|
|
class Internal_FontDelta
|
|
{
|
|
// Internal_FontDelta is a simple minded attempt at measuring the difference between a target font and a candidate font.
|
|
// It is probably a poor substitued for something fancier like PANOSE information.
|
|
// Unfortunately, we generally do not have PANOSE information about the target font.
|
|
|
|
public:
|
|
Internal_FontDelta() = default;
|
|
~Internal_FontDelta() = default;
|
|
Internal_FontDelta(const Internal_FontDelta&) = default;
|
|
Internal_FontDelta& operator= (const Internal_FontDelta&) = default;
|
|
|
|
// Initialize this to be a measure of the difference between target_font and candidate_font
|
|
// The target_clean_*_name values take some time to compute, so they are pre-calculated and
|
|
// passed in. There are generally multiple candidate fonts.
|
|
Internal_FontDelta(
|
|
const ON_Font& target_font,
|
|
const ON_wString& target_clean_logfont_name,
|
|
const ON_wString& target_clean_family_name,
|
|
const ON_Font* candidate_font
|
|
)
|
|
: m_candidate_font(candidate_font)
|
|
{
|
|
if (nullptr == m_candidate_font)
|
|
{
|
|
m_hash_delta = 6;
|
|
return;
|
|
}
|
|
|
|
// When we have identical PostSript names, we assume they
|
|
// are correct and assume font family, weight, stretch, and style
|
|
// are exact matches.
|
|
const ON_wString target_postscript_name = target_font.PostScriptName();
|
|
const bool bEqualPostScriptName
|
|
= target_postscript_name.IsNotEmpty()
|
|
&& ON_wString::EqualOrdinal(target_postscript_name, m_candidate_font->PostScriptName(), true);
|
|
|
|
const bool bEqualLogfontName
|
|
= bEqualPostScriptName
|
|
|| (
|
|
ON_Font::Origin::WindowsFont == target_font.FontOrigin()
|
|
&& ON_Font::Origin::WindowsFont == candidate_font->FontOrigin()
|
|
&& target_clean_logfont_name.IsNotEmpty()
|
|
&& Internal_EqualLogfontName(target_clean_logfont_name, candidate_font)
|
|
);
|
|
|
|
m_weight_delta
|
|
= bEqualPostScriptName
|
|
? 0
|
|
: abs(((int)static_cast<unsigned int>(target_font.FontWeight())) - ((int)static_cast<unsigned int>(candidate_font->FontWeight())));
|
|
m_stretch_delta
|
|
= bEqualPostScriptName
|
|
? 0
|
|
: abs(((int)static_cast<unsigned int>(target_font.FontStretch())) - ((int)static_cast<unsigned int>(candidate_font->FontStretch())));
|
|
m_style_delta
|
|
= bEqualPostScriptName
|
|
? 0
|
|
: abs(((int)static_cast<unsigned int>(target_font.FontStyle())) - ((int)static_cast<unsigned int>(candidate_font->FontStyle())));
|
|
|
|
ON_wString candidate_clean_family_name;
|
|
const bool bEqualFamilyName
|
|
= bEqualLogfontName
|
|
|| Internal_EqualFamilyName(target_clean_family_name, candidate_font, candidate_clean_family_name);
|
|
|
|
if (target_font.IsUnderlined() != candidate_font->IsUnderlined())
|
|
m_underline_and_strikethrough_delta++;
|
|
if (target_font.IsStrikethrough() != candidate_font->IsStrikethrough())
|
|
m_underline_and_strikethrough_delta++;
|
|
|
|
m_family_name_delta = 0;
|
|
|
|
|
|
if (
|
|
bEqualFamilyName
|
|
&& 0 == m_weight_delta
|
|
&& 0 == m_stretch_delta
|
|
&& 0 == m_style_delta
|
|
)
|
|
{
|
|
// This is either an exact match (m_hash_delta = 0) or a very close match (m_hash_delta = 1).
|
|
m_hash_delta
|
|
= (0 == m_underline_and_strikethrough_delta && target_font.FontCharacteristicsHash() == candidate_font->FontCharacteristicsHash())
|
|
? 0
|
|
: 1;
|
|
return;
|
|
}
|
|
|
|
// If PANOSE information were available for target_font and candidate_font, it would be useful here.
|
|
|
|
if (
|
|
bEqualLogfontName
|
|
&& 0 == m_stretch_delta
|
|
&& m_style_delta <= 1
|
|
)
|
|
{
|
|
// There are at most 4 "faces" with the same WINDOWS LOGFONT.lfFaceName and the differences
|
|
// among the four "faces" are almost always in weight (normal or bold) or style (upright or italic).
|
|
// Fonts with larger differences in weight or style or any significant difference in stretch
|
|
// typically have different values of LOGFONT.lfFaceName.
|
|
// (Underlined and Strikethrough are not "faces").
|
|
m_hash_delta = 2;
|
|
return;
|
|
}
|
|
|
|
if (bEqualFamilyName)
|
|
{
|
|
// A family, like "Arial", can have manu faces with a wide variety of weights, stretches, and styles.
|
|
// The LOGFONTs with lfFaceName = "Arial", "Arial Narrow", "Arial Black", ... are all in the "Arial" family.
|
|
m_hash_delta = 3;
|
|
return;
|
|
}
|
|
|
|
// Differences below here (m_hash_delta >= 4) are generally visually significant.
|
|
// Calculate font family name overlap
|
|
int name_overlap = Internal_NameOverLap(target_clean_family_name, candidate_clean_family_name, true);
|
|
m_family_name_delta = target_clean_family_name.Length() - name_overlap;
|
|
if (0 == m_family_name_delta)
|
|
{
|
|
// clean_font_family_name.Length() = name_overlap means that
|
|
// clean_font_family_name.Length() <= installed_font_family_name.Length()
|
|
// and installed_font_family_name begins with clean_font_family_name.
|
|
// If installed_font_family_name.Length() > clean_font_family_name.Length(),
|
|
// m_name_delta < 0 and it measures how much tail was not matched
|
|
m_family_name_delta = target_clean_family_name.Length() - candidate_clean_family_name.Length();
|
|
}
|
|
|
|
m_hash_delta
|
|
= (0 == m_style_delta || ON_Font::Style::Upright != target_font.FontStyle())
|
|
? 4
|
|
: 5;
|
|
}
|
|
|
|
bool IsExactMatch() const
|
|
{
|
|
return (nullptr != m_candidate_font && 0 == m_hash_delta );
|
|
}
|
|
|
|
// If Compare(lhs,rhs) < 0, then lhs is a better match for the target font passed to the constructor.
|
|
static int Compare(const Internal_FontDelta& lhs, const Internal_FontDelta& rhs)
|
|
{
|
|
if (lhs.m_candidate_font == rhs.m_candidate_font)
|
|
return 0;
|
|
if (nullptr == rhs.m_candidate_font)
|
|
return -1; // lhs is better because it has a candidate font
|
|
if (nullptr == lhs.m_candidate_font)
|
|
return 1; // rhs is better because it has a candidate font
|
|
|
|
// m_hash_delta >= 0 and smaller values of m_hash_delta indicate better matches
|
|
int rc = (lhs.m_hash_delta - rhs.m_hash_delta);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = (lhs.m_family_name_delta - rhs.m_family_name_delta);
|
|
if (0 != rc)
|
|
{
|
|
if (0 == lhs.m_family_name_delta)
|
|
return -1; // lhs is in the same family
|
|
|
|
if (0 == rhs.m_family_name_delta)
|
|
return 1; // rhs is in the same family
|
|
|
|
if (lhs.m_family_name_delta < 0 && rhs.m_family_name_delta < 0)
|
|
{
|
|
// When both values of m_name_delta are negative, it
|
|
// means their font family names both began with the
|
|
// family name we are searching for. In this
|
|
// case the shortest tail is best.
|
|
// The length of that tail is -m_name_delta,
|
|
// so the least negative m_delta is the best match.
|
|
return -rc;
|
|
}
|
|
// If at least one value of m_delta > 0, then the
|
|
// smallest value of m_delta is the best match.
|
|
return rc;
|
|
}
|
|
|
|
rc = (lhs.m_style_delta - rhs.m_style_delta);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = (lhs.m_weight_delta - rhs.m_weight_delta);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = (lhs.m_stretch_delta - rhs.m_stretch_delta);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = (lhs.m_underline_and_strikethrough_delta - rhs.m_underline_and_strikethrough_delta);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Points to a cadidate for matching the original font
|
|
const ON_Font* m_candidate_font = nullptr;
|
|
|
|
// 0: exact match
|
|
// 1: same family, weight, stretch, style match - different hash
|
|
// 2: same LOGFONT.lfName - different weight,stretch,style
|
|
// 3: same family name - different LOGFONT.lfFaceName, weight,stretch,style
|
|
// 4: some overlap in family name
|
|
// 5: least favoriable match
|
|
int m_hash_delta = 0;
|
|
|
|
// m_family_name_delta
|
|
// <0: partial overlap
|
|
// 0: exact match
|
|
// >0: installed font family name is longer but exact match with start
|
|
int m_family_name_delta = 0;
|
|
|
|
// m_logfont_name_delta
|
|
// <0: partial overlap
|
|
// 0: exact match
|
|
// >0: installed font LOGFONT.lfFaceName name is longer but exact match with start
|
|
int m_logfont_name_delta = 0;
|
|
|
|
// Weight delta = absolute value of difference between weights.
|
|
int m_weight_delta = 0;
|
|
|
|
// Stretch (width) delta = absolute value of difference between stretch.
|
|
int m_stretch_delta = 0;
|
|
|
|
// Style (slope) delta = absolute value of difference between font styles.
|
|
int m_style_delta = 0;
|
|
|
|
// Underlined and strikethrough delta = number of different bools.
|
|
int m_underline_and_strikethrough_delta = 0;
|
|
};
|
|
|
|
const ON_Font* ON_Font::BestMatch(
|
|
ON_Font const*const* candidate_font_list,
|
|
size_t candidate_font_count
|
|
) const
|
|
{
|
|
if (nullptr == candidate_font_list || candidate_font_count < 1)
|
|
return nullptr;
|
|
|
|
if (1 == candidate_font_count)
|
|
return candidate_font_list[0];
|
|
|
|
const ON_wString target_clean_logfont_name = Internal_NameOverlapCleanName(WindowsLogfontName(),true);
|
|
const ON_wString target_clean_family_name = Internal_NameOverlapCleanName(FamilyName(),true);
|
|
|
|
Internal_FontDelta best_match;
|
|
|
|
for (size_t i = 0; i < candidate_font_count; i++)
|
|
{
|
|
const ON_Font* candidate_font = candidate_font_list[i];
|
|
if (nullptr == candidate_font)
|
|
continue;
|
|
|
|
const Internal_FontDelta delta(
|
|
*this,
|
|
target_clean_logfont_name,
|
|
target_clean_family_name,
|
|
candidate_font
|
|
);
|
|
|
|
if (delta.IsExactMatch())
|
|
return candidate_font;
|
|
|
|
if (nullptr == best_match.m_candidate_font || Internal_FontDelta::Compare(delta, best_match) < 0 )
|
|
best_match = delta;
|
|
}
|
|
|
|
return best_match.m_candidate_font;
|
|
}
|
|
|
|
unsigned int ON_Font::WeightStretchStyleDeviation(
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
ON_Font::Weight available_weight,
|
|
ON_Font::Stretch available_stretch,
|
|
ON_Font::Style available_style
|
|
)
|
|
{
|
|
if (ON_Font::Weight::Unset == prefered_weight)
|
|
prefered_weight = available_weight;
|
|
if (ON_Font::Stretch::Unset == prefered_stretch)
|
|
prefered_stretch = available_stretch;
|
|
if (ON_Font::Style::Unset == prefered_style)
|
|
prefered_style = available_style;
|
|
const unsigned int weight_delta = abs(((int)prefered_weight) - ((int)available_weight));
|
|
const unsigned int stretch_delta = abs(((int)prefered_stretch) - ((int)available_stretch));
|
|
const unsigned int style_delta = abs(((int)prefered_style) - ((int)available_style));
|
|
return 4*(1000U * style_delta + 20U * weight_delta + stretch_delta);
|
|
}
|
|
|
|
unsigned int ON_Font::WeightStretchStyleDeviation(
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
const ON_Font* available_font
|
|
)
|
|
{
|
|
if (nullptr == available_font)
|
|
return 0xFFFFFFF;
|
|
|
|
return ON_Font::WeightStretchStyleDeviation(
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
available_font->FontWeight(),
|
|
available_font->FontStretch(),
|
|
available_font->FontStyle()
|
|
);
|
|
}
|
|
|
|
unsigned int ON_Font::WeightStretchStyleDeviation(
|
|
const ON_Font* prefered_weight_stretch_style,
|
|
const ON_Font* available_font
|
|
)
|
|
{
|
|
if (nullptr == prefered_weight_stretch_style)
|
|
prefered_weight_stretch_style = &ON_Font::Default;
|
|
ON_Font::Weight prefered_weight = prefered_weight_stretch_style->FontWeight();
|
|
ON_Font::Stretch prefered_stretch = prefered_weight_stretch_style->FontStretch();
|
|
ON_Font::Style prefered_style = prefered_weight_stretch_style->FontStyle();
|
|
return ON_Font::WeightStretchStyleDeviation(prefered_weight, prefered_stretch, prefered_style, available_font);
|
|
}
|
|
|
|
unsigned int ON_Font::UnderlinedStrikethroughDeviation(
|
|
bool bPreferedUnderline,
|
|
bool bPreferedStrikethrough,
|
|
bool bAvailableUnderline,
|
|
bool bAvailableStrikethrough
|
|
)
|
|
{
|
|
const unsigned int underlined_dev = (unsigned int)abs((int)(bPreferedUnderline ? 1 : 0) - (int)(bAvailableUnderline ? 1 : 0));
|
|
const unsigned int strikethrough_dev = (unsigned int)abs((int)(bPreferedStrikethrough ? 1 : 0) - (int)(bAvailableStrikethrough ? 1 : 0));
|
|
return 2 * underlined_dev + strikethrough_dev;
|
|
}
|
|
|
|
unsigned int ON_Font::UnderlinedStrikethroughDeviation(
|
|
bool bPreferedUnderline,
|
|
bool bPreferedStrikethrough,
|
|
const ON_Font* available_font
|
|
)
|
|
{
|
|
return ON_Font::UnderlinedStrikethroughDeviation(
|
|
bPreferedUnderline,
|
|
bPreferedStrikethrough,
|
|
(nullptr == available_font) ? false : available_font->IsUnderlined(),
|
|
(nullptr == available_font) ? false : available_font->IsStrikethrough()
|
|
);
|
|
}
|
|
|
|
unsigned int ON_Font::UnderlinedStrikethroughDeviation(
|
|
const ON_Font* prefered_underlined_strikethrough,
|
|
const ON_Font* available_font
|
|
)
|
|
{
|
|
return ON_Font::UnderlinedStrikethroughDeviation(
|
|
(nullptr == prefered_underlined_strikethrough) ? false : prefered_underlined_strikethrough->IsUnderlined(),
|
|
(nullptr == prefered_underlined_strikethrough) ? false : prefered_underlined_strikethrough->IsStrikethrough(),
|
|
(nullptr == available_font) ? false : available_font->IsUnderlined(),
|
|
(nullptr == available_font) ? false : available_font->IsStrikethrough()
|
|
);
|
|
}
|
|
|
|
unsigned int ON_Font::RichTextPropertyDeviation(
|
|
bool bPreferedRtfBold,
|
|
bool bPreferedItalic,
|
|
bool bPreferedUnderline,
|
|
bool bPreferedStrikethrough,
|
|
const ON_Font* available_font
|
|
)
|
|
{
|
|
if (nullptr == available_font)
|
|
return 0xFFFFFFFF;
|
|
return ON_Font::RichTextPropertyDeviation(
|
|
bPreferedRtfBold,
|
|
bPreferedItalic,
|
|
bPreferedUnderline,
|
|
bPreferedStrikethrough,
|
|
available_font->IsBold(),
|
|
available_font->IsItalic(),
|
|
available_font->IsUnderlined(),
|
|
available_font->IsStrikethrough()
|
|
);
|
|
}
|
|
|
|
unsigned int ON_Font::RichTextPropertyDeviation(
|
|
bool bPreferedRtfBold,
|
|
bool bPreferedItalic,
|
|
bool bPreferedUnderline,
|
|
bool bPreferedStrikethrough,
|
|
bool bAvailableRtfBold,
|
|
bool bAvailableItalic,
|
|
bool bAvailableUnderline,
|
|
bool bAvailableStrikethrough
|
|
)
|
|
{
|
|
const ON_Font::Weight prefered_weight = bPreferedRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal;
|
|
const ON_Font::Weight available_weight = bAvailableRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal;
|
|
|
|
const ON_Font::Style prefered_style = bPreferedItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright;
|
|
const ON_Font::Style available_style = bPreferedItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright;
|
|
|
|
const unsigned int wss_dev = ON_Font::WeightStretchStyleDeviation(
|
|
prefered_weight, ON_Font::Stretch::Medium, prefered_style,
|
|
available_weight, ON_Font::Stretch::Medium, available_style
|
|
);
|
|
|
|
const unsigned int us_dev = ON_Font::UnderlinedStrikethroughDeviation(
|
|
bPreferedUnderline,
|
|
bPreferedStrikethrough,
|
|
bAvailableUnderline,
|
|
bAvailableStrikethrough
|
|
);
|
|
return wss_dev + us_dev;
|
|
}
|
|
|
|
const ON_Font* ON_Font::Internal_BestMatchWeightStretchStyle(
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
ON_Font const * const * font_list,
|
|
size_t font_count
|
|
)
|
|
{
|
|
if (nullptr == font_list || font_count <= 0)
|
|
return nullptr;
|
|
|
|
const ON_Font* best_font = nullptr;
|
|
unsigned int best_delta = 0xFFFFFFFF;
|
|
for (size_t i = 0; i < font_count; i++)
|
|
{
|
|
const ON_Font* font = font_list[i];
|
|
if (nullptr == font)
|
|
continue;
|
|
const unsigned int delta = ON_Font::WeightStretchStyleDeviation(
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
font
|
|
);
|
|
if (0 == delta)
|
|
return font;
|
|
if (nullptr == best_font || delta < best_delta)
|
|
{
|
|
best_font = font;
|
|
best_delta = delta;
|
|
}
|
|
}
|
|
|
|
return best_font;
|
|
}
|
|
|
|
const ON_Font* ON_Font::BestMatch(
|
|
const ON_SimpleArray< const ON_Font* >& font_list
|
|
) const
|
|
{
|
|
return BestMatch(
|
|
font_list.Array(),
|
|
font_list.UnsignedCount()
|
|
);
|
|
}
|
|
|
|
const ON_FontList& ON_Font::ManagedFontList()
|
|
{
|
|
return ON_ManagedFonts::ManagedFonts();
|
|
}
|
|
|
|
const ON_FontList& ON_Font::InstalledFontList()
|
|
{
|
|
return ON_ManagedFonts::InstalledFonts();
|
|
}
|
|
|
|
const ON_Font* ON_Font::InstalledFontFromRichTextProperties(
|
|
const wchar_t* rtf_font_name,
|
|
bool bRtfBold,
|
|
bool bRtfItalic
|
|
)
|
|
{
|
|
ON_wString local_rtf_font_name(rtf_font_name);
|
|
local_rtf_font_name.TrimLeftAndRight();
|
|
if (local_rtf_font_name.IsEmpty())
|
|
local_rtf_font_name = ON_Font::Default.RichTextFontName();
|
|
rtf_font_name = local_rtf_font_name;
|
|
|
|
// It is critical that bRequireFaceMatch because the input name must
|
|
// clearly identify a font in some way. The search below allows for this
|
|
// name to appear in various contexts (LOGFONT, PostScript, Family+Face, Family).
|
|
const bool bRequireFaceMatch = true;
|
|
|
|
// It is critical that bRequireStyleMatch = false. See below
|
|
const bool bRequireStyleMatch = false;
|
|
|
|
// The preferred_weight is a bias used to pick between faces that have matching name properties.
|
|
// An exact match is not required because bRequireStyleMatch = false;
|
|
const ON_Font::Weight preferred_weight = bRtfBold ? ON_Font::Weight::Semibold : ON_Font::Weight::Medium;
|
|
|
|
// The preferred_style is a bias used to pick between faces that have matching name properties.
|
|
// An exact match is not required because bRequireStyleMatch = false;
|
|
const ON_Font::Style preferred_style = bRtfItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright;
|
|
|
|
const bool bUnderlined = false;
|
|
const bool bStrikethrough = false;
|
|
const wchar_t* ignore_postscript_name = nullptr;
|
|
const wchar_t* ignore_windows_logfont_name = nullptr;
|
|
const wchar_t* ignore_family_name = nullptr;
|
|
const wchar_t* ignore_prefered_face_name = nullptr;
|
|
|
|
|
|
// 1st: Try LOGFONT name. For Windows this is the very best choice.
|
|
// For MacOS this works the best in practice. If it fails, we move onto PostScript.
|
|
const ON_Font* installed_font = ON_Font::InstalledFontList().FromNames(
|
|
ignore_postscript_name,
|
|
rtf_font_name, // windows_logfont_name,
|
|
ignore_family_name,
|
|
ignore_prefered_face_name,
|
|
preferred_weight, ON_Font::Stretch::Unset, preferred_style,
|
|
bRequireFaceMatch, bRequireStyleMatch, bUnderlined, bStrikethrough,
|
|
0.0
|
|
);
|
|
|
|
while (nullptr == installed_font)
|
|
{
|
|
|
|
// 2nd: Try PostScript name
|
|
installed_font = ON_Font::InstalledFontList().FromNames(
|
|
rtf_font_name, // postscript_name,
|
|
ignore_windows_logfont_name,
|
|
ignore_family_name,
|
|
ignore_prefered_face_name,
|
|
preferred_weight, ON_Font::Stretch::Unset, preferred_style,
|
|
bRequireFaceMatch, bRequireStyleMatch, bUnderlined, bStrikethrough,
|
|
0.0
|
|
);
|
|
if (nullptr != installed_font)
|
|
break;
|
|
|
|
// 3rd: Try Family + Face name
|
|
const wchar_t hyphen[2] = {ON_wString::HyphenMinus,0};
|
|
for (const wchar_t* separator = rtf_font_name + 1; 0 != *separator; ++separator)
|
|
{
|
|
if (ON_wString::Space != *separator && ON_wString::HyphenMinus != *separator)
|
|
continue;
|
|
ON_wString family_name(rtf_font_name, (int)(separator - rtf_font_name));
|
|
family_name.TrimLeftAndRight();
|
|
family_name.TrimLeftAndRight(hyphen);
|
|
family_name.TrimLeftAndRight();
|
|
if (family_name.IsEmpty())
|
|
continue;
|
|
ON_wString face_name(separator + 1);
|
|
face_name.TrimLeftAndRight();
|
|
face_name.TrimLeftAndRight(hyphen);
|
|
face_name.TrimLeftAndRight();
|
|
if (face_name.IsEmpty())
|
|
continue;
|
|
|
|
installed_font = ON_Font::InstalledFontList().FromNames(
|
|
ignore_postscript_name,
|
|
ignore_windows_logfont_name,
|
|
family_name, face_name,
|
|
preferred_weight, ON_Font::Stretch::Unset, preferred_style,
|
|
bRequireFaceMatch, bRequireStyleMatch, bUnderlined, bStrikethrough,
|
|
0.0
|
|
);
|
|
if (nullptr != installed_font)
|
|
break;
|
|
}
|
|
if (nullptr != installed_font)
|
|
break;
|
|
|
|
|
|
// 4th: Try Family name
|
|
installed_font = ON_Font::InstalledFontList().FromNames(
|
|
ignore_postscript_name,
|
|
ignore_windows_logfont_name,
|
|
rtf_font_name, // family name
|
|
ignore_prefered_face_name,
|
|
preferred_weight, ON_Font::Stretch::Unset, preferred_style,
|
|
bRequireFaceMatch, bRequireStyleMatch, bUnderlined, bStrikethrough,
|
|
0.0
|
|
);
|
|
if (nullptr != installed_font)
|
|
break;
|
|
|
|
// No installed font comes close to matching the input name parameter
|
|
break;
|
|
}
|
|
|
|
if (nullptr != installed_font)
|
|
{
|
|
const ON_FontFaceQuartet q = installed_font->InstalledFontQuartet();
|
|
const ON_Font* f = q.ClosestFace(bRtfBold, bRtfItalic);
|
|
return (nullptr != f) ? f : installed_font;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
const ON_wString ON_Font::RichTextPropertiesToString(
|
|
bool bRtfBold,
|
|
bool bRtfItalic,
|
|
bool bRtfUnderlined,
|
|
bool bRtfStrikethrough
|
|
)
|
|
{
|
|
return ON_wString::FormatToString(
|
|
L"%ls%ls%ls",
|
|
(bRtfBold ? (bRtfItalic ? L"Bold Italic" : L"Bold") : (bRtfItalic ? L"Italic" : L"Regular")),
|
|
(bRtfUnderlined ? L" Underlined" : L""),
|
|
(bRtfStrikethrough ? L" Strikethrough" : L"")
|
|
);
|
|
}
|
|
|
|
const ON_wString ON_Font::RichTextPropertiesToString(
|
|
ON_Font::Weight rtf_weight,
|
|
ON_Font::Style rtf_style,
|
|
bool bRtfUnderlined,
|
|
bool bRtfStrikethrough
|
|
)
|
|
{
|
|
return RichTextPropertiesToString(
|
|
ON_Font::IsBoldWeight(rtf_weight),
|
|
ON_Font::Style::Italic == rtf_style,
|
|
bRtfUnderlined,
|
|
bRtfStrikethrough
|
|
);
|
|
}
|
|
|
|
const ON_wString ON_Font::RichTextPropertiesToString(
|
|
const ON_Font* font
|
|
)
|
|
{
|
|
if (font == nullptr)
|
|
font = &ON_Font::Default;
|
|
return RichTextPropertiesToString(
|
|
font->FontWeight(),
|
|
font->FontStyle(),
|
|
font->IsUnderlined(),
|
|
font->IsStrikethrough()
|
|
);
|
|
}
|
|
|
|
static unsigned int Internal_RtfDeviation(
|
|
const ON_Font* font,
|
|
bool bRtfBold,
|
|
bool bRtfItalic,
|
|
bool bRftUnderlined,
|
|
bool bRftStrikethrough
|
|
)
|
|
{
|
|
if (nullptr == font)
|
|
return 0xFFFFFFFF;
|
|
|
|
unsigned int bold_dev = (unsigned int)abs((int)(font->IsBold()?1:0) - (int)(bRtfBold?1:0));
|
|
unsigned int italic_dev = (unsigned int)abs((int)(font->IsItalic()?1:0) - (int)(bRtfItalic?1:0));
|
|
unsigned int undelined_dev = (unsigned int)abs((int)(font->IsUnderlined()?1:0) - (int)(bRftUnderlined?1:0));
|
|
unsigned int strikethrough_dev = (unsigned int)abs((int)(font->IsStrikethrough()?1:0) - (int)(bRftStrikethrough?1:0));
|
|
|
|
return (8 * italic_dev + 4 * bold_dev + 2 * undelined_dev + 1 * strikethrough_dev);
|
|
}
|
|
|
|
const ON_Font* ON_Font::ManagedFontFromRichTextProperties(
|
|
const wchar_t* rtf_font_name,
|
|
bool bRtfBold,
|
|
bool bRtfItalic,
|
|
bool bRftUnderlined,
|
|
bool bRftStrikethrough
|
|
)
|
|
{
|
|
ON_wString s(rtf_font_name);
|
|
s.TrimLeftAndRight();
|
|
if (s.IsEmpty())
|
|
s = ON_Font::DefaultFamilyName();
|
|
rtf_font_name = s;
|
|
|
|
// insure exat true/false settings so we can compare
|
|
bRtfBold = bRtfBold ? true : false;
|
|
bRftStrikethrough = bRftStrikethrough ? true : false;
|
|
|
|
const ON_Font* managed_font = ManagedFontList().FromRichTextProperties(rtf_font_name, bRtfBold, bRtfItalic, bRftUnderlined, bRftStrikethrough);
|
|
unsigned int managed_font_dev = Internal_RtfDeviation(managed_font, bRtfBold, bRtfItalic, bRftUnderlined, bRftStrikethrough);
|
|
if (nullptr != managed_font && managed_font_dev <= 3)
|
|
{
|
|
if (managed_font_dev > 0)
|
|
{
|
|
// add underlined and strikethrough settings
|
|
ON_Font font(*managed_font);
|
|
font.SetUnderlined(bRftUnderlined);
|
|
font.SetStrikethrough(bRftStrikethrough);
|
|
managed_font = font.ManagedFont();
|
|
}
|
|
return managed_font;
|
|
}
|
|
|
|
const ON_Font* installed_font = ON_Font::InstalledFontFromRichTextProperties(rtf_font_name, bRtfBold, bRtfItalic);
|
|
unsigned int installed_font_dev = Internal_RtfDeviation(installed_font, bRtfBold, bRtfItalic, bRftUnderlined, bRftStrikethrough);
|
|
if (nullptr != installed_font && installed_font_dev <= 3)
|
|
{
|
|
if (installed_font_dev > 0)
|
|
{
|
|
ON_Font font(*installed_font);
|
|
font.SetUnderlined(bRftUnderlined);
|
|
font.SetStrikethrough(bRftStrikethrough);
|
|
managed_font = font.ManagedFont();
|
|
}
|
|
else
|
|
{
|
|
managed_font = installed_font->ManagedFont();
|
|
}
|
|
return managed_font;
|
|
}
|
|
|
|
if (nullptr != managed_font && managed_font_dev <= installed_font_dev)
|
|
return managed_font; // found something in the "rtf family/face" on this device or from a recently read model.
|
|
|
|
if (nullptr != installed_font)
|
|
return installed_font->ManagedFont(); // found something in the "rtf family/face" on this device
|
|
|
|
const ON_wString loc_family_name(
|
|
(nullptr != installed_font && installed_font->FamilyName().IsNotEmpty())
|
|
? installed_font->FamilyName()
|
|
: ON_wString(rtf_font_name)
|
|
);
|
|
|
|
const ON_wString en_family_name(
|
|
(nullptr != installed_font && installed_font->m_en_family_name.IsNotEmpty())
|
|
? installed_font->m_en_family_name
|
|
: loc_family_name
|
|
);
|
|
|
|
// There is not font is not installed on this device with any type of name that is equal to rtf_font_name
|
|
ON_Font font((nullptr != installed_font) ? (*installed_font) : ON_Font::Default);
|
|
|
|
if (bRtfBold != font.IsBold())
|
|
font.SetFontWeight(bRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal);
|
|
if (bRtfItalic!= font.IsItalic())
|
|
font.SetFontStyle(bRtfItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright);
|
|
if (bRftUnderlined)
|
|
font.SetUnderlined(bRftUnderlined);
|
|
if (bRftStrikethrough)
|
|
font.SetUnderlined(bRftStrikethrough);
|
|
|
|
font.Internal_ClearAllNames();
|
|
|
|
ON_wString postscript_name = rtf_font_name;
|
|
ON_wString face_name;
|
|
if (bRtfBold && bRtfItalic)
|
|
{
|
|
postscript_name += L"-BoldItalic";
|
|
face_name = L"Bold Italic";
|
|
}
|
|
else if (bRtfBold)
|
|
{
|
|
postscript_name += L"-Bold";
|
|
face_name = L"Bold";
|
|
}
|
|
else if (bRtfItalic)
|
|
{
|
|
postscript_name += L"-Bold";
|
|
face_name = L"Italic";
|
|
}
|
|
else if (bRtfItalic)
|
|
{
|
|
face_name = L"Regular";
|
|
}
|
|
|
|
font.m_loc_family_name = loc_family_name;
|
|
font.m_en_family_name = en_family_name;
|
|
|
|
// Best guess face name
|
|
font.m_loc_face_name = face_name;
|
|
font.m_en_face_name = font.m_loc_face_name;
|
|
|
|
// Best guess PostScript name.
|
|
font.m_loc_postscript_name = postscript_name;
|
|
font.m_en_postscript_name = font.m_loc_postscript_name;
|
|
|
|
// This is not correct, but works better than anything else, especially when saving as V5.
|
|
font.m_loc_windows_logfont_name = rtf_font_name;
|
|
font.m_en_windows_logfont_name = font.m_loc_windows_logfont_name;
|
|
|
|
return font.ManagedFont();
|
|
}
|
|
|
|
const ON_2dex ON_FontList::Internal_SearchSortedList(
|
|
const ON_Font* key,
|
|
ON_FontPtrCompareFunc compare_func,
|
|
const ON_SimpleArray< const ON_Font* >& sorted_font_list
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == key || nullptr == compare_func)
|
|
break;
|
|
const int sorted_count = sorted_font_list.Count();
|
|
if (sorted_count <= 0)
|
|
break;
|
|
const int k = sorted_font_list.BinarySearch(&key, compare_func);
|
|
if (k < 0 || k >= sorted_count)
|
|
break;
|
|
int i0 = k;
|
|
while (i0 > 0 && 0 == compare_func(&key, &(sorted_font_list[i0 - 1]) ))
|
|
i0--;
|
|
int i1 = k+1;
|
|
while (i1 < sorted_count && 0 == compare_func(&key, &(sorted_font_list[i1]) ))
|
|
i1++;
|
|
return ON_2dex(i0, i1);
|
|
}
|
|
|
|
return ON_2dex(ON_UNSET_INT_INDEX,ON_UNSET_INT_INDEX);
|
|
|
|
}
|
|
|
|
void ON_FontList::Internal_EmptyLists()
|
|
{
|
|
m_by_index.SetCount(0);
|
|
m_unsorted.SetCount(0);
|
|
m_by_postscript_name.SetCount(0);
|
|
m_by_windows_logfont_name.SetCount(0);
|
|
m_by_family_name.SetCount(0);
|
|
m_by_english_postscript_name.SetCount(0);
|
|
m_by_english_windows_logfont_name.SetCount(0);
|
|
m_by_english_family_name.SetCount(0);
|
|
m_by_quartet_name.SetCount(0);
|
|
m_quartet_list.Destroy();
|
|
}
|
|
|
|
static int Internal_CompareLogfontNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
int rc = ON_FontList::CompareWindowsLogfontName(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ON_FontList::CompareWeightStretchStyle(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ON_FontList::CompareUnderlinedStrikethroughPointSize(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return ON_ManagedFonts::CompareFontCharacteristicsHash(lhs, rhs);
|
|
}
|
|
|
|
static int Internal_CompareFamilyNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
int rc = ON_FontList::CompareFamilyAndFaceName(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return Internal_CompareLogfontNameEtc(lhs, rhs);
|
|
}
|
|
|
|
static int Internal_ComparePostScriptNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
int rc = ON_FontList::ComparePostScriptName(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return Internal_CompareFamilyNameEtc(lhs, rhs);
|
|
}
|
|
|
|
|
|
static int Internal_CompareEnglishLogfontNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
int rc = ON_FontList::CompareEnglishWindowsLogfontName(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ON_FontList::CompareWeightStretchStyle(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ON_FontList::CompareUnderlinedStrikethroughPointSize(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return ON_ManagedFonts::CompareFontCharacteristicsHash(lhs, rhs);
|
|
}
|
|
|
|
static int Internal_CompareEnglishFamilyNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
int rc = ON_FontList::CompareEnglishFamilyAndFaceName(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return Internal_CompareEnglishLogfontNameEtc(lhs, rhs);
|
|
}
|
|
|
|
static int Internal_CompareEnglishPostScriptNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
int rc = ON_FontList::CompareEnglishPostScriptName(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return Internal_CompareEnglishFamilyNameEtc(lhs, rhs);
|
|
}
|
|
|
|
|
|
static int Internal_CompareQuartetNameEtc(ON_Font const* const* lhs, ON_Font const* const* rhs)
|
|
{
|
|
int rc = ON_FontList::CompareQuartetName(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ON_FontList::CompareWeightStretchStyle(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
rc = ON_FontList::CompareUnderlinedStrikethroughPointSize(lhs, rhs);
|
|
if (0 != rc)
|
|
return rc;
|
|
|
|
return ON_ManagedFonts::CompareFontCharacteristicsHash(lhs, rhs);
|
|
}
|
|
|
|
void ON_FontList::Internal_UpdateSortedLists() const
|
|
{
|
|
const int unsorted_count = m_unsorted.Count();
|
|
if (unsorted_count <= 0)
|
|
return;
|
|
|
|
ON_SimpleArray< const ON_Font* >* sorted_lists[7] =
|
|
{
|
|
&m_by_postscript_name,
|
|
&m_by_windows_logfont_name,
|
|
&m_by_family_name,
|
|
&m_by_english_postscript_name,
|
|
&m_by_english_windows_logfont_name,
|
|
&m_by_english_family_name,
|
|
&m_by_quartet_name
|
|
};
|
|
|
|
ON_FontPtrCompareFunc compare_funcs[7] =
|
|
{
|
|
Internal_ComparePostScriptNameEtc,
|
|
Internal_CompareLogfontNameEtc,
|
|
Internal_CompareFamilyNameEtc,
|
|
|
|
Internal_CompareEnglishPostScriptNameEtc,
|
|
Internal_CompareEnglishLogfontNameEtc,
|
|
Internal_CompareEnglishFamilyNameEtc,
|
|
|
|
Internal_CompareQuartetNameEtc
|
|
};
|
|
|
|
const int array_dex_max = (int)(sizeof(sorted_lists) / sizeof(sorted_lists[0]));
|
|
|
|
for (int array_dex = 0; array_dex < array_dex_max; array_dex++)
|
|
{
|
|
ON_SimpleArray< const ON_Font* >& sorted_list = *(sorted_lists[array_dex]);
|
|
|
|
bool bNeedSort = false;
|
|
sorted_list.Reserve(sorted_list.Count() + unsorted_count);
|
|
for (int j = 0; j < unsorted_count; j++)
|
|
{
|
|
const ON_Font* font = m_unsorted[j];
|
|
if (nullptr == font)
|
|
continue;
|
|
if (0 == array_dex)
|
|
{
|
|
if (font->PostScriptName(m_name_locale).IsEmpty())
|
|
continue;
|
|
}
|
|
else if (1 == array_dex)
|
|
{
|
|
if (font->WindowsLogfontName(m_name_locale).IsEmpty())
|
|
continue;
|
|
}
|
|
else if (2 == array_dex)
|
|
{
|
|
if (font->FamilyName(m_name_locale).IsEmpty())
|
|
continue;
|
|
}
|
|
else if (3 == array_dex)
|
|
{
|
|
const ON_wString en = font->PostScriptName(ON_Font::NameLocale::English);
|
|
if (en.IsEmpty())
|
|
continue;
|
|
if (ON_wString::EqualOrdinal(en, font->PostScriptName(ON_Font::NameLocale::English), true))
|
|
continue;
|
|
}
|
|
else if (4 == array_dex)
|
|
{
|
|
const ON_wString en = font->WindowsLogfontName(ON_Font::NameLocale::English);
|
|
if (en.IsEmpty())
|
|
continue;
|
|
if (ON_wString::EqualOrdinal(en, font->WindowsLogfontName(ON_Font::NameLocale::English), true))
|
|
continue;
|
|
}
|
|
else if (5 == array_dex)
|
|
{
|
|
const ON_wString en = font->FamilyName(ON_Font::NameLocale::English);
|
|
if (en.IsEmpty())
|
|
continue;
|
|
if (ON_wString::EqualOrdinal(en, font->FamilyName(ON_Font::NameLocale::English), true))
|
|
continue;
|
|
}
|
|
else if (6 == array_dex)
|
|
{
|
|
const ON_wString qname = font->QuartetName();
|
|
if (qname.IsEmpty())
|
|
continue;
|
|
// m_quartet_list[] will get remade when it's needed
|
|
m_quartet_list.SetCount(0);
|
|
}
|
|
|
|
sorted_list.Append(font);
|
|
bNeedSort = true;
|
|
}
|
|
|
|
if ( bNeedSort )
|
|
sorted_list.QuickSort(compare_funcs[array_dex]);
|
|
}
|
|
|
|
m_unsorted.SetCount(0);
|
|
}
|
|
|
|
ON_FontList::ON_FontList(
|
|
bool bMatchUnderlineAndStrikethrough
|
|
)
|
|
: m_bMatchUnderlineStrikethroughAndPointSize(bMatchUnderlineAndStrikethrough)
|
|
{}
|
|
|
|
|
|
unsigned int ON_FontList::Count() const
|
|
{
|
|
return m_by_index.UnsignedCount();
|
|
}
|
|
|
|
ON_Font::NameLocale ON_FontList::NameLocale() const
|
|
{
|
|
return m_name_locale;
|
|
}
|
|
|
|
|
|
const ON_Font* ON_FontList::FromPostScriptName(
|
|
const wchar_t* postscript_name
|
|
) const
|
|
{
|
|
return FromPostScriptName(postscript_name, ON_Font::Weight::Normal, ON_Font::Stretch::Condensed, ON_Font::Style::Upright, false, false);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromPostScriptName(
|
|
const wchar_t* postscript_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style
|
|
) const
|
|
{
|
|
return FromNames(
|
|
postscript_name,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
false,
|
|
false
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromPostScriptName(
|
|
const wchar_t* postscript_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
) const
|
|
{
|
|
return FromNames(
|
|
postscript_name,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
false,
|
|
false,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
0.0
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromWindowsLogfontName(
|
|
const wchar_t* windows_logfont_name
|
|
) const
|
|
{
|
|
return FromWindowsLogfontName(windows_logfont_name, ON_Font::Weight::Normal, ON_Font::Stretch::Condensed, ON_Font::Style::Upright);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromWindowsLogfontName(
|
|
const wchar_t* windows_logfont_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style
|
|
) const
|
|
{
|
|
return FromNames(
|
|
nullptr,
|
|
windows_logfont_name,
|
|
nullptr,
|
|
nullptr,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
false,
|
|
false
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromWindowsLogfontName(
|
|
const wchar_t* windows_logfont_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
) const
|
|
{
|
|
return FromNames(
|
|
nullptr,
|
|
windows_logfont_name,
|
|
nullptr,
|
|
nullptr,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
false,
|
|
false,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
0.0
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromFamilyName(
|
|
const wchar_t* family_name,
|
|
const wchar_t* prefered_face_name
|
|
) const
|
|
{
|
|
return FromFamilyName(family_name, prefered_face_name, ON_Font::Weight::Normal, ON_Font::Stretch::Medium, ON_Font::Style::Upright);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromFamilyName(
|
|
const wchar_t* family_name,
|
|
const wchar_t* prefered_face_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style
|
|
) const
|
|
{
|
|
return FromNames(
|
|
nullptr,
|
|
nullptr,
|
|
family_name,
|
|
prefered_face_name,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
false,
|
|
false
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromFamilyName(
|
|
const wchar_t* family_name,
|
|
const wchar_t* prefered_face_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
) const
|
|
{
|
|
return FromNames(
|
|
nullptr,
|
|
nullptr,
|
|
family_name,
|
|
prefered_face_name,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
false,
|
|
false,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
0.0
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromRichTextProperties(
|
|
const wchar_t* rtf_font_name,
|
|
bool bRtfBold,
|
|
bool bRtfItalic,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
) const
|
|
{
|
|
// ballpark weight and stretch values. Closest match is returned.
|
|
const ON_Font::Weight prefered_weight = bRtfBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal;
|
|
const ON_Font::Stretch prefered_stretch = ON_Font::Stretch::Medium;
|
|
const ON_Font::Style prefered_style = bRtfItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright;
|
|
|
|
bool bRequireFaceMatch = false;
|
|
bool bRequireStyleMatch = true;
|
|
|
|
return FromNames(
|
|
rtf_font_name, // PostScript guess
|
|
rtf_font_name, // Windows LOGFONT.lfFaceName guess
|
|
rtf_font_name, // Family name guess
|
|
nullptr,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
bRequireFaceMatch,
|
|
bRequireStyleMatch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
0.0
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromNames(
|
|
const wchar_t* postscript_name,
|
|
const wchar_t* windows_logfont_name,
|
|
const wchar_t* family_name,
|
|
const wchar_t* prefered_face_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
bool bRequireFaceMatch,
|
|
bool bRequireStyleMatch
|
|
) const
|
|
{
|
|
const bool bMatchUnderlineStrikethroughAndPointSize = false;
|
|
return Internal_FromNames(
|
|
postscript_name,
|
|
windows_logfont_name,
|
|
family_name,
|
|
prefered_face_name,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
bRequireFaceMatch,
|
|
bRequireStyleMatch,
|
|
bMatchUnderlineStrikethroughAndPointSize,
|
|
false,
|
|
false,
|
|
0.0
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromNames(
|
|
const wchar_t* postscript_name,
|
|
const wchar_t* windows_logfont_name,
|
|
const wchar_t* family_name,
|
|
const wchar_t* prefered_face_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
bool bRequireFaceMatch,
|
|
bool bRequireStyleMatch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough,
|
|
double point_size
|
|
) const
|
|
{
|
|
bool bMatchUnderlineStrikethroughAndPointSize = true;
|
|
return Internal_FromNames(
|
|
postscript_name,
|
|
windows_logfont_name,
|
|
family_name,
|
|
prefered_face_name,
|
|
prefered_weight,
|
|
prefered_stretch,
|
|
prefered_style,
|
|
bRequireFaceMatch,
|
|
bRequireStyleMatch,
|
|
bMatchUnderlineStrikethroughAndPointSize,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
point_size
|
|
);
|
|
}
|
|
|
|
|
|
const ON_Font* ON_FontList::Internal_FromNames(
|
|
const wchar_t* postscript_name,
|
|
const wchar_t* windows_logfont_name,
|
|
const wchar_t* family_name,
|
|
const wchar_t* prefered_face_name,
|
|
ON_Font::Weight prefered_weight,
|
|
ON_Font::Stretch prefered_stretch,
|
|
ON_Font::Style prefered_style,
|
|
bool bRequireFaceMatch,
|
|
bool bRequireStyleMatch,
|
|
bool bMatchUnderlineStrikethroughAndPointSize,
|
|
bool bUnderlined,
|
|
bool bStrikethrough,
|
|
double point_size
|
|
) const
|
|
{
|
|
if (ON_Font::Stretch::Unset == prefered_stretch)
|
|
bRequireStyleMatch = false;
|
|
|
|
if (bUnderlined)
|
|
bUnderlined = true;
|
|
|
|
if (bStrikethrough)
|
|
bStrikethrough = true;
|
|
|
|
if (!(point_size > 0.0 && point_size < ON_UNSET_POSITIVE_FLOAT))
|
|
point_size = 0.0;
|
|
|
|
if (false == m_bMatchUnderlineStrikethroughAndPointSize)
|
|
bMatchUnderlineStrikethroughAndPointSize = false;
|
|
|
|
const ON_SimpleArray< const ON_Font* > * sorted_lists[16] = {};
|
|
|
|
ON_FontPtrCompareFunc compare_funcs[16] = { 0 };
|
|
|
|
ON_Font key;
|
|
|
|
key.m_loc_postscript_name = postscript_name;
|
|
key.m_loc_postscript_name.TrimLeftAndRight();
|
|
key.m_en_postscript_name = key.m_loc_postscript_name;
|
|
|
|
key.m_loc_windows_logfont_name = windows_logfont_name;
|
|
key.m_loc_windows_logfont_name.TrimLeftAndRight();
|
|
key.m_en_windows_logfont_name = key.m_loc_windows_logfont_name;
|
|
|
|
key.m_loc_family_name = family_name;
|
|
key.m_loc_family_name.TrimLeftAndRight();
|
|
key.m_en_family_name = key.m_loc_family_name;
|
|
|
|
key.m_loc_face_name = prefered_face_name;
|
|
key.m_loc_face_name.TrimLeftAndRight();
|
|
key.m_en_face_name = key.m_loc_face_name;
|
|
|
|
key.m_font_weight = prefered_weight;
|
|
key.m_font_stretch = prefered_stretch;
|
|
key.m_font_style = prefered_style;
|
|
|
|
|
|
const bool bKeyHasFamilyAndFace = key.m_loc_family_name.IsNotEmpty() && key.m_loc_face_name.IsNotEmpty();
|
|
|
|
if (false == bKeyHasFamilyAndFace)
|
|
bRequireFaceMatch = false;
|
|
|
|
int pass_count = 0;
|
|
|
|
// First compare family AND face name. In general, there will not be multiple
|
|
// fonts with the same family and face name combination.
|
|
if (key.m_loc_family_name.IsNotEmpty() && key.m_loc_face_name.IsNotEmpty())
|
|
{
|
|
sorted_lists[pass_count] = &m_by_family_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareFamilyAndFaceName;
|
|
pass_count++;
|
|
|
|
sorted_lists[pass_count] = &m_by_english_family_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareEnglishFamilyAndFaceName;
|
|
pass_count++;
|
|
}
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
// On Windows, check LOGFONT.lfFaceName before PostScript
|
|
// It is common for 4 distinct faces to have the same LOGFONT lfFaceName.
|
|
if (key.m_loc_windows_logfont_name.IsNotEmpty())
|
|
{
|
|
sorted_lists[pass_count] = &m_by_windows_logfont_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareWindowsLogfontName;
|
|
pass_count++;
|
|
|
|
sorted_lists[pass_count] = &m_by_english_windows_logfont_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareEnglishWindowsLogfontName;
|
|
pass_count++;
|
|
}
|
|
#endif
|
|
|
|
// Check PostScript name
|
|
// On Windows, when simulated fonts or OpenType variable face fonts are in use,
|
|
// it is very common for distict faces to have the same PostScript font name.
|
|
// It is less common in MacOS.
|
|
if (key.m_loc_postscript_name.IsNotEmpty())
|
|
{
|
|
sorted_lists[pass_count] = &m_by_postscript_name;
|
|
compare_funcs[pass_count] = ON_FontList::ComparePostScriptName;
|
|
pass_count++;
|
|
|
|
sorted_lists[pass_count] = &m_by_english_postscript_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareEnglishPostScriptName;
|
|
pass_count++;
|
|
}
|
|
|
|
#if !defined(ON_RUNTIME_WIN)
|
|
// Windows LOGFONT.lfFaceName checked after PostScript
|
|
// It is common for 4 distinct faces to have the same LOGFONT lfFaceName.
|
|
if (key.m_loc_windows_logfont_name.IsNotEmpty())
|
|
{
|
|
sorted_lists[pass_count] = &m_by_windows_logfont_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareWindowsLogfontName;
|
|
pass_count++;
|
|
|
|
sorted_lists[pass_count] = &m_by_english_windows_logfont_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareEnglishWindowsLogfontName;
|
|
pass_count++;
|
|
}
|
|
#endif
|
|
|
|
// The final passes search by Family name.
|
|
// It is generally the case that multiple faces have the same family name.
|
|
if (key.m_loc_family_name.IsNotEmpty())
|
|
{
|
|
sorted_lists[pass_count] = &m_by_family_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareFamilyName;
|
|
pass_count++;
|
|
|
|
sorted_lists[pass_count] = &m_by_english_family_name;
|
|
compare_funcs[pass_count] = ON_FontList::CompareEnglishFamilyName;
|
|
pass_count++;
|
|
}
|
|
|
|
if (pass_count <= 0)
|
|
return nullptr;
|
|
|
|
// Move any unsorted fonts into the sorted lists.
|
|
Internal_UpdateSortedLists();
|
|
|
|
const ON_Font* font = nullptr;
|
|
unsigned int font_dev = 0xFFFFFFFF;
|
|
|
|
const ON_Font* pkey = &key;
|
|
|
|
for (int pass = 0; pass < pass_count; pass++)
|
|
{
|
|
ON_FontPtrCompareFunc compare_func = compare_funcs[pass];
|
|
if (nullptr == compare_func)
|
|
continue;
|
|
|
|
const ON_Font* candidate = nullptr;
|
|
unsigned int candidate_dev = 0xFFFFFFFF;
|
|
|
|
for (int list_dex = 0; list_dex < 2; list_dex++)
|
|
{
|
|
if (1 == list_dex)
|
|
{
|
|
if (nullptr == sorted_lists[pass])
|
|
continue;
|
|
}
|
|
|
|
const ON_SimpleArray< const ON_Font* >& sorted_list
|
|
= (1==list_dex)
|
|
? *(sorted_lists[pass])
|
|
: m_unsorted;
|
|
|
|
const ON_2dex subset
|
|
= (1==list_dex)
|
|
? ON_FontList::Internal_SearchSortedList(&key, compare_func, sorted_list)
|
|
: ON_2dex(0,m_unsorted.Count())
|
|
;
|
|
if (subset.i < 0 || subset.j <= 0)
|
|
continue;
|
|
|
|
for (int i = subset.i; i < subset.j; i++)
|
|
{
|
|
candidate = sorted_list[i];
|
|
if (nullptr == candidate)
|
|
continue;
|
|
|
|
if (0 == list_dex)
|
|
{
|
|
if (0 != compare_func(&pkey, &candidate))
|
|
continue;
|
|
}
|
|
|
|
if (bMatchUnderlineStrikethroughAndPointSize)
|
|
{
|
|
if (candidate->IsUnderlined() != bUnderlined)
|
|
continue;
|
|
if (candidate->IsStrikethrough() != bStrikethrough)
|
|
continue;
|
|
if (candidate->PointSize() != point_size)
|
|
continue;
|
|
}
|
|
if (bRequireStyleMatch && prefered_style != candidate->FontStyle())
|
|
continue;
|
|
|
|
const bool bCandidateFamilyAndFaceMatch
|
|
= bKeyHasFamilyAndFace
|
|
&& (ON_Font::EqualFontFamilyAndFace(&key, candidate) || 0 == ON_FontList::CompareEnglishFamilyAndFaceName(&pkey,&candidate));
|
|
|
|
if (bRequireFaceMatch && candidate->FamilyName().IsNotEmpty() && false == bCandidateFamilyAndFaceMatch)
|
|
continue;
|
|
|
|
const bool bFontFamilyAndFaceMatch
|
|
= bKeyHasFamilyAndFace
|
|
&& (nullptr != font)
|
|
&& (ON_Font::EqualFontFamilyAndFace(&key, font) || 0 == ON_FontList::CompareEnglishFamilyAndFaceName(&pkey,&candidate));
|
|
|
|
if (bFontFamilyAndFaceMatch && false == bCandidateFamilyAndFaceMatch)
|
|
continue;
|
|
|
|
candidate_dev = ON_Font::WeightStretchStyleDeviation(prefered_weight, prefered_stretch, prefered_style, candidate);
|
|
if (0 == candidate_dev)
|
|
{
|
|
if ( bCandidateFamilyAndFaceMatch || false == bKeyHasFamilyAndFace)
|
|
return candidate;
|
|
}
|
|
|
|
if (
|
|
nullptr == font
|
|
|| (bCandidateFamilyAndFaceMatch && false == bFontFamilyAndFaceMatch)
|
|
|| candidate_dev < font_dev
|
|
)
|
|
{
|
|
font = candidate;
|
|
font_dev = candidate_dev;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return font;
|
|
}
|
|
|
|
unsigned int ON_FontList::FontListFromNames(
|
|
const wchar_t* postscript_name,
|
|
const wchar_t* windows_logfont_name,
|
|
const wchar_t* family_name,
|
|
const wchar_t* face_name,
|
|
ON_SimpleArray< const ON_Font* >& font_list
|
|
) const
|
|
{
|
|
const unsigned int font_list_count0 = font_list.UnsignedCount();
|
|
|
|
ON_Font key;
|
|
|
|
key.m_loc_postscript_name = postscript_name;
|
|
key.m_loc_postscript_name.TrimLeftAndRight();
|
|
key.m_en_postscript_name = key.m_loc_postscript_name;
|
|
|
|
key.m_loc_windows_logfont_name = windows_logfont_name;
|
|
key.m_loc_windows_logfont_name.TrimLeftAndRight();
|
|
key.m_en_windows_logfont_name = key.m_loc_windows_logfont_name;
|
|
|
|
key.m_loc_family_name = family_name;
|
|
key.m_loc_family_name.TrimLeftAndRight();
|
|
key.m_en_family_name = key.m_loc_family_name;
|
|
|
|
Internal_UpdateSortedLists();
|
|
|
|
for (int pass = 0; pass < 3; pass++)
|
|
{
|
|
ON_SimpleArray< const ON_Font* >* sorted_list = nullptr;
|
|
ON_2dex subset(-1, -1);
|
|
switch (pass)
|
|
{
|
|
case 0:
|
|
if (key.m_loc_postscript_name.IsEmpty())
|
|
continue;
|
|
sorted_list = &m_by_postscript_name;
|
|
subset = Internal_SearchSortedList(&key, ON_FontList::ComparePostScriptName, *sorted_list);
|
|
break;
|
|
|
|
case 1:
|
|
if (key.m_loc_windows_logfont_name.IsEmpty())
|
|
continue;
|
|
sorted_list = &m_by_windows_logfont_name;
|
|
subset = Internal_SearchSortedList(&key, ON_FontList::CompareWindowsLogfontName, *sorted_list);
|
|
break;
|
|
|
|
case 2:
|
|
if (key.m_loc_family_name.IsEmpty())
|
|
continue;
|
|
sorted_list = &m_by_family_name;
|
|
subset = Internal_SearchSortedList(&key, ON_FontList::CompareFamilyName, *sorted_list);
|
|
break;
|
|
}
|
|
|
|
if (subset.j <= 0)
|
|
break;
|
|
|
|
const ON_Font* pkey = &key;
|
|
for (int i = subset.i; i < subset.j; i++)
|
|
{
|
|
const ON_Font* font = (*sorted_list)[i];
|
|
if (
|
|
pass < 1
|
|
&& key.m_loc_windows_logfont_name.IsNotEmpty()
|
|
&& 0 != ON_FontList::CompareWindowsLogfontName(&pkey, &font)
|
|
)
|
|
continue;
|
|
if (key.m_loc_family_name.IsNotEmpty())
|
|
{
|
|
if (0 != ON_FontList::CompareFamilyName(&pkey, &font))
|
|
continue;
|
|
if (key.m_loc_face_name.IsNotEmpty()
|
|
&& false == ON_wString::EqualOrdinal(key.FaceName(m_name_locale), font->FaceName(m_name_locale), true)
|
|
)
|
|
continue;
|
|
}
|
|
font_list.Append(font);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return font_list.UnsignedCount() - font_list_count0;
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromFontProperties(
|
|
const ON_Font* font_properties,
|
|
bool bRequireFaceMatch,
|
|
bool bRequireStyleMatch
|
|
) const
|
|
{
|
|
return FromNames(
|
|
font_properties->PostScriptName(m_name_locale),
|
|
font_properties->WindowsLogfontName(m_name_locale),
|
|
font_properties->FamilyName(m_name_locale),
|
|
font_properties->FaceName(m_name_locale),
|
|
font_properties->FontWeight(),
|
|
font_properties->FontStretch(),
|
|
font_properties->FontStyle(),
|
|
bRequireFaceMatch,
|
|
bRequireStyleMatch
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FromFontProperties(
|
|
const ON_Font* font_properties,
|
|
bool bRequireFaceMatch,
|
|
bool bRequireStyleMatch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough,
|
|
double point_size
|
|
) const
|
|
{
|
|
return FromNames(
|
|
font_properties->PostScriptName(m_name_locale),
|
|
font_properties->WindowsLogfontName(m_name_locale),
|
|
font_properties->FamilyName(m_name_locale),
|
|
font_properties->FaceName(m_name_locale),
|
|
font_properties->FontWeight(),
|
|
font_properties->FontStretch(),
|
|
font_properties->FontStyle(),
|
|
bRequireFaceMatch,
|
|
bRequireStyleMatch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
point_size
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FamilyMemberWithWeightStretchStyle(
|
|
const wchar_t* family_name,
|
|
ON_Font::Weight desired_weight,
|
|
ON_Font::Stretch desired_stretch,
|
|
ON_Font::Style desired_style
|
|
) const
|
|
{
|
|
ON_Font key;
|
|
key.m_loc_family_name = family_name;
|
|
key.m_loc_family_name.TrimLeftAndRight();
|
|
if (key.m_loc_family_name.IsEmpty())
|
|
key.m_loc_family_name = ON_Font::DefaultFamilyName();
|
|
else
|
|
key.m_en_family_name = key.m_loc_family_name;
|
|
|
|
key.m_font_weight = desired_weight;
|
|
key.m_font_stretch = desired_stretch;
|
|
key.m_font_style = desired_style;
|
|
|
|
const ON_2dex subdex = Internal_SearchSortedList(&key, ON_FontList::CompareFamilyName, m_by_family_name);
|
|
if (subdex.j <= 0)
|
|
return nullptr;
|
|
const ON_Font* candidate = nullptr;
|
|
unsigned int candidate_dev = 0xFFFFFFFF;
|
|
for (int i = subdex.i; i < subdex.j; i++)
|
|
{
|
|
const ON_Font* font = m_by_family_name[i];
|
|
if (nullptr == font)
|
|
continue;
|
|
unsigned int font_dev = ON_Font::WeightStretchStyleDeviation(&key, font);
|
|
if (nullptr == candidate || font_dev < candidate_dev)
|
|
{
|
|
candidate = font;
|
|
candidate_dev = font_dev;
|
|
}
|
|
}
|
|
return candidate;
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FamilyMemberWithWeightStretchStyle(
|
|
const ON_Font* font,
|
|
ON_Font::Weight desired_weight,
|
|
ON_Font::Stretch desired_stretch,
|
|
ON_Font::Style desired_style
|
|
) const
|
|
{
|
|
if (nullptr == font)
|
|
font = &ON_Font::Default;
|
|
else if ( font->FamilyName().IsEmpty() && font->FaceName().IsEmpty() && font->WindowsLogfontName().IsEmpty() )
|
|
font = &ON_Font::Default;
|
|
|
|
if (ON_Font::Weight::Unset == desired_weight)
|
|
desired_weight = font->FontWeight();
|
|
if (ON_Font::Stretch::Unset == desired_stretch)
|
|
desired_stretch = font->FontStretch();
|
|
if (ON_Font::Style::Unset == desired_style)
|
|
desired_style = font->FontStyle();
|
|
|
|
ON_wString family_name = font->FamilyName();
|
|
if (family_name.IsEmpty())
|
|
{
|
|
const ON_SimpleArray< const ON_Font* > * sorted_lists[2] = {&m_by_windows_logfont_name,&m_by_postscript_name};
|
|
ON_FontPtrCompareFunc compare_funcs[2] = {ON_FontList::CompareWindowsLogfontName,ON_FontList::ComparePostScriptName};
|
|
const bool bNameIsEmpty[2] = { font->WindowsLogfontName().IsEmpty(),font->PostScriptName().IsEmpty() };
|
|
const int k1 = (int)(sizeof(bNameIsEmpty) / sizeof(bNameIsEmpty[0]));
|
|
for (int k = 0; k < k1 && family_name.IsEmpty(); k++)
|
|
{
|
|
if (bNameIsEmpty[k])
|
|
continue;
|
|
const ON_SimpleArray< const ON_Font* >& sorted_list = *sorted_lists[k];
|
|
const ON_2dex subdex = Internal_SearchSortedList(font, compare_funcs[k], sorted_list);
|
|
if (subdex.j <= 0)
|
|
continue;
|
|
for (int i = subdex.i; i < subdex.j && family_name.IsEmpty(); i++)
|
|
{
|
|
const ON_Font* f = sorted_list[i];
|
|
if (nullptr == f)
|
|
continue;
|
|
family_name = f->FamilyName();
|
|
if (family_name.IsNotEmpty())
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const ON_Font* family_member = FamilyMemberWithWeightStretchStyle(family_name, desired_weight, desired_stretch, desired_style);
|
|
return family_member;
|
|
}
|
|
|
|
const ON_Font* ON_Font::InstalledFamilyMemberWithWeightStretchStyle(
|
|
ON_Font::Weight desired_weight,
|
|
ON_Font::Stretch desired_stretch,
|
|
ON_Font::Style desired_style
|
|
) const
|
|
{
|
|
return ON_Font::InstalledFontList().FamilyMemberWithWeightStretchStyle(
|
|
this,
|
|
desired_weight,
|
|
desired_stretch,
|
|
desired_style
|
|
);
|
|
}
|
|
|
|
|
|
const ON_Font* ON_Font::ManagedFamilyMemberWithRichTextProperties(
|
|
bool bBold,
|
|
bool bItalic,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
) const
|
|
{
|
|
// Get clean boolean values for safe comparisons.
|
|
bBold = bBold ? true : false;
|
|
bItalic = bItalic ? true : false;
|
|
bUnderlined = bUnderlined ? true : false;
|
|
bStrikethrough = bStrikethrough ? true : false;
|
|
|
|
const ON_Font::Weight current_weight = FontWeight();
|
|
ON_Font::Weight desired_weight = current_weight;
|
|
if (bBold != IsBoldInQuartet())
|
|
{
|
|
const ON_FontFaceQuartet q = InstalledFontQuartet();
|
|
const ON_Font* f = nullptr;
|
|
if (bBold)
|
|
{
|
|
// increase desired_weight
|
|
f = (bItalic) ? q.BoldItalicFace() : q.BoldFace();
|
|
if ( nullptr == f)
|
|
f = (bItalic) ? q.BoldFace() : q.BoldItalicFace();
|
|
if (nullptr != f)
|
|
{
|
|
if (ON_Font::Weight::Unset == current_weight || static_cast<unsigned char>(f->FontWeight()) > static_cast<unsigned char>(current_weight))
|
|
desired_weight = f->FontWeight();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// descrease desired_weight
|
|
f = (bItalic) ? q.ItalicFace() : q.RegularFace();
|
|
if ( nullptr == f)
|
|
f = (bItalic) ? q.RegularFace() : q.ItalicFace();
|
|
if (nullptr != f)
|
|
{
|
|
if (ON_Font::Weight::Unset == current_weight || static_cast<unsigned char>(f->FontWeight()) < static_cast<unsigned char>(current_weight))
|
|
desired_weight = f->FontWeight();
|
|
}
|
|
}
|
|
}
|
|
|
|
const ON_Font::Style desired_style
|
|
= (bItalic != IsItalic())
|
|
? (bItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright)
|
|
: FontStyle();
|
|
|
|
return ManagedFamilyMemberWithWeightStretchStyle(
|
|
desired_weight,
|
|
FontStretch(),
|
|
desired_style,
|
|
bUnderlined,
|
|
bStrikethrough
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_Font::ManagedFamilyMemberWithWeightStretchStyle(
|
|
ON_Font::Weight desired_weight,
|
|
ON_Font::Stretch desired_stretch,
|
|
ON_Font::Style desired_style,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
) const
|
|
{
|
|
if (ON_Font::Weight::Unset == desired_weight)
|
|
desired_weight = FontWeight();
|
|
if (ON_Font::Stretch::Unset == desired_stretch)
|
|
desired_stretch = FontStretch();
|
|
if (ON_Font::Style::Unset == desired_style)
|
|
desired_style = FontStyle();
|
|
|
|
bool bChangeWeight = (desired_weight != FontWeight());
|
|
bool bChangeStretch = (desired_stretch != FontStretch());
|
|
bool bChangeStyle = (desired_style != FontStyle());
|
|
bool bChangeUnderlined = ((bUnderlined ? true : false) != IsUnderlined());
|
|
bool bChangeStrikethrough = ((bStrikethrough ? true : false) != IsStrikethrough());
|
|
|
|
bool bChangeSomething
|
|
= bChangeWeight
|
|
|| bChangeStretch
|
|
|| bChangeStyle
|
|
|| bChangeUnderlined
|
|
|| bChangeStrikethrough;
|
|
|
|
const ON_Font* font = this;
|
|
|
|
if ( bChangeWeight || bChangeStretch || bChangeStyle )
|
|
{
|
|
const ON_Font* installed_font = InstalledFamilyMemberWithWeightStretchStyle(desired_weight, desired_stretch, desired_style);
|
|
if (nullptr != installed_font)
|
|
{
|
|
font = installed_font;
|
|
bChangeWeight = false;
|
|
bChangeStretch = false;
|
|
bChangeStyle = false;
|
|
bChangeUnderlined = (bUnderlined ? true : false) != installed_font->IsUnderlined();
|
|
bChangeStrikethrough = ((bStrikethrough ? true : false) != installed_font->IsStrikethrough());
|
|
bChangeSomething
|
|
= bChangeWeight
|
|
|| bChangeStretch
|
|
|| bChangeStyle
|
|
|| bChangeUnderlined
|
|
|| bChangeStrikethrough;
|
|
}
|
|
}
|
|
|
|
if (false == bChangeSomething)
|
|
return font->ManagedFont();
|
|
|
|
ON_Font changed_font(*font);
|
|
|
|
if (bChangeWeight && ON_Font::Weight::Unset != desired_weight)
|
|
changed_font.SetFontWeight(desired_weight);
|
|
|
|
if (bChangeStretch && ON_Font::Stretch::Unset != desired_stretch)
|
|
changed_font.SetFontStretch(desired_stretch);
|
|
|
|
if (bChangeStyle && ON_Font::Style::Unset != desired_style)
|
|
changed_font.SetFontStyle(desired_style);
|
|
|
|
if (bChangeUnderlined)
|
|
changed_font.SetUnderlined(bUnderlined);
|
|
|
|
if (bChangeStrikethrough)
|
|
changed_font.SetStrikethrough(bStrikethrough);
|
|
|
|
return changed_font.ManagedFont();
|
|
}
|
|
|
|
const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByPostScriptName() const
|
|
{
|
|
Internal_UpdateSortedLists();
|
|
return m_by_postscript_name;
|
|
}
|
|
|
|
const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByWindowsLogfontName() const
|
|
{
|
|
Internal_UpdateSortedLists();
|
|
return m_by_windows_logfont_name;
|
|
}
|
|
|
|
const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByFamilyName() const
|
|
{
|
|
Internal_UpdateSortedLists();
|
|
return m_by_family_name;
|
|
}
|
|
|
|
const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByQuartetName() const
|
|
{
|
|
Internal_UpdateSortedLists();
|
|
return m_by_quartet_name;
|
|
}
|
|
|
|
const ON_SimpleArray< const class ON_Font* >& ON_FontList::ByIndex() const
|
|
{
|
|
return m_by_index;
|
|
}
|
|
|
|
const ON_FontFaceQuartet ON_FontList::QuartetFromQuartetName(
|
|
const wchar_t* quartet_name
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
const ON_FontFaceQuartet qname(quartet_name, nullptr, nullptr, nullptr, nullptr);
|
|
if (qname.QuartetName().IsEmpty())
|
|
break;
|
|
|
|
const ON_ClassArray< ON_FontFaceQuartet >& quartet_list = ON_FontList::QuartetList();
|
|
const int quartet_list_count = quartet_list.Count();
|
|
int i = quartet_list.BinarySearch(&qname, ON_FontFaceQuartet::CompareQuartetName);
|
|
if (i < 0 || i >= quartet_list_count)
|
|
break;
|
|
|
|
while (i > 0 && 0 == ON_FontFaceQuartet::CompareQuartetName(&qname, &quartet_list[i - 1]))
|
|
i--;
|
|
return quartet_list[i];
|
|
}
|
|
return ON_FontFaceQuartet::Empty;
|
|
}
|
|
|
|
const ON_Font* ON_FontList::FontFromQuartetProperties(
|
|
const wchar_t* quartet_name,
|
|
bool bBold,
|
|
bool bItalic
|
|
) const
|
|
{
|
|
const ON_FontFaceQuartet qname(quartet_name, nullptr, nullptr, nullptr, nullptr);
|
|
if (qname.QuartetName().IsEmpty())
|
|
return nullptr;
|
|
|
|
const ON_ClassArray< ON_FontFaceQuartet >& quartet_list = ON_FontList::QuartetList();
|
|
const int quartet_list_count = quartet_list.Count();
|
|
int i = quartet_list.BinarySearch(&qname, ON_FontFaceQuartet::CompareQuartetName);
|
|
if (i < 0 || i >= quartet_list_count)
|
|
return nullptr;
|
|
|
|
while (i > 0 && 0 == ON_FontFaceQuartet::CompareQuartetName(&qname, &quartet_list[i - 1]))
|
|
i--;
|
|
|
|
do
|
|
{
|
|
const ON_FontFaceQuartet& q = quartet_list[i];
|
|
if (0 != ON_FontFaceQuartet::CompareQuartetName(&qname, &q))
|
|
break;
|
|
const ON_Font* font = q.Face(bBold, bItalic);
|
|
if (nullptr != font)
|
|
return font;
|
|
i++;
|
|
} while (i < quartet_list_count);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const
|
|
{
|
|
// call ByQuartetName() first to insure m_quartet_list[] is set correctly.
|
|
const ON_SimpleArray<const ON_Font*>& a = this->ByQuartetName();
|
|
|
|
if (m_quartet_list.Count() > 0)
|
|
return m_quartet_list;
|
|
|
|
const unsigned int font_count = a.UnsignedCount();
|
|
m_quartet_list.Reserve(32 + font_count / 4);
|
|
|
|
const ON_Font* f = nullptr;
|
|
const ON_Font* upright6[6] = {};
|
|
const ON_Font* italic6[6] = {};
|
|
|
|
for (unsigned int i = 0; i < font_count; i++)
|
|
{
|
|
f = a[i];
|
|
if (nullptr == f)
|
|
continue;
|
|
|
|
const ON_wString quartet_name = f->QuartetName();
|
|
if (quartet_name.IsEmpty())
|
|
continue;
|
|
|
|
memset(upright6, 0, sizeof(upright6));
|
|
memset(italic6, 0, sizeof(italic6));
|
|
|
|
const ON_Font** sextet = (f->IsItalic()) ? italic6 : upright6;
|
|
if (ON_Font::Weight::Normal == f->FontWeight())
|
|
sextet[2] = f;
|
|
else if (ON_Font::Weight::Medium == f->FontWeight())
|
|
sextet[3] = f;
|
|
else if (f->IsBold())
|
|
sextet[4] = f;
|
|
else if (f->IsLight())
|
|
sextet[1] = f;
|
|
else
|
|
continue;
|
|
|
|
while(i+1 < font_count)
|
|
{
|
|
f = a[i + 1];
|
|
if (nullptr == f)
|
|
break;
|
|
if (false == quartet_name.EqualOrdinal(f->QuartetName(), true))
|
|
break;
|
|
i++;
|
|
|
|
const unsigned int f_weight = static_cast<unsigned char>(f->FontWeight());
|
|
sextet = (f->IsItalic()) ? italic6 : upright6;
|
|
if (ON_Font::Weight::Normal == f->FontWeight())
|
|
sextet[2] = f;
|
|
else if (ON_Font::Weight::Medium == f->FontWeight())
|
|
sextet[3] = f;
|
|
else if (f->IsBold())
|
|
{
|
|
if (nullptr == sextet[4])
|
|
sextet[4] = f;
|
|
else if (f_weight < static_cast<unsigned char>(sextet[4]->FontWeight()))
|
|
{
|
|
if (nullptr == sextet[5])
|
|
sextet[5] = sextet[4];
|
|
sextet[4] = f;
|
|
}
|
|
else if (nullptr != sextet[5] && f_weight > static_cast<unsigned char>(sextet[5]->FontWeight()))
|
|
sextet[5] = f;
|
|
}
|
|
else if (f->IsLight())
|
|
{
|
|
if (nullptr == sextet[1])
|
|
sextet[1] = f;
|
|
else if (f_weight > static_cast<unsigned char>(sextet[1]->FontWeight()))
|
|
{
|
|
if ( nullptr == sextet[0])
|
|
sextet[0] = sextet[1];
|
|
sextet[1] = f;
|
|
}
|
|
else if (nullptr != sextet[0] && f_weight < static_cast<unsigned char>(sextet[0]->FontWeight()))
|
|
sextet[0] = f;
|
|
}
|
|
}
|
|
|
|
for (int sex_dex = 0; sex_dex < 2; sex_dex++)
|
|
{
|
|
sextet = (1==sex_dex) ? italic6 : upright6;
|
|
|
|
if (nullptr == sextet[2])
|
|
{
|
|
sextet[2] = sextet[3];
|
|
sextet[3] = nullptr;
|
|
}
|
|
else if (nullptr == sextet[4])
|
|
{
|
|
sextet[4] = sextet[3];
|
|
sextet[3] = nullptr;
|
|
}
|
|
|
|
if (nullptr != sextet[2])
|
|
{
|
|
if (nullptr != sextet[5])
|
|
sextet[4] = sextet[5];
|
|
continue;
|
|
}
|
|
|
|
if (nullptr != sextet[1] && nullptr != sextet[4])
|
|
{
|
|
sextet[2] = sextet[1];
|
|
continue;
|
|
}
|
|
|
|
if (nullptr != sextet[1])
|
|
{
|
|
if (nullptr != sextet[0])
|
|
{
|
|
sextet[2] = sextet[0];
|
|
sextet[4] = sextet[1];
|
|
}
|
|
else
|
|
{
|
|
sextet[2] = sextet[1];
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (nullptr != sextet[4])
|
|
{
|
|
sextet[2] = sextet[4];
|
|
sextet[4] = sextet[5];
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ON_FontFaceQuartet q(quartet_name,upright6[2],upright6[4],italic6[2],italic6[4]);
|
|
if (q.IsEmpty())
|
|
continue;
|
|
|
|
m_quartet_list.Append(q);
|
|
}
|
|
|
|
return m_quartet_list;
|
|
}
|
|
|
|
|
|
unsigned int ON_FontList::AddFont(
|
|
const ON_Font* font,
|
|
bool bCheckforDuplicates
|
|
)
|
|
{
|
|
if (nullptr == font)
|
|
return false;
|
|
|
|
if (
|
|
font->PostScriptName(m_name_locale).IsEmpty()
|
|
&& font->WindowsLogfontName(m_name_locale).IsEmpty()
|
|
&& font->FamilyName(m_name_locale).IsEmpty()
|
|
)
|
|
return false;
|
|
|
|
if (
|
|
ON_Font::Weight::Unset == font->FontWeight()
|
|
|| ON_Font::Stretch::Unset == font->FontStretch()
|
|
|| ON_Font::Style::Unset == font->FontStyle()
|
|
)
|
|
return false;
|
|
|
|
if (bCheckforDuplicates)
|
|
{
|
|
const ON_Font* f = FromFontProperties(font, true, true);
|
|
if (
|
|
nullptr != f
|
|
&& f->FontWeight() == font->FontWeight()
|
|
&& f->FontStretch() == font->FontStretch()
|
|
&& f->FontStyle() == font->FontStyle()
|
|
&& f->IsUnderlined() == font->IsUnderlined()
|
|
&& f->IsStrikethrough() == font->IsStrikethrough()
|
|
&& ON_wString::EqualOrdinal(f->PostScriptName(m_name_locale), font->PostScriptName(m_name_locale), true)
|
|
&& ON_wString::EqualOrdinal(f->WindowsLogfontName(m_name_locale), font->WindowsLogfontName(m_name_locale), true)
|
|
&& ON_wString::EqualOrdinal(f->FamilyName(m_name_locale), font->FamilyName(m_name_locale), true)
|
|
&& ON_wString::EqualOrdinal(f->FaceName(m_name_locale), font->FaceName(m_name_locale), true)
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_by_index.Append(font);
|
|
|
|
int count = m_by_index.Count();
|
|
if (count > 1)
|
|
{
|
|
for (int i = count - 2; i <= 0; i--)
|
|
{
|
|
// font = m_by_index[i+1]
|
|
const ON_Font* a = m_by_index[i];
|
|
if (a->RuntimeSerialNumber() <= font->RuntimeSerialNumber())
|
|
break;
|
|
|
|
// almost never happens so bubble insertion is ok
|
|
m_by_index[i] = font;
|
|
m_by_index[i + 1] = a;
|
|
}
|
|
}
|
|
|
|
m_unsorted.Append(font);
|
|
m_quartet_list.SetCount(0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
unsigned int ON_FontList::AddFonts(
|
|
const ON_SimpleArray< const ON_Font* >& fonts
|
|
)
|
|
{
|
|
const size_t font_count = fonts.UnsignedCount();
|
|
const ON_Font * const * font_list = fonts.Array();
|
|
return AddFonts(font_count,font_list);
|
|
}
|
|
|
|
unsigned int ON_FontList::AddFonts(
|
|
size_t font_count,
|
|
const ON_Font * const * font_list
|
|
)
|
|
{
|
|
if (nullptr == font_list || font_count <= 0)
|
|
return 0;
|
|
|
|
unsigned int rc = 0;
|
|
for (size_t i = 0; i < font_count; i++)
|
|
{
|
|
rc += AddFont(font_list[i],false);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
const ON_Font* ON_Font::InstalledFont(
|
|
bool bAllowBestMatch
|
|
) const
|
|
{
|
|
return IsInstalledFont()
|
|
? this
|
|
: ON_ManagedFonts::InstalledFonts().FromFontProperties(this, !bAllowBestMatch, !bAllowBestMatch);
|
|
}
|
|
|
|
bool ON_Font::IsManagedFont() const
|
|
{
|
|
return ( 0 != m_runtime_serial_number );
|
|
}
|
|
|
|
bool ON_Font::IsInstalledFont() const
|
|
{
|
|
bool rc;
|
|
switch (m_font_type)
|
|
{
|
|
case ON_Font::FontType::InstalledFont:
|
|
rc = true;
|
|
break;
|
|
case ON_Font::FontType::ManagedFont:
|
|
rc = (1 == m_managed_face_is_installed);
|
|
break;
|
|
default:
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
const wchar_t* face_name
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFont(0.0, face_name);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
double point_size,
|
|
const wchar_t* face_name
|
|
)
|
|
{
|
|
const bool bBold = false;
|
|
const bool bItalic = false;
|
|
|
|
return ON_Font::GetManagedFont(
|
|
point_size,
|
|
face_name,
|
|
bBold,
|
|
bItalic
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
const wchar_t* face_name,
|
|
bool bBold
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFont(0.0, face_name, bBold);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
double point_size,
|
|
const wchar_t* face_name,
|
|
bool bBold
|
|
)
|
|
{
|
|
const bool bItalic = false;
|
|
return ON_Font::GetManagedFont( point_size, face_name, bBold, bItalic );
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
const wchar_t* face_name,
|
|
bool bBold,
|
|
bool bItalic
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFont(0.0, face_name, bBold, bItalic);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
double point_size,
|
|
const wchar_t* name,
|
|
bool bBold,
|
|
bool bItalic
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFont(
|
|
point_size,
|
|
name,
|
|
bBold ? ON_Font::Weight::Bold : ON_Font::Default.FontWeight(),
|
|
bItalic ? ON_Font::Style::Italic : ON_Font::Default.FontStyle()
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
const wchar_t* face_name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFont(0.0, face_name, font_weight, font_style);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
double point_size,
|
|
const wchar_t* name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style
|
|
)
|
|
{
|
|
// Using ON_Font::InstalledFontFromRichTextProperties() fixes lots
|
|
// of RTF parsing bugs including RH-49028 and "Arial Black" bugs.
|
|
// Depending on the context, name can be LOGFONT, PostScript, Family+Face, Family, ...
|
|
// The font_weight parameter is typically a "face" indication.
|
|
// Treating it as a literal value causes more bugs than it fixes.
|
|
const ON_Font* installed_font = ON_Font::InstalledFontFromRichTextProperties(
|
|
name,
|
|
ON_Font::IsBoldWeight(font_weight),
|
|
(ON_Font::Style::Italic == font_style)
|
|
);
|
|
if (nullptr != installed_font)
|
|
{
|
|
if (point_size > 0.0 && point_size < ON_Font::AnnotationFontApplePointSize)
|
|
{
|
|
ON_Font f(*installed_font);
|
|
f.m_point_size = point_size;
|
|
return f.ManagedFont();
|
|
}
|
|
return installed_font->ManagedFont();
|
|
}
|
|
|
|
// Less reliable approach
|
|
const unsigned int logfont_charset = static_cast<unsigned int>(ON_Font::WindowsLogfontCharSetFromFaceName(name));
|
|
|
|
return ON_Font::GetManagedFont(
|
|
point_size,
|
|
name,
|
|
font_weight,
|
|
font_style,
|
|
ON_Font::Default.m_font_stretch,
|
|
ON_Font::Default.m_font_bUnderlined,
|
|
ON_Font::Default.m_font_bStrikethrough,
|
|
ON_FontMetrics::DefaultLineFeedRatio,
|
|
logfont_charset
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
const wchar_t* face_name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style,
|
|
ON_Font::Stretch font_stretch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough,
|
|
double linefeed_ratio,
|
|
unsigned int logfont_charset
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFont(
|
|
0.0, // point_size
|
|
face_name,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
linefeed_ratio,
|
|
logfont_charset
|
|
);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFont(
|
|
double point_size,
|
|
const wchar_t* face_name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style,
|
|
ON_Font::Stretch font_stretch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough,
|
|
double linefeed_ratio,
|
|
unsigned int logfont_charset
|
|
)
|
|
{
|
|
ON_Font font_characteristics;
|
|
if ( false == font_characteristics.SetFontCharacteristics(
|
|
point_size,
|
|
face_name,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
linefeed_ratio,
|
|
logfont_charset
|
|
))
|
|
return nullptr;
|
|
return font_characteristics.ManagedFont();
|
|
}
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
|
|
const ON_Font* ON_Font::GetManagedFontFromWindowsLogfont(
|
|
int map_mode,
|
|
HDC hdc,
|
|
const LOGFONT& logfont
|
|
)
|
|
{
|
|
ON_Font font_characteristics;
|
|
if (false == font_characteristics.SetFromWindowsLogFont(map_mode, hdc,logfont))
|
|
return nullptr;
|
|
return font_characteristics.ManagedFont();
|
|
}
|
|
|
|
#endif
|
|
|
|
const ON_Font* ON_Font::GetManagedFontFromFontDescription(
|
|
const wchar_t* font_description
|
|
)
|
|
{
|
|
ON_Font font_characteristics;
|
|
if ( false == font_characteristics.SetFromFontDescription(font_description) )
|
|
return nullptr;
|
|
return font_characteristics.ManagedFont();
|
|
}
|
|
|
|
bool ON_Font::IsNotAppleFontName(
|
|
const wchar_t* font_description
|
|
)
|
|
{
|
|
if (nullptr == font_description || 0 == font_description[0])
|
|
return true;
|
|
if ( ON_wString::EqualOrdinal(L"Default",font_description,true) )
|
|
return true;
|
|
// In RH-35535 Marlin reports that Arial is shipped with OS X.
|
|
//if ( ON_wString::EqualOrdinal(L"Arial",font_description,true) )
|
|
// return true;
|
|
return false;
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFontFromAppleFontName(
|
|
const char* postscript_name
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFontFromPostScriptName(postscript_name);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFontFromAppleFontName(
|
|
const wchar_t* postscript_name
|
|
)
|
|
{
|
|
return ON_Font::GetManagedFontFromPostScriptName(postscript_name);
|
|
}
|
|
|
|
|
|
const ON_Font* ON_Font::GetManagedFontFromPostScriptName(
|
|
const char* postscript_name
|
|
)
|
|
{
|
|
const ON_wString wide_string_postscript_name(postscript_name);
|
|
return ON_Font::GetManagedFontFromPostScriptName(wide_string_postscript_name);
|
|
}
|
|
|
|
const ON_Font* ON_Font::GetManagedFontFromPostScriptName(
|
|
const wchar_t* postscript_name
|
|
)
|
|
{
|
|
ON_wString buffer(postscript_name);
|
|
buffer.TrimLeftAndRight();
|
|
postscript_name = buffer;
|
|
if (nullptr == postscript_name || 0 == postscript_name[0])
|
|
return &ON_Font::Default;
|
|
|
|
const ON_Font* managed_font = ON_Font::ManagedFontList().FromPostScriptName(postscript_name);
|
|
if (nullptr != managed_font)
|
|
return managed_font;
|
|
|
|
const ON_Font* installed_font = ON_Font::InstalledFontList().FromPostScriptName(postscript_name);
|
|
if (nullptr != installed_font)
|
|
return installed_font->ManagedFont();
|
|
|
|
// This font is not installed.
|
|
ON_Font font(ON_Font::Unset);
|
|
|
|
// prefered weight/stretch/style since this font is not installed
|
|
font.SetFontWeight(ON_Font::Weight::Normal);
|
|
font.SetFontStretch(ON_Font::Stretch::Medium);
|
|
font.SetFontStyle(ON_Font::Style::Upright);
|
|
font.m_loc_postscript_name = postscript_name;
|
|
font.m_en_postscript_name = font.m_loc_postscript_name;
|
|
|
|
return font.ManagedFont();
|
|
}
|
|
|
|
int ON_Font::WindowsLogfontWeightFromWeight(
|
|
ON_Font::Weight font_weight
|
|
)
|
|
{
|
|
int logfont_weight = (int)(100U*static_cast<unsigned int>(font_weight));
|
|
if ( logfont_weight < 50 )
|
|
logfont_weight = 400;
|
|
if ( logfont_weight < 150 )
|
|
logfont_weight = 100;
|
|
else if ( logfont_weight >= 850 )
|
|
logfont_weight = 900;
|
|
else if (0 != logfont_weight % 100)
|
|
{
|
|
int delta = logfont_weight %100;
|
|
if (delta < 50)
|
|
logfont_weight -= delta;
|
|
else
|
|
logfont_weight += (100-delta);
|
|
}
|
|
return logfont_weight;
|
|
}
|
|
|
|
int ON_Font::AppleWeightOfFontFromWeight(
|
|
ON_Font::Weight font_weight
|
|
)
|
|
{
|
|
return ON_Font::WindowsLogfontWeightFromWeight(font_weight)/100;
|
|
}
|
|
|
|
double ON_Font::AppleFontWeightTraitFromWeight(
|
|
ON_Font::Weight font_weight
|
|
)
|
|
{
|
|
// These values are selected to optimize conversion of font weights between Windows and Apple platforms.
|
|
// https://mcneel.myjetbrains.com/youtrack/issue/RH-37075
|
|
|
|
|
|
const double default_apple_font_weight_trait = 0.0;
|
|
|
|
double w = ((double)((int)static_cast<unsigned char>(font_weight)) - 400.0) / 750.0;
|
|
if (w < -1.0)
|
|
w = -1.0;
|
|
else if (w > 1.0)
|
|
w = 1.0;
|
|
if (!(-1.0 <= w && w < 1.0))
|
|
w = default_apple_font_weight_trait;
|
|
|
|
double apple_font_weight_trait;
|
|
switch (font_weight)
|
|
{
|
|
case ON_Font::Weight::Unset:
|
|
apple_font_weight_trait = default_apple_font_weight_trait;
|
|
break;
|
|
case ON_Font::Weight::Thin:
|
|
apple_font_weight_trait = -0.4;
|
|
break;
|
|
case ON_Font::Weight::Ultralight:
|
|
apple_font_weight_trait = w;
|
|
break;
|
|
case ON_Font::Weight::Light:
|
|
apple_font_weight_trait = w;
|
|
break;
|
|
case ON_Font::Weight::Normal:
|
|
apple_font_weight_trait = 0.0;
|
|
break;
|
|
case ON_Font::Weight::Medium:
|
|
apple_font_weight_trait = w;
|
|
break;
|
|
case ON_Font::Weight::Semibold:
|
|
apple_font_weight_trait = w;
|
|
break;
|
|
case ON_Font::Weight::Bold:
|
|
apple_font_weight_trait = 0.4;
|
|
break;
|
|
case ON_Font::Weight::Ultrabold:
|
|
apple_font_weight_trait = w;
|
|
break;
|
|
case ON_Font::Weight::Heavy:
|
|
apple_font_weight_trait = w;
|
|
break;
|
|
default:
|
|
apple_font_weight_trait = default_apple_font_weight_trait;
|
|
break;
|
|
}
|
|
|
|
// The valid value range is from -1.0 to 1.0. The value of 0.0 corresponds to the regular or medium font weight.
|
|
return
|
|
(-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0)
|
|
? apple_font_weight_trait
|
|
: default_apple_font_weight_trait;
|
|
}
|
|
|
|
ON_Font::Weight ON_Font::WeightFromWindowsLogfontWeight(
|
|
int windows_logfont_weight
|
|
)
|
|
{
|
|
if ( windows_logfont_weight <= 0 || windows_logfont_weight > 1000 )
|
|
return ON_Font::Weight::Normal;
|
|
|
|
if ( windows_logfont_weight < 150 )
|
|
return ON_Font::Weight::Thin;
|
|
|
|
if ( windows_logfont_weight >= 850 )
|
|
return ON_Font::Weight::Heavy;
|
|
|
|
const ON_Font::Weight weights[] =
|
|
{
|
|
ON_Font::Weight::Thin, // = 1
|
|
ON_Font::Weight::Ultralight, // = 2
|
|
ON_Font::Weight::Light, // = 3
|
|
ON_Font::Weight::Normal, // = 4
|
|
ON_Font::Weight::Medium, // = 5
|
|
ON_Font::Weight::Semibold, // = 6
|
|
ON_Font::Weight::Bold, // = 7
|
|
ON_Font::Weight::Ultrabold, // = 8
|
|
ON_Font::Weight::Heavy, // = 9
|
|
};
|
|
|
|
const size_t weight_count = sizeof(weights) / sizeof(weights[0]);
|
|
ON_Font::Weight font_weight = ON_Font::Weight::Normal;
|
|
int delta = std::abs(static_cast<int>(ON_Font::WindowsLogfontWeightFromWeight(font_weight)) - windows_logfont_weight);
|
|
|
|
for (size_t i = 0; 0 != delta && i < weight_count; i++)
|
|
{
|
|
// look for a closer match
|
|
int d = std::abs(static_cast<int>(ON_Font::WindowsLogfontWeightFromWeight(weights[i])) - windows_logfont_weight);
|
|
if (d < delta)
|
|
{
|
|
font_weight = weights[i];
|
|
delta = d;
|
|
}
|
|
}
|
|
|
|
return font_weight;
|
|
}
|
|
|
|
ON_Font::Weight ON_Font::WeightFromAppleWeightOfFont(
|
|
int apple_weight_of_font
|
|
)
|
|
{
|
|
return ON_Font::WeightFromWindowsLogfontWeight(apple_weight_of_font*100);
|
|
}
|
|
|
|
ON_Font::Weight ON_Font::WeightFromAppleFontWeightTrait(
|
|
double apple_font_weight_trait
|
|
)
|
|
{
|
|
if (false == ON_IsValid(apple_font_weight_trait))
|
|
return ON_Font::Weight::Unset;
|
|
|
|
const double x = (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) ? apple_font_weight_trait : 0.0;
|
|
int windows_logfont_weight = (int)(400.0 + 750.0*x);
|
|
if (windows_logfont_weight < 1)
|
|
windows_logfont_weight = 1;
|
|
else if (windows_logfont_weight > 1000)
|
|
windows_logfont_weight = 1000;
|
|
return ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight);
|
|
}
|
|
|
|
void ON_Font::Internal_CopyFrom(
|
|
const ON_Font& src
|
|
)
|
|
{
|
|
if ( 0 == ((ON__UINT_PTR)(&src)) )
|
|
{
|
|
ON_ERROR("nullptr is target of ON_Font copy ctor or operator=. Crash is imminent or already happened.");
|
|
return;
|
|
}
|
|
|
|
const bool bThisIsManagedFont = (ON_Font::FontType::ManagedFont == m_font_type);
|
|
|
|
if ( bThisIsManagedFont )
|
|
{
|
|
if (0 == m_runtime_serial_number)
|
|
{
|
|
ON_ERROR("Invalid parameters: true == bThisIsManagedFont and 0 == m_runtime_serial_number.");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (0 != m_runtime_serial_number && ON_Font::FontType::ManagedFont != m_font_type)
|
|
{
|
|
ON_ERROR("Invalid parameters: false == bThisIsManagedFont and 0 != m_runtime_serial_number.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( ((ON__UINT_PTR)(&src)) <= ON_PTR_SEMAPHORE_MAX )
|
|
{
|
|
// If 8 == ((ON__UINT_PTR)(&src)), Initializing ON_Font::Unset (and m_runtime_serial_number is 0 )
|
|
// If 16 == ((ON__UINT_PTR)(&src)), Initializing ON_Font::Default (and m_runtime_serial_number is already set to 1)
|
|
// (Multiples of 8 are used so that runtime pointer alignment checkers don't get alarmed.)
|
|
const bool bDefaultFont = (m_runtime_serial_number > 0);
|
|
|
|
m_font_weight = bDefaultFont ? ON_Font::Weight::Normal : ON_Font::Weight::Unset;
|
|
m_font_stretch = bDefaultFont ? ON_Font::Stretch::Medium : ON_Font::Stretch::Unset;
|
|
m_font_style = bDefaultFont ? ON_Font::Style::Upright : ON_Font::Style::Unset;
|
|
|
|
m_loc_family_name = (bDefaultFont ? ON_Font::DefaultFamilyName() : L"");
|
|
m_en_family_name = (bDefaultFont ? ON_Font::DefaultFamilyName() : L"");
|
|
|
|
m_loc_face_name = (bDefaultFont ? ON_Font::DefaultFaceName() : L"");
|
|
m_en_face_name = (bDefaultFont ? ON_Font::DefaultFaceName() : L"");
|
|
|
|
m_loc_windows_logfont_name = (bDefaultFont ? ON_Font::DefaultWindowsLogfontName() : L"");
|
|
m_en_windows_logfont_name = (bDefaultFont ? ON_Font::DefaultWindowsLogfontName() : L"");
|
|
|
|
m_loc_postscript_name = (bDefaultFont ? ON_Font::DefaultPostScriptName() : L"");
|
|
m_en_postscript_name = (bDefaultFont ? ON_Font::DefaultPostScriptName() : L"");
|
|
|
|
m_font_bUnderlined = false;
|
|
m_font_bStrikethrough = false;
|
|
|
|
m_apple_font_weight_trait = 0.0;
|
|
|
|
m_windows_logfont_weight = 400;
|
|
m_logfont_charset = ON_Font::WindowsConstants::logfont_default_charset;
|
|
|
|
m_point_size = 0.0;
|
|
|
|
m_font_origin
|
|
= bDefaultFont
|
|
?
|
|
#if defined(ON_RUNTIME_WIN)
|
|
ON_Font::Origin::WindowsFont
|
|
#elif defined(ON_RUNTIME_APPLE)
|
|
ON_Font::Origin::AppleFont
|
|
#else
|
|
ON_Font::Origin::Unknown
|
|
#endif
|
|
: ON_Font::Origin::Unset;
|
|
|
|
const ON_Font* installed_font
|
|
= bDefaultFont
|
|
? ON_Font::InstalledFontList().FromFontProperties(this, true, true)
|
|
: nullptr;
|
|
|
|
if (
|
|
nullptr != installed_font
|
|
&& ON_Font::EqualFontFamily(this, installed_font)
|
|
&& m_font_style == installed_font->FontStyle()
|
|
)
|
|
{
|
|
// Set stretch from installed font.
|
|
m_font_stretch = installed_font->FontStretch();
|
|
|
|
// want an exact name match
|
|
if ( installed_font->m_loc_postscript_name.IsNotEmpty() )
|
|
m_loc_postscript_name = installed_font->m_loc_postscript_name;
|
|
if ( installed_font->m_en_postscript_name.IsNotEmpty() )
|
|
m_en_postscript_name = installed_font->m_en_postscript_name;
|
|
|
|
if ( installed_font->m_loc_family_name.IsNotEmpty() )
|
|
m_loc_family_name = installed_font->m_loc_family_name;
|
|
if ( installed_font->m_en_family_name.IsNotEmpty() )
|
|
m_en_family_name = installed_font->m_en_family_name;
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
if ( installed_font->m_loc_face_name.IsNotEmpty() )
|
|
m_loc_face_name = installed_font->m_loc_face_name;
|
|
if ( installed_font->m_en_face_name.IsNotEmpty() )
|
|
m_en_face_name = installed_font->m_en_face_name;
|
|
|
|
if ( installed_font->m_loc_windows_logfont_name.IsNotEmpty() )
|
|
m_loc_windows_logfont_name = installed_font->m_loc_windows_logfont_name;
|
|
if ( installed_font->m_en_windows_logfont_name.IsNotEmpty() )
|
|
m_en_windows_logfont_name = installed_font->m_en_windows_logfont_name;
|
|
|
|
m_logfont_charset = installed_font->m_logfont_charset;
|
|
#endif
|
|
|
|
// exact platform weight match
|
|
m_windows_logfont_weight = installed_font->m_windows_logfont_weight;
|
|
m_apple_font_weight_trait = installed_font->m_apple_font_weight_trait;
|
|
|
|
m_panose1 = installed_font->m_panose1;
|
|
|
|
// share glyph cache
|
|
m_font_glyph_cache = installed_font->m_font_glyph_cache;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_font_weight = src.m_font_weight;
|
|
m_windows_logfont_weight = src.m_windows_logfont_weight;
|
|
m_apple_font_weight_trait = src.m_apple_font_weight_trait;
|
|
|
|
m_font_style = src.m_font_style;
|
|
m_font_stretch = src.m_font_stretch;
|
|
m_font_bUnderlined = src.m_font_bUnderlined;
|
|
m_font_bStrikethrough = src.m_font_bStrikethrough;
|
|
m_logfont_charset = src.m_logfont_charset;
|
|
|
|
m_locale_name = src.m_locale_name;
|
|
|
|
m_loc_postscript_name = src.m_loc_postscript_name;
|
|
m_en_postscript_name = src.m_en_postscript_name;
|
|
|
|
m_loc_family_name = src.m_loc_family_name;
|
|
m_en_family_name = src.m_en_family_name;
|
|
|
|
m_loc_face_name = src.m_loc_face_name;
|
|
m_en_face_name = src.m_en_face_name;
|
|
|
|
m_loc_windows_logfont_name = src.m_loc_windows_logfont_name;
|
|
m_en_windows_logfont_name = src.m_en_windows_logfont_name;
|
|
|
|
bool bCopyCache = (0 == m_runtime_serial_number && ON_Font::FontType::Unset == m_font_type);
|
|
if (
|
|
false == bCopyCache
|
|
&& ON_Font::FontType::ManagedFont == m_font_type
|
|
&& ON_Font::FontType::InstalledFont == src.m_font_type
|
|
&& nullptr != src.m_font_glyph_cache.get()
|
|
&& nullptr == m_font_glyph_cache.get()
|
|
)
|
|
{
|
|
// destination font is managed and src font is installed
|
|
bCopyCache = true;
|
|
}
|
|
if (bCopyCache)
|
|
m_font_glyph_cache = src.m_font_glyph_cache;
|
|
|
|
m_point_size = src.m_point_size;
|
|
|
|
m_font_origin = src.m_font_origin;
|
|
|
|
m_panose1 = src.m_panose1;
|
|
|
|
m_simulated = src.m_simulated;
|
|
}
|
|
|
|
m_font_characteristics_hash = ON_SHA1_Hash::ZeroDigest;
|
|
|
|
}
|
|
|
|
ON_Font::ON_Font()
|
|
{
|
|
/*
|
|
//sz = 192
|
|
//offsets[0] 0 const unsigned __int64
|
|
//this
|
|
//offsets[1] 0 const unsigned __int64
|
|
//m_runtime_serial_number
|
|
//offsets[2] 4 const unsigned __int64
|
|
//m_windows_logfont_weight
|
|
//offsets[3] 8 const unsigned __int64
|
|
//m_point_size
|
|
//offsets[4] 16 const unsigned __int64
|
|
//m_apple_font_weight_trait
|
|
//offsets[5] 24 const unsigned __int64
|
|
//m_font_weight
|
|
//offsets[6] 25 const unsigned __int64
|
|
//offsets[7] 26 const unsigned __int64
|
|
//offsets[8] 27 const unsigned __int64
|
|
//offsets[9] 28 const unsigned __int64
|
|
//offsets[10] 29 const unsigned __int64
|
|
//offsets[11] 30 const unsigned __int64
|
|
//offsets[12] 31 const unsigned __int64
|
|
//m_font_type
|
|
//offsets[13] 32 const unsigned __int64
|
|
//m_locale_name
|
|
//offsets[14] 40 const unsigned __int64
|
|
//offsets[15] 48 const unsigned __int64
|
|
//offsets[16] 56 const unsigned __int64
|
|
//offsets[17] 64 const unsigned __int64
|
|
//offsets[18] 72 const unsigned __int64
|
|
//m_loc_face_name
|
|
//offsets[19] 80 const unsigned __int64
|
|
//offsets[20] 88 const unsigned __int64
|
|
//offsets[21] 96 const unsigned __int64
|
|
//m_en_windows_logfont_name
|
|
//offsets[22] 104 const unsigned __int64
|
|
//m_simulated
|
|
//offsets[23] 105 const unsigned __int64
|
|
//m_reserved1
|
|
//offsets[24] 106 const unsigned __int64
|
|
//m_panose1
|
|
//offsets[25] 116 const unsigned __int64
|
|
//m_font_characteristics_hash
|
|
//offsets[26] 136 const unsigned __int64
|
|
//m_reserved2
|
|
//offsets[27] 144 const unsigned __int64
|
|
//m_reserved3
|
|
//offsets[28] 152 const unsigned __int64
|
|
//m_reserved4
|
|
//offsets[29] 160 const unsigned __int64
|
|
//m_font_glyph_cache
|
|
//offsets[30] 176 const unsigned __int64
|
|
//m_free_type_face
|
|
//offsets[31] 184 const unsigned __int64
|
|
//m_reserved5
|
|
//offsets[32] 192 const unsigned __int64
|
|
//(this+1)
|
|
*/
|
|
|
|
|
|
////const size_t sz = sizeof(*this);
|
|
|
|
////const char* p[33] =
|
|
////{
|
|
//// (const char*)this,
|
|
//// (const char*)&m_runtime_serial_number,
|
|
//// (const char*)&m_windows_logfont_weight,
|
|
//// (const char*)&m_point_size,
|
|
//// (const char*)&m_apple_font_weight_trait,
|
|
//// (const char*)&m_font_weight,
|
|
//// (const char*)&m_font_style,
|
|
//// (const char*)&m_font_stretch,
|
|
//// (const char*)&m_font_bUnderlined,
|
|
//// (const char*)&m_font_bStrikethrough,
|
|
//// (const char*)&m_logfont_charset,
|
|
//// (const char*)&m_font_origin,
|
|
//// (const char*)&m_font_type,
|
|
//// (const char*)&m_locale_name,
|
|
//// (const char*)&m_loc_postscript_name,
|
|
//// (const char*)&m_en_postscript_name,
|
|
//// (const char*)&m_loc_family_name,
|
|
//// (const char*)&m_en_family_name,
|
|
//// (const char*)&m_loc_face_name,
|
|
//// (const char*)&m_en_face_name,
|
|
//// (const char*)&m_loc_windows_logfont_name,
|
|
//// (const char*)&m_en_windows_logfont_name,
|
|
//// (const char*)&m_simulated,
|
|
//// (const char*)&m_reserved1,
|
|
//// (const char*)&m_panose1,
|
|
//// (const char*)&m_font_characteristics_hash,
|
|
//// (const char*)&m_reserved2,
|
|
//// (const char*)&m_reserved3,
|
|
//// (const char*)&m_reserved4,
|
|
//// (const char*)&m_font_glyph_cache,
|
|
//// (const char*)&m_free_type_face,
|
|
//// (const char*)&m_reserved5,
|
|
//// (const char*)(this+1),
|
|
////};
|
|
|
|
////const size_t offsets[sizeof(p)/sizeof(p[0])] = {
|
|
//// 0,
|
|
//// (size_t)(p[1] - p[0]),
|
|
//// (size_t)(p[2] - p[0]),
|
|
//// (size_t)(p[3] - p[0]),
|
|
//// (size_t)(p[4] - p[0]),
|
|
//// (size_t)(p[5] - p[0]),
|
|
//// (size_t)(p[6] - p[0]),
|
|
//// (size_t)(p[7] - p[0]),
|
|
//// (size_t)(p[8] - p[0]),
|
|
//// (size_t)(p[9] - p[0]),
|
|
|
|
//// (size_t)(p[10] - p[0]),
|
|
//// (size_t)(p[11] - p[0]),
|
|
//// (size_t)(p[12] - p[0]),
|
|
//// (size_t)(p[13] - p[0]),
|
|
//// (size_t)(p[14] - p[0]),
|
|
//// (size_t)(p[15] - p[0]),
|
|
//// (size_t)(p[16] - p[0]),
|
|
//// (size_t)(p[17] - p[0]),
|
|
//// (size_t)(p[18] - p[0]),
|
|
//// (size_t)(p[19] - p[0]),
|
|
|
|
//// (size_t)(p[20] - p[0]),
|
|
//// (size_t)(p[21] - p[0]),
|
|
//// (size_t)(p[22] - p[0]),
|
|
//// (size_t)(p[23] - p[0]),
|
|
//// (size_t)(p[24] - p[0]),
|
|
//// (size_t)(p[25] - p[0]),
|
|
//// (size_t)(p[26] - p[0]),
|
|
//// (size_t)(p[27] - p[0]),
|
|
//// (size_t)(p[28] - p[0]),
|
|
//// (size_t)(p[29] - p[0]),
|
|
|
|
//// (size_t)(p[30] - p[0]),
|
|
//// (size_t)(p[31] - p[0]),
|
|
//// (size_t)(p[32] - p[0])
|
|
////};
|
|
|
|
////int breakpointhere = 99;
|
|
}
|
|
|
|
ON_Font::ON_Font(
|
|
ON_Font::FontType font_type,
|
|
const ON_Font& src
|
|
)
|
|
: m_runtime_serial_number(
|
|
(ON_Font::FontType::ManagedFont == font_type)
|
|
? (++ON_Font::__runtime_serial_number_generator) // only managed fonts have non-zero m_runtime_serial_number
|
|
: 0
|
|
)
|
|
, m_font_type(font_type)
|
|
{
|
|
// This is a private constructor used to create managed fonts.
|
|
Internal_CopyFrom(src);
|
|
}
|
|
|
|
ON_Font::ON_Font(const ON_Font& src)
|
|
: m_runtime_serial_number( (ON_PTR_SEMAPHORE2 == ((ON__UINT_PTR)&src)) ? 1 : 0)
|
|
, m_font_type( (ON_PTR_SEMAPHORE2 == ((ON__UINT_PTR)&src)) ? ON_Font::FontType::ManagedFont : ON_Font::FontType::Unset)
|
|
{
|
|
// (ON_PTR_SEMAPHORE1 == ((ON__UINT_PTR)&src)) means we are constructing ON_Font::Unset.
|
|
// (ON_PTR_SEMAPHORE2 == ((ON__UINT_PTR)&src)) means we are constructing ON_Font::Default.
|
|
// Otherwise "this" should be an ordinary and unmanaged font.
|
|
// See opennurbs_statics.cpp and ON_Font::CopyHelper() for more information.
|
|
Internal_CopyFrom(src);
|
|
}
|
|
|
|
ON_Font& ON_Font::operator=(const ON_Font& src)
|
|
{
|
|
if (this != &src)
|
|
{
|
|
if (IsManagedFont() )
|
|
{
|
|
// managed fonts can never be modified
|
|
if ( false == ON_Font::EqualFontCharacteristics(*this, src) )
|
|
{
|
|
ON_ERROR("Attempt to modify a managed font");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Internal_CopyFrom(src);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
bool ON_Font::SetFontCharacteristics(
|
|
const wchar_t* gdi_logfont_name,
|
|
bool bBold,
|
|
bool bItalic,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
)
|
|
{
|
|
return SetFontCharacteristics(
|
|
0.0,
|
|
gdi_logfont_name,
|
|
bBold,
|
|
bItalic,
|
|
bUnderlined,
|
|
bStrikethrough
|
|
);
|
|
}
|
|
|
|
bool ON_Font::SetFontCharacteristics(
|
|
double point_size,
|
|
const wchar_t * gdi_logfont_name,
|
|
bool bBold,
|
|
bool bItalic,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
)
|
|
{
|
|
if (nullptr == gdi_logfont_name || 0 == gdi_logfont_name[0])
|
|
{
|
|
gdi_logfont_name = ON_Font::Default.m_loc_windows_logfont_name;
|
|
}
|
|
return SetFontCharacteristics(
|
|
point_size,
|
|
gdi_logfont_name,
|
|
(bBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal),
|
|
(bItalic ? ON_Font::Style::Italic : ON_Font::Style::Upright),
|
|
ON_Font::Default.m_font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
ON_FontMetrics::DefaultLineFeedRatio,
|
|
ON_Font::WindowsLogfontCharSetFromFaceName(gdi_logfont_name)
|
|
);
|
|
}
|
|
|
|
bool ON_Font::IsValidFaceName(
|
|
const wchar_t* face_name
|
|
)
|
|
{
|
|
if ( nullptr == face_name || 0 == face_name[0] || ON_wString::Space == face_name[0])
|
|
return false;
|
|
|
|
int i = 0;
|
|
while (i < 32 && 0 != face_name[i])
|
|
{
|
|
if (face_name[i] < ON_wString::Space )
|
|
return false;
|
|
switch (face_name[i])
|
|
{
|
|
case ';':
|
|
case '"':
|
|
case '\'':
|
|
case '`':
|
|
case '=':
|
|
case '#':
|
|
// lots more
|
|
return false;
|
|
//case '@': - There are valid fonts like @Gulim with '@' in the name.
|
|
// case '.' - Many apple font names begin with a period
|
|
// case '-' - A hyphen is common in PostScript names
|
|
// case ' ' - A space is common in many names
|
|
default:
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if (0 != face_name[i])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ON_Font::Weight ON_Font::FontWeightFromUnsigned(
|
|
unsigned int unsigned_font_weight
|
|
)
|
|
{
|
|
switch (unsigned_font_weight)
|
|
{
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Unset);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Thin);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Ultralight);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Light);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Normal);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Medium);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Semibold);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Bold);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Ultrabold);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Weight::Heavy);
|
|
default:
|
|
break;
|
|
}
|
|
ON_ERROR("unsigned_font_weight is not valid");
|
|
return ON_Font::Weight::Unset;
|
|
}
|
|
|
|
bool ON_Font::EqualWeightStretchStyle(
|
|
const ON_Font* lhs,
|
|
const ON_Font* rhs,
|
|
bool bUnsetIsEqual
|
|
)
|
|
{
|
|
return
|
|
ON_Font::EqualWeight(lhs, rhs, bUnsetIsEqual)
|
|
&& ON_Font::EqualStretch(lhs, rhs, bUnsetIsEqual)
|
|
&& ON_Font::EqualStyle(lhs, rhs, bUnsetIsEqual);
|
|
}
|
|
|
|
bool ON_Font::EqualWeight(
|
|
const ON_Font* lhs,
|
|
const ON_Font* rhs,
|
|
bool bUnsetIsEqual
|
|
)
|
|
{
|
|
if (nullptr == lhs || nullptr == rhs)
|
|
return false;
|
|
if (lhs->m_font_weight != rhs->m_font_weight)
|
|
{
|
|
if (false == bUnsetIsEqual)
|
|
return false;
|
|
if (ON_Font::Weight::Unset != lhs->m_font_weight && ON_Font::Weight::Unset != rhs->m_font_weight)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_Font::EqualStretch(
|
|
const ON_Font* lhs,
|
|
const ON_Font* rhs,
|
|
bool bUnsetIsEqual
|
|
)
|
|
{
|
|
if (nullptr == lhs || nullptr == rhs)
|
|
return false;
|
|
if (lhs->m_font_stretch != rhs->m_font_stretch)
|
|
{
|
|
if (false == bUnsetIsEqual)
|
|
return false;
|
|
if (ON_Font::Stretch::Unset != lhs->m_font_stretch && ON_Font::Stretch::Unset != rhs->m_font_stretch)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_Font::EqualStyle(
|
|
const ON_Font* lhs,
|
|
const ON_Font* rhs,
|
|
bool bUnsetIsEqual
|
|
)
|
|
{
|
|
if (nullptr == lhs || nullptr == rhs)
|
|
return false;
|
|
if (lhs->m_font_style != rhs->m_font_style)
|
|
{
|
|
if (false == bUnsetIsEqual)
|
|
return false;
|
|
if (ON_Font::Style::Unset != lhs->m_font_style && ON_Font::Style::Unset != rhs->m_font_style)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int ON_Font::CompareWeight(
|
|
ON_Font::Weight weight_a,
|
|
ON_Font::Weight weight_b
|
|
)
|
|
{
|
|
unsigned int a = static_cast<unsigned int>(weight_a);
|
|
unsigned int b = static_cast<unsigned int>(weight_b);
|
|
if ( a < b )
|
|
return -1;
|
|
if ( a < b )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
ON_Font::Style ON_Font::FontStyleFromUnsigned(
|
|
unsigned int unsigned_font_style
|
|
)
|
|
{
|
|
switch (unsigned_font_style)
|
|
{
|
|
ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Upright);
|
|
ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Italic);
|
|
ON_ENUM_FROM_UNSIGNED_CASE( ON_Font::Style::Oblique);
|
|
default:
|
|
break;
|
|
}
|
|
ON_ERROR("unsigned_font_style is not valid");
|
|
return ON_Font::Style::Upright;
|
|
}
|
|
|
|
bool ON_Font::IsBoldWeight(
|
|
ON_Font::Weight weight
|
|
)
|
|
{
|
|
return (static_cast<unsigned int>(weight) >= static_cast<unsigned int>(ON_Font::Weight::Semibold));
|
|
}
|
|
|
|
const wchar_t* ON_Font::WeightToWideString(
|
|
ON_Font::Weight font_weight
|
|
)
|
|
{
|
|
const wchar_t* s;
|
|
switch (font_weight)
|
|
{
|
|
case ON_Font::Weight::Unset:
|
|
s = L"Unsetweight";
|
|
break;
|
|
case ON_Font::Weight::Thin:
|
|
s = L"Thin";
|
|
break;
|
|
case ON_Font::Weight::Ultralight:
|
|
s = L"Ultralight";
|
|
break;
|
|
case ON_Font::Weight::Light:
|
|
s = L"Light";
|
|
break;
|
|
case ON_Font::Weight::Normal:
|
|
s = L"Normal";
|
|
break;
|
|
case ON_Font::Weight::Medium:
|
|
s = L"Medium";
|
|
break;
|
|
case ON_Font::Weight::Semibold:
|
|
s = L"Semibold";
|
|
break;
|
|
case ON_Font::Weight::Bold:
|
|
s = L"Bold";
|
|
break;
|
|
case ON_Font::Weight::Ultrabold:
|
|
s = L"Ultrabold";
|
|
break;
|
|
case ON_Font::Weight::Heavy:
|
|
s = L"Heavy";
|
|
break;
|
|
default:
|
|
s = L"";
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
const wchar_t* ON_Font::StretchToWideString(
|
|
ON_Font::Stretch font_stretch
|
|
)
|
|
{
|
|
const wchar_t* s;
|
|
switch (font_stretch)
|
|
{
|
|
case ON_Font::Stretch::Unset:
|
|
s = L"Unsetstretch";
|
|
break;
|
|
case ON_Font::Stretch::Ultracondensed:
|
|
s = L"Ultracondensed";
|
|
break;
|
|
case ON_Font::Stretch::Extracondensed:
|
|
s = L"Extracondensed";
|
|
break;
|
|
case ON_Font::Stretch::Condensed:
|
|
s = L"Condensed";
|
|
break;
|
|
case ON_Font::Stretch::Semicondensed:
|
|
s = L"Semicondensed";
|
|
break;
|
|
case ON_Font::Stretch::Medium:
|
|
s = L"Medium";
|
|
break;
|
|
case ON_Font::Stretch::Semiexpanded:
|
|
s = L"Semiexpanded";
|
|
break;
|
|
case ON_Font::Stretch::Expanded:
|
|
s = L"Expanded";
|
|
break;
|
|
case ON_Font::Stretch::Extraexpanded:
|
|
s = L"Extraexpanded";
|
|
break;
|
|
case ON_Font::Stretch::Ultraexpanded:
|
|
s = L"Ultraexpanded";
|
|
break;
|
|
default:
|
|
s = L"";
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
const wchar_t* ON_Font::StyleToWideString(
|
|
ON_Font::Style font_style
|
|
)
|
|
{
|
|
const wchar_t* s;
|
|
switch (font_style)
|
|
{
|
|
case ON_Font::Style::Unset:
|
|
s = L"Unsetstyle";
|
|
break;
|
|
case ON_Font::Style::Upright:
|
|
s = L"Upright";
|
|
break;
|
|
case ON_Font::Style::Italic:
|
|
s = L"Italic";
|
|
break;
|
|
case ON_Font::Style::Oblique:
|
|
s = L"Oblique";
|
|
break;
|
|
default:
|
|
s = L"";
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
ON_Font::Stretch ON_Font::FontStretchFromUnsigned(
|
|
unsigned int unsigned_font_stretch
|
|
)
|
|
{
|
|
switch (unsigned_font_stretch)
|
|
{
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Unset);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Ultracondensed);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Extracondensed);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Condensed);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Semicondensed);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Medium);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Semiexpanded);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Expanded);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Extraexpanded);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Stretch::Ultraexpanded);
|
|
default:
|
|
break;
|
|
}
|
|
ON_ERROR("unsigned_font_stretch is not valid");
|
|
return ON_Font::Stretch::Unset;
|
|
}
|
|
|
|
unsigned int ON_Font::FontCharacteristicsAsUnsigned() const
|
|
{
|
|
return ON_Font::Internal_FontCharacteristicsAsUnsigned(
|
|
FontWeight(),
|
|
FontStyle(),
|
|
FontStretch(),
|
|
m_font_bUnderlined,
|
|
m_font_bStrikethrough
|
|
);
|
|
}
|
|
|
|
unsigned int ON_Font::Internal_FontCharacteristicsAsUnsigned(
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style,
|
|
ON_Font::Stretch font_stretch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
)
|
|
{
|
|
unsigned int a[][2]
|
|
= {
|
|
{ 2U, 1U }, // insures 0 is not a valid FontCharacteristicsAsUnsigned() value.
|
|
{ 10U, static_cast<unsigned int>(font_weight) },
|
|
{ 4U, static_cast<unsigned int>(font_style) },
|
|
{ 10U, static_cast<unsigned int>(font_stretch) },
|
|
// The static_cast<unsigned int> in b ? 1U : 0U is to work around a CLang compiler bug.
|
|
{ 2U, static_cast<unsigned int>(bUnderlined ? 1U : 0U) },
|
|
{ 2U, static_cast<unsigned int>(bStrikethrough ? 1U : 0U) }
|
|
// insert new information below this line.
|
|
};
|
|
|
|
const int count = (int)(sizeof(a) / sizeof(a[0]));
|
|
int i = count-1;
|
|
unsigned int u = a[i][1] % a[i][0];
|
|
|
|
for (i--; i >= 0; i--)
|
|
{
|
|
u = (u * a[i][0]) + (a[i][1] % a[i][0]);
|
|
}
|
|
|
|
return u;
|
|
}
|
|
|
|
void ON_Font::Internal_GetFontCharacteristicsFromUnsigned(
|
|
unsigned int font_characteristics_as_unsigned,
|
|
ON_Font::Weight& font_weight,
|
|
ON_Font::Stretch& font_stretch,
|
|
ON_Font::Style& font_style,
|
|
bool& bUnderlined,
|
|
bool& bStrikethrough
|
|
)
|
|
{
|
|
unsigned int u = font_characteristics_as_unsigned;
|
|
const unsigned int u_one = u % 2;
|
|
u /= 2;
|
|
const unsigned int u_font_weight = u % 10;
|
|
u /= 10;
|
|
const unsigned int u_font_style = u % 4;
|
|
u /= 4;
|
|
const unsigned int u_font_stretch = u % 10;
|
|
u /= 10;
|
|
const unsigned int u_bUnderlined = u % 2;
|
|
u /= 2;
|
|
const unsigned int u_bStrikethrough = u % 2;
|
|
u /= 2;
|
|
// extract new information below this line
|
|
|
|
font_weight = (1U == u_one && u_font_weight > 0) ? ON_Font::FontWeightFromUnsigned(u_font_weight) : ON_Font::Default.FontWeight();
|
|
font_style = (1U == u_one) ? ON_Font::FontStyleFromUnsigned(u_font_style) : ON_Font::Default.FontStyle();
|
|
font_stretch = (1U == u_one) ? ON_Font::FontStretchFromUnsigned(u_font_stretch) : ON_Font::Default.FontStretch();
|
|
bUnderlined = (1U == u_one) ? (1 == u_bUnderlined) : ON_Font::Default.IsUnderlined();
|
|
bStrikethrough = (1U == u_one) ? (1 == u_bStrikethrough) : ON_Font::Default.IsStrikethrough();
|
|
}
|
|
|
|
bool ON_Font::Internal_SetFontCharacteristicsFromUnsigned(
|
|
unsigned int font_characteristics_as_unsigned
|
|
)
|
|
{
|
|
ON_Font::Weight font_weight = ON_Font::Weight::Normal;
|
|
ON_Font::Stretch font_stretch = ON_Font::Stretch::Medium;
|
|
ON_Font::Style font_style = ON_Font::Style::Upright;
|
|
bool bUnderlined = false;
|
|
bool bStrikethrough = false;
|
|
|
|
ON_Font::Internal_GetFontCharacteristicsFromUnsigned(
|
|
font_characteristics_as_unsigned,
|
|
font_weight,
|
|
font_stretch,
|
|
font_style,
|
|
bUnderlined,
|
|
bStrikethrough
|
|
);
|
|
|
|
|
|
const ON_wString windows_logfont_name = WindowsLogfontName();
|
|
|
|
bool rc = SetFontCharacteristics(
|
|
windows_logfont_name,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough
|
|
);
|
|
|
|
return rc;
|
|
}
|
|
|
|
unsigned int ON_Font::CRC32(
|
|
bool bIgnoreNameOrdinalCase
|
|
) const
|
|
{
|
|
unsigned int u = FontCharacteristicsAsUnsigned();
|
|
|
|
const ON_wString windows_logfont_name = WindowsLogfontName();
|
|
const ON_wString windows_logfont_name_crc
|
|
= bIgnoreNameOrdinalCase
|
|
? windows_logfont_name.MapStringOrdinal(ON_StringMapOrdinalType::MinimumOrdinal)
|
|
: windows_logfont_name;
|
|
|
|
// SHould probably include PostScript name as well.
|
|
|
|
ON__UINT32 hash = ON_CRC32(0, sizeof(u), &u );
|
|
hash = ON_CRC32(hash,windows_logfont_name_crc.Length()*sizeof(wchar_t),static_cast<const wchar_t*>(windows_logfont_name_crc));
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
if ( m_point_size > 0.0 )
|
|
hash = ON_CRC32(hash, sizeof(m_point_size), &m_point_size);
|
|
#endif
|
|
|
|
return hash;
|
|
}
|
|
|
|
bool ON_Font::SetFontCharacteristics(
|
|
const wchar_t* gdi_logfont_name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style,
|
|
ON_Font::Stretch font_stretch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
)
|
|
{
|
|
return SetFontCharacteristics(
|
|
0.0,
|
|
gdi_logfont_name,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough
|
|
);
|
|
}
|
|
|
|
bool ON_Font::SetFontCharacteristics(
|
|
double point_size,
|
|
const wchar_t* gdi_logfont_name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style,
|
|
ON_Font::Stretch font_stretch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough
|
|
)
|
|
{
|
|
const unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(gdi_logfont_name);
|
|
|
|
double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio;
|
|
|
|
return SetFontCharacteristics(
|
|
point_size,
|
|
gdi_logfont_name,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
linefeed_ratio,
|
|
logfont_charset
|
|
);
|
|
}
|
|
|
|
bool ON_Font::SetFontCharacteristics(
|
|
const wchar_t* gdi_logfont_name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style,
|
|
ON_Font::Stretch font_stretch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough,
|
|
double linefeed_ratio,
|
|
unsigned int logfont_charset
|
|
)
|
|
{
|
|
return SetFontCharacteristics(
|
|
0.0,
|
|
gdi_logfont_name,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
linefeed_ratio,
|
|
logfont_charset
|
|
);
|
|
}
|
|
|
|
bool ON_Font::SetFontCharacteristics(
|
|
double point_size,
|
|
const wchar_t* gdi_logfont_name,
|
|
ON_Font::Weight font_weight,
|
|
ON_Font::Style font_style,
|
|
ON_Font::Stretch font_stretch,
|
|
bool bUnderlined,
|
|
bool bStrikethrough,
|
|
double linefeed_ratio,
|
|
unsigned int logfont_charset
|
|
)
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
|
|
// Save face_name - it often points to this->m_face_name
|
|
// and *this = ON_Font::Unset will set this->m_face_name to the empty string.
|
|
ON_wString local_face_name(gdi_logfont_name);
|
|
local_face_name.TrimLeftAndRight();
|
|
gdi_logfont_name = static_cast<const wchar_t*>(local_face_name);
|
|
|
|
*this = ON_Font::Unset;
|
|
|
|
if (false == ON_Font::IsValidFaceName(gdi_logfont_name))
|
|
return false;
|
|
|
|
if (logfont_charset >= 256)
|
|
return false;
|
|
|
|
ON_Font new_characteristics(ON_Font::Unset);
|
|
|
|
new_characteristics.m_loc_windows_logfont_name = gdi_logfont_name;
|
|
new_characteristics.m_en_windows_logfont_name = new_characteristics.m_loc_windows_logfont_name;
|
|
|
|
new_characteristics.m_font_weight = ON_Font::FontWeightFromUnsigned(static_cast<unsigned char>(font_weight));
|
|
new_characteristics.m_point_size
|
|
= (point_size > 0.0 && point_size < 2147483640.0)
|
|
? point_size
|
|
: 0.0;
|
|
new_characteristics.m_windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(new_characteristics.m_font_weight);
|
|
new_characteristics.m_apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(new_characteristics.m_font_weight);
|
|
new_characteristics.m_font_style = font_style;
|
|
new_characteristics.m_font_stretch = font_stretch;
|
|
new_characteristics.m_font_bUnderlined = bUnderlined ? true : false;
|
|
new_characteristics.m_font_bStrikethrough = bStrikethrough;
|
|
|
|
new_characteristics.m_loc_family_name = ON_Font::FamilyNameFromDirtyName(gdi_logfont_name);
|
|
new_characteristics.m_en_family_name = new_characteristics.m_loc_family_name;
|
|
|
|
if (
|
|
ON_wString::EqualOrdinal(L"CityBlueprint", -1, gdi_logfont_name, -1, true)
|
|
|| ON_wString::EqualOrdinal(L"CountryBlueprint", -1, gdi_logfont_name, -1, true)
|
|
)
|
|
{
|
|
// These two fonts were didstrubuted with ACAD for decades.
|
|
// Thy have several errors in their defintion including being
|
|
// marked as SYMBOL_CHARSET rather than ANSI_CHARSET.
|
|
logfont_charset = ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
}
|
|
else if (ON_Font::logfont_symbol_charset == logfont_charset)
|
|
{
|
|
// verify this is correct.
|
|
logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(new_characteristics.WindowsLogfontName());
|
|
}
|
|
else
|
|
{
|
|
logfont_charset = ON_Font::WindowsConstants::logfont_default_charset;
|
|
}
|
|
|
|
new_characteristics.m_logfont_charset
|
|
= (ON_Font::WindowsConstants::logfont_symbol_charset == (unsigned char)logfont_charset)
|
|
? ON_Font::WindowsConstants::logfont_symbol_charset
|
|
: ON_Font::WindowsConstants::logfont_default_charset;
|
|
|
|
if (
|
|
// 3 fast checks to avoid time consuming hash calculation
|
|
false == ON_wString::EqualOrdinal(WindowsLogfontName(),new_characteristics.WindowsLogfontName(),false)
|
|
|| false == ON_wString::EqualOrdinal(PostScriptName(),new_characteristics.PostScriptName(),false)
|
|
|| false == ON_wString::EqualOrdinal(FamilyName(),new_characteristics.FamilyName(),false)
|
|
|| false == ON_wString::EqualOrdinal(FaceName(),new_characteristics.FaceName(),false)
|
|
|| m_font_weight != new_characteristics.m_font_weight
|
|
|| m_font_stretch != new_characteristics.m_font_stretch
|
|
|| m_font_style != new_characteristics.m_font_style
|
|
|| FontCharacteristicsHash() != new_characteristics.FontCharacteristicsHash()
|
|
)
|
|
{
|
|
Internal_CopyFrom(new_characteristics);
|
|
if (0 == m_runtime_serial_number)
|
|
{
|
|
// destination font is not managed
|
|
m_font_glyph_cache = nullptr;
|
|
}
|
|
|
|
if (
|
|
ON_Font::Stretch::Medium == m_font_stretch
|
|
&& (ON_Font::Weight::Normal == m_font_weight || ON_Font::Weight::Bold == m_font_weight)
|
|
&& (ON_Font::Style::Upright == m_font_style || ON_Font::Style::Italic == m_font_style)
|
|
)
|
|
{
|
|
const ON_wString logfontName = WindowsLogfontName();
|
|
ON_wString postscript_suffix;
|
|
bool bSetMakePostScriptName = false;
|
|
if (
|
|
ON_wString::EqualOrdinal(L"Arial", logfontName, true)
|
|
)
|
|
{
|
|
// Because Arial is common and its PostScript name has "MT" appended,
|
|
// the post script name is set here so the font will readily work
|
|
// in situation that key off the PostScript name.
|
|
bSetMakePostScriptName = true;
|
|
postscript_suffix = L"MT";
|
|
}
|
|
else if (ON_wString::EqualOrdinal(L"Segoe Print", logfontName, true))
|
|
{
|
|
bSetMakePostScriptName = true;
|
|
}
|
|
else if (ON_wString::EqualOrdinal(L"Segoe Script", logfontName, true))
|
|
{
|
|
bSetMakePostScriptName = true;
|
|
}
|
|
else if (ON_wString::EqualOrdinal(L"Segoe UI", logfontName, true))
|
|
{
|
|
bSetMakePostScriptName = true;
|
|
}
|
|
else if (ON_wString::EqualOrdinal(L"Microsoft YaHei", logfontName, true))
|
|
{
|
|
bSetMakePostScriptName = true;
|
|
}
|
|
else if (ON_wString::EqualOrdinal(L"Microsoft YaHei UI", logfontName, true))
|
|
{
|
|
bSetMakePostScriptName = true;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (false == bSetMakePostScriptName)
|
|
break;
|
|
|
|
// The fonts listed above all remove spaces from m_face_name to get
|
|
// the beginning of the postscript name.
|
|
ON_wString postscript_name = logfontName;
|
|
postscript_name.TrimLeftAndRight();
|
|
postscript_name.Remove(ON_wString::Space);
|
|
if (postscript_name.IsEmpty())
|
|
break;
|
|
|
|
// add -<Weight><Style>
|
|
ON_wString postscript_face;
|
|
if (ON_Font::Weight::Bold == m_font_weight)
|
|
postscript_face += L"Bold";
|
|
if (ON_Font::Style::Italic == m_font_style)
|
|
postscript_face += L"Italic";
|
|
if (postscript_face.IsNotEmpty())
|
|
{
|
|
postscript_name += ON_wString::HyphenMinus;
|
|
postscript_name += postscript_face;
|
|
}
|
|
|
|
// Add suffix
|
|
postscript_name += postscript_suffix;
|
|
|
|
m_loc_postscript_name = postscript_name;
|
|
m_en_postscript_name = postscript_name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Internal_AfterModification();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool ON_Font::SetFromAppleFontName(
|
|
const wchar_t* apple_font_name
|
|
)
|
|
{
|
|
return SetFromAppleFontName(apple_font_name, 0.0);
|
|
}
|
|
|
|
bool ON_Font::SetFromAppleFontName(
|
|
const wchar_t* apple_font_name,
|
|
double point_size
|
|
)
|
|
{
|
|
ON_wString local_apple_font_name(apple_font_name);
|
|
local_apple_font_name.TrimLeftAndRight();
|
|
if (local_apple_font_name.IsEmpty())
|
|
return false;
|
|
|
|
apple_font_name = static_cast<const wchar_t*>(local_apple_font_name);
|
|
|
|
const bool bAnnotationFont
|
|
= ON_Font::IsValidPointSize(point_size) && point_size < ON_Font::AnnotationFontCellHeight
|
|
? false
|
|
: true;
|
|
|
|
bool rc = false;
|
|
#if defined (ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
|
|
// If the current computer has the same Apple font, this will return the highest fidelity match.
|
|
CTFontRef appleFont = ON_Font::AppleCTFont(apple_font_name,point_size);
|
|
if (nullptr != appleFont)
|
|
{
|
|
// In some cases, the NSfont.fontName is different from apple_font_name
|
|
// For example, when apple_font_name = "Arial", NSfont.fontName = "ArialMT".
|
|
const bool b = SetFromAppleCTFont(appleFont, bAnnotationFont);
|
|
CFRelease(appleFont);
|
|
if(b)
|
|
return true;
|
|
}
|
|
#endif
|
|
rc = SetFromFontDescription(apple_font_name, apple_font_name);
|
|
|
|
if (false == rc)
|
|
{
|
|
// Assume this name is valid but the particular font is not
|
|
// installed on this computer and unconditionally set m_postscript_font_name
|
|
m_loc_postscript_name = local_apple_font_name;
|
|
m_loc_family_name = ON_Font::FamilyNameFromDirtyName(m_loc_postscript_name);
|
|
|
|
m_en_postscript_name = m_loc_postscript_name;
|
|
m_en_family_name = m_loc_family_name;
|
|
}
|
|
|
|
m_point_size
|
|
= bAnnotationFont
|
|
? 0.0 // indicates annotation font point size on all platforms
|
|
: point_size;
|
|
|
|
// Not running on an Apple platform
|
|
Internal_AfterModification();
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool Internal_EqualName(
|
|
const ON_wString& lhs,
|
|
const ON_wString& rhs
|
|
)
|
|
{
|
|
return (lhs.IsNotEmpty() && ON_wString::EqualOrdinal(lhs, rhs, true));
|
|
}
|
|
|
|
bool ON_Font::EqualFontFamily(
|
|
const ON_Font* lhs,
|
|
const ON_Font* rhs
|
|
)
|
|
{
|
|
if (nullptr == lhs || nullptr == rhs)
|
|
return false;
|
|
if (lhs == rhs)
|
|
return true;
|
|
|
|
if (Internal_EqualName(lhs->m_en_family_name, rhs->m_en_family_name))
|
|
return true;
|
|
if (Internal_EqualName(lhs->FamilyName(ON_Font::NameLocale::LocalizedFirst), rhs->FamilyName(ON_Font::NameLocale::LocalizedFirst)))
|
|
return true;
|
|
if (lhs->FaceName().IsNotEmpty() && rhs->FamilyName().IsNotEmpty())
|
|
return false; // both lhs and rhs have non-empty family names.
|
|
|
|
// Testing PostScript, and LOGFONT names handles cases where the family name is missing.
|
|
const bool rc
|
|
= Internal_EqualName(lhs->m_en_postscript_name, rhs->m_en_postscript_name)
|
|
|| Internal_EqualName(lhs->m_en_windows_logfont_name, rhs->m_en_windows_logfont_name)
|
|
|| Internal_EqualName(lhs->PostScriptName(ON_Font::NameLocale::LocalizedFirst),rhs->PostScriptName(ON_Font::NameLocale::LocalizedFirst))
|
|
|| Internal_EqualName(lhs->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst),rhs->WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst))
|
|
;
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Font::EqualFontFamilyAndFace(
|
|
const ON_Font* lhs,
|
|
const ON_Font* rhs
|
|
)
|
|
{
|
|
if (nullptr == lhs || nullptr == rhs)
|
|
return false;
|
|
if (lhs == rhs)
|
|
return true;
|
|
|
|
bool rc = EqualFontFamily(lhs,rhs);
|
|
if (rc)
|
|
{
|
|
rc = Internal_EqualName(lhs->m_en_face_name, rhs->m_en_face_name)
|
|
|| Internal_EqualName(lhs->FaceName(ON_Font::NameLocale::LocalizedFirst), rhs->FaceName(ON_Font::NameLocale::LocalizedFirst))
|
|
;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
const ON_wString ON_Font::Locale() const
|
|
{
|
|
return m_locale_name;
|
|
}
|
|
|
|
const wchar_t* ON_Font::AppleFontNameAsPointer() const
|
|
{
|
|
return ON_Font::Internal_GetName(ON_Font::NameLocale::LocalizedFirst, m_loc_postscript_name, m_en_postscript_name).Array();
|
|
}
|
|
|
|
const ON_wString& ON_Font::AppleFontName() const
|
|
{
|
|
return
|
|
m_loc_postscript_name.IsNotEmpty()
|
|
? m_loc_postscript_name
|
|
: m_en_postscript_name;
|
|
}
|
|
|
|
const wchar_t* ON_Font::PostScriptNameAsPointer() const
|
|
{
|
|
return ON_Font::Internal_GetName(ON_Font::NameLocale::LocalizedFirst, m_loc_postscript_name, m_en_postscript_name).Array();
|
|
}
|
|
|
|
const ON_wString ON_Font::PostScriptName(ON_Font::NameLocale name_locale) const
|
|
{
|
|
return ON_Font::Internal_GetName(name_locale, m_loc_postscript_name, m_en_postscript_name);
|
|
}
|
|
|
|
const ON_wString ON_Font::PostScriptName() const
|
|
{
|
|
return ON_Font::Internal_GetName(ON_Font::NameLocale::LocalizedFirst, m_loc_postscript_name, m_en_postscript_name);
|
|
}
|
|
|
|
const ON_wString ON_Font::FamilyName(ON_Font::NameLocale name_locale) const
|
|
{
|
|
return ON_Font::Internal_GetName(name_locale, m_loc_family_name, m_en_family_name);
|
|
}
|
|
|
|
const ON_wString ON_Font::FamilyName() const
|
|
{
|
|
return FamilyName(ON_Font::NameLocale::LocalizedFirst);
|
|
}
|
|
|
|
const ON_wString ON_Font::FaceName(
|
|
ON_Font::NameLocale name_locale
|
|
) const
|
|
{
|
|
return ON_Font::Internal_GetName(name_locale, m_loc_face_name, m_en_face_name);
|
|
}
|
|
|
|
const ON_wString ON_Font::FaceName() const
|
|
{
|
|
return FaceName(ON_Font::NameLocale::LocalizedFirst);
|
|
}
|
|
|
|
const ON_wString ON_Font::WindowsLogfontName(
|
|
ON_Font::NameLocale name_locale
|
|
) const
|
|
{
|
|
return ON_Font::Internal_GetName(name_locale, m_loc_windows_logfont_name, m_en_windows_logfont_name);
|
|
}
|
|
|
|
const ON_wString ON_Font::WindowsLogfontName() const
|
|
{
|
|
return WindowsLogfontName(ON_Font::NameLocale::LocalizedFirst);
|
|
}
|
|
|
|
const ON_wString ON_Font::QuartetName(
|
|
ON_Font::NameLocale name_locale
|
|
) const
|
|
{
|
|
// This may need adjustment for MacOS.
|
|
return WindowsLogfontName(name_locale);
|
|
}
|
|
|
|
const ON_wString ON_Font::QuartetName() const
|
|
{
|
|
return ON_Font::QuartetName(ON_Font::NameLocale::LocalizedFirst);
|
|
}
|
|
|
|
bool ON_Font::IsBoldInQuartet() const
|
|
{
|
|
for (;;)
|
|
{
|
|
const ON_FontFaceQuartet q = InstalledFontQuartet();
|
|
const bool bQuartetItalic = (ON_Font::Style::Italic == m_font_style || ON_Font::Style::Oblique == m_font_style);
|
|
|
|
const ON_Font* regular
|
|
= bQuartetItalic
|
|
? q.ItalicFace()
|
|
: q.RegularFace();
|
|
|
|
const ON_Font* bold
|
|
= bQuartetItalic
|
|
? q.BoldItalicFace()
|
|
: q.BoldFace();
|
|
|
|
if (nullptr == regular && nullptr == bold)
|
|
break;
|
|
|
|
if (nullptr == bold)
|
|
return false; // No bold in this quartet.
|
|
|
|
if (nullptr == regular)
|
|
return true; // no regular in this quartet.
|
|
|
|
const unsigned int font_weight = static_cast<unsigned char>(FontWeight());
|
|
const unsigned int regular_weight = static_cast<unsigned char>(regular->FontWeight());
|
|
const unsigned int bold_weight = static_cast<unsigned char>(bold->FontWeight());
|
|
if (regular_weight < bold_weight)
|
|
{
|
|
return (2 * font_weight > (regular_weight + bold_weight));
|
|
}
|
|
return (font_weight > regular_weight);
|
|
}
|
|
|
|
return IsBold();
|
|
}
|
|
|
|
const ON_FontFaceQuartet ON_Font::InstalledFontQuartet() const
|
|
{
|
|
return InstalledFontList().QuartetFromQuartetName(QuartetName());
|
|
}
|
|
|
|
|
|
const ON_wString& ON_Font::Internal_GetName(
|
|
ON_Font::NameLocale name_locale,
|
|
const ON_wString& localized_name,
|
|
const ON_wString& english_name
|
|
)
|
|
{
|
|
if (ON_Font::NameLocale::Localized == name_locale)
|
|
return localized_name;
|
|
if (ON_Font::NameLocale::English == name_locale)
|
|
return english_name;
|
|
return localized_name.IsNotEmpty() ? localized_name : english_name;
|
|
}
|
|
|
|
const ON_wString Internal_RichTextCleaner
|
|
(
|
|
const wchar_t* dirty_name
|
|
)
|
|
{
|
|
ON_wString clean_name(dirty_name);
|
|
clean_name.TrimLeftAndRight();
|
|
clean_name.Remove(ON_wString::Space);
|
|
clean_name.Remove(ON_wString::HyphenMinus);
|
|
return clean_name;
|
|
}
|
|
|
|
const ON_wString ON_Font::RichTextFontName() const
|
|
{
|
|
return ON_Font::RichTextFontName(this, false);
|
|
}
|
|
|
|
const ON_wString ON_Font::RichTextFontName(
|
|
const ON_Font* font,
|
|
bool bDefaultIfEmpty
|
|
)
|
|
{
|
|
// Depending on what created the RTF or what will be parsing the RTF,
|
|
// the face name in the RTF can be a PostScript name, LOGFONT.lfFaceName,
|
|
// IDWriteFont family name, ..., and probably several other choices.
|
|
|
|
// The complete PostScript name is a bad choice. It doesn't work for
|
|
// WIndows RTF readers. Opennurbs relies on a hueristic to convert
|
|
// PostScript names to family names.
|
|
//
|
|
// The Windows LOGFONT.lfFaceName is a bad choice. It doesn't work for
|
|
// Apple RTF readers but it works better on Windows readers than
|
|
// the proper family name for faces like "Arial Narrow" and "Arial Black"
|
|
// In these cases. "Arial" is the family name, but there are generally
|
|
// not enough details in RTF files and the code that parses it to recover
|
|
// and select the Narrow or Black face if the family is set to Arial.
|
|
//
|
|
// For fonts that have only the 4 standard faces (regular, bold, italic,
|
|
// and bold-italic), the ON_Font::FamilyName() generally works well both
|
|
// platforms. However, it is not easy to determine when a font will only
|
|
// be published with the 4 standard faces and popular fonts often have
|
|
// more than 4 faces so they can offer a wider variety of weight,
|
|
// stretch/width, and style/slope options.
|
|
//
|
|
// The more general problem is that modern fonts and font rendering
|
|
// has outgrown the limitations codes that parse RTF.
|
|
|
|
const ON_Font* fonts[2]{ font, bDefaultIfEmpty ? &ON_Font::Default : nullptr };
|
|
ON_wString rtf_font_name;
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
const ON_Font* f = fonts[i];
|
|
if (nullptr == f)
|
|
continue;
|
|
|
|
// It is important to use the WindowsLogfontName
|
|
// before resorting to family name. This gives the best
|
|
// results for getting the right answer from font faces
|
|
// like "Arial Narrow" and "Arial Black". In both cases,
|
|
// "Arial" is the more correct choice of a family name
|
|
// but there are generally not enough details in RTF files
|
|
// and the code that parses it to use the Narrow or Black
|
|
// face if the family is set to Arial.
|
|
rtf_font_name = f->WindowsLogfontName();
|
|
if (rtf_font_name.IsNotEmpty())
|
|
break;
|
|
|
|
rtf_font_name = f->FamilyName();
|
|
if (rtf_font_name.IsNotEmpty())
|
|
break;
|
|
|
|
rtf_font_name = ON_Font::FamilyNameFromDirtyName(f->PostScriptName());
|
|
if (rtf_font_name.IsNotEmpty())
|
|
break;
|
|
|
|
// DO NOT USE FACE NAME - it is a term like "Regular" / "Bold" / ...and is useless.
|
|
}
|
|
|
|
if (rtf_font_name.IsEmpty() && bDefaultIfEmpty)
|
|
{
|
|
rtf_font_name = ON_Font::DefaultFamilyName();
|
|
}
|
|
|
|
return rtf_font_name;
|
|
}
|
|
|
|
/*
|
|
InternalHashToName is used to map hash values from
|
|
a ON_Font::FontNameHash() to the corresponding correct font Family name.
|
|
*/
|
|
class InternalHashToName
|
|
{
|
|
public:
|
|
InternalHashToName() = default;
|
|
~InternalHashToName() = default;
|
|
InternalHashToName(const InternalHashToName&) = default;
|
|
InternalHashToName& operator=(const InternalHashToName&) = default;
|
|
|
|
public:
|
|
InternalHashToName
|
|
(
|
|
const wchar_t* dirty_name,
|
|
const wchar_t* clean_family_name
|
|
)
|
|
: m_dirty_name_hash(ON_Font::FontNameHash(dirty_name,true))
|
|
, m_family_name(clean_family_name)
|
|
{}
|
|
|
|
InternalHashToName
|
|
(
|
|
const wchar_t* clean_family_name
|
|
)
|
|
: m_dirty_name_hash(ON_Font::FontNameHash(clean_family_name,false))
|
|
, m_family_name(clean_family_name)
|
|
{}
|
|
|
|
public:
|
|
static int CompareHash(const void* lhs, const void* rhs)
|
|
{
|
|
return ON_SHA1_Hash::Compare(((const InternalHashToName*)lhs)->m_dirty_name_hash, ((const InternalHashToName*)rhs)->m_dirty_name_hash);
|
|
}
|
|
|
|
static size_t SortAndCullByHash(InternalHashToName* a, size_t a_count)
|
|
{
|
|
if (nullptr == a || a_count <= 0)
|
|
return 0;
|
|
|
|
ON_qsort(a, a_count, sizeof(a[0]), InternalHashToName::CompareHash);
|
|
size_t culled_count = 1;
|
|
const InternalHashToName* k1 = &a[0];
|
|
for (size_t i = 1; i < a_count; i++)
|
|
{
|
|
if (0 == InternalHashToName::CompareHash(k1, &a[i]) )
|
|
continue;
|
|
a[culled_count] = a[i];
|
|
k1 = &a[culled_count];
|
|
culled_count++;
|
|
}
|
|
return culled_count;
|
|
}
|
|
public:
|
|
ON_SHA1_Hash m_dirty_name_hash = ON_SHA1_Hash::ZeroDigest;
|
|
const wchar_t* m_family_name = nullptr;
|
|
};
|
|
|
|
static ON_OutlineFigure::Type Internal_FigureTypeFromHashedFontName(
|
|
const InternalHashToName* key
|
|
)
|
|
{
|
|
static InternalHashToName single_stroke_name_map[] =
|
|
{
|
|
// Known single stroke fonts names
|
|
|
|
// Machine Tool
|
|
InternalHashToName(L"Machine Tool Gothic"), // Family
|
|
InternalHashToName(L"MachineToolGothic"), // PostScript
|
|
InternalHashToName(L"Machine Tool Grenadier"), // Family
|
|
InternalHashToName(L"MachineToolGrenadier"), // PostScript
|
|
InternalHashToName(L"Machine Tool Handscript"), // Family
|
|
InternalHashToName(L"MachineToolHandscript"), // PostScript
|
|
InternalHashToName(L"Machine Tool SanSerif"), // Family
|
|
InternalHashToName(L"MachineToolSanSerif"), // PostScript
|
|
InternalHashToName(L"Machine Tool Script"), // Family
|
|
InternalHashToName(L"MachineToolScript"), // PostScript
|
|
|
|
// MecSoft
|
|
InternalHashToName(L"MecSoft_Font-1"), // Family
|
|
InternalHashToName(L"MecSoft_Font-1"), // PostScript
|
|
|
|
// Orach Technic 1L
|
|
InternalHashToName(L"OrachTech1Lotf"), // Family
|
|
InternalHashToName(L"OrachTech1Lotf"), // PostScript
|
|
InternalHashToName(L"OrachTechDemo1Lotf"), // Family
|
|
InternalHashToName(L"OrachTechDemo1Lotf"), // PostScript
|
|
InternalHashToName(L"OrachTech1Lttf"), // Family
|
|
InternalHashToName(L"OrachTech1Lttf"), // PostScript
|
|
InternalHashToName(L"OrachTechDemo1Lttf"), // Family
|
|
InternalHashToName(L"OrachTechDemo1Lttf"), // PostScript
|
|
|
|
// Rhino Single Stroke by Lowell Walmsley
|
|
InternalHashToName(L"RHSS"), // Family
|
|
InternalHashToName(L"RhSS-Normal"), // PostScript
|
|
|
|
// SD Stroke Open Type Font
|
|
InternalHashToName(L"SDstroke"), // Family
|
|
InternalHashToName(L"SDstroke-latin"), // PostScript
|
|
};
|
|
|
|
static InternalHashToName double_stroke_name_map[] =
|
|
{
|
|
// Known double stroke fonts names
|
|
// If a double stroke font is treated as a single stroke font,
|
|
// then the "TrueType" automatic closure segment is dropped
|
|
// and glyphs for code points like o O 8 Q, ... have missing segments.
|
|
|
|
// CamBam Stick fonts
|
|
InternalHashToName(L"1CamBam_Stick_1"), // Family
|
|
InternalHashToName(L"1CamBamStick1-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_2"), // Family
|
|
InternalHashToName(L"1CamBamStick2-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_3"), // Family
|
|
InternalHashToName(L"1CamBamStick3-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_4"), // Family
|
|
InternalHashToName(L"1CamBamStick4-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_5"), // Family
|
|
InternalHashToName(L"1CamBamStick5-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_6"), // Family
|
|
InternalHashToName(L"1CamBamStick6-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_7"), // Family
|
|
InternalHashToName(L"1CamBamStick7-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_8"), // Family
|
|
InternalHashToName(L"1CamBamStick8-normal"), // PostScript
|
|
InternalHashToName(L"1CamBam_Stick_9"), // Family
|
|
InternalHashToName(L"1CamBamStick9"), // PostScript
|
|
|
|
// CNC Vector
|
|
InternalHashToName(L"CNC Vector"), // Family
|
|
InternalHashToName(L"CNCVector"), // PostScript
|
|
InternalHashToName(L"CNC Vector Mirror"), // Family
|
|
InternalHashToName(L"CNCVectorMirror"), // PostScript
|
|
|
|
// NOTE WELL:
|
|
// Orach Technic 2L fonts are NOT a double stroke fonts.
|
|
// The figure outlines are simple closed perimenters
|
|
// that contain postive areas.
|
|
// The behave poorly in simulated bold face.
|
|
};
|
|
|
|
static InternalHashToName perimeter_name_map[] =
|
|
{
|
|
// Known perimeter stroke fonts names
|
|
// In fact, almost every TrueType, PostScript, and OpenType font
|
|
// has periemter figures. The list below contains fonts
|
|
// that some people might think are single or double stroke
|
|
// and common fonts so we don't keep checking.
|
|
|
|
// Common
|
|
InternalHashToName(L"Arial"),
|
|
InternalHashToName(L"Bahnschrift"),
|
|
InternalHashToName(L"CityBlueprint"),
|
|
InternalHashToName(L"CountryBlueprint"),
|
|
InternalHashToName(L"Courier"),
|
|
InternalHashToName(L"Courier New"),
|
|
InternalHashToName(L"Franklin Gothic"),
|
|
InternalHashToName(L"Helvetica"),
|
|
InternalHashToName(L"Helvetica Neue"),
|
|
InternalHashToName(L"Segoe UI"),
|
|
InternalHashToName(L"Times New Roman"),
|
|
InternalHashToName(L"Tecnnic"),
|
|
InternalHashToName(L"ArialMT"),
|
|
InternalHashToName(L"CourierNewPS"),
|
|
InternalHashToName(L"CourierNewPSMT"),
|
|
InternalHashToName(L"FranklinGothic"),
|
|
InternalHashToName(L"HelveticaNeue"),
|
|
InternalHashToName(L"TimesNewRomanPS"),
|
|
InternalHashToName(L"TimesNewRomanPSMT"),
|
|
|
|
// Orach Technic 1L
|
|
InternalHashToName(L"OrachTech2Lotf"), // Family
|
|
InternalHashToName(L"OrachTech2Lotf"), // PostScript
|
|
InternalHashToName(L"OrachTechDemo2Lotf"), // Family
|
|
InternalHashToName(L"OrachTechDemo2Lotf"), // PostScript
|
|
InternalHashToName(L"OrachTech2Lttf"), // Family
|
|
InternalHashToName(L"OrachTech2Lttf"), // PostScript
|
|
InternalHashToName(L"OrachTechDemo2Lttf"), // Family
|
|
InternalHashToName(L"OrachTechDemo2Lttf"), // PostScript
|
|
};
|
|
|
|
static size_t single_stroke_name_map_count = 0;
|
|
static size_t double_stroke_name_map_count = 0;
|
|
static size_t perimeter_name_map_count = 0;
|
|
|
|
if ( nullptr == key)
|
|
return ON_OutlineFigure::Type::Unknown;
|
|
|
|
const InternalHashToName* e;
|
|
const size_t sizeof_e = sizeof(*e);
|
|
|
|
if (0 == single_stroke_name_map_count)
|
|
{
|
|
single_stroke_name_map_count = InternalHashToName::SortAndCullByHash(single_stroke_name_map, (sizeof(single_stroke_name_map) / sizeof_e));
|
|
double_stroke_name_map_count = InternalHashToName::SortAndCullByHash(double_stroke_name_map, (sizeof(double_stroke_name_map) / sizeof_e));
|
|
perimeter_name_map_count = InternalHashToName::SortAndCullByHash(perimeter_name_map, (sizeof(perimeter_name_map) / sizeof_e));
|
|
}
|
|
|
|
|
|
e = (const InternalHashToName*)bsearch(key, single_stroke_name_map, single_stroke_name_map_count, sizeof_e, InternalHashToName::CompareHash);
|
|
if (nullptr != e)
|
|
return ON_OutlineFigure::Type::SingleStroke;
|
|
|
|
e = (const InternalHashToName*)bsearch(key, double_stroke_name_map, double_stroke_name_map_count, sizeof_e, InternalHashToName::CompareHash);
|
|
if (nullptr != e)
|
|
return ON_OutlineFigure::Type::DoubleStroke;
|
|
|
|
e = (const InternalHashToName*)bsearch(key, perimeter_name_map, perimeter_name_map_count, sizeof_e, InternalHashToName::CompareHash);
|
|
if (nullptr != e)
|
|
return ON_OutlineFigure::Type::Perimeter;
|
|
|
|
return ON_OutlineFigure::Type::Unknown;
|
|
}
|
|
|
|
static bool Internal_IsEngravingFont(
|
|
const InternalHashToName* key
|
|
)
|
|
{
|
|
|
|
// The structure of the font is a "perimeter", but the intended use
|
|
// is for engraving. The OrachTech*2* fonts behave poorly when used
|
|
// as perimeter fonts.
|
|
static InternalHashToName double_line_name_map[] =
|
|
{
|
|
// Orach Technic 2L
|
|
InternalHashToName(L"OrachTech2Lotf"), // Family
|
|
InternalHashToName(L"OrachTech2Lotf"), // PostScript
|
|
InternalHashToName(L"OrachTechDemo2Lotf"), // Family
|
|
InternalHashToName(L"OrachTechDemo2Lotf"), // PostScript
|
|
InternalHashToName(L"OrachTech2Lttf"), // Family
|
|
InternalHashToName(L"OrachTech2Lttf"), // PostScript
|
|
InternalHashToName(L"OrachTechDemo2Lttf"), // Family
|
|
InternalHashToName(L"OrachTechDemo2Lttf"), // PostScript
|
|
};
|
|
|
|
static size_t double_line_name_map_count = 0;
|
|
|
|
const InternalHashToName* e;
|
|
const size_t sizeof_e = sizeof(*e);
|
|
|
|
if (0 == double_line_name_map_count)
|
|
{
|
|
double_line_name_map_count = InternalHashToName::SortAndCullByHash(double_line_name_map, (sizeof(double_line_name_map) / sizeof_e));
|
|
}
|
|
|
|
e = (const InternalHashToName*)bsearch(key, double_line_name_map, double_line_name_map_count, sizeof_e, InternalHashToName::CompareHash);
|
|
if (nullptr != e)
|
|
return true;
|
|
|
|
ON_OutlineFigure::Type outline_type = Internal_FigureTypeFromHashedFontName(key);
|
|
if (ON_OutlineFigure::Type::SingleStroke == outline_type)
|
|
return true;
|
|
if (ON_OutlineFigure::Type::DoubleStroke == outline_type)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
const wchar_t* ON_OutlineFigure::OrientationToWideString(
|
|
ON_OutlineFigure::Orientation orientation
|
|
)
|
|
{
|
|
const wchar_t* s;
|
|
|
|
switch (orientation)
|
|
{
|
|
case ON_OutlineFigure::Orientation::Unset:
|
|
s = L"Unset";
|
|
break;
|
|
case ON_OutlineFigure::Orientation::CounterClockwise:
|
|
s = L"CounterClockwise";
|
|
break;
|
|
case ON_OutlineFigure::Orientation::Clockwise:
|
|
s = L"Clockwise";
|
|
break;
|
|
case ON_OutlineFigure::Orientation::NotOriented:
|
|
s = L"NotOriented";
|
|
break;
|
|
case ON_OutlineFigure::Orientation::Error:
|
|
s = L"Error";
|
|
break;
|
|
default:
|
|
s = L"<INVALID>";
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
ON_OutlineFigure::Type ON_OutlineFigure::FigureTypeFromFontName(
|
|
const wchar_t* font_name
|
|
)
|
|
{
|
|
if (nullptr == font_name || 0 == font_name[0])
|
|
return ON_OutlineFigure::Type::Unset; // better than unknown when name is empty
|
|
|
|
const InternalHashToName key(font_name, nullptr);
|
|
return Internal_FigureTypeFromHashedFontName(&key);
|
|
}
|
|
|
|
ON_OutlineFigure::Type ON_Font::OutlineFigureType() const
|
|
{
|
|
const ON_wString names[] =
|
|
{
|
|
FamilyName(),
|
|
FamilyName(ON_Font::NameLocale::English),
|
|
PostScriptName(),
|
|
PostScriptName(ON_Font::NameLocale::English)
|
|
};
|
|
|
|
InternalHashToName key[sizeof(names)/sizeof(names[0])];
|
|
|
|
const int name_count = (int)(sizeof(names) / sizeof(names[0]));
|
|
int key_count = 0;
|
|
for (int i = 0; i < name_count; i++)
|
|
{
|
|
const ON_wString& name = names[i];
|
|
if (name.IsEmpty())
|
|
continue;
|
|
|
|
// computing the hash is much more expensive than checking for duplicate names (which is common)
|
|
bool bSkipName = name.IsEmpty();
|
|
for (int j = 0; false == bSkipName && j < i; j++)
|
|
bSkipName = (name == names[j]);
|
|
if (bSkipName)
|
|
continue;
|
|
|
|
// compute name hash
|
|
key[key_count] = InternalHashToName(name, nullptr);
|
|
|
|
// searching for a duplicate hash is more expensive than checking for duplicate hash
|
|
// (which is common because of space and hyphen differences between family and postscript names)
|
|
for (int j = 0; false == bSkipName && j < key_count; j++)
|
|
bSkipName = (key[key_count].m_dirty_name_hash == key[i].m_dirty_name_hash);
|
|
if (bSkipName)
|
|
continue;
|
|
|
|
// search for matching name hash is lists of known single stroke and double stroke fonts
|
|
ON_OutlineFigure::Type figure_type = Internal_FigureTypeFromHashedFontName(&key[key_count]);
|
|
key_count++;
|
|
if (
|
|
ON_OutlineFigure::Type::Unset != figure_type
|
|
&& ON_OutlineFigure::Type::Unknown != figure_type
|
|
&& ON_OutlineFigure::Type::Mixed != figure_type
|
|
)
|
|
return figure_type;
|
|
}
|
|
return ON_OutlineFigure::Type::Unknown;
|
|
}
|
|
|
|
bool ON_Font::IsSingleStrokeFont() const
|
|
{
|
|
return ON_OutlineFigure::Type::SingleStroke == OutlineFigureType();
|
|
}
|
|
|
|
|
|
bool ON_Font::IsDoubleStrokeFont() const
|
|
{
|
|
return ON_OutlineFigure::Type::DoubleStroke == OutlineFigureType();
|
|
}
|
|
|
|
bool ON_Font::IsSingleStrokeOrDoubleStrokeFont() const
|
|
{
|
|
const ON_OutlineFigure::Type figure_type = OutlineFigureType();
|
|
return (
|
|
ON_OutlineFigure::Type::SingleStroke == figure_type
|
|
|| ON_OutlineFigure::Type::DoubleStroke == figure_type
|
|
);
|
|
}
|
|
|
|
bool ON_Font::IsEngravingFont() const
|
|
{
|
|
const ON_wString names[] =
|
|
{
|
|
FamilyName(),
|
|
FamilyName(ON_Font::NameLocale::English),
|
|
PostScriptName(),
|
|
PostScriptName(ON_Font::NameLocale::English)
|
|
};
|
|
|
|
InternalHashToName key[sizeof(names)/sizeof(names[0])];
|
|
|
|
const int name_count = (int)(sizeof(names) / sizeof(names[0]));
|
|
int key_count = 0;
|
|
for (int i = 0; i < name_count; i++)
|
|
{
|
|
const ON_wString& name = names[i];
|
|
if (name.IsEmpty())
|
|
continue;
|
|
|
|
// computing the hash is much more expensive than checking for duplicate names (which is common)
|
|
bool bSkipName = name.IsEmpty();
|
|
for (int j = 0; false == bSkipName && j < i; j++)
|
|
bSkipName = (name == names[j]);
|
|
if (bSkipName)
|
|
continue;
|
|
|
|
// compute name hash
|
|
key[key_count] = InternalHashToName(name, nullptr);
|
|
|
|
// searching for a duplicate hash is more expensive than checking for duplicate hash
|
|
// (which is common because of space and hyphen differences between family and postscript names)
|
|
for (int j = 0; false == bSkipName && j < key_count; j++)
|
|
bSkipName = (key[key_count].m_dirty_name_hash == key[i].m_dirty_name_hash);
|
|
if (bSkipName)
|
|
continue;
|
|
|
|
// search for matching name hash is lists of known single stroke and double stroke fonts
|
|
bool bIsEngravingFont = Internal_IsEngravingFont(&key[key_count]);
|
|
key_count++;
|
|
if (bIsEngravingFont)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
const ON_wString ON_Font::FamilyNameFromDirtyName(
|
|
const wchar_t* dirty_name
|
|
)
|
|
{
|
|
if (nullptr == dirty_name
|
|
|| 0 == dirty_name[0]
|
|
|| ON_wString::HyphenMinus == dirty_name[0])
|
|
{
|
|
return ON_wString::EmptyString;
|
|
}
|
|
|
|
// incredibly common query
|
|
if (ON_wString::EqualOrdinal(ON_Font::DefaultFamilyName(), dirty_name, true))
|
|
{
|
|
return ON_wString(ON_Font::DefaultFamilyName());
|
|
}
|
|
|
|
static bool bInitialized = false;
|
|
static ON_SimpleArray<InternalHashToName> clean_names;
|
|
static ON_SimpleArray<InternalHashToName> dirty_names;
|
|
|
|
const size_t sizeof_name = sizeof(clean_names[0]);
|
|
|
|
if (false == bInitialized)
|
|
{
|
|
bInitialized = true;
|
|
ON_MemoryAllocationTracking disable_tracking(false);
|
|
ON_SimpleArray<const ON_Font*> installed_fonts;
|
|
ON_Font::GetInstalledFontList(installed_fonts);
|
|
size_t installed_count = installed_fonts.UnsignedCount();
|
|
clean_names.Reserve(16 + installed_count);
|
|
dirty_names.Reserve(16 + 2*installed_count);
|
|
|
|
// Append known clean family names that contain a hypen.
|
|
clean_names.Append(InternalHashToName(L"AvenirLT-Roman"));
|
|
clean_names.Append(InternalHashToName(L"MecSoft_Font-1"));
|
|
clean_names.Append(InternalHashToName(L"MingLiU_HKSCS-ExtB"));
|
|
clean_names.Append(InternalHashToName(L"MingLiU-ExtB"));
|
|
clean_names.Append(InternalHashToName(L"OCR-A II"));
|
|
clean_names.Append(InternalHashToName(L"PMingLiU-ExtB"));
|
|
clean_names.Append(InternalHashToName(L"SimSun-ExtB"));
|
|
|
|
// Append known clean family names that end in "MT"
|
|
clean_names.Append(InternalHashToName(L"Brush Script MT"));
|
|
clean_names.Append(InternalHashToName(L"Devanagari MT"));
|
|
clean_names.Append(InternalHashToName(L"French Script MT"));
|
|
clean_names.Append(InternalHashToName(L"Gujarati MT"));
|
|
clean_names.Append(InternalHashToName(L"Gurmukhi MT"));
|
|
clean_names.Append(InternalHashToName(L"New Peninim MT"));
|
|
clean_names.Append(InternalHashToName(L"OCR B MT"));
|
|
|
|
// Append common clean family names (in case they are not installed on this computer)
|
|
clean_names.Append(InternalHashToName(L"Arial"));
|
|
clean_names.Append(InternalHashToName(L"Bahnschrift"));
|
|
clean_names.Append(InternalHashToName(L"CityBlueprint")); // common in ACAD dwg files
|
|
clean_names.Append(InternalHashToName(L"CountryBlueprint")); // common in ACAD dwg files
|
|
clean_names.Append(InternalHashToName(L"Courier"));
|
|
clean_names.Append(InternalHashToName(L"Courier New"));
|
|
clean_names.Append(InternalHashToName(L"Franklin Gothic"));
|
|
clean_names.Append(InternalHashToName(L"Helvetica"));
|
|
clean_names.Append(InternalHashToName(L"Helvetica Neue"));
|
|
clean_names.Append(InternalHashToName(L"Segoe UI"));
|
|
clean_names.Append(InternalHashToName(L"Times New Roman"));
|
|
clean_names.Append(InternalHashToName(L"Tecnnic")); // common in ACAD dwg files
|
|
|
|
// Append common dirty names (in case they are not installed on this computer)
|
|
dirty_names.Append(InternalHashToName(L"ArialMT", L"Arial"));
|
|
dirty_names.Append(InternalHashToName(L"CourierNewPS", L"Courier New")); // Handles all CourierNewPS-...MT names
|
|
dirty_names.Append(InternalHashToName(L"CourierNewPSMT", L"Courier New"));
|
|
dirty_names.Append(InternalHashToName(L"FranklinGothic", L"Franklin Gothic"));
|
|
dirty_names.Append(InternalHashToName(L"HelveticaNeue", L"Helvetica Neue")); // Handles all PostScript "HelveticaNeue-..." names
|
|
dirty_names.Append(InternalHashToName(L"TimesNewRomanPS", L"Times New Roman")); // Handles all TimesNewRomanPS-...MT names
|
|
dirty_names.Append(InternalHashToName(L"TimesNewRomanPSMT", L"Times New Roman"));
|
|
|
|
const wchar_t* prev_clean_family_name = nullptr;
|
|
for (size_t i = 0; i < installed_count; i++)
|
|
{
|
|
#if defined(ON_RUNTIME_ANDROID)
|
|
const ON_Font* font = installed_fonts[(int)i];
|
|
#else
|
|
const ON_Font* font = installed_fonts[i];
|
|
#endif
|
|
if (nullptr == font)
|
|
continue;
|
|
|
|
// NOTE:
|
|
// font and the clean_family_name pointer persist for the lifetime of the application.
|
|
const wchar_t* clean_family_name = static_cast<const wchar_t*>(font->FamilyName());
|
|
if (nullptr == clean_family_name || 0 == clean_family_name[0])
|
|
continue;
|
|
|
|
// Add clean_family_name to clean_names[]
|
|
if (false == ON_wString::EqualOrdinal(clean_family_name, prev_clean_family_name, true))
|
|
{
|
|
// Since the installed font list is sorted by family name, we can limit the sorting
|
|
// and culling time for clean_names[] by keeping track of prev_clean_family_name
|
|
clean_names.Append(InternalHashToName(clean_family_name));
|
|
prev_clean_family_name = clean_family_name;
|
|
}
|
|
|
|
// Add PostScript name and LOGFONT.lfFaceName to dirty_names[]
|
|
for (int j = 0; j < 2; j++)
|
|
{
|
|
// NOTE:
|
|
// font and the other_name pointer persist for the lifetime of the application.
|
|
//
|
|
// Do NOT use the font->Description() string. That string does not persist
|
|
// And it adds to additional functionallity because it has the form <clean family name>-...
|
|
const wchar_t* other_name
|
|
= (0 == j)
|
|
? static_cast<const wchar_t*>(font->PostScriptName())
|
|
: static_cast<const wchar_t*>(font->WindowsLogfontName());
|
|
if (nullptr == other_name || 0 == other_name[0])
|
|
continue;
|
|
if (ON_wString::EqualOrdinal(clean_family_name, other_name, true))
|
|
{
|
|
// LOGFONT.lfFaceName or PostScript name = clean family name
|
|
// and does not need to be in the "dirty" list. If this name
|
|
// is ever passed in, it woll be handled by the clean_list[] entry.
|
|
continue;
|
|
}
|
|
dirty_names.Append(InternalHashToName(other_name,clean_family_name));
|
|
}
|
|
}
|
|
|
|
// cull duplicates and ambiguous cases
|
|
for (int adex = 0; adex < 2; adex++)
|
|
{
|
|
ON_SimpleArray<InternalHashToName>& a
|
|
= (0 == adex)
|
|
? clean_names
|
|
: dirty_names;
|
|
|
|
const size_t count0 = a.UnsignedCount();
|
|
|
|
ON_qsort(a.Array(), count0, sizeof_name, InternalHashToName::CompareHash);
|
|
int count1 = 0;
|
|
InternalHashToName candidate;
|
|
for (size_t i = 0; i < count0; i++)
|
|
{
|
|
#if defined(ON_RUNTIME_ANDROID)
|
|
InternalHashToName e = a[(int)i];
|
|
#else
|
|
InternalHashToName e = a[i];
|
|
#endif
|
|
if (candidate.m_dirty_name_hash == e.m_dirty_name_hash)
|
|
{
|
|
if (nullptr == candidate.m_family_name)
|
|
continue;
|
|
|
|
if (ON_wString::EqualOrdinal(candidate.m_family_name, e.m_family_name, true))
|
|
continue; // duplicate
|
|
|
|
// rare ambiguous case
|
|
// Fonts with different family names have the same dirty name.
|
|
candidate.m_family_name = nullptr;
|
|
continue;
|
|
}
|
|
|
|
if (candidate.m_family_name)
|
|
{
|
|
a[count1++] = candidate;
|
|
}
|
|
candidate = e;
|
|
}
|
|
if (candidate.m_family_name)
|
|
{
|
|
a[count1++] = candidate;
|
|
}
|
|
a.SetCount(count1);
|
|
a.Shrink();
|
|
}
|
|
}
|
|
|
|
const InternalHashToName* e = nullptr;
|
|
const InternalHashToName clean_key(dirty_name);
|
|
e = (const InternalHashToName*)bsearch(&clean_key, clean_names.Array(), clean_names.UnsignedCount(), sizeof_name, InternalHashToName::CompareHash);
|
|
if (nullptr != e)
|
|
{
|
|
// Ignoring case, spaces, and hyphens, dirty_name is a clean family.
|
|
return ON_wString(e->m_family_name);
|
|
}
|
|
|
|
const InternalHashToName dirty_key(dirty_name, dirty_name);
|
|
e = (const InternalHashToName*)bsearch(&dirty_key, dirty_names.Array(), dirty_names.UnsignedCount(), sizeof_name, InternalHashToName::CompareHash);
|
|
if (nullptr != e)
|
|
{
|
|
// Ignoring case, spaces, and hyphens, dirty_name is a PostScript or LOGFONT.lfFaceName
|
|
// and we know the corresponding clean family name.
|
|
return ON_wString(e->m_family_name);
|
|
}
|
|
|
|
// No clean family name is exlicity known for the dirty_name parameter.
|
|
// Truncate the input dirty_name at the first hypen to remove PostScript face descriptions.
|
|
// (This is the reason we explicty add all known faces with hypens above).
|
|
ON_wString best_guess(dirty_name);
|
|
for (const wchar_t* c = dirty_name; 0 != *c; c++)
|
|
{
|
|
if (ON_wString::HyphenMinus != *c)
|
|
continue;
|
|
|
|
// Truncate dirty_name at the first hyphen
|
|
int len = (int)(c - dirty_name);
|
|
|
|
if (len >= 6 && 'M' == dirty_name[len - 2] && 'T' == dirty_name[len - 1])
|
|
{
|
|
// dirty_name = "...MT-...". Assume that we are dealing with a PostScript name
|
|
// and the clean family name does not include the MT.
|
|
// Above we have explicity handled known clean family names that end in MT.
|
|
len -= 2;
|
|
}
|
|
|
|
best_guess.SetLength(len);
|
|
break;
|
|
}
|
|
|
|
return best_guess;
|
|
}
|
|
|
|
const ON_String ON_Font::CleanFontName(
|
|
const wchar_t* dirty_font_name,
|
|
ON_StringMapOrdinalType map
|
|
)
|
|
{
|
|
ON_wString clean_font_name(dirty_font_name);
|
|
wchar_t* s0 = clean_font_name.Array();
|
|
wchar_t* s1 = s0;
|
|
const bool bMap = (ON_StringMapOrdinalType::Identity != map);
|
|
for (wchar_t* s = s0, c = *s++; 0 != c; c = *s++)
|
|
{
|
|
if (
|
|
ON_wString::Space == c
|
|
|| ON_wString::HyphenMinus == c
|
|
|| ON_wString::Underscore == c
|
|
)
|
|
{
|
|
// PostScript, DWrite, LOGFONT all treat spaces, underbars and hyphens
|
|
// differently. The purpose of this function is to get a string that
|
|
// can be used to search for matching fonts. Ignoring spaces, underbars and hyphens
|
|
// makes it possible to find installed fonts using a "dirty name" from another computer.
|
|
continue;
|
|
}
|
|
if (bMap)
|
|
c = ON_wString::MapCharacterOrdinal(map, c);
|
|
*s1++ = c;
|
|
}
|
|
*s1 = 0;
|
|
clean_font_name.SetLength(s1 - s0);
|
|
if (map != ON_StringMapOrdinalType::Identity)
|
|
{
|
|
return clean_font_name.MapStringOrdinal(map);
|
|
}
|
|
return clean_font_name;
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_Font::FontNameHash(
|
|
const wchar_t* font_name,
|
|
bool bStopAtHyphen
|
|
)
|
|
{
|
|
ON_SHA1 sha1;
|
|
const wchar_t* font_name0 = font_name;
|
|
for (wchar_t c = (nullptr != font_name) ? *font_name++ : 0; 0 != c; c = *font_name++)
|
|
{
|
|
if (ON_wString::Space == c)
|
|
continue;
|
|
if (ON_wString::Underscore == c)
|
|
continue;
|
|
if (ON_wString::HyphenMinus == c)
|
|
{
|
|
if (bStopAtHyphen)
|
|
{
|
|
// The hyphens in these special cases are integral parts of the
|
|
// family part of the PostScript name and cannot terminate hashing.
|
|
if (font_name == (font_name0 + 3) && ON_wString::EqualOrdinal(L"MS-", 3, font_name0, 3, true))
|
|
continue;
|
|
if (font_name == (font_name0 + 4) && ON_wString::EqualOrdinal(L"OCR-A", 5, font_name0, 5, true))
|
|
continue;
|
|
if (font_name == (font_name0 + 9) && ON_wString::EqualOrdinal(L"AvenirLT-Roman", 14, font_name0, 14, true))
|
|
continue;
|
|
if (font_name == (font_name0 + 12) && ON_wString::EqualOrdinal(L"MecSoftFont-1", 13, font_name0, 13, true))
|
|
continue;
|
|
if (font_name == (font_name0 + 13) && ON_wString::EqualOrdinal(L"MecSoft_Font-1", 14, font_name0, 14, true))
|
|
continue;
|
|
|
|
// Terminate hashing at this hypen
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
ON__UINT32 cp = ON_DecodeUTF16SurrogatePair(c, *font_name, 0);
|
|
if (0 != cp)
|
|
{
|
|
// (c,*font_name) is a UTF-16 surrogate pair for unicode codepoint cp
|
|
font_name++;
|
|
}
|
|
else
|
|
{
|
|
cp = ON_UnicodeMapCodePointOrdinal(ON_StringMapOrdinalType::MinimumOrdinal, (ON__UINT32)c);
|
|
}
|
|
sha1.AccumulateUnsigned32(cp);
|
|
}
|
|
return sha1.Hash();
|
|
}
|
|
|
|
static bool IsAtoZ(
|
|
const wchar_t* s
|
|
)
|
|
{
|
|
return (nullptr != s && ((s[0] >= 'A' && s[0] <= 'Z') || (s[0] >= 'a' && s[0] <= 'z')));
|
|
}
|
|
|
|
static const unsigned int ParseToken(
|
|
const wchar_t*& s,
|
|
size_t count,
|
|
const wchar_t*const* token,
|
|
const unsigned int* token_rc,
|
|
unsigned int token_not_found_rc
|
|
)
|
|
{
|
|
if (IsAtoZ(s))
|
|
{
|
|
for (size_t i = 0; i < count; i++)
|
|
{
|
|
const int len = (int)ON_wString::Length(token[i]);
|
|
if (ON_wString::EqualOrdinal(token[i], len, s, len, true))
|
|
{
|
|
s += len;
|
|
return token_rc[i];
|
|
}
|
|
}
|
|
}
|
|
return token_not_found_rc;
|
|
}
|
|
|
|
static bool SkipSeparator(bool bSkipSpace, const wchar_t*& s)
|
|
{
|
|
if (nullptr == s)
|
|
return false;
|
|
bool rc = false;
|
|
switch (s[0])
|
|
{
|
|
case '-':
|
|
case '_':
|
|
case ',':
|
|
case ';':
|
|
case '.':
|
|
rc = true;
|
|
break;
|
|
default:
|
|
if (bSkipSpace && ON_wString::Space == s[0])
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (rc)
|
|
s++;
|
|
return rc;
|
|
}
|
|
|
|
static bool ParseStretch(
|
|
const wchar_t*& s,
|
|
ON_Font::Stretch& font_stretch
|
|
)
|
|
{
|
|
if (false == IsAtoZ(s))
|
|
return false;
|
|
|
|
const wchar_t* s1 = s;
|
|
|
|
const wchar_t* prefix_token[] = { L"SEMI", L"DEMI", L"EXTRA", L"ULTRA" };
|
|
const size_t prefix_count = sizeof(prefix_token) / sizeof(prefix_token[0]);
|
|
unsigned int prefix_rc[prefix_count] = { 1, 1, 2, 3 };
|
|
const unsigned int prefix_id = ParseToken(s1, prefix_count, prefix_token, prefix_rc, 0);
|
|
|
|
if ( 0 != prefix_id )
|
|
SkipSeparator(true,s1);
|
|
|
|
const unsigned int medium_rc = (0 != prefix_id) ? 0 : 2;
|
|
const wchar_t* name_token[] = { L"CONDENSED", L"MEDIUM", L"NORMAL", L"EXPANDED", };
|
|
const size_t name_count = sizeof(name_token) / sizeof(name_token[0]);
|
|
unsigned int name_rc[name_count] = { 1,medium_rc,medium_rc,3 };
|
|
const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0);
|
|
if (0 == name_id)
|
|
return false;
|
|
|
|
|
|
bool rc = false;
|
|
switch (name_id)
|
|
{
|
|
case 1: // Condensed
|
|
switch (prefix_id)
|
|
{
|
|
case 0: // no prefix
|
|
font_stretch = ON_Font::Stretch::Condensed;
|
|
rc = true;
|
|
break;
|
|
case 1: // semi
|
|
font_stretch = ON_Font::Stretch::Semicondensed;
|
|
rc = true;
|
|
break;
|
|
case 2: // extra
|
|
font_stretch = ON_Font::Stretch::Extracondensed;
|
|
rc = true;
|
|
break;
|
|
case 3: // ultra
|
|
font_stretch = ON_Font::Stretch::Ultracondensed;
|
|
rc = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 2: // Medium
|
|
switch (prefix_id)
|
|
{
|
|
case 0: // no prefix
|
|
font_stretch = ON_Font::Stretch::Medium;
|
|
rc = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 3: // Expanded
|
|
switch (prefix_id)
|
|
{
|
|
case 0: // no prefix
|
|
font_stretch = ON_Font::Stretch::Expanded;
|
|
rc = true;
|
|
break;
|
|
case 1: // semi
|
|
font_stretch = ON_Font::Stretch::Semiexpanded;
|
|
rc = true;
|
|
break;
|
|
case 2: // extra
|
|
font_stretch = ON_Font::Stretch::Extraexpanded;
|
|
rc = true;
|
|
break;
|
|
case 3: // ultra
|
|
font_stretch = ON_Font::Stretch::Ultraexpanded;
|
|
rc = true;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (rc)
|
|
{
|
|
s = s1;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool ParseWeight(
|
|
const wchar_t*& s,
|
|
ON_Font::Weight& font_weight
|
|
)
|
|
{
|
|
if (false == IsAtoZ(s))
|
|
return false;
|
|
|
|
const wchar_t* s1 = s;
|
|
|
|
const wchar_t* prefix_token[] = { L"SEMI", L"DEMI", L"EXTRA", L"ULTRA" };
|
|
const size_t prefix_count = sizeof(prefix_token) / sizeof(prefix_token[0]);
|
|
unsigned int prefix_rc[prefix_count] = { 1, 1, 2, 2 };
|
|
const unsigned int prefix_id = ParseToken(s1, prefix_count, prefix_token, prefix_rc, 0);
|
|
|
|
if ( prefix_id > 0 )
|
|
SkipSeparator(true,s1);
|
|
|
|
const unsigned int medium_rc = (0 != prefix_id) ? 0 : 3;
|
|
const unsigned int normal_rc = (0 != prefix_id) ? 0 : 4;
|
|
|
|
const wchar_t* name_token[] = { L"THIN", L"LIGHT", L"NORMAL", L"REGULAR", L"MEDIUM", L"BOLD", L"HEAVY", L"BLACK" };
|
|
const size_t name_count = sizeof(name_token) / sizeof(name_token[0]);
|
|
unsigned int name_rc[name_count] = { 1, 2, normal_rc, normal_rc, medium_rc, 5, 6, 6 };
|
|
const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0);
|
|
if (0 == name_id)
|
|
return false;
|
|
|
|
bool rc = false;
|
|
switch (name_id)
|
|
{
|
|
case 1: // Thin
|
|
font_weight = ON_Font::Weight::Thin;
|
|
rc = true;
|
|
break;
|
|
|
|
case 2: // Light
|
|
switch (prefix_id)
|
|
{
|
|
case 0:
|
|
font_weight = ON_Font::Weight::Light;
|
|
rc = true;
|
|
case 1: // Semi
|
|
font_weight = ON_Font::Weight::Light;
|
|
rc = true;
|
|
case 2: // Ultra
|
|
font_weight = ON_Font::Weight::Ultralight;
|
|
rc = true;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 3: // Normal
|
|
font_weight = ON_Font::Weight::Normal;
|
|
rc = true;
|
|
break;
|
|
|
|
case 4: // Medium
|
|
font_weight = ON_Font::Weight::Medium;
|
|
rc = true;
|
|
break;
|
|
|
|
case 5: // Bold
|
|
switch (prefix_id)
|
|
{
|
|
case 0:
|
|
font_weight = ON_Font::Weight::Bold;
|
|
rc = true;
|
|
case 1: // Semi
|
|
font_weight = ON_Font::Weight::Semibold;
|
|
rc = true;
|
|
case 2: // Ultra
|
|
font_weight = ON_Font::Weight::Ultrabold;
|
|
rc = true;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 6: // Heavy
|
|
font_weight = ON_Font::Weight::Heavy;
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (rc)
|
|
{
|
|
s = s1;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool ParseStyle(
|
|
const wchar_t*& s,
|
|
ON_Font::Style& font_style
|
|
)
|
|
{
|
|
if (false == IsAtoZ(s))
|
|
return false;
|
|
|
|
const wchar_t* s1 = s;
|
|
|
|
const wchar_t* name_token[] = { L"UPRIGHT", L"ROMAN", L"ITALIC", L"OBLIQUE" };
|
|
const size_t name_count = sizeof(name_token) / sizeof(name_token[0]);
|
|
unsigned int name_rc[name_count] = { 1,1,2,3 };
|
|
const unsigned int name_id = ParseToken(s1, name_count, name_token, name_rc, 0);
|
|
if (0 == name_id)
|
|
return false;
|
|
|
|
bool rc = false;
|
|
switch (name_id)
|
|
{
|
|
case 1: // Upright
|
|
font_style = ON_Font::Style::Upright;
|
|
rc = true;
|
|
break;
|
|
|
|
case 2: // Italic
|
|
font_style = ON_Font::Style::Italic;
|
|
rc = true;
|
|
break;
|
|
|
|
case 3: // Oblique
|
|
font_style = ON_Font::Style::Oblique;
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (rc)
|
|
{
|
|
s = s1;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ON_Font::SetFromPostScriptName(
|
|
const wchar_t* postscript_font_name
|
|
)
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED)
|
|
return false;
|
|
|
|
// Save name in local memory before clearing this
|
|
ON_wString local_postscript_font_name(postscript_font_name);
|
|
local_postscript_font_name.TrimLeftAndRight();
|
|
postscript_font_name = static_cast<const wchar_t*>(local_postscript_font_name);
|
|
|
|
*this = ON_Font::Unset;
|
|
|
|
if (nullptr == postscript_font_name || 0 == postscript_font_name[0])
|
|
return false;
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
// TODO: Get Windows IDWriteFont with matching post_script_name
|
|
|
|
#elif defined (ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
|
|
// If the current computer has the same font, this will return the
|
|
// highest fidelity match.
|
|
// NOTE WELL: The NSFont API fails for some valid PostScript names.
|
|
// For example, the some of the 14 or so faces Helvetica Neue
|
|
// are not round tripped using the NSFont by name creation and
|
|
// are round tripped by using the CTFont API.
|
|
CTFontRef apple_font = ON_Font::AppleCTFont(postscript_font_name,0.0);
|
|
if (nullptr != apple_font)
|
|
{
|
|
return SetFromAppleCTFont(apple_font,true);
|
|
}
|
|
#endif
|
|
|
|
return SetFromFontDescription(nullptr, postscript_font_name);
|
|
}
|
|
|
|
bool ON_Font::SetFromFontDescription(
|
|
const wchar_t* font_description
|
|
)
|
|
{
|
|
const wchar_t* postscript_font_name = nullptr;
|
|
return SetFromFontDescription(
|
|
font_description,
|
|
postscript_font_name
|
|
);
|
|
}
|
|
|
|
bool ON_Font::SetFromFontDescription(
|
|
const wchar_t* font_description,
|
|
const wchar_t* postscript_name
|
|
)
|
|
{
|
|
ON_wString local_font_description(font_description);
|
|
local_font_description.TrimLeftAndRight();
|
|
font_description = static_cast<const wchar_t*>(local_font_description);
|
|
|
|
ON_wString local_postscript_name(postscript_name);
|
|
local_postscript_name.TrimLeftAndRight();
|
|
postscript_name = static_cast<const wchar_t*>(local_postscript_name);
|
|
|
|
if (nullptr == font_description || font_description[0] <= ON_wString::Space)
|
|
{
|
|
font_description = local_postscript_name;
|
|
local_font_description = local_postscript_name;
|
|
if (nullptr == font_description || font_description[0] <= ON_wString::Space)
|
|
return false;
|
|
}
|
|
|
|
// As names are discovered that do not work in the code below,
|
|
// add a special case here. These are typically fonts that have
|
|
// words like Upright, Italic, Oblique, Regular, Semi, Demi, Extra, Ultra, Medium
|
|
// Black, Heavy, ... as part of the face name and those words must not
|
|
// be parsed as possible weight, style or stretch characteristics applied
|
|
// to a root face name.
|
|
const wchar_t* special_cases[] = {
|
|
// Apple's "Times New Roman" and "Times New Roman Bold Italic" is an example of why
|
|
// special cases are required. The face name is "Times New Roman" and "Roman" no
|
|
// longer indicates an upright style.
|
|
// However, Apple's "Avenir Roman" and "Avenir Oblique" fonts provide an example
|
|
// where "Roman" is style and "Avenir" is a face name. The default parsing
|
|
// handles Avenir and any other font names that are using Roman as a style.
|
|
L"Times New Roman",
|
|
|
|
// Put new special case names above this nullptr which terminates the special_cases[] list.
|
|
nullptr
|
|
};
|
|
|
|
// NOTE WELL:
|
|
// It is important that local_face_name be created from a pointer
|
|
// so that it's string buffer is not shared with other ON_wStrings.
|
|
// wchar_t values in local_face_name are modifed via const_cast<> below.
|
|
ON_wString local_face_name = static_cast<const wchar_t*>(font_description);
|
|
const wchar_t* characteristics = nullptr;
|
|
int face_name_length = local_face_name.Length();
|
|
for (int i = 0; nullptr != special_cases[i]; i++)
|
|
{
|
|
const int special_case_length = ON_wString::Length(special_cases[i]);
|
|
if (special_case_length > face_name_length)
|
|
continue;
|
|
|
|
if (special_case_length < face_name_length && local_face_name[special_case_length] > ON_wString::Space )
|
|
continue;
|
|
|
|
if (false == ON_wString::EqualOrdinal(special_cases[i], special_case_length, font_description, special_case_length, true))
|
|
continue;
|
|
|
|
characteristics = static_cast<const wchar_t*>(local_face_name) + special_case_length;
|
|
break;
|
|
}
|
|
|
|
if (nullptr == characteristics)
|
|
characteristics = static_cast<const wchar_t*>(local_face_name) + 1;
|
|
|
|
const wchar_t x = (wchar_t)1;
|
|
|
|
ON_Font::Weight font_weight = ON_Font::Default.m_font_weight;
|
|
for (wchar_t* s0 = const_cast<wchar_t*>(characteristics); 0 != *s0; s0++)
|
|
{
|
|
const wchar_t* s1 = s0;
|
|
if (ParseWeight(s1, font_weight))
|
|
{
|
|
while ( s0 < s1 )
|
|
*s0++ = x; // NOTE - modifies local_face_name content
|
|
break;
|
|
}
|
|
}
|
|
|
|
ON_Font::Style font_style = ON_Font::Default.m_font_style;
|
|
for (wchar_t* s0 = const_cast<wchar_t*>(characteristics); 0 != *s0; s0++)
|
|
{
|
|
const wchar_t* s1 = s0;
|
|
if (ParseStyle(s1, font_style))
|
|
{
|
|
while ( s0 < s1 )
|
|
*s0++ = x; // NOTE - modifies local_face_name content
|
|
break;
|
|
}
|
|
}
|
|
|
|
ON_Font::Stretch font_stretch = ON_Font::Default.m_font_stretch;
|
|
for (wchar_t* s0 = const_cast<wchar_t*>(characteristics); 0 != *s0; s0++)
|
|
{
|
|
const wchar_t* s1 = s0;
|
|
if (ParseStretch(s1, font_stretch))
|
|
{
|
|
while ( s0 < s1 )
|
|
*s0++ = x; // NOTE - modifies local_face_name content
|
|
break;
|
|
}
|
|
}
|
|
|
|
face_name_length = 0;
|
|
for (const wchar_t* s = static_cast<const wchar_t*>(local_face_name); 0 != *s; s++)
|
|
{
|
|
if (*s >= ON_wString::Space)
|
|
face_name_length++;
|
|
else
|
|
{
|
|
while (*s > 0 && *s <= ON_wString::Space)
|
|
s++;
|
|
break;
|
|
}
|
|
}
|
|
local_face_name.SetLength(face_name_length);
|
|
local_face_name.TrimLeftAndRight();
|
|
const wchar_t* face_name = static_cast<const wchar_t*>(local_face_name);
|
|
|
|
bool rc = SetFontCharacteristics(
|
|
face_name,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
false,
|
|
false,
|
|
ON_FontMetrics::DefaultLineFeedRatio,
|
|
ON_Font::WindowsConstants::logfont_default_charset
|
|
);
|
|
|
|
if (rc)
|
|
{
|
|
m_loc_postscript_name = local_postscript_name;
|
|
m_en_postscript_name = m_loc_postscript_name;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_Object overrides
|
|
|
|
bool ON_Font::IsValid(ON_TextLog* text_log) const
|
|
{
|
|
return (
|
|
(FamilyName().IsNotEmpty() || WindowsLogfontName().IsNotEmpty() || PostScriptName().IsNotEmpty())
|
|
&& ON_Font::Weight::Unset != m_font_weight
|
|
&& ON_Font::Style::Unset != m_font_style
|
|
&& ON_Font::Stretch::Unset != m_font_stretch
|
|
);
|
|
}
|
|
|
|
void ON_Font::Dump(ON_TextLog& dump) const
|
|
{
|
|
const bool bTextHash = dump.IsTextHash();;
|
|
|
|
ON_wString s;
|
|
|
|
if (bTextHash)
|
|
{
|
|
dump.Print(L"Font family name = ...\n");
|
|
dump.PushIndent();
|
|
dump.Print(
|
|
L"The font face name and other properties depend on the platform \n"
|
|
"or the fonts installed on a particular computer. Information like \n"
|
|
"this is omitted from dumps used for SHA-1 hash caluculations so \n"
|
|
"hash values from different platforms and computers can be compared. \n"
|
|
);
|
|
}
|
|
else
|
|
{
|
|
const ON_wString description = Description();
|
|
dump.Print(L"Font description = \"%ls\"\n", static_cast<const wchar_t*>( description ));
|
|
dump.PushIndent();
|
|
|
|
const ON_wString family_name = FamilyName();
|
|
dump.Print(L"Family name = \"%ls\"\n", static_cast<const wchar_t*>(family_name ));
|
|
const ON_wString en_family_name = FamilyName(ON_Font::NameLocale::English);
|
|
if ( en_family_name.IsNotEmpty() && en_family_name != family_name)
|
|
dump.Print(L"Family name (English)= \"%ls\"\n", static_cast<const wchar_t*>(en_family_name ));
|
|
|
|
const ON_wString face_name = FaceName();
|
|
dump.Print(L"Face name = \"%ls\"\n", static_cast<const wchar_t*>( face_name ));
|
|
const ON_wString en_face_name = FaceName(ON_Font::NameLocale::English);
|
|
if ( en_face_name.IsNotEmpty() && en_face_name != face_name)
|
|
dump.Print(L"Face name (English)= \"%ls\"\n", static_cast<const wchar_t*>(en_face_name ));
|
|
|
|
const ON_wString postscript_name = PostScriptName();
|
|
dump.Print(L"PostScript name = \"%ls\"\n", static_cast<const wchar_t*>(postscript_name));
|
|
const ON_wString en_postscript_name = PostScriptName(ON_Font::NameLocale::English);
|
|
if ( en_postscript_name.IsNotEmpty() && en_postscript_name != postscript_name)
|
|
dump.Print(L"PostScript name (English)= \"%ls\"\n", static_cast<const wchar_t*>(en_postscript_name ));
|
|
|
|
const ON_wString windows_logfont_name = WindowsLogfontName();
|
|
dump.Print(L"Windows LOGFONT name = \"%ls\"\n", static_cast<const wchar_t*>(windows_logfont_name));
|
|
const ON_wString en_windows_logfont_name = WindowsLogfontName(ON_Font::NameLocale::English);
|
|
if ( en_windows_logfont_name.IsNotEmpty() && en_windows_logfont_name != windows_logfont_name)
|
|
dump.Print(L"Windows LOGFONT name (English)= \"%ls\"\n", static_cast<const wchar_t*>(en_windows_logfont_name ));
|
|
|
|
const ON_wString rich_text_name = ON_Font::RichTextFontName(this, false);
|
|
dump.Print(L"Rich text name = \"%ls\"\n", static_cast<const wchar_t*>(rich_text_name));
|
|
|
|
s = ON_wString::EmptyString;
|
|
switch (this->FontOrigin())
|
|
{
|
|
case ON_Font::Origin::Unset: break;
|
|
case ON_Font::Origin::Unknown: s = L"Unknown"; break;
|
|
case ON_Font::Origin::WindowsFont: s = L"Windows Font"; break;
|
|
case ON_Font::Origin::AppleFont: s = L"Apple Font"; break;
|
|
default: s = ON_wString::FormatToString(L"%u", static_cast<unsigned int>(this->FontOrigin())); break;
|
|
};
|
|
|
|
if (s.IsNotEmpty())
|
|
{
|
|
dump.Print(L"Origin = %ls\n", static_cast<const wchar_t*>(s));
|
|
}
|
|
}
|
|
|
|
if (ON_Font::IsValidPointSize(m_point_size))
|
|
{
|
|
dump.Print(L"PointSize = %g\n", m_point_size);
|
|
}
|
|
else
|
|
{
|
|
dump.Print(L"PointSize = annotation default\n");
|
|
}
|
|
|
|
bool bInQuartet = false;
|
|
for(;;)
|
|
{
|
|
const ON_FontFaceQuartet q = this->InstalledFontQuartet();
|
|
if (q.QuartetName().IsEmpty())
|
|
break;
|
|
|
|
const bool bQuartetItalic = (ON_Font::Style::Italic == m_font_style || ON_Font::Style::Oblique == m_font_style);
|
|
const ON_Font* regular = bQuartetItalic ? q.ItalicFace() : q.RegularFace();
|
|
const ON_Font* bold = bQuartetItalic ? q.BoldItalicFace() : q.BoldFace();
|
|
const ON_Font* quartet_face;
|
|
if (nullptr != regular && FontWeight() == regular->FontWeight())
|
|
quartet_face = regular;
|
|
else if (nullptr != bold && FontWeight() == bold->FontWeight())
|
|
quartet_face = bold;
|
|
else
|
|
quartet_face = nullptr;
|
|
if (nullptr == quartet_face)
|
|
break;
|
|
|
|
bInQuartet = true;
|
|
if (quartet_face == q.RegularFace())
|
|
dump.Print("Quartet: %ls (Regular member)\n", static_cast<const wchar_t*>(q.QuartetName()));
|
|
else if (quartet_face == q.BoldFace())
|
|
dump.Print("Quartet: %ls (Bold member)\n", static_cast<const wchar_t*>(q.QuartetName()));
|
|
else if (quartet_face == q.ItalicFace())
|
|
dump.Print("Quartet: %ls (Italic member)\n", static_cast<const wchar_t*>(q.QuartetName()));
|
|
else if (quartet_face == q.BoldItalicFace())
|
|
dump.Print("Quartet: %ls (Bold-Italic member)\n", static_cast<const wchar_t*>(q.QuartetName()));
|
|
else
|
|
bInQuartet = false;
|
|
|
|
break;
|
|
}
|
|
|
|
if (false == bInQuartet)
|
|
dump.Print("Quartet: None\n");
|
|
|
|
s = ON_Font::WeightToWideString(FontWeight());
|
|
if( s.IsEmpty())
|
|
s = ON_wString::FormatToString(L"%u", static_cast<unsigned int>(this->FontWeight()));
|
|
if (SimulatedWeight())
|
|
s += L" (simulated)";
|
|
dump.Print(L"Weight = %ls\n", static_cast<const wchar_t*>(s));
|
|
|
|
s = ON_Font::StretchToWideString(FontStretch());
|
|
if( s.IsEmpty())
|
|
s = ON_wString::FormatToString(L"%u", static_cast<unsigned int>(this->FontStretch()));
|
|
if (SimulatedStretch())
|
|
s += L" (simulated)";
|
|
dump.Print(L"Stretch = %ls\n", static_cast<const wchar_t*>(s));
|
|
|
|
s = ON_Font::StyleToWideString(FontStyle());
|
|
if( s.IsEmpty())
|
|
s = ON_wString::FormatToString(L"%u", static_cast<unsigned int>(this->FontStyle()));
|
|
if (SimulatedStyle())
|
|
s += L" (simulated)";
|
|
dump.Print(L"Style = %ls\n", static_cast<const wchar_t*>(s));
|
|
|
|
dump.Print(L"Underlined = %ls\n", this->IsUnderlined() ? L"true" : L"false");
|
|
|
|
dump.Print(L"Strikethrough = %ls\n", this->IsStrikethrough() ? L"true" : L"false");
|
|
|
|
dump.Print(L"Symbol font = %ls\n", this->IsSymbolFont() ? L"true" : L"false");
|
|
|
|
const ON_OutlineFigure::Type font_figure_type = this->OutlineFigureType();
|
|
if (ON_OutlineFigure::Type::SingleStroke == font_figure_type)
|
|
dump.Print(L"Engraving font = Single-stroke\n");
|
|
else if (ON_OutlineFigure::Type::DoubleStroke == font_figure_type)
|
|
dump.Print(L"Engraving font = Double-stroke\n");
|
|
|
|
if (
|
|
ON_PANOSE1::FamilyKind::Any != m_panose1.PANOSE1FamilyKind()
|
|
&& ON_PANOSE1::FamilyKind::NoFit != m_panose1.PANOSE1FamilyKind()
|
|
)
|
|
{
|
|
// PANOSE1 may contains some useful information
|
|
m_panose1.Dump(dump);
|
|
}
|
|
|
|
|
|
if (false == bTextHash)
|
|
{
|
|
const ON_wString characteristics_hash = FontCharacteristicsHash().ToString(true);
|
|
dump.Print(L"Font characteristics SHA-1 hash = %ls\n", static_cast<const wchar_t*>(characteristics_hash));
|
|
|
|
unsigned int runtime_sn = RuntimeSerialNumber();
|
|
if (runtime_sn >= 1)
|
|
{
|
|
if (this == &ON_Font::Default)
|
|
dump.Print(L"Managed font <%u> (ON_Font::Default)\n", runtime_sn);
|
|
else if (IsInstalledFont())
|
|
{
|
|
dump.Print(L"Managed font <%u> (face installed on device)\n", runtime_sn);
|
|
}
|
|
else
|
|
{
|
|
dump.Print(L"Managed font <%u> (face not installed on device)\n", runtime_sn);
|
|
}
|
|
|
|
for (int fm_pass = 0; fm_pass < 1; fm_pass++)
|
|
{
|
|
const bool bNormalized = (1 == fm_pass);
|
|
const ON_FontMetrics fm
|
|
= bNormalized
|
|
? FontMetrics()
|
|
: FontUnitFontMetrics();
|
|
dump.Print(bNormalized ? L"Normalized font metrics:" : L"Font metrics:");
|
|
if (fm.IsSet())
|
|
{
|
|
dump.PrintNewLine();
|
|
dump.PushIndent();
|
|
fm.Dump(dump);
|
|
dump.PopIndent();
|
|
}
|
|
else
|
|
{
|
|
dump.Print(L" Unset\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
{
|
|
// LOGFONT details
|
|
const LOGFONT logfont = this->WindowsLogFont(0, nullptr);
|
|
//TEXTMETRIC logfont_tm;
|
|
//bool bHaveTextMetric
|
|
// = logfont.lfHeight > 0
|
|
// && ON_Font::GetWindowsTextMetrics(MM_TEXT, nullptr, logfont, logfont_tm);
|
|
//if ( bHaveTextMetric )
|
|
// dump.Print(L"Windows LOGFONT and TEXTMETRIC:\n");
|
|
//else
|
|
// dump.Print(L"Windows LOGFONT:\n");
|
|
//ON_TextLogIndent indent_logfont(dump);
|
|
ON_Font::DumpLogfont(&logfont, dump);
|
|
//if (bHaveTextMetric)
|
|
//{
|
|
// ON_Font::DumpTextMetric(&logfont_tm, dump);
|
|
//}
|
|
}
|
|
#elif defined (ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
|
|
{
|
|
CTFontRef apple_font = AppleCTFont();
|
|
dump.Print(L"Apple Core Text font:\n");
|
|
dump.PushIndent();
|
|
ON_Font::DumpCTFont(apple_font, dump);
|
|
dump.PopIndent();
|
|
}
|
|
#elif defined(OPENNURBS_FREETYPE_SUPPORT)
|
|
// Look in opennurbs_system_rumtime.h for the correct place to define OPENNURBS_FREETYPE_SUPPORT.
|
|
// Do NOT define OPENNURBS_FREETYPE_SUPPORT here or in your project setting ("makefile").
|
|
if (runtime_sn >= 1)
|
|
{
|
|
// Free Type font details
|
|
DumpFreeType(dump);
|
|
}
|
|
#endif
|
|
}
|
|
dump.PopIndent();
|
|
}
|
|
|
|
bool ON_FontMetrics::IsUnset() const
|
|
{
|
|
return (
|
|
0 == m_UPM
|
|
&& 0 == m_ascent
|
|
&& 0 == m_descent
|
|
&& 0 == m_line_space
|
|
&& 0 == m_ascent_of_capital
|
|
&& 0 == m_ascent_of_x
|
|
&& 0 == m_strikeout_thickness
|
|
&& 0 == m_strikeout_position
|
|
&& 0 == m_underscore_thickness
|
|
&& 0 == m_underscore_position
|
|
);
|
|
}
|
|
|
|
bool ON_FontMetrics::IsSet() const
|
|
{
|
|
return IsUnset() ? false : true;
|
|
}
|
|
|
|
void ON_FontMetrics::Dump(class ON_TextLog& text_log) const
|
|
{
|
|
text_log.Print("Units per EM = %d\n", m_UPM);
|
|
text_log.Print("Ascent = %d\n", m_ascent);
|
|
text_log.Print("Descent = %d\n", m_descent);
|
|
text_log.Print("Distance between baselines = %d\n", m_line_space);
|
|
text_log.Print("Ascent of capital = %d\n", AscentOfCapital());
|
|
text_log.Print("Ascent of x = %d\n", AscentOfx());
|
|
text_log.Print("Strikeout thickness = %d\n", m_strikeout_thickness);
|
|
text_log.Print("Strikeout position = %d\n", m_strikeout_position);
|
|
text_log.Print("Underscore thickness = %d\n", m_underscore_thickness);
|
|
text_log.Print("Underscore position = %d\n", m_underscore_position);
|
|
}
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
|
|
#define INTERNAL_CASE_STRING(c) case c: s = #c; break
|
|
|
|
static const ON_String Internal_WindowsLogFontCharSet(
|
|
BYTE charSet
|
|
)
|
|
{
|
|
ON_String s;
|
|
switch (charSet)
|
|
{
|
|
INTERNAL_CASE_STRING(ANSI_CHARSET);
|
|
INTERNAL_CASE_STRING(DEFAULT_CHARSET);
|
|
INTERNAL_CASE_STRING(SYMBOL_CHARSET);
|
|
INTERNAL_CASE_STRING(SHIFTJIS_CHARSET);
|
|
INTERNAL_CASE_STRING(HANGEUL_CHARSET);
|
|
INTERNAL_CASE_STRING(GB2312_CHARSET);
|
|
INTERNAL_CASE_STRING(CHINESEBIG5_CHARSET);
|
|
INTERNAL_CASE_STRING(OEM_CHARSET);
|
|
#if(WINVER >= 0x0400)
|
|
INTERNAL_CASE_STRING(JOHAB_CHARSET);
|
|
INTERNAL_CASE_STRING(HEBREW_CHARSET);
|
|
INTERNAL_CASE_STRING(ARABIC_CHARSET);
|
|
INTERNAL_CASE_STRING(GREEK_CHARSET);
|
|
INTERNAL_CASE_STRING(TURKISH_CHARSET);
|
|
INTERNAL_CASE_STRING(VIETNAMESE_CHARSET);
|
|
INTERNAL_CASE_STRING(THAI_CHARSET);
|
|
INTERNAL_CASE_STRING(EASTEUROPE_CHARSET);
|
|
INTERNAL_CASE_STRING(RUSSIAN_CHARSET);
|
|
INTERNAL_CASE_STRING(MAC_CHARSET);
|
|
INTERNAL_CASE_STRING(BALTIC_CHARSET);
|
|
#endif
|
|
default:
|
|
s = ON_String::FormatToString("%u", (unsigned char)charSet);
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
static const ON_String Internal_WindowsLogFontFamily(
|
|
BYTE pitchAndFamily
|
|
)
|
|
{
|
|
ON_String s;
|
|
const BYTE family = (0xF0 & pitchAndFamily);
|
|
switch (family)
|
|
{
|
|
INTERNAL_CASE_STRING(FF_DONTCARE);
|
|
INTERNAL_CASE_STRING(FF_ROMAN);
|
|
INTERNAL_CASE_STRING(FF_SWISS);
|
|
INTERNAL_CASE_STRING(FF_MODERN);
|
|
INTERNAL_CASE_STRING(FF_SCRIPT);
|
|
INTERNAL_CASE_STRING(FF_DECORATIVE);
|
|
default:
|
|
s = ON_String::FormatToString("%02X", family);
|
|
break;
|
|
};
|
|
return s;
|
|
}
|
|
|
|
static const ON_String Internal_WindowsLogFontQuality(
|
|
const BYTE lfQuality
|
|
)
|
|
{
|
|
ON_String s;
|
|
switch (lfQuality)
|
|
{
|
|
INTERNAL_CASE_STRING(DEFAULT_QUALITY);
|
|
INTERNAL_CASE_STRING(DRAFT_QUALITY);
|
|
INTERNAL_CASE_STRING(PROOF_QUALITY);
|
|
#if(WINVER >= 0x0400)
|
|
INTERNAL_CASE_STRING(NONANTIALIASED_QUALITY);
|
|
INTERNAL_CASE_STRING(ANTIALIASED_QUALITY);
|
|
#endif /* WINVER >= 0x0400 */
|
|
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WINXP)
|
|
INTERNAL_CASE_STRING(CLEARTYPE_QUALITY);
|
|
INTERNAL_CASE_STRING(CLEARTYPE_NATURAL_QUALITY);
|
|
#endif
|
|
default:
|
|
s = ON_String::FormatToString("%u", (unsigned int)lfQuality);
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static const ON_String Internal_WindowsLogFontOutPrecision(
|
|
const BYTE lfOutPrecision
|
|
)
|
|
{
|
|
ON_String s;
|
|
switch (lfOutPrecision)
|
|
{
|
|
INTERNAL_CASE_STRING(OUT_DEFAULT_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_STRING_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_CHARACTER_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_STROKE_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_TT_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_DEVICE_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_RASTER_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_TT_ONLY_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_OUTLINE_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_SCREEN_OUTLINE_PRECIS);
|
|
INTERNAL_CASE_STRING(OUT_PS_ONLY_PRECIS);
|
|
default:
|
|
s = ON_String::FormatToString("%u", (unsigned int)lfOutPrecision);
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static const ON_String Internal_WindowsLogFontClipPrecision(
|
|
const BYTE lfClipPrecision
|
|
)
|
|
{
|
|
ON_String s;
|
|
switch (lfClipPrecision)
|
|
{
|
|
INTERNAL_CASE_STRING(CLIP_DEFAULT_PRECIS);
|
|
INTERNAL_CASE_STRING(CLIP_CHARACTER_PRECIS);
|
|
INTERNAL_CASE_STRING(CLIP_STROKE_PRECIS);
|
|
INTERNAL_CASE_STRING(CLIP_MASK);
|
|
INTERNAL_CASE_STRING(CLIP_LH_ANGLES);
|
|
INTERNAL_CASE_STRING(CLIP_TT_ALWAYS);
|
|
#if (_WIN32_WINNT >= _WIN32_WINNT_LONGHORN)
|
|
INTERNAL_CASE_STRING(CLIP_DFA_DISABLE);
|
|
#endif
|
|
INTERNAL_CASE_STRING(CLIP_EMBEDDED);
|
|
default:
|
|
s = ON_String::FormatToString("%u", (unsigned int)lfClipPrecision);
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void ON_Font::DumpLogfont(
|
|
const LOGFONT* logfont,
|
|
ON_TextLog& text_log
|
|
)
|
|
{
|
|
if (nullptr == logfont)
|
|
{
|
|
text_log.Print(L"LOGFONT = nullptr\n");
|
|
return;
|
|
}
|
|
|
|
text_log.Print(L"LOGFONT\n");
|
|
text_log.PushIndent();
|
|
ON_wString sFaceName = logfont->lfFaceName;
|
|
|
|
text_log.Print(L"lfFaceName = %ls\n",static_cast<const wchar_t*>(sFaceName));
|
|
text_log.Print("lfHeight = %d\n", logfont->lfHeight);
|
|
text_log.Print("lfWidth = %d\n", logfont->lfWidth);
|
|
text_log.Print("lfEscapement = %d.%d degrees\n", (logfont->lfEscapement)/10, (logfont->lfEscapement)%10);
|
|
text_log.Print("lfOrientation = %d.%d degrees\n", (logfont->lfOrientation)/10, (logfont->lfOrientation)%10);
|
|
|
|
ON_String s;
|
|
|
|
switch (logfont->lfWeight)
|
|
{
|
|
case FW_DONTCARE: s = L"FW_DONTCARE"; break;
|
|
case FW_THIN: s = "FW_THIN"; break;
|
|
case FW_EXTRALIGHT: s = "FW_EXTRALIGHT"; break; // = FW_ULTRALIGHT
|
|
case FW_LIGHT: s = "FW_LIGHT"; break;
|
|
case FW_NORMAL: s = "FW_NORMAL"; break; // = FW_REGULAR
|
|
case FW_MEDIUM: s = "FW_MEDIUM"; break;
|
|
case FW_SEMIBOLD: s = "FW_SEMIBOLD"; break; // = FW_DEMIBOLD
|
|
case FW_BOLD: s = "FW_BOLD"; break;
|
|
case FW_EXTRABOLD: s = "FW_EXTRABOLD"; break; // = FW_ULTRABOLD
|
|
case FW_HEAVY: s = "FW_HEAVY"; break; // = FW_BLACK
|
|
default: s = ON_String::EmptyString; break;
|
|
}
|
|
if (s.IsNotEmpty())
|
|
s += " = ";
|
|
s += ON_String::FormatToString("%d", logfont->lfWeight);
|
|
text_log.Print("lfWeight = %s\n", static_cast<const char*>(s));
|
|
|
|
|
|
text_log.Print("lfItalic = %s\n", logfont->lfItalic ? "true" : "false");
|
|
text_log.Print("lfUnderline = %s\n", logfont->lfUnderline ? "true" : "false");
|
|
text_log.Print("lfStrikeOut = %s\n", logfont->lfStrikeOut ? "true" : "false");
|
|
|
|
s = Internal_WindowsLogFontCharSet(logfont->lfCharSet);
|
|
text_log.Print("lfCharSet = %s\n", static_cast<const char*>(s));
|
|
|
|
s = Internal_WindowsLogFontOutPrecision(logfont->lfOutPrecision);
|
|
text_log.Print("lfOutPrecision = %s\n", static_cast<const char*>(s));
|
|
|
|
s = Internal_WindowsLogFontClipPrecision(logfont->lfClipPrecision);
|
|
text_log.Print("lfClipPrecision = %s\n", static_cast<const char*>(s));
|
|
|
|
s = Internal_WindowsLogFontQuality(logfont->lfQuality);
|
|
text_log.Print("lfQuality = %s\n", static_cast<const char*>(s));
|
|
|
|
const unsigned int pitch = (logfont->lfPitchAndFamily & 0x03);
|
|
const unsigned int family = (logfont->lfPitchAndFamily & 0xF);
|
|
switch (pitch)
|
|
{
|
|
case DEFAULT_PITCH: s = "DEFAULT_PITCH"; break;
|
|
case FIXED_PITCH: s = "FIXED_PITCH"; break;
|
|
case VARIABLE_PITCH: s = "VARIABLE_PITCH"; break;
|
|
default: s = ON_String::FormatToString("0x%02x", pitch);
|
|
};
|
|
s += " | ";
|
|
s += Internal_WindowsLogFontFamily(logfont->lfPitchAndFamily);
|
|
if (s.IsNotEmpty())
|
|
s += L" = ";
|
|
s += ON_wString::FormatToString(L"0x%02x", (unsigned int)logfont->lfPitchAndFamily);
|
|
text_log.Print("lfPitchAndFamily = %s\n", static_cast<const char*>(s));
|
|
text_log.PopIndent();
|
|
}
|
|
|
|
void ON_Font::DumpTextMetric(
|
|
const TEXTMETRIC* tm,
|
|
ON_TextLog& text_log
|
|
)
|
|
{
|
|
if (nullptr == tm)
|
|
{
|
|
text_log.Print("TEXTMETRIC = nullptr\n");
|
|
return;
|
|
}
|
|
|
|
text_log.Print("TEXTMETRIC\n");
|
|
text_log.PushIndent();
|
|
|
|
text_log.Print("tmHeight = %d\n",tm->tmHeight);
|
|
text_log.Print("tmAscent = %d\n",tm->tmAscent);
|
|
text_log.Print("tmDescent = %d\n",tm->tmDescent);
|
|
|
|
text_log.Print("tmInternalLeading = %d\n",tm->tmInternalLeading);
|
|
text_log.Print("tmExternalLeading = %d\n",tm->tmExternalLeading);
|
|
|
|
text_log.Print("tmAveCharWidth = %d\n",tm->tmAveCharWidth);
|
|
text_log.Print("tmMaxCharWidth = %d\n",tm->tmMaxCharWidth);
|
|
|
|
text_log.Print("tmWeight = %d\n",tm->tmWeight);
|
|
text_log.Print("tmOverhang = %d\n",tm->tmOverhang);
|
|
|
|
text_log.Print("tmDigitizedAspectX = %d\n",tm->tmDigitizedAspectX);
|
|
text_log.Print("tmDigitizedAspectY = %d\n",tm->tmDigitizedAspectY);
|
|
|
|
text_log.Print(L"tmFirstChar = U+%04X\n",(ON__UINT32)tm->tmFirstChar);
|
|
text_log.Print(L"tmLastChar = U+%04X\n",(ON__UINT32)tm->tmLastChar);
|
|
text_log.Print(L"tmDefaultChar = U+%04X\n",(ON__UINT32)tm->tmDefaultChar);
|
|
text_log.Print(L"tmBreakChar = U+%04X\n",(ON__UINT32)tm->tmBreakChar);
|
|
|
|
text_log.Print("tmItalic = %s\n",tm->tmItalic?"true":"false");
|
|
text_log.Print("tmUnderlined = %s\n",tm->tmUnderlined?"true":"false");
|
|
text_log.Print("tmStruckOut = %s\n",tm->tmStruckOut?"true":"false");
|
|
|
|
ON_String s;
|
|
|
|
const BYTE pitch = (0x0F & tm->tmPitchAndFamily);
|
|
if (0 != (TMPF_FIXED_PITCH & pitch))
|
|
{
|
|
if (s.IsNotEmpty())
|
|
s += " | ";
|
|
s += "TMPF_FIXED_PITCH";
|
|
}
|
|
if (0 != (TMPF_VECTOR & pitch))
|
|
{
|
|
if (s.IsNotEmpty())
|
|
s += " | ";
|
|
s += "TMPF_VECTOR";
|
|
}
|
|
if (0 != (TMPF_TRUETYPE & pitch))
|
|
{
|
|
if (s.IsNotEmpty())
|
|
s += " | ";
|
|
s += "TMPF_TRUETYPE";
|
|
}
|
|
if (0 != (TMPF_DEVICE & pitch))
|
|
{
|
|
if (s.IsNotEmpty())
|
|
s += " | ";
|
|
s += "TMPF_DEVICE";
|
|
}
|
|
|
|
if (s.IsNotEmpty())
|
|
s += " | ";
|
|
s += Internal_WindowsLogFontFamily(tm->tmPitchAndFamily);
|
|
if (s.IsNotEmpty())
|
|
s += " = ";
|
|
s += ON_wString::FormatToString(L"0x%02X", (unsigned int)tm->tmPitchAndFamily);
|
|
text_log.Print("tmPitchAndFamily = %s\n", static_cast<const char*>(s));
|
|
|
|
|
|
s = Internal_WindowsLogFontCharSet(tm->tmCharSet);
|
|
text_log.Print("tmCharSet = %s\n", static_cast<const char*>(s));
|
|
|
|
text_log.PopIndent();
|
|
}
|
|
|
|
const ON_wString ON_WindowsDWriteFontInformation::FamilyName() const
|
|
{
|
|
return m_loc_family_name.IsNotEmpty() ? m_loc_family_name : m_en_family_name;
|
|
}
|
|
|
|
const ON_wString ON_WindowsDWriteFontInformation::FaceName() const
|
|
{
|
|
return m_loc_face_name.IsNotEmpty() ? m_loc_face_name : m_en_face_name;
|
|
}
|
|
|
|
const ON_wString ON_WindowsDWriteFontInformation::PostScriptName() const
|
|
{
|
|
return m_loc_postscript_name.IsNotEmpty() ? m_loc_postscript_name : m_en_postscript_name;
|
|
}
|
|
|
|
const ON_wString ON_WindowsDWriteFontInformation::WindowsLogfontName() const
|
|
{
|
|
return m_loc_gdi_family_name.IsNotEmpty() ? m_loc_gdi_family_name : m_en_gdi_family_name;
|
|
}
|
|
|
|
int ON_WindowsDWriteFontInformation::CompareFamilyName(
|
|
const ON_WindowsDWriteFontInformation* lhs,
|
|
const ON_WindowsDWriteFontInformation* rhs)
|
|
{
|
|
if (lhs == rhs) return 0;
|
|
if (nullptr == lhs) return 1;
|
|
if (nullptr == rhs) return -1;
|
|
return ON_wString::CompareOrdinal(lhs->m_loc_family_name, rhs->m_loc_family_name, true);
|
|
}
|
|
|
|
int ON_WindowsDWriteFontInformation::CompareFamilyNameFaceNameWeightStretchStyle(
|
|
const ON_WindowsDWriteFontInformation* lhs,
|
|
const ON_WindowsDWriteFontInformation* rhs)
|
|
{
|
|
if (lhs == rhs) return 0;
|
|
if (nullptr == lhs) return 1;
|
|
if (nullptr == rhs) return -1;
|
|
int rc = ON_wString::CompareOrdinal(lhs->FamilyName(), rhs->FamilyName(), true);
|
|
if (0 != rc)
|
|
return rc;
|
|
rc = ON_wString::CompareOrdinal(lhs->FaceName(), rhs->FaceName(), true);
|
|
if (0 != rc)
|
|
return rc;
|
|
rc = lhs->m_weight - rhs->m_weight;
|
|
if (0 != rc)
|
|
return rc;
|
|
rc = lhs->m_stretch - rhs->m_stretch;
|
|
if (0 != rc)
|
|
return rc;
|
|
rc = lhs->m_style - rhs->m_style;
|
|
if (0 != rc)
|
|
return rc;
|
|
return 0;
|
|
}
|
|
|
|
int ON_WindowsDWriteFontInformation::ComparePostScriptName(
|
|
const ON_WindowsDWriteFontInformation* lhs,
|
|
const ON_WindowsDWriteFontInformation* rhs)
|
|
{
|
|
if (lhs == rhs) return 0;
|
|
if (nullptr == lhs) return 1;
|
|
if (nullptr == rhs) return -1;
|
|
return ON_wString::CompareOrdinal(lhs->PostScriptName(), rhs->PostScriptName(), true);
|
|
}
|
|
|
|
|
|
unsigned char ON_Font::WindowsLogfontCharSetFromLogfont(
|
|
const LOGFONT* logfont,
|
|
bool bValidateSymbolFont
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == logfont)
|
|
break;
|
|
if (0 == logfont->lfFaceName[0])
|
|
break;
|
|
const int logfont_capacity = (int)(sizeof(logfont->lfFaceName) / sizeof(logfont->lfFaceName[0]));
|
|
if ( ON_wString::EqualOrdinal(L"CityBlueprint", -1, logfont->lfFaceName, logfont_capacity, true))
|
|
return ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
if ( ON_wString::EqualOrdinal(L"CountryBlueprint", -1, logfont->lfFaceName, logfont_capacity, true))
|
|
return ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
if (ON_Font::WindowsConstants::logfont_symbol_charset != logfont->lfCharSet)
|
|
break;
|
|
if (bValidateSymbolFont
|
|
&& ON_Font::WindowsConstants::logfont_symbol_charset != ON_Font::WindowsLogfontCharSetFromFaceName(logfont->lfFaceName)
|
|
)
|
|
break;
|
|
return ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
}
|
|
|
|
return ON_Font::WindowsConstants::logfont_default_charset;
|
|
}
|
|
|
|
|
|
|
|
|
|
ON_Font::ON_Font(
|
|
ON_Font::FontType font_type,
|
|
const class ON_WindowsDWriteFontInformation& dwrite_font_information
|
|
)
|
|
: m_runtime_serial_number((ON_Font::FontType::ManagedFont == font_type)?++ON_Font::__runtime_serial_number_generator:0)
|
|
, m_font_type(font_type)
|
|
{
|
|
const LOGFONT logfont = dwrite_font_information.m_gdi_interop_logfont;
|
|
m_windows_logfont_weight = logfont.lfWeight;
|
|
m_font_weight = ON_Font::WeightFromWindowsLogfontWeight(m_windows_logfont_weight);
|
|
m_apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(m_font_weight);
|
|
m_point_size = 0.0;
|
|
|
|
m_font_style = (0 != logfont.lfItalic) ? ON_Font::Style::Italic : ON_Font::Style::Upright;
|
|
|
|
m_font_stretch = ON_Font::FontStretchFromDWriteStretch(dwrite_font_information.m_stretch,ON_Font::Stretch::Medium);
|
|
|
|
m_font_bUnderlined = false;
|
|
m_font_bStrikethrough = false;
|
|
|
|
m_logfont_charset = ON_Font::WindowsLogfontCharSetFromLogfont(&logfont,false);
|
|
|
|
m_font_origin = ON_Font::Origin::WindowsFont;
|
|
|
|
m_locale_name = dwrite_font_information.m_prefered_locale;
|
|
|
|
m_loc_postscript_name = dwrite_font_information.m_loc_postscript_name;
|
|
m_en_postscript_name = dwrite_font_information.m_en_postscript_name;
|
|
|
|
m_loc_family_name = dwrite_font_information.m_loc_family_name;
|
|
m_en_family_name = dwrite_font_information.m_en_family_name;
|
|
|
|
m_loc_face_name = dwrite_font_information.m_loc_face_name;
|
|
m_en_face_name = dwrite_font_information.m_en_face_name;
|
|
|
|
m_loc_windows_logfont_name = dwrite_font_information.m_loc_gdi_family_name;
|
|
m_en_windows_logfont_name = dwrite_font_information.m_en_gdi_family_name;
|
|
for(;;)
|
|
{
|
|
// For simulated fonts, the DWrite GDI family name is sometimes too long for a valid logfont name.
|
|
if (
|
|
m_loc_windows_logfont_name.IsNotEmpty() && m_loc_windows_logfont_name.Length() <= 32
|
|
&& m_en_windows_logfont_name.IsNotEmpty() && m_en_windows_logfont_name.Length() <= 32
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
if (0 == dwrite_font_information.m_gdi_interop_logfont.lfFaceName[0])
|
|
break;
|
|
// logfont_name[] = logfont lfFaceName we are cetain is null terminated.
|
|
wchar_t logfont_name[1 + (sizeof(dwrite_font_information.m_gdi_interop_logfont.lfFaceName) / sizeof(dwrite_font_information.m_gdi_interop_logfont.lfFaceName[0]))];
|
|
const size_t logfont_name_capacity = (sizeof(logfont_name)/sizeof(logfont_name[0])) - 1;
|
|
memcpy(logfont_name, dwrite_font_information.m_gdi_interop_logfont.lfFaceName, logfont_name_capacity*sizeof(logfont_name[0]));
|
|
logfont_name[logfont_name_capacity] = 0;
|
|
if (m_en_windows_logfont_name.IsEmpty() || m_en_windows_logfont_name.Length() > 32)
|
|
m_en_windows_logfont_name = logfont_name;
|
|
if (m_loc_windows_logfont_name.IsEmpty() || m_loc_windows_logfont_name.Length() > 32)
|
|
m_loc_windows_logfont_name = logfont_name;
|
|
break;
|
|
}
|
|
|
|
|
|
m_panose1 = dwrite_font_information.m_panose1;
|
|
|
|
this->SetSimulated(
|
|
dwrite_font_information.m_bSimulatedBold,
|
|
false,
|
|
dwrite_font_information.m_bSimulatedOblique,
|
|
dwrite_font_information.m_bSimulatedOther
|
|
);
|
|
|
|
if ( ON_Font::FontType::ManagedFont == font_type || ON_Font::FontType::InstalledFont == font_type )
|
|
{
|
|
ON_FontGlyphCache* font_cache = ON_FontGlyphCache::New();
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
font_cache->m_dwrite_font = dwrite_font_information.m_dwrite_font;
|
|
dwrite_font_information.m_dwrite_font->AddRef();
|
|
#endif
|
|
if (dwrite_font_information.m_font_metrics.HeightsAreValid())
|
|
{
|
|
font_cache->SetFontMetrics(dwrite_font_information.m_font_metrics);
|
|
}
|
|
m_font_glyph_cache = std::shared_ptr<ON_FontGlyphCache>(font_cache);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
bool ON_Font::Write(
|
|
ON_BinaryArchive& file // serialize definition to binary archive
|
|
) const
|
|
{
|
|
|
|
if (file.Archive3dmVersion() < 60
|
|
|| file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version
|
|
)
|
|
{
|
|
ON_WARNING("This font should probably be an ON_TextStyle.");
|
|
return WriteV5(
|
|
RuntimeSerialNumber(),
|
|
ON_nil_uuid,
|
|
file
|
|
);
|
|
}
|
|
|
|
|
|
if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,5))
|
|
return false;
|
|
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
// version 1.0
|
|
unsigned int fc = FontCharacteristicsAsUnsigned();
|
|
if (!file.WriteInt(fc))
|
|
break;
|
|
|
|
// Windows LOGFONT.lfFaceName
|
|
if ( !file.WriteWideString(WindowsLogfontName()) )
|
|
break;
|
|
|
|
// PostScript name
|
|
if (!file.WriteString(PostScriptName()))
|
|
break;
|
|
|
|
// version 1.1 added font_description August 2016
|
|
ON_wString obsolete_font_description = PostScriptName();
|
|
if (obsolete_font_description.IsEmpty())
|
|
obsolete_font_description = FamilyName();
|
|
if (obsolete_font_description.IsEmpty())
|
|
obsolete_font_description = WindowsLogfontName();
|
|
if (!file.WriteString(obsolete_font_description))
|
|
break;
|
|
|
|
// version 1.2 added m_windows_logfont_weight and m_apple_font_weight_trait
|
|
if (!file.WriteInt(m_windows_logfont_weight))
|
|
break;
|
|
|
|
if (!file.WriteDouble(m_apple_font_weight_trait))
|
|
break;
|
|
|
|
// version 1.3 added additional m_point_size and m_LOGFONT_* values.
|
|
if (!file.WriteDouble(m_point_size))
|
|
break;
|
|
|
|
const bool bOBSOLETEBool = false;
|
|
if (!file.WriteBool(bOBSOLETEBool))
|
|
break;
|
|
|
|
// version 1.4 explicit Family name
|
|
if (!file.WriteString(FamilyName()))
|
|
break;
|
|
|
|
// version 1.5 explicit names in loc and en and PANOSE1 settings
|
|
if (!file.WriteString(m_locale_name))
|
|
break;
|
|
if (!file.WriteString(m_loc_postscript_name))
|
|
break;
|
|
if (!file.WriteString(m_en_postscript_name))
|
|
break;
|
|
if (!file.WriteString(m_loc_windows_logfont_name))
|
|
break;
|
|
if (!file.WriteString(m_en_windows_logfont_name))
|
|
break;
|
|
if (!file.WriteString(m_loc_family_name))
|
|
break;
|
|
if (!file.WriteString(m_en_family_name))
|
|
break;
|
|
if (!file.WriteString(m_loc_face_name))
|
|
break;
|
|
if (!file.WriteString(m_en_face_name))
|
|
break;
|
|
if (!m_panose1.Write(file))
|
|
break;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!file.EndWrite3dmChunk())
|
|
rc = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Font::WriteV5(
|
|
int V5_font_index,
|
|
ON_UUID V5_font_id,
|
|
ON_BinaryArchive& file // serialize definition to binary archive
|
|
) const
|
|
{
|
|
bool rc = file.Write3dmChunkVersion(1, 2);
|
|
while(rc)
|
|
{
|
|
rc = file.Write3dmReferencedComponentIndex(ON_ModelComponent::Type::TextStyle,V5_font_index);
|
|
if(!rc)
|
|
break;
|
|
|
|
// Mac Rhino 5 uses the V5 "m_font_description" field to store Apple font names because
|
|
// there was no other appropriate place to save the information in a V5 file format.
|
|
ON_wString description = PostScriptName();
|
|
if (description.IsEmpty())
|
|
description = FamilyName();
|
|
if (description.IsEmpty())
|
|
description = WindowsLogfontName();
|
|
|
|
rc = file.WriteString(description);
|
|
if(!rc)
|
|
break;
|
|
|
|
{
|
|
// 18 October 2002 Dale Lear:
|
|
// This is a result of a bug in old code.
|
|
// The Windows LOGFONT.lfFaceName was written as an array of 64 unsigned shorts.
|
|
unsigned short sh[64];
|
|
memset(sh, 0, sizeof(sh));
|
|
const ON_wString logfont_name = WindowsLogfontName();
|
|
const int logfont_name_len = logfont_name.Length();
|
|
const wchar_t* logfont_name_str = static_cast<const wchar_t*>(logfont_name);
|
|
|
|
int i;
|
|
for(i = 0; i < 64 && i < logfont_name_len; i++)
|
|
sh[i] = (unsigned short)(logfont_name_str[i]);
|
|
rc = file.WriteShort(64, sh);
|
|
if(!rc) break;
|
|
}
|
|
|
|
// 1.1 additions
|
|
int windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight);
|
|
if (file.Archive3dmVersion() < 60)
|
|
{
|
|
// V5 and earlier files had 4 permitted weights
|
|
// Light = 300
|
|
// Normal = 400
|
|
// Medium = 500
|
|
// Bold = 700
|
|
if ( windows_logfont_weight <= 0 )
|
|
windows_logfont_weight = 400;
|
|
if ( windows_logfont_weight < 350 )
|
|
windows_logfont_weight = 300;
|
|
else if ( windows_logfont_weight < 450 )
|
|
windows_logfont_weight = 400;
|
|
else if ( windows_logfont_weight < 600 )
|
|
windows_logfont_weight = 500;
|
|
else if ( windows_logfont_weight <= 1000 )
|
|
windows_logfont_weight = 700;
|
|
else
|
|
windows_logfont_weight = 400;
|
|
}
|
|
|
|
rc = file.WriteInt(static_cast<int>(windows_logfont_weight));
|
|
if(!rc) break;
|
|
rc = file.WriteInt(ON_Font::Style::Italic == m_font_style);
|
|
if(!rc) break;
|
|
rc = file.WriteDouble(ON_FontMetrics::DefaultLineFeedRatio);
|
|
if(!rc) break;
|
|
|
|
// 1.2 addition
|
|
rc = file.WriteUuid(V5_font_id);
|
|
if(!rc) break;
|
|
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Font::Read(
|
|
ON_BinaryArchive& file // restore definition from binary archive
|
|
)
|
|
{
|
|
// On September 16, 2015 the "V5" ON_Font was split into
|
|
// ON_TextStyle (a document object) and
|
|
// ON_Font (a current runtime resource)
|
|
*this = ON_Font::Unset;
|
|
|
|
ON__UINT32 typecode = 0;
|
|
ON__INT64 big_value = 0;
|
|
if (file.Archive3dmVersion() < 60
|
|
|| file.ArchiveOpenNURBSVersion() < ON_TextStyle::binary_archive_opennurbs_version
|
|
|| (file.PeekAt3dmBigChunkType(&typecode,&big_value) && 1 == typecode)
|
|
)
|
|
{
|
|
ON_WARNING("Should probably be reading an ON_TextStyle");
|
|
int font_index = -1;
|
|
ON_UUID font_id = ON_nil_uuid;
|
|
return ReadV5(
|
|
file,
|
|
&font_index,
|
|
&font_id
|
|
);
|
|
}
|
|
|
|
int major_verision = 0;
|
|
int minor_verision = 0;
|
|
if (!file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_verision, &minor_verision))
|
|
{
|
|
*this = ON_Font::Default;
|
|
return false;
|
|
}
|
|
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
if ( 1 != major_verision )
|
|
break;
|
|
|
|
unsigned int fc = 0;
|
|
if (!file.ReadInt(&fc))
|
|
break;
|
|
|
|
ON_Font::Weight font_weight = ON_Font::Weight::Normal;
|
|
ON_Font::Stretch font_stretch = ON_Font::Stretch::Medium;
|
|
ON_Font::Style font_style = ON_Font::Style::Upright;
|
|
bool bUnderlined = false;
|
|
bool bStrikethrough = false;
|
|
ON_Font::Internal_GetFontCharacteristicsFromUnsigned(
|
|
fc,
|
|
font_weight,
|
|
font_stretch,
|
|
font_style,
|
|
bUnderlined,
|
|
bStrikethrough
|
|
);
|
|
|
|
ON_wString windows_logfont_name;
|
|
if ( !file.ReadWideString(windows_logfont_name) )
|
|
break;
|
|
|
|
ON_wString postscript_name;
|
|
if (!file.ReadString(postscript_name))
|
|
break;
|
|
|
|
if (ON::RuntimeEnvironment::Windows == file.ArchiveRuntimeEnvironment())
|
|
{
|
|
// Dale Lear - August 16, 2016.
|
|
// The value of m_postscript_font_name is damaged in many archives written by Windows Rhino.
|
|
// It has values like "Font 01". So, I'm going to clean it
|
|
// during read and we will get it right going forward.
|
|
// This value is not saved in V5 files.
|
|
const unsigned int broken_apple_font_name_version = ON_VersionNumberConstruct(6, 0, 2016, 8, 18, 0);
|
|
const unsigned int archive_version = file.Archive3dmVersion();
|
|
const unsigned int archive_opennurbs_version = file.ArchiveOpenNURBSVersion();
|
|
if (
|
|
archive_version < 60
|
|
|| (60 == archive_version && archive_opennurbs_version <= broken_apple_font_name_version)
|
|
)
|
|
{
|
|
postscript_name = ON_wString::EmptyString;
|
|
}
|
|
}
|
|
|
|
const ON_Font* installed_font = ON_Font::InstalledFontList().FromNames(
|
|
postscript_name,
|
|
windows_logfont_name,
|
|
windows_logfont_name,
|
|
nullptr,
|
|
font_weight,
|
|
font_stretch,
|
|
font_style,
|
|
false,
|
|
true
|
|
);
|
|
|
|
if (nullptr != installed_font)
|
|
{
|
|
*this = *installed_font;
|
|
this->m_font_bUnderlined = bUnderlined;
|
|
this->m_font_bStrikethrough = bStrikethrough;
|
|
}
|
|
else
|
|
{
|
|
// Either the information in the .3dm file is junk or the font is not installed on this device.
|
|
if (windows_logfont_name.IsNotEmpty())
|
|
SetWindowsLogfontName(windows_logfont_name);
|
|
else
|
|
*this = ON_Font::Default;
|
|
Internal_SetFontCharacteristicsFromUnsigned(fc);
|
|
if (windows_logfont_name.IsNotEmpty())
|
|
{
|
|
m_loc_postscript_name = postscript_name;
|
|
m_en_postscript_name = postscript_name;
|
|
}
|
|
}
|
|
|
|
if (minor_verision <= 0)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
// version 1.1 added m_font_description August 2016
|
|
ON_wString obsolete_font_description;
|
|
if ( !file.ReadString(obsolete_font_description) )
|
|
break;
|
|
|
|
if (minor_verision <= 1)
|
|
{
|
|
// m_windows_logfont_weight and m_apple_font_weight_trait
|
|
// are set above in the call to SetFontCharacteristicsFromUnsigned().
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
// version 1.2 added m_windows_logfont_weight and m_apple_font_weight_trait
|
|
int windows_logfont_weight = 0;
|
|
if (!file.ReadInt(&windows_logfont_weight))
|
|
break;
|
|
const bool bSetLogfontWeight
|
|
=
|
|
#if defined(ON_RUNTIME_WIN)
|
|
(nullptr == installed_font)
|
|
#else
|
|
(ON::RuntimeEnvironment::Windows == file.ArchiveRuntimeEnvironment())
|
|
#endif
|
|
;
|
|
if (bSetLogfontWeight)
|
|
m_windows_logfont_weight = windows_logfont_weight;
|
|
|
|
double apple_font_weight_trait = 0.0;
|
|
if (!file.ReadDouble(&apple_font_weight_trait))
|
|
break;
|
|
const bool bSetAppleFontWeightTrait
|
|
=
|
|
#if defined(ON_RUNTIME_WIN)
|
|
(ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment())
|
|
#else
|
|
(nullptr == installed_font)
|
|
#endif
|
|
;
|
|
if (bSetAppleFontWeightTrait)
|
|
m_apple_font_weight_trait = apple_font_weight_trait;
|
|
|
|
if (minor_verision <= 2)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
// version 1.3 added additional m_point_size and m_LOGFONT_* values.
|
|
if (!file.ReadDouble(&m_point_size))
|
|
break;
|
|
|
|
bool bOBSOLETE_Bool = false;
|
|
if (!file.ReadBool(&bOBSOLETE_Bool))
|
|
break;
|
|
if (bOBSOLETE_Bool)
|
|
{
|
|
unsigned char obsolete_c = 0;
|
|
if (!file.ReadChar(&obsolete_c))
|
|
break;
|
|
if (!file.ReadChar(&obsolete_c))
|
|
break;
|
|
if (!file.ReadChar(&obsolete_c))
|
|
break;
|
|
if (!file.ReadChar(&obsolete_c))
|
|
break;
|
|
int obsolete_i = 0;
|
|
if (!file.ReadInt(&obsolete_i))
|
|
break;
|
|
if (!file.ReadInt(&obsolete_i))
|
|
break;
|
|
if (!file.ReadInt(&obsolete_i))
|
|
break;
|
|
if (!file.ReadInt(&obsolete_i))
|
|
break;
|
|
}
|
|
|
|
if (minor_verision <= 3)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
// version 1.4 added explicit Family name
|
|
ON_wString family_name;
|
|
if (!file.ReadString(family_name))
|
|
break;
|
|
|
|
if (nullptr == installed_font)
|
|
{
|
|
m_loc_family_name = family_name;
|
|
m_en_family_name = family_name;
|
|
}
|
|
|
|
if (minor_verision <= 4)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
// version 1.5 explicit names in loc and en and PANOSE1 settings
|
|
if (!file.ReadString(m_locale_name))
|
|
break;
|
|
if (!file.ReadString(m_loc_postscript_name))
|
|
break;
|
|
if (!file.ReadString(m_en_postscript_name))
|
|
break;
|
|
if (!file.ReadString(m_loc_windows_logfont_name))
|
|
break;
|
|
if (!file.ReadString(m_en_windows_logfont_name))
|
|
break;
|
|
if (!file.ReadString(m_loc_family_name))
|
|
break;
|
|
if (!file.ReadString(m_en_family_name))
|
|
break;
|
|
if (!file.ReadString(m_loc_face_name))
|
|
break;
|
|
if (!file.ReadString(m_en_face_name))
|
|
break;
|
|
if (!m_panose1.Read(file))
|
|
break;
|
|
|
|
|
|
if (minor_verision <= 5)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (
|
|
false == rc
|
|
|| (WindowsLogfontName().IsEmpty()
|
|
&& PostScriptName().IsEmpty()
|
|
&& FamilyName().IsEmpty())
|
|
)
|
|
{
|
|
*this = ON_Font::Default;
|
|
}
|
|
|
|
if (!file.EndRead3dmChunk())
|
|
rc = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_Font::ReadV5(
|
|
ON_BinaryArchive& file, // restore definition from binary archive
|
|
int* V5_font_index,
|
|
ON_UUID* V5_font_id
|
|
)
|
|
{
|
|
*this = ON_Font::Unset;
|
|
|
|
if ( nullptr != V5_font_index )
|
|
*V5_font_index = -1;
|
|
if ( nullptr != V5_font_id )
|
|
*V5_font_id = ON_nil_uuid;
|
|
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
if (!file.Read3dmChunkVersion(&major_version, &minor_version))
|
|
{
|
|
*this = ON_Font::Default;
|
|
return false;
|
|
}
|
|
|
|
bool rc = false;
|
|
|
|
ON_wString windows_logfont_name;
|
|
ON_wString postscript_name;
|
|
ON_Font::Weight font_weight = ON_Font::Weight::Normal;
|
|
ON_Font::Stretch font_stretch = ON_Font::Stretch::Unset;
|
|
ON_Font::Style font_style = ON_Font::Style::Upright;
|
|
int logfont_weight = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if ( 1 != major_version )
|
|
break;
|
|
int i;
|
|
if (!file.ReadInt(&i))
|
|
break;
|
|
if ( nullptr != V5_font_index )
|
|
*V5_font_index = i;
|
|
|
|
ON_wString corrupt_information_font_description;
|
|
if (!file.ReadString(corrupt_information_font_description))
|
|
break;
|
|
|
|
if ( false == ON_Font::IsNotAppleFontName(corrupt_information_font_description) )
|
|
{
|
|
if (ON::RuntimeEnvironment::Apple == file.ArchiveRuntimeEnvironment())
|
|
{
|
|
// Files written by Mac Rhino 5 for Mac have the Apple font name stored in this string.
|
|
postscript_name = corrupt_information_font_description;
|
|
}
|
|
else if ( file.ArchiveOpenNURBSVersion() > ON_VersionNumberConstruct(6, 2, 2018, 2, 23, 0) )
|
|
{
|
|
postscript_name = corrupt_information_font_description;
|
|
}
|
|
}
|
|
|
|
// 18 October 2002 Dale Lear:
|
|
// This code deals with an old bug.
|
|
// The Windows LOGFONT.lfFaceName was written as an array of 64 unsigned shorts.
|
|
unsigned short sh[64];
|
|
if (!file.ReadShort(64, sh))
|
|
break;
|
|
|
|
wchar_t facename[65];
|
|
for(i = 0; i < 64; i++)
|
|
facename[i] = sh[i];
|
|
facename[64] = 0;
|
|
windows_logfont_name = facename;
|
|
|
|
m_logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(WindowsLogfontName());
|
|
if (minor_version >= 1)
|
|
{
|
|
if (!file.ReadInt(&logfont_weight))
|
|
break;
|
|
|
|
if (logfont_weight >= 100 && logfont_weight <= 1000)
|
|
{
|
|
font_weight = ON_Font::WeightFromWindowsLogfontWeight(logfont_weight);
|
|
}
|
|
|
|
int bItalic = 0;
|
|
if (!file.ReadInt(&bItalic))
|
|
break;
|
|
if ( 0 != bItalic )
|
|
font_style = ON_Font::Style::Italic;
|
|
|
|
double obsolete_linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio;
|
|
if (!file.ReadDouble(&obsolete_linefeed_ratio))
|
|
break;
|
|
|
|
if (minor_version >= 2)
|
|
{
|
|
ON_UUID uuid = ON_nil_uuid;
|
|
if (!file.ReadUuid(uuid))
|
|
break;
|
|
if ( nullptr != V5_font_id )
|
|
*V5_font_id = uuid;
|
|
}
|
|
}
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
const ON_Font* installed_font = ON_Font::InstalledFontList().FromNames(
|
|
postscript_name,
|
|
windows_logfont_name,
|
|
nullptr,
|
|
nullptr,
|
|
font_weight,
|
|
font_stretch,
|
|
font_style,
|
|
false,
|
|
true
|
|
);
|
|
|
|
if (nullptr != installed_font)
|
|
{
|
|
*this = *installed_font;
|
|
}
|
|
else if (rc)
|
|
{
|
|
SetWindowsLogfontName(windows_logfont_name);
|
|
Internal_SetFontWeightTrio(
|
|
font_weight,
|
|
logfont_weight,
|
|
ON_UNSET_VALUE,
|
|
false
|
|
);
|
|
m_loc_postscript_name = postscript_name;
|
|
m_en_postscript_name = m_loc_postscript_name;
|
|
m_loc_family_name = ON_Font::FamilyNameFromDirtyName(m_loc_windows_logfont_name);
|
|
m_en_family_name = ON_Font::FamilyNameFromDirtyName(m_en_windows_logfont_name);
|
|
}
|
|
else
|
|
{
|
|
*this = ON_Font::Default;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
unsigned int ON_Font::RuntimeSerialNumber() const
|
|
{
|
|
return m_runtime_serial_number;
|
|
}
|
|
|
|
unsigned int ON_Font::ManagedFontSerialNumber() const
|
|
{
|
|
if (0 != m_runtime_serial_number)
|
|
return m_runtime_serial_number;
|
|
const ON_Font* mananged_font = this->ManagedFont();
|
|
return (nullptr == mananged_font) ? 0 : mananged_font->RuntimeSerialNumber();
|
|
}
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
static
|
|
int CALLBACK ON__IsSymbolCharSetFontFaceNameHelper(ENUMLOGFONTEX* lf, NEWTEXTMETRICEX* tm, DWORD font_type, LPARAM)
|
|
{
|
|
// If the fontname in the logfont structure has
|
|
// a corresponding symbol font on the system,
|
|
// set the lfCharSet member to SYMBOL_CHARSET,
|
|
// otherwise DEFAULT_CHARSET
|
|
// The input logfont structure may be modified.
|
|
return 7;
|
|
}
|
|
#endif
|
|
|
|
unsigned char ON_Font::WindowsLogfontCharSetFromFaceName(
|
|
const wchar_t* face_name
|
|
)
|
|
{
|
|
unsigned char logfont_charset = ON_Font::WindowsConstants::logfont_default_charset;
|
|
|
|
|
|
if( nullptr != face_name && face_name[0] > ON_wString::Space )
|
|
{
|
|
// Problem fonts
|
|
if (ON_wString::EqualOrdinal(L"CityBlueprint", -1, face_name, -1, true))
|
|
return ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
|
|
if (ON_wString::EqualOrdinal(L"CountryBlueprint", -1, face_name, -1, true))
|
|
return ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
HDC font_hdc = ON_Font::CreateWindowsLogfontDeviceContext();
|
|
if(nullptr != font_hdc)
|
|
{
|
|
// See if there is a font with this facename that has the symbol charset
|
|
LOGFONT logfont;
|
|
memset(&logfont, 0, sizeof(logfont));
|
|
for(int i = 0; i < LF_FACESIZE && face_name[i]; i++)
|
|
logfont.lfFaceName[i] = face_name[i];
|
|
|
|
// Dale lear - set logfont.lfOutPrecision May 2017
|
|
logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis;
|
|
|
|
// Is it a SYMBOL_CHARSET font?
|
|
logfont.lfCharSet = ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
if (7 == ::EnumFontFamiliesEx(font_hdc, &logfont, (FONTENUMPROC)ON__IsSymbolCharSetFontFaceNameHelper, 0, 0))
|
|
{
|
|
// Yes, this facename is a "symbol font"
|
|
logfont_charset = ON_Font::WindowsConstants::logfont_symbol_charset;
|
|
}
|
|
ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return logfont_charset;
|
|
}
|
|
|
|
const ON_wString& ON_Font::FontDescription() const
|
|
{
|
|
return
|
|
m_loc_postscript_name.IsNotEmpty()
|
|
? m_loc_postscript_name
|
|
: m_en_postscript_name;
|
|
}
|
|
|
|
bool ON_Font::SetFontDescriptionForExperts(
|
|
const wchar_t* ignored_parameter
|
|
)
|
|
{
|
|
// OBSOLETE V6 function.
|
|
return true;
|
|
}
|
|
|
|
const wchar_t* ON_Font::FontDescriptionAsPointer() const
|
|
{
|
|
return static_cast<const wchar_t*>(PostScriptName());
|
|
}
|
|
|
|
const ON_wString ON_Font::Description(
|
|
) const
|
|
{
|
|
return Description(ON_Font::NameLocale::LocalizedFirst, ON_wString::HyphenMinus, ON_wString::Space, true);
|
|
}
|
|
|
|
const ON_wString ON_Font::Description(
|
|
ON_Font::NameLocale name_local,
|
|
wchar_t family_separator,
|
|
wchar_t weight_width_slope_separator,
|
|
bool bIncludeUndelinedAndStrikethrough
|
|
) const
|
|
{
|
|
ON_wString description;
|
|
if ((FamilyName().IsEmpty() || FaceName().IsEmpty()) && WindowsLogfontName().IsNotEmpty())
|
|
{
|
|
description = WindowsLogfontName();
|
|
}
|
|
else if ( FamilyName().IsNotEmpty() )
|
|
{
|
|
description = FamilyName();
|
|
if ( FaceName().IsNotEmpty() )
|
|
{
|
|
description += L" ";
|
|
description += FaceName();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
description = ON_Font::FamilyNameFromDirtyName(PostScriptName());
|
|
}
|
|
|
|
wchar_t separator = family_separator;
|
|
|
|
ON_wString weight;
|
|
switch (FontWeight())
|
|
{
|
|
case ON_Font::Weight::Unset:
|
|
break;
|
|
case ON_Font::Weight::Normal:
|
|
break;
|
|
|
|
default:
|
|
weight = ON_Font::WeightToWideString(FontWeight());
|
|
break;
|
|
}
|
|
if (weight.IsNotEmpty())
|
|
{
|
|
description += separator;
|
|
description += weight;
|
|
separator = weight_width_slope_separator;
|
|
}
|
|
|
|
ON_wString stretch;
|
|
switch (FontStretch())
|
|
{
|
|
case ON_Font::Stretch::Unset:
|
|
break;
|
|
case ON_Font::Stretch::Medium:
|
|
break;
|
|
|
|
default:
|
|
stretch = ON_Font::StretchToWideString(FontStretch());
|
|
break;
|
|
};
|
|
if (stretch.IsNotEmpty())
|
|
{
|
|
description += separator;
|
|
description += stretch;
|
|
separator = weight_width_slope_separator;
|
|
}
|
|
|
|
|
|
ON_wString slope;
|
|
switch (FontStyle())
|
|
{
|
|
case ON_Font::Style::Unset:
|
|
break;
|
|
case ON_Font::Style::Upright:
|
|
break;
|
|
default:
|
|
slope = ON_Font::StyleToWideString(FontStyle());
|
|
break;
|
|
}
|
|
if (slope.IsNotEmpty())
|
|
{
|
|
description += separator;
|
|
description += slope;
|
|
separator = weight_width_slope_separator;
|
|
}
|
|
|
|
if (IsUnderlined())
|
|
{
|
|
description += separator;
|
|
description += L"Underlined";
|
|
separator = weight_width_slope_separator;
|
|
}
|
|
|
|
if (IsStrikethrough())
|
|
{
|
|
description += separator;
|
|
description += L"Strikethrough";
|
|
separator = weight_width_slope_separator;
|
|
}
|
|
|
|
return description;
|
|
}
|
|
|
|
double ON_Font::LinefeedRatio() const
|
|
{
|
|
return ON_FontMetrics::DefaultLineFeedRatio;
|
|
}
|
|
|
|
bool ON_Font::HasUnsetProperties(
|
|
bool bCheckFamilyName,
|
|
bool bCheckPostScriptName
|
|
) const
|
|
{
|
|
if (ON_Font::Style::Unset == m_font_style)
|
|
return true;
|
|
if (ON_Font::Weight::Unset == m_font_weight)
|
|
return true;
|
|
if (ON_Font::Stretch::Unset == m_font_stretch)
|
|
return true;
|
|
|
|
if (bCheckFamilyName && FamilyName().IsEmpty() && WindowsLogfontName().IsEmpty())
|
|
return true;
|
|
|
|
if (bCheckPostScriptName && PostScriptName().IsEmpty())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
unsigned int ON_Font::SetUnsetProperties(
|
|
const ON_Font& source,
|
|
bool bUpdateDescription
|
|
)
|
|
{
|
|
if (false == ON_FONT_MODIFICATION_PERMITTED)
|
|
return 0;
|
|
|
|
unsigned int changed_property_count = 0;
|
|
|
|
if (ON_Font::Weight::Unset == m_font_weight
|
|
&& ON_Font::Weight::Unset != source.m_font_weight
|
|
)
|
|
{
|
|
m_windows_logfont_weight = source.m_windows_logfont_weight;
|
|
m_apple_font_weight_trait = source.m_apple_font_weight_trait;
|
|
m_font_weight = source.m_font_weight;
|
|
changed_property_count++;
|
|
}
|
|
|
|
if (ON_Font::Style::Unset == m_font_style
|
|
&& ON_Font::Style::Unset == source.m_font_style
|
|
)
|
|
{
|
|
m_font_style = source.m_font_style;
|
|
changed_property_count++;
|
|
}
|
|
|
|
if (ON_Font::Stretch::Unset == m_font_stretch
|
|
&& ON_Font::Stretch::Unset != source.m_font_stretch
|
|
)
|
|
{
|
|
m_font_stretch = source.m_font_stretch;
|
|
changed_property_count++;
|
|
}
|
|
|
|
const bool bEqualWeightStretch
|
|
= ON_Font::Weight::Unset != m_font_weight
|
|
&& ON_Font::Stretch::Unset != m_font_stretch
|
|
&& m_font_weight == source.m_font_weight
|
|
&& m_font_stretch == source.m_font_stretch
|
|
;
|
|
|
|
const bool bEqualWeightStretchStyle
|
|
= bEqualWeightStretch
|
|
&& ON_Font::Style::Unset != m_font_style
|
|
&& m_font_style == source.m_font_style;
|
|
|
|
bool bSameFamily = ON_Font::EqualFontFamily(this, &source);
|
|
|
|
bool bCopyFamilyName = false;
|
|
bool bCopyLOGFONTFaceName = false;
|
|
bool bCopyPostScriptName = false;
|
|
if ( FamilyName().IsEmpty() && WindowsLogfontName().IsEmpty() && PostScriptName().IsEmpty() )
|
|
{
|
|
bCopyFamilyName = source.FamilyName().IsNotEmpty();
|
|
bCopyLOGFONTFaceName = source.WindowsLogfontName().IsNotEmpty();
|
|
bCopyPostScriptName = source.PostScriptName().IsNotEmpty();
|
|
}
|
|
else if ( bSameFamily )
|
|
{
|
|
|
|
// at least one of m_family_name or m_face_name is set.
|
|
bCopyFamilyName = FamilyName().IsEmpty();
|
|
|
|
// If "this" and source have the same m_family_name[], weight, and stretch,
|
|
// then copy source.m_face_name.
|
|
//
|
|
// NOTE WELL:
|
|
// Both "this" and source must have the same weight and stretch because
|
|
// is is common for LOGFONT name to be used to distigush between weight
|
|
// and/or stretch in the same family.
|
|
// Generally, this situation is a result of LOGFONT GDI restrictions
|
|
// and occures when a family has more than the four standard faces
|
|
// (regular, bold, italic, bold-italic).
|
|
// Example:
|
|
// Family = "Arial"
|
|
// LOGFONTs face names include "Arial", "Arial Narrow" (different stretch), "Arial Black" (different weight)
|
|
// Family = "Segoe UI"
|
|
// Many LOGFONTs with various weights and stretches.
|
|
bCopyLOGFONTFaceName
|
|
= source.WindowsLogfontName().IsNotEmpty()
|
|
&& bEqualWeightStretch
|
|
;
|
|
|
|
bCopyPostScriptName =
|
|
source.PostScriptName().IsNotEmpty()
|
|
&& bEqualWeightStretchStyle
|
|
;
|
|
}
|
|
|
|
if (bCopyFamilyName)
|
|
{
|
|
m_loc_family_name = source.m_loc_family_name;
|
|
m_en_family_name = source.m_en_family_name;
|
|
m_loc_face_name = source.m_loc_face_name;
|
|
m_en_face_name = source.m_en_face_name;
|
|
changed_property_count++;
|
|
}
|
|
|
|
if (bCopyLOGFONTFaceName)
|
|
{
|
|
m_loc_windows_logfont_name = source.m_loc_windows_logfont_name;
|
|
m_en_windows_logfont_name = source.m_en_windows_logfont_name;
|
|
changed_property_count++;
|
|
}
|
|
|
|
// The PostScript name identifies the complete face (family, weight, stretch, style).
|
|
if ( bCopyPostScriptName)
|
|
{
|
|
m_loc_postscript_name = source.m_loc_postscript_name;
|
|
m_en_postscript_name = source.m_en_postscript_name;
|
|
changed_property_count++;
|
|
}
|
|
|
|
if (changed_property_count > 0)
|
|
{
|
|
m_simulated = 0;
|
|
m_managed_face_is_installed = 0;
|
|
Internal_AfterModification();
|
|
}
|
|
|
|
return changed_property_count;
|
|
}
|
|
|
|
bool ON_Font::SetFontFaceName(
|
|
const wchar_t* windows_logfont_name
|
|
)
|
|
{
|
|
return SetWindowsLogfontName(windows_logfont_name);
|
|
}
|
|
|
|
bool ON_Font::SetFamilyName(
|
|
const wchar_t* family_name
|
|
)
|
|
{
|
|
ON_wString local_family_name = ON_Font::FamilyNameFromDirtyName(family_name);
|
|
local_family_name.TrimLeftAndRight();
|
|
family_name = local_family_name;
|
|
|
|
if ( ON_wString::EqualOrdinal(family_name, FamilyName(), false) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
|
|
Internal_ClearName(true, true, true, true);
|
|
|
|
m_logfont_charset = ON_Font::WindowsConstants::logfont_default_charset;
|
|
|
|
m_loc_family_name = family_name;
|
|
m_en_family_name = m_loc_family_name;
|
|
|
|
Internal_AfterModification();
|
|
return true;
|
|
}
|
|
|
|
bool ON_Font::SetWindowsLogfontName(
|
|
const wchar_t* windows_logfont_name
|
|
)
|
|
{
|
|
if (false == ON_Font::IsValidFaceName(windows_logfont_name))
|
|
return false;
|
|
|
|
const ON_Font::Weight font_weight = ON_Font::Weight::Normal;
|
|
const ON_Font::Style font_style = ON_Font::Style::Upright;
|
|
const ON_Font::Stretch font_stretch = ON_Font::Stretch::Medium;
|
|
|
|
const ON_Font* installed_font =
|
|
ON_Font::InstalledFontList().FromNames(
|
|
nullptr,
|
|
windows_logfont_name,
|
|
nullptr,
|
|
nullptr,
|
|
font_weight,
|
|
font_stretch,
|
|
font_style,
|
|
false,
|
|
false
|
|
);
|
|
|
|
if (nullptr != installed_font)
|
|
{
|
|
*this = *installed_font;
|
|
}
|
|
else
|
|
{
|
|
const unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(windows_logfont_name);
|
|
|
|
const ON_wString family_name = ON_Font::FamilyNameFromDirtyName(windows_logfont_name);
|
|
|
|
if (
|
|
ON_wString::EqualOrdinal(windows_logfont_name, m_loc_windows_logfont_name, false)
|
|
&& family_name == m_loc_family_name
|
|
&& font_weight == m_font_weight
|
|
&& font_style == m_font_style
|
|
&& font_stretch == m_font_stretch
|
|
&& logfont_charset == m_logfont_charset
|
|
&& ON_Font::Origin::Unknown == m_font_origin
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (false == ON_FONT_MODIFICATION_PERMITTED)
|
|
return false;
|
|
|
|
// TODO - better support for generating PostScript names from LOGFONT.lfFaceNames
|
|
// This code at least handles the 4 most common names passed to this setter.
|
|
if (ON_wString::EqualOrdinal(L"Arial", windows_logfont_name, true))
|
|
m_loc_postscript_name = L"ArialMT";
|
|
else if (ON_wString::EqualOrdinal(L"Segoe UI", windows_logfont_name, true)
|
|
|| ON_wString::EqualOrdinal(L"SegoeUI", windows_logfont_name, true))
|
|
{
|
|
m_loc_postscript_name = L"SegoeUI";
|
|
}
|
|
else if (ON_wString::EqualOrdinal(L"Helvetica Neue", windows_logfont_name, true)
|
|
|| ON_wString::EqualOrdinal(L"HelveticaNeue", windows_logfont_name, true))
|
|
m_loc_postscript_name = L"HelveticaNeue";
|
|
else if (ON_wString::EqualOrdinal(L"Helvetica", windows_logfont_name, true))
|
|
m_loc_postscript_name = L"Helvetica";
|
|
else
|
|
m_loc_postscript_name = ON_wString::EmptyString;
|
|
|
|
m_en_postscript_name = m_loc_postscript_name;
|
|
|
|
m_loc_family_name = family_name;
|
|
m_en_family_name = m_loc_family_name;
|
|
|
|
m_loc_windows_logfont_name = windows_logfont_name;
|
|
m_en_windows_logfont_name = m_loc_windows_logfont_name;
|
|
|
|
m_logfont_charset = logfont_charset;
|
|
m_font_weight = font_weight;
|
|
m_font_style = font_style;
|
|
m_font_stretch = font_stretch;
|
|
m_font_origin = ON_Font::Origin::Unknown;
|
|
}
|
|
|
|
Internal_AfterModification();
|
|
|
|
return true;
|
|
}
|
|
|
|
const wchar_t* ON_Font::FontFaceName() const
|
|
{
|
|
// Was never a "real" face name.
|
|
// Always returned a value from or used in a Windows GDI LOGFONT.lfFaceName[].
|
|
return
|
|
m_loc_windows_logfont_name.IsNotEmpty()
|
|
? static_cast<const wchar_t*>(m_loc_windows_logfont_name)
|
|
: static_cast<const wchar_t*>(m_en_windows_logfont_name);
|
|
}
|
|
|
|
const wchar_t* ON_Font::WindowsLogfontNameAsPointer() const
|
|
{
|
|
return
|
|
m_loc_windows_logfont_name.IsNotEmpty()
|
|
? static_cast<const wchar_t*>(m_loc_windows_logfont_name)
|
|
: static_cast<const wchar_t*>(m_en_windows_logfont_name);
|
|
}
|
|
|
|
ON_Font::Weight ON_Font::FontWeight() const
|
|
{
|
|
return m_font_weight;
|
|
}
|
|
|
|
bool ON_Font::IsLight() const
|
|
{
|
|
const int font_weight = ON_Font::WindowsLogfontWeightFromWeight(m_font_weight);
|
|
const int normal_weight = ON_Font::WindowsLogfontWeightFromWeight(ON_Font::Weight::Normal);
|
|
return (font_weight < normal_weight && m_font_weight != ON_Font::Weight::Unset);
|
|
}
|
|
|
|
bool ON_Font::IsNormalWeight() const
|
|
{
|
|
return (ON_Font::Weight::Normal == m_font_weight || ON_Font::Weight::Medium == m_font_weight);
|
|
}
|
|
|
|
bool ON_Font::IsBold() const
|
|
{
|
|
return ON_Font::IsBoldWeight(m_font_weight);
|
|
}
|
|
|
|
bool ON_Font::IsItalic() const
|
|
{
|
|
return (ON_Font::Style::Italic == m_font_style);
|
|
}
|
|
|
|
bool ON_Font::IsUpright() const
|
|
{
|
|
return (ON_Font::Style::Upright == m_font_style);
|
|
}
|
|
|
|
bool ON_Font::IsOblique()
|
|
{
|
|
return (ON_Font::Style::Oblique == m_font_style);
|
|
}
|
|
|
|
void ON_Font::Internal_AfterModification(
|
|
)
|
|
{
|
|
// This occures when a characteristic like PostScriptName(), weight, facename, style is changed
|
|
// on a font that was original set from complete platform information.
|
|
// It means the ON_Font may or may not have valid settings in platform specific
|
|
// fields. In particular the Apple font name is typically no longer valid.
|
|
if (ON_Font::Origin::Unset != m_font_origin && ON_Font::Origin::Unknown != m_font_origin)
|
|
m_font_origin = ON_Font::Origin::Unknown;
|
|
m_panose1 = ON_PANOSE1::Zero;
|
|
m_simulated = 0;
|
|
m_font_glyph_cache.reset();
|
|
}
|
|
void ON_Font::Internal_ClearAllNames()
|
|
{
|
|
Internal_ClearName(true, true, true, true);
|
|
}
|
|
|
|
void ON_Font::Internal_ClearName(
|
|
bool bClearFamilyName,
|
|
bool bClearFaceName,
|
|
bool bClearPostScriptName,
|
|
bool bClearWindowsLogfontName
|
|
)
|
|
{
|
|
if (bClearFamilyName)
|
|
{
|
|
m_loc_family_name = ON_wString::EmptyString;
|
|
m_en_family_name = ON_wString::EmptyString;
|
|
}
|
|
if (bClearFaceName)
|
|
{
|
|
m_loc_face_name = ON_wString::EmptyString;
|
|
m_en_face_name = ON_wString::EmptyString;
|
|
}
|
|
if (bClearPostScriptName)
|
|
{
|
|
m_loc_postscript_name = ON_wString::EmptyString;
|
|
m_en_postscript_name = ON_wString::EmptyString;
|
|
}
|
|
if (bClearWindowsLogfontName)
|
|
{
|
|
m_loc_windows_logfont_name = ON_wString::EmptyString;
|
|
m_en_windows_logfont_name = ON_wString::EmptyString;
|
|
}
|
|
}
|
|
|
|
|
|
bool ON_Font::Internal_SetFontWeightTrio(
|
|
ON_Font::Weight font_weight,
|
|
int windows_logfont_weight,
|
|
double apple_font_weight_trait,
|
|
bool bUpdateFontDescription
|
|
)
|
|
{
|
|
if (ON_Font::Weight::Unset == font_weight)
|
|
{
|
|
if (windows_logfont_weight > 0 && windows_logfont_weight <= 1000)
|
|
{
|
|
font_weight = ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight);
|
|
}
|
|
else if (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0)
|
|
{
|
|
font_weight = ON_Font::WeightFromAppleFontWeightTrait(apple_font_weight_trait);
|
|
}
|
|
}
|
|
|
|
font_weight = ON_Font::FontWeightFromUnsigned((unsigned int)static_cast<unsigned char>(font_weight));
|
|
if (ON_Font::Weight::Unset == font_weight)
|
|
return false;
|
|
|
|
if (false == (1 <= windows_logfont_weight && windows_logfont_weight <= 1000))
|
|
windows_logfont_weight = ON_Font::WindowsLogfontWeightFromWeight(font_weight);
|
|
|
|
if (false == (-1.0 <= apple_font_weight_trait && apple_font_weight_trait <= 1.0) )
|
|
apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(font_weight);
|
|
|
|
if (
|
|
font_weight != m_font_weight
|
|
|| false == (apple_font_weight_trait == m_apple_font_weight_trait) // handles nans correctly
|
|
|| windows_logfont_weight != m_windows_logfont_weight
|
|
)
|
|
{
|
|
if (false == ON_FONT_MODIFICATION_PERMITTED)
|
|
return false;
|
|
|
|
if (font_weight != m_font_weight)
|
|
{
|
|
m_font_weight = font_weight;
|
|
}
|
|
|
|
if (false == (apple_font_weight_trait == m_apple_font_weight_trait))
|
|
m_apple_font_weight_trait = apple_font_weight_trait;
|
|
|
|
if (windows_logfont_weight != m_windows_logfont_weight)
|
|
m_windows_logfont_weight = windows_logfont_weight;
|
|
|
|
Internal_AfterModification();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ON_Font::SetFontWeight(ON_Font::Weight font_weight)
|
|
{
|
|
return Internal_SetFontWeightTrio(
|
|
font_weight,
|
|
-1,
|
|
ON_UNSET_VALUE,
|
|
font_weight != m_font_weight
|
|
);
|
|
}
|
|
|
|
|
|
bool ON_Font::SetWindowsLogfontWeight(
|
|
int windows_logfont_weight
|
|
)
|
|
{
|
|
const ON_Font::Weight font_weight = ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight);
|
|
return Internal_SetFontWeightTrio(
|
|
font_weight,
|
|
windows_logfont_weight,
|
|
ON_UNSET_VALUE,
|
|
windows_logfont_weight != m_windows_logfont_weight
|
|
);
|
|
}
|
|
|
|
int ON_Font::WindowsLogfontWeight() const
|
|
{
|
|
return
|
|
(100 <= m_windows_logfont_weight && m_windows_logfont_weight <= 1000)
|
|
? m_windows_logfont_weight
|
|
: ON_Font::WindowsLogfontWeightFromWeight(FontWeight());
|
|
}
|
|
|
|
bool ON_Font::SetAppleWeightOfFont(
|
|
int apple_weight_of_font
|
|
)
|
|
{
|
|
const ON_Font::Weight font_weight = ON_Font::WeightFromAppleWeightOfFont(apple_weight_of_font);
|
|
double apple_font_weight_trait = ON_Font::AppleFontWeightTraitFromWeight(font_weight);
|
|
if (0 <= apple_weight_of_font && apple_weight_of_font <= 9)
|
|
{
|
|
apple_font_weight_trait = (1.0 - apple_weight_of_font) / 7.5;
|
|
if (apple_font_weight_trait < -1.0)
|
|
apple_font_weight_trait = -1.0;
|
|
else if (apple_font_weight_trait > 1.0)
|
|
apple_font_weight_trait = 1.0;
|
|
}
|
|
return Internal_SetFontWeightTrio(
|
|
font_weight,
|
|
-1,
|
|
apple_font_weight_trait,
|
|
PostScriptName().IsEmpty()
|
|
);
|
|
}
|
|
|
|
int ON_Font::AppleWeightOfFont() const
|
|
{
|
|
return WindowsLogfontWeight() / 100;
|
|
}
|
|
|
|
bool ON_Font::SetAppleFontWeightTrait(
|
|
double apple_font_weight_trait
|
|
)
|
|
{
|
|
if (false == ON_IsValid(apple_font_weight_trait))
|
|
return false; // nan or unset value
|
|
|
|
const ON_Font::Weight font_weight = ON_Font::WeightFromAppleFontWeightTrait(apple_font_weight_trait);
|
|
|
|
return Internal_SetFontWeightTrio(
|
|
font_weight,
|
|
-1,
|
|
apple_font_weight_trait,
|
|
PostScriptName().IsEmpty()
|
|
);
|
|
}
|
|
|
|
double ON_Font::AppleFontWeightTrait() const
|
|
{
|
|
return
|
|
(m_apple_font_weight_trait >= -1.0 && m_apple_font_weight_trait <= 1.0)
|
|
? m_apple_font_weight_trait
|
|
: ON_Font::AppleFontWeightTraitFromWeight(this->FontWeight());
|
|
}
|
|
|
|
bool ON_Font::IsValidPointSize(
|
|
double point_size
|
|
)
|
|
{
|
|
return (point_size > 0.0 && point_size < 1.0e10);
|
|
}
|
|
|
|
double ON_Font::PointSize() const
|
|
{
|
|
return m_point_size;
|
|
}
|
|
|
|
bool ON_Font::SetPointSize(
|
|
double point_size
|
|
)
|
|
{
|
|
double x
|
|
= ON_Font::IsValidPointSize(point_size)
|
|
? point_size
|
|
: 0.0;
|
|
if (!(m_point_size == x))
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
m_point_size = x;
|
|
// Do not call Internal_AfterModification() here - the source of the fundamental font definition is the same.
|
|
m_font_characteristics_hash = ON_SHA1_Hash::ZeroDigest;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ON_Font::Style ON_Font::FontStyle() const
|
|
{
|
|
return m_font_style;
|
|
}
|
|
|
|
bool ON_Font::SetFontStyle(
|
|
ON_Font::Style font_style
|
|
)
|
|
{
|
|
if (m_font_style != font_style)
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
|
|
m_font_style = font_style;
|
|
Internal_AfterModification();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ON_Font::Stretch ON_Font::FontStretch() const
|
|
{
|
|
return m_font_stretch;
|
|
}
|
|
|
|
bool ON_Font::SetFontStretch(
|
|
ON_Font::Stretch font_stretch
|
|
)
|
|
{
|
|
if (m_font_stretch != font_stretch)
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
|
|
m_font_stretch = font_stretch;
|
|
Internal_AfterModification();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_Font::IsUnderlined() const
|
|
{
|
|
return m_font_bUnderlined;
|
|
}
|
|
|
|
bool ON_Font::SetUnderlined(bool bUnderlined)
|
|
{
|
|
if( m_font_bUnderlined != (bUnderlined?true:false))
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
m_font_bUnderlined = (bUnderlined?true:false);
|
|
m_font_characteristics_hash = ON_SHA1_Hash::ZeroDigest;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_Font::IsStrikethrough() const
|
|
{
|
|
return m_font_bStrikethrough;
|
|
}
|
|
|
|
bool ON_Font::SetStrikethrough(bool bStrikethrough)
|
|
{
|
|
if(m_font_bStrikethrough != (bStrikethrough?true:false))
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
m_font_bStrikethrough = (bStrikethrough?true:false);
|
|
m_font_characteristics_hash = ON_SHA1_Hash::ZeroDigest;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ON_Font::IsSymbolFont() const
|
|
{
|
|
return (ON_Font::WindowsConstants::logfont_symbol_charset == m_logfont_charset);
|
|
}
|
|
|
|
const ON_PANOSE1 ON_Font::PANOSE1() const
|
|
{
|
|
return m_panose1;
|
|
}
|
|
|
|
void ON_Font::SetPANOSE1(
|
|
ON_PANOSE1 panose1
|
|
)
|
|
{
|
|
m_panose1 = panose1;
|
|
}
|
|
|
|
unsigned char ON_Font::LogfontCharSet() const
|
|
{
|
|
return m_logfont_charset;
|
|
}
|
|
|
|
bool ON_Font::SetLogfontCharSet(unsigned char logfont_charset)
|
|
{
|
|
if (ON_Font::WindowsConstants::logfont_symbol_charset != logfont_charset)
|
|
logfont_charset = ON_Font::WindowsConstants::logfont_default_charset;
|
|
if(logfont_charset != m_logfont_charset)
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
m_logfont_charset = logfont_charset;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const ON_FontMetrics& ON_Font::FontMetrics() const
|
|
{
|
|
class ON_FontGlyphCache* font_cache = FontGlyphCache(true);
|
|
if (nullptr != font_cache)
|
|
return font_cache->m_normalized_metrics;
|
|
return ON_FontMetrics::Unset;
|
|
}
|
|
|
|
const ON_FontMetrics& ON_Font::FontUnitFontMetrics() const
|
|
{
|
|
class ON_FontGlyphCache* font_cache = FontGlyphCache(true);
|
|
if (nullptr != font_cache)
|
|
return font_cache->m_font_unit_metrics;
|
|
return ON_FontMetrics::Unset;
|
|
}
|
|
|
|
double ON_Font::FontUnitToNormalizedScale() const
|
|
{
|
|
class ON_FontGlyphCache* font_cache = FontGlyphCache(true);
|
|
if (nullptr != font_cache)
|
|
return font_cache->m_font_unit_to_normalized_scale;
|
|
return 1.0;
|
|
}
|
|
|
|
double ON_Font::NormalizedToFontUnitScale() const
|
|
{
|
|
class ON_FontGlyphCache* font_cache = FontGlyphCache(true);
|
|
if (nullptr != font_cache)
|
|
return font_cache->m_normalized_to_font_unit_scale;
|
|
return 1.0;
|
|
}
|
|
|
|
|
|
int ON_Font::HeightOfI() const
|
|
{
|
|
return FontMetrics().AscentOfCapital();
|
|
}
|
|
|
|
int ON_Font::HeightOfLinefeed() const
|
|
{
|
|
return FontMetrics().LineSpace();
|
|
}
|
|
|
|
double ON_Font::HeightScale(double text_height) const
|
|
{
|
|
return FontMetrics().GlyphScale(text_height);
|
|
}
|
|
|
|
int ON_Font::GetStrikeoutSize() const
|
|
{
|
|
return FontMetrics().StrikeoutThickness();
|
|
}
|
|
|
|
int ON_Font::GetStrikeoutPosition() const
|
|
{
|
|
return FontMetrics().StrikeoutPosition();
|
|
}
|
|
|
|
int ON_Font::GetUnderscoreSize() const
|
|
{
|
|
return FontMetrics().UnderscoreThickness();
|
|
}
|
|
|
|
int ON_Font::GetUnderscorePosition() const
|
|
{
|
|
return FontMetrics().UnderscorePosition();
|
|
}
|
|
|
|
const class ON_SHA1_Hash& ON_Font::FontCharacteristicsHash() const
|
|
{
|
|
if (m_font_characteristics_hash.IsZeroDigest())
|
|
{
|
|
ON_SHA1 sha1;
|
|
|
|
sha1.AccumulateUnsigned64(sizeof(*this));
|
|
|
|
ON_SHA1_Hash string_hash;
|
|
const ON_wString windows_logfont_name = WindowsLogfontName();
|
|
if ( windows_logfont_name.IsNotEmpty() )
|
|
{
|
|
string_hash = ON_SHA1_Hash::StringHash(windows_logfont_name);
|
|
sha1.AccumulateSubHash(string_hash);
|
|
}
|
|
sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_font_weight));
|
|
sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_font_style));
|
|
sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_font_stretch));
|
|
sha1.AccumulateBool(m_font_bUnderlined);
|
|
sha1.AccumulateBool(m_font_bStrikethrough);
|
|
sha1.AccumulateUnsigned8(static_cast<ON__UINT8>(m_logfont_charset));
|
|
|
|
const ON_wString family_name = FamilyName();
|
|
if (family_name.IsNotEmpty())
|
|
{
|
|
string_hash = ON_SHA1_Hash::StringHash(family_name);
|
|
sha1.AccumulateSubHash(string_hash);
|
|
}
|
|
|
|
if (m_point_size > 0)
|
|
{
|
|
sha1.AccumulateDouble(m_point_size);
|
|
}
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
sha1.AccumulateInteger32(m_windows_logfont_weight);
|
|
#endif
|
|
|
|
#if defined(ON_RUNTIME_APPLE)
|
|
sha1.AccumulateDouble(m_apple_font_weight_trait);
|
|
const ON_wString postscript_name = PostScriptName();
|
|
if (postscript_name.IsNotEmpty())
|
|
{
|
|
string_hash = ON_SHA1_Hash::StringHash(postscript_name);
|
|
sha1.AccumulateSubHash(string_hash);
|
|
}
|
|
#endif
|
|
|
|
m_font_characteristics_hash = sha1.Hash();
|
|
}
|
|
|
|
return m_font_characteristics_hash;
|
|
}
|
|
|
|
|
|
int ON_Font::CompareFontCharacteristicsForExperts(
|
|
bool bComparePlatformSpecificCharacteristics,
|
|
bool bIgnoreUnsetCharacteristics,
|
|
const ON_Font& lhs,
|
|
const ON_Font& rhs
|
|
)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
|
|
for (;;)
|
|
{
|
|
const ON_wString lhs_family_name = lhs.FamilyName();
|
|
const ON_wString rhs_family_name = rhs.FamilyName();
|
|
const bool bCompareFamilyName =
|
|
false == bIgnoreUnsetCharacteristics
|
|
|| (0 != lhs_family_name.IsNotEmpty() && 0 != rhs_family_name.IsNotEmpty());
|
|
if (bCompareFamilyName)
|
|
{
|
|
i = ON_wString::CompareOrdinal(lhs_family_name, rhs_family_name, true);
|
|
j = 0;
|
|
if ( i != j )
|
|
break;
|
|
}
|
|
|
|
const ON_wString lhs_logfont_name = lhs.WindowsLogfontName();
|
|
const ON_wString rhs_logfont_name = rhs.WindowsLogfontName();
|
|
const bool bCompareWindowsLogfontName =
|
|
false == bIgnoreUnsetCharacteristics
|
|
|| (lhs_logfont_name.IsNotEmpty() && rhs_logfont_name.IsNotEmpty());
|
|
if (bCompareWindowsLogfontName)
|
|
{
|
|
i = ON_wString::CompareOrdinal(lhs_logfont_name, rhs_logfont_name, true);
|
|
j = 0;
|
|
if ( i != j )
|
|
break;
|
|
}
|
|
|
|
const ON_Font::Weight lhs_weight = lhs.m_font_weight;
|
|
const ON_Font::Weight rhs_weight = rhs.m_font_weight;
|
|
const bool bCompareWeight =
|
|
false == bIgnoreUnsetCharacteristics
|
|
|| (ON_Font::Weight::Unset != lhs_weight && ON_Font::Weight::Unset != rhs_weight);
|
|
if (bCompareWeight)
|
|
{
|
|
i = ON_Font::WindowsLogfontWeightFromWeight(lhs_weight);
|
|
j = ON_Font::WindowsLogfontWeightFromWeight(rhs_weight);
|
|
if (i != j)
|
|
break;
|
|
}
|
|
|
|
const ON_Font::Style lhs_style = lhs.m_font_style;
|
|
const ON_Font::Style rhs_style = rhs.m_font_style;
|
|
const bool bCompareStyle =
|
|
false == bIgnoreUnsetCharacteristics
|
|
|| (ON_Font::Style::Unset != lhs_style && ON_Font::Style::Unset != rhs_style);
|
|
if (bCompareStyle)
|
|
{
|
|
i = static_cast<int>(lhs_style);
|
|
j = static_cast<int>(rhs_style);
|
|
if (i != j)
|
|
break;
|
|
}
|
|
|
|
const ON_Font::Stretch lhs_stretch = lhs.m_font_stretch;
|
|
const ON_Font::Stretch rhs_stretch = rhs.m_font_stretch;
|
|
const bool bCompareStretch =
|
|
false == bIgnoreUnsetCharacteristics
|
|
|| (ON_Font::Stretch::Unset != lhs_stretch && ON_Font::Stretch::Unset != rhs_stretch);
|
|
if (bCompareStretch)
|
|
{
|
|
i = static_cast<int>(lhs_stretch);
|
|
j = static_cast<int>(rhs_stretch);
|
|
if (i != j)
|
|
break;
|
|
}
|
|
|
|
i = lhs.m_font_bUnderlined ? 1 : 0;
|
|
j = rhs.m_font_bUnderlined ? 1 : 0;
|
|
if ( i != j )
|
|
break;
|
|
|
|
i = lhs.m_font_bStrikethrough ? 1 : 0;
|
|
j = rhs.m_font_bStrikethrough ? 1 : 0;
|
|
if ( i != j )
|
|
break;
|
|
|
|
const double lhs_point_size = ON_Font::IsValidPointSize(lhs.m_point_size) ? lhs.m_point_size : 0.0;
|
|
const double rhs_point_size = ON_Font::IsValidPointSize(rhs.m_point_size) ? rhs.m_point_size : 0.0;
|
|
j = 0;
|
|
if (lhs_point_size < rhs_point_size)
|
|
i = -1;
|
|
else if (rhs_point_size < lhs_point_size)
|
|
i = 1;
|
|
else
|
|
i = 0;
|
|
if (i != j)
|
|
break;
|
|
|
|
if (false == bComparePlatformSpecificCharacteristics)
|
|
break;
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
i = (ON_Font::WindowsConstants::logfont_symbol_charset == lhs.m_logfont_charset) ? 1 : 0;
|
|
j = (ON_Font::WindowsConstants::logfont_symbol_charset == rhs.m_logfont_charset) ? 1 : 0;
|
|
if ( i != j )
|
|
break;
|
|
#endif
|
|
|
|
|
|
#if defined(ON_RUNTIME_APPLE)
|
|
const ON_wString lhs_postscript_name = lhs.PostScriptName();
|
|
const ON_wString rhs_postscript_name = rhs.PostScriptName();
|
|
const bool bComparePostScriptName =
|
|
false == bIgnoreUnsetCharacteristics
|
|
|| (lhs_postscript_name.IsNotEmpty() && rhs_postscript_name.IsNotEmpty());
|
|
if (bComparePostScriptName)
|
|
{
|
|
i = ON_wString::CompareOrdinal(lhs_postscript_name, rhs_postscript_name, false);
|
|
if (0 == i)
|
|
{
|
|
if (lhs.m_apple_font_weight_trait < rhs.m_apple_font_weight_trait)
|
|
i = -1;
|
|
else if (lhs.m_apple_font_weight_trait > rhs.m_apple_font_weight_trait)
|
|
i = 1;
|
|
}
|
|
j = 0;
|
|
if (i != j)
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
if (i < j)
|
|
return -1;
|
|
if (i > j)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ON_Font::CompareFontCharacteristics(
|
|
const ON_Font& lhs,
|
|
const ON_Font& rhs
|
|
)
|
|
{
|
|
const bool bComparePlatformSpecificCharacteristics = true;
|
|
const bool bIgnoreUnsetCharacteristics = false;
|
|
return ON_Font::CompareFontCharacteristicsForExperts(
|
|
bComparePlatformSpecificCharacteristics,
|
|
bIgnoreUnsetCharacteristics,
|
|
lhs,
|
|
rhs
|
|
);
|
|
}
|
|
|
|
bool ON_Font::EqualFontCharacteristics(
|
|
const ON_Font& a,
|
|
const ON_Font& b
|
|
)
|
|
{
|
|
return (0 == ON_Font::CompareFontCharacteristics(a,b));
|
|
}
|
|
|
|
|
|
ON_Font::Origin ON_Font::FontOriginFromUnsigned(
|
|
unsigned int unsigned_font_origin
|
|
)
|
|
{
|
|
switch (unsigned_font_origin)
|
|
{
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Origin::Unset);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Origin::Unknown);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Origin::WindowsFont);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_Font::Origin::AppleFont);
|
|
};
|
|
|
|
ON_ERROR("Invalid unsigned_font_origin value.");
|
|
return ON_Font::Origin::Unset;
|
|
}
|
|
|
|
ON_Font::Origin ON_Font::FontOrigin() const
|
|
{
|
|
return m_font_origin;
|
|
}
|
|
|
|
void ON_Font::SetFontOrigin(
|
|
ON_Font::Origin font_origin
|
|
)
|
|
{
|
|
m_font_origin = font_origin;
|
|
}
|
|
|
|
bool ON_Font::IsSimulated() const
|
|
{
|
|
return 0 != m_simulated;
|
|
}
|
|
|
|
bool ON_Font::SimulatedWeight() const
|
|
{
|
|
return (0 != (m_simulated & 2));
|
|
}
|
|
|
|
bool ON_Font::SimulatedStretch() const
|
|
{
|
|
return (0 != (m_simulated & 4));
|
|
}
|
|
|
|
bool ON_Font::SimulatedStyle() const
|
|
{
|
|
return (0 != (m_simulated & 8));
|
|
}
|
|
|
|
void ON_Font::SetSimulated(
|
|
bool bSimulatedWeight,
|
|
bool bSimulatedStretch,
|
|
bool bSimulatedStyle,
|
|
bool bSimulatedOther
|
|
)
|
|
{
|
|
ON__UINT8 s = 0;
|
|
if (bSimulatedWeight)
|
|
s |= 2;
|
|
if (bSimulatedStretch)
|
|
s |= 4;
|
|
if (bSimulatedStyle)
|
|
s |= 8;
|
|
if (bSimulatedOther)
|
|
s |= 1;
|
|
m_simulated = s;
|
|
}
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
|
|
static int ON_InternalVerticalPixelScale(
|
|
HDC hdc,
|
|
bool bDCtoLP,
|
|
int y0
|
|
)
|
|
{
|
|
int y1 = -1;
|
|
|
|
for (;;)
|
|
{
|
|
POINT origin{ 0,0 };
|
|
POINT pt{ 0, y0 };
|
|
if (bDCtoLP)
|
|
{
|
|
if (0 == ::DPtoLP(hdc, &pt, 1))
|
|
break;
|
|
if (0 == ::DPtoLP(hdc, &origin, 1))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (0 == ::LPtoDP(hdc, &pt, 1))
|
|
break;
|
|
if (0 == ::LPtoDP(hdc, &origin, 1))
|
|
break;
|
|
}
|
|
y1 = std::abs(pt.y - origin.y);
|
|
break;
|
|
}
|
|
|
|
return y1;
|
|
}
|
|
|
|
|
|
bool ON_Font::GetWindowsDeviceToLogicalHeightScales(
|
|
HDC hdc,
|
|
double* device_to_logical_scale,
|
|
double* logical_to_device_scale
|
|
)
|
|
{
|
|
if (nullptr != device_to_logical_scale)
|
|
*device_to_logical_scale = 1.0;
|
|
if (nullptr != logical_to_device_scale)
|
|
*logical_to_device_scale = 1.0;
|
|
|
|
int device_coordinate = 0;
|
|
int logical_coordinate = 0;
|
|
|
|
// see which is larger, device or logical
|
|
const int thousand = 1000;
|
|
const int device_to_logical_1000x = ON_InternalVerticalPixelScale(hdc, true, thousand);
|
|
const int logical_to_device_1000x = ON_InternalVerticalPixelScale(hdc, false, thousand);
|
|
if (device_to_logical_1000x <= 0 && logical_to_device_1000x <= 0)
|
|
{
|
|
ON_ERROR("DPtoLP(hdc, ...) and LPtoDP(hdc, ...) failed.");
|
|
return false;
|
|
}
|
|
|
|
if (thousand == device_to_logical_1000x && thousand == logical_to_device_1000x)
|
|
{
|
|
// vertical logical pixels are the same size as device pixels
|
|
device_coordinate = 1;
|
|
logical_coordinate = 1;
|
|
}
|
|
else
|
|
{
|
|
// bDCtoLP = true if logical pixels are smaller than device pixels
|
|
const bool bDCtoLP = (device_to_logical_1000x >= logical_to_device_1000x);
|
|
const int s1000 = bDCtoLP ? device_to_logical_1000x : logical_to_device_1000x;
|
|
int y0 = 0;
|
|
int y1 = 0;
|
|
const int s1024 = ON_InternalVerticalPixelScale(hdc, bDCtoLP, 1024);
|
|
for (y0 = 1; y0 <= 100; y0++)
|
|
{
|
|
y1 = ON_InternalVerticalPixelScale(hdc, bDCtoLP, y0);
|
|
if (y0*s1000 == y1*thousand || y0*s1024 == y1*1024)
|
|
{
|
|
break;
|
|
}
|
|
y1 = 0;
|
|
}
|
|
if (0 == y1)
|
|
{
|
|
y0 = thousand;
|
|
y1 = s1000;
|
|
}
|
|
if (bDCtoLP)
|
|
{
|
|
device_coordinate = y0;
|
|
logical_coordinate = y1;
|
|
}
|
|
else
|
|
{
|
|
device_coordinate = y1;
|
|
logical_coordinate = y0;
|
|
}
|
|
}
|
|
|
|
if (nullptr != device_to_logical_scale)
|
|
{
|
|
*device_to_logical_scale = ((double)logical_coordinate) / ((double)device_coordinate);
|
|
}
|
|
|
|
if (nullptr != logical_to_device_scale)
|
|
{
|
|
*logical_to_device_scale = ((double)device_coordinate) / ((double)logical_coordinate);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
HDC ON_Font::CreateWindowsLogfontDeviceContext()
|
|
{
|
|
return ::CreateCompatibleDC(nullptr);
|
|
}
|
|
|
|
void ON_Font::DeleteWindowsLogfontDeviceContext(
|
|
HDC hdc
|
|
)
|
|
{
|
|
if ( nullptr != hdc )
|
|
::DeleteDC(hdc);
|
|
}
|
|
|
|
bool ON_Font::GetWindowsTextMetrics(
|
|
int map_mode,
|
|
HDC hdc,
|
|
const LOGFONT& logfont,
|
|
TEXTMETRIC& textmetric
|
|
)
|
|
{
|
|
bool rc = false;
|
|
|
|
TEXTMETRIC tm;
|
|
memset(&tm, 0, sizeof(tm));
|
|
textmetric = tm;
|
|
|
|
// need to get TEXTMETRICS tmInternalLeading
|
|
const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX);
|
|
const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode);
|
|
HDC font_hdc = nullptr;
|
|
|
|
const int savedDC
|
|
= (nullptr != hdc)
|
|
? ::SaveDC(hdc)
|
|
: 0;
|
|
|
|
HFONT hfont = 0;
|
|
|
|
for (;;)
|
|
{
|
|
if (nullptr == hdc)
|
|
{
|
|
font_hdc = ON_Font::CreateWindowsLogfontDeviceContext();
|
|
hdc = font_hdc;
|
|
if (nullptr == hdc)
|
|
break;
|
|
}
|
|
|
|
const int hdc_mm = ::GetMapMode(hdc);
|
|
|
|
int mm
|
|
= bValidMapModeParameter
|
|
? map_mode
|
|
: ((nullptr != hdc) ? hdc_mm : 0);
|
|
|
|
const bool bValidMM = (mm >= MM_MIN && mm <= MM_MAX);
|
|
|
|
if (false == bValidMM)
|
|
{
|
|
if (ON_Font::MAP_MODE_ZERO_ERROR_SUPPRESS != map_mode)
|
|
{
|
|
ON_ERROR("GetMapMode(hdc) failed.");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (hdc_mm != mm && bValidMM)
|
|
{
|
|
::SetMapMode(hdc, mm);
|
|
}
|
|
|
|
hfont = ::CreateFontIndirect(&logfont);
|
|
if (0 == hfont)
|
|
break;
|
|
|
|
::SelectObject(hdc, hfont);
|
|
|
|
if (0 == ::GetTextMetrics(hdc, &tm))
|
|
{
|
|
if (ON_Font::MAP_MODE_ZERO_ERROR_SUPPRESS != map_mode)
|
|
{
|
|
ON_ERROR("GetTextMetrics(hdc,...) failed.");
|
|
}
|
|
break;
|
|
}
|
|
|
|
textmetric = tm;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (nullptr != font_hdc)
|
|
ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc);
|
|
else if ( nullptr != hdc)
|
|
::RestoreDC(hdc, savedDC);
|
|
|
|
if (0 != hfont)
|
|
::DeleteObject(hfont);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int ON_Font::WindowsLogfontCharacterHeight(
|
|
int map_mode,
|
|
HDC hdc,
|
|
const LOGFONT& logfont
|
|
)
|
|
{
|
|
if (logfont.lfHeight <= 0)
|
|
return logfont.lfHeight;
|
|
|
|
int logfont_lfHeight = 0;
|
|
|
|
for (;;)
|
|
{
|
|
TEXTMETRIC tm;
|
|
memset(&tm, 0, sizeof(tm));
|
|
bool bHaveTextMetric = ON_Font::GetWindowsTextMetrics(
|
|
map_mode,
|
|
hdc,
|
|
logfont,
|
|
tm
|
|
);
|
|
|
|
if (false == bHaveTextMetric)
|
|
{
|
|
if (ON_Font::MAP_MODE_ZERO_ERROR_SUPPRESS != map_mode)
|
|
{
|
|
ON_ERROR("ON_Font::GetWindowsTextMetrics...) failed.");
|
|
}
|
|
break;
|
|
}
|
|
|
|
const int cellHeight = tm.tmAscent + tm.tmDescent;
|
|
const int characterHeight = cellHeight - tm.tmInternalLeading;
|
|
if (cellHeight <= 0 || characterHeight <= 0)
|
|
{
|
|
ON_ERROR("tm.tmInternalLeading >= cellHeight.");
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
// logfont_lfHeight is negative to indicate it is character height.
|
|
// (positive values are cell height)
|
|
logfont_lfHeight = -characterHeight;
|
|
break;
|
|
}
|
|
|
|
return logfont_lfHeight;
|
|
}
|
|
|
|
int ON_Font::WindowsLogfontCellHeight(
|
|
int map_mode,
|
|
HDC hdc,
|
|
const LOGFONT& logfont
|
|
)
|
|
{
|
|
if (logfont.lfHeight >= 0)
|
|
return logfont.lfHeight;
|
|
|
|
int logfont_lfHeight = 0;
|
|
|
|
for (;;)
|
|
{
|
|
TEXTMETRIC tm;
|
|
memset(&tm, 0, sizeof(tm));
|
|
bool bHaveTextMetric = ON_Font::GetWindowsTextMetrics(
|
|
map_mode,
|
|
hdc,
|
|
logfont,
|
|
tm
|
|
);
|
|
|
|
if (false == bHaveTextMetric)
|
|
{
|
|
ON_ERROR("ON_Font::GetWindowsTextMetrics...) failed.");
|
|
break;
|
|
}
|
|
|
|
const int cellHeight = tm.tmAscent + tm.tmDescent;
|
|
if (cellHeight <= 0)
|
|
{
|
|
ON_ERROR("tm.tmAscent + tm.tmDescent < 0.");
|
|
break;
|
|
}
|
|
|
|
// logfont_lfHeight is positive to indicate it is cell height.
|
|
// (negative values are character height)
|
|
logfont_lfHeight = cellHeight;
|
|
break;
|
|
}
|
|
|
|
return logfont_lfHeight;
|
|
}
|
|
|
|
|
|
int ON_Font::WindowsLogfontCharacterHeightFromPointSize(
|
|
int map_mode,
|
|
HDC hdc,
|
|
double point_size
|
|
)
|
|
{
|
|
int logfont_lfHeight = 0;
|
|
|
|
const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX);
|
|
const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode);
|
|
HDC font_hdc = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
if (0 == point_size)
|
|
break;
|
|
|
|
if (!(point_size > 0.0 && point_size < 21474836.47))
|
|
{
|
|
ON_ERROR("Invalid point_size parameter.");
|
|
break;
|
|
}
|
|
|
|
if (bNeedDeviceContext && nullptr == hdc)
|
|
{
|
|
font_hdc = ON_Font::CreateWindowsLogfontDeviceContext();
|
|
hdc = font_hdc;
|
|
if (nullptr == hdc)
|
|
break;
|
|
}
|
|
|
|
const int mm
|
|
= bValidMapModeParameter
|
|
? map_mode
|
|
: ((nullptr != hdc) ? ::GetMapMode(hdc) : 0);
|
|
|
|
if (mm < MM_MIN || mm > MM_MAX)
|
|
{
|
|
ON_ERROR("GetMapMode(hdc) failed.");
|
|
break;
|
|
}
|
|
|
|
double points_to_lfHeight = 0.0;
|
|
switch (mm)
|
|
{
|
|
case MM_TEXT:
|
|
// Each logical unit is mapped to one device pixel. Positive x is to the right; positive y is down.
|
|
if(nullptr != hdc)
|
|
{
|
|
const int device_pixels_per_logical_inch = ::GetDeviceCaps(hdc, LOGPIXELSY);
|
|
if (device_pixels_per_logical_inch <= 0)
|
|
{
|
|
ON_ERROR("GetDeviceCaps(hdc, LOGPIXELSY) failed.");
|
|
break;
|
|
}
|
|
|
|
double device_to_logical_scale = 1.0;
|
|
if (false == ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, &device_to_logical_scale, nullptr) )
|
|
{
|
|
ON_ERROR("ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, ...) failed.");
|
|
break;
|
|
}
|
|
|
|
// MM_TEXT points_to_lfHeight units
|
|
// = ((device pixel height)/(logical inch)) * (logical pixel height) / ( (points/inch)*(device pixel height) )
|
|
// = ( (logical pixel height) / points ) * (inch / (logical inch))
|
|
points_to_lfHeight = ((static_cast<double>(device_pixels_per_logical_inch)*device_to_logical_scale)) / 72.0;
|
|
}
|
|
break;
|
|
case MM_LOMETRIC:
|
|
// Each logical unit is mapped to 0.1 millimeter. Positive x is to the right; positive y is up.
|
|
// (254 tenths of a mm/inch)/(72 points/inch)
|
|
points_to_lfHeight = 254.0/72.0;
|
|
break;
|
|
case MM_HIMETRIC:
|
|
// Each logical unit is mapped to 0.01 millimeter. Positive x is to the right; positive y is up.
|
|
// (2540 hundredths of a mm/inch)/(72 points/inch)
|
|
points_to_lfHeight = 2540.0/72.0;
|
|
break;
|
|
case MM_LOENGLISH:
|
|
// Each logical unit is mapped to 0.01 inch. Positive x is to the right; positive y is up.
|
|
// (100 hundredths of a inch/inch)/(72 points/inch)
|
|
points_to_lfHeight = 1.0/0.72;
|
|
break;
|
|
case MM_HIENGLISH:
|
|
// Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up.
|
|
// (1000 thousandths of a inch/inch)/(72 points/inch)
|
|
points_to_lfHeight = 1.0/0.072;
|
|
break;
|
|
case MM_TWIPS:
|
|
// Each logical unit is mapped to one twentieth of a printer's point (1/1440 inch, also called a "twip"). Positive x is to the right; positive y is up.
|
|
// 20 twips / point
|
|
points_to_lfHeight = 20.0;
|
|
break;
|
|
case MM_ISOTROPIC:
|
|
// Logical units are mapped to arbitrary units with equally scaled axes;
|
|
// that is, one unit along the x-axis is equal to one unit along the y-axis.
|
|
// Use the SetWindowExtEx and SetViewportExtEx functions to specify the units
|
|
// and the orientation of the axes. Graphics device interface makes adjustments
|
|
// as necessary to ensure the x and y units remain the same size.
|
|
// (When the windows extent is set, the viewport will be adjusted to keep
|
|
// the units isotropic).
|
|
ON_ERROR("GetMapMode(hdc) returned MM_ISOTROPIC mapping mode.");
|
|
break;
|
|
case MM_ANISOTROPIC:
|
|
// Logical units are mapped to arbitrary units with arbitrarily scaled axes.
|
|
// Use the SetWindowExtEx and SetViewportExtEx functions to specify the units,
|
|
// orientation, and scaling required.
|
|
ON_ERROR("GetMapMode(hdc) returned MM_ANISOTROPIC mapping mode.");
|
|
break;
|
|
default:
|
|
ON_ERROR("GetMapMode(hdc) returned undocumented mapping mode.");
|
|
}
|
|
|
|
if (0 != points_to_lfHeight)
|
|
{
|
|
logfont_lfHeight = -static_cast<int>(fabs(points_to_lfHeight*point_size + 0.5));
|
|
if (0 == logfont_lfHeight)
|
|
logfont_lfHeight = -1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (nullptr != font_hdc)
|
|
{
|
|
ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc);
|
|
}
|
|
|
|
return logfont_lfHeight;
|
|
}
|
|
|
|
double ON_Font::PointSizeFromWindowsLogfontCharacterHeight(
|
|
int map_mode,
|
|
HDC hdc,
|
|
int logfont_character_height
|
|
)
|
|
{
|
|
double point_size = 0.0;
|
|
|
|
const bool bValidMapModeParameter = (map_mode >= MM_MIN && map_mode <= MM_MAX);
|
|
|
|
const bool bNeedDeviceContext = (false == bValidMapModeParameter || MM_TEXT == map_mode);
|
|
|
|
HDC font_hdc = 0;
|
|
|
|
// Windows LOGFONT.lfHeight is negative when
|
|
// the value is a character height. This
|
|
// function allow the user to pass in a positive value.
|
|
const int logfont_lfHeight
|
|
= (logfont_character_height <= 0)
|
|
? logfont_character_height
|
|
: -logfont_character_height;
|
|
|
|
for (;;)
|
|
{
|
|
if (logfont_lfHeight >= 0)
|
|
break;
|
|
|
|
if (bNeedDeviceContext && nullptr == hdc )
|
|
{
|
|
font_hdc = ON_Font::CreateWindowsLogfontDeviceContext();
|
|
hdc = font_hdc;
|
|
if (nullptr == hdc)
|
|
break;
|
|
}
|
|
|
|
const int mm
|
|
= bValidMapModeParameter
|
|
? map_mode
|
|
: ((nullptr != hdc) ? ::GetMapMode(hdc) : 0);
|
|
|
|
if (mm < MM_MIN || mm > MM_MAX)
|
|
{
|
|
ON_ERROR("GetMapMode(hdc) failed.");
|
|
break;
|
|
}
|
|
|
|
double lfHeight_to_points = 0.0;
|
|
switch (mm)
|
|
{
|
|
case MM_TEXT:
|
|
// Each logical unit is mapped to one device pixel. Positive x is to the right; positive y is down.
|
|
if(nullptr != hdc)
|
|
{
|
|
const int device_pixels_per_logical_inch = ::GetDeviceCaps(hdc, LOGPIXELSY);
|
|
if (device_pixels_per_logical_inch <= 0)
|
|
{
|
|
ON_ERROR("GetDeviceCaps(hdc, LOGPIXELSY) failed.");
|
|
break;
|
|
}
|
|
|
|
double logical_to_device_scale = 1.0;
|
|
if (false == ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, nullptr, &logical_to_device_scale))
|
|
{
|
|
ON_ERROR("ON_Font::GetWindowsDeviceToLogicalHeightScales(hdc, ...) failed.");
|
|
break;
|
|
}
|
|
|
|
// MM_TEXT lfHeight_to_points units
|
|
// = ( (points/inch) * (device pixel height) ) / ( ((device pixel height)/(logical inch)) * (logical pixel height) )
|
|
// = ( points / (logical pixel height) ) * ( (logical inch)/inch )
|
|
lfHeight_to_points = (72.0*logical_to_device_scale) / static_cast<double>(device_pixels_per_logical_inch);
|
|
}
|
|
break;
|
|
case MM_LOMETRIC:
|
|
// Each logical unit is mapped to 0.1 millimeter. Positive x is to the right; positive y is up.
|
|
// 72 points/inch / (254 tenths of a mm/inch)
|
|
lfHeight_to_points = 72.0 / 254.0;
|
|
break;
|
|
case MM_HIMETRIC:
|
|
// Each logical unit is mapped to 0.01 millimeter. Positive x is to the right; positive y is up.
|
|
// 72 points/inch / (2540 hundredths of a mm/inch)
|
|
lfHeight_to_points = 72.0 / 2540.0;
|
|
break;
|
|
case MM_LOENGLISH:
|
|
// Each logical unit is mapped to 0.01 inch. Positive x is to the right; positive y is up.
|
|
// 72 points/inch / (100 hundredths of a inch/inch)
|
|
lfHeight_to_points = 0.72;
|
|
break;
|
|
case MM_HIENGLISH:
|
|
// Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up.
|
|
// 72 points/inch / (1000 thousandths of a inch/inch)
|
|
lfHeight_to_points = 0.072;
|
|
break;
|
|
case MM_TWIPS:
|
|
// Each logical unit is mapped to one twentieth of a printer's point (1/1440 inch, also called a "twip"). Positive x is to the right; positive y is up.
|
|
// 1 point / 20 twips
|
|
lfHeight_to_points = 1.0/20.0;
|
|
break;
|
|
case MM_ISOTROPIC:
|
|
// Logical units are mapped to arbitrary units with equally scaled axes;
|
|
// that is, one unit along the x-axis is equal to one unit along the y-axis.
|
|
// Use the SetWindowExtEx and SetViewportExtEx functions to specify the units
|
|
// and the orientation of the axes. Graphics device interface makes adjustments
|
|
// as necessary to ensure the x and y units remain the same size.
|
|
// (When the windows extent is set, the viewport will be adjusted to keep
|
|
// the units isotropic).
|
|
ON_ERROR("GetMapMode(hdc) returned MM_ISOTROPIC mapping mode.");
|
|
break;
|
|
case MM_ANISOTROPIC:
|
|
// Logical units are mapped to arbitrary units with arbitrarily scaled axes.
|
|
// Use the SetWindowExtEx and SetViewportExtEx functions to specify the units,
|
|
// orientation, and scaling required.
|
|
ON_ERROR("GetMapMode(hdc) returned MM_ANISOTROPIC mapping mode.");
|
|
break;
|
|
default:
|
|
ON_ERROR("GetMapMode(hdc) returned undocumented mapping mode.");
|
|
}
|
|
|
|
point_size = fabs(lfHeight_to_points*static_cast<double>(logfont_lfHeight));
|
|
|
|
for (;;)
|
|
{
|
|
// Because LOGFONT lfHeight is an integer and point_size is typically
|
|
// an integer and sometimes a multiple of 0.5, the following rouding
|
|
// is performed so point_size will round trip.
|
|
double i0 = floor(point_size);
|
|
if (i0 == point_size)
|
|
break;
|
|
|
|
if (lfHeight_to_points < 0.5 && 2.0*point_size == floor(2.0*point_size))
|
|
break;
|
|
|
|
double i1 = ceil(point_size);
|
|
if (i1 >= 1.0)
|
|
{
|
|
if (i0 >= 1.0 && fabs(i0 - point_size) < fabs(i1 - point_size) && fabs(i0 - point_size) < lfHeight_to_points)
|
|
{
|
|
if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, i0))
|
|
{
|
|
point_size = i0;
|
|
break;
|
|
}
|
|
}
|
|
else if (fabs(i1 - point_size) < fabs(i0 - point_size) && fabs(i1 - point_size) < lfHeight_to_points)
|
|
{
|
|
if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, i1))
|
|
{
|
|
point_size = i1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lfHeight_to_points < 0.25 && fabs(point_size - 0.5*(i0 + i1)) < lfHeight_to_points)
|
|
{
|
|
if (logfont_lfHeight == ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode, hdc, 0.5*(i0 + i1)))
|
|
{
|
|
point_size = 0.5*(i0 + i1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (nullptr != font_hdc)
|
|
{
|
|
ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc);
|
|
}
|
|
|
|
return point_size;
|
|
}
|
|
|
|
bool ON_Font::SetFromWindowsLogFont(
|
|
int map_mode,
|
|
HDC hdc,
|
|
const LOGFONT& dirty_logfont
|
|
)
|
|
{
|
|
if ( false == ON_FONT_MODIFICATION_PERMITTED )
|
|
return false;
|
|
|
|
*this = ON_Font::Unset;
|
|
|
|
const bool bUnderlined = (0 != dirty_logfont.lfUnderline) ? true : false;
|
|
const bool bStrikethrough = (0 != dirty_logfont.lfStrikeOut) ? true : false;
|
|
|
|
LOGFONT logfont = dirty_logfont;
|
|
logfont.lfWidth = 0;
|
|
logfont.lfEscapement = 0;
|
|
logfont.lfOrientation = 0;
|
|
logfont.lfUnderline = 0;
|
|
logfont.lfStrikeOut = 0;
|
|
logfont.lfCharSet = ON_Font::WindowsLogfontCharSetFromLogfont(&logfont, true);
|
|
logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis;
|
|
logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
logfont.lfQuality = ON_Font::WindowsConstants::logfont_quality;
|
|
logfont.lfPitchAndFamily = ON_Font::WindowsConstants::logfont_pitch_and_family;
|
|
int face_name_capacity = (int)((sizeof(logfont.lfFaceName) / sizeof(logfont.lfFaceName[0])));
|
|
for (int i = 0; i < face_name_capacity; i++)
|
|
{
|
|
if (0 == logfont.lfFaceName[i])
|
|
{
|
|
for (i++; i < face_name_capacity; i++)
|
|
logfont.lfFaceName[i] = 0;
|
|
}
|
|
}
|
|
logfont.lfFaceName[face_name_capacity-1] = 0;
|
|
|
|
// Calculate point size
|
|
// If logfont.lfHeight > 0, +lfHeight = cell height.
|
|
// If logfont.lfHeight < 0, -lfHeight = character height = cell height - TEXTMETRIC.tmInternalLeading.
|
|
// PointSize() calculations require a character height.
|
|
const int logfont_lfHeight
|
|
= (0 != logfont.lfHeight)
|
|
? ON_Font::WindowsLogfontCharacterHeight(map_mode, hdc, logfont)
|
|
: 0;
|
|
|
|
const double point_size
|
|
= (logfont_lfHeight < 0)
|
|
? ON_Font::PointSizeFromWindowsLogfontCharacterHeight(map_mode, hdc, logfont_lfHeight)
|
|
: 0.0;
|
|
|
|
logfont.lfHeight = ON_Font::Constants::AnnotationFontCellHeight;
|
|
|
|
// TODO - use DWrite gdi interop to get an IDWriteFont and use it to set the ON_Font.
|
|
|
|
const ON_Font::Weight font_weight = ON_Font::WeightFromWindowsLogfontWeight(logfont.lfWeight);
|
|
const ON_Font::Style font_style = (0 != logfont.lfItalic) ? ON_Font::Style::Italic : ON_Font::Style::Upright;
|
|
const ON_Font::Stretch font_stretch = ON_Font::Stretch::Medium;
|
|
const double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio;
|
|
|
|
bool rc = SetFontCharacteristics(
|
|
point_size,
|
|
logfont.lfFaceName,
|
|
font_weight,
|
|
font_style,
|
|
font_stretch,
|
|
bUnderlined,
|
|
bStrikethrough,
|
|
linefeed_ratio,
|
|
logfont.lfCharSet
|
|
);
|
|
|
|
if (rc)
|
|
{
|
|
rc = Internal_SetFontWeightTrio(
|
|
font_weight,
|
|
logfont.lfWeight,
|
|
ON_UNSET_VALUE,
|
|
false
|
|
);
|
|
}
|
|
|
|
// Do last because other setters sometimes unset the orgin.
|
|
SetFontOrigin(ON_Font::Origin::WindowsFont);
|
|
|
|
return rc;
|
|
}
|
|
|
|
const LOGFONT ON_Font::WindowsLogFont(
|
|
int map_mode,
|
|
HDC hdc
|
|
) const
|
|
{
|
|
LOGFONT logfont;
|
|
|
|
memset(&logfont, 0, sizeof(LOGFONT));
|
|
|
|
logfont.lfHeight
|
|
= (m_point_size > 0.0)
|
|
? ON_Font::WindowsLogfontCharacterHeightFromPointSize(map_mode,hdc,m_point_size)
|
|
: ON_Font::Constants::AnnotationFontCellHeight;
|
|
|
|
logfont.lfWidth = 0;
|
|
logfont.lfEscapement = 0;
|
|
logfont.lfOrientation = 0;
|
|
|
|
const ON_Font::Weight font_weight_from_windows_logfont_weight = ON_Font::WeightFromWindowsLogfontWeight(m_windows_logfont_weight);
|
|
logfont.lfWeight
|
|
= (m_windows_logfont_weight > 0 && m_windows_logfont_weight <= 1000 && font_weight_from_windows_logfont_weight == m_font_weight)
|
|
? m_windows_logfont_weight
|
|
: ON_Font::WindowsLogfontWeightFromWeight(m_font_weight);
|
|
|
|
logfont.lfItalic = (ON_Font::Style::Italic == m_font_style) ? 1 : 0;
|
|
logfont.lfUnderline = m_font_bUnderlined ? 1 : 0;
|
|
logfont.lfStrikeOut = m_font_bStrikethrough ? 1 : 0;
|
|
logfont.lfCharSet = m_logfont_charset;
|
|
logfont.lfOutPrecision = ON_Font::WindowsConstants::logfont_out_precis;
|
|
logfont.lfClipPrecision = 0;
|
|
logfont.lfQuality = ON_Font::WindowsConstants::logfont_quality;
|
|
logfont.lfPitchAndFamily = ON_Font::WindowsConstants::logfont_pitch_and_family;
|
|
|
|
const ON_wString windows_logfont_name = WindowsLogfontName();
|
|
const int windows_logfont_name_len = windows_logfont_name.Length();
|
|
const wchar_t* s = static_cast<const wchar_t*>(windows_logfont_name);
|
|
|
|
for(int i = 0; i < windows_logfont_name_len && i < LF_FACESIZE; i++)
|
|
logfont.lfFaceName[i] = s[i];
|
|
logfont.lfFaceName[LF_FACESIZE - 1] = 0;
|
|
|
|
return logfont;
|
|
}
|
|
|
|
const MAT2 ON_Font::WindowsFontMat2() const
|
|
{
|
|
MAT2 mat2;
|
|
memset(&mat2, 0, sizeof(mat2));
|
|
|
|
mat2.eM11.fract = 0;
|
|
mat2.eM11.value = 1;
|
|
|
|
mat2.eM12.fract = 0;
|
|
mat2.eM12.value = 0;
|
|
|
|
mat2.eM21.fract = 0;
|
|
mat2.eM21.value = 0;
|
|
|
|
mat2.eM22.fract = 0;
|
|
mat2.eM22.value = 1;
|
|
|
|
double eM11 = 1.0;
|
|
|
|
switch (FontStretch())
|
|
{
|
|
case ON_Font::Stretch::Ultracondensed:
|
|
eM11 = 0.2;
|
|
break;
|
|
case ON_Font::Stretch::Extracondensed:
|
|
eM11 = 0.3;
|
|
break;
|
|
case ON_Font::Stretch::Condensed:
|
|
eM11 = 0.5;
|
|
break;
|
|
case ON_Font::Stretch::Semicondensed:
|
|
eM11 = 0.75;
|
|
break;
|
|
case ON_Font::Stretch::Medium:
|
|
eM11 = 1.0;
|
|
break;
|
|
case ON_Font::Stretch::Semiexpanded:
|
|
eM11 = 1.25;
|
|
break;
|
|
case ON_Font::Stretch::Expanded:
|
|
eM11 = 1.5;
|
|
break;
|
|
case ON_Font::Stretch::Extraexpanded:
|
|
eM11 = 2.0;
|
|
break;
|
|
case ON_Font::Stretch::Ultraexpanded:
|
|
eM11 = 3.0;
|
|
break;
|
|
}
|
|
|
|
if (eM11 > 0.0 && 1.0 != eM11)
|
|
{
|
|
FIXED fx = { 1, 0 };
|
|
double ffrac = fmod(eM11, 1.0);
|
|
if (ffrac != ffrac)
|
|
ffrac = 0.0;
|
|
fx.fract = static_cast<unsigned short>(floor(65535.0 * ffrac));
|
|
fx.value = static_cast<short>(eM11 - ffrac);
|
|
mat2.eM11 = fx;
|
|
}
|
|
|
|
return mat2;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
void ON_Font::DestroyFontGlyphCache()
|
|
{
|
|
m_font_glyph_cache.reset();
|
|
}
|
|
|
|
class ON_FontGlyphCache* ON_Font::FontGlyphCache(
|
|
bool bCreateIfMissing
|
|
) const
|
|
{
|
|
ON_FontGlyphCache* font_cache = m_font_glyph_cache.get();
|
|
if(nullptr == font_cache)
|
|
{
|
|
// get the static ON_Font that matches this one
|
|
const ON_Font* managed_font = this->ManagedFont();
|
|
if(nullptr != managed_font)
|
|
{
|
|
// The first call to ON_Font::GetManagedFont() adds
|
|
// &ON_Font::Default and calculates its font metric
|
|
// and glyph information. The this != managed_font
|
|
// text below prevents a silly copy in this case
|
|
// and in other unanticipated cases.
|
|
if ( this != managed_font)
|
|
m_font_glyph_cache = managed_font->m_font_glyph_cache;
|
|
font_cache = m_font_glyph_cache.get();
|
|
}
|
|
}
|
|
return font_cache;
|
|
}
|
|
|
|
// static
|
|
void ON_Font::GetRunBounds(
|
|
const ON_Font& font,
|
|
const wchar_t* text,
|
|
double font_height_pixels,
|
|
ON::TextHorizontalAlignment horizontal_alignment,
|
|
ON::TextVerticalAlignment vertical_alignment,
|
|
ON_2dPoint& bounds_min,
|
|
ON_2dPoint& bounds_max,
|
|
int& line_count
|
|
)
|
|
{
|
|
line_count = 0;
|
|
bounds_min = ON_2dPoint::Origin;
|
|
bounds_max = ON_2dPoint::Origin;
|
|
|
|
ON_wString run = text;
|
|
int textlength = run.Length();
|
|
if(textlength < 1)
|
|
return;
|
|
|
|
const ON_Font* managed_font = font.ManagedFont();
|
|
if (nullptr == managed_font)
|
|
managed_font = &ON_Font::Default;
|
|
|
|
const ON_FontMetrics& fm = managed_font->FontMetrics();
|
|
|
|
const int height_of_I = fm.AscentOfCapital();
|
|
const int height_of_LF = fm.LineSpace();
|
|
|
|
ON_TextBox text_box;
|
|
line_count = ON_FontGlyph::GetGlyphListBoundingBox(text, managed_font, text_box);
|
|
if ( line_count < 0 || false == text_box.IsSet())
|
|
return;
|
|
|
|
bounds_min.x = text_box.m_bbmin.i;
|
|
bounds_min.y = text_box.m_bbmin.j;
|
|
|
|
bounds_max.x = text_box.m_bbmax.i;
|
|
bounds_max.y = text_box.m_bbmax.j;
|
|
|
|
// in text_box coordinate system (0.0) = left most baseline point of first line.
|
|
const ON_2dPoint first_line_basepoint = ON_2dPoint::Origin;
|
|
const ON_2dPoint last_line_basepoint(first_line_basepoint.x, -(line_count - 1)*height_of_LF);
|
|
|
|
ON_2dVector offset = ON_2dVector::ZeroVector;
|
|
|
|
switch (horizontal_alignment)
|
|
{
|
|
case ON::TextHorizontalAlignment::Left:
|
|
offset.x = -first_line_basepoint.x;
|
|
break;
|
|
|
|
case ON::TextHorizontalAlignment::Center:
|
|
// Text command uses m_max_basepoint (not bbmax)
|
|
offset.x = -first_line_basepoint.x - 0.5*text_box.m_max_basepoint.i;
|
|
break;
|
|
|
|
case ON::TextHorizontalAlignment::Right:
|
|
// Text command uses m_max_basepoint (not bbmax)
|
|
offset.x = -first_line_basepoint.x - text_box.m_max_basepoint.i;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (vertical_alignment)
|
|
{
|
|
case ON::TextVerticalAlignment::Top:
|
|
// (*,0) is at the top of an "I" on the first line.
|
|
offset.y = -first_line_basepoint.y - height_of_I;
|
|
break;
|
|
case ON::TextVerticalAlignment::MiddleOfTop:
|
|
// (*,0) is at the middle of an "I" on the first line.
|
|
offset.y = -first_line_basepoint.y - 0.5*height_of_I;
|
|
break;
|
|
case ON::TextVerticalAlignment::BottomOfTop:
|
|
// (*,0) is at the baseline of the first line.
|
|
offset.y = -first_line_basepoint.y;
|
|
break;
|
|
|
|
case ON::TextVerticalAlignment::Middle:
|
|
{
|
|
// (*,0) is the vertical middle of lines (not vertical middle of glyph bounding box)
|
|
double h = (line_count - 1)*height_of_LF + height_of_I;
|
|
offset.y = -last_line_basepoint.y + 0.5*h;
|
|
}
|
|
break;
|
|
|
|
case ON::TextVerticalAlignment::MiddleOfBottom:
|
|
// (*,0) is at the middle of an "I" on the last line.
|
|
offset.y = -last_line_basepoint.y - 0.5*height_of_I;
|
|
break;
|
|
case ON::TextVerticalAlignment::Bottom:
|
|
// (*,0) is at the basline of the last line.
|
|
offset.y = -last_line_basepoint.y;
|
|
break;
|
|
case ON::TextVerticalAlignment::BottomOfBoundingBox:
|
|
// (*,0) is on the bottom the the glyphs' bounding box
|
|
offset.y = -bounds_min.y;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
bounds_min += offset;
|
|
bounds_max += offset;
|
|
|
|
// scale is applied last.
|
|
const double scale = font_height_pixels / (double)ON_Font::Constants::AnnotationFontCellHeight;
|
|
if (scale > 0.0)
|
|
{
|
|
bounds_min.x = scale * text_box.m_bbmin.i;
|
|
bounds_max.x = scale * text_box.m_bbmax.i;
|
|
bounds_min.y = scale * text_box.m_bbmin.j;
|
|
bounds_max.y = scale * text_box.m_bbmax.j;
|
|
}
|
|
}
|
|
|
|
const ON_FontGlyph* ON_Font::Internal_ManagedCodePointGlyph(
|
|
ON__UINT32 unicode_code_point,
|
|
bool bCreateIfMissing,
|
|
bool bFindSubstitutes
|
|
) const
|
|
{
|
|
if ( false == ON_IsValidUnicodeCodePoint(unicode_code_point) )
|
|
{
|
|
// valid UNICODE codepoint values are <= 0x10FFFF
|
|
ON_ERROR("invalid codepoint parameter.");
|
|
return nullptr;
|
|
}
|
|
|
|
if (false == IsManagedFont())
|
|
{
|
|
ON_ERROR("this->IsManagedFont() must be true.");
|
|
return nullptr;
|
|
}
|
|
|
|
// First see if we already have the glyph in the cache
|
|
ON_FontGlyphCache* font_cache = FontGlyphCache(true);
|
|
ON_GlyphMap* glyph_map
|
|
= (nullptr == font_cache)
|
|
? nullptr
|
|
: font_cache->m_glyphmap.get();
|
|
if (nullptr == glyph_map)
|
|
{
|
|
ON_ERROR("Unable to create ON_FontGlyphCache.");
|
|
return nullptr;
|
|
}
|
|
|
|
const ON_FontGlyph* managed_glyph = glyph_map->FindGlyph(unicode_code_point);
|
|
if (nullptr != managed_glyph)
|
|
return managed_glyph;
|
|
|
|
if (false == bCreateIfMissing)
|
|
return nullptr;
|
|
|
|
ON_FontGlyph glyph;
|
|
glyph.SetCodePoint(this, unicode_code_point);
|
|
|
|
// Call external function to get glyph details
|
|
ON_TextBox font_unit_glyph_box = ON_TextBox::Unset;
|
|
const unsigned int font_glyph_index = ON_ManagedFonts::GetGlyphMetricsInFontDesignUnits(this, unicode_code_point, font_unit_glyph_box);
|
|
if (0 != font_glyph_index)
|
|
{
|
|
if (font_unit_glyph_box.IsSet())
|
|
{
|
|
glyph.m_font_unit_glyph_bbox = font_unit_glyph_box;
|
|
// Save a normalized box as well.
|
|
const double normalize_scale = this->FontUnitToNormalizedScale();
|
|
ON_TextBox normalized_glyph_box = font_unit_glyph_box;
|
|
if (normalize_scale > 0.0)
|
|
normalized_glyph_box = ON_TextBox::Scale(font_unit_glyph_box, normalize_scale);
|
|
glyph.m_normalized_glyph_bbox = normalized_glyph_box;
|
|
}
|
|
glyph.Internal_SetFontGlyphIndex(font_glyph_index);
|
|
bFindSubstitutes = false;
|
|
}
|
|
|
|
|
|
return glyph_map->InsertGlyph(glyph);
|
|
}
|
|
|
|
#if defined(ON_RUNTIME_WIN)
|
|
|
|
static bool ON_Internal_WindowsGetGlyphMetricsEx(
|
|
const ON_Font* font,
|
|
int logfont_height,
|
|
ON__UINT32 unicode_code_point,
|
|
class ON_TextBox& font_unit_glyph_box
|
|
)
|
|
{
|
|
font_unit_glyph_box = ON_TextBox::Unset;
|
|
|
|
if (logfont_height <= 0)
|
|
logfont_height = ON_Font::Constants::AnnotationFontCellHeight;
|
|
|
|
if (false == ON_IsValidUnicodeCodePoint(unicode_code_point) )
|
|
return false;
|
|
if (nullptr == font)
|
|
return false;
|
|
font = font->ManagedFont();
|
|
if (nullptr == font)
|
|
return false;
|
|
|
|
wchar_t w[8] = { 0 };
|
|
const size_t w_capacity = (sizeof(w) / sizeof(w[0])) - 1;
|
|
const int w_count = ON_EncodeWideChar(unicode_code_point, w_capacity, w);
|
|
if ( 0 == w[0] || w_count <= 0 || w_count > (int)w_capacity )
|
|
return false;
|
|
|
|
HDC font_hdc = nullptr;
|
|
HFONT hfont = nullptr;
|
|
HGDIOBJ hfont0 = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
font_hdc = ON_Font::CreateWindowsLogfontDeviceContext();
|
|
if (nullptr == font_hdc)
|
|
break;
|
|
|
|
LOGFONT lf = font->WindowsLogFont(0,nullptr);
|
|
lf.lfHeight = logfont_height;
|
|
hfont = ::CreateFontIndirect(&lf);
|
|
if (nullptr == hfont)
|
|
break;
|
|
hfont0 = ::SelectObject(font_hdc, hfont);
|
|
|
|
ON__UINT16 glyphindex[sizeof(w)/sizeof(w[0])] = { 0 };
|
|
const DWORD gicount = ::GetGlyphIndices(font_hdc, w, w_count, glyphindex, GGI_MARK_NONEXISTING_GLYPHS);
|
|
if (GDI_ERROR == gicount)
|
|
break;
|
|
if (0xffff == glyphindex[0] || 0xffff == glyphindex[1])
|
|
{
|
|
// March 2017 Dale Lear
|
|
// https://mcneel.myjetbrains.com/youtrack/issue/RH-38377
|
|
//
|
|
// The GGI_MARK_NONEXISTING_GLYPHS flag causes GetGlyphIndices()
|
|
// to set glyphindex[] values to 0xFFFF for missing glyphs.
|
|
//
|
|
// GetGlyphIndices() is not capable of getting this glyph in the hdc font.
|
|
// This often happes for surrogate pair encodings, even when the
|
|
// glyph does exist in the font.
|
|
//
|
|
// GetGlyphIndices cannot find glyph in the font
|
|
// Often a surrogate pair and we probably need to be using
|
|
// Windows newer Uniscribe API instead of the tired old
|
|
// GetGlyphIndices + GetGlyphOutlineW stuff.
|
|
break;
|
|
}
|
|
|
|
GLYPHMETRICS glm;
|
|
memset(&glm, 0, sizeof(glm));
|
|
MAT2 mat2 = font->WindowsFontMat2();
|
|
const DWORD cb_size = ::GetGlyphOutlineW(font_hdc, glyphindex[0], GGO_NATIVE | GGO_GLYPH_INDEX, &glm, 0, nullptr, &mat2);
|
|
if (GDI_ERROR == cb_size)
|
|
break;
|
|
|
|
////bool ul = false, so = false;
|
|
////int ulsize = 0, ulpos = 0, sosize = 0, sopos = 0;
|
|
////HGDIOBJ hfont = GetCurrentObject(hdc, OBJ_FONT);
|
|
////LOGFONT lf = { 0 };
|
|
////if (sizeof(lf) == GetObject(hfont, sizeof(lf), &lf))
|
|
////{
|
|
//// ul = lf.lfUnderline ? true : false;
|
|
//// so = lf.lfStrikeOut ? true : false;
|
|
////}
|
|
|
|
////if (ul || so)
|
|
////{
|
|
//// OUTLINETEXTMETRIC otm = { 0 };
|
|
//// if (0 != ::GetOutlineTextMetrics(hdc, sizeof(OUTLINETEXTMETRIC), &otm))
|
|
//// {
|
|
//// sosize = otm.otmsStrikeoutSize;
|
|
//// sopos = otm.otmsStrikeoutPosition;
|
|
//// ulsize = otm.otmsUnderscoreSize;
|
|
//// ulpos = otm.otmsUnderscorePosition;
|
|
//// }
|
|
////}
|
|
|
|
font_unit_glyph_box.m_advance.i = glm.gmCellIncX;
|
|
font_unit_glyph_box.m_advance.j = glm.gmCellIncY;
|
|
if (cb_size == 0)
|
|
{
|
|
// Non-printing char - nothing to draw
|
|
font_unit_glyph_box.m_bbmin.i = 0;
|
|
font_unit_glyph_box.m_bbmin.j = 0;
|
|
font_unit_glyph_box.m_bbmax.i = font_unit_glyph_box.m_advance.i;
|
|
font_unit_glyph_box.m_bbmax.j = font_unit_glyph_box.m_advance.j;
|
|
}
|
|
else
|
|
{
|
|
font_unit_glyph_box.m_bbmin.i = glm.gmptGlyphOrigin.x;
|
|
font_unit_glyph_box.m_bbmin.j = glm.gmptGlyphOrigin.y - (int)glm.gmBlackBoxY;
|
|
font_unit_glyph_box.m_bbmax.i = (int)glm.gmBlackBoxX + glm.gmptGlyphOrigin.x;
|
|
font_unit_glyph_box.m_bbmax.j = glm.gmptGlyphOrigin.y;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (nullptr != font_hdc)
|
|
{
|
|
if (nullptr != hfont)
|
|
{
|
|
::SelectObject(font_hdc, hfont0);
|
|
::DeleteObject(hfont);
|
|
}
|
|
ON_Font::DeleteWindowsLogfontDeviceContext(font_hdc);
|
|
}
|
|
|
|
return font_unit_glyph_box.IsSet();
|
|
}
|
|
|
|
bool ON_WindowsGetGlyphMetrics(
|
|
const ON_Font* font,
|
|
ON__UINT32 unicode_code_point,
|
|
class ON_TextBox& font_unit_glyph_box
|
|
)
|
|
{
|
|
return ON_Internal_WindowsGetGlyphMetricsEx(font, 0, unicode_code_point, font_unit_glyph_box);
|
|
}
|
|
|
|
#endif
|
|
|
|
void ON_Font::SetCustomMeasurementFunctions(
|
|
ON_GetGlyphMetricsFuncType measureGlyphFunc,
|
|
ON_GetFontMetricsFuncType metricsFunc
|
|
)
|
|
{
|
|
ON_Font::Internal_CustomGetGlyphMetricsFunc = measureGlyphFunc;
|
|
ON_Font::Internal_CustomGetFontMetricsFunc = metricsFunc;
|
|
}
|
|
|
|
unsigned int ON_ManagedFonts::GetGlyphMetricsInFontDesignUnits(
|
|
const class ON_Font* font,
|
|
ON__UINT32 unicode_code_point,
|
|
class ON_TextBox& glyph_metrics_in_font_design_units
|
|
)
|
|
{
|
|
unsigned int glyph_index = 0;
|
|
|
|
const ON_FontGlyph g(font, unicode_code_point);
|
|
|
|
if (nullptr != ON_Font::Internal_CustomGetGlyphMetricsFunc)
|
|
{
|
|
glyph_index = ON_Font::Internal_CustomGetGlyphMetricsFunc(&g, glyph_metrics_in_font_design_units);
|
|
if (0 != glyph_index)
|
|
return glyph_index;
|
|
}
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
glyph_index = ON_WindowsDWriteGetGlyphMetrics(&g, glyph_metrics_in_font_design_units);
|
|
#elif defined(ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
|
|
glyph_index = ON_AppleFontGetGlyphMetrics(&g, glyph_metrics_in_font_design_units);
|
|
#elif defined(OPENNURBS_FREETYPE_SUPPORT)
|
|
// Look in opennurbs_system_rumtime.h for the correct place to define OPENNURBS_FREETYPE_SUPPORT.
|
|
// Do NOT define OPENNURBS_FREETYPE_SUPPORT here or in your project setting ("makefile").
|
|
glyph_index = ON_FreeTypeGetGlyphMetrics(&g, glyph_metrics_in_font_design_units);
|
|
#endif
|
|
|
|
if (0 != glyph_index)
|
|
return glyph_index;
|
|
|
|
glyph_metrics_in_font_design_units = ON_TextBox::Unset;
|
|
return 0;
|
|
}
|
|
|
|
bool ON_ManagedFonts::GetFontMetricsInFontDesignUnits(
|
|
const ON_Font* font,
|
|
ON_FontMetrics& font_metrics_in_font_design_units
|
|
)
|
|
{
|
|
if (nullptr == font)
|
|
{
|
|
font_metrics_in_font_design_units = ON_FontMetrics::LastResortMetrics;
|
|
return false;
|
|
}
|
|
|
|
const ON_FontGlyphCache* font_glyph_cache = font->m_font_glyph_cache.get();
|
|
if (nullptr != font_glyph_cache && font_glyph_cache->m_font_unit_metrics.HeightsAreValid())
|
|
{
|
|
if (font_glyph_cache->m_font_unit_metrics.HeightsAreValid())
|
|
{
|
|
font_metrics_in_font_design_units = font_glyph_cache->m_font_unit_metrics;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
ON_FontMetrics fm(ON_FontMetrics::Unset);
|
|
for (;;)
|
|
{
|
|
if (nullptr != ON_Font::Internal_CustomGetFontMetricsFunc)
|
|
{
|
|
ON_Font::Internal_CustomGetFontMetricsFunc(font, fm);
|
|
if (fm.AscentDescentAndUPMAreValid())
|
|
break;
|
|
}
|
|
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
ON_WindowsDWriteGetFontMetrics(font, fm);
|
|
#elif defined(ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
|
|
ON_AppleFontGetFontMetrics(font, fm);
|
|
#elif defined(OPENNURBS_FREETYPE_SUPPORT)
|
|
// Look in opennurbs_system_rumtime.h for the correct place to define OPENNURBS_FREETYPE_SUPPORT.
|
|
// Do NOT define OPENNURBS_FREETYPE_SUPPORT here or in your project setting ("makefile").
|
|
ON_FreeTypeGetFontMetrics(font, fm);
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
|
|
if (false == fm.AscentDescentAndUPMAreValid())
|
|
{
|
|
font_metrics_in_font_design_units = ON_FontMetrics::LastResortMetrics;
|
|
return false;
|
|
}
|
|
|
|
if (fm.HeightsAreValid() && (fm.AscentOfCapital() > 0 || fm.Ascent() <= 0))
|
|
{
|
|
font_metrics_in_font_design_units = fm;
|
|
return true;
|
|
}
|
|
|
|
const int UPM = fm.UPM();
|
|
const int ascent = fm.Ascent();
|
|
const int descent = fm.Descent();
|
|
const int line_space =
|
|
(fm.LineSpace() >= (ascent - descent) && fm.LineSpace() < 0xFFFF)
|
|
? fm.LineSpace()
|
|
: (int)ceil(ON_FontMetrics::DefaultLineFeedRatio*(ascent - descent));
|
|
const int ascent_of_capital = fm.AscentOfCapital();
|
|
const int ascent_of_x = fm.AscentOfx();
|
|
|
|
fm.SetHeights(ascent, descent, UPM, line_space);
|
|
fm.SetAscentOfCapital((ascent_of_capital > 0 && ascent_of_capital <= ascent) ? ascent_of_capital : ascent);
|
|
fm.SetAscentOfx((ascent_of_x > 0 && ascent_of_x <= ascent) ? ascent_of_x : 0);
|
|
|
|
font_metrics_in_font_design_units
|
|
= (fm.HeightsAreValid())
|
|
? fm
|
|
: ON_FontMetrics::LastResortMetrics;
|
|
|
|
return false;
|
|
}
|
|
|
|
const ON_FontList& ON_ManagedFonts::InstalledFonts()
|
|
{
|
|
if (0 == List.m_installed_fonts.Count())
|
|
{
|
|
ON_MemoryAllocationTracking disable_tracking(false);
|
|
ON_SimpleArray<const ON_Font*> device_list;
|
|
#if defined(ON_OS_WINDOWS_GDI)
|
|
ON_ManagedFonts::Internal_GetWindowsInstalledFonts(device_list);
|
|
#elif defined (ON_RUNTIME_APPLE_CORE_TEXT_AVAILABLE)
|
|
ON_ManagedFonts::Internal_GetAppleInstalledCTFonts(device_list);
|
|
#endif
|
|
if (device_list.Count() > 0)
|
|
{
|
|
List.m_installed_fonts.AddFonts(device_list);
|
|
List.m_installed_fonts.Internal_UpdateSortedLists();
|
|
}
|
|
}
|
|
return List.m_installed_fonts;
|
|
}
|