Skip to contents

In Japanese

This article is the English entry point for people who want to extend or contribute to rtfreporter. It gives the big picture; the two deep-dive documents are linked below (each opens in English with a Japanese toggle).

🌐 Design specification: Internal class design (S3)

Design principles

  • S3 throughout. Every object is a plain S3 list, with no external OOP framework. Objects are therefore dput()-able and serialise cleanly with saveRDS().
  • Zero third-party runtime dependencies. Imports: holds only packages that ship with R (grDevices, grid, methods, stats, utils). Every optional integration (gt, gtsummary, rtables/tern, flextable, huxtable, ggplot2, …) lives in Suggests: and is reached through .need_pkg(), which errors helpfully when the package is absent and warns when it is too old.
  • twips are the only length unit used internally (1 inch = 1440 twips).
  • Immutable, functional helpers. Internal mutators return a new copy of the object rather than mutating in place.
  • β€œnone” means omit. A border side of "none" emits no RTF command at all, rather than an explicit zero-width border.

Repository layout

rtfreporter/
β”œβ”€β”€ R/
β”‚   β”œβ”€β”€ pipe-composition.R   # public pipe API: rtf_document/section/tables/figures
β”‚   β”œβ”€β”€ rtftable.R           # rtftable() S3 object + normalisation helpers
β”‚   β”œβ”€β”€ rtfplot.R            # rtfplot() S3 object
β”‚   β”œβ”€β”€ rtfreport.R          # internal rtfreport document object + rtf_header/footer
β”‚   β”œβ”€β”€ generate_rtfreport.R # the RTF renderer (S3 doc -> .rtf text)
β”‚   β”œβ”€β”€ rtf_border.R         # border hierarchy (S3)
β”‚   β”œβ”€β”€ rtf_table_style.R    # shared table style (S3, snapshot semantics)
β”‚   β”œβ”€β”€ col_header.R         # unified column-header API (col_cell / rtf_col_header)
β”‚   β”œβ”€β”€ as_rtftables.R       # table-object -> list of rtftable pages (the hub)
β”‚   β”œβ”€β”€ as_rtftable.R        # single-page convenience wrapper
β”‚   β”œβ”€β”€ gt_adapter.R         # gt / gtsummary metadata extraction
β”‚   β”œβ”€β”€ rtables_adapter.R    # rtables / tern metadata extraction
β”‚   β”œβ”€β”€ paginate.R           # (deprecated) pagination generic + internal core
β”‚   β”œβ”€β”€ blank_rows.R         # blank-row specifications + attribute reader
β”‚   └── ...
β”œβ”€β”€ inst/resources/          # admin-tunable RTF command templates & defaults
β”œβ”€β”€ tests/testthat/          # the test suite (S3, edition 3)
└── vignettes/               # user guides (root) + articles/ (site-only)

The object model

rtf_document          (public S3, built by the pipe API)
  β”œβ”€β”€ contents[]      one entry per page: rtftable | rtfplot
  β”œβ”€β”€ titles[]        per-page title block (character vector)
  β”œβ”€β”€ footnotes[]     per-page footnote block
  └── sections[]      header/footer per page-range (via from_page)

rtftable              one table (single- or multi-data.frame)
  β”œβ”€β”€ data / data_list
  β”œβ”€β”€ col_header(_list)   character vector | stacked spanning rows
  β”œβ”€β”€ col_spec            per-column align / bold / italic / indent / border
  β”œβ”€β”€ cell_styles         per-cell overrides (bold/italic/underline/indent)
  β”œβ”€β”€ border              rtf_table_border (zones)
  └── width / height / padding / valign fields

rtfplot               one embedded PNG/JPEG figure

rtf_document is the public, pipe-friendly surface. Internally it is converted to an rtfreport object by .pipe_doc_to_rtfreport(), validated by .rtfreport_validate(), and handed to the renderer.

The public classes and their constructors

Class Constructor(s) Role
rtf_document rtf_document() The document being assembled (pages + sections + document-wide settings).
rtftable rtftable(), as_rtftable(), as_rtftables() One table (or one page of a paginated table).
rtfplot rtfplot() One embedded figure.
rtf_border rtf_border(), TRUE, rtf_border(top = TRUE) (the older rtf_border_top() / _bottom() / _box() / _none() / rtf_border_with() are deprecated) A border for a selection: its outer edges plus the rules between the rows (inside_h) and between the cells (inside_v) inside it. What it applies to is decided by where you attach it.
rtf_table_border rtf_table_border(), rtf_border_tfl() β€” both deprecated The five-zone map. Superseded: attach an rtf_border() to rtftable(border = ) for the whole table, or to style_zone() for one kind of row; the TFL preset is border = "tfl" or rtf_table_style_tfl().
rtf_table_style rtf_table_style(), rtf_table_style_with(), rtf_table_style_tfl() A reusable style (borders + padding + row-height defaults), captured by snapshot at construction.
rtf_col_header rtf_col_header(), col_cell() Multi-row / spanning column headers.

Not every public function is a constructor. rtf_config() is a configurator, not a constructor: it takes an rtf_document, updates only the non-NULL fields, and returns a copy of the same rtf_document class (it sets no new class) – just like the other pipe verbs (rtf_tables(), rtf_section(), …). Likewise rtf_header() / rtf_footer() return plain (unclassed) lists describing a section’s header / footer: to change a row, make it again.

rtf_document vs rtfreport. rtf_document is the public object you build and hold via the pipe. It is converted to a separate internal class, rtfreport, only at render time (by generate_rtfreport()); see Internal class design (S3). You do not construct or handle rtfreport directly.

print() on an rtftable previews the laid-out cells in the console (format() gives those lines, summary() the metadata); plot() draws a wireframe of a table’s borders or of the document.

The rendering pipeline

rtf_document()  |>  rtf_section()  |>  rtf_tables() / rtf_figures()
        β”‚
        β–Ό   .pipe_doc_to_rtfreport()         (adapter: pipe API -> rtfreport)
   rtfreport
        β”‚   .rtfreport_validate()            (auto-default section, checks)
        β–Ό
   generate_rtfreport(doc, "out.rtf")        (renderer in generate_rtfreport.R)
        β”‚   .render_rtftable() / .render_rtfplot()
        β–Ό
   .rtf  text file

The renderer is pure string assembly driven by templates in inst/resources/rtf_commands.R; size/spacing defaults live in inst/resources/rtfreporter_defaults.R so they can be tuned without touching code.

The table-object hub: as_rtftables()

as_rtftables() is the single entry point that turns an external table object into a list of rtftable pages. It supports gt_tbl, gtsummary tables, rtables/tern (VTableTree), and plain data.frame/tibble (or a list of any of these).

Every source is reduced to one common intermediate β€” a kwargs list β€” which the rest of the pipeline consumes identically:

list(
  data            = <data.frame>,   # the rendered body
  col_header      = <chr | stacked spanning rows>,
  col_spec        = <list(list(col=, align=, ...), ...)>,
  cell_styles     = <list per row, or NULL>,
  titles_block    = <chr | NULL>,   # -> page title
  footnotes_block = <chr | NULL>    # -> page footnote
)

gt_adapter.R and rtables_adapter.R each produce exactly this shape. as_rtftables() then (optionally) sorts the body rows before the split (sort_by / sort_desc, so grouping sees the ordered rows), paginates the body (.paginate_df()), slices cell_styles per page, replicates the shared header/width/spanning metadata onto every page, and attaches the title/footnote blocks as the rtf_titles / rtf_footnotes attributes that rtf_tables() reads. A column can be carried through the split for grouping/ordering and then hidden from the rendered pages (drop_cols): the columns stay present for .paginate_df(), then .apply_col_drop() removes them and reindexes every position-indexed argument (header incl.Β spanning, col_spec, widths, row_title, cell_styles) to the remaining columns just before each page’s rtftable() is built.

Because the adapters all converge on one shape, adding a new source type is a small, well-contained job β€” see Adding a table-object adapter.

Where to read more