From 5f462fed0d78cd944403bf831ef8a1b23642962d Mon Sep 17 00:00:00 2001 From: Will Pearson Date: Sat, 15 Sep 2018 11:26:15 -0700 Subject: [PATCH] Update source to v6.8.18240.20051 Previous source was actually for 6.1... --- .../example_brep.xcodeproj/project.pbxproj | 4 +- .../example_convert.xcodeproj/project.pbxproj | 4 +- .../example_read.xcodeproj/project.pbxproj | 4 +- .../project.pbxproj | 4 +- example_test/example_test.cpp | 902 ++ example_test/example_test.vcxproj | 170 + example_test/example_test.vcxproj.filters | 23 + .../example_test.xcodeproj/project.pbxproj | 294 + .../contents.xcworkspacedata | 7 + .../project.pbxproj | 4 +- .../example_write.xcodeproj/project.pbxproj | 4 +- .../project.pbxproj | 4 +- makefile | 14 +- opennurbs.vcxproj.metaproj | 9 +- opennurbs_3dm_properties.cpp | 2 +- opennurbs_3dm_settings.cpp | 654 +- opennurbs_3dm_settings.h | 179 + opennurbs_annotationbase.cpp | 120 +- opennurbs_annotationbase.h | 22 +- opennurbs_apple_nsfont.cpp | 231 + opennurbs_apple_nsfont.h | 40 + opennurbs_archive.cpp | 25 +- opennurbs_archive.h | 26 + opennurbs_archive_manifest.cpp | 71 +- opennurbs_array_defs.h | 54 +- opennurbs_atomic_op.h | 78 + opennurbs_bitmap.cpp | 3 + opennurbs_brep.cpp | 19 +- opennurbs_brep.h | 3 +- opennurbs_color.h | 2 + opennurbs_curve.cpp | 88 +- opennurbs_curve.h | 23 +- opennurbs_defines.cpp | 22 + opennurbs_defines.h | 29 +- opennurbs_detail.cpp | 43 + opennurbs_detail.h | 6 + opennurbs_dimension.cpp | 423 +- opennurbs_dimension.h | 43 +- opennurbs_dimensionformat.cpp | 6 +- opennurbs_dimensionstyle.cpp | 65 +- opennurbs_error.cpp | 616 +- opennurbs_error.h | 139 + opennurbs_error_message.cpp | 5 + opennurbs_extensions.cpp | 55 +- opennurbs_extensions.h | 63 +- opennurbs_file_utilities.cpp | 8 +- opennurbs_font.cpp | 8757 +++++++++++++++-- opennurbs_font.h | 4367 ++++++-- opennurbs_freetype.cpp | 1665 +--- opennurbs_freetype.h | 32 +- opennurbs_glyph_outline.cpp | 2639 +++++ opennurbs_hatch.cpp | 5 + opennurbs_internal_V2_annotation.cpp | 15 +- opennurbs_internal_V5_annotation.cpp | 11 +- opennurbs_internal_Vx_annotation.cpp | 77 +- opennurbs_internal_glyph.h | 173 +- opennurbs_leader.cpp | 72 +- opennurbs_leader.h | 8 + opennurbs_linetype.cpp | 10 + opennurbs_linetype.h | 27 + opennurbs_locale.cpp | 25 +- opennurbs_lookup.cpp | 4 +- opennurbs_material.cpp | 176 +- opennurbs_material.h | 21 +- opennurbs_mesh.cpp | 6 +- opennurbs_mesh_ngon.cpp | 42 +- opennurbs_model_component.cpp | 29 +- opennurbs_object.cpp | 6 +- opennurbs_point.cpp | 113 + opennurbs_point.h | 77 + opennurbs_public.sln | 35 +- opennurbs_public.vcxproj | 5 + opennurbs_public.vcxproj.metaproj | 17 +- opennurbs_public.xcodeproj/project.pbxproj | 8 +- .../contents.xcworkspacedata | 6 +- opennurbs_public_staticlib.vcxproj | 5 + opennurbs_public_staticlib.vcxproj.metaproj | 17 +- opennurbs_public_version.h | 23 +- opennurbs_revsurface.cpp | 2 +- opennurbs_sha1.cpp | 6 + opennurbs_staticlib.vcxproj.metaproj | 9 +- opennurbs_statics.cpp | 524 +- opennurbs_string.cpp | 429 +- opennurbs_string.h | 119 +- opennurbs_string_compare.cpp | 233 +- opennurbs_string_value.h | 4 +- opennurbs_string_values.cpp | 10 + opennurbs_subd.cpp | 436 +- opennurbs_subd.h | 485 +- opennurbs_subd_archive.cpp | 1 + opennurbs_subd_copy.cpp | 8 +- opennurbs_subd_data.cpp | 135 +- opennurbs_subd_data.h | 153 +- opennurbs_subd_iter.cpp | 12 +- opennurbs_subd_limit.cpp | 580 +- opennurbs_subd_mesh.cpp | 2048 +++- opennurbs_subd_ref.cpp | 12 +- opennurbs_surface.cpp | 11 + opennurbs_surface.h | 5 + opennurbs_system_runtime.h | 26 +- opennurbs_text.cpp | 296 +- opennurbs_text.h | 18 + opennurbs_text_style.cpp | 31 +- opennurbs_textcontext.cpp | 38 +- opennurbs_textcontext.h | 1 + opennurbs_textglyph.cpp | 241 +- opennurbs_textiterator.cpp | 1011 +- opennurbs_textiterator.h | 58 +- opennurbs_textlog.cpp | 64 +- opennurbs_textlog.h | 32 +- opennurbs_textobject.cpp | 22 +- opennurbs_textobject.h | 8 + opennurbs_textrun.cpp | 29 +- opennurbs_texture.h | 9 +- opennurbs_unicode.cpp | 118 +- opennurbs_unicode.h | 28 +- opennurbs_version_number.cpp | 1 + opennurbs_win_dwrite.cpp | 1819 ++++ opennurbs_win_dwrite.h | 55 + opennurbs_wip.h | 11 +- opennurbs_wstring.cpp | 424 +- readme.txt | 2 +- .../project.pbxproj | 4 +- zlib/zlib.vcxproj.metaproj | 9 +- 124 files changed, 27735 insertions(+), 4925 deletions(-) create mode 100644 example_test/example_test.cpp create mode 100644 example_test/example_test.vcxproj create mode 100644 example_test/example_test.vcxproj.filters create mode 100644 example_test/example_test.xcodeproj/project.pbxproj create mode 100644 example_test/example_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 opennurbs_apple_nsfont.cpp create mode 100644 opennurbs_apple_nsfont.h create mode 100644 opennurbs_atomic_op.h create mode 100644 opennurbs_glyph_outline.cpp create mode 100644 opennurbs_win_dwrite.cpp create mode 100644 opennurbs_win_dwrite.h diff --git a/example_brep/example_brep.xcodeproj/project.pbxproj b/example_brep/example_brep.xcodeproj/project.pbxproj index b727a74c..f681238a 100644 --- a/example_brep/example_brep.xcodeproj/project.pbxproj +++ b/example_brep/example_brep.xcodeproj/project.pbxproj @@ -183,7 +183,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -225,7 +225,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; diff --git a/example_convert/example_convert.xcodeproj/project.pbxproj b/example_convert/example_convert.xcodeproj/project.pbxproj index 7e5e13df..97f66c81 100644 --- a/example_convert/example_convert.xcodeproj/project.pbxproj +++ b/example_convert/example_convert.xcodeproj/project.pbxproj @@ -183,7 +183,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -225,7 +225,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; diff --git a/example_read/example_read.xcodeproj/project.pbxproj b/example_read/example_read.xcodeproj/project.pbxproj index 53de201f..33c4c1ce 100644 --- a/example_read/example_read.xcodeproj/project.pbxproj +++ b/example_read/example_read.xcodeproj/project.pbxproj @@ -191,7 +191,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -234,7 +234,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; diff --git a/example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj b/example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj index 4d6c33da..4e739d7d 100644 --- a/example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj +++ b/example_roundtrip/example_roundtrip.xcodeproj/project.pbxproj @@ -183,7 +183,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -225,7 +225,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; diff --git a/example_test/example_test.cpp b/example_test/example_test.cpp new file mode 100644 index 00000000..2a06ed24 --- /dev/null +++ b/example_test/example_test.cpp @@ -0,0 +1,902 @@ +/* +// +// Copyright (c) 1993-2018 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Assoicates. +// +// 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 . +// +//////////////////////////////////////////////////////////////// +*/ + +//////////////////////////////////////////////////////////////// +// +// example_test.cpp +// +// Example program using the Rhino file IO toolkit. The program reads +// Rhino 3dm model files and verifies they can be saved and reread with +// no changes to core model content. The program is a console application +// that takes options, file names, and directory names as command line +// arguments. +// +//////////////////////////////////////////////////////////////////////// + +#include "../opennurbs_public_examples.h" + +class Internal_CTestContext +{ +public: + enum : unsigned int + { + sizeof_a = 4088 + }; + + Internal_CTestContext() + { + m_a = onmalloc(sizeof_a); + } + + ~Internal_CTestContext() + { + onfree(m_a); + } + + void SetInitialDirecory( + const wchar_t* initial_directory, + unsigned int counter + ) + { + m_initial_directory = Internal_CleanPath(initial_directory); + m_initial_directory_length = m_initial_directory.Length(); + m_initial_directory_counter = counter; + } + + void* m_a = nullptr; + ON__INT64 m_min_delta_time = 0; + ON__INT64 m_max_delta_time = 0; + + unsigned int m_file_count = 0; + unsigned int m_directory_count = 0; + unsigned int m_file_open_error_count = 0; + unsigned int m_max_directory_tree_depth = 0; + unsigned int m_max_file_count = 0; + + const ON_wString TextLogPathFromFullPath( + const wchar_t* full_path + ) const; + +private: + ON_wString m_initial_directory; + unsigned int m_initial_directory_counter = 0; + int m_initial_directory_length = 0; + Internal_CTestContext(const Internal_CTestContext&) = delete; + Internal_CTestContext operator=(const Internal_CTestContext&) = delete; + const ON_wString Internal_CleanPath(const wchar_t* dirty_path) const + { + const wchar_t* volume = 0; + const wchar_t* path = 0; + + // Use local path in case drive, dir, file_name_stem or ext are being reused. + on_wsplitpath(dirty_path, &volume, &path, nullptr, nullptr); + ON_wString clean_path(path); + if (clean_path.IsEmpty()) + return ON_wString(dirty_path); + clean_path.Replace(ON_wString::Backslash, ON_wString::Slash); + + if (nullptr != volume && volume < path) + { + ON_wString clean_volume(volume, (int)(path - volume)); + return (clean_volume + clean_path); + } + + return clean_path; + } + +}; + +const ON_wString Internal_CTestContext::TextLogPathFromFullPath(const wchar_t* full_path) const +{ + // replace initial directory with and use / for + // the directory separator so that output files are standardized. + ON_wString text_log_path; + ON_wString path1 = Internal_CleanPath(full_path); + if (m_initial_directory_length > 0 && + ON_wString::EqualOrdinal(m_initial_directory, m_initial_directory_length, path1, m_initial_directory_length, true) + ) + { + text_log_path + = (m_initial_directory_counter>0) + ? ON_wString::FormatToString(L"", m_initial_directory_counter) + : ON_wString(L""); + text_log_path += static_cast(path1) + m_initial_directory_length; + } + else + { + text_log_path = path1; + } + return text_log_path; +} + + +static const ONX_ErrorCounter Internal_TestModelRead( + ON_TextLog& text_log, + const ON_wString text_log_3dm_file_name, + ON_BinaryArchive& source_archive, + bool bVerbose + ) +{ + ONX_ModelTest::Type test_type = ONX_ModelTest::Type::ReadWriteReadCompare; + + ///// + // + // Read the orginal file + // + text_log.PushIndent(); + ONX_ModelTest test; + test.ReadTest(source_archive, test_type, true, text_log_3dm_file_name, &text_log); + text_log.PrintNewLine(); + text_log.Print("Test Results:\n"); + text_log.PushIndent(); + test.Dump(text_log); + text_log.PopIndent(); + + ONX_ErrorCounter error_counter = test.ErrorCounter(); + + const ONX_Model* source_model = test.SourceModel().get(); + if (nullptr == source_model) + { + text_log.PopIndent(); + return error_counter; + } + + const bool bCompareTestFailed = ONX_ModelTest::Result::Fail == test.TestResult(ONX_ModelTest::Type::ReadWriteReadCompare); + + if ( bVerbose || bCompareTestFailed ) + { + for (int i = 0; i < 2; i++) + { + if (0 == i) + test.DumpSourceModel(); + else + test.DumpReadWriteReadModel(); + if (false == bCompareTestFailed) + break; + } + } + + text_log.PrintNewLine(); + + const ON_ModelComponent::Type component_types[] = + { + // TODO uncomment types as components are derived from ON_ModelComponent + ON_ModelComponent::Type::Image, + //ON_ModelComponent::Type::TextureMapping, + ON_ModelComponent::Type::RenderMaterial, + ON_ModelComponent::Type::LinePattern, + ON_ModelComponent::Type::Layer, + //ON_ModelComponent::Type::Group, + ON_ModelComponent::Type::TextStyle, + ON_ModelComponent::Type::DimStyle, + ON_ModelComponent::Type::RenderLight, + ON_ModelComponent::Type::HatchPattern, + ON_ModelComponent::Type::InstanceDefinition, + ON_ModelComponent::Type::ModelGeometry, + //ON_ModelComponent::Type::HistoryRecord + }; + + const unsigned int component_type_count = (unsigned int)(sizeof(component_types) / sizeof(component_types[0])); + + const ONX_Model& model = *source_model; + + error_counter.ClearLibraryErrorsAndWarnings(); + + const ON_ComponentManifest& manifest = model.Manifest(); + ON_SerialNumberMap it_map; + + for (unsigned int i = 0; i < component_type_count; i++) + { + const ON_ModelComponent::Type component_type = component_types[i]; + const bool bUniqueNameRequired = ON_ModelComponent::UniqueNameRequired(component_type); + const bool bEmbeddedFileComponent = (ON_ModelComponent::Type::Image == component_type); + ONX_ModelComponentIterator it(model,component_type); + unsigned int it_count = 0; + for (ON_ModelComponentReference mcr = it.FirstComponentReference(); false == mcr.IsEmpty(); mcr = it.NextComponentReference()) + { + const ON_ModelComponent* model_component = mcr.ModelComponent(); + if (nullptr == model_component) + { + ON_ERROR("Iterator returned nullptr mcr.ModelComponent()"); + continue; + } + if (model_component->ComponentType() != component_type) + { + ON_ERROR("Iterator returned wrong mcr.ModelComponent()->ComponentType()"); + continue; + } + const ON__UINT64 model_component_sn = model_component->RuntimeSerialNumber(); + const ON_UUID model_component_id = model_component->Id(); + if (0 == model_component_sn) + { + ON_ERROR("Iterator mcr.ModelComponent()->RuntimeSerialNumber() is zero."); + continue; + } + if (ON_nil_uuid == model_component_id) + { + ON_ERROR("Iterator mcr.ModelComponent()->Id() is nil."); + continue; + } + const ON_SerialNumberMap::SN_ELEMENT* e_sn = it_map.FindSerialNumber(model_component_sn); + if (nullptr != e_sn) + { + ON_ERROR("Iterator returned component serial number twice."); + continue; + } + const ON_SerialNumberMap::SN_ELEMENT* e_id = it_map.FindId(model_component_id); + if (nullptr != e_id) + { + ON_ERROR("Iterator returned component id twice."); + continue; + } + ON_SerialNumberMap::SN_ELEMENT* e = it_map.AddSerialNumberAndId(model_component_sn,model_component_id); + if (nullptr == e) + { + ON_ERROR("ON_SerialNumberMap failed to add sn and id."); + continue; + } + if (e->m_value.m_u_type != 0) + { + ON_ERROR("ON_SerialNumberMap error."); + continue; + } + e->m_value.m_u_type = 1; + e->m_value.m_u.ptr = (void*)model_component; + + const ON_ComponentManifestItem& item = manifest.ItemFromComponentRuntimeSerialNumber(model_component_sn); + if (item.IsUnset()) + { + ON_ERROR("Iterator returned item not in manifest."); + continue; + } + if (model_component_sn != item.ComponentRuntimeSerialNumber()) + { + ON_ERROR("manifest.ItemFromComponentRuntimeSerialNumber() error."); + continue; + } + if (model_component_id != item.Id()) + { + ON_ERROR("item has wrong id."); + continue; + } + + const ON_ComponentManifestItem& item_id = manifest.ItemFromId(model_component_id); + if (&item != &item_id) + { + ON_ERROR("manifest.ItemFromId() failed."); + continue; + } + + if (bUniqueNameRequired || bEmbeddedFileComponent) + { + const ON_Bitmap* embedded_file_reference = bEmbeddedFileComponent ? ON_Bitmap::Cast(model_component) : nullptr; + const ON_NameHash full_path_hash + = (nullptr != embedded_file_reference) + ? ON_NameHash::CreateFilePathHash(embedded_file_reference->FileReference()) + : ON_NameHash::EmptyNameHash; + const ON_NameHash name_hash + =bEmbeddedFileComponent + ? full_path_hash + : model_component->NameHash(); + if (false == name_hash.IsValidAndNotEmpty()) + { + ON_ERROR("model compoent name is not valid."); + continue; + } + if (name_hash != item.NameHash()) + { + ON_ERROR("item has wrong name hash."); + continue; + } + const ON_ComponentManifestItem& item_name = manifest.ItemFromNameHash(component_type,name_hash); + if (&item != &item_name) + { + ON_ERROR("manifest.ItemFromNameHash() failed."); + continue; + } + } + it_count++; + } + + if (it_count != manifest.ActiveAndDeletedComponentCount(component_type)) + { + ON_ERROR("iterator count != manifest.ActiveAndDeletedComponentCount(component_type)"); + continue; + } + } + + text_log.PopIndent(); + error_counter.AddLibraryErrorsAndWarnings(); + return error_counter; +} + +static const ONX_ErrorCounter Internal_TestFileRead( + ON_TextLog& text_log, + const ON_wString fullpath, + const ON_wString text_log_path, + bool bVerbose + ) +{ + FILE* fp = nullptr; + + fp = ON_FileStream::Open3dmToRead(fullpath); + + ONX_ErrorCounter error_counter; + error_counter.ClearLibraryErrorsAndWarnings(); + + const ON_wString path_to_print + = (text_log_path.IsNotEmpty()) + ? text_log_path + : fullpath; + + for (;;) + { + if (nullptr == fp) + { + text_log.Print( + L"Skipped file: %ls\n", + static_cast(path_to_print) + ); + error_counter.IncrementFailureCount(); + break; + } + + text_log.Print( + L"File name: %ls\n", + static_cast(path_to_print) + ); + + ON_BinaryFile source_archive(ON::archive_mode::read3dm, fp); + source_archive.SetArchiveFullPath(fullpath); + + error_counter += Internal_TestModelRead(text_log, path_to_print, source_archive, bVerbose); + break; + } + + if ( nullptr != fp ) + { + ON_FileStream::Close(fp); + fp = nullptr; + } + + return error_counter; +} + +static int Internal_ComparePath(const ON_wString* lhs, const ON_wString* rhs) +{ + // sort nullptr to end of list. + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return 1; + if (nullptr == rhs) + return -1; + int rc = ON_wString::CompareOrdinal(static_cast(*lhs), static_cast(*rhs), true); + if ( 0 == rc ) + rc = ON_wString::CompareOrdinal(static_cast(*lhs), static_cast(*rhs), false); + return rc; +} + +static const ONX_ErrorCounter Internal_TestReadFolder( + ON_TextLog& text_log, + const wchar_t* directory_path, + unsigned int directory_tree_depth, + bool bVerbose, + Internal_CTestContext& test_context + ) +{ + ONX_ErrorCounter error_counter; + error_counter.ClearLibraryErrors(); + + if (nullptr == directory_path || 0 == directory_path[0]) + { + text_log.Print(L"Empty directory name.\n"); + } + + ON_FileIterator fit; + if (false == fit.Initialize(directory_path)) + { + text_log.Print( + L"Invalid directory name: %ls\n", + directory_path + ); + error_counter.IncrementFailureCount(); + return error_counter; + } + + const ON_wString text_log_directory_name + = (directory_tree_depth <= 1) + ? ON_wString(directory_path) + : test_context.TextLogPathFromFullPath(directory_path); + text_log.Print( + L"Directory name: %ls\n", + static_cast(text_log_directory_name) + ); + text_log.PushIndent(); + + ON_ClassArray< ON_wString > sub_directories(32); + ON_ClassArray< ON_wString > skipped_files(32); + ON_ClassArray< ON_wString > tested_files(32); + + for ( bool bHaveItem = fit.FirstItem(); bHaveItem; bHaveItem = fit.NextItem() ) + { + if ( test_context.m_max_file_count > 0 && test_context.m_file_count >= test_context.m_max_file_count) + break; + + if (fit.CurrentItemIsDirectory()) + { + if (directory_tree_depth < test_context.m_max_directory_tree_depth) + { + sub_directories.Append(fit.CurrentItemFullPathName()); + } + continue; + } + + ON_wString fullpath = fit.CurrentItemFullPathName(); + + if (ON_FileStream::Is3dmFile(fullpath, false)) + tested_files.Append(fullpath); + else + skipped_files.Append(fullpath); + } + + // sort file and folder names so test order depends only on content. + // This is required so results from different computers can be compared. + sub_directories.QuickSort(Internal_ComparePath); + skipped_files.QuickSort(Internal_ComparePath); + tested_files.QuickSort(Internal_ComparePath); + + if (skipped_files.Count() > 0) + { + text_log.PrintNewLine(); + for (int i = 0; i < skipped_files.Count(); i++) + { + const ON_wString path_to_print = test_context.TextLogPathFromFullPath(skipped_files[i]); + text_log.Print( + L"Skipped file: %ls\n", + static_cast(path_to_print) + ); + } + text_log.PrintNewLine(); + } + + for ( int i = 0; i < tested_files.Count(); i++ ) + { + const ON_wString full_path = tested_files[i]; + const ON_wString path_to_print = test_context.TextLogPathFromFullPath(full_path); + test_context.m_file_count++; + const ONX_ErrorCounter file_error_counter = Internal_TestFileRead(text_log, full_path, path_to_print, bVerbose); + error_counter += file_error_counter; + } + + for (int i = 0; i < sub_directories.Count(); i++) + { + if (test_context.m_max_file_count > 0 && test_context.m_file_count >= test_context.m_max_file_count) + break; + const ON_wString sub_directory_path = sub_directories[i]; + test_context.m_directory_count++; + error_counter += Internal_TestReadFolder(text_log, sub_directory_path, directory_tree_depth + 1, bVerbose, test_context); + } + + text_log.PopIndent(); + + return error_counter; +} + +static ONX_ErrorCounter Internal_Test( + unsigned int max_directory_tree_depth, + ON_wString full_path, + bool bVerbose, + ON_TextLog& text_log, + unsigned int& directory_counter, + unsigned int& file_count, + unsigned int& folder_count +) +{ + + ONX_ErrorCounter err = ONX_ErrorCounter::Zero; + + if (ON_FileSystem::IsFile(full_path)) + { + if (ON_FileStream::Is3dmFile(full_path, false)) + { + text_log.Print(L"Testing 3dm file: %ls\n", static_cast(full_path)); + err = Internal_TestFileRead(text_log, full_path, ON_wString::EmptyString, bVerbose); + file_count++; + } + } + else if ( max_directory_tree_depth > 0 ) + { + if (full_path.Length() != 1 || false == ON_FileSystemPath::IsDirectorySeparator(full_path[0], true)) + { + const wchar_t dir_seps[3] = { ON_FileSystemPath::DirectorySeparator,ON_FileSystemPath::AlternateDirectorySeparator,0 }; + full_path.TrimRight(dir_seps); + } + if (ON_FileSystem::IsDirectory(full_path)) + { + text_log.Print(L"Testing 3dm files in folder: %ls\n", static_cast(full_path)); + Internal_CTestContext test_context; + directory_counter++; + test_context.SetInitialDirecory(full_path,directory_counter); + test_context.m_max_directory_tree_depth = max_directory_tree_depth; + + err = Internal_TestReadFolder(text_log, full_path, 1, bVerbose, test_context); + file_count += test_context.m_file_count; + folder_count += test_context.m_directory_count; + } + } + + return err; +} + +static void Internal_PrintHelp( + const char* example_test_exe_name, + ON_TextLog& text_log +) +{ + if ( 0 == example_test_exe_name || 0 == example_test_exe_name[0]) + example_test_exe_name = "example_test"; + + text_log.Print("\n"); + text_log.Print("SYNOPSIS:\n"); + text_log.Print(" %s [-out:outputfilename.txt] [-r[:N]] \n",example_test_exe_name ); + text_log.Print("\n"); + text_log.Print("DESCRIPTION:\n"); + text_log.Print(" If a file is listed, it is read as an opennurbs model file.\n"); + text_log.Print(" If a directory is listed, all .3dm files in that directory\n"); + text_log.Print(" are read as opennurbs model files.\n"); + text_log.Print("\n"); + text_log.Print(" Available options:\n"); + text_log.Print(" -out:outputfilename.txt\n"); + text_log.Print(" The output is written to the named file.\n"); + text_log.Print(" -recursive\n"); + text_log.Print(" Recursivly reads files in subdirectories.\n"); + text_log.Print(" If a :N is appended to the option, N limits the recursion depth.\n"); + text_log.Print(" -r:1 reads the directory and does not recurse.\n"); + text_log.Print(" -verbose\n"); + text_log.Print(" Include a full listing of 3dm file contents.\n"); + text_log.Print("\n"); + text_log.Print("RETURNS:\n"); + text_log.Print(" 0: All tested files passed.\n"); + text_log.Print(" >0: Number of failures, errors, or warnings. See output for details.\n"); + text_log.Print("\n"); + text_log.Print("EXAMPLE:\n"); + text_log.Print(" %s -out:list.txt -resursive:2 .../example_files\n",example_test_exe_name); + text_log.Print(" with read all the opennurbs .3dm files in the\n"); + text_log.Print(" example_files/ directory and immediate subdirectories of example_files/.\n"); +} + +static const char* Internal_ParseOptionHead( + const char* s, + const char* option_name0, + const char* option_name1 = nullptr, + const char* option_name2 = nullptr +) +{ + if (nullptr == s || 0 == s[0]) + return nullptr; + if ('-' != s[0] ) + { +#if defined(ON_OS_WINDOWS) + if ( '/' != s[0] ) +#endif + return nullptr; + } + + s++; + if (0 == s[0]) + return nullptr; + + const char* option_names[] = { option_name0, option_name1, option_name2 }; + const size_t option_name_count = sizeof(option_names) / sizeof(option_names[0]); + for (size_t i = 0; i < option_name_count; i++) + { + const char* option_name = option_names[i]; + const int option_name_length = ON_String::Length(option_name); + if (option_name_length < 1) + continue; + if (ON_String::EqualOrdinal(option_name, option_name_length, s, option_name_length, true)) + return s + option_name_length; + } + return nullptr; +} + +static const ON_String Internal_ParseOptionTail( + const char* s +) +{ + for (;;) + { + if (nullptr == s || ':' != s[0] || s[1] <= ON_String::Space) + break; + ON_String tail(s+1); + tail.TrimRight(); + int len = tail.Length(); + if (len < 1) + break; + if ('"' == tail[0] && '"' == tail[len - 1]) + tail.TrimLeftAndRight("\""); + else if ('\'' == tail[0] && '\'' == tail[len - 1]) + tail.TrimLeftAndRight("'"); + if (tail.Length() < 1) + break; + return tail; + } + return ON_String::EmptyString; +} + + +static bool Internal_ParseOption_OUT(const char* s, ON_String& output_file_name) +{ + output_file_name = ON_wString::EmptyString; + const char* tail = Internal_ParseOptionHead(s, "out" ); + if (nullptr == tail) + return false; + output_file_name = Internal_ParseOptionTail(tail); + return true; +} + +static bool Internal_Parse_ParsOption_RECURSE(const char* s, unsigned int& N) +{ + const char* tail = Internal_ParseOptionHead(s, "r", "recurse", "recursive" ); + if (nullptr == tail) + return false; + + if (0 == tail[0]) + { + N = 16; // sanity limit of default directory recursion depth + return true; + } + + N = 0; + const ON_String num = Internal_ParseOptionTail(tail); + if (num.IsNotEmpty()) + { + unsigned int u = 0; + const char* s1 = ON_String::ToNumber(num, u, &u); + if (nullptr != s1 && s1 > static_cast(num) && u >= 1 && 0 == s1[0]) + N = u; + } + return true; +} + +static bool Internal_Parse_ParsOption_VERBOSE(const char* s) +{ + const char* tail = Internal_ParseOptionHead(s, "v", "verbose"); + return (nullptr != tail && 0 == tail[0]); +} + +static void Internal_PrintIntroduction( + const ON_wString& exe_name, + ON_TextLog& text_log +) +{ + text_log.Print(L"Executable: %ls\n", static_cast(exe_name)); + text_log.Print("Compiled: " __DATE__ " " __TIME__ "\n"); + const ON_wString platform_name = +#if defined(ON_RUNTIME_WIN) + L"Windows" +#elif defined(ON_RUNTIME_APPLE_IOS) + L"Apple iOS" +#elif defined(ON_RUNTIME_APPLE_MACOS) + L"Apple Mac OS" +#elif defined(ON_RUNTIME_APPLE) + L"Apple" +#elif defined(ON_RUNTIME_ANDROID) + L"Android" +#else + L"Unknown" +#endif + ; + ON::Version(); + text_log.Print(L"Platform: %ls\n", static_cast(platform_name)); + text_log.Print( + "Opennurbs version: %u.%u %04u-%02u-%02u %02u:%02u (%u)\n", + ON::VersionMajor(), + ON::VersionMinor(), + ON::VersionYear(), + ON::VersionMonth(), + ON::VersionDayOfMonth(), + ON::VersionHour(), + ON::VersionMinute(), + ON::VersionBranch() + ); + text_log.Print("Current time: "); + text_log.PrintCurrentTime(); + text_log.Print(" UCT\n"); +} + +int main( int argc, const char *argv[] ) +{ + // If you are using OpenNURBS as a Windows DLL, then you MUST use + // ON::OpenFile() to open the file. If you are not using OpenNURBS + // as a Windows DLL, then you may use either ON::OpenFile() or fopen() + // to open the file. + + const char* example_test_exe_name = 0; + if ( argc >= 1 && 0 != argv && 0 != argv[0] && 0 != argv[0][0] ) + { + on_splitpath( + argv[0], + nullptr, // volume + nullptr, // directory, + &example_test_exe_name, + nullptr // extension + ); + } + + if ( 0 == example_test_exe_name || 0 == example_test_exe_name[0] ) + { +#if defined(ON_OS_WINDOWS) + example_test_exe_name = "example_test.exe"; +#else + example_test_exe_name = "example_test"; +#endif + } + + const ON_wString exe_name((nullptr != argv && argc > 0) ? argv[0] : ((const char*)nullptr)); + + ON_TextLog print_to_stdout; + print_to_stdout.SetIndentSize(2); + + int argi; + if ( argc < 2 ) + { + Internal_PrintHelp(example_test_exe_name, print_to_stdout); + return 0; + } + + // Call once in your application to initialze opennurbs library + ON::Begin(); + + // default text_log is to stdout + ON_TextLog* text_log = &print_to_stdout; + FILE* text_log_fp = nullptr; + + unsigned int maximum_directory_depth = 0; + + bool bVerbose = false; // true = output will include a full listing of all 3dm file contents. + + ONX_ErrorCounter err; + unsigned int file_count = 0; + unsigned int folder_count = 0; + unsigned int directory_arg_counter = 0; + + bool bPrintIntroduction = true; + + for ( argi = 1; argi < argc; argi++ ) + { + const char* arg = argv[argi]; + + // check for -out: option + ON_String output_file_name; + if ( Internal_ParseOption_OUT(arg,output_file_name) ) + { + // need a new introduction when output destination changes. + bPrintIntroduction = true; + + if (output_file_name.IsEmpty()) + continue; // invalid option systax + + // change destination of text_log file + if ( text_log != &print_to_stdout ) + { + delete text_log; + text_log = nullptr; + } + if ( text_log_fp ) + { + ON::CloseFile(text_log_fp); + } + + FILE* text_fp = ON::OpenFile(output_file_name,"w"); + if ( text_fp ) + { + text_log_fp = text_fp; + text_log = new ON_TextLog(text_log_fp); + text_log->SetIndentSize(2); + } + + continue; + } + + // check for -recursive: option + if (Internal_Parse_ParsOption_RECURSE(arg, maximum_directory_depth)) + { + continue; + } + + if (Internal_Parse_ParsOption_VERBOSE(arg)) + { + bVerbose = true; + continue; + } + + ON_wString full_path(arg); + + if ( nullptr == text_log ) + text_log = &print_to_stdout; + + if (bPrintIntroduction) + { + bPrintIntroduction = false; + Internal_PrintIntroduction(exe_name, *text_log); + } + + err += Internal_Test( + maximum_directory_depth, + full_path, + bVerbose, + *text_log, + directory_arg_counter, + file_count, + folder_count + ); + } + + if (bPrintIntroduction) + { + bPrintIntroduction = false; + Internal_PrintIntroduction(exe_name, *text_log); + } + + if (folder_count > 0) + { + text_log->Print( + L"Tested %u 3dm files in %u folders.", + file_count, + folder_count + ); + } + else + { + text_log->Print( + L"Tested %u 3dm files.", + file_count + ); + } + + if (err.FailureCount() > 0) + text_log->Print(" Failures. "); + else if (err.ErrorCount() > 0) + text_log->Print(" Errors. "); + else if (err.FailureCount() > 0) + text_log->Print(" Warnings. "); + else + text_log->Print(" All tests passed. "); + err.Dump(*text_log); + text_log->PrintNewLine(); + + if ( text_log != &print_to_stdout ) + { + delete text_log; + text_log = nullptr; + } + + if ( text_log_fp ) + { + // close the text text_log file + ON::CloseFile( text_log_fp ); + text_log_fp = 0; + } + + // OPTIONAL: Call just before your application exits to clean + // up opennurbs class definition information. + // Opennurbs will not work correctly after ON::End() + // is called. + ON::End(); + + return err.TotalCount(); +} diff --git a/example_test/example_test.vcxproj b/example_test/example_test.vcxproj new file mode 100644 index 00000000..a63cce2d --- /dev/null +++ b/example_test/example_test.vcxproj @@ -0,0 +1,170 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {865E8D8D-8E03-4601-A7B5-A8C4094ECB8B} + Win32Proj + exampletest + 10.0.16299.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Level4 + + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Level4 + + + + Console + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Level4 + + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Level4 + + + + Console + true + true + true + setargv.obj;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + \ No newline at end of file diff --git a/example_test/example_test.vcxproj.filters b/example_test/example_test.vcxproj.filters new file mode 100644 index 00000000..e0fdc24e --- /dev/null +++ b/example_test/example_test.vcxproj.filters @@ -0,0 +1,23 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/example_test/example_test.xcodeproj/project.pbxproj b/example_test/example_test.xcodeproj/project.pbxproj new file mode 100644 index 00000000..ecc0f38e --- /dev/null +++ b/example_test/example_test.xcodeproj/project.pbxproj @@ -0,0 +1,294 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 1D4E12A22016B8A400B90EA3 /* example_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1D4E12A12016B8A400B90EA3 /* example_test.cpp */; }; + 1D4E12A42016B99F00B90EA3 /* libopennurbs_public.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D4E12A52016B99F00B90EA3 /* libopennurbs_public.a */; }; + 1D4E12A62016B9A600B90EA3 /* libopennurbs_public_zlib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D4E12A72016B9A600B90EA3 /* libopennurbs_public_zlib.a */; }; + 1D4E12A82016B9AB00B90EA3 /* libopennurbs_public_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D4E12A92016B9AB00B90EA3 /* libopennurbs_public_freetype.a */; }; + 1D4E12AB2016B9DD00B90EA3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D4E12AA2016B9DD00B90EA3 /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 1D4E12952016B7C800B90EA3 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1D4E12972016B7C800B90EA3 /* example_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D4E12A12016B8A400B90EA3 /* example_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = example_test.cpp; sourceTree = ""; }; + 1D4E12A52016B99F00B90EA3 /* libopennurbs_public.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libopennurbs_public.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D4E12A72016B9A600B90EA3 /* libopennurbs_public_zlib.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libopennurbs_public_zlib.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D4E12A92016B9AB00B90EA3 /* libopennurbs_public_freetype.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libopennurbs_public_freetype.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 1D4E12AA2016B9DD00B90EA3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1D4E12942016B7C800B90EA3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D4E12AB2016B9DD00B90EA3 /* Cocoa.framework in Frameworks */, + 1D4E12A62016B9A600B90EA3 /* libopennurbs_public_zlib.a in Frameworks */, + 1D4E12A82016B9AB00B90EA3 /* libopennurbs_public_freetype.a in Frameworks */, + 1D4E12A42016B99F00B90EA3 /* libopennurbs_public.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1D4E128E2016B7C800B90EA3 = { + isa = PBXGroup; + children = ( + 1D4E12A12016B8A400B90EA3 /* example_test.cpp */, + 1D4E12982016B7C800B90EA3 /* Products */, + 1D4E12A32016B99F00B90EA3 /* Frameworks */, + ); + sourceTree = ""; + }; + 1D4E12982016B7C800B90EA3 /* Products */ = { + isa = PBXGroup; + children = ( + 1D4E12972016B7C800B90EA3 /* example_test */, + ); + name = Products; + sourceTree = ""; + }; + 1D4E12A32016B99F00B90EA3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1D4E12AA2016B9DD00B90EA3 /* Cocoa.framework */, + 1D4E12A92016B9AB00B90EA3 /* libopennurbs_public_freetype.a */, + 1D4E12A72016B9A600B90EA3 /* libopennurbs_public_zlib.a */, + 1D4E12A52016B99F00B90EA3 /* libopennurbs_public.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1D4E12962016B7C800B90EA3 /* example_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1D4E129E2016B7C800B90EA3 /* Build configuration list for PBXNativeTarget "example_test" */; + buildPhases = ( + 1D4E12932016B7C800B90EA3 /* Sources */, + 1D4E12942016B7C800B90EA3 /* Frameworks */, + 1D4E12952016B7C800B90EA3 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_test; + productName = example_test; + productReference = 1D4E12972016B7C800B90EA3 /* example_test */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1D4E128F2016B7C800B90EA3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = "OpenNURBS 3dm File IO Toolkit"; + TargetAttributes = { + 1D4E12962016B7C800B90EA3 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1D4E12922016B7C800B90EA3 /* Build configuration list for PBXProject "example_test" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 1D4E128E2016B7C800B90EA3; + productRefGroup = 1D4E12982016B7C800B90EA3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1D4E12962016B7C800B90EA3 /* example_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 1D4E12932016B7C800B90EA3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1D4E12A22016B8A400B90EA3 /* example_test.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1D4E129C2016B7C800B90EA3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 1D4E129D2016B7C800B90EA3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 1D4E129F2016B7C800B90EA3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 1D4E12A02016B7C800B90EA3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1D4E12922016B7C800B90EA3 /* Build configuration list for PBXProject "example_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D4E129C2016B7C800B90EA3 /* Debug */, + 1D4E129D2016B7C800B90EA3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1D4E129E2016B7C800B90EA3 /* Build configuration list for PBXNativeTarget "example_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1D4E129F2016B7C800B90EA3 /* Debug */, + 1D4E12A02016B7C800B90EA3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1D4E128F2016B7C800B90EA3 /* Project object */; +} diff --git a/example_test/example_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example_test/example_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..b5e33a8f --- /dev/null +++ b/example_test/example_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example_userdata/example_userdata.xcodeproj/project.pbxproj b/example_userdata/example_userdata.xcodeproj/project.pbxproj index 549de888..0648828b 100644 --- a/example_userdata/example_userdata.xcodeproj/project.pbxproj +++ b/example_userdata/example_userdata.xcodeproj/project.pbxproj @@ -189,7 +189,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -231,7 +231,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; diff --git a/example_write/example_write.xcodeproj/project.pbxproj b/example_write/example_write.xcodeproj/project.pbxproj index 9c671ce9..2451f558 100644 --- a/example_write/example_write.xcodeproj/project.pbxproj +++ b/example_write/example_write.xcodeproj/project.pbxproj @@ -191,7 +191,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -233,7 +233,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; diff --git a/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj index 6691b24f..38c009a2 100644 --- a/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj +++ b/freetype263/opennurbs_public_freetype.xcodeproj/project.pbxproj @@ -358,7 +358,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -400,7 +400,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; diff --git a/makefile b/makefile index 8265027a..9fcf211d 100644 --- a/makefile +++ b/makefile @@ -78,6 +78,7 @@ ON_INC = opennurbs.h \ opennurbs_archive.h \ opennurbs_array.h \ opennurbs_array_defs.h \ + opennurbs_atomic_op.h \ opennurbs_base32.h \ opennurbs_base64.h \ opennurbs_beam.h \ @@ -269,6 +270,7 @@ ON_SRC = opennurbs_3dm_attributes.cpp \ opennurbs_fsp.cpp \ opennurbs_function_list.cpp \ opennurbs_geometry.cpp \ + opennurbs_glyph_outline.cpp \ opennurbs_group.cpp \ opennurbs_hash_table.cpp \ opennurbs_hatch.cpp \ @@ -768,17 +770,17 @@ EXAMPLE_INC = examples.h \ EXAMPLE_OBJ = example_read/example_read.o \ example_write/example_write.o \ + example_test/example_test.o \ example_convert/example_convert.o \ example_brep/example_brep.o \ example_userdata/example_ud.o \ - example_userdata/example_userdata.o \ - example_roundtrip/example_roundtrip.o + example_userdata/example_userdata.o EXAMPLES = example_read/example_read \ example_write/example_write \ + example_test/example_test \ example_convert/example_convert \ example_brep/example_brep \ - example_roundtrip/example_roundtrip \ example_userdata/example_userdata all : $(OPENNURBS_LIB_FILE) $(EXAMPLES) @@ -812,6 +814,9 @@ example_read/example_read : example_read/example_read.o example_userdata/example example_write/example_write : example_write/example_write.o example_userdata/example_ud.o $(OPENNURBS_LIB_FILE) $(LINK) $(LINKFLAGS) example_write/example_write.o example_userdata/example_ud.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ +example_test/example_test : example_test/example_test.o $(OPENNURBS_LIB_FILE) + $(LINK) $(LINKFLAGS) example_test/example_test.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ + example_convert/example_convert : example_convert/example_convert.o example_userdata/example_ud.o $(OPENNURBS_LIB_FILE) $(LINK) $(LINKFLAGS) example_convert/example_convert.o example_userdata/example_ud.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ @@ -821,9 +826,6 @@ example_brep/example_brep : example_brep/example_brep.o $(OPENNURBS_LIB_FILE) example_userdata/example_userdata : example_userdata/example_userdata.o $(OPENNURBS_LIB_FILE) $(LINK) $(LINKFLAGS) example_userdata/example_userdata.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ -example_roundtrip/example_roundtrip : example_roundtrip/example_roundtrip.o $(OPENNURBS_LIB_FILE) - $(LINK) $(LINKFLAGS) example_roundtrip/example_roundtrip.o -L. -l$(OPENNURBS_LIB_NAME) -lm -o $@ - clean : -$(RM) $(OPENNURBS_LIB_FILE) -$(RM) $(ON_OBJ) diff --git a/opennurbs.vcxproj.metaproj b/opennurbs.vcxproj.metaproj index 4a7df32f..441df6d5 100644 --- a/opennurbs.vcxproj.metaproj +++ b/opennurbs.vcxproj.metaproj @@ -34,13 +34,16 @@ false true ;C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets + true false WarnAndContinue + true + <_RestoreSolutionFileUsed>true <_GenerateRestoreGraphProjectEntryInputProperties> - RestoreUseCustomAfterTargets=; - NuGetRestoreTargets=C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets; - BuildProjectReferences=false; ExcludeRestorePackageImports=true; + _RestoreSolutionFileUsed=true; + SolutionDir=D:\BuildAgent\work\commercial\src4\BuildSolutions\; + SolutionName=Supplemental; diff --git a/opennurbs_3dm_properties.cpp b/opennurbs_3dm_properties.cpp index 0839008a..a66f2440 100644 --- a/opennurbs_3dm_properties.cpp +++ b/opennurbs_3dm_properties.cpp @@ -154,7 +154,7 @@ int ON_3dmRevisionHistory::NewRevision() current_user[0] = 0; m_sLastEditedBy = current_user; #endif -#if defined(ON_RUNTIME_APPLE) +#if defined(ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) m_sLastEditedBy = NSFullUserName().UTF8String; #endif diff --git a/opennurbs_3dm_settings.cpp b/opennurbs_3dm_settings.cpp index 5283aef6..2f12bc19 100644 --- a/opennurbs_3dm_settings.cpp +++ b/opennurbs_3dm_settings.cpp @@ -1202,7 +1202,7 @@ bool ON_3dmAnnotationSettings::Read( ON_BinaryArchive& file ) int minor_version = 0; bool rc = file.Read3dmChunkVersion(&major_version,&minor_version); if ( major_version == 1 ) { - if ( minor_version >= 0 ) { + if (minor_version >= 0) { if (rc) rc = file.ReadDouble(&m_dimscale); if (rc) rc = file.ReadDouble(&m_textheight); if (rc) rc = file.ReadDouble(&m_dimexe); @@ -1213,42 +1213,59 @@ bool ON_3dmAnnotationSettings::Read( ON_BinaryArchive& file ) { unsigned int i; - if (rc) + if (rc) { - rc = file.ReadInt( &i ); + rc = file.ReadInt(&i); if (rc) m_dimunits = ON::LengthUnitSystemFromUnsigned(i); } } - if (rc) rc = file.ReadInt( &m_arrowtype ); - if (rc) rc = file.ReadInt( &m_angularunits ); - if (rc) rc = file.ReadInt( &m_lengthformat ); - if (rc) rc = file.ReadInt( &m_angleformat ); + if (rc) rc = file.ReadInt(&m_arrowtype); + if (rc) rc = file.ReadInt(&m_angularunits); + if (rc) rc = file.ReadInt(&m_lengthformat); + if (rc) rc = file.ReadInt(&m_angleformat); unsigned int obsolete_value = 0; if (rc) rc = file.ReadInt(&obsolete_value); - if (rc) rc = file.ReadInt( &m_resolution ); + if (rc) rc = file.ReadInt(&m_resolution); - if (rc) rc = file.ReadString( m_facename ); + if (rc) rc = file.ReadString(m_facename); - // files that do not contain m_bEnableAnnotationScaling, + bool bV6orLater = file.Archive3dmVersion() >= 60; + bool bV5 = !bV6orLater && file.Archive3dmVersion() >= 5; + + if (bV6orLater) + { + // files that do not contain m_bEnableAnnotationScaling, // set m_bEnableAnnotationScaling = false so the display // image does not change. - m_b_V5_EnableAnnotationScaling = 1; + m_b_V5_EnableAnnotationScaling = 1; - // files that do not contain m_bEnableModelSpaceAnnotationScaling, - // set m_bEnableModelSpaceAnnotationScaling = true so the display - // image does not change. - //*********** This is probably right for v5 files, but not for pre-V5 ************* - m_bEnableModelSpaceAnnotationScaling = 1; + // files that do not contain m_bEnableModelSpaceAnnotationScaling, + // set m_bEnableModelSpaceAnnotationScaling = true so the display + // image does not change. + //*********** This is probably right for v5 files, but not for pre-V5 ************* + m_bEnableModelSpaceAnnotationScaling = 1; - // files that do not contain m_bEnableLayoutSpaceAnnotationScaling, - // set m_bEnableLayoutAnnotationScaling = false so the display - // image does not change. - // ********** This should be set from m_b_V5_EnableAnnotationScaling for V5 files ************* - m_bEnableLayoutSpaceAnnotationScaling = 1; + // files that do not contain m_bEnableLayoutSpaceAnnotationScaling, + // set m_bEnableLayoutAnnotationScaling = false so the display + // image does not change. + // ********** This should be set from m_b_V5_EnableAnnotationScaling for V5 files ************* + m_bEnableLayoutSpaceAnnotationScaling = 1; + } + else if (bV5) + { + m_bEnableModelSpaceAnnotationScaling = 1; + } + else + { + // v4 or earlier - no layout or model space scaling + m_b_V5_EnableAnnotationScaling = 0; + m_bEnableModelSpaceAnnotationScaling = 0; + m_bEnableLayoutSpaceAnnotationScaling = 0; + } // files that do not contain m_bEnableHatchScaling, // set m_bEnableHatchScaling = false so the display @@ -4777,6 +4794,16 @@ bool ON_3dmSettings::Read(ON_BinaryArchive& file ) *this = ON_3dmSettings::Default; + if (60 > file.Archive3dmVersion()) + { + m_AnnotationSettings.EnableLayoutSpaceAnnotationScaling(false); + m_AnnotationSettings.EnableModelSpaceAnnotationScaling(false); + m_AnnotationSettings.EnableHatchScaling(false); + m_AnnotationSettings.Enable_V5_AnnotationScaling(false); + } + + + if ( 1 == file.Archive3dmVersion() ) { rc = Read_v1(file); @@ -5337,3 +5364,588 @@ void ON_3dmSettings::Dump( ON_TextLog& dump ) const } + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmAnimationProperties +// + +const ON_3dmAnimationProperties ON_3dmAnimationProperties::Default; + +bool ON_3dmAnimationProperties::Read(ON_BinaryArchive& archive) +{ + //*this = ON_3dmAnimationProperties::Default; + +// If the file archive version is not 4, + // just return. + if( archive.Archive3dmVersion() < 4 ) + return true; + + if (archive.Archive3dmVersion() == 4) + { + // This fixes bugs like RH-35784 + // It appears that the current code in CAnimationToolsPlugIn::ReadDocument() + // is not capable of reading information saved by the V4 version of this plug-in. + + return true; + } + + // Read the major and minor version of the + // document data + int major = 0, minor = 0; + if( !archive.Read3dmChunkVersion(&major, &minor) ) + return false; + + // At this point, if we've changed the format of + // our document data, we'll want to compare the + // major and minor revision numbers and read our + // data accordingly. + + //READ camera and target points count + int cp_count = 0; + if( !archive.ReadInt(&cp_count) ) + return false; + int tp_count = 0; + if( !archive.ReadInt(&tp_count) ) + return false; + if( cp_count < 1 || tp_count < 1 ) + return true; + + m_aCameraPoints.SetCount(0); + m_aTargetPoints.SetCount(0); + + for( int i = 0; i < cp_count; i++ ) + { + //read camera points + ON_3dPoint pt = ON_3dPoint::UnsetPoint; + if( !archive.ReadPoint( pt ) ) + return false; + m_aCameraPoints.Append( pt ); + } + for( int i = 0; i < tp_count; i++ ) + { + //read camera points + ON_3dPoint pt = ON_3dPoint::UnsetPoint; + if( !archive.ReadPoint( pt ) ) + return false; + m_aTargetPoints.Append( pt ); + } + + //READ OTHER PARAMETERS + int act = 5;//none + if( !archive.ReadInt( &act ) ) + return false; + if( act < 0 || act > 5 ) + return true; + m_CaptureTypes = static_cast(act); + + if( !archive.ReadString(m_sFileExtension)) + return false; + if( !archive.ReadString(m_sCaptureMethod)) + return false; + if( !archive.ReadUuid(m_idDisplayMode)) + return false; + if( !archive.ReadString(m_sHtmlFilename)) + return false; + if( !archive.ReadString(m_sViewport)) + return false; + if( !archive.ReadInt(&m_iFrameCount)) + return false; + if( !archive.ReadInt(&m_iCurrentFrame)) + return false; + if( !archive.ReadUuid(m_idCameraPath)) + return false; + if( !archive.ReadUuid(m_idTargetPath)) + return false; + if( !archive.ReadDouble(&m_dLatitude)) + return false; + if( !archive.ReadDouble(&m_dLongitude)) + return false; + if( !archive.ReadDouble(&m_dNorthAngle)) + return false; + if( !archive.ReadInt(&m_iStartDay)) + return false; + if( !archive.ReadInt(&m_iStartMonth)) + return false; + if( !archive.ReadInt(&m_iStartYear)) + return false; + if( !archive.ReadInt(&m_iEndDay) ) + return false; + if( !archive.ReadInt(&m_iEndMonth)) + return false; + if( !archive.ReadInt(&m_iEndYear)) + return false; + if( !archive.ReadInt(&m_iStartHour)) + return false; + if( !archive.ReadInt(&m_iStartMinutes)) + return false; + if( !archive.ReadInt(&m_iStartSeconds)) + return false; + if( !archive.ReadInt(&m_iEndHour)) + return false; + if( !archive.ReadInt(&m_iEndMinutes)) + return false; + if( !archive.ReadInt(&m_iEndSeconds)) + return false; + if( !archive.ReadInt(&m_iDaysBetweenFrames)) + return false; + if( !archive.ReadInt(&m_iMinutesBetweenFrames)) + return false; + if( !archive.ReadString(m_sFolderName)) + return false; + if( !archive.ReadInt(&m_iLightIndex)) + return false; + if( !archive.ReadBool(&m_bRenderFull)) + return false; + if( !archive.ReadBool(&m_bRenderPreview)) + return false; + + return true; +} + +bool ON_3dmAnimationProperties::Write(ON_BinaryArchive& archive) const +{ + int cp_count = CameraPoints().Count(); + int tp_count = TargetPoints().Count(); + + //Added by Rajaa - May 2, 2009 - next 2 lines commented out (test added to CallWriteDocument) + // Bug # 48383 - all validation happens on CallWriteDocument and not WriteDocument + + if( cp_count < 1 || tp_count < 1) + return true; + + if( !archive.WriteInt( cp_count ) )//Count of camera points + return true; + if( !archive.WriteInt( tp_count ) )// count of target points + return false; + + for( int i = 0; i < cp_count; i++ ) + { + //write camera points + if( !archive.WritePoint( m_aCameraPoints[i] ) ) + return false; + } + for( int i = 0; i < tp_count; i++ ) + { + //write camera points + if( !archive.WritePoint( m_aTargetPoints[i] ) ) + return false; + } + + //SAVE OTHER PARAMETERS +// ARecord::AType AnimationCaptureType; + const int iCaptureType = static_cast(m_CaptureTypes); + if( !archive.WriteInt(iCaptureType)) + return false; + if( !archive.WriteString(m_sFileExtension)) + return false; + if( !archive.WriteString(m_sCaptureMethod)) + return false; + if( !archive.WriteUuid(m_idDisplayMode)) + return false; + if( !archive.WriteString(m_sHtmlFilename)) + return false; + if( !archive.WriteString(m_sViewport)) + return false; + if( !archive.WriteInt(m_iFrameCount)) + return false; + if( !archive.WriteInt(m_iCurrentFrame)) + return false; + if( !archive.WriteUuid(m_idCameraPath)) + return false; + if( !archive.WriteUuid(m_idTargetPath)) + return false; + if( !archive.WriteDouble(m_dLatitude)) + return false; + if( !archive.WriteDouble(m_dLongitude)) + return false; + if( !archive.WriteDouble(m_dNorthAngle)) + return false; + if( !archive.WriteInt(m_iStartDay)) + return false; + if( !archive.WriteInt(m_iStartMonth)) + return false; + if( !archive.WriteInt(m_iStartYear)) + return false; + if( !archive.WriteInt(m_iEndDay)) + return false; + if( !archive.WriteInt(m_iEndMonth)) + return false; + if( !archive.WriteInt(m_iEndYear)) + return false; + if( !archive.WriteInt(m_iStartHour)) + return false; + if( !archive.WriteInt(m_iStartMinutes)) + return false; + if( !archive.WriteInt(m_iStartSeconds)) + return false; + if( !archive.WriteInt(m_iEndHour)) + return false; + if( !archive.WriteInt(m_iEndMinutes)) + return false; + if( !archive.WriteInt(m_iEndSeconds)) + return false; + if( !archive.WriteInt(m_iDaysBetweenFrames)) + return false; + if( !archive.WriteInt(m_iMinutesBetweenFrames)) + return false; + if( !archive.WriteString(m_sFolderName)) + return false; + if( !archive.WriteInt(m_iLightIndex)) + return false; + if( !archive.WriteBool(m_bRenderFull)) + return false; + if( !archive.WriteBool(m_bRenderPreview)) + return false; + + return true; +} + +ON_3dmAnimationProperties::CaptureTypes ON_3dmAnimationProperties::CaptureType(void) const +{ + return m_CaptureTypes; +} + +void ON_3dmAnimationProperties::SetCaptureType(CaptureTypes type) +{ + m_CaptureTypes = type; +} + +ON_wString ON_3dmAnimationProperties::FileExtension(void) const +{ + return m_sFileExtension; +} + +void ON_3dmAnimationProperties::SetFileExtension(const ON_wString& s) +{ + m_sFileExtension = s; +} + +ON_UUID ON_3dmAnimationProperties::DisplayMode(void) const +{ + return m_idDisplayMode; +} + +void ON_3dmAnimationProperties::SetDisplayMode(const ON_UUID& id) +{ + m_idDisplayMode = id; +} + +ON_wString ON_3dmAnimationProperties::ViewportName(void) const +{ + return m_sViewport; +} + +void ON_3dmAnimationProperties::SetViewportName(const ON_wString& s) +{ + m_sViewport = s; +} + +ON_wString ON_3dmAnimationProperties::HtmlFilename(void) const +{ + return m_sHtmlFilename; +} + +void ON_3dmAnimationProperties::SetHtmlFilename(const ON_wString& s) +{ + m_sHtmlFilename = s; +} + +ON_wString ON_3dmAnimationProperties::CaptureMethod(void) const +{ + return m_sCaptureMethod; +} + +void ON_3dmAnimationProperties::SetCaptureMethod(const ON_wString& s) +{ + m_sCaptureMethod = s; +} + +ON_3dPointArray& ON_3dmAnimationProperties::CameraPoints(void) +{ + return m_aCameraPoints; +} + +const ON_3dPointArray& ON_3dmAnimationProperties::CameraPoints(void) const +{ + return m_aCameraPoints; +} + +ON_3dPointArray& ON_3dmAnimationProperties::TargetPoints(void) +{ + return m_aTargetPoints; +} + +const ON_3dPointArray& ON_3dmAnimationProperties::TargetPoints(void) const +{ + return m_aTargetPoints; +} + +int ON_3dmAnimationProperties::FrameCount(void) const +{ + return m_iFrameCount; +} + +void ON_3dmAnimationProperties::SetFrameCount(int i) +{ + m_iFrameCount = i; +} + +int ON_3dmAnimationProperties::CurrentFrame(void) const +{ + return m_iCurrentFrame; +} + +void ON_3dmAnimationProperties::SetCurrentFrame(int i) +{ + m_iCurrentFrame = i; +} + +ON_UUID ON_3dmAnimationProperties::CameraPathId(void) const +{ + return m_idCameraPath; +} + +void ON_3dmAnimationProperties::SetCameraPathId(const ON_UUID& id) +{ + m_idCameraPath = id; +} + +ON_UUID ON_3dmAnimationProperties::TargetPathId(void) const +{ + return m_idTargetPath; +} + +void ON_3dmAnimationProperties::SetTargetPathId(const ON_UUID& id) +{ + m_idTargetPath = id; +} + +double ON_3dmAnimationProperties::Latitude(void) const +{ + return m_dLatitude; +} + +void ON_3dmAnimationProperties::SetLatitude(double d) +{ + m_dLatitude = d; +} + +double ON_3dmAnimationProperties::Longitude(void) const +{ + return m_dLongitude; +} + +void ON_3dmAnimationProperties::SetLongitude(double d) +{ + m_dLongitude = d; +} + +double ON_3dmAnimationProperties::NorthAngle(void) const +{ + return m_dNorthAngle; +} + +void ON_3dmAnimationProperties::SetNorthAngle(double d) +{ + m_dNorthAngle = d; +} + +int ON_3dmAnimationProperties::StartDay(void) const +{ + return m_iStartDay; +} + +void ON_3dmAnimationProperties::SetStartDay(int i) +{ + m_iStartDay = i; +} + +int ON_3dmAnimationProperties::StartMonth(void) const +{ + return m_iStartMonth; +} + +void ON_3dmAnimationProperties::SetStartMonth(int i) +{ + m_iStartMonth = i; +} + +int ON_3dmAnimationProperties::StartYear(void) const +{ + return m_iStartYear; +} + +void ON_3dmAnimationProperties::SetStartYear(int i) +{ + m_iStartYear = i; +} + +int ON_3dmAnimationProperties::EndDay(void) const +{ + return m_iEndDay; +} + +void ON_3dmAnimationProperties::SetEndDay(int i) +{ + m_iEndDay = i; +} + +int ON_3dmAnimationProperties::EndMonth(void) const +{ + return m_iEndMonth; +} + +void ON_3dmAnimationProperties::SetEndMonth(int i) +{ + m_iEndMonth= i; +} + +int ON_3dmAnimationProperties::EndYear(void) const +{ + return m_iEndYear; +} + +void ON_3dmAnimationProperties::SetEndYear(int i) +{ + m_iEndYear = i; +} + +int ON_3dmAnimationProperties::StartHour(void) const +{ + return m_iStartHour; +} + +void ON_3dmAnimationProperties::SetStartHour(int i) +{ + m_iStartHour = i; +} + +int ON_3dmAnimationProperties::StartMinutes(void) const +{ + return m_iStartMinutes; +} + +void ON_3dmAnimationProperties::SetStartMinutes(int i) +{ + m_iStartMinutes = i; +} + +int ON_3dmAnimationProperties::StartSeconds(void) const +{ + return m_iStartSeconds; +} + +void ON_3dmAnimationProperties::SetStartSeconds(int i) +{ + m_iStartSeconds = i; +} + +int ON_3dmAnimationProperties::EndHour(void) const +{ + return m_iEndHour; +} + +void ON_3dmAnimationProperties::SetEndHour(int i) +{ + m_iEndHour = i; +} + +int ON_3dmAnimationProperties::EndMinutes(void) const +{ + return m_iEndMinutes; +} + +void ON_3dmAnimationProperties::SetEndMinutes(int i) +{ + m_iEndMinutes = i; +} + +int ON_3dmAnimationProperties::EndSeconds(void) const +{ + return m_iEndSeconds; +} + +void ON_3dmAnimationProperties::SetEndSeconds(int i) +{ + m_iEndSeconds = i; +} + +int ON_3dmAnimationProperties::DaysBetweenFrames(void) const +{ + return m_iDaysBetweenFrames; +} + +void ON_3dmAnimationProperties::SetDaysBetweenFrames(int i) +{ + m_iDaysBetweenFrames = i; +} + +int ON_3dmAnimationProperties::MinutesBetweenFrames(void) const +{ + return m_iMinutesBetweenFrames; +} + +void ON_3dmAnimationProperties::SetMinutesBetweenFrames(int i) +{ + m_iMinutesBetweenFrames = i; +} + +int ON_3dmAnimationProperties::LightIndex(void) const +{ + return m_iLightIndex; +} + +void ON_3dmAnimationProperties::SetLightIndex(int i) +{ + m_iLightIndex = i; +} + +ON_wString ON_3dmAnimationProperties::FolderName(void) const +{return m_sFolderName; +} + +void ON_3dmAnimationProperties::SetFolderName(const ON_wString& s) +{ + m_sFolderName = s; +} + +const ON_ClassArray& ON_3dmAnimationProperties::Images(void) const +{ + return m_aImages; +} + +ON_ClassArray& ON_3dmAnimationProperties::Images(void) +{ + return m_aImages; +} + +ON_ClassArray& ON_3dmAnimationProperties::Dates(void) +{ + return m_aDates; +} + +const ON_ClassArray& ON_3dmAnimationProperties::Dates(void) const +{ + return m_aDates; +} + +bool ON_3dmAnimationProperties::RenderFull(void) const +{ + return m_bRenderFull; +} + +void ON_3dmAnimationProperties::SetRenderFull(bool b) +{ + m_bRenderFull = b; +} + +bool ON_3dmAnimationProperties::RenderPreview(void) const +{ + return m_bRenderPreview; +} + +void ON_3dmAnimationProperties::SetRenderPreview(bool b) +{ + m_bRenderPreview = b; +} diff --git a/opennurbs_3dm_settings.h b/opennurbs_3dm_settings.h index 3b55381f..46e877e0 100644 --- a/opennurbs_3dm_settings.h +++ b/opennurbs_3dm_settings.h @@ -1420,4 +1420,183 @@ private: bool Write_v2(ON_BinaryArchive&) const; }; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// ON_3dmAnimationProperties +// + +class ON_CLASS ON_3dmAnimationProperties +{ +public: + ON_3dmAnimationProperties() = default; + ~ON_3dmAnimationProperties() = default; + ON_3dmAnimationProperties(const ON_3dmAnimationProperties&) = default; + ON_3dmAnimationProperties& operator=(const ON_3dmAnimationProperties&) = default; + + static const ON_3dmAnimationProperties Default; + + bool Read(ON_BinaryArchive&); + bool Write(ON_BinaryArchive&) const; + +public: + enum class CaptureTypes : int + { + path = 0, + turntable, + flythrough, + day_sun_study, + seasonal_sun_study, + none + }; + + CaptureTypes CaptureType(void) const; + void SetCaptureType(CaptureTypes t); + + ON_wString FileExtension(void) const; + void SetFileExtension(const ON_wString& s); + + ON_wString CaptureMethod(void) const; + void SetCaptureMethod(const ON_wString& s); + + ON_wString ViewportName(void) const; + void SetViewportName(const ON_wString& s); + + ON_wString HtmlFilename(void) const; + void SetHtmlFilename(const ON_wString& s); + + ON_UUID DisplayMode(void) const; + void SetDisplayMode(const ON_UUID& id); + + ON_3dPointArray& CameraPoints(void); + const ON_3dPointArray& CameraPoints(void) const; + + ON_3dPointArray& TargetPoints(void); + const ON_3dPointArray& TargetPoints(void) const; + + int FrameCount(void) const; + void SetFrameCount(int i); + + int CurrentFrame(void) const; + void SetCurrentFrame(int i); + + ON_UUID CameraPathId(void) const; + void SetCameraPathId(const ON_UUID& id); + + ON_UUID TargetPathId(void) const; + void SetTargetPathId(const ON_UUID& id); + + double Latitude(void) const; + void SetLatitude(double d); + + double Longitude(void) const; + void SetLongitude(double d); + + double NorthAngle(void) const; + void SetNorthAngle(double d); + + int StartDay(void) const; + void SetStartDay(int i); + + int StartMonth(void) const; + void SetStartMonth(int i); + + int StartYear(void) const; + void SetStartYear(int i); + + int EndDay(void) const; + void SetEndDay(int i); + + int EndMonth(void) const; + void SetEndMonth(int i); + + int EndYear(void) const; + void SetEndYear(int i); + + int StartHour(void) const; + void SetStartHour(int i); + + int StartMinutes(void) const; + void SetStartMinutes(int i); + + int StartSeconds(void) const; + void SetStartSeconds(int i); + + int EndHour(void) const; + void SetEndHour(int i); + + int EndMinutes(void) const; + void SetEndMinutes(int i); + + int EndSeconds(void) const; + void SetEndSeconds(int i); + + int DaysBetweenFrames(void) const; + void SetDaysBetweenFrames(int i); + + int MinutesBetweenFrames(void) const; + void SetMinutesBetweenFrames(int i); + + int LightIndex(void) const; + void SetLightIndex(int i); + + ON_wString FolderName(void) const; + void SetFolderName(const ON_wString& s); + + ON_ClassArray& Images(void); + const ON_ClassArray& Images(void) const; + + ON_ClassArray& Dates(void); + const ON_ClassArray& Dates(void) const; + + bool RenderFull(void) const; + void SetRenderFull(const bool b); + + bool RenderPreview(void) const; + void SetRenderPreview(const bool b); + +private: + CaptureTypes m_CaptureTypes = CaptureTypes::none; + ON_wString m_sFileExtension = L"jpg"; + ON_wString m_sCaptureMethod; + ON_wString m_sHtmlFilename; + ON_wString m_sViewport; + ON_UUID m_idDisplayMode = ON_nil_uuid; + ON_3dPointArray m_aCameraPoints; + ON_3dPointArray m_aTargetPoints; + int m_iFrameCount = 100; + int m_iCurrentFrame = 1; + ON_UUID m_idCameraPath = ON_nil_uuid; + ON_UUID m_idTargetPath = ON_nil_uuid; + double m_dLatitude = 51.2838; + double m_dLongitude = 0.0; + double m_dNorthAngle = 0.0; + int m_iStartDay = 1; + int m_iStartMonth = 6; + int m_iStartYear = 2012; + int m_iEndDay = 1; + int m_iEndMonth = 6; + int m_iEndYear = 2010; + int m_iStartHour = 6; + int m_iStartMinutes = 0; + int m_iStartSeconds = 0; + int m_iEndHour = 18; + int m_iEndMinutes = 0; + int m_iEndSeconds = 59; + int m_iDaysBetweenFrames = 30; + int m_iMinutesBetweenFrames = 30; + int m_iLightIndex = -1; + ON_wString m_sFolderName; + ON_ClassArray m_aImages; + ON_ClassArray m_aDates; + bool m_bRenderFull = false; + bool m_bRenderPreview = false; + +private: + unsigned char m_reserved1 = 0; + unsigned char m_reserved2 = 0; + ON__UINT32 m_reserved4 = 0; + ON__INT_PTR reserved = 0; +}; + #endif diff --git a/opennurbs_annotationbase.cpp b/opennurbs_annotationbase.cpp index 08baf1c1..2a296fd1 100644 --- a/opennurbs_annotationbase.cpp +++ b/opennurbs_annotationbase.cpp @@ -65,12 +65,13 @@ void ON_Annotation::Internal_CopyFrom(const ON_Annotation& src) m_dimstyle_id = src.m_dimstyle_id; m_plane = src.m_plane; m_horizontal_direction = src.m_horizontal_direction; + m_allow_text_scaling = src.m_allow_text_scaling; if (nullptr != src.m_text) m_text = new ON_TextContent(*src.m_text); if (nullptr != src.m_override_dimstyle) { m_override_dimstyle = new ON_DimStyle(*src.m_override_dimstyle); -} + } } void ON_Annotation::Internal_Destroy() @@ -331,7 +332,8 @@ bool ON_Annotation::Internal_WriteAnnotation( { // content_version = 2 - added override dimstyle to ON_Annotation RH-37176 // content_version = 3 - added m_horizontal_direction - const int content_version = 3; + // content_version = 4 - added m_allow_text_scaling - Lowell + const int content_version = 4; if (false == archive.BeginWrite3dmAnonymousChunk(content_version)) return false; @@ -365,6 +367,10 @@ bool ON_Annotation::Internal_WriteAnnotation( if (!archive.WriteVector(m_horizontal_direction)) break; + // content_version = 4 ( 17 May, 2018 - Lowell) + if (!archive.WriteBool(m_allow_text_scaling)) + break; + rc = true; break; } @@ -468,6 +474,16 @@ bool ON_Annotation::Internal_ReadAnnotation( if (!archive.ReadVector(m_horizontal_direction)) break; + if (content_version <= 3) + { + rc = true; + break; + } + + // content_version = 4 ( 17 May, 2018 - Lowell) + if (!archive.ReadBool(&m_allow_text_scaling)) + break; + rc = true; break; } @@ -485,6 +501,46 @@ bool ON_Annotation::IsValid(ON_TextLog* text_log) const ); } +bool ON_Annotation::GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const +{ + const ON_Text* pText = ON_Text::Cast(this); + if (nullptr != pText) + return pText->GetTextXform(model_xform, vp, dimstyle, dimscale, text_xform_out); + + const ON_Leader* pLeader = ON_Leader::Cast(this); + if (nullptr != pLeader) + return pLeader->GetTextXform(model_xform, vp, dimstyle, dimscale, text_xform_out); + + const ON_DimLinear* pDimLinear = ON_DimLinear::Cast(this); + if (nullptr != pDimLinear) + return pDimLinear->GetTextXform(model_xform, vp, dimstyle, dimscale, text_xform_out); + + const ON_DimAngular* pDimAngular = ON_DimAngular::Cast(this); + if (nullptr != pDimAngular) + return pDimAngular->GetTextXform(model_xform, vp, dimstyle, dimscale, text_xform_out); + + const ON_DimRadial* pDimRadial = ON_DimRadial::Cast(this); + if (nullptr != pDimRadial) + return pDimRadial->GetTextXform(model_xform, vp, dimstyle, dimscale, text_xform_out); + + const ON_DimOrdinate* pDimOrdinate = ON_DimOrdinate::Cast(this); + if (nullptr != pDimOrdinate) + return pDimOrdinate->GetTextXform(model_xform, vp, dimstyle, dimscale, text_xform_out); + + const ON_Centermark* pCentermark = ON_Centermark::Cast(this); + if (nullptr != pCentermark) + return pCentermark->GetTextXform(vp, dimstyle, dimscale, text_xform_out); + + ON_ERROR("Annotation type not handled"); + return false; +} + void ON_Annotation::SetPlane(const ON_Plane& plane) { m_plane = plane; @@ -662,6 +718,19 @@ bool ON_Annotation::IsOverrideStylePointer( return (nullptr != ptr && ptr == m_override_dimstyle); } +bool ON_Annotation::AllowTextScaling() const +{ + return m_allow_text_scaling; +} + +void ON_Annotation::SetAllowTextScaling(bool scale) +{ + if (scale != m_allow_text_scaling) + { + m_allow_text_scaling = scale ? true : false; + ClearBoundingBox(); + } +} bool ON_Annotation::IsOverrideDimStyleCandidate( const ON_DimStyle* override_style_candidate, @@ -2952,7 +3021,7 @@ bool ON_Annotation::SetAnnotationBold(bool bold, const ON_DimStyle* parent_style parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); ON_wString newrtf; - if (!bold && parent_style->Font().IsBold()) + if (!bold && parent_style->Font().IsBoldInQuartet()) { newrtf = ON_TextContext::FormatRtfString(rtfstr, parent_style, true, true, false, false, false, false, false, false, L""); newrtf.Replace(L"\\b", L"\\b0"); @@ -3038,6 +3107,44 @@ bool ON_Annotation::SetAnnotationUnderline(bool underline, const ON_DimStyle* pa return false; } + +bool ON_Annotation::SetAnnotationFont(const ON_Font* font, const ON_DimStyle* parent_style) +{ + if (nullptr != font) + { + const ON_wString fontname = ON_Font::RichTextFontName(font, true); + bool bold = font->IsBoldInQuartet(); + bool italic = font->IsItalic(); + + ON_Dimension* dim = ON_Dimension::Cast(this); + const wchar_t* textstring; + if (nullptr == dim) + textstring = RichText().Array(); + else + textstring = dim->UserText(); + ON_wString rtfstr(textstring); + + const ON_wString newrtf = ON_TextContext::FormatRtfString(rtfstr, parent_style, !bold, bold, !italic, italic, false, false, false, true, fontname); + if (newrtf.IsNotEmpty()) + { + if (nullptr != dim) + { + dim->SetUserText(newrtf); + } + else + { + ON_TextContent* text = this->Text(); + ON::AnnotationType type = this->Type(); + parent_style = &ON_DimStyle::DimStyleOrDefault(parent_style); + text->ReplaceTextString(newrtf, type, parent_style); + SetText(text); + } + return true; + } + } + return false; +} + bool ON_Annotation::SetAnnotationFacename(bool set_or_clear, const wchar_t* facename, const ON_DimStyle* parent_style) { ON_Dimension* dim = ON_Dimension::Cast(this); @@ -3074,6 +3181,13 @@ bool ON_Annotation::FirstCharTextProperties(const wchar_t* rtfstr, bool& bold, b return rc; } +const ON_Font* ON_Annotation::FirstCharFont() const +{ + if(nullptr != Text()) + return Text()->FirstCharFont(); + return &ON_Font::Default; +} + diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h index 9b32433d..2c4befd3 100644 --- a/opennurbs_annotationbase.h +++ b/opennurbs_annotationbase.h @@ -158,7 +158,6 @@ protected: ) const; public: - virtual bool GetTextXform( const ON_Viewport* vp, const ON_DimStyle* dimstyle, @@ -166,6 +165,14 @@ public: ON_Xform& text_xform_out ) const = 0; + bool GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const; + void SetPlane(const ON_Plane& plane); const ON_Plane& Plane() const; @@ -393,9 +400,14 @@ public: const ON_DimStyle* ptr ) const; + // These functions are being added to continue the V5 behavior of + // per-object text scaling + bool AllowTextScaling() const; + void SetAllowTextScaling(bool scale); + protected: ON::AnnotationType m_annotation_type = ON::AnnotationType::Unset; - unsigned char m_reserved1 = 0; + bool m_allow_text_scaling = true; unsigned char m_reserved2 = 0; unsigned char m_reserved3 = 0; unsigned int m_reserved4 = 0; @@ -815,12 +827,12 @@ public: */ const bool FontSubstituted(const ON_DimStyle* parent_style) const; - - bool SetAnnotationBold(bool bold, const ON_DimStyle* dimstyle); bool SetAnnotationItalic(bool italic, const ON_DimStyle* dimstyle); bool SetAnnotationUnderline(bool underline, const ON_DimStyle* dimstyle); bool SetAnnotationFacename(bool set_or_clear, const wchar_t* facename, const ON_DimStyle* parent_style); + bool SetAnnotationFont(const ON_Font* font, const ON_DimStyle* parent_style); + static bool SetAnnotationTextFormat(ON_wString& rtf_in, const wchar_t* fmt_str_on, const wchar_t* fmt_str_off, bool set_on); static bool SetRtfFmt(ON_wString& rtf_in, const wchar_t* fmt_str); @@ -828,6 +840,8 @@ public: static int FindRtfTable(ON_wString rtf_in, int startidx, const wchar_t* tablename); static bool FirstCharTextProperties(const wchar_t* rtf_in, bool& bold, bool& italic, bool& underline, ON_wString& facename); + + const ON_Font* FirstCharFont() const; }; diff --git a/opennurbs_apple_nsfont.cpp b/opennurbs_apple_nsfont.cpp new file mode 100644 index 00000000..b1329898 --- /dev/null +++ b/opennurbs_apple_nsfont.cpp @@ -0,0 +1,231 @@ +// +// Copyright (c) 1993-2018 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// + +#include "opennurbs.h" + +#if !defined(ON_COMPILING_OPENNURBS) +// This check is included in all opennurbs source .c and .cpp files to insure +// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. +// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined +// and the opennurbs .h files alter what is declared and how it is declared. +#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs +#endif + +#if defined(ON_RUNTIME_APPLE) && defined(ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) + +#include "opennurbs_internal_glyph.h" +#include "opennurbs_apple_nsfont.h" + +void ON_AppleNSFontGetFontMetrics( + const ON_Font* font, + ON_FontMetrics& font_metrics + ) +{ + for (;;) + { + if (nullptr == font) + break; + + font = font->ManagedFont(); + if (nullptr == font) + break; + + NSFont* appleFont = font->AppleFont(); + if (nullptr == appleFont) + break; + + // No way to get actual font design units per M from NSFont API? + // 2048 is a common value and the default. + unsigned int font_design_units_per_M = 2048; +#if defined(OPENNURBS_FREETYPE_SUPPORT) + const unsigned int freetypeUPM = ON_FreeTypeGetFontUnitsPerM(font); + if (freetypeUPM > 0) + font_design_units_per_M = freetypeUPM; +#endif + + const double point_size = appleFont.pointSize; + const double UPM = font_design_units_per_M; + + // This scaling approach recovers correct values in font design units + // when compared with values from DWrite and FreeType. + const double pointSizeToUPM = (point_size > 0.0) ? (UPM/point_size) : 1.0; + + const double leading = pointSizeToUPM*((double)appleFont.leading); + const double ascent = pointSizeToUPM*((double)appleFont.ascender); + const double descent = pointSizeToUPM*((double)appleFont.descender); + + const double line_space1 = ascent - descent + leading; + const double line_space2 = pointSizeToUPM*((double)(appleFont.ascender-appleFont.descender+appleFont.leading)); + const double line_space = (line_space2>=line_space1) ? line_space2 : line_space1; + + const double ascent_of_x = pointSizeToUPM*((double)appleFont.xHeight); + double ascent_of_capital = pointSizeToUPM*((double)appleFont.capHeight); + + const double underscore_position = pointSizeToUPM*((double)appleFont.underlinePosition); + const double underscore_thickness = pointSizeToUPM*((double)appleFont.underlineThickness); + + font_metrics = ON_FontMetrics::Unset; + font_metrics.SetHeights( + ascent, + descent, + UPM, + line_space + ); + if (false == font_metrics.AscentDescentAndUPMAreValid()) + break; + + font_metrics.SetAscentOfx(ascent_of_x); + font_metrics.SetAscentOfCapital(ascent_of_capital); + font_metrics.SetUnderscore(underscore_position,underscore_thickness); + + // Have to fake strikeout settings - not in NSFont API? + //int strikeout_position = ...; + //int strikeout_thickness = ...; + double h = (ascent_of_capital > 0) ? ascent_of_capital : ascent; + if (h > 0.0) + { + font_metrics.SetStrikeout( + (0.5*h), + (0.5*underscore_thickness) + ); + } + + if (font_metrics.AscentDescentAndUPMAreValid()) + return; + break; + } + + font_metrics = ON_FontMetrics::Unset; + return; +} + +#if defined(ON_NSFONT_GLYPH_SUPPORT_WIP) + +bool ON_AppleNSFontGetGlyphMetrics( + NSFont* appleFont, + unsigned int font_design_units_per_M, + unsigned int glyphIndex, + class ON_TextBox& glyph_metrics +) +{ + // TODO - make this work on MacOS + glyph_metrics = ON_TextBox::Unset; + return false; +} + +bool ON_AppleNSFontGetGlyphOutline( + NSFont* appleFont, + unsigned int font_design_units_per_M, + unsigned int glyphIndex, + ON_OutlineFigure::Type figure_type, + class ON_Outline& outline +) +{ + // TODO - make this work on MacOS + outline = ON_Outline::Unset; + return false; +} + +ON__UINT_PTR ON_AppleNSFontGetGlyphMetrics( + const ON_FontGlyph* glyph, + unsigned int font_design_units_per_M, + ON_TextBox& glyph_metrics +) +{ + glyph_metrics = ON_TextBox::Unset; + + if (nullptr == glyph) + return 0; + + if (false == glyph->CodePointIsSet()) + return 0; + + const ON_Font* font = glyph->Font(); + if (nullptr == font) + return 0; + + NSFont* appleFont = font->AppleFont(); + if (nullptr == appleFont) + return 0; + + ON__UINT_PTR glpyh_id = 0; + + bool rc = false; + + // NEVER TESTED + //NSString* baseString = [NSString stringWithFormat:@"%C", glpyh->CodePoint()]; + //NSGlyphInfo* glyphInfo = [NSGlyphInfo glyphInfoWithGlyph:glyph forFont:appleFont baseString: baseString]; + //if (nullptr == glyphInfo) + // return 0; + + //glyph_id = (ON__UINT_PTR)glyphInfo.glyphID(); + + //const bool rc + // = (0 != glpyh_id) + // ? ON_AppleNSFontGetGlyphMetrics(appleFont, (unsigned int)glpyh_id, glyph_metrics) + // : false; + + return rc ? glpyh_id : 0; +} + +bool ON_AppleNSFontGetGlyphOutline( + const ON_FontGlyph* glyph, + unsigned int font_design_units_per_M, + ON_OutlineFigure::Type figure_type, + class ON_Outline& outline +) +{ + outline = ON_Outline::Unset; + + if (nullptr == glyph) + return false; + + if (false == glyph->CodePointIsSet()) + return false; + + ON__UINT64 glpyh_id = (ON__UINT64)glyph->FontGlyphId(); + if (glpyh_id > 0xFFFFFFFF) + return false; + + const unsigned int glyphIndex = (unsigned int)glpyh_id; + + const ON_Font* font = glyph->Font(); + if (nullptr == font) + return false; + + if (ON_OutlineFigure::Type::Unset == figure_type) + { + ON_OutlineFigure::Type font_figure_type = font->OutlineFigureType(); + if (ON_OutlineFigure::Type::Unset != font_figure_type) + { + figure_type = font_figure_type; + } + } + + NSFont* appleFont = font->AppleFont(); + if (nullptr == appleFont) + return false; + + const bool rc = ON_AppleNSFontGetGlyphOutline( + appleFont, + font_design_units_per_M, + glyphIndex, + figure_type, + outline + ); + + return rc; +} + +#endif +#endif diff --git a/opennurbs_apple_nsfont.h b/opennurbs_apple_nsfont.h new file mode 100644 index 00000000..816c5ab3 --- /dev/null +++ b/opennurbs_apple_nsfont.h @@ -0,0 +1,40 @@ +/* +// +// Copyright (c) 1993-2018 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_APPLE_NSFONT_INC_) +#define OPENNURBS_APPLE_NSFONT_INC_ + +//#define ON_NSFONT_GLYPH_SUPPORT_WIP +#if defined(ON_RUNTIME_APPLE) && defined(ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) && defined(ON_NSFONT_GLYPH_SUPPORT_WIP) + +ON_DECL +bool ON_AppleNSFontGetGlyphMetrics( + NSFont* appleFont, + unsigned int font_design_units_per_M, + unsigned int glyphIndex, + class ON_TextBox& glyph_metrics +); + +ON_DECL +bool ON_AppleNSFontGetGlyphOutline( + NSFont* appleFont, + unsigned int font_design_units_per_M, + unsigned int glyphIndex, + ON_OutlineFigure::Type figure_type, + class ON_Outline& outline +); +#endif + +#endif diff --git a/opennurbs_archive.cpp b/opennurbs_archive.cpp index 30f1cde8..e4ff7e21 100644 --- a/opennurbs_archive.cpp +++ b/opennurbs_archive.cpp @@ -3351,6 +3351,10 @@ ON_BinaryArchive::WriteUTF16String( bool ON_BinaryArchive::WriteString( const ON_String& sUTF8 ) { + // The ON_String::IsValid call prevents corrupt strings from breaking file IO + // The parameter MUST be false here. + sUTF8.IsValid(false); + size_t string_utf8_element_count = sUTF8.Length(); if ( string_utf8_element_count ) string_utf8_element_count++; @@ -3638,6 +3642,12 @@ ON_BinaryArchive::WriteString( const ON_wString& s ) #pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4127 ) #endif + // The ON_wString::IsValid call prevents corrupt strings from breaking file IO + // The parameter MUST be false here. + s.IsValid(false); + + // NOTE WELL: + // In some cases there are embedded nulls in strings. size_t string_element_count = s.Length(); if ( string_element_count > 0) string_element_count++; @@ -16572,7 +16582,12 @@ ON_BinaryFile::EnableMemoryBuffer( size_t ON_BinaryFile::Internal_ReadOverride( size_t count, void* p ) { - return (m_fp) ? fread( p, 1, count, m_fp ) : 0; + const size_t rc = (m_fp) ? fread( p, 1, count, m_fp ) : 0; + if (rc != count && nullptr != m_fp) + { + ON_ERROR("fread() failed."); + } + return rc; } size_t ON_BinaryFile::Internal_WriteOverride( size_t count, const void* p ) @@ -16586,6 +16601,10 @@ size_t ON_BinaryFile::Internal_WriteOverride( size_t count, const void* p ) if ( !Flush() ) // flush existing memory buffer to disk return 0; rc = fwrite( p, 1, count, m_fp ); // write directly to disk + if (rc != count) + { + ON_ERROR("fwrite() failed - situation A."); + } } else { @@ -16600,6 +16619,10 @@ size_t ON_BinaryFile::Internal_WriteOverride( size_t count, const void* p ) else { rc = fwrite( p, 1, count, m_fp ); + if (rc != count) + { + ON_ERROR("fwrite() failed - situation B."); + } } } return rc; diff --git a/opennurbs_archive.h b/opennurbs_archive.h index e83a13f1..20e33f21 100644 --- a/opennurbs_archive.h +++ b/opennurbs_archive.h @@ -1268,6 +1268,16 @@ public: const ON_ManifestMapItem& b ); + static int CompareTypeAndSourceId( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ); + + static int CompareTypeAndDestinationId( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ); + static int CompareTypeAndSourceIdAndIndex( const ON_ManifestMapItem& a, const ON_ManifestMapItem& b @@ -1552,6 +1562,22 @@ public: const class ON_ManifestMapItem& map_item ); + /* + Parameters: + map_item - [in] + The source settings must exacty match source settings of an existing map. + The destination settings are the new values to assign. + bIgnoreSourceIndex - [in] + If true, the value of map_item.SourceIndex() is ignored. + Otherwise, it must exactly match the source index setting of an existing map. + Return: + True if a mapping was successfully updated (even when the destation settings did not change). + */ + bool UpdatetMapItemDestination( + const class ON_ManifestMapItem& map_item, + bool bIgnoreSourceIndex + ); + const class ON_ManifestMapItem& MapItemFromSourceId( const ON_UUID& source_item_id ) const; diff --git a/opennurbs_archive_manifest.cpp b/opennurbs_archive_manifest.cpp index 70e57eb7..d6c54985 100644 --- a/opennurbs_archive_manifest.cpp +++ b/opennurbs_archive_manifest.cpp @@ -462,6 +462,9 @@ void ON_SHA1::AccumulateString( ON_StringMapOrdinalType mapping ) { + // Do not permit corrupt strings to crash this code. + str.IsValid(false); + AccumulateString( str.Array(), str.Length(), mapping); } @@ -571,6 +574,9 @@ void ON_SHA1::AccumulateString( ON_StringMapOrdinalType mapping ) { + // Do not permit corrupt strings to crash this code. + str.IsValid(false); + AccumulateString( str.Array(), str.Length(), mapping); } @@ -1279,6 +1285,35 @@ int ON_ManifestMapItem::Compare( return ON_UuidCompare(a.m_destination_id, b.m_destination_id); } + +int ON_ManifestMapItem::CompareTypeAndSourceId( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ) +{ + const unsigned int at = static_cast(a.m_component_type); + const unsigned int bt = static_cast(b.m_component_type); + if (at < bt) + return -1; + if (at > bt) + return 1; + return ON_UuidCompare(a.m_source_id, b.m_source_id); +} + +int ON_ManifestMapItem::CompareTypeAndDestinationId( + const ON_ManifestMapItem& a, + const ON_ManifestMapItem& b + ) +{ + const unsigned int at = static_cast(a.m_component_type); + const unsigned int bt = static_cast(b.m_component_type); + if (at < bt) + return -1; + if (at > bt) + return 1; + return ON_UuidCompare(a.m_destination_id, b.m_destination_id); +} + int ON_ManifestMapItem::CompareTypeAndSourceIdAndIndex( const ON_ManifestMapItem& a, const ON_ManifestMapItem& b @@ -1316,7 +1351,6 @@ int ON_ManifestMapItem::CompareTypeAndDestinationIdAndIndex( } - int ON_ManifestMapItem::CompareTypeAndSourceIndex( const ON_ManifestMapItem& a, const ON_ManifestMapItem& b) @@ -1679,7 +1713,7 @@ void ON_ManifestMapImpl::Internal_Copy(const ON_ManifestMapImpl& src) if (src.m_source_id_hash_table.ItemCount() > 0) { const ON__UINT32 src_hash_table_sn = src.m_source_id_hash_table.HashTableSerialNumber(); - ON_FixedSizePoolIterator fit(m_fsp); + ON_FixedSizePoolIterator fit(src.m_fsp); for (const ON_ManifestMap_Hash32TableItem* src_item = static_cast(fit.FirstElement()); nullptr != src_item; src_item = static_cast(fit.NextElement()) @@ -1740,7 +1774,16 @@ ON_ManifestMap::~ON_ManifestMap() bool ON_ManifestMap::UpdatetMapItemDestination( const class ON_ManifestMapItem& map_item - ) +) +{ + const bool bIgnoreSourceIndex = false; + return UpdatetMapItemDestination(map_item, bIgnoreSourceIndex); +} + +bool ON_ManifestMap::UpdatetMapItemDestination( + const class ON_ManifestMapItem& map_item, + bool bIgnoreSourceIndex + ) { if (map_item.SourceIsUnset()) { @@ -1766,10 +1809,26 @@ bool ON_ManifestMap::UpdatetMapItemDestination( } } + // map_item_id = item in this manifest map for the SourceId(). + // map_item_id must be valid and have a matching type and id. const class ON_ManifestMapItem& map_item_id = MapItemFromSourceId(map_item.SourceId()); - if ( 0 != ON_ManifestMapItem::CompareTypeAndSourceIdAndIndex(map_item_id,map_item) ) + + if (ON_nil_uuid == map_item_id.SourceId()) { - ON_ERROR("map_item source settings are not equal to corresponding ON_ManifestMap item source settings."); + ON_ERROR("map_item.SourceId() is not in the ON_ManifestMap as a source id."); + return false; + } + if ( 0 != ON_ManifestMapItem::CompareTypeAndSourceId(map_item_id,map_item) ) + { + ON_ERROR("map_item type is not equal to corresponding ON_ManifestMap item source type."); + return false; + } + if ( + false == bIgnoreSourceIndex + && map_item_id.SourceIndex() != map_item.SourceIndex() + ) + { + ON_ERROR("map_item source index is not equal to corresponding ON_ManifestMap item source index."); return false; } @@ -1780,7 +1839,7 @@ bool ON_ManifestMap::UpdatetMapItemDestination( if ( map_item_index.ComponentType() != map_item.ComponentType() || map_item_index.SourceId() != map_item.SourceId() - || map_item_index.SourceIndex() != map_item.SourceIndex() + || (false == bIgnoreSourceIndex && map_item_index.SourceIndex() != map_item.SourceIndex()) ) { ON_ERROR("map_item source settings are not equal to corresponding ON_ManifestMap item source settings."); diff --git a/opennurbs_array_defs.h b/opennurbs_array_defs.h index 4f54fba8..18497222 100644 --- a/opennurbs_array_defs.h +++ b/opennurbs_array_defs.h @@ -481,43 +481,49 @@ T& ON_SimpleArray::AppendNew() template void ON_SimpleArray::Append( const T& x ) { + const T* p = &x; if ( m_count == m_capacity ) { const int newcapacity = NewCapacity(); - if (m_a) + if ( p >= m_a && p < (m_a + m_capacity) ) { - const int s = (int)(&x - m_a); // (int) cast is for 64 bit pointers - if ( s >= 0 && s < m_capacity ) - { - // 26 Sep 2005 Dale Lear - // User passed in an element of the m_a[] - // that will get reallocated by the call - // to Reserve(newcapacity). - T temp; // ON_*Array<> templates do not require robust copy constructor. - temp = x; // ON_*Array<> templates require a robust operator=. - Reserve( newcapacity ); - m_a[m_count++] = temp; - return; - } + // 26 Sep 2005 Dale Lear + // x is in the block of memory about to be reallocated. + void* temp = onmalloc(sizeof(T)); + memcpy(temp, p, sizeof(T)); + p = (T*)temp; } Reserve(newcapacity); } - m_a[m_count++] = x; + m_a[m_count++] = *p; + if (p != &x) + onfree((void*)p); } template -void ON_SimpleArray::Append( int count, const T* p ) +void ON_SimpleArray::Append( int count, const T* buffer ) { - if ( count > 0 && p ) + if ( count > 0 && nullptr != buffer ) { + const size_t sizeof_buffer = count * sizeof(T); + void* temp = nullptr; if ( count + m_count > m_capacity ) { int newcapacity = NewCapacity(); if ( newcapacity < count + m_count ) newcapacity = count + m_count; + if ( buffer >= m_a && buffer < (m_a + m_capacity) ) + { + // buffer is in the block of memory about to be reallocated + temp = onmalloc(sizeof_buffer); + memcpy(temp, buffer, sizeof_buffer); + buffer = (const T*)temp; + } Reserve( newcapacity ); } - memcpy( (void*)(m_a + m_count), (void*)(p), count*sizeof(T) ); + memcpy( (void*)(m_a + m_count), (void*)(buffer), sizeof_buffer ); + if (nullptr != temp) + onfree(temp); m_count += count; } } @@ -527,14 +533,24 @@ void ON_SimpleArray::Insert( int i, const T& x ) { if( i >= 0 && i <= m_count ) { + const T* p = &x; if ( m_count == m_capacity ) { + if (&x >= m_a && &x < (m_a + m_capacity)) + { + // x is in the block of memory about to be reallocated. + void* temp = onmalloc(sizeof(T)); + memcpy(temp, p, sizeof(T)); + p = (T*)temp; + } int newcapacity = NewCapacity(); Reserve( newcapacity ); } m_count++; Move( i+1, i, m_count-1-i ); - m_a[i] = x; + m_a[i] = *p; + if (p != &x) + onfree((void*)p); } } diff --git a/opennurbs_atomic_op.h b/opennurbs_atomic_op.h new file mode 100644 index 00000000..c2ef278c --- /dev/null +++ b/opennurbs_atomic_op.h @@ -0,0 +1,78 @@ +/* $NoKeywords: $ */ +/* +// +// Copyright (c) 1993-2018 Robert McNeel & Associates. All rights reserved. +// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert +// McNeel & Associates. +// +// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. +// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF +// MERCHANTABILITY ARE HEREBY DISCLAIMED. +// +// For complete openNURBS copyright information see . +// +//////////////////////////////////////////////////////////////// +*/ + +#if !defined(OPENNURBS_ATOMIC_OP_INC_) +#define OPENNURBS_ATOMIC_OP_INC_ + + +/* +Description: + Expert user tool to decrement the value of the parameter as an atomic operation. + +Parameters: + ptr_int32 - [in] + A non-null pointer to a signed 32-bit integer. + +Returns: + Decremented value. + +Example: + int i = 3; + // j will be 2 + int j = ON_AtomicDecrementInt32(&i); + +Remarks: + Caller is responsible for insuring ptr_int32 is not nullptr. +*/ +// ON_AtomicDecrementInt32(int* ptr_int32) + + +/* +Description: + Expert user tool to increment the value of the parameter as an atomic operation. + +Parameters: + ptr_int32 - [in] + A non-null pointer to a signed 32-bit integer. + +Returns: + Incremented value. + +Example: + int i = 3; + // j will be 4 + int j = ON_AtomicIncrementInt32(&i); + +Remarks: + Caller is responsible for insuring ptr_int32 points to + a signed 32-bit integer. +*/ +// ON_AtomicIncrementInt32(int* ptr_int32) + +#if defined(ON_RUNTIME_WIN) +#define ON_AtomicDecrementInt32(ptr_int32) InterlockedDecrement((long*)(ptr_int32)) +#define ON_AtomicIncrementInt32(ptr_int32) InterlockedIncrement((long*)(ptr_int32)) +#elif defined(ON_RUNTIME_APPLE_MACOS) +#include +#define ON_AtomicDecrementInt32(ptr_int32) OSAtomicDecrement32((int*)(ptr_int32)) +#define ON_AtomicIncrementInt32(ptr_int32) OSAtomicIncrement32((int*)(ptr_int32)) +#else +// NOT thread safe +#define ON_AtomicDecrementInt32(ptr_int32) (--(*ptr_int32)) +#define ON_AtomicIncrementInt32(ptr_int32) (++(*ptr_int32)) +#endif + +#endif diff --git a/opennurbs_bitmap.cpp b/opennurbs_bitmap.cpp index 461ea37e..b443bb98 100644 --- a/opennurbs_bitmap.cpp +++ b/opennurbs_bitmap.cpp @@ -199,10 +199,13 @@ int ON_WindowsBitmapHelper_PaletteColorCount( int bmiHeader_biClrUsed, int bmiHe { case 1: color_count = 2; + break; case 4: color_count = 16; + break; case 8: color_count = 256; + break; default: color_count = 0; } diff --git a/opennurbs_brep.cpp b/opennurbs_brep.cpp index 0ec420ea..f8455a3c 100644 --- a/opennurbs_brep.cpp +++ b/opennurbs_brep.cpp @@ -5354,6 +5354,8 @@ int ON_Brep::NextTrim(int ti) const { const ON_BrepTrim& trim = m_T[ti]; const int li = trim.m_li; + if (li < 0 || li >= m_L.Count()) + return -1; const ON_BrepLoop& loop = m_L[li]; const int trim_count = loop.m_ti.Count(); int lti; @@ -5368,10 +5370,12 @@ int ON_Brep::PrevTrim(int ti) const { const ON_BrepTrim& trim = m_T[ti]; const int li = trim.m_li; + if (li < 0 || li >= m_L.Count()) + return -1; const ON_BrepLoop& loop = m_L[li]; const int trim_count = loop.m_ti.Count(); int lti; - for ( lti = 0; loop.m_ti[lti] != ti && lti < trim_count; lti++) + for ( lti = 0; lti < trim_count && loop.m_ti[lti] != ti; lti++) ;/* empty for*/ if ( lti < 0 || lti >= trim_count ) return -1; @@ -5815,6 +5819,10 @@ bool ON_Brep::GetTightBoundingBox(ON_BoundingBox& tight_bbox, bool bGrowBox, con { const ON_BrepFace& face = m_F[i]; + //15 July 2018 - Chuck - If a face has been deleted, it will not have an associated surface. + if (face.SurfaceOf() == NULL) + continue; + ON_NurbsSurface nsrf; if (0 == face.SurfaceOf()->GetNurbForm(nsrf) || false == nsrf.IsValid()) return false; @@ -7072,6 +7080,14 @@ int ON_ClosedCurveOrientation( const ON_Curve& curve, const ON_Xform* xform ) } +int ON_ClosedCurveOrientation(const ON_Curve& curve, const ON_Plane& plane) +{ + ON_Xform x; + x.Rotation(plane, ON_Plane::World_xy); + return ON_ClosedCurveOrientation(curve, &x); +} + + double ON_CurveOrientationArea( const ON_Curve* curve, const ON_Interval* domain, @@ -8318,7 +8334,6 @@ static void PropagateLabel(const ON_Brep& B, V.m_vertex_user.i = label; } } - for (int trim_i=0; trim_i= min_dist) + return ChangeClosedCurveSeam(t); + return false; +} + bool ON_Curve::ChangeClosedCurveSeam( double t ) { // this virtual function is overridden by curves that can be closed @@ -1241,7 +1249,7 @@ bool ON_Curve::EvaluatePoint( const class ON_ObjRef& objref, ON_3dPoint& P ) con ON_3dPoint F1, F2; if ( ellipse.GetFoci(F1,F2) ) { - P = ( F1.DistanceTo(Q) <= F1.DistanceTo(Q)) ? F1 : F2; + P = ( F1.DistanceTo(Q) <= F2.DistanceTo(Q)) ? F1 : F2; rc = true; } } @@ -1668,6 +1676,25 @@ static bool ForceMatchArcs(ON_ArcCurve& Arc0, int end0, ON_ArcCurve& Arc1, int e } +static bool FastIsShort(const ON_Curve& crv, double tol) + +{ + ON_3dPoint P[5]; + P[0] = crv.PointAtStart(); + P[4] = crv.PointAtEnd(); + if (P[0].DistanceTo(P[4]) >= tol) + return false; + double d = 0.0; + for (int i=1; i<4; i++){ + P[i] = crv.PointAt(crv.Domain().ParameterAt(0.25*(double)i)); + d += P[i].DistanceTo(P[i-1]); + if (d >= tol) + return false; + } + d += P[4].DistanceTo(P[3]); + return (d < tol) ? true : false; +} + bool ON_ForceMatchCurveEnds(ON_Curve& Crv0, int end0, ON_Curve& Crv1, int end1) { @@ -1740,22 +1767,68 @@ bool ON_ForceMatchCurveEnds(ON_Curve& Crv0, int end0, ON_Curve& Crv1, int end1) } bool rc = true; + bool bTryAgain = false; if (bMove[0]){ bool brc = (end0) ? seg[0]->SetEndPoint(P) : seg[0]->SetStartPoint(P); if (!brc) rc = false; + else { + //23 Jan 2018 - Chuck - If yanking a polycurve segment at the join causes + //that seg to be tiny, remove it and try again. See RH-43661 + if (ON_CurveType(&Crv0) == ON::ctPolycurve){ + ON_PolyCurve* polycurve = ON_PolyCurve::Cast(&Crv0); + if (polycurve->Count() > 1 && FastIsShort(*seg[0], 10.0*ON_ZERO_TOLERANCE)) + bTryAgain = (end0) ? polycurve->Remove() : polycurve->Remove(0); + } + } } if (bMove[1]){ bool brc = (end1) ? seg[1]->SetEndPoint(P) : seg[1]->SetStartPoint(P); if (!brc) rc = false; + else { + //23 Jan 2018 - Chuck - If yanking a polycurve segment at the join causes + //that seg to be tiny, remove it and try again. See RH-43661 + if (ON_CurveType(&Crv1) == ON::ctPolycurve){ + ON_PolyCurve* polycurve = ON_PolyCurve::Cast(&Crv1); + if (polycurve->Count() > 1 && FastIsShort(*seg[1], 10.0*ON_ZERO_TOLERANCE)){ + bool bRem = (end1) ? polycurve->Remove() : polycurve->Remove(0); + if (bRem) + bTryAgain = true; + } + } + } } + //23 Jan 2018 - Chuck - If yanking a polycurve segment at the join causes + //that seg to be tiny, remove it and try again. See RH-43661 + if (bTryAgain)//One of these is a polycurve with one less segment thane before, so no infinite recursion. + return ON_ForceMatchCurveEnds(Crv0, end0, Crv1, end1); + return rc; } +static bool Internal_IsUniformCubic(const ON_NurbsCurve& curve) +{ + if (4 != curve.m_order) + return false; + if (curve.m_cv_count < curve.m_order) + return false; + if (0 != curve.m_is_rat) + return false; + if (nullptr == curve.m_knot) + return false; + const int knot_count = curve.KnotCount(); + for (int i = 0; i < knot_count; i++) + { + if (curve.m_knot[i] != (double)(i - 2)) + return false; + } + return true; +} + bool ON_NurbsCurve::RepairBadKnots( double knot_tolerance, bool bRepair ) { bool rc = false; @@ -1782,11 +1855,14 @@ bool ON_NurbsCurve::RepairBadKnots( double knot_tolerance, bool bRepair ) { if ( m_knot[0] != m_knot[m_order-2] || m_knot[m_cv_count-1] != m_knot[m_cv_count+m_order-3] ) { - rc = true; - if ( bRepair ) - ClampEnd(2); - else - return rc; + if (false == Internal_IsUniformCubic(*this)) + { + rc = true; + if (bRepair) + ClampEnd(2); + else + return rc; + } } } diff --git a/opennurbs_curve.h b/opennurbs_curve.h index 28e542d6..5a0d5128 100644 --- a/opennurbs_curve.h +++ b/opennurbs_curve.h @@ -203,6 +203,23 @@ public: ); + /* + Description: + If this curve is closed, then modify it so that + the start/end point is at curve parameter t. + Parameters: + t - [in] curve parameter of new start/end point. The + returned curves domain will start at t. + min_dist - [in] Do not change if Crv(t) is within min_dist of the original seam + Returns: + true if successful, and seam was moved. + */ + + bool ChangeClosedCurveSeam( + double t, + double min_dist + ); + /* Description: If this curve is closed, then modify it so that @@ -1413,13 +1430,17 @@ Paramters: curve - [in] simple (no self intersections) closed planar curve xform - [in] Transformation to map the curve to the xy plane. If the curve is parallel to the xy plane, you may pass nullptr. + plane - [in] If curve is on plane then determine the orientation in relation to + plane's orientation. Returns: +1: The curve's orientation is counter clockwise in the xy plane. -1: The curve's orientation is clockwise in the xy plane. 0: Unable to compute the curve's orientation. */ ON_DECL -int ON_ClosedCurveOrientation( const ON_Curve& curve, const ON_Xform* xform ); +int ON_ClosedCurveOrientation(const ON_Curve& curve, const ON_Xform* xform); +ON_DECL +int ON_ClosedCurveOrientation(const ON_Curve& curve, const ON_Plane& plane); /* diff --git a/opennurbs_defines.cpp b/opennurbs_defines.cpp index aa05ba53..d913bf17 100644 --- a/opennurbs_defines.cpp +++ b/opennurbs_defines.cpp @@ -2321,6 +2321,28 @@ ON_2udex::ON_2udex( , j(jValue) {} +int ON_2udex::DictionaryCompare( + const ON_2udex* lhs, + const ON_2udex* rhs +) +{ + if (lhs == rhs) + return 0; + if (nullptr == lhs) + return 1; + if (nullptr == rhs) + return -1; + if (lhs->i < rhs->i) + return -1; + if (lhs->i > rhs->i) + return 1; + if (lhs->j < rhs->j) + return -1; + if (lhs->j > rhs->j) + return 1; + return 0; +} + ON_3udex::ON_3udex( unsigned int iValue, unsigned int jValue, diff --git a/opennurbs_defines.h b/opennurbs_defines.h index fe08c2c6..b8334357 100644 --- a/opennurbs_defines.h +++ b/opennurbs_defines.h @@ -62,6 +62,8 @@ #define ON_ENUM_FROM_UNSIGNED_CASE(e) case (unsigned int)e: return(e); break #define ON_ENUM_TO_STRING_CASE(e) case e: return( ON_String(#e) ); break #define ON_ENUM_TO_WIDE_STRING_CASE(e) case e: return( ON_wString(#e) ); break +#define ON_ENUM_TO_STRING_CASE_SET(e,s) case e: (s)=ON_String(#e); break +#define ON_ENUM_TO_WIDE_STRING_CASE_SET(e,s) case e: (s)=ON_wString(#e); break /* export/import */ #if defined(OPENNURBS_EXPORTS) @@ -261,6 +263,19 @@ extern ON_EXTERN_DECL const float ON_FLT_PINF; extern ON_EXTERN_DECL const float ON_FLT_NINF; +/* +The ON_PTR_SEMAPHORE* values are used in rare cases +when a special signal must be passed as a pointer argument. +The values must be a multiple of 8 to suppress runtime pointer alignment checks. +The values must never be a valid user heap or stack pointer value. +*/ +#define ON_PTR_SEMAPHORE1 ((ON__UINT_PTR)8) +#define ON_PTR_SEMAPHORE2 ((ON__UINT_PTR)16) +#define ON_PTR_SEMAPHORE3 ((ON__UINT_PTR)24) +#define ON_PTR_SEMAPHORE4 ((ON__UINT_PTR)32) +#define ON_PTR_SEMAPHORE_MAX ((ON__UINT_PTR)32) + + /* Description: Paramters: @@ -444,6 +459,11 @@ public: ON_2udex(unsigned int i, unsigned int j); + static int DictionaryCompare( + const ON_2udex* lhs, + const ON_2udex* rhs + ); + static const ON_2udex Unset; // (ON_UNSET_UINT_INDEX, ON_UNSET_UINT_INDEX); static const ON_2udex Zero; // (0, 0) }; @@ -654,9 +674,12 @@ public: Returns: The value of OPENNURBS_VERSION_BRANCH, which is defined in opennurbs_version.h 0: developer build - 1: trunk build - 2: release candidate build - 3: release build + 1: Windows Commercial build + 2: Mac Commercial build + 3: Windows BETA build + 4: Mac Beta build + 5: Windows WIP build + 6: Mac WIP build */ static unsigned int VersionBranch(); diff --git a/opennurbs_detail.cpp b/opennurbs_detail.cpp index a4b0d29b..1b3432f3 100644 --- a/opennurbs_detail.cpp +++ b/opennurbs_detail.cpp @@ -196,3 +196,46 @@ bool ON_DetailView::Transform( const ON_Xform& xform ) return m_boundary.Transform(xform); } +bool ON_DetailView::UpdateFrustum( + ON::LengthUnitSystem model_units, + ON::LengthUnitSystem paper_units +) +{ + if (!m_view.m_vp.IsParallelProjection()) + return false; + if (!(m_page_per_model_ratio > 0.0)) + return false; + + ON_BoundingBox bbox = BoundingBox(); + double port_width = bbox.m_max.x - bbox.m_min.x; + double port_height = bbox.m_max.y - bbox.m_min.y; + if (!(port_height > 0.0) || !(port_width > 0.0)) + return false; + + double detail_width_on_paper = bbox.m_max.x - bbox.m_min.x; + double detail_width_on_paper_mm = detail_width_on_paper * ON::UnitScale(paper_units, ON::LengthUnitSystem::Millimeters); + if (!(detail_width_on_paper_mm > 0.0)) + return false; + + double frustum_width_mm = detail_width_on_paper_mm / m_page_per_model_ratio; + double frustum_width = frustum_width_mm * ON::UnitScale(ON_UnitSystem(ON::LengthUnitSystem::Millimeters), model_units); + + double aspect = fabs(port_width / port_height); + if (!(aspect > 0.0)) + return false; + + double frustum_height = frustum_width / aspect; + if (!(frustum_height > 0.0)) + return false; + + double fr_left, fr_right, fr_top, fr_bottom, fr_near, fr_far; + if (m_view.m_vp.GetFrustum(&fr_left, &fr_right, &fr_bottom, &fr_top, &fr_near, &fr_far)) + { + fr_left = (fr_left + fr_right) / 2.0 - frustum_width / 2.0; + fr_right = fr_left + frustum_width; + fr_bottom = (fr_bottom + fr_top) / 2.0 - frustum_height / 2.0; + fr_top = fr_bottom + frustum_height; + return m_view.m_vp.SetFrustum(fr_left, fr_right, fr_bottom, fr_top, fr_near, fr_far); + } + return false; +} diff --git a/opennurbs_detail.h b/opennurbs_detail.h index e759a528..f820423c 100644 --- a/opennurbs_detail.h +++ b/opennurbs_detail.h @@ -81,6 +81,12 @@ public: // 2d curve in page layout coordinates in mm // (0,0) = lower left corner of page ON_NurbsCurve m_boundary; + + // Update frustum to match bounding box and detail scale + bool UpdateFrustum( + ON::LengthUnitSystem model_units, + ON::LengthUnitSystem paper_units + ); }; diff --git a/opennurbs_dimension.cpp b/opennurbs_dimension.cpp index 0dfd60b4..83b6b1a0 100644 --- a/opennurbs_dimension.cpp +++ b/opennurbs_dimension.cpp @@ -69,7 +69,7 @@ void ON_Dimension::Internal_Destroy() void ON_Dimension::Internal_CopyFrom(const ON_Dimension& src) { - m_text_rotation = src.m_text_rotation; + // m_text_rotation NOT used in 6.0 - m_text_rotation = src.m_text_rotation; m_use_default_text_point = src.m_use_default_text_point; m_user_text_point = src.m_user_text_point; m_user_text = src.m_user_text; @@ -150,12 +150,18 @@ const wchar_t* ON_Dimension::PlainUserText() const // Add to natural rotation double ON_Dimension::TextRotation() const { - return m_text_rotation; + // This V5 function should have been removed from the 6.0 SDK. + // It returned some angle in radians and it doesn't do anything in V6. It was almost always zero. + // Text rotation is handled completely differently in V5 an V6. + return 0.0; } -void ON_Dimension::SetTextRotation(double rotation_radians) +void ON_Dimension::SetTextRotation(double ignored_rotation_radians) { - m_text_rotation = remainder(rotation_radians, (2.0 * ON_PI)); + // This V5 function and m_text_rotation should have been removed from the 6.0 SDK. + // Text rotation is handled completely differently in V5 an V6. + // m_text_rotation = remainder(rotation_radians, (2.0 * ON_PI)); + return; } bool ON_Dimension::GetTextRect(ON_3dPoint text_rect[4]) const @@ -337,6 +343,8 @@ ON_Dimension::ForceText ON_Dimension::ForceTextFromUnsigned( ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Inside); ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Right); ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::Left); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::HintRight); + ON_ENUM_FROM_UNSIGNED_CASE(ON_Dimension::ForceText::HintLeft); } ON_ERROR("Invalid type_as_unsigned parameter."); return (ON_Dimension::ForceText::Auto); @@ -359,7 +367,7 @@ bool ON_Dimension::Internal_WriteDimension( if (!archive.WriteString(m_user_text)) break; - if (!archive.WriteDouble(m_text_rotation)) + if (!archive.WriteDouble(0.0)) // OBSOLETE m_text_rotation break; if (!archive.WriteBool(m_use_default_text_point)) break; @@ -412,7 +420,8 @@ bool ON_Dimension::Internal_ReadDimension( if (!archive.ReadString(m_user_text)) break; - if (!archive.ReadDouble(&m_text_rotation)) + double obsolete_text_rotation = 0.0; + if (!archive.ReadDouble(&obsolete_text_rotation)) break; if (!archive.ReadBool(&m_use_default_text_point)) break; @@ -583,6 +592,18 @@ bool ON_DimLinear::GetTextXform( double dimscale, ON_Xform& text_xform_out ) const +{ + return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out); +} + + +bool ON_DimLinear::GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const { bool rc = false; if (nullptr == dimstyle) @@ -618,22 +639,8 @@ bool ON_DimLinear::GetTextXform( ON_Xform dimplane_to_textpoint(1.0); // Dimension plane to text point translation ON_Xform text_rotation(1.0); // Text rotation around text plane origin point - const ON_Plane& dimplane = Plane(); - ON_3dVector dim_xaxis = dimplane.xaxis; - //ON_3dVector dim_yaxis = dimplane.yaxis; - ON_3dVector dim_zaxis = dimplane.zaxis; - - ON_3dVector view_xdir = ON_3dVector::XAxis; - ON_3dVector view_ydir = ON_3dVector::YAxis; - ON_3dVector view_zdir = ON_3dVector::ZAxis; - if (nullptr != vp) - { - view_xdir = vp->CameraX(); - view_ydir = vp->CameraY(); - view_zdir = vp->CameraZ(); - } - - const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : -cos(80.0*ON_PI / 180.0); + // The amount past vertical where text flips to the other orientation + const double fliptol = (nullptr != vp && vp->Projection() == ON::view_projection::perspective_view) ? 0.0 : cos(80.0*ON_PI / 180.0); ON_3dPoint text_center = ON_3dPoint::Origin; // Text starts out approximately centered at origin @@ -651,9 +658,10 @@ bool ON_DimLinear::GetTextXform( text_height = -2.0 * text_gap; text_xform_out = ON_Xform::IdentityTransformation; - text_to_dimplane.Rotation(ON_Plane::World_xy, dimplane); // Rotate text from starting text plane to dimension plane + text_to_dimplane.Rotation(ON_Plane::World_xy, Plane()); // Rotate text from starting text plane to dimension plane bool draw_forward = dimstyle->DrawForward(); +#pragma region ArrowAndTextFitting // See if arrows and text will all fit inside extension lines // or what has to be moved outside bool arrowflipped[2] = { false, false }; @@ -670,16 +678,16 @@ bool ON_DimLinear::GetTextXform( // V6_Dimstyle Arrow1 & Arrow2 double asz = dimstyle->ArrowSize() * dimscale; - double total_text_width = text_width; - if (force_text != ON_Dimension::ForceText::Auto) + double total_text_width = (ON_DimStyle::ContentAngleStyle::Horizontal == text_angle_style) ? text_height : text_width; + + if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::Right) + { total_text_width = 0.0; + text_outside = true; + } else if (0.0 < total_text_width) total_text_width += text_gap; - if (force_text != ON_Dimension::ForceText::Auto && - force_text != ON_Dimension::ForceText::Inside) - text_outside = true; - static double arrow_width_factor = 1.1; double total_arrow_width = asz * arrow_width_factor * 2; if (ForceArrow::Outside == force_arrow) @@ -715,9 +723,9 @@ bool ON_DimLinear::GetTextXform( { // move textpoint outside right arrow by 1/2 text width + 1-1/2 arrow width double x = text_width * 0.5 + text_gap; - if (force_text == ON_Dimension::ForceText::Left) + if (force_text == ON_Dimension::ForceText::Left || force_text == ON_Dimension::ForceText::HintLeft) { - if (arrowflipped[0]) + if (arrowflipped[0]) x += (asz * arrow_width_factor); text_pt = ArrowPoint1().x < ArrowPoint2().x ? ArrowPoint1() : ArrowPoint2(); text_pt.x -= x; @@ -731,14 +739,37 @@ bool ON_DimLinear::GetTextXform( } } - // text is in dimension plane +#pragma endregion ArrowAndTextFitting + + ON_3dVector dim_xaxis = Plane().xaxis; + ON_3dVector dim_yaxis = Plane().yaxis; + ON_3dVector dim_zaxis = Plane().zaxis; + if (nullptr != model_xform) + { + dim_xaxis.Transform(*model_xform); + dim_yaxis.Transform(*model_xform); + dim_zaxis.Transform(*model_xform); + } + + ON_3dVector view_xdir = ON_3dVector::XAxis; + ON_3dVector view_ydir = ON_3dVector::YAxis; + ON_3dVector view_zdir = ON_3dVector::ZAxis; + if (nullptr != vp) + { + view_xdir = vp->CameraX(); + view_ydir = vp->CameraY(); + view_zdir = vp->CameraZ(); + } + + // text is in dimension plane, not horizontal to the view ON_3dVector text_xdir = dim_xaxis; + ON_2dVector h_dir = HorizontalDirection(); if (ON::TextOrientation::InPlane == text_orientation) { if (ON_DimStyle::ContentAngleStyle::Rotated == text_angle_style) { // Rotation angle = 0 means the text is horizontal - text_angle = TextRotation(); + text_angle = 0.0; //TextRotation(); } else if (ON_DimStyle::ContentAngleStyle::Aligned == text_angle_style) { @@ -746,27 +777,79 @@ bool ON_DimLinear::GetTextXform( } if (ON_DimStyle::ContentAngleStyle::Aligned != text_angle_style) { - ON_2dVector h = HorizontalDirection(); - double h_angle = atan2(h.y, h.x); + double h_angle = atan2(h_dir.y, h_dir.x); text_angle += h_angle; text_xdir.Rotate(h_angle, dim_zaxis); } } - const bool from_the_back = (view_zdir * dim_zaxis < 0.0); - const double upsign = (view_xdir*text_xdir) < fliptol ? -1.0 : 1.0; + double XoX = dim_xaxis * view_xdir; + double XoY = dim_xaxis * view_ydir; + double YoX = dim_yaxis * view_xdir; + double YoY = dim_yaxis * view_ydir; + bool from_the_back = (view_zdir * dim_zaxis < 0.0); + if (nullptr != model_xform && model_xform->Determinant() < 0.0) + from_the_back = !from_the_back; + + double upsign = 1.0; + + // This part shifts text to the correct side of the dimension line + if (fabs(XoX) > fabs(XoY)) // more horizontal + { + if (YoY > 0.0) + upsign = 1.0; + else + upsign = -1.0; + } + else // more vertical + { + if (from_the_back) + { + if (YoX < 0.0) + { + if (XoX < fliptol) + upsign = 1.0; + else + upsign = -1.0; + } + else + { + if (XoX > -fliptol) + upsign = -1.0; + else + upsign = 1.0; + } + } + else + { + if (YoX > 0.0) + { + if (XoX > fliptol) + upsign = 1.0; + else + upsign = -1.0; + } + else + { + if (XoX < -fliptol) + upsign = -1.0; + else + upsign = 1.0; + } + } + } if (ON_DimStyle::TextLocation::AboveDimLine == text_location) { // Moves the text to AboveLine if that's the alignment mode double d = (text_height * 0.5 + text_gap) * upsign; - if (from_the_back) - d = -d; + //if (from_the_back) + // d = -d; text_pt.y += d; } - ON_3dPoint text_point_3d = dimplane.PointAt(text_pt.x, text_pt.y); // 3d text point - dimplane_to_textpoint = ON_Xform::TranslationTransformation(text_point_3d - dimplane.origin); // Move from dimplane origin to text point + ON_3dPoint text_point_3d = Plane().PointAt(text_pt.x, text_pt.y); // 3d text point + dimplane_to_textpoint = ON_Xform::TranslationTransformation(text_point_3d - Plane().origin); // Move from dimplane origin to text point text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale); // dimscale @@ -783,23 +866,25 @@ bool ON_DimLinear::GetTextXform( if (ON::TextOrientation::InView == text_orientation) // Draw dimension horizontal to view { - view_xdir = ON_3dVector::XAxis; - view_ydir = ON_3dVector::YAxis; - view_zdir = ON_3dVector::ZAxis; - if (nullptr != vp) + if (nullptr != model_xform) { - view_xdir = vp->CameraX(); - view_ydir = vp->CameraY(); - view_zdir = vp->CameraZ(); + ON_Xform xf(*model_xform); + xf.Invert(); + view_xdir.Transform(xf); + view_ydir.Transform(xf); + view_zdir.Transform(xf); } + ON_Xform tp2sxf; // Text point to view plane rotation - tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_xdir, view_ydir, view_zdir); + tp2sxf.Rotation(text_point_3d, Plane().xaxis, Plane().yaxis, Plane().zaxis, text_point_3d, view_xdir, view_ydir, view_zdir); text_xform_out = tp2sxf * text_xform_out; } else if (draw_forward) { bool fx = false; bool fy = false; + if (from_the_back) + upsign = -upsign; fx = upsign < 0.0; if (from_the_back) fy = !fx; @@ -877,7 +962,7 @@ bool ON_DimLinear::GetAnnotationBoundingBox( return false; ON_Xform text_xform; - GetTextXform(vp, dimstyle, dimscale, text_xform); + GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform); ON_BoundingBox dim_box; @@ -892,12 +977,11 @@ bool ON_DimLinear::GetAnnotationBoundingBox( text_rect[3].Set(dim_box.m_min.x, dim_box.m_max.y, 0.0); for (int i = 0; i < 4; i++) text_rect[i].Transform(text_xform); // Text + gap bounding rect - } - dim_box.Destroy(); - for (int i = 0; i < 4; i++) - { - dim_box.Set(text_rect[i], 0 < i ? true : false); + for (int i = 0; i < 4; i++) + { + dim_box.Set(text_rect[i], 0 < i ? true : false); + } } // Get non-text display geometry for the dimension @@ -1407,6 +1491,15 @@ static int ClipLineToTextRect( if (!v0.Unitize()) return 0; + bool bTextRectBackwards = false; + { + ON_3dVector a = text_rect[2] - text_rect[0]; + ON_3dVector b = text_rect[3] - text_rect[1]; + ON_3dVector c = ON_CrossProduct(a, b); + if ((c * cam_dir) < 0.0) + bTextRectBackwards = true; + } + for (int i = 0; i < 4; i++) { v1 = text_rect[i] - cam_loc; @@ -1414,6 +1507,8 @@ static int ClipLineToTextRect( return 0; // Makes normals facing out of frustum ON_3dVector z = ON_CrossProduct(v0, v1); + if (bTextRectBackwards) + z = -z; if (!frust_plane_eq[i].Create(cam_loc, z)) return 0; v0 = v1; @@ -1525,7 +1620,7 @@ static int ClipArcToTextRect( cam_plane_eq.Create(cam_loc, cam_dir); ON_Plane frust_plane[4]; - ON_3dVector v0; + ON_3dVector v0, v1; v0 = text_rect[3] - cam_loc; if (!v0.Unitize()) return 0; @@ -1536,6 +1631,30 @@ static int ClipArcToTextRect( return 0; } + bool bTextRectBackwards = false; + { + ON_3dVector a = text_rect[2] - text_rect[0]; + ON_3dVector b = text_rect[3] - text_rect[1]; + ON_3dVector c = ON_CrossProduct(a, b); + if ((c * cam_dir) < 0.0) + bTextRectBackwards = true; + } + + for (int i = 0; i < 4; i++) + { + v1 = text_rect[i] - cam_loc; + if (!v1.Unitize()) + return 0; + // Makes normals facing out of frustum + ON_3dVector z = ON_CrossProduct(v0, v1); + z.Unitize(); + if (bTextRectBackwards) + z = -z; + if(!frust_plane[i].CreateFromNormal(cam_loc, z)) + return 0; + v0 = v1; + } + double s[4]; int intcount = 0; for (int i = 0; i < 4; i++) @@ -1726,19 +1845,27 @@ bool ON_DimLinear::GetDisplayLines( if (UseDefaultTextPoint() && ON_DimStyle::TextLocation::InDimLine != text_location) { + // If the dimline is under the text, and the text extends past the end of the dimline, + // make the dim line as long as the text if the text is offset sideways from the + // extension lines. + // If the text overlaps the extensions in both directions, it is centered and the + // dimension line will hang out just a little each way, so don't do it in that case double t0, t1; lines[2].ClosestPointTo(text_rect[0], &t0); lines[2].ClosestPointTo(text_rect[1], &t1); - if (t0 > t1) + if (fabs(t0 - t1) > 0.00001) // if text rect has some width { - double t = t0; t0 = t1; t1 = t; + if (t0 > t1) + { + double t = t0; t0 = t1; t1 = t; + } + ON_Line l = lines[2]; + if (t0 < 0.0 && t1 < 1.0) + l.from = lines[2].PointAt(t0); + if (t1 > 1.0 && t0 > 0.0) + l.to = lines[2].PointAt(t1); + lines[2] = l; } - ON_Line l = lines[2]; - if (t0 < 0.0) - l.from = lines[2].PointAt(t0); - if (t1 > 1.0) - l.to = lines[2].PointAt(t1); - lines[2] = l; } if ( @@ -2579,7 +2706,7 @@ bool ON_DimAngular::GetAnnotationBoundingBox( view_ydir = vp->CameraY(); } ON_Xform text_xform; - GetTextXform(vp, dimstyle, dimscale, text_xform); + GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform); ON_BoundingBox dim_box; @@ -2640,6 +2767,17 @@ bool ON_DimAngular::GetTextXform( double dimscale, ON_Xform& text_xform_out ) const +{ + return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out); +} + +bool ON_DimAngular::GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const { if (nullptr == dimstyle) return false; @@ -2792,7 +2930,7 @@ bool ON_DimAngular::GetTextXform( if (ON_DimStyle::ContentAngleStyle::Rotated == text_angle_style) { // Rotation angle = 0 means the text is horizontal - text_angle = TextRotation(); + text_angle = 0.0; //TextRotation(); Was some gooffy 0 to 1 property from V5. Not degrees or radians } else if (ON_DimStyle::ContentAngleStyle::Horizontal == text_angle_style) { @@ -2861,6 +2999,13 @@ bool ON_DimAngular::GetTextXform( view_ydir = vp->CameraY(); view_zdir = vp->CameraZ(); } + ON_3dVector dim_xdir = Plane().xaxis; + ON_3dVector dim_ydir = Plane().yaxis; + if (nullptr != model_xform) + { + dim_xdir.Transform(*model_xform); + dim_ydir.Transform(*model_xform); + } if (ON::TextOrientation::InView == text_orientation) // Draw dimension horizontal to view { @@ -2873,9 +3018,12 @@ bool ON_DimAngular::GetTextXform( { ON_3dVector text_right_dir_local(1.0, 0.0, 0.0); text_right_dir_local.Transform(text_xform_out); + if (nullptr != model_xform) + text_right_dir_local.Transform(*model_xform); if (text_right_dir_local.Unitize()) { - ON_3dVector text_up_dir_local = ON_CrossProduct(dimplane.zaxis, text_right_dir_local); + ON_3dVector zdir = ON_CrossProduct(dim_xdir, dim_ydir); + ON_3dVector text_up_dir_local = ON_CrossProduct(zdir, text_right_dir_local); bool fx = (0.0 > view_xdir * text_right_dir_local); bool fy = (0.0 > view_ydir * text_up_dir_local); @@ -3733,7 +3881,7 @@ bool ON_DimRadial::GetAnnotationBoundingBox( return false; ON_Xform text_xform; - GetTextXform(vp, dimstyle, dimscale, text_xform); + GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform); ON_BoundingBox dim_box; @@ -3781,6 +3929,17 @@ bool ON_DimRadial::GetTextXform( double dimscale, ON_Xform& text_xform_out ) const +{ + return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out); +} + +bool ON_DimRadial::GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const { const ON_TextContent* text = Text(); if (nullptr == text) @@ -3800,7 +3959,6 @@ bool ON_DimRadial::GetTextXform( const_cast(text)->Create(rtfstr, annotation_type, dimstyle, wrapped, width, rot); } - const ON_Plane& dimplane = Plane(); ON_3dPoint text_center = ON_3dPoint::Origin; ON_3dPoint cp[4]; @@ -3813,7 +3971,8 @@ bool ON_DimRadial::GetTextXform( const ON::TextOrientation text_orientation = dimstyle->DimRadialTextOrientation(); const ON_DimStyle::ContentAngleStyle text_alignment = dimstyle->DimRadialTextAngleStyle(); - const ON_DimStyle::TextLocation text_location = + // Always move text to InLine if it's going to be horizontal and in the view plane + const ON_DimStyle::TextLocation text_location = (ON::TextOrientation::InView == text_orientation) ? ON_DimStyle::TextLocation::InDimLine : dimstyle->DimRadialTextLocation(); @@ -3822,7 +3981,9 @@ bool ON_DimRadial::GetTextXform( bool draw_forward = dimstyle->DrawForward(); ON_Xform dimplane_xf(1.0); - dimplane_xf.Rotation(textplane, dimplane); // Rotate text from world xy to dimension plane + + dimplane_xf.Rotation(textplane, Plane()); // Rotate text from world xy to dimension plane + ON_Xform textpt_xf(1.0); // Dimension plane to text point translation ON_Xform textrot_xf(1.0); // Text rotation around text plane origin point ON_Xform textscale_xf(1.0); @@ -3836,7 +3997,9 @@ bool ON_DimRadial::GetTextXform( ON_2dPoint radius_pt = RadiusPoint(); ON_2dPoint center_pt(0.0, 0.0); ON_2dPoint kink_pt(ON_2dPoint::UnsetPoint); - ON_2dVector rv = radius_pt; + ON_2dVector radius_vector = radius_pt; + if (!radius_vector.Unitize()) + return false; if (fabs(dimline_pt.x) < ON_SQRT_EPSILON) dimline_pt.x = 0.0; if (fabs(dimline_pt.y) < ON_SQRT_EPSILON) @@ -3845,12 +4008,29 @@ bool ON_DimRadial::GetTextXform( radius_pt.x = 0.0; if (fabs(radius_pt.y) < ON_SQRT_EPSILON) radius_pt.y = 0.0; - if (!rv.Unitize()) - return false; ON_2dVector tail_dir(1.0, 0.0); kink_pt = KneePoint(); + ON_3dVector dim_xdir = Plane().xaxis; + ON_3dVector dim_ydir = Plane().yaxis; + if (nullptr != model_xform) + { + dim_xdir.Transform(*model_xform); + dim_ydir.Transform(*model_xform); + } + + ON_3dVector view_x = ON_3dVector::XAxis; + ON_3dVector view_y = ON_3dVector::YAxis; + ON_3dVector view_z = ON_3dVector::ZAxis; + if (nullptr != vp) + { + view_x = vp->CameraX(); + view_y = vp->CameraY(); + view_z = vp->CameraZ(); + } + + // Text is horizontal in CPlane, not view if (ON_DimStyle::ContentAngleStyle::Horizontal == text_alignment && ON_2dPoint::UnsetPoint != kink_pt) { @@ -3870,13 +4050,13 @@ bool ON_DimRadial::GetTextXform( tail_dir.Set(-1.0, 0.0); } } - + // Text is aligned with last leader segment else if (ON_DimStyle::ContentAngleStyle::Aligned == text_alignment) // && no kink point { double d = ((ON_2dVector)dimline_pt).Length(); if (((ON_2dVector)dimline_pt) * ((ON_2dVector)radius_pt) < 0.0) d = -d; // text point is on the other side of center from arrow point - dimline_pt = rv * d; // With no kink, adjust dimline point to line up with radius point + dimline_pt = radius_vector * d; // With no kink, adjust dimline point to line up with radius point tail_dir = dimline_pt - radius_pt; if (ON_SQRT_EPSILON > tail_dir.Length() || !tail_dir.Unitize()) tail_dir = radius_pt - center_pt; @@ -3888,7 +4068,6 @@ bool ON_DimRadial::GetTextXform( return false; // Text position adjustment - ON_2dVector shift(0.0, 0.0); if(ON_DimStyle::TextLocation::AboveDimLine == text_location) shift.y = text_height / 2.0 + text_gap; @@ -3899,6 +4078,9 @@ bool ON_DimRadial::GetTextXform( if (-ON_SQRT_EPSILON > tail_dir.x) // text to left shift.y = -shift.y; + if (dim_ydir * view_y < 0.0) + shift.y = -shift.y; + shift.Rotate(tail_dir.y, tail_dir.x); textpt_xf = ON_Xform::TranslationTransformation( ON_3dVector(dimline_pt + shift) ); @@ -3909,27 +4091,19 @@ bool ON_DimRadial::GetTextXform( text_xform_out = ON_Xform::DiagonalTransformation(dimscale, dimscale, dimscale); + // Text is horizontal to view if (ON::TextOrientation::InView != text_orientation) text_xform_out = textrot_xf * text_xform_out; text_xform_out = textpt_xf * text_xform_out; text_xform_out = dimplane_xf * text_xform_out; - ON_3dPoint text_point_3d = dimplane.PointAt(dimline_pt.x + shift.x, dimline_pt.y + shift.y); - - ON_3dVector view_x = ON_3dVector::XAxis; - ON_3dVector view_y = ON_3dVector::YAxis; - ON_3dVector view_z = ON_3dVector::ZAxis; - if (nullptr != vp) - { - view_x = vp->CameraX(); - view_y = vp->CameraY(); - view_z = vp->CameraZ(); - } + ON_3dPoint text_point_3d = Plane().PointAt(dimline_pt.x + shift.x, dimline_pt.y + shift.y); + // Text is horizontal to view if (ON::TextOrientation::InView == text_orientation) { ON_Xform tp2sxf; // Text point to view plane rotation - tp2sxf.Rotation(text_point_3d, dimplane.xaxis, dimplane.yaxis, dimplane.zaxis, text_point_3d, view_x, view_y, view_z); + tp2sxf.Rotation(text_point_3d, Plane().xaxis, Plane().yaxis, Plane().zaxis, text_point_3d, view_x, view_y, view_z); text_xform_out = tp2sxf * text_xform_out; } else @@ -3938,9 +4112,12 @@ bool ON_DimRadial::GetTextXform( // Check if the text is right-reading ON_3dVector text_right_dir(1.0, 0.0, 0.0); text_right_dir.Transform(text_xform_out); + if(nullptr != model_xform) + text_right_dir.Transform(*model_xform); if (text_right_dir.Unitize()) { - ON_3dVector text_up_dir = ON_CrossProduct(dimplane.zaxis, text_right_dir); + ON_3dVector zdir = ON_CrossProduct(dim_xdir, dim_ydir); + ON_3dVector text_up_dir = ON_CrossProduct(zdir, text_right_dir); bool fx = (0.0 > view_x * text_right_dir); bool fy = (0.0 > view_y * text_up_dir); if (fx || fy) @@ -3953,7 +4130,7 @@ bool ON_DimRadial::GetTextXform( } if (fy) { - mxf.Mirror(text_center, textplane.yaxis); + mxf.Mirror(ON_3dPoint::Origin, textplane.yaxis); text_xform_out = text_xform_out * mxf; } } @@ -4203,7 +4380,7 @@ bool ON_DimRadial::GetDisplayLines( ) { if (!dimstyle->LeaderHasLanding() && fabs(dimline_point.x - kink_point.x) < ON_ZERO_TOLERANCE) - landinglength = dimstyle->TextHeight() * dimstyle->DimScale(); + landinglength = dimstyle->TextHeight() * dimscale; } if(ON_DimStyle::TextLocation::AboveDimLine == text_location && ON::TextOrientation::InView != dimstyle->DimRadialTextOrientation()) @@ -4511,7 +4688,7 @@ bool ON_DimOrdinate::GetAnnotationBoundingBox( view_ydir = vp->CameraY(); } ON_Xform textxform; - GetTextXform(vp, dimstyle, dimscale, textxform); + GetTextXform(nullptr, vp, dimstyle, dimscale, textxform); dbox.Transform(textxform); } @@ -4530,6 +4707,17 @@ bool ON_DimOrdinate::GetTextXform( double dimscale, ON_Xform& text_xform_out ) const +{ + return GetTextXform(nullptr, vp, dimstyle, dimscale, text_xform_out); +} + +bool ON_DimOrdinate::GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out +) const { // This gets the display text that's already on the dimension @@ -4592,7 +4780,7 @@ bool ON_DimOrdinate::GetTextXform( if (MeasuredDirection::Xaxis == direction) // Tail direction is vertical { tail_dir.Set(0.0, 1.0, 0.0); - if (ldrpt.y < defpt.y) // tail directioin is down + if (ldrpt.y < defpt.y) // tail direction is down { tail_dir.y = -1.0; shift.x = -shift.x; @@ -4619,6 +4807,12 @@ bool ON_DimOrdinate::GetTextXform( W2CX.Transform(xfWorld2Cam); W2CY.Transform(xfWorld2Cam); W2CZ.Transform(xfWorld2Cam); + if (nullptr != model_xform) + { + W2CX.Transform(*model_xform); + W2CY.Transform(*model_xform); + W2CZ.Transform(*model_xform); + } } bool xright = (W2CX * ON_3dVector::XAxis) > -ON_SQRT_EPSILON; bool yup = (W2CY * ON_3dVector::YAxis) > -ON_SQRT_EPSILON; @@ -4666,11 +4860,26 @@ bool ON_DimOrdinate::GetTextXform( { ON_3dVector text_right_dir(1.0, 0.0, 0.0); text_right_dir.Transform(text_xform_out); - if (text_right_dir.Unitize()) + if (nullptr != model_xform) + text_right_dir.Transform(*model_xform); + ON_3dVector text_up_dir(0.0, 1.0, 0.0); + text_up_dir.Transform(text_xform_out); + if (nullptr != model_xform) + text_up_dir.Transform(*model_xform); + if (text_right_dir.Unitize() && text_up_dir.Unitize()) { - ON_3dVector text_up_dir = ON_CrossProduct(dimplane.zaxis, text_right_dir); - bool fx = (0.0 > view_xdir * text_right_dir); - bool fy = (0.0 > view_ydir * text_up_dir); + bool fx = false; + bool fy = false; + if (direction == MeasuredDirection::Xaxis) + { + fx = (view_ydir * text_right_dir) < 0.0; + fy = (view_xdir * text_up_dir) > 0.0; + } + else + { + fx = (view_xdir * text_right_dir) < 0.0; + fy = (view_ydir * text_up_dir) < 0.0; + } ON_Xform mxf; // Mirror xform for backwards text to adjust DrawForward if (fx) @@ -4720,7 +4929,7 @@ bool ON_DimOrdinate::Create( Set2dDefPt(def_pton); Set2dLeaderPt(ldr_pton); SetKinkOffset1(kinkoffset1); - SetKinkOffset1(kinkoffset2); + SetKinkOffset2(kinkoffset2); } } return rc; @@ -4956,7 +5165,7 @@ bool ON_DimOrdinate::Get3dPoints( *kink2 = m_plane.PointAt(m_ldr_pt.x - ko1, m_ldr_pt.y); } } - return true; + return true; } bool ON_DimOrdinate::GetDisplayLines( @@ -4972,9 +5181,17 @@ bool ON_DimOrdinate::GetDisplayLines( ON_ERROR("Wrong linecount calling ON_DimOrdinate::GetDisplayLines.\n"); return false; } - ON_3dPoint defpt, ldrpt, kink1, kink2; + ON_3dPoint defpt, ldrpt, kink1, kink2, frompt; Get3dPoints(nullptr, &defpt, &ldrpt, &kink1, &kink2); - lines[0].from = defpt; + frompt = defpt; + double eodist = dimstyle->ExtOffset() * dimscale; + ON_3dVector eodir = kink1 - defpt; + if (eodir.Unitize()) + { + frompt = defpt + (eodir * eodist); + } + + lines[0].from = frompt; lines[0].to = kink1; if (ON_SQRT_EPSILON < lines[0].Length()) isline[0] = true; @@ -5124,7 +5341,7 @@ double ON_DimOrdinate::Measurement() const if (DistanceScale() != 1.0) m *= DistanceScale(); - return m; + return fabs(m); } diff --git a/opennurbs_dimension.h b/opennurbs_dimension.h index bbe4faaf..0d528c60 100644 --- a/opennurbs_dimension.h +++ b/opennurbs_dimension.h @@ -58,6 +58,10 @@ public: Right = 2, /// Left = 3, + /// If override isn't specified and text doesn't fit, move it right + HintRight = 4, + /// If override isn't specified and text doesn't fit, move it left + HintLeft = 5, }; #pragma endregion @@ -95,7 +99,10 @@ public: virtual double Measurement() const = 0; // Add to natural rotation + ON_DEPRECATED_MSG("ON_Dimension::TextRotation() is a mistake. Use ON_Annotation::TextRotationRadians().") double TextRotation() const; + + ON_DEPRECATED_MSG("ON_Dimension::SetTextRotation() is a mistake. Use ON_Annotation::SetTextRotationRadians().") void SetTextRotation(double rotation_radians); bool ArrowIsFlipped(int i) const; @@ -172,7 +179,7 @@ public: protected: ON_wString m_user_text = L"<>"; // If user overridden, or "<>" to use default - double m_text_rotation = 0.0; + double m_reserved = 0.0; mutable ON_wString m_plain_user_text; bool m_use_default_text_point = true; @@ -277,6 +284,14 @@ public: ) const override; // ON_Annotation override // Gets transform for dimension text from ON_xy_plane to 3d display location + bool GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const; + bool GetTextXform( const ON_Viewport* vp, const ON_DimStyle* dimstyle, @@ -471,7 +486,15 @@ public: bool bGrow = false ) const override; // ON_Annotation override - // Gets transform for dimension text from ON_xy_plane to 3d display location +// Gets transform for dimension text from ON_xy_plane to 3d display location + bool GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const; + bool GetTextXform( const ON_Viewport* vp, const ON_DimStyle* dimstyle, @@ -757,6 +780,14 @@ public: ) const override; // ON_Annotation override // Gets transform for dimension text from ON_xy_plane to 3d display location + bool GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const; + bool GetTextXform( const ON_Viewport* vp, const ON_DimStyle* dimstyle, @@ -905,6 +936,14 @@ public: ON_Xform& text_xform_out ) const override; + bool GetTextXform( + const ON_Xform* model_xform, + const ON_Viewport* vp, + const ON_DimStyle* dimstyle, + double dimscale, + ON_Xform& text_xform_out + ) const; + bool Create( const ON_UUID style_id, const ON_Plane& plane, diff --git a/opennurbs_dimensionformat.cpp b/opennurbs_dimensionformat.cpp index a84daf5c..a3cf1bbc 100644 --- a/opennurbs_dimensionformat.cpp +++ b/opennurbs_dimensionformat.cpp @@ -235,7 +235,7 @@ bool ON_NumberFormatter::FormatNumber( if (0 != numerator) { if(bracket_fractions) - sFormat2.Format(L"[[%d/%d]]", numerator, denominator); + sFormat2.Format(L"[[/%d/%d]]", numerator, denominator); else sFormat2.Format(L"%d/%d", numerator, denominator); sFormat += sFormat2; @@ -313,14 +313,14 @@ bool ON_NumberFormatter::FormatNumber( if (wholeinches > 0 || include_feet) { if (bracket_fractions) - sInches.Format(L"%d [[%d/%d]]\"", wholeinches, numerator, denominator); + sInches.Format(L"%d [[/%d/%d]]\"", wholeinches, numerator, denominator); else sInches.Format(L"%d %d/%d\"", wholeinches, numerator, denominator); } else { if (bracket_fractions) - sInches.Format(L"[[%d/%d]]\"", numerator, denominator); + sInches.Format(L"[[/%d/%d]]\"", numerator, denominator); else sInches.Format(L"%d/%d\"", numerator, denominator); } diff --git a/opennurbs_dimensionstyle.cpp b/opennurbs_dimensionstyle.cpp index 3bbe84e7..c725cb1c 100644 --- a/opennurbs_dimensionstyle.cpp +++ b/opennurbs_dimensionstyle.cpp @@ -2142,7 +2142,7 @@ bool ON_DimStyle::Write( ON_BinaryArchive& file // serialize definition to binary archive ) const { - if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 6)) + if (!file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, 7)) return false; bool rc = false; @@ -2213,10 +2213,20 @@ bool ON_DimStyle::Write( if (!file.WriteInt(u)) break; if (!file.WriteInt(m_alternate_lengthresolution)) break; + + // The ON_wString::IsValid calls prevent corrupt strings from breaking file IO + m_prefix.IsValid(true); if (!file.WriteString(m_prefix)) break; + + m_suffix.IsValid(true); if (!file.WriteString(m_suffix)) break; + + m_alternate_prefix.IsValid(true); if (!file.WriteString(m_alternate_prefix)) break; + + m_alternate_suffix.IsValid(true); if (!file.WriteString(m_alternate_suffix)) break; + if (!file.WriteDouble(m_dimextension)) break; if (!file.WriteBool(m_bSuppressExtension1)) break; if (!file.WriteBool(m_bSuppressExtension2)) break; @@ -2457,6 +2467,10 @@ bool ON_DimStyle::Write( if (!file.WriteInt(u)) break; // END chunk version 1.6 information + u = static_cast(m_centermark_style); + if (!file.WriteInt(u)) break; + // END chunk version 1.7 information + rc = true; break; } @@ -2530,12 +2544,15 @@ bool ON_DimStyle::Read( case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kNormal: m_dim_text_location = ON_DimStyle::TextLocation::AboveDimLine; m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + break; case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kAboveLine: m_dim_text_location = ON_DimStyle::TextLocation::AboveDimLine; m_dimradial_text_location = ON_DimStyle::TextLocation::AboveDimLine; + break; case ON_INTERNAL_OBSOLETE::V5_TextDisplayMode::kInLine: m_dim_text_location = ON_DimStyle::TextLocation::InDimLine; m_dimradial_text_location = ON_DimStyle::TextLocation::InDimLine; + break; default: // DO NO HARM ... use ON_DimStyle constructor defaults. break; @@ -3021,6 +3038,16 @@ bool ON_DimStyle::Read( if (!file.ReadInt(&u)) break; m_alternate_dimension_length_display = ON_DimStyle::LengthDisplayFromUnsigned(u); // END chunk version 1.6 information + if (minor_version <= 6) + { + rc = true; + break; + } + + u = static_cast(m_centermark_style); + if (!file.ReadInt(&u)) break; + m_centermark_style = ON_DimStyle::CentermarkStyleFromUnsigned(u); + // END chunk version 1.7 information rc = true; break; @@ -3200,6 +3227,11 @@ bool ON_DimStyle::Internal_SetStringMember( ON_wString& class_member ) { + if (false == class_member.IsValid(true)) + { + // just in case value pointed to the bogus string array. + value = L""; + } bool bValueChanged = false; if (false == class_member.EqualOrdinal(value,false) ) { @@ -3356,7 +3388,7 @@ const bool ON_DimStyle::FontSubstituted() const return (nullptr == m_managed_font) || (0 != ON_Font::CompareFontCharacteristics(*m_managed_font, m_font_characteristics)) - || (m_managed_font->FontDescription() != m_font_characteristics.FontDescription()) + || (m_managed_font->Description() != m_font_characteristics.Description()) ; } @@ -3381,7 +3413,7 @@ void ON_DimStyle::SetFont( const bool bFontChanged = bManagedFontChanged || (0 != ON_Font::CompareFontCharacteristics(font_characteristics, m_font_characteristics)) - || m_font_characteristics.FontDescription() != font_characteristics.FontDescription() + || m_font_characteristics.Description() != font_characteristics.Description() ; // copy font_characteristics unconditionally in case compare missed some detail. @@ -3426,7 +3458,7 @@ ON_DimStyle* ON_DimStyle::CreateFromFont( if ( model_view_text_scale > 0.0 && ON_IsValid(model_view_text_scale)) destination->SetDimScale(model_view_text_scale); - const ON_wString font_description = font_characteristics->FontDescription(); + const ON_wString font_description = font_characteristics->Description(); if (font_description.IsNotEmpty()) { const ON_wString name @@ -3472,10 +3504,19 @@ const class ON_SHA1_Hash ON_DimStyle::TextPositionPropertiesHash() const sha1.AccumulateBool(m_bAlternate); sha1.AccumulateDouble(m_alternate_lengthfactor); sha1.AccumulateInteger32(m_alternate_lengthresolution); + + m_prefix.IsValid(true); sha1.AccumulateString(m_prefix); + + m_suffix.IsValid(true); sha1.AccumulateString(m_suffix); + + m_alternate_prefix.IsValid(true); sha1.AccumulateString(m_alternate_prefix); + + m_alternate_suffix.IsValid(true); sha1.AccumulateString(m_alternate_suffix); + sha1.AccumulateDouble(m_dimextension); sha1.AccumulateUnsigned32(static_cast(m_tolerance_format)); sha1.AccumulateInteger32(m_tolerance_resolution); @@ -3667,6 +3708,7 @@ void ON_DimStyle::SetAlternateLengthResolution(int resolution) const ON_wString& ON_DimStyle::Prefix() const { + m_prefix.IsValid(true); return m_prefix; } @@ -3677,6 +3719,7 @@ void ON_DimStyle::SetPrefix(const wchar_t* prefix) const ON_wString& ON_DimStyle::Suffix() const { + m_suffix.IsValid(true); return m_suffix; } @@ -3687,6 +3730,7 @@ void ON_DimStyle::SetSuffix(const wchar_t* suffix) const ON_wString& ON_DimStyle::AlternatePrefix() const { + m_alternate_prefix.IsValid(true); return m_alternate_prefix; } @@ -3697,6 +3741,7 @@ void ON_DimStyle::SetAlternatePrefix(const wchar_t* prefix) const ON_wString& ON_DimStyle::AlternateSuffix() const { + m_alternate_suffix.IsValid(true); return m_alternate_suffix; } @@ -5443,7 +5488,10 @@ double ON_DimStyle::ToleranceLowerValue() const double ON_DimStyle::ToleranceHeightScale() const { - return m_tolerance_height_scale; + // Intentionally using StackHeightScale for both fractions and tolerances + // Tolerances aren't allowed with fractional formats + return StackHeightScale(); +// return m_tolerance_height_scale; } double ON_DimStyle::BaselineSpacing() const @@ -5503,8 +5551,11 @@ void ON_DimStyle::SetToleranceLowerValue(double lower_value) void ON_DimStyle::SetToleranceHeightScale(double scale) { - if (ON_IsValid(scale) && scale > ON_SQRT_EPSILON) - Internal_SetDoubleMember(ON_DimStyle::field::ToleranceHeightScale, scale, m_tolerance_height_scale); + // Intentionally using StackHeightScale for both fractions and tolerances + // Tolerances aren't allowed with fractional formats + SetStackHeightScale(scale); + //if (ON_IsValid(scale) && scale > ON_SQRT_EPSILON) + // Internal_SetDoubleMember(ON_DimStyle::field::ToleranceHeightScale, scale, m_tolerance_height_scale); } void ON_DimStyle::SetBaselineSpacing(double spacing) diff --git a/opennurbs_error.cpp b/opennurbs_error.cpp index 3f5b441d..4fdafdf6 100644 --- a/opennurbs_error.cpp +++ b/opennurbs_error.cpp @@ -40,14 +40,13 @@ // ON_Warning() // -#define ON_MAX_ERROR_MESSAGE_COUNT 50 +#define ON_MAX_ERROR_MESSAGE_COUNT ((int)ON_ErrorLog::MaximumEventCount) static int ON_ERROR_COUNT = 0; static int ON_WARNING_COUNT = 0; static int ON_MATH_ERROR_COUNT = 0; +class ON_ErrorLog* ON_ACTIVE_ERROR_LOG = nullptr; -// 0 = no break -// 1 = break on errors, warnings, and asserts static int ON_DEBUG_ERROR_MESSAGE_OPTION = 0; @@ -87,7 +86,7 @@ void ON_MathError( const char* sFunctionName ) { - ON_MATH_ERROR_COUNT++; // <- Good location for a debugger breakpoint. + ON_MATH_ERROR_COUNT++; if ( 0 == sModuleName) sModuleName = ""; @@ -130,226 +129,573 @@ bool ON_IsNotValid() return false; } -static void ON_PrintErrorMessage( - int type, // 0 = warning, 1 = error, 2 = assert - const char* sFileName, - int line_number, - const char* sFunctionName, - const char* sFormat, - va_list args - ) +static bool ON_Internal_PrintErrorMessage() { + return (0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_ERROR_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT); +} + +static bool ON_Internal_PrintWarningMessage() +{ + return (0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_WARNING_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT); +} + + +static bool ON_Internal_LogError() +{ + return (nullptr != ON_ACTIVE_ERROR_LOG); +} + +static void ON_Internal_RecordErrorEvent( + const ON_ErrorEvent& error_event, + bool bPrint + ) +{ + if (nullptr != ON_ACTIVE_ERROR_LOG) + ON_ACTIVE_ERROR_LOG->Append(error_event); + + if ( 0 == ON_DEBUG_ERROR_MESSAGE_OPTION ) return; - if ( 0 == type ) - { - // too many warnings - warning messages are suppressed - if ( ON_WARNING_COUNT > ON_MAX_ERROR_MESSAGE_COUNT ) - return; - } - else if ( 1 == type || 2 == type ) - { - // too many errors, asserts, etc. - error messages are suppressed - if ( ON_ERROR_COUNT > ON_MAX_ERROR_MESSAGE_COUNT ) - return; - } - else - { + if (false == bPrint) return; - } - char buffer[1024]; - const size_t buffer_capacity = sizeof(buffer)/sizeof(buffer[0]); - buffer[0] = 0; - buffer[buffer_capacity-1] = 0; - - if ( 0 == type ) + const ON_ErrorEvent::Type error_type = error_event.EventType(); + ON_String s; + if ( ON_ErrorEvent::Type::Warning == error_type ) { - if ( ON_WARNING_COUNT < ON_MAX_ERROR_MESSAGE_COUNT ) - { - if (0 == sFileName ) - sFileName = ""; - if ( sFunctionName && sFunctionName[0] ) - ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS WARNING # %d %s.%d %s()",ON_WARNING_COUNT,sFileName,line_number,sFunctionName); - else - ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS WARNING # %d %s.%d",ON_WARNING_COUNT,sFileName,line_number); - } - else if ( ON_WARNING_COUNT == ON_MAX_ERROR_MESSAGE_COUNT ) - { - ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS WARNING # %d ... Suspending warning messages." ,ON_WARNING_COUNT); - sFormat = 0; - } + if (ON_WARNING_COUNT == ON_MAX_ERROR_MESSAGE_COUNT) + s = ON_String::FormatToString("ON_WARNING # %d: ... suspending warning messages.", ON_WARNING_COUNT); + else + s = ON_String::FormatToString("ON_WARNING # %d: ", ON_WARNING_COUNT) + error_event.ToString(); } else { - if ( ON_ERROR_COUNT < ON_MAX_ERROR_MESSAGE_COUNT ) - { - if (0 == sFileName ) - sFileName = ""; - if ( sFunctionName && sFunctionName[0] ) - ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS ERROR # %d %s.%d %s()",ON_ERROR_COUNT,sFileName,line_number,sFunctionName); - else - ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS ERROR # %d %s.%d",ON_ERROR_COUNT,sFileName,line_number); - } - else if ( ON_ERROR_COUNT == ON_MAX_ERROR_MESSAGE_COUNT ) - { - ON_String::FormatIntoBuffer( buffer, buffer_capacity, "openNURBS ERROR # %d ... Suspending error messages.", ON_ERROR_COUNT ); - sFormat = 0; - } + if (ON_ERROR_COUNT == ON_MAX_ERROR_MESSAGE_COUNT) + s = ON_String::FormatToString("ON_ERROR # %d: ... suspending error messages.", ON_ERROR_COUNT); + else + s = ON_String::FormatToString("ON_ERROR # %d: ", ON_ERROR_COUNT) + error_event.ToString(); } - if ( (0 != buffer[0] && 0 == buffer[buffer_capacity-1]) ) + if ( s.IsNotEmpty() ) { - if ( 0 != sFormat && 0 != sFormat[0] ) + int obsolete_type; + switch (error_type) { - for ( size_t i = 0; i < buffer_capacity; i++ ) - { - if ( 0 == buffer[i]) - { - if ( i + 32 < buffer_capacity ) - { - buffer[i++] = ':'; - buffer[i++] = 32; // space - buffer[i] = 0; - ON_String::FormatVargsIntoBuffer(buffer + i, buffer_capacity-i, sFormat, args ); - } - break; - } - } + case ON_ErrorEvent::Type::Unset: + obsolete_type = 0; + break; + case ON_ErrorEvent::Type::Warning: + obsolete_type = 0; + break; + case ON_ErrorEvent::Type::Error: + obsolete_type = 1; + break; + case ON_ErrorEvent::Type::Assert: + obsolete_type = 2; + break; + case ON_ErrorEvent::Type::Custom: + obsolete_type = 0; + break; + case ON_ErrorEvent::Type::SubDError: + obsolete_type = 0; + break; + case ON_ErrorEvent::Type::NotValid: + obsolete_type = 0; + break; + default: + obsolete_type = 0; + break; } - ON_ErrorMessage(type,buffer); + ON_ErrorMessage(obsolete_type,static_cast(s)); } } -#if defined(__ANDROID__) -static void ON_PrintErrorMessage( - int type, // 0 = warning, 1 = error, 2 = assert - const char* sFileName, - int line_number, - const char* sFunctionName, - const char* sFormat, - int empty_args - ) -{ - va_list empty_va; - ON_PrintErrorMessage(type, sFileName, line_number, sFunctionName, sFormat, empty_va); -} -#endif - void ON_VARGS_FUNC_CDECL ON_Error( const char* sFileName, int line_number, const char* sFormat, - ...) + ... +) { ON_IncrementErrorCount(); - if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_ERROR_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + const bool bPrintErrorMessage = ON_Internal_PrintErrorMessage(); + if ( bPrintErrorMessage || ON_Internal_LogError() ) { + ON_String description; if (sFormat && sFormat[0]) { va_list args; va_start(args, sFormat); - ON_PrintErrorMessage(1,sFileName,line_number,0,sFormat,args); + description.FormatVargs(sFormat, args); va_end(args); } - else - { - ON_PrintErrorMessage(1,sFileName,line_number,0,0,0); - } + + const ON_ErrorEvent error_event( + ON_ErrorEvent::Type::Error, + sFileName, + (unsigned int)line_number, + nullptr, + description + ); + + ON_Internal_RecordErrorEvent(error_event, bPrintErrorMessage); } } -void ON_VARGS_FUNC_CDECL ON_ErrorEx(const char* sFileName, int line_number, const char* sFunctionName, - const char* sFormat, ...) +void ON_VARGS_FUNC_CDECL ON_ErrorEx( + const char* sFileName, + int line_number, + const char* sFunctionName, + const char* sFormat, + ... +) { ON_IncrementErrorCount(); - if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_ERROR_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + const bool bPrintErrorMessage = ON_Internal_PrintErrorMessage(); + if (bPrintErrorMessage || ON_Internal_LogError()) { - if (sFormat && sFormat[0]) + ON_String description; + if (sFormat && sFormat[0]) { va_list args; va_start(args, sFormat); - ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,sFormat,args); + description.FormatVargs(sFormat, args); va_end(args); } - else - { - ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,0,0); - } + + const ON_ErrorEvent error_event( + ON_ErrorEvent::Type::Error, + sFileName, + (unsigned int)line_number, + sFunctionName, + description + ); + + ON_Internal_RecordErrorEvent(error_event, bPrintErrorMessage); } } -void ON_VARGS_FUNC_CDECL ON_Warning(const char* sFileName, int line_number, - const char* sFormat, ...) +void ON_VARGS_FUNC_CDECL ON_Warning( + const char* sFileName, + int line_number, + const char* sFormat, + ... +) { ON_IncrementWarningCount(); - if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_WARNING_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + const bool bPrintErrorMessage = ON_Internal_PrintWarningMessage(); + if ( bPrintErrorMessage || ON_Internal_LogError()) { + ON_String description; if (sFormat && sFormat[0]) { va_list args; va_start(args, sFormat); - ON_PrintErrorMessage(0,sFileName,line_number,0,sFormat,args); + description.FormatVargs(sFormat, args); va_end(args); } - else - { - ON_PrintErrorMessage(0,sFileName,line_number,0,0,0); - } + + const ON_ErrorEvent error_event( + ON_ErrorEvent::Type::Warning, + sFileName, + (unsigned int)line_number, + nullptr, + description + ); + + ON_Internal_RecordErrorEvent(error_event, bPrintErrorMessage); } } - -void ON_VARGS_FUNC_CDECL ON_WarningEx(const char* sFileName, int line_number, const char* sFunctionName, - const char* sFormat, ...) +void ON_VARGS_FUNC_CDECL ON_WarningEx( + const char* sFileName, + int line_number, + const char* sFunctionName, + const char* sFormat, + ... +) { ON_IncrementWarningCount(); - if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_WARNING_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + const bool bPrintErrorMessage = ON_Internal_PrintWarningMessage(); + if ( bPrintErrorMessage || ON_Internal_LogError()) { + ON_String description; if (sFormat && sFormat[0]) { va_list args; va_start(args, sFormat); - ON_PrintErrorMessage(0,sFileName,line_number,sFunctionName,sFormat,args); + description.FormatVargs(sFormat, args); va_end(args); } - else - { - ON_PrintErrorMessage(0,sFileName,line_number,sFunctionName,0,0); - } + + const ON_ErrorEvent error_event( + ON_ErrorEvent::Type::Warning, + sFileName, + (unsigned int)line_number, + sFunctionName, + description + ); + + ON_Internal_RecordErrorEvent(error_event, bPrintErrorMessage); } } -void ON_VARGS_FUNC_CDECL ON_REMOVE_ASAP_AssertEx(int bCondition, - const char* sFileName, int line_number, const char* sFunctionName, - const char* sFormat, ...) +void ON_VARGS_FUNC_CDECL ON_REMOVE_ASAP_AssertEx( + int bCondition, + const char* sFileName, + int line_number, + const char* sFunctionName, + const char* sFormat, + ... +) { if ( false == bCondition) { - if ( 0 != ON_DEBUG_ERROR_MESSAGE_OPTION && ON_ERROR_COUNT <= ON_MAX_ERROR_MESSAGE_COUNT ) + + const bool bPrintErrorMessage = ON_Internal_PrintErrorMessage(); + if ( bPrintErrorMessage || ON_Internal_LogError()) { + ON_String description; if (sFormat && sFormat[0]) { va_list args; va_start(args, sFormat); - ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,sFormat,args); + description.FormatVargs(sFormat, args); va_end(args); } - else - { - ON_PrintErrorMessage(1,sFileName,line_number,sFunctionName,0,0); - } + + const ON_ErrorEvent error_event( + ON_ErrorEvent::Type::Warning, + sFileName, + (unsigned int)line_number, + sFunctionName, + description + ); + + ON_Internal_RecordErrorEvent(error_event, bPrintErrorMessage); } } } +//////////////////////////////////////////////////////////////////////// +// +// ON_ErrorEvent +// +ON_ErrorEvent::ON_ErrorEvent(const ON_ErrorEvent& src) +{ + Internal_CopyFrom(src); +} + +ON_ErrorEvent& ON_ErrorEvent::operator=(const ON_ErrorEvent& src) +{ + if (this != &src) + { + Internal_CopyFrom(src); + } + return *this; +} + +void ON_ErrorEvent::Internal_CopyFrom(const ON_ErrorEvent& src) +{ + m_event_type = src.m_event_type; + m_line_number = src.m_line_number; + m_file_name = nullptr; + m_function_name = nullptr; + m_description = nullptr; + + const size_t sizeof_buffer = sizeof(m_buffer); + memcpy(m_buffer, src.m_buffer, sizeof_buffer); + + // buffer_capacity keeps code working if char changes to wchar_t + const size_t buffer_capacity = sizeof_buffer/sizeof(m_buffer[0]); + const char* src_buffer = &src.m_buffer[0]; + + if (nullptr != src.m_file_name && src.m_file_name >= src_buffer) + { + const size_t i = (src.m_file_name - src_buffer); + if (i >= 0 && i < buffer_capacity) + m_file_name = &m_buffer[i]; + } + if (nullptr != src.m_function_name && src.m_function_name >= src_buffer) + { + const size_t i = (src.m_function_name - src_buffer); + if (i >= 0 && i < buffer_capacity) + m_function_name = &m_buffer[i]; + } + if (nullptr != src.m_description && src.m_description >= src_buffer) + { + const size_t i = (src.m_description - src_buffer); + if (i >= 0 && i < buffer_capacity) + m_description = &m_buffer[i]; + } +} + + +ON_ErrorEvent::ON_ErrorEvent( + ON_ErrorEvent::Type event_type, + const char* file_name, + unsigned int line_number, + const char* function_name, + const char* description +) + : m_event_type(event_type) + , m_line_number(line_number) +{ + const size_t buffer_capacity = sizeof(m_buffer) / sizeof(m_buffer[0]); + size_t file_name_length = ON_String::Length(file_name); + size_t function_name_length = ON_String::Length(function_name); + size_t description_length = ON_String::Length(description); + if (file_name_length + function_name_length + description_length + 3 > buffer_capacity) + { + const size_t cap = buffer_capacity / 4; + if (file_name_length > cap ) + { + file_name += (file_name_length - cap); + file_name_length = cap; + } + if (file_name_length + function_name_length + description_length + 3 > buffer_capacity) + { + if (function_name_length > cap) + { + function_name += (function_name_length - cap); + function_name_length = cap; + } + } + } + + char* c = m_buffer; + char* c1 = m_buffer + (buffer_capacity - 1); + + if (file_name_length > 0 && c < c1) + { + m_file_name = c; + for (size_t i = 0; i < file_name_length && c < c1; i++) + *c++ = file_name[i]; + if (c < c1) + *c++ = 0; + } + + if (function_name_length > 0 && c < c1) + { + m_function_name = c; + for (size_t i = 0; i < function_name_length && c < c1; i++) + *c++ = function_name[i]; + if (c < c1) + *c++ = 0; + } + + if (description_length > 0 && c < c1) + { + m_description = c; + for (size_t i = 0; i < description_length && c < c1; i++) + *c++ = description[i]; + if (c < c1) + *c++ = 0; + } + + *c1 = 0; +} + +const ON_ErrorEvent ON_ErrorEvent::Create( + ON_ErrorEvent::Type event_type, + const char* file_name, + unsigned int line_number, + const char* function_name, + const char* description +) +{ + return ON_ErrorEvent( + event_type, + file_name, + line_number, + function_name, + description + ); +} + +const char* ON_ErrorEvent::FileName() const +{ + return (nullptr != m_file_name || 0 != m_file_name) ? m_file_name : ""; +} + +const char* ON_ErrorEvent::FunctionName() const +{ + return (nullptr != m_function_name || 0 != m_function_name) ? m_function_name : ""; +} + +const char* ON_ErrorEvent::Description() const +{ + return (nullptr != m_description || 0 != m_description) ? m_description : ""; +} + +unsigned int ON_ErrorEvent::LineNumber() const +{ + return m_line_number; +} + +ON_ErrorEvent::Type ON_ErrorEvent::EventType() const +{ + return m_event_type; +} + +const char* ON_ErrorEvent::TypeToString( + ON_ErrorEvent::Type event_type +) +{ + const char* s; + switch (event_type) + { + case ON_ErrorEvent::Type::Unset: + s = "Unset"; + break; + case ON_ErrorEvent::Type::Warning: + s = "Warning"; + break; + case ON_ErrorEvent::Type::Error: + s = "Error"; + break; + case ON_ErrorEvent::Type::Assert: + s = "Assert"; + break; + case ON_ErrorEvent::Type::Custom: + s = "Custom"; + break; + case ON_ErrorEvent::Type::SubDError: + s = "SubDError"; + break; + case ON_ErrorEvent::Type::NotValid: + s = "NotValid"; + break; + default: + s = "Invalid"; + break; + } + + return s; +} + +const ON_String ON_ErrorEvent::ToString() const +{ + const char* function_name = FunctionName(); + const char* description = Description(); + if (0 != function_name[0]) + { + return ON_String::FormatToString( + "%s.%u %s(): %s \"%s\"", + FileName(), + LineNumber(), + function_name, + ON_ErrorEvent::TypeToString(m_event_type), + description + ); + } + return ON_String::FormatToString( + "%s.%u: %s \"%s\"", + FileName(), + LineNumber(), + ON_ErrorEvent::TypeToString(m_event_type), + description + ); +} + +void ON_ErrorEvent::Dump( + class ON_TextLog& text_log +) const +{ + const ON_String s(ToString()); + text_log.Print("%s\n", static_cast(s)); +} + + +//////////////////////////////////////////////////////////////////////// +// +// ON_ErrorLog +// + +bool ON_ErrorLog::EnableLogging() +{ + if (nullptr == this) + return false; + if (this == ON_ACTIVE_ERROR_LOG) + return true; + if (nullptr != ON_ACTIVE_ERROR_LOG) + return false; + if (Count() >= ON_ErrorLog::MaximumEventCount) + return false; + + ON_ACTIVE_ERROR_LOG = this; + return true; +} + +void ON_ErrorLog::DisableLogging() +{ + if (nullptr != ON_ACTIVE_ERROR_LOG && this == ON_ACTIVE_ERROR_LOG) + { + ON_ACTIVE_ERROR_LOG = nullptr; + } +} + +void ON_ErrorLog::Dump( + class ON_TextLog& text_log +) const +{ + const unsigned int count = Count(); + text_log.Print("Error log: %u events\n", count); + ON_TextLogIndent indent1(text_log); + for (unsigned int i = 0; i < count; i++) + { + m_events[i].Dump(text_log); + } +} + + +ON_ErrorLog::~ON_ErrorLog() +{ + if (ON_ACTIVE_ERROR_LOG == this) + ON_ACTIVE_ERROR_LOG = nullptr; +} + +unsigned int ON_ErrorLog::Append( + const ON_ErrorEvent& error_event +) +{ + const unsigned int event_capacity = (unsigned int)(sizeof(m_events) / sizeof(m_events[0])); + if (m_event_count >= event_capacity) + return 0; + + if (m_event_count < event_capacity) + { + m_events[m_event_count++] = error_event; + if (m_event_count == event_capacity && ON_ACTIVE_ERROR_LOG == this) + { + // stop wasting time logging errors + ON_ACTIVE_ERROR_LOG = nullptr; + } + } + return m_event_count; +} + +unsigned int ON_ErrorLog::Count() const +{ + return m_event_count <= ON_ErrorLog::MaximumEventCount ? m_event_count : ON_ErrorLog::MaximumEventCount; +} + +const ON_ErrorEvent& ON_ErrorLog::Event(unsigned int i) const +{ + return (i < m_event_count) ? m_events[i] : ON_ErrorEvent::Unset; +} + +void ON_ErrorLog::Clear() +{ + m_event_count = 0; +} diff --git a/opennurbs_error.h b/opennurbs_error.h index 7a2a9f0c..b25d92a5 100644 --- a/opennurbs_error.h +++ b/opennurbs_error.h @@ -129,4 +129,143 @@ void ON_MathError( ON_END_EXTERNC +#if defined(ON_CPLUSPLUS) + +class ON_CLASS ON_ErrorEvent +{ +public: + enum class Type : unsigned char + { + Unset = 0, + Warning = 1, // call to ON_WARNING / ON_Warning / ON_WarningEx + Error = 2, // call to ON_ERROR / ON_Error / ON_ErrorEx + Assert = 3, // ON_ASSERT (do not use ON_ASSERT - write code that handles errors and calls ON_ERROR) + Custom = 4, + SubDError = 5, // call to ON_SubDIncrementErrorCount() + NotValid = 6 // call to ON_IsNotValid() + }; + + static const char* TypeToString( + ON_ErrorEvent::Type event_type + ); + + const ON_String ToString() const; + +public: + ON_ErrorEvent() = default; + ~ON_ErrorEvent() = default; + ON_ErrorEvent(const ON_ErrorEvent&); + ON_ErrorEvent& operator=(const ON_ErrorEvent&); + + ON_ErrorEvent( + ON_ErrorEvent::Type event_type, + const char* file_name, + unsigned int line_number, + const char* function_name, + const char* description + ); + + static const ON_ErrorEvent Create( + ON_ErrorEvent::Type event_type, + const char* file_name, + unsigned int line_number, + const char* function_name, + const char* description + ); + + static const ON_ErrorEvent Unset; + + const char* FileName() const; + const char* FunctionName() const; + const char* Description() const; + unsigned int LineNumber() const; + ON_ErrorEvent::Type EventType() const; + + void Dump( + class ON_TextLog& text_log + ) const; + +private: + friend class ON_ErrorLog; + + ON_ErrorEvent::Type m_event_type = ON_ErrorEvent::Type::Unset; + unsigned char m_reserved1 = 0; + unsigned short m_reserved2 = 0; + unsigned int m_line_number = 0; + const char* m_file_name = nullptr; + const char* m_function_name = nullptr; + const char* m_description = nullptr; + char m_buffer[128] = {}; + + void Internal_CopyFrom(const ON_ErrorEvent& src); +}; + + +class ON_CLASS ON_ErrorLog +{ +public: + enum : unsigned int + { + MaximumEventCount = 32 + }; +public: + ON_ErrorLog() = default; + virtual ~ON_ErrorLog(); + ON_ErrorLog(const ON_ErrorLog&) = default; + ON_ErrorLog& operator=(const ON_ErrorLog&) = default; + + /* + Parameters: + error_event - [in] + event to add + Returns: + 0: Event not added because maximum capacity reached. + >0: Number of events after adding error_event. + */ + virtual + unsigned int Append( + const ON_ErrorEvent& error_event + ); + + /* + Returns: + Total number of error events. + */ + unsigned int Count() const; + + /* + Parameters: + i - [in] + zero based event index. + Returns + Event at specified index or ON_ErrorEvent::Unset if the index is out of range. + */ + const ON_ErrorEvent& Event(unsigned int i) const; + + void Clear(); + + /* + Returns: + True if up to ON_ErrorLog::MaximumErrorCount error events will be saved in this to this error log. + False if another error log is active. + */ + bool EnableLogging(); + + /* + Description: + Stop logging errors to this error log. + */ + void DisableLogging(); + + void Dump( + class ON_TextLog& text_log + ) const; + +protected: + unsigned int m_event_count = 0; + ON_ErrorEvent m_events[ON_ErrorLog::MaximumEventCount]; +}; + +#endif + #endif diff --git a/opennurbs_error_message.cpp b/opennurbs_error_message.cpp index 2ebc95fa..c07eff65 100644 --- a/opennurbs_error_message.cpp +++ b/opennurbs_error_message.cpp @@ -47,3 +47,8 @@ void ON_ErrorMessage( #endif } } + + + + + diff --git a/opennurbs_extensions.cpp b/opennurbs_extensions.cpp index 94d905a4..eebac150 100644 --- a/opennurbs_extensions.cpp +++ b/opennurbs_extensions.cpp @@ -575,6 +575,21 @@ ON_ModelComponentReference ONX_Model::ComponentFromId( : ON_ModelComponentReference::Empty; } +ON_ModelComponentReference ONX_Model::ModelGeometryFromId( + ON_UUID model_object_id +) const +{ + return ComponentFromId(ON_ModelComponent::Type::ModelGeometry, model_object_id); +} + +const ON_ModelGeometryComponent& ONX_Model::ModelGeometryComponentFromId( + ON_UUID model_object_id +) const +{ + const ON_ModelGeometryComponent* p = ON_ModelGeometryComponent::Cast(ModelGeometryFromId(model_object_id).ModelComponent()); + return (nullptr != p) ? *p : ON_ModelGeometryComponent::Unset; +} + ON_ModelComponentReference ONX_Model::ComponentFromName( ON_ModelComponent::Type component_type, ON_UUID component_parent_id, @@ -4206,6 +4221,14 @@ const ON_wString ONX_ModelTest::Source3dmFilePath() const return m_source_3dm_file_path; } +const ON_wString ONX_ModelTest::TextLogSource3dmFilePath() const +{ + return + m_text_log_3dm_file_path.IsNotEmpty() + ? m_text_log_3dm_file_path + : Source3dmFilePath(); +} + unsigned int ONX_ModelTest::Source3dmFileVersion() const { @@ -4251,6 +4274,7 @@ bool ONX_ModelTest::ReadTest( const char* file_path, ONX_ModelTest::Type test_type, bool bKeepModels, + const char* text_log_file_path, ON_TextLog* text_log ) { @@ -4283,7 +4307,8 @@ bool ONX_ModelTest::ReadTest( ON_BinaryFile archive(ON::archive_mode::read3dm, fp); archive.SetArchiveFullPath(ON_wString(file_path)); - Internal_ReadTest(archive, test_type, bKeepModels, text_log); + ON_wString wide_text_log_file_path(text_log_file_path); + Internal_ReadTest(archive, test_type, bKeepModels, wide_text_log_file_path, text_log); break; } @@ -4304,6 +4329,7 @@ bool ONX_ModelTest::ReadTest( const wchar_t* file_path, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ) { @@ -4333,8 +4359,7 @@ bool ONX_ModelTest::ReadTest( ON_BinaryFile archive(ON::archive_mode::read3dm, fp); archive.SetArchiveFullPath(file_path); - - Internal_ReadTest(archive, test_type, bKeepModels, text_log); + Internal_ReadTest(archive, test_type, bKeepModels, text_log_file_path, text_log); break; } @@ -4354,6 +4379,7 @@ bool ONX_ModelTest::ReadTest( FILE* fp, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ) { @@ -4374,7 +4400,7 @@ bool ONX_ModelTest::ReadTest( ON_BinaryFile archive(ON::archive_mode::read3dm, fp); - Internal_ReadTest(archive, test_type, bKeepModels, text_log); + Internal_ReadTest(archive, test_type, bKeepModels, text_log_file_path, text_log); break; } @@ -4553,8 +4579,9 @@ void ONX_ModelTest::Dump(ON_TextLog& text_log) const text_log.Print("Test type: %s\n", ONX_ModelTest::TestTypeToString(test_type)); - const ON_wString source_archive = Source3dmFilePath(); - text_log.Print(L"Source 3dm file path: %ls\n", static_cast(source_archive)); + //const ON_wString source_archive = Source3dmFilePath(); + const ON_wString test_log_source_archive = TextLogSource3dmFilePath(); + text_log.Print(L"Source 3dm file path: %ls\n", static_cast(test_log_source_archive)); text_log.Print(L"Source 3dm file version: %u\n", Source3dmFileVersion()); text_log.Print("Result: "); @@ -4628,11 +4655,12 @@ bool ONX_ModelTest::ReadTest( ON_BinaryArchive& archive, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ) { Internal_BeginTest(); - Internal_ReadTest(archive, test_type, bKeepModels, text_log); + Internal_ReadTest(archive, test_type, bKeepModels, text_log_file_path, text_log); return Internal_TallyTestResults(); } @@ -4640,11 +4668,14 @@ void ONX_ModelTest::Internal_ReadTest( ON_BinaryArchive& archive, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ) { m_test_type = test_type; m_source_3dm_file_path = archive.ArchiveFullPath(); + m_text_log_3dm_file_path = text_log_file_path; + const unsigned int current_3dm_file_version = (unsigned int)ON_BinaryArchive::CurrentArchiveVersion(); ON_TextLogNull devnull; @@ -4667,12 +4698,14 @@ void ONX_ModelTest::Internal_ReadTest( if (bKeepModels) this->m_model[0] = model0_sp; - ON_String file_path(archive.ArchiveFullPath()); - if (file_path.IsEmpty()) - file_path = "archive"; + ON_String text_log_3dm_archive_name = TextLogSource3dmFilePath(); + if (text_log_3dm_archive_name.IsEmpty()) + { + text_log_3dm_archive_name = "archive"; + } const ON_String read0_description - = ON_String::FormatToString("ONX_Model.Read(%s,...)", static_cast(file_path)); + = ON_String::FormatToString("ONX_Model.Read(%s,...)", static_cast(text_log_3dm_archive_name)); // read the original file text_log->Print("Calling %s ...\n", static_cast(read0_description)); diff --git a/opennurbs_extensions.h b/opennurbs_extensions.h index a7e31899..ded85638 100644 --- a/opennurbs_extensions.h +++ b/opennurbs_extensions.h @@ -1245,16 +1245,31 @@ public: double model_space_text_scale ); + /* + Description: + Find a model geometry component from Id + Parameters: + model_geometry_component_id - [in] + Returns: + If there is a model geometry component with the id, it is returned. + Otherwise, ON_ModelComponentReference::Empty is returned. + */ + ON_ModelComponentReference ModelGeometryFromId( + ON_UUID model_geometry_component_id + ) const; - ON_ModelGeometryComponent ModelGeometryFromIndex( - int model_object_index - ); - ON_ModelGeometryComponent ModelGeometryFromUnsignedIndex( - unsigned int model_object_index - ); - ON_ModelGeometryComponent ModelGeometryFromId( - unsigned int model_object_index - ); + /* + Description: + Find a model geometry component from Id + Parameters: + model_geometry_component_id - [in] + Returns: + If there is a model geometry component with the id, it is returned. + Otherwise, ON_ModelGeometryComponent::Unset is returned. + */ + const ON_ModelGeometryComponent& ModelGeometryComponentFromId( + ON_UUID model_geometry_component_id + ) const; public: ON_SimpleArray m_userdata_table; @@ -1718,6 +1733,9 @@ public: bKeepModels - [in] If true, then the ONX_Models created by reading 3dm archives are saved so the can be examined after the tests complete. + text_log_file_path - [in] + If not empty, the string to use for file_path in the output text_log. + This is used to create logs on different computers that can be compared. text_log - [in] If text_log is not nullptr, then a summary of the test is sent to text_log. Returns: @@ -1728,6 +1746,7 @@ public: const char* file_path, ONX_ModelTest::Type test_type, bool bKeepModels, + const char* text_log_file_path, ON_TextLog* text_log ); @@ -1742,6 +1761,9 @@ public: bKeepModels - [in] If true, then the ONX_Models created by reading 3dm archives are saved so the can be examined after the tests complete. + text_log_file_path - [in] + If not empty, the string to use for file_path in the output text_log. + This is used to create logs on different computers that can be compared. text_log - [in] If text_log is not nullptr, then a summary of the test is sent to text_log. Returns: @@ -1752,6 +1774,7 @@ public: const wchar_t* file_path, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ); @@ -1766,6 +1789,9 @@ public: bKeepModels - [in] If true, then the ONX_Models created by reading 3dm archives are saved so the can be examined after the tests complete. + text_log_file_path - [in] + If not empty, the string to use for file_path in the output text_log. + This is used to create logs on different computers that can be compared. text_log - [in] If text_log is not nullptr, then a summary of the test is sent to text_log. Returns: @@ -1776,6 +1802,7 @@ public: FILE* fp, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ); @@ -1784,13 +1811,15 @@ public: Description: ONX_Model::Test() can be used to test reading a specific file. Parameters: - file_path - [in] - file path + archive - [in] test_type - [in] test to perform. bKeepModels - [in] If true, then the ONX_Models created by reading 3dm archives are saved so the can be examined after the tests complete. + text_log_file_path - [in] + If not empty, the string to use for file_path in the output text_log. + This is used to create logs on different computers that can be compared. text_log - [in] If text_log is not nullptr, then a summary of the test is sent to text_log. Returns: @@ -1801,6 +1830,7 @@ public: ON_BinaryArchive& archive, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ); @@ -1886,6 +1916,7 @@ private: ON_BinaryArchive& archive, ONX_ModelTest::Type test_type, bool bKeepModels, + const wchar_t* text_log_file_path, ON_TextLog* text_log ); @@ -1902,6 +1933,12 @@ public: */ const ON_wString Source3dmFilePath() const; + /* + Returns: + The string used in the output log to identify the source 3dm file. + */ + const ON_wString TextLogSource3dmFilePath() const; + /* Returns: Version of the 3dm fie, 1,2,3,4,5,50,60,... @@ -1968,6 +2005,10 @@ public: ON_wString m_source_3dm_file_path; + // if set, used when printing the name of m_source_3dm_file_path in the text + // log so results from different computers can be compared. + ON_wString m_text_log_3dm_file_path; + unsigned int m_model_3dm_file_version[3]; unsigned int m_current_test_index = 0; diff --git a/opennurbs_file_utilities.cpp b/opennurbs_file_utilities.cpp index 130cfd42..d8b1307c 100644 --- a/opennurbs_file_utilities.cpp +++ b/opennurbs_file_utilities.cpp @@ -43,6 +43,10 @@ #endif #endif +#if defined(ON_RUNTIME_APPLE) +#include "unistd.h" //for unlink +#endif + /////////////////////////////////////////////////////////////////////////////// void ON_String::SplitPath( @@ -1700,7 +1704,7 @@ const ON_wString ON_FileSystemPath::PlatformPath( ON_FileSystemPath::PathId path #if defined(ON_RUNTIME_WIN) KNOWNFOLDERID platform_path_id; #define ON_INTERNAL_SET_LOCAL_DIRECTORY_ID(win_fid,apple_fid) platform_path_id = win_fid -#elif defined(ON_RUNTIME_APPLE) +#elif defined(ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) NSSearchPathDirectory platform_path_id; #define ON_INTERNAL_SET_LOCAL_DIRECTORY_ID(win_fid,apple_fid) platform_path_id = apple_fid #endif @@ -1743,7 +1747,7 @@ const ON_wString ON_FileSystemPath::PlatformPath( ON_FileSystemPath::PathId path CoTaskMemFree(windows_path); } -#elif defined(ON_RUNTIME_APPLE) +#elif defined(ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) NSArray *apple_paths = NSSearchPathForDirectoriesInDomains(platform_path_id, NSUserDomainMask, YES); if ([apple_paths count] > 0) diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp index 4a0935ef..ab51db54 100644 --- a/opennurbs_font.cpp +++ b/opennurbs_font.cpp @@ -22,6 +22,241 @@ #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(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(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 { @@ -45,7 +280,17 @@ int ON_FontMetrics::UPM() const int ON_FontMetrics::AscentOfI() const { - return m_ascent_of_I; + 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 @@ -53,8 +298,14 @@ double ON_FontMetrics::GlyphScale(double text_height) const // Please consult Dale Lear and the ON_FontGlyph // bounding box, outline, and advance calculation code // before making any modifications to this function. - const double y = (double)AscentOfI(); - return (text_height > 0.0 && y > 0.0) ? (text_height / y) : 1.0; + 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 @@ -77,20 +328,48 @@ int ON_FontMetrics::UnderscorePosition() const return m_underscore_position; } -bool ON_FontMetrics::HeightsAreValid() const +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; - if (m_line_space < m_ascent - m_descent) - return false; - if (m_ascent_of_I > m_ascent) - return false; - if (m_UPM <= 0) - return false; + return true; } +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, @@ -110,13 +389,38 @@ void ON_FontMetrics::SetHeights( } 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_I + int ascent_of_capital ) { - m_ascent_of_I = (ascent_of_I > 0 && ascent_of_I < -ON_UNSET_INT_INDEX) ? ascent_of_I : 0; + 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( @@ -137,14 +441,122 @@ void ON_FontMetrics::SetUnderscore( 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 @@ -157,11 +569,14 @@ const ON_FontMetrics ON_FontMetrics::Scale( scaled_font_metrics.m_UPM = Internal_ScaleInt(scale, scaled_font_metrics.m_UPM); scaled_font_metrics.m_ascent = Internal_ScaleInt(scale, scaled_font_metrics.m_ascent); scaled_font_metrics.m_descent = Internal_ScaleInt(scale, scaled_font_metrics.m_descent); - if (font_metrics.m_line_space == Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,font_metrics.m_ascent_of_I)) - scaled_font_metrics.m_line_space = Internal_ScaleInt(ON_FontMetrics::DefaultLineFeedRatio,scaled_font_metrics.m_ascent_of_I); + + 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_ascent_of_I = Internal_ScaleInt(scale, scaled_font_metrics.m_ascent_of_I); scaled_font_metrics.m_strikeout_thickness = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_thickness); scaled_font_metrics.m_strikeout_position = Internal_ScaleInt(scale, scaled_font_metrics.m_strikeout_position); @@ -173,22 +588,61 @@ const ON_FontMetrics ON_FontMetrics::Scale( return scaled_font_metrics; } -// ON_GlyphMap::GlyphPool needs to be allocated before ON_ManagedFonts::List. -// This is so the pool is still around when the ON_ManagedFonts::List -// is being destroyed. -ON_Internal_FontGlyphPool ON_Internal_FontGlyphPool::theGlyphItemPool; -ON_ManagedFonts ON_ManagedFonts::List = ON_ManagedFonts(); +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 managed_fonts(m_managed_fonts_by_serial_number); - m_managed_fonts.Destroy(); - m_managed_fonts_by_serial_number.Destroy(); + + ON_SimpleArray 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(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 @@ -197,83 +651,72 @@ ON_ManagedFonts::~ON_ManagedFonts() managed_font->m_font_glyph_cache.reset(); - if (managed_font->m_runtime_serial_number >= 2) - delete managed_font; + 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 m_glyphmap; }; -static int CompareManagedFontCharacteristics( - const void* a, - const void* b - ) +ON_FontGlyphCache* ON_FontGlyphCache::New() { - const ON_Font* a_font = *((const ON_Font*const*)a); - const ON_Font* b_font = *((const ON_Font*const*)b); - return ON_Font::CompareFontCharacteristics(*a_font,*b_font); + ON_FontGlyphCache* font_cache = new ON_FontGlyphCache(); + font_cache->m_glyphmap = std::make_shared(); + return font_cache; } -const ON_Font* BinarySearchForManagedFontCharacteristics( const ON_Font& key, const ON_Font*const* base, size_t nel ) +void ON_FontGlyphCache::SetFontMetrics( + const ON_FontMetrics& font_unit_metrics +) { - if (nel > 0 && nullptr != base ) - { - size_t i; - const ON_Font* font; - int c; + m_font_unit_metrics = font_unit_metrics; - while ( nel > 0 ) - { - i = nel/2; - font = base[i]; - c = ON_Font::CompareFontCharacteristics(key,*font); - if ( c < 0 ) - { - nel = i; - } - else if ( c > 0 ) - { - i++; - base += i; - nel -= i; - } - else - { - return font; - } - } - } - return nullptr; + 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 int CompareManagedFontSerialNumber( - const void* a, - const void* b - ) -{ - const unsigned int a_sn = (*((const ON_Font*const*)a))->RuntimeSerialNumber(); - const unsigned int b_sn = (*((const ON_Font*const*)b))->RuntimeSerialNumber(); - if (a_sn < b_sn) - return -1; - if (a_sn > b_sn) - return 1; - return 0; -} - -const ON_Font* BinarySearchForManagedFontSerialNumber( unsigned int key, const ON_Font*const* base, size_t nel ) +static const ON_Font* Internal_BinarySearchForManagedFontSerialNumber( unsigned int key, const ON_Font*const* base, size_t nel ) { if (nel > 0 && nullptr != base ) { @@ -327,11 +770,22 @@ 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_by_serial_number.Array(); - const unsigned int font_count = m_managed_fonts_by_serial_number.UnsignedCount(); + 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()) @@ -339,116 +793,218 @@ const ON_Font* ON_ManagedFonts::GetFromSerialNumber( // This test should always find the managed font as long as the current numbering scheme is used. return managed_fonts[managed_font_serial_number - 1]; } - - unsigned int& sorted_count = m_sorted_by_serial_number_count; - - if (font_count > sorted_count + 4) - { - ON_qsort((void*)managed_fonts, font_count, sizeof(managed_fonts[0]), CompareManagedFontSerialNumber ); - sorted_count = font_count; - } - else - { - // most recently added font will be in the unsorted section. - for (unsigned int i = sorted_count; i < font_count; i++) - { - if (managed_font_serial_number == managed_fonts[i]->RuntimeSerialNumber()) - return managed_fonts[i]; - } - } + return - (sorted_count > 0) - ? BinarySearchForManagedFontSerialNumber(managed_font_serial_number, managed_fonts, sorted_count) + (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.UnsignedCount()) + if ( 0 == m_managed_fonts.Count() ) { // Put ON_Font::Default as the first entry in this list. - Internal_AddManagedFont(&ON_Font::Default); + Internal_AddManagedFont( &ON_Font::Default, nullptr ); } - const ON_Font* const * managed_fonts = m_managed_fonts.Array(); - const unsigned int font_count = m_managed_fonts.UnsignedCount(); - unsigned int& sorted_count = m_sorted_count; + if (font_characteristics.IsManagedFont()) + return &font_characteristics; - if (font_count > sorted_count + 4) + + 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()) { - ON_qsort((void*)managed_fonts, font_count, sizeof(managed_fonts[0]), CompareManagedFontCharacteristics ); - sorted_count = font_count; - } - else - { - // most recently added font will be in the unsorted section. - for (unsigned int i = sorted_count; i < font_count; i++) + bool bHaveName + = font_characteristics.PostScriptName().IsNotEmpty() + || font_characteristics.WindowsLogfontName().IsNotEmpty() + || font_characteristics.FamilyName().IsNotEmpty(); + if ( false == bHaveName || font_characteristics.HasUnsetProperties(false, false) ) { - if (0 == ON_Font::CompareFontCharacteristics( font_characteristics, *managed_fonts[i] ) ) - return managed_fonts[i]; + 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; } - const ON_Font* managed_font = BinarySearchForManagedFontCharacteristics(font_characteristics,m_managed_fonts.Array(),m_sorted_count); - if (nullptr == managed_font && bCreateIfNotFound) + ON_Font::NameLocale name_locale = ON_Font::NameLocale::LocalizedFirst; + + for (;;) { - if (font_characteristics.FontDescription().IsEmpty() - && font_characteristics.AppleFontName().IsEmpty() - && 0 == font_characteristics.FontFaceName()[0] - ) + // 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()) { - managed_font = &ON_Font::Default; + if (ON_wString::EqualOrdinal(name, ON_Font::Default.PostScriptName(name_locale), true)) + { + return &ON_Font::Default; + } + break; // Different PostScript names } - else + + name = set_font_characteristics->WindowsLogfontName(name_locale); + if (name.IsNotEmpty()) { - // The managed font constructor does not copy user data, m_font_index, m_font_id, m_gonna_change_font_cache - ON_MemoryAllocationTracking disable_tracking(false); - managed_font = Internal_AddManagedFont(new ON_Font(2, font_characteristics)); + 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; } - -const ON_Font* ON_ManagedFonts::GetFromAppleFontName( - const wchar_t* apple_font_name, - bool bCreateIfNotFound - ) -{ - if (0 == m_managed_fonts.UnsignedCount()) - { - // Put ON_Font::Default as the first entry in this list. - Internal_AddManagedFont(&ON_Font::Default); - } - - const ON_Font* const * managed_fonts = m_managed_fonts.Array(); - const unsigned int font_count = m_managed_fonts.UnsignedCount(); - - for (unsigned int i = 0; i < font_count; i++) - { - if (0 == ON_StringCompareOrdinalWideChar(apple_font_name, -1, managed_fonts[i]->m_apple_font_name, -1, true) ) - return managed_fonts[i]; - } - - const ON_Font* managed_font = nullptr; - if (bCreateIfNotFound) - { - ON_Font font_characteristics; - font_characteristics.SetFromAppleFontName(apple_font_name); - // The managed font constructor does not copy user data, m_font_index, m_font_id, m_gonna_change_font_cache - managed_font = Internal_AddManagedFont(new ON_Font(2,font_characteristics)); - } - - return managed_font; -} - - -static void Internal_AddManagedFontCleanString( +static void Internal_AddManagedFontSingleRefCountString( const ON_wString& s ) { @@ -459,167 +1015,1016 @@ static void Internal_AddManagedFontCleanString( } const ON_Font* ON_ManagedFonts::Internal_AddManagedFont( - const ON_Font* managed_font + 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); - ///////////////////// - // - // Put the cached glyph information here so we only have one set for each font - // - ON_FontGlyphCache* font_cache = new ON_FontGlyphCache(); - font_cache->m_glyphmap = std::make_shared(); - managed_font->m_font_glyph_cache = std::shared_ptr(font_cache); - - if (true) + if (0 == m_default_font_ptr && 1 == managed_font->m_runtime_serial_number) { - ON_FontMetrics font_unit_metrics; + // 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; + } - ON_ManagedFonts::GetFontMetrics( - managed_font, - font_unit_metrics - ); + 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; - const int ascent = font_unit_metrics.Ascent(); - const int descent = font_unit_metrics.Descent(); - if (font_unit_metrics.UPM() <= 0) + 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(font_cache); + } + + if (nullptr != font_cache) + { + if (false == font_cache->m_font_unit_metrics.HeightsAreValid()) { - const int line_space = font_unit_metrics.LineSpace(); - if (ascent > descent) - font_unit_metrics.SetHeights(ascent, descent, ascent - descent, line_space); - } - const int UPM = font_unit_metrics.UPM(); + ON_FontMetrics font_metrics_in_font_design_units; - if (UPM > 0 ) - { - int ascent_of_I = font_unit_metrics.AscentOfI(); - int line_space = font_unit_metrics.LineSpace(); - - if (ascent_of_I <= 0) + if ( + nullptr != managed_font_metrics_in_font_design_units + && managed_font_metrics_in_font_design_units->HeightsAreValid() + ) { - // Get 'I' glyph height. - // Do not use glyph cache or per glyph substuted fonts here. - // This call is used only to set the value of - // font_cache->m_unnormalized_metrics.m_height_of_I - // and that value needs to come from the font. - ON_TextBox unnormalized_glyph_box = ON_TextBox::Unset; - if (0 != ON_ManagedFonts::GetGlyphMetrics(managed_font, ON_Font::Constants::MetricsGlyphCodePoint, unnormalized_glyph_box)) - { - if (unnormalized_glyph_box.IsSet() && unnormalized_glyph_box.m_bbmax.j > 0) - ascent_of_I = unnormalized_glyph_box.m_bbmax.j; - } - - if (ascent_of_I <= 0) - { - if (ascent_of_I <= 0 && line_space > ascent - descent) - { - ascent_of_I = (int)ceil(line_space / ON_FontMetrics::DefaultLineFeedRatio); - } - - if (ascent_of_I <= 0 && &ON_Font::Default != managed_font) - { - const ON_FontMetrics default_font_unit_metrics = ON_Font::Default.FontUnitFontMetrics(); - if (default_font_unit_metrics.AscentOfI() > 0 && default_font_unit_metrics.UPM() > 0) - { - const double scale = ((double)UPM) / ((double)default_font_unit_metrics.UPM()); - ascent_of_I = (int)ceil(scale*default_font_unit_metrics.AscentOfI()); - } - } - - if (ascent > 0 && ascent_of_I > ascent) - ascent_of_I = ascent; - - } + 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 + ); } - if (line_space <= 0 && ascent_of_I > 0 ) - line_space = (int)ceil(ON_FontMetrics::DefaultLineFeedRatio*ascent_of_I); - - font_unit_metrics.SetHeights(ascent, descent, UPM, line_space); - font_unit_metrics.SetAscentOfI(ascent_of_I); - - font_cache->m_font_unit_metrics = font_unit_metrics; - - - font_cache->m_normalized_to_font_unit_scale = ((double)UPM) / ((double)ON_Font::Constants::AnnotationFontCellHeight); - font_cache->m_font_unit_to_normalized_scale = ((double)ON_Font::Constants::AnnotationFontCellHeight) / ((double)UPM); - - font_cache->m_normalized_metrics - = (font_cache->m_font_unit_to_normalized_scale > 0.0 && 1.0 != font_cache->m_font_unit_to_normalized_scale) - ? ON_FontMetrics::Scale(font_cache->m_font_unit_metrics, font_cache->m_font_unit_to_normalized_scale) - : font_cache->m_font_unit_metrics; + font_cache->SetFontMetrics(font_metrics_in_font_design_units); } } - - if ( false == font_cache->m_font_unit_metrics.HeightsAreValid() ) - { - ON_ERROR("Unable to get useful font metrics."); - // continue and save what we have - } - - const unsigned int font_count0 = m_managed_fonts.UnsignedCount(); - if (font_count0 > 0) - { - if ( font_count0 == m_sorted_by_serial_number_count - && m_managed_fonts_by_serial_number[font_count0 - 1]->RuntimeSerialNumber() < managed_font->RuntimeSerialNumber() - ) - { - m_sorted_by_serial_number_count++; - } - } - - m_managed_fonts.Append(managed_font); - m_managed_fonts_by_serial_number.Append(managed_font); - Internal_AddManagedFontCleanString(managed_font->m_apple_font_name); - Internal_AddManagedFontCleanString(managed_font->m_font_description); + // 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; } -unsigned int ON_ManagedFonts::GetList( - ON_SimpleArray< const ON_Font* >& managed_fonts - ) -{ - const unsigned int font_count = m_managed_fonts_by_serial_number.UnsignedCount(); - if (m_sorted_by_serial_number_count < font_count) - { - ON_qsort((void*)m_managed_fonts_by_serial_number.Array(), font_count, sizeof(managed_fonts[0]), CompareManagedFontSerialNumber); - m_sorted_by_serial_number_count = font_count; - } - managed_fonts = m_managed_fonts_by_serial_number; - return managed_fonts.UnsignedCount(); -} - #define ON_MANAGED_FONT_CHECK(falure_return_value) {if (0 != m_managed_font) (ON_ERROR("Cannot modify managed fonts."); return (falure_return_value);}} + // // END list of managed ON_Fonts // ////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////// +// +// BEGIN list of platform ON_Fonts +// + +#if defined (ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) + +//static NSString* Internal_FontCategory (NSFont* font) +//{ +// // determine file path for NSFont +// CTFontRef fontRef = (__bridge CTFontRef) font; +// CFURLRef fontURLRef = (CFURLRef) CTFontCopyAttribute (fontRef, kCTFontURLAttribute); +// NSURL* fontURL = (NSURL*) CFBridgingRelease (fontURLRef); +// NSString* fontPath = fontURL.path; +// if ([fontPath hasPrefix: @"/System/"]) +// return @"System"; +// if ([fontPath hasPrefix: @"/Library/Fonts/Microsoft/"]) +// return @"Microsoft"; +// if ([fontPath hasPrefix: @"/Library/Fonts/"]) +// return @"Library"; +// return @"User"; +//} + + +//static bool Internal_IsSystemFont (NSFont* font) +//{ +// // determine file path for NSFont +// CTFontRef fontRef = (__bridge CTFontRef) font; +// CFURLRef fontURLRef = (CFURLRef) CTFontCopyAttribute (fontRef, kCTFontURLAttribute); +// NSURL* fontURL = (NSURL*) CFBridgingRelease (fontURLRef); +// NSString* fontPath = fontURL.path; +// return [fontPath hasPrefix: @"/System/"] || [fontPath hasPrefix: @"/Library/"]; +//} + + +static void Internal_GetAppleInstalledFonts( + ON_SimpleArray& platform_font_list +) +{ + const bool bAnnotationFont = true; + const double pointSize = ON_Font::Constants::AnnotationFontApplePointSize; + + + for (NSString* fontManagerName in[NSFontManager.sharedFontManager availableFonts]) + { + const bool bIsInternalSystemFont = [fontManagerName hasPrefix : @"."]; + if (bIsInternalSystemFont) + { + continue; // skip internal system fonts + } + + const ON_String utf8fontManagerName(fontManagerName.UTF8String); + + NSFont* apple_font = [NSFont fontWithName : fontManagerName size : pointSize]; + if (apple_font == nil) + { + continue; // damaged font + } + + const ON_wString ON_Font_apple_font_name(apple_font.fontName.UTF8String); + + //NSString* fontCategory = Internal_FontCategory(apple_font); + + ON_Font* platform_font = new ON_Font(); + if (false == platform_font->SetFromAppleFont(apple_font, bAnnotationFont)) + { + delete platform_font; + continue; + } + platform_font_list.Append(platform_font); + } +} +#endif + +#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*)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_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 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& 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_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 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()); +} + +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(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: \n", quartet_face[i]); + else + text_log.Print( + L"%ls: %ls %ls (%ls) Weight = %ls Slope = %ls \n", + quartet_face[i], + static_cast(font->FamilyName()), + static_cast(font->FaceName()), + static_cast(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(lhs_font->FontStyle()); + const int rhs_font_style = (int)static_cast(rhs_font->FontStyle()); + rc = (lhs_font_style - rhs_font_style); + if (0 != rc) + return rc; + + // width + const int lhs_font_stretch = (int)static_cast(lhs_font->FontStretch()); + const int rhs_font_stretch = (int)static_cast(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 ( IsManagedFont() ) + 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 { - // ON_Font::Default and managed fonts can never be modified - if ( this == &ON_Font::Default ) - ON_ErrorEx(file_name, line_number, function_name, "ON_Font::Default cannot be modified."); - else - ON_ErrorEx(file_name, line_number, function_name, "Managed fonts cannot be modified."); - return false; + // Modificaton of this font means the managed information it references + // will not be valid. A reference to the correct cached information + // will be generated when it is actually needed. + m_font_glyph_cache.reset(); + return true; } - // 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__) @@ -652,19 +2057,13 @@ const ON_FontGlyph* ON_Font::CodePointGlyph( const ON_Font* ON_Font::GetManagedFont( const ON_Font& font_characteristics, bool bCreateIfNotFound - ) +) { - if ( font_characteristics.IsManagedFont() ) + if (font_characteristics.IsManagedFont()) { // No need to look it up and return itself. return &font_characteristics; } - -#if defined (ON_RUNTIME_APPLE) - const ON_Font* font = ON_ManagedFonts::List.GetFromAppleFontName(font_characteristics.m_apple_font_name,bCreateIfNotFound); - if (nullptr != font) - return font; -#endif return ON_ManagedFonts::List.GetFromFontCharacteristics(font_characteristics,bCreateIfNotFound); } @@ -680,7 +2079,2215 @@ unsigned int ON_Font::GetManagedFontList( ON_SimpleArray< const ON_Font* >& managed_fonts ) { - return ON_ManagedFonts::List.GetList(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(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(target_font.FontWeight())) - ((int)static_cast(candidate_font->FontWeight()))); + m_stretch_delta + = bEqualPostScriptName + ? 0 + : abs(((int)static_cast(target_font.FontStretch())) - ((int)static_cast(candidate_font->FontStretch()))); + m_style_delta + = bEqualPostScriptName + ? 0 + : abs(((int)static_cast(target_font.FontStyle())) - ((int)static_cast(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 s(rtf_font_name); + s.TrimLeftAndRight(); + if (s.IsEmpty()) + s = ON_Font::DefaultFamilyName(); + rtf_font_name = s; + + ON_Font font(ON_Font::Default); + if (bRtfBold) + font.SetFontWeight(ON_Font::Weight::Bold); + if (bRtfItalic) + font.SetFontStyle(ON_Font::Style::Italic); + + font.Internal_ClearAllNames(); + font.m_loc_family_name = rtf_font_name; + font.m_en_family_name = font.m_loc_family_name; + + font.m_loc_postscript_name = rtf_font_name; + font.m_en_postscript_name = font.m_loc_postscript_name; + + font.m_loc_windows_logfont_name = rtf_font_name; + font.m_en_windows_logfont_name = font.m_loc_windows_logfont_name; + + return ON_Font::InstalledFontList().FromFontProperties(&font,false,true); +} + + +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::Condensed, 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; + + if (key.m_loc_family_name.IsEmpty() || key.m_loc_face_name.IsEmpty()) + 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; + if (bRequireFaceMatch && candidate->FamilyName().IsNotEmpty() && false == ON_Font::EqualFontFamilyAndFace(&key, candidate)) + continue; + candidate_dev = ON_Font::WeightStretchStyleDeviation(prefered_weight, prefered_stretch, prefered_style, candidate); + if (0 == candidate_dev) + return candidate; + if (nullptr == font || 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 desired_weight + = (bBold != IsBold()) + ? (bBold ? ON_Font::Weight::Bold : ON_Font::Weight::Normal) + : 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& 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(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(sextet[4]->FontWeight())) + { + if (nullptr == sextet[5]) + sextet[5] = sextet[4]; + sextet[4] = f; + } + else if (nullptr != sextet[5] && f_weight > static_cast(sextet[5]->FontWeight())) + sextet[5] = f; + } + else if (f->IsLight()) + { + if (nullptr == sextet[1]) + sextet[1] = f; + else if (f_weight > static_cast(sextet[1]->FontWeight())) + { + if ( nullptr == sextet[0]) + sextet[0] = sextet[1]; + sextet[1] = f; + } + else if (nullptr != sextet[0] && f_weight < static_cast(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 @@ -688,6 +4295,24 @@ 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 ) @@ -766,10 +4391,7 @@ const ON_Font* ON_Font::GetManagedFont( ON_Font::Style font_style ) { - unsigned int logfont_charset - = (nullptr != face_name && 0 != face_name[0]) - ? static_cast(ON_Font::WindowsLogfontCharSetFromFaceName(face_name)) - : static_cast(ON_Font::WindowsConstants::logfont_default_charset); + const unsigned int logfont_charset = static_cast(ON_Font::WindowsLogfontCharSetFromFaceName(face_name)); return ON_Font::GetManagedFont( point_size, @@ -877,17 +4499,71 @@ bool ON_Font::IsNotAppleFontName( } const ON_Font* ON_Font::GetManagedFontFromAppleFontName( - const wchar_t* apple_font_name + const char* postscript_name ) { - ON_Font font_characteristics; - if ( false == font_characteristics.SetFromAppleFontName(apple_font_name) ) - return nullptr; - return font_characteristics.ManagedFont(); + 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(); +} + +#if defined(ON_RUNTIME_APPLE_OBJECTIVE_C_AVAILABLE) +const ON_Font* ON_Font::GetManagedFontFromAppleFont( + NSFont* apple_font, + bool bAnnotationFont +) +{ + ON_Font font_characteristics; + if ( false == font_characteristics.SetFromAppleFont(apple_font,bAnnotationFont) ) + return nullptr; + return font_characteristics.ManagedFont(); +} +#endif int ON_Font::WindowsLogfontWeightFromWeight( ON_Font::Weight font_weight @@ -1008,7 +4684,7 @@ ON_Font::Weight ON_Font::WeightFromWindowsLogfontWeight( }; const size_t weight_count = sizeof(weights) / sizeof(weights[0]); - ON_Font::Weight font_weight = ON_Font::Default.m_font_weight; + ON_Font::Weight font_weight = ON_Font::Weight::Normal; int delta = std::abs(static_cast(ON_Font::WindowsLogfontWeightFromWeight(font_weight)) - windows_logfont_weight); for (size_t i = 0; 0 != delta && i < weight_count; i++) @@ -1048,57 +4724,349 @@ ON_Font::Weight ON_Font::WeightFromAppleFontWeightTrait( return ON_Font::WeightFromWindowsLogfontWeight(windows_logfont_weight); } -void ON_Font::CopyHelper(const ON_Font& src) +void ON_Font::Internal_CopyFrom( + const ON_Font& src + ) { - m_font_weight = src.m_font_weight; - m_windows_logfont_weight = src.m_windows_logfont_weight; - m_apple_font_weight_trait = src.m_apple_font_weight_trait; - - m_font_style = src.m_font_style; - m_font_stretch = src.m_font_stretch; - m_font_bUnderlined = src.m_font_bUnderlined; - m_font_bStrikethrough = src.m_font_bStrikethrough; - m_logfont_charset = src.m_logfont_charset; - - memset(m_face_name, 0, sizeof(m_face_name)); - for (int i = 0; i < ON_Font::face_name_capacity && 0 != src.m_face_name[i]; i++) + if ( 0 == ((ON__UINT_PTR)(&src)) ) { - m_face_name[i] = src.m_face_name[i]; + 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); - m_font_description = src.m_font_description; - - m_apple_font_name = src.m_apple_font_name; - if (0 == m_runtime_serial_number) + if ( bThisIsManagedFont ) { - // destination font is not managed - m_font_glyph_cache = src.m_font_glyph_cache; + 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_point_size = src.m_point_size; + 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() { - memset(m_face_name, 0, sizeof(m_face_name)); + /* + //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( - unsigned char managed_status, + ON_Font::FontType font_type, const ON_Font& src ) - : m_runtime_serial_number((1 == managed_status || 2 == managed_status) ? (++ON_Font::__runtime_serial_number_generator) : 0) + : 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) { - CopyHelper(src); + // 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(0) + : 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) { - memset(m_face_name, 0, sizeof(m_face_name)); - CopyHelper(src); + // (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) @@ -1115,7 +5083,7 @@ ON_Font& ON_Font::operator=(const ON_Font& src) } else { - CopyHelper(src); + Internal_CopyFrom(src); } } return *this; @@ -1123,7 +5091,7 @@ ON_Font& ON_Font::operator=(const ON_Font& src) bool ON_Font::SetFontCharacteristics( - const wchar_t* face_name, + const wchar_t* gdi_logfont_name, bool bBold, bool bItalic, bool bUnderlined, @@ -1132,7 +5100,7 @@ bool ON_Font::SetFontCharacteristics( { return SetFontCharacteristics( 0.0, - face_name, + gdi_logfont_name, bBold, bItalic, bUnderlined, @@ -1142,25 +5110,27 @@ bool ON_Font::SetFontCharacteristics( bool ON_Font::SetFontCharacteristics( double point_size, - const wchar_t * face_name, + const wchar_t * gdi_logfont_name, bool bBold, bool bItalic, bool bUnderlined, bool bStrikethrough ) { - if (nullptr == face_name || 0 == face_name[0] ) - face_name = ON_Font::Default.m_face_name; + 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, - face_name, + 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(face_name) + ON_Font::WindowsLogfontCharSetFromFaceName(gdi_logfont_name) ); } @@ -1172,7 +5142,7 @@ bool ON_Font::IsValidFaceName( return false; int i = 0; - while (i < ON_Font::face_name_capacity && 0 != face_name[i]) + while (i < 32 && 0 != face_name[i]) { if (face_name[i] < ON_wString::Space ) return false; @@ -1187,6 +5157,9 @@ bool ON_Font::IsValidFaceName( // 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; } @@ -1223,6 +5196,72 @@ ON_Font::Weight ON_Font::FontWeightFromUnsigned( 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 @@ -1253,6 +5292,127 @@ ON_Font::Style ON_Font::FontStyleFromUnsigned( return ON_Font::Style::Upright; } +bool ON_Font::IsBoldWeight( + ON_Font::Weight weight +) +{ + return (static_cast(weight) >= static_cast(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 ) @@ -1319,10 +5479,14 @@ unsigned int ON_Font::Internal_FontCharacteristicsAsUnsigned( return u; } - -bool ON_Font::Internal_SetFontCharacteristicsFromUnsigned( - unsigned int font_characteristics_as_unsigned - ) +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; @@ -1339,45 +5503,63 @@ bool ON_Font::Internal_SetFontCharacteristicsFromUnsigned( u /= 2; // extract new information below this line - ON_Font::Weight font_weight = (1U == u_one && u_font_weight > 0) ? ON_Font::FontWeightFromUnsigned(u_font_weight) : ON_Font::Default.FontWeight(); - ON_Font::Style font_style = (1U == u_one) ? ON_Font::FontStyleFromUnsigned(u_font_style) : ON_Font::Default.FontStyle(); - ON_Font::Stretch font_stretch = (1U == u_one) ? ON_Font::FontStretchFromUnsigned(u_font_stretch) : ON_Font::Default.FontStretch(); - bool bUnderlined = (1U == u_one) ? (1 == u_bUnderlined) : ON_Font::Default.IsUnderlined(); - bool bStrikethrough = (1U == u_one) ? (1 == u_bStrikethrough) : ON_Font::Default.IsStrikethrough(); - - return SetFontCharacteristics( - m_face_name, + font_weight = (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 bIgnoreFaceNameOrdinalCase + bool bIgnoreNameOrdinalCase ) const { unsigned int u = FontCharacteristicsAsUnsigned(); - wchar_t mapped_face_name[ON_Font::face_name_capacity+1]; - int element_count = 0; - while (element_count < ON_Font::face_name_capacity && 0 != m_face_name[element_count] ) - element_count++; - const wchar_t* face_name = m_face_name; - if (bIgnoreFaceNameOrdinalCase) - { - ON_wString::MapStringOrdinal( - ON_StringMapOrdinalType::MinimumOrdinal, - m_face_name, - element_count, - mapped_face_name, - ON_Font::face_name_capacity - ); - face_name = mapped_face_name; - } + + 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,element_count*sizeof(face_name[0]),face_name); + hash = ON_CRC32(hash,windows_logfont_name_crc.Length()*sizeof(wchar_t),static_cast(windows_logfont_name_crc)); #if defined(ON_RUNTIME_WIN) if ( m_point_size > 0.0 ) @@ -1388,7 +5570,7 @@ unsigned int ON_Font::CRC32( } bool ON_Font::SetFontCharacteristics( - const wchar_t* face_name, + const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, @@ -1398,7 +5580,7 @@ bool ON_Font::SetFontCharacteristics( { return SetFontCharacteristics( 0.0, - face_name, + gdi_logfont_name, font_weight, font_style, font_stretch, @@ -1409,7 +5591,7 @@ bool ON_Font::SetFontCharacteristics( bool ON_Font::SetFontCharacteristics( double point_size, - const wchar_t* face_name, + const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, @@ -1417,13 +5599,13 @@ bool ON_Font::SetFontCharacteristics( bool bStrikethrough ) { - const unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(face_name); + const unsigned char logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(gdi_logfont_name); double linefeed_ratio = ON_FontMetrics::DefaultLineFeedRatio; return SetFontCharacteristics( point_size, - face_name, + gdi_logfont_name, font_weight, font_style, font_stretch, @@ -1435,7 +5617,7 @@ bool ON_Font::SetFontCharacteristics( } bool ON_Font::SetFontCharacteristics( - const wchar_t* face_name, + const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, @@ -1447,7 +5629,7 @@ bool ON_Font::SetFontCharacteristics( { return SetFontCharacteristics( 0.0, - face_name, + gdi_logfont_name, font_weight, font_style, font_stretch, @@ -1460,7 +5642,7 @@ bool ON_Font::SetFontCharacteristics( bool ON_Font::SetFontCharacteristics( double point_size, - const wchar_t* face_name, + const wchar_t* gdi_logfont_name, ON_Font::Weight font_weight, ON_Font::Style font_style, ON_Font::Stretch font_stretch, @@ -1470,19 +5652,27 @@ bool ON_Font::SetFontCharacteristics( unsigned int logfont_charset ) { - if (this == &ON_Font::Default) + if ( false == ON_FONT_MODIFICATION_PERMITTED ) return false; - if (false == ON_Font::IsValidFaceName(face_name)) + // 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(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 new_characteristics(ON_Font::Unset); - for (int i = 0; i < ON_Font::face_name_capacity && 0 != face_name[i]; i++) - new_characteristics.m_face_name[i] = face_name[i]; + new_characteristics.m_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(font_weight)); new_characteristics.m_point_size @@ -1496,122 +5686,442 @@ bool ON_Font::SetFontCharacteristics( new_characteristics.m_font_bUnderlined = bUnderlined ? true : false; new_characteristics.m_font_bStrikethrough = bStrikethrough; - if (ON_Font::logfont_symbol_charset == logfont_charset) + 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.m_face_name); + logfont_charset = ON_Font::WindowsLogfontCharSetFromFaceName(new_characteristics.WindowsLogfontName()); + } + else + { + logfont_charset = ON_Font::WindowsConstants::logfont_default_charset; } - new_characteristics.m_logfont_charset = (unsigned char)logfont_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 - 0 != memcmp(m_face_name,new_characteristics.m_face_name,sizeof(m_face_name)) + 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() ) { - if ( false == ON_FONT_MODIFICATION_PERMITTED ) - return false; - CopyHelper(new_characteristics); + Internal_CopyFrom(new_characteristics); if (0 == m_runtime_serial_number) { // destination font is not managed m_font_glyph_cache = nullptr; } - Internal_SetFontDescription(); + + 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 -