大量のJPGファイルをPDFファイルに変換して結合

スキャニングしてできた大量のJPGファイルを1個のPDFファイルに結合する場合、ImageMagickのconvertを使って

convert *.jpg output.pdf

とすれば簡単に作成できる。
しかしこの方法では、作成時に大量のメモリを消費してしまうのが難点。

私のPC環境で、約180個のJPGファイルを1個のPDFファイルに処理するのに約6分かかった。

処理中、物理メモリを約1GB消費、PF使用量が約1.5GB増加、CPU使用率は20%前後増加といった具合。

処理中、ハードディスクのアクセスランプが点灯しっぱなしになっていたから、使用可能な物理メモリが少なくなれば、もっと時間がかかるかもしれない。

で、あれこれ調べてみたところ、pdftkを使用すれば改善できそうなことがわかった。

pdftkのWindows版は、以下のサイトから入手できる。

pdftk – the pdf toolkit

pdftkを使って大量のJPGファイルをPDFファイルへ変換して、1個のPDFファイルへ結合する手順は、以下の通り。

  1. ImageMagicのconvertを使用して、JPGファイルを1個ずついくつかまとめてPDFファイルへ変換する。
  2. 手順1で作成したPDFファイルを結合する。
  3. 手順1で作成したPDFファイルを削除する。
  4. JPGファイルを削除する。

上記の処理するにあたっては、Perlを使用。ソースコードは、以下の通り。

#!/usr/local/bin/perl
use strict;
use warnings;

my @list = &getFileList;
my @pdf;
my @jpg = @list;
my $tmpPdf = "tmp000";

while (scalar(@jpg) > 0) {
    my $arg = join(" ", splice(@jpg, 0, 20));	#一度に結合するJPGファイルの個数
    &execute("convert $arg $tmpPdf.pdf");
    push(@pdf, "$tmpPdf.pdf");
    $tmpPdf++;
}

my $oldPdf = "";
my $newPdf = "new000";

while (scalar(@pdf) > 0) {
    my $arg = join(" ", splice(@pdf, 0, 20));	#一度に結合するPDFファイルの個数
    &execute("pdftk $oldPdf $arg cat output $newPdf.pdf");	#前回結合したPDFファイル($oldPdf)がある場合は、先頭に追加

    if (length($oldPdf) > 0) {
        unlink($oldPdf);
    }

    $oldPdf = $newPdf . ".pdf";
    $newPdf++;
    unlink split(/\s+/, $arg);
}

&execute("rename $oldPdf output.pdf");
unlink @list;
exit;

sub getFileList {
    open(DIR, "dir *.jpg |");
    my $list = join(" ", sort <DIR>);
    my @list = split(/\s+/, $list);
    close(DIR);

    open(DIR, "dir *.JPG |");
    $list = join(" ", sort <DIR>);
    push(@list, split(/\s+/, $list));
    close(DIR);

    sort @list;
}

sub execute {
    print $_[0], "\n";
    system($_[0]);
}

上記のスクリプトをコマンドプロンプトから実行してみた結果、CPU使用率は増えたものの、物理メモリ消費量とPF使用量についてはほとんど変化は見られず、かなりの改善が見られた。

時間的には、240個のJPGファイルをImageMagickのconvertだけで処理した場合、約3分15秒かかったのが、pdftkを併用した上記のスクリプトで処理した場合だと、約1分15秒となった。

ImageMagickのconvertを使って大量の画像ファイルを1個のPDFファイルにする際に時間がかかっている方は、上記のようにpdftkを併用する方法を一度試してみると良いかもしれない。

2010年06月26日(土) 21時38分  

データベース接続部分を比較してみた

ふと、自分が扱えるプログラミング言語でデータベースへ接続する部分を比較してみたくなったので、書いてみた。表示のさせ方はそれぞれ異なるけれど、取得しているデータはどのプログラミング言語も同じ。

接続先はデスクトップPC上のMySQLサーバーで、テーブルは去年PHPの勉強で作成したものを利用。

Perl

#!/usr/local/bin/perl

