Sync changes from upstream repository

This commit is contained in:
Bozo the Builder
2026-04-14 03:39:26 -07:00
parent 5bdffa6848
commit faabf6d9cc
16 changed files with 464 additions and 236 deletions
+6
View File
@@ -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;
/*
+8 -2
View File
@@ -196,7 +196,13 @@ ON_TextContent* ON_Dimension::RebuildDimensionText(
}
else
{
displaytext = displaytext + UserText();
// October 2, 2025 - Tim
// As far as I can tell RebuildDimensionText only gets called with
// expandanglebrackets set to false from the acad export plugin otherwise
// I would be jumpy about making these changes.
// These changes fix https://mcneel.myjetbrains.com/youtrack/issue/RH-89323
displaytext = PlainText();
if (dimstyle->Prefix().IsNotEmpty() || dimstyle->Suffix().IsNotEmpty())
{
int ci = displaytext.Find(L"<>");
@@ -206,7 +212,7 @@ ON_TextContent* ON_Dimension::RebuildDimensionText(
if (displaytext.Length() > ci + 2)
right = displaytext.Right(displaytext.Length() - ci - 2);
displaytext = displaytext.Left(ci);
displaytext = displaytext + dimstyle->Prefix();
displaytext = dimstyle->Prefix() + displaytext;
displaytext = displaytext + L"<>";
displaytext = displaytext + dimstyle->Suffix();
displaytext = displaytext + right;
+2 -2
View File
@@ -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),
+15
View File
@@ -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
+8
View File
@@ -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
+21
View File
@@ -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
+1
View File
@@ -470,6 +470,7 @@ unsigned int ON_Mesh::SizeOf() const
{
unsigned int sz = ON_Geometry::SizeOf();
sz += m_V.SizeOfArray();
sz += m_dV.SizeOfArray();
sz += m_F.SizeOfArray();
sz += m_N.SizeOfArray();
sz += m_FN.SizeOfArray();
+1 -1
View File
@@ -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)]];
+6
View File
@@ -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)");
+12 -12
View File
@@ -6,17 +6,17 @@
// To update version numbers, edit ..\build\build_dates.msbuild
#define RMA_VERSION_MAJOR 8
#define RMA_VERSION_MINOR 26
#define RMA_VERSION_MINOR 29
////////////////////////////////////////////////////////////////
//
// These are set automatically by the build system as the
// first step in each build.
//
#define RMA_VERSION_YEAR 2025
#define RMA_VERSION_MONTH 12
#define RMA_VERSION_DATE 15
#define RMA_VERSION_HOUR 19
#define RMA_VERSION_YEAR 2026
#define RMA_VERSION_MONTH 3
#define RMA_VERSION_DATE 4
#define RMA_VERSION_HOUR 11
#define RMA_VERSION_MINUTE 0
////////////////////////////////////////////////////////////////
@@ -35,20 +35,20 @@
// 3 = build system release build
#define RMA_VERSION_BRANCH 0
#define VERSION_WITH_COMMAS 8,26,25349,19000
#define VERSION_WITH_PERIODS 8.26.25349.19000
#define COPYRIGHT "Copyright (C) 1993-2025, Robert McNeel & Associates. All Rights Reserved."
#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."
#define RMA_VERSION_NUMBER_MAJOR_STRING "8"
#define RMA_VERSION_NUMBER_MAJOR_WSTRING L"8"
#define RMA_PREVIOUS_VERSION_NUMBER_MAJOR_WSTRING L"7"
#define RMA_VERSION_NUMBER_SR_STRING "SR26"
#define RMA_VERSION_NUMBER_SR_WSTRING L"SR26"
#define RMA_VERSION_NUMBER_SR_STRING "SR29"
#define RMA_VERSION_NUMBER_SR_WSTRING L"SR29"
#define RMA_VERSION_WITH_PERIODS_STRING "8.26.25349.19000"
#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.26.25349.19000"
#define RMA_VERSION_WITH_PERIODS_STRING "8.29.26063.11000"
#define RMA_VERSION_WITH_PERIODS_WSTRING L"8.29.26063.11000"
+2 -2
View File
@@ -621,7 +621,7 @@ bool ON_Quaternion::GetYawPitchRoll(double& yaw, double& pitch, double& roll) co
ON_Xform X;
bool rc = GetRotation(X);
if (rc)
rc = GetYawPitchRoll(yaw, pitch, roll);
rc = X.GetYawPitchRoll(yaw, pitch, roll);
return rc;
}
@@ -630,7 +630,7 @@ bool ON_Quaternion::GetEulerZYZ(double& alpha, double& beta, double& gamma) con
ON_Xform X;
bool rc = GetRotation(X);
if (rc)
rc = GetEulerZYZ(alpha, beta, gamma);
rc = X.GetEulerZYZ(alpha, beta, gamma);
return rc;
}
+38
View File
@@ -111,6 +111,7 @@ ON_SectionStyle& ON_SectionStyle::operator=(const ON_SectionStyle& other)
}
return *this;
}
bool ON_SectionStyle::IsValid( ON_TextLog* text_log ) const
{
if (false == ON_ModelComponent::IsValid(text_log))
@@ -134,6 +135,43 @@ bool ON_SectionStyle::IsValid( ON_TextLog* text_log ) const
return true;
}
const ON_SectionStyle* ON_SectionStyle::FromModelComponentRef(
const class ON_ModelComponentReference& model_component_reference,
const ON_SectionStyle* none_return_value
)
{
const ON_SectionStyle* p = ON_SectionStyle::Cast(model_component_reference.ModelComponent());
return (nullptr != p) ? p : none_return_value;
}
bool ON_SectionStyle::UpdateReferencedComponents(
const class ON_ComponentManifest& source_manifest,
const class ON_ComponentManifest& destination_manifest,
const class ON_ManifestMap& manifest_map
)
{
bool rc = true;
// Update hatch pattern index
int hatch_index = HatchIndex();
if (hatch_index >= 0)
{
int destination_hatch_index = -1;
if (manifest_map.GetAndValidateDestinationIndex(ON_ModelComponent::Type::HatchPattern, hatch_index, destination_manifest, &destination_hatch_index))
{
hatch_index = destination_hatch_index;
}
else
{
ON_ERROR("Unable to update hatch pattern reference.");
rc = false;
hatch_index = DefaultSectionStylePrivate.m_hatch_index;
}
SetHatchIndex(hatch_index);
}
return rc;
}
// 12 Aug 2021 S. Baer
// When adding new fields written to 3dm files, always add information to this
// Dump function. Dump is used by the opennurbs file testing framework to
+32 -13
View File
@@ -31,32 +31,52 @@ public:
static const ON_SectionStyle Unset; // index = ON_UNSET_INT_INDEX, id = nil
/*
Description:
Tests that name is set and there is at least one non-zero length segment
Parameters:
model_component_reference - [in]
none_return_value - [in]
value to return if ON_SectionStyle::Cast(model_component_ref.ModelComponent())
is nullptr
Returns:
If ON_SectionStyle::Cast(model_component_ref.ModelComponent()) is not nullptr,
that pointer is returned. Otherwise, none_return_value is returned.
*/
static const ON_SectionStyle* FromModelComponentRef(
const class ON_ModelComponentReference& model_component_reference,
const ON_SectionStyle* none_return_value
);
bool UpdateReferencedComponents(
const class ON_ComponentManifest& source_manifest,
const class ON_ComponentManifest& destination_manifest,
const class ON_ManifestMap& manifest_map
) override;
/*
Description:
Tests that name is set and there is at least one non-zero length segment
*/
bool IsValid( class ON_TextLog* text_log = nullptr ) const override;
void Dump( ON_TextLog& ) const override; // for debugging
/*
Description:
Write to file (serialize definition to binary archive)
Description:
Write to file (serialize definition to binary archive)
*/
bool Write(ON_BinaryArchive&) const override;
/*
Description:
Read from file (restore definition from binary archive)
Description:
Read from file (restore definition from binary archive)
*/
bool Read(ON_BinaryArchive&) override;
/*
Description:
Test only the section style attributes below for equality. Does not
perform any testing of the ON_ModelComponent fields
Parameters:
other: other section style to compare against this
Description:
Test only the section style attributes below for equality. Does not
perform any testing of the ON_ModelComponent fields
Parameters:
other: other section style to compare against this
*/
bool SectionAttributesEqual(const ON_SectionStyle& other) const;
@@ -89,7 +109,6 @@ public:
SectionBackgroundFillMode BackgroundFillMode() const;
void SetBackgroundFillMode(SectionBackgroundFillMode mode);
// Custom background fill color. If unset (default), the object's color or
// material is used for a fill
ON_Color BackgroundFillColor(bool print) const;
+2
View File
@@ -46,6 +46,7 @@ enum class ON_SubDGetControlNetMeshPriority : unsigned char
};
#pragma region RH_C_SHARED_ENUM [ON_SubDTextureCoordinateType] [Rhino.Geometry.SubDTextureCoordinateType] [byte]
/// <summary>
/// ON_SubDTextureCoordinateType identifies the way ON_SubDMeshFragment texture coordinates are set from an ON_SubDFace.
/// </summary>
@@ -91,6 +92,7 @@ enum class ON_SubDTextureCoordinateType : unsigned char
///</summary>
FromMapping = 7,
};
#pragma endregion
#pragma region RH_C_SHARED_ENUM [ON_SubDVertexTag] [Rhino.Geometry.SubDVertexTag] [byte]
/// <summary>
+64 -4
View File
@@ -537,6 +537,14 @@ const ON_wString ON_TextContent::Internal_GetPlainText(bool evaluate_fields, boo
map.j = plaintext.Length();
map.k = (int)wcslen(str);
}
// December 3, 2025 - Tim
// Fix for https://mcneel.myjetbrains.com/youtrack/issue/RH-90830
// Add a space before the fraction if is not the first
// chunk of text.
if (0 < ri && nullptr != wcschr(str, '/'))
plaintext += " ";
plaintext += str;
}
}
@@ -2524,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;
}
@@ -2544,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;
}
@@ -2558,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;
}
@@ -2623,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,
@@ -2633,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;
}
+246 -200
View File
@@ -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<const ON_FontGlyph*> 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(