hexapdf 0.29.0 → 0.31.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/lib/hexapdf/cli/command.rb +16 -1
  4. data/lib/hexapdf/cli/info.rb +9 -1
  5. data/lib/hexapdf/cli/inspect.rb +2 -2
  6. data/lib/hexapdf/composer.rb +76 -28
  7. data/lib/hexapdf/configuration.rb +17 -4
  8. data/lib/hexapdf/dictionary_fields.rb +7 -2
  9. data/lib/hexapdf/document/pages.rb +31 -18
  10. data/lib/hexapdf/document.rb +8 -1
  11. data/lib/hexapdf/encryption/standard_security_handler.rb +2 -2
  12. data/lib/hexapdf/filter/flate_decode.rb +20 -8
  13. data/lib/hexapdf/layout/page_style.rb +144 -0
  14. data/lib/hexapdf/layout.rb +1 -0
  15. data/lib/hexapdf/task/optimize.rb +8 -6
  16. data/lib/hexapdf/type/font_simple.rb +14 -2
  17. data/lib/hexapdf/type/object_stream.rb +7 -2
  18. data/lib/hexapdf/type/outline.rb +1 -1
  19. data/lib/hexapdf/type/outline_item.rb +1 -1
  20. data/lib/hexapdf/type/page.rb +29 -8
  21. data/lib/hexapdf/type/xref_stream.rb +11 -4
  22. data/lib/hexapdf/version.rb +1 -1
  23. data/lib/hexapdf/writer.rb +1 -1
  24. data/test/hexapdf/document/test_pages.rb +25 -0
  25. data/test/hexapdf/encryption/test_standard_security_handler.rb +2 -2
  26. data/test/hexapdf/filter/test_flate_decode.rb +19 -5
  27. data/test/hexapdf/layout/test_page_style.rb +70 -0
  28. data/test/hexapdf/task/test_optimize.rb +11 -9
  29. data/test/hexapdf/test_composer.rb +35 -10
  30. data/test/hexapdf/test_dictionary_fields.rb +9 -4
  31. data/test/hexapdf/test_writer.rb +8 -8
  32. data/test/hexapdf/type/test_font_simple.rb +18 -6
  33. data/test/hexapdf/type/test_object_stream.rb +16 -7
  34. data/test/hexapdf/type/test_outline.rb +3 -1
  35. data/test/hexapdf/type/test_outline_item.rb +3 -1
  36. data/test/hexapdf/type/test_page.rb +42 -11
  37. data/test/hexapdf/type/test_xref_stream.rb +6 -1
  38. metadata +4 -2
@@ -38,6 +38,8 @@ require 'set'
38
38
  require 'hexapdf/serializer'
39
39
  require 'hexapdf/content/parser'
40
40
  require 'hexapdf/content/operator'
41
+ require 'hexapdf/type/xref_stream'
42
+ require 'hexapdf/type/object_stream'
41
43
 
42
44
  module HexaPDF
43
45
  module Task
@@ -124,7 +126,7 @@ module HexaPDF
124
126
  if object_streams == :generate
125
127
  process_object_streams(doc, :generate, xref_streams)
126
128
  elsif xref_streams == :generate
127
- doc.add({Type: :XRef})
129
+ doc.add({}, type: Type::XRefStream)
128
130
  end
129
131
  end
130
132
 
@@ -150,14 +152,14 @@ module HexaPDF
150
152
  end
151
153
  objects_to_delete.each {|obj| rev.delete(obj) }
152
154
  if xref_streams == :generate && !xref_stream
153
- rev.add(doc.wrap({Type: :XRef}, oid: doc.revisions.next_oid))
155
+ rev.add(doc.wrap({}, type: Type::XRefStream, oid: doc.revisions.next_oid))
154
156
  end
155
157
  end
156
158
  when :generate
157
159
  doc.revisions.each do |rev|
158
160
  xref_stream = false
159
161
  count = 0
160
- objstms = [doc.wrap({Type: :ObjStm})]
162
+ objstms = [doc.wrap({}, type: Type::ObjectStream)]
161
163
  old_objstms = []
162
164
  rev.each do |obj|
163
165
  case obj.type
@@ -173,7 +175,7 @@ module HexaPDF
173
175
  objstms[-1].add_object(obj)
174
176
  count += 1
175
177
  if count == 200
176
- objstms << doc.wrap({Type: :ObjStm})
178
+ objstms << doc.wrap({}, type: Type::ObjectStream)
177
179
  count = 0
178
180
  end
179
181
  end
@@ -182,7 +184,7 @@ module HexaPDF
182
184
  objstm.data.oid = doc.revisions.next_oid
183
185
  rev.add(objstm)
184
186
  end
185
- rev.add(doc.wrap({Type: :XRef}, oid: doc.revisions.next_oid)) unless xref_stream
187
+ rev.add(doc.wrap({}, type: Type::XRefStream, oid: doc.revisions.next_oid)) unless xref_stream
186
188
  end
187
189
  end
188
190
  end
@@ -207,7 +209,7 @@ module HexaPDF
207
209
  xref_stream = true if obj.type == :XRef
208
210
  delete_fields_with_defaults(obj)
209
211
  end
210
- rev.add(doc.wrap({Type: :XRef}, oid: doc.revisions.next_oid)) unless xref_stream
212
+ rev.add(doc.wrap({}, type: Type::XRefStream, oid: doc.revisions.next_oid)) unless xref_stream
211
213
  end
212
214
  end
213
215
  end
@@ -171,9 +171,21 @@ module HexaPDF
171
171
  yield("Required field #{field} is not set", false) if self[field].nil?
172
172
  end
173
173
 
174
+ widths = self[:Widths]
174
175
  if key?(:Widths) && key?(:LastChar) && key?(:FirstChar) &&
175
- self[:Widths].length != (self[:LastChar] - self[:FirstChar] + 1)
176
- yield("Invalid number of entries in field Widths", false)
176
+ widths.length != (self[:LastChar] - self[:FirstChar] + 1)
177
+ yield("Invalid number of entries in field Widths", true)
178
+ difference = self[:LastChar] - self[:FirstChar] + 1 - widths.length
179
+ if difference > 0
180
+ missing_value = if widths.count(widths[0]) == widths.length
181
+ widths[0]
182
+ else
183
+ self[:FontDescriptor]&.[](:MissingWidth) || 0
184
+ end
185
+ difference.times { widths << missing_value }
186
+ else
187
+ widths.slice!(difference, -difference)
188
+ end
177
189
  end
178
190
  end
179
191
 
@@ -101,8 +101,8 @@ module HexaPDF
101
101
  define_type :ObjStm
102
102
 
103
103
  define_field :Type, type: Symbol, required: true, default: type, version: '1.5'
104
- define_field :N, type: Integer # not required, will be auto-filled on #write_objects
105
- define_field :First, type: Integer # not required, will be auto-filled on #write_objects
104
+ define_field :N, type: Integer, required: true
105
+ define_field :First, type: Integer, required: true
106
106
  define_field :Extends, type: Stream
107
107
 
108
108
  # Parses the stream and returns an ObjectStream::Data object that can be used for retrieving
@@ -230,6 +230,11 @@ module HexaPDF
230
230
 
231
231
  # Validates that the generation number of the object stream is zero.
232
232
  def perform_validation
233
+ # Assign dummy values so that the validation for required values works since those values
234
+ # are only set on #write_objects
235
+ self[:N] ||= 0
236
+ self[:First] ||= 0
237
+
233
238
  super
234
239
  yield("Object stream has invalid generation number > 0", false) if gen != 0
235
240
  end
@@ -126,7 +126,7 @@ module HexaPDF
126
126
  if (first && !last) || (!first && last)
127
127
  yield('Outline dictionary is missing an endpoint reference', true)
128
128
  node, dir = first ? [first, :Next] : [last, :Prev]
129
- node = node[dir] while node.key?(dir)
129
+ node = node[dir] while node[dir]
130
130
  self[dir == :Next ? :Last : :First] = node
131
131
  elsif !first && !last && self[:Count] && self[:Count] != 0
132
132
  yield('Outline dictionary key /Count set but no items exist', true)
@@ -397,7 +397,7 @@ module HexaPDF
397
397
  if (first && !last) || (!first && last)
398
398
  yield('Outline item dictionary is missing an endpoint reference', true)
399
399
  node, dir = first ? [first, :Next] : [last, :Prev]
400
- node = node[dir] while node.key?(dir)
400
+ node = node[dir] while node[dir]
401
401
  self[dir == :Next ? :Last : :First] = node
402
402
  elsif !first && !last && self[:Count] && self[:Count] != 0
403
403
  yield('Outline item dictionary key /Count set but no descendants exist', true)
@@ -104,8 +104,16 @@ module HexaPDF
104
104
  Executive: [0, 0, 522, 756].freeze,
105
105
  }.freeze
106
106
 
107
- # Returns the media box for the given paper size. See PAPER_SIZE for the defined paper sizes.
107
+ # Returns the media box for the given paper size or array.
108
+ #
109
+ # If an array is specified, it needs to contain exactly four numbers. The +orientation+
110
+ # argument is not used in this case.
111
+ #
112
+ # See PAPER_SIZE for the defined paper sizes.
108
113
  def self.media_box(paper_size, orientation: :portrait)
114
+ return paper_size if paper_size.kind_of?(Array) && paper_size.size == 4 &&
115
+ paper_size.all?(Numeric)
116
+
109
117
  unless PAPER_SIZE.key?(paper_size)
110
118
  raise HexaPDF::Error, "Invalid paper size specified: #{paper_size}"
111
119
  end
@@ -118,9 +126,6 @@ module HexaPDF
118
126
  # The inheritable fields.
119
127
  INHERITABLE_FIELDS = [:Resources, :MediaBox, :CropBox, :Rotate].freeze
120
128
 
121
- # The required inheritable fields.
122
- REQUIRED_INHERITABLE_FIELDS = [:Resources, :MediaBox].freeze
123
-
124
129
  define_type :Page
125
130
 
126
131
  define_field :Type, type: Symbol, required: true, default: type
@@ -609,10 +614,26 @@ module HexaPDF
609
614
  return unless parent_node
610
615
 
611
616
  super
612
- REQUIRED_INHERITABLE_FIELDS.each do |name|
613
- next if self[name]
614
- yield("Inheritable page field #{name} not set", name == :Resources)
615
- resources.validate(&block) if name == :Ressources
617
+
618
+ unless self[:Resources]
619
+ yield("Required inheritable page field Resources not set", true)
620
+ resources.validate(&block)
621
+ end
622
+
623
+ unless self[:MediaBox]
624
+ yield("Required inheritable page field MediaBox not set", true)
625
+ index = self.index
626
+ box_before = index == 0 ? nil : document.pages[index - 1][:MediaBox]
627
+ box_after = index == document.pages.count - 1 ? nil : document.pages[index + 1]&.[](:MediaBox)
628
+ self[:MediaBox] =
629
+ if box_before && (box_before&.value == box_after&.value || box_after.nil?)
630
+ box_before.dup
631
+ elsif box_after && box_before.nil?
632
+ box_after
633
+ else
634
+ self.class.media_box(document.config['page.default_media_box'],
635
+ orientation: document.config['page.default_media_orientation'])
636
+ end
616
637
  end
617
638
  end
618
639
 
@@ -72,12 +72,10 @@ module HexaPDF
72
72
 
73
73
  define_field :Type, type: Symbol, default: type, required: true, indirect: false,
74
74
  version: '1.5'
75
- # Size is not required because it will be auto-filled before the object is written
76
- define_field :Size, type: Integer, indirect: false
75
+ define_field :Size, type: Integer, indirect: false, required: true
77
76
  define_field :Index, type: PDFArray, indirect: false
78
77
  define_field :Prev, type: Integer, indirect: false
79
- # W is not required because it will be auto-filled on #update_with_xref_section_and_trailer
80
- define_field :W, type: PDFArray, indirect: false
78
+ define_field :W, type: PDFArray, indirect: false, required: true
81
79
 
82
80
  # Returns an XRefSection that represents the content of this cross-reference stream.
83
81
  #
@@ -219,6 +217,15 @@ module HexaPDF
219
217
  [[1, middle, 2], pack_string]
220
218
  end
221
219
 
220
+ def perform_validation #:nodoc
221
+ # Size is not required because it will be auto-filled before the object is written
222
+ # W is not required because it will be auto-filled on #update_with_xref_section_and_trailer
223
+ # Set both here to dummy values to make validation work for the required values
224
+ self[:Size] ||= 1
225
+ self[:W] ||= [1, 1, 1]
226
+ super
227
+ end
228
+
222
229
  end
223
230
 
224
231
  end
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.29.0'
40
+ VERSION = '0.31.0'
41
41
 
42
42
  end
@@ -207,7 +207,7 @@ module HexaPDF
207
207
  end
208
208
 
209
209
  if (!object_streams.empty? || @use_xref_streams) && xref_stream.nil?
210
- xref_stream = @document.wrap({Type: :XRef}, oid: @document.revisions.next_oid)
210
+ xref_stream = @document.wrap({}, type: Type::XRefStream, oid: @document.revisions.next_oid)
211
211
  rev.add(xref_stream)
212
212
  end
213
213
 
@@ -14,6 +14,31 @@ describe HexaPDF::Document::Pages do
14
14
  end
15
15
  end
16
16
 
17
+ describe "create" do
18
+ it "uses the defaults from the configuration for missing arguments" do
19
+ page = @doc.pages.create
20
+ assert_equal([0, 0, 595, 842], page.box(:media).value)
21
+ end
22
+
23
+ it "allows specifying a reference to a predefined page size" do
24
+ page = @doc.pages.create(media_box: :A3)
25
+ assert_equal([0, 0, 842, 1191], page.box(:media).value)
26
+ end
27
+
28
+ it "allows specifying the orientation for a predefined page size" do
29
+ page = @doc.pages.create(media_box: :A4, orientation: :landscape)
30
+ assert_equal([0, 0, 842, 595], page.box(:media).value)
31
+
32
+ page = @doc.pages.create(orientation: :landscape)
33
+ assert_equal([0, 0, 842, 595], page.box(:media).value)
34
+ end
35
+
36
+ it "allows using a media box array" do
37
+ page = @doc.pages.create(media_box: [0, 0, 12, 24], orientation: :landscape)
38
+ assert_equal([0, 0, 12, 24], page.box(:media).value)
39
+ end
40
+ end
41
+
17
42
  describe "add" do
18
43
  it "adds a new empty page when no page is given" do
19
44
  page = @doc.pages.add
@@ -213,14 +213,14 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
213
213
  exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
214
214
  @handler.set_up_decryption({Filter: :NonStandard, V: 2})
215
215
  end
216
- assert_match(/Invalid \/Filter/i, exp.message)
216
+ assert_match(/Invalid \/Filter value NonStandard/i, exp.message)
217
217
  end
218
218
 
219
219
  it "fails if the /R value is incorrect" do
220
220
  exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
221
221
  @handler.set_up_decryption({Filter: :Standard, V: 2, R: 5})
222
222
  end
223
- assert_match(/Invalid \/R/i, exp.message)
223
+ assert_match(/Invalid \/R value 5/i, exp.message)
224
224
  end
225
225
 
226
226
  it "fails if the supplied password is invalid" do
@@ -26,12 +26,26 @@ describe HexaPDF::Filter::FlateDecode do
26
26
  assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded_predictor), @predictor_opts)))
27
27
  end
28
28
 
29
- it "fails on invalid input" do
30
- assert_raises(HexaPDF::FilterError) do
31
- collector(@obj.decoder(feeder(@encoded[0..-2], @encoded.length - 3)))
29
+ describe "invalid input is handled as good as possible" do
30
+ def strict_mode
31
+ HexaPDF::GlobalConfiguration['filter.flate.on_error'] = proc { true }
32
+ yield
33
+ ensure
34
+ HexaPDF::GlobalConfiguration['filter.flate.on_error'] = proc { false }
32
35
  end
33
- assert_raises(HexaPDF::FilterError) do
34
- collector(@obj.decoder(feeder("some data")))
36
+
37
+ it "handles completely invalid data" do
38
+ assert_equal('', collector(@obj.decoder(feeder("some data"))))
39
+ assert_raises(HexaPDF::FilterError) do
40
+ strict_mode { collector(@obj.decoder(feeder("some data"))) }
41
+ end
42
+ end
43
+
44
+ it "handles missing data" do
45
+ assert_equal('abcdefg', collector(@obj.decoder(feeder(@encoded[0..-2]))))
46
+ assert_raises(HexaPDF::FilterError) do
47
+ strict_mode { collector(@obj.decoder(feeder(@encoded[0..-2]))) }
48
+ end
35
49
  end
36
50
  end
37
51
  end
@@ -0,0 +1,70 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/layout/page_style'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Layout::PageStyle do
8
+ it "allows assigning the page size, orientation and template on initialization" do
9
+ block = lambda {}
10
+ style = HexaPDF::Layout::PageStyle.new(page_size: :A3, orientation: :landscape, &block)
11
+ assert_equal(:A3, style.page_size)
12
+ assert_equal(:landscape, style.orientation)
13
+ assert_same(block, style.template)
14
+ end
15
+
16
+ it "uses defaults for all values" do
17
+ style = HexaPDF::Layout::PageStyle.new
18
+ assert_equal(:A4, style.page_size)
19
+ assert_equal(:portrait, style.orientation)
20
+ assert_nil(style.template)
21
+ assert_nil(style.frame)
22
+ assert_nil(style.next_style)
23
+ end
24
+
25
+ describe "create_page" do
26
+ before do
27
+ @doc = HexaPDF::Document.new
28
+ end
29
+
30
+ it "creates a new page object" do
31
+ style = HexaPDF::Layout::PageStyle.new do |canvas, istyle|
32
+ canvas.rectangle(0, 0, 10, 10).stroke
33
+ istyle.frame = :frame
34
+ istyle.next_style = :other
35
+ end
36
+ page = style.create_page(@doc)
37
+ assert_equal([0, 0, 595, 842], page.box(:media))
38
+ assert_equal("0 0 10 10 re\nS\n", page.contents)
39
+ assert_equal(:frame, style.frame)
40
+ assert_equal(:other, style.next_style)
41
+ assert_equal(0, @doc.pages.count)
42
+ end
43
+
44
+ it "works when no template is set" do
45
+ style = HexaPDF::Layout::PageStyle.new
46
+ page = style.create_page(@doc)
47
+ assert_equal("", page.contents)
48
+ end
49
+
50
+ it "creates a default frame if none is set beforehand or during template execution" do
51
+ style = HexaPDF::Layout::PageStyle.new
52
+ style.create_page(@doc)
53
+ assert_kind_of(HexaPDF::Layout::Frame, style.frame)
54
+ assert_equal(36, style.frame.left)
55
+ assert_equal(36, style.frame.bottom)
56
+ assert_equal(523, style.frame.width)
57
+ assert_equal(770, style.frame.height)
58
+ end
59
+ end
60
+
61
+ it "creates new frame objects given a page and a margin specification" do
62
+ doc = HexaPDF::Document.new
63
+ style = HexaPDF::Layout::PageStyle.new
64
+ frame = style.create_frame(style.create_page(doc), [15, 10])
65
+ assert_equal(10, frame.left)
66
+ assert_equal(15, frame.bottom)
67
+ assert_equal(575, frame.width)
68
+ assert_equal(812, frame.height)
69
+ end
70
+ end
@@ -81,8 +81,10 @@ describe HexaPDF::Task::Optimize do
81
81
  end
82
82
 
83
83
  it "compacts and deletes xref streams" do
84
- @doc.revisions.all[0].add(@doc.wrap({Type: :XRef}, oid: @doc.revisions.next_oid))
85
- @doc.revisions.all[1].add(@doc.wrap({Type: :XRef}, oid: @doc.revisions.next_oid))
84
+ @doc.revisions.all[0].add(@doc.wrap({}, type: HexaPDF::Type::XRefStream,
85
+ oid: @doc.revisions.next_oid))
86
+ @doc.revisions.all[1].add(@doc.wrap({}, type: HexaPDF::Type::XRefStream,
87
+ oid: @doc.revisions.next_oid))
86
88
  @doc.task(:optimize, compact: true, xref_streams: :delete)
87
89
  assert_no_xrefstms
88
90
  assert_default_deleted
@@ -92,8 +94,8 @@ describe HexaPDF::Task::Optimize do
92
94
  describe "object_streams" do
93
95
  def reload_document_with_objstm_from_io
94
96
  io = StringIO.new
95
- objstm = @doc.add({Type: :ObjStm})
96
- @doc.add({Type: :XRef})
97
+ objstm = @doc.add({}, type: HexaPDF::Type::ObjectStream)
98
+ @doc.add({}, type: HexaPDF::Type::XRefStream)
97
99
  objstm.add_object(@doc.add({Type: :Test}))
98
100
  @doc.write(io)
99
101
  io.rewind
@@ -102,7 +104,7 @@ describe HexaPDF::Task::Optimize do
102
104
 
103
105
  it "generates object streams" do
104
106
  210.times { @doc.add(5) }
105
- objstm = @doc.add({Type: :ObjStm})
107
+ objstm = @doc.add({}, type: HexaPDF::Type::ObjectStream)
106
108
  reload_document_with_objstm_from_io
107
109
  @doc.task(:optimize, object_streams: :generate)
108
110
  assert_objstms_generated
@@ -122,8 +124,8 @@ describe HexaPDF::Task::Optimize do
122
124
  end
123
125
 
124
126
  it "deletes object and generates xref streams" do
125
- @doc.add({Type: :ObjStm})
126
- xref = @doc.add({Type: :XRef})
127
+ @doc.add({}, type: HexaPDF::Type::ObjectStream)
128
+ xref = @doc.add({}, type: HexaPDF::Type::XRefStream)
127
129
  @doc.task(:optimize, object_streams: :delete, xref_streams: :generate)
128
130
  assert_no_objstms
129
131
  assert_xrefstms_generated
@@ -140,13 +142,13 @@ describe HexaPDF::Task::Optimize do
140
142
  end
141
143
 
142
144
  it "reuses an xref stream in generatation mode" do
143
- @doc.add({Type: :XRef})
145
+ @doc.add({}, type: HexaPDF::Type::XRefStream)
144
146
  @doc.task(:optimize, xref_streams: :generate)
145
147
  assert_xrefstms_generated
146
148
  end
147
149
 
148
150
  it "deletes xref streams" do
149
- @doc.add({Type: :XRef})
151
+ @doc.add({}, type: HexaPDF::Type::XRefStream)
150
152
  @doc.task(:optimize, xref_streams: :delete)
151
153
  assert_no_xrefstms
152
154
  assert_default_deleted
@@ -39,6 +39,14 @@ describe HexaPDF::Composer do
39
39
  assert_equal(682, composer.frame.height)
40
40
  end
41
41
 
42
+ it "allows skipping the initial page creation" do
43
+ composer = HexaPDF::Composer.new(skip_page_creation: true)
44
+ assert_nil(composer.page)
45
+ assert_nil(composer.canvas)
46
+ assert_nil(composer.frame)
47
+ assert_nil(composer.page_style(:default))
48
+ end
49
+
42
50
  it "yields itself" do
43
51
  yielded = nil
44
52
  composer = HexaPDF::Composer.new {|c| yielded = c }
@@ -56,7 +64,7 @@ describe HexaPDF::Composer do
56
64
  end
57
65
 
58
66
  describe "new_page" do
59
- it "creates a new page with the stored information" do
67
+ it "creates a new page" do
60
68
  c = HexaPDF::Composer.new(page_size: [0, 0, 50, 100], margin: 10)
61
69
  c.new_page
62
70
  assert_equal([0, 0, 50, 100], c.page.box.value)
@@ -64,16 +72,33 @@ describe HexaPDF::Composer do
64
72
  assert_equal(10, c.frame.bottom)
65
73
  end
66
74
 
67
- it "uses the provided information for the new and all following pages" do
68
- @composer.new_page(page_size: [0, 0, 50, 100], margin: 10)
69
- assert_equal([0, 0, 50, 100], @composer.page.box.value)
70
- assert_equal(10, @composer.frame.left)
71
- assert_equal(10, @composer.frame.bottom)
75
+ it "uses the named page style for the new page" do
76
+ @composer.page_style(:other, page_size: [0, 0, 100, 100])
77
+ @composer.new_page(:other)
78
+ assert_equal([0, 0, 100, 100], @composer.page.box.value)
79
+ end
80
+
81
+ it "sets the next page's style to the next_style value of the used page style" do
82
+ @composer.page_style(:one, page_size: [0, 0, 1, 1]).next_style = :two
83
+ @composer.page_style(:two, page_size: [0, 0, 2, 2]).next_style = :one
84
+ @composer.new_page(:one)
85
+ assert_equal([0, 0, 1, 1], @composer.page.box.value)
86
+ @composer.new_page
87
+ assert_equal([0, 0, 2, 2], @composer.page.box.value)
88
+ @composer.new_page
89
+ assert_equal([0, 0, 1, 1], @composer.page.box.value)
90
+ end
91
+
92
+ it "uses the current page style for new pages if no next_style value is set" do
93
+ @composer.page_style(:one, page_size: [0, 0, 1, 1])
94
+ @composer.new_page(:one)
95
+ assert_equal([0, 0, 1, 1], @composer.page.box.value)
72
96
  @composer.new_page
73
- assert_same(@composer.document.pages[2], @composer.page)
74
- assert_equal([0, 0, 50, 100], @composer.page.box.value)
75
- assert_equal(10, @composer.frame.left)
76
- assert_equal(10, @composer.frame.bottom)
97
+ assert_equal([0, 0, 1, 1], @composer.page.box.value)
98
+ end
99
+
100
+ it "fails if the specified page style has not been defined" do
101
+ assert_raises(ArgumentError) { @composer.new_page(:unknown) }
77
102
  end
78
103
  end
79
104
 
@@ -188,10 +188,15 @@ describe HexaPDF::DictionaryFields do
188
188
  ["D:19981223-08'00'", [1998, 12, 23, 00, 00, 00, "-08:00"]],
189
189
  ["D:199812-08'00'", [1998, 12, 01, 00, 00, 00, "-08:00"]],
190
190
  ["D:1998-08'00'", [1998, 01, 01, 00, 00, 00, "-08:00"]],
191
- ["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], # non-standard, missing '
192
- ["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], # non-standard, missing '
193
- ["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], # non-standard, TZ hour to large
194
- ["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], # non-standard, TZ min to large
191
+ ["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
192
+ ["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], # missing '
193
+ ["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], # TZ hour too large
194
+ ["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], # TZ min too large
195
+ ["D:19982423195210-08'00'", [1998, 12, 23, 19, 52, 10, "-08:00"]], # months too large
196
+ ["D:19981273195210-08'00'", [1998, 12, 31, 19, 52, 10, "-08:00"]], # day too large
197
+ ["D:19981223275210-08'00'", [1998, 12, 23, 23, 52, 10, "-08:00"]], # hour too large
198
+ ["D:19981223197710-08'00'", [1998, 12, 23, 19, 59, 10, "-08:00"]], # minute too large
199
+ ["D:19981223195280-08'00'", [1998, 12, 23, 19, 52, 59, "-08:00"]], # seconds too large
195
200
  ].each do |str, data|
196
201
  obj = @field.convert(str, self)
197
202
  assert_equal(Time.new(*data), obj, "date str used: #{str}")
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.29.0)>>
43
+ <</Producer(HexaPDF version 0.31.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.29.0)>>
75
+ <</Producer(HexaPDF version 0.31.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -164,14 +164,14 @@ describe HexaPDF::Writer do
164
164
 
165
165
  document = HexaPDF::Document.new(io: io)
166
166
  assert_equal(3, document.revisions.count)
167
- assert_equal(1, document.revisions.all[0].object(2)[:Kids].length)
168
- assert_equal(2, document.revisions.all[1].object(2)[:Kids].length)
169
- assert_equal(3, document.revisions.all[2].object(2)[:Kids].length)
167
+ assert_equal(1, document.revisions.all[0].object(3)[:Kids].length)
168
+ assert_equal(2, document.revisions.all[1].object(3)[:Kids].length)
169
+ assert_equal(3, document.revisions.all[2].object(3)[:Kids].length)
170
170
  end
171
171
 
172
172
  it "creates an xref stream if no xref stream is in a revision but object streams are" do
173
173
  document = HexaPDF::Document.new
174
- document.add({Type: :ObjStm})
174
+ document.add({}, type: HexaPDF::Type::ObjectStream)
175
175
  HexaPDF::Writer.new(document, StringIO.new).write
176
176
  assert_equal(:XRef, document.object(4).type)
177
177
  end
@@ -184,7 +184,7 @@ describe HexaPDF::Writer do
184
184
 
185
185
  document = HexaPDF::Document.new(io: io)
186
186
  document.pages.add
187
- document.add({Type: :ObjStm})
187
+ document.add({}, type: HexaPDF::Type::ObjectStream)
188
188
  io2 = StringIO.new
189
189
  HexaPDF::Writer.new(document, io2).write_incremental
190
190
 
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
214
214
  <</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
215
215
  endobj
216
216
  5 0 obj
217
- <</Producer(HexaPDF version 0.29.0)>>
217
+ <</Producer(HexaPDF version 0.31.0)>>
218
218
  endobj
219
219
  4 0 obj
220
220
  <</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
@@ -15,7 +15,7 @@ describe HexaPDF::Type::FontSimple do
15
15
  EOF
16
16
  font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Embedded, Flags: 0b100,
17
17
  FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
18
- Descent: -100, CapHeight: 800, StemV: 20})
18
+ MissingWidth: 500, Descent: -100, CapHeight: 800, StemV: 20})
19
19
  @font = @doc.add({Type: :Font, Encoding: :WinAnsiEncoding,
20
20
  BaseFont: :Embedded, FontDescriptor: font_descriptor, ToUnicode: cmap,
21
21
  FirstChar: 32, LastChar: 34, Widths: [600, 0, 700]},
@@ -130,9 +130,7 @@ describe HexaPDF::Type::FontSimple do
130
130
  end
131
131
 
132
132
  it "returns the /MissingWidth of a /FontDescriptor if available and the width was not found" do
133
- assert_equal(0, @font.width(0))
134
- @font[:FontDescriptor][:MissingWidth] = 99
135
- assert_equal(99, @font.width(0))
133
+ assert_equal(500, @font.width(0))
136
134
  end
137
135
 
138
136
  it "returns 0 for a missing code point when FontDescriptor is not available" do
@@ -169,8 +167,22 @@ describe HexaPDF::Type::FontSimple do
169
167
  end
170
168
 
171
169
  it "validates the lengths of the /Widths field" do
172
- @font[:Widths] = [65]
173
- refute(@font.validate)
170
+ @font[:Widths] = [65, 65]
171
+ assert(@font.validate)
172
+ assert_equal([65, 65, 65], @font[:Widths])
173
+
174
+ @font[:Widths] = [65, 70]
175
+ assert(@font.validate)
176
+ assert_equal([65, 70, 500], @font[:Widths])
177
+
178
+ @font[:Widths] = [65, 70]
179
+ @font.delete(:FontDescriptor)
180
+ assert(@font.validate)
181
+ assert_equal([65, 70, 0], @font[:Widths])
182
+
183
+ @font[:Widths] = [65, 65, 70, 90, 100]
184
+ assert(@font.validate)
185
+ assert_equal([65, 65, 70], @font[:Widths])
174
186
  end
175
187
  end
176
188
  end