The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
simPDF builds multi-page PDF reports on
top of R’s built-in graphics device (pdf() /
cairo_pdf()). It is deliberately small: base R only, no
knitr/LaTeX/pandoc toolchain, so
a report is written in a single fast pass.
Its defining idea is measured layout. Every block of
content reports its real width and height (via
strwidth()/strheight() on the open device);
the vertical cursor advances by that measured height; and pages break
automatically. This removes the text/table overlap that plagues reports
built with hand-computed row/column coordinates – content never collides
no matter how many parameters, rows, or random effects it has.
Interactive fillable forms (AcroForm CRFs) are out of scope and
handled by the sibling package pdfCRF; simPDF
is for reports and figures.
A report is a document (sp_new()), a
rectangular content frame (frame_set()),
and a list of blocks run through the flow
engine (flow_run()). Coordinates are PDF points
(1/72 inch); doc$W/doc$H are the page
width/height.
doc <- sp_new(out(), paper = "letter")
flow_run(doc, list(
block_para("Hello, simPDF", size = 16, font = 2), # font: 1 plain, 2 bold, 3 italic
block_rule(),
block_para("This paragraph is measured and wraps to the frame width automatically.
Add as much text as you like; the engine breaks lines and pages for you.")
))
sp_close(doc)flow_run() measures each block, draws it if it fits,
otherwise starts a new page. NULL entries are dropped, so
conditional blocks (if (cond) block_*()) are fine.
block_para() word-wraps and aligns prose.
block_pre() renders a character vector one physical line
each – ideal for capture.output() dumps – and auto-shrinks
the font so the widest line still fits the frame width.
doc <- sp_new(out())
flow_run(doc, list(
block_para("Left / centre / right alignment", size = 12, font = 2),
block_para("centred", align = "center"),
block_para("right", align = "right"),
block_para("A fixed-point summary printed verbatim:", size = 11, font = 2),
block_pre(capture.output(summary(1:100)))
))
sp_close(doc)block_table() renders a data.frame/matrix with
column widths measured from the content, numeric
columns right-aligned, a header that repeats on every
page, and automatic row-boundary page
splitting. Row names become a leading label column.
big <- data.frame(ID = 1:200, TIME = round(runif(200, 0, 24), 2),
DV = round(rlnorm(200), 3), WT = round(runif(200, 50, 90), 1))
doc <- sp_new(out())
frame_set(doc, top = doc$H - 45, bottom = 45, left = 45, right = doc$W - 45)
flow_run(doc, list(block_para("Input data", 13, font = 2), block_table(big, size = 9)))
cat("pages:", doc$page_no, "\n")
#> pages: 4
sp_close(doc)block_matrix() handles a wide matrix by
splitting its columns into groups that each fit the page (row labels
repeated in every group). This is what fixes the classic “print a 14x14
covariance matrix” overlap.
M <- matrix(round(rnorm(144, 0, .4), 3), 12, 12,
dimnames = list(paste0("Eta", 1:12), paste0("Eta", 1:12)))
doc <- sp_new(out()); frame_set(doc, top = doc$H-45, bottom = 45, left = 45, right = doc$W-45)
flow_run(doc, list(block_para("Omega matrix (12 x 12)", 13, font = 2),
block_matrix(M, size = 9)))
sp_close(doc)block_plot() draws one base-R plot into a reserved band
inline with the text (the expression is captured
unevaluated and run at draw time), so a heading, a plot, and a table can
share a page and paginate together.
set.seed(1); pred <- rlnorm(80); dv <- pred * exp(rnorm(80, 0, .3))
doc <- sp_new(out()); frame_set(doc, top = doc$H-45, bottom = 45, left = 45, right = doc$W-45)
flow_run(doc, list(
block_keep(list(
block_para("Goodness of fit", 12, font = 2),
block_plot({ plot(pred, dv); abline(0, 1, lty = 3) }, height = 220)))
))
sp_close(doc)For a whole page of panels (e.g. one diagnostic grid
per subject), use sp_figure_page(), which manages the page
outside the flow engine. Draw exactly prod(mfrow) panels
per call.
sp_width() / sp_height() expose the exact
metrics the engine uses (AFM-exact for the core fonts). The tracer
records every drawn text box so you can assert that a report has
zero overlaps – handy in package tests.
doc <- sp_new(out()); frame_set(doc, top = doc$H-45, bottom = 45, left = 45, right = doc$W-45)
sp_trace(doc, TRUE)
flow_run(doc, list(block_para("Measured & overlap-free", 14, font = 2),
block_table(head(mtcars))))
cat("Courier '12345' at 10pt =", sp_width(doc, "12345", size = 10, family = "mono"), "pt\n")
#> Courier '12345' at 10pt = 30 pt
cat("overlapping text boxes =", nrow(sp_overlaps(doc)), "\n")
#> overlapping text boxes = 0
sp_close(doc)block_flow_diagram() lays out a tree of nodes
(e.g. NONMEM model runs) as boxes sized to their text, tidily arranged
and scaled to fit – so more models shrink the diagram
instead of cluttering it.
nodes <- data.frame(
id = c("001","002","003","004","005"),
parent = c("", "001", "001", "002", "002"),
label = c("Run 001\nOFV 1000", "Run 002\nOFV 980", "Run 003\nOFV 975",
"Run 004\nOFV 960", "Run 005\nOFV 955"),
stringsAsFactors = FALSE)
doc <- sp_new(out()); frame_set(doc, top = doc$H-45, bottom = 45, left = 40, right = doc$W-40)
flow_run(doc, list(block_para("Model development flow", 14, font = 2),
block_flow_diagram(nodes, height = 400)))
sp_close(doc)The typical pattern (as used by nmw): keep your
parsing/statistics, and replace the PDF-generation with a
blocks list plus one flow_run(), wrapping any
diagnostic plots in block_plot() /
sp_figure_page():
my_report <- function(x, file = "report.pdf") {
# ... compute tables/values from x ...
doc <- sp_new(file, paper = "letter", family = "Courier")
frame_set(doc, top = doc$H - 45, bottom = 45, left = 45, right = doc$W - 45)
flow_run(doc, list(
block_para("Summary", size = 16, font = 2), block_rule(),
block_table(my_table),
block_para("Diagnostics", size = 12, font = 2),
block_plot(plot(my_x, my_y), height = 220)
), footer = block_para("CONFIDENTIAL", size = 8, align = "center"))
sp_close(doc)
}Add simPDF to your Imports. Because
simPDF uses only the graphics device, your diagnostic plots
keep working unchanged – they are simply placed by the flow engine
instead of by hand-computed coordinates, and never overlap.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.