hexapdf 0.9.1 → 0.9.2

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