pdf-core 0.3.1 → 0.4.0
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.
- 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
|