Работаем с OpenDocument из Perl::Журнал СА 12.2007
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Подписка
Архив номеров
Где купить
Наука и технологии
Авторам
Рекламодателям
Контакты
   

  Опросы
  Статьи

Дата-центры  

Дата-центры: есть ли опасность утечки данных?

Российские компании уже несколько лет испытывают дефицит вычислительных мощностей. Рост числа проектов,

 Читать далее...

Книжная полка  

Защиты много не бывает

Среди книжных новинок издательства «БХВ» есть несколько изданий, посвященных методам социальной инженерии

 Читать далее...

Событие  

В банке рассола ждет сисадмина с полей фрактал-кукумбер

Читайте впечатления о слете ДСА 2024, рассказанные волонтером и участником слета

 Читать далее...

Организация бесперебойной работы  

Бесперебойная работа ИТ-инфраструктуры в режиме 24/7 Как обеспечить ее в нынешних условиях?

Год назад ИТ-компания «Крок» провела исследование «Ключевые тренды сервисного рынка 2023». Результаты

 Читать далее...

Книжная полка  

Читайте и познавайте мир технологий!

Издательство «БХВ» продолжает радовать выпуском интересных и полезных, к тому же прекрасно

 Читать далее...

СУБД PostgreSQL  

СУБД Postgres Pro

Сертификация по новым требованиям ФСТЭК и роль администратора без доступа к данным

 Читать далее...

Критическая инфраструктура  

КИИ для оператора связи. Готовы ли компании к повышению уровня кибербезопасности?

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

 Читать далее...

Архитектура ПО  

Архитектурные метрики. Качество архитектуры и способность системы к эволюционированию

Обычно соответствие программного продукта требованиям мы проверяем через скоуп вполне себе понятных

 Читать далее...

Как хорошо вы это знаете  

Что вам известно о разработках компании ARinteg?

Компания ARinteg (ООО «АРинтег») – системный интегратор на российском рынке ИБ –

 Читать далее...

Графические редакторы  

Рисование абстрактных гор в стиле Paper Cut

Векторный графический редактор Inkscape – яркий представитель той прослойки open source, с

 Читать далее...

День сисадмина  

Учите матчасть! Или как стать системным администратором

Лето – время не только отпусков, но и хорошая возможность определиться с профессией

 Читать далее...

День сисадмина  

Живой айтишник – это всегда движение. Остановка смерти подобна

Наши авторы рассказывают о своем опыте и дают советы начинающим системным администраторам.

 Читать далее...

Виртуализация  

Рынок решений для виртуализации

По данным «Обзора российского рынка инфраструктурного ПО и перспектив его развития», сделанного

 Читать далее...

Книжная полка  

Как стать креативным и востребованным

Издательский дом «Питер» предлагает новинки компьютерной литературы, а также книги по бизнесу

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 6252
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 6955
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

12.03.2018г.
Просмотров: 4244
Комментарии: 0
Глубокое обучение с точки зрения практика

 Читать далее...

12.03.2018г.
Просмотров: 3021
Комментарии: 0
Изучаем pandas

 Читать далее...

12.03.2018г.
Просмотров: 3815
Комментарии: 0
Программирование на языке Rust (Цветное издание)

 Читать далее...

19.12.2017г.
Просмотров: 3833
Комментарии: 0
Глубокое обучение

 Читать далее...

19.12.2017г.
Просмотров: 6328
Комментарии: 0
Анализ социальных медиа на Python

 Читать далее...

19.12.2017г.
Просмотров: 3179
Комментарии: 0
Основы блокчейна

 Читать далее...

19.12.2017г.
Просмотров: 3472
Комментарии: 0
Java 9. Полный обзор нововведений

 Читать далее...

16.02.2017г.
Просмотров: 7288
Комментарии: 0
Опоздавших не бывает, или книга о стеке

 Читать далее...

17.05.2016г.
Просмотров: 10655
Комментарии: 0
Теория вычислений для программистов

 Читать далее...

30.03.2015г.
Просмотров: 12375
Комментарии: 0
От математики к обобщенному программированию

 Читать далее...

18.02.2014г.
Просмотров: 14012
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

 Читать далее...

13.02.2014г.
Просмотров: 9137
Комментарии: 0
Читайте, размышляйте, действуйте

 Читать далее...

12.02.2014г.
Просмотров: 7090
Комментарии: 0
Рисуем наши мысли

 Читать далее...

10.02.2014г.
Просмотров: 5398
Комментарии: 3
Страна в цифрах

 Читать далее...

18.12.2013г.
Просмотров: 4628
Комментарии: 0
Большие данные меняют нашу жизнь

 Читать далее...

18.12.2013г.
Просмотров: 3436
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

 Читать далее...

04.12.2013г.
Просмотров: 3167
Комментарии: 0
Паутина в облаках

 Читать далее...

03.12.2013г.
Просмотров: 3411
Комментарии: 0
Рецензия на книгу «MongoDB в действии»

 Читать далее...

02.12.2013г.
Просмотров: 3034
Комментарии: 0
Не думай о минутах свысока

 Читать далее...

Друзья сайта  

 Работаем с OpenDocument из Perl

Архив номеров / 2007 / Выпуск №12 (61) / Работаем с OpenDocument из Perl

Рубрика: Программирование /  Веб-программирование

ВАЛЕНТИН СИНИЦЫН

Работаем с OpenDocument из Perl

Открытые форматы документов приобретают все большую популярность – и вы тоже можете извлечь из них пользу, даже не запуская OpenOffice.org. Все, что вам потребуется, – это интерпретатор Perl и задача, которую необходимо решить.

Сценариями, генерирующими динамический HTML-код, уже давно никого не удивишь. Простота языка разметки и повсеместная доступность веб-браузеров сделали этот язык «номером один» для представления структурированной информации. Однако, при своих несомненных достоинствах, HTML ориентирован в первую очередь на просмотр в электронном виде. Что же делать, если необходимо создать динамический документ для печати?

Ответом может быть OpenDocument 1.0, он же ISO/IEC 26300:2006 – открытый формат офисных документов, поддерживаемый, в первую очередь, открытым офисным пакетом OpenOffice.org, а при наличии соответствующего расширения – и Microsoft Office (XP и выше). Для программирования же удобно использовать Perl – универсальный язык-клей, усиленный модулем OpenOffice::OODoc Жана-Мари Гуарна (Jean-Marie Gouarne). Помимо OpenDocument, OpenOffice::OODoc поддерживает также и более старый формат OpenOffice.org 1.x.

Установка

Как и все в мире Perl, OpenOffice::OODoc доступен со CPAN [1]. Там же находятся и его основные зависимости: Archive::Zip [2] и XML::Twig [3]. Установка модуля производится стандартным образом:

perl Makefile.pl

make

make test

make install

Пользователи ActivePerl, разумеется, могут воспользоваться вместо этого утилитой PPM:

ppm install OpenOffice-OODoc

На этапе сборки вам будет предложено ввести некоторые параметры; особое внимание следует обратить на локальную кодировку. Данные настройки сохраняются в файле config.xml в том же каталоге, где находятся файлы модуля (обычно site/lib/OpenOffice/OODoc относительно стандартного пути библиотек Perl). Для PPM-пакета конфигурирование в процессе установки не предусмотрено, поэтому config.xml необходимо отредактировать вручную.

Архитектура

Модуль OpenOffice::OODoc имеет трехуровневую структуру. На самой нижней ступени иерархии находится класс OpenOffice::OODoc::File, инкапсулирующий функционал Archive::Zip и предоставляющий доступ к XML-содержимому документа для других компонентов модуля (см. врезку). Над ним располагается OpenDocument::OODoc::XPath, наследующий XML::Twig и обеспечивающий работу с XML-деревом OpenDocument посредством XPath. На этом можно было бы и остановиться: спецификация формата открыта, так что все необходимые операции можно выполнять посредством XPath-запросов, однако OODoc идет дальше и предоставляет несколько высокоуровневых «оберток», облегчающих работу с распространенными типами содержимого OpenDocument:

  • OpenOffice::OODoc::Text – класс для взаимодействия с текстом (абзацы, заголовки, списки, таблицы, сноски, секции и т. д.);
  • OpenOffice::OODoc::Image – класс, обеспечивающий работу с изображениями;
  • OpenOffice::OODoc::Styles – как нетрудно догадаться по названию, этот класс управляет стилями документа (шрифты, цвета и многое другое);
  • OpenOffice::OODoc::Meta – метаданные документа: заголовок, автор, дата создания и т. п.

Обратите внимание, что деление на классы происходит по типу содержимого, а не документа, то есть, например, OODoc::Text используется как для ODT, так и для ODS. На вершине иерархии находится класс OpenOffice::OODoc::Document, наследующий от OpenOffice::OODoc::Text, Image и Styles. Таким образом, экземпляром OODoc::Document может быть представлен практически любой документ, будь то текст, электронная таблица или презентация. Неприятным исключением из этого перечня являются диаграммы – с ними OpenOffice::OODoc работать не умеет (хочется надеяться, что пока).

Из всего перечисленного выше наиболее развитым интерфейсом обладает OODoc::Text. Для большинства текстовых элементов поддерживаются следующие действия: вставить/добавить в конец документа (insertXXX/appendXXX), найти по стилю/содержимому/чему-то еще (selectByXXX), получить список всех элементов заданного типа/конкретное вхождение по его номеру (getXXX), удалить (removeXXX), а также некоторые другие. Интересно, что в объектной модели OpenOffice::OODoc методы часто приписываются не к самому элементу, а к документу в целом. Так, например, для получения текстового содержимого элемента используется конструкция $document->getText($element), 
а не $element->getText, как можно было бы ожидать.

Прослойки, реализуемые указанными выше классами, весьма упрощают выполнение типовых операций над документом, но при этом являются сравнительно «тонкими», так что знание основ формата все же не помешает. Спецификацию OpenDocument (в форматах ODT и PDF) можно загрузить с [4], а также [5], если вас интересует неофициальный русскоязычный перевод.

Думаю, на сегодня теории достаточно. В составе POD-документации OpenOffice::OODoc имеется страница Intro, помогающая глубже разобраться с возможностями модуля. Мы же посмотрим, как применить его к решению стандартных задач. Приведенные примеры были сделаны нарочито простыми, но в то же время расширяемыми, так что при необходимости их можно превратить в полноценные программы. Полный исходный код можно скачать на сайте журнала www.samag.ru, в соответствующем разделе.

Первый шаг

OpenOffice::OODoc позволяет как создавать новые документы «с нуля» (точнее, на основе заранее подготовленных шаблонов, хранящихся в виде несжатых XML-файлов в каталоге, путь к которому возвращается функцией ooTemplatePath()), так и преобразовывать уже существующие. Традиционно, вторая задача считается более сложной, поэтому мы сфокусируемся именно на ней.

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

1: #!/usr/bin/perl

2: use strict;

3: use OpenOffice::OODoc;

4: my $file = shift;

5: die "Usage: $0 filename.odt" unless ($file);

6: my $doc = ooDocument(file => $file, member => 'content');

7: $doc->outputDelimitersOn();

8: print scalar $doc->getTextContent();

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

В строке 6 создается объект класса OODoc::Document (использование функций ooXXX вместо соответствующих конструкторов new является предпочтительным способом). Мы указываем, что нас будет интересовать файл content.xml, в котором и хранится содержимое документа. При использовании функции ooStyle() для получения доступа к именованным стилям здесь будет уместно указать 'style'.

Строка 7 предписывает включить «ограничители» (delimiters) – о них мы поговорим чуть позже.

Наконец, в строке 8 мы получаем текстовое содержимое документа методом getTextContent() и выводим его на консоль. При этом getTextContent() преобразует родной для OpenDocument UTF-8 в локальную кодировку, установленную вами в файле config.xml. Обратите внимание на scalar: в списочном контексте getTextContent() возвращает массив строк, составляющих документ; мы же хотим получить его содержимое в виде одной строки. В этом случае в качестве разделителя строк используется значение свойства 
$doc->{'line_separator'}, которое вы можете установить по своему усмотрению.

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

$doc->outputDelimitersOff();

my $content = $doc->getTextContent();

$content =~ s/\s+//mg;

print length $content;

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

