Статьи

Создание PDF с помощью PHP

PDF-файлы могут быть лучшим форматом для распространения документов в Интернете. В сегодняшнем уроке я покажу вам, как создавать PDF-файлы с помощью PHP. Давай сделаем это!


Существует ряд методов, которые вы можете использовать для создания PDF-файлов с помощью PHP. Вы можете использовать библиотеку PDFlib, но это довольно дорого, по крайней мере, для коммерческой работы. Сегодня мы будем работать с библиотекой FPDF , которая бесплатна как для личного, так и для коммерческого использования и совсем не сложна в использовании.

Для начала зайдите на сайт FPDF и нажмите « Загрузки» слева. Под всеми руководствами найдите zip-архив версии 1.6 (несмотря на то, что с момента последнего обновления прошло некоторое время, форумы на сайте все еще довольно активны). Распакуйте файлы; все, что вам нужно из этой папки, это файл fpdf.php и каталог шрифтов.

Давайте сделаем «Привет, мир!» пример, чтобы получить нас в скорости. Конечно, первое, что вам нужно сделать, это потребовать библиотеку; Затем мы создаем новый экземпляр класса FPDF.

1
2
3
require(‘fpdf/fpdf.php’);
 
$pdf = new FPDF(‘P’, ‘pt’, ‘Letter’);

Обратите внимание, что мы передали три значения конструктору FPDF; первая — это ориентация нашей страницы; в нашем случае это портрет (вы также можете сделать ‘L’andscape). Следующий параметр — это тип блока, который мы хотим использовать; мы выбираем точки, потому что именно в них будут измеряться шрифты; мы могли бы выбрать мм, см или дюймы. Наконец, у нас есть формат бумаги, который мы, очевидно, установили как букву; мы могли бы установить его Legal, A3, A4 или A5. Все три из этих параметров являются опцией; их значения по умолчанию ('P', 'mm', 'A4') .

Прежде чем мы сможем начать добавлять текст в наш документ, нам нужно сделать две вещи: 1) добавить страницу в pdf и 2) установить шрифт.

1
2
$pdf->AddPage();
$pdf->SetFont(‘Arial’, », 12);

Вы также можете использовать AddPage всякий раз, когда хотите вставить разрыв страницы; сначала мы устанавливаем тип шрифта; это может быть один из встроенных семейств шрифтов (Courier, Helvetica / Arial, Times, Symbol или ZapfDingbats) или тот, который вы добавили с AddFont метода AddFont . Далее идет формат шрифта; мы установили наши как обычные, но мы могли бы сделать «B’old», «I’talic», «U’nderline» или любую их группу («BUI»). Наконец, мы устанавливаем размер шрифта в пунктах.

Теперь мы готовы добавить текст. Весь текст, который вы пишете в своем PDF, будет написан в ячейках.

1
$pdf->Cell(100, 16, «Hello, World!»);

Так, что это делает? Первый параметр — это ширина ячейки; вторая высота этого. Конечно, третий текст для ячейки. Есть довольно много других параметров, но сначала давайте посмотрим, как это выглядит. Чтобы закончить PDF, давайте использовать метод вывода.

1
$pdf->Output();

Мы могли бы дать этому методу два параметра, но сейчас мы просто оставим их. Как это сейчас, PDF будет отображаться, когда мы заходим на страницу. Итак, давайте сделаем это прямо сейчас! Вот что я получаю:


Молодец! Вы создали свой первый PDF. Давайте вернемся к строке, которая создала текстовую ячейку, и внесем изменения:

1
$pdf->Cell(100, 16, «Hello, World!», 1);

Мы добавили четвертый параметр; это граница. В нашем случае мы поставили границу вокруг. Вы можете сделать разделительную границу со строкой: «LB» добавит границы слева и снизу; «TLBR» такой же, как 1.

Если вы перезагрузите страницу сейчас и обнаружите, что ничего не произошло, просто добавьте случайную строку запроса в URL, чтобы принудительно обновить страницу.

Мы собираемся вскоре сделать небольшой сценарий, генерирующий квитанцию, но давайте сначала посмотрим, как установить еще несколько параметров.



Вы можете заполнить текстовые ячейки цветом фона; мы увидим, как это сделать в нашем приложении, но сначала нужно установить цвет заливки. Просто передайте этой функции значения красного, зеленого и синего (от 0 до 255). Вы можете установить цвет текста таким же образом.

1
2
$pdf->SetFillColor(36, 96, 84);
$pdf->SetTextColor(255);

Если вы хотите установить для всех трех цветов одинаковое значение, просто используйте один параметр.

При рисовании границы на ячейках несложно настроить ширину границы. Просто вызовите этот метод, передавая в float; помните, что вы устанавливаете единицы измерения при создании экземпляра FPDF. Когда вы начинаете, границы шириной 0,2 мм.

1
$pdf->SetLineWidth(1);

Эти методы предназначены для настройки свойств PDF, метаданных. Каждый из них берет строку с соответствующим содержанием. Они также могут принимать необязательный логический тип, который определяет кодировку: true — это UTF-8, а false — это ISO-8859-1.

1
2
3
4
5
$pdf->SetAuthor(«Collis and Cyan Ta’eed»);
$pdf->SetTitle(«How to be a Rockstar Freelancer»);
$pdf->SetSubject(«Freelancing»);
$pdf->SetKeywords(«Freelance Brand Project Contract Marketing Clients»);
$pdf->SetCreator(«EnvatoEdit»);

По умолчанию поля вашей страницы составляют 1 см. Вы можете установить верхнее, левое и правое поля индивидуально с их соответствующими функциями или установить их все сразу.

1
2
3
4
5
$pdf->SetMargins(40, 35, 20);
// is eqaul to
$pdf->SetTopMargins(40);
$pdf->SetLeftMargin(35);
$pdf->SetRightMargin(20);

Но как насчет нижнего поля? Я рад, что ты спросил, что позаботился о другой функции.

Эта функция принимает логическое значение, определяющее, хотите ли вы, чтобы библиотека добавляла разрыв страницы при необходимости, или же вы хотите сделать это вручную. Если значение равно true (по умолчанию), вы можете добавить второй параметр, определяющий, насколько близко вы хотите добраться до разрыва. По умолчанию это 2 см.

1
2
$pdf->SetAutoPageBreak(false);
$pdf->SetAutoPageBreak(true, 40);

Теперь, когда у нас есть кое-что полезное для создания PDF, давайте начнем наше небольшое приложение. Начнем с создания формы HTML. Когда пользователь нажимает «отправить», мы берем значения из формы и используем их для создания квитанции для нашего клиента.

Прежде всего, небольшая оболочка HTML:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
    <meta charset=’utf-8′ />
    <title>Invoice Form</title>
    <link rel=»stylesheet» href=»style.css» />
</head>
<body>
<div id=»wrap»><div>
    <h1>Checkout</h1>
    <form method=»post» action=»create_reciept.php»>
 
    </form>
</div></div>

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<fieldset>
    <legend>Personal Information</legend>
    <div class=»col»>
        <p>
            <label for=»name»>Name</label>
            <input type=»text» name=»name» value=»John Doe» />
        </p>
        <p>
            <label for=»email»>Email Address</label>
            <input type=»text» name=»email» value=»[email protected]» />
        </p>
    </div>
    <div class=»col»>
        <p>
            <label for=»address»>Address</label>
            <input type=»text» name=»address» value=»123 Main Street» />
        </p>
        <p>
            <label for=»city»>City</label>
            <input type=»text» name=»city» value=»Toronto» />
        </p>
        <p>
            <label for=»province»>Province</label>
            <input type=»text» name=»province» value=»Ontario» />
        </p>
        <p>
            <label for=»postal_code»>Postal Code</label>
            <input type=»text» name=»postal_code» value=»A1B 2C3″ />
        </p>
        <p>
            <label for=»country»>Country</label><input type=»text» name=»country» value=»Canada» />
        </p>
    </div>
</fieldset>

Обратите внимание на две вещи: мы увидим стиль для .col когда .col до CSS; это позволит нам расположить эти поля в два столбца. Во-вторых, я дал все входные значения, чтобы потом сэкономить время.

