hexapdf 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -0
- data/README.md +1 -1
- data/lib/hexapdf/cli/command.rb +63 -63
- data/lib/hexapdf/cli/inspect.rb +14 -5
- data/lib/hexapdf/cli/modify.rb +0 -1
- data/lib/hexapdf/cli/optimize.rb +5 -5
- data/lib/hexapdf/composer.rb +14 -0
- data/lib/hexapdf/configuration.rb +26 -0
- data/lib/hexapdf/content/graphics_state.rb +1 -1
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +1 -1
- data/lib/hexapdf/document/annotations.rb +173 -0
- data/lib/hexapdf/document/layout.rb +45 -6
- data/lib/hexapdf/document.rb +28 -7
- data/lib/hexapdf/error.rb +11 -3
- data/lib/hexapdf/font/true_type/subsetter.rb +15 -2
- data/lib/hexapdf/font/true_type_wrapper.rb +1 -0
- data/lib/hexapdf/font/type1_wrapper.rb +1 -0
- data/lib/hexapdf/layout/style.rb +101 -7
- data/lib/hexapdf/object.rb +2 -2
- data/lib/hexapdf/pdf_array.rb +25 -3
- data/lib/hexapdf/tokenizer.rb +4 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +57 -8
- data/lib/hexapdf/type/acro_form/field.rb +1 -0
- data/lib/hexapdf/type/acro_form/form.rb +7 -6
- data/lib/hexapdf/type/acro_form/java_script_actions.rb +9 -2
- data/lib/hexapdf/type/acro_form/text_field.rb +9 -2
- data/lib/hexapdf/type/annotation.rb +71 -1
- data/lib/hexapdf/type/annotations/appearance_generator.rb +348 -0
- data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
- data/lib/hexapdf/type/annotations/border_styling.rb +160 -0
- data/lib/hexapdf/type/annotations/circle.rb +65 -0
- data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
- data/lib/hexapdf/type/annotations/line.rb +490 -0
- data/lib/hexapdf/type/annotations/square.rb +65 -0
- data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
- data/lib/hexapdf/type/annotations/widget.rb +52 -116
- data/lib/hexapdf/type/annotations.rb +8 -0
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +0 -1
- data/lib/hexapdf/xref_section.rb +7 -4
- data/test/hexapdf/content/test_graphics_state.rb +2 -3
- data/test/hexapdf/content/test_operator.rb +4 -5
- data/test/hexapdf/digital_signature/test_cms_handler.rb +7 -8
- data/test/hexapdf/digital_signature/test_handler.rb +2 -3
- data/test/hexapdf/digital_signature/test_pkcs1_handler.rb +1 -2
- data/test/hexapdf/document/test_annotations.rb +55 -0
- data/test/hexapdf/document/test_layout.rb +24 -2
- data/test/hexapdf/font/test_true_type_wrapper.rb +7 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +7 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
- data/test/hexapdf/layout/test_style.rb +27 -2
- data/test/hexapdf/task/test_optimize.rb +1 -1
- data/test/hexapdf/test_composer.rb +7 -0
- data/test/hexapdf/test_document.rb +11 -3
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_pdf_array.rb +36 -3
- data/test/hexapdf/test_stream.rb +1 -2
- data/test/hexapdf/test_xref_section.rb +1 -1
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +78 -3
- data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
- data/test/hexapdf/type/acro_form/test_field.rb +5 -0
- data/test/hexapdf/type/acro_form/test_form.rb +17 -1
- data/test/hexapdf/type/acro_form/test_java_script_actions.rb +21 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +7 -1
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +482 -0
- data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
- data/test/hexapdf/type/annotations/test_border_styling.rb +114 -0
- data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
- data/test/hexapdf/type/annotations/test_line.rb +169 -0
- data/test/hexapdf/type/annotations/test_widget.rb +35 -81
- data/test/hexapdf/type/test_annotation.rb +55 -0
- data/test/hexapdf/type/test_form.rb +6 -0
- metadata +17 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: e0e469ae650b98b48c88b662dab27cb405b676c706639c8c1cf358d6aefc8f53
         | 
| 4 | 
            +
              data.tar.gz: d170526233e1e9aa37403c1bd8d2aa38697641c1b4fb4d4a5f6d8f02f299585b
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: fd18408ed2c2474e3395bf59b3a0281cb23005d16bf7d5b9d93f673dfa131fc215f342018d0cdce2de053db85c000a68c28a76ad980b0432b1a159bd51b1ed0c
         | 
| 7 | 
            +
              data.tar.gz: c58b13ed27c980bb9670b9521a12b1640fc3960aea1188f6135951cfd1503a28e7633cb3f6be8c63d478d64a88e0bbdc9b84bdbc982aa3ec000aeac1a8627597
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,82 @@ | |
| 1 | 
            +
            ## 1.3.0 - 2025-04-23
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ### Added
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            * [HexaPDF::Type::Annotations::Square] for rectangle annotations as well as
         | 
