hexapdf 0.1.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 +7 -0
- data/CONTRIBUTERS +3 -0
- data/LICENSE +26 -0
- data/README.md +88 -0
- data/Rakefile +121 -0
- data/VERSION +1 -0
- data/agpl-3.0.txt +661 -0
- data/bin/hexapdf +6 -0
- data/data/hexapdf/afm/Courier-Bold.afm +342 -0
- data/data/hexapdf/afm/Courier-BoldOblique.afm +342 -0
- data/data/hexapdf/afm/Courier-Oblique.afm +342 -0
- data/data/hexapdf/afm/Courier.afm +342 -0
- data/data/hexapdf/afm/Helvetica-Bold.afm +2827 -0
- data/data/hexapdf/afm/Helvetica-BoldOblique.afm +2827 -0
- data/data/hexapdf/afm/Helvetica-Oblique.afm +3051 -0
- data/data/hexapdf/afm/Helvetica.afm +3051 -0
- data/data/hexapdf/afm/MustRead.html +1 -0
- data/data/hexapdf/afm/Symbol.afm +213 -0
- data/data/hexapdf/afm/Times-Bold.afm +2588 -0
- data/data/hexapdf/afm/Times-BoldItalic.afm +2384 -0
- data/data/hexapdf/afm/Times-Italic.afm +2667 -0
- data/data/hexapdf/afm/Times-Roman.afm +2419 -0
- data/data/hexapdf/afm/ZapfDingbats.afm +225 -0
- data/data/hexapdf/encoding/glyphlist.txt +4305 -0
- data/data/hexapdf/encoding/zapfdingbats.txt +225 -0
- data/examples/arc.rb +50 -0
- data/examples/graphics.rb +274 -0
- data/examples/hello_world.rb +16 -0
- data/examples/machupicchu.jpg +0 -0
- data/examples/merging.rb +24 -0
- data/examples/optimizing.rb +20 -0
- data/examples/show_char_bboxes.rb +55 -0
- data/examples/standard_pdf_fonts.rb +72 -0
- data/examples/truetype.rb +45 -0
- data/lib/hexapdf/cli/extract.rb +128 -0
- data/lib/hexapdf/cli/info.rb +121 -0
- data/lib/hexapdf/cli/inspect.rb +157 -0
- data/lib/hexapdf/cli/modify.rb +218 -0
- data/lib/hexapdf/cli.rb +121 -0
- data/lib/hexapdf/configuration.rb +392 -0
- data/lib/hexapdf/content/canvas.rb +1974 -0
- data/lib/hexapdf/content/color_space.rb +364 -0
- data/lib/hexapdf/content/graphic_object/arc.rb +267 -0
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +208 -0
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +173 -0
- data/lib/hexapdf/content/graphic_object.rb +81 -0
- data/lib/hexapdf/content/graphics_state.rb +579 -0
- data/lib/hexapdf/content/operator.rb +1072 -0
- data/lib/hexapdf/content/parser.rb +204 -0
- data/lib/hexapdf/content/processor.rb +451 -0
- data/lib/hexapdf/content/transformation_matrix.rb +172 -0
- data/lib/hexapdf/content.rb +47 -0
- data/lib/hexapdf/data_dir.rb +51 -0
- data/lib/hexapdf/dictionary.rb +303 -0
- data/lib/hexapdf/dictionary_fields.rb +382 -0
- data/lib/hexapdf/document.rb +589 -0
- data/lib/hexapdf/document_utils.rb +209 -0
- data/lib/hexapdf/encryption/aes.rb +206 -0
- data/lib/hexapdf/encryption/arc4.rb +93 -0
- data/lib/hexapdf/encryption/fast_aes.rb +79 -0
- data/lib/hexapdf/encryption/fast_arc4.rb +67 -0
- data/lib/hexapdf/encryption/identity.rb +63 -0
- data/lib/hexapdf/encryption/ruby_aes.rb +447 -0
- data/lib/hexapdf/encryption/ruby_arc4.rb +96 -0
- data/lib/hexapdf/encryption/security_handler.rb +494 -0
- data/lib/hexapdf/encryption/standard_security_handler.rb +616 -0
- data/lib/hexapdf/encryption.rb +94 -0
- data/lib/hexapdf/error.rb +73 -0
- data/lib/hexapdf/filter/ascii85_decode.rb +160 -0
- data/lib/hexapdf/filter/ascii_hex_decode.rb +87 -0
- data/lib/hexapdf/filter/dct_decode.rb +57 -0
- data/lib/hexapdf/filter/encryption.rb +59 -0
- data/lib/hexapdf/filter/flate_decode.rb +93 -0
- data/lib/hexapdf/filter/jpx_decode.rb +56 -0
- data/lib/hexapdf/filter/lzw_decode.rb +191 -0
- data/lib/hexapdf/filter/predictor.rb +266 -0
- data/lib/hexapdf/filter/run_length_decode.rb +108 -0
- data/lib/hexapdf/filter.rb +176 -0
- data/lib/hexapdf/font/cmap/parser.rb +146 -0
- data/lib/hexapdf/font/cmap/writer.rb +176 -0
- data/lib/hexapdf/font/cmap.rb +90 -0
- data/lib/hexapdf/font/encoding/base.rb +77 -0
- data/lib/hexapdf/font/encoding/difference_encoding.rb +64 -0
- data/lib/hexapdf/font/encoding/glyph_list.rb +150 -0
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +221 -0
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +265 -0
- data/lib/hexapdf/font/encoding/standard_encoding.rb +205 -0
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +244 -0
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +280 -0
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +250 -0
- data/lib/hexapdf/font/encoding.rb +68 -0
- data/lib/hexapdf/font/true_type/font.rb +179 -0
- data/lib/hexapdf/font/true_type/table/cmap.rb +103 -0
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +384 -0
- data/lib/hexapdf/font/true_type/table/directory.rb +92 -0
- data/lib/hexapdf/font/true_type/table/glyf.rb +166 -0
- data/lib/hexapdf/font/true_type/table/head.rb +143 -0
- data/lib/hexapdf/font/true_type/table/hhea.rb +109 -0
- data/lib/hexapdf/font/true_type/table/hmtx.rb +79 -0
- data/lib/hexapdf/font/true_type/table/loca.rb +79 -0
- data/lib/hexapdf/font/true_type/table/maxp.rb +112 -0
- data/lib/hexapdf/font/true_type/table/name.rb +218 -0
- data/lib/hexapdf/font/true_type/table/os2.rb +200 -0
- data/lib/hexapdf/font/true_type/table/post.rb +230 -0
- data/lib/hexapdf/font/true_type/table.rb +155 -0
- data/lib/hexapdf/font/true_type.rb +48 -0
- data/lib/hexapdf/font/true_type_wrapper.rb +240 -0
- data/lib/hexapdf/font/type1/afm_parser.rb +230 -0
- data/lib/hexapdf/font/type1/character_metrics.rb +67 -0
- data/lib/hexapdf/font/type1/font.rb +123 -0
- data/lib/hexapdf/font/type1/font_metrics.rb +117 -0
- data/lib/hexapdf/font/type1/pfb_parser.rb +71 -0
- data/lib/hexapdf/font/type1.rb +52 -0
- data/lib/hexapdf/font/type1_wrapper.rb +193 -0
- data/lib/hexapdf/font_loader/from_configuration.rb +70 -0
- data/lib/hexapdf/font_loader/standard14.rb +98 -0
- data/lib/hexapdf/font_loader.rb +85 -0
- data/lib/hexapdf/font_utils.rb +89 -0
- data/lib/hexapdf/image_loader/jpeg.rb +166 -0
- data/lib/hexapdf/image_loader/pdf.rb +89 -0
- data/lib/hexapdf/image_loader/png.rb +410 -0
- data/lib/hexapdf/image_loader.rb +68 -0
- data/lib/hexapdf/importer.rb +139 -0
- data/lib/hexapdf/name_tree_node.rb +78 -0
- data/lib/hexapdf/number_tree_node.rb +67 -0
- data/lib/hexapdf/object.rb +363 -0
- data/lib/hexapdf/parser.rb +349 -0
- data/lib/hexapdf/rectangle.rb +99 -0
- data/lib/hexapdf/reference.rb +98 -0
- data/lib/hexapdf/revision.rb +206 -0
- data/lib/hexapdf/revisions.rb +194 -0
- data/lib/hexapdf/serializer.rb +326 -0
- data/lib/hexapdf/stream.rb +279 -0
- data/lib/hexapdf/task/dereference.rb +109 -0
- data/lib/hexapdf/task/optimize.rb +230 -0
- data/lib/hexapdf/task.rb +68 -0
- data/lib/hexapdf/tokenizer.rb +406 -0
- data/lib/hexapdf/type/catalog.rb +107 -0
- data/lib/hexapdf/type/embedded_file.rb +87 -0
- data/lib/hexapdf/type/file_specification.rb +232 -0
- data/lib/hexapdf/type/font.rb +81 -0
- data/lib/hexapdf/type/font_descriptor.rb +109 -0
- data/lib/hexapdf/type/font_simple.rb +190 -0
- data/lib/hexapdf/type/font_true_type.rb +47 -0
- data/lib/hexapdf/type/font_type1.rb +162 -0
- data/lib/hexapdf/type/form.rb +103 -0
- data/lib/hexapdf/type/graphics_state_parameter.rb +79 -0
- data/lib/hexapdf/type/image.rb +73 -0
- data/lib/hexapdf/type/info.rb +70 -0
- data/lib/hexapdf/type/names.rb +69 -0
- data/lib/hexapdf/type/object_stream.rb +224 -0
- data/lib/hexapdf/type/page.rb +355 -0
- data/lib/hexapdf/type/page_tree_node.rb +269 -0
- data/lib/hexapdf/type/resources.rb +212 -0
- data/lib/hexapdf/type/trailer.rb +128 -0
- data/lib/hexapdf/type/viewer_preferences.rb +73 -0
- data/lib/hexapdf/type/xref_stream.rb +204 -0
- data/lib/hexapdf/type.rb +67 -0
- data/lib/hexapdf/utils/bit_field.rb +87 -0
- data/lib/hexapdf/utils/bit_stream.rb +148 -0
- data/lib/hexapdf/utils/lru_cache.rb +65 -0
- data/lib/hexapdf/utils/math_helpers.rb +55 -0
- data/lib/hexapdf/utils/object_hash.rb +130 -0
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +93 -0
- data/lib/hexapdf/utils/sorted_tree_node.rb +339 -0
- data/lib/hexapdf/version.rb +39 -0
- data/lib/hexapdf/writer.rb +199 -0
- data/lib/hexapdf/xref_section.rb +152 -0
- data/lib/hexapdf.rb +34 -0
- data/man/man1/hexapdf.1 +249 -0
- data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
- data/test/data/fonts/Ubuntu-Title.ttf +0 -0
- data/test/data/images/cmyk.jpg +0 -0
- data/test/data/images/fillbytes.jpg +0 -0
- data/test/data/images/gray.jpg +0 -0
- data/test/data/images/greyscale-1bit.png +0 -0
- data/test/data/images/greyscale-2bit.png +0 -0
- data/test/data/images/greyscale-4bit.png +0 -0
- data/test/data/images/greyscale-8bit.png +0 -0
- data/test/data/images/greyscale-alpha-8bit.png +0 -0
- data/test/data/images/greyscale-trns-8bit.png +0 -0
- data/test/data/images/greyscale-with-gamma1.0.png +0 -0
- data/test/data/images/greyscale-with-gamma1.5.png +0 -0
- data/test/data/images/indexed-1bit.png +0 -0
- data/test/data/images/indexed-2bit.png +0 -0
- data/test/data/images/indexed-4bit.png +0 -0
- data/test/data/images/indexed-8bit.png +0 -0
- data/test/data/images/indexed-alpha-4bit.png +0 -0
- data/test/data/images/indexed-alpha-8bit.png +0 -0
- data/test/data/images/rgb.jpg +0 -0
- data/test/data/images/truecolour-8bit.png +0 -0
- data/test/data/images/truecolour-alpha-8bit.png +0 -0
- data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
- data/test/data/images/truecolour-srgb-8bit.png +0 -0
- data/test/data/minimal.pdf +44 -0
- data/test/data/standard-security-handler/README +9 -0
- data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
- data/test/hexapdf/common_tokenizer_tests.rb +204 -0
- data/test/hexapdf/content/common.rb +31 -0
- data/test/hexapdf/content/graphic_object/test_arc.rb +93 -0
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +91 -0
- data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
- data/test/hexapdf/content/test_canvas.rb +1113 -0
- data/test/hexapdf/content/test_color_space.rb +97 -0
- data/test/hexapdf/content/test_graphics_state.rb +138 -0
- data/test/hexapdf/content/test_operator.rb +619 -0
- data/test/hexapdf/content/test_parser.rb +66 -0
- data/test/hexapdf/content/test_processor.rb +156 -0
- data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
- data/test/hexapdf/encryption/common.rb +87 -0
- data/test/hexapdf/encryption/test_aes.rb +121 -0
- data/test/hexapdf/encryption/test_arc4.rb +39 -0
- data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
- data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
- data/test/hexapdf/encryption/test_identity.rb +21 -0
- data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
- data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
- data/test/hexapdf/encryption/test_security_handler.rb +356 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +274 -0
- data/test/hexapdf/filter/common.rb +53 -0
- data/test/hexapdf/filter/test_ascii85_decode.rb +60 -0
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +33 -0
- data/test/hexapdf/filter/test_encryption.rb +24 -0
- data/test/hexapdf/filter/test_flate_decode.rb +35 -0
- data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
- data/test/hexapdf/filter/test_predictor.rb +183 -0
- data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
- data/test/hexapdf/font/cmap/test_parser.rb +67 -0
- data/test/hexapdf/font/cmap/test_writer.rb +58 -0
- data/test/hexapdf/font/encoding/test_base.rb +35 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +21 -0
- data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
- data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
- data/test/hexapdf/font/test_encoding.rb +27 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +110 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +66 -0
- data/test/hexapdf/font/true_type/common.rb +19 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +59 -0
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +133 -0
- data/test/hexapdf/font/true_type/table/test_directory.rb +35 -0
- data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
- data/test/hexapdf/font/true_type/table/test_head.rb +76 -0
- data/test/hexapdf/font/true_type/table/test_hhea.rb +40 -0
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +38 -0
- data/test/hexapdf/font/true_type/table/test_loca.rb +43 -0
- data/test/hexapdf/font/true_type/table/test_maxp.rb +62 -0
- data/test/hexapdf/font/true_type/table/test_name.rb +95 -0
- data/test/hexapdf/font/true_type/table/test_os2.rb +65 -0
- data/test/hexapdf/font/true_type/table/test_post.rb +89 -0
- data/test/hexapdf/font/true_type/test_font.rb +120 -0
- data/test/hexapdf/font/true_type/test_table.rb +41 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +51 -0
- data/test/hexapdf/font/type1/test_font.rb +68 -0
- data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +28 -0
- data/test/hexapdf/font_loader/test_standard14.rb +22 -0
- data/test/hexapdf/image_loader/test_jpeg.rb +83 -0
- data/test/hexapdf/image_loader/test_pdf.rb +47 -0
- data/test/hexapdf/image_loader/test_png.rb +258 -0
- data/test/hexapdf/task/test_dereference.rb +46 -0
- data/test/hexapdf/task/test_optimize.rb +137 -0
- data/test/hexapdf/test_configuration.rb +82 -0
- data/test/hexapdf/test_data_dir.rb +32 -0
- data/test/hexapdf/test_dictionary.rb +284 -0
- data/test/hexapdf/test_dictionary_fields.rb +185 -0
- data/test/hexapdf/test_document.rb +574 -0
- data/test/hexapdf/test_document_utils.rb +144 -0
- data/test/hexapdf/test_filter.rb +96 -0
- data/test/hexapdf/test_font_utils.rb +47 -0
- data/test/hexapdf/test_importer.rb +78 -0
- data/test/hexapdf/test_object.rb +177 -0
- data/test/hexapdf/test_parser.rb +394 -0
- data/test/hexapdf/test_rectangle.rb +36 -0
- data/test/hexapdf/test_reference.rb +41 -0
- data/test/hexapdf/test_revision.rb +139 -0
- data/test/hexapdf/test_revisions.rb +93 -0
- data/test/hexapdf/test_serializer.rb +169 -0
- data/test/hexapdf/test_stream.rb +262 -0
- data/test/hexapdf/test_tokenizer.rb +30 -0
- data/test/hexapdf/test_writer.rb +120 -0
- data/test/hexapdf/test_xref_section.rb +35 -0
- data/test/hexapdf/type/test_catalog.rb +30 -0
- data/test/hexapdf/type/test_embedded_file.rb +16 -0
- data/test/hexapdf/type/test_file_specification.rb +148 -0
- data/test/hexapdf/type/test_font.rb +35 -0
- data/test/hexapdf/type/test_font_descriptor.rb +51 -0
- data/test/hexapdf/type/test_font_simple.rb +190 -0
- data/test/hexapdf/type/test_font_type1.rb +128 -0
- data/test/hexapdf/type/test_form.rb +60 -0
- data/test/hexapdf/type/test_info.rb +14 -0
- data/test/hexapdf/type/test_names.rb +9 -0
- data/test/hexapdf/type/test_object_stream.rb +84 -0
- data/test/hexapdf/type/test_page.rb +260 -0
- data/test/hexapdf/type/test_page_tree_node.rb +255 -0
- data/test/hexapdf/type/test_resources.rb +167 -0
- data/test/hexapdf/type/test_trailer.rb +109 -0
- data/test/hexapdf/type/test_xref_stream.rb +131 -0
- data/test/hexapdf/utils/test_bit_field.rb +47 -0
- data/test/hexapdf/utils/test_lru_cache.rb +22 -0
- data/test/hexapdf/utils/test_object_hash.rb +115 -0
- data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +232 -0
- data/test/test_helper.rb +56 -0
- metadata +427 -0
@@ -0,0 +1,1113 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require_relative 'common'
|
5
|
+
require 'hexapdf/content/canvas'
|
6
|
+
require 'hexapdf/document'
|
7
|
+
require 'hexapdf/content/processor'
|
8
|
+
require 'hexapdf/content/parser'
|
9
|
+
|
10
|
+
describe HexaPDF::Content::Canvas do
|
11
|
+
before do
|
12
|
+
@processor = TestHelper::OperatorRecorder.new
|
13
|
+
@parser = HexaPDF::Content::Parser.new
|
14
|
+
|
15
|
+
@doc = HexaPDF::Document.new
|
16
|
+
@doc.config['graphic_object.arc.max_curves'] = 4
|
17
|
+
@page = @doc.pages.add_page
|
18
|
+
@canvas = @page.canvas
|
19
|
+
end
|
20
|
+
|
21
|
+
# Asserts that the content string contains the operators.
|
22
|
+
def assert_operators(content, operators)
|
23
|
+
@processor.recorded_ops.clear
|
24
|
+
@parser.parse(content, @processor)
|
25
|
+
assert_equal(operators, @processor.recorded_ops)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Asserts that a specific operator is invoked when the block is executed.
|
29
|
+
def assert_operator_invoked(op, *args)
|
30
|
+
mock = Minitest::Mock.new
|
31
|
+
if args.empty?
|
32
|
+
mock.expect(:invoke, nil) { true }
|
33
|
+
mock.expect(:serialize, '') { true }
|
34
|
+
else
|
35
|
+
mock.expect(:invoke, nil, [@canvas] + args)
|
36
|
+
mock.expect(:serialize, '', [@canvas.instance_variable_get(:@serializer)] + args)
|
37
|
+
end
|
38
|
+
op_before = @canvas.instance_variable_get(:@operators)[op]
|
39
|
+
@canvas.instance_variable_get(:@operators)[op] = mock
|
40
|
+
yield
|
41
|
+
assert(mock.verify)
|
42
|
+
ensure
|
43
|
+
@canvas.instance_variable_get(:@operators)[op] = op_before
|
44
|
+
end
|
45
|
+
|
46
|
+
# Asserts that the block raises an error when in one of the given graphics objects.
|
47
|
+
def assert_raises_in_graphics_object(*objects, &block)
|
48
|
+
objects.each do |graphics_object|
|
49
|
+
@canvas.graphics_object = graphics_object
|
50
|
+
assert_raises(HexaPDF::Error, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "contents" do
|
55
|
+
it "returns the serialized contents of the canvas operations" do
|
56
|
+
@canvas.save_graphics_state {}
|
57
|
+
assert_equal("q\nQ\n", @canvas.contents)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "stream_data" do
|
62
|
+
it "it closes an open path object" do
|
63
|
+
@canvas.move_to(5, 5)
|
64
|
+
assert_equal("5 5 m\nn\n", @canvas.stream_data.fiber.resume)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "it closes an open text object" do
|
68
|
+
@canvas.begin_text
|
69
|
+
assert_equal("BT\nET\n", @canvas.stream_data.fiber.resume)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "rewinds the graphics state stack" do
|
73
|
+
@canvas.save_graphics_state
|
74
|
+
@canvas.begin_text
|
75
|
+
assert_equal("q\nBT\nET\nQ\n", @canvas.stream_data.fiber.resume)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "resources" do
|
80
|
+
it "returns the resources of the context object" do
|
81
|
+
assert_equal(@page.resources, @canvas.resources)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "save_graphics_state" do
|
86
|
+
it "invokes the operator implementation" do
|
87
|
+
assert_operator_invoked(:q) { @canvas.save_graphics_state }
|
88
|
+
end
|
89
|
+
|
90
|
+
it "is serialized correctly when no block is used" do
|
91
|
+
@canvas.save_graphics_state
|
92
|
+
assert_operators(@canvas.contents, [[:save_graphics_state]])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "is serialized correctly when a block is used" do
|
96
|
+
@canvas.save_graphics_state { }
|
97
|
+
assert_operators(@canvas.contents, [[:save_graphics_state], [:restore_graphics_state]])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
101
|
+
assert_raises_in_graphics_object(:path, :clipping_path) { @canvas.save_graphics_state }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "restore_graphics_state" do
|
106
|
+
it "invokes the operator implementation" do
|
107
|
+
assert_operator_invoked(:Q) { @canvas.restore_graphics_state }
|
108
|
+
end
|
109
|
+
|
110
|
+
it "is serialized correctly" do
|
111
|
+
@canvas.graphics_state.save
|
112
|
+
@canvas.restore_graphics_state
|
113
|
+
assert_operators(@page.contents, [[:restore_graphics_state]])
|
114
|
+
end
|
115
|
+
|
116
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
117
|
+
assert_raises_in_graphics_object(:path, :clipping_path) { @canvas.restore_graphics_state }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "transform" do
|
122
|
+
it "invokes the operator implementation" do
|
123
|
+
assert_operator_invoked(:cm, 1, 2, 3, 4, 5, 6) { @canvas.transform(1, 2, 3, 4, 5, 6) }
|
124
|
+
end
|
125
|
+
|
126
|
+
it "is serialized correctly when no block is used" do
|
127
|
+
@canvas.transform(1, 2, 3, 4, 5, 6)
|
128
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [1, 2, 3, 4, 5, 6]]])
|
129
|
+
end
|
130
|
+
|
131
|
+
it "is serialized correctly when a block is used" do
|
132
|
+
@canvas.transform(1, 2, 3, 4, 5, 6) {}
|
133
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
134
|
+
[:concatenate_matrix, [1, 2, 3, 4, 5, 6]],
|
135
|
+
[:restore_graphics_state]])
|
136
|
+
end
|
137
|
+
|
138
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
139
|
+
assert_raises_in_graphics_object(:path, :clipping_path) { @canvas.transform(1, 2, 3, 4, 5, 6) }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "rotate" do
|
144
|
+
it "can rotate around the origin" do
|
145
|
+
@canvas.rotate(90)
|
146
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [0, 1, -1, 0, 0, 0]]])
|
147
|
+
end
|
148
|
+
|
149
|
+
it "can rotate about an arbitrary point" do
|
150
|
+
@canvas.rotate(90, origin: [100, 200])
|
151
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [0.0, 1.0, -1.0, 0.0, 300.0, 100.0]]])
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "scale" do
|
156
|
+
it "can scale from the origin" do
|
157
|
+
@canvas.scale(5, 10)
|
158
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [5, 0, 0, 10, 0, 0]]])
|
159
|
+
end
|
160
|
+
|
161
|
+
it "can scale from an arbitrary point" do
|
162
|
+
@canvas.scale(5, 10, origin: [100, 200])
|
163
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [5, 0, 0, 10, -400, -1800]]])
|
164
|
+
end
|
165
|
+
|
166
|
+
it "works with a single scale factor" do
|
167
|
+
@canvas.scale(5)
|
168
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [5, 0, 0, 5, 0, 0]]])
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "translate" do
|
173
|
+
it "translates the origin" do
|
174
|
+
@canvas.translate(100, 200)
|
175
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [1, 0, 0, 1, 100, 200]]])
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "skew" do
|
180
|
+
it "can skew from the origin" do
|
181
|
+
@canvas.skew(45, 0)
|
182
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [1, 1, 0, 1, 0, 0]]])
|
183
|
+
end
|
184
|
+
|
185
|
+
it "can skew from an arbitrary point" do
|
186
|
+
@canvas.skew(45, 0, origin: [100, 200])
|
187
|
+
assert_operators(@page.contents, [[:concatenate_matrix, [1, 1, 0, 1, 0, -100]]])
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "private gs_getter_setter" do
|
192
|
+
it "returns the current value when used with a nil argument" do
|
193
|
+
@canvas.graphics_state.line_width = 5
|
194
|
+
assert_equal(5, @canvas.send(:gs_getter_setter, :line_width, :w, nil))
|
195
|
+
end
|
196
|
+
|
197
|
+
it "returns the canvas object when used with a non-nil argument or a block" do
|
198
|
+
assert_equal(@canvas, @canvas.send(:gs_getter_setter, :line_width, :w, 15))
|
199
|
+
assert_equal(@canvas, @canvas.send(:gs_getter_setter, :line_width, :w, 15) {})
|
200
|
+
end
|
201
|
+
|
202
|
+
it "invokes the operator implementation when a non-nil argument is used" do
|
203
|
+
assert_operator_invoked(:w, 5) { @canvas.send(:gs_getter_setter, :line_width, :w, 5) }
|
204
|
+
assert_operator_invoked(:w, 15) { @canvas.send(:gs_getter_setter, :line_width, :w, 15) {} }
|
205
|
+
end
|
206
|
+
|
207
|
+
it "doesn't add an operator if the value is equal to the current one" do
|
208
|
+
@canvas.send(:gs_getter_setter, :line_width, :w,
|
209
|
+
@canvas.send(:gs_getter_setter, :line_width, :w, nil))
|
210
|
+
assert_operators(@page.contents, [])
|
211
|
+
end
|
212
|
+
|
213
|
+
it "always saves and restores the graphics state if a block is used" do
|
214
|
+
@canvas.send(:gs_getter_setter, :line_width, :w,
|
215
|
+
@canvas.send(:gs_getter_setter, :line_width, :w, nil)) {}
|
216
|
+
assert_operators(@page.contents, [[:save_graphics_state], [:restore_graphics_state]])
|
217
|
+
end
|
218
|
+
|
219
|
+
it "is serialized correctly when no block is used" do
|
220
|
+
@canvas.send(:gs_getter_setter, :line_width, :w, 5)
|
221
|
+
assert_operators(@page.contents, [[:set_line_width, [5]]])
|
222
|
+
end
|
223
|
+
|
224
|
+
it "is serialized correctly when a block is used" do
|
225
|
+
@canvas.send(:gs_getter_setter, :line_width, :w, 5) do
|
226
|
+
@canvas.send(:gs_getter_setter, :line_width, :w, 15)
|
227
|
+
end
|
228
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
229
|
+
[:set_line_width, [5]],
|
230
|
+
[:set_line_width, [15]],
|
231
|
+
[:restore_graphics_state]])
|
232
|
+
end
|
233
|
+
|
234
|
+
it "fails if a block is given without an argument" do
|
235
|
+
assert_raises(ArgumentError) { @canvas.send(:gs_getter_setter, :line_width, :w, nil) {} }
|
236
|
+
end
|
237
|
+
|
238
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
239
|
+
assert_raises_in_graphics_object(:path, :clipping_path) do
|
240
|
+
@canvas.send(:gs_getter_setter, :line_width, :w, 5)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Asserts that the method +name+ invoked with +values+ invokes the #gs_getter_setter helper method
|
246
|
+
# with the +name+, +operator+ and +expected_value+ as arguments.
|
247
|
+
def assert_gs_getter_setter(name, operator, expected_value, *values)
|
248
|
+
args = [name, operator, expected_value]
|
249
|
+
assert_method_invoked(@canvas, :gs_getter_setter, args, check_block: true) do
|
250
|
+
@canvas.send(name, *values) {}
|
251
|
+
end
|
252
|
+
unless values.compact.empty?
|
253
|
+
@canvas.send(name, *values)
|
254
|
+
assert_equal(expected_value, @canvas.graphics_state.send(name))
|
255
|
+
end
|
256
|
+
assert_respond_to(@canvas, name)
|
257
|
+
end
|
258
|
+
|
259
|
+
describe "line_width" do
|
260
|
+
it "uses the gs_getter_setter implementation" do
|
261
|
+
assert_gs_getter_setter(:line_width, :w, 5, 5)
|
262
|
+
assert_gs_getter_setter(:line_width, :w, nil, nil)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "line_cap_style" do
|
267
|
+
it "uses the gs_getter_setter implementation" do
|
268
|
+
assert_gs_getter_setter(:line_cap_style, :J, 1, :round)
|
269
|
+
assert_gs_getter_setter(:line_cap_style, :J, nil, nil)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
describe "line_join_style" do
|
274
|
+
it "uses the gs_getter_setter implementation" do
|
275
|
+
assert_gs_getter_setter(:line_join_style, :j, 1, :round)
|
276
|
+
assert_gs_getter_setter(:line_join_style, :j, nil, nil)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "miter_limit" do
|
281
|
+
it "uses the gs_getter_setter implementation" do
|
282
|
+
assert_gs_getter_setter(:miter_limit, :M, 15, 15)
|
283
|
+
assert_gs_getter_setter(:miter_limit, :M, nil, nil)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "line_dash_pattern" do
|
288
|
+
it "uses the gs_getter_setter implementation" do
|
289
|
+
assert_gs_getter_setter(:line_dash_pattern, :d, nil, nil)
|
290
|
+
assert_gs_getter_setter(:line_dash_pattern, :d,
|
291
|
+
HexaPDF::Content::LineDashPattern.new, 0)
|
292
|
+
assert_gs_getter_setter(:line_dash_pattern, :d,
|
293
|
+
HexaPDF::Content::LineDashPattern.new([5]), 5)
|
294
|
+
assert_gs_getter_setter(:line_dash_pattern, :d,
|
295
|
+
HexaPDF::Content::LineDashPattern.new([5], 2), 5, 2)
|
296
|
+
assert_gs_getter_setter(:line_dash_pattern, :d,
|
297
|
+
HexaPDF::Content::LineDashPattern.new([5, 3], 2), [5, 3], 2)
|
298
|
+
assert_gs_getter_setter(:line_dash_pattern, :d,
|
299
|
+
HexaPDF::Content::LineDashPattern.new([5, 3], 2),
|
300
|
+
HexaPDF::Content::LineDashPattern.new([5, 3], 2))
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe "rendering_intent" do
|
305
|
+
it "uses the gs_getter_setter implementation" do
|
306
|
+
assert_gs_getter_setter(:rendering_intent, :ri, :Perceptual, :Perceptual)
|
307
|
+
assert_gs_getter_setter(:rendering_intent, :ri, nil, nil)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
describe "opacity" do
|
312
|
+
it "returns the current values when no argument/nil arguments are provided" do
|
313
|
+
assert_equal({fill_alpha: 1.0, stroke_alpha: 1.0}, @canvas.opacity)
|
314
|
+
end
|
315
|
+
|
316
|
+
it "returns the canvas object when at least one non-nil argument is provided" do
|
317
|
+
assert_equal(@canvas, @canvas.opacity(fill_alpha: 0.5))
|
318
|
+
end
|
319
|
+
|
320
|
+
it "invokes the operator implementation when at least one non-nil argument is used" do
|
321
|
+
assert_operator_invoked(:gs, :GS1) do
|
322
|
+
@canvas.opacity(fill_alpha: 1.0, stroke_alpha: 0.5)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
it "doesn't add an operator if the values are not really changed" do
|
327
|
+
@canvas.opacity(fill_alpha: 1.0, stroke_alpha: 1.0)
|
328
|
+
assert_operators(@page.contents, [])
|
329
|
+
end
|
330
|
+
|
331
|
+
it "always saves and restores the graphics state if a block is used" do
|
332
|
+
@canvas.opacity(fill_alpha: 1.0, stroke_alpha: 1.0) {}
|
333
|
+
assert_operators(@page.contents, [[:save_graphics_state], [:restore_graphics_state]])
|
334
|
+
end
|
335
|
+
|
336
|
+
it "adds the needed entry to the /ExtGState resources dictionary" do
|
337
|
+
@canvas.graphics_state.alpha_source = true
|
338
|
+
@canvas.opacity(fill_alpha: 0.5, stroke_alpha: 0.7)
|
339
|
+
assert_equal({Type: :ExtGState, CA: 0.7, ca: 0.5, AIS: false},
|
340
|
+
@canvas.resources.ext_gstate(:GS1))
|
341
|
+
end
|
342
|
+
|
343
|
+
it "is serialized correctly when no block is used" do
|
344
|
+
@canvas.opacity(fill_alpha: 0.5, stroke_alpha: 0.7)
|
345
|
+
assert_operators(@page.contents, [[:set_graphics_state_parameters, [:GS1]]])
|
346
|
+
end
|
347
|
+
|
348
|
+
it "is serialized correctly when a block is used" do
|
349
|
+
@canvas.opacity(fill_alpha: 0.5) do
|
350
|
+
@canvas.opacity(stroke_alpha: 0.7)
|
351
|
+
end
|
352
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
353
|
+
[:set_graphics_state_parameters, [:GS1]],
|
354
|
+
[:set_graphics_state_parameters, [:GS2]],
|
355
|
+
[:restore_graphics_state]])
|
356
|
+
end
|
357
|
+
|
358
|
+
it "fails if a block is given without an argument" do
|
359
|
+
assert_raises(ArgumentError) { @canvas.opacity {} }
|
360
|
+
end
|
361
|
+
|
362
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
363
|
+
assert_raises_in_graphics_object(:path, :clipping_path) { @canvas.opacity(fill_alpha: 1.0) }
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
describe "private color_getter_setter" do
|
368
|
+
def invoke(*params, &block)
|
369
|
+
@canvas.send(:color_getter_setter, :stroke_color, params, :RG, :G, :K, :CS, :SCN, &block)
|
370
|
+
end
|
371
|
+
|
372
|
+
it "returns the current value when used with no argument" do
|
373
|
+
color = @canvas.graphics_state.stroke_color
|
374
|
+
assert_equal(color, invoke)
|
375
|
+
end
|
376
|
+
|
377
|
+
it "returns the canvas when used with a non-nil argument and no block" do
|
378
|
+
assert_equal(@canvas, invoke(255))
|
379
|
+
assert_equal(@canvas, invoke(255) {})
|
380
|
+
end
|
381
|
+
|
382
|
+
it "doesn't add an operator if the value is equal to the current one" do
|
383
|
+
invoke(0.0)
|
384
|
+
assert_operators(@page.contents, [])
|
385
|
+
end
|
386
|
+
|
387
|
+
it "always saves and restores the graphics state if a block is used" do
|
388
|
+
invoke(0.0) {}
|
389
|
+
assert_operators(@page.contents, [[:save_graphics_state], [:restore_graphics_state]])
|
390
|
+
end
|
391
|
+
|
392
|
+
it "adds an unknown color space to the resource dictionary" do
|
393
|
+
invoke(HexaPDF::Content::ColorSpace::Universal.new([:Pattern, :DeviceRGB]).color(:Name))
|
394
|
+
assert_equal([:Pattern, :DeviceRGB], @page.resources.color_space(:CS1).definition)
|
395
|
+
end
|
396
|
+
|
397
|
+
it "is serialized correctly when no block is used" do
|
398
|
+
invoke(102)
|
399
|
+
invoke([102])
|
400
|
+
invoke("6600FF")
|
401
|
+
invoke(102, 0, 255)
|
402
|
+
invoke(0, 20, 40, 80)
|
403
|
+
invoke(HexaPDF::Content::ColorSpace::Universal.new([:Pattern]).color(:Name))
|
404
|
+
assert_operators(@page.contents, [[:set_device_gray_stroking_color, [0.4]],
|
405
|
+
[:set_device_rgb_stroking_color, [0.4, 0, 1]],
|
406
|
+
[:set_device_cmyk_stroking_color, [0, 0.2, 0.4, 0.8]],
|
407
|
+
[:set_stroking_color_space, [:CS1]],
|
408
|
+
[:set_stroking_color, [:Name]]])
|
409
|
+
end
|
410
|
+
|
411
|
+
it "is serialized correctly when a block is used" do
|
412
|
+
invoke(102) { invoke(255) }
|
413
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
414
|
+
[:set_device_gray_stroking_color, [0.4]],
|
415
|
+
[:set_device_gray_stroking_color, [1.0]],
|
416
|
+
[:restore_graphics_state]])
|
417
|
+
end
|
418
|
+
|
419
|
+
it "fails if a block is given without an argument" do
|
420
|
+
assert_raises(ArgumentError) { invoke {} }
|
421
|
+
end
|
422
|
+
|
423
|
+
it "fails if an unsupported number of component values is provided" do
|
424
|
+
assert_raises(ArgumentError) { invoke(5, 5) }
|
425
|
+
end
|
426
|
+
|
427
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
428
|
+
assert_raises_in_graphics_object(:path, :clipping_path) { invoke(0.5) }
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
# Asserts that the method +name+ invoked with +values+ invokes the #color_getter_setter helper
|
433
|
+
# method with the +expected_values+ as arguments.
|
434
|
+
def assert_color_getter_setter(name, expected_values, *values)
|
435
|
+
assert_method_invoked(@canvas, :color_getter_setter, expected_values, check_block: true) do
|
436
|
+
@canvas.send(name, *values) {}
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
describe "stroke_color" do
|
441
|
+
it "uses the color_getter_setter implementation" do
|
442
|
+
assert_color_getter_setter(:stroke_color, [:stroke_color, [255], :RG, :G, :K, :CS, :SCN], 255)
|
443
|
+
assert_color_getter_setter(:stroke_color, [:stroke_color, [], :RG, :G, :K, :CS, :SCN])
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe "fill_color" do
|
448
|
+
it "uses the color_getter_setter implementation" do
|
449
|
+
assert_color_getter_setter(:fill_color, [:fill_color, [255], :rg, :g, :k, :cs, :scn], 255)
|
450
|
+
assert_color_getter_setter(:fill_color, [:fill_color, [], :rg, :g, :k, :cs, :scn])
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
describe "move_to" do
|
455
|
+
it "invokes the operator implementation" do
|
456
|
+
assert_operator_invoked(:m, 5, 6) { @canvas.move_to(5, 6) }
|
457
|
+
end
|
458
|
+
|
459
|
+
it "returns the canvas object" do
|
460
|
+
assert_equal(@canvas, @canvas.move_to(5, 6))
|
461
|
+
end
|
462
|
+
|
463
|
+
it "sets the current point correctly" do
|
464
|
+
@canvas.move_to(5, 6)
|
465
|
+
assert_equal([5, 6], @canvas.current_point)
|
466
|
+
end
|
467
|
+
|
468
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
469
|
+
assert_raises_in_graphics_object(:clipping_path) { @canvas.move_to(5, 6) }
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe "line_to" do
|
474
|
+
before do
|
475
|
+
@canvas.graphics_object = :path
|
476
|
+
end
|
477
|
+
|
478
|
+
it "invokes the operator implementation" do
|
479
|
+
assert_operator_invoked(:l, 5, 6) { @canvas.line_to(5, 6) }
|
480
|
+
end
|
481
|
+
|
482
|
+
it "returns the canvas object" do
|
483
|
+
assert_equal(@canvas, @canvas.line_to(5, 6))
|
484
|
+
end
|
485
|
+
|
486
|
+
it "sets the current point correctly" do
|
487
|
+
@canvas.line_to(5, 6)
|
488
|
+
assert_equal([5, 6], @canvas.current_point)
|
489
|
+
end
|
490
|
+
|
491
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
492
|
+
assert_raises_in_graphics_object(:none, :text, :clipping_path) { @canvas.line_to(5, 6) }
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
describe "curve_to" do
|
497
|
+
before do
|
498
|
+
@canvas.graphics_object = :path
|
499
|
+
end
|
500
|
+
|
501
|
+
it "invokes the operator implementation" do
|
502
|
+
assert_operator_invoked(:c, 5, 6, 7, 8, 9, 10) { @canvas.curve_to(9, 10, p1: [5, 6], p2: [7, 8]) }
|
503
|
+
assert_operator_invoked(:v, 7, 8, 9, 10) { @canvas.curve_to(9, 10, p2: [7, 8]) }
|
504
|
+
assert_operator_invoked(:y, 5, 6, 9, 10) { @canvas.curve_to(9, 10, p1: [5, 6]) }
|
505
|
+
end
|
506
|
+
|
507
|
+
it "returns the canvas object" do
|
508
|
+
assert_equal(@canvas, @canvas.curve_to(5, 6, p1: [7, 8]))
|
509
|
+
end
|
510
|
+
|
511
|
+
it "sets the current point correctly" do
|
512
|
+
@canvas.curve_to(5, 6, p1: [9, 10])
|
513
|
+
assert_equal([5, 6], @canvas.current_point)
|
514
|
+
end
|
515
|
+
|
516
|
+
it "raises an error if both control points are omitted" do
|
517
|
+
assert_raises(ArgumentError) { @canvas.curve_to(9, 10) }
|
518
|
+
end
|
519
|
+
|
520
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
521
|
+
assert_raises_in_graphics_object(:none, :text, :clipping_path) do
|
522
|
+
@canvas.curve_to(5, 6, p1: [7, 8])
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
describe "rectangle" do
|
528
|
+
it "invokes the operator implementation when radius == 0" do
|
529
|
+
assert_operator_invoked(:re, 5, 10, 15, 20) { @canvas.rectangle(5, 10, 15, 20) }
|
530
|
+
end
|
531
|
+
|
532
|
+
it "invokes the polygon method when radius != 0" do
|
533
|
+
args = [0, 0, 10, 0, 10, 10, 0, 10, radius: 5]
|
534
|
+
assert_method_invoked(@canvas, :polygon, args) do
|
535
|
+
@canvas.rectangle(0, 0, 10, 10, radius: 5)
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
it "returns the canvas object" do
|
540
|
+
assert_equal(@canvas, @canvas.rectangle(5, 6, 7, 8))
|
541
|
+
end
|
542
|
+
|
543
|
+
it "sets the current point correctly" do
|
544
|
+
@canvas.rectangle(5, 6, 7, 8)
|
545
|
+
assert_equal([5, 6], @canvas.current_point)
|
546
|
+
end
|
547
|
+
|
548
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
549
|
+
assert_raises_in_graphics_object(:clipping_path) { @canvas.rectangle(5, 6, 7, 8) }
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
describe "close_subpath" do
|
554
|
+
before do
|
555
|
+
@canvas.graphics_object = :path
|
556
|
+
end
|
557
|
+
|
558
|
+
it "invokes the operator implementation" do
|
559
|
+
assert_operator_invoked(:h) { @canvas.close_subpath }
|
560
|
+
end
|
561
|
+
|
562
|
+
it "returns the canvas object" do
|
563
|
+
assert_equal(@canvas, @canvas.close_subpath)
|
564
|
+
end
|
565
|
+
|
566
|
+
it "sets the current point correctly" do
|
567
|
+
@canvas.move_to(1, 1)
|
568
|
+
@canvas.line_to(10, 10)
|
569
|
+
@canvas.close_subpath
|
570
|
+
assert_equal([1, 1], @canvas.current_point)
|
571
|
+
end
|
572
|
+
|
573
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
574
|
+
assert_raises_in_graphics_object(:none, :text) { @canvas.close_subpath }
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
describe "line" do
|
579
|
+
it "serializes correctly" do
|
580
|
+
@canvas.line(1, 2, 3, 4)
|
581
|
+
assert_operators(@canvas.contents, [[:move_to, [1, 2]], [:line_to, [3, 4]]])
|
582
|
+
end
|
583
|
+
|
584
|
+
it "returns the canvas object" do
|
585
|
+
assert_equal(@canvas, @canvas.line(1, 2, 3, 4))
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
describe "polyline" do
|
590
|
+
it "serializes correctly" do
|
591
|
+
@canvas.polyline(1, 2, 3, 4, 5, 6)
|
592
|
+
assert_operators(@canvas.contents, [[:move_to, [1, 2]], [:line_to, [3, 4]], [:line_to, [5, 6]]])
|
593
|
+
end
|
594
|
+
|
595
|
+
it "returns the canvas object" do
|
596
|
+
assert_equal(@canvas, @canvas.polyline(1, 2, 3, 4))
|
597
|
+
end
|
598
|
+
|
599
|
+
it "fails if not enought points are supplied" do
|
600
|
+
assert_raises(ArgumentError) { @canvas.polyline(5, 6) }
|
601
|
+
end
|
602
|
+
|
603
|
+
it "fails if a y-coordinate is missing" do
|
604
|
+
assert_raises(ArgumentError) { @canvas.polyline(5, 6, 7, 8, 9) }
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
describe "polygon" do
|
609
|
+
it "serializes correctly with no radius" do
|
610
|
+
@canvas.polygon(1, 2, 3, 4, 5, 6)
|
611
|
+
assert_operators(@canvas.contents, [[:move_to, [1, 2]], [:line_to, [3, 4]],
|
612
|
+
[:line_to, [5, 6]], [:close_subpath]])
|
613
|
+
end
|
614
|
+
|
615
|
+
it "serializes correctly with a radius" do
|
616
|
+
@canvas.polygon(-1, -1, -1, 1, 1, 1, 1, -1, radius: 1)
|
617
|
+
k = @canvas.class::KAPPA.round(6)
|
618
|
+
assert_operators(@canvas.contents, [[:move_to, [-1, 0]],
|
619
|
+
[:line_to, [-1, 0]], [:curve_to, [-1, k, -k, 1, 0, 1]],
|
620
|
+
[:line_to, [0, 1]], [:curve_to, [k, 1, 1, k, 1, 0]],
|
621
|
+
[:line_to, [1, 0]], [:curve_to, [1, -k, k, -1, 0, -1]],
|
622
|
+
[:line_to, [0, -1]], [:curve_to, [-k, -1, -1, -k, -1, 0]],
|
623
|
+
[:close_subpath]])
|
624
|
+
end
|
625
|
+
|
626
|
+
it "returns the canvas object" do
|
627
|
+
assert_equal(@canvas, @canvas.polyline(1, 2, 3, 4, 5, 6))
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
describe "circle" do
|
632
|
+
it "uses arc for the hard work" do
|
633
|
+
assert_method_invoked(@canvas, :arc, [5, 6, a: 7]) do
|
634
|
+
@canvas.graphics_object = :path
|
635
|
+
@canvas.circle(5, 6, 7)
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
it "serializes correctly" do
|
640
|
+
@canvas.circle(0, 0, 1)
|
641
|
+
@processor.recorded_ops.clear
|
642
|
+
@parser.parse(@canvas.contents, @processor)
|
643
|
+
assert_equal([:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :close_subpath],
|
644
|
+
@processor.recorded_ops.map(&:first))
|
645
|
+
end
|
646
|
+
|
647
|
+
it "returns the canvas object" do
|
648
|
+
assert_equal(@canvas, @canvas.circle(1, 2, 3))
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
describe "ellipse" do
|
653
|
+
it "uses arc for the hard work" do
|
654
|
+
assert_method_invoked(@canvas, :ellipse, [5, 6, a: 7, b: 5, inclination: 10]) do
|
655
|
+
@canvas.ellipse(5, 6, a: 7, b: 5, inclination: 10)
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
it "serializes correctly" do
|
660
|
+
@canvas.ellipse(0, 0, a: 10, b: 5, inclination: 10)
|
661
|
+
@processor.recorded_ops.clear
|
662
|
+
@parser.parse(@canvas.contents, @processor)
|
663
|
+
assert_equal([:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :close_subpath],
|
664
|
+
@processor.recorded_ops.map(&:first))
|
665
|
+
end
|
666
|
+
|
667
|
+
it "returns the canvas object" do
|
668
|
+
assert_equal(@canvas, @canvas.circle(1, 2, 3))
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
describe "arc" do
|
673
|
+
it "serializes correctly" do
|
674
|
+
@canvas.arc(0, 0, a: 1, b: 1, start_angle: 0, end_angle: 360, inclination: 0)
|
675
|
+
@canvas.arc(0, 0, a: 1, b: 1, start_angle: 0, end_angle: 360, clockwise: true, inclination: 0)
|
676
|
+
assert_operators(@canvas.contents, [[:move_to, [1, 0]],
|
677
|
+
[:curve_to, [1, 0.548584, 0.548584, 1, 0, 1]],
|
678
|
+
[:curve_to, [-0.548584, 1, -1, 0.548584, -1, 0]],
|
679
|
+
[:curve_to, [-1, -0.548584, -0.548584, -1, 0, -1]],
|
680
|
+
[:curve_to, [0.548584, -1, 1, -0.548584, 1, 0]],
|
681
|
+
[:move_to, [1, 0]],
|
682
|
+
[:curve_to, [1, -0.548584, 0.548584, -1, 0, -1]],
|
683
|
+
[:curve_to, [-0.548584, -1, -1, -0.548584, -1, 0]],
|
684
|
+
[:curve_to, [-1, 0.548584, -0.548584, 1, 0, 1]],
|
685
|
+
[:curve_to, [0.548584, 1, 1, 0.548584, 1, 0]]])
|
686
|
+
end
|
687
|
+
|
688
|
+
it "returns the canvas object" do
|
689
|
+
assert_equal(@canvas, @canvas.arc(1, 2, a: 3))
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
describe "graphic_object" do
|
694
|
+
it "returns a new graphic object given a name" do
|
695
|
+
arc = @canvas.graphic_object(:arc)
|
696
|
+
assert_respond_to(arc, :draw)
|
697
|
+
arc1 = @canvas.graphic_object(:arc)
|
698
|
+
refute_same(arc, arc1)
|
699
|
+
end
|
700
|
+
|
701
|
+
it "returns a configured graphic object given a name" do
|
702
|
+
arc = @canvas.graphic_object(:arc, cx: 10)
|
703
|
+
assert_equal(10, arc.cx)
|
704
|
+
end
|
705
|
+
|
706
|
+
it "reconfigures the given graphic object" do
|
707
|
+
arc = @canvas.graphic_object(:arc)
|
708
|
+
arc1 = @canvas.graphic_object(arc, cx: 10)
|
709
|
+
assert_same(arc, arc1)
|
710
|
+
assert_equal(10, arc.cx)
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
describe "draw" do
|
715
|
+
it "draws the, optionally configured, graphic object onto the canvas" do
|
716
|
+
obj = Object.new
|
717
|
+
obj.define_singleton_method(:options) { @options }
|
718
|
+
obj.define_singleton_method(:configure) {|**kwargs| @options = kwargs; self}
|
719
|
+
obj.define_singleton_method(:draw) {|canvas| canvas.move_to(@options[:x], @options[:y])}
|
720
|
+
@canvas.draw(obj, x: 5, y: 6)
|
721
|
+
assert_operators(@canvas.contents, [[:move_to, [5, 6]]])
|
722
|
+
end
|
723
|
+
|
724
|
+
it "returns the canvas object" do
|
725
|
+
assert_equal(@canvas, @canvas.draw(:arc))
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
describe "path painting methods" do
|
730
|
+
before do
|
731
|
+
@canvas.graphics_object = :path
|
732
|
+
end
|
733
|
+
|
734
|
+
it "invokes the respective operator implementation" do
|
735
|
+
assert_operator_invoked(:S) { @canvas.stroke }
|
736
|
+
assert_operator_invoked(:s) { @canvas.close_stroke }
|
737
|
+
assert_operator_invoked(:f) { @canvas.fill(:nonzero) }
|
738
|
+
assert_operator_invoked(:'f*') { @canvas.fill(:even_odd) }
|
739
|
+
assert_operator_invoked(:B) { @canvas.fill_stroke(:nonzero) }
|
740
|
+
assert_operator_invoked(:'B*') { @canvas.fill_stroke(:even_odd) }
|
741
|
+
assert_operator_invoked(:b) { @canvas.close_fill_stroke(:nonzero) }
|
742
|
+
assert_operator_invoked(:'b*') { @canvas.close_fill_stroke(:even_odd) }
|
743
|
+
assert_operator_invoked(:n) { @canvas.end_path }
|
744
|
+
end
|
745
|
+
|
746
|
+
it "returns the canvas object" do
|
747
|
+
[:stroke, :close_stroke, :fill, :fill_stroke, :close_fill_stroke, :end_path].each do |m|
|
748
|
+
@canvas.graphics_object = :path
|
749
|
+
assert_equal(@canvas, @canvas.send(m))
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
754
|
+
[:stroke, :close_stroke, :fill, :fill_stroke, :close_fill_stroke, :end_path].each do |m|
|
755
|
+
assert_raises_in_graphics_object(:none, :text) { @canvas.send(m) }
|
756
|
+
end
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
describe "clip_path" do
|
761
|
+
before do
|
762
|
+
@canvas.graphics_object = :path
|
763
|
+
end
|
764
|
+
|
765
|
+
it "invokes the respective operator implementation" do
|
766
|
+
assert_operator_invoked(:W) { @canvas.clip_path(:nonzero) }
|
767
|
+
assert_operator_invoked(:'W*') { @canvas.clip_path(:even_odd) }
|
768
|
+
end
|
769
|
+
|
770
|
+
it "returns the canvas object" do
|
771
|
+
assert_equal(@canvas, @canvas.clip_path)
|
772
|
+
end
|
773
|
+
|
774
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
775
|
+
assert_raises_in_graphics_object(:none, :text, :clipping_path) { @canvas.clip_path }
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
describe "xobject" do
|
780
|
+
before do
|
781
|
+
@image = @doc.add(Subtype: :Image, Width: 10, Height: 5)
|
782
|
+
@image.source_path = File.join(TEST_DATA_DIR, 'images', 'gray.jpg')
|
783
|
+
@form = @doc.add(Subtype: :Form, BBox: [100, 50, 200, 100])
|
784
|
+
end
|
785
|
+
|
786
|
+
it "can use any xobject specified via a filename" do
|
787
|
+
xobject = @canvas.xobject(@image.source_path, at: [0, 0])
|
788
|
+
assert_equal(xobject, @page.resources.xobject(:XO1))
|
789
|
+
end
|
790
|
+
|
791
|
+
it "can use any xobject specified via an IO object" do
|
792
|
+
File.open(@image.source_path, 'rb') do |file|
|
793
|
+
xobject = @canvas.xobject(file, at: [0, 0])
|
794
|
+
assert_equal(xobject, @page.resources.xobject(:XO1))
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
798
|
+
it "can use an already existing xobject" do
|
799
|
+
xobject = @canvas.xobject(@image, at: [0, 0])
|
800
|
+
assert_equal(xobject, @page.resources.xobject(:XO1))
|
801
|
+
end
|
802
|
+
|
803
|
+
it "correctly serializes the image with no options" do
|
804
|
+
@canvas.xobject(@image, at: [1, 2])
|
805
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
806
|
+
[:concatenate_matrix, [10, 0, 0, 5, 1, 2]],
|
807
|
+
[:paint_xobject, [:XO1]],
|
808
|
+
[:restore_graphics_state]])
|
809
|
+
end
|
810
|
+
|
811
|
+
it "correctly serializes the image with just the width given" do
|
812
|
+
@canvas.image(@image, at: [1, 2], width: 20)
|
813
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
814
|
+
[:concatenate_matrix, [20, 0, 0, 10, 1, 2]],
|
815
|
+
[:paint_xobject, [:XO1]],
|
816
|
+
[:restore_graphics_state]])
|
817
|
+
end
|
818
|
+
|
819
|
+
it "correctly serializes the image with just the height given" do
|
820
|
+
@canvas.image(@image, at: [1, 2], height: 10)
|
821
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
822
|
+
[:concatenate_matrix, [20, 0, 0, 10, 1, 2]],
|
823
|
+
[:paint_xobject, [:XO1]],
|
824
|
+
[:restore_graphics_state]])
|
825
|
+
end
|
826
|
+
|
827
|
+
it "correctly serializes the image with both width and height given" do
|
828
|
+
@canvas.image(@image, at: [1, 2], width: 10, height: 20)
|
829
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
830
|
+
[:concatenate_matrix, [10, 0, 0, 20, 1, 2]],
|
831
|
+
[:paint_xobject, [:XO1]],
|
832
|
+
[:restore_graphics_state]])
|
833
|
+
end
|
834
|
+
|
835
|
+
it "correctly serializes the form with no options" do
|
836
|
+
@canvas.xobject(@form, at: [1, 2])
|
837
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
838
|
+
[:concatenate_matrix, [1, 0, 0, 1, -99, -48]],
|
839
|
+
[:paint_xobject, [:XO1]],
|
840
|
+
[:restore_graphics_state]])
|
841
|
+
end
|
842
|
+
|
843
|
+
it "correctly serializes the form with just the width given" do
|
844
|
+
@canvas.image(@form, at: [1, 2], width: 50)
|
845
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
846
|
+
[:concatenate_matrix, [0.5, 0, 0, 0.5, -99, -48]],
|
847
|
+
[:paint_xobject, [:XO1]],
|
848
|
+
[:restore_graphics_state]])
|
849
|
+
end
|
850
|
+
|
851
|
+
it "correctly serializes the form with just the height given" do
|
852
|
+
@canvas.image(@form, at: [1, 2], height: 10)
|
853
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
854
|
+
[:concatenate_matrix, [0.2, 0, 0, 0.2, -99, -48]],
|
855
|
+
[:paint_xobject, [:XO1]],
|
856
|
+
[:restore_graphics_state]])
|
857
|
+
end
|
858
|
+
|
859
|
+
it "correctly serializes the form with both width and height given" do
|
860
|
+
@canvas.image(@form, at: [1, 2], width: 50, height: 10)
|
861
|
+
assert_operators(@page.contents, [[:save_graphics_state],
|
862
|
+
[:concatenate_matrix, [0.5, 0, 0, 0.2, -99, -48]],
|
863
|
+
[:paint_xobject, [:XO1]],
|
864
|
+
[:restore_graphics_state]])
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
describe "character_spacing" do
|
869
|
+
it "uses the gs_getter_setter implementation" do
|
870
|
+
assert_gs_getter_setter(:character_spacing, :Tc, 0.25, 0.25)
|
871
|
+
assert_gs_getter_setter(:character_spacing, :Tc, nil, nil)
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
describe "word_spacing" do
|
876
|
+
it "uses the gs_getter_setter implementation" do
|
877
|
+
assert_gs_getter_setter(:word_spacing, :Tw, 0.25, 0.25)
|
878
|
+
assert_gs_getter_setter(:word_spacing, :Tw, nil, nil)
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
882
|
+
describe "horizontal_scaling" do
|
883
|
+
it "uses the gs_getter_setter implementation" do
|
884
|
+
assert_gs_getter_setter(:horizontal_scaling, :Tz, 50, 50)
|
885
|
+
assert_gs_getter_setter(:horizontal_scaling, :Tz, nil, nil)
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
describe "leading" do
|
890
|
+
it "uses the gs_getter_setter implementation" do
|
891
|
+
assert_gs_getter_setter(:leading, :TL, 15, 15)
|
892
|
+
assert_gs_getter_setter(:leading, :TL, nil, nil)
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
describe "text_rendering_mode" do
|
897
|
+
it "uses the gs_getter_setter implementation" do
|
898
|
+
assert_gs_getter_setter(:text_rendering_mode, :Tr, 0, :fill)
|
899
|
+
assert_gs_getter_setter(:text_rendering_mode, :Tr, nil, nil)
|
900
|
+
end
|
901
|
+
end
|
902
|
+
|
903
|
+
describe "text_rise" do
|
904
|
+
it "uses the gs_getter_setter implementation" do
|
905
|
+
assert_gs_getter_setter(:text_rise, :Ts, 15, 15)
|
906
|
+
assert_gs_getter_setter(:text_rise, :Ts, nil, nil)
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
describe "begin_text" do
|
911
|
+
it "invokes the operator implementation" do
|
912
|
+
assert_operator_invoked(:BT) { @canvas.begin_text }
|
913
|
+
end
|
914
|
+
|
915
|
+
it "serializes correctly" do
|
916
|
+
@canvas.begin_text
|
917
|
+
@canvas.begin_text
|
918
|
+
@canvas.begin_text(force_new: true)
|
919
|
+
@parser.parse(@canvas.contents, @processor)
|
920
|
+
assert_equal([:begin_text, :end_text, :begin_text], @processor.recorded_ops.map(&:first))
|
921
|
+
end
|
922
|
+
|
923
|
+
it "returns the canvas object" do
|
924
|
+
assert_equal(@canvas, @canvas.begin_text)
|
925
|
+
end
|
926
|
+
|
927
|
+
it "fails if the current graphics object doesn't allow a new text object" do
|
928
|
+
assert_raises(HexaPDF::Error) do
|
929
|
+
@canvas.graphics_object = :path
|
930
|
+
@canvas.begin_text
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
935
|
+
assert_raises_in_graphics_object(:path, :clipping_path) { @canvas.begin_text }
|
936
|
+
end
|
937
|
+
end
|
938
|
+
|
939
|
+
describe "end_text" do
|
940
|
+
it "invokes the operator implementation" do
|
941
|
+
@canvas.graphics_object = :text
|
942
|
+
assert_operator_invoked(:ET) { @canvas.end_text }
|
943
|
+
end
|
944
|
+
|
945
|
+
it "serializes correctly" do
|
946
|
+
@canvas.end_text
|
947
|
+
@canvas.begin_text
|
948
|
+
@canvas.end_text
|
949
|
+
@canvas.end_text
|
950
|
+
@parser.parse(@page.contents, @processor)
|
951
|
+
assert_equal([:begin_text, :end_text], @processor.recorded_ops.map(&:first))
|
952
|
+
end
|
953
|
+
|
954
|
+
it "returns the canvas object" do
|
955
|
+
assert_equal(@canvas, @canvas.begin_text)
|
956
|
+
end
|
957
|
+
|
958
|
+
it "fails if invoked while in an unsupported graphics objects" do
|
959
|
+
assert_raises_in_graphics_object(:path, :clipping_path) { @canvas.end_text }
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
describe "text_matrix" do
|
964
|
+
it "invokes the operator implementation" do
|
965
|
+
@canvas.text_matrix(1, 2, 3, 4, 5, 6)
|
966
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
967
|
+
[:set_text_matrix, [1, 2, 3, 4, 5,6]]])
|
968
|
+
end
|
969
|
+
|
970
|
+
it "returns the canvas object" do
|
971
|
+
assert_equal(@canvas, @canvas.text_matrix(1, 1, 1, 1, 1, 1))
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
describe "move_text_cursor" do
|
976
|
+
describe "invokes the operator implementation" do
|
977
|
+
it "moves to the next line" do
|
978
|
+
@canvas.move_text_cursor
|
979
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
980
|
+
[:move_text_next_line]])
|
981
|
+
end
|
982
|
+
|
983
|
+
it "moves to the next line with an offset" do
|
984
|
+
@canvas.move_text_cursor(offset: [5, 10], absolute: false)
|
985
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
986
|
+
[:move_text, [5, 10]]])
|
987
|
+
end
|
988
|
+
|
989
|
+
it "moves to an absolute position" do
|
990
|
+
@canvas.move_text_cursor(offset: [5, 10], absolute: true)
|
991
|
+
assert_operators(@canvas.contents, [[:begin_text],
|
992
|
+
[:set_text_matrix, [1, 0, 0, 1, 5, 10]]])
|
993
|
+
end
|
994
|
+
end
|
995
|
+
|
996
|
+
it "returns the canvas object" do
|
997
|
+
assert_equal(@canvas, @canvas.move_text_cursor)
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
|
1001
|
+
describe "text_cursor" do
|
1002
|
+
it "returns the text cursor position" do
|
1003
|
+
@canvas.move_text_cursor(offset: [5, 10])
|
1004
|
+
assert_equal([5, 10], @canvas.text_cursor)
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
it "fails if invoked outside a text object" do
|
1008
|
+
assert_raises_in_graphics_object(:none, :path, :clipping_path) { @canvas.text_cursor }
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
describe "font" do
|
1013
|
+
it "returns the set font" do
|
1014
|
+
assert_nil(@canvas.font)
|
1015
|
+
@canvas.font("Times", size: 10)
|
1016
|
+
assert_same(@doc.fonts.load("Times"), @canvas.font)
|
1017
|
+
@canvas.font("Helvetica", size: 10)
|
1018
|
+
assert_operators(@canvas.contents, [[:set_font_and_size, [:F1, 10]],
|
1019
|
+
[:set_leading, [12.0]],
|
1020
|
+
[:set_font_and_size, [:F2, 10]]])
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
it "sets the font and optionally the font size" do
|
1024
|
+
@canvas.font("Times", size: 12, variant: :italic)
|
1025
|
+
assert_same(@doc.fonts.load("Times", variant: :italic), @canvas.font)
|
1026
|
+
assert_equal(12, @canvas.font_size)
|
1027
|
+
@canvas.font("Helvetica")
|
1028
|
+
assert_equal(12, @canvas.font_size)
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
describe "font_size" do
|
1033
|
+
it "returns the set font size" do
|
1034
|
+
assert_equal(0, @canvas.font_size)
|
1035
|
+
@canvas.font("Times", size: 10) # calls #font_size
|
1036
|
+
assert_equal(10, @canvas.font_size)
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
it "sets the font size and, optionally, the leading" do
|
1040
|
+
@canvas.font("Times", size: 10)
|
1041
|
+
assert_equal(10, @canvas.font_size)
|
1042
|
+
assert_equal(12, @canvas.leading)
|
1043
|
+
@canvas.font_size(10, leading: 20)
|
1044
|
+
assert_equal(10, @canvas.font_size)
|
1045
|
+
assert_equal(20, @canvas.leading)
|
1046
|
+
@canvas.font_size(10, leading: nil)
|
1047
|
+
assert_equal(10, @canvas.font_size)
|
1048
|
+
assert_equal(20, @canvas.leading)
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
it "fails if no valid font is already set" do
|
1052
|
+
assert_raises(HexaPDF::Error) { @canvas.font_size(10) }
|
1053
|
+
end
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
describe "show_glyphs" do
|
1057
|
+
it "serializes correctly" do
|
1058
|
+
@canvas.font("Times", size: 20)
|
1059
|
+
@canvas.horizontal_scaling(200)
|
1060
|
+
@canvas.character_spacing(1)
|
1061
|
+
@canvas.word_spacing(2)
|
1062
|
+
|
1063
|
+
font = @canvas.font
|
1064
|
+
@canvas.show_glyphs(font.decode_utf8("Hal lo").insert(2, -35).insert(0, -10))
|
1065
|
+
assert_in_delta(116.68, @canvas.text_cursor[0])
|
1066
|
+
assert_equal(0, @canvas.text_cursor[1])
|
1067
|
+
@canvas.font_size(10)
|
1068
|
+
@canvas.show_glyphs(font.decode_utf8("Hal"))
|
1069
|
+
assert_operators(@canvas.contents, [[:set_font_and_size, [:F1, 20]],
|
1070
|
+
[:set_leading, [24]],
|
1071
|
+
[:set_horizontal_scaling, [200]],
|
1072
|
+
[:set_character_spacing, [1]],
|
1073
|
+
[:set_word_spacing, [2]],
|
1074
|
+
[:begin_text],
|
1075
|
+
[:show_text_with_positioning, [['', -10, "Ha", -35, "l lo"]]],
|
1076
|
+
[:set_font_and_size, [:F1, 10]],
|
1077
|
+
[:set_leading, [12]],
|
1078
|
+
[:show_text_with_positioning, [["Hal"]]],
|
1079
|
+
])
|
1080
|
+
end
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
describe "text" do
|
1084
|
+
it "sets the text cursor position if instructed" do
|
1085
|
+
@canvas.font("Times", size: 10)
|
1086
|
+
@canvas.text("Hallo", at: [100, 100])
|
1087
|
+
assert_operators(@canvas.contents, [[:set_font_and_size, [:F1, 10]],
|
1088
|
+
[:set_leading, [12]],
|
1089
|
+
[:begin_text],
|
1090
|
+
[:set_text_matrix, [1, 0, 0, 1, 100, 100]],
|
1091
|
+
[:show_text_with_positioning, [["Hallo"]]],
|
1092
|
+
])
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
it "shows text, possibly split over multiple lines" do
|
1096
|
+
@canvas.font("Times", size: 10)
|
1097
|
+
@canvas.text("H\u{D A}H\u{A}H\u{B}H\u{c}H\u{D}H\u{85}H\u{2028}H\u{2029}H")
|
1098
|
+
assert_operators(@canvas.contents, [[:set_font_and_size, [:F1, 10]],
|
1099
|
+
[:set_leading, [12]],
|
1100
|
+
[:begin_text],
|
1101
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1102
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1103
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1104
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1105
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1106
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1107
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1108
|
+
[:show_text_with_positioning, [["H"]]], [:move_text_next_line],
|
1109
|
+
[:show_text_with_positioning, [["H"]]],
|
1110
|
+
])
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
end
|