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
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Content
|
|
5
|
+
module SmartTextExtractor
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def extract(document)
|
|
9
|
+
results = []
|
|
10
|
+
document.pages.each do |page|
|
|
11
|
+
text = extract_page_text(document, page)
|
|
12
|
+
results << { page: page, text: text }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
block_given? ? yield(results) : results
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def extract_page_text(document, page)
|
|
19
|
+
contents = page.value[:Contents]
|
|
20
|
+
return "" unless contents
|
|
21
|
+
|
|
22
|
+
ref = contents.is_a?(Pdfrb::Model::Reference) ? contents : nil
|
|
23
|
+
return "" unless ref
|
|
24
|
+
|
|
25
|
+
stream = document.object(ref)
|
|
26
|
+
return "" unless stream.is_a?(Pdfrb::Model::Cos::Stream)
|
|
27
|
+
|
|
28
|
+
data = stream.decoded_stream
|
|
29
|
+
return "" unless data && !data.empty?
|
|
30
|
+
|
|
31
|
+
collector = TextCollector.new(document, page)
|
|
32
|
+
begin
|
|
33
|
+
collector.process(data)
|
|
34
|
+
rescue StandardError
|
|
35
|
+
# Best effort extraction
|
|
36
|
+
end
|
|
37
|
+
collector.text
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class TextCollector < Pdfrb::Content::Processor
|
|
41
|
+
attr_reader :text
|
|
42
|
+
|
|
43
|
+
def initialize(document, page)
|
|
44
|
+
super()
|
|
45
|
+
@document = document
|
|
46
|
+
@page = page
|
|
47
|
+
@text = +""
|
|
48
|
+
@last_y = nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def show_text(str)
|
|
52
|
+
insert_line_break_if_needed
|
|
53
|
+
@text << decode_text(str)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def show_text_array(arr)
|
|
57
|
+
insert_line_break_if_needed
|
|
58
|
+
arr.each do |e|
|
|
59
|
+
@text << decode_text(e) if e.is_a?(String)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def move_text(tx, ty, **)
|
|
64
|
+
super
|
|
65
|
+
insert_line_break_if_needed
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def insert_line_break_if_needed
|
|
71
|
+
cur_y = graphics_state.text_state.text_matrix.f
|
|
72
|
+
if @last_y && (cur_y - @last_y).abs > 0.001
|
|
73
|
+
@text << "\n"
|
|
74
|
+
end
|
|
75
|
+
@last_y = cur_y
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def decode_text(bytes)
|
|
79
|
+
bytes.to_s.dup.force_encoding("UTF-8")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
private_constant :TextCollector
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Content
|
|
5
|
+
# Tiling patterns (ISO 32000-2 §8.7.3). A tiling pattern is a
|
|
6
|
+
# repeating cell that fills an area. Type 1 (constant spacing) and
|
|
7
|
+
# Type 2 (no distortion) are supported.
|
|
8
|
+
#
|
|
9
|
+
# The pattern cell is drawn via a block using the Canvas API.
|
|
10
|
+
# Registered in page Resources under /Pattern.
|
|
11
|
+
class TilingPattern
|
|
12
|
+
PATTERN_TYPE = 1
|
|
13
|
+
|
|
14
|
+
attr_reader :paint_type, :tiling_type, :bbox, :x_step, :y_step
|
|
15
|
+
|
|
16
|
+
# @param paint_type [Integer] 1 = colored, 2 = uncolored.
|
|
17
|
+
# @param tiling_type [Integer] 1 = constant, 2 = no distortion, 3 = constant spacing + no distortion.
|
|
18
|
+
# @param bbox [Array] bounding box of the pattern cell.
|
|
19
|
+
# @param x_step [Numeric] horizontal repeat distance.
|
|
20
|
+
# @param y_step [Numeric] vertical repeat distance.
|
|
21
|
+
def initialize(paint_type: 1, tiling_type: 1, bbox: [0, 0, 16, 16],
|
|
22
|
+
x_step: 16, y_step: 16)
|
|
23
|
+
@paint_type = paint_type
|
|
24
|
+
@tiling_type = tiling_type
|
|
25
|
+
@bbox = bbox
|
|
26
|
+
@x_step = x_step
|
|
27
|
+
@y_step = y_step
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Build the pattern stream and register it in the page's Resources.
|
|
31
|
+
# @param document [Pdfrb::Document]
|
|
32
|
+
# @param page [Pdfrb::Model::Cos::Dictionary] target page.
|
|
33
|
+
# @yield [canvas] block that draws the pattern cell.
|
|
34
|
+
# @return [Symbol] the resource name (e.g. :P1)
|
|
35
|
+
def register_on(document, page)
|
|
36
|
+
pattern_stream = document.add(
|
|
37
|
+
{
|
|
38
|
+
Type: :Pattern,
|
|
39
|
+
PatternType: PATTERN_TYPE,
|
|
40
|
+
PaintType: @paint_type,
|
|
41
|
+
TilingType: @tiling_type,
|
|
42
|
+
BBox: @bbox,
|
|
43
|
+
XStep: @x_step,
|
|
44
|
+
YStep: @y_step,
|
|
45
|
+
},
|
|
46
|
+
type: Pdfrb::Model::Cos::Stream
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if block_given?
|
|
50
|
+
canvas = Pdfrb::Content::Canvas.new(pattern_stream, document: document)
|
|
51
|
+
yield canvas
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
ref = Pdfrb::Model::Reference.new(pattern_stream.oid, pattern_stream.gen)
|
|
55
|
+
register_in_resources(document, page, ref)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def register_in_resources(_document, page, ref)
|
|
61
|
+
resources = page.value[:Resources]
|
|
62
|
+
resources = {} unless resources.is_a?(::Hash)
|
|
63
|
+
pattern_dict = resources[:Pattern] || {}
|
|
64
|
+
name = next_pattern_name(pattern_dict)
|
|
65
|
+
pattern_dict[name] = ref
|
|
66
|
+
resources[:Pattern] = pattern_dict
|
|
67
|
+
page.value[:Resources] = resources
|
|
68
|
+
name
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def next_pattern_name(pattern_dict)
|
|
72
|
+
existing = pattern_dict.keys.map(&:to_s).grep(/\AP\d+\z/).map { |k| k[1..].to_i }
|
|
73
|
+
num = (existing.max || 0) + 1
|
|
74
|
+
:"P#{num}"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Content
|
|
5
|
+
class TransformationMatrix
|
|
6
|
+
attr_reader :a, :b, :c, :d, :e, :f
|
|
7
|
+
|
|
8
|
+
def initialize(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0)
|
|
9
|
+
@a = a.to_f
|
|
10
|
+
@b = b.to_f
|
|
11
|
+
@c = c.to_f
|
|
12
|
+
@d = d.to_f
|
|
13
|
+
@e = e.to_f
|
|
14
|
+
@f = f.to_f
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def identity?
|
|
19
|
+
@a == 1 && @b.zero? && @c.zero? && @d == 1 && @e.zero? && @f.zero?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def multiply(other)
|
|
23
|
+
self.class.new(
|
|
24
|
+
(@a * other.a) + (@c * other.b),
|
|
25
|
+
(@b * other.a) + (@d * other.b),
|
|
26
|
+
(@a * other.c) + (@c * other.d),
|
|
27
|
+
(@b * other.c) + (@d * other.d),
|
|
28
|
+
(@a * other.e) + (@c * other.f) + @e,
|
|
29
|
+
(@b * other.e) + (@d * other.f) + @f,
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def translate(tx, ty)
|
|
34
|
+
multiply(self.class.new(1, 0, 0, 1, tx, ty))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def scale(sx, sy = sx)
|
|
38
|
+
multiply(self.class.new(sx, 0, 0, sy, 0, 0))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def rotate(angle_radians)
|
|
42
|
+
cos = Math.cos(angle_radians)
|
|
43
|
+
sin = Math.sin(angle_radians)
|
|
44
|
+
multiply(self.class.new(cos, sin, -sin, cos, 0, 0))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def skew(fa, fb)
|
|
48
|
+
multiply(self.class.new(1, Math.tan(fa), Math.tan(fb), 1, 0, 0))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_a
|
|
52
|
+
[@a, @b, @c, @d, @e, @f]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def ==(other)
|
|
56
|
+
other.is_a?(self.class) && to_a == other.to_a
|
|
57
|
+
end
|
|
58
|
+
alias eql? ==
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/pdfrb/content.rb
CHANGED
|
@@ -1,38 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Pdfrb
|
|
4
|
-
# Content-stream layer. Pages and Form XObjects carry a content
|
|
5
|
-
# stream: a sequence of `operands operator` instructions that draw
|
|
6
|
-
# graphics and text. This layer parses, walks, and emits those
|
|
7
|
-
# streams.
|
|
8
|
-
#
|
|
9
|
-
# Architecture mirrors HexaPDF::Content:
|
|
10
|
-
# * +Tokenizer+ from Source layer produces tokens (content
|
|
11
|
-
# streams use the same PDF object syntax as COS values).
|
|
12
|
-
# * +Content::Parser+ groups operands + operator into invocations.
|
|
13
|
-
# * +Content::Operator::Base+ subclasses implement each PDF
|
|
14
|
-
# operator (one file per category — open/closed via `register`).
|
|
15
|
-
# * +Content::Processor+ walks a stream, invoking operators
|
|
16
|
-
# against a +GraphicsState+ (with a stack for `q`/`Q`).
|
|
17
|
-
# * +Content::Canvas+ is the high-level drawing API on a Stream.
|
|
18
4
|
module Content
|
|
19
5
|
autoload :GraphicsState, "pdfrb/content/graphics_state"
|
|
20
6
|
autoload :Operator, "pdfrb/content/operator"
|
|
21
7
|
autoload :Parser, "pdfrb/content/parser"
|
|
22
8
|
autoload :Processor, "pdfrb/content/processor"
|
|
9
|
+
autoload :ColorSpace, "pdfrb/content/color_space"
|
|
10
|
+
autoload :TransformationMatrix, "pdfrb/content/transformation_matrix"
|
|
23
11
|
autoload :Canvas, "pdfrb/content/canvas"
|
|
24
12
|
autoload :GraphicObject, "pdfrb/content/graphic_object"
|
|
13
|
+
autoload :Operators, "pdfrb/content/operators"
|
|
14
|
+
autoload :TilingPattern, "pdfrb/content/tiling_pattern"
|
|
15
|
+
autoload :HiddenTextDetector, "pdfrb/content/hidden_text_detector"
|
|
16
|
+
autoload :SmartTextExtractor, "pdfrb/content/smart_text_extractor"
|
|
17
|
+
autoload :Shading, "pdfrb/content/shading"
|
|
25
18
|
end
|
|
26
19
|
end
|
|
27
|
-
|
|
28
|
-
# Eager-load operator subclasses so they self-register in the
|
|
29
|
-
# Operator::REGISTRY. (Each file calls Operator.register at the bottom.)
|
|
30
|
-
require "pdfrb/content/operators/general"
|
|
31
|
-
require "pdfrb/content/operators/path"
|
|
32
|
-
require "pdfrb/content/operators/painting"
|
|
33
|
-
require "pdfrb/content/operators/text_state"
|
|
34
|
-
require "pdfrb/content/operators/text_positioning"
|
|
35
|
-
require "pdfrb/content/operators/text_showing"
|
|
36
|
-
require "pdfrb/content/operators/color"
|
|
37
|
-
require "pdfrb/content/operators/graphics_state_params"
|
|
38
|
-
require "pdfrb/content/operators/marked_content"
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Destination
|
|
5
|
+
# Destination fit types (ISO 32000-2 §12.3.2.2). Each type specifies
|
|
6
|
+
# how the page should be displayed when navigated to.
|
|
7
|
+
#
|
|
8
|
+
# OCP: new fit type = subclass + register_as.
|
|
9
|
+
class Fit
|
|
10
|
+
class << self
|
|
11
|
+
def registry
|
|
12
|
+
@registry ||= {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def register(name, klass)
|
|
16
|
+
registry[name.to_sym] = klass
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def [](name)
|
|
20
|
+
registry[name.to_sym]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def types
|
|
24
|
+
registry.keys
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def fit_keyword
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def register_as(name = fit_keyword)
|
|
32
|
+
Fit.register(name, self)
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :page_ref
|
|
38
|
+
|
|
39
|
+
def initialize(page_ref)
|
|
40
|
+
@page_ref = page_ref
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Serialize to the PDF destination array form.
|
|
44
|
+
# @return [Pdfrb::Model::PdfArray]
|
|
45
|
+
def to_pdf
|
|
46
|
+
Pdfrb::Model::PdfArray.new([@page_ref, self.class.fit_keyword])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# /Fit — fit the entire page in the window.
|
|
51
|
+
class FullPage < Fit
|
|
52
|
+
class << self
|
|
53
|
+
def fit_keyword; :Fit; end
|
|
54
|
+
end
|
|
55
|
+
register_as
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# /FitH top — fit width, top at +top+.
|
|
59
|
+
class FitHorizontal < Fit
|
|
60
|
+
attr_reader :top
|
|
61
|
+
|
|
62
|
+
def initialize(page_ref, top: nil)
|
|
63
|
+
super(page_ref)
|
|
64
|
+
@top = top
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class << self
|
|
68
|
+
def fit_keyword; :FitH; end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def to_pdf
|
|
72
|
+
Pdfrb::Model::PdfArray.new([@page_ref, :FitH, @top].compact)
|
|
73
|
+
end
|
|
74
|
+
register_as
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# /FitV left — fit height, left at +left+.
|
|
78
|
+
class FitVertical < Fit
|
|
79
|
+
attr_reader :left
|
|
80
|
+
|
|
81
|
+
def initialize(page_ref, left: nil)
|
|
82
|
+
super(page_ref)
|
|
83
|
+
@left = left
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class << self
|
|
87
|
+
def fit_keyword; :FitV; end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def to_pdf
|
|
91
|
+
Pdfrb::Model::PdfArray.new([@page_ref, :FitV, @left].compact)
|
|
92
|
+
end
|
|
93
|
+
register_as
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# /FitR left bottom right top — fit the specified rectangle.
|
|
97
|
+
class FitRectangle < Fit
|
|
98
|
+
attr_reader :left, :bottom, :right, :top
|
|
99
|
+
|
|
100
|
+
def initialize(page_ref, left:, bottom:, right:, top:)
|
|
101
|
+
super(page_ref)
|
|
102
|
+
@left = left
|
|
103
|
+
@bottom = bottom
|
|
104
|
+
@right = right
|
|
105
|
+
@top = top
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class << self
|
|
109
|
+
def fit_keyword; :FitR; end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def to_pdf
|
|
113
|
+
Pdfrb::Model::PdfArray.new([@page_ref, :FitR, @left, @bottom, @right, @top])
|
|
114
|
+
end
|
|
115
|
+
register_as
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# /XYZ left top zoom — explicit position and zoom.
|
|
119
|
+
class XYZ < Fit
|
|
120
|
+
attr_reader :left, :top, :zoom
|
|
121
|
+
|
|
122
|
+
def initialize(page_ref, left: nil, top: nil, zoom: nil)
|
|
123
|
+
super(page_ref)
|
|
124
|
+
@left = left
|
|
125
|
+
@top = top
|
|
126
|
+
@zoom = zoom
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class << self
|
|
130
|
+
def fit_keyword; :XYZ; end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def to_pdf
|
|
134
|
+
Pdfrb::Model::PdfArray.new([@page_ref, :XYZ, @left, @top, @zoom].compact)
|
|
135
|
+
end
|
|
136
|
+
register_as
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# /FitB — fit the bounding box of the page contents.
|
|
140
|
+
class FitBoundingBox < Fit
|
|
141
|
+
class << self
|
|
142
|
+
def fit_keyword; :FitB; end
|
|
143
|
+
end
|
|
144
|
+
register_as
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# /FitBH top — fit bounding box width, top at +top+.
|
|
148
|
+
class FitBoundingBoxHorizontal < Fit
|
|
149
|
+
attr_reader :top
|
|
150
|
+
|
|
151
|
+
def initialize(page_ref, top: nil)
|
|
152
|
+
super(page_ref)
|
|
153
|
+
@top = top
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
class << self
|
|
157
|
+
def fit_keyword; :FitBH; end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def to_pdf
|
|
161
|
+
Pdfrb::Model::PdfArray.new([@page_ref, :FitBH, @top].compact)
|
|
162
|
+
end
|
|
163
|
+
register_as
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# /FitBV left — fit bounding box height, left at +left+.
|
|
167
|
+
class FitBoundingBoxVertical < Fit
|
|
168
|
+
attr_reader :left
|
|
169
|
+
|
|
170
|
+
def initialize(page_ref, left: nil)
|
|
171
|
+
super(page_ref)
|
|
172
|
+
@left = left
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
class << self
|
|
176
|
+
def fit_keyword; :FitBV; end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def to_pdf
|
|
180
|
+
Pdfrb::Model::PdfArray.new([@page_ref, :FitBV, @left].compact)
|
|
181
|
+
end
|
|
182
|
+
register_as
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
# Destinations (ISO 32000-2 §12.3.2). A destination specifies a view
|
|
5
|
+
# of a page: which page, how to fit it in the viewport, and optional
|
|
6
|
+
# position/zoom parameters.
|
|
7
|
+
#
|
|
8
|
+
# Each fit type is a value object that serializes to a PDF destination
|
|
9
|
+
# array: [page_ref /Fit], [page_ref /XYZ x y zoom], etc.
|
|
10
|
+
module Destination
|
|
11
|
+
autoload :Fit, "pdfrb/destination/fit"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module DigitalSignature
|
|
5
|
+
module CmsHandler
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def type; :"adbe.pkcs7.detached"; end
|
|
9
|
+
|
|
10
|
+
def sign(document, cert:, key:, **)
|
|
11
|
+
Pdfrb::DigitalSignature::Signing.sign(document, cert: cert, key: key, **)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def verify(signature_data, signed_data, trusted_certs: [])
|
|
15
|
+
pkcs7 = OpenSSL::PKCS7.new(signature_data)
|
|
16
|
+
store = OpenSSL::X509::Store.new
|
|
17
|
+
trusted_certs.each { |c| store.add_cert(c) }
|
|
18
|
+
pkcs7.verify(nil, store, signed_data, OpenSSL::PKCS7::DETACHED)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -1,28 +1,124 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "openssl"
|
|
4
|
+
require "stringio"
|
|
4
5
|
|
|
5
6
|
module Pdfrb
|
|
6
7
|
module DigitalSignature
|
|
7
|
-
# PKCS#7 signing (s12.8.3). Creates a signature field, computes
|
|
8
|
-
# the byte-range hash, and embeds the detached PKCS#7 signature.
|
|
9
8
|
module Signing
|
|
9
|
+
PLACEHOLDER_HEX_LEN = 8192
|
|
10
|
+
|
|
10
11
|
module_function
|
|
11
12
|
|
|
12
|
-
def sign(document, cert:, key:,
|
|
13
|
+
def sign(document, cert:, key:, reason: nil, name: nil)
|
|
13
14
|
raise Pdfrb::Error, "OpenSSL cert required" unless cert.is_a?(OpenSSL::X509::Certificate)
|
|
14
15
|
raise Pdfrb::Error, "OpenSSL PKey required" unless key.is_a?(OpenSSL::PKey::PKey)
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
sig_dict
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
sig_dict = build_sig_dict(document, reason: reason, name: name)
|
|
18
|
+
attach_to_acroform(document, sig_dict)
|
|
19
|
+
|
|
20
|
+
pdf = serialize(document)
|
|
21
|
+
pdf = insert_contents_placeholder(pdf, sig_dict.oid)
|
|
22
|
+
finalize_signature(pdf, cert, key)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def build_sig_dict(document, reason:, name:)
|
|
26
|
+
hash = {
|
|
27
|
+
Type: :Sig,
|
|
28
|
+
Filter: :"Adobe.PPKLite",
|
|
29
|
+
SubFilter: :"adbe.pkcs7.detached",
|
|
30
|
+
ByteRange: [0, 0, 0, 0],
|
|
31
|
+
}
|
|
32
|
+
hash[:Reason] = reason if reason
|
|
33
|
+
hash[:Name] = name if name
|
|
34
|
+
hash[:M] = "D:#{Time.now.utc.strftime("%Y%m%d%H%M%S+00'00'")}"
|
|
35
|
+
document.add(hash, type: Pdfrb::Model::Cos::Dictionary)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def attach_to_acroform(document, sig_dict)
|
|
39
|
+
catalog = document.catalog
|
|
40
|
+
catalog.value[:AcroForm] ||= {}
|
|
41
|
+
catalog.value[:AcroForm][:SigFlags] = 3
|
|
42
|
+
catalog.value[:AcroForm][:Fields] ||= []
|
|
43
|
+
|
|
44
|
+
field = document.add(
|
|
45
|
+
{ Type: :Annot, Subtype: :Widget, FT: :Sig, T: "Signature1",
|
|
46
|
+
V: Pdfrb::Model::Reference.new(sig_dict.oid, sig_dict.gen),
|
|
47
|
+
Rect: [0, 0, 0, 0] },
|
|
23
48
|
type: Pdfrb::Model::Cos::Dictionary
|
|
24
49
|
)
|
|
25
|
-
|
|
50
|
+
catalog.value[:AcroForm][:Fields] <<
|
|
51
|
+
Pdfrb::Model::Reference.new(field.oid, field.gen)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def serialize(document)
|
|
55
|
+
io = StringIO.new
|
|
56
|
+
Pdfrb::Writer.write(document, io)
|
|
57
|
+
io.string
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def insert_contents_placeholder(pdf, sig_oid)
|
|
61
|
+
sig_pattern = "#{sig_oid} 0 obj"
|
|
62
|
+
sig_start = pdf.index(sig_pattern)
|
|
63
|
+
return pdf unless sig_start
|
|
64
|
+
|
|
65
|
+
dict_end = pdf.index(">>", sig_start)
|
|
66
|
+
return pdf unless dict_end
|
|
67
|
+
|
|
68
|
+
placeholder = "0" * PLACEHOLDER_HEX_LEN
|
|
69
|
+
contents_str = "/Contents <#{placeholder}>"
|
|
70
|
+
|
|
71
|
+
"#{pdf[0, dict_end + 2]}\n#{contents_str}#{pdf[(dict_end + 2)..]}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def finalize_signature(pdf, cert, key)
|
|
75
|
+
marker = "/Contents <"
|
|
76
|
+
contents_marker = pdf.index(marker)
|
|
77
|
+
return pdf unless contents_marker
|
|
78
|
+
|
|
79
|
+
hex_start = contents_marker + marker.bytesize
|
|
80
|
+
hex_end = pdf.index(">", hex_start)
|
|
81
|
+
return pdf unless hex_end
|
|
82
|
+
|
|
83
|
+
part1_end = hex_start
|
|
84
|
+
part2_start = hex_end + 1
|
|
85
|
+
part2_len = pdf.bytesize - part2_start
|
|
86
|
+
|
|
87
|
+
br_old = "/ByteRange [0 0 0 0]"
|
|
88
|
+
br_new = "/ByteRange [0 #{part1_end} #{part2_start} #{part2_len}]"
|
|
89
|
+
size_diff = br_new.bytesize - br_old.bytesize
|
|
90
|
+
|
|
91
|
+
if size_diff.positive?
|
|
92
|
+
part1_end += size_diff
|
|
93
|
+
part2_start += size_diff
|
|
94
|
+
br_new = "/ByteRange [0 #{part1_end} #{part2_start} #{part2_len}]"
|
|
95
|
+
new_size_diff = br_new.bytesize - br_old.bytesize
|
|
96
|
+
if new_size_diff != size_diff
|
|
97
|
+
adjust = new_size_diff - size_diff
|
|
98
|
+
part1_end += adjust
|
|
99
|
+
part2_start += adjust
|
|
100
|
+
br_new = "/ByteRange [0 #{part1_end} #{part2_start} #{part2_len}]"
|
|
101
|
+
end
|
|
102
|
+
elsif size_diff.negative?
|
|
103
|
+
br_new = br_new.ljust(br_old.bytesize)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
pdf = pdf.sub(br_old, br_new)
|
|
107
|
+
|
|
108
|
+
hex_start = part1_end
|
|
109
|
+
hex_end = pdf.index(">", hex_start)
|
|
110
|
+
hex_len = hex_end - hex_start
|
|
111
|
+
|
|
112
|
+
signed_data = pdf.bytes[0, part1_end].pack("C*") +
|
|
113
|
+
pdf.bytes[part2_start, part2_len].pack("C*")
|
|
114
|
+
|
|
115
|
+
der = OpenSSL::PKCS7.sign(cert, key, signed_data, [],
|
|
116
|
+
OpenSSL::PKCS7::DETACHED |
|
|
117
|
+
OpenSSL::PKCS7::BINARY).to_der
|
|
118
|
+
|
|
119
|
+
hex_sig = der.unpack1("H*").ljust(hex_len, "0")
|
|
120
|
+
|
|
121
|
+
pdf[0, hex_start] + hex_sig + pdf[hex_end..]
|
|
26
122
|
end
|
|
27
123
|
end
|
|
28
124
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module DigitalSignature
|
|
5
|
+
module TimestampHandler
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def type; :"adbe.revision"; end
|
|
9
|
+
|
|
10
|
+
def create_timestamp(data, tsa_url: nil)
|
|
11
|
+
return nil unless tsa_url
|
|
12
|
+
|
|
13
|
+
require "net/http"
|
|
14
|
+
require "uri"
|
|
15
|
+
|
|
16
|
+
uri = URI(tsa_url)
|
|
17
|
+
response = Net::HTTP.start(uri.host, uri.port) do |http|
|
|
18
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
|
19
|
+
req["Content-Type"] = "application/timestamp-query"
|
|
20
|
+
req.body = build_tsr_request(data)
|
|
21
|
+
http.request(req)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
response.body if response.is_a?(Net::HTTPSuccess)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def build_tsr_request(data)
|
|
28
|
+
require "openssl"
|
|
29
|
+
hash = OpenSSL::Digest.new("SHA256")
|
|
30
|
+
req = OpenSSL::ASN1::Sequence.new([
|
|
31
|
+
OpenSSL::ASN1::Integer.new(1),
|
|
32
|
+
OpenSSL::ASN1::Sequence.new([
|
|
33
|
+
OpenSSL::ASN1::Sequence.new([
|
|
34
|
+
OpenSSL::ASN1::ObjectId.new("2.16.840.1.101.3.4.2.1"),
|
|
35
|
+
OpenSSL::ASN1::OctetString.new(hash.digest(data).force_encoding("BINARY")),
|
|
36
|
+
]),
|
|
37
|
+
]),
|
|
38
|
+
OpenSSL::ASN1::Integer.new(0),
|
|
39
|
+
OpenSSL::ASN1::Boolean.new(false),
|
|
40
|
+
])
|
|
41
|
+
req.to_der
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|