Genrator Image Text


/ Published in: PHP
Save to your folder(s)

Create a simple image with text


Copy this code and paste it in your HTML
  1. <?php
  2. /**
  3.   * ImageText class, class.generatorimagetext.php
  4.   * Module Stickers Custom Text
  5.   * @category classes
  6.   *
  7.   * @author Steven Titren <[email protected]>
  8.   * @copyright webaki.com
  9.   *
  10.   */
  11.  
  12. class ImageText
  13. {
  14. /**
  15.   * La largeur de l'image
  16.   * @var int
  17.   * @access protected
  18.   */
  19. protected $_width;
  20.  
  21. /**
  22.   * La hauteur de l'image
  23.   * @var int
  24.   * @access protected
  25.   */
  26. protected $_height;
  27.  
  28. /**
  29.   * Le chemin vers la police à utiliser
  30.   * @var string
  31.   * @access protected
  32.   */
  33. protected $_font;
  34.  
  35. /**
  36.   * La taille de la police (en pt)
  37.   * @var int
  38.   * @access protected
  39.   */
  40. protected $_fontSize;
  41.  
  42. /**
  43.   * La couleur du texte en RGB
  44.   * @var array
  45.   * @access protected
  46.   */
  47. protected $_textColor;
  48.  
  49. /**
  50.   * La couleur du fond en RGB
  51.   * @var array
  52.   * @access protected
  53.   */
  54. protected $_bgColor;
  55.  
  56. /**
  57.   * Le nombre de point par pouce (dot per inch)
  58.   * @var int
  59.   * @access protected
  60.   */
  61. protected $_dpi = 72;
  62.  
  63. /**
  64.   * Le texte à afficher dans l'image
  65.   * @var string
  66.   * @access protected
  67.   */
  68. protected $_string;
  69.  
  70. /**
  71.   * Le flux de l'image créé
  72.   * @var resource
  73.   * @access protected
  74.   */
  75. protected $_img;
  76.  
  77. /**
  78.   * La position du texte sur l'axe x
  79.   * @var int
  80.   * @access protected
  81.   */
  82. protected $_posX;
  83.  
  84. /**
  85.   * La position du texte sur l'axe y
  86.   * @var int
  87.   * @access protected
  88.   */
  89. protected $_posY;
  90.  
  91. /**
  92.   * Enregistre les valeurs possible pour les position sur l'axe x
  93.   * @var array
  94.   * @access protected
  95.   */
  96. protected $_posXlitteral = array('left', 'center', 'right');
  97.  
  98. /**
  99.   * Enregistre les valeurs possible pour les position sur l'axe y
  100.   * @var array
  101.   * @access protected
  102.   */
  103. protected $_posYlitteral = array('top', 'middle', 'bottom');
  104.  
  105. /**
  106.   * Constructeur
  107.   * @param string $string La texte à afficher dans l'image
  108.   * @param int $width La largeur de l'image
  109.   * @param int $height La hauteur de l'image
  110.   * @param string $font Le chemin vers la police d'écriture à utiliser
  111.   * @param int $fontSize La taille de la police
  112.   * @param array $textColor La couleur du texte
  113.   * @param array $bgColor La couleur de fond de l'image
  114.   * @param int $dpi Le nombre de point par pouce (dot per inch)
  115.   * @return void
  116.   */
  117. public function __construct (
  118. $string=null,
  119. $width=null, $height=null,
  120. $font=null, $fontSize=null,
  121. $textColor=array(), $bgColor=array(),
  122. $dpi = null
  123. )
  124. {
  125. if($string!=null)
  126. $this->setString($string);
  127.  
  128. if($width!=null)
  129. $this->setWidth($width);
  130.  
  131. if($height!=null)
  132. $this->setHeight($height);
  133.  
  134. if($font!=null)
  135. $this->setFont($font);
  136.  
  137. if($fontSize!=null)
  138. $this->setFontSizeInPx($fontSize);
  139.  
  140. if(is_array($textColor))
  141. {
  142. if(count($textColor)>0)
  143. $this->setTextColorRGB($textColor);
  144. }
  145. else
  146. {
  147. if($textColor!=null)
  148. $this->setTextColorHexa($textColor);
  149. }
  150.  
  151. if(is_array($bgColor))
  152. {
  153. if(count($bgColor)>0)
  154. $this->setBgColorRGB($bgColor);
  155. }
  156. else
  157. {
  158. if($bgColor!=null)
  159. $this->setBgColorHexa($bgColor);
  160. }
  161.  
  162. if($dpi!=null)
  163. $this->setDpi($dpi);
  164.  
  165. $this->setPosition('center middle');
  166. }
  167.  
  168. /**
  169.   * Récupérer le taille du texte
  170.   * @return array La largeur et la hauteur de la police en pixel
  171.   */
  172. public function getTextSizePX()
  173. {
  174. $bbox = $this->_getBBox();
  175.  
  176. if(!$bbox)
  177. return false;
  178.  
  179. $w = intval(abs($bbox[2] - $bbox[0]));
  180. $h = intval(abs($bbox[7] - $bbox[1]));
  181.  
  182. return array('width'=>$w, 'height'=>$h);
  183. }
  184.  
  185. /**
  186.   * Récupérer le taille du texte
  187.   * @return array La largeur et la hauteur de la police
  188.   */
  189. public function getTextSizeCm()
  190. {
  191. $sizePX = $this->getTextSizePX();
  192.  
  193. if(!$sizePX)
  194. return false;
  195.  
  196. $w = self::convertPxInCm($sizePX['width']);
  197. $h = self::convertPxInCm($sizePX['height']);
  198.  
  199. return array('width'=>$w, 'height'=>$h);
  200. }
  201.  
  202. /**
  203.   * Retourne la superfie du texte
  204.   * @return float
  205.   */
  206. public function calculateArea()
  207. {
  208. $size = $this->getTextSizeCm();
  209.  
  210. if(!$size)
  211. return false;
  212.  
  213. return ($size['width']/100) * ($size['height']/100);
  214. }
  215.  
  216. /**
  217.   * Contruit l'image sans l'afficher
  218.   * @return boolean
  219.   */
  220. public function buildStream()
  221. {
  222. if(!$this->_isReady())
  223. return $this->_reportError(__METHOD__, __LINE__, 'Le générateur manque d\'information pour continuer.');
  224.  
  225. $this->_img = imagecreatetruecolor($this->_width, $this->_height);
  226.  
  227. if(!$this->_img)
  228. return false;
  229.  
  230. $bgColor = imagecolorallocate($this->_img, $this->_bgColor[0], $this->_bgColor[1], $this->_bgColor[2]);
  231. $textColor = imagecolorallocate($this->_img, $this->_textColor[0], $this->_textColor[1], $this->_textColor[2]);
  232.  
  233. imagefilledrectangle($this->_img, 0, 0, ($this->_width-1), ($this->_height-1), $bgColor);
  234.  
  235. imagefttext($this->_img, $this->_fontSize, 0, $this->_posX, $this->_posY, $textColor, $this->_font, $this->_string);
  236.  
  237. return true;
  238. }
  239.  
  240. /**
  241.   * Affiche l'image
  242.   * @return void
  243.   */
  244. public function displayImage()
  245. {
  246. header('Content-type: image/png');
  247. imagepng($this->_img);
  248. imagedestroy($this->_img);
  249. }
  250.  
  251. public function saveImage($filename)
  252. {
  253. imagepng($this->_img, $filename);
  254. imagedestroy($this->_img);
  255. }
  256.  
  257. /**
  258.   * Vérifie que le générateur à toute les informations
  259.   * @return boolean
  260.   */
  261. public function _isReady()
  262. {
  263. if(strlen($this->_string)<=0)
  264. return false;
  265.  
  266. if($this->_width<=0)
  267. return false;
  268.  
  269. if($this->_height<=0)
  270. return false;
  271.  
  272. if(!$this->checkFontFile())
  273. return false;
  274.  
  275. if($this->_fontSize<=0)
  276. return false;
  277.  
  278. if(!self::checkColorRGG($this->_textColor))
  279. return false;
  280.  
  281. if(!self::checkColorRGG($this->_bgColor))
  282. return false;
  283.  
  284. if($this->_dpi<=0)
  285. return false;
  286.  
  287. if($this->_posX<0 || $this->_posX>=$this->_width)
  288. return false;
  289.  
  290. if($this->_posY<0 || $this->_posY>=$this->_height)
  291. return false;
  292.  
  293. return true;
  294. }
  295.  
  296.  
  297. /****************/
  298. /*** SETTER ***/
  299. /****************/
  300.  
  301.  
  302. /**
  303.   * Changer le texte à afficher
  304.   * @param string $string La texte à afficher dans l'image
  305.   * @return boolean
  306.   */
  307. public function setString($string)
  308. {
  309. $this->_string = trim($string);
  310.  
  311. if(strlen($this->_string)<=0)
  312. return $this->_reportError(__METHOD__, __LINE__, 'La chaine de caractère ne doit pas être vide.');
  313.  
  314. return true;
  315. }
  316.  
  317. /**
  318.   * Changer la largeur de l'image
  319.   * @param int $width La largeur de l'image
  320.   * @return boolean
  321.   */
  322. public function setWidth($width)
  323. {
  324. $width = intval($width);
  325.  
  326. if($width<=0)
  327. return $this->_reportError(__METHOD__, __LINE__, 'La largeur de l\'image doit être strictement supérieur à 0px');
  328.  
  329. $this->_width = $width;
  330. return true;
  331. }
  332.  
  333. /**
  334.   * Changer la hauteur de l'image
  335.   * @param int $width La hauteur de l'image
  336.   * @return boolean
  337.   */
  338. public function setHeight($height)
  339. {
  340. $height = intval($height);
  341.  
  342. if($height<=0)
  343. return $this->_reportError(__METHOD__, __LINE__, 'La hauteur de l\'image doit être strictement supérieur à 0px');
  344.  
  345. $this->_height = $height;
  346. return true;
  347. }
  348.  
  349. /**
  350.   * Changer le chemin de la police d'écriture
  351.   * @param string $font La police d'écriture
  352.   * @return boolean
  353.   */
  354. public function setFont($font)
  355. {
  356. if(!is_file($font))
  357. return $this->_reportError(__METHOD__, __LINE__, 'Le fichier de la police n\'existe pas.');
  358.  
  359. $this->_font = trim($font);
  360. return true;
  361. }
  362.  
  363. /**
  364.   * Changer la taille de la police
  365.   * @param int $fontSize La taille de la police en pixel
  366.   * @return boolean
  367.   */
  368. public function setFontSizeInPx($fontSize)
  369. {
  370. $fontSize = intval($fontSize);
  371.  
  372. if($fontSize<=0)
  373. return $this->_reportError(__METHOD__, __LINE__, 'La taille de la police doit être strictement supérieur à 0px');
  374.  
  375. $this->_fontSize = $fontSize;
  376. return true;
  377. }
  378.  
  379. /**
  380.   * Changer la taille de la police
  381.   * @param int $fontSize La taille de la police en cm
  382.   * @return boolean
  383.   */
  384. public function setFontSizeInCm($fontSize)
  385. {
  386. $fontSize = intval($fontSize);
  387.  
  388. if($fontSize<=0)
  389. return $this->_reportError(__METHOD__, __LINE__, 'La taille de la police doit être strictement supérieur à 0px');
  390.  
  391. $this->_fontSize = self::convertCmInPx($fontSize, $this->_dpi);
  392. return true;
  393. }
  394.  
  395. /**
  396.   * Changer la couleur du texte
  397.   * @param array $color La couleur du texte en RGB
  398.   * @return boolean
  399.   */
  400. public function setTextColorRGB($color)
  401. {
  402. if(!self::checkColorRGG($color))
  403. return $this->_reportError(__METHOD__, __LINE__, 'Couleur RGB invalide');
  404.  
  405. $this->_textColor = $color;
  406. return true;
  407. }
  408.  
  409. /**
  410.   * Changer la couleur du texte
  411.   * @param string $color La couleur du texte en hexadecimal
  412.   * @return boolean
  413.   */
  414. public function setTextColorHexa($color)
  415. {
  416. $colorRGB = self::colorHexaToRGB($color);
  417.  
  418. return ($colorRGB!==false) ? $this->setTextColorRGB($colorRGB) : false;
  419. }
  420.  
  421. /**
  422.   * Changer la couleur du fond
  423.   * @param array $color La couleur du fond en RGB
  424.   * @return boolean
  425.   */
  426. public function setBgColorRGB($color)
  427. {
  428. if(!self::checkColorRGG($color))
  429. return $this->_reportError(__METHOD__, __LINE__, 'Couleur RGB invalide');
  430.  
  431. $this->_bgColor = $color;
  432. return true;
  433. }
  434.  
  435. /**
  436.   * Changer la couleur du fond
  437.   * @param string $color La couleur du fond en hexadecimal
  438.   * @return boolean
  439.   */
  440. public function setBgColorHexa($color)
  441. {
  442. $colorRGB = self::colorHexaToRGB($color);
  443.  
  444. return ($colorRGB!==false) ? $this->setBgColorRGB($colorRGB) : false;
  445. }
  446.  
  447. /**
  448.   * Changer le nombre de ppp (ou dpi)
  449.   * @param int $dpi Le nombre de ppp (ou dpi)
  450.   * @return boolean
  451.   */
  452. public function setDpi($dpi)
  453. {
  454. $dpi = intval($dpi);
  455.  
  456. if($dpi<0)
  457. return $this->_reportError(__METHOD__, __LINE__, 'Le nombre de point par pour (dot per inch) doit être strictement supérieur à 0.');
  458.  
  459. $this->_dpi = $dpi;
  460. return true;
  461. }
  462.  
  463. /**
  464.   * Change la position du texte en X
  465.   * @param int $posX La position en X
  466.   * @return boolean
  467.   */
  468. public function setPosX($posX)
  469. {
  470. $posX = intval($posX);
  471.  
  472. if($posX<0)
  473. return $this->_reportError(__METHOD__, __LINE__, 'La position en x doit être strictement supérieur à 0.');
  474.  
  475. $this->_posX = $posX;
  476. return true;
  477. }
  478.  
  479. /**
  480.   * Change la position du texte en Y
  481.   * @param int $posY La position en Y
  482.   * @return boolean
  483.   */
  484. public function setPosY($posY)
  485. {
  486. $posY = intval($posY);
  487.  
  488. if($posY<0)
  489. return $this->_reportError(__METHOD__, __LINE__, 'La position en y doit être strictement supérieur à 0.');
  490.  
  491. $this->_posY = $posY;
  492. return true;
  493. }
  494.  
  495. /**
  496.   * Permet de spécifier la position du texte de façon littéral
  497.   * <p>
  498.   * Valeur possible: <br />
  499.   * left top
  500.   * left middle
  501.   * left bottom
  502.   * center top
  503.   * center middle
  504.   * center bottom
  505.   * right top
  506.   * right middle
  507.   * right bottom
  508.   * </p>
  509.   * @param string $pos La position du texte.
  510.   */
  511. public function setPosition($pos = 'left top')
  512. {
  513. $pos = explode(' ', $pos);
  514. if(count($pos)!=2)
  515. return $this->_reportError(__METHOD__, __LINE__, 'Merci de spécifie une position sur x et sur y.');
  516.  
  517. $pos1 = trim($pos[0]);
  518. $pos2 = trim($pos[1]);
  519.  
  520. if(in_array($pos1, $this->_posXlitteral))
  521. {
  522. $x = $this->_gePositionX($pos1);
  523. $y = $this->_gePositionY($pos2);
  524. }
  525. else
  526. {
  527. $x = $this->_gePositionX($pos2);
  528. $y = $this->_gePositionY($pos1);
  529. }
  530.  
  531. if($x===false || $y===false)
  532. return false;
  533.  
  534. $this->_posX = $x;
  535. $this->_posY = $y;
  536.  
  537. return true;
  538. }
  539.  
  540.  
  541. /****************/
  542. /*** GETTER ***/
  543. /****************/
  544.  
  545.  
  546. /**
  547.   * Récupérer le texte
  548.   * @return string
  549.   */
  550. public function getString()
  551. {
  552. return $this->_string;
  553. }
  554.  
  555. /**
  556.   * Récupérer la largeur de l'image
  557.   * @return int
  558.   */
  559. public function getWidth()
  560. {
  561. return $this->_width;
  562. }
  563.  
  564. /**
  565.   * Récupérer la hauteur de l'image
  566.   * @return int
  567.   */
  568. public function getHeight()
  569. {
  570. return $this->_height;
  571. }
  572.  
  573. /**
  574.   * Récupérer le chemin de la police d'écriture utilisé
  575.   * @return string
  576.   */
  577. public function getFont()
  578. {
  579. return $this->_font;
  580. }
  581.  
  582. /**
  583.   * Récupérer la taille de la police en px
  584.   * @return int
  585.   */
  586. public function getFontSizeInPx()
  587. {
  588. return $this->_fontSize;
  589. }
  590.  
  591. /**
  592.   * Récupérer la taille de la police en cm
  593.   * @return int
  594.   */
  595. public function getFontSizeInCm()
  596. {
  597. return self::convertPxInCm($this->_fontSize, $this->_dpi);
  598. }
  599.  
  600. /**
  601.   * Récupérer la couleur RGB du texte
  602.   * @return array
  603.   */
  604. public function getTextColorRGB()
  605. {
  606. return $this->_textColor;
  607. }
  608.  
  609. /**
  610.   * Récupérer la couleur Hexa du texte
  611.   * @return string
  612.   */
  613. public function getTextColorHexa()
  614. {
  615. return self::colorRGBToHexa($this->_textColor);
  616. }
  617.  
  618. /**
  619.   * Récupérer la couleur RGB du fond
  620.   * @return array
  621.   */
  622. public function getBgColorRGB()
  623. {
  624. return $this->_bgColor;
  625. }
  626.  
  627. /**
  628.   * Récupérer la couleur Hexa du fond
  629.   * @return string
  630.   */
  631. public function getBgColorHexa()
  632. {
  633. return self::colorRGBToHexa($this->_bgColor);
  634. }
  635.  
  636. /**
  637.   * Récupérer le nombre de ppp (ou dpi)
  638.   * @return int
  639.   */
  640. public function getDpi()
  641. {
  642. return $this->_dpi;
  643. }
  644.  
  645. /**
  646.   * Récupérer la position en X
  647.   * @return int
  648.   */
  649. public function getPosX()
  650. {
  651. return $this->_posX;
  652. }
  653.  
  654. /**
  655.   * Récupérer la position en Y
  656.   * @return int
  657.   */
  658. public function getPosY()
  659. {
  660. return $this->_posY;
  661. }
  662.  
  663. /**
  664.   * Calcule le rectangle d'encadrement pour le texte, en utilisant la police courante
  665.   * @return array
  666.   * @access protected
  667.   */
  668. protected function _getBBox()
  669. {
  670. if($this->_fontSize<=0 || !$this->checkFontFile())
  671. return false;
  672.  
  673. return imageftbbox($this->_fontSize, 0, $this->_font, $this->_string);
  674. }
  675.  
  676. /**
  677.   * Permet de récupérer la position en x de façon littéral
  678.   * @param string $posx La position en x souhaité (left, center, right)
  679.   * @return int|boolean
  680.   */
  681. protected function _gePositionX($posx)
  682. {
  683. $bbox = $this->_getBBox();
  684.  
  685. if(!$bbox)
  686. return false;
  687.  
  688. switch(trim($posx))
  689. {
  690. case 'left':
  691. $x = 0;
  692. break;
  693.  
  694. case 'center':
  695. $x = $bbox[0] + ($this->_width / 2) - ($bbox[2] / 2);
  696. break;
  697.  
  698. case 'right' :
  699. $x = $this->_width-($bbox[2]-$bbox[0]);
  700. break;
  701.  
  702. default:
  703. return false;
  704. }
  705.  
  706. return $x;
  707. }
  708.  
  709. /**
  710.   * Permet de récupérer la position en y de façon littéral
  711.   * @param string $posy La position en y souhaité (top, middle, bottom)
  712.   * @return int|boolean
  713.   */
  714. protected function _gePositionY($posy)
  715. {
  716. $bbox = $this->_getBBox();
  717.  
  718. if(!$bbox)
  719. return false;
  720.  
  721. switch(trim($posy))
  722. {
  723. case 'top':
  724. $y = abs($bbox[7]);
  725. break;
  726.  
  727. case 'middle':
  728. $y = $bbox[1] + ($this->_height / 2) - ($bbox[7] / 2);
  729. break;
  730.  
  731. case 'bottom' :
  732. $y = $this->_height;
  733. break;
  734.  
  735. default:
  736. return false;
  737. }
  738.  
  739. return $y;
  740. }
  741.  
  742.  
  743. /****************/
  744. /*** TOOLS ****/
  745. /****************/
  746.  
  747.  
  748. /**
  749.   * Verifie que la couleur RGb est correct
  750.   * @param array $colorRGB Le tableau contenu les index des différente teine
  751.   * @return boolean vrai s'il correspond d'un couleur RGB correct, faux sinon.
  752.   */
  753. public static function checkColorRGG($colorRGB)
  754. {
  755. if(!is_array($colorRGB))
  756. return false;
  757.  
  758. if(count($colorRGB)!=3)
  759. return false;
  760.  
  761. foreach($colorRGB as $color)
  762. {
  763. if($color<0 || $color>255)
  764. return false;
  765. }
  766.  
  767. return true;
  768. }
  769.  
  770. /**
  771.   * Transforme une couleur hexadécimal en couleur RGB
  772.   * @param string $colorHexa La couleur hexadécimal (avec ou sans le #)
  773.   * @return mixed Le tableau de la couleur, faux la conversion à échoué.
  774.   */
  775. public static function colorHexaToRGB($colorHexa)
  776. {
  777. $colorRGB = array();
  778.  
  779. // gestion du #...
  780. if (substr($colorHexa,0,1)=="#")
  781. $colorHexa=substr($colorHexa,1,6);
  782.  
  783. $colorRGB[0] = hexdec(substr($colorHexa, 0, 2));
  784. $colorRGB[1] = hexdec(substr($colorHexa, 2, 2));
  785. $colorRGB[2] = hexdec(substr($colorHexa, 4, 2));
  786.  
  787. return self::checkColorRGG($colorRGB) ? $colorRGB : false;
  788. }
  789.  
  790. /**
  791.   * Transforme une couleur RGB en couleur hexadécimal
  792.   * @param array $colorRGB La couleur RGB
  793.   * @return mixed Le code héxa de la couleur, faux si la conversion à échoué.
  794.   */
  795. public static function colorRGBToHexa($colorRGB)
  796. {
  797. if(self::checkColorRGG($colorRGB))
  798. return "#" . str_pad(dechex( ($colorRGB[0]<<16)|($colorRGB[1]<<8)|$colorRGB[2] ), 6, "0", STR_PAD_LEFT);
  799. else
  800. return false;
  801. }
  802.  
  803. /**
  804.   * Convertir des cm en pixel
  805.   * @param float $cm Le nombre de cm
  806.   * @param $dpi $dpi Le nombre de ppp
  807.   * @return int
  808.   */
  809. public static function convertCmInPx($cm, $dpi=72)
  810. {
  811. return round(($cm*$dpi)/2.54);
  812. }
  813.  
  814. /**
  815.   * Convertir des pixel en cm
  816.   * @param int $px Le nombre de px
  817.   * @param $dpi $dpi Le nombre de ppp
  818.   * @return flat
  819.   */
  820. public static function convertPxInCm($px, $dpi=72, $float=2)
  821. {
  822. return floatval(round(($px/$dpi)*2.54, $float));
  823. }
  824.  
  825. /**
  826.   * Calcule le nombre de pixel en changant de ppp
  827.   * @param int $px Le nombre de px
  828.   * @param int $dpi_origin Le nombre de ppp d'origine
  829.   * @param int $dpi_final Le nombre de ppp pour le calcul
  830.   * @return int
  831.   */
  832. public static function changeDPI($px, $dpi_origin, $dpi_final=72)
  833. {
  834. return round(($px*$dpi_origin)/$dpi_final);
  835. }
  836.  
  837. /**
  838.   * Permet de vérifier si le fichier de la police est correct
  839.   * @return boolean
  840.   */
  841. public function checkFontFile()
  842. {
  843. return is_file($this->_font);
  844. }
  845.  
  846. /**
  847. * Génére un rapport d'erreur et retourne la valeur false.
  848. * @param string $error L'erreur à retourner
  849. * @param string $method Une chaine contenant la classe et la méthode responsables de l'erreur ("Classe::Méthode")
  850. * @param int $line La ligne où l'erreur a été provoquée
  851. * @access protected
  852. * @return bolean Le booléen false
  853. */
  854. protected function _reportError($method = __METHOD__, $line = __LINE__, $error = null)
  855. {
  856. throw new Exception('['.$method.']{'.$line.'} '.trim($error));
  857. return false;
  858. }
  859. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.