ImpressのすべてのページをPNGとして出力するプログラム
LibreOfficeKitは,
ここで紹介するプログラムはレンダリング機能のみを使います。プレゼンテーションツールであるImpressの,
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <vector>
#include <fstream>
#include <png++/png.hpp>
#define LOK_USE_UNSTABLE_API
#include <LibreOfficeKit/LibreOfficeKit.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
namespace lok_tools {
const std::string LO_PATH = "/usr/lib/libreoffice/program";
}
void usage(const char *name)
{
std::cerr << "Usage: " << name;
std::cerr << " [-p path-of-libreoffice] [-d dpi] ImpressFile BaseName";
std::cerr << std::endl;
}
int splitImpress(lok::Document *doc, std::string base, int dpi)
{
if (!doc || base.empty() || dpi == 0) return -1;
if (doc->getDocumentType() != LOK_DOCTYPE_PRESENTATION) {
std::cerr << "Error: Is not Impress file (type = ";
std::cerr << doc->getDocumentType() << ")" << std::endl;
return -1;
}
std::string extname = ".png";
for (int i = 0; i < doc->getParts(); ++i) {
long pageWidth, pageHeight;
doc->setPart(i);
doc->getDocumentSize(&pageWidth, &pageHeight);
/* 1 Twips = 1/1440 inch */
int canvasWidth = pageWidth * dpi / 1440;
int canvasHeight = pageHeight * dpi / 1440;
std::vector<unsigned char> pixmap(canvasWidth * canvasHeight * 4);
doc->paintTile(pixmap.data(), canvasWidth, canvasHeight, 0, 0,
pageWidth, pageHeight);
png::image <png::rgba_pixel> image(canvasWidth, canvasHeight);
for (int x = 0; x < canvasWidth; ++x) {
for (int y = 0; y < canvasHeight; ++y) {
image[y][x].red = pixmap[(canvasWidth * y + x) * 4];
image[y][x].green = pixmap[(canvasWidth * y + x) * 4 + 1];
image[y][x].blue = pixmap[(canvasWidth * y + x) * 4 + 2];
image[y][x].alpha = pixmap[(canvasWidth * y + x) * 4 + 3];
}
}
std::string filename = base + std::to_string(i) + extname;
image.write(filename);
}
return 0;
}
int main(int argc, char **argv)
{
int opt;
std::string lo_path = lok_tools::LO_PATH;
int dpi = 96;
while ((opt = getopt(argc, argv, "p:d:")) != -1) {
switch (opt) {
case 'p':
lo_path = std::string(optarg);
break;
case 'd':
dpi = atoi(optarg);
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
}
if (argc - optind < 2) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
const char *from_file = argv[optind++];
const char *base_name = argv[optind];
try {
lok::Office *lo = lok::lok_cpp_init(lo_path.c_str());
if (!lo) {
std::cerr << "Error: Failed to initialise LibreOfficeKit";
std::cerr << std::endl;
exit(EXIT_FAILURE);
}
lok::Document *doc = lo->documentLoad(from_file);
if (!doc) {
std::cerr << "Error: Failed to load document: ";
std::cerr << lo->getError() << std::endl;
delete lo;
exit(EXIT_FAILURE);
}
if (splitImpress(doc, base_name, dpi)) {
std::cerr << "Error: Failed to split document" << std::endl;
delete doc;
delete lo;
exit(EXIT_FAILURE);
}
delete doc;
delete lo;
} catch (const std::exception & e) {
std::cerr << "Error: " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
return 0;
}
ビルドする前にpng++のヘッダーファイルをインストールしてください。また,
$ sudo apt install libpng++-dev $ g++ lok_split.cpp -Wall -Werror -std=c++11 \ `pkg-config --cflags --libs libpng` -ldl -o lok_split
std::to_
を使いたかったので-std=c++11
を指定しています。LibreOfficeKitで必要というわけではありません。コマンドの使い方は次のとおりです。
$ ./lok_split Impressのファイル名 変換先の名前
「変換先の名前-d
」
実はLibreOfficeKitにおいて,#define LOK_
」
「lok::Document
」getDocumentType()
では,getParts()
ではImpressのスライド数,getPart()
では現在のスライド番号を取得でき,setPart()
ではスライドを移動できます。ちなみにCalcの場合は,
getDocumentSize()
を使うとページのサイズを取得できます。このときの単位は
paintTile()
メソッドを用いると,
inline void paintTile(unsigned char* pBuffer, /* 出力バッファのポインタ */
const int nCanvasWidth, /* pBufferの横方向のピクセルサイズ */
const int nCanvasHeight, /* pBufferの縦方向のピクセルサイズ */
const int nTilePosX, /* ページの出力する領域のX座標をTWIPで */
const int nTilePosY, /* ページの出力する領域のY座標をTWIPで */
const int nTileWidth, /* ページの出力する領域の幅をTWIPで */
const int nTileHeight) /* ページの出力する領域の高さをTWIPで */
今回はページ全体を出力したかったので,nTilePosX/
は0で,nTileWidth/
はgetDocumentSize()
で取得した値をそのまま使います。またpaintTile()
で出力されるフォーマットは現在のところRGBAのみとなります。一応getTileMode()
でフォーマットの種別を判定できます。
あとはpng::image
のインスタンスに1ピクセルずつコピーしてPNG画像にして保存しています
- ※3
- 実際はもっと効率の良い変換方法があるのかもしれませんが,
ここではベタな方法を使っています。
ちなみにlibreoffice
コマンドの--convert-to
」pdftoppm
コマンドを使うと良いでしょう。
$ libreoffice --headless --convert-to pdf foo.odp
$ pdftoppm -png foo.pdf slide