hexapdf 0.6.0 → 0.7.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 +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
|
-
|