From d426e12f73d1cc4f277ef638fcd441fbe34522c6 Mon Sep 17 00:00:00 2001 From: Gregory Brown Date: Wed, 15 Jan 2014 17:11:55 -0500 Subject: [PATCH] Transitional extraction of templates This commit is a squashed subset of #640, which begins the process of extracting templates out of Prawn::Document and into an extension. It's not complete, but it's functional enough where I'd like to include it in release for user testing. It accomplishes two main goals: * Disables templates by default in Prawn * Allows those who will use the future prawn-templates gem to try it out as an extension by requiring "prawn/templates" The way I extracted the code is not meant to be a permanent solution. We will need to move away from a module-based monkey patch to some sort of object-oriented solution, and we should aim to cut down the duplication of code in the templates extension. But this is something we can do later. We also need to finish extracting the lower-level features that templates depend upon, but that too can be done in a future release. This patch is primarily about removing the top-level use of the feature so that we can be sure we didn't break anything critical in the process of doing so. --- lib/prawn/document.rb | 55 +++++--------- lib/prawn/templates.rb | 75 +++++++++++++++++++ manual/manual/manual.rb | 1 - manual/templates/full_template.rb | 25 ------- manual/templates/page_template.rb | 48 ------------ manual/templates/templates.rb | 27 ------- ...late_spec.rb => template_spec_obsolete.rb} | 3 +- 7 files changed, 97 insertions(+), 137 deletions(-) create mode 100644 lib/prawn/templates.rb delete mode 100644 manual/templates/full_template.rb delete mode 100644 manual/templates/page_template.rb delete mode 100644 manual/templates/templates.rb rename spec/{template_spec.rb => template_spec_obsolete.rb} (99%) diff --git a/lib/prawn/document.rb b/lib/prawn/document.rb index eb71dcb87..d91dd8739 100644 --- a/lib/prawn/document.rb +++ b/lib/prawn/document.rb @@ -62,6 +62,14 @@ class Document include Prawn::Stamp include Prawn::SoftMask + # 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, + :compress, :skip_encoding, :background, :info, + :optimize_objects, :text_formatter, :print_scaling] + # 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. @@ -138,7 +146,6 @@ def self.generate(filename,options={},&block) # :background:: An image path to be used as background on all pages [nil] # :background_scale:: Backgound image scale [1] [nil] # :info:: Generic hash allowing for custom metadata properties [nil] - # :template:: The path to an existing PDF file to use as a template [nil] # :text_formatter: The text formatter to use for :inline_formatted text [Prawn::Text::Formatted::Parser] # # Setting e.g. the :margin to 100 points and the :left_margin to 50 will result in margins @@ -173,12 +180,7 @@ def self.generate(filename,options={},&block) 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, :text_formatter, :print_scaling], options - - + Prawn.verify_options VALID_OPTIONS, options # need to fix, as the refactoring breaks this # raise NotImplementedError if options[:skip_page_creation] @@ -204,16 +206,7 @@ def initialize(options={},&block) options[:size] = options.delete(:page_size) options[:layout] = options.delete(:page_layout) - if options[:template] - fresh_content_streams(options) - go_to_page(1) - else - if options[:skip_page_creation] || options[:template] - start_new_page(options.merge(:orphan => true)) - else - start_new_page(options) - end - end + initialize_first_page(options) @bounding_box = @margin_box @@ -236,6 +229,14 @@ 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 @@ -246,14 +247,6 @@ def page # pdf.start_new_page(:left_margin => 50, :right_margin => 50) # pdf.start_new_page(:margin => 100) # - # 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 @@ -270,7 +263,6 @@ def start_new_page(options = {}) 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 = PDF::Core::Page.new(self, page_options) @@ -283,9 +275,7 @@ def start_new_page(options = {}) @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] + use_graphic_settings unless options[:orphan] state.insert_page(state.page, @page_number) @@ -655,13 +645,8 @@ def compression_enabled? 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_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 + # previous settings. def use_graphic_settings(override_settings = false) set_fill_color if current_fill_color != "000000" || override_settings set_stroke_color if current_stroke_color != "000000" || override_settings diff --git a/lib/prawn/templates.rb b/lib/prawn/templates.rb new file mode 100644 index 000000000..b95530ffe --- /dev/null +++ b/lib/prawn/templates.rb @@ -0,0 +1,75 @@ +warn "Templates are no longer supported in Prawn!\n" + + "This code is for experimental testing only, and\n" + + "will extracted into its own gem in a future Prawn release" + +module Prawn + module Templates + def initialize_first_page(options) + return super unless options[:template] + + fresh_content_streams(options) + go_to_page(1) + end + + ## FIXME: This is going to be terribly brittle because + # it copy-pastes the start_new_page method. But at least + # it should only run when templates are used. + + def start_new_page(options = {}) + return super unless options[:template] + + if last_page = state.page + last_page_size = last_page.size + last_page_layout = last_page.layout + last_page_margins = last_page.margins + end + + 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 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 = {} if new_graphic_state + page_options.merge!(:graphic_state => new_graphic_state) + end + + merge_template_options(page_options, 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 + if last_page && (last_page.size != state.page.size || + last_page.layout != state.page.layout) + @bounding_box = @margin_box + end + + state.page.new_content_stream + use_graphic_settings(true) + forget_text_rendering_mode! + + unless options[:orphan] + state.insert_page(state.page, @page_number) + @page_number += 1 + + 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 + end + end + + 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_template => true) + end + end +end + +Prawn::Document::VALID_OPTIONS << :template +Prawn::Document.extensions << Prawn::Templates diff --git a/manual/manual/manual.rb b/manual/manual/manual.rb index 6d1ff38f0..a7dd6aaf7 100644 --- a/manual/manual/manual.rb +++ b/manual/manual/manual.rb @@ -30,6 +30,5 @@ load_package "document_and_page_options" load_package "outline" load_package "repeatable_content" - load_package "templates" load_package "security" end diff --git a/manual/templates/full_template.rb b/manual/templates/full_template.rb deleted file mode 100644 index eb4e52afb..000000000 --- a/manual/templates/full_template.rb +++ /dev/null @@ -1,25 +0,0 @@ -# encoding: utf-8 -# -#NOTE: Templates are currently unmaintained and may be removed by Prawn 1.0! -# -# You may load another PDF while creating a new one. Just pass the loaded PDF -# filename to the :template option when creating/generating the new -# PDF. -# -# The provided PDF will be loaded and its first page will be set as the -# current page. If you'd like to resume the document you may take advantage of -# two helpers: page_count and go_to_page. -# -require File.expand_path(File.join(File.dirname(__FILE__), - %w[.. example_helper])) - -filename = "#{Prawn::DATADIR}/pdfs/multipage_template.pdf" - -Prawn::Example.generate("full_template.pdf", :template => filename) do - go_to_page(page_count) - - start_new_page - - text "Previous pages and content imported.", :align => :center - text "This page and content is brand new.", :align => :center -end diff --git a/manual/templates/page_template.rb b/manual/templates/page_template.rb deleted file mode 100644 index d4112a8d9..000000000 --- a/manual/templates/page_template.rb +++ /dev/null @@ -1,48 +0,0 @@ -# encoding: utf-8 -# NOTE: Templates are currently unmaintained and may be removed by Prawn 1.0! -# -# If you only need to load some pages from another PDF, you can accomplish it -# with the start_new_page method. You may pass it a -# :template option with the path for an existing pdf and a -# :template_page option to specify which page to load. -# You can also load a :template using a URI: -# -# require 'open-uri' -# -# start_new_page(:template => open('url_for_your.pdf')) -# -# The following example loads some pages from an existing PDF. If we don't -# specify the :template_page option, the first page of the template -# PDF will be loaded. That's what happens on the first load below. Then we load -# a page by specifying the :template_page option and then we do it -# again this time adding some content to the loaded page. -# -require File.expand_path(File.join(File.dirname(__FILE__), - %w[.. example_helper])) - -filename = File.basename(__FILE__).gsub('.rb', '.pdf') -Prawn::Example.generate(filename) do - text "Please scan the next 3 pages to see the page templates in action." - move_down 10 - text "You also might want to look at the pdf used as a template: " - url = "https://github.com/prawnpdf/prawn/raw/master/data/pdfs/form.pdf" - move_down 10 - - formatted_text [{:text => url, :link => url}] - - filename = "#{Prawn::DATADIR}/pdfs/form.pdf" - start_new_page(:template => filename) - - start_new_page(:template => filename, :template_page => 2) - - start_new_page(:template => filename, :template_page => 2) - - fill_color "FF8888" - - text_box "John Doe", :at => [75, cursor-75] - text_box "john@doe.com", :at => [75, cursor-105] - text_box "John Doe inc", :at => [75, cursor-135] - text_box "You didn't think I'd tell, did you?", :at => [75, cursor-165] - - fill_color "000000" -end diff --git a/manual/templates/templates.rb b/manual/templates/templates.rb deleted file mode 100644 index 7c3d237c5..000000000 --- a/manual/templates/templates.rb +++ /dev/null @@ -1,27 +0,0 @@ -# encoding: utf-8 -# -# Examples for loading existing pdfs. -# -require File.expand_path(File.join(File.dirname(__FILE__), - %w[.. example_helper])) - -Prawn::Example.generate("templates.pdf", :page_size => "FOLIO") do - - package "templates" do |p| - - p.example "full_template", :eval_source => false, :full_source => true - p.example "page_template" - - p.intro do - prose("NOTE: Templates are currently unmaintained and may be removed by Prawn 1.0!") - prose("Templates let you embed other PDF documents inside the current one. - - The examples show:") - - list( "How to load the whole content from another PDF", - "How to load single pages from another PDF" - ) - end - - end -end diff --git a/spec/template_spec.rb b/spec/template_spec_obsolete.rb similarity index 99% rename from spec/template_spec.rb rename to spec/template_spec_obsolete.rb index 9197f3fab..599338112 100644 --- a/spec/template_spec.rb +++ b/spec/template_spec_obsolete.rb @@ -1,4 +1,5 @@ -require File.join(File.expand_path(File.dirname(__FILE__)), "spec_helper") +require_relative "spec_helper" +require_relative "../lib/prawn/templates" describe "Document built from a template" do it "should have the same page count as the source document" do