← Documentation

Print to PDF

Enterprise / Pro add-on

Print to PDF (and print to paper) produces a polished document from a form view, with full control over the cover page, header, footer, and layout. It’s ideal for records, approvals, and customer copies.

1. What you can configure

AreaOptions
Cover pageOptional logo, title, subtitle, and body content.
HeaderCustom HTML content and a logo.
FooterDisclaimer text, printed-by name, print date, and page numbers.
TabsOption to convert tab sets into section headings in the printout.
Empty fieldsOption to hide fields with no value.
AttachmentsImage attachments (png, gif, jpg, jpeg, bmp, webp) render inline as actual pictures, with the filename shown underneath as a caption; non-image attachments (documents, zips, etc.) still show as a filename link. Controlled by “Show image attachments as images” (toggle, default on) and “Attachment image max height (px)” (default 200; width scales proportionally, capped to the content column width).

Per-image logo height. The Logo URL — on both the cover page and the header — accepts an h or height query-string parameter, e.g. …/logo.png?h=100, which overrides the configured Logo height (px) for that specific image. This is handy when the Logo URL is set via a token (e.g. [LogoField]) that resolves to different logo files per item or site — each stored URL can carry its own height instead of sharing one fixed value. With no such parameter, the configured height is used as before.

2. Every element type is included

The printout isn’t limited to fields — every element on the form is rendered into the document:

  • Field values and vLookup tables.
  • HTML sections, Rich text sections and Custom HTML rows.
  • Reusable Content blocks — rendered just as they appear on the live form.
  • Repeating Sections — as a full table, including calculated columns, lookup-derived columns and the totals row (Count / Sum / Average / Min / Max).
  • Related Items — as a table of the related items and their configured fields.
  • Power BI reports — printed as a labeled link to the live report (an interactive report can’t be captured into a static PDF).

Enhanced fields print as what you see, not what’s stored

Most field enhancements only change how a field is edited — which input control appears, or an added scan/capture button. The value a completed form displays is already plain text, so it prints correctly with no special handling: treeview lookup rendering, text-as-dropdown, pattern/mask, GPS capture and the barcode scanner all fall into that group.

Three needed real work, because their on-screen appearance is nothing like the value in the list:

EnhancementHow it prints
Star ratingAs filled and empty stars (★ / ☆), matching the count and color shown on screen — not the bare number stored in the field.
Signature captureAs the actual signature image. Multi-signature setups print every signature, with their names and timestamps — not the raw stored data as a wall of text.
Custom render functionAs whatever your function last rendered on the live form.

Why the custom render function is handled differently. The other two are recomputed from the stored value, so they print identically no matter what is on screen. A custom render function can’t be re-run safely for print — it may depend on other live page state, run asynchronously, or have side effects that shouldn’t fire twice — so DFFS reads what it already rendered instead. In practice that means the printout reflects the form as it stood when you printed it.

One rough edge: if a custom-render field also has a field description configured, the description text can be pulled into the printed output alongside the custom content. There’s no separate markup to tell them apart.

3. Accurate visibility

When generating the document, Print to PDF automatically triggers the form’s onclick rules while iterating over tabs. This means the printout shows only what would actually be visible at runtime — conditional logic is respected, so hidden fields don’t leak into the PDF. Elements hidden via a rule’s Show element / Hide element action (and everything nested inside them) are left out too.

Repeating Sections and Related Items don’t have their own targetable id, so to hide one from the PDF with a rule, hide its enclosing row or column — the same as hiding it on the live form.

4. Generating a document

The feature runs on demand from the display form. Configure the cover/header/footer once in the form configuration, then produce the PDF (or send to paper) whenever you need a record.

File name. The File name template setting (e.g. {ID} - {Title}) is resolved — tokens replaced and characters that aren’t valid in a filename stripped — and used as the suggested filename in the browser’s native “Save as PDF” dialog.

Only the Save as PDF button produces the document. The print layout lives in the page as a hidden, print-only container, and it is emitted exclusively through the Save as PDF flow. A plain browser print — Ctrl+P, or File > Print — never includes it. That matters because a native print can’t be intercepted or cancelled by the page: keeping the document out of it is what makes the tab-rule pass, the resolved print date and file name, and a blocking dffs_PrePrintAction (section 6) impossible to bypass.

5. Worked example — a branded approval record

Goal: From a completed approval form, produce a clean PDF for the records.

  1. In the form’s Print to PDF configuration:
    • Cover page: company logo, title “Purchase Approval”, subtitle with the request ID.
    • Header: small logo + document name.
    • Footer: disclaimer text, printed-by name, print date, and page numbers.
    • Convert tabs to section headings: on.
    • Hide empty fields: on.
  2. Open a completed item in the display form and generate the PDF.

