piglop-prawn 0.10.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +340 -0
- data/HACKING +49 -0
- data/LICENSE +56 -0
- data/README +140 -0
- data/Rakefile +52 -0
- data/data/encodings/win_ansi.txt +29 -0
- data/data/fonts/Action Man.dfont +0 -0
- data/data/fonts/Activa.ttf +0 -0
- data/data/fonts/Chalkboard.ttf +0 -0
- data/data/fonts/Courier-Bold.afm +342 -0
- data/data/fonts/Courier-BoldOblique.afm +342 -0
- data/data/fonts/Courier-Oblique.afm +342 -0
- data/data/fonts/Courier.afm +342 -0
- data/data/fonts/DejaVuSans.ttf +0 -0
- data/data/fonts/Dustismo_Roman.ttf +0 -0
- data/data/fonts/Helvetica-Bold.afm +2827 -0
- data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
- data/data/fonts/Helvetica-Oblique.afm +3051 -0
- data/data/fonts/Helvetica.afm +3051 -0
- data/data/fonts/MustRead.html +19 -0
- data/data/fonts/Symbol.afm +213 -0
- data/data/fonts/Times-Bold.afm +2588 -0
- data/data/fonts/Times-BoldItalic.afm +2384 -0
- data/data/fonts/Times-Italic.afm +2667 -0
- data/data/fonts/Times-Roman.afm +2419 -0
- data/data/fonts/ZapfDingbats.afm +225 -0
- data/data/fonts/comicsans.ttf +0 -0
- data/data/fonts/gkai00mp.ttf +0 -0
- data/data/images/16bit.alpha +0 -0
- data/data/images/16bit.dat +0 -0
- data/data/images/16bit.png +0 -0
- data/data/images/arrow.png +0 -0
- data/data/images/arrow2.png +0 -0
- data/data/images/barcode_issue.png +0 -0
- data/data/images/dice.alpha +0 -0
- data/data/images/dice.dat +0 -0
- data/data/images/dice.png +0 -0
- data/data/images/dice_interlaced.png +0 -0
- data/data/images/fractal.jpg +0 -0
- data/data/images/letterhead.jpg +0 -0
- data/data/images/page_white_text.alpha +0 -0
- data/data/images/page_white_text.dat +0 -0
- data/data/images/page_white_text.png +0 -0
- data/data/images/pigs.jpg +0 -0
- data/data/images/rails.dat +0 -0
- data/data/images/rails.png +0 -0
- data/data/images/ruport.png +0 -0
- data/data/images/ruport_data.dat +0 -0
- data/data/images/ruport_transparent.png +0 -0
- data/data/images/ruport_type0.png +0 -0
- data/data/images/stef.jpg +0 -0
- data/data/images/tru256.bmp +0 -0
- data/data/images/web-links.dat +1 -0
- data/data/images/web-links.png +0 -0
- data/data/pdfs/complex_template.pdf +0 -0
- data/data/pdfs/contains_ttf_font.pdf +0 -0
- data/data/pdfs/encrypted.pdf +0 -0
- data/data/pdfs/hexagon.pdf +61 -0
- data/data/pdfs/indirect_reference.pdf +86 -0
- data/data/pdfs/nested_pages.pdf +118 -0
- data/data/pdfs/resources_as_indirect_object.pdf +83 -0
- data/data/pdfs/two_hexagons.pdf +90 -0
- data/data/pdfs/version_1_6.pdf +61 -0
- data/data/shift_jis_text.txt +1 -0
- data/examples/bounding_box/bounding_boxes.rb +43 -0
- data/examples/bounding_box/indentation.rb +34 -0
- data/examples/bounding_box/russian_boxes.rb +36 -0
- data/examples/bounding_box/stretched_nesting.rb +67 -0
- data/examples/example_helper.rb +4 -0
- data/examples/general/background.rb +23 -0
- data/examples/general/canvas.rb +15 -0
- data/examples/general/context_sensitive_headers.rb +37 -0
- data/examples/general/float.rb +11 -0
- data/examples/general/margin.rb +36 -0
- data/examples/general/measurement_units.rb +51 -0
- data/examples/general/metadata-info.rb +16 -0
- data/examples/general/multi_page_layout.rb +18 -0
- data/examples/general/outlines.rb +50 -0
- data/examples/general/page_geometry.rb +31 -0
- data/examples/general/page_numbering.rb +15 -0
- data/examples/general/repeaters.rb +47 -0
- data/examples/general/stamp.rb +41 -0
- data/examples/general/templates.rb +13 -0
- data/examples/graphics/basic_images.rb +23 -0
- data/examples/graphics/cmyk.rb +12 -0
- data/examples/graphics/curves.rb +11 -0
- data/examples/graphics/hexagon.rb +13 -0
- data/examples/graphics/image_fit.rb +15 -0
- data/examples/graphics/image_flow.rb +37 -0
- data/examples/graphics/image_position.rb +17 -0
- data/examples/graphics/line.rb +32 -0
- data/examples/graphics/png_types.rb +22 -0
- data/examples/graphics/polygons.rb +16 -0
- data/examples/graphics/remote_images.rb +12 -0
- data/examples/graphics/rounded_polygons.rb +19 -0
- data/examples/graphics/rounded_rectangle.rb +20 -0
- data/examples/graphics/ruport_style_helpers.rb +19 -0
- data/examples/graphics/stroke_bounds.rb +20 -0
- data/examples/graphics/stroke_cap_and_join.rb +45 -0
- data/examples/graphics/stroke_dash.rb +42 -0
- data/examples/graphics/transformations.rb +52 -0
- data/examples/graphics/transparency.rb +26 -0
- data/examples/m17n/chinese_text_wrapping.rb +17 -0
- data/examples/m17n/euro.rb +15 -0
- data/examples/m17n/sjis.rb +28 -0
- data/examples/m17n/utf8.rb +13 -0
- data/examples/m17n/win_ansi_charset.rb +54 -0
- data/examples/security/hello_foo.rb +8 -0
- data/examples/table/bill.rb +53 -0
- data/examples/table/cell.rb +12 -0
- data/examples/table/checkerboard.rb +22 -0
- data/examples/table/header.rb +14 -0
- data/examples/table/inline_format_table.rb +12 -0
- data/examples/table/multi_page_table.rb +9 -0
- data/examples/table/simple_table.rb +24 -0
- data/examples/table/subtable.rb +12 -0
- data/examples/table/widths.rb +20 -0
- data/examples/text/alignment.rb +18 -0
- data/examples/text/dfont.rb +48 -0
- data/examples/text/family_based_styling.rb +24 -0
- data/examples/text/font_calculations.rb +91 -0
- data/examples/text/font_size.rb +33 -0
- data/examples/text/hyphenation.rb +45 -0
- data/examples/text/indent_paragraphs.rb +18 -0
- data/examples/text/inline_format.rb +72 -0
- data/examples/text/kerning.rb +30 -0
- data/examples/text/rotated.rb +98 -0
- data/examples/text/shaped_text_box.rb +31 -0
- data/examples/text/simple_text.rb +17 -0
- data/examples/text/simple_text_ttf.rb +17 -0
- data/examples/text/text_box.rb +88 -0
- data/examples/text/text_box_returning_excess.rb +51 -0
- data/examples/text/text_flow.rb +67 -0
- data/lib/prawn/compatibility.rb +51 -0
- data/lib/prawn/core/annotations.rb +61 -0
- data/lib/prawn/core/byte_string.rb +9 -0
- data/lib/prawn/core/destinations.rb +90 -0
- data/lib/prawn/core/document_state.rb +84 -0
- data/lib/prawn/core/literal_string.rb +16 -0
- data/lib/prawn/core/name_tree.rb +165 -0
- data/lib/prawn/core/object_store.rb +219 -0
- data/lib/prawn/core/page.rb +179 -0
- data/lib/prawn/core/pdf_object.rb +83 -0
- data/lib/prawn/core/reference.rb +112 -0
- data/lib/prawn/core/text/formatted/arranger.rb +260 -0
- data/lib/prawn/core/text/formatted/line_wrap.rb +127 -0
- data/lib/prawn/core/text/formatted/wrap.rb +113 -0
- data/lib/prawn/core/text/line_wrap.rb +198 -0
- data/lib/prawn/core/text/wrap.rb +80 -0
- data/lib/prawn/core/text.rb +115 -0
- data/lib/prawn/core.rb +89 -0
- data/lib/prawn/document/bounding_box.rb +425 -0
- data/lib/prawn/document/graphics_state.rb +48 -0
- data/lib/prawn/document/internals.rb +176 -0
- data/lib/prawn/document/page_geometry.rb +136 -0
- data/lib/prawn/document/snapshot.rb +87 -0
- data/lib/prawn/document.rb +573 -0
- data/lib/prawn/encoding.rb +121 -0
- data/lib/prawn/errors.rb +94 -0
- data/lib/prawn/font/afm.rb +225 -0
- data/lib/prawn/font/dfont.rb +42 -0
- data/lib/prawn/font/ttf.rb +350 -0
- data/lib/prawn/font.rb +342 -0
- data/lib/prawn/graphics/cap_style.rb +38 -0
- data/lib/prawn/graphics/color.rb +205 -0
- data/lib/prawn/graphics/dash.rb +71 -0
- data/lib/prawn/graphics/join_style.rb +38 -0
- data/lib/prawn/graphics/transformation.rb +156 -0
- data/lib/prawn/graphics/transparency.rb +99 -0
- data/lib/prawn/graphics.rb +321 -0
- data/lib/prawn/images/jpg.rb +46 -0
- data/lib/prawn/images/png.rb +226 -0
- data/lib/prawn/images.rb +348 -0
- data/lib/prawn/measurement_extensions.rb +46 -0
- data/lib/prawn/measurements.rb +71 -0
- data/lib/prawn/outline.rb +278 -0
- data/lib/prawn/repeater.rb +129 -0
- data/lib/prawn/security/arcfour.rb +51 -0
- data/lib/prawn/security.rb +262 -0
- data/lib/prawn/stamp.rb +126 -0
- data/lib/prawn/table/accessors.rb +180 -0
- data/lib/prawn/table/cell/in_table.rb +27 -0
- data/lib/prawn/table/cell/subtable.rb +65 -0
- data/lib/prawn/table/cell/text.rb +128 -0
- data/lib/prawn/table/cell.rb +330 -0
- data/lib/prawn/table.rb +421 -0
- data/lib/prawn/text/box.rb +380 -0
- data/lib/prawn/text/formatted/box.rb +215 -0
- data/lib/prawn/text/formatted/fragment.rb +127 -0
- data/lib/prawn/text/formatted/parser.rb +202 -0
- data/lib/prawn/text/formatted.rb +4 -0
- data/lib/prawn/text.rb +442 -0
- data/lib/prawn.rb +25 -0
- data/spec/annotations_spec.rb +90 -0
- data/spec/bounding_box_spec.rb +190 -0
- data/spec/cell_spec.rb +337 -0
- data/spec/destinations_spec.rb +15 -0
- data/spec/document_spec.rb +461 -0
- data/spec/font_spec.rb +314 -0
- data/spec/formatted_text_arranger_spec.rb +400 -0
- data/spec/formatted_text_box_spec.rb +659 -0
- data/spec/formatted_text_fragment_spec.rb +211 -0
- data/spec/graphics_spec.rb +446 -0
- data/spec/images_spec.rb +96 -0
- data/spec/inline_formatted_text_parser_spec.rb +446 -0
- data/spec/jpg_spec.rb +25 -0
- data/spec/line_wrap_spec.rb +341 -0
- data/spec/measurement_units_spec.rb +23 -0
- data/spec/name_tree_spec.rb +112 -0
- data/spec/object_store_spec.rb +160 -0
- data/spec/outline_spec.rb +229 -0
- data/spec/pdf_object_spec.rb +127 -0
- data/spec/png_spec.rb +237 -0
- data/spec/reference_spec.rb +82 -0
- data/spec/repeater_spec.rb +96 -0
- data/spec/security_spec.rb +120 -0
- data/spec/snapshot_spec.rb +138 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/stamp_spec.rb +108 -0
- data/spec/stroke_styles_spec.rb +163 -0
- data/spec/table_spec.rb +583 -0
- data/spec/template_spec.rb +158 -0
- data/spec/text_at_spec.rb +119 -0
- data/spec/text_box_spec.rb +675 -0
- data/spec/text_spacing_spec.rb +57 -0
- data/spec/text_spec.rb +328 -0
- data/spec/text_with_inline_formatting_spec.rb +193 -0
- data/spec/transparency_spec.rb +75 -0
- data/vendor/pdf-inspector/README +18 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/extgstate.rb +18 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +131 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/page.rb +25 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/text.rb +46 -0
- data/vendor/pdf-inspector/lib/pdf/inspector/xobject.rb +19 -0
- data/vendor/pdf-inspector/lib/pdf/inspector.rb +26 -0
- data/vendor/ttfunk/data/fonts/DejaVuSans.ttf +0 -0
- data/vendor/ttfunk/data/fonts/comicsans.ttf +0 -0
- data/vendor/ttfunk/example.rb +45 -0
- data/vendor/ttfunk/lib/ttfunk/directory.rb +17 -0
- data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +88 -0
- data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +69 -0
- data/vendor/ttfunk/lib/ttfunk/reader.rb +44 -0
- data/vendor/ttfunk/lib/ttfunk/resource_file.rb +78 -0
- data/vendor/ttfunk/lib/ttfunk/subset/base.rb +141 -0
- data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +50 -0
- data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +48 -0
- data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +63 -0
- data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +55 -0
- data/vendor/ttfunk/lib/ttfunk/subset.rb +18 -0
- data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +72 -0
- data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +54 -0
- data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +126 -0
- data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +79 -0
- data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +34 -0
- data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +81 -0
- data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +37 -0
- data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +64 -0
- data/vendor/ttfunk/lib/ttfunk/table/head.rb +44 -0
- data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +41 -0
- data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +47 -0
- data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +62 -0
- data/vendor/ttfunk/lib/ttfunk/table/kern.rb +79 -0
- data/vendor/ttfunk/lib/ttfunk/table/loca.rb +43 -0
- data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +40 -0
- data/vendor/ttfunk/lib/ttfunk/table/name.rb +125 -0
- data/vendor/ttfunk/lib/ttfunk/table/os2.rb +78 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +43 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +35 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +23 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +17 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +17 -0
- data/vendor/ttfunk/lib/ttfunk/table/post.rb +91 -0
- data/vendor/ttfunk/lib/ttfunk/table/simple.rb +14 -0
- data/vendor/ttfunk/lib/ttfunk/table.rb +46 -0
- data/vendor/ttfunk/lib/ttfunk.rb +102 -0
- metadata +364 -0
@@ -0,0 +1,165 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# name_tree.rb : Implements NameTree 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 Prawn
|
10
|
+
module Core
|
11
|
+
module NameTree #:nodoc:
|
12
|
+
class Node #:nodoc:
|
13
|
+
attr_reader :children
|
14
|
+
attr_reader :limit
|
15
|
+
attr_reader :document
|
16
|
+
attr_accessor :parent
|
17
|
+
attr_accessor :ref
|
18
|
+
|
19
|
+
def initialize(document, limit, parent=nil)
|
20
|
+
@document = document
|
21
|
+
@children = []
|
22
|
+
@limit = limit
|
23
|
+
@parent = parent
|
24
|
+
@ref = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def empty?
|
28
|
+
children.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def size
|
32
|
+
leaf? ? children.size : children.inject(0) { |sum, child| sum + child.size }
|
33
|
+
end
|
34
|
+
|
35
|
+
def leaf?
|
36
|
+
children.empty? || children.first.is_a?(Value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def add(name, value)
|
40
|
+
self << Value.new(name, value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_hash
|
44
|
+
hash = {}
|
45
|
+
|
46
|
+
hash[:Limits] = [least, greatest] if parent
|
47
|
+
if leaf?
|
48
|
+
hash[:Names] = children if leaf?
|
49
|
+
else
|
50
|
+
hash[:Kids] = children.map { |child| child.ref }
|
51
|
+
end
|
52
|
+
|
53
|
+
return hash
|
54
|
+
end
|
55
|
+
|
56
|
+
def least
|
57
|
+
if leaf?
|
58
|
+
children.first.name
|
59
|
+
else
|
60
|
+
children.first.least
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def greatest
|
65
|
+
if leaf?
|
66
|
+
children.last.name
|
67
|
+
else
|
68
|
+
children.last.greatest
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def <<(value)
|
73
|
+
if children.empty?
|
74
|
+
children << value
|
75
|
+
elsif leaf?
|
76
|
+
children.insert(insertion_point(value), value)
|
77
|
+
split! if children.length > limit
|
78
|
+
else
|
79
|
+
fit = children.detect { |child| child >= value }
|
80
|
+
fit = children.last unless fit
|
81
|
+
fit << value
|
82
|
+
end
|
83
|
+
|
84
|
+
value
|
85
|
+
end
|
86
|
+
|
87
|
+
def >=(value)
|
88
|
+
children.empty? || children.last >= value
|
89
|
+
end
|
90
|
+
|
91
|
+
def split!
|
92
|
+
if parent
|
93
|
+
parent.split(self)
|
94
|
+
else
|
95
|
+
left, right = new_node(self), new_node(self)
|
96
|
+
split_children(self, left, right)
|
97
|
+
children.replace([left, right])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
def split(node)
|
104
|
+
new_child = new_node(self)
|
105
|
+
split_children(node, node, new_child)
|
106
|
+
index = children.index(node)
|
107
|
+
children.insert(index+1, new_child)
|
108
|
+
split! if children.length > limit
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def new_node(parent=nil)
|
114
|
+
node = Node.new(document, limit, parent)
|
115
|
+
node.ref = document.ref!(node)
|
116
|
+
return node
|
117
|
+
end
|
118
|
+
|
119
|
+
def split_children(node, left, right)
|
120
|
+
half = (node.limit+1)/2
|
121
|
+
|
122
|
+
left_children, right_children = node.children[0...half], node.children[half..-1]
|
123
|
+
|
124
|
+
left.children.replace(left_children)
|
125
|
+
right.children.replace(right_children)
|
126
|
+
|
127
|
+
unless node.leaf?
|
128
|
+
left_children.each { |child| child.parent = left }
|
129
|
+
right_children.each { |child| child.parent = right }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def insertion_point(value)
|
134
|
+
children.each_with_index do |child, index|
|
135
|
+
return index if child >= value
|
136
|
+
end
|
137
|
+
return children.length
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class Value #:nodoc:
|
142
|
+
include Comparable
|
143
|
+
|
144
|
+
attr_reader :name
|
145
|
+
attr_reader :value
|
146
|
+
|
147
|
+
def initialize(name, value)
|
148
|
+
@name, @value = Prawn::Core::LiteralString.new(name), value
|
149
|
+
end
|
150
|
+
|
151
|
+
def <=>(leaf)
|
152
|
+
name <=> leaf.name
|
153
|
+
end
|
154
|
+
|
155
|
+
def inspect
|
156
|
+
"#<Value: #{name.inspect} : #{value.inspect}>"
|
157
|
+
end
|
158
|
+
|
159
|
+
def to_s
|
160
|
+
"#{name} : #{value}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# prawn/core/object_store.rb : Implements PDF object repository for Prawn
|
4
|
+
#
|
5
|
+
# Copyright August 2009, Brad Ediger. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
|
10
|
+
require 'pdf/reader'
|
11
|
+
|
12
|
+
module Prawn
|
13
|
+
module Core
|
14
|
+
class ObjectStore #:nodoc:
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
attr_reader :min_version
|
18
|
+
|
19
|
+
BASE_OBJECTS = %w[info pages root]
|
20
|
+
|
21
|
+
def initialize(opts = {})
|
22
|
+
@objects = {}
|
23
|
+
@identifiers = []
|
24
|
+
|
25
|
+
load_file(opts[:template]) if opts[:template]
|
26
|
+
|
27
|
+
@info ||= ref(opts[:info] || {}).identifier
|
28
|
+
@root ||= ref(:Type => :Catalog).identifier
|
29
|
+
if pages.nil?
|
30
|
+
root.data[:Pages] = ref(:Type => :Pages, :Count => 0, :Kids => [])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ref(data, &block)
|
35
|
+
push(size + 1, data, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def info
|
39
|
+
@objects[@info]
|
40
|
+
end
|
41
|
+
|
42
|
+
def root
|
43
|
+
@objects[@root]
|
44
|
+
end
|
45
|
+
|
46
|
+
def pages
|
47
|
+
root.data[:Pages]
|
48
|
+
end
|
49
|
+
|
50
|
+
def page_count
|
51
|
+
pages.data[:Count]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Adds the given reference to the store and returns the reference object.
|
55
|
+
# If the object provided is not a Prawn::Core::Reference, one is created from the
|
56
|
+
# arguments provided.
|
57
|
+
#
|
58
|
+
def push(*args, &block)
|
59
|
+
reference = if args.first.is_a?(Prawn::Core::Reference)
|
60
|
+
args.first
|
61
|
+
else
|
62
|
+
Prawn::Core::Reference.new(*args, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
@objects[reference.identifier] = reference
|
66
|
+
@identifiers << reference.identifier
|
67
|
+
reference
|
68
|
+
end
|
69
|
+
|
70
|
+
alias_method :<<, :push
|
71
|
+
|
72
|
+
def each
|
73
|
+
@identifiers.each do |id|
|
74
|
+
yield @objects[id]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def [](id)
|
79
|
+
@objects[id]
|
80
|
+
end
|
81
|
+
|
82
|
+
def size
|
83
|
+
@identifiers.size
|
84
|
+
end
|
85
|
+
alias_method :length, :size
|
86
|
+
|
87
|
+
def compact
|
88
|
+
# Clear live markers
|
89
|
+
each { |o| o.live = false }
|
90
|
+
|
91
|
+
# Recursively mark reachable objects live, starting from the roots
|
92
|
+
# (the only objects referenced in the trailer)
|
93
|
+
root.mark_live
|
94
|
+
info.mark_live
|
95
|
+
|
96
|
+
# Renumber live objects to eliminate gaps (shrink the xref table)
|
97
|
+
if @objects.any?{ |_, o| !o.live }
|
98
|
+
new_id = 1
|
99
|
+
new_objects = {}
|
100
|
+
new_identifiers = []
|
101
|
+
|
102
|
+
each do |obj|
|
103
|
+
if obj.live
|
104
|
+
obj.identifier = new_id
|
105
|
+
new_objects[new_id] = obj
|
106
|
+
new_identifiers << new_id
|
107
|
+
new_id += 1
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
@objects = new_objects
|
112
|
+
@identifiers = new_identifiers
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# returns the object ID for a particular page in the document. Pages
|
117
|
+
# are indexed starting at 1 (not 0!).
|
118
|
+
#
|
119
|
+
# object_id_for_page(1)
|
120
|
+
# => 5
|
121
|
+
# object_id_for_page(10)
|
122
|
+
# => 87
|
123
|
+
# object_id_for_page(-11)
|
124
|
+
# => 17
|
125
|
+
#
|
126
|
+
def object_id_for_page(k)
|
127
|
+
k -= 1 if k > 0
|
128
|
+
flat_page_ids = get_page_objects(pages).flatten
|
129
|
+
flat_page_ids[k]
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# returns a nested array of object IDs for all pages in this object store.
|
135
|
+
#
|
136
|
+
def get_page_objects(obj)
|
137
|
+
if obj.data[:Type] == :Page
|
138
|
+
obj.identifier
|
139
|
+
elsif obj.data[:Type] == :Pages
|
140
|
+
obj.data[:Kids].map { |kid| get_page_objects(kid) }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# takes a source PDF and uses it as a template for this document.
|
145
|
+
#
|
146
|
+
def load_file(filename)
|
147
|
+
unless File.file?(filename)
|
148
|
+
raise ArgumentError, "#{filename} does not exist"
|
149
|
+
end
|
150
|
+
|
151
|
+
unless PDF.const_defined?("Hash")
|
152
|
+
raise "PDF::Hash not found. Is PDF::Reader > 0.8?"
|
153
|
+
end
|
154
|
+
|
155
|
+
hash = PDF::Hash.new(filename)
|
156
|
+
src_info = hash.trailer[:Info]
|
157
|
+
src_root = hash.trailer[:Root]
|
158
|
+
@min_version = hash.version.to_f
|
159
|
+
|
160
|
+
if hash.trailer[:Encrypt]
|
161
|
+
msg = "Template file is an encrypted PDF, it can't be used as a template"
|
162
|
+
raise Prawn::Errors::TemplateError, msg
|
163
|
+
end
|
164
|
+
|
165
|
+
if src_info
|
166
|
+
@info = load_object_graph(hash, src_info).identifier
|
167
|
+
end
|
168
|
+
|
169
|
+
if src_root
|
170
|
+
@root = load_object_graph(hash, src_root).identifier
|
171
|
+
end
|
172
|
+
rescue PDF::Reader::MalformedPDFError, PDF::Reader::InvalidObjectError
|
173
|
+
msg = "Error reading template file. If you are sure it's a valid PDF, it may be a bug."
|
174
|
+
raise Prawn::Errors::TemplateError, msg
|
175
|
+
rescue PDF::Reader::UnsupportedFeatureError
|
176
|
+
msg = "Template file contains unsupported PDF features"
|
177
|
+
raise Prawn::Errors::TemplateError, msg
|
178
|
+
end
|
179
|
+
|
180
|
+
# recurse down an object graph from a source PDF, importing all the indirect
|
181
|
+
# objects we find.
|
182
|
+
#
|
183
|
+
# hash is the PDF::Hash to extract objects from, object is the object to
|
184
|
+
# extract.
|
185
|
+
#
|
186
|
+
def load_object_graph(hash, object)
|
187
|
+
@loaded_objects ||= {}
|
188
|
+
case object
|
189
|
+
when Hash then
|
190
|
+
object.each { |key,value| object[key] = load_object_graph(hash, value) }
|
191
|
+
object
|
192
|
+
when Array then
|
193
|
+
object.map { |item| load_object_graph(hash, item)}
|
194
|
+
when PDF::Reader::Reference then
|
195
|
+
unless @loaded_objects.has_key?(object.id)
|
196
|
+
@loaded_objects[object.id] = ref(nil)
|
197
|
+
new_obj = load_object_graph(hash, hash[object])
|
198
|
+
if new_obj.kind_of?(PDF::Reader::Stream)
|
199
|
+
stream_dict = load_object_graph(hash, new_obj.hash)
|
200
|
+
@loaded_objects[object.id].data = stream_dict
|
201
|
+
@loaded_objects[object.id] << new_obj.data
|
202
|
+
else
|
203
|
+
@loaded_objects[object.id].data = new_obj
|
204
|
+
end
|
205
|
+
end
|
206
|
+
@loaded_objects[object.id]
|
207
|
+
when PDF::Reader::Stream
|
208
|
+
# Stream is a subclass of string, so this is here to prevent the stream
|
209
|
+
# being wrapped in a LiteralString
|
210
|
+
object
|
211
|
+
when String
|
212
|
+
Prawn::Core::LiteralString.new(object)
|
213
|
+
else
|
214
|
+
object
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# prawn/core/page.rb : Implements low-level representation of a PDF page
|
4
|
+
#
|
5
|
+
# Copyright February 2010, Gregory Brown. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
#
|
9
|
+
module Prawn
|
10
|
+
module Core
|
11
|
+
class Page #:nodoc:
|
12
|
+
attr_accessor :document, :content, :dictionary, :margins
|
13
|
+
|
14
|
+
def initialize(document, options={})
|
15
|
+
@document = document
|
16
|
+
@margins = options[:margins] || { :left => 36,
|
17
|
+
:right => 36,
|
18
|
+
:top => 36,
|
19
|
+
:bottom => 36 }
|
20
|
+
|
21
|
+
if options[:object_id]
|
22
|
+
init_from_object(options)
|
23
|
+
else
|
24
|
+
init_new_page(options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def layout
|
29
|
+
return @layout if @layout
|
30
|
+
|
31
|
+
mb = dictionary.data[:MediaBox]
|
32
|
+
if mb[3] > mb[2]
|
33
|
+
:portrait
|
34
|
+
else
|
35
|
+
:landscape
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def size
|
40
|
+
@size || dimensions[2,2]
|
41
|
+
end
|
42
|
+
|
43
|
+
def in_stamp_stream?
|
44
|
+
!!@stamp_stream
|
45
|
+
end
|
46
|
+
|
47
|
+
def stamp_stream(dictionary)
|
48
|
+
@stamp_stream = ""
|
49
|
+
@stamp_dictionary = dictionary
|
50
|
+
|
51
|
+
document.send(:update_colors)
|
52
|
+
yield if block_given?
|
53
|
+
document.send(:update_colors)
|
54
|
+
|
55
|
+
@stamp_dictionary.data[:Length] = @stamp_stream.length + 1
|
56
|
+
@stamp_dictionary << @stamp_stream
|
57
|
+
|
58
|
+
@stamp_stream = nil
|
59
|
+
@stamp_dictionary = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def content
|
63
|
+
@stamp_stream || document.state.store[@content]
|
64
|
+
end
|
65
|
+
|
66
|
+
# As per the PDF spec, each page can have multiple content streams. This will
|
67
|
+
# add a fresh, empty content stream this the page, mainly for use in loading
|
68
|
+
# template files.
|
69
|
+
#
|
70
|
+
def new_content_stream
|
71
|
+
return if in_stamp_stream?
|
72
|
+
|
73
|
+
unless dictionary.data[:Contents].is_a?(Array)
|
74
|
+
dictionary.data[:Contents] = [content]
|
75
|
+
end
|
76
|
+
@content = document.ref(:Length => 0)
|
77
|
+
dictionary.data[:Contents] << document.state.store[@content]
|
78
|
+
end
|
79
|
+
|
80
|
+
def dictionary
|
81
|
+
@stamp_dictionary || document.state.store[@dictionary]
|
82
|
+
end
|
83
|
+
|
84
|
+
def resources
|
85
|
+
if dictionary.data[:Resources]
|
86
|
+
document.deref(dictionary.data[:Resources])
|
87
|
+
else
|
88
|
+
dictionary.data[:Resources] = {}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def fonts
|
93
|
+
if resources[:Font]
|
94
|
+
document.deref(resources[:Font])
|
95
|
+
else
|
96
|
+
resources[:Font] = {}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def xobjects
|
101
|
+
if resources[:XObject]
|
102
|
+
document.deref(resources[:XObject])
|
103
|
+
else
|
104
|
+
resources[:XObject] = {}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def ext_gstates
|
109
|
+
if resources[:ExtGState]
|
110
|
+
document.deref(resources[:ExtGState])
|
111
|
+
else
|
112
|
+
resources[:ExtGState] = {}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def finalize
|
117
|
+
if dictionary.data[:Contents].is_a?(Array)
|
118
|
+
dictionary.data[:Contents].each do |stream|
|
119
|
+
stream.compress_stream if document.compression_enabled?
|
120
|
+
stream.data[:Length] = stream.stream.size
|
121
|
+
end
|
122
|
+
else
|
123
|
+
content.compress_stream if document.compression_enabled?
|
124
|
+
content.data[:Length] = content.stream.size
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def imported_page?
|
129
|
+
@imported_page
|
130
|
+
end
|
131
|
+
|
132
|
+
def dimensions
|
133
|
+
return dictionary.data[:MediaBox] if imported_page?
|
134
|
+
|
135
|
+
coords = Prawn::Document::PageGeometry::SIZES[size] || size
|
136
|
+
[0,0] + case(layout)
|
137
|
+
when :portrait
|
138
|
+
coords
|
139
|
+
when :landscape
|
140
|
+
coords.reverse
|
141
|
+
else
|
142
|
+
raise Prawn::Errors::InvalidPageLayout,
|
143
|
+
"Layout must be either :portrait or :landscape"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def init_from_object(options)
|
150
|
+
@dictionary = options[:object_id].to_i
|
151
|
+
unless dictionary.data[:Contents].is_a?(Array) # content only on leafs
|
152
|
+
@content = dictionary.data[:Contents].identifier
|
153
|
+
end
|
154
|
+
|
155
|
+
@stamp_stream = nil
|
156
|
+
@stamp_dictionary = nil
|
157
|
+
@imported_page = true
|
158
|
+
end
|
159
|
+
|
160
|
+
def init_new_page(options)
|
161
|
+
@size = options[:size] || "LETTER"
|
162
|
+
@layout = options[:layout] || :portrait
|
163
|
+
|
164
|
+
@content = document.ref(:Length => 0)
|
165
|
+
@dictionary = document.ref(:Type => :Page,
|
166
|
+
:Parent => document.state.store.pages,
|
167
|
+
:MediaBox => dimensions,
|
168
|
+
:Contents => content)
|
169
|
+
|
170
|
+
resources[:ProcSet] = [:PDF, :Text, :ImageB, :ImageC, :ImageI]
|
171
|
+
|
172
|
+
@stamp_stream = nil
|
173
|
+
@stamp_dictionary = nil
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# pdf_object.rb : Handles Ruby to PDF object serialization
|
4
|
+
#
|
5
|
+
# Copyright April 2008, Gregory Brown. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
9
|
+
# Top level Module
|
10
|
+
#
|
11
|
+
module Prawn
|
12
|
+
module Core #:nodoc:
|
13
|
+
|
14
|
+
module_function
|
15
|
+
|
16
|
+
# Serializes Ruby objects to their PDF equivalents. Most primitive objects
|
17
|
+
# will work as expected, but please note that Name objects are represented
|
18
|
+
# by Ruby Symbol objects and Dictionary objects are represented by Ruby hashes
|
19
|
+
# (keyed by symbols)
|
20
|
+
#
|
21
|
+
# Examples:
|
22
|
+
#
|
23
|
+
# PdfObject(true) #=> "true"
|
24
|
+
# PdfObject(false) #=> "false"
|
25
|
+
# PdfObject(1.2124) #=> "1.2124"
|
26
|
+
# PdfObject("foo bar") #=> "(foo bar)"
|
27
|
+
# PdfObject(:Symbol) #=> "/Symbol"
|
28
|
+
# PdfObject(["foo",:bar, [1,2]]) #=> "[foo /bar [1 2]]"
|
29
|
+
#
|
30
|
+
def PdfObject(obj, in_content_stream = false)
|
31
|
+
case(obj)
|
32
|
+
when NilClass then "null"
|
33
|
+
when TrueClass then "true"
|
34
|
+
when FalseClass then "false"
|
35
|
+
when Numeric then String(obj)
|
36
|
+
when Array
|
37
|
+
"[" << obj.map { |e| PdfObject(e, in_content_stream) }.join(' ') << "]"
|
38
|
+
when Prawn::Core::LiteralString
|
39
|
+
obj = obj.gsub(/[\\\n\(\)]/) { |m| "\\#{m}" }
|
40
|
+
"(#{obj})"
|
41
|
+
when Time
|
42
|
+
obj = obj.strftime("D:%Y%m%d%H%M%S%z").chop.chop + "'00'"
|
43
|
+
obj = obj.gsub(/[\\\n\(\)]/) { |m| "\\#{m}" }
|
44
|
+
"(#{obj})"
|
45
|
+
when Prawn::Core::ByteString
|
46
|
+
"<" << obj.unpack("H*").first << ">"
|
47
|
+
when String
|
48
|
+
obj = "\xFE\xFF" + obj.unpack("U*").pack("n*") unless in_content_stream
|
49
|
+
"<" << obj.unpack("H*").first << ">"
|
50
|
+
when Symbol
|
51
|
+
"/" + obj.to_s.unpack("C*").map { |n|
|
52
|
+
if n < 33 || n > 126 || [35,40,41,47,60,62].include?(n)
|
53
|
+
"#" + n.to_s(16).upcase
|
54
|
+
else
|
55
|
+
[n].pack("C*")
|
56
|
+
end
|
57
|
+
}.join
|
58
|
+
when Hash
|
59
|
+
output = "<< "
|
60
|
+
obj.each do |k,v|
|
61
|
+
unless String === k || Symbol === k
|
62
|
+
raise Prawn::Errors::FailedObjectConversion,
|
63
|
+
"A PDF Dictionary must be keyed by names"
|
64
|
+
end
|
65
|
+
output << PdfObject(k.to_sym, in_content_stream) << " " <<
|
66
|
+
PdfObject(v, in_content_stream) << "\n"
|
67
|
+
end
|
68
|
+
output << ">>"
|
69
|
+
when Prawn::Core::Reference
|
70
|
+
obj.to_s
|
71
|
+
when Prawn::Core::NameTree::Node
|
72
|
+
PdfObject(obj.to_hash)
|
73
|
+
when Prawn::Core::NameTree::Value
|
74
|
+
PdfObject(obj.name) + " " + PdfObject(obj.value)
|
75
|
+
when Prawn::OutlineRoot, Prawn::OutlineItem
|
76
|
+
PdfObject(obj.to_hash)
|
77
|
+
else
|
78
|
+
raise Prawn::Errors::FailedObjectConversion,
|
79
|
+
"This object cannot be serialized to PDF (#{obj.inspect})"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|