Skip to content

Commit

Permalink
- 0.3.5
Browse files Browse the repository at this point in the history
    - 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!`
  • Loading branch information
gutschilla committed Aug 25, 2016
1 parent 3d126ee commit da8beda
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 40 deletions.
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changes

- 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!`
- 0.3.4
- BUGFIX: fix merge confusion to **realy** support `xvfb-run` or other
command prefixes to wkhtmltopdf
Expand Down
30 changes: 25 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
# elixir-pdf-generator

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.

# New in 0.3.4

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.

# New in 0.3.4 and 0.3.5

- 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!`
- 0.3.4
- BUGFIX: fix merge confusion to **realy** support `xvfb-run` or other
command prefixes to wkhtmltopdf
Expand Down Expand Up @@ -57,8 +67,18 @@ $ iex -S mix
html = "<html><body><p>Hi there!</p></body></html>"
# be aware, this may take a while...
{ :ok, file_name } = PdfGenerator.generate html, page_size: "A5", open_password: "s3cr3t"
{ :ok, filename } = PdfGenerator.generate html, page_size: "A5", open_password: "s3cr3t"
{ :ok, pdf_content } = File.read file_name
# or, if you prefail methods that rais on error:
filename = PdfGenerator.generate! html
```

Or use the bang-methods:

```
filename = PdfGenerator.generate! "<html>..."
pdf_binary = PdfGenerator.generate_binary! "<html>..."
```

# Options and Configuration
Expand Down
70 changes: 39 additions & 31 deletions lib/pdf_generator.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule PdfGenerator do

@vsn "0.3.0"
@vsn "0.3.5"

@moduledoc """
# PdfGenerator
Expand Down Expand Up @@ -90,7 +90,7 @@ defmodule PdfGenerator do
- edit_password: password required to edit PDF
- shell_params: list of command-line arguments to wkhtmltopdf
see http://wkhtmltopdf.org/usage/wkhtmltopdf.txt for all options
- delete_temporary: :html to remove the temporary html generated in
- delete_temporary: true to remove the temporary html generated in
the system tmp dir
# Examples
Expand All @@ -101,18 +101,20 @@ defmodule PdfGenerator do
page_size: "A5",
open_password: "secret",
edit_password: "g3h31m",
shell_params: [ "--outline", "--outline-depth3", "3" ]
shell_params: [ "--outline", "--outline-depth3", "3" ],
delete_temporary: true
)
"""
def generate( html ) do
generate html, page_size: "A4"
end

def generate( html, options ) do
wkhtml_path = PdfGenerator.PathAgent.get.wkhtml_path
html_file = Path.join System.tmp_dir, Misc.Random.string <> ".html"
wkhtml_path = PdfGenerator.PathAgent.get.wkhtml_path
random_filebase = Path.join System.tmp_dir, Misc.Random.string
html_file = random_filebase <> ".html"
pdf_file = random_filebase <> ".pdf"
File.write html_file, html
pdf_file = Path.join System.tmp_dir, Misc.Random.string <> ".pdf"

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

if Keyword.get(options, :delete_temporary) == :html do
File.rm html_file
end
if Keyword.get(options, :delete_temporary), do: html_file |> File.rm

case status do
0 ->
Expand All @@ -155,27 +155,14 @@ defmodule PdfGenerator do

def encrypt_pdf( pdf_input_path, user_pw, owner_pw ) do
pdftk_path = PdfGenerator.PathAgent.get.pdftk_path

owner_pw =
case owner_pw do
nil -> Misc.Random.string(16)
_ -> owner_pw
end

user_pw =
case user_pw do
nil -> Misc.Random.string(16)
_ -> user_pw
end

pdf_output_file = Path.join System.tmp_dir, Misc.Random.string <> ".pdf"

%Result{ out: _output, status: status } = Porcelain.exec(
pdftk_path, [
pdf_input_path,
"output", pdf_output_file,
"owner_pw", owner_pw,
"user_pw", user_pw,
"owner_pw", owner_pw |> random_if_undef,
"user_pw", user_pw |> random_if_undef,
"encrypt_128bit",
"allow", "Printing", "CopyContents"
]
Expand All @@ -185,31 +172,52 @@ defmodule PdfGenerator do
0 -> { :ok, pdf_output_file }
_ -> { :error, "Encrpying the PDF via pdftk failed" }
end

end

defp random_if_undef(nil), do: Misc.Random.string(16)
defp random_if_undef(any), do: any

@doc """
Takes same options as `generate` but will return an
`{:ok, binary_pdf_content}` tuple.
In case option _delete_temporary_ is true, will as well delete the temporary
pdf file.
"""
def generate_binary( options \\ [] ) do
result = generate options
def generate_binary(html, options \\ []) do
result = generate html, options
case result do
{:ok, filename} -> {:ok, filename |> File.read! }
{:ok, filename} -> {:ok, filename |> read_and_maybe_delete(options) }
{:error, reason} -> {:error, reason}
end
end

defp read_and_maybe_delete(filename, options) do
content = filename |> File.read!
if Keyword.get(options, :delete_temporary), do: filename |> File.rm
content
end

@doc """
Same as generate_binary but returns PDF content directly or raises on
error.
"""
def generate_binary!( options \\ [] ) do
result = generate_binary options
def generate_binary!(html, options \\ []) do
result = generate_binary html, options
case result do
{:ok, content} -> content
{:error, reason} -> raise reason
{:error, reason} -> raise "in-place generation failed: " <> reason
end
end

@doc """
Same as generate but returns PDF file name only (raises on error).
"""
def generate!(html, options \\ []) do
result = generate html, options
case result do
{:ok, filename} -> filename
{:error, reason} -> raise "HTML generation failed: " <> reason
end
end
end
37 changes: 33 additions & 4 deletions test/pdf_generator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,44 @@ defmodule PdfGeneratorTest do
end

test "command prefix with noop env" do
{:ok, _temp_filename } = PdfGenerator.generate @html, [ command_prefix: "env" ]
{:ok, _temp_filename } = PdfGenerator.generate @html, command_prefix: "env"
end

test "generate_binary reads file" do
assert {:ok, "%PDF-1" <> _pdf} = @html |> PdfGenerator.generate_binary
end

test "generate! returns a filename" do
@html
|> PdfGenerator.generate!
|> File.exists?
|> assert
end

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

test "generate_binary reads file" do
assert {:ok, "%PDF-1" <> _pdf} = PdfGenerator.generate_binary( @html )
test "delete_temporary works" do
# w/o delete_temporary, html should be there
@html
|> PdfGenerator.generate!
|> String.replace( ~r(\.pdf$), ".html")
|> File.exists?
|> assert

# with delete_temporary, html file should be gone
@html
|> PdfGenerator.generate!(delete_temporary: true)
|> String.replace( ~r(\.pdf$), ".html")
|> File.exists?
|> refute

# cannot really be sure if temporyr file was deleted but this shouldn't
# crash at least. We could scan the temp dir before and after but had to
# make sure no other process wrote something in there which isn't exactly
# robust.
assert {:ok, "%PDF-1" <> _pdf} = @html |> PdfGenerator.generate_binary(delete_temporary: true)
end

end

0 comments on commit da8beda

Please sign in to comment.