pdf-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1e04dbc623a502e35e4ab9879b4e418a22799525
4
+ data.tar.gz: 8bcd0c9a82f9330fa074af80e43f8f371edba50f
5
+ SHA512:
6
+ metadata.gz: ede1498abbf6a6a93e1410cd019c320beb0b490a64ebbbf2bc1d13c2ba56e257e098db49da253444a7dbc70482a28b8ca1f8dd41bf9fa310a88080856a1445e5
7
+ data.tar.gz: d872d4b0b03aab9bd55bb3a1c2caa855280bbb6216e1ca89bcd0fd4aa9329f70893a846e0db53fd826a4069c1300e1f100369b88e7ba976d0d9ff49b50c4d9d7
data/lib/pdf/core.rb ADDED
@@ -0,0 +1,35 @@
1
+ require_relative "core/pdf_object"
2
+ require_relative "core/annotations"
3
+ require_relative "core/byte_string"
4
+ require_relative "core/destinations"
5
+ require_relative "core/filters"
6
+ require_relative "core/stream"
7
+ require_relative "core/reference"
8
+ require_relative "core/literal_string"
9
+ require_relative "core/filter_list"
10
+ require_relative "core/page"
11
+ require_relative "core/object_store"
12
+ require_relative "core/document_state"
13
+ require_relative "core/name_tree"
14
+ require_relative "core/graphics_state"
15
+ require_relative "core/page_geometry"
16
+ require_relative "core/outline"
17
+
18
+ module PDF
19
+ module Core
20
+ module Errors
21
+ # This error is raised when PdfObject() fails
22
+ FailedObjectConversion = Class.new(StandardError)
23
+
24
+ # This error is raised when object store fails to load a template file
25
+ TemplateError = Class.new(StandardError)
26
+
27
+ # This error is raise when trying to restore a graphic state that
28
+ EmptyGraphicStateStack = Class.new(StandardError)
29
+
30
+ # This error is raised when Document#page_layout is set to anything
31
+ # other than :portrait or :landscape
32
+ InvalidPageLayout = Class.new(StandardError)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ # annotations.rb : Implements low-level annotation support for PDF
4
+ #
5
+ # Copyright November 2008, Jamis Buck. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+ #
9
+ module PDF
10
+ module Core
11
+ # Provides very low-level support for annotations.
12
+ #
13
+ module Annotations #:nodoc:
14
+
15
+ # Adds a new annotation (section 8.4 in PDF spec) to the current page.
16
+ # +options+ must be a Hash describing the annotation.
17
+ #
18
+ def annotate(options)
19
+ state.page.dictionary.data[:Annots] ||= []
20
+ options = sanitize_annotation_hash(options)
21
+ state.page.dictionary.data[:Annots] << ref!(options)
22
+ return options
23
+ end
24
+
25
+ # A convenience method for creating Text annotations. +rect+ must be an array
26
+ # of four numbers, describing the bounds of the annotation. +contents+ should
27
+ # be a string, to be shown when the annotation is activated.
28
+ #
29
+ def text_annotation(rect, contents, options={})
30
+ options = options.merge(:Subtype => :Text, :Rect => rect, :Contents => contents)
31
+ annotate(options)
32
+ end
33
+
34
+ # A convenience method for creating Link annotations. +rect+ must be an array
35
+ # of four numbers, describing the bounds of the annotation. The +options+ hash
36
+ # should include either :Dest (describing the target destination, usually as a
37
+ # string that has been recorded in the document's Dests tree), or :A (describing
38
+ # an action to perform on clicking the link), or :PA (for describing a URL to
39
+ # link to).
40
+ #
41
+ def link_annotation(rect, options={})
42
+ options = options.merge(:Subtype => :Link, :Rect => rect)
43
+ annotate(options)
44
+ end
45
+
46
+ private
47
+
48
+ def sanitize_annotation_hash(options)
49
+ options = options.merge(:Type => :Annot)
50
+
51
+ if options[:Dest].is_a?(String)
52
+ options[:Dest] = PDF::Core::LiteralString.new(options[:Dest])
53
+ end
54
+
55
+ options
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+ module PDF
3
+ module Core
4
+ # This is used to differentiate strings that must be encoded as
5
+ # a byte string, such as binary data from encrypted strings.
6
+ class ByteString < String #:nodoc:
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,90 @@
1
+ # encoding: utf-8
2
+
3
+ # Implements destination support for PDF
4
+ #
5
+ # Copyright November 2008, Jamis Buck. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ module PDF
10
+ module Core
11
+ module Destinations #:nodoc:
12
+
13
+ # The maximum number of children to fit into a single node in the Dests tree.
14
+ NAME_TREE_CHILDREN_LIMIT = 20 #:nodoc:
15
+
16
+ # The Dests name tree in the Name dictionary (see Prawn::Document::Internal#names).
17
+ # This name tree is used to store named destinations (PDF spec 8.2.1).
18
+ # (For more on name trees, see section 3.8.4 in the PDF spec.)
19
+ #
20
+ def dests
21
+ names.data[:Dests] ||= ref!(PDF::Core::NameTree::Node.new(self, NAME_TREE_CHILDREN_LIMIT))
22
+ end
23
+
24
+ # Adds a new destination to the dests name tree (see #dests). The
25
+ # +reference+ parameter will be converted into a PDF::Core::Reference if
26
+ # it is not already one.
27
+ #
28
+ def add_dest(name, reference)
29
+ reference = ref!(reference) unless reference.is_a?(PDF::Core::Reference)
30
+ dests.data.add(name, reference)
31
+ end
32
+
33
+ # Return a Dest specification for a specific location (and optional zoom
34
+ # level).
35
+ #
36
+ def dest_xyz(left, top, zoom=nil, dest_page=page)
37
+ [dest_page.dictionary, :XYZ, left, top, zoom]
38
+ end
39
+
40
+ # Return a Dest specification that will fit the given page into the
41
+ # viewport.
42
+ #
43
+ def dest_fit(dest_page=page)
44
+ [dest_page.dictionary, :Fit]
45
+ end
46
+
47
+ # Return a Dest specification that will fit the given page horizontally
48
+ # into the viewport, aligned vertically at the given top coordinate.
49
+ #
50
+ def dest_fit_horizontally(top, dest_page=page)
51
+ [dest_page.dictionary, :FitH, top]
52
+ end
53
+
54
+ # Return a Dest specification that will fit the given page vertically
55
+ # into the viewport, aligned horizontally at the given left coordinate.
56
+ #
57
+ def dest_fit_vertically(left, dest_page=page)
58
+ [dest_page.dictionary, :FitV, left]
59
+ end
60
+
61
+ # Return a Dest specification that will fit the given rectangle into the
62
+ # viewport, for the given page.
63
+ #
64
+ def dest_fit_rect(left, bottom, right, top, dest_page=page)
65
+ [dest_page.dictionary, :FitR, left, bottom, right, top]
66
+ end
67
+
68
+ # Return a Dest specfication that will fit the given page's bounding box
69
+ # into the viewport.
70
+ #
71
+ def dest_fit_bounds(dest_page=page)
72
+ [dest_page.dictionary, :FitB]
73
+ end
74
+
75
+ # Same as #dest_fit_horizontally, but works on the page's bounding box
76
+ # instead of the entire page.
77
+ #
78
+ def dest_fit_bounds_horizontally(top, dest_page=page)
79
+ [dest_page.dictionary, :FitBH, top]
80
+ end
81
+
82
+ # Same as #dest_fit_vertically, but works on the page's bounding box
83
+ # instead of the entire page.
84
+ #
85
+ def dest_fit_bounds_vertically(left, dest_page=page)
86
+ [dest_page.dictionary, :FitBV, left]
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,78 @@
1
+ module PDF
2
+ module Core
3
+ class DocumentState #:nodoc:
4
+ def initialize(options)
5
+ normalize_metadata(options)
6
+
7
+ if options[:template]
8
+ @store = PDF::Core::ObjectStore.new(:template => options[:template])
9
+ @store.info.data.merge!(options[:info]) if options[:info]
10
+ else
11
+ @store = PDF::Core::ObjectStore.new(:info => options[:info])
12
+ end
13
+
14
+ @version = 1.3
15
+ @pages = []
16
+ @page = nil
17
+ @trailer = {}
18
+ @compress = options.fetch(:compress, false)
19
+ @encrypt = options.fetch(:encrypt, false)
20
+ @encryption_key = options[:encryption_key]
21
+ @optimize_objects = options.fetch(:optimize_objects, false)
22
+ @skip_encoding = options.fetch(:skip_encoding, false)
23
+ @before_render_callbacks = []
24
+ @on_page_create_callback = nil
25
+ end
26
+
27
+ attr_accessor :store, :version, :pages, :page, :trailer, :compress,
28
+ :encrypt, :encryption_key, :optimize_objects, :skip_encoding,
29
+ :before_render_callbacks, :on_page_create_callback
30
+
31
+ def populate_pages_from_store(document)
32
+ return 0 if @store.page_count <= 0 || @pages.size > 0
33
+
34
+ count = (1..@store.page_count)
35
+ @pages = count.map do |index|
36
+ orig_dict_id = @store.object_id_for_page(index)
37
+ PDF::Core::Page.new(document, :object_id => orig_dict_id)
38
+ end
39
+
40
+ end
41
+
42
+ def normalize_metadata(options)
43
+ options[:info] ||= {}
44
+ options[:info][:Creator] ||= "Prawn"
45
+ options[:info][:Producer] ||= "Prawn"
46
+
47
+ options[:info]
48
+ end
49
+
50
+ def insert_page(page, page_number)
51
+ pages.insert(page_number, page)
52
+ store.pages.data[:Kids].insert(page_number, page.dictionary)
53
+ store.pages.data[:Count] += 1
54
+ end
55
+
56
+ def on_page_create_action(doc)
57
+ on_page_create_callback[doc] if on_page_create_callback
58
+ end
59
+
60
+ def before_render_actions(doc)
61
+ before_render_callbacks.each{ |c| c.call(self) }
62
+ end
63
+
64
+ def page_count
65
+ pages.length
66
+ end
67
+
68
+ def render_body(output)
69
+ store.compact if optimize_objects
70
+ store.each do |ref|
71
+ ref.offset = output.size
72
+ output << (@encrypt ? ref.encrypted_object(@encryption_key) :
73
+ ref.object)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,51 @@
1
+ module PDF
2
+ module Core
3
+ class FilterList
4
+ def initialize
5
+ @list = []
6
+ end
7
+
8
+ def <<(filter)
9
+ case filter
10
+ when Symbol
11
+ @list << [filter, nil]
12
+ when ::Hash
13
+ filter.each do |name, params|
14
+ @list << [name, params]
15
+ end
16
+ else
17
+ raise "Can not interpret input as filter: #{filter.inspect}"
18
+ end
19
+
20
+ self
21
+ end
22
+
23
+ def normalized
24
+ @list
25
+ end
26
+ alias_method :to_a, :normalized
27
+
28
+ def names
29
+ @list.map do |(name, _)|
30
+ name
31
+ end
32
+ end
33
+
34
+ def decode_params
35
+ @list.map do |(_, params)|
36
+ params
37
+ end
38
+ end
39
+
40
+ def inspect
41
+ @list.inspect
42
+ end
43
+
44
+ def each(&block)
45
+ @list.each do |filter|
46
+ block.call(filter)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ # prawn/core/filters.rb : Implements stream filters
4
+ #
5
+ # Copyright February 2013, Alexander Mankuta. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+ require 'zlib'
10
+
11
+ module PDF
12
+ module Core
13
+ module Filters
14
+ module FlateDecode
15
+ def self.encode(stream, params = nil)
16
+ Zlib::Deflate.deflate(stream)
17
+ end
18
+
19
+ def self.decode(stream, params = nil)
20
+ Zlib::Inflate.inflate(stream)
21
+ end
22
+ end
23
+
24
+ # Pass through stub
25
+ module DCTDecode
26
+ def self.encode(stream, params = nil)
27
+ stream
28
+ end
29
+
30
+ def self.decode(stream, params = nil)
31
+ stream
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Implements graphics state saving and restoring
4
+ #
5
+ # Copyright January 2010, Michael Witrant. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details
8
+ #
9
+
10
+
11
+ module PDF
12
+ module Core
13
+ class GraphicStateStack
14
+ attr_accessor :stack
15
+
16
+ def initialize(previous_state = nil)
17
+ self.stack = [GraphicState.new(previous_state)]
18
+ end
19
+
20
+ def save_graphic_state(graphic_state = nil)
21
+ stack.push(GraphicState.new(graphic_state || current_state))
22
+ end
23
+
24
+ def restore_graphic_state
25
+ if stack.empty?
26
+ raise PDF::Core::Errors::EmptyGraphicStateStack,
27
+ "\n You have reached the end of the graphic state stack"
28
+ end
29
+ stack.pop
30
+ end
31
+
32
+ def current_state
33
+ stack.last
34
+ end
35
+
36
+ def present?
37
+ stack.size > 0
38
+ end
39
+
40
+ def empty?
41
+ stack.empty?
42
+ end
43
+
44
+ end
45
+
46
+ class GraphicState
47
+ attr_accessor :color_space, :dash, :cap_style, :join_style, :line_width, :fill_color, :stroke_color
48
+
49
+ def initialize(previous_state = nil)
50
+ @color_space = previous_state ? previous_state.color_space.dup : {}
51
+ @fill_color = previous_state ? previous_state.fill_color : "000000"
52
+ @stroke_color = previous_state ? previous_state.stroke_color : "000000"
53
+ @dash = previous_state ? previous_state.dash : { :dash => nil, :space => nil, :phase => 0 }
54
+ @cap_style = previous_state ? previous_state.cap_style : :butt
55
+ @join_style = previous_state ? previous_state.join_style : :miter
56
+ @line_width = previous_state ? previous_state.line_width : 1
57
+ end
58
+
59
+ def dash_setting
60
+ if @dash[:dash].kind_of?(Array)
61
+ "[#{@dash[:dash].join(' ')}] #{@dash[:phase]} d"
62
+ else
63
+ "[#{@dash[:dash]} #{@dash[:space]}] #{@dash[:phase]} d"
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end