Программист мобильных приложений
0,0
рейтинг
25 января 2012 в 19:35

Разработка → Не очень честная генерация DOC файлов на PHP из песочницы

PHP*
У каждой задачи есть несколько вариантов решения. И иногда в угоду скорости приходится выбирать не самый красивый, зато работающий и выполняющий поставленные перед ним цели. Итак, в один не очень прекрасный день возникла необходимость реализовать следующую функцию: у каждой (почти) страницы сайта должны быть автоматически сгенерированные копии в форматах DOC и PDF. С сохранением всех таблиц и картинок внутри контента. И если с PDF всё относительно просто (tcpdf наш друг и брат), то с DOC'ом возникла морока. Под катом — пример решения данной задачи.В голову приходили последовательно такие варианты решения:
  • Установить на сервер OpenOffice (сервер на FreeBSD) и разобраться с преобразованием. Красивое решение, но время поджимало.
  • Сгенерировать вместо DOC'а файлик в формате RTF, благо формат открытый и библиотек для работы с ним много. Минус – готового преобразователя HTML->RTF (с, напомню, сохранением картинок и таблиц) я не нашел, а писать свою – для этого нужен определённый запас времени
  • Не очень честный — просто “в лоб” сохранить HTML страницу с расширением DOC – Word 2003 и выше откроет без проблем, проверено. Плюс – отличнейшая скорость преобразования и сохранение всей верстки. Минус – картинки таким образом не сохранить (да и честность метода несколько хромает).
Вот собственно из третьего варианта и родилось окончательное решение: создавать документ формата MHT, интегрировать в него картинки и сохранить с расширением DOC. Для генерации использовалась простенькая библиотека, взятая отсюда .Код не претендует на красоту и универсальность, более того, в нем есть проблемы, не актуальные для того сайта, но главное – он работает, и его достаточно, чтобы разобраться в теме.

Пример применения написанной функции:

$link="m.habrahabr.ru/post/136811/";
CreateDOC($link,"test.doc");



А вот и исходный код функции:

function CreateDOC($link,$filename)
{

//выделяем базовый домен, пригодится
$base_link=$link;
$base_link=explode("/",$link);
unset($base_link[count($base_link)-1]);
$base_link[]="";
$base_link=implode("/",$base_link);

//получаем текст страницы
$get_text=file_get_contents($link);

//создаём объект, который и будет генерировать нам конечный mht
$MhtFileMaker = new MhtFileMaker();


//подключаем картинки к файлу

//более прямая регулярка, вытаскиваюшая пути картинок, спасибо хабраюзеру FlexIDK
preg_match_all('@<img(.*)?src="([^"]+)"@ui', $get_text, $matches);

foreach ($matches[4] as $img)
	{
		$img_tmp=$img;
		$img_tmp_old=$img;
		
		//помните, базовая ссылка? пригодилась же!
		if (strpos($img_tmp,"http")===FALSE) 
			$img_tmp=$base_link.$img_tmp;
	
	
		//выделяем путь картинки БЕЗ адреса домена
		$img_array=explode("//",$img_tmp);
		$img_name_only=$img_array[1];
		$img_name_only=explode("/",$img_name_only);
		unset($img_name_only[0]);
		$img_name_only=implode("/",$img_name_only);

		//заменяем адрес картинки на относительный (без домена)
		$get_text=str_replace($img_tmp_old,$img_name_only,$get_text);
	
		//добавляем картинку в конечный файлГ
		$MhtFileMaker->AddFile($img_tmp, $img_name_only, NULL);
};

//разобрали картинки, теперь создаём окончательный файл
$MhtFileMaker->AddContents("index.html","text/html",$get_text);

//сохраняем файл
$MhtFileMaker->MakeFile($filename);
};
Естественно, можно написать на основе этого гораздо более универсальный и прямой класс, но для наших целей этого было достаточно. Главное — данное решение работает, и достаточно быстро. Надеюсь, оно покажется кому то достаточно полезным.

Update: в комментариях протестировали получившийся файл — нормально он открывается только в Microsoft Word 2003 и выше, в сторонних продуктах (OpenOffice и другие) возникают проблемы. Так же в комментариях приведены ссылки на многие другие, более правильные методы преобразования

Update 2: Обновил исходник — FlexIDK предложил более удачную регулярку, выбирающую пути картинок без лишних символов.
Дмитрий @Newbilius
карма
92,5
рейтинг 0,0
Программист мобильных приложений
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (43)

  • +8
    Кстати, MHT — отличное решение для сокращения количества запросов к высоконагруженному серверу. Увы, разработчики как хрома, так и файрфокса не удосужились за 11 лет реализовать RFC, опубликованный в 1999-ом году.
    • 0
      Надо mod_mht к Апачу сделать чтобы он на ходу страницы в .mht собирал и отдавал.
  • +5
    Я для аналогичной задачи использовал AbiWord:

    abiword -t doc document.html -o document.doc

    Была тонкость. На девелоперской машинке AbiWord подтягивал картинки по http, а на продакшн почему-то нет. Пришлось в html вписать путь к картинкам не http:// ...., а полный путь к файлу file:///… и на выходе получался настоящий doc-файл с картинками, который везде открывался.
    • 0
      Спасибо! Будет время, попробую и этот вариант.
  • +2
    Кстати по поводу openoffice и преобразования. Eсть использующий openoffice преобразователь документов unoconv
    • 0
      Благодарю, возьму на заметку!

      У описанного мною метода по сравнению с внешними утилитами всё-таки есть ещё один плюс — вышеописанный скрипт можно выполнить и на shared хостинге, без каких-либо дополнительных утилит.
      • 0
        Да, безусловно, это большой плюс.
  • +4
    Пример сконвертированного файла.

    Скачал, попытался открыть в LibreOffice 3.4 — открывается как текстовый файл с кракозябрами вместо картинок и отсутствием форматирования.
    Так что решение пойдёт, наверно, только для пользователей MS Office :-(
    • 0
      Честно, не догадался проверить результат в LibreOffice/OpenOffice, только в Word XP/2003 и старше. Значит не такой уж и красивый способ вышел. Воистину, не очень честный.
      • +2
        Pages из пакета iWork и вовсе файл не открывает.
      • 0
        MS Word for Mac — плохо всё
  • 0
    Второй вариант решения можно с помощью вот этого инструменат реализовать: code.google.com/p/wkhtmltopdf/
    • 0
      Пардон, там html в rtf надо, не доглядел.
      • 0
        Зато для меня в самый раз — спасибо
  • +4
    А в случае сохранения в обычный html нельзя ли картинки интегрировать с помощью конструкции data:image/gif;base64? :)
  • –8
    Я для PDF когда-то использовал класс что-то типа HTML->PDF.

    Думаю должен такой и для ворда быть :)
    На будущее.
  • +1
    OpenXML, docx ваши друзья. Ну и так к слову. В java с этим лучше ;)
  • +1
    Как вовремя вы подоспели с этой статьёй! Неделю назад задавал такой же вопрос в Q&A на хабре, один из ответов был как раз использовать mht. Не успел ещё взяться за дело, как вы уже даёте всё разложили по полочкам, спасибо :)
  • НЛО прилетело и опубликовало эту надпись здесь
    • –1
      для простых таблиц вроде же можна юзать обычный csv с расширением xls
      • 0
        CSV — далеко не таблица, а скорее список значений, крайне ограниченный по возможностям
    • 0
      Для openoffic-а такие костыли просто не нужны. Сгенерил XML, припаковал zip-ом, и готов OO-файл :)
  • +6
  • 0
    Тоже поделюсь phpword.codeplex.com/
    Колонтитулы, нумерация страниц, объединение ячеек(строк) в таблицах ну и прочее…
    • 0
      Ой, извиняюсь. Не подходит это под вашу задачу, тут «страаашное» форматирование текста, просто html скормить не получится
  • +3
    PHPRtfLite хорошая библиотечка для создания правда RTF документа, а не DOC, но создает достаточно исправно *.rtf) sourceforge.net/projects/phprtf/ сам ей пользуюсь, доросла до релиза и многофункциональна, есть документация.
  • +1
    А еще можно в офисе подготовить шаблон документа в формате rtf и расставить по нему переменные какие-нибудь. Например, %var%. Потом просто открыть rtf как строку и заменить переменные на нужные данные.

    Пока только не разобрался можно ли таким способом дополнительные строки в таблицах создавать. Но всякие платежки создаются на-ура!
    • 0
      В моём случае внутрь %var% должны автоматически попасть таблицы, рисунки, и т.д. Т.е., преобразование не такое тривиальное, увы.
  • +1
    У вас конечно всё портят картинки.
    Но вообще, ещё есть «самый джедайский» путь — сделать правильный html без какого-либо мусора в DOM-структуре (вся верстка на CSS). Если мусора в верстке нет, то и Word, и Excel, и куча другого софта нормально открывают такие html файлы и без всякой конвертации в другие форматы.
  • 0
    К сожалению, это не DOC. Если JPG переименовать в PNG, то картинка всё равно откроется, но прозрачность от этого не появится. И у вас то же.
    • +2
      А давайте все проблемы решать так? Windows переименуем в Linux, ie6 переименуем в chrome, icq в jabber, mp3 в ogg и т. д.
      По делу — не кому не советую делать то, что описано в статье. Это конечно хорошо, что всё откроется, но представьте теперь что может делать пользователь с этим файлом дальше? Возьмёт попытается отправить в Google Docs — косяк, или захочет открыть в какой-нибудь специфичной программе. Естественно у него ничего не получится, пользователь будет ругать разработчиков. Из таких идей и получается у нас потом неразбериха и путаница, когда разработчику приходится проверять, а действительно ли пользователь загрузил JPG, или это переименованная BMP…
      Если задача только открыть файл — ну и отправляйте html/mht, всё откроется прекрасно.
      • 0
        Согласен, решение не очень прямое, потому и метод назван «не очень честным». Уточню в статье, что получившийся файл открывается только в семействе продуктов «Microsoft Word».

        Если задача только открыть файл — ну и отправляйте html/mht, всё откроется прекрасно.


        Не всегда есть возможность решить задачу правильно. Требовалось получить именно «файл с раширением doc, открывающийся в Microsoft Word'е». В будущем, без сомнения, задача будет решена прямее, но при отсутствии достаточного количества времени на решение это было лучшее, что пришло мне в голову.
  • +1
    Программа Просмотр на маке отказалась открывать этот файл, а LibreOffice открыл кракозябры исходного текста.
  • 0
    регулярка:
    preg_match_all('@
  • 0
    Dirty hack
  • +1
    Если уж обманывать то я бы из PDF'ка генерировал бы картинку и вставлял бы ее в любой формат XLS, DOC или еще куда :) или просто отдавал бы в виде картинки

    плюсы:
    1. кросс платформенность.
    2. при таком решении не нужны вообще никакие форматы, картинка она и в африке картинка, откроется везде хоть на мобильнике!
    3. правильная печать.

    минусы:
    1 не возможность редактирования.
    2. в зависимости от содержимого страницы, размер итогового файла может быть не приемлимо большим

    Все зависит от целей и задач.
    • 0
      Да, с картинкой неплохая идея, если редактирование не требуется.
      Насколько я понимаю, автор создает некие офлайн-версии сайтов. Но там ведь нужна кликабельность ссылок!
  • +1
    А почему нельзя взять конвертор написаный на другом языке и просто с его помощью уже получить нармальный файл необходимого формата. Есть например очень хороший JODConverter, написаный на джаве.
    Кстате там же есть реализация на питоне.
  • 0
    /<img\b[^>]+src=([\'\"])(.*)\\1/iU
  • 0
    Унылое говно.

    Народ, да забудьте наконец вы этот проприетарный DOC как страшный сон! Иначе мы так ещё десяток лет будем писать всевозможные генераторы (генерирующие при том в большинстве случаев далеко не настоящий DOC, а RTF или чего похуже) и парсеры для него.
    Сейчас стандартами являются OpenDocument, Office Open XML, PDF. Вот и ориентируйтесь.
    • 0
      OO документ не откроется с помощью Microsoft Word'а 2003 например. Если уж требуется кроссплатформенность и универсальность, идеальным вариантом будет RTF или MHT (или HTML с base64 интегрированными картинками).

      Но повторюсь, мне требовалось решить конкретную, жестко заданную в ТЗ задачу: файлы DOC, открываемые Word 2003 и выше.

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