Skip to content

Commit

Permalink
Move Prawn::Core into PDF namespace.
Browse files Browse the repository at this point in the history
This commit does not address the circular dependencies that exist between
Prawn and its core API, but it does pass the tests and generate the manual!
  • Loading branch information
practicingruby authored and Jia Brown committed Nov 29, 2013
1 parent def8464 commit 2c321b2
Show file tree
Hide file tree
Showing 57 changed files with 2,732 additions and 2,763 deletions.
22 changes: 22 additions & 0 deletions lib/pdf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# encoding: utf-8
# Prawn : A library for PDF generation in Ruby
#
# Copyright April 2008, Gregory Brown. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.


require_relative "pdf/pdf_object"
require_relative "pdf/filters"
require_relative "pdf/filter_list"
require_relative "pdf/stream"
require_relative "pdf/reference"
require_relative "pdf/page"
require_relative "pdf/object_store"
require_relative "pdf/document_state"
require_relative "pdf/literal_string"
require_relative "pdf/byte_string"
require_relative "pdf/name_tree"
require_relative "pdf/annotations"
require_relative "pdf/destinations"

58 changes: 58 additions & 0 deletions lib/pdf/annotations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# encoding: utf-8

# annotations.rb : Implements low-level annotation support for PDF
#
# Copyright November 2008, Jamis Buck. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
#
module PDF
# Provides very low-level support for annotations.
#
module Annotations #:nodoc:

# Adds a new annotation (section 8.4 in PDF spec) to the current page.
# +options+ must be a Hash describing the annotation.
#
def annotate(options)
state.page.dictionary.data[:Annots] ||= []
options = sanitize_annotation_hash(options)
state.page.dictionary.data[:Annots] << ref!(options)
return options
end

# A convenience method for creating Text annotations. +rect+ must be an array
# of four numbers, describing the bounds of the annotation. +contents+ should
# be a string, to be shown when the annotation is activated.
#
def text_annotation(rect, contents, options={})
options = options.merge(:Subtype => :Text, :Rect => rect, :Contents => contents)
annotate(options)
end

# A convenience method for creating Link annotations. +rect+ must be an array
# of four numbers, describing the bounds of the annotation. The +options+ hash
# should include either :Dest (describing the target destination, usually as a
# string that has been recorded in the document's Dests tree), or :A (describing
# an action to perform on clicking the link), or :PA (for describing a URL to
# link to).
#
def link_annotation(rect, options={})
options = options.merge(:Subtype => :Link, :Rect => rect)
annotate(options)
end

private

def sanitize_annotation_hash(options)
options = options.merge(:Type => :Annot)

if options[:Dest].is_a?(String)
options[:Dest] = PDF::LiteralString.new(options[:Dest])
end

options
end

end
end
7 changes: 7 additions & 0 deletions lib/pdf/byte_string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# encoding: utf-8
module PDF
# This is used to differentiate strings that must be encoded as
# a byte string, such as binary data from encrypted strings.
class ByteString < String #:nodoc:
end
end
88 changes: 88 additions & 0 deletions lib/pdf/destinations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# encoding: utf-8

# prawn/core/destinations.rb : Implements destination support for PDF
#
# Copyright November 2008, Jamis Buck. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.

module PDF
module Destinations #:nodoc:

# The maximum number of children to fit into a single node in the Dests tree.
NAME_TREE_CHILDREN_LIMIT = 20 #:nodoc:

# The Dests name tree in the Name dictionary (see Prawn::Document::Internal#names).
# This name tree is used to store named destinations (PDF spec 8.2.1).
# (For more on name trees, see section 3.8.4 in the PDF spec.)
#
def dests
names.data[:Dests] ||= ref!(PDF::NameTree::Node.new(self, NAME_TREE_CHILDREN_LIMIT))
end

# Adds a new destination to the dests name tree (see #dests). The
# +reference+ parameter will be converted into a Prawn::Reference if
# it is not already one.
#
def add_dest(name, reference)
reference = ref!(reference) unless reference.is_a?(PDF::Reference)
dests.data.add(name, reference)
end

# Return a Dest specification for a specific location (and optional zoom
# level).
#
def dest_xyz(left, top, zoom=nil, dest_page=page)
[dest_page.dictionary, :XYZ, left, top, zoom]
end

# Return a Dest specification that will fit the given page into the
# viewport.
#
def dest_fit(dest_page=page)
[dest_page.dictionary, :Fit]
end

# Return a Dest specification that will fit the given page horizontally
# into the viewport, aligned vertically at the given top coordinate.
#
def dest_fit_horizontally(top, dest_page=page)
[dest_page.dictionary, :FitH, top]
end

# Return a Dest specification that will fit the given page vertically
# into the viewport, aligned horizontally at the given left coordinate.
#
def dest_fit_vertically(left, dest_page=page)
[dest_page.dictionary, :FitV, left]
end

# Return a Dest specification that will fit the given rectangle into the
# viewport, for the given page.
#
def dest_fit_rect(left, bottom, right, top, dest_page=page)
[dest_page.dictionary, :FitR, left, bottom, right, top]
end

# Return a Dest specfication that will fit the given page's bounding box
# into the viewport.
#
def dest_fit_bounds(dest_page=page)
[dest_page.dictionary, :FitB]
end

# Same as #dest_fit_horizontally, but works on the page's bounding box
# instead of the entire page.
#
def dest_fit_bounds_horizontally(top, dest_page=page)
[dest_page.dictionary, :FitBH, top]
end

# Same as #dest_fit_vertically, but works on the page's bounding box
# instead of the entire page.
#
def dest_fit_bounds_vertically(left, dest_page=page)
[dest_page.dictionary, :FitBV, left]
end
end
end
77 changes: 77 additions & 0 deletions lib/pdf/document_state.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
module PDF
class DocumentState #:nodoc:
def initialize(options)
normalize_metadata(options)

if options[:template]
@store = PDF::ObjectStore.new(:template => options[:template])
@store.info.data.merge!(options[:info]) if options[:info]
else
@store = PDF::ObjectStore.new(:info => options[:info])
end

@version = 1.3
@pages = []
@page = nil
@trailer = {}
@compress = options.fetch(:compress, false)
@encrypt = options.fetch(:encrypt, false)
@encryption_key = options[:encryption_key]
@optimize_objects = options.fetch(:optimize_objects, false)
@skip_encoding = options.fetch(:skip_encoding, false)
@before_render_callbacks = []
@on_page_create_callback = nil
end

attr_accessor :store, :version, :pages, :page, :trailer, :compress,
:encrypt, :encryption_key, :optimize_objects, :skip_encoding,
:before_render_callbacks, :on_page_create_callback

def populate_pages_from_store(document)
return 0 if @store.page_count <= 0 || @pages.size > 0

count = (1..@store.page_count)
@pages = count.map do |index|
orig_dict_id = @store.object_id_for_page(index)
PDF::Page.new(document, :object_id => orig_dict_id)
end

end

def normalize_metadata(options)
options[:info] ||= {}
options[:info][:Creator] ||= "Prawn"
options[:info][:Producer] ||= "Prawn"

options[:info]
end

def insert_page(page, page_number)
pages.insert(page_number, page)
store.pages.data[:Kids].insert(page_number, page.dictionary)
store.pages.data[:Count] += 1
end

def on_page_create_action(doc)
on_page_create_callback[doc] if on_page_create_callback
end

def before_render_actions(doc)
before_render_callbacks.each{ |c| c.call(self) }
end

def page_count
pages.length
end

def render_body(output)
store.compact if optimize_objects
store.each do |ref|
ref.offset = output.size
output << (@encrypt ? ref.encrypted_object(@encryption_key) :
ref.object)
end
end

end
end
49 changes: 49 additions & 0 deletions lib/pdf/filter_list.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module PDF
class FilterList
def initialize
@list = []
end

def <<(filter)
case filter
when Symbol
@list << [filter, nil]
when ::Hash
filter.each do |name, params|
@list << [name, params]
end
else
raise "Can not interpret input as filter: #{filter.inspect}"
end

self
end

def normalized
@list
end
alias_method :to_a, :normalized

def names
@list.map do |(name, _)|
name
end
end

def decode_params
@list.map do |(_, params)|
params
end
end

def inspect
@list.inspect
end

def each(&block)
@list.each do |filter|
block.call(filter)
end
end
end
end
34 changes: 34 additions & 0 deletions lib/pdf/filters.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# encoding: utf-8

# prawn/core/filters.rb : Implements stream filters
#
# Copyright February 2013, Alexander Mankuta. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.

require 'zlib'

module PDF
module Filters
module FlateDecode
def self.encode(stream, params = nil)
Zlib::Deflate.deflate(stream)
end

def self.decode(stream, params = nil)
Zlib::Inflate.inflate(stream)
end
end

# Pass through stub
module DCTDecode
def self.encode(stream, params = nil)
stream
end

def self.decode(stream, params = nil)
stream
end
end
end
end
14 changes: 14 additions & 0 deletions lib/pdf/literal_string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# encoding: utf-8
module PDF
# This is used to differentiate strings that must be encoded as
# a *literal* string, versus those that can be encoded in
# the PDF hexadecimal format.
#
# Some features of the PDF format appear to require that literal
# strings be used. One such feature is the /Dest key of a link
# annotation; if a hex encoded string is used there, the links
# do not work (as tested in Mac OS X Preview, and Adobe Acrobat
# Reader).
class LiteralString < String #:nodoc:
end
end
Loading

0 comments on commit 2c321b2

Please sign in to comment.