hexapdf 0.20.2 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +5 -3
  4. data/Rakefile +10 -1
  5. data/examples/018-composer.rb +10 -10
  6. data/lib/hexapdf/cli/batch.rb +4 -6
  7. data/lib/hexapdf/cli/form.rb +9 -1
  8. data/lib/hexapdf/cli/info.rb +5 -1
  9. data/lib/hexapdf/cli/inspect.rb +59 -0
  10. data/lib/hexapdf/cli/split.rb +1 -1
  11. data/lib/hexapdf/composer.rb +147 -53
  12. data/lib/hexapdf/configuration.rb +7 -3
  13. data/lib/hexapdf/content/canvas.rb +1 -1
  14. data/lib/hexapdf/content/color_space.rb +1 -1
  15. data/lib/hexapdf/content/operator.rb +7 -7
  16. data/lib/hexapdf/content/parser.rb +3 -3
  17. data/lib/hexapdf/content/processor.rb +9 -9
  18. data/lib/hexapdf/document/signatures.rb +5 -4
  19. data/lib/hexapdf/document.rb +7 -0
  20. data/lib/hexapdf/encryption/security_handler.rb +5 -1
  21. data/lib/hexapdf/font/true_type/font.rb +7 -7
  22. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  23. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  24. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +3 -3
  25. data/lib/hexapdf/font/true_type_wrapper.rb +9 -14
  26. data/lib/hexapdf/font/type1/font.rb +10 -12
  27. data/lib/hexapdf/font/type1_wrapper.rb +15 -17
  28. data/lib/hexapdf/layout/box.rb +12 -9
  29. data/lib/hexapdf/layout/image_box.rb +1 -1
  30. data/lib/hexapdf/layout/style.rb +28 -8
  31. data/lib/hexapdf/layout/text_fragment.rb +10 -9
  32. data/lib/hexapdf/parser.rb +5 -0
  33. data/lib/hexapdf/tokenizer.rb +3 -3
  34. data/lib/hexapdf/type/acro_form/appearance_generator.rb +6 -4
  35. data/lib/hexapdf/type/acro_form/choice_field.rb +2 -2
  36. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  37. data/lib/hexapdf/type/annotation.rb +1 -1
  38. data/lib/hexapdf/type/font_type0.rb +1 -1
  39. data/lib/hexapdf/type/font_type3.rb +1 -1
  40. data/lib/hexapdf/type/resources.rb +4 -4
  41. data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -1
  42. data/lib/hexapdf/type/signature.rb +1 -1
  43. data/lib/hexapdf/type/trailer.rb +3 -3
  44. data/lib/hexapdf/version.rb +1 -1
  45. data/lib/hexapdf/writer.rb +11 -3
  46. data/lib/hexapdf/xref_section.rb +1 -1
  47. data/test/hexapdf/common_tokenizer_tests.rb +5 -5
  48. data/test/hexapdf/content/test_graphics_state.rb +1 -0
  49. data/test/hexapdf/content/test_operator.rb +2 -2
  50. data/test/hexapdf/content/test_processor.rb +1 -1
  51. data/test/hexapdf/encryption/test_security_handler.rb +11 -3
  52. data/test/hexapdf/encryption/test_standard_security_handler.rb +23 -29
  53. data/test/hexapdf/filter/test_predictor.rb +16 -20
  54. data/test/hexapdf/font/test_type1_wrapper.rb +5 -1
  55. data/test/hexapdf/font/true_type/table/common.rb +1 -1
  56. data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
  57. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +1 -1
  58. data/test/hexapdf/image_loader/test_pdf.rb +6 -8
  59. data/test/hexapdf/image_loader/test_png.rb +2 -2
  60. data/test/hexapdf/layout/test_box.rb +11 -1
  61. data/test/hexapdf/layout/test_style.rb +23 -0
  62. data/test/hexapdf/layout/test_text_fragment.rb +21 -21
  63. data/test/hexapdf/test_composer.rb +115 -52
  64. data/test/hexapdf/test_dictionary.rb +2 -2
  65. data/test/hexapdf/test_document.rb +11 -9
  66. data/test/hexapdf/test_object.rb +1 -1
  67. data/test/hexapdf/test_parser.rb +13 -7
  68. data/test/hexapdf/test_serializer.rb +20 -22
  69. data/test/hexapdf/test_stream.rb +7 -9
  70. data/test/hexapdf/test_writer.rb +13 -2
  71. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
  72. data/test/hexapdf/type/acro_form/test_choice_field.rb +1 -1
  73. data/test/hexapdf/type/signature/common.rb +1 -1
  74. data/test/hexapdf/type/test_annotation.rb +7 -1
  75. data/test/hexapdf/type/test_font_type0.rb +1 -1
  76. data/test/hexapdf/type/test_font_type1.rb +7 -7
  77. data/test/hexapdf/type/test_image.rb +13 -17
  78. metadata +2 -2
