pdf-core 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pdf/core.rb +1 -0
- data/lib/pdf/core/renderer.rb +268 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f20557e9a614c10244d01e2b1ed78d4434cef6c
|
4
|
+
data.tar.gz: 8668979b6592e45d81d8274e8acd387642e5a3fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68d6e43d2579ff909d9cabc4a9d641648e9830685a2d2b76d24fe85046e48ea22fb7bbb4bf005d3442d4595b44b18dddd29dad37f99ecd32a3b9fa515490503e
|
7
|
+
data.tar.gz: f6f49f285719b0d72d3113bc465db2e588d52c2b1c096c7285cb56f43744a31122d4a0c8338ab88353b29555bb352d4c2cc662eac2c73164948dda3ca5c72933
|
data/lib/pdf/core.rb
CHANGED
@@ -0,0 +1,268 @@
|
|
1
|
+
require "stringio"
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Core
|
5
|
+
class Renderer
|
6
|
+
def initialize(state)
|
7
|
+
@state = state
|
8
|
+
@state.populate_pages_from_store(self)
|
9
|
+
|
10
|
+
min_version(state.store.min_version) if state.store.min_version
|
11
|
+
|
12
|
+
@page_number = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :state
|
16
|
+
|
17
|
+
# Creates a new Reference and adds it to the Document's object
|
18
|
+
# list. The +data+ argument is anything that Prawn::PdfObject() can convert.
|
19
|
+
#
|
20
|
+
# Returns the identifier which points to the reference in the ObjectStore
|
21
|
+
#
|
22
|
+
def ref(data)
|
23
|
+
ref!(data).identifier
|
24
|
+
end
|
25
|
+
|
26
|
+
# Like ref, but returns the actual reference instead of its identifier.
|
27
|
+
#
|
28
|
+
# While you can use this to build up nested references within the object
|
29
|
+
# tree, it is recommended to persist only identifiers, and then provide
|
30
|
+
# helper methods to look up the actual references in the ObjectStore
|
31
|
+
# if needed. If you take this approach, Document::Snapshot
|
32
|
+
# will probably work with your extension
|
33
|
+
#
|
34
|
+
def ref!(data)
|
35
|
+
state.store.ref(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
# At any stage in the object tree an object can be replaced with an
|
39
|
+
# indirect reference. To get access to the object safely, regardless
|
40
|
+
# of if it's hidden behind a Prawn::Reference, wrap it in deref().
|
41
|
+
#
|
42
|
+
def deref(obj)
|
43
|
+
obj.is_a?(PDF::Core::Reference) ? obj.data : obj
|
44
|
+
end
|
45
|
+
|
46
|
+
# Appends a raw string to the current page content.
|
47
|
+
#
|
48
|
+
# # Raw line drawing example:
|
49
|
+
# x1,y1,x2,y2 = 100,500,300,550
|
50
|
+
# pdf.add_content("%.3f %.3f m" % [ x1, y1 ]) # move
|
51
|
+
# pdf.add_content("%.3f %.3f l" % [ x2, y2 ]) # draw path
|
52
|
+
# pdf.add_content("S") # stroke
|
53
|
+
#
|
54
|
+
def add_content(str)
|
55
|
+
save_graphics_state if graphic_state.nil?
|
56
|
+
state.page.content << str << "\n"
|
57
|
+
end
|
58
|
+
|
59
|
+
# The Name dictionary (PDF spec 3.6.3) for this document. It is
|
60
|
+
# lazily initialized, so that documents that do not need a name
|
61
|
+
# dictionary do not incur the additional overhead.
|
62
|
+
#
|
63
|
+
def names
|
64
|
+
state.store.root.data[:Names] ||= ref!(:Type => :Names)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns true if the Names dictionary is in use for this document.
|
68
|
+
#
|
69
|
+
def names?
|
70
|
+
state.store.root.data[:Names]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Defines a block to be called just before the document is rendered.
|
74
|
+
#
|
75
|
+
def before_render(&block)
|
76
|
+
state.before_render_callbacks << block
|
77
|
+
end
|
78
|
+
|
79
|
+
# Defines a block to be called just before a new page is started.
|
80
|
+
#
|
81
|
+
def on_page_create(&block)
|
82
|
+
if block_given?
|
83
|
+
state.on_page_create_callback = block
|
84
|
+
else
|
85
|
+
state.on_page_create_callback = nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def start_new_page(options = {})
|
90
|
+
if last_page = state.page
|
91
|
+
last_page_size = last_page.size
|
92
|
+
last_page_layout = last_page.layout
|
93
|
+
last_page_margins = last_page.margins
|
94
|
+
end
|
95
|
+
|
96
|
+
page_options = {:size => options[:size] || last_page_size,
|
97
|
+
:layout => options[:layout] || last_page_layout,
|
98
|
+
:margins => last_page_margins}
|
99
|
+
if last_page
|
100
|
+
new_graphic_state = last_page.graphic_state.dup if last_page.graphic_state
|
101
|
+
|
102
|
+
#erase the color space so that it gets reset on new page for fussy pdf-readers
|
103
|
+
new_graphic_state.color_space = {} if new_graphic_state
|
104
|
+
page_options.merge!(:graphic_state => new_graphic_state)
|
105
|
+
end
|
106
|
+
|
107
|
+
state.page = PDF::Core::Page.new(self, page_options)
|
108
|
+
|
109
|
+
state.insert_page(state.page, @page_number)
|
110
|
+
@page_number += 1
|
111
|
+
|
112
|
+
state.on_page_create_action(self)
|
113
|
+
end
|
114
|
+
|
115
|
+
def page_count
|
116
|
+
state.page_count
|
117
|
+
end
|
118
|
+
|
119
|
+
# Re-opens the page with the given (1-based) page number so that you can
|
120
|
+
# draw on it.
|
121
|
+
#
|
122
|
+
# See Prawn::Document#number_pages for a sample usage of this capability.
|
123
|
+
|
124
|
+
def go_to_page(k)
|
125
|
+
@page_number = k
|
126
|
+
state.page = state.pages[k-1]
|
127
|
+
end
|
128
|
+
|
129
|
+
def finalize_all_page_contents
|
130
|
+
(1..page_count).each do |i|
|
131
|
+
go_to_page i
|
132
|
+
while graphic_stack.present?
|
133
|
+
restore_graphics_state
|
134
|
+
end
|
135
|
+
state.page.finalize
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# raise the PDF version of the file we're going to generate.
|
140
|
+
# A private method, designed for internal use when the user adds a feature
|
141
|
+
# to their document that requires a particular version.
|
142
|
+
#
|
143
|
+
def min_version(min)
|
144
|
+
state.version = min if min > state.version
|
145
|
+
end
|
146
|
+
|
147
|
+
# Renders the PDF document to string.
|
148
|
+
# Pass an open file descriptor to render to file.
|
149
|
+
#
|
150
|
+
def render(output = StringIO.new)
|
151
|
+
if output.instance_of?(StringIO)
|
152
|
+
output.set_encoding(::Encoding::ASCII_8BIT)
|
153
|
+
end
|
154
|
+
finalize_all_page_contents
|
155
|
+
|
156
|
+
render_header(output)
|
157
|
+
render_body(output)
|
158
|
+
render_xref(output)
|
159
|
+
render_trailer(output)
|
160
|
+
if output.instance_of?(StringIO)
|
161
|
+
str = output.string
|
162
|
+
str.force_encoding(::Encoding::ASCII_8BIT)
|
163
|
+
return str
|
164
|
+
else
|
165
|
+
return nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Renders the PDF document to file.
|
170
|
+
#
|
171
|
+
# pdf.render_file "foo.pdf"
|
172
|
+
#
|
173
|
+
def render_file(filename)
|
174
|
+
File.open(filename, "wb") { |f| render(f) }
|
175
|
+
end
|
176
|
+
|
177
|
+
# Write out the PDF Header, as per spec 3.4.1
|
178
|
+
#
|
179
|
+
def render_header(output)
|
180
|
+
state.before_render_actions(self)
|
181
|
+
|
182
|
+
# pdf version
|
183
|
+
output << "%PDF-#{state.version}\n"
|
184
|
+
|
185
|
+
# 4 binary chars, as recommended by the spec
|
186
|
+
output << "%\xFF\xFF\xFF\xFF\n"
|
187
|
+
end
|
188
|
+
|
189
|
+
# Write out the PDF Body, as per spec 3.4.2
|
190
|
+
#
|
191
|
+
def render_body(output)
|
192
|
+
state.render_body(output)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Write out the PDF Cross Reference Table, as per spec 3.4.3
|
196
|
+
#
|
197
|
+
def render_xref(output)
|
198
|
+
@xref_offset = output.size
|
199
|
+
output << "xref\n"
|
200
|
+
output << "0 #{state.store.size + 1}\n"
|
201
|
+
output << "0000000000 65535 f \n"
|
202
|
+
state.store.each do |ref|
|
203
|
+
output.printf("%010d", ref.offset)
|
204
|
+
output << " 00000 n \n"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Write out the PDF Trailer, as per spec 3.4.4
|
209
|
+
#
|
210
|
+
def render_trailer(output)
|
211
|
+
trailer_hash = {:Size => state.store.size + 1,
|
212
|
+
:Root => state.store.root,
|
213
|
+
:Info => state.store.info}
|
214
|
+
trailer_hash.merge!(state.trailer) if state.trailer
|
215
|
+
|
216
|
+
output << "trailer\n"
|
217
|
+
output << PDF::Core::PdfObject(trailer_hash) << "\n"
|
218
|
+
output << "startxref\n"
|
219
|
+
output << @xref_offset << "\n"
|
220
|
+
output << "%%EOF" << "\n"
|
221
|
+
end
|
222
|
+
|
223
|
+
def open_graphics_state
|
224
|
+
add_content "q"
|
225
|
+
end
|
226
|
+
|
227
|
+
def close_graphics_state
|
228
|
+
add_content "Q"
|
229
|
+
end
|
230
|
+
|
231
|
+
def save_graphics_state(graphic_state = nil)
|
232
|
+
graphic_stack.save_graphic_state(graphic_state)
|
233
|
+
open_graphics_state
|
234
|
+
if block_given?
|
235
|
+
yield
|
236
|
+
restore_graphics_state
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Returns true if content streams will be compressed before rendering,
|
241
|
+
# false otherwise
|
242
|
+
#
|
243
|
+
def compression_enabled?
|
244
|
+
!!state.compress
|
245
|
+
end
|
246
|
+
|
247
|
+
# Pops the last saved graphics state off the graphics state stack and
|
248
|
+
# restores the state to those values
|
249
|
+
def restore_graphics_state
|
250
|
+
if graphic_stack.empty?
|
251
|
+
raise PDF::Core::Errors::EmptyGraphicStateStack,
|
252
|
+
"\n You have reached the end of the graphic state stack"
|
253
|
+
end
|
254
|
+
close_graphics_state
|
255
|
+
graphic_stack.restore_graphic_state
|
256
|
+
end
|
257
|
+
|
258
|
+
def graphic_stack
|
259
|
+
state.page.stack
|
260
|
+
end
|
261
|
+
|
262
|
+
def graphic_state
|
263
|
+
save_graphics_state unless graphic_stack.current_state
|
264
|
+
graphic_stack.current_state
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pdf-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregory Brown
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2014-09-
|
15
|
+
date: 2014-09-16 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: pdf-reader
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- lib/pdf/core/page_geometry.rb
|
119
119
|
- lib/pdf/core/pdf_object.rb
|
120
120
|
- lib/pdf/core/reference.rb
|
121
|
+
- lib/pdf/core/renderer.rb
|
121
122
|
- lib/pdf/core/stream.rb
|
122
123
|
- lib/pdf/core/text.rb
|
123
124
|
- pdf-core.gemspec
|