Pdf.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. <?php
  2. /**
  3. * PDF schema handling
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin\Plugins\Schema\Pdf;
  7. use PhpMyAdmin\Pdf as PdfLib;
  8. use PhpMyAdmin\Relation;
  9. use PhpMyAdmin\Util;
  10. use function class_exists;
  11. use function count;
  12. use function getcwd;
  13. use function max;
  14. use function mb_ord;
  15. use function str_replace;
  16. use function strlen;
  17. use function ucfirst;
  18. use function is_array;
  19. // phpcs:disable PSR1.Files.SideEffects
  20. /**
  21. * Skip the plugin if TCPDF is not available.
  22. */
  23. if (! class_exists('TCPDF')) {
  24. $GLOBALS['skip_import'] = true;
  25. return;
  26. }
  27. /**
  28. * block attempts to directly run this script
  29. */
  30. if (getcwd() == __DIR__) {
  31. die('Attack stopped');
  32. }
  33. // phpcs:enable
  34. /**
  35. * Extends the "TCPDF" class and helps
  36. * in developing the structure of PDF Schema Export
  37. *
  38. * @see TCPDF
  39. *
  40. * @access public
  41. */
  42. class Pdf extends PdfLib
  43. {
  44. /** @var int|float */
  45. public $xMin;
  46. /** @var int|float */
  47. public $yMin;
  48. /** @var int|float */
  49. public $leftMargin = 10;
  50. /** @var int|float */
  51. public $topMargin = 10;
  52. /** @var int|float */
  53. public $scale;
  54. /** @var array */
  55. public $customLinks;
  56. /** @var array */
  57. public $widths;
  58. /** @var float */
  59. public $cMargin;
  60. /** @var string */
  61. private $ff = PdfLib::PMA_PDF_FONT;
  62. /** @var string */
  63. private $offline;
  64. /** @var int */
  65. private $pageNumber;
  66. /** @var bool */
  67. private $withDoc;
  68. /** @var string */
  69. private $db;
  70. /** @var Relation */
  71. private $relation;
  72. /**
  73. * Constructs PDF for schema export.
  74. *
  75. * @param string $orientation page orientation
  76. * @param string $unit unit
  77. * @param string $paper the format used for pages
  78. * @param int $pageNumber schema page number that is being exported
  79. * @param bool $withDoc with document dictionary
  80. * @param string $db the database name
  81. *
  82. * @access public
  83. */
  84. public function __construct(
  85. $orientation,
  86. $unit,
  87. $paper,
  88. $pageNumber,
  89. $withDoc,
  90. $db
  91. ) {
  92. global $dbi;
  93. parent::__construct($orientation, $unit, $paper);
  94. $this->pageNumber = $pageNumber;
  95. $this->withDoc = $withDoc;
  96. $this->db = $db;
  97. $this->relation = new Relation($dbi);
  98. }
  99. /**
  100. * Sets the value for margins
  101. *
  102. * @param float $c_margin margin
  103. *
  104. * @return void
  105. */
  106. public function setCMargin($c_margin)
  107. {
  108. $this->cMargin = $c_margin;
  109. }
  110. /**
  111. * Sets the scaling factor, defines minimum coordinates and margins
  112. *
  113. * @param float|int $scale The scaling factor
  114. * @param float|int $xMin The minimum X coordinate
  115. * @param float|int $yMin The minimum Y coordinate
  116. * @param float|int $leftMargin The left margin
  117. * @param float|int $topMargin The top margin
  118. *
  119. * @return void
  120. */
  121. public function setScale(
  122. $scale = 1,
  123. $xMin = 0,
  124. $yMin = 0,
  125. $leftMargin = -1,
  126. $topMargin = -1
  127. ) {
  128. $this->scale = $scale;
  129. $this->xMin = $xMin;
  130. $this->yMin = $yMin;
  131. if ($this->leftMargin != -1) {
  132. $this->leftMargin = $leftMargin;
  133. }
  134. if ($this->topMargin == -1) {
  135. return;
  136. }
  137. $this->topMargin = $topMargin;
  138. }
  139. /**
  140. * Outputs a scaled cell
  141. *
  142. * @see TCPDF::Cell()
  143. *
  144. * @param float|int $w The cell width
  145. * @param float|int $h The cell height
  146. * @param string $txt The text to output
  147. * @param mixed $border Whether to add borders or not
  148. * @param int $ln Where to put the cursor once the output is done
  149. * @param string $align Align mode
  150. * @param int $fill Whether to fill the cell with a color or not
  151. * @param string $link Link
  152. *
  153. * @return void
  154. */
  155. public function cellScale(
  156. $w,
  157. $h = 0,
  158. $txt = '',
  159. $border = 0,
  160. $ln = 0,
  161. $align = '',
  162. $fill = 0,
  163. $link = ''
  164. ) {
  165. $h /= $this->scale;
  166. $w /= $this->scale;
  167. $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link);
  168. }
  169. /**
  170. * Draws a scaled line
  171. *
  172. * @see TCPDF::Line()
  173. *
  174. * @param float $x1 The horizontal position of the starting point
  175. * @param float $y1 The vertical position of the starting point
  176. * @param float $x2 The horizontal position of the ending point
  177. * @param float $y2 The vertical position of the ending point
  178. *
  179. * @return void
  180. */
  181. public function lineScale($x1, $y1, $x2, $y2)
  182. {
  183. $x1 = ($x1 - $this->xMin) / $this->scale + $this->leftMargin;
  184. $y1 = ($y1 - $this->yMin) / $this->scale + $this->topMargin;
  185. $x2 = ($x2 - $this->xMin) / $this->scale + $this->leftMargin;
  186. $y2 = ($y2 - $this->yMin) / $this->scale + $this->topMargin;
  187. $this->Line($x1, $y1, $x2, $y2);
  188. }
  189. /**
  190. * Sets x and y scaled positions
  191. *
  192. * @see TCPDF::SetXY()
  193. *
  194. * @param float $x The x position
  195. * @param float $y The y position
  196. *
  197. * @return void
  198. */
  199. public function setXyScale($x, $y)
  200. {
  201. $x = ($x - $this->xMin) / $this->scale + $this->leftMargin;
  202. $y = ($y - $this->yMin) / $this->scale + $this->topMargin;
  203. $this->SetXY($x, $y);
  204. }
  205. /**
  206. * Sets the X scaled positions
  207. *
  208. * @see TCPDF::SetX()
  209. *
  210. * @param float $x The x position
  211. *
  212. * @return void
  213. */
  214. public function setXScale($x)
  215. {
  216. $x = ($x - $this->xMin) / $this->scale + $this->leftMargin;
  217. $this->SetX($x);
  218. }
  219. /**
  220. * Sets the scaled font size
  221. *
  222. * @see TCPDF::SetFontSize()
  223. *
  224. * @param float $size The font size (in points)
  225. *
  226. * @return void
  227. */
  228. public function setFontSizeScale($size)
  229. {
  230. // Set font size in points
  231. $size /= $this->scale;
  232. $this->SetFontSize($size);
  233. }
  234. /**
  235. * Sets the scaled line width
  236. *
  237. * @see TCPDF::SetLineWidth()
  238. *
  239. * @param float $width The line width
  240. *
  241. * @return void
  242. */
  243. public function setLineWidthScale($width)
  244. {
  245. $width /= $this->scale;
  246. $this->SetLineWidth($width);
  247. }
  248. /**
  249. * This method is used to render the page header.
  250. *
  251. * @see TCPDF::Header()
  252. *
  253. * @return void
  254. */
  255. // @codingStandardsIgnoreLine
  256. public function Header()
  257. {
  258. global $dbi;
  259. // We only show this if we find something in the new pdf_pages table
  260. // This function must be named "Header" to work with the TCPDF library
  261. if (! $this->withDoc) {
  262. return;
  263. }
  264. if ($this->offline || $this->pageNumber == -1) {
  265. $pg_name = __('PDF export page');
  266. } else {
  267. $test_query = 'SELECT * FROM '
  268. . Util::backquote($GLOBALS['cfgRelation']['db']) . '.'
  269. . Util::backquote($GLOBALS['cfgRelation']['pdf_pages'])
  270. . ' WHERE db_name = \'' . $dbi->escapeString($this->db)
  271. . '\' AND page_nr = \'' . $this->pageNumber . '\'';
  272. $test_rs = $this->relation->queryAsControlUser($test_query);
  273. $pageDesc = '';
  274. $pages = $dbi->fetchAssoc($test_rs);
  275. if (is_array($pages)) {
  276. $pageDesc = (string) $pages['page_descr'];
  277. }
  278. $pg_name = ucfirst($pageDesc);
  279. }
  280. $this->SetFont($this->ff, 'B', 14);
  281. $this->Cell(0, 6, $pg_name, 'B', 1, 'C');
  282. $this->SetFont($this->ff, '');
  283. $this->Ln();
  284. }
  285. /**
  286. * This function must be named "Footer" to work with the TCPDF library
  287. *
  288. * @see PDF::Footer()
  289. *
  290. * @return void
  291. */
  292. // @codingStandardsIgnoreLine
  293. public function Footer()
  294. {
  295. if (! $this->withDoc) {
  296. return;
  297. }
  298. parent::Footer();
  299. }
  300. /**
  301. * Sets widths
  302. *
  303. * @param array $w array of widths
  304. *
  305. * @return void
  306. */
  307. public function setWidths(array $w)
  308. {
  309. // column widths
  310. $this->widths = $w;
  311. }
  312. /**
  313. * Generates table row.
  314. *
  315. * @param array $data Data for table
  316. * @param array $links Links for table cells
  317. *
  318. * @return void
  319. */
  320. public function row(array $data, array $links)
  321. {
  322. // line height
  323. $nb = 0;
  324. $data_cnt = count($data);
  325. for ($i = 0; $i < $data_cnt; $i++) {
  326. $nb = max($nb, $this->numLines($this->widths[$i], $data[$i]));
  327. }
  328. $il = $this->FontSize;
  329. $h = ($il + 1) * $nb;
  330. // page break if necessary
  331. $this->checkPageBreak($h);
  332. // draw the cells
  333. $data_cnt = count($data);
  334. for ($i = 0; $i < $data_cnt; $i++) {
  335. $w = $this->widths[$i];
  336. // save current position
  337. $x = $this->GetX();
  338. $y = $this->GetY();
  339. // draw the border
  340. $this->Rect($x, $y, $w, $h);
  341. if (isset($links[$i])) {
  342. $this->Link($x, $y, $w, $h, $links[$i]);
  343. }
  344. // print text
  345. $this->MultiCell($w, $il + 1, $data[$i], 0, 'L');
  346. // go to right side
  347. $this->SetXY($x + $w, $y);
  348. }
  349. // go to line
  350. $this->Ln($h);
  351. }
  352. /**
  353. * Compute number of lines used by a multicell of width w
  354. *
  355. * @param int $w width
  356. * @param string $txt text
  357. *
  358. * @return int
  359. */
  360. public function numLines($w, $txt)
  361. {
  362. $cw = &$this->CurrentFont['cw'];
  363. if ($w == 0) {
  364. $w = $this->w - $this->rMargin - $this->x;
  365. }
  366. $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
  367. $s = str_replace("\r", '', $txt);
  368. $nb = strlen($s);
  369. if ($nb > 0 && $s[$nb - 1] == "\n") {
  370. $nb--;
  371. }
  372. $sep = -1;
  373. $i = 0;
  374. $j = 0;
  375. $l = 0;
  376. $nl = 1;
  377. while ($i < $nb) {
  378. $c = $s[$i];
  379. if ($c == "\n") {
  380. $i++;
  381. $sep = -1;
  382. $j = $i;
  383. $l = 0;
  384. $nl++;
  385. continue;
  386. }
  387. if ($c === ' ') {
  388. $sep = $i;
  389. }
  390. $l += $cw[mb_ord($c)] ?? 0;
  391. if ($l > $wmax) {
  392. if ($sep == -1) {
  393. if ($i == $j) {
  394. $i++;
  395. }
  396. } else {
  397. $i = $sep + 1;
  398. }
  399. $sep = -1;
  400. $j = $i;
  401. $l = 0;
  402. $nl++;
  403. } else {
  404. $i++;
  405. }
  406. }
  407. return $nl;
  408. }
  409. /**
  410. * Set whether the document is generated from client side DB
  411. *
  412. * @param string $value whether offline
  413. *
  414. * @return void
  415. *
  416. * @access private
  417. */
  418. public function setOffline($value)
  419. {
  420. $this->offline = $value;
  421. }
  422. }