Reports: headers, footers, titles, footnotes and tokens
Source:vignettes/articles/reports.Rmd
reports.RmdThe report spec is the document around a table, a listing or a
figure: the page, the running header and footer, the titles and
footnotes, a watermark. tfl_report_code() writes the
rtfreporter calls it stands for
(doc <- rtf_document(...), rtf_section(),
rtf_tables(), rtf_titles(),
rtf_footnotes()), and tfl_report() makes the
same document from a plan or pages in hand.
The sheets
| Sheet | One row per | Holds |
|---|---|---|
report |
report |
type (table, listing,
figure, user), file
({output_id}.rtf), program, font sizes,
page_header / page_footer,
watermark, a figure’s size |
page |
report | paper, orientation, margins, the document’s text defaults;
font and font_size_half_points (the study’s
row is the company’s) |
header, footer, titles,
footnotes
|
line |
line, left, center,
right
|
tokens |
token |
name, value: a token of one’s own,
{STUDY} in any cell above |
plus the study keys output_path (where the
RTF files go) and program_dir (where the programs are, for
{PROGRAM}).
Written once for the study
The output_id rule makes the study’s look a matter of
default rows (a blank output_id), written
once; each report adds or replaces only what is its own. On the line
sheets the line number is the key:
- the study’s page header is default lines 1 and 2, and each report’s titles follow on its own lines 3, 4, …;
- a run line 99 in the default footer closes every report;
- a report’s line replaces the default line of the same number, and a
report’s line that says
(none)takes it out.
df <- function(...) data.frame(..., stringsAsFactors = FALSE)
spec <- tfl_table_spec(
study = c(output_path = "output", program_dir = "programs"),
report = df(output_id = c(NA, "T-14-1-1", "T-14-3-1"),
file = c("{output_id}.rtf", NA, NA),
watermark = c("DRAFT", NA, NA)),
header = df(output_id = c(NA, NA, "T-14-1-1", "T-14-1-1", "T-14-3-1"),
line = c(1, 2, 3, 4, 3),
left = c("{SPONSOR}", "Protocol: {STUDY}", NA, NA, NA),
center = c(NA, NA, "Table 14.1.1", "Demographics",
"Table 14.3.1"),
right = c("{CUTOFF}", "Page {PAGE} of {TOTAL_PAGES}",
NA, NA, NA)),
footer = df(output_id = c(NA, "T-14-3-1"), line = c(99, 99),
left = c("{PROGRAM} Generated on: {DATETIME}", "(none)")),
footnotes = df(output_id = "T-14-3-1", line = 1,
left = "MedDRA version {MEDDRA}."),
tokens = df(output_id = c(NA, NA, NA, NA, "T-14-3-1"),
name = c("SPONSOR", "STUDY", "CUTOFF", "MEDDRA", "CUTOFF"),
value = c("ACME Pharma", "ABC-123", "Data cut-off: 01JUN2026",
"27.1", "Data cut-off: 01SEP2026")))The demographics table gets the study’s header with its own two title lines, the study’s run line, and the study’s tokens:
cat(tfl_report_code(spec, "T-14-1-1", content = "plan"), sep = "\n")
doc <- rtf_document(
watermark = "DRAFT",
program_fallback = "programs/T-14-1-1",
tokens = list(
SPONSOR = "ACME Pharma",
STUDY = "ABC-123",
CUTOFF = "Data cut-off: 01JUN2026",
MEDDRA = "27.1"
),
file = "output/T-14-1-1.rtf"
) |>
rtf_section(
header = rtf_header(
list(
c(l = "{SPONSOR}", r = "{CUTOFF}"),
c(l = "Protocol: {STUDY}", r = "Page {PAGE} of {TOTAL_PAGES}"),
c(c = "Table 14.1.1"),
c(c = "Demographics")
)
),
footer = rtf_footer(list(c(l = "{PROGRAM} Generated on: {DATETIME}")))
) |>
rtf_tables(plan)The AE table has its own cut-off (its CUTOFF row
replaces the study’s), a footnote, and no run line (its line 99 says
(none)):
cat(tfl_report_code(spec, "T-14-3-1", content = "plan"), sep = "\n")
doc <- rtf_document(
watermark = "DRAFT",
program_fallback = "programs/T-14-3-1",
tokens = list(
SPONSOR = "ACME Pharma",
STUDY = "ABC-123",
MEDDRA = "27.1",
CUTOFF = "Data cut-off: 01SEP2026"
),
file = "output/T-14-3-1.rtf"
) |>
rtf_section(
header = rtf_header(
list(
c(l = "{SPONSOR}", r = "{CUTOFF}"),
c(l = "Protocol: {STUDY}", r = "Page {PAGE} of {TOTAL_PAGES}"),
c(c = "Table 14.3.1")
)
)
) |>
rtf_tables(plan) |>
rtf_footnotes(list(list(c(l = "MedDRA version {MEDDRA}."))))Tokens
A cell of the header, footer, titles or footnotes may say a token:
-
rtfreporter’s own, filled when the file is written:
{PAGE},{TOTAL_PAGES},{PROGRAM}(or{PROGRAM_FULL}, its absolute path) and{DATETIME}.{PROGRAM}is the report sheet’sprogramjoined toprogram_dir; withprogramblank, it is the file name of the program that runs (and the report’s id only when none is found). -
one’s own, from the
tokenssheet: a name in upper case (a letter, then letters, digits or_), not one of rtfreporter’s. A blankoutput_idis the study’s default; a report’s row of the same name replaces it, and(none)takes it out. They go tortf_document(tokens = ).
A value that is the same for every report – the sponsor, the
protocol, the cut-off – is written once, as a study row of the
tokens sheet; the header and footer say
{SPONSOR} and never repeat it.
Each report’s own tokens
A report’s header, footer, titles and footnotes may also say its own tokens, so that one header serves every report:
-
{OUTPUT_ID}– the report’s id; -
{OUTPUT_LABEL}– made from the id: “T-14-1-1” is “Table 14.1.1” (the kind from the first letter, the number from the first digit, separators made dots);tokensrowsOUTPUT_KIND_TABLE/_LISTING/_FIGUREgive other words; -
{OUTPUT_TITLE},{OUTPUT_POPULATION},{OUTPUT_SECTION},{STUDY_ID}– blank unless thetokenssheet gives them (tflplanner writes them from a TOC).
A band line left with nothing but empty tokens (and brackets:
<{OUTPUT_POPULATION}>) is not printed.
tfl_report_tokens() shows the values a report fills them
with:
sp <- tfl_table_spec(
report = df(output_id = c("T-14-1-1", "T-14-3-1"), type = "table"),
page = df(output_id = c(NA, "T-14-3-1"), font = c("Courier New", NA),
font_size_half_points = c("18", "16")),
header = df(output_id = NA, line = c("1", "2", "3", "4", "5"),
left = c("{COMPANY}", "PROTOCOL: {STUDY_ID}", NA, NA, NA),
center = c(NA, NA, "{OUTPUT_LABEL}", "{OUTPUT_TITLE}",
"<{OUTPUT_POPULATION}>"),
right = c(NA, "Page {PAGE} of {TOTAL_PAGES}", NA, NA, NA)),
tokens = df(output_id = c(NA, NA, "T-14-1-1", "T-14-1-1", "T-14-3-1"),
name = c("COMPANY", "STUDY_ID", "OUTPUT_TITLE",
"OUTPUT_POPULATION", "OUTPUT_TITLE"),
value = c("Sample Pharma", "ABC-123",
"Demographic Characteristics", "Safety Analysis Set",
"Treatment-Emergent Adverse Events")))
tfl_report_tokens(sp, "T-14-1-1")
#> COMPANY OUTPUT_LABEL
#> "Sample Pharma" "Table 14.1.1"
#> OUTPUT_TITLE OUTPUT_POPULATION
#> "Demographic Characteristics" "Safety Analysis Set"
#> STUDY_ID
#> "ABC-123"The company’s font, and the study’s setup written once
The page sheet’s font and
font_size_half_points set the document’s font. The study’s
row (a blank output_id) is the company’s; a report’s row
that says its own wins (the AE table above is a size smaller). Blank:
rtfreporter’s own (Courier, 9 pt).
What every report shares – that font, the study’s tokens, the running
header and footer – tfl_report_setup_code() writes once, as
the code a study’s report programs source:
cat(tfl_report_setup_code(sp), sep = "\n")
options(
rtfreporter.font = "Courier New",
rtfreporter.font_size_half_points = 18
)
options(
rtfreporter.tokens = list(COMPANY = "Sample Pharma", STUDY_ID = "ABC-123")
)
study_header_lines <- list(
c(l = "{COMPANY}"),
c(l = "PROTOCOL: {STUDY_ID}", r = "Page {PAGE} of {TOTAL_PAGES}"),
c(c = "{OUTPUT_LABEL}"),
c(c = "{OUTPUT_TITLE}"),
c(c = "<{OUTPUT_POPULATION}>")
)
study_header <- rtf_header(study_header_lines, drop_empty_rows = TRUE)A report’s program written with
tfl_report_code(setup = TRUE) then says only what is its
own – its tokens, a font of its own – and the study’s values are in one
place, in the definition and in the code:
cat(tfl_report_code(sp, "T-14-3-1", content = "plan", setup = TRUE), sep = "\n")
doc <- rtf_document(
default_format = rtf_default_format(font_size_half_points = 16),
program_fallback = "T-14-3-1",
tokens = list(
OUTPUT_LABEL = "Table 14.3.1",
OUTPUT_TITLE = "Treatment-Emergent Adverse Events",
OUTPUT_POPULATION = ""
),
file = "T-14-3-1.rtf"
) |>
rtf_section(header = study_header) |>
rtf_tables(plan)A program standing alone (setup = FALSE, the default)
says them itself; the file is the same either way.
Page patterns
Some reports want a page that differs from the study’s: a compact
landscape page, another footer. The report sheets (report,
page, header, footer,
titles, footnotes, tokens) hold
named page patterns besides the blank rows: rows whose
output_id is @ and a name
(@Compact), saying only what differs from Standard (the
blank rows). A report chooses one in report$pattern (blank:
Standard). Its own rows win over its pattern’s, and the pattern’s over
Standard’s, by each sheet’s usual rules; (none) takes a
line out at either level. What a pattern changes is written in its
reports’ programs; Standard stays in the study’s setup.
The document
tfl_report() makes the document from the content – a
plan, rtftable pages, or a plain data frame as here;
tfl_report_path() says where it goes
(output_path and the report’s file):
content <- data.frame(Statistic = "n", `Drug X` = "80", Control = "78", check.names = FALSE)
doc <- tfl_report(spec, "T-14-1-1", content = content)
tfl_report_path(spec, "T-14-1-1")
#> [1] "output/T-14-1-1.rtf"
generate_rtfreport(doc, tfl_report_path(spec, "T-14-1-1"), overwrite = TRUE)The example workbook
report.xlsx in the package is a study’s report spec for
the five example tables: the sponsor and protocol on lines 1 and 2 of
the default header, each table’s titles on its lines 3 to 6, a run line
99 in the default footer, and a PK table that puts its run line under
the table instead (page_footer = FALSE).
ex <- system.file("extdata", "ard-spec", package = "tflspec")
rs <- tfl_read_report_spec(file.path(ex, c("report.xlsx", "study.xlsx")))
rs$report[, c("output_id", "type", "file", "page_footer", "note")]
#> output_id type file page_footer note
#> 1 <NA> table {output_id}.rtf <NA> the study's defaults
#> 2 LB <NA> <NA> <NA> one section per parameter
#> 3 PK <NA> <NA> FALSE the run line goes under the tableA company’s TOC as report specs
A study’s list of outputs usually exists before any table, in the
company’s own workbook. tfl_read_toc() reads it through a
map of its columns, and gives the report,
titles and footnotes sheets – every report
exists, titled, before a table is written:
toc <- file.path(tempdir(), "toc.csv")
write.csv(df(
`Output No.` = c("", "T-14-1-1", "T-14-3-1", "L-16-2-7"),
Title = c("14.1 Demographics", "Demographic Characteristics",
"Treatment-Emergent Adverse Events", "Listing of Serious AEs"),
`Analysis Set` = c("", "Safety Analysis Set", "Safety Analysis Set",
"Safety Analysis Set"),
Datasets = c("", "ADSL", "ADSL, ADAE", "ADAE"),
check.names = FALSE), toc, row.names = FALSE)
sp <- tfl_read_toc(toc, map = list(output_id = "Output No.", title = "Title",
population = "Analysis Set",
datasets = "Datasets"))
sp$report[, c("output_id", "type")]
#> output_id type
#> 1 T-14-1-1 table
#> 2 T-14-3-1 table
#> 3 L-16-2-7 listing
sp$titles[, c("output_id", "line", "center")]
#> output_id line center
#> 1 T-14-1-1 1 Demographic Characteristics
#> 2 T-14-1-1 2 Safety Analysis Set
#> 3 T-14-3-1 1 Treatment-Emergent Adverse Events
#> 4 T-14-3-1 2 Safety Analysis Set
#> 5 L-16-2-7 1 Listing of Serious AEs
#> 6 L-16-2-7 2 Safety Analysis Set
attr(sp, "sections")
#> T-14-1-1 T-14-3-1 L-16-2-7
#> "14.1 Demographics" "14.1 Demographics" "14.1 Demographics"
attr(sp, "datasets")
#> T-14-1-1 T-14-3-1 L-16-2-7
#> "ADSL" "ADSL | ADAE" "ADAE"The kind of each report comes from its id (T-,
L-, F-) when the TOC does not say it; a
heading row (“14.1 Demographics”) is each report’s section. Write the
result with
tfl_write_specs("spec/report.xlsx", report = sp) (or
tfl_write_report_spec()), and open it in Excel or in
tflplanner.