Clojure で PDFBox を使って PDF ファイルのサムネイル画像を作成する

Clojure を使って PDF ファイルのサムネイル画像を生成してみた。

Apache PDFBox – Apache PDFBox – Java PDF Library を使うと PDF ファイルからテキストや画像を抽出したり、ページのサムネイル画像を生成したりすることができるらしい。
利用には FontBox というライブラリも必要だったので、PDFBox と同じ場所(Apache PDFBox – Download)からダウンロードした。

さらに commons-logging というライブラリも必要になったので、Commons Logging – Overview からダウンロードして配置する。

Clojure プロジェクトの管理には Leiningen(technomancy’s leiningen at master – GitHub)を使用したので、lib ディレクトリにこれらの jar ライブラリを放り込んでおく。

パネルに画像を表示する方法は、『Clojure で JPanel に画像を表示させ、ボタンで画像を切り替える』に記載した方法で行なった。

サムネイル画像の生成には getScaledInstance を使った。

(ns pdf2img.core
  (:gen-class)
  (:use [clojure.contrib.str-utils :only (re-split)])
  (:import
     (org.apache.pdfbox.pdmodel PDDocument)
     (org.apache.pdfbox.pdfparser PDFParser)
     (java.awt Graphics Graphics2D Image Component Color)
     (java.awt.event ActionListener)
     (java.awt.image BufferedImage)
     (java.io File FileInputStream)
     (javax.imageio ImageIO)
     (javax.swing.filechooser FileNameExtensionFilter)
     (javax.swing JFrame JMenuBar JMenu JMenuItem JFileChooser JLabel JPanel JOptionPane)))

(def *width* 128)
(def *height* 180)
(def *pdf-file-path* (ref {:path ""}))
(def *img-buf*
  (ref {:buf (BufferedImage.
               *width*
               *height*
               BufferedImage/TYPE_BYTE_BINARY)}))

(defn exit []
  (System/exit 0))

; 拡張子を取り除く
(defn remove-ext [path]
  (first (re-split #"(?i)\.[^\.]*$" path)))

; 拡張子を変換
(defn convert-ext [path ext]
  (str (remove-ext path) "." ext))

(defn read-pdf-file [file-path]
  (with-open [pdf-stream (FileInputStream. file-path)]
    (do
      (let [pdf-parser (PDFParser. pdf-stream)]
        (.parse pdf-parser)
        (let [pdf (.getPDDocument pdf-parser)]
          pdf)))))

(defn bufimage-to-graphics2d [bufimage]
  (let [g2d (.createGraphics bufimage)]
    g2d))

; 画像のリサイズ処理
(defn resize [orig-image new-width]
  (let [orig-width (.getWidth orig-image)
        orig-height (.getHeight orig-image)
        resize-ratio (/ new-width orig-width)
        new-height (* orig-height resize-ratio)
        resize-image (BufferedImage. new-width new-height (.getType orig-image))]
    (do
      (.drawImage (.getGraphics resize-image)
                  (.getScaledInstance orig-image new-width new-height Image/SCALE_AREA_AVERAGING)
                  0 0 new-width new-height nil)
      resize-image)))

(defn render [g2d img]
  (.drawImage g2d img 0 0 nil))

(defn create-panel []
  (proxy [JPanel] []
    (paint [g2d]
      (do
        (.setColor g2d Color/WHITE)
        (.fillRect g2d 0 0 *width* *height*)
        (render g2d (@*img-buf* :buf))))))

(defn set-img-buf [img]
  (dosync (ref-set *img-buf* (assoc @*img-buf* :buf img))))

(defn set-image [panel img]
  (let [g2d (.getGraphics panel)]
    (do
      (.setColor g2d Color/WHITE)
      (.fillRect g2d 0 0 *width* *height*)
      (render g2d img)
      (set-img-buf img)
      (.dispose g2d))))

(defn -main [& args]
  (let [new-width *width*
        frame (JFrame. "PDF2IMG")
        image-panel (create-panel)
        menu-bar (JMenuBar.)
        menu-file (JMenu. "ファイル")
        menu-help (JMenu. "ヘルプ")
        menu-item-about (JMenuItem. "About...")
        menu-item-open (JMenuItem. "開く...")
        menu-item-save (JMenuItem. "保存する...")
        menu-item-exit (JMenuItem. "終了する")]
    ; open menu
    (.addActionListener menu-item-open
      (proxy [ActionListener] []
        (actionPerformed [evt]
          (do
            (let [fc (JFileChooser.)
                  ext-filter (FileNameExtensionFilter. "PDFファイル(*.pdf)" (into-array ["pdf"]))]
              (.setFileFilter fc ext-filter)
              (.showOpenDialog fc nil)
              (dosync (ref-set *pdf-file-path* (assoc @*pdf-file-path* :path (str (.getSelectedFile fc)))))
              (let [pdf (read-pdf-file (str (.getSelectedFile fc)))
                    pages (.. pdf getDocumentCatalog getAllPages)
                    page (first pages)
                    bufimage (.convertToImage page)
                    resized-image (resize bufimage new-width)]
                (dosync (ref-set *img-buf* (assoc @*img-buf* :buf resized-image)))
                (do
                  (.setBounds image-panel 100 20 228 230)
                  (set-image image-panel resized-image)
                  (.setEnabled menu-item-save true))))))))

    ; save menu
    (doto menu-item-save
      (.setEnabled false)
      (.addActionListener
        (proxy [ActionListener] []
          (actionPerformed [evt]
            (do
              (let [fc (JFileChooser.)
                    ext-filter (FileNameExtensionFilter. "PNGファイル(*.png)" (into-array ["png"]))]
                (.setFileFilter fc ext-filter)
                (.setSelectedFile fc (File. (convert-ext (@*pdf-file-path* :path) "png")))
                (let [selected (.showSaveDialog fc nil)]
                  (if (= selected JFileChooser/APPROVE_OPTION)
                    (ImageIO/write (@*img-buf* :buf) "png" (File. (str (.getSelectedFile fc))))))))))))

    ; about menu
    (doto menu-item-about
      (.addActionListener
        (proxy [ActionListener] []
          (actionPerformed [evt]
            (JOptionPane/showMessageDialog
              nil
              "PDF2IMGは、PDFファイルのサムネイルを作成します。"
              "About"
              JOptionPane/PLAIN_MESSAGE)))))

    ; exit menu
    (.addActionListener menu-item-exit
      (proxy [ActionListener] []
        (actionPerformed [evt]
          (exit))))

    ; set menu-bar
    (doto menu-bar
      (.add menu-file)
      (.add menu-help))

    (doto menu-file
      (.add menu-item-open)
      (.add menu-item-save)
      (.add menu-item-exit))

    (doto menu-help
      (.add menu-item-about))

    ; set frame
    (doto frame
      (.setLayout nil)
      (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
      (.setJMenuBar menu-bar)
      (.add image-panel)
      (.setSize 330 300)
      (.setResizable false)
      (.setVisible true))))

実行結果のスクリーンショット

Clojure で PDF ファイルのサムネイルを生成した Screenshot

プログラミングClojure
プログラミングClojure

posted with amazlet at 10.05.19
Stuart Halloway
オーム社
売り上げランキング: 8244
«
»