ChromicPDF Documentation

repository·main·Indexed 19 days ago

https://github.com/bitcrowd/chromic_pdf

An Elixir HTML-to-PDF renderer that communicates directly with Chrome's DevTools API via pipes. It is designed to be Node.js-free and does not require Puppeteer. ChromicPDF supports rendering from URLs, local files, raw HTML strings, and Plug integration. It features session pool configuration, PDF/A support via Ghostscript, and the ability to concatenate multiple sources into a single PDF.

Tokens
4.5K
Snippets
17
Records
20
Agent score
67%

What's inside ChromicPDF

  1. Install ChromicPDF

    main

    ChromicPDF is a supervision tree rather than a standalone application. To use it, you must add it to your runtime dependencies and include it in your application's supervision tree.

    1. Add dependency

    Add {:chromic_pdf, "~> 1.17"} to your mix.exs file.

    2. Start in supervision tree

    Add ChromicPDF to your list of children in your application's start/2 function.

    # mix.exs
    def deps do
      [ {
        :chromic_pdf,
        "~> 1.17"
      }
    ]
    end
    
    # lib/my_app/application.ex
    def MyApp.Application do
      def start(_type, _args) do
        children = [
          # other apps...
          ChromicPDF
        ]
    
        Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
      end
    end
  2. Requirements for ChromicPDF

    main

    To run ChromicPDF, you must have the following installed on your system:

    • Chromium or Chrome: Required for the core HTML-to-PDF rendering.
    • Ghostscript (Optional): Required if you want to use PDF/A support or concatenate multiple sources.
  3. Use wait_for to wait for specific element attributes

    main

    The ProtocolOptions module allows you to replace a standard wait_for option with an automated evaluate script. If you provide a wait_for option with a selector and an attribute, the library will automatically inject a JavaScript loop that waits until the specified element possesses that attribute before proceeding.

    Example wait_for structure:

    %{selector: ".my-element", attribute: "data-ready"}
  4. Set up multiple ChromicPDF instances using ChromicPDF.Supervisor

    main

    If you need to separate PDF worker pools or provide a custom API for your PDF module, you can use ChromicPDF.Supervisor. You do this by defining a module that uses ChromicPDF.Supervisor and adding it to your application's supervision tree.

    To use it, define a module in your application and include it in your start/2 function.

    defmodule MyApp.MyPDFGenerator do
      use ChromicPDF.Supervisor
    end
    
    defmodule MyApp.Application do
      use Application
    
      def start(_type, _args) do
        children = [
          MyApp.MyPDFGenerator
        ]
    
        Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
      end
    end
  5. Use ChromicPDF.Plug for request forwarding

    main

    The ChromicPDF.Plug module implements a request forwarding mechanism. It allows an internal endpoint (serving incoming requests from Chrome) to forward the request back to the original caller process that initiated the PDF generation.

    To use it, you must register the plug in your router and call ChromicPDF.print_to_pdf/2 using the {:plug, ...} tuple format.

    ### 1. Register the plug in your router
    forward "/makepdf", ChromicPDF.Plug
    
    ### 2. Call print_to_pdf from the caller side
    ChromicPDF.print_to_pdf({
      {:plug,
        url: "http://localhost:4000/makepdf",
        forward: {MyTemplate, :render, [%{hello: :world}]
      }
    })
    
    ### 3. Implement your template
    defmodule MyTemplate do
      def render(conn, assigns) do
        # Either send response via conn (and return conn)
        # Or return content to be sent by the plug
      end
    end
  6. Use the Template API for page dimensions

    main

    The ChromicPDF.Template module provides tools to control PDF page dimensions (e.g., A4). You can use ChromicPDF.Template.source_and_options/1 to prepare a source and options map, which is then passed to ChromicPDF.print_to_pdf/2.

    [content: "<p>Hello Template</p>", size: :a4]
    |> ChromicPDF.Template.source_and_options()
    |> ChromicPDF.print_to_pdf()
  7. Use the Main API to generate PDFs

    main

    The main API allows you to render HTML from URLs or local files into PDF format.

    Use ChromicPDF.print_to_pdf/2 with a {:url, url} tuple and an output: option specifying the destination path.

    Use ChromicPDF.print_to_pdfa/2 to generate PDF/A compliant files. This function accepts a callback function that receives the path to the generated temporary PDF file, which is useful for uploading to S3 or sending via email.

    # Prints a local HTML file to PDF.
    ChromicPDF.print_to_pdf({:url, "https://example.net"}, output: "example.pdf")
    
    # Print to PDF/A using a callback for the generated file path
    ChromicPDF.print_to_pdfa({:url, "file:///example.html"}, output: fn pdf ->
      # Send pdf via mail, upload to S3, ...
    end)
  8. Concatenate multiple sources into one PDF

    main

    If Ghostscript is installed, you can pass a list of sources to ChromicPDF.print_to_pdf/2. The library will automatically concatenate them into a single PDF file.

    ChromicPDF.print_to_pdf([{:html, "page 1"}, {:html, "page 2"}], output: "joined.pdf")
  9. Configure browser session pools via :session_pool

    main

    The :session_pool configuration option allows you to define one or more pools of browser sessions. You can provide a single list of options for a default pool, or a map of named pools for more granular control.

    When configuring a pool, the following keys are available:

    • :size: The number of sessions in the pool (defaults to default_pool_size() from ChromicPDF.Utils).
    • :timeout: Timeout for operations (defaults to 5000).
    • :init_timeout: Timeout for initializing a session (defaults to 5000).
    • :close_timeout: Timeout for closing a session (defaults to 1000).
    • :checkout_timeout: Timeout for checking out a session from the pool (defaults to 5000).
    • :max_uses: The maximum number of times a session can be used before being recycled (defaults to 1000).
    • :offline: Whether to run in offline mode (defaults to false).
    • :ignore_certificate_errors: Whether to ignore SSL certificate errors (defaults to false).
    • :unhandled_runtime_exceptions: How to handle unhandled exceptions (defaults to :log).

    Note on Deprecation: The option :max_session_uses is deprecated. You should instead use :max_uses within the specific pool configuration: [session_pool: [max_uses: N]].

    # Example: Defining a default pool
    config = [session_pool: [size: 5, max_uses: 500]]
    
    # Example: Defining multiple named pools
    config = %{
      "high_performance" => [size: 10, timeout: 10000],
      "default" => [size: 2]
    }
  10. Configure ChromicPDF.Supervisor options

    main

    When starting a ChromicPDF.Supervisor instance, you can provide several global options to configure the browser and Ghostscript pools.

    Global Options

    • :name: An atom identifying this specific ChromicPDF instance.
    • :on_demand: A boolean. If true, the supervisor holds the configuration in an Agent and only spawns a temporary browser instance when a job is triggered. This is useful for dev/test environments.
    • :session_pool: Configuration for the browser session pool. Can be a single list of session_pool_option() or a map of named_session_pools (mapping an atom name to a list of options).
    • :ghostscript_pool: A list of ghostscript_pool_option() (e.g., [{:size, 5}]).
    • :chrome_args: (Local Chrome only) extended_chrome_args to pass to the browser.
    • :chrome_executable: (Local Chrome only) Path to the Chrome binary.
    • :chrome_address: (Remote Chrome only) {host :: binary(), port :: non_neg_integer()}.
  11. Troubleshoot Chrome communication errors

    main

    When using ChromicPDF, communication failures with the Chrome instance are raised as ChromicPDF.ChromeError exceptions. These errors typically fall into several categories:

    SSL Certificate Errors

    If you encounter an error starting with net::ERR_CERT, Chrome is unable to verify the remote host's SSL certificate.

    • Cause: Invalid or expired certificates on a production system, or self-signed certificates on development/test systems.
    • Solution: For development/test systems with self-signed certificates, you can disable certificate verification by passing the :ignore_certificate_errors flag in your configuration.

    Network Connectivity Errors

    If you encounter net::ERR_INTERNET_DISCONNECTED:

    • Cause: Chrome cannot establish a connection to a remote URL. This happens if you lack internet access, a firewall is blocking the connection, or if you are intentionally running ChromicPDF in "offline mode".

    JS Runtime and Evaluation Errors

    • Unhandled exception in JS runtime: An exception was thrown within the Chrome JS runtime (:exception_thrown).
    • Console API called in JS runtime: A console.* method was called during execution (:console_api_called).
    • Exception in :evaluate expression: An error occurred while executing a specific expression via the :evaluate method (:evaluate). The error message will attempt to point out the specific line number in the evaluated expression.
    # To bypass SSL certificate errors in development:
    {ChromicPDF, ignore_certificate_errors: true}
  12. Capture screenshots with ChromicPDF.capture_screenshot/2

    main

    Captures a screenshot of a page. This call blocks until the screenshot is created.

    Options

    • capture_screenshot: %{format: "jpeg"}: Specify image format (e.g., png, jpeg).
    • full_page: true: Increases the viewport to fit the entire content (requires Chrome 91+).

    Output

    Returns {:ok, blob} where blob is the Base64-encoded image.

    # Full page screenshot
    ChromicPDF.capture_screenshot(
      {:url, "file:///very-long-content.html"},
      full_page: true
    )