Phantom.jsとimageMagickで.html to .png to .pdf

年末のプレゼンに向けてこんなの作ってみた。 テスト一切書いてなかったり、Cakefileが微妙だったり、デザインがやっつけだったりまだまだ改善の余地ありですが、一旦完成。

  georgeOsdDev/SlideBase

express + socket.IOでスライドショーを共有するみたいな感じです。
  Jxck/SlideStream の劣化版といえばそれまでですが、 js,coffeescriptの勉強という意味で以下の分厚い本を読みつつ1から自分で作ってみた。


webサーバはexpressでadminログインしたプレゼンターの操作をsocket.IOでブロードキャストで基本的なアイデアや作りは冒頭申したとおり、  Jxck/SlideStream まんまなんですが、
以下の構成パッケージで、socket.IOとexpressでセッション共有する際にJxckさんのブログのままだと動かなかったのでcookieのとりかたは pxsta's Memo さんを参考にcookieのパースが必要みたい。

package.json


            "dependencies": {
              "express": "3.0.0",
              "ejs": ">= 0.0.1",
              "connect": "2.6.0",
              "socket.io": "0.9.10",
              "connect-redis": "~1.4.5",
              "cookie": "0.0.5",
              "underscore": "~1.4.2"
            }
            

expressサーバ側


            app.use util.parallel(
              express.methodOverride(), 
              express.session
                store: sessionStore
                secret: config.session.secret
                maxAge: false
                cookie:
                  httpOnly: false
              )
            

socket.IOサーバ側


            io.set 'authorization', (handshakeData, callback) ->
              if handshakeData.headers.cookie
                singedCookie = slideBaseUtil.parseCookie decodeURIComponent(handshakeData.headers.cookie)
                cookie = slideBaseUtil.parseSignedCookies singedCookie
                sessionID = cookie['connect.sid']
                store.get sessionID ,(err,session) ->
                  if err
                    log "err", err
                    callback err.message, false
                  else if !session
                    callback "session not found", false
                  else
                    log "session found"
                    handshakeData.cookie = cookie
                    handshakeData.sessionID = sessionID
                    handshakeData.session = session
                    callback null, true
              else
                callback "cookie not found", true
            

slideBaseUtilの中身はこんな感じ


            connect = require 'connect'
            cookie  = require 'cookie'
            
            parseCookie: (target) ->
              cookie.parse target
            parseSignedCookies: (singedCookie) ->
              connect.utils.parseSignedCookies singedCookie, config.session.secret
            

クライアントサイドでプラグインみたいに別ファイルのいろんなところからsocketを使えるようにするには

            execEmit: (name,data) ->
              console.log "execEmit "+name
              obj =
                name:'plugin'
                plugin:
                  name:name
                  data:data
              $('body').trigger 'execEmit', obj
            socket?.on 'connect', ->
              $('body').on 'execEmit', (event,obj) ->
                socket.emit obj.name, obj
            

みたいにbodyにイベント作ってそれを外から呼ぶみたいにしてみた。
あとはサーバ側にもpluginで受けたらそのままブロードキャストするようにかいて


            socket.on 'plugin', (data) ->
              socket.broadcast.volatile.emit "plugin", data
            

クライアントサイドはpluginを受けたらpluginとして登録してある関数を呼ぶみたいな感じにしておけば未定義のものが仮に送られてきても大丈夫みたいな感じ。


            socket.on 'plugin', (obj) ->
              if not obj.plugin or not obj.plugin.name then return false
              if not isEnableServerPush obj.plugin.name then return false
              func = sbClient.plugins[obj.plugin.name].get("callback") || {}
              func(obj.data)
            

さて、タイトルにあるとおり感動したのはPhantom.js。
このやり方もJxckさんの ブログ を参考にしたのですが、チョット違うのはPhantom.jsからクリックイベントを呼んでページ遷移させるとこと、sam2pとpdftkの代わりに imageMagick っていうツールを使いました。imageMagickを使うと、複数pngから一発で1枚のpdfに変換することができました。

Phantom.jsからクリックイベントを呼ぶには


            page.includeJs 'http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js',->
              offset = page.evaluate ->
                $('#next').offset()
              page.sendEvent 'click', offset.left+1, offset.top+1
            
みたいな感じで、叩きたいボタンや場所のoffsetを取得してpage.sendEvent('イベント',top,left)を呼べばOK。これでページ送りができますが、それぞれ非同期でやってる?ようなので、この直後にpage.render(outout)を呼んじゃうとまだクリックされてなかったりするのでsetTimeoutで微妙な調整がいるかも。sendEventの完了タイミングとれたりすると便利なのに。
さて、imageMagickインストールはhomebrew経由でカンタン。
            brew install imagemagick
            
これでこんな感じでいくつかコマンドが使えるようになります。
            22:26:33:oshidatakeharu@george-on-mba.local:/usr/local/Cellar/imagemagick/6.7.7-6/bin:master$ ll
            total 304
            -r-xr-xr-x  1 oshidatakeharu  admin  1290 Nov 11 12:08 Magick++-config
            -r-xr-xr-x  1 oshidatakeharu  admin  1256 Nov 11 12:08 Magick-config
            -r-xr-xr-x  1 oshidatakeharu  admin  1264 Nov 11 12:08 MagickCore-config
            -r-xr-xr-x  1 oshidatakeharu  admin  1269 Nov 11 12:08 MagickWand-config
            -r-xr-xr-x  1 oshidatakeharu  admin  1251 Nov 11 12:08 Wand-config
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 animate
            -r-xr-xr-x  1 oshidatakeharu  admin  9260 Nov 11 12:08 compare
            -r-xr-xr-x  1 oshidatakeharu  admin  9204 Nov 11 12:08 composite
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 conjure
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 convert
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 display
            -r-xr-xr-x  1 oshidatakeharu  admin  9260 Nov 11 12:08 identify
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 import
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 mogrify
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 montage
            -r-xr-xr-x  1 oshidatakeharu  admin  9196 Nov 11 12:08 stream
            
このうちconvertっていうのがファイル変換してくれるやつで、
            convert src/*.png dest/output.pdf
            
とかあるいは
            convert -append 1.png 2.png output.pdf
            
みたいな感じでpngが複数ページ構成のpdfファイルに1つにまとまります。
カンタンでイイね。
あーなんか断片的でまとまりのないエントリーですみません。

Comments

*