@@ -19,7 +19,7 @@ describe HexaPDF::Composer do
19
19
  assert_equal(36, @composer.frame.bottom)
20
20
  assert_equal(523, @composer.frame.width)
21
21
  assert_equal(770, @composer.frame.height)
22
- assert_equal("Times", @composer.base_style.font)
22
+ assert_kind_of(HexaPDF::Layout::Style, @composer.style(:base))
23
23
  end
24
24
 
25
25
  it "allows the customization of the page size" do
@@ -86,83 +86,144 @@ describe HexaPDF::Composer do
86
86
  assert_equal(806, @composer.y)
87
87
  end
88
88
 
89
+ describe "style" do
90
+ it "creates a new style if it does not exist based on the base argument" do
91
+ @composer.style(:base, font_size: 20)
92
+ assert_equal(20, @composer.style(:newstyle, subscript: true).font_size)
93
+ refute( @composer.style(:base).subscript)
94
+ assert_equal(10, @composer.style(:another_new, base: nil).font_size)
95
+ assert(@composer.style(:yet_another_new, base: :newstyle).subscript)
96
+ end
97
+
98
+ it "returns the named style" do
99
+ assert_kind_of(HexaPDF::Layout::Style, @composer.style(:base))
100
+ end
101
+
102
+ it "updates the style with the given properties" do
103
+ assert_equal(20, @composer.style(:base, font_size: 20).font_size)
104
+ end
105
+ end
106
+
89
107
  describe "text" do
90
- it "creates a text box and draws it on the canvas" do
91
- box = nil
92
- @composer.define_singleton_method(:draw_box) {|arg| box = arg }
108
+ before do
109
+ test_self = self
110
+ @composer.define_singleton_method(:draw_box) do |arg|
111
+ test_self.instance_variable_set(:@box, arg)
112
+ end
113
+ end
93
114
 
115
+ it "creates a text box and draws it on the canvas" do
94
116
  @composer.text("Test", width: 10, height: 15)
95
- assert_equal(10, box.width)
96
- assert_equal(15, box.height)
97
- assert_same(@composer.document.fonts.add("Times"), box.style.font)
98
- items = box.instance_variable_get(:@items)
117
+ assert_equal(10, @box.width)
118
+ assert_equal(15, @box.height)
119
+ assert_same(@composer.document.fonts.add("Times"), @box.style.font)
120
+ items = @box.instance_variable_get(:@items)
99
121
  assert_equal(1, items.length)
100
- assert_same(box.style, items.first.style)
122
+ assert_same(@box.style, items.first.style)
101
123
  end
102
124
 
103
125
  it "allows setting of a custom style" do
104
- box = nil
105
- @composer.define_singleton_method(:draw_box) {|arg| box = arg }
106
-
107
- @composer.text("Test", style: HexaPDF::Layout::Style.new(font_size: 20))
108
- assert_same(@composer.document.fonts.add("Times"), box.style.font)
109
- assert_equal(20, box.style.font_size)
126
+ style = HexaPDF::Layout::Style.new(font_size: 20, font: ['Times', {variant: :bold}])
127
+ @composer.text("Test", style: style)
128
+ assert_same(@box.style, style)
129
+ assert_same(@composer.document.fonts.add("Times", variant: :bold), @box.style.font)
130
+ assert_equal(20, @box.style.font_size)
131
+
132
+ @composer.text("Test", style: {font_size: 20})
133
+ assert_equal(20, @box.style.font_size)
134
+
135
+ @composer.style(:named, font_size: 20)
136
+ @composer.text("Test", style: :named)
137
+ assert_equal(20, @box.style.font_size)
110
138
  end
111
139
 
112
140
  it "updates the used style with the provided options" do
113
- box = nil
114
- @composer.define_singleton_method(:draw_box) {|arg| box = arg }
141
+ @composer.text("Test", style: {subscript: true}, font_size: 20)
142
+ assert_equal(20, @box.style.font_size)
143
+ end
115
144
 
116
- @composer.text("Test", style: HexaPDF::Layout::Style.new, font_size: 20)
117
- assert_equal(20, box.style.font_size)
145
+ it "allows using a box style different from the text style" do
146
+ style = HexaPDF::Layout::Style.new(font_size: 20)
147
+ @composer.text("Test", box_style: style)
148
+ refute_same(@box.instance_variable_get(:@items).first.style, style)
149
+ assert_same(@box.style, style)
150
+
151
+ @composer.style(:named, font_size: 20)
152
+ @composer.text("Test", box_style: :named)
153
+ assert_equal(20, @box.style.font_size)
118
154
  end
119
155
  end
120
156
 
121
157
  describe "formatted_text" do
122
- it "creates a text box with the formatted text and draws it on the canvas" do
123
- box = nil
124
- @composer.define_singleton_method(:draw_box) {|arg| box = arg }
158
+ before do
159
+ test_self = self
160
+ @composer.define_singleton_method(:draw_box) do |arg|
161
+ test_self.instance_variable_set(:@box, arg)
162
+ end
163
+ end
125
164
 
165
+ it "creates a text box with the given text and draws it on the canvas" do
126
166
  @composer.formatted_text(["Test"], width: 10, height: 15)
127
- assert_equal(10, box.width)
128
- assert_equal(15, box.height)
129
- assert_equal(1, box.instance_variable_get(:@items).length)
167
+ assert_equal(10, @box.width)
168
+ assert_equal(15, @box.height)
169
+ assert_equal(1, @box.instance_variable_get(:@items).length)
130
170
  end
131
171
 
132
- it "a hash can be used for custom style properties" do
133
- box = nil
134
- @composer.define_singleton_method(:draw_box) {|arg| box = arg }
172
+ it "allows using a hash with :text key instead of a simple string" do
173
+ @composer.formatted_text([{text: "Test"}])
174
+ items = @box.instance_variable_get(:@items)
175
+ assert_equal(4, items[0].items.length)
176
+ end
135
177
 
136
- @composer.formatted_text([{text: "Test", font_size: 20}], align: :center)
137
- items = box.instance_variable_get(:@items)
138
- assert_equal(1, items.length)
139
- assert_equal(20, items.first.style.font_size)
140
- assert_equal(:center, items.first.style.align)
141
- assert_equal(10, box.style.font_size)
178
+ it "uses an empty string if the :text key for a hash is not specified" do
179
+ @composer.formatted_text([{font_size: "Test"}])
180
+ items = @box.instance_variable_get(:@items)
181
+ assert_equal(0, items[0].items.length)
142
182
  end
143
183
 
144
- it "a hash can be used to provide a custom style" do
145
- box = nil
146
- @composer.define_singleton_method(:draw_box) {|arg| box = arg }
184
+ it "allows setting a custom base style for all parts" do
185
+ @composer.formatted_text(["Test", "other"], font_size: 20)
186
+ items = @box.instance_variable_get(:@items)
187
+ assert_equal(20, @box.style.font_size)
188
+ assert_equal(20, items[0].style.font_size)
189
+ assert_equal(20, items[1].style.font_size)
190
+ end
191
+
192
+ it "allows using custom style properties for a single part" do
193
+ @composer.formatted_text([{text: "Test", font_size: 20}, "test"], align: :center)
194
+ items = @box.instance_variable_get(:@items)
195
+ assert_equal(10, @box.style.font_size)
147
196
 
148
- @composer.formatted_text([{text: "Test", style: HexaPDF::Layout::Style.new(fill_color: 128),
149
- font_size: 20}], align: :center)
150
- items = box.instance_variable_get(:@items)
151
- assert_equal(20, items.first.style.font_size)
152
- assert_equal(128, items.first.style.fill_color)
153
- assert_equal(:center, items.first.style.align)
197
+ assert_equal(20, items[0].style.font_size)
198
+ assert_equal(:center, items[0].style.align)
199
+
200
+ assert_equal(10, items[1].style.font_size)
201
+ assert_equal(:center, items[1].style.align)
154
202
  end
155
203
 
156
- it "a hash can be used to link to an URL" do
157
- box = nil
158
- @composer.define_singleton_method(:draw_box) {|arg| box = arg }
204
+ it "allows using a custom style as basis for a single part" do
205
+ @composer.formatted_text([{text: "Test", style: {font_size: 20}, subscript: true}, "test"],
206
+ align: :center)
207
+ items = @box.instance_variable_get(:@items)
208
+ assert_equal(10, @box.style.font_size)
159
209
 
160
- @composer.formatted_text([{text: "Test", link: "URI"}, {link: "URI"}])
161
- items = box.instance_variable_get(:@items)
162
- assert_equal(2, items.length)
163
- assert_equal(4, items[0].items.length)
164
- assert_equal(3, items[1].items.length)
210
+ assert_equal(20, items[0].style.font_size)
211
+ assert_equal(:left, items[0].style.align)
212
+ assert(items[0].style.subscript)
213
+
214
+ assert_equal(10, items[1].style.font_size)
215
+ assert_equal(:center, items[1].style.align)
216
+ refute(items[1].style.subscript)
217
+ end
218
+
219
+ it "allows specifying a link to an URL via the :link key" do
220
+ @composer.formatted_text([{text: "Test", link: "URI"}, {link: "URI"}, "test"])
221
+ items = @box.instance_variable_get(:@items)
222
+ assert_equal(3, items.length)
223
+ assert_equal(4, items[0].items.length, "text should be Test")
224
+ assert_equal(3, items[1].items.length, "text should be URI")
165
225
  assert_equal([:link, {uri: 'URI'}], items[0].style.overlays.instance_variable_get(:@layers)[0])
226
+ refute(items[2].style.overlays?)
166
227
  end
167
228
  end
168
229
 
@@ -172,9 +233,11 @@ describe HexaPDF::Composer do
172
233
  @composer.define_singleton_method(:draw_box) {|arg| box = arg }
173
234
  image_path = File.join(TEST_DATA_DIR, 'images', 'gray.jpg')
174
235
 
175
- @composer.image(image_path, width: 10, height: 15)
236
+ @composer.image(image_path, width: 10, height: 15, style: {font_size: 20}, subscript: true)
176
237
  assert_equal(10, box.width)
177
238
  assert_equal(15, box.height)
239
+ assert_equal(20, box.style.font_size)
240
+ assert(box.style.subscript)
178
241
  assert_same(@composer.document.images.add(image_path), box.image)
179
242
  end
180
243
  end
@@ -19,8 +19,8 @@ describe HexaPDF::Dictionary do
19
19
  klass.new(obj, oid: 1)
20
20
  end
21
21
 
22
- def delete(_obj)
23
- _obj.data.value = nil
22
+ def delete(obj)
23
+ obj.data.value = nil
24
24
  end
25
25
 
26
26
  def wrap(obj, type:)
@@ -476,16 +476,14 @@ describe HexaPDF::Document do
476
476
 
477
477
  describe "write" do
478
478
  it "writes the document to a file" do
479
- begin
480
- file = Tempfile.new('hexapdf-write')
481
- file.close
482
- @io_doc.write(file.path)
483
- HexaPDF::Document.open(file.path) do |doc|
484
- assert_equal(200, doc.object(2).value)
485
- end
486
- ensure
487
- file.unlink
479
+ file = Tempfile.new('hexapdf-write')
480
+ file.close
481
+ @io_doc.write(file.path)
482
+ HexaPDF::Document.open(file.path) do |doc|
483
+ assert_equal(200, doc.object(2).value)
488
484
  end
485
+ ensure
486
+ file.unlink
489
487
  end
490
488
 
491
489
  it "writes the document to an IO object" do
@@ -666,4 +664,8 @@ describe HexaPDF::Document do
666
664
  assert_raises(LocalJumpError) { @doc.cache(:a, :b) }
667
665
  end
668
666
  end
667
+
668
+ it "can be inspected and the output is not too large" do
669
+ assert_match(/HexaPDF::Document:\d+/, @doc.inspect)
670
+ end
669
671
  end
@@ -68,7 +68,7 @@ describe HexaPDF::Object do
68
68
 
69
69
  it "works for arrays" do
70
70
  obj = HexaPDF::PDFArray.new([:b, HexaPDF::Object.new(:a, oid: 3, document: @doc)],
71
- oid: 1, document: @doc)
71
+ oid: 1, document: @doc)
72
72
  assert_equal([:b, :a], HexaPDF::Object.make_direct(obj))
73
73
  end
74
74
  end
@@ -109,7 +109,7 @@ describe HexaPDF::Parser do
109
109
 
110
110
  it "treats indirect objects with invalid values as null objects" do
111
111
  create_parser("1 0 obj <</test ( /other (end)>> endobj")
112
- object, * = @parser.parse_indirect_object
112
+ object, * = @parser.parse_indirect_object
113
113
  assert_nil(object)
114
114
  end
115
115
 
@@ -475,33 +475,33 @@ describe HexaPDF::Parser do
475
475
  describe "invalid numbering of main xref section" do
476
476
  it "handles the xref if the numbering is off by N" do
477
477
  create_parser(" 1 0 obj 1 endobj\n" \
478
- "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
478
+ "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
479
479
  section, _trailer = @parser.parse_xref_section_and_trailer(17)
480
480
  assert_equal(HexaPDF::XRefSection.in_use_entry(1, 0, 1), section[1])
481
481
  end
482
482
 
483
483
  it "fails if the first entry is not the one for oid=0" do
484
484
  create_parser(" 1 0 obj 1 endobj\n" \
485
- "xref\n1 2\n0000000000 00005 f \n0000000001 00000 n \ntrailer\n<<>>\n")
485
+ "xref\n1 2\n0000000000 00005 f \n0000000001 00000 n \ntrailer\n<<>>\n")
486
486
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
487
487
  assert_match(/Main.*invalid numbering/i, exp.message)
488
488
 
489
489
  create_parser(" 1 0 obj 1 endobj\n" \
490
- "xref\n1 2\n0000000001 00000 n \n0000000001 00000 n \ntrailer\n<<>>\n")
490
+ "xref\n1 2\n0000000001 00000 n \n0000000001 00000 n \ntrailer\n<<>>\n")
491
491
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
492
492
  assert_match(/Main.*invalid numbering/i, exp.message)
493
493
  end
494
494
 
495
495
  it "fails if the tested entry position is invalid" do
496
496
  create_parser(" 1 0 obj 1 endobj\n" \
497
- "xref\n1 2\n0000000000 65535 f \n0000000005 00000 n \ntrailer\n<<>>\n")
497
+ "xref\n1 2\n0000000000 65535 f \n0000000005 00000 n \ntrailer\n<<>>\n")
498
498
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
499
499
  assert_match(/Main.*invalid numbering/i, exp.message)
500
500
  end
501
501
 
502
502
  it "fails if the tested entry position's oid doesn't match the corrected entry oid" do
503
503
  create_parser(" 2 0 obj 1 endobj\n" \
504
- "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
504
+ "xref\n1 2\n0000000000 65535 f \n0000000001 00000 n \ntrailer\n<<>>\n")
505
505
  exp = assert_raises(HexaPDF::MalformedPDFError) { @parser.parse_xref_section_and_trailer(17) }
506
506
  assert_match(/Main.*invalid numbering/i, exp.message)
507
507
  end
@@ -580,6 +580,12 @@ describe HexaPDF::Parser do
580
580
  @xref = HexaPDF::XRefSection.in_use_entry(1, 0, 100)
581
581
  end
582
582
 
583
+ it "can tell us if the cross-reference table was reconstructed" do
584
+ create_parser("1 0 obj\n5\nendobj\ntrailer\n<</Size 1>>")
585
+ @parser.load_object(@xref)
586
+ assert(@parser.reconstructed?)
587
+ end
588
+
583
589
  it "serially parses the contents" do
584
590
  create_parser("1 0 obj\n5\nendobj\n1 0 obj\n6\nendobj\ntrailer\n<</Size 1>>")
585
591
  assert_equal(6, @parser.load_object(@xref).value)
@@ -643,7 +649,7 @@ describe HexaPDF::Parser do
643
649
  end
644
650
 
645
651
  it "uses the first trailer in case of a linearized file" do
646
- create_parser("1 0 obj\n<</Linearized true>>\nendobj\ntrailer <</Size 1/Prev 342>>\ntrailer <</Size 2>>")
652
+ create_parser("1 0 obj\n<</Linearized true>>\nendobj\ntrailer <</Size 1/Prev 34>>\ntrailer <</Size 2>>")
647
653
  assert_equal({Size: 1}, @parser.reconstructed_revision.trailer.value)
648
654
  end
649
655
 
@@ -70,16 +70,16 @@ describe HexaPDF::Serializer do
70
70
 
71
71
  it "serializes symbols" do
72
72
  assert_serialized("/Name", :Name)
73
- assert_serialized("/A;Name_With-Various***Chars?", 'A;Name_With-Various***Chars?'.intern)
74
- assert_serialized("/1.2", '1.2'.intern)
75
- assert_serialized("/$$", '$$'.intern)
76
- assert_serialized("/@pattern", '@pattern'.intern)
77
- assert_serialized('/.notdef', '.notdef'.intern)
78
- assert_serialized('/lime#20Green', 'lime Green'.intern)
79
- assert_serialized('/paired#28#29parentheses', 'paired()parentheses'.intern)
80
- assert_serialized('/The_Key_of_F#23_Minor', 'The_Key_of_F#_Minor'.intern)
81
- assert_serialized('/ ', ''.intern)
82
- assert_serialized('/H#c3#b6#c3#9fgang', "Hößgang".intern)
73
+ assert_serialized("/A;Name_With-Various***Chars?", :'A;Name_With-Various***Chars?')
74
+ assert_serialized("/1.2", :'1.2')
75
+ assert_serialized("/$$", :$$)
76
+ assert_serialized("/@pattern", :@pattern)
77
+ assert_serialized('/.notdef', :'.notdef')
78
+ assert_serialized('/lime#20Green', :'lime Green')
79
+ assert_serialized('/paired#28#29parentheses', :'paired()parentheses')
80
+ assert_serialized('/The_Key_of_F#23_Minor', :'The_Key_of_F#_Minor')
81
+ assert_serialized('/ ', :"")
82
+ assert_serialized('/H#c3#b6#c3#9fgang', :Hößgang)
83
83
  assert_serialized('/H#e8lp', "H\xE8lp".force_encoding('BINARY').intern)
84
84
  end
85
85
 
@@ -101,18 +101,16 @@ describe HexaPDF::Serializer do
101
101
  end
102
102
 
103
103
  it "serializes time like objects" do
104
- begin
105
- tz = ENV['TZ']
106
- ENV['TZ'] = 'Europe/Vienna'
107
- assert_serialized("(D:20150416094100)", Time.new(2015, 04, 16, 9, 41, 0, 0))
108
- assert_serialized("(D:20150416094100+01'00')", Time.new(2015, 04, 16, 9, 41, 0, 3600))
109
- assert_serialized("(D:20150416094100-01'20')", Time.new(2015, 04, 16, 9, 41, 0, -4800))
110
- assert_serialized("(D:20150416000000+02'00')", Date.parse("2015-04-16 9:41:00 +02:00"))
111
- assert_serialized("(D:20150416094100+02'00')",
112
- Time.parse("2015-04-16 9:41:00 +02:00").to_datetime)
113
- ensure
114
- ENV['TZ'] = tz
115
- end
104
+ tz = ENV['TZ']
105
+ ENV['TZ'] = 'Europe/Vienna'
106
+ assert_serialized("(D:20150416094100)", Time.new(2015, 04, 16, 9, 41, 0, 0))
107
+ assert_serialized("(D:20150416094100+01'00')", Time.new(2015, 04, 16, 9, 41, 0, 3600))
108
+ assert_serialized("(D:20150416094100-01'20')", Time.new(2015, 04, 16, 9, 41, 0, -4800))
109
+ assert_serialized("(D:20150416000000+02'00')", Date.parse("2015-04-16 9:41:00 +02:00"))
110
+ assert_serialized("(D:20150416094100+02'00')",
111
+ Time.parse("2015-04-16 9:41:00 +02:00").to_datetime)
112
+ ensure
113
+ ENV['TZ'] = tz
116
114
  end
117
115
 
118
116
  it "serializes HexaPDF objects" do
@@ -47,15 +47,13 @@ describe HexaPDF::StreamData do
47
47
  end
48
48
 
49
49
  it "returns a fiber for a string representing a file name" do
50
- begin
51
- file = Tempfile.new('hexapdf-stream')
52
- file.write('source')
53
- file.close
54
- s = HexaPDF::StreamData.new(file.path)
55
- assert_equal('source', s.fiber.resume)
56
- ensure
57
- file.unlink
58
- end
50
+ file = Tempfile.new('hexapdf-stream')
51
+ file.write('source')
52
+ file.close
53
+ s = HexaPDF::StreamData.new(file.path)
54
+ assert_equal('source', s.fiber.resume)
55
+ ensure
56
+ file.unlink
59
57
  end
60
58
  end
61
59
 
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.20.2)>>
43
+ <</Producer(HexaPDF version 0.21.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.20.2)>>
75
+ <</Producer(HexaPDF version 0.21.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -123,6 +123,17 @@ describe HexaPDF::Writer do
123
123
  HexaPDF::Writer.write(doc, output_io, incremental: true)
124
124
  refute_match(/^trailer/, output_io.string)
125
125
  end
126
+
127
+ it "raises an error if the used encryption was changed" do
128
+ io = StringIO.new
129
+ doc = HexaPDF::Document.new
130
+ doc.encrypt
131
+ doc.write(io)
132
+
133
+ doc = HexaPDF::Document.new(io: io)
134
+ doc.encrypt(owner_password: 'test')
135
+ assert_raises(HexaPDF::Error) { doc.write('notused', incremental: true) }
136
+ end
126
137
  end
127
138
 
128
139
  it "creates an xref stream if no xref stream is in a revision but object streams are" do
@@ -602,8 +602,7 @@ describe HexaPDF::Type::AcroForm::AppearanceGenerator do
602
602
  [:end_text],
603
603
  [:restore_graphics_state],
604
604
  [:restore_graphics_state],
605
- [:end_marked_content]],
606
- )
605
+ [:end_marked_content]])
607
606
  end
608
607
  end
609
608
 
@@ -120,7 +120,7 @@ describe HexaPDF::Type::AcroForm::ChoiceField do
120
120
 
121
121
  describe "option items" do
122
122
  before do
123
- @items = [["a", "Zx"], "\xFE\xFF".b << "Töne".encode('UTF-16BE').b, "H\xe4llo".b,]
123
+ @items = [["a", "Zx"], "\xFE\xFF".b << "Töne".encode('UTF-16BE').b, "H\xe4llo".b]
124
124
  end
125
125
 
126
126
  it "sets the option items" do
@@ -9,7 +9,7 @@ module TestHelper
9
9
  end
10
10
 
11
11
  def ca_certificate
12
- @ca_cert ||=
12
+ @ca_certificate ||=
13
13
  begin
14
14
  ca_name = OpenSSL::X509::Name.parse('/C=AT/O=HexaPDF/CN=HexaPDF Test Root CA')
15
15
 
@@ -77,7 +77,13 @@ describe HexaPDF::Type::Annotation do
77
77
  stream[:BBox] = [1, 2, 3, 4]
78
78
  appearance = @annot.appearance
79
79
  assert_same(stream.data, appearance.data)
80
- assert_equal(:Form, appearance[:Subtype])
80
+ assert_kind_of(HexaPDF::Type::Form, appearance)
81
+
82
+ stream[:Type] = :XObject
83
+ stream[:Subtype] = :Form
84
+ appearance = @annot.appearance
85
+ assert_same(stream.data, appearance.data)
86
+ assert_kind_of(HexaPDF::Type::Form, appearance)
81
87
 
82
88
  @annot[:AP][:N] = {X: {}}
83
89
  assert_nil(@annot.appearance)
@@ -112,7 +112,7 @@ describe HexaPDF::Type::FontType0 do
112
112
  end
113
113
 
114
114
  it "calls the configured proc if no mapping is available" do
115
- @font[:Encoding] = :"Identity-H"
115
+ @font[:Encoding] = :'Identity-H'
116
116
  @cid_font[:CIDSystemInfo][:Registry] = :Unknown
117
117
  assert_raises(HexaPDF::Error) { @font.to_utf8(32) }
118
118
  end
@@ -10,24 +10,24 @@ describe HexaPDF::Type::FontType1::StandardFonts do
10
10
  end
11
11
 
