Module: HexaPDF::Filter::RunLengthDecode

Defined in:
lib/hexapdf/filter/run_length_decode.rb

Overview

Implements the run length filter.

See: HexaPDF::Filter, PDF2.0 s7.4.5

Constant Summary collapse

EOD =

:nodoc:

128.chr

Class Method Summary collapse

Class Method Details

.decoder(source, _ = nil) ⇒ Object

See HexaPDF::Filter



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/hexapdf/filter/run_length_decode.rb', line 52

def self.decoder(source, _ = nil)
  Fiber.new do
    i = 0
    result = ''.b
    data = source.resume
    while data && i < data.length
      length = data.getbyte(i)
      if length < 128 && i + length + 1 < data.length # no byte run and enough bytes
        result << data[i + 1, length + 1]
        i += length + 2
      elsif length > 128 && i + 1 < data.length # byte run and enough bytes
        result << data[i + 1] * (257 - length)
        i += 2
      elsif length != 128 # not enough bytes in data
        Fiber.yield(result)
        if source.alive? && (new_data = source.resume)
          data = data[i..-1] << new_data
        else
          raise FilterError, "Missing data for run length encoded stream"
        end
        i = 0
        result = ''.b
      else # EOD reached
        break
      end

      if i == data.length && source.alive? && (data = source.resume)
        Fiber.yield(result)
        i = 0
        result = ''.b
      end
    end
    result unless result.empty?
  end
end

.encoder(source, _ = nil) ⇒ Object

See HexaPDF::Filter



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/hexapdf/filter/run_length_decode.rb', line 89

def self.encoder(source, _ = nil)
  Fiber.new do
    while source.alive? && (data = source.resume)
      result = ''.b
      strscan = StringScanner.new(data)
      until strscan.eos?
        if strscan.scan(/(.)\1{1,127}/m) # a run of <= 128 same characters
          result << (257 - strscan.matched_size).chr << strscan[1]
        else # a run of characters until two same characters or length > 128
          match = strscan.scan(/.{1,128}?(?=(.)\1|\z)|.{128}/m)
          result << (match.length - 1).chr << match
        end
      end
      Fiber.yield(result)
    end
    EOD
  end
end