How Preview Images Are Generated for Template Documents in DocuSeal
DocuSeal generates preview images for template documents using the Templates::ProcessDocument module, which rasterizes PDFs via Pdfium and processes images via Vips, storing results as Active Storage attachments named preview_images.
When users upload documents to a template in DocuSeal—an open-source document signing platform—the system automatically creates web-ready preview images to facilitate visual editing and review. This process handles both PDF and image formats through a modular pipeline that converts source files into optimized PNG or JPEG attachments. Understanding how preview images are generated for template documents reveals the architecture behind DocuSeal's document visualization capabilities.
Core Processing Logic in Templates::ProcessDocument
The central orchestration happens in lib/templates/process_document.rb, where the Templates::ProcessDocument module determines file types and delegates to specialized generators.
Entry Point and Format Detection
When a new attachment is saved, the system invokes Templates::ProcessDocument.call(attachment, data, …). This method inspects the content type and branches accordingly: PDFs trigger generate_pdf_preview_images, while standard images trigger generate_preview_image.
Image Attachment Processing
For image uploads, the generate_preview_image method performs the following operations:
- Deletes any existing preview images for the record to prevent accumulation
- Loads the original file using Vips (with special handling for BMP formats via
LoadBmp) - Rotates and resizes the image to a maximum width of 1400 pixels
- Encodes the result as a PNG with modest compression
- Creates a new
ActiveStorage::Attachmentnamedpreview_imagespointing to a freshActiveStorage::Blob
PDF Attachment Processing
For PDF documents, generate_pdf_preview_images handles multi-page rasterization:
- Clears previous preview images
- Opens the PDF using HexaPDF for metadata extraction and Pdfium for rendering
- Determines page limits (up to 15 pages or fewer if file size thresholds are exceeded)
- Renders each page to a bitmap with a maximum width of 1400px using
Pdfium::Document#render_to_bitmap - Converts bitmaps to Vips images and writes them as PNG or JPEG depending on configuration
- Uploads each page as a separate
ActiveStorage::Blobwithpreview_imagesattachments
Background Processing with Sidekiq
Large PDFs can overwhelm synchronous request cycles, so DocuSeal queues intensive processing via GeneratePreviewImagesJob in app/jobs/generate_preview_images_job.rb. This Sidekiq job loads the attachment by ID, calculates the valid page range, and invokes Templates::ProcessDocument.generate_document_preview_images with a concurrency limit of 1 to prevent system overload.
Storage Architecture and Associations
The preview_images attachment is declared globally in config/initializers/active_storage.rb using has_many_attached :preview_images, making the relationship available across models. Controllers like app/controllers/templates_preview_controller.rb preload these associations for efficient access, while lib/templates/clone_attachments.rb ensures preview images are duplicated when templates are cloned.
Implementation Examples
To generate a preview for a single image attachment:
# Called from Templates::ProcessDocument.call for a non‑PDF attachment
Templates::ProcessDocument.generate_preview_image(attachment, data)
To generate per-page previews for a PDF document:
# Called from Templates::ProcessDocument.call for a PDF attachment
Templates::ProcessDocument.generate_pdf_preview_images(attachment, pdf_data, max_pages: 15)
The background job implementation handles large documents asynchronously:
class GeneratePreviewImagesJob
include Sidekiq::Job
def perform(params = {})
attachment = ActiveStorage::Attachment.find(params['attachment_id'])
max_page = [attachment.metadata['pdf']['number_of_pages'].to_i - 1,
Templates::ProcessDocument::MAX_NUMBER_OF_PAGES_PROCESSED].min
Templates::ProcessDocument.generate_document_preview_images(
attachment,
attachment.download,
1..max_page,
concurrency: 1
)
end
end
Summary
- Format-specific pipelines: The system branches between
generate_preview_imagefor bitmaps andgenerate_pdf_preview_imagesfor PDFs inlib/templates/process_document.rb. - Rendering engines: Vips handles image manipulation and PDF page conversion, while Pdfium rasterizes PDF source files and HexaPDF extracts metadata.
- Storage model: All previews are persisted as
preview_imagesActive Storage attachments, configured in the initializer and accessible across template models. - Scalability: The
GeneratePreviewImagesJobbackground worker processes large documents asynchronously with controlled concurrency to maintain system stability. - Data integrity: When templates are duplicated,
lib/templates/clone_attachments.rbensures preview images are copied alongside source documents.
Frequently Asked Questions
What file formats does DocuSeal support for preview generation?
DocuSeal supports standard image formats including BMP (handled specially via LoadBmp), JPEG, and PNG through the Vips library. For documents, it processes PDF files using Pdfium for rendering and HexaPDF for metadata extraction.
Why does DocuSeal use Pdfium instead of ImageMagick for PDF previews?
According to the source code in lib/templates/process_document.rb, DocuSeal uses Pdfium specifically for PDF rasterization because it provides direct bitmap rendering capabilities through Pdfium::Document#render_to_bitmap, while HexaPDF handles metadata extraction. This separation allows precise control over page-by-page processing up to the 15-page limit.
How does DocuSeal prevent preview generation from blocking web requests?
For large PDFs, DocuSeal queues processing in GeneratePreviewImagesJob, a Sidekiq background job defined in app/jobs/generate_preview_images_job.rb. The job executes with concurrency: 1 to limit resource usage, processing pages asynchronously after the initial upload completes.
What happens to preview images when a template is cloned?
When duplicating templates, the lib/templates/clone_attachments.rb module explicitly handles copying of preview image attachments through methods like clone_document_preview_images_attachments, ensuring that cloned templates retain visual previews without requiring regeneration.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →