Sep19th

Importing multiple pdf pages and documents into PDFlib - Part 2

Nate Constant PDFLib, PDF, Rails, Ruby , , , Read on

This is a followup to my first post about importing multiple pages into PDFlib .
I have created a class that will allow you to import multi page PDF’s as well as importing multiple PDF documents into one generated PDF. It will also fill all of your PDFlib text blocks with the data you provide:

Please post any questions or comments about it.

Here’s the class on pastie: http://pastie.caboo.se/14052

Here’s the code:

# — HERE’S HOW TO USE THIS CLASS
# Created by Nate Constant, http://shabadeehoob.com . Please contact me if you need help using it
=begin
  1. open your PDF doc in Acrobat Standard (or Pro).
  2. using the PDFlib block tool draw your text blocks in your pdf where you want them
   a. if you are drawing blocks for checkboxes make sure to set the font to “ZapfDingbats” —IMPORTANT: dont use “Zapf Dingbats” <- see the space in there?
   b. if you are drawing blocks that are supposed to circle something (i.e. “circle your answer”) make sure to set the font size to something large to make the circle
  3. use the “Save As” to save your pdf into the directory you want (probably the directory in your rails app because you can access it via #{RAILS_ROOT})
  4. in this class go to the “fill_app”  method and set your “searchpath”. this is the path for your pdf doc in your rails app
  5. point your browser to the generate_pdf action and let er rip!
=end

class PdfDocsController < ApplicationController
  def generate_pdf
    @settings=Hash.new
    @settings[‘checkbox_type’] = “8″ # ZapfDingbats font number
    @settings[‘circlebox_type’] = “O”
    circle_type = “”
    @default_width = 612
    @default_height = 792
    @default_value = “n/a”
    @data = Hash.new(@default_value)

    @settings[“checkbox_type”] = “8″ #ZapfDingbats character number for checkbox
    @settings[“circle_type”]    = “O” #use this for circling answers

    # — NORMALLY YOU WOULD POPULATE THE @data INSTANCE VAR FROM A DATABASE OR SOMEWHERE ELSE
    @data[“male”]     = @settings[“checkbox_type”]
    @data[“name”]     = “Nate Constant”
    @data[“address”]  = “1234 some street”
    @data[“city”]     = “Denver”
    @data[“state”]    = “Colorado”

    send_data fill_app, :filename => “pdf_document.pdf”, :type => “application/pdf”,:disposition => “inline”
  end

  private
  def fill_app
    searchpath = #{RAILS_ROOT}/my_pdfs”

    p = PDFlib.new
    if (p.begin_document(“”, “compatibility = 1.6″) == -1)
      logger.info “Error: “ + p.get_errmsg()
    end

    # Set the search path for fonts and PDF files
    p.set_parameter(“SearchPath”, searchpath)
    p.set_parameter(“pdiwarning”, “true”)
    p.set_info(“Creator”, “businesscard.rb”)
    p.set_info(“Author”, “Your Name”)
    p.set_info(“Title”, “The title of your PDF”)

    #IMPORT YOUR PDF YOU CREATED WITH THE PDF BLOCK TOOL
    import_file(p,“answer_pleading.pdf”) 

    p.end_document(“”)
    p.get_buffer()
  end
  def import_file(p,filename)
    blockcontainer = p.open_pdi(filename, “”, 0)
    if (blockcontainer == -1)
      logger.info “Error: “ + p.get_errmsg()
    end

    regularfont = p.load_font(“Helvetica”, “winansi”, “”)
    checkbox_font = p.load_font(“ZapfDingbats”,“builtin”,“”)
    #loop through all pages and put data in the blocks
    number_of_pages = p.get_pdi_value(“/Root/Pages/Count”,blockcontainer,-1,0)
    1.upto(number_of_pages){|page_num|

        #get current page
        page = p.open_pdi_page(blockcontainer, page_num, “”)
        if (page == -1)
          logger.info “Error: “ + p.get_errmsg()
        end

        #get all blocks for the page and put them in an array to use later
        num_of_blocks_on_page = p.get_pdi_value(“vdp/blockcount”,blockcontainer,page,0)
        cur_blocks =  Array.new
        0.upto(num_of_blocks_on_page-1){ |i|
          cur_blocks.push( p.get_pdi_parameter(“vdp/Blocks[#{i}]/Name”, blockcontainer, page, 0))
          #logger.info p.get_pdi_parameter(”vdp/Blocks[#{i}]/Name”, blockcontainer, page, 0)
        }
        width = p.get_pdi_value(“width”,blockcontainer,page,0)
        height = p.get_pdi_value(“height”,blockcontainer,page,0)
        #set page size
        p.begin_page_ext(@default_width, @default_height, “topdown”)
        # This will adjust the page size to the block container’s size.
        orient =  width>height ? “orientate west” : “”
        p.fit_pdi_page(page, 0, @default_height, #{orient})

        # Fill all text blocks with dynamic data
        cur_blocks.each { |key, value|
          use_font = p.get_pdi_parameter(“vdp/Blocks/#{key}/fontname”, blockcontainer, page, 0)
          if use_font == “ZapfDingbats”#is this block a checkbox?
            font_string = “font #{checkbox_font.to_s} embedding encoding=builtin”
            @data[key] = “” if @data[key] == @default_value
          else
            font_string =“font #{regularfont.to_s} embedding encoding=winansi”
          end
          if (p.fill_textblock(page, key, @data[key].to_s,font_string) == -1)
            logger.info “Warning: “ + p.get_errmsg
          end
        }

        p.end_page_ext(“”)
        p.close_pdi_page(page)
    }#end looping through all of the pages
    p.close_pdi(blockcontainer)
  end
end

Update: Tony Buser has modified this code by making it into a hash object. View it here

Jul22nd

Importing multiple pages into PDFLib

Nate Constant PDFLib, PDF, Rails, Ruby , , , Read on

So, as I’ve stated before, I’m creating an application that imports a large PDF into PDFlib so that i can populate textfields on each page from a database table.

PDFlib essentially imports one page at a time to work with. So here’s code to import a multipage PDF and populate the fields for each page.

def populate_pdf
  #setup some defaults
  infile = "alaska_pdflib.pdf"
  searchpath = "../state_doc_originals"

  #this data should pull from a database, the keys of the hash must be the same as the block names on the pdf
  data = {
    "last_name" => "Constant",
    "first_name" => "Nate",
    "middle_name" => "Andrew",
    "dob_m" => "09",
    "dob_y"=>"1980",
    "dob_d" => "18",
    "city_of_birth" => "Omaha",
    "state_of_birth" => "Nebraska",
    "gender_male" => "8"
  }
  p = PDFlib.new
  if (p.begin_document("", "compatibility = 1.6") == -1)
    logger.info "Error: " + p.get_errmsg()
  end

  # Set the search path for fonts and PDF files
  p.set_parameter("SearchPath", searchpath)
  p.set_parameter("pdiwarning", "true")
  p.set_info("Creator", "businesscard.rb")
  p.set_info("Author", "QuickDocData")
  p.set_info("Title", "Alaska State License Application")

  #open the document
  blockcontainer = p.open_pdi(infile, "", 0)
  if (blockcontainer == -1)
    logger.info "Error: " + p.get_errmsg()
  end
  regularfont = p.load_font("Arial", "winansi", "")
  checkbox_font = p.load_font("ZapfDingbats","builtin","")

  #loop through all pages and put data in the blocks
  number_of_pages = p.get_pdi_value("/Root/Pages/Count",blockcontainer,-1,0)
  1.upto(number_of_pages){|page_num|
    #get current page
    page = p.open_pdi_page(blockcontainer, page_num, "")
    if (page == -1)
        logger.info "Error: " + p.get_errmsg()
    end
    #get all blocks for the page and put them in an array to use later
    num_of_blocks_on_page = p.get_pdi_value("vdp/blockcount",blockcontainer,page,0)
    cur_blocks = Array.new

    0.upto(num_of_blocks_on_page-1){ |i|
      cur_blocks.push( p.get_pdi_parameter("vdp/Blocks[#{i}]/Name", blockcontainer, page, 0))
      #logger.info p.get_pdi_parameter("vdp/Blocks[#{i}]/Name", blockcontainer, page, 0)
    }

    #set page size
    p.begin_page_ext(595, 842, "topdown")
    # This will adjust the page size to the block container's size.
    p.fit_pdi_page(page, 0, 842, "")
    # Fill all text blocks with dynamic data

    cur_blocks.each { |key, value|
      use_font = p.get_pdi_parameter("vdp/Blocks/#{key}/fontname", blockcontainer, page, 0)
      if use_font == "ZapfDingbats"#is this block a checkbox?
        font_string = "font #{checkbox_font.to_s} embedding encoding=builtin"
      else
        font_string ="font #{regularfont.to_s} embedding encoding=winansi"
      end
      if (p.fill_textblock(page, key, data[key].to_s,font_string) == -1)
        logger.info "Warning: " + p.get_errmsg
      end
    }
    p.end_page_ext("")
    p.close_pdi_page(page)
  }#end looping through all of the pages

  p.close_pdi(blockcontainer)
  p.end_document("")
  p.get_buffer()
end
Jul22nd

Installation of PDFLib on Rails

Nate Constant PDFLib, PDF, Rails, Ruby , , , Read on

On a large project I’m working on we need to be able to import a (rather large) PDF and populate it’s fields from a database table, essentially. I looked into a few options like the built-in Ruby PDF generator PDF::Writer. PDF::Writer is good for generating PDF’s but not so good with importing existing documents for manipulation. Enter PDFLib. Unfortunately PDFLib is fairly expensive but it has everything I’m looking for. Probably one of the greatest features is it comes with a PDFLib Block Tool which allows you to add textfields anywhere on a PDF to be populated at a later time.

One nice thing about PDFLib is (for Ruby on Rails) you can install it into the plugins folder. This makes it so much easier than worrying about installing a package on your server, especially if your hosting your site on a shared environment (like Dreamhost).

I’m a fairly newer Ruby on Rails programmer and haven’t used plugins very much so I thought it would be tough to install. This proved not the case. Here’s the simple instructions:

1. Download the Ruby bindings from the PDFLib site depending on which system you’re using.

2. Extract the compressed file

3. Place the pdflib library file into your Rails App’s plugins vendor folder. On Mac OSX the library file is pdflib.library, on Linux the library file is pdflib.so.

If you’re like me, you do development on your local Mac system and upload the site to your host, which is a Linux/Apache server. In this case make sure to put the pdflib.so library file in the Rails app which is on the server and the pdflib.library file in the Rails app on your local machine.

That’s it!

Now that the library is installed, you can test the examples that come with pdflib and start to build your own PDF application.