PDF Generation: Setup and TroubleshootingBUSINESS
Turn every form submission into a branded PDF and (optionally) attach it to the notification email. This page walks through the two-checkbox setup, what the generated PDF looks like, where files land, how non-Latin scripts are handled, and the four failure modes that cover almost every PDF-related support ticket.
Open the form in the builder, open Form Settings, find the PDF Generation section, check Generate PDF of submission and (optionally) Attach PDF to notification email. Save the form. Submit a test entry. A PDF lands in wp-content/uploads/formforge/pdf/ and (if you checked the attach box) arrives as an attachment on the notification email.
How PDF generation works
FormForge Pro uses the mPDF library to render each submission as a PDF document. The PDF layout comes from a built-in template that reads the form’s field labels and values, wraps them in a clean two-column table with a header (optional site logo, form title, accent color bar) and a footer (submission date, entry ID, custom footer text), and outputs to wp-content/uploads/formforge/pdf/ with a unique filename per entry.
The generated PDF surfaces one main way for now:
- Notification email attachment: when Attach PDF to notification email is checked, the PDF gets attached to every notification the form sends out. Recipients click the attachment in their mail client and open the PDF directly.
- Developer download endpoint: an AJAX endpoint at
admin-ajax.php?action=rfp_download_pdf&entry_id=Xre-generates the PDF for any entry on demand. Admin users can hit it directly; a security token (see developer hooks) lets you surface a public download link to a submitter without exposing the AJAX endpoint to anyone else. There’s no built-in Download button in the Entries admin yet, so today the attach-to-notification-email path is the primary way both you and your customers see the PDF.
Enable PDF generation on a form
Confirm your license tier is Business or higher
PDF Generation is a Business-tier feature. Pro-tier and Personal-tier licenses can toggle the checkboxes in the form settings, but the actual PDF generation code doesn’t load, so nothing happens on submit. Upgrade at FormForge pricing if needed.
Confirm the mPDF library is installed
Go to FormForge > Settings and look for the PDF status line. If it reads PDF generation is ready, you’re set. If it reads PDF generation requires the mPDF library. Please run “composer install” in the plugin directory or contact support, the library ships with FormForge Pro but wasn’t installed for your deployment; see the troubleshooting section below.
Open the form in the builder
Go to FormForge > All Forms and click the form name to open the drag-and-drop builder.
In the Form Settings panel, click the Advanced tab
The Form Settings panel is on the right side of the builder and is visible by default (whenever you’re not editing a specific field). Along the top of the panel are five tabs: General, Notifications, Confirmation, Integrations, and Advanced. Click Advanced and scroll to the PDF Generation section.
Check “Generate PDF of submission”
Enables auto-generation on every form submission. The PDF is written to wp-content/uploads/formforge/pdf/ immediately after the entry saves.
Optionally check “Attach PDF to notification email”
If you also want the PDF to arrive as an email attachment on every outgoing notification, check this box too. The two checkboxes are independent: you can generate PDFs without attaching them (admin download only), or attach without generating (not useful, since there’s nothing to attach). The normal setup is both boxes checked.
Save the form
Click Save Form at the top right of the builder. Submit a test entry to confirm the PDF arrives.
What the generated PDF looks like
The default template renders as a portrait-orientation A4 page with three regions:
| Region | Contents |
|---|---|
| Header | Optional logo (top-left, max 180px wide, 60px tall). Form title in the accent color. Accent-color bar underlines the header. |
| Body | Two-column table of field labels (left) and submitted values (right). Every non-empty field appears in order. File uploads render the filename. Star ratings render as filled and empty star characters plus the numeric score. NPS scores render with the Promoter / Passive / Detractor category. URL and email values render as clickable links. Layout-only fields (Section, HTML, Page Break) and hidden fields are skipped. |
| Footer | Submission date, entry ID, and optional custom footer text. Small font, muted color. |
The form-settings UI exposes just the two enable checkboxes today. Every other PDF setting (page size, orientation, accent color, header logo URL, custom footer text, PDF password protection) has a sensible default baked in but is only tunable via the filter hooks below. Defaults: A4 portrait, accent color #2271b1, no logo, no password, submission date and entry ID in the footer.
Where PDFs are stored and how long they last
Every generated PDF lives at:
wp-content/uploads/formforge/pdf/form-{form_id}-entry-{entry_id}-{random}.pdfThe random suffix is eight alphanumeric characters, so the exact filename isn’t guessable by anyone who happens to know the form and entry IDs. FormForge also drops .htaccess and index.php files into the directory on first use, so the files can’t be enumerated via directory listing.
A daily cleanup job removes any PDF older than one hour from the working directory. That’s intentional: PDFs are always re-generatable from the entry data via the AJAX download endpoint, so the on-disk copy is treated as a short-lived cache rather than a long-term archive. If you need long-term archival, either attach the PDF to the notification email at generation time (email inbox becomes the archive), or hit the download endpoint and store the file wherever fits your retention policy.
Hitting the download endpoint for an entry re-runs mPDF against the current entry data plus the current form settings. That means if you change the accent color or logo in the form’s PDF filter hooks after entries have been submitted, older entries will download with the new branding, not the branding at the time of submission. This is usually desirable (consistent branding across old submissions) but if you need historic-branding fidelity, snapshot the PDF at generation time by attaching it to the notification email.
Handling non-Latin scripts (Arabic, Chinese, Japanese, Cyrillic, etc.)
mPDF renders each character by looking up its Unicode code point in the currently-selected font. If the font doesn’t include a glyph for a given character, the PDF shows a box or a question mark in that position.
FormForge ships with the DejaVu Sans font family as the default, which covers:
- Latin (English, Spanish, French, German, Italian, Portuguese, and most European languages)
- Greek
- Cyrillic (Russian, Ukrainian, Bulgarian, Serbian, and other Cyrillic-script languages)
- Armenian, Georgian
- Basic diacritics and math symbols
If your form collects submissions in scripts outside DejaVu’s coverage (Arabic, Hebrew, Thai, Chinese, Japanese, Korean, Devanagari), you’ll see boxes. The fix has two parts:
Drop a compatible font file into the FormForge fonts directory
The directory is wp-content/uploads/formforge/pdf-fonts/. Any .ttf or .otf font file placed there is discoverable by mPDF. For CJK (Chinese / Japanese / Korean), Google’s Noto CJK family is a common free choice. For Arabic and Hebrew, Amiri and Frank Ruehl are good starting points.
Register the font with mPDF via the rfp_mpdf_config filter
Add a small snippet in your theme’s functions.php or a custom plugin:
add_filter( 'rfp_mpdf_config', function( $config ) {
$config['fontdata']['notosans'] = array(
'R' => 'NotoSansCJK-Regular.ttf',
'B' => 'NotoSansCJK-Bold.ttf',
);
return $config;
} );The R and B keys are Regular and Bold weights. Add I for italic and BI for bold-italic if you have those files too.
mPDF’s autoScriptToLang picks it up automatically
FormForge’s default mPDF config enables autoScriptToLang and autoLangToFont, so mPDF will automatically switch to your registered font for runs of non-Latin text within the same PDF. You don’t need to set the default font manually.
Set up a test form with one text field, submit a value in the non-Latin script you care about, and generate the PDF. If characters render correctly there, they’ll render correctly in every future submission on any form.
Troubleshooting the four common failures
1. “PDF generation requires the mPDF library”
The FormForge Pro zip should ship with the mPDF library pre-bundled in the vendor/ directory, so you shouldn’t normally see this error. If you do: your host or deployment process is stripping the vendor directory during install (common on git-based deploys and Bedrock-style setups that .gitignore vendor/). Fix: run composer install inside the wp-content/plugins/formforge-pro/ directory, or ask your host to include vendor/ in the deployment. Confirm with FormForge > Settings showing “PDF generation is ready.”
2. “Could not create PDF directories”
The web server doesn’t have write permission on wp-content/uploads/. This is a general WordPress issue, not a FormForge-specific one; the WordPress Media Library uploads would also fail. Fix: contact your host to fix the permissions on wp-content/uploads/, or if you have SSH access, run chmod 755 wp-content/uploads/ and confirm the WordPress admin user (usually www-data, apache, or your PHP-FPM pool user) owns the directory.
3. Non-Latin characters render as boxes or question marks
Font coverage issue. See the non-Latin scripts section above for the two-step fix: drop the font file into wp-content/uploads/formforge/pdf-fonts/, register it via rfp_mpdf_config. mPDF picks it up automatically after that.
4. PDF generation is very slow, or fails with a timeout
mPDF is memory-hungry, and forms with many fields, embedded images, or non-Latin script (which triggers additional font loading) can push memory usage past PHP’s limits. Two levers:
- Raise
memory_limitto 256M (or 512M for very large forms with images). Check your current value in Tools > Site Health > Info under Server. Raise viaphp.ini,.htaccess, or your host’s control panel. - Raise
max_execution_timeto 60 seconds for the same reason. Default 30 seconds is usually enough for simple forms but not for anything with multiple images or long non-Latin text runs.
If simple forms without images or non-Latin script are still slow, that’s abnormal. Enable WP_DEBUG_LOG, submit a test entry, then check wp-content/debug.log for entries prefixed FormForge Pro PDF Error: which surface the exact mPDF exception. Send that to support for help.
Gather: (a) the exact status shown in FormForge > Settings for PDF availability, (b) the PDF Generation section settings from the form, (c) contents of wp-content/debug.log filtered for lines starting with “FormForge Pro PDF Error”, (d) whether other forms on the same site can generate PDFs correctly. Send to support and we can dig in.
Developer hooks
Three filter and action hooks let you customize the PDF output beyond what the Form Settings UI exposes.
rfp_mpdf_config filter
Runs before the mPDF instance is created. Lets you override any mPDF configuration option (page size, margins, default font, fontdata registration, PDF/A compliance mode, custom stylesheets, and more). Signature: apply_filters( 'rfp_mpdf_config', $config, $entry_id, $form_id, $settings ).
add_filter( 'rfp_mpdf_config', function( $config, $entry_id, $form_id, $settings ) {
$config['margin_top'] = 25;
$config['margin_bottom'] = 20;
return $config;
}, 10, 4 );rfp_mpdf_before_write action
Fires with the mPDF instance immediately before the main HTML body is written to it. Use for cover pages, watermarks, custom fonts, or SetHeader / SetFooter overrides. Signature: do_action( 'rfp_mpdf_before_write', $mpdf, $entry_id, $form_id, $settings ).
add_action( 'rfp_mpdf_before_write', function( $mpdf, $entry_id, $form_id, $settings ) {
$mpdf->SetWatermarkText( 'CONFIDENTIAL' );
$mpdf->showWatermarkText = true;
}, 10, 4 );rfp_mpdf_after_write action
Fires with the mPDF instance immediately after the main HTML body is written. Use for appending signature pages, terms and conditions, quiz results, or any additional content that should come after the field-value table. Signature: do_action( 'rfp_mpdf_after_write', $mpdf, $entry_id, $form_id, $settings ).
Every hook is called with the mPDF instance (or config array), the entry ID, the form ID, and the PDF settings array for the form. That gives you enough context to conditionally customize the PDF based on which form or which entry is being generated. For example, only add a watermark to forms with a specific ID, or only use CJK fonts if the entry data contains CJK characters.
FAQ
How do I enable PDF generation on a FormForge Pro form?
PDF Generation is a Business-tier feature. Open the form in the builder, open Form Settings, find the PDF Generation section, check Generate PDF of submission to auto-create a PDF for every entry. Optionally check Attach PDF to notification email to include the PDF as an attachment in outgoing notifications. Save the form.
Where do generated PDFs get saved?
Every generated PDF is written to wp-content/uploads/formforge/pdf/ with a filename like form-42-entry-158-a1b2c3d4.pdf. The directory is protected by an auto-generated .htaccess with “deny from all” plus an empty index.php so files aren’t listable directly. An AJAX endpoint at admin-ajax.php?action=rfp_download_pdf handles authenticated admin downloads and, with a security token, public-share download links.
How long are generated PDFs kept?
A daily cleanup job removes any PDF older than one hour from the working directory and the mPDF temp directory. If a customer needs the PDF long-term, either attach it to the notification email at generation time (email inbox becomes the archive), or hit the AJAX download endpoint and archive the file yourself. The endpoint regenerates the PDF from entry data on demand, so cleanup deletes the cached file, not the ability to recover a copy.
The PDF shows boxes instead of Arabic / Chinese / Japanese / Cyrillic characters. Why?
The mPDF library needs the appropriate font file to render each script. FormForge ships with the DejaVu Sans font family which covers Latin, Greek, Cyrillic, Armenian, Georgian, and a few others. Scripts outside DejaVu’s coverage (Arabic, Hebrew, Thai, Chinese, Japanese, Korean, Devanagari) require additional font files. Drop the font file (a .ttf file) into wp-content/uploads/formforge/pdf-fonts/ and use the rfp_mpdf_config filter to register it under fontdata. mPDF’s autoScriptToLang setting will then automatically pick it up for any non-Latin runs in the submitted data. Full walkthrough in the non-Latin scripts section.
I enabled PDF generation but no PDF appears. Why?
Four things to check in order. (1) Is your license tier at Business or higher? PDF generation is gated at Business. (2) Is the mPDF library installed? Check FormForge > Settings for a “PDF generation is ready” status; if it reads “PDF generation requires the mPDF library,” run composer install in the plugin directory or contact support. (3) Is wp-content/uploads/ writable by the web server? If not, PDF creation fails silently with “Could not create PDF directories.” (4) Check wp-content/debug.log for entries prefixed “FormForge Pro PDF Error:” which surface the exact mPDF exception.
Can I customize the PDF template beyond the default header / body / footer?
Yes, via three filter and action hooks: rfp_mpdf_config (customize the mPDF configuration array), rfp_mpdf_before_write (fires with the mPDF instance before the main HTML is written, useful for cover pages, watermarks, custom fonts), and rfp_mpdf_after_write (fires after the main HTML is written, useful for signatures, terms and conditions, or additional pages). See the developer hooks section for signatures and examples.
Why is PDF generation slow?
mPDF is memory-intensive and can be slow on forms with many fields or large images. Two levers: (1) raise the PHP memory_limit to 256M (128M is usually not enough for PDFs with images) and max_execution_time to 60 seconds via php.ini or your host’s control panel. (2) Use the rfp_mpdf_config filter to disable expensive features like autoScriptToLang if your forms only ever contain Latin characters. Truly slow generation is worth investigating; if you’re seeing multi-second delays on simple forms, contact support.
Can I use PDF generation with multi-page forms?
Yes. The PDF template renders every field the visitor submitted regardless of which form page they were on. Page Break fields themselves don’t appear in the PDF (they’re layout markers, not data). The PDF is a flat, print-friendly summary of everything the visitor entered across all pages of a wizard.