Внимательный читатель может обратить внимание, что во втором примере мы почему-то использовали метод outputDelimitersOff(), чтобы отключить вывод «ограничителей». Подобная «чехарда», конечно, не способствует пониманию процесса, поэтому попробуем объясниться: ограничитель – это всего-навсего пара строковых констант, обрамляющая текстовое представление некоторого элемента. Например, в настройке по умолчанию элементы text:span (их назначение совпадает с таковым в HTML) выделяются «елочками» (<< и >>), а сноски – фигурными скобками и префиксом 'NOTE: '. Разумеется, ограничители можно изменять по своему усмотрению; этим мы и воспользуемся в следующем примере, выводящем текст OpenDocument в раскраске «а-ля Lynx»:

use Term::ANSIColor qw(:constants);

...

my $doc = ooDocument(file => $file, member => 'content');

$doc->{'delimiters'}{'text:h'} = { 'begin' => RED, 'end' => RESET };

$doc->{'delimiters'}{'text:a'} = { 'begin' => BLUE, 'end' => RESET };

$doc->{'delimiters'}{'text:list-item'} = { 'begin' => YELLOW . '* ' . RESET, 'end' => '' };

$doc->{'delimiters'}{'text:span'} = { 'begin' => BOLD, 'end' => RESET };

$doc->outputDelimitersOn();

print scalar $doc->getTextContent();

Как можно видеть, разделители – это просто хэш, на который указывает свойство delimiters объекта OODoc::Text. Его ключами являются имена элементов: text:h соответствует заголовкам (они у нас выводятся красным цветом), text:a – гиперссылкам (синий цвет), text:list-item – элементам списков (желтая звездочка в качестве маркера – стандарт OpenDocument различает нумерованные и ненумерованные списки не на уровне тегов, как HTML, а на уровне стилей, и мы вернемся к этому вопросу чуть позже), а text:span – стилевому выделению (жирный, курсив и т. п.). Соответствующие константы определены в модуле Term::ANSIColor.

К сожалению, в версии 2.035 (и, по-видимому, более ранних) в классе OODoc::Text присутствует ошибка, которая препятствует корректному выводу списков в методе getTextContent(). Исправление для нее можно найти на сайте журнала www.samag.ru в разделе «Исходный код». Автор модуля на предложенный патч, к сожалению, пока не отреагировал.

Легкость применения ограничителей одновременно является и плюсом, и минусом, поскольку серьезно сужает их область применения. Ограничитель можно связать только с именем элемента, поэтому, например, различить заголовки различных уровней (за это отвечает атрибут text:outline-level) с их помощью невозможно. Для более серьезных целей необходимо использовать другой подход – и один из возможных вариантов мы рассмотрим в следующем разделе.

HTML-конвертер

Преобразование OpenDocument в HTML едва ли можно назвать трудной задачей – соответствующий экспортный фильтр включен в стандартную поставку OpenOffice.org; существуют и внешние XSLT-таблицы, например, расширение для Mozilla Firefox [6]. И в то же время каждый, кто сталкивался с необходимостью опубликовать документ OpenDocument на своем веб-сайте, думаю, согласится: с данной задачей указанные инструменты справляются далеко не на «пятерку». Проблема в том, что они (из лучших, разумеется, побуждений) стремятся максимально сохранить внешний вид документа, в результате чего он обрастает встроенными стилями и CSS-таблицами, а вам-то необходимо сохранить смысловую разметку и внедрить документ в уже имеющийся дизайн! Умело используя OpenOffice::OODoc, можно экспортировать в HTML только те аспекты документа, которые действительно нужны, и игнорировать остальное.

Спецификация OpenDocument 1.0 насчитывает 700 страниц, но, к счастью, далеко не все из перечисленного в ней может встретиться вам в «рядовом» документе. Приведенный ниже пример поддерживает заголовки, абзацы, списки, стилевые выделения, гиперссылки и изображения; в случае необходимости можно легко добавить и другие элементы (первая кандидатура – очевидно, таблицы).

my $doc = ooDocument(file => $i_file, member => 'content');

my $meta = ooMeta(file => $i_file);

$doc->outputDelimitersOff();

open(O_FILE, '>' . $o_file) || die "Unable to open $o_file";

my $title = $meta->title();

my $author = $meta->creator();

my $charset = ooLocalEncoding();

print O_FILE <<"__HEADER__";

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>$title</title>

<meta name="Author" content="$author">

<meta http-equiv="Content-Type" ?

    content="text/html; charset=$charset">

</head>

<body>

__HEADER__

