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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -3
  3. data/README.md +146 -7
  4. data/docker/Dockerfile.chromedriver +23 -5
  5. data/docker/entrypoint.sh +41 -0
  6. data/lib/bidi2pdf/bidi/auth_interceptor.rb +3 -0
  7. data/lib/bidi2pdf/bidi/browser_tab.rb +60 -19
  8. data/lib/bidi2pdf/bidi/client.rb +7 -5
  9. data/lib/bidi2pdf/bidi/commands/cdp_get_session.rb +21 -0
  10. data/lib/bidi2pdf/bidi/commands/page_print.rb +101 -0
  11. data/lib/bidi2pdf/bidi/commands/print_parameters_validator.rb +4 -1
  12. data/lib/bidi2pdf/bidi/commands.rb +2 -0
  13. data/lib/bidi2pdf/bidi/connection_manager.rb +3 -0
  14. data/lib/bidi2pdf/bidi/event_manager.rb +34 -4
  15. data/lib/bidi2pdf/bidi/interceptor.rb +12 -2
  16. data/lib/bidi2pdf/bidi/session.rb +35 -2
  17. data/lib/bidi2pdf/bidi/web_socket_dispatcher.rb +5 -5
  18. data/lib/bidi2pdf/chromedriver_manager.rb +25 -11
  19. data/lib/bidi2pdf/cli.rb +9 -2
  20. data/lib/bidi2pdf/test_helpers/configuration.rb +67 -0
  21. data/lib/bidi2pdf/test_helpers/images/extractor.rb +99 -0
  22. data/lib/bidi2pdf/test_helpers/images/image_similarity_checker.rb +50 -0
  23. data/lib/bidi2pdf/test_helpers/images/tiff_helper.rb +204 -0
  24. data/lib/bidi2pdf/test_helpers/images.rb +12 -0
  25. data/lib/bidi2pdf/test_helpers/matchers/contains_pdf_image.rb +29 -0
  26. data/lib/bidi2pdf/test_helpers/pdf_file_helper.rb +39 -0
  27. data/lib/bidi2pdf/test_helpers/spec_paths_helper.rb +60 -0
  28. data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_container.rb +0 -6
  29. data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_test_helper.rb +103 -0
  30. data/lib/bidi2pdf/test_helpers/testcontainers/shared_docker_network.rb +21 -0
  31. data/lib/bidi2pdf/test_helpers/testcontainers/testcontainers_refinement.rb +53 -0
  32. data/lib/bidi2pdf/test_helpers/testcontainers.rb +17 -0
  33. data/lib/bidi2pdf/test_helpers.rb +7 -0
  34. data/lib/bidi2pdf/version.rb +1 -1
  35. data/sig/bidi2pdf/bidi/event_manager.rbs +19 -13
  36. metadata +55 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d71c88a5941411b13770993de9b38f6321c263765ffce1a9bbd347fb960855ac
4
- data.tar.gz: aa64333d4dc4de54f6e1b627287a5d11661634f3244a8e3a213428c243f155f1
3
+ metadata.gz: 134f35256652da3e5e6c8e383e97c98779a1e2665798dbfa5d133bc85130b8cc
4
+ data.tar.gz: dd106af3e757d26ba3d935bbe54d0249402280a84f1b1a8e27855d19b4cd155c
5
5
  SHA512:
6
- metadata.gz: 1d598fe002552f46e53f803f46577adceeeb087b377a40b486d5d2ef7bf713463f429aa26b2687fb7d0b865d73aacf3262be71e17db154794ac82e1e4a245986
7
- data.tar.gz: 3b7cb02b0e857e551c720a665ac31d3669a9a27e8c9e3e5c1cdc497517b8fbcd3e917d6b0735113e3b956b23ded042b44c72bfff637cfdbf2431642bd98aaa2b
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/dieter-medium/bidi2pdf/compare/v0.1.8..HEAD
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.8..HEAD)
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 ruby:3.3
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 apt-get update && apt-get upgrade -y && \
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 = Bidi2pdf::Bidi::Commands::BrowsingContextPrint.new context: browsing_context_id, print_options: print_options
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["result"]["data"]
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
- &network_events.method(:handle_event))
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
- #{content ? "script.text = #{content.to_json};" : ""}
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
- #{id ? "script.id = '#{id}';" : ""}
522
+ #{"script.id = '#{id}';" if id}
493
523
  #{js_src_part}
494
524
 
495
525
  document.head.appendChild(script);
496
526
 
497
- #{url ? "" : "resolve(script);"}
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
- #{id ? "link.id = '#{id}';" : ""}
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
- #{id ? "style.id = '#{id}';" : ""}
577
+ #{"style.id = '#{id}';" if id}
548
578
  #{" "}
549
- #{content ? "style.textContent = #{content.to_json};" : ""}
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
- &network_events.method(:handle_event)
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
- &logger_events.method(:handle_event))
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|
@@ -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, &block)
129
- names.each { |name| dispatcher.on_event(name, &block) }
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(block) = dispatcher.remove_message_listener(block)
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, &block)
144
- names.each { |event_name| dispatcher.remove_event_listener(event_name, block) }
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.