From 166901420e5b36e7eaaba62f1d9b8f1d5419dbf3 Mon Sep 17 00:00:00 2001 From: Bozo the Builder Date: Mon, 15 Jun 2026 11:01:35 -0700 Subject: [PATCH] Sync changes from upstream repository --- opennurbs_annotationbase.h | 6 + opennurbs_font.cpp | 4 +- opennurbs_instance.cpp | 15 + opennurbs_instance.h | 8 + opennurbs_internal_V2_annotation.cpp | 21 ++ opennurbs_mesh_ngon.cpp | 2 +- opennurbs_pointcloud.cpp | 6 + opennurbs_public_version.h | 22 +- opennurbs_subd.h | 2 + opennurbs_text.cpp | 60 +++- opennurbs_textrun.cpp | 446 +++++++++++++++------------ 11 files changed, 374 insertions(+), 218 deletions(-) diff --git a/opennurbs_annotationbase.h b/opennurbs_annotationbase.h index 09a47ef2..c560b646 100644 --- a/opennurbs_annotationbase.h +++ b/opennurbs_annotationbase.h @@ -960,6 +960,12 @@ public: //--------------------------- // ON_Object overrides + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + bool IsValid( class ON_TextLog* text_log = nullptr ) const override; /* diff --git a/opennurbs_font.cpp b/opennurbs_font.cpp index e4944e7e..d255ac03 100644 --- a/opennurbs_font.cpp +++ b/opennurbs_font.cpp @@ -5554,7 +5554,7 @@ const ON_ClassArray< ON_FontFaceQuartet >& ON_FontList::QuartetList() const && ssw_dex.j >= 0 && ssw_dex.j < 2 && ssw_dex.k >= 1 && ssw_dex.k < max_weight_dex ) - ? fonts_by_ssw[ssw_dex.i][ssw_dex.k][ssw_dex.k] + ? fonts_by_ssw[ssw_dex.i][ssw_dex.j][ssw_dex.k] : nullptr; if (nullptr != cleanf) { @@ -7638,7 +7638,7 @@ const ON_wString ON_Font::FakeWindowsLogfontNameFromFamilyAndPostScriptNames( Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Heavy", L"Avenir Heavy", ON_FontFaceQuartet::Member::Regular), Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-HeavyOblique", L"Avenir Heavy", ON_FontFaceQuartet::Member::Italic), Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-Black", L"Avenir Black", ON_FontFaceQuartet::Member::Regular), - Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-BlackOblique", L"Avenir-Black", ON_FontFaceQuartet::Member::Italic), + Internal_FakeWindowsLogfontName(L"Avenir", L"Avenir-BlackOblique", L"Avenir Black", ON_FontFaceQuartet::Member::Italic), Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-UltraLight", L"Avenir Next Ultralight", ON_FontFaceQuartet::Member::Regular), Internal_FakeWindowsLogfontName(L"Avenir Next", L"AvenirNext-UltraLightItalic", L"Avenir Next Ultralight", ON_FontFaceQuartet::Member::Italic), diff --git a/opennurbs_instance.cpp b/opennurbs_instance.cpp index 22666c51..deb6965f 100644 --- a/opennurbs_instance.cpp +++ b/opennurbs_instance.cpp @@ -1761,6 +1761,21 @@ bool ON_InstanceRef::IsValid( ON_TextLog* text_log ) const return true; } +unsigned int ON_InstanceRef::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Geometry)); + return sz; +} + +ON__UINT32 ON_InstanceRef::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = ON_CRC32(current_remainder, sizeof(m_instance_definition_uuid), &m_instance_definition_uuid); + current_remainder = ON_CRC32(current_remainder, sizeof(m_xform), &m_xform); + current_remainder = ON_CRC32(current_remainder, sizeof(m_bbox), &m_bbox); + return current_remainder; +} + bool ON_InstanceRef::Write( ON_BinaryArchive& binary_archive ) const diff --git a/opennurbs_instance.h b/opennurbs_instance.h index f4271587..c1ef7ed0 100644 --- a/opennurbs_instance.h +++ b/opennurbs_instance.h @@ -657,14 +657,22 @@ public: // virtual ON_Object overrides // bool IsValid( class ON_TextLog* text_log = nullptr ) const override; + bool Write( ON_BinaryArchive& binary_archive ) const override; + bool Read( ON_BinaryArchive& binary_archive ) override; ON::object_type ObjectType() const override; + // virtual ON_Object::SizeOf override + unsigned int SizeOf() const override; + + // virtual ON_Object::DataCRC override + ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override; + ///////////////////////////////////////////////////////////// // // virtual ON_Geometry overrides diff --git a/opennurbs_internal_V2_annotation.cpp b/opennurbs_internal_V2_annotation.cpp index 29b03037..b2ff33d4 100644 --- a/opennurbs_internal_V2_annotation.cpp +++ b/opennurbs_internal_V2_annotation.cpp @@ -5645,6 +5645,27 @@ void ON_TextDot::EmergencyDestroy() m_display_bits = 0; } +unsigned int ON_TextDot::SizeOf() const +{ + unsigned int sz = ON_Geometry::SizeOf(); + sz += (sizeof(*this) - sizeof(ON_Geometry)); + sz += m_primary_text.SizeOf(); + sz += m_secondary_text.SizeOf(); + sz += m_font_face.SizeOf(); + return sz; +} + +ON__UINT32 ON_TextDot::DataCRC(ON__UINT32 current_remainder) const +{ + current_remainder = m_center_point.DataCRC(current_remainder); + current_remainder = m_primary_text.DataCRC(current_remainder); + current_remainder = m_secondary_text.DataCRC(current_remainder); + current_remainder = m_font_face.DataCRC(current_remainder); + current_remainder = ON_CRC32(current_remainder, sizeof(m_display_bits), &m_display_bits); + current_remainder = ON_CRC32(current_remainder, sizeof(m_height_in_points), &m_height_in_points); + return current_remainder; +} + bool ON_TextDot::IsValid( ON_TextLog* text_log ) const diff --git a/opennurbs_mesh_ngon.cpp b/opennurbs_mesh_ngon.cpp index 9207b215..e2f0ac6c 100644 --- a/opennurbs_mesh_ngon.cpp +++ b/opennurbs_mesh_ngon.cpp @@ -5241,10 +5241,10 @@ ON_Plane ON_Plane::FromPointList( // ON_3dVector N(ON_3dVector::ZeroVector); ON_3dVector X(ON_3dVector::UnsetVector); + const unsigned int index_0123[4] = { 0,1,2,3 }; if ( point_index_count <= 4 ) { // use "standard" face normal for quads and triangles. - const unsigned int index_0123[4] = {0,1,2,3}; if ( 0 == point_index_list ) point_index_list = index_0123; Pi0 = point_list[point_index_list[point_stride*(point_count-1)]]; diff --git a/opennurbs_pointcloud.cpp b/opennurbs_pointcloud.cpp index ab3b33c8..681df0c4 100644 --- a/opennurbs_pointcloud.cpp +++ b/opennurbs_pointcloud.cpp @@ -132,6 +132,7 @@ void ON_PointCloud::Dump( ON_TextLog& dump ) const const bool bHasNormals = HasPointNormals(); const bool bHasColors = HasPointColors(); const bool bHasHiddenPoints = (HiddenPointCount() > 0); + const bool bHasPointValues = HasPointValues(); const int point_count = m_P.Count(); dump.Print("ON_PointCloud: %d points\n",point_count); dump.PushIndent(); @@ -156,6 +157,11 @@ void ON_PointCloud::Dump( ON_TextLog& dump ) const dump.Print(", color = "); dump.PrintRGB(m_C[i]); } + if (bHasPointValues) // 20-Jan-2026 show point value + { + dump.Print(", value = "); + dump.Print(m_V[i]); + } if (bHasHiddenPoints && m_H[i]) { dump.Print(" (hidden)"); diff --git a/opennurbs_public_version.h b/opennurbs_public_version.h index 4664f9a5..7322ef81 100644 --- a/opennurbs_public_version.h +++ b/opennurbs_public_version.h @@ -6,7 +6,7 @@ // To update version numbers, edit ..\build\build_dates.msbuild #define RMA_VERSION_MAJOR 8 -#define RMA_VERSION_MINOR 27 +#define RMA_VERSION_MINOR 29 //////////////////////////////////////////////////////////////// // @@ -14,10 +14,10 @@ // first step in each build. // #define RMA_VERSION_YEAR 2026 -#define RMA_VERSION_MONTH 1 -#define RMA_VERSION_DATE 19 -#define RMA_VERSION_HOUR 16 -#define RMA_VERSION_MINUTE 2 +#define RMA_VERSION_MONTH 3 +#define RMA_VERSION_DATE 4 +#define RMA_VERSION_HOUR 11 +#define RMA_VERSION_MINUTE 0 //////////////////////////////////////////////////////////////// // @@ -35,8 +35,8 @@ // 3 = build system release build #define RMA_VERSION_BRANCH 0 -#define VERSION_WITH_COMMAS 8,27,26019,16020 -#define VERSION_WITH_PERIODS 8.27.26019.16020 +#define VERSION_WITH_COMMAS 8,29,26063,11000 +#define VERSION_WITH_PERIODS 8.29.26063.11000 #define COPYRIGHT "Copyright (C) 1993-2026, Robert McNeel & Associates. All Rights Reserved." #define SPECIAL_BUILD_DESCRIPTION "Public OpenNURBS C++ 3dm file IO library." @@ -44,11 +44,11 @@ #define RMA_VERSION_NUMBER_MAJOR_WSTRING L"8" #define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"7" -#define RMA_VERSION_NUMBER_SR_STRING "SR27" -#define RMA_VERSION_NUMBER_SR_WSTRING L"SR27" +#define RMA_VERSION_NUMBER_SR_STRING "SR29" +#define RMA_VERSION_NUMBER_SR_WSTRING L"SR29" -#define RMA_VERSION_WITH_PERIODS_STRING "8.27.26019.16020" -#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.27.26019.16020" +#define RMA_VERSION_WITH_PERIODS_STRING "8.29.26063.11000" +#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.29.26063.11000" diff --git a/opennurbs_subd.h b/opennurbs_subd.h index f329e63d..6fd02df2 100644 --- a/opennurbs_subd.h +++ b/opennurbs_subd.h @@ -46,6 +46,7 @@ enum class ON_SubDGetControlNetMeshPriority : unsigned char }; +#pragma region RH_C_SHARED_ENUM [ON_SubDTextureCoordinateType] [Rhino.Geometry.SubDTextureCoordinateType] [byte] /// /// ON_SubDTextureCoordinateType identifies the way ON_SubDMeshFragment texture coordinates are set from an ON_SubDFace. /// @@ -91,6 +92,7 @@ enum class ON_SubDTextureCoordinateType : unsigned char /// FromMapping = 7, }; +#pragma endregion #pragma region RH_C_SHARED_ENUM [ON_SubDVertexTag] [Rhino.Geometry.SubDVertexTag] [byte] /// diff --git a/opennurbs_text.cpp b/opennurbs_text.cpp index a45ec660..05d75d15 100644 --- a/opennurbs_text.cpp +++ b/opennurbs_text.cpp @@ -2532,9 +2532,22 @@ bool ON_TextContent::FormatLength( wchar_t decimal_char, ON_wString & output) { + // Record length before appending so we only replace periods in the newly added portion + const int length_before = output.Length(); bool rc = ON_NumberFormatter::FormatLength( distance, output_lengthdisplay, round_off, resolution, zero_suppress, bracket_fractions, output); if (rc && ON_wString::DecimalAsPeriod != decimal_char) - output.Replace(ON_wString::DecimalAsPeriod, decimal_char); + { + // Only replace periods in the newly appended portion, not in any pre-existing text RH-83506 + wchar_t* str = output.Array(); + if (nullptr != str) + { + for (int i = length_before; i < output.Length(); i++) + { + if (str[i] == ON_wString::DecimalAsPeriod) + str[i] = decimal_char; + } + } + } return rc; } @@ -2552,9 +2565,22 @@ bool ON_TextContent::FormatAngleStringDMS( int resolution, ON_wString& formatted_string) { + // Record length before appending so we only replace periods in the newly added portion + const int length_before = formatted_string.Length(); bool rc = ON_NumberFormatter::FormatAngleStringDMS(angle_degrees, resolution, formatted_string); if (rc && ON_wString::DecimalAsPeriod != decimal_char) - formatted_string.Replace(ON_wString::DecimalAsPeriod, decimal_char); + { + // Only replace periods in the newly appended portion, not in any pre-existing text RH-83506 + wchar_t* str = formatted_string.Array(); + if (nullptr != str) + { + for (int i = length_before; i < formatted_string.Length(); i++) + { + if (str[i] == ON_wString::DecimalAsPeriod) + str[i] = decimal_char; + } + } + } return rc; } @@ -2566,9 +2592,22 @@ bool ON_TextContent::FormatAngleStringDecimal( wchar_t decimal_char, ON_wString& formatted_string) { + // Record length before appending so we only replace periods in the newly added portion + const int length_before = formatted_string.Length(); bool rc = ON_NumberFormatter::FormatAngleStringDecimal(angle_radians, resolution, roundoff, zero_suppression, formatted_string); if (rc && ON_wString::DecimalAsPeriod != decimal_char) - formatted_string.Replace(ON_wString::DecimalAsPeriod, decimal_char); + { + // Only replace periods in the newly appended portion, not in any pre-existing text RH-83506 + wchar_t* str = formatted_string.Array(); + if (nullptr != str) + { + for (int i = length_before; i < formatted_string.Length(); i++) + { + if (str[i] == ON_wString::DecimalAsPeriod) + str[i] = decimal_char; + } + } + } return rc; } @@ -2631,6 +2670,8 @@ bool ON_TextContent::FormatAreaOrVolume( ON_DimStyle::OBSOLETE_length_format output_format = ON_DimStyle::OBSOLETE_length_format::Decimal; + // Record length before appending so we only replace periods in the newly added portion + const int length_before = formatted_string.Length(); bool rc = ON_NumberFormatter::FormatNumber( value, output_format, @@ -2641,7 +2682,18 @@ bool ON_TextContent::FormatAreaOrVolume( formatted_string); if (rc && ON_wString::DecimalAsPeriod != decimal_char) - formatted_string.Replace(ON_wString::DecimalAsPeriod, decimal_char); + { + // Only replace periods in the newly appended portion, not in any pre-existing text RH-83506 + wchar_t* str = formatted_string.Array(); + if (nullptr != str) + { + for (int i = length_before; i < formatted_string.Length(); i++) + { + if (str[i] == ON_wString::DecimalAsPeriod) + str[i] = decimal_char; + } + } + } return rc; } diff --git a/opennurbs_textrun.cpp b/opennurbs_textrun.cpp index 581893e2..86141c20 100644 --- a/opennurbs_textrun.cpp +++ b/opennurbs_textrun.cpp @@ -1048,6 +1048,250 @@ void ON_TextRun::SetUnicodeString(ON__UINT32*& dest, size_t count, const ON__UIN } } +// 2026-02-10 - kike@mcneel.com : See RH-91129 +// ON_TextRun::WrapTextRun should be iterative instead of recursive. +// But by now and to fix the issue the recursion level is now 128 +// And the recursive function ON_TextWrapper::WrapTextRun is lighter on stack than before. +// Also the glyph extraction is done just once, so should be faster than before. +class ON_TextWrapper +{ + const int max_recursion_level = 128; + + const ON_TextRun& text_run; + ON_TextRunArray& newruns; + const wchar_t* display_string; + const size_t wcscount; + + const ON_Font* font; + double height_scale; + ON_SimpleArray glyph_list; + ON_TextBox text_box; + +public: + ON_TextWrapper(const ON_TextRun& run, ON_TextRunArray& runs) : + text_run(run), + newruns(runs), + display_string(run.DisplayString()), + wcscount(nullptr != display_string ? wcslen(display_string) : 0) + { } + + int WrapText( + int call_count, // recursion depth + int start_char_offset, // char offset in cp array + double wrapwidth, // max linewidth + double& y_offset, // y offset from input run from previously added soft returns + double& linewidth // linewidth so far + ) + { + font = text_run.Font(); + if (nullptr == font) + return 0; + + height_scale = text_run.HeightScale(font); // Font units to world units + ON_FontGlyph::GetGlyphList(display_string, font, ON_NextLine, glyph_list, text_box); + { + const ON_FontGlyph* Aglyph = font->CodePointGlyph((ON__UINT32)L'A'); + if (nullptr == Aglyph) + return 0; + + const ON_TextBox& Aglyph_box = Aglyph->GlyphBox(); + double Awidth = Aglyph_box.m_advance.i * height_scale; + // height scale is ~1e-3 so floor() will pretty much always make this value 0 + // when text height is much less than 1, so this is a test for a NAN result + if (floor(Awidth) < 0.0) + { + ON_ERROR("Font height scale * width of 'A' is less than 0\n"); + return 0; + } + if (!(Awidth > 0.0 && wrapwidth >= Awidth)) + return 0; + } + + return WrapTextRun(call_count, start_char_offset, wrapwidth, y_offset, linewidth); + } + +private: + int WrapTextRun( + int call_count, // recursion depth + int start_char_offset, // char offset in cp array + double wrapwidth, // max linewidth + double& y_offset, // y offset from input run from previously added soft returns + double& linewidth // linewidth so far + ) + { + int new_count = 0; + + if (max_recursion_level < call_count) + { + ON_ERROR("WrapTextRun: Recursion too deep."); + return 0; + } + if (0 > start_char_offset) + { + ON_ERROR("WrapTextRun: String start offset < 0."); + start_char_offset = 0; + } + if (0.0 > linewidth) + { + ON_ERROR("WrapTextRun: Linewidtht < 0."); + linewidth = 0.0; + } + + wchar_t* temp_display_str = (wchar_t*)onmalloc((wcscount + 1) * sizeof(wchar_t)); + double runwidth = 0.0; // run width without trailing spaces + { +#pragma region Run Width + //double runwidth0 = 0.0; // run width including trailing spaces + if (0 == start_char_offset) // using the whole run + { + //runwidth = Advance().x; + runwidth = text_box.m_advance.i * height_scale; + } + else // Part of the run has already been picked off and added to the previous line + { + // Find width of remaining characters + for (int ci = start_char_offset; ci < wcscount && ci < glyph_list.Count(); ci++) + { + const ON_FontGlyph* gi = glyph_list[ci]; + + if (nullptr != gi) + { + const ON_TextBox& glyph_box = gi->GlyphBox(); + double charwidth = glyph_box.m_advance.i * height_scale; + runwidth += charwidth; + } + } + } + + if (0.0 > runwidth) + runwidth = 0.0; +#pragma endregion Run Width + +#pragma region Whole Run + if (runwidth + linewidth <= wrapwidth || 2 > wcscount) + { + // Adding this entire run won't go past wrap width + // or the run has only 0 or 1 character and can't be wrapped + ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); + if (nullptr != newrun) + { + *newrun = text_run; + if (text_run.Type() == ON_TextRun::RunType::kNewline || + text_run.Type() == ON_TextRun::RunType::kParagraph || + text_run.Type() == ON_TextRun::RunType::kSoftreturn) + linewidth = 0.0; + else + { + if (0 != start_char_offset) + { + wcsncpy(temp_display_str, display_string + start_char_offset, wcscount - start_char_offset); + temp_display_str[wcscount - start_char_offset] = 0; + newrun->SetDisplayString(temp_display_str); + } + linewidth += runwidth; + } + newruns.AppendRun(newrun); + onfree(temp_display_str); + return 1; // 1 new run was added + } + } +#pragma endregion Whole Run + } + + // Find what part of the run will fit + bool found_space = false; + int last_space = -1; + int run_length = 0; + double curwidth = 0.0; + double linefeedheight = font->FontMetrics().LineSpace() * height_scale; + + int sp_count = 0; + for (int ci = start_char_offset; ci < (int)wcscount; ci++) + { + if ((ci + 1) < wcscount && + display_string[ci] >= 0xD800 && display_string[ci] < 0xDC00 && + display_string[ci + 1] >= 0xDC00 && display_string[ci + 1] < 0xE000) + { + sp_count++; + continue; + } + + int gi = ci - sp_count; + if (gi >= glyph_list.Count()) + break; + + const ON_FontGlyph* glyph = glyph_list[gi]; + if (nullptr != glyph) + { + const ON_TextBox& glyph_box = glyph->GlyphBox(); + curwidth += glyph_box.m_advance.i * height_scale; + run_length++; + + if (linewidth + curwidth > wrapwidth) // reached wrapping width + { + if (found_space) // store run up to last space + run_length = last_space - start_char_offset + 1; + else if (0.0 < linewidth) // A line is already started + run_length = 0; + else // no space yet - store run up to this char position + run_length = ci - start_char_offset; + + if (0 < run_length) + { + ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); // make a new run + if (nullptr != newrun) + { + *newrun = text_run; + wcsncpy(temp_display_str, display_string + start_char_offset, run_length); + temp_display_str[run_length] = 0; + newrun->SetDisplayString(temp_display_str); + newrun->SetOffset(ON_2dVector(0.0, y_offset + text_run.Offset().y)); + newruns.AppendRun(newrun); + } + } + // add a soft return + ON_TextRun* lfrun = ON_TextRun::GetManagedTextRun(); + if (nullptr != lfrun) + { + lfrun->SetFont(font); + lfrun->SetType(ON_TextRun::RunType::kSoftreturn); + lfrun->SetTextHeight(text_run.TextHeight()); + newruns.AppendRun(lfrun); + + // Starting a new line now + linewidth = 0.0; + curwidth = 0.0; + y_offset -= linefeedheight; + } + + int wrapcount = WrapTextRun(call_count + 1, run_length + start_char_offset, wrapwidth, y_offset, linewidth); + + onfree(temp_display_str); + return new_count + wrapcount; + } + if (iswspace(display_string[ci])) + { + found_space = true; + last_space = ci; + } + } + } + + ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); // make a new run + if (nullptr != newrun) + { + *newrun = text_run; + wcsncpy(temp_display_str, display_string + start_char_offset, run_length); + temp_display_str[run_length] = 0; + newrun->SetOffset(ON_2dVector(0.0, y_offset + text_run.Offset().y)); + newruns.AppendRun(newrun); + new_count += 1; + } + onfree(temp_display_str); + return new_count; + } +}; + int ON_TextRun::WrapTextRun( int call_count, // recursion depth int start_char_offset, // char offset in cp array @@ -1057,206 +1301,8 @@ int ON_TextRun::WrapTextRun( ON_TextRunArray& newruns// new runs made by wrapping ) const { - int new_count = 0; - - if (500 < call_count) - { - ON_ERROR("WrapTextRun: Recursion too deep."); - return 0; - } - if (0 > start_char_offset) - { - ON_ERROR("WrapTextRun: String start offset < 0."); - start_char_offset = 0; - } - - if (0.0 > linewidth) - { - ON_ERROR("WrapTextRun: Linewidtht < 0."); - linewidth = 0.0; - } - - const wchar_t* display_string = DisplayString(); - - size_t wcscount = 0; - if (nullptr != display_string) - wcscount = wcslen(display_string); - - const ON_Font* font = Font(); - if (nullptr == font) - return 0; - double height_scale = HeightScale(font); // Font units to world units - - ON_SimpleArray< const ON_FontGlyph*> glyph_list; - ON_TextBox text_box; - - ON_FontGlyph::GetGlyphList(display_string, font, ON_NextLine, glyph_list, text_box); - int glyph_count = glyph_list.Count(); - - const ON_FontGlyph* Aglyph = font->CodePointGlyph((ON__UINT32)L'A'); - if (nullptr == Aglyph) - return 0; - const ON_TextBox Aglyph_box = Aglyph->GlyphBox(); - double Awidth = Aglyph_box.m_advance.i * height_scale; - // height scale is ~1e-3 so floor() will pretty much always make this value 0 - // when text height is much less than 1, so this is a test for a NAN result - if (floor(Awidth) < 0.0) - { - ON_ERROR("Font height scale * width of 'A' is less than 0\n"); - return 0; - } - if (!(Awidth > 0.0 && wrapwidth >= Awidth)) - return 0; - -#pragma region Run Width - double runwidth = 0.0; // run width without trailing spaces - //double runwidth0 = 0.0; // run width including trailing spaces - if (0 == start_char_offset) // using the whole run - { - //runwidth = Advance().x; - runwidth = text_box.m_advance.i * height_scale; - } - else // Part of the run has already been picked off and added to the previous line - { - // Find width of remaining characters - for (int ci = start_char_offset; ci < wcscount && ci < glyph_count; ci++) - { - const ON_FontGlyph* gi = glyph_list[ci]; - - if (nullptr != gi) - { - const ON_TextBox glyph_box = gi->GlyphBox(); - double charwidth = glyph_box.m_advance.i * height_scale; - runwidth += charwidth; - } - } - } - - if (0.0 > runwidth) - runwidth = 0.0; -#pragma endregion Run Width - -#pragma region Whole Run - wchar_t* temp_display_str = (wchar_t*)onmalloc((wcscount + 1) * sizeof(wchar_t)); - if (runwidth + linewidth <= wrapwidth || 2 > wcscount) - { - // Adding this entire run won't go past wrap width - // or the run has only 0 or 1 character and can't be wrapped - ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); - if (nullptr != newrun) - { - *newrun = *this; - if (Type() == ON_TextRun::RunType::kNewline || - Type() == ON_TextRun::RunType::kParagraph || - Type() == ON_TextRun::RunType::kSoftreturn) - linewidth = 0.0; - else - { - if (0 != start_char_offset) - { - wcsncpy(temp_display_str, display_string + start_char_offset, wcscount - start_char_offset); - temp_display_str[wcscount - start_char_offset] = 0; - newrun->SetDisplayString(temp_display_str); - } - linewidth += runwidth; - } - newruns.AppendRun(newrun); - onfree(temp_display_str); - return 1; // 1 new run was added - } - } -#pragma endregion Whole Run - - // Find what part of the run will fit - bool found_space = false; - int last_space = -1; - int run_length = 0; - double curwidth = 0.0; - double linefeedheight = font->FontMetrics().LineSpace() * height_scale; - - int sp_count = 0; - for (int ci = start_char_offset; ci < (int)wcscount; ci++) - { - if ((ci + 1) < wcscount && - display_string[ci] >= 0xD800 && display_string[ci] < 0xDC00 && - display_string[ci + 1] >= 0xDC00 && display_string[ci + 1] < 0xE000) - { - sp_count++; - continue; - } - - int gi = ci - sp_count; - if (gi >= glyph_count) - break; - - const ON_FontGlyph* glyph = glyph_list[gi]; - if (nullptr != glyph) - { - const ON_TextBox glyph_box = glyph->GlyphBox(); - curwidth += glyph_box.m_advance.i * height_scale; - run_length++; - - if (linewidth + curwidth > wrapwidth) // reached wrapping width - { - if (found_space) // store run up to last space - run_length = last_space - start_char_offset + 1; - else if (0.0 < linewidth) // A line is already started - run_length = 0; - else // no space yet - store run up to this char position - run_length = ci - start_char_offset; - - if (0 < run_length) - { - ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); // make a new run - if (nullptr != newrun) - { - *newrun = *this; - wcsncpy(temp_display_str, display_string + start_char_offset, run_length); - temp_display_str[run_length] = 0; - newrun->SetDisplayString(temp_display_str); - newrun->SetOffset(ON_2dVector(0.0, y_offset + Offset().y)); - newruns.AppendRun(newrun); - } - } - // add a soft return - ON_TextRun* lfrun = ON_TextRun::GetManagedTextRun(); - if (nullptr != lfrun) - { - lfrun->SetFont(Font()); - lfrun->SetType(ON_TextRun::RunType::kSoftreturn); - lfrun->SetTextHeight(this->TextHeight()); - newruns.AppendRun(lfrun); - - // Starting a new line now - linewidth = 0.0; - curwidth = 0.0; - y_offset -= linefeedheight; - } - - int wrapcount = WrapTextRun(call_count + 1, run_length + start_char_offset, wrapwidth, y_offset, linewidth, newruns); - onfree(temp_display_str); - return new_count + wrapcount; - } - if (iswspace(display_string[ci])) - { - found_space = true; - last_space = ci; - } - } - } - - ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); // make a new run - if (nullptr != newrun) - { - *newrun = *this; - wcsncpy(temp_display_str, display_string + start_char_offset, run_length); - temp_display_str[run_length] = 0; - newrun->SetOffset(ON_2dVector(0.0, y_offset + Offset().y)); - newruns.AppendRun(newrun); - new_count += 1; - } - onfree(temp_display_str); - return new_count; + ON_TextWrapper wrapper(*this, newruns); + return wrapper.WrapText(call_count, start_char_offset, wrapwidth, y_offset, linewidth); } ON_StackedText::StackStyle ON_StackedText::StackStyleFromUnsigned(