prawn-core 0.7.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|