No Description

BinaryStream.php 9.8KB

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