Skip to content

Commit

Permalink
Merge pull request #498 from yob/fix-reading-tempfile
Browse files Browse the repository at this point in the history
Ensure PDFs can be read from Tempfile instances
  • Loading branch information
yob authored Jul 2, 2022
2 parents 01f2739 + 8877391 commit aacc768
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 12 deletions.
6 changes: 4 additions & 2 deletions lib/pdf/reader/object_hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# typed: true
# frozen_string_literal: true

require 'tempfile'

class PDF::Reader
# Provides low level access to the objects in a PDF file via a hash-like
# object.
Expand Down Expand Up @@ -597,12 +599,12 @@ def read_version
end

def extract_io_from(input)
if input.is_a?(IO) || input.is_a?(StringIO)
if input.is_a?(IO) || input.is_a?(StringIO) || input.is_a?(Tempfile)
input
elsif File.file?(input.to_s)
StringIO.new read_as_binary(input.to_s)
else
raise ArgumentError, "input must be an IO-like object or a filename"
raise ArgumentError, "input must be an IO-like object or a filename (#{input.class})"
end
end

Expand Down
18 changes: 9 additions & 9 deletions rbi/pdf-reader.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module PDF
sig { returns(PDF::Reader::ObjectHash) }
attr_reader :objects

sig { params(input: T.any(String, IO), opts: T::Hash[T.untyped, T.untyped]).void }
sig { params(input: T.any(String, Tempfile, IO), opts: T::Hash[T.untyped, T.untyped]).void }
def initialize(input, opts = {})
@cache = T.let(T.unsafe(nil), PDF::Reader::ObjectCache)
@objects = T.let(T.unsafe(nil), PDF::Reader::ObjectHash)
Expand All @@ -24,7 +24,7 @@ module PDF
sig { returns(Float) }
def pdf_version; end

sig { params(input: T.any(String, IO), opts: T::Hash[T.untyped, T.untyped], block: T.proc.params(arg0: PDF::Reader).void).returns(T.untyped) }
sig { params(input: T.any(String, Tempfile, IO), opts: T::Hash[T.untyped, T.untyped], block: T.proc.params(arg0: PDF::Reader).void).returns(T.untyped) }
def self.open(input, opts = {}, &block); end

sig { returns(T::Array[PDF::Reader::Page]) }
Expand Down Expand Up @@ -93,11 +93,11 @@ module PDF
sig { returns(Integer) }
attr_reader :pos

sig { params(io: T.any(StringIO, IO), opts: T::Hash[Symbol, T.untyped]).void }
sig { params(io: T.any(StringIO, Tempfile, IO), opts: T::Hash[Symbol, T.untyped]).void }
def initialize(io, opts = {})
@pos = T.let(T.unsafe(nil), Integer)
@tokens = T.let(T.unsafe(nil), T::Array[T.any(String, PDF::Reader::Reference)])
@io = T.let(T.unsafe(nil), T.any(StringIO, IO))
@io = T.let(T.unsafe(nil), T.any(StringIO, Tempfile, IO))
@in_content_stream = T.let(T.unsafe(nil), T::Boolean)
end

Expand Down Expand Up @@ -657,9 +657,9 @@ module PDF
)) }
attr_reader :sec_handler

sig { params(input: T.any(IO, StringIO, String), opts: T::Hash[Symbol, T.untyped]).void }
sig { params(input: T.any(IO, Tempfile, StringIO, String), opts: T::Hash[Symbol, T.untyped]).void }
def initialize(input, opts = {})
@io = T.let(T.unsafe(nil), T.any(IO, StringIO))
@io = T.let(T.unsafe(nil), T.any(IO, Tempfile, StringIO))
@xref = T.let(T.unsafe(nil), PDF::Reader::XRef)
@pdf_version = T.let(T.unsafe(nil), Float)
@trailer = T.let(T.unsafe(nil), T::Hash[Symbol, T.untyped])
Expand Down Expand Up @@ -800,7 +800,7 @@ module PDF
sig { returns(Float) }
def read_version; end

sig { params(input: T.any(IO, StringIO, String)).returns(T.any(IO, StringIO)) }
sig { params(input: T.any(IO, Tempfile, StringIO, String)).returns(T.any(IO, Tempfile, StringIO)) }
def extract_io_from(input); end

sig { params(input: String).returns(String) }
Expand Down Expand Up @@ -1842,9 +1842,9 @@ module PDF
sig { returns(T::Hash[Symbol, T.untyped]) }
attr_reader :trailer

sig { params(io: T.any(IO, StringIO)).void }
sig { params(io: T.any(IO, Tempfile, StringIO)).void }
def initialize(io)
@io = T.let(T.unsafe(nil), T.any(IO, StringIO))
@io = T.let(T.unsafe(nil), T.any(IO, Tempfile, StringIO))
@junk_offset = T.let(T.unsafe(nil), Integer)
@xref = T.let(T.unsafe(nil), T::Hash[Integer, T::Hash[Integer, Integer]])
@trailer = T.let(T.unsafe(nil), T::Hash[Symbol, T.untyped])
Expand Down
21 changes: 20 additions & 1 deletion spec/object_hash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,27 @@
expect(h.map { |ref, obj| obj.class }.size).to eql(57)
end

it "correctly loads a PDF from a string containing a file path" do
filename = pdf_spec_file("cairo-unicode")
h = PDF::Reader::ObjectHash.new(filename)

expect(h.map { |ref, obj| obj.class }.size).to eql(57)
end

# This means PDF::Reader.open(URI.open("https://example.com/foo.pdf")) will work
it "correctly loads a PDF via a Tempfile object" do
filename = pdf_spec_file("cairo-unicode")

Tempfile.open('foo.pdf') do |tempfile|
tempfile.write binread(filename)
tempfile.rewind
h = PDF::Reader::ObjectHash.new(tempfile)

expect(h.map { |ref, obj| obj.class }.size).to eql(57)
end
end

it "raises an ArgumentError if passed a non filename and non IO" do
pdf_spec_file("cairo-unicode")
expect {PDF::Reader::ObjectHash.new(10)}.to raise_error(ArgumentError)
end
end
Expand Down

0 comments on commit aacc768

Please sign in to comment.