/* $NoKeywords: $ */ /* // Copyright (c) 1993-2012 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 class ON_CLASS ON_TextRunPool : public ON_FixedSizePool { public: static ON_TextRunPool thePool; private: ON_TextRunPool() { ON_FixedSizePool::Create(sizeof(ON_TextRun), 0, 0); } }; ON_TextRunPool ON_TextRunPool::thePool; ON_TextRun::ON_TextRun(bool bManagedTextRun) : m_managed_status(bManagedTextRun ? 1 : 0) {} ON_TextRun* ON_TextRun::GetManagedTextRun() { void* p = ON_TextRunPool::thePool.AllocateDirtyElement(); ON_TextRun* run = new(p)ON_TextRun(true); return run; } ON_TextRun* ON_TextRun::GetManagedTextRun( const ON_TextRun& src ) { ON_TextRun* run = ON_TextRun::GetManagedTextRun(); *run = src; return run; } /* Description: Returns an ON_TextRun to the pool for reuse. */ bool ON_TextRun::ReturnManagedTextRun( ON_TextRun* run ) { if ( nullptr == run ) return true; if (1 == run->m_managed_status) { if (0 == run->m_active_status) { run->Internal_Destroy(); run->m_active_status = 1; ON_TextRunPool::thePool.ReturnElement(run); return true; } ON_ERROR("Attempt to return a managed run that is not active."); return false; } ON_ERROR("Attempt to return a run that is not managed."); return false; } bool ON_TextRun::IsManagedTextRun() const { return (1 == m_managed_status); } bool ON_TextRun::IsActiveManagedTextRun() const { return (1 == m_managed_status && 0 == m_active_status); } void ON_TextRunArray::InsertRun(int i, ON_TextRun*& run) { ON_SimpleArray< ON_TextRun* >::Insert(i, run); run = nullptr; } void ON_TextRunArray::RemoveRun(int i) { ON_SimpleArray< ON_TextRun* >::Remove(i); } void ON_TextRunArray::AppendRun(ON_TextRun*& run) { ON_TextRun*& new_run = ON_SimpleArray< ON_TextRun* >::AppendNew(); new_run = run; // copy pointer, not data run = 0; // null source pointer } void ON_TextRunArray::Internal_Destroy() { for (int i = 0; i < m_count; i++) { ON_TextRun* text_run = m_a[i]; if (nullptr != text_run) { m_a[i] = nullptr; if (text_run->IsManagedTextRun()) { ON_TextRun::ReturnManagedTextRun(text_run); } else { delete text_run; } } } Empty(); } ON_TextRun*const* ON_TextRunArray::Array() const { return (m_count > 0) ? m_a : nullptr; } const ON_TextRun* ON_TextRunArray::operator[](int i) const { return (i >= 0 && i < m_count) ? m_a[i] : nullptr; } ON_TextRun* ON_TextRunArray::operator[](int i) { return (i >= 0 && i < m_count) ? m_a[i] : nullptr; } int ON_TextRunArray::Count() const { return m_count; } unsigned int ON_TextRunArray::UnsignedCount() const { return (unsigned int)m_count; } void ON_TextRunArray::SetTextHeight(double height) { for (int ri = 0; ri < Count(); ri++) { ON_TextRun* run = m_a[ri]; if (nullptr == run) continue; run->SetTextHeight(height); } } bool ON_TextRunArray::Get2dCorners(ON_2dPoint corners[4]) const { ON_2dPoint minpt(0.0, 0.0), maxpt(0.0, 0.0); bool rc = false; for (int ri = 0; ri < Count(); ri++) { const ON_TextRun* run = m_a[ri]; if (nullptr == run) continue; rc = true; const ON_2dVector& offset = run->Offset(); const ON_BoundingBox run_bbox = run->BoundingBox(); if (minpt.x > run_bbox.m_min.x + offset.x) minpt.x = run_bbox.m_min.x + offset.x; if (minpt.y > run_bbox.m_min.y + offset.y) minpt.y = run_bbox.m_min.y + offset.y; if (maxpt.x < run_bbox.m_max.x + offset.x) maxpt.x = run_bbox.m_max.x + offset.x; if (maxpt.y < run_bbox.m_max.y + offset.y) maxpt.y = run_bbox.m_max.y + offset.y; #if defined(ON_DEBUG) && defined(ON_COMPILER_MSC) rc = rc; // good place for a breakpoint #endif } corners[0].Set(minpt.x, minpt.y); corners[1].Set(maxpt.x, minpt.y); corners[2].Set(maxpt.x, maxpt.y); corners[3].Set(minpt.x, maxpt.y); return rc; } void ON_TextRunArray::Internal_CopyFrom(const ON_TextRunArray& src) { SetCount(0); Reserve(src.m_count); for (int i = 0; i < src.m_count; i++) { const ON_TextRun* src_text_run = src.m_a[i]; if ( nullptr != src_text_run ) AppendNew() = ON_TextRun::GetManagedTextRun(*src_text_run); } } ON_TextRunArray::~ON_TextRunArray() { Internal_Destroy(); } ON_TextRunArray& ON_TextRunArray::operator=(const ON_TextRunArray& src) { if (this != &src) { Internal_Destroy(); Internal_CopyFrom(src); } return *this; } ON_TextRunArray::ON_TextRunArray(const ON_TextRunArray& src) { Internal_CopyFrom(src); } //------------------------------------------------------------ ON_StackedText::~ON_StackedText() { if (nullptr != m_top_run) { ON_TextRun::ReturnManagedTextRun(m_top_run); } if (nullptr != m_bottom_run) { ON_TextRun::ReturnManagedTextRun(m_bottom_run); } m_top_run = nullptr; m_bottom_run = nullptr; m_parent_run = nullptr; } ON_StackedText::ON_StackedText(const ON_StackedText& src) : ON_StackedText() { if (this != &src) *this = src; } ON_StackedText& ON_StackedText::operator=(const ON_StackedText& src) { if (&src != this) { ON_TextRun::ReturnManagedTextRun(m_top_run); ON_TextRun::ReturnManagedTextRun(m_bottom_run); m_top_run = nullptr; m_bottom_run = nullptr; if (nullptr != src.m_top_run) { m_top_run = ON_TextRun::GetManagedTextRun(); *m_top_run = *src.m_top_run; } if (nullptr != src.m_bottom_run) { m_bottom_run = ON_TextRun::GetManagedTextRun(); *m_bottom_run = *src.m_bottom_run; } m_separator = src.m_separator; m_parent_run = nullptr; } return *this; } //bool ON_StackedText::Write( // ON_BinaryArchive& archive // ) const //{ // return WriteNested(0,archive); //} // //bool ON_StackedText::Read( // ON_BinaryArchive& archive // ) //{ // return ReadNested(0,archive); //} // //bool ON_StackedText::WriteNested( // unsigned int nested_depth, // ON_BinaryArchive& archive // ) const //{ // if ( nested_depth > 8 ) // return false; // // if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) // return false; // // nested_depth++; // // bool rc = false; // for (;;) // { // if (!archive.WriteBool(nullptr != m_top_run)) // break; // if (nullptr != m_top_run) // { // if (!m_top_run->WriteNested(nested_depth,archive)) // break; // } // // if (!archive.WriteBool(nullptr != m_bottom_run)) // break; // if (nullptr != m_bottom_run) // { // if (!m_bottom_run->WriteNested(nested_depth,archive)) // break; // } // // unsigned int u = static_cast(m_separator); // if (!archive.WriteInt(u)) // break; // // rc = true; // break; // } // // if (!archive.EndWrite3dmChunk()) // rc = false; // // return rc; //} // //bool ON_StackedText::ReadNested( // unsigned int nested_depth, // ON_BinaryArchive& archive // ) //{ // *this = ON_StackedText::Empty; // if ( nested_depth > 8 ) // return false; // // int major_version = 0; // int minor_version = 0; // if ( !archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) // return false; // // nested_depth++; // // bool rc = false; // for (;;) // { // if (1 != major_version) // { // // This is prototype IO code and increasing major_version is not an error // // until we are sure we got it right. // rc = true; // break; // } // // bool b = false; // if (!archive.ReadBool(&b)) // break; // if (b) // { // m_top_run = ON_TextRun::GetManagedTextRun(); // if (!m_top_run->ReadNested(nested_depth, archive)) // { // ON_TextRun::ReturnManagedTextRun(m_top_run); // m_top_run = nullptr; // break; // } // } // // if (!archive.ReadBool(&b)) // break; // if (b) // { // m_bottom_run = ON_TextRun::GetManagedTextRun(); // if (!m_bottom_run->ReadNested(nested_depth, archive)) // { // ON_TextRun::ReturnManagedTextRun(m_bottom_run); // m_bottom_run = nullptr; // break; // } // } // // unsigned int u = 0; // if (!archive.ReadInt(&u)) // break; // // m_separator = static_cast(u); // // rc = true; // break; // } // // if (!archive.EndRead3dmChunk()) // rc = false; // // return rc; //} // //------------------------------------------------------------ ON_TextRun::~ON_TextRun() { Internal_Destroy(); } ON_TextRun::ON_TextRun(const ON_TextRun& src) { Internal_CopyFrom(src); } void ON_TextRun::Internal_Destroy() { m_managed_font = nullptr; if (nullptr != m_codepoints) { onfree(m_codepoints); m_codepoints = nullptr; } m_text_string.Destroy(); m_display_string.Destroy(); if (nullptr != m_stacked_text) { delete m_stacked_text; m_stacked_text = nullptr; } } void ON_TextRun::Internal_CopyFrom(const ON_TextRun& src) { // "this" is clean. m_managed_font = src.m_managed_font; size_t cplen = CodepointCount(src.m_codepoints); SetUnicodeString(cplen, src.m_codepoints); m_text_string = src.m_text_string; m_display_string = src.m_display_string; m_text_stacked = src.m_text_stacked; if (nullptr != src.m_stacked_text) { m_stacked_text = new ON_StackedText(*src.m_stacked_text); m_stacked_text->m_parent_run = this; } m_color = src.m_color; m_run_type = src.m_run_type; m_direction = src.m_direction; m_run_text_height = src.m_run_text_height; m_offset = src.m_offset; m_advance = src.m_advance; m_bbox = src.m_bbox; m_height_scale = src.m_height_scale; m_stackscale = src.m_stackscale; m_indent = src.m_indent; m_left_margin = src.m_left_margin; m_right_margin = src.m_right_margin; m_line_index = src.m_line_index; } ON_TextRun& ON_TextRun::operator=(const ON_TextRun& src) { if ( &src != this ) { Internal_Destroy(); Internal_CopyFrom(src); } return *this; } void ON_TextRun::Init( const ON_Font* managed_font, double height, double stackscale, ON_Color color, bool bold, bool italic, bool underlined, bool strikethrough, bool deletedisplay) { *this = ON_TextRun::Empty; m_color = color; m_run_text_height = height; m_stackscale = stackscale; if (nullptr == managed_font) managed_font = &ON_Font::Default; if ( false == managed_font->IsManagedFont() || managed_font->IsBold() != bold || managed_font->IsItalic() != italic || managed_font->IsUnderlined() != underlined || managed_font->IsStrikethrough() != strikethrough ) { managed_font = ON_Font::ManagedFontFromRichTextProperties( ON_Font::RichTextFontName(managed_font, true), bold, italic, underlined, strikethrough); } m_managed_font = managed_font; } bool ON_TextRun::IsText() const { return Type() == RunType::kText; } bool ON_TextRun::IsNewline() const { return Type() == RunType::kNewline; } bool ON_TextRun::IsColumn() const { return Type() == RunType::kColumn; } static bool RunIsInvalid() { return false; } bool ON_TextRun::IsValid() const { switch (Type()) { case RunType::kField: case RunType::kText: { Stacked is_stacked = IsStacked(); if ( IsStacked() != Stacked::kStacked && (nullptr == m_codepoints || 0 == ON_TextRun::CodepointCount(m_codepoints)) && !m_text_string.IsEmpty()) return RunIsInvalid(); if (!(m_run_text_height > 0.0)) return RunIsInvalid(); switch (is_stacked) { case Stacked::kNone: { if (nullptr != m_stacked_text) return RunIsInvalid(); break; } case Stacked::kStacked: { if (nullptr == m_stacked_text) return RunIsInvalid(); if (nullptr == m_stacked_text->m_top_run) return RunIsInvalid(); if (!m_stacked_text->m_top_run->IsValid()) return RunIsInvalid(); if (nullptr == m_stacked_text->m_bottom_run) return RunIsInvalid(); if (!m_stacked_text->m_bottom_run->IsValid()) return RunIsInvalid(); break; } case Stacked::kTop: { if (nullptr != m_stacked_text) return RunIsInvalid(); break; } case Stacked::kBottom: { if (nullptr != m_stacked_text) return RunIsInvalid(); break; } } break; } case RunType::kNewline: case RunType::kParagraph: case RunType::kSoftreturn: { if (!(m_run_text_height > 0.0)) return RunIsInvalid(); break; } default: return RunIsInvalid(); } return true; } void ON_TextRun::Internal_ContentChanged() const { m_text_run_hash = ON_SHA1_Hash::ZeroDigest; m_text_run_display_hash = ON_SHA1_Hash::ZeroDigest; } void ON_TextRun::SetType(ON_TextRun::RunType type) { //if (type == ON_TextRun::RunType::kField) // type = type; if (m_run_type != type) { Internal_ContentChanged(); m_run_type = type; } } ON_TextRun::RunType ON_TextRun::Type() const { //if (m_run_type == ON_TextRun::RunType::kField) // int i = 2; return m_run_type; } ON_TextRun::RunDirection ON_TextRun::FlowDirection() const { return m_direction; } void ON_TextRun::SetDisplayString(const wchar_t* str) { if (str != m_display_string.Array()) { ON_wString local(str); if (m_display_string != local) { Internal_ContentChanged(); m_display_string = local; } } } const wchar_t* ON_TextRun::DisplayString() const { if (m_display_string.IsEmpty()) return TextString(); else return m_display_string.Array(); } const wchar_t* ON_TextRun::TextString() const { if (m_text_string.IsEmpty()) { if (nullptr != m_codepoints) { bool b = sizeof(wchar_t) == sizeof(m_codepoints[0]); if (b) return (const wchar_t*)m_codepoints; size_t cplen = ON_TextRun::CodepointCount(m_codepoints); if (0 < cplen) ON_TextContext::ConvertCodepointsToString((int)cplen, m_codepoints, m_text_string); } } return m_text_string.Array(); } ON_TextRun::Stacked ON_TextRun::IsStacked() const { return m_text_stacked; } void ON_TextRun::SetStacked(Stacked stacked) { if (m_text_stacked != stacked) { Internal_ContentChanged(); m_text_stacked = stacked; } } void ON_TextRun::SetStackedOff() { SetStacked(Stacked::kNone); } double ON_TextRun::TextHeight() const { return m_run_text_height; } void ON_TextRun::SetTextHeight(double h) { if(h > 0.0) { if (!(m_run_text_height == h)) { Internal_ContentChanged(); m_run_text_height = h; } if (!(m_height_scale == -1.0)) { Internal_ContentChanged(); m_height_scale = -1.0; } } } ON_Color ON_TextRun::Color() const { return m_color; } void ON_TextRun::SetColor(ON_Color color) { if (m_color != color) { Internal_ContentChanged(); m_color = color; } } void ON_TextRun::SetFont(const ON_Font* font) { const ON_Font* managed_font = (nullptr != font) ? font->ManagedFont() : nullptr; if (m_managed_font != managed_font) { Internal_ContentChanged(); m_managed_font = managed_font; } if (!(m_height_scale == -1.0)) { Internal_ContentChanged(); m_height_scale = -1.0; } } const ON_Font* ON_TextRun::Font() const { return m_managed_font; } const ON_BoundingBox& ON_TextRun::BoundingBox() const { return m_bbox; }; void ON_TextRun::SetBoundingBox(ON_2dPoint pmin, ON_2dPoint pmax) { // If this function is removed and bounding box lazy evaluation // is used, then remove m_bbox from the SHA1 hash calculation. if ( !(m_bbox.m_min.x == pmin.x && m_bbox.m_min.y == pmin.y && 0.0 == m_bbox.m_min.z && m_bbox.m_max.x == pmax.x && m_bbox.m_max.y == pmax.y && 0.0 == m_bbox.m_max.z ) ) { Internal_ContentChanged(); m_bbox.m_min.x = pmin.x; m_bbox.m_min.y = pmin.y; m_bbox.m_min.z = 0.0; m_bbox.m_max.x = pmax.x; m_bbox.m_max.y = pmax.y; m_bbox.m_max.z = 0.0; } } const ON_2dVector& ON_TextRun::Offset() const { return m_offset; } void ON_TextRun::SetOffset(ON_2dVector offset) { if (!(m_offset == offset)) { Internal_ContentChanged(); m_offset = offset; } } const ON_2dVector& ON_TextRun::Advance() const { return m_advance; } void ON_TextRun::SetAdvance(ON_2dVector advance) { if (m_advance != advance) { Internal_ContentChanged(); m_advance = advance; } } ON_SHA1_Hash ON_TextRun::TextRunContentHash() const { return TextRunContentHash(true); } ON_SHA1_Hash ON_TextRun::TextRunContentHash( bool bEvaluateFields ) const { if (ON_SHA1_Hash::ZeroDigest == m_text_run_hash) { ON_SHA1 sha1; const bool bIsStacked = ( ON_TextRun::Stacked::kStacked == IsStacked() && nullptr != m_stacked_text && this != m_stacked_text->m_top_run && this != m_stacked_text->m_bottom_run && (nullptr != m_stacked_text->m_top_run || nullptr != m_stacked_text->m_bottom_run) ); if (nullptr != m_managed_font) { sha1.AccumulateSubHash(m_managed_font->FontCharacteristicsHash()); } TextString(); // insure m_text_string is set. sha1.AccumulateString(m_text_string); sha1.AccumulateDouble(m_run_text_height); if ( m_height_scale > 0.0 ) sha1.AccumulateDouble(m_height_scale); sha1.AccumulateDouble(m_stackscale); sha1.Accumulate2dVector(m_advance); sha1.Accumulate2dVector(m_offset); sha1.AccumulateDouble(m_indent); sha1.AccumulateDouble(m_left_margin); sha1.AccumulateDouble(m_right_margin); sha1.AccumulateUnsigned32(static_cast(m_color)); sha1.AccumulateUnsigned8(static_cast(m_text_stacked)); sha1.AccumulateUnsigned8(static_cast(m_run_type)); sha1.AccumulateUnsigned8(static_cast(m_direction)); if ( m_bbox.IsValid() ) sha1.AccumulateBoundingBox(m_bbox); if( bIsStacked ) { sha1.AccumulateUnsigned32(static_cast(m_stacked_text->m_separator)); if (nullptr != m_stacked_text->m_top_run) { sha1.AccumulateSubHash(m_stacked_text->m_top_run->TextRunContentHash(false)); } if (nullptr != m_stacked_text->m_bottom_run) { sha1.AccumulateSubHash(m_stacked_text->m_bottom_run->TextRunContentHash(false)); } } m_text_run_hash = sha1.Hash(); if (m_display_string.IsEmpty()) { m_text_run_display_hash = m_text_run_hash; } else { sha1.AccumulateString(m_display_string); if( bIsStacked ) { if (nullptr != m_stacked_text->m_top_run && m_stacked_text->m_top_run->m_display_string.IsNotEmpty() ) { sha1.AccumulateString(m_stacked_text->m_top_run->m_display_string); } if (nullptr != m_stacked_text->m_bottom_run && m_stacked_text->m_bottom_run->m_display_string.IsNotEmpty() ) { sha1.AccumulateString(m_stacked_text->m_bottom_run->m_display_string); } } m_text_run_display_hash = sha1.Hash(); } } return bEvaluateFields ? m_text_run_display_hash : m_text_run_hash; } void ON_TextRun::SetStackFractionHeight(double stackscale) { if (ON_SQRT_EPSILON < stackscale && 10000.0 > stackscale) { if (!(m_stackscale == stackscale)) { Internal_ContentChanged(); m_stackscale = stackscale; } } } double ON_TextRun::StackHeightFraction() const { return m_stackscale; } //static double ON_TextRun::DefaultStackFractionHeight() { return 0.7; } void ON_TextRun::Get2dCorners(ON_2dPoint corners[4]) const { const ON_2dVector& offset = Offset(); corners[0].Set(m_bbox.m_min.x + offset.x, m_bbox.m_min.y + offset.y); corners[1].Set(m_bbox.m_max.x + offset.x, m_bbox.m_min.y + offset.y); corners[2].Set(m_bbox.m_max.x + offset.x, m_bbox.m_max.y + offset.y); corners[3].Set(m_bbox.m_min.x + offset.x, m_bbox.m_max.y + offset.y); } // This returns the scale of run.m_height(the wcs text height) / HeightOfI. to // convert from font native units to model units. // It doesn't take into account anything about annotation scaling double ON_TextRun::HeightScale(const ON_Font* font) const { if(m_height_scale <= 0.0) { if (0 != font) const_cast< ON_TextRun* >(this)->m_height_scale = font->FontMetrics().GlyphScale(m_run_text_height); } return m_height_scale; } // Does not include terminating 0 // static size_t ON_TextRun::CodepointCount(const ON__UINT32* cp) { if (0 == cp) return 0; size_t i = 0; while (0 != cp[i]) i++; return i; } const ON__UINT32* ON_TextRun::UnicodeString() const { return m_codepoints; } // count doesn't include terminating 0, but a terminating 0 is added void ON_TextRun::SetUnicodeString(size_t count, const ON__UINT32* cp) { m_bbox.Destroy(); m_text_string.Empty(); m_display_string.Empty(); SetUnicodeString(m_codepoints, count, cp); #ifdef _DEBUG TextString(); #endif } void ON_TextRun::SetUnicodeString(ON__UINT32*& dest, size_t count, const ON__UINT32* cp) { if (0 == count || 0 == cp) { if (0 != dest) { onfree(dest); dest = nullptr; } return; } ON__UINT32* newcp = (ON__UINT32*)onrealloc(dest, (count + 1)*sizeof(ON__UINT32)); if(0 == newcp) { onfree(dest); dest = nullptr; } else { dest = newcp; memcpy(dest, cp, count*sizeof(ON__UINT32)); dest[count] = 0; // add terminating zero } } static int ConvertCpToWChar(ON__UINT32 cp, wchar_t wch[3]) { unsigned int error_status = 0; int ok = ON_ConvertUTF32ToWideChar( 0, //int bTestByteOrder, &cp, //const ON__UINT32* sUTF32, 1, //int sUTF32_count, wch, //wchar_t* sWideChar, 3, //int sWideChar_count, &error_status, //unsigned int* error_status, 0xFFFFFFFF, //unsigned int error_mask, 0xFFFD, //ON__UINT32 error_code_point, 0 //const ON__UINT32** sNextUTF32 ); return ok; } int ON_TextRun::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 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 ON__UINT32* cp = UnicodeString(); int cpcount = (int)CodepointCount(cp); const ON_Font* font = Font(); if (nullptr == font) return 0; double height_scale = HeightScale(font); // Font units to world units 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; } 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 < cpcount; ci++) { const ON_FontGlyph* gi = font->CodePointGlyph(cp[ci]); wchar_t wch[3] = { 0, 0, 0 }; if (nullptr != gi) { int wccnt = ConvertCpToWChar(gi->CodePoint(), wch); if (1 == wccnt) { 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 > cpcount) { // 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) newrun->SetUnicodeString(cpcount - start_char_offset, cp + start_char_offset); linewidth += runwidth; } newruns.AppendRun(newrun); 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; for (int ci = start_char_offset; ci < cpcount; ci++) { const ON_FontGlyph* gi = font->CodePointGlyph(cp[ci]); wchar_t wch[3] = { 0, 0, 0 }; if (nullptr != gi) { int wccnt = ConvertCpToWChar(gi->CodePoint(), wch); if (1 == wccnt) { const ON_TextBox glyph_box = gi->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; newrun->SetUnicodeString(run_length, cp + start_char_offset); 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); return new_count + wrapcount; } if (iswspace(wch[0])) { found_space = true; last_space = ci; } } } } ON_TextRun* newrun = ON_TextRun::GetManagedTextRun(); // make a new run if (nullptr != newrun) { *newrun = *this; newrun->SetUnicodeString(run_length, cp + start_char_offset); newrun->SetOffset(ON_2dVector(0.0, y_offset + Offset().y)); newruns.AppendRun(newrun); new_count += 1; } return new_count; } ON_StackedText::StackStyle ON_StackedText::StackStyleFromUnsigned( unsigned int stack_style_as_unsigned ) { switch (stack_style_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_StackedText::StackStyle::kUnset); ON_ENUM_FROM_UNSIGNED_CASE(ON_StackedText::StackStyle::kHorizontalToScreen); ON_ENUM_FROM_UNSIGNED_CASE(ON_StackedText::StackStyle::kSlanted); } ON_ERROR("stack_style_as_unsigned parameter is not valid"); return ON_StackedText::StackStyle::kUnset; } ON_TextRun::RunType ON_TextRun::RunTypeFromUnsigned( unsigned int run_type_as_unsigned ) { switch (run_type_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kNone); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kText); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kNewline); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kSoftreturn); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kParagraph); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kColumn); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kField); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kFieldValue); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kFontdef); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kHeader); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunType::kFonttbl); } ON_ERROR("run_type_as_unsigned parameter is not valid"); return ON_TextRun::RunType::kNone; } enum class Stacked : unsigned char { kNone = 0, kStacked = 1, kTop = 2, kBottom = 3 }; ON_TextRun::Stacked ON_TextRun::StackedFromUnsigned( unsigned int stacked_as_unsigned ) { switch (stacked_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kNone); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kStacked); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kTop); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::Stacked::kBottom); } ON_ERROR("stacked_as_unsigned parameter is not valid"); return ON_TextRun::Stacked::kNone; } enum class RunDirection : unsigned char { kLtR = 0, kRtL = 1, }; ON_TextRun::RunDirection ON_TextRun::RunDirectionFromUnsigned( unsigned int run_direction_as_unsigned ) { switch (run_direction_as_unsigned) { ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunDirection::kLtR); ON_ENUM_FROM_UNSIGNED_CASE(ON_TextRun::RunDirection::kRtL); } ON_ERROR("run_direction_as_unsigned parameter is not valid"); return ON_TextRun::RunDirection::kLtR; } //bool ON_TextRun::Write( // ON_BinaryArchive& archive // ) const //{ // return WriteNested(0,archive); //} // //bool ON_TextRun::Read( // ON_BinaryArchive& archive // ) //{ // return ReadNested(0,archive); //} // //bool ON_TextRun::WriteNested( // unsigned int nested_depth, // ON_BinaryArchive& archive // ) const //{ // if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0)) // return false; // bool rc = false; // for (;;) // { // if (!archive.WriteUuid(m_Font_id)) // break; // // // const ON__UINT32* cp1 = m_codepoints; // if (nullptr != cp1) // { // while ( 0 != *cp1 ) // cp1++; // } // // unsigned int count = static_cast(cp1 - m_codepoints); // if (!archive.WriteInt(count)) // break; // if (!archive.WriteInt(count,m_codepoints)) // break; // // unsigned int u = static_cast(m_stacked); // if (!archive.WriteInt(u)) // break; // // if (!archive.WriteBool(nullptr!=m_stacked_text)) // break; // if (nullptr != m_stacked_text) // { // // nested_depth is correct. // // m_stacked_text will increment nested_depth if it calls back to ON_TextRun::WriteNested(). // if (!m_stacked_text->WriteNested(nested_depth,archive)) // break; // } // // if (!archive.WriteColor(m_color)) // break; // // u = static_cast(m_type); // if (!archive.WriteInt(u)) // break; // // u = static_cast(m_direction); // if (!archive.WriteInt(u)) // break; // // if (!archive.WriteDouble(m_height)) // break; // if (!archive.WriteVector(m_offset)) // break; // if (!archive.WriteVector(m_advance)) // break; // if (!archive.WriteBoundingBox(m_bbox)) // break; // if (!archive.WriteDouble(m_height_scale)) // break; // if (!archive.WriteDouble(m_indent)) // break; // if (!archive.WriteDouble(m_left_margin)) // break; // if (!archive.WriteDouble(m_right_margin)) // break; // if (!archive.WriteInt(m_line_index)) // break; // // rc = true; // break; // } // if (!archive.EndWrite3dmChunk()) // rc = false; // return rc; //} // //bool ON_TextRun::ReadNested( // unsigned int nested_depth, // ON_BinaryArchive& archive // ) //{ // *this = ON_TextRun::Empty; // if ( nested_depth > 8 ) // return false; // // int major_version = 0; // int minor_version = 0; // if ( !archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version)) // return false; // // nested_depth++; // // bool rc = false; // for (;;) // { // if (1 != major_version) // { // // This is prototype IO code and increasing major_version is not an error // // until we are sure we got it right. // rc = true; // break; // } // // if (!archive.ReadUuid(m_Font_id)) // break; // // unsigned int count = 0; // if (!archive.ReadInt(&count)) // break; // if (count > 0) // { // m_codepoints = (ON__UINT32*)onmalloc( (count + 1)*sizeof(m_codepoints[0])); // if ( nullptr == m_codepoints ) // break; // m_codepoints[count] = 0; // if (!archive.ReadInt(count,m_codepoints)) // break; // } // // unsigned int u = static_cast(m_stacked); // if (!archive.WriteInt(u)) // break; // // if (!archive.WriteBool(nullptr!=m_stacked_text)) // break; // if (nullptr != m_stacked_text) // { // // nested_depth is correct. // // m_stacked_text will increment nested_depth if it calls back to ON_TextRun::WriteNested(). // if (!m_stacked_text->WriteNested(nested_depth,archive)) // break; // } // // if (!archive.WriteColor(m_color)) // break; // // u = static_cast(m_type); // if (!archive.WriteInt(u)) // break; // // u = static_cast(m_direction); // if (!archive.WriteInt(u)) // break; // // if (!archive.WriteDouble(m_height)) // break; // if (!archive.WriteVector(m_offset)) // break; // if (!archive.WriteVector(m_advance)) // break; // if (!archive.WriteBoundingBox(m_bbox)) // break; // if (!archive.WriteDouble(m_height_scale)) // break; // if (!archive.WriteDouble(m_indent)) // break; // if (!archive.WriteDouble(m_left_margin)) // break; // if (!archive.WriteDouble(m_right_margin)) // break; // if (!archive.WriteInt(m_line_index)) // break; // // rc = true; // break; // } // // if (!archive.EndRead3dmChunk()) // rc = false; // // return rc; //} //------------------------------------------------------------