関連記事一覧
LibreOfficeでPDF文書を自動生成 (この記事)
https://qiita.com/nanbuwks/items/8ea939ad497fec29c069
LibreOffice で MySQL連動 PDF帳票を作る
https://qiita.com/nanbuwks/items/d4696542760cf4b8a24a
WebアプリでPDF文書を自動生成 on Ubuntu14.04 LTS
https://qiita.com/nanbuwks/items/7dbd2a0a79b232f864b4
WebアプリでPDF文書を自動生成 on Ubuntu20.04 LTS
https://qiita.com/nanbuwks/items/f11e4e3aabac33f9a32b
後からわかったこと
javaを使ったシステムの修復をしていましたが、こんなことしなくても今では
$ soffice --headless --convert-to pdf *.ods
とするだけでOKだったです。
やれやれ。
(2021/03/25 追記: 上記のやり方を使った記事を書きました)
「LibreOffice で MySQL連動 PDF帳票を作る」
https://qiita.com/nanbuwks/items/d4696542760cf4b8a24a
以下ムダな努力
10年以上前に、伝票をWebから自動生成したシステムを作っていた。
サーバを引っ越した時にこの仕組みは動かなくなっていたので修復。
ローカルのPCで動かす
まずはローカルのPCで動くところまで。
これがうまくいったら、Webシステムで動作するところまでやる予定。
過去のWebシステムとの違い
過去のWebシステムは2006年から稼働。
X-WindowとOpenOffice.orgを動作させていた。
なのだけれど基本sshなどでの操作前提のサーバだったので、OpenOffice.orgは
DISPLAY変数を指定してスクリプトで起動していました。
ちなみに、X-Windowのスクリーンセーバでリソース食われたりしてパフォーマンス低下したのもいい思い出です。
- Ubuntu6ぐらい
- OpenOffice.org
さて、現在はUbuntuは16ぐらい。OpenOffice.orgはLibreOfficeとして動かしたい。
今回動作させるローカルのPCにはリアルなX-Windowが動いているのでLibreOfficeを動かすのには問題がありまんが、この後動かすWebシステムは、リアルなX-windowを動かさない方法にする予定です。
javaのプログラムをサルベージ
昔作ったクラス。
PDFConverter.class
これはOpenOfficeで作ったodsファイルをPDFに変換して保存するもの。
odsファイルは、あらかじめ雛形を作っておき、XMLをコンテンツに応じて書き換えて生成する。
javaソースを復元
ソースが無かったので復元。
http://qiita.com/nanbuwks/items/76151b888dc8ca537228
「Java をディスコンパイル」
import com.sun.star.beans.PropertyValue;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.XCloseable;
import java.io.File;
public class PDFConverter
{
public static void main(String[] paramArrayOfString)
{
try
{
String str1 = convertToURL(paramArrayOfString[0]);
String str2 = getFilterName(paramArrayOfString[0]);
String str3 = str1.replaceAll("\\..{3}\\Z", ".pdf");
XComponentContext localXComponentContext = Bootstrap.bootstrap();
XMultiComponentFactory localXMultiComponentFactory = localXComponentContext.getServiceManager();
Object localObject1 = localXMultiComponentFactory.createInstanceWithContext("com.sun.star.frame.Desktop", localXComponentContext);
XComponentLoader localXComponentLoader = (XComponentLoader)UnoRuntime.queryInterface(XComponentLoader.class, localObject1);
PropertyValue[] arrayOfPropertyValue = new PropertyValue[2];
arrayOfPropertyValue[0] = new PropertyValue();
arrayOfPropertyValue[0].Name = "Hidden";
arrayOfPropertyValue[0].Value = new Boolean(true);
arrayOfPropertyValue[1] = new PropertyValue();
arrayOfPropertyValue[1].Name = "ReadOnly";
arrayOfPropertyValue[1].Value = new Boolean(true);
XComponent localXComponent1 = localXComponentLoader.loadComponentFromURL(str1, "_blank", 0, arrayOfPropertyValue);
arrayOfPropertyValue[0] = new PropertyValue();
arrayOfPropertyValue[0].Name = "FilterName";
arrayOfPropertyValue[0].Value = str2;
arrayOfPropertyValue[1] = new PropertyValue();
arrayOfPropertyValue[1].Name = "Overwrite";
arrayOfPropertyValue[1].Value = new Boolean(true);
XStorable localXStorable = (XStorable)UnoRuntime.queryInterface(XStorable.class, localXComponent1);
localXStorable.storeToURL(str3, arrayOfPropertyValue);
XCloseable localXCloseable = (XCloseable)UnoRuntime.queryInterface(XCloseable.class, localXStorable);
if (localXCloseable != null)
{
localXCloseable.close(false);
}
else
{
XComponent localXComponent2 = (XComponent)UnoRuntime.queryInterface(XComponent.class, localXStorable);
localXComponent2.dispose();
}
}
catch (Exception localException)
{
localException.printStackTrace();
System.exit(1);
}
finally
{
System.exit(0);
}
}
private static String convertToURL(String paramString)
throws Exception
{
File localFile = new File(paramString);
StringBuffer localStringBuffer = new StringBuffer("file:///");
localStringBuffer.append(localFile.getCanonicalPath().replace('\\', '/'));
return localStringBuffer.toString();
}
確か、このプログラムはサンプルプログラムを元にしたもの。
サンプルプログラムは後に述べるlibreoffice-dev-docをインストールすると /usr/share/doc/libreoffice-dev-doc/examples/java/DocumentHandling にある。ライセンスはBSDライセンス。
$ cat DocumentConverter.java
/*************************************************************************
*
* The Contents of this file are made available subject to the terms of
* the BSD license.
*
* Copyright 2000, 2010 Oracle and/or its affiliates.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Sun Microsystems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*************************************************************************/
import com.sun.star.uno.UnoRuntime;
import java.io.File;
/** The class <CODE>DocumentConverter</CODE> allows you to convert all documents
* in a given directory and in its subdirectories to a given type. A converted
* document will be created in the same directory as the origin document.
*
*/
public class DocumentConverter {
/** Containing the loaded documents
*/
static com.sun.star.frame.XComponentLoader xCompLoader = null;
/** Containing the given type to convert to
*/
static String sConvertType = "";
/** Containing the given extension
*/
static String sExtension = "";
/** Containing the current file or directory
*/
static String sIndent = "";
/** Containing the directory where the converted files are saved
*/
static String sOutputDir = "";
/** Traversing the given directory recursively and converting their files to
* the favoured type if possible
* @param fileDirectory Containing the directory
*/
static void traverse( File fileDirectory ) {
// Testing, if the file is a directory, and if so, it throws an exception
if ( !fileDirectory.isDirectory() ) {
throw new IllegalArgumentException(
"not a directory: " + fileDirectory.getName()
);
}
// Prepare Url for the output directory
File outdir = new File(DocumentConverter.sOutputDir);
String sOutUrl = "file:///" + outdir.getAbsolutePath().replace( '\\', '/' );
System.out.println("\nThe converted documents will stored in \""
+ outdir.getPath() + "!");
System.out.println(sIndent + "[" + fileDirectory.getName() + "]");
sIndent += " ";
// Getting all files and directories in the current directory
File[] entries = fileDirectory.listFiles();
// Iterating for each file and directory
for ( int i = 0; i < entries.length; ++i ) {
// Testing, if the entry in the list is a directory
if ( entries[ i ].isDirectory() ) {
// Recursive call for the new directory
traverse( entries[ i ] );
} else {
// Converting the document to the favoured type
try {
// Composing the URL by replacing all backslashs
String sUrl = "file:///"
+ entries[ i ].getAbsolutePath().replace( '\\', '/' );
// Loading the wanted document
com.sun.star.beans.PropertyValue propertyValues[] =
new com.sun.star.beans.PropertyValue[1];
propertyValues[0] = new com.sun.star.beans.PropertyValue();
propertyValues[0].Name = "Hidden";
propertyValues[0].Value = Boolean.TRUE;
Object oDocToStore =
DocumentConverter.xCompLoader.loadComponentFromURL(
sUrl, "_blank", 0, propertyValues);
// Getting an object that will offer a simple way to store
// a document to a URL.
com.sun.star.frame.XStorable xStorable =
UnoRuntime.queryInterface(
com.sun.star.frame.XStorable.class, oDocToStore );
// Preparing properties for converting the document
propertyValues = new com.sun.star.beans.PropertyValue[2];
// Setting the flag for overwriting
propertyValues[0] = new com.sun.star.beans.PropertyValue();
propertyValues[0].Name = "Overwrite";
propertyValues[0].Value = Boolean.TRUE;
// Setting the filter name
propertyValues[1] = new com.sun.star.beans.PropertyValue();
propertyValues[1].Name = "FilterName";
propertyValues[1].Value = DocumentConverter.sConvertType;
// Appending the favoured extension to the origin document name
int index1 = sUrl.lastIndexOf('/');
int index2 = sUrl.lastIndexOf('.');
String sStoreUrl = sOutUrl + sUrl.substring(index1, index2 + 1)
+ DocumentConverter.sExtension;
// Storing and converting the document
xStorable.storeAsURL(sStoreUrl, propertyValues);
// Closing the converted document. Use XCloseable.close if the
// interface is supported, otherwise use XComponent.dispose
com.sun.star.util.XCloseable xCloseable =
UnoRuntime.queryInterface(
com.sun.star.util.XCloseable.class, xStorable);
if ( xCloseable != null ) {
xCloseable.close(false);
} else {
com.sun.star.lang.XComponent xComp =
UnoRuntime.queryInterface(
com.sun.star.lang.XComponent.class, xStorable);
xComp.dispose();
}
}
catch( Exception e ) {
e.printStackTrace(System.err);
}
System.out.println(sIndent + entries[ i ].getName());
}
}
sIndent = sIndent.substring(2);
}
/** Bootstrap UNO, getting the remote component context, getting a new instance
* of the desktop (used interface XComponentLoader) and calling the
* static method traverse
* @param args The array of the type String contains the directory, in which
* all files should be converted, the favoured converting type
* and the wanted extension
*/
public static void main( String args[] ) {
if ( args.length < 3 ) {
System.out.println("usage: java -jar DocumentConverter.jar " +
"\"<directory to convert>\" \"<type to convert to>\" " +
"\"<extension>\" \"<output_directory>\"");
System.out.println("\ne.g.:");
System.out.println("usage: java -jar DocumentConverter.jar " +
"\"c:/myoffice\" \"swriter: MS Word 97\" \"doc\"");
System.exit(1);
}
com.sun.star.uno.XComponentContext xContext = null;
try {
// get the remote office component context
xContext = com.sun.star.comp.helper.Bootstrap.bootstrap();
System.out.println("Connected to a running office ...");
// get the remote office service manager
com.sun.star.lang.XMultiComponentFactory xMCF =
xContext.getServiceManager();
Object oDesktop = xMCF.createInstanceWithContext(
"com.sun.star.frame.Desktop", xContext);
xCompLoader = UnoRuntime.queryInterface(com.sun.star.frame.XComponentLoader.class,
oDesktop);
// Getting the given starting directory
File file = new File(args[0]);
// Getting the given type to convert to
sConvertType = args[1];
// Getting the given extension that should be appended to the
// origin document
sExtension = args[2];
// Getting the given type to convert to
sOutputDir = args[3];
// Starting the conversion of documents in the given directory
// and subdirectories
traverse(file);
System.exit(0);
} catch( Exception e ) {
e.printStackTrace(System.err);
System.exit(1);
}
}
}
環境を整える
Ubuntu Linux 16.04 において、
Java のインストール
今のJava環境、ちょっと複雑で良くわからない。動かそうとしたら
$ java
プログラム 'java' は以下のパッケージで見つかりました:
* default-jre
* gcj-5-jre-headless
* openjdk-8-jre-headless
* gcj-4.8-jre-headless
* gcj-4.9-jre-headless
* openjdk-9-jre-headless
次の操作を試してください: sudo apt install <選択したパッケージ>
うーん、default-jreをインストールしてみよう。
$ sudo apt-get install default-jre
[sudo] nanbuwks のパスワード:
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
gyp libatk-wrapper-java libatk-wrapper-java-jni libbonobo2-0 libbonobo2-common libgnome-2-0 libgnome2-common
libjs-inherits libjs-node-uuid liborbit-2-0 libssl-dev libssl-doc libuv1 libuv1-dev linux-headers-4.4.0-57
linux-headers-4.4.0-57-generic linux-headers-4.4.0-59 linux-headers-4.4.0-59-generic linux-headers-4.4.0-62
linux-headers-4.4.0-62-generic linux-headers-4.4.0-63 linux-headers-4.4.0-63-generic linux-headers-4.4.0-64
linux-headers-4.4.0-64-generic linux-headers-4.4.0-66 linux-headers-4.4.0-66-generic linux-headers-4.4.0-71
linux-headers-4.4.0-71-generic linux-headers-4.4.0-72 linux-headers-4.4.0-72-generic linux-headers-4.4.0-75
linux-headers-4.4.0-75-generic linux-headers-4.4.0-78 linux-headers-4.4.0-78-generic linux-headers-4.4.0-79
linux-headers-4.4.0-79-generic linux-headers-4.4.0-81 linux-headers-4.4.0-81-generic linux-headers-4.4.0-83
linux-headers-4.4.0-83-generic linux-headers-4.4.0-87 linux-headers-4.4.0-87-generic linux-image-4.4.0-57-generic
linux-image-4.4.0-59-generic linux-image-4.4.0-62-generic linux-image-4.4.0-63-generic linux-image-4.4.0-64-generic
linux-image-4.4.0-66-generic linux-image-4.4.0-71-generic linux-image-4.4.0-72-generic linux-image-4.4.0-75-generic
linux-image-4.4.0-78-generic linux-image-4.4.0-79-generic linux-image-4.4.0-81-generic linux-image-4.4.0-83-generic
linux-image-4.4.0-87-generic linux-image-extra-4.4.0-57-generic linux-image-extra-4.4.0-59-generic
linux-image-extra-4.4.0-62-generic linux-image-extra-4.4.0-63-generic linux-image-extra-4.4.0-64-generic
linux-image-extra-4.4.0-66-generic linux-image-extra-4.4.0-71-generic linux-image-extra-4.4.0-72-generic
linux-image-extra-4.4.0-75-generic linux-image-extra-4.4.0-78-generic linux-image-extra-4.4.0-79-generic
linux-image-extra-4.4.0-81-generic linux-image-extra-4.4.0-83-generic linux-image-extra-4.4.0-87-generic
これを削除するには 'sudo apt autoremove' を利用してください。
以下の追加パッケージがインストールされます:
default-jre-headless
提案パッケージ:
default-java-plugin
以下のパッケージが新たにインストールされます:
default-jre default-jre-headless
アップグレード: 0 個、新規インストール: 2 個、削除: 0 個、保留: 250 個。
5,360 B のアーカイブを取得する必要があります。
この操作後に追加で 28.7 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://jp.archive.ubuntu.com/ubuntu xenial/main amd64 default-jre-headless amd64 2:1.8-56ubuntu2 [4,380 B]
取得:2 http://jp.archive.ubuntu.com/ubuntu xenial/main amd64 default-jre amd64 2:1.8-56ubuntu2 [980 B]
5,360 B を 0秒 で取得しました (14.3 kB/s)
以前に未選択のパッケージ default-jre-headless を選択しています。
(データベースを読み込んでいます ... 現在 794349 個のファイルとディレクトリがインストールされています。)
.../default-jre-headless_2%3a1.8-56ubuntu2_amd64.deb を展開する準備をしています ...
default-jre-headless (2:1.8-56ubuntu2) を展開しています...
以前に未選択のパッケージ default-jre を選択しています。
.../default-jre_2%3a1.8-56ubuntu2_amd64.deb を展開する準備をしています ...
default-jre (2:1.8-56ubuntu2) を展開しています...
default-jre-headless (2:1.8-56ubuntu2) を設定しています ...
default-jre (2:1.8-56ubuntu2) を設定しています ...
としたがうまくいかない
$ java PDFConverter 1500336208000000004.ods
プログラム 'java' は以下のパッケージで見つかりました:
* default-jre
* gcj-5-jre-headless
* openjdk-8-jre-headless
* gcj-4.8-jre-headless
* gcj-4.9-jre-headless
* openjdk-9-jre-headless
次の操作を試してください: sudo apt install <選択したパッケージ>
$ jre PDFConverter 1500336208000000004.ods
コマンド 'jre' は見つかりませんでした。もしかして:
コマンド 're' - パッケージ 're' (universe)
コマンド 'joe' - パッケージ 'joe' (universe)
コマンド 'joe' - パッケージ 'joe-jupp' (universe)
jre: コマンドが見つかりません
仕方がないのでopenjdk-8を明示的にインストール・・・したがなんだか調子が悪いので更に remove して再度install
$ sudo apt-get install openjdk-8-jdk openjdk-8-jre openjdk-8-jre-headless
$ sudo apt-get remove openjdk-8-jdk openjdk-8-jre openjdk-8-jre-headless
パッケージリストを読み込んでいます... 完了
$ sudo apt-get install openjdk-8-jdk openjdk-8-jre openjdk-8-jre-headless
これで、java自体は動作するようになりました。
待ち受け
soffice with "-accept=socket,host=localhost,port=8100;urp" option
とドキュメントに書いてあったけれども、
$ soffice -accept=socket,host=localhost,port=8100;urp
コマンド 'urp' は見つかりませんでした。もしかして:
コマンド 'arp' - パッケージ 'net-tools' (main)
コマンド 'unp' - パッケージ 'unp' (universe)
コマンド 'ur' - パッケージ 'libur-perl' (universe)
コマンド 'burp' - パッケージ 'burp' (universe)
コマンド 'rup' - パッケージ 'rstat-client' (universe)
urp: コマンドが見つかりません
となる。ダブルクォーテーション付きでしないといけないのね。
$ soffice "-accept=socket,host=localhost,port=8100;urp"
別端末で、
$ netstat -a
稼働中のインターネット接続 (サーバと確立)
Proto 受信-Q 送信-Q 内部アドレス 外部アドレス 状態
tcp 0 0 localhost:8100 *:* LISTEN
・
・
・
待ち受けしてます。
動かしてみる→失敗
$ java PDFConverter 1500336208000000004.ods
$
何も起こらずに終了。
元々のシステムの呼び出しスクリプトを見てみる。
$ cat PDFConverterWenv.sh
export CLASSPATH=.:/usr/lib/openoffice/program/classes/juh.jar:/usr/lib/openoffice/program/classes/jurt.jar:/usr/lib/openoffice/program/classes/ridl.jar:/usr/lib/openoffice/program/classes/unoloader.jar:/usr/lib/openoffice/program/classes/unoil.jar:/usr/lib/openoffice/program
export OFFICE_PROGRAM_PATH=/usr/bin
export OO_SDK_CPP_HOME=/usr/bin
export OO_SDK_HOME=/usr/lib/openoffice_sdk
export OO_SDK_JAVA_HOME=/usr//bin/java
export OO_SDK_MAKE_HOME=/usr/bin
export OO_SDK_NAME=openoffice_sdk
export OO_SDK_OUTPUT_DIR=/home/nanbuwks
export OO_SDK_URE_BIN_DIR=/usr/lib/openoffice/program
export OO_SDK_URE_HOME=
export OO_SDK_URE_JAVA_DIR=/usr/lib/openoffice/program/classes
export OO_SDK_URE_LIB_DIR=/usr/lib/openoffice/program
export OO_SDK_ZIP_HOME=/usr/bin
export export DISPLAY=localhost:0
# set
cp ../$1.ods .
/usr/bin/java PDFConverter $1.ods
sdkとかないといけなかったかな?
sdkインストール
LibreOfficeのSDKってどんな名前?
パッケージを探してみる
$ sudo apt-cache search LibreOffice-SDK
見つからない
$ sudo apt-cache search Libreoffice | grep -i sdk
libreoffice-dev - office productivity suite -- SDK
libreoffice-dev-doc - office productivity suite -- SDK documentation
なるほど。ではこれらをインストール。
$ sudo apt-get install libreoffice-dev libreoffice-dev-doc
先ほどのPDFConverterWebv.shをLibreOffice用に書き換える。
$ cat PDFConverterWenv.sh
export CLASSPATH=.:/usr/lib/libreoffice/program/classes/juh.jar:/usr/lib/libreoffice/program/classes/jurt.jar:/usr/lib/libreoffice/program/classes/ridl.jar:/usr/lib/libreoffice/program/classes/unoloader.jar:/usr/lib/libreoffice/program/classes/unoil.jar:/usr/lib/libreoffice/program
export OFFICE_PROGRAM_PATH=/usr/bin
export OO_SDK_CPP_HOME=/usr/bin
export OO_SDK_HOME=/usr/lib/libreoffice/sdk
export OO_SDK_JAVA_HOME=/usr//bin/java
export OO_SDK_MAKE_HOME=/usr/bin
export OO_SDK_NAME=libreoffice_sdk
export OO_SDK_OUTPUT_DIR=/home/nanbuwks
export OO_SDK_URE_BIN_DIR=/usr/lib/libreoffice/program
export OO_SDK_URE_HOME=
export OO_SDK_URE_JAVA_DIR=/usr/lib/libreoffice/program/classes
export OO_SDK_URE_LIB_DIR=/usr/lib/libreoffice/program
export OO_SDK_ZIP_HOME=/usr/bin
export export DISPLAY=localhost:0
# set
cp ../$1.ods .
/usr/bin/java PDFConverter $1.ods
変換
$./PDFConverterWenv.sh 1500336208000000004
$ ls -alh 1500336208000000004.pdf
-rw-rw-r-- 1 nanbuwks nanbuwks 124K 8月 14 18:02 1500336208000000004.pdf
できた。