HTML с картинками в DOC на PHP собственными руками

в 6:21, , рубрики: doc, mht, php, картинки, Песочница, метки: , , ,

В статье Не очень честная генерация 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']));
}

Пример работы скрипта, вы можете посмотреть по следующей ссылке:

http://pihpi.ru/getFile.php

Несколько слов о Libre Office:

Текст выводиться, а вот картинки увы. Почитав про Mime пытался сделать через Content-ID, не вышло. Либо я чего-то не знаю, либо Libre Office вообще не хочет поддерживать MIME HTML.

Автор: stagnantice

Источник

  1. Boris:

    А есть более новые версии данного скрипта?
    А то чёто не работает

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js