Калининград+7.962.2626.555

Создание Word (.docx) документов на PHP

06.10.2014

На момент написания данной статьи релиз PHPWord датируется 8 июля 2011 года. Да еще бета версия. Конечно старовата, но с другой стороны, если класс хорошо выполняет поставленную задачу, то почему бы и нет?!

К делу: скачиваем, подключаем обычным инклюдом и вперед.

Создаем экземпляр класса:

$PHPWord = new PHPWord();

Необязательно, но можем добавить, что по-умолчанию используем шрифт Arial размером 14 пунктов.

$PHPWord->setDefaultFontName('Arial');
$PHPWord->setDefaultFontSize(14);

Добавляем новый раздел в документ:

$section = $PHPWord->createSection([array $sectionStyle]);

По-умолчанию этот метод создает страницу A4 книжной ориентации. Поля: по 2,5 см верхнее, левое и правое и 2 см нижнее.

Массив $sectionStyle может содержать:

$sectionStyle = array(
    'orientation' => 'landscape', // альбомная ориентация страницы
    'marginTop' => '0', // по-умолчанию равен 1418* и соответствует 2,5 см отступа сверху
    'marginLeft' => '0', // по-умолчанию равен 1418* и соответствует 2,5 см отступа слева
    'marginRight' => '0', // по-умолчанию равен 1418* и соответствует 2,5 см отступа справа
    'marginBottom' => '0', // по-умолчанию равен 1134* и соответствует 2 см отступа снизу
    'pageSizeW' => '8419', // по-умолчанию равен 11906* и соответствует 210 мм по ширине
    'pageSizeH' => '11906', // по-умолчанию равен 16838* и соответствует 297 мм по высоте
    'borderColor'=>'999999', // Цвет ненужного бордюра
    'borderSize'=>'100', // Ширина ненужного бордюра*
);

* В качестве единиц измерения тут используются типографские твипы. Для справки: 1 твип равен 1/567 см.

Текст

У нас есть пустая страница. Для начала добавим обычную текстовую строку. Для этого существует метод addText() и два синтаксиса:

$section->addText(string $text[, array $textStyle]);
// или
$section->addText(string $text[, string $fontStyleName[, string $paragraphStyleName]]);

На практике выглядит это так:

$section->addText('Создание сайтов — Лаборатории WEB');

Тут стоит сделать замечание: автор PHPWord решил, что все, кто будет пользоваться его классом будут работать в кодировке отличной от UTF-8. Если просматривать код PHPWord, то там везде, как через мясорубку, все текстовые переменные проходят через utf8_encode(). Вот в моем случае это сыграло не на руку, потому что я как раз-то работаю с UTF-8.

Что делать, если вы тоже работаете с UTF-8? Варианта как минимум два:

  1. перед тем как отдать строки в PHPWord измените их кодировку на не UTF-8 с помощью iconv();
  2. прошерстите PHPWord и удалите все utf8_encode() оттуда.

Мной был выбран второй вариант.

Двигаемся дальше... Наведем «красоту» в тексте.

Первый вариант — это объявление всякой «красоты» непосредственно в методе addText().

$section->addText('Разработка сайтов — Лаборатория WEB', array(
    'name' => 'Tahoma',
    'color' => '990000',
    'bold' => true,
    'italic' => true,
    'size' => 16,
));

Второй вариант — объединение набора «красот» в стиль.

$PHPWord->addFontStyle('fStyle', array(
    'name' => 'Tahoma',
    'color' => '990000',
    'bold' => true,
    'italic' => true,
    'size' => 16,
));
$section->addText('Изготовление сайтов — Лаборатория WEB', 'fStyle');

Сейчас был задан стиль для шрифта, но можно задать стиль и для параграфа:

$PHPWord->addParagraphStyle('pStyle', array(
    'align' => 'center',
    'spaceBefore' => 100, // отступ сверху
    'spaceAfter' => 100, // отступ снизу
    'spacing' => 100, // межстрочный интервал
));

