hexapdf 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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