hexapdf 0.9.1 → 0.9.2

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: 42f7d2d181cfcbe2c6b8a99add1901773b07b459c81964efeb4d506a86932620
4
- data.tar.gz: eb36de061e2f0dd9cb2ee939b12d9feb04bb0993ae3657258fe5dff98c2b8466
3
+ metadata.gz: ee462224ffa152978df2a0aebd05b52c7a75a21655880858bbb4b61e2eccd3dd
4
+ data.tar.gz: dc2eedf3a556c7a85fbfb229372512043f33200c6ffb6223b344c96bf4aff91b
5
5
  SHA512:
6
- metadata.gz: 473cd080839b097d72113963fbe709e94a0a718dce6db8790636178bb206a61ac7a095398896e21166c413344fa932d8d9da35003e4c65ec04e22990f994aa04
7
- data.tar.gz: 547e778a0b621ed708d295db9f14fd9026d2c08a7480a5dfc0fcb0f2854158588a89840993783590db4e583d9d70e9ab97302caa44247de1fe4cf259b5e73065
6
+ metadata.gz: da87ec95143ad653a606c699dc4e29ac3e4abf6b4e1a0bfc535ede8221f047ab35f7b69556eaa7b7cf23d7da69909685c515d98ea67f1e1e0265ebbb460f2eb7
7
+ data.tar.gz: f47ec5e10af27baf8a8ee06d4f496f53841b57acc93ce1111288576a473dfb73d73052aa65f21835e19ebadb6e87868fcc3d63f84c82dbfd81dca7ae56eb9975
@@ -1,3 +1,10 @@
1
+ ## 0.9.2 - 2019-05-22
2
+
3
+ ### Changed
4
+
5
+ * [HexaPDF::Encryption::AES] to handle invalid padding
6
+ * [HexaPDF::Filter::FlateDecode] to correctly handle invalid empty streams
7
+
1
8
  ## 0.9.1 - 2019-03-26
2
9
 
3
10
  ### Fixed
@@ -1,4 +1,4 @@
1
1
  Count Name
2
2
  ======= ====
3
- 1034 Thomas Leitner <t_leitner@gmx.at>
3
+ 1037 Thomas Leitner <t_leitner@gmx.at>
4
4
  1 Daniel Kraus <bovender@bovender.de>
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 0.9.2
@@ -170,13 +170,18 @@ module HexaPDF
170
170
  # Removes the padding from the data according to the PKCS#5 padding scheme and returns the
171
171
  # result.
172
172
  #
173
+ # In case the padding is not correct as per the specification, it is assumed that there is
174
+ # no padding and the input is returned as is.
175
+ #
173
176
  # See: PDF1.7 s7.6.2
174
177
  def unpad(data)
175
178
  padding_length = data.getbyte(-1)
176
- if padding_length > BLOCK_SIZE || padding_length == 0
177
- raise HexaPDF::EncryptionError, "Invalid AES padding length #{padding_length}"
179
+ if padding_length > BLOCK_SIZE || padding_length == 0 ||
180
+ data[-padding_length, padding_length].each_byte.any? {|byte| byte != padding_length }
181
+ data
182
+ else
183
+ data[0...-padding_length]
178
184
  end
179
- data[0...-padding_length]
180
185
  end
181
186
 
182
187
  end
@@ -49,10 +49,14 @@ module HexaPDF
49
49
  module FlateDecode
50
50
 
51
51
  # See HexaPDF::Filter
52
+ #
53
+ # The decoder also handles the case of an empty string not deflated to a correct flate stream
54
+ # but just output as an empty string.
52
55
  def self.decoder(source, options = nil)
53
56
  fib = Fiber.new do
54
57
  inflater = Zlib::Inflate.new
55
58
  while source.alive? && (data = source.resume)
59
+ next if data.empty?
56
60
  begin
57
61
  data = inflater.inflate(data)
58
62
  rescue StandardError => e
@@ -61,7 +65,7 @@ module HexaPDF
61
65
  Fiber.yield(data)
62
66
  end
63
67
  begin
64
- data = (data = inflater.finish).empty? ? nil : data
68
+ data = inflater.total_in == 0 || (data = inflater.finish).empty? ? nil : data
65
69
  inflater.close
66
70
  data
67
71
  rescue StandardError => e
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.9.1'
40
+ VERSION = '0.9.2'
41
41
 
42
42
  end
@@ -48,12 +48,6 @@ describe HexaPDF::Encryption::AES do
48
48
  end
49
49
  end
50
50
 
51
- it "fails on decryption if the padding is invalid" do
52
- assert_raises(HexaPDF::EncryptionError) do
53
- @algorithm_class.decrypt('some' * 4, 'iv' * 8 + 'somedata' * 4)
54
- end
55
- end
56
-
57
51
  it "fails on decryption if not enough bytes are provided" do
58
52
  assert_raises(HexaPDF::EncryptionError) do
59
53
  @algorithm_class.decrypt('some' * 4, 'no iv')
@@ -102,10 +96,18 @@ describe HexaPDF::Encryption::AES do
102
96
  assert_equal('a' * 40, result)
103
97
  end
104
98
 
105
- it "fails on decryption if the padding is invalid" do
106
- assert_raises(HexaPDF::EncryptionError) do
107
- TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, Fiber.new { 'a' * 32 }))
108
- end
99
+ it "decryption works if the padding is invalid" do
100
+ f = Fiber.new { 'a' * 32 }
101
+ result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
102
+ assert_equal('a' * 16, result)
103
+
104
+ f = Fiber.new { 'a' * 31 + "\x00" }
105
+ result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
106
+ assert_equal('a' * 15 + "\x00", result)
107
+
108
+ f = Fiber.new { 'a' * 29 + "\x00\x01\x03" }
109
+ result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
110
+ assert_equal('a' * 13 + "\x00\x01\x03", result)
109
111
  end
110
112
 
111
113
  it "fails on decryption if not enough bytes are provided" do
@@ -17,13 +17,22 @@ describe HexaPDF::Filter::FlateDecode do
17
17
  end
18
18
 
19
19
  describe "decoder" do
20
+ it "works for empty input" do
21
+ assert_equal('', collector(@obj.decoder(Fiber.new { "" })))
22
+ assert_equal('', collector(@obj.decoder(Fiber.new {})))
23
+ end
24
+
20
25
  it "applies the Predictor after decoding" do
21
26
  assert_equal(@decoded, collector(@obj.decoder(feeder(@encoded_predictor), @predictor_opts)))
22
27
  end
23
28
 
24
29
  it "fails on invalid input" do
25
- assert_raises(HexaPDF::FilterError) { collector(@obj.decoder(feeder("some test"))) }
26
- assert_raises(HexaPDF::FilterError) { collector(@obj.decoder(Fiber.new {})) }
30
+ assert_raises(HexaPDF::FilterError) do
31
+ collector(@obj.decoder(feeder(@encoded[0..-2], @encoded.length - 3)))
32
+ end
33
+ assert_raises(HexaPDF::FilterError) do
34
+ collector(@obj.decoder(feeder("some data")))
35
+ end
27
36
  end
28
37
  end
29
38
 
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.9.1)>>
43
+ <</Producer(HexaPDF version 0.9.2)>>
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.9.1)>>
75
+ <</Producer(HexaPDF version 0.9.2)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
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.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-26 00:00:00.000000000 Z
11
+ date: 2019-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -206,7 +206,6 @@ files:
206
206
  - examples/016-frame_automatic_box_placement.rb
207
207
  - examples/017-frame_text_flow.rb
208
208
  - examples/018-composer.rb
209
- - examples/019-column_box.rb
210
209
  - examples/emoji-smile.png
211
210
  - examples/emoji-wink.png
212
211
  - examples/machupicchu.jpg
@@ -318,7 +317,6 @@ files:
318
317
  - lib/hexapdf/importer.rb
319
318
  - lib/hexapdf/layout.rb
320
319
  - lib/hexapdf/layout/box.rb
321
- - lib/hexapdf/layout/column_box.rb
322
320
  - lib/hexapdf/layout/frame.rb
323
321
  - lib/hexapdf/layout/image_box.rb
324
322
  - lib/hexapdf/layout/inline_box.rb
@@ -1,57 +0,0 @@
1
- # ## Column Box
2
- #
3
- # This example shows how [HexaPDF::Layout::Frame] and [HexaPDF::Layout::TextBox]
4
- # can be used to flow text around objects.
5
- #
6
- # Three boxes are placed repeatedly onto the frame until it is filled: two
7
- # floating boxes (one left, one right) and a text box. The text box is styled to
8
- # flow its content around the other two boxes.
9
- #
10
- # Usage:
11
- # : `ruby frame_text_flow.rb`
12
- #
13
-
14
- require 'hexapdf'
15
- require 'hexapdf/utils/graphics_helpers'
16
-
17
- include HexaPDF::Layout
18
- include HexaPDF::Utils::GraphicsHelpers
19
-
20
- doc = HexaPDF::Document.new
21
-
22
- sample_text = "Lorem ipsum dolor sit amet, con\u{00AD}sectetur
23
- adipis\u{00AD}cing elit, sed do eiusmod tempor incididunt ut labore et
24
- dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
25
- ullamco laboris nisi ut aliquip ex ea commodo consequat.
26
- ".tr("\n", ' ') * 5
27
- items = [TextFragment.create(sample_text, font: doc.fonts.add("Times"))]
28
-
29
- page = doc.pages.add
30
- media_box = page.box(:media)
31
- canvas = page.canvas
32
- frame = Frame.new(media_box.left + 20, media_box.bottom + 20,
33
- media_box.width - 40, media_box.height - 40)
34
-
35
- image = doc.images.add(File.join(__dir__, 'machupicchu.jpg'))
36
- iw, ih = calculate_dimensions(image.width, image.height, rwidth: 100)
37
-
38
- boxes = []
39
- 3.times do
40
- boxes << Box.create(width: iw, height: ih,
41
- margin: [10, 30], position: :float) do |canv, box|
42
- canv.image(image, at: [0, 0], width: 100)
43
- end
44
- boxes << Box.create(width: 50, height: 50, margin: 20,
45
- position: :float, position_hint: :right,
46
- border: {width: 1, color: [[255, 0, 0]]})
47
- boxes << TextBox.new(items, style: {position: :flow, align: :justify})
48
- end
49
- columns = ColumnBox.new(boxes) #, style: {position: :flow})
50
- polygon = Geom2D::Polygon([250, 350], [350, 350], [350, 500], [250, 500])
51
- #frame.remove_area(polygon)
52
- #canvas.draw(:geom2d, object: polygon)
53
- result = frame.fit(columns)
54
- p result.success?
55
- frame.draw(canvas, result)
56
-
57
- doc.write("column_box.pdf", optimize: true)
@@ -1,161 +0,0 @@
1
- # -*- encoding: utf-8; frozen_string_literal: true -*-
2
- #
3
- #--
4
- # This file is part of HexaPDF.
5
- #
6
- # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
7
- # Copyright (C) 2014-2019 Thomas Leitner
8
- #
9
- # HexaPDF is free software: you can redistribute it and/or modify it
10
- # under the terms of the GNU Affero General Public License version 3 as
11
- # published by the Free Software Foundation with the addition of the
12
- # following permission added to Section 15 as permitted in Section 7(a):
13
- # FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
14
- # THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
15
- # INFRINGEMENT OF THIRD PARTY RIGHTS.
16
- #
17
- # HexaPDF is distributed in the hope that it will be useful, but WITHOUT
18
- # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
- # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
20
- # License for more details.
21
- #
22
- # You should have received a copy of the GNU Affero General Public License
23
- # along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
24
- #
25
- # The interactive user interfaces in modified source and object code
26
- # versions of HexaPDF must display Appropriate Legal Notices, as required
27
- # under Section 5 of the GNU Affero General Public License version 3.
28
- #
29
- # In accordance with Section 7(b) of the GNU Affero General Public
30
- # License, a covered work must retain the producer line in every PDF that
31
- # is created or manipulated using HexaPDF.
32
- #
33
- # If the GNU Affero General Public License doesn't fit your need,
34
- # commercial licenses are available at <https://gettalong.at/hexapdf/>.
35
- #++
36
- require 'hexapdf/layout/box'
37
-
38
- module HexaPDF
39
- module Layout
40
-
41
- # A ColumnBox arranges boxes in one or more columns.
42
- #
43
- # The number of columns as well as the size of the gap between the columns can be modified.
44
- class ColumnBox < Box
45
-
46
- # The child boxes of this ColumnBox.
47
- attr_reader :children
48
-
49
- # The number of columns.
50
- # TODO: allow array with column widths later like [100, :*, :*]; same for gaps
51
- attr_reader :columns
52
-
53
- # The size of the gap between the columns.
54
- attr_reader :gap
55
-
56
- # Creates a new ColumnBox object for the given +children+ boxes.
57
- def initialize(children = [], columns = 2, gap: 36, **kwargs)
58
- super(kwargs, &:unused_draw_block)
59
- @children = children
60
- @columns = columns
61
- @gap = gap
62
- end
63
-
64
- # Fits the column box into the available space.
65
- def fit(available_width, available_height, frame)
66
- last_height_difference = 1_000_000
67
- height = if style.position == :flow
68
- frame.height
69
- else
70
- (@initial_height > 0 ? @initial_height : available_height) - reserved_height
71
- end
72
- while true
73
- @frames = []
74
- if style.position == :flow
75
- column_width = (frame.width - gap * (@columns - 1)).to_f / @columns
76
- @columns.times do |col_nr|
77
- left = (column_width + gap) * col_nr + frame.left
78
- bottom = frame.bottom
79
- rect = Geom2D::Polygon([left, bottom],
80
- [left + column_width, bottom],
81
- [left + column_width, bottom + height],
82
- [left, bottom + height])
83
- shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, rect, :intersection)
84
- col_frame = Frame.new(left, bottom, column_width, height)
85
- col_frame.shape = shape
86
- @frames << col_frame
87
- end
88
- @frame_index = 0
89
- @results = @children.map {|child_box| fit_box(child_box) }
90
- @width = frame.width
91
- @height = frame.height - @frames.min_by(&:y).y
92
- else
93
- width = (@initial_width > 0 ? @initial_width : available_width) - reserved_width
94
- column_width = (width - gap * (@columns - 1)).to_f / @columns
95
- @columns.times do |col_nr|
96
- @frames << Frame.new((column_width + gap) * col_nr, 0, column_width, height)
97
- end
98
- @frame_index = 0
99
- @results = @children.map {|child_box| fit_box(child_box) }
100
- @width = width
101
- @height = height - @frames.min_by(&:y).y
102
- end
103
- min_y, max_y = @frames.minmax_by(&:y).map(&:y)
104
- p [height, @frames.map(&:y), last_height_difference, min_y, max_y]
105
- # TOOD: @result.any?(&:empty?) only for the first run!!!! if the first run fails, we
106
- # cannot balance the columns because there is too much content.
107
- # TODO: another break condition is if the @results didn't change since the last run
108
- break if max_y != height && (@results.any?(&:empty?) ||
109
- max_y - min_y >= last_height_difference ||
110
- max_y - min_y < 0.5)
111
- last_height_difference = max_y - min_y
112
- height -= last_height_difference / 2.0
113
- p [height]
114
- end
115
- @results.all? {|res| res.length == 1 }
116
- end
117
-
118
- private
119
-
120
- def fit_box(box)
121
- cur_frame = @frames[@frame_index]
122
- fitted_boxes = []
123
- while true && cur_frame
124
- result = cur_frame.fit(box)
125
- if result.success?
126
- cur_frame.remove_area(result.mask)
127
- fitted_boxes << result
128
- break
129
- elsif cur_frame.full?
130
- @frame_index += 1
131
- break if @frame_index == @frames.length
132
- cur_frame = @frames[@frame_index]
133
- else
134
- draw_box, box = cur_frame.split(result)
135
- if draw_box
136
- cur_frame.remove_area(result.mask)
137
- fitted_boxes << result
138
- elsif !cur_frame.find_next_region
139
- @frame_index += 1
140
- break if @frame_index == @frames.length
141
- cur_frame = @frames[@frame_index]
142
- end
143
- end
144
- end
145
- fitted_boxes
146
- end
147
-
148
- # Draws the child boxes onto the canvas at position [x, y].
149
- def draw_content(canvas, x, y)
150
- x = y = 0 if style.position == :flow
151
- @results.each do |result_boxes|
152
- result_boxes.each do |result|
153
- result.box.draw(canvas, x + result.x, y + result.y)
154
- end
155
- end
156
- end
157
-
158
- end
159
-
160
- end
161
- end