37 {
38 $info = &$this->getid3->info;
39
40
41 $info[
'png'] = array();
42 $thisfile_png = &
$info[
'png'];
43
44 $info[
'fileformat'] =
'png';
45 $info[
'video'][
'dataformat'] =
'png';
46 $info[
'video'][
'lossless'] =
false;
47
48 fseek($this->getid3->fp,
$info[
'avdataoffset'], SEEK_SET);
49 $PNGfiledata =
fread($this->getid3->fp, $this->getid3->fread_buffer_size());
50 $offset = 0;
51
52 $PNGidentifier = substr($PNGfiledata, $offset, 8);
53 $offset += 8;
54
55 if ($PNGidentifier != "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
56 $info[
'error'][] =
'First 8 bytes of file ('.Helper::PrintHexBytes($PNGidentifier).
') did not match expected PNG identifier';
57 unset(
$info[
'fileformat']);
58
59 return false;
60 }
61
62 while (((
ftell($this->getid3->fp) - (strlen($PNGfiledata) - $offset)) <
$info[
'filesize'])) {
64 $offset += 4;
65 while (((strlen($PNGfiledata) - $offset) < ($chunk[
'data_length'] + 4)) && (
ftell($this->getid3->fp) <
$info[
'filesize'])) {
66 $PNGfiledata .=
fread($this->getid3->fp, $this->getid3->fread_buffer_size());
67 }
68 $chunk['type_text'] = substr($PNGfiledata, $offset, 4);
69 $offset += 4;
71 $chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']);
72 $offset += $chunk['data_length'];
74 $offset += 4;
75
76 $chunk['flags']['ancilliary'] = (bool) ($chunk['type_raw'] & 0x20000000);
77 $chunk['flags']['private'] = (bool) ($chunk['type_raw'] & 0x00200000);
78 $chunk['flags']['reserved'] = (bool) ($chunk['type_raw'] & 0x00002000);
79 $chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x00000020);
80
81
82 $thisfile_png[$chunk['type_text']] = array();
83 $thisfile_png_chunk_type_text = &$thisfile_png[$chunk['type_text']];
84
85 switch ($chunk['type_text']) {
86
87 case 'IHDR':
88 $thisfile_png_chunk_type_text['header'] = $chunk;
91 $thisfile_png_chunk_type_text[
'raw'][
'bit_depth'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 8, 1));
92 $thisfile_png_chunk_type_text[
'raw'][
'color_type'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 9, 1));
93 $thisfile_png_chunk_type_text[
'raw'][
'compression_method'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 10, 1));
94 $thisfile_png_chunk_type_text[
'raw'][
'filter_method'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 11, 1));
95 $thisfile_png_chunk_type_text[
'raw'][
'interlace_method'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 12, 1));
96
97 $thisfile_png_chunk_type_text[
'compression_method_text'] = $this->
PNGcompressionMethodLookup($thisfile_png_chunk_type_text[
'raw'][
'compression_method']);
98 $thisfile_png_chunk_type_text['color_type']['palette'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x01);
99 $thisfile_png_chunk_type_text['color_type']['true_color'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x02);
100 $thisfile_png_chunk_type_text['color_type']['alpha'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x04);
101
102 $info[
'video'][
'resolution_x'] = $thisfile_png_chunk_type_text[
'width'];
103 $info[
'video'][
'resolution_y'] = $thisfile_png_chunk_type_text[
'height'];
104
105 $info[
'video'][
'bits_per_sample'] = $this->
IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text[
'raw'][
'color_type'], $thisfile_png_chunk_type_text[
'raw'][
'bit_depth']);
106 break;
107
108 case 'PLTE':
109 $thisfile_png_chunk_type_text['header'] = $chunk;
110 $paletteoffset = 0;
111 for ($i = 0; $i <= 255; $i++) {
112
113
114
118 $thisfile_png_chunk_type_text[$i] = ((
$red << 16) | (
$green << 8) | (
$blue));
119 }
120 break;
121
122 case 'tRNS':
123 $thisfile_png_chunk_type_text['header'] = $chunk;
124 switch ($thisfile_png['IHDR']['raw']['color_type']) {
125 case 0:
126 $thisfile_png_chunk_type_text[
'transparent_color_gray'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 2));
127 break;
128
129 case 2:
130 $thisfile_png_chunk_type_text[
'transparent_color_red'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 2));
131 $thisfile_png_chunk_type_text[
'transparent_color_green'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 2, 2));
132 $thisfile_png_chunk_type_text[
'transparent_color_blue'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 4, 2));
133 break;
134
135 case 3:
136 for ($i = 0; $i < strlen($chunk['data']); $i++) {
137 $thisfile_png_chunk_type_text[
'palette_opacity'][$i] =
Helper::BigEndian2Int(substr($chunk[
'data'], $i, 1));
138 }
139 break;
140
141 case 4:
142 case 6:
143 $info[
'error'][] =
'Invalid color_type in tRNS chunk: '.$thisfile_png[
'IHDR'][
'raw'][
'color_type'];
144
145 default:
146 $info[
'warning'][] =
'Unhandled color_type in tRNS chunk: '.$thisfile_png[
'IHDR'][
'raw'][
'color_type'];
147 break;
148 }
149 break;
150
151 case 'gAMA':
152 $thisfile_png_chunk_type_text['header'] = $chunk;
154 break;
155
156 case 'cHRM':
157 $thisfile_png_chunk_type_text['header'] = $chunk;
158 $thisfile_png_chunk_type_text[
'white_x'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 4)) / 100000;
159 $thisfile_png_chunk_type_text[
'white_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 4, 4)) / 100000;
160 $thisfile_png_chunk_type_text[
'red_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 8, 4)) / 100000;
161 $thisfile_png_chunk_type_text[
'red_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 12, 4)) / 100000;
162 $thisfile_png_chunk_type_text[
'green_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 16, 4)) / 100000;
163 $thisfile_png_chunk_type_text[
'green_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 20, 4)) / 100000;
164 $thisfile_png_chunk_type_text[
'blue_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 24, 4)) / 100000;
165 $thisfile_png_chunk_type_text[
'blue_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 28, 4)) / 100000;
166 break;
167
168 case 'sRGB':
169 $thisfile_png_chunk_type_text['header'] = $chunk;
171 $thisfile_png_chunk_type_text[
'reindering_intent_text'] = $this->
PNGsRGBintentLookup($thisfile_png_chunk_type_text[
'reindering_intent']);
172 break;
173
174 case 'iCCP':
175 $thisfile_png_chunk_type_text['header'] = $chunk;
176 list($profilename, $compressiondata) = explode("\x00", $chunk['data'], 2);
177 $thisfile_png_chunk_type_text['profile_name'] = $profilename;
178 $thisfile_png_chunk_type_text[
'compression_method'] =
Helper::BigEndian2Int(substr($compressiondata, 0, 1));
179 $thisfile_png_chunk_type_text['compression_profile'] = substr($compressiondata, 1);
180
181 $thisfile_png_chunk_type_text[
'compression_method_text'] = $this->
PNGcompressionMethodLookup($thisfile_png_chunk_type_text[
'compression_method']);
182 break;
183
184 case 'tEXt':
185 $thisfile_png_chunk_type_text['header'] = $chunk;
186 list($keyword,
$text) = explode(
"\x00", $chunk[
'data'], 2);
187 $thisfile_png_chunk_type_text['keyword'] = $keyword;
188 $thisfile_png_chunk_type_text[
'text'] =
$text;
189
190 $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
191 break;
192
193 case 'zTXt':
194 $thisfile_png_chunk_type_text['header'] = $chunk;
195 list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2);
196 $thisfile_png_chunk_type_text['keyword'] = $keyword;
197 $thisfile_png_chunk_type_text[
'compression_method'] =
Helper::BigEndian2Int(substr($otherdata, 0, 1));
198 $thisfile_png_chunk_type_text['compressed_text'] = substr($otherdata, 1);
199 $thisfile_png_chunk_type_text[
'compression_method_text'] = $this->
PNGcompressionMethodLookup($thisfile_png_chunk_type_text[
'compression_method']);
200 switch ($thisfile_png_chunk_type_text['compression_method']) {
201 case 0:
202 $thisfile_png_chunk_type_text['text'] = gzuncompress($thisfile_png_chunk_type_text['compressed_text']);
203 break;
204
205 default:
206
207 break;
208 }
209
210 if (isset($thisfile_png_chunk_type_text['text'])) {
211 $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
212 }
213 break;
214
215 case 'iTXt':
216 $thisfile_png_chunk_type_text['header'] = $chunk;
217 list($keyword, $otherdata) = explode("\x00", $chunk['data'], 2);
218 $thisfile_png_chunk_type_text['keyword'] = $keyword;
219 $thisfile_png_chunk_type_text[
'compression'] = (bool)
Helper::BigEndian2Int(substr($otherdata, 0, 1));
220 $thisfile_png_chunk_type_text[
'compression_method'] =
Helper::BigEndian2Int(substr($otherdata, 1, 1));
221 $thisfile_png_chunk_type_text[
'compression_method_text'] = $this->
PNGcompressionMethodLookup($thisfile_png_chunk_type_text[
'compression_method']);
222 list($languagetag, $translatedkeyword,
$text) = explode(
"\x00", substr($otherdata, 2), 3);
223 $thisfile_png_chunk_type_text['language_tag'] = $languagetag;
224 $thisfile_png_chunk_type_text['translated_keyword'] = $translatedkeyword;
225
226 if ($thisfile_png_chunk_type_text['compression']) {
227
228 switch ($thisfile_png_chunk_type_text['compression_method']) {
229 case 0:
230 $thisfile_png_chunk_type_text[
'text'] = gzuncompress(
$text);
231 break;
232
233 default:
234
235 break;
236 }
237
238 } else {
239
240 $thisfile_png_chunk_type_text[
'text'] =
$text;
241
242 }
243
244 if (isset($thisfile_png_chunk_type_text['text'])) {
245 $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
246 }
247 break;
248
249 case 'bKGD':
250 $thisfile_png_chunk_type_text['header'] = $chunk;
251 switch ($thisfile_png['IHDR']['raw']['color_type']) {
252 case 0:
253 case 4:
255 break;
256
257 case 2:
258 case 6:
259 $thisfile_png_chunk_type_text[
'background_red'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0 * $thisfile_png[
'IHDR'][
'raw'][
'bit_depth'], $thisfile_png[
'IHDR'][
'raw'][
'bit_depth']));
260 $thisfile_png_chunk_type_text[
'background_green'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 1 * $thisfile_png[
'IHDR'][
'raw'][
'bit_depth'], $thisfile_png[
'IHDR'][
'raw'][
'bit_depth']));
261 $thisfile_png_chunk_type_text[
'background_blue'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 2 * $thisfile_png[
'IHDR'][
'raw'][
'bit_depth'], $thisfile_png[
'IHDR'][
'raw'][
'bit_depth']));
262 break;
263
264 case 3:
266 break;
267
268 default:
269 break;
270 }
271 break;
272
273 case 'pHYs':
274 $thisfile_png_chunk_type_text['header'] = $chunk;
275 $thisfile_png_chunk_type_text[
'pixels_per_unit_x'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 4));
276 $thisfile_png_chunk_type_text[
'pixels_per_unit_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 4, 4));
277 $thisfile_png_chunk_type_text[
'unit_specifier'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 8, 1));
278 $thisfile_png_chunk_type_text[
'unit'] = $this->
PNGpHYsUnitLookup($thisfile_png_chunk_type_text[
'unit_specifier']);
279 break;
280
281 case 'sBIT':
282 $thisfile_png_chunk_type_text['header'] = $chunk;
283 switch ($thisfile_png['IHDR']['raw']['color_type']) {
284 case 0:
285 $thisfile_png_chunk_type_text[
'significant_bits_gray'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 1));
286 break;
287
288 case 2:
289 case 3:
290 $thisfile_png_chunk_type_text[
'significant_bits_red'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 1));
291 $thisfile_png_chunk_type_text[
'significant_bits_green'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 1, 1));
292 $thisfile_png_chunk_type_text[
'significant_bits_blue'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 2, 1));
293 break;
294
295 case 4:
296 $thisfile_png_chunk_type_text[
'significant_bits_gray'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 1));
297 $thisfile_png_chunk_type_text[
'significant_bits_alpha'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 1, 1));
298 break;
299
300 case 6:
301 $thisfile_png_chunk_type_text[
'significant_bits_red'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 1));
302 $thisfile_png_chunk_type_text[
'significant_bits_green'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 1, 1));
303 $thisfile_png_chunk_type_text[
'significant_bits_blue'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 2, 1));
304 $thisfile_png_chunk_type_text[
'significant_bits_alpha'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 3, 1));
305 break;
306
307 default:
308 break;
309 }
310 break;
311
312 case 'sPLT':
313 $thisfile_png_chunk_type_text['header'] = $chunk;
314 list($palettename, $otherdata) = explode("\x00", $chunk['data'], 2);
315 $thisfile_png_chunk_type_text['palette_name'] = $palettename;
316 $sPLToffset = 0;
317 $thisfile_png_chunk_type_text[
'sample_depth_bits'] =
Helper::BigEndian2Int(substr($otherdata, $sPLToffset, 1));
318 $sPLToffset += 1;
319 $thisfile_png_chunk_type_text['sample_depth_bytes'] = $thisfile_png_chunk_type_text['sample_depth_bits'] / 8;
320 $paletteCounter = 0;
321 while ($sPLToffset < strlen($otherdata)) {
322 $thisfile_png_chunk_type_text[
'red'][$paletteCounter] =
Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text[
'sample_depth_bytes']));
323 $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
324 $thisfile_png_chunk_type_text[
'green'][$paletteCounter] =
Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text[
'sample_depth_bytes']));
325 $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
326 $thisfile_png_chunk_type_text[
'blue'][$paletteCounter] =
Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text[
'sample_depth_bytes']));
327 $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
328 $thisfile_png_chunk_type_text[
'alpha'][$paletteCounter] =
Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text[
'sample_depth_bytes']));
329 $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
330 $thisfile_png_chunk_type_text[
'frequency'][$paletteCounter] =
Helper::BigEndian2Int(substr($otherdata, $sPLToffset, 2));
331 $sPLToffset += 2;
332 $paletteCounter++;
333 }
334 break;
335
336 case 'hIST':
337 $thisfile_png_chunk_type_text['header'] = $chunk;
338 $hISTcounter = 0;
339 while ($hISTcounter < strlen($chunk['data'])) {
340 $thisfile_png_chunk_type_text[$hISTcounter] =
Helper::BigEndian2Int(substr($chunk[
'data'], $hISTcounter / 2, 2));
341 $hISTcounter += 2;
342 }
343 break;
344
345 case 'tIME':
346 $thisfile_png_chunk_type_text['header'] = $chunk;
353 $thisfile_png_chunk_type_text['unix'] = gmmktime($thisfile_png_chunk_type_text['hour'], $thisfile_png_chunk_type_text['minute'], $thisfile_png_chunk_type_text['second'], $thisfile_png_chunk_type_text['month'], $thisfile_png_chunk_type_text['day'], $thisfile_png_chunk_type_text['year']);
354 break;
355
356 case 'oFFs':
357 $thisfile_png_chunk_type_text['header'] = $chunk;
358 $thisfile_png_chunk_type_text[
'position_x'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 4),
false,
true);
359 $thisfile_png_chunk_type_text[
'position_y'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 4, 4),
false,
true);
360 $thisfile_png_chunk_type_text[
'unit_specifier'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 8, 1));
361 $thisfile_png_chunk_type_text[
'unit'] = $this->
PNGoFFsUnitLookup($thisfile_png_chunk_type_text[
'unit_specifier']);
362 break;
363
364 case 'pCAL':
365 $thisfile_png_chunk_type_text['header'] = $chunk;
366 list($calibrationname, $otherdata) = explode("\x00", $chunk['data'], 2);
367 $thisfile_png_chunk_type_text['calibration_name'] = $calibrationname;
368 $pCALoffset = 0;
369 $thisfile_png_chunk_type_text[
'original_zero'] =
Helper::BigEndian2Int(substr($chunk[
'data'], $pCALoffset, 4),
false,
true);
370 $pCALoffset += 4;
371 $thisfile_png_chunk_type_text[
'original_max'] =
Helper::BigEndian2Int(substr($chunk[
'data'], $pCALoffset, 4),
false,
true);
372 $pCALoffset += 4;
373 $thisfile_png_chunk_type_text[
'equation_type'] =
Helper::BigEndian2Int(substr($chunk[
'data'], $pCALoffset, 1));
374 $pCALoffset += 1;
375 $thisfile_png_chunk_type_text[
'equation_type_text'] = $this->
PNGpCALequationTypeLookup($thisfile_png_chunk_type_text[
'equation_type']);
376 $thisfile_png_chunk_type_text[
'parameter_count'] =
Helper::BigEndian2Int(substr($chunk[
'data'], $pCALoffset, 1));
377 $pCALoffset += 1;
378 $thisfile_png_chunk_type_text['parameters'] = explode("\x00", substr($chunk['data'], $pCALoffset));
379 break;
380
381 case 'sCAL':
382 $thisfile_png_chunk_type_text['header'] = $chunk;
383 $thisfile_png_chunk_type_text[
'unit_specifier'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 1));
384 $thisfile_png_chunk_type_text[
'unit'] = $this->
PNGsCALUnitLookup($thisfile_png_chunk_type_text[
'unit_specifier']);
385 list($pixelwidth, $pixelheight) = explode("\x00", substr($chunk['data'], 1));
386 $thisfile_png_chunk_type_text['pixel_width'] = $pixelwidth;
387 $thisfile_png_chunk_type_text['pixel_height'] = $pixelheight;
388 break;
389
390 case 'gIFg':
391 $gIFgCounter = 0;
392 if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
393 $gIFgCounter = count($thisfile_png_chunk_type_text);
394 }
395 $thisfile_png_chunk_type_text[$gIFgCounter]['header'] = $chunk;
396 $thisfile_png_chunk_type_text[$gIFgCounter][
'disposal_method'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 0, 1));
397 $thisfile_png_chunk_type_text[$gIFgCounter][
'user_input_flag'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 1, 1));
398 $thisfile_png_chunk_type_text[$gIFgCounter][
'delay_time'] =
Helper::BigEndian2Int(substr($chunk[
'data'], 2, 2));
399 break;
400
401 case 'gIFx':
402 $gIFxCounter = 0;
403 if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
404 $gIFxCounter = count($thisfile_png_chunk_type_text);
405 }
406 $thisfile_png_chunk_type_text[$gIFxCounter]['header'] = $chunk;
407 $thisfile_png_chunk_type_text[$gIFxCounter]['application_identifier'] = substr($chunk['data'], 0, 8);
408 $thisfile_png_chunk_type_text[$gIFxCounter]['authentication_code'] = substr($chunk['data'], 8, 3);
409 $thisfile_png_chunk_type_text[$gIFxCounter]['application_data'] = substr($chunk['data'], 11);
410 break;
411
412 case 'IDAT':
413 $idatinformationfieldindex = 0;
414 if (isset($thisfile_png['IDAT']) && is_array($thisfile_png['IDAT'])) {
415 $idatinformationfieldindex = count($thisfile_png['IDAT']);
416 }
417 unset($chunk['data']);
418 $thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk;
419 break;
420
421 case 'IEND':
422 $thisfile_png_chunk_type_text['header'] = $chunk;
423 break;
424
425 default:
426
427 $thisfile_png_chunk_type_text['header'] = $chunk;
428 $info[
'warning'][] =
'Unhandled chunk type: '.$chunk[
'type_text'];
429 break;
430 }
431 }
432
433 return true;
434 }
fseek($bytes, $whence=SEEK_SET)
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
PNGsRGBintentLookup($sRGB)
@staticvar array $PNGsRGBintentLookup
IHDRcalculateBitsPerSample($color_type, $bit_depth)
PNGpCALequationTypeLookup($equationtype)
@staticvar array $PNGpCALequationTypeLookup
PNGoFFsUnitLookup($unitid)
@staticvar array $PNGoFFsUnitLookup
PNGpHYsUnitLookup($unitid)
@staticvar array $PNGpHYsUnitLookup
PNGcompressionMethodLookup($compressionmethod)
@staticvar array $PNGcompressionMethodLookup
PNGsCALUnitLookup($unitid)
@staticvar array $PNGsCALUnitLookup