И использовать эти стили как совместно, так и по-отдельности:

$section->addText('Поддержка сайтов — Лаборатория WEB', 'fStyle', 'pStyle');
// или
$section->addText('Продвижение сайтов — Лаборатория WEB', null, 'pStyle');

Если вам нужно объединить в одном параграфе несколько текстовых блоков с разным форматированием, то для этого существует метод createTextRun():

$textrun = $section->createTextRun('pStyle');
$textrun->addText('Жирный', array(
    'bold' => true
));
$textrun->addText('Курсив', array(
    'italic' => true
));
$textrun->addText('Красный', array(
    'color'=>'990000'
));

С текстом, вроде, все ясно. Перенос курсора на следующую строку:

$section->addTextBreak([int $number]); // В скобках указывается количество строк на которое нужно перейти. По-умолчанию $number = 1

Изображения

Изображения вставляются также просто, как и текст. Для этого используется метод addImage():

$section->addImage(string $srcLocalImage[, array $imageStyle]);

Массив $imageStyle может содержать:

$imageStyle = array(
    'width' => '200', // в пикселях
    'height' => '200', // в пикселях
    'align' => 'center', // left || right || center
)

На практике это выглядит так:

$section->addImage('path-to-image.png', $imageStyle);

Ссылки

Метод для добавления ссылки addLink():

$section->addLink(string $url, [string $text[, string $linkFontStyle[, string $paragraphStyle]]]);

Наведение «красоты» для ссылки:

$PHPWord->addLinkStyle('lStyle', array(
    'name' => 'Tahoma',
    'color' => '990000',
    'bold' => true,
    'italic' => true,
    'size' => 16,
));

На практике это выглядит:

$section->addLink('http://www.w-lab.ru', 'Лаборатория WEB', 'lStyle', 'pStyle');

Таблицы

С таблицами немного сложнее. Для добавления таблицы на страницу используем метод addTable(). Как и в случае с текстом, для таблиц существует два синтаксиса. Первый выглядит так:

$table = $section->addTable([array $tableStyle]);

Массив $tableStyle может содержать:

$tableStyle = array(
    'cellMarginTop' => 0, // отступ от ячейки сверху *
    'cellMarginRight' => 0, // отступ от ячейки справа *
    'cellMarginBottom' => 0, // отступ от ячейки снизу *
    'cellMarginLeft' => 0, // отступ от ячейки слева *
);

* в твипах.

cellMarginTop, cellMarginRight, cellMarginBottom, cellMarginLeft можно заменить одним cellMargin.

Второй синтаксис:

$table = $section->addTable([string $tableStyleName]);

Для того, чтобы назначить $tableStyleName, вызовем метод addTableStyle():

$PHPWord->addTableStyle(string $styleName, array $tableStyle[, array $firstRowTableStyle]);

Как можно понять из названия, массив $firstRowTableStyle отвечает за стили первой строки таблицы.

На практике:

$word->addTableStyle('tStyle',  array(
    'borderSize' => 6,
    'borderColor' => '999999',
    'cellMarginTop' => 40,
    'cellMarginRight' => 20,
    'cellMarginBottom' => 40,
    'cellMarginLeft' => 20,
), array(
    'borderSize' => 12,
    'borderColor' => '000000',
    'cellMargin' => 80,
));
$table = $section->addTable('tStyle');

Тут мы назначили для ячеек всей таблицы ширину границы 6, цвет серый, с отступами 40 20 40 20. А для ячеек первой строки ширину границы 12, черного цвета с отступами 80 со всех сторон.

Теперь в таблицу нужно добавить строку. Для этого существует метод addRow():

$table->addRow([int $rowHeight]); // $rowHeight — высота строки в твипах

И методом addCell() добавляем ячейку:

$cell = $table->addCell(int $cellWidth[, array $cellStyle]);

