Python で LibreOffice 拡張をステップバイステップで作ってみる

「LibreOffice マクロを Ubuntu + Python で作る」
https://qiita.com/nanbuwks/items/77d5707b9a2507972676

において、Python でのマクロが実現できました。一旦マクロにしてしまえば更にここから LibreOffice 拡張に仕立て上げることができますが、 LibreOffice Basic でのマクロの事例が多く Python はなかなかよくわかりません。

今回は、

「LibreOffice マクロ：Pythonでのプログラム例集」
https://qiita.com/nanbuwks/items/1f25e8839089eaefd6d4

のマクロを元に LibreOffice 拡張を作っていきます。



# 元となるマクロ

最初の Item に "Hello World" と書く以下のマクロを使います。

```
import uno

def HelloWorldPythonItem0():

    oDoc = XSCRIPTCONTEXT.getDocument()
    oSlideList =oDoc.getDrawPages()

    oSlide=oSlideList.getByIndex(0)
    oItem=oSlide.getByIndex(0)
    oItem.String="HelloWorld"

    return None
```

このマクロはドキュメント中のItemに文字を描画します。
Impress でテストするのがわかりやすいでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/eddb36aa-e2ed-e545-2b37-bfcd204eda72.png)

# 作業場所を作る

適当な場所にフォルダを作ります。フォルダの中身は以下の通りです。

「マクロ言語としてのPython [OpenOffice Wikiの和訳]」
https://vdlz.xyz/PC/LibreOffice/Macro/Python/Doc/PythonAsAMacroLanguage.html

を元に以下のように作業します。

```
.
├── META-INF
│   └── manifest.xml
└── src
    └── HelloWorldOxt.py
```

# 最初の一歩

## HelloWorldOxt.py

先のマクロプログラムをコピーし、名前を変更しておきます。
内容を以下のように変更します。

```

import uno

# this must be added to every script file (the
# name org.openoffice.script.DummyImplementationForPythonScripts should be changed to something
# different (must be unique within an office installation !)

# これは、あらゆるスクリプト・ファイル（名前org.openoffice.script.DummyImplementationForPythonScripts）
# に追加する必要があり、別のものに変更する必要があります。
# （オフィスのインストール内で一意である必要があります！

def HelloWorldPythonOxt():

    oDoc = XSCRIPTCONTEXT.getDocument()
    oSlideList =oDoc.getDrawPages()

    oSlide=oSlideList.getByIndex(0)
    oItem=oSlide.getByIndex(0)
    oItem.String="HelloWorld"

    return None


# --- faked component, dummy to allow registration with unopkg, no functionality expected
# --- 偽のコンポーネント、unopkgでの登録を可能にするダミー、機能は期待されていません

import unohelper
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation( \
    None,"org.openoffice.script.DummyImplementationForPythonScripts", \
    ("org.openoffice.script.DummyServiceForPythonScripts",),)

```
これも、以下の内容からコメント部分他を使用しています。
「マクロ言語としてのPython [OpenOffice Wikiの和訳]」
https://vdlz.xyz/PC/LibreOffice/Macro/Python/Doc/PythonAsAMacroLanguage.html

def を HelloWorldPythonItem0 から HelloWorldPythonOxt に変更しています。コメントにも書いていますが、既存のマクロと名前をかぶらないようにする必要があるためです。


## manifest.xml

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">
<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest">
<manifest:file-entry manifest:media-type="application/vnd.sun.star.framework-script" manifest:full-path="src"/>
</manifest:manifest>

```

## oxt ファイル作成

```
$ zip -r hellooxt.oxt *
```

## oxt ファイルを登録
LibreOfficeから「ツール」-「拡張機能マネージャー」から、oxt ファイルを以下のように登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/6eed4791-ed3c-0350-7e19-41feeff25d6b.png)

## 実行

再起動して、「ツール」-「マクロを実行」で以下を実行します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/22bfbd6b-f19c-a1ab-7ec0-7d14bd7a71cf.png)

問題なく実行できました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/4afbec53-9a19-c1f3-3585-81c84e60b526.png)

# アイコンを追加してみる

次に、リソースとして画像アイコンを追加してみます。作業フォルダ内に description.xml を以下のように作成します。

```

<?xml version="1.0" encoding="UTF-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006" 
xmlns:d="http://openoffice.org/extensions/description/2006"
xmlns:xlink="http://www.w3.org/1999/xlink">
 
  <version value="1.0" />   
  <platform value="all" />
  <icon>
	  <default xlink:href="images/icon.png" />
    <high-contrast xlink:href="images/icon.png" />
  </icon>
</description>

