OpenOffice + Delphi

OpenOffice.org ведет свое происхождение от офисного пакета StarOffice, разработанного немецкой фирмой StarDivision в середине 90-х годов. Осенью 1999 года корпорация Sun купила StarDivision. В июне 2000 года, уже под торговой маркой SUN вышел StarOffice 5.2 под MS Windows, Linux и Solaris. 13 октября 2000 года были открыты исходные тексты StarOffice (за исключение кода некоторых модулей, разработанных третьими фирмами), и этот день официально считается днем рождения OpenOffice.org.

В настоящее время над кодом OpenOffice.org работают как добровольцы со всего света, так и программисты корпорации SUN. Sun Microsystems, Inc в основном и финансирует деятельность проекта OpenOffice.org.

В настоящее время из одного исходного кода, разрабатываемого сообществом OpenOffice.org выпускаются два продукта: StarOffice, в который добавляются компоненты под проприетарной лицензией и свободный OpenOffice.org. В настоящее время в OpenOffice.org большинство проприетарных компонентов, присутствующих в StarOffice заменено их свободными аналогами.

Все более широкие массы компьютерной общественности обращают свой взор на офисный пакет OpenOffice.org. Вслед, за обыкновеннными пользователями пришел черед и программистов, волею судеб вынужденных работать с новым детищем. Коллег наших можно разделить на три условных группы: первая и самая многочисленная - люди, писавшие под Visual Basic; вторая - программисты Java, и третья, инструментом представителей которой являются Delphi, C Builder, VC++ etc.

Почему именно такая градация? С первыми все очевидно. офисный пакет от Майкрософт в качестве языка использует VBA, а переход на новый продукт редко когда начинается с нуля. То есть главной задачей этой группы является адаптация имеющихся кодов под OpenOffice.org. Со второй группой, тоже все более-менее понятно - компонентная модель OpenOffice.org заточена под Java. Более того, одним из трех приоритетных языков, поддерживаемых разработчиками, является Java. В состав приоритетных, кроме Java входят C++ и StarBasic (аналог VBA от Майкрософта). Третья группа перспективной разработчиками не считается (далее речь будет идти сугубо о Delphi). А жаль, потому как использование СОМ-связки с офисными пакетами один из очень расспростаненных приемов. Достаточно вспомнить хотя бы составление отчетов, с целью их дальнейшей модификации или передачи третьим лицам, а про использование встроенной проверки орфографии я вообще молчу.

Обидно другое. Я давно пользуюсь девизом: "Документация как секс - лучше любая, чем никакой". Но извините, меня очень огорчил тот факт, что в составе 94-мегабайтного набора инструментов разработчика (OpenOffice.org Software Development Kit), который мне пришлось выкачать (правда в архиве это в 2 раза меньше. :)) есть единственный рабочий пример связки OpenOffice.org-Delphi и к тому же не рабочий. Этого, при всем уважении к разработчикам, я понять не могу. Если уж вы игнорируете среду разработки, то будьте добры, до блеска отдрайте единственный пример. Ну да ладно, это все эмоции. После ручной доработки код из примера заработал. Прямо как в известном анекдоте. Украли "шпиены" чертежи страшного советского самолета (допустим СУ-24 :)) и передали своим техникам. Те собирают - паровоз получается и хоть ты тресни. И тут главный "шпиен" внизу страницы маленькую сносочку обнаруживает: "После сборки тщательно обработать напильником".

Слюни в жилетку пускать рано. В том же SDK есть масса рабочих примеров на Visual Basic, Java и C++. Так что скажем слова благодарности разработчикам, что не дают они расслабиться нашему пытливому уму. Однако, уважаемый читатель, если автоматизацию офиса ты собираешься проводить только с помощью Delphi и не более, хочу предостеречь тебя от безсмысленной траты трафика. Отдельные статьи и примеры кода имеются в интернете.

Компонентая модель OpenOffice.org
В дальнейшем я буду использовать названия OpenOffice.org (OO) и StarOffice.org (SO) в одном контексте, потому как что применимо для одного применимо и для другого.

Работа StarOffice.org базируется на компонентной модели, именуемой Универсальные Сетевые Объекты (Universal Network Objects, UNO).

UNO предоставляет механизм для кроссплафторменного и языконезависимого программирования. Как я уже отмечал, основное внимание уделяется аспектам работы с Java, C++ и StarOffice Basic. Оно и понятно, ведь во главу угла ставиться кроссплатформенность. Более того, пальму первенства держат даже такие экзотические языки как JavaScript и PHP. Но странно как-то, по крайней мере для меня, будет выглядеть эта связка.

Начиная с версии Старофис 5.2, автоматизация OLE является составной частью пакета. Однако, прямой доступ к этим компонентам (UNO) из Delphi получить не удастся. Для этого используется механизм "моста автоматизации" (Automation Bridge), который объединяет интерфейс автоматизации OLE с языковыми связками StarOffice API.

UNO предоставляет единственный интерфейс для работы со своими компонентами - Сервис Менеджер (Service Manager). Сервис Менеджер по сути является фабрикой классов, следовательно возвращает интерфейс IClassFactory. С точки зрения концепции модели UNO иного пути для создания объектов автоматизации StarOffice.org как через Сервис Менеджер нету.

UNO изначально не создавался с поддержкой автоматизации и COM, а был ориентирован на работу со скриптовыми языками.

По материалам "http://developers.sun.com/techtopics/desktop/reference/techart/staroffice_sdk.html"

Сервис Менеджер
Как уже отмечалось, СМ (Сервис Менеджер) - это стартовая точка для всех клиентов автоматизации. Раз СМ является COM-компонентом, то он имеет CLSID и програмный идентификатор, а именно 'com.sun.star.ServiceManager' .

Исполняемый код СМ содержится в файле soffice.exe. Этот исполняемый файл держит все СОМ подключения и открытые документы, будь-то текстовые либо электронные таблицы. Даже в случае насильственного повторного запуска, запущенный процесс берет на себя всю заботу об открытии документов, предоставлении интерфейсов OLE автоматизации, а копию закрывает. Поэтому в памяти может находится только один такой процесс. А это значит, что для связки OO-Delphi нету понятия подключения к существующему СОМ объекту, то есть GetActiveObject имеет тот результат что и вызов CreateOleObject. Поэтому, дабы избежать ненужной проверки на существование запущенного процесса, будем использовать второй вариант, что неизбежно приведет нас к нужному результату.

СМ, так же как и объекты, которые он создает, и объекты, предоставляемые им же косвенно через результаты возврата функций, является типичным объектом автоматизации. Имеется возможность удаленного доступа через механизм DCOM.

Сервис Менеджер базируется на Сервис Менеджере UNO и ничем не отличается от обычных компонентов UNO. "Как же так?" - спросите вы, "Ведь было сказано, что UNO не поддерживает СОМ". И будете совершенно правы, за конвертацию Сервис Менеджера отвечает сервис 'com.sun.star.bridge.OleBridgeSupplier2' .

Результирующий объект автоматизации содержит в себе объект UNO и транслирует вызовы IDispatch.Invoke в необходимые функции интерфейсов UNO. Возвращаемые после таких вызовов объекты, тоже автоматически конвертируются в правильные СОМ-объекты. Развернутую статью по сервисам конвертации оставлю на потом. Однако замечу, что материал интересен, как минимум, с теоретической точки зрения.

Подытожу. Схема взаемодействия такова: СервисМенеджер - ОО Десктоп - Документы (открытые либо созданные).

По мат. " http://api.openoffice.org/docs/DevelopersGuide/ProfUNO/ProfUNO.htm#1+4+4+Automation+Bridge"

Практика
Сердцем примера является модуль SampleCode, в котором и происходит взаемодействие с сервером автоматизации. Рассмотрим исходный код. Важные места я буду обрамлять комментариями. Задачу клиента разделим на такие составляющие: связь с сервером, создание нового документа, открытие существующего документа в формате html, добавление в документ строки текста и, наконец, сохранение документа в формате Microsoft Word *.doc. В итоге у нас должен получиться своего рода конвертор из гипертекста в файлы Ворда. Приступим. Не забудьте скачать пример к статье (3.6Kb).

	
unit SampleCode;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComObj, Variants;

type TOpenOffice = class function Connect: boolean; procedure Disconnect; function CreateDocument: boolean; function OpenDocument(const aFileUrl:string): boolean; procedure SaveDocument(const aFileUrl:string); procedure InsertText(const aText: String); private StarOffice: Variant; Document: Variant; function MakePropertyValue(PropName, PropValue:string):variant; end;

implementation

{Метод Connect создает подключение к серверу автоматизации. Как упоминалось выше, в нашем случае понятия "подключиться к существующему" и "создать подключение" не имеют принципиальной разницы. Поэтому пользуемся функцией CreateOleObject.}

function TOpenOffice.Connect: boolean; begin if VarIsEmpty(StarOffice) then StarOffice := CreateOleObject( 'com.sun.star.ServiceManager' ); Result := not (VarIsEmpty(StarOffice) or VarIsNull(StarOffice)); end;

procedure TOpenOffice.Disconnect; begin StarOffice := Unassigned; end;