Мы добавим еще один набор полей для продуктов, которые покупает клиент:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<fieldset>
    <legend>Purchases</legend>
    <table>
        <thead>
            <tr><td>Product</td><td>Price</td></tr>
        <thead>
        <tbody>
            <tr>
                <td><input type=’text’ value=’a neat product’ name=’product[]’ /></td>
                <td>$<input type=’text’ value=’10.00′ name=’price[]’ /></td>
            </tr>
            <tr>
                <td><input type=’text’ value=’something else cool’ name=’product[]’ /></td>
                <td>$<input type=’text’ value=’9.99′ name=’price[]’ /></td>
            </tr>
            <tr>
                <td><input type=’text’ value=’buy this too!’
                <td>$<input type=’text’ value=’17.85′ name=’price[]’ /></td>
            </tr>
            <tr>
                <td><input type=’text’ value=’And finally this’ name=’product[]’ /></td>
                <td>$<input type=’text’ value=’5.67′ name=’price[]’ /></td>
            </tr>
        </tbody>
    </table>
</fieldset>

Я думаю, что информация, которую мы здесь показываем, подходит для таблицы, так что это то, что мы используем. Кроме того, я поставил набор квадратных скобок в конце имен входов; обратите внимание, что у нас есть набор входных данных с именем product[] и набор с именем price[] . Они будут опубликованы в виде массивов.

Конечно, наша форма заканчивается кнопкой отправки.

1
<button type=»submit»>Submit Order</button>

Теперь мы можем перейти к CSS. Мы не будем подробно останавливаться на этом, но это совсем не сложно. Вы можете поместить его в style.css , на который мы style.css в HTML.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  body {
    font: 13px/1.5 ‘Helvetica Neue’, Arial, ‘Liberation Sans’, FreeSans, sans-serif;
    background:#ececec;
}
.col {
    width:50%;
    float:left;
}
fieldset {
    border:1px solid #474747;
    -moz-border-radius:5px;
    -webkit-border-radius:5px;
    margin-bottom:10px;
}
legend {
    background:#474747;
    color:#fff;
    padding:3px 15px;
    -moz-border-radius:5px;
    -webkit-border-radius:5px;
    font-weight:bold;
}
#wrap {
    width:820px;
    padding:20px;
    margin:20px auto;
    background:#fff;
    border:1px solid #ccc;
    -moz-border-radius:10px;
    -webkit-border-radius:10px;
}
p {
    padding:2px;
}
label {
    display:inline-block;
    width:100px;
    font-weight:bold;
}
label:after {
    content: «:»;
}
input {
    display:inline-block;
    width:200px;
    border:1px solid #ccc;
    padding:5px;
    margin:0;
    -moz-border-radius:5px;
    -webkit-border-radius:5px;
}
thead td {
    text-align:center;
    font-weight:bold;
}
td + td > input {
    width:50px;
}
button {
    border:1px solid #474747;
    background:#ccc;
    -moz-border-radius:5px;
    -webkit-border-radius:5px;
    padding:10px;
    margin:10px;
    font-weight:bold;
    font-size:200%;
}
button:hover {
    background:#ececec;
    cursor:pointer;
}

Как видите, мы придаем большинству элементов минималистский стиль; ничего потрясающего, но это будет легко для глаз. Вот как выглядит наша форма:


Таким образом, когда наш клиент заполняет форму и нажимает кнопку « create_reciept.php данные формы будут опубликованы в create_reciept.php . Итак, давайте создадим файл прямо сейчас!


Мы вернулись к PHP сейчас. Перво-наперво: получить библиотеку:

1
require(‘fpdf/fpdf.php’);

Далее, вместо создания экземпляра класса FPDF, мы собираемся расширить его собственным классом. Это позволит нам сделать некоторые изящные вещи. Смотрите внимательно сейчас:

1
2
3
4
5
6
7
8
9
class PDF_reciept extends FPDF {
    function __construct ($orientation = ‘P’, $unit = ‘pt’, $format = ‘Letter’, $margin = 40) {
        $this->FPDF($orientation, $unit, $format);
        $this->SetTopMargin($margin);
        $this->SetLeftMargin($margin);
        $this->SetRightMargin($margin);
        $this->SetAutoPageBreak(true, $margin);
    }
}

