Skip to content

Commit da8beda

Browse files
committed
- 0.3.5
- add `generate_binray` and `generate_binary!` that immediately return the PDF binary instead of an `{:ok, filename}` tuple. - add `generate!` to immediately return the filename - some more tests - minor change `delete_temporary` must be truthy. (the old supported value `:html` will stil work) and will delete both intermediate HTML And PDF files in ``generate_binary` and `generate_binary!`
1 parent 3d126ee commit da8beda

File tree

4 files changed

+105
-40
lines changed

4 files changed

+105
-40
lines changed

CHANGES.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changes
22

3+
- 0.3.5
4+
- add `generate_binray` and `generate_binary!` that immediately return the
5+
PDF binary instead of an `{:ok, filename}` tuple.
6+
- add `generate!` to immediately return the filename
7+
- some more tests
8+
- minor change `delete_temporary` must be truthy. (the old supported value
9+
`:html` will stil work) and will delete both intermediate HTML And PDF
10+
files in ``generate_binary` and `generate_binary!`
311
- 0.3.4
412
- BUGFIX: fix merge confusion to **realy** support `xvfb-run` or other
513
command prefixes to wkhtmltopdf

README.md

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
# elixir-pdf-generator
22

3-
A wrapper for wkhtmltopdf (HTML to PDF) and PDFTK (adds in encryption) for use in Elixir projects. If available, it will use xvfb-run (x virtual frame buffer) to use wkhtmltopdf on systems that have no X installed, e.g. a server.
4-
5-
# New in 0.3.4
6-
3+
A wrapper for wkhtmltopdf (HTML to PDF) and PDFTK (adds in encryption) for use
4+
in Elixir projects. If available, it will use xvfb-run (x virtual frame buffer)
5+
to use wkhtmltopdf on systems that have no X installed, e.g. a server.
6+
7+
# New in 0.3.4 and 0.3.5
8+
9+
- 0.3.5
10+
- add `generate_binray` and `generate_binary!` that immediately return the
11+
PDF binary instead of an `{:ok, filename}` tuple.
12+
- add `generate!` to immediately return the filename
13+
- some more tests
14+
- minor change `delete_temporary` must be truthy. (the old supported value
15+
`:html` will stil work) and will delete both intermediate HTML And PDF
16+
files in ``generate_binary` and `generate_binary!`
717
- 0.3.4
818
- BUGFIX: fix merge confusion to **realy** support `xvfb-run` or other
919
command prefixes to wkhtmltopdf
@@ -57,8 +67,18 @@ $ iex -S mix
5767
5868
html = "<html><body><p>Hi there!</p></body></html>"
5969
# be aware, this may take a while...
60-
{ :ok, file_name } = PdfGenerator.generate html, page_size: "A5", open_password: "s3cr3t"
70+
{ :ok, filename } = PdfGenerator.generate html, page_size: "A5", open_password: "s3cr3t"
6171
{ :ok, pdf_content } = File.read file_name
72+
73+
# or, if you prefail methods that rais on error:
74+
filename = PdfGenerator.generate! html
75+
```
76+
77+
Or use the bang-methods:
78+
79+
```
80+
filename = PdfGenerator.generate! "<html>..."
81+
pdf_binary = PdfGenerator.generate_binary! "<html>..."
6282
```
6383

6484
# Options and Configuration

lib/pdf_generator.ex

+39-31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
defmodule PdfGenerator do
22

3-
@vsn "0.3.0"
3+
@vsn "0.3.5"
44

55
@moduledoc """
66
# PdfGenerator
@@ -90,7 +90,7 @@ defmodule PdfGenerator do
9090
- edit_password: password required to edit PDF
9191
- shell_params: list of command-line arguments to wkhtmltopdf
9292
see http://wkhtmltopdf.org/usage/wkhtmltopdf.txt for all options
93-
- delete_temporary: :html to remove the temporary html generated in
93+
- delete_temporary: true to remove the temporary html generated in
9494
the system tmp dir
9595
9696
# Examples
@@ -101,18 +101,20 @@ defmodule PdfGenerator do
101101
page_size: "A5",
102102
open_password: "secret",
103103
edit_password: "g3h31m",
104-
shell_params: [ "--outline", "--outline-depth3", "3" ]
104+
shell_params: [ "--outline", "--outline-depth3", "3" ],
105+
delete_temporary: true
105106
)
106107
"""
107108
def generate( html ) do
108109
generate html, page_size: "A4"
109110
end
110111

111112
def generate( html, options ) do
112-
wkhtml_path = PdfGenerator.PathAgent.get.wkhtml_path
113-
html_file = Path.join System.tmp_dir, Misc.Random.string <> ".html"
113+
wkhtml_path = PdfGenerator.PathAgent.get.wkhtml_path
114+
random_filebase = Path.join System.tmp_dir, Misc.Random.string
115+
html_file = random_filebase <> ".html"
116+
pdf_file = random_filebase <> ".pdf"
114117
File.write html_file, html
115-
pdf_file = Path.join System.tmp_dir, Misc.Random.string <> ".pdf"
116118

117119
shell_params = [
118120
"--page-size", Keyword.get( options, :page_size ) || "A4",
@@ -135,9 +137,7 @@ defmodule PdfGenerator do
135137
executable, arguments, [in: "", out: :string, err: :string]
136138
)
137139

