Socket
Book a DemoInstallSign in
Socket

bidi2pdf

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bidi2pdf

0.1.12
bundlerRubygems
Version published
Maintainers
1
Created
Source

Build Status Maintainability Gem Version Open Source Helpers

πŸ“„ Bidi2pdf – Bulletproof PDF generation via Chrome's BiDi Protocol

Bidi2pdf is a powerful Ruby gem that transforms modern web pages into high-fidelity PDFs using Chrome’s BiDirectional (BiDi) protocol. Whether you're automating reports, archiving websites, or shipping documentation, Bidi2pdf gives you precision, flexibility, and full control.

πŸ“š Table of Contents

✨ Key Features

βœ… One-liner CLI – From URL to PDF in a single command
βœ… Full customization – Inject cookies, headers, auth credentials
βœ… Smart waiting – Wait for complete page load or network idle
βœ… Headless support – Run quietly in the background
βœ… Docker-ready – Plug and play with containers
βœ… Modern architecture – Built on Chrome's next-gen BiDi protocol
βœ… Network logging – Know which requests fail during rendering
βœ… Console log capture – See what goes wrong inside the browser

⚑ Quick Start

Get up and running in three easy steps:

# 1. Install the gem (system-wide)
gem install bidi2pdf

# 2. Render any page to PDF
bidi2pdf render --url https://example.com --output example.pdf

# 3. Open the PDF (macOS shown; use xdg-open on Linux)
open example.pdf

Bundler users – Add it to your project with bundle add bidi2pdf.

πŸš€ Installation

Bundler

gem 'bidi2pdf'

Standalone

gem install bidi2pdf

Requirements

βš™οΈ Basic Usage

Command-line

bidi2pdf render --url https://example.com/invoice/14432423 --output example.pdf

Advanced CLI Options

bidi2pdf render \
  --url https://example.com/invoice/14432423 \
  --output example.pdf \
  --cookie session=abc123 \
  --header X-API-KEY=token \
  --auth admin:password \
  --wait_network_idle \
  --wait_window_loaded \
  --log-level debug

🧠 Programmatic API

Classic Approach

require 'bidi2pdf'

launcher = Bidi2pdf::Launcher.new(
  url: 'https://example.com/invoice/14432423',
  output: 'example.pdf',
  cookies: { 'session' => 'abc123' },
  headers: { 'X-API-KEY' => 'token' },
  auth: { username: 'admin', password: 'password' },
  wait_window_loaded: true,
  wait_network_idle: true
)

launcher.launch

DSL – Quick & Clean

require "bidi2pdf"

Bidi2pdf::DSL.with_tab(headless: true) do |tab|
  tab.navigate_to("https://example.com/invoice/14432423")
  tab.wait_until_network_idle
  tab.print("example.pdf")
end

🧬 Deep Integration Example

Get fine-grained control using Chrome sessions, tabs, and BiDi commands:

πŸ” Show full example
require "bidi2pdf"

# 1. Remote or local session?
session = Bidi2pdf::Bidi::Session.new(
  session_url: "http://localhost:9092/session",
  headless: true,
)

# Alternative: local session via ChromeDriver
# manager = Bidi2pdf::ChromedriverManager.new(headless: false)
# manager.start
# session = manager.session

session.start
session.client.on_close { puts "WebSocket session closed" }

# 2. Create browser/tab
browser = session.browser
context = browser.create_user_context
window = context.create_browser_window
tab = window.create_browser_tab

# 3. Inject configuration
tab.set_cookie(name: "auth", value: "secret", domain: "example.com", secure: true)
tab.add_headers(url_patterns: [{ type: "pattern", protocol: "https", hostname: "example.com", port: "443" }],
                headers: [{ name: "X-API-KEY", value: "12345678" }])
tab.basic_auth(url_patterns: [{ type: "pattern", protocol: "https", hostname: "example.com", port: "443" }],
               username: "username", password: "secret")

# 4. Render PDF
tab.navigate_to "https://example.com/invoice/14432423"

# Alternative: send html code to the browser
# tab.render_html_content("<html>...</html>")

# Inject JavaScript if, needed
# as an url
# tab.inject_script "https://example.com/script.js" 
# or inline
# tab.inject_script "console.log('Hello from injected script!')"

# Inject CSS if needed
# as an url
# tab.inject_style url: "https://example.com/simple.css"
# or inline
# tab.inject_style content: "body { background-color: red; }"

tab.wait_until_network_idle
tab.print("my.pdf")

# 5. Cleanup
tab.close
window.close
context.close
session.close

🌐 Architecture

%%{  init: {
      "theme": "base",
      "themeVariables": {
        "primaryColor":  "#E0E7FF",
        "secondaryColor":"#FEF9C3",
        "edgeLabelBackground":"#FFFFFF",
        "fontSize":"14px",
        "nodeBorderRadius":"6"
      }
    }
}%%
flowchart LR
%% ----- Ruby side ---------
    A["fa:fa-gem Ruby Application"]
    B["fa:fa-gem bidi2pdf<br/>Library"]
%% ----Chrome environment -----------
    subgraph C["fa:fa-chrome Chrome Environment"]
        direction TB
        C1["fa:fa-chrome Local Chrome<br/>(sub-process)"]
        C2["fa:fa-docker Docker Chrome<br/>(remote)"]
    end

    D[[PDF File]]
%% ---- Data / control flows ------
    A -- " HTML / URL + JS / CSS " --> B
    B -- " WebDriver BiDi " --> C1
    B -- " WebDriver BiDi " --> C2
    C1 -- " PDF bytes " --> B
    C2 -- " PDF bytes " --> B
    B -- " PDF " --> D
%% --- Optional extra styling classes (for future tweaks) ---
    classDef ruby fill:#E0E7FF,stroke:#6366F1,color:#1E1B4B;
    classDef chrome fill:#FEF9C3,stroke:#F59E0B,color:#78350F;
    class A,B ruby;
    class C1,C2 chrome;

🐳 Docker Support

πŸ› οΈ Build & Run Locally

# Prepare the environment
rake build

# Build the Docker image
docker build -t bidi2pdf -f docker/Dockerfile .

# Run the container and generate a PDF
docker run -it --rm \
  -v ./output:/reports \
  bidi2pdf \
  bidi2pdf render --url=https://example.com/invoice/14432423 --output /reports/example.pdf

Grab it directly from Docker Hub

docker run -it --rm \
  -v ./output:/reports \
  dieters877565/bidi2pdf:main-slim \
  bidi2pdf render --url=https://example.com/invoice/14432423 --output /reports/example.pdf

βœ… Tip: Mount your local directory (e.g. ./output) to /reports in the container to easily access the generated PDFs.

Docker Compose

rake build
docker compose -f docker/docker-compose.yml up -d

# simple example
docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http://nginx/sample.html --wait_window_loaded --wait_network_idle --output /reports/simple.pdf

# with a local file
docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=file:///reports/sample.html--wait_network_idle --output /reports/simple.pdf


# basic auth example
docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http://nginx/basic/sample.html --auth admin:secret --wait_window_loaded --wait_network_idle --output /reports/basic.pdf

# header example
docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http://nginx/header/sample.html --header "X-API-KEY=secret" --wait_window_loaded --wait_network_idle --output /reports/header.pdf

# cookie example
docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http://nginx/cookie/sample.html --cookie "auth=secret" --wait_window_loaded --wait_network_idle --output /reports/cookie.pdf

# remote chrome example
docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http://nginx/cookie/sample.html --remote_browser_url http://remote-chrome:3000/session --cookie "auth=secret" --wait_window_loaded --wait_network_idle --output /reports/remote.pdf

docker compose -f docker/docker-compose.yml down

🧩 Configuration Options

FlagDescription
--urlTarget URL (required)
--outputOutput PDF file (default: output.pdf)
--cookieSet cookie in name=value format
--headerInject custom header name=value
--authBasic auth as user:pass
--headlessRun Chrome headless (default: true)
--portChromeDriver port (0 = auto)
--wait_window_loadedWait until window.loaded is set to true
--wait_network_idleWait until network is idle
--log_levelLog level: debug, info, warn, error, fatal
--remote_browser_urlConnect to remote Chrome session
--default_timeoutOperation timeout (default: 60s)

πŸš‚ Rails Integration

Rails integration is available as an additional gem:

# In your Gemfile
gem 'bidi2pdf-rails'

For full documentation and usage examples, visit: https://github.com/dieter-medium/bidi2pdf-rails

πŸ§ͺ Test Helpers

Bidi2pdf provides a suite of RSpec helpers (activated with pdf: true) to simplify PDF-related testing:

SpecPathsHelper

– spec_dir β†’ returns your spec directory
– tmp_dir β†’ returns your tmp directory
– tmp_file(*parts) β†’ builds a tmp file path
– random_tmp_dir(*dirs, prefix:) β†’ builds a random tmp directory

  • fixture_file(*parts) β†’ returns the path to a fixture file

PdfFileHelper

– with_pdf_debug(pdf_data) { |data| … } β†’ on failure, writes PDF to disk
– store_pdf_file(pdf_data, filename_prefix = "test") β†’ saves PDF and returns path

Rspec Matchers

  • have_pdf_page_count β†’ checks if the PDF has a specific number of pages
  • match_pdf_text β†’ checks if the PDF equals a specific text, after stripping whitespace and normalizing characters
  • contains_pdf_text β†’ checks if the PDF contains a specific text, after stripping whitespace and normalizing characters, supporting regex
  • contains_pdf_image β†’ checks if the PDF contains a specific image

ChromedriverContainer

require "bidi2pdf/test_helpers/testcontainers" you can use the chromedriver_container helper to start a ChromeDriver container for your tests. This is useful if you don't want to run ChromeDriver locally or if you want to ensure a clean environment for your tests.

This also provides the helper methods:

  • session_url β†’ returns the session URL for the ChromeDriver container
  • chromedriver_container β†’ returns the Testcontainers container object
  • create_session -> creates a Bidi2pdf::Bidi::Session object for the ChromeDriver container

With the environment variable DISABLE_CHROME_SANDBOX set to true, the container will run Chrome without the sandbox. This is useful for CI environments where the sandbox may cause issues.

Example

require "bidi2pdf/test_helpers"
require "bidi2pdf/test_helpers/images" # <= for image matching, requires lib-vips
require "bidi2pdf/test_helpers/testcontainers" # <= requires testcontainers gem

RSpec.describe "PDF generation", :pdf, :chromedriver do
  it "generates a PDF with the correct content" do
    pdf_data = generate_pdf("https://example.com/invoice/14432423")
    expect(pdf_data).to have_pdf_page_count(1)
    expect(pdf_data).to match_pdf_text("Hello, world!")
    expect(pdf_data).to contain_pdf_image(fixture_file("logo.png"))
  end
end

πŸ›  Development

# Setup
bin/setup

# Run tests
rake spec

# Open interactive console
bin/console

πŸ“œ License

This project is licensed under the MIT License.

FAQs

Package last updated on 02 Sep 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚑️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.