pdfrb 0.3.0 → 0.7.1
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/.rubocop.yml +1 -1
- data/CHANGELOG.md +51 -0
- data/Gemfile +1 -0
- data/PROPOSAL.canvas-clip.md +92 -0
- data/PROPOSAL.canvas-draw-image-matrix.md +87 -0
- data/PROPOSAL.canvas-rich-text.md +103 -0
- data/PROPOSAL.canvas-text-lines.md +77 -0
- data/PROPOSAL.canvas-transparency.md +106 -0
- data/PROPOSAL.external-font-metrics.md +129 -0
- data/PROPOSAL.font-embedding-fontfile2.md +88 -0
- data/PROPOSAL.font-resolver-ttc.md +105 -0
- data/PROPOSAL.font-resolver.md +96 -0
- data/PROPOSAL.font-subsetting.md +98 -0
- data/PROPOSAL.gradient-shadings.md +110 -0
- data/PROPOSAL.page-mediabox-setter.md +66 -0
- data/PROPOSAL.per-glyph-width.md +91 -0
- data/PROPOSAL.stringio-require.md +53 -0
- data/PROPOSAL.struct-tree-root.md +102 -0
- data/PROPOSAL.text-measurement.md +93 -0
- data/PROPOSAL.ttf-table-parsing-fix.md +112 -0
- data/RELEASE.md +61 -0
- data/data/pdfrb/afm/Courier-Bold.afm +342 -342
- data/data/pdfrb/afm/Courier-BoldOblique.afm +342 -342
- data/data/pdfrb/afm/Courier-Oblique.afm +342 -342
- data/data/pdfrb/afm/Courier.afm +342 -342
- data/data/pdfrb/afm/Helvetica-Bold.afm +2827 -2827
- data/data/pdfrb/afm/Helvetica-BoldOblique.afm +2827 -2827
- data/data/pdfrb/afm/Helvetica-Oblique.afm +3051 -3051
- data/data/pdfrb/afm/Helvetica.afm +3051 -3051
- data/data/pdfrb/afm/Symbol.afm +213 -213
- data/data/pdfrb/afm/Times-Bold.afm +2588 -2588
- data/data/pdfrb/afm/Times-BoldItalic.afm +2384 -2384
- data/data/pdfrb/afm/Times-Italic.afm +2667 -2667
- data/data/pdfrb/afm/Times-Roman.afm +2419 -2419
- data/data/pdfrb/afm/ZapfDingbats.afm +225 -225
- data/lib/pdfrb/action/base.rb +21 -0
- data/lib/pdfrb/action/types.rb +178 -0
- data/lib/pdfrb/action.rb +55 -0
- data/lib/pdfrb/annotation/base.rb +79 -0
- data/lib/pdfrb/annotation/types.rb +222 -0
- data/lib/pdfrb/annotation.rb +78 -0
- data/lib/pdfrb/appearance/generator.rb +216 -0
- data/lib/pdfrb/appearance.rb +11 -0
- data/lib/pdfrb/arlington/object_definition.rb +2 -2
- data/lib/pdfrb/arlington/predicate/evaluator.rb +1 -1
- data/lib/pdfrb/arlington/predicate/functions.rb +25 -9
- data/lib/pdfrb/arlington/predicate.rb +1 -5
- data/lib/pdfrb/cli/batch.rb +18 -0
- data/lib/pdfrb/cli/debug_info.rb +18 -0
- data/lib/pdfrb/cli/files.rb +16 -0
- data/lib/pdfrb/cli/fonts.rb +16 -0
- data/lib/pdfrb/cli/form.rb +18 -0
- data/lib/pdfrb/cli/image2pdf.rb +18 -0
- data/lib/pdfrb/cli/images.rb +34 -0
- data/lib/pdfrb/cli/info.rb +20 -0
- data/lib/pdfrb/cli/inspect.rb +18 -0
- data/lib/pdfrb/cli/merge.rb +18 -0
- data/lib/pdfrb/cli/modify.rb +18 -0
- data/lib/pdfrb/cli/optimize.rb +18 -0
- data/lib/pdfrb/cli/split.rb +21 -0
- data/lib/pdfrb/cli/watermark.rb +18 -0
- data/lib/pdfrb/cli.rb +291 -7
- data/lib/pdfrb/color/color_space.rb +259 -0
- data/lib/pdfrb/color/icc_profile.rb +90 -0
- data/lib/pdfrb/color.rb +12 -0
- data/lib/pdfrb/compare/comparator.rb +244 -0
- data/lib/pdfrb/compare/report.rb +78 -0
- data/lib/pdfrb/compare.rb +42 -0
- data/lib/pdfrb/composer.rb +161 -0
- data/lib/pdfrb/conformance/pdf_a.rb +317 -56
- data/lib/pdfrb/conformance/pdf_ua.rb +186 -29
- data/lib/pdfrb/conformance/pdf_x.rb +247 -0
- data/lib/pdfrb/conformance/rule.rb +78 -0
- data/lib/pdfrb/conformance/structure_elements.rb +174 -0
- data/lib/pdfrb/conformance/verapdf_bridge.rb +92 -0
- data/lib/pdfrb/conformance.rb +7 -3
- data/lib/pdfrb/content/canvas.rb +354 -72
- data/lib/pdfrb/content/color_space.rb +157 -0
- data/lib/pdfrb/content/hidden_text_detector.rb +167 -0
- data/lib/pdfrb/content/operator.rb +21 -1
- data/lib/pdfrb/content/operators/clipping.rb +36 -0
- data/lib/pdfrb/content/operators/graphics_state_params.rb +15 -0
- data/lib/pdfrb/content/operators/inline_image.rb +53 -0
- data/lib/pdfrb/content/operators.rb +22 -0
- data/lib/pdfrb/content/shading/base.rb +139 -0
- data/lib/pdfrb/content/shading.rb +17 -0
- data/lib/pdfrb/content/smart_text_extractor.rb +85 -0
- data/lib/pdfrb/content/tiling_pattern.rb +78 -0
- data/lib/pdfrb/content/transformation_matrix.rb +61 -0
- data/lib/pdfrb/content.rb +7 -26
- data/lib/pdfrb/destination/fit.rb +185 -0
- data/lib/pdfrb/destination.rb +13 -0
- data/lib/pdfrb/digital_signature/cms_handler.rb +22 -0
- data/lib/pdfrb/digital_signature/signing.rb +107 -11
- data/lib/pdfrb/digital_signature/timestamp_handler.rb +45 -0
- data/lib/pdfrb/digital_signature/verification.rb +88 -9
- data/lib/pdfrb/digital_signature/verification_result.rb +14 -0
- data/lib/pdfrb/digital_signature.rb +26 -2
- data/lib/pdfrb/document/annotations.rb +120 -3
- data/lib/pdfrb/document/associated_files.rb +104 -0
- data/lib/pdfrb/document/colors.rb +95 -0
- data/lib/pdfrb/document/destinations.rb +127 -5
- data/lib/pdfrb/document/display.rb +84 -0
- data/lib/pdfrb/document/fonts.rb +417 -45
- data/lib/pdfrb/document/form.rb +338 -0
- data/lib/pdfrb/document/graphics_state.rb +91 -0
- data/lib/pdfrb/document/images.rb +4 -4
- data/lib/pdfrb/document/info.rb +102 -0
- data/lib/pdfrb/document/layers.rb +62 -0
- data/lib/pdfrb/document/metadata.rb +12 -5
- data/lib/pdfrb/document/output_intents.rb +91 -0
- data/lib/pdfrb/document/page_labels.rb +80 -0
- data/lib/pdfrb/document/pages.rb +68 -19
- data/lib/pdfrb/document/portfolio.rb +119 -0
- data/lib/pdfrb/document/shadings.rb +159 -0
- data/lib/pdfrb/document/stamps.rb +68 -0
- data/lib/pdfrb/document/structure.rb +242 -0
- data/lib/pdfrb/document.rb +73 -4
- data/lib/pdfrb/encryption/identity.rb +13 -0
- data/lib/pdfrb/encryption/password_verification.rb +53 -8
- data/lib/pdfrb/encryption/security_handler.rb +8 -5
- data/lib/pdfrb/encryption/standard_security_handler.rb +154 -81
- data/lib/pdfrb/encryption.rb +17 -0
- data/lib/pdfrb/error.rb +2 -2
- data/lib/pdfrb/filter/brotli_decode.rb +32 -0
- data/lib/pdfrb/filter/ccitt_fax_decode.rb +12 -0
- data/lib/pdfrb/filter/crypt.rb +2 -2
- data/lib/pdfrb/filter/dct_decode.rb +18 -0
- data/lib/pdfrb/filter/flate_decode.rb +4 -18
- data/lib/pdfrb/filter/jbig2_decode.rb +12 -0
- data/lib/pdfrb/filter/jpx_decode.rb +18 -0
- data/lib/pdfrb/filter/lzw_decode.rb +3 -2
- data/lib/pdfrb/filter/png_predictor.rb +94 -0
- data/lib/pdfrb/filter.rb +26 -13
- data/lib/pdfrb/font/afm_parser.rb +50 -26
- data/lib/pdfrb/font/cmap/writer.rb +9 -0
- data/lib/pdfrb/font/encoding/win_ansi_encoding.rb +17 -0
- data/lib/pdfrb/font/encoding.rb +32 -3
- data/lib/pdfrb/font/metrics_helper.rb +121 -0
- data/lib/pdfrb/font/true_type/file.rb +10 -1
- data/lib/pdfrb/font/true_type/glyf.rb +82 -0
- data/lib/pdfrb/font/true_type/kern.rb +58 -0
- data/lib/pdfrb/font/true_type/loca.rb +42 -0
- data/lib/pdfrb/font/true_type/maxp.rb +44 -0
- data/lib/pdfrb/font/true_type/name.rb +53 -0
- data/lib/pdfrb/font/true_type/post.rb +93 -0
- data/lib/pdfrb/font/true_type/subsetter.rb +3 -5
- data/lib/pdfrb/font/true_type/wrapper.rb +102 -0
- data/lib/pdfrb/font/true_type.rb +7 -0
- data/lib/pdfrb/font.rb +2 -0
- data/lib/pdfrb/font_loader/from_configuration.rb +19 -0
- data/lib/pdfrb/font_loader/from_file.rb +20 -0
- data/lib/pdfrb/font_loader/standard14.rb +20 -0
- data/lib/pdfrb/font_loader/variant_from_name.rb +36 -0
- data/lib/pdfrb/font_loader.rb +4 -0
- data/lib/pdfrb/font_resolver.rb +138 -0
- data/lib/pdfrb/image/audit.rb +94 -0
- data/lib/pdfrb/image/downsampler.rb +124 -0
- data/lib/pdfrb/image.rb +13 -0
- data/lib/pdfrb/image_loader/jpeg.rb +39 -58
- data/lib/pdfrb/image_loader/png.rb +82 -251
- data/lib/pdfrb/layout/bidi.rb +198 -0
- data/lib/pdfrb/layout/box.rb +49 -0
- data/lib/pdfrb/layout/box_fitter.rb +54 -0
- data/lib/pdfrb/layout/column_box.rb +71 -0
- data/lib/pdfrb/layout/container_box.rb +45 -0
- data/lib/pdfrb/layout/frame.rb +67 -0
- data/lib/pdfrb/layout/image_box.rb +44 -0
- data/lib/pdfrb/layout/inline_box.rb +25 -0
- data/lib/pdfrb/layout/line.rb +55 -0
- data/lib/pdfrb/layout/list_box.rb +87 -0
- data/lib/pdfrb/layout/numeric_refinements.rb +43 -0
- data/lib/pdfrb/layout/page_style.rb +65 -0
- data/lib/pdfrb/layout/style.rb +62 -0
- data/lib/pdfrb/layout/table_box.rb +66 -0
- data/lib/pdfrb/layout/text_box.rb +38 -0
- data/lib/pdfrb/layout/text_fragment.rb +45 -0
- data/lib/pdfrb/layout/text_layouter.rb +123 -0
- data/lib/pdfrb/layout.rb +28 -0
- data/lib/pdfrb/linearization/hint_stream.rb +93 -0
- data/lib/pdfrb/linearization/writer.rb +259 -0
- data/lib/pdfrb/linearization.rb +21 -0
- data/lib/pdfrb/model/cos/dictionary.rb +12 -0
- data/lib/pdfrb/model/pdf_array.rb +2 -2
- data/lib/pdfrb/model/type/action.rb +42 -0
- data/lib/pdfrb/model/type/action_go_to_3d_view.rb +14 -0
- data/lib/pdfrb/model/type/action_go_to_dp.rb +16 -0
- data/lib/pdfrb/model/type/action_go_to_r.rb +26 -0
- data/lib/pdfrb/model/type/action_goto.rb +34 -0
- data/lib/pdfrb/model/type/action_hide.rb +26 -0
- data/lib/pdfrb/model/type/action_import_data.rb +18 -0
- data/lib/pdfrb/model/type/action_java_script.rb +18 -0
- data/lib/pdfrb/model/type/action_launch.rb +22 -0
- data/lib/pdfrb/model/type/action_movie.rb +22 -0
- data/lib/pdfrb/model/type/action_named.rb +23 -0
- data/lib/pdfrb/model/type/action_nop.rb +12 -0
- data/lib/pdfrb/model/type/action_rendition.rb +28 -0
- data/lib/pdfrb/model/type/action_reset_form.rb +22 -0
- data/lib/pdfrb/model/type/action_rich_media_execute.rb +17 -0
- data/lib/pdfrb/model/type/action_set_ocg_state.rb +32 -0
- data/lib/pdfrb/model/type/action_set_state.rb +14 -0
- data/lib/pdfrb/model/type/action_sound_action.rb +23 -0
- data/lib/pdfrb/model/type/action_submit_form.rb +64 -0
- data/lib/pdfrb/model/type/action_thread.rb +23 -0
- data/lib/pdfrb/model/type/action_trans.rb +30 -0
- data/lib/pdfrb/model/type/action_uri.rb +30 -0
- data/lib/pdfrb/model/type/af_embedded_file.rb +11 -0
- data/lib/pdfrb/model/type/af_file_specification.rb +34 -0
- data/lib/pdfrb/model/type/annotation.rb +49 -10
- data/lib/pdfrb/model/type/appearance.rb +41 -0
- data/lib/pdfrb/model/type/appearance_characteristics.rb +36 -0
- data/lib/pdfrb/model/type/appearance_generator.rb +22 -0
- data/lib/pdfrb/model/type/appearance_trap_net.rb +84 -0
- data/lib/pdfrb/model/type/border_effect.rb +25 -0
- data/lib/pdfrb/model/type/border_styling.rb +38 -0
- data/lib/pdfrb/model/type/box_color_info.rb +21 -0
- data/lib/pdfrb/model/type/box_style.rb +19 -0
- data/lib/pdfrb/model/type/button.rb +17 -0
- data/lib/pdfrb/model/type/cal_gray.rb +17 -0
- data/lib/pdfrb/model/type/cal_rgb.rb +22 -0
- data/lib/pdfrb/model/type/caret_annotation.rb +21 -0
- data/lib/pdfrb/model/type/catalog.rb +99 -16
- data/lib/pdfrb/model/type/choice.rb +21 -0
- data/lib/pdfrb/model/type/cid_font.rb +52 -1
- data/lib/pdfrb/model/type/cid_font_descriptor_metrics.rb +28 -0
- data/lib/pdfrb/model/type/cid_system_info.rb +28 -0
- data/lib/pdfrb/model/type/circle_annotation.rb +20 -0
- data/lib/pdfrb/model/type/collection.rb +27 -0
- data/lib/pdfrb/model/type/collection_field.rb +24 -0
- data/lib/pdfrb/model/type/collection_item.rb +19 -0
- data/lib/pdfrb/model/type/collection_schema.rb +25 -0
- data/lib/pdfrb/model/type/collection_sort.rb +13 -0
- data/lib/pdfrb/model/type/crypt_filter.rb +58 -0
- data/lib/pdfrb/model/type/device_n.rb +37 -0
- data/lib/pdfrb/model/type/device_n_mixing_hints.rb +17 -0
- data/lib/pdfrb/model/type/device_n_process.rb +13 -0
- data/lib/pdfrb/model/type/doc_mdp_transform_parameters.rb +21 -0
- data/lib/pdfrb/model/type/document_security_store.rb +55 -0
- data/lib/pdfrb/model/type/embedded_file.rb +19 -0
- data/lib/pdfrb/model/type/embedded_file_parameter.rb +48 -0
- data/lib/pdfrb/model/type/encryption_public_key.rb +38 -0
- data/lib/pdfrb/model/type/encryption_standard.rb +68 -0
- data/lib/pdfrb/model/type/field_mdp_transform_parameters.rb +26 -0
- data/lib/pdfrb/model/type/file_attachment_annotation.rb +29 -0
- data/lib/pdfrb/model/type/file_spec_ef.rb +22 -0
- data/lib/pdfrb/model/type/file_spec_rf.rb +21 -0
- data/lib/pdfrb/model/type/file_specification.rb +43 -0
- data/lib/pdfrb/model/type/file_trailer.rb +54 -0
- data/lib/pdfrb/model/type/font.rb +73 -2
- data/lib/pdfrb/model/type/font_cid_type0.rb +17 -0
- data/lib/pdfrb/model/type/font_cid_type2.rb +17 -0
- data/lib/pdfrb/model/type/font_descriptor.rb +47 -0
- data/lib/pdfrb/model/type/font_descriptor_cid_type0.rb +15 -0
- data/lib/pdfrb/model/type/font_descriptor_cid_type2.rb +15 -0
- data/lib/pdfrb/model/type/font_encoding.rb +47 -0
- data/lib/pdfrb/model/type/font_file.rb +28 -0
- data/lib/pdfrb/model/type/font_file2.rb +32 -0
- data/lib/pdfrb/model/type/font_file3.rb +23 -0
- data/lib/pdfrb/model/type/font_multiple_master.rb +60 -0
- data/lib/pdfrb/model/type/font_true_type.rb +1 -1
- data/lib/pdfrb/model/type/font_type0.rb +1 -1
- data/lib/pdfrb/model/type/font_type1.rb +1 -1
- data/lib/pdfrb/model/type/font_type3.rb +1 -1
- data/lib/pdfrb/model/type/form_field.rb +73 -0
- data/lib/pdfrb/model/type/free_text_annotation.rb +37 -0
- data/lib/pdfrb/model/type/function.rb +42 -0
- data/lib/pdfrb/model/type/function_exponential.rb +31 -0
- data/lib/pdfrb/model/type/function_postscript.rb +25 -0
- data/lib/pdfrb/model/type/function_sampled.rb +55 -0
- data/lib/pdfrb/model/type/function_stitching.rb +33 -0
- data/lib/pdfrb/model/type/generic_appearance.rb +40 -0
- data/lib/pdfrb/model/type/graphics_state_parameter.rb +69 -0
- data/lib/pdfrb/model/type/halftone.rb +21 -0
- data/lib/pdfrb/model/type/halftone_type1.rb +17 -0
- data/lib/pdfrb/model/type/halftone_type10.rb +16 -0
- data/lib/pdfrb/model/type/halftone_type16.rb +11 -0
- data/lib/pdfrb/model/type/halftone_type5.rb +24 -0
- data/lib/pdfrb/model/type/halftone_type6.rb +16 -0
- data/lib/pdfrb/model/type/highlight_annotation.rb +11 -0
- data/lib/pdfrb/model/type/icc_based_color_space.rb +24 -0
- data/lib/pdfrb/model/type/icon_fit.rb +31 -0
- data/lib/pdfrb/model/type/indexed.rb +32 -0
- data/lib/pdfrb/model/type/info.rb +44 -0
- data/lib/pdfrb/model/type/ink_annotation.rb +29 -0
- data/lib/pdfrb/model/type/interactive_form.rb +36 -0
- data/lib/pdfrb/model/type/interior_color.rb +12 -0
- data/lib/pdfrb/model/type/jbig2_globals.rb +15 -0
- data/lib/pdfrb/model/type/lab.rb +27 -0
- data/lib/pdfrb/model/type/line_annotation.rb +44 -0
- data/lib/pdfrb/model/type/line_ending_styling.rb +39 -0
- data/lib/pdfrb/model/type/link_annotation.rb +41 -0
- data/lib/pdfrb/model/type/link_appearance.rb +9 -0
- data/lib/pdfrb/model/type/mark_information.rb +23 -0
- data/lib/pdfrb/model/type/marked_content_reference.rb +24 -0
- data/lib/pdfrb/model/type/markup_annotation.rb +36 -0
- data/lib/pdfrb/model/type/mdp_dict.rb +28 -0
- data/lib/pdfrb/model/type/measure.rb +39 -0
- data/lib/pdfrb/model/type/media_clip.rb +28 -0
- data/lib/pdfrb/model/type/media_offset.rb +106 -0
- data/lib/pdfrb/model/type/metadata.rb +27 -3
- data/lib/pdfrb/model/type/misc_helpers.rb +75 -0
- data/lib/pdfrb/model/type/movie.rb +28 -0
- data/lib/pdfrb/model/type/names.rb +51 -0
- data/lib/pdfrb/model/type/namespace.rb +27 -0
- data/lib/pdfrb/model/type/object_reference.rb +31 -0
- data/lib/pdfrb/model/type/object_stream.rb +38 -0
- data/lib/pdfrb/model/type/opt_content_ext.rb +94 -0
- data/lib/pdfrb/model/type/opt_content_page_element.rb +18 -0
- data/lib/pdfrb/model/type/optional_content_config.rb +56 -0
- data/lib/pdfrb/model/type/optional_content_group.rb +35 -10
- data/lib/pdfrb/model/type/optional_content_membership.rb +36 -2
- data/lib/pdfrb/model/type/optional_content_properties.rb +16 -1
- data/lib/pdfrb/model/type/outline.rb +40 -6
- data/lib/pdfrb/model/type/outline_item.rb +45 -0
- data/lib/pdfrb/model/type/output_intent.rb +30 -0
- data/lib/pdfrb/model/type/page.rb +127 -0
- data/lib/pdfrb/model/type/page_label.rb +68 -0
- data/lib/pdfrb/model/type/page_piece_info.rb +58 -0
- data/lib/pdfrb/model/type/page_tree_node.rb +51 -16
- data/lib/pdfrb/model/type/page_tree_node_root.rb +15 -0
- data/lib/pdfrb/model/type/pattern.rb +16 -0
- data/lib/pdfrb/model/type/pattern_shading.rb +25 -0
- data/lib/pdfrb/model/type/pattern_tiling.rb +25 -0
- data/lib/pdfrb/model/type/polygon_annotation.rb +29 -0
- data/lib/pdfrb/model/type/polygon_polyline.rb +14 -0
- data/lib/pdfrb/model/type/polyline_annotation.rb +29 -0
- data/lib/pdfrb/model/type/popup_annotation.rb +20 -0
- data/lib/pdfrb/model/type/printer_mark_annotation.rb +13 -0
- data/lib/pdfrb/model/type/redact_annotation.rb +28 -0
- data/lib/pdfrb/model/type/rendition.rb +31 -0
- data/lib/pdfrb/model/type/resources.rb +59 -14
- data/lib/pdfrb/model/type/rich_media_activation.rb +24 -0
- data/lib/pdfrb/model/type/rich_media_animation.rb +25 -0
- data/lib/pdfrb/model/type/rich_media_annotation.rb +18 -0
- data/lib/pdfrb/model/type/rich_media_command.rb +19 -0
- data/lib/pdfrb/model/type/rich_media_configuration.rb +32 -0
- data/lib/pdfrb/model/type/rich_media_content.rb +28 -0
- data/lib/pdfrb/model/type/rich_media_cue_point.rb +18 -0
- data/lib/pdfrb/model/type/rich_media_deactivation.rb +17 -0
- data/lib/pdfrb/model/type/rich_media_instance.rb +33 -0
- data/lib/pdfrb/model/type/rich_media_presentation.rb +29 -0
- data/lib/pdfrb/model/type/rich_media_settings.rb +33 -0
- data/lib/pdfrb/model/type/rich_media_window.rb +20 -0
- data/lib/pdfrb/model/type/screen_annotation.rb +19 -0
- data/lib/pdfrb/model/type/separation.rb +25 -0
- data/lib/pdfrb/model/type/sig_field_lock.rb +25 -0
- data/lib/pdfrb/model/type/sig_field_seed_value.rb +25 -0
- data/lib/pdfrb/model/type/signature.rb +65 -0
- data/lib/pdfrb/model/type/signature_build.rb +86 -0
- data/lib/pdfrb/model/type/signature_field.rb +17 -0
- data/lib/pdfrb/model/type/software_identifier.rb +19 -0
- data/lib/pdfrb/model/type/sound.rb +38 -0
- data/lib/pdfrb/model/type/sound_annotation.rb +21 -0
- data/lib/pdfrb/model/type/square_annotation.rb +24 -0
- data/lib/pdfrb/model/type/square_circle.rb +13 -0
- data/lib/pdfrb/model/type/squiggly_annotation.rb +11 -0
- data/lib/pdfrb/model/type/stamp_annotation.rb +19 -0
- data/lib/pdfrb/model/type/strikeout_annotation.rb +11 -0
- data/lib/pdfrb/model/type/struct_elem.rb +75 -0
- data/lib/pdfrb/model/type/struct_tree_root.rb +34 -0
- data/lib/pdfrb/model/type/structure_attributes.rb +44 -0
- data/lib/pdfrb/model/type/structure_element_kid.rb +33 -0
- data/lib/pdfrb/model/type/text_annotation.rb +25 -0
- data/lib/pdfrb/model/type/text_appearance.rb +9 -0
- data/lib/pdfrb/model/type/text_field.rb +21 -0
- data/lib/pdfrb/model/type/text_markup_annotation.rb +30 -0
- data/lib/pdfrb/model/type/three_d_activation.rb +32 -0
- data/lib/pdfrb/model/type/three_d_animation_style.rb +24 -0
- data/lib/pdfrb/model/type/three_d_background.rb +28 -0
- data/lib/pdfrb/model/type/three_d_cross_section.rb +25 -0
- data/lib/pdfrb/model/type/three_d_lighting_scheme.rb +26 -0
- data/lib/pdfrb/model/type/three_d_measure.rb +20 -0
- data/lib/pdfrb/model/type/three_d_measure_3dc.rb +23 -0
- data/lib/pdfrb/model/type/three_d_measure_ad3.rb +14 -0
- data/lib/pdfrb/model/type/three_d_measure_ld3.rb +14 -0
- data/lib/pdfrb/model/type/three_d_measure_pd3.rb +20 -0
- data/lib/pdfrb/model/type/three_d_measure_rd3.rb +14 -0
- data/lib/pdfrb/model/type/three_d_node.rb +37 -0
- data/lib/pdfrb/model/type/three_d_reference.rb +21 -0
- data/lib/pdfrb/model/type/three_d_render_mode.rb +34 -0
- data/lib/pdfrb/model/type/three_d_stream.rb +48 -0
- data/lib/pdfrb/model/type/three_d_units.rb +18 -0
- data/lib/pdfrb/model/type/three_d_view.rb +40 -0
- data/lib/pdfrb/model/type/to_unicode_cmap_stream.rb +23 -0
- data/lib/pdfrb/model/type/transition.rb +68 -0
- data/lib/pdfrb/model/type/underline_annotation.rb +11 -0
- data/lib/pdfrb/model/type/ur_transform_parameters.rb +33 -0
- data/lib/pdfrb/model/type/uri_dict.rb +18 -0
- data/lib/pdfrb/model/type/url_alias.rb +19 -0
- data/lib/pdfrb/model/type/variable_text_field.rb +47 -0
- data/lib/pdfrb/model/type/viewer_preferences.rb +42 -0
- data/lib/pdfrb/model/type/watermark_annotation.rb +13 -0
- data/lib/pdfrb/model/type/widget_annotation.rb +48 -0
- data/lib/pdfrb/model/type/widget_appearance.rb +9 -0
- data/lib/pdfrb/model/type/xobject_form.rb +41 -0
- data/lib/pdfrb/model/type/xobject_image.rb +56 -0
- data/lib/pdfrb/model/type/xref_stream.rb +37 -0
- data/lib/pdfrb/model/type.rb +289 -6
- data/lib/pdfrb/serializer.rb +2 -2
- data/lib/pdfrb/source/tokenizer.rb +0 -2
- data/lib/pdfrb/task/benchmark.rb +83 -0
- data/lib/pdfrb/task/extract_text.rb +1 -1
- data/lib/pdfrb/task/generate_corpus.rb +130 -0
- data/lib/pdfrb/task/memory_profile.rb +84 -0
- data/lib/pdfrb/task/optimize.rb +97 -7
- data/lib/pdfrb/task/regenerate_appearances.rb +98 -0
- data/lib/pdfrb/task.rb +4 -3
- data/lib/pdfrb/test_utils.rb +112 -0
- data/lib/pdfrb/validator.rb +82 -0
- data/lib/pdfrb/version.rb +1 -1
- data/lib/pdfrb/writer.rb +117 -27
- data/lib/pdfrb/xmp/packet.rb +122 -0
- data/lib/pdfrb/xmp/schemas.rb +96 -0
- data/lib/pdfrb/xmp.rb +44 -0
- data/lib/pdfrb/xref_section.rb +2 -2
- data/lib/pdfrb.rb +24 -54
- data/pdfrb.gemspec +45 -0
- data/script/apply_fixes.sh +843 -0
- data/script/make_jpeg_fixture.rb +53 -0
- data/script/make_png_fixture.rb +25 -0
- metadata +319 -4
|
@@ -1,116 +1,189 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "openssl"
|
|
4
4
|
|
|
5
5
|
module Pdfrb
|
|
6
6
|
module Encryption
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
7
|
+
class StandardSecurityHandler
|
|
8
|
+
PAD = String.new("\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08" \
|
|
9
|
+
"\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A",
|
|
10
|
+
encoding: Encoding::BINARY).freeze
|
|
11
|
+
|
|
12
|
+
attr_reader :key, :key_length, :version, :revision, :permissions
|
|
13
|
+
|
|
14
|
+
def initialize(trailer_dict)
|
|
15
|
+
@encrypt = trailer_dict[:Encrypt]
|
|
16
|
+
@id = trailer_dict[:ID]
|
|
17
|
+
@version = (@encrypt[:V] || 0).to_i
|
|
18
|
+
@revision = (@encrypt[:R] || 3).to_i
|
|
19
|
+
@key_length = (@encrypt[:Length] || 40).to_i / 8
|
|
20
|
+
@permissions = (@encrypt[:P] || -1).to_i
|
|
21
|
+
@key = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def verify_user_password?(password)
|
|
25
|
+
computed = compute_encryption_key(password)
|
|
26
|
+
@key = computed
|
|
27
|
+
verify_user_password_hash?(computed)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def compute_encryption_key(password)
|
|
31
|
+
padded = pad_password(password)
|
|
32
|
+
owner_hash = @encrypt[:O] || String.new("", encoding: Encoding::BINARY)
|
|
33
|
+
perms = [@permissions].pack("V")
|
|
34
|
+
id_first = id_bytes
|
|
35
|
+
|
|
36
|
+
md5 = Digest::MD5.new
|
|
37
|
+
md5.update(padded)
|
|
38
|
+
md5.update(owner_hash)
|
|
39
|
+
md5.update(perms)
|
|
40
|
+
md5.update(id_first)
|
|
41
|
+
|
|
42
|
+
if @revision >= 4
|
|
43
|
+
md5.update(@encrypt[:UE] || "")
|
|
44
|
+
md5.update(@encrypt[:Perms] ? [@encrypt[:Perms]].pack("N") : "")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
hash = md5.digest
|
|
48
|
+
|
|
49
|
+
if @revision >= 3
|
|
50
|
+
50.times { hash = Digest::MD5.digest(hash[0, @key_length]) }
|
|
31
51
|
end
|
|
32
|
-
|
|
52
|
+
|
|
53
|
+
hash[0, @key_length]
|
|
33
54
|
end
|
|
34
55
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
def compute_object_key(oid, gen)
|
|
57
|
+
md5 = Digest::MD5.new
|
|
58
|
+
md5.update(@key)
|
|
59
|
+
md5.update([oid].pack("V"))
|
|
60
|
+
md5.update([gen].pack("v"))
|
|
61
|
+
|
|
62
|
+
if @version >= 4
|
|
63
|
+
md5.update("\xAA")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
md5.digest[0, [@key_length + 5, 16].min]
|
|
42
67
|
end
|
|
43
68
|
|
|
44
|
-
def
|
|
45
|
-
return
|
|
69
|
+
def encrypt_data(data, oid, gen)
|
|
70
|
+
return data unless @key
|
|
46
71
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
72
|
+
obj_key = compute_object_key(oid, gen)
|
|
73
|
+
|
|
74
|
+
if @version >= 4
|
|
75
|
+
encrypt_aes_cbc(data, obj_key)
|
|
76
|
+
else
|
|
77
|
+
encrypt_rc4(data, obj_key)
|
|
51
78
|
end
|
|
52
79
|
end
|
|
53
80
|
|
|
54
|
-
|
|
55
|
-
|
|
81
|
+
# Serializer-compatible alias for encrypt_data. The Serializer
|
|
82
|
+
# calls encrypter.encrypt(payload, oid, gen) on string/stream
|
|
83
|
+
# payloads during serialization.
|
|
84
|
+
alias encrypt encrypt_data
|
|
85
|
+
|
|
86
|
+
def decrypt_data(data, oid, gen)
|
|
87
|
+
return data unless @key
|
|
56
88
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
89
|
+
obj_key = compute_object_key(oid, gen)
|
|
90
|
+
|
|
91
|
+
if @version >= 4
|
|
92
|
+
decrypt_aes_cbc(data, obj_key)
|
|
93
|
+
else
|
|
94
|
+
decrypt_rc4(data, obj_key)
|
|
61
95
|
end
|
|
62
96
|
end
|
|
63
97
|
|
|
64
98
|
private
|
|
65
99
|
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
def pad_password(password)
|
|
101
|
+
pw = password.to_s.encode(Encoding::BINARY)[0, 32]
|
|
102
|
+
pw + PAD[pw.bytesize, 32 - pw.bytesize]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def id_bytes
|
|
106
|
+
return String.new("", encoding: Encoding::BINARY) unless @id
|
|
69
107
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
encrypt.is_a?(Pdfrb::Model::Cos::Dictionary) ? encrypt : nil
|
|
108
|
+
first = @id.is_a?(Array) ? @id[0] : @id
|
|
109
|
+
first.to_s.encode(Encoding::BINARY)
|
|
73
110
|
end
|
|
74
111
|
|
|
75
|
-
def
|
|
76
|
-
@
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
revision: @revision,
|
|
82
|
-
key_length_bits: (@encrypt_dict[:Length] || 40).to_i,
|
|
83
|
-
encrypt_metadata: @encrypt_dict.fetch(:EncryptMetadata, true)
|
|
84
|
-
)
|
|
112
|
+
def verify_user_password_hash?(key)
|
|
113
|
+
return true if @revision < 3
|
|
114
|
+
|
|
115
|
+
hash = compute_user_password_hash(key)
|
|
116
|
+
stored = @encrypt[:U] || ""
|
|
117
|
+
hash[0, 16] == stored[0, 16]
|
|
85
118
|
end
|
|
86
119
|
|
|
87
|
-
def
|
|
88
|
-
|
|
89
|
-
|
|
120
|
+
def compute_user_password_hash(key)
|
|
121
|
+
md5 = Digest::MD5.new
|
|
122
|
+
md5.update(PAD)
|
|
123
|
+
md5.update(id_bytes)
|
|
124
|
+
hash = md5.digest
|
|
90
125
|
|
|
91
|
-
|
|
92
|
-
|
|
126
|
+
rc4 = RC4Impl.new(key)
|
|
127
|
+
20.times { |i| hash = rc4.process(hash, key ^ i) }
|
|
128
|
+
|
|
129
|
+
hash[0, 16]
|
|
93
130
|
end
|
|
94
131
|
|
|
95
|
-
def
|
|
96
|
-
|
|
132
|
+
def encrypt_rc4(data, key)
|
|
133
|
+
RC4Impl.new(key).process(data)
|
|
97
134
|
end
|
|
98
135
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
md5 = Digest::MD5.new
|
|
102
|
-
md5.update(@file_key)
|
|
103
|
-
md5.update([oid].pack("V"))
|
|
104
|
-
md5.update([gen].pack("V"))
|
|
105
|
-
md5.update("sAlT".b) if cipher_type == :aes
|
|
106
|
-
md5.digest.byteslice(0, output_len)
|
|
136
|
+
def decrypt_rc4(data, key)
|
|
137
|
+
RC4Impl.new(key).process(data)
|
|
107
138
|
end
|
|
108
139
|
|
|
109
|
-
def
|
|
110
|
-
OpenSSL::Random.random_bytes(16)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
140
|
+
def encrypt_aes_cbc(data, key)
|
|
141
|
+
iv = OpenSSL::Random.random_bytes(16)
|
|
142
|
+
cipher = OpenSSL::Cipher.new("aes-128-cbc")
|
|
143
|
+
cipher.encrypt
|
|
144
|
+
cipher.key = key[0, 16]
|
|
145
|
+
cipher.iv = iv
|
|
146
|
+
encrypted = cipher.update(data) + cipher.final
|
|
147
|
+
iv + encrypted
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def decrypt_aes_cbc(data, key)
|
|
151
|
+
return data if data.bytesize < 16
|
|
152
|
+
|
|
153
|
+
iv = data[0, 16]
|
|
154
|
+
ciphertext = data[16..]
|
|
155
|
+
decipher = OpenSSL::Cipher.new("aes-128-cbc")
|
|
156
|
+
decipher.decrypt
|
|
157
|
+
decipher.key = key[0, 16]
|
|
158
|
+
decipher.iv = iv
|
|
159
|
+
decipher.update(ciphertext) + decipher.final
|
|
160
|
+
rescue OpenSSL::Cipher::CipherError
|
|
161
|
+
data
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Minimal RC4 implementation
|
|
166
|
+
class RC4Impl
|
|
167
|
+
def initialize(key)
|
|
168
|
+
@s = (0..255).to_a
|
|
169
|
+
j = 0
|
|
170
|
+
key.bytes.each_with_index do |_b, i|
|
|
171
|
+
j = (j + @s[i] + key.getbyte(i % key.bytesize)) & 0xFF
|
|
172
|
+
@s[i], @s[j] = @s[j], @s[i]
|
|
173
|
+
end
|
|
174
|
+
@i = @j = 0
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def process(data, _xor = nil)
|
|
178
|
+
result = data.dup.force_encoding(Encoding::BINARY)
|
|
179
|
+
result.bytes.each_with_index do |_b, idx|
|
|
180
|
+
@i = (@i + 1) & 0xFF
|
|
181
|
+
@j = (@j + @s[@i]) & 0xFF
|
|
182
|
+
@s[@i], @s[@j] = @s[@j], @s[@i]
|
|
183
|
+
k = @s[(@s[@i] + @s[@j]) & 0xFF]
|
|
184
|
+
result.setbyte(idx, result.getbyte(idx) ^ k)
|
|
185
|
+
end
|
|
186
|
+
result
|
|
114
187
|
end
|
|
115
188
|
end
|
|
116
189
|
end
|
data/lib/pdfrb/encryption.rb
CHANGED
|
@@ -16,5 +16,22 @@ module Pdfrb
|
|
|
16
16
|
autoload :RC4, "pdfrb/encryption/rc4"
|
|
17
17
|
autoload :AES, "pdfrb/encryption/aes"
|
|
18
18
|
autoload :PasswordVerification, "pdfrb/encryption/password_verification"
|
|
19
|
+
autoload :Identity, "pdfrb/encryption/identity"
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Look up a registered security-handler subclass by /Filter name.
|
|
24
|
+
def handler_for(filter_name)
|
|
25
|
+
SecurityHandler.lookup(filter_name)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Build a security handler for +document+ by inspecting its /Encrypt.
|
|
29
|
+
# Returns nil if the document is not encrypted.
|
|
30
|
+
def handler_for_document(document, **opts)
|
|
31
|
+
SecurityHandler.for(document, decryption_opts: opts)
|
|
32
|
+
end
|
|
19
33
|
end
|
|
20
34
|
end
|
|
35
|
+
|
|
36
|
+
# Register built-in handlers after the module is fully defined.
|
|
37
|
+
Pdfrb::Encryption::SecurityHandler.register("Standard", Pdfrb::Encryption::StandardSecurityHandler)
|
data/lib/pdfrb/error.rb
CHANGED
|
@@ -29,9 +29,9 @@ module Pdfrb
|
|
|
29
29
|
class MalformedPdfError < ParseError
|
|
30
30
|
attr_reader :recovered
|
|
31
31
|
|
|
32
|
-
def initialize(message, recovered: false, **
|
|
32
|
+
def initialize(message, recovered: false, **)
|
|
33
33
|
@recovered = recovered
|
|
34
|
-
super(message, **
|
|
34
|
+
super(message, **)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def recovered? = @recovered
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Filter
|
|
5
|
+
module BrotliDecode
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def decoder(data, **_opts)
|
|
9
|
+
return data unless data
|
|
10
|
+
|
|
11
|
+
begin
|
|
12
|
+
require "brotli"
|
|
13
|
+
Brotli.inflate(data)
|
|
14
|
+
rescue LoadError
|
|
15
|
+
raise FilterError, "BrotliDecode requires the 'brotli' gem"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def encoder(data, **_opts)
|
|
20
|
+
return data unless data
|
|
21
|
+
|
|
22
|
+
begin
|
|
23
|
+
require "brotli"
|
|
24
|
+
Brotli.deflate(data)
|
|
25
|
+
rescue LoadError
|
|
26
|
+
data
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
REGISTRY[:BrotliDecode] = BrotliDecode if defined?(REGISTRY)
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/pdfrb/filter/crypt.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Pdfrb
|
|
|
15
15
|
return bytes unless document
|
|
16
16
|
return bytes if parms == :Identity || parms.nil?
|
|
17
17
|
|
|
18
|
-
handler = document.security_handler if document.
|
|
18
|
+
handler = document.security_handler if !document.config["security.handler"].nil?
|
|
19
19
|
return bytes unless handler
|
|
20
20
|
|
|
21
21
|
handler.decrypt(bytes, document.current_object_oid)
|
|
@@ -25,7 +25,7 @@ module Pdfrb
|
|
|
25
25
|
return bytes unless document
|
|
26
26
|
return bytes if parms == :Identity || parms.nil?
|
|
27
27
|
|
|
28
|
-
handler = document.security_handler if document.
|
|
28
|
+
handler = document.security_handler if !document.config["security.handler"].nil?
|
|
29
29
|
return bytes unless handler
|
|
30
30
|
|
|
31
31
|
handler.encrypt(bytes, document.current_object_oid)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Filter
|
|
5
|
+
module DCTDecode
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def decoder(data, **_opts)
|
|
9
|
+
data
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def encoder(data, **_opts)
|
|
13
|
+
data
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
REGISTRY[:DCTDecode] = DCTDecode if defined?(REGISTRY)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -51,24 +51,10 @@ module Pdfrb
|
|
|
51
51
|
# predictor: 10..15 = PNG predictors; we implement the None/Sub/Up/
|
|
52
52
|
# Average/Paeth (filter type byte at start of each row).
|
|
53
53
|
def apply_png_predictor_decode(bytes, predictor:, columns:, colors:, bits_per_component:)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
rows = bytes.each_byte.each_slice(row_len).to_a
|
|
59
|
-
prev_row = nil
|
|
60
|
-
bpp = (colors * (bits_per_component / 8)) || 1
|
|
61
|
-
bpp = 1 if bpp.zero?
|
|
62
|
-
|
|
63
|
-
out = +""
|
|
64
|
-
rows.each do |row|
|
|
65
|
-
filter_type = row[0]
|
|
66
|
-
data = row[1..]
|
|
67
|
-
unfiltered = unfilter_row(filter_type, data, prev_row, bpp)
|
|
68
|
-
out << unfiltered.pack("C*")
|
|
69
|
-
prev_row = unfiltered
|
|
70
|
-
end
|
|
71
|
-
out.force_encoding(Encoding::BINARY)
|
|
54
|
+
Pdfrb::Filter::PNGPredictor.decode(
|
|
55
|
+
bytes, predictor: predictor, columns: columns,
|
|
56
|
+
colors: colors, bits_per_component: bits_per_component
|
|
57
|
+
)
|
|
72
58
|
end
|
|
73
59
|
|
|
74
60
|
def unfilter_row(filter_type, data, prev_row, bpp)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Filter
|
|
5
|
+
module JPXDecode
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def decoder(data, **_opts)
|
|
9
|
+
data
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def encoder(data, **_opts)
|
|
13
|
+
data
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
REGISTRY[:JPXDecode] = JPXDecode if defined?(REGISTRY)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -86,12 +86,13 @@ module Pdfrb
|
|
|
86
86
|
predictor = parms[:Predictor] || 1
|
|
87
87
|
return bytes if predictor == 1
|
|
88
88
|
|
|
89
|
-
Pdfrb::Filter::
|
|
89
|
+
Pdfrb::Filter::PNGPredictor.decode(
|
|
90
90
|
bytes,
|
|
91
91
|
predictor: predictor,
|
|
92
92
|
columns: parms[:Columns] || 1,
|
|
93
93
|
colors: parms[:Colors] || 1,
|
|
94
|
-
bits_per_component: parms[:BitsPerComponent] || 8
|
|
94
|
+
bits_per_component: parms[:BitsPerComponent] || 8
|
|
95
|
+
)
|
|
95
96
|
end
|
|
96
97
|
end
|
|
97
98
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Filter
|
|
5
|
+
# PNG predictor (de)coding shared between FlateDecode and LZWDecode
|
|
6
|
+
# (s7.4.8.4 / RFC 2083 6.6). Extracted as a standalone module so
|
|
7
|
+
# both filters can use it without one calling the other's private
|
|
8
|
+
# methods.
|
|
9
|
+
module PNGPredictor
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def decode(bytes, predictor:, columns:, colors:, bits_per_component:)
|
|
13
|
+
return bytes unless (10..15).cover?(predictor)
|
|
14
|
+
|
|
15
|
+
row_len = columns * colors * (bits_per_component / 8)
|
|
16
|
+
row_len += 1 # leading filter-type byte per row
|
|
17
|
+
rows = bytes.each_byte.each_slice(row_len).to_a
|
|
18
|
+
prev_row = nil
|
|
19
|
+
bpp = (colors * (bits_per_component / 8)) || 1
|
|
20
|
+
bpp = 1 if bpp.zero?
|
|
21
|
+
|
|
22
|
+
out = +""
|
|
23
|
+
rows.each do |row|
|
|
24
|
+
filter_type = row[0]
|
|
25
|
+
data = row[1..]
|
|
26
|
+
unfiltered = unfilter_row(filter_type, data, prev_row, bpp)
|
|
27
|
+
out << unfiltered.pack("C*")
|
|
28
|
+
prev_row = unfiltered
|
|
29
|
+
end
|
|
30
|
+
out.force_encoding(Encoding::BINARY)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def unfilter_row(filter_type, data, prev_row, bpp)
|
|
34
|
+
case filter_type
|
|
35
|
+
when 1 then png_sub(data, bpp)
|
|
36
|
+
when 2 then png_up(data, prev_row)
|
|
37
|
+
when 3 then png_average(data, prev_row, bpp)
|
|
38
|
+
when 4 then png_paeth(data, prev_row, bpp)
|
|
39
|
+
else data
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def png_sub(data, bpp)
|
|
44
|
+
bytes = data.dup
|
|
45
|
+
bpp.upto(bytes.length - 1).each do |i|
|
|
46
|
+
bytes[i] = (bytes[i] + bytes[i - bpp]) & 0xFF
|
|
47
|
+
end
|
|
48
|
+
bytes
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def png_up(data, prev_row)
|
|
52
|
+
return data unless prev_row
|
|
53
|
+
|
|
54
|
+
bytes = data.dup
|
|
55
|
+
(0...bytes.length).each do |i|
|
|
56
|
+
bytes[i] = (bytes[i] + (prev_row[i] || 0)) & 0xFF
|
|
57
|
+
end
|
|
58
|
+
bytes
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def png_average(data, prev_row, bpp)
|
|
62
|
+
bytes = data.dup
|
|
63
|
+
(0...bytes.length).each do |i|
|
|
64
|
+
left = bytes[i - bpp] || 0
|
|
65
|
+
up = prev_row ? (prev_row[i] || 0) : 0
|
|
66
|
+
bytes[i] = (bytes[i] + ((left + up) / 2)) & 0xFF
|
|
67
|
+
end
|
|
68
|
+
bytes
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def png_paeth(data, prev_row, bpp)
|
|
72
|
+
bytes = data.dup
|
|
73
|
+
(0...bytes.length).each do |i|
|
|
74
|
+
left = bytes[i - bpp] || 0
|
|
75
|
+
up = prev_row ? (prev_row[i] || 0) : 0
|
|
76
|
+
up_left = prev_row ? (prev_row[i - bpp] || 0) : 0
|
|
77
|
+
bytes[i] = (bytes[i] + paeth(left, up, up_left)) & 0xFF
|
|
78
|
+
end
|
|
79
|
+
bytes
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def paeth(a, b, c)
|
|
83
|
+
p = a + b - c
|
|
84
|
+
pa = (p - a).abs
|
|
85
|
+
pb = (p - b).abs
|
|
86
|
+
pc = (p - c).abs
|
|
87
|
+
return a if pa <= pb && pa <= pc
|
|
88
|
+
return b if pb <= pc
|
|
89
|
+
|
|
90
|
+
c
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/pdfrb/filter.rb
CHANGED
|
@@ -6,7 +6,18 @@ module Pdfrb
|
|
|
6
6
|
# document)` class methods, registered via `register_as`.
|
|
7
7
|
module Filter
|
|
8
8
|
autoload :Base, "pdfrb/filter/base"
|
|
9
|
-
|
|
9
|
+
autoload :FlateDecode, "pdfrb/filter/flate_decode"
|
|
10
|
+
autoload :ASCIIHexDecode, "pdfrb/filter/ascii_hex_decode"
|
|
11
|
+
autoload :ASCII85Decode, "pdfrb/filter/ascii_85_decode"
|
|
12
|
+
autoload :LZWDecode, "pdfrb/filter/lzw_decode"
|
|
13
|
+
autoload :RunLengthDecode, "pdfrb/filter/run_length_decode"
|
|
14
|
+
autoload :Crypt, "pdfrb/filter/crypt"
|
|
15
|
+
autoload :DCTDecode, "pdfrb/filter/dct_decode"
|
|
16
|
+
autoload :JPXDecode, "pdfrb/filter/jpx_decode"
|
|
17
|
+
autoload :CCITTFaxDecode, "pdfrb/filter/ccitt_fax_decode"
|
|
18
|
+
autoload :JBIG2Decode, "pdfrb/filter/jbig2_decode"
|
|
19
|
+
autoload :BrotliDecode, "pdfrb/filter/brotli_decode"
|
|
20
|
+
autoload :PNGPredictor, "pdfrb/filter/png_predictor"
|
|
10
21
|
class << self
|
|
11
22
|
def registry; @registry ||= {}; end
|
|
12
23
|
|
|
@@ -18,7 +29,21 @@ module Pdfrb
|
|
|
18
29
|
registry[name.to_s]
|
|
19
30
|
end
|
|
20
31
|
|
|
32
|
+
# Force-load all filter implementations so register_as calls
|
|
33
|
+
# fire and populate the registry. Idempotent.
|
|
34
|
+
def eager_load!
|
|
35
|
+
return if @eager_loaded
|
|
36
|
+
|
|
37
|
+
constants.each do |c|
|
|
38
|
+
const_get(c)
|
|
39
|
+
rescue NameError
|
|
40
|
+
# Skip autoload targets that don't define a class.
|
|
41
|
+
end
|
|
42
|
+
@eager_loaded = true
|
|
43
|
+
end
|
|
44
|
+
|
|
21
45
|
def apply(bytes, filters:, parms:, direction:, document: nil)
|
|
46
|
+
eager_load!
|
|
22
47
|
return bytes if filters.empty?
|
|
23
48
|
|
|
24
49
|
case direction
|
|
@@ -50,15 +75,3 @@ module Pdfrb
|
|
|
50
75
|
end
|
|
51
76
|
end
|
|
52
77
|
end
|
|
53
|
-
|
|
54
|
-
# Eager-load filters so register_as calls populate the registry
|
|
55
|
-
# before any stream decode happens. Must be after the Filter module
|
|
56
|
-
# and its singleton methods are fully defined.
|
|
57
|
-
require "pdfrb/filter/base"
|
|
58
|
-
require "pdfrb/filter/flate_decode"
|
|
59
|
-
require "pdfrb/filter/ascii_hex_decode"
|
|
60
|
-
require "pdfrb/filter/ascii_85_decode"
|
|
61
|
-
require "pdfrb/filter/lzw_decode"
|
|
62
|
-
require "pdfrb/filter/run_length_decode"
|
|
63
|
-
require "pdfrb/filter/crypt"
|
|
64
|
-
require "pdfrb/filter/passthrough_filters"
|