hexapdf 0.5.0 → 0.6.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 +76 -2
- data/CONTRIBUTERS +1 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/boxes.rb +68 -0
- data/examples/graphics.rb +12 -12
- data/examples/{text_box_alignment.rb → text_layouter_alignment.rb} +14 -14
- data/examples/text_layouter_inline_boxes.rb +66 -0
- data/examples/{text_box_line_wrapping.rb → text_layouter_line_wrapping.rb} +9 -10
- data/examples/{text_box_shapes.rb → text_layouter_shapes.rb} +58 -54
- data/examples/text_layouter_styling.rb +125 -0
- data/examples/truetype.rb +5 -7
- data/lib/hexapdf/cli/command.rb +1 -0
- data/lib/hexapdf/configuration.rb +170 -106
- data/lib/hexapdf/content/canvas.rb +41 -36
- data/lib/hexapdf/content/graphics_state.rb +15 -0
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/dictionary.rb +20 -8
- data/lib/hexapdf/dictionary_fields.rb +8 -6
- data/lib/hexapdf/document.rb +25 -26
- data/lib/hexapdf/document/fonts.rb +4 -4
- data/lib/hexapdf/document/images.rb +2 -2
- data/lib/hexapdf/document/pages.rb +16 -16
- data/lib/hexapdf/encryption/security_handler.rb +41 -9
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +7 -1
- data/lib/hexapdf/font/true_type/font.rb +20 -0
- data/lib/hexapdf/font/type1/font.rb +23 -0
- data/lib/hexapdf/font_loader.rb +1 -0
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -3
- data/lib/hexapdf/font_loader/from_file.rb +65 -0
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/layout.rb +3 -2
- data/lib/hexapdf/layout/box.rb +146 -0
- data/lib/hexapdf/layout/inline_box.rb +40 -31
- data/lib/hexapdf/layout/{line_fragment.rb → line.rb} +12 -13
- data/lib/hexapdf/layout/style.rb +630 -41
- data/lib/hexapdf/layout/text_fragment.rb +80 -12
- data/lib/hexapdf/layout/{text_box.rb → text_layouter.rb} +164 -109
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/parser.rb +4 -1
- data/lib/hexapdf/revisions.rb +11 -4
- data/lib/hexapdf/stream.rb +8 -9
- data/lib/hexapdf/tokenizer.rb +5 -3
- data/lib/hexapdf/type.rb +3 -0
- data/lib/hexapdf/type/action.rb +56 -0
- data/lib/hexapdf/type/actions.rb +52 -0
- data/lib/hexapdf/type/actions/go_to.rb +52 -0
- data/lib/hexapdf/type/actions/go_to_r.rb +54 -0
- data/lib/hexapdf/type/actions/launch.rb +73 -0
- data/lib/hexapdf/type/actions/uri.rb +65 -0
- data/lib/hexapdf/type/annotation.rb +85 -0
- data/lib/hexapdf/type/annotations.rb +51 -0
- data/lib/hexapdf/type/annotations/link.rb +70 -0
- data/lib/hexapdf/type/annotations/markup_annotation.rb +70 -0
- data/lib/hexapdf/type/annotations/text.rb +81 -0
- data/lib/hexapdf/type/catalog.rb +3 -1
- data/lib/hexapdf/type/embedded_file.rb +6 -11
- data/lib/hexapdf/type/file_specification.rb +4 -6
- data/lib/hexapdf/type/font.rb +3 -1
- data/lib/hexapdf/type/font_descriptor.rb +18 -16
- data/lib/hexapdf/type/form.rb +3 -1
- data/lib/hexapdf/type/graphics_state_parameter.rb +3 -1
- data/lib/hexapdf/type/image.rb +4 -2
- data/lib/hexapdf/type/info.rb +2 -5
- data/lib/hexapdf/type/names.rb +2 -5
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/page.rb +14 -1
- data/lib/hexapdf/type/page_tree_node.rb +9 -6
- data/lib/hexapdf/type/resources.rb +2 -5
- data/lib/hexapdf/type/trailer.rb +2 -5
- data/lib/hexapdf/type/viewer_preferences.rb +2 -5
- data/lib/hexapdf/type/xref_stream.rb +3 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +3 -1
- data/test/hexapdf/content/test_canvas.rb +29 -3
- data/test/hexapdf/content/test_graphics_state.rb +11 -0
- data/test/hexapdf/content/test_operator.rb +3 -2
- data/test/hexapdf/document/test_fonts.rb +8 -8
- data/test/hexapdf/document/test_images.rb +4 -12
- data/test/hexapdf/document/test_pages.rb +7 -7
- data/test/hexapdf/encryption/test_security_handler.rb +1 -5
- data/test/hexapdf/filter/test_predictor.rb +40 -12
- data/test/hexapdf/font/true_type/test_font.rb +16 -0
- data/test/hexapdf/font/type1/test_font.rb +30 -0
- data/test/hexapdf/font_loader/test_from_file.rb +29 -0
- data/test/hexapdf/font_loader/test_standard14.rb +4 -3
- data/test/hexapdf/layout/test_box.rb +104 -0
- data/test/hexapdf/layout/test_inline_box.rb +24 -10
- data/test/hexapdf/layout/{test_line_fragment.rb → test_line.rb} +9 -9
- data/test/hexapdf/layout/test_style.rb +519 -31
- data/test/hexapdf/layout/test_text_fragment.rb +136 -15
- data/test/hexapdf/layout/{test_text_box.rb → test_text_layouter.rb} +224 -144
- data/test/hexapdf/layout/test_text_shaper.rb +1 -1
- data/test/hexapdf/test_configuration.rb +12 -6
- data/test/hexapdf/test_dictionary.rb +27 -2
- data/test/hexapdf/test_dictionary_fields.rb +10 -1
- data/test/hexapdf/test_document.rb +14 -13
- data/test/hexapdf/test_parser.rb +12 -0
- data/test/hexapdf/test_revisions.rb +34 -0
- data/test/hexapdf/test_stream.rb +1 -1
- data/test/hexapdf/test_type.rb +18 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/actions/test_launch.rb +24 -0
- data/test/hexapdf/type/actions/test_uri.rb +23 -0
- data/test/hexapdf/type/annotations/test_link.rb +19 -0
- data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
- data/test/hexapdf/type/annotations/test_text.rb +38 -0
- data/test/hexapdf/type/test_annotation.rb +38 -0
- data/test/hexapdf/type/test_file_specification.rb +0 -7
- data/test/hexapdf/type/test_info.rb +0 -5
- data/test/hexapdf/type/test_page.rb +14 -0
- data/test/hexapdf/type/test_page_tree_node.rb +4 -1
- data/test/hexapdf/type/test_trailer.rb +0 -4
- data/test/test_helper.rb +6 -3
- metadata +36 -15
- data/examples/text_box_inline_boxes.rb +0 -56
- data/examples/text_box_styling.rb +0 -72
- data/test/hexapdf/type/test_embedded_file.rb +0 -16
- data/test/hexapdf/type/test_names.rb +0 -9
|
@@ -30,13 +30,19 @@ describe HexaPDF::Configuration do
|
|
|
30
30
|
|
|
31
31
|
it "can create a new config object by merging another one or a hash" do
|
|
32
32
|
@config['hash'] = {'test' => :test, 'other' => :other}
|
|
33
|
+
@config['array'] = [5, 6]
|
|
33
34
|
config = @config.merge('test' => :other)
|
|
34
35
|
assert_equal(:other, config['test'])
|
|
35
36
|
|
|
36
37
|
config['hash']['test'] = :other
|
|
37
|
-
|
|
38
|
-
assert_equal(:other,
|
|
39
|
-
assert_equal(:other,
|
|
38
|
+
config1 = @config.merge(config)
|
|
39
|
+
assert_equal(:other, config1['hash']['test'])
|
|
40
|
+
assert_equal(:other, config1['hash']['other'])
|
|
41
|
+
|
|
42
|
+
config2 = @config.merge(config)
|
|
43
|
+
config2['array'].unshift(4)
|
|
44
|
+
assert_equal([4, 5, 6], config2['array'])
|
|
45
|
+
assert_equal([5, 6], config['array'])
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
describe "constantize" do
|
|
@@ -51,9 +57,9 @@ describe HexaPDF::Configuration do
|
|
|
51
57
|
end
|
|
52
58
|
|
|
53
59
|
it "returns a constant for a nested option" do
|
|
54
|
-
@config['test'] = {'test' => 'HexaPDF', 'const' => HexaPDF}
|
|
55
|
-
assert_equal(HexaPDF, @config.constantize('test', 'test'))
|
|
56
|
-
assert_equal(HexaPDF, @config.constantize('test', 'const'))
|
|
60
|
+
@config['test'] = {'test' => ['HexaPDF'], 'const' => {'const' => HexaPDF}}
|
|
61
|
+
assert_equal(HexaPDF, @config.constantize('test', 'test', 0))
|
|
62
|
+
assert_equal(HexaPDF, @config.constantize('test', 'const', 'const'))
|
|
57
63
|
|
|
58
64
|
@config['test'] = ['HexaPDF', HexaPDF]
|
|
59
65
|
assert_equal(HexaPDF, @config.constantize('test', 0))
|
|
@@ -69,6 +69,16 @@ describe HexaPDF::Dictionary do
|
|
|
69
69
|
refute(HexaPDF::Dictionary.field(:Test))
|
|
70
70
|
assert_equal([], HexaPDF::Dictionary.each_field.to_a)
|
|
71
71
|
end
|
|
72
|
+
|
|
73
|
+
it "allows defining a static type" do
|
|
74
|
+
@test_class.define_type(:MyClass)
|
|
75
|
+
assert_equal(:MyClass, @test_class.type)
|
|
76
|
+
assert_equal(:MyClass, @test_class.new({}).type)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "::type returns nil if no static type has been defined" do
|
|
80
|
+
assert_nil(@test_class.type)
|
|
81
|
+
end
|
|
72
82
|
end
|
|
73
83
|
|
|
74
84
|
describe "after_data_change" do
|
|
@@ -166,6 +176,21 @@ describe HexaPDF::Dictionary do
|
|
|
166
176
|
end
|
|
167
177
|
end
|
|
168
178
|
|
|
179
|
+
describe "key?" do
|
|
180
|
+
it "returns false for unset keys" do
|
|
181
|
+
refute(@dict.key?(:UnsetKey))
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "returns false for keys with a nil value" do
|
|
185
|
+
@dict[:NilKey] = nil
|
|
186
|
+
refute(@dict.key?(:NilKey))
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "returns true for keys with a non-nil value" do
|
|
190
|
+
assert(@dict.key?(:Other))
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
169
194
|
describe "validate_fields" do
|
|
170
195
|
before do
|
|
171
196
|
@test_class.define_field(:Inherited, type: [Array, Symbol], required: true, indirect: false)
|
|
@@ -260,9 +285,9 @@ describe HexaPDF::Dictionary do
|
|
|
260
285
|
end
|
|
261
286
|
end
|
|
262
287
|
|
|
263
|
-
describe "
|
|
288
|
+
describe "to_h" do
|
|
264
289
|
it "returns a shallow copy of the value" do
|
|
265
|
-
obj = @dict.
|
|
290
|
+
obj = @dict.to_h
|
|
266
291
|
refute_equal(obj.object_id, @dict.value.object_id)
|
|
267
292
|
assert_equal(obj, @dict.value)
|
|
268
293
|
end
|
|
@@ -154,13 +154,22 @@ describe HexaPDF::DictionaryFields do
|
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
it "allows conversion from a string" do
|
|
157
|
-
|
|
157
|
+
assert(@field.convert?("test"))
|
|
158
158
|
|
|
159
159
|
@doc = Minitest::Mock.new
|
|
160
160
|
@doc.expect(:wrap, :data, [{F: 'test'}, {type: HexaPDF::Type::FileSpecification}])
|
|
161
161
|
@field.convert('test', @doc)
|
|
162
162
|
@doc.verify
|
|
163
163
|
end
|
|
164
|
+
|
|
165
|
+
it "allows conversion from a hash/dictionary" do
|
|
166
|
+
assert(@field.convert?({}))
|
|
167
|
+
|
|
168
|
+
@doc = Minitest::Mock.new
|
|
169
|
+
@doc.expect(:wrap, :data, [{F: 'test'}, {type: HexaPDF::Type::FileSpecification}])
|
|
170
|
+
@field.convert({F: 'test'}, @doc)
|
|
171
|
+
@doc.verify
|
|
172
|
+
end
|
|
164
173
|
end
|
|
165
174
|
|
|
166
175
|
describe "RectangleConverter" do
|
|
@@ -264,15 +264,18 @@ EOF
|
|
|
264
264
|
|
|
265
265
|
describe "wrap" do
|
|
266
266
|
before do
|
|
267
|
-
@myclass = Class.new(HexaPDF::
|
|
268
|
-
@
|
|
267
|
+
@myclass = Class.new(HexaPDF::Dictionary)
|
|
268
|
+
@myclass.define_type(:MyClass)
|
|
269
|
+
@myclass2 = Class.new(HexaPDF::Dictionary)
|
|
269
270
|
HexaPDF::GlobalConfiguration['object.type_map'][:MyClass] = @myclass
|
|
270
|
-
HexaPDF::GlobalConfiguration['object.subtype_map'][:
|
|
271
|
+
HexaPDF::GlobalConfiguration['object.subtype_map'][nil][:Global] = @myclass2
|
|
272
|
+
HexaPDF::GlobalConfiguration['object.subtype_map'][:MyClass] = {TheSecond: @myclass2}
|
|
271
273
|
end
|
|
272
274
|
|
|
273
275
|
after do
|
|
274
276
|
HexaPDF::GlobalConfiguration['object.type_map'].delete(:MyClass)
|
|
275
|
-
HexaPDF::GlobalConfiguration['object.subtype_map'].delete(:
|
|
277
|
+
HexaPDF::GlobalConfiguration['object.subtype_map'][nil].delete(:Global)
|
|
278
|
+
HexaPDF::GlobalConfiguration['object.subtype_map'][:MyClass].delete(:TheSecond)
|
|
276
279
|
end
|
|
277
280
|
|
|
278
281
|
it "uses a suitable default type if no special type is specified" do
|
|
@@ -324,16 +327,18 @@ EOF
|
|
|
324
327
|
|
|
325
328
|
it "uses the type/subtype information in the hash that should be wrapped" do
|
|
326
329
|
assert_kind_of(@myclass, @doc.wrap(Type: :MyClass))
|
|
327
|
-
|
|
328
|
-
assert_kind_of(@myclass2, @doc.wrap(
|
|
330
|
+
refute_kind_of(@myclass2, @doc.wrap(Subtype: :TheSecond))
|
|
331
|
+
assert_kind_of(@myclass2, @doc.wrap(Subtype: :Global))
|
|
332
|
+
assert_kind_of(@myclass2, @doc.wrap(Type: :MyClass, S: :TheSecond))
|
|
329
333
|
assert_kind_of(@myclass, @doc.wrap(Type: :MyClass, Subtype: :TheThird))
|
|
330
334
|
end
|
|
331
335
|
|
|
332
336
|
it "respects the given type/subtype arguments" do
|
|
333
337
|
assert_kind_of(@myclass, @doc.wrap({Type: :Other}, type: :MyClass))
|
|
334
|
-
assert_kind_of(@myclass2, @doc.wrap({Subtype: :Other}, subtype: :
|
|
338
|
+
assert_kind_of(@myclass2, @doc.wrap({Subtype: :Other}, subtype: :Global))
|
|
335
339
|
assert_kind_of(@myclass2, @doc.wrap({Type: :Other, Subtype: :Other},
|
|
336
340
|
type: :MyClass, subtype: :TheSecond))
|
|
341
|
+
assert_kind_of(@myclass2, @doc.wrap({Subtype: :TheSecond}, type: @myclass))
|
|
337
342
|
end
|
|
338
343
|
|
|
339
344
|
it "directly uses a class given via the type argument" do
|
|
@@ -529,12 +534,8 @@ EOF
|
|
|
529
534
|
end
|
|
530
535
|
|
|
531
536
|
describe "task" do
|
|
532
|
-
after do
|
|
533
|
-
HexaPDF::GlobalConfiguration['task.map'].delete(:test)
|
|
534
|
-
end
|
|
535
|
-
|
|
536
537
|
it "executes the given task with options" do
|
|
537
|
-
|
|
538
|
+
@doc.config['task.map'][:test] = lambda do |doc, arg1:|
|
|
538
539
|
assert_equal(doc, @doc)
|
|
539
540
|
assert_equal(:arg1, arg1)
|
|
540
541
|
end
|
|
@@ -542,7 +543,7 @@ EOF
|
|
|
542
543
|
end
|
|
543
544
|
|
|
544
545
|
it "executes the given task with a block" do
|
|
545
|
-
|
|
546
|
+
@doc.config['task.map'][:test] = lambda do |doc, **, &block|
|
|
546
547
|
assert_equal(doc, @doc)
|
|
547
548
|
block.call('inside')
|
|
548
549
|
end
|
data/test/hexapdf/test_parser.rb
CHANGED
|
@@ -284,6 +284,12 @@ EOF
|
|
|
284
284
|
assert_equal(HexaPDF::XRefSection.free_entry(1, 65536), section[1])
|
|
285
285
|
end
|
|
286
286
|
|
|
287
|
+
it "handles xref with missing whitespace at end" do
|
|
288
|
+
create_parser("xref\n0 2\n0000000000 00000 n\n0000000000 65536 n\ntrailer\n<<>>\n")
|
|
289
|
+
section, _trailer = @parser.parse_xref_section_and_trailer(0)
|
|
290
|
+
assert_equal(HexaPDF::XRefSection.free_entry(1, 65536), section[1])
|
|
291
|
+
end
|
|
292
|
+
|
|
287
293
|
it "fails if the xref keyword is missing/mangled" do
|
|
288
294
|
create_parser("xTEf\n0 d\n0000000000 00000 n \ntrailer\n<< >>\n")
|
|
289
295
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
@@ -351,6 +357,12 @@ EOF
|
|
|
351
357
|
assert_match(/invalid.*cross-reference entry/i, exp.message)
|
|
352
358
|
end
|
|
353
359
|
|
|
360
|
+
it "parse_xref_section_and_trailer fails if trailing second whitespace is missing" do
|
|
361
|
+
create_parser("xref\n0 1\n0000000000 00000 n\ntrailer\n<<>>\n")
|
|
362
|
+
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(0) }
|
|
363
|
+
assert_match(/invalid.*cross-reference subsection entry/i, exp.message)
|
|
364
|
+
end
|
|
365
|
+
|
|
354
366
|
it "parse_indirect_object fails if an empty indirect object is found" do
|
|
355
367
|
create_parser("1 0 obj\nendobj")
|
|
356
368
|
exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_indirect_object }
|
|
@@ -123,4 +123,38 @@ EOF
|
|
|
123
123
|
assert_equal(400, @revisions[2].object(2).value)
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
|
+
|
|
127
|
+
it "handles invalid PDFs that have a loop via the xref /Prev or /XRefStm entries" do
|
|
128
|
+
io = StringIO.new(<<EOF)
|
|
129
|
+
%PDF-1.7
|
|
130
|
+
1 0 obj
|
|
131
|
+
10
|
|
132
|
+
endobj
|
|
133
|
+
|
|
134
|
+
xref
|
|
135
|
+
0 2
|
|
136
|
+
0000000000 65535 f
|
|
137
|
+
0000000009 00000 n
|
|
138
|
+
trailer
|
|
139
|
+
<< /Size 2 /Prev 148>>
|
|
140
|
+
startxref
|
|
141
|
+
28
|
|
142
|
+
%%EOF
|
|
143
|
+
|
|
144
|
+
2 0 obj
|
|
145
|
+
300
|
|
146
|
+
endobj
|
|
147
|
+
|
|
148
|
+
xref
|
|
149
|
+
2 1
|
|
150
|
+
0000000301 00000 n
|
|
151
|
+
trailer
|
|
152
|
+
<< /Size 3 /Prev 28 /XRefStm 148>>
|
|
153
|
+
startxref
|
|
154
|
+
148
|
|
155
|
+
%%EOF
|
|
156
|
+
EOF
|
|
157
|
+
doc = HexaPDF::Document.new(io: io)
|
|
158
|
+
assert_equal(2, doc.revisions.count)
|
|
159
|
+
end
|
|
126
160
|
end
|
data/test/hexapdf/test_stream.rb
CHANGED
|
@@ -132,7 +132,7 @@ describe HexaPDF::Stream do
|
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
def encoded_data(str, encoders = [])
|
|
135
|
-
map =
|
|
135
|
+
map = @document.config['filter.map']
|
|
136
136
|
tmp = feeder(str)
|
|
137
137
|
encoders.each {|e| tmp = ::Object.const_get(map[e]).encoder(tmp)}
|
|
138
138
|
collector(tmp)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/type'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Type do
|
|
7
|
+
it "all autoload files have no syntax error" do
|
|
8
|
+
HexaPDF::Type.constants.each do |const|
|
|
9
|
+
HexaPDF::Type.const_get(const) # no assert needed here
|
|
10
|
+
end
|
|
11
|
+
HexaPDF::Type::Actions.constants.each do |const|
|
|
12
|
+
HexaPDF::Type::Actions.const_get(const) # no assert needed here
|
|
13
|
+
end
|
|
14
|
+
HexaPDF::Type::Annotations.constants.each do |const|
|
|
15
|
+
HexaPDF::Type::Annotations.const_get(const) # no assert needed here
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
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.6.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.6.0)>>
|
|
76
76
|
endobj
|
|
77
77
|
2 0 obj
|
|
78
78
|
<</Length 10>>stream
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/actions/launch'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::Actions::Launch do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@action = HexaPDF::Type::Actions::Launch.new({}, document: @doc)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "validation" do
|
|
14
|
+
it "needs a launch target" do
|
|
15
|
+
refute(@action.validate)
|
|
16
|
+
|
|
17
|
+
@action.value = {F: {}}
|
|
18
|
+
assert(@action.validate)
|
|
19
|
+
|
|
20
|
+
@action.value = {Win: {F: "test.exe"}}
|
|
21
|
+
assert(@action.validate)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/actions/uri'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::Actions::URI do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@action = HexaPDF::Type::Actions::URI.new({}, document: @doc)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "validation" do
|
|
14
|
+
it "URI needs to be ASCII only" do
|
|
15
|
+
refute(@action.validate)
|
|
16
|
+
|
|
17
|
+
@action[:URI] = "hellö"
|
|
18
|
+
refute(@action.validate(auto_correct: false))
|
|
19
|
+
assert(@action.validate(auto_correct: true))
|
|
20
|
+
assert_equal("hell%C3%B6", @action[:URI])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/annotations/link'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::Annotations::Link do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@annot = HexaPDF::Type::Annotations::Link.new({Rect: [0, 0, 1, 1]}, document: @doc)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "validation" do
|
|
14
|
+
it "checks for valid /H value" do
|
|
15
|
+
@annot[:H] = :invalid
|
|
16
|
+
refute(@annot.validate {|msg| assert_match(/contains invalid value/, msg)})
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/annotations/markup_annotation'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::Annotations::MarkupAnnotation do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@annot = HexaPDF::Type::Annotations::MarkupAnnotation.new({Subtype: :Text, Rect: [0, 0, 1, 1]},
|
|
11
|
+
document: @doc)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe "validation" do
|
|
15
|
+
it "needs IRT set if RT is set" do
|
|
16
|
+
assert(@annot.validate)
|
|
17
|
+
|
|
18
|
+
@annot[:RT] = :R
|
|
19
|
+
refute(@annot.validate)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/annotations/text'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::Annotations::Text do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@doc.version = '1.5'
|
|
11
|
+
@annot = HexaPDF::Type::Annotations::Text.new({Rect: [0, 0, 1, 1]}, document: @doc)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe "validation" do
|
|
15
|
+
it "checks for correct /StateModel values" do
|
|
16
|
+
@annot[:StateModel] = 'Invalid'
|
|
17
|
+
refute(@annot.validate {|msg| assert_match(/contains invalid value/, msg)})
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "automatically sets /StateModel based on the /State entry if possible" do
|
|
21
|
+
@annot[:State] = 'Marked'
|
|
22
|
+
assert(@annot.validate)
|
|
23
|
+
assert_equal('Marked', @annot[:StateModel])
|
|
24
|
+
|
|
25
|
+
@annot.delete(:StateModel)
|
|
26
|
+
@annot[:State] = 'Unknown'
|
|
27
|
+
refute(@annot.validate {|msg| assert_match(/StateModel required/, msg)})
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "checks whether /State and /StateModel match" do
|
|
31
|
+
@annot[:State] = 'Marked'
|
|
32
|
+
@annot[:StateModel] = 'Marked'
|
|
33
|
+
assert(@annot.validate)
|
|
34
|
+
@annot[:StateModel] = 'Review'
|
|
35
|
+
refute(@annot.validate {|msg| assert_match(/\/State and \/StateModel don't agree/, msg)})
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/annotation'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::Annotation do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@annot = @doc.add(Type: :Annotation, F: 0b100011)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe "flags" do
|
|
14
|
+
it "returns all flags" do
|
|
15
|
+
assert_equal([:invisible, :hidden, :no_view], @annot.flags)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "flagged?" do
|
|
20
|
+
it "returns true if the given flag is set" do
|
|
21
|
+
assert(@annot.flagged?(:hidden))
|
|
22
|
+
refute(@annot.flagged?(:locked))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "raises an error if an unknown flag name is provided" do
|
|
26
|
+
assert_raises(ArgumentError) { @annot.flagged?(:unknown) }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "flag" do
|
|
31
|
+
it "sets the given flag bits" do
|
|
32
|
+
@annot.flag(:locked)
|
|
33
|
+
assert_equal([:invisible, :hidden, :no_view, :locked], @annot.flags)
|
|
34
|
+
@annot.flag(:locked, clear_existing: true)
|
|
35
|
+
assert_equal([:locked], @annot.flags)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|