Пишем простой интерпретатор-"рисовалку" на 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 [6811 просмотров]