tcpdi.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. <?php
  2. //
  3. // TCPDI - Version 1.0
  4. // Based on FPDI - Version 1.4.4
  5. //
  6. // Copyright 2004-2013 Setasign - Jan Slabon
  7. //
  8. // Licensed under the Apache License, Version 2.0 (the "License");
  9. // you may not use this file except in compliance with the License.
  10. // You may obtain a copy of the License at
  11. //
  12. // https://www.apache.org/licenses/LICENSE-2.0
  13. //
  14. // Unless required by applicable law or agreed to in writing, software
  15. // distributed under the License is distributed on an "AS IS" BASIS,
  16. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. // See the License for the specific language governing permissions and
  18. // limitations under the License.
  19. //
  20. // Dummy shim to allow unmodified use of fpdf_tpl
  21. class FPDF extends TCPDF {}
  22. require_once('fpdf_tpl.php');
  23. require_once('tcpdi_parser.php');
  24. class TCPDI extends FPDF_TPL {
  25. /**
  26. * Actual filename
  27. * @var string
  28. */
  29. var $current_filename;
  30. /**
  31. * Parser-Objects
  32. * @var array
  33. */
  34. var $parsers;
  35. /**
  36. * Current parser
  37. * @var object
  38. */
  39. var $current_parser;
  40. /**
  41. * object stack
  42. * @var array
  43. */
  44. var $_obj_stack;
  45. /**
  46. * done object stack
  47. * @var array
  48. */
  49. var $_don_obj_stack;
  50. /**
  51. * Current Object Id.
  52. * @var integer
  53. */
  54. var $_current_obj_id;
  55. /**
  56. * The name of the last imported page box
  57. * @var string
  58. */
  59. var $lastUsedPageBox;
  60. /**
  61. * Cache for imported pages/template ids
  62. * @var array
  63. */
  64. var $_importedPages = array();
  65. /**
  66. * Set a source-file
  67. *
  68. * @param string $filename a valid filename
  69. * @return int number of available pages
  70. */
  71. function setSourceFile($filename) {
  72. $this->current_filename = $filename;
  73. if (!isset($this->parsers[$filename]))
  74. $this->parsers[$filename] = $this->_getPdfParser($filename);
  75. $this->current_parser =& $this->parsers[$filename];
  76. $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion()));
  77. return $this->parsers[$filename]->getPageCount();
  78. }
  79. /**
  80. * Set a source-file PDF data
  81. *
  82. * @param string $pdfdata The PDF file content
  83. * @return int number of available pages
  84. */
  85. function setSourceData($pdfdata) {
  86. $filename = uniqid('tcpdi-');
  87. $this->current_filename = $filename;
  88. if (!isset($this->parsers[$filename]))
  89. $this->parsers[$filename] = new tcpdi_parser($pdfdata, $filename);
  90. $this->current_parser =& $this->parsers[$filename];
  91. $this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion()));
  92. return $this->parsers[$filename]->getPageCount();
  93. }
  94. /**
  95. * Returns a PDF parser object
  96. *
  97. * @param string $filename
  98. * @return fpdi_pdf_parser
  99. */
  100. function _getPdfParser($filename) {
  101. $data = file_get_contents($filename);
  102. return new tcpdi_parser($data, $filename);
  103. }
  104. /**
  105. * Get the current PDF version
  106. *
  107. * @return string
  108. */
  109. function getPDFVersion() {
  110. return $this->PDFVersion;
  111. }
  112. /**
  113. * Set the PDF version
  114. *
  115. * @return string
  116. */
  117. function setPDFVersion($version = '1.7') {
  118. $this->PDFVersion = $version;
  119. }
  120. /**
  121. * Import a page
  122. *
  123. * @param int $pageno pagenumber
  124. * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
  125. */
  126. function importPage($pageno, $boxName = '/CropBox') {
  127. if ($this->_intpl) {
  128. return $this->error('Please import the desired pages before creating a new template.');
  129. }
  130. $fn = $this->current_filename;
  131. // check if page already imported
  132. $pageKey = $fn . '-' . ((int)$pageno) . $boxName;
  133. if (isset($this->_importedPages[$pageKey]))
  134. return $this->_importedPages[$pageKey];
  135. $parser =& $this->parsers[$fn];
  136. $parser->setPageno($pageno);
  137. if (!in_array($boxName, $parser->availableBoxes))
  138. return $this->Error(sprintf('Unknown box: %s', $boxName));
  139. $pageboxes = $parser->getPageBoxes($pageno, $this->k);
  140. /**
  141. * MediaBox
  142. * CropBox: Default -> MediaBox
  143. * BleedBox: Default -> CropBox
  144. * TrimBox: Default -> CropBox
  145. * ArtBox: Default -> CropBox
  146. */
  147. if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
  148. $boxName = '/CropBox';
  149. if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox')
  150. $boxName = '/MediaBox';
  151. if (!isset($pageboxes[$boxName]))
  152. return false;
  153. $this->lastUsedPageBox = $boxName;
  154. $box = $pageboxes[$boxName];
  155. $this->tpl++;
  156. $this->tpls[$this->tpl] = array();
  157. $tpl =& $this->tpls[$this->tpl];
  158. $tpl['parser'] =& $parser;
  159. $tpl['resources'] = $parser->getPageResources();
  160. $tpl['buffer'] = $parser->getContent();
  161. $tpl['box'] = $box;
  162. // To build an array that can be used by PDF_TPL::useTemplate()
  163. $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
  164. // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
  165. $tpl['x'] = 0;
  166. $tpl['y'] = 0;
  167. // handle rotated pages
  168. $rotation = $parser->getPageRotation($pageno);
  169. $tpl['_rotationAngle'] = 0;
  170. if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
  171. $steps = $angle / 90;
  172. $_w = $tpl['w'];
  173. $_h = $tpl['h'];
  174. $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
  175. $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
  176. if ($angle < 0)
  177. $angle += 360;
  178. $tpl['_rotationAngle'] = $angle * -1;
  179. }
  180. $this->_importedPages[$pageKey] = $this->tpl;
  181. return $this->tpl;
  182. }
  183. /**
  184. * Returns the last used page box
  185. *
  186. * @return string
  187. */
  188. function getLastUsedPageBox() {
  189. return $this->lastUsedPageBox;
  190. }
  191. function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) {
  192. if ($adjustPageSize == true && is_null($_x) && is_null($_y)) {
  193. $size = $this->getTemplateSize($tplidx, $_w, $_h);
  194. $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
  195. $size = array($size['w'], $size['h']);
  196. $this->setPageFormat($size, $orientation);
  197. }
  198. $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
  199. $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
  200. $this->_out('Q');
  201. return $s;
  202. }
  203. /**
  204. * Private method, that rebuilds all needed objects of source files
  205. */
  206. function _putimportedobjects() {
  207. if (is_array($this->parsers) && count($this->parsers) > 0) {
  208. foreach($this->parsers AS $filename => $p) {
  209. $this->current_parser =& $this->parsers[$filename];
  210. if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
  211. while(($n = key($this->_obj_stack[$filename])) !== null) {
  212. $nObj = $this->current_parser->getObjectVal($this->_obj_stack[$filename][$n][1]);
  213. $this->_newobj($this->_obj_stack[$filename][$n][0]);
  214. if ($nObj[0] == PDF_TYPE_STREAM) {
  215. $this->pdf_write_value($nObj);
  216. } else {
  217. $this->pdf_write_value($nObj[1]);
  218. }
  219. $this->_out('endobj');
  220. $this->_obj_stack[$filename][$n] = null; // free memory
  221. unset($this->_obj_stack[$filename][$n]);
  222. reset($this->_obj_stack[$filename]);
  223. }
  224. }
  225. // We're done with this parser. Clean it up to free a bit of RAM.
  226. $this->current_parser->cleanUp();
  227. unset($this->parsers[$filename]);
  228. }
  229. }
  230. }
  231. /**
  232. * Private Method that writes the form xobjects
  233. */
  234. function _putformxobjects() {
  235. $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
  236. reset($this->tpls);
  237. foreach($this->tpls AS $tplidx => $tpl) {
  238. $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
  239. $this->_newobj();
  240. $cN = $this->n; // TCPDF/Protection: rem current "n"
  241. $this->tpls[$tplidx]['n'] = $this->n;
  242. $this->_out('<<' . $filter . '/Type /XObject');
  243. $this->_out('/Subtype /Form');
  244. $this->_out('/FormType 1');
  245. $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
  246. (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
  247. (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
  248. (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
  249. (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
  250. ));
  251. $c = 1;
  252. $s = 0;
  253. $tx = 0;
  254. $ty = 0;
  255. if (isset($tpl['box'])) {
  256. $tx = -$tpl['box']['llx'];
  257. $ty = -$tpl['box']['lly'];
  258. if ($tpl['_rotationAngle'] <> 0) {
  259. $angle = $tpl['_rotationAngle'] * M_PI/180;
  260. $c=cos($angle);
  261. $s=sin($angle);
  262. switch($tpl['_rotationAngle']) {
  263. case -90:
  264. $tx = -$tpl['box']['lly'];
  265. $ty = $tpl['box']['urx'];
  266. break;
  267. case -180:
  268. $tx = $tpl['box']['urx'];
  269. $ty = $tpl['box']['ury'];
  270. break;
  271. case -270:
  272. $tx = $tpl['box']['ury'];
  273. $ty = -$tpl['box']['llx'];
  274. break;
  275. }
  276. }
  277. } elseif ($tpl['x'] != 0 || $tpl['y'] != 0) {
  278. $tx = -$tpl['x'] * 2;
  279. $ty = $tpl['y'] * 2;
  280. }
  281. $tx *= $this->k;
  282. $ty *= $this->k;
  283. if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
  284. $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
  285. $c, $s, -$s, $c, $tx, $ty
  286. ));
  287. }
  288. $this->_out('/Resources ');
  289. if (isset($tpl['resources'])) {
  290. $this->current_parser =& $tpl['parser'];
  291. $this->pdf_write_value($tpl['resources']); // "n" will be changed
  292. } else {
  293. $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  294. if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
  295. $this->_out('/Font <<');
  296. foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
  297. $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
  298. $this->_out('>>');
  299. }
  300. if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
  301. isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
  302. {
  303. $this->_out('/XObject <<');
  304. if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
  305. foreach($this->_res['tpl'][$tplidx]['images'] as $image)
  306. $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
  307. }
  308. if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
  309. foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
  310. $this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R');
  311. }
  312. $this->_out('>>');
  313. }
  314. $this->_out('>>');
  315. }
  316. $this->_out('/Group <</Type/Group/S/Transparency>>');
  317. $nN = $this->n; // TCPDF: rem new "n"
  318. $this->n = $cN; // TCPDF: reset to current "n"
  319. $p = $this->_getrawstream($p);
  320. $this->_out('/Length ' . strlen($p) . ' >>');
  321. $this->_out("stream\n" . $p . "\nendstream");
  322. $this->_out('endobj');
  323. $this->n = $nN; // TCPDF: reset to new "n"
  324. }
  325. $this->_putimportedobjects();
  326. }
  327. /**
  328. * Rewritten to handle existing own defined objects
  329. */
  330. function _newobj($obj_id = false, $onlynewobj = false) {
  331. if (!$obj_id) {
  332. $obj_id = ++$this->n;
  333. }
  334. //Begin a new object
  335. if (!$onlynewobj) {
  336. $this->offsets[$obj_id] = $this->bufferlen;
  337. $this->_out($obj_id . ' 0 obj');
  338. $this->_current_obj_id = $obj_id; // for later use with encryption
  339. }
  340. return $obj_id;
  341. }
  342. /**
  343. * Writes a value
  344. * Needed to rebuild the source document
  345. *
  346. * @param mixed $value A PDF-Value. Structure of values see cases in this method
  347. */
  348. function pdf_write_value(&$value)
  349. {
  350. switch ($value[0]) {
  351. case PDF_TYPE_STRING:
  352. if ($this->encrypted) {
  353. $value[1] = $this->_unescape($value[1]);
  354. $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
  355. $value[1] = TCPDF_STATIC::_escape($value[1]);
  356. }
  357. break;
  358. case PDF_TYPE_STREAM:
  359. if ($this->encrypted) {
  360. $value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]);
  361. $value[1][1]['/Length'] = array(
  362. PDF_TYPE_NUMERIC,
  363. strlen($value[2][1])
  364. );
  365. }
  366. break;
  367. case PDF_TYPE_HEX:
  368. if ($this->encrypted) {
  369. $value[1] = $this->hex2str($value[1]);
  370. $value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]);
  371. // remake hexstring of encrypted string
  372. $value[1] = $this->str2hex($value[1]);
  373. }
  374. break;
  375. }
  376. switch ($value[0]) {
  377. case PDF_TYPE_TOKEN:
  378. $this->_straightOut('/'.$value[1] . ' ');
  379. break;
  380. case PDF_TYPE_NUMERIC:
  381. case PDF_TYPE_REAL:
  382. if (is_float($value[1]) && $value[1] != 0) {
  383. $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
  384. } else {
  385. $this->_straightOut($value[1] . ' ');
  386. }
  387. break;
  388. case PDF_TYPE_ARRAY:
  389. // An array. Output the proper
  390. // structure and move on.
  391. $this->_straightOut('[');
  392. for ($i = 0; $i < count($value[1]); $i++) {
  393. $this->pdf_write_value($value[1][$i]);
  394. }
  395. $this->_out(']');
  396. break;
  397. case PDF_TYPE_DICTIONARY:
  398. // A dictionary.
  399. $this->_straightOut('<<');
  400. reset ($value[1]);
  401. foreach ($value[1] as $k => $v) {
  402. $this->_straightOut($k . ' ');
  403. $this->pdf_write_value($v);
  404. }
  405. $this->_straightOut('>>');
  406. break;
  407. case PDF_TYPE_OBJREF:
  408. // An indirect object reference
  409. // Fill the object stack if needed
  410. $cpfn =& $this->current_parser->uniqueid;
  411. if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
  412. $this->_newobj(false, true);
  413. $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
  414. $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
  415. }
  416. $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
  417. $this->_out($objid . ' 0 R');
  418. break;
  419. case PDF_TYPE_STRING:
  420. // A string.
  421. $this->_straightOut('(' . $value[1] . ')');
  422. break;
  423. case PDF_TYPE_STREAM:
  424. // A stream. First, output the
  425. // stream dictionary, then the
  426. // stream data itself.
  427. $this->pdf_write_value($value[1]);
  428. $this->_out('stream');
  429. $this->_out($value[2][1]);
  430. $this->_out('endstream');
  431. break;
  432. case PDF_TYPE_HEX:
  433. $this->_straightOut('<' . $value[1] . '>');
  434. break;
  435. case PDF_TYPE_BOOLEAN:
  436. $this->_straightOut($value[1] ? 'true ' : 'false ');
  437. break;
  438. case PDF_TYPE_NULL:
  439. // The null object.
  440. $this->_straightOut('null ');
  441. break;
  442. }
  443. }
  444. /**
  445. * Modified so not each call will add a newline to the output.
  446. */
  447. function _straightOut($s) {
  448. if ($this->state == 2) {
  449. if ($this->inxobj) {
  450. // we are inside an XObject template
  451. $this->xobjects[$this->xobjid]['outdata'] .= $s;
  452. } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
  453. // puts data before page footer
  454. $pagebuff = $this->getPageBuffer($this->page);
  455. $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
  456. $footer = substr($pagebuff, -$this->footerlen[$this->page]);
  457. $this->setPageBuffer($this->page, $page.$s.$footer);
  458. // update footer position
  459. $this->footerpos[$this->page] += strlen($s);
  460. } else {
  461. // set page data
  462. $this->setPageBuffer($this->page, $s, true);
  463. }
  464. } elseif ($this->state > 0) {
  465. // set general data
  466. $this->setBuffer($s);
  467. }
  468. }
  469. /**
  470. * rewritten to close opened parsers
  471. *
  472. */
  473. function _enddoc() {
  474. parent::_enddoc();
  475. $this->_closeParsers();
  476. }
  477. /**
  478. * close all files opened by parsers
  479. */
  480. function _closeParsers() {
  481. if ($this->state > 2 && is_array($this->parsers) && count($this->parsers) > 0) {
  482. $this->cleanUp();
  483. return true;
  484. }
  485. return false;
  486. }
  487. /**
  488. * Removes cylced references and closes the file handles of the parser objects
  489. */
  490. function cleanUp() {
  491. foreach ($this->parsers as $k => $_){
  492. $this->parsers[$k]->cleanUp();
  493. $this->parsers[$k] = null;
  494. unset($this->parsers[$k]);
  495. }
  496. }
  497. // Functions from here on are taken from FPDI's fpdi2tcpdf_bridge.php to remove dependence on it
  498. function _putstream($s, $n=0) {
  499. $this->_out($this->_getstream($s, $n));
  500. }
  501. function _getxobjectdict() {
  502. $out = parent::_getxobjectdict();
  503. if (count($this->tpls)) {
  504. foreach($this->tpls as $tplidx => $tpl) {
  505. $out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']);
  506. }
  507. }
  508. return $out;
  509. }
  510. /**
  511. * Unescapes a PDF string
  512. *
  513. * @param string $s
  514. * @return string
  515. */
  516. function _unescape($s) {
  517. $out = '';
  518. for ($count = 0, $n = strlen($s); $count < $n; $count++) {
  519. if ($s[$count] != '\\' || $count == $n-1) {
  520. $out .= $s[$count];
  521. } else {
  522. switch ($s[++$count]) {
  523. case ')':
  524. case '(':
  525. case '\\':
  526. $out .= $s[$count];
  527. break;
  528. case 'f':
  529. $out .= chr(0x0C);
  530. break;
  531. case 'b':
  532. $out .= chr(0x08);
  533. break;
  534. case 't':
  535. $out .= chr(0x09);
  536. break;
  537. case 'r':
  538. $out .= chr(0x0D);
  539. break;
  540. case 'n':
  541. $out .= chr(0x0A);
  542. break;
  543. case "\r":
  544. if ($count != $n-1 && $s[$count+1] == "\n")
  545. $count++;
  546. break;
  547. case "\n":
  548. break;
  549. default:
  550. // Octal-Values
  551. if (ord($s[$count]) >= ord('0') &&
  552. ord($s[$count]) <= ord('9')) {
  553. $oct = ''. $s[$count];
  554. if (ord($s[$count+1]) >= ord('0') &&
  555. ord($s[$count+1]) <= ord('9')) {
  556. $oct .= $s[++$count];
  557. if (ord($s[$count+1]) >= ord('0') &&
  558. ord($s[$count+1]) <= ord('9')) {
  559. $oct .= $s[++$count];
  560. }
  561. }
  562. $out .= chr(octdec($oct));
  563. } else {
  564. $out .= $s[$count];
  565. }
  566. }
  567. }
  568. }
  569. return $out;
  570. }
  571. /**
  572. * Hexadecimal to string
  573. *
  574. * @param string $hex
  575. * @return string
  576. */
  577. function hex2str($hex) {
  578. return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex));
  579. }
  580. /**
  581. * String to hexadecimal
  582. *
  583. * @param string $str
  584. * @return string
  585. */
  586. function str2hex($str) {
  587. return current(unpack('H*', $str));
  588. }
  589. }