pdfkit-middleware-to-file 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ .bundle
23
+ spec/custom_wkhtmltopdf_path.rb
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :gemcutter
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pdfkit (0.5.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.2)
10
+ mocha (0.9.10)
11
+ rake
12
+ rack (1.2.1)
13
+ rack-test (0.5.6)
14
+ rack (>= 1.0)
15
+ rake (0.8.7)
16
+ rspec (2.2.0)
17
+ rspec-core (~> 2.2)
18
+ rspec-expectations (~> 2.2)
19
+ rspec-mocks (~> 2.2)
20
+ rspec-core (2.2.1)
21
+ rspec-expectations (2.2.0)
22
+ diff-lcs (~> 1.1.2)
23
+ rspec-mocks (2.2.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ mocha (>= 0.9.10)
30
+ pdfkit!
31
+ rack-test (>= 0.5.6)
32
+ rspec (~> 2.2.0)
data/History.md ADDED
@@ -0,0 +1,19 @@
1
+ 2011-06-17
2
+ ==================
3
+ * Bump to 0.5.1
4
+ * Fix for response body coming through as an array.
5
+ * Added root_url configuration for setup where a host my not know its own name.
6
+ * Awareness of Bundler when looking for the wkhtmltopdf executable.
7
+ * Fix for file data getting truncated in Ruby 1.8.6
8
+ * Fix for 0.5.0 release getting stuck rendering all requests as PDFs.
9
+ * More robust meta tag detection.
10
+
11
+ 2010-12-27
12
+ ==================
13
+ * Bump to 0.5.0
14
+ * Switched to popen - adds support for JRuby and Windows
15
+ * Pulled in support for pdf rendering conditions in middleware via Rémy Coutable
16
+ * Use `which` to try and determine path to wkhtmltopdf
17
+ * Removed wkhtmltopdf auto installer
18
+ * Changed :disable\_smart\_shrinking to false for default options.
19
+ * Added History.md
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 jdpace
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/POST_INSTALL ADDED
@@ -0,0 +1,14 @@
1
+ ******************************************************************
2
+
3
+ Install wkhtmltopdf:
4
+
5
+ 1. Install by hand (recomended):
6
+
7
+ https://github.com/jdpace/PDFKit/wiki/Installing-WKHTMLTOPDF
8
+
9
+ 2. Try using the wkhtmltopdf-binary gem (mac + linux i386)
10
+
11
+ gem install wkhtmltopdf-binary
12
+
13
+
14
+ ******************************************************************
data/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # PDFKit
2
+
3
+ Create PDFs using plain old HTML+CSS. Uses [wkhtmltopdf](http://github.com/antialize/wkhtmltopdf) on the back-end which renders HTML using Webkit.
4
+
5
+ ## Install
6
+
7
+ ### PDFKit
8
+
9
+ gem install pdfkit
10
+
11
+ ### wkhtmltopdf
12
+
13
+ 1. Install by hand (recommended):
14
+
15
+ <https://github.com/jdpace/PDFKit/wiki/Installing-WKHTMLTOPDF>
16
+
17
+ 2. Try using the wkhtmltopdf-binary gem (mac + linux i386)
18
+
19
+ gem install wkhtmltopdf-binary
20
+
21
+ *Note:* The automated installer has been removed.
22
+
23
+ ## Usage
24
+
25
+ # PDFKit.new takes the HTML and any options for wkhtmltopdf
26
+ # run `wkhtmltopdf --extended-help` for a full list of options
27
+ kit = PDFKit.new(html, :page_size => 'Letter')
28
+ kit.stylesheets << '/path/to/css/file'
29
+
30
+ # Git an inline PDF
31
+ pdf = kit.to_pdf
32
+
33
+ # Save the PDF to a file
34
+ file = kit.to_file('/path/to/save/pdf')
35
+
36
+ # PDFKit.new can optionally accept a URL or a File.
37
+ # Stylesheets can not be added when source is provided as a URL of File.
38
+ kit = PDFKit.new('http://google.com')
39
+ kit = PDFKit.new(File.new('/path/to/html'))
40
+
41
+ # Add any kind of option through meta tags
42
+ PDFKit.new('<html><head><meta name="pdfkit-page_size" content="Letter")
43
+
44
+ ## Configuration
45
+
46
+ If you're on Windows or you installed wkhtmltopdf by hand to a location other than /usr/local/bin you will need to tell PDFKit where the binary is. You can configure PDFKit like so:
47
+
48
+ # config/initializers/pdfkit.rb
49
+ PDFKit.configure do |config|
50
+ # config.wkhtmltopdf = '/path/to/wkhtmltopdf'
51
+ # config.default_options = {
52
+ # :page_size => 'Legal',
53
+ # :print_media_type => true
54
+ # }
55
+ # config.root_url = "http://localhost" # Use only if your external hostname is unavailable on the server.
56
+ end
57
+
58
+ ## Middleware
59
+
60
+ PDFKit comes with a middleware that allows users to get a PDF view of any page on your site by appending .pdf to the URL.
61
+
62
+ ### Middleware Setup
63
+
64
+ **Non-Rails Rack apps**
65
+
66
+ # in config.ru
67
+ require 'pdfkit'
68
+ use PDFKit::Middleware
69
+
70
+ **Rails apps**
71
+
72
+ # in application.rb(Rails3) or environment.rb(Rails2)
73
+ require 'pdfkit'
74
+ config.middleware.use PDFKit::Middleware
75
+
76
+ **With PDFKit options**
77
+
78
+ # options will be passed to PDFKit.new
79
+ config.middleware.use PDFKit::Middleware, :print_media_type => true
80
+
81
+ **With conditions to limit routes that can be generated in pdf**
82
+
83
+ # conditions can be regexps (either one or an array)
84
+ config.middleware.use PDFKit::Middleware, {}, :only => %r[^/public]
85
+ config.middleware.use PDFKit::Middleware, {}, :only => [%r[^/invoice], %r[^/public]]
86
+
87
+ # conditions can be strings (either one or an array)
88
+ config.middleware.use PDFKit::Middleware, {}, :only => '/public'
89
+ config.middleware.use PDFKit::Middleware, {}, :only => ['/invoice', '/public']
90
+
91
+ ## Troubleshooting
92
+
93
+ * **Single thread issue:** In development environments it is common to run a
94
+ single server process. This can cause issues when rendering your pdf
95
+ requires wkhtmltopdf to hit your server again (for images, js, css).
96
+ This is because the resource requests will get blocked by the initial
97
+ request and the initial request will be waiting on the resource
98
+ requests causing a deadlock.
99
+
100
+ This is usually not an issue in a production environment. To get
101
+ around this issue you may want to run a server with multiple workers
102
+ like Passenger or try to embed your resources within your HTML to
103
+ avoid extra HTTP requests.
104
+
105
+ * **Resources aren't included in the PDF:** Images, CSS, or JavaScript
106
+ does not seem to be downloading correctly in the PDF. This is due
107
+ to the fact that wkhtmltopdf does not know where to find those files.
108
+ Make sure you are using absolute paths (start with forward slash) to
109
+ your resources. If you are using PDFKit to generate PDFs from a raw
110
+ HTML source make sure you use complete paths (either file paths or
111
+ urls including the domain). In restrictive server environments the
112
+ root_url configuration may be what you are looking for change your
113
+ asset host.
114
+
115
+ ## TODO
116
+ - add amd64 support in --install-wkhtmltopdf
117
+
118
+ ## Note on Patches/Pull Requests
119
+
120
+ * Fork the project.
121
+ * Setup your development environment with: gem install bundler; bundle install
122
+ * Make your feature addition or bug fix.
123
+ * Add tests for it. This is important so I don't break it in a
124
+ future version unintentionally.
125
+ * Commit, do not mess with rakefile, version, or history.
126
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
127
+ * Send me a pull request. Bonus points for topic branches.
128
+
129
+ ## Copyright
130
+
131
+ Copyright (c) 2010 Jared Pace. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require 'rspec/core/rake_task'
7
+ RSpec::Core::RakeTask.new(:spec) do |spec|
8
+ end
9
+
10
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
11
+ spec.rcov = true
12
+ end
13
+
14
+ task :default => :spec
15
+
16
+ require 'rake/rdoctask'
17
+ Rake::RDocTask.new do |rdoc|
18
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
19
+
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = "PDFKit #{version}"
22
+ rdoc.rdoc_files.include('README*')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
@@ -0,0 +1,43 @@
1
+ class PDFKit
2
+ class Configuration
3
+ attr_accessor :meta_tag_prefix, :default_options, :root_url
4
+ attr_writer :wkhtmltopdf
5
+
6
+ def initialize
7
+ @meta_tag_prefix = 'pdfkit-'
8
+ @default_options = {
9
+ :disable_smart_shrinking => false,
10
+ :page_size => 'Letter',
11
+ :margin_top => '0.75in',
12
+ :margin_right => '0.75in',
13
+ :margin_bottom => '0.75in',
14
+ :margin_left => '0.75in',
15
+ :encoding => "UTF-8"
16
+ }
17
+ end
18
+
19
+ def wkhtmltopdf
20
+ @wkhtmltopdf ||= (defined?(Bundler) ? `bundle exec which wkhtmltopdf` : `which wkhtmltopdf`).chomp
21
+ end
22
+ end
23
+
24
+ class << self
25
+ attr_accessor :configuration
26
+ end
27
+
28
+ # Configure PDFKit someplace sensible,
29
+ # like config/initializers/pdfkit.rb
30
+ #
31
+ # @example
32
+ # PDFKit.configure do |config|
33
+ # config.wkhtmltopdf = '/usr/bin/wkhtmltopdf'
34
+ # end
35
+
36
+ def self.configuration
37
+ @configuration ||= Configuration.new
38
+ end
39
+
40
+ def self.configure
41
+ yield(configuration)
42
+ end
43
+ end
@@ -0,0 +1,81 @@
1
+ class PDFKit
2
+
3
+ class Middleware
4
+
5
+ def initialize(app, options = {}, conditions = {})
6
+ @app = app
7
+ @options = options
8
+ @conditions = conditions
9
+ end
10
+
11
+ def call(env)
12
+ @request = Rack::Request.new(env)
13
+ @render_pdf = false
14
+
15
+ set_request_to_render_as_pdf(env) if render_as_pdf?
16
+ status, headers, response = @app.call(env)
17
+
18
+ if rendering_pdf? && headers['Content-Type'] =~ /text\/html|application\/xhtml\+xml/
19
+ body = response.respond_to?(:body) ? response.body : response.join
20
+ body = body.join if body.is_a?(Array)
21
+ pdf = PDFKit.new(translate_paths(body, env), @options)
22
+ file = pdf.to_file( "~/myballs.pdf" )
23
+ body = pdf.to_pdf
24
+ response = [body]
25
+
26
+ # Do not cache PDFs
27
+ headers.delete('ETag')
28
+ headers.delete('Cache-Control')
29
+
30
+ headers["Content-Length"] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
31
+ headers["Content-Type"] = "application/pdf"
32
+ end
33
+
34
+ [status, headers, response]
35
+ end
36
+
37
+ private
38
+
39
+ # Change relative paths to absolute
40
+ def translate_paths(body, env)
41
+ # Host with protocol
42
+ root = PDFKit.configuration.root_url || "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/"
43
+
44
+ body.gsub(/(href|src)=(['"])\/([^\"']*|[^"']*)['"]/, '\1=\2' + root + '\3\2')
45
+ end
46
+
47
+ def rendering_pdf?
48
+ @render_pdf
49
+ end
50
+
51
+ def render_as_pdf?
52
+ request_path_is_pdf = @request.path.match(%r{\.pdf$})
53
+
54
+ if request_path_is_pdf && @conditions[:only]
55
+ rules = [@conditions[:only]].flatten
56
+ rules.any? do |pattern|
57
+ if pattern.is_a?(Regexp)
58
+ @request.path =~ pattern
59
+ else
60
+ @request.path[0, pattern.length] == pattern
61
+ end
62
+ end
63
+ else
64
+ request_path_is_pdf
65
+ end
66
+ end
67
+
68
+ def set_request_to_render_as_pdf(env)
69
+ @render_pdf = true
70
+ path = @request.path.sub(%r{\.pdf$}, '')
71
+ %w[PATH_INFO REQUEST_URI].each { |e| env[e] = path }
72
+ env['HTTP_ACCEPT'] = concat(env['HTTP_ACCEPT'], Rack::Mime.mime_type('.html'))
73
+ env["Rack-Middleware-PDFKit"] = "true"
74
+ end
75
+
76
+ def concat(accepts, type)
77
+ (accepts || '').split(',').unshift(type).compact.join(',')
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,137 @@
1
+ class PDFKit
2
+
3
+ class NoExecutableError < StandardError
4
+ def initialize
5
+ msg = "No wkhtmltopdf executable found at #{PDFKit.configuration.wkhtmltopdf}\n"
6
+ msg << ">> Please install wkhtmltopdf - https://github.com/jdpace/PDFKit/wiki/Installing-WKHTMLTOPDF"
7
+ super(msg)
8
+ end
9
+ end
10
+
11
+ class ImproperSourceError < StandardError
12
+ def initialize(msg)
13
+ super("Improper Source: #{msg}")
14
+ end
15
+ end
16
+
17
+ attr_accessor :source, :stylesheets
18
+ attr_reader :options
19
+
20
+ def initialize(url_file_or_html, options = {})
21
+ @source = Source.new(url_file_or_html)
22
+
23
+ @stylesheets = []
24
+
25
+ @options = PDFKit.configuration.default_options.merge(options)
26
+ @options.merge! find_options_in_meta(url_file_or_html) unless source.url?
27
+ @options = normalize_options(@options)
28
+
29
+ raise NoExecutableError.new unless File.exists?(PDFKit.configuration.wkhtmltopdf)
30
+ end
31
+
32
+ def command(path = nil)
33
+ args = [executable]
34
+ args += @options.to_a.flatten.compact
35
+ args << '--quiet'
36
+
37
+ if @source.html?
38
+ args << '-' # Get HTML from stdin
39
+ else
40
+ args << @source.to_s
41
+ end
42
+
43
+ args << (path || '-') # Write to file or stdout
44
+
45
+ args.map {|arg| %Q{"#{arg.gsub('"', '\"')}"}}
46
+ end
47
+
48
+ def executable
49
+ default = PDFKit.configuration.wkhtmltopdf
50
+ return default if default !~ /^\// # its not a path, so nothing we can do
51
+ if File.exist?(default)
52
+ default
53
+ else
54
+ default.split('/').last
55
+ end
56
+ end
57
+
58
+ def to_pdf(path=nil)
59
+ append_stylesheets
60
+
61
+ args = command(path)
62
+ invoke = args.join(' ')
63
+
64
+ result = IO.popen(invoke, "wb+") do |pdf|
65
+ pdf.puts(@source.to_s) if @source.html?
66
+ pdf.close_write
67
+ pdf.gets(nil)
68
+ end
69
+ result = File.read(path) if path
70
+
71
+ raise "command failed: #{invoke}" if result.to_s.strip.empty?
72
+ return result
73
+ end
74
+
75
+ def to_file(path)
76
+ self.to_pdf(path)
77
+ File.new(path)
78
+ end
79
+
80
+ protected
81
+
82
+ def find_options_in_meta(content)
83
+ # Read file if content is a File
84
+ content = content.read if content.is_a?(File)
85
+
86
+ found = {}
87
+ content.scan(/<meta [^>]*>/) do |meta|
88
+ if meta.match(/name=["']#{PDFKit.configuration.meta_tag_prefix}/)
89
+ name = meta.scan(/name=["']#{PDFKit.configuration.meta_tag_prefix}([^"']*)/)[0][0]
90
+ found[name] = meta.scan(/content=["']([^"']*)/)[0][0]
91
+ end
92
+ end
93
+
94
+ found
95
+ end
96
+
97
+ def style_tag_for(stylesheet)
98
+ "<style>#{File.read(stylesheet)}</style>"
99
+ end
100
+
101
+ def append_stylesheets
102
+ raise ImproperSourceError.new('Stylesheets may only be added to an HTML source') if stylesheets.any? && !@source.html?
103
+
104
+ stylesheets.each do |stylesheet|
105
+ if @source.to_s.match(/<\/head>/)
106
+ @source.to_s.gsub!(/(<\/head>)/, style_tag_for(stylesheet)+'\1')
107
+ else
108
+ @source.to_s.insert(0, style_tag_for(stylesheet))
109
+ end
110
+ end
111
+ end
112
+
113
+ def normalize_options(options)
114
+ normalized_options = {}
115
+
116
+ options.each do |key, value|
117
+ next if !value
118
+ normalized_key = "--#{normalize_arg key}"
119
+ normalized_options[normalized_key] = normalize_value(value)
120
+ end
121
+ normalized_options
122
+ end
123
+
124
+ def normalize_arg(arg)
125
+ arg.to_s.downcase.gsub(/[^a-z0-9]/,'-')
126
+ end
127
+
128
+ def normalize_value(value)
129
+ case value
130
+ when TrueClass
131
+ nil
132
+ else
133
+ value.to_s
134
+ end
135
+ end
136
+
137
+ end
@@ -0,0 +1,23 @@
1
+ class PDFKit
2
+ class Source
3
+ def initialize(url_file_or_html)
4
+ @source = url_file_or_html
5
+ end
6
+
7
+ def url?
8
+ @source.is_a?(String) && @source.match(/\Ahttp/)
9
+ end
10
+
11
+ def file?
12
+ @source.kind_of?(File)
13
+ end
14
+
15
+ def html?
16
+ !(url? || file?)
17
+ end
18
+
19
+ def to_s
20
+ file? ? @source.path : @source
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ class PDFKit
2
+ VERSION = "0.5.1"
3
+ end
data/lib/pdfkit.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'pdfkit/source'
2
+ require 'pdfkit/pdfkit'
3
+ require 'pdfkit/middleware'
4
+ require 'pdfkit/configuration'
Binary file
Binary file
data/pdfkit.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "pdfkit/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "pdfkit-middleware-to-file"
7
+ s.version = "0.5.3" # PDFKit::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jared Pace", "Relevance", "Ashish"]
10
+ # s.email = ["jared@codewordstudios.com"]
11
+ # s.homepage = "http://github.com/jdpace/PDFKit"
12
+ s.summary = "HTML+CSS -> PDF"
13
+ s.description = "Uses wkhtmltopdf to create PDFs using HTML - and save file -> from Ashish post on stack overflow"
14
+
15
+ s.rubyforge_project = "pdfkit"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ # Developmnet Dependencies
23
+ s.add_development_dependency(%q<rspec>, ["~> 2.2.0"])
24
+ s.add_development_dependency(%q<mocha>, [">= 0.9.10"])
25
+ s.add_development_dependency(%q<rack-test>, [">= 0.5.6"])
26
+ end
27
+
@@ -0,0 +1 @@
1
+ body { font-size: 20px; }
@@ -0,0 +1,5 @@
1
+ <html>
2
+ <body>
3
+ <h1>Oh Hai!</h1>
4
+ </body>
5
+ </html>
@@ -0,0 +1,195 @@
1
+ require 'spec_helper'
2
+
3
+ def app; Rack::Lint.new(@app); end
4
+
5
+ def mock_app(options = {}, conditions = {})
6
+ main_app = lambda { |env|
7
+ @env = env
8
+ headers = {'Content-Type' => "text/html"}
9
+ [200, headers, @body || ['Hello world!']]
10
+ }
11
+
12
+ builder = Rack::Builder.new
13
+ builder.use PDFKit::Middleware, options, conditions
14
+ builder.run main_app
15
+ @app = builder.to_app
16
+ end
17
+
18
+ describe PDFKit::Middleware do
19
+
20
+ describe "#call" do
21
+ describe "conditions" do
22
+ describe ":only" do
23
+
24
+ describe "regex" do
25
+ describe "one" do
26
+ before { mock_app({}, :only => %r[^/public]) }
27
+
28
+ context "matching" do
29
+ specify do
30
+ get 'http://www.example.org/public/test.pdf'
31
+ last_response.headers["Content-Type"].should == "application/pdf"
32
+ last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
33
+ end
34
+ end
35
+
36
+ context "not matching" do
37
+ specify do
38
+ get 'http://www.example.org/secret/test.pdf'
39
+ last_response.headers["Content-Type"].should == "text/html"
40
+ last_response.body.should == "Hello world!"
41
+ end
42
+ end
43
+ end # one regex
44
+
45
+ describe "multiple" do
46
+ before { mock_app({}, :only => [%r[^/invoice], %r[^/public]]) }
47
+
48
+ context "matching" do
49
+ specify do
50
+ get 'http://www.example.org/public/test.pdf'
51
+ last_response.headers["Content-Type"].should == "application/pdf"
52
+ last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
53
+ end
54
+ end
55
+
56
+ context "not matching" do
57
+ specify do
58
+ get 'http://www.example.org/secret/test.pdf'
59
+ last_response.headers["Content-Type"].should == "text/html"
60
+ last_response.body.should == "Hello world!"
61
+ end
62
+ end
63
+ end # multiple regex
64
+ end # regex
65
+
66
+ describe "string" do
67
+ describe "one" do
68
+ before { mock_app({}, :only => '/public') }
69
+
70
+ context "matching" do
71
+ specify do
72
+ get 'http://www.example.org/public/test.pdf'
73
+ last_response.headers["Content-Type"].should == "application/pdf"
74
+ last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
75
+ end
76
+ end
77
+
78
+ context "not matching" do
79
+ specify do
80
+ get 'http://www.example.org/secret/test.pdf'
81
+ last_response.headers["Content-Type"].should == "text/html"
82
+ last_response.body.should == "Hello world!"
83
+ end
84
+ end
85
+ end # one string
86
+
87
+ describe "multiple" do
88
+ before { mock_app({}, :only => ['/invoice', '/public']) }
89
+
90
+ context "matching" do
91
+ specify do
92
+ get 'http://www.example.org/public/test.pdf'
93
+ last_response.headers["Content-Type"].should == "application/pdf"
94
+ last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
95
+ end
96
+ end
97
+
98
+ context "not matching" do
99
+ specify do
100
+ get 'http://www.example.org/secret/test.pdf'
101
+ last_response.headers["Content-Type"].should == "text/html"
102
+ last_response.body.should == "Hello world!"
103
+ end
104
+ end
105
+ end # multiple string
106
+ end # string
107
+
108
+ end
109
+ end
110
+
111
+ describe "remove .pdf from PATH_INFO and REQUEST_URI" do
112
+ before { mock_app }
113
+
114
+ context "matching" do
115
+ specify do
116
+ get 'http://www.example.org/public/file.pdf'
117
+ @env["PATH_INFO"].should == "/public/file"
118
+ @env["REQUEST_URI"].should == "/public/file"
119
+ end
120
+ specify do
121
+ get 'http://www.example.org/public/file.txt'
122
+ @env["PATH_INFO"].should == "/public/file.txt"
123
+ @env["REQUEST_URI"].should be_nil
124
+ end
125
+ end
126
+
127
+ end
128
+ end
129
+
130
+ describe "#translate_paths" do
131
+ before do
132
+ @pdf = PDFKit::Middleware.new({})
133
+ @env = { 'REQUEST_URI' => 'http://example.com/document.pdf', 'rack.url_scheme' => 'http', 'HTTP_HOST' => 'example.com' }
134
+ end
135
+
136
+ it "should correctly parse relative url with single quotes" do
137
+ @body = %{<html><head><link href='/stylesheets/application.css' media='screen' rel='stylesheet' type='text/css' /></head><body><img alt='test' src="/test.png" /></body></html>}
138
+ body = @pdf.send :translate_paths, @body, @env
139
+ body.should == "<html><head><link href='http://example.com/stylesheets/application.css' media='screen' rel='stylesheet' type='text/css' /></head><body><img alt='test' src=\"http://example.com/test.png\" /></body></html>"
140
+ end
141
+
142
+ it "should correctly parse relative url with double quotes" do
143
+ @body = %{<link href="/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />}
144
+ body = @pdf.send :translate_paths, @body, @env
145
+ body.should == "<link href=\"http://example.com/stylesheets/application.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
146
+ end
147
+
148
+ it "should return the body even if there are no valid substitutions found" do
149
+ @body = "NO MATCH"
150
+ body = @pdf.send :translate_paths, @body, @env
151
+ body.should == "NO MATCH"
152
+ end
153
+ end
154
+
155
+ describe "#translate_paths with root_url configuration" do
156
+ before do
157
+ @pdf = PDFKit::Middleware.new({})
158
+ @env = { 'REQUEST_URI' => 'http://example.com/document.pdf', 'rack.url_scheme' => 'http', 'HTTP_HOST' => 'example.com' }
159
+ PDFKit.configure do |config|
160
+ config.root_url = "http://example.net/"
161
+ end
162
+ end
163
+
164
+ it "should add the root_url" do
165
+ @body = %{<html><head><link href='/stylesheets/application.css' media='screen' rel='stylesheet' type='text/css' /></head><body><img alt='test' src="/test.png" /></body></html>}
166
+ body = @pdf.send :translate_paths, @body, @env
167
+ body.should == "<html><head><link href='http://example.net/stylesheets/application.css' media='screen' rel='stylesheet' type='text/css' /></head><body><img alt='test' src=\"http://example.net/test.png\" /></body></html>"
168
+ end
169
+
170
+ after do
171
+ PDFKit.configure do |config|
172
+ config.root_url = nil
173
+ end
174
+ end
175
+ end
176
+
177
+ it "should not get stuck rendering each request as pdf" do
178
+ mock_app
179
+ # false by default. No requests.
180
+ @app.send(:rendering_pdf?).should be_false
181
+
182
+ # Remain false on a normal request
183
+ get 'http://www.example.org/public/file'
184
+ @app.send(:rendering_pdf?).should be_false
185
+
186
+ # Return true on a pdf request.
187
+ get 'http://www.example.org/public/file.pdf'
188
+ @app.send(:rendering_pdf?).should be_true
189
+
190
+ # Restore to false on any non-pdf request.
191
+ get 'http://www.example.org/public/file'
192
+ @app.send(:rendering_pdf?).should be_false
193
+ end
194
+
195
+ end
@@ -0,0 +1,227 @@
1
+ #encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe PDFKit do
5
+
6
+ context "initialization" do
7
+ it "should accept HTML as the source" do
8
+ pdfkit = PDFKit.new('<h1>Oh Hai</h1>')
9
+ pdfkit.source.should be_html
10
+ pdfkit.source.to_s.should == '<h1>Oh Hai</h1>'
11
+ end
12
+
13
+ it "should accept a URL as the source" do
14
+ pdfkit = PDFKit.new('http://google.com')
15
+ pdfkit.source.should be_url
16
+ pdfkit.source.to_s.should == 'http://google.com'
17
+ end
18
+
19
+ it "should accept a File as the source" do
20
+ file_path = File.join(SPEC_ROOT,'fixtures','example.html')
21
+ pdfkit = PDFKit.new(File.new(file_path))
22
+ pdfkit.source.should be_file
23
+ pdfkit.source.to_s.should == file_path
24
+ end
25
+
26
+ it "should parse the options into a cmd line friedly format" do
27
+ pdfkit = PDFKit.new('html', :page_size => 'Letter')
28
+ pdfkit.options.should have_key('--page-size')
29
+ end
30
+
31
+ it "should provide default options" do
32
+ pdfkit = PDFKit.new('<h1>Oh Hai</h1>')
33
+ ['--margin-top', '--margin-right', '--margin-bottom', '--margin-left'].each do |option|
34
+ pdfkit.options.should have_key(option)
35
+ end
36
+ end
37
+
38
+ it "should default to 'UTF-8' encoding" do
39
+ pdfkit = PDFKit.new('Captación')
40
+ pdfkit.options['--encoding'].should == 'UTF-8'
41
+ end
42
+
43
+ it "should not have any stylesheedt by default" do
44
+ pdfkit = PDFKit.new('<h1>Oh Hai</h1>')
45
+ pdfkit.stylesheets.should be_empty
46
+ end
47
+ end
48
+
49
+ context "command" do
50
+ it "should contstruct the correct command" do
51
+ pdfkit = PDFKit.new('html', :page_size => 'Letter', :toc_l1_font_size => 12)
52
+ pdfkit.command[0].should include('wkhtmltopdf')
53
+ pdfkit.command[pdfkit.command.index('"--page-size"') + 1].should == '"Letter"'
54
+ pdfkit.command[pdfkit.command.index('"--toc-l1-font-size"') + 1].should == '"12"'
55
+ end
56
+
57
+ it "will not include default options it is told to omit" do
58
+ PDFKit.configure do |config|
59
+ config.default_options[:disable_smart_shrinking] = true
60
+ end
61
+
62
+ pdfkit = PDFKit.new('html')
63
+ pdfkit.command.should include('"--disable-smart-shrinking"')
64
+ pdfkit = PDFKit.new('html', :disable_smart_shrinking => false)
65
+ pdfkit.command.should_not include('"--disable-smart-shrinking"')
66
+ end
67
+
68
+ it "should encapsulate string arguments in quotes" do
69
+ pdfkit = PDFKit.new('html', :header_center => "foo [page]")
70
+ pdfkit.command[pdfkit.command.index('"--header-center"') + 1].should == '"foo [page]"'
71
+ end
72
+
73
+ it "read the source from stdin if it is html" do
74
+ pdfkit = PDFKit.new('html')
75
+ pdfkit.command[-2..-1].should == ['"-"', '"-"']
76
+ end
77
+
78
+ it "specify the URL to the source if it is a url" do
79
+ pdfkit = PDFKit.new('http://google.com')
80
+ pdfkit.command[-2..-1].should == ['"http://google.com"', '"-"']
81
+ end
82
+
83
+ it "should specify the path to the source if it is a file" do
84
+ file_path = File.join(SPEC_ROOT,'fixtures','example.html')
85
+ pdfkit = PDFKit.new(File.new(file_path))
86
+ pdfkit.command[-2..-1].should == [%Q{"#{file_path}"}, '"-"']
87
+ end
88
+
89
+ it "should specify the path for the ouput if a apth is given" do
90
+ file_path = "/path/to/output.pdf"
91
+ pdfkit = PDFKit.new("html")
92
+ pdfkit.command(file_path).last.should == %Q{"#{file_path}"}
93
+ end
94
+
95
+ it "should detect special pdfkit meta tags" do
96
+ body = %{
97
+ <html>
98
+ <head>
99
+ <meta name="pdfkit-page_size" content="Legal"/>
100
+ <meta name="pdfkit-orientation" content="Landscape"/>
101
+ </head>
102
+ </html>
103
+ }
104
+ pdfkit = PDFKit.new(body)
105
+ pdfkit.command[pdfkit.command.index('"--page-size"') + 1].should == '"Legal"'
106
+ pdfkit.command[pdfkit.command.index('"--orientation"') + 1].should == '"Landscape"'
107
+ end
108
+
109
+ it "should detect special pdfkit meta tags despite bad markup" do
110
+ body = %{
111
+ <html>
112
+ <head>
113
+ <meta name="pdfkit-page_size" content="Legal"/>
114
+ <meta name="pdfkit-orientation" content="Landscape"/>
115
+ </head>
116
+ <br>
117
+ </html>
118
+ }
119
+ pdfkit = PDFKit.new(body)
120
+ pdfkit.command[pdfkit.command.index('"--page-size"') + 1].should == '"Legal"'
121
+ pdfkit.command[pdfkit.command.index('"--orientation"') + 1].should == '"Landscape"'
122
+ end
123
+
124
+ it "should skip non-pdfkit meta tags" do
125
+ body = %{
126
+ <html>
127
+ <head>
128
+ <meta name="test-page_size" content="Legal"/>
129
+ <meta name="pdfkit-orientation" content="Landscape"/>
130
+ </head>
131
+ <br>
132
+ </html>
133
+ }
134
+ pdfkit = PDFKit.new(body)
135
+ pdfkit.command[pdfkit.command.index('"--orientation"') + 1].should == '"Landscape"'
136
+ end
137
+
138
+ end
139
+
140
+ context "#to_pdf" do
141
+ it "should generate a PDF of the HTML" do
142
+ pdfkit = PDFKit.new('html', :page_size => 'Letter')
143
+ pdf = pdfkit.to_pdf
144
+ pdf[0...4].should == "%PDF" # PDF Signature at beginning of file
145
+ end
146
+
147
+ it "should generate a PDF with a numerical parameter" do
148
+ pdfkit = PDFKit.new('html', :header_spacing => 1)
149
+ pdf = pdfkit.to_pdf
150
+ pdf[0...4].should == "%PDF" # PDF Signature at beginning of file
151
+ end
152
+
153
+ it "should generate a PDF with a symbol parameter" do
154
+ pdfkit = PDFKit.new('html', :page_size => :Letter)
155
+ pdf = pdfkit.to_pdf
156
+ pdf[0...4].should == "%PDF" # PDF Signature at beginning of file
157
+ end
158
+
159
+ it "should have the stylesheet added to the head if it has one" do
160
+ pdfkit = PDFKit.new("<html><head></head><body>Hai!</body></html>")
161
+ css = File.join(SPEC_ROOT,'fixtures','example.css')
162
+ pdfkit.stylesheets << css
163
+ pdfkit.to_pdf
164
+ pdfkit.source.to_s.should include("<style>#{File.read(css)}</style>")
165
+ end
166
+
167
+ it "should prepend style tags if the HTML doesn't have a head tag" do
168
+ pdfkit = PDFKit.new("<html><body>Hai!</body></html>")
169
+ css = File.join(SPEC_ROOT,'fixtures','example.css')
170
+ pdfkit.stylesheets << css
171
+ pdfkit.to_pdf
172
+ pdfkit.source.to_s.should include("<style>#{File.read(css)}</style><html>")
173
+ end
174
+
175
+ it "should throw an error if the source is not html and stylesheets have been added" do
176
+ pdfkit = PDFKit.new('http://google.com')
177
+ css = File.join(SPEC_ROOT,'fixtures','example.css')
178
+ pdfkit.stylesheets << css
179
+ lambda { pdfkit.to_pdf }.should raise_error(PDFKit::ImproperSourceError)
180
+ end
181
+ end
182
+
183
+ context "#to_file" do
184
+ before do
185
+ @file_path = File.join(SPEC_ROOT,'fixtures','test.pdf')
186
+ File.delete(@file_path) if File.exist?(@file_path)
187
+ end
188
+
189
+ after do
190
+ File.delete(@file_path)
191
+ end
192
+
193
+ it "should create a file with the PDF as content" do
194
+ pdfkit = PDFKit.new('html', :page_size => 'Letter')
195
+ file = pdfkit.to_file(@file_path)
196
+ file.should be_instance_of(File)
197
+ File.read(file.path)[0...4].should == "%PDF" # PDF Signature at beginning of file
198
+ end
199
+
200
+ it "should not truncate data (in Ruby 1.8.6)" do
201
+ file_path = File.join(SPEC_ROOT,'fixtures','example.html')
202
+ pdfkit = PDFKit.new(File.new(file_path))
203
+ pdf_data = pdfkit.to_pdf
204
+ file = pdfkit.to_file(@file_path)
205
+ file_data = open(@file_path, 'rb') {|io| io.read }
206
+ pdf_data.size.should == file_data.size
207
+ end
208
+ end
209
+
210
+ context "security" do
211
+ before do
212
+ @test_path = File.join(SPEC_ROOT,'fixtures','security-oops')
213
+ File.delete(@test_path) if File.exist?(@test_path)
214
+ end
215
+
216
+ after do
217
+ File.delete(@test_path) if File.exist?(@test_path)
218
+ end
219
+
220
+ it "should not allow shell injection in options" do
221
+ pdfkit = PDFKit.new('html', :header_center => "a title\"; touch #{@test_path} #")
222
+ pdfkit.to_pdf
223
+ File.exist?(@test_path).should be_false
224
+ end
225
+ end
226
+
227
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe PDFKit::Source do
4
+
5
+ describe "#url?" do
6
+ it "should return true if passed a url like string" do
7
+ source = PDFKit::Source.new('http://google.com')
8
+ source.should be_url
9
+ end
10
+
11
+ it "should return false if passed a file" do
12
+ source = PDFKit::Source.new(File.new(__FILE__))
13
+ source.should_not be_url
14
+ end
15
+
16
+ it "should return false if passed HTML" do
17
+ source = PDFKit::Source.new('<blink>Oh Hai!</blink>')
18
+ source.should_not be_url
19
+ end
20
+
21
+ it "should return false if passed HTML with embedded urls at the beginning of a line" do
22
+ source = PDFKit::Source.new("<blink>Oh Hai!</blink>\nhttp://www.google.com")
23
+ source.should_not be_url
24
+ end
25
+ end
26
+
27
+ describe "#file?" do
28
+ it "should return true if passed a file" do
29
+ source = PDFKit::Source.new(File.new(__FILE__))
30
+ source.should be_file
31
+ end
32
+
33
+ it "should return false if passed a url like string" do
34
+ source = PDFKit::Source.new('http://google.com')
35
+ source.should_not be_file
36
+ end
37
+
38
+ it "should return false if passed HTML" do
39
+ source = PDFKit::Source.new('<blink>Oh Hai!</blink>')
40
+ source.should_not be_file
41
+ end
42
+ end
43
+
44
+ describe "#html?" do
45
+ it "should return true if passed HTML" do
46
+ source = PDFKit::Source.new('<blink>Oh Hai!</blink>')
47
+ source.should be_html
48
+ end
49
+
50
+ it "should return false if passed a file" do
51
+ source = PDFKit::Source.new(File.new(__FILE__))
52
+ source.should_not be_html
53
+ end
54
+
55
+ it "should return false if passed a url like string" do
56
+ source = PDFKit::Source.new('http://google.com')
57
+ source.should_not be_html
58
+ end
59
+ end
60
+
61
+ describe "#to_s" do
62
+ it "should return the HTML if passed HTML" do
63
+ source = PDFKit::Source.new('<blink>Oh Hai!</blink>')
64
+ source.to_s.should == '<blink>Oh Hai!</blink>'
65
+ end
66
+
67
+ it "should return a path if passed a file" do
68
+ source = PDFKit::Source.new(File.new(__FILE__))
69
+ source.to_s.should == __FILE__
70
+ end
71
+
72
+ it "should return the url if passed a url like string" do
73
+ source = PDFKit::Source.new('http://google.com')
74
+ source.to_s.should == 'http://google.com'
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,14 @@
1
+ SPEC_ROOT = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(SPEC_ROOT)
3
+ $LOAD_PATH.unshift(File.join(SPEC_ROOT, '..', 'lib'))
4
+ require 'pdfkit'
5
+ require 'rspec'
6
+ require 'rspec/autorun'
7
+ require 'mocha'
8
+ require 'rack'
9
+ require 'rack/test'
10
+ require 'custom_wkhtmltopdf_path' if File.exists?(File.join(SPEC_ROOT, 'custom_wkhtmltopdf_path.rb'))
11
+
12
+ RSpec.configure do |config|
13
+ include Rack::Test::Methods
14
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdfkit-middleware-to-file
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-05-15 00:00:00.000000000 Z
14
+ date: 2013-05-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rspec
@@ -67,7 +67,32 @@ email:
67
67
  executables: []
68
68
  extensions: []
69
69
  extra_rdoc_files: []
70
- files: []
70
+ files:
71
+ - .document
72
+ - .gitignore
73
+ - .rspec
74
+ - Gemfile
75
+ - Gemfile.lock
76
+ - History.md
77
+ - LICENSE
78
+ - POST_INSTALL
79
+ - README.md
80
+ - Rakefile
81
+ - lib/pdfkit.rb
82
+ - lib/pdfkit/configuration.rb
83
+ - lib/pdfkit/middleware.rb
84
+ - lib/pdfkit/pdfkit.rb
85
+ - lib/pdfkit/source.rb
86
+ - lib/pdfkit/version.rb
87
+ - pdfkit-middleware-to-file-0.5.1.gem
88
+ - pdfkit-middleware-to-file-0.5.2.gem
89
+ - pdfkit.gemspec
90
+ - spec/fixtures/example.css
91
+ - spec/fixtures/example.html
92
+ - spec/middleware_spec.rb
93
+ - spec/pdfkit_spec.rb
94
+ - spec/source_spec.rb
95
+ - spec/spec_helper.rb
71
96
  homepage:
72
97
  licenses: []
73
98
  post_install_message:
@@ -92,4 +117,10 @@ rubygems_version: 1.8.25
92
117
  signing_key:
93
118
  specification_version: 3
94
119
  summary: HTML+CSS -> PDF
95
- test_files: []
120
+ test_files:
121
+ - spec/fixtures/example.css
122
+ - spec/fixtures/example.html
123
+ - spec/middleware_spec.rb
124
+ - spec/pdfkit_spec.rb
125
+ - spec/source_spec.rb
126
+ - spec/spec_helper.rb