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,130 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2016 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#++
|
33
|
+
|
34
|
+
module HexaPDF
|
35
|
+
module Utils
|
36
|
+
|
37
|
+
# There are some structures in a PDF file, for example cross reference tables, that index data
|
38
|
+
# based on object and generation numbers. However, there is a restriction that in such
|
39
|
+
# structures the object numbers must be unique, e.g. there may not be entries for [1, 0] and
|
40
|
+
# \[1, 1] at the same time.
|
41
|
+
#
|
42
|
+
# This class can be used for storing/retrieving data for such structures.
|
43
|
+
class ObjectHash
|
44
|
+
|
45
|
+
include Enumerable
|
46
|
+
|
47
|
+
# The biggest object number that is stored in the object hash or zero if no objects are
|
48
|
+
# stored.
|
49
|
+
attr_reader :max_oid
|
50
|
+
|
51
|
+
# Creates a new object hash.
|
52
|
+
def initialize
|
53
|
+
@table = {}
|
54
|
+
@oids = {}
|
55
|
+
@max_oid = 0
|
56
|
+
end
|
57
|
+
|
58
|
+
# :call-seq:
|
59
|
+
# objhash[oid, gen] = data
|
60
|
+
#
|
61
|
+
# Sets the data for the given object and generation numbers.
|
62
|
+
#
|
63
|
+
# If there is already an entry for the given object number (even if the generation number is
|
64
|
+
# different), this entry will be removed.
|
65
|
+
def []=(oid, gen, data)
|
66
|
+
@table[oid] = data
|
67
|
+
@oids[oid] = gen
|
68
|
+
@max_oid = oid if oid > @max_oid
|
69
|
+
end
|
70
|
+
|
71
|
+
# :call-seq:
|
72
|
+
# objhash[oid] -> data or nil
|
73
|
+
# objhash[oid, gen] -> data or nil
|
74
|
+
#
|
75
|
+
# Returns the data for the given object number, or for the given object and generation
|
76
|
+
# numbers.
|
77
|
+
#
|
78
|
+
# If there is no such data, +nil+ is returned.
|
79
|
+
def [](oid, gen = nil)
|
80
|
+
(gen.nil? || gen_for_oid(oid) == gen || nil) && @table[oid]
|
81
|
+
end
|
82
|
+
|
83
|
+
# :call-seq:
|
84
|
+
# objhash.gen_for_oid(oid) -> Integer or nil
|
85
|
+
#
|
86
|
+
# Returns the generation number that is stored along the given object number, or +nil+ if
|
87
|
+
# the object number is not used.
|
88
|
+
def gen_for_oid(oid)
|
89
|
+
@oids[oid]
|
90
|
+
end
|
91
|
+
|
92
|
+
# :call-seq:
|
93
|
+
# objhash.entry?(oid) -> true or false
|
94
|
+
# objhash.entry?(oid, gen) -> true or false
|
95
|
+
#
|
96
|
+
# Returns +true+ if there is an entry for the given object number, or for the given object
|
97
|
+
# and generation numbers.
|
98
|
+
def entry?(oid, gen = nil)
|
99
|
+
(gen ? gen_for_oid(oid) == gen : @oids.key?(oid))
|
100
|
+
end
|
101
|
+
|
102
|
+
# Deletes the entry for the given object number.
|
103
|
+
def delete(oid)
|
104
|
+
@table.delete(oid)
|
105
|
+
@oids.delete(oid)
|
106
|
+
@max_oid = oids.max || 0 if oid == @max_oid
|
107
|
+
end
|
108
|
+
|
109
|
+
# :call-seq:
|
110
|
+
# objhash.each {|oid, gen, data| block } -> objhash
|
111
|
+
# objhash.each -> Enumerator
|
112
|
+
#
|
113
|
+
# Calls the given block once for every entry, passing an array consisting of the object and
|
114
|
+
# generation number and the associated data as arguments.
|
115
|
+
def each
|
116
|
+
return to_enum(__method__) unless block_given?
|
117
|
+
@oids.keys.each {|oid| yield(oid, @oids[oid], @table[oid]) if @table.key?(oid)}
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns all used object numbers as an array.
|
122
|
+
def oids
|
123
|
+
@oids.keys
|
124
|
+
end
|
125
|
+
private :oids
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2016 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#++
|
33
|
+
|
34
|
+
module HexaPDF
|
35
|
+
module Utils
|
36
|
+
|
37
|
+
# Implements encoding conversion functions for the PDFDocEncoding.
|
38
|
+
#
|
39
|
+
# The PDFDocEncoding is used, together with UTF-16BE, for strings outside content streams.
|
40
|
+
# When a PDF file is loaded and a text string in a PDF object does not start with the UTF-16BE
|
41
|
+
# BOM U+FEFF, it is automatically converted to UTF-8 on access.
|
42
|
+
#
|
43
|
+
# The same is done for text strings in UTF-16BE encoding. Therefore all text strings can be
|
44
|
+
# assumed to be in UTF-8.
|
45
|
+
#
|
46
|
+
# When a PDF file is written, text strings are automatically encoded in either PDFDocEncoding
|
47
|
+
# or UTF-16BE depending on the characters in the text string.
|
48
|
+
#
|
49
|
+
# See: PDF1.7 s7.9.2, D.1, D.3
|
50
|
+
module PDFDocEncoding
|
51
|
+
|
52
|
+
# :nodoc:
|
53
|
+
CHARACTER_MAP = %W[\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
|
54
|
+
\uFFFD \u0009 \u000A \uFFFD \uFFFD \u000D \uFFFD \uFFFD
|
55
|
+
\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
|
56
|
+
\u02D8 \u02C7 \u02C6 \u02D9 \u02DD \u02DB \u02DA \u02DC
|
57
|
+
\u0020 \u0021 \u0022 \u0023 \u0024 \u0025 \u0026 \u0027
|
58
|
+
\u0028 \u0029 \u002a \u002b \u002c \u002d \u002e \u002f
|
59
|
+
\u0030 \u0031 \u0032 \u0033 \u0034 \u0035 \u0036 \u0037
|
60
|
+
\u0038 \u0039 \u003a \u003b \u003c \u003d \u003e \u003f
|
61
|
+
\u0040 \u0041 \u0042 \u0043 \u0044 \u0045 \u0046 \u0047
|
62
|
+
\u0048 \u0049 \u004a \u004b \u004c \u004d \u004e \u004f
|
63
|
+
\u0050 \u0051 \u0052 \u0053 \u0054 \u0055 \u0056 \u0057
|
64
|
+
\u0058 \u0059 \u005a \u005b \u005c \u005d \u005e \u005f
|
65
|
+
\u0060 \u0061 \u0062 \u0063 \u0064 \u0065 \u0066 \u0067
|
66
|
+
\u0068 \u0069 \u006a \u006b \u006c \u006d \u006e \u006f
|
67
|
+
\u0070 \u0071 \u0072 \u0073 \u0074 \u0075 \u0076 \u0077
|
68
|
+
\u0078 \u0079 \u007a \u007b \u007c \u007d \u007e \uFFFD
|
69
|
+
\u2022 \u2020 \u2021 \u2026 \u2014 \u2013 \u0192 \u2044
|
70
|
+
\u2039 \u203a \u2212 \u2030 \u201e \u201c \u201d \u2018
|
71
|
+
\u2019 \u201a \u2122 \ufb01 \ufb02 \u0141 \u0152 \u0160
|
72
|
+
\u0178 \u017d \u0131 \u0142 \u0153 \u0161 \u017e \uFFFD
|
73
|
+
\u20ac \u00a1 \u00a2 \u00a3 \u00a4 \u00a5 \u00a6 \u00a7
|
74
|
+
\u00a8 \u00a9 \u00aa \u00ab \u00ac \uFFFD \u00ae \u00af
|
75
|
+
\u00b0 \u00b1 \u00b2 \u00b3 \u00b4 \u00b5 \u00b6 \u00b7
|
76
|
+
\u00b8 \u00b9 \u00ba \u00bb \u00bc \u00bd \u00be \u00bf
|
77
|
+
\u00c0 \u00c1 \u00c2 \u00c3 \u00c4 \u00c5 \u00c6 \u00c7
|
78
|
+
\u00c8 \u00c9 \u00ca \u00cb \u00cc \u00cd \u00ce \u00cf
|
79
|
+
\u00d0 \u00d1 \u00d2 \u00d3 \u00d4 \u00d5 \u00d6 \u00d7
|
80
|
+
\u00d8 \u00d9 \u00da \u00db \u00dc \u00dd \u00de \u00df
|
81
|
+
\u00e0 \u00e1 \u00e2 \u00e3 \u00e4 \u00e5 \u00e6 \u00e7
|
82
|
+
\u00e8 \u00e9 \u00ea \u00eb \u00ec \u00ed \u00ee \u00ef
|
83
|
+
\u00f0 \u00f1 \u00f2 \u00f3 \u00f4 \u00f5 \u00f6 \u00f7
|
84
|
+
\u00f8 \u00f9 \u00fa \u00fb \u00fc \u00fd \u00fe \u00ff]
|
85
|
+
|
86
|
+
# Converts the given string to UTF-8, assuming it contains bytes in PDFDocEncoding.
|
87
|
+
def self.convert_to_utf8(str)
|
88
|
+
str.each_byte.with_object('') {|byte, result| result << CHARACTER_MAP[byte]}
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2016 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#++
|
33
|
+
|
34
|
+
module HexaPDF
|
35
|
+
module Utils
|
36
|
+
|
37
|
+
# Provides the convenience methods that are used for name trees and number trees.
|
38
|
+
#
|
39
|
+
# The provided methods require two methods defined in the including class so that they work
|
40
|
+
# correctly:
|
41
|
+
#
|
42
|
+
# leaf_node_container_name::
|
43
|
+
# Defines the dictionary entry name that contains the leaf node entries.
|
44
|
+
#
|
45
|
+
# For example, for name trees this would be :Names.
|
46
|
+
#
|
47
|
+
# key_type::
|
48
|
+
# Defines the class that is used for the keys in the tree.
|
49
|
+
#
|
50
|
+
# The class defined this way is used for making sure that only valid keys are used.
|
51
|
+
#
|
52
|
+
# For example, for name trees this would be String.
|
53
|
+
#
|
54
|
+
# See: HexaPDF::NameTreeNode, HexaPDF::NumberTreeNode
|
55
|
+
module SortedTreeNode
|
56
|
+
|
57
|
+
# Tree nodes must always be indirect.
|
58
|
+
#
|
59
|
+
# Note: There is no requirement that the root node of a tree must be indirect. However, making
|
60
|
+
# it indirect simplifies the implementation and is not against the spec.
|
61
|
+
def must_be_indirect?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
# :call-seq:
|
66
|
+
# tree.add_entry(key, data, overwrite: true) -> true or false
|
67
|
+
#
|
68
|
+
# Adds a new tree entry (key-data pair) to the sorted tree and returns +true+ if it was
|
69
|
+
# successfully added.
|
70
|
+
#
|
71
|
+
# If the option +overwrite+ is +true+, an existing entry is overwritten. Otherwise an error is
|
72
|
+
# raised.
|
73
|
+
#
|
74
|
+
# This method has to be invoked on the root node of the tree!
|
75
|
+
def add_entry(key, data, overwrite: true)
|
76
|
+
if key?(:Limits)
|
77
|
+
raise HexaPDF::Error, "Adding a new tree entry is only allowed via the root node"
|
78
|
+
elsif !key.kind_of?(key_type)
|
79
|
+
raise ArgumentError, "A key must be a #{key_type} object, not a #{key.class}"
|
80
|
+
end
|
81
|
+
|
82
|
+
container_name = leaf_node_container_name
|
83
|
+
|
84
|
+
if (!key?(:Kids) && !key?(container_name)) ||
|
85
|
+
(value[:Kids] && self[:Kids].empty?)
|
86
|
+
value.delete(:Kids)
|
87
|
+
value[container_name] = []
|
88
|
+
end
|
89
|
+
|
90
|
+
if key?(container_name)
|
91
|
+
result = insert_pair(self[container_name], key, data, overwrite: overwrite)
|
92
|
+
split_if_needed(self, self)
|
93
|
+
else
|
94
|
+
stack = []
|
95
|
+
path_to_key(self, key, stack)
|
96
|
+
|
97
|
+
result = insert_pair(stack.last[container_name], key, data, overwrite: overwrite)
|
98
|
+
stack.last[:Limits] = stack.last[container_name].values_at(0, -2)
|
99
|
+
stack.reverse_each.inject do |nested_node, node|
|
100
|
+
nested_lower = nested_node[:Limits][0]
|
101
|
+
nested_upper = nested_node[:Limits][1]
|
102
|
+
if node[:Limits][0] > nested_lower
|
103
|
+
node[:Limits][0] = nested_lower
|
104
|
+
elsif node[:Limits][1] < nested_upper
|
105
|
+
node[:Limits][1] = nested_upper
|
106
|
+
end
|
107
|
+
node
|
108
|
+
end
|
109
|
+
|
110
|
+
split_if_needed(stack[-2] || self, stack[-1])
|
111
|
+
end
|
112
|
+
|
113
|
+
result
|
114
|
+
end
|
115
|
+
|
116
|
+
# Deletes the entry specified by the +key+ from the tree and returns the data. If the tree
|
117
|
+
# doesn't contain the key, +nil+ is returned.
|
118
|
+
#
|
119
|
+
# This method has to be invoked on the root node of the tree!
|
120
|
+
def delete_entry(key)
|
121
|
+
if key?(:Limits)
|
122
|
+
raise HexaPDF::Error, "Deleting a tree entry is only allowed via the root node"
|
123
|
+
end
|
124
|
+
|
125
|
+
stack = [self]
|
126
|
+
path_to_key(self, key, stack)
|
127
|
+
container_name = leaf_node_container_name
|
128
|
+
|
129
|
+
return unless stack.last[container_name]
|
130
|
+
index = find_in_leaf_node(stack.last[container_name], key)
|
131
|
+
return unless stack.last[container_name][index] == key
|
132
|
+
|
133
|
+
value = stack.last[container_name].delete_at(index)
|
134
|
+
document.delete(value) if value.kind_of?(HexaPDF::Object)
|
135
|
+
value = stack.last[container_name].delete_at(index)
|
136
|
+
|
137
|
+
stack.last[:Limits] = stack.last[container_name].values_at(0, -2) if stack.last[:Limits]
|
138
|
+
|
139
|
+
stack.reverse_each.inject do |nested_node, node|
|
140
|
+
if (!nested_node[container_name] || nested_node[container_name].empty?) &&
|
141
|
+
(!nested_node[:Kids] || nested_node[:Kids].empty?)
|
142
|
+
node[:Kids].delete_at(node[:Kids].index {|n| document.deref(n) == nested_node})
|
143
|
+
document.delete(nested_node)
|
144
|
+
end
|
145
|
+
if node[:Kids].size > 0 && node[:Limits]
|
146
|
+
node[:Limits][0] = document.deref(node[:Kids][0])[:Limits][0]
|
147
|
+
node[:Limits][1] = document.deref(node[:Kids][-1])[:Limits][1]
|
148
|
+
end
|
149
|
+
node
|
150
|
+
end
|
151
|
+
|
152
|
+
value
|
153
|
+
end
|
154
|
+
|
155
|
+
# Finds and returns the associated entry for the key, or returns +nil+ if no such key is
|
156
|
+
# found.
|
157
|
+
def find_entry(key)
|
158
|
+
container_name = leaf_node_container_name
|
159
|
+
node = self
|
160
|
+
result = nil
|
161
|
+
|
162
|
+
while result.nil?
|
163
|
+
if node.key?(container_name)
|
164
|
+
index = find_in_leaf_node(node[container_name], key)
|
165
|
+
if node[container_name][index] == key
|
166
|
+
result = document.deref(node[container_name][index + 1])
|
167
|
+
end
|
168
|
+
elsif node.key?(:Kids)
|
169
|
+
index = find_in_intermediate_node(node[:Kids], key)
|
170
|
+
node = document.deref(node[:Kids][index])
|
171
|
+
break unless key >= node[:Limits][0] && key <= node[:Limits][1]
|
172
|
+
else
|
173
|
+
break
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
result
|
178
|
+
end
|
179
|
+
|
180
|
+
# :call-seq:
|
181
|
+
# node.each_entry {|key, data| block } -> node
|
182
|
+
# node.each_entry -> Enumerator
|
183
|
+
#
|
184
|
+
# Calls the given block once for each entry (key-data pair) of the sorted tree.
|
185
|
+
def each_entry
|
186
|
+
return to_enum(__method__) unless block_given?
|
187
|
+
|
188
|
+
container_name = leaf_node_container_name
|
189
|
+
stack = [self]
|
190
|
+
until stack.empty?
|
191
|
+
node = document.deref(stack.pop)
|
192
|
+
if node.key?(container_name)
|
193
|
+
data = node[container_name]
|
194
|
+
index = 0
|
195
|
+
while index < data.length
|
196
|
+
yield(data[index], document.deref(data[index + 1]))
|
197
|
+
index += 2
|
198
|
+
end
|
199
|
+
elsif node.key?(:Kids)
|
200
|
+
stack.concat(node[:Kids].reverse)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
self
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
# Starting from node traverses the tree to the node where the key is located or, if not
|
210
|
+
# present, where it would be located and adds the nodes to the stack.
|
211
|
+
def path_to_key(node, key, stack)
|
212
|
+
return unless node.key?(:Kids)
|
213
|
+
index = find_in_intermediate_node(node[:Kids], key)
|
214
|
+
stack << document.deref(node[:Kids][index])
|
215
|
+
path_to_key(stack.last, key, stack)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Returns the index into the /Kids array where the entry for +key+ is located or, if not
|
219
|
+
# present, where it would be located.
|
220
|
+
def find_in_intermediate_node(array, key)
|
221
|
+
left = 0
|
222
|
+
right = array.length - 1
|
223
|
+
while left < right
|
224
|
+
mid = (left + right) / 2
|
225
|
+
limits = document.deref(array[mid])[:Limits]
|
226
|
+
if limits[1] < key
|
227
|
+
left = mid + 1
|
228
|
+
elsif limits[0] > key
|
229
|
+
right = mid - 1
|
230
|
+
else
|
231
|
+
left = right = mid
|
232
|
+
end
|
233
|
+
end
|
234
|
+
left
|
235
|
+
end
|
236
|
+
|
237
|
+
# Inserts the key-data pair into array at the correct position and returns +true+ if the
|
238
|
+
# key-data pair was successfully inserted.
|
239
|
+
#
|
240
|
+
# An existing entry for the key is only overwritten if the option +overwrite+ is +true+.
|
241
|
+
def insert_pair(array, key, data, overwrite: true)
|
242
|
+
index = find_in_leaf_node(array, key)
|
243
|
+
return false if array[index] == key && !overwrite
|
244
|
+
|
245
|
+
if array[index] == key
|
246
|
+
array[index + 1] = data
|
247
|
+
else
|
248
|
+
array.insert(index, key, data)
|
249
|
+
end
|
250
|
+
|
251
|
+
true
|
252
|
+
end
|
253
|
+
|
254
|
+
# Returns the index into the array where the entry for +key+ is located or, if not present,
|
255
|
+
# where it would be located.
|
256
|
+
def find_in_leaf_node(array, key)
|
257
|
+
left = 0
|
258
|
+
right = array.length - 1
|
259
|
+
while left <= right
|
260
|
+
mid = ((left + right) / 2) & ~1 # mid must be even because of [key val key val...]
|
261
|
+
if array[mid] < key
|
262
|
+
left = mid + 2
|
263
|
+
elsif array[mid] > key
|
264
|
+
right = mid - 2
|
265
|
+
else
|
266
|
+
left = mid
|
267
|
+
right = left - 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
left
|
271
|
+
end
|
272
|
+
|
273
|
+
# Splits the leaf node if it contains the maximum number of entries.
|
274
|
+
def split_if_needed(parent, leaf_node)
|
275
|
+
container_name = leaf_node_container_name
|
276
|
+
max_size = config['sorted_tree.max_leaf_node_size'] * 2
|
277
|
+
return unless leaf_node[container_name].size >= max_size
|
278
|
+
|
279
|
+
split_point = (max_size / 2) & ~1
|
280
|
+
if parent == leaf_node
|
281
|
+
node1 = document.add(document.wrap({}, type: self.class))
|
282
|
+
node2 = document.add(document.wrap({}, type: self.class))
|
283
|
+
node1[container_name] = leaf_node[container_name][0, split_point]
|
284
|
+
node1[:Limits] = node1[container_name].values_at(0, -2)
|
285
|
+
node2[container_name] = leaf_node[container_name][split_point..-1]
|
286
|
+
node2[:Limits] = node2[container_name].values_at(0, -2)
|
287
|
+
parent.delete(container_name)
|
288
|
+
parent[:Kids] = [node1, node2]
|
289
|
+
else
|
290
|
+
node1 = document.add(document.wrap({}, type: self.class))
|
291
|
+
node1[container_name] = leaf_node[container_name].slice!(split_point..-1)
|
292
|
+
node1[:Limits] = node1[container_name].values_at(0, -2)
|
293
|
+
leaf_node[:Limits][1] = leaf_node[container_name][-2]
|
294
|
+
index = 1 + parent[:Kids].index {|o| document.deref(o) == leaf_node}
|
295
|
+
parent[:Kids].insert(index, node1)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Validates the sorted tree node.
|
300
|
+
def perform_validation
|
301
|
+
super
|
302
|
+
container_name = leaf_node_container_name
|
303
|
+
|
304
|
+
# All kids entries must be indirect objects
|
305
|
+
if key?(:Kids)
|
306
|
+
self[:Kids].each do |kid|
|
307
|
+
unless (kid.kind_of?(HexaPDF::Object) && kid.indirect?) ||
|
308
|
+
kid.kind_of?(HexaPDF::Reference)
|
309
|
+
yield("Child entries of sorted tree nodes must be indirect objects", false)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# All keys of the container must be lexically ordered strings and the container must be
|
315
|
+
# correctly formatted
|
316
|
+
if key?(container_name)
|
317
|
+
container = self[container_name]
|
318
|
+
if container.length.odd?
|
319
|
+
yield("Sorted tree leaf node contains odd number of entries", false)
|
320
|
+
end
|
321
|
+
index = 0
|
322
|
+
old = nil
|
323
|
+
while index < container.length
|
324
|
+
key = document.unwrap(container[index])
|
325
|
+
if !key.kind_of?(key_type)
|
326
|
+
yield("A key must be a #{key_type} object, not a #{key.class}", false)
|
327
|
+
elsif old && old > key
|
328
|
+
yield("Sorted tree leaf node entries are not correctly sorted", false)
|
329
|
+
end
|
330
|
+
old = key
|
331
|
+
index += 2
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2016 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#++
|
33
|
+
|
34
|
+
module HexaPDF
|
35
|
+
|
36
|
+
# The version of HexaPDF.
|
37
|
+
VERSION = '0.1.0'
|
38
|
+
|
39
|
+
end
|