```

images/icon.png として42x42 の png 画像を作成し、配置します。
![icon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/87f4bc0e-a82f-4abc-69cf-7859b70bef81.png)


oxt ファイルを作成します。

```
$ rm hellooxt.oxt; zip -r hellooxt.oxt *
```
「ツール」-「拡張機能マネージャー」から一旦旧い hellooxt.oxt を登録解除して、新しい hellooxt.oxt を登録し直します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/b8cce27d-3788-328c-7a17-0723546cf889.png)
登録時にアイコンが表示されるようになりました。

# メニューバーに登録する

META-INF/manifest.xml に1行追加して以下のようにします。

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">
<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest">
        <manifest:file-entry manifest:media-type="application/vnd.sun.star.configuration-data" manifest:full-path="Addons.xcu"/>
        <manifest:file-entry manifest:media-type="application/vnd.sun.star.framework-script" manifest:full-path="src"/>
</manifest:manifest>
```
UI の関連付けは Addons.xcu で行うように指定をします。

## ダミーのメニューをメニューバーに登録する

作業ディレクトリに以下の内容でAddons.xcuを作ります。

Addons.xcu の内容は Java 的な抽象化がされていて、更に公式資料も要領を得ず、巷のサンプルも古くて動かないのが多いという地獄のような状況ですが、
「Libreoffice extension development with C++ - Part 5 - Build a simple addon with a menu and toolbar buttons.」
https://niocs.github.io/LOBook/extensions/part5.html
を読みつつ歯を食いしばりながら作っていきます。
```

<?xml version='1.0' encoding='UTF-8'?>
<oor:component-data oor:package="org.openoffice.Office" oor:name="Addons"
        xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <node oor:name="AddonUI">

                <node oor:name="OfficeMenuBar">
                  <node oor:name="com.nanbuwks.oxttestproject.menubar" oor:op="replace">
                        <prop oor:name="Title" oor:type="xs:string">
                          <value xml:lang="en-US">HelloOxt</value>
                        </prop>
                        <prop oor:name="Target" oor:type="xs:string">
                           <value>_self</value>
                        </prop>
                        <node oor:name="Submenu">
                            <node oor:name="m1" oor:op="replace">
                                <prop oor:name="URL" oor:type="xs:string">
                                    <value>.uno:Bold</value>
                                </prop>
                                <prop oor:name="Title" oor:type="xs:string">
                                    <value xml:lang="en-US">Hello1</value>
                                </prop>
                                <prop oor:name="Target" oor:type="xs:string">
                                    <value>_self</value>
                                </prop>
                                <prop oor:name="Context" oor:type="xs:string">
                                    <value>com.sun.star.presentation.PresentationDocument</value>
                                </prop>
                            </node>

                            <node oor:name="m2" oor:op="replace">
                                <prop oor:name="URL" oor:type="xs:string">
                                    <value>.uno:Italic</value>
                                </prop>
                                <prop oor:name="Title" oor:type="xs:string">
                                    <value xml:lang="en-US">Hello2</value>
                                </prop>
                                <prop oor:name="Target" oor:type="xs:string">
                                    <value>_self</value>
                                </prop>
                                <prop oor:name="Context" oor:type="xs:string">
                                    <value>com.sun.star.presentation.PresentationDocument</value>
                                </prop>
                            </node>
                       </node>
                  </node>
               </node>

        </node>
</oor:component-data>

```

先の資料によると、 prop oor:name=Content の指定は以下のようになるそうです。

> i. Writer: com.sun.star.text.TextDocument
> ii. Spreadsheet: com.sun.star.sheet.SpreadsheetDocument
> iii. Presentation: com.sun.star.presentation.PresentationDocument
> iv. Draw: com.sun.star.drawing.DrawingDocument
> v. Formula: com.sun.star.formula.FormulaProperties
> vi. Chart: com.sun.star.chart2.ChartDocument
> vii. Bibliography: com.sun.star.frame.Bibliography


メニューバーに HelloOxt が登録されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/1c67581a-9f9e-782b-3145-42e497964d21.png)


選択すると、選択テキストが Bold ないし Italic になります。


## メニューバーのリンク先を python プログラムに指定する




先の
```
<value>.uno:Bold</value>
```
を、
```
<value>vnd.sun.star.script:hellooxt.oxt|srce|HelloWorldOxt.py$HelloWorldPythonOxt?language=Python&amp;location=user:uno_packages</value>
```
に置き換えると、Python プログラムを呼ぶことができます。


# (以下続く)

# 資料

「Libreoffice extension development with C++ - Part 5 - Build a simple addon with a menu and toolbar buttons.」
https://niocs.github.io/LOBook/extensions/part5.html

「LibreOffice Developer's Guide: Chapter 4 - Extensions - The Document Foundation Wiki」
https://wiki.documentfoundation.org/Documentation/DevGuide/Extensions

「LibreOffice5(6)既存インターフェイスを継承してPythonスクリプトをUNOコンポーネント化する例&#65306;その1-p--q」
https://p--q.blogspot.com/2015/11/libreoffice56pythonuno1.html
数少ない python でのチャレンジ