hexapdf 0.29.0 → 0.30.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fdace78c8d34d39c345e2ccd04edda4755e2fc8076cc7320793a4ef16f48520
4
- data.tar.gz: a0ec03dc2d579eb8663512ec0c84cadbc877b9ec43cabb8ea0d6ab58df337585
3
+ metadata.gz: 139f1864e4decd05c57468dc3b326a9cc4e749f1d35d4b5dadb3e0549215afd6
4
+ data.tar.gz: e7a0d34979abe6738a084dec2334f449ebc1db3eb479dd5f2374a81eeabd83d7
5
5
  SHA512:
6
- metadata.gz: a8b18782359af03f0eda710658d0839554f0e95cd8068683dcaea56e8493c3b8a0b4cc30c9e39a3fdbe743df4d5e34b84ce4ab27125c8c14300b861ecbff16e9
7
- data.tar.gz: 8d5c60a368d85fa9c2b118d955933943f5d0cf79e91d744348cf1e3f1c9435d44880033c844b58ab098c8ab8d1e2e7cd4c5e04710133b25543f31457277d0ba2
6
+ metadata.gz: 339c38737585eafdf447f7516a52ba7ce40b7e2e476336a2404e97a97645d5043b2956835d874296841b8b68d5935c53d97244ac90a2fd621d0471ce85ae8809
7
+ data.tar.gz: 4a4c843a32859c639fdf724ca1c6492fa3d387a1c8a92335a2341e82de42a5b9bc30663a3226c1cc0f2ca116dd638071e24b595b323e64fa1e0342d8681199bd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.30.0 - 2023-01-13
2
+
3
+ ### Added
4
+
5
+ * [HexaPDF::Document::Pages#create] for creating a page object without adding it
6
+ to the page tree
7
+
8
+ ### Changed
9
+
10
+ * `HexaPDF::Type::FontSimple#perform_validation` to correct /Widths fields in
11
+ case it has an invalid number of entries
12
+
13
+ ### Fixed
14
+
15
+ * [HexaPDF::DictionaryFields::DateConverter] to handle invalid months, day,
16
+ hour, minute and second values
17
+
18
+
1
19
  ## 0.29.0 - 2023-01-30
2
20
 
3
21
  ### Added
@@ -305,8 +305,13 @@ module HexaPDF
305
305
  else
306
306
  (m[7] == '-' ? -1 : 1) * (m[8].to_i * 3600 + m[9].to_i * 60).clamp(0, 86399)
307
307
  end
308
- Time.new(m[1].to_i, (m[2] ? m[2].to_i : 1), (m[3] ? m[3].to_i : 1),
309
- m[4].to_i, m[5].to_i, m[6].to_i, utc_offset)
308
+ begin
309
+ Time.new(m[1].to_i, (m[2] ? m[2].to_i : 1), (m[3] ? m[3].to_i : 1),
310
+ m[4].to_i, m[5].to_i, m[6].to_i, utc_offset)
311
+ rescue ArgumentError
312
+ Time.new(m[1].to_i, m[2].to_i.clamp(1, 12), m[3].to_i.clamp(1, 31),
313
+ m[4].to_i.clamp(0, 23), m[5].to_i.clamp(0, 59), m[6].to_i.clamp(0, 59), utc_offset)
314
+ end
310
315
  end
311
316
 
312
317
  end
@@ -75,28 +75,45 @@ module HexaPDF
75
75
  @document.catalog.pages
76
76
  end
77
77
 
78
- # :call-seq:
79
- # pages.add -> new_page
80
- # pages.add(media_box, orientation: :portrait) -> new_page
81
- # pages.add(page) -> page
78
+ # Creates a page object and returns it *without* adding it to the page tree.
79
+ #
80
+ # +media_box+::
81
+ # If this argument is +nil+/not specified, the value is taken from the configuration
82
+ # option 'page.default_media_box'.
83
+ #
84
+ # If the resulting value is an array with four numbers (specifying the media box), the new
85
+ # page will have these exact dimensions.
82
86
  #
83
- # Adds the page or a new empty page at the end and returns it.
87
+ # If the value is a symbol, it is taken as a reference to a pre-defined media box in
88
+ # HexaPDF::Type::Page::PAPER_SIZE. The +orientation+ can then be used to specify the page
89
+ # orientation.
84
90
  #
85
- # If no argument is given, a new page with the default dimensions (see configuration option
86
- # 'page.default_media_box') is used.
91
+ # +orientation+::
92
+ # If this argument is not specified, it is taken from 'page.default_media_orientation'. It
93
+ # is only used if +media_box+ is a symbol and not an array.
94
+ def create(media_box: nil, orientation: nil)
95
+ media_box ||= @document.config['page.default_media_box']
96
+ orientation ||= @document.config['page.default_media_orientation']
97
+ box = if media_box.kind_of?(Array)
98
+ media_box
99
+ else
100
+ Type::Page.media_box(media_box, orientation: orientation)
101
+ end
102
+ @document.add({Type: :Page, MediaBox: box})
103
+ end
104
+
105
+ # :call-seq:
106
+ # pages.add -> new_page
107
+ # pages.add(page) -> page
108
+ # pages.add(media_box, orientation: nil) -> new_page
87
109
  #
88
- # If the single argument is an array with four numbers (specifying the media box), the new
89
- # page will have these dimensions.
110
+ # Adds the given page or a new empty page at the end and returns it.
90
111
  #
91
- # If the single argument is a symbol, it is taken as referencing a pre-defined media box in
92
- # HexaPDF::Type::Page::PAPER_SIZE for the new page. The optional argument +orientation+ can be
93
- # used to change the orientation to :landscape if needed.
94
- def add(page = nil, orientation: :portrait)
95
- if page.kind_of?(Array)
96
- page = @document.add({Type: :Page, MediaBox: page})
97
- elsif page.kind_of?(Symbol)
98
- box = Type::Page.media_box(page, orientation: orientation)
99
- page = @document.add({Type: :Page, MediaBox: box})
112
+ # If called with a page object as argument, that page object is used. Otherwise #create is
113
+ # called with the arguments +media_box+ and +orientation+ to create a new page.
114
+ def add(page = nil, orientation: nil)
115
+ unless page.kind_of?(HexaPDF::Type::Page)
116
+ page = create(media_box: page, orientation: orientation)
100
117
  end
101
118
  @document.catalog.pages.add_page(page)
102
119
  end
@@ -324,10 +324,10 @@ module HexaPDF
324
324
  def prepare_decryption(password: '', check_permissions: true)
325
325
  if dict[:Filter] != :Standard
326
326
  raise(HexaPDF::UnsupportedEncryptionError,
327
- "Invalid /Filter value for standard security handler")
327
+ "Invalid /Filter value #{dict[:Filter]} for standard security handler")
328
328
  elsif ![2, 3, 4, 6].include?(dict[:R])
329
329
  raise(HexaPDF::UnsupportedEncryptionError,
330
- "Invalid /R value for standard security handler")
330
+ "Invalid /R value #{dict[:R]} for standard security handler")
331
331
  elsif dict[:R] <= 4 && !document.trailer[:ID].kind_of?(PDFArray)
332
332
  document.trailer[:ID] = ['', '']
333
333
  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
 
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.29.0'
40
+ VERSION = '0.30.0'
41
41
 
42
42
  end
@@ -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
@@ -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.30.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.30.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -164,9 +164,9 @@ 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
@@ -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.30.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
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.29.0
4
+ version: 0.30.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: 2023-01-30 00:00:00.000000000 Z
11
+ date: 2023-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse