This document describes the complete C++ renderer architecture used by ksTFL to convert JSON report specifications into deterministic DOCX output.
R API
-> src/rcpp_bindings.cpp
-> kstfl::Renderer
Renderer pipeline
Parse -> Resolve -> Model -> Measure -> Paginate -> Emit -> Package
Core modules
json_parser.cpp : spec/template/data parsing
style_resolver.cpp : style cascade + page/width resolution
logical_table.cpp : header grid + logical row stream + styleRows actions
font_scanner.cpp : system font discovery + fallback resolution
text_measurer.cpp : HarfBuzz-based deterministic measurement
paginator.cpp : vertical and horizontal pagination
docx_*.cpp : OOXML emission for document parts
zip_writer.cpp : DOCX ZIP packaging
┌───────────────────────────────────────────────────────────────────────┐
│ Phase 1: Parse │
│ parse_template_bundle() │
│ parse_spec_json_string() -> TFLDocument │
│ parse_data_json_string() -> DataTable │
│ resolve figure media paths for Figure specs │
│ │
│ Phase 1b: Enforce isColBreak layout constraints │
│ for specs with isColBreak columns: force │
│ allow_row_break_across_pages=false, repeat_header_on_each_page=true │
│ (warn if template values were overridden) │
└───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐
│ Phase 2: Initialize text subsystem │
│ FontCache: load configured font dirs and fallback font │
│ TextMeasurer(font_cache) │
└───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐
│ Phase 3: Per-spec processing │
│ │
│ 3a) Resolve │
│ StyleResolver(template, spec_styles) │
│ resolve_page_config() │
│ resolve_table_width() │
│ resolve_column_widths() │
│ │
│ 3b) Model │
│ LogicalTableBuilder::build() │
│ - header grid + vMerge metadata │
│ - logical data rows │
│ - styleRows actions (c_style, c_clear, c_merge, c_glue, │
│ c_addrow, c_pageBreak) │
│ │
│ 3c) Measure │
│ Header rows: vMerge-aware 2-pass height balancing │
│ Body cells: TextMeasurer::measure_plain() │
│ - inline markup parsing │
│ - HarfBuzz shaping │
│ - deterministic wrapping and line-height math │
│ │
│ 3d) Paginate │
│ Paginator::paginate() │
│ - build_segments(): split by isColBreak, repeat isID columns │
│ - compute_row_heights(): baseline full-table heights │
│ - compute_segment_column_widths(): segment width scaling │
│ - compute_segment_row_heights(): per-segment row heights │
│ - compute static blocks: titles/subtitles/header/footer/notes │
│ - vertical fill + LastPage post-pass (deterministic mode only) │
│ - row-break mode keeps natural flow; no LastPage reshuffling │
│ │
│ 3e) Dedupe restoration │
│ Restore deduped values at page boundaries │
└───────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐
│ Phase 4: Emit and package DOCX │
│ DocxEmitter::emit() │
│ - document.xml, styles.xml, rels, header/footer parts │
│ - page/section assembly, table/header/body emission │
│ - optional TOC │
│ ZipWriter: package all OOXML parts into .docx │
└───────────────────────────────────────────────────────────────────────┘
Defined primarily in src/kstfl/types.h.
Length,
PageConfig, MarginsFontProps,
ParagraphProps, TableCellProps,
StyleDef, StylesTemplateColumnSpec,
StubColumn, TextGroup,
LogicalCell, LogicalRow,
HeaderGridPageSlice,
HorizontalSegment, PaginationResultFontCache reads font metrics from OS/2 tables, with
hhea as fallback, to match Word line-height behavior.TextMeasurer uses HarfBuzz shaping for deterministic
run widths and wrapping.btLr, tbRl) are
handled explicitly.isColBreak, repeats isID columns in each
segment, and scales non-ID columns to the remaining width. When
isColBreak is active, the renderer also enforces
allow_row_break_across_pages = FALSE and
repeat_header_on_each_page = TRUE.trHeight values remain
exact.The renderer applies styles in this effective order (later layers override earlier ones unless marked structural/non-overridable):
operator<=> (three-way comparison) for
LengthMergeable) constraining style merge
templatesconstexpr std::array lookup tables for OOXML enum
conversionsstd::ranges::sort, std::ranges::any_of,
std::ranges::transformusing enum in switch statementsstd::to_chars for double formatting[[nodiscard]] on all pure/value-returning
functionssrc/Makevars always compiles vendored HarfBuzz,
FreeType, and minizip sources under src/vendor/pkg-config probe or system-library fallback is used
on Linuxsrc/Makevars.win with
Rtools-provided static librariesspec json + data json + template json
-> Renderer::render_from_strings()
-> StyleResolver::resolve_*()
-> LogicalTableBuilder::build()
-> TextMeasurer::measure_plain()
-> Paginator::paginate()
-> segment row heights
-> DocxEmitter::emit_table_row()
-> exact trHeight from segment.row_heights
-> ZipWriter::close()
-> output.docx