hexapdf 0.23.0 → 0.24.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 +66 -0
- data/LICENSE +1 -1
- data/Rakefile +1 -1
- data/examples/016-frame_automatic_box_placement.rb +7 -2
- data/examples/017-frame_text_flow.rb +10 -18
- data/examples/020-column_box.rb +40 -0
- data/examples/021-list_box.rb +26 -0
- data/lib/hexapdf/cli/batch.rb +1 -1
- data/lib/hexapdf/cli/command.rb +1 -1
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +2 -2
- data/lib/hexapdf/cli/image2pdf.rb +1 -1
- data/lib/hexapdf/cli/images.rb +1 -1
- data/lib/hexapdf/cli/info.rb +2 -2
- data/lib/hexapdf/cli/inspect.rb +2 -2
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/cli/watermark.rb +1 -1
- data/lib/hexapdf/cli.rb +1 -1
- data/lib/hexapdf/composer.rb +45 -126
- data/lib/hexapdf/configuration.rb +17 -1
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/color_space.rb +1 -1
- data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/geom2d.rb +2 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object.rb +1 -1
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +1 -1
- data/lib/hexapdf/data_dir.rb +1 -1
- data/lib/hexapdf/dictionary.rb +1 -1
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document/layout.rb +397 -0
- data/lib/hexapdf/document/pages.rb +17 -1
- data/lib/hexapdf/document/signatures.rb +5 -4
- data/lib/hexapdf/document.rb +8 -1
- data/lib/hexapdf/encryption/aes.rb +1 -1
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +1 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +30 -21
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/encryption.rb +1 -1
- data/lib/hexapdf/error.rb +1 -1
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/crypt.rb +1 -1
- data/lib/hexapdf/filter/encryption.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +1 -1
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +1 -1
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/base.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/glyph_list.rb +2 -2
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +1 -1
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/font.rb +1 -1
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
- data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
- data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
- data/lib/hexapdf/font/true_type/table/head.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
- data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
- data/lib/hexapdf/font/true_type/table/name.rb +1 -1
- data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type.rb +1 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +1 -1
- data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
- data/lib/hexapdf/font/type1.rb +1 -1
- data/lib/hexapdf/font/type1_wrapper.rb +1 -1
- data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
- data/lib/hexapdf/font_loader/from_file.rb +1 -1
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/font_loader.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +1 -1
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +1 -1
- data/lib/hexapdf/layout/box.rb +121 -22
- data/lib/hexapdf/layout/box_fitter.rb +136 -0
- data/lib/hexapdf/layout/column_box.rb +247 -0
- data/lib/hexapdf/layout/frame.rb +155 -139
- data/lib/hexapdf/layout/image_box.rb +19 -4
- data/lib/hexapdf/layout/inline_box.rb +1 -1
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/list_box.rb +355 -0
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/style.rb +5 -1
- data/lib/hexapdf/layout/text_box.rb +20 -9
- data/lib/hexapdf/layout/text_fragment.rb +3 -2
- data/lib/hexapdf/layout/text_layouter.rb +17 -2
- data/lib/hexapdf/layout/text_shaper.rb +1 -1
- data/lib/hexapdf/layout/width_from_polygon.rb +12 -7
- data/lib/hexapdf/layout.rb +4 -1
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +1 -1
- data/lib/hexapdf/parser.rb +1 -8
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/serializer.rb +1 -1
- data/lib/hexapdf/stream.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +1 -1
- data/lib/hexapdf/task.rb +1 -1
- data/lib/hexapdf/tokenizer.rb +1 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +1 -1
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +1 -1
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +1 -1
- data/lib/hexapdf/type/annotations.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +1 -1
- data/lib/hexapdf/type/font_true_type.rb +1 -1
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type1.rb +1 -1
- data/lib/hexapdf/type/font_type3.rb +1 -1
- data/lib/hexapdf/type/form.rb +1 -1
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +1 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/names.rb +1 -1
- data/lib/hexapdf/type/object_stream.rb +1 -1
- data/lib/hexapdf/type/page.rb +1 -1
- data/lib/hexapdf/type/page_tree_node.rb +19 -2
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -1
- data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +1 -1
- data/lib/hexapdf/type/signature/handler.rb +1 -1
- data/lib/hexapdf/type/signature/verification_result.rb +1 -1
- data/lib/hexapdf/type/signature.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +2 -2
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +1 -1
- data/lib/hexapdf/type.rb +1 -1
- data/lib/hexapdf/utils/bit_field.rb +1 -1
- data/lib/hexapdf/utils/bit_stream.rb +1 -1
- data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
- data/lib/hexapdf/utils/lru_cache.rb +1 -1
- data/lib/hexapdf/utils/math_helpers.rb +1 -1
- data/lib/hexapdf/utils/object_hash.rb +1 -1
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +9 -7
- data/lib/hexapdf/xref_section.rb +1 -1
- data/lib/hexapdf.rb +1 -1
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +1 -1
- data/test/hexapdf/document/test_destinations.rb +1 -1
- data/test/hexapdf/document/test_images.rb +1 -1
- data/test/hexapdf/document/test_layout.rb +264 -0
- data/test/hexapdf/document/test_pages.rb +9 -0
- data/test/hexapdf/document/test_signatures.rb +10 -3
- data/test/hexapdf/encryption/test_security_handler.rb +1 -1
- data/test/hexapdf/font/encoding/test_glyph_list.rb +4 -0
- data/test/hexapdf/layout/test_box.rb +53 -3
- data/test/hexapdf/layout/test_box_fitter.rb +62 -0
- data/test/hexapdf/layout/test_column_box.rb +159 -0
- data/test/hexapdf/layout/test_frame.rb +99 -38
- data/test/hexapdf/layout/test_image_box.rb +1 -1
- data/test/hexapdf/layout/test_list_box.rb +249 -0
- data/test/hexapdf/layout/test_text_box.rb +17 -2
- data/test/hexapdf/layout/test_text_fragment.rb +1 -1
- data/test/hexapdf/layout/test_text_layouter.rb +42 -17
- data/test/hexapdf/layout/test_width_from_polygon.rb +13 -0
- data/test/hexapdf/test_composer.rb +11 -0
- data/test/hexapdf/test_dictionary_fields.rb +9 -9
- data/test/hexapdf/test_document.rb +4 -4
- data/test/hexapdf/test_filter.rb +1 -1
- data/test/hexapdf/test_parser.rb +0 -2
- data/test/hexapdf/test_revisions.rb +2 -2
- data/test/hexapdf/test_serializer.rb +1 -5
- data/test/hexapdf/test_writer.rb +58 -3
- data/test/hexapdf/type/test_page_tree_node.rb +21 -1
- data/test/hexapdf/type/test_trailer.rb +3 -3
- data/test/test_helper.rb +5 -1
- metadata +28 -3
|
@@ -57,6 +57,14 @@ describe HexaPDF::Layout::Box do
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
it "returns false when asking whether it is a split box by default" do
|
|
61
|
+
refute(create_box.split_box?)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "doesn't support the position :flow" do
|
|
65
|
+
refute(create_box.supports_position_flow?)
|
|
66
|
+
end
|
|
67
|
+
|
|
60
68
|
describe "fit" do
|
|
61
69
|
before do
|
|
62
70
|
@frame = Object.new
|
|
@@ -89,9 +97,51 @@ describe HexaPDF::Layout::Box do
|
|
|
89
97
|
end
|
|
90
98
|
end
|
|
91
99
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
describe "split" do
|
|
101
|
+
before do
|
|
102
|
+
@box = create_box(width: 100, height: 100)
|
|
103
|
+
@box.fit(100, 100, nil)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "doesn't need to be split if it completely fits" do
|
|
107
|
+
assert_equal([@box, nil], @box.split(100, 100, nil))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "can't be split if it doesn't (completely) fit and its width is greater than the available width" do
|
|
111
|
+
@box.fit(90, 100, nil)
|
|
112
|
+
assert_equal([nil, @box], @box.split(50, 150, nil))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "can't be split if it doesn't (completely) fit and its height is greater than the available height" do
|
|
116
|
+
@box.fit(90, 100, nil)
|
|
117
|
+
assert_equal([nil, @box], @box.split(150, 50, nil))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "can't be split if it doesn't (completely) fit and its content width is zero" do
|
|
121
|
+
box = create_box(width: 0, height: 100)
|
|
122
|
+
assert_equal([nil, box], box.split(150, 150, nil))
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "can't be split if it doesn't (completely) fit and its content height is zero" do
|
|
126
|
+
box = create_box(width: 100, height: 0)
|
|
127
|
+
assert_equal([nil, box], box.split(150, 150, nil))
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "can't be split if it doesn't (completely) fit as the default implementation knows nothing about the content" do
|
|
131
|
+
@box.style.position = :flow # make sure we would generally be splitable
|
|
132
|
+
@box.fit(90, 100, nil)
|
|
133
|
+
assert_equal([nil, @box], @box.split(150, 150, nil))
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "can create a cloned box for splitting" do
|
|
138
|
+
box = create_box
|
|
139
|
+
box.fit(100, 100, nil)
|
|
140
|
+
cloned_box = box.send(:create_split_box)
|
|
141
|
+
assert(cloned_box.split_box?)
|
|
142
|
+
refute(cloned_box.instance_variable_get(:@fit_successful))
|
|
143
|
+
assert_equal(0, cloned_box.width)
|
|
144
|
+
assert_equal(0, cloned_box.height)
|
|
95
145
|
end
|
|
96
146
|
|
|
97
147
|
describe "draw" do
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'geom2d/polygon'
|
|
5
|
+
require 'hexapdf/layout/box_fitter'
|
|
6
|
+
require 'hexapdf/layout'
|
|
7
|
+
|
|
8
|
+
describe HexaPDF::Layout::BoxFitter do
|
|
9
|
+
before do
|
|
10
|
+
shape = Geom2D::Polygon([0, 0], [100, 0], [100, 90], [10, 90], [10, 80], [0, 80])
|
|
11
|
+
frames = [
|
|
12
|
+
HexaPDF::Layout::Frame.new(0, 0, 100, 100, shape: shape),
|
|
13
|
+
HexaPDF::Layout::Frame.new(100, 100, 50, 50),
|
|
14
|
+
]
|
|
15
|
+
@box_fitter = HexaPDF::Layout::BoxFitter.new(frames)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def fit_box(count, width: 10, height: 10)
|
|
19
|
+
ibox = HexaPDF::Layout::InlineBox.create(width: width, height: height) {}
|
|
20
|
+
@box_fitter.fit(HexaPDF::Layout::TextBox.new(items: [ibox] * count))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def check_result(*pos, content_heights:, successful: true, boxes_remain: false)
|
|
24
|
+
pos.each_slice(2).with_index do |(x, y), index|
|
|
25
|
+
assert_equal(x, @box_fitter.fit_results[index].x, "x #{index}")
|
|
26
|
+
assert_equal(y, @box_fitter.fit_results[index].y, "y #{index}")
|
|
27
|
+
end
|
|
28
|
+
assert_equal(content_heights, @box_fitter.content_heights)
|
|
29
|
+
successful ? assert(@box_fitter.fit_successful?) : refute(@box_fitter.fit_successful?)
|
|
30
|
+
rboxes = @box_fitter.remaining_boxes.empty?
|
|
31
|
+
boxes_remain ? refute(rboxes) : assert(rboxes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "successfully places boxes only in one frame" do
|
|
35
|
+
fit_box(20)
|
|
36
|
+
fit_box(20)
|
|
37
|
+
check_result(10, 60, 0, 40, content_heights: [50, 0])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "successfully places boxes in multiple frames, without splitting" do
|
|
41
|
+
fit_box(1, height: 80)
|
|
42
|
+
fit_box(1, height: 40)
|
|
43
|
+
check_result(10, 10, 100, 110, content_heights: [80, 40])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "successfully places boxes in multiple framess, with splitting" do
|
|
47
|
+
fit_box(63)
|
|
48
|
+
fit_box(30)
|
|
49
|
+
fit_box(10)
|
|
50
|
+
check_result(10, 20, 0, 0, 100, 130, 100, 110, content_heights: [90, 40])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "fails when some boxes can't be fitted" do
|
|
54
|
+
fit_box(9)
|
|
55
|
+
fit_box(70)
|
|
56
|
+
fit_box(40)
|
|
57
|
+
fit_box(20)
|
|
58
|
+
check_result(10, 80, 0, 10, 0, 0, 100, 100, successful: false, boxes_remain: true,
|
|
59
|
+
content_heights: [90, 50])
|
|
60
|
+
assert_equal(2, @box_fitter.remaining_boxes.size)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require_relative '../content/common'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
require 'hexapdf/layout/column_box'
|
|
7
|
+
|
|
8
|
+
describe HexaPDF::Layout::ColumnBox do
|
|
9
|
+
before do
|
|
10
|
+
@frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
|
11
|
+
inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {}
|
|
12
|
+
@text_boxes = 5.times.map do
|
|
13
|
+
HexaPDF::Layout::TextBox.new(items: [inline_box] * 15, style: {position: :default})
|
|
14
|
+
end
|
|
15
|
+
draw_block = lambda do |canvas, box|
|
|
16
|
+
canvas.move_to(0, 0).end_path
|
|
17
|
+
end
|
|
18
|
+
@fixed_size_boxes = 15.times.map { HexaPDF::Layout::Box.new(width: 20, height: 10, &draw_block) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_box(**kwargs)
|
|
22
|
+
HexaPDF::Layout::ColumnBox.new(gaps: 10, **kwargs)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def check_box(box, width, height, fit_pos = nil)
|
|
26
|
+
assert(box.fit(@frame.available_width, @frame.available_height, @frame), "box fit?")
|
|
27
|
+
assert_equal(width, box.width, "box width")
|
|
28
|
+
assert_equal(height, box.height, "box height")
|
|
29
|
+
if fit_pos
|
|
30
|
+
box_fitter = box.instance_variable_get(:@box_fitter)
|
|
31
|
+
assert_equal(fit_pos.size, box_fitter.fit_results.size)
|
|
32
|
+
fit_pos.each_with_index do |(x, y), index|
|
|
33
|
+
assert_equal(x, box_fitter.fit_results[index].x, "result[#{index}].x")
|
|
34
|
+
assert_equal(y, box_fitter.fit_results[index].y, "result[#{index}].y")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "initialize" do
|
|
40
|
+
it "creates a new instance with the given arguments" do
|
|
41
|
+
box = create_box(children: [:a], columns: 3, gaps: 10, equal_height: false)
|
|
42
|
+
assert_equal([:a], box.children)
|
|
43
|
+
assert_equal([-1, -1, -1], box.columns)
|
|
44
|
+
assert_equal([10], box.gaps)
|
|
45
|
+
assert_equal(false, box.equal_height)
|
|
46
|
+
assert(box.supports_position_flow?)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe "fit" do
|
|
51
|
+
[:default, :flow].each do |position|
|
|
52
|
+
it "respects the set initial width, position #{position}" do
|
|
53
|
+
box = create_box(children: @text_boxes[0..1], width: 50, style: {position: position})
|
|
54
|
+
check_box(box, 50, 80)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "respects the set initial height, position #{position}" do
|
|
58
|
+
box = create_box(children: @text_boxes[0..1], height: 50, equal_height: false,
|
|
59
|
+
style: {position: position})
|
|
60
|
+
check_box(box, 100, 50)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "respects the border and padding around all columns, position #{position}" do
|
|
64
|
+
box = create_box(children: @fixed_size_boxes[0, 3],
|
|
65
|
+
style: {border: {width: [5, 4, 3, 2]}, padding: [5, 4, 3, 2], position: position})
|
|
66
|
+
check_box(box, 100, 36, [[4, 80], [4, 70], [53, 80]])
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "uses the frame's current cursor position and available width/height when style position=:default" do
|
|
71
|
+
@frame.remove_area(Geom2D::Polygon([0, 0], [10, 0], [10, 90], [100, 90], [100, 100], [0, 100]))
|
|
72
|
+
box = create_box(children: @fixed_size_boxes[0, 4])
|
|
73
|
+
check_box(box, 90, 20, [[10, 80], [10, 70], [60, 80], [60, 70]])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "respects the frame's shape when style position=:flow" do
|
|
77
|
+
@frame.remove_area(Geom2D::Polygon([30, 65], [70, 65], [70, 35], [30, 35]))
|
|
78
|
+
box = create_box(children: @text_boxes[0, 3], style: {position: :flow})
|
|
79
|
+
check_box(box, 100, 70, [[0, 70], [0, 60], [0, 30], [55, 80], [55, 70], [70, 30]])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "allows fitting the contents to fill the columns instead of equalizing the height" do
|
|
83
|
+
box = create_box(children: @fixed_size_boxes, equal_height: false)
|
|
84
|
+
check_box(box, 100, 100, [[0, 90], [0, 80], [0, 70], [0, 60], [0, 50], [0, 40], [0, 30],
|
|
85
|
+
[0, 20], [0, 10], [0, 0], [55, 90], [55, 80], [55, 70],
|
|
86
|
+
[55, 60], [55, 50]])
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "columns calculations" do
|
|
90
|
+
it "works for a single column with a specified width" do
|
|
91
|
+
box = create_box(children: @fixed_size_boxes[0..0], columns: [50])
|
|
92
|
+
check_box(box, 50, 10, [[0, 90]])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "works for multiple columns with specified widths" do
|
|
96
|
+
box = create_box(children: @fixed_size_boxes[0..1], columns: [50, 30])
|
|
97
|
+
check_box(box, 90, 10, [[0, 90], [60, 90]])
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "works for a single column with auto-width" do
|
|
101
|
+
box = create_box(children: @fixed_size_boxes[0..0], columns: [-5])
|
|
102
|
+
check_box(box, 100, 10, [[0, 90]])
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "works for multiple columns with auto-widths" do
|
|
106
|
+
box = create_box(children: @fixed_size_boxes[0..1], columns: [-2, -1])
|
|
107
|
+
check_box(box, 100, 10, [[0, 90], [70, 90]])
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "works for mixed columns with specified widths and auto-widths" do
|
|
111
|
+
box = create_box(children: @fixed_size_boxes[0..2], columns: [20, -1, -2])
|
|
112
|
+
check_box(box, 100, 10, [[0, 90], [30, 90], [60, 90]])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "cycles the gap array" do
|
|
116
|
+
box = create_box(children: @fixed_size_boxes[0..3], columns: 4, gaps: [5, 10])
|
|
117
|
+
check_box(box, 100, 10, [[0, 90], [25, 90], [55, 90], [80, 90]])
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "fails if the necessary width is larger than the available one" do
|
|
121
|
+
box = create_box(children: @fixed_size_boxes[0..2], columns: 4, gaps: [40])
|
|
122
|
+
refute(box.fit(100, 100, @frame))
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "splits the children if they are too big to fill the colums" do
|
|
128
|
+
box = create_box(children: @fixed_size_boxes, width: 50, height: 50)
|
|
129
|
+
box.fit(100, 100, @frame)
|
|
130
|
+
box_a, box_b = box.split(100, 100, @frame)
|
|
131
|
+
assert_same(box, box_a)
|
|
132
|
+
assert(box_b.split_box?)
|
|
133
|
+
assert_equal(5, box_b.children.size)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "draws the result onto the canvas" do
|
|
137
|
+
box = create_box(children: @fixed_size_boxes)
|
|
138
|
+
box.fit(100, 100, @frame)
|
|
139
|
+
|
|
140
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
141
|
+
box.draw(@canvas, 0, 0)
|
|
142
|
+
operators = 90.step(to: 20, by: -10).map do |y|
|
|
143
|
+
[[:save_graphics_state],
|
|
144
|
+
[:concatenate_matrix, [1, 0, 0, 1, 0, y]],
|
|
145
|
+
[:move_to, [0, 0]],
|
|
146
|
+
[:end_path],
|
|
147
|
+
[:restore_graphics_state]]
|
|
148
|
+
end
|
|
149
|
+
operators.concat(90.step(to: 30, by: -10).map do |y|
|
|
150
|
+
[[:save_graphics_state],
|
|
151
|
+
[:concatenate_matrix, [1, 0, 0, 1, 55, y]],
|
|
152
|
+
[:move_to, [0, 0]],
|
|
153
|
+
[:end_path],
|
|
154
|
+
[:restore_graphics_state]]
|
|
155
|
+
end)
|
|
156
|
+
operators.flatten!(1)
|
|
157
|
+
assert_operators(@canvas.contents, operators)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
require 'test_helper'
|
|
4
4
|
require 'hexapdf/layout/frame'
|
|
5
5
|
require 'hexapdf/layout/box'
|
|
6
|
+
require 'hexapdf/document'
|
|
7
|
+
|
|
8
|
+
describe HexaPDF::Layout::Frame do
|
|
9
|
+
it "shows the box's mask area on #draw when using debug output" do
|
|
10
|
+
doc = HexaPDF::Document.new(config: {'debug' => true})
|
|
11
|
+
canvas = doc.pages.add.canvas
|
|
12
|
+
box = HexaPDF::Layout::Box.create(width: 20, height: 20) {}
|
|
13
|
+
result = HexaPDF::Layout::Frame::FitResult.new(box)
|
|
14
|
+
result.mask = Geom2D::Polygon([0, 0], [0, 20], [20, 20], [20, 0])
|
|
15
|
+
result.x = result.y = 0
|
|
16
|
+
result.draw(canvas)
|
|
17
|
+
assert_equal(<<~CONTENTS, canvas.contents)
|
|
18
|
+
q
|
|
19
|
+
0.0 0.501961 0.0 rg
|
|
20
|
+
0.0 0.392157 0.0 RG
|
|
21
|
+
/GS1 gs
|
|
22
|
+
0 0 m
|
|
23
|
+
0 20 l
|
|
24
|
+
20 20 l
|
|
25
|
+
20 0 l
|
|
26
|
+
h
|
|
27
|
+
B
|
|
28
|
+
Q
|
|
29
|
+
q
|
|
30
|
+
1 0 0 1 0 0 cm
|
|
31
|
+
Q
|
|
32
|
+
CONTENTS
|
|
33
|
+
end
|
|
34
|
+
end
|
|
6
35
|
|
|
7
36
|
describe HexaPDF::Layout::Frame do
|
|
8
37
|
before do
|
|
@@ -23,16 +52,14 @@ describe HexaPDF::Layout::Frame do
|
|
|
23
52
|
assert_equal(150, @frame.available_height)
|
|
24
53
|
end
|
|
25
54
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
assert_same(contour_line, frame.contour_line)
|
|
35
|
-
end
|
|
55
|
+
it "allows setting the shape of the frame on initialization" do
|
|
56
|
+
shape = Geom2D::Polygon([50, 10], [55, 100], [105, 100], [105, 10])
|
|
57
|
+
frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150, shape: shape)
|
|
58
|
+
assert_equal(shape, frame.shape)
|
|
59
|
+
assert_equal(55, frame.x)
|
|
60
|
+
assert_equal(100, frame.y)
|
|
61
|
+
assert_equal(50, frame.available_width)
|
|
62
|
+
assert_equal(90, frame.available_height)
|
|
36
63
|
end
|
|
37
64
|
|
|
38
65
|
it "returns an appropriate width specification object" do
|
|
@@ -44,15 +71,25 @@ describe HexaPDF::Layout::Frame do
|
|
|
44
71
|
before do
|
|
45
72
|
@frame = HexaPDF::Layout::Frame.new(10, 10, 100, 100)
|
|
46
73
|
@canvas = Minitest::Mock.new
|
|
74
|
+
def @canvas.context
|
|
75
|
+
Object.new.tap do |ctx|
|
|
76
|
+
def ctx.document; Object.new.tap {|doc| def doc.config; Hash.new(false); end } end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
47
79
|
end
|
|
48
80
|
|
|
49
81
|
# Creates a box with the given option, storing it in @box, and draws it inside @frame. It is
|
|
50
82
|
# checked whether the box coordinates are pos and whether the frame has the shape given by
|
|
51
83
|
# points.
|
|
52
|
-
def check_box(box_opts, pos, points)
|
|
84
|
+
def check_box(box_opts, pos, mask, points)
|
|
85
|
+
flow_supported = !box_opts.delete(:doesnt_support_position_flow)
|
|
53
86
|
@box = HexaPDF::Layout::Box.create(**box_opts) {}
|
|
87
|
+
@box.define_singleton_method(:supports_position_flow?) { true } if flow_supported
|
|
54
88
|
@canvas.expect(:translate, nil, pos)
|
|
55
|
-
|
|
89
|
+
fit_result = @frame.fit(@box)
|
|
90
|
+
refute_nil(fit_result)
|
|
91
|
+
@frame.draw(@canvas, fit_result)
|
|
92
|
+
assert_equal(mask, fit_result.mask.bbox.to_a)
|
|
56
93
|
assert_equal(points, @frame.shape.polygons.map(&:to_a))
|
|
57
94
|
@canvas.verify
|
|
58
95
|
end
|
|
@@ -75,6 +112,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
75
112
|
check_box(
|
|
76
113
|
{width: 50, height: 50, position: :absolute, position_hint: [10, 10]},
|
|
77
114
|
[20, 20],
|
|
115
|
+
[20, 20, 70, 70],
|
|
78
116
|
[[[10, 10], [110, 10], [110, 110], [10, 110]],
|
|
79
117
|
[[20, 20], [70, 20], [70, 70], [20, 70]]]
|
|
80
118
|
)
|
|
@@ -84,6 +122,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
84
122
|
check_box(
|
|
85
123
|
{position: :absolute, position_hint: [10, 10]},
|
|
86
124
|
[20, 20],
|
|
125
|
+
[20, 20, 110, 110],
|
|
87
126
|
[[[10, 10], [110, 10], [110, 20], [20, 20], [20, 110], [10, 110]]]
|
|
88
127
|
)
|
|
89
128
|
end
|
|
@@ -93,6 +132,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
93
132
|
{width: 50, height: 50, position: :absolute, position_hint: [10, 10],
|
|
94
133
|
margin: [10, 20, 30, 40]},
|
|
95
134
|
[20, 20],
|
|
135
|
+
[-20, -10, 90, 80],
|
|
96
136
|
[[[10, 80], [90, 80], [90, 10], [110, 10], [110, 110], [10, 110]]]
|
|
97
137
|
)
|
|
98
138
|
end
|
|
@@ -102,18 +142,21 @@ describe HexaPDF::Layout::Frame do
|
|
|
102
142
|
it "draws the box on the left side" do
|
|
103
143
|
check_box({width: 50, height: 50},
|
|
104
144
|
[10, 60],
|
|
145
|
+
[10, 60, 110, 110],
|
|
105
146
|
[[[10, 10], [110, 10], [110, 60], [10, 60]]])
|
|
106
147
|
end
|
|
107
148
|
|
|
108
149
|
it "draws the box on the right side" do
|
|
109
150
|
check_box({width: 50, height: 50, position_hint: :right},
|
|
110
151
|
[60, 60],
|
|
152
|
+
[10, 60, 110, 110],
|
|
111
153
|
[[[10, 10], [110, 10], [110, 60], [10, 60]]])
|
|
112
154
|
end
|
|
113
155
|
|
|
114
156
|
it "draws the box in the center" do
|
|
115
157
|
check_box({width: 50, height: 50, position_hint: :center},
|
|
116
158
|
[35, 60],
|
|
159
|
+
[10, 60, 110, 110],
|
|
117
160
|
[[[10, 10], [110, 10], [110, 60], [10, 60]]])
|
|
118
161
|
end
|
|
119
162
|
|
|
@@ -121,7 +164,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
121
164
|
[:left, :center, :right].each do |hint|
|
|
122
165
|
it "ignores all margins if the box fills the whole frame, with position hint #{hint}" do
|
|
123
166
|
check_box({margin: 10, position_hint: hint},
|
|
124
|
-
[10, 10], [])
|
|
167
|
+
[10, 10], [10, 10, 110, 110], [])
|
|
125
168
|
assert_equal(100, @box.width)
|
|
126
169
|
assert_equal(100, @box.height)
|
|
127
170
|
end
|
|
@@ -130,6 +173,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
130
173
|
"frame's, with position hint #{hint}" do
|
|
131
174
|
check_box({height: 50, margin: 10, position_hint: hint},
|
|
132
175
|
[10, 60],
|
|
176
|
+
[10, 50, 110, 110],
|
|
133
177
|
[[[10, 10], [110, 10], [110, 50], [10, 50]]])
|
|
134
178
|
end
|
|
135
179
|
|
|
@@ -138,6 +182,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
138
182
|
remove_area(:top)
|
|
139
183
|
check_box({height: 50, margin: 10, position_hint: hint},
|
|
140
184
|
[10, 40],
|
|
185
|
+
[10, 30, 110, 100],
|
|
141
186
|
[[[10, 10], [110, 10], [110, 30], [10, 30]]])
|
|
142
187
|
assert_equal(100, @box.width)
|
|
143
188
|
end
|
|
@@ -147,6 +192,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
147
192
|
remove_area(:left)
|
|
148
193
|
check_box({height: 50, margin: 10, position_hint: hint},
|
|
149
194
|
[30, 60],
|
|
195
|
+
[10, 50, 110, 110],
|
|
150
196
|
[[[20, 10], [110, 10], [110, 50], [20, 50]]])
|
|
151
197
|
assert_equal(80, @box.width)
|
|
152
198
|
end
|
|
@@ -156,6 +202,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
156
202
|
remove_area(:right)
|
|
157
203
|
check_box({height: 50, margin: 10, position_hint: hint},
|
|
158
204
|
[10, 60],
|
|
205
|
+
[10, 50, 110, 110],
|
|
159
206
|
[[[10, 10], [100, 10], [100, 50], [10, 50]]])
|
|
160
207
|
assert_equal(80, @box.width)
|
|
161
208
|
end
|
|
@@ -164,6 +211,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
164
211
|
it "perfectly centers a box if possible, margins ignored" do
|
|
165
212
|
check_box({width: 50, height: 10, margin: [10, 10, 10, 20], position_hint: :center},
|
|
166
213
|
[35, 100],
|
|
214
|
+
[10, 90, 110, 110],
|
|
167
215
|
[[[10, 10], [110, 10], [110, 90], [10, 90]]])
|
|
168
216
|
end
|
|
169
217
|
|
|
@@ -171,6 +219,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
171
219
|
remove_area(:left, :right)
|
|
172
220
|
check_box({width: 40, height: 10, margin: [10, 10, 10, 20], position_hint: :center},
|
|
173
221
|
[40, 100],
|
|
222
|
+
[10, 90, 110, 110],
|
|
174
223
|
[[[20, 10], [100, 10], [100, 90], [20, 90]]])
|
|
175
224
|
end
|
|
176
225
|
|
|
@@ -178,6 +227,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
178
227
|
remove_area(:left, :right)
|
|
179
228
|
check_box({width: 20, height: 10, margin: [10, 10, 10, 40], position_hint: :center},
|
|
180
229
|
[65, 100],
|
|
230
|
+
[10, 90, 110, 110],
|
|
181
231
|
[[[20, 10], [100, 10], [100, 90], [20, 90]]])
|
|
182
232
|
end
|
|
183
233
|
end
|
|
@@ -187,18 +237,21 @@ describe HexaPDF::Layout::Frame do
|
|
|
187
237
|
it "draws the box on the left side" do
|
|
188
238
|
check_box({width: 50, height: 50, position: :float},
|
|
189
239
|
[10, 60],
|
|
240
|
+
[10, 60, 60, 110],
|
|
190
241
|
[[[10, 10], [110, 10], [110, 110], [60, 110], [60, 60], [10, 60]]])
|
|
191
242
|
end
|
|
192
243
|
|
|
193
244
|
it "draws the box on the right side" do
|
|
194
245
|
check_box({width: 50, height: 50, position: :float, position_hint: :right},
|
|
195
246
|
[60, 60],
|
|
247
|
+
[60, 60, 110, 110],
|
|
196
248
|
[[[10, 10], [110, 10], [110, 60], [60, 60], [60, 110], [10, 110]]])
|
|
197
249
|
end
|
|
198
250
|
|
|
199
251
|
it "draws the box in the center" do
|
|
200
252
|
check_box({width: 50, height: 50, position: :float, position_hint: :center},
|
|
201
253
|
[35, 60],
|
|
254
|
+
[35, 60, 85, 110],
|
|
202
255
|
[[[10, 10], [110, 10], [110, 110], [85, 110], [85, 60], [35, 60],
|
|
203
256
|
[35, 110], [10, 110]]])
|
|
204
257
|
end
|
|
@@ -207,7 +260,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
207
260
|
[:left, :center, :right].each do |hint|
|
|
208
261
|
it "ignores all margins if the box fills the whole frame, with position hint #{hint}" do
|
|
209
262
|
check_box({margin: 10, position: :float, position_hint: hint},
|
|
210
|
-
[10, 10], [])
|
|
263
|
+
[10, 10], [10, 10, 110, 110], [])
|
|
211
264
|
assert_equal(100, @box.width)
|
|
212
265
|
assert_equal(100, @box.height)
|
|
213
266
|
end
|
|
@@ -216,6 +269,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
216
269
|
it "ignores the left, but not the right margin if aligned left to the frame border" do
|
|
217
270
|
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :left},
|
|
218
271
|
[10, 60],
|
|
272
|
+
[10, 50, 70, 110],
|
|
219
273
|
[[[10, 10], [110, 10], [110, 110], [70, 110], [70, 50], [10, 50]]])
|
|
220
274
|
end
|
|
221
275
|
|
|
@@ -223,12 +277,14 @@ describe HexaPDF::Layout::Frame do
|
|
|
223
277
|
remove_area(:left)
|
|
224
278
|
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :left},
|
|
225
279
|
[30, 60],
|
|
280
|
+
[20, 50, 90, 110],
|
|
226
281
|
[[[20, 10], [110, 10], [110, 110], [90, 110], [90, 50], [20, 50]]])
|
|
227
282
|
end
|
|
228
283
|
|
|
229
284
|
it "uses the left and the right margin if aligned center" do
|
|
230
285
|
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :center},
|
|
231
286
|
[35, 60],
|
|
287
|
+
[25, 50, 95, 110],
|
|
232
288
|
[[[10, 10], [110, 10], [110, 110], [95, 110], [95, 50], [25, 50],
|
|
233
289
|
[25, 110], [10, 110]]])
|
|
234
290
|
end
|
|
@@ -236,6 +292,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
236
292
|
it "ignores the right, but not the left margin if aligned right to the frame border" do
|
|
237
293
|
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :right},
|
|
238
294
|
[60, 60],
|
|
295
|
+
[50, 50, 110, 110],
|
|
239
296
|
[[[10, 10], [110, 10], [110, 50], [50, 50], [50, 110], [10, 110]]])
|
|
240
297
|
end
|
|
241
298
|
|
|
@@ -243,6 +300,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
243
300
|
remove_area(:right)
|
|
244
301
|
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :right},
|
|
245
302
|
[40, 60],
|
|
303
|
+
[30, 50, 100, 110],
|
|
246
304
|
[[[10, 10], [100, 10], [100, 50], [30, 50], [30, 110], [10, 110]]])
|
|
247
305
|
end
|
|
248
306
|
end
|
|
@@ -250,35 +308,48 @@ describe HexaPDF::Layout::Frame do
|
|
|
250
308
|
|
|
251
309
|
describe "flowing boxes" do
|
|
252
310
|
it "flows inside the frame's outline" do
|
|
253
|
-
check_box({width: 10, height: 20, position: :flow},
|
|
311
|
+
check_box({width: 10, height: 20, margin: 10, position: :flow},
|
|
254
312
|
[0, 90],
|
|
255
|
-
[
|
|
313
|
+
[10, 80, 110, 110],
|
|
314
|
+
[[[10, 10], [110, 10], [110, 80], [10, 80]]])
|
|
256
315
|
end
|
|
257
|
-
end
|
|
258
316
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
317
|
+
it "uses position=default if the box indicates it doesn't support flowing contents" do
|
|
318
|
+
check_box({width: 10, height: 20, margin: 10, position: :flow, doesnt_support_position_flow: true},
|
|
319
|
+
[10, 90],
|
|
320
|
+
[10, 80, 110, 110],
|
|
321
|
+
[[[10, 10], [110, 10], [110, 80], [10, 80]]])
|
|
322
|
+
end
|
|
262
323
|
end
|
|
263
324
|
|
|
264
325
|
it "can't fit the box if there is no available space" do
|
|
265
326
|
@frame.remove_area(Geom2D::Polygon([0, 0], [110, 0], [110, 110], [0, 110]))
|
|
266
327
|
box = HexaPDF::Layout::Box.create
|
|
267
|
-
refute(@frame.fit(box))
|
|
328
|
+
refute(@frame.fit(box).success?)
|
|
268
329
|
end
|
|
269
330
|
|
|
270
|
-
it "
|
|
271
|
-
|
|
272
|
-
box
|
|
273
|
-
|
|
331
|
+
it "handles (but doesn't draw) the box if the its height or width is zero" do
|
|
332
|
+
result = Minitest::Mock.new
|
|
333
|
+
box = Minitest::Mock.new
|
|
334
|
+
|
|
335
|
+
result.expect(:box, box)
|
|
336
|
+
box.expect(:height, 0)
|
|
337
|
+
@frame.draw(@canvas, result)
|
|
338
|
+
|
|
339
|
+
result.expect(:box, box)
|
|
340
|
+
box.expect(:height, 5)
|
|
341
|
+
result.expect(:box, box)
|
|
342
|
+
box.expect(:width, 0)
|
|
343
|
+
@frame.draw(@canvas, result)
|
|
344
|
+
|
|
345
|
+
result.verify
|
|
274
346
|
end
|
|
275
347
|
end
|
|
276
348
|
|
|
277
349
|
describe "split" do
|
|
278
350
|
it "splits the box if necessary" do
|
|
279
351
|
box = HexaPDF::Layout::Box.create(width: 10, height: 10)
|
|
280
|
-
assert_equal([
|
|
281
|
-
assert_nil(@frame.instance_variable_get(:@fit_data).box)
|
|
352
|
+
assert_equal([box, nil], @frame.split(@frame.fit(box)))
|
|
282
353
|
end
|
|
283
354
|
end
|
|
284
355
|
|
|
@@ -348,17 +419,7 @@ describe HexaPDF::Layout::Frame do
|
|
|
348
419
|
frame.remove_area(Geom2D::PolygonSet(top_cut, left_cut))
|
|
349
420
|
|
|
350
421
|
check_regions(frame, [[0, 100, 20, 60], [0, 90, 20, 50], [0, 80, 100, 40],
|
|
351
|
-
[30,
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
describe "remove_area" do
|
|
356
|
-
it "recalculates the contour line only if necessary" do
|
|
357
|
-
contour = Geom2D::Polygon([10, 10], [10, 90], [90, 90], [90, 10])
|
|
358
|
-
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100, contour_line: contour)
|
|
359
|
-
frame.remove_area(Geom2D::Polygon([0, 0], [20, 0], [20, 100], [0, 100]))
|
|
360
|
-
assert_equal([[[20, 10], [90, 10], [90, 90], [20, 90]]],
|
|
361
|
-
frame.contour_line.polygons.map(&:to_a))
|
|
422
|
+
[30, 80, 70, 80], [0, 20, 100, 20]])
|
|
362
423
|
end
|
|
363
424
|
end
|
|
364
425
|
end
|