Мы начали с нашего нового класса, написав конструктор. Мы дали всем параметрам значения по умолчанию, чтобы наш конструктор можно было вызывать без них. Внутри функции мы вызываем конструктор нашего родительского класса, а затем устанавливаем поля.

Теперь у нас две специальные функции: верхний и нижний колонтитулы.

1
2
3
4
5
6
function Header() {
    $this->SetFont(‘Arial’, ‘B’, 20);
    $this->SetFillColor(36, 96, 84);
    $this->SetTextColor(225);
    $this->Cell(0, 30, «Nettuts+ Online Store», 0, 1, ‘C’, true);
}

Эта функция будет запускаться в начале каждой страницы нашего PDF. Сначала мы устанавливаем шрифт, цвет заливки и цвет текста. Затем мы выводим нашу ячейку. Обратите внимание, что мы установили ширину равной 0. Это специальное значение; ячейка будет занимать все пространство справа от начальной точки (пока не достигнет правого поля). Поскольку мы начинаем с левого поля, мы получим ячейку на всю ширину страницы (минус поля).

Теперь для функции нижнего колонтитула:

1
2
3
4
5
6
function Footer() {
    $this->SetFont(‘Arial’, », 12);
    $this->SetTextColor(0);
    $this->SetXY(0,-60);
    $this->Cell(0, 20, «Thank you for shopping at Nettuts+!», ‘T’, 0, ‘C’);
}

Опять же, мы начинаем с установки шрифта и цвета текста; мы не можем быть уверены, какими будут эти значения, когда придем к заголовку, поэтому установим их просто для уверенности. Далее мы устанавливаем координаты X и Y. Это устанавливает верхний левый угол нашей следующей ячейки. Мы устанавливаем X в 0, просто чтобы убедиться, что мы в начале строки. Устанавливаем Y на -60; отрицательное число относится к нижней части страницы, поэтому это устанавливает курсор на 60 пикселей выше нижней части.

Далее мы создаем ячейку; опять же, мы используем трюк с 0 шириной; мы устанавливаем высоту до 20 пунктов. Мы устанавливаем текст, назначаем ему верхнюю границу и центрируем текст. Это оно!

Мы вернемся к нашему классу снова, но сейчас давайте начнем строить квитанцию. После создания экземпляра нашего класса, добавления страницы и установки шрифта мы можем приступить к делу.

1
2
3
$pdf = new PDF_receipt();
$pdf->AddPage();
$pdf->SetFont(‘Arial’, », 12);

Это первое, что мы сделаем, это переместим наш курсор; мы можем сделать это с помощью метода SetY .

1
$pdf->SetY(100);

Это не сдвинет нас на 100 пунктов; Это устанавливает нам 100 баллов от верхней части страницы. Это не включает в себя маржу, поэтому мы на 30 пунктов ниже нижней части нашего заголовка.

1
2
3
$pdf->Cell(100, 13, «Ordered By»);
$pdf->SetFont(‘Arial, »);
$pdf->Cell(100, 13, $_POST[‘name’]);

Мы начинаем с создания ячейки шириной 100pt и высотой 13pt с текстом «Упорядочено». Далее мы сбрасываем шрифт, чтобы удалить жирный шрифт. Затем мы создаем еще одну ячейку и передаем ей имя, которое мы получили из нашей формы. Конечно, в реальном проекте вы будете проверять эти значения, прежде чем использовать их.

Двигаясь дальше, добавим дату покупки:

1
2
3
4
$pdf->SetFont(‘Arial’, ‘B’, 12);
$pdf->Cell(50, 13, ‘Date’);
$pdf->SetFont(‘Arial’, »);
$pdf->Cell(100, 13, date(‘F j, Y’), 0, 1);

