View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0005144 | NoesisGUI | C++ SDK | public | 2026-07-22 11:37 | 2026-07-22 11:47 |
| Reporter | FrancoisRecisio | Assigned To | jsantos | ||
| Priority | normal | Severity | minor | ||
| Status | assigned | Resolution | open | ||
| Product Version | 3.2.13 | ||||
| Target Version | 3.2.14 | ||||
| Summary | 0005144: Some texts contain empty runs | ||||
| Description | I wanted to extract the runs from a text. It works well, but sometimes I have noticed that empty runs (with a width of 0) are added, for example before non-Latin characters. | ||||
| Attached Files | EmptyRun.xaml (592 bytes)
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Background="White" TextElement.FontSize="30" TextElement.Foreground="Black"> <TextBlock Margin="30" Text="こんにちは世界 안녕하세요, 세상 Hello world 你好世界 你好世界" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Margin="30" Text="こんにちは世界 안녕하세요 세상 Hello world 你好世界 你好世界" HorizontalAlignment="Center" VerticalAlignment="Center" /> </StackPanel> emptyrun.patch (5,751 bytes)
Index: Src/Packages/Drawing/VGL/Inc/NsDrawing/VGLTextLayout.h
===================================================================
--- Src/Packages/Drawing/VGL/Inc/NsDrawing/VGLTextLayout.h (revision 17620)
+++ Src/Packages/Drawing/VGL/Inc/NsDrawing/VGLTextLayout.h (working copy)
@@ -313,6 +313,10 @@
/// Unserializes from the specified memory buffer
void Unserialize(const char* buffer);
+#if NS_TEXT_SHAPING
+ const Vector<GlyphRun, 1>& kfeGetRuns() const { return mRuns; }
+#endif
+
/// Cleanup operations on internal resources
static void Finalize();
Index: Src/Packages/Drawing/VGL/Src/ShapingFull.h
===================================================================
--- Src/Packages/Drawing/VGL/Src/ShapingFull.h (revision 17620)
+++ Src/Packages/Drawing/VGL/Src/ShapingFull.h (working copy)
@@ -1250,7 +1250,10 @@
float& x, float& y)
{
// This function expect a grapheme index, not a character index
- NS_ASSERT(grapheme < layout->mNumGraphemes);
+ if (!(grapheme < layout->mNumGraphemes))
+ {
+ return;
+ }
// Glyphs -> Display ordering
// ShapingGlyphs -> Logical ordering
Index: Src/Packages/Gui/Core/Include/NsGui/FormattedText.h
===================================================================
--- Src/Packages/Gui/Core/Include/NsGui/FormattedText.h (revision 17620)
+++ Src/Packages/Gui/Core/Include/NsGui/FormattedText.h (working copy)
@@ -56,6 +56,18 @@
float baseline;
};
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#if NS_TEXT_SHAPING
+struct kfeRunInfo
+{
+ float left;
+ float width;
+
+ float center() const { return left + width * 0.5f; }
+ float right() const { return left + width; }
+};
+#endif
+
NS_WARNING_PUSH
NS_MSVC_WARNING_DISABLE(4251 4275)
@@ -164,6 +176,10 @@
bool IsInitialized() const;
void Init();
+#if NS_TEXT_SHAPING
+ Vector<kfeRunInfo> kfeComputeRunInfos() const;
+#endif
+
NS_IMPLEMENT_INTERFACE_FIXUP
private:
Index: Src/Packages/Gui/Core/Src/FormattedText.cpp
===================================================================
--- Src/Packages/Gui/Core/Src/FormattedText.cpp (revision 17620)
+++ Src/Packages/Gui/Core/Src/FormattedText.cpp (working copy)
@@ -997,7 +997,30 @@
return brushIndex == -1 || mBrushes[brushIndex]->IsTransparent();
}
+#if NS_TEXT_SHAPING
+
////////////////////////////////////////////////////////////////////////////////////////////////////
+Vector<kfeRunInfo> FormattedText::kfeComputeRunInfos() const
+{
+ Vector<kfeRunInfo> output;
+
+ const Vector<GlyphRun, 1>& glyphRuns = mTextLayout->kfeGetRuns();
+ output.Reserve(glyphRuns.Size());
+ for (const GlyphRun& glyphRun : glyphRuns)
+ {
+ float left, right, y;
+ mTextLayout->HitTestTextPosition(glyphRun.firstRenderGlyph, false, left, y);
+ mTextLayout->HitTestTextPosition(glyphRun.firstRenderGlyph + glyphRun.numRenderGlyphs - 1, true, right, y);
+
+ output.EmplaceBack(kfeRunInfo{ left, right - left });
+ }
+
+ return output;
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
NS_BEGIN_COLD_REGION
NS_IMPLEMENT_REFLECTION(FormattedText)
Index: Src/Packages/Gui/Core/Src/TextBlock.cpp
===================================================================
--- Src/Packages/Gui/Core/Src/TextBlock.cpp (revision 17620)
+++ Src/Packages/Gui/Core/Src/TextBlock.cpp (working copy)
@@ -20,8 +20,10 @@
#include <NsGui/DrawingContext.h>
#include <NsGui/FormattedText.h>
#include <NsGui/RectangleGeometry.h>
+#include <NsGui/SolidColorBrush.h>
#include <NsGui/MatrixTransform.h>
#include <NsGui/Typography.h>
+#include <NsGui/Pen.h>
#include <NsCore/UTF8.h>
#include <NsCore/String.h>
#include <NsCore/ReflectionImplement.h>
@@ -31,6 +33,7 @@
#include "LayoutFlags.h"
+Noesis::Ptr<Noesis::Pen> pen;
#undef DrawText
@@ -42,6 +45,7 @@
#include <NsCore/HashMap.h>
static HashSet<TextBlock*> gRegisteredTexts;
#endif
+#include <NsGui/Brushes.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
enum TextFlags
@@ -75,6 +79,11 @@
gRegisteredTexts.Insert(this);
#endif
+
+
+ pen = Noesis::MakePtr<Noesis::Pen>();
+ pen->SetBrush(Noesis::Brushes::Black());
+ pen->SetThickness(1);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -97,6 +106,11 @@
gRegisteredTexts.Insert(this);
#endif
+
+
+ pen = Noesis::MakePtr<Noesis::Pen>();
+ pen->SetBrush(Noesis::Brushes::Black());
+ pen->SetThickness(1);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -701,6 +715,21 @@
}
const Thickness& padding = GetPadding();
+
+
+ int i = 0;
+ auto boup = mFormattedText->kfeComputeRunInfos();
+ for (auto kfe_run_info : boup)
+ {
+ context->DrawRectangle(
+ kfe_run_info.width >= 5.0f ? (i % 2 == 0 ? Noesis::Brushes::Red() : Noesis::Brushes::Green()) : Noesis::Brushes::Blue(),
+ pen,
+ Noesis::Rect(Noesis::Point(padding.left, padding.top) + Noesis::Point(kfe_run_info.left, kfe_run_info.width >= 5.0f ? 0 : 10.0f), Noesis::Size(Noesis::Max(kfe_run_info.width, 5.0f), textSize.height)));
+ i++;
+ }
+
+
+
context->DrawText(mFormattedText, Point(padding.left, padding.top));
if (NS_UNLIKELY(rtl))
| ||||
| Platform | Any | ||||
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-07-22 11:37 | FrancoisRecisio | New Issue | |
| 2026-07-22 11:37 | FrancoisRecisio | File Added: EmptyRun.xaml | |
| 2026-07-22 11:37 | FrancoisRecisio | File Added: emptyrun.patch | |
| 2026-07-22 11:37 | FrancoisRecisio | File Added: emptyrun.png | |
| 2026-07-22 11:46 | jsantos | Assigned To | => jsantos |
| 2026-07-22 11:46 | jsantos | Status | new => assigned |
| 2026-07-22 11:47 | jsantos | Product Version | 3.2.14 => 3.2.13 |
| 2026-07-22 11:47 | jsantos | Target Version | => 3.2.14 |