hexapdf 0.9.3 → 0.10.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/CONTRIBUTERS +2 -1
  4. data/VERSION +1 -1
  5. data/lib/hexapdf/cli/command.rb +9 -5
  6. data/lib/hexapdf/cli/images.rb +68 -13
  7. data/lib/hexapdf/cli/inspect.rb +201 -71
  8. data/lib/hexapdf/content/canvas.rb +1 -1
  9. data/lib/hexapdf/dictionary.rb +15 -1
  10. data/lib/hexapdf/dictionary_fields.rb +5 -4
  11. data/lib/hexapdf/document.rb +15 -6
  12. data/lib/hexapdf/encryption/security_handler.rb +3 -2
  13. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +165 -165
  14. data/lib/hexapdf/font/true_type_wrapper.rb +2 -2
  15. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  16. data/lib/hexapdf/image_loader/jpeg.rb +13 -11
  17. data/lib/hexapdf/reference.rb +5 -0
  18. data/lib/hexapdf/revision.rb +14 -0
  19. data/lib/hexapdf/serializer.rb +6 -6
  20. data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
  21. data/lib/hexapdf/type/image.rb +3 -1
  22. data/lib/hexapdf/version.rb +1 -1
  23. data/lib/hexapdf/xref_section.rb +8 -0
  24. data/man/man1/hexapdf.1 +88 -20
  25. data/test/data/images/truecolour-alpha-8bit.png +0 -0
  26. data/test/data/images/ycck.jpg +0 -0
  27. data/test/hexapdf/content/test_canvas.rb +2 -2
  28. data/test/hexapdf/document/test_images.rb +7 -5
  29. data/test/hexapdf/encryption/test_security_handler.rb +13 -0
  30. data/test/hexapdf/image_loader/test_jpeg.rb +10 -0
  31. data/test/hexapdf/image_loader/test_png.rb +3 -2
  32. data/test/hexapdf/test_dictionary.rb +9 -0
  33. data/test/hexapdf/test_dictionary_fields.rb +15 -0
  34. data/test/hexapdf/test_document.rb +5 -2
  35. data/test/hexapdf/test_reference.rb +4 -0
  36. data/test/hexapdf/test_revision.rb +12 -0
  37. data/test/hexapdf/test_writer.rb +5 -5
  38. data/test/hexapdf/test_xref_section.rb +11 -0
  39. data/test/hexapdf/type/test_form.rb +1 -1
  40. data/test/hexapdf/type/test_image.rb +28 -19
  41. metadata +3 -2
@@ -3,6 +3,7 @@
3
3
  require 'test_helper'
4
4
  require 'hexapdf/dictionary_fields'
5
5
  require 'hexapdf/dictionary'
6
+ require 'hexapdf/stream'
6
7
  require 'hexapdf/type'
7
8
 
8
9
  describe HexaPDF::DictionaryFields do
@@ -74,6 +75,20 @@ describe HexaPDF::DictionaryFields do
74
75
  @doc.verify
75
76
  end
76
77
 
78
+ it "allows conversion from an HexaPDF::Dictionary to a Stream if stream data is set" do
79
+ @field = self.class::Field.new(HexaPDF::Stream)
80
+ @doc.expect(:wrap, :data, [HexaPDF::Dictionary, Hash])
81
+ data = HexaPDF::PDFData.new({}, 0, 0, "")
82
+ @field.convert(HexaPDF::Dictionary.new(data), @doc)
83
+ @doc.verify
84
+ end
85
+
86
+ it "doesn't allow conversion to a Stream subclass from Hash or Dictionary" do
87
+ @field = self.class::Field.new(HexaPDF::Stream)
88
+ refute(@field.convert({}, @doc))
89
+ refute(@field.convert(HexaPDF::Dictionary.new(Test: :value), @doc))
90
+ end
91
+
77
92
  it "doesn't allow conversion from nil" do
78
93
  refute(@field.convert(nil, @doc))
79
94
  end
@@ -267,6 +267,7 @@ describe HexaPDF::Document do
267
267
  @myclass = Class.new(HexaPDF::Dictionary)
268
268
  @myclass.define_type(:MyClass)
269
269
  @myclass2 = Class.new(HexaPDF::Dictionary)
270
+ @myclass2.define_field(:Test, type: String, required: true)
270
271
  HexaPDF::GlobalConfiguration['object.type_map'][:MyClass] = @myclass
271
272
  HexaPDF::GlobalConfiguration['object.subtype_map'][nil][:Global] = @myclass2
272
273
  HexaPDF::GlobalConfiguration['object.subtype_map'][:MyClass] = {TheSecond: @myclass2}
@@ -328,14 +329,16 @@ describe HexaPDF::Document do
328
329
  it "uses the type/subtype information in the hash that should be wrapped" do
329
330
  assert_kind_of(@myclass, @doc.wrap(Type: :MyClass))
330
331
  refute_kind_of(@myclass2, @doc.wrap(Subtype: :TheSecond))
331
- assert_kind_of(@myclass2, @doc.wrap(Subtype: :Global))
332
+ refute_kind_of(@myclass2, @doc.wrap(Subtype: :Global))
333
+ assert_kind_of(@myclass2, @doc.wrap(Subtype: :Global, Test: "true"))
332
334
  assert_kind_of(@myclass2, @doc.wrap(Type: :MyClass, S: :TheSecond))
333
335
  assert_kind_of(@myclass, @doc.wrap(Type: :MyClass, Subtype: :TheThird))
334
336
  end
335
337
 
336
338
  it "respects the given type/subtype arguments" do
337
339
  assert_kind_of(@myclass, @doc.wrap({Type: :Other}, type: :MyClass))
338
- assert_kind_of(@myclass2, @doc.wrap({Subtype: :Other}, subtype: :Global))
340
+ refute_kind_of(@myclass2, @doc.wrap({Subtype: :Other}, subtype: :Global))
341
+ assert_kind_of(@myclass2, @doc.wrap({Subtype: :Other, Test: "true"}, subtype: :Global))
339
342
  assert_kind_of(@myclass2, @doc.wrap({Type: :Other, Subtype: :Other},
340
343
  type: :MyClass, subtype: :TheSecond))
341
344
  assert_kind_of(@myclass2, @doc.wrap({Subtype: :TheSecond}, type: @myclass))
@@ -40,6 +40,10 @@ describe HexaPDF::Reference do
40
40
  refute(h.key?(HexaPDF::Reference.new(5, 8)))
41
41
  end
42
42
 
43
+ it "shows the PDF serialization as string representation " do
44
+ assert_equal("5 7 R", HexaPDF::Reference.new(5, 7).to_s)
45
+ end
46
+
43
47
  it "shows oid and gen on inspection" do
44
48
  assert_match(/\[5, 7\]/, HexaPDF::Reference.new(5, 7).inspect)
45
49
  end
@@ -63,6 +63,18 @@ describe HexaPDF::Revision do
63
63
  end
64
64
  end
65
65
 
66
+ describe "xref" do
67
+ it "returns the xref structure" do
68
+ assert_equal(@xref_section[2, 0], @rev.xref(HexaPDF::Reference.new(2, 0)))
69
+ assert_equal(@xref_section[2, 0], @rev.xref(2))
70
+ end
71
+
72
+ it "returns nil if no xref entry is found" do
73
+ assert_nil(@rev.xref(@ref))
74
+ assert_nil(@rev.xref(1))
75
+ end
76
+ end
77
+
66
78
  describe "object" do
67
79
  it "returns nil if no object is found" do
68
80
  assert_nil(@rev.object(@ref))
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.9.3)>>
43
+ <</Producer(HexaPDF version 0.10.0)>>
44
44
  endobj
45
45
  xref
46
46
  3 1
@@ -48,7 +48,7 @@ describe HexaPDF::Writer do
48
48
  trailer
49
49
  <</Prev 219/Size 4/Root<</Type/Catalog>>/Info 3 0 R>>
50
50
  startxref
51
- 348
51
+ 349
52
52
  %%EOF
53
53
  EOF
54
54
 
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
72
72
  141
