hexapdf 0.20.3 → 0.21.1

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -1
  3. data/README.md +5 -3
  4. data/Rakefile +10 -1
  5. data/examples/018-composer.rb +10 -10
  6. data/examples/020-column_box.rb +57 -0
  7. data/lib/hexapdf/cli/batch.rb +4 -6
  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/aes.rb +9 -5
  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/column_box.rb +168 -0
  30. data/lib/hexapdf/layout/image_box.rb +1 -1
  31. data/lib/hexapdf/layout/style.rb +28 -8
  32. data/lib/hexapdf/layout/text_fragment.rb +10 -9
  33. data/lib/hexapdf/parser.rb +5 -0
  34. data/lib/hexapdf/tokenizer.rb +3 -3
  35. data/lib/hexapdf/type/acro_form/appearance_generator.rb +6 -4
  36. data/lib/hexapdf/type/acro_form/choice_field.rb +2 -2
  37. data/lib/hexapdf/type/acro_form/field.rb +2 -2
  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/xref_section.rb +1 -1
  46. data/test/hexapdf/common_tokenizer_tests.rb +5 -5
  47. data/test/hexapdf/content/test_graphics_state.rb +1 -0
  48. data/test/hexapdf/content/test_operator.rb +2 -2
  49. data/test/hexapdf/content/test_processor.rb +1 -1
  50. data/test/hexapdf/encryption/test_aes.rb +8 -0
  51. data/test/hexapdf/encryption/test_standard_security_handler.rb +23 -29
  52. data/test/hexapdf/filter/test_predictor.rb +16 -20
  53. data/test/hexapdf/font/test_type1_wrapper.rb +5 -1
  54. data/test/hexapdf/font/true_type/table/common.rb +1 -1
  55. data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
  56. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +1 -1
  57. data/test/hexapdf/image_loader/test_pdf.rb +6 -8
  58. data/test/hexapdf/image_loader/test_png.rb +2 -2
  59. data/test/hexapdf/layout/test_box.rb +11 -1
  60. data/test/hexapdf/layout/test_style.rb +23 -0
  61. data/test/hexapdf/layout/test_text_fragment.rb +21 -21
  62. data/test/hexapdf/test_composer.rb +115 -52
  63. data/test/hexapdf/test_dictionary.rb +2 -2
  64. data/test/hexapdf/test_document.rb +11 -9
  65. data/test/hexapdf/test_object.rb +1 -1
  66. data/test/hexapdf/test_parser.rb +13 -7
  67. data/test/hexapdf/test_serializer.rb +20 -22
  68. data/test/hexapdf/test_stream.rb +7 -9
  69. data/test/hexapdf/test_writer.rb +2 -2
  70. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +1 -2
  71. data/test/hexapdf/type/acro_form/test_choice_field.rb +1 -1
  72. data/test/hexapdf/type/signature/common.rb +1 -1
  73. data/test/hexapdf/type/test_font_type0.rb +1 -1
  74. data/test/hexapdf/type/test_font_type1.rb +7 -7
  75. data/test/hexapdf/type/test_image.rb +13 -17
  76. metadata +4 -2
@@ -604,6 +604,26 @@ describe HexaPDF::Layout::Style do
604
604
  end
605
605
  end
606
606
 
607
+ describe "self.create" do
608
+ it "returns the provided style argument" do
609
+ assert_same(@style, HexaPDF::Layout::Style.create(@style))
610
+ end
611
+
612
+ it "creates a new Style object based on the passed hash" do
613
+ style = HexaPDF::Layout::Style.create(font_size: 10, fill_color: 'green')
614
+ assert_equal(10, style.font_size)
615
+ assert_equal('green', style.fill_color)
616
+ end
617
+
618
+ it "creates an empty Style object if nil is passed" do
619
+ assert_kind_of(HexaPDF::Layout::Style, HexaPDF::Layout::Style.create(nil))
620
+ end
621
+
622
+ it "raises an error if an invalid object is provided" do
623
+ assert_raises(ArgumentError) { HexaPDF::Layout::Style.create(5) }
624
+ end
625
+ end
626
+
607
627
  it "can assign values on initialization" do
608
628
  style = HexaPDF::Layout::Style.new(font_size: 10)
609
629
  assert_equal(10, style.font_size)
@@ -692,6 +712,9 @@ describe HexaPDF::Layout::Style do
692
712
 
693
713
  @style.stroke_dash_pattern(5, 2)
694
714
  assert_equal([[5], 2], @style.stroke_dash_pattern.to_operands)
715
+
716
+ @style.line_spacing(1.2)
717
+ assert_equal([:proportional, 1.2], [@style.line_spacing.type, @style.line_spacing.value])
695
718
  end
696
719
 
697
720
  it "allows checking for valid values" do
@@ -20,7 +20,7 @@ describe HexaPDF::Layout::TextFragment do
20
20
  end
21
21
 
22
22
  describe "create" do
23
- it "creates a TextFragment from text and options" do
23
+ it "creates a TextFragment from text and style" do
24
24
  frag = HexaPDF::Layout::TextFragment.create("Tom", font: @font, font_size: 20,
25
25
  font_features: {kern: true})
26
26
  assert_equal(4, frag.items.length)
@@ -180,16 +180,16 @@ describe HexaPDF::Layout::TextFragment do
180
180
  [:set_line_width, [5]],
181
181
  [:set_line_cap_style, [1]],
182
182
  [:set_line_dash_pattern, [[5], 0]]],
183
- back: [[:end_text],
184
- [:save_graphics_state],
185
- [:set_device_gray_stroking_color, [0]],
186
- [:set_line_width, [@fragment.style.calculated_underline_thickness]],
187
- [:set_line_cap_style, [0]],
188
- [:set_line_dash_pattern, [[], 0]],
189
- [:move_to, [10, 15]],
190
- [:line_to, [40.88, 15]],
191
- [:stroke_path],
192
- [:restore_graphics_state]])
183
+ back: [[:end_text],
184
+ [:save_graphics_state],
185
+ [:set_device_gray_stroking_color, [0]],
186
+ [:set_line_width, [@fragment.style.calculated_underline_thickness]],
187
+ [:set_line_cap_style, [0]],
188
+ [:set_line_dash_pattern, [[], 0]],
189
+ [:move_to, [10, 15]],
190
+ [:line_to, [40.88, 15]],
191
+ [:stroke_path],
192
+ [:restore_graphics_state]])
193
193
  end
194
194
 
195
195
  it "draws the strikeout line" do
@@ -201,16 +201,16 @@ describe HexaPDF::Layout::TextFragment do
201
201
  [:set_line_width, [5]],
202
202
  [:set_line_cap_style, [1]],
203
203
  [:set_line_dash_pattern, [[5], 0]]],
204
- back: [[:end_text],
205
- [:save_graphics_state],
206
- [:set_device_gray_stroking_color, [0]],
207
- [:set_line_width, [@fragment.style.calculated_strikeout_thickness]],
208
- [:set_line_cap_style, [0]],
209
- [:set_line_dash_pattern, [[], 0]],
210
- [:move_to, [10, 21.01]],
211
- [:line_to, [40.88, 21.01]],
212
- [:stroke_path],
213
- [:restore_graphics_state]])
204
+ back: [[:end_text],
205
+ [:save_graphics_state],
206
+ [:set_device_gray_stroking_color, [0]],
207
+ [:set_line_width, [@fragment.style.calculated_strikeout_thickness]],
208
+ [:set_line_cap_style, [0]],
209
+ [:set_line_dash_pattern, [[], 0]],
210
+ [:move_to, [10, 21.01]],
211
+ [:line_to, [40.88, 21.01]],
212
+ [:stroke_path],
213
+ [:restore_graphics_state]])
214
214
  end
215
215
  end
216
216
 
@@ -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.3)>>
43
+ <</Producer(HexaPDF version 0.21.1)>>
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.3)>>
75
+ <</Producer(HexaPDF version 0.21.1)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -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
 
@@ -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