Related
I'm creating a graph in fpdf and I saw this question and it is exactly the same as what I need but when I copied it on my report-weekly.php file it doesn't work and gave me an error message of
Notice: Undefined offset: 0 in C:\xampp\htdocs\qtqt\asd\report-weekly.php on line 112
Notice: Undefined offset: 1 in C:\xampp\htdocs\qtqt\asd\report-weekly.php on line 112
Notice: Undefined offset: 2 in C:\xampp\htdocs\qtqt\asd\report-weekly.php on line 112
FPDF error: Some data has already been output, can't send PDF file
this is the code I copied
<?php
require('diag/sector.php');
class PDF_Diag extends PDF_Sector {
var $legends;
var $wLegend;
var $sum;
var $NbVal;
function ColumnChart($w, $h, $data, $format, $color=null, $maxVal=0, $nbDiv=4)
{
// RGB for color 0
$colors[0][0] = 155;
$colors[0][1] = 75;
$colors[0][2] = 155;
// RGB for color 1
$colors[1][0] = 0;
$colors[1][1] = 155;
$colors[1][2] = 0;
// RGB for color 2
$colors[2][0] = 75;
$colors[2][1] = 155;
$colors[2][2] = 255;
// RGB for color 3
$colors[3][0] = 75;
$colors[3][1] = 0;
$colors[3][2] = 155;
$this->SetFont('Courier', '', 10);
$this->SetLegends($data,$format);
// Starting corner (current page position where the chart has been inserted)
$XPage = $this->GetX();
$YPage = $this->GetY();
$margin = 2;
// Y position of the chart
$YDiag = $YPage + $margin;
// chart HEIGHT
$hDiag = floor($h - $margin * 2);
// X position of the chart
$XDiag = $XPage + $margin;
// chart LENGHT
$lDiag = floor($w - $margin * 3 - $this->wLegend);
if($color == null)
$color=array(155,155,155);
if ($maxVal == 0)
{
foreach($data as $val)
{
if(max($val) > $maxVal)
{
$maxVal = max($val);
}
}
}
// define the distance between the visual reference lines (the lines which cross the chart's internal area and serve as visual reference for the column's heights)
$valIndRepere = ceil($maxVal / $nbDiv);
// adjust the maximum value to be plotted (recalculate through the newly calculated distance between the visual reference lines)
$maxVal = $valIndRepere * $nbDiv;
// define the distance between the visual reference lines (in milimeters)
$hRepere = floor($hDiag / $nbDiv);
// adjust the chart HEIGHT
$hDiag = $hRepere * $nbDiv;
// determine the height unit (milimiters/data unit)
$unit = $hDiag / $maxVal;
// determine the bar's thickness
$lBar = floor($lDiag / ($this->NbVal + 1));
$lDiag = $lBar * ($this->NbVal + 1);
$eColumn = floor($lBar * 80 / 100);
// draw the chart border
$this->SetLineWidth(0.2);
$this->Rect($XDiag, $YDiag, $lDiag, $hDiag);
$this->SetFont('Courier', '', 10);
$this->SetFillColor($color[0],$color[1],$color[2]);
$i=0;
foreach($data as $val)
{
//Column
$yval = $YDiag + $hDiag;
$xval = $XDiag + ($i + 1) * $lBar - $eColumn/2;
$lval = floor($eColumn/(count($val)));
$j=0;
foreach($val as $v)
{
$hval = (int)($v * $unit);
$this->SetFillColor($colors[$j][0], $colors[$j][1], $colors[$j][2]);
$this->Rect($xval+($lval*$j), $yval, $lval, -$hval, 'DF');
$j++;
}
//Legend
$this->SetXY($xval, $yval + $margin);
$this->Cell($lval, 5, $this->legends[$i],0,0,'C');
$i++;
}
//Scales
for ($i = 0; $i <= $nbDiv; $i++)
{
$ypos = $YDiag + $hRepere * $i;
$this->Line($XDiag, $ypos, $XDiag + $lDiag, $ypos);
$val = ($nbDiv - $i) * $valIndRepere;
$ypos = $YDiag + $hRepere * $i;
$xpos = $XDiag - $margin - $this->GetStringWidth($val);
$this->Text($xpos, $ypos, $val);
}
}
function SetLegends($data, $format)
{
$this->legends=array();
$this->wLegend=0;
$this->NbVal=count($data);
}
}
$pdf = new PDF_Diag();
$pdf->AddPage();
$data[0] = array(470, 490, 90);
$data[1] = array(450, 530, 110);
$data[2] = array(420, 580, 100);
// Column chart
$pdf->SetFont('Arial', 'BIU', 12);
$pdf->Cell(210, 5, 'Chart Title', 0, 1, 'C');
$pdf->Ln(8);
$valX = $pdf->GetX();
$valY = $pdf->GetY();
$pdf->ColumnChart(110, 100, $data, null, array(255,175,100));
//$pdf->SetXY($valX, $valY);
$pdf->Output();
?>
I would like to draw in a PDF file.
Example: Open the PDF file and get such drawing tools like circle, square, text etc... Using these tools will draw shapes on the PDF file.
I searched on google and found such options like pdf.js. But it's not implemented in core PHP or normal MVC structure. It's implemented in JavaScript.
Any alternative for pdf.js to draw the shape in a PDF file?
I am looking for the same as the example shown here.
Last year when I came upon the same problem I researched and after some tweaking and adjustments I managed to make it work. So here's a detailed explanation on how to set up and use my method.
I'm using a combination of two libraries:
FPDF: (Free Portable Document Format) which allows to generate PDF files with PHP
FPDI: (Free Portable Document Importer) which uses existing PDF and converts them to templates to used by FPDF
First, you'll need to download the two libraries: FPDF is found here in the download section and FPDI is on this page. You will be given two folders. Go ahead and add them to your project.
Here's my directory structure:
Let's go in index.php (or any other file for that matter) and edit a PDF file that we'll name sample.pdf. I had found some code from the official documentation but actually did some modifications to simplify it.
You will see I have added the method nextPage() to the PDF class to make the navigation between pages easier.
<?php
require_once('FPDF/fpdf.php');
require_once('FPDI/fpdi.php');
// path of PDF file
$fullPathToFile = "sample.pdf";
class PDF extends FPDI {
var $fileIndex;
var $currentPage = 1;
function Header() {
global $fullPathToFile;
if (is_null($this->fileIndex)) {
$this->numPages = $this->setSourceFile($fullPathToFile);
$this->fileIndex = $this->importPage(1);
} $this->useTemplate($this->fileIndex, 0, 0,200);
}
function nextPage() {
if($this->currentPage != 1) {
$this->fileIndex = $this->importPage($this->currentPage);
}
$this->addPage();
return ++$this->currentPage;
}
}
// initiate PDF
$pdf = new PDF();
// go to first page
$pdf->nextPage();
// add content to current page
$pdf->SetFont("helvetica", "", 20);
$pdf->SetTextColor(220, 20, 60);
$pdf->Text(50, 20, "I should not be here!");
// move to next page and add content
$pdf->nextPage();
$pdf->SetFont("arial", "", 15);
$pdf->SetTextColor(65, 105, 225);
$pdf->Text(50, 20, "Me neither!!!");
//show the PDF in page
$pdf->Output();
The Output() method can receive different arguments: you can simply output the PDF file in a frame or you can force the download of the PDF file to the user's computer. Read here for more information on that.
DEMO!
FPDF's community has written several scripts one of which might interest you:
it's the geometric figures FPDF plugin (id script69.php). It allows you to draw lines, rectangles, curves, ellipses, circles, polygon among others.
Here's a bonus for you:
Create a new file called draw.php and put the source code provided on here. I have provided the source code below (the first three lines are different than the original source code to make it work).
<?php
require_once('FPDF/fpdf.php');
require_once('FPDI/fpdi.php');
class PDF_Draw extends FPDI {
// Sets line style
// Parameters:
// - style: Line style. Array with keys among the following:
// . width: Width of the line in user units
// . cap: Type of cap to put on the line (butt, round, square). The difference between 'square' and 'butt' is that 'square' projects a flat end past the end of the line.
// . join: miter, round or bevel
// . dash: Dash pattern. Is 0 (without dash) or array with series of length values, which are the lengths of the on and off dashes.
// For example: (2) represents 2 on, 2 off, 2 on , 2 off ...
// (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
// . phase: Modifier of the dash pattern which is used to shift the point at which the pattern starts
// . color: Draw color. Array with components (red, green, blue)
function SetLineStyle($style) {
extract($style);
if (isset($width)) {
$width_prev = $this->LineWidth;
$this->SetLineWidth($width);
$this->LineWidth = $width_prev;
}
if (isset($cap)) {
$ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
if (isset($ca[$cap]))
$this->_out($ca[$cap] . ' J');
}
if (isset($join)) {
$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
if (isset($ja[$join]))
$this->_out($ja[$join] . ' j');
}
if (isset($dash)) {
$dash_string = '';
if ($dash) {
$tab = explode(',', $dash);
$dash_string = '';
foreach ($tab as $i => $v) {
if ($i > 0)
$dash_string .= ' ';
$dash_string .= sprintf('%.2F', $v);
}
}
if (!isset($phase) || !$dash)
$phase = 0;
$this->_out(sprintf('[%s] %.2F d', $dash_string, $phase));
}
if (isset($color)) {
list($r, $g, $b) = $color;
$this->SetDrawColor($r, $g, $b);
}
}
// Draws a line
// Parameters:
// - x1, y1: Start point
// - x2, y2: End point
// - style: Line style. Array like for SetLineStyle
function Line($x1, $y1, $x2, $y2, $style = null) {
if ($style)
$this->SetLineStyle($style);
parent::Line($x1, $y1, $x2, $y2);
}
// Draws a rectangle
// Parameters:
// - x, y: Top left corner
// - w, h: Width and height
// - style: Style of rectangle (draw and/or fill: D, F, DF, FD)
// - border_style: Border style of rectangle. Array with some of this index
// . all: Line style of all borders. Array like for SetLineStyle
// . L: Line style of left border. null (no border) or array like for SetLineStyle
// . T: Line style of top border. null (no border) or array like for SetLineStyle
// . R: Line style of right border. null (no border) or array like for SetLineStyle
// . B: Line style of bottom border. null (no border) or array like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
function Rect($x, $y, $w, $h, $style = '', $border_style = null, $fill_color = null) {
if (!(false === strpos($style, 'F')) && $fill_color) {
list($r, $g, $b) = $fill_color;
$this->SetFillColor($r, $g, $b);
}
switch ($style) {
case 'F':
$border_style = null;
parent::Rect($x, $y, $w, $h, $style);
break;
case 'DF': case 'FD':
if (!$border_style || isset($border_style['all'])) {
if (isset($border_style['all'])) {
$this->SetLineStyle($border_style['all']);
$border_style = null;
}
} else
$style = 'F';
parent::Rect($x, $y, $w, $h, $style);
break;
default:
if (!$border_style || isset($border_style['all'])) {
if (isset($border_style['all']) && $border_style['all']) {
$this->SetLineStyle($border_style['all']);
$border_style = null;
}
parent::Rect($x, $y, $w, $h, $style);
}
break;
}
if ($border_style) {
if (isset($border_style['L']) && $border_style['L'])
$this->Line($x, $y, $x, $y + $h, $border_style['L']);
if (isset($border_style['T']) && $border_style['T'])
$this->Line($x, $y, $x + $w, $y, $border_style['T']);
if (isset($border_style['R']) && $border_style['R'])
$this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
if (isset($border_style['B']) && $border_style['B'])
$this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
}
}
// Draws a Bézier curve (the Bézier curve is tangent to the line between the control points at either end of the curve)
// Parameters:
// - x0, y0: Start point
// - x1, y1: Control point 1
// - x2, y2: Control point 2
// - x3, y3: End point
// - style: Style of rectangule (draw and/or fill: D, F, DF, FD)
// - line_style: Line style for curve. Array like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style = '', $line_style = null, $fill_color = null) {
if (!(false === strpos($style, 'F')) && $fill_color) {
list($r, $g, $b) = $fill_color;
$this->SetFillColor($r, $g, $b);
}
switch ($style) {
case 'F':
$op = 'f';
$line_style = null;
break;
case 'FD': case 'DF':
$op = 'B';
break;
default:
$op = 'S';
break;
}
if ($line_style)
$this->SetLineStyle($line_style);
$this->_Point($x0, $y0);
$this->_Curve($x1, $y1, $x2, $y2, $x3, $y3);
$this->_out($op);
}
// Draws an ellipse
// Parameters:
// - x0, y0: Center point
// - rx, ry: Horizontal and vertical radius (if ry = 0, draws a circle)
// - angle: Orientation angle (anti-clockwise)
// - astart: Start angle
// - afinish: Finish angle
// - style: Style of ellipse (draw and/or fill: D, F, DF, FD, C (D + close))
// - line_style: Line style for ellipse. Array like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
// - nSeg: Ellipse is made up of nSeg Bézier curves
function Ellipse($x0, $y0, $rx, $ry = 0, $angle = 0, $astart = 0, $afinish = 360, $style = '', $line_style = null, $fill_color = null, $nSeg = 8) {
if ($rx) {
if (!(false === strpos($style, 'F')) && $fill_color) {
list($r, $g, $b) = $fill_color;
$this->SetFillColor($r, $g, $b);
}
switch ($style) {
case 'F':
$op = 'f';
$line_style = null;
break;
case 'FD': case 'DF':
$op = 'B';
break;
case 'C':
$op = 's'; // small 's' means closing the path as well
break;
default:
$op = 'S';
break;
}
if ($line_style)
$this->SetLineStyle($line_style);
if (!$ry)
$ry = $rx;
$rx *= $this->k;
$ry *= $this->k;
if ($nSeg < 2)
$nSeg = 2;
$astart = deg2rad((float) $astart);
$afinish = deg2rad((float) $afinish);
$totalAngle = $afinish - $astart;
$dt = $totalAngle/$nSeg;
$dtm = $dt/3;
$x0 *= $this->k;
$y0 = ($this->h - $y0) * $this->k;
if ($angle != 0) {
$a = -deg2rad((float) $angle);
$this->_out(sprintf('q %.2F %.2F %.2F %.2F %.2F %.2F cm', cos($a), -1 * sin($a), sin($a), cos($a), $x0, $y0));
$x0 = 0;
$y0 = 0;
}
$t1 = $astart;
$a0 = $x0 + ($rx * cos($t1));
$b0 = $y0 + ($ry * sin($t1));
$c0 = -$rx * sin($t1);
$d0 = $ry * cos($t1);
$this->_Point($a0 / $this->k, $this->h - ($b0 / $this->k));
for ($i = 1; $i <= $nSeg; $i++) {
// Draw this bit of the total curve
$t1 = ($i * $dt) + $astart;
$a1 = $x0 + ($rx * cos($t1));
$b1 = $y0 + ($ry * sin($t1));
$c1 = -$rx * sin($t1);
$d1 = $ry * cos($t1);
$this->_Curve(($a0 + ($c0 * $dtm)) / $this->k,
$this->h - (($b0 + ($d0 * $dtm)) / $this->k),
($a1 - ($c1 * $dtm)) / $this->k,
$this->h - (($b1 - ($d1 * $dtm)) / $this->k),
$a1 / $this->k,
$this->h - ($b1 / $this->k));
$a0 = $a1;
$b0 = $b1;
$c0 = $c1;
$d0 = $d1;
}
$this->_out($op);
if ($angle !=0)
$this->_out('Q');
}
}
// Draws a circle
// Parameters:
// - x0, y0: Center point
// - r: Radius
// - astart: Start angle
// - afinish: Finish angle
// - style: Style of circle (draw and/or fill) (D, F, DF, FD, C (D + close))
// - line_style: Line style for circle. Array like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
// - nSeg: Ellipse is made up of nSeg Bézier curves
function Circle($x0, $y0, $r, $astart = 0, $afinish = 360, $style = '', $line_style = null, $fill_color = null, $nSeg = 8) {
$this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nSeg);
}
// Draws a polygon
// Parameters:
// - p: Points. Array with values x0, y0, x1, y1,..., x(np-1), y(np - 1)
// - style: Style of polygon (draw and/or fill) (D, F, DF, FD)
// - line_style: Line style. Array with one of this index
// . all: Line style of all lines. Array like for SetLineStyle
// . 0..np-1: Line style of each line. Item is 0 (not line) or like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
function Polygon($p, $style = '', $line_style = null, $fill_color = null) {
$np = count($p) / 2;
if (!(false === strpos($style, 'F')) && $fill_color) {
list($r, $g, $b) = $fill_color;
$this->SetFillColor($r, $g, $b);
}
switch ($style) {
case 'F':
$line_style = null;
$op = 'f';
break;
case 'FD': case 'DF':
$op = 'B';
break;
default:
$op = 'S';
break;
}
$draw = true;
if ($line_style)
if (isset($line_style['all']))
$this->SetLineStyle($line_style['all']);
else { // 0 .. (np - 1), op = {B, S}
$draw = false;
if ('B' == $op) {
$op = 'f';
$this->_Point($p[0], $p[1]);
for ($i = 2; $i < ($np * 2); $i = $i + 2)
$this->_Line($p[$i], $p[$i + 1]);
$this->_Line($p[0], $p[1]);
$this->_out($op);
}
$p[$np * 2] = $p[0];
$p[($np * 2) + 1] = $p[1];
for ($i = 0; $i < $np; $i++)
if (!empty($line_style[$i]))
$this->Line($p[$i * 2], $p[($i * 2) + 1], $p[($i * 2) + 2], $p[($i * 2) + 3], $line_style[$i]);
}
if ($draw) {
$this->_Point($p[0], $p[1]);
for ($i = 2; $i < ($np * 2); $i = $i + 2)
$this->_Line($p[$i], $p[$i + 1]);
$this->_Line($p[0], $p[1]);
$this->_out($op);
}
}
// Draws a regular polygon
// Parameters:
// - x0, y0: Center point
// - r: Radius of circumscribed circle
// - ns: Number of sides
// - angle: Orientation angle (anti-clockwise)
// - circle: Draw circumscribed circle or not
// - style: Style of polygon (draw and/or fill) (D, F, DF, FD)
// - line_style: Line style. Array with one of this index
// . all: Line style of all lines. Array like for SetLineStyle
// . 0..ns-1: Line style of each line. Item is 0 (not line) or like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
// - circle_style: Style of circumscribed circle (draw and/or fill) (D, F, DF, FD) (if draw)
// - circle_line_style: Line style for circumscribed circle. Array like for SetLineStyle (if draw)
// - circle_fill_color: Fill color for circumscribed circle. Array with components (red, green, blue) (if draw fill circle)
function RegularPolygon($x0, $y0, $r, $ns, $angle = 0, $circle = false, $style = '', $line_style = null, $fill_color = null, $circle_style = '', $circle_line_style = null, $circle_fill_color = null) {
if ($ns < 3)
$ns = 3;
if ($circle)
$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_line_style, $circle_fill_color);
$p = null;
for ($i = 0; $i < $ns; $i++) {
$a = $angle + ($i * 360 / $ns);
$a_rad = deg2rad((float) $a);
$p[] = $x0 + ($r * sin($a_rad));
$p[] = $y0 + ($r * cos($a_rad));
}
$this->Polygon($p, $style, $line_style, $fill_color);
}
// Draws a star polygon
// Parameters:
// - x0, y0: Center point
// - r: Radius of circumscribed circle
// - nv: Number of vertices
// - ng: Number of gaps (ng % nv = 1 => regular polygon)
// - angle: Orientation angle (anti-clockwise)
// - circle: Draw circumscribed circle or not
// - style: Style of polygon (draw and/or fill) (D, F, DF, FD)
// - line_style: Line style. Array with one of this index
// . all: Line style of all lines. Array like for SetLineStyle
// . 0..n-1: Line style of each line. Item is 0 (not line) or like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
// - circle_style: Style of circumscribed circle (draw and/or fill) (D, F, DF, FD) (if draw)
// - circle_line_style: Line style for circumscribed circle. Array like for SetLineStyle (if draw)
// - circle_fill_color: Fill color for circumscribed circle. Array with components (red, green, blue) (if draw fill circle)
function StarPolygon($x0, $y0, $r, $nv, $ng, $angle = 0, $circle = false, $style = '', $line_style = null, $fill_color = null, $circle_style = '', $circle_line_style = null, $circle_fill_color = null) {
if ($nv < 2)
$nv = 2;
if ($circle)
$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_line_style, $circle_fill_color);
$p2 = null;
$visited = null;
for ($i = 0; $i < $nv; $i++) {
$a = $angle + ($i * 360 / $nv);
$a_rad = deg2rad((float) $a);
$p2[] = $x0 + ($r * sin($a_rad));
$p2[] = $y0 + ($r * cos($a_rad));
$visited[] = false;
}
$p = null;
$i = 0;
do {
$p[] = $p2[$i * 2];
$p[] = $p2[($i * 2) + 1];
$visited[$i] = true;
$i += $ng;
$i %= $nv;
} while (!$visited[$i]);
$this->Polygon($p, $style, $line_style, $fill_color);
}
// Draws a rounded rectangle
// Parameters:
// - x, y: Top left corner
// - w, h: Width and height
// - r: Radius of the rounded corners
// - round_corner: Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left
// - style: Style of rectangle (draw and/or fill) (D, F, DF, FD)
// - border_style: Border style of rectangle. Array like for SetLineStyle
// - fill_color: Fill color. Array with components (red, green, blue)
function RoundedRect($x, $y, $w, $h, $r, $round_corner = '1111', $style = '', $border_style = null, $fill_color = null) {
if ('0000' == $round_corner) // Not rounded
$this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
else { // Rounded
if (!(false === strpos($style, 'F')) && $fill_color) {
list($red, $g, $b) = $fill_color;
$this->SetFillColor($red, $g, $b);
}
switch ($style) {
case 'F':
$border_style = null;
$op = 'f';
break;
case 'FD': case 'DF':
$op = 'B';
break;
default:
$op = 'S';
break;
}
if ($border_style)
$this->SetLineStyle($border_style);
$MyArc = 4 / 3 * (sqrt(2) - 1);
$this->_Point($x + $r, $y);
$xc = $x + $w - $r;
$yc = $y + $r;
$this->_Line($xc, $y);
if ($round_corner[0])
$this->_Curve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
else
$this->_Line($x + $w, $y);
$xc = $x + $w - $r ;
$yc = $y + $h - $r;
$this->_Line($x + $w, $yc);
if ($round_corner[1])
$this->_Curve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
else
$this->_Line($x + $w, $y + $h);
$xc = $x + $r;
$yc = $y + $h - $r;
$this->_Line($xc, $y + $h);
if ($round_corner[2])
$this->_Curve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
else
$this->_Line($x, $y + $h);
$xc = $x + $r;
$yc = $y + $r;
$this->_Line($x, $yc);
if ($round_corner[3])
$this->_Curve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
else {
$this->_Line($x, $y);
$this->_Line($x + $r, $y);
}
$this->_out($op);
}
}
/* PRIVATE METHODS */
// Sets a draw point
// Parameters:
// - x, y: Point
function _Point($x, $y) {
$this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k));
}
// Draws a line from last draw point
// Parameters:
// - x, y: End point
function _Line($x, $y) {
$this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k));
}
// Draws a Bézier curve from last draw point
// Parameters:
// - x1, y1: Control point 1
// - x2, y2: Control point 2
// - x3, y3: End point
function _Curve($x1, $y1, $x2, $y2, $x3, $y3) {
$this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
}
}
?>
Don't forget to add require_once('FPDF/fpdf.php'); and require_once('FPDI/fpdi.php'); at the top of the page assuming you have this directory structure:
Then with code in index.php instead of extending the class FPDI we can directly extend PDF_Draw. This way the code we add earlier still work but now we can use new methods like Line(), Curve(), Rect() etc...
Here is the full index.php code:
<?php
require_once('draw.php');
// path of PDF file
$fullPathToFile = "sample.pdf";
class PDF extends PDF_Draw {
.
.
.
}
// initiate PDF
$pdf = new PDF();
// go to first page
$pdf->nextPage();
// add content to current page
$pdf->SetFont("helvetica", "", 20);
$pdf->SetTextColor(220, 20, 60);
$pdf->Text(50, 20, "I should not be here!");
// move to next page and add content
$pdf->nextPage();
$pdf->SetFont("arial", "", 15);
$style = array('width' => 0.5, 'cap' => 'butt', 'join' => 'miter', 'dash' => '10,20,5,10', 'phase' => 10, 'color' => array(255, 0, 0));
$style2 = array('width' => 0.5, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(255, 0, 0));
$style3 = array('width' => 1, 'cap' => 'round', 'join' => 'round', 'dash' => '2,10', 'color' => array(255, 0, 0));
$style4 = array('L' => 0,
'T' => array('width' => 0.25, 'cap' => 'butt', 'join' => 'miter', 'dash' => '20,10', 'phase' => 10, 'color' => array(100, 100, 255)),
'R' => array('width' => 0.50, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(50, 50, 127)),
'B' => array('width' => 0.75, 'cap' => 'square', 'join' => 'miter', 'dash' => '30,10,5,10'));
$style5 = array('width' => 0.25, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0));
$style6 = array('width' => 0.5, 'cap' => 'butt', 'join' => 'miter', 'dash' => '10,10', 'color' => array(0, 255, 0));
$style7 = array('width' => 0.5, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(200, 200, 0));
// Line
$pdf->Text(5, 7, 'Line');
$pdf->Line(5, 10, 80, 30, $style);
// Rect
$pdf->Text(100, 7, 'Rectangle');
$pdf->Rect(100, 10, 40, 20, 'DF', $style4, array(220, 220, 200));
// Curve
$pdf->Text(5, 37, 'Curve');
$pdf->Curve(5, 40, 30, 55, 70, 45, 60, 75, null, $style6);
// Circle and ellipse
$pdf->Text(5, 82, 'Circle and ellipse');
$pdf->SetLineStyle($style5);
$pdf->Circle(25,105,20);
// Polygon
$pdf->Text(5, 132, 'Polygon');
$pdf->SetLineStyle(array('width' => 0.5, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
$pdf->Polygon(array(5,135,45,135,15,165));
// Regular polygon
$pdf->Text(5, 172, 'Regular polygon');
$pdf->SetLineStyle($style5);
$pdf->RegularPolygon(20, 190, 15, 6, 0, 1, 'F');
// Star polygon
$pdf->Text(5, 212, 'Star polygon');
$pdf->SetLineStyle($style5);
$pdf->StarPolygon(20, 230, 15, 20, 3, 0, 1, 'F');
// Rounded rectangle
$pdf->Text(5, 252, 'Rounded rectangle');
$pdf->SetLineStyle(array('width' => 0.5, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
$pdf->RoundedRect(5, 255, 40, 30, 3.50, '1111', 'DF');
$pdf->SetTextColor(65, 105, 225);
$pdf->Text(50, 20, "Me neither!!!");
//show the PDF in page
$pdf->Output();
DEMO!
TCPDF (tcpdf.org) seems to handle PDF graphics methods.
Cf. examples/example_012.php:
// create new PDF document
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// Line
$pdf->Text(5, 4, 'Line examples');
$pdf->Line(5, 10, 80, 30, $style);
$pdf->Line(5, 10, 5, 30, $style2);
$pdf->Line(5, 10, 80, 10, $style3);
// Rect
$pdf->Text(100, 4, 'Rectangle examples');
$pdf->Rect(100, 10, 40, 20, 'DF', $style4, array(220, 220, 200));
$pdf->Rect(145, 10, 40, 20, 'D', array('all' => $style3));
And its GitHub project tecnickcom/tcpdf indicated being 100% PHP.
However, this search shows it can create and modify a new PDF document. It might not be able to open and modify an existing one.
http://www.fpdf.org
If You want to draw using x, y coordinates
fpdf as well as tcpdf can be useful for you.
global $title;
// Calculate width
$w = $this->GetStringWidth($title)+6;
$this->SetX((210-$w)/2);
// Colors of frame, background and text
$this->SetDrawColor(0,80,180);
$this->SetFillColor(230,230,0);
$this->SetTextColor(220,50,50);
// Thickness of frame (1 mm)
$this->SetLineWidth(1);
// Title
$this->Cell($w,9,$title,1,1,'C',true);
// Line break
$this->Ln(10);
}
In PHP, what is the most straightforward way to convert a RGB triplet to HSV values?
Here is a simple, straightforward method that returns HSV values as degrees and percentages, which is what Photoshop's color picker uses.
Note that the return values are not rounded, you can do that yourself if required. Keep in mind that H(360) == H(0), so H values of 359.5 and greater should round to 0
Heavily documented for learning purposes.
/**
* Licensed under the terms of the BSD License.
* (Basically, this means you can do whatever you like with it,
* but if you just copy and paste my code into your app, you
* should give me a shout-out/credit :)
*/
<?php
function RGBtoHSV($R, $G, $B) // RGB values: 0-255, 0-255, 0-255
{ // HSV values: 0-360, 0-100, 0-100
// Convert the RGB byte-values to percentages
$R = ($R / 255);
$G = ($G / 255);
$B = ($B / 255);
// Calculate a few basic values, the maximum value of R,G,B, the
// minimum value, and the difference of the two (chroma).
$maxRGB = max($R, $G, $B);
$minRGB = min($R, $G, $B);
$chroma = $maxRGB - $minRGB;
// Value (also called Brightness) is the easiest component to calculate,
// and is simply the highest value among the R,G,B components.
// We multiply by 100 to turn the decimal into a readable percent value.
$computedV = 100 * $maxRGB;
// Special case if hueless (equal parts RGB make black, white, or grays)
// Note that Hue is technically undefined when chroma is zero, as
// attempting to calculate it would cause division by zero (see
// below), so most applications simply substitute a Hue of zero.
// Saturation will always be zero in this case, see below for details.
if ($chroma == 0)
return array(0, 0, $computedV);
// Saturation is also simple to compute, and is simply the chroma
// over the Value (or Brightness)
// Again, multiplied by 100 to get a percentage.
$computedS = 100 * ($chroma / $maxRGB);
// Calculate Hue component
// Hue is calculated on the "chromacity plane", which is represented
// as a 2D hexagon, divided into six 60-degree sectors. We calculate
// the bisecting angle as a value 0 <= x < 6, that represents which
// portion of which sector the line falls on.
if ($R == $minRGB)
$h = 3 - (($G - $B) / $chroma);
elseif ($B == $minRGB)
$h = 1 - (($R - $G) / $chroma);
else // $G == $minRGB
$h = 5 - (($B - $R) / $chroma);
// After we have the sector position, we multiply it by the size of
// each sector's arc (60 degrees) to obtain the angle in degrees.
$computedH = 60 * $h;
return array($computedH, $computedS, $computedV);
}
?>
<?php
function RGB_TO_HSV ($R, $G, $B) // RGB Values:Number 0-255
{ // HSV Results:Number 0-1
$HSL = array();
$var_R = ($R / 255);
$var_G = ($G / 255);
$var_B = ($B / 255);
$var_Min = min($var_R, $var_G, $var_B);
$var_Max = max($var_R, $var_G, $var_B);
$del_Max = $var_Max - $var_Min;
$V = $var_Max;
if ($del_Max == 0)
{
$H = 0;
$S = 0;
}
else
{
$S = $del_Max / $var_Max;
$del_R = ( ( ( $var_Max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
$del_G = ( ( ( $var_Max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
$del_B = ( ( ( $var_Max - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
if ($var_R == $var_Max) $H = $del_B - $del_G;
else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B;
else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R;
if ($H<0) $H++;
if ($H>1) $H--;
}
$HSL['H'] = $H;
$HSL['S'] = $S;
$HSL['V'] = $V;
return $HSL;
}
Thoroughly tested and compressed, this is the function I'm going to stick with for converting RGB to HSV:
function RGBtoHSV($r,$g,$b) {
$r=($r/255); $g=($g/255); $b=($b/255);
$maxRGB=max($r,$g,$b); $minRGB=min($r,$g,$b); $chroma=$maxRGB-$minRGB;
if($chroma==0) return array('h'=>0,'s'=>0,'v'=>$maxRGB);
if($r==$minRGB)$h=3-(($g-$b)/$chroma);
elseif($b==$minRGB)$h=1-(($r-$g)/$chroma); else $h=5-(($b-$r)/$chroma);
return array('h'=>60*$h,'s'=>$chroma/$maxRGB,'v'=>$maxRGB);
}
Example:
Example using color "DarkSalmon":
echo '<pre><code>'. print_r( RGBtoHSV(233,150,122), true ) .'</code></pre>';
...returns:
Array
(
[h] => 15.135135135135
[s] => 0.47639484978541
[v] => 0.91372549019608
)
I did it like this
function convertRgbToHsv($rgb)
{
$r = (int)substr($rgb, 0, 3) / 255;
$g = (int)substr($rgb, 3, 3) / 255;
$b = (int)substr($rgb, 6, 3) / 255;
$max = max($r, $g, $b);
$min = min($r, $g, $b);
$delta = $max - $min;
if (!$delta) {
$h = 0;
} else if ($r === $max) {
$h = 60 * ((($g - $b) / $delta) % 6);
} else if ($g === $max) {
$h = 60 * ((($b - $r) / $delta) + 2);
} else {
$h = 60 * ((($r - $g) / $delta) + 4);
}
$s = !!$max ? $delta / $max : 0;
$v = $max;
$hsv = array("h" => $h, "s" => $s, "v" => $v);
return $hsv;
}
Link to reference material here
Here's my spin on it, along with a unit test. Since the S and V values are percentages, this code returns them as integers (0, 100) as opposed to (0, 1) - Example, 75 instead of 0.75.
final class MathService
{
/**
* Converts an RGB point into HSV
*
* #param int $r
* #param int $g
* #param int $b
* #return array
*/
public function rgbToHsv(int $r, int $g, int $b): array
{
$rPrime = $r / 255;
$gPrime = $g / 255;
$bPrime = $b / 255;
$max = max([$rPrime, $gPrime, $bPrime]);
$min = min([$rPrime, $gPrime, $bPrime]);
$delta = $max - $min;
// Calculate H
if ($delta == 0) {
$h = 0;
} else {
if ($max === $rPrime) {
$h = 60 * ((($gPrime - $bPrime) / $delta) % 6);
}
if ($max === $gPrime) {
$h = 60 * ((($bPrime - $rPrime) / $delta) + 2);
}
if ($max === $bPrime) {
$h = 60 * ((($rPrime - $gPrime) / $delta) + 4);
}
}
// Calculate S
if ($max == 0) {
$s = 0;
} else {
$s = $delta / $max;
}
// Calculate V
$v = $max;
return [$h, (int)($s * 100), (int)($v * 100)];
}
}
PHPUnit test case with PHP 7.2
/**
* #test
*/
public function rgbToHsv_ComputesCorrectValues(): void
{
$service = new MathService();
$samples = [
// [R, G, B, H, S, V]
[0, 0, 0, 0, 0, 0],
[255, 255, 255, 0, 0, 100],
[255, 0, 0, 0, 100, 100],
[0, 255, 0, 120, 100, 100],
[0, 0, 255, 240, 100, 100],
[255, 255, 0, 60, 100, 100],
[0, 255, 255, 180, 100, 100],
[255, 0, 255, 300, 100, 100],
[192, 192, 192, 0, 0, 75],
[128, 128, 128, 0, 0, 50],
[128, 0, 0, 0, 100, 50],
[128, 128, 0, 60, 100, 50],
[0, 128, 0, 120, 100, 50],
[128, 0, 128, 300, 100, 50],
[0, 128, 128, 180, 100, 50],
[0, 0, 128, 240, 100, 50],
];
foreach ($samples as $sample) {
list($r, $g, $b) = array_slice($sample, 0, 3);
$expected = array_slice($sample, 3);
$hsv = $service->rgbToHsv($r, $g, $b);
list($h, $s, $v) = $hsv;
self::assertEquals($expected, $hsv, "Error converting ({$r}, ${g}, ${b}). Got ({$h}, {$s}, {$v})");
}
}
I am working on a dynamic store project and I use a loop to print all color options for a product as color boxes, however I really need to add a "border" to these colors which are light. I tried something like the following but It is very limited, it is actually limited to white color only, it won't catch something like #ddd, #eea... etc
Here is my loop:
foreach($colors as $color) {
$color = trim($color);
if (!empty($color)) {
if (in_array($color, array('white','White','#fff','#FFF','#FFFFFF','#ffffff'))) {
$bordercolor = '#bbb';
} else {
$bordercolor = $color;
}
}
}
Colors is an array from backend like: White, #000, #cc0000, etc. It is not practical to add all exceptions in the if/else condition too, any quick idea?
Transform HTML colour to RGB, then to Hue-Saturation-Lightnes (HSV)
<?php
function HTMLToRGB($htmlCode)
{
if($htmlCode[0] == '#')
$htmlCode = substr($htmlCode, 1);
if (strlen($htmlCode) == 3)
{
$htmlCode = $htmlCode[0] . $htmlCode[0] . $htmlCode[1] . $htmlCode[1] . $htmlCode[2] . $htmlCode[2];
}
$r = hexdec($htmlCode[0] . $htmlCode[1]);
$g = hexdec($htmlCode[2] . $htmlCode[3]);
$b = hexdec($htmlCode[4] . $htmlCode[5]);
return $b + ($g << 0x8) + ($r << 0x10);
}
function RGBToHSL($RGB) {
$r = 0xFF & ($RGB >> 0x10);
$g = 0xFF & ($RGB >> 0x8);
$b = 0xFF & $RGB;
$r = ((float)$r) / 255.0;
$g = ((float)$g) / 255.0;
$b = ((float)$b) / 255.0;
$maxC = max($r, $g, $b);
$minC = min($r, $g, $b);
$l = ($maxC + $minC) / 2.0;
if($maxC == $minC)
{
$s = 0;
$h = 0;
}
else
{
if($l < .5)
{
$s = ($maxC - $minC) / ($maxC + $minC);
}
else
{
$s = ($maxC - $minC) / (2.0 - $maxC - $minC);
}
if($r == $maxC)
$h = ($g - $b) / ($maxC - $minC);
if($g == $maxC)
$h = 2.0 + ($b - $r) / ($maxC - $minC);
if($b == $maxC)
$h = 4.0 + ($r - $g) / ($maxC - $minC);
$h = $h / 6.0;
}
$h = (int)round(255.0 * $h);
$s = (int)round(255.0 * $s);
$l = (int)round(255.0 * $l);
return (object) Array('hue' => $h, 'saturation' => $s, 'lightness' => $l);
}
$colour = '#F12346';
$rgb = HTMLToRGB($colour);
$hsl = RGBToHSL($rgb);
var_dump($hsl);
Usage:
$colour = '#F12346';
$rgb = HTMLToRGB($colour);
$hsl = RGBToHSL($rgb);
if($hsl->lightness > 200) {
// this is light colour!
}
Source:
http://www.caperna.org/computing/repository/hsl-rgb-color-conversion-php
Demo:
http://codepad.org/X7KV4n4n
What I'd do in this situation is detect the lightness of the color using HSL, and compare that against a certain percentage. For example, the lightness attribute in the HSL algorithm takes the chroma (M - m where M is the largest RGB value and m is the smallest RGB value) and divides that by 2.
function lightness($R = 255, $G = 255, $B = 255) {
return (max($R, $G, $B) + min($R, $G, $B)) / 510.0; // HSL algorithm
}
The above function would return a percentage of how light the color you've selected is (simple hex -> rgb conversions are required for this also, but that should be pretty easy). The reason I divided by 510 instead of 2 is because in order to get the percentage after dividing by 2, you divide by 255. To make it faster you can simply say: (x / 2) / 255 = x / 510. Then I'd compare the value returned by the above function to, say, 80%.
$r = hexdec($hex[0].$hex[1]);
$g = hexdec($hex[2].$hex[3]);
$b = hexdec($hex[4].$hex[5]);
if(lightness($r, $g, $b) >= .8) {
// add border
} else {
// no border
}
In addition to other formulas given by other answers, you may want to consider Luma.
function luma($r, $g, $b)
{
return (0.2126 * $r + 0.7152 * $g + 0.0722 * $b) / 255;
}
$l = luma(0, 15, 255);
Values closer to 0 will be darker. Values closer to 1 will be lighter.
Short way if you have RGB color as hexadecimal string:
$hexRGB = "4488BB";
if(hexdec(substr($hexRGB,0,2))+hexdec(substr($hexRGB,2,2))+hexdec(substr($hexRGB,4,2))> 381){
//bright color
}else{
//dark color
}
Note: The thereshold 381 is the sum of the values at average level. If you want the thereshold to be lighter or darker, upper or lower 381 in the range 1 - 765.
There also is a simpler way with even less code:
<?php
//Functions
function getRGB($colorCode) {
//Turn html color code into RGB
$var_R = substr($colorCode, 0, 2);
$var_G = substr($colorCode, 2, 2);
$var_B = substr($colorCode, 4, 2);
//Get Hex values
$val_R = hexdec($var_R);
$val_G = hexdec($var_G);
$val_B = hexdec($var_B);
//Red is seen as light too, gets fixed with this
$remRed = hexdec('99');
if ($val_R > $remRed) {
$RGB = $val_G.' '.$val_B;
} else {
$RGB = $val_R.' '.$val_G.' '.$val_B;
}
return $RGB;
}
function getHSL($R = 255, $G = 255, $B = 255) {
$hsl = (max($R, $G, $B) + min($R, $G, $B)) / 510.0;
return $hsl;
}
?>
Now the calling:
$color = 0000FF; //Blue
$RGBcode = getRGB($color); //Returns 0 0 255
$RGBcode = str_replace(' ', ', ', $RGBcode); //Replaces an empty space with a ,
$val_HSL = getHSL($RGBcode); //Returns value from 0.5 to 1
if ($val_HSL >= 0.8) {
//Reject color
} else {
//Accept Color
$color = '#'.$color; //Sets it to html: #0000FF
}
Here’s a short derived version from #Luca C.’s answer for colors in HEX format (e.g. #FFFFFF or #FFF)
<?php
function isDark($hex){
$average = 381; // range 1 - 765
if(strlen(trim($hex)) == 4){
$hex = "#" . substr($hex,1,1) . substr($hex,1,1) . substr($hex,2,1) . substr($hex,2,1) . substr($hex,3,1) . substr($hex,3,1);
}
return ((hexdec(substr($hex,1,2))+hexdec(substr($hex,3,2))+hexdec(substr($hex,5,2)) < $average) ? true : false);
}
var_dump( isDark("#000000") ); // bool(true)
?>
In PHP, what is the most straightforward way to convert a RGB triplet to HSV values?
Here is a simple, straightforward method that returns HSV values as degrees and percentages, which is what Photoshop's color picker uses.
Note that the return values are not rounded, you can do that yourself if required. Keep in mind that H(360) == H(0), so H values of 359.5 and greater should round to 0
Heavily documented for learning purposes.
/**
* Licensed under the terms of the BSD License.
* (Basically, this means you can do whatever you like with it,
* but if you just copy and paste my code into your app, you
* should give me a shout-out/credit :)
*/
<?php
function RGBtoHSV($R, $G, $B) // RGB values: 0-255, 0-255, 0-255
{ // HSV values: 0-360, 0-100, 0-100
// Convert the RGB byte-values to percentages
$R = ($R / 255);
$G = ($G / 255);
$B = ($B / 255);
// Calculate a few basic values, the maximum value of R,G,B, the
// minimum value, and the difference of the two (chroma).
$maxRGB = max($R, $G, $B);
$minRGB = min($R, $G, $B);
$chroma = $maxRGB - $minRGB;
// Value (also called Brightness) is the easiest component to calculate,
// and is simply the highest value among the R,G,B components.
// We multiply by 100 to turn the decimal into a readable percent value.
$computedV = 100 * $maxRGB;
// Special case if hueless (equal parts RGB make black, white, or grays)
// Note that Hue is technically undefined when chroma is zero, as
// attempting to calculate it would cause division by zero (see
// below), so most applications simply substitute a Hue of zero.
// Saturation will always be zero in this case, see below for details.
if ($chroma == 0)
return array(0, 0, $computedV);
// Saturation is also simple to compute, and is simply the chroma
// over the Value (or Brightness)
// Again, multiplied by 100 to get a percentage.
$computedS = 100 * ($chroma / $maxRGB);
// Calculate Hue component
// Hue is calculated on the "chromacity plane", which is represented
// as a 2D hexagon, divided into six 60-degree sectors. We calculate
// the bisecting angle as a value 0 <= x < 6, that represents which
// portion of which sector the line falls on.
if ($R == $minRGB)
$h = 3 - (($G - $B) / $chroma);
elseif ($B == $minRGB)
$h = 1 - (($R - $G) / $chroma);
else // $G == $minRGB
$h = 5 - (($B - $R) / $chroma);
// After we have the sector position, we multiply it by the size of
// each sector's arc (60 degrees) to obtain the angle in degrees.
$computedH = 60 * $h;
return array($computedH, $computedS, $computedV);
}
?>
<?php
function RGB_TO_HSV ($R, $G, $B) // RGB Values:Number 0-255
{ // HSV Results:Number 0-1
$HSL = array();
$var_R = ($R / 255);
$var_G = ($G / 255);
$var_B = ($B / 255);
$var_Min = min($var_R, $var_G, $var_B);
$var_Max = max($var_R, $var_G, $var_B);
$del_Max = $var_Max - $var_Min;
$V = $var_Max;
if ($del_Max == 0)
{
$H = 0;
$S = 0;
}
else
{
$S = $del_Max / $var_Max;
$del_R = ( ( ( $var_Max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
$del_G = ( ( ( $var_Max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
$del_B = ( ( ( $var_Max - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
if ($var_R == $var_Max) $H = $del_B - $del_G;
else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B;
else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R;
if ($H<0) $H++;
if ($H>1) $H--;
}
$HSL['H'] = $H;
$HSL['S'] = $S;
$HSL['V'] = $V;
return $HSL;
}
Thoroughly tested and compressed, this is the function I'm going to stick with for converting RGB to HSV:
function RGBtoHSV($r,$g,$b) {
$r=($r/255); $g=($g/255); $b=($b/255);
$maxRGB=max($r,$g,$b); $minRGB=min($r,$g,$b); $chroma=$maxRGB-$minRGB;
if($chroma==0) return array('h'=>0,'s'=>0,'v'=>$maxRGB);
if($r==$minRGB)$h=3-(($g-$b)/$chroma);
elseif($b==$minRGB)$h=1-(($r-$g)/$chroma); else $h=5-(($b-$r)/$chroma);
return array('h'=>60*$h,'s'=>$chroma/$maxRGB,'v'=>$maxRGB);
}
Example:
Example using color "DarkSalmon":
echo '<pre><code>'. print_r( RGBtoHSV(233,150,122), true ) .'</code></pre>';
...returns:
Array
(
[h] => 15.135135135135
[s] => 0.47639484978541
[v] => 0.91372549019608
)
I did it like this
function convertRgbToHsv($rgb)
{
$r = (int)substr($rgb, 0, 3) / 255;
$g = (int)substr($rgb, 3, 3) / 255;
$b = (int)substr($rgb, 6, 3) / 255;
$max = max($r, $g, $b);
$min = min($r, $g, $b);
$delta = $max - $min;
if (!$delta) {
$h = 0;
} else if ($r === $max) {
$h = 60 * ((($g - $b) / $delta) % 6);
} else if ($g === $max) {
$h = 60 * ((($b - $r) / $delta) + 2);
} else {
$h = 60 * ((($r - $g) / $delta) + 4);
}
$s = !!$max ? $delta / $max : 0;
$v = $max;
$hsv = array("h" => $h, "s" => $s, "v" => $v);
return $hsv;
}
Link to reference material here
Here's my spin on it, along with a unit test. Since the S and V values are percentages, this code returns them as integers (0, 100) as opposed to (0, 1) - Example, 75 instead of 0.75.
final class MathService
{
/**
* Converts an RGB point into HSV
*
* #param int $r
* #param int $g
* #param int $b
* #return array
*/
public function rgbToHsv(int $r, int $g, int $b): array
{
$rPrime = $r / 255;
$gPrime = $g / 255;
$bPrime = $b / 255;
$max = max([$rPrime, $gPrime, $bPrime]);
$min = min([$rPrime, $gPrime, $bPrime]);
$delta = $max - $min;
// Calculate H
if ($delta == 0) {
$h = 0;
} else {
if ($max === $rPrime) {
$h = 60 * ((($gPrime - $bPrime) / $delta) % 6);
}
if ($max === $gPrime) {
$h = 60 * ((($bPrime - $rPrime) / $delta) + 2);
}
if ($max === $bPrime) {
$h = 60 * ((($rPrime - $gPrime) / $delta) + 4);
}
}
// Calculate S
if ($max == 0) {
$s = 0;
} else {
$s = $delta / $max;
}
// Calculate V
$v = $max;
return [$h, (int)($s * 100), (int)($v * 100)];
}
}
PHPUnit test case with PHP 7.2
/**
* #test
*/
public function rgbToHsv_ComputesCorrectValues(): void
{
$service = new MathService();
$samples = [
// [R, G, B, H, S, V]
[0, 0, 0, 0, 0, 0],
[255, 255, 255, 0, 0, 100],
[255, 0, 0, 0, 100, 100],
[0, 255, 0, 120, 100, 100],
[0, 0, 255, 240, 100, 100],
[255, 255, 0, 60, 100, 100],
[0, 255, 255, 180, 100, 100],
[255, 0, 255, 300, 100, 100],
[192, 192, 192, 0, 0, 75],
[128, 128, 128, 0, 0, 50],
[128, 0, 0, 0, 100, 50],
[128, 128, 0, 60, 100, 50],
[0, 128, 0, 120, 100, 50],
[128, 0, 128, 300, 100, 50],
[0, 128, 128, 180, 100, 50],
[0, 0, 128, 240, 100, 50],
];
foreach ($samples as $sample) {
list($r, $g, $b) = array_slice($sample, 0, 3);
$expected = array_slice($sample, 3);
$hsv = $service->rgbToHsv($r, $g, $b);
list($h, $s, $v) = $hsv;
self::assertEquals($expected, $hsv, "Error converting ({$r}, ${g}, ${b}). Got ({$h}, {$s}, {$v})");
}
}