adzap-wicked_pdf 2.0.0.beta1
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/.github/issue_template.md +15 -0
- data/.gitignore +21 -0
- data/.rubocop.yml +22 -0
- data/.rubocop_todo.yml +63 -0
- data/.travis.yml +30 -0
- data/CHANGELOG.md +135 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +446 -0
- data/Rakefile +31 -0
- data/gemfiles/4.2.gemfile +6 -0
- data/gemfiles/5.0.gemfile +6 -0
- data/gemfiles/5.1.gemfile +6 -0
- data/gemfiles/5.2.gemfile +9 -0
- data/gemfiles/rails_edge.gemfile +9 -0
- data/generators/wicked_pdf/templates/wicked_pdf.rb +21 -0
- data/generators/wicked_pdf/wicked_pdf_generator.rb +7 -0
- data/init.rb +2 -0
- data/lib/generators/wicked_pdf_generator.rb +6 -0
- data/lib/wicked_pdf.rb +29 -0
- data/lib/wicked_pdf/asset_helper.rb +141 -0
- data/lib/wicked_pdf/binary.rb +56 -0
- data/lib/wicked_pdf/command.rb +52 -0
- data/lib/wicked_pdf/document.rb +47 -0
- data/lib/wicked_pdf/middleware.rb +101 -0
- data/lib/wicked_pdf/option_parser.rb +220 -0
- data/lib/wicked_pdf/pdf_helper.rb +17 -0
- data/lib/wicked_pdf/progress.rb +33 -0
- data/lib/wicked_pdf/railtie.rb +19 -0
- data/lib/wicked_pdf/renderer.rb +121 -0
- data/lib/wicked_pdf/tempfile.rb +13 -0
- data/lib/wicked_pdf/version.rb +3 -0
- data/test/dummy/app/assets/javascripts/application.js +16 -0
- data/test/dummy/app/assets/javascripts/wicked.js +1 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/assets/stylesheets/wicked.css +1 -0
- data/test/dummy/config/database.yml +3 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/log/.gitignore +1 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/fixtures/database.yml +4 -0
- data/test/fixtures/document_with_long_line.html +16 -0
- data/test/fixtures/wicked.css +1 -0
- data/test/fixtures/wicked.js +1 -0
- data/test/functional/pdf_helper_test.rb +61 -0
- data/test/functional/wicked_pdf_asset_helper_test.rb +118 -0
- data/test/test_helper.rb +33 -0
- data/test/unit/wicked_pdf_binary_test.rb +52 -0
- data/test/unit/wicked_pdf_command_test.rb +4 -0
- data/test/unit/wicked_pdf_document_test.rb +60 -0
- data/test/unit/wicked_pdf_option_parser_test.rb +128 -0
- data/test/unit/wicked_pdf_renderer_test.rb +43 -0
- data/test/unit/wicked_pdf_test.rb +8 -0
- data/test/unit/wkhtmltopdf_location_test.rb +50 -0
- data/wicked_pdf.gemspec +38 -0
- metadata +249 -0
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rdoc/task'
|
4
|
+
require 'rails/version'
|
5
|
+
require 'bundler/gem_tasks'
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => [:test, :rubocop]
|
9
|
+
|
10
|
+
desc 'Test the wicked_pdf plugin.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Run RuboCop'
|
19
|
+
task :rubocop do
|
20
|
+
require 'rubocop/rake_task'
|
21
|
+
RuboCop::RakeTask.new
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Generate documentation for the wicked_pdf gem.'
|
25
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
26
|
+
rdoc.rdoc_dir = 'rdoc'
|
27
|
+
rdoc.title = 'WickedPdf'
|
28
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
29
|
+
rdoc.rdoc_files.include('README.md')
|
30
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# WickedPDF Global Configuration
|
2
|
+
#
|
3
|
+
# Use this to set up shared configuration options for your entire application.
|
4
|
+
# Any of the configuration options shown here can also be applied to single
|
5
|
+
# models by passing arguments to the `render :pdf` call.
|
6
|
+
#
|
7
|
+
# To learn more, check out the README:
|
8
|
+
#
|
9
|
+
# https://github.com/adzap/wicked_pdf/blob/master/README.md
|
10
|
+
|
11
|
+
WickedPdf.config = {
|
12
|
+
# Path to the wkhtmltopdf executable: This usually isn't needed if using
|
13
|
+
# one of the wkhtmltopdf-binary family of gems.
|
14
|
+
# exe_path: '/usr/local/bin/wkhtmltopdf',
|
15
|
+
# or
|
16
|
+
# exe_path: Gem.bin_path('wkhtmltopdf-binary', 'wkhtmltopdf')
|
17
|
+
|
18
|
+
# Layout file to be used for all PDFs
|
19
|
+
# (but can be overridden in `render :pdf` calls)
|
20
|
+
# layout: 'pdf.html',
|
21
|
+
}
|
data/init.rb
ADDED
data/lib/wicked_pdf.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# wkhtml2pdf Ruby interface
|
2
|
+
# http://wkhtmltopdf.org/
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
require 'digest/md5'
|
6
|
+
require 'rbconfig'
|
7
|
+
require 'open3'
|
8
|
+
|
9
|
+
require 'active_support/core_ext/object/blank'
|
10
|
+
|
11
|
+
require 'wicked_pdf/version'
|
12
|
+
require 'wicked_pdf/tempfile'
|
13
|
+
require 'wicked_pdf/binary'
|
14
|
+
require 'wicked_pdf/option_parser'
|
15
|
+
require 'wicked_pdf/progress'
|
16
|
+
require 'wicked_pdf/command'
|
17
|
+
require 'wicked_pdf/document'
|
18
|
+
require 'wicked_pdf/railtie' if defined?(Rails.env)
|
19
|
+
require 'wicked_pdf/middleware'
|
20
|
+
|
21
|
+
module WickedPdf
|
22
|
+
class << self
|
23
|
+
attr_accessor :config
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.new(command = WickedPdf::Command.new)
|
27
|
+
WickedPdf::Document.new(command)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module WickedPdf
|
4
|
+
module AssetHelper
|
5
|
+
ASSET_URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/
|
6
|
+
|
7
|
+
def self.root_path
|
8
|
+
String === Rails.root ? Pathname.new(Rails.root) : Rails.root
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.add_extension(filename, extension)
|
12
|
+
filename.to_s.split('.').include?(extension) ? filename : "#{filename}.#{extension}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def wicked_pdf_asset_base64(path)
|
16
|
+
asset = find_asset(path)
|
17
|
+
raise "Could not find asset '#{path}'" if asset.nil?
|
18
|
+
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, '')
|
19
|
+
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def wicked_pdf_stylesheet_link_tag(*sources)
|
23
|
+
stylesheet_contents = sources.collect do |source|
|
24
|
+
source = WickedPdf::AssetHelper.add_extension(source, 'css')
|
25
|
+
"<style type='text/css'>#{read_asset(source)}</style>"
|
26
|
+
end.join("\n")
|
27
|
+
|
28
|
+
stylesheet_contents.gsub(ASSET_URL_REGEX) do
|
29
|
+
if Regexp.last_match[1].starts_with?('data:')
|
30
|
+
"url(#{Regexp.last_match[1]})"
|
31
|
+
else
|
32
|
+
"url(#{wicked_pdf_asset_path(Regexp.last_match[1])})"
|
33
|
+
end
|
34
|
+
end.html_safe
|
35
|
+
end
|
36
|
+
|
37
|
+
def wicked_pdf_image_tag(img, options = {})
|
38
|
+
image_tag wicked_pdf_asset_path(img), options
|
39
|
+
end
|
40
|
+
|
41
|
+
def wicked_pdf_javascript_src_tag(jsfile, options = {})
|
42
|
+
jsfile = WickedPdf::AssetHelper.add_extension(jsfile, 'js')
|
43
|
+
javascript_include_tag wicked_pdf_asset_path(jsfile), options
|
44
|
+
end
|
45
|
+
|
46
|
+
def wicked_pdf_javascript_include_tag(*sources)
|
47
|
+
sources.collect do |source|
|
48
|
+
source = WickedPdf::AssetHelper.add_extension(source, 'js')
|
49
|
+
"<script type='text/javascript'>#{read_asset(source)}</script>"
|
50
|
+
end.join("\n").html_safe
|
51
|
+
end
|
52
|
+
|
53
|
+
def wicked_pdf_asset_path(asset)
|
54
|
+
if (pathname = asset_pathname(asset).to_s) =~ URI_REGEXP
|
55
|
+
pathname
|
56
|
+
else
|
57
|
+
"file:///#{pathname}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# borrowed from actionpack/lib/action_view/helpers/asset_url_helper.rb
|
64
|
+
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
|
65
|
+
|
66
|
+
def asset_pathname(source)
|
67
|
+
if precompiled_or_absolute_asset?(source)
|
68
|
+
asset = asset_path(source)
|
69
|
+
pathname = prepend_protocol(asset)
|
70
|
+
if pathname =~ URI_REGEXP
|
71
|
+
# asset_path returns an absolute URL using asset_host if asset_host is set
|
72
|
+
pathname
|
73
|
+
else
|
74
|
+
File.join(Rails.public_path, asset.sub(/\A#{Rails.application.config.action_controller.relative_url_root}/, ''))
|
75
|
+
end
|
76
|
+
else
|
77
|
+
asset = find_asset(source)
|
78
|
+
if asset
|
79
|
+
# older versions need pathname, Sprockets 4 supports only filename
|
80
|
+
asset.respond_to?(:filename) ? asset.filename : asset.pathname
|
81
|
+
else
|
82
|
+
File.join(Rails.public_path, source)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_asset(path)
|
88
|
+
if Rails.application.assets.respond_to?(:find_asset)
|
89
|
+
Rails.application.assets.find_asset(path, :base_path => Rails.application.root.to_s)
|
90
|
+
else
|
91
|
+
Sprockets::Railtie.build_environment(Rails.application).find_asset(path, :base_path => Rails.application.root.to_s)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# will prepend a http or default_protocol to a protocol relative URL
|
96
|
+
# or when no protcol is set.
|
97
|
+
def prepend_protocol(source)
|
98
|
+
protocol = WickedPdf.config[:default_protocol] || 'http'
|
99
|
+
if source[0, 2] == '//'
|
100
|
+
source = [protocol, ':', source].join
|
101
|
+
elsif source[0] != '/' && !source[0, 8].include?('://')
|
102
|
+
source = [protocol, '://', source].join
|
103
|
+
end
|
104
|
+
source
|
105
|
+
end
|
106
|
+
|
107
|
+
def precompiled_or_absolute_asset?(source)
|
108
|
+
Rails.configuration.assets.compile == false ||
|
109
|
+
source.to_s[0] == '/' ||
|
110
|
+
source.to_s.match(/\Ahttps?\:\/\//)
|
111
|
+
end
|
112
|
+
|
113
|
+
def read_asset(source)
|
114
|
+
if precompiled_or_absolute_asset?(source)
|
115
|
+
pathname = asset_pathname(source)
|
116
|
+
if pathname =~ URI_REGEXP
|
117
|
+
read_from_uri(pathname)
|
118
|
+
elsif File.file?(pathname)
|
119
|
+
IO.read(pathname)
|
120
|
+
end
|
121
|
+
else
|
122
|
+
find_asset(source).to_s
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def read_from_uri(uri)
|
127
|
+
encoding = ':UTF-8'
|
128
|
+
asset = open(uri, "r#{encoding}", &:read)
|
129
|
+
asset = gzip(asset) if WickedPdf.config[:expect_gzipped_remote_assets]
|
130
|
+
asset
|
131
|
+
end
|
132
|
+
|
133
|
+
def gzip(asset)
|
134
|
+
stringified_asset = StringIO.new(asset)
|
135
|
+
gzipper = Zlib::GzipReader.new(stringified_asset)
|
136
|
+
gzipper.read
|
137
|
+
rescue Zlib::GzipFile::Error
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module WickedPdf
|
2
|
+
class Binary
|
3
|
+
EXE_NAME = 'wkhtmltopdf'.freeze
|
4
|
+
DEFAULT_BINARY_VERSION = Gem::Version.new('0.9.9')
|
5
|
+
|
6
|
+
attr_reader :path, :default_version
|
7
|
+
|
8
|
+
def initialize(binary_path = nil, default_version = DEFAULT_BINARY_VERSION)
|
9
|
+
@path = binary_path || find_binary_path
|
10
|
+
@default_version = default_version
|
11
|
+
|
12
|
+
raise "Location of #{EXE_NAME} unknown" if @path.empty?
|
13
|
+
raise "Bad #{EXE_NAME}'s path: #{@path}" unless File.exist?(@path)
|
14
|
+
raise "#{EXE_NAME} is not executable" unless File.executable?(@path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def version
|
18
|
+
@version ||= retrieve_binary_version
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_version_string(version_info)
|
22
|
+
match_data = /wkhtmltopdf\s*(\d*\.\d*\.\d*\w*)/.match(version_info)
|
23
|
+
if match_data && (match_data.length == 2)
|
24
|
+
Gem::Version.new(match_data[1])
|
25
|
+
else
|
26
|
+
default_version
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def retrieve_binary_version
|
33
|
+
_stdin, stdout, _stderr = Open3.popen3(@path + ' -V')
|
34
|
+
parse_version_string(stdout.gets(nil))
|
35
|
+
rescue StandardError
|
36
|
+
default_version
|
37
|
+
end
|
38
|
+
|
39
|
+
def find_binary_path
|
40
|
+
return WickedPdf.config[:exe_path] if WickedPdf.config[:exe_path]
|
41
|
+
|
42
|
+
begin
|
43
|
+
detected_path = (defined?(Bundler) ? Bundler.which('wkhtmltopdf') : `which wkhtmltopdf`).chomp
|
44
|
+
return detected_path if detected_path.present?
|
45
|
+
rescue StandardError
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
possible_locations = (ENV['PATH'].split(':') + %w[/usr/bin /usr/local/bin]).uniq
|
50
|
+
possible_locations += %w[~/bin] if ENV.key?('HOME')
|
51
|
+
|
52
|
+
exe_path ||= possible_locations.map { |l| File.expand_path("#{l}/#{EXE_NAME}") }.find { |location| File.exist?(location) }
|
53
|
+
exe_path || ''
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module WickedPdf
|
2
|
+
class Command
|
3
|
+
attr_reader :binary, :option_parser
|
4
|
+
|
5
|
+
def initialize(binary: Binary.new, option_parser: nil)
|
6
|
+
@binary = binary
|
7
|
+
@option_parser = option_parser || OptionParser.new(@binary.version)
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(options, *args)
|
11
|
+
command = [binary.path]
|
12
|
+
command += option_parser.parse(options)
|
13
|
+
command += args
|
14
|
+
|
15
|
+
print_command(command.inspect) if in_development_mode?
|
16
|
+
|
17
|
+
if track_progress?(options)
|
18
|
+
Progress.new(options[:progress]).execute(command)
|
19
|
+
else
|
20
|
+
begin
|
21
|
+
err = Open3.popen3(*command) do |_stdin, _stdout, stderr|
|
22
|
+
stderr.read
|
23
|
+
end
|
24
|
+
rescue StandardError => e
|
25
|
+
raise "Failed to execute:\n#{command}\nError: #{e}"
|
26
|
+
end
|
27
|
+
|
28
|
+
raise "Error generating PDF\n Command Error: #{err}" if options[:raise_on_all_errors] && !err.empty?
|
29
|
+
err
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def in_development_mode?
|
36
|
+
defined?(Rails.env) && Rails.env.development?
|
37
|
+
end
|
38
|
+
|
39
|
+
def print_command(cmd)
|
40
|
+
# TODO: if no Rails what then?
|
41
|
+
Rails.logger.debug '[wicked_pdf]: ' + cmd
|
42
|
+
end
|
43
|
+
|
44
|
+
def track_progress?(options)
|
45
|
+
options[:progress] && !on_windows?
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_windows?
|
49
|
+
RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module WickedPdf
|
2
|
+
class Document
|
3
|
+
def initialize(command = Command.new)
|
4
|
+
@command = command
|
5
|
+
end
|
6
|
+
|
7
|
+
def pdf_from_html_file(filepath, options = {})
|
8
|
+
pdf_from_url("file:///#{filepath}", options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def pdf_from_string(string, options = {})
|
12
|
+
options = options.dup
|
13
|
+
options.merge!(WickedPdf.config) { |_key, option, _config| option }
|
14
|
+
string_file = WickedPdf::Tempfile.new('wicked_pdf.html', options[:temp_path])
|
15
|
+
string_file.binmode
|
16
|
+
string_file.write(string)
|
17
|
+
string_file.close
|
18
|
+
|
19
|
+
pdf_from_html_file(string_file.path, options)
|
20
|
+
ensure
|
21
|
+
string_file.close! if string_file
|
22
|
+
end
|
23
|
+
|
24
|
+
def pdf_from_url(url, options = {})
|
25
|
+
# merge in global config options
|
26
|
+
options.merge!(WickedPdf.config) { |_key, option, _config| option }
|
27
|
+
generated_pdf_file = WickedPdf::Tempfile.new('wicked_pdf_generated_file.pdf', options[:temp_path])
|
28
|
+
|
29
|
+
result = @command.execute(options, url, generated_pdf_file.path.to_s)
|
30
|
+
|
31
|
+
if options[:return_file]
|
32
|
+
return_file = options.delete(:return_file)
|
33
|
+
return generated_pdf_file
|
34
|
+
end
|
35
|
+
|
36
|
+
generated_pdf_file.rewind
|
37
|
+
generated_pdf_file.binmode
|
38
|
+
pdf = generated_pdf_file.read
|
39
|
+
|
40
|
+
raise "PDF could not be generated!\n Command Error: #{result}" if pdf && pdf.rstrip.empty?
|
41
|
+
|
42
|
+
pdf
|
43
|
+
ensure
|
44
|
+
generated_pdf_file.close! if generated_pdf_file && !return_file
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|