hexapdf 0.21.0 → 0.21.1

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: b666a69330b87a3ad7a5c937a1113a66a3b5ed3d0cb4f4478ee24f97bed0e411
4
- data.tar.gz: 460c3cd90b8f76d3e4d32fdd6f1bc8fd71ddfef5d18799c6a207f562773371f8
3
+ metadata.gz: 9792b3c8be5fd3c3521aa5fe1a66e1315f677494f199e3e8b0e1eb85c9b967c7
4
+ data.tar.gz: a66f1d6751cac4ca2fc6a35bf493ba60eefee96edca090ae92ed810b756d8761
5
5
  SHA512:
6
- metadata.gz: a9135019912d8c3d1e282797054dfbc99c028f26aa28f8412a138ab3d8d67124c4e9c52ad104eeb4289487e47dd850e3ff4211b5e928cdb0a8aab2d8ea773930
7
- data.tar.gz: 070d0facdb6576fb6a0377a7139609ee648a580be879dabfd10e943bd2f62d66d9d5077058edcecf9ca3eb4e7969a2427c7a4af26bbb4d8f734e238f7022c7cd
6
+ metadata.gz: 111570ec411684ffe5214833419128b697721c4e5fdd16b61578c3d459a8ab9dde411780dd582e06620d52a260062df30a116b7c3c8544647f5e61f131c6bdc5
7
+ data.tar.gz: 3d098cb4098f6a02bc832cbe70aebc0b28a40a21b33b53c8e8e055f240fd513a5d96f53846c62ebbc2a00d17d6f99271dd3850e98a70cebb6f32e1a7e279cc87
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.21.1 - 2022-03-12
2
+
3
+ ### Fixed
4
+
5
+ - Handling of invalid AES encrypted files where the padding is missing
6
+
7
+
1
8
  ## 0.21.0 - 2022-03-04
2
9
 
3
10
  ### Added
@@ -246,7 +253,7 @@
246
253
 
247
254
  ## 0.16.0 - 2021-09-28
248
255
 
249
- ## Added
256
+ ### Added
250
257
 
251
258
  * Support for RGB color values of the form "RGB" in addition to "RRGGBB" and for
252
259
  CSS color module level 3 color names
data/Rakefile CHANGED
@@ -49,7 +49,7 @@ namespace :dev do
49
49
  task :test_all do
50
50
  versions = `rbenv versions --bare | grep -i 2.[567]\\\\\\|3.`.split("\n")
51
51
  versions.each do |version|
52
- sh "rbenv shell #{version} &>/dev/null && rake test"
52
+ sh "eval \"$(rbenv init -)\"; rbenv shell #{version} && ruby -v && rake test"
53
53
  end
54
54
  puts "Looks okay? (enter to continue, Ctrl-c to abort)"
55
55
  $stdin.gets
@@ -0,0 +1,57 @@
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)
@@ -114,10 +114,12 @@ module HexaPDF
114
114
  #
115
115
  # See: PDF1.7 s7.6.2.
116
116
  def decrypt(key, data)
117
- if data.length % BLOCK_SIZE != 0 || data.length < 2 * BLOCK_SIZE
117
+ if data.length % BLOCK_SIZE != 0 || data.length < BLOCK_SIZE
118
118
  raise HexaPDF::EncryptionError, "Invalid data for decryption, need 32 + 16*n bytes"
119
119
  end
120
- unpad(new(key, data.slice!(0, BLOCK_SIZE), :decrypt).process(data))
120
+ iv = data.slice!(0, BLOCK_SIZE)
121
+ # Handle invalid files with missing padding
122
+ data.empty? ? data : unpad(new(key, iv, :decrypt).process(data))
121
123
  end
122
124
 
123
125
  # Returns a Fiber object that decrypts the data from the given source fiber with the
@@ -140,11 +142,13 @@ module HexaPDF
140
142
  Fiber.yield(algorithm.process(new_data))
141
143
  end
142
144
 
143
- if data.length < BLOCK_SIZE || data.length % BLOCK_SIZE != 0
145
+ if data.length % BLOCK_SIZE != 0
144
146
  raise HexaPDF::EncryptionError, "Invalid data for decryption, need 32 + 16*n bytes"
147
+ elsif data.empty?
148
+ data # Handle invalid files with missing padding
149
+ else
150
+ unpad(algorithm.process(data))
145
151
  end
146
-
147
- unpad(algorithm.process(data))
148
152
  end
149
153
  end
150
154
 
