lib/prawn/document.rb in prawn-0.12.0 vs lib/prawn/document.rb in prawn-0.13.0

- old
+ new

@@ -5,11 +5,10 @@ # Copyright April 2008, Gregory Brown. All Rights Reserved. # # This is free software. Please see the LICENSE and COPYING files for details. require "stringio" -require "prawn/document/page_geometry" require "prawn/document/bounding_box" require "prawn/document/column_box" require "prawn/document/internals" require "prawn/document/span" require "prawn/document/snapshot" @@ -50,19 +49,20 @@ # # See the new and generate methods for further details on the above. # class Document include Prawn::Document::Internals - include Prawn::Core::Annotations - include Prawn::Core::Destinations + include PDF::Core::Annotations + include PDF::Core::Destinations include Prawn::Document::Snapshot include Prawn::Document::GraphicsState include Prawn::Document::Security include Prawn::Text include Prawn::Graphics include Prawn::Images include Prawn::Stamp + include Prawn::SoftMask # Any module added to this array will be included into instances of # Prawn::Document at the per-object level. These will also be inherited by # any subclasses. # @@ -134,12 +134,14 @@ # <tt>:bottom_margin</tt>:: Sets the bottom margin in points [0.5 inch] # <tt>:skip_page_creation</tt>:: Creates a document without starting the first page [false] # <tt>:compress</tt>:: Compresses content streams before rendering them [false] # <tt>:optimize_objects</tt>:: Reduce number of PDF objects in output, at expense of render time [false] # <tt>:background</tt>:: An image path to be used as background on all pages [nil] + # <tt>:background_scale</tt>:: Backgound image scale [1] [nil] # <tt>:info</tt>:: Generic hash allowing for custom metadata properties [nil] # <tt>:template</tt>:: The path to an existing PDF file to use as a template [nil] + # <tt>:text_formatter</tt>: The text formatter to use for <tt>:inline_format</tt>ted text [Prawn::Text::Formatted::Parser] # # Setting e.g. the :margin to 100 points and the :left_margin to 50 will result in margins # of 100 points on every side except for the left, where it will be 50. # # The :margin can also be an array much like CSS shorthand: @@ -164,36 +166,39 @@ # # # New document, Custom size # pdf = Prawn::Document.new(:page_size => [200, 300]) # # # New document, with background - # pdf = Prawn::Document.new(:background => "#{Prawn::BASEDIR}/data/images/pigs.jpg") + # pdf = Prawn::Document.new(:background => "#{Prawn::DATADIR}/images/pigs.jpg") # def initialize(options={},&block) options = options.dup Prawn.verify_options [:page_size, :page_layout, :margin, :left_margin, :right_margin, :top_margin, :bottom_margin, :skip_page_creation, :compress, :skip_encoding, :background, :info, - :optimize_objects, :template], options + :optimize_objects, :template, :text_formatter], options # need to fix, as the refactoring breaks this # raise NotImplementedError if options[:skip_page_creation] self.class.extensions.reverse_each { |e| extend e } - @internal_state = Prawn::Core::DocumentState.new(options) + @internal_state = PDF::Core::DocumentState.new(options) @internal_state.populate_pages_from_store(self) min_version(state.store.min_version) if state.store.min_version @background = options[:background] + @background_scale = options[:background_scale] || 1 @font_size = 12 @bounding_box = nil @margin_box = nil @page_number = 0 + @text_formatter = options.delete(:text_formatter) || Text::Formatted::Parser + options[:size] = options.delete(:page_size) options[:layout] = options.delete(:page_layout) if options[:template] fresh_content_streams(options) @@ -215,10 +220,11 @@ attr_accessor :margin_box attr_reader :margins, :y attr_writer :font_size attr_accessor :page_number + attr_accessor :text_formatter def state @internal_state end @@ -239,10 +245,13 @@ # A template for a page can be specified by pointing to the path of and existing pdf. # One can also specify which page of the template which defaults otherwise to 1. # # pdf.start_new_page(:template => multipage_template.pdf, :template_page => 2) # + # Note: templates get indexed by either the object_id of the filename or stream + # entered so that if you reuse the same template multiple times be sure to use the + # same instance for more efficient use of resources and smaller rendered pdfs. def start_new_page(options = {}) if last_page = state.page last_page_size = last_page.size last_page_layout = last_page.layout last_page_margins = last_page.margins @@ -250,18 +259,18 @@ page_options = {:size => options[:size] || last_page_size, :layout => options[:layout] || last_page_layout, :margins => last_page_margins} if last_page - new_graphic_state = last_page.graphic_state.dup + new_graphic_state = last_page.graphic_state.dup if last_page.graphic_state #erase the color space so that it gets reset on new page for fussy pdf-readers - new_graphic_state.color_space = {} + new_graphic_state.color_space = {} if new_graphic_state page_options.merge!(:graphic_state => new_graphic_state) end merge_template_options(page_options, options) if options[:template] - state.page = Prawn::Core::Page.new(self, page_options) + state.page = PDF::Core::Page.new(self, page_options) apply_margin_options(options) generate_margin_box # Reset the bounding box if the new page has different size or layout @@ -270,16 +279,17 @@ @bounding_box = @margin_box end state.page.new_content_stream if options[:template] use_graphic_settings(options[:template]) + forget_text_rendering_mode! if options[:template] unless options[:orphan] state.insert_page(state.page, @page_number) @page_number += 1 - canvas { image(@background, :at => bounds.top_left) } if @background + canvas { image(@background, :scale => @background_scale, :at => bounds.top_left) } if @background @y = @bounding_box.absolute_top float do state.on_page_create_action(self) end @@ -346,32 +356,36 @@ yield go_to_page(original_page) unless page_number == original_page self.y = original_y end - # Renders the PDF document to string + # Renders the PDF document to string. + # Pass an open file descriptor to render to file. # - def render - output = StringIO.new + def render(output = StringIO.new) finalize_all_page_contents render_header(output) render_body(output) render_xref(output) render_trailer(output) - str = output.string - str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding) - str + if output.instance_of?(StringIO) + str = output.string + str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding) + return str + else + return nil + end end # Renders the PDF document to file. # # pdf.render_file "foo.pdf" # def render_file(filename) Kernel.const_defined?("Encoding") ? mode = "wb:ASCII-8BIT" : mode = "wb" - File.open(filename,mode) { |f| f << render } + File.open(filename,mode) { |f| render(f) } end # The bounds method returns the current bounding box you are currently in, # which is by default the box represented by the margin box on the # document itself. When called from within a created <tt>bounding_box</tt> @@ -537,30 +551,30 @@ # <total> refers to the total amount of pages in the document. Page numbering should # occur at the end of your Prawn::Document.generate block because the method iterates # through existing pages after they are created. # # Parameters are: - # - # <tt>string</tt>:: Template string for page number wording. + # + # <tt>string</tt>:: Template string for page number wording. # Should include '<page>' and, optionally, '<total>'. # <tt>options</tt>:: A hash for page numbering and text box options. - # <tt>:page_filter</tt>:: A filter to specify which pages to place page numbers on. + # <tt>:page_filter</tt>:: A filter to specify which pages to place page numbers on. # Refer to the method 'page_match?' # <tt>:start_count_at</tt>:: The starting count to increment pages from. # <tt>:total_pages</tt>:: If provided, will replace <total> with the value given. - # Useful to override the total number of pages when using + # Useful to override the total number of pages when using # the start_count_at option. # <tt>:color</tt>:: Text fill color. # # Please refer to Prawn::Text::text_box for additional options concerning text # formatting and placement. # # Example: Print page numbers on every page except for the first. Start counting from # five. # # Prawn::Document.generate("page_with_numbering.pdf") do - # number_pages "<page> in a total of <total>", + # number_pages "<page> in a total of <total>", # {:start_count_at => 5, # :page_filter => lambda{ |pg| pg != 1 }, # :at => [bounds.right - 50, 0], # :align => :right, # :size => 14} @@ -575,46 +589,46 @@ :all end total_pages = opts.delete(:total_pages) txtcolor = opts.delete(:color) # An explicit height so that we can draw page numbers in the margins - opts[:height] = 50 - + opts[:height] = 50 unless opts.has_key?(:height) + start_count = false pseudopage = 0 (1..page_count).each do |p| unless start_count pseudopage = case start_count_at when 0 1 else start_count_at.to_i end - end + end if page_match?(page_filter, p) go_to_page(p) # have to use fill_color here otherwise text reverts back to default fill color fill_color txtcolor unless txtcolor.nil? total_pages = total_pages.nil? ? page_count : total_pages str = string.gsub("<page>","#{pseudopage}").gsub("<total>","#{total_pages}") text_box str, opts start_count = true # increment page count as soon as first match found - end + end pseudopage += 1 if start_count end end # Provides a way to execute a block of code repeatedly based on a - # page_filter. + # page_filter. # # Available page filters are: # :all repeats on every page # :odd repeats on odd pages # :even repeats on even pages # some_array repeats on every page listed in the array # some_range repeats on every page included in the range - # some_lambda yields page number and repeats for true return values + # some_lambda yields page number and repeats for true return values def page_match?(page_filter, page_number) case page_filter when :all true when :odd @@ -624,11 +638,11 @@ when Range, Array page_filter.include?(page_number) when Proc page_filter.call(page_number) end - end + end # Returns true if content streams will be compressed before rendering, # false otherwise # @@ -638,11 +652,11 @@ private def merge_template_options(page_options, options) object_id = state.store.import_page(options[:template], options[:template_page] || 1) - page_options.merge!(:object_id => object_id ) + page_options.merge!(:object_id => object_id, :page_template => true) end # setting override_settings to true ensures that a new graphic state does not end up using # previous settings especially from imported template streams def use_graphic_settings(override_settings = false) @@ -694,8 +708,12 @@ [:left,:right,:top,:bottom].each do |side| if margin = options[:"#{side}_margin"] state.page.margins[side] = margin end end + end + + def font_metric_cache #:nodoc: + @font_metric_cache ||= FontMetricCache.new( self ) end end end