⚠ This is not a general purpose HTML/CSS renderer, only the specified tags/properties below are targeted
Implementation of Rich Text Rendering with flexible renderer/platform integrations. Use it as follows:
auto id1 = ImRichText::CreateRichText("<blink>This is blinking</blink>"
"<marquee>This is moving...</marquee>"
"<meter value='3' max='10'></meter>"
"<s><q>Quotation </q><cite>Citation</cite></s>"
"<br>Powered by: <a href='https://https://github.com/ajax-crypto/ImRichText'>ImRichText</a>"
"<ul style='font-size: 36px;'><li>item</li><li>item</li></ul>");
auto id2 = ImRichText::CreateRichText("2<sup>2</sup> equals 4 <hr style=\"height: 4px; color: sienna;\"/>"
"<p style=\"color: rgb(150, 0, 0);\">Paragraph <b>bold <i>italics</i> bold2 </b></p>"
"<h1 style=\"color: darkblue;\">Heading	</h1>"
"<span style='background: teal; color: white;'>White on Teal</span><br/>"
"<mark>This is highlighted! <small>This is small...</small></mark>");
ImRichText::DefaultConfigParams params;
params.DefaultFontSize = 24.f;
params.FontLoadFlags = ImRichText::FLT_HasSmall | ImRichText::FLT_Proportional | ImRichText::FLT_HasH1 |
ImRichText::FLT_HasSuperscript;
params.FontScale = 1.5f;
auto config = ImRichText::GetDefaultConfig(params);
#ifdef IM_RICHTEXT_TARGET_IMGUI
ImRichText::ImGuiRenderer renderer{ *config };
ImRichText::ImGuiGLFWPlatform platform;
config->Renderer = &renderer;
config->Platform = &platform;
#elif defined(IM_RICHTEXT_TARGET_BLEND2D)
Blend2DRenderer renderer{ context };
config->Renderer = &renderer;
#endif
config->ListItemBullet = ImRichText::BulletType::Arrow;
ImRichText::PushConfig(*config);
while (<event-loop>)
{
if (ImGui::Begin(...))
{
// ... other widgets
ImRichText::GetCurrentConfig()->DefaultBgColor = ImColor{ 255, 255, 255 };
ImRichText::Show(id1);
ImRichText::GetCurrentConfig()->DefaultBgColor = ImColor{ 200, 200, 200 };
ImRichText::Show(id2);
// ... other widgets
}
}
Just include the .h and .cpp files in your project. (You will need a C++17 compiler)
The following subset of HTML tags/CSS properties are supported:
Tags | Description | Implementation Status |
---|---|---|
span | A region of text with certain style properties | Yes |
p | Start a paragraph in new line (paragraph indent can be specified in RenderConfig::ParagraphStop ) |
Yes |
font | Specify size, family, weight, style for a block of text | Yes |
sup/sub | Superscript/Subscript | Yes1 |
hr | Horizontal line | Yes |
h1...h6 | Header (bold) text with a line underneath | Yes |
ul | Un-numbered list (with bullets) | Yes |
ol | Numbered list (with nested numberings i.e. 1.2.3) | Yes |
li | List Item | Yes |
br | Line Break | Yes |
b/strong | Bold block of text | Yes |
i/em/cite | Italics block of text | Yes |
mark | Highlight current block of text | Yes |
small | Reduce font size to 80% of current block | Yes |
q | Wrap text inside quotation mark | Yes |
u | Underline current block of text | Yes2 |
a | Make current block of text a hyperlink (handle click events) | Yes |
abbr | Mark current block as an abbreviation, title attribute contains tooltip |
Yes |
s/del | Draw a horizontal line through the text content | Yes |
blink | Make current block of text blink | Yes |
marquee | Make current block of text scroll horizontally | Yes |
meter | Create a progress bar inline | Yes |
font | Specify custom font with family/size/weight/etc. | Yes |
center | Center align text | Yes |
pre | Preformatted text with monospaced font | Under progress |
code | Use monospace font for this block of text | Under progress |
blockquote | Blockquote as in HTML | Under progress |
Property Name(s) | Value/Example |
---|---|
background/background-color/color | rgb(r, g, b) /rgba(r, g, b, a) /hsl(h, s, l) /linear-gradient(color-stops) 3 CSS color name |
padding/padding-top/etc. | px /em units |
font-size | pt /px /em (absolute) / % (of parent font size) / xx-small , x-small , medium , large , etc. |
font-family | name of font family |
font-weight | value between 0-800 or light /normal /bold |
font-style | italics/oblique |
height/width | px /em |
list-style-type | (Only for list items) circle /disk /square /custom 4 |
border/border-top/etc. | 2px solid gray 5 |
border-color | As specified for background |
border-width | Numeric value in pixel |
border-radius/border-top-left-radius/etc. | px /em |
alignment | left /right /center /justify (Horizontal text alignment) |
vertical-align | top /bottom /center (Vertical text alignment) |
box-shadow | offset , blur and spread supported |
text-overflow | Under progress |
text-wrap | Under progress |
word-break | Under progress |
white-space-collapse | Under progress |
white-space | Under progress |
In order to handle rich text as specified above, fonts need to be managed i.e. different family, weights, sizes, etc.
The library internally uses default fonts (on Windows, Segoe UI family for proportional and Consolas for monospace).
However, user can provide their own font provider through IRenderer
interface.
NOTE : The default ImGui renderer implementation doest not support dynamic font loading right now. All fonts must be
loaded by ImRichText::LoadDefaultFonts
functions before rendering.
- Support for class/id with stylesheets
- Remove pointers from API
- Add support for
margin
- Add support for line style (solid, dotted, dashed) for
border
- Implement baseline text alignment (May need to use FreeType backend)
- Integration example with Clay layout library
- Roman numerals for numbered lists
- Tables (
<table>
,<tr>
,<th>
,<td>
tags) - Thread safety
- Use a library (roll your own?) to lookup font(s) based on glyph ranges.
- Internationalization support by integrating Harfbuzz (Unicode Bidir algorithm)
- Add ways to remove C++ standard library dependencies
- Conical gradient fills for backgrounds
- Clipping mask support (requires ImGui changes?)
- Text effects like "glow", "shadow", etc.
- Add a tooltip property as a replacement for title property
- Build scripts like cmake, build2, make, etc. This library is intended to be used by simply copying the .h/.cpp files.
- Full-fledged support for CSS3 styling with layout
- Support alternate syntax i.e. Markdown, Restructured Text, MathML, etc.
- Integrating scripting languages
The library depends on ImGui and C++17 standard library. It can be compiled using any C++17 compiler.
The following headers are used: vector
, deque
, unordered_map
, tuple
, chrono
, string_view
, optional
.
If using the default font manager, additionally, map
, unordered_set
, filesystem
is also used.
In order to customize certain behavior at build-time, the following macros can be used
Macro name | Functionality | Default Value |
---|---|---|
IM_RICHTEXT_MAXDEPTH |
Maximum depth of nested blocks/tags in Rich Text | 32 |
IM_RICHTEXT_MAX_LISTDEPTH |
Maximum depth of nested lists | 16 |
IM_RICHTEXT_MAX_LISTITEM |
Maxmimum number of list items at a specific depth | 128 |
IM_RICHTEXT_MAXTABSTOP |
Maxmimum number of nested <p> /paragraphs |
32 |
IM_RICHTEXT_ENABLE_PARSER_LOGS |
Enable printing parsing + layout logs in console in debug builds | Not defined |
IM_RICHTEXT_BLINK_ANIMATION_INTERVAL |
Specify blink animation interval | 500ms |
IM_RICHTEXT_MARQUEE_ANIMATION_INTERVAL |
Specify interval (1/FPS ) for marquee animation |
18ms |
IM_RICHTEXT_MAX_COLORSTOPS |
Specify maximum color stops in gradients | 8 |
IM_RICHTEXT_TARGET_IMGUI |
Specify if target is ImGui | undefined |
IM_RICHTEXT_TARGET_BLEND2D |
Specify if target is Blend2D | undefined |
IM_FONTMANAGER_STANDALONE |
Specify if using only font manager in standalone context (without rest of ImRichText) |
When _DEBUG
macro is defined, if a console is present, error messages will be printed along
with the parsing state i.e. entering/exiting tags. Custom properties or unknonw tags are ignored, but reported.
Contributions welcome, especially in getting Linux/MacOS examples added! Prefer build.sh build scripts, over cmake/ninja/etc. solutions.
The coding style is C++ with minimal OOP constructs i.e. no classes, inheritance, runtime polymorphism etc.
The only exception being the interfaces which enable runtime polymorphism for rendering and platform integration.
Features such as attributes, vocabulary types (std::optional
& std::tuple
) & non-capturing lambdas can be used. There are no
exceptions or RTTI used in the library. Minimal uses of templates has "container of T" (No SFINAE shenanigans)
There is minimal use of raw pointers, mostly as a reference and no use of smart pointers. The library aims to minimize allocations on heap hence, most resources are pre-allocated or live on the stack. The only heap usage comes from limited use of standard library containers where applicable.
The current implementation is not thread-safe, and the library is not intended to be used in a multi-threaded environment. In terms of design, the library does not construct an explicit AST, but rather uses a stack-based approach to parse and render the text.
The following interface is available to port it to any graphics API desired:
struct IRenderer
{
void* UserData = nullptr;
virtual void SetClipRect(ImVec2 startpos, ImVec2 endpos) = 0;
virtual void ResetClipRect() = 0;
virtual void DrawLine(ImVec2 startpos, ImVec2 endpos, uint32_t color, float thickness = 1.f) = 0;
virtual void DrawPolyline(ImVec2* points, int sz, uint32_t color, float thickness) = 0;
virtual void DrawTriangle(ImVec2 pos1, ImVec2 pos2, ImVec2 pos3, uint32_t color, bool filled, bool thickness = 1.f) = 0;
virtual void DrawRect(ImVec2 startpos, ImVec2 endpos, uint32_t color, bool filled, float thickness = 1.f, float radius = 0.f, int corners = 0) = 0;
virtual void DrawRectGradient(ImVec2 startpos, ImVec2 endpos, uint32_t topleftcolor, uint32_t toprightcolor, uint32_t bottomrightcolor, uint32_t bottomleftcolor) = 0;
virtual void DrawPolygon(ImVec2* points, int sz, uint32_t color, bool filled, float thickness = 1.f) = 0;
virtual void DrawPolyGradient(ImVec2* points, uint32_t* colors, int sz) = 0;
virtual void DrawCircle(ImVec2 center, float radius, uint32_t color, bool filled, bool thickness = 1.f) = 0;
virtual void DrawRadialGradient(ImVec2 center, float radius, uint32_t in, uint32_t out, int start, int end) = 0;
virtual void DrawBullet(ImVec2 startpos, ImVec2 endpos, uint32_t color, int index, int depth) {};
virtual bool SetCurrentFont(std::string_view family, float sz, FontType type) { return false; };
virtual bool SetCurrentFont(void* fontptr) { return false; };
virtual void ResetFont() {};
virtual ImVec2 GetTextSize(std::string_view text, void* fontptr) = 0;
virtual void DrawText(std::string_view text, ImVec2 pos, uint32_t color) = 0;
virtual void DrawText(std::string_view text, std::string_view family, ImVec2 pos, float sz, uint32_t color, FontType type) = 0;
virtual void DrawTooltip(ImVec2 pos, std::string_view text) = 0;
virtual float EllipsisWidth(void* fontptr);
void DrawDefaultBullet(BulletType type, ImVec2 initpos, const BoundedBox& bounds, uint32_t color, float bulletsz);
};
For platform integration (to handle clicks/hover events), the following interface is available:
struct IPlatform
{
virtual ImVec2 GetCurrentMousePos() = 0;
virtual bool IsMouseClicked() = 0;
virtual void HandleHyperlink(std::string_view) = 0;
virtual void RequestFrame() = 0;
virtual void HandleHover(bool) = 0;
};
For text shaping integration, the following interface is available:
struct ITextShaper
{
struct WordProperty
{
void* font;
WordBreakBehavior wb;
};
using StyleAccessor = WordProperty (*)(int wordIdx, void* userdata);
using LineRecorder = void (*)(int wordIdx, void* userdata);
using WordRecorder = void (*)(int wordIdx, std::string_view, ImVec2 dim, void* userdata);
virtual void ShapeText(float availwidth, const Span<std::string_view>& words,
StyleAccessor accessor, LineRecorder lineRecorder, WordRecorder wordRecorder,
const RenderConfig& config, void* userdata) = 0;
virtual void SegmentText(std::string_view content, WhitespaceCollapseBehavior wsbhv,
LineRecorder lineRecorder, WordRecorder wordRecorder, const RenderConfig& config,
bool ignoreLineBreaks, bool ignoreEscapeCodes, void* userdata) = 0;
virtual int NextGraphemeCluster(const char* from, const char* end) const = 0;
virtual int NextWordBreak(const char* from, const char* end) const = 0;
virtual int NextLineBreak(const char* from, const char* end) const = 0;
};
The library comes with multiple text shapers i.e. ASCII, ASCII Extended, Simple UTF-8 (without diacritics, kerning & ligature). A text shaper based on Unicode BiDir algorithm is possible with SheenBiDi + HarfBuzz.
NOTE : Platform integration is optional, and can be omitted. If omitted, <blink>
, <marquee>
will not animate, and,
a
hyperlinks and tooltips will not be functional.
Default implementations are provided for ImGui and Blend2D (Under progress) Platform integration implementation provided for ImGui + GLFW (available in examples directory)
Footnotes
-
Nested subscript/superscript is untested at the moment ↩
-
Underline text due to
<u>
tag is not baseline-underlined, but underlined beneath the whole text ↩ -
Only axis aligned gradients are support as
background
property ↩ -
Custom bullets are also possible, set
RenderConfig::DrawBullet
function pointer andlist-style-type
property tocustom
↩ -
Border line type is parsed but not used for rendering ↩