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