lib/prawn/document.rb in prawn-0.1.2 vs lib/prawn/document.rb in prawn-0.2.0

- old
+ new

@@ -9,44 +9,50 @@ require "stringio" require "prawn/document/page_geometry" require "prawn/document/bounding_box" require "prawn/document/text" require "prawn/document/table" +require "prawn/document/internals" module Prawn class Document - + + include Prawn::Document::Internals include Prawn::Graphics include Prawn::Images include Text include PageGeometry attr_accessor :y, :margin_box attr_reader :margins, :page_size, :page_layout - - + # Creates and renders a PDF document. # - # The block argument is necessary only when you need to make - # use of a closure. - # - # # Using implicit block form and rendering to a file - # Prawn::Document.generate "foo.pdf" do + # 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 + # from the enclosing scope within this block. + # + # # Using implicit block form and rendering to a file + # Prawn::Document.generate "foo.pdf" do # font "Times-Roman" # text "Hello World", :at => [200,720], :size => 32 - # end - # - # # Using explicit block form and rendering to a file - # content = "Hello World" - # Prawn::Document.generate "foo.pdf" do |pdf| + # end + # + # If you need to access your local and instance variables, use the explicit + # block form shown below. In this case, Prawn yields an instance of + # PDF::Document and the block is an ordinary closure: + # + # # Using explicit block form and rendering to a file + # content = "Hello World" + # Prawn::Document.generate "foo.pdf" do |pdf| # pdf.font "Times-Roman" # pdf.text content, :at => [200,720], :size => 32 - # end + # end # def self.generate(filename,options={},&block) - pdf = Prawn::Document.new(options) - block.arity < 1 ? pdf.instance_eval(&block) : yield(pdf) + pdf = Prawn::Document.new(options,&block) pdf.render_file(filename) end # Creates a new PDF Document. The following options are available: # @@ -59,47 +65,51 @@ # <tt>:top_margin</tt>:: Sets the top margin in points [ 0.5 inch] # <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] # + # Usage: # # # New document, US Letter paper, portrait orientation # pdf = Prawn::Document.new # # # New document, A4 paper, landscaped # pdf = Prawn::Document.new(:page_size => "A4", :page_layout => :landscape) - # - # # New document, draws a line at the start of each new page - # pdf = Prawn::Document.new(:on_page_start => - # lambda { |doc| doc.line [0,100], [300,100] } ) # - def initialize(options={}) + def initialize(options={},&block) + Prawn.verify_options [:page_size, :page_layout, :on_page_start, + :on_page_stop, :left_margin, :right_margin, :top_margin, + :bottom_margin, :skip_page_creation, :compress ], options + @objects = [] @info = ref(:Creator => "Prawn", :Producer => "Prawn") @pages = ref(:Type => :Pages, :Count => 0, :Kids => []) - @root = ref(:Type => :Catalog, :Pages => @pages) - @page_start_proc = options[:on_page_start] - @page_stop_proc = options[:on_page_stop] - @page_size = options[:page_size] || "LETTER" - @page_layout = options[:page_layout] || :portrait - @compress = options[:compress] || false + @root = ref(:Type => :Catalog, :Pages => @pages) + @page_size = options[:page_size] || "LETTER" + @page_layout = options[:page_layout] || :portrait + @compress = options[:compress] || false + @skip_encoding = options[:skip_encoding] + + text_options.update(options[:text_options] || {}) @margins = { :left => options[:left_margin] || 36, :right => options[:right_margin] || 36, :top => options[:top_margin] || 36, :bottom => options[:bottom_margin] || 36 } generate_margin_box @bounding_box = @margin_box - start_new_page unless options[:skip_page_creation] + start_new_page unless options[:skip_page_creation] + + if block + block.arity < 1 ? instance_eval(&block) : block[self] + end end - # Creates and advances to a new page in the document. - # Runs the <tt>on_page_start</tt> lambda if one was provided at - # document creation time (See Document.new). + # 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 # # pdf.start_new_page(:size => "LEGAL", :layout => :landscape) @@ -114,26 +124,18 @@ @margins[side] = options[:"#{side}_margin"] end end finish_page_content if @page_content - generate_margin_box - @page_content = ref(:Length => 0) - - @current_page = ref(:Type => :Page, - :Parent => @pages, - :MediaBox => page_dimensions, - :Contents => @page_content) - set_current_font - update_colors + build_new_page_content + @pages.data[:Kids] << @current_page @pages.data[:Count] += 1 add_content "q" - @y = @margin_box.absolute_top - @page_start_proc[self] if @page_start_proc + @y = @bounding_box.absolute_top end # Returns the number of pages in the document # # pdf = Prawn::Document.new @@ -174,10 +176,17 @@ # a <tt>bounding_box</tt> block, the box defined by that call will # be used. # def bounds @bounding_box + end + + # Sets Document#bounds to the BoundingBox provided. If you don't know + # why you'd need to do this, chances are, you can ignore this feature + # + def bounds=(bounding_box) + @bounding_box = bounding_box end # Moves up the document by n points # def move_up(n) @@ -229,114 +238,55 @@ move_down(y) yield move_down(y) 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 - + + # Returns true if content streams will be compressed before rendering, + # false otherwise + # def compression_enabled? - @compress - end - + !!@compress + end private + # See Prawn::Document::Internals for low-level PDF functions + + def build_new_page_content + generate_margin_box + @page_content = ref(:Length => 0) + + @current_page = ref(:Type => :Page, + :Parent => @pages, + :MediaBox => page_dimensions, + :Contents => @page_content) + font.add_to_current_page if @font_name + update_colors + end + def generate_margin_box old_margin_box = @margin_box @margin_box = BoundingBox.new( self, [ @margins[:left], page_dimensions[-1] - @margins[:top] ] , :width => page_dimensions[-2] - (@margins[:left] + @margins[:right]), :height => page_dimensions[-1] - (@margins[:top] + @margins[:bottom]) ) # update bounding box if not flowing from the previous page - # TODO: This may have a bug where the old margin is restored + # FIXME: This may have a bug where the old margin is restored # when the bounding box exits. @bounding_box = @margin_box if old_margin_box == @bounding_box end - - def ref(data) - @objects.push(Prawn::Reference.new(@objects.size + 1, data)).last - end - - def add_content(str) - @page_content << str << "\n" - end - - # Add a new type to the current pages ProcSet - def proc_set(*types) - @current_page.data[:ProcSet] ||= ref([]) - @current_page.data[:ProcSet].data |= types - end - - def page_resources - @current_page.data[:Resources] ||= {} - end - - def page_fonts - page_resources[:Font] ||= {} - end - - def page_xobjects - page_resources[:XObject] ||= {} - end - def finish_page_content - @page_stop_proc[self] if @page_stop_proc - add_content "Q" - @page_content.compress_stream if compression_enabled? - @page_content.data[:Length] = @page_content.stream.size - end - - # Write out the PDF Header, as per spec 3.4.1 - def render_header(output) - # pdf version - output << "%PDF-1.3\n" - - # 4 binary chars, as recommended by the spec - output << "\xFF\xFF\xFF\xFF\n" - end - - # Write out the PDF Body, as per spec 3.4.2 - def render_body(output) - @objects.each do |ref| - ref.offset = output.size - output << ref.object - end - end - - # Write out the PDF Cross Reference Table, as per spec 3.4.3 - def render_xref(output) - @xref_offset = output.size - output << "xref\n" - output << "0 #{@objects.size + 1}\n" - output << "0000000000 65535 f \n" - @objects.each do |ref| - output.printf("%010d", ref.offset) - output << " 00000 n \n" - end - end - - # Write out the PDF Body, as per spec 3.4.4 - def render_trailer(output) - trailer_hash = {:Size => @objects.size + 1, - :Root => @root, - :Info => @info} - - output << "trailer\n" - output << Prawn::PdfObject(trailer_hash) << "\n" - output << "startxref\n" - output << @xref_offset << "\n" - output << "%%EOF" - end end end