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,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pdfrb
|
|
4
|
+
class Document
|
|
5
|
+
# Catalog display controls. Sets viewer behavior: hide toolbar,
|
|
6
|
+
# page layout, page mode, and open-action destination.
|
|
7
|
+
#
|
|
8
|
+
# /ViewerPreferences — fine-grained viewer controls
|
|
9
|
+
# /PageLayout — how pages are laid out in the viewer
|
|
10
|
+
# /PageMode — which panels are visible on open
|
|
11
|
+
# /OpenAction — initial destination or action
|
|
12
|
+
class Display
|
|
13
|
+
PAGE_LAYOUTS = %i[
|
|
14
|
+
SinglePage OneColumn TwoColumnLeft TwoColumnRight
|
|
15
|
+
TwoPageLeft TwoPageRight
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
PAGE_MODES = %i[
|
|
19
|
+
UseNone UseOutlines UseThumbs FullScreen UseOC UseAttachments
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
NON_FULLSCREEN_PAGE_MODES = %i[UseNone UseOutlines UseThumbs UseOC].freeze
|
|
23
|
+
|
|
24
|
+
DIRECTIONS = %i[L2R R2L].freeze
|
|
25
|
+
|
|
26
|
+
attr_reader :document
|
|
27
|
+
|
|
28
|
+
def initialize(document)
|
|
29
|
+
@document = document
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param layout [Symbol] one of PAGE_LAYOUTS.
|
|
33
|
+
def page_layout=(layout)
|
|
34
|
+
raise ArgumentError, "invalid page layout: #{layout}" unless PAGE_LAYOUTS.include?(layout)
|
|
35
|
+
|
|
36
|
+
document.catalog.value[:PageLayout] = layout
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @param mode [Symbol] one of PAGE_MODES.
|
|
40
|
+
def page_mode=(mode)
|
|
41
|
+
raise ArgumentError, "invalid page mode: #{mode}" unless PAGE_MODES.include?(mode)
|
|
42
|
+
|
|
43
|
+
document.catalog.value[:PageMode] = mode
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Set the /OpenAction destination or action.
|
|
47
|
+
# @param dest_or_action [Array, Hash, Pdfrb::Destination::Fit,
|
|
48
|
+
# Pdfrb::Model::PdfArray] the destination array or action dict.
|
|
49
|
+
def open_action=(dest_or_action)
|
|
50
|
+
document.catalog.value[:OpenAction] = dest_or_action
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get or set /ViewerPreferences as a keyword-hash.
|
|
54
|
+
def viewer_preferences(**opts)
|
|
55
|
+
return read_viewer_preferences if opts.empty?
|
|
56
|
+
|
|
57
|
+
vp = document.catalog.value[:ViewerPreferences]
|
|
58
|
+
vp ||= {}
|
|
59
|
+
opts.each do |key, value|
|
|
60
|
+
vp[key] = value
|
|
61
|
+
end
|
|
62
|
+
document.catalog.value[:ViewerPreferences] = vp
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Convenience: hide toolbar and menubar, fit window.
|
|
66
|
+
def presentation_mode!
|
|
67
|
+
viewer_preferences(
|
|
68
|
+
HideToolbar: true,
|
|
69
|
+
HideMenubar: true,
|
|
70
|
+
FitWindow: true,
|
|
71
|
+
CenterWindow: true,
|
|
72
|
+
DisplayDocTitle: true
|
|
73
|
+
)
|
|
74
|
+
self.page_mode = :FullScreen
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def read_viewer_preferences
|
|
80
|
+
document.catalog.value[:ViewerPreferences] || {}
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/pdfrb/document/fonts.rb
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
3
5
|
module Pdfrb
|
|
4
6
|
class Document
|
|
5
|
-
# Font facade. +add+ registers a font in the catalog /Resources
|
|
6
|
-
# (so the canvas can reference it via +Tf+) and returns the
|
|
7
|
-
# resource name to use with the canvas.
|
|
8
|
-
#
|
|
9
|
-
# Supports the 14 PDF standard Type1 fonts by name (Helvetica,
|
|
10
|
-
# Times-Roman, Courier, etc.) without embedding. Future TTF/OTF
|
|
11
|
-
# loaders (TODO 111/112) plug in via +register_loader+.
|
|
12
7
|
class Fonts
|
|
13
8
|
STANDARDS = %w[
|
|
14
9
|
Helvetica Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique
|
|
@@ -17,78 +12,421 @@ module Pdfrb
|
|
|
17
12
|
Symbol ZapfDingbats
|
|
18
13
|
].freeze
|
|
19
14
|
|
|
15
|
+
NO_ENCODING_FONTS = %w[Symbol ZapfDingbats].freeze
|
|
16
|
+
DEFAULT_WIDTH = 500
|
|
17
|
+
|
|
20
18
|
attr_reader :document
|
|
21
19
|
|
|
22
20
|
def initialize(document)
|
|
23
21
|
@document = document
|
|
24
22
|
@next_id = 1
|
|
25
|
-
@registry = {}
|
|
23
|
+
@registry = {}
|
|
24
|
+
@encodings = {}
|
|
25
|
+
@font_dicts = {}
|
|
26
|
+
@afm_metrics = {}
|
|
27
|
+
@used_codepoints = Hash.new { |h, k| h[k] = Set.new }
|
|
28
|
+
@font_streams = {}
|
|
26
29
|
end
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
# For the 14 standard fonts: pass the font name; no embedding
|
|
31
|
-
# is required. For everything else, use the loader protocol
|
|
32
|
-
# (TODO 111).
|
|
33
|
-
def add(name_or_io, **opts)
|
|
31
|
+
def add(name_or_io, **)
|
|
34
32
|
name = font_name_for(name_or_io)
|
|
35
33
|
cached = @registry[name]
|
|
36
34
|
return cached if cached
|
|
37
35
|
|
|
38
36
|
resource = next_resource_name
|
|
39
|
-
register_font(resource, name, **
|
|
37
|
+
font_dict = register_font(resource, name, **)
|
|
38
|
+
@encodings[resource] = font_dict&.value&.[](:Encoding)
|
|
39
|
+
@font_dicts[resource] = font_dict
|
|
40
|
+
load_afm_metrics(resource, name)
|
|
41
|
+
if @pending_io_data
|
|
42
|
+
load_ttf_metrics(resource, @pending_io_data) unless @afm_metrics[resource]
|
|
43
|
+
@font_streams[resource] = @pending_io_data
|
|
44
|
+
@pending_io_data = nil
|
|
45
|
+
end
|
|
46
|
+
@pending_subtype = nil
|
|
47
|
+
if @afm_metrics[resource] && font_dict&.value&.[](:Widths)
|
|
48
|
+
font_dict.value[:Widths] = @afm_metrics[resource][:widths]
|
|
49
|
+
end
|
|
40
50
|
@registry[name] = resource
|
|
41
51
|
resource
|
|
42
52
|
end
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
def [](name)
|
|
46
|
-
@registry[name]
|
|
47
|
-
end
|
|
54
|
+
def [](name); @registry[name]; end
|
|
48
55
|
|
|
49
|
-
def each(&
|
|
56
|
+
def each(&)
|
|
50
57
|
return enum_for(:each) unless block_given?
|
|
51
58
|
|
|
52
|
-
@registry.each(&
|
|
59
|
+
@registry.each(&)
|
|
53
60
|
self
|
|
54
61
|
end
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
def used_codepoints(resource); @used_codepoints[resource]; end
|
|
64
|
+
def encoding_for(resource); @encodings[resource]; end
|
|
65
|
+
|
|
66
|
+
def encode_text(text, resource)
|
|
67
|
+
text.to_s.each_codepoint { |cp| @used_codepoints[resource] << cp }
|
|
68
|
+
enc = @encodings[resource]
|
|
69
|
+
return text.to_s.b unless enc
|
|
70
|
+
|
|
71
|
+
Pdfrb::Font::Encoding.encode(enc, text.to_s)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def encodable?(text, resource); !encode_text(text, resource).include?("?"); end
|
|
75
|
+
|
|
76
|
+
def measure_text(text, font:, size:)
|
|
77
|
+
return 0 unless text && size
|
|
78
|
+
|
|
79
|
+
metrics = @afm_metrics[font]
|
|
80
|
+
return text.to_s.length * size.to_f * 0.5 unless metrics
|
|
81
|
+
|
|
82
|
+
total = text.to_s.each_char.sum do |ch|
|
|
83
|
+
byte = ch.bytes.first || 0
|
|
84
|
+
metrics[:widths][byte] || DEFAULT_WIDTH
|
|
64
85
|
end
|
|
86
|
+
total * size.to_f / 1000.0
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def text_width(text, _resource, size:)
|
|
90
|
+
return 0 unless text && size
|
|
91
|
+
|
|
92
|
+
res = @afm_metrics[_resource] ? _resource : (@registry[_resource.to_s] || _resource)
|
|
93
|
+
metrics = @afm_metrics[res]
|
|
94
|
+
return text.to_s.length * size.to_f * 0.5 unless metrics
|
|
95
|
+
|
|
96
|
+
upem = metrics[:units_per_em] || 1000
|
|
97
|
+
total = text.to_s.each_char.sum do |ch|
|
|
98
|
+
byte = ch.bytes.first || 0
|
|
99
|
+
metrics[:widths][byte] || DEFAULT_WIDTH
|
|
100
|
+
end
|
|
101
|
+
total * size.to_f / upem.to_f
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def glyph_width(resource, codepoint)
|
|
105
|
+
metrics = @afm_metrics[resource]
|
|
106
|
+
return DEFAULT_WIDTH unless metrics
|
|
107
|
+
|
|
108
|
+
cp = codepoint.is_a?(String) ? (codepoint.each_codepoint.first || 0) : codepoint.to_i
|
|
109
|
+
return 0 if cp > 255
|
|
110
|
+
|
|
111
|
+
metrics[:widths][cp] || 0
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def glyph_widths(resource, codepoints)
|
|
115
|
+
cps = codepoints.is_a?(String) ? codepoints.each_codepoint.to_a : codepoints
|
|
116
|
+
cps.map { |cp| glyph_width(resource, cp) }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def metrics_for(resource); @afm_metrics[resource]; end
|
|
120
|
+
|
|
121
|
+
def valid_font_data?(data)
|
|
122
|
+
return false unless data.is_a?(String) && data.bytesize >= 4
|
|
123
|
+
|
|
124
|
+
magic = data.byteslice(0, 4)
|
|
125
|
+
["ttcf".b, "\x00\x01\x00\x00".b, "OTTO".b, "true".b, "typ1".b].include?(magic)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def embedded?(resource)
|
|
129
|
+
dict = @font_dicts[resource]
|
|
130
|
+
return false unless dict
|
|
131
|
+
|
|
132
|
+
desc = dict.value[:FontDescriptor]
|
|
133
|
+
return false unless desc
|
|
134
|
+
|
|
135
|
+
desc = document.object(desc) if desc.is_a?(Pdfrb::Model::Reference)
|
|
136
|
+
desc&.value&.key?(:FontFile2)
|
|
137
|
+
end
|
|
65
138
|
|
|
66
|
-
|
|
67
|
-
|
|
139
|
+
def subset_fonts!
|
|
140
|
+
@font_streams.each do |resource, data|
|
|
141
|
+
next unless valid_font_data?(data)
|
|
142
|
+
|
|
143
|
+
codepoints = @used_codepoints[resource]
|
|
144
|
+
next if codepoints.empty?
|
|
145
|
+
|
|
146
|
+
begin
|
|
147
|
+
ttf = Pdfrb::Font::TrueType::File.new(data)
|
|
148
|
+
subsetter = Pdfrb::Font::TrueType::Subsetter.new(ttf)
|
|
149
|
+
subset = subsetter.subset(codepoints.to_a)
|
|
150
|
+
dict = @font_dicts[resource]
|
|
151
|
+
next unless dict
|
|
152
|
+
|
|
153
|
+
desc_ref = dict.value[:FontDescriptor]
|
|
154
|
+
next unless desc_ref
|
|
155
|
+
|
|
156
|
+
desc = desc_ref.is_a?(Pdfrb::Model::Reference) ? document.object(desc_ref) : desc_ref
|
|
157
|
+
next unless desc
|
|
158
|
+
|
|
159
|
+
fd_stream = document.add({ Length: subset.bytesize }, type: Pdfrb::Model::Cos::Stream)
|
|
160
|
+
fd_stream.stream = subset
|
|
161
|
+
desc.value[:FontFile2] = Pdfrb::Model::Reference.new(fd_stream.oid, fd_stream.gen)
|
|
162
|
+
rescue StandardError
|
|
163
|
+
next
|
|
164
|
+
end
|
|
68
165
|
end
|
|
69
166
|
end
|
|
70
167
|
|
|
168
|
+
class << self
|
|
169
|
+
def loaders; @loaders ||= []; end
|
|
170
|
+
def register_loader(loader); loaders.unshift(loader); end
|
|
171
|
+
end
|
|
172
|
+
|
|
71
173
|
register_loader ->(doc, name, **opts) {
|
|
72
174
|
next nil unless STANDARDS.include?(name.to_s)
|
|
73
|
-
next nil unless opts[:embedded].nil?
|
|
175
|
+
next nil unless opts[:embedded].nil?
|
|
74
176
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
177
|
+
widths = Array.new(256, DEFAULT_WIDTH)
|
|
178
|
+
tu_stream = build_tounicode(doc)
|
|
179
|
+
tu_ref = Pdfrb::Model::Reference.new(tu_stream.oid, tu_stream.gen)
|
|
180
|
+
fd = doc.add({
|
|
181
|
+
Type: :FontDescriptor, FontName: name.to_sym, Flags: 32,
|
|
182
|
+
FontBBox: [0, 0, 1000, 1000], ItalicAngle: 0,
|
|
183
|
+
Ascent: 800, Descent: -200, CapHeight: 700, StemV: 80
|
|
184
|
+
}, type: Pdfrb::Model::Cos::Dictionary)
|
|
185
|
+
fd_ref = Pdfrb::Model::Reference.new(fd.oid, fd.gen)
|
|
186
|
+
font_hash = {
|
|
187
|
+
Type: :Font, Subtype: :Type1, BaseFont: name.to_sym,
|
|
188
|
+
FirstChar: 0, LastChar: 255, Widths: widths,
|
|
189
|
+
FontDescriptor: fd_ref, ToUnicode: tu_ref
|
|
190
|
+
}
|
|
191
|
+
font_hash[:Encoding] = :WinAnsiEncoding unless NO_ENCODING_FONTS.include?(name.to_s)
|
|
192
|
+
doc.add(font_hash, type: Pdfrb::Model::Type::FontType1)
|
|
79
193
|
}
|
|
80
194
|
|
|
195
|
+
def self.build_tounicode(doc)
|
|
196
|
+
cmap_body = build_tounicode_cmap
|
|
197
|
+
stream = doc.add({ Length: cmap_body.bytesize }, type: Pdfrb::Model::Cos::Stream)
|
|
198
|
+
stream.stream = cmap_body
|
|
199
|
+
stream
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def self.build_tounicode_cmap
|
|
203
|
+
lines = []
|
|
204
|
+
lines << "/CIDInit /ProcSet findresource begin"
|
|
205
|
+
lines << "12 dict begin"
|
|
206
|
+
lines << "begincmap"
|
|
207
|
+
lines << "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def"
|
|
208
|
+
lines << "/CMapName /Adobe-Identity-UCS def"
|
|
209
|
+
lines << "/CMapType 2 def"
|
|
210
|
+
lines << "1 begincodespacerange"
|
|
211
|
+
lines << "<00> <FF>"
|
|
212
|
+
lines << "endcodespacerange"
|
|
213
|
+
table = Pdfrb::Font::Encoding::WinAnsiEncoding::TABLE
|
|
214
|
+
pairs = []
|
|
215
|
+
table.each_with_index { |cp, byte| pairs << format("<%02X> <%04X>", byte, cp) if cp }
|
|
216
|
+
pairs.each_slice(100) do |chunk|
|
|
217
|
+
lines << "#{chunk.length} beginbfchar"
|
|
218
|
+
lines.concat(chunk)
|
|
219
|
+
lines << "endbfchar"
|
|
220
|
+
end
|
|
221
|
+
lines << "endcmap"
|
|
222
|
+
lines << "CMapName currentdict /CMap defineresource pop"
|
|
223
|
+
lines << "end"
|
|
224
|
+
lines << "end"
|
|
225
|
+
"#{lines.join("\n")}\n"
|
|
226
|
+
end
|
|
227
|
+
|
|
81
228
|
private
|
|
82
229
|
|
|
230
|
+
def load_afm_metrics(resource, name)
|
|
231
|
+
return unless STANDARDS.include?(name.to_s)
|
|
232
|
+
|
|
233
|
+
afm_path = Pdfrb::DataDir.resolve("afm", "#{name}.afm")
|
|
234
|
+
return unless File.exist?(afm_path)
|
|
235
|
+
|
|
236
|
+
begin
|
|
237
|
+
parser = Pdfrb::Font::AFMParser.from_file(afm_path)
|
|
238
|
+
widths = build_winansi_widths(parser)
|
|
239
|
+
@afm_metrics[resource] = {
|
|
240
|
+
widths: widths,
|
|
241
|
+
ascent: parser.ascender || 800,
|
|
242
|
+
descent: parser.descender || -200,
|
|
243
|
+
cap_height: parser.cap_height || 700,
|
|
244
|
+
bbox: parser.bbox || [0, 0, 1000, 1000],
|
|
245
|
+
units_per_em: 1000,
|
|
246
|
+
}
|
|
247
|
+
rescue StandardError
|
|
248
|
+
nil
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def load_ttf_metrics(resource, data)
|
|
253
|
+
return unless valid_font_data?(data)
|
|
254
|
+
|
|
255
|
+
begin
|
|
256
|
+
ttf = Pdfrb::Font::TrueType::File.new(data)
|
|
257
|
+
widths = Array.new(256, DEFAULT_WIDTH)
|
|
258
|
+
|
|
259
|
+
table = Pdfrb::Font::Encoding::WinAnsiEncoding::TABLE
|
|
260
|
+
table.each_with_index do |cp, byte|
|
|
261
|
+
next unless cp
|
|
262
|
+
|
|
263
|
+
begin
|
|
264
|
+
glyph_id = ttf.cmap.glyph_id_for(cp)
|
|
265
|
+
next unless glyph_id&.positive?
|
|
266
|
+
|
|
267
|
+
w = ttf.hmtx.advance_width(glyph_id)
|
|
268
|
+
widths[byte] = w if w&.positive?
|
|
269
|
+
rescue StandardError
|
|
270
|
+
next
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
head = ttf.head
|
|
275
|
+
hhea = ttf.hhea
|
|
276
|
+
@afm_metrics[resource] = {
|
|
277
|
+
widths: widths,
|
|
278
|
+
ascent: hhea.ascender || 800,
|
|
279
|
+
descent: hhea.descender || -200,
|
|
280
|
+
cap_height: nil,
|
|
281
|
+
bbox: head.bbox || [0, 0, 1000, 1000],
|
|
282
|
+
units_per_em: head.units_per_em || 1000,
|
|
283
|
+
}
|
|
284
|
+
rescue StandardError
|
|
285
|
+
nil
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def build_winansi_widths(parser)
|
|
290
|
+
widths = Array.new(256, DEFAULT_WIDTH)
|
|
291
|
+
(0..127).each do |byte|
|
|
292
|
+
metric = parser.char_metrics[byte]
|
|
293
|
+
widths[byte] = metric[:width].to_i if metric && metric[:width]
|
|
294
|
+
end
|
|
295
|
+
table = Pdfrb::Font::Encoding::WinAnsiEncoding::TABLE
|
|
296
|
+
(128..255).each do |byte|
|
|
297
|
+
cp = table[byte]
|
|
298
|
+
next unless cp
|
|
299
|
+
|
|
300
|
+
glyph_name = unicode_to_glyph_name(cp)
|
|
301
|
+
next unless glyph_name
|
|
302
|
+
|
|
303
|
+
metric = parser.char_metrics[glyph_name]
|
|
304
|
+
widths[byte] = metric[:width].to_i if metric && metric[:width]
|
|
305
|
+
end
|
|
306
|
+
widths
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def unicode_to_glyph_name(cp)
|
|
310
|
+
return cp.chr if cp < 128
|
|
311
|
+
|
|
312
|
+
{ 0x20AC => "Euro", 0x201A => "quotesinglbase", 0x0192 => "florin", 0x201E => "quotedblbase",
|
|
313
|
+
0x2026 => "ellipsis", 0x2020 => "dagger", 0x2021 => "daggerdbl", 0x02C6 => "circumflex",
|
|
314
|
+
0x2030 => "perthousand", 0x0160 => "Scaron", 0x2039 => "guilsinglleft", 0x0152 => "OE",
|
|
315
|
+
0x017D => "Zcaron", 0x2018 => "quoteleft", 0x2019 => "quoteright", 0x201C => "quotedblleft",
|
|
316
|
+
0x201D => "quotedblright", 0x2022 => "bullet", 0x2013 => "endash", 0x2014 => "emdash",
|
|
317
|
+
0x02DC => "tilde", 0x2122 => "trademark", 0x0161 => "scaron", 0x203A => "guilsinglright",
|
|
318
|
+
0x0153 => "oe", 0x017E => "zcaron", 0x0178 => "Ydieresis", 0x00A0 => "space",
|
|
319
|
+
0x00A1 => "exclamdown", 0x00A2 => "cent", 0x00A3 => "sterling", 0x00A4 => "currency",
|
|
320
|
+
0x00A5 => "yen", 0x00A6 => "brokenbar", 0x00A7 => "section", 0x00A8 => "dieresis",
|
|
321
|
+
0x00A9 => "copyright", 0x00AA => "ordfeminine", 0x00AB => "guillemotleft",
|
|
322
|
+
0x00AC => "logicalnot", 0x00AD => "hyphen", 0x00AE => "registered", 0x00AF => "macron",
|
|
323
|
+
0x00B0 => "degree", 0x00B1 => "plusminus", 0x00B2 => "twosuperior", 0x00B3 => "threesuperior",
|
|
324
|
+
0x00B4 => "acute", 0x00B5 => "mu", 0x00B6 => "paragraph", 0x00B7 => "periodcentered",
|
|
325
|
+
0x00B8 => "cedilla", 0x00B9 => "onesuperior", 0x00BA => "ordmasculine",
|
|
326
|
+
0x00BB => "guillemotright", 0x00BC => "onequarter", 0x00BD => "onehalf",
|
|
327
|
+
0x00BE => "threequarters", 0x00BF => "questiondown", 0x00C0 => "Agrave",
|
|
328
|
+
0x00C1 => "Aacute", 0x00C2 => "Acircumflex", 0x00C3 => "Atilde", 0x00C4 => "Adieresis",
|
|
329
|
+
0x00C5 => "Aring", 0x00C6 => "AE", 0x00C7 => "Ccedilla", 0x00C8 => "Egrave",
|
|
330
|
+
0x00C9 => "Eacute", 0x00CA => "Ecircumflex", 0x00CB => "Edieresis", 0x00CC => "Igrave",
|
|
331
|
+
0x00CD => "Iacute", 0x00CE => "Icircumflex", 0x00CF => "Idieresis", 0x00D0 => "Eth",
|
|
332
|
+
0x00D1 => "Ntilde", 0x00D2 => "Ograve", 0x00D3 => "Oacute", 0x00D4 => "Ocircumflex",
|
|
333
|
+
0x00D5 => "Otilde", 0x00D6 => "Odieresis", 0x00D7 => "multiply", 0x00D8 => "Oslash",
|
|
334
|
+
0x00D9 => "Ugrave", 0x00DA => "Uacute", 0x00DB => "Ucircumflex", 0x00DC => "Udieresis",
|
|
335
|
+
0x00DD => "Yacute", 0x00DE => "Thorn", 0x00DF => "germandbls", 0x00E0 => "agrave",
|
|
336
|
+
0x00E1 => "aacute", 0x00E2 => "acircumflex", 0x00E3 => "atilde", 0x00E4 => "adieresis",
|
|
337
|
+
0x00E5 => "aring", 0x00E6 => "ae", 0x00E7 => "ccedilla", 0x00E8 => "egrave",
|
|
338
|
+
0x00E9 => "eacute", 0x00EA => "ecircumflex", 0x00EB => "edieresis", 0x00EC => "igrave",
|
|
339
|
+
0x00ED => "iacute", 0x00EE => "icircumflex", 0x00EF => "idieresis", 0x00F0 => "eth",
|
|
340
|
+
0x00F1 => "ntilde", 0x00F2 => "ograve", 0x00F3 => "oacute", 0x00F4 => "ocircumflex",
|
|
341
|
+
0x00F5 => "otilde", 0x00F6 => "odieresis", 0x00F7 => "divide", 0x00F8 => "oslash",
|
|
342
|
+
0x00F9 => "ugrave", 0x00FA => "uacute", 0x00FB => "ucircumflex", 0x00FC => "udieresis",
|
|
343
|
+
0x00FD => "yacute", 0x00FE => "thorn", 0x00FF => "ydieresis" }[cp]
|
|
344
|
+
end
|
|
345
|
+
|
|
83
346
|
def font_name_for(name_or_io)
|
|
84
347
|
case name_or_io
|
|
85
|
-
when Symbol
|
|
348
|
+
when Symbol
|
|
349
|
+
name_or_io.to_s
|
|
350
|
+
when String
|
|
351
|
+
if File.file?(name_or_io)
|
|
352
|
+
@pending_io_data = File.binread(name_or_io)
|
|
353
|
+
@pending_subtype = true_type_subtype(@pending_io_data)
|
|
354
|
+
subset_tag = subset_tag_for(@pending_io_data)
|
|
355
|
+
base = postscript_name_for(@pending_io_data) ||
|
|
356
|
+
File.basename(name_or_io, ".*")
|
|
357
|
+
"#{subset_tag}+#{base}"
|
|
358
|
+
else
|
|
359
|
+
name_or_io.to_s
|
|
360
|
+
end
|
|
361
|
+
when IO, StringIO
|
|
362
|
+
@pending_io_data = name_or_io.read
|
|
363
|
+
@pending_subtype = true_type_subtype(@pending_io_data)
|
|
364
|
+
unless valid_font_data?(@pending_io_data)
|
|
365
|
+
Pdfrb.logger&.warn("Font data does not look like a valid TTF/OTF")
|
|
366
|
+
end
|
|
367
|
+
subset_tag = subset_tag_for(@pending_io_data)
|
|
368
|
+
base = postscript_name_for(@pending_io_data) ||
|
|
369
|
+
"EmbeddedFont#{@pending_io_data.bytesize}"
|
|
370
|
+
"#{subset_tag}+#{base}"
|
|
86
371
|
else
|
|
87
|
-
raise ArgumentError,
|
|
88
|
-
"font name must be a String or Symbol; TTF/OTF IO loading lands in TODO 111"
|
|
372
|
+
raise ArgumentError, "font name must be a String, Symbol, or IO"
|
|
89
373
|
end
|
|
90
374
|
end
|
|
91
375
|
|
|
376
|
+
# Extract the PostScript name (name table nameID 6) from raw
|
|
377
|
+
# TTF/OTF bytes. Returns nil if the data isn't parseable or the
|
|
378
|
+
# name record is missing. Used to derive a meaningful BaseFont
|
|
379
|
+
# name (e.g. "MinionPro-Regular") instead of falling back to a
|
|
380
|
+
# size-derived placeholder.
|
|
381
|
+
def postscript_name_for(font_bytes)
|
|
382
|
+
return nil unless font_bytes && font_bytes.bytesize >= 12
|
|
383
|
+
return nil unless valid_font_data?(font_bytes)
|
|
384
|
+
|
|
385
|
+
ttf = Pdfrb::Font::TrueType::File.new(font_bytes)
|
|
386
|
+
name = ttf.name_table_parsed
|
|
387
|
+
return nil unless name
|
|
388
|
+
|
|
389
|
+
sanitize_ps_name(name.ps_name)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# PDF /BaseFont names must be a Name token: no whitespace, no
|
|
393
|
+
# delimiter chars (()[<>/{%]). Trim and substitute.
|
|
394
|
+
def sanitize_ps_name(name)
|
|
395
|
+
return nil if name.nil? || name.empty?
|
|
396
|
+
|
|
397
|
+
name.to_s.gsub(/[\s()\[\]<>{}\/%]+/, "-")
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Generate a deterministic 6-letter subset tag from the font bytes
|
|
401
|
+
# so re-embedding the same font produces the same prefix. Format
|
|
402
|
+
# is six uppercase ASCII letters derived from the SHA-1 of the
|
|
403
|
+
# first 1 KB of the font file (per PDF spec s9.6.4).
|
|
404
|
+
def subset_tag_for(font_bytes)
|
|
405
|
+
require "digest"
|
|
406
|
+
digest = Digest::SHA1.digest(font_bytes.byteslice(0, 1024) || "")
|
|
407
|
+
digest.byteslice(0, 6).bytes.each_with_object(+"") do |b, s|
|
|
408
|
+
s << ((b % 26) + 65).chr
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def true_type_subtype(data)
|
|
413
|
+
magic = data&.byteslice(0, 4)
|
|
414
|
+
# Per PDF spec s9.6.2 + s9.9: magic distinguishes the two
|
|
415
|
+
# outline formats. "OTTO" means OpenType with CFF outlines,
|
|
416
|
+
# which must be embedded via /FontFile3 + /Subtype /OpenType
|
|
417
|
+
# and treated as /Subtype /Type1 (CFF) — NOT as /Subtype
|
|
418
|
+
# /TrueType, which was the bug (Issue #62).
|
|
419
|
+
return :TrueType if magic == "\x00\x01\x00\x00".b
|
|
420
|
+
return :TrueType if magic == "true".b
|
|
421
|
+
return :Type1 if magic == "OTTO".b
|
|
422
|
+
|
|
423
|
+
nil
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def cff_font?(data)
|
|
427
|
+
true_type_subtype(data) == :Type1
|
|
428
|
+
end
|
|
429
|
+
|
|
92
430
|
def next_resource_name
|
|
93
431
|
sym = :"F#{@next_id}"
|
|
94
432
|
@next_id += 1
|
|
@@ -97,16 +435,50 @@ module Pdfrb
|
|
|
97
435
|
|
|
98
436
|
def register_font(resource, name, **opts)
|
|
99
437
|
loader = self.class.loaders.find { |l| l.call(document, name, **opts) }
|
|
100
|
-
font_dict = loader ? loader.call(document, name, **opts) :
|
|
101
|
-
default_font(name)
|
|
438
|
+
font_dict = loader ? loader.call(document, name, **opts) : default_font(name)
|
|
102
439
|
attach_to_resources(resource, font_dict)
|
|
440
|
+
font_dict
|
|
103
441
|
end
|
|
104
442
|
|
|
105
443
|
def default_font(name)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
444
|
+
tu_stream = self.class.build_tounicode(document)
|
|
445
|
+
tu_ref = Pdfrb::Model::Reference.new(tu_stream.oid, tu_stream.gen)
|
|
446
|
+
|
|
447
|
+
fd = document.add({
|
|
448
|
+
Type: :FontDescriptor, FontName: name.to_sym, Flags: 32,
|
|
449
|
+
FontBBox: [0, 0, 1000, 1000], ItalicAngle: 0,
|
|
450
|
+
Ascent: 800, Descent: -200, CapHeight: 700, StemV: 80
|
|
451
|
+
}, type: Pdfrb::Model::Cos::Dictionary)
|
|
452
|
+
fd_ref = Pdfrb::Model::Reference.new(fd.oid, fd.gen)
|
|
453
|
+
|
|
454
|
+
if @pending_io_data && valid_font_data?(@pending_io_data)
|
|
455
|
+
font_file = document.add(
|
|
456
|
+
{ Length: @pending_io_data.bytesize },
|
|
457
|
+
type: Pdfrb::Model::Cos::Stream
|
|
458
|
+
)
|
|
459
|
+
font_file.stream = @pending_io_data
|
|
460
|
+
# Per PDF spec s9.9: TrueType outlines → FontFile2; CFF (OTF)
|
|
461
|
+
# outlines → FontFile3 with /Subtype /OpenType. Issue #62
|
|
462
|
+
# was embedding OTF/CFF as TrueType via FontFile2.
|
|
463
|
+
if cff_font?(@pending_io_data)
|
|
464
|
+
font_file.value[:Subtype] = :OpenType
|
|
465
|
+
font_file.value[:Length1] = @pending_io_data.bytesize
|
|
466
|
+
fd.value[:FontFile3] =
|
|
467
|
+
Pdfrb::Model::Reference.new(font_file.oid, font_file.gen)
|
|
468
|
+
else
|
|
469
|
+
font_file.value[:Length1] = @pending_io_data.bytesize
|
|
470
|
+
fd.value[:FontFile2] =
|
|
471
|
+
Pdfrb::Model::Reference.new(font_file.oid, font_file.gen)
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
subtype = @pending_subtype || :Type1
|
|
476
|
+
document.add({
|
|
477
|
+
Type: :Font, Subtype: subtype, BaseFont: name.to_sym,
|
|
478
|
+
Encoding: :WinAnsiEncoding, FirstChar: 0, LastChar: 255,
|
|
479
|
+
Widths: Array.new(256, DEFAULT_WIDTH),
|
|
480
|
+
FontDescriptor: fd_ref, ToUnicode: tu_ref
|
|
481
|
+
}, type: subtype == :TrueType ? Pdfrb::Model::Type::FontTrueType : Pdfrb::Model::Type::FontType1)
|
|
110
482
|
end
|
|
111
483
|
|
|
112
484
|
def attach_to_resources(resource, font_dict)
|