Export a Screenplay to PDF

Render a parsed script straight to a PDF file, laid out with the same margins, indents, and monospace font a printed screenplay uses.

Install the Extra

PDF export depends on fpdf2, which fountain-py ships as an optional extra rather than a core dependency:

$ pip install "fountain-py[pdf]"

PDFRenderer raises ImportError at construction time if fpdf2 is not installed. The error message names the preceding install command.

Render a File

from fountain import FountainParser
from fountain.renderers.pdf.renderer import PDFRenderer

document = FountainParser().parse_file("screenplay.fountain")

pdf_bytes = PDFRenderer().render_bytes(document)

with open("screenplay.pdf", "wb") as f:
    f.write(pdf_bytes)

render_bytes() returns PDF bytes, not text, so open the output file in binary mode. With no arguments, PDFRenderer() uses the Letter page size and a standard screenplay layout.

Choose a Page Size

fountain.renderers.pdf.geometry ships three presets. LETTER (8.5” x 11”) is the default, A4 is 210mm x 297mm, and HALF_LETTER (5.5” x 8.5”) is a half-size booklet page. Pass one to the geometry argument:

>>> from fountain import FountainParser
>>> from fountain.renderers.pdf.renderer import PDFRenderer
>>> from fountain.renderers.pdf.geometry import HALF_LETTER
>>> document = FountainParser().parse("INT. HOUSE - DAY\n\nJohn enters.")
>>> pdf_bytes = PDFRenderer(geometry=HALF_LETTER).render_bytes(document)
>>> pdf_bytes.startswith(b"%PDF")
True

If none of the presets fit, build a custom size with PageGeometry directly. Here is a 6” x 9” trade paperback page with a binding offset reserved on the left margin:

from fountain.renderers.pdf.geometry import PageGeometry

geometry = PageGeometry(width_in=6, height_in=9, margin_in=1, binding_offset_in=0.25)
pdf_bytes = PDFRenderer(geometry=geometry).render_bytes(document)

geometry.text_width_in must work out positive; a margin and binding offset that consume the whole page width raises ValueError when you render.

Choose a Layout Profile

fountain.renderers.pdf.profile carries the font and the per-element indent/width. It positions scene headings, action, character cues, parentheticals, dialogue, and transitions on the page. PDFRenderer() defaults to the SCREENPLAY profile: Courier 12pt, with character cues indented 2.5” from the text block’s left edge and dialogue indented 1.5”:

>>> from fountain.elements import ElementType
>>> from fountain.renderers.pdf.profile import SCREENPLAY
>>> SCREENPLAY.element_layout[ElementType.CHARACTER].left_indent_in
2.5
>>> SCREENPLAY.element_layout[ElementType.DIALOGUE].left_indent_in
1.5

To change the font or retarget an indent, build a custom profile. Use LayoutProfile and ElementLayout, then pass it to the profile argument:

from types import MappingProxyType
from fountain.elements import ElementType
from fountain.renderers.pdf.profile import ElementLayout, LayoutProfile

profile = LayoutProfile(
    font_name="Courier",
    font_size_pt=10,
    element_layout=MappingProxyType(
        {
            ElementType.CHARACTER: ElementLayout(left_indent_in=2.0, width_in=3.0),
            ElementType.DIALOGUE: ElementLayout(left_indent_in=1.0, width_in=4.0),
        }
    ),
)

pdf_bytes = PDFRenderer(profile=profile).render_bytes(document)

Element types missing from a custom profile’s element_layout fall back to a zero-indent block spanning the full text width.

What Gets Omitted

Notes, sections, synopses, and boneyard comments are writer-only tools, so PDF export omits them, matching the other renderers. A forced page break (===) starts a new PDF page. Dual dialogue renders as two single-column blocks in sequence, the left side first and the right side second. It matches PlainTextRenderer rather than laying the two sides out side by side.