hexapdf 0.8.0 → 0.9.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 +52 -1
- data/CONTRIBUTERS +1 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/018-composer.rb +44 -0
- data/lib/hexapdf/cli.rb +2 -0
- data/lib/hexapdf/cli/command.rb +2 -2
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/cli/split.rb +82 -0
- data/lib/hexapdf/composer.rb +303 -0
- data/lib/hexapdf/configuration.rb +2 -2
- data/lib/hexapdf/content/canvas.rb +3 -6
- data/lib/hexapdf/dictionary.rb +0 -3
- data/lib/hexapdf/document.rb +30 -22
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +8 -8
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/layout/box.rb +55 -12
- data/lib/hexapdf/layout/frame.rb +143 -46
- data/lib/hexapdf/layout/image_box.rb +96 -0
- data/lib/hexapdf/layout/inline_box.rb +10 -0
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/style.rb +55 -3
- data/lib/hexapdf/layout/text_box.rb +38 -8
- data/lib/hexapdf/layout/text_layouter.rb +66 -52
- data/lib/hexapdf/object.rb +3 -2
- data/lib/hexapdf/parser.rb +6 -1
- data/lib/hexapdf/rectangle.rb +6 -0
- data/lib/hexapdf/reference.rb +4 -6
- data/lib/hexapdf/revision.rb +34 -8
- data/lib/hexapdf/revisions.rb +12 -2
- data/lib/hexapdf/stream.rb +18 -0
- data/lib/hexapdf/task.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +2 -2
- data/lib/hexapdf/type/form.rb +10 -0
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +25 -5
- data/man/man1/hexapdf.1 +17 -0
- data/test/hexapdf/layout/test_box.rb +7 -1
- data/test/hexapdf/layout/test_frame.rb +38 -1
- data/test/hexapdf/layout/test_image_box.rb +73 -0
- data/test/hexapdf/layout/test_inline_box.rb +8 -0
- data/test/hexapdf/layout/test_line.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +58 -1
- data/test/hexapdf/layout/test_text_box.rb +57 -12
- data/test/hexapdf/layout/test_text_layouter.rb +14 -4
- data/test/hexapdf/task/test_optimize.rb +3 -3
- data/test/hexapdf/test_composer.rb +258 -0
- data/test/hexapdf/test_document.rb +29 -3
- data/test/hexapdf/test_importer.rb +8 -2
- data/test/hexapdf/test_object.rb +3 -1
- data/test/hexapdf/test_parser.rb +6 -0
- data/test/hexapdf/test_rectangle.rb +7 -0
- data/test/hexapdf/test_reference.rb +3 -1
- data/test/hexapdf/test_revision.rb +13 -0
- data/test/hexapdf/test_revisions.rb +1 -0
- data/test/hexapdf/test_stream.rb +13 -0
- data/test/hexapdf/test_writer.rb +13 -2
- data/test/hexapdf/type/test_annotation.rb +1 -1
- data/test/hexapdf/type/test_form.rb +13 -2
- metadata +10 -4
@@ -386,17 +386,22 @@ describe HexaPDF::Document do
|
|
386
386
|
|
387
387
|
describe "each" do
|
388
388
|
it "iterates over the current objects" do
|
389
|
-
assert_equal([10, 200, nil], @io_doc.each(
|
389
|
+
assert_equal([10, 200, nil], @io_doc.each(only_current: true).sort.map(&:value))
|
390
390
|
end
|
391
391
|
|
392
392
|
it "iterates over all objects" do
|
393
|
-
assert_equal([10, 200, 20, 30, nil], @io_doc.each(
|
393
|
+
assert_equal([10, 200, 20, 30, nil], @io_doc.each(only_current: false).sort.map(&:value))
|
394
|
+
end
|
395
|
+
|
396
|
+
it "iterates over all loaded objects" do
|
397
|
+
assert_equal(200, @io_doc.object(2).value)
|
398
|
+
assert_equal([200], @io_doc.each(only_loaded: true).sort.map(&:value))
|
394
399
|
end
|
395
400
|
|
396
401
|
it "yields the revision as second argument if the block accepts exactly two arguments" do
|
397
402
|
objs = [[10, 20, 30], [200, nil]]
|
398
403
|
data = @io_doc.revisions.map.with_index {|rev, i| objs[i].map {|o| [o, rev] } }.reverse.flatten
|
399
|
-
@io_doc.each(
|
404
|
+
@io_doc.each(only_current: false) do |obj, rev|
|
400
405
|
assert(data.shift == obj.value)
|
401
406
|
assert_equal(data.shift, rev)
|
402
407
|
end
|
@@ -448,6 +453,21 @@ describe HexaPDF::Document do
|
|
448
453
|
@doc.trailer[:ID] = :Symbol
|
449
454
|
refute(@doc.validate {|obj| assert_same(@doc.trailer, obj) })
|
450
455
|
end
|
456
|
+
|
457
|
+
it "validates only loaded objects" do
|
458
|
+
io = StringIO.new
|
459
|
+
doc = HexaPDF::Document.new
|
460
|
+
doc.pages.add.delete(:Resources)
|
461
|
+
page = doc.pages.add
|
462
|
+
page[:Annots] = [doc.add(Type: :Annot, Subtype: :Link, Rect: [0, 0, 1, 1], H: :Z)]
|
463
|
+
doc.write(io, validate: false)
|
464
|
+
doc = HexaPDF::Document.new(io: io)
|
465
|
+
doc.pages[0] # force loading of the first page
|
466
|
+
|
467
|
+
refute(doc.validate(auto_correct: false, only_loaded: true)) # bc of Resources
|
468
|
+
assert(doc.validate(only_loaded: true))
|
469
|
+
refute(doc.validate(auto_correct: false)) # bc of annot key H
|
470
|
+
end
|
451
471
|
end
|
452
472
|
|
453
473
|
describe "write" do
|
@@ -470,6 +490,12 @@ describe HexaPDF::Document do
|
|
470
490
|
refute(io.string.empty?)
|
471
491
|
end
|
472
492
|
|
493
|
+
it "writes the document incrementally" do
|
494
|
+
io = StringIO.new
|
495
|
+
@io_doc.write(io, incremental: true)
|
496
|
+
assert_equal(@io.string, io.string[0, @io.string.length])
|
497
|
+
end
|
498
|
+
|
473
499
|
it "fails if the document is not valid" do
|
474
500
|
@doc.trailer[:Size] = :Symbol
|
475
501
|
assert_raises(HexaPDF::Error) { @doc.write(StringIO.new(''.b)) }
|
@@ -6,8 +6,14 @@ require 'hexapdf/document'
|
|
6
6
|
|
7
7
|
describe HexaPDF::Importer::NullableWeakRef do
|
8
8
|
it "returns nil instead of an error when the referred-to object is GCed" do
|
9
|
-
|
10
|
-
|
9
|
+
refs = []
|
10
|
+
obj = nil
|
11
|
+
100.times do
|
12
|
+
refs << HexaPDF::Importer::NullableWeakRef.new(Object.new)
|
13
|
+
ObjectSpace.garbage_collect
|
14
|
+
ObjectSpace.garbage_collect
|
15
|
+
break if (obj = refs.find {|ref| !ref.weakref_alive? })
|
16
|
+
end
|
11
17
|
assert_equal("", obj.to_s)
|
12
18
|
end
|
13
19
|
end
|
data/test/hexapdf/test_object.rb
CHANGED
@@ -135,7 +135,7 @@ describe HexaPDF::Object do
|
|
135
135
|
assert_match(/\[5, 0\].*value=5/, obj.inspect)
|
136
136
|
end
|
137
137
|
|
138
|
-
it "can be compared to another object" do
|
138
|
+
it "can be compared to another object or reference" do
|
139
139
|
obj = HexaPDF::Object.new(5, oid: 5)
|
140
140
|
|
141
141
|
assert_equal(obj, HexaPDF::Object.new(obj))
|
@@ -143,6 +143,8 @@ describe HexaPDF::Object do
|
|
143
143
|
refute_equal(obj, HexaPDF::Object.new(6, oid: 5))
|
144
144
|
refute_equal(obj, HexaPDF::Object.new(5, oid: 1))
|
145
145
|
refute_equal(obj, HexaPDF::Object.new(5, oid: 5, gen: 1))
|
146
|
+
|
147
|
+
assert_equal(obj, HexaPDF::Reference.new(5, 0))
|
146
148
|
end
|
147
149
|
|
148
150
|
it "works correctly as hash key, is interchangable in this regard with Reference objects" do
|
data/test/hexapdf/test_parser.rb
CHANGED
@@ -194,6 +194,12 @@ describe HexaPDF::Parser do
|
|
194
194
|
end
|
195
195
|
|
196
196
|
describe "startxref_offset" do
|
197
|
+
it "caches the offset value" do
|
198
|
+
assert_equal(308, @parser.startxref_offset)
|
199
|
+
@parser.instance_eval { @io }.string.sub!(/308\n/, "309\n")
|
200
|
+
assert_equal(308, @parser.startxref_offset)
|
201
|
+
end
|
202
|
+
|
197
203
|
it "returns the correct offset" do
|
198
204
|
assert_equal(308, @parser.startxref_offset)
|
199
205
|
end
|
@@ -35,6 +35,13 @@ describe HexaPDF::Rectangle do
|
|
35
35
|
assert_equal(4, rect.height)
|
36
36
|
end
|
37
37
|
|
38
|
+
it "allows comparison to arrays" do
|
39
|
+
rect = HexaPDF::Rectangle.new([0, 1, 2, 5])
|
40
|
+
assert(rect == [0, 1, 2, 5])
|
41
|
+
rect.oid = 5
|
42
|
+
refute(rect == [0, 1, 2, 5])
|
43
|
+
end
|
44
|
+
|
38
45
|
describe "validation" do
|
39
46
|
it "ensures that it is a correct PDF rectangle" do
|
40
47
|
doc = HexaPDF::Document.new
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'test_helper'
|
4
4
|
require 'hexapdf/reference'
|
5
|
+
require 'hexapdf/object'
|
5
6
|
|
6
7
|
describe HexaPDF::Reference do
|
7
8
|
it "correctly assigns oid and gen on initialization" do
|
@@ -25,10 +26,11 @@ describe HexaPDF::Reference do
|
|
25
26
|
assert_nil(HexaPDF::Reference.new(1, 0) <=> 5)
|
26
27
|
end
|
27
28
|
|
28
|
-
it "is comparable to
|
29
|
+
it "is comparable to objects that have an oid and gen" do
|
29
30
|
assert_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(5, 7))
|
30
31
|
refute_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(5, 8))
|
31
32
|
refute_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(4, 7))
|
33
|
+
assert_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Object.new(:data, oid: 5, gen: 7))
|
32
34
|
end
|
33
35
|
|
34
36
|
it "behaves correctly as hash key" do
|
@@ -125,6 +125,11 @@ describe HexaPDF::Revision do
|
|
125
125
|
obj3 = @rev.object(3)
|
126
126
|
assert_equal([@obj, obj2, obj3], @rev.each.to_a)
|
127
127
|
end
|
128
|
+
|
129
|
+
it "iterates only over loaded objects" do
|
130
|
+
obj = @rev.object(2)
|
131
|
+
assert_equal([obj], @rev.each(only_loaded: true).to_a)
|
132
|
+
end
|
128
133
|
end
|
129
134
|
|
130
135
|
it "works without a cross-reference section" do
|
@@ -136,4 +141,12 @@ describe HexaPDF::Revision do
|
|
136
141
|
rev.delete(@ref, mark_as_free: false)
|
137
142
|
refute(rev.object?(@ref))
|
138
143
|
end
|
144
|
+
|
145
|
+
it "can iterate over all modified objects" do
|
146
|
+
obj = @rev.object(2)
|
147
|
+
assert_equal([], @rev.each_modified_object.to_a)
|
148
|
+
obj.value = :Other
|
149
|
+
@rev.add(@obj)
|
150
|
+
assert_equal([obj, @obj], @rev.each_modified_object.to_a)
|
151
|
+
end
|
139
152
|
end
|
@@ -118,6 +118,7 @@ describe HexaPDF::Revisions do
|
|
118
118
|
|
119
119
|
describe "initialize" do
|
120
120
|
it "automatically loads all revisions from the underlying IO object" do
|
121
|
+
assert_kind_of(HexaPDF::Parser, @revisions.parser)
|
121
122
|
assert_equal(20, @revisions.revision(0).object(2).value)
|
122
123
|
assert_equal(300, @revisions[1].object(2).value)
|
123
124
|
assert_equal(400, @revisions[2].object(2).value)
|
data/test/hexapdf/test_stream.rb
CHANGED
@@ -58,6 +58,19 @@ describe HexaPDF::StreamData do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
describe "==" do
|
63
|
+
it "compares with other stream data objects" do
|
64
|
+
s = HexaPDF::StreamData.new(:source, decode_parms: [:a, nil, :b])
|
65
|
+
assert_equal(s, HexaPDF::StreamData.new(:source, decode_parms: [:a, nil, :b]))
|
66
|
+
refute_equal(s, HexaPDF::StreamData.new(:source, decode_parms: [:a, :b]))
|
67
|
+
refute_equal(s, HexaPDF::StreamData.new(:source, decode_parms: [:a, nil, :b], offset: 5))
|
68
|
+
end
|
69
|
+
|
70
|
+
it "returns false if compared with an other object of a different class" do
|
71
|
+
refute_equal(:source, HexaPDF::StreamData.new(:source))
|
72
|
+
end
|
73
|
+
end
|
61
74
|
end
|
62
75
|
|
63
76
|
describe HexaPDF::Stream do
|
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.9.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.9.0)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -103,6 +103,17 @@ describe HexaPDF::Writer do
|
|
103
103
|
assert_document_conversion(@compressed_input_io)
|
104
104
|
end
|
105
105
|
|
106
|
+
it "writes a document in incremental mode" do
|
107
|
+
doc = HexaPDF::Document.new(io: @std_input_io)
|
108
|
+
doc.pages.add
|
109
|
+
output_io = StringIO.new
|
110
|
+
HexaPDF::Writer.write(doc, output_io, incremental: true)
|
111
|
+
assert_equal(output_io.string[0, @std_input_io.string.length], @std_input_io.string)
|
112
|
+
doc = HexaPDF::Document.new(io: output_io)
|
113
|
+
assert_equal(4, doc.revisions.size)
|
114
|
+
assert_equal(2, doc.revisions.current.each.to_a.size)
|
115
|
+
end
|
116
|
+
|
106
117
|
it "raises an error if no xref stream is in a revision but object streams are" do
|
107
118
|
document = HexaPDF::Document.new
|
108
119
|
document.add(Type: :ObjStm)
|
@@ -12,9 +12,20 @@ describe HexaPDF::Type::Form do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
describe "box" do
|
15
|
+
before do
|
16
|
+
@form[:BBox] = [10, 10, 110, 60]
|
17
|
+
end
|
18
|
+
|
15
19
|
it "returns the /BBox entry" do
|
16
|
-
|
17
|
-
|
20
|
+
assert_equal([10, 10, 110, 60], @form.box.value)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns the box's width" do
|
24
|
+
assert_equal(100, @form.width)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns the box's height" do
|
28
|
+
assert_equal(50, @form.height)
|
18
29
|
end
|
19
30
|
end
|
20
31
|
|
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.9.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: 2018-
|
11
|
+
date: 2018-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|
@@ -36,14 +36,14 @@ dependencies:
|
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '0.
|
39
|
+
version: '0.2'
|
40
40
|
type: :runtime
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '0.
|
46
|
+
version: '0.2'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: kramdown
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -205,6 +205,7 @@ files:
|
|
205
205
|
- examples/015-boxes.rb
|
206
206
|
- examples/016-frame_automatic_box_placement.rb
|
207
207
|
- examples/017-frame_text_flow.rb
|
208
|
+
- examples/018-composer.rb
|
208
209
|
- examples/emoji-smile.png
|
209
210
|
- examples/emoji-wink.png
|
210
211
|
- examples/machupicchu.jpg
|
@@ -219,6 +220,8 @@ files:
|
|
219
220
|
- lib/hexapdf/cli/merge.rb
|
220
221
|
- lib/hexapdf/cli/modify.rb
|
221
222
|
- lib/hexapdf/cli/optimize.rb
|
223
|
+
- lib/hexapdf/cli/split.rb
|
224
|
+
- lib/hexapdf/composer.rb
|
222
225
|
- lib/hexapdf/configuration.rb
|
223
226
|
- lib/hexapdf/content.rb
|
224
227
|
- lib/hexapdf/content/canvas.rb
|
@@ -315,6 +318,7 @@ files:
|
|
315
318
|
- lib/hexapdf/layout.rb
|
316
319
|
- lib/hexapdf/layout/box.rb
|
317
320
|
- lib/hexapdf/layout/frame.rb
|
321
|
+
- lib/hexapdf/layout/image_box.rb
|
318
322
|
- lib/hexapdf/layout/inline_box.rb
|
319
323
|
- lib/hexapdf/layout/line.rb
|
320
324
|
- lib/hexapdf/layout/numeric_refinements.rb
|
@@ -533,6 +537,7 @@ files:
|
|
533
537
|
- test/hexapdf/image_loader/test_png.rb
|
534
538
|
- test/hexapdf/layout/test_box.rb
|
535
539
|
- test/hexapdf/layout/test_frame.rb
|
540
|
+
- test/hexapdf/layout/test_image_box.rb
|
536
541
|
- test/hexapdf/layout/test_inline_box.rb
|
537
542
|
- test/hexapdf/layout/test_line.rb
|
538
543
|
- test/hexapdf/layout/test_style.rb
|
@@ -543,6 +548,7 @@ files:
|
|
543
548
|
- test/hexapdf/layout/test_width_from_polygon.rb
|
544
549
|
- test/hexapdf/task/test_dereference.rb
|
545
550
|
- test/hexapdf/task/test_optimize.rb
|
551
|
+
- test/hexapdf/test_composer.rb
|
546
552
|
- test/hexapdf/test_configuration.rb
|
547
553
|
- test/hexapdf/test_data_dir.rb
|
548
554
|
- test/hexapdf/test_dictionary.rb
|