ab10454469283040908b8ac072f69d880485cc1d
[yaffs-website] / vendor / phenx / php-font-lib / src / FontLib / BinaryStream.php
1 <?php
2 /**
3  * @package php-font-lib
4  * @link    https://github.com/PhenX/php-font-lib
5  * @author  Fabien Ménager <fabien.menager@gmail.com>
6  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7  */
8
9 namespace FontLib;
10
11 /**
12  * Generic font file binary stream.
13  *
14  * @package php-font-lib
15  */
16 class BinaryStream {
17   /**
18    * @var resource The file pointer
19    */
20   protected $f;
21
22   const uint8        = 1;
23   const  int8        = 2;
24   const uint16       = 3;
25   const  int16       = 4;
26   const uint32       = 5;
27   const  int32       = 6;
28   const shortFrac    = 7;
29   const Fixed        = 8;
30   const  FWord       = 9;
31   const uFWord       = 10;
32   const F2Dot14      = 11;
33   const longDateTime = 12;
34   const char         = 13;
35
36   const modeRead      = "rb";
37   const modeWrite     = "wb";
38   const modeReadWrite = "rb+";
39
40   static function backtrace() {
41     var_dump(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
42   }
43
44   /**
45    * Open a font file in read mode
46    *
47    * @param string $filename The file name of the font to open
48    *
49    * @return bool
50    */
51   public function load($filename) {
52     return $this->open($filename, self::modeRead);
53   }
54
55   /**
56    * Open a font file in a chosen mode
57    *
58    * @param string $filename The file name of the font to open
59    * @param string $mode     The opening mode
60    *
61    * @throws \Exception
62    * @return bool
63    */
64   public function open($filename, $mode = self::modeRead) {
65     if (!in_array($mode, array(self::modeRead, self::modeWrite, self::modeReadWrite))) {
66       throw new \Exception("Unkown file open mode");
67     }
68
69     $this->f = fopen($filename, $mode);
70
71     return $this->f != false;
72   }
73
74   /**
75    * Close the internal file pointer
76    */
77   public function close() {
78     return fclose($this->f) != false;
79   }
80
81   /**
82    * Change the internal file pointer
83    *
84    * @param resource $fp
85    *
86    * @throws \Exception
87    */
88   public function setFile($fp) {
89     if (!is_resource($fp)) {
90       throw new \Exception('$fp is not a valid resource');
91     }
92
93     $this->f = $fp;
94   }
95
96   /**
97    * Create a temporary file in write mode
98    *
99    * @param bool $allow_memory Allow in-memory files
100    *
101    * @return resource the temporary file pointer resource
102    */
103   public static function getTempFile($allow_memory = true) {
104     $f = null;
105
106     if ($allow_memory) {
107       $f = fopen("php://temp", "rb+");
108     }
109     else {
110       $f = fopen(tempnam(sys_get_temp_dir(), "fnt"), "rb+");
111     }
112
113     return $f;
114   }
115
116   /**
117    * Move the internal file pinter to $offset bytes
118    *
119    * @param int $offset
120    *
121    * @return bool True if the $offset position exists in the file
122    */
123   public function seek($offset) {
124     return fseek($this->f, $offset, SEEK_SET) == 0;
125   }
126
127   /**
128    * Gives the current position in the file
129    *
130    * @return int The current position
131    */
132   public function pos() {
133     return ftell($this->f);
134   }
135
136   public function skip($n) {
137     fseek($this->f, $n, SEEK_CUR);
138   }
139
140   public function read($n) {
141     if ($n < 1) {
142       return "";
143     }
144
145     return fread($this->f, $n);
146   }
147
148   public function write($data, $length = null) {
149     if ($data === null || $data === "" || $data === false) {
150       return 0;
151     }
152
153     return fwrite($this->f, $data, $length);
154   }
155
156   public function readUInt8() {
157     return ord($this->read(1));
158   }
159
160   public function readUInt8Many($count) {
161     return array_values(unpack("C*", $this->read($count)));
162   }
163
164   public function writeUInt8($data) {
165     return $this->write(chr($data), 1);
166   }
167
168   public function readInt8() {
169     $v = $this->readUInt8();
170
171     if ($v >= 0x80) {
172       $v -= 0x100;
173     }
174
175     return $v;
176   }
177
178   public function readInt8Many($count) {
179     return array_values(unpack("c*", $this->read($count)));
180   }
181
182   public function writeInt8($data) {
183     if ($data < 0) {
184       $data += 0x100;
185     }
186
187     return $this->writeUInt8($data);
188   }
189
190   public function readUInt16() {
191     $a = unpack("nn", $this->read(2));
192
193     return $a["n"];
194   }
195
196   public function readUInt16Many($count) {
197     return array_values(unpack("n*", $this->read($count * 2)));
198   }
199
200   public function readUFWord() {
201     return $this->readUInt16();
202   }
203
204   public function writeUInt16($data) {
205     return $this->write(pack("n", $data), 2);
206   }
207
208   public function writeUFWord($data) {
209     return $this->writeUInt16($data);
210   }
211
212   public function readInt16() {
213     $a = unpack("nn", $this->read(2));
214     $v = $a["n"];
215
216     if ($v >= 0x8000) {
217       $v -= 0x10000;
218     }
219
220     return $v;
221   }
222
223   public function readInt16Many($count) {
224     $vals = array_values(unpack("n*", $this->read($count * 2)));
225     foreach ($vals as &$v) {
226       if ($v >= 0x8000) {
227         $v -= 0x10000;
228       }
229     }
230
231     return $vals;
232   }
233
234   public function readFWord() {
235     return $this->readInt16();
236   }
237
238   public function writeInt16($data) {
239     if ($data < 0) {
240       $data += 0x10000;
241     }
242
243     return $this->writeUInt16($data);
244   }
245
246   public function writeFWord($data) {
247     return $this->writeInt16($data);
248   }
249
250   public function readUInt32() {
251     $a = unpack("NN", $this->read(4));
252
253     return $a["N"];
254   }
255
256   public function writeUInt32($data) {
257     return $this->write(pack("N", $data), 4);
258   }
259
260   public function readFixed() {
261     $d  = $this->readInt16();
262     $d2 = $this->readUInt16();
263
264     return round($d + $d2 / 0x10000, 4);
265   }
266
267   public function writeFixed($data) {
268     $left  = floor($data);
269     $right = ($data - $left) * 0x10000;
270
271     return $this->writeInt16($left) + $this->writeUInt16($right);
272   }
273
274   public function readLongDateTime() {
275     $this->readUInt32(); // ignored
276     $date = $this->readUInt32() - 2082844800;
277     
278     # PHP_INT_MIN isn't defined in PHP < 7.0
279     $php_int_min = defined("PHP_INT_MIN") ? PHP_INT_MIN : ~PHP_INT_MAX;
280
281     if (is_string($date) || $date > PHP_INT_MAX || $date < $php_int_min) {
282       $date = 0;
283     }
284
285     return strftime("%Y-%m-%d %H:%M:%S", $date);
286   }
287
288   public function writeLongDateTime($data) {
289     $date = strtotime($data);
290     $date += 2082844800;
291
292     return $this->writeUInt32(0) + $this->writeUInt32($date);
293   }
294
295   public function unpack($def) {
296     $d = array();
297     foreach ($def as $name => $type) {
298       $d[$name] = $this->r($type);
299     }
300
301     return $d;
302   }
303
304   public function pack($def, $data) {
305     $bytes = 0;
306     foreach ($def as $name => $type) {
307       $bytes += $this->w($type, $data[$name]);
308     }
309
310     return $bytes;
311   }
312
313   /**
314    * Read a data of type $type in the file from the current position
315    *
316    * @param mixed $type The data type to read
317    *
318    * @return mixed The data that was read
319    */
320   public function r($type) {
321     switch ($type) {
322       case self::uint8:
323         return $this->readUInt8();
324       case self::int8:
325         return $this->readInt8();
326       case self::uint16:
327         return $this->readUInt16();
328       case self::int16:
329         return $this->readInt16();
330       case self::uint32:
331         return $this->readUInt32();
332       case self::int32:
333         return $this->readUInt32();
334       case self::shortFrac:
335         return $this->readFixed();
336       case self::Fixed:
337         return $this->readFixed();
338       case self::FWord:
339         return $this->readInt16();
340       case self::uFWord:
341         return $this->readUInt16();
342       case self::F2Dot14:
343         return $this->readInt16();
344       case self::longDateTime:
345         return $this->readLongDateTime();
346       case self::char:
347         return $this->read(1);
348       default:
349         if (is_array($type)) {
350           if ($type[0] == self::char) {
351             return $this->read($type[1]);
352           }
353           if ($type[0] == self::uint16) {
354             return $this->readUInt16Many($type[1]);
355           }
356           if ($type[0] == self::int16) {
357             return $this->readInt16Many($type[1]);
358           }
359           if ($type[0] == self::uint8) {
360             return $this->readUInt8Many($type[1]);
361           }
362           if ($type[0] == self::int8) {
363             return $this->readInt8Many($type[1]);
364           }
365
366           $ret = array();
367           for ($i = 0; $i < $type[1]; $i++) {
368             $ret[] = $this->r($type[0]);
369           }
370
371           return $ret;
372         }
373
374         return null;
375     }
376   }
377
378   /**
379    * Write $data of type $type in the file from the current position
380    *
381    * @param mixed $type The data type to write
382    * @param mixed $data The data to write
383    *
384    * @return int The number of bytes read
385    */
386   public function w($type, $data) {
387     switch ($type) {
388       case self::uint8:
389         return $this->writeUInt8($data);
390       case self::int8:
391         return $this->writeInt8($data);
392       case self::uint16:
393         return $this->writeUInt16($data);
394       case self::int16:
395         return $this->writeInt16($data);
396       case self::uint32:
397         return $this->writeUInt32($data);
398       case self::int32:
399         return $this->writeUInt32($data);
400       case self::shortFrac:
401         return $this->writeFixed($data);
402       case self::Fixed:
403         return $this->writeFixed($data);
404       case self::FWord:
405         return $this->writeInt16($data);
406       case self::uFWord:
407         return $this->writeUInt16($data);
408       case self::F2Dot14:
409         return $this->writeInt16($data);
410       case self::longDateTime:
411         return $this->writeLongDateTime($data);
412       case self::char:
413         return $this->write($data, 1);
414       default:
415         if (is_array($type)) {
416           if ($type[0] == self::char) {
417             return $this->write($data, $type[1]);
418           }
419
420           $ret = 0;
421           for ($i = 0; $i < $type[1]; $i++) {
422             if (isset($data[$i])) {
423               $ret += $this->w($type[0], $data[$i]);
424             }
425           }
426
427           return $ret;
428         }
429
430         return null;
431     }
432   }
433
434   /**
435    * Converts a Uint32 value to string
436    *
437    * @param int $uint32
438    *
439    * @return string The string
440    */
441   public function convertUInt32ToStr($uint32) {
442     return chr(($uint32 >> 24) & 0xFF) . chr(($uint32 >> 16) & 0xFF) . chr(($uint32 >> 8) & 0xFF) . chr($uint32 & 0xFF);
443   }
444 }