for my $elt ($doc->selectElementsByContent('.*')) {

  if    ($elt->isHeading) {

    print O_FILE heading($elt), "\n";

  }

  elsif ($elt->isParagraph) {

    print O_FILE '<p>', paragraph_content($elt), "</p>\n";

  }

  elsif ($elt->isItemList) {

    print O_FILE list($elt, 1), "\n"; 

  }

}

print O_FILE <<'__FOOTER__';

</body>

</html>

__FOOTER__

close(O_FILE);

Мы генерируем заголовок, основываясь на мета-информации документа; функция ooLocalEncoding() возвращает системную кодировку, установленную вами в файле config.xml. Основная работа, очевидно, сосредоточена в цикле:

for my $elt ($doc->selectElementsByContent('.*')) { ... }

Метод selectElementsByContent('.*') возвращает список всех элементов второго уровня ('.*' – это регулярное выражение-фильтр; замените его на 'apples', чтобы получить только те элементы, в которых упоминаются яблоки). Наш скрипт различает три типа таких элементов: заголовки (Heading), абзацы (Paragraphs) и списки (ItemList) – все остальное может встретиться только в содержимом абзаца (элементы третьего и более высоких уровней). Обработать заголовок проще всего:

sub heading {

  my $elt = shift;

  my $level = $doc->getOutlineLevel($elt);

  return "<h$level>" . paragraph_content($elt) . "</h$level>\n";

}

Метод getOutlineLevel() возвращает номер уровня заголовка, мы генерируем соответствующий ему тег <hi>.

Функция paragraph_content обрабатывает содержимое параграфа и является в некотором смысле центральной – все, что содержит хотя бы толику текста, проходит через нее. Неудивительно, что она в несколько раз длиннее heading().

sub paragraph_content {

  my $para = shift;

  my $result;

  for my $child ($para->getChildNodes) {

    if   ($child->isSpan) {

      $result .= span($child);

    }

    elsif ($child->isHyperlink) {

      $result .= hyperlink($child);

    }

    elsif ($child->isImage) {

      $result .= image($child);

    }

    elsif ($child->getName eq '#PCDATA') {

      my $text = ooDecodeText($child->text);

      $text =~ s/&/&amp;/g;

      $text =~ s/</&lt;/g;

      $text =~ s/>/&gt;/g;

      $result .= $text;

    }

    else {

      warn "Element '" . $child->getName . "' was ignored\n";

    }

  }

  return $result;

}

В рамках принятой нами упрощенной модели под «содержимым абзаца» подразумеваются: простой текст (#PCDATA, обратите внимание на декодирование из внутренней UTF-8 в локальную кодировку системы и замену символов, запрещенных в HTML), стилевые выделения (span), гиперссылки (hyperlink) и изображения (image). Все они обрабатываются одноименными функциями, поэтому при необходимости включить в данный код, например, сноски, это не составит большого труда. Для необработанных элементов генерируется предупреждение.

Функция span() отвечает за выделение текста полужирным шрифтом и курсивом:

sub span {

  my $elt = shift;

  my %attrs = $doc->getStyleAttributes($doc->textStyle($elt));

  if ($attrs{'properties'}->{'fo:font-weight'} eq 'bold') {

    return "<b>" . paragraph_content($elt) . "</b>";

  } elsif ($attrs{'properties'}->{'fo:font-style'} eq 'italic') {

    return "<i>" . paragraph_content($elt) . "</i>";

  }

}

Конструкция $doc->getStyleAttributes($doc->textStyle($elt)) возвращает стиль, связанный с элементом $elt. В таком виде она будет работать только для автоматических стилей; для работы с именованными стилями необходимо создать объект OODoc::Style на базе файла styles.xml (ooStyle(member => 'styles')); Помимо задействованных здесь свойств fo:font-weight и fo:font-style, назначение которых, надеюсь, понятно из кода, могут оказаться полезными fo:color (цвет текста) и fo:background-color (цвет фона). Полный список свойств стилей можно найти, конечно, в спецификации [4].

Функции image() и list() действуют аналогичным образом, и мы не будем приводить их здесь ради экономии места. Остановимся лишь на способе различить нумерованный и ненумерованный список:

 1:  my $is_ordered = 0;

 2:  my $style = $doc->getStyleElement($doc->textStyle($elt), namespace => 'text', type => 'list-style');

 3:  my $st = $doc->getNodeByXPath("//*[\@text:level=\"$level\"]", $style);

 4:  if ($st && $st->getName eq 'text:list-level-style-number') {

 5:    $is_ordered = 1;

 6:  } elsif ($st && $st->getName eq 'text:list-level-style-bullet') {

 7:    $is_ordered = 0;

 8:  } else {

 9:    warn 'Unknown type of the list: '. $st . "\n";

10:  }

Как было отмечено выше, OpenDocument различает эти два типа списков на уровне стилей; параметры нумерованных списков задаются при помощи элемента text:list-level-style-number, а ненумерованных – text:list-level-style-bullet. XPath-выражение в строке 3 просто возвращает элемент стиля, соответствующий списку заданного уровня $level (а уровень, в свою очередь, определяется глубиной вложенности списков друг в друга).

По шаблону

Другой распространенной задачей является генерация документов по шаблону. В простейшем случае она сводится к подстановке имен, дат, адресов, сумм, реквизитов и другой уместной информации в письма, извещения, платежные поручения и т. п. Эта задача решается средствами «чистого» OpenOffice.org благодаря механизму полей, которые могут заполняться даже из внешней базы данных, но если вы чувствуете, что для решения данной проблемы лучше создать скрипт, то его простейший вариант может выглядеть так:

my $doc = ooDocument(file => $i_file, member => 'content');

$doc->userFieldValue('someField', 'someValue');

$doc->save($o_file);

Данный код открывает документ, имя которого содержится в $i_file, присваивает полю someField значение «someValue» и сохраняет его под именем $o_file. Документ-шаблон готовится обычным образом, вставка полей происходит через меню «Вставка -> Поле -> Дополнительно -> Переменные -> Поле пользователя» (в OpenOffice.org 2.3 Pro).

Несколько более интересным представляется вариант, когда на лету модифицируется не только содержимое, но и сама структура документа. Здесь опять удобно провести аналогию с динамическими веб-страницами. В простейшем случае они представляют собой HTML-код, перемежающийся с инструкциями по его созданию, обрамленными специальными тегами. Тот же подход, но на языке OpenDocument, может звучать так: шаблон представляет собой обычный документ OpenDocument со вставками кода на Perl, выделенными специальным стилем. Отметим, что похожая идея реализуется библиотекой с красивым именем pod (Pyhton Open Document), но для языка Pyhton [7].

Чтобы создать шаблон, подобный предложенному выше, откройте OpenOffice.org Writer и создайте новый стиль текста, например, на базе обычного Базового стиля. Назовите его как-нибудь вроде EmbeddedPerl (пробелы допустимы, но нежелательны – в коде нашего скрипта их придется менять на _20_). В качестве «Следующего стиля» можно указать все тот же «Базовый» – тогда при нажатии <Enter> будет автоматически начинаться обычный абзац. Для стиля EmbeddedPerl удобно установить моноширинный шрифт и цвет фона, отличный от стандартного. Однако OOo Writer – не IDE, поэтому при вводе кода следует соблюдать некоторые меры предосторожности: всегда использовать «мягкий» перевод строки (<Shift + Enter>) и отключить автозамену кавычек («Сервис -> Автозамена -> Параметры»).

Центральная часть скрипта, обрабатывающего предложенный шаблон, может выглядеть так:

my $block_no = 1;

my $image_no = 1;

my $anchor;

for my $para ($doc->selectParagraphsByStyle('EmbeddedPerl')) {

    my $code = $doc->getText($para);

    $anchor = $para;

    my $result = eval $code;

    die "Error in template block $block_no: $@" if ($@);

    if (ref $result && $result->isElementNode) {

        $doc->replaceElement($para, $result);

    } else {

           if ($result) {

            my $new_para = $doc->insertParagraph($para);

            $doc->setText($new_para, $result);

        }

        $doc->removeElement($para);

    }

    $block_no++;

}

$doc->save($o_file);

Метод selectParagraphsByStyle() возвращает список абзацев, имеющих заданный стиль (поэтому важно использовать «мягкий» перевод строки: нажав Enter, вы создадите новый абзац, и код во вставке оборвется). Если вставка, выполняемая функцией eval(), вернула объект Element, он замещает собой абзац, в котором размещалась вставка; в противном случае абзац просто удаляется. Любое другое непустое возвращаемое значение трактуется как абзац текста, логическая ложь означает «удалить вставку и не предпринимать ничего сверх этого».

Теоретически во вставке может находиться любой Perl-код; он также имеет доступ ко всем переменным нашего скрипта. Однако вводить длинные программы в шаблон неудобно, поэтому разумно создать функции-обертки вроде heading(), paragraph(), list() и image(), вставляющие в документ заголовок, абзац, список и изображение соответственно. Для пользователя шаблона вызов этой функции будет выглядеть как простая подстановка или разворачивание макроса. Функция image(), например, может выглядеть так:

sub image {

    my ($file, $width, $height) = @_;

    $anchor = $doc->insertParagraph($anchor, position => 'after');

    $doc->insertImageElement("Image" . $image_no++, attachment => $anchor, import => $file, size => "${width}cm, ${height}cm");

    return undef;

}

Изображения в OpenDocument являются содержимым абзаца: мы создаем его методом insertParagraph(). Переменная $anchor содержит последний элемент, сгенерированный текущей вставкой, и используется в качестве якоря, чтобы данные элементы появлялись в итоговом документе в том же порядке, что и в шаблоне. Изображения, вставленные в документ, не обязательно будут иметь родной размер, поэтому их высота и ширина задается явным образом (в сантиметрах; использовать здесь 'px', то есть пискели, невозможно). Функция возвращает undef, так что никаких других изменений структуры документа в основном цикле не производится.

По такому же принципу можно запрограммировать и все остальные функции. Следует только иметь в виду одно обстоятельство: стиль, который вы явно или неявно присваиваете созданному элементу, уже должен быть определен в документе. Этого можно добиться, создав его вручную (средствами OpenOffice::OODoc::Style) или разместив в документе элемент с нужным стилем прямо в редакторе OOo Writer.

Рассмотренные здесь примеры, конечно, не претендуют на полноту, но, хочется надеяться, дают представление об использовании OpenOffice::OODoc при решении повседневных задач. Все скрипты, по возможности, проектировались максимально расширяемыми, так что вы можете использовать их в собственных целях. Если у вас получится что-то общественно-полезное, не забудьте опубликовать код где-нибудь на SourceForge.net!

Приложение

OpenDocument изнутри

Документы OpenDocument представляют собой обычный ZIP-архив, расширение которого подбирается в соответствии с типом документа (.odt – для текста, .ods – для электронных таблиц и т. п.). Внутри архива находится несколько XML-файлов и каталогов, наиболее важные из которых таковы:

  • content.xml – непосредственно содержимое документа: текст, таблицы, ссылки на изображения. Последние могут находиться в каталоге Pictures в том же архиве или быть полностью внешними. В файле content.xml также определяются так называемые автоматические стили – то есть те, которые создаются офисным пакетом, когда вы нажимаете кнопки «жирный», «курсив», «список» в панели инструментов. В отличие от именованных стилей (Базовый, Основной текст, Заголовок 1 и т. д.), автоматические стили не имеют постоянного имени.
  • styles.xml – место для хранения именованных стилей.
  • meta.xml – метаданные документа (имя автора, дата создания, заголовок и т. п.)

Формат OpenDocument легко читается человеком. Если вы пользуетесь OpenOffice.org, не забудьте снять галочку «Оптимизация по размеру для формата XML» в «Параметры -> Загрузка/сохранение -> Общие», чтобы XML-содержимое сохранялось в отформатированном виде (а не в одну строку, как это происходит с оригинальной сборкой OOo по умолчанию).

  1. OpenOffice::OODOC – http://search.cpan.org/~jmgdoc/OpenOffice-OODoc-2.035.
  2. Archive::Zip – http://search.cpan.org/~adamk/Archive-Zip-1.23.
  3. XML::Twig – http://search.cpan.org/~mirod/XML-Twig-3.32.
  4. Спецификация OpenDocument – http://www.oasis-open.org/committees/office.
  5. Русскоязычный перевод спецификации OpenDocument – http://www.i-rs.ru/odf/translation.
  6. ODFReader – https://addons.mozilla.org/firefox/addon/1888.
  7. Библиотека pod – http://appyframework.org/pod.html.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-45
E-mail: sa@samag.ru