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 +4 -4
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTERS +1 -1
- data/VERSION +1 -1
- data/lib/hexapdf/encryption/aes.rb +8 -3
- data/lib/hexapdf/filter/flate_decode.rb +5 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +12 -10
- data/test/hexapdf/filter/test_flate_decode.rb +11 -2
- data/test/hexapdf/test_writer.rb +2 -2
- metadata +2 -4
- data/examples/019-column_box.rb +0 -57
- data/lib/hexapdf/layout/column_box.rb +0 -161
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee462224ffa152978df2a0aebd05b52c7a75a21655880858bbb4b61e2eccd3dd
|
4
|
+
data.tar.gz: dc2eedf3a556c7a85fbfb229372512043f33200c6ffb6223b344c96bf4aff91b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da87ec95143ad653a606c699dc4e29ac3e4abf6b4e1a0bfc535ede8221f047ab35f7b69556eaa7b7cf23d7da69909685c515d98ea67f1e1e0265ebbb460f2eb7
|
7
|
+
data.tar.gz: f47ec5e10af27baf8a8ee06d4f496f53841b57acc93ce1111288576a473dfb73d73052aa65f21835e19ebadb6e87868fcc3d63f84c82dbfd81dca7ae56eb9975
|
data/CHANGELOG.md
CHANGED
data/CONTRIBUTERS
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
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
|
-
|
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
|
data/lib/hexapdf/version.rb
CHANGED
@@ -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 "
|
106
|
-
|
107
|
-
|
108
|
-
|
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)
|
26
|
-
|
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
|
|
data/test/hexapdf/test_writer.rb
CHANGED
@@ -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.
|
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.
|
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.
|
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-
|
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
|
data/examples/019-column_box.rb
DELETED
@@ -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
|