hexapdf 0.5.0 → 0.6.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 +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
|