hexapdf 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -1
- data/LICENSE +1 -1
- data/README.md +3 -0
- data/Rakefile +1 -1
- data/data/hexapdf/fonts/Inter-Bold.ttf +0 -0
- data/data/hexapdf/fonts/Inter-BoldItalic.ttf +0 -0
- data/data/hexapdf/fonts/Inter-Italic.ttf +0 -0
- data/data/hexapdf/fonts/Inter-Regular.ttf +0 -0
- data/data/hexapdf/fonts/OFL.txt +92 -0
- data/examples/005-merging.rb +2 -1
- data/examples/019-acro_form.rb +3 -1
- data/examples/030-pdfa.rb +9 -16
- data/examples/034-text_shaping.rb +37 -0
- data/lib/hexapdf/cli/batch.rb +1 -1
- data/lib/hexapdf/cli/command.rb +1 -1
- data/lib/hexapdf/cli/debug_info.rb +1 -1
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/fonts.rb +6 -4
- data/lib/hexapdf/cli/form.rb +1 -1
- data/lib/hexapdf/cli/image2pdf.rb +1 -1
- data/lib/hexapdf/cli/images.rb +17 -17
- data/lib/hexapdf/cli/info.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +1 -1
- data/lib/hexapdf/cli/merge.rb +14 -2
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/cli/optimize.rb +1 -1
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/cli/usage.rb +1 -1
- data/lib/hexapdf/cli/watermark.rb +1 -1
- data/lib/hexapdf/cli.rb +1 -1
- data/lib/hexapdf/composer.rb +1 -1
- data/lib/hexapdf/configuration.rb +15 -2
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/canvas_composer.rb +1 -1
- data/lib/hexapdf/content/color_space.rb +1 -1
- data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object.rb +1 -1
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/content/parser.rb +1 -1
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/content/smart_text_extractor.rb +10 -4
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +1 -1
- data/lib/hexapdf/data_dir.rb +1 -1
- data/lib/hexapdf/dictionary.rb +1 -1
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/digital_signature/cms_handler.rb +1 -1
- data/lib/hexapdf/digital_signature/handler.rb +1 -1
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +1 -1
- data/lib/hexapdf/digital_signature/signature.rb +1 -1
- data/lib/hexapdf/digital_signature/signatures.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +1 -1
- data/lib/hexapdf/digital_signature/signing.rb +1 -1
- data/lib/hexapdf/digital_signature/verification_result.rb +1 -1
- data/lib/hexapdf/digital_signature.rb +1 -1
- data/lib/hexapdf/document/annotations.rb +26 -1
- data/lib/hexapdf/document/destinations.rb +1 -1
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document/layout.rb +1 -1
- data/lib/hexapdf/document/metadata.rb +1 -1
- data/lib/hexapdf/document/pages.rb +1 -1
- data/lib/hexapdf/document.rb +1 -1
- data/lib/hexapdf/encryption/aes.rb +1 -1
- data/lib/hexapdf/encryption/arc4.rb +1 -1
- data/lib/hexapdf/encryption/fast_aes.rb +1 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/encryption.rb +1 -1
- data/lib/hexapdf/error.rb +1 -1
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/brotli_decode.rb +1 -1
- data/lib/hexapdf/filter/crypt.rb +1 -1
- data/lib/hexapdf/filter/encryption.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +1 -1
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +16 -10
- data/lib/hexapdf/font/cmap.rb +1 -1
- data/lib/hexapdf/font/encoding/base.rb +1 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +1 -1
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/font.rb +1 -1
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +4 -4
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
- data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
- data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
- data/lib/hexapdf/font/true_type/table/head.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
- data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
- data/lib/hexapdf/font/true_type/table/name.rb +1 -1
- data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type.rb +1 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +9 -6
- data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
- data/lib/hexapdf/font/type1.rb +1 -1
- data/lib/hexapdf/font/type1_wrapper.rb +1 -1
- data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
- data/lib/hexapdf/font_loader/from_file.rb +5 -1
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/font_loader/variant_from_name.rb +1 -1
- data/lib/hexapdf/font_loader.rb +48 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +1 -1
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +1 -1
- data/lib/hexapdf/layout/box.rb +1 -1
- data/lib/hexapdf/layout/box_fitter.rb +1 -1
- data/lib/hexapdf/layout/column_box.rb +1 -1
- data/lib/hexapdf/layout/container_box.rb +3 -5
- data/lib/hexapdf/layout/frame.rb +1 -1
- data/lib/hexapdf/layout/image_box.rb +1 -1
- data/lib/hexapdf/layout/inline_box.rb +1 -1
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/list_box.rb +1 -1
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/page_style.rb +1 -1
- data/lib/hexapdf/layout/style.rb +67 -5
- data/lib/hexapdf/layout/table_box.rb +97 -14
- data/lib/hexapdf/layout/text_box.rb +1 -1
- data/lib/hexapdf/layout/text_fragment.rb +14 -8
- data/lib/hexapdf/layout/text_layouter.rb +1 -1
- data/lib/hexapdf/layout/text_shaper.rb +163 -11
- data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
- data/lib/hexapdf/layout.rb +1 -1
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +1 -1
- data/lib/hexapdf/parser.rb +1 -1
- data/lib/hexapdf/pdf_array.rb +1 -1
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +1 -1
- data/lib/hexapdf/revision.rb +1 -1
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/serializer.rb +3 -3
- data/lib/hexapdf/stream.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/import_pages.rb +185 -0
- data/lib/hexapdf/task/merge_acro_form.rb +1 -1
- data/lib/hexapdf/task/optimize.rb +1 -1
- data/lib/hexapdf/task/pdfa.rb +1 -1
- data/lib/hexapdf/task.rb +2 -1
- data/lib/hexapdf/test_utils.rb +1 -1
- data/lib/hexapdf/tokenizer.rb +1 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +1 -1
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +1 -1
- data/lib/hexapdf/type/acro_form/form.rb +1 -1
- data/lib/hexapdf/type/acro_form/java_script_actions.rb +1 -1
- data/lib/hexapdf/type/acro_form/signature_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +1 -1
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +1 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +1 -1
- data/lib/hexapdf/type/actions/launch.rb +1 -1
- data/lib/hexapdf/type/actions/set_ocg_state.rb +1 -1
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/appearance_generator.rb +43 -1
- data/lib/hexapdf/type/annotations/border_effect.rb +1 -1
- data/lib/hexapdf/type/annotations/border_styling.rb +1 -1
- data/lib/hexapdf/type/annotations/circle.rb +1 -1
- data/lib/hexapdf/type/annotations/ink.rb +107 -0
- data/lib/hexapdf/type/annotations/interior_color.rb +1 -1
- data/lib/hexapdf/type/annotations/line.rb +1 -1
- data/lib/hexapdf/type/annotations/line_ending_styling.rb +1 -1
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/annotations/polygon.rb +1 -1
- data/lib/hexapdf/type/annotations/polygon_polyline.rb +1 -1
- data/lib/hexapdf/type/annotations/polyline.rb +1 -1
- data/lib/hexapdf/type/annotations/square.rb +1 -1
- data/lib/hexapdf/type/annotations/square_circle.rb +1 -1
- data/lib/hexapdf/type/annotations/text.rb +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +1 -1
- data/lib/hexapdf/type/annotations.rb +2 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +1 -1
- data/lib/hexapdf/type/cmap.rb +1 -1
- data/lib/hexapdf/type/document_security_store.rb +1 -1
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +1 -1
- data/lib/hexapdf/type/font.rb +4 -4
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +1 -1
- data/lib/hexapdf/type/font_true_type.rb +1 -1
- data/lib/hexapdf/type/font_type0.rb +1 -1
- data/lib/hexapdf/type/font_type1.rb +1 -1
- data/lib/hexapdf/type/font_type3.rb +6 -1
- data/lib/hexapdf/type/form.rb +1 -1
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +1 -1
- data/lib/hexapdf/type/info.rb +1 -1
- data/lib/hexapdf/type/mark_information.rb +1 -1
- data/lib/hexapdf/type/marked_content_reference.rb +1 -1
- data/lib/hexapdf/type/measure.rb +1 -1
- data/lib/hexapdf/type/metadata.rb +1 -1
- data/lib/hexapdf/type/names.rb +1 -1
- data/lib/hexapdf/type/namespace.rb +1 -1
- data/lib/hexapdf/type/object_reference.rb +1 -1
- data/lib/hexapdf/type/object_stream.rb +1 -1
- data/lib/hexapdf/type/optional_content_configuration.rb +1 -1
- data/lib/hexapdf/type/optional_content_group.rb +1 -1
- data/lib/hexapdf/type/optional_content_membership.rb +1 -1
- data/lib/hexapdf/type/optional_content_properties.rb +1 -1
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/output_intent.rb +1 -1
- data/lib/hexapdf/type/page.rb +1 -1
- data/lib/hexapdf/type/page_label.rb +1 -1
- data/lib/hexapdf/type/page_tree_node.rb +1 -1
- data/lib/hexapdf/type/resources.rb +1 -1
- data/lib/hexapdf/type/struct_elem.rb +1 -1
- data/lib/hexapdf/type/struct_tree_root.rb +1 -1
- data/lib/hexapdf/type/trailer.rb +1 -1
- data/lib/hexapdf/type/viewer_preferences.rb +1 -1
- data/lib/hexapdf/type/xref_stream.rb +1 -1
- data/lib/hexapdf/type.rb +1 -1
- data/lib/hexapdf/utils/bit_field.rb +1 -1
- data/lib/hexapdf/utils/bit_stream.rb +1 -1
- data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
- data/lib/hexapdf/utils/lru_cache.rb +1 -1
- data/lib/hexapdf/utils/math_helpers.rb +1 -1
- data/lib/hexapdf/utils/object_hash.rb +1 -1
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +1 -1
- data/lib/hexapdf/utils.rb +1 -1
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +1 -1
- data/lib/hexapdf/xref_section.rb +1 -1
- data/lib/hexapdf.rb +1 -1
- data/test/hexapdf/digital_signature/common.rb +5 -5
- data/test/hexapdf/digital_signature/test_cms_handler.rb +1 -1
- data/test/hexapdf/document/test_annotations.rb +10 -0
- data/test/hexapdf/document/test_layout.rb +6 -3
- data/test/hexapdf/filter/test_brotli_decode.rb +1 -1
- data/test/hexapdf/font/cmap/test_writer.rb +8 -6
- data/test/hexapdf/font/test_true_type_wrapper.rb +6 -2
- data/test/hexapdf/font/true_type/test_subsetter.rb +7 -6
- data/test/hexapdf/font_loader/test_from_file.rb +7 -0
- data/test/hexapdf/layout/test_container_box.rb +3 -1
- data/test/hexapdf/layout/test_style.rb +4 -0
- data/test/hexapdf/layout/test_table_box.rb +117 -1
- data/test/hexapdf/layout/test_text_fragment.rb +18 -8
- data/test/hexapdf/layout/test_text_shaper.rb +55 -5
- data/test/hexapdf/task/test_import_pages.rb +126 -0
- data/test/hexapdf/test_serializer.rb +1 -1
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +63 -0
- data/test/hexapdf/type/annotations/test_ink.rb +31 -0
- data/test/hexapdf/type/test_font_type3.rb +4 -0
- metadata +26 -2
|
@@ -19,8 +19,9 @@ describe HexaPDF::Font::CMap::Writer do
|
|
|
19
19
|
1 begincodespacerange
|
|
20
20
|
<0000> <FFFF>
|
|
21
21
|
endcodespacerange
|
|
22
|
-
|
|
22
|
+
3 beginbfchar
|
|
23
23
|
<0060><0090>
|
|
24
|
+
<00A0><00410042>
|
|
24
25
|
<3A51><d840dc3e>
|
|
25
26
|
endbfchar
|
|
26
27
|
2 beginbfrange
|
|
@@ -79,6 +80,7 @@ describe HexaPDF::Font::CMap::Writer do
|
|
|
79
80
|
0x1379.upto(0x137B) do |i|
|
|
80
81
|
@to_unicode_mapping << [i, 0x90FE + i - 0x1379]
|
|
81
82
|
end
|
|
83
|
+
@to_unicode_mapping << [0x00A0, "AB"]
|
|
82
84
|
@to_unicode_mapping << [0x3A51, 0x2003E]
|
|
83
85
|
end
|
|
84
86
|
|
|
@@ -89,17 +91,17 @@ describe HexaPDF::Font::CMap::Writer do
|
|
|
89
91
|
end
|
|
90
92
|
|
|
91
93
|
it "works if the last item is a range" do
|
|
92
|
-
@to_unicode_mapping
|
|
93
|
-
@to_unicode_cmap_data.sub!(/
|
|
94
|
-
@to_unicode_cmap_data.sub!(/<
|
|
94
|
+
@to_unicode_mapping[-2, 2] = []
|
|
95
|
+
@to_unicode_cmap_data.sub!(/3 beginbfchar/, '1 beginbfchar')
|
|
96
|
+
@to_unicode_cmap_data.sub!(/<00A0>.*<d840dc3e>\n/m, '')
|
|
95
97
|
assert_equal(@to_unicode_cmap_data,
|
|
96
98
|
HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
|
|
97
99
|
end
|
|
98
100
|
|
|
99
101
|
it "works with only ranges" do
|
|
100
|
-
@to_unicode_mapping
|
|
102
|
+
@to_unicode_mapping[-2, 2] = []
|
|
101
103
|
@to_unicode_mapping.delete_at(0x5f)
|
|
102
|
-
@to_unicode_cmap_data.sub!(/\
|
|
104
|
+
@to_unicode_cmap_data.sub!(/\n3 beginbfchar.*endbfchar/m, '')
|
|
103
105
|
assert_equal(@to_unicode_cmap_data,
|
|
104
106
|
HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
|
|
105
107
|
end
|
|
@@ -37,6 +37,10 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
37
37
|
refute(HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false).subset?)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
it "can be asked for the filename from which the wrapped font was created" do
|
|
41
|
+
assert_equal(@font_file, @font_wrapper.filename)
|
|
42
|
+
end
|
|
43
|
+
|
|
40
44
|
describe "decode_*" do
|
|
41
45
|
it "decode_utf8 returns an array of glyph objects" do
|
|
42
46
|
assert_equal("Test",
|
|
@@ -119,9 +123,9 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
119
123
|
assert_equal([3].pack('n'), code)
|
|
120
124
|
end
|
|
121
125
|
|
|
122
|
-
it "doesn't use char codes 13, 40, 41 and 92 because they would need to be escaped" do
|
|
126
|
+
it "doesn't use char codes 10, 13, 40, 41 and 92 because they would need to be escaped" do
|
|
123
127
|
codes = 1.upto(93).map {|i| @font_wrapper.encode(@font_wrapper.glyph(i)) }.join
|
|
124
|
-
assert_equal([1..12, 14..39, 42..91, 93..
|
|
128
|
+
assert_equal([1..9, 11..12, 14..39, 42..91, 93..98].flat_map(&:to_a).pack('n*'), codes)
|
|
125
129
|
end
|
|
126
130
|
|
|
127
131
|
it "raises an error if an InvalidGlyph is encoded" do
|
|
@@ -29,12 +29,13 @@ describe HexaPDF::Font::TrueType::Subsetter do
|
|
|
29
29
|
|
|
30
30
|
it "doesn't use certain subset glyph IDs for performance reasons" do
|
|
31
31
|
1.upto(93) {|i| @subsetter.use_glyph(i) }
|
|
32
|
-
# glyph 0
|
|
33
|
-
assert_equal(1 + 93 +
|
|
34
|
-
1.upto(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
# glyph 0 and 93 are used glyph, 5 special glyphs
|
|
33
|
+
assert_equal(1 + 93 + 5, @subsetter.instance_variable_get(:@glyph_map).size)
|
|
34
|
+
1.upto(9) {|i| assert_equal(i, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
35
|
+
10.upto(11) {|i| assert_equal(i + 1, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
36
|
+
12.upto(37) {|i| assert_equal(i + 2, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
37
|
+
38.upto(87) {|i| assert_equal(i + 4, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
38
|
+
88.upto(93) {|i| assert_equal(i + 5, @subsetter.subset_glyph_id(i), "id=#{i}") }
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
it "creates the subset font file" do
|
|
@@ -30,6 +30,13 @@ describe HexaPDF::FontLoader::FromFile do
|
|
|
30
30
|
refute(wrapper.subset?)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
it "raises an error if the provided font does not contain TrueType outlines" do
|
|
34
|
+
font = HexaPDF::Font::TrueType::Font.new(File.open(@font_file, 'rb'))
|
|
35
|
+
font.directory.instance_variable_get(:@tables).delete('glyf')
|
|
36
|
+
exception = assert_raises(HexaPDF::Error) { @klass.call(@doc, font) }
|
|
37
|
+
assert_match(/does not contain TrueType but CFF/, exception.message)
|
|
38
|
+
end
|
|
39
|
+
|
|
33
40
|
it "returns nil if the given name doesn't represent a file" do
|
|
34
41
|
assert_nil(@klass.call(@doc, "Unknown"))
|
|
35
42
|
end
|
|
@@ -71,9 +71,11 @@ describe HexaPDF::Layout::ContainerBox do
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
it "splits the box if splitting is allowed and the content is too big" do
|
|
74
|
-
box = create_box([child_box(height: 80), child_box(height: 30)], splitable: true)
|
|
74
|
+
box = create_box([child_box(width: 30, height: 80), child_box(height: 30)], splitable: true)
|
|
75
75
|
box.fit(@frame.available_width, @frame.available_height, @frame)
|
|
76
76
|
assert(box.fit_result.overflow?)
|
|
77
|
+
assert_equal(100, box.width)
|
|
78
|
+
assert_equal(80, box.height)
|
|
77
79
|
end
|
|
78
80
|
end
|
|
79
81
|
|
|
@@ -785,6 +785,10 @@ describe HexaPDF::Layout::Style do
|
|
|
785
785
|
assert_equal(100, @style.horizontal_scaling)
|
|
786
786
|
assert_equal(0, @style.text_rise)
|
|
787
787
|
assert_equal({}, @style.font_features)
|
|
788
|
+
assert_nil(@style.font_script)
|
|
789
|
+
assert_equal(:ltr, @style.direction)
|
|
790
|
+
assert_nil(@style.language)
|
|
791
|
+
assert_equal(:internal, @style.shaping_engine)
|
|
788
792
|
assert_equal(HexaPDF::Content::TextRenderingMode::FILL, @style.text_rendering_mode)
|
|
789
793
|
assert_equal([0], @style.fill_color.components)
|
|
790
794
|
assert_equal(1, @style.fill_alpha)
|
|
@@ -128,6 +128,20 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
|
128
128
|
assert_equal(32, cell.height)
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
+
it "can split a cell if necessary" do
|
|
132
|
+
children = [HexaPDF::Layout::Box.create(width: 70, height: 60),
|
|
133
|
+
HexaPDF::Layout::Box.create(width: 70, height: 60)]
|
|
134
|
+
container = HexaPDF::Layout::ContainerBox.new(children: children, splitable: true)
|
|
135
|
+
[container, container.children].each do |used_children|
|
|
136
|
+
cell = create_cell(children: used_children)
|
|
137
|
+
assert(cell.fit(100, 100, @frame).overflow?)
|
|
138
|
+
assert_equal(100, cell.width)
|
|
139
|
+
assert_equal(72, cell.height)
|
|
140
|
+
assert_equal(used_children == container ? 100 : 82, cell.preferred_width)
|
|
141
|
+
assert_equal(72, cell.preferred_height)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
131
145
|
it "doesn't fit children that are too big" do
|
|
132
146
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 300, height: 20))
|
|
133
147
|
assert(cell.fit(100, 100, @frame).failure?)
|
|
@@ -142,6 +156,18 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
|
142
156
|
end
|
|
143
157
|
end
|
|
144
158
|
|
|
159
|
+
describe "split" do
|
|
160
|
+
it "assigns the overflown boxes to the split box" do
|
|
161
|
+
children = [HexaPDF::Layout::Box.create(width: 70, height: 60),
|
|
162
|
+
HexaPDF::Layout::Box.create(width: 70, height: 60)]
|
|
163
|
+
cell = create_cell(children: children)
|
|
164
|
+
assert(cell.fit(100, 100, @frame).overflow?)
|
|
165
|
+
box, overflow_box = cell.split
|
|
166
|
+
assert_same(cell, box)
|
|
167
|
+
assert_equal([children[1]], overflow_box.children)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
145
171
|
describe "draw" do
|
|
146
172
|
before do
|
|
147
173
|
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
@@ -349,6 +375,13 @@ describe HexaPDF::Layout::TableBox::Cells do
|
|
|
349
375
|
cells = create_cells([[:a, :b], [:c], [:d, :e]])
|
|
350
376
|
assert_equal([[:a, :b], [:c], [:d, :e]], cells.each_row.map {|cols| cols.map(&:children) })
|
|
351
377
|
end
|
|
378
|
+
|
|
379
|
+
it "allows iterating over rows containing an overridden one" do
|
|
380
|
+
cells = create_cells([[:a, :b], [:c], [:d, :e]])
|
|
381
|
+
cells.instance_variable_set(:@overridden_row_index, 1)
|
|
382
|
+
cells.instance_variable_set(:@overridden_row_cells, [cells[2, 1], cells[2, 0]])
|
|
383
|
+
assert_equal([[:a, :b], [:e, :d], [:d, :e]], cells.each_row.map {|cols| cols.map(&:children) })
|
|
384
|
+
end
|
|
352
385
|
end
|
|
353
386
|
|
|
354
387
|
describe "style" do
|
|
@@ -612,6 +645,29 @@ describe HexaPDF::Layout::TableBox do
|
|
|
612
645
|
check_box(box, :overflow, 160, 10,
|
|
613
646
|
[[0, 0, 80, 10], [80, 0, 80, 10], [nil, nil, 80, 0], [nil, nil, 0, 0]])
|
|
614
647
|
end
|
|
648
|
+
|
|
649
|
+
it "fails if not even enough height for the first row is available" do
|
|
650
|
+
check_box(create_box(height: 10), :failure, 160, 10)
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
describe "last row splitting" do
|
|
654
|
+
before do
|
|
655
|
+
@boxes = [[80, 50], [80, 40], [80, 70]].map do |w, h|
|
|
656
|
+
HexaPDF::Layout::Box.new(width: w, height: h, &@draw_block)
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
it "splits the last row if necessary" do
|
|
661
|
+
box = create_box(cells: [[@boxes[0], @boxes[1, 2]]], cell_style: {padding: 0, border: {width: 0}})
|
|
662
|
+
check_box(box, :overflow, 160, 50, [[0, 0, 80, 50], [80, 0, 80, 50]])
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
it "doesn't split the last row if it is part of a row span" do
|
|
666
|
+
cells = [[@boxes[0], {content: @boxes[1, 2], row_span: 2}]]
|
|
667
|
+
box = create_box(cells: cells, cell_style: {padding: 0, border: {width: 0}})
|
|
668
|
+
check_box(box, :failure, 160, 0)
|
|
669
|
+
end
|
|
670
|
+
end
|
|
615
671
|
end
|
|
616
672
|
|
|
617
673
|
describe "split" do
|
|
@@ -672,6 +728,23 @@ describe HexaPDF::Layout::TableBox do
|
|
|
672
728
|
end
|
|
673
729
|
end
|
|
674
730
|
end
|
|
731
|
+
|
|
732
|
+
it "splits the last row of a table" do
|
|
733
|
+
box = create_box(cells: [[@fixed_size_boxes[0], @fixed_size_boxes[1, 3]]],
|
|
734
|
+
cell_style: {padding: 0, border: {width: 0}})
|
|
735
|
+
assert(box.fit(100, 15, @frame).overflow?)
|
|
736
|
+
box_a, box_b = box.split
|
|
737
|
+
assert_same(box_a, box)
|
|
738
|
+
|
|
739
|
+
assert_equal(0, box_a.start_row_index)
|
|
740
|
+
assert_equal(0, box_a.last_fitted_row_index)
|
|
741
|
+
assert_equal(0, box_b.start_row_index)
|
|
742
|
+
assert_equal(-1, box_b.last_fitted_row_index)
|
|
743
|
+
|
|
744
|
+
assert_nil(box_b.cells[0, 0].children)
|
|
745
|
+
assert_same(box_a.cells[0, 0].style, box_b.cells[0, 0].style)
|
|
746
|
+
assert_equal(@fixed_size_boxes[2, 2], box_b.cells[0, 1].children)
|
|
747
|
+
end
|
|
675
748
|
end
|
|
676
749
|
|
|
677
750
|
describe "draw_content" do
|
|
@@ -722,7 +795,7 @@ describe HexaPDF::Layout::TableBox do
|
|
|
722
795
|
assert_operators(@canvas.contents, operators)
|
|
723
796
|
end
|
|
724
797
|
|
|
725
|
-
it "correctly works for split
|
|
798
|
+
it "correctly works for split tables" do
|
|
726
799
|
box = create_box(cell_style: {padding: 0, border: {width: 0}})
|
|
727
800
|
assert(box.fit(100, 10, @frame).overflow?)
|
|
728
801
|
_, split_box = box.split
|
|
@@ -753,6 +826,49 @@ describe HexaPDF::Layout::TableBox do
|
|
|
753
826
|
assert_operators(@canvas.contents, operators)
|
|
754
827
|
end
|
|
755
828
|
|
|
829
|
+
it "correctly works for split cells" do
|
|
830
|
+
box = create_box(cells: [[@fixed_size_boxes[0], @fixed_size_boxes[1, 3]]],
|
|
831
|
+
cell_style: {padding: 0, border: {width: 0}})
|
|
832
|
+
box.cells[0, 0].style.background_color = 'red'
|
|
833
|
+
assert(box.fit(100, 10, @frame).overflow?)
|
|
834
|
+
_, split_box = box.split
|
|
835
|
+
assert(split_box.fit(100, 100, @frame).success?)
|
|
836
|
+
|
|
837
|
+
box.draw(@canvas, 20, 10)
|
|
838
|
+
split_box.draw(@canvas, 0, 50)
|
|
839
|
+
operators = [[:save_graphics_state],
|
|
840
|
+
[:set_device_rgb_non_stroking_color, [1, 0, 0]],
|
|
841
|
+
[:append_rectangle, [20, 10, 50, 10]],
|
|
842
|
+
[:fill_path_non_zero],
|
|
843
|
+
[:restore_graphics_state],
|
|
844
|
+
[:save_graphics_state],
|
|
845
|
+
[:concatenate_matrix, [1, 0, 0, 1, 20, 10]],
|
|
846
|
+
[:move_to, [0, 0]],
|
|
847
|
+
[:end_path],
|
|
848
|
+
[:restore_graphics_state],
|
|
849
|
+
[:save_graphics_state],
|
|
850
|
+
[:concatenate_matrix, [1, 0, 0, 1, 70, 10]],
|
|
851
|
+
[:move_to, [0, 0]],
|
|
852
|
+
[:end_path],
|
|
853
|
+
[:restore_graphics_state],
|
|
854
|
+
[:save_graphics_state],
|
|
855
|
+
[:set_device_rgb_non_stroking_color, [1, 0, 0]],
|
|
856
|
+
[:append_rectangle, [0, 50, 50, 20]],
|
|
857
|
+
[:fill_path_non_zero],
|
|
858
|
+
[:restore_graphics_state],
|
|
859
|
+
[:save_graphics_state],
|
|
860
|
+
[:concatenate_matrix, [1, 0, 0, 1, 50, 60]],
|
|
861
|
+
[:move_to, [0, 0]],
|
|
862
|
+
[:end_path],
|
|
863
|
+
[:restore_graphics_state],
|
|
864
|
+
[:save_graphics_state],
|
|
865
|
+
[:concatenate_matrix, [1, 0, 0, 1, 50, 50]],
|
|
866
|
+
[:move_to, [0, 0]],
|
|
867
|
+
[:end_path],
|
|
868
|
+
[:restore_graphics_state]]
|
|
869
|
+
assert_operators(@canvas.contents, operators)
|
|
870
|
+
end
|
|
871
|
+
|
|
756
872
|
it "correctly works for tables with headers and footers" do
|
|
757
873
|
box = create_box(header: lambda {|_| [@fixed_size_boxes[10, 1]] },
|
|
758
874
|
footer: lambda {|_| [@fixed_size_boxes[12, 1]] },
|
|
@@ -50,24 +50,29 @@ describe HexaPDF::Layout::TextFragment do
|
|
|
50
50
|
it "replaces invalid glyphs with the result of the block" do
|
|
51
51
|
zapf_dingbats = @doc.fonts.add('ZapfDingbats')
|
|
52
52
|
i = 0
|
|
53
|
-
fallback = lambda do |codepoint,
|
|
54
|
-
case (i += 1) %
|
|
53
|
+
fallback = lambda do |codepoint, invalid_glyph|
|
|
54
|
+
case (i += 1) % 4
|
|
55
55
|
when 0 then []
|
|
56
56
|
when 1 then [zapf_dingbats.decode_codepoint(codepoint)]
|
|
57
57
|
when 2 then @font.decode_utf8("Tom")
|
|
58
|
+
when 3 then [invalid_glyph]
|
|
58
59
|
end
|
|
59
60
|
end
|
|
60
61
|
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20, font_features: {kern: true})
|
|
61
62
|
|
|
62
|
-
frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("✂Tom
|
|
63
|
-
|
|
63
|
+
frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("✂Tom✂Tom✂Tom✂Tom\u{ad}",
|
|
64
|
+
style, &fallback)
|
|
65
|
+
assert_equal(8, frags.size)
|
|
64
66
|
assert_equal(zapf_dingbats, frags[0].style.font)
|
|
65
67
|
assert_equal(:a2, frags[0].items[0].name)
|
|
68
|
+
assert_equal("Tom", frags[1].text)
|
|
69
|
+
assert_equal("Tom", frags[2].text)
|
|
66
70
|
assert_equal(@font, frags[2].style.font)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
assert_equal(
|
|
70
|
-
assert_equal(
|
|
71
|
+
assert_equal("Tom", frags[3].text)
|
|
72
|
+
assert_equal(:'.notdef', frags[4].items[0].name)
|
|
73
|
+
assert_equal("Tom", frags[5].text)
|
|
74
|
+
assert_equal("Tom", frags[6].text)
|
|
75
|
+
assert_equal("\u{ad}", frags[7].text)
|
|
71
76
|
end
|
|
72
77
|
end
|
|
73
78
|
|
|
@@ -173,6 +178,11 @@ describe HexaPDF::Layout::TextFragment do
|
|
|
173
178
|
[:move_text_next_line]])
|
|
174
179
|
end
|
|
175
180
|
|
|
181
|
+
it "without any horizontal or vertical movement" do
|
|
182
|
+
@fragment.draw(@canvas, 0, 0, ignore_text_properties: true)
|
|
183
|
+
assert_operators(@canvas.contents, [[:begin_text]])
|
|
184
|
+
end
|
|
185
|
+
|
|
176
186
|
it "only horizontal movement" do
|
|
177
187
|
@fragment.draw(@canvas, 20, 0, ignore_text_properties: true)
|
|
178
188
|
assert_operators(@canvas.contents, [[:begin_text],
|
|
@@ -15,7 +15,7 @@ describe HexaPDF::Layout::TextShaper do
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def setup_fragment(items, **options)
|
|
18
|
-
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20,
|
|
18
|
+
style = HexaPDF::Layout::Style.new(font: @font, font_size: 20, **options)
|
|
19
19
|
HexaPDF::Layout::TextFragment.new(items, style)
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -26,14 +26,15 @@ describe HexaPDF::Layout::TextShaper do
|
|
|
26
26
|
|
|
27
27
|
it "handles ligatures" do
|
|
28
28
|
fragment = setup_fragment(@font.decode_utf8('fish fish fi').insert(1, 100).
|
|
29
|
-
insert(0, 100), liga: true)
|
|
29
|
+
insert(0, 100), font_features: {liga: true})
|
|
30
30
|
@shaper.shape_text(fragment)
|
|
31
31
|
assert_equal([100, :fi, :s, :h, :space, :fi, :s, :h, :space, :fi],
|
|
32
32
|
fragment.items.map {|item| item.kind_of?(Numeric) ? item : item.id })
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it "handles kerning" do
|
|
36
|
-
fragment = setup_fragment(@font.decode_utf8('fish fish wow').insert(1, 100),
|
|
36
|
+
fragment = setup_fragment(@font.decode_utf8('fish fish wow').insert(1, 100),
|
|
37
|
+
font_features: {kern: true})
|
|
37
38
|
@shaper.shape_text(fragment)
|
|
38
39
|
assert_equal([:f, 100, :i, :s, :h, :space, :f, 20, :i, :s, :h, :space, :w, 10, :o, 25, :w],
|
|
39
40
|
fragment.items.map {|item| item.kind_of?(Numeric) ? item : item.id })
|
|
@@ -47,16 +48,65 @@ describe HexaPDF::Layout::TextShaper do
|
|
|
47
48
|
@font = HexaPDF::Font::TrueTypeWrapper.new(@doc, @wrapped_font)
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
it "handles kerning" do
|
|
51
|
+
it "handles kerning via the kern table" do
|
|
51
52
|
data = [0, 1].pack('n2') <<
|
|
52
53
|
[0, 6 + 8 + 12, 0x1].pack('n3') <<
|
|
53
54
|
[2, 0, 0, 0, 53, 80, -20, 80, 81, -10].pack('n4n2s>n2s>')
|
|
54
55
|
table = create_table(:Kern, data, standalone: true)
|
|
55
56
|
@wrapped_font.instance_eval { @tables[:kern] = table }
|
|
56
|
-
fragment = setup_fragment(@font.decode_utf8('Top Top').insert(1, 100),
|
|
57
|
+
fragment = setup_fragment(@font.decode_utf8('Top Top').insert(1, 100),
|
|
58
|
+
shaping_engine: :internal, font_features: {kern: true})
|
|
57
59
|
@shaper.shape_text(fragment)
|
|
58
60
|
assert_equal([53, [100], 80, [10], 81, 3, 53, [20], 80, [10], 81],
|
|
59
61
|
fragment.items.map {|item| item.kind_of?(Numeric) ? [item] : item.id })
|
|
60
62
|
end
|
|
63
|
+
|
|
64
|
+
describe "HarfBuzz OpenType shaper" do
|
|
65
|
+
before do
|
|
66
|
+
@font = @doc.fonts.add('Inter')
|
|
67
|
+
skip if Gem.win_platform?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "performs the shaping" do
|
|
71
|
+
# Test composition of o+diaresis, invalid char \n, kerning WA, x/y offsets with marks
|
|
72
|
+
fragment = setup_fragment(@font.decode_utf8("ö\nWAď̄"), shaping_engine: :harfbuzz,
|
|
73
|
+
font_features: {kern: true})
|
|
74
|
+
assert_equal(8, fragment.items.size)
|
|
75
|
+
result = @shaper.shape_text(fragment)
|
|
76
|
+
assert_equal(2, result.size)
|
|
77
|
+
[[791, 0, 459, [56.640625], 2, 603],
|
|
78
|
+
[[664.55078125], 1773, [-664.55078125]]].each_with_index do |expected, index|
|
|
79
|
+
assert_equal(expected,
|
|
80
|
+
result[index].items.map {|item| item.kind_of?(Numeric) ? [item] : item.id })
|
|
81
|
+
end
|
|
82
|
+
assert_equal("\n", result[0].items[1].str)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "handles glyphs with the same cluster number" do
|
|
86
|
+
# Force Harfbuzz into cluster level 0 to force the same cluster numbers
|
|
87
|
+
cluster_level_method = HarfBuzz::Buffer.instance_method(:cluster_level=)
|
|
88
|
+
HarfBuzz::Buffer.remove_method(:cluster_level=)
|
|
89
|
+
HarfBuzz::Buffer.define_method(:cluster_level=) {|val| }
|
|
90
|
+
|
|
91
|
+
fragment = setup_fragment(@font.decode_utf8("ď̄aď̄"), shaping_engine: :harfbuzz)
|
|
92
|
+
result = @shaper.shape_text(fragment)
|
|
93
|
+
assert_equal("ď̄", result[0].items[0].str)
|
|
94
|
+
assert_equal("", result[1].items[1].str)
|
|
95
|
+
ensure
|
|
96
|
+
HarfBuzz::Buffer.remove_method(:cluster_level=)
|
|
97
|
+
HarfBuzz::Buffer.define_method(:cluster_level=, cluster_level_method)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "raises an error if the harfbuzz-ruby gem is not available" do
|
|
101
|
+
Object.send(:remove_const, :HARFBUZZ_AVAILABLE)
|
|
102
|
+
Object.const_set(:HARFBUZZ_AVAILABLE, false)
|
|
103
|
+
fragment = setup_fragment(@font.decode_utf8('test'), shaping_engine: :harfbuzz)
|
|
104
|
+
error = assert_raises(HexaPDF::Error) { @shaper.shape_text(fragment) }
|
|
105
|
+
assert_match(/harfbuzz-ruby/, error.message)
|
|
106
|
+
ensure
|
|
107
|
+
Object.send(:remove_const, :HARFBUZZ_AVAILABLE)
|
|
108
|
+
Object.const_set(:HARFBUZZ_AVAILABLE, true)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
61
111
|
end
|
|
62
112
|
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/task/import_pages'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Task::ImportPages do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@pages = [@doc.pages.add, @doc.pages.add]
|
|
11
|
+
@pages[0][:Page1] = true
|
|
12
|
+
@pages[1][:Page2] = true
|
|
13
|
+
|
|
14
|
+
@target = HexaPDF::Document.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe "pages argument" do
|
|
18
|
+
it "imports all pages by default" do
|
|
19
|
+
@target.task(:import_pages, source: @doc)
|
|
20
|
+
assert_equal(2, @target.pages.count)
|
|
21
|
+
assert(@target.pages[0][:Page1])
|
|
22
|
+
assert(@target.pages[1][:Page2])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "imports the provided page objects" do
|
|
26
|
+
@target.task(:import_pages, source: @doc, pages: @pages.reverse)
|
|
27
|
+
assert_equal(2, @target.pages.count)
|
|
28
|
+
assert(@target.pages[0][:Page2])
|
|
29
|
+
assert(@target.pages[1][:Page1])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "imports a single page" do
|
|
33
|
+
@target.task(:import_pages, source: @doc, pages: 1)
|
|
34
|
+
assert_equal(1, @target.pages.count)
|
|
35
|
+
assert(@target.pages[0][:Page2])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "imports a page range" do
|
|
39
|
+
@target.task(:import_pages, source: @doc, pages: 0..-1)
|
|
40
|
+
assert_equal(2, @target.pages.count)
|
|
41
|
+
assert(@target.pages[0][:Page1])
|
|
42
|
+
assert(@target.pages[1][:Page2])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "imports multiple pages" do
|
|
46
|
+
@target.task(:import_pages, source: @doc, pages: [1, 0..-1],
|
|
47
|
+
ocgs: :ignore, acro_form: :ignore)
|
|
48
|
+
assert_equal(2, @target.pages.count)
|
|
49
|
+
assert(@target.pages[0][:Page2])
|
|
50
|
+
assert(@target.pages[1][:Page1])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "doesn't append the pages if specified so" do
|
|
55
|
+
result = @target.task(:import_pages, source: @doc, append: false)
|
|
56
|
+
assert_equal(0, @target.pages.count)
|
|
57
|
+
assert_equal(2, result.size)
|
|
58
|
+
assert(result[0][:Page1])
|
|
59
|
+
assert(result[1][:Page2])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "merges the AcroForm fields" do
|
|
63
|
+
form = @doc.acro_form(create: true)
|
|
64
|
+
field = form.create_text_field("Text")
|
|
65
|
+
field.create_widget(@doc.pages[0], Rect: [0, 0, 0, 0])
|
|
66
|
+
@doc.dispatch_message(:complete_objects)
|
|
67
|
+
@doc.validate
|
|
68
|
+
|
|
69
|
+
@target.task(:import_pages, source: @doc)
|
|
70
|
+
assert_equal(1, @target.acro_form.root_fields.size)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "ocgs argument" do
|
|
74
|
+
before do
|
|
75
|
+
@ocg1 = @doc.optional_content.ocg('OCG')
|
|
76
|
+
@ocg1.add_to_ui(path: @ocg1)
|
|
77
|
+
@ocg2 = @doc.optional_content.ocg('OCMD')
|
|
78
|
+
@ocg2.add_to_ui(path: @ocg2)
|
|
79
|
+
@ocg2.off!
|
|
80
|
+
@ocmd = @doc.optional_content.create_ocmd(@ocg2)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "doesn't preserve unused ocgs" do
|
|
84
|
+
@target.task(:import_pages, source: @doc)
|
|
85
|
+
assert(@target.optional_content.ocgs.empty?)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "preserves OCGs and OCMDs in content streams" do
|
|
89
|
+
canvas = @doc.pages[0].canvas
|
|
90
|
+
canvas.optional_content(@ocg1)
|
|
91
|
+
canvas.optional_content(@ocmd)
|
|
92
|
+
@target.task(:import_pages, source: @doc)
|
|
93
|
+
assert_equal(['OCG', 'OCMD'], @target.optional_content.ocgs.map(&:name))
|
|
94
|
+
assert(@target.optional_content.ocg('OCG').on?)
|
|
95
|
+
refute(@target.optional_content.ocg('OCMD').on?)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "preserves OCGs/OCMDs associated with XObjects" do
|
|
99
|
+
canvas = @doc.pages[0].canvas
|
|
100
|
+
form = canvas.form
|
|
101
|
+
form[:OC] = @ocg1
|
|
102
|
+
canvas.xobject(form, at: [0, 0])
|
|
103
|
+
@target.task(:import_pages, source: @doc)
|
|
104
|
+
assert_equal(['OCG'], @target.optional_content.ocgs.map(&:name))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "preserves OCGs/OCMDs associated with annotations" do
|
|
108
|
+
annot = @doc.annotations.create_line(@doc.pages[0], start_point: [0, 0], end_point: [50, 50])
|
|
109
|
+
annot[:OC] = @ocmd
|
|
110
|
+
annot.regenerate_appearance
|
|
111
|
+
@target.task(:import_pages, source: @doc)
|
|
112
|
+
assert_equal(['OCMD'], @target.optional_content.ocgs.map(&:name))
|
|
113
|
+
refute(@target.optional_content.ocg('OCMD').on?)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "preserves the radio button group state of imported OCGs" do
|
|
117
|
+
@doc.pages[0].canvas.optional_content(@ocg1)
|
|
118
|
+
@doc.optional_content.default_configuration[:RBGroups] = [[@ocg1, @ocg2]]
|
|
119
|
+
@target.task(:import_pages, source: @doc)
|
|
120
|
+
assert_equal(['OCG'], @target.optional_content.ocgs.map {|ocg| ocg.name })
|
|
121
|
+
rb_groups = @target.optional_content.default_configuration[:RBGroups]
|
|
122
|
+
assert_equal(1, rb_groups.size)
|
|
123
|
+
assert_equal(['OCG'], rb_groups[0].map(&:name))
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -104,7 +104,7 @@ describe HexaPDF::Serializer do
|
|
|
104
104
|
|
|
105
105
|
it "serializes strings" do
|
|
106
106
|
assert_serialized("(Hallo)", "Hallo")
|
|
107
|
-
assert_serialized("(Hallo\\r
|
|
107
|
+
assert_serialized("(Hallo\\r\\n\t\\(\\)\\\\)", "Hallo\r\n\t()\\")
|
|
108
108
|
assert_serialized("(\xFE\xFF\x00H\x00a\x00l\x00\f\x00\b\x00\\()".b, "Hal\f\b(")
|
|
109
109
|
end
|
|
110
110
|
|
|
@@ -605,4 +605,67 @@ describe HexaPDF::Type::Annotations::AppearanceGenerator do
|
|
|
605
605
|
[:fill_and_stroke_path_non_zero]], range: 6..-1)
|
|
606
606
|
end
|
|
607
607
|
end
|
|
608
|
+
|
|
609
|
+
describe "ink" do
|
|
610
|
+
before do
|
|
611
|
+
@ink = @doc.add({Type: :Annot, Subtype: :Ink, C: [0],
|
|
612
|
+
InkList: [[100, 100, 200, 150], [210, 80, 110, 160]]})
|
|
613
|
+
@generator = HexaPDF::Type::Annotations::AppearanceGenerator.new(@ink)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
it "sets the print flag and unsets the hidden flag" do
|
|
617
|
+
@ink.flag(:hidden)
|
|
618
|
+
@generator.create_appearance
|
|
619
|
+
assert(@ink.flagged?(:print))
|
|
620
|
+
refute(@ink.flagged?(:hidden))
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
it "creates the appearance" do
|
|
624
|
+
@generator.create_appearance
|
|
625
|
+
assert_equal([96, 76, 214, 164], @ink[:Rect])
|
|
626
|
+
assert_equal([96, 76, 214, 164], @ink.appearance[:BBox])
|
|
627
|
+
assert_operators(@ink.appearance.stream,
|
|
628
|
+
[[:move_to, [100, 100]],
|
|
629
|
+
[:line_to, [200, 150]],
|
|
630
|
+
[:move_to, [210, 80]],
|
|
631
|
+
[:line_to, [110, 160]],
|
|
632
|
+
[:stroke_path]])
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
describe "stroke color" do
|
|
636
|
+
it "uses the specified border color for stroking operations" do
|
|
637
|
+
@ink.border_style(color: "red")
|
|
638
|
+
@generator.create_appearance
|
|
639
|
+
assert_operators(@ink.appearance.stream,
|
|
640
|
+
[:set_device_rgb_stroking_color, [1, 0, 0]], range: 0)
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
it "works with a transparent border" do
|
|
644
|
+
@ink.border_style(color: :transparent)
|
|
645
|
+
@generator.create_appearance
|
|
646
|
+
assert_operators(@ink.appearance.stream, [:end_path], range: 4)
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
it "sets the specified border line width" do
|
|
651
|
+
@ink.border_style(width: 4)
|
|
652
|
+
@generator.create_appearance
|
|
653
|
+
assert_operators(@ink.appearance.stream,
|
|
654
|
+
[:set_line_width, [4]], range: 0)
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
it "sets the specified line dash pattern if it is an array" do
|
|
658
|
+
@ink.border_style(style: [5, 2])
|
|
659
|
+
@generator.create_appearance
|
|
660
|
+
assert_operators(@ink.appearance.stream,
|
|
661
|
+
[:set_line_dash_pattern, [[5, 2], 0]], range: 0)
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
it "sets the specified opacity" do
|
|
665
|
+
@ink.opacity(fill_alpha: 0.5, stroke_alpha: 0.5)
|
|
666
|
+
@generator.create_appearance
|
|
667
|
+
assert_operators(@ink.appearance.stream,
|
|
668
|
+
[:set_graphics_state_parameters, [:GS1]], range: 0)
|
|
669
|
+
end
|
|
670
|
+
end
|
|
608
671
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/document'
|
|
5
|
+
require 'hexapdf/type/annotations/ink'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Type::Annotations::Ink do
|
|
8
|
+
before do
|
|
9
|
+
@doc = HexaPDF::Document.new
|
|
10
|
+
@annot = @doc.add({Type: :Annot, Subtype: :Ink, Rect: [0, 0, 0, 0]})
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "returns the paths" do
|
|
14
|
+
assert_equal([], @annot.paths)
|
|
15
|
+
@annot[:InkList] = [[10, 20, 30, 40], [50, 60, 70, 80]]
|
|
16
|
+
assert_equal([[10, 20, 30, 40], [50, 60, 70, 80]], @annot.paths)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "add_paths" do
|
|
20
|
+
it "adds the given path" do
|
|
21
|
+
assert_same(@annot, @annot.add_path(1, 2, 3, 4, 5, 6))
|
|
22
|
+
assert_equal([[1, 2, 3, 4, 5, 6]], @annot[:InkList])
|
|
23
|
+
@annot.add_path(7, 8, 9, 10)
|
|
24
|
+
assert_equal([[1, 2, 3, 4, 5, 6], [7, 8, 9, 10]], @annot[:InkList])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "raises an ArgumentError if an uneven number of arguments is provided" do
|
|
28
|
+
assert_raises(ArgumentError) { @annot.add_path(1, 2, 3) }
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|