hexapdf 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +33 -0
- data/CONTRIBUTERS +1 -1
- data/LICENSE +1 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/hexapdf +1 -1
- data/examples/text_layouter_styling.rb +1 -2
- data/lib/hexapdf.rb +2 -2
- data/lib/hexapdf/cli.rb +3 -3
- data/lib/hexapdf/cli/batch.rb +5 -5
- data/lib/hexapdf/cli/command.rb +15 -17
- data/lib/hexapdf/cli/files.rb +3 -3
- data/lib/hexapdf/cli/images.rb +3 -4
- data/lib/hexapdf/cli/info.rb +5 -5
- data/lib/hexapdf/cli/inspect.rb +6 -6
- data/lib/hexapdf/cli/merge.rb +6 -6
- data/lib/hexapdf/cli/modify.rb +4 -4
- data/lib/hexapdf/cli/optimize.rb +3 -3
- data/lib/hexapdf/configuration.rb +4 -5
- data/lib/hexapdf/content.rb +2 -2
- data/lib/hexapdf/content/canvas.rb +35 -36
- data/lib/hexapdf/content/color_space.rb +9 -14
- data/lib/hexapdf/content/graphic_object.rb +2 -2
- data/lib/hexapdf/content/graphic_object/arc.rb +3 -3
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +2 -2
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +4 -8
- data/lib/hexapdf/content/graphics_state.rb +4 -13
- data/lib/hexapdf/content/operator.rb +33 -35
- data/lib/hexapdf/content/parser.rb +28 -18
- data/lib/hexapdf/content/processor.rb +4 -5
- data/lib/hexapdf/content/transformation_matrix.rb +2 -2
- data/lib/hexapdf/data_dir.rb +2 -2
- data/lib/hexapdf/dictionary.rb +8 -9
- data/lib/hexapdf/dictionary_fields.rb +7 -10
- data/lib/hexapdf/document.rb +18 -18
- data/lib/hexapdf/document/files.rb +12 -10
- data/lib/hexapdf/document/fonts.rb +2 -2
- data/lib/hexapdf/document/images.rb +3 -3
- data/lib/hexapdf/document/pages.rb +4 -4
- data/lib/hexapdf/encryption.rb +2 -2
- data/lib/hexapdf/encryption/aes.rb +2 -2
- data/lib/hexapdf/encryption/arc4.rb +4 -4
- data/lib/hexapdf/encryption/fast_aes.rb +2 -2
- data/lib/hexapdf/encryption/fast_arc4.rb +4 -4
- data/lib/hexapdf/encryption/identity.rb +5 -4
- data/lib/hexapdf/encryption/ruby_aes.rb +147 -139
- data/lib/hexapdf/encryption/ruby_arc4.rb +4 -4
- data/lib/hexapdf/encryption/security_handler.rb +11 -12
- data/lib/hexapdf/encryption/standard_security_handler.rb +6 -9
- data/lib/hexapdf/error.rb +7 -9
- data/lib/hexapdf/filter.rb +2 -3
- data/lib/hexapdf/filter/ascii85_decode.rb +3 -3
- data/lib/hexapdf/filter/ascii_hex_decode.rb +2 -2
- data/lib/hexapdf/filter/dct_decode.rb +2 -2
- data/lib/hexapdf/filter/encryption.rb +2 -2
- data/lib/hexapdf/filter/flate_decode.rb +16 -33
- data/lib/hexapdf/filter/jpx_decode.rb +2 -2
- data/lib/hexapdf/filter/lzw_decode.rb +4 -4
- data/lib/hexapdf/filter/predictor.rb +2 -6
- data/lib/hexapdf/filter/run_length_decode.rb +2 -2
- data/lib/hexapdf/font/cmap.rb +2 -3
- data/lib/hexapdf/font/cmap/parser.rb +11 -11
- data/lib/hexapdf/font/cmap/writer.rb +25 -25
- data/lib/hexapdf/font/encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/base.rb +2 -2
- data/lib/hexapdf/font/encoding/difference_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/glyph_list.rb +6 -6
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/standard_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +2 -2
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +2 -2
- data/lib/hexapdf/font/invalid_glyph.rb +7 -7
- data/lib/hexapdf/font/true_type.rb +2 -2
- data/lib/hexapdf/font/true_type/builder.rb +13 -7
- data/lib/hexapdf/font/true_type/font.rb +2 -3
- data/lib/hexapdf/font/true_type/optimizer.rb +2 -2
- data/lib/hexapdf/font/true_type/subsetter.rb +4 -4
- data/lib/hexapdf/font/true_type/table.rb +3 -5
- data/lib/hexapdf/font/true_type/table/cmap.rb +2 -2
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +9 -16
- data/lib/hexapdf/font/true_type/table/directory.rb +4 -4
- data/lib/hexapdf/font/true_type/table/glyf.rb +2 -2
- data/lib/hexapdf/font/true_type/table/head.rb +3 -2
- data/lib/hexapdf/font/true_type/table/hhea.rb +2 -2
- data/lib/hexapdf/font/true_type/table/hmtx.rb +2 -2
- data/lib/hexapdf/font/true_type/table/kern.rb +3 -3
- data/lib/hexapdf/font/true_type/table/loca.rb +3 -3
- data/lib/hexapdf/font/true_type/table/maxp.rb +2 -2
- data/lib/hexapdf/font/true_type/table/name.rb +3 -5
- data/lib/hexapdf/font/true_type/table/os2.rb +4 -3
- data/lib/hexapdf/font/true_type/table/post.rb +3 -7
- data/lib/hexapdf/font/true_type_wrapper.rb +20 -22
- data/lib/hexapdf/font/type1.rb +2 -2
- data/lib/hexapdf/font/type1/afm_parser.rb +7 -7
- data/lib/hexapdf/font/type1/character_metrics.rb +2 -2
- data/lib/hexapdf/font/type1/font.rb +3 -3
- data/lib/hexapdf/font/type1/font_metrics.rb +2 -4
- data/lib/hexapdf/font/type1/pfb_parser.rb +2 -2
- data/lib/hexapdf/font/type1_wrapper.rb +8 -9
- data/lib/hexapdf/font_loader.rb +2 -2
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
- data/lib/hexapdf/font_loader/from_file.rb +2 -2
- data/lib/hexapdf/font_loader/standard14.rb +2 -2
- data/lib/hexapdf/image_loader.rb +2 -2
- data/lib/hexapdf/image_loader/jpeg.rb +6 -4
- data/lib/hexapdf/image_loader/pdf.rb +2 -2
- data/lib/hexapdf/image_loader/png.rb +6 -6
- data/lib/hexapdf/importer.rb +6 -4
- data/lib/hexapdf/layout.rb +2 -2
- data/lib/hexapdf/layout/box.rb +4 -4
- data/lib/hexapdf/layout/inline_box.rb +2 -2
- data/lib/hexapdf/layout/line.rb +6 -6
- data/lib/hexapdf/layout/numeric_refinements.rb +2 -2
- data/lib/hexapdf/layout/style.rb +17 -8
- data/lib/hexapdf/layout/text_fragment.rb +86 -48
- data/lib/hexapdf/layout/text_layouter.rb +40 -21
- data/lib/hexapdf/layout/text_shaper.rb +2 -2
- data/lib/hexapdf/name_tree_node.rb +2 -2
- data/lib/hexapdf/number_tree_node.rb +2 -2
- data/lib/hexapdf/object.rb +6 -8
- data/lib/hexapdf/parser.rb +10 -10
- data/lib/hexapdf/rectangle.rb +4 -4
- data/lib/hexapdf/reference.rb +2 -2
- data/lib/hexapdf/revision.rb +4 -4
- data/lib/hexapdf/revisions.rb +5 -5
- data/lib/hexapdf/serializer.rb +27 -24
- data/lib/hexapdf/stream.rb +4 -4
- data/lib/hexapdf/task.rb +2 -2
- data/lib/hexapdf/task/dereference.rb +4 -4
- data/lib/hexapdf/task/optimize.rb +5 -4
- data/lib/hexapdf/tokenizer.rb +12 -14
- data/lib/hexapdf/type.rb +2 -2
- data/lib/hexapdf/type/action.rb +3 -3
- data/lib/hexapdf/type/actions.rb +2 -2
- data/lib/hexapdf/type/actions/go_to.rb +2 -2
- data/lib/hexapdf/type/actions/go_to_r.rb +2 -2
- data/lib/hexapdf/type/actions/launch.rb +2 -2
- data/lib/hexapdf/type/actions/uri.rb +3 -3
- data/lib/hexapdf/type/annotation.rb +3 -3
- data/lib/hexapdf/type/annotations.rb +2 -2
- data/lib/hexapdf/type/annotations/link.rb +2 -2
- data/lib/hexapdf/type/annotations/markup_annotation.rb +2 -2
- data/lib/hexapdf/type/annotations/text.rb +2 -2
- data/lib/hexapdf/type/catalog.rb +4 -4
- data/lib/hexapdf/type/cid_font.rb +4 -5
- data/lib/hexapdf/type/embedded_file.rb +3 -3
- data/lib/hexapdf/type/file_specification.rb +6 -7
- data/lib/hexapdf/type/font.rb +4 -4
- data/lib/hexapdf/type/font_descriptor.rb +3 -4
- data/lib/hexapdf/type/font_simple.rb +12 -16
- data/lib/hexapdf/type/font_true_type.rb +2 -2
- data/lib/hexapdf/type/font_type0.rb +5 -5
- data/lib/hexapdf/type/font_type1.rb +4 -4
- data/lib/hexapdf/type/form.rb +3 -3
- data/lib/hexapdf/type/graphics_state_parameter.rb +3 -3
- data/lib/hexapdf/type/image.rb +13 -14
- data/lib/hexapdf/type/info.rb +2 -2
- data/lib/hexapdf/type/names.rb +2 -2
- data/lib/hexapdf/type/object_stream.rb +5 -5
- data/lib/hexapdf/type/page.rb +9 -10
- data/lib/hexapdf/type/page_tree_node.rb +5 -6
- data/lib/hexapdf/type/resources.rb +11 -11
- data/lib/hexapdf/type/trailer.rb +3 -3
- data/lib/hexapdf/type/viewer_preferences.rb +2 -2
- data/lib/hexapdf/type/xref_stream.rb +8 -4
- data/lib/hexapdf/utils/bit_field.rb +4 -4
- data/lib/hexapdf/utils/bit_stream.rb +4 -4
- data/lib/hexapdf/utils/graphics_helpers.rb +2 -2
- data/lib/hexapdf/utils/lru_cache.rb +2 -2
- data/lib/hexapdf/utils/math_helpers.rb +2 -2
- data/lib/hexapdf/utils/object_hash.rb +3 -3
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +3 -3
- data/lib/hexapdf/utils/sorted_tree_node.rb +12 -11
- data/lib/hexapdf/version.rb +3 -3
- data/lib/hexapdf/writer.rb +8 -8
- data/lib/hexapdf/xref_section.rb +3 -3
- data/test/hexapdf/common_tokenizer_tests.rb +6 -7
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +0 -1
- data/test/hexapdf/content/test_canvas.rb +28 -36
- data/test/hexapdf/content/test_color_space.rb +4 -0
- data/test/hexapdf/content/test_graphics_state.rb +2 -0
- data/test/hexapdf/content/test_operator.rb +6 -7
- data/test/hexapdf/content/test_parser.rb +2 -2
- data/test/hexapdf/content/test_processor.rb +1 -1
- data/test/hexapdf/document/test_files.rb +1 -0
- data/test/hexapdf/document/test_images.rb +1 -1
- data/test/hexapdf/encryption/common.rb +3 -3
- data/test/hexapdf/encryption/test_aes.rb +10 -4
- data/test/hexapdf/encryption/test_identity.rb +1 -1
- data/test/hexapdf/encryption/test_security_handler.rb +13 -17
- data/test/hexapdf/encryption/test_standard_security_handler.rb +12 -12
- data/test/hexapdf/filter/test_ascii85_decode.rb +2 -3
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +6 -1
- data/test/hexapdf/filter/test_flate_decode.rb +2 -2
- data/test/hexapdf/filter/test_lzw_decode.rb +10 -10
- data/test/hexapdf/filter/test_predictor.rb +10 -2
- data/test/hexapdf/filter/test_run_length_decode.rb +1 -1
- data/test/hexapdf/font/cmap/test_parser.rb +40 -40
- data/test/hexapdf/font/cmap/test_writer.rb +29 -29
- data/test/hexapdf/font/test_true_type_wrapper.rb +3 -2
- data/test/hexapdf/font/true_type/common.rb +2 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +5 -4
- data/test/hexapdf/font/true_type/table/test_glyf.rb +2 -2
- data/test/hexapdf/font/true_type/table/test_head.rb +2 -2
- data/test/hexapdf/font/true_type/table/test_name.rb +9 -6
- data/test/hexapdf/font/true_type/test_builder.rb +8 -3
- data/test/hexapdf/font/true_type/test_optimizer.rb +1 -2
- data/test/hexapdf/font/type1/test_afm_parser.rb +2 -2
- data/test/hexapdf/image_loader/test_jpeg.rb +1 -1
- data/test/hexapdf/image_loader/test_pdf.rb +1 -1
- data/test/hexapdf/image_loader/test_png.rb +3 -3
- data/test/hexapdf/layout/test_inline_box.rb +10 -1
- data/test/hexapdf/layout/test_line.rb +4 -4
- data/test/hexapdf/layout/test_style.rb +19 -7
- data/test/hexapdf/layout/test_text_fragment.rb +73 -27
- data/test/hexapdf/layout/test_text_layouter.rb +84 -68
- data/test/hexapdf/layout/test_text_shaper.rb +4 -6
- data/test/hexapdf/task/test_dereference.rb +2 -2
- data/test/hexapdf/task/test_optimize.rb +16 -7
- data/test/hexapdf/test_configuration.rb +1 -1
- data/test/hexapdf/test_data_dir.rb +2 -2
- data/test/hexapdf/test_dictionary.rb +6 -3
- data/test/hexapdf/test_dictionary_fields.rb +15 -14
- data/test/hexapdf/test_document.rb +47 -48
- data/test/hexapdf/test_filter.rb +30 -26
- data/test/hexapdf/test_importer.rb +14 -0
- data/test/hexapdf/test_object.rb +16 -4
- data/test/hexapdf/test_parser.rb +36 -36
- data/test/hexapdf/test_reference.rb +7 -5
- data/test/hexapdf/test_revision.rb +1 -1
- data/test/hexapdf/test_revisions.rb +90 -90
- data/test/hexapdf/test_serializer.rb +3 -2
- data/test/hexapdf/test_stream.rb +2 -4
- data/test/hexapdf/test_tokenizer.rb +2 -2
- data/test/hexapdf/test_writer.rb +80 -80
- data/test/hexapdf/test_xref_section.rb +1 -1
- data/test/hexapdf/type/annotations/test_link.rb +1 -1
- data/test/hexapdf/type/annotations/test_text.rb +3 -3
- data/test/hexapdf/type/test_font_descriptor.rb +1 -1
- data/test/hexapdf/type/test_font_simple.rb +2 -2
- data/test/hexapdf/type/test_font_type0.rb +0 -1
- data/test/hexapdf/type/test_image.rb +0 -3
- data/test/hexapdf/type/test_object_stream.rb +2 -1
- data/test/hexapdf/type/test_page.rb +5 -1
- data/test/hexapdf/type/test_page_tree_node.rb +4 -4
- data/test/hexapdf/type/test_trailer.rb +1 -1
- data/test/hexapdf/type/test_xref_stream.rb +4 -0
- data/test/hexapdf/utils/test_bit_field.rb +2 -0
- data/test/hexapdf/utils/test_bit_stream.rb +1 -1
- data/test/hexapdf/utils/test_lru_cache.rb +1 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +10 -4
- data/test/test_helper.rb +3 -6
- metadata +3 -3
@@ -16,15 +16,23 @@ describe HexaPDF::Layout::TextFragment do
|
|
16
16
|
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20,
|
17
17
|
horizontal_scaling: 200, character_spacing: 1,
|
18
18
|
word_spacing: 2, text_rise: text_rise)
|
19
|
-
@fragment = HexaPDF::Layout::TextFragment.new(items
|
19
|
+
@fragment = HexaPDF::Layout::TextFragment.new(items, style)
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
describe "create" do
|
23
|
+
it "creates a TextFragment from text and options" do
|
24
|
+
frag = HexaPDF::Layout::TextFragment.create("Tom", font: @font, font_size: 20,
|
25
|
+
font_features: {kern: true})
|
26
|
+
assert_equal(4, frag.items.length)
|
27
|
+
assert_equal(36.18, frag.width)
|
28
|
+
assert_equal(13.66 + 4.34, frag.height)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "creates a TextFragment from text and a Style object" do
|
32
|
+
style = HexaPDF::Layout::Style.new(font: @font)
|
33
|
+
frag = HexaPDF::Layout::TextFragment.create("Tom", style)
|
34
|
+
assert_equal(style, frag.style)
|
35
|
+
end
|
28
36
|
end
|
29
37
|
|
30
38
|
describe "initialize" do
|
@@ -34,12 +42,12 @@ describe HexaPDF::Layout::TextFragment do
|
|
34
42
|
|
35
43
|
it "can use a Style object" do
|
36
44
|
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20)
|
37
|
-
frag = HexaPDF::Layout::TextFragment.new(
|
45
|
+
frag = HexaPDF::Layout::TextFragment.new(@items, style)
|
38
46
|
assert_equal(20, frag.style.font_size)
|
39
47
|
end
|
40
48
|
|
41
|
-
it "can use
|
42
|
-
frag = HexaPDF::Layout::TextFragment.new(
|
49
|
+
it "can use style options" do
|
50
|
+
frag = HexaPDF::Layout::TextFragment.new(@items, font: @font, font_size: 20)
|
43
51
|
assert_equal(20, frag.style.font_size)
|
44
52
|
end
|
45
53
|
end
|
@@ -51,7 +59,7 @@ describe HexaPDF::Layout::TextFragment do
|
|
51
59
|
describe "draw" do
|
52
60
|
def setup_with_style(**styles)
|
53
61
|
setup_fragment(@font.decode_utf8('H'), 2)
|
54
|
-
styles.each {|name, value| @fragment.style.send(name, value)}
|
62
|
+
styles.each {|name, value| @fragment.style.send(name, value) }
|
55
63
|
@canvas = @doc.pages.add.canvas
|
56
64
|
@fragment.draw(@canvas, 10, 15)
|
57
65
|
end
|
@@ -59,16 +67,15 @@ describe HexaPDF::Layout::TextFragment do
|
|
59
67
|
def assert_draw_operators(*args, front: [], middle: args, back: [])
|
60
68
|
ops = [
|
61
69
|
*front,
|
62
|
-
[:begin_text],
|
63
|
-
[:set_text_matrix, [1, 0, 0, 1, 10, 15]],
|
64
70
|
[:set_font_and_size, [:F1, 20]],
|
65
|
-
[:set_leading, [24.0]],
|
66
71
|
[:set_horizontal_scaling, [200]],
|
67
72
|
[:set_character_spacing, [1]],
|
68
73
|
[:set_word_spacing, [2]],
|
69
74
|
[:set_text_rise, [2]],
|
70
75
|
*middle,
|
71
|
-
[:
|
76
|
+
[:begin_text],
|
77
|
+
[:set_text_matrix, [1, 0, 0, 1, 10, 15]],
|
78
|
+
[:show_text, ['!']],
|
72
79
|
*back,
|
73
80
|
].compact
|
74
81
|
assert_operators(@canvas.contents, ops)
|
@@ -79,6 +86,47 @@ describe HexaPDF::Layout::TextFragment do
|
|
79
86
|
assert_draw_operators
|
80
87
|
end
|
81
88
|
|
89
|
+
it "doesn't set the text properties if instructed to do so" do
|
90
|
+
setup_fragment([])
|
91
|
+
@canvas = @doc.pages.add.canvas
|
92
|
+
@fragment.draw(@canvas, 10, 15, ignore_text_properties: true)
|
93
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
94
|
+
[:set_text_matrix, [1, 0, 0, 1, 10, 15]]])
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "uses an appropriate text position setter" do
|
98
|
+
before do
|
99
|
+
setup_fragment([])
|
100
|
+
@canvas = @doc.pages.add.canvas
|
101
|
+
end
|
102
|
+
|
103
|
+
it "with text leading graphics state" do
|
104
|
+
@canvas.begin_text.leading(10)
|
105
|
+
@fragment.draw(@canvas, 0, -10, ignore_text_properties: true)
|
106
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
107
|
+
[:set_leading, [10]],
|
108
|
+
[:move_text_next_line]])
|
109
|
+
end
|
110
|
+
|
111
|
+
it "only horizontal movement" do
|
112
|
+
@fragment.draw(@canvas, 20, 0, ignore_text_properties: true)
|
113
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
114
|
+
[:move_text, [20, 0]]])
|
115
|
+
end
|
116
|
+
|
117
|
+
it "only vertical movement" do
|
118
|
+
@fragment.draw(@canvas, 0, 10, ignore_text_properties: true)
|
119
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
120
|
+
[:move_text, [0, 10]]])
|
121
|
+
end
|
122
|
+
|
123
|
+
it "horizontal and vertical movement" do
|
124
|
+
@fragment.draw(@canvas, 10, 10, ignore_text_properties: true)
|
125
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
126
|
+
[:set_text_matrix, [1, 0, 0, 1, 10, 10]]])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
82
130
|
it "draws styled filled text" do
|
83
131
|
setup_with_style(fill_color: 0.5, fill_alpha: 0.5)
|
84
132
|
assert_draw_operators([:set_graphics_state_parameters, [:GS1]],
|
@@ -132,14 +180,16 @@ describe HexaPDF::Layout::TextFragment do
|
|
132
180
|
[:set_line_width, [5]],
|
133
181
|
[:set_line_cap_style, [1]],
|
134
182
|
[:set_line_dash_pattern, [[5], 0]]],
|
135
|
-
back: [[:
|
183
|
+
back: [[:end_text],
|
184
|
+
[:save_graphics_state],
|
185
|
+
[:set_device_gray_stroking_color, [0]],
|
136
186
|
[:set_line_width, [@fragment.style.calculated_underline_thickness]],
|
137
187
|
[:set_line_cap_style, [0]],
|
138
188
|
[:set_line_dash_pattern, [[], 0]],
|
139
|
-
[:end_text],
|
140
189
|
[:move_to, [10, 15]],
|
141
190
|
[:line_to, [40.88, 15]],
|
142
|
-
[:stroke_path]
|
191
|
+
[:stroke_path],
|
192
|
+
[:restore_graphics_state]])
|
143
193
|
end
|
144
194
|
|
145
195
|
it "draws the strikeout line" do
|
@@ -151,14 +201,16 @@ describe HexaPDF::Layout::TextFragment do
|
|
151
201
|
[:set_line_width, [5]],
|
152
202
|
[:set_line_cap_style, [1]],
|
153
203
|
[:set_line_dash_pattern, [[5], 0]]],
|
154
|
-
back: [[:
|
204
|
+
back: [[:end_text],
|
205
|
+
[:save_graphics_state],
|
206
|
+
[:set_device_gray_stroking_color, [0]],
|
155
207
|
[:set_line_width, [@fragment.style.calculated_strikeout_thickness]],
|
156
208
|
[:set_line_cap_style, [0]],
|
157
209
|
[:set_line_dash_pattern, [[], 0]],
|
158
|
-
[:end_text],
|
159
210
|
[:move_to, [10, 21.01]],
|
160
211
|
[:line_to, [40.88, 21.01]],
|
161
|
-
[:stroke_path]
|
212
|
+
[:stroke_path],
|
213
|
+
[:restore_graphics_state]])
|
162
214
|
end
|
163
215
|
end
|
164
216
|
|
@@ -190,12 +242,6 @@ describe HexaPDF::Layout::TextFragment do
|
|
190
242
|
it "calculates the height" do
|
191
243
|
assert_equal(13.66 + 4.34, @fragment.height)
|
192
244
|
end
|
193
|
-
|
194
|
-
it "draws nothing" do
|
195
|
-
canvas = @doc.pages.add.canvas
|
196
|
-
@fragment.draw(canvas, 10, 15)
|
197
|
-
assert_operators(canvas.contents, [])
|
198
|
-
end
|
199
245
|
end
|
200
246
|
|
201
247
|
describe "normal text" do
|
@@ -48,6 +48,13 @@ module TestTextLayouterHelpers
|
|
48
48
|
assert_equal(item.items, obj.item.items)
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
def assert_line_wrapping(result, widths)
|
53
|
+
rest, lines = *result
|
54
|
+
assert(rest.empty?)
|
55
|
+
assert_equal(widths.length, lines.count)
|
56
|
+
widths.each_with_index {|width, index| assert_equal(width, lines[index].width) }
|
57
|
+
end
|
51
58
|
end
|
52
59
|
|
53
60
|
describe HexaPDF::Layout::TextLayouter::SimpleTextSegmentation do
|
@@ -61,14 +68,14 @@ describe HexaPDF::Layout::TextLayouter::SimpleTextSegmentation do
|
|
61
68
|
|
62
69
|
def setup_fragment(text, style = nil)
|
63
70
|
if style
|
64
|
-
HexaPDF::Layout::TextFragment.
|
71
|
+
HexaPDF::Layout::TextFragment.create(text, style)
|
65
72
|
else
|
66
73
|
HexaPDF::Layout::TextFragment.create(text, font: @font)
|
67
74
|
end
|
68
75
|
end
|
69
76
|
|
70
77
|
it "handles InlineBox objects" do
|
71
|
-
input = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {
|
78
|
+
input = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {}
|
72
79
|
result = @obj.call([input, input])
|
73
80
|
assert_equal(2, result.size)
|
74
81
|
assert_box(result[0], input)
|
@@ -103,11 +110,12 @@ describe HexaPDF::Layout::TextLayouter::SimpleTextSegmentation do
|
|
103
110
|
end
|
104
111
|
|
105
112
|
it "insert a mandatory break when an Unicode line boundary characters is encountered" do
|
106
|
-
frag = setup_fragment("A\rB\r\nC\nD\vE\fF\u{85}G\u{2029}H\u{2028}I")
|
113
|
+
frag = setup_fragment("A\rB\r\nC\nD\vE\fF\u{85}G\u{2029}H\u{2028}I\r")
|
114
|
+
frag.items << 5 << frag.items[-2]
|
107
115
|
|
108
116
|
result = @obj.call([frag])
|
109
|
-
assert_equal(
|
110
|
-
[1, 3, 5, 7, 9, 11, 13].each do |index|
|
117
|
+
assert_equal(20, result.size)
|
118
|
+
[1, 3, 5, 7, 9, 11, 13, 17, 19].each do |index|
|
111
119
|
assert_penalty(result[index],
|
112
120
|
HexaPDF::Layout::TextLayouter::Penalty::MandatoryParagraphBreak.penalty)
|
113
121
|
end
|
@@ -163,98 +171,83 @@ module CommonLineWrappingTests
|
|
163
171
|
|
164
172
|
it "breaks before a box if it doesn't fit onto the line anymore" do
|
165
173
|
rest, lines = call(boxes(25, 50, 25, 10))
|
166
|
-
|
167
|
-
|
168
|
-
lines.each {|line| line.items.each {|item| assert_kind_of(HexaPDF::Layout::InlineBox, item)}}
|
169
|
-
assert_equal(100, lines[0].width)
|
170
|
-
assert_equal(10, lines[1].width)
|
174
|
+
assert_line_wrapping([rest, lines], [100, 10])
|
175
|
+
lines.each {|line| line.items.each {|item| assert_kind_of(HexaPDF::Layout::InlineBox, item) } }
|
171
176
|
end
|
172
177
|
|
173
178
|
it "breaks at a glue and ignores it if it doesn't fit onto the line anymore" do
|
174
|
-
|
175
|
-
|
176
|
-
assert_equal(2, lines.count)
|
177
|
-
assert_equal(90, lines[0].width)
|
178
|
-
assert_equal(20, lines[1].width)
|
179
|
+
result = call(boxes(90) + [glue(20)] + boxes(20))
|
180
|
+
assert_line_wrapping(result, [90, 20])
|
179
181
|
end
|
180
182
|
|
181
183
|
it "handles spaces at the start of a line" do
|
182
|
-
rest, lines = call(boxes(25, 50)
|
183
|
-
|
184
|
-
assert_equal(1, lines.count)
|
185
|
-
assert_equal(75, lines[0].width)
|
184
|
+
rest, lines = call([glue(15)] + boxes(25, 50))
|
185
|
+
assert_line_wrapping([rest, lines], [75])
|
186
186
|
assert_equal(25, lines[0].items[0].width)
|
187
187
|
end
|
188
188
|
|
189
189
|
it "handles spaces at the end of a line" do
|
190
|
-
rest, lines = call(boxes(20, 50
|
191
|
-
|
192
|
-
assert_equal(2, lines.count)
|
193
|
-
assert_equal(70, lines[0].width)
|
194
|
-
assert_equal(20, lines[1].width)
|
190
|
+
rest, lines = call(boxes(20, 50) + [glue(10), glue(10)] + boxes(20))
|
191
|
+
assert_line_wrapping([rest, lines], [70, 20])
|
195
192
|
assert_equal(50, lines[0].items[-1].width)
|
196
193
|
end
|
197
194
|
|
198
195
|
it "handles spaces at the end of a line before a mandatory break" do
|
199
|
-
rest, lines = call(boxes(20, 50
|
200
|
-
|
201
|
-
assert_equal(2, lines.count)
|
202
|
-
assert_equal(70, lines[0].width)
|
203
|
-
assert_equal(20, lines[1].width)
|
196
|
+
rest, lines = call(boxes(20, 50) + [glue(10), penalty(-5000)] + boxes(20))
|
197
|
+
assert_line_wrapping([rest, lines], [70, 20])
|
204
198
|
assert_equal(50, lines[0].items[-1].width)
|
205
199
|
end
|
206
200
|
|
207
201
|
it "handles multiple glue items after another" do
|
208
|
-
|
209
|
-
|
210
|
-
assert_equal(2, lines.count)
|
211
|
-
assert_equal(80, lines[0].width)
|
212
|
-
assert_equal(70, lines[1].width)
|
202
|
+
result = call(boxes(20) + [glue(20), glue(20)] + boxes(20, 50, 20))
|
203
|
+
assert_line_wrapping(result, [80, 70])
|
213
204
|
end
|
214
205
|
|
215
206
|
it "handles mandatory line breaks" do
|
216
|
-
rest, lines = call(boxes(20
|
217
|
-
|
218
|
-
assert_equal(2, lines.count)
|
219
|
-
assert_equal(20, lines[0].width)
|
220
|
-
assert_equal(20, lines[1].width)
|
207
|
+
rest, lines = call(boxes(20) + [penalty(-5000)] + boxes(20))
|
208
|
+
assert_line_wrapping([rest, lines], [20, 20])
|
221
209
|
assert(lines[0].ignore_justification?)
|
222
210
|
end
|
223
211
|
|
224
212
|
it "handles breaking at penalties with zero width" do
|
225
|
-
|
226
|
-
|
227
|
-
assert_equal(2, lines.count)
|
228
|
-
assert_equal(90, lines[0].width)
|
229
|
-
assert_equal(20, lines[1].width)
|
213
|
+
result = call(boxes(80) + [penalty(0)] + boxes(10) + [penalty(0)] + boxes(20))
|
214
|
+
assert_line_wrapping(result, [90, 20])
|
230
215
|
end
|
231
216
|
|
232
217
|
it "handles breaking at penalties with non-zero width if they fit on the line" do
|
233
|
-
|
234
|
-
rest, lines = call(boxes(20
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
218
|
+
pitem = penalty(0, boxes(20).first)
|
219
|
+
rest, lines = call(boxes(20) + [pitem] + boxes(50) + [glue(10), pitem] + boxes(30))
|
220
|
+
assert_line_wrapping([rest, lines], [100, 30])
|
221
|
+
assert_same(pitem.item, lines[0].items[-1])
|
222
|
+
end
|
223
|
+
|
224
|
+
it "handles breaking at penalties with non-zero width that fit on the line and are followed by 1+ penalties" do
|
225
|
+
pitem = penalty(0, boxes(20).first)
|
226
|
+
result = call(boxes(80) + [pitem, penalty(0), penalty(0)] + boxes(30))
|
227
|
+
assert_line_wrapping(result, [100, 30])
|
240
228
|
end
|
241
229
|
|
242
230
|
it "handles penalties with non-zero width if they don't fit on the line" do
|
243
|
-
item =
|
244
|
-
|
245
|
-
|
246
|
-
assert_equal(2, lines.count)
|
247
|
-
assert_equal(70, lines[0].width)
|
248
|
-
assert_equal(40, lines[1].width)
|
231
|
+
item = boxes(20).first
|
232
|
+
result = call(boxes(70) + [glue(10)] + boxes(10) + [penalty(0, item)] + boxes(30))
|
233
|
+
assert_line_wrapping(result, [70, 40])
|
249
234
|
end
|
250
235
|
|
251
|
-
it "handles breaking at
|
252
|
-
item =
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
236
|
+
it "handles breaking at penalties with non-zero width surrounded by glue" do
|
237
|
+
item = boxes(20).first
|
238
|
+
result = call(boxes(70) + [glue(10)] + [penalty(0, item)] + [glue(30)] + boxes(30))
|
239
|
+
assert_line_wrapping(result, [100, 30])
|
240
|
+
end
|
241
|
+
|
242
|
+
it "handles prohibited breakpoint penalties with zero width" do
|
243
|
+
result = call(boxes(70) + [glue(10)] + boxes(10) + [penalty(5000)] + boxes(30))
|
244
|
+
assert_line_wrapping(result, [70, 40])
|
245
|
+
end
|
246
|
+
|
247
|
+
it "handles prohibited breakpoint penalties with non-zero width" do
|
248
|
+
item = boxes(20).first
|
249
|
+
result = call(boxes(70) + [glue(10)] + boxes(10) + [penalty(5000, item)] + boxes(30))
|
250
|
+
assert_line_wrapping(result, [70, 60])
|
258
251
|
end
|
259
252
|
|
260
253
|
it "stops when nil is returned by the block: last item is a box" do
|
@@ -527,7 +520,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
527
520
|
rest, reason = layouter.fit
|
528
521
|
assert(rest.empty?)
|
529
522
|
assert_equal(:success, reason)
|
530
|
-
assert_equal(str.strip.length, layouter.lines.sum {|l| l.items.sum {|i| i.items.count}})
|
523
|
+
assert_equal(str.strip.length, layouter.lines.sum {|l| l.items.sum {|i| i.items.count } })
|
531
524
|
assert_equal(45, layouter.actual_height)
|
532
525
|
|
533
526
|
layouter = HexaPDF::Layout::TextLayouter.new(items: [frag], width: 1, height: height,
|
@@ -564,7 +557,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
564
557
|
end
|
565
558
|
|
566
559
|
it "applies the optional horizontal offsets if set" do
|
567
|
-
x_offsets = lambda {|height, line_height| height + line_height}
|
560
|
+
x_offsets = lambda {|height, line_height| height + line_height }
|
568
561
|
layouter = HexaPDF::Layout::TextLayouter.new(items: boxes(*([[20, 10]] * 7)), width: 60,
|
569
562
|
x_offsets: x_offsets, height: 100, style: @style)
|
570
563
|
rest, reason = layouter.fit
|
@@ -582,7 +575,17 @@ describe HexaPDF::Layout::TextLayouter do
|
|
582
575
|
processor = TestHelper::OperatorRecorder.new
|
583
576
|
HexaPDF::Content::Parser.new.parse(content, processor)
|
584
577
|
result = processor.recorded_ops
|
585
|
-
result.select
|
578
|
+
leading = (result.select {|name, _| name == :set_leading } || [0]).map(&:last).flatten.first
|
579
|
+
pos = [0, 0]
|
580
|
+
result.select! {|name, _| name == :set_text_matrix || name == :move_text_next_line }.
|
581
|
+
map! do |name, ops|
|
582
|
+
if name == :set_text_matrix
|
583
|
+
pos = ops[-2, 2]
|
584
|
+
elsif name == :move_text_next_line
|
585
|
+
pos[1] -= leading
|
586
|
+
end
|
587
|
+
pos.dup
|
588
|
+
end
|
586
589
|
positions.each_with_index do |(x, y), index|
|
587
590
|
assert_in_delta(x, result[index][0], 0.00001)
|
588
591
|
assert_in_delta(y, result[index][1], 0.00001)
|
@@ -600,6 +603,19 @@ describe HexaPDF::Layout::TextLayouter do
|
|
600
603
|
@line2w = HexaPDF::Layout::TextFragment.create("more text.", font: @font).width
|
601
604
|
end
|
602
605
|
|
606
|
+
it "returns the result of #fit if #fit needs to be run" do
|
607
|
+
rest, reason = @layouter.draw(@canvas, 0, 0)
|
608
|
+
assert(rest.empty?)
|
609
|
+
assert_equal(:success, reason)
|
610
|
+
|
611
|
+
@layouter.items = [HexaPDF::Layout::InlineBox.create(width: @width + 10) {}]
|
612
|
+
rest, reason = @layouter.draw(@canvas, 0, 0)
|
613
|
+
assert_equal(1, rest.size)
|
614
|
+
assert_equal(:box, reason)
|
615
|
+
|
616
|
+
assert_nil(@layouter.draw(@canvas, 0, 0))
|
617
|
+
end
|
618
|
+
|
603
619
|
it "can horizontally align the contents to the left" do
|
604
620
|
top = 100
|
605
621
|
@layouter.style.align = :left
|
@@ -697,7 +713,7 @@ describe HexaPDF::Layout::TextLayouter do
|
|
697
713
|
|
698
714
|
it "makes sure that text fragments don't pollute the graphics state for inline boxes" do
|
699
715
|
frag = HexaPDF::Layout::TextFragment.create("Demo", font: @font)
|
700
|
-
inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {|c, _| c.text("A")}
|
716
|
+
inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {|c, _| c.text("A") }
|
701
717
|
layouter = HexaPDF::Layout::TextLayouter.new(items: [frag, inline_box], width: 200)
|
702
718
|
assert_raises(HexaPDF::Error) { layouter.draw(@canvas, 0, 0) }
|
703
719
|
end
|
@@ -16,7 +16,7 @@ describe HexaPDF::Layout::TextShaper do
|
|
16
16
|
|
17
17
|
def setup_fragment(items, **options)
|
18
18
|
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20, font_features: options)
|
19
|
-
HexaPDF::Layout::TextFragment.new(items
|
19
|
+
HexaPDF::Layout::TextFragment.new(items, style)
|
20
20
|
end
|
21
21
|
|
22
22
|
describe "Type1 font features" do
|
@@ -29,14 +29,14 @@ describe HexaPDF::Layout::TextShaper do
|
|
29
29
|
insert(0, 100), liga: true)
|
30
30
|
@shaper.shape_text(fragment)
|
31
31
|
assert_equal([100, :fi, :s, :h, :space, :fi, :s, :h, :space, :fi],
|
32
|
-
fragment.items.map {|item| item.kind_of?(Numeric) ? item : item.id})
|
32
|
+
fragment.items.map {|item| item.kind_of?(Numeric) ? item : item.id })
|
33
33
|
end
|
34
34
|
|
35
35
|
it "handles kerning" do
|
36
36
|
fragment = setup_fragment(@font.decode_utf8('fish fish wow').insert(1, 100), kern: true)
|
37
37
|
@shaper.shape_text(fragment)
|
38
38
|
assert_equal([:f, 100, :i, :s, :h, :space, :f, 20, :i, :s, :h, :space, :w, 10, :o, 25, :w],
|
39
|
-
fragment.items.map {|item| item.kind_of?(Numeric) ? item : item.id})
|
39
|
+
fragment.items.map {|item| item.kind_of?(Numeric) ? item : item.id })
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -56,9 +56,7 @@ describe HexaPDF::Layout::TextShaper do
|
|
56
56
|
fragment = setup_fragment(@font.decode_utf8('Top Top').insert(1, 100), kern: true)
|
57
57
|
@shaper.shape_text(fragment)
|
58
58
|
assert_equal([53, [100], 80, [10], 81, 3, 53, [20], 80, [10], 81],
|
59
|
-
fragment.items.map {|item| item.kind_of?(Numeric) ? [item] : item.id})
|
59
|
+
fragment.items.map {|item| item.kind_of?(Numeric) ? [item] : item.id })
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
64
|
-
|