LibreOffice5(107)Javaの例:Universal Content Broker (UCB)をPythonにする:その2

2017-12-10

旧ブログ

t f B! P L
LibreOffice5(98)Javaの例:Universal Content Broker (UCB)をPythonにする:その1の続きです。LibreOffice 6.0 SDK - Developer's Guide ExamplesのDataStreamRetrieverとDataStreamComposerをPythonにしました。PropertiesComposerとPropertiesRetrieverはLibreOffice5(106)FileContentのサービスとインターフェイスの一覧の問題でPythonにできませんでした。

前の関連記事:LibreOffice5(106)FileContentのサービスとインターフェイスの一覧


Javaの例:DataStreamRetrieverをPythonにする


ファイルのFileConentからインプットストリームを取得する例です。
#!/opt/libreoffice5.4/program/python
# -*- coding: utf-8 -*-
import unohelper  # オートメーションには必須(必須なのはuno)。
import os
from com.sun.star.ucb import OpenCommandArgument2  # Struct
from com.sun.star.ucb import OpenMode  # 定数
from com.sun.star.io import XActiveDataSink
from com.sun.star.ucb import Command  # Struct
def macro():  # オートメーションでのみ実行可。マクロだとカレントディレクトリが使えない。  
 if not os.path.exists(os.path.join("data", "data.txt")):  # インプットストリームを取得するファイルがなければ作成する。
  if not os.path.exists("data"):  # dataフォルダが存在しない時。
   os.mkdir("data")  # dataフォルダを作成。
  os.chdir("data")  # dataフォルダに移動。
  with open("data.txt", 'w', encoding="utf-8") as f:  # インプットストリームを取得するファイルを作成。
   f.write("sample sample sample sample sample sample sample sample EOF") 
  os.chdir("..") # 元のフォルダに戻る。
 ctx = XSCRIPTCONTEXT.getComponentContext()  # コンポーネントコンテクストの取得。
 smgr = ctx.getServiceManager()  # サービスマネージャーの取得。
 print("""-----------------------------------------------------------------------
DataStreamRetriever - obtains the data stream from a document resource.
-----------------------------------------------------------------------""")
 pwd = unohelper.systemPathToFileUrl(os.getcwd())  # 現在のディレクトリのfileurlを取得。。
 sourceurl =  "/".join((pwd, "data/data.txt"))  # インプットストリームを取得するfileurlを取得。 
 ucb =  smgr.createInstanceWithContext("com.sun.star.ucb.UniversalContentBroker", ctx)  # UniversalContentBroker。
 content = ucb.queryContent(ucb.createContentIdentifier(sourceurl))  # FileContentを取得。
 datasink = MyActiveDataSink()  # XActiveDataSinkを持ったクラスをインスタンス化。
 arg = OpenCommandArgument2(Mode=OpenMode.DOCUMENT, Priority=32768, Sink=datasink)  # SinkにXActiveDataSinkを持ったインスタンスを渡すとアクティブデータシンクとして使える。
 command = Command(Name="open", Handle=-1, Argument=arg)  # コマンド。
 content.execute(command, 0, None)  # コピー元ファイルについてコマンドを実行。setInputStream()でアクティブデータシンクにインプットストリームが渡される。
 inputstream = datasink.getInputStream()  # アクティブデータシンクからインプットストリームを取得。
 if inputstream:  # インプットストリームが取得出来た時。
  txt = "Getting data stream for resource {} succeeded.".format(sourceurl)
  print("""{}
{}""".format(txt, "-"*len(txt)))
  n, buffer = inputstream.readSomeBytes([], 65536)  # 第1引数のリストは戻り値のタプルの第2要素で返ってくる。
  txt = bytes(buffer).decode("utf-8")  # "".join(map(chr, buffer))でもよい。byteに変換して文字列に変換している。
  print("""Read bytes : {}
Read data (only first 64K displayed):
{} """.format(n, txt))
class MyActiveDataSink(unohelper.Base, XActiveDataSink):  # アクティブデータシンク。 
 def setInputStream(self, stream):
  self.stream = stream
 def getInputStream(self):
  return self.stream
