Assemble
This article demonstrates how to assemble multiple RTF files into a single RTF or DOCX file using rtflite.
Prerequisites
To enable DOCX support, install rtflite with the docx extra:
pip install rtflite[docx]
Define input files
from pathlib import Path
from rtflite import assemble_docx, assemble_rtf, concatenate_docx
input_files = [
"example-ae-summary.rtf",
"example-efficacy.rtf",
]
Assemble into RTF
assemble_rtf(input_files, "combined-rtf.rtf")
Assemble into DOCX with toggle fields
assemble_docx(input_files, "combined-docx.docx")
Open combined-docx.docx in Word and refresh fields to resolve the
INCLUDETEXT placeholders. Without opening in Word, the placeholders
appear as Error! Reference source not found.
because python-docx does not evaluate those fields.
Note
The assemble_docx workflow intentionally mirrors how medical writers
paste hyperlinks into CSR sections. After the link is in place,
toggling fields or running Update Field in Word reconnects the
assembled file to the original TLFs without additional copy-paste work.
This hybrid approach, which relies on Word's field codes, is documented
in the r4csr book.
Another example to assemble into DOCX with mixed orientation pages (portrait, landscape):
assemble_docx(
input_files,
"combined-mixed.docx",
landscape=[False, True]
)
Open combined-mixed.docx in Word and update fields to pull in the portrait
and landscape tables. Until the fields are refreshed, you will see the same
Error! Reference source not found. placeholders.
Assemble into DOCX without toggle fields
RTFDocument.write_docx (added in rtflite 2.2.0) creates DOCX files for
individual rtflite tables directly.
Use concatenate_docx (added in rtflite 2.3.0, powered by python-docx)
to concatenate the DOCX outputs when you need final files without manual
field refreshes, similar to assemble_rtf.
Generate example DOCX tables:
from importlib.resources import files
import polars as pl
import rtflite as rtf
ae_path = files("rtflite.data").joinpath("adae.parquet")
ae = pl.read_parquet(ae_path)
ae_summary = (
ae.group_by(["TRTA", "AEDECOD"])
.agg(pl.len().alias("n"))
.pivot(values="n", index="AEDECOD", on="TRTA")
.fill_null(0)
.sort("AEDECOD")
)
# Two portrait tables
portrait_doc1 = rtf.RTFDocument(
df=ae_summary.head(12),
rtf_title=rtf.RTFTitle(text="Adverse Events (Portrait)"),
rtf_column_header=rtf.RTFColumnHeader(
text=[
"Adverse Events",
"Placebo (N=86)",
"Xanomeline High Dose (N=84)",
"Xanomeline Low Dose (N=84)",
],
),
rtf_body=rtf.RTFBody(col_rel_width=[4, 2, 2, 2]),
)
portrait_doc1.write_docx("portrait-ae-1.docx")
portrait_doc2 = rtf.RTFDocument(
df=ae_summary.slice(12, 12),
rtf_title=rtf.RTFTitle(text="Adverse Events (Portrait, Part 2)"),
rtf_column_header=rtf.RTFColumnHeader(
text=[
"Adverse Events",
"Placebo (N=86)",
"Xanomeline High Dose (N=84)",
"Xanomeline Low Dose (N=84)",
],
),
rtf_body=rtf.RTFBody(col_rel_width=[4, 2, 2, 2]),
)
portrait_doc2.write_docx("portrait-ae-2.docx")
# One landscape table
landscape_doc = rtf.RTFDocument(
df=ae_summary.head(20),
rtf_page=rtf.RTFPage(
orientation="landscape",
nrow=10,
border_first="dashed",
border_last="dashed",
),
rtf_title=rtf.RTFTitle(text="Adverse Events Summary - Landscape Layout"),
rtf_column_header=rtf.RTFColumnHeader(
text=[
"Adverse Events",
"Placebo (N=86)",
"Xanomeline High Dose (N=84)",
"Xanomeline Low Dose (N=84)",
],
),
rtf_body=rtf.RTFBody(col_rel_width=[4, 2, 2, 2]),
)
landscape_doc.write_docx("landscape-ae.docx")
Concatenate two portrait DOCX files
Use concatenate_docx to start from the first DOCX (avoids a blank leading
page) and add a new section per file so each starts on its own page with the
correct orientation.
portrait_files = [
"portrait-ae-1.docx",
"portrait-ae-2.docx",
]
concatenate_docx(
portrait_files,
"combined-python-docx.docx",
)
Concatenate portrait + landscape DOCX files
files_with_orientation = [
("portrait-ae-1.docx", False),
("landscape-ae.docx", True),
]
concatenate_docx(
[path for path, _ in files_with_orientation],
"combined-python-docx-mixed.docx",
landscape=[is_landscape for _, is_landscape in files_with_orientation],
)