hexapdf 0.14.0 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/lib/hexapdf/dictionary.rb +5 -2
- data/lib/hexapdf/font/true_type/subsetter.rb +12 -3
- data/lib/hexapdf/font/true_type/table/post.rb +15 -10
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
- data/lib/hexapdf/font_loader/from_file.rb +18 -8
- data/lib/hexapdf/importer.rb +3 -2
- data/lib/hexapdf/parser.rb +9 -1
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/serializer.rb +33 -7
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_post.rb +1 -1
- data/test/hexapdf/font/true_type/test_subsetter.rb +5 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
- data/test/hexapdf/font_loader/test_from_file.rb +7 -0
- data/test/hexapdf/test_dictionary.rb +5 -0
- data/test/hexapdf/test_parser.rb +12 -0
- data/test/hexapdf/test_writer.rb +2 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4010e277168cec5c8cc5d584ec324064461e63756d18b538cd335235fe04e6d
|
4
|
+
data.tar.gz: 2b7a71463082a32605adee682c81cdde6b0eb48d360ca66249b08884f82e571b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5748273dc4dc532cd365598e25c4a9cc5872011d2eb638c2986050aeed0a68d2dc5769fda075eb60cbcb76fccbfb1a5b52c3c58581cb6e969978c17d770013e6
|
7
|
+
data.tar.gz: 0ab3abf80967804486fa1f50f186b508fd792acfbd8c47646fa7d0c5b0245161e2833620142b2f05a1ee73b01145016dca7bf7781d579284160c9d2dd2c78d0c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.14.1 - 2021-01-21
|
2
|
+
|
3
|
+
### Changed
|
4
|
+
|
5
|
+
* Validation message when checking for allowed values to include the invalid
|
6
|
+
object
|
7
|
+
* [HexaPDF::FontLoader::FromFile] to allow (re)using an existing font object
|
8
|
+
* [HexaPDF::Importer] internals to avoid problems with retained memory
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
* Parsing of invalid PDF files where whitespace is missing after the integer
|
13
|
+
value of an indirect object
|
14
|
+
* [HexaPDF::Dictionary] so that adding new key-value pairs during validation is
|
15
|
+
possible
|
16
|
+
|
17
|
+
|
1
18
|
## 0.14.0 - 2020-12-30
|
2
19
|
|
3
20
|
### Added
|
data/lib/hexapdf/dictionary.rb
CHANGED
@@ -155,6 +155,9 @@ module HexaPDF
|
|
155
155
|
# available (see ::define_field).
|
156
156
|
#
|
157
157
|
# * Returns the default value if one is specified and no value is available.
|
158
|
+
#
|
159
|
+
# Note: This method may throw a "can't add a new key into hash during iteration" error in
|
160
|
+
# certain cases because it potentially modifies the underlying hash!
|
158
161
|
def [](name)
|
159
162
|
field = self.class.field(name)
|
160
163
|
data = if key?(name)
|
@@ -255,7 +258,7 @@ module HexaPDF
|
|
255
258
|
|
256
259
|
# Iterates over all currently set fields and those that are required.
|
257
260
|
def each_set_key_or_required_field #:yields: name, field
|
258
|
-
value.
|
261
|
+
value.keys.each {|name| yield(name, self.class.field(name)) }
|
259
262
|
self.class.each_field do |name, field|
|
260
263
|
yield(name, field) if field.required? && !value.key?(name)
|
261
264
|
end
|
@@ -301,7 +304,7 @@ module HexaPDF
|
|
301
304
|
|
302
305
|
# Check the value of the field against the allowed values.
|
303
306
|
if field.allowed_values && !field.allowed_values.include?(obj)
|
304
|
-
yield("Field #{name} does not contain an allowed value")
|
307
|
+
yield("Field #{name} does not contain an allowed value: #{obj.inspect}")
|
305
308
|
end
|
306
309
|
|
307
310
|
# Check if field value needs to be (in)direct
|
@@ -63,6 +63,12 @@ module HexaPDF
|
|
63
63
|
def use_glyph(glyph_id)
|
64
64
|
return @glyph_map[glyph_id] if @glyph_map.key?(glyph_id)
|
65
65
|
@last_id += 1
|
66
|
+
# Handle codes for ASCII characters \r, (, ) and \ specially so that they never appear in
|
67
|
+
# the output (PDF serialization would need to escape them)
|
68
|
+
if @last_id == 13 || @last_id == 40 || @last_id == 41 || @last_id == 92
|
69
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
70
|
+
@last_id += 1
|
71
|
+
end
|
66
72
|
@glyph_map[glyph_id] = @last_id
|
67
73
|
end
|
68
74
|
|
@@ -107,7 +113,7 @@ module HexaPDF
|
|
107
113
|
locations = []
|
108
114
|
|
109
115
|
@glyph_map.each_key do |old_gid|
|
110
|
-
glyph = orig_glyf[old_gid]
|
116
|
+
glyph = orig_glyf[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
111
117
|
locations << table.size
|
112
118
|
data = glyph.raw_data
|
113
119
|
if glyph.compound?
|
@@ -134,7 +140,7 @@ module HexaPDF
|
|
134
140
|
hmtx = @font[:hmtx]
|
135
141
|
data = ''.b
|
136
142
|
@glyph_map.each_key do |old_gid|
|
137
|
-
metric = hmtx[old_gid]
|
143
|
+
metric = hmtx[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
138
144
|
data << [metric.advance_width, metric.left_side_bearing].pack('n2')
|
139
145
|
end
|
140
146
|
data
|
@@ -166,7 +172,10 @@ module HexaPDF
|
|
166
172
|
# Adds the components of compound glyphs to the subset.
|
167
173
|
def add_glyph_components
|
168
174
|
glyf = @font[:glyf]
|
169
|
-
@glyph_map.keys.each
|
175
|
+
@glyph_map.keys.each do |gid|
|
176
|
+
next if gid.kind_of?(Symbol)
|
177
|
+
glyf[gid].components&.each {|cgid| use_glyph(cgid) }
|
178
|
+
end
|
170
179
|
end
|
171
180
|
|
172
181
|
end
|
@@ -99,18 +99,23 @@ module HexaPDF
|
|
99
99
|
@max_mem_type42, @min_mem_type1, @max_mem_type1 = read_formatted(24, 's>2N5')
|
100
100
|
|
101
101
|
sub_table_length = directory_entry.length - 32
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
102
|
+
cur_pos = io.pos
|
103
|
+
@glyph_names = lambda do |glyph_id|
|
104
|
+
io.pos = cur_pos
|
105
|
+
@glyph_names = case @format
|
106
|
+
when 1 then Format1.parse(io, sub_table_length)
|
107
|
+
when 2 then Format2.parse(io, sub_table_length)
|
108
|
+
when 3 then Format3.parse(io, sub_table_length)
|
109
|
+
when 4 then Format4.parse(io, sub_table_length)
|
110
110
|
else
|
111
|
-
[]
|
111
|
+
if font.config['font.true_type.unknown_format'] == :raise
|
112
|
+
raise HexaPDF::Error, "Unsupported post table format: #{@format}"
|
113
|
+
else
|
114
|
+
[]
|
115
|
+
end
|
112
116
|
end
|
113
|
-
|
117
|
+
@glyph_names[glyph_id]
|
118
|
+
end
|
114
119
|
end
|
115
120
|
|
116
121
|
# 'post' table format 1
|
@@ -63,8 +63,8 @@ module HexaPDF
|
|
63
63
|
file = document.config['font.map'].dig(name, variant)
|
64
64
|
return nil if file.nil?
|
65
65
|
|
66
|
-
unless File.file?(file)
|
67
|
-
raise HexaPDF::Error, "The configured font file #{file}
|
66
|
+
unless file.kind_of?(HexaPDF::Font::TrueType::Font) || File.file?(file)
|
67
|
+
raise HexaPDF::Error, "The configured font file #{file} is not a valid value"
|
68
68
|
end
|
69
69
|
FromFile.call(document, file, subset: subset)
|
70
70
|
end
|
@@ -39,26 +39,36 @@ require 'hexapdf/font/true_type_wrapper'
|
|
39
39
|
module HexaPDF
|
40
40
|
module FontLoader
|
41
41
|
|
42
|
-
# This module interprets the font name as file name and tries to load it
|
42
|
+
# This module interprets the font name either as file name and tries to load it, or as font
|
43
|
+
# object to be wrapped directly.
|
43
44
|
module FromFile
|
44
45
|
|
45
|
-
#
|
46
|
+
# :call-seq:
|
47
|
+
# FromFile.call(document, file_name, subset: true, **) -> wrapped_font
|
48
|
+
# FromFile.call(document, font_object, subset: true, **) -> wrapped_font
|
46
49
|
#
|
47
|
-
#
|
48
|
-
#
|
50
|
+
# Returns an appropriate font wrapper for the given file name or font object.
|
51
|
+
#
|
52
|
+
# If a file name is given, the file object representing the font file is *not* closed and if
|
53
|
+
# needed must be closed by the caller once the font is not needed anymore.
|
54
|
+
#
|
55
|
+
# The first form using a file name is easier to use in one-off cases. However, if multiple
|
56
|
+
# documents always refer to the same font, the second form is better to avoid re-parsing the
|
57
|
+
# font file.
|
49
58
|
#
|
50
59
|
# +document+::
|
51
60
|
# The PDF document to associate the font object with.
|
52
61
|
#
|
53
|
-
# +
|
54
|
-
# The file name.
|
62
|
+
# +file_name+/+font_object+::
|
63
|
+
# The file name or TrueType font object.
|
55
64
|
#
|
56
65
|
# +subset+::
|
57
66
|
# Specifies whether the font should be subset if possible.
|
58
67
|
def self.call(document, name, subset: true, **)
|
59
|
-
|
68
|
+
is_font = name.kind_of?(HexaPDF::Font::TrueType::Font)
|
69
|
+
return nil unless is_font || File.file?(name)
|
60
70
|
|
61
|
-
font = HexaPDF::Font::TrueType::Font.new(File.open(name, 'rb'))
|
71
|
+
font = is_font ? name : HexaPDF::Font::TrueType::Font.new(File.open(name, 'rb'))
|
62
72
|
HexaPDF::Font::TrueTypeWrapper.new(document, font, subset: subset)
|
63
73
|
end
|
64
74
|
|
data/lib/hexapdf/importer.rb
CHANGED
@@ -90,7 +90,7 @@ module HexaPDF
|
|
90
90
|
#
|
91
91
|
# An error is raised if the object doesn't belong to the +source+ document.
|
92
92
|
def import(object)
|
93
|
-
mapped_object = @mapper[object.data] if object.kind_of?(HexaPDF::Object)
|
93
|
+
mapped_object = @mapper[object.data]&.__getobj__ if object.kind_of?(HexaPDF::Object)
|
94
94
|
if object.kind_of?(HexaPDF::Object) && object.document? && @source != object.document
|
95
95
|
raise HexaPDF::Error, "Import error: Incorrect document object for importer"
|
96
96
|
elsif mapped_object && mapped_object == @destination.object(mapped_object)
|
@@ -118,7 +118,8 @@ module HexaPDF
|
|
118
118
|
if object.type == :Catalog || object.type == :Pages
|
119
119
|
@mapper[object.data] = nil
|
120
120
|
else
|
121
|
-
obj =
|
121
|
+
obj = object.dup
|
122
|
+
@mapper[object.data] = NullableWeakRef.new(obj)
|
122
123
|
obj.document = @destination.__getobj__
|
123
124
|
obj.instance_variable_set(:@data, obj.data.dup)
|
124
125
|
obj.data.oid = 0
|
data/lib/hexapdf/parser.rb
CHANGED
@@ -113,7 +113,15 @@ module HexaPDF
|
|
113
113
|
maybe_raise("No indirect object value between 'obj' and 'endobj'", pos: @tokenizer.pos)
|
114
114
|
object = nil
|
115
115
|
else
|
116
|
-
|
116
|
+
begin
|
117
|
+
object = @tokenizer.next_object
|
118
|
+
rescue MalformedPDFError
|
119
|
+
# Handle often found invalid indirect object with missing whitespace after number
|
120
|
+
maybe_raise("Invalid object value after 'obj'", pos: @tokenizer.pos,
|
121
|
+
force: !(tok.kind_of?(Tokenizer::Token) && tok =~ /\A\d+endobj\z/))
|
122
|
+
object = tok.to_i
|
123
|
+
@tokenizer.pos -= 6
|
124
|
+
end
|
117
125
|
end
|
118
126
|
|
119
127
|
tok = @tokenizer.next_token
|
data/lib/hexapdf/pdf_array.rb
CHANGED
@@ -66,7 +66,7 @@ module HexaPDF
|
|
66
66
|
# subclasses of HexaPDF::Object are returned as is (it makes no sense, for example, to return
|
67
67
|
# the hash that describes the Catalog instead of the Catalog object).
|
68
68
|
def [](arg1, arg2 = nil)
|
69
|
-
data = value[arg1,
|
69
|
+
data = arg2 ? value[arg1, arg2] : value[arg1]
|
70
70
|
return if data.nil?
|
71
71
|
|
72
72
|
if arg2 || arg1.kind_of?(Range)
|
data/lib/hexapdf/serializer.rb
CHANGED
@@ -88,13 +88,39 @@ module HexaPDF
|
|
88
88
|
|
89
89
|
# Creates a new Serializer object.
|
90
90
|
def initialize
|
91
|
-
@dispatcher =
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
91
|
+
@dispatcher = {
|
92
|
+
Hash => 'serialize_hash',
|
93
|
+
Array => 'serialize_array',
|
94
|
+
Symbol => 'serialize_symbol',
|
95
|
+
String => 'serialize_string',
|
96
|
+
Integer => 'serialize_integer',
|
97
|
+
Float => 'serialize_float',
|
98
|
+
Time => 'serialize_time',
|
99
|
+
TrueClass => 'serialize_trueclass',
|
100
|
+
FalseClass => 'serialize_falseclass',
|
101
|
+
NilClass => 'serialize_nilclass',
|
102
|
+
HexaPDF::Reference => 'serialize_hexapdf_reference',
|
103
|
+
HexaPDF::Object => 'serialize_hexapdf_object',
|
104
|
+
HexaPDF::Stream => 'serialize_hexapdf_stream',
|
105
|
+
HexaPDF::Dictionary => 'serialize_hexapdf_object',
|
106
|
+
HexaPDF::PDFArray => 'serialize_hexapdf_object',
|
107
|
+
HexaPDF::Rectangle => 'serialize_hexapdf_object',
|
108
|
+
}
|
109
|
+
@dispatcher.default_proc = lambda do |h, klass|
|
110
|
+
h[klass] = if klass <= HexaPDF::Stream
|
111
|
+
"serialize_hexapdf_stream"
|
112
|
+
elsif klass <= HexaPDF::Object
|
113
|
+
"serialize_hexapdf_object"
|
114
|
+
else
|
115
|
+
method = nil
|
116
|
+
klass.ancestors.each do |ancestor_klass|
|
117
|
+
name = ancestor_klass.name.to_s.downcase
|
118
|
+
name.gsub!(/::/, '_')
|
119
|
+
method = "serialize_#{name}"
|
120
|
+
break if respond_to?(method, true)
|
121
|
+
end
|
122
|
+
method
|
123
|
+
end
|
98
124
|
end
|
99
125
|
@encrypter = false
|
100
126
|
@io = nil
|
data/lib/hexapdf/version.rb
CHANGED
@@ -72,7 +72,7 @@ describe HexaPDF::Font::TrueType::Table::Post do
|
|
72
72
|
assert_equal('.notdef', table[0])
|
73
73
|
|
74
74
|
@font.config['font.true_type.unknown_format'] = :raise
|
75
|
-
assert_raises(HexaPDF::Error) { create_table(:Post) }
|
75
|
+
assert_raises(HexaPDF::Error) { create_table(:Post)[0] }
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
@@ -27,6 +27,11 @@ describe HexaPDF::Font::TrueType::Subsetter do
|
|
27
27
|
assert_equal(value, @subsetter.subset_glyph_id(5))
|
28
28
|
end
|
29
29
|
|
30
|
+
it "doesn't use certain subset glyph IDs for performance reasons" do
|
31
|
+
1.upto(13) {|i| @subsetter.use_glyph(i) }
|
32
|
+
assert_equal(14, @subsetter.subset_glyph_id(13))
|
33
|
+
end
|
34
|
+
|
30
35
|
it "creates the subset font file" do
|
31
36
|
gid = @font[:cmap].preferred_table[0x41]
|
32
37
|
@subsetter.use_glyph(gid)
|
@@ -8,13 +8,17 @@ describe HexaPDF::FontLoader::FromConfiguration do
|
|
8
8
|
before do
|
9
9
|
@doc = HexaPDF::Document.new
|
10
10
|
font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
|
11
|
-
@
|
11
|
+
@font_obj = HexaPDF::Font::TrueType::Font.new(File.open(font_file, 'rb'))
|
12
|
+
@doc.config['font.map'] = {'font' => {none: font_file}, 'font1' => {none: @font_obj}}
|
12
13
|
@klass = HexaPDF::FontLoader::FromConfiguration
|
13
14
|
end
|
14
15
|
|
15
16
|
it "loads the configured font" do
|
16
17
|
wrapper = @klass.call(@doc, "font")
|
17
18
|
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
19
|
+
wrapper = @klass.call(@doc, "font1")
|
20
|
+
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
21
|
+
assert_same(@font_obj, wrapper.wrapped_font)
|
18
22
|
end
|
19
23
|
|
20
24
|
it "passes the subset value to the wrapper" do
|
@@ -24,7 +28,7 @@ describe HexaPDF::FontLoader::FromConfiguration do
|
|
24
28
|
refute(wrapper.subset?)
|
25
29
|
end
|
26
30
|
|
27
|
-
it "fails if the font
|
31
|
+
it "fails if the provided font is invalid" do
|
28
32
|
@doc.config['font.map']['font'][:none] << "unknown"
|
29
33
|
assert_raises(HexaPDF::Error) { @klass.call(@doc, "font") }
|
30
34
|
end
|
@@ -34,6 +38,6 @@ describe HexaPDF::FontLoader::FromConfiguration do
|
|
34
38
|
end
|
35
39
|
|
36
40
|
it "returns a hash with all configured fonts" do
|
37
|
-
assert_equal({'font' => [:none]}, @klass.available_fonts(@doc))
|
41
|
+
assert_equal({'font' => [:none], 'font1' => [:none]}, @klass.available_fonts(@doc))
|
38
42
|
end
|
39
43
|
end
|
@@ -16,6 +16,13 @@ describe HexaPDF::FontLoader::FromFile do
|
|
16
16
|
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
17
17
|
end
|
18
18
|
|
19
|
+
it "loads the specified font object" do
|
20
|
+
font = HexaPDF::Font::TrueType::Font.new(File.open(@font_file, 'rb'))
|
21
|
+
wrapper = @klass.call(@doc, font)
|
22
|
+
assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
|
23
|
+
assert_same(font, wrapper.wrapped_font)
|
24
|
+
end
|
25
|
+
|
19
26
|
it "passes the subset value to the wrapper" do
|
20
27
|
wrapper = @klass.call(@doc, @font_file)
|
21
28
|
assert(wrapper.subset?)
|
@@ -283,6 +283,11 @@ describe HexaPDF::Dictionary do
|
|
283
283
|
@obj[:TestClass][:Nested][:Nested][:TestClass][:Inherited] = :symbol
|
284
284
|
assert(@obj.validate)
|
285
285
|
end
|
286
|
+
|
287
|
+
it "makes sure validation works in special case where the dictionary is modified" do
|
288
|
+
@dict[:Array] = 5
|
289
|
+
refute(@dict.validate {|_, _, object| object[:Boolean] })
|
290
|
+
end
|
286
291
|
end
|
287
292
|
|
288
293
|
describe "delete" do
|
data/test/hexapdf/test_parser.rb
CHANGED
@@ -88,6 +88,12 @@ describe HexaPDF::Parser do
|
|
88
88
|
assert_equal('12', TestHelper.collector(stream.fiber))
|
89
89
|
end
|
90
90
|
|
91
|
+
it "handles invalid indirect object value consisting of number followed by endobj without space" do
|
92
|
+
create_parser("1 0 obj 749endobj")
|
93
|
+
object, * = @parser.parse_indirect_object
|
94
|
+
assert_equal(749, object)
|
95
|
+
end
|
96
|
+
|
91
97
|
it "recovers from an invalid stream length value" do
|
92
98
|
create_parser("1 0 obj<</Length 4>> stream\n12endstream endobj")
|
93
99
|
obj, _, _, stream = @parser.parse_indirect_object
|
@@ -151,6 +157,12 @@ describe HexaPDF::Parser do
|
|
151
157
|
assert_match(/not CR alone/, exp.message)
|
152
158
|
end
|
153
159
|
|
160
|
+
it "fails for numbers followed by endobj without space" do
|
161
|
+
create_parser("1 0 obj 749endobj")
|
162
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
163
|
+
assert_match(/Invalid object value after 'obj'/, exp.message)
|
164
|
+
end
|
165
|
+
|
154
166
|
it "fails if the stream length value is invalid" do
|
155
167
|
create_parser("1 0 obj<</Length 4>> stream\n12endstream endobj")
|
156
168
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
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.14.
|
43
|
+
<</Producer(HexaPDF version 0.14.1)>>
|
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.14.
|
75
|
+
<</Producer(HexaPDF version 0.14.1)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
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.14.
|
4
|
+
version: 0.14.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -631,7 +631,7 @@ files:
|
|
631
631
|
homepage: https://hexapdf.gettalong.org
|
632
632
|
licenses:
|
633
633
|
- AGPL-3.0
|
634
|
-
-
|
634
|
+
- Nonstandard
|
635
635
|
metadata: {}
|
636
636
|
post_install_message:
|
637
637
|
rdoc_options: []
|