» Главная
eXcode.ru » Статьи » PHP
» Новости
» Опросы
» Файлы
» Журнал



Пользователей: 0
Гостей: 10





Обработка изображений средствами PHP




  1. Введение [http://www.php5.ru#intro]
  2. Сборка [http://www.php5.ru#build]
  3. Генерация изображения [http://www.php5.ru#paint]
  4. Вывод текста, а также диаграммы и коллекционеры марок [http://www.php5.ru#text]
  5. Изменение размера: thumbnails, или "превьюшки" [http://www.php5.ru#size]

Введение

PHP приобрел всемирную популярность не только как удобное средство вывода HTML и обработки форм, но и, во многом, благодаря набору расширений, позволяющим выполнять всевозможные стоящие перед веб-разработчиком задачи. Одним из таких расширений является библиотека GD, предназначенная для работы с растровыми изображениями.

В этой статье мы рассмотрим работу со второй версией библиотеки GD.

Сборка

Для успешного выполнения примеров из этой статьи необходимо, чтобы PHP был собран с поддержкой второй версии библиотеки GD.

Для того, чтобы узнать наличие и используемую версию графической библиотеки, воспользуемся всеми нами, без сомнения, любимой функцией phpinfo() [http://www.php.net/phpinfo], и взглянем на раздел "GD":

gd

GD Support enabled
GD Version bundled (2.0.15 compatible)
FreeType Support enabled
FreeType Linkage with freetype
GIF Read Support enabled
JPG Support enabled
PNG Support enabled
WBMP Support enabled
XBM Support enabled
JIS-mapped Japanese Font Support enabled

Если ничего похожего вы не нашли - значит, PHP собран без поддержки графической библиотеки, и придется его пересобирать (или попросить об этом админа).

В противном случае, сначала взглянем на строку "GD Version". Если версия GD - первая (например, 1.6), то часть материала этой статьи вам не подойдет (и, в любом случае, рекомендую обновиться до второй версии). Вторая же версия тоже может быть разная. :) Обратите внимание на слово "bundled". Его присуствие означает, что PHP собран с библиотекой GD, поставляемой вместе с PHP: именно эту библиотеку рекомендуется использовать, так как разработчики PHP исправили множество ошибок и недочетов, присущих оригинальной GD. Впрочем, если используется внешняя библиотека (слово "bundled" в строке "GD Version" отсутствует) - ничего страшного, все приведенные ниже примеры будут работать.

В нижеследующих строках мы видим, какие графические форматы поддерживаются данной конфигурацией библиотеки GD. В частности, работа с форматом GIF поддерживается в режиме "только чтение" из-за проблем с лицензированием используемого в GIF алгоритма компрессии.

Итак, если требуется пересборка PHP, следует это и сделать, загрузив при необходимости последнюю версию PHP, и указать при сборке параметр:

--with-gd[=location]

Необязательный параметр location указывает путь к внешней библиотеке GD. Если его опустить (в смысле, не указывать) - используется bundled-версия.

Для поддержки работы с шрифтами TrueType, обсуждаемой в главе "Вывод текста..." [http://www.php5.ru#text], также понадобится наличие библиотеки FreeType [http://www.freetype.org] и следующие параметры конфигурационной строки:

--enable-gd-native-ttf
--with-freetype-dir=/путь/к/библиотеке/FreeType

Примечание: Для сборки GD с поддержкой GIF Write под FreeBSD, необходимо предварительно установить переменную среды WITH_LZW:
export WITH_LZW=yes

Примечание 2:Выше подразумевалось, что используется Unix-подобная ОС. Если же PHP установлен локально, да еще и под Windows - то надо просто раскомментить одну строчку в php.ini - надеюсь, догадаетесь, какую. :)

Генерация изображения с помощью PHP

Ну что ж, со сборкой PHP мы разобрались, и теперь нам не терпится нарисовать и вывести в броузер какую-нибудь картинку с помощью PHP. Надеюсь, вы прекрасно осознаете, что нельзя вот так вот просто взять и вывести картинку посреди HTML-кода в том же самом скрипте (если не осознаете, прочтите внимательно эту статью [http://faq.phpclub.net/na_tanke]): в HTML-документе мы разместим, как и обычно, тэг <img>, а в его атрибуте src укажем не картинку, как обычно, а PHP-скрипт:

<img src="/i/articles/image/image.php">

Теперь приступим к написанию этого самого image.php.

Заголовок. Он же header.

Прежде всего, как броузер узнает, что image.php - это не HTML-документ и не что-то еще, а картинка?

Тип документа броузер определяет по заголовку Content-type. На самом деле, этот заголовок - обязательный и всегда присуствует; по умолчанию, PHP услужливо "отдает" заголовок Content-type: text/html. Обычно это поведение PHP нам прекрасно подходит - но не в данном случае. Придется нам вывести нужный заголовок самим:

<?php
  header(Content-type: image/png); // устанавливаем тип документа - "изображение в формате PNG".
?>

Приступим теперь непосредственно к генерации картинки.

Создание изображения

Для создания изображения, в нашем распоряжении две функции:

1. imagecreate() [http://www.php.net/imagecreate]. С помощью этой функции можно создать изображение на основе палитры, содержащей фиксированный набор цветов. Каждый цвет палитры необходимо описать с помощью функции imagecolorallocate() [http://www.php.net/imagecolorallocate]. Этот способ создания изображения был единственным при работе с первой версией GD, и необходим при работе с ориентированными на палитру форматами, такими как GIF. Однако введенный во второй версии (и отныне рекомендуемый разработчиками) способ, на мой взгляд, гораздо более удобен.

2. imagecreatetruecolor() [http://www.php.net/imagecreatetruecolor]. Эта функция создает TrueColor-изображение, то есть цвет каждой точки определяется произвольным цветом, задаваемым в координатах RGB. Помимо того, что это удобнее, чем работа с палитрой, такой подход позволяет производить масштабирование изображения с гораздо меньшими потерями качества. Но об этом позже. Сейчас просто создадим изображение:

<?php
  $image = imagecreatetruecolor(80,60) // создаем изображение...
    or die(Cannot create image);     // ...или прерываем работу скрипта в случае ошибки

  imagedestroy($image);                // освобождаем память, выделенную для изображения
?>

Функция imagecreatetruecolor (как, кстати, и функция imagecreate), принимает два обязательных целочисленных параметра - ширину (в нашем примере - 80 пикселей) и высоту (60 пикселей) картинки, и возвращает идентификатор ресурса (в данном случае - изображения), который мы присваиваем переменной $image, которой в дальнейшем будем постоянно пользоваться.

Если по какой-либо страшной причине (недостатке памяти, например) создать изображение не получается, функция возвращает false - в этом случае мы прерываем работу скрипта.

Хотя мы с этим изображением, пока что, ничего не делаем, мы обязаны освободить выделенную для него память с помощью функции imagedestroy() [http://www.php.net/imagedestroy]: к сожалению, автоматическое освобождение памяти происходит не всегда, и игнорирование рекомендации всегда использовать imagedestroy может привести к весьма серьезным утечкам памяти.

Немножко порисуем

Итак, с помощью функции imagecreatetruecolor() [http://www.php.net/imagecreatetruecolor] мы создали "труколорное" и, если верить документации, черное изображение размером 80x60. Так что, те, в чьи планы входит создание Web-галереи репродукций "квадрата Малевича", могут смело переходить к следующей главе. :) Для остальных, продолжим. Нарисуем на картинке что-нибудь содержательное.

<?php

  $image = imagecreatetruecolor(80,60) // создаем изображение... 
    or die(Cannot create image);     // ...или прерываем работу скрипта в случае ошибки 


  // "Зальем" фон картинки синим цветом...
  imagefill($image, 0, 0, 0x000080);
  // Нарисуем желтый контурный эллипс...
  imageellipse($image, 40, 30, 50, 50, 0xFFFF00);
  // ...и еще пару, но сплошных...
  imagefilledellipse($image, 30, 20, 10, 10, 0xFFFF00);
  imagefilledellipse($image, 50, 20, 10, 10, 0xFFFF00);
  // ...вертикальную линию...
  imageline($image, 40, 28, 40, 38, 0xFFFF00);
  // ...и дугу.
  imagearc($image, 40, 30, 40, 40, 45, 135, 0xFFFF00);

  // Устанавливаем тип документа - "изображение в формате PNG"...
  header(Content-type: image/png); 
  // ...И, наконец, выведем сгенерированную картинку в формате PNG:
  imagepng($image);

  imagedestroy($image);                // освобождаем память, выделенную для изображения

?>

Палка, палка, огуречик, вот и вышел человечек :)

Используемые для рисования функции весьма просты для понимания. Их описание (как и описание всех GD-функций) вы найдете здесь [http://www.php.net/image]. В случае трудностей с английским, просто "поиграйтесь", меняя значения параметров - как вы, несомненно, уже догадались, это ни что иное, как координаты, и цвета в виде 0xRRGGBB.

Подробнее же мы разберем вот эту строку:
imagepng($image);

Выше мы создали в памяти изображение, и всячески над ним извращались. :) Это мы делали без привязки к какому-либо выходному формату - просто работали с набором байтов в памяти. А вот функции вида imageформат (imagepng() [http://www.php.net/imagepng], imagejpeg() [http://www.php.net/imagejpeg], imagewbmp() [http://www.php.net/imagewbmp]...) генерируют на основе этого самого набора байтов, на который ссылается идентификатор $image, картинку в соответствующем формате, и выводят ее в выходной поток - то бишь, проще говоря, в броузер.

Нелишне здесь вспомнить и о строке header(′Content-type: image/png′), где мы указали тип документа - обратите внимание: здесь png, и там png. :) Справедливости ради, надо заметить, что большинство броузеров воспринимают только часть "image" этого заголовка, а формат самой картинки уже определяют по ее заголовкам, характерным для каждого формата; однако, лучше все же не надеяться на "интеллект" броузера и указывать правильный формат изображения.

Вывод текста, а также диаграммы и коллекционеры марок

Используя полученные в результате рисования смайликов знания, попробуем порисовать диаграммы, а заодно и научимся выводить на картинках текст (не забудьте только о библиотеке FreeType, о необходимости наличия которой сказано в главе "Сборка".

Предположим, вы располагаете следующей важной статистической информацией о коллекционерах почтовых марок:

<?php
  // Назовем этот файл data.php - он нам еще понадобится.

  $Title = Количество марок у моих друзей;
  $Data = array( 
              Коля    => 16,
              Петя    => 14,
              Федя    => 11,
              Маша    => 17,
              Ипполит => 8
             );
?>

...и горите желанием представить эту информацию в виде "столбиков" - для удобного сравнения. Столбики мы рисовать уже умеем - стоит только немножко вспомнить азы арифметики:

<?php 

  define(GRAPH_WIDTH,         400);               // ширина картинки 
  define(GRAPH_HEIGHT,        300);               // высота картинки 
  define(GRAPH_OFFSET_TOP,    40);                // отступ сверху 
  define(GRAPH_OFFSET_LEFT,   40);                // отступ слева 
  define(GRAPH_OFFSET_RIGHT,  5);                 // отстут справа 
  define(GRAPH_OFFSET_BOTTOM, 30);                // отступ снизу 

  $colors = array(0xFF0000,0x00FF00,0x0000FF,     // цвета столбцов 
                  0xFFFF00,0x00FFFF,0xFF00FF); 

  require(data.php); // Вот и понадобился data.php :)

  // Считаем ширину столбцов 
  $col_width = (GRAPH_WIDTH - GRAPH_OFFSET_LEFT - GRAPH_OFFSET_RIGHT) / count($Data); 

  // Считаем высоту столбца, соответствующего максимальному значению 
  $col_maxheight = (GRAPH_HEIGHT - GRAPH_OFFSET_TOP - GRAPH_OFFSET_BOTTOM); 

  // Ищем максимальное значение в массиве, соответствующее столбцу максимальной высоты 
  $max_value = max($Data); 

  $image = imagecreatetruecolor(GRAPH_WIDTH,GRAPH_HEIGHT) // создаем изображение... 
    or die(Cannot create image);     // ...или прерываем работу скрипта в случае ошибки 

  imagefill($image, 0, 0, 0xFFFFFF);  // белый фон 

  // рисуем столбцы 
  $x = GRAPH_OFFSET_LEFT; 
  $y = GRAPH_OFFSET_TOP + $col_maxheight; 
  $i = 0; 
  foreach($Data as $value) { 
    imagefilledrectangle(              // рисуем сплошной прямоугольник
      $image, 
      $x, 
      $y - round($value*$col_maxheight/$max_value), 
      $x + $col_width - 1, 
      $y, 
      $colors[$i++%count($colors)]
    ); 
    $x += $col_width;
  } 

  // рисуем координатную ось 
  imageline($image, GRAPH_OFFSET_LEFT - 5, GRAPH_OFFSET_TOP, 
            GRAPH_OFFSET_LEFT - 5, $y, 0xCCCCCC); 
  for($value=0; $value<=$max_value; $value++) {
    imageline($image, GRAPH_OFFSET_LEFT - 7, $Y = $y - round($value*$col_maxheight/$max_value), 
            GRAPH_OFFSET_LEFT - 5, $Y, 0xCCCCCC); 
    imagestring($image, 1, GRAPH_OFFSET_LEFT / 2, $Y - 4, $value, 0x000000);
  }

  // Устанавливаем тип документа - "изображение в формате PNG"... 
  header(Content-type: image/png); 
  // ...И, наконец, выведем сгенерированную картинку в формате PNG: 
  imagepng($image); 

  imagedestroy($image);                // освобождаем память, выделенную для изображения 

?>

Обратите внимание на строку
imagestring [http://www.php.net/imagestring]($image, 1, GRAPH_OFFSET_LEFT / 2, $Y - 4, $value, 0x000000);
с помощью которой мы выводим числа на координатной оси. Второй параметр - один из встроенных в GD шрифтов (от 1 до 5, чем больше число - тем крупнее шрифт).

Так зачем же, скажете вы, нам какие-то там TrueType-шрифты и FreeType-библиотеки, если мы и так прекрасно пишем на картинке? А вот затем, скажу я вам, что писать-то мы хотим по-русски, а встроенные шрифты о существовании кириллицы даже и не подозревают. А нам надо бы подписать столбики именно по-русски. Да и выбор встроенных шрифтов невелик.

Итак, нам понадобится:

- Функция imagettftext() [http://www.php.net/imagettftext], которая рисует выбранным TrueType-шрифтом на картинке,

- Какой-нибудь кириллический TrueType-шрифт. Возьмем, например, arial.ttf из всеми нами любимой Винды, да не просто возьмем, а положим его туда, где лежит наш скрипт,

- Поскольку функция imagettftext() [http://www.php.net/imagettftext] воспринимает кодировку Unicode, но никак не Windows-1251, то нам пригодится вот такая функция для соответствующего преобразования:

<?php // Разместим этот код в файле win2uni.php...

  // Преобразование Windows 1251 -> Unicode
  function win2uni($s)
  {
    $s = convert_cyr_string($s,w,i); // преобразование win1251 -> iso8859-5
    // преобразование iso8859-5 -> unicode:
    for ($result=′′, $i=0; $i<strlen($s); $i++) {
      $charcode = ord($s[$i]);
      $result .= ($charcode>175)?"&#".(1040+($charcode-176)).";":$s[$i];
    }
    return $result;
  }
?>

- Функция imagettfbbox() [http://www.php.net/imagettfbbox], которая поможет нам вычислить высоту и ширину выводимого шрифтом текста.

Сначала потренируемся:

<?php

  require(win2uni.php);

  define(WIDTH, 200);
  define(HEIGHT, 60);
  define(FONT_NAME, arial.ttf);
  define(FONT_SIZE, 20);

  $image = imagecreatetruecolor(WIDTH,HEIGHT)
    or die(Cannot create image);

  // Не забываем преобразовать текст в кодировку Unicode
  $text = win2uni(Всем привет! :));

  $coord = imagettfbbox(
     FONT_SIZE,  // размер шрифта
     0,          // угол наклона шрифта (0 = не наклоняем)
     FONT_NAME,  // имя шрифта, а если точнее, ttf-файла
     $text       // собственно, текст
     );

  /* Функция imagettfbbox возвращает нам массив из восьми элементов,
     содержащий всевозможные координаты минимального прямоугольника,
     в который можно вписать данный текст. Индексы массива
     удобно обозначить на схеме в виде координат (x,y):

     (6,7)           (4,5)
       +---------------+
       |Всем привет! :)|
       +---------------+
     (0,1)           (2,3)

     Число элементов массива может на первый взгляд показаться избыточным,
     но не следует забывать о возможности вывода текста под произвольным
     углом.

     По этой схеме легко вычислить ширину и высоту текста:
  */
  $width = $coord[2] - $coord[0];
  $height = $coord[1] - $coord[7];

  // Зная ширину и высоту изображения, располагаем текст по центру:

  $X = (WIDTH - $width) / 2;
  $Y = (HEIGHT + $height) / 2;

  imagettftext(
    $image,      // как всегда, идентификатор ресурса
    FONT_SIZE,   // размер шрифта
    0,           // угол наклона шрифта
    $X, $Y,      // координаты (x,y), соответствующие левому нижнему
                 // углу первого символа
    0xFFFFFF,    // цвет шрифта
    FONT_NAME,   // имя ttf-файла
    $text
  );

  header(Content-type: image/png);
  imagepng($image);
  imagedestroy($image);

?>

Тренировка прошла успешно - всех поприветствовали. Можно теперь приступать к нашим диаграммам.

<?php 

  define(GRAPH_WIDTH,         400);               // ширина картинки 
  define(GRAPH_HEIGHT,        300);               // высота картинки 
  define(GRAPH_OFFSET_TOP,    40);                // отступ сверху 
  define(GRAPH_OFFSET_LEFT,   40);                // отступ слева 
  define(GRAPH_OFFSET_RIGHT,  5);                 // отстут справа 
  define(GRAPH_OFFSET_BOTTOM, 30);                // отступ снизу 

  define(FONT_NAME, arial.ttf);                 // Имя шрифта
  define(FONT_SIZE, 12);                          // Размер шрифта

  $colors = array(0xFF0000,0x00FF00,0x0000FF,     // цвета столбцов 
                  0xFFFF00,0x00FFFF,0xFF00FF); 

  require(data.php);
  require(win2uni.php);

  // Считаем ширину столбцов 
  $col_width = (GRAPH_WIDTH - GRAPH_OFFSET_LEFT - GRAPH_OFFSET_RIGHT) / count($Data); 

  // Считаем высоту столбца, соответствующего максимальному значению 
  $col_maxheight = (GRAPH_HEIGHT - GRAPH_OFFSET_TOP - GRAPH_OFFSET_BOTTOM); 

  // Ищем максимальное значение в массиве, соответствующее столбцу максимальной высоты 
  $max_value = max($Data); 

  $image = imagecreatetruecolor(GRAPH_WIDTH,GRAPH_HEIGHT) // создаем изображение... 
    or die(Cannot create image);     // ...или прерываем работу скрипта в случае ошибки 

  imagefill($image, 0, 0, 0xFFFFFF);  // белый фон

  // рисуем столбцы 
  $x = GRAPH_OFFSET_LEFT; 
  $y = GRAPH_OFFSET_TOP + $col_maxheight; 
  $i = 0; 
  foreach($Data as $name => $value) { 
    imagefilledrectangle(              // рисуем сплошной прямоугольник
      $image, 
      $x, 
      $y - round($value*$col_maxheight/$max_value), 
      $x + $col_width - 1, 
      $y, 
      $colors[$i++%count($colors)]
    );

    // Выводим текст:
    // .. преобразование в Unicode...
    $text = win2uni($name);
    // .. расчет координат...
    $coord = imagettfbbox(FONT_SIZE,0,FONT_NAME,$text);
    $text_x = $x + ($col_width - $coord[2] - $coord[0]) / 2;
    $text_y = GRAPH_HEIGHT - 5;
    // .. и вывод текста
    imagettftext($image,FONT_SIZE,0,$text_x,$text_y,0x000000,FONT_NAME,$text);

    $x += $col_width;
  } 

  // Выводим заголовок
  $text = win2uni($Title);
  $coord = imagettfbbox(FONT_SIZE,0,FONT_NAME,$text);
  $text_x = $x + ($col_width - $coord[2] - $coord[0]) / 2;
  $text_y = (GRAPH_OFFSET_TOP - $coord[1] - $coord[7]) / 2; 
  imagettftext($image,FONT_SIZE,0,$text_x,$text_y,0x000000,FONT_NAME,$text);

  // рисуем координатную ось 
  imageline($image, GRAPH_OFFSET_LEFT - 5, GRAPH_OFFSET_TOP, 
            GRAPH_OFFSET_LEFT - 5, $y, 0xCCCCCC); 
  for($value=0; $value<=$max_value; $value++) {
    imageline($image, GRAPH_OFFSET_LEFT - 7, $Y = $y - round($value*$col_maxheight/$max_value), 
            GRAPH_OFFSET_LEFT - 5, $Y, 0xCCCCCC); 
    imagestring($image, 1, GRAPH_OFFSET_LEFT / 2, $Y - 4, $value, 0x000000);
  }

  header(Content-type: image/png); 
  imagepng($image); 
  imagedestroy($image);

?>

Барабанная дробь... Запускаем...

Ура! Получилось! :)

Изменение размера: thumbnails, или "превьюшки"

Часто перед веб-разработчиком стоит задача генерации уменьшенных копий изображений для предварительного просмотра; можно даже сказать, что это - одно из самых распространенных применений библиотеки GD.

В GD1, поддерживающей только изображения на основе палитры, присуствовала лишь функция imagecopyresized() [http://www.php.net/imagecopyresized]; качество уменьшенных изображений, генерируемых этой ей, мягко говоря, оставляло желать лучшего: работая с фиксированной палитрой, ограниченной 255 цветами, весьма затруднительно обеспечить качественный антиалиасинг.

Во второй версии библиотеки, с появлением поддержки TrueColor и imagecreatetruecolor() [http://www.php.net/imagecreatetruecolor], введена новая функция - imagecopyresampled() [http://www.php.net/imagecopyresampled], обеспечивающая весьма достойное качество "превьюшек".

Продемонстрируем работу с этой функцией. Предположим, у нас есть файл original.jpg, допустим, 400x250 пикселей, и мы хотим создать ее уменьшенный вариант small.jpg - 100x60. Можно поступить так:

<?php

  define(SOURCE, original.jpg)К началу статьи





Добавил: PIXELДата публикации: 2007-10-12 14:36:08
Рейтинг статьи:3.00 [Голосов 5]Кол-во просмотров: 11191

Комментарии читателей

Всего комментариев: 6

2017-01-30 12:30:08
lpavelrof
Наш сервис предоставляет настоящие лайки на фото заказчиков, которые готовы платить за качество.

Именно для этого мы и набираем удалённых сотрудников, которые будут выполнять работу, то есть ставить лайки и получить за это деньги.

Чтобы стать нашим удалённым сотрудником и начать ставить лайки, зарабатывая при этом 45 рублей за 1 поставленный лайк,

Вам достаточно просто зарегистрироваться на нашем сервисе. > www.like.zarplatt.ru <

Вывод заработанных средств ежедневно в течении нескольких минут.

2017-01-23 22:33:56
pegorrof
Увлекательная работа или подработка не выходя из дома.
Мы предлагаем: Достойная зарплата без задержек, справка 2-НДФЛ, карьерный рост, бесплатное обучение,
свободный график, страховые взносы в Пенсионный фонд, официальный договор (по желанию).
График Вашей работы, Вы устанавливаете сами.
Ваш возраст и образование значения не имеют.
Обязательные условия: наличие компьютера и интернета.
Ваши действия:
1. На сайте ( www.off-rabota.tk ) скачайте и установите приложение.
2. Ознакомьтесь с содержимым.
3. Пройдите несложную регистрацию.
4. Обязательно, пройдите курсы обучения. (Бесплатно)
5. Начинайте зарабатывать!
Зарплату мы перечисляем только на банковскую карту любой платёжной системы.
(Для работы советуем оформить отдельную карту)
ВНИМАНИЕ! Весь процесс регистрации, обучения совершенно бесплатны. Не попадитесь в руки мошенников!

2012-09-15 22:26:35
Bodom
Надо попробовать сделать что то похожее на моем блоге http://vsewmire.ru/

2012-02-12 19:47:37
впывп
врврвры

2011-01-19 10:45:27
Любовь
Спасибо большое! Все стало понятно!

2009-10-25 03:17:08
5RAY STUDIO
Спасибо за статью, пригодилась.
Ваше имя: *
Текст записи: *
Имя:

Пароль:



Регистрация

Как вы относитесь к блогам?
Не знаю что это такое!
17% (13)
ничего особенного
35% (27)
иногда читаю чужие блоги
27% (21)
постоянно читаю блоги
1% (1)
веду свой блог
5% (4)
считаю блоги двигателем интернета
6% (5)
ЖЖ рулит, фсе остальное ф топку!
9% (7)

Проголосовало: 78
Речь сисадмина к девушке, пришедшей регистрироваться на сервере:
- С точки зрения сетевой безопасности вы представляете собой потенциальную дырку.
Рейтинг: 3.4/10 (5)
Посмотреть все анекдоты