zpdf 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fee29fbf4f292dfd6cb941b4e43828f3d3b51c2b
4
+ data.tar.gz: 808775939cf8169b55e3f6bdb07e142013cf2d07
5
+ SHA512:
6
+ metadata.gz: db3a7d94cb6aaab4245e79ae722fad5f3dcfb0cfad67549ee77755fbe828934a99acff277770ec4794539b63c096f9664d1df8b56c6311b0a2e991475b49b925
7
+ data.tar.gz: d94ee994533ad5c914ca920d6f0bf5c56fed947fbb6358d9fe78f570baac9f45677905c39bba648b2348dbdc5185adefd33aadb5974475dca63e33aa4b217d12
data/lib/z_pdf/base.rb ADDED
@@ -0,0 +1,132 @@
1
+ require 'z_pdf/html_pdf_object'
2
+ require 'z_pdf/z_pdf_helper'
3
+
4
+ module ZPdf #:nodoc:
5
+ #
6
+ # Example:
7
+ #
8
+ # class PdfInvoice < ZPdf::Base
9
+ #
10
+ # def invoice(client)
11
+ # @client = client
12
+ # generate( :pdf_params => { 'page-size' => 'Letter', 'margin-top' => 4, 'margin-bottom' => 9.5, 'outline' => true } )
13
+ # end
14
+ #
15
+ #
16
+ class RenderError < StandardError
17
+ end
18
+
19
+ class MissingTemplate < RenderError
20
+ def initialize(generator_class,view_paths,part_name,templates_path,template_name)
21
+ paths = view_paths.compact.map{ |p| p.to_s.inspect }.join(", ")
22
+ super("Missing #{part_name} template \"#{template_name}\" in \"#{templates_path}\" for #{generator_class.name} with view_paths: #{paths}")
23
+ end
24
+ end
25
+
26
+ class Base < AbstractController::Base
27
+ abstract!
28
+
29
+ include AbstractController::Rendering
30
+ include AbstractController::Layouts
31
+ include AbstractController::Helpers
32
+ include AbstractController::Translation
33
+ include AbstractController::AssetPaths
34
+ include AbstractController::Logger
35
+
36
+ helper ZPdf::PdfHelper
37
+
38
+ private_class_method :new #:nodoc:
39
+
40
+ class_attribute :default_params
41
+ self.default_params = {
42
+ :header_template => nil,
43
+ :footer_template => nil
44
+ }.freeze
45
+
46
+ class_attribute :default_pdf_params
47
+ self.default_pdf_params = { 'outline' => true }.freeze
48
+
49
+ config_accessor :pdf_views_path, :wkhtmltopdf_path
50
+
51
+ class << self
52
+
53
+ def generator_name
54
+ @generator_name ||= name.underscore
55
+ end
56
+ attr_writer :generator_name
57
+ alias :controller_path :generator_name
58
+
59
+ def default(value = nil)
60
+ self.default_params = default_params.merge(value).freeze if value
61
+ default_params
62
+ end
63
+
64
+ def default_pdf(value = nil)
65
+ self.default_pdf_params = default_pdf_params.merge(value).freeze if value
66
+ default_pdf_params
67
+ end
68
+
69
+ def respond_to?(method, *args)
70
+ super || action_methods.include?(method.to_s)
71
+ end
72
+
73
+ protected
74
+
75
+ def method_missing(method, *args) #:nodoc:
76
+ if action_methods.include?(method.to_s)
77
+ new(method, *args).html_pdf
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ end # class << self
84
+
85
+ attr_internal :html_pdf
86
+
87
+ def initialize(method_name, *args)
88
+ super()
89
+ process(method_name, *args)
90
+ end
91
+
92
+ def process(*args) #:nodoc:
93
+ lookup_context.skip_default_locale!
94
+ super
95
+ end
96
+
97
+ def generate(parameters={})
98
+ render_html(parameters)
99
+ end
100
+
101
+ protected
102
+
103
+ def render_html(parameters={})
104
+ params = parameters.reverse_merge(self.class.default)
105
+ html_parts = render_html_parts(params)
106
+ pdf_params = (parameters[:pdf_params] || {}).reverse_merge(self.class.default_pdf)
107
+ @_html_pdf = HtmlPdfObject.new(html_parts,pdf_params)
108
+ end
109
+
110
+ def render_html_parts(params) #:nodoc:
111
+ parts = {}
112
+
113
+ # TODO : support inline content
114
+ templates_path = params.delete(:template_path) || self.class.generator_name
115
+ template_names = { :content => params.delete(:template_name) || action_name,
116
+ :header => params.delete(:header_template),
117
+ :footer => params.delete(:footer_template) }
118
+ template_names.each_pair do |part_name,template_name|
119
+ unless template_name.nil?
120
+ if template = lookup_context.find_all(template_name,templates_path).first
121
+ parts[part_name] = render(:template => template)
122
+ else
123
+ raise MissingTemplate.new(self.class,lookup_context.view_paths,part_name,templates_path,template_name)
124
+ end
125
+ end
126
+ end
127
+ parts
128
+ end
129
+
130
+ ActiveSupport.run_load_hooks(:zpdf, self)
131
+ end
132
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Creates a module named PdfControllerMethods in lib/pdf_controller_module.rb
3
+
4
+ The module can then be included in controller classes to provide methods
5
+ to send PDF content.
6
+
7
+ Example:
8
+ `rails generate z_pdf:controller_module`
9
+
@@ -0,0 +1,13 @@
1
+ require 'rails/generators/base'
2
+
3
+ module ZPdf
4
+ module Generators
5
+ class ControllerModuleGenerator < Rails::Generators::Base
6
+ source_root File.join(File.dirname(__FILE__),'templates')
7
+
8
+ def create_controller_module
9
+ template "controller_module_template.rb", 'lib/pdf_controller_methods.rb'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,57 @@
1
+ require 'iconv'
2
+
3
+ # USAGE:
4
+ # This module provides basic methods to send PDF content. Simply include this module
5
+ # in your controller(s):
6
+ #
7
+ # class InvoiceController < ApplicationController
8
+ # include PdfControllerMethods
9
+ # ....
10
+
11
+ # def show
12
+ # ...
13
+ # respond_to do |format|
14
+ # ....
15
+ # format.pdf {
16
+ # send_pdf_content(InvoiceGenerator.invoice(@invoice).pdf_content,
17
+ # :file_name => 'invoice.pdf', :force_download => true)
18
+ # }
19
+ # ....
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+
25
+ module PdfControllerMethods
26
+
27
+ # uncomment and implement if you want to add custom functionality when this module is
28
+ # included.
29
+ # def self.included(base)
30
+ # base.class_eval <<-EOV
31
+ # def do_something_when_pdf_controller_methods_are_included
32
+ # end
33
+ # EOV
34
+ # end
35
+
36
+ protected
37
+ # convert a file name to ISO-8859-1, so that all browsers correctly parse it.
38
+ def sanitize_file_name(name)
39
+ c = Iconv.new('ISO-8859-15','UTF-8')
40
+ s = c.iconv(name.gsub(/[—–]/,'-'))
41
+ s.gsub!(/^.*(\\|\/)/, '')
42
+ s.gsub(/[\:\*\?\"\<\>\|]/, '_')
43
+ end
44
+
45
+ def send_pdf_content(pdf_content,options = {})
46
+ force_download = options[:force_download] || false
47
+ file_name = options[:file_name] || self.class.name.underscore
48
+ # content type is 'iso-8859-1' because we want the header values (namely the filename) to
49
+ # be interpreted as such.
50
+ headers["Content-Type"] ||= 'application/pdf; charset=iso-8859-1'
51
+ if force_download
52
+ headers["Content-Disposition"] = "attachment; filename=\"#{sanitize_file_name(file_name)}\""
53
+ end
54
+ render :text => pdf_content, :layout => false
55
+ end
56
+
57
+ end
@@ -0,0 +1,14 @@
1
+ Description:
2
+ Stubs out a new PDF producer and its views. Pass the producer name, either
3
+ CamelCased or under_scored, and an optional list of document names as arguments.
4
+
5
+ This generates a producer class in app/pdf_producers and invokes your template
6
+ engine and test framework generators.
7
+
8
+ Example:
9
+ `rails generate z_pdf:producer Forms invoice receipt`
10
+
11
+ creates a Forms producer class and views:
12
+ PDF Producer: app/pdf_producers/forms.rb
13
+ Views: app/views/forms/invoice.erb
14
+ app/views/forms/receipt.erb
@@ -0,0 +1,34 @@
1
+ require 'rails/generators/base'
2
+ require 'rails/generators/erb'
3
+
4
+ module ZPdf
5
+ module Generators
6
+ class ProducerGenerator < Erb::Generators::Base
7
+ source_root File.join(File.dirname(__FILE__),'templates')
8
+
9
+ argument :actions, :type => :array, :default => [], :banner => "method method"
10
+
11
+ def create_renderer_file
12
+ template "producer_template.rb", File.join('app/pdf_producers', class_path, "#{file_name}.rb")
13
+ end
14
+
15
+ def create_view_files
16
+ rails_root = Rails.root.to_s + "/"
17
+ config = Rails.application.config
18
+ paths = Rails.application.paths
19
+ pdf_views_path = ( (config.zpdf ? config.zpdf.pdf_views_path : nil) || paths.app.views.to_a.first).gsub(rails_root,'')
20
+
21
+ base_path = File.join(pdf_views_path,class_path,file_name)
22
+ empty_directory base_path
23
+ actions.each do |a|
24
+ @action = a
25
+ ['','_header','_footer'].each do |view_name|
26
+ @path = File.join(base_path,filename_with_extensions("#{a}#{view_name}"))
27
+ template "view_template#{view_name}.html.erb", @path
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ class <%= class_name %> < ZPdf::Base
2
+ default_pdf 'outline' => true, 'page-size' => 'Letter'
3
+ <% for action in actions -%>
4
+
5
+ def <%= action %>
6
+ generate :header_template => "<%= action %>_header",
7
+ :footer_template => "<%= action %>_footer",
8
+ :pdf_params => { 'margin-top' => 4, 'margin-bottom' => 4 }
9
+ end
10
+ <% end -%>
11
+ end
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <style>
3
+ body { margin-top: 1.5em; }
4
+ </style>
5
+ <body>
6
+ <h1><%= class_name %>#<%= @action %></h1>
7
+ <p>Find me in <%= @path %></p>
8
+ </body>
9
+ </html>
@@ -0,0 +1,5 @@
1
+ <html>
2
+ <body>
3
+ <p>Footer for <%= class_name %>#<%= @action %> (Find me in <%= @path %>)</p>
4
+ </body>
5
+ </html>
@@ -0,0 +1,5 @@
1
+ <html>
2
+ <body>
3
+ <p>Header for <%= class_name %>#<%= @action %> (Find me in <%= @path %>)</p>
4
+ </body>
5
+ </html>
@@ -0,0 +1,121 @@
1
+ require 'thread'
2
+ require 'iconv'
3
+
4
+ module ZPdf
5
+
6
+ class HtmlPdfObject
7
+
8
+ attr_reader :html_parts
9
+ attr_reader :pdf_content
10
+
11
+ def self.wkhtmltopdf_path
12
+ @@wkhtmltopdf_path ||= get_wkhtmltopdf_path
13
+ end
14
+
15
+ def self.get_wkhtmltopdf_path
16
+ ZPdf::Base.wkhtmltopdf_path || find_wkhtmltopdf_executable # will try to call it anyway, if it is on the path
17
+ end
18
+
19
+ def initialize(html_parts = {},params = {})
20
+ @html_parts = html_parts
21
+ @params = params
22
+ @verbose = params.delete('verbose')
23
+ end
24
+
25
+ def html_parts=(value)
26
+ @html_parts = value
27
+ end
28
+
29
+ def pdf_content
30
+ @pdf_content ||= convert_to_pdf
31
+ end
32
+
33
+ def save_to_file(filename)
34
+ File.open(filename,'wb') { |f| f.write(pdf_content) }
35
+ end
36
+
37
+ protected
38
+
39
+ def convert_to_pdf
40
+ wk_params = @params.dup
41
+ html_files = { :header => make_temp_file_with_content(@html_parts[:header]),
42
+ :footer => make_temp_file_with_content(@html_parts[:footer]),
43
+ :content => make_temp_file_with_content(@html_parts[:content]) }
44
+ begin
45
+ outfile_path = html_files[:content].path + '.pdf' # append correct extension to temp file name
46
+ wk_params['header-html'] = html_files[:header].nil? ? nil : "file:///#{html_files[:header].path}"
47
+ wk_params['footer-html'] = html_files[:footer].nil? ? nil : "file:///#{html_files[:footer].path}"
48
+ wk_params['quiet'] = true
49
+
50
+ cmd = "#{self.class.wkhtmltopdf_path}"
51
+ wk_params.each_pair do |k,v|
52
+ next if v.nil? || v === false
53
+ cmd << (v == true ? " --#{k}" : " --#{k} #{v}")
54
+ end
55
+
56
+ cmd << " file:///#{html_files[:content].path} #{outfile_path}"
57
+ puts "\n\nexecuting #{cmd}\n\n" if @verbose
58
+ # avoid errors on systems where command processor is not UTF-8
59
+ `#{Iconv.new('ISO-8859-1//TRANSLIT','UTF-8').iconv(cmd)}`
60
+ if File.exists?(outfile_path)
61
+ output = File.open(outfile_path,'rb') { |f| f.read }
62
+ File.unlink(outfile_path)
63
+ else
64
+ raise ZPdf::RenderError.new("Error running wkhtmltopdf: #{cmd}")
65
+ end
66
+ ensure
67
+ html_files.each_pair do |k,v|
68
+ File.unlink(v.path) if v && File.exists?(v.path) # make sure we delete our temporary files!
69
+ end
70
+ end
71
+ output
72
+ end
73
+
74
+ # return an instance of File with a unique name, suitable for writing
75
+ def create_temp_file(basename = '__pdf.html.object',ext = 'html',tmpdir = Dir::tmpdir,mode = 'wb')
76
+ @@_temp_file_mutex ||= Mutex.new
77
+
78
+ failures = n = 0
79
+ begin
80
+ @@_temp_file_mutex.lock
81
+ begin
82
+ tmpname = File.join(tmpdir, sprintf('%s.%d.%d.%s', basename, $$, n, ext))
83
+ n += 1
84
+ end while File.exists?(tmpname)
85
+ return File.open(tmpname,mode)
86
+ rescue
87
+ failures += 1
88
+ retry if failures < 10
89
+ raise ZPdf::RenderError.new("ZPdf::HtmlPdfObject cannot create temp file")
90
+ ensure
91
+ @@_temp_file_mutex.unlock
92
+ end
93
+ end
94
+
95
+ def make_temp_file_with_content(content)
96
+ unless content.nil? || content.to_s.empty?
97
+ f = create_temp_file
98
+ f.write(content)
99
+ f.flush
100
+ f.close
101
+ f
102
+ end
103
+ end
104
+
105
+ def self.find_wkhtmltopdf_executable
106
+ rbCfg = defined?(RbConfig) ? RbConfig : Config
107
+ if rbCfg::CONFIG['host_os'] =~ /mswin|mingw/ # windows?
108
+ ENV['PATH'].split(';').each do |p|
109
+ exec_path = File.join(p,'wkhtmltopdf.exe')
110
+ return exec_path if File.exists?(exec_path)
111
+ end
112
+ return 'wkhtmltopdf.exe'
113
+ else # *nix
114
+ exec_path = `which wkhtmltopdf`.to_s.strip
115
+ return File.exists?(exec_path) ? exec_path : 'wkhtmltopdf'
116
+ end
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,32 @@
1
+ require "zpdf"
2
+ require "rails"
3
+
4
+ module ZPdf
5
+ class Railtie < Rails::Railtie
6
+ config.zpdf = ActiveSupport::OrderedOptions.new
7
+
8
+ initializer "zpdf.set_configs" do |app|
9
+ paths = app.config.paths
10
+ options = app.config.zpdf
11
+
12
+ options.assets_dir ||= paths.public.to_a.first
13
+ options.javascripts_dir ||= paths.public.javascripts.to_a.first
14
+ options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
15
+
16
+ # by default, the views path for ZPdf::Base is the same as other views
17
+ pdf_views_path = options.pdf_views_path || paths.app.views.to_a.first
18
+
19
+ ActiveSupport.on_load(:zpdf) do
20
+ include app.routes.url_helpers
21
+ prepend_view_path pdf_views_path
22
+ options.each { |k,v| send("#{k}=", v) }
23
+ end
24
+ end
25
+
26
+ generators do
27
+ load File.expand_path('./generators/producers/producer.rb',File.dirname(__FILE__))
28
+ load File.expand_path('./generators/controller_module/controller_module.rb',File.dirname(__FILE__))
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ module ZPdf
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 2
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module ZPdf
2
+ module PdfHelper
3
+
4
+ end
5
+ end
data/lib/zpdf.rb ADDED
@@ -0,0 +1,25 @@
1
+ zpdf_path = File.expand_path(File.dirname(__FILE__))
2
+ $:.unshift(zpdf_path) if File.directory?(zpdf_path) && !$:.include?(zpdf_path)
3
+
4
+ require 'rails'
5
+ require 'active_support/core_ext/module/attr_internal'
6
+ require 'active_support/lazy_load_hooks'
7
+ require 'abstract_controller'
8
+ require 'action_view'
9
+ require 'z_pdf/version'
10
+ require 'z_pdf/railtie'
11
+
12
+ module ZPdf
13
+ extend ::ActiveSupport::Autoload
14
+
15
+ # autoload :AdvAttrAccessor
16
+ # autoload :Collector
17
+ autoload :Base
18
+ # autoload :DeliveryMethods
19
+ # autoload :DeprecatedApi
20
+ # autoload :MailHelper
21
+ # autoload :OldApi
22
+ # autoload :TestCase
23
+ # autoload :TestHelper
24
+ end
25
+
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zpdf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Charles Bedard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: actionpack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: expectations
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 2.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.0.0
55
+ description:
56
+ email: charles@cbedard.net
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/z_pdf/base.rb
62
+ - lib/z_pdf/generators/controller_module/controller_module.rb
63
+ - lib/z_pdf/generators/controller_module/templates/controller_module_template.rb
64
+ - lib/z_pdf/generators/controller_module/USAGE
65
+ - lib/z_pdf/generators/producers/producer.rb
66
+ - lib/z_pdf/generators/producers/templates/producer_template.rb
67
+ - lib/z_pdf/generators/producers/templates/view_template.html.erb
68
+ - lib/z_pdf/generators/producers/templates/view_template_footer.html.erb
69
+ - lib/z_pdf/generators/producers/templates/view_template_header.html.erb
70
+ - lib/z_pdf/generators/producers/USAGE
71
+ - lib/z_pdf/html_pdf_object.rb
72
+ - lib/z_pdf/railtie.rb
73
+ - lib/z_pdf/version.rb
74
+ - lib/z_pdf/z_pdf_helper.rb
75
+ - lib/zpdf.rb
76
+ homepage: http://github.com/schmlblk/zpdf
77
+ licenses: []
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - '>='
86
+ - !ruby/object:Gem::Version
87
+ version: 1.9.2
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.0.14
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: HTML-based PDF rendering engine (using wkhtmltopdf)
99
+ test_files: []