hexapdf 0.46.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +342 -16
- data/LICENSE +1 -1
- data/README.md +9 -8
- data/examples/009-text_layouter_alignment.rb +4 -0
- data/examples/010-text_layouter_inline_boxes.rb +4 -0
- data/examples/011-text_layouter_line_wrapping.rb +4 -0
- data/examples/012-text_layouter_styling.rb +9 -4
- data/examples/013-text_layouter_shapes.rb +5 -0
- data/examples/015-boxes.rb +3 -0
- data/examples/016-frame_automatic_box_placement.rb +3 -0
- data/examples/017-frame_text_flow.rb +3 -0
- data/examples/022-outline.rb +5 -1
- data/examples/{028-frame_mask_mode.rb → 028-composer_mask_mode.rb} +3 -3
- data/lib/hexapdf/cli/batch.rb +1 -1
- data/lib/hexapdf/cli/command.rb +65 -65
- data/lib/hexapdf/cli/debug_info.rb +98 -0
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/fonts.rb +1 -1
- data/lib/hexapdf/cli/form.rb +11 -6
- data/lib/hexapdf/cli/image2pdf.rb +1 -1
- data/lib/hexapdf/cli/images.rb +19 -4
- data/lib/hexapdf/cli/info.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +24 -8
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +1 -2
- data/lib/hexapdf/cli/optimize.rb +6 -6
- data/lib/hexapdf/cli/split.rb +1 -1
- data/lib/hexapdf/cli/usage.rb +1 -1
- data/lib/hexapdf/cli/watermark.rb +1 -1
- data/lib/hexapdf/cli.rb +20 -2
- data/lib/hexapdf/composer.rb +22 -1
- data/lib/hexapdf/configuration.rb +56 -1
- data/lib/hexapdf/content/canvas.rb +1 -1
- data/lib/hexapdf/content/canvas_composer.rb +1 -1
- data/lib/hexapdf/content/color_space.rb +1 -1
- data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
- data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
- data/lib/hexapdf/content/graphic_object.rb +1 -1
- data/lib/hexapdf/content/graphics_state.rb +2 -2
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/content/parser.rb +22 -23
- data/lib/hexapdf/content/processor.rb +1 -1
- data/lib/hexapdf/content/transformation_matrix.rb +1 -1
- data/lib/hexapdf/content.rb +1 -1
- data/lib/hexapdf/data_dir.rb +1 -1
- data/lib/hexapdf/dictionary.rb +8 -2
- data/lib/hexapdf/dictionary_fields.rb +2 -2
- data/lib/hexapdf/digital_signature/cms_handler.rb +19 -2
- data/lib/hexapdf/digital_signature/handler.rb +1 -1
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +1 -1
- data/lib/hexapdf/digital_signature/signature.rb +2 -2
- data/lib/hexapdf/digital_signature/signatures.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +3 -3
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -2
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +25 -5
- data/lib/hexapdf/digital_signature/signing.rb +1 -1
- data/lib/hexapdf/digital_signature/verification_result.rb +1 -1
- data/lib/hexapdf/digital_signature.rb +1 -1
- data/lib/hexapdf/document/annotations.rb +220 -0
- data/lib/hexapdf/document/destinations.rb +1 -1
- data/lib/hexapdf/document/files.rb +1 -1
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/document/images.rb +1 -1
- data/lib/hexapdf/document/layout.rb +95 -16
- data/lib/hexapdf/document/metadata.rb +11 -4
- data/lib/hexapdf/document/pages.rb +1 -1
- data/lib/hexapdf/document.rb +52 -9
- data/lib/hexapdf/encryption/aes.rb +1 -1
- data/lib/hexapdf/encryption/arc4.rb +3 -3
- data/lib/hexapdf/encryption/fast_aes.rb +1 -1
- data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
- data/lib/hexapdf/encryption/identity.rb +1 -1
- data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
- data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +4 -2
- data/lib/hexapdf/encryption/standard_security_handler.rb +40 -29
- data/lib/hexapdf/encryption.rb +1 -1
- data/lib/hexapdf/error.rb +12 -4
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/filter/crypt.rb +1 -1
- data/lib/hexapdf/filter/encryption.rb +1 -1
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/pass_through.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +1 -1
- data/lib/hexapdf/filter/run_length_decode.rb +1 -1
- data/lib/hexapdf/filter.rb +1 -1
- data/lib/hexapdf/font/cmap/parser.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +59 -5
- data/lib/hexapdf/font/cmap.rb +18 -7
- data/lib/hexapdf/font/encoding/base.rb +28 -1
- data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
- data/lib/hexapdf/font/encoding.rb +1 -1
- data/lib/hexapdf/font/invalid_glyph.rb +1 -1
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/font.rb +1 -1
- data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
- data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
- data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
- data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
- data/lib/hexapdf/font/true_type/table/head.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
- data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
- data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
- data/lib/hexapdf/font/true_type/table/name.rb +1 -1
- data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +7 -2
- data/lib/hexapdf/font/true_type.rb +1 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +51 -16
- data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
- data/lib/hexapdf/font/type1.rb +1 -1
- data/lib/hexapdf/font/type1_wrapper.rb +3 -4
- data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
- data/lib/hexapdf/font_loader/from_file.rb +1 -1
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/font_loader/variant_from_name.rb +1 -1
- data/lib/hexapdf/font_loader.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/pdf.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +1 -1
- data/lib/hexapdf/image_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +2 -2
- data/lib/hexapdf/layout/box.rb +6 -1
- data/lib/hexapdf/layout/box_fitter.rb +1 -1
- data/lib/hexapdf/layout/column_box.rb +1 -1
- data/lib/hexapdf/layout/container_box.rb +64 -29
- data/lib/hexapdf/layout/frame.rb +1 -1
- data/lib/hexapdf/layout/image_box.rb +1 -1
- data/lib/hexapdf/layout/inline_box.rb +1 -1
- data/lib/hexapdf/layout/line.rb +1 -1
- data/lib/hexapdf/layout/list_box.rb +1 -1
- data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
- data/lib/hexapdf/layout/page_style.rb +1 -1
- data/lib/hexapdf/layout/style.rb +133 -22
- data/lib/hexapdf/layout/table_box.rb +86 -14
- data/lib/hexapdf/layout/text_box.rb +1 -1
- data/lib/hexapdf/layout/text_fragment.rb +13 -2
- data/lib/hexapdf/layout/text_layouter.rb +1 -1
- data/lib/hexapdf/layout/text_shaper.rb +1 -1
- data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
- data/lib/hexapdf/layout.rb +1 -1
- data/lib/hexapdf/name_tree_node.rb +1 -1
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/object.rb +4 -4
- data/lib/hexapdf/parser.rb +36 -7
- data/lib/hexapdf/pdf_array.rb +26 -4
- data/lib/hexapdf/rectangle.rb +1 -1
- data/lib/hexapdf/reference.rb +2 -2
- data/lib/hexapdf/revision.rb +7 -3
- data/lib/hexapdf/revisions.rb +1 -1
- data/lib/hexapdf/serializer.rb +8 -8
- data/lib/hexapdf/stream.rb +1 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/task/merge_acro_form.rb +164 -0
- data/lib/hexapdf/task/optimize.rb +5 -5
- data/lib/hexapdf/task/pdfa.rb +1 -1
- data/lib/hexapdf/task.rb +2 -1
- data/lib/hexapdf/test_utils.rb +3 -2
- data/lib/hexapdf/tokenizer.rb +52 -44
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +66 -13
- data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
- data/lib/hexapdf/type/acro_form/field.rb +6 -2
- data/lib/hexapdf/type/acro_form/form.rb +23 -32
- data/lib/hexapdf/type/acro_form/java_script_actions.rb +10 -3
- data/lib/hexapdf/type/acro_form/signature_field.rb +19 -8
- data/lib/hexapdf/type/acro_form/text_field.rb +10 -3
- data/lib/hexapdf/type/acro_form/variable_text_field.rb +13 -5
- data/lib/hexapdf/type/acro_form.rb +1 -1
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/actions/go_to.rb +2 -1
- data/lib/hexapdf/type/actions/go_to_r.rb +2 -1
- data/lib/hexapdf/type/actions/launch.rb +6 -2
- data/lib/hexapdf/type/actions/set_ocg_state.rb +1 -1
- data/lib/hexapdf/type/actions/uri.rb +1 -1
- data/lib/hexapdf/type/actions.rb +1 -1
- data/lib/hexapdf/type/annotation.rb +78 -3
- data/lib/hexapdf/type/annotations/appearance_generator.rb +426 -0
- data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
- data/lib/hexapdf/type/annotations/border_styling.rb +160 -0
- data/lib/hexapdf/type/annotations/circle.rb +65 -0
- data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
- data/lib/hexapdf/type/annotations/line.rb +334 -0
- data/lib/hexapdf/type/annotations/line_ending_styling.rb +208 -0
- data/lib/hexapdf/type/annotations/link.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +15 -3
- data/lib/hexapdf/type/annotations/polygon.rb +64 -0
- data/lib/hexapdf/type/annotations/polygon_polyline.rb +109 -0
- data/lib/hexapdf/type/annotations/polyline.rb +64 -0
- data/lib/hexapdf/type/annotations/square.rb +65 -0
- data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
- data/lib/hexapdf/type/annotations/text.rb +1 -1
- data/lib/hexapdf/type/annotations/widget.rb +56 -118
- data/lib/hexapdf/type/annotations.rb +13 -1
- data/lib/hexapdf/type/catalog.rb +5 -2
- data/lib/hexapdf/type/cid_font.rb +6 -3
- data/lib/hexapdf/type/cmap.rb +58 -0
- data/lib/hexapdf/type/embedded_file.rb +1 -1
- data/lib/hexapdf/type/file_specification.rb +18 -15
- data/lib/hexapdf/type/font.rb +1 -1
- data/lib/hexapdf/type/font_descriptor.rb +5 -4
- data/lib/hexapdf/type/font_simple.rb +4 -2
- data/lib/hexapdf/type/font_true_type.rb +3 -1
- data/lib/hexapdf/type/font_type0.rb +2 -2
- data/lib/hexapdf/type/font_type1.rb +19 -1
- data/lib/hexapdf/type/font_type3.rb +1 -2
- data/lib/hexapdf/type/form.rb +8 -5
- data/lib/hexapdf/type/graphics_state_parameter.rb +8 -5
- data/lib/hexapdf/type/icon_fit.rb +1 -1
- data/lib/hexapdf/type/image.rb +9 -5
- data/lib/hexapdf/type/info.rb +3 -3
- data/lib/hexapdf/type/mark_information.rb +3 -3
- data/lib/hexapdf/type/marked_content_reference.rb +59 -0
- data/lib/hexapdf/type/measure.rb +57 -0
- data/lib/hexapdf/type/metadata.rb +1 -1
- data/lib/hexapdf/type/names.rb +1 -1
- data/lib/hexapdf/type/namespace.rb +57 -0
- data/lib/hexapdf/type/object_reference.rb +57 -0
- data/lib/hexapdf/type/object_stream.rb +1 -1
- data/lib/hexapdf/type/optional_content_configuration.rb +2 -2
- data/lib/hexapdf/type/optional_content_group.rb +1 -1
- data/lib/hexapdf/type/optional_content_membership.rb +2 -2
- data/lib/hexapdf/type/optional_content_properties.rb +1 -1
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/output_intent.rb +1 -1
- data/lib/hexapdf/type/page.rb +6 -4
- data/lib/hexapdf/type/page_label.rb +1 -1
- data/lib/hexapdf/type/page_tree_node.rb +1 -1
- data/lib/hexapdf/type/resources.rb +13 -9
- data/lib/hexapdf/type/struct_elem.rb +72 -0
- data/lib/hexapdf/type/struct_tree_root.rb +64 -0
- data/lib/hexapdf/type/trailer.rb +1 -1
- data/lib/hexapdf/type/viewer_preferences.rb +5 -4
- data/lib/hexapdf/type/xref_stream.rb +1 -1
- data/lib/hexapdf/type.rb +8 -1
- data/lib/hexapdf/utils/bit_field.rb +1 -1
- data/lib/hexapdf/utils/bit_stream.rb +1 -1
- data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
- data/lib/hexapdf/utils/lru_cache.rb +1 -1
- data/lib/hexapdf/utils/math_helpers.rb +1 -1
- data/lib/hexapdf/utils/object_hash.rb +1 -1
- data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
- data/lib/hexapdf/utils/sorted_tree_node.rb +17 -4
- data/lib/hexapdf/utils.rb +1 -1
- data/lib/hexapdf/version.rb +2 -2
- data/lib/hexapdf/writer.rb +3 -2
- data/lib/hexapdf/xref_section.rb +25 -6
- data/lib/hexapdf.rb +1 -1
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5-R5.pdf +44 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5-R5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5-R5.pdf +0 -0
- data/test/hexapdf/common_tokenizer_tests.rb +7 -7
- data/test/hexapdf/content/test_graphics_state.rb +2 -3
- data/test/hexapdf/content/test_operator.rb +4 -5
- data/test/hexapdf/digital_signature/common.rb +6 -1
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +6 -1
- data/test/hexapdf/digital_signature/signing/test_timestamp_handler.rb +12 -0
- data/test/hexapdf/digital_signature/test_cms_handler.rb +25 -15
- data/test/hexapdf/digital_signature/test_handler.rb +2 -3
- data/test/hexapdf/digital_signature/test_pkcs1_handler.rb +1 -2
- data/test/hexapdf/digital_signature/test_signature.rb +7 -0
- data/test/hexapdf/digital_signature/test_signatures.rb +12 -7
- data/test/hexapdf/document/test_annotations.rb +75 -0
- data/test/hexapdf/document/test_layout.rb +38 -10
- data/test/hexapdf/document/test_metadata.rb +13 -1
- data/test/hexapdf/encryption/common.rb +1 -1
- data/test/hexapdf/encryption/test_aes.rb +1 -1
- data/test/hexapdf/encryption/test_arc4.rb +2 -2
- data/test/hexapdf/encryption/test_security_handler.rb +8 -6
- data/test/hexapdf/encryption/test_standard_security_handler.rb +7 -3
- data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
- data/test/hexapdf/filter/test_flate_decode.rb +2 -3
- data/test/hexapdf/font/cmap/test_writer.rb +73 -16
- data/test/hexapdf/font/encoding/test_base.rb +20 -0
- data/test/hexapdf/font/encoding/test_glyph_list.rb +1 -1
- data/test/hexapdf/font/test_true_type_wrapper.rb +31 -5
- data/test/hexapdf/font/test_type1_wrapper.rb +8 -1
- data/test/hexapdf/font/true_type/test_table.rb +12 -0
- data/test/hexapdf/layout/test_box.rb +8 -2
- data/test/hexapdf/layout/test_container_box.rb +34 -6
- data/test/hexapdf/layout/test_list_box.rb +7 -7
- data/test/hexapdf/layout/test_page_style.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +46 -12
- data/test/hexapdf/layout/test_table_box.rb +66 -16
- data/test/hexapdf/layout/test_text_box.rb +0 -6
- data/test/hexapdf/layout/test_text_fragment.rb +3 -3
- data/test/hexapdf/layout/test_text_layouter.rb +4 -2
- data/test/hexapdf/task/test_merge_acro_form.rb +104 -0
- data/test/hexapdf/task/test_optimize.rb +3 -1
- data/test/hexapdf/test_composer.rb +15 -0
- data/test/hexapdf/test_dictionary.rb +15 -0
- data/test/hexapdf/test_dictionary_fields.rb +1 -0
- data/test/hexapdf/test_document.rb +26 -8
- data/test/hexapdf/test_filter.rb +1 -1
- data/test/hexapdf/test_importer.rb +7 -0
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_parser.rb +87 -18
- data/test/hexapdf/test_pdf_array.rb +36 -3
- data/test/hexapdf/test_revision.rb +27 -6
- data/test/hexapdf/test_revisions.rb +1 -1
- data/test/hexapdf/test_serializer.rb +4 -4
- data/test/hexapdf/test_stream.rb +1 -2
- data/test/hexapdf/test_tokenizer.rb +1 -1
- data/test/hexapdf/test_writer.rb +22 -8
- data/test/hexapdf/test_xref_section.rb +15 -0
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +118 -26
- data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
- data/test/hexapdf/type/acro_form/test_field.rb +10 -0
- data/test/hexapdf/type/acro_form/test_form.rb +32 -9
- data/test/hexapdf/type/acro_form/test_java_script_actions.rb +21 -0
- data/test/hexapdf/type/acro_form/test_signature_field.rb +3 -1
- data/test/hexapdf/type/acro_form/test_text_field.rb +7 -1
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +14 -1
- data/test/hexapdf/type/actions/test_launch.rb +6 -2
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +608 -0
- data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
- data/test/hexapdf/type/annotations/test_border_styling.rb +114 -0
- data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
- data/test/hexapdf/type/annotations/test_line.rb +144 -0
- data/test/hexapdf/type/annotations/test_line_ending_styling.rb +42 -0
- data/test/hexapdf/type/annotations/test_polygon_polyline.rb +29 -0
- data/test/hexapdf/type/annotations/test_widget.rb +47 -81
- data/test/hexapdf/type/test_annotation.rb +58 -0
- data/test/hexapdf/type/test_font_type1.rb +20 -1
- data/test/hexapdf/type/test_form.rb +7 -1
- data/test/hexapdf/type/test_image.rb +1 -1
- data/test/hexapdf/type/test_page.rb +7 -1
- data/test/hexapdf/type/test_page_tree_node.rb +2 -2
- data/test/hexapdf/type/test_resources.rb +3 -1
- data/test/hexapdf/utils/test_sorted_tree_node.rb +18 -7
- data/test/test_helper.rb +7 -0
- metadata +69 -9
data/lib/hexapdf/cli/command.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
#++
|
|
36
36
|
|
|
37
37
|
require 'io/console'
|
|
38
|
-
require 'ostruct'
|
|
39
38
|
require 'cmdparse'
|
|
40
39
|
require 'hexapdf/document'
|
|
41
40
|
require 'hexapdf/font/true_type'
|
|
@@ -53,7 +52,7 @@ module HexaPDF
|
|
|
53
52
|
module Extensions #:nodoc:
|
|
54
53
|
def help_banner #:nodoc:
|
|
55
54
|
"hexapdf #{HexaPDF::VERSION} - Versatile PDF Manipulation Tool\n" \
|
|
56
|
-
"Copyright (c) 2014-
|
|
55
|
+
"Copyright (c) 2014-2025 Thomas Leitner; licensed under the AGPLv3\n\n" \
|
|
57
56
|
"#{format(usage, indent: 7)}\n\n"
|
|
58
57
|
end
|
|
59
58
|
|
|
@@ -68,21 +67,22 @@ module HexaPDF
|
|
|
68
67
|
|
|
69
68
|
def initialize(*args, **kwargs, &block) #:nodoc:
|
|
70
69
|
super
|
|
71
|
-
@out_options =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
70
|
+
@out_options = {
|
|
71
|
+
compact: true,
|
|
72
|
+
compress_pages: false,
|
|
73
|
+
object_streams: :preserve,
|
|
74
|
+
xref_streams: :preserve,
|
|
75
|
+
streams: :preserve,
|
|
76
|
+
optimize_fonts: false,
|
|
77
|
+
prune_page_resources: false,
|
|
78
|
+
encryption: :preserve,
|
|
79
|
+
enc_user_pwd: nil,
|
|
80
|
+
enc_owner_pwd: nil,
|
|
81
|
+
enc_key_length: 128,
|
|
82
|
+
enc_algorithm: :aes,
|
|
83
|
+
enc_force_v4: false,
|
|
84
|
+
enc_permissions: [],
|
|
85
|
+
}
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
protected
|
|
@@ -163,7 +163,7 @@ module HexaPDF
|
|
|
163
163
|
if command_parser.verbosity_info?
|
|
164
164
|
puts "Creating output document #{out_file}"
|
|
165
165
|
end
|
|
166
|
-
doc.write(out_file, validate: false, incremental: incremental)
|
|
166
|
+
doc.write(out_file, validate: false, compact: false, incremental: incremental)
|
|
167
167
|
end
|
|
168
168
|
end
|
|
169
169
|
|
|
@@ -183,35 +183,35 @@ module HexaPDF
|
|
|
183
183
|
options.separator("")
|
|
184
184
|
options.separator("Optimization options:")
|
|
185
185
|
options.on("--[no-]compact", "Delete unnecessary PDF objects (default: " \
|
|
186
|
-
"#{@out_options
|
|
187
|
-
@out_options
|
|
186
|
+
"#{@out_options[:compact]})") do |c|
|
|
187
|
+
@out_options[:compact] = c
|
|
188
188
|
end
|
|
189
189
|
options.on("--object-streams MODE", [:generate, :preserve, :delete],
|
|
190
190
|
"Handling of object streams (either generate, preserve or delete; " \
|
|
191
|
-
"default: #{@out_options
|
|
192
|
-
@out_options
|
|
191
|
+
"default: #{@out_options[:object_streams]})") do |os|
|
|
192
|
+
@out_options[:object_streams] = os
|
|
193
193
|
end
|
|
194
194
|
options.on("--xref-streams MODE", [:generate, :preserve, :delete],
|
|
195
195
|
"Handling of cross-reference streams (either generate, preserve or delete; " \
|
|
196
|
-
"default: #{@out_options
|
|
197
|
-
@out_options
|
|
196
|
+
"default: #{@out_options[:xref_streams]})") do |x|
|
|
197
|
+
@out_options[:xref_streams] = x
|
|
198
198
|
end
|
|
199
199
|
options.on("--streams MODE", [:compress, :preserve, :uncompress],
|
|
200
200
|
"Handling of stream data (either compress, preserve or uncompress; default: " \
|
|
201
|
-
"#{@out_options
|
|
202
|
-
@out_options
|
|
201
|
+
"#{@out_options[:streams]})") do |streams|
|
|
202
|
+
@out_options[:streams] = streams
|
|
203
203
|
end
|
|
204
204
|
options.on("--[no-]compress-pages", "Recompress page content streams (may take a long " \
|
|
205
|
-
"time; default: #{@out_options
|
|
206
|
-
@out_options
|
|
205
|
+
"time; default: #{@out_options[:compress_pages]})") do |c|
|
|
206
|
+
@out_options[:compress_pages] = c
|
|
207
207
|
end
|
|
208
208
|
options.on("--[no-]prune-page-resources", "Prunes unused objects from the page resources " \
|
|
209
|
-
"(may take a long time; default: #{@out_options
|
|
210
|
-
@out_options
|
|
209
|
+
"(may take a long time; default: #{@out_options[:prune_page_resources]})") do |c|
|
|
210
|
+
@out_options[:prune_page_resources] = c
|
|
211
211
|
end
|
|
212
212
|
options.on("--[no-]optimize-fonts", "Optimize embedded font files; " \
|
|
213
|
-
"default: #{@out_options
|
|
214
|
-
@out_options
|
|
213
|
+
"default: #{@out_options[:optimize_fonts]})") do |o|
|
|
214
|
+
@out_options[:optimize_fonts] = o
|
|
215
215
|
end
|
|
216
216
|
end
|
|
217
217
|
|
|
@@ -222,37 +222,37 @@ module HexaPDF
|
|
|
222
222
|
options.separator("")
|
|
223
223
|
options.separator("Encryption options:")
|
|
224
224
|
options.on("--decrypt", "Remove any encryption") do
|
|
225
|
-
@out_options
|
|
225
|
+
@out_options[:encryption] = :remove
|
|
226
226
|
end
|
|
227
227
|
options.on("--encrypt", "Encrypt the output file") do
|
|
228
|
-
@out_options
|
|
228
|
+
@out_options[:encryption] = :add
|
|
229
229
|
end
|
|
230
230
|
options.on("--owner-password PASSWORD", String, "The owner password to be set on the " \
|
|
231
231
|
"output file (use - for reading from standard input)") do |pwd|
|
|
232
|
-
@out_options
|
|
233
|
-
@out_options
|
|
232
|
+
@out_options[:encryption] = :add
|
|
233
|
+
@out_options[:enc_owner_pwd] = (pwd == '-' ? read_password("Owner password") : pwd)
|
|
234
234
|
end
|
|
235
235
|
options.on("--user-password PASSWORD", String, "The user password to be set on the " \
|
|
236
236
|
"output file (use - for reading from standard input)") do |pwd|
|
|
237
|
-
@out_options
|
|
238
|
-
@out_options
|
|
237
|
+
@out_options[:encryption] = :add
|
|
238
|
+
@out_options[:enc_user_pwd] = (pwd == '-' ? read_password("User password") : pwd)
|
|
239
239
|
end
|
|
240
240
|
options.on("--algorithm ALGORITHM", [:aes, :arc4],
|
|
241
241
|
"The encryption algorithm: aes or arc4 (default: " \
|
|
242
|
-
"#{@out_options
|
|
243
|
-
@out_options
|
|
244
|
-
@out_options
|
|
242
|
+
"#{@out_options[:enc_algorithm]})") do |a|
|
|
243
|
+
@out_options[:encryption] = :add
|
|
244
|
+
@out_options[:enc_algorithm] = a
|
|
245
245
|
end
|
|
246
246
|
options.on("--key-length BITS", Integer,
|
|
247
247
|
"The encryption key length in bits (default: " \
|
|
248
|
-
"#{@out_options
|
|
249
|
-
@out_options
|
|
250
|
-
@out_options
|
|
248
|
+
"#{@out_options[:enc_key_length]})") do |i|
|
|
249
|
+
@out_options[:encryption] = :add
|
|
250
|
+
@out_options[:enc_key_length] = i
|
|
251
251
|
end
|
|
252
252
|
options.on("--force-V4",
|
|
253
253
|
"Force use of encryption version 4 if key length=128 and algorithm=arc4") do
|
|
254
|
-
@out_options
|
|
255
|
-
@out_options
|
|
254
|
+
@out_options[:encryption] = :add
|
|
255
|
+
@out_options[:enc_force_v4] = true
|
|
256
256
|
end
|
|
257
257
|
syms = HexaPDF::Encryption::StandardSecurityHandler::Permissions::SYMBOL_TO_PERMISSION.keys
|
|
258
258
|
options.on("--permissions PERMS", Array,
|
|
@@ -264,8 +264,8 @@ module HexaPDF
|
|
|
264
264
|
end
|
|
265
265
|
perm.to_sym
|
|
266
266
|
end
|
|
267
|
-
@out_options
|
|
268
|
-
@out_options
|
|
267
|
+
@out_options[:encryption] = :add
|
|
268
|
+
@out_options[:enc_permissions] = perms
|
|
269
269
|
end
|
|
270
270
|
end
|
|
271
271
|
|
|
@@ -273,12 +273,12 @@ module HexaPDF
|
|
|
273
273
|
#
|
|
274
274
|
# See: #define_optimization_options
|
|
275
275
|
def apply_optimization_options(doc)
|
|
276
|
-
doc.task(:optimize, compact: @out_options
|
|
277
|
-
object_streams: @out_options
|
|
278
|
-
xref_streams: @out_options
|
|
279
|
-
compress_pages: @out_options
|
|
280
|
-
prune_page_resources: @out_options
|
|
281
|
-
if @out_options
|
|
276
|
+
doc.task(:optimize, compact: @out_options[:compact],
|
|
277
|
+
object_streams: @out_options[:object_streams],
|
|
278
|
+
xref_streams: @out_options[:xref_streams],
|
|
279
|
+
compress_pages: @out_options[:compress_pages],
|
|
280
|
+
prune_page_resources: @out_options[:prune_page_resources])
|
|
281
|
+
if @out_options[:streams] != :preserve || @out_options[:optimize_fonts]
|
|
282
282
|
doc.each do |obj|
|
|
283
283
|
optimize_stream(obj)
|
|
284
284
|
optimize_font(obj)
|
|
@@ -292,15 +292,15 @@ module HexaPDF
|
|
|
292
292
|
|
|
293
293
|
# Applies the chosen stream mode to the given object.
|
|
294
294
|
def optimize_stream(obj)
|
|
295
|
-
return if @out_options
|
|
295
|
+
return if @out_options[:streams] == :preserve || !obj.respond_to?(:set_filter) ||
|
|
296
296
|
Array(obj[:Filter]).any? {|f| IGNORED_FILTERS[f] }
|
|
297
297
|
|
|
298
|
-
obj.set_filter(@out_options
|
|
298
|
+
obj.set_filter(@out_options[:streams] == :compress ? :FlateDecode : nil)
|
|
299
299
|
end
|
|
300
300
|
|
|
301
301
|
# Optimize the object if it is a font object.
|
|
302
302
|
def optimize_font(obj)
|
|
303
|
-
return unless @out_options
|
|
303
|
+
return unless @out_options[:optimize_fonts] && obj.kind_of?(HexaPDF::Type::Font) &&
|
|
304
304
|
(obj[:Subtype] == :TrueType ||
|
|
305
305
|
(obj[:Subtype] == :Type0 && obj.descendant_font[:Subtype] == :CIDFontType2)) &&
|
|
306
306
|
obj.embedded?
|
|
@@ -319,14 +319,14 @@ module HexaPDF
|
|
|
319
319
|
#
|
|
320
320
|
# See: #define_encryption_options
|
|
321
321
|
def apply_encryption_options(doc)
|
|
322
|
-
case @out_options
|
|
322
|
+
case @out_options[:encryption]
|
|
323
323
|
when :add
|
|
324
|
-
doc.encrypt(algorithm: @out_options
|
|
325
|
-
key_length: @out_options
|
|
326
|
-
force_v4: @out_options
|
|
327
|
-
permissions: @out_options
|
|
328
|
-
owner_password: @out_options
|
|
329
|
-
user_password: @out_options
|
|
324
|
+
doc.encrypt(algorithm: @out_options[:enc_algorithm],
|
|
325
|
+
key_length: @out_options[:enc_key_length],
|
|
326
|
+
force_v4: @out_options[:enc_force_v4],
|
|
327
|
+
permissions: @out_options[:enc_permissions],
|
|
328
|
+
owner_password: @out_options[:enc_owner_pwd],
|
|
329
|
+
user_password: @out_options[:enc_user_pwd])
|
|
330
330
|
when :remove
|
|
331
331
|
doc.encrypt(name: nil)
|
|
332
332
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
|
2
|
+
#
|
|
3
|
+
#--
|
|
4
|
+
# This file is part of HexaPDF.
|
|
5
|
+
#
|
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
+
# Copyright (C) 2014-2025 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
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
|
35
|
+
#++
|
|
36
|
+
|
|
37
|
+
require 'hexapdf/cli/command'
|
|
38
|
+
|
|
39
|
+
module HexaPDF
|
|
40
|
+
module CLI
|
|
41
|
+
|
|
42
|
+
# Creates debugging information for adding to an issue.
|
|
43
|
+
class DebugInfo < Command
|
|
44
|
+
|
|
45
|
+
def initialize #:nodoc:
|
|
46
|
+
super('debug-info', takes_commands: false)
|
|
47
|
+
short_desc("Create debug information for a PDF file")
|
|
48
|
+
long_desc(<<~EOF)
|
|
49
|
+
Creates debug information for a possibly malformed PDF file that can be attached to an
|
|
50
|
+
issue.
|
|
51
|
+
|
|
52
|
+
Two files are created: anonymized-FILE where all strings are replaced with zeroes and
|
|
53
|
+
debug_info.txt with additional debug information.
|
|
54
|
+
EOF
|
|
55
|
+
|
|
56
|
+
options.on("--password PASSWORD", "-p", String,
|
|
57
|
+
"The password for decryption. Use - for reading from standard input.") do |pwd|
|
|
58
|
+
@password = (pwd == '-' ? read_password : pwd)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@password = nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def execute(file) #:nodoc:
|
|
65
|
+
output_name = "anonymized-#{file}"
|
|
66
|
+
puts "Creating anonymized file '#{output_name}'"
|
|
67
|
+
data = File.binread(file)
|
|
68
|
+
data.gsub!(/(>>\s*stream\s*)(.*?)(\s*endstream)/m) {|m| "#{$1}#{'0' * $2.length}#{$3}" }
|
|
69
|
+
data.gsub!(/([^<]<)([0-9A-Fa-f#{Tokenizer::WHITESPACE}]*?)>/m) {|m| "#{$1}#{'0' * $2.length}>" }
|
|
70
|
+
data.gsub!(/\((.*?)\)/m) {|m| "(#{'0' * $1.length})" }
|
|
71
|
+
File.binwrite(output_name, data)
|
|
72
|
+
|
|
73
|
+
debug_info = +''
|
|
74
|
+
puts "Collecting debug information in debug_info.txt"
|
|
75
|
+
begin
|
|
76
|
+
output = capture_output { HexaPDF::CLI::Application.new.parse(['info', '--check', file]) }
|
|
77
|
+
debug_info << "Output:\n"<< output
|
|
78
|
+
rescue
|
|
79
|
+
debug_info << "Error collecting info: #{$!.message}\n"
|
|
80
|
+
end
|
|
81
|
+
File.write('debug_info.txt', debug_info)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def capture_output
|
|
87
|
+
stdout, stderr = $stdout, $stderr
|
|
88
|
+
$stdout = $stderr = StringIO.new
|
|
89
|
+
yield
|
|
90
|
+
$stdout.string
|
|
91
|
+
ensure
|
|
92
|
+
$stdout, $stderr = stdout, stderr
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/hexapdf/cli/files.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/cli/fonts.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/cli/form.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -161,8 +161,8 @@ module HexaPDF
|
|
|
161
161
|
(field.alternate_field_name ? " (#{field.alternate_field_name})" : '')
|
|
162
162
|
concrete_field_type = field.concrete_field_type
|
|
163
163
|
nice_field_type = concrete_field_type.to_s.split('_').map(&:capitalize).join(' ')
|
|
164
|
-
size = "
|
|
165
|
-
position = "
|
|
164
|
+
size = "#{widget[:Rect].width.round(3)}x#{widget[:Rect].height.round(3)}"
|
|
165
|
+
position = "x=#{widget[:Rect].left}, y=#{widget[:Rect].bottom}"
|
|
166
166
|
field_value = if !field.field_value || concrete_field_type != :signature_field
|
|
167
167
|
field.field_value.inspect
|
|
168
168
|
else
|
|
@@ -172,10 +172,15 @@ module HexaPDF
|
|
|
172
172
|
temp
|
|
173
173
|
end
|
|
174
174
|
|
|
175
|
+
if concrete_field_type == :radio_button
|
|
176
|
+
rb_name = ((widget.appearance_dict&.normal_appearance&.value&.keys || []) - [:Off]).first
|
|
177
|
+
rb_name = " (#{rb_name.inspect})"
|
|
178
|
+
end
|
|
179
|
+
|
|
175
180
|
flags = field_flags(field)
|
|
176
|
-
puts " #{field_name}" << (flags.empty? ? '' : " (#{flags.join(', ')})")
|
|
181
|
+
puts " #{field_name}#{rb_name}" << (flags.empty? ? '' : " (#{flags.join(', ')})")
|
|
177
182
|
if command_parser.verbosity_info?
|
|
178
|
-
printf(" └─ %-22s | %-20s\n", nice_field_type, "#{
|
|
183
|
+
printf(" └─ %-22s | %-20s\n", nice_field_type, "#{position}, #{size} ")
|
|
179
184
|
end
|
|
180
185
|
puts " └─ #{field_value}"
|
|
181
186
|
if command_parser.verbosity_info?
|
|
@@ -285,7 +290,7 @@ module HexaPDF
|
|
|
285
290
|
page.each_annotation do |annotation|
|
|
286
291
|
next unless annotation[:Subtype] == :Widget
|
|
287
292
|
field = annotation.form_field
|
|
288
|
-
next if field.concrete_field_type == :push_button
|
|
293
|
+
next if !field.concrete_field_type || field.concrete_field_type == :push_button
|
|
289
294
|
if with_seen || !seen[field.full_field_name]
|
|
290
295
|
yield(page, page_index, field, annotation)
|
|
291
296
|
seen[field.full_field_name] = true
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/cli/images.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
#++
|
|
36
36
|
|
|
37
37
|
require 'set'
|
|
38
|
+
require 'fileutils'
|
|
38
39
|
require 'hexapdf/cli/command'
|
|
39
40
|
|
|
40
41
|
module HexaPDF
|
|
@@ -132,7 +133,7 @@ module HexaPDF
|
|
|
132
133
|
printf("%5s %5s %9s %6s %6s %5s %4s %3s %5s %5s %6s %5s %8s\n",
|
|
133
134
|
"index", "page", "oid", "width", "height", "color", "comp", "bpc",
|
|
134
135
|
"x-ppi", "y-ppi", "size", "type", "writable")
|
|
135
|
-
puts("-" *
|
|
136
|
+
puts("-" * 84)
|
|
136
137
|
each_image(doc) do |image, index, pindex, (x_ppi, y_ppi)|
|
|
137
138
|
info = image.info
|
|
138
139
|
size = human_readable_file_size(image[:Length] + image[:SMask]&.[](:Length).to_i)
|
|
@@ -145,20 +146,34 @@ module HexaPDF
|
|
|
145
146
|
|
|
146
147
|
# Extracts the images with the given indices.
|
|
147
148
|
def extract_images(doc)
|
|
149
|
+
FileUtils.mkdir_p(File.dirname("#{@prefix}filename"))
|
|
150
|
+
prefix = File.directory?(@prefix) ? @prefix : "#{@prefix}-"
|
|
151
|
+
|
|
148
152
|
done = Set.new
|
|
153
|
+
count = total = 0
|
|
149
154
|
each_image(doc) do |image, index, _|
|
|
150
155
|
next unless (@indices.include?(index) || @indices.include?(0)) && !done.include?(index)
|
|
156
|
+
total += 1
|
|
151
157
|
info = image.info
|
|
152
158
|
if info.writable
|
|
153
|
-
|
|
159
|
+
count += 1
|
|
160
|
+
path = "#{prefix}#{index}.#{image.info.extension}"
|
|
154
161
|
maybe_raise_on_existing_file(path)
|
|
155
|
-
|
|
162
|
+
if command_parser.verbosity_info?
|
|
163
|
+
puts "Extracting image #{index} (#{image.width}x#{image.height}, " \
|
|
164
|
+
"#{info.color_space}, #{info.type}) to #{path}..."
|
|
165
|
+
end
|
|
156
166
|
image.write(path)
|
|
157
167
|
done << index
|
|
168
|
+
if info.color_space == :cmyk && info.type == :jpeg
|
|
169
|
+
$stderr.puts "Note (image #{path}): JPEG uses CMYK colorspace and may " \
|
|
170
|
+
"need color post-processing"
|
|
171
|
+
end
|
|
158
172
|
elsif command_parser.verbosity_warning?
|
|
159
173
|
$stderr.puts "Warning (image #{index}): PDF image format not supported for writing"
|
|
160
174
|
end
|
|
161
175
|
end
|
|
176
|
+
puts "Created #{count} image files (out of #{total} selected)" if command_parser.verbosity_info?
|
|
162
177
|
end
|
|
163
178
|
|
|
164
179
|
# Iterates over all images.
|
data/lib/hexapdf/cli/info.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/cli/inspect.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -188,12 +188,20 @@ module HexaPDF
|
|
|
188
188
|
end
|
|
189
189
|
serialize(obj.value, recursive: true) if obj
|
|
190
190
|
|
|
191
|
-
when 's', 'stream', 'raw', 'raw-stream'
|
|
191
|
+
when 's', 'stream', 'raw', 'raw-stream', 'sd'
|
|
192
192
|
if (obj = pdf_object_from_string_reference(data.shift) rescue $stderr.puts($!.message)) &&
|
|
193
193
|
obj.kind_of?(HexaPDF::Stream)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
if command == 'sd'
|
|
195
|
+
if obj.respond_to?(:process_contents)
|
|
196
|
+
obj.process_contents(ContentProcessor.new)
|
|
197
|
+
else
|
|
198
|
+
$stderr.puts("Error: The object is not a Form XObject or page")
|
|
199
|
+
end
|
|
200
|
+
else
|
|
201
|
+
source = (command.start_with?('raw') ? obj.stream_source : obj.stream_decoder)
|
|
202
|
+
while source.alive? && (stream_data = source.resume)
|
|
203
|
+
$stdout.write(stream_data)
|
|
204
|
+
end
|
|
197
205
|
end
|
|
198
206
|
elsif command_parser.verbosity_info?
|
|
199
207
|
$stderr.puts("Note: Object has no stream data")
|
|
@@ -285,6 +293,10 @@ module HexaPDF
|
|
|
285
293
|
IO.copy_stream(@doc.revisions.parser.io, $stdout, length, 0)
|
|
286
294
|
else
|
|
287
295
|
puts "Document has #{@doc.revisions.count} revision#{@doc.revisions.count == 1 ? '' : 's'}"
|
|
296
|
+
if @doc.revisions.parser.reconstructed? && @doc.revisions.count == 1 &&
|
|
297
|
+
@doc.revisions.current == @doc.revisions.parser.reconstructed_revision
|
|
298
|
+
puts "Document cross-reference table has been reconstructed"
|
|
299
|
+
end
|
|
288
300
|
revision_information do |rev, index, count, signature, end_offset|
|
|
289
301
|
type = if rev.trailer[:XRefStm]
|
|
290
302
|
"xref table + stream"
|
|
@@ -395,16 +407,19 @@ module HexaPDF
|
|
|
395
407
|
end
|
|
396
408
|
io = @doc.revisions.parser.io
|
|
397
409
|
|
|
398
|
-
startxrefs = @doc.revisions.map {|rev| rev.trailer[:Prev] }
|
|
399
410
|
io.seek(0, IO::SEEK_END)
|
|
400
|
-
startxrefs
|
|
411
|
+
startxrefs = @doc.revisions.map {|rev| rev.trailer[:Prev].to_i } <<
|
|
412
|
+
@doc.revisions.parser.startxref_offset <<
|
|
413
|
+
io.pos
|
|
414
|
+
startxrefs.sort!
|
|
415
|
+
startxrefs.shift
|
|
401
416
|
|
|
402
417
|
@doc.revisions.each_with_index.map do |rev, index|
|
|
403
418
|
end_index = 0
|
|
404
419
|
sig = signatures[rev]
|
|
405
420
|
if sig
|
|
406
421
|
end_index = sig[:ByteRange][-2] + sig[:ByteRange][-1]
|
|
407
|
-
|
|
422
|
+
elsif rev != @doc.revisions.parser.reconstructed_revision
|
|
408
423
|
io.seek(startxrefs[index], IO::SEEK_SET)
|
|
409
424
|
buffer = ''.b
|
|
410
425
|
while io.pos < startxrefs[index + 1]
|
|
@@ -424,6 +439,7 @@ module HexaPDF
|
|
|
424
439
|
["OID[,GEN] | o[bject] OID[,GEN]", "Print object"],
|
|
425
440
|
["r[ecursive] OID[,GEN]", "Print object recursively"],
|
|
426
441
|
["s[tream] OID[,GEN]", "Print filtered stream"],
|
|
442
|
+
["sd OID[,GEN]", "Print the decoded stream of a Form XObject or page"],
|
|
427
443
|
["raw[-stream] OID[,GEN]", "Print raw stream"],
|
|
428
444
|
["rev[ision] [NUMBER]", "Print or extract revision"],
|
|
429
445
|
["x[ref] OID[,GEN]", "Print the cross-reference entry"],
|
data/lib/hexapdf/cli/merge.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/cli/modify.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
|
35
35
|
#++
|
|
36
36
|
|
|
37
|
-
require 'ostruct'
|
|
38
37
|
require 'hexapdf/cli/command'
|
|
39
38
|
|
|
40
39
|
module HexaPDF
|
data/lib/hexapdf/cli/optimize.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -53,11 +53,11 @@ module HexaPDF
|
|
|
53
53
|
EOF
|
|
54
54
|
|
|
55
55
|
@password = nil
|
|
56
|
-
@out_options
|
|
57
|
-
@out_options
|
|
58
|
-
@out_options
|
|
59
|
-
@out_options
|
|
60
|
-
@out_options
|
|
56
|
+
@out_options[:compact] = true
|
|
57
|
+
@out_options[:xref_streams] = :generate
|
|
58
|
+
@out_options[:object_streams] = :generate
|
|
59
|
+
@out_options[:streams] = :compress
|
|
60
|
+
@out_options[:optimize_fonts] = true
|
|
61
61
|
|
|
62
62
|
options.on("--password PASSWORD", "-p", String,
|
|
63
63
|
"The password for decryption. Use - for reading from standard input.") do |pwd|
|
data/lib/hexapdf/cli/split.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
data/lib/hexapdf/cli/usage.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# This file is part of HexaPDF.
|
|
5
5
|
#
|
|
6
6
|
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
|
7
|
-
# Copyright (C) 2014-
|
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
|
8
8
|
#
|
|
9
9
|
# HexaPDF is free software: you can redistribute it and/or modify it
|
|
10
10
|
# under the terms of the GNU Affero General Public License version 3 as
|