diff --git a/lib/rubrik.rb b/lib/rubrik.rb index 1f67254..cf4a487 100644 --- a/lib/rubrik.rb +++ b/lib/rubrik.rb @@ -3,7 +3,6 @@ require "sorbet-runtime" require "pdf-reader" -require "irb" module Rubrik class Error < StandardError; end diff --git a/lib/rubrik/document.rb b/lib/rubrik/document.rb index 270ad08..e8dd5fd 100644 --- a/lib/rubrik/document.rb +++ b/lib/rubrik/document.rb @@ -12,19 +12,18 @@ class Document SIGNATURE_SIZE = 8_192 sig {returns(T.any(File, Tempfile, StringIO))} - attr_reader :io + attr_accessor :io sig {returns(PDF::Reader::ObjectHash)} - attr_reader :objects + attr_accessor :objects sig {returns(T::Array[{id: PDF::Reader::Reference, value: T.untyped}])} - attr_reader :modified_objects - - sig {returns(PDF::Reader::Reference)} - attr_reader :interactive_form_id + attr_accessor :modified_objects sig {returns(Integer)} - attr_reader :last_object_id + attr_accessor :last_object_id + + private :io=, :objects=, :modified_objects=, :last_object_id= sig {params(input: T.any(File, Tempfile, StringIO)).void} def initialize(input) @@ -63,7 +62,7 @@ def add_signature_field V: signature_value_id, Type: :Annot, Subtype: :Widget, - Rect: [20, 20, 120, 120], + Rect: [0, 0, 0, 0], F: 4, P: first_page_reference } @@ -87,7 +86,7 @@ def interactive_form sig {void} def fetch_or_create_interactive_form! root_ref = objects.trailer[:Root] - root = T.let(objects.fetch(root_ref), Hash) + root = T.let(objects.fetch(root_ref), T::Hash[Symbol, T.untyped]) if root.key?(:AcroForm) form_id = root[:AcroForm] @@ -111,17 +110,5 @@ def fetch_or_create_interactive_form! def assign_new_object_id! PDF::Reader::Reference.new(self.last_object_id += 1, 0) end - - sig {params(io: T.any(File, Tempfile, StringIO)).returns(T.any(File, Tempfile, StringIO))} - attr_writer :io - - sig {params(objects: PDF::Reader::ObjectHash).returns(PDF::Reader::ObjectHash)} - attr_writer :objects - - sig {params(modified_objects: T::Array[{id: PDF::Reader::Reference, value: T.untyped}]).returns(T::Array[{id: PDF::Reader::Reference, value: T.untyped}])} - attr_writer :modified_objects - - sig {params(last_object_id: Integer).returns(Integer)} - attr_writer :last_object_id end end diff --git a/lib/rubrik/document/increment.rb b/lib/rubrik/document/increment.rb index cd79f6c..b9783f3 100644 --- a/lib/rubrik/document/increment.rb +++ b/lib/rubrik/document/increment.rb @@ -9,13 +9,15 @@ module Increment extend T::Sig extend self - sig {params(document: Rubrik::Document, io: T.any(File, Tempfile, StringIO)).returns(T.any(File, Tempfile, StringIO))} + sig {params(document: Rubrik::Document, io: T.any(File, Tempfile, StringIO)).void} def call(document, io:) document.io.rewind - IO.copy_stream(T.unsafe(document.io), T.unsafe(io)) + IO.copy_stream(document.io, io) io << "\n" - new_xref = Array.new + + new_xref = T.let([], T::Array[T::Hash[Symbol, Integer]]) + new_xref << {id: 0} document.modified_objects.each do |object| integer_id = T.let(object[:id].to_i, Integer) @@ -31,18 +33,22 @@ def call(document, io:) new_xref_pos = io.pos new_xref_subsections = new_xref - .sort_by { _1[:id] } - .chunk_while { _1[:id] + 1 == _2[:id] } + .sort_by { |entry| entry.fetch(:id) } + .chunk_while { _1.fetch(:id) + 1 == _2[:id] } io << "xref\n" - io << "0 1\n" - io << "0000000000 65535 f\n" new_xref_subsections.each do |subsection| - starting_id = subsection.first[:id] + starting_id = T.must(subsection.first).fetch(:id) length = subsection.length io << "#{starting_id} #{length}\n" + + if starting_id.zero? + io << "0000000000 65535 f\n" + subsection.shift + end + subsection.each { |entry| io << "#{format("%010d", entry[:offset])} 00000 n\n" } end @@ -51,9 +57,6 @@ def call(document, io:) io << "startxref\n" io << "#{new_xref_pos.to_s}\n" io << "%%EOF\n" - - io.rewind - io end private diff --git a/lib/rubrik/fill_signature.rb b/lib/rubrik/fill_signature.rb index 61d2814..d739ad5 100644 --- a/lib/rubrik/fill_signature.rb +++ b/lib/rubrik/fill_signature.rb @@ -15,7 +15,7 @@ module FillSignature private_key: OpenSSL::PKey::RSA, public_key: OpenSSL::X509::Certificate, certificate_chain: T::Array[OpenSSL::X509::Certificate]) - .returns(T.any(File, StringIO, Tempfile))} + .void} FIRST_OFFSET = 0 @@ -29,8 +29,7 @@ def call(io, signature_value_ref:, private_key:, public_key:, certificate_chain: io.gets("<") first_length = io.pos - 1 - # we need to double the SIGNATURE_SIZE because the hex encoding double the data size - # we also need to sum +2 to account for "<" and ">" of the hex string + # We need to sum +2 to account for "<" and ">" of the hex string second_offset = first_length + Document::SIGNATURE_SIZE + 2 second_length = io.size - second_offset diff --git a/lib/rubrik/sign.rb b/lib/rubrik/sign.rb index 4406837..4a84a63 100644 --- a/lib/rubrik/sign.rb +++ b/lib/rubrik/sign.rb @@ -13,6 +13,9 @@ module Sign certificate_chain: T::Array[OpenSSL::X509::Certificate]) .void} def self.call(input, output, private_key:, public_key:, certificate_chain: []) + input.binmode + output.reopen(T.unsafe(output), "wb+") if !output.is_a?(StringIO) + document = Rubrik::Document.new(input) document.add_signature_field