PHP

индекс
206,80

Простейшая генерация odt файла из существующего — работа над ошибками

Всем свойственно ошибаться… но я считаю, что ошибки стоит исправлять.

Недавно обновился OpenOffice до версии 3.2 и я заметил, что генерируемые документы odt по-моему хабратопику открываются в нём с ошибками. Первой мыслью было то, что произошли какие-то кардинальные изменения в спецификации ODF 1.2. Но всё оказалось проще:
Спецификация ODF требует в начале zip архива несжатый файл mimetype. OpenOffice до версии 3.2 распознавал odt файлы и не соблюдающие этого требования, в новой же версии это обязательно.

Все испортил класс ZipArchive.



Дело все в том, что ziparchive после метода close() записывает файлы не в том порядке, в каком мы их добавили, и уж тем более не умеет нормально регулировать уровень сжатия файлов.

Пришлось искать альтернативу. Как альтернатива был взят файл zip.lib.php из phpmyadmin и совсем слегка «допилен» — буквально затронул две строчки, добавив степень сжатия для файлов. (поправленный файл можно найти в архиве в конце статьи)

Вот эти строки:

public function addFile($data, $name, $time = 0, $compresslevel=-1)

    $zdata  = gzcompress($data,$compresslevel);  // compress data 


Вся наша работа над ошибками пройдёт только в файле download.php:

<?php

if (!file_exists('doc.list')) die;

//текущее время - время создания файлов
$time = time();

//файл, который будем отдавать
$outname = 'zayavlenie.odt';

// класс для создания zip архива
require_once 'zip.lib.php';

//создаем новый архив
$zip = new zipfile();

//проходимся по структуре нашего архива
$files = file('doc.list') ;
foreach ($files as $filename) {
  $filename = trim($filename);

  //если файл content.xml, то проводим в нем подстановку пользовательских полей
  if ($filename == "content.xml") {

    //значения полей
    $vars = array(
      'ФИО'=>'Иванова И.И.',
      'Дата'=>date('d.m.Y'),
      'Планета'=>'Юпитер'
    );

    //создаем объект simplexml
    $xml = new SimpleXMLElement(file_get_contents('doc/' . $filename));

    //получаем заранее нужные namespace
    $ns = $xml->getNamespaces(true);

    //проверяем есть ли в файле пользовательские поля
    $fields = $xml->children($ns["office"])->body->text->children($ns["text"])->{"user-field-decls"};
    if ($fields) {
      //если есть, пробегаемся по ним и заменяем их атрибут string-value на новый
      foreach ($fields->children($ns["text"]) as $field) {

        if (isset($vars[(string) $field->attributes($ns["text"])->name])) {
          $field->attributes($ns["office"])->{"string-value"} = $vars[(string) $field->attributes($ns["text"])->name];
        }
      }

    }
    $data = $xml->asXML();

  }
  else {
    $data = file_get_contents('doc/' . $filename);
  }

  //Добавляем файл/директрию в архив
  //Если файл mimetype, то его нельзя сжимать - уровень компрессии 0
  $zip->addFile($data, $filename, $time, $filename == 'mimetype' ? 0 : - 1);

}

//очищаем буфер и выдаем файл
ob_clean();

header('Content-Disposition: attachment; filename="' . $outname . '"');
header('Content-type: application/odt');
print $zip->file();

?>

* This source code was highlighted with Source Code Highlighter.


Вот собственно и все. Надеюсь кому-нибудь данная работа над ошибками пригодится!

Скачать исходник целиком
_________
Текст подготовлен в ХабраРедакторе
+11
12 марта 2010, 11:53
36

комментарии (3)

–1
Thomas #
ИМХО код zip.lib.php был лишним.

В целом полезно, но думаю не каждый заказчик знает про .odt формат.
0
Irker #
насчет zip.lib.php согласен, немного упростил статью.
–1
Goodkat #
Для ворда можно отдавать docx, rtf или HTML-файл с расширением .doc

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