pdfrb 0.2.1 → 0.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/.rubocop.yml +1 -1
- data/CHANGELOG.md +24 -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 +48 -0
- 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 +312 -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 +217 -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.rb +21 -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/form_xobject.rb +68 -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 +31 -257
- 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_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 +286 -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.rb +3 -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 +25 -53
- 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 +312 -2
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
5
|
+
module Pdfrb
|
|
6
|
+
module Content
|
|
7
|
+
# Detects hidden / transparent text in content streams. Hidden text
|
|
8
|
+
# is text rendered with Tr=3 (invisible) or with alpha=0 (fully
|
|
9
|
+
# transparent). PDF/A and PDF/UA require such text to be tagged as
|
|
10
|
+
# /Artifact or wrapped in a structure element.
|
|
11
|
+
#
|
|
12
|
+
# Inspired by mn2pdf's PDFHiddenTextStripper. Walks content streams
|
|
13
|
+
# via the Content::Parser, tracking graphics state changes.
|
|
14
|
+
class HiddenTextDetector
|
|
15
|
+
HiddenItem = Struct.new(:text, :reason, :page, keyword_init: true) do
|
|
16
|
+
def hidden?
|
|
17
|
+
!reason.nil?
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :document
|
|
22
|
+
|
|
23
|
+
def initialize(document)
|
|
24
|
+
@document = document
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Walk all content streams. Returns HiddenItem objects for each
|
|
28
|
+
# text-showing operation. Items with a nil reason are visible.
|
|
29
|
+
def detect
|
|
30
|
+
items = []
|
|
31
|
+
@document.each_indirect_object do |obj|
|
|
32
|
+
next unless content_stream?(obj)
|
|
33
|
+
|
|
34
|
+
page = find_page(obj)
|
|
35
|
+
detect_in_stream(obj, page) { |item| items << item }
|
|
36
|
+
end
|
|
37
|
+
items
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns only the hidden text items (rendering_mode_3 or alpha=0).
|
|
41
|
+
def hidden_only
|
|
42
|
+
detect.select(&:hidden?)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def content_stream?(obj)
|
|
48
|
+
return false unless obj.is_a?(Pdfrb::Model::Cos::Stream)
|
|
49
|
+
return true if obj.value[:Type] == :Page
|
|
50
|
+
|
|
51
|
+
subtype = obj.value[:Subtype]
|
|
52
|
+
return false if subtype == :Image
|
|
53
|
+
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def find_page(stream_obj)
|
|
58
|
+
@document.each_indirect_object do |obj|
|
|
59
|
+
next unless obj.is_a?(Pdfrb::Model::Cos::Dictionary)
|
|
60
|
+
next unless obj[:Type] == :Page
|
|
61
|
+
|
|
62
|
+
contents = obj[:Contents]
|
|
63
|
+
next unless contents
|
|
64
|
+
|
|
65
|
+
list = if contents.is_a?(Pdfrb::Model::PdfArray)
|
|
66
|
+
contents.value
|
|
67
|
+
else
|
|
68
|
+
contents.is_a?(::Array) ? contents : [contents]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
return obj if list.any? do |c|
|
|
72
|
+
c_obj = c.is_a?(Pdfrb::Model::Reference) ? @document.object(c) : c
|
|
73
|
+
c_obj == stream_obj
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def detect_in_stream(stream_obj, page, &)
|
|
80
|
+
bytes = stream_obj.stream
|
|
81
|
+
return unless bytes && !bytes.empty?
|
|
82
|
+
|
|
83
|
+
io = StringIO.new(bytes.b)
|
|
84
|
+
tokenizer = Pdfrb::Source::Tokenizer.new(io)
|
|
85
|
+
parser = Content::Parser.new(tokenizer)
|
|
86
|
+
collector = Collector.new(page)
|
|
87
|
+
|
|
88
|
+
parser.each_invocation do |op_class, operands|
|
|
89
|
+
op_name = op_class.name
|
|
90
|
+
case op_name
|
|
91
|
+
when "Tr"
|
|
92
|
+
collector.text_rendering_mode = operands.first.to_i if operands.first
|
|
93
|
+
when "ca"
|
|
94
|
+
collector.stroke_alpha = operands.first.to_f if operands.first
|
|
95
|
+
when "CA"
|
|
96
|
+
collector.non_stroke_alpha = operands.first.to_f if operands.first
|
|
97
|
+
when "Tj"
|
|
98
|
+
collector.show_text(operands.first)
|
|
99
|
+
when "TJ"
|
|
100
|
+
collector.show_text_array(operands.first)
|
|
101
|
+
when "'", "\""
|
|
102
|
+
collector.move_to_next_line
|
|
103
|
+
collector.show_text(operands.first) if operands.first
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
collector.items.each(&)
|
|
108
|
+
rescue StandardError
|
|
109
|
+
nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Internal: collects text-show operations with their visibility state.
|
|
113
|
+
class Collector
|
|
114
|
+
attr_reader :items
|
|
115
|
+
|
|
116
|
+
def initialize(page)
|
|
117
|
+
@items = []
|
|
118
|
+
@page = page
|
|
119
|
+
@text_rendering_mode = 0
|
|
120
|
+
@non_stroke_alpha = 1.0
|
|
121
|
+
@stroke_alpha = 1.0
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def text_rendering_mode=(value)
|
|
125
|
+
@text_rendering_mode = value
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def non_stroke_alpha=(value)
|
|
129
|
+
@non_stroke_alpha = value
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def stroke_alpha=(value)
|
|
133
|
+
@stroke_alpha = value
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def show_text(text)
|
|
137
|
+
return if text.nil?
|
|
138
|
+
|
|
139
|
+
reason = current_reason
|
|
140
|
+
@items << HiddenItem.new(text: text.to_s, reason: reason, page: @page)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def show_text_array(items)
|
|
144
|
+
return unless items.is_a?(::Array)
|
|
145
|
+
|
|
146
|
+
items.each do |item|
|
|
147
|
+
if item.is_a?(::String)
|
|
148
|
+
show_text(item)
|
|
149
|
+
end
|
|
150
|
+
# Numeric items are positioning values; ignore them
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def move_to_next_line; end
|
|
155
|
+
|
|
156
|
+
private
|
|
157
|
+
|
|
158
|
+
def current_reason
|
|
159
|
+
return :rendering_mode_invisible if @text_rendering_mode == 3
|
|
160
|
+
return :alpha_zero_hidden if @non_stroke_alpha.to_f == 0.0 || @stroke_alpha.to_f == 0.0
|
|
161
|
+
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -20,12 +20,32 @@ module Pdfrb
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def [](name)
|
|
23
|
-
|
|
23
|
+
key = name.to_s
|
|
24
|
+
result = @registry[key]
|
|
25
|
+
return result if result
|
|
26
|
+
|
|
27
|
+
# Lazy-load on miss: trigger eager_load! which populates
|
|
28
|
+
# the registry, then retry.
|
|
29
|
+
eager_load! unless @eager_loaded
|
|
30
|
+
@registry[key]
|
|
24
31
|
end
|
|
25
32
|
|
|
26
33
|
def names
|
|
27
34
|
@registry.keys
|
|
28
35
|
end
|
|
36
|
+
|
|
37
|
+
# Force-load all operator implementations so register calls
|
|
38
|
+
# fire and populate the registry. Idempotent.
|
|
39
|
+
def eager_load!
|
|
40
|
+
return if @eager_loaded
|
|
41
|
+
|
|
42
|
+
Pdfrb::Content::Operators.constants.each do |c|
|
|
43
|
+
Pdfrb::Content::Operators.const_get(c)
|
|
44
|
+
rescue NameError
|
|
45
|
+
# Skip modules that don't resolve.
|
|
46
|
+
end
|
|
47
|
+
@eager_loaded = true
|
|
48
|
+
end
|
|
29
49
|
end
|
|
30
50
|
|
|
31
51
|
# Base class. Subclasses override +name+ and optionally
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Content
|
|
5
|
+
module Operator
|
|
6
|
+
# Clipping path operators (s8.5.4). W and W* modify the current
|
|
7
|
+
# clipping path by intersecting it with the current path. The
|
|
8
|
+
# clipping path takes effect when the next painting operator or
|
|
9
|
+
# `n` (end path) is executed.
|
|
10
|
+
|
|
11
|
+
# `W` — modify clipping path using nonzero winding rule.
|
|
12
|
+
class ClipNonZero < NoArg
|
|
13
|
+
def self.name; "W"; end
|
|
14
|
+
register
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# `W*` — modify clipping path using even-odd rule.
|
|
18
|
+
class ClipEvenOdd < NoArg
|
|
19
|
+
def self.name; "W*"; end
|
|
20
|
+
register
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# `Do` — invoke an XObject (Image or Form) by resource name.
|
|
24
|
+
class InvokeXObject < Base
|
|
25
|
+
class << self
|
|
26
|
+
def name; "Do"; end
|
|
27
|
+
|
|
28
|
+
def serialize(_serializer, name)
|
|
29
|
+
"/#{name} Do\n"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
register
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -73,6 +73,21 @@ module Pdfrb
|
|
|
73
73
|
end
|
|
74
74
|
register
|
|
75
75
|
end
|
|
76
|
+
class NonStrokeAlpha < Base
|
|
77
|
+
def self.name; "ca"; end
|
|
78
|
+
def self.invoke(processor, alpha)
|
|
79
|
+
processor.update_graphics_state(non_stroke_alpha: alpha.to_f)
|
|
80
|
+
end
|
|
81
|
+
register
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
class StrokeAlpha < Base
|
|
85
|
+
def self.name; "CA"; end
|
|
86
|
+
def self.invoke(processor, alpha)
|
|
87
|
+
processor.update_graphics_state(stroke_alpha: alpha.to_f)
|
|
88
|
+
end
|
|
89
|
+
register
|
|
90
|
+
end
|
|
76
91
|
end
|
|
77
92
|
end
|
|
78
93
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Content
|
|
5
|
+
# Operator implementation files. Each file defines one or more
|
|
6
|
+
# Operator::Base subclasses that register themselves on load.
|
|
7
|
+
# Eager loading is triggered by Operator.eager_load!.
|
|
8
|
+
module Operators
|
|
9
|
+
autoload :General, "pdfrb/content/operators/general"
|
|
10
|
+
autoload :Path, "pdfrb/content/operators/path"
|
|
11
|
+
autoload :Painting, "pdfrb/content/operators/painting"
|
|
12
|
+
autoload :TextState, "pdfrb/content/operators/text_state"
|
|
13
|
+
autoload :TextPositioning, "pdfrb/content/operators/text_positioning"
|
|
14
|
+
autoload :TextShowing, "pdfrb/content/operators/text_showing"
|
|
15
|
+
autoload :Color, "pdfrb/content/operators/color"
|
|
16
|
+
autoload :GraphicsStateParams, "pdfrb/content/operators/graphics_state_params"
|
|
17
|
+
autoload :MarkedContent, "pdfrb/content/operators/marked_content"
|
|
18
|
+
autoload :Clipping, "pdfrb/content/operators/clipping"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Content
|
|
5
|
+
module Shading
|
|
6
|
+
# Base shading builder. Subclasses set +shading_type+ and
|
|
7
|
+
# +shading_fields+. Creates a shading dict and registers it
|
|
8
|
+
# in the page's /Resources /Shading sub-dict.
|
|
9
|
+
class Base
|
|
10
|
+
attr_reader :color_space
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def shading_type
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(color_space: :DeviceGray)
|
|
19
|
+
@color_space = color_space
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Build the shading dictionary value (a Hash).
|
|
23
|
+
def to_dict
|
|
24
|
+
{ ShadingType: self.class.shading_type,
|
|
25
|
+
ColorSpace: @color_space }.merge!(shading_fields)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Register this shading on a page and return the resource name.
|
|
29
|
+
# @param document [Pdfrb::Document]
|
|
30
|
+
# @param page [Pdfrb::Model::Cos::Dictionary] target page.
|
|
31
|
+
# @return [Symbol] resource name (e.g. :Sh1)
|
|
32
|
+
def register_on(document, page)
|
|
33
|
+
shading_obj = document.add(to_dict, type: Pdfrb::Model::Cos::Dictionary)
|
|
34
|
+
ref = Pdfrb::Model::Reference.new(shading_obj.oid, shading_obj.gen)
|
|
35
|
+
resources = ensure_resources(document, page)
|
|
36
|
+
shading_dict = resources.value[:Shading]
|
|
37
|
+
if shading_dict.nil?
|
|
38
|
+
shading_dict = {}
|
|
39
|
+
resources.value[:Shading] = shading_dict
|
|
40
|
+
end
|
|
41
|
+
name = next_shading_name(shading_dict)
|
|
42
|
+
shading_dict[name] = ref
|
|
43
|
+
name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
protected
|
|
47
|
+
|
|
48
|
+
def shading_fields
|
|
49
|
+
{}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def ensure_resources(document, page)
|
|
55
|
+
resources = page.value[:Resources]
|
|
56
|
+
return resources if resources.is_a?(Pdfrb::Model::Cos::Dictionary)
|
|
57
|
+
|
|
58
|
+
resources_hash = if resources.is_a?(Pdfrb::Model::Reference)
|
|
59
|
+
document.object(resources)&.value
|
|
60
|
+
else
|
|
61
|
+
resources
|
|
62
|
+
end
|
|
63
|
+
resources_hash ||= {}
|
|
64
|
+
wrapped = Pdfrb::Model::Cos::Dictionary.new(resources_hash)
|
|
65
|
+
page.value[:Resources] = wrapped
|
|
66
|
+
wrapped
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def next_shading_name(shading_dict)
|
|
70
|
+
existing = shading_dict.keys.map(&:to_s).grep(/\ASh\d+\z/).map { |k| k[2..].to_i }
|
|
71
|
+
num = (existing.max || 0) + 1
|
|
72
|
+
:"Sh#{num}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Axial (linear) gradient. ShadingType 2.
|
|
77
|
+
# ISO 32000-2 §8.7.4.5.4.
|
|
78
|
+
class Axial < Base
|
|
79
|
+
attr_reader :start_point, :end_point, :function, :extend
|
|
80
|
+
|
|
81
|
+
def initialize(color_space:, start_point:, end_point:,
|
|
82
|
+
function:, extend: nil)
|
|
83
|
+
super(color_space: color_space)
|
|
84
|
+
@start_point = start_point
|
|
85
|
+
@end_point = end_point
|
|
86
|
+
@function = function
|
|
87
|
+
@extend = extend
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class << self
|
|
91
|
+
def shading_type; 2; end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
protected
|
|
95
|
+
|
|
96
|
+
def shading_fields
|
|
97
|
+
fields = {
|
|
98
|
+
Coords: Pdfrb::Model::PdfArray.new([*@start_point, *@end_point]),
|
|
99
|
+
Function: @function,
|
|
100
|
+
}
|
|
101
|
+
fields[:Extend] = Pdfrb::Model::PdfArray.new(@extend) if @extend
|
|
102
|
+
fields
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Radial gradient. ShadingType 3.
|
|
107
|
+
# ISO 32000-2 §8.7.4.5.3.
|
|
108
|
+
class Radial < Base
|
|
109
|
+
attr_reader :center1, :radius1, :center2, :radius2, :function, :extend
|
|
110
|
+
|
|
111
|
+
def initialize(color_space:, center1:, radius1:, center2:, radius2:,
|
|
112
|
+
function:, extend: nil)
|
|
113
|
+
super(color_space: color_space)
|
|
114
|
+
@center1 = center1
|
|
115
|
+
@radius1 = radius1
|
|
116
|
+
@center2 = center2
|
|
117
|
+
@radius2 = radius2
|
|
118
|
+
@function = function
|
|
119
|
+
@extend = extend
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
class << self
|
|
123
|
+
def shading_type; 3; end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
protected
|
|
127
|
+
|
|
128
|
+
def shading_fields
|
|
129
|
+
fields = {
|
|
130
|
+
Coords: Pdfrb::Model::PdfArray.new([*@center1, @radius1, *@center2, @radius2]),
|
|
131
|
+
Function: @function,
|
|
132
|
+
}
|
|
133
|
+
fields[:Extend] = Pdfrb::Model::PdfArray.new(@extend) if @extend
|
|
134
|
+
fields
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
module Content
|
|
5
|
+
# Shading patterns (ISO 32000-2 §8.7.4). Shadings define smooth
|
|
6
|
+
# color transitions: axial (linear) gradients, radial gradients,
|
|
7
|
+
# and free-form Gouraud-triangle meshes.
|
|
8
|
+
#
|
|
9
|
+
# Each shading is a dictionary with /ShadingType and type-specific
|
|
10
|
+
# parameters. Registered in page Resources under /Shading.
|
|
11
|
+
module Shading
|
|
12
|
+
autoload :Base, "pdfrb/content/shading/base"
|
|
13
|
+
autoload :Axial, "pdfrb/content/shading/base"
|
|
14
|
+
autoload :Radial, "pdfrb/content/shading/base"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -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
|