lib/origami/filters/runlength.rb
=begin This file is part of Origami, PDF manipulation framework for Ruby Copyright (C) 2016 Guillaume Delugré. Origami is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Origami is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Origami. If not, see <http://www.gnu.org/licenses/>. =end module Origami module Filter class InvalidRunLengthDataError < DecodeError #:nodoc: end # # Class representing a Filter used to encode and decode data using RLE compression algorithm. # class RunLength include Filter EOD = 128 #:nodoc: # # Encodes data using RLE compression method. # _stream_:: The data to encode. # def encode(stream) result = ::String.new.b i = 0 while i < stream.size # How many identical bytes coming? length = compute_run_length(stream, i) # If more than 1, then compress them. if length > 1 result << (257 - length).chr << stream[i] i += length # Otherwise how many different bytes to copy? else next_pos = find_next_run(stream, i) length = next_pos - i result << (length - 1).chr << stream[i, length] i += length end end result << EOD.chr end # # Decodes data using RLE decompression method. # _stream_:: The data to decode. # def decode(stream) result = ::String.new.b i = 0 until i >= stream.length or stream[i].ord == EOD do # At least two bytes are required. if i > stream.length - 2 raise InvalidRunLengthDataError.new("Truncated run-length data", input_data: stream, decoded_data: result) end length = stream[i].ord if length < EOD result << stream[i + 1, length + 1] i = i + length + 2 else result << stream[i + 1] * (257 - length) i = i + 2 end end # Check if offset is beyond the end of data. if i > stream.length raise InvalidRunLengthDataError.new("Truncated run-length data", input_data: stream, decoded_data: result) end result end private # # Find the position of the next byte at which a new run starts. # def find_next_run(input, pos) start = pos pos += 1 while pos + 1 < input.size and (pos - start + 1) < EOD and input[pos] != input[pos + 1] pos + 1 end # # Computes the length of the run at the given position. # def compute_run_length(input, pos) run_length = 1 while pos + 1 < input.size and run_length < EOD and input[pos] == input[pos + 1] run_length += 1 pos += 1 end run_length end end RL = RunLength end end