| 6 | 
            +
              [HexaPDF::Document::Annotations#create_rectangle]
         | 
| 7 | 
            +
            * [HexaPDF::Type::Annotations::Circle] for ellipse annotations as well as
         | 
| 8 | 
            +
              [HexaPDF::Document::Annotations#create_ellipse]
         | 
| 9 | 
            +
            * Basic appearance generation for push button fields
         | 
| 10 | 
            +
            * [HexaPDF::Type::Annotation::BorderEffect] type class
         | 
| 11 | 
            +
            * [HexaPDF::Type::Annotations::BorderEffect] module that provides convenience
         | 
| 12 | 
            +
              access to the border effect dictionary
         | 
| 13 | 
            +
            * [HexaPDF::Document::Layout#style?] and [HexaPDF::Composer#style?] for checking
         | 
| 14 | 
            +
              whether a given style (name) exists
         | 
| 15 | 
            +
            * [HexaPDF::Layout::Style#each_property] for iterating over all set properties
         | 
| 16 | 
            +
            * [HexaPDF::Layout::Style#merge] for merging another style instance
         | 
| 17 | 
            +
            * [HexaPDF::Layout::Style#box_options] for specifying box initialization options
         | 
| 18 | 
            +
            * [HexaPDF::Layout::Style#font_bold] and [HexaPDF::Layout::Style#font_italic]
         | 
| 19 | 
            +
              for setting bold and/or italic variants independently of the font name
         | 
| 20 | 
            +
            * [HexaPDF::PDFArray#map!] for mapping elements in-place
         | 
| 21 | 
            +
            * [HexaPDF::PDFArray#compact!] for removing `nil` elements
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ### Changed
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            * **Breaking change**: [HexaPDF::Type::Annotations::Widget::MarkerStyle::new]
         | 
| 26 | 
            +
              got a new positional argument
         | 
| 27 | 
            +
            * [HexaPDF::Type::Annotations::Widget#marker_style] to allow setting and
         | 
| 28 | 
            +
              retrieving the font for push buttons
         | 
| 29 | 
            +
            * Extracted `#interior_color` from [HexaPDF::Type::Annotations::Line] into
         | 
| 30 | 
            +
              [HexaPDF::Type::Annotations::InteriorColor]
         | 
| 31 | 
            +
            * CLI command `hexapdf inspect` to support decoding Form XObject streams
         | 
| 32 | 
            +
            * [HexaPDF::Layout::Style#line_spacing] to accept a `LineSpacing` object when
         | 
| 33 | 
            +
              setting the value
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            ### Fixed
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            * Text extraction with macOS Preview due a bug in Preview
         | 
| 38 | 
            +
            * [HexaPDF::PDFArray#reject!] to work according to documented method signature
         | 
| 39 | 
            +
            * [HexaPDF::Type::AcroForm::Field#create_widget] to ensure the proper type
         | 
| 40 | 
            +
              class is stored in the document in case an embedded widget is extracted
         | 
| 41 | 
            +
            * [HexaPDF::Type::AcroForm::Form] validation to ensure that all field objects in
         | 
| 42 | 
            +
              the field hierarchy are using a field type class
         | 
| 43 | 
            +
            * [HexaPDF::Type::AcroForm::Form] validation to delete merged fields
         | 
| 44 | 
            +
             | 
| 45 | 
            +
             | 
| 46 | 
            +
            ## 1.2.0 - 2025-02-10
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            ### Added
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            * **Breaking change**: Argument `compact` to [HexaPDF::Document#write] to
         | 
| 51 | 
            +
              automatically run the 'compact' optimization task
         | 
| 52 | 
            +
            * [HexaPDF::Document::Annotations], accessible via
         | 
| 53 | 
            +
              [HexaPDF::Document#annotations], as convenience interface for working with
         | 
| 54 | 
            +
              annotations
         | 
| 55 | 
            +
            * [HexaPDF::Type::Annotations::AppearanceGenerator] as central class for
         | 
| 56 | 
            +
              generating appearance streams
         | 
| 57 | 
            +
            * [HexaPDF::Type::Annotations::Line] for line annotations
         | 
| 58 | 
            +
            * [HexaPDF::Type::Annotation#opacity] for setting the opacity values when
         | 
| 59 | 
            +
              regenerating the appearance stream
         | 
| 60 | 
            +
            * [HexaPDF::Type::Annotation#contents] for setting the text of the annotation
         | 
| 61 | 
            +
            * Configuration option 'acro_form.text_field.on_max_len_exceeded' to allow
         | 
| 62 | 
            +
              custom handling of too long values
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            ### Changed
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            * **Breaking change**: Extracted `#border_style` and associated data class from
         | 
| 67 | 
            +
              [HexaPDF::Type::Annotations::Widget] into
         | 
| 68 | 
            +
              [HexaPDF::Type::Annotations::BorderStyling]
         | 
| 69 | 
            +
            * [HexaPDF::Type::Form#canvas] to allow getting the canvas without the initial
         | 
| 70 | 
            +
              translation
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            ### Fixed
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            * AcroForm Javascript actions to gracefully handle the special values infinity
         | 
| 75 | 
            +
              and NaN
         | 
| 76 | 
            +
            * Type1 and TrueType font wrappers to handle the case where fonts are first
         | 
| 77 | 
            +
              added and later deleted
         | 
| 78 | 
            +
             | 
| 79 | 
            +
             | 
| 1 80 | 
             
            ## 1.1.1 - 2025-01-08
         | 
| 2 81 |  | 
| 3 82 | 
             
            ### Fixed
         | 
    
        data/README.md
    CHANGED
    
    | @@ -82,7 +82,7 @@ canvas.text("Hello World!", at: [20, 400]) | |
| 82 82 | 
             
            doc.write("hello-world.pdf")
         | 
| 83 83 | 
             
            ~~~
         | 
| 84 84 |  | 
| 85 | 
            -
            For detailed information have a look at the [HexaPDF website][website] where you will the API
         | 
| 85 | 
            +
            For detailed information have a look at the [HexaPDF website][website] where you will find the API
         | 
| 86 86 | 
             
            documentation, example code and more.
         | 
| 87 87 |  | 
| 88 88 | 
             
            It is recommend to use the HTML API documentation provided by the HexaPDF website as it is enhanced
         | 
    
        data/lib/hexapdf/cli/command.rb
    CHANGED
    
    | @@ -35,7 +35,6 @@ | |
| 35 35 | 
             
            #++
         | 
| 36 36 |  | 
| 37 37 | 
             
            require 'io/console'
         | 
| 38 | 
            -
            require 'ostruct'
         | 
| 39 38 | 
             
            require 'cmdparse'
         | 
| 40 39 | 
             
            require 'hexapdf/document'
         | 
| 41 40 | 
             
            require 'hexapdf/font/true_type'
         | 
| @@ -68,21 +67,22 @@ module HexaPDF | |
| 68 67 |  | 
| 69 68 | 
             
                  def initialize(*args, **kwargs, &block) #:nodoc:
         | 
| 70 69 | 
             
                    super
         | 
| 71 | 
            -
                    @out_options =  | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 70 | 
            +
                    @out_options = {
         | 
| 71 | 
            +
                      compact: true,
         | 
| 72 | 
            +
                      compress_pages: false,
         | 
| 73 | 
            +
                      object_streams: :preserve,
         | 
| 74 | 
            +
                      xref_streams: :preserve,
         | 
| 75 | 
            +
                      streams: :preserve,
         | 
| 76 | 
            +
                      optimize_fonts: false,
         | 
| 77 | 
            +
                      prune_page_resources: false,
         | 
| 78 | 
            +
                      encryption: :preserve,
         | 
| 79 | 
            +
                      enc_user_pwd: nil,
         | 
| 80 | 
            +
                      enc_owner_pwd: nil,
         | 
| 81 | 
            +
                      enc_key_length: 128,
         | 
| 82 | 
            +
                      enc_algorithm: :aes,
         | 
| 83 | 
            +
                      enc_force_v4: false,
         | 
| 84 | 
            +
                      enc_permissions: [],
         | 
| 85 | 
            +
                    }
         | 
| 86 86 | 
             
                  end
         | 
| 87 87 |  | 
| 88 88 | 
             
                  protected
         | 
| @@ -163,7 +163,7 @@ module HexaPDF | |
| 163 163 | 
             
                      if command_parser.verbosity_info?
         | 
| 164 164 | 
             
                        puts "Creating output document #{out_file}"
         | 
| 165 165 | 
             
                      end
         | 
| 166 | 
            -
                      doc.write(out_file, validate: false, incremental: incremental)
         | 
| 166 | 
            +
                      doc.write(out_file, validate: false, compact: false, incremental: incremental)
         | 
| 167 167 | 
             
                    end
         | 
| 168 168 | 
             
                  end
         | 
| 169 169 |  | 
| @@ -183,35 +183,35 @@ module HexaPDF | |
| 183 183 | 
             
                    options.separator("")
         | 
| 184 184 | 
             
                    options.separator("Optimization options:")
         | 
| 185 185 | 
             
                    options.on("--[no-]compact", "Delete unnecessary PDF objects (default: " \
         | 
| 186 | 
            -
                               "#{@out_options | 
| 187 | 
            -
                      @out_options | 
| 186 | 
            +
                               "#{@out_options[:compact]})") do |c|
         | 
| 187 | 
            +
                      @out_options[:compact] = c
         | 
| 188 188 | 
             
                    end
         | 
| 189 189 | 
             
                    options.on("--object-streams MODE", [:generate, :preserve, :delete],
         | 
| 190 190 | 
             
                               "Handling of object streams (either generate, preserve or delete; " \
         | 
| 191 | 
            -
                                 "default: #{@out_options | 
| 192 | 
            -
                      @out_options | 
| 191 | 
            +
                                 "default: #{@out_options[:object_streams]})") do |os|
         | 
| 192 | 
            +
                      @out_options[:object_streams] = os
         | 
| 193 193 | 
             
                    end
         | 
| 194 194 | 
             
                    options.on("--xref-streams MODE", [:generate, :preserve, :delete],
         | 
| 195 195 | 
             
                               "Handling of cross-reference streams (either generate, preserve or delete; " \
         | 
| 196 | 
            -
                                 "default: #{@out_options | 
| 197 | 
            -
                      @out_options | 
| 196 | 
            +
                                 "default: #{@out_options[:xref_streams]})") do |x|
         | 
| 197 | 
            +
                      @out_options[:xref_streams] = x
         | 
| 198 198 | 
             
                    end
         | 
| 199 199 | 
             
                    options.on("--streams MODE", [:compress, :preserve, :uncompress],
         | 
| 200 200 | 
             
                               "Handling of stream data (either compress, preserve or uncompress; default: " \
         | 
| 201 | 
            -
                                 "#{@out_options | 
| 202 | 
            -
                      @out_options | 
| 201 | 
            +
                                 "#{@out_options[:streams]})") do |streams|
         | 
| 202 | 
            +
                      @out_options[:streams] = streams
         | 
| 203 203 | 
             
                    end
         | 
| 204 204 | 
             
                    options.on("--[no-]compress-pages", "Recompress page content streams (may take a long " \
         | 
| 205 | 
            -
                               "time; default: #{@out_options | 
| 206 | 
            -
                      @out_options | 
| 205 | 
            +
                               "time; default: #{@out_options[:compress_pages]})") do |c|
         | 
| 206 | 
            +
                      @out_options[:compress_pages] = c
         | 
| 207 207 | 
             
                    end
         | 
| 208 208 | 
             
                    options.on("--[no-]prune-page-resources", "Prunes unused objects from the page resources " \
         | 
| 209 | 
            -
                               "(may take a long time; default: #{@out_options | 
| 210 | 
            -
                      @out_options | 
| 209 | 
            +
                               "(may take a long time; default: #{@out_options[:prune_page_resources]})") do |c|
         | 
| 210 | 
            +
                      @out_options[:prune_page_resources] = c
         | 
| 211 211 | 
             
                    end
         | 
| 212 212 | 
             
                    options.on("--[no-]optimize-fonts", "Optimize embedded font files; " \
         | 
| 213 | 
            -
                               "default: #{@out_options | 
| 214 | 
            -
                      @out_options | 
| 213 | 
            +
                               "default: #{@out_options[:optimize_fonts]})") do |o|
         | 
| 214 | 
            +
                      @out_options[:optimize_fonts] = o
         | 
| 215 215 | 
             
                    end
         | 
| 216 216 | 
             
                  end
         | 
| 217 217 |  | 
| @@ -222,37 +222,37 @@ module HexaPDF | |
| 222 222 | 
             
                    options.separator("")
         | 
| 223 223 | 
             
                    options.separator("Encryption options:")
         | 
| 224 224 | 
             
                    options.on("--decrypt", "Remove any encryption") do
         | 
| 225 | 
            -
                      @out_options | 
| 225 | 
            +
                      @out_options[:encryption] = :remove
         | 
| 226 226 | 
             
                    end
         | 
| 227 227 | 
             
                    options.on("--encrypt", "Encrypt the output file") do
         | 
| 228 | 
            -
                      @out_options | 
| 228 | 
            +
                      @out_options[:encryption] = :add
         | 
| 229 229 | 
             
                    end
         | 
| 230 230 | 
             
                    options.on("--owner-password PASSWORD", String, "The owner password to be set on the " \
         | 
| 231 231 | 
             
                               "output file (use - for reading from standard input)") do |pwd|
         | 
| 232 | 
            -
                      @out_options | 
| 233 | 
            -
                      @out_options | 
| 232 | 
            +
                      @out_options[:encryption] = :add
         | 
| 233 | 
            +
                      @out_options[:enc_owner_pwd] = (pwd == '-' ? read_password("Owner password") : pwd)
         | 
| 234 234 | 
             
                    end
         | 
| 235 235 | 
             
                    options.on("--user-password PASSWORD", String, "The user password to be set on the " \
         | 
| 236 236 | 
             
                               "output file (use - for reading from standard input)") do |pwd|
         | 
| 237 | 
            -
                      @out_options | 
| 238 | 
            -
                      @out_options | 
| 237 | 
            +
                      @out_options[:encryption] = :add
         | 
| 238 | 
            +
                      @out_options[:enc_user_pwd] = (pwd == '-' ? read_password("User password") : pwd)
         | 
| 239 239 | 
             
                    end
         | 
| 240 240 | 
             
                    options.on("--algorithm ALGORITHM", [:aes, :arc4],
         | 
| 241 241 | 
             
                               "The encryption algorithm: aes or arc4 (default: " \
         | 
| 242 | 
            -
                                 "#{@out_options | 
| 243 | 
            -
                      @out_options | 
| 244 | 
            -
                      @out_options | 
| 242 | 
            +
                                 "#{@out_options[:enc_algorithm]})") do |a|
         | 
| 243 | 
            +
                      @out_options[:encryption] = :add
         | 
| 244 | 
            +
                      @out_options[:enc_algorithm] = a
         | 
| 245 245 | 
             
                    end
         | 
| 246 246 | 
             
                    options.on("--key-length BITS", Integer,
         | 
| 247 247 | 
             
                               "The encryption key length in bits (default: " \
         | 
| 248 | 
            -
                                 "#{@out_options | 
| 249 | 
            -
                      @out_options | 
| 250 | 
            -
                      @out_options | 
| 248 | 
            +
                                 "#{@out_options[:enc_key_length]})") do |i|
         | 
| 249 | 
            +
                      @out_options[:encryption] = :add
         | 
| 250 | 
            +
                      @out_options[:enc_key_length] = i
         | 
| 251 251 | 
             
                    end
         | 
| 252 252 | 
             
                    options.on("--force-V4",
         | 
| 253 253 | 
             
                               "Force use of encryption version 4 if key length=128 and algorithm=arc4") do
         | 
| 254 | 
            -
                      @out_options | 
| 255 | 
            -
                      @out_options | 
| 254 | 
            +
                      @out_options[:encryption] = :add
         | 
| 255 | 
            +
                      @out_options[:enc_force_v4] = true
         | 
| 256 256 | 
             
                    end
         | 
| 257 257 | 
             
                    syms = HexaPDF::Encryption::StandardSecurityHandler::Permissions::SYMBOL_TO_PERMISSION.keys
         | 
| 258 258 | 
             
                    options.on("--permissions PERMS", Array,
         | 
| @@ -264,8 +264,8 @@ module HexaPDF | |
| 264 264 | 
             
                        end
         | 
| 265 265 | 
             
                        perm.to_sym
         | 
| 266 266 | 
             
                      end
         | 
| 267 | 
            -
                      @out_options | 
| 268 | 
            -
                      @out_options | 
| 267 | 
            +
                      @out_options[:encryption] = :add
         | 
| 268 | 
            +
                      @out_options[:enc_permissions] = perms
         | 
| 269 269 | 
             
                    end
         | 
| 270 270 | 
             
                  end
         | 
| 271 271 |  | 
| @@ -273,12 +273,12 @@ module HexaPDF | |
| 273 273 | 
             
                  #
         | 
| 274 274 | 
             
                  # See: #define_optimization_options
         | 
| 275 275 | 
             
                  def apply_optimization_options(doc)
         | 
| 276 | 
            -
                    doc.task(:optimize, compact: @out_options | 
| 277 | 
            -
                             object_streams: @out_options | 
| 278 | 
            -
                             xref_streams: @out_options | 
| 279 | 
            -
                             compress_pages: @out_options | 
| 280 | 
            -
                             prune_page_resources: @out_options | 
| 281 | 
            -
                    if @out_options | 
| 276 | 
            +
                    doc.task(:optimize, compact: @out_options[:compact],
         | 
| 277 | 
            +
                             object_streams: @out_options[:object_streams],
         | 
| 278 | 
            +
                             xref_streams: @out_options[:xref_streams],
         | 
| 279 | 
            +
                             compress_pages: @out_options[:compress_pages],
         | 
| 280 | 
            +
                             prune_page_resources: @out_options[:prune_page_resources])
         | 
| 281 | 
            +
                    if @out_options[:streams] != :preserve || @out_options[:optimize_fonts]
         | 
| 282 282 | 
             
                      doc.each do |obj|
         | 
| 283 283 | 
             
                        optimize_stream(obj)
         | 
| 284 284 | 
             
                        optimize_font(obj)
         | 
| @@ -292,15 +292,15 @@ module HexaPDF | |
| 292 292 |  | 
| 293 293 | 
             
                  # Applies the chosen stream mode to the given object.
         | 
| 294 294 | 
             
                  def optimize_stream(obj)
         | 
| 295 | 
            -
                    return if @out_options | 
| 295 | 
            +
                    return if @out_options[:streams] == :preserve || !obj.respond_to?(:set_filter) ||
         | 
| 296 296 | 
             
                      Array(obj[:Filter]).any? {|f| IGNORED_FILTERS[f] }
         | 
| 297 297 |  | 
| 298 | 
            -
                    obj.set_filter(@out_options | 
| 298 | 
            +
                    obj.set_filter(@out_options[:streams] == :compress ? :FlateDecode : nil)
         | 
| 299 299 | 
             
                  end
         | 
| 300 300 |  | 
| 301 301 | 
             
                  # Optimize the object if it is a font object.
         | 
| 302 302 | 
             
                  def optimize_font(obj)
         | 
| 303 | 
            -
                    return unless @out_options | 
| 303 | 
            +
                    return unless @out_options[:optimize_fonts] && obj.kind_of?(HexaPDF::Type::Font) &&
         | 
| 304 304 | 
             
                      (obj[:Subtype] == :TrueType ||
         | 
| 305 305 | 
             
                       (obj[:Subtype] == :Type0 && obj.descendant_font[:Subtype] == :CIDFontType2)) &&
         | 
| 306 306 | 
             
                      obj.embedded?
         | 
| @@ -319,14 +319,14 @@ module HexaPDF | |
| 319 319 | 
             
                  #
         | 
| 320 320 | 
             
                  # See: #define_encryption_options
         | 
| 321 321 | 
             
                  def apply_encryption_options(doc)
         | 
| 322 | 
            -
                    case @out_options | 
| 322 | 
            +
                    case @out_options[:encryption]
         | 
| 323 323 | 
             
                    when :add
         | 
| 324 | 
            -
                      doc.encrypt(algorithm: @out_options | 
| 325 | 
            -
                                  key_length: @out_options | 
| 326 | 
            -
                                  force_v4: @out_options | 
| 327 | 
            -
                                  permissions: @out_options | 
| 328 | 
            -
                                  owner_password: @out_options | 
| 329 | 
            -
                                  user_password: @out_options | 
| 324 | 
            +
                      doc.encrypt(algorithm: @out_options[:enc_algorithm],
         | 
| 325 | 
            +
                                  key_length: @out_options[:enc_key_length],
         | 
| 326 | 
            +
                                  force_v4: @out_options[:enc_force_v4],
         | 
| 327 | 
            +
                                  permissions: @out_options[:enc_permissions],
         | 
| 328 | 
            +
                                  owner_password: @out_options[:enc_owner_pwd],
         | 
| 329 | 
            +
                                  user_password: @out_options[:enc_user_pwd])
         | 
| 330 330 | 
             
                    when :remove
         | 
| 331 331 | 
             
                      doc.encrypt(name: nil)
         | 
| 332 332 | 
             
                    end
         | 
    
        data/lib/hexapdf/cli/inspect.rb
    CHANGED
    
    | @@ -188,12 +188,20 @@ module HexaPDF | |
| 188 188 | 
             
                              end
         | 
| 189 189 | 
             
                        serialize(obj.value, recursive: true) if obj
         | 
| 190 190 |  | 
| 191 | 
            -
                      when 's', 'stream', 'raw', 'raw-stream'
         | 
| 191 | 
            +
                      when 's', 'stream', 'raw', 'raw-stream', 'sd'
         | 
| 192 192 | 
             
                        if (obj = pdf_object_from_string_reference(data.shift) rescue $stderr.puts($!.message)) &&
         | 
| 193 193 | 
             
                            obj.kind_of?(HexaPDF::Stream)
         | 
| 194 | 
            -
                           | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 194 | 
            +
                          if command == 'sd'
         | 
| 195 | 
            +
                            if obj.respond_to?(:process_contents)
         | 
| 196 | 
            +
                              obj.process_contents(ContentProcessor.new)
         | 
| 197 | 
            +
                            else
         | 
| 198 | 
            +
                              $stderr.puts("Error: The object is not a Form XObject or page")
         | 
| 199 | 
            +
                            end
         | 
| 200 | 
            +
                          else
         | 
| 201 | 
            +
                            source = (command.start_with?('raw') ? obj.stream_source : obj.stream_decoder)
         | 
| 202 | 
            +
                            while source.alive? && (stream_data = source.resume)
         | 
| 203 | 
            +
                              $stdout.write(stream_data)
         | 
| 204 | 
            +
                            end
         | 
| 197 205 | 
             
                          end
         | 
| 198 206 | 
             
                        elsif command_parser.verbosity_info?
         | 
| 199 207 | 
             
                          $stderr.puts("Note: Object has no stream data")
         | 
| @@ -396,7 +404,7 @@ module HexaPDF | |
| 396 404 | 
             
                    io = @doc.revisions.parser.io
         | 
| 397 405 |  | 
| 398 406 | 
             
                    io.seek(0, IO::SEEK_END)
         | 
| 399 | 
            -
                    startxrefs = @doc.revisions.map {|rev| rev.trailer[:Prev] } <<
         | 
| 407 | 
            +
                    startxrefs = @doc.revisions.map {|rev| rev.trailer[:Prev].to_i } <<
         | 
| 400 408 | 
             
                                 @doc.revisions.parser.startxref_offset <<
         | 
| 401 409 | 
             
                                 io.pos
         | 
| 402 410 | 
             
                    startxrefs.sort!
         | 
| @@ -427,6 +435,7 @@ module HexaPDF | |
| 427 435 | 
             
                    ["OID[,GEN] | o[bject] OID[,GEN]", "Print object"],
         | 
| 428 436 | 
             
                    ["r[ecursive] OID[,GEN]", "Print object recursively"],
         | 
| 429 437 | 
             
                    ["s[tream] OID[,GEN]", "Print filtered stream"],
         | 
| 438 | 
            +
                    ["sd OID[,GEN]", "Print the decoded stream of a Form XObject or page"],
         | 
| 430 439 | 
             
                    ["raw[-stream] OID[,GEN]", "Print raw stream"],
         | 
| 431 440 | 
             
                    ["rev[ision] [NUMBER]", "Print or extract revision"],
         | 
| 432 441 | 
             
                    ["x[ref] OID[,GEN]", "Print the cross-reference entry"],
         | 
    
        data/lib/hexapdf/cli/modify.rb
    CHANGED
    
    
    
        data/lib/hexapdf/cli/optimize.rb
    CHANGED
    
    | @@ -53,11 +53,11 @@ module HexaPDF | |
| 53 53 | 
             
                    EOF
         | 
| 54 54 |  | 
| 55 55 | 
             
                    @password = nil
         | 
| 56 | 
            -
                    @out_options | 
| 57 | 
            -
                    @out_options | 
| 58 | 
            -
                    @out_options | 
| 59 | 
            -
                    @out_options | 
| 60 | 
            -
                    @out_options | 
| 56 | 
            +
                    @out_options[:compact] = true
         | 
| 57 | 
            +
                    @out_options[:xref_streams] = :generate
         | 
| 58 | 
            +
                    @out_options[:object_streams] = :generate
         | 
| 59 | 
            +
                    @out_options[:streams] = :compress
         | 
| 60 | 
            +
                    @out_options[:optimize_fonts] = true
         | 
| 61 61 |  | 
| 62 62 | 
             
                    options.on("--password PASSWORD", "-p", String,
         | 
| 63 63 | 
             
                               "The password for decryption. Use - for reading from standard input.") do |pwd|
         | 
    
        data/lib/hexapdf/composer.rb
    CHANGED
    
    | @@ -261,6 +261,20 @@ module HexaPDF | |
| 261 261 | 
             
                  @document.layout.style(name, base: base, **properties)
         | 
| 262 262 | 
             
                end
         | 
| 263 263 |  | 
| 264 | 
            +
                # Returns +true+ if a style with the given +name+ exists, else +false+.
         | 
| 265 | 
            +
                #
         | 
| 266 | 
            +
                # See HexaPDF::Document::Layout#style for details; this method is just a thin wrapper around
         | 
| 267 | 
            +
                # that method.
         | 
| 268 | 
            +
                #
         | 
| 269 | 
            +
                # Example:
         | 
| 270 | 
            +
                #
         | 
| 271 | 
            +
                #   composer.style(:header, font: 'Helvetica')
         | 
| 272 | 
            +
                #   composer.style?(:header)     # => true
         | 
| 273 | 
            +
                #   composer.style?(:paragraph)  # => false
         | 
| 274 | 
            +
                def style?(name)
         | 
| 275 | 
            +
                  @document.layout.style?(name)
         | 
| 276 | 
            +
                end
         | 
| 277 | 
            +
             | 
| 264 278 | 
             
                # :call-seq:
         | 
| 265 279 | 
             
                #    composer.styles              -> styles
         | 
| 266 280 | 
             
                #    composer.styles(**mapping)   -> styles
         | 
| @@ -224,6 +224,21 @@ module HexaPDF | |
| 224 224 | 
             
              # acro_form.text_field.default_width::
         | 
| 225 225 | 
             
              #    A number specifying the default width of AcroForm text fields which should be auto-sized.
         | 
| 226 226 | 
             
              #
         | 
| 227 | 
            +
              # acro_form.text_field.on_max_len_exceeded::
         | 
| 228 | 
            +
              #    Callback hook when the value of a text field exceeds the set maximum length.
         | 
| 229 | 
            +
              #
         | 
| 230 | 
            +
              #    The value needs to be an object that responds to \#call(field, value) where +field+ is the
         | 
| 231 | 
            +
              #    AcroForm text field on which the value is set and +value+ is the invalid value. The returned
         | 
| 232 | 
            +
              #    value is used instead of the invalid value.
         | 
| 233 | 
            +
              #
         | 
| 234 | 
            +
              #    The default implementation raises an error.
         | 
| 235 | 
            +
              #
         | 
| 236 | 
            +
              # annotation.appearance_generator::
         | 
| 237 | 
            +
              #    The class that should be used for generating appearances for annotations. If the value is a
         | 
| 238 | 
            +
              #    String, it should contain the name of a constant to such a class.
         | 
| 239 | 
            +
              #
         | 
| 240 | 
            +
              #    See HexaPDF::Type::Annotations::AppearanceGenerator
         | 
| 241 | 
            +
              #
         | 
| 227 242 | 
             
              # debug::
         | 
| 228 243 | 
             
              #    If set to +true+, enables debug output.
         | 
| 229 244 | 
             
              #
         | 
| @@ -502,6 +517,10 @@ module HexaPDF | |
| 502 517 | 
             
                                      "#{field.concrete_field_type} field named '#{field.full_field_name}'"
         | 
| 503 518 | 
             
                                  end,
         | 
| 504 519 | 
             
                                  'acro_form.text_field.default_width' => 100,
         | 
| 520 | 
            +
                                  'acro_form.text_field.on_max_len_exceeded' => proc do |field, value|
         | 
| 521 | 
            +
                                    raise HexaPDF::Error, "Value exceeds maximum allowed length of #{field[:MaxLen]}"
         | 
| 522 | 
            +
                                  end,
         | 
| 523 | 
            +
                                  'annotation.appearance_generator' => 'HexaPDF::Type::Annotations::AppearanceGenerator',
         | 
| 505 524 | 
             
                                  'debug' => false,
         | 
| 506 525 | 
             
                                  'document.auto_decrypt' => true,
         | 
| 507 526 | 
             
                                  'document.on_invalid_string' => proc do |str|
         | 
| @@ -690,6 +709,7 @@ module HexaPDF | |
| 690 709 | 
             
                                    XXAcroFormField: 'HexaPDF::Type::AcroForm::Field',
         | 
| 691 710 | 
             
                                    XXAppearanceDictionary: 'HexaPDF::Type::Annotation::AppearanceDictionary',
         | 
| 692 711 | 
             
                                    Border: 'HexaPDF::Type::Annotation::Border',
         | 
| 712 | 
            +
                                    XXBorderEffect: 'HexaPDF::Type::Annotation::BorderEffect',
         | 
| 693 713 | 
             
                                    SigFieldLock: 'HexaPDF::Type::AcroForm::SignatureField::LockDictionary',
         | 
| 694 714 | 
             
                                    SV: 'HexaPDF::Type::AcroForm::SignatureField::SeedValueDictionary',
         | 
| 695 715 | 
             
                                    SVCert: 'HexaPDF::Type::AcroForm::SignatureField::CertificateSeedValueDictionary',
         | 
| @@ -746,6 +766,9 @@ module HexaPDF | |
| 746 766 | 
             
                                      Text: 'HexaPDF::Type::Annotations::Text',
         | 
| 747 767 | 
             
                                      Link: 'HexaPDF::Type::Annotations::Link',
         | 
| 748 768 | 
             
                                      Widget: 'HexaPDF::Type::Annotations::Widget',
         | 
| 769 | 
            +
                                      Line: 'HexaPDF::Type::Annotations::Line',
         | 
| 770 | 
            +
                                      Square: 'HexaPDF::Type::Annotations::Square',
         | 
| 771 | 
            +
                                      Circle: 'HexaPDF::Type::Annotations::Circle',
         | 
| 749 772 | 
             
                                      XML: 'HexaPDF::Type::Metadata',
         | 
| 750 773 | 
             
                                      GTS_PDFX: 'HexaPDF::Type::OutputIntent',
         | 
| 751 774 | 
             
                                      GTS_PDFA1: 'HexaPDF::Type::OutputIntent',
         | 
| @@ -774,6 +797,9 @@ module HexaPDF | |
| 774 797 | 
             
                                      Text: 'HexaPDF::Type::Annotations::Text',
         | 
| 775 798 | 
             
                                      Link: 'HexaPDF::Type::Annotations::Link',
         | 
| 776 799 | 
             
                                      Widget: 'HexaPDF::Type::Annotations::Widget',
         | 
| 800 | 
            +
                                      Line: 'HexaPDF::Type::Annotations::Line',
         | 
| 801 | 
            +
                                      Square: 'HexaPDF::Type::Annotations::Square',
         | 
| 802 | 
            +
                                      Circle: 'HexaPDF::Type::Annotations::Circle',
         | 
| 777 803 | 
             
                                    },
         | 
| 778 804 | 
             
                                    XXAcroFormField: {
         | 
| 779 805 | 
             
                                      Tx: 'HexaPDF::Type::AcroForm::TextField',
         | 
| @@ -255,7 +255,7 @@ module HexaPDF | |
| 255 255 | 
             
                      array.inject(0) {|m, n| m < 0 ? m : (n < 0 ? -1 : m + n) } <= 0)
         | 
| 256 256 | 
             
                      raise ArgumentError, "Invalid line dash pattern: #{array.inspect} #{phase.inspect}"
         | 
| 257 257 | 
             
                    end
         | 
| 258 | 
            -
                    @array = array | 
| 258 | 
            +
                    @array = array
         | 
| 259 259 | 
             
                    @phase = phase
         | 
| 260 260 | 
             
                  end
         | 
| 261 261 |  | 
| @@ -108,7 +108,7 @@ module HexaPDF | |
| 108 108 | 
             
                    # +type+::
         | 
| 109 109 | 
             
                    #     The type can either be :cms when creating standard PDF CMS signatures or :pades when
         | 
| 110 110 | 
             
                    #     creating PAdES compatible signatures. PAdES signatures are part of PDF 2.0.
         | 
| 111 | 
            -
                    def create(data, type: :cms, &block) # :yield:  | 
| 111 | 
            +
                    def create(data, type: :cms, &block) # :yield: digest_algorithm, hashed_data
         | 
| 112 112 | 
             
                      signed_attrs = create_signed_attrs(data, signing_time: (type == :cms))
         | 
| 113 113 | 
             
                      signature = digest_and_sign_data(set(*signed_attrs.value).to_der, &block)
         | 
| 114 114 | 
             
                      unsigned_attrs = create_unsigned_attrs(signature)
         |