12
12
  it "checks whether a given name corresponds to a standard font via #standard_font?" do
13
- assert(@obj.standard_font?(:"Times-Roman"))
13
+ assert(@obj.standard_font?(:'Times-Roman'))
14
14
  assert(@obj.standard_font?(:TimesNewRoman))
15
15
  refute(@obj.standard_font?(:LibreSans))
16
16
  end
17
17
 
18
18
  it "returns the standard PDF name for an alias via #standard_name" do
19
- assert_equal(:"Times-Roman", @obj.standard_name(:TimesNewRoman))
19
+ assert_equal(:'Times-Roman', @obj.standard_name(:TimesNewRoman))
20
20
  end
21
21
 
22
22
  describe "font" do
23
23
  it "returns the Type1 font object for a given standard name" do
24
- font = @obj.font(:"Times-Roman")
24
+ font = @obj.font(:'Times-Roman')
25
25
  assert_equal("Times Roman", font.full_name)
26
26
  end
27
27
 
28
28
  it "caches the font for reuse" do
29
- font = @obj.font(:"Times-Roman")
30
- assert_same(font, @obj.font(:"Times-Roman"))
29
+ font = @obj.font(:'Times-Roman')
30
+ assert_same(font, @obj.font(:'Times-Roman'))
31
31
  end
32
32
 
33
33
  it "returns nil if the given name doesn't belong to a standard font" do
@@ -40,7 +40,7 @@ describe HexaPDF::Type::FontType1 do
40
40
  before do
41
41
  @doc = HexaPDF::Document.new
42
42
  @font = @doc.add({Type: :Font, Subtype: :Type1, Encoding: :WinAnsiEncoding,
43
- BaseFont: :"Times-Roman"})
43
+ BaseFont: :'Times-Roman'})
44
44
 
45
45
  font_file = @doc.add({}, stream: <<-EOF)
46
46
  /Encoding 256 array
@@ -95,7 +95,7 @@ describe HexaPDF::Type::FontType1 do
95
95
 
96
96
  describe "bounding_box" do
97
97
  it "returns the bounding box for a standard font" do
98
- font = HexaPDF::Type::FontType1::StandardFonts.font(:"Times-Roman")
98
+ font = HexaPDF::Type::FontType1::StandardFonts.font(:'Times-Roman')
99
99
  assert_equal(font.bounding_box, @font.bounding_box)
100
100
  end
101
101
 
@@ -165,26 +165,22 @@ describe HexaPDF::Type::Image do
165
165
  end
166
166
 
167
167
  it "writes JPEG images to a file with .jpg extension" do
168
- begin
169
- file = Tempfile.new(['hexapdf-image-write-test', '.jpg'])
170
- image = @doc.images.add(@jpg)
171
- image.write(file.path)
172
- assert_equal(File.binread(@jpg), File.binread(file.path))
173
- ensure
174
- file.unlink
175
- end
168
+ file = Tempfile.new(['hexapdf-image-write-test', '.jpg'])
169
+ image = @doc.images.add(@jpg)
170
+ image.write(file.path)
171
+ assert_equal(File.binread(@jpg), File.binread(file.path))
172
+ ensure
173
+ file.unlink
176
174
  end
177
175
 
178
176
  it "writes JPEG2000 images to a file with .jpx extension" do
179
- begin
180
- file = Tempfile.new(['hexapdf-image-write-test', '.jpx'])
181
- image = @doc.images.add(@jpg)
182
- image.set_filter(:JPXDecode) # fake it
183
- image.write(file.path)
184
- assert_equal(File.binread(@jpg), File.binread(file.path))
185
- ensure
186
- file.unlink
187
- end
177
+ file = Tempfile.new(['hexapdf-image-write-test', '.jpx'])
178
+ image = @doc.images.add(@jpg)
179
+ image.set_filter(:JPXDecode) # fake it
180
+ image.write(file.path)
181
+ assert_equal(File.binread(@jpg), File.binread(file.path))
182
+ ensure
183
+ file.unlink
188
184
  end
189
185
 
190
186
  Dir.glob(File.join(TEST_DATA_DIR, 'images', '*.png')).each do |png_file|
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.20.2
4
+ version: 0.21.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: 2022-01-17 00:00:00.000000000 Z
11
+ date: 2022-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse