PageRenderTime 145ms CodeModel.GetById 96ms RepoModel.GetById 0ms app.codeStats 1ms

/include/html2pdf/styleHTML.class.php

https://bitbucket.org/sleininger/stock_online
PHP | 1573 lines | 1211 code | 196 blank | 166 comment | 255 complexity | dbce01057ac78c5ff6d753e1720f2059 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1, GPL-3.0
  1. <?php
  2. /**
  3. * Logiciel : HTML2PDF - classe styleHTML
  4. *
  5. * Convertisseur HTML => PDF
  6. * Distribué sous la licence LGPL.
  7. *
  8. * @author Laurent MINGUET <webmaster@html2pdf.fr>
  9. * @version 4.01
  10. */
  11. class styleHTML
  12. {
  13. protected $pdf = null; // référence au PDF parent
  14. protected $htmlColor = array(); // liste des couleurs HTML
  15. protected $onlyLeft = false; // indique si on est dans un sous HTML et qu'on bloque ŕ gauche
  16. protected $defaultFont = null; // fonte par défaut si la fonte demandée n'existe pas
  17. public $value = array(); // valeurs actuelles
  18. public $css = array(); // tableau des CSS
  19. public $css_keys = array(); // tableau des clefs CSS, pour l'ordre d'execution
  20. public $table = array(); // tableau d'empilement pour historisation des niveaux
  21. /**
  22. * Constructeur
  23. *
  24. * @param &pdf référence ŕ l'objet HTML2PDF parent
  25. * @return null
  26. */
  27. public function __construct(&$pdf)
  28. {
  29. $this->init(); // initialisation
  30. $this->setPdfParent($pdf);
  31. }
  32. public function setPdfParent(&$pdf)
  33. {
  34. $this->pdf = &$pdf;
  35. }
  36. public function setOnlyLeft()
  37. {
  38. $this->value['text-align'] = 'left';
  39. $this->onlyLeft = true;
  40. }
  41. public function getOldValues()
  42. {
  43. return isset($this->table[count($this->table)-1]) ? $this->table[count($this->table)-1] : $this->value;
  44. }
  45. /**
  46. * définit la fonte par défaut si aucun fonte n'est spécifiée, ou si la fonte demandée n'existe pas
  47. *
  48. * @param string nom de la fonte par defaut. si null : Arial pour fonte non spécifiée, et erreur pour fonte non existante
  49. * @return string nom de l'ancienne fonte par defaut
  50. */
  51. public function setDefaultFont($default = null)
  52. {
  53. $old = $this->defaultFont;
  54. $this->defaultFont = $default;
  55. if ($default) $this->value['font-family'] = $default;
  56. return $old;
  57. }
  58. /**
  59. * Initialisation du style
  60. *
  61. * @return null
  62. */
  63. protected function init()
  64. {
  65. global $webcolor;
  66. $this->htmlColor = &$webcolor;
  67. $this->table = array();
  68. $this->value = array();
  69. $this->initStyle();
  70. // initialisation des styles sans héritages
  71. $this->resetStyle();
  72. }
  73. public function initStyle()
  74. {
  75. $this->value['id_balise'] = 'body'; // balise
  76. $this->value['id_name'] = null; // name
  77. $this->value['id_id'] = null; // id
  78. $this->value['id_class'] = null; // class
  79. $this->value['id_lst'] = array('*'); // lst de dependance
  80. $this->value['mini-size'] = 1.; // rapport de taille spécifique aux sup, sub
  81. $this->value['mini-decal'] = 0; // rapport de position spécifique aux sup, sub
  82. $this->value['font-family'] = 'Arial';
  83. $this->value['font-bold'] = false;
  84. $this->value['font-italic'] = false;
  85. $this->value['font-underline'] = false;
  86. $this->value['font-overline'] = false;
  87. $this->value['font-linethrough'] = false;
  88. $this->value['text-transform'] = 'none';
  89. $this->value['font-size'] = $this->ConvertToMM('10pt');
  90. $this->value['text-indent'] = 0;
  91. $this->value['text-align'] = 'left';
  92. $this->value['vertical-align'] = 'middle';
  93. $this->value['line-height'] = 'normal';
  94. $this->value['position'] = null;
  95. $this->value['x'] = null;
  96. $this->value['y'] = null;
  97. $this->value['width'] = 0;
  98. $this->value['height'] = 0;
  99. $this->value['top'] = null;
  100. $this->value['right'] = null;
  101. $this->value['bottom'] = null;
  102. $this->value['left'] = null;
  103. $this->value['float'] = null;
  104. $this->value['display'] = null;
  105. $this->value['rotate'] = null;
  106. $this->value['overflow'] = 'visible';
  107. $this->value['color'] = array(0, 0, 0);
  108. $this->value['background'] = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null);
  109. $this->value['border'] = array();
  110. $this->value['padding'] = array();
  111. $this->value['margin'] = array();
  112. $this->value['margin-auto'] = false;
  113. $this->value['list-style-type'] = '';
  114. $this->value['list-style-image'] = '';
  115. $this->value['xc'] = null;
  116. $this->value['yc'] = null;
  117. }
  118. /**
  119. * Initialisation des styles sans héritages
  120. *
  121. * @param string balise HTML
  122. * @return null
  123. */
  124. public function resetStyle($balise = '')
  125. {
  126. $collapse = isset($this->value['border']['collapse']) ? $this->value['border']['collapse'] : false;
  127. if (!in_array($balise, array('tr', 'td', 'th', 'thead', 'tbody', 'tfoot'))) $collapse = false;
  128. $this->value['position'] = null;
  129. $this->value['x'] = null;
  130. $this->value['y'] = null;
  131. $this->value['width'] = 0;
  132. $this->value['height'] = 0;
  133. $this->value['top'] = null;
  134. $this->value['right'] = null;
  135. $this->value['bottom'] = null;
  136. $this->value['left'] = null;
  137. $this->value['float'] = null;
  138. $this->value['display'] = null;
  139. $this->value['rotate'] = null;
  140. $this->value['overflow'] = 'visible';
  141. $this->value['background'] = array('color' => null, 'image' => null, 'position' => null, 'repeat' => null);
  142. $this->value['border'] = array(
  143. 't' => $this->readBorder('none'),
  144. 'r' => $this->readBorder('none'),
  145. 'b' => $this->readBorder('none'),
  146. 'l' => $this->readBorder('none'),
  147. 'radius' => array(
  148. 'tl' => array(0, 0),
  149. 'tr' => array(0, 0),
  150. 'br' => array(0, 0),
  151. 'bl' => array(0, 0)
  152. ),
  153. 'collapse' => $collapse,
  154. );
  155. if (!in_array($balise, array('h1', 'h2', 'h3', 'h4', 'h5', 'h6')))
  156. $this->value['margin'] = array('t'=>0,'r'=>0,'b'=>0,'l'=>0);
  157. if ($balise=='p')
  158. {
  159. $this->value['margin']['t'] = null;
  160. $this->value['margin']['b'] = null;
  161. }
  162. $this->value['margin-auto'] = false;
  163. if (in_array($balise, array('div', 'fieldset')))
  164. $this->value['vertical-align'] = 'top';
  165. if (in_array($balise, array('fieldset', 'legend')))
  166. {
  167. $radius = $this->ConvertToMM('5px');
  168. $this->value['border'] = array(
  169. 't' => $this->readBorder('solid 1px #000000'),
  170. 'r' => $this->readBorder('solid 1px #000000'),
  171. 'b' => $this->readBorder('solid 1px #000000'),
  172. 'l' => $this->readBorder('solid 1px #000000'),
  173. 'radius' => array(),
  174. 'collapse' => false,
  175. );
  176. $this->value['border']['radius'] = array(
  177. 'tl' => array($radius, $radius),
  178. 'tr' => array($radius, $radius),
  179. 'br' => array($radius, $radius),
  180. 'bl' => array($radius, $radius));
  181. }
  182. if (in_array($balise, array('ul', 'li')))
  183. {
  184. $this->value['list-style-type'] = '';
  185. $this->value['list-style-image'] = '';
  186. }
  187. if (!in_array($balise, array('tr', 'td')))
  188. {
  189. $this->value['padding'] = array(
  190. 't' => 0,
  191. 'r' => 0,
  192. 'b' => 0,
  193. 'l' => 0
  194. );
  195. }
  196. else
  197. {
  198. $this->value['padding'] = array(
  199. 't' => $this->ConvertToMM('1px'),
  200. 'r' => $this->ConvertToMM('1px'),
  201. 'b' => $this->ConvertToMM('1px'),
  202. 'l' => $this->ConvertToMM('1px')
  203. );
  204. }
  205. if ($balise=='hr')
  206. {
  207. $this->value['border'] = array(
  208. 't' => $this->readBorder('solid 1px #000000'),
  209. 'r' => $this->readBorder('solid 1px #000000'),
  210. 'b' => $this->readBorder('solid 1px #000000'),
  211. 'l' => $this->readBorder('solid 1px #000000'),
  212. 'radius' => array(
  213. 'tl' => array(0, 0),
  214. 'tr' => array(0, 0),
  215. 'br' => array(0, 0),
  216. 'bl' => array(0, 0)
  217. ),
  218. 'collapse' => false,
  219. );
  220. $this->ConvertBackground('#FFFFFF', $this->value['background']);
  221. }
  222. $this->value['xc'] = null;
  223. $this->value['yc'] = null;
  224. }
  225. /**
  226. * Initialisation de la font PDF
  227. *
  228. * @return null
  229. */
  230. public function FontSet()
  231. {
  232. $family = strtolower($this->value['font-family']);
  233. $b = ($this->value['font-bold'] ? 'B' : '');
  234. $i = ($this->value['font-italic'] ? 'I' : '');
  235. $u = ($this->value['font-underline'] ? 'U' : '');
  236. $d = ($this->value['font-linethrough'] ? 'D' : '');
  237. $o = ($this->value['font-overline'] ? 'O' : '');
  238. if ($this->defaultFont)
  239. {
  240. $style = $b.$i;
  241. if($family=='arial')
  242. $family='helvetica';
  243. elseif($family=='symbol' || $family=='zapfdingbats')
  244. $style='';
  245. $fontkey = $family.$style;
  246. if (!$this->pdf->isLoadedFont($fontkey))
  247. $family = $this->defaultFont;
  248. }
  249. if($family=='arial')
  250. $family='helvetica';
  251. elseif($family=='symbol' || $family=='zapfdingbats')
  252. $style='';
  253. // taille en mm, ŕ ramener en pt
  254. $size = $this->value['font-size'];
  255. $size = 72 * $size / 25.4;
  256. // application de la fonte
  257. $this->pdf->SetFont($family, $b.$i.$u.$d.$o, $this->value['mini-size']*$size);
  258. $this->pdf->setTextColorArray($this->value['color']);
  259. if ($this->value['background']['color'])
  260. $this->pdf->setFillColorArray($this->value['background']['color']);
  261. else
  262. $this->pdf->setFillColor(255);
  263. }
  264. /**
  265. * Monter d'un niveau dans l'historisation
  266. *
  267. * @return null
  268. */
  269. public function save()
  270. {
  271. $this->table[count($this->table)] = $this->value;
  272. }
  273. /**
  274. * Descendre d'un niveau dans l'historisation
  275. *
  276. * @return null
  277. */
  278. public function load()
  279. {
  280. if (count($this->table))
  281. {
  282. $this->value = $this->table[count($this->table)-1];
  283. unset($this->table[count($this->table)-1]);
  284. }
  285. }
  286. public function restorePosition()
  287. {
  288. if ($this->value['y']==$this->pdf->getY()) $this->pdf->setY($this->value['yc'], false);
  289. }
  290. public function setPosition()
  291. {
  292. $current_x = $this->pdf->getX();
  293. $current_y = $this->pdf->getY();
  294. $this->value['xc'] = $current_x;
  295. $this->value['yc'] = $current_y;
  296. if ($this->value['position']=='relative' || $this->value['position']=='absolute')
  297. {
  298. if ($this->value['right']!==null)
  299. {
  300. $x = $this->getLastWidth(true) - $this->value['right'] - $this->value['width'];
  301. if ($this->value['margin']['r']) $x-= $this->value['margin']['r'];
  302. }
  303. else
  304. {
  305. $x = $this->value['left'];
  306. if ($this->value['margin']['l']) $x+= $this->value['margin']['l'];
  307. }
  308. if ($this->value['bottom']!==null)
  309. {
  310. $y = $this->getLastHeight(true) - $this->value['bottom'] - $this->value['height'];
  311. if ($this->value['margin']['b']) $y-= $this->value['margin']['b'];
  312. }
  313. else
  314. {
  315. $y = $this->value['top'];
  316. if ($this->value['margin']['t']) $y+= $this->value['margin']['t'];
  317. }
  318. if ($this->value['position']=='relative')
  319. {
  320. $this->value['x'] = $current_x + $x;
  321. $this->value['y'] = $current_y + $y;
  322. }
  323. else
  324. {
  325. $this->value['x'] = $this->getLastAbsoluteX()+$x;
  326. $this->value['y'] = $this->getLastAbsoluteY()+$y;
  327. }
  328. }
  329. else
  330. {
  331. $this->value['x'] = $current_x;
  332. $this->value['y'] = $current_y;
  333. if ($this->value['margin']['l']) $this->value['x']+= $this->value['margin']['l'];
  334. if ($this->value['margin']['t']) $this->value['y']+= $this->value['margin']['t'];
  335. }
  336. $this->pdf->setXY($this->value['x'], $this->value['y']);
  337. }
  338. /**
  339. * Analyse un tableau de style provenant du parseurHTML
  340. *
  341. * @param string nom de la balise
  342. * @param array tableau de style
  343. * @return null
  344. */
  345. public function getSvgStyle($balise, &$param)
  346. {
  347. // preparation
  348. $balise = strtolower($balise);
  349. $id = isset($param['id']) ? strtolower(trim($param['id'])) : null; if (!$id) $id = null;
  350. $name = isset($param['name']) ? strtolower(trim($param['name'])) : null; if (!$name) $name = null;
  351. // lecture de la propriete classe
  352. $class = array();
  353. $tmp = isset($param['class']) ? strtolower(trim($param['class'])) : '';
  354. $tmp = explode(' ', $tmp);
  355. foreach($tmp as $k => $v)
  356. {
  357. $v = trim($v);
  358. if ($v) $class[] = $v;
  359. }
  360. // identification de la balise et des styles direct qui pourraient lui ętre appliqués
  361. $this->value['id_balise'] = $balise;
  362. $this->value['id_name'] = $name;
  363. $this->value['id_id'] = $id;
  364. $this->value['id_class'] = $class;
  365. $this->value['id_lst'] = array();
  366. $this->value['id_lst'][] = '*';
  367. $this->value['id_lst'][] = $balise;
  368. if (!isset($this->value['svg']))
  369. {
  370. $this->value['svg'] = array(
  371. 'stroke' => null,
  372. 'stroke-width' => $this->ConvertToMM('1pt'),
  373. 'fill' => null,
  374. 'fill-opacity' => null,
  375. );
  376. }
  377. if (count($class))
  378. {
  379. foreach($class as $v)
  380. {
  381. $this->value['id_lst'][] = '*.'.$v;
  382. $this->value['id_lst'][] = '.'.$v;
  383. $this->value['id_lst'][] = $balise.'.'.$v;
  384. }
  385. }
  386. if ($id)
  387. {
  388. $this->value['id_lst'][] = '*#'.$id;
  389. $this->value['id_lst'][] = '#'.$id;
  390. $this->value['id_lst'][] = $balise.'#'.$id;
  391. }
  392. // style CSS
  393. $styles = $this->getFromCSS();
  394. // on ajoute le style propre ŕ la balise
  395. $styles = array_merge($styles, $param['style']);
  396. if (isset($styles['stroke'])) $this->value['svg']['stroke'] = $this->ConvertToColor($styles['stroke'], $res);
  397. if (isset($styles['stroke-width'])) $this->value['svg']['stroke-width'] = $this->ConvertToMM($styles['stroke-width']);
  398. if (isset($styles['fill'])) $this->value['svg']['fill'] = $this->ConvertToColor($styles['fill'], $res);
  399. if (isset($styles['fill-opacity'])) $this->value['svg']['fill-opacity'] = 1.*$styles['fill-opacity'];
  400. return $this->value['svg'];
  401. }
  402. /**
  403. * Analyse un tableau de style provenant du parseurHTML
  404. *
  405. * @param string nom de la balise
  406. * @param array tableau de style
  407. * @param array tableau initialisant des styles
  408. * @return null
  409. */
  410. public function analyse($balise, &$param, $heritage = null)
  411. {
  412. // preparation
  413. $balise = strtolower($balise);
  414. $id = isset($param['id']) ? strtolower(trim($param['id'])) : null; if (!$id) $id = null;
  415. $name = isset($param['name']) ? strtolower(trim($param['name'])) : null; if (!$name) $name = null;
  416. // lecture de la propriete classe
  417. $class = array();
  418. $tmp = isset($param['class']) ? strtolower(trim($param['class'])) : '';
  419. $tmp = explode(' ', $tmp);
  420. foreach($tmp as $k => $v)
  421. {
  422. $v = trim($v);
  423. if ($v) $class[] = $v;
  424. }
  425. // identification de la balise et des styles direct qui pourraient lui ętre appliqués
  426. $this->value['id_balise'] = $balise;
  427. $this->value['id_name'] = $name;
  428. $this->value['id_id'] = $id;
  429. $this->value['id_class'] = $class;
  430. $this->value['id_lst'] = array();
  431. $this->value['id_lst'][] = '*';
  432. $this->value['id_lst'][] = $balise;
  433. if (count($class))
  434. {
  435. foreach($class as $v)
  436. {
  437. $this->value['id_lst'][] = '*.'.$v;
  438. $this->value['id_lst'][] = '.'.$v;
  439. $this->value['id_lst'][] = $balise.'.'.$v;
  440. }
  441. }
  442. if ($id)
  443. {
  444. $this->value['id_lst'][] = '*#'.$id;
  445. $this->value['id_lst'][] = '#'.$id;
  446. $this->value['id_lst'][] = $balise.'#'.$id;
  447. }
  448. // style CSS
  449. $styles = $this->getFromCSS();
  450. // on ajoute le style propre ŕ la balise
  451. $styles = array_merge($styles, $param['style']);
  452. if (isset($param['allwidth']) && !isset($styles['width'])) $styles['width'] = '100%';
  453. // mise ŕ zero des styles non hérités
  454. $this->resetStyle($balise);
  455. if ($heritage)
  456. {
  457. foreach($heritage as $he_nom => $he_val)
  458. {
  459. if (is_array($he_val))
  460. {
  461. foreach($he_val as $he2_nom => $he2_val)
  462. $this->value[$he_nom][$he2_nom] = $he2_val;
  463. }
  464. else
  465. $this->value[$he_nom] = $he_val;
  466. }
  467. }
  468. // interpreration des nouvelles valeurs
  469. $correct_width = false;
  470. $no_width = true;
  471. foreach($styles as $nom => $val)
  472. {
  473. switch($nom)
  474. {
  475. case 'font-family':
  476. $val = explode(',', $val);
  477. $val = trim($val[0]);
  478. if ($val) $this->value['font-family'] = $val;
  479. break;
  480. case 'font-weight':
  481. $this->value['font-bold'] = ($val=='bold');
  482. break;
  483. case 'font-style':
  484. $this->value['font-italic'] = ($val=='italic');
  485. break;
  486. case 'text-decoration':
  487. $val = explode(' ', $val);
  488. $this->value['font-underline'] = (in_array('underline', $val));
  489. $this->value['font-overline'] = (in_array('overline', $val));
  490. $this->value['font-linethrough'] = (in_array('line-through', $val));
  491. break;
  492. case 'text-indent':
  493. $this->value['text-indent'] = $this->ConvertToMM($val);
  494. break;
  495. case 'text-transform':
  496. if (!in_array($val, array('none', 'capitalize', 'uppercase', 'lowercase'))) $val = 'none';
  497. $this->value['text-transform'] = $val;
  498. break;
  499. case 'font-size':
  500. $val = $this->ConvertToMM($val, $this->value['font-size']);
  501. if ($val) $this->value['font-size'] = $val;
  502. break;
  503. case 'color':
  504. $res = null;
  505. $this->value['color'] = $this->ConvertToColor($val, $res);
  506. if ($balise=='hr')
  507. {
  508. $this->value['border']['l']['color'] = $this->value['color'];
  509. $this->value['border']['t']['color'] = $this->value['color'];
  510. $this->value['border']['r']['color'] = $this->value['color'];
  511. $this->value['border']['b']['color'] = $this->value['color'];
  512. }
  513. break;
  514. case 'text-align':
  515. $val = strtolower($val);
  516. if (!in_array($val, array('left', 'right', 'center', 'justify', 'li_right'))) $val = 'left';
  517. $this->value['text-align'] = $val;
  518. break;
  519. case 'vertical-align':
  520. $this->value['vertical-align'] = $val;
  521. break;
  522. case 'width':
  523. $this->value['width'] = $this->ConvertToMM($val, $this->getLastWidth());
  524. if ($this->value['width'] && substr($val, -1)=='%') $correct_width=true;
  525. $no_width = false;
  526. break;
  527. case 'height':
  528. $this->value['height'] = $this->ConvertToMM($val, $this->getLastHeight());
  529. break;
  530. case 'line-height':
  531. if (preg_match('/^[0-9\.]+$/isU', $val)) $val = floor($val*100).'%';
  532. $this->value['line-height'] = $val;
  533. break;
  534. case 'rotate':
  535. if (!in_array($val, array(0, -90, 90, 180, 270, -180, -270))) $val = null;
  536. if ($val<0) $val+= 360;
  537. $this->value['rotate'] = $val;
  538. break;
  539. case 'overflow':
  540. if (!in_array($val, array('visible', 'hidden'))) $val = 'visible';
  541. $this->value['overflow'] = $val;
  542. break;
  543. case 'padding':
  544. $val = explode(' ', $val);
  545. foreach($val as $k => $v)
  546. {
  547. $v = trim($v);
  548. if ($v!='') $val[$k] = $v;
  549. else unset($val[$k]);
  550. }
  551. $val = array_values($val);
  552. $this->duplicateBorder($val);
  553. $this->value['padding']['t'] = $this->ConvertToMM($val[0], 0);
  554. $this->value['padding']['r'] = $this->ConvertToMM($val[1], 0);
  555. $this->value['padding']['b'] = $this->ConvertToMM($val[2], 0);
  556. $this->value['padding']['l'] = $this->ConvertToMM($val[3], 0);
  557. break;
  558. case 'padding-top':
  559. $this->value['padding']['t'] = $this->ConvertToMM($val, 0);
  560. break;
  561. case 'padding-right':
  562. $this->value['padding']['r'] = $this->ConvertToMM($val, 0);
  563. break;
  564. case 'padding-bottom':
  565. $this->value['padding']['b'] = $this->ConvertToMM($val, 0);
  566. break;
  567. case 'padding-left':
  568. $this->value['padding']['l'] = $this->ConvertToMM($val, 0);
  569. break;
  570. case 'margin':
  571. if ($val=='auto')
  572. {
  573. $this->value['margin-auto'] = true;
  574. break;
  575. }
  576. $val = explode(' ', $val);
  577. foreach($val as $k => $v)
  578. {
  579. $v = trim($v);
  580. if ($v!='') $val[$k] = $v;
  581. else unset($val[$k]);
  582. }
  583. $val = array_values($val);
  584. $this->duplicateBorder($val);
  585. $this->value['margin']['t'] = $this->ConvertToMM($val[0], 0);
  586. $this->value['margin']['r'] = $this->ConvertToMM($val[1], 0);
  587. $this->value['margin']['b'] = $this->ConvertToMM($val[2], 0);
  588. $this->value['margin']['l'] = $this->ConvertToMM($val[3], 0);
  589. break;
  590. case 'margin-top':
  591. $this->value['margin']['t'] = $this->ConvertToMM($val, 0);
  592. break;
  593. case 'margin-right':
  594. $this->value['margin']['r'] = $this->ConvertToMM($val, 0);
  595. break;
  596. case 'margin-bottom':
  597. $this->value['margin']['b'] = $this->ConvertToMM($val, 0);
  598. break;
  599. case 'margin-left':
  600. $this->value['margin']['l'] = $this->ConvertToMM($val, 0);
  601. break;
  602. case 'border':
  603. $val = $this->readBorder($val);
  604. $this->value['border']['t'] = $val;
  605. $this->value['border']['r'] = $val;
  606. $this->value['border']['b'] = $val;
  607. $this->value['border']['l'] = $val;
  608. break;
  609. case 'border-style':
  610. $val = explode(' ', $val);
  611. foreach($val as $val_k => $val_v)
  612. if (!in_array($val_v, array('solid', 'dotted', 'dashed')))
  613. $val[$val_k] = null;
  614. $this->duplicateBorder($val);
  615. if ($val[0]) $this->value['border']['t']['type'] = $val[0];
  616. if ($val[1]) $this->value['border']['r']['type'] = $val[1];
  617. if ($val[2]) $this->value['border']['b']['type'] = $val[2];
  618. if ($val[3]) $this->value['border']['l']['type'] = $val[3];
  619. break;
  620. case 'border-top-style':
  621. if (in_array($val, array('solid', 'dotted', 'dashed')))
  622. $this->value['border']['t']['type'] = $val;
  623. break;
  624. case 'border-right-style':
  625. if (in_array($val, array('solid', 'dotted', 'dashed')))
  626. $this->value['border']['r']['type'] = $val;
  627. break;
  628. case 'border-bottom-style':
  629. if (in_array($val, array('solid', 'dotted', 'dashed')))
  630. $this->value['border']['b']['type'] = $val;
  631. break;
  632. case 'border-left-style':
  633. if (in_array($val, array('solid', 'dotted', 'dashed')))
  634. $this->value['border']['l']['type'] = $val;
  635. break;
  636. case 'border-color':
  637. $res = false;
  638. $val = preg_replace('/,[\s]+/', ',', $val);
  639. $val = explode(' ', $val);
  640. foreach($val as $val_k => $val_v)
  641. {
  642. $val[$val_k] = $this->ConvertToColor($val_v, $res);
  643. if (!$res) $val[$val_k] = null;
  644. }
  645. $this->duplicateBorder($val);
  646. if (is_array($val[0])) $this->value['border']['t']['color'] = $val[0];
  647. if (is_array($val[1])) $this->value['border']['r']['color'] = $val[1];
  648. if (is_array($val[2])) $this->value['border']['b']['color'] = $val[2];
  649. if (is_array($val[3])) $this->value['border']['l']['color'] = $val[3];
  650. break;
  651. case 'border-top-color':
  652. $res = false;
  653. $val = $this->ConvertToColor($val, $res);
  654. if ($res) $this->value['border']['t']['color'] = $val;
  655. break;
  656. case 'border-right-color':
  657. $res = false;
  658. $val = $this->ConvertToColor($val, $res);
  659. if ($res) $this->value['border']['r']['color'] = $val;
  660. break;
  661. case 'border-bottom-color':
  662. $res = false;
  663. $val = $this->ConvertToColor($val, $res);
  664. if ($res) $this->value['border']['b']['color'] = $val;
  665. break;
  666. case 'border-left-color':
  667. $res = false;
  668. $val = $this->ConvertToColor($val, $res);
  669. if ($res) $this->value['border']['l']['color'] = $val;
  670. break;
  671. case 'border-width':
  672. $val = explode(' ', $val);
  673. foreach($val as $val_k => $val_v)
  674. {
  675. $val[$val_k] = $this->ConvertToMM($val_v, 0);
  676. }
  677. $this->duplicateBorder($val);
  678. if ($val[0]) $this->value['border']['t']['width'] = $val[0];
  679. if ($val[1]) $this->value['border']['r']['width'] = $val[1];
  680. if ($val[2]) $this->value['border']['b']['width'] = $val[2];
  681. if ($val[3]) $this->value['border']['l']['width'] = $val[3];
  682. break;
  683. case 'border-top-width':
  684. $val = $this->ConvertToMM($val, 0);
  685. if ($val) $this->value['border']['t']['width'] = $val;
  686. break;
  687. case 'border-right-width':
  688. $val = $this->ConvertToMM($val, 0);
  689. if ($val) $this->value['border']['r']['width'] = $val;
  690. break;
  691. case 'border-bottom-width':
  692. $val = $this->ConvertToMM($val, 0);
  693. if ($val) $this->value['border']['b']['width'] = $val;
  694. break;
  695. case 'border-left-width':
  696. $val = $this->ConvertToMM($val, 0);
  697. if ($val) $this->value['border']['l']['width'] = $val;
  698. break;
  699. case 'border-collapse':
  700. if ($balise=='table') $this->value['border']['collapse'] = ($val=='collapse');
  701. break;
  702. case 'border-radius':
  703. $val = explode('/', $val); if (count($val)>2) break;
  704. $val_h = $this->ConvertToRadius(trim($val[0]));
  705. if (count($val_h)<1 || count($val_h)>4) break;
  706. if (!isset($val_h[1])) $val_h[1] = $val_h[0];
  707. if (!isset($val_h[2])) $val_h = array($val_h[0], $val_h[0], $val_h[1], $val_h[1]);
  708. if (!isset($val_h[3])) $val_h[3] = $val_h[1];
  709. if (isset($val[1]))
  710. {
  711. $val_v = $this->ConvertToRadius(trim($val[1]));
  712. if (count($val_v)<1 || count($val_v)>4) break;
  713. if (!isset($val_v[1])) $val_v[1] = $val_v[0];
  714. if (!isset($val_v[2])) $val_v = array($val_v[0], $val_v[0], $val_v[1], $val_v[1]);
  715. if (!isset($val_v[3])) $val_v[3] = $val_v[1];
  716. }
  717. else
  718. $val_v = $val_h;
  719. $this->value['border']['radius'] = array(
  720. 'tl' => array($val_h[0], $val_v[0]),
  721. 'tr' => array($val_h[1], $val_v[1]),
  722. 'br' => array($val_h[2], $val_v[2]),
  723. 'bl' => array($val_h[3], $val_v[3])
  724. );
  725. break;
  726. case 'border-top-left-radius':
  727. $val = $this->ConvertToRadius($val);
  728. if (count($val)<1 || count($val)>2) break;
  729. $this->value['border']['radius']['tl'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
  730. break;
  731. case 'border-top-right-radius':
  732. $val = $this->ConvertToRadius($val);
  733. if (count($val)<1 || count($val)>2) break;
  734. $this->value['border']['radius']['tr'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
  735. break;
  736. case 'border-bottom-right-radius':
  737. $val = $this->ConvertToRadius($val);
  738. if (count($val)<1 || count($val)>2) break;
  739. $this->value['border']['radius']['br'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
  740. break;
  741. case 'border-bottom-left-radius':
  742. $val = $this->ConvertToRadius($val);
  743. if (count($val)<1 || count($val)>2) break;
  744. $this->value['border']['radius']['bl'] = array($val[0], isset($val[1]) ? $val[1] : $val[0]);
  745. break;
  746. case 'border-top':
  747. $this->value['border']['t'] = $this->readBorder($val);
  748. break;
  749. case 'border-right':
  750. $this->value['border']['r'] = $this->readBorder($val);
  751. break;
  752. case 'border-bottom':
  753. $this->value['border']['b'] = $this->readBorder($val);
  754. break;
  755. case 'border-left':
  756. $this->value['border']['l'] = $this->readBorder($val);
  757. break;
  758. case 'background-color':
  759. $this->value['background']['color'] = $this->ConvertBackgroundColor($val);
  760. break;
  761. case 'background-image':
  762. $this->value['background']['image'] = $this->ConvertBackgroundImage($val);
  763. break;
  764. case 'background-position':
  765. $res = null;
  766. $this->value['background']['position'] = $this->ConvertBackgroundPosition($val, $res);
  767. break;
  768. case 'background-repeat':
  769. $this->value['background']['repeat'] = $this->ConvertBackgroundRepeat($val);
  770. break;
  771. case 'background':
  772. $this->ConvertBackground($val, $this->value['background']);
  773. break;
  774. case 'position':
  775. if ($val=='absolute') $this->value['position'] = 'absolute';
  776. else if ($val=='relative') $this->value['position'] = 'relative';
  777. else $this->value['position'] = null;
  778. break;
  779. case 'float':
  780. if ($val=='left') $this->value['float'] = 'left';
  781. else if ($val=='right') $this->value['float'] = 'right';
  782. else $this->value['float'] = null;
  783. break;
  784. case 'display':
  785. if ($val=='inline') $this->value['display'] = 'inline';
  786. else if ($val=='block') $this->value['display'] = 'block';
  787. else if ($val=='none') $this->value['display'] = 'none';
  788. else $this->value['display'] = null;
  789. break;
  790. case 'top':
  791. case 'bottom':
  792. case 'left':
  793. case 'right':
  794. $this->value[$nom] = $val;
  795. break;
  796. case 'list-style':
  797. case 'list-style-type':
  798. case 'list-style-image':
  799. if ($nom=='list-style') $nom = 'list-style-type';
  800. $this->value[$nom] = $val;
  801. break;
  802. default:
  803. break;
  804. }
  805. }
  806. $return = true;
  807. if ($this->value['margin']['t']===null) $this->value['margin']['t'] = $this->value['font-size'];
  808. if ($this->value['margin']['b']===null) $this->value['margin']['b'] = $this->value['font-size'];
  809. if ($this->onlyLeft) $this->value['text-align'] = 'left';
  810. // correction de la largeur pour correspondre au modčle de boite quick
  811. if ($no_width && in_array($balise, array('div', 'fieldset')) && $this->value['position']!='absolute')
  812. {
  813. $this->value['width'] = $this->getLastWidth();
  814. $this->value['width']-= $this->value['margin']['l'] + $this->value['margin']['r'];
  815. }
  816. else
  817. {
  818. if ($correct_width)
  819. {
  820. if (!in_array($balise, array('table', 'div', 'fieldset', 'hr')))
  821. {
  822. $this->value['width']-= $this->value['padding']['l'] + $this->value['padding']['r'];
  823. $this->value['width']-= $this->value['border']['l']['width'] + $this->value['border']['r']['width'];
  824. }
  825. if (in_array($balise, array('th', 'td')))
  826. {
  827. $this->value['width']-= $this->ConvertToMM(isset($param['cellspacing']) ? $param['cellspacing'] : '2px');
  828. $return = false;
  829. }
  830. if ($this->value['width']<0) $this->value['width']=0;
  831. }
  832. else
  833. {
  834. if ($this->value['width'])
  835. {
  836. if ($this->value['border']['l']['width']) $this->value['width'] += $this->value['border']['l']['width'];
  837. if ($this->value['border']['r']['width']) $this->value['width'] += $this->value['border']['r']['width'];
  838. if ($this->value['padding']['l']) $this->value['width'] += $this->value['padding']['l'];
  839. if ($this->value['padding']['r']) $this->value['width'] += $this->value['padding']['r'];
  840. }
  841. }
  842. }
  843. if ($this->value['height'])
  844. {
  845. if ($this->value['border']['b']['width']) { $this->value['height'] += $this->value['border']['b']['width']; }
  846. if ($this->value['border']['t']['width']) { $this->value['height'] += $this->value['border']['t']['width']; }
  847. if ($this->value['padding']['b']) $this->value['height'] += $this->value['padding']['b'];
  848. if ($this->value['padding']['t']) $this->value['height'] += $this->value['padding']['t'];
  849. }
  850. if ($this->value['top']!=null) $this->value['top'] = $this->ConvertToMM($this->value['top'], $this->getLastHeight(true));
  851. if ($this->value['bottom']!=null) $this->value['bottom'] = $this->ConvertToMM($this->value['bottom'], $this->getLastHeight(true));
  852. if ($this->value['left']!=null) $this->value['left'] = $this->ConvertToMM($this->value['left'], $this->getLastWidth(true));
  853. if ($this->value['right']!=null) $this->value['right'] = $this->ConvertToMM($this->value['right'], $this->getLastWidth(true));
  854. if ($this->value['top'] && $this->value['bottom'] && $this->value['height']) $this->value['bottom'] = null;
  855. if ($this->value['left'] && $this->value['right'] && $this->value['width']) $this->value['right'] = null;
  856. return $return;
  857. }
  858. /**
  859. * Récupération de la hauteur de ligne courante
  860. *
  861. * @return float hauteur en mm
  862. */
  863. public function getLineHeight()
  864. {
  865. $val = $this->value['line-height'];
  866. if ($val=='normal') $val = '108%';
  867. return $this->ConvertToMM($val, $this->value['font-size']);
  868. }
  869. /**
  870. * Récupération de la largeur de l'objet parent
  871. *
  872. * @return float largeur
  873. */
  874. public function getLastWidth($mode = false)
  875. {
  876. for($k=count($this->table)-1; $k>=0; $k--)
  877. {
  878. if ($this->table[$k]['width'])
  879. {
  880. $w = $this->table[$k]['width'];
  881. if ($mode)
  882. {
  883. $w+= $this->table[$k]['border']['l']['width'] + $this->table[$k]['padding']['l'] + 0.02;
  884. $w+= $this->table[$k]['border']['r']['width'] + $this->table[$k]['padding']['r'] + 0.02;
  885. }
  886. return $w;
  887. }
  888. }
  889. return $this->pdf->getW() - $this->pdf->getlMargin() - $this->pdf->getrMargin();
  890. }
  891. /**
  892. * Récupération de la hauteur de l'objet parent
  893. *
  894. * @return float hauteur
  895. */
  896. public function getLastHeight($mode = false)
  897. {
  898. for($k=count($this->table)-1; $k>=0; $k--)
  899. {
  900. if ($this->table[$k]['height'])
  901. {
  902. $h = $this->table[$k]['height'];
  903. if ($mode)
  904. {
  905. $h+= $this->table[$k]['border']['t']['width'] + $this->table[$k]['padding']['t'] + 0.02;
  906. $h+= $this->table[$k]['border']['b']['width'] + $this->table[$k]['padding']['b'] + 0.02;
  907. }
  908. return $h;
  909. }
  910. }
  911. return $this->pdf->getH() - $this->pdf->gettMargin() - $this->pdf->getbMargin();
  912. }
  913. public function getFloat()
  914. {
  915. if ($this->value['float']=='left') return 'left';
  916. if ($this->value['float']=='right') return 'right';
  917. return null;
  918. }
  919. public function getLastValue($key)
  920. {
  921. $nb = count($this->table);
  922. if ($nb>0)
  923. return $this->table[$nb-1][$key];
  924. return null;
  925. }
  926. protected function getLastAbsoluteX()
  927. {
  928. for($k=count($this->table)-1; $k>=0; $k--)
  929. {
  930. if ($this->table[$k]['x'] && $this->table[$k]['position']) return $this->table[$k]['x'];
  931. }
  932. return $this->pdf->getlMargin();
  933. }
  934. protected function getLastAbsoluteY()
  935. {
  936. for($k=count($this->table)-1; $k>=0; $k--)
  937. {
  938. if ($this->table[$k]['y'] && $this->table[$k]['position']) return $this->table[$k]['y'];
  939. }
  940. return $this->pdf->gettMargin();
  941. }
  942. /**
  943. * Récupération des propriétés CSS de la balise en cours
  944. *
  945. * @return array() tableau des propriétés CSS
  946. */
  947. protected function getFromCSS()
  948. {
  949. $styles = array(); // style ŕ appliquer
  950. $getit = array(); // styles ŕ récuperer
  951. // identification des styles direct, et ceux des parents
  952. $lst = array();
  953. $lst[] = $this->value['id_lst'];
  954. for($i=count($this->table)-1; $i>=0; $i--) $lst[] = $this->table[$i]['id_lst'];
  955. // identification des styles ŕ récuperer
  956. foreach($this->css_keys as $key => $num)
  957. if ($this->getReccursiveStyle($key, $lst))
  958. $getit[$key] = $num;
  959. // si des styles sont ŕ recuperer
  960. if (count($getit))
  961. {
  962. // on les récupčre, mais dans l'odre de définition, afin de garder les priorités
  963. asort($getit);
  964. foreach($getit as $key => $val) $styles = array_merge($styles, $this->css[$key]);
  965. }
  966. return $styles;
  967. }
  968. /**
  969. * Identification des styles ŕ récuperer, en fonction de la balise et de ses parents
  970. *
  971. * @param string clef CSS ŕ analyser
  972. * @param array() tableau des styles direct, et ceux des parents
  973. * @param string prochaine etape
  974. * @return boolean clef autorisée ou non
  975. */
  976. protected function getReccursiveStyle($key, $lst, $next = null)
  977. {
  978. // si propchaine etape, on construit les valeurs
  979. if ($next!==null)
  980. {
  981. if ($next) $key = trim(substr($key, 0, -strlen($next))); // on elčve cette etape
  982. unset($lst[0]);
  983. if (!count($lst)) return false; // pas d'etape possible
  984. $lst = array_values($lst);
  985. }
  986. // pour chaque style direct possible de l'etape en cours
  987. foreach($lst[0] as $nom)
  988. {
  989. if ($key==$nom) return true; // si la clef conrrespond => ok
  990. if (substr($key, -strlen(' '.$nom))==' '.$nom && $this->getReccursiveStyle($key, $lst, $nom)) return true; // si la clef est la fin, on analyse ce qui précčde
  991. }
  992. // si on est pas ŕ la premiere etape, on doit analyse toutes les sous etapes
  993. if ($next!==null && $this->getReccursiveStyle($key, $lst, '')) return true;
  994. // aucun style trouvé
  995. return false;
  996. }
  997. /**
  998. * Analyse d'une propriété Border
  999. *
  1000. * @param string propriété border
  1001. * @return array() propriété décodée
  1002. */
  1003. public function readBorder($val)
  1004. {
  1005. $none = array('type' => 'none', 'width' => 0, 'color' => array(0, 0, 0));
  1006. // valeurs par défault
  1007. $type = 'solid';
  1008. $width = $this->ConvertToMM('1pt');
  1009. $color = array(0, 0, 0);
  1010. // nettoyage des valeurs
  1011. $val = explode(' ', $val);
  1012. foreach($val as $k => $v)
  1013. {
  1014. $v = trim($v);
  1015. if ($v) $val[$k] = $v;
  1016. else unset($val[$k]);
  1017. }
  1018. $val = array_values($val);
  1019. // identification des valeurs
  1020. $res = null;
  1021. foreach($val as $key)
  1022. {
  1023. if ($key=='none' || $key=='hidden') return $none;
  1024. if ($this->ConvertToMM($key)!==null)
  1025. $width = $this->ConvertToMM($key);
  1026. else if (in_array($key, array('solid', 'dotted', 'dashed', 'double')))
  1027. $type = $key;
  1028. else
  1029. {
  1030. $tmp = $this->ConvertToColor($key, $res);
  1031. if ($res) $color = $tmp;
  1032. }
  1033. }
  1034. if (!$width) return $none;
  1035. return array('type' => $type, 'width' => $width, 'color' => $color);
  1036. }
  1037. protected function duplicateBorder(&$val)
  1038. {
  1039. if (count($val)==1)
  1040. {
  1041. $val[1] = $val[0];
  1042. $val[2] = $val[0];
  1043. $val[3] = $val[0];
  1044. }
  1045. else if (count($val)==2)
  1046. {
  1047. $val[2] = $val[0];
  1048. $val[3] = $val[1];
  1049. }
  1050. else if (count($val)==3)
  1051. {
  1052. $val[3] = $val[1];
  1053. }
  1054. }
  1055. public function ConvertBackground($stl, &$res)
  1056. {
  1057. // Image
  1058. $text = '/url\(([^)]*)\)/isU';
  1059. if (preg_match($text, $stl, $match))
  1060. {
  1061. $res['image'] = $this->ConvertBackgroundImage($match[0]);
  1062. $stl = preg_replace($text, '', $stl);
  1063. $stl = preg_replace('/[\s]+/', ' ', $stl);
  1064. }
  1065. // protection des espaces
  1066. $stl = preg_replace('/,[\s]+/', ',', $stl);
  1067. $lst = explode(' ', $stl);
  1068. $pos = '';
  1069. foreach($lst as $val)
  1070. {
  1071. $ok = false;
  1072. $color = $this->ConvertToColor($val, $ok);
  1073. if ($ok)
  1074. {
  1075. $res['color'] = $color;
  1076. }
  1077. else if ($val=='transparent')
  1078. {
  1079. $res['color'] = null;
  1080. }
  1081. else
  1082. {
  1083. $repeat = $this->ConvertBackgroundRepeat($val);
  1084. if ($repeat)
  1085. {
  1086. $res['repeat'] = $repeat;
  1087. }
  1088. else
  1089. {
  1090. $pos.= ($pos ? ' ' : '').$val;
  1091. }
  1092. }
  1093. }
  1094. if ($pos)
  1095. {
  1096. $pos = $this->ConvertBackgroundPosition($pos, $ok);
  1097. if ($ok) $res['position'] = $pos;
  1098. }
  1099. }
  1100. public function ConvertBackgroundColor($val)
  1101. {
  1102. $res = null;
  1103. if ($val=='transparent') return null;
  1104. else return $this->ConvertToColor($val, $res);
  1105. }
  1106. public function ConvertBackgroundImage($val)
  1107. {
  1108. if ($val=='none')
  1109. return null;
  1110. else if (preg_match('/^url\(([^)]*)\)$/isU', $val, $match))
  1111. return $match[1];
  1112. else
  1113. return null;
  1114. }
  1115. public function ConvertBackgroundPosition($val, &$res)
  1116. {
  1117. $val = explode(' ', $val);
  1118. if (count($val)<2)
  1119. {
  1120. if (!$val[0]) return null;
  1121. $val[1] = 'center';
  1122. }
  1123. if (count($val)>2) return null;
  1124. $x = 0;
  1125. $y = 0;
  1126. $res = true;
  1127. if ($val[0]=='left') $x = '0%';
  1128. else if ($val[0]=='center') $x = '50%';
  1129. else if ($val[0]=='right') $x = '100%';
  1130. else if ($val[0]=='top') $y = '0%';
  1131. else if ($val[0]=='bottom') $y = '100%';
  1132. else if (preg_match('/^[-]?[0-9\.]+%$/isU', $val[0])) $x = $val[0];
  1133. else if ($this->ConvertToMM($val[0])) $x = $this->ConvertToMM($val[0]);
  1134. else $res = false;
  1135. if ($val[1]=='left') $x = '0%';
  1136. else if ($val[1]=='right') $x = '100%';
  1137. else if ($val[1]=='top') $y = '0%';
  1138. else if ($val[1]=='center') $y = '50%';
  1139. else if ($val[1]=='bottom') $y = '100%';
  1140. else if (preg_match('/^[-]?[0-9\.]+%$/isU', $val[1])) $y = $val[1];
  1141. else if ($this->ConvertToMM($val[1])) $y = $this->ConvertToMM($val[1]);
  1142. else $res = false;
  1143. $val[0] = $x;
  1144. $val[1] = $y;
  1145. return $val;
  1146. }
  1147. public function ConvertBackgroundRepeat($val)
  1148. {
  1149. switch($val)
  1150. {
  1151. case 'repeat':
  1152. return array(true, true);
  1153. case 'repeat-x':
  1154. return array(true, false);
  1155. case 'repeat-y':
  1156. return array(false, true);
  1157. case 'no-repeat':
  1158. return array(false, false);
  1159. }
  1160. return null;
  1161. }
  1162. /**
  1163. * Convertir une longueur en mm
  1164. *
  1165. * @param string longueur, avec unité, ŕ convertir
  1166. * @param float longueur du parent
  1167. * @return float longueur exprimée en mm
  1168. */
  1169. public function ConvertToMM($val, $old=0.)
  1170. {
  1171. $val = trim($val);
  1172. if (preg_match('/^[0-9\.\-]+$/isU', $val)) $val.= 'px';
  1173. if (preg_match('/^[0-9\.\-]+px$/isU', $val)) $val = 25.4/96. * str_replace('px', '', $val);
  1174. else if (preg_match('/^[0-9\.\-]+pt$/isU', $val)) $val = 25.4/72. * str_replace('pt', '', $val);
  1175. else if (preg_match('/^[0-9\.\-]+in$/isU', $val)) $val = 25.4 * str_replace('in', '', $val);
  1176. else if (preg_match('/^[0-9\.\-]+mm$/isU', $val)) $val = 1.*str_replace('mm', '', $val);
  1177. else if (preg_match('/^[0-9\.\-]+%$/isU', $val)) $val = 1.*$old*str_replace('%', '', $val)/100.;
  1178. else $val = null;
  1179. return $val;
  1180. }
  1181. public function ConvertToRadius($val)
  1182. {
  1183. $val = explode(' ', $val);
  1184. foreach($val as $k => $v)
  1185. {
  1186. $v = trim($v);
  1187. if ($v)
  1188. {
  1189. $v = $this->ConvertToMM($v, 0);
  1190. if ($v!==null)
  1191. $val[$k] = $v;
  1192. else
  1193. unset($val[$k]);
  1194. }
  1195. else unset($val[$k]);
  1196. }
  1197. return array_values($val);
  1198. }
  1199. /**
  1200. * Décomposition d'un code couleur HTML
  1201. *
  1202. * @param string couleur au format CSS
  1203. * @return array(r, v, b) couleur exprimé par ses comporantes R, V, B, de 0 ŕ 255.
  1204. */
  1205. public function ConvertToColor($val, &$res)
  1206. {
  1207. $val = trim($val);
  1208. $res = true;
  1209. if (strtolower($val)=='transparent') return array(null, null, null);
  1210. if (isset($this->htmlColor[strtolower($val)]))
  1211. {
  1212. $val = $this->htmlColor[strtolower($val)];
  1213. $r = floatVal(hexdec(substr($val, 0, 2)));
  1214. $v = floatVal(hexdec(substr($val, 2, 2)));
  1215. $b = floatVal(hexdec(substr($val, 4, 2)));
  1216. $col = array($r, $v, $b);
  1217. }
  1218. elseif (preg_match('/^#[0-9A-Fa-f]{6}$/isU', $val))
  1219. {
  1220. $r = floatVal(hexdec(substr($val, 1, 2)));
  1221. $v = floatVal(hexdec(substr($val, 3, 2)));
  1222. $b = floatVal(hexdec(substr($val, 5, 2)));
  1223. $col = array($r, $v, $b);
  1224. }
  1225. elseif (preg_match('/^#[0-9A-F]{3}$/isU', $val))
  1226. {
  1227. $r = floatVal(hexdec(substr($val, 1, 1).substr($val, 1, 1)));
  1228. $v = floatVal(hexdec(substr($val, 2, 1).substr($val, 2, 1)));
  1229. $b = floatVal(hexdec(substr($val, 3, 1).substr($val, 3, 1)));
  1230. $col = array($r, $v, $b);
  1231. }
  1232. elseif (preg_match('/rgb\([\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*\)/isU', $val, $match))
  1233. {
  1234. $r = $this->ConvertSubColor($match[1]);
  1235. $v = $this->ConvertSubColor($match[2]);
  1236. $b = $this->ConvertSubColor($match[3]);
  1237. $col = array($r*255., $v*255., $b*255.);
  1238. }
  1239. elseif (preg_match('/cmyk\([\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*,[\s]*([0-9%\.]+)[\s]*\)/isU', $val, $match))
  1240. {
  1241. $c = $this->ConvertSubColor($match[1]);
  1242. $m = $this->ConvertSubColor($match[2]);
  1243. $y = $this->ConvertSubColor($match[3]);
  1244. $k = $this->ConvertSubColor($match[4]);
  1245. $col = array($c*100., $m*100., $y*100., $k*100.);
  1246. }
  1247. else
  1248. {
  1249. $col = array(0., 0., 0.);
  1250. $res = false;
  1251. }
  1252. return $col;
  1253. }
  1254. protected function ConvertSubColor($c)
  1255. {
  1256. if (substr($c, -1)=='%') $c = floatVal(substr($c, 0, -1))/100.;
  1257. else
  1258. {
  1259. $c = floatVal($c);
  1260. if ($c>1) $c = $c/255.;
  1261. }
  1262. return $c;
  1263. }
  1264. /**
  1265. * Analyser une feuille de style
  1266. *
  1267. * @param string code CSS
  1268. * @return null
  1269. */
  1270. protected function analyseStyle(&$code)
  1271. {
  1272. // on remplace tous les espaces, tab, \r, \n, par des espaces uniques
  1273. $code = preg_replace('/[\s]+/', ' ', $code);
  1274. // on enlčve les commentaires
  1275. $code = preg_replace('/\/\*.*?\*\//s', '', $code);
  1276. // on analyse chaque style
  1277. preg_match_all('/([^{}]+){([^}]*)}/isU', $code, $match);
  1278. for($k=0; $k<count($match[0]); $k++)
  1279. {
  1280. // noms
  1281. $noms = strtolower(trim($match[1][$k]));
  1282. // style, séparé par des; => on remplie le tableau correspondant
  1283. $styles = trim($match[2][$k]);
  1284. $styles = explode(';', $styles);
  1285. $stl = array();
  1286. foreach($styles as $style)
  1287. {
  1288. $tmp = explode(':', $style);
  1289. if (count($tmp)>1)
  1290. {
  1291. $cod = $tmp[0]; unset($tmp[0]); $tmp = implode(':', $tmp);
  1292. $stl[trim(strtolower($cod))] = trim($tmp);
  1293. }
  1294. }
  1295. // décomposition des noms par les ,
  1296. $noms = explode(',', $noms);
  1297. foreach($noms as $nom)
  1298. {
  1299. $nom = trim($nom);
  1300. // Si il a une fonction spécifique, comme :hover => on zap
  1301. if (strpos($nom, ':')!==false) continue;
  1302. if (!isset($this->css[$nom]))
  1303. $this->css[$nom] = $stl;
  1304. else
  1305. $this->css[$nom] = array_merge($this->css[$nom], $stl);
  1306. }
  1307. }
  1308. $this->css_keys = array_flip(array_keys($this->css));
  1309. }
  1310. /**
  1311. * Extraction des feuille de style du code HTML
  1312. *
  1313. * @param string code HTML
  1314. * @return null
  1315. */
  1316. public function readStyle(&$html)
  1317. {
  1318. $style = ' ';
  1319. // extraction des balises link, et suppression de celles-ci dans le code HTML
  1320. preg_match_all('/<link([^>]*)>/isU', $html, $match);
  1321. $html = preg_replace('/<link[^>]*>/isU', '', $html);
  1322. $html = preg_replace('/<\/link[^>]*>/isU', '', $html);
  1323. // analyse de chaque balise
  1324. foreach($match[1] as $code)
  1325. {
  1326. $tmp = array();
  1327. // lecture des paramétres du type nom=valeur
  1328. $prop = '([a-zA-Z0-9_]+)=([^"\'\s>]+)';
  1329. preg_match_all('/'.$prop.'/is', $code, $match);
  1330. for($k=0; $k<count($match[0]); $k++)
  1331. $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
  1332. // lecture des paramétres du type nom="valeur"
  1333. $prop = '([a-zA-Z0-9_]+)=["]([^"]*)["]';
  1334. preg_match_all('/'.$prop.'/is', $code, $match);
  1335. for($k=0; $k<count($match[0]); $k++)
  1336. $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
  1337. // lecture des paramétres du type nom='valeur'
  1338. $prop = "([a-zA-Z0-9_]+)=[']([^']*)[']";
  1339. preg_match_all('/'.$prop.'/is', $code, $match);
  1340. for($k=0; $k<count($match[0]); $k++)
  1341. $tmp[trim(strtolower($match[1][$k]))] = trim($match[2][$k]);
  1342. // si de type text/css => on garde
  1343. if (isset($tmp['type']) && strtolower($tmp['type'])=='text/css' && isset($tmp['href']))
  1344. {
  1345. $content = @file_get_contents($tmp['href']);
  1346. $url = $tmp['href'];
  1347. if (strpos($url, 'http://')!==false)
  1348. {
  1349. $url = str_replace('http://', '', $url);
  1350. $url = explode('/', $url);
  1351. $url_main = 'http://'.$url[0].'/';
  1352. $url_self = $url; unset($url_self[count($url_self)-1]); $url_self = 'http://'.implode('/', $url_self).'/';
  1353. $content = preg_replace('/url\(([^\\\\][^)]*)\)/isU', 'url('.$url_self.'$1)', $content);
  1354. $content = preg_replace('/url\((\\\\[^)]*)\)/isU', 'url('.$url_main.'$1)', $content);
  1355. }
  1356. else
  1357. {
  1358. // @todo
  1359. // $content = preg_replace('/url\(([^)]*)\)/isU', 'url('.dirname($url).'/$1)', $content);
  1360. }
  1361. $style.= $content."\n";
  1362. }
  1363. }
  1364. // extraction des balises style, et suppression de celles-ci dans le code HTML
  1365. preg_match_all('/<style[^>]*>(.*)<\/style[^>]*>/isU', $html, $match);
  1366. $html = preg_replace('/<style[^>]*>(.*)<\/style[^>]*>/isU', '', $html);
  1367. // analyse de chaque balise
  1368. foreach($match[1] as $code)
  1369. {
  1370. $code = str_replace('<!--', '', $code);
  1371. $code = str_replace('-->', '', $code);
  1372. $style.= $code."\n";
  1373. }
  1374. $this->analyseStyle($style);
  1375. }
  1376. }