За последние 24 часа нас посетили 22618 программистов и 1193 робота. Сейчас ищут 753 программиста ...

PHP Word Excel. Создание документов по шаблону.

Тема в разделе "Прочие вопросы по PHP", создана пользователем Николай Костылев, 26 фев 2009.

  1. Николай Костылев

    Николай Костылев Активный пользователь

    С нами с:
    26 фев 2009
    Сообщения:
    3
    Симпатии:
    0
    Представляю на всеобщее обозрение результат двухнедельного поиска в интернете соответствующей информации.
    Передо мной встала следующая задача.
    Из корпоративной базы данных информацию выдать в виде отчетов в формате MS Word и MS Excel.
    Ну и конечно отчет выставить в WEB.
    Механизм выборки данных и публикации в WEB описывать не буду. Очень много информации в инете, да и баз данных море.
    А вот формирование документов по шаблону - вещь полезная. Почему то нигде не нашел примеров работы с шаблонами.

    Оговорка. Всё отрабатывалось на Windows Server 2003 + Oracle Apache Server.
    О Linux-платформе в конце статьи.

    Итак.

    Excel.
    С Excel-ом попроще. Готовим красивый файл. Форматируем внутренности, т.е. готовим ячейки куда будем прописывать данные. Теперь пишем PHP:

    <?php
    // Создаем СОМ-объект
    $excel = new COM("Excel.Application") or die("Unable to instanciate excel");
    $excel->Visible = false;
    $excel->DisplayAlerts = false;
    // Выводим версию MS Excel
    echo "I'm using MS Excel {$excel->Version}";
    $excel->Application->Visible = 0;
    $excel->DisplayAlerts = 0;
    # Открытие сущуствующей проформы
    $excel->Workbooks->Open("C:\\temp\\example.xls");
    // Выбираем активный лист и устанавливаем курсов в область ячейки (1, 1)
    $sheet = $excel->Worksheets(1);
    $sheet->activate;
    $cell = $sheet->Cells(1,1);
    $cell->Activate;
    // Записываем в ячейку текст или данные
    $cell->value = 'Test';
    // Сохраняем как новый документ - от туда и выбрасываем в инет
    $excel->Workbooks[1]->SaveAs("c:\\temp\\New.xls");
    // Всё - уходим
    $excel->Quit();
    $excel->Release();
    $excel = Null;
    ?>

    Примеры форматирования ячеек
    $change = $excel -> Selection -> Range("A1");
    $change -> Font -> Bold = true;
    $change -> Font -> Italic = true;
    $change -> Font -> Underline = true;
    $change -> Font -> Name = "Times New Roman";
    $change -> Font -> Size = 12;
    $change -> Font -> ColorIndex = 3;

    Про форматирование в конце статьи дам общие рекомендации.

    Word.
    Ну а теперь самое интересное. Пока до этого докопался ...
    Даже в O'Reilly Programming PHP такого не нашел.

    С Вордовскими шаблонами работа заключается в следующем:
    а) записать данные в закладку (надеюсь не надо объяснять что такое закладка в Ворде) - использовать что-то типа <FIELD1> и методом поиска и замены не рекомендую - может когда-то такая комбинация встретиться;
    б) добавить какое-то количество строк в какую-то таблицу в документе (таблиц может быть много, причем как в колонтитулах так и в самом документе);
    в) записать данные в ячейку таблицы;
    г) нарисовать в ячейке линию (border) - это для подчеркивания итогов в основном;
    д) объединить несколько ячеек таблицы - это из практики формирования судовых документов.
    Поверьте - всё остальное готовится заранее в шаблоне.

    Ну что? Кто нибудь решал такую задачу?

    Итак пишем PHP:
    <?php
    //создаем новый объект COM – word.application
    $word = new COM("word.application") or die("Cannot create Word object");
    $word->Visible = false;
    $word->DisplayAlerts = false;
    // переменная $empty нужна для подстановки
    // неопределенных переменных в методы VBA
    $empty = new VARIANT();
    // Открытие сущуствующей проформы
    $template_file = "C:/temp/doc3.doc";
    $word->Documents->Open($template_file);
    //
    // Пишем в закладку 'test'
    // естественно в шаблоне такая закладка в нужном
    // месте должна быть подготовлена
    $current_date = date("m/d/Y");
    $bookmarkname = "test";
    $objBookmark = $word->ActiveDocument->Bookmarks($bookmarkname);
    $range = $objBookmark->Range;
    $range->Text = $current_date;
    //
    // Пишем в ячейку таблицы
    $objTable = $word->ActiveDocument->Tables(1);
    $objCell = $objTable->Cell(1,1);
    $range = $objCell->Range;
    $range->Text = "cell(1.1)";
    //
    // Рисуем линию ячейки
    $mySelect = $objCell->Select();
    $myBorder = $word->Selection->Borders(-1);
    $myBorder->LineStyle = 1;
    // Оговорка - не используйте Borders(wdBorderTop)
    // Для распознавания Виндовых имен типа wdBorderTop
    // необходимо подгружать библиотеку
    //com_load_typelib('Word.Application');
    // что в свою очередь инициирует на сервере еще один процесс MS Word
    // который потом закончить нельзя
    // числовые эквиваленты таких имен как wdBorderTop
    // легко найти в справке по VBA в самом Ворде
    //
    // Добавляем строки к таблице
    $word->Selection->GoTo('2',$empty,'2',$empty);
    $word->Selection->InsertRowsBelow(1);
    // Самое интересное
    // Объединяем ячейки
    $myTable = $word->ActiveDocument->Tables(2);
    $rangeStart = $myTable->Cell(1,4);
    $myRangeStart = $rangeStart->Range->Start();
    $rangeEnd = $myTable->Cell(2,4);
    $myRangeEnd = $rangeEnd->Range->End();
    $myRange = $word->ActiveDocument->Range($myRangeStart,$myRangeEnd);
    $myRange->Cells->Merge();
    //
    // Сохраняем документ под новым именем
    $new_file = "C:/temp/new3.doc";
    $word->Documents[1]->SaveAs($new_file);
    //
    // Всё - уходим
    $word->Quit();
    $word = null;
    unset($word);
    ?>

    Ну вот и всё. Пользуйтесь.

    Теперь общие замечания или выводы по интерпретации кода VBA в PHP.
    Всё очень просто.
    Оказывается если придерживаться одного правила,
    то переносить код VBA в PHP очень просто.
    ////////////////////////////////////////////////////////////////////////////////////////////
    ПРАВИЛО! Использовать больше двух '->' в одной строке нельзя!!!
    ////////////////////////////////////////////////////////////////////////////////////////////
    Пример. Объединение ячеек.

    Код в VBA:

    Set myTable = ActiveDocument.Tables(1)
    If Not IsNull(rstBl!ORDER_NO) Then
    ActiveDocument.Tables(1).Cell(vCurentRow, 10).Range.Text = rstBl![ORDER_NO].Value
    Set myRange = ActiveDocument.Range(myTable.Cell(vCurentRow, 10).Range.Start, myTable.Cell((vCurentRow + vMaxRows), 10).Range.End)
    myRange.Select
    Selection.Cells.Merge
    End If

    Код PHP:

    // Объединяем ячейки
    $myTable = $word->ActiveDocument->Tables(2);
    $rangeStart = $myTable->Cell(1,4);
    $myRangeStart = $rangeStart->Range->Start();
    $rangeEnd = $myTable->Cell(2,4);
    $myRangeEnd = $rangeEnd->Range->End();
    $myRange = $word->ActiveDocument->Range($myRangeStart,$myRangeEnd);
    $myRange->Cells->Merge();

    Как видите - внимательно превращая VBA-точки в PHP-стрелки, можно писать в PHP как в VBA.

    Теперь про платформу Linux.
    Конечно с таким решением задачи я сам лично не согласен. Т.е. приходится использовать Виндовый сервак. Нужен движок Ворда и Экселя.
    Я выбрал решение следующее. Linux, OpenOffice Calc Writer, JavaScript, PHP.
    Как решу задачу - дополню эту статью.

    Всем успехов.
    Письма благодарности жду по адресу n.kostilev@gmail.com
     
  2. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    это все очень мило. я бы сказал заебись, а теперь повтори тоже самое под никсами?
     
  3. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    1. Excel - есть готовые классы. порыться надо только.
    2. Word. все давно юзают под эту тему RTF формат.
     
  4. akrinel

    akrinel Активный пользователь

    С нами с:
    26 янв 2009
    Сообщения:
    955
    Симпатии:
    1
    Адрес:
    Spb
    440Hz, ответь на вопрос юного падавана плиз:

    Вот есть OpenOffice, у него(если я не ошибаюсь) открытые исходники, если выдрать из него процесс конвертации в .doc формат и сделать отдельной программой, это будет очень суровым решением подобной проблемы?
     
  5. Херня понаписана.
    И ворд, и эксель понимают html разметку в теле документа.
    И никаких ком-обьектов не надо.
     
  6. akrinel, у MSO тоже открытые исходники. И на базе этих данных уже есть готовое решение.
     
  7. armadillo

    armadillo Активный пользователь

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    akrinel вешаешь ОО демоном и в командной строке вызываешь макрос...
     
  8. akrinel

    akrinel Активный пользователь

    С нами с:
    26 янв 2009
    Сообщения:
    955
    Симпатии:
    1
    Адрес:
    Spb
    Спамить людей не люблю, поэтому пишу спасибо вам здесь. Было любопытно почитать.
     
  9. Николай Костылев

    Николай Костылев Активный пользователь

    С нами с:
    26 фев 2009
    Сообщения:
    3
    Симпатии:
    0
    Просьба не выражаться. А с такими "культурными" общаться желания нету. Про классы - да есть, RTF - можно - а в жизни, когда юзверю всё ни почём и он жалуется руководителю - надо в ворде - попробуй не сделать. Про поиск - попробуй найти хоть один пример. Две недели поиска - ничего реального. Тока запись в голый документ.
     
  10. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    с вордом делал только вставку в готовый RTF (RTF открытый формат. можно поизголяться).
    с екселем классов море.

    PHP:
    1.  
    2. <?php
    3.  
    4. require_once('../../texdoc.inc');
    5. require_once('../../trash.inc');
    6.  
    7. require_once('../../../../oops/start.inc');
    8.  
    9.  
    10. function xlsBOF()
    11. {
    12.     echo pack("ssssss", 0x809, 0x8, 0x0, 0x10, 0x0, 0x0);
    13.     return;
    14. }
    15.  
    16. // Excel end of file footer
    17. function xlsEOF()
    18. {
    19.     echo pack("ss", 0x0A, 0x00);
    20.     return;
    21. }
    22.  
    23. // Function to write a Number (double) into Row, Col
    24. function xlsWriteNumber($Row, $Col, $Value)
    25. {
    26.     echo pack("sssss", 0x203, 14, $Row, $Col, 0x0);
    27.     echo pack("d", $Value);
    28.     return;
    29. }
    30.  
    31. // Function to write a label (text) into Row, Col
    32. function xlsWriteLabel($Row, $Col, $Value )
    33. {
    34.     $L = strlen($Value);
    35.     echo pack("ssssss", 0x204, 8 + $L, $Row, $Col, 0x0, $L);
    36.     echo $Value;
    37.     return;
    38. }
    39.  
    40. header ( "Expires: Mon, 1 Apr 1974 05:00:00 GMT" );
    41. header ( "Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT" );
    42. header ( "Cache-Control: no-cache, must-revalidate" );
    43. header ( "Pragma: no-cache" );
    44. header ( "Content-type: application/x-msexcel" );
    45. header ( "Content-Disposition: attachment; filename=order.xls" );
    46. header ( "Content-Description: PHP Generated XLS Data" );
    47.  
    48. xlsBOF();
    49.  
    50. xlsWriteLabel(0,0,"Производитель");
    51. xlsWriteLabel(0,1,"Код");
    52. xlsWriteLabel(0,2,"Описание");
    53. xlsWriteLabel(0,3,"Кол-во");
    54. xlsWriteLabel(0,4,"Цена");
    55. xlsWriteLabel(0,5,"Всего");
    56.  
    57.  
    58. $DATA = $TT->Getdata();
    59.  
    60. $TOT = 0;
    61.  
    62. $num = 1;
    63.  
    64. foreach($DATA as $TK => $TV) {
    65.  
    66.     xlsWriteLabel ($num,0,$TV->brand);
    67.     xlsWriteLabel ($num,1,$TV->code);
    68.     xlsWriteLabel ($num,2,$TV->name);
    69.     xlsWriteNumber($num,3,$TV->cnt);
    70.     xlsWriteLabel ($num,4,(number_format($TV->price/100,2,',','')));
    71.     xlsWriteLabel ($num,5,(number_format($TV->price/100*$TV->cnt,2)).'');
    72.  
    73.     $TOT += $TV->price*$TV->cnt;
    74.  
    75.     $num++;
    76.  
    77. }
    78.  
    79.  
    80. xlsWriteLabel($num,0,'ИТОГО');
    81. xlsWriteLabel($num,5,(number_format($TOT/100,2)).'');
    82.  
    83. xlsEOF();
    84.  
    85. ?>
    мне несколько раз предлагали и требовали сделать ворд из инета - я посылал нафиг. сразу.
     
  11. Николай Костылев

    Николай Костылев Активный пользователь

    С нами с:
    26 фев 2009
    Сообщения:
    3
    Симпатии:
    0
    Если у кого-то есть практический пример решения такой задачи на платформе Linux (Ooo Calc, Ooo Writer, PHP, Java, JavaScript) - просьба поделиться примером.
     
  12. alexey_baranov

    alexey_baranov Активный пользователь

    С нами с:
    3 фев 2009
    Сообщения:
    647
    Симпатии:
    0
    Адрес:
    Сургут
    про ворд не знаю, никогда не приходилось такого делать, а вот про Excel после долгих поисков и разочарований пришел к PHPExcel.

    Логика там проста:
    - готовишь документ в памяти при помощи класса PHPExcel.
    - потом выбираешь один из 5 райтеров, который записывает его в нужный формат или отправляет пользаку- это одной строчкой.

    или если наоборот надо готовый файл обработать, то
    - читаешь файл при помощи одного из ридеров в память в PHPExcel,
    - при помощи методов PHPExcel читаешь его как душе угодно

    В комплекте есть ридеры и райтеры для Ex 2003, Ex 2007, CSV и Acrobat. Я добавил туда свой PostgresReader, который по SQL-запросу заполняет PHPExcel, а потом я его могу любым райтером выгрузить. И написал небольшой моторчик для работы с шаблонами. Пол года уже не знаю проблем. Главное достоинство- один интерфейс для все работает на *никсах.
     
  13. armadillo

    armadillo Активный пользователь

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    объем?
     
  14. alexey_baranov

    alexey_baranov Активный пользователь

    С нами с:
    3 фев 2009
    Сообщения:
    647
    Симпатии:
    0
    Адрес:
    Сургут
    объем чего? памяти или документа

    вот простой примерчик, который иллюстрирует удобства PHPExcel

    это экселевский файл, который выступает в роли шаблона документов
    [​IMG]

    а это документ, который получается после того как над шаблоном немного поработает простенький шаблонизатор
    [​IMG]

    в принципе логика работы шаблонизатора понятна из картинок. Фишка тут в том, что все форматирование документа, то есть как он будет выглядеть в итоге производится в самом экселе (просто редактирую а потом сохраняю обычный экселевский файл). А из PHP только наполнение. К примеру, до PHPExcel я пользовался пировским Excel_spreadsheet_writer. Чтобы добиться там такого же форматирования как на картинке нужно было убить целый день.

    В PHPExcel доступны фильтры, шмильтры, колонтитулы, разметка страницы, положение страницы, метаданные типа автора и описания, блокировки ячеек, именнованые ячейки, которые можно использовать для написания настоящего шаблонизатора на подобии 1С, всплывающие подсказки, графики, картинки любое форматирование текста, фоны, шмоны, границы и все остальное, я уже устал.

    Еще если не нравится Эксель2007, можно сохранить в PDF. для этого нужно только поменять одну строчку, где выбирается райтер
    $writer= PHPExcel_IOFactory($excel, 'Excel2007') заменить на $writer= PHPExcel_IOFactory($excel, 'PDF')
    весь остальной код остается неизменным, потому что документ готовится в памяти и только в последний момент привязывается к какому- то конкретному формату, а все ридеры и райтеры, которые это делают, реализуют один интерфейс и отличаются только реализацией.
     
  15. Саня

    Саня Активный пользователь

    С нами с:
    7 май 2009
    Сообщения:
    14
    Симпатии:
    0
    Адрес:
    Екатеринбург
    Алексей, если не затруднит прицепи свои доработки. Все будут только благодарны. И заранее спасибо
     
  16. alexey_baranov

    alexey_baranov Активный пользователь

    С нами с:
    3 фев 2009
    Сообщения:
    647
    Симпатии:
    0
    Адрес:
    Сургут
    просто исходники устроят? а то у меня тут аврал с переводом на AD и на новый интерфейс, да еще одно старое направление возрождать буду. все разом навалилось.

    если надо примеры и комментарии, то только по-позже, как немного разгребусь.
     
  17. Саня

    Саня Активный пользователь

    С нами с:
    7 май 2009
    Сообщения:
    14
    Симпатии:
    0
    Адрес:
    Екатеринбург
    Хорошо, лучше в качественном виде тогда потом, только не забудь плз, многим будет полезно, мне в том числе :)
     
  18. botton

    botton Активный пользователь

    С нами с:
    9 окт 2008
    Сообщения:
    31
    Симпатии:
    0
    Адрес:
    Камчатский край
    А почему вы не хотите сохранить нужный шаблончик ехел документа в формате хмл с настройками фильтров, цветов, шрифтом и прочего и уже потом из пхп крутить и вертеть его как угодно? чем не вариант... а пользователю передать файл с расширением cls (если не ошибаюсь) и всё будет ровненько)
     
  19. Саня

    Саня Активный пользователь

    С нами с:
    7 май 2009
    Сообщения:
    14
    Симпатии:
    0
    Адрес:
    Екатеринбург
    Под Юниксами я использовал php_writeexcel классы. Удобно.
     
  20. Mark32

    Mark32 Активный пользователь

    С нами с:
    15 июн 2008
    Сообщения:
    539
    Симпатии:
    2
    Тема интересная и важная, но как верно подметил 440Hz, под никсами такая штука не катит.
    У меня такой вопрос. При генерации дока в RTF, например при открытии в ненависном опенофис проблем не будет? Сталкивался с тем (конечно история другая), что при простой вставке html в doc в опенофисе сюрприз - крякозябры. Вот и вопрос - этот опенофис вообще открывает RTF?:) если да, то кодировка не должна изменятся специально под этот опенофис?
     
  21. Mark32

    Mark32 Активный пользователь

    С нами с:
    15 июн 2008
    Сообщения:
    539
    Симпатии:
    2
  22. Mark32

    Mark32 Активный пользователь

    С нами с:
    15 июн 2008
    Сообщения:
    539
    Симпатии:
    2
    440Hz
    кинь пример php2rtf пожалуйста.
     
  23. mistbow

    mistbow Активный пользователь

    С нами с:
    26 июн 2009
    Сообщения:
    1
    Симпатии:
    0
    Тоже появилась задача работы с RTF, XLS, PDF или подобными... лучше используя готовые шаблоны.

    получилось-таки реализовать не на виндах? ;-)