lib/prawn/document.rb in prawn-0.14.0 vs lib/prawn/document.rb in prawn-0.15.0

- old
+ new

@@ -5,10 +5,11 @@ # Copyright April 2008, Gregory Brown. All Rights Reserved. # # This is free software. Please see the LICENSE and COPYING files for details. require "stringio" + require_relative "document/bounding_box" require_relative "document/column_box" require_relative "document/internals" require_relative "document/span" require_relative "document/snapshot" @@ -60,10 +61,12 @@ include Prawn::Graphics include Prawn::Images include Prawn::Stamp include Prawn::SoftMask + # @group Extension API + # NOTE: We probably need to rethink the options validation system, but this # constant temporarily allows for extensions to modify the list. VALID_OPTIONS = [:page_size, :page_layout, :margin, :left_margin, :right_margin, :top_margin, :bottom_margin, :skip_page_creation, @@ -88,18 +91,32 @@ # # Prawn::Document.generate("foo.pdf") do # party! # end # + # def self.extensions @extensions ||= [] end - def self.inherited(base) #:nodoc: + # @private + def self.inherited(base) extensions.each { |e| base.extensions << e } end + # @group Stable Attributes + + attr_accessor :margin_box + attr_reader :margins, :y + attr_accessor :page_number + + # @group Extension Attributes + + attr_accessor :text_formatter + + # @group Stable API + # Creates and renders a PDF document. # # When using the implicit block form, Prawn will evaluate the block # within an instance of Prawn::Document, simplifying your syntax. # However, please note that you will not be able to reference variables @@ -213,32 +230,12 @@ if block block.arity < 1 ? instance_eval(&block) : block[self] end end - attr_accessor :margin_box - attr_reader :margins, :y - attr_writer :font_size - attr_accessor :page_number - attr_accessor :text_formatter + # @group Stable API - def state - @internal_state - end - - def page - state.page - end - - def initialize_first_page(options) - if options[:skip_page_creation] - start_new_page(options.merge(:orphan => true)) - else - start_new_page(options) - end - end - # Creates and advances to a new page in the document. # # Page size, margins, and layout can also be set when generating a # new page. These values will become the new defaults for page creation # @@ -354,10 +351,13 @@ # Renders the PDF document to string. # Pass an open file descriptor to render to file. # def render(output = StringIO.new) + if output.instance_of?(StringIO) + output.set_encoding(::Encoding::ASCII_8BIT) + end finalize_all_page_contents render_header(output) render_body(output) render_xref(output) @@ -410,10 +410,11 @@ @bounding_box end # Returns the innermost non-stretchy bounding box. # + # @private def reference_bounds @bounding_box.reference_bounds end # Sets Document#bounds to the BoundingBox provided. See above for a brief @@ -495,51 +496,10 @@ # def indent(left, right = 0, &block) bounds.indent(left, right, &block) end - - def mask(*fields) # :nodoc: - # Stores the current state of the named attributes, executes the block, and - # then restores the original values after the block has executed. - # -- I will remove the nodoc if/when this feature is a little less hacky - stored = {} - fields.each { |f| stored[f] = send(f) } - yield - fields.each { |f| send("#{f}=", stored[f]) } - end - - # Attempts to group the given block vertically within the current context. - # First attempts to render it in the current position on the current page. - # If that attempt overflows, it is tried anew after starting a new context - # (page or column). Returns a logically true value if the content fits in - # one page/column, false if a new page or column was needed. - # - # Raises CannotGroup if the provided content is too large to fit alone in - # the current page or column. - # - def group(second_attempt=false) - old_bounding_box = @bounding_box - @bounding_box = SimpleDelegator.new(@bounding_box) - - def @bounding_box.move_past_bottom - raise RollbackTransaction - end - - success = transaction { yield } - - @bounding_box = old_bounding_box - - unless success - raise Prawn::Errors::CannotGroup if second_attempt - old_bounding_box.move_past_bottom - group(second_attempt=true) { yield } - end - - success - end - # Places a text box on specified pages for page numbering. This should be called # towards the end of document creation, after all your content is already in # place. In your template string, <page> refers to the current page, and # <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 @@ -608,10 +568,50 @@ end pseudopage += 1 if start_count end end + # Returns true if content streams will be compressed before rendering, + # false otherwise + # + def compression_enabled? + !!state.compress + end + + # @group Experimental API + + # Attempts to group the given block vertically within the current context. + # First attempts to render it in the current position on the current page. + # If that attempt overflows, it is tried anew after starting a new context + # (page or column). Returns a logically true value if the content fits in + # one page/column, false if a new page or column was needed. + # + # Raises CannotGroup if the provided content is too large to fit alone in + # the current page or column. + # + def group(second_attempt=false) + old_bounding_box = @bounding_box + @bounding_box = SimpleDelegator.new(@bounding_box) + + # @private + def @bounding_box.move_past_bottom + raise RollbackTransaction + end + + success = transaction { yield } + + @bounding_box = old_bounding_box + + unless success + raise Prawn::Errors::CannotGroup if second_attempt + old_bounding_box.move_past_bottom + group(second_attempt=true) { yield } + end + + success + end + # Provides a way to execute a block of code repeatedly based on a # page_filter. # # Available page filters are: # :all repeats on every page @@ -633,18 +633,45 @@ when Proc page_filter.call(page_number) end end + # @private + + def mask(*fields) + # Stores the current state of the named attributes, executes the block, and + # then restores the original values after the block has executed. + # -- I will remove the nodoc if/when this feature is a little less hacky + stored = {} + fields.each { |f| stored[f] = send(f) } + yield + fields.each { |f| send("#{f}=", stored[f]) } + end - # Returns true if content streams will be compressed before rendering, - # false otherwise - # - def compression_enabled? - !!state.compress + # @group Extension API + + def initialize_first_page(options) + if options[:skip_page_creation] + start_new_page(options.merge(:orphan => true)) + else + start_new_page(options) + end end + ## Internals. Don't depend on them! + + # @private + def state + @internal_state + end + + # @private + def page + state.page + end + private + # setting override_settings to true ensures that a new graphic state does not end up using # previous settings. def use_graphic_settings(override_settings = false) set_fill_color if current_fill_color != "000000" || override_settings