prawn-core 0.7.2 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/examples/general/background.rb +1 -1
- data/examples/general/measurement_units.rb +2 -2
- data/examples/general/outlines.rb +50 -0
- data/examples/general/repeaters.rb +11 -7
- data/examples/general/stamp.rb +6 -6
- data/examples/graphics/basic_images.rb +1 -1
- data/examples/graphics/curves.rb +1 -1
- data/examples/graphics/rounded_polygons.rb +19 -0
- data/examples/graphics/rounded_rectangle.rb +20 -0
- data/examples/graphics/transformations.rb +52 -0
- data/examples/m17n/win_ansi_charset.rb +1 -1
- data/examples/text/font_calculations.rb +3 -3
- data/examples/text/indent_paragraphs.rb +18 -0
- data/examples/text/kerning.rb +4 -4
- data/examples/text/rotated.rb +98 -0
- data/examples/text/simple_text.rb +3 -3
- data/examples/text/simple_text_ttf.rb +1 -1
- data/lib/prawn/byte_string.rb +1 -0
- data/lib/prawn/core.rb +12 -5
- data/lib/prawn/core/object_store.rb +99 -0
- data/lib/prawn/core/page.rb +96 -0
- data/lib/prawn/core/text.rb +75 -0
- data/lib/prawn/document.rb +71 -78
- data/lib/prawn/document/annotations.rb +2 -2
- data/lib/prawn/document/bounding_box.rb +19 -9
- data/lib/prawn/document/column_box.rb +13 -12
- data/lib/prawn/document/graphics_state.rb +49 -0
- data/lib/prawn/document/internals.rb +5 -40
- data/lib/prawn/document/page_geometry.rb +1 -18
- data/lib/prawn/document/snapshot.rb +12 -7
- data/lib/prawn/errors.rb +18 -0
- data/lib/prawn/font.rb +4 -2
- data/lib/prawn/font/afm.rb +8 -0
- data/lib/prawn/font/dfont.rb +12 -4
- data/lib/prawn/font/ttf.rb +9 -0
- data/lib/prawn/graphics.rb +66 -9
- data/lib/prawn/graphics/color.rb +1 -1
- data/lib/prawn/graphics/transformation.rb +156 -0
- data/lib/prawn/graphics/transparency.rb +3 -7
- data/lib/prawn/images.rb +4 -3
- data/lib/prawn/images/png.rb +2 -2
- data/lib/prawn/outline.rb +278 -0
- data/lib/prawn/pdf_object.rb +5 -3
- data/lib/prawn/repeater.rb +25 -13
- data/lib/prawn/stamp.rb +6 -29
- data/lib/prawn/text.rb +139 -121
- data/lib/prawn/text/box.rb +168 -102
- data/spec/bounding_box_spec.rb +7 -2
- data/spec/document_spec.rb +7 -5
- data/spec/font_spec.rb +9 -1
- data/spec/graphics_spec.rb +229 -0
- data/spec/object_store_spec.rb +5 -5
- data/spec/outline_spec.rb +229 -0
- data/spec/repeater_spec.rb +18 -1
- data/spec/snapshot_spec.rb +7 -7
- data/spec/span_spec.rb +6 -2
- data/spec/spec_helper.rb +7 -3
- data/spec/stamp_spec.rb +13 -0
- data/spec/text_at_spec.rb +119 -0
- data/spec/text_box_spec.rb +257 -4
- data/spec/text_spec.rb +278 -180
- data/vendor/pdf-inspector/lib/pdf/inspector/graphics.rb +12 -0
- metadata +16 -3
- data/lib/prawn/object_store.rb +0 -92
@@ -21,9 +21,9 @@ module Prawn
|
|
21
21
|
# +options+ must be a Hash describing the annotation.
|
22
22
|
#
|
23
23
|
def annotate(options)
|
24
|
-
|
24
|
+
page.dictionary.data[:Annots] ||= []
|
25
25
|
options = sanitize_annotation_hash(options)
|
26
|
-
|
26
|
+
page.dictionary.data[:Annots] << ref!(options)
|
27
27
|
return options
|
28
28
|
end
|
29
29
|
|
@@ -15,6 +15,9 @@ module Prawn
|
|
15
15
|
# A bounding box serves two important purposes:
|
16
16
|
# * Provide bounds for flowing text, starting at a given point
|
17
17
|
# * Translate the origin (0,0) for graphics primitives
|
18
|
+
#
|
19
|
+
# A point and :width must be provided. :height is optional.
|
20
|
+
# (See stretchyness below)
|
18
21
|
#
|
19
22
|
# ==Positioning
|
20
23
|
#
|
@@ -153,7 +156,7 @@ module Prawn
|
|
153
156
|
#
|
154
157
|
def bounding_box(*args, &block)
|
155
158
|
init_bounding_box(block) do |_|
|
156
|
-
|
159
|
+
map_to_absolute!(args[0])
|
157
160
|
@bounding_box = BoundingBox.new(self, *args)
|
158
161
|
end
|
159
162
|
end
|
@@ -167,9 +170,9 @@ module Prawn
|
|
167
170
|
#
|
168
171
|
def canvas(&block)
|
169
172
|
init_bounding_box(block, :hold_position => true) do |_|
|
170
|
-
@bounding_box = BoundingBox.new(self, [0,
|
171
|
-
:width =>
|
172
|
-
:height =>
|
173
|
+
@bounding_box = BoundingBox.new(self, [0,page.dimensions[3]],
|
174
|
+
:width => page.dimensions[2],
|
175
|
+
:height => page.dimensions[3]
|
173
176
|
)
|
174
177
|
end
|
175
178
|
end
|
@@ -198,10 +201,17 @@ module Prawn
|
|
198
201
|
class BoundingBox
|
199
202
|
|
200
203
|
def initialize(parent, point, options={}) #:nodoc:
|
204
|
+
unless options[:width]
|
205
|
+
raise ArgumentError, "BoundingBox needs the :width option to be set"
|
206
|
+
end
|
207
|
+
|
201
208
|
@parent = parent
|
202
209
|
@x, @y = point
|
203
210
|
@width, @height = options[:width], options[:height]
|
204
|
-
|
211
|
+
@stretched_height = nil
|
212
|
+
end
|
213
|
+
|
214
|
+
attr_reader :parent
|
205
215
|
|
206
216
|
# The translated origin (x,y-height) which describes the location
|
207
217
|
# of the bottom left corner of the bounding box
|
@@ -214,7 +224,7 @@ module Prawn
|
|
214
224
|
#
|
215
225
|
# Example, position some text 3 pts from the left of the containing box:
|
216
226
|
#
|
217
|
-
#
|
227
|
+
# draw_text('hello', :at => [(bounds.left + 3), 0])
|
218
228
|
#
|
219
229
|
def left
|
220
230
|
0
|
@@ -245,7 +255,7 @@ module Prawn
|
|
245
255
|
#
|
246
256
|
# Example, position some text 3 pts from the right of the containing box:
|
247
257
|
#
|
248
|
-
#
|
258
|
+
# draw_text('hello', :at => [(bounds.right - 3), 0])
|
249
259
|
#
|
250
260
|
def right
|
251
261
|
@width
|
@@ -255,7 +265,7 @@ module Prawn
|
|
255
265
|
#
|
256
266
|
# Example, position some text 3 pts from the top of the containing box:
|
257
267
|
#
|
258
|
-
#
|
268
|
+
# draw_text('hello', :at => [0, (bounds.top - 3)])
|
259
269
|
#
|
260
270
|
def top
|
261
271
|
height
|
@@ -265,7 +275,7 @@ module Prawn
|
|
265
275
|
#
|
266
276
|
# Example, position some text 3 pts from the bottom of the containing box:
|
267
277
|
#
|
268
|
-
#
|
278
|
+
# draw_text('hello', :at => [0, (bounds.bottom + 3)])
|
269
279
|
#
|
270
280
|
def bottom
|
271
281
|
0
|
@@ -9,20 +9,21 @@ require "prawn/document/bounding_box"
|
|
9
9
|
module Prawn
|
10
10
|
class Document
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
# A column box is a bounding box with the additional property that when
|
13
|
+
# text flows past the bottom, it will wrap first to another column on the
|
14
|
+
# same page, and only flow to the next page when all the columns are
|
15
|
+
# filled.
|
16
|
+
#
|
17
|
+
# column_box accepts the same parameters as bounding_box, as well as the
|
18
|
+
# number of :columns and a :spacer (in points) between columns.
|
19
|
+
#
|
20
|
+
# Defaults are :columns = 3 and :spacer = font_size
|
21
|
+
#
|
22
|
+
# Under PDF::Writer, "spacer" was known as "gutter"
|
23
|
+
#
|
23
24
|
def column_box(*args, &block)
|
24
25
|
init_column_box(block) do |_|
|
25
|
-
|
26
|
+
map_to_absolute!(args[0])
|
26
27
|
@bounding_box = ColumnBox.new(self, *args)
|
27
28
|
end
|
28
29
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# graphics_state.rb: 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
|
+
module Prawn
|
11
|
+
class Document
|
12
|
+
module GraphicsState
|
13
|
+
|
14
|
+
# Pushes the current graphics state on to the graphics state stack so we
|
15
|
+
# can restore it when finished with a change we want to isolate (such as
|
16
|
+
# modifying the transformation matrix). Used in pairs with
|
17
|
+
# restore_graphics_state or passed a block
|
18
|
+
#
|
19
|
+
# Example without a block:
|
20
|
+
#
|
21
|
+
# save_graphics_state
|
22
|
+
# rotate 30
|
23
|
+
# text "rotated text"
|
24
|
+
# restore_graphics_state
|
25
|
+
#
|
26
|
+
# Example with a block:
|
27
|
+
#
|
28
|
+
# save_graphics_state do
|
29
|
+
# rotate 30
|
30
|
+
# text "rotated text"
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
def save_graphics_state
|
34
|
+
add_content "q"
|
35
|
+
if block_given?
|
36
|
+
yield
|
37
|
+
restore_graphics_state
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Pops the last saved graphics state off the graphics state stack and
|
42
|
+
# restores the state to those values
|
43
|
+
def restore_graphics_state
|
44
|
+
add_content "Q"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -37,18 +37,6 @@ module Prawn
|
|
37
37
|
@store.ref(data)
|
38
38
|
end
|
39
39
|
|
40
|
-
# Grabs the reference for the current page content
|
41
|
-
#
|
42
|
-
def page_content
|
43
|
-
@active_stamp_stream || @store[@page_content]
|
44
|
-
end
|
45
|
-
|
46
|
-
# Grabs the reference for the current page
|
47
|
-
#
|
48
|
-
def current_page
|
49
|
-
@active_stamp_dictionary || @store[@current_page]
|
50
|
-
end
|
51
|
-
|
52
40
|
# Appends a raw string to the current page content.
|
53
41
|
#
|
54
42
|
# # Raw line drawing example:
|
@@ -58,32 +46,8 @@ module Prawn
|
|
58
46
|
# pdf.add_content("S") # stroke
|
59
47
|
#
|
60
48
|
def add_content(str)
|
61
|
-
|
49
|
+
page.content << str << "\n"
|
62
50
|
end
|
63
|
-
|
64
|
-
# The Resources dictionary for the current page
|
65
|
-
#
|
66
|
-
def page_resources
|
67
|
-
current_page.data[:Resources] ||= {}
|
68
|
-
end
|
69
|
-
|
70
|
-
# The Font dictionary for the current page
|
71
|
-
#
|
72
|
-
def page_fonts
|
73
|
-
page_resources[:Font] ||= {}
|
74
|
-
end
|
75
|
-
|
76
|
-
# The XObject dictionary for the current page
|
77
|
-
#
|
78
|
-
def page_xobjects
|
79
|
-
page_resources[:XObject] ||= {}
|
80
|
-
end
|
81
|
-
|
82
|
-
# The ExtGState dictionary for the current page
|
83
|
-
#
|
84
|
-
def page_ext_gstates
|
85
|
-
page_resources[:ExtGState] ||= {}
|
86
|
-
end
|
87
51
|
|
88
52
|
# The Name dictionary (PDF spec 3.6.3) for this document. It is
|
89
53
|
# lazily initialized, so that documents that do not need a name
|
@@ -127,15 +91,16 @@ module Prawn
|
|
127
91
|
(1..page_count).each do |i|
|
128
92
|
go_to_page i
|
129
93
|
repeaters.each { |r| r.run(i) }
|
130
|
-
|
131
|
-
|
132
|
-
|
94
|
+
restore_graphics_state
|
95
|
+
page.content.compress_stream if compression_enabled?
|
96
|
+
page.content.data[:Length] = page.content.stream.size
|
133
97
|
end
|
134
98
|
end
|
135
99
|
|
136
100
|
# raise the PDF version of the file we're going to generate.
|
137
101
|
# A private method, designed for internal use when the user adds a feature
|
138
102
|
# to their document that requires a particular version.
|
103
|
+
#
|
139
104
|
def min_version(min)
|
140
105
|
@version = min if min > @version
|
141
106
|
end
|
@@ -131,23 +131,6 @@ module Prawn
|
|
131
131
|
"LETTER" => [612.00, 792.00],
|
132
132
|
"TABLOID" => [792.00, 1224.00] }
|
133
133
|
|
134
|
-
|
135
|
-
# Returns the width and height of the current page in PDF points.
|
136
|
-
# Usually, you'll want the dimensions of the bounding_box or
|
137
|
-
# margin_box instead.
|
138
|
-
#
|
139
|
-
def page_dimensions
|
140
|
-
coords = SIZES[page_size] || page_size
|
141
|
-
[0,0] + case(page_layout)
|
142
|
-
when :portrait
|
143
|
-
coords
|
144
|
-
when :landscape
|
145
|
-
coords.reverse
|
146
|
-
else
|
147
|
-
raise Prawn::Errors::InvalidPageLayout,
|
148
|
-
"Layout must be either :portrait or :landscape"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
134
|
+
end
|
152
135
|
end
|
153
136
|
end
|
@@ -5,6 +5,7 @@
|
|
5
5
|
# Copyright August 2009, Brad Ediger. All Rights Reserved.
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
8
9
|
require 'delegate'
|
9
10
|
|
10
11
|
module Prawn
|
@@ -43,9 +44,10 @@ module Prawn
|
|
43
44
|
|
44
45
|
# Takes a current snapshot of the document's state, sufficient to
|
45
46
|
# reconstruct it after it was amended.
|
47
|
+
#
|
46
48
|
def take_snapshot
|
47
|
-
{:page_content => Marshal.load(Marshal.dump(
|
48
|
-
:current_page => Marshal.load(Marshal.dump(
|
49
|
+
{:page_content => Marshal.load(Marshal.dump(page.content)),
|
50
|
+
:current_page => Marshal.load(Marshal.dump(page.dictionary)),
|
49
51
|
:page_number => @page_number,
|
50
52
|
:page_kids => @store.pages.data[:Kids].map{|kid| kid.identifier},
|
51
53
|
:dests => names? &&
|
@@ -53,17 +55,20 @@ module Prawn
|
|
53
55
|
end
|
54
56
|
|
55
57
|
# Rolls the page state back to the state of the given snapshot.
|
58
|
+
#
|
56
59
|
def restore_snapshot(shot)
|
57
60
|
# Because these objects are referenced by identifier from the Pages
|
58
61
|
# dictionary, we can't just restore them over the current refs in
|
59
62
|
# page_content and current_page. We have to restore them over the old
|
60
63
|
# ones.
|
61
|
-
|
62
|
-
|
64
|
+
page.content = shot[:page_content].identifier
|
65
|
+
page.content.replace shot[:page_content]
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
+
page.dictionary = shot[:current_page].identifier
|
68
|
+
page.dictionary.replace shot[:current_page]
|
69
|
+
page.dictionary.data[:Contents] = page.content
|
70
|
+
|
71
|
+
@page_number = shot[:page_number]
|
67
72
|
|
68
73
|
@page_number = shot[:page_number]
|
69
74
|
|
data/lib/prawn/errors.rb
CHANGED
@@ -32,6 +32,11 @@ module Prawn
|
|
32
32
|
#
|
33
33
|
CannotFit = Class.new(StandardError)
|
34
34
|
|
35
|
+
# Raised if group() is called with a block that is too big to be
|
36
|
+
# rendered in the current context.
|
37
|
+
#
|
38
|
+
CannotGroup = Class.new(StandardError)
|
39
|
+
|
35
40
|
# This error is raised when Prawn is being used on a M17N aware VM,
|
36
41
|
# and the user attempts to add text that isn't compatible with UTF-8
|
37
42
|
# to their document
|
@@ -61,5 +66,18 @@ module Prawn
|
|
61
66
|
# This error is raised when an object is attempted to be
|
62
67
|
# referenced by name, but no such name is associated with an object
|
63
68
|
UndefinedObjectName = Class.new(StandardError)
|
69
|
+
|
70
|
+
# This error is raised when a required option has not been set
|
71
|
+
RequiredOption = Class.new(StandardError)
|
72
|
+
|
73
|
+
# This error is raised when a requested outline item with a given title does not exist
|
74
|
+
UnknownOutlineTitle = Class.new(StandardError)
|
75
|
+
|
76
|
+
# This error is raised when a block is required, but not provided
|
77
|
+
BlockRequired = Class.new(StandardError)
|
78
|
+
|
79
|
+
# This error is rased when a graphics method is called with improper arguments
|
80
|
+
InvalidGraphicsPath = Class.new(StandardError)
|
81
|
+
|
64
82
|
end
|
65
83
|
end
|
data/lib/prawn/font.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
# Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
#
|
8
9
|
require "prawn/font/afm"
|
9
10
|
require "prawn/font/ttf"
|
10
11
|
require "prawn/font/dfont"
|
@@ -47,7 +48,7 @@ module Prawn
|
|
47
48
|
def font(name=nil, options={})
|
48
49
|
return((defined?(@font) && @font) || font("Helvetica")) if name.nil?
|
49
50
|
|
50
|
-
raise Errors::NotOnPage
|
51
|
+
raise Errors::NotOnPage if pages.empty? && !page.in_stamp_stream?
|
51
52
|
new_font = find_font(name, options)
|
52
53
|
|
53
54
|
if block_given?
|
@@ -277,6 +278,7 @@ module Prawn
|
|
277
278
|
# font. The string is expected to be UTF-8 going in. It will be re-encoded
|
278
279
|
# and the new string will be returned. For an in-place (destructive)
|
279
280
|
# version, see normalize_encoding!.
|
281
|
+
#
|
280
282
|
def normalize_encoding(string)
|
281
283
|
raise NotImplementedError, "subclasses of Prawn::Font must implement #normalize_encoding"
|
282
284
|
end
|
@@ -307,7 +309,7 @@ module Prawn
|
|
307
309
|
#
|
308
310
|
def add_to_current_page(subset)
|
309
311
|
@references[subset] ||= register(subset)
|
310
|
-
@document.
|
312
|
+
@document.page.fonts.merge!(identifier_for(subset) => @references[subset])
|
311
313
|
end
|
312
314
|
|
313
315
|
def identifier_for(subset) #:nodoc:
|
data/lib/prawn/font/afm.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# prawn/font/afm.rb : Implements AFM font support for Prawn
|
4
|
+
#
|
5
|
+
# Copyright May 2008, Gregory Brown / James Healy. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
|
+
|
1
9
|
require 'prawn/encoding'
|
2
10
|
|
3
11
|
module Prawn
|
data/lib/prawn/font/dfont.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# font.rb : The Prawn font class
|
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
|
+
#
|
1
9
|
require 'prawn/font/ttf'
|
2
10
|
|
3
11
|
module Prawn
|
@@ -11,16 +19,16 @@ module Prawn
|
|
11
19
|
# list is not necessarily the font at index 0 in the file.
|
12
20
|
#
|
13
21
|
def self.named_fonts(file)
|
14
|
-
TTFunk::ResourceFile.open(file) do |
|
15
|
-
return
|
22
|
+
TTFunk::ResourceFile.open(file) do |f|
|
23
|
+
return f.resources_for("sfnt")
|
16
24
|
end
|
17
25
|
end
|
18
26
|
|
19
27
|
# Returns the number of fonts contained in the dfont file.
|
20
28
|
#
|
21
29
|
def self.font_count(file)
|
22
|
-
TTFunk::ResourceFile.open(file) do |
|
23
|
-
return
|
30
|
+
TTFunk::ResourceFile.open(file) do |f|
|
31
|
+
return f.map["sfnt"][:list].length
|
24
32
|
end
|
25
33
|
end
|
26
34
|
|
data/lib/prawn/font/ttf.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# prawn/font/ttf.rb : Implements AFM font support for Prawn
|
4
|
+
#
|
5
|
+
# Copyright May 2008, Gregory Brown / James Healy / Jamis Buck
|
6
|
+
# All Rights Reserved.
|
7
|
+
#
|
8
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
9
|
+
|
1
10
|
require 'ttfunk'
|
2
11
|
require 'ttfunk/subset_collection'
|
3
12
|
|