draft_forge 0.3.0 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34f09b604f2763d94777f3cd8ae5ad4a1a486dac2663d3d4259c12bf107d7ed1
4
- data.tar.gz: 323c0bd1ea8e9dbf4111f8a56e48c6e8f2883fc3460e624c35f88a912c01a989
3
+ metadata.gz: 00ddff91bfa7f755bd68bf5f4a205892f5ec5bb270b98120b0dc0d11b0bc3e2b
4
+ data.tar.gz: 2a953c25d5b6cbdb75d48483670e7437849751856c031fd7a6abb08f1e3f71e7
5
5
  SHA512:
6
- metadata.gz: 75143db17f2b1da748254d123e8bf6a96e371acf8c990c9bf04fc67db53056e61dc7355b9b519fbe983603066276efcdfc174cf6b6824e3fefe821b20a45cde3
7
- data.tar.gz: 07eb51422510955246dea81c90757dc19235f402146a6cb76e8012de42dde0b079ff941c05a6b529068263a71362f520f13cb4ff484c32832916d2d371ce0359
6
+ metadata.gz: afcfcf35621e4e60f95d6432a04bdd22b255db91936875aacc9ae75cbee90e128138fe50a3398e4f11ee1e95f0624419dd44272fac8b2ea9340380ad8b667d0c
7
+ data.tar.gz: d6c0634575431f10a6fc2a1679208f1cf2c306ad9963e2ab643cf8d47b9c89a06ae8b75ba3542889a6b9c91dd1db026592eae2d3f5e70ddad5f73fbcd25e22da
data/README.md CHANGED
@@ -45,6 +45,12 @@ DraftForge::FetchExport.call(export.id)
45
45
  # => { id: 1, status: "queued" }
46
46
  ```
47
47
 
48
+ ```ruby
49
+ # Render HTML directly to a PDF without queuing
50
+ file = DraftForge::PdfRenderer.call("<p>Hello</p>")
51
+ file.close!
52
+ ```
53
+
48
54
  ## Configuration
49
55
 
50
56
  `DraftForge` exposes simple configuration hooks for PDF rendering and HTML
@@ -64,6 +70,10 @@ DraftForge.configure do |config|
64
70
  end
65
71
  ```
66
72
 
73
+ ## Performance
74
+
75
+ `ExportPdfJob` streams generated PDFs to a temporary file before attaching, keeping memory usage low even for very large, 100+ page exports.
76
+
67
77
  ## Testing
68
78
 
69
79
  Run the RSpec suite from this directory to verify changes:
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'grover'
3
2
 
4
3
  module DraftForge
5
4
  class ExportPdfJob < ActiveJob::Base
@@ -9,56 +8,15 @@ module DraftForge
9
8
  export = Export.find(export_id)
10
9
  export.update!(status: :processing)
11
10
 
12
- safe_html = HtmlSanitizer.call(raw_html)
13
- html = wrap_html(safe_html)
14
-
15
- pdf = Grover.new(html, DraftForge.pdf_options).to_pdf
11
+ tempfile = PdfRenderer.call(raw_html)
16
12
 
17
13
  filename = export.requested_filename.presence || "document.pdf"
18
- export.pdf.attach(io: StringIO.new(pdf), filename: filename, content_type: 'application/pdf')
14
+ export.pdf.attach(io: tempfile, filename: filename, content_type: 'application/pdf')
15
+ tempfile.close!
19
16
  export.update!(status: :complete)
20
17
  rescue => e
21
18
  Rails.logger.error("[DraftForge] Export failed: #{e.class}: #{e.message}")
22
19
  export.update!(status: :failed) rescue nil
23
20
  end
