PluginProbe
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin / 2.4.2
PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin v2.4.2
trunk 1.5 1.6 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 All 71 releases
pdf-print / mpdf / utils / image_details.php

image_details.php in PDF & Print by BestWebSoft – WordPress Posts and Pages PDF Generator Plugin 2.4.2, at mpdf/utils/image_details.php

521 lines 18.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 $orig_srcpath='../tests/tiger.png'; // as specified in your file (could be full URL)
4 $file = 'http://127.0.0.1/MPDF1.com/common/mpdf/tests/tiger.png'; // Full URL
5 $fileIsLocal = true; // is the file in the same domain?
6
7 //======================================================================
8
9 $ppUx = 0;
10 $type = '';
11 $data = '';
12
13 echo 'File: ' .$orig_srcpath . '<br />';
14 echo 'Full File URL: ' .$file . '<br />';
15 if ($orig_srcpath && $fileIsLocal && $check = @fopen($orig_srcpath,"rb")) {
16 fclose($check);
17 $file=$orig_srcpath;
18 $data = file_get_contents($file);
19 $type = _imageTypeFromString($data);
20 echo 'File accessed using fopen on $orig_srcpath' . '<br />';
21 }
22 if (!$data && $check = @fopen($file,"rb")) {
23 fclose($check);
24 $data = file_get_contents($file);
25 $type = _imageTypeFromString($data);
26 echo 'File accessed using fopen on $file' . '<br />';
27 }
28 if ((!$data || !$type) && !ini_get('allow_url_fopen') ) { // only worth trying if remote file and !ini_get('allow_url_fopen')
29 file_get_contents_by_socket($file, $data); // needs full url?? even on local (never needed for local)
30 if ($data) { $type = _imageTypeFromString($data); }
31 echo 'File accessed using socket ' . '<br />';
32 }
33 if ((!$data || !$type) && !ini_get('allow_url_fopen') && function_exists("curl_init")) {
34 file_get_contents_by_curl($file, $data); // needs full url?? even on local (never needed for local)
35 if ($data) { $type = _imageTypeFromString($data); }
36 echo 'File accessed using cURL ' . '<br />';
37 }
38
39
40
41 if (!$data) { echo 'Could not access image file' . '<br />'; exit; }
42
43 echo 'Image type determined: ' .strtoupper($type) . '<br />';
44
45 // JPEG
46 if ($type == 'jpeg' || $type == 'jpg') {
47 $hdr = _jpgHeaderFromString($data);
48 if (!$hdr) { echo 'Error parsing JPG header' . '<br />'; exit; }
49 $a = _jpgDataFromHeader($hdr);
50 $channels = intval($a[4]);
51 echo 'Width: ' . $a[0]. '<br />';
52 echo 'Height: ' . $a[1]. '<br />';
53 echo 'Colorspace: ' . $a[2]. '<br />';
54 echo 'BPC (bits per channel): ' . $a[3]. '<br />';
55 echo 'Channels: ' . $channels. '<br />';
56
57 $j = strpos($data,'JFIF');
58 if ($j) {
59 //Read resolution
60 $unitSp=ord(substr($data,($j+7),1));
61 if ($unitSp > 0) {
62 $ppUx=_twobytes2int(substr($data,($j+8),2)); // horizontal pixels per meter, usually set to zero
63 if ($unitSp == 2) { // = dots per cm (if == 1 set as dpi)
64 $ppUx=round($ppUx/10 *25.4);
65 }
66 }
67 echo 'Resolution ppUx: ' . $ppUx. '<br />';
68 }
69 else echo 'JFIF not found in header' . '<br />';
70
71
72 // mPDF 6 ICC profile
73 $offset = 0;
74 $icc = array();
75 while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) {
76 // get ICC sequence length
77 $length = _twobytes2int(substr($data, ($pos - 2),2)) - 16;
78 $sn = max(1, ord($data[($pos + 12)]));
79 $nom = max(1, ord($data[($pos + 13)]));
80 $icc[($sn - 1)] = substr($data, ($pos + 14), $length);
81 $offset = ($pos + 14 + $length);
82 }
83 // order and compact ICC segments
84 if (count($icc) > 0) {
85 echo 'ICC profile present' . '<br />';
86 ksort($icc);
87 $icc = implode('', $icc);
88 if (substr($icc, 36, 4) != 'acsp') {
89 // invalid ICC profile
90 echo 'ICC profile INVALID (no acsp flag)' . '<br />';
91 }
92 $input = substr($icc, 16, 4);
93 $output = substr($icc, 20, 4);
94 echo 'ICC profile Input: ' . $input. '<br />';
95 echo 'ICC profile Output: ' . $output. '<br />';
96 // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
97 if ($input != 'RGB ' || $output != 'XYZ ') { echo 'ICC profile ignored by mPDF' . '<br />'; }
98 }
99
100 }
101
102 // PNG
103 else if ($type == 'png') {
104 //Check signature
105 if(substr($data,0,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
106 echo 'Error parsing PNG identifier<br />'; exit;
107 }
108 //Read header chunk
109 if(substr($data,12,4)!='IHDR') {
110 echo 'Incorrect PNG file (no IHDR block found)<br />'; exit;
111 }
112
113 $w=_fourbytes2int(substr($data,16,4));
114 $h=_fourbytes2int(substr($data,20,4));
115 $bpc=ord(substr($data,24,1));
116 $errpng = false;
117 $pngalpha = false;
118 $channels = 0;
119
120 echo 'Width: ' .$w . '<br />';
121 echo 'Height: ' .$h . '<br />';
122 echo 'BPC (bits per channel): ' .$bpc . '<br />';
123
124 $ct=ord(substr($data,25,1));
125 if($ct==0) { $colspace='DeviceGray'; $channels = 1; }
126 elseif($ct==2) { $colspace='DeviceRGB'; $channels = 3; }
127 elseif($ct==3) { $colspace='Indexed'; $channels = 1; }
128 elseif($ct==4) { $colspace='DeviceGray'; $channels = 1; $errpng = 'alpha channel'; $pngalpha = true; }
129 else { $colspace='DeviceRGB'; $channels = 3; $errpng = 'alpha channel'; $pngalpha = true; }
130
131 echo 'Colorspace: ' . $colspace. '<br />';
132 echo 'Channels: ' . $channels. '<br />';
133 if ($pngalpha) echo 'Alpha channel detected'. '<br />';
134
135 if ($ct < 4 && strpos($data,'tRNS')!==false) { echo 'Transparency detected'. '<br />'; $errpng = 'transparency'; $pngalpha = true;}
136
137 if ($ct == 3 && strpos($data,'iCCP')!==false) { echo 'Indexed plus ICC'. '<br />'; $errpng = 'indexed plus ICC'; }
138
139 if(ord(substr($data,26,1))!=0) { echo 'compression method not set to zero<br />'; $errpng = 'compression method'; } // only 0 should be specified
140 if(ord(substr($data,27,1))!=0) { echo 'filter method not set to zero<br />'; $errpng = 'filter method'; } // only 0 should be specified
141 if(ord(substr($data,28,1))!=0) { echo 'interlaced file not set to zero<br />'; $errpng = 'interlaced file'; }
142
143 $j = strpos($data,'pHYs');
144 if ($j) {
145 //Read resolution
146 $unitSp=ord(substr($data,($j+12),1));
147 if ($unitSp == 1) {
148 $ppUx=_fourbytes2int(substr($data,($j+4),4)); // horizontal pixels per meter, usually set to zero
149 $ppUx=round($ppUx/1000 *25.4);
150 }
151 echo 'Resolution ppUx: ' . $ppUx. '<br />';
152 }
153
154 // mPDF 6 Gamma correction
155 $gamma_correction = 0;
156 $gAMA = 0;
157 $j = strpos($data,'gAMA');
158 if ($j && strpos($data,'sRGB')===false) { // sRGB colorspace - overrides gAMA
159 $gAMA=_fourbytes2int(substr($data,($j+4),4)); // Gamma value times 100000
160 $gAMA /= 100000;
161
162 }
163
164 if ($gAMA) { $gamma_correction = 1/$gAMA; }
165
166 // Don't need to apply gamma correction if == default i.e. 2.2
167 if ($gamma_correction > 2.15 && $gamma_correction < 2.25) { $gamma_correction = 0; }
168
169 if ($gamma_correction) { echo 'Gamma correction detected'. '<br />'; }
170
171 // NOT supported at present
172 if (strpos($data,'sRGB')!==false) echo 'sRGB colorspace - NOT supported at present'. '<br />';
173 if (strpos($data,'cHRM')!==false) echo 'Chromaticity and Whitepoint - NOT supported at present'. '<br />';
174
175 if (($errpng || $pngalpha || $gamma_correction)) {
176 if (function_exists('gd_info')) { $gd = gd_info(); }
177 else {$gd = array(); }
178 if (!isset($gd['PNG Support'])) { echo 'GD library required for PNG image ('.$errpng.')'. '<br />';}
179 $im = imagecreatefromstring($data);
180
181 if (!$im) { echo 'Error creating GD image from PNG file ('.$errpng.')'. '<br />';}
182 $w = imagesx($im);
183 $h = imagesy($im);
184 if ($im) {
185
186 // Alpha channel set (including using tRNS for Paletted images)
187 if ($pngalpha) {
188 echo 'Alpha channel will be used by mPDF (including when tRNS present in Paletted images)<br />';
189 if ($colspace=='Indexed') { echo '...Generating Alpha channel values from tRNS (Indexed)<br />'; }
190 else if ($ct===0 || $ct==2) { echo '...Generating Alpha channel values from tRNS (non-Indexed)<br />'; }
191 else { echo '...Extracting Alpha channel<br />'; }
192
193
194 }
195 else { // No alpha/transparency set (but cannot read directly because e.g. bit-depth != 8, interlaced etc)
196 echo 'No alpha/transparency set (but cannot read directly because e.g. bit-depth != 8, interlaced etc)<br />';
197
198 // ICC profile
199 $icc = false;
200 $p = strpos($data,'iCCP');
201 if ($p && $colspace=="Indexed") {
202 $p += 4;
203 $n=_fourbytes2int(substr($data,($p-8),4));
204 $nullsep = strpos(substr($data,$p,80), chr(0));
205 $icc = substr($data, ($p+$nullsep+2), ($n-($nullsep+2)) );
206 // Test if file is gzcompressed
207 if (ord(substr($str, 0, 1)) == 0x1f && ord(substr($str, 1, 1)) == 0x8b) {
208 $icc = @gzuncompress($icc); // Ignored if fails
209 }
210 if ($icc) {
211 echo 'ICC profile present' . '<br />';
212 if (substr($icc, 36, 4) != 'acsp') { echo 'ICC profile INVALID (no acsp flag)' . '<br />'; $icc = false; } // invalid ICC profile
213 else {
214 $input = substr($icc, 16, 4);
215 $output = substr($icc, 20, 4);
216 echo 'ICC profile Input: ' . $input. '<br />';
217 echo 'ICC profile Output: ' . $output. '<br />';
218 // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
219 if ($input != 'RGB ' || $output != 'XYZ ') { $icc = false; echo 'ICC profile ignored by mPDF' . '<br />'; }
220 }
221 }
222 // Convert to RGB colorspace so can use ICC Profile
223 if ($icc) { echo 'ICC profile and Indexed colorspace both present - need to Convert to RGB colorspace so can use ICC Profile<br />'; }
224 }
225
226
227 }
228 }
229 echo 'PNG Image parsed on second pass' . '<br />';
230
231 // SECOND PASS
232 imagealphablending($im, false);
233 imagesavealpha($im, false);
234 imageinterlace($im, false);
235 ob_start();
236 $check = @imagepng($im);
237 if (!$check) { echo 'Error creating temporary image object whilst using GD library to parse PNG image' . '<br />'; }
238 $data = ob_get_contents();
239 ob_end_clean();
240 if (!$data) { echo 'Error parsing temporary file image object created with GD library to parse PNG image' . '<br />'; }
241 imagedestroy($im);
242
243
244 //Check signature
245 if(substr($data,0,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
246 echo 'Error parsing PNG identifier<br />'; exit;
247 }
248 //Read header chunk
249 if(substr($data,12,4)!='IHDR') {
250 echo 'Incorrect PNG file (no IHDR block found)<br />'; exit;
251 }
252
253 $w=_fourbytes2int(substr($data,16,4));
254 $h=_fourbytes2int(substr($data,20,4));
255 $bpc=ord(substr($data,24,1));
256 $errpng = false;
257 $pngalpha = false;
258 $channels = 0;
259
260 echo 'Width: ' .$w . '<br />';
261 echo 'Height: ' .$h . '<br />';
262 echo 'BPC (bits per channel): ' .$bpc . '<br />';
263
264 $ct=ord(substr($data,25,1));
265 if($ct==0) { $colspace='DeviceGray'; $channels = 1; }
266 elseif($ct==2) { $colspace='DeviceRGB'; $channels = 3; }
267 elseif($ct==3) { $colspace='Indexed'; $channels = 1; }
268 elseif($ct==4) { $colspace='DeviceGray'; $channels = 1; $errpng = 'alpha channel'; $pngalpha = true; }
269 else { $colspace='DeviceRGB'; $channels = 3; $errpng = 'alpha channel'; $pngalpha = true; }
270
271 echo 'Colorspace: ' . $colspace. '<br />';
272 echo 'Channels: ' . $channels. '<br />';
273 if ($pngalpha) echo 'Alpha channel detected'. '<br />';
274
275 if ($ct < 4 && strpos($data,'tRNS')!==false) { echo 'Transparency detected'. '<br />'; $errpng = 'transparency'; $pngalpha = true;}
276
277 if ($ct == 3 && strpos($data,'iCCP')!==false) { echo 'Indexed plus ICC'. '<br />'; $errpng = 'indexed plus ICC'; }
278
279 if(ord(substr($data,26,1))!=0) { echo 'compression method not set to zero<br />'; $errpng = 'compression method'; } // only 0 should be specified
280 if(ord(substr($data,27,1))!=0) { echo 'filter method not set to zero<br />'; $errpng = 'filter method'; } // only 0 should be specified
281 if(ord(substr($data,28,1))!=0) { echo 'interlaced file not set to zero<br />'; $errpng = 'interlaced file'; }
282
283 $j = strpos($data,'pHYs');
284 if ($j) {
285 //Read resolution
286 $unitSp=ord(substr($data,($j+12),1));
287 if ($unitSp == 1) {
288 $ppUx=_fourbytes2int(substr($data,($j+4),4)); // horizontal pixels per meter, usually set to zero
289 $ppUx=round($ppUx/1000 *25.4);
290 }
291 echo 'Resolution ppUx: ' . $ppUx. '<br />';
292 }
293
294 //Gamma correction
295 $gamma_correction = 0;
296 $gAMA = 0;
297 $j = strpos($data,'gAMA');
298 if ($j && strpos($data,'sRGB')===false) { // sRGB colorspace - overrides gAMA
299 $gAMA=_fourbytes2int(substr($data,($j+4),4)); // Gamma value times 100000
300 $gAMA /= 100000;
301
302 }
303
304 if ($gAMA) { $gamma_correction = 1/$gAMA; }
305
306 // Don't need to apply gamma correction if == default i.e. 2.2
307 if ($gamma_correction > 2.15 && $gamma_correction < 2.25) { $gamma_correction = 0; }
308
309 if ($gamma_correction) { echo 'Gamma correction detected'. '<br />'; }
310
311 // NOT supported at present
312 if (strpos($data,'sRGB')!==false) echo 'sRGB colorspace - NOT supported at present'. '<br />';
313 if (strpos($data,'cHRM')!==false) echo 'Chromaticity and Whitepoint - NOT supported at present'. '<br />';
314
315 }
316
317 else { // PNG image with no need to convert alpha channels, bpc <> 8 etc.
318 //Scan chunks looking for palette, transparency and image data
319 $pal='';
320 $trns='';
321 $pngdata='';
322 $icc = false;
323 $p = 33;
324 do {
325 $n=_fourbytes2int(substr($data,$p,4)); $p += 4;
326 $type=substr($data,$p,4); $p += 4;
327 if ($type=='PLTE') {
328 //Read palette
329 $pal=substr($data,$p,$n); $p += $n;
330 $p += 4;
331 }
332 else if($type=='tRNS') {
333 //Read transparency info
334 $t=substr($data,$p,$n); $p += $n;
335 if ($ct==0) $trns=array(ord(substr($t,1,1)));
336 else if ($ct==2) $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1)));
337 else {
338 $pos=strpos($t,chr(0));
339 if(is_int($pos)) $trns=array($pos);
340 }
341 $p += 4;
342 }
343 else if ($type=='IDAT') {
344 $pngdata.=substr($data,$p,$n); $p += $n;
345 $p += 4;
346 }
347 else if ($type=='iCCP') {
348 $nullsep = strpos(substr($data,$p,80), chr(0));
349 $icc = substr($data, ($p+$nullsep+2), ($n-($nullsep+2)) );
350 // Test if file is gzcompressed
351 if (ord(substr($str, 0, 1)) == 0x1f && ord(substr($str, 1, 1)) == 0x8b) {
352 $icc = @gzuncompress($icc); // Ignored if fails
353 }
354 if ($icc) {
355 echo 'ICC profile present' . '<br />';
356 if (substr($icc, 36, 4) != 'acsp') { echo 'ICC profile INVALID (no acsp flag)' . '<br />'; $icc = false; } // invalid ICC profile
357 else {
358 $input = substr($icc, 16, 4);
359 $output = substr($icc, 20, 4);
360 echo 'ICC profile Input: ' . $input. '<br />';
361 echo 'ICC profile Output: ' . $output. '<br />';
362 // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
363 if ($input != 'RGB ' || $output != 'XYZ ') { $icc = false; echo 'ICC profile ignored by mPDF' . '<br />'; }
364 }
365 }
366 $p += $n;
367 $p += 4;
368 }
369
370
371 else if($type=='IEND') { break; }
372 else if (preg_match('/[a-zA-Z]{4}/',$type)) { $p += $n+4; }
373 else { echo 'Error parsing PNG image data<br />'; }
374 }
375 while($n);
376 if (!$pngdata) { echo 'Error parsing PNG image data - no IDAT data found<br />';}
377 if ($colspace=='Indexed' && empty($pal)) { echo 'Error parsing PNG image data - missing colour palette<br />'; }
378 echo 'PNG Image parsed directly' . '<br />';
379
380 }
381
382
383 }
384
385 // GIF
386 else if ($type == 'gif') {
387 }
388
389 // BMP (Windows Bitmap)
390 else if ($type == 'bmp') {
391 }
392 // WMF
393 else if ($type == 'wmf') {
394 }
395 // UNKNOWN TYPE - try GD imagecreatefromstring
396 else {
397 if (function_exists('gd_info')) { $gd = gd_info(); }
398 else {$gd = array(); }
399 if (isset($gd['PNG Support']) && $gd['PNG Support']) {
400 $im = @imagecreatefromstring($data);
401 if ($im) { echo 'Image type not recognised, but image created from file using GD imagecreate' . '<br />'; }
402 else { echo 'Error parsing image file - image type not recognised, and not supported by GD imagecreate' . '<br />'; }
403 }
404 }
405
406
407 exit;
408
409 //==============================================================
410
411 function _fourbytes2int($s) {
412 //Read a 4-byte integer from string
413 return (ord($s[0])<<24) + (ord($s[1])<<16) + (ord($s[2])<<8) + ord($s[3]);
414 }
415
416 function _twobytes2int($s) { // equivalent to _get_ushort
417 //Read a 2-byte integer from string
418 return (ord(substr($s, 0, 1))<<8) + ord(substr($s, 1, 1));
419 }
420
421 function _jpgHeaderFromString(&$data) {
422 $p = 4;
423 $p += _twobytes2int(substr($data, $p, 2)); // Length of initial marker block
424 $marker = substr($data, $p, 2);
425 while($marker != chr(255).chr(192) && $marker != chr(255).chr(194) && $p<strlen($data)) {
426 // Start of frame marker (FFC0) or (FFC2) mPDF 4.4.004
427 $p += (_twobytes2int(substr($data, $p+2, 2))) + 2; // Length of marker block
428 $marker = substr($data, $p, 2);
429 }
430 if ($marker != chr(255).chr(192) && $marker != chr(255).chr(194)) { return false; }
431 return substr($data, $p+2, 10);
432 }
433
434 function _jpgDataFromHeader($hdr) {
435 $bpc = ord(substr($hdr, 2, 1));
436 if (!$bpc) { $bpc = 8; }
437 $h = _twobytes2int(substr($hdr, 3, 2));
438 $w = _twobytes2int(substr($hdr, 5, 2));
439 $channels = ord(substr($hdr, 7, 1));
440 if ($channels==3) { $colspace='DeviceRGB'; }
441 elseif($channels==4) { $colspace='DeviceCMYK'; }
442 else { $colspace='DeviceGray'; }
443 return array($w, $h, $colspace, $bpc, $channels);
444 }
445
446 function file_get_contents_by_curl($url, &$data) {
447 $timeout = 5;
448 $ch = curl_init($url);
449 curl_setopt($ch, CURLOPT_HEADER, 0);
450 curl_setopt($ch, CURLOPT_NOBODY, 0);
451 curl_setopt ( $ch , CURLOPT_RETURNTRANSFER , 1 );
452 curl_setopt ( $ch , CURLOPT_CONNECTTIMEOUT , $timeout );
453 $data = curl_exec($ch);
454 curl_close($ch);
455 }
456
457
458 function file_get_contents_by_socket($url, &$data) {
459 // mPDF 5.7.3
460 $timeout = 1;
461 $p = parse_url($url);
462 $file = $p['path'];
463 if ($p['scheme']=='https') {
464 $prefix = 'ssl://';
465 $port = ($p['port'] ? $p['port'] : 443);
466 }
467 else {
468 $prefix = '';
469 $port = ($p['port'] ? $p['port'] : 80);
470 }
471 if ($p['query']) { $file .= '?'.$p['query']; }
472 if(!($fh = @fsockopen($prefix.$p['host'], $port, $errno, $errstr, $timeout))) { return false; }
473
474 $getstring =
475 "GET ".$file." HTTP/1.0 \r\n" .
476 "Host: ".$p['host']." \r\n" .
477 "Connection: close\r\n\r\n";
478 fwrite($fh, $getstring);
479 // Get rid of HTTP header
480 $s = fgets($fh, 1024);
481 if (!$s) { return false; }
482 $httpheader .= $s;
483 while (!feof($fh)) {
484 $s = fgets($fh, 1024);
485 if ( $s == "\r\n" ) { break; }
486 }
487 $data = '';
488 while (!feof($fh)) {
489 $data .= fgets($fh, 1024);
490 }
491 fclose($fh);
492 }
493
494 //==============================================================
495
496 function _imageTypeFromString(&$data) {
497 $type = '';
498 if (substr($data, 6, 4)== 'JFIF' || substr($data, 6, 4)== 'Exif' || substr($data, 0, 2)== chr(255).chr(216)) { // 0xFF 0xD8
499 $type = 'jpeg';
500 }
501 else if (substr($data, 0, 6)== "GIF87a" || substr($data, 0, 6)== "GIF89a") {
502 $type = 'gif';
503 }
504 else if (substr($data, 0, 8)== chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
505 $type = 'png';
506 }
507 /*-- IMAGES-WMF --*/
508 else if (substr($data, 0, 4)== chr(215).chr(205).chr(198).chr(154)) {
509 $type = 'wmf';
510 }
511 /*-- END IMAGES-WMF --*/
512 else if (preg_match('/<svg.*<\/svg>/is',$data)) {
513 $type = 'svg';
514 }
515 // BMP images
516 else if (substr($data, 0, 2)== "BM") {
517 $type = 'bmp';
518 }
519 return $type;
520 }
521