Опять же, мы установили шрифт жирным шрифтом; после написания «Date» мы удаляем жирный шрифт и печатаем текущую дату, используя PHP-функцию date. Указанный нами формат даст нам название месяца, номер дня с запятой и год из четырех цифр. Мы устанавливаем границу как 0 (по умолчанию), чтобы мы могли добраться до параметра line. Мы устанавливаем его как 1, что будет помещать нашу позицию в начало следующей строки; это эквивалент нажатия «enter» в текстовом редакторе.

Двигаемся дальше. , ,

1
2
3
4
5
6
$pdf->SetX(140);
$pdf->SetFont(‘Arial’, ‘I’);
$pdf->Cell(200, 15, $_POST[‘address’], 0, 2);
$pdf->Cell(200, 15, $_POST[‘city’] . ‘, ‘ . $_POST[‘province’], 0, 2);
$pdf->Cell(200, 15, $_POST[‘postal_code’] . ‘ ‘ . $_POST[‘country’]);
$pdf->Ln(100);

Функция SetX делает то же самое, что SetY функция SetY , за исключением того, что она выставляет нас справа. Мы ставим нашу позицию на 100pt справа от левого поля. Затем мы выделяем наш шрифт курсивом и начинаем выводить адрес. Все три строки адреса имеют ширину 200pt и высоту 15pt. Здесь важно отметить, что наш параметр строки равен 2. Это означает, что после каждой ячейки мы будем переходить к следующей строке и вкладке, чтобы выровняться с предыдущей ячейкой. Наконец, мы вызываем метод Ln , который добавляет разрыв строки. Без параметра он сдвинет нашу позицию вниз по высоте последней напечатанной ячейки. Мы передали ему параметр, поэтому мы снижаем 100pt.

Вот как мы выглядим до сих пор:


Теперь распечатаем таблицу продуктов, которые приобрел наш клиент.

1
$pdf->PriceTable($_POST[‘product’], $_POST[‘price’]);

Мы передали два массива, которые мы получили из нашего HTML-файла. Но откуда этот метод PriceTable ? Мы собираемся сделать это прямо сейчас, в нашем классе PDF_receipt . Это еще одно преимущество расширения класса: мы можем зафиксировать потенциально трудоемкую функциональность в методе.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
    function PriceTable($products, $prices) {
    $this->SetFont(‘Arial’, ‘B’, 12);
    $this->SetTextColor(0);
    $this->SetFillColor(36, 140, 129);
    $this->SetLineWidth(1);
    $this->Cell(427, 25, «Item Description», ‘LTR’, 0, ‘C’, true);
    $this->Cell(100, 25, «Price», ‘LTR’, 1, ‘C’, true);
 
    $this->SetFont(‘Arial’, »);
    $this->SetFillColor(238);
    $this->SetLineWidth(0.2);
    $fill = false;
 
    for ($i = 0; $i < count($products); $i++) {
        $this->Cell(427, 20, $products[$i], 1, 0, ‘L’, $fill);
        $this->Cell(100, 20, ‘$’ . $prices[$i], 1, 1, ‘R’, $fill);
        $fill = !$fill;
    }
    $this->SetX(367);
    $this->Cell(100, 20, «Total», 1);
    $this->Cell(100, 20, ‘$’ . array_sum($prices), 1, 1, ‘R’);
}

Вот функция. Мы начнем с установки нашего шрифта, цвета текста, цвета заливки и ширины линии. Затем мы настраиваем наши ячейки заголовка. Каждый из них имеет рамку слева, сверху и справа, текст по центру и заливку.

Затем мы удаляем «жирность» из шрифта и сбрасываем цвет заливки и ширину линии. Мы также создаем переменную $fill , для которой установлено значение false. Мы будем использовать это для добавления зебры в наш стол.

Цикл for проходит через массив продуктов и цен (в них должно быть одинаковое количество товаров на основе таблицы в нашем HTML-файле). Мы создаем ячейку для каждой записи, помещая название продукта под заголовком «Описание товара» и цену под заголовком «Цена». У них обоих есть граница. Товар выровнен по левому краю, цена выровнена по правому краю. Наконец, они оба используют $fill , что в настоящее время неверно. После каждой строки мы переключаем значение $fill на противоположное логическое значение, поэтому следующая строка будет иметь серый фон.

