Skip to contents

Builds a figure display, the "F" in TFL. figure() takes a ggplot, a recorded base-R plot, a zero-argument drawing function, or a path to a PNG / JPG file, and surrounds it with the same canonical submission chrome as a table: up to four centred title lines, footnotes, and the per-page header / footer drawn from the active preset(). Pass a list to emit one figure per page in a single file.

Usage

figure(
  plot,
  titles = NULL,
  footnotes = NULL,
  width = NULL,
  height = NULL,
  halign = "center",
  valign = "middle",
  dpi = 300,
  meta = NULL
)

Arguments

plot

The figure to display. One of: a ggplot object; a recorded base plot from grDevices::recordPlot(); a zero-argument function that draws to the active device when called; a length-1 path to a .png, .jpg, or .jpeg file; or a list of any of these for a multi-page figure.

Tip: a list may mix kinds freely — a ggplot, a recorded plot, and a PNG path can share one multi-page figure.

titles

Title lines above the figure. <character> | NULL. One element per line, up to four; same {glue} interpolation and md() / html() inline formatting as tabular(). NULL draws no titles.

footnotes

Footnote lines below the figure. <character> | NULL. One element per line; same interpolation and inline formatting as titles. NULL draws no footnotes.

width

Drawn image width in inches. <numeric(1)> | NULL. NULL fills the full printable width.

height

Drawn image height in inches. <numeric(1)> | NULL. NULL fills the body box height (the printable height minus the title and footnote chrome). Set a smaller value to leave vertical slack for valign to place the image within.

halign

Horizontal placement in the content box. <character(1)>. One of:

  • "left"

  • "center" (default)

  • "right"

valign

Vertical placement in the content box. <character(1)>. One of:

  • "top"

  • "middle" (default)

  • "bottom"

Note: continuous backends (HTML / Markdown) render the figure contained to the viewport with no fixed page height, so valign is a no-op there; the paged backends honour it exactly.

dpi

Raster resolution for plot inputs. <numeric(1)>. Resolution in dots per inch for PNG rasterisation. Ignored for file inputs (passed through unchanged) and for vector PDF targets.

meta

Per-page token data frame. <data.frame> | NULL. Multi-page only: one row per plot, whose columns become {token} values in that page's titles / footnotes. Ignored (with a warning) for a single-plot figure.

Value

A figure_spec. Pass it to emit() to write a file, or print it to preview the figure inline.

Details

Two-axis placement. By default the image FILLS the body content box — the full printable width by the box height (the printable height minus the title / footnote chrome). Pass an explicit width / height smaller than the box and halign / valign place the image within the resulting slack, independently — horizontally (left / center / right) and vertically (top / middle / bottom), both defaulting to centred. Paged backends (RTF / PDF / DOCX) honour valign exactly against the content-box height; with the box-filling default there is no vertical slack, so valign only bites once you set a shorter height. The continuous backends (HTML / Markdown) render the figure responsively, contained to the viewport, so halign still applies but valign is a no-op there.

Format-aware rasterisation. Plot inputs render to vector PDF for .pdf / .tex targets and to PNG at dpi for every other backend; file inputs pass through byte-for-byte. No raster work happens until emit().

Styling the chrome. A figure carries the same chrome surfaces as a table, so style() and the preset() cosmetic knobs reach its titles, footnotes, and page header / footer — e.g. style(fig, font_size = 14, .at = cells_title()) or preset(fig, colors = list(footnotes = c(text = "grey40"))). A figure has no body, column headers, or subgroup banner, so styling those surfaces is an error. Inter-section spacing follows the preset spacing knob (title, footnote), exactly like a table.

See also

Terminal verb: emit() (write the figure to a file).

Shared chrome: preset() / set_preset() (page geometry, fonts, header / footer), style() (per-figure title / footnote / page-chrome styling), tabular() (the table sibling).

Class predicate: is_figure_spec().

Examples

# ---- Example 1: a single base-R figure with submission chrome ----
#
# A zero-argument drawing function is the simplest portable input: it
# draws to whatever device the backend opens. Here, subjects enrolled
# per treatment arm from the bundled BigN frame, wrapped in the
# canonical title block and a population footnote.
arms <- cdisc_saf_n[cdisc_saf_n$arm_short != "Total", ]

draw_enrollment <- function() {
  barplot(
    arms$n,
    names.arg = arms$arm_short,
    ylab = "Subjects enrolled",
    col = "grey70"
  )
}

fig <- figure(
  draw_enrollment,
  titles = c(
    "Figure 14.1.1",
    "Subjects Enrolled by Treatment Arm",
    "Safety Population"
  ),
  footnotes = "Total enrolled: N = 254."
)
fig

 

Figure 14.1.1

Subjects Enrolled by Treatment Arm

Safety Population

 

Figure

Total enrolled: N = 254.

# ---- Example 2: one figure per page from a list ---- # # A list input emits one figure per page in a single file. Each arm # gets its own page; the kinds may mix (a ggplot, a recorded plot, or # a PNG path could share the list). Bottom-anchored here to show the # two-axis placement. draw_arm <- function(i) { force(i) function() { barplot(arms$n[i], names.arg = arms$arm_short[i], col = "grey70") } } per_arm <- figure( lapply(seq_len(nrow(arms)), draw_arm), titles = c("Figure 14.1.2", "Enrollment by Arm, One Page per Arm"), valign = "bottom" ) per_arm

 

Figure 14.1.2

Enrollment by Arm, One Page per Arm

 

Figure
Figure
Figure