prawn 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +3 -4
- data/lib/prawn.rb +3 -6
- data/lib/prawn/document.rb +8 -27
- data/lib/prawn/document/internals.rb +28 -145
- data/lib/prawn/font.rb +2 -23
- data/lib/prawn/font/ttf.rb +1 -1
- data/lib/prawn/graphics.rb +12 -12
- data/lib/prawn/graphics/cap_style.rb +1 -1
- data/lib/prawn/graphics/color.rb +3 -3
- data/lib/prawn/graphics/dash.rb +1 -1
- data/lib/prawn/graphics/join_style.rb +1 -1
- data/lib/prawn/graphics/patterns.rb +1 -1
- data/lib/prawn/graphics/transformation.rb +1 -1
- data/lib/prawn/graphics/transparency.rb +2 -2
- data/lib/prawn/grid.rb +5 -5
- data/lib/prawn/images.rb +2 -2
- data/lib/prawn/security.rb +1 -1
- data/lib/prawn/soft_mask.rb +3 -3
- data/lib/prawn/stamp.rb +1 -1
- data/lib/prawn/text.rb +8 -0
- data/lib/prawn/text/formatted/fragment.rb +19 -8
- data/lib/prawn/version.rb +5 -0
- data/lib/prawn/view.rb +91 -0
- data/manual/basic_concepts/basic_concepts.rb +3 -1
- data/manual/basic_concepts/view.rb +42 -0
- data/prawn.gemspec +9 -4
- data/spec/document_spec.rb +14 -19
- data/spec/graphics_spec.rb +11 -11
- data/spec/line_wrap_spec.rb +10 -7
- data/spec/view_spec.rb +43 -0
- metadata +25 -8
- data/VERSION +0 -1
- data/lib/prawn/document/graphics_state.rb +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 287dd21a281ca025939e562e2d9d0dae14373d21
|
4
|
+
data.tar.gz: 0c08643d45840aad66a59661f7593bc3abb89302
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 013f40d6fa76d1cf34ed85578e8ce5700c3ba47bbf6b373b9856ae2a34fe42421d83da002604f1acb5bdfd7fdcf0acc85a1f58d9397c89d46254c2e01da5764a
|
7
|
+
data.tar.gz: b478644390db0dcd837063a79c126cf0d8d70a53746755dae3797bda11553b8520dd33d3ca983301a7627a76b74d824337d3497abba5966683eedd81efb5a2d5
|
data/Rakefile
CHANGED
@@ -16,10 +16,9 @@ end
|
|
16
16
|
|
17
17
|
desc "Show library's code statistics"
|
18
18
|
task :stats do
|
19
|
-
require 'code_statistics'
|
20
|
-
CodeStatistics::
|
21
|
-
|
22
|
-
["Specs", "spec"] ).to_s
|
19
|
+
require 'code_statistics/code_statistics'
|
20
|
+
puts CodeStatistics::CodeStatistics.new( [["Prawn", "lib"],
|
21
|
+
["Specs", "spec"]] ).to_s
|
23
22
|
end
|
24
23
|
|
25
24
|
YARD::Rake::YardocTask.new do |t|
|
data/lib/prawn.rb
CHANGED
@@ -21,8 +21,6 @@ module Prawn
|
|
21
21
|
BASEDIR = File.expand_path(File.join(dir, '..'))
|
22
22
|
DATADIR = File.expand_path(File.join(dir, '..', 'data'))
|
23
23
|
|
24
|
-
VERSION = File.read("#{BASEDIR}/VERSION").strip
|
25
|
-
|
26
24
|
FLOAT_PRECISION = 1.0e-9
|
27
25
|
|
28
26
|
# Whe set to true, Prawn will verify hash options to ensure only valid keys
|
@@ -64,8 +62,9 @@ module Prawn
|
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
67
|
-
require_relative "prawn/
|
65
|
+
require_relative "prawn/version"
|
68
66
|
|
67
|
+
require_relative "prawn/errors"
|
69
68
|
|
70
69
|
require_relative "prawn/utilities"
|
71
70
|
require_relative "prawn/text"
|
@@ -84,10 +83,8 @@ require_relative "prawn/measurements"
|
|
84
83
|
require_relative "prawn/repeater"
|
85
84
|
require_relative "prawn/outline"
|
86
85
|
require_relative "prawn/grid"
|
87
|
-
|
86
|
+
require_relative "prawn/view"
|
88
87
|
require_relative "prawn/image_handler"
|
89
88
|
|
90
|
-
|
91
|
-
|
92
89
|
Prawn.image_handler.register(Prawn::Images::PNG)
|
93
90
|
Prawn.image_handler.register(Prawn::Images::JPG)
|
data/lib/prawn/document.rb
CHANGED
@@ -12,7 +12,6 @@ require_relative "document/bounding_box"
|
|
12
12
|
require_relative "document/column_box"
|
13
13
|
require_relative "document/internals"
|
14
14
|
require_relative "document/span"
|
15
|
-
require_relative "document/graphics_state"
|
16
15
|
|
17
16
|
module Prawn
|
18
17
|
|
@@ -53,7 +52,6 @@ module Prawn
|
|
53
52
|
include Prawn::Document::Internals
|
54
53
|
include PDF::Core::Annotations
|
55
54
|
include PDF::Core::Destinations
|
56
|
-
include Prawn::Document::GraphicsState
|
57
55
|
include Prawn::Document::Security
|
58
56
|
include Prawn::Text
|
59
57
|
include Prawn::Graphics
|
@@ -202,9 +200,9 @@ module Prawn
|
|
202
200
|
self.class.extensions.reverse_each { |e| extend e }
|
203
201
|
@internal_state = PDF::Core::DocumentState.new(options)
|
204
202
|
@internal_state.populate_pages_from_store(self)
|
205
|
-
min_version(state.store.min_version) if state.store.min_version
|
203
|
+
renderer.min_version(state.store.min_version) if state.store.min_version
|
206
204
|
|
207
|
-
min_version(1.6) if options[:print_scaling] == :none
|
205
|
+
renderer.min_version(1.6) if options[:print_scaling] == :none
|
208
206
|
|
209
207
|
@background = options[:background]
|
210
208
|
@background_scale = options[:background_scale] || 1
|
@@ -349,23 +347,13 @@ module Prawn
|
|
349
347
|
# Renders the PDF document to string.
|
350
348
|
# Pass an open file descriptor to render to file.
|
351
349
|
#
|
352
|
-
def render(
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
finalize_all_page_contents
|
357
|
-
|
358
|
-
render_header(output)
|
359
|
-
render_body(output)
|
360
|
-
render_xref(output)
|
361
|
-
render_trailer(output)
|
362
|
-
if output.instance_of?(StringIO)
|
363
|
-
str = output.string
|
364
|
-
str.force_encoding(::Encoding::ASCII_8BIT)
|
365
|
-
return str
|
366
|
-
else
|
367
|
-
return nil
|
350
|
+
def render(*a, &b)
|
351
|
+
(1..page_count).each do |i|
|
352
|
+
go_to_page i
|
353
|
+
repeaters.each { |r| r.run(i) }
|
368
354
|
end
|
355
|
+
|
356
|
+
renderer.render(*a, &b)
|
369
357
|
end
|
370
358
|
|
371
359
|
# Renders the PDF document to file.
|
@@ -567,13 +555,6 @@ module Prawn
|
|
567
555
|
end
|
568
556
|
end
|
569
557
|
|
570
|
-
# Returns true if content streams will be compressed before rendering,
|
571
|
-
# false otherwise
|
572
|
-
#
|
573
|
-
def compression_enabled?
|
574
|
-
!!state.compress
|
575
|
-
end
|
576
|
-
|
577
558
|
# @group Experimental API
|
578
559
|
|
579
560
|
# Attempts to group the given block vertically within the current context.
|
@@ -6,6 +6,8 @@
|
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
|
9
|
+
require "forwardable"
|
10
|
+
|
9
11
|
module Prawn
|
10
12
|
class Document
|
11
13
|
|
@@ -16,159 +18,40 @@ module Prawn
|
|
16
18
|
#
|
17
19
|
# @private
|
18
20
|
module Internals
|
19
|
-
|
20
|
-
# list. The +data+ argument is anything that Prawn::PdfObject() can convert.
|
21
|
-
#
|
22
|
-
# Returns the identifier which points to the reference in the ObjectStore
|
23
|
-
#
|
24
|
-
def ref(data)
|
25
|
-
ref!(data).identifier
|
26
|
-
end
|
27
|
-
|
28
|
-
# Like ref, but returns the actual reference instead of its identifier.
|
29
|
-
#
|
30
|
-
# While you can use this to build up nested references within the object
|
31
|
-
# tree, it is recommended to persist only identifiers, and them provide
|
32
|
-
# helper methods to look up the actual references in the ObjectStore
|
33
|
-
# if needed. If you take this approach, Prawn::Document::Snapshot
|
34
|
-
# will probably work with your extension
|
35
|
-
#
|
36
|
-
def ref!(data)
|
37
|
-
state.store.ref(data)
|
38
|
-
end
|
39
|
-
|
40
|
-
# At any stage in the object tree an object can be replaced with an
|
41
|
-
# indirect reference. To get access to the object safely, regardless
|
42
|
-
# of if it's hidden behind a Prawn::Reference, wrap it in deref().
|
43
|
-
#
|
44
|
-
def deref(obj)
|
45
|
-
obj.is_a?(PDF::Core::Reference) ? obj.data : obj
|
46
|
-
end
|
47
|
-
|
48
|
-
# Appends a raw string to the current page content.
|
49
|
-
#
|
50
|
-
# # Raw line drawing example:
|
51
|
-
# x1,y1,x2,y2 = 100,500,300,550
|
52
|
-
# pdf.add_content("%.3f %.3f m" % [ x1, y1 ]) # move
|
53
|
-
# pdf.add_content("%.3f %.3f l" % [ x2, y2 ]) # draw path
|
54
|
-
# pdf.add_content("S") # stroke
|
55
|
-
#
|
56
|
-
def add_content(str)
|
57
|
-
save_graphics_state if graphic_state.nil?
|
58
|
-
state.page.content << str << "\n"
|
59
|
-
end
|
60
|
-
|
61
|
-
# The Name dictionary (PDF spec 3.6.3) for this document. It is
|
62
|
-
# lazily initialized, so that documents that do not need a name
|
63
|
-
# dictionary do not incur the additional overhead.
|
64
|
-
#
|
65
|
-
def names
|
66
|
-
state.store.root.data[:Names] ||= ref!(:Type => :Names)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Returns true if the Names dictionary is in use for this document.
|
70
|
-
#
|
71
|
-
def names?
|
72
|
-
state.store.root.data[:Names]
|
73
|
-
end
|
74
|
-
|
75
|
-
# Defines a block to be called just before the document is rendered.
|
76
|
-
#
|
77
|
-
def before_render(&block)
|
78
|
-
state.before_render_callbacks << block
|
79
|
-
end
|
80
|
-
|
81
|
-
# Defines a block to be called just before a new page is started.
|
82
|
-
#
|
83
|
-
def on_page_create(&block)
|
84
|
-
if block_given?
|
85
|
-
state.on_page_create_callback = block
|
86
|
-
else
|
87
|
-
state.on_page_create_callback = nil
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
private
|
92
|
-
|
93
|
-
# adds a new, empty content stream to each page. Used in templating so
|
94
|
-
# that imported content streams can be left pristine
|
95
|
-
#
|
96
|
-
def fresh_content_streams(options={})
|
97
|
-
(1..page_count).each do |i|
|
98
|
-
go_to_page i
|
99
|
-
state.page.new_content_stream
|
100
|
-
apply_margin_options(options)
|
101
|
-
generate_margin_box
|
102
|
-
use_graphic_settings(options[:template])
|
103
|
-
forget_text_rendering_mode!
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def finalize_all_page_contents
|
108
|
-
(1..page_count).each do |i|
|
109
|
-
go_to_page i
|
110
|
-
repeaters.each { |r| r.run(i) }
|
111
|
-
while graphic_stack.present?
|
112
|
-
restore_graphics_state
|
113
|
-
end
|
114
|
-
state.page.finalize
|
115
|
-
end
|
116
|
-
end
|
21
|
+
extend Forwardable
|
117
22
|
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
|
123
|
-
|
124
|
-
|
23
|
+
# These methods are not officially part of Prawn's public API,
|
24
|
+
# but they are used in documentation and possibly in extensions.
|
25
|
+
# Perhaps they will become part of the extension API?
|
26
|
+
# Anyway, for now it's not clear what we should do w. them.
|
27
|
+
delegate [ :graphic_state,
|
28
|
+
:save_graphics_state,
|
29
|
+
:restore_graphics_state ] => :renderer
|
125
30
|
|
126
|
-
#
|
31
|
+
# FIXME: This is a circular reference, because in theory Prawn should
|
32
|
+
# be passing instances of renderer to PDF::Core::Page, but it's
|
33
|
+
# passing Prawn::Document objects instead.
|
127
34
|
#
|
128
|
-
|
129
|
-
|
35
|
+
# A proper design would probably not require Prawn to directly instantiate
|
36
|
+
# PDF::Core::Page objects at all!
|
37
|
+
delegate [:compression_enabled?] => :renderer
|
130
38
|
|
131
|
-
|
132
|
-
|
39
|
+
# FIXME: More circular references in PDF::Core::Page.
|
40
|
+
delegate [ :ref, :ref!, :deref ] => :renderer
|
133
41
|
|
134
|
-
|
135
|
-
|
136
|
-
|
42
|
+
# FIXME: Another circular reference, because we mix in a module from
|
43
|
+
# PDF::Core to provide destinations, which in theory should not
|
44
|
+
# rely on a Prawn::Document object but is currently wired up that way.
|
45
|
+
delegate [:names] => :renderer
|
137
46
|
|
138
|
-
#
|
139
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
end
|
47
|
+
# FIXME: Circular reference because we mix PDF::Core::Text into
|
48
|
+
# Prawn::Document. PDF::Core::Text should either be split up or
|
49
|
+
# moved in its entirety back up into Prawn.
|
50
|
+
delegate [:add_content] => :renderer
|
143
51
|
|
144
|
-
|
145
|
-
|
146
|
-
def render_xref(output)
|
147
|
-
@xref_offset = output.size
|
148
|
-
output << "xref\n"
|
149
|
-
output << "0 #{state.store.size + 1}\n"
|
150
|
-
output << "0000000000 65535 f \n"
|
151
|
-
state.store.each do |ref|
|
152
|
-
output.printf("%010d", ref.offset)
|
153
|
-
output << " 00000 n \n"
|
154
|
-
end
|
52
|
+
def renderer
|
53
|
+
@renderer ||= PDF::Core::Renderer.new(state)
|
155
54
|
end
|
156
|
-
|
157
|
-
# Write out the PDF Trailer, as per spec 3.4.4
|
158
|
-
#
|
159
|
-
def render_trailer(output)
|
160
|
-
trailer_hash = {:Size => state.store.size + 1,
|
161
|
-
:Root => state.store.root,
|
162
|
-
:Info => state.store.info}
|
163
|
-
trailer_hash.merge!(state.trailer) if state.trailer
|
164
|
-
|
165
|
-
output << "trailer\n"
|
166
|
-
output << PDF::Core::PdfObject(trailer_hash) << "\n"
|
167
|
-
output << "startxref\n"
|
168
|
-
output << @xref_offset << "\n"
|
169
|
-
output << "%%EOF" << "\n"
|
170
|
-
end
|
171
|
-
|
172
55
|
end
|
173
56
|
end
|
174
57
|
end
|
data/lib/prawn/font.rb
CHANGED
@@ -398,31 +398,10 @@ module Prawn
|
|
398
398
|
|
399
399
|
private
|
400
400
|
|
401
|
-
# generate a font identifier that hasn't been used on the
|
401
|
+
# generate a font identifier that hasn't been used on the current page yet
|
402
402
|
#
|
403
403
|
def generate_unique_id
|
404
|
-
|
405
|
-
|
406
|
-
while id.nil? || page_contains_font_id?(id)
|
407
|
-
offset += 1
|
408
|
-
id = :"F#{@document.font_registry.size + offset}"
|
409
|
-
end
|
410
|
-
|
411
|
-
id
|
412
|
-
end
|
413
|
-
|
414
|
-
# Returns true if the provided font identifier already exists in the document.
|
415
|
-
# This is used when adding new fonts to a document to ensure we don't step
|
416
|
-
# on fonts imported from a template.
|
417
|
-
#
|
418
|
-
# page_contains_font_id?("F1")
|
419
|
-
# => true
|
420
|
-
#
|
421
|
-
def page_contains_font_id?(id)
|
422
|
-
id = id.to_s
|
423
|
-
@document.state.page.fonts.keys.any? { |exist_id|
|
424
|
-
exist_id.to_s[0,id.size] == id
|
425
|
-
}
|
404
|
+
:"F#{@document.font_registry.size + 1}"
|
426
405
|
end
|
427
406
|
|
428
407
|
def size
|
data/lib/prawn/font/ttf.rb
CHANGED
@@ -245,7 +245,7 @@ module Prawn
|
|
245
245
|
|
246
246
|
# Embed the font metrics in the document after everything has been
|
247
247
|
# drawn, just before the document is emitted.
|
248
|
-
@document.before_render { |doc| embed(ref, subset) }
|
248
|
+
@document.renderer.before_render { |doc| embed(ref, subset) }
|
249
249
|
|
250
250
|
ref
|
251
251
|
end
|
data/lib/prawn/graphics.rb
CHANGED
@@ -47,7 +47,7 @@ module Prawn
|
|
47
47
|
#
|
48
48
|
def move_to(*point)
|
49
49
|
x,y = map_to_absolute(point)
|
50
|
-
add_content("%.3f %.3f m" % [ x, y ])
|
50
|
+
renderer.add_content("%.3f %.3f m" % [ x, y ])
|
51
51
|
end
|
52
52
|
|
53
53
|
# Draws a line from the current drawing position to the specified point.
|
@@ -58,7 +58,7 @@ module Prawn
|
|
58
58
|
#
|
59
59
|
def line_to(*point)
|
60
60
|
x,y = map_to_absolute(point)
|
61
|
-
add_content("%.3f %.3f l" % [ x, y ])
|
61
|
+
renderer.add_content("%.3f %.3f l" % [ x, y ])
|
62
62
|
end
|
63
63
|
|
64
64
|
# Draws a Bezier curve from the current drawing position to the
|
@@ -72,7 +72,7 @@ module Prawn
|
|
72
72
|
"as :bounds => [[x1,y1],[x2,y2]]"
|
73
73
|
|
74
74
|
curve_points = (options[:bounds] << dest).map { |e| map_to_absolute(e) }
|
75
|
-
add_content("%.3f %.3f %.3f %.3f %.3f %.3f c" %
|
75
|
+
renderer.add_content("%.3f %.3f %.3f %.3f %.3f %.3f c" %
|
76
76
|
curve_points.flatten )
|
77
77
|
end
|
78
78
|
|
@@ -83,7 +83,7 @@ module Prawn
|
|
83
83
|
#
|
84
84
|
def rectangle(point,width,height)
|
85
85
|
x,y = map_to_absolute(point)
|
86
|
-
add_content("%.3f %.3f %.3f %.3f re" % [ x, y - height, width, height ])
|
86
|
+
renderer.add_content("%.3f %.3f %.3f %.3f re" % [ x, y - height, width, height ])
|
87
87
|
end
|
88
88
|
|
89
89
|
# Draws a rounded rectangle given <tt>point</tt>, <tt>width</tt> and
|
@@ -239,7 +239,7 @@ module Prawn
|
|
239
239
|
line_to(*point)
|
240
240
|
end
|
241
241
|
# close the path
|
242
|
-
add_content "h"
|
242
|
+
renderer.add_content "h"
|
243
243
|
end
|
244
244
|
|
245
245
|
# Draws a rounded polygon from specified points using the radius to define bezier curves
|
@@ -255,7 +255,7 @@ module Prawn
|
|
255
255
|
rounded_vertex(radius, points[i], points[i + 1], points[i + 2])
|
256
256
|
end
|
257
257
|
# close the path
|
258
|
-
add_content "h"
|
258
|
+
renderer.add_content "h"
|
259
259
|
end
|
260
260
|
|
261
261
|
|
@@ -276,7 +276,7 @@ module Prawn
|
|
276
276
|
#
|
277
277
|
def stroke
|
278
278
|
yield if block_given?
|
279
|
-
add_content "S"
|
279
|
+
renderer.add_content "S"
|
280
280
|
end
|
281
281
|
|
282
282
|
# Closes and strokes the current path. If a block is provided, yields to
|
@@ -284,7 +284,7 @@ module Prawn
|
|
284
284
|
#
|
285
285
|
def close_and_stroke
|
286
286
|
yield if block_given?
|
287
|
-
add_content "s"
|
287
|
+
renderer.add_content "s"
|
288
288
|
end
|
289
289
|
|
290
290
|
# Draws and strokes a rectangle represented by the current bounding box
|
@@ -364,7 +364,7 @@ module Prawn
|
|
364
364
|
#
|
365
365
|
def fill(options={})
|
366
366
|
yield if block_given?
|
367
|
-
add_content(options[:fill_rule] == :even_odd ? "f*" : "f")
|
367
|
+
renderer.add_content(options[:fill_rule] == :even_odd ? "f*" : "f")
|
368
368
|
end
|
369
369
|
|
370
370
|
# Closes, fills, and strokes the current path. If a block is provided,
|
@@ -378,13 +378,13 @@ module Prawn
|
|
378
378
|
#
|
379
379
|
def fill_and_stroke(options={})
|
380
380
|
yield if block_given?
|
381
|
-
add_content(options[:fill_rule] == :even_odd ? "b*" : "b")
|
381
|
+
renderer.add_content(options[:fill_rule] == :even_odd ? "b*" : "b")
|
382
382
|
end
|
383
383
|
|
384
384
|
# Closes the current path.
|
385
385
|
#
|
386
386
|
def close_path
|
387
|
-
add_content "h"
|
387
|
+
renderer.add_content "h"
|
388
388
|
end
|
389
389
|
|
390
390
|
##
|
@@ -612,7 +612,7 @@ module Prawn
|
|
612
612
|
end
|
613
613
|
|
614
614
|
def write_line_width
|
615
|
-
add_content("#{current_line_width} w")
|
615
|
+
renderer.add_content("#{current_line_width} w")
|
616
616
|
end
|
617
617
|
|
618
618
|
def map_to_absolute(*point)
|