hexapdf 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -0
- data/CONTRIBUTERS +1 -1
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/examples/arc.rb +1 -1
- data/examples/graphics.rb +1 -1
- data/examples/hello_world.rb +1 -1
- data/examples/merging.rb +1 -1
- data/examples/show_char_bboxes.rb +1 -1
- data/examples/standard_pdf_fonts.rb +1 -1
- data/examples/truetype.rb +1 -1
- data/lib/hexapdf/cli.rb +14 -7
- data/lib/hexapdf/cli/extract.rb +1 -1
- data/lib/hexapdf/cli/info.rb +2 -2
- data/lib/hexapdf/cli/inspect.rb +4 -4
- data/lib/hexapdf/cli/modify.rb +151 -51
- data/lib/hexapdf/configuration.rb +1 -1
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/dictionary.rb +6 -19
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/document.rb +23 -16
- data/lib/hexapdf/document/files.rb +130 -0
- data/lib/hexapdf/{font_utils.rb → document/fonts.rb} +40 -38
- data/lib/hexapdf/document/images.rb +117 -0
- data/lib/hexapdf/document/pages.rb +125 -0
- data/lib/hexapdf/encryption/aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +10 -10
- data/lib/hexapdf/encryption/standard_security_handler.rb +11 -8
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -6
- data/lib/hexapdf/font/cmap/writer.rb +5 -7
- data/lib/hexapdf/font/true_type.rb +4 -1
- data/lib/hexapdf/font/true_type/font.rb +8 -16
- data/lib/hexapdf/font/true_type/table.rb +5 -16
- data/lib/hexapdf/font/true_type/table/cmap.rb +2 -7
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +2 -6
- data/lib/hexapdf/font/true_type/table/directory.rb +0 -5
- data/lib/hexapdf/font/true_type/table/glyf.rb +3 -11
- data/lib/hexapdf/font/true_type/table/head.rb +0 -12
- data/lib/hexapdf/font/true_type/table/hhea.rb +0 -7
- data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -5
- data/lib/hexapdf/font/true_type/table/loca.rb +0 -4
- data/lib/hexapdf/font/true_type/table/maxp.rb +0 -8
- data/lib/hexapdf/font/true_type/table/name.rb +3 -17
- data/lib/hexapdf/font/true_type/table/os2.rb +0 -14
- data/lib/hexapdf/font/true_type/table/post.rb +0 -8
- data/lib/hexapdf/font/true_type_wrapper.rb +1 -1
- data/lib/hexapdf/font/type1.rb +2 -2
- data/lib/hexapdf/font/type1/font.rb +2 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +10 -1
- data/lib/hexapdf/font/type1_wrapper.rb +2 -1
- data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/object.rb +18 -5
- data/lib/hexapdf/rectangle.rb +8 -1
- data/lib/hexapdf/revisions.rb +4 -2
- data/lib/hexapdf/serializer.rb +3 -3
- data/lib/hexapdf/stream.rb +3 -2
- data/lib/hexapdf/task/dereference.rb +4 -5
- data/lib/hexapdf/task/optimize.rb +6 -3
- data/lib/hexapdf/tokenizer.rb +3 -3
- data/lib/hexapdf/type/file_specification.rb +2 -2
- data/lib/hexapdf/type/form.rb +19 -0
- data/lib/hexapdf/type/page.rb +21 -6
- data/lib/hexapdf/type/page_tree_node.rb +27 -34
- data/lib/hexapdf/utils/bit_stream.rb +1 -1
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/man/man1/hexapdf.1 +259 -187
- data/test/hexapdf/content/graphic_object/test_arc.rb +1 -1
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +1 -1
- data/test/hexapdf/content/graphic_object/test_solid_arc.rb +1 -1
- data/test/hexapdf/content/test_canvas.rb +1 -1
- data/test/hexapdf/document/test_files.rb +71 -0
- data/test/hexapdf/{test_font_utils.rb → document/test_fonts.rb} +1 -2
- data/test/hexapdf/document/test_images.rb +78 -0
- data/test/hexapdf/document/test_pages.rb +114 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +26 -5
- data/test/hexapdf/font/test_true_type_wrapper.rb +1 -1
- data/test/hexapdf/font/true_type/common.rb +0 -4
- data/test/hexapdf/font/true_type/table/test_cmap.rb +0 -6
- data/test/hexapdf/font/true_type/table/test_directory.rb +0 -5
- data/test/hexapdf/font/true_type/table/test_glyf.rb +5 -8
- data/test/hexapdf/font/true_type/table/test_head.rb +0 -20
- data/test/hexapdf/font/true_type/table/test_hhea.rb +0 -7
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +2 -7
- data/test/hexapdf/font/true_type/table/test_loca.rb +4 -8
- data/test/hexapdf/font/true_type/table/test_maxp.rb +0 -7
- data/test/hexapdf/font/true_type/table/test_name.rb +0 -19
- data/test/hexapdf/font/true_type/table/test_os2.rb +0 -8
- data/test/hexapdf/font/true_type/table/test_post.rb +0 -13
- data/test/hexapdf/font/true_type/test_font.rb +14 -38
- data/test/hexapdf/font/true_type/test_table.rb +0 -9
- data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
- data/test/hexapdf/task/test_dereference.rb +5 -1
- data/test/hexapdf/task/test_optimize.rb +1 -1
- data/test/hexapdf/test_dictionary.rb +4 -0
- data/test/hexapdf/test_document.rb +0 -7
- data/test/hexapdf/test_importer.rb +4 -4
- data/test/hexapdf/test_object.rb +31 -9
- data/test/hexapdf/test_rectangle.rb +18 -0
- data/test/hexapdf/test_revisions.rb +7 -0
- data/test/hexapdf/test_serializer.rb +6 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_form.rb +12 -0
- data/test/hexapdf/type/test_page.rb +39 -20
- data/test/hexapdf/type/test_page_tree_node.rb +28 -21
- metadata +21 -9
- data/lib/hexapdf/document_utils.rb +0 -209
- data/test/hexapdf/test_document_utils.rb +0 -144
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
4
|
require 'hexapdf/rectangle'
|
5
|
+
require 'hexapdf/document'
|
5
6
|
|
6
7
|
describe HexaPDF::Rectangle do
|
7
8
|
describe "after_data_change" do
|
@@ -33,4 +34,21 @@ describe HexaPDF::Rectangle do
|
|
33
34
|
assert_equal(2, rect.width)
|
34
35
|
assert_equal(4, rect.height)
|
35
36
|
end
|
37
|
+
|
38
|
+
describe "validation" do
|
39
|
+
it "ensures that it is a correct PDF rectangle" do
|
40
|
+
doc = HexaPDF::Document.new
|
41
|
+
rect = HexaPDF::Rectangle.new([0, 1, 2, 3], document: doc)
|
42
|
+
assert(rect.validate)
|
43
|
+
|
44
|
+
rect.value.shift
|
45
|
+
refute(rect.validate)
|
46
|
+
|
47
|
+
rect.value.unshift(:A)
|
48
|
+
refute(rect.validate)
|
49
|
+
|
50
|
+
rect.data.value = :A
|
51
|
+
refute(rect.validate)
|
52
|
+
end
|
53
|
+
end
|
36
54
|
end
|
@@ -82,6 +82,13 @@ EOF
|
|
82
82
|
assert_equal(1, @revisions.each.to_a.size)
|
83
83
|
assert_equal([10, 200], @revisions.current.each.to_a.sort.map(&:value))
|
84
84
|
end
|
85
|
+
|
86
|
+
it "handles objects correctly that are in multiple revisions" do
|
87
|
+
@revisions.current.add(@revisions[0].object(1))
|
88
|
+
@revisions.merge
|
89
|
+
assert_equal(1, @revisions.each.to_a.size)
|
90
|
+
assert_equal([10, 200], @revisions.current.each.to_a.sort.map(&:value))
|
91
|
+
end
|
85
92
|
end
|
86
93
|
|
87
94
|
describe "initialize" do
|
@@ -25,6 +25,12 @@ describe HexaPDF::Serializer do
|
|
25
25
|
assert_equal(result, @serializer.serialize(object))
|
26
26
|
end
|
27
27
|
|
28
|
+
it "works correctly with unnamed classes and modules" do
|
29
|
+
klass = Class.new(String)
|
30
|
+
s = klass.new("test")
|
31
|
+
assert_serialized("(test)", s)
|
32
|
+
end
|
33
|
+
|
28
34
|
it "serializes nil" do
|
29
35
|
assert_serialized("null", nil)
|
30
36
|
end
|
data/test/hexapdf/test_writer.rb
CHANGED
@@ -40,7 +40,7 @@ startxref
|
|
40
40
|
219
|
41
41
|
%%EOF
|
42
42
|
3 0 obj
|
43
|
-
<</Producer(HexaPDF version 0.
|
43
|
+
<</Producer(HexaPDF version 0.2.0)>>
|
44
44
|
endobj
|
45
45
|
xref
|
46
46
|
3 1
|
@@ -72,7 +72,7 @@ startxref
|
|
72
72
|
141
|
73
73
|
%%EOF
|
74
74
|
6 0 obj
|
75
|
-
<</Producer(HexaPDF version 0.
|
75
|
+
<</Producer(HexaPDF version 0.2.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -57,4 +57,16 @@ describe HexaPDF::Type::Form do
|
|
57
57
|
assert_equal([[:set_line_width, [10]]], processor.recorded_ops)
|
58
58
|
end
|
59
59
|
end
|
60
|
+
|
61
|
+
describe "canvas" do
|
62
|
+
it "always returns the same Canvas instance" do
|
63
|
+
canvas = @form.canvas
|
64
|
+
assert_same(canvas, @form.canvas)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "fails if the form XObject already has data" do
|
68
|
+
@form.stream = '10 w'
|
69
|
+
assert_raises(HexaPDF::Error) { @form.canvas }
|
70
|
+
end
|
71
|
+
end
|
60
72
|
end
|
@@ -49,13 +49,11 @@ describe HexaPDF::Type::Page do
|
|
49
49
|
|
50
50
|
@kid[:Rotate] = :kid_rotate
|
51
51
|
assert_equal(:kid_rotate, @page[:Rotate])
|
52
|
-
@kid.delete(:Rotate)
|
53
|
-
assert_equal(0, @page[:Rotate])
|
54
52
|
end
|
55
53
|
|
56
|
-
it "
|
57
|
-
page
|
58
|
-
|
54
|
+
it "returns nil or the default value if no value is set anywhere" do
|
55
|
+
assert_nil(@page[:MediaBox])
|
56
|
+
assert_equal(0, @page[:Rotate])
|
59
57
|
end
|
60
58
|
end
|
61
59
|
|
@@ -71,7 +69,7 @@ describe HexaPDF::Type::Page do
|
|
71
69
|
|
72
70
|
describe "box" do
|
73
71
|
before do
|
74
|
-
@page = @doc.pages.
|
72
|
+
@page = @doc.pages.add
|
75
73
|
end
|
76
74
|
|
77
75
|
it "returns the correct media box" do
|
@@ -106,13 +104,13 @@ describe HexaPDF::Type::Page do
|
|
106
104
|
|
107
105
|
describe "contents" do
|
108
106
|
it "returns the contents of a single content stream" do
|
109
|
-
page = @doc.pages.
|
107
|
+
page = @doc.pages.add
|
110
108
|
page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
|
111
109
|
assert_equal('q 10 w Q', page.contents)
|
112
110
|
end
|
113
111
|
|
114
112
|
it "returns the concatenated contents of multiple content stream" do
|
115
|
-
page = @doc.pages.
|
113
|
+
page = @doc.pages.add
|
116
114
|
page[:Contents] = [@doc.wrap({}, stream: 'q 10'), @doc.wrap({}, stream: 'w Q')]
|
117
115
|
assert_equal('q 10 w Q', page.contents)
|
118
116
|
end
|
@@ -120,13 +118,13 @@ describe HexaPDF::Type::Page do
|
|
120
118
|
|
121
119
|
describe "contents=" do
|
122
120
|
it "creates a content stream if none already exist" do
|
123
|
-
page = @doc.pages.
|
121
|
+
page = @doc.pages.add
|
124
122
|
page.contents = 'test'
|
125
123
|
assert_equal('test', page[:Contents].stream)
|
126
124
|
end
|
127
125
|
|
128
126
|
it "reuses an existing content stream" do
|
129
|
-
page = @doc.pages.
|
127
|
+
page = @doc.pages.add
|
130
128
|
page[:Contents] = content = @doc.wrap({}, stream: 'q 10 w Q')
|
131
129
|
page.contents = 'test'
|
132
130
|
assert_equal(content, page[:Contents])
|
@@ -134,7 +132,7 @@ describe HexaPDF::Type::Page do
|
|
134
132
|
end
|
135
133
|
|
136
134
|
it "reuses the first content stream and deletes the rest if more than one exist" do
|
137
|
-
page = @doc.pages.
|
135
|
+
page = @doc.pages.add
|
138
136
|
page[:Contents] = [content = @doc.add({}, stream: 'q 10 w Q'), @doc.add({}, stream: 'q Q')]
|
139
137
|
page.contents = 'test'
|
140
138
|
assert_equal(content, page[:Contents])
|
@@ -144,33 +142,54 @@ describe HexaPDF::Type::Page do
|
|
144
142
|
|
145
143
|
describe "resources" do
|
146
144
|
it "creates the resource dictionary if it is not found" do
|
147
|
-
page = @doc.add(Type: :Page, Parent: @doc.pages)
|
145
|
+
page = @doc.add(Type: :Page, Parent: @doc.pages.root)
|
148
146
|
resources = page.resources
|
149
147
|
assert_equal(:XXResources, resources.type)
|
150
148
|
assert_equal({}, resources.value)
|
151
149
|
end
|
152
150
|
|
153
151
|
it "returns the already used resource dictionary" do
|
154
|
-
@doc.pages[:Resources] = {Font: {F1: nil}}
|
155
|
-
page = @doc.pages.
|
152
|
+
@doc.pages.root[:Resources] = {Font: {F1: nil}}
|
153
|
+
page = @doc.pages.add(@doc.add(Type: :Page))
|
156
154
|
resources = page.resources
|
157
155
|
assert_equal(:XXResources, resources.type)
|
158
|
-
assert_equal(@doc.pages[:Resources], resources)
|
156
|
+
assert_equal(@doc.pages.root[:Resources], resources)
|
159
157
|
end
|
160
158
|
end
|
161
159
|
|
162
160
|
describe "process_contents" do
|
163
161
|
it "parses the contents and processes it" do
|
164
|
-
page = @doc.pages.
|
162
|
+
page = @doc.pages.add
|
165
163
|
page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
|
166
164
|
assert_operators(page, [[:save_graphics_state], [:set_line_width, [10]],
|
167
165
|
[:restore_graphics_state]])
|
168
166
|
end
|
169
167
|
end
|
170
168
|
|
169
|
+
describe "index" do
|
170
|
+
it "returns the index of the page in the page tree" do
|
171
|
+
kid1 = @doc.add(Type: :Pages, Parent: @doc.pages.root, Count: 4)
|
172
|
+
@doc.pages.root[:Kids] << kid1
|
173
|
+
|
174
|
+
kid11 = @doc.add(Type: :Pages, Parent: kid1)
|
175
|
+
page1 = kid11.add_page
|
176
|
+
kid1[:Kids] << kid11
|
177
|
+
page2 = kid1.add_page
|
178
|
+
kid12 = @doc.add(Type: :Pages, Parent: kid1)
|
179
|
+
page3 = kid12.add_page
|
180
|
+
page4 = kid12.add_page
|
181
|
+
kid1[:Kids] << kid12
|
182
|
+
|
183
|
+
assert_equal(0, page1.index)
|
184
|
+
assert_equal(1, page2.index)
|
185
|
+
assert_equal(2, page3.index)
|
186
|
+
assert_equal(3, page4.index)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
171
190
|
describe "canvas" do
|
172
191
|
before do
|
173
|
-
@page = @doc.pages.
|
192
|
+
@page = @doc.pages.add
|
174
193
|
end
|
175
194
|
|
176
195
|
it "works correctly if invoked on an empty page, using type :page in first invocation" do
|
@@ -234,7 +253,7 @@ describe HexaPDF::Type::Page do
|
|
234
253
|
|
235
254
|
describe "to_form_xobject" do
|
236
255
|
it "creates an independent form xobject" do
|
237
|
-
page = @doc.pages.
|
256
|
+
page = @doc.pages.add
|
238
257
|
page.contents = "test"
|
239
258
|
form = page.to_form_xobject
|
240
259
|
refute(form.indirect?)
|
@@ -242,13 +261,13 @@ describe HexaPDF::Type::Page do
|
|
242
261
|
end
|
243
262
|
|
244
263
|
it "works for pages without content" do
|
245
|
-
page = @doc.pages.
|
264
|
+
page = @doc.pages.add
|
246
265
|
form = page.to_form_xobject
|
247
266
|
assert_equal('', form.stream)
|
248
267
|
end
|
249
268
|
|
250
269
|
it "uses the raw stream data if possible to avoid unnecessary work" do
|
251
|
-
page = @doc.pages.
|
270
|
+
page = @doc.pages.add
|
252
271
|
page.contents = HexaPDF::StreamData.new(StringIO.new("test"))
|
253
272
|
form = page.to_form_xobject
|
254
273
|
assert_same(form.raw_stream, page[:Contents].raw_stream)
|
@@ -151,40 +151,47 @@ describe HexaPDF::Type::PageTreeNode do
|
|
151
151
|
define_multilevel_page_tree
|
152
152
|
end
|
153
153
|
|
154
|
-
it "
|
155
|
-
assert_nil(@root.delete_page(20))
|
156
|
-
assert_nil(@root.delete_page(-20))
|
157
|
-
assert_equal(8, @root.page_count)
|
158
|
-
end
|
159
|
-
|
160
|
-
it "deletes the correct page" do
|
154
|
+
it "deletes the correct page by index" do
|
161
155
|
assert_equal(@pages[2], @root.delete_page(2))
|
162
156
|
assert_equal(2, @kid12.page_count)
|
163
157
|
assert_equal(4, @kid1.page_count)
|
164
158
|
assert_equal(7, @root.page_count)
|
159
|
+
assert_nil(@pages[2][:Parent])
|
165
160
|
|
166
161
|
assert_equal(@pages[5], @root.delete_page(4))
|
167
162
|
assert_equal(6, @root.page_count)
|
163
|
+
assert_nil(@pages[5][:Parent])
|
168
164
|
end
|
169
165
|
|
170
|
-
it "deletes
|
171
|
-
assert_equal(@pages[
|
172
|
-
assert_equal(
|
166
|
+
it "deletes the given page" do
|
167
|
+
assert_equal(@pages[2], @root.delete_page(@pages[2]))
|
168
|
+
assert_equal(@pages[5], @root.delete_page(@pages[5]))
|
169
|
+
end
|
170
|
+
|
171
|
+
it "allows deleting a page from an intermediary node" do
|
172
|
+
assert_equal(@pages[2], @kid1.delete_page(@pages[2]))
|
173
173
|
assert_equal(7, @root.page_count)
|
174
|
-
assert_nil(@doc.object(@kid11).value)
|
175
|
-
assert_equal(@pages[1], @kid1[:Kids][0])
|
176
174
|
end
|
177
175
|
|
178
|
-
it "
|
179
|
-
|
180
|
-
|
181
|
-
@root
|
182
|
-
|
176
|
+
it "does nothing if the page index is not valid" do
|
177
|
+
assert_nil(@root.delete_page(20))
|
178
|
+
assert_nil(@root.delete_page(-20))
|
179
|
+
assert_equal(8, @root.page_count)
|
180
|
+
end
|
183
181
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
182
|
+
it "does nothing if the page is not in its parent's /Kids array" do
|
183
|
+
@kid12[:Kids].shift
|
184
|
+
assert_nil(@root.delete_page(@pages[2]))
|
185
|
+
assert_equal(8, @root.page_count)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "does nothing if the page is not part of the page tree" do
|
189
|
+
pages = @doc.add(Type: :Pages, Count: 1)
|
190
|
+
page = @doc.add(Type: :Page, Parent: pages)
|
191
|
+
pages[:Kids] << page
|
192
|
+
|
193
|
+
assert_nil(@root.delete_page(page))
|
194
|
+
assert_equal(8, @root.page_count)
|
188
195
|
end
|
189
196
|
end
|
190
197
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -31,19 +31,25 @@ dependencies:
|
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 3.0.1
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: kramdown
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '0
|
39
|
+
version: '1.0'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.13.0
|
40
43
|
type: :development
|
41
44
|
prerelease: false
|
42
45
|
version_requirements: !ruby/object:Gem::Requirement
|
43
46
|
requirements:
|
44
47
|
- - "~>"
|
45
48
|
- !ruby/object:Gem::Version
|
46
|
-
version: '0
|
49
|
+
version: '1.0'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.13.0
|
47
53
|
description: |-
|
48
54
|
HexaPDF is a pure Ruby library with an accompanying application for working with PDF files.
|
49
55
|
|
@@ -56,6 +62,7 @@ executables:
|
|
56
62
|
extensions: []
|
57
63
|
extra_rdoc_files: []
|
58
64
|
files:
|
65
|
+
- CHANGELOG.md
|
59
66
|
- CONTRIBUTERS
|
60
67
|
- LICENSE
|
61
68
|
- README.md
|
@@ -112,7 +119,10 @@ files:
|
|
112
119
|
- lib/hexapdf/dictionary.rb
|
113
120
|
- lib/hexapdf/dictionary_fields.rb
|
114
121
|
- lib/hexapdf/document.rb
|
115
|
-
- lib/hexapdf/
|
122
|
+
- lib/hexapdf/document/files.rb
|
123
|
+
- lib/hexapdf/document/fonts.rb
|
124
|
+
- lib/hexapdf/document/images.rb
|
125
|
+
- lib/hexapdf/document/pages.rb
|
116
126
|
- lib/hexapdf/encryption.rb
|
117
127
|
- lib/hexapdf/encryption/aes.rb
|
118
128
|
- lib/hexapdf/encryption/arc4.rb
|
@@ -173,7 +183,6 @@ files:
|
|
173
183
|
- lib/hexapdf/font_loader.rb
|
174
184
|
- lib/hexapdf/font_loader/from_configuration.rb
|
175
185
|
- lib/hexapdf/font_loader/standard14.rb
|
176
|
-
- lib/hexapdf/font_utils.rb
|
177
186
|
- lib/hexapdf/image_loader.rb
|
178
187
|
- lib/hexapdf/image_loader/jpeg.rb
|
179
188
|
- lib/hexapdf/image_loader/pdf.rb
|
@@ -306,6 +315,10 @@ files:
|
|
306
315
|
- test/hexapdf/content/test_parser.rb
|
307
316
|
- test/hexapdf/content/test_processor.rb
|
308
317
|
- test/hexapdf/content/test_transformation_matrix.rb
|
318
|
+
- test/hexapdf/document/test_files.rb
|
319
|
+
- test/hexapdf/document/test_fonts.rb
|
320
|
+
- test/hexapdf/document/test_images.rb
|
321
|
+
- test/hexapdf/document/test_pages.rb
|
309
322
|
- test/hexapdf/encryption/common.rb
|
310
323
|
- test/hexapdf/encryption/test_aes.rb
|
311
324
|
- test/hexapdf/encryption/test_arc4.rb
|
@@ -350,6 +363,7 @@ files:
|
|
350
363
|
- test/hexapdf/font/true_type/test_table.rb
|
351
364
|
- test/hexapdf/font/type1/test_afm_parser.rb
|
352
365
|
- test/hexapdf/font/type1/test_font.rb
|
366
|
+
- test/hexapdf/font/type1/test_font_metrics.rb
|
353
367
|
- test/hexapdf/font/type1/test_pfb_parser.rb
|
354
368
|
- test/hexapdf/font_loader/test_from_configuration.rb
|
355
369
|
- test/hexapdf/font_loader/test_standard14.rb
|
@@ -363,9 +377,7 @@ files:
|
|
363
377
|
- test/hexapdf/test_dictionary.rb
|
364
378
|
- test/hexapdf/test_dictionary_fields.rb
|
365
379
|
- test/hexapdf/test_document.rb
|
366
|
-
- test/hexapdf/test_document_utils.rb
|
367
380
|
- test/hexapdf/test_filter.rb
|
368
|
-
- test/hexapdf/test_font_utils.rb
|
369
381
|
- test/hexapdf/test_importer.rb
|
370
382
|
- test/hexapdf/test_object.rb
|
371
383
|
- test/hexapdf/test_parser.rb
|
@@ -1,209 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
#
|
3
|
-
#--
|
4
|
-
# This file is part of HexaPDF.
|
5
|
-
#
|
6
|
-
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
-
# Copyright (C) 2016 Thomas Leitner
|
8
|
-
#
|
9
|
-
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
-
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
-
# published by the Free Software Foundation with the addition of the
|
12
|
-
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
-
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
-
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
-
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
-
#
|
17
|
-
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
-
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
-
# License for more details.
|
21
|
-
#
|
22
|
-
# You should have received a copy of the GNU Affero General Public License
|
23
|
-
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
-
#
|
25
|
-
# The interactive user interfaces in modified source and object code
|
26
|
-
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
-
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
-
#
|
29
|
-
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
-
# License, a covered work must retain the producer line in every PDF that
|
31
|
-
# is created or manipulated using HexaPDF.
|
32
|
-
#++
|
33
|
-
|
34
|
-
require 'hexapdf/configuration'
|
35
|
-
|
36
|
-
module HexaPDF
|
37
|
-
|
38
|
-
# This class provides utility functions for PDF documents. It is available through the
|
39
|
-
# HexaPDF::Document#utils method.
|
40
|
-
#
|
41
|
-
# Some functions can't be attributed to a single "manager" object. For example, while embedding a
|
42
|
-
# file can be done within a HexaPDF::Type::Filespecification object, loading an image from a file
|
43
|
-
# as a PDF object doesn't have such a place. Such functions are available via this class.
|
44
|
-
class DocumentUtils
|
45
|
-
|
46
|
-
# This module provides methods for managing the images embedded in a PDF file; images
|
47
|
-
# themselves are represented by the HexaPDF::Type::Image class.
|
48
|
-
#
|
49
|
-
# Since an image can be used as a mask for another image, not all image objects found in a PDF
|
50
|
-
# are really used as images. Such cases are all handled by this class automatically.
|
51
|
-
module Images
|
52
|
-
|
53
|
-
# :call-seq:
|
54
|
-
# images.add_image(file) -> image
|
55
|
-
# images.add_image(io) -> image
|
56
|
-
#
|
57
|
-
# Adds the image from the given file or IO to the PDF and returns the image object.
|
58
|
-
#
|
59
|
-
# If the image has been added to the PDF before (i.e. if there is an image object with the
|
60
|
-
# same path name), the already existing image object is returned.
|
61
|
-
def add_image(file_or_io)
|
62
|
-
name = if file_or_io.kind_of?(String)
|
63
|
-
file_or_io
|
64
|
-
elsif file_or_io.respond_to?(:to_path)
|
65
|
-
file_or_io.to_path
|
66
|
-
end
|
67
|
-
if name
|
68
|
-
name = File.absolute_path(name)
|
69
|
-
image = each_image.find {|im| im.source_path == name}
|
70
|
-
end
|
71
|
-
unless image
|
72
|
-
image = image_loader_for(file_or_io).load(@document, file_or_io)
|
73
|
-
image.source_path = name
|
74
|
-
end
|
75
|
-
image
|
76
|
-
end
|
77
|
-
|
78
|
-
# :call-seq:
|
79
|
-
# images.each_image {|image| block } -> images
|
80
|
-
# images.each_image -> Enumerator
|
81
|
-
#
|
82
|
-
# Iterates over all images in the PDF.
|
83
|
-
#
|
84
|
-
# Note that only real images are yielded which means, for example, that images used as soft
|
85
|
-
# mask are not.
|
86
|
-
def each_image(&block)
|
87
|
-
images = @document.each(current: false).select do |obj|
|
88
|
-
obj[:Subtype] == :Image && !obj[:ImageMask]
|
89
|
-
end
|
90
|
-
masks = images.each_with_object([]) do |image, temp|
|
91
|
-
temp << image[:Mask] if image[:Mask].kind_of?(Stream)
|
92
|
-
temp << image[:SMask] if image[:SMask].kind_of?(Stream)
|
93
|
-
end
|
94
|
-
(images - masks).each(&block)
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
# Returns the image loader (see ImageLoader) for the given file or IO stream or raises an
|
100
|
-
# error if no suitable image loader is found.
|
101
|
-
def image_loader_for(file_or_io)
|
102
|
-
GlobalConfiguration['image_loader'].each_index do |index|
|
103
|
-
loader = GlobalConfiguration.constantize('image_loader', index) do
|
104
|
-
raise HexaPDF::Error, "Couldn't retrieve image loader from configuration"
|
105
|
-
end
|
106
|
-
return loader if loader.handles?(file_or_io)
|
107
|
-
end
|
108
|
-
|
109
|
-
raise HexaPDF::Error, "Couldn't find suitable image loader"
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|
113
|
-
|
114
|
-
|
115
|
-
# This module provides methods for managing file specification of a PDF file.
|
116
|
-
#
|
117
|
-
# Note that for a given PDF file not all file specifications may be found, e.g. when a file
|
118
|
-
# specification is only a string. Therefore this module can only handle those file
|
119
|
-
# specifications that are indirect file specification dictionaries with the /Type key set.
|
120
|
-
module Files
|
121
|
-
|
122
|
-
# :call-seq:
|
123
|
-
# files.add_file(filename, name: File.basename(filename), description: nil, embed: true) -> file_spec
|
124
|
-
# files.add_file(io, name:, description: nil) -> file_spec
|
125
|
-
#
|
126
|
-
# Adds the file or IO to the PDF and returns the corresponding file specification object.
|
127
|
-
#
|
128
|
-
# Options:
|
129
|
-
#
|
130
|
-
# name::
|
131
|
-
# The name that should be used for the file path. This name is also for registering the
|
132
|
-
# file in the EmbeddedFiles name tree.
|
133
|
-
#
|
134
|
-
# description::
|
135
|
-
# A description of the file.
|
136
|
-
#
|
137
|
-
# embed::
|
138
|
-
# When an IO object is given, it is always embedded and this option is ignored.
|
139
|
-
#
|
140
|
-
# When a filename is given and this option is +true+, then the file is embedded. Otherwise
|
141
|
-
# only a reference to it is stored.
|
142
|
-
#
|
143
|
-
# See: HexaPDF::Type::FileSpecification
|
144
|
-
def add_file(file_or_io, name: nil, description: nil, embed: true)
|
145
|
-
name ||= File.basename(file_or_io) if file_or_io.kind_of?(String)
|
146
|
-
if name.nil?
|
147
|
-
raise ArgumentError, "The name argument is mandatory when given an IO object"
|
148
|
-
end
|
149
|
-
|
150
|
-
spec = @document.add(Type: :Filespec)
|
151
|
-
spec.path = name
|
152
|
-
spec[:Desc] = description if description
|
153
|
-
spec.embed(file_or_io, name: name, register: true) if embed || !file_or_io.kind_of?(String)
|
154
|
-
spec
|
155
|
-
end
|
156
|
-
|
157
|
-
# :call-seq:
|
158
|
-
# files.each_file(search: false) {|file_spec| block } -> files
|
159
|
-
# files.each_file(search: false) -> Enumerator
|
160
|
-
#
|
161
|
-
# Iterates over indirect file specification dictionaries of the PDF.
|
162
|
-
#
|
163
|
-
# By default, only the file specifications in their standard locations, namely in the
|
164
|
-
# EmbeddedFiles name tree and in the page annotations, are returned. If the +search+ option is
|
165
|
-
# +true+, then all indirect objects are searched for file specification dictionaries which is
|
166
|
-
# much slower.
|
167
|
-
def each_file(search: false)
|
168
|
-
return to_enum(__method__, search: search) unless block_given?
|
169
|
-
|
170
|
-
if search
|
171
|
-
@document.each(current: false) do |obj|
|
172
|
-
yield(obj) if obj.type == :Filespec
|
173
|
-
end
|
174
|
-
else
|
175
|
-
seen = {}
|
176
|
-
tree = @document.catalog[:Names] && @document.catalog[:Names][:EmbeddedFiles]
|
177
|
-
tree.each_entry do |_, spec|
|
178
|
-
seen[spec] = true
|
179
|
-
yield(spec)
|
180
|
-
end if tree
|
181
|
-
|
182
|
-
@document.pages.each_page do |page|
|
183
|
-
next unless page[:Annots]
|
184
|
-
page[:Annots].each do |annot|
|
185
|
-
annot = @document.deref(annot)
|
186
|
-
next unless annot[:Subtype] == :FileAttachment
|
187
|
-
spec = @document.deref(annot[:FS])
|
188
|
-
yield(spec) unless seen.key?(spec)
|
189
|
-
seen[spec] = true
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
self
|
195
|
-
end
|
196
|
-
|
197
|
-
end
|
198
|
-
|
199
|
-
include Images
|
200
|
-
include Files
|
201
|
-
|
202
|
-
# Creates a new DocumentUtils object for the given PDF document.
|
203
|
-
def initialize(document)
|
204
|
-
@document = document
|
205
|
-
end
|
206
|
-
|
207
|
-
end
|
208
|
-
|
209
|
-
end
|