hexapdf 0.12.3 → 0.13.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/CHANGELOG.md +38 -0
- data/lib/hexapdf/cli/command.rb +4 -2
- data/lib/hexapdf/cli/image2pdf.rb +2 -1
- data/lib/hexapdf/cli/info.rb +51 -2
- data/lib/hexapdf/cli/inspect.rb +30 -8
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/configuration.rb +15 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/dictionary.rb +4 -4
- data/lib/hexapdf/dictionary_fields.rb +1 -9
- data/lib/hexapdf/document.rb +31 -12
- data/lib/hexapdf/document/files.rb +0 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -0
- data/lib/hexapdf/font/cmap.rb +1 -4
- data/lib/hexapdf/font/true_type/table/head.rb +1 -0
- data/lib/hexapdf/font/true_type/table/os2.rb +2 -0
- data/lib/hexapdf/image_loader/png.rb +3 -2
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +23 -23
- data/lib/hexapdf/layout/text_shaper.rb +3 -2
- data/lib/hexapdf/object.rb +30 -25
- data/lib/hexapdf/parser.rb +65 -3
- data/lib/hexapdf/pdf_array.rb +9 -2
- data/lib/hexapdf/revisions.rb +29 -21
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +6 -4
- data/lib/hexapdf/type/acro_form/choice_field.rb +4 -4
- data/lib/hexapdf/type/acro_form/field.rb +35 -5
- data/lib/hexapdf/type/acro_form/form.rb +6 -4
- data/lib/hexapdf/type/acro_form/text_field.rb +2 -1
- data/lib/hexapdf/type/actions/uri.rb +3 -2
- data/lib/hexapdf/type/annotations/widget.rb +3 -4
- data/lib/hexapdf/type/catalog.rb +2 -2
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +3 -1
- data/lib/hexapdf/type/font_true_type.rb +6 -2
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/form.rb +2 -1
- data/lib/hexapdf/type/image.rb +2 -2
- data/lib/hexapdf/type/page.rb +16 -7
- data/lib/hexapdf/type/page_tree_node.rb +29 -5
- data/lib/hexapdf/type/resources.rb +1 -0
- data/lib/hexapdf/type/trailer.rb +2 -3
- data/lib/hexapdf/utils/sorted_tree_node.rb +18 -15
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_arc.rb +4 -4
- data/test/hexapdf/content/test_canvas.rb +3 -3
- data/test/hexapdf/content/test_color_space.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +4 -4
- data/test/hexapdf/encryption/test_standard_security_handler.rb +11 -11
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +3 -4
- data/test/hexapdf/test_configuration.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +3 -1
- data/test/hexapdf/test_dictionary_fields.rb +2 -2
- data/test/hexapdf/test_document.rb +4 -4
- data/test/hexapdf/test_object.rb +44 -26
- data/test/hexapdf/test_parser.rb +115 -55
- data/test/hexapdf/test_pdf_array.rb +7 -0
- data/test/hexapdf/test_revisions.rb +35 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
- data/test/hexapdf/type/acro_form/test_field.rb +39 -0
- data/test/hexapdf/type/acro_form/test_form.rb +4 -4
- data/test/hexapdf/type/acro_form/test_text_field.rb +2 -0
- data/test/hexapdf/type/test_font_simple.rb +2 -1
- data/test/hexapdf/type/test_font_true_type.rb +6 -0
- data/test/hexapdf/type/test_form.rb +1 -1
- data/test/hexapdf/type/test_page.rb +8 -1
- data/test/hexapdf/type/test_page_tree_node.rb +42 -0
- data/test/hexapdf/utils/test_bit_field.rb +2 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -9
- metadata +5 -12
@@ -107,6 +107,13 @@ describe HexaPDF::PDFArray do
|
|
107
107
|
assert_equal([1, :data, @array[2]], @array[0, 5])
|
108
108
|
end
|
109
109
|
|
110
|
+
it "allows deleting an object" do
|
111
|
+
obj = @array.value[1]
|
112
|
+
assert_same(obj, @array.delete(obj))
|
113
|
+
ref = HexaPDF::Object.new(:test, oid: 1)
|
114
|
+
assert_equal(ref, @array.delete(ref))
|
115
|
+
end
|
116
|
+
|
110
117
|
describe "slice!" do
|
111
118
|
it "allows deleting a single element" do
|
112
119
|
@array.slice!(2)
|
@@ -158,4 +158,39 @@ describe HexaPDF::Revisions do
|
|
158
158
|
doc = HexaPDF::Document.new(io: io)
|
159
159
|
assert_equal(2, doc.revisions.count)
|
160
160
|
end
|
161
|
+
|
162
|
+
it "uses the reconstructed revision if errors are found when loading from an IO" do
|
163
|
+
io = StringIO.new(<<~EOF)
|
164
|
+
%PDF-1.7
|
165
|
+
1 0 obj
|
166
|
+
10
|
167
|
+
endobj
|
168
|
+
|
169
|
+
xref
|
170
|
+
0 2
|
171
|
+
0000000000 65535 f
|
172
|
+
0000000009 00000 n
|
173
|
+
trailer
|
174
|
+
<< /Size 5 >>
|
175
|
+
startxref
|
176
|
+
28
|
177
|
+
%%EOF
|
178
|
+
|
179
|
+
2 0 obj
|
180
|
+
300
|
181
|
+
endobj
|
182
|
+
|
183
|
+
xref
|
184
|
+
2 1
|
185
|
+
0000000301 00000 n
|
186
|
+
trailer
|
187
|
+
<< /Size 3 /Prev 100>>
|
188
|
+
startxref
|
189
|
+
139
|
190
|
+
%%EOF
|
191
|
+
EOF
|
192
|
+
doc = HexaPDF::Document.new(io: io)
|
193
|
+
assert_equal(2, doc.revisions.count)
|
194
|
+
assert_same(doc.revisions[0].trailer.value, doc.revisions[1].trailer.value)
|
195
|
+
end
|
161
196
|
end
|
data/test/hexapdf/test_writer.rb
CHANGED
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
|
|
40
40
|
219
|
41
41
|
%%EOF
|
42
42
|
3 0 obj
|
43
|
-
<</Producer(HexaPDF version 0.
|
43
|
+
<</Producer(HexaPDF version 0.13.0)>>
|
44
44
|
endobj
|
45
45
|
xref
|
46
46
|
3 1
|
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
|
|
72
72
|
141
|
73
73
|
%%EOF
|
74
74
|
6 0 obj
|
75
|
-
<</Producer(HexaPDF version 0.
|
75
|
+
<</Producer(HexaPDF version 0.13.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -478,7 +478,6 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
478
478
|
[:restore_graphics_state],
|
479
479
|
[:end_marked_content]])
|
480
480
|
end
|
481
|
-
|
482
481
|
end
|
483
482
|
|
484
483
|
describe "choice fields" do
|
@@ -495,7 +494,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
|
|
495
494
|
|
496
495
|
describe "font resolution in case the referenced font is not usable" do
|
497
496
|
before do
|
498
|
-
@doc.config['acro_form.fallback_font'] = ['Times', variant: :none]
|
497
|
+
@doc.config['acro_form.fallback_font'] = ['Times', {variant: :none}]
|
499
498
|
@field[:V] = 'Test'
|
500
499
|
end
|
501
500
|
|
@@ -99,6 +99,12 @@ describe HexaPDF::Type::AcroForm::Field do
|
|
99
99
|
refute(@field.terminal_field?)
|
100
100
|
end
|
101
101
|
|
102
|
+
it "can check whether a widget is embedded in the field" do
|
103
|
+
refute(@field.embedded_widget?)
|
104
|
+
@field[:Subtype] = :Wdiget
|
105
|
+
assert(@field.embedded_widget?)
|
106
|
+
end
|
107
|
+
|
102
108
|
describe "each_widget" do
|
103
109
|
it "yields a wrapped instance of self if a single widget is embedded" do
|
104
110
|
@field[:Subtype] = :Widget
|
@@ -175,6 +181,39 @@ describe HexaPDF::Type::AcroForm::Field do
|
|
175
181
|
end
|
176
182
|
end
|
177
183
|
|
184
|
+
describe "delete_widget" do
|
185
|
+
before do
|
186
|
+
@page = @doc.pages.add
|
187
|
+
end
|
188
|
+
|
189
|
+
it "does nothing if the provided widget doesn't belong to the field" do
|
190
|
+
wrong_widget = @doc.add({Subtype: :Widget})
|
191
|
+
|
192
|
+
@field.create_widget(@page)
|
193
|
+
@field.delete_widget(wrong_widget)
|
194
|
+
assert_equal(:Widget, @field[:Subtype])
|
195
|
+
|
196
|
+
@field.create_widget(@page)
|
197
|
+
@field.delete_widget(wrong_widget)
|
198
|
+
assert_equal(2, @field[:Kids].size)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "deletes the widget if it is embedded" do
|
202
|
+
widget = @field.create_widget(@page)
|
203
|
+
@field.delete_widget(widget)
|
204
|
+
refute(@field.key?(:Subtype))
|
205
|
+
assert(@page[:Annots].empty?)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "deletes the widget if it is not embedded" do
|
209
|
+
@field.create_widget(@page)
|
210
|
+
widget2 = @field.create_widget(@page)
|
211
|
+
@field.delete_widget(widget2)
|
212
|
+
assert_equal(1, @field[:Kids].size)
|
213
|
+
assert_equal(@field[:Kids].value, @page[:Annots].value)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
178
217
|
describe "perform_validation" do
|
179
218
|
before do
|
180
219
|
@field[:FT] = :Tx
|
@@ -7,7 +7,7 @@ require 'hexapdf/type/acro_form/form'
|
|
7
7
|
describe HexaPDF::Type::AcroForm::Form do
|
8
8
|
before do
|
9
9
|
@doc = HexaPDF::Document.new
|
10
|
-
@acro_form = @doc.add({}, type: :XXAcroForm)
|
10
|
+
@acro_form = @doc.add({Fields: []}, type: :XXAcroForm)
|
11
11
|
end
|
12
12
|
|
13
13
|
describe "signature flags" do
|
@@ -49,7 +49,7 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
49
49
|
root_fields = @acro_form.find_root_fields
|
50
50
|
assert_equal(result, root_fields.map(&:value))
|
51
51
|
assert_kind_of(HexaPDF::Type::AcroForm::TextField, root_fields[0])
|
52
|
-
|
52
|
+
assert_equal([], @acro_form[:Fields].value)
|
53
53
|
|
54
54
|
@acro_form.find_root_fields!
|
55
55
|
assert_equal(result, @acro_form[:Fields].value.map(&:value))
|
@@ -204,9 +204,9 @@ describe HexaPDF::Type::AcroForm::Form do
|
|
204
204
|
|
205
205
|
it "checks whether the font used in /DA is available in /DR" do
|
206
206
|
@acro_form[:DA] = '/F2 0 Tf /F1 0 Tf'
|
207
|
-
refute(@acro_form.validate {|msg
|
207
|
+
refute(@acro_form.validate {|msg| assert_match(/DR must also be present/, msg) })
|
208
208
|
@acro_form.default_resources[:Font] = {}
|
209
|
-
refute(@acro_form.validate {|msg
|
209
|
+
refute(@acro_form.validate {|msg| assert_match(/font.*is not.*resource/, msg) })
|
210
210
|
@acro_form.default_resources[:Font][:F1] = :yes
|
211
211
|
assert(@acro_form.validate)
|
212
212
|
end
|
@@ -161,8 +161,9 @@ describe HexaPDF::Type::FontSimple do
|
|
161
161
|
end
|
162
162
|
|
163
163
|
describe "validation" do
|
164
|
+
before { assert(@font.validate) }
|
165
|
+
|
164
166
|
it "validates the existence of required keys" do
|
165
|
-
assert(@font.validate)
|
166
167
|
@font.delete(:FirstChar)
|
167
168
|
refute(@font.validate)
|
168
169
|
end
|
@@ -16,6 +16,12 @@ describe HexaPDF::Type::FontTrueType do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
describe "validation" do
|
19
|
+
it "ignores some missing fields if the font name is one of the standard PDF fonts" do
|
20
|
+
@font[:BaseFont] = :'Arial,Bold'
|
21
|
+
[:FirstChar, :LastChar, :Widths, :FontDescriptor].each {|field| @font.delete(field) }
|
22
|
+
assert(@font.validate)
|
23
|
+
end
|
24
|
+
|
19
25
|
it "requires that the FontDescriptor key is set" do
|
20
26
|
assert(@font.validate)
|
21
27
|
@font.delete(:FontDescriptor)
|
@@ -49,7 +49,7 @@ describe HexaPDF::Type::Form do
|
|
49
49
|
it "creates the resource dictionary if it is not found" do
|
50
50
|
resources = @form.resources
|
51
51
|
assert_equal(:XXResources, resources.type)
|
52
|
-
assert_equal({}, resources.value)
|
52
|
+
assert_equal({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]}, resources.value)
|
53
53
|
end
|
54
54
|
|
55
55
|
it "returns the already used resource dictionary" do
|
@@ -277,7 +277,7 @@ describe HexaPDF::Type::Page do
|
|
277
277
|
page = @doc.add({Type: :Page, Parent: @doc.pages.root})
|
278
278
|
resources = page.resources
|
279
279
|
assert_equal(:XXResources, resources.type)
|
280
|
-
assert_equal({}, resources.value)
|
280
|
+
assert_equal({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]}, resources.value)
|
281
281
|
end
|
282
282
|
|
283
283
|
it "returns the already used resource dictionary" do
|
@@ -319,6 +319,13 @@ describe HexaPDF::Type::Page do
|
|
319
319
|
end
|
320
320
|
end
|
321
321
|
|
322
|
+
it "returns all ancestor page tree nodes of a page" do
|
323
|
+
root = @doc.add({Type: :Pages})
|
324
|
+
kid = @doc.add({Type: :Pages, Parent: root})
|
325
|
+
page = @doc.add({Type: :Page, Parent: kid})
|
326
|
+
assert_equal([kid, root], page.ancestor_nodes)
|
327
|
+
end
|
328
|
+
|
322
329
|
describe "canvas" do
|
323
330
|
before do
|
324
331
|
@page = @doc.pages.add
|
@@ -206,6 +206,48 @@ describe HexaPDF::Type::PageTreeNode do
|
|
206
206
|
end
|
207
207
|
end
|
208
208
|
|
209
|
+
describe "move_page" do
|
210
|
+
before do
|
211
|
+
define_multilevel_page_tree
|
212
|
+
end
|
213
|
+
|
214
|
+
it "moves the page to the first place" do
|
215
|
+
@root.move_page(@pages[1], 0)
|
216
|
+
assert_equal([@pages[1], @pages[0], *@pages[2..-1]], @root.each_page.to_a)
|
217
|
+
assert(@root.validate)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "moves the page to the correct location with a positive index" do
|
221
|
+
@root.move_page(1, 3)
|
222
|
+
assert_equal([@pages[0], @pages[2], @pages[1], *@pages[3..-1]], @root.each_page.to_a)
|
223
|
+
assert(@root.validate)
|
224
|
+
end
|
225
|
+
|
226
|
+
it "moves the page to the last place" do
|
227
|
+
@root.move_page(1, -1)
|
228
|
+
assert_equal([@pages[0], *@pages[2..-1], @pages[1]], @root.each_page.to_a)
|
229
|
+
assert(@root.validate)
|
230
|
+
end
|
231
|
+
|
232
|
+
it "fails if the index to the moving page is invalid" do
|
233
|
+
assert_raises(HexaPDF::Error) { @root.move_page(10, 0) }
|
234
|
+
end
|
235
|
+
|
236
|
+
it "fails if the moving page was deleted/is null" do
|
237
|
+
@doc.delete(@pages[0])
|
238
|
+
assert_raises(HexaPDF::Error) { @root.move_page(@pages[0], 3) }
|
239
|
+
end
|
240
|
+
|
241
|
+
it "fails if the page was not yet added to a page tree" do
|
242
|
+
page = @doc.add({Type: :Page})
|
243
|
+
assert_raises(HexaPDF::Error) { @root.move_page(page, 3) }
|
244
|
+
end
|
245
|
+
|
246
|
+
it "fails if the page is not part of the page tree" do
|
247
|
+
assert_raises(HexaPDF::Error) { @kid1.move_page(@pages[6], 3) }
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
209
251
|
describe "each_page" do
|
210
252
|
before do
|
211
253
|
define_multilevel_page_tree
|
@@ -13,9 +13,9 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
13
13
|
|
14
14
|
def add_multilevel_entries
|
15
15
|
@kid11 = @doc.add({Limits: ['c', 'f'], Names: ['c', 1, 'f', 1]}, type: HexaPDF::NameTreeNode)
|
16
|
-
@kid12 = @doc.add({Limits: ['i', 'm'], Names: ['i', 1, 'm', 1]})
|
16
|
+
@kid12 = @doc.add({Limits: ['i', 'm'], Names: ['i', 1, 'm', 1]}, type: HexaPDF::NameTreeNode)
|
17
17
|
ref = HexaPDF::Reference.new(@kid11.oid, @kid11.gen)
|
18
|
-
@kid1 = @doc.add({Limits: ['c', 'm'], Kids: [ref, @kid12]})
|
18
|
+
@kid1 = @doc.add({Limits: ['c', 'm'], Kids: [ref, @kid12]}, type: HexaPDF::NameTreeNode)
|
19
19
|
@kid21 = @doc.add({Limits: ['o', 'q'], Names: ['o', 1, 'q', 1]}, type: HexaPDF::NameTreeNode)
|
20
20
|
@kid221 = @doc.add({Limits: ['s', 'u'], Names: ['s', 1, 'u', 1]}, type: HexaPDF::NameTreeNode)
|
21
21
|
@kid22 = @doc.add({Limits: ['s', 'u'], Kids: [@kid221]}, type: HexaPDF::NameTreeNode)
|
@@ -73,11 +73,11 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
73
73
|
@root.add_entry('p', 1)
|
74
74
|
@root.add_entry('r', 1)
|
75
75
|
@root.add_entry('v', 1)
|
76
|
-
assert_equal(['a', 'm'], @kid1[:Limits])
|
76
|
+
assert_equal(['a', 'm'], @kid1[:Limits].value)
|
77
77
|
assert_equal(['a', 'f'], @kid11[:Limits].value)
|
78
78
|
assert_equal(['a', 1, 'c', 1, 'e', 1, 'f', 1], @kid11[:Names].value)
|
79
|
-
assert_equal(['g', 'm'], @kid12[:Limits])
|
80
|
-
assert_equal(['g', 1, 'i', 1, 'j', 1, 'm', 1], @kid12[:Names])
|
79
|
+
assert_equal(['g', 'm'], @kid12[:Limits].value)
|
80
|
+
assert_equal(['g', 1, 'i', 1, 'j', 1, 'm', 1], @kid12[:Names].value)
|
81
81
|
assert_equal(['n', 'v'], @kid2[:Limits].value)
|
82
82
|
assert_equal(['n', 'q'], @kid21[:Limits].value)
|
83
83
|
assert_equal(['n', 1, 'o', 1, 'p', 1, 'q', 1], @kid21[:Names].value)
|
@@ -193,15 +193,16 @@ describe HexaPDF::Utils::SortedTreeNode do
|
|
193
193
|
end
|
194
194
|
|
195
195
|
it "checks that all kid objects are indirect objects" do
|
196
|
-
@root[:Kids][0] = HexaPDF::Reference.new(@kid1.oid, @kid1.gen)
|
196
|
+
@root[:Kids][0] = ref = HexaPDF::Reference.new(@kid1.oid, @kid1.gen)
|
197
197
|
assert(@root.validate)
|
198
198
|
|
199
|
-
@root[:Kids][0] =
|
199
|
+
@root[:Kids][0] = ref
|
200
200
|
@kid1.oid = 0
|
201
|
-
|
201
|
+
assert(@root.validate do |message, c|
|
202
202
|
assert_match(/must be indirect objects/, message)
|
203
|
-
|
203
|
+
assert(c)
|
204
204
|
end)
|
205
|
+
assert(@kid1.indirect?)
|
205
206
|
end
|
206
207
|
|
207
208
|
it "checks that leaf node containers have an even number of entries" do
|
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.13.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: 2020-
|
11
|
+
date: 2020-11-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -78,20 +78,14 @@ dependencies:
|
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '0
|
82
|
-
- - ">="
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
version: 0.82.0
|
81
|
+
version: '1.0'
|
85
82
|
type: :development
|
86
83
|
prerelease: false
|
87
84
|
version_requirements: !ruby/object:Gem::Requirement
|
88
85
|
requirements:
|
89
86
|
- - "~>"
|
90
87
|
- !ruby/object:Gem::Version
|
91
|
-
version: '0
|
92
|
-
- - ">="
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
version: 0.82.0
|
88
|
+
version: '1.0'
|
95
89
|
description: |
|
96
90
|
HexaPDF is a pure Ruby library with an accompanying application for working with PDF
|
97
91
|
files.
|
@@ -653,8 +647,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
653
647
|
- !ruby/object:Gem::Version
|
654
648
|
version: '0'
|
655
649
|
requirements: []
|
656
|
-
|
657
|
-
rubygems_version: 2.7.6.2
|
650
|
+
rubygems_version: 3.0.3
|
658
651
|
signing_key:
|
659
652
|
specification_version: 4
|
660
653
|
summary: HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|