Code Duplication    Length = 283-283 lines in 2 locations

htdocs/class/xoopseditor/tinymce4/external_plugins/filemanager/include/classPhpPsdReader.php 1 location

@@ 29-311 (lines=283) @@
26
*	- fix 32 bit colors... has something to do with gamma and exposure available since CS2, but dunno how to read them...
27
*/
28
29
class PhpPsdReader
30
{
31
    public $infoArray;
32
    public $fp;
33
    public $fileName;
34
    public $tempFileName;
35
    public $colorBytesLength;
36
37
    public function __construct($fileName)
38
    {
39
        set_time_limit(0);
40
        $this->infoArray = [];
41
        $this->fileName  = $fileName;
42
        $this->fp        = fopen($this->fileName, 'rb');
43
44
        if ('8BPS' === fread($this->fp, 4)) {
45
            $this->infoArray['version id'] = $this->_getInteger(2);
46
            fseek($this->fp, 6, SEEK_CUR); // 6 bytes of 0's
47
            $this->infoArray['channels']   = $this->_getInteger(2);
48
            $this->infoArray['rows']       = $this->_getInteger(4);
49
            $this->infoArray['columns']    = $this->_getInteger(4);
50
            $this->infoArray['colorDepth'] = $this->_getInteger(2);
51
            $this->infoArray['colorMode']  = $this->_getInteger(2);
52
53
            /* COLOR MODE DATA SECTION */ //4bytes Length The length of the following color data.
54
            $this->infoArray['colorModeDataSectionLength'] = $this->_getInteger(4);
55
            fseek($this->fp, $this->infoArray['colorModeDataSectionLength'], SEEK_CUR); // ignore this snizzle
56
57
            /*  IMAGE RESOURCES */
58
            $this->infoArray['imageResourcesSectionLength'] = $this->_getInteger(4);
59
            fseek($this->fp, $this->infoArray['imageResourcesSectionLength'], SEEK_CUR); // ignore this snizzle
60
61
            /*  LAYER AND MASK */
62
            $this->infoArray['layerMaskDataSectionLength'] = $this->_getInteger(4);
63
            fseek($this->fp, $this->infoArray['layerMaskDataSectionLength'], SEEK_CUR); // ignore this snizzle
64
65
            /*  IMAGE DATA */
66
            $this->infoArray['compressionType']           = $this->_getInteger(2);
67
            $this->infoArray['oneColorChannelPixelBytes'] = $this->infoArray['colorDepth'] / 8;
68
            $this->colorBytesLength                       = $this->infoArray['rows'] * $this->infoArray['columns'] * $this->infoArray['oneColorChannelPixelBytes'];
69
70
            if (2 == $this->infoArray['colorMode']) {
71
                $this->infoArray['error'] = 'images with indexed colours are not supported yet';
72
                return false;
73
            }
74
        } else {
75
            $this->infoArray['error'] = 'invalid or unsupported psd';
76
            return false;
77
        }
78
    }
79
80
    public function getImage()
81
    {
82
        // decompress image data if required
83
        switch ($this->infoArray['compressionType']) {
84
            // case 2:, case 3: zip not supported yet..
85
            case 1:
86
                // packed bits
87
                $this->infoArray['scanLinesByteCounts'] = [];
88
                for ($i = 0; $i < ($this->infoArray['rows'] * $this->infoArray['channels']); $i++) {
89
                    $this->infoArray['scanLinesByteCounts'][] = $this->_getInteger(2);
90
                }
91
                $this->tempFileName = tempnam(realpath('/tmp'), 'decompressedImageData');
92
                $tfp                = fopen($this->tempFileName, 'wb');
93
                foreach ($this->infoArray['scanLinesByteCounts'] as $scanLinesByteCount) {
94
                    fwrite($tfp, $this->_getPackedBitsDecoded(fread($this->fp, $scanLinesByteCount)));
95
                }
96
                fclose($tfp);
97
                fclose($this->fp);
98
                $this->fp = fopen($this->tempFileName, 'rb');
99
            default:
100
                // continue with current file handle;
101
                break;
102
        }
103
104
        // let's write pixel by pixel....
105
        $image = imagecreatetruecolor($this->infoArray['columns'], $this->infoArray['rows']);
106
107
        for ($rowPointer = 0; ($rowPointer < $this->infoArray['rows']); $rowPointer++) {
108
            for ($columnPointer = 0; ($columnPointer < $this->infoArray['columns']); $columnPointer++) {
109
                /* 	The color mode of the file. Supported values are: Bitmap=0;
110
                    Grayscale=1; Indexed=2; RGB=3; CMYK=4; Multichannel=7;
111
                    Duotone=8; Lab=9.
112
                */
113
                switch ($this->infoArray['colorMode']) {
114
                    case 2: // indexed... info should be able to extract from color mode data section. not implemented yet, so is grayscale
115
                        exit;
116
                        break;
117
                    case 0:
118
                        // bit by bit
119
                        if (0 == $columnPointer) {
120
                            $bitPointer = 0;
121
                        }
122
                        if (0 == $bitPointer) {
123
                            $currentByteBits = str_pad(base_convert(bin2hex(fread($this->fp, 1)), 16, 2), 8, '0', STR_PAD_LEFT);
124
                        }
125
                        $r = $g = $b = (('1' == $currentByteBits[$bitPointer]) ? 0 : 255);
126
                        $bitPointer++;
127
                        if (8 == $bitPointer) {
128
                            $bitPointer = 0;
129
                        }
130
                        break;
131
132
                    case 1:
133
                    case 8: // 8 is indexed with 1 color..., so grayscale
134
                        $r = $g = $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
135
                        break;
136
137
                    case 4: // CMYK
138
                        $c                 = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
139
                        $currentPointerPos = ftell($this->fp);
140
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
141
                        $m = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
142
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
143
                        $y = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
144
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
145
                        $k = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
146
                        fseek($this->fp, $currentPointerPos);
147
                        $r = round(($c * $k) / (pow(2, $this->infoArray['colorDepth']) - 1));
148
                        $g = round(($m * $k) / (pow(2, $this->infoArray['colorDepth']) - 1));
149
                        $b = round(($y * $k) / (pow(2, $this->infoArray['colorDepth']) - 1));
150
151
                        break;
152
153
                    case 9: // hunter Lab
154
                        // i still need an understandable lab2rgb convert algorithm... if you have one, please let me know!
155
                        $l                 = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
156
                        $currentPointerPos = ftell($this->fp);
157
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
158
                        $a = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
159
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
160
                        $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
161
                        fseek($this->fp, $currentPointerPos);
162
163
                        $r = $l;
164
                        $g = $a;
165
                        $b = $b;
166
167
                        break;
168
                    default:
169
                        $r                 = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
170
                        $currentPointerPos = ftell($this->fp);
171
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
172
                        $g = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
173
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
174
                        $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
175
                        fseek($this->fp, $currentPointerPos);
176
                        break;
177
                }
178
179
                if ((2 == $this->infoArray['oneColorChannelPixelBytes'])) {
180
                    $r = $r >> 8;
181
                    $g = $g >> 8;
182
                    $b = $b >> 8;
183
                } elseif ((4 == $this->infoArray['oneColorChannelPixelBytes'])) {
184
                    $r = $r >> 24;
185
                    $g = $g >> 24;
186
                    $b = $b >> 24;
187
                }
188
189
                $pixelColor = imagecolorallocate($image, $r, $g, $b);
190
                imagesetpixel($image, $columnPointer, $rowPointer, $pixelColor);
191
            }
192
        }
193
        fclose($this->fp);
194
        if (isset($this->tempFileName)) {
195
            unlink($this->tempFileName);
196
        }
197
        return $image;
198
    }
199
200
    /**
201
     *
202
     * PRIVATE FUNCTIONS
203
     * @param $string
204
     * @return string
205
     */
206
207
    public function _getPackedBitsDecoded($string)
208
    {
209
        /*
210
        The PackBits algorithm will precede a block of data with a one byte header n, where n is interpreted as follows:
211
        n Meaning
212
        0 to 127 Copy the next n + 1 symbols verbatim
213
        -127 to -1 Repeat the next symbol 1 - n times
214
        -128 Do nothing
215
216
        Decoding:
217
        Step 1. Read the block header (n).
218
        Step 2. If the header is an EOF exit.
219
        Step 3. If n is non-negative, copy the next n + 1 symbols to the output stream and go to step 1.
220
        Step 4. If n is negative, write 1 - n copies of the next symbol to the output stream and go to step 1.
221
222
        */
223
224
        $stringPointer = 0;
225
        $returnString  = '';
226
227
        while (1) {
228
            if (isset($string[$stringPointer])) {
229
                $headerByteValue = $this->_unsignedToSigned(hexdec(bin2hex($string[$stringPointer])), 1);
230
            } else {
231
                return $returnString;
232
            }
233
            $stringPointer++;
234
235
            if ($headerByteValue >= 0) {
236
                for ($i = 0; $i <= $headerByteValue; $i++) {
237
                    $returnString .= $string[$stringPointer];
238
                    $stringPointer++;
239
                }
240
            } else {
241
                if (-128 != $headerByteValue) {
242
                    $copyByte = $string[$stringPointer];
243
                    $stringPointer++;
244
245
                    for ($i = 0; $i < (1 - $headerByteValue); $i++) {
246
                        $returnString .= $copyByte;
247
                    }
248
                }
249
            }
250
        }
251
    }
252
253
    public function _unsignedToSigned($int, $byteSize = 1)
254
    {
255
        switch ($byteSize) {
256
            case 1:
257
                if ($int < 128) {
258
                    return $int;
259
                } else {
260
                    return -256 + $int;
261
                }
262
                break;
263
264
            case 2:
265
                if ($int < 32768) {
266
                    return $int;
267
                } else {
268
                    return -65536 + $int;
269
                }
270
271
            case 4:
272
                if ($int < 2147483648) {
273
                    return $int;
274
                } else {
275
                    return -4294967296 + $int;
276
                }
277
278
            default:
279
                return $int;
280
        }
281
    }
282
283
    public function _hexReverse($hex)
284
    {
285
        $output = '';
286
        if (strlen($hex) % 2) {
287
            return false;
288
        }
289
        for ($pointer = strlen($hex); $pointer >= 0; $pointer -= 2) {
290
            $output .= substr($hex, $pointer, 2);
291
        }
292
        return $output;
293
    }
294
295
    public function _getInteger($byteCount = 1)
296
    {
297
        switch ($byteCount) {
298
            case 4:
299
                // for some strange reason this is still broken...
300
                return @reset(unpack('N', fread($this->fp, 4)));
301
                break;
302
303
            case 2:
304
                return @reset(unpack('n', fread($this->fp, 2)));
305
                break;
306
307
            default:
308
                return hexdec($this->_hexReverse(bin2hex(fread($this->fp, $byteCount))));
309
        }
310
    }
311
}
312
313
/**
314
 * Returns an image identifier representing the image obtained from the given filename, using only GD, returns an empty string on failure

htdocs/class/xoopseditor/tinymce4bootstrap/external_plugins/filemanager/include/classPhpPsdReader.php 1 location

@@ 29-311 (lines=283) @@
26
*	- fix 32 bit colors... has something to do with gamma and exposure available since CS2, but dunno how to read them...
27
*/
28
29
class PhpPsdReader
30
{
31
    public $infoArray;
32
    public $fp;
33
    public $fileName;
34
    public $tempFileName;
35
    public $colorBytesLength;
36
37
    public function __construct($fileName)
38
    {
39
        set_time_limit(0);
40
        $this->infoArray = [];
41
        $this->fileName  = $fileName;
42
        $this->fp        = fopen($this->fileName, 'rb');
43
44
        if ('8BPS' === fread($this->fp, 4)) {
45
            $this->infoArray['version id'] = $this->_getInteger(2);
46
            fseek($this->fp, 6, SEEK_CUR); // 6 bytes of 0's
47
            $this->infoArray['channels']   = $this->_getInteger(2);
48
            $this->infoArray['rows']       = $this->_getInteger(4);
49
            $this->infoArray['columns']    = $this->_getInteger(4);
50
            $this->infoArray['colorDepth'] = $this->_getInteger(2);
51
            $this->infoArray['colorMode']  = $this->_getInteger(2);
52
53
            /* COLOR MODE DATA SECTION */ //4bytes Length The length of the following color data.
54
            $this->infoArray['colorModeDataSectionLength'] = $this->_getInteger(4);
55
            fseek($this->fp, $this->infoArray['colorModeDataSectionLength'], SEEK_CUR); // ignore this snizzle
56
57
            /*  IMAGE RESOURCES */
58
            $this->infoArray['imageResourcesSectionLength'] = $this->_getInteger(4);
59
            fseek($this->fp, $this->infoArray['imageResourcesSectionLength'], SEEK_CUR); // ignore this snizzle
60
61
            /*  LAYER AND MASK */
62
            $this->infoArray['layerMaskDataSectionLength'] = $this->_getInteger(4);
63
            fseek($this->fp, $this->infoArray['layerMaskDataSectionLength'], SEEK_CUR); // ignore this snizzle
64
65
            /*  IMAGE DATA */
66
            $this->infoArray['compressionType']           = $this->_getInteger(2);
67
            $this->infoArray['oneColorChannelPixelBytes'] = $this->infoArray['colorDepth'] / 8;
68
            $this->colorBytesLength                       = $this->infoArray['rows'] * $this->infoArray['columns'] * $this->infoArray['oneColorChannelPixelBytes'];
69
70
            if (2 == $this->infoArray['colorMode']) {
71
                $this->infoArray['error'] = 'images with indexed colours are not supported yet';
72
                return false;
73
            }
74
        } else {
75
            $this->infoArray['error'] = 'invalid or unsupported psd';
76
            return false;
77
        }
78
    }
79
80
    public function getImage()
81
    {
82
        // decompress image data if required
83
        switch ($this->infoArray['compressionType']) {
84
            // case 2:, case 3: zip not supported yet..
85
            case 1:
86
                // packed bits
87
                $this->infoArray['scanLinesByteCounts'] = [];
88
                for ($i = 0; $i < ($this->infoArray['rows'] * $this->infoArray['channels']); $i++) {
89
                    $this->infoArray['scanLinesByteCounts'][] = $this->_getInteger(2);
90
                }
91
                $this->tempFileName = tempnam(realpath('/tmp'), 'decompressedImageData');
92
                $tfp                = fopen($this->tempFileName, 'wb');
93
                foreach ($this->infoArray['scanLinesByteCounts'] as $scanLinesByteCount) {
94
                    fwrite($tfp, $this->_getPackedBitsDecoded(fread($this->fp, $scanLinesByteCount)));
95
                }
96
                fclose($tfp);
97
                fclose($this->fp);
98
                $this->fp = fopen($this->tempFileName, 'rb');
99
            default:
100
                // continue with current file handle;
101
                break;
102
        }
103
104
        // let's write pixel by pixel....
105
        $image = imagecreatetruecolor($this->infoArray['columns'], $this->infoArray['rows']);
106
107
        for ($rowPointer = 0; ($rowPointer < $this->infoArray['rows']); $rowPointer++) {
108
            for ($columnPointer = 0; ($columnPointer < $this->infoArray['columns']); $columnPointer++) {
109
                /* 	The color mode of the file. Supported values are: Bitmap=0;
110
                    Grayscale=1; Indexed=2; RGB=3; CMYK=4; Multichannel=7;
111
                    Duotone=8; Lab=9.
112
                */
113
                switch ($this->infoArray['colorMode']) {
114
                    case 2: // indexed... info should be able to extract from color mode data section. not implemented yet, so is grayscale
115
                        exit;
116
                        break;
117
                    case 0:
118
                        // bit by bit
119
                        if (0 == $columnPointer) {
120
                            $bitPointer = 0;
121
                        }
122
                        if (0 == $bitPointer) {
123
                            $currentByteBits = str_pad(base_convert(bin2hex(fread($this->fp, 1)), 16, 2), 8, '0', STR_PAD_LEFT);
124
                        }
125
                        $r = $g = $b = (('1' == $currentByteBits[$bitPointer]) ? 0 : 255);
126
                        $bitPointer++;
127
                        if (8 == $bitPointer) {
128
                            $bitPointer = 0;
129
                        }
130
                        break;
131
132
                    case 1:
133
                    case 8: // 8 is indexed with 1 color..., so grayscale
134
                        $r = $g = $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
135
                        break;
136
137
                    case 4: // CMYK
138
                        $c                 = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
139
                        $currentPointerPos = ftell($this->fp);
140
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
141
                        $m = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
142
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
143
                        $y = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
144
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
145
                        $k = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
146
                        fseek($this->fp, $currentPointerPos);
147
                        $r = round(($c * $k) / (pow(2, $this->infoArray['colorDepth']) - 1));
148
                        $g = round(($m * $k) / (pow(2, $this->infoArray['colorDepth']) - 1));
149
                        $b = round(($y * $k) / (pow(2, $this->infoArray['colorDepth']) - 1));
150
151
                        break;
152
153
                    case 9: // hunter Lab
154
                        // i still need an understandable lab2rgb convert algorithm... if you have one, please let me know!
155
                        $l                 = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
156
                        $currentPointerPos = ftell($this->fp);
157
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
158
                        $a = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
159
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
160
                        $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
161
                        fseek($this->fp, $currentPointerPos);
162
163
                        $r = $l;
164
                        $g = $a;
165
                        $b = $b;
166
167
                        break;
168
                    default:
169
                        $r                 = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
170
                        $currentPointerPos = ftell($this->fp);
171
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
172
                        $g = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
173
                        fseek($this->fp, $this->colorBytesLength - 1, SEEK_CUR);
174
                        $b = $this->_getInteger($this->infoArray['oneColorChannelPixelBytes']);
175
                        fseek($this->fp, $currentPointerPos);
176
                        break;
177
                }
178
179
                if ((2 == $this->infoArray['oneColorChannelPixelBytes'])) {
180
                    $r = $r >> 8;
181
                    $g = $g >> 8;
182
                    $b = $b >> 8;
183
                } elseif ((4 == $this->infoArray['oneColorChannelPixelBytes'])) {
184
                    $r = $r >> 24;
185
                    $g = $g >> 24;
186
                    $b = $b >> 24;
187
                }
188
189
                $pixelColor = imagecolorallocate($image, $r, $g, $b);
190
                imagesetpixel($image, $columnPointer, $rowPointer, $pixelColor);
191
            }
192
        }
193
        fclose($this->fp);
194
        if (isset($this->tempFileName)) {
195
            unlink($this->tempFileName);
196
        }
197
        return $image;
198
    }
199
200
    /**
201
     *
202
     * PRIVATE FUNCTIONS
203
     * @param $string
204
     * @return string
205
     */
206
207
    public function _getPackedBitsDecoded($string)
208
    {
209
        /*
210
        The PackBits algorithm will precede a block of data with a one byte header n, where n is interpreted as follows:
211
        n Meaning
212
        0 to 127 Copy the next n + 1 symbols verbatim
213
        -127 to -1 Repeat the next symbol 1 - n times
214
        -128 Do nothing
215
216
        Decoding:
217
        Step 1. Read the block header (n).
218
        Step 2. If the header is an EOF exit.
219
        Step 3. If n is non-negative, copy the next n + 1 symbols to the output stream and go to step 1.
220
        Step 4. If n is negative, write 1 - n copies of the next symbol to the output stream and go to step 1.
221
222
        */
223
224
        $stringPointer = 0;
225
        $returnString  = '';
226
227
        while (1) {
228
            if (isset($string[$stringPointer])) {
229
                $headerByteValue = $this->_unsignedToSigned(hexdec(bin2hex($string[$stringPointer])), 1);
230
            } else {
231
                return $returnString;
232
            }
233
            $stringPointer++;
234
235
            if ($headerByteValue >= 0) {
236
                for ($i = 0; $i <= $headerByteValue; $i++) {
237
                    $returnString .= $string[$stringPointer];
238
                    $stringPointer++;
239
                }
240
            } else {
241
                if (-128 != $headerByteValue) {
242
                    $copyByte = $string[$stringPointer];
243
                    $stringPointer++;
244
245
                    for ($i = 0; $i < (1 - $headerByteValue); $i++) {
246
                        $returnString .= $copyByte;
247
                    }
248
                }
249
            }
250
        }
251
    }
252
253
    public function _unsignedToSigned($int, $byteSize = 1)
254
    {
255
        switch ($byteSize) {
256
            case 1:
257
                if ($int < 128) {
258
                    return $int;
259
                } else {
260
                    return -256 + $int;
261
                }
262
                break;
263
264
            case 2:
265
                if ($int < 32768) {
266
                    return $int;
267
                } else {
268
                    return -65536 + $int;
269
                }
270
271
            case 4:
272
                if ($int < 2147483648) {
273
                    return $int;
274
                } else {
275
                    return -4294967296 + $int;
276
                }
277
278
            default:
279
                return $int;
280
        }
281
    }
282
283
    public function _hexReverse($hex)
284
    {
285
        $output = '';
286
        if (strlen($hex) % 2) {
287
            return false;
288
        }
289
        for ($pointer = strlen($hex); $pointer >= 0; $pointer -= 2) {
290
            $output .= substr($hex, $pointer, 2);
291
        }
292
        return $output;
293
    }
294
295
    public function _getInteger($byteCount = 1)
296
    {
297
        switch ($byteCount) {
298
            case 4:
299
                // for some strange reason this is still broken...
300
                return @reset(unpack('N', fread($this->fp, 4)));
301
                break;
302
303
            case 2:
304
                return @reset(unpack('n', fread($this->fp, 2)));
305
                break;
306
307
            default:
308
                return hexdec($this->_hexReverse(bin2hex(fread($this->fp, $byteCount))));
309
        }
310
    }
311
}
312
313
/**
314
 * Returns an image identifier representing the image obtained from the given filename, using only GD, returns an empty string on failure