phantompdf 1.0.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 +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +86 -0
- data/LICENSE +20 -0
- data/NEWS.md +0 -0
- data/README.md +146 -0
- data/Rakefile +6 -0
- data/lib/phantompdf/assets.rb +20 -0
- data/lib/phantompdf/config.rb +32 -0
- data/lib/phantompdf/error.rb +7 -0
- data/lib/phantompdf/generator.rb +104 -0
- data/lib/phantompdf/middleware.rb +46 -0
- data/lib/phantompdf/source.rb +35 -0
- data/lib/phantompdf/version.rb +3 -0
- data/lib/phantompdf.rb +36 -0
- data/phantompdf.gemspec +26 -0
- data/spec/fixtures/phantompdf.html +23 -0
- data/spec/phantompdf/assets_spec.rb +21 -0
- data/spec/phantompdf/config_spec.rb +27 -0
- data/spec/phantompdf/generator_spec.rb +128 -0
- data/spec/phantompdf/middleware_spec.rb +27 -0
- data/spec/phantompdf/source_spec.rb +89 -0
- data/spec/spec_helper.rb +27 -0
- data/vendor/assets/javascripts/rasterize.js +148 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 17ce8d8da194279be8c3578a6229421e12ac9312
|
4
|
+
data.tar.gz: 1ced8f6dac53b42f7362c6f407ebaf4d00a67e65
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6eefd81192554a6ad80b75ee9324f0ad48007637bcf1089eb6c6f4862dabec47bcd9257a99da223dc554b56f6558af20b8e3624124859ef45f3adf4f9d805089
|
7
|
+
data.tar.gz: f36cfcce568fcd1e6ff266f6036d7820153504442be8be4d33ae8b9de16d9993644a51c5778215f21f3112dbdcb5c4c98324868181f3261c2a1012976d0261da
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
phantompdf (1.0.0)
|
5
|
+
json
|
6
|
+
phantomjs
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
Ascii85 (1.0.2)
|
12
|
+
afm (0.2.0)
|
13
|
+
byebug (2.0.0)
|
14
|
+
columnize (~> 0.3.6)
|
15
|
+
debugger-linecache (~> 1.2.0)
|
16
|
+
capybara (1.1.4)
|
17
|
+
mime-types (>= 1.16)
|
18
|
+
nokogiri (>= 1.3.3)
|
19
|
+
rack (>= 1.0.0)
|
20
|
+
rack-test (>= 0.5.4)
|
21
|
+
selenium-webdriver (~> 2.0)
|
22
|
+
xpath (~> 0.1.4)
|
23
|
+
childprocess (0.3.9)
|
24
|
+
ffi (~> 1.0, >= 1.0.11)
|
25
|
+
columnize (0.3.6)
|
26
|
+
debugger-linecache (1.2.0)
|
27
|
+
diff-lcs (1.2.4)
|
28
|
+
eventmachine (1.0.3)
|
29
|
+
faye-websocket (0.4.7)
|
30
|
+
eventmachine (>= 0.12.0)
|
31
|
+
ffi (1.9.0)
|
32
|
+
hashery (2.1.1)
|
33
|
+
http_parser.rb (0.5.3)
|
34
|
+
json (1.8.0)
|
35
|
+
mime-types (1.24)
|
36
|
+
mini_portile (0.5.1)
|
37
|
+
multi_json (1.7.9)
|
38
|
+
nokogiri (1.6.0)
|
39
|
+
mini_portile (~> 0.5.0)
|
40
|
+
pdf-reader (1.3.3)
|
41
|
+
Ascii85 (~> 1.0.0)
|
42
|
+
afm (~> 0.2.0)
|
43
|
+
hashery (~> 2.0)
|
44
|
+
ruby-rc4
|
45
|
+
ttfunk
|
46
|
+
phantomjs (1.8.1.1)
|
47
|
+
poltergeist
|
48
|
+
poltergeist (1.0.3)
|
49
|
+
capybara (~> 1.1)
|
50
|
+
childprocess (~> 0.3)
|
51
|
+
faye-websocket (~> 0.4.4)
|
52
|
+
http_parser.rb (~> 0.5.3)
|
53
|
+
multi_json (~> 1.0)
|
54
|
+
rack (1.4.5)
|
55
|
+
rack-test (0.6.2)
|
56
|
+
rack (>= 1.0)
|
57
|
+
rake (10.1.0)
|
58
|
+
rspec (2.13.0)
|
59
|
+
rspec-core (~> 2.13.0)
|
60
|
+
rspec-expectations (~> 2.13.0)
|
61
|
+
rspec-mocks (~> 2.13.0)
|
62
|
+
rspec-core (2.13.1)
|
63
|
+
rspec-expectations (2.13.0)
|
64
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
65
|
+
rspec-mocks (2.13.1)
|
66
|
+
ruby-rc4 (0.1.5)
|
67
|
+
rubyzip (0.9.9)
|
68
|
+
selenium-webdriver (2.35.0)
|
69
|
+
childprocess (>= 0.2.5)
|
70
|
+
multi_json (~> 1.0)
|
71
|
+
rubyzip
|
72
|
+
websocket (~> 1.0.4)
|
73
|
+
ttfunk (1.0.3)
|
74
|
+
websocket (1.0.7)
|
75
|
+
xpath (0.1.4)
|
76
|
+
nokogiri (~> 1.3)
|
77
|
+
|
78
|
+
PLATFORMS
|
79
|
+
ruby
|
80
|
+
|
81
|
+
DEPENDENCIES
|
82
|
+
byebug
|
83
|
+
pdf-reader
|
84
|
+
phantompdf!
|
85
|
+
rake
|
86
|
+
rspec
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Mc.Spring
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/NEWS.md
ADDED
File without changes
|
data/README.md
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
## PhantomPDF
|
2
|
+
|
3
|
+
Generate PDF from HTML using PhantomJS!
|
4
|
+
|
5
|
+
Supporting *URL*, *FILE* and *STRING* formats for HTML resource.
|
6
|
+
|
7
|
+
## Why PhantomPDF?
|
8
|
+
|
9
|
+
Within the Ruby community, there is no simply way to generate *PDF* from *HTML*. You must setup dependences separate and then call some wrapped methods around. When you manage several servers or need to do migrations around, things become much more terrible!
|
10
|
+
|
11
|
+
PhantomPDF was born to simplify those. It brings simple API and easy maintenance.
|
12
|
+
|
13
|
+
**Yes, PhantomPDF is simply!** You only need to take care of your HTML layout. And then put remaining work to PhantomPDF.
|
14
|
+
|
15
|
+
## How to start?
|
16
|
+
|
17
|
+
### Installation
|
18
|
+
|
19
|
+
##### For Gemfile project
|
20
|
+
Adding following to your `Gemfile`
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'phantompdf'
|
24
|
+
```
|
25
|
+
|
26
|
+
and then execute
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
$ bundle
|
30
|
+
```
|
31
|
+
|
32
|
+
##### For normal project
|
33
|
+
```ruby
|
34
|
+
$ gem install phantompdf
|
35
|
+
```
|
36
|
+
|
37
|
+
That's all! Pretty simple?!
|
38
|
+
|
39
|
+
### Usage
|
40
|
+
|
41
|
+
##### Quick starts
|
42
|
+
```ruby
|
43
|
+
require 'phantompdf'
|
44
|
+
|
45
|
+
options = {
|
46
|
+
:format => 'A4',
|
47
|
+
:margin => '1cm'
|
48
|
+
}
|
49
|
+
output_file = '/tmp/phantompdf_google.pdf'
|
50
|
+
|
51
|
+
# for *URL* resource
|
52
|
+
url = 'http://www.google.com'
|
53
|
+
url_generator = PhantomPDF::Generator.new(url, output_file, options)
|
54
|
+
returned_file = url_generator.generate # If output_file is valid(writable) returned_file == output_file, otherwise returned_file should be temp file of your OS.
|
55
|
+
|
56
|
+
# for *FILE* resource
|
57
|
+
file = 'file://path/to/file.html'
|
58
|
+
file_generator = PhantomPDF::Generator.new(file, output_file, options)
|
59
|
+
returned_file = file_generator.generate
|
60
|
+
|
61
|
+
# for *STRING* resource
|
62
|
+
html = '<h1>Hello, PhantomPDF!</h1>'
|
63
|
+
html_generator = PhantomPDF::Generator.new(html, output_file, options)
|
64
|
+
returned_file = html_generator.generate
|
65
|
+
|
66
|
+
# want to dynamic output?
|
67
|
+
url_generator.generate('/tmp/dynamic_phantompdf_google.pdf') # This will output pdf file to /tmp/dynamic_phantompdf_google.pdf though you pass *output* arg when creating generator instance.
|
68
|
+
```
|
69
|
+
|
70
|
+
##### Configuration
|
71
|
+
You can configure PhantomPDF globally in your project, such as Rails' initializers principle.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
PhantomPDF.configure do |config|
|
75
|
+
# default pdf output format, e.g. "5in*7.5in", "10cm*20cm", "A4", "Letter"
|
76
|
+
:format => 'A4',
|
77
|
+
|
78
|
+
# default pdf header, formatted in [headerHeight*]headerString(HTML is supported)
|
79
|
+
# default to 1.2cm when you omit headerHeight
|
80
|
+
# you can use *%{pageNum}* and *%{pageTotal}* to refer current values of page
|
81
|
+
# example: 1.2cm*<h5>PhantomPDF header %{pageNum}/%{pageTotal}
|
82
|
+
:header => nil,
|
83
|
+
|
84
|
+
# default pdf footer, formatted in footerHeight*footerString(HTML is supported)
|
85
|
+
# you can use *%{pageNum}* and *%{pageTotal}* to refer current values of page
|
86
|
+
# example: 0.6cm*<h5>PhantomPDF header %{pageNum}/%{pageTotal}
|
87
|
+
:footer => nil,
|
88
|
+
|
89
|
+
# default page margin
|
90
|
+
:margin => '1cm',
|
91
|
+
|
92
|
+
# default page orientation, 'portrait' or 'landscape'
|
93
|
+
:orientation => 'portrait',
|
94
|
+
|
95
|
+
# default page zoom factor
|
96
|
+
:zoom => 1,
|
97
|
+
|
98
|
+
# default cookies, only used for *URL* resource
|
99
|
+
:cookies => {},
|
100
|
+
|
101
|
+
# PhantomJS running timeout
|
102
|
+
:timeout => 90000,
|
103
|
+
|
104
|
+
# pdf rendering timeout, increase if your HTML page is big
|
105
|
+
:rendering_timeout => 1000
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
You can also configure PhantomPDF in place when needs.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
# for *URL* resource
|
113
|
+
url = 'http://www.google.com'
|
114
|
+
url_generator = PhantomPDF::Generator.new(url, output_file, options)
|
115
|
+
url_generator.options.header = '0.8cm*<h3>Configure PhantomPDF dynamic</h3>'
|
116
|
+
returned_file = url_generator.generate
|
117
|
+
```
|
118
|
+
|
119
|
+
##### APIs
|
120
|
+
```ruby
|
121
|
+
# create PhantomPDF
|
122
|
+
PhantomPDF::Generator.new(input, output=nil, options={})
|
123
|
+
|
124
|
+
# generate pdf
|
125
|
+
PhantomPDF::Generator#generate(output=>nil)
|
126
|
+
|
127
|
+
# raise PhantomPDF::RenderingError when failed to generate pdf
|
128
|
+
PhantomPDF::Generator#generate!(output=>nil)
|
129
|
+
|
130
|
+
# get generated pdf as string
|
131
|
+
PhantomPDF::Generator#to_string
|
132
|
+
```
|
133
|
+
|
134
|
+
### Contributing
|
135
|
+
If you'd like to help improve PhantomPDF or you find some bugs. You can contribute to it by following:
|
136
|
+
|
137
|
+
- Submit an issue describing your problem
|
138
|
+
- Fork your repo and work your magic
|
139
|
+
- Test your code as far as possible
|
140
|
+
- Submit a pull request when you finished hack
|
141
|
+
|
142
|
+
### License
|
143
|
+
PhantomPDF is released under [MIT license](http://www.opensource.org/licenses/MIT).
|
144
|
+
|
145
|
+
### Authors
|
146
|
+
[Spring MC](https://twitter.com/mcspring)
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module PhantomPDF
|
2
|
+
class Assets
|
3
|
+
attr_accessor :root, :javascripts
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def root
|
7
|
+
@root ||= File.expand_path('../../../', __FILE__)
|
8
|
+
end
|
9
|
+
|
10
|
+
def javascripts(name)
|
11
|
+
@javascripts ||= {}
|
12
|
+
|
13
|
+
@javascripts[name] ||= "#{root}/vendor/assets/javascripts/#{name}.js"
|
14
|
+
@javascripts[name] = nil unless File.exist?(@javascripts[name])
|
15
|
+
|
16
|
+
@javascripts[name]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PhantomPDF
|
2
|
+
class Config
|
3
|
+
attr_accessor :default_options
|
4
|
+
attr_reader :phantomjs
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
@default_options = {
|
8
|
+
:format => 'A4',
|
9
|
+
:header => nil,
|
10
|
+
:footer => nil,
|
11
|
+
:margin => '1cm',
|
12
|
+
:orientation => 'portrait',
|
13
|
+
:zoom => 1,
|
14
|
+
:cookies => {},
|
15
|
+
:timeout => 90000,
|
16
|
+
:rendering_timeout => 1000
|
17
|
+
}
|
18
|
+
|
19
|
+
@default_options.merge! options || {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def phantomjs
|
23
|
+
@phantomjs ||= ::Phantomjs.path
|
24
|
+
end
|
25
|
+
|
26
|
+
[:format, :header, :footer, :margin, :orientation, :zoom, :cookies, :timeout, :rendering_timeout].each do |key|
|
27
|
+
define_method("#{key}=") do |val|
|
28
|
+
@default_options[key] = val
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'json'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
module PhantomPDF
|
6
|
+
class Generator
|
7
|
+
attr_accessor :input, :output, :config
|
8
|
+
attr_reader :options, :cookies, :exception
|
9
|
+
|
10
|
+
def initialize(input, output=nil, options={})
|
11
|
+
@input = Source.new(input)
|
12
|
+
@output = dump_output(output, !output.nil?)
|
13
|
+
@options = Config.new(options).default_options
|
14
|
+
@exception = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate(path=nil)
|
18
|
+
@output = dump_output(path, true) unless path.nil?
|
19
|
+
|
20
|
+
result = run
|
21
|
+
unless $?.exitstatus == 0
|
22
|
+
@exception = result.split(/\n/)
|
23
|
+
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
result.strip
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate!(path=nil)
|
31
|
+
result = generate(path)
|
32
|
+
raise RenderingError.new(@exception.join("\n")) if result.nil?
|
33
|
+
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_string
|
38
|
+
result = generate(nil)
|
39
|
+
return '' if result.nil?
|
40
|
+
|
41
|
+
File.open(result, 'rb').read
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def run
|
46
|
+
::Phantomjs.run(*dump_args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def dump_output(path, strict)
|
50
|
+
if strict
|
51
|
+
raise DestinationTypeError.new('Destination must be a valid file path!') unless path.is_a?(String)
|
52
|
+
path = File.expand_path(path)
|
53
|
+
raise DestinationPermitError.new('Destination does not writable!') unless File.writable?(File.dirname(path))
|
54
|
+
else
|
55
|
+
path = path.is_a?(String) ? File.expand_path(path) : nil
|
56
|
+
path = Tempfile.new('temp_pdf_file').path if path.nil? || !File.writable?(File.dirname(path))
|
57
|
+
end
|
58
|
+
|
59
|
+
path
|
60
|
+
end
|
61
|
+
|
62
|
+
def dump_args
|
63
|
+
format, header, footer = options[:format], options[:header], options[:footer]
|
64
|
+
zoom, margin, orientation = options[:zoom], options[:margin], options[:orientation]
|
65
|
+
rendering_timeout, timeout = options[:rendering_timeout], options[:timeout]
|
66
|
+
cookie_file = dump_cookies(options[:cookies])
|
67
|
+
|
68
|
+
[Assets.javascripts('rasterize'),
|
69
|
+
@input.to_s,
|
70
|
+
@output,
|
71
|
+
format, dump_header(header), dump_footer(footer),
|
72
|
+
margin, orientation, zoom,
|
73
|
+
cookie_file,
|
74
|
+
rendering_timeout, timeout].map(&:to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
def dump_header(header)
|
78
|
+
return nil if header.nil? || header.empty?
|
79
|
+
|
80
|
+
return "1.2cm*#{header}" unless header.split('*')[0].to_f > 0
|
81
|
+
|
82
|
+
header
|
83
|
+
end
|
84
|
+
|
85
|
+
def dump_footer(footer)
|
86
|
+
return nil if footer.nil? || footer.empty?
|
87
|
+
|
88
|
+
return "0.7cm*#{header}" unless footer.split('*')[0].to_f > 0
|
89
|
+
|
90
|
+
footer
|
91
|
+
end
|
92
|
+
|
93
|
+
def dump_cookies(cookies)
|
94
|
+
cookie_host = @input.url? ? URI::parse(@input.to_s).host : '/'
|
95
|
+
|
96
|
+
cookie_json = cookies.inject([]) {|ck, (k, v)| ck.push({:name => k, :value => v, :domain => cookie_host}); ck}.to_json
|
97
|
+
return nil if cookie_json.empty?
|
98
|
+
|
99
|
+
cookie_file = Tempfile.new('temp_cookie_file')
|
100
|
+
cookie_file.write(cookie_json)
|
101
|
+
cookie_file.path
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module PhantomPDF
|
2
|
+
class Middleware
|
3
|
+
REGEXP_PDF = /\.pdf\z/i
|
4
|
+
|
5
|
+
def initialize(app, output=nil)
|
6
|
+
@app = app
|
7
|
+
@output = output
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@request = Rack::Request.new(env)
|
12
|
+
|
13
|
+
return @app.call(env) unless request_pdf?
|
14
|
+
|
15
|
+
status, headers, response = @app.call(env)
|
16
|
+
return [status, headers, response] if status != 200 || headers['Content-Type'] != 'text/html'
|
17
|
+
|
18
|
+
response_body = render_pdf(response.first)
|
19
|
+
|
20
|
+
headers.merge!({
|
21
|
+
'Content-Type' => 'application/pdf',
|
22
|
+
'Content-Length' => response_body.size.to_s
|
23
|
+
})
|
24
|
+
|
25
|
+
[200, headers, [response_body]]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def render_pdf(html)
|
30
|
+
Generator.new(html, render_path).to_string
|
31
|
+
end
|
32
|
+
|
33
|
+
def render_path
|
34
|
+
file_name = "#{Digest::MD5.hexdigest(@request.path)}.pdf"
|
35
|
+
|
36
|
+
return Tempfile.new(file_name).path if @output.nil? || !File.directory?(@output) || !File.writable?(@output)
|
37
|
+
|
38
|
+
"#{@output}/#{file_name}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def request_pdf?
|
42
|
+
return true if @request.path.match(Middleware::REGEXP_PDF)
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module PhantomPDF
|
4
|
+
class Source
|
5
|
+
def initialize(src)
|
6
|
+
@source = src
|
7
|
+
|
8
|
+
raise SourceTypeError.new('Unsupported source type.') unless valid?
|
9
|
+
end
|
10
|
+
|
11
|
+
def url?
|
12
|
+
!URI.parse(@source).scheme.nil?
|
13
|
+
rescue
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def file?
|
18
|
+
!url? && (@source.kind_of?(File) || File.exists?(@source))
|
19
|
+
end
|
20
|
+
|
21
|
+
def html?
|
22
|
+
!(url? || file?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
url? || file? || html?
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
return @source if url? || html?
|
31
|
+
|
32
|
+
@source.kind_of?(File) ? @source.path : @source
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/phantompdf.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'phantompdf/version'
|
2
|
+
require 'phantompdf/error'
|
3
|
+
require 'phantompdf/assets'
|
4
|
+
require 'phantompdf/source'
|
5
|
+
require 'phantompdf/config'
|
6
|
+
require 'phantompdf/generator'
|
7
|
+
require 'phantompdf/middleware'
|
8
|
+
|
9
|
+
module PhantomPDF
|
10
|
+
# Configure PhantomPDF someplace sensible,
|
11
|
+
# like config/initializers/phantompdf.rb
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# PhantomPDF.configure do |config|
|
15
|
+
# config.format = 'A4',
|
16
|
+
# config.header = nil,
|
17
|
+
# config.footer = nil,
|
18
|
+
# config.margin = '1cm',
|
19
|
+
# config.orientation = 'portrait',
|
20
|
+
# config.zoom = 1,
|
21
|
+
# config.cookies = {},
|
22
|
+
# config.timeout = 90000,
|
23
|
+
# config.rendering_timeout = 1000
|
24
|
+
# end
|
25
|
+
class << self
|
26
|
+
attr_accessor :configuration
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configuration
|
30
|
+
@configuration ||= Config.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.configure
|
34
|
+
yield(configuration)
|
35
|
+
end
|
36
|
+
end
|
data/phantompdf.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
phantompdf_lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(phantompdf_lib) unless $LOAD_PATH.include?(phantompdf_lib)
|
4
|
+
|
5
|
+
require 'phantompdf/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'phantompdf'
|
9
|
+
spec.version = PhantomPDF::VERSION
|
10
|
+
spec.authors = ['Spring MC']
|
11
|
+
spec.email = %w(Heresy.Mc@gmail.com)
|
12
|
+
spec.description = %q{Generate PDF from HTML using PhantomJS}
|
13
|
+
spec.summary = %q{A PhantomJS based PDF generator}
|
14
|
+
spec.homepage = 'https://github.com/mcspring/phantompdf'
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = %w(lib)
|
19
|
+
spec.add_runtime_dependency 'phantomjs'
|
20
|
+
spec.add_runtime_dependency 'json'
|
21
|
+
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_development_dependency 'pdf-reader'
|
25
|
+
spec.add_development_dependency 'byebug'
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<html>
|
2
|
+
<head></head>
|
3
|
+
<body>
|
4
|
+
<header>
|
5
|
+
<h1>Hello PhantomPDF!</h1>
|
6
|
+
</header>
|
7
|
+
<section>
|
8
|
+
<div>
|
9
|
+
<p>
|
10
|
+
This is a HTML file used for PhantomPDF testing.
|
11
|
+
</p>
|
12
|
+
</div>
|
13
|
+
</section>
|
14
|
+
<footer>
|
15
|
+
<div>
|
16
|
+
<p>
|
17
|
+
For more, please visit <a href="https://github.com/mcspring/phantompdf" title="Generate PDF from HTML using Phantomjs" target="_blank">https://github.com/mcspring/phantompdf</a>
|
18
|
+
</p>
|
19
|
+
</div>
|
20
|
+
</footer>
|
21
|
+
</body>
|
22
|
+
</html>
|
23
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module PhantomPDF
|
4
|
+
describe Assets do
|
5
|
+
context ".root" do
|
6
|
+
it "should return vendor folder" do
|
7
|
+
File.directory?(Assets.root).should be_true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context ".javascripts" do
|
12
|
+
it "should return rasterize.js abs path" do
|
13
|
+
File.exist?(Assets.javascripts('rasterize')).should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should return nil for un-existed file" do
|
17
|
+
Assets.javascripts('un-exist-javascript').should be_nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module PhantomPDF
|
4
|
+
describe Config do
|
5
|
+
before do
|
6
|
+
@config = Config.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "#phantomjs" do
|
10
|
+
it "should respond to :phantomjs" do
|
11
|
+
@config.should respond_to(:phantomjs)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return phantomjs bin path" do
|
15
|
+
@config.phantomjs.should == Phantomjs.path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
[:format, :header, :footer, :margin, :zoom, :orientation, :cookies, :timeout, :rendering_timeout].each do |method|
|
20
|
+
method = :"#{method}="
|
21
|
+
|
22
|
+
it "should respond to #{method}" do
|
23
|
+
@config.should respond_to(method)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module PhantomPDF
|
4
|
+
describe Generator do
|
5
|
+
let(:fixtures_root) {
|
6
|
+
File.expand_path('../../fixtures', __FILE__)
|
7
|
+
}
|
8
|
+
|
9
|
+
subject { Generator.new("#{fixtures_root}/phantompdf.html") }
|
10
|
+
|
11
|
+
context "attributes" do
|
12
|
+
[:input, :output, :config].each do |rwattr|
|
13
|
+
it { should respond_to(rwattr) }
|
14
|
+
it { should respond_to("#{rwattr}=".to_sym) }
|
15
|
+
end
|
16
|
+
|
17
|
+
[:options, :cookies, :exception].each do |rattr|
|
18
|
+
it { should respond_to(rattr) }
|
19
|
+
it { should_not respond_to("#{rattr}=") }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "#generate" do
|
24
|
+
fixtures_root = File.expand_path('../../fixtures', __FILE__)
|
25
|
+
|
26
|
+
{
|
27
|
+
url: 'http://www.google.com',
|
28
|
+
file: "#{fixtures_root}/phantompdf.html",
|
29
|
+
html: File.read("#{fixtures_root}/phantompdf.html")
|
30
|
+
}.each do |key, value|
|
31
|
+
context "with #{key}" do
|
32
|
+
it "should works" do
|
33
|
+
Generator.new(value).generate.should be_pdf_file
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with output" do
|
39
|
+
before :all do
|
40
|
+
@url = 'http://www.google.com'
|
41
|
+
@file = '/tmp/google.pdf'
|
42
|
+
end
|
43
|
+
|
44
|
+
after :each do
|
45
|
+
File.exist?(@file) && File.unlink(@file)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should generate pdf file following :output" do
|
49
|
+
File.exist?(@file).should be_false
|
50
|
+
Generator.new(@url, @file).generate
|
51
|
+
File.exist?(@file).should be_true
|
52
|
+
|
53
|
+
@file.should be_pdf_file
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should raise PhantomPDF::DestinationTypeError when :output is not a string" do
|
57
|
+
expect{
|
58
|
+
Generator.new(@url, Object.new)
|
59
|
+
}.to raise_error(PhantomPDF::DestinationTypeError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should raise PhantomPDF::DestinationPermitError when :output is not writable" do
|
63
|
+
File.stub(:writable?, '/tmp') { false }
|
64
|
+
|
65
|
+
expect{
|
66
|
+
Generator.new(@url, @file)
|
67
|
+
}.to raise_error(PhantomPDF::DestinationPermitError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with options for pdf format" do
|
72
|
+
before :all do
|
73
|
+
@url = 'http://www.google.com'
|
74
|
+
@file = '/tmp/google.pdf'
|
75
|
+
end
|
76
|
+
|
77
|
+
after :each do
|
78
|
+
File.exist?(@file) && File.unlink(@file)
|
79
|
+
end
|
80
|
+
|
81
|
+
pending "should support custom :header" do
|
82
|
+
header = 'Hello, PhantomPDF header!'
|
83
|
+
|
84
|
+
Generator.new(@url, @file, {:header => header}).generate
|
85
|
+
|
86
|
+
pdf_content = PDF::Reader.new(@file).page(1).text
|
87
|
+
pdf_content.should include(header)
|
88
|
+
end
|
89
|
+
|
90
|
+
pending "should support custom :footer" do
|
91
|
+
header = 'Hello, PhantomPDF footer!'
|
92
|
+
|
93
|
+
Generator.new(@url, @file, {:footer => header}).generate
|
94
|
+
|
95
|
+
pdf_content = PDF::Reader.new(@file).page(1).text
|
96
|
+
pdf_content.should include(header)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "#generate!" do
|
102
|
+
before :all do
|
103
|
+
@url = 'http://www.google.com'
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should raise PhantomPDF::RenderingError when failed to generate" do
|
107
|
+
$?.stub(:exitstatus) { 1 }
|
108
|
+
|
109
|
+
generator = Generator.new(@url)
|
110
|
+
generator.stub(:run) { 'rendering error' }
|
111
|
+
|
112
|
+
expect{
|
113
|
+
generator.generate!
|
114
|
+
}.to raise_error(PhantomPDF::RenderingError)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "#to_string" do
|
119
|
+
before :all do
|
120
|
+
@url = 'http://www.google.com'
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should return string" do
|
124
|
+
Generator.new(@url).to_string.should be_pdf_string
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module PhantomPDF
|
4
|
+
describe Middleware do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
|
7
|
+
let(:page) { File.read File.expand_path('../../fixtures/phantompdf.html', __FILE__) }
|
8
|
+
let(:phantompdf) do
|
9
|
+
lambda {|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => page.size.to_s}, [page]]}
|
10
|
+
end
|
11
|
+
let(:app) { Middleware.new(phantompdf, '/tmp') }
|
12
|
+
|
13
|
+
it "should works" do
|
14
|
+
get '/index.pdf'
|
15
|
+
|
16
|
+
last_response.status.should == 200
|
17
|
+
last_response.body.should be_pdf_string
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should respond with original data without PDF request" do
|
21
|
+
get '/'
|
22
|
+
|
23
|
+
last_response.status.should == 200
|
24
|
+
last_response.body.should_not be_pdf_string
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module PhantomPDF
|
4
|
+
describe Source do
|
5
|
+
before do
|
6
|
+
@url = ['http://www.test.com', 'file://tmp/phantompdf.html', 'ftp://phantompdf:passwd@test.com/phantompdf.html'].sample
|
7
|
+
@file = File.expand_path('../../fixtures/phantompdf.html', __FILE__)
|
8
|
+
@html = File.read(@file)
|
9
|
+
|
10
|
+
@url_source = Source.new(@url)
|
11
|
+
@file_source = Source.new(@file)
|
12
|
+
@html_source = Source.new(@html)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "#url?" do
|
16
|
+
it "should return true for url" do
|
17
|
+
@url_source.url?.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return false for file" do
|
21
|
+
@file_source.url?.should be_false
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return false for html" do
|
25
|
+
@html_source.url?.should be_false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "#file?" do
|
30
|
+
it "should return false for url" do
|
31
|
+
@url_source.file?.should be_false
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return true for file" do
|
35
|
+
@file_source.file?.should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return false for html" do
|
39
|
+
@html_source.file?.should be_false
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return false if file does not exist" do
|
43
|
+
Source.new('path/to/unexisted/file').file?.should be_false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "#html?" do
|
48
|
+
it "should return false for url" do
|
49
|
+
@url_source.html?.should be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should return false for file" do
|
53
|
+
@file_source.html?.should be_false
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should return true for html" do
|
57
|
+
@html_source.html?.should be_true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "#valid?" do
|
62
|
+
it "should return true for url" do
|
63
|
+
@url_source.valid?.should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should return true for file" do
|
67
|
+
@file_source.valid?.should be_true
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return true for html" do
|
71
|
+
@html_source.valid?.should be_true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "#to_s" do
|
76
|
+
it "should return url for url source" do
|
77
|
+
@url_source.to_s.should == @url
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return file path for file source" do
|
81
|
+
@file_source.to_s.should == @file
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should return html string for html source" do
|
85
|
+
@html_source.to_s.should == @html
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'phantomjs'
|
2
|
+
require 'phantompdf'
|
3
|
+
require 'pdf-reader'
|
4
|
+
require 'byebug'
|
5
|
+
|
6
|
+
require 'rack/test'
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
# some staff goes here
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec::Matchers.define :be_pdf_file do
|
13
|
+
match do |actual|
|
14
|
+
case actual
|
15
|
+
when File
|
16
|
+
actual.read[0...4] == '%PDF'
|
17
|
+
when String
|
18
|
+
File.exist?(actual) && File.open(actual).read[0...4] == '%PDF'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec::Matchers.define :be_pdf_string do
|
24
|
+
match do |actual|
|
25
|
+
actual[0...4] == '%PDF'
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
var page = require('webpage').create(),
|
2
|
+
fs = require('fs'),
|
3
|
+
system = require('system'),
|
4
|
+
system_args_length = system.args.length;
|
5
|
+
if (system_args_length < 3 || system_args_length > 12) {
|
6
|
+
console.log('Usage: phantomjs rasterize.js SOURCE DESTINATION [paperWidth*paperHeight|paperFormat] [header] [footer] [margin] [orientation] [zoom] [cookie_file] [render_timeout] [timeout]');
|
7
|
+
console.log(' : paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
|
8
|
+
phantom.exit(1);
|
9
|
+
}
|
10
|
+
|
11
|
+
var input = system.args[1],
|
12
|
+
output = system.args[2],
|
13
|
+
|
14
|
+
margin = system.args[6] || '0cm',
|
15
|
+
orientation = system.args[7] || 'portrait',
|
16
|
+
zoom = system.args[8] || '1.0',
|
17
|
+
|
18
|
+
cookie_file = system.args[9],
|
19
|
+
cookies = {},
|
20
|
+
|
21
|
+
render_timeout = system.args[10] || 10000,
|
22
|
+
timeout = system.args[11] || 90000;
|
23
|
+
|
24
|
+
window.setTimeout(function () {
|
25
|
+
console.log("Shit's being weird no result within " + timeout + "ms");
|
26
|
+
phantom.exit(1);
|
27
|
+
}, timeout);
|
28
|
+
|
29
|
+
page.viewportSize = { width:600, height:600 };
|
30
|
+
if (output.substr(-4) === '.pdf') {
|
31
|
+
var size, header, footer,
|
32
|
+
paper_size_options = {};
|
33
|
+
if (system_args_length > 3) {
|
34
|
+
size = system.args[3].split('*');
|
35
|
+
paper_size_options = size.length === 2 ? {width:size[0], height:size[1], margin:'0px'} : {format:system.args[3], orientation:orientation, margin:margin};
|
36
|
+
}
|
37
|
+
|
38
|
+
if (system_args_length > 4) {
|
39
|
+
header = system.args[4].split('*');
|
40
|
+
if (header.length >= 2) {
|
41
|
+
paper_size_options['header'] = {
|
42
|
+
height: header.shift(),
|
43
|
+
contents: phantom.callback(function(pageNum, pageTotal){
|
44
|
+
return header.join('*').replace(new RegExp('%{pageNum}', 'g'), pageNum).replace(new RegExp('%{pageTotal}', 'g'), pageTotal);
|
45
|
+
})
|
46
|
+
};
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
if (system_args_length > 5) {
|
51
|
+
footer = system.args[5].split('*');
|
52
|
+
if (footer.length >= 2) {
|
53
|
+
paper_size_options['footer'] = {
|
54
|
+
height: footer.shift(),
|
55
|
+
contents: phantom.callback(function(pageNum, pageTotal){
|
56
|
+
return footer.join('*').replace(new RegExp('%{pageNum}', 'g'), pageNum).replace(new RegExp('%{pageTotal}', 'g'), pageTotal);
|
57
|
+
})
|
58
|
+
};
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
page.paperSize = paper_size_options;
|
63
|
+
}
|
64
|
+
page.zoomFactor = zoom;
|
65
|
+
|
66
|
+
if (/<([a-z]+?\d*?).*?>[^\0]*?<\/\1>/i.test(input) === true) {
|
67
|
+
page.content = input;
|
68
|
+
|
69
|
+
window.setTimeout(function () {
|
70
|
+
page.render(output + '_tmp.pdf');
|
71
|
+
|
72
|
+
if (fs.exists(output)) {
|
73
|
+
fs.remove(output);
|
74
|
+
}
|
75
|
+
|
76
|
+
try {
|
77
|
+
fs.move(output + '_tmp.pdf', output);
|
78
|
+
|
79
|
+
console.log(output);
|
80
|
+
} catch (e) {
|
81
|
+
phantom.exit(1);
|
82
|
+
throw e;
|
83
|
+
}
|
84
|
+
|
85
|
+
phantom.exit();
|
86
|
+
}, render_timeout);
|
87
|
+
} else {
|
88
|
+
var statusCode = null;
|
89
|
+
|
90
|
+
if (cookie_file) {
|
91
|
+
try {
|
92
|
+
fd = fs.open(cookie_file, 'r');
|
93
|
+
cookies = JSON.parse(fd.read());
|
94
|
+
fs.remove(cookie_file);
|
95
|
+
|
96
|
+
|
97
|
+
phantom.cookiesEnabled = true;
|
98
|
+
phantom.cookies = cookies;
|
99
|
+
} catch (e) {
|
100
|
+
// console.log(e);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
// determine the statusCode
|
105
|
+
page.onResourceReceived = function (resource) {
|
106
|
+
if (new RegExp('^'+input).test(resource.url)) {
|
107
|
+
statusCode = resource.status;
|
108
|
+
}
|
109
|
+
};
|
110
|
+
|
111
|
+
page.open(input, function (status) {
|
112
|
+
if (status !== 'success' || (statusCode !== null && statusCode != 200)) {
|
113
|
+
console.log(statusCode, 'Failed to load the input!');
|
114
|
+
|
115
|
+
if (fs.exists(output)) {
|
116
|
+
fs.remove(output);
|
117
|
+
}
|
118
|
+
|
119
|
+
try {
|
120
|
+
fs.touch(output);
|
121
|
+
} catch (e) {
|
122
|
+
phantom.exit(1);
|
123
|
+
throw e;
|
124
|
+
}
|
125
|
+
|
126
|
+
phantom.exit(1);
|
127
|
+
} else {
|
128
|
+
window.setTimeout(function () {
|
129
|
+
page.render(output + '_tmp.pdf');
|
130
|
+
|
131
|
+
if (fs.exists(output)) {
|
132
|
+
fs.remove(output);
|
133
|
+
}
|
134
|
+
|
135
|
+
try {
|
136
|
+
fs.move(output + '_tmp.pdf', output);
|
137
|
+
|
138
|
+
console.log(output);
|
139
|
+
} catch (e) {
|
140
|
+
phantom.exit(1);
|
141
|
+
throw e;
|
142
|
+
}
|
143
|
+
|
144
|
+
phantom.exit();
|
145
|
+
}, render_timeout);
|
146
|
+
}
|
147
|
+
});
|
148
|
+
}
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: phantompdf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Spring MC
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: phantomjs
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pdf-reader
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: byebug
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Generate PDF from HTML using PhantomJS
|
98
|
+
email:
|
99
|
+
- Heresy.Mc@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- .gitignore
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- LICENSE
|
108
|
+
- NEWS.md
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- lib/phantompdf.rb
|
112
|
+
- lib/phantompdf/assets.rb
|
113
|
+
- lib/phantompdf/config.rb
|
114
|
+
- lib/phantompdf/error.rb
|
115
|
+
- lib/phantompdf/generator.rb
|
116
|
+
- lib/phantompdf/middleware.rb
|
117
|
+
- lib/phantompdf/source.rb
|
118
|
+
- lib/phantompdf/version.rb
|
119
|
+
- phantompdf.gemspec
|
120
|
+
- spec/fixtures/phantompdf.html
|
121
|
+
- spec/phantompdf/assets_spec.rb
|
122
|
+
- spec/phantompdf/config_spec.rb
|
123
|
+
- spec/phantompdf/generator_spec.rb
|
124
|
+
- spec/phantompdf/middleware_spec.rb
|
125
|
+
- spec/phantompdf/source_spec.rb
|
126
|
+
- spec/spec_helper.rb
|
127
|
+
- vendor/assets/javascripts/rasterize.js
|
128
|
+
homepage: https://github.com/mcspring/phantompdf
|
129
|
+
licenses: []
|
130
|
+
metadata: {}
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - '>='
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.0.3
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: A PhantomJS based PDF generator
|
151
|
+
test_files:
|
152
|
+
- spec/fixtures/phantompdf.html
|
153
|
+
- spec/phantompdf/assets_spec.rb
|
154
|
+
- spec/phantompdf/config_spec.rb
|
155
|
+
- spec/phantompdf/generator_spec.rb
|
156
|
+
- spec/phantompdf/middleware_spec.rb
|
157
|
+
- spec/phantompdf/source_spec.rb
|
158
|
+
- spec/spec_helper.rb
|