Skip to content

Commit

Permalink
Merge https://github.com/rymai/PDFKit into rymai-master
Browse files Browse the repository at this point in the history
Conflicts:
	lib/pdfkit/configuration.rb
	lib/pdfkit/middleware.rb
	spec/middleware_spec.rb
  • Loading branch information
jdpace committed Dec 27, 2010
2 parents 94efcef + 87addbd commit 0eace29
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 86 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ pkg

## PROJECT::SPECIFIC
.bundle
spec/custom_wkhtmltopdf_path.rb
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ group :development do
gem "mocha"
gem "jeweler"
gem "rack"
gem "rack-test"
end
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ GEM
mocha (0.9.9)
rake
rack (1.2.1)
rack-test (0.5.6)
rack (>= 1.0)
rake (0.8.7)
rspec (2.0.1)
rspec-core (~> 2.0.1)
Expand All @@ -33,5 +35,6 @@ DEPENDENCIES
jeweler
mocha
rack
rack-test
rspec (~> 2.0.0.beta.8)
rspec-core (~> 2.0.0.beta.8)
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ PDFKit comes with a middleware that allows users to get a PDF view of any page o
# options will be passed to PDFKit.new
config.middleware.use PDFKit::Middleware, :print_media_type => true

**With conditions to limit routes that can be generated in pdf**

# conditions can be regexes (either one or an array)
config.middleware.use PDFKit::Middleware, {}, :only => %r[^/public]
config.middleware.use PDFKit::Middleware, {}, :only => [%r[^/invoice], %r[^/public]]

# conditions can be strings (either one or an array)
config.middleware.use PDFKit::Middleware, {}, :only => '/public'
config.middleware.use PDFKit::Middleware, {}, :only => ['/invoice', '/public']

## TODO
- add amd64 support in --install-wkhtmltopdf

Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ begin
gem.add_development_dependency "rspec", "~> 2.0.0.beta.8"
gem.add_development_dependency "rspec-core", "~> 2.0.0.beta.8"
gem.add_development_dependency 'mocha'
gem.add_development_dependency 'rack-test'
gem.post_install_message = File.read('POST_INSTALL')
end
Jeweler::GemcutterTasks.new
Expand Down
6 changes: 2 additions & 4 deletions lib/pdfkit/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,12 @@ class << self
# PDFKit.configure do |config|
# config.wkhtmltopdf = '/usr/bin/wkhtmltopdf'
# end

def self.configuration
@configuration ||= Configuration.new
end



def self.configure
self.configuration
yield(configuration)
end
end
110 changes: 62 additions & 48 deletions lib/pdfkit/middleware.rb
Original file line number Diff line number Diff line change
@@ -1,63 +1,77 @@
class PDFKit

class Middleware

def initialize(app, options = {})
@app = app
@options = options

def initialize(app, options = {}, conditions = {})
@app = app
@options = options
@conditions = conditions
end

def call(env)
@render_pdf = false
set_request_to_render_as_pdf(env) if env['PATH_INFO'].match(/\.pdf$/)

@request = Rack::Request.new(env)

set_request_to_render_as_pdf(env) if render_as_pdf?
status, headers, response = @app.call(env)

request = Rack::Request.new(env)
if @render_pdf && headers['Content-Type'] =~ /text\/html|application\/xhtml\+xml/
body = response.body

body = translate_paths(body, env)

pdf = PDFKit.new(body, @options)
body = pdf.to_pdf


if rendering_pdf? && headers['Content-Type'] =~ /text\/html|application\/xhtml\+xml/
body = response.respond_to?(:body) ? response.body : response.join
body = PDFKit.new(translate_paths(body, env), @options).to_pdf
response = [body]

# Do not cache PDFs
headers.delete('ETag')
headers.delete('Cache-Control')

headers["Content-Length"] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
headers["Content-Type"] = "application/pdf"
headers.store("Middleware", "PDFKit")

response = [body]

headers["Content-Length"] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
headers["Content-Type"] = "application/pdf"
headers["Rack-Middleware-PDFKit"] = "true"
end

[status, headers, response]
end

private

# Change relative paths to absolute
def translate_paths(body, env)
# Host with protocol
root = env['rack.url_scheme'] + "://" + env['HTTP_HOST'] + "/"

