hexapdf 0.29.0 → 0.31.0

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