PHP + Word

    image

    Что делать, если нужно создавать много Word файлов одинакового вида, но разного содержания? Например заполнять бланки, квитанции.

    Есть 3 варианта:
    1) использовать одну из библиотек для работы с Word документами
    2) сохранить документ в формате docx, открыть архиватором и внутри мы увидим "\word\document.xml" — чистый xml, с которым можно работать через str_replace (спасибо Enuriru за подсказку)
    3) использовать сторонний сервис, который сделает за меня большую часть работы

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

    Второй варинт хорош и прост, когда мы работаем с word документами в формате .docx, но к сожалению он не поддерживает формат .doc

    В процессе проработки третьего варианта, наткнулся на интересное решение LiveDocx

    Преимущества:
    — файл шаблона можно создать привычным способом через Word
    — представление документа в форматах doc, docx, rtf, pdf
    — не нужно заморачиваться с представлением Word документа через html или XML
    — простота подключения
    — надежность — сервис существует давно и под него даже есть готовая библиотека от Zend
    Недостатки:
    — в бесплатной версии ограничение на 250 генерируемых документов в сутки
    — шаблон нельзя менять (например нельзя сгенерировать таблицу с кол-вом строк, равным количеству элементов в базе)



    Вот так выглядел присланный заказчиком файл.



    Приступим.

    1) Для начала нужно разметить шаблон. Открываем файл и размечаем нужные поля специальными вордовскими переменными mergeField, вот как это делается в Word 2007:
    — Вставка => Экспресс блоки => Поле
    Появляется окно.

    Выбираем тип поля: MergeField => В имени поле пишем имя переменной => Жмем ОК

    Таким образом размечаем весь документ, получится нечто похожее на:



    Сохраняем документ, кладём в директорию нашего приложения.

    2) Поработаем с кодом

    Регистрируемся на сайте LiveDocx, получаем логин и пароль.

    Если вы счастливый/несчастливый обладатель Zend Framework, то всё довольно просто, поддержка LiveDocx идёт прямо из коробки:

        // Создаём объект для работы с сервисом и передаём свой логин и пароль   
        $livDoc = new Zend_Service_LiveDocx_MailMerge(array(
            'username' => 'yourusername',
            'password' => 'yourpassword'
       ));
    
        // Передаём значения для наших mergeFields в Word шаблоне
        $livDoc->assign('orderNum','Номер заказа');
        $livDoc->assign('orderDay',date('d', 'Дата заказа'));
    
        // Задаём путь к файлу шаблона и передаём его объекту
        $documentPath = 'contract_bid_for_customer.doc';
        $livDoc->setLocalTemplate($documentPath);
    
        // заполняем документ с помощью сервиса 
        $livDoc->createDocument();
        $doc = $livDoc->retrieveDocument('doc');
    
        // отдаём готовый документ для скачки 
        header("Cache-Control: public");
        header("Content-Description: File Transfer");
        $fileName = "Документ.doc";
        header("Content-Disposition: attachment; filename=$fileName");
        header("Content-Type: application/msword");
        header("Content-Transfer-Encoding: binary");
        echo $doc;
        die;


    Если же вы предпочитаете plain php, то голодными тоже не останетесь.

    Для работы понадобится включенный модуль soap, в большинстве случаев он включен по умолчанию, но проверьте как обстоит дело у вас:
    phpinfo();
    Раздел SOAP
    Вы должны увидеть Soap client enabled и Soap server enabled.

        // Выключаем WSDL кэширование
        ini_set ('soap.wsdl_cache_enabled', 0);
    
        // Выставляем временную зону
        date_default_timezone_set('Europe/Moscow');
    
        // Создаём экземпляр объекта Soap и передаём ему свои учетные данные
        $soap = new SoapClient('https://api.livedocx.com/1.2/mailmerge.asmx?WSDL');
        $soap->LogIn(
            array(
                'username' => 'yourusername',
                'password' => 'yourpassword'
            )
        );
    
        // Путь к файлу шаблона
        $data = file_get_contents('contract_bid_for_customer.doc');
    
        // Установка расширения файла .doc и параметров кодирования
        $soap->SetLocalTemplate(
            array(
                'template' => base64_encode($data),
                'format'   => 'doc'
            )
        );
    
        // Задаём значения переменным
        $fieldValues = array (
            'orderNum'  => 'Номер заказа',
            'orderDay'  => 'Дата заказа'
        );
    
        // Эта хитрая функция преобразует массив c переменными в то что понимает SOAP
        function assocArrayToArrayOfArrayOfString ($assoc)
        {
            $arrayKeys   = array_keys($assoc);
            $arrayValues = array_values($assoc);
            return array ($arrayKeys, $arrayValues);
        }
    
        // Передаём переменные в наш LiveDocx объект
         $soap->SetFieldValues(
            array (
                'fieldValues' => assocArrayToArrayOfArrayOfString($fieldValues)
            )
        );
    
        // Формируем документ
        $soap->CreateDocument();
        $result = $soap->RetrieveDocument(
            array(
                'format' => 'doc'
            )
        );
        $doc = base64_decode($result->RetrieveDocumentResult);
        
        // Разрываем сессию с SOAP 
        $soap->LogOut();
    
        // Отдаём вордовский файл  
        header("Cache-Control: public");
        header("Content-Description: File Transfer");
        $fileName = "Документ.doc";
        header("Content-Disposition: attachment; filename=$fileName");
        header("Content-Type: application/msword");
        header("Content-Transfer-Encoding: binary");
        echo $doc;
        die;


    3) На выходе мы имеем вот такой файл



    LiveDocx поддерживает и другие форматы: DOCX, RTF and PDF.

    Подробнее вы можете почитать здесь:
    livedocx.com
    phplivedocx.org/articles/getting-started-with-phplivedocx
    blog.zendguru.com/2010/02/13/creating-word-processing-document-using-zend_service_livedocx
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 59

      +9
      Если нужно заменить строки текста (и не надо, например, собирать документ по кускам или вставлять картинки), то можно сделать проще.
      Создаем любой сложности документ документ, так же размечаем — например {address}, {phone} и сохраняем в формате docx.
      Потом открываем блокнотом и… видим обычный xml. В котором можем заменить данные простым str_replace.
        +2
        > «Потом открываем блокнотом и… видим обычный xml»
        Только сначала надо разархивировать, а потом уже открывать в блокноте.
          +1
          > «Потом открываем блокнотом и…»
          и уж точно не надо ипользовать для этих целей блокнот…
            0
            Не нужно ничего разархивировать.

            1. Из Word'a шаблон документа сохраняем как file.xml
            2. $template = file_get_contents(«file.xml»);
            3. $template = str_replace("{phone}", "+7999887766", $template);
            4. header(«Content-type: application/msword»);
            5. echo $template.

            Profit.
            0
            Если сохранить в формате docx, то в блокноте откроются кракозябы. Обычный XML мы получим только если сохраним документ в формате XML-документ Word. А это уже потребует дополнительноей конвертации в XML и обратно в doc.
            Ваш вариант тоже рабочий, но не настолько прост как вы описали.
              +1
              Забыл про внутренню структуру совсем. Но в любом случае архив распаковывается, и в \word\document.xml все читаемо и без крякозябр.
                +1
                Спасибо за вариант, укажу о нём в статье
                  0
                  а с Doc такой вариант работает?
                    +1
                    К сожалению нет.
                      0
                      Если приспичит, можно конвертором каким-нибудь гонять doc -> docx -> doc.
                  –1
                  так же размечаем — например {address}, {phone} и сохраняем в формате docx.

                  Ага и получаем косяков пачку. MS World вставляет дополнительную разметку в файл для что называется более простого сравнивания. В результате чего обычными xml средствами изменять файлы не возможно. Придется менять на xxxadressxxx, xxxphonexxx. их не разбивает.
                  +5
                  Не понимаю чем вам вариант передачи HTML+php в rtf не устраевает? Делаем быстро шаблон через какой-нить визуальный редактор HTML, вставляем переменные PHP и генерируем RTF. www.cyberforum.ru/php/thread68701.html вот простой пример.

                  Почему именно docx? у заказчика проблемы с RTF?
                    +1
                    Поддерживаю. В свое время решил подобную задачу именно при помощи RTF. Оригинальные шаблоны заказчика сохранил в RTF, проставил «макросы» в нужных местах, а в PHP читал файл и заменял макросы на значения. Всё просто, и у заказчика никаких проблем с редактированием шаблонов не возникало — в том же MS Word всё аналогично редактируется и прозрачно для пользователя.
                    0
                    Поясните, с какими из задач не справился (бы) phpword? (хотя судя по документу это большая таблица и ее следует делать в екселе, для чего есть и phpexcel).
                      0
                      На сколько мне известно PHPWord не может работать с форматом doc. А данный сервис может))
                      0
                      Я подобное делал с OpenOffice. Сохранял в fodt (да, это тоже xml) и менял. Сжатия там нет, так что все достаточно просто.
                      Единственный косяк — OpenOffice почему-то любит разбивать слова на куски. Лечится так: открываем текстовым редактором свой шаблон, находим свои %var% (или как там) и вручную приводим к человеческому виду.
                        0
                        А в чём сложность любой документ сохранить как html или mht и редактировать как HTML-шаблон обычный?
                          0
                          Как правило, нужно чтобы на выходе был тот же *.doc или *.docx
                            0
                            Достаточно написать в конце .doc, и voi la, перед нами .doc! Или вам зачем-то нужен именно документ внутреннего формата Word?
                              0
                              В таком случае при открытии оно выдает предупреждене что файл кагбы не совсем подлинного формата, и как правило юзеров очень напрягает, не опытным блондам на ум приходит только одно — «Ахтунг! это ВИРУС!» и начинается спамилка негатива во всех направлениях!
                                0
                                Основная задача, конечно же, чтобы загруженный документ корректно и без лишних вопросов открывался в M$ Word ;-)
                              0
                              Тег не там закрыл :(
                                0
                                совсем не то по ссылке
                                0
                                XML содержится не только в docx. Я работал с doc'ами содержащими XML. Они поддерживаются офисом начиная с 2003.
                                  +1
                                  Справедливости ради есть ещё один вариант, очевидный, по-моему, для тех кто работает под для винды:
                                  0) Использовать OLE automation, жаль под никсами это затруднительно, да и офис надо покупать.

                                  Можно этот вариант развить и написать свой сервис, лишенный недостатков LiveDocx и делающий ровно то, что вам нужно без особых ограничений.

                                    0
                                    Непереносимо и неуниверсально. Ниже написал — самый оптимальный вариант работы с обычными документами — это программная обработка исключительно ODF, а дальше, если уж никак без доков не обойтись, конвертация ODF в doc через OpenOffice PyUNO.
                                    +1
                                    Я для генерации xls использую XML + XSLT, где на выходе получается SpreadsheetML
                                    Легко и нет зависимости от сторонних сервисов
                                      +1
                                      Есть еще вариант использовать UNO + OpenOffice.
                                      года два назад был момент в жизни когда хотелось с ним разобраться но все закончилось после установки и запуска Офиса и модуля для ПХП, и тестового запуска из примеров :)
                                      Вообще вещь очень интересная и на мой взгляд крайне функциональная, но в твоем случае ВОЗМОЖНО из пушки по воробям
                                        +1
                                        Не, не из пушки. Если использовать UNO только для конвертации, а программно обрабатывать ODF — самое то. ИМХО, оптимальнейшей решение в 99.9% случаев когда необходимо программно обрабатывать документы. Прямая работа с форматами майкрософта это вообще изврат, который поискать ещё надо.
                                        0
                                        Хм. И эта система видит интернет? А как там в Питере с законодательством на этот счет?
                                          +2
                                          OpenSource — сообщество не могло не высказаться по этому поводу. Позвольте добавить еще вариант:

                                          4) Открытая LGPL библиотека PHPWord для создания docx-документов phpword.codeplex.com/ (имеется также PHPExcel и PHPPowerPoint)
                                            +2
                                            Добавлю, что далее можно использовать команднострочные утилиты конвертации. К примеру, открытый AbiWord умеет преобразовывать форматы: rtf, doc, docx и др.
                                            0
                                            По-моему, самый простой вариант — использовать rtf. В нем расставляем переменные и потом через str_replace меняем их и перезаписываем файл. Правда, хз как с таблицами поступать, когда новые строки надо :)
                                              0
                                              каменный век какой-то: RTF, маркеры, замена…
                                              0
                                              А штатными средствами office.microsoft.com/ru-ru/word-help/HA001034920.aspx не проще ли?
                                                +1
                                                У меня тестовое задание было на собеседовании по теме XSLT — нужно было из размеченного DOCX извлекать данные. В принципе оказалось очень просто. Внутри обцыный XML (правда структура довольно сложная, но нам все тонкости не нужно знать.

                                                2 последовательных трансформации и готово… Строк 100 XSLT кода вышло.

                                                Мой совет — забейте на этот жуткий бинарный doc пятнадцатилетней давности и используйте docx или rtf если odt не можете. И используйте XPath для навигации по XML а не str_replace. И, между прочим, docx поддерживает кастомные теги разметки для выделения спец. полей.

                                                Использовать сторонний сервис для заполнения шаблонов документов — это как-то слишком ненадежно.
                                                  +1
                                                  использую вот это решение в 1 килобайт
                                                  www.phpclasses.org/package/2763-PHP-Convert-HTML-into-Microsoft-Word-documents.html

                                                  открывается HTML-ный файлик ворда .doc
                                                  вообще без проблем.
                                                    +1
                                                    Зачем же костылить-то так? Есть нормальный odf, который парсится и создаётся на ура в любом виде из практически любого скриптового языка, в том числе PHP. Соответственно работаем всегда с ODF, а если, упаси Боже, кому-то нужны doc, то используем возможности OpenOffice по автоматической конвертации между форматами (PyUNO). Работать с docx или тем более с doc — это извращение, абсолютно бесполезное к тому же. Не надо себе трудностей создавать. Вся обработка электронных документов исключительно через ODF — это самый оптимальный способ, если у вас конечно не .NET приложение. Неоднокрано проверено.

                                                    +3
                                                    1) использовать одну из библиотек для работы с Word документами

                                                    А таких много хоть сколько-нибудь годных? И они, наверное, бесплатные?

                                                    2) сохранить документ в формате docx, открыть архиватором и внутри мы увидим "\word\document.xml" — чистый xml, с которым можно работать через str_replace

                                                    Это п#здец, вообще говоря. Потом ещё удивляются, откуда столько «быдлокодеров» и «говнокода» берётся. Впрочем, ничего личного, может человек просто PHP изучил полгода назад, а до того программирование в глаза не видел, поэтому придумывает способы в рамках своих знаний.

                                                    Там где есть XML, не важно какой, есть и XSLT.
                                                    Преобразования/шаблонизация вордовских документов были ещё в Office 2003 доступны. Делаем шаблон документа, сохраняем как XML документ, расставляем элементы, привязываем к DOM. Потом остаётся только подгружать XML с подставляемыми данными и получать конечный документ.
                                                    Примеры:
                                                    XML/XSLT Word Report Generator
                                                    Generating Word documents using XSLT
                                                    Литература: Office 2003 XML for Power Users (скачать).
                                                      0
                                                      ну да, а что это даст, когда надо структуру документа изменить? Строчку в таблицу, например, добавить? Хотя это возможно и получится, а вот что-то более комплексное — нет. Не надо создавать себе трудностей и работать с форматами, для которых не существует адекватных библиотек парсинга и модификации. XSLT конечно хорошо, но, например, на Perl OpenOffice::OODoc явно получше для любых операций с документами. На PHP не помню что там есть для работы с ODF, но явно что-то должно быть. Парсить XML когда можно работать напрямую с данными и структурами в документе через какую-либо библиотеку — это всё же изврат.
                                                        +2
                                                        ну да, а что это даст, когда надо структуру документа изменить? Строчку в таблицу, например, добавить? Хотя это возможно и получится, а вот что-то более комплексное — нет.

                                                        n штук исходных XML + XSLT преобразование → новый XML, в т.ч. совершенно другой структуры
                                                        Делайте настолько комплексное, насколько хватит ваших знаний XML, XPath и XSL. Правда, исходя из вашего комментария, можно предположить, что их пока нет вообще.

                                                        Полистал описание OpenOffice::OODoc — как и ожидалось, просто некоторая специализированная надстройка над DOM и XPath для схемы ODF. Для Java в таком случае тоже есть нечто.
                                                        Если нужны именно преобразования одного документа (шаблона) в другой — не очень понимаю, чем для этого клиентские библиотеки (ориентированные прежде всего на создание документов и простейшие операции с уже существующими) подходят лучше, нежели XSLT.

                                                        На PHP не помню что там есть для работы с ODF, но явно что-то должно быть.

                                                        У него самого точно ничего нет, а сторонние библиотеки к чему угодно можно прикрутить. Только нужен ли в таком случае именно PHP? ;)

                                                        Парсить XML когда можно работать напрямую с данными и структурами в документе через какую-либо библиотеку — это всё же изврат.

                                                        Вообще говоря, «парсить» никто и не предлагал… Транформация ≠ парсинг. За вас всё отпарсит уже существующий код библиотек.
                                                          0
                                                          Просто XSLT предполагает то, что вы разбираетесь во внутренней структуре формата. А штуки по типу OODoc позволяют вам вообще не задумываться о том, что там внутри файла с расширение .odt находится, а работать только с данными, используя стандартную объектную модель. Спецификация ODF 1.2 не такая и маленькая, и ИМХО это не тот случай, когда надо применять XSLT, особенно когда есть полнофункциональные альтернативы. И нет, я знаю, что такое XSLT, но плотно никогда не работал, да, как-то не было случая помучить что-то XML'ное.
                                                            0
                                                            XSLT — это какое-это мегаофигенное, но не по-детски сложное говно. (ИМХО!)

                                                            И для генерации документов подходит так же, как крупнокалиберная корабельная пушка для прицельной стрельбы по воробьям.
                                                              0
                                                              ничуть не сложнее многих других способов
                                                              несколько хороших примеров + неделя практики = горы свернёте :)
                                                              но, конечно, без знаний XML и XPath никуда
                                                        0
                                                        >Это п#здец, вообще говоря. Потом ещё удивляются, откуда столько «быдлокодеров» и «говнокода» берётся

                                                        Откуда столько ненависти?

                                                        Вы забываете про целесообразность. Какие xslt? Какие шаблонизации? Еще книги какие-то читать?
                                                        Зачем это все, когда в документе нужно вставить фио, и есть элементарное решение в несколько строк кода?
                                                          0
                                                          если вам надо всего-лишь вставить ФИО и т.п., то это делается посредством «слияния»
                                                          Слияние документов Word
                                                          и не надо велосипед изобретать
                                                            0
                                                            Отдельный человек, который будет склеивать документы, тыкая по менюшкам в ворде, не нужен. Нужен скрипт ;)
                                                              0
                                                              освойте макросы и VBA
                                                              это всяко «ближе к телу», чем какие-то убожества по замене «А» на «Б» с помощью PHP
                                                                0
                                                                Оно работает на *nix серверах?
                                                                  0
                                                                  *NIX+MS Word вообще странный выбор
                                                                  изобретать столько костылей из-за проприетарного формата, вместо того чтобы генерить HTML или PDF
                                                                  но к чему вопрос? данный топик такую задачу не ставил, например
                                                                  а в умелых руках всё работает
                                                                    0
                                                                    Часто нужны именно word-документы, к сожалению. Я знаю по опыту.

                                                                    А еще, я искренне недоумеваю (серьезно, искренне!), почему вы (и еще некоторые люди тут) советуют не изобретать велосипед, а какие-то библиотеки, специализированные инструменты, когда программист может решить задачу за пару минут на любом известном ему серверном языке программирования :)

                                                                    В данном случае, как раз, все эти инструменты — преусловутые велосипеды, которые нужно еще купить/привезти (установить, изучить), когда можно просто пройти десяток метров.

                                                                    Посмотрите вот на этот комментарий. Даже для замены пары десятков значений код мало будет отличаться. Элементарное решение.

                                                                    Нет, конечно, если программист умеет работать с преусловутыми «специализированными инструментами», можно воспользоваться ими, но мне почему-то кажется, что в этом случае придется затратить больше времени и тащить за проектом лишние библиотеки.
                                                        0
                                                        Если не обязательно на сервере, то: office.microsoft.com/ru-ru/word-help/HA001034920.aspx
                                                          0
                                                          А нет решения для замены {Variables} для python? rtf, doc, docx без разницы. Парсить xml здорово, но хотелось бы попроще. Нужно для интеграции в arcgis, а там только python.
                                                            0
                                                            open("myfile.xml", "r").read().replace("{phone}", "2-12-85-06")?
                                                            0
                                                            assocArrayToArrayOfArrayOfString
                                                            

                                                            Воу, воу, воу — полегче!

                                                            Only users with full accounts can post comments. Log in, please.