if __name__ == "__main__":  # オートメーションで実行するとき
 def automation():  # オートメーションのためにglobalに出すのはこの関数のみにする。
  import officehelper
  from functools import wraps
  import sys
  from com.sun.star.beans import PropertyValue  # Struct
  from com.sun.star.script.provider import XScriptContext  
  def connectOffice(func):  # funcの前後でOffice接続の処理
   @wraps(func)
   def wrapper():  # LibreOfficeをバックグラウンドで起動してコンポーネントテクストとサービスマネジャーを取得する。
    try:
     ctx = officehelper.bootstrap()  # コンポーネントコンテクストの取得。
    except:
     print("Could not establish a connection with a running office.", file=sys.stderr)
     sys.exit()
    print("Connected to a running office ...")
    smgr = ctx.getServiceManager()  # サービスマネジャーの取得。
    print("Using {} {}".format(*_getLOVersion(ctx, smgr)))  # LibreOfficeのバージョンを出力。
    return func(ctx, smgr)  # 引数の関数の実行。
   def _getLOVersion(ctx, smgr):  # LibreOfficeの名前とバージョンを返す。
    cp = smgr.createInstanceWithContext('com.sun.star.configuration.ConfigurationProvider', ctx)
    node = PropertyValue(Name = 'nodepath', Value = 'org.openoffice.Setup/Product' )  # share/registry/main.xcd内のノードパス。
    ca = cp.createInstanceWithArguments('com.sun.star.configuration.ConfigurationAccess', (node,))
    return ca.getPropertyValues(('ooName', 'ooSetupVersion'))  # LibreOfficeの名前とバージョンをタプルで返す。
   return wrapper
  @connectOffice  # createXSCRIPTCONTEXTの引数にctxとsmgrを渡すデコレータ。
  def createXSCRIPTCONTEXT(ctx, smgr):  # XSCRIPTCONTEXTを生成。
   class ScriptContext(unohelper.Base, XScriptContext):
    def __init__(self, ctx):
     self.ctx = ctx
    def getComponentContext(self):
     return self.ctx
    def getDesktop(self):
     return ctx.getByName('/singletons/com.sun.star.frame.theDesktop')  # com.sun.star.frame.Desktopはdeprecatedになっている。
    def getDocument(self):
     return self.getDesktop().getCurrentComponent()
   return ScriptContext(ctx)  
  return createXSCRIPTCONTEXT()  # XSCRIPTCONTEXTの取得。
 XSCRIPTCONTEXT = automation()  # XSCRIPTCONTEXTを取得。 
 macro()
16行目まででインプットストリームを取得するファイルをPythonで作成しています。

スクリプトがあるフォルダを起点にしているのでマクロでは実行できず、オートメーションでしか実行できません。

35行目のreadBytes()メソッドの第1引数は[out]なので空のリストを渡すと戻り値にsequence<byte>(Pythonではタプル)が入って返ってきます。

readBytes()メソッド自体の戻り値はint型で要素数が入っているので、readBytes()メソッドの戻り値は(int, バイトのタプル)のタプルで返ってきます。

その次の行でバイトのタプルをbytes(バイト列)に変換してutf-8でデコードして文字列を取得しています。

OpenCommandArgument2 StructのSinkアトリビュートにXActiveDataSinkインターフェイスをもったオブジェクトを渡すとインプットストリームが取得でき、XOutputStreamインターフェイスをもったオブジェクトを渡すとアウトプットストリームを渡せます(Documents - Apache OpenOffice Wiki)。

Javaの例:DataStreamComposerをPythonにする


