Module: HexaPDF::Filter::ASCII85Decode

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

Overview

This filter module implements the ASCII-85 filter which can encode arbitrary data into an ASCII compatible format that expands the original data only by a factor of 4:5.

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

Constant Summary collapse

VALUE_TO_CHAR =

:nodoc:

{}
POW85_1 =

:nodoc:

85
POW85_2 =

:nodoc:

85**2
POW85_3 =

:nodoc:

85**3
POW85_4 =

:nodoc:

85**4
MAX_VALUE =

:nodoc:

0xffffffff
FIXED_SUBTRAHEND =

:nodoc:

33 * (POW85_4 + POW85_3 + POW85_2 + POW85_1 + 1)

Class Method Summary collapse

Class Method Details

.decoder(source, _ = nil) ⇒ Object

See HexaPDF::Filter



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/hexapdf/filter/ascii85_decode.rb', line 65

def self.decoder(source, _ = nil)
  Fiber.new do
    rest = nil
    finished = false

    while !finished && source.alive? && (data = source.resume)
      data.tr!(HexaPDF::Tokenizer::WHITESPACE, '')
      if data.index(/[^!-uz~]/)
        raise FilterError, "Invalid characters in ASCII85 stream"
      end

      if rest
        data = rest << data
        rest = nil
      end

      result = []
      scanner = StringScanner.new(data)
      until scanner.eos?
        if (m = scanner.scan(/[!-u]{5}/))
          num = (m.getbyte(0) * POW85_4 + m.getbyte(1) * POW85_3 +
            m.getbyte(2) * POW85_2 + m.getbyte(3) * POW85_1 +
            m.getbyte(4)) - FIXED_SUBTRAHEND
          if num > MAX_VALUE
            raise FilterError, "Value outside range in ASCII85 stream"
          end
          result << num
        elsif scanner.scan(/z/)
          result << 0
        elsif scanner.scan(/([!-u]{0,4})~>/)
          rest = scanner[1] unless scanner[1].empty?
          finished = true
          break
        else
          rest = scanner.scan(/.+/)
        end
      end
      Fiber.yield(result.pack('N*')) unless result.empty?
    end

    if rest
      if rest.index('z') || rest.index('~')
        raise FilterError, "End of ASCII85 encoded stream is invalid"
      end

      rlen = rest.length
      rest << "u" * (5 - rlen)
      num = (rest.getbyte(0) * POW85_4 + rest.getbyte(1) * POW85_3 +
        rest.getbyte(2) * POW85_2 + rest.getbyte(3) * POW85_1 +
        rest.getbyte(4)) - FIXED_SUBTRAHEND
      if num > MAX_VALUE
        raise FilterError, "Value outside base-85 range in ASCII85 stream"
      end
      [num].pack('N')[0, rlen - 1]
    end
  end
end

.encoder(source, _ = nil) ⇒ Object

See HexaPDF::Filter



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/hexapdf/filter/ascii85_decode.rb', line 124

def self.encoder(source, _ = nil)
  Fiber.new do
    rest = nil

    while source.alive? && (data = source.resume)
      data = rest << data if rest

      rlen = data.length % 4
      rest = (rlen != 0 ? data.slice!(-rlen, rlen) : nil)
      next if data.length < 4

      data = data.unpack('N*').inject(''.b) do |memo, num|
        memo << if num == 0
                  'z'
                else
                  VALUE_TO_CHAR[num / POW85_4 % 85] + VALUE_TO_CHAR[num / POW85_3 % 85] <<
                    VALUE_TO_CHAR[num / POW85_2 % 85] << VALUE_TO_CHAR[num / POW85_1 % 85] <<
                    VALUE_TO_CHAR[num % 85]
                end
      end

      Fiber.yield(data)
    end

    if rest
      rlen = rest.length
      num = (rest + "\0" * (4 - rlen)).unpack1('N')
      ((VALUE_TO_CHAR[num / POW85_4 % 85] + VALUE_TO_CHAR[num / POW85_3 % 85] <<
        VALUE_TO_CHAR[num / POW85_2 % 85] << VALUE_TO_CHAR[num / POW85_1 % 85] <<
        VALUE_TO_CHAR[num % 85])[0, rlen + 1] << "~>").force_encoding(Encoding::BINARY)
    else
      "~>".b
    end
  end
end