use strict;
use warnings;
binmode STDERR, ":encoding(shiftjis)";
$/ = "\r\n";

use Encode;
use Encode::Guess qw/euc-jp shiftjis 7bit-jis/;
use DBI;

#MySQLの設定
my $sql_server = 'localhost';
my $sql_user   = 'hogehoge';
my $sql_pass   = 'fizzbuzz';
my $sql_db     = 'dbname';

#MySQLのデータベースへ接続
my $db = DBI->connect("DBI:mysql:$sql_user:$sql_server", $sql_user, $sql_pass, {RaiseError => 0, PrintError => 1});

#クエリの準備
my $sth = $db->prepare("select id, name, address, tel, fax, mail, site, station from company");

#クエリーを実行して結果を取得
$sth->execute;
while (my @row = $sth->fetchrow_array) {
    #undef要素があれば、 "null" という文字列を代入(warning回避)
    @row = map {    $_ = (defined($_) ? $_ : "null")    } @row;
    #grep {    $_ = (defined($_) ? $_ : "null")    } @row;    #上の行は、grepを使っても書ける

    print encode("shiftjis", decode("Guess", join("\t", @row))), "\n";
}
$sth->finish;

#データベースから切断
$db->disconnect;

exit;

PHP

<?php
print '<?xml version="1.0" encoding="EUC_JP"?>'."\n";
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; Charset=euc-jp" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="Robots" content="noindex, nofollow" />
<meta name="Robots" content="noarchive" />

<title>db-connect</title>

<link type="text/css" rel="stylesheet" href="./default.css" />
<link type="text/css" rel="stylesheet" href="./default-style.css" />
</head>

<body>
<h1>db-connect</h1>

<hr />

<table>
<caption>Company List</caption>
<tr>
<th>id</th>
<th>name</th>
<th>address</th>
<th>tel</th>
<th>fax</th>
<th>mail</th>
<th>site</th>
<th>station</th>
</tr>

<?php
$sql_server = "localhost";
$sql_user = "hogehoge";
$sql_pass = "fizzbuzz";
$sql_db  = "dbname";

//MySQLのデータベースへ接続
$db = mysql_connect($sql_server, $sql_user, $sql_pass);

//データベースの選択
mysql_select_db($sql_db, $db);

//クエリの準備
$query = "select id, name, address, tel, fax, mail, site, station from company";

//クエリーを実行して結果を取得
$rows = mysql_query($query, $db);

//データベースから切断
mysql_close();

//検索された行数分ループ
while ($row = mysql_fetch_assoc($rows)) {
    print <<<TEXT_HTML

<tr>
<td>{$row['id']}</td>
<td>{$row['name']}</td>
<td>{$row['address']}</td>
<td>{$row['tel']}</td>
<td>{$row['fax']}</td>
<td>{$row['mail']}</td>
<td>{$row['site']}</td>
<td>{$row['station']}</td>
</tr>

TEXT_HTML;
}
?>

</table>

<hr />

</body>
</html>

JSP

<?xml version="1.0" encoding="EUC_JP"?>
<%@ page import="java.sql.*" %>
<%@ page language="java" contentType="text/html; charset=euc-jp" pageEncoding="euc-jp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; Charset=euc-jp" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="Robots" content="noindex, nofollow" />
<meta name="Robots" content="noarchive" />

<title>db-connect</title>

<link type="text/css" rel="stylesheet" href="./default.css" />
<link type="text/css" rel="stylesheet" href="./default-style.css" />
</head>

<body>
<h1>db-connect</h1>

<hr />

<table>
<caption>Company List</caption>
<tr>
<th>id</th>
<th>name</th>
<th>address</th>
<th>tel</th>
<th>fax</th>
<th>mail</th>
<th>site</th>
<th>station</th>
</tr>

<%
    String sql_server = "localhost";
    String sql_user = "hogehoge";
    String sql_pass = "fizzbuzz";
    String sql_db = "dbname";
    //String sql_url = "jdbc:mysql://localhost/" + sql_db;
    String sql_url = "jdbc:mysql://localhost/dbname?user=hogehoge&password=fizzbuzz";

    try {
        //JDBCドライバの読み込み
        Class.forName("com.mysql.jdbc.Driver").newInstance();

        //MySQLのデータベースへ接続
        //Connection db = DriverManager.getConnection(sql_url, sql_user, sql_pass);
        Connection db = DriverManager.getConnection(sql_url);

        //ステートメントオブジェクトの取得
        Statement stmt = db.createStatement();

        //クエリの準備
        String query = "select id, name, address, tel, fax, mail, site, station from company";

        //クエリーを実行して結果を取得
        ResultSet rs = stmt.executeQuery(query);

        //検索された行数分ループ
        while(rs.next()){
%>

<tr>
<td><%= rs.getInt("id") %></td>
<td><%= rs.getString("name") %></td>
<td><%= rs.getString("address") %></td>
<td><%= rs.getString("tel") %></td>
<td><%= rs.getString("fax") %></td>
<td><%= rs.getString("mail") %></td>
<td><%= rs.getString("site") %></td>
<td><%= rs.getString("station") %></td>
</tr>

<%
        }

        //データベースから切断
        stmt.close();

        //MySQLのデータベースとの接続を切断
        db.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
%>

</table>

<hr />

</body>
</html>

Javaアプリケーション

import java.sql.*;

public class DBConnect {
    public static void main(String[] args) {
        String sql_server = "localhost";
        String sql_user    = "hogehoge";
        String sql_pass    = "fizzbuzz";
        String sql_db    = "dbname";
        //String sql_url    = "jdbc:mysql://localhost/" + sql_db;
        String sql_url = "jdbc:mysql://localhost/dbname?user=hogehoge&password=fizzbuzz";

        try {
            //JDBCドライバの読み込み
            Class.forName("com.mysql.jdbc.Driver").newInstance();

            //MySQLのデータベースへ接続
            //Connection db = DriverManager.getConnection(sql_url, sql_user, sql_pass);
            Connection db = DriverManager.getConnection(sql_url);

            //目的のテーブルを操作

            //ステートメントオブジェクトの取得
            Statement stmt = db.createStatement();

            //クエリの準備
            String query = "select id, name, address from company";

            //クエリーを実行して結果を取得
            ResultSet rs = stmt.executeQuery(query);

            //検索された行数分ループ
            while (rs.next()) {
                System.out.printf("%2d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
                    rs.getInt("id"),
                    rs.getString("name"),
                    rs.getString("address"),
                    rs.getString("tel"),
                    rs.getString("fax"),
                    rs.getString("mail"),
                    rs.getString("site"),
                    rs.getString("station")
                );
            }

            //データベースから切断
            stmt.close();

            //MySQLのデータベースとの接続を切断
            db.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

こうしてみると、JavaアプリケーションとJSPはデータを表示させる部分が異なるだけで、データベースへ接続する処理は全く同じなのがわかる。異なるのは、JSPの場合はJDBCドライバを所定のディレクトリに用意しておくぐらい。

PHPはphp.iniの設定でデータベースを扱えるように変更、PerlはDBIモジュールとDBD::mysqlモジュールをuseすれば、それぞれデータベースにアクセス出来るようになる。

本当はRubyとExcelでも試してみたかったのだが、Rubyはまだ習得出来ておらず、またExcelはMyODBCは用意出来たのだが、データベースクエリが用意出来なかったので、今回は割愛。

今回試した中では、一番手軽にデータベースへ接続出来たのは、Perlだった。なんと言ってもモジュールさえ利用出来れば、接続出来てしまうというのが理由。

その次は、PHP。設定が必要だが、一度設定してしまえば、あとは関数をコールするだけというお手軽さ。実行速度の面もPerlと並んで申し分ない。

残るJSPとJavaアプリケーションは、コードの再利用性という面では一番かもしれないけれど、接続出来るようにするまでの手間は微妙、実行速度は早いと思う。

以上、独断と偏見に満ちた、私の環境での比較結果。

また時間が出来たら、次回はRubyとExcel、Javaアプレット等からデータベースへ接続できるかどうかも試してみたい。

2008年02月24日(日) 23時50分