この例は二つのファイルのFileContentをそれぞれ取得して、ストリームを使って一方のファイルの内容をもう一方に上書きします。
#!/opt/libreoffice5.4/program/python
# -*- coding: utf-8 -*-
import unohelper  # オートメーションには必須(必須なのはuno)。
import os
from datetime import datetime
from com.sun.star.ucb import OpenCommandArgument2  # Struct
from com.sun.star.ucb import OpenMode  # 定数
from com.sun.star.io import XActiveDataSink
from com.sun.star.ucb import Command  # Struct
from com.sun.star.ucb import InsertCommandArgument  # Struct
def macro():  # オートメーションでのみ実行可。マクロだとカレントディレクトリが使えない。
 t = datetime.now().isoformat().split(".")[0].replace("-", "").replace(":", "")  # コピー先ファイル名に使う年月日T時分秒を結合した文字列を取得。
 targetfile = "".join(("resource-", t))  # コピー先ファイル名。
 with open(targetfile, 'w', encoding="utf-8") as f: # コピー先のファイルの作成。
  f.write("This is the content of a sample data file.")  # コピー先ファイルの内容。これはコピー元の内容に上書きされて消える。
 if not os.path.exists(os.path.join("data", "data.txt")):  # コピー元ファイルが存在しない時。
  if not os.path.exists("data"):  # コピー元ファイルを入れているdataフォルダがない時。
   os.mkdir("data")  # dataフォルダを作成。
  os.chdir("data")  # dataフォルダに移動。
  with open("data.txt", 'w', encoding="utf-8") as f:  # コピー元のファイルの作成。
   f.write("sample sample sample EOF")  #  コピー元ファイルの内容。EOFはただの文字列。
  os.chdir("..")  # 元のフォルダに戻る。
 ctx = XSCRIPTCONTEXT.getComponentContext()  # コンポーネントコンテクストの取得。
 smgr = ctx.getServiceManager()  # サービスマネージャーの取得。
 print("""-----------------------------------------------------------------
DataStreamComposer - sets the data stream of a document resource.
The data stream is obtained from another (the source) document resource before.
-----------------------------------------------------------------""")
 pwd = unohelper.systemPathToFileUrl(os.getcwd())  # 現在のディレクトリのfileurlを取得。。
 targeturl = "/".join((pwd, targetfile))  # コピー先ファイルのfileurl。
 sourceurl =  "/".join((pwd, "data/data.txt"))  # コピー元ファイルのfileurlを取得。  
 ucb =  smgr.createInstanceWithContext("com.sun.star.ucb.UniversalContentBroker", ctx)  # UniversalContentBroker。
 content = ucb.queryContent(ucb.createContentIdentifier(sourceurl))  # fileurlからFileContentを取得。
 datasink = MyActiveDataSink()  # XActiveDataSinkを持ったクラスをインスタンス化。
 arg = OpenCommandArgument2(Mode=OpenMode.DOCUMENT, Priority=32768, Sink=datasink)  # SinkにXActiveDataSinkを持ったインスタンスを渡すとアクティブデータシンクとして使える。
 command = Command(Name="open", Handle=-1, Argument=arg)  # コマンド。
 content.execute(command, 0, None)  # コピー元ファイルについてコマンドを実行。setInputStream()でアクティブデータシンクにインプットストリームが渡される。
 inputstream = datasink.getInputStream()  # アクティブデータシンクからコピー元ファイルのインプットストリームを取得。
 content = ucb.queryContent(ucb.createContentIdentifier(targeturl))  # コピー先ファイルのFileContentを取得。
 insertcommandargument = InsertCommandArgument(Data=inputstream, ReplaceExisting=True)  # 上書きモードでストリームで置換させる。
 command = Command(Name="insert", Handle=-1, Argument=insertcommandargument)  # コマンド。
 content.execute(command, 0, None)  # コピー先ファイルについてコマンドを実行。
 print("""Setting data stream succeeded.
Source URL: {}
Target URL: {}""".format(sourceurl, targeturl))
このスクリプトもスクリプトがあるフォルダを起点にしているのでマクロでは実行できず、オートメーションでしか実行できません。

46行目以降に最初のスクリプトの40行目以降を追加する必要があります。

22行目まではコピー元とコピー先のファイルをPythonで作成しています。

最後にコピー元とコピー先のファイルのfileurlをprint()で出力していますが、コードではコピーに成功したかどうかは確認していません。

参考にしたサイト


LibreOffice 6.0 SDK - Developer's Guide Examples
DataStreamRetrieverとDataStreamComposerをPythonにしました。

Documents - Apache OpenOffice Wiki
OpenCommandArgument2 StructのSinkアトリビュートの解説。

python - Convert bytes to a string? - Stack Overflow
バイト列を文字列に変換する方法。

次の関連記事:LibreOffice5(108)ドキュメントストレージからファイルシステムストレージにデータをコピーする

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