В статье Не очень честная генерация DOC файлов на PHP был описан описан способ генерации DOC файла при помощи генерации MHT (MIME HTML) используя стороннюю библиотеку. Сегодня я расскажу о своей собственной генерации в этот формат. Плюсы моего способа следующие:
1) В OpenOffice читаемый текст и картинки.
2) В Word открывается файл в электронном виде, а не на весь экран.
3) Наш скрипт будет принимать HTML и отдавать сразу DOC файл на скачивание.
Ко всему прочему, вы поймете как преобразовать голый HTML в MHT собственными руками. Ошибки если и будут, то копаться в коде будет проще.
Начнем с функции, которая отдаст DOC файл на скачивание и будет работать во всех браузерах и со всеми протоколами(у меня с этим были проблемы):
/* Отсылаем файл на закачку */
function send_download($filename, $charset = 'cp1251')
{
header ($_SERVER["SERVER_PROTOCOL"] . ' 200 OK');
if (ereg('Opera(/| )([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
$UserBrowser = "Opera";
elseif (ereg('MSIE ([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
$UserBrowser = "IE";
else
$UserBrowser = '';
$mime_type = ($UserBrowser == 'IE' || $UserBrowser == 'Opera') ?
'application/octetstream' : 'application/octet-stream';
header("Content-Type: application/msword; charset=".$charset);
$ua = (isset($_SERVER['HTTP_USER_AGENT']))?$_SERVER['HTTP_USER_AGENT']:'';
$isMSIE = preg_match('@MSIE ([0-9].[0-9]{1,2})@', $ua);
if ($isMSIE)
{
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
}
else
{
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Pragma: no-cache');
}
}
Теперь перейдем к самой генерации DOC файла, для этого создадим форму, которая будет отправлять нам html с картинками, картинки лежат на нашем сайте.
<form action="getFile.php" method="POST">
<textarea name="text" rows ="10" cols="60"><?=str_replace(array('<', '>'), array('<', '>'), 'Это всего лишь <b>пример</b><img src="/images/logo.gif">');?></textarea>
<input type="submit" value="HTML TO DOC"/>
</form>
Преобразовывать изображения мы будем при помощи base64, создадим функцию — callback для этого:
/* Преобразование изображений */
function prepareImage($matches) {
global $IMAGES, $IMAGE_NAMES, $IMAGE_COUNT,$gldir,$SITE;
$imgfile = $_SERVER['DOCUMENT_ROOT'].'/'.$matches[2];
$imgbinary = fread(fopen($imgfile, "r"), filesize($imgfile));
$url = $SITE.$matches[1];
$data = chunk_split(base64_encode($imgbinary));
$IMAGE_COUNT++;
$ext = substr($matches[2], strpos($matches[2], '.') + 1, strlen($matches[2]));
$imgName = 'images'.$IMAGE_COUNT.'.'.$ext;
$IMAGES .= '
--doc_file_part_na_habrahabr
Content-Location: '.$gldir.'images/'.$imgName.'
Content-Transfer-Encoding: base64
Content-Type: image/'.$ext.'
'.$data.'
';
$pr1 = $matches[1];
$pr2 = $matches[3];
$IMAGE_NAMES .= '
<o:File HRef=3D"'.$imgName.'"/>';
return '<v:imagedata src=3D"'.$gldir.'images/'.$imgName.'" o:href=3D"'.$url.'"/></v:shape><![endif]--><![if !vml]><span style=3D"mso-ignore:vglayout"><img border=3D0 src=3D"'$gldir'.images/'.$imgName.'" alt=3DHaut v:shapes=3D"_x0000_i1057" '.$p1.' '.$pr2.'></span><![endif]>';
}
Сразу извиняюсь, за то, что код написан в функциях, а все данные храниться в глобальных переменных. Код был написан, когда я ещё только начинал писать на PHP. Теперь создадим функцию, которая поможет нам с текстом, чтобы он хотя бы читался и в OpenOffice.
/* Преобразование текста */
function xml_entities($text, $charset = 'cp1251'){
global $SITE;
/* Ищем изображения и добавляем их в файл */
$text = preg_replace_callback(
'/<img([a-zA-Z0-9:/.-?=_&s;"]*)src="([А-Яа-яa-zA-Zds:/.-?=_&]*)"([a-zA-Z0-9:/.-?=_&s;"]*)>/',
"prepareImage",
$text);
/* Преобразовываем ссылки относительные на глобальные */
$text = preg_replace('/href="/','href=3D"'.$SITE, $text);
/* Все пробелы должны быть закодированы как =3D, 3D - это шестнадцатиричный код пробела */
$text = preg_replace('/=(?=[^3])/','=3D',$text);
$text = preg_replace('/s?=s?"/','=3D"',$text);
/* Кодируем текст, чтобы читался в OpenOffice */
$text = htmlentities($text, null, $charset);
$fi = array(""","&","'","<",">");
$re = array('"',"&","'","<",">");
return str_replace($fi, $re, $text);
}
Теперь сам код скрипта:
global $SITE;
/* Имя сайта, на который будут вести ссылки и с которого нужно будет загрузить изображения */
$SITE = 'http://pihpi.ru';
function htmlToDoc($name, $html, $charset = 'cp1251') {
$nameFile = $name.'.doc';
global $IMAGES, $IMAGE_NAMES, $IMAGE_COUNT, $gldir, $SITE;
$IMAGE_COUNT = 0;
$IMAGE_NAMES = '';
$IMAGES = '';
$gldir = 'file:///C:/AF22D505/';
/* doc_file_part_na_habrahabr - это название части файла может быть любым. Для того, чтобы узнать подробнее, читайте про MIME или как отправить по почте файл с картинками, аналогия очевидна. */
$head = 'MIME-Version: 1.0
Content-Type: multipart/related; boundary="doc_file_part_na_habrahabr"
--doc_file_part_na_habrahabr
Content-Location: '.$gldir.$nameFile.'
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset="windows-1251"
<html xmlns:o=3D"urn:schemas-microsoft-com:office:office"
xmlns:w=3D"urn:schemas-microsoft-com:office:word"
xmlns=3D"http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv=3DContent-Type content=3D"text/html; charset=3Dwindows-1251">
<meta name=3DProgId content=3DWord.Document>
<meta name=3DGenerator content=3D"Microsoft Word 11">
<meta name=3DOriginator content=3D"Microsoft Word 11">
<link rel=3DFile-List href=3D"filelist.xml">
<!--[if gte mso 9]><xml>
<w:WordDocument>
<w:View>Print</w:View>
<w:GrammarState>Clean</w:GrammarState>
<w:ValidateAgainstSchemas/>
<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
<w:IgnoreMixedContent>false</w:IgnoreMixedContent>
<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
<w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
</w:WordDocument>
</xml><![endif]--><!--[if gte mso 9]><xml>
<w:LatentStyles DefLockedState=3D"false" LatentStyleCount=3D"156">
</w:LatentStyles>
</xml><![endif]-->
<style>
<!--
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-parent:"";
margin:0cm;
margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:12.0pt;
font-family:"Tahoma";
mso-fareast-font-family:"Tahoma";}
@page Section1
{size:595.3pt 841.9pt;
margin:18.0pt 19.3pt 18.0pt 18.0pt;
mso-header-margin:35.4pt;
mso-footer-margin:35.4pt;
mso-paper-source:0;}
div.Section1
{page:Section1;}
-->
</style>
<!--[if gte mso 10]>
<style>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:"41E43144B44743D43044F 44243043143B=
0438446430";
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:"Tahoma";
mso-ansi-language:#0400;
mso-fareast-language:#0400;
mso-bidi-language:#0400;
width:100%;
}
td.br1{
border:1px solid black;
}
</style>
<![endif]-->
</head>
<body>';
$end = '
</body>
</html>
';
$html = xml_entities($html, $charset);
/* Мы получили все картинки, теперь сгенерим xml файл с ними */
$fileList = '
--doc_file_part_na_habrahabr
Content-Location: '.$gldir.'filelist.xml
Content-Transfer-Encoding: quoted-printable
Content-Type: text/xml; charset="utf-8"
<xml xmlns:o=3D"urn:schemas-microsoft-com:office:office">
<o:MainFile HRef=3D"../'.$nameFile.'"/>
'.$IMAGE_NAMES.'
<o:File HRef=3D"filelist.xml"/>
</xml>
';
$content = $head.$html.$end.$IMAGES.$fileList.'--doc_file_part_na_habrahabr--';
send_download($nameFile);
echo $content;
exit();
}
Ну и наконец само преобразование HTML в DOC в действии:
if (isset($_POST['text'])) {
htmlToDoc('article', str_replace('\', '', $_POST['text']));
}
Пример работы скрипта, вы можете посмотреть по следующей ссылке:
Несколько слов о Libre Office:
Текст выводиться, а вот картинки увы. Почитав про Mime пытался сделать через Content-ID, не вышло. Либо я чего-то не знаю, либо Libre Office вообще не хочет поддерживать MIME HTML.
Автор: stagnantice
А есть более новые версии данного скрипта?
А то чёто не работает