body.gsub(/(href|src)=(['"])\/([^\"']*|[^"']*)['"]/,'\1=\2'+root+'\3\2')
end

def set_request_to_render_as_pdf(env)
@render_pdf = true

path = Pathname(env['PATH_INFO'])
['PATH_INFO','REQUEST_URI'].each { |e| env[e] = path.to_s.sub(/#{path.extname}$/,'') } if path.extname == '.pdf'
env['HTTP_ACCEPT'] = concat(env['HTTP_ACCEPT'], Rack::Mime.mime_type('.html'))
env['MIDDLEWARE'] = "PDFKit"
end

def concat(accepts, type)
(accepts || '').split(',').unshift(type).compact.join(',')

# Change relative paths to absolute
def translate_paths(body, env)
# Host with protocol
root = "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/"

body.gsub(/(href|src)=(['"])\/([^\"']*|[^"']*)['"]/, '\1=\2' + root + '\3\2')
end

def rendering_pdf?
@render_pdf
end

def render_as_pdf?
request_path_is_pdf = @request.path.match(%r{\.pdf$})

if request_path_is_pdf && @conditions[:only]
rules = [@conditions[:only]].flatten
rules.any? do |pattern|
if pattern.is_a?(Regexp)
@request.path =~ pattern
else
@request.path[0, pattern.length] == pattern
end
end
else
request_path_is_pdf
end

end

def set_request_to_render_as_pdf(env)
@render_pdf = true
path = @request.path.sub(%r{\.pdf$}, '')
%w[PATH_INFO REQUEST_URI].each { |e| env[e] = path }
env['HTTP_ACCEPT'] = concat(env['HTTP_ACCEPT'], Rack::Mime.mime_type('.html'))
end

def concat(accepts, type)
(accepts || '').split(',').unshift(type).compact.join(',')
end

end
end
153 changes: 127 additions & 26 deletions spec/middleware_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,136 @@
require 'spec_helper'

def app; Rack::Lint.new(@app); end

def mock_app(options = {}, conditions = {})
main_app = lambda { |env|
@env = env
headers = {'Content-Type' => "text/html"}
[200, headers, @body || ['Hello world!']]
}

builder = Rack::Builder.new
builder.use PDFKit::Middleware, options, conditions
builder.run main_app
@app = builder.to_app
end

describe PDFKit::Middleware do

describe "#call" do
describe "conditions" do
describe ":only" do

describe "regex" do
describe "one" do
before { mock_app({}, :only => %r[^/public]) }

context "matching" do
specify do
get 'http://www.example.org/public/test.pdf'
last_response.headers["Content-Type"].should == "application/pdf"
last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
end
end

context "not matching" do
specify do
get 'http://www.example.org/secret/test.pdf'
last_response.headers["Content-Type"].should == "text/html"
last_response.body.should == "Hello world!"
end
end
end # one regex

describe "multiple" do
before { mock_app({}, :only => [%r[^/invoice], %r[^/public]]) }

context "matching" do
specify do
get 'http://www.example.org/public/test.pdf'
last_response.headers["Content-Type"].should == "application/pdf"
last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
end
end

context "not matching" do
specify do
get 'http://www.example.org/secret/test.pdf'
last_response.headers["Content-Type"].should == "text/html"
last_response.body.should == "Hello world!"
end
end
end # multiple regex
end # regex

describe "string" do
describe "one" do
before { mock_app({}, :only => '/public') }

context "matching" do
specify do
get 'http://www.example.org/public/test.pdf'
last_response.headers["Content-Type"].should == "application/pdf"
last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
end
end

context "not matching" do
specify do
get 'http://www.example.org/secret/test.pdf'
last_response.headers["Content-Type"].should == "text/html"
last_response.body.should == "Hello world!"
end
end
end # one string

describe "multiple" do
before { mock_app({}, :only => ['/invoice', '/public']) }

context "matching" do
specify do
get 'http://www.example.org/public/test.pdf'
last_response.headers["Content-Type"].should == "application/pdf"
last_response.body.bytesize.should == PDFKit.new("Hello world!").to_pdf.bytesize
end
end

context "not matching" do
specify do
get 'http://www.example.org/secret/test.pdf'
last_response.headers["Content-Type"].should == "text/html"
last_response.body.should == "Hello world!"
end
end
end # multiple string
end # string

end
end

describe "remove .pdf from PATH_INFO and REQUEST_URI" do
before { mock_app }

context "matching" do
specify do
get 'http://www.example.org/public/file.pdf'
@env["PATH_INFO"].should == "/public/file"
@env["REQUEST_URI"].should == "/public/file"
end
specify do
get 'http://www.example.org/public/file.txt'
@env["PATH_INFO"].should == "/public/file.txt"
@env["REQUEST_URI"].should be_nil
end
end

end
end

describe "#translate_paths" do

before do
@pdf = PDFKit::Middleware.new({})
@env = {'REQUEST_URI' => 'http://example.com/document.pdf', 'rack.url_scheme' => 'http', 'HTTP_HOST' => 'example.com'}
@env = { 'REQUEST_URI' => 'http://example.com/document.pdf', 'rack.url_scheme' => 'http', 'HTTP_HOST' => 'example.com' }
end

it "should correctly parse relative url with single quotes" do
Expand All @@ -26,29 +151,5 @@
body.should == "NO MATCH"
end
end

describe "#set_request_to_render_as_pdf" do

before do
@pdf = PDFKit::Middleware.new({})

@pdf_env = {'PATH_INFO' => Pathname.new("file.pdf"), 'REQUEST_URI' => Pathname.new("file.pdf")}
@non_pdf_env = {'PATH_INFO' => Pathname.new("file.txt"), 'REQUEST_URI' => Pathname.new("file.txt")}
end

it "should replace .pdf in PATH_INFO when the extname is .pdf" do
@pdf.send :set_request_to_render_as_pdf, @pdf_env
@pdf_env['PATH_INFO'].should == "file"
end

it "should replace .pdf in REQUEST_URI when the extname is .pdf" do
@pdf.send :set_request_to_render_as_pdf, @pdf_env
@pdf_env['REQUEST_URI'].should == "file"
end

it "should inform RAILS of itself by setting a request header" do
@pdf.send :set_request_to_render_as_pdf, @pdf_env
@pdf_env['MIDDLEWARE'].should == "PDFKit"
end
end
end
6 changes: 5 additions & 1 deletion spec/pdfkit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

it "should provide default options" do
pdfkit = PDFKit.new('<h1>Oh Hai</h1>')
['--disable-smart-shrinking', '--margin-top', '--margin-right', '--margin-bottom', '--margin-left'].each do |option|
['--margin-top', '--margin-right', '--margin-bottom', '--margin-left'].each do |option|
pdfkit.options.should have_key(option)
end
end
Expand All @@ -55,6 +55,10 @@
end

it "will not include default options it is told to omit" do
PDFKit.configure do |config|
config.default_options[:disable_smart_shrinking] = true
end

pdfkit = PDFKit.new('html')
pdfkit.command.should include('--disable-smart-shrinking')
pdfkit = PDFKit.new('html', :disable_smart_shrinking => false)
Expand Down
Loading

0 comments on commit 0eace29

Please sign in to comment.