File: depredict.rb

package info (click to toggle)
ruby-pdf-reader 2.15.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,512 kB
  • sloc: ruby: 11,959; sh: 46; makefile: 11
file content (147 lines) | stat: -rw-r--r-- 4,719 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# coding: utf-8
# typed: strict
# frozen_string_literal: true

class PDF::Reader
  module Filter # :nodoc:
    # some filter implementations support preprocessing of the  data to
    # improve compression
    class Depredict

      #: (?Hash[untyped, untyped]) -> void
      def initialize(options = {})
        @options = options
      end

      ################################################################################
      # Streams can be preprocessed to improve compression. This reverses the
      # preprocessing
      #
      #: (String) -> String
      def filter(data)
        predictor = @options[:Predictor].to_i

        case predictor
        when 0, 1 then
          data
        when 2    then
          tiff_depredict(data)
        when 10, 11, 12, 13, 14, 15 then
          png_depredict(data)
        else
          raise  MalformedPDFError, "Unrecognised predictor value (#{predictor})"
        end
      end

      private

      ################################################################################
      #: (untyped) -> String
      def tiff_depredict(data)
        data        = data.unpack("C*")
        unfiltered  = ''
        bpc         = @options[:BitsPerComponent] || 8
        pixel_bits  = bpc * @options[:Colors]
        pixel_bytes = pixel_bits / 8
        line_len    = (pixel_bytes * @options[:Columns])
        pos         = 0

        if bpc != 8
          raise UnsupportedFeatureError, "TIFF predictor onlys supports 8 Bits Per Component"
        end

        until pos > data.size
          row_data = data[pos, line_len]
          row_data.each_with_index do |byte, index|
            left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
            row_data[index] = (byte + left) % 256
          end
          unfiltered += row_data.pack("C*")
          pos += line_len
        end

        unfiltered
      end
      ################################################################################
      #: (untyped) -> String
      def png_depredict(data)
        return data if @options[:Predictor].to_i < 10

        data = data.unpack("C*")

        pixel_bytes     = @options[:Colors] || 1
        scanline_length = (pixel_bytes * @options[:Columns]) + 1
        row = 0
        pixels = []
        paeth, pa, pb, pc = 0, 0, 0, 0
        until data.empty? do
          row_data = data.slice! 0, scanline_length
          filter = row_data.shift
          case filter
          when 0 # None
          when 1 # Sub
            row_data.each_with_index do |byte, index|
              left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]
              row_data[index] = (byte + left) % 256
              #p [byte, left, row_data[index]]
            end
          when 2 # Up
            row_data.each_with_index do |byte, index|
              col = index / pixel_bytes
              upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
              row_data[index] = (upper + byte) % 256
            end
          when 3  # Average
            row_data.each_with_index do |byte, index|
              col = index / pixel_bytes
              upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes]
              left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes]

              row_data[index] = (byte + ((left + upper)/2).floor) % 256
            end
          when 4 # Paeth
            left = upper = upper_left = 0
            row_data.each_with_index do |byte, index|
              col = index / pixel_bytes

              left = index < pixel_bytes ? 0 : Integer(row_data[index - pixel_bytes])
              if row.zero?
                upper = upper_left = 0
              else
                upper = Integer(pixels[row-1][col][index % pixel_bytes])
                upper_left = col.zero? ? 0 :
                  Integer(pixels[row-1][col-1][index % pixel_bytes])
              end

              p = left + upper - upper_left
              pa = (p - left).abs
              pb = (p - upper).abs
              pc = (p - upper_left).abs

              paeth = if pa <= pb && pa <= pc
                        left
                      elsif pb <= pc
                        upper
                      else
                        upper_left
                      end

              row_data[index] = (byte + paeth) % 256
            end
          else
            raise MalformedPDFError, "Invalid filter algorithm #{filter}"
          end

          s = []
          row_data.each_slice pixel_bytes do |slice|
            s << slice
          end
          pixels << s
          row += 1
        end

        pixels.map { |bytes| bytes.flatten.pack("C*") }.join("")
      end
    end
  end
end