hexapdf 0.33.0 → 0.34.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 +4 -4
- data/CHANGELOG.md +42 -1
- data/examples/026-optional_content.rb +55 -0
- data/examples/027-composer_optional_content.rb +83 -0
- data/lib/hexapdf/cli/command.rb +7 -1
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +2 -4
- data/lib/hexapdf/composer.rb +2 -1
- data/lib/hexapdf/configuration.rb +21 -1
- data/lib/hexapdf/content/canvas.rb +52 -0
- data/lib/hexapdf/content/operator.rb +2 -0
- data/lib/hexapdf/dictionary.rb +1 -0
- data/lib/hexapdf/dictionary_fields.rb +1 -2
- data/lib/hexapdf/digital_signature/verification_result.rb +1 -2
- data/lib/hexapdf/document/layout.rb +3 -0
- data/lib/hexapdf/document/pages.rb +1 -1
- data/lib/hexapdf/document.rb +7 -0
- data/lib/hexapdf/encryption/ruby_aes.rb +10 -20
- data/lib/hexapdf/layout/box.rb +23 -3
- data/lib/hexapdf/layout/column_box.rb +2 -1
- data/lib/hexapdf/layout/frame.rb +23 -6
- data/lib/hexapdf/layout/inline_box.rb +20 -9
- data/lib/hexapdf/layout/list_box.rb +34 -20
- data/lib/hexapdf/layout/page_style.rb +2 -1
- data/lib/hexapdf/layout/style.rb +46 -6
- data/lib/hexapdf/layout/table_box.rb +9 -7
- data/lib/hexapdf/layout/text_box.rb +9 -2
- data/lib/hexapdf/layout/text_fragment.rb +28 -2
- data/lib/hexapdf/layout/text_layouter.rb +21 -5
- data/lib/hexapdf/stream.rb +1 -2
- data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
- data/lib/hexapdf/type/actions.rb +1 -0
- data/lib/hexapdf/type/annotations/text.rb +1 -2
- data/lib/hexapdf/type/catalog.rb +10 -1
- data/lib/hexapdf/type/cid_font.rb +15 -1
- data/lib/hexapdf/type/form.rb +75 -5
- data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
- data/lib/hexapdf/type/optional_content_group.rb +370 -0
- data/lib/hexapdf/type/optional_content_membership.rb +63 -0
- data/lib/hexapdf/type/optional_content_properties.rb +158 -0
- data/lib/hexapdf/type/page.rb +27 -11
- data/lib/hexapdf/type/page_label.rb +4 -8
- data/lib/hexapdf/type.rb +4 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +0 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas.rb +49 -0
- data/test/hexapdf/document/test_layout.rb +7 -2
- data/test/hexapdf/document/test_pages.rb +6 -6
- data/test/hexapdf/layout/test_box.rb +13 -4
- data/test/hexapdf/layout/test_frame.rb +13 -1
- data/test/hexapdf/layout/test_inline_box.rb +17 -8
- data/test/hexapdf/layout/test_list_box.rb +48 -31
- data/test/hexapdf/layout/test_style.rb +10 -0
- data/test/hexapdf/layout/test_table_box.rb +32 -26
- data/test/hexapdf/layout/test_text_box.rb +8 -0
- data/test/hexapdf/layout/test_text_fragment.rb +33 -0
- data/test/hexapdf/layout/test_text_layouter.rb +32 -5
- data/test/hexapdf/test_composer.rb +10 -0
- data/test/hexapdf/test_dictionary.rb +10 -0
- data/test/hexapdf/test_document.rb +4 -0
- data/test/hexapdf/test_writer.rb +3 -3
- data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
- data/test/hexapdf/type/test_catalog.rb +11 -0
- data/test/hexapdf/type/test_form.rb +119 -0
- data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
- data/test/hexapdf/type/test_optional_content_group.rb +158 -0
- data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
- data/test/hexapdf/type/test_page.rb +2 -2
- metadata +14 -3
|
@@ -103,8 +103,7 @@ module HexaPDF
|
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
# :nodoc:
|
|
107
|
-
NUMBERING_STYLE_MAPPING = {
|
|
106
|
+
NUMBERING_STYLE_MAPPING = { # :nodoc:
|
|
108
107
|
decimal: :D, D: :D,
|
|
109
108
|
uppercase_roman: :R, R: :R,
|
|
110
109
|
lowercase_roman: :r, r: :r,
|
|
@@ -113,8 +112,7 @@ module HexaPDF
|
|
|
113
112
|
none: nil
|
|
114
113
|
}
|
|
115
114
|
|
|
116
|
-
# :nodoc:
|
|
117
|
-
REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse]
|
|
115
|
+
REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse] # :nodoc:
|
|
118
116
|
|
|
119
117
|
# :call-seq:
|
|
120
118
|
# page_label.numbering_style -> numbering_style
|
|
@@ -174,8 +172,7 @@ module HexaPDF
|
|
|
174
172
|
|
|
175
173
|
private
|
|
176
174
|
|
|
177
|
-
# :nodoc:
|
|
178
|
-
ALPHABET = ('A'..'Z').to_a
|
|
175
|
+
ALPHABET = ('A'..'Z').to_a # :nodoc:
|
|
179
176
|
|
|
180
177
|
# Maps the given number to uppercase (or, if +lowercase+ is +true+, lowercase) letters (e.g. 1
|
|
181
178
|
# -> A, 27 -> AA, 28 -> AB, ...).
|
|
@@ -188,8 +185,7 @@ module HexaPDF
|
|
|
188
185
|
lowercase ? result.downcase : result
|
|
189
186
|
end
|
|
190
187
|
|
|
191
|
-
# :nodoc:
|
|
192
|
-
ROMAN_NUMERAL_MAPPING = {
|
|
188
|
+
ROMAN_NUMERAL_MAPPING = { # :nodoc:
|
|
193
189
|
1000 => "M",
|
|
194
190
|
900 => "CM",
|
|
195
191
|
500 => "D",
|
data/lib/hexapdf/type.rb
CHANGED
|
@@ -76,6 +76,10 @@ module HexaPDF
|
|
|
76
76
|
autoload(:OutlineItem, 'hexapdf/type/outline_item')
|
|
77
77
|
autoload(:PageLabel, 'hexapdf/type/page_label')
|
|
78
78
|
autoload(:MarkInformation, 'hexapdf/type/mark_information')
|
|
79
|
+
autoload(:OptionalContentGroup, 'hexapdf/type/optional_content_group')
|
|
80
|
+
autoload(:OptionalContentMembership, 'hexapdf/type/optional_content_membership')
|
|
81
|
+
autoload(:OptionalContentProperties, 'hexapdf/type/optional_content_properties')
|
|
82
|
+
autoload(:OptionalContentConfiguration, 'hexapdf/type/optional_content_configuration')
|
|
79
83
|
|
|
80
84
|
end
|
|
81
85
|
|
|
@@ -52,7 +52,6 @@ module HexaPDF
|
|
|
52
52
|
# See: PDF2.0 s7.9.2, D.1, D.3
|
|
53
53
|
module PDFDocEncoding
|
|
54
54
|
|
|
55
|
-
# :nodoc:
|
|
56
55
|
CHARACTER_MAP = %W[\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
|
|
57
56
|
\uFFFD \u0009 \u000A \uFFFD \uFFFD \u000D \uFFFD \uFFFD
|
|
58
57
|
\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
|
data/lib/hexapdf/version.rb
CHANGED
|
@@ -71,6 +71,13 @@ describe HexaPDF::Content::Canvas do
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
describe "pos" do
|
|
75
|
+
it "returns the transformed position" do
|
|
76
|
+
@canvas.translate(9, 4)
|
|
77
|
+
assert_equal([10, 5], @canvas.pos(1, 1))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
74
81
|
describe "save_graphics_state" do
|
|
75
82
|
it "invokes the operator implementation" do
|
|
76
83
|
assert_operator_invoked(:q) { @canvas.save_graphics_state }
|
|
@@ -1280,6 +1287,48 @@ describe HexaPDF::Content::Canvas do
|
|
|
1280
1287
|
end
|
|
1281
1288
|
end
|
|
1282
1289
|
|
|
1290
|
+
describe "optional_content" do
|
|
1291
|
+
it "invokes the marked-sequence operator implementation" do
|
|
1292
|
+
assert_operator_invoked(:BDC, :OC, :P1) { @canvas.optional_content('Test') }
|
|
1293
|
+
end
|
|
1294
|
+
|
|
1295
|
+
it "is serialized correctly when no block is used" do
|
|
1296
|
+
@canvas.optional_content('Test')
|
|
1297
|
+
assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]]])
|
|
1298
|
+
end
|
|
1299
|
+
|
|
1300
|
+
it "is serialized correctly when a block is used" do
|
|
1301
|
+
@canvas.optional_content('Test') {}
|
|
1302
|
+
assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
|
|
1303
|
+
[:end_marked_content]])
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
it "uses the provided OCG dictionary" do
|
|
1307
|
+
ocg = @doc.optional_content.add_ocg('Test')
|
|
1308
|
+
@canvas.optional_content(ocg)
|
|
1309
|
+
assert_equal(ocg, @page.resources.property_list(:P1))
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
it "uses an existing OCG specified by name" do
|
|
1313
|
+
ocg = @doc.optional_content.add_ocg('Test')
|
|
1314
|
+
@canvas.optional_content('Test')
|
|
1315
|
+
assert_equal(ocg, @page.resources.property_list(:P1))
|
|
1316
|
+
end
|
|
1317
|
+
|
|
1318
|
+
it "creates an OCG if the named one doesn't yet exist" do
|
|
1319
|
+
@canvas.optional_content('Test')
|
|
1320
|
+
assert_equal(@doc.optional_content.ocg('Test'), @page.resources.property_list(:P1))
|
|
1321
|
+
end
|
|
1322
|
+
|
|
1323
|
+
it "always creates a new OCG if use_existing_ocg is false" do
|
|
1324
|
+
ocg = @doc.optional_content.add_ocg('Test')
|
|
1325
|
+
@canvas.optional_content('Test', use_existing_ocg: false)
|
|
1326
|
+
pl_item = @page.resources.property_list(:P1)
|
|
1327
|
+
refute_equal(ocg, pl_item)
|
|
1328
|
+
assert_equal(@doc.optional_content.ocgs.last, pl_item)
|
|
1329
|
+
end
|
|
1330
|
+
end
|
|
1331
|
+
|
|
1283
1332
|
describe "color_from_specification "do
|
|
1284
1333
|
it "accepts a color string" do
|
|
1285
1334
|
assert_equal([1, 0, 0], @canvas.color_from_specification("red").components)
|
|
@@ -219,6 +219,10 @@ describe HexaPDF::Document::Layout do
|
|
|
219
219
|
box = @layout.text_box("Test", box_style: :named)
|
|
220
220
|
assert_equal(20, box.style.font_size)
|
|
221
221
|
end
|
|
222
|
+
|
|
223
|
+
it "raises an error if the to-be-used style doesn't exist" do
|
|
224
|
+
assert_raises(HexaPDF::Error) { @layout.text_box("Test", style: :unknown) }
|
|
225
|
+
end
|
|
222
226
|
end
|
|
223
227
|
|
|
224
228
|
describe "formatted_text" do
|
|
@@ -305,9 +309,10 @@ describe HexaPDF::Document::Layout do
|
|
|
305
309
|
|
|
306
310
|
it "allows creating an inline box through a hash with a :box key" do
|
|
307
311
|
block = lambda {|item| item.box(:base, width: 5, height: 15) }
|
|
308
|
-
box = @layout.formatted_text_box([{box: :
|
|
312
|
+
box = @layout.formatted_text_box([{box: :column, columns: 1, width: 100, block: block}])
|
|
309
313
|
ibox = box.instance_variable_get(:@items).first
|
|
310
|
-
|
|
314
|
+
ibox.fit_wrapped_box(nil)
|
|
315
|
+
assert_equal(100, ibox.width)
|
|
311
316
|
assert_equal(15, ibox.height)
|
|
312
317
|
end
|
|
313
318
|
|
|
@@ -196,15 +196,15 @@ describe HexaPDF::Document::Pages do
|
|
|
196
196
|
end
|
|
197
197
|
|
|
198
198
|
it "works for multiple page label entries" do
|
|
199
|
-
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :
|
|
199
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :D}, 7, {S: :A}]}
|
|
200
200
|
result = @doc.pages.each_labelling_range.to_a
|
|
201
|
-
assert_equal([[0, 2, {S: :r}], [2, 5, {S: :
|
|
201
|
+
assert_equal([[0, 2, {S: :r}], [2, 5, {S: :D}], [7, 3, {S: :A}]],
|
|
202
202
|
result.map {|s, c, l| [s, c, l.value] })
|
|
203
203
|
end
|
|
204
204
|
|
|
205
205
|
it "returns a zero or negative count for the last range if there aren't enough pages" do
|
|
206
206
|
assert_equal(10, @doc.pages.count)
|
|
207
|
-
@doc.catalog[:PageLabels] = {Nums: [0, {S: :
|
|
207
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :D}, 10, {S: :r}]}
|
|
208
208
|
assert_equal(0, @doc.pages.each_labelling_range.to_a[-1][1])
|
|
209
209
|
@doc.catalog[:PageLabels][:Nums][2] = 11
|
|
210
210
|
assert_equal(-1, @doc.pages.each_labelling_range.to_a[-1][1])
|
|
@@ -221,19 +221,19 @@ describe HexaPDF::Document::Pages do
|
|
|
221
221
|
|
|
222
222
|
it "adds an entry for the range starting at 0 if it doesn't exist" do
|
|
223
223
|
label = @doc.pages.add_labelling_range(5)
|
|
224
|
-
assert_equal([{S: :
|
|
224
|
+
assert_equal([{S: :D}, label],
|
|
225
225
|
@doc.catalog.page_labels[:Nums].value.values_at(1, 3))
|
|
226
226
|
end
|
|
227
227
|
end
|
|
228
228
|
|
|
229
229
|
describe "delete_labelling_range" do
|
|
230
230
|
before do
|
|
231
|
-
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :
|
|
231
|
+
@doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :D}]}
|
|
232
232
|
end
|
|
233
233
|
|
|
234
234
|
it "deletes the labelling range for a given start index" do
|
|
235
235
|
label = @doc.pages.delete_labelling_range(5)
|
|
236
|
-
assert_equal({S: :
|
|
236
|
+
assert_equal({S: :D}, label)
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
it "deletes the labelling range for 0 if it is the last, together with the number tree" do
|
|
@@ -56,8 +56,8 @@ describe HexaPDF::Layout::Box do
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
it "allows setting custom properties" do
|
|
59
|
-
|
|
60
|
-
assert_equal({'key' => :value},
|
|
59
|
+
assert_equal({}, create_box(properties: nil).properties)
|
|
60
|
+
assert_equal({'key' => :value}, create_box(properties: {'key' => :value}).properties)
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -150,6 +150,10 @@ describe HexaPDF::Layout::Box do
|
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
describe "draw" do
|
|
153
|
+
before do
|
|
154
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
155
|
+
end
|
|
156
|
+
|
|
153
157
|
it "draws the box onto the canvas" do
|
|
154
158
|
box = create_box(width: 150, height: 130) do |canvas, _|
|
|
155
159
|
canvas.line_width(15)
|
|
@@ -161,7 +165,6 @@ describe HexaPDF::Layout::Box do
|
|
|
161
165
|
box.style.underlays.add {|canvas, _| canvas.line_width(10) }
|
|
162
166
|
box.style.overlays.add {|canvas, _| canvas.line_width(20) }
|
|
163
167
|
|
|
164
|
-
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
165
168
|
box.draw(@canvas, 5, 5)
|
|
166
169
|
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
167
170
|
[:set_graphics_state_parameters, [:GS1]],
|
|
@@ -195,7 +198,6 @@ describe HexaPDF::Layout::Box do
|
|
|
195
198
|
end
|
|
196
199
|
|
|
197
200
|
it "draws nothing onto the canvas if the box is empty" do
|
|
198
|
-
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
199
201
|
box = create_box
|
|
200
202
|
box.draw(@canvas, 5, 5)
|
|
201
203
|
assert_operators(@canvas.contents, [])
|
|
@@ -204,6 +206,13 @@ describe HexaPDF::Layout::Box do
|
|
|
204
206
|
refute(box.style.border?)
|
|
205
207
|
refute(box.style.overlays?)
|
|
206
208
|
end
|
|
209
|
+
|
|
210
|
+
it "wraps the box in optional content markers if the optional_content property is set" do
|
|
211
|
+
box = create_box(properties: {'optional_content' => 'Text'})
|
|
212
|
+
box.draw(@canvas, 0, 0)
|
|
213
|
+
assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
|
|
214
|
+
[:end_marked_content]])
|
|
215
|
+
end
|
|
207
216
|
end
|
|
208
217
|
|
|
209
218
|
describe "empty?" do
|
|
@@ -5,7 +5,7 @@ require 'hexapdf/layout/frame'
|
|
|
5
5
|
require 'hexapdf/layout/box'
|
|
6
6
|
require 'hexapdf/document'
|
|
7
7
|
|
|
8
|
-
describe HexaPDF::Layout::Frame do
|
|
8
|
+
describe HexaPDF::Layout::Frame::FitResult do
|
|
9
9
|
it "shows the box's mask area on #draw when using debug output" do
|
|
10
10
|
doc = HexaPDF::Document.new(config: {'debug' => true})
|
|
11
11
|
canvas = doc.pages.add.canvas
|
|
@@ -15,6 +15,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
15
15
|
result.x = result.y = 0
|
|
16
16
|
result.draw(canvas)
|
|
17
17
|
assert_equal(<<~CONTENTS, canvas.contents)
|
|
18
|
+
/OC /P1 BDC
|
|
18
19
|
q
|
|
19
20
|
0.0 0.501961 0.0 rg
|
|
20
21
|
0.0 0.392157 0.0 RG
|
|
@@ -22,10 +23,13 @@ describe HexaPDF::Layout::Frame do
|
|
|
22
23
|
0 0 20 20 re
|
|
23
24
|
B
|
|
24
25
|
Q
|
|
26
|
+
EMC
|
|
25
27
|
q
|
|
26
28
|
1 0 0 1 0 0 cm
|
|
27
29
|
Q
|
|
28
30
|
CONTENTS
|
|
31
|
+
ocg = doc.optional_content.ocgs.first
|
|
32
|
+
assert_equal([['Debug', ocg]], doc.optional_content.default_configuration[:Order])
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
|
|
@@ -34,6 +38,14 @@ describe HexaPDF::Layout::Frame do
|
|
|
34
38
|
@frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
|
|
35
39
|
end
|
|
36
40
|
|
|
41
|
+
it "allows accessing the context's document" do
|
|
42
|
+
assert_nil(@frame.document)
|
|
43
|
+
context = Minitest::Mock.new
|
|
44
|
+
context.expect(:document, :document)
|
|
45
|
+
assert_equal(:document, HexaPDF::Layout::Frame.new(0, 0, 10, 10, context: context).document)
|
|
46
|
+
context.verify
|
|
47
|
+
end
|
|
48
|
+
|
|
37
49
|
it "allows access to the bounding box attributes" do
|
|
38
50
|
assert_equal(5, @frame.left)
|
|
39
51
|
assert_equal(10, @frame.bottom)
|
|
@@ -24,25 +24,28 @@ describe HexaPDF::Layout::InlineBox do
|
|
|
24
24
|
assert_equal(:top, ibox.valign)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
it "fails if the wrapped box has not width set" do
|
|
28
|
+
box = HexaPDF::Document.new.layout.text("test is not going good")
|
|
29
|
+
assert_raises(HexaPDF::Error) { inline_box(box) }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "fit_wrapped_box" do
|
|
27
34
|
it "automatically fits the provided box into a frame" do
|
|
28
35
|
ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good", width: 20))
|
|
36
|
+
ibox.fit_wrapped_box(nil)
|
|
29
37
|
assert_equal(20, ibox.width)
|
|
30
38
|
assert_equal(45, ibox.height)
|
|
31
39
|
end
|
|
32
40
|
|
|
33
|
-
it "fails if the wrapped box has not width set" do
|
|
34
|
-
box = HexaPDF::Document.new.layout.text("test is not going good")
|
|
35
|
-
assert_raises(HexaPDF::Error) { inline_box(box) }
|
|
36
|
-
end
|
|
37
|
-
|
|
38
41
|
it "fails if the wrapped box could not be fit" do
|
|
39
42
|
box = HexaPDF::Document.new.layout.text("test is not going good", width: 1)
|
|
40
|
-
assert_raises(HexaPDF::Error) { inline_box(box) }
|
|
43
|
+
assert_raises(HexaPDF::Error) { inline_box(box).fit_wrapped_box(nil) }
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
it "fails if the height is not set explicitly and during fitting" do
|
|
44
47
|
assert_raises(HexaPDF::Error) do
|
|
45
|
-
inline_box(HexaPDF::Layout::Box.create(width: 10))
|
|
48
|
+
inline_box(HexaPDF::Layout::Box.create(width: 10)).fit_wrapped_box(nil)
|
|
46
49
|
end
|
|
47
50
|
end
|
|
48
51
|
end
|
|
@@ -50,7 +53,9 @@ describe HexaPDF::Layout::InlineBox do
|
|
|
50
53
|
it "draws the wrapped box at the correct position" do
|
|
51
54
|
doc = HexaPDF::Document.new
|
|
52
55
|
canvas = doc.pages.add.canvas
|
|
53
|
-
inline_box(doc.layout.text("", width: 20, margin: [15, 10]))
|
|
56
|
+
box = inline_box(doc.layout.text("", width: 20, margin: [15, 10]))
|
|
57
|
+
box.fit_wrapped_box(nil)
|
|
58
|
+
box.draw(canvas, 100, 200)
|
|
54
59
|
assert_equal("q\n1 0 0 1 110 -99785 cm\nQ\n", canvas.contents)
|
|
55
60
|
end
|
|
56
61
|
|
|
@@ -59,6 +64,10 @@ describe HexaPDF::Layout::InlineBox do
|
|
|
59
64
|
refute(HexaPDF::Layout::InlineBox.create(width: 10, height: 15) {}.empty?)
|
|
60
65
|
end
|
|
61
66
|
|
|
67
|
+
it "returns the style of the box" do
|
|
68
|
+
assert_same(@box.box.style, @box.style)
|
|
69
|
+
end
|
|
70
|
+
|
|
62
71
|
describe "valign" do
|
|
63
72
|
it "has a default value of :baseline" do
|
|
64
73
|
assert_equal(:baseline, @box.valign)
|
|
@@ -6,7 +6,9 @@ require 'hexapdf/layout/list_box'
|
|
|
6
6
|
|
|
7
7
|
describe HexaPDF::Layout::ListBox do
|
|
8
8
|
before do
|
|
9
|
-
@
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@page = @doc.pages.add
|
|
11
|
+
@frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100, context: @page)
|
|
10
12
|
inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {}
|
|
11
13
|
@text_boxes = 5.times.map do
|
|
12
14
|
HexaPDF::Layout::TextBox.new(items: [inline_box] * 15, style: {position: :default})
|
|
@@ -23,8 +25,8 @@ describe HexaPDF::Layout::ListBox do
|
|
|
23
25
|
assert_equal(height, box.height, "box height")
|
|
24
26
|
if fit_pos
|
|
25
27
|
results = box.instance_variable_get(:@results)
|
|
26
|
-
results.each_with_index do |
|
|
27
|
-
box_fitter.fit_results.each_with_index do |fit_result, result_index|
|
|
28
|
+
results.each_with_index do |item_result, item_index|
|
|
29
|
+
item_result.box_fitter.fit_results.each_with_index do |fit_result, result_index|
|
|
28
30
|
x, y = fit_pos.shift
|
|
29
31
|
assert_equal(x, fit_result.x, "item #{item_index}, result #{result_index}, x")
|
|
30
32
|
assert_equal(y, fit_result.y, "item #{item_index}, result #{result_index}, y")
|
|
@@ -55,7 +57,7 @@ describe HexaPDF::Layout::ListBox do
|
|
|
55
57
|
it "is empty if nothing could be fit" do
|
|
56
58
|
box = create_box(children: [@text_boxes[0]], width: 5)
|
|
57
59
|
box.fit(@frame.available_width, @frame.available_height, @frame)
|
|
58
|
-
assert(
|
|
60
|
+
assert(box.empty?)
|
|
59
61
|
end
|
|
60
62
|
end
|
|
61
63
|
|
|
@@ -90,6 +92,12 @@ describe HexaPDF::Layout::ListBox do
|
|
|
90
92
|
check_box(box, 100, 90, [[10, 80], [10, 60], [10, 40], [50, 10]])
|
|
91
93
|
end
|
|
92
94
|
|
|
95
|
+
it "calculates the correct height if the marker is higher than the content" do
|
|
96
|
+
box = create_box(children: @text_boxes[0, 1], content_indentation: 20,
|
|
97
|
+
style: {font_size: 30})
|
|
98
|
+
check_box(box, 100, 27, [[20, 80]])
|
|
99
|
+
end
|
|
100
|
+
|
|
93
101
|
it "respects the content indentation" do
|
|
94
102
|
box = create_box(children: @text_boxes[0, 1], content_indentation: 30)
|
|
95
103
|
check_box(box, 100, 30, [[30, 70]])
|
|
@@ -99,6 +107,11 @@ describe HexaPDF::Layout::ListBox do
|
|
|
99
107
|
box = create_box(children: @text_boxes[0, 2], item_spacing: 30)
|
|
100
108
|
check_box(box, 100, 70, [[10, 80], [10, 30]])
|
|
101
109
|
end
|
|
110
|
+
|
|
111
|
+
it "fails for unknown item types" do
|
|
112
|
+
box = create_box(children: @text_boxes[0, 1], item_type: :unknown)
|
|
113
|
+
assert_raises(HexaPDF::Error) { box.fit(100, 100, @frame) }
|
|
114
|
+
end
|
|
102
115
|
end
|
|
103
116
|
|
|
104
117
|
describe "split" do
|
|
@@ -108,7 +121,7 @@ describe HexaPDF::Layout::ListBox do
|
|
|
108
121
|
box_a, box_b = box.split(100, 100, @frame)
|
|
109
122
|
assert_same(box, box_a)
|
|
110
123
|
assert_equal(:show_first_marker, box_b.split_box?)
|
|
111
|
-
assert_equal(1, box_a.instance_variable_get(:@results)[0].fit_results.size)
|
|
124
|
+
assert_equal(1, box_a.instance_variable_get(:@results)[0].box_fitter.fit_results.size)
|
|
112
125
|
assert_equal(1, box_b.children.size)
|
|
113
126
|
assert_equal(2, box_b.start_number)
|
|
114
127
|
end
|
|
@@ -119,7 +132,7 @@ describe HexaPDF::Layout::ListBox do
|
|
|
119
132
|
box_a, box_b = box.split(100, 100, @frame)
|
|
120
133
|
assert_same(box, box_a)
|
|
121
134
|
assert_equal(:hide_first_marker, box_b.split_box?)
|
|
122
|
-
assert_equal(1, box_a.instance_variable_get(:@results)[0].fit_results.size)
|
|
135
|
+
assert_equal(1, box_a.instance_variable_get(:@results)[0].box_fitter.fit_results.size)
|
|
123
136
|
assert_equal(2, box_b.children.size)
|
|
124
137
|
assert_equal(1, box_b.start_number)
|
|
125
138
|
end
|
|
@@ -127,20 +140,22 @@ describe HexaPDF::Layout::ListBox do
|
|
|
127
140
|
|
|
128
141
|
describe "draw" do
|
|
129
142
|
before do
|
|
130
|
-
@canvas =
|
|
143
|
+
@canvas = @page.canvas
|
|
131
144
|
draw_block = lambda {|canvas, box| }
|
|
132
145
|
@fixed_size_boxes = 5.times.map { HexaPDF::Layout::Box.new(width: 20, height: 10, &draw_block) }
|
|
133
146
|
end
|
|
134
147
|
|
|
135
148
|
it "draws the result" do
|
|
136
|
-
box = create_box(children: @fixed_size_boxes[0, 2]
|
|
149
|
+
box = create_box(children: @fixed_size_boxes[0, 2],
|
|
150
|
+
style: {font_size: 11, fill_color: 0.5})
|
|
137
151
|
box.fit(100, 100, @frame)
|
|
138
152
|
box.draw(@canvas, 0, 100 - box.height)
|
|
139
153
|
operators = [
|
|
140
154
|
[:save_graphics_state],
|
|
141
|
-
[:set_font_and_size, [:F1,
|
|
155
|
+
[:set_font_and_size, [:F1, 11]],
|
|
156
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
142
157
|
[:begin_text],
|
|
143
|
-
[:set_text_matrix, [1, 0, 0, 1, 1.
|
|
158
|
+
[:set_text_matrix, [1, 0, 0, 1, 1.15, 92.487]],
|
|
144
159
|
[:show_text, ["\x95".b]],
|
|
145
160
|
[:end_text],
|
|
146
161
|
[:restore_graphics_state],
|
|
@@ -149,9 +164,10 @@ describe HexaPDF::Layout::ListBox do
|
|
|
149
164
|
[:restore_graphics_state],
|
|
150
165
|
|
|
151
166
|
[:save_graphics_state],
|
|
152
|
-
[:set_font_and_size, [:F1,
|
|
167
|
+
[:set_font_and_size, [:F1, 11]],
|
|
168
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
153
169
|
[:begin_text],
|
|
154
|
-
[:set_text_matrix, [1, 0, 0, 1, 1.
|
|
170
|
+
[:set_text_matrix, [1, 0, 0, 1, 1.15, 82.487]],
|
|
155
171
|
[:show_text, ["\x95".b]],
|
|
156
172
|
[:end_text],
|
|
157
173
|
[:restore_graphics_state],
|
|
@@ -162,16 +178,18 @@ describe HexaPDF::Layout::ListBox do
|
|
|
162
178
|
assert_operators(@canvas.contents, operators)
|
|
163
179
|
end
|
|
164
180
|
|
|
165
|
-
it "draws a
|
|
166
|
-
box = create_box(children: @fixed_size_boxes[0, 1], item_type: :circle
|
|
181
|
+
it "draws a circle as marker" do
|
|
182
|
+
box = create_box(children: @fixed_size_boxes[0, 1], item_type: :circle,
|
|
183
|
+
style: {font_size: 11, fill_color: 0.5})
|
|
167
184
|
box.fit(100, 100, @frame)
|
|
168
185
|
box.draw(@canvas, 0, 100 - box.height)
|
|
169
186
|
operators = [
|
|
170
187
|
[:save_graphics_state],
|
|
171
|
-
[:set_font_and_size, [:F1, 5]],
|
|
172
|
-
[:set_text_rise, [-
|
|
188
|
+
[:set_font_and_size, [:F1, 5.5]],
|
|
189
|
+
[:set_text_rise, [-6.111111]],
|
|
190
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
173
191
|
[:begin_text],
|
|
174
|
-
[:set_text_matrix, [1, 0, 0, 1, 0.
|
|
192
|
+
[:set_text_matrix, [1, 0, 0, 1, 0.1985, 100]],
|
|
175
193
|
[:show_text, ["m".b]],
|
|
176
194
|
[:end_text],
|
|
177
195
|
[:restore_graphics_state],
|
|
@@ -183,15 +201,17 @@ describe HexaPDF::Layout::ListBox do
|
|
|
183
201
|
end
|
|
184
202
|
|
|
185
203
|
it "draws a square as marker" do
|
|
186
|
-
box = create_box(children: @fixed_size_boxes[0, 1], item_type: :square
|
|
204
|
+
box = create_box(children: @fixed_size_boxes[0, 1], item_type: :square,
|
|
205
|
+
style: {font_size: 11, fill_color: 0.5})
|
|
187
206
|
box.fit(100, 100, @frame)
|
|
188
207
|
box.draw(@canvas, 0, 100 - box.height)
|
|
189
208
|
operators = [
|
|
190
209
|
[:save_graphics_state],
|
|
191
|
-
[:set_font_and_size, [:F1, 5]],
|
|
192
|
-
[:set_text_rise, [-
|
|
210
|
+
[:set_font_and_size, [:F1, 5.5]],
|
|
211
|
+
[:set_text_rise, [-6.111111]],
|
|
212
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
193
213
|
[:begin_text],
|
|
194
|
-
[:set_text_matrix, [1, 0, 0, 1,
|
|
214
|
+
[:set_text_matrix, [1, 0, 0, 1, 0.8145, 100]],
|
|
195
215
|
[:show_text, ["n".b]],
|
|
196
216
|
[:end_text],
|
|
197
217
|
[:restore_graphics_state],
|
|
@@ -204,14 +224,16 @@ describe HexaPDF::Layout::ListBox do
|
|
|
204
224
|
|
|
205
225
|
it "draws decimal numbers as marker" do
|
|
206
226
|
box = create_box(children: @fixed_size_boxes[0, 2], item_type: :decimal,
|
|
227
|
+
style: {font_size: 11, fill_color: 0.5},
|
|
207
228
|
content_indentation: 20)
|
|
208
229
|
box.fit(100, 100, @frame)
|
|
209
230
|
box.draw(@canvas, 0, 100 - box.height)
|
|
210
231
|
operators = [
|
|
211
232
|
[:save_graphics_state],
|
|
212
|
-
[:set_font_and_size, [:F1,
|
|
233
|
+
[:set_font_and_size, [:F1, 11]],
|
|
234
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
213
235
|
[:begin_text],
|
|
214
|
-
[:set_text_matrix, [1, 0, 0, 1,
|
|
236
|
+
[:set_text_matrix, [1, 0, 0, 1, 6.75, 92.487]],
|
|
215
237
|
[:show_text, ["1.".b]],
|
|
216
238
|
[:end_text],
|
|
217
239
|
[:restore_graphics_state],
|
|
@@ -220,9 +242,10 @@ describe HexaPDF::Layout::ListBox do
|
|
|
220
242
|
[:restore_graphics_state],
|
|
221
243
|
|
|
222
244
|
[:save_graphics_state],
|
|
223
|
-
[:set_font_and_size, [:F1,
|
|
245
|
+
[:set_font_and_size, [:F1, 11]],
|
|
246
|
+
[:set_device_gray_non_stroking_color, [0.5]],
|
|
224
247
|
[:begin_text],
|
|
225
|
-
[:set_text_matrix, [1, 0, 0, 1,
|
|
248
|
+
[:set_text_matrix, [1, 0, 0, 1, 6.75, 82.487]],
|
|
226
249
|
[:show_text, ["2.".b]],
|
|
227
250
|
[:end_text],
|
|
228
251
|
[:restore_graphics_state],
|
|
@@ -272,11 +295,5 @@ describe HexaPDF::Layout::ListBox do
|
|
|
272
295
|
]
|
|
273
296
|
assert_operators(@canvas.contents, operators)
|
|
274
297
|
end
|
|
275
|
-
|
|
276
|
-
it "fails for unknown item types" do
|
|
277
|
-
box = create_box(children: @fixed_size_boxes[0, 1], item_type: :unknown)
|
|
278
|
-
box.fit(100, 100, @frame)
|
|
279
|
-
assert_raises(HexaPDF::Error) { box.draw(@canvas, 0, 0) }
|
|
280
|
-
end
|
|
281
298
|
end
|
|
282
299
|
end
|
|
@@ -580,7 +580,10 @@ describe HexaPDF::Layout::Style::LinkLayer do
|
|
|
580
580
|
it "fails if more than one possible target is chosen" do
|
|
581
581
|
assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(dest: true, uri: true) }
|
|
582
582
|
assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(dest: true, file: true) }
|
|
583
|
+
assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(dest: true, action: true) }
|
|
583
584
|
assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(uri: true, file: true) }
|
|
585
|
+
assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(uri: true, action: true) }
|
|
586
|
+
assert_raises(ArgumentError) { HexaPDF::Layout::Style::LinkLayer.new(file: true, action: true) }
|
|
584
587
|
end
|
|
585
588
|
|
|
586
589
|
it "fails if an invalid border is provided" do
|
|
@@ -656,6 +659,12 @@ describe HexaPDF::Layout::Style::LinkLayer do
|
|
|
656
659
|
assert_nil(annot[:Dest])
|
|
657
660
|
end
|
|
658
661
|
|
|
662
|
+
it "works for actions" do
|
|
663
|
+
annot = call_link(action: {Type: :Action, S: :SetOCGState})
|
|
664
|
+
assert_equal({Type: :Action, S: :SetOCGState}, annot[:A].value)
|
|
665
|
+
assert_nil(annot[:Dest])
|
|
666
|
+
end
|
|
667
|
+
|
|
659
668
|
it "works for destinations set via the 'link' custom box property" do
|
|
660
669
|
@box.properties['link'] = [@canvas.context, :FitH]
|
|
661
670
|
annot = call_link({})
|
|
@@ -773,6 +782,7 @@ describe HexaPDF::Layout::Style do
|
|
|
773
782
|
refute(@style.subscript)
|
|
774
783
|
refute(@style.superscript)
|
|
775
784
|
refute(@style.last_line_gap)
|
|
785
|
+
refute(@style.fill_horizontal)
|
|
776
786
|
assert_kind_of(HexaPDF::Layout::Style::Layers, @style.underlays)
|
|
777
787
|
assert_kind_of(HexaPDF::Layout::Style::Layers, @style.overlays)
|
|
778
788
|
end
|