@@ -0,0 +1,168 @@
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-2022 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)
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
+ p '-'*100
74
+ @frames = []
75
+ if style.position == :flow
76
+ column_width = (frame.width - gap * (@columns - 1)).to_f / @columns
77
+ @columns.times do |col_nr|
78
+ left = (column_width + gap) * col_nr + frame.left
79
+ bottom = frame.bottom
80
+ rect = Geom2D::Polygon([left, bottom],
81
+ [left + column_width, bottom],
82
+ [left + column_width, bottom + height],
83
+ [left, bottom + height])
84
+ shape = Geom2D::Algorithms::PolygonOperation.run(frame.shape, rect, :intersection)
85
+ col_frame = Frame.new(left, bottom, column_width, height)
86
+ col_frame.shape = shape
87
+ @frames << col_frame
88
+ end
89
+ @frame_index = 0
90
+ @results = @children.map {|child_box| fit_box(child_box) }
91
+ @width = frame.width
92
+ @height = frame.height - @frames.min_by(&:y).y
93
+ else
94
+ width = (@initial_width > 0 ? @initial_width : available_width) - reserved_width
95
+ column_width = (width - gap * (@columns - 1)).to_f / @columns
96
+ @columns.times do |col_nr|
97
+ @frames << Frame.new((column_width + gap) * col_nr, 0, column_width, height)
98
+ end
99
+ @frame_index = 0
100
+ @results = @children.map {|child_box| fit_box(child_box) }
101
+ @width = width
102
+ @height = height - @frames.min_by(&:y).y
103
+ end
104
+ min_y, max_y = @frames.minmax_by(&:y).map(&:y)
105
+ p [height, @frames.map(&:y), last_height_difference, min_y, max_y]
106
+ # TOOD: @result.any?(&:empty?) only for the first run!!!! if the first run fails, we
107
+ # cannot balance the columns because there is too much content.
108
+ # TODO: another break condition is if the @results didn't change since the last run
109
+ p [:maybe_redo, min_y, max_y, height, last_height_difference]
110
+ p [@results.map {|arr| arr.all? {|r| r.status }}]
111
+ break if max_y != height && @results.all? {|arr| !arr.empty? && arr.all? {|r| r.success? }} &&
112
+ (@results.any?(&:empty?) ||
113
+ max_y - min_y >= last_height_difference ||
114
+ max_y - min_y < 0.5)
115
+ if max_y == 0 && min_y == 0
116
+ height += last_height_difference / 4.0
117
+ else
118
+ last_height_difference = max_y - min_y
119
+ height -= last_height_difference / 2.0
120
+ end
121
+ end
122
+ @results.all? {|res| res.length == 1 }
123
+ end
124
+
125
+ private
126
+
127
+ def fit_box(box)
128
+ cur_frame = @frames[@frame_index]
129
+ fit_results = []
130
+ while cur_frame
131
+ result = cur_frame.fit(box)
132
+ if result.success?
133
+ cur_frame.remove_area(result.mask)
134
+ fit_results << result
135
+ break
136
+ elsif cur_frame.full?
137
+ @frame_index += 1
138
+ break if @frame_index == @frames.length
139
+ cur_frame = @frames[@frame_index]
140
+ else
141
+ draw_box, box = cur_frame.split(result)
142
+ if draw_box
143
+ cur_frame.remove_area(result.mask)
144
+ fit_results << result
145
+ elsif !cur_frame.find_next_region
146
+ @frame_index += 1
147
+ break if @frame_index == @frames.length
148
+ cur_frame = @frames[@frame_index]
149
+ end
150
+ end
151
+ end
152
+ fit_results
153
+ end
154
+
155
+ # Draws the child boxes onto the canvas at position [x, y].
156
+ def draw_content(canvas, x, y)
157
+ x = y = 0 if style.position == :flow
158
+ @results.each do |result_boxes|
159
+ result_boxes.each do |result|
160
+ result.box.draw(canvas, x + result.x, y + result.y)
161
+ end
162
+ end
163
+ end
164
+
165
+ end
166
+
167
+ end
168
+ end
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.21.0'
40
+ VERSION = '0.21.1'
41
41
 
42
42
  end
@@ -48,6 +48,10 @@ describe HexaPDF::Encryption::AES do
48
48
  end
49
49
  end
50
50
 
51
+ it "handles invalid files with missing 16 byte padding" do
52
+ assert_equal('', @algorithm_class.decrypt('some key' * 2, 'iv' * 8))
53
+ end
54
+
51
55
  it "fails on decryption if not enough bytes are provided" do
52
56
  assert_raises(HexaPDF::EncryptionError) do
53
57
  @algorithm_class.decrypt('some' * 4, 'no iv')
@@ -97,6 +101,10 @@ describe HexaPDF::Encryption::AES do
97
101
  end
98
102
 
99
103
  it "decryption works if the padding is invalid" do
104
+ f = Fiber.new { 'a' * 16 }
105
+ result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
106
+ assert_equal('', result)
107
+
100
108
  f = Fiber.new { 'a' * 32 }
101
109
  result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
102
110
  assert_equal('a' * 16, result)
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.21.0)>>
43
+ <</Producer(HexaPDF version 0.21.1)>>
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.21.0)>>
75
+ <</Producer(HexaPDF version 0.21.1)>>
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.21.0
4
+ version: 0.21.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-04 00:00:00.000000000 Z
11
+ date: 2022-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -217,6 +217,7 @@ files:
217
217
  - examples/017-frame_text_flow.rb
218
218
  - examples/018-composer.rb
219
219
  - examples/019-acro_form.rb
220
+ - examples/020-column_box.rb
220
221
  - examples/emoji-smile.png
221
222
  - examples/emoji-wink.png
222
223
  - examples/machupicchu.jpg
@@ -333,6 +334,7 @@ files:
333
334
  - lib/hexapdf/importer.rb
334
335
  - lib/hexapdf/layout.rb
335
336
  - lib/hexapdf/layout/box.rb
337
+ - lib/hexapdf/layout/column_box.rb
336
338
  - lib/hexapdf/layout/frame.rb
337
339
  - lib/hexapdf/layout/image_box.rb
338
340
  - lib/hexapdf/layout/inline_box.rb