hexapdf 0.17.1 → 0.17.2
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 +1024 -0
- data/LICENSE +29 -0
- data/README.md +129 -0
- data/Rakefile +109 -0
- data/agpl-3.0.txt +661 -0
- data/examples/001-hello_world.rb +16 -0
- data/examples/002-graphics.rb +275 -0
- data/examples/003-arcs.rb +50 -0
- data/examples/004-optimizing.rb +23 -0
- data/examples/005-merging.rb +27 -0
- data/examples/006-standard_pdf_fonts.rb +73 -0
- data/examples/007-truetype.rb +42 -0
- data/examples/008-show_char_bboxes.rb +55 -0
- data/examples/009-text_layouter_alignment.rb +47 -0
- data/examples/010-text_layouter_inline_boxes.rb +64 -0
- data/examples/011-text_layouter_line_wrapping.rb +57 -0
- data/examples/012-text_layouter_styling.rb +122 -0
- data/examples/013-text_layouter_shapes.rb +176 -0
- data/examples/014-text_in_polygon.rb +60 -0
- data/examples/015-boxes.rb +76 -0
- data/examples/016-frame_automatic_box_placement.rb +90 -0
- data/examples/017-frame_text_flow.rb +60 -0
- data/examples/018-composer.rb +44 -0
- data/examples/019-acro_form.rb +88 -0
- data/examples/emoji-smile.png +0 -0
- data/examples/emoji-wink.png +0 -0
- data/examples/machupicchu.jpg +0 -0
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
- data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
- data/test/data/fonts/Ubuntu-Title.ttf +0 -0
- data/test/data/images/cmyk.jpg +0 -0
- data/test/data/images/fillbytes.jpg +0 -0
- data/test/data/images/gray.jpg +0 -0
- data/test/data/images/greyscale-1bit.png +0 -0
- data/test/data/images/greyscale-2bit.png +0 -0
- data/test/data/images/greyscale-4bit.png +0 -0
- data/test/data/images/greyscale-8bit.png +0 -0
- data/test/data/images/greyscale-alpha-8bit.png +0 -0
- data/test/data/images/greyscale-trns-8bit.png +0 -0
- data/test/data/images/greyscale-with-gamma1.0.png +0 -0
- data/test/data/images/greyscale-with-gamma1.5.png +0 -0
- data/test/data/images/indexed-1bit.png +0 -0
- data/test/data/images/indexed-2bit.png +0 -0
- data/test/data/images/indexed-4bit.png +0 -0
- data/test/data/images/indexed-8bit.png +0 -0
- data/test/data/images/indexed-alpha-4bit.png +0 -0
- data/test/data/images/indexed-alpha-8bit.png +0 -0
- data/test/data/images/rgb.jpg +0 -0
- data/test/data/images/truecolour-8bit.png +0 -0
- data/test/data/images/truecolour-alpha-8bit.png +0 -0
- data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
- data/test/data/images/truecolour-srgb-8bit.png +0 -0
- data/test/data/images/ycck.jpg +0 -0
- data/test/data/minimal.pdf +44 -0
- data/test/data/standard-security-handler/README +9 -0
- data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
- data/test/hexapdf/common_tokenizer_tests.rb +236 -0
- data/test/hexapdf/content/common.rb +39 -0
- data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
- data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
- data/test/hexapdf/content/test_canvas.rb +1279 -0
- data/test/hexapdf/content/test_color_space.rb +176 -0
- data/test/hexapdf/content/test_graphics_state.rb +151 -0
- data/test/hexapdf/content/test_operator.rb +619 -0
- data/test/hexapdf/content/test_parser.rb +99 -0
- data/test/hexapdf/content/test_processor.rb +163 -0
- data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
- data/test/hexapdf/document/test_files.rb +72 -0
- data/test/hexapdf/document/test_fonts.rb +60 -0
- data/test/hexapdf/document/test_images.rb +72 -0
- data/test/hexapdf/document/test_pages.rb +130 -0
- data/test/hexapdf/encryption/common.rb +87 -0
- data/test/hexapdf/encryption/test_aes.rb +129 -0
- data/test/hexapdf/encryption/test_arc4.rb +39 -0
- data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
- data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
- data/test/hexapdf/encryption/test_identity.rb +21 -0
- data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
- data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
- data/test/hexapdf/encryption/test_security_handler.rb +380 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
- data/test/hexapdf/filter/common.rb +53 -0
- data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
- data/test/hexapdf/filter/test_crypt.rb +21 -0
- data/test/hexapdf/filter/test_encryption.rb +24 -0
- data/test/hexapdf/filter/test_flate_decode.rb +44 -0
- data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
- data/test/hexapdf/filter/test_predictor.rb +219 -0
- data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
- data/test/hexapdf/font/cmap/test_parser.rb +102 -0
- data/test/hexapdf/font/cmap/test_writer.rb +66 -0
- data/test/hexapdf/font/encoding/test_base.rb +45 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
- data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
- data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
- data/test/hexapdf/font/test_cmap.rb +104 -0
- data/test/hexapdf/font/test_encoding.rb +27 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
- data/test/hexapdf/font/true_type/common.rb +17 -0
- data/test/hexapdf/font/true_type/table/common.rb +27 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
- data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
- data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
- data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
- data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
- data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
- data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
- data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
- data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
- data/test/hexapdf/font/true_type/test_builder.rb +42 -0
- data/test/hexapdf/font/true_type/test_font.rb +116 -0
- data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
- data/test/hexapdf/font/true_type/test_table.rb +48 -0
- data/test/hexapdf/font/type1/common.rb +6 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
- data/test/hexapdf/font/type1/test_font.rb +104 -0
- data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
- data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
- data/test/hexapdf/font_loader/test_from_file.rb +36 -0
- data/test/hexapdf/font_loader/test_standard14.rb +33 -0
- data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
- data/test/hexapdf/image_loader/test_pdf.rb +47 -0
- data/test/hexapdf/image_loader/test_png.rb +259 -0
- data/test/hexapdf/layout/test_box.rb +154 -0
- data/test/hexapdf/layout/test_frame.rb +350 -0
- data/test/hexapdf/layout/test_image_box.rb +73 -0
- data/test/hexapdf/layout/test_inline_box.rb +71 -0
- data/test/hexapdf/layout/test_line.rb +206 -0
- data/test/hexapdf/layout/test_style.rb +790 -0
- data/test/hexapdf/layout/test_text_box.rb +140 -0
- data/test/hexapdf/layout/test_text_fragment.rb +375 -0
- data/test/hexapdf/layout/test_text_layouter.rb +758 -0
- data/test/hexapdf/layout/test_text_shaper.rb +62 -0
- data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
- data/test/hexapdf/task/test_dereference.rb +51 -0
- data/test/hexapdf/task/test_optimize.rb +162 -0
- data/test/hexapdf/test_composer.rb +258 -0
- data/test/hexapdf/test_configuration.rb +93 -0
- data/test/hexapdf/test_data_dir.rb +32 -0
- data/test/hexapdf/test_dictionary.rb +340 -0
- data/test/hexapdf/test_dictionary_fields.rb +269 -0
- data/test/hexapdf/test_document.rb +641 -0
- data/test/hexapdf/test_filter.rb +100 -0
- data/test/hexapdf/test_importer.rb +106 -0
- data/test/hexapdf/test_object.rb +258 -0
- data/test/hexapdf/test_parser.rb +645 -0
- data/test/hexapdf/test_pdf_array.rb +169 -0
- data/test/hexapdf/test_rectangle.rb +73 -0
- data/test/hexapdf/test_reference.rb +50 -0
- data/test/hexapdf/test_revision.rb +188 -0
- data/test/hexapdf/test_revisions.rb +196 -0
- data/test/hexapdf/test_serializer.rb +195 -0
- data/test/hexapdf/test_stream.rb +274 -0
- data/test/hexapdf/test_tokenizer.rb +80 -0
- data/test/hexapdf/test_type.rb +18 -0
- data/test/hexapdf/test_writer.rb +140 -0
- data/test/hexapdf/test_xref_section.rb +61 -0
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
- data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
- data/test/hexapdf/type/acro_form/test_field.rb +259 -0
- data/test/hexapdf/type/acro_form/test_form.rb +357 -0
- data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
- data/test/hexapdf/type/actions/test_launch.rb +24 -0
- data/test/hexapdf/type/actions/test_uri.rb +23 -0
- data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
- data/test/hexapdf/type/annotations/test_text.rb +34 -0
- data/test/hexapdf/type/annotations/test_widget.rb +225 -0
- data/test/hexapdf/type/test_annotation.rb +97 -0
- data/test/hexapdf/type/test_catalog.rb +48 -0
- data/test/hexapdf/type/test_cid_font.rb +61 -0
- data/test/hexapdf/type/test_file_specification.rb +141 -0
- data/test/hexapdf/type/test_font.rb +67 -0
- data/test/hexapdf/type/test_font_descriptor.rb +61 -0
- data/test/hexapdf/type/test_font_simple.rb +176 -0
- data/test/hexapdf/type/test_font_true_type.rb +31 -0
- data/test/hexapdf/type/test_font_type0.rb +120 -0
- data/test/hexapdf/type/test_font_type1.rb +142 -0
- data/test/hexapdf/type/test_font_type3.rb +26 -0
- data/test/hexapdf/type/test_form.rb +120 -0
- data/test/hexapdf/type/test_image.rb +261 -0
- data/test/hexapdf/type/test_info.rb +9 -0
- data/test/hexapdf/type/test_object_stream.rb +117 -0
- data/test/hexapdf/type/test_page.rb +598 -0
- data/test/hexapdf/type/test_page_tree_node.rb +315 -0
- data/test/hexapdf/type/test_resources.rb +209 -0
- data/test/hexapdf/type/test_trailer.rb +116 -0
- data/test/hexapdf/type/test_xref_stream.rb +143 -0
- data/test/hexapdf/utils/test_bit_field.rb +63 -0
- data/test/hexapdf/utils/test_bit_stream.rb +69 -0
- data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
- data/test/hexapdf/utils/test_lru_cache.rb +22 -0
- data/test/hexapdf/utils/test_object_hash.rb +120 -0
- data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
- data/test/test_helper.rb +58 -0
- metadata +263 -3
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/layout/frame'
|
|
5
|
+
require 'hexapdf/layout/box'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Layout::Frame do
|
|
8
|
+
before do
|
|
9
|
+
@frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "allows access to the bounding box attributes" do
|
|
13
|
+
assert_equal(5, @frame.left)
|
|
14
|
+
assert_equal(10, @frame.bottom)
|
|
15
|
+
assert_equal(100, @frame.width)
|
|
16
|
+
assert_equal(150, @frame.height)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "allows access to the current region attributes" do
|
|
20
|
+
assert_equal(5, @frame.x)
|
|
21
|
+
assert_equal(160, @frame.y)
|
|
22
|
+
assert_equal(100, @frame.available_width)
|
|
23
|
+
assert_equal(150, @frame.available_height)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "contour_line" do
|
|
27
|
+
it "has a contour line equal to the bounding box by default" do
|
|
28
|
+
assert_equal([[5, 10], [105, 10], [105, 160], [5, 160]], @frame.contour_line.polygons[0].to_a)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "can have a custom contour line polygon" do
|
|
32
|
+
contour_line = Geom2D::Polygon([0, 0], [10, 10], [10, 0])
|
|
33
|
+
frame = HexaPDF::Layout::Frame.new(0, 0, 10, 10, contour_line: contour_line)
|
|
34
|
+
assert_same(contour_line, frame.contour_line)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "returns an appropriate width specification object" do
|
|
39
|
+
ws = @frame.width_specification(10)
|
|
40
|
+
assert_kind_of(HexaPDF::Layout::WidthFromPolygon, ws)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "fit and draw" do
|
|
44
|
+
before do
|
|
45
|
+
@frame = HexaPDF::Layout::Frame.new(10, 10, 100, 100)
|
|
46
|
+
@canvas = Minitest::Mock.new
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Creates a box with the given option, storing it in @box, and draws it inside @frame. It is
|
|
50
|
+
# checked whether the box coordinates are pos and whether the frame has the shape given by
|
|
51
|
+
# points.
|
|
52
|
+
def check_box(box_opts, pos, points)
|
|
53
|
+
@box = HexaPDF::Layout::Box.create(**box_opts) {}
|
|
54
|
+
@canvas.expect(:translate, nil, pos)
|
|
55
|
+
assert(@frame.draw(@canvas, @box))
|
|
56
|
+
assert_equal(points, @frame.shape.polygons.map(&:to_a))
|
|
57
|
+
@canvas.verify
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Removes a 10pt area from the :left, :right or :top.
|
|
61
|
+
def remove_area(*areas)
|
|
62
|
+
areas.each do |area|
|
|
63
|
+
@frame.remove_area(
|
|
64
|
+
case area
|
|
65
|
+
when :left then Geom2D::Polygon([10, 10], [10, 110], [20, 110], [20, 10])
|
|
66
|
+
when :right then Geom2D::Polygon([100, 10], [100, 110], [110, 110], [110, 10])
|
|
67
|
+
when :top then Geom2D::Polygon([10, 110], [110, 110], [110, 100], [10, 100])
|
|
68
|
+
end
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "absolute position" do
|
|
74
|
+
it "draws the box at the given absolute position" do
|
|
75
|
+
check_box(
|
|
76
|
+
{width: 50, height: 50, position: :absolute, position_hint: [10, 10]},
|
|
77
|
+
[20, 20],
|
|
78
|
+
[[[10, 10], [110, 10], [110, 110], [10, 110]],
|
|
79
|
+
[[20, 20], [70, 20], [70, 70], [20, 70]]]
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "determines the available space for #fit by using the space to the right and above" do
|
|
84
|
+
check_box(
|
|
85
|
+
{position: :absolute, position_hint: [10, 10]},
|
|
86
|
+
[20, 20],
|
|
87
|
+
[[[10, 10], [110, 10], [110, 20], [20, 20], [20, 110], [10, 110]]]
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "always removes the whole margin box from the frame" do
|
|
92
|
+
check_box(
|
|
93
|
+
{width: 50, height: 50, position: :absolute, position_hint: [10, 10],
|
|
94
|
+
margin: [10, 20, 30, 40]},
|
|
95
|
+
[20, 20],
|
|
96
|
+
[[[10, 80], [90, 80], [90, 10], [110, 10], [110, 110], [10, 110]]]
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe "default position" do
|
|
102
|
+
it "draws the box on the left side" do
|
|
103
|
+
check_box({width: 50, height: 50},
|
|
104
|
+
[10, 60],
|
|
105
|
+
[[[10, 10], [110, 10], [110, 60], [10, 60]]])
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "draws the box on the right side" do
|
|
109
|
+
check_box({width: 50, height: 50, position_hint: :right},
|
|
110
|
+
[60, 60],
|
|
111
|
+
[[[10, 10], [110, 10], [110, 60], [10, 60]]])
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "draws the box in the center" do
|
|
115
|
+
check_box({width: 50, height: 50, position_hint: :center},
|
|
116
|
+
[35, 60],
|
|
117
|
+
[[[10, 10], [110, 10], [110, 60], [10, 60]]])
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe "with margin" do
|
|
121
|
+
[:left, :center, :right].each do |hint|
|
|
122
|
+
it "ignores all margins if the box fills the whole frame, with position hint #{hint}" do
|
|
123
|
+
check_box({margin: 10, position_hint: hint},
|
|
124
|
+
[10, 10], [])
|
|
125
|
+
assert_equal(100, @box.width)
|
|
126
|
+
assert_equal(100, @box.height)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "ignores the left/top/right margin if the available bounds coincide with the " \
|
|
130
|
+
"frame's, with position hint #{hint}" do
|
|
131
|
+
check_box({height: 50, margin: 10, position_hint: hint},
|
|
132
|
+
[10, 60],
|
|
133
|
+
[[[10, 10], [110, 10], [110, 50], [10, 50]]])
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "doesn't ignore top margin if the available bounds' top doesn't coincide with the " \
|
|
137
|
+
"frame's top, with position hint #{hint}" do
|
|
138
|
+
remove_area(:top)
|
|
139
|
+
check_box({height: 50, margin: 10, position_hint: hint},
|
|
140
|
+
[10, 40],
|
|
141
|
+
[[[10, 10], [110, 10], [110, 30], [10, 30]]])
|
|
142
|
+
assert_equal(100, @box.width)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "doesn't ignore left margin if the available bounds' left doesn't coincide with the " \
|
|
146
|
+
"frame's left, with position hint #{hint}" do
|
|
147
|
+
remove_area(:left)
|
|
148
|
+
check_box({height: 50, margin: 10, position_hint: hint},
|
|
149
|
+
[30, 60],
|
|
150
|
+
[[[20, 10], [110, 10], [110, 50], [20, 50]]])
|
|
151
|
+
assert_equal(80, @box.width)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "doesn't ignore right margin if the available bounds' right doesn't coincide with " \
|
|
155
|
+
"the frame's right, with position hint #{hint}" do
|
|
156
|
+
remove_area(:right)
|
|
157
|
+
check_box({height: 50, margin: 10, position_hint: hint},
|
|
158
|
+
[10, 60],
|
|
159
|
+
[[[10, 10], [100, 10], [100, 50], [10, 50]]])
|
|
160
|
+
assert_equal(80, @box.width)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it "perfectly centers a box if possible, margins ignored" do
|
|
165
|
+
check_box({width: 50, height: 10, margin: [10, 10, 10, 20], position_hint: :center},
|
|
166
|
+
[35, 100],
|
|
167
|
+
[[[10, 10], [110, 10], [110, 90], [10, 90]]])
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "perfectly centers a box if possible, margins not ignored" do
|
|
171
|
+
remove_area(:left, :right)
|
|
172
|
+
check_box({width: 40, height: 10, margin: [10, 10, 10, 20], position_hint: :center},
|
|
173
|
+
[40, 100],
|
|
174
|
+
[[[20, 10], [100, 10], [100, 90], [20, 90]]])
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "centers a box as good as possible when margins aren't equal" do
|
|
178
|
+
remove_area(:left, :right)
|
|
179
|
+
check_box({width: 20, height: 10, margin: [10, 10, 10, 40], position_hint: :center},
|
|
180
|
+
[65, 100],
|
|
181
|
+
[[[20, 10], [100, 10], [100, 90], [20, 90]]])
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe "floating boxes" do
|
|
187
|
+
it "draws the box on the left side" do
|
|
188
|
+
check_box({width: 50, height: 50, position: :float},
|
|
189
|
+
[10, 60],
|
|
190
|
+
[[[10, 10], [110, 10], [110, 110], [60, 110], [60, 60], [10, 60]]])
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it "draws the box on the right side" do
|
|
194
|
+
check_box({width: 50, height: 50, position: :float, position_hint: :right},
|
|
195
|
+
[60, 60],
|
|
196
|
+
[[[10, 10], [110, 10], [110, 60], [60, 60], [60, 110], [10, 110]]])
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
describe "with margin" do
|
|
200
|
+
[:left, :right].each do |hint|
|
|
201
|
+
it "ignores all margins if the box fills the whole frame, with position hint #{hint}" do
|
|
202
|
+
check_box({margin: 10, position: :float, position_hint: hint},
|
|
203
|
+
[10, 10], [])
|
|
204
|
+
assert_equal(100, @box.width)
|
|
205
|
+
assert_equal(100, @box.height)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it "ignores the left, but not the right margin if aligned left to the frame border" do
|
|
210
|
+
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :left},
|
|
211
|
+
[10, 60],
|
|
212
|
+
[[[10, 10], [110, 10], [110, 110], [70, 110], [70, 50], [10, 50]]])
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it "uses the left and the right margin if aligned left and not to the frame border" do
|
|
216
|
+
remove_area(:left)
|
|
217
|
+
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :left},
|
|
218
|
+
[30, 60],
|
|
219
|
+
[[[20, 10], [110, 10], [110, 110], [90, 110], [90, 50], [20, 50]]])
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "ignores the right, but not the left margin if aligned right to the frame border" do
|
|
223
|
+
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :right},
|
|
224
|
+
[60, 60],
|
|
225
|
+
[[[10, 10], [110, 10], [110, 50], [50, 50], [50, 110], [10, 110]]])
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
it "uses the left and the right margin if aligned right and not to the frame border" do
|
|
229
|
+
remove_area(:right)
|
|
230
|
+
check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :right},
|
|
231
|
+
[40, 60],
|
|
232
|
+
[[[10, 10], [100, 10], [100, 50], [30, 50], [30, 110], [10, 110]]])
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
describe "flowing boxes" do
|
|
238
|
+
it "flows inside the frame's outline" do
|
|
239
|
+
check_box({width: 10, height: 20, position: :flow},
|
|
240
|
+
[0, 90],
|
|
241
|
+
[[[10, 10], [110, 10], [110, 90], [10, 90]]])
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it "doesn't draw the box if it doesn't fit into the available space" do
|
|
246
|
+
box = HexaPDF::Layout::Box.create(width: 150, height: 50)
|
|
247
|
+
refute(@frame.draw(@canvas, box))
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it "can't fit the box if there is no available space" do
|
|
251
|
+
@frame.remove_area(Geom2D::Polygon([0, 0], [110, 0], [110, 110], [0, 110]))
|
|
252
|
+
box = HexaPDF::Layout::Box.create
|
|
253
|
+
refute(@frame.fit(box))
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "draws the box even if the box's height is zero" do
|
|
257
|
+
box = HexaPDF::Layout::Box.create
|
|
258
|
+
box.define_singleton_method(:height) { 0 }
|
|
259
|
+
assert(@frame.draw(@canvas, box))
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
describe "split" do
|
|
264
|
+
it "splits the box if necessary" do
|
|
265
|
+
box = HexaPDF::Layout::Box.create(width: 10, height: 10)
|
|
266
|
+
assert_equal([nil, box], @frame.split(box))
|
|
267
|
+
assert_nil(@frame.instance_variable_get(:@fit_data).box)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
describe "find_next_region" do
|
|
272
|
+
# Checks all availability regions of the frame
|
|
273
|
+
def check_regions(frame, regions)
|
|
274
|
+
regions.each_with_index do |region, index|
|
|
275
|
+
assert_equal(region[0], frame.x, "region #{index} invalid x")
|
|
276
|
+
assert_equal(region[1], frame.y, "region #{index} invalid y")
|
|
277
|
+
assert_equal(region[2], frame.available_width, "region #{index} invalid available width")
|
|
278
|
+
assert_equal(region[3], frame.available_height, "region #{index} invalid available height")
|
|
279
|
+
frame.find_next_region
|
|
280
|
+
end
|
|
281
|
+
assert_equal(0, frame.x)
|
|
282
|
+
assert_equal(0, frame.y)
|
|
283
|
+
assert_equal(0, frame.available_width)
|
|
284
|
+
assert_equal(0, frame.available_height)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# o------+
|
|
288
|
+
# | |
|
|
289
|
+
# | |
|
|
290
|
+
# | |
|
|
291
|
+
# +------+
|
|
292
|
+
it "works for a rectangular region" do
|
|
293
|
+
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 300)
|
|
294
|
+
check_regions(frame, [[0, 300, 100, 300]])
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# o--------+
|
|
298
|
+
# | |
|
|
299
|
+
# | +--+ |
|
|
300
|
+
# | | | |
|
|
301
|
+
# | +--+ |
|
|
302
|
+
# | |
|
|
303
|
+
# +--------+
|
|
304
|
+
it "works for a region with a hole" do
|
|
305
|
+
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
|
306
|
+
frame.remove_area(Geom2D::Polygon([20, 20], [80, 20], [80, 80], [20, 80]))
|
|
307
|
+
check_regions(frame, [[0, 100, 100, 20], [0, 100, 20, 100],
|
|
308
|
+
[0, 80, 20, 80], [0, 20, 100, 20]])
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# o--+ +--+
|
|
312
|
+
# | | | |
|
|
313
|
+
# | +--+ |
|
|
314
|
+
# | |
|
|
315
|
+
# +--------+
|
|
316
|
+
it "works for a u-shaped frame" do
|
|
317
|
+
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
|
318
|
+
frame.remove_area(Geom2D::Polygon([30, 100], [70, 100], [70, 60], [30, 60]))
|
|
319
|
+
check_regions(frame, [[0, 100, 30, 100], [0, 60, 100, 60]])
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# o---+ +--+
|
|
323
|
+
# | | +--+ |
|
|
324
|
+
# | +--+ |
|
|
325
|
+
# | |
|
|
326
|
+
# +----+ |
|
|
327
|
+
# +----+ |
|
|
328
|
+
# | |
|
|
329
|
+
# +------------+
|
|
330
|
+
it "works for a complicated frame" do
|
|
331
|
+
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
|
|
332
|
+
top_cut = Geom2D::Polygon([20, 100], [20, 80], [40, 80], [40, 90], [60, 90], [60, 100])
|
|
333
|
+
left_cut = Geom2D::Polygon([0, 20], [30, 20], [30, 40], [0, 40])
|
|
334
|
+
frame.remove_area(Geom2D::PolygonSet(top_cut, left_cut))
|
|
335
|
+
|
|
336
|
+
check_regions(frame, [[0, 100, 20, 60], [0, 90, 20, 50], [0, 80, 100, 40],
|
|
337
|
+
[30, 40, 70, 40], [0, 20, 100, 20]])
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
describe "remove_area" do
|
|
342
|
+
it "recalculates the contour line only if necessary" do
|
|
343
|
+
contour = Geom2D::Polygon([10, 10], [10, 90], [90, 90], [90, 10])
|
|
344
|
+
frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100, contour_line: contour)
|
|
345
|
+
frame.remove_area(Geom2D::Polygon([0, 0], [20, 0], [20, 100], [0, 100]))
|
|
346
|
+
assert_equal([[[20, 10], [90, 10], [90, 90], [20, 90]]],
|
|
347
|
+
frame.contour_line.polygons.map(&:to_a))
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require_relative '../content/common'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
require 'hexapdf/layout/image_box'
|
|
7
|
+
|
|
8
|
+
describe HexaPDF::Layout::ImageBox do
|
|
9
|
+
before do
|
|
10
|
+
@image = HexaPDF::Stream.new({Subtype: :Image}, stream: '')
|
|
11
|
+
@image.define_singleton_method(:width) { 40 }
|
|
12
|
+
@image.define_singleton_method(:height) { 20 }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create_box(**kwargs)
|
|
16
|
+
HexaPDF::Layout::ImageBox.new(@image, **kwargs)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "initialize" do
|
|
20
|
+
it "takes the image to be displayed" do
|
|
21
|
+
box = create_box
|
|
22
|
+
assert_equal(@image, box.image)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "fit" do
|
|
27
|
+
it "fits with fixed dimensions" do
|
|
28
|
+
box = create_box(width: 50, height: 30, style: {padding: [10, 4, 6, 2]})
|
|
29
|
+
assert(box.fit(100, 100, nil))
|
|
30
|
+
assert_equal(50, box.width)
|
|
31
|
+
assert_equal(30, box.height)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "fits with a fixed width" do
|
|
35
|
+
box = create_box(width: 60, style: {padding: [10, 4, 6, 2]})
|
|
36
|
+
assert(box.fit(100, 100, nil))
|
|
37
|
+
assert_equal(60, box.width)
|
|
38
|
+
assert_equal(43, box.height)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "fits with a fixed height" do
|
|
42
|
+
box = create_box(height: 40, style: {padding: [10, 4, 6, 2]})
|
|
43
|
+
assert(box.fit(100, 100, nil))
|
|
44
|
+
assert_equal(54, box.width)
|
|
45
|
+
assert_equal(40, box.height)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "fits with auto-scaling to available space" do
|
|
49
|
+
box = create_box(style: {padding: [10, 4, 6, 2]})
|
|
50
|
+
assert(box.fit(100, 100, nil))
|
|
51
|
+
assert_equal(100, box.width)
|
|
52
|
+
assert_equal(63, box.height)
|
|
53
|
+
|
|
54
|
+
assert(box.fit(100, 30, nil))
|
|
55
|
+
assert_equal(34, box.width)
|
|
56
|
+
assert_equal(30, box.height)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe "draw" do
|
|
61
|
+
it "draws the image" do
|
|
62
|
+
box = create_box(height: 40, style: {padding: [10, 4, 6, 2]})
|
|
63
|
+
box.fit(100, 100, nil)
|
|
64
|
+
|
|
65
|
+
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
66
|
+
box.draw(@canvas, 0, 0)
|
|
67
|
+
assert_operators(@canvas.contents, [[:save_graphics_state],
|
|
68
|
+
[:concatenate_matrix, [48, 0, 0, 24, 2, 6]],
|
|
69
|
+
[:paint_xobject, [:XO1]],
|
|
70
|
+
[:restore_graphics_state]])
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/layout/inline_box'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Layout::InlineBox do
|
|
7
|
+
before do
|
|
8
|
+
@box = HexaPDF::Layout::InlineBox.create(width: 10, height: 15, margin: [15, 10])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "needs a box to wrap and an optional alignment on initialization" do
|
|
12
|
+
ibox = HexaPDF::Layout::InlineBox.new(@box)
|
|
13
|
+
assert_equal(@box, ibox.box)
|
|
14
|
+
assert_equal(:baseline, ibox.valign)
|
|
15
|
+
|
|
16
|
+
ibox = HexaPDF::Layout::InlineBox.new(@box, valign: :top)
|
|
17
|
+
assert_equal(:top, ibox.valign)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "draws the wrapped box at the correct position" do
|
|
21
|
+
canvas = Object.new
|
|
22
|
+
block = lambda do |c, x, y|
|
|
23
|
+
assert_equal(canvas, c)
|
|
24
|
+
assert_equal(10, x)
|
|
25
|
+
assert_equal(15, y)
|
|
26
|
+
end
|
|
27
|
+
@box.box.stub(:draw, block) do
|
|
28
|
+
@box.draw(canvas, 0, 0)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "returns true if the inline box is empty with no drawing operations" do
|
|
33
|
+
assert(@box.empty?)
|
|
34
|
+
refute(HexaPDF::Layout::InlineBox.create(width: 10, height: 15) {}.empty?)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "valign" do
|
|
38
|
+
it "has a default value of :baseline" do
|
|
39
|
+
assert_equal(:baseline, @box.valign)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "can be changed on creation" do
|
|
43
|
+
box = HexaPDF::Layout::InlineBox.create(width: 10, height: 15, valign: :test)
|
|
44
|
+
assert_equal(:test, box.valign)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "returns the width including margins" do
|
|
49
|
+
assert_equal(30, @box.width)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "returns the height including margins" do
|
|
53
|
+
assert_equal(45, @box.height)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "returns 0 for x_min" do
|
|
57
|
+
assert_equal(0, @box.x_min)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "returns width for x_max" do
|
|
61
|
+
assert_equal(@box.width, @box.x_max)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "returns 0 for y_min" do
|
|
65
|
+
assert_equal(0, @box.y_min)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "returns height for y_max" do
|
|
69
|
+
assert_equal(@box.height, @box.y_max)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Layout::Line::HeightCalculator do
|
|
7
|
+
before do
|
|
8
|
+
@calc = HexaPDF::Layout::Line::HeightCalculator.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "simulate the height as if an item was added" do
|
|
12
|
+
@calc << HexaPDF::Layout::InlineBox.create(width: 10, height: 20, valign: :baseline) {}
|
|
13
|
+
assert_equal([0, 20, 0, 0], @calc.result)
|
|
14
|
+
new_item = HexaPDF::Layout::InlineBox.create(width: 10, height: 30, valign: :top) {}
|
|
15
|
+
assert_equal([-10, 20, 30], @calc.simulate_height(new_item))
|
|
16
|
+
assert_equal([0, 20, 0, 0], @calc.result)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe HexaPDF::Layout::Line do
|
|
21
|
+
before do
|
|
22
|
+
@doc = HexaPDF::Document.new
|
|
23
|
+
@font = @doc.fonts.add("Times", custom_encoding: true)
|
|
24
|
+
@line = HexaPDF::Layout::Line.new
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def setup_fragment(text)
|
|
28
|
+
HexaPDF::Layout::TextFragment.create(text, font: @font, font_size: 10)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def setup_box(width, height, valign = :baseline)
|
|
32
|
+
HexaPDF::Layout::InlineBox.create(width: width, height: height, valign: valign) {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "initialize" do
|
|
36
|
+
it "allows setting the items of the line fragment" do
|
|
37
|
+
frag1 = setup_fragment("Hello")
|
|
38
|
+
frag2 = HexaPDF::Layout::TextFragment.new(frag1.items.slice!(3, 2), frag1.style)
|
|
39
|
+
line = HexaPDF::Layout::Line.new([frag1, frag2])
|
|
40
|
+
assert_equal(1, line.items.count)
|
|
41
|
+
assert_equal(5, line.items[0].items.count)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe "add" do
|
|
46
|
+
it "adds items to the line" do
|
|
47
|
+
@line << :test << :other
|
|
48
|
+
assert_equal([:test, :other], @line.items)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "combines text fragments if possible" do
|
|
52
|
+
frag1 = setup_fragment("Home")
|
|
53
|
+
frag2 = HexaPDF::Layout::TextFragment.new(frag1.items.slice!(2, 2), frag1.style)
|
|
54
|
+
@line << setup_fragment("o") << :other << frag1 << frag2
|
|
55
|
+
assert_equal(3, @line.items.length)
|
|
56
|
+
assert_equal(4, @line.items.last.items.length)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "duplicates the first of two combinable text fragments if its items are frozen" do
|
|
60
|
+
frag1 = setup_fragment("Home")
|
|
61
|
+
frag2 = HexaPDF::Layout::TextFragment.new(frag1.items.slice!(2, 2), frag1.style)
|
|
62
|
+
frag1.items.freeze
|
|
63
|
+
frag2.items.freeze
|
|
64
|
+
|
|
65
|
+
@line << setup_fragment("o") << frag1 << frag2 << :other
|
|
66
|
+
assert_equal(3, @line.items.length)
|
|
67
|
+
assert_equal(4, @line.items[-2].items.length)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe "with text fragments" do
|
|
72
|
+
before do
|
|
73
|
+
@frag_h = setup_fragment("H")
|
|
74
|
+
@frag_y = setup_fragment("y")
|
|
75
|
+
@line << @frag_h << @frag_y << @frag_h
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "calculates the various x/y values correctly" do
|
|
79
|
+
assert_equal(@frag_h.x_min, @line.x_min)
|
|
80
|
+
assert_equal(@frag_h.width + @frag_y.width + @frag_h.x_max, @line.x_max)
|
|
81
|
+
assert_equal(@frag_y.y_min, @line.y_min)
|
|
82
|
+
assert_equal(@frag_h.y_max, @line.y_max)
|
|
83
|
+
assert_equal(@frag_y.y_min, @line.text_y_min)
|
|
84
|
+
assert_equal(@frag_h.y_max, @line.text_y_max)
|
|
85
|
+
assert_equal(2 * @frag_h.width + @frag_y.width, @line.width)
|
|
86
|
+
assert_equal(@frag_h.y_max - @frag_y.y_min, @line.height)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "and with inline boxes" do
|
|
90
|
+
it "x_min is correct if an inline box is the first item" do
|
|
91
|
+
@line.items.unshift(setup_box(10, 10))
|
|
92
|
+
assert_equal(0, @line.x_min)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "x_max is correct if an inline box is the last item" do
|
|
96
|
+
@line << setup_box(10, 10)
|
|
97
|
+
assert_equal(@line.width, @line.x_max)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "doesn't change text_y_min/text_y_max" do
|
|
101
|
+
text_y_min, text_y_max = @line.text_y_min, @line.text_y_max
|
|
102
|
+
@line << setup_box(10, 30, :text_top) << setup_box(10, 30, :text_bottom)
|
|
103
|
+
@line.clear_cache
|
|
104
|
+
assert_equal(text_y_min, @line.text_y_min)
|
|
105
|
+
assert_equal(text_y_max, @line.text_y_max)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "y values are not changed if all boxes are smaller than the text's height" do
|
|
109
|
+
*y_values = @line.y_min, @line.y_max, @line.text_y_min, @line.text_y_max
|
|
110
|
+
@line << setup_box(10, 5, :baseline)
|
|
111
|
+
@line.clear_cache
|
|
112
|
+
assert_equal(y_values, [@line.y_min, @line.y_max, @line.text_y_min, @line.text_y_max])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "changes y_max to fit if baseline boxes are higher than the text" do
|
|
116
|
+
y_min = @line.y_min
|
|
117
|
+
box = setup_box(10, 50, :baseline)
|
|
118
|
+
@line.add(box)
|
|
119
|
+
|
|
120
|
+
@line.clear_cache
|
|
121
|
+
assert_equal(50, @line.y_max)
|
|
122
|
+
assert_equal(y_min, @line.y_min)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "changes y_max to fit if text_bottom boxes are higher than the text" do
|
|
126
|
+
y_min = @line.y_min
|
|
127
|
+
box = setup_box(10, 50, :text_bottom)
|
|
128
|
+
@line.add(box)
|
|
129
|
+
|
|
130
|
+
@line.clear_cache
|
|
131
|
+
assert_equal(50 + @line.text_y_min, @line.y_max)
|
|
132
|
+
assert_equal(y_min, @line.y_min)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "changes y_max to fit if bottom boxes are higher than the text" do
|
|
136
|
+
y_min = @line.y_min
|
|
137
|
+
box = setup_box(10, 50, :bottom)
|
|
138
|
+
@line.add(box)
|
|
139
|
+
|
|
140
|
+
@line.clear_cache
|
|
141
|
+
assert_equal(50 + @line.text_y_min, @line.y_max)
|
|
142
|
+
assert_equal(y_min, @line.y_min)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "changes y_min to fit if text_top/top boxes are higher than the text" do
|
|
146
|
+
y_max = @line.y_max
|
|
147
|
+
box = setup_box(10, 50, :text_top)
|
|
148
|
+
@line.add(box)
|
|
149
|
+
|
|
150
|
+
@line.clear_cache
|
|
151
|
+
assert_equal(@line.text_y_max - 50, @line.y_min)
|
|
152
|
+
assert_equal(y_max, @line.y_max)
|
|
153
|
+
|
|
154
|
+
box.instance_variable_set(:@valign, :top)
|
|
155
|
+
@line.clear_cache
|
|
156
|
+
assert_equal(@line.text_y_max - 50, @line.y_min)
|
|
157
|
+
assert_equal(y_max, @line.y_max)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "changes y_min/y_max to fit if boxes are aligned in both directions" do
|
|
161
|
+
@line << setup_box(10, 20, :text_top) <<
|
|
162
|
+
setup_box(10, 20, :text_bottom) <<
|
|
163
|
+
setup_box(10, 20, :top) <<
|
|
164
|
+
setup_box(10, 70, :bottom)
|
|
165
|
+
assert_equal(@line.text_y_max - 20, @line.y_min)
|
|
166
|
+
assert_equal(@line.text_y_max - 20 + 70, @line.y_max)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "fails when accessing a vertical measurement if an item uses an invalid valign value" do
|
|
172
|
+
@line << setup_box(10, 20, :invalid)
|
|
173
|
+
assert_raises(HexaPDF::Error) { @line.y_min }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe "each" do
|
|
177
|
+
it "iterates over all items and yields them with their offset values" do
|
|
178
|
+
@line << setup_fragment("H") <<
|
|
179
|
+
setup_box(10, 10, :top) <<
|
|
180
|
+
setup_box(10, 10, :text_top) <<
|
|
181
|
+
setup_box(10, 10, :baseline) <<
|
|
182
|
+
setup_box(10, 10, :text_bottom) <<
|
|
183
|
+
setup_box(10, 10, :bottom)
|
|
184
|
+
result = [
|
|
185
|
+
[@line.items[0], 0, 0],
|
|
186
|
+
[@line.items[1], @line.items[0].width, @line.y_max - 10],
|
|
187
|
+
[@line.items[2], @line.items[0].width + 10, @line.text_y_max - 10],
|
|
188
|
+
[@line.items[3], @line.items[0].width + 20, 0],
|
|
189
|
+
[@line.items[4], @line.items[0].width + 30, @line.text_y_min],
|
|
190
|
+
[@line.items[5], @line.items[0].width + 40, @line.y_min],
|
|
191
|
+
]
|
|
192
|
+
assert_equal(result, @line.to_enum(:each).map {|*a| a })
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
it "fails if an item uses an invalid valign value" do
|
|
196
|
+
@line << setup_box(10, 10, :invalid)
|
|
197
|
+
assert_raises(HexaPDF::Error) { @line.each {} }
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it "allows ignoring line justification" do
|
|
202
|
+
refute(@line.ignore_justification?)
|
|
203
|
+
@line.ignore_justification!
|
|
204
|
+
assert(@line.ignore_justification?)
|
|
205
|
+
end
|
|
206
|
+
end
|