После того, как мы пройдемся по всем покупкам, мы хотим указать общую стоимость. Мы поднимем наш курсор 367pt от левого края страницы. Затем мы создаем ячейку «Итого» и, наконец, используем функцию php array_sum для суммирования всех цен в $prices . И это таблица цен!

Возвращаясь к основному разделу нашего PDF, теперь мы пропустим 50pt.

1
$pdf->Ln(50);

До сих пор мы использовали только функцию Cell для печати на нашей странице. Также мы можем использовать два других метода: MultiCell и Write . Обе эти функции предназначены для строк текста, которые слишком длинны для одной ячейки полной ширины. Давайте сначала посмотрим на MultiCell .

MultiCell очень похож на Cell , за исключением того, что когда он достигнет ширины, указанной для ячейки, он создаст еще одну под ней и продолжит запись. Он также сломается, когда попадет ‘\ n’. Из-за этого ему не нужен параметр свойства строки. Он также не имеет параметра ссылки, который имеет Cell . Какая? Мы еще не упомянули параметр ссылки? Ну, Cell может принимать еще один параметр, после параметра fill: это либо строка ссылки, либо объект ссылки, указывающий на другое место в PDF, созданное с AddLink функции AddLink .

1
2
$message = «Thank you for ordering at the Nettuts+ online store. Our policy is to ship your materials within two business days of purchase. On all orders over $20.00, we offer free 2-3 day shipping. If you haven’t received your items in 3 busines days, let us know and we’ll reimburse you 5%.\n\nWe hope you enjoy the items you have purchased. If you have any questions, you can email us at the following email address:»;
$pdf->MultiCell(0, 15, $message);

Текст не так уж важен, но он слишком длинный и стоит на одной строке; Кроме того, в нем есть несколько разрывов строк. Хотя мы не используем их здесь, MultiCell также принимает параметры border, alignment и fill.

Метод Write принимает только три параметра: высоту, текст и необязательную ссылку. Там нет параметра ширины, потому что он пойдет на правое поле, прежде чем он разорвет линию. Хотя это не лучшее использование, мы добавим ссылку:

1
2
3
4
$pdf->SetFont(‘Arial’, ‘U’, 12);
$pdf->SetTextColor(1, 162, 232);
 
$pdf->Write(13, «[email protected]», «mailto:[email protected]»);

Обратите внимание, что мы устанавливаем шрифт и цвет текста перед записью, чтобы он выглядел как ссылка.

Вот и все для PHP. Давайте добавим еще одну особенность: мы отправим нашу форму с Ajax. Прежде чем вернуться к index.html, внесите эти изменения, чтобы вызвать Output :

1
$pdf->Output(‘reciept.pdf’, ‘F’);

Первый параметр — это имя нашего файла; второй — то, что мы хотим сделать с файлом, в этом случае, сохраните его на сервере. Вы также можете форсировать загрузку с помощью ‘D’, возвращать ее как строку с ‘S’. По умолчанию, выводится в браузер, «I» (для встроенного).

Вернемся к HTML, давайте импортируем jQuery и добавим наш обработчик кликов к кнопке отправки:

01
02
03
04
05
06
07
08
09
10
11
<script src=»http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js»></script>
<script type=»text/javascript»>
$(‘button’).click(function () {
    $.post(‘create_reciept.php’, $(‘form’).serialize(), function () {
        $(‘div#wrap div’).fadeOut( function () {
            $(this).empty().html(«<h2>Thank you!</h2><p>Thank you for your order. Please <a href=’reciept.pdf’>download your reciept</a>. </p>»).fadeIn();
        });
    });
    return false;
});
</script>

Когда кнопка нажата, мы отправляем форму. При успешном возвращении мы исчезаем из второго div, очищаем его содержимое и перезагружаем его, используя функцию HTML. Этот новый HTML имеет ссылку для получения. Затем мы возвращаем div. Не забывайте возвращать false, чтобы форма не отправлялась регулярно.

Вот что мы получаем


И вот наш готовый PDF.


И это все, ребята! Хит комментарии с замечаниями или предложениями!