class HexaPDF::Stream
Forward declaration of Stream
to circumvent circular require problem
Implements Stream
objects of the PDF object system.
Stream
Objects¶ ↑
A stream may also be associated with a PDF object but only if the value is a PDF dictionary. This associated dictionary further describes the stream, like its length or how it is encoded.
Such a stream object in PDF contains string data but of possibly unlimited length. Therefore it is used for large amounts of data like images, page descriptions or embedded files.
The basic Object
class cannot hold stream data, only this subclass contains the necessary methods to conveniently work with the stream data!
Note that support for external streams (/F, /FFilter, /FDecodeParms) is not yet implemented!
See: PDF1.7 s7.3.8, Dictionary
Public Instance Methods
Stream
objects must always be indirect.
# File lib/hexapdf/stream.rb, line 148 def must_be_indirect? true end
Sets the filters that should be used for encoding the stream.
The arguments filter
as well as decode_parms
can either be a single items or arrays.
The filters have to be specified in the *decoding order*! For example, if the filters would be [:A85, :Fl], the stream would first be encoded with the Flate and then with the ASCII85 filter.
# File lib/hexapdf/stream.rb, line 244 def set_filter(filter, decode_parms = nil) if filter.nil? || (filter.kind_of?(Array) && filter.empty?) delete(:Filter) else self[:Filter] = filter end if decode_parms.nil? || (decode_parms.kind_of?(Array) && decode_parms.empty?) || !key?(:Filter) delete(:DecodeParms) else self[:DecodeParms] = decode_parms end end
Returns the (possibly decoded) stream data as string.
Note that modifications done to the returned string are not reflected in the Stream
object itself. The modified string must explicitly be assigned via stream=
to take effect.
# File lib/hexapdf/stream.rb, line 166 def stream if data.stream.kind_of?(String) data.stream.dup else HexaPDF::Filter.string_from_source(stream_decoder) end end
Assigns a new stream data object.
The stream
argument can be a HexaPDF::StreamData
object, a String object or nil
.
If stream
is nil
, an empty binary string is used instead.
# File lib/hexapdf/stream.rb, line 157 def stream=(stream) data.stream = stream after_data_change end
Returns the decoder Fiber for the stream data.
See the Filter
module for more information on how to work with the fiber.
# File lib/hexapdf/stream.rb, line 194 def stream_decoder source = stream_source if data.stream.kind_of?(StreamData) data.stream.filter.zip(data.stream.decode_parms) do |filter, decode_parms| source = filter_for_name(filter).decoder(source, decode_parms) end end source end
Returns the encoder Fiber for the stream data.
See the Filter
module for more information on how to work with the fiber.
# File lib/hexapdf/stream.rb, line 212 def stream_encoder(source = stream_source) encoder_data = [document.unwrap(self[:Filter])].flatten. zip([document.unwrap(self[:DecodeParms])].flatten). delete_if {|f, _| f.nil? } if data.stream.kind_of?(StreamData) decoder_data = data.stream.filter.zip(data.stream.decode_parms) while !decoder_data.empty? && !encoder_data.empty? && decoder_data.last == encoder_data.last decoder_data.pop encoder_data.pop end decoder_data.each do |filter, decode_parms| source = filter_for_name(filter).decoder(source, decode_parms) end end encoder_data.reverse!.each do |filter, decode_parms| source = filter_for_name(filter).encoder(source, decode_parms) end source end
Returns the Fiber representing the unprocessed content of the stream.
# File lib/hexapdf/stream.rb, line 183 def stream_source if data.stream.kind_of?(String) HexaPDF::Filter.source_from_string(data.stream) else data.stream.fiber(config['io.chunk_size']) end end
Private Instance Methods
Makes sure that the stream data is either a String or a HexaPDF::StreamData
object.
HexaPDF::Object#after_data_change
# File lib/hexapdf/stream.rb, line 261 def after_data_change super data.stream ||= ''.b unless data.stream.kind_of?(StreamData) || data.stream.kind_of?(String) raise ArgumentError, "Object of class #{data.stream.class} cannot be used as stream value" end end
Returns the filter object that corresponds to the given filter name.
See: HexaPDF::Filter
# File lib/hexapdf/stream.rb, line 272 def filter_for_name(filter_name) config.constantize('filter.map', filter_name) do raise HexaPDF::Error, "Unknown stream filter '#{filter_name}' encountered" end end