--- name: pdf-generation-patterns description: "Generating PDF documents from Salesforce using Visualforce renderAs='pdf', PageReference.getContentAsPDF(), and ContentVersion storage. Covers the Flying Saucer rendering engine constraints, inline CSS requirements, server-side data loading, LWC PDF limitations, and programmatic PDF attachment to records. NOT for Quote PDF template customization (use apex/quote-pdf-customization). NOT for OmniStudio DocGen document generation. NOT for Salesforce Reports exported as PDF." category: apex salesforce-version: "Spring '25+" well-architected-pillars: - Security - Reliability - Performance triggers: - "how do I generate a PDF from a Visualforce page in Salesforce and save it to Files" - "my Visualforce PDF is blank or broken — JavaScript not running and external stylesheets not loading" - "how to programmatically create a PDF and attach it to a record using Apex" - "LWC component needs to output a PDF — what is the supported pattern" - "how to store a generated PDF as a ContentVersion linked to a Salesforce record" tags: - pdf-generation - visualforce - renderAs-pdf - flying-saucer - pageReference - contentVersion - contentDocumentLink - inline-css - apex inputs: - "Source record Id (Opportunity, Account, custom object, etc.) whose data populates the PDF" - "Delivery mechanism: on-demand browser download, programmatic attachment to ContentVersion, or email attachment" - "Design assets: logos, fonts — whether hosted as static resources" - "Whether the PDF must be generated by an LWC (requires VF wrapper pattern)" - "Async vs. synchronous generation requirement — determines Queueable vs. direct controller approach" outputs: - "Visualforce page with renderAs='pdf', showHeader='false', sidebar='false', and server-side controller" - "Apex controller class loading all required data server-side with FLS-safe SOQL (WITH USER_MODE)" - "Inline CSS using CSS 2.1 table-based layout (no Flexbox, Grid, or JS-dependent styles)" - "Queueable Apex class calling PageReference.getContentAsPDF(), null-checking the blob, and inserting ContentVersion + ContentDocumentLink" - "LWC-to-VF wrapper wiring if LWC-initiated PDF generation is required" dependencies: [] version: 1.0.0 author: Pranav Nagrecha updated: 2026-04-06 --- # PDF Generation Patterns This skill activates when a Salesforce implementation needs to generate PDF documents — either rendered on demand from a Visualforce page or programmatically stored as Files (ContentVersion) on Salesforce records. It covers the complete lifecycle: Flying Saucer engine constraints, CSS and JavaScript restrictions, Apex controller design, `PageReference.getContentAsPDF()` usage, ContentVersion storage, LWC limitations, and async generation patterns. --- ## Before Starting Gather this context before working on anything in this domain: - **Rendering engine constraints:** Salesforce uses the Flying Saucer HTML-to-PDF engine for `renderAs='pdf'` on ``. This engine executes no JavaScript and ignores external CDN stylesheets. All CSS must be inlined or referenced via a Salesforce-hosted static resource, and all data must be loaded server-side in the Apex controller before the page renders. - **Data loading:** There is no client-side execution during PDF rendering. Any SOQL, field lookups, or computed values must be resolved in the Apex controller's constructor or lazy-loaded properties before the page is returned to the renderer. - **LWC limitation:** LWC cannot set `renderAs='pdf'` and cannot produce a PDF natively. The documented pattern is a VF wrapper page that acts as the PDF template, invoked from an LWC via a navigation API or programmatically through a headless Apex Queueable. - **Callout limit:** `PageReference.getContentAsPDF()` counts as one callout against the 100-callout-per-transaction limit. Bulk PDF generation must be scoped accordingly. - **Delivery mechanism:** Determine upfront whether the PDF is a browser download (VF page rendered directly), a programmatic attachment to a record (ContentVersion + ContentDocumentLink), or an email attachment (Messaging.EmailFileAttachment). --- ## Core Concepts ### Concept 1: Visualforce renderAs='pdf' and the Flying Saucer Engine Setting `renderAs="pdf"` on `` directs Salesforce to pipe the rendered HTML through the Flying Saucer library (an iText-based HTML-to-PDF converter) before sending the response. Consequences that cause the majority of production bugs: - **JavaScript is completely ignored.** There is no JS engine in the rendering pipeline. Scripts embedded in or referenced by the page produce no output and throw no error — they are silently skipped. - **External CDN stylesheets are not loaded.** `` references are ignored by the renderer because it does not make external authenticated calls on behalf of the browser session. All CSS must be inline `