このページの本文へ

JavaScriptでEPUBビューアーを自作してみた (5/7)

2010年11月15日 10時00分更新

文●古籏一浩

  • この記事をはてなブックマークに追加
本文印刷

content.opfファイルを解析する

 サンプル2はEPUB内の特定のXHTMLしか表示できませんが、実際のEPUB形式ではXHTMLのファイル名やディレクトリ階層を自由に指定できます。

 EPUBファイル内で使用されるXHTMLファイルやページの順番は「content.opf」というXMLファイルに記述されており、たとえば今回のサンプルデータの場合は以下のようになっています。

■content.opfの内容

<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookID" version="2.0">
  <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
    <dc:identifier id="BookID" opf:scheme="UUID">fcdbe9e3-a185-40ee-8a61-920d69c69db7</dc:identifier>
    <meta name="Sigil version" content="0.2.3"/>
  </metadata>
  <manifest>
    <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
    <item id="Section0001.xhtml" href="Text/Section0001.xhtml" media-type="application/xhtml+xml"/>
  </manifest>
  <spine toc="ncx">
    <itemref idref="Section0001.xhtml"/>
  </spine>
</package>

 使用するファイルはmanifest要素内にあるitem要素に、ページの表示順序はspine要素内のitemref要素に記述されています。spine要素内のitemref要素に記述されているidref属性のIDを読み出し、同じIDを持つitem要素をmanifest要素内から探し出すと、表示するXHTMLファイルを特定できます。実際に表示するXHTMLファイルはitem要素のhref属性に書かれています。

 content.opfのXMLデータを解析するプログラム(analysisXML関数)を作成します。XMLデータ処理を簡素化するため、以下のようにして文字列として格納されているXMLをページ上に流し込みます。これによってDOMとして取り扱えるようになり、getElementsByTagName()などを使ってアクセスできます。

document.getElementById("epubxml").innerHTML = xmldata;
var epubXML = document.getElementById("epubxml");

【図】fig08.png

XMLデータが流し込まれる

 ファイル情報が記述されているmanifest要素内のitem要素には、以下のようにしてアクセスできます。変数itemTagにはitem要素が配列として入ります。

var itemTag = epubXML.getElementsByTagName("manifest")[0].getElementsByTagName("item");

 ページの表示順序が記述されているspine要素内のitemref要素は以下のようにして取得します。変数indexItemにはitemref要素が配列として入ります。

var indexItem = epubXML.getElementsByTagName("spine")[0].getElementsByTagName("itemref");

 次に、itemref要素のidref属性に記述されているIDを読み出します。以下のようにgetAttribute()を使います。

var ID = indexItem[i].getAttribute("idref");

 あとはfor()を使って同じIDが見つかるまで繰り返します。以下の例ではエラー処理はしていませんが、EPUBデータが間違っていない限り必ず対応するIDがあります。

for(var i=0; i<indexItem.length; i++){
  var ID = indexItem[i].getAttribute("idref");  // IDを読み出す
  for(var j=0; j<itemTag.length; j++){
    var cID = itemTag[j].getAttribute("id");  // IDを読み出す
    if (ID == cID){
      fileList.push(itemTag[j].getAttribute("href"));  // href属性を読み出す
      break// ループから抜ける
    }
  }
}

 content.opfファイルの解析が終わってファイルリストを取得できたら、最初のXHTMLファイルだけ表示してみましょう。以下のようになります。

xhtmlList = analysisXML(zip.files["OEBPS/content.opf"].data); // XMLデータ解析
var text = zip.files["OEBPS/"+xhtmlList[0]].data; // 最初のページを表示
document.getElementById("result").innerHTML = Utf8.decode(text);

【図】fig09.png

EPUBデータので指定された最初のXHTMLファイルが表示される

ここまでの処理をまとめたのがサンプル3です。

■サンプル3

<!DOCTYPE html> 
<html> 
<head> 
<meta charset="UTF-8" /> 
<title>EPUBデータの最初のページだけを表示</title>
<script type="text/javascript" src="js/sjis.js" charset="shift_jis"></script>
<script type="text/javascript" src="js/zip.js" charset="shift_jis"></script>
<script type="text/javascript" src="js/utf8.js" charset="utf-8"></script>
<script>
window.addEventListener("load", function(){
  document.getElementById("viewEPUB").addEventListener("click", function(){
    document.getElementById("result").innerHTML = "";
    var fileData = document.getElementById("myFile").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);
      var zip = Zip.inflate(bytes);
      // console.dir(zip.files["OEBPS/content.opf"]);
      xhtmlList = analysisXML(zip.files["OEBPS/content.opf"].data); // XMLデータ解析
      var text = zip.files["OEBPS/"+xhtmlList[0]].data; // 最初のページを表示
      document.getElementById("result").innerHTML = Utf8.decode(text);
    }
    reader.readAsBinaryString(fileData);
  }, true);
}, true);
// XMLデータを解析してXHTMLファイル一覧を返す
function analysisXML(xmldata){
  document.getElementById("epubxml").innerHTML = xmldata;
  var epubXML = document.getElementById("epubxml");
  var fileList = [];
  // EPUB表示に必要なファイル項目を取得
  var itemTag = epubXML.getElementsByTagName("manifest")[0].getElementsByTagName("item");
  // 表示順を示すitemrefタグを取得
  var indexItem = epubXML.getElementsByTagName("spine")[0].getElementsByTagName("itemref");
  // ページの表示順番を取得(IDからファイル名を取得)
  for(var i=0; i<indexItem.length; i++){
    var ID = indexItem[i].getAttribute("idref");  // IDを読み出す
    for(var j=0; j<itemTag.length; j++){
      var cID = itemTag[j].getAttribute("id");  // IDを読み出す
      // console.log(ID+" = "+cID); // 確認したい人はコメント削除してください
      if (ID == cID){
        fileList.push(itemTag[j].getAttribute("href"));  // href属性を読み出す
        break// ループから抜ける
      }
    }
  }
  return fileList;
}
</script>
</head> 
<body>
<h1>EPUBデータの最初のページだけを表示</h1>
<form action="./test.cgi" method="get">
  <input type="file" id="myFile" />
  <input type="button" id="viewEPUB" value="EPUB表示" />
</form>
<div id="result"></div>
<div id="epubxml" style="display:none"></div>
</body> 
</html>

この連載の記事

一覧へ

この記事の編集者は以下の記事をオススメしています