73
73
  %%EOF
74
74
  6 0 obj
75
- <</Producer(HexaPDF version 0.9.3)>>
75
+ <</Producer(HexaPDF version 0.10.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -81,11 +81,11 @@ describe HexaPDF::Writer do
81
81
  endobj
82
82
  4 0 obj
83
83
  <</Size 7/Prev 141/Root<</Type/Catalog>>/Info 6 0 R/Type/XRef/W[1 2 2]/Index[2 1 4 1 6 1]/Filter/FlateDecode/DecodeParms<</Columns 5/Predictor 12>>/Length 22>>stream
84
- x\xDAcbdlc``b`\xB0\x04\x93\x93\x19\x18\x00\f\x0F\x01[
84
+ x\xDAcbdlg``b`\xB0\x04\x93\x93\x18\x18\x00\f\e\x01[
85
85
  endstream
86
86
  endobj
87
87
  startxref
88
- 447
88
+ 448
89
89
  %%EOF
90
90
  EOF
91
91
  end
@@ -3,6 +3,17 @@
3
3
  require 'test_helper'
4
4
  require 'hexapdf/xref_section'
5
5
 
6
+ describe HexaPDF::XRefSection::Entry do
7
+ it "can describe itself" do
8
+ free = HexaPDF::XRefSection::Entry.new(:free, 1, 2)
9
+ normal = HexaPDF::XRefSection::Entry.new(:in_use, 1, 2, 10)
10
+ compressed = HexaPDF::XRefSection::Entry.new(:compressed, 1, 0, 2, 10)
11
+ assert_match(/1,2 type=free/, free.to_s)
12
+ assert_match(/1,2 type=normal/, normal.to_s)
13
+ assert_match(/1,0 type=compressed/, compressed.to_s)
14
+ end
15
+ end
16
+
6
17
  describe HexaPDF::XRefSection do
7
18
  before do
8
19
  @xref_section = HexaPDF::XRefSection.new
@@ -8,7 +8,7 @@ require 'hexapdf/type/form'
8
8
  describe HexaPDF::Type::Form do
9
9
  before do
10
10
  @doc = HexaPDF::Document.new
11
- @form = @doc.wrap({}, subtype: :Form)
11
+ @form = @doc.wrap({}, type: :XObject, subtype: :Form)
12
12
  end
13
13
 
14
14
  describe "box" do
@@ -12,19 +12,19 @@ describe HexaPDF::Type::Image do
12
12
  end
13
13
 
14
14
  it "returns the width of the image" do
15
- @image = @doc.wrap(Subtype: :Image, Width: 10)
15
+ @image = @doc.wrap(Type: :XObject, Subtype: :Image, Width: 10)
16
16
  assert_equal(10, @image.width)
17
17
  end
18
18
 
19
19
  it "returns the height of the image" do
20
- @image = @doc.wrap(Subtype: :Image, Height: 10)
20
+ @image = @doc.wrap(Type: :XObject, Subtype: :Image, Height: 10)
21
21
  assert_equal(10, @image.height)
22
22
  end
23
23
 
24
24
  describe "info" do
25
25
  before do
26
- @image = @doc.wrap(Subtype: :Image, Width: 10, Height: 5, ColorSpace: :DeviceRGB,
27
- BitsPerComponent: 4)
26
+ @image = @doc.wrap(Type: :XObject, Subtype: :Image, Width: 10, Height: 5,
27
+ ColorSpace: :DeviceRGB, BitsPerComponent: 4)
28
28
  end
29
29
 
30
30
  it "uses the Width, Height and BitsPerComponent values" do
@@ -131,18 +131,24 @@ describe HexaPDF::Type::Image do
131
131
  @file.unlink
132
132
  end
133
133
 
134
- `pngcheck 2>&1`
135
- if $?.exitstatus != 0
136
- warn("Skipping PNG output validity check because pngcheck executable is missing")
137
- PNG_CHECK_AVAILABLE = false
138
- else
139
- PNG_CHECK_AVAILABLE = true
140
- end
134
+ `which pngcheck 2>&1`
135
+ PNG_CHECK_AVAILABLE = $?.exitstatus == 0
136
+
137
+ `which pngtopnm 2>&1`
138
+ PNG_COMPARE_AVAILABLE = $?.exitstatus == 0
141
139
 
142
- def assert_valid_png(filename)
143
- return unless PNG_CHECK_AVAILABLE
144
- result = `pngcheck -q #{filename}`
145
- assert(result.empty?, "pngcheck error: #{result}")
140
+ def assert_valid_png(filename, original = nil)
141
+ if PNG_CHECK_AVAILABLE
142
+ result = `pngcheck -q #{filename}`
143
+ assert(result.empty?, "pngcheck error: #{result}")
144
+ else
145
+ skip("Skipping PNG output validity check because pngcheck executable is missing")
146
+ end
147
+ if PNG_COMPARE_AVAILABLE
148
+ assert_equal(`pngtopnm #{original}`, `pngtopnm #{filename}`) if original
149
+ else
150
+ skip("Skipping PNG output comparison check because pngtopnm executable is missing")
151
+ end
146
152
  end
147
153
 
148
154
  it "can write to an IO" do
@@ -179,8 +185,11 @@ describe HexaPDF::Type::Image do
179
185
  it "writes #{File.basename(png_file)} correctly as PNG file" do
180
186
  image = @doc.images.add(png_file)
181
187
  image.write(@file.path)
188
+ assert_valid_png(@file.path, png_file)
182
189
 
183
- assert_valid_png(@file.path)
190
+ image.delete(:DecodeParms) # force re-encoding of stream
191
+ image.write(@file.path)
192
+ assert_valid_png(@file.path, png_file)
184
193
 
185
194
  new_image = @doc.images.add(@file.path)
186
195
 
@@ -208,7 +217,7 @@ describe HexaPDF::Type::Image do
208
217
  end
209
218
 
210
219
  it "works for greyscale indexed images" do
211
- image = @doc.add(Subtype: :Image, Width: 2, Height: 2, BitsPerComponent: 2,
220
+ image = @doc.add(Type: :XObject, Subtype: :Image, Width: 2, Height: 2, BitsPerComponent: 2,
212
221
  ColorSpace: [:Indexed, :DeviceGray, 3, "\x00\x40\x80\xFF".b])
213
222
  image.stream = HexaPDF::StreamData.new(filter: :ASCIIHexDecode) { "10 B0".b }
214
223
  image.write(@file.path)
@@ -227,13 +236,13 @@ describe HexaPDF::Type::Image do
227
236
  end
228
237
 
229
238
  it "fails if an unsupported colorspace is used" do
230
- image = @doc.add(Subtype: :Image, Width: 1, Height: 1, BitsPerComponent: 8,
239
+ image = @doc.add(Type: :XObject, Subtype: :Image, Width: 1, Height: 1, BitsPerComponent: 8,
231
240
  ColorSpace: :ICCBased)
232
241
  assert_raises(HexaPDF::Error) { image.write(@file) }
233
242
  end
234
243
 
235
244
  it "fails if an indexed image with an unsupported colorspace is used" do
236
- image = @doc.add(Subtype: :Image, Width: 1, Height: 1, BitsPerComponent: 8,
245
+ image = @doc.add(Type: :XObject, Subtype: :Image, Width: 1, Height: 1, BitsPerComponent: 8,
237
246
  ColorSpace: [:Indexed, :ICCBased, 0, "0"])
238
247
  assert_raises(HexaPDF::Error) { image.write(@file) }
239
248
  end
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.9.3
4
+ version: 0.10.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: 2019-06-13 00:00:00.000000000 Z
11
+ date: 2019-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -436,6 +436,7 @@ files:
436
436
  - test/data/images/truecolour-alpha-8bit.png
437
437
  - test/data/images/truecolour-gama-chrm-8bit.png
438
438
  - test/data/images/truecolour-srgb-8bit.png
439
+ - test/data/images/ycck.jpg
439
440
  - test/data/minimal.pdf
440
441
  - test/data/standard-security-handler/README
441
442
  - test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf