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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -1
  3. data/CONTRIBUTERS +1 -1
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -1
  6. data/examples/018-composer.rb +44 -0
  7. data/lib/hexapdf/cli.rb +2 -0
  8. data/lib/hexapdf/cli/command.rb +2 -2
  9. data/lib/hexapdf/cli/optimize.rb +1 -1
  10. data/lib/hexapdf/cli/split.rb +82 -0
  11. data/lib/hexapdf/composer.rb +303 -0
  12. data/lib/hexapdf/configuration.rb +2 -2
  13. data/lib/hexapdf/content/canvas.rb +3 -6
  14. data/lib/hexapdf/dictionary.rb +0 -3
  15. data/lib/hexapdf/document.rb +30 -22
  16. data/lib/hexapdf/document/files.rb +1 -1
  17. data/lib/hexapdf/document/images.rb +1 -1
  18. data/lib/hexapdf/filter/predictor.rb +8 -8
  19. data/lib/hexapdf/layout.rb +1 -0
  20. data/lib/hexapdf/layout/box.rb +55 -12
  21. data/lib/hexapdf/layout/frame.rb +143 -46
  22. data/lib/hexapdf/layout/image_box.rb +96 -0
  23. data/lib/hexapdf/layout/inline_box.rb +10 -0
  24. data/lib/hexapdf/layout/line.rb +1 -1
  25. data/lib/hexapdf/layout/style.rb +55 -3
  26. data/lib/hexapdf/layout/text_box.rb +38 -8
  27. data/lib/hexapdf/layout/text_layouter.rb +66 -52
  28. data/lib/hexapdf/object.rb +3 -2
  29. data/lib/hexapdf/parser.rb +6 -1
  30. data/lib/hexapdf/rectangle.rb +6 -0
  31. data/lib/hexapdf/reference.rb +4 -6
  32. data/lib/hexapdf/revision.rb +34 -8
  33. data/lib/hexapdf/revisions.rb +12 -2
  34. data/lib/hexapdf/stream.rb +18 -0
  35. data/lib/hexapdf/task.rb +1 -1
  36. data/lib/hexapdf/task/dereference.rb +1 -1
  37. data/lib/hexapdf/task/optimize.rb +2 -2
  38. data/lib/hexapdf/type/form.rb +10 -0
  39. data/lib/hexapdf/version.rb +1 -1
  40. data/lib/hexapdf/writer.rb +25 -5
  41. data/man/man1/hexapdf.1 +17 -0
  42. data/test/hexapdf/layout/test_box.rb +7 -1
  43. data/test/hexapdf/layout/test_frame.rb +38 -1
  44. data/test/hexapdf/layout/test_image_box.rb +73 -0
  45. data/test/hexapdf/layout/test_inline_box.rb +8 -0
  46. data/test/hexapdf/layout/test_line.rb +1 -1
  47. data/test/hexapdf/layout/test_style.rb +58 -1
  48. data/test/hexapdf/layout/test_text_box.rb +57 -12
  49. data/test/hexapdf/layout/test_text_layouter.rb +14 -4
  50. data/test/hexapdf/task/test_optimize.rb +3 -3
  51. data/test/hexapdf/test_composer.rb +258 -0
  52. data/test/hexapdf/test_document.rb +29 -3
  53. data/test/hexapdf/test_importer.rb +8 -2
  54. data/test/hexapdf/test_object.rb +3 -1
  55. data/test/hexapdf/test_parser.rb +6 -0
  56. data/test/hexapdf/test_rectangle.rb +7 -0
  57. data/test/hexapdf/test_reference.rb +3 -1
  58. data/test/hexapdf/test_revision.rb +13 -0
  59. data/test/hexapdf/test_revisions.rb +1 -0
  60. data/test/hexapdf/test_stream.rb +13 -0
  61. data/test/hexapdf/test_writer.rb +13 -2
  62. data/test/hexapdf/type/test_annotation.rb +1 -1
  63. data/test/hexapdf/type/test_form.rb +13 -2
  64. 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(current: true).sort.map(&:value))
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(current: false).sort.map(&:value))
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(current: false) do |obj, rev|
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
- obj = HexaPDF::Importer::NullableWeakRef.new(Object.new)
10
- GC.start
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
@@ -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
@@ -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 itself" do
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)
@@ -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
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.8.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.8.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)
@@ -7,7 +7,7 @@ require 'hexapdf/type/annotation'
7
7
  describe HexaPDF::Type::Annotation do
8
8
  before do
9
9
  @doc = HexaPDF::Document.new
10
- @annot = @doc.add(Type: :Annotation, F: 0b100011)
10
+ @annot = @doc.add(Type: :Annot, F: 0b100011)
11
11
  end
12
12
 
13
13
  describe "flags" do
@@ -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
- @form[:BBox] = :media
17
- assert_equal(:media, @form.box)
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.8.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-10-26 00:00:00.000000000 Z
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.1'
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.1'
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