{Метод CreateDocument создает новый текстовый документ. Как видите Сервис Менеджер, который доступен через переменную StarOffice, создает и возвращет экземпляр DeskTop. Рассмотрим пристальнее метод LoadComponentFromURL. Первым параметром передается имя файла, в виде URL. На самом деле строка 'private:factory/swriter' действительно указывает на существующий файл. Это файл шаблона, по которому создается пустой текстовый документ. Следующий параметр ('_blank') указывает на то, что документ должен открыться в новом окне. Третьим параметром идут флаги вызова, пока что всегда равны нулю. И последним параметром, идут опции открытия документов. На самом деле, это массив пар название опции - значение опции. В данном случае, дополнительных опций мы не используем, поэтому передаем пустой массив. Передачу опций более детально рассмотрим ниже. }

function TOpenOffice.CreateDocument: boolean; var StarDesktop: Variant; begin StarDesktop := StarOffice.createInstance( 'com.sun.star.frame.Desktop' ); Document := StarDesktop.LoadComponentFromURL( 'private:factory/swriter' , '_blank' , 0, VarArrayCreate([0, -1], varVariant)); Result := not (VarIsEmpty(Document) or VarIsNull(Document)); end;

function TOpenOffice.MakePropertyValue(PropName, PropValue:string):variant; var Struct: variant; begin Struct := StarOffice.Bridge_GetStruct( 'com.sun.star.beans.PropertyValue' ); Struct.Name := PropName; Struct.Value := PropValue; Result := Struct; end;

{Рассмотрим метод OpenDocument, который открывает существующий документ. Отличия его от метода CreateDocument невелики, я бы даже сказал ничтожны. Так как тут мы будем использовать вызов метода LoadComponentFromURL с дополнительными опциями, создадим заранее массив необходимого размера, в данном случае на одну ячейку. За создание пар опция-значение, отвечает сам ОпенОфис. Поэтому эту участок работы оформлен в отдельную функцию MakePropertyValue. Отличия на этом закончились.}

function TOpenOffice.OpenDocument(const aFileUrl:string): boolean; var StarDesktop: Variant; VariantArr: variant; begin StarDesktop := StarOffice.CreateInstance( 'com.sun.star.frame.Desktop' ); VariantArr := VarArrayCreate([0, 0], varVariant); VariantArr[0] := MakePropertyValue( 'FilterName' , 'HTML (StarWriter)' ); Document := StarDesktop.LoadComponentFromURL( aFileUrl, '_blank' , 0, VariantArr); Result := not (VarIsEmpty(Document) or VarIsNull(Document)); end;

{Я думаю, сохранение документа в определенный формат не вызовет у вас трудностей. Аналогии LoadComponentFromURL с напрашиваются сами собой. }

procedure TOpenOffice.SaveDocument(const aFileUrl:string); var StarDesktop: Variant; VariantArr: variant; begin StarDesktop := StarOffice.createInstance( 'com.sun.star.frame.Desktop' ); VariantArr := VarArrayCreate([0, 0], varVariant); VariantArr[0] := MakePropertyValue( 'FilterName' , 'MS Word 97' ); //VariantArr[0] := MakePropertyValue('FilterName', 'Rich Text Format'); если кому-то понадобится ;-) Document.StoreToURL(aFileUrl, VariantArr); end;

{Метод InsertText добавляет строку текста в активный документ. Комментарии на аглийском мои личные. Оставлю как есть, потому что не могу красиво и лаконично перевести. :)}

procedure TOpenOffice.InsertText(const aText: String); var oCursor: Variant; oText: Variant; begin //get document text object oText := Document.GetText; //create cursor oCursor := oText.CreateTextCursor; //set some text properties oCursor.SetPropertyValue( 'CharColor' , 255); oCursor.SetPropertyValue( 'CharShadowed' , True); //insert string oText.InsertString(oCursor, aText, false); //insert line break character oText.InsertControlCharacter(oCursor, 0, false); end;

end.

Заключение
Главная цель этой статьи не в написании навороченного клиента, а в том, чтобы показать программистам, обделенным вниманием разработчиков, что у них под боком лежит инструмент не менее мощный (а с учетом того, что в состав ОпенОфиса входит векторный редактор), а то и более гибкий и удобный, чем его собрат Microsoft Office.

К сожалению, как я уже писал, документации по связке ОО+Delphi практически нету. Максимум, это примеры сродни этому. Однако, есть много примеров для других языков. И если вам не чужды понятия мозговой деятельности, милости прошу. Начать рекоммендую с форума разработчиков. Особо следует обратить внимание на раздел с примерами.

Отдельно хочу выразить благодарность Роману Игнатьеву (Romkin©) и Юрию Федорову© за потраченное время.

На сегодня все, надеюсь на продолжение. Все в ваших руках - пишите отзывы.

 


Страница сайта http://silicontaiga.ru
Оригинал находится по адресу http://silicontaiga.ru/home.asp?artId=5609