ExportLatex.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Set of methods used to build dumps of tables as Latex
  5. *
  6. * @package PhpMyAdmin-Export
  7. * @subpackage Latex
  8. */
  9. declare(strict_types=1);
  10. namespace PhpMyAdmin\Plugins\Export;
  11. use PhpMyAdmin\DatabaseInterface;
  12. use PhpMyAdmin\Export;
  13. use PhpMyAdmin\Plugins\ExportPlugin;
  14. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup;
  15. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup;
  16. use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
  17. use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
  18. use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
  19. use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
  20. use PhpMyAdmin\Relation;
  21. use PhpMyAdmin\Transformations;
  22. use PhpMyAdmin\Util;
  23. /**
  24. * Handles the export for the Latex format
  25. *
  26. * @package PhpMyAdmin-Export
  27. * @subpackage Latex
  28. */
  29. class ExportLatex extends ExportPlugin
  30. {
  31. /**
  32. * Constructor
  33. */
  34. public function __construct()
  35. {
  36. parent::__construct();
  37. // initialize the specific export sql variables
  38. $this->initSpecificVariables();
  39. $this->setProperties();
  40. }
  41. /**
  42. * Initialize the local variables that are used for export Latex
  43. *
  44. * @return void
  45. */
  46. protected function initSpecificVariables()
  47. {
  48. /* Messages used in default captions */
  49. $GLOBALS['strLatexContent'] = __('Content of table @TABLE@');
  50. $GLOBALS['strLatexContinued'] = __('(continued)');
  51. $GLOBALS['strLatexStructure'] = __('Structure of table @TABLE@');
  52. }
  53. /**
  54. * Sets the export Latex properties
  55. *
  56. * @return void
  57. */
  58. protected function setProperties()
  59. {
  60. global $plugin_param;
  61. $hide_structure = false;
  62. if ($plugin_param['export_type'] == 'table'
  63. && ! $plugin_param['single_table']
  64. ) {
  65. $hide_structure = true;
  66. }
  67. $exportPluginProperties = new ExportPluginProperties();
  68. $exportPluginProperties->setText('LaTeX');
  69. $exportPluginProperties->setExtension('tex');
  70. $exportPluginProperties->setMimeType('application/x-tex');
  71. $exportPluginProperties->setOptionsText(__('Options'));
  72. // create the root group that will be the options field for
  73. // $exportPluginProperties
  74. // this will be shown as "Format specific options"
  75. $exportSpecificOptions = new OptionsPropertyRootGroup(
  76. "Format Specific Options"
  77. );
  78. // general options main group
  79. $generalOptions = new OptionsPropertyMainGroup("general_opts");
  80. // create primary items and add them to the group
  81. $leaf = new BoolPropertyItem(
  82. "caption",
  83. __('Include table caption')
  84. );
  85. $generalOptions->addProperty($leaf);
  86. // add the main group to the root group
  87. $exportSpecificOptions->addProperty($generalOptions);
  88. // what to dump (structure/data/both) main group
  89. $dumpWhat = new OptionsPropertyMainGroup(
  90. "dump_what",
  91. __('Dump table')
  92. );
  93. // create primary items and add them to the group
  94. $leaf = new RadioPropertyItem("structure_or_data");
  95. $leaf->setValues(
  96. [
  97. 'structure' => __('structure'),
  98. 'data' => __('data'),
  99. 'structure_and_data' => __('structure and data'),
  100. ]
  101. );
  102. $dumpWhat->addProperty($leaf);
  103. // add the main group to the root group
  104. $exportSpecificOptions->addProperty($dumpWhat);
  105. // structure options main group
  106. if (! $hide_structure) {
  107. $structureOptions = new OptionsPropertyMainGroup(
  108. "structure",
  109. __('Object creation options')
  110. );
  111. $structureOptions->setForce('data');
  112. // create primary items and add them to the group
  113. $leaf = new TextPropertyItem(
  114. "structure_caption",
  115. __('Table caption:')
  116. );
  117. $leaf->setDoc('faq6-27');
  118. $structureOptions->addProperty($leaf);
  119. $leaf = new TextPropertyItem(
  120. "structure_continued_caption",
  121. __('Table caption (continued):')
  122. );
  123. $leaf->setDoc('faq6-27');
  124. $structureOptions->addProperty($leaf);
  125. $leaf = new TextPropertyItem(
  126. "structure_label",
  127. __('Label key:')
  128. );
  129. $leaf->setDoc('faq6-27');
  130. $structureOptions->addProperty($leaf);
  131. if (! empty($GLOBALS['cfgRelation']['relation'])) {
  132. $leaf = new BoolPropertyItem(
  133. "relation",
  134. __('Display foreign key relationships')
  135. );
  136. $structureOptions->addProperty($leaf);
  137. }
  138. $leaf = new BoolPropertyItem(
  139. "comments",
  140. __('Display comments')
  141. );
  142. $structureOptions->addProperty($leaf);
  143. if (! empty($GLOBALS['cfgRelation']['mimework'])) {
  144. $leaf = new BoolPropertyItem(
  145. "mime",
  146. __('Display media (MIME) types')
  147. );
  148. $structureOptions->addProperty($leaf);
  149. }
  150. // add the main group to the root group
  151. $exportSpecificOptions->addProperty($structureOptions);
  152. }
  153. // data options main group
  154. $dataOptions = new OptionsPropertyMainGroup(
  155. "data",
  156. __('Data dump options')
  157. );
  158. $dataOptions->setForce('structure');
  159. // create primary items and add them to the group
  160. $leaf = new BoolPropertyItem(
  161. "columns",
  162. __('Put columns names in the first row:')
  163. );
  164. $dataOptions->addProperty($leaf);
  165. $leaf = new TextPropertyItem(
  166. "data_caption",
  167. __('Table caption:')
  168. );
  169. $leaf->setDoc('faq6-27');
  170. $dataOptions->addProperty($leaf);
  171. $leaf = new TextPropertyItem(
  172. "data_continued_caption",
  173. __('Table caption (continued):')
  174. );
  175. $leaf->setDoc('faq6-27');
  176. $dataOptions->addProperty($leaf);
  177. $leaf = new TextPropertyItem(
  178. "data_label",
  179. __('Label key:')
  180. );
  181. $leaf->setDoc('faq6-27');
  182. $dataOptions->addProperty($leaf);
  183. $leaf = new TextPropertyItem(
  184. 'null',
  185. __('Replace NULL with:')
  186. );
  187. $dataOptions->addProperty($leaf);
  188. // add the main group to the root group
  189. $exportSpecificOptions->addProperty($dataOptions);
  190. // set the options for the export plugin property item
  191. $exportPluginProperties->setOptions($exportSpecificOptions);
  192. $this->properties = $exportPluginProperties;
  193. }
  194. /**
  195. * Outputs export header
  196. *
  197. * @return bool Whether it succeeded
  198. */
  199. public function exportHeader()
  200. {
  201. global $crlf;
  202. global $cfg;
  203. $head = '% phpMyAdmin LaTeX Dump' . $crlf
  204. . '% version ' . PMA_VERSION . $crlf
  205. . '% https://www.phpmyadmin.net/' . $crlf
  206. . '%' . $crlf
  207. . '% ' . __('Host:') . ' ' . $cfg['Server']['host'];
  208. if (! empty($cfg['Server']['port'])) {
  209. $head .= ':' . $cfg['Server']['port'];
  210. }
  211. $head .= $crlf
  212. . '% ' . __('Generation Time:') . ' '
  213. . Util::localisedDate() . $crlf
  214. . '% ' . __('Server version:') . ' ' . $GLOBALS['dbi']->getVersionString() . $crlf
  215. . '% ' . __('PHP Version:') . ' ' . PHP_VERSION . $crlf;
  216. return $this->export->outputHandler($head);
  217. }
  218. /**
  219. * Outputs export footer
  220. *
  221. * @return bool Whether it succeeded
  222. */
  223. public function exportFooter()
  224. {
  225. return true;
  226. }
  227. /**
  228. * Outputs database header
  229. *
  230. * @param string $db Database name
  231. * @param string $db_alias Aliases of db
  232. *
  233. * @return bool Whether it succeeded
  234. */
  235. public function exportDBHeader($db, $db_alias = '')
  236. {
  237. if (empty($db_alias)) {
  238. $db_alias = $db;
  239. }
  240. global $crlf;
  241. $head = '% ' . $crlf
  242. . '% ' . __('Database:') . ' \'' . $db_alias . '\'' . $crlf
  243. . '% ' . $crlf;
  244. return $this->export->outputHandler($head);
  245. }
  246. /**
  247. * Outputs database footer
  248. *
  249. * @param string $db Database name
  250. *
  251. * @return bool Whether it succeeded
  252. */
  253. public function exportDBFooter($db)
  254. {
  255. return true;
  256. }
  257. /**
  258. * Outputs CREATE DATABASE statement
  259. *
  260. * @param string $db Database name
  261. * @param string $export_type 'server', 'database', 'table'
  262. * @param string $db_alias Aliases of db
  263. *
  264. * @return bool Whether it succeeded
  265. */
  266. public function exportDBCreate($db, $export_type, $db_alias = '')
  267. {
  268. return true;
  269. }
  270. /**
  271. * Outputs the content of a table in JSON format
  272. *
  273. * @param string $db database name
  274. * @param string $table table name
  275. * @param string $crlf the end of line sequence
  276. * @param string $error_url the url to go back in case of error
  277. * @param string $sql_query SQL query for obtaining data
  278. * @param array $aliases Aliases of db/table/columns
  279. *
  280. * @return bool Whether it succeeded
  281. */
  282. public function exportData(
  283. $db,
  284. $table,
  285. $crlf,
  286. $error_url,
  287. $sql_query,
  288. array $aliases = []
  289. ) {
  290. $db_alias = $db;
  291. $table_alias = $table;
  292. $this->initAlias($aliases, $db_alias, $table_alias);
  293. $result = $GLOBALS['dbi']->tryQuery(
  294. $sql_query,
  295. DatabaseInterface::CONNECT_USER,
  296. DatabaseInterface::QUERY_UNBUFFERED
  297. );
  298. $columns_cnt = $GLOBALS['dbi']->numFields($result);
  299. $columns = [];
  300. $columns_alias = [];
  301. for ($i = 0; $i < $columns_cnt; $i++) {
  302. $columns[$i] = $col_as = $GLOBALS['dbi']->fieldName($result, $i);
  303. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  304. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  305. }
  306. $columns_alias[$i] = $col_as;
  307. }
  308. $buffer = $crlf . '%' . $crlf . '% ' . __('Data:') . ' ' . $table_alias
  309. . $crlf . '%' . $crlf . ' \\begin{longtable}{|';
  310. for ($index = 0; $index < $columns_cnt; $index++) {
  311. $buffer .= 'l|';
  312. }
  313. $buffer .= '} ' . $crlf;
  314. $buffer .= ' \\hline \\endhead \\hline \\endfoot \\hline ' . $crlf;
  315. if (isset($GLOBALS['latex_caption'])) {
  316. $buffer .= ' \\caption{'
  317. . Util::expandUserString(
  318. $GLOBALS['latex_data_caption'],
  319. [
  320. 'texEscape',
  321. static::class,
  322. ],
  323. [
  324. 'table' => $table_alias,
  325. 'database' => $db_alias,
  326. ]
  327. )
  328. . '} \\label{'
  329. . Util::expandUserString(
  330. $GLOBALS['latex_data_label'],
  331. null,
  332. [
  333. 'table' => $table_alias,
  334. 'database' => $db_alias,
  335. ]
  336. )
  337. . '} \\\\';
  338. }
  339. if (! $this->export->outputHandler($buffer)) {
  340. return false;
  341. }
  342. // show column names
  343. if (isset($GLOBALS['latex_columns'])) {
  344. $buffer = '\\hline ';
  345. for ($i = 0; $i < $columns_cnt; $i++) {
  346. $buffer .= '\\multicolumn{1}{|c|}{\\textbf{'
  347. . self::texEscape(stripslashes($columns_alias[$i])) . '}} & ';
  348. }
  349. $buffer = mb_substr($buffer, 0, -2) . '\\\\ \\hline \hline ';
  350. if (! $this->export->outputHandler($buffer . ' \\endfirsthead ' . $crlf)) {
  351. return false;
  352. }
  353. if (isset($GLOBALS['latex_caption'])) {
  354. if (! $this->export->outputHandler(
  355. '\\caption{'
  356. . Util::expandUserString(
  357. $GLOBALS['latex_data_continued_caption'],
  358. [
  359. 'texEscape',
  360. static::class,
  361. ],
  362. [
  363. 'table' => $table_alias,
  364. 'database' => $db_alias,
  365. ]
  366. )
  367. . '} \\\\ '
  368. )
  369. ) {
  370. return false;
  371. }
  372. }
  373. if (! $this->export->outputHandler($buffer . '\\endhead \\endfoot' . $crlf)) {
  374. return false;
  375. }
  376. } else {
  377. if (! $this->export->outputHandler('\\\\ \hline')) {
  378. return false;
  379. }
  380. }
  381. // print the whole table
  382. while ($record = $GLOBALS['dbi']->fetchAssoc($result)) {
  383. $buffer = '';
  384. // print each row
  385. for ($i = 0; $i < $columns_cnt; $i++) {
  386. if ($record[$columns[$i]] !== null
  387. && isset($record[$columns[$i]])
  388. ) {
  389. $column_value = self::texEscape(
  390. stripslashes($record[$columns[$i]])
  391. );
  392. } else {
  393. $column_value = $GLOBALS['latex_null'];
  394. }
  395. // last column ... no need for & character
  396. if ($i == ($columns_cnt - 1)) {
  397. $buffer .= $column_value;
  398. } else {
  399. $buffer .= $column_value . " & ";
  400. }
  401. }
  402. $buffer .= ' \\\\ \\hline ' . $crlf;
  403. if (! $this->export->outputHandler($buffer)) {
  404. return false;
  405. }
  406. }
  407. $buffer = ' \\end{longtable}' . $crlf;
  408. if (! $this->export->outputHandler($buffer)) {
  409. return false;
  410. }
  411. $GLOBALS['dbi']->freeResult($result);
  412. return true;
  413. } // end getTableLaTeX
  414. /**
  415. * Outputs table's structure
  416. *
  417. * @param string $db database name
  418. * @param string $table table name
  419. * @param string $crlf the end of line sequence
  420. * @param string $error_url the url to go back in case of error
  421. * @param string $export_mode 'create_table', 'triggers', 'create_view',
  422. * 'stand_in'
  423. * @param string $export_type 'server', 'database', 'table'
  424. * @param bool $do_relation whether to include relation comments
  425. * @param bool $do_comments whether to include the pmadb-style column
  426. * comments as comments in the structure;
  427. * this is deprecated but the parameter is
  428. * left here because export.php calls
  429. * exportStructure() also for other
  430. * export types which use this parameter
  431. * @param bool $do_mime whether to include mime comments
  432. * @param bool $dates whether to include creation/update/check dates
  433. * @param array $aliases Aliases of db/table/columns
  434. *
  435. * @return bool Whether it succeeded
  436. */
  437. public function exportStructure(
  438. $db,
  439. $table,
  440. $crlf,
  441. $error_url,
  442. $export_mode,
  443. $export_type,
  444. $do_relation = false,
  445. $do_comments = false,
  446. $do_mime = false,
  447. $dates = false,
  448. array $aliases = []
  449. ) {
  450. $db_alias = $db;
  451. $table_alias = $table;
  452. $this->initAlias($aliases, $db_alias, $table_alias);
  453. global $cfgRelation;
  454. /* We do not export triggers */
  455. if ($export_mode == 'triggers') {
  456. return true;
  457. }
  458. /**
  459. * Get the unique keys in the table
  460. */
  461. $unique_keys = [];
  462. $keys = $GLOBALS['dbi']->getTableIndexes($db, $table);
  463. foreach ($keys as $key) {
  464. if ($key['Non_unique'] == 0) {
  465. $unique_keys[] = $key['Column_name'];
  466. }
  467. }
  468. /**
  469. * Gets fields properties
  470. */
  471. $GLOBALS['dbi']->selectDb($db);
  472. // Check if we can use Relations
  473. list($res_rel, $have_rel) = $this->relation->getRelationsAndStatus(
  474. $do_relation && ! empty($cfgRelation['relation']),
  475. $db,
  476. $table
  477. );
  478. /**
  479. * Displays the table structure
  480. */
  481. $buffer = $crlf . '%' . $crlf . '% ' . __('Structure:') . ' '
  482. . $table_alias . $crlf . '%' . $crlf . ' \\begin{longtable}{';
  483. if (! $this->export->outputHandler($buffer)) {
  484. return false;
  485. }
  486. $alignment = '|l|c|c|c|';
  487. if ($do_relation && $have_rel) {
  488. $alignment .= 'l|';
  489. }
  490. if ($do_comments) {
  491. $alignment .= 'l|';
  492. }
  493. if ($do_mime && $cfgRelation['mimework']) {
  494. $alignment .= 'l|';
  495. }
  496. $buffer = $alignment . '} ' . $crlf;
  497. $header = ' \\hline ';
  498. $header .= '\\multicolumn{1}{|c|}{\\textbf{' . __('Column')
  499. . '}} & \\multicolumn{1}{|c|}{\\textbf{' . __('Type')
  500. . '}} & \\multicolumn{1}{|c|}{\\textbf{' . __('Null')
  501. . '}} & \\multicolumn{1}{|c|}{\\textbf{' . __('Default') . '}}';
  502. if ($do_relation && $have_rel) {
  503. $header .= ' & \\multicolumn{1}{|c|}{\\textbf{' . __('Links to') . '}}';
  504. }
  505. if ($do_comments) {
  506. $header .= ' & \\multicolumn{1}{|c|}{\\textbf{' . __('Comments') . '}}';
  507. $comments = $this->relation->getComments($db, $table);
  508. }
  509. if ($do_mime && $cfgRelation['mimework']) {
  510. $header .= ' & \\multicolumn{1}{|c|}{\\textbf{MIME}}';
  511. $mime_map = $this->transformations->getMime($db, $table, true);
  512. }
  513. // Table caption for first page and label
  514. if (isset($GLOBALS['latex_caption'])) {
  515. $buffer .= ' \\caption{'
  516. . Util::expandUserString(
  517. $GLOBALS['latex_structure_caption'],
  518. [
  519. 'texEscape',
  520. static::class,
  521. ],
  522. [
  523. 'table' => $table_alias,
  524. 'database' => $db_alias,
  525. ]
  526. )
  527. . '} \\label{'
  528. . Util::expandUserString(
  529. $GLOBALS['latex_structure_label'],
  530. null,
  531. [
  532. 'table' => $table_alias,
  533. 'database' => $db_alias,
  534. ]
  535. )
  536. . '} \\\\' . $crlf;
  537. }
  538. $buffer .= $header . ' \\\\ \\hline \\hline' . $crlf
  539. . '\\endfirsthead' . $crlf;
  540. // Table caption on next pages
  541. if (isset($GLOBALS['latex_caption'])) {
  542. $buffer .= ' \\caption{'
  543. . Util::expandUserString(
  544. $GLOBALS['latex_structure_continued_caption'],
  545. [
  546. 'texEscape',
  547. static::class,
  548. ],
  549. [
  550. 'table' => $table_alias,
  551. 'database' => $db_alias,
  552. ]
  553. )
  554. . '} \\\\ ' . $crlf;
  555. }
  556. $buffer .= $header . ' \\\\ \\hline \\hline \\endhead \\endfoot ' . $crlf;
  557. if (! $this->export->outputHandler($buffer)) {
  558. return false;
  559. }
  560. $fields = $GLOBALS['dbi']->getColumns($db, $table);
  561. foreach ($fields as $row) {
  562. $extracted_columnspec = Util::extractColumnSpec($row['Type']);
  563. $type = $extracted_columnspec['print_type'];
  564. if (empty($type)) {
  565. $type = ' ';
  566. }
  567. if (! isset($row['Default'])) {
  568. if ($row['Null'] != 'NO') {
  569. $row['Default'] = 'NULL';
  570. }
  571. }
  572. $field_name = $col_as = $row['Field'];
  573. if (! empty($aliases[$db]['tables'][$table]['columns'][$col_as])) {
  574. $col_as = $aliases[$db]['tables'][$table]['columns'][$col_as];
  575. }
  576. $local_buffer = $col_as . "\000" . $type . "\000"
  577. . (($row['Null'] == '' || $row['Null'] == 'NO')
  578. ? __('No') : __('Yes'))
  579. . "\000" . (isset($row['Default']) ? $row['Default'] : '');
  580. if ($do_relation && $have_rel) {
  581. $local_buffer .= "\000";
  582. $local_buffer .= $this->getRelationString(
  583. $res_rel,
  584. $field_name,
  585. $db,
  586. $aliases
  587. );
  588. }
  589. if ($do_comments && $cfgRelation['commwork']) {
  590. $local_buffer .= "\000";
  591. if (isset($comments[$field_name])) {
  592. $local_buffer .= $comments[$field_name];
  593. }
  594. }
  595. if ($do_mime && $cfgRelation['mimework']) {
  596. $local_buffer .= "\000";
  597. if (isset($mime_map[$field_name])) {
  598. $local_buffer .= str_replace(
  599. '_',
  600. '/',
  601. $mime_map[$field_name]['mimetype']
  602. );
  603. }
  604. }
  605. $local_buffer = self::texEscape($local_buffer);
  606. if ($row['Key'] == 'PRI') {
  607. $pos = mb_strpos($local_buffer, "\000");
  608. $local_buffer = '\\textit{'
  609. .
  610. mb_substr($local_buffer, 0, $pos)
  611. . '}' .
  612. mb_substr($local_buffer, $pos);
  613. }
  614. if (in_array($field_name, $unique_keys)) {
  615. $pos = mb_strpos($local_buffer, "\000");
  616. $local_buffer = '\\textbf{'
  617. .
  618. mb_substr($local_buffer, 0, $pos)
  619. . '}' .
  620. mb_substr($local_buffer, $pos);
  621. }
  622. $buffer = str_replace("\000", ' & ', $local_buffer);
  623. $buffer .= ' \\\\ \\hline ' . $crlf;
  624. if (! $this->export->outputHandler($buffer)) {
  625. return false;
  626. }
  627. } // end while
  628. $buffer = ' \\end{longtable}' . $crlf;
  629. return $this->export->outputHandler($buffer);
  630. } // end of the 'exportStructure' method
  631. /**
  632. * Escapes some special characters for use in TeX/LaTeX
  633. *
  634. * @param string $string the string to convert
  635. *
  636. * @return string the converted string with escape codes
  637. */
  638. public static function texEscape($string)
  639. {
  640. $escape = [
  641. '$',
  642. '%',
  643. '{',
  644. '}',
  645. '&',
  646. '#',
  647. '_',
  648. '^',
  649. ];
  650. $cnt_escape = count($escape);
  651. for ($k = 0; $k < $cnt_escape; $k++) {
  652. $string = str_replace($escape[$k], '\\' . $escape[$k], $string);
  653. }
  654. return $string;
  655. }
  656. }