PDFの検索結果に画像でスニペットを表示する実験

お蔵入りしていたコード。
PDFを検索したときの結果には画像でスニペットを返した方が見やすいかも、というアイディアを実現しようとしたもの。なぜ画像の方がいいかというと検索語近辺のテキストをスニペットにしても、表だったりPDFの中で文字列が表示される順に記述されていなかったときに脈絡がなくて役に立たないことがあるから。

さて、デモコードはまずユーザからURLで指定されたPDFファイルをサーバでダウンロード、テキストを抽出する。テキストにはPDF上での位置情報が付与されているので、それを含めてJSON形式でJavaScriptのコードに直接出力。文字列の検索はJavaScriptでやって、ハイライトは先の位置情報から画像の上にCSSで乗せる。

現状では、重い、PNGへの変換精度がいまいち、検索の機能が貧弱、等々実用化するまでには問題がたくさんある。

PDFからテキストを抽出、PNGに変換するところにはいくつか既存のアプリを使っている。

<?php
require_once 'Zend/Uri.php';
define('PDF_SAVE_PATH', '/tmp/pdfsearch/');
define('PNG_SAVE_PATH', '/var/www/zuzara.org/pdfsearch/png/');
dl('json.so');
$errorMessage = '';
$pdfUrl = (isset($_GET['pdfurl']) ? $_GET['pdfurl'] : '');
$seed   = '';
$images = array();
$data = array();
if ($pdfUrl != '') {
    try {
        $uri = Zend_Uri::factory($pdfUrl);
        if ($uri->valid() === true) {
            $seed = md5($pdfUrl);
            $pdfFile = PDF_SAVE_PATH . $seed . '.pdf';
            $txtFile = PDF_SAVE_PATH . "$seed.txt";
            if (!file_exists($pdfFile)) {
                `wget -q -O $pdfFile "$pdfUrl"`;
                `pdftoppm -f 1 -l 1 $pdfFile $pdfFile`;
                `mogrify -format png $pdfFile-*`;
                $cmd = "$pdfFile-*.png " . PNG_SAVE_PATH;
                `mv $cmd`;
                `/usr/local/src/xpdf-3.02/xpdf/pdftotext $pdfFile -f 1 -l 1 -cfg /etc/xpdfrc -layout -enc UTF-8 >$txtFile`;
                $buf = file_get_contents($txtFile);
                $frags = explode('*** line fragments ***', $buf);
                foreach ($frags as $i => $frag) {
                    $d = preg_match_all('@x=([\d\.]+)\.\.([\d\.]+) y=([\d\.]+)\.\.([\d\.]+) base=[\d\.]+ \', text: (.*?)\'@s', $frag, $m);
                    $data[$i] = array();
                    for ($j = 0; $j < $d; $j++) {
                        //$data[$i][] = array('text' => $m[5][$j], 'xMin' => $m[1][$j], 'xMax' => $m[2][$j], 'yMin' => $m[3][$j], 'yMax' => $m[4][$j], );
                        for ($k = 1; $k <= 4; $k++) {
                            $m[$k][$j] = intval($m[$k][$j]) * 2.084;
                        }
                        $data[$i][] = array('text' => $m[5][$j], 'top' => $m[3][$j], 'left' => $m[1][$j], 'width' => $m[2][$j] - $m[1][$j], 'height' => $m[4][$j] - $m[3][$j], );
                    }
                }
                file_put_contents($txtFile, serialize($data));
            } else {
                $data = unserialize(file_get_contents($txtFile));
            }
            foreach (new DirectoryIterator(PNG_SAVE_PATH) as $file) {
                if (strpos($file->getFilename(), $seed) !== false) {
                    $images[] = $file->getFilename();
                }
            }
            sort($images);
        } else {
            $pdfUrl = '';
        }
    } catch (Zend_Uri_Exception $e) {
        $errorMessage = $e->getMessage();
    }
}
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>pdfsearch</title>
<script type="text/javascript" src="jquery-1.2.6.min.js"></script>
<script type="text/javascript" src="jquery.dimensions.js"></script>
<script type="text/javascript">
data = <?php echo json_encode($data); ?>;
$(function() {
    $('#searchbox').submit(function() {
        var query = $(':text').val();
        var matched = 0;
        jQuery.each(data, function(i) {
                jQuery.each(data[i], function(j) {
                        if (data[i][j].text.match(query)) {
                            var t = $('#p-0').position().top + data[i][j].top;
                            $('#highlight').css({ top: t,
                                                  left: ($('#p-0').position().left + data[i][j].left),
                                                  width: data[i][j].width,
                                                  height: data[i][j].height,
                                                  });
                            $(window).scrollTop(t - 50);
                            $('#highlight').show('fast');
                            matched++;
                            return;
                        }
                    }
                );
            }
        );
        if (matched == 0) {
            alert("did not match.");
        }
        return false;
    });
});
</script>
<link rel="stylesheet" href="style.css" type="text/css" media="screen" />
</head>
<body>
<h1>pdfsearch</h1>
<?php if ($errorMessage != ''): ?>
<div id="errormessage"><?php echo $errorMessage; ?></div>
<?php endif; ?>
<?php if (count($images) > 0): ?>
<form id="searchbox" action="" method="GET">
<input type="text" name="search" />
<input type="submit" value="submit" />
</form>
<?php endif; ?>
<?php foreach ($images as $p => $image): ?>
<h2>page: <?php echo $p+1; ?></h2>
<img id="p-<?php echo $p; ?>" src="png/<?php echo $image; ?>" alt="" />
<?php break; /*XXX*/ endforeach; ?>
<form id="submitpdfurl" action="" method="GET">
pdf url:&nbsp;
<input type="text" id="pdfurl" name="pdfurl" value="<?php echo htmlspecialchars($pdfUrl, ENT_QUOTES); ?>" />
<input type="submit" value="Convert!" />
</form>
<div id="highlight"></div>
</body>
</html>
This entry was posted in いじる. Bookmark the permalink. Both comments and trackbacks are currently closed.

One Comment

  1. ozlitdth
    Posted 2013/08/29 at 3:30 pm | Permalink

    chanel バッグ

Page optimized by WP Minify WordPress Plugin