The result is a multi-page document with a branded cover, each tab rendered as a section, no empty fields, and a footer recording who printed it and when.

6. Custom JS hooks

Two Custom JS callbacks let you take control of the export. Both are display-form only, because Save as PDF is, and both are scoped to the Custom JS that defines them — a vLookup child form’s hook never fires for the parent form.

Refuse to print an incomplete form

dffs_PrePrintAction(ctx) runs the moment Save as PDF is clicked — before the file name template is resolved, before the tab rules run, and before the document is rendered. It’s awaited, so it can be async. Return false (or a Promise resolving to false) to cancel: nothing is printed and the browser’s print dialog never opens.

DFFS deliberately shows no message of its own when a print is cancelled — exactly like dffs_PreSaveAction — so tell the user why nothing happened. dffs_showDialog() is the simplest way:

window.dffs_PrePrintAction = async function (ctx) {
  if (ctx.values.Status !== "Approved") {
    dffs_showDialog({
      title: "Cannot create PDF",
      body: "This request must be approved before it can be printed."
    });
    return false;
  }
  if (!ctx.values.SignedBy) {
    dffs_showDialog({ title: "Cannot create PDF", body: "The form is not signed yet." });
    return false;
  }
};

Mask or reformat fields in the printed document only

ctx.values is a mutable copy of the current field values, keyed by internal name. Anything you write there affects only the PDF — the form on screen is untouched. (To change the form itself, use setFieldValue() as before.)

window.dffs_PrePrintAction = function (ctx) {
  // Mask an internal note in the printed document, but leave it visible on the form
  ctx.values.InternalNotes = "[Not included in the printed version]";
  // Reformat a value for print
  ctx.values.Amount = "USD " + ctx.values.Amount;
};

Returning an object of { internalName: value } works as an alternative — it’s merged on top of ctx.values:

window.dffs_PrePrintAction = function (ctx) {
  return { InternalNotes: "[Redacted]" };
};

The two uses combine: validate first, then sanitize, then let the print continue.

The sanitized values are used consistently everywhere in the produced document — the printed field values, the {Token}s in the cover page, header and footer, and the {Token}s in the file name template. A masked field can’t leak through the suggested filename.

ctx contains:

PropertyDescription
valuesMutable copy of the current field values keyed by internal name. Changes affect the PDF only.
itemDataCopy of the raw list item (ID, Created, Author, …).
settingsCopy of the effective Print to PDF settings, including anything applied via dffs_setPdfSettingsOverride. It’s a copy, so changing it has no effect.
formTypeThe form type the configuration belongs to.
modeAlways "disp" — Save as PDF is display-form only.

To print a field empty, set it to "" or null. Don’t delete the key — a deleted key falls back to the live field value rather than blanking it. With Hide empty fields enabled, a field set to "" drops out of the document entirely.

ctx.values holds each value in the shape DFFS renders from, which isn’t always a plain string — a person field, a hyperlink field or a multi-choice field holds a structured value. Writing a plain string works for text, note, choice and number fields; for other types, check the printed result before relying on it.

If your function throws, the print is cancelled and the error is logged to the console. That’s deliberate: a validation or sanitizing step that crashed must not silently let an unchecked document through.

Log that an export happened

dffs_PostPrintAction(ctx) runs after the print/save dialog has closed. It’s fire-and-forget — not awaited. ctx carries documentTitle (the resolved file name), printDate, values (what was actually printed, including any sanitizing above), itemData, settings, formType and mode.

window.dffs_PostPrintAction = async function (ctx) {
  await dffs_spAdd("PDF export log", {
    Title: ctx.documentTitle,
    Item: String(ctx.itemData.ID),
    Exported: new Date().toISOString()
  });
};

It can’t tell a saved PDF from a dismissed dialog. Browsers expose no signal for which button the user pressed, so this fires either way. Treat it as “a PDF export was started” rather than as an audit trail of files that actually exist. This is a browser limitation, not a DFFS one, and there is no workaround.

7. Tips

  • Hide empty fields keeps records tidy when forms have many optional fields.
  • Convert tabs to section headings turns a wizard/tabbed form into a readable linear document.
  • Because onclick rules run during generation, test your conditional logic once in the PDF to confirm the right fields appear.
  • Your Custom CSS applies to the printed output. The print markup carries the standard .dffs-field and .dffs-field-label classes rather than inline styles, so CSS you already wrote against those classes takes effect in the PDF without needing !important. Note that .dffs-required is not applied in the print layout — required-field state isn’t meaningful in a finished document — so don’t rely on it for print styling.
  • Overriding PDF settings per site. If one form configuration is shared across several sites and each needs its own PDF setup — a different logo, say, or PDF enabled in one place only — use the Custom JS function dffs_setPdfSettingsOverride(). It patches the configured PDF settings one level deep per section, so you change only what differs.