├── VERSION ├── init.rb ├── .document ├── lib ├── ezprint │ ├── processors │ │ ├── base.rb │ │ ├── pdfkit.rb │ │ └── prince.rb │ ├── railtie.rb │ └── pdf_helper.rb └── ezprint.rb ├── .gitignore ├── MIT-LICENSE ├── Rakefile ├── ezprint.gemspec ├── README └── README.rdoc /VERSION: -------------------------------------------------------------------------------- 1 | 0.3.1 -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(__FILE__), "lib", "ezprint") 2 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | README.rdoc 2 | lib/*.rb 3 | lib/**/*.rb 4 | bin/* 5 | features/**/*.feature 6 | LICENSE 7 | -------------------------------------------------------------------------------- /lib/ezprint/processors/base.rb: -------------------------------------------------------------------------------- 1 | module Ezprint 2 | module Processors 3 | class Base 4 | def self.process(html_string, options = {}) 5 | raise RuntimeError.new "Process is not implemented" 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 | *.swo 16 | 17 | ## PROJECT::GENERAL 18 | coverage 19 | rdoc 20 | pkg 21 | 22 | ## PROJECT::SPECIFIC 23 | -------------------------------------------------------------------------------- /lib/ezprint/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'action_controller/base' 2 | require 'action_dispatch/http/mime_types' 3 | 4 | module Ezprint 5 | class Railtie < Rails::Railtie 6 | 7 | initializer :init_mime_types do 8 | Mime::Type.register 'application/pdf', :pdf 9 | end 10 | 11 | initializer :insert_into_action_controller do 12 | ActiveSupport.on_load :action_controller do 13 | ActionController::Base.send(:include, Ezprint::PdfHelper) 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/ezprint/processors/pdfkit.rb: -------------------------------------------------------------------------------- 1 | require 'pdfkit' 2 | 3 | module Ezprint 4 | module Processors 5 | class Pdfkit < Base 6 | def self.process(html_string, options = {}) 7 | stylesheets = options.delete(:stylesheets) 8 | kit = PDFKit.new(self.process_html(html_string), options) 9 | kit.stylesheets = stylesheets 10 | kit.to_pdf 11 | end 12 | 13 | def self.process_html(html) 14 | # reroute absolute paths 15 | html.gsub!("src=\"/", "src=\"#{Rails.public_path}/") 16 | html.gsub!("href=\"/", "src=\"#{Rails.public_path}/") 17 | html.gsub!("url(/", "url(#{Rails.public_path}/") 18 | html 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/ezprint.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/inflector' 2 | 3 | module Ezprint 4 | autoload :PdfHelper, 'ezprint/pdf_helper' 5 | 6 | module Processors 7 | autoload :Base, 'ezprint/processors/base' 8 | autoload :Pdfkit, 'ezprint/processors/pdfkit' 9 | autoload :Prince, 'ezprint/processors/prince' 10 | end 11 | 12 | @@processor = :pdfkit 13 | mattr_accessor :processor 14 | 15 | def self.get_processor 16 | ::Ezprint::Processors.const_get(Ezprint.processor.to_s.classify) 17 | end 18 | end 19 | 20 | if defined?(::Rails::Railtie) 21 | require 'ezprint/railtie' 22 | else 23 | # Rails 2.x 24 | Mime::Type.register 'application/pdf', :pdf 25 | ActionController::Base.send(:include, Ezprint::PdfHelper) 26 | end 27 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Jason Stewart 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. 21 | -------------------------------------------------------------------------------- /lib/ezprint/processors/prince.rb: -------------------------------------------------------------------------------- 1 | module Ezprint 2 | module Processors 3 | class Prince < Base 4 | def self.process(html_string, options = {}) 5 | pdf = IO.popen(self.cmd(options), "w+") 6 | pdf.puts(self.process_html(html_string)) 7 | pdf.close_write 8 | result = pdf.gets(nil) 9 | pdf.close_read 10 | result 11 | end 12 | 13 | def self.cmd(options) 14 | stylesheets = options.delete(:stylesheets) 15 | prince_cmd = `which prince`.chomp 16 | 17 | if prince_cmd.length == 0 18 | raise RuntimeError.new "Cannot locate prince binary. Please check your path" 19 | end 20 | 21 | prince_cmd << " --input=html --server " 22 | stylesheets.each { |s| prince_cmd << " -s #{s} " } 23 | prince_cmd << " --silent - -o -" 24 | end 25 | 26 | def self.process_html(html) 27 | # reroute absolute paths 28 | html.gsub!("src=\"/", "src=\"#{Rails.public_path}/") 29 | html.gsub!("href=\"/", "src=\"#{Rails.public_path}/") 30 | html.gsub!("url(/", "url(#{Rails.public_path}/") 31 | html 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | 4 | begin 5 | require 'jeweler' 6 | Jeweler::Tasks.new do |gem| 7 | gem.name = "ezprint" 8 | gem.summary = %Q{A Rails wrapper for the PDFkit library. Meant to be a drop in replacement for princely.} 9 | gem.description = %Q{A Rails wrapper for the PDFkit library. Meant to be a drop in replacement for princely.} 10 | gem.email = "jstewart@fusionary.com" 11 | gem.homepage = "http://github.com/jstewart/ezprint" 12 | gem.authors = ["Jason Stewart"] 13 | gem.add_dependency 'pdfkit', '~> 0.5.0' 14 | end 15 | 16 | rescue LoadError 17 | puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" 18 | end 19 | 20 | require 'rake/testtask' 21 | Rake::TestTask.new(:test) do |test| 22 | test.libs << 'lib' << 'test' 23 | test.pattern = 'test/**/*_test.rb' 24 | test.verbose = true 25 | end 26 | 27 | task :default => :test 28 | 29 | require 'rake/rdoctask' 30 | Rake::RDocTask.new do |rdoc| 31 | if File.exist?('VERSION') 32 | version = File.read('VERSION') 33 | else 34 | version = "" 35 | end 36 | 37 | rdoc.rdoc_dir = 'rdoc' 38 | rdoc.title = "ezprint #{version}" 39 | rdoc.options << '--line-numbers' << '--inline-source' 40 | rdoc.rdoc_files.include('README*') 41 | rdoc.rdoc_files.include('lib/**/*.rb') 42 | end 43 | -------------------------------------------------------------------------------- /ezprint.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{ezprint} 8 | s.version = "0.3.1" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Jason Stewart"] 12 | s.date = %q{2011-05-16} 13 | s.description = %q{A Rails wrapper for the PDFkit library. Meant to be a drop in replacement for princely.} 14 | s.email = %q{jstewart@fusionary.com} 15 | s.extra_rdoc_files = [ 16 | "README", 17 | "README.rdoc" 18 | ] 19 | s.files = [ 20 | ".document", 21 | "MIT-LICENSE", 22 | "README", 23 | "README.rdoc", 24 | "Rakefile", 25 | "VERSION", 26 | "ezprint.gemspec", 27 | "init.rb", 28 | "lib/ezprint.rb", 29 | "lib/ezprint/pdf_helper.rb", 30 | "lib/ezprint/processors/base.rb", 31 | "lib/ezprint/processors/pdfkit.rb", 32 | "lib/ezprint/processors/prince.rb", 33 | "lib/ezprint/railtie.rb" 34 | ] 35 | s.homepage = %q{http://github.com/jstewart/ezprint} 36 | s.require_paths = ["lib"] 37 | s.rubygems_version = %q{1.6.1} 38 | s.summary = %q{A Rails wrapper for the PDFkit library. Meant to be a drop in replacement for princely.} 39 | 40 | if s.respond_to? :specification_version then 41 | s.specification_version = 3 42 | 43 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 44 | s.add_runtime_dependency(%q, ["~> 0.5.0"]) 45 | else 46 | s.add_dependency(%q, ["~> 0.5.0"]) 47 | end 48 | else 49 | s.add_dependency(%q, ["~> 0.5.0"]) 50 | end 51 | end 52 | 53 | -------------------------------------------------------------------------------- /lib/ezprint/pdf_helper.rb: -------------------------------------------------------------------------------- 1 | module Ezprint 2 | module PdfHelper 3 | def self.included(base) 4 | base.class_eval do 5 | alias_method_chain :render, :ezprint 6 | end 7 | end 8 | 9 | def render_with_ezprint(options = nil, *args, &block) 10 | if options.is_a?(Symbol) or options.nil? or options[:pdf].nil? 11 | render_without_ezprint(options, *args, &block) 12 | else 13 | options[:name] ||= options.delete(:pdf) 14 | make_and_send_pdf(options.delete(:name), options) 15 | end 16 | end 17 | 18 | private 19 | 20 | def make_pdf(options = {}) 21 | template = options.delete(:template) || File.join(controller_path,action_name) 22 | layout = options.delete(:layout) || false 23 | stylesheets = options[:stylesheets] || [] 24 | options[:stylesheets] = stylesheets.map { |s| stylesheet_file_path(s) } 25 | 26 | ENV["RAILS_ASSET_ID"] = '' # Stop Rails from appending timestamps to assets 27 | html_string = render_to_string(:template => template, :layout => layout) 28 | Ezprint.get_processor.send(:process, html_string, options) 29 | end 30 | 31 | def make_and_send_pdf(pdf_name, options = {}) 32 | filename = "#{pdf_name}.pdf" 33 | if request.headers['User-Agent'] =~ /MSIE ([0-9]{1,}[\.0-9]{0,})/ 34 | response.headers['Content-Disposition'] = "attachment;filename=\"#{filename}.pdf\"" 35 | response.headers['Content-Description'] = 'File Transfer' 36 | response.headers['Content-Transfer-Encoding'] = 'binary' 37 | response.headers['Expires'] = '0' 38 | response.headers['Pragma'] = 'public' 39 | end 40 | 41 | send_data( 42 | make_pdf(options), 43 | :filename => filename, 44 | :type => 'application/pdf' 45 | ) 46 | end 47 | 48 | def stylesheet_file_path(stylesheet) 49 | stylesheet = stylesheet.to_s.gsub(".css","") 50 | 51 | stylesheets_dir = if defined? ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR 52 | ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR 53 | else 54 | config.stylesheets_dir 55 | end 56 | File.join(stylesheets_dir, "#{stylesheet}.css") 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Ezprint 2 | ======= 3 | 4 | Ezprint is a drop in replacement for the princely plugin. It uses PDFKit 5 | as the backend instead of princexml, possibly saving you millions of 6 | dollars. I recommend using the Rack middleware component of PDFKit to 7 | print PDFs in rails, but this plugin makes an easy transition from 8 | prince->PDFKit for those using princely. 9 | 10 | Installation 11 | ============ 12 | 13 | Rails 2.x 14 | 15 | gem install ezprint 16 | in environment.rb config.gem "ezprint" 17 | 18 | Rails 3 19 | 20 | gem 'ezprint' 21 | then run "bundle install" 22 | 23 | Example 24 | ======= 25 | 26 | The examples here are similar to princely, since the 27 | plugin is basically a reworking of the princely source 28 | 29 | class PDFExample < ApplicationController 30 | def show 31 | respond_to do |format| 32 | format.html 33 | format.pdf do 34 | render :pdf => "My Awesome PDF", 35 | :template => "controller/action.pdf.erb", 36 | :stylesheets => ["application","print"] 37 | :layout => "pdf" 38 | end 39 | end 40 | end 41 | 42 | # Alternatively, you can use make_and_send_pdf to 43 | # render out a PDF for the action without a 44 | # respond_to block. 45 | def pdf 46 | make_and_send_pdf("file_name") 47 | end 48 | end 49 | 50 | Render Defaults 51 | =============== 52 | 53 | The defaults for the render options are as follows: 54 | 55 | layout: false 56 | template: the template for the current controller/action 57 | stylesheets: none 58 | 59 | 60 | Using another PDF processor 61 | =========================== 62 | 63 | While ezprint has been designed with PDFKit in mind, other PDF processors 64 | such as princexml can be used. A princexml processor is included and can be used 65 | by setting Ezprint.processor = :prince in an initializer. 66 | 67 | It's also easy to create your own processor: 68 | 69 | module Ezprint 70 | module Processors 71 | class MyProcessor < Base 72 | def self.process(html_string, options = {}) 73 | # Code to return a PDF string from an html string here. 74 | end 75 | end 76 | end 77 | end 78 | 79 | Possible Gotchas 80 | ================ 81 | 82 | If you're getting a "Broken Pipe" error when rendering PDF documents, you may need to 83 | install the wkhtmltopdf-binary gem or put it in your Gemfile. 84 | 85 | 86 | Credits 87 | ======= 88 | 89 | Michael Bleigh for writing the awesome princely plugin, which most of the code is reworked from. 90 | 91 | Resources 92 | ========= 93 | 94 | Copyright (c) 2010 Jason Stewart, released under the MIT license. 95 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Ezprint 2 | 3 | Ezprint is a drop in replacement for the princely plugin. It uses PDFKit 4 | as the backend instead of princexml, possibly saving you millions of 5 | dollars. I recommend using the Rack middleware component of PDFKit to 6 | print PDFs in rails, but this plugin makes an easy transition from 7 | prince->PDFKit for those using princely. 8 | 9 | == Installation 10 | 11 | == Rails 2.x 12 | 13 | gem install ezprint 14 | 15 | in environment.rb config.gem "ezprint" 16 | 17 | == Rails 3 18 | 19 | gem 'ezprint' 20 | 21 | then run "bundle install" 22 | 23 | == Example 24 | 25 | The examples here are similar to princely, since the 26 | plugin is basically a reworking of the princely source 27 | 28 | class PDFExample < ApplicationController 29 | def show 30 | respond_to do |format| 31 | format.html 32 | format.pdf do 33 | render :pdf => "My Awesome PDF", 34 | :template => "controller/action.pdf.erb", 35 | :stylesheets => ["application","print"] 36 | :layout => "pdf" 37 | end 38 | end 39 | end 40 | 41 | # Alternatively, you can use make_and_send_pdf to 42 | # render out a PDF for the action without a 43 | # respond_to block. 44 | def pdf 45 | make_and_send_pdf("file_name") 46 | end 47 | end 48 | 49 | == Render Defaults 50 | 51 | The defaults for the render options are as follows: 52 | 53 | layout: false 54 | template: the template for the current controller/action 55 | stylesheets: none 56 | 57 | 58 | == Using another PDF processor 59 | 60 | While ezprint has been designed with PDFKit in mind, other PDF processors 61 | such as princexml can be used. A princexml processor is included and can be used 62 | by setting Ezprint.processor = :prince in an initializer. 63 | 64 | It's also easy to create your own processor: 65 | 66 | module Ezprint 67 | module Processors 68 | class MyProcessor < Base 69 | def self.process(html_string, options = {}) 70 | # Code to return a PDF string from an html string here. 71 | end 72 | end 73 | end 74 | end 75 | 76 | == Possible Gotchas 77 | 78 | If you're getting a "Broken Pipe" error when rendering PDF documents, you may need to 79 | install the wkhtmltopdf-binary gem or put it in your Gemfile. 80 | 81 | 82 | == Credits 83 | 84 | * Michael Bleigh for writing the awesome princely plugin, which most of the code is reworked from. 85 | 86 | == Resources 87 | 88 | * Copyright (c) 2010 Jason Stewart, released under the MIT license. 89 | --------------------------------------------------------------------------------