hexapdf 0.33.0 → 0.34.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -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 +3 -2
- data/lib/hexapdf/configuration.rb +21 -1
- data/lib/hexapdf/content/canvas.rb +52 -0
- data/lib/hexapdf/content/operator.rb +3 -1
- 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 +40 -16
- 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/content/test_operator.rb +3 -1
- 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 +30 -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 +7 -5
- metadata +14 -3
@@ -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
|
@@ -5,6 +5,10 @@ require 'hexapdf/document'
|
|
5
5
|
require 'hexapdf/layout/table_box'
|
6
6
|
|
7
7
|
describe HexaPDF::Layout::TableBox::Cell do
|
8
|
+
before do
|
9
|
+
@frame = HexaPDF::Layout::Frame.new(0, 0, 0, 0)
|
10
|
+
end
|
11
|
+
|
8
12
|
def create_cell(**kwargs)
|
9
13
|
HexaPDF::Layout::TableBox::Cell.new(row: 1, column: 1, **kwargs)
|
10
14
|
end
|
@@ -46,7 +50,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
46
50
|
|
47
51
|
it "returns true if the cell has no content" do
|
48
52
|
cell = create_cell(children: nil, style: {border: {width: 0}})
|
49
|
-
cell.fit(100, 100,
|
53
|
+
cell.fit(100, 100, @frame)
|
50
54
|
assert(cell.empty?)
|
51
55
|
end
|
52
56
|
end
|
@@ -54,7 +58,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
54
58
|
describe "update_height" do
|
55
59
|
it "updates the height to the correct one" do
|
56
60
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 10, height: 10))
|
57
|
-
cell.fit(100, 100,
|
61
|
+
cell.fit(100, 100, @frame)
|
58
62
|
assert_equal(22, cell.height)
|
59
63
|
cell.update_height(50)
|
60
64
|
assert_equal(50, cell.height)
|
@@ -62,7 +66,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
62
66
|
|
63
67
|
it "fails if the given height is smaller than the one determined during #fit" do
|
64
68
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 10, height: 10))
|
65
|
-
cell.fit(100, 100,
|
69
|
+
cell.fit(100, 100, @frame)
|
66
70
|
err = assert_raises(HexaPDF::Error) { cell.update_height(5) }
|
67
71
|
assert_match(/at least as big/, err.message)
|
68
72
|
end
|
@@ -71,7 +75,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
71
75
|
describe "fit" do
|
72
76
|
it "fits a single box" do
|
73
77
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 10))
|
74
|
-
cell.fit(100, 100,
|
78
|
+
cell.fit(100, 100, @frame)
|
75
79
|
assert_equal(100, cell.width)
|
76
80
|
assert_equal(22, cell.height)
|
77
81
|
assert_equal(32, cell.preferred_width)
|
@@ -80,7 +84,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
80
84
|
|
81
85
|
it "fits a single box with horizontal aligning not being :left" do
|
82
86
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 10, position_hint: :center))
|
83
|
-
cell.fit(100, 100,
|
87
|
+
cell.fit(100, 100, @frame)
|
84
88
|
assert_equal(66, cell.preferred_width)
|
85
89
|
end
|
86
90
|
|
@@ -88,7 +92,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
88
92
|
box1 = HexaPDF::Layout::Box.create(width: 20, height: 10)
|
89
93
|
box2 = HexaPDF::Layout::Box.create(width: 50, height: 15)
|
90
94
|
cell = create_cell(children: [box1, box2])
|
91
|
-
cell.fit(100, 100,
|
95
|
+
cell.fit(100, 100, @frame)
|
92
96
|
assert_equal(100, cell.width)
|
93
97
|
assert_equal(37, cell.height)
|
94
98
|
assert_equal(62, cell.preferred_width)
|
@@ -99,13 +103,13 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
99
103
|
box1 = HexaPDF::Layout::Box.create(width: 20, height: 10, position_hint: :center)
|
100
104
|
box2 = HexaPDF::Layout::Box.create(width: 50, height: 15)
|
101
105
|
cell = create_cell(children: [box1, box2])
|
102
|
-
cell.fit(100, 100,
|
106
|
+
cell.fit(100, 100, @frame)
|
103
107
|
assert_equal(66, cell.preferred_width)
|
104
108
|
end
|
105
109
|
|
106
110
|
it "fits the cell even if it has no content" do
|
107
111
|
cell = create_cell(children: nil)
|
108
|
-
cell.fit(100, 100,
|
112
|
+
cell.fit(100, 100, @frame)
|
109
113
|
assert_equal(100, cell.width)
|
110
114
|
assert_equal(12, cell.height)
|
111
115
|
assert_equal(12, cell.preferred_width)
|
@@ -114,8 +118,8 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
114
118
|
|
115
119
|
it "doesn't fit anything if the available width or height are too small" do
|
116
120
|
cell = create_cell(children: nil)
|
117
|
-
refute(cell.fit(10, 100,
|
118
|
-
refute(cell.fit(100, 10,
|
121
|
+
refute(cell.fit(10, 100, @frame))
|
122
|
+
refute(cell.fit(100, 10, @frame))
|
119
123
|
end
|
120
124
|
end
|
121
125
|
|
@@ -129,7 +133,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
129
133
|
box1 = HexaPDF::Layout::Box.create(width: 20, height: 10, position_hint: :center, &draw_block)
|
130
134
|
box2 = HexaPDF::Layout::Box.create(width: 50, height: 15, &draw_block)
|
131
135
|
box = create_cell(children: [box1, box2])
|
132
|
-
box.fit(100, 100,
|
136
|
+
box.fit(100, 100, @frame)
|
133
137
|
box.draw(@canvas, 10, 75)
|
134
138
|
operators = [[:save_graphics_state],
|
135
139
|
[:append_rectangle, [9.5, 74.5, 101.0, 38.0]],
|
@@ -153,7 +157,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
153
157
|
|
154
158
|
it "works for a cell without content" do
|
155
159
|
box = create_cell(children: nil, style: {border: {width: 0}})
|
156
|
-
box.fit(100, 100,
|
160
|
+
box.fit(100, 100, @frame)
|
157
161
|
box.draw(@canvas, 10, 75)
|
158
162
|
assert_operators(@canvas.contents, [])
|
159
163
|
end
|
@@ -355,7 +359,9 @@ end
|
|
355
359
|
|
356
360
|
describe HexaPDF::Layout::TableBox do
|
357
361
|
before do
|
358
|
-
@
|
362
|
+
@doc = HexaPDF::Document.new
|
363
|
+
@page = @doc.pages.add
|
364
|
+
@frame = HexaPDF::Layout::Frame.new(0, 0, 160, 100, context: @page)
|
359
365
|
draw_block = lambda {|canvas, _box| canvas.move_to(0, 0).end_path }
|
360
366
|
@fixed_size_boxes = 15.times.map { HexaPDF::Layout::Box.new(width: 20, height: 10, &draw_block) }
|
361
367
|
end
|
@@ -425,7 +431,7 @@ describe HexaPDF::Layout::TableBox do
|
|
425
431
|
footer = lambda {|_| [[nil]] }
|
426
432
|
box = create_box(header: header, footer: footer, cells: [[nil], [nil]],
|
427
433
|
cell_style: {background_color: 'black'})
|
428
|
-
refute(box.fit(100, 40,
|
434
|
+
refute(box.fit(100, 40, @frame))
|
429
435
|
box_a, box_b = box.split(100, 40, nil)
|
430
436
|
assert_same(box_a, box)
|
431
437
|
assert_equal('black', box_b.header_cells[0, 0].style.background_color)
|
@@ -544,8 +550,8 @@ describe HexaPDF::Layout::TableBox do
|
|
544
550
|
describe "split" do
|
545
551
|
it "splits the table if some rows could not be fit into the available region" do
|
546
552
|
box = create_box
|
547
|
-
refute(box.fit(100, 25,
|
548
|
-
box_a, box_b = box.split(100, 25,
|
553
|
+
refute(box.fit(100, 25, @frame))
|
554
|
+
box_a, box_b = box.split(100, 25, @frame)
|
549
555
|
assert_same(box_a, box)
|
550
556
|
assert(box_b.split_box?)
|
551
557
|
|
@@ -560,8 +566,8 @@ describe HexaPDF::Layout::TableBox do
|
|
560
566
|
[{header: cells_creator}, {footer: cells_creator}].each do |args|
|
561
567
|
box = create_box(**args)
|
562
568
|
box.cells.style(padding: 0, border: {width: 0})
|
563
|
-
refute(box.fit(100, 25,
|
564
|
-
box_a, box_b = box.split(100, 25,
|
569
|
+
refute(box.fit(100, 25, @frame))
|
570
|
+
box_a, box_b = box.split(100, 25, @frame)
|
565
571
|
assert_nil(box_a)
|
566
572
|
assert_same(box_b, box)
|
567
573
|
end
|
@@ -571,8 +577,8 @@ describe HexaPDF::Layout::TableBox do
|
|
571
577
|
cells_creator = lambda {|_| [@fixed_size_boxes[10, 2]] }
|
572
578
|
[{header: cells_creator}, {footer: cells_creator}].each do |args|
|
573
579
|
box = create_box(**args)
|
574
|
-
refute(box.fit(100, 50,
|
575
|
-
box_a, box_b = box.split(100, 50,
|
580
|
+
refute(box.fit(100, 50, @frame))
|
581
|
+
box_a, box_b = box.split(100, 50, @frame)
|
576
582
|
assert_same(box_a, box)
|
577
583
|
|
578
584
|
assert_equal(0, box_a.start_row_index)
|
@@ -591,12 +597,12 @@ describe HexaPDF::Layout::TableBox do
|
|
591
597
|
|
592
598
|
describe "draw_content" do
|
593
599
|
before do
|
594
|
-
@canvas =
|
600
|
+
@canvas = @page.canvas
|
595
601
|
end
|
596
602
|
|
597
603
|
it "draws the result onto the canvas" do
|
598
604
|
box = create_box
|
599
|
-
box.fit(100, 100,
|
605
|
+
box.fit(100, 100, @frame)
|
600
606
|
box.draw(@canvas, 20, 10)
|
601
607
|
operators = [[:save_graphics_state],
|
602
608
|
[:append_rectangle, [20.0, 32.0, 50.5, 23.0]],
|
@@ -651,9 +657,9 @@ describe HexaPDF::Layout::TableBox do
|
|
651
657
|
|
652
658
|
it "correctly works for split boxes" do
|
653
659
|
box = create_box(cell_style: {padding: 0, border: {width: 0}})
|
654
|
-
refute(box.fit(100, 10,
|
655
|
-
_, split_box = box.split(100, 10,
|
656
|
-
assert(split_box.fit(100, 100,
|
660
|
+
refute(box.fit(100, 10, @frame))
|
661
|
+
_, split_box = box.split(100, 10, @frame)
|
662
|
+
assert(split_box.fit(100, 100, @frame))
|
657
663
|
|
658
664
|
box.draw(@canvas, 20, 10)
|
659
665
|
split_box.draw(@canvas, 0, 50)
|
@@ -684,7 +690,7 @@ describe HexaPDF::Layout::TableBox do
|
|
684
690
|
box = create_box(header: lambda {|_| [@fixed_size_boxes[10, 1]] },
|
685
691
|
footer: lambda {|_| [@fixed_size_boxes[12, 1]] },
|
686
692
|
cell_style: {padding: 0, border: {width: 0}})
|
687
|
-
box.fit(100, 100,
|
693
|
+
box.fit(100, 100, @frame)
|
688
694
|
box.draw(@canvas, 20, 10)
|
689
695
|
operators = [[:save_graphics_state],
|
690
696
|
[:concatenate_matrix, [1, 0, 0, 1, 20, 40]],
|
@@ -25,6 +25,14 @@ describe HexaPDF::Layout::TextBox do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
it "returns the text contents as string" do
|
29
|
+
doc = HexaPDF::Document.new
|
30
|
+
font = doc.fonts.add("Times")
|
31
|
+
box = create_box([HexaPDF::Layout::TextFragment.create('Test ', font: font), @inline_box,
|
32
|
+
HexaPDF::Layout::TextFragment.create('here', font: font)])
|
33
|
+
assert_equal('Test here', box.text)
|
34
|
+
end
|
35
|
+
|
28
36
|
describe "fit" do
|
29
37
|
it "fits into a rectangular area" do
|
30
38
|
box = create_box([@inline_box] * 5, style: {padding: 10})
|
@@ -51,6 +51,10 @@ describe HexaPDF::Layout::TextFragment do
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
it "returns the text value of the items as string" do
|
55
|
+
assert_equal("Hal lo\u{00a0}d\n", setup_fragment(@font.decode_utf8("Hal lo\u{00a0}d\n")).text)
|
56
|
+
end
|
57
|
+
|
54
58
|
it "allows duplicating with only its attributes while also setting new items" do
|
55
59
|
setup_fragment([20])
|
56
60
|
@fragment.properties['key'] = :value
|
@@ -392,6 +396,35 @@ describe HexaPDF::Layout::TextFragment do
|
|
392
396
|
end
|
393
397
|
end
|
394
398
|
|
399
|
+
describe "fill_horizontal!" do
|
400
|
+
before do
|
401
|
+
@fragment = HexaPDF::Layout::TextFragment.create('ab', fill_horizontal: 1, font: @font)
|
402
|
+
end
|
403
|
+
|
404
|
+
it "returns the fragment if the given width is too small" do
|
405
|
+
assert_same(@fragment, @fragment.fill_horizontal!(0.1))
|
406
|
+
end
|
407
|
+
|
408
|
+
it "repeats all items of the fragment" do
|
409
|
+
fragment = @fragment.fill_horizontal!(@fragment.width * 2)
|
410
|
+
assert_equal([*(@fragment.items * 2), 0], fragment.items)
|
411
|
+
assert_in_delta(@fragment.width * 2, fragment.width)
|
412
|
+
end
|
413
|
+
|
414
|
+
it "adds, after repeating, items from the start of the fragment to fill the available space" do
|
415
|
+
fragment = @fragment.fill_horizontal!(90)
|
416
|
+
assert_equal([*(@fragment.items * 9), @fragment.items[0], 3.3333333333332673], fragment.items)
|
417
|
+
assert_in_delta(90, fragment.width)
|
418
|
+
end
|
419
|
+
|
420
|
+
it "sets the character spacing correctly to account for the remaining space after filling with items" do
|
421
|
+
fragment = @fragment.fill_horizontal!(90)
|
422
|
+
refute_same(@fragment.style, fragment.style)
|
423
|
+
assert_equal(0.033333333333332674, fragment.style.character_spacing)
|
424
|
+
assert_in_delta(90, fragment.width)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
395
428
|
it "can be inspected" do
|
396
429
|
frag = setup_fragment(@font.decode_utf8("H") << 5)
|
397
430
|
assert_match(/:H/, frag.inspect)
|
@@ -262,7 +262,7 @@ module CommonLineWrappingTests
|
|
262
262
|
end
|
263
263
|
|
264
264
|
it "handles prohibited breakpoint penalties with non-zero width" do
|
265
|
-
item = boxes(20).first
|
265
|
+
item = boxes(20).first.item
|
266
266
|
result = call(boxes(70) + [glue(10)] + boxes(10) + [penalty(5000, item)] + boxes(30))
|
267
267
|
assert_line_wrapping(result, [70, 60])
|
268
268
|
end
|
@@ -295,11 +295,34 @@ module CommonLineWrappingTests
|
|
295
295
|
assert_equal(2, lines.count)
|
296
296
|
end
|
297
297
|
|
298
|
+
it "handles items with fill_horizontal correctly" do
|
299
|
+
doc = HexaPDF::Document.new
|
300
|
+
font = doc.fonts.add("Times")
|
301
|
+
box1 = HexaPDF::Layout::TextLayouter::Box.new(
|
302
|
+
HexaPDF::Layout::TextFragment.create('.', font: font, fill_horizontal: 1)
|
303
|
+
)
|
304
|
+
box2 = HexaPDF::Layout::TextLayouter::Box.new(
|
305
|
+
HexaPDF::Layout::TextFragment.create('.', font: font, fill_horizontal: 2)
|
306
|
+
)
|
307
|
+
items = [box1, *boxes(10), box2]
|
308
|
+
rest, lines = call(items, 40)
|
309
|
+
assert_equal(0, rest.size)
|
310
|
+
assert_equal(1, lines.size)
|
311
|
+
|
312
|
+
line = lines.first
|
313
|
+
refute_same(items[0].item, line.items[0])
|
314
|
+
assert_same(items[1].item, line.items[1])
|
315
|
+
refute_same(items[2].item, line.items[2])
|
316
|
+
assert_equal(10, line.items[0].width)
|
317
|
+
assert_equal(10, line.items[1].width)
|
318
|
+
assert_equal(20, line.items[2].width)
|
319
|
+
end
|
298
320
|
end
|
299
321
|
|
300
322
|
describe HexaPDF::Layout::TextLayouter::SimpleLineWrapping do
|
301
323
|
before do
|
302
324
|
@obj = HexaPDF::Layout::TextLayouter::SimpleLineWrapping
|
325
|
+
@mock_frame = nil
|
303
326
|
end
|
304
327
|
|
305
328
|
describe "fixed width wrapping" do
|
@@ -308,7 +331,9 @@ describe HexaPDF::Layout::TextLayouter::SimpleLineWrapping do
|
|
308
331
|
def call(items, width = 100, &block)
|
309
332
|
lines = []
|
310
333
|
block ||= proc { true }
|
311
|
-
rest = @obj.call(items, proc { width })
|
334
|
+
rest = @obj.call(items, proc { width }, @mock_frame) do |line, item|
|
335
|
+
lines << line; block.call(line, item)
|
336
|
+
end
|
312
337
|
[rest, lines]
|
313
338
|
end
|
314
339
|
end
|
@@ -319,7 +344,9 @@ describe HexaPDF::Layout::TextLayouter::SimpleLineWrapping do
|
|
319
344
|
def call(items, width = 100, &block)
|
320
345
|
lines = []
|
321
346
|
block ||= proc { true }
|
322
|
-
rest = @obj.call(items, proc {|_| width })
|
347
|
+
rest = @obj.call(items, proc {|_| width }, @mock_frame) do |line, i|
|
348
|
+
lines << line; block.call(line, i)
|
349
|
+
end
|
323
350
|
[rest, lines]
|
324
351
|
end
|
325
352
|
|
@@ -334,7 +361,7 @@ describe HexaPDF::Layout::TextLayouter::SimpleLineWrapping do
|
|
334
361
|
end
|
335
362
|
end
|
336
363
|
lines = []
|
337
|
-
rest = @obj.call(boxes([20, 10], [10, 10], [20, 15], [40, 10]), width_block) do |line|
|
364
|
+
rest = @obj.call(boxes([20, 10], [10, 10], [20, 15], [40, 10]), width_block, @mock_frame) do |line|
|
338
365
|
height += line.height
|
339
366
|
lines << line
|
340
367
|
true
|
@@ -357,7 +384,7 @@ describe HexaPDF::Layout::TextLayouter::SimpleLineWrapping do
|
|
357
384
|
lines = []
|
358
385
|
item = HexaPDF::Layout::InlineBox.create(width: 20, height: 10) {}
|
359
386
|
items = boxes([20, 10]) + [penalty(0, item)] + boxes([40, 15])
|
360
|
-
rest = @obj.call(items, width_block) do |line|
|
387
|
+
rest = @obj.call(items, width_block, @mock_frame) do |line|
|
361
388
|
height += line.height
|
362
389
|
lines << line
|
363
390
|
true
|
@@ -119,6 +119,26 @@ describe HexaPDF::Composer do
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
describe "page_style" do
|
123
|
+
it "returns the page style if no argument or block is given" do
|
124
|
+
page_style = @composer.page_style(:default)
|
125
|
+
assert_kind_of(HexaPDF::Layout::PageStyle, page_style)
|
126
|
+
assert_equal(:A4, page_style.page_size)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "sets a page style using the given attributes" do
|
130
|
+
@composer.page_style(:other, page_size: :A3)
|
131
|
+
assert_equal(:A3, @composer.page_style(:other).page_size)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "sets a page style using default attributes but with a block" do
|
135
|
+
@composer.page_style(:other) {|canvas, style| style.frame = :hallo }
|
136
|
+
style = @composer.page_style(:other)
|
137
|
+
style.create_page(@composer.document)
|
138
|
+
assert_equal(:hallo, style.frame)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
122
142
|
describe "text/formatted_text/image/box/method_missing" do
|
123
143
|
before do
|
124
144
|
test_self = self
|
@@ -243,6 +263,16 @@ describe HexaPDF::Composer do
|
|
243
263
|
[:restore_graphics_state]])
|
244
264
|
end
|
245
265
|
|
266
|
+
it "returns the last drawn box" do
|
267
|
+
box = create_box(height: 400)
|
268
|
+
assert_same(box, @composer.draw_box(box))
|
269
|
+
|
270
|
+
box = create_box(height: 400)
|
271
|
+
split_box = create_box(height: 100)
|
272
|
+
box.define_singleton_method(:split) {|*| [box, split_box] }
|
273
|
+
assert_same(split_box, @composer.draw_box(box))
|
274
|
+
end
|
275
|
+
|
246
276
|
it "raises an error if a box doesn't fit onto an empty page" do
|
247
277
|
assert_raises(HexaPDF::Error) do
|
248
278
|
@composer.draw_box(create_box(height: 800))
|
@@ -94,6 +94,16 @@ describe HexaPDF::Dictionary do
|
|
94
94
|
obj = @test_class.new(nil)
|
95
95
|
assert_equal(:MyType, obj.value[:Type])
|
96
96
|
end
|
97
|
+
|
98
|
+
it "doesn't set the default values for required fields if the type class might be wrong" do
|
99
|
+
@test_class.define_type(:MyType)
|
100
|
+
obj = @test_class.new({})
|
101
|
+
assert_equal([], obj.value[:Array])
|
102
|
+
obj = @test_class.new({Type: :MyType})
|
103
|
+
assert_equal([], obj.value[:Array])
|
104
|
+
obj = @test_class.new({Type: :OtherType})
|
105
|
+
refute(obj.key?(:Array))
|
106
|
+
end
|
97
107
|
end
|
98
108
|
|
99
109
|
describe "[]" do
|
@@ -561,6 +561,10 @@ describe HexaPDF::Document do
|
|
561
561
|
assert_kind_of(HexaPDF::Type::Outline, @doc.outline)
|
562
562
|
end
|
563
563
|
|
564
|
+
it "returns the optional content properties" do
|
565
|
+
assert_kind_of(HexaPDF::Type::OptionalContentProperties, @doc.optional_content)
|
566
|
+
end
|
567
|
+
|
564
568
|
it "can be inspected and the output is not too large" do
|
565
569
|
assert_match(/HexaPDF::Document:\d+/, @doc.inspect)
|
566
570
|
end
|
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.
|
43
|
+
<</Producer(HexaPDF version 0.34.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.
|
75
|
+
<</Producer(HexaPDF version 0.34.1)>>
|
76
76
|
endobj
|
77
77
|
2 0 obj
|
78
78
|
<</Length 10>>stream
|
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
|
|
214
214
|
<</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
|
215
215
|
endobj
|
216
216
|
5 0 obj
|
217
|
-
<</Producer(HexaPDF version 0.
|
217
|
+
<</Producer(HexaPDF version 0.34.1)>>
|
218
218
|
endobj
|
219
219
|
4 0 obj
|
220
220
|
<</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
|