БлогNot. Пишем простой интерпретатор-"рисовалку" на PHP

Пишем простой интерпретатор-"рисовалку" на PHP

Наверное, для решения такой задачи достаточно придумать несколько команд и формат, в котором мы их будем вводить или вставлять из буфера в обычное многострочное поле <textarea>. Поле будет называться text, а кнопка отправки action, код HTML-формы сделаем после написания скрипта.

Минимум нужных функций уже есть в библиотеке GD, которая обычно подключена на хостинге. Нам остаётся разобрать полученную от пользователя программу и выполнить её. Конечно, основное внимание в коде следует уделить обработке данных, полученных методом POST из формы. Для простоты примем, что одна команда располагается на одной строке, а всё, что скрипт не сможет интерпретировать, он будет просто пропускать, не придираясь к неправильному формату. Поддерживаемые команды перечислены на странице скрипта, а вот его полный листинг:

<?php

function trimall ($string) {
 $string=preg_replace("/\r/","",trim($string));
 $string=preg_replace("/  +/"," ",$string);
 $string=preg_replace("/ \n/","\n",$string);
 $string=preg_replace("/\n\n+/","\n",$string);
 return $string;
}

function good_numeric ($vals,$defaultvals,$minvals,$maxvals) {
 //параметры, значения по умолчанию, минимальные допустимые значения, максимальные допустимые значения
 $n = count($defaultvals);
 $res = array ();
 for ($i=0; $i<$n; $i++) {
  if (!isset($vals[$i]) or !is_numeric($vals[$i])) $res[$i]=$defaultvals[$i];
  else if ($vals[$i]<$minvals[$i]) $res[$i] = $minvals[$i];
  else if ($vals[$i]>$maxvals[$i]) $res[$i] = $maxvals[$i];
  else $res[$i] = $vals[$i];
 }
 return $res;
}

//error_reporting(0);
if (!(isset($_POST['text']) and isset($_POST['action']))) die ("No data!");
if (!function_exists('imagecreate')) die ("Need GD library!");

$text = trimall(htmlspecialchars($_POST['text'])); //данные формы
$img=''; //картинка
$imgX = 0; $imgY = 0; //размеры
$col=''; //текущий цвет
$cX = 0; $cY = 0; //текущая точка
$font = 1; //шрифт, встроенные = 1..5

$strings = explode("\n", $text); //разделим текст на строки
if (is_array($strings)) $strings = array_diff($strings, array('',null,'\n')); //удалим пустые строки
$num = 0;
foreach ($strings as $command) {
 $num++;
 if ($command == 'size') $command = 'size 800,600';
 if (strpos($command,' ')===false) continue;
 list($name, $paramstr) = explode(' ', $command, 2); //извлекли имя команды и параметры
 $name = strtolower($name);
 $params = explode(",", $paramstr); //распарсили параметры по запятой
 if (is_array($params)) $params = array_diff($params, array('',' ',null)); //удалим пустые значения среди параметров
 switch ($name) { //выбор и выполнение команды
  case 'size':
   if (!empty($img)) break;
   list ($w,$h) = good_numeric($params,array(800,600),array(10,10),array(1000,1000));
   $img = imageCreate($w,$h) or die ("Can't create image!");
   $imgX = imagesx($img);
   $imgY = imagesy($img);
   imagefilledrectangle($img,0,0,$imgX,$imgY,imageColorAllocate($img,255,255,255));
   $col = imagecolorallocate($img,0,0,0);
  break;
  case 'setcolor':
   if (empty($img)) break;
   list ($red,$green,$blue) = good_numeric($params,array(255,255,255),array(0,0,0),array(255,255,255));
   $col = imageColorAllocate($img,$red,$green,$blue);
  break;
  case 'line':
  case 'rect':
  case 'fillrect':
   if (empty($img)) break;
   list ($x1,$y1,$x2,$y2) = good_numeric($params,
    array(0,0,$imgX-1,$imgY-1), array(0,0,0,0), array($imgX-1,$imgY-1,$imgX-1,$imgY-1));
   if ($name=='line') imageline($img, $x1, $y1, $x2, $y2, $col); 
   else if ($name=='rect') imagerectangle($img, $x1, $y1, $x2, $y2, $col);
   else imagefilledrectangle($img, $x1, $y1, $x2, $y2, $col);
  break;
  case 'thickness':
   if (empty($img)) break;
   $w = good_numeric($params,array(1),array(1),array(round(min($imgX,$imgY)/2)));
   imagesetthickness($img, $w[0]);
  break;
  case 'circle':
  case 'fillcircle':
   if (empty($img)) break;
   list ($x,$y,$r) = good_numeric($params,
    array(round($imgX/2),round($imgY/2),round(min($imgX,$imgY)/2)), array(0,0,0),
    array($imgX-1,$imgY-1,round(min($imgX,$imgY)/2)));
   if ($name=='circle') imagearc($img, $x, $y, $r*2, $r*2, 0, 360, $col);
   else imagefilledellipse ($img, $x, $y, $r*2, $r*2, $col);
  break;
  case 'point':
   if (empty($img)) break;
   list ($x,$y) = good_numeric($params, array(round($imgX/2),round($imgY/2)), array(0,0), array($imgX-1,$imgY-1));
   $cX = $x; $cY = $y;
   imagesetpixel ($img,$x,$y,$col);
  case 'font':
   $fnt = good_numeric($params,array(1),array(1),array(5)); 
   $font = $fnt[0];
  break;
  case 'text':
   if (empty($img)) break;
   imagestring ($img,$font, $cX, $cY, $paramstr, $col);
  break;
 }
}

header("Content-Disposition: inline; filename=draw.png");
header("Content-type: image/png"); //Отдать браузеру картинку
imagepng($img);
imagedestroy($img);

?>

Первый заголовок header нужен для того, чтобы у файла сразу были имя и тип при выборе из браузера команды сохранения картинки. Иначе будет предлагать сохранить как файл типа .php

Так как вывод текста выполняется функцией imagestring, не-латиница выводиться не должна.

 Этот скрипт в работе

Пример программы: генерируем картинку с российским флагом
size 450,300
setcolor 255,255,255
fillrect 0,0,449,99
setcolor 0,0,255
fillrect 0,100,449,199
setcolor 255,0,0
fillrect 0,200,449,299
setcolor 204,204,204
rect 0,0,449,299
Пример программы: рисуем нечто, пользуясь умолчаниями скрипта (размерами картинки 800x600)
size 
fillrect 0,0,799,599
setcolor 255,0,0
fillcircle 400,300,200
setcolor 0,0,255
thickness 3
line 0,0,800,600
line 0,599,799,0
point 350,70
setcolor 255,255,255
font 3
text Hello, world!
Файл .html с формой, вызывающий показанный скрипт под именем draw.php, может быть, например, таким
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=Windows-1251">
<title>Простейший интерпретатор-рисовалка на PHP</title>
</head>
<body>

<form method="post" action="draw.php" target="_blank">
<table align="center" border="0" cellpadding="4" cellspacing="0" width="61%">
 <tr><td align="center">
  Текст программы:
  <br><textarea name="text" rows="20" cols="50" maxlength="10000"></textarea>
 </td></tr><tr><td align="center">
  <input type="submit" name="action" value="Выполнить"> 
 </td></tr><tr><td>
 <p><b>Примеры команд:</b>
 <br><font color="blue">size 800,600</font> создать рисунок 800x600 пикселов - должна быть первой командой 
в программе, повторные size игнорируются;
 <br><font color="blue">setcolor 255,0,0</font> установить красный цвет рисования;
 <br><font color="blue">thickness 5</font> установить толщину линии 5 пикселов;
 <br><font color="blue">line 0,0,100,50</font> линия из верхнего левого угла рисунка в точку с координатами 
(100,50) - не забываем, что координата по оси Y считается сверху вниз и обе координаты отсчитываются с нуля;
 <br><font color="blue">rect 100,100,600,200</font> прямоугольник с указанными координатами левого верхнего 
угла (100,100) и правого нижнего (600,200);
 <br><font color="blue">fillrect 300,250,500,450</font> закрашенный прямоугольник с указанными координатами 
левого верхнего угла (300,250) и правого нижнего (500,450);
 <br><font color="blue">circle 250,250,300</font> окружность с центром в точке (250,250) радиусом 300 пикселов;
 <br><font color="blue">fillcircle 250,250,300</font> то же, но окружность будет закрашена;
 <br><font color="blue">point 350,70</font> ставим точку по указанным координатам;
 <br><font color="blue">font 3</font> указываем номер шрифта (только встроенные латинские, номера от 1 до 5 включительно);
 <br><font color="blue">text Hello, world!</font> выводим строку текста по координатам последней точки.
 </p>
 <p><b>Примечания:</b>
 <br>одна команда пишется на одной строке;
 <br>всё неинтерпретированное должно просто проигнорироваться.
 </p>
</td></tr></table>
</form>

</body></html>

02.12.2015, 17:39 [6706 просмотров]


теги: графика php

К этой статье пока нет комментариев, Ваш будет первым