Node Tree — Base & Text
Every visual element in a Docraft document is a DocraftLoomNode in the
loom node tree (see About Docraft). Nodes are built by
DocraftLoomTreeBuilder (Craft Language Parser) from parsed Craft Language
elements and then walked by the four pipeline visitors (Pipeline).
DocraftLoomNode
Base class for every node: child management, LayoutBox, positioning mode
(block/absolute), z_index, optional name, padding, and CSS-like
margin. Implements interfaces::DocraftLoomIVisitorNode so any node can
be walked by a DocraftLoomIVisitor (see Pipeline).
-
class DocraftLoomNode : public docraft::loom::interfaces::DocraftLoomIVisitorNode
Represents a node in the Docraft Loom structure, supporting child-node management and layout features.
This class provides the ability to manage child nodes and store layout information through the use of
LayoutBox. It also inherits frominterfaces::DocraftLoomIVisitorNode, enabling visitor-based operations.Subclassed by docraft::loom::nodes::DocraftLoomBlankLine, docraft::loom::nodes::DocraftLoomImage, docraft::loom::nodes::DocraftLoomList, docraft::loom::nodes::DocraftLoomNewPage, docraft::loom::nodes::DocraftLoomParagraph, docraft::loom::nodes::DocraftLoomShape, docraft::loom::nodes::DocraftLoomStrokedLine, docraft::loom::nodes::DocraftLoomTable, docraft::loom::nodes::DocraftLoomTableCell, docraft::loom::nodes::DocraftLoomText
Public Functions
-
~DocraftLoomNode() override = default
-
int children_count() const
-
std::shared_ptr<DocraftLoomNode> remove_child(int index)
-
std::shared_ptr<const DocraftLoomNode> child(int index) const
-
std::shared_ptr<DocraftLoomNode> edit_child(int index)
-
void clear_children()
-
std::vector<int> paint_order_indices() const
Returns this node’s direct-child indices (0..children_count()-1), stable-sorted ascending by each child’s z_index() — the order in which this node’s own children should be painted (sibling-scoped only, CSS stacking-context style; does not look into grandchildren or any other container’s children).
Stability preserves declaration order among children that share the same z_index.
-
DocraftPositionType position_mode() const
Returns the positioning mode (block flow or absolute).
-
void set_position_mode(DocraftPositionType position_mode)
Sets the positioning mode.
Absolute nodes are placed at explicit_position() during layout and do not advance the parent’s cursor.
-
const Position &explicit_position() const
Returns the explicit position used when position_mode() is kAbsolute.
-
void set_explicit_position(const Position &position)
Sets the explicit position used when position_mode() is kAbsolute.
-
int z_index() const
Returns the paint-order index.
Higher values paint later (on top); default 0 makes every existing node a no-op for z-index sorting.
-
void set_z_index(int z_index)
-
const std::string &name() const
Returns the node’s optional name (empty by default), settable from Craft-language’s
nameattribute for later lookup/debugging.
-
void set_name(const std::string &name)
-
float padding() const
Inset (in points, uniform on every side) between this node’s own box edge and where it lays out its children — e.g.
DocraftLoomRectangle offsets its children’s start position by padding() and grows its own measured size by 2x padding() around them. A no-op default for node types that don’t read it (leaves like Text/Image/Circle).
-
void set_padding(float padding)
-
const DocraftLoomMargin &margin() const
Extra space (in points, per edge) this node asks its parent’s stacking layout to reserve around it, on top of whatever gap the parent’s own spacing()/spacing-like property already adds between children (see DocraftLoomLayoutContainer::spacing()) — e.g.
a
<Title>can set a larger margin() than a plain<Text>/<Paragraph>so it reads as more separated from what follows it, regardless of which container it’s stacked in. Containers combine the touching edges of two adjacent children via max(), not sum (mirrors CSS margin collapsing), so two neighbors both asking for breathing room don’t double up — vertical stacks (Rectangle/VStack) combine bottom/top, horizontal stacks (HStack) combine right/left.
-
void set_margin(float margin)
-
void set_margin(float top, float right, float bottom, float left)
-
inline virtual bool keeps_own_size_in_weighted_slot() const
Whether this node’s own declared size should survive being placed into a weighted stacking layout’s (HStack/VStack) resolved slot, instead of being stretched to fill it.
Default false — filling the slot is the whole point of weight() for most node types (a Rectangle used as a column background, plain text, a nested container). DocraftLoomImage overrides this when it has an explicit width(): stretching an image along only one axis (its own height() is left untouched) distorts it. A plain virtual here lets the layout processor ask any child without a dynamic_cast/type-check chain — a future node type with the same concern just overrides this, no processor edits required.
-
~DocraftLoomNode() override = default
LayoutBox and supporting types
LayoutBox is the per-node accumulator the pipeline fills in, one field per
stage: measured_size (Measure) → frame (Layout) → page_index
(Pagination). Later stages read only what earlier stages wrote — skipping a
stage for a node type breaks whatever reads its field.
-
class LayoutBox
Struct representing a layout box, which encapsulates layout-related data for nodes.
The
LayoutBoxstruct stores the results of the pipeline’s three sequential steps:Measurement step (
measured_size): the calculated size of the node. Ungated — Measure is unconditionally the pipeline’s first pass, so there’s no “too early” caller to guard against.Layout step (
frame): the final position and size to apply. Only readable/ writable with aLayoutProof, mintable only byDocraftLoomLayoutProcessor— code from an earlier stage (Measure) simply cannot obtain one, so reading a frame before Layout has run is a compile error, not a silently wrong 0.Pagination step (
page_index): which page this node landed on. Gated the same way byPageIndexProof, whose constructor additionally requires an existingLayoutProof— so minting one is itself proof that Layout ran first, enforcing the Measure -> Layout -> Pagination order at the type level, not by convention.
This header has no knowledge of the pipeline or of tests:
LayoutProofandPageIndexProofeach name exactly one friend of their own —DocraftLoomLayoutBoxLayoutAccessorandDocraftLoomLayoutBoxPaginationAccessorrespectively (both below) — two narrow, protected-only minting seams, kept separate so that inheriting the ability to seal a frame never also grants the ability to seal a page index, or vice versa. Whoever legitimately needs to mint one kind of proof —DocraftLoomLayoutProcessor(frames),DocraftLoomPaginationProcessor(page indices) in production,docraft::test::utils::LayoutBoxTestAccess(both, for fixtures) in tests — gets there by inheriting the matching accessor, not by being named here individually; adding a new legitimate minter never requires touching this file again.Public Functions
-
LayoutBox() = default
-
inline const Rect &frame(const LayoutProof&) const
-
inline Rect &edit_frame(const LayoutProof &proof)
-
inline std::optional<LayoutProof> layout_proof() const
The proof Layout sealed onto this node, or nullopt if Layout hasn’t run for it yet — the one runtime check in this scheme (whether Layout has happened at all for this specific node), done once per read site instead of trusted implicitly.
-
inline int page_index(const PageIndexProof&) const
-
inline void set_page_index(int index, const PageIndexProof &proof)
-
inline std::optional<PageIndexProof> page_index_proof() const
-
inline int page_index_or_unpaginated() const
Returns the sealed page index, or -1 (the existing “render on every
page” sentinel) if Pagination hasn’t sealed one yet.
Deliberately needs no proof, unlike page_index(const PageIndexProof&): “not yet paginated” has exactly one safe answer here (render everywhere), unlike frame()’s case, where there is no safe placeholder geometry to fall back to — so this accessor can stay ungated without reintroducing the “silently wrong number” failure mode this class exists to close. Used by DocraftLoomRenderingProcessor::should_render(), which must keep working for nodes/tests that never run real Pagination.
-
class LayoutProof
Public Functions
-
LayoutProof(const LayoutProof&) = default
-
LayoutProof &operator=(const LayoutProof&) = default
-
LayoutProof(const LayoutProof&) = default
-
class PageIndexProof
Public Functions
-
PageIndexProof(const PageIndexProof&) = default
-
PageIndexProof &operator=(const PageIndexProof&) = default
-
PageIndexProof(const PageIndexProof&) = default
-
struct Size
Struct representing a size with width and height.
-
struct Rect
Struct representing a rectangle with a position and size.
-
struct DocraftLoomMargin
Per-edge outer spacing (points), CSS-margin-shorthand style.
DocraftLoomText
The base leaf text node — <Text>. Independent bold/italic/underline/
strikeout flags, font family/size, color, alignment, and optional word
wrapping (wrap_width).
-
class DocraftLoomText : public docraft::loom::nodes::DocraftLoomNode
Subclassed by docraft::loom::nodes::DocraftLoomPageNumber, docraft::loom::nodes::DocraftLoomSubtitle, docraft::loom::nodes::DocraftLoomTitle
Public Functions
-
DocraftLoomText()
-
DocraftLoomText(const std::string &text)
-
~DocraftLoomText() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
-
const std::string &text() const
-
void set_text(const std::string &text)
-
const std::string &font_family() const
-
void set_font_family(const std::string &font_family)
-
float font_size() const
-
void set_font_size(float font_size)
-
bool bold() const
-
void set_bold(bool bold)
-
bool italic() const
-
void set_italic(bool italic)
-
bool underline() const
-
void set_underline(bool underline)
-
bool strikeout() const
-
void set_strikeout(bool strikeout)
-
const DocraftColor &color() const
-
void set_color(const DocraftColor &color)
-
TextAlignment alignment() const
-
void set_alignment(TextAlignment alignment)
-
std::string resolved_font_name() const
Resolves the backend-registered font name for font_family() + bold() + italic() (e.g.
“Helvetica” + bold -> “Helvetica-Bold”), falling back to the plain constructed name if it isn’t registered with the backend.
-
float wrap_width() const
Sets the width (in points) this text wraps to.
0 (the default) disables wrapping — text() is measured/drawn as a single line, today’s behavior. Setting a positive value makes DocraftLoomMeasureProcessor word-wrap text() into wrapped_lines() at measure time, and alignment() (including kJustified) is applied per line within this width at render time.
-
void set_wrap_width(float wrap_width)
-
float width() const
The text’s own box width — an alias of wrap_width().
For a text node the wrap box and the alignment box are the same box, and
widthis the name the Craft common-attribute set uses for it. Exposing it under that name is what lets the tree builder’s generic apply_common_attributes() pickwidthup (it gates onrequires { n.set_width(v); }); without it,width="..."on a<Text>was accepted by the parser and then silently dropped, which also leftalignmentresolving against whatever width the parent happened to relay — the whole canvas, for a Canvas child.
-
void set_width(float width)
-
const std::vector<std::string> &wrapped_lines() const
The lines text() was wrapped into, populated by DocraftLoomMeasureProcessor when wrap_width() > 0.
Empty when wrapping is disabled.
-
void set_wrapped_lines(std::vector<std::string> lines)
-
float ascent() const
Distance from the baseline to the top of the line box, populated by DocraftLoomMeasureProcessor at measure time.
Rendering reads this instead of re-querying the text backend, keeping font-metric measurement confined to the measure step.
-
void set_ascent(float ascent)
-
float descent() const
Distance from the baseline to the bottom of the line box, populated by DocraftLoomMeasureProcessor at measure time.
Note
descent is negative, so the space below the baseline is stored as a negative number.
-
void set_descent(float descent)
-
DocraftLoomText()
DocraftLoomTitle / DocraftLoomSubtitle
Thin DocraftLoomText subclasses (<Title>/<Subtitle>) whose
constructors pick larger/bolder defaults and a margin that tracks the
effective font_size — all overridable via ordinary Text attributes.
-
class DocraftLoomTitle : public docraft::loom::nodes::DocraftLoomText
Heading-level text node (
<Title>in Craft Language).A specialized DocraftLoomText whose constructor sets h1-like defaults (larger bold font, and a larger margin() than plain text/paragraphs default to, so a title reads as more separated from what follows it) — any of these are still overridable via explicit attributes, parsed exactly like
<Text>’s (see DocraftTextParser).Public Functions
-
DocraftLoomTitle()
-
~DocraftLoomTitle() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
-
DocraftLoomTitle()
-
class DocraftLoomSubtitle : public docraft::loom::nodes::DocraftLoomText
Sub-heading text node (
<Subtitle>in Craft Language).A specialized DocraftLoomText whose constructor sets h2-like defaults (bold font, and a larger margin() than plain text/paragraphs default to — though smaller than DocraftLoomTitle’s — so it reads as separated from what follows it) — any of these are still overridable via explicit attributes, parsed exactly like
<Text>’s (see DocraftTextParser).Public Functions
-
DocraftLoomSubtitle()
-
~DocraftLoomSubtitle() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
-
DocraftLoomSubtitle()
DocraftLoomPageNumber
<PageNumber/> — a DocraftLoomText subclass that recomputes its display
string from the current/total page count at render time, then delegates to
the same text-rendering path as any other DocraftLoomText.
-
class DocraftLoomPageNumber : public docraft::loom::nodes::DocraftLoomText
Self-updating text node that displays the current page number (and total page count) at render time.
Measured using a fixed placeholder string (mirroring legacy’s DocraftPageNumber) since the real page count isn’t known until pagination completes — Measure/Layout treat it exactly like ordinary text.
Public Functions
-
DocraftLoomPageNumber()
-
~DocraftLoomPageNumber() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
-
inline void set_format(const std::string &format)
Display format applied at render time.
{page}is replaced with the current 1-based page number,{total}with the document’s total page count. Defaults to"{page}".
-
inline const std::string &format() const
-
DocraftLoomPageNumber()
DocraftLoomParagraph
<Paragraph> — groups DocraftLoomText children into one flowing
vertical block with line_spacing, space_before/space_after, and a
default alignment individual children can override.
-
class DocraftLoomParagraph : public docraft::loom::nodes::DocraftLoomNode
Loom node representing a paragraph — a block-level container of text runs.
A
DocraftLoomParagraphgroups one or moreDocraftLoomTextchildren into a single vertical block. During the measure pass, the paragraph accumulates the heights of its children (scaled byline_spacing) and addsspace_before/space_aftermargins. Width is the maximum measured width among its children.Children are added via
DocraftLoomNode::add_child()and are visited recursively by the measure processor before the paragraph’s own size is computed.Public Functions
-
DocraftLoomParagraph()
Constructs a paragraph with default typographic values.
Defaults:
line_spacing = 1.2,space_before = 0,space_after = 0,alignment = TextAlignment::kLeft.
-
~DocraftLoomParagraph() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
Dispatches this node to the visitor’s
visit(DocraftLoomParagraph*)overload.- Parameters:
visitor – Visitor that will process this node.
-
float line_spacing() const
Returns the line spacing multiplier applied to each child’s height.
A value of
1.0means tight (no extra space between lines);1.2is the typographic convention for comfortable reading.- Returns:
Line spacing multiplier.
-
void set_line_spacing(float line_spacing)
Sets the line spacing multiplier.
- Parameters:
line_spacing – Multiplier applied to each child’s height. Must be > 0.
-
float space_before() const
Returns the vertical space added above the paragraph (in points).
- Returns:
Space before the paragraph in points.
-
void set_space_before(float space_before)
Sets the vertical space above the paragraph.
- Parameters:
space_before – Space in points. Use 0 for no extra margin.
-
float space_after() const
Returns the vertical space added below the paragraph (in points).
- Returns:
Space after the paragraph in points.
-
void set_space_after(float space_after)
Sets the vertical space below the paragraph.
- Parameters:
space_after – Space in points. Use 0 for no extra margin.
-
TextAlignment alignment() const
Returns the default text alignment for this paragraph’s content.
- Returns:
Text alignment enum value.
-
void set_alignment(TextAlignment alignment)
Sets the default text alignment for this paragraph’s content.
Individual
DocraftLoomTextchildren may override this with their own alignment.- Parameters:
alignment – Text alignment to apply.
-
DocraftLoomParagraph()
DocraftLoomImage
<Image> — PNG/JPEG from a file path, or raw pixel data injected via the
template engine (base64-decoded). Dimensions are never auto-derived from the
file; set width/height explicitly.
-
class DocraftLoomImage : public docraft::loom::nodes::DocraftLoomNode
Public Functions
-
DocraftLoomImage() = default
-
~DocraftLoomImage() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
-
const std::string &path() const
-
void set_path(const std::string &path)
Sets the image path and derives format() from its file extension, mirroring legacy’s DocraftImage::set_path.
Width/height are never auto-derived from the file here (legacy’s own dimension-detection is a stub returning {0,0}).
-
ImageFormat format() const
-
const std::vector<unsigned char> &raw_data() const
-
int raw_pixel_width() const
-
int raw_pixel_height() const
-
bool has_raw_data() const
-
void set_raw_data(const std::vector<unsigned char> &data, int pixel_width, int pixel_height)
-
float width() const
-
void set_width(float width)
-
float height() const
-
void set_height(float height)
-
virtual bool keeps_own_size_in_weighted_slot() const override
Whether this node’s own declared size should survive being placed into a weighted stacking layout’s (HStack/VStack) resolved slot, instead of being stretched to fill it.
Default false — filling the slot is the whole point of weight() for most node types (a Rectangle used as a column background, plain text, a nested container). DocraftLoomImage overrides this when it has an explicit width(): stretching an image along only one axis (its own height() is left untouched) distorts it. A plain virtual here lets the layout processor ask any child without a dynamic_cast/type-check chain — a future node type with the same concern just overrides this, no processor edits required.
-
DocraftLoomImage() = default
DocraftLoomBlankLine / DocraftLoomNewPage
Pure spacers/markers — <Blank/> reserves vertical space (default 12pt,
about one line); <NewPage/> forces an unconditional page break during
pagination. Neither one paints anything.
-
class DocraftLoomBlankLine : public docraft::loom::nodes::DocraftLoomNode
-
class DocraftLoomNewPage : public docraft::loom::nodes::DocraftLoomNode
Zero-size marker node for a forced page break.
Carries no visible content — DocraftLoomPaginationProcessor::paginate_body() recognizes it and unconditionally advances to a fresh page whenever it’s encountered among the body’s top-level children, instead of running the normal fits-on-this-page check.
Public Functions
-
DocraftLoomNewPage() = default
-
~DocraftLoomNewPage() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
-
DocraftLoomNewPage() = default