138-
if Keyword.get(options, :delete_temporary) == :html do
139-
File.rm html_file
140-
end
140+
if Keyword.get(options, :delete_temporary), do: html_file |> File.rm
141141

142142
case status do
143143
0 ->
@@ -155,27 +155,14 @@ defmodule PdfGenerator do
155155

156156
def encrypt_pdf( pdf_input_path, user_pw, owner_pw ) do
157157
pdftk_path = PdfGenerator.PathAgent.get.pdftk_path
158-
159-
owner_pw =
160-
case owner_pw do
161-
nil -> Misc.Random.string(16)
162-
_ -> owner_pw
163-
end
164-
165-
user_pw =
166-
case user_pw do
167-
nil -> Misc.Random.string(16)
168-
_ -> user_pw
169-
end
170-
171158
pdf_output_file = Path.join System.tmp_dir, Misc.Random.string <> ".pdf"
172159

173160
%Result{ out: _output, status: status } = Porcelain.exec(
174161
pdftk_path, [
175162
pdf_input_path,
176163
"output", pdf_output_file,
177-
"owner_pw", owner_pw,
178-
"user_pw", user_pw,
164+
"owner_pw", owner_pw |> random_if_undef,
165+
"user_pw", user_pw |> random_if_undef,
179166
"encrypt_128bit",
180167
"allow", "Printing", "CopyContents"
181168
]
@@ -185,31 +172,52 @@ defmodule PdfGenerator do
185172
0 -> { :ok, pdf_output_file }
186173
_ -> { :error, "Encrpying the PDF via pdftk failed" }
187174
end
188-
189175
end
190176

177+
defp random_if_undef(nil), do: Misc.Random.string(16)
178+
defp random_if_undef(any), do: any
179+
191180
@doc """
192181
Takes same options as `generate` but will return an
193182
`{:ok, binary_pdf_content}` tuple.
183+
184+
In case option _delete_temporary_ is true, will as well delete the temporary
185+
pdf file.
194186
"""
195-
def generate_binary( options \\ [] ) do
196-
result = generate options
187+
def generate_binary(html, options \\ []) do
188+
result = generate html, options
197189
case result do
198-
{:ok, filename} -> {:ok, filename |> File.read! }
190+
{:ok, filename} -> {:ok, filename |> read_and_maybe_delete(options) }
199191
{:error, reason} -> {:error, reason}
200192
end
201193
end
202194

195+
defp read_and_maybe_delete(filename, options) do
196+
content = filename |> File.read!
197+
if Keyword.get(options, :delete_temporary), do: filename |> File.rm
198+
content
199+
end
200+
203201
@doc """
204202
Same as generate_binary but returns PDF content directly or raises on
205203
error.
206204
"""
207-
def generate_binary!( options \\ [] ) do
208-
result = generate_binary options
205+
def generate_binary!(html, options \\ []) do
206+
result = generate_binary html, options
209207
case result do
210208
{:ok, content} -> content
211-
{:error, reason} -> raise reason
209+
{:error, reason} -> raise "in-place generation failed: " <> reason
212210
end
213211
end
214212

213+
@doc """
214+
Same as generate but returns PDF file name only (raises on error).
215+
"""
216+
def generate!(html, options \\ []) do
217+
result = generate html, options
218+
case result do
219+
{:ok, filename} -> filename
220+
{:error, reason} -> raise "HTML generation failed: " <> reason
221+
end
222+
end
215223
end

test/pdf_generator_test.exs

+33-4
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,44 @@ defmodule PdfGeneratorTest do
2020
end
2121

2222
test "command prefix with noop env" do
23-
{:ok, _temp_filename } = PdfGenerator.generate @html, [ command_prefix: "env" ]
23+
{:ok, _temp_filename } = PdfGenerator.generate @html, command_prefix: "env"
24+
end
25+
26+
test "generate_binary reads file" do
27+
assert {:ok, "%PDF-1" <> _pdf} = @html |> PdfGenerator.generate_binary
28+
end
29+
30+
test "generate! returns a filename" do
31+
@html
32+
|> PdfGenerator.generate!
33+
|> File.exists?
34+
|> assert
2435
end
2536

2637
test "generate_binary! reads file" do
27-
assert "%PDF-1" <> _pdf = PdfGenerator.generate_binary!( @html )
38+
assert "%PDF-1" <> _pdf = @html |> PdfGenerator.generate_binary!
2839
end
2940

30-
test "generate_binary reads file" do
31-
assert {:ok, "%PDF-1" <> _pdf} = PdfGenerator.generate_binary( @html )
41+
test "delete_temporary works" do
42+
# w/o delete_temporary, html should be there
43+
@html
44+
|> PdfGenerator.generate!
45+
|> String.replace( ~r(\.pdf$), ".html")
46+
|> File.exists?
47+
|> assert
48+
49+
# with delete_temporary, html file should be gone
50+
@html
51+
|> PdfGenerator.generate!(delete_temporary: true)
52+
|> String.replace( ~r(\.pdf$), ".html")
53+
|> File.exists?
54+
|> refute
55+
56+
# cannot really be sure if temporyr file was deleted but this shouldn't
57+
# crash at least. We could scan the temp dir before and after but had to
58+
# make sure no other process wrote something in there which isn't exactly
59+
# robust.
60+
assert {:ok, "%PDF-1" <> _pdf} = @html |> PdfGenerator.generate_binary(delete_temporary: true)
3261
end
3362

3463
end

0 commit comments

Comments
 (0)