zpdf 0.0.2

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 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: []