Здесь $cellWidth — ширина ячейки в твипах, а массив $cellStyle может содержать:

$cellStyle = array(
    'valign' => 'center', // top || bottom || center || both
    'textDirection' => PHPWord_Style_Cell:TEXT_DIR_BTLR, // PHPWord_Style_Cell:TEXT_DIR_BTLR || PHPWord_Style_Cell:TEXT_DIR_TBRL
    'bgColor' => 'fafafa',
    'borderTopSize' => 6,
    'borderRightSize' => 6,
    'borderBottomSize' => 6,
    'borderLeftSize' => 6,
    'borderSize' => 6, // вместо borderTopSize, borderRightSize, borderBottomSize, borderLeftSize
    'borderTopColor' => '999999',
    'borderRightColor' => '999999',
    'borderBottomColor' => '999999',
    'borderLeftColor' => '999999',
    'borderColor' => '999999', // вместо borderTopColor, borderRightColor, borderBottomColor, borderLeftColor
);

Последнее, что нужно сделать — это добавить содержимое в новую ячейку (добавим текст). Сделать это можно двумя способами:

$cell = $table->addCell();
$cell->addText('Создание Langing Page — Лаборатория WEB');
// или
$table->addCell()->addText('Разработка Langing Page — Лаборатория WEB');

Списки

Добавление на страницу нумерованных и ненумерованных списков осуществляется методом addListItem():

$section->addListItem(string $text[, int $depth[, string $textStyle[, array $listStyle[, string $paragraphStyle]]]]);

Здесь $depth — глубина (вложенность) списка от 1 до 9, а массив $listType может состоять из:

$listType = array(
    'listType' => PHPWord_Style_ListItem:TYPE_NUMBER, // одноуровневый нумерованный список
);

Также параметр 'listType' может принимать следующие значения:

  • PHPWord_Style_ListItem:TYPE_NUMBER_NESTED — многоуровневый нумерованный список;
  • PHPWord_Style_ListItem:TYPE_BULLET_FILLED — ненумерованный список с маркерами в виде закрашенных кругов;
  • PHPWord_Style_ListItem:TYPE_BULLET_EMPTY — ненумерованный список с маркерами в виде незакрашенных кругов;
  • PHPWord_Style_ListItem:TYPE_SQUARE_FILLED — ненумерованный список с маркерами в виде закрашенных квадратов.

Колонтитулы

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

Создадим верхний и нижний колонтитулы и добавим в них содержимое:

$header = $section->createHeader();
$header->addText('Лаборатория WEB');

$footer = $section->createFooter();
$footer->addPreserveText('Страница {PAGE} из {NUMPAGES}', array(
    'italic' => true,
),
array(
    'align' => 'right',
));

Метод addPreserveText() существует специально для добавления номеров страниц.

Разное

$section->addPageBreak(); // Разрыв страницы

Метаданные:

$meta = $PHPWord->getProperties();
$meta->setTitle('Название');
$meta->setSubject('Тема');
$meta->setCreator('Автор');
$meta->setCompany('Учреждение');
$meta->setDescription('Заметки');
$meta->setCategory('Группа');
$meta->setLastModifiedBy('Автор изменений');
$meta->setKeywords('Ключевые слова');
$meta->setCreated(time()); // Дата и время создания документа
$meta->setModified(time()); //Дата и время последнего изменения документа

Сохранение файлов

В файл на жесткий:

$writer = PHPWord_IOFactory::createWriter($PHPWord, 'Word2007');
$writer->save('document.docx');

Вывод вопроса на скачивание:

header("Content-Type: application/msword");
header("Content-Transfer-Encoding: binary");
header('Content-Disposition: attachment;filename="document.docx"');
header('Cache-Control: max-age=0');
$writer = PHPWord_IOFactory::createWriter($PHPWord, 'Word2007');
$writer->save('php://output');