.. Copyright (c) 2026 Tobias Erbsland - Erbsland DEV. https://erbsland.dev SPDX-License-Identifier: Apache-2.0 ****************** Rich Text and HTML ****************** The :cpp:any:`erbsland::cterm::text` helpers let you render a focused subset of HTML as styled terminal output. They are designed to give you predictable, document-like formatting without the complexity of a full HTML engine. The system is structured into three layers: * :cpp:any:`HtmlRenderer ` parses HTML fragments or full documents and renders them either into a :cpp:any:`String ` or directly to a :cpp:any:`CursorWriter `. * :cpp:any:`Style ` defines the visual appearance of headings, paragraphs, lists, blockquotes, code blocks, and inline elements such as emphasis or links. :cpp:any:`StyleSelector ` and :cpp:any:`StyleRole ` let you target those rules precisely. * :cpp:any:`TextNode ` represents the intermediate document tree. You can inspect or construct this tree when you need more control than direct rendering provides. If you only need plain terminal text, see :doc:`text`. For details on paragraph wrapping and layout behavior used by block rendering, refer to :doc:`paragraph-options`. Usage ===== Rendering HTML Fragments to Terminal Text ----------------------------------------- Use :cpp:any:`HtmlRenderer ` when you want to convert HTML into terminal-friendly output. The renderer supports a practical subset of HTML, including: * paragraphs and headings * links * unordered and ordered lists * definition lists * blockquotes * code blocks * horizontal rules * common inline formatting (````, ````, etc.) .. code-block:: cpp using namespace erbsland::cterm; namespace rich = erbsland::cterm::text; const auto html = std::string_view{ "

Status

" "

Hello world.

" "
  • One
  • Two
" "

Documentation

"}; const auto summary = rich::HtmlRenderer{html}.renderString(); terminal.print(summary); Use ``renderString()`` when you want a self-contained result that you can store, combine, or print later. Use ``renderTo()`` when you are writing directly to a :cpp:any:`CursorWriter ` and want to preserve block spacing, margins, and cursor positioning across multiple render operations. Customizing Heading, List, and Code Styles ------------------------------------------ With :cpp:any:`Style `, you define how your rendered content looks through selector-based rules. Rules can target a semantic role, an optional heading or list level, a list kind, and named style tokens parsed from ``TextNode::style()``. .. code-block:: cpp namespace rich = erbsland::cterm::text; auto style = std::make_shared(); style->setBaseTextStyle(CharStyle{Color{fg::BrightWhite, bg::Inherited}}); style->edit(rich::StyleSelector::strong()).setTextStyle(CharStyle{Color{fg::BrightYellow, bg::Inherited}}); style->edit(rich::StyleSelector::code()).setTextStyle(CharStyle{Color{fg::BrightCyan, bg::Black}}); style->edit(rich::StyleSelector::heading(1)) .setTextStyle(CharStyle{Color{fg::BrightWhite, bg::Blue}}) .setSuffix(U" ") .setLineFill(U'='); style->edit(rich::StyleSelector::listItem(rich::StyleListKind::Bullet, 0)) .setLiteralMarker(U"• ", CharStyle{Color{fg::BrightMagenta, bg::Inherited}}); rich::HtmlRenderer{ "

Title

  • Entry
", style}.renderTo(buffer); Block-level layout is controlled via :cpp:any:`ParagraphIndents `. The renderer automatically collapses adjacent vertical margins where it makes sense, so headings and paragraphs behave like cohesive document blocks rather than isolated ``printParagraph()`` calls. Styling Inline Span Tokens -------------------------- Generic inline spans can be styled explicitly with :cpp:any:`StyleSelector::span() ` and optional style tokens parsed from HTML ``class`` attributes. .. code-block:: cpp namespace rich = erbsland::cterm::text; auto style = std::make_shared(); style->edit(rich::StyleSelector::span()).setTextStyle(Color{fg::White, bg::Inherited}); style->edit(rich::StyleSelector{rich::StyleRole::Span, {"tag"}}) .setTextStyle(Color{fg::BrightBlack, bg::BrightYellow}); style->edit(rich::StyleSelector{rich::StyleRole::Span, {"warning"}}) .setTextStyle(Color{fg::BrightYellow, bg::Inherited}, CharAttributes::Bold); rich::HtmlRenderer{ "

beta Read carefully.

", style}.renderTo(buffer); This works well for terminal badges, inline tags, severity labels, and other compact document cues that should stay part of the text flow. Inspecting and Building TextNode Trees -------------------------------------- :cpp:any:`TextNode ` gives you access to the parsed document structure. This is useful when you want to: * debug or inspect parsed HTML * transform content before rendering * build structured terminal output programmatically .. code-block:: cpp namespace rich = erbsland::cterm::text; auto document = rich::TextNode::createDocument(); auto paragraph = rich::TextNode::createParagraph(); auto link = rich::TextNode::createLink("/docs"); link->addChild(rich::TextNode::createText(U"Open docs")); paragraph->addChild(link); document->addChild(paragraph); for (const auto &line : document->toDiagnosticTree(2)) { terminal.printLine(line); } When the HTML parser encounters unsupported block-level elements (for example tables or SVG), it inserts :cpp:any:`TextNode::Type::Unsupported ` nodes instead of silently dropping content. This makes missing support explicit and easier to handle in your application logic. Interface ========= .. doxygenclass:: erbsland::cterm::text::HtmlRenderer :members: .. doxygentypedef:: erbsland::cterm::text::StylePtr .. doxygentypedef:: erbsland::cterm::text::StyleConstPtr .. doxygenclass:: erbsland::cterm::text::Style :members: .. doxygenenum:: erbsland::cterm::text::StyleRole .. doxygenenum:: erbsland::cterm::text::StyleListKind .. doxygenclass:: erbsland::cterm::text::StyleSelector :members: .. doxygenclass:: erbsland::cterm::text::StyleMarker :members: .. doxygenclass:: erbsland::cterm::text::StyleRule :members: .. doxygentypedef:: erbsland::cterm::text::TextNodePtr .. doxygentypedef:: erbsland::cterm::text::TextNodeConstPtr .. doxygentypedef:: erbsland::cterm::text::TextNodeWeakPtr .. doxygenclass:: erbsland::cterm::text::TextNode :members: