bidi2pdf 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -1
- data/README.md +168 -55
- data/docker/Dockerfile +11 -3
- data/docker/Dockerfile.chromedriver +4 -2
- data/docker/Dockerfile.slim +75 -0
- data/lib/bidi2pdf/bidi/browser_tab.rb +47 -7
- data/lib/bidi2pdf/bidi/commands/set_tab_cookie.rb +9 -1
- data/lib/bidi2pdf/bidi/logger_events.rb +86 -0
- data/lib/bidi2pdf/bidi/network_event.rb +25 -7
- data/lib/bidi2pdf/bidi/network_event_formatters/network_event_console_formatter.rb +109 -0
- data/lib/bidi2pdf/bidi/network_event_formatters/network_event_formatter_utils.rb +53 -0
- data/lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb +125 -0
- data/lib/bidi2pdf/bidi/network_event_formatters.rb +11 -0
- data/lib/bidi2pdf/bidi/network_events.rb +24 -5
- data/lib/bidi2pdf/bidi/session.rb +1 -1
- data/lib/bidi2pdf/cli.rb +28 -5
- data/lib/bidi2pdf/dsl.rb +45 -0
- data/lib/bidi2pdf/launcher.rb +7 -3
- data/lib/bidi2pdf/session_runner.rb +21 -4
- data/lib/bidi2pdf/version.rb +1 -1
- data/lib/bidi2pdf.rb +7 -2
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 541e9193b3285383b2a82065b173af87c68edeb08ec157125761f44285118dff
|
4
|
+
data.tar.gz: ab12e33ad21e8377ef964aa12d0d9b0ba757ee941c217febe6d46fb1cc381d5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91be15b3310098c7f16153331fa0a27a8b869dfebc9f3720a7da26d2c59b8444f266e21e93d74891b423e50d8223b01b2198f230753d38ad523b8c846c12150f
|
7
|
+
data.tar.gz: 504516ecc1eac40e6c6e159c5f14aafdeb9d23bc587fcf6914ab60d8676a930a7866d6d0c07998398359c402780584f1e5b317c274dd6f3dba0ba50c1a73cbe6
|
data/CHANGELOG.md
CHANGED
@@ -6,10 +6,47 @@ All notable changes to this project will be documented in this file.
|
|
6
6
|
|
7
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
8
8
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
9
|
-
|
9
|
+
|
10
|
+
[unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.5..HEAD
|
10
11
|
|
11
12
|
<!-- generated by git-cliff end -->
|
12
13
|
|
14
|
+
## [0.1.6]
|
15
|
+
|
16
|
+
### ⚠️ Breaking Changes
|
17
|
+
|
18
|
+
- Rename view_html_page to render_html by @dieter-medium
|
19
|
+
- Rename wait_until_all_finished to wait_until_network_idle by @dieter-medium
|
20
|
+
|
21
|
+
### 📝 Docs
|
22
|
+
|
23
|
+
- Update Docker instructions in README by @dieter-medium
|
24
|
+
|
25
|
+
### 🔄 Changed
|
26
|
+
|
27
|
+
- Add details on network logging and console capture by @dieter-medium
|
28
|
+
|
29
|
+
### 🚀 Added
|
30
|
+
|
31
|
+
- Add PDF network log formatting and customizable outputs by @dieter-medium
|
32
|
+
- Add option to log network traffic and handle failures within the cli command by @dieter-medium
|
33
|
+
- Add structured network traffic logging by @dieter-medium
|
34
|
+
- Add slim variant Dockerfile and build matrix for CI by @dieter-medium
|
35
|
+
- Add official image at [Docker Hub](https://hub.docker.com/r/dieters877565/bidi2pdf) by @dieter-medium
|
36
|
+
|
37
|
+
## [0.1.5] - 2025-04-10
|
38
|
+
|
39
|
+
### 📝 Docs
|
40
|
+
|
41
|
+
- Improve README structure and update content by @dieter-medium
|
42
|
+
- Add step-by-step usage guide to README by @dieter-medium
|
43
|
+
|
44
|
+
### 🚀 Added
|
45
|
+
|
46
|
+
- Add support for rendering local HTML files or in memory HTML by @dieter-medium
|
47
|
+
- Add DSL and specs for generating PDFs with browser tabs by @dieter-medium
|
48
|
+
- Enhance print method with block support for PDF handling by @dieter-medium
|
49
|
+
|
13
50
|
## [0.1.4] - 2025-04-10
|
14
51
|
|
15
52
|
### 🎨 Refactored
|
@@ -79,3 +116,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
79
116
|
## [0.1.0] - 2025-03-26
|
80
117
|
|
81
118
|
- Initial release
|
119
|
+
|
120
|
+
[unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.6..HEAD
|
121
|
+
|
122
|
+
[0.1.6]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.5..v0.1.6
|
123
|
+
|
124
|
+
[0.1.5]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.4..v0.1.5
|
125
|
+
|
126
|
+
[0.1.4]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.3..v0.1.4
|
data/README.md
CHANGED
@@ -3,52 +3,60 @@
|
|
3
3
|
[](https://badge.fury.io/rb/bidi2pdf)
|
4
4
|
[](https://codeclimate.com/github/dieter-medium/bidi2pdf/test_coverage)
|
5
5
|
|
6
|
-
|
6
|
+
---
|
7
7
|
|
8
|
-
|
9
|
-
offers precise control over PDF generation with support for modern web technologies.
|
8
|
+
# 📄 Bidi2pdf – Bulletproof PDF generation via Chrome's BiDi Protocol
|
10
9
|
|
11
|
-
|
10
|
+
**Bidi2pdf** is a powerful Ruby gem that transforms modern web pages into high-fidelity PDFs using Chrome’s
|
11
|
+
**BiDirectional (BiDi)** protocol. Whether you're automating reports, archiving websites, or shipping documentation,
|
12
|
+
Bidi2pdf gives you **precision, flexibility, and full control**.
|
12
13
|
|
13
|
-
|
14
|
-
- **Rich Configuration** - Customize with cookies, headers, and authentication
|
15
|
-
- **Waiting Conditions** - Wait for window loaded or network idle
|
16
|
-
- **Headless Support** - Run without a visible browser
|
17
|
-
- **Docker Ready** - Easy containerization
|
18
|
-
- **Modern Architecture** - Uses Chrome's BiDi protocol for better control
|
14
|
+
---
|
19
15
|
|
20
|
-
##
|
16
|
+
## ✨ Key Features
|
21
17
|
|
22
|
-
|
18
|
+
✅ **One-liner CLI** – From URL to PDF in a single command
|
19
|
+
✅ **Full customization** – Inject cookies, headers, auth credentials
|
20
|
+
✅ **Smart waiting** – Wait for complete page load or network idle
|
21
|
+
✅ **Headless support** – Run quietly in the background
|
22
|
+
✅ **Docker-ready** – Plug and play with containers
|
23
|
+
✅ **Modern architecture** – Built on Chrome's next-gen BiDi protocol
|
24
|
+
✅ **Network logging** – Know which requests fail during rendering
|
25
|
+
✅ **Console log capture** – See what goes wrong inside the browser
|
26
|
+
|
27
|
+
---
|
28
|
+
|
29
|
+
## 🚀 Installation
|
30
|
+
|
31
|
+
### Bundler
|
23
32
|
|
24
33
|
```ruby
|
25
34
|
gem 'bidi2pdf'
|
26
35
|
```
|
27
36
|
|
28
|
-
|
37
|
+
### Standalone
|
29
38
|
|
30
39
|
```bash
|
31
|
-
|
40
|
+
gem install bidi2pdf
|
32
41
|
```
|
33
42
|
|
34
|
-
###
|
43
|
+
### Requirements
|
35
44
|
|
36
|
-
- **Ruby
|
37
|
-
- **
|
38
|
-
|
39
|
-
Downloads and installs the ChromeDriver version matching your installed Chrome/Chromium browser
|
40
|
-
Eliminates the need to manually install or update ChromeDriver
|
41
|
-
Ensures compatibility between Chrome and ChromeDriver versions
|
45
|
+
- **Ruby** ≥ 3.3
|
46
|
+
- **Chrome/Chromium**
|
47
|
+
- Automatic ChromeDriver management via [chromedriver-binary](https://github.com/dieter-medium/chromedriver-binary)
|
42
48
|
|
43
|
-
|
49
|
+
---
|
44
50
|
|
45
|
-
|
51
|
+
## ⚙️ Basic Usage
|
52
|
+
|
53
|
+
### Command-line
|
46
54
|
|
47
55
|
```bash
|
48
56
|
bidi2pdf render --url https://example.com --output example.pdf
|
49
57
|
```
|
50
58
|
|
51
|
-
### Advanced Options
|
59
|
+
### Advanced CLI Options
|
52
60
|
|
53
61
|
```bash
|
54
62
|
bidi2pdf render \
|
@@ -62,14 +70,18 @@ bidi2pdf render \
|
|
62
70
|
--log-level debug
|
63
71
|
```
|
64
72
|
|
65
|
-
|
73
|
+
---
|
74
|
+
|
75
|
+
## 🧠 Programmatic API
|
76
|
+
|
77
|
+
### Classic Approach
|
66
78
|
|
67
79
|
```ruby
|
68
80
|
require 'bidi2pdf'
|
69
81
|
|
70
82
|
launcher = Bidi2pdf::Launcher.new(
|
71
83
|
url: 'https://example.com',
|
72
|
-
output: 'example.pdf',
|
84
|
+
output: 'example.pdf',
|
73
85
|
cookies: { 'session' => 'abc123' },
|
74
86
|
headers: { 'X-API-KEY' => 'token' },
|
75
87
|
auth: { username: 'admin', password: 'password' },
|
@@ -78,25 +90,111 @@ launcher = Bidi2pdf::Launcher.new(
|
|
78
90
|
)
|
79
91
|
|
80
92
|
launcher.launch
|
93
|
+
```
|
94
|
+
|
95
|
+
### DSL – Quick & Clean
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
require "bidi2pdf"
|
99
|
+
|
100
|
+
Bidi2pdf::DSL.with_tab(headless: true) do |tab|
|
101
|
+
tab.navigate_to("https://example.com")
|
102
|
+
tab.wait_until_network_idle
|
103
|
+
tab.print("example.pdf")
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
---
|
108
|
+
|
109
|
+
## 🧬 Deep Integration Example
|
110
|
+
|
111
|
+
Get fine-grained control using Chrome sessions, tabs, and BiDi commands:
|
112
|
+
|
113
|
+
<details>
|
114
|
+
<summary>🔍 Show full example</summary>
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
require "bidi2pdf"
|
118
|
+
|
119
|
+
# 1. Remote or local session?
|
120
|
+
session = Bidi2pdf::Bidi::Session.new(
|
121
|
+
session_url: "http://localhost:9092/session",
|
122
|
+
headless: true,
|
123
|
+
)
|
124
|
+
|
125
|
+
# Alternative: local session via ChromeDriver
|
126
|
+
# manager = Bidi2pdf::ChromedriverManager.new(headless: false)
|
127
|
+
# manager.start
|
128
|
+
# session = manager.session
|
129
|
+
|
130
|
+
session.start
|
131
|
+
session.client.on_close { puts "WebSocket session closed" }
|
132
|
+
|
133
|
+
# 2. Create browser/tab
|
134
|
+
browser = session.browser
|
135
|
+
context = browser.create_user_context
|
136
|
+
window = context.create_browser_window
|
137
|
+
tab = window.create_browser_tab
|
81
138
|
|
82
|
-
#
|
139
|
+
# 3. Inject configuration
|
140
|
+
tab.set_cookie(name: "auth", value: "secret", domain: "example.com", secure: true)
|
141
|
+
tab.add_headers(url_patterns: [{ type: "pattern", protocol: "https", hostname: "example.com", port: "443" }],
|
142
|
+
headers: [{ name: "X-API-KEY", value: "12345678" }])
|
143
|
+
tab.basic_auth(url_patterns: [{ type: "pattern", protocol: "https", hostname: "example.com", port: "443" }],
|
144
|
+
username: "username", password: "secret")
|
145
|
+
|
146
|
+
# 4. Render PDF
|
147
|
+
tab.navigate_to "https://example.com"
|
148
|
+
|
149
|
+
# Alternative: send html code to the browser
|
150
|
+
# tab.render_html_content("<html>...</html>")
|
151
|
+
|
152
|
+
tab.wait_until_network_idle
|
153
|
+
tab.print("my.pdf")
|
154
|
+
|
155
|
+
# 5. Cleanup
|
156
|
+
tab.close
|
157
|
+
window.close
|
158
|
+
session.close
|
83
159
|
```
|
84
160
|
|
85
|
-
|
161
|
+
</details>
|
162
|
+
|
163
|
+
---
|
164
|
+
|
165
|
+
## 🐳 Docker Support
|
86
166
|
|
87
|
-
Build
|
167
|
+
### 🛠️ Build & Run Locally
|
88
168
|
|
89
169
|
```bash
|
90
|
-
#
|
170
|
+
# Prepare the environment
|
91
171
|
rake build
|
172
|
+
|
173
|
+
# Build the Docker image
|
92
174
|
docker build -t bidi2pdf -f docker/Dockerfile .
|
93
175
|
|
94
|
-
#
|
95
|
-
docker run -it --rm
|
176
|
+
# Run the container and generate a PDF
|
177
|
+
docker run -it --rm \
|
178
|
+
-v ./output:/reports \
|
179
|
+
bidi2pdf \
|
96
180
|
bidi2pdf render --url=https://example.com --output /reports/example.pdf
|
181
|
+
|
97
182
|
```
|
98
183
|
|
99
|
-
###
|
184
|
+
### ⚡ Use the Prebuilt Image (Recommended for Fast Start)
|
185
|
+
|
186
|
+
Grab it directly from [Docker Hub](https://hub.docker.com/r/dieters877565/bidi2pdf)
|
187
|
+
|
188
|
+
```bash
|
189
|
+
docker run -it --rm \
|
190
|
+
-v ./output:/reports \
|
191
|
+
dieters877565/bidi2pdf:main-slim \
|
192
|
+
bidi2pdf render --url=https://example.com --output /reports/example.pdf
|
193
|
+
```
|
194
|
+
|
195
|
+
✅ Tip: Mount your local directory (e.g. ./output) to /reports in the container to easily access the generated PDFs.
|
196
|
+
|
197
|
+
### Docker Compose
|
100
198
|
|
101
199
|
```bash
|
102
200
|
rake build
|
@@ -105,6 +203,10 @@ docker compose -f docker/docker-compose.yml up -d
|
|
105
203
|
# simple example
|
106
204
|
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
|
107
205
|
|
206
|
+
# with a local file
|
207
|
+
docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=file:///reports/sample.html--wait_network_idle --output /reports/simple.pdf
|
208
|
+
|
209
|
+
|
108
210
|
# basic auth example
|
109
211
|
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
|
110
212
|
|
@@ -120,31 +222,42 @@ docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http:
|
|
120
222
|
docker compose -f docker/docker-compose.yml down
|
121
223
|
```
|
122
224
|
|
123
|
-
|
225
|
+
---
|
226
|
+
|
227
|
+
## 🧩 Configuration Options
|
124
228
|
|
125
|
-
|
|
126
|
-
|
127
|
-
| `--url` |
|
128
|
-
| `--output` | Output PDF
|
129
|
-
| `--cookie` |
|
130
|
-
| `--header` |
|
131
|
-
| `--auth` | Basic auth
|
132
|
-
| `--headless` | Run Chrome
|
133
|
-
| `--port` |
|
134
|
-
| `--wait_window_loaded` | Wait
|
135
|
-
| `--wait_network_idle` | Wait
|
136
|
-
| `--log_level` | Log level
|
137
|
-
| `--remote_browser_url` |
|
138
|
-
| `--default_timeout` |
|
229
|
+
| Flag | Description |
|
230
|
+
|------------------------|--------------------------------------------|
|
231
|
+
| `--url` | Target URL (required) |
|
232
|
+
| `--output` | Output PDF file (default: output.pdf) |
|
233
|
+
| `--cookie` | Set cookie in `name=value` format |
|
234
|
+
| `--header` | Inject custom header `name=value` |
|
235
|
+
| `--auth` | Basic auth as `user:pass` |
|
236
|
+
| `--headless` | Run Chrome headless (default: true) |
|
237
|
+
| `--port` | ChromeDriver port (0 = auto) |
|
238
|
+
| `--wait_window_loaded` | Wait until `window.loaded` is set to true |
|
239
|
+
| `--wait_network_idle` | Wait until network is idle |
|
240
|
+
| `--log_level` | Log level: debug, info, warn, error, fatal |
|
241
|
+
| `--remote_browser_url` | Connect to remote Chrome session |
|
242
|
+
| `--default_timeout` | Operation timeout (default: 60s) |
|
139
243
|
|
140
|
-
|
244
|
+
---
|
141
245
|
|
142
|
-
|
246
|
+
## 🛠 Development
|
247
|
+
|
248
|
+
```bash
|
249
|
+
# Setup
|
250
|
+
bin/setup
|
251
|
+
|
252
|
+
# Run tests
|
253
|
+
rake spec
|
254
|
+
|
255
|
+
# Open interactive console
|
256
|
+
bin/console
|
257
|
+
```
|
143
258
|
|
144
|
-
|
145
|
-
2. Run `rake spec` to run the tests
|
146
|
-
3. Run `bin/console` for an interactive prompt
|
259
|
+
---
|
147
260
|
|
148
|
-
## License
|
261
|
+
## 📜 License
|
149
262
|
|
150
|
-
|
263
|
+
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
data/docker/Dockerfile
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
FROM ruby:3.3
|
2
2
|
|
3
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
4
|
+
|
3
5
|
# Install dependencies
|
4
|
-
RUN apt-get update &&
|
5
|
-
apt-get install -y \
|
6
|
-
chromium \
|
6
|
+
RUN apt-get update && apt-get upgrade &&\
|
7
|
+
apt-get install -y --no-install-recommends\
|
8
|
+
chromium chromium-driver\
|
7
9
|
libglib2.0-0 \
|
8
10
|
libnss3 \
|
9
11
|
libxss1 \
|
@@ -19,6 +21,12 @@ RUN apt-get update && \
|
|
19
21
|
# Create a non-root user
|
20
22
|
RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser
|
21
23
|
|
24
|
+
# ARM compatibility workaround:
|
25
|
+
# On ARM architectures (such as Apple Silicon), downloading chromedriver via automated scripts may fail or cause ELF binary errors,
|
26
|
+
# such as "rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2".
|
27
|
+
# To avoid these issues, we directly install 'chromium-driver' via the package manager and explicitly create a symlink in the expected location.
|
28
|
+
|
29
|
+
RUN mkdir -p /home/appuser/.webdrivers && ln -s /usr/bin/chromedriver /home/appuser/.webdrivers/chromedriver
|
22
30
|
|
23
31
|
# Set working directory
|
24
32
|
WORKDIR /app
|
@@ -2,9 +2,11 @@ FROM ruby:3.3
|
|
2
2
|
|
3
3
|
ARG CHROMEDRIVER_PORT=3000
|
4
4
|
|
5
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
6
|
+
|
5
7
|
# Install dependencies
|
6
|
-
RUN apt-get update && \
|
7
|
-
apt-get install -y \
|
8
|
+
RUN apt-get update && apt-get upgrade && \
|
9
|
+
apt-get install -y --no-install-recommends\
|
8
10
|
chromium \
|
9
11
|
libglib2.0-0 \
|
10
12
|
libnss3 \
|
@@ -0,0 +1,75 @@
|
|
1
|
+
FROM ruby:3.3-slim AS builder
|
2
|
+
|
3
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
4
|
+
|
5
|
+
# Install dependencies
|
6
|
+
RUN apt-get update && apt-get upgrade && \
|
7
|
+
apt-get install -y --no-install-recommends \
|
8
|
+
chromium \
|
9
|
+
libglib2.0-0 \
|
10
|
+
libnss3 \
|
11
|
+
libxss1 \
|
12
|
+
libasound2 \
|
13
|
+
libatk-bridge2.0-0 \
|
14
|
+
libgtk-3-0 \
|
15
|
+
libdrm2 \
|
16
|
+
curl \
|
17
|
+
unzip \
|
18
|
+
xvfb \
|
19
|
+
build-essential \
|
20
|
+
libpq-dev pkg-config \
|
21
|
+
&& rm -rf /var/lib/apt/lists/*
|
22
|
+
|
23
|
+
# Set working directory
|
24
|
+
WORKDIR /app
|
25
|
+
|
26
|
+
# Copy your gem into container
|
27
|
+
COPY ./pkg/bidi2pdf-*.gem ./
|
28
|
+
|
29
|
+
RUN gem install ./bidi2pdf-*.gem
|
30
|
+
|
31
|
+
|
32
|
+
# Stage 2
|
33
|
+
|
34
|
+
FROM ruby:3.3-slim
|
35
|
+
|
36
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
37
|
+
|
38
|
+
# Install dependencies
|
39
|
+
RUN apt-get update && apt-get upgrade &&\
|
40
|
+
apt-get install -y --no-install-recommends\
|
41
|
+
chromium chromium-driver\
|
42
|
+
libglib2.0-0 \
|
43
|
+
libnss3 \
|
44
|
+
libxss1 \
|
45
|
+
libasound2 \
|
46
|
+
libatk-bridge2.0-0 \
|
47
|
+
libgtk-3-0 \
|
48
|
+
libdrm2 \
|
49
|
+
curl \
|
50
|
+
unzip \
|
51
|
+
xvfb \
|
52
|
+
&& rm -rf /var/lib/apt/lists/*
|
53
|
+
|
54
|
+
COPY --from=builder /usr/local/bundle /usr/local/bundle
|
55
|
+
|
56
|
+
# Create a non-root user
|
57
|
+
RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser
|
58
|
+
|
59
|
+
# ARM compatibility workaround:
|
60
|
+
# On ARM architectures (such as Apple Silicon), downloading chromedriver via automated scripts may fail or cause ELF binary errors,
|
61
|
+
# such as "rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2".
|
62
|
+
# To avoid these issues, we directly install 'chromium-driver' via the package manager and explicitly create a symlink in the expected location.
|
63
|
+
|
64
|
+
RUN mkdir -p /home/appuser/.webdrivers && ln -s /usr/bin/chromedriver /home/appuser/.webdrivers/chromedriver
|
65
|
+
|
66
|
+
# Set working directory
|
67
|
+
WORKDIR /app
|
68
|
+
|
69
|
+
RUN chown -R appuser:appuser /app
|
70
|
+
|
71
|
+
# Switch to non-root user
|
72
|
+
USER appuser
|
73
|
+
|
74
|
+
CMD ["/usr/bin/bash"]
|
75
|
+
|
@@ -3,13 +3,14 @@
|
|
3
3
|
require "base64"
|
4
4
|
|
5
5
|
require_relative "network_events"
|
6
|
+
require_relative "logger_events"
|
6
7
|
require_relative "auth_interceptor"
|
7
8
|
require_relative "add_headers_interceptor"
|
8
9
|
|
9
10
|
module Bidi2pdf
|
10
11
|
module Bidi
|
11
12
|
class BrowserTab
|
12
|
-
attr_reader :client, :browsing_context_id, :user_context_id, :tabs, :network_events, :open
|
13
|
+
attr_reader :client, :browsing_context_id, :user_context_id, :tabs, :network_events, :open, :logger_events
|
13
14
|
|
14
15
|
def initialize(client, browsing_context_id, user_context_id)
|
15
16
|
@client = client
|
@@ -17,6 +18,7 @@ module Bidi2pdf
|
|
17
18
|
@user_context_id = user_context_id
|
18
19
|
@tabs = []
|
19
20
|
@network_events = NetworkEvents.new browsing_context_id
|
21
|
+
@logger_events = LoggerEvents.new browsing_context_id
|
20
22
|
@open = true
|
21
23
|
end
|
22
24
|
|
@@ -77,10 +79,13 @@ module Bidi2pdf
|
|
77
79
|
).tap { |interceptor| interceptor.register_with_client(client: client) }
|
78
80
|
end
|
79
81
|
|
80
|
-
def
|
81
|
-
client.on_event("network.responseStarted", "network.responseCompleted", "network.fetchError",
|
82
|
+
def navigate_to(url)
|
83
|
+
client.on_event("network.beforeRequestSent", "network.responseStarted", "network.responseCompleted", "network.fetchError",
|
82
84
|
&network_events.method(:handle_event))
|
83
85
|
|
86
|
+
client.on_event("log.entryAdded",
|
87
|
+
&logger_events.method(:handle_event))
|
88
|
+
|
84
89
|
cmd = Bidi2pdf::Bidi::Commands::BrowsingContextNavigate.new url: url, context: browsing_context_id
|
85
90
|
|
86
91
|
client.send_cmd_and_wait(cmd) do |response|
|
@@ -88,6 +93,13 @@ module Bidi2pdf
|
|
88
93
|
end
|
89
94
|
end
|
90
95
|
|
96
|
+
def render_html_content(html_content)
|
97
|
+
base64_encoded = Base64.strict_encode64(html_content)
|
98
|
+
data_url = "data:text/html;charset=utf-8;base64,#{base64_encoded}"
|
99
|
+
|
100
|
+
navigate_to(data_url)
|
101
|
+
end
|
102
|
+
|
91
103
|
def execute_script(script)
|
92
104
|
cmd = Bidi2pdf::Bidi::Commands::ScriptEvaluate.new context: browsing_context_id, expression: script
|
93
105
|
client.send_cmd_and_wait(cmd) do |response|
|
@@ -97,8 +109,29 @@ module Bidi2pdf
|
|
97
109
|
end
|
98
110
|
end
|
99
111
|
|
100
|
-
def
|
101
|
-
network_events.
|
112
|
+
def wait_until_network_idle(timeout: 10, poll_interval: 0.1)
|
113
|
+
network_events.wait_until_network_idle(timeout: timeout, poll_interval: poll_interval)
|
114
|
+
end
|
115
|
+
|
116
|
+
def log_network_traffic(format: :console, output: nil, print_options: { background: true }, &block)
|
117
|
+
format = format.to_sym
|
118
|
+
|
119
|
+
if format == :console
|
120
|
+
network_events.log_network_traffic format: :console
|
121
|
+
elsif format == :pdf
|
122
|
+
html_content = network_events.log_network_traffic format: :html
|
123
|
+
|
124
|
+
return unless html_content
|
125
|
+
|
126
|
+
logging_tab = create_browser_tab
|
127
|
+
|
128
|
+
logging_tab.render_html_content(html_content)
|
129
|
+
logging_tab.wait_until_network_idle
|
130
|
+
|
131
|
+
logging_tab.print(output, print_options: print_options, &block)
|
132
|
+
|
133
|
+
logging_tab.close
|
134
|
+
end
|
102
135
|
end
|
103
136
|
|
104
137
|
def close
|
@@ -111,7 +144,8 @@ module Bidi2pdf
|
|
111
144
|
@open = false
|
112
145
|
end
|
113
146
|
|
114
|
-
|
147
|
+
# rubocop: disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
148
|
+
def print(outputfile = nil, print_options: { background: true }, &block)
|
115
149
|
cmd = Bidi2pdf::Bidi::Commands::BrowsingContextPrint.new context: browsing_context_id, print_options: print_options
|
116
150
|
|
117
151
|
client.send_cmd_and_wait(cmd) do |response|
|
@@ -119,19 +153,25 @@ module Bidi2pdf
|
|
119
153
|
pdf_base64 = response["result"]["data"]
|
120
154
|
|
121
155
|
if outputfile
|
156
|
+
raise PrintError, "Folder does not exist: #{File.dirname(outputfile)}" unless File.directory?(File.dirname(outputfile))
|
157
|
+
|
122
158
|
File.binwrite(outputfile, Base64.decode64(pdf_base64))
|
123
159
|
Bidi2pdf.logger.info "PDF saved as '#{outputfile}'."
|
124
160
|
else
|
125
161
|
Bidi2pdf.logger.info "PDF generated successfully."
|
126
162
|
end
|
127
163
|
|
128
|
-
|
164
|
+
block.call(pdf_base64) if block_given?
|
165
|
+
|
166
|
+
return pdf_base64 unless outputfile || block_given?
|
129
167
|
else
|
130
168
|
Bidi2pdf.logger.error "Error printing: #{response}"
|
131
169
|
end
|
132
170
|
end
|
133
171
|
end
|
134
172
|
|
173
|
+
# rubocop: enable Metrics/AbcSize, Metrics/PerceivedComplexity
|
174
|
+
|
135
175
|
private
|
136
176
|
|
137
177
|
def close_context
|
@@ -6,6 +6,14 @@ module Bidi2pdf
|
|
6
6
|
class SetTabCookie
|
7
7
|
include Base
|
8
8
|
|
9
|
+
class << self
|
10
|
+
attr_writer :time_provider
|
11
|
+
|
12
|
+
def time_provider
|
13
|
+
@time_provider ||= -> { Time.now }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
9
17
|
attr_reader :name, :value, :domain, :path, :secure, :http_only, :same_site, :ttl, :browsing_context_id
|
10
18
|
|
11
19
|
def initialize(name:,
|
@@ -29,7 +37,7 @@ module Bidi2pdf
|
|
29
37
|
end
|
30
38
|
|
31
39
|
def expiry
|
32
|
-
|
40
|
+
self.class.time_provider.call.to_i + ttl
|
33
41
|
end
|
34
42
|
|
35
43
|
def method_name
|