2012年2月5日日曜日

ブラウザベースのEPUB読み込み

ふと思いついた
達人出版会などがEPUB形式で配布しているのもあり、 タブレットではEPUBで読む機会も増えてきました。 PDFで読むのがまだ主流と思いますが、未来に行くならEPUBだ! とまぁこう思う所存でございます。※アンマオモッテナイデス 先日EPUBの圧縮方式をJavaでやりましたが、ふと思いついたんです。 ブラウザベースでEPUBを読み込んで、そのまま読めたら最高だと。 Adobeのリーダーインストールすんのか?とかいろいろ葛藤がある中、 ChromeExtensionで読めたら最高じゃない? ※クロスブラウジングしなくていいからじゃないだからね! って事で少し作成してみる事にする。
幸運なことに
先日のブログでも言いましたが、EPUBのファイルはZIP形式です。 先人によりZIP解凍してくれるライブラリがありました。 「JavaScriptでZIP解凍できるのかよ!」と驚きながらありがたやー。 ※これがあったから始めようと思ったりしたり、、、 って事で、それを使ってEPUBを解凍する事にした。 ファイルの指定はinputのfileに指定したらクライアント上で展開っていう形。
   //ファイルを変更された場合
   var fileData = document.getElementById("epubFile").files[0]
   var reader = new FileReader(); 

   //読み込みが完了したら
   reader.onload = function(evt){
          var bytes = [];
          var byteData = evt.target.result;
          //データを変換
          for (var i=0; i<byteData.length; i++)bytes[i] = byteData.charCodeAt(i) & 0xff;
          //epubファイルを解凍
          var epub = Zip.inflate(bytes);
   };
   reader.readAsBinaryString(fileData);
って感じでchangeイベント等で行えば良い。サーバが関係しないから思ったより簡単!速い! ・・・JavaScript速くなったなぁ、、、トオイメ。 Zip.inflate()によってfilesプロパティに内部のファイルにアクセスできるようになり、 files["ファイル名"]でFileクラスがもらえるので そこに生データがあったり、inflate()で解凍してくれたりする。 なので ・mimetypeの内容確認 mimetypeは非圧縮かどうかって確認まではしませんでした。 ・META-INF/container.xmlのOPF位置の確認を行う。 container.xmlに関しては文字列からDomにして確認
function changeDom(data) {
    var xmldom = new DOMParser();
    var dom = xmldom.parseFromString( data, "application/xml" );
 return dom;
}
・目次データの作成 container.xmlのopfファイル位置を取得してそれをDOMに変換して、 manifestなどを読み込んで配列データにしてファイル名を取っておく。 ここで難しいのは 圧縮ファイル名は「OEBPS/xxxx.xhtml」となっているけど、 XHTML上の指定は「xxxx.xhtml」という感じで相対パスになっているはず。 ※OPFファイルからの相対っていう仕様なのかな? OPFファイルを抜き出した時に「OEBPS」などのディレクトリ名を残しておいて XHTML内部のファイルの解析を行うときに使用できるようにしておく。
HTMLの表示、、、と問題。
目次から表示したいファイルやら何やらは取得できるのだけど、表示する際には少し工夫が必要で、 「xxxxx.html」はJavaScript上の「xxxx/xxxx.html」という名称で取得できかつ、その場には存在しない。 要は単純にリンク先は存在しない。 ・リンクをクリックされる。 ・HTMLを取得する ・それをHTML上に展開する って寸法になる。 クリックに対しては
<a href="javascript:void(0)" onclick="open('xxxx.xhtml'); return false;"></a>
見たいにやって関数をかませてやるといい。 それをinnerHTMLやらでそのまま表示。
(∩´∀`)∩ワーイ。ママ。ヒトリデデキタヨ。 ※サンプルは白鯨です。 そしてEPUBを見ていると異変に気づく。 スタイルシートあたってないわ、画像が表示できないわ、、、である。
<img src="images/xxxx.jpg">
Σ(゚д゚lll)ガーン、、、これも相対パスやないかい! Chromeのコンソールを見ると 「GET file:///C:/xxxx/EPubReader/css/stylesheet.css 」 と嘆かわしいログが出ている、、、トリニイカナイデ!
画像の埋め込み
バイナリデータが手元にあって、画像が表示できない苦悩(´・ω・`) サーバは偉大だ、、、と今更ながらに思うこの閉塞感。。。 imgタグのアクセスを擬似的にメモリデータに持ってこようとか 頭の中ではいろいろぐるぐるしたけど、 imgタグのsrcにバイナリを埋め込む方法がある事を知る。 細かいファイルとかをこうやって埋め込んでアクセス数を減らすテクがあるのか。。。 知らなかったけどこれ使える! zipやらutf8のライブラリやらjqueryやらでワラワラjsファイルが増えていってるけど クライアントのみである事を活かす為にはアクセス数など気にしない!base64.jsを追加。
マッテタヨ(∩´∀`)∩クジラタン!
またも問題発生
200k位の画像になってくると「"Maximum call stack size exceeded"」と出るのだ。 。。。。まぁスタック足りないだろうな、、、と1日置く。 原因はzipライブラリの59行目にあった。
var blob = String.fromCharCode.apply(null, this.data);
これに大きめのデータを突っ込むとアウトらしいので
  File.prototype.getBlob = function (dataArray) {
 var blob;
    try {
        blob = String.fromCharCode.apply(null,dataArray);
    } catch (err) {
     //データを分割して変換してつなげる
     var leng = dataArray.length;
     var dataLength = 100000;
     var loopNum = Math.ceil(leng / dataLength);
     var blobData = new Array();
     blobData[loopNum] = new Array();
 
     for ( var idx = 0; idx < loopNum; ++idx ) {
      var dataIdx = idx * dataLength;
      var splitData = dataArray.slice(dataIdx,dataIdx + dataLength);
      blobData[idx] = this.getBlob(splitData);
     }
     blob = blobData.join("");
    }
    return blob;
  };
って関数を追加してやった。 もちろんこれだと100000でスタックエラー出たら永久ループじゃん!こうすれば高速だ。 ってありそうだったけど、まぁ応急処置だとご勘弁を。 一応手元にある画像ではこれでエラーはなくなったけど、 多分何らかの問題はまた出てくるんだろうな。。。
公開までの道のり
・SVGファイル指定してあるとどうもダメみたい。 ・スタイルシートを当てないとダメ ・EPUBファイルがJavaScript依存とかしているとなんかもっと大変な気がしている。 ・YouTube埋め込んで合ったら、、、、大丈夫なのかな? など。まぁ公開までいけるかは微妙だけど、これで文章読む事位はできるようになる。 JavaScriptで何やってくれんねん。って感じですが、いろいろできるもんです。 ChromeStore上にEPUB系がまだないみたいなので 少し頑張ってみようかな?とは思っています。

1 件のコメント:

Yuzo Hebishima さんのコメント...

すみません。再度EPUB系を調べたらいっぱいありました><。