bidi2pdf 0.1.8 → 0.1.10
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 +88 -3
- data/README.md +146 -7
- data/docker/Dockerfile.chromedriver +23 -5
- data/docker/entrypoint.sh +41 -0
- data/lib/bidi2pdf/bidi/auth_interceptor.rb +3 -0
- data/lib/bidi2pdf/bidi/browser_tab.rb +60 -19
- data/lib/bidi2pdf/bidi/client.rb +7 -5
- data/lib/bidi2pdf/bidi/commands/cdp_get_session.rb +21 -0
- data/lib/bidi2pdf/bidi/commands/page_print.rb +101 -0
- data/lib/bidi2pdf/bidi/commands/print_parameters_validator.rb +4 -1
- data/lib/bidi2pdf/bidi/commands.rb +2 -0
- data/lib/bidi2pdf/bidi/connection_manager.rb +3 -0
- data/lib/bidi2pdf/bidi/event_manager.rb +34 -4
- data/lib/bidi2pdf/bidi/interceptor.rb +12 -2
- data/lib/bidi2pdf/bidi/session.rb +35 -2
- data/lib/bidi2pdf/bidi/web_socket_dispatcher.rb +5 -5
- data/lib/bidi2pdf/chromedriver_manager.rb +25 -11
- data/lib/bidi2pdf/cli.rb +9 -2
- data/lib/bidi2pdf/test_helpers/configuration.rb +67 -0
- data/lib/bidi2pdf/test_helpers/images/extractor.rb +99 -0
- data/lib/bidi2pdf/test_helpers/images/image_similarity_checker.rb +50 -0
- data/lib/bidi2pdf/test_helpers/images/tiff_helper.rb +204 -0
- data/lib/bidi2pdf/test_helpers/images.rb +12 -0
- data/lib/bidi2pdf/test_helpers/matchers/contains_pdf_image.rb +29 -0
- data/lib/bidi2pdf/test_helpers/pdf_file_helper.rb +39 -0
- data/lib/bidi2pdf/test_helpers/spec_paths_helper.rb +60 -0
- data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_container.rb +0 -6
- data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_test_helper.rb +103 -0
- data/lib/bidi2pdf/test_helpers/testcontainers/shared_docker_network.rb +21 -0
- data/lib/bidi2pdf/test_helpers/testcontainers/testcontainers_refinement.rb +53 -0
- data/lib/bidi2pdf/test_helpers/testcontainers.rb +17 -0
- data/lib/bidi2pdf/test_helpers.rb +7 -0
- data/lib/bidi2pdf/version.rb +1 -1
- data/sig/bidi2pdf/bidi/event_manager.rbs +19 -13
- metadata +55 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 134f35256652da3e5e6c8e383e97c98779a1e2665798dbfa5d133bc85130b8cc
|
4
|
+
data.tar.gz: dd106af3e757d26ba3d935bbe54d0249402280a84f1b1a8e27855d19b4cd155c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15d9df827cefb697a44f78945f5fc20866319c7fc6a403f5ad58817f239deca9857fc0923e0a7faf582dac6d7f0770236310cca8bd58aab138ab6499749c6d37
|
7
|
+
data.tar.gz: '09b043e8da1b79a4cc95c708c32afa78b00fa3f6edbedd2e0dacfdd562aff1a385be50f36cc78951a771b1b63c775eac11980dec07d1998476b9b247d6114377'
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
<!-- generated by git-cliff start -->
|
2
|
-
|
3
2
|
# Changelog
|
4
3
|
|
5
4
|
All notable changes to this project will be documented in this file.
|
@@ -7,10 +6,93 @@ All notable changes to this project will be documented in this file.
|
|
7
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
8
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
9
8
|
|
10
|
-
[unreleased]: https://github.com
|
9
|
+
[unreleased]: https://github.com///compare/v0.1.10..HEAD
|
11
10
|
|
12
11
|
<!-- generated by git-cliff end -->
|
13
12
|
|
13
|
+
## [0.1.10] - 2025-06-18
|
14
|
+
|
15
|
+
### 💄 Style
|
16
|
+
|
17
|
+
- Improve readability of conditional statements
|
18
|
+
|
19
|
+
### 📝 Docs
|
20
|
+
|
21
|
+
- Update README with Quick Start section
|
22
|
+
- Add Table of Contents and architecture diagram
|
23
|
+
- Add high level overview diagram to README
|
24
|
+
- Update example URLs in README for clarity
|
25
|
+
|
26
|
+
### 🔄 Changed
|
27
|
+
|
28
|
+
- Merge pull request #19 from dieter-medium/enhance-docs
|
29
|
+
- Merge pull request #18 from dieter-medium/enhance-docs
|
30
|
+
- Merge pull request #13 from dieter-medium/dependabot/bundler/main/base64-0.3.0
|
31
|
+
- Merge branch 'main' into dependabot/bundler/main/base64-0.3.0
|
32
|
+
- Merge pull request #14 from dieter-medium/dependabot/bundler/main/rake-13.3.0
|
33
|
+
- Merge branch 'main' into dependabot/bundler/main/rake-13.3.0
|
34
|
+
- Merge pull request #12 from dieter-medium/dependabot/bundler/main/rspec-3.13.1
|
35
|
+
- Merge pull request #11 from dieter-medium/dependabot/bundler/main/rubocop-1.75.7
|
36
|
+
- Merge branch 'main' into dependabot/bundler/main/rubocop-1.75.7
|
37
|
+
- Merge pull request #10 from dieter-medium/dependabot/bundler/main/json-2.12.2
|
38
|
+
- Merge branch 'main' into dependabot/bundler/main/json-2.12.2
|
39
|
+
- Merge pull request #9 from dieter-medium/dependabot/bundler/main/diff-lcs-1.6.2
|
40
|
+
- Merge pull request #7 from dieter-medium/dependabot/bundler/main/rubocop-1.75.6
|
41
|
+
- Merge pull request #8 from dieter-medium/dependabot/bundler/main/json-2.12.0
|
42
|
+
- Merge pull request #6 from dieter-medium/dependabot/bundler/main/rbs-3.9.4
|
43
|
+
- Merge pull request #5 from dieter-medium/dependabot/bundler/main/chromedriver-binary-0.1.3
|
44
|
+
- Merge pull request #4 from dieter-medium/add-more-default-chrome-args
|
45
|
+
- Merge pull request #3 from dieter-medium/make-configuration-rw
|
46
|
+
- Merge pull request #2 from dieter-medium/add-test-helpers
|
47
|
+
- Merge pull request #1 from dieter-medium/testing-vips
|
48
|
+
|
49
|
+
### 🔧 Build
|
50
|
+
|
51
|
+
- Bump rspec from 3.13.0 to 3.13.1
|
52
|
+
- Bump rake from 13.2.1 to 13.3.0
|
53
|
+
- Update base64 requirement from ~> 0.2.0 to >= 0.2, < 0.4
|
54
|
+
- Bump rubocop from 1.75.3 to 1.76.2
|
55
|
+
- Bump diff-lcs from 1.6.1 to 1.6.2
|
56
|
+
- Bump json from 2.10.2 to 2.12.2
|
57
|
+
- Bump rbs from 3.9.2 to 3.9.4
|
58
|
+
- Bump chromedriver-binary from 0.1.2 to 0.1.3
|
59
|
+
|
60
|
+
### 🚀 Added
|
61
|
+
|
62
|
+
- Add CDP session handling for PDF generation
|
63
|
+
- Expand default Chrome arguments for sessions
|
64
|
+
- Add configuration setter for TestHelpers
|
65
|
+
- Add test helpers for PDF testing and handling
|
66
|
+
- Add image extraction and similarity checking
|
67
|
+
|
68
|
+
## [0.1.9] - 2025-05-04
|
69
|
+
|
70
|
+
### 🎨 Refactored
|
71
|
+
|
72
|
+
- Enhance reader thread management in Chromedriver by @dieter-medium
|
73
|
+
- Improve event listener logging by @dieter-medium
|
74
|
+
- Improve event listener management by @dieter-medium
|
75
|
+
- Centralize chromedriver test helpers for reuse in other projects by @dieter-medium
|
76
|
+
|
77
|
+
### 🐛 Fixed
|
78
|
+
|
79
|
+
- Close event socket during session cleanup by @dieter-medium
|
80
|
+
- Add test for generating PDFs in parallel by @dieter-medium
|
81
|
+
|
82
|
+
### 💄 Style
|
83
|
+
|
84
|
+
- Update Bootstrap stylesheet link by @dieter-medium
|
85
|
+
|
86
|
+
### 🔧 Build
|
87
|
+
|
88
|
+
- Update Ruby version in action.yml by @dieter-medium
|
89
|
+
- Update Dockerfile.chromedriver and chromedriver.yml for enhancements by @dieter-medium
|
90
|
+
- Update Dockerfile.chromedriver for improved environment setup and vnc support by @dieter-medium
|
91
|
+
|
92
|
+
### 🚀 Added
|
93
|
+
|
94
|
+
- Enhance testcontainers with shared network support by @dieter-medium
|
95
|
+
|
14
96
|
## [0.1.8] - 2025-04-22
|
15
97
|
|
16
98
|
### 🎨 Refactored
|
@@ -172,8 +254,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
172
254
|
|
173
255
|
- Initial release
|
174
256
|
|
257
|
+
## Changelog
|
175
258
|
|
176
|
-
- [unreleased](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.
|
259
|
+
- [unreleased](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.10..HEAD)
|
260
|
+
- [0.1.10](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.9..V0.1.10)
|
261
|
+
- [0.1.9](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.8..v0.1.9)
|
177
262
|
- [0.1.8](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.7..v0.1.8)
|
178
263
|
- [0.1.7](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.6..v0.1.7)
|
179
264
|
- [0.1.6](https://github.com/dieter-medium/bidi2pdf/compare/v0.1.5..v0.1.6)
|
data/README.md
CHANGED
@@ -14,6 +14,23 @@ Bidi2pdf gives you **precision, flexibility, and full control**.
|
|
14
14
|
|
15
15
|
---
|
16
16
|
|
17
|
+
## 📚 Table of Contents
|
18
|
+
|
19
|
+
1. [Key Features](#key-features)
|
20
|
+
2. [Quick Start](#quick-start)
|
21
|
+
3. [Why BiDi?](#why-bidi-instead-of-cdp)
|
22
|
+
4. [Installation](#installation)
|
23
|
+
5. [CLI Usage](#cli-usage)
|
24
|
+
6. [Library API](#library-api)
|
25
|
+
7. [Architecture](#architecture)
|
26
|
+
8. [Docker](#docker)
|
27
|
+
9. [Configuration Options](#configuration-options)
|
28
|
+
10. [Rails Integration](#rails-integration)
|
29
|
+
11. [Test Helpers](#test-helpers)
|
30
|
+
12. [Development](#development)
|
31
|
+
13. [Contributing](#contributing)
|
32
|
+
14. [License](#license)
|
33
|
+
|
17
34
|
## ✨ Key Features
|
18
35
|
|
19
36
|
✅ **One-liner CLI** – From URL to PDF in a single command
|
@@ -27,6 +44,25 @@ Bidi2pdf gives you **precision, flexibility, and full control**.
|
|
27
44
|
|
28
45
|
---
|
29
46
|
|
47
|
+
## ⚡ Quick Start
|
48
|
+
|
49
|
+
Get up and running in three easy steps:
|
50
|
+
|
51
|
+
```bash
|
52
|
+
# 1. Install the gem (system-wide)
|
53
|
+
gem install bidi2pdf
|
54
|
+
|
55
|
+
# 2. Render any page to PDF
|
56
|
+
bidi2pdf render --url https://example.com --output example.pdf
|
57
|
+
|
58
|
+
# 3. Open the PDF (macOS shown; use xdg-open on Linux)
|
59
|
+
open example.pdf
|
60
|
+
```
|
61
|
+
|
62
|
+
> **Bundler users** – Add it to your project with `bundle add bidi2pdf`.
|
63
|
+
|
64
|
+
---
|
65
|
+
|
30
66
|
## 🚀 Installation
|
31
67
|
|
32
68
|
### Bundler
|
@@ -54,14 +90,14 @@ gem install bidi2pdf
|
|
54
90
|
### Command-line
|
55
91
|
|
56
92
|
```bash
|
57
|
-
bidi2pdf render --url https://example.com --output example.pdf
|
93
|
+
bidi2pdf render --url https://example.com/invoice/14432423 --output example.pdf
|
58
94
|
```
|
59
95
|
|
60
96
|
### Advanced CLI Options
|
61
97
|
|
62
98
|
```bash
|
63
99
|
bidi2pdf render \
|
64
|
-
--url https://example.com \
|
100
|
+
--url https://example.com/invoice/14432423 \
|
65
101
|
--output example.pdf \
|
66
102
|
--cookie session=abc123 \
|
67
103
|
--header X-API-KEY=token \
|
@@ -81,7 +117,7 @@ bidi2pdf render \
|
|
81
117
|
require 'bidi2pdf'
|
82
118
|
|
83
119
|
launcher = Bidi2pdf::Launcher.new(
|
84
|
-
url: 'https://example.com',
|
120
|
+
url: 'https://example.com/invoice/14432423',
|
85
121
|
output: 'example.pdf',
|
86
122
|
cookies: { 'session' => 'abc123' },
|
87
123
|
headers: { 'X-API-KEY' => 'token' },
|
@@ -99,7 +135,7 @@ launcher.launch
|
|
99
135
|
require "bidi2pdf"
|
100
136
|
|
101
137
|
Bidi2pdf::DSL.with_tab(headless: true) do |tab|
|
102
|
-
tab.navigate_to("https://example.com")
|
138
|
+
tab.navigate_to("https://example.com/invoice/14432423")
|
103
139
|
tab.wait_until_network_idle
|
104
140
|
tab.print("example.pdf")
|
105
141
|
end
|
@@ -145,7 +181,7 @@ tab.basic_auth(url_patterns: [{ type: "pattern", protocol: "https", hostname: "e
|
|
145
181
|
username: "username", password: "secret")
|
146
182
|
|
147
183
|
# 4. Render PDF
|
148
|
-
tab.navigate_to "https://example.com"
|
184
|
+
tab.navigate_to "https://example.com/invoice/14432423"
|
149
185
|
|
150
186
|
# Alternative: send html code to the browser
|
151
187
|
# tab.render_html_content("<html>...</html>")
|
@@ -176,6 +212,48 @@ session.close
|
|
176
212
|
|
177
213
|
---
|
178
214
|
|
215
|
+
## 🌐 Architecture
|
216
|
+
|
217
|
+
```mermaid
|
218
|
+
%%{ init: {
|
219
|
+
"theme": "base",
|
220
|
+
"themeVariables": {
|
221
|
+
"primaryColor": "#E0E7FF",
|
222
|
+
"secondaryColor":"#FEF9C3",
|
223
|
+
"edgeLabelBackground":"#FFFFFF",
|
224
|
+
"fontSize":"14px",
|
225
|
+
"nodeBorderRadius":"6"
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}%%
|
229
|
+
flowchart LR
|
230
|
+
%% ----- Ruby side ---------
|
231
|
+
A["fa:fa-gem Ruby Application"]
|
232
|
+
B["fa:fa-gem bidi2pdf<br/>Library"]
|
233
|
+
%% ----Chrome environment -----------
|
234
|
+
subgraph C["fa:fa-chrome Chrome Environment"]
|
235
|
+
direction TB
|
236
|
+
C1["fa:fa-chrome Local Chrome<br/>(sub-process)"]
|
237
|
+
C2["fa:fa-docker Docker Chrome<br/>(remote)"]
|
238
|
+
end
|
239
|
+
|
240
|
+
D[[PDF File]]
|
241
|
+
%% ---- Data / control flows ------
|
242
|
+
A -- " HTML / URL + JS / CSS " --> B
|
243
|
+
B -- " WebDriver BiDi " --> C1
|
244
|
+
B -- " WebDriver BiDi " --> C2
|
245
|
+
C1 -- " PDF bytes " --> B
|
246
|
+
C2 -- " PDF bytes " --> B
|
247
|
+
B -- " PDF " --> D
|
248
|
+
%% --- Optional extra styling classes (for future tweaks) ---
|
249
|
+
classDef ruby fill:#E0E7FF,stroke:#6366F1,color:#1E1B4B;
|
250
|
+
classDef chrome fill:#FEF9C3,stroke:#F59E0B,color:#78350F;
|
251
|
+
class A,B ruby;
|
252
|
+
class C1,C2 chrome;
|
253
|
+
```
|
254
|
+
|
255
|
+
---
|
256
|
+
|
179
257
|
## 🐳 Docker Support
|
180
258
|
|
181
259
|
### 🛠️ Build & Run Locally
|
@@ -191,7 +269,7 @@ docker build -t bidi2pdf -f docker/Dockerfile .
|
|
191
269
|
docker run -it --rm \
|
192
270
|
-v ./output:/reports \
|
193
271
|
bidi2pdf \
|
194
|
-
bidi2pdf render --url=https://example.com --output /reports/example.pdf
|
272
|
+
bidi2pdf render --url=https://example.com/invoice/14432423 --output /reports/example.pdf
|
195
273
|
|
196
274
|
```
|
197
275
|
|
@@ -203,7 +281,7 @@ Grab it directly from [Docker Hub](https://hub.docker.com/r/dieters877565/bidi2p
|
|
203
281
|
docker run -it --rm \
|
204
282
|
-v ./output:/reports \
|
205
283
|
dieters877565/bidi2pdf:main-slim \
|
206
|
-
bidi2pdf render --url=https://example.com --output /reports/example.pdf
|
284
|
+
bidi2pdf render --url=https://example.com/invoice/14432423 --output /reports/example.pdf
|
207
285
|
```
|
208
286
|
|
209
287
|
✅ Tip: Mount your local directory (e.g. ./output) to /reports in the container to easily access the generated PDFs.
|
@@ -271,6 +349,67 @@ visit: [https://github.com/dieter-medium/bidi2pdf-rails](https://github.com/diet
|
|
271
349
|
|
272
350
|
---
|
273
351
|
|
352
|
+
## 🧪 Test Helpers
|
353
|
+
|
354
|
+
Bidi2pdf provides a suite of RSpec helpers (activated with `pdf: true`) to
|
355
|
+
simplify PDF-related testing:
|
356
|
+
|
357
|
+
### SpecPathsHelper
|
358
|
+
|
359
|
+
– `spec_dir` → returns your spec directory
|
360
|
+
– `tmp_dir` → returns your tmp directory
|
361
|
+
– `tmp_file(*parts)` → builds a tmp file path
|
362
|
+
– `random_tmp_dir(*dirs, prefix:)` → builds a random tmp directory
|
363
|
+
|
364
|
+
- `fixture_file(*parts)` → returns the path to a fixture file
|
365
|
+
|
366
|
+
### PdfFileHelper
|
367
|
+
|
368
|
+
– `with_pdf_debug(pdf_data) { |data| … }` → on failure, writes PDF to disk
|
369
|
+
– `store_pdf_file(pdf_data, filename_prefix = "test")` → saves PDF and returns path
|
370
|
+
|
371
|
+
### Rspec Matchers
|
372
|
+
|
373
|
+
- `have_pdf_page_count` → checks if the PDF has a specific number of pages
|
374
|
+
- `match_pdf_text` → checks if the PDF equals a specific text, after stripping whitespace and normalizing characters
|
375
|
+
- `contains_pdf_text` → checks if the PDF contains a specific text, after stripping whitespace and normalizing
|
376
|
+
characters, supporting regex
|
377
|
+
- `contains_pdf_image` → checks if the PDF contains a specific image
|
378
|
+
|
379
|
+
### ChromedriverContainer
|
380
|
+
|
381
|
+
`require "bidi2pdf/test_helpers/testcontainers"` you can use the `chromedriver_container` helper to
|
382
|
+
start a ChromeDriver container for your tests. This is useful if you don't want to run ChromeDriver locally
|
383
|
+
or if you want to ensure a clean environment for your tests.
|
384
|
+
|
385
|
+
This also provides the helper methods:
|
386
|
+
|
387
|
+
- `session_url` → returns the session URL for the ChromeDriver container
|
388
|
+
- `chromedriver_container` → returns the Testcontainers container object
|
389
|
+
- `create_session` -> creates a `Bidi2pdf::Bidi::Session` object for the ChromeDriver container
|
390
|
+
|
391
|
+
With the environment variable `DISABLE_CHROME_SANDBOX` set to `true`, the container will run Chrome without
|
392
|
+
the sandbox. This is useful for CI environments where the sandbox may cause issues.
|
393
|
+
|
394
|
+
#### Example
|
395
|
+
|
396
|
+
```ruby
|
397
|
+
require "bidi2pdf/test_helpers"
|
398
|
+
require "bidi2pdf/test_helpers/images" # <= for image matching, requires lib-vips
|
399
|
+
require "bidi2pdf/test_helpers/testcontainers" # <= requires testcontainers gem
|
400
|
+
|
401
|
+
RSpec.describe "PDF generation", :pdf, :chromedriver do
|
402
|
+
it "generates a PDF with the correct content" do
|
403
|
+
pdf_data = generate_pdf("https://example.com/invoice/14432423")
|
404
|
+
expect(pdf_data).to have_pdf_page_count(1)
|
405
|
+
expect(pdf_data).to match_pdf_text("Hello, world!")
|
406
|
+
expect(pdf_data).to contain_pdf_image(fixture_file("logo.png"))
|
407
|
+
end
|
408
|
+
end
|
409
|
+
```
|
410
|
+
|
411
|
+
---
|
412
|
+
|
274
413
|
## 🛠 Development
|
275
414
|
|
276
415
|
```bash
|
@@ -1,13 +1,16 @@
|
|
1
|
-
FROM
|
1
|
+
FROM debian:bookworm-slim
|
2
2
|
|
3
3
|
ARG CHROMEDRIVER_PORT=3000
|
4
4
|
|
5
5
|
ENV DEBIAN_FRONTEND=noninteractive
|
6
|
+
ENV LANG=en_US.UTF-8
|
6
7
|
|
7
8
|
# Install dependencies
|
8
|
-
RUN
|
9
|
+
RUN echo "deb http://deb.debian.org/debian bookworm contrib non-free" > /etc/apt/sources.list.d/contrib.list &&\
|
10
|
+
echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | debconf-set-selections &&\
|
11
|
+
apt-get update && apt-get upgrade -y && \
|
9
12
|
apt-get install -y --no-install-recommends\
|
10
|
-
chromium chromium-driver\
|
13
|
+
chromium chromium-driver chromium-l10n chromium-sandbox\
|
11
14
|
libglib2.0-0 \
|
12
15
|
libnss3 \
|
13
16
|
libxss1 \
|
@@ -18,10 +21,21 @@ RUN apt-get update && apt-get upgrade -y && \
|
|
18
21
|
curl \
|
19
22
|
unzip \
|
20
23
|
xvfb \
|
24
|
+
x11vnc \
|
25
|
+
fluxbox \
|
26
|
+
xterm \
|
27
|
+
wmctrl \
|
28
|
+
net-tools xauth \
|
29
|
+
fonts-liberation fonts-dejavu-core fonts-noto-core fonts-noto-cjk fonts-noto-color-emoji fonts-symbola fontconfig ttf-mscorefonts-installer\
|
30
|
+
libnss3 libatk1.0-0 \
|
31
|
+
libx11-6 libxss1 libgtk-3-0 libgbm1 \
|
32
|
+
locales sed \
|
33
|
+
&& sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen \
|
34
|
+
&& locale-gen en_US.UTF-8 \
|
21
35
|
&& rm -rf /var/lib/apt/lists/*
|
22
36
|
|
23
37
|
# Create a non-root user
|
24
|
-
RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser
|
38
|
+
RUN groupadd -r appuser && useradd -r -g appuser -G audio,video -m -d /home/appuser appuser
|
25
39
|
|
26
40
|
COPY ./docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
27
41
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
@@ -36,12 +50,16 @@ RUN mkdir -p /home/appuser/.webdrivers && ln -s /usr/bin/chromedriver /home/appu
|
|
36
50
|
# Set working directory
|
37
51
|
WORKDIR /app
|
38
52
|
|
53
|
+
RUN mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix
|
54
|
+
|
39
55
|
# Switch to non-root user
|
40
56
|
USER appuser
|
41
57
|
|
42
|
-
RUN gem install chromedriver-binary && ruby -e 'require "chromedriver/binary"; puts Chromedriver::Binary::ChromedriverDownloader.update'
|
58
|
+
# RUN gem install chromedriver-binary && ruby -e 'require "chromedriver/binary"; puts Chromedriver::Binary::ChromedriverDownloader.update'
|
43
59
|
|
44
60
|
ENV CHROMEDRIVER_PORT=${CHROMEDRIVER_PORT}
|
45
61
|
EXPOSE ${CHROMEDRIVER_PORT}
|
62
|
+
# VNC
|
63
|
+
EXPOSE 5900
|
46
64
|
|
47
65
|
CMD ["/usr/local/bin/entrypoint.sh"]
|
data/docker/entrypoint.sh
CHANGED
@@ -3,6 +3,47 @@
|
|
3
3
|
USER_DATA_DIR=/home/appuser/.cache
|
4
4
|
mkdir -p ${USER_DATA_DIR}
|
5
5
|
|
6
|
+
if [ "$ENABLE_XVFB" = "true" ]; then
|
7
|
+
rm -rf /tmp/.X99-lock
|
8
|
+
|
9
|
+
export DISPLAY=:99
|
10
|
+
Xvfb :99 -screen 0 1920x1080x24 &
|
11
|
+
|
12
|
+
old_umask=$(umask)
|
13
|
+
umask 077
|
14
|
+
|
15
|
+
touch /home/appuser/.Xauthority
|
16
|
+
export XAUTHORITY=/home/appuser/.Xauthority
|
17
|
+
|
18
|
+
xauth generate :99 . trusted
|
19
|
+
|
20
|
+
umask $old_umask
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
until xdpyinfo -display ${DISPLAY} >/dev/null 2>&1; do
|
25
|
+
sleep 0.2
|
26
|
+
done
|
27
|
+
|
28
|
+
fluxbox &
|
29
|
+
|
30
|
+
until wmctrl -m > /dev/null 2>&1; do
|
31
|
+
sleep 0.2
|
32
|
+
done
|
33
|
+
fi
|
34
|
+
|
35
|
+
if [ "$ENABLE_VNC" = "true" ]; then
|
36
|
+
VNC_PASS=${VNC_PASS:-$(tr -dc A-Za-z0-9 </dev/urandom | head -c 12)}
|
37
|
+
echo "VNC password: $VNC_PASS"
|
38
|
+
old_umask=$(umask)
|
39
|
+
umask 077
|
40
|
+
mkdir -p /home/appuser/.vnc
|
41
|
+
x11vnc -storepasswd $VNC_PASS /home/appuser/.vnc/passwd
|
42
|
+
umask $old_umask
|
43
|
+
x11vnc -display WAIT:99 -xkb -noxrecord -noxfixes -noxdamage -forever -shared -noshm -usepw -rfbauth /home/appuser/.vnc/passwd &
|
44
|
+
fi
|
45
|
+
|
46
|
+
# DISPLAY=:99 /home/appuser/.webdrivers/chromedriver --port=33259 --whitelisted-ips="" --allowed-origins="*" --disable-dev-shm-usage --disable-gpu --verbose
|
6
47
|
/home/appuser/.webdrivers/chromedriver --port=${CHROMEDRIVER_PORT} \
|
7
48
|
--headless \
|
8
49
|
--whitelisted-ips="" \
|
@@ -39,6 +39,7 @@ module Bidi2pdf
|
|
39
39
|
|
40
40
|
private
|
41
41
|
|
42
|
+
# rubocop:disable Naming/PredicateMethod
|
42
43
|
def handled_bad_credentials(navigation_id, network_id, url)
|
43
44
|
return false unless network_ids.include?(network_id)
|
44
45
|
|
@@ -55,6 +56,8 @@ module Bidi2pdf
|
|
55
56
|
|
56
57
|
true
|
57
58
|
end
|
59
|
+
|
60
|
+
# rubocop:enable Naming/PredicateMethod
|
58
61
|
end
|
59
62
|
end
|
60
63
|
end
|
@@ -141,7 +141,7 @@ module Bidi2pdf
|
|
141
141
|
headers:,
|
142
142
|
url_patterns:
|
143
143
|
)
|
144
|
-
AddHeadersInterceptor.new(
|
144
|
+
@header_interceptor = AddHeadersInterceptor.new(
|
145
145
|
context: browsing_context_id,
|
146
146
|
url_patterns: url_patterns,
|
147
147
|
headers: headers
|
@@ -155,7 +155,7 @@ module Bidi2pdf
|
|
155
155
|
# @param [Array<String>] url_patterns The URL patterns to match.
|
156
156
|
# @return [AuthInterceptor] The interceptor instance.
|
157
157
|
def basic_auth(username:, password:, url_patterns:)
|
158
|
-
AuthInterceptor.new(
|
158
|
+
@basic_auth_interceptor = AuthInterceptor.new(
|
159
159
|
context: browsing_context_id,
|
160
160
|
url_patterns: url_patterns,
|
161
161
|
username: username, password: password
|
@@ -371,13 +371,13 @@ module Bidi2pdf
|
|
371
371
|
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
372
372
|
def print(outputfile = nil, print_options: { background: true }, &block)
|
373
373
|
Bidi2pdf.notification_service.instrument("print.bidi2pdf") do |instrumentation_payload|
|
374
|
-
cmd =
|
374
|
+
cmd, extractor = build_command_and_extractor(print_options)
|
375
375
|
|
376
376
|
instrumentation_payload[:cmd] = cmd
|
377
377
|
|
378
378
|
client.send_cmd_and_wait(cmd) do |response|
|
379
379
|
if response["result"]
|
380
|
-
pdf_base64 = response
|
380
|
+
pdf_base64 = extractor.call response
|
381
381
|
|
382
382
|
instrumentation_payload[:pdf_base64] = pdf_base64
|
383
383
|
|
@@ -404,6 +404,36 @@ module Bidi2pdf
|
|
404
404
|
|
405
405
|
private
|
406
406
|
|
407
|
+
def build_command_and_extractor(print_options)
|
408
|
+
cmd_type = (print_options.delete(:cmd_type) || :bidi).to_sym
|
409
|
+
|
410
|
+
if cmd_type == :bidi
|
411
|
+
cmd = Bidi2pdf::Bidi::Commands::BrowsingContextPrint.new(
|
412
|
+
context: browsing_context_id,
|
413
|
+
print_options: print_options
|
414
|
+
)
|
415
|
+
extractor = ->(response) { response.dig("result", "data") }
|
416
|
+
else
|
417
|
+
cmd = Bidi2pdf::Bidi::Commands::PagePrint.new(
|
418
|
+
cdp_session: cdp_session,
|
419
|
+
print_options: print_options
|
420
|
+
)
|
421
|
+
extractor = ->(response) { response.dig("result", "result", "data") }
|
422
|
+
end
|
423
|
+
|
424
|
+
[cmd, extractor]
|
425
|
+
end
|
426
|
+
|
427
|
+
def cdp_session
|
428
|
+
@cdp_session ||= begin
|
429
|
+
cmd = Bidi2pdf::Bidi::Commands::CdpGetSession.new context: browsing_context_id
|
430
|
+
client.send_cmd_and_wait(cmd) do |response|
|
431
|
+
Bidi2pdf.logger.debug "CDP session: #{response.inspect}"
|
432
|
+
response["result"]["session"]
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
407
437
|
def navigate_with_listeners(url)
|
408
438
|
register_event_listeners
|
409
439
|
|
@@ -430,14 +460,14 @@ module Bidi2pdf
|
|
430
460
|
return if @event_handlers_registered
|
431
461
|
|
432
462
|
@event_handlers_registered = true
|
463
|
+
@listener_refs ||= {}
|
433
464
|
|
434
|
-
client.on_event("network.beforeRequestSent", "network.responseStarted", "network.responseCompleted", "network.fetchError",
|
435
|
-
|
465
|
+
@listener_refs[:network] = client.on_event("network.beforeRequestSent", "network.responseStarted", "network.responseCompleted", "network.fetchError",
|
466
|
+
&network_events.method(:handle_event))
|
436
467
|
|
437
|
-
client.on_event("log.entryAdded",
|
438
|
-
&logger_events.method(:handle_event))
|
468
|
+
@listener_refs[:logger] = client.on_event("log.entryAdded", &logger_events.method(:handle_event))
|
439
469
|
|
440
|
-
client.on_event("browsingContext.navigationFailed", &navigation_failed_events.method(:handle_event))
|
470
|
+
@listener_refs[:navigation_failed] = client.on_event("browsingContext.navigationFailed", &navigation_failed_events.method(:handle_event))
|
441
471
|
end
|
442
472
|
|
443
473
|
def handle_injection_exception(response, url, exception_class)
|
@@ -479,7 +509,7 @@ module Bidi2pdf
|
|
479
509
|
const script = document.createElement('script');
|
480
510
|
script.type = 'text/javascript';
|
481
511
|
|
482
|
-
#{
|
512
|
+
#{"script.text = #{content.to_json};" if content}
|
483
513
|
|
484
514
|
script.addEventListener(
|
485
515
|
'error',
|
@@ -489,12 +519,12 @@ module Bidi2pdf
|
|
489
519
|
{once: true},
|
490
520
|
);
|
491
521
|
|
492
|
-
#{
|
522
|
+
#{"script.id = '#{id}';" if id}
|
493
523
|
#{js_src_part}
|
494
524
|
|
495
525
|
document.head.appendChild(script);
|
496
526
|
|
497
|
-
#{
|
527
|
+
#{"resolve(script);" unless url}
|
498
528
|
});
|
499
529
|
JS
|
500
530
|
end
|
@@ -515,7 +545,7 @@ module Bidi2pdf
|
|
515
545
|
link.type = 'text/css';
|
516
546
|
link.href = '#{url}';
|
517
547
|
#{" "}
|
518
|
-
#{
|
548
|
+
#{"link.id = '#{id}';" if id}
|
519
549
|
#{" "}
|
520
550
|
link.addEventListener(
|
521
551
|
'load',
|
@@ -544,9 +574,9 @@ module Bidi2pdf
|
|
544
574
|
const style = document.createElement('style');
|
545
575
|
style.type = 'text/css';
|
546
576
|
#{" "}
|
547
|
-
#{
|
577
|
+
#{"style.id = '#{id}';" if id}
|
548
578
|
#{" "}
|
549
|
-
#{
|
579
|
+
#{"style.textContent = #{content.to_json};" if content}
|
550
580
|
#{" "}
|
551
581
|
document.head.appendChild(style);
|
552
582
|
resolve(style);
|
@@ -568,16 +598,27 @@ module Bidi2pdf
|
|
568
598
|
end
|
569
599
|
|
570
600
|
# Removes event listeners for the browser tab.
|
601
|
+
# rubocop:disable Metrics/AbcSize
|
571
602
|
def remove_event_listeners
|
603
|
+
return if @listener_refs.nil? || @listener_refs.empty?
|
604
|
+
|
572
605
|
Bidi2pdf.logger.debug2 "Network events: #{network_events.all_events.map(&:to_s)}"
|
573
606
|
|
574
|
-
client.remove_event_listener "network.responseStarted", "network.responseCompleted", "network.fetchError",
|
575
|
-
|
607
|
+
client.remove_event_listener("network.beforeRequestSent", "network.responseStarted", "network.responseCompleted", "network.fetchError",
|
608
|
+
@listener_refs[:network])
|
576
609
|
|
577
|
-
client.remove_event_listener("log.entryAdded",
|
578
|
-
|
610
|
+
client.remove_event_listener("log.entryAdded", @listener_refs[:logger])
|
611
|
+
|
612
|
+
@header_interceptor&.unregister_with_client(client: client)
|
613
|
+
@basic_auth_interceptor&.unregister_with_client(client: client)
|
614
|
+
|
615
|
+
@header_interceptor = nil
|
616
|
+
@basic_auth_interceptor = nil
|
617
|
+
@listener_refs = {}
|
579
618
|
end
|
580
619
|
|
620
|
+
# rubocop:enable Metrics/AbcSize
|
621
|
+
|
581
622
|
# Closes all tabs associated with the browser tab.
|
582
623
|
def close_tabs
|
583
624
|
tabs.each do |tab|
|
data/lib/bidi2pdf/bidi/client.rb
CHANGED
@@ -125,23 +125,25 @@ module Bidi2pdf
|
|
125
125
|
#
|
126
126
|
# @param [Array<String>] names The names of the events to subscribe to.
|
127
127
|
# @yield [event_data] A block to handle the event data.
|
128
|
-
def on_event(*names, &
|
129
|
-
|
128
|
+
def on_event(*names, &)
|
129
|
+
listener = dispatcher.on_event(*names, &)
|
130
130
|
cmd = Bidi2pdf::Bidi::Commands::SessionSubscribe.new(events: names)
|
131
131
|
send_cmd(cmd) if names.any?
|
132
|
+
|
133
|
+
listener
|
132
134
|
end
|
133
135
|
|
134
136
|
# Removes a message listener.
|
135
137
|
#
|
136
138
|
# @param [Proc] block The listener block to remove.
|
137
|
-
def remove_message_listener(
|
139
|
+
def remove_message_listener(listener) = dispatcher.remove_message_listener(listener)
|
138
140
|
|
139
141
|
# Removes event listeners for specific events.
|
140
142
|
#
|
141
143
|
# @param [Array<String>] names The names of the events to unsubscribe from.
|
142
144
|
# @param [Proc] block The listener block to remove.
|
143
|
-
def remove_event_listener(*names,
|
144
|
-
names.each { |event_name| dispatcher.remove_event_listener(event_name,
|
145
|
+
def remove_event_listener(*names, listener)
|
146
|
+
names.each { |event_name| dispatcher.remove_event_listener(event_name, listener) }
|
145
147
|
end
|
146
148
|
|
147
149
|
# Closes the WebSocket connection.
|