24
-
25
- private
26
-
27
- def wrap_html(body)
28
- options = DraftForge.pdf_options
29
- size = options[:format] || 'A4'
30
- margin = options[:margin] || {}
31
- top = margin[:top] || '20mm'
32
- right = margin[:right] || '15mm'
33
- bottom = margin[:bottom] || '20mm'
34
- left = margin[:left] || '15mm'
35
-
36
- <<~HTML
37
- <!doctype html>
38
- <html>
39
- <head>
40
- <meta charset="utf-8" />
41
- <meta name="viewport" content="width=device-width, initial-scale=1" />
42
- <style>
43
- @page { size: #{size}; margin: #{top} #{right} #{bottom} #{left}; }
44
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', Arial, 'Noto Sans', 'Liberation Sans', sans-serif; font-size: 12px; line-height: 1.45; color: #111; }
45
- h1, h2, h3 { margin: 0 0 8px; }
46
- h1 { font-size: 22px; }
47
- h2 { font-size: 18px; }
48
- h3 { font-size: 15px; }
49
- p { margin: 0 0 8px; }
50
- table { width: 100%; border-collapse: collapse; margin: 8px 0; }
51
- th, td { border: 1px solid #ccc; padding: 6px 8px; vertical-align: top; }
52
- img { max-width: 100%; height: auto; }
53
- .page-break { page-break-after: always; }
54
- .checklist li { list-style: none; }
55
- </style>
56
- </head>
57
- <body>
58
- #{body}
59
- </body>
60
- </html>
61
- HTML
62
- end
63
21
  end
64
22
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grover'
4
+
5
+ module DraftForge
6
+ class PdfRenderer
7
+ def self.call(raw_html)
8
+ safe_html = HtmlSanitizer.call(raw_html)
9
+ html = wrap_html(safe_html)
10
+
11
+ tempfile = Tempfile.new(["draft_forge", ".pdf"])
12
+ tempfile.binmode
13
+ begin
14
+ Grover.new(html, **DraftForge.pdf_options).to_pdf(path: tempfile.path)
15
+ tempfile.rewind
16
+ tempfile
17
+ rescue
18
+ tempfile.close!
19
+ raise
20
+ end
21
+ end
22
+
23
+ def self.wrap_html(body)
24
+ options = DraftForge.pdf_options
25
+ size = options[:format] || 'A4'
26
+ margin = options[:margin] || {}
27
+ top = margin[:top] || '20mm'
28
+ right = margin[:right] || '15mm'
29
+ bottom = margin[:bottom] || '20mm'
30
+ left = margin[:left] || '15mm'
31
+
32
+ <<~HTML
33
+ <!doctype html>
34
+ <html>
35
+ <head>
36
+ <meta charset="utf-8" />
37
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
38
+ <style>
39
+ @page { size: #{size}; margin: #{top} #{right} #{bottom} #{left}; }
40
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', Arial, 'Noto Sans', 'Liberation Sans', sans-serif; font-size: 12px; line-height: 1.45; color: #111; }
41
+ h1, h2, h3 { margin: 0 0 8px; }
42
+ h1 { font-size: 22px; }
43
+ h2 { font-size: 18px; }
44
+ h3 { font-size: 15px; }
45
+ p { margin: 0 0 8px; }
46
+ table { width: 100%; border-collapse: collapse; margin: 8px 0; }
47
+ th, td { border: 1px solid #ccc; padding: 6px 8px; vertical-align: top; }
48
+ img { max-width: 100%; height: auto; }
49
+ .page-break { page-break-after: always; }
50
+ .checklist li { list-style: none; }
51
+ </style>
52
+ </head>
53
+ <body>
54
+ #{body}
55
+ </body>
56
+ </html>
57
+ HTML
58
+ end
59
+ end
60
+ end
61
+
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module DraftForge
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draft_forge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Attara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-26 00:00:00.000000000 Z
11
+ date: 2025-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -77,6 +77,7 @@ files:
77
77
  - app/services/draft_forge/editor_js_renderer.rb
78
78
  - app/services/draft_forge/fetch_export.rb
79
79
  - app/services/draft_forge/html_sanitizer.rb
80
+ - app/services/draft_forge/pdf_renderer.rb
80
81
  - config/routes.rb
81
82
  - lib/draft_forge.rb
82
83
  - lib/draft_forge/engine.rb