OSDN Git Service

Change directory structure.
[dvibrowser/dvi2epub.git] / src / main / java / jp / sourceforge / dvibrowser / dvicore / doc / DirectFileDviDocument.java
diff --git a/src/main/java/jp/sourceforge/dvibrowser/dvicore/doc/DirectFileDviDocument.java b/src/main/java/jp/sourceforge/dvibrowser/dvicore/doc/DirectFileDviDocument.java
new file mode 100644 (file)
index 0000000..b0c1258
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2009, Takeyuki Nagao
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the
+ * following conditions are met:
+ * 
+ *  * Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *  * Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer in the documentation and/or other
+ *    materials provided with the distribution.
+ *    
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+package jp.sourceforge.dvibrowser.dvicore.doc;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+
+import jp.sourceforge.dvibrowser.dvicore.DviConstants;
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviFontTable;
+import jp.sourceforge.dvibrowser.dvicore.DviObject;
+import jp.sourceforge.dvibrowser.dvicore.DviUnit;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviInput;
+import jp.sourceforge.dvibrowser.dvicore.api.DviPage;
+import jp.sourceforge.dvibrowser.dvicore.api.HasURL;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviCommand;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostPost;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPostamble;
+import jp.sourceforge.dvibrowser.dvicore.cmd.DviPreamble;
+import jp.sourceforge.dvibrowser.dvicore.io.ByteArrayDviData;
+import jp.sourceforge.dvibrowser.dvicore.io.DviRandomAccessFileInput;
+import jp.sourceforge.dvibrowser.dvicore.render.EmptyDviExecutorHandler;
+
+
+// TODO: Optimize this class.  It's really slow for a large file.
+public class DirectFileDviDocument
+extends DviObject
+implements DviDocument, HasURL
+{
+  public static final long MAX_BUFFER_LENGTH = 10000000;
+    /* Buffer size for the data after postamble. 10MB */
+
+  private final File file;
+
+  private DviPreamble   preamble = null;
+  private DviPostamble  postamble = null;
+  private DviPostPost   postPost = null;
+
+  final DviFontTable fontTable = new DviFontTable();
+
+  private ArrayList<DviPage> pages = new ArrayList<DviPage>();
+
+  public DirectFileDviDocument(DviContextSupport dcs, File file)
+  throws DviException
+  {
+    super(dcs);
+    this.file = file;
+    try {
+      RandomAccessFile in = new RandomAccessFile(file, "r");
+      parseRandomAccessFile(in);
+      in.close();
+    } catch (IOException ex) {
+      throw new DviException(ex);
+    }
+  }
+  
+  private void parseRandomAccessFile(RandomAccessFile in)
+  throws IOException, DviException
+  {
+    // Parse the preamble.
+    {
+      int idByte, num, den, mag, k;
+      byte [] comment;
+
+      if (DviCommand.DVI_PRE != in.readUnsignedByte())
+        throw new DviException
+          ("Format error in dvi file: file doesn't start with pre.");
+
+      idByte = in.readUnsignedByte();
+      num = in.readInt();
+      den = in.readInt();
+      mag = in.readInt();
+
+      k = in.readUnsignedByte();
+      comment = new byte[k];
+      in.readFully(comment);
+
+      preamble = new DviPreamble(
+        idByte, DviUnit.getInstance(num, den, mag), comment);
+    }
+
+    // Determine the location of the postamble.
+    final long postPostPointer;
+    {
+      long pos = in.length() - 1;
+      int postamblePointer;
+      int idByte;
+
+      long paddingSize = 0;
+
+      while (true) {
+        if (pos < 0)
+          throw new DviException(
+            "Dvi file ended while looking for the postamble.");
+        in.seek(pos);
+        if (DviConstants.DVI_TRAILER != in.readUnsignedByte()) break;
+        paddingSize++;
+        pos--;
+      }
+      pos -= 5;
+      if (pos < 0)
+        throw new DviException(
+          "Dvi file ended while looking for the postamble.");
+
+      postPostPointer = pos;
+
+      /* pos -> +0 DVI_POST_POST (U1)
+       *        +1 post_offset   (U4)      
+       *        +5 id_byte       (U1)      
+       *        +6 padding       paddingSize copies of ((byte)223).
+       */
+      in.seek(pos);
+
+      if (DviCommand.DVI_POST_POST != in.readUnsignedByte())
+        throw new DviException(
+          "Format error in dvi file: unable to find post_post.");
+
+      postamblePointer = in.readInt();
+      if (postamblePointer < 0 ||
+        (long) postamblePointer > in.length() - 33)
+          throw new DviException(
+            "Format error in dvi file: file size too short.");
+
+      /* TODO: check id_byte */
+      idByte = in.readUnsignedByte();
+
+      postPost = new DviPostPost(postamblePointer, idByte);
+    }
+
+    in.seek(postPost.postamblePointer());
+
+    /* pos -> + 0 DVI_POST        (U1)
+     *        + 1 bp              (S4)
+     *        + 5 num             (S4)
+     *        + 9 den             (S4)
+     *        +13 mag             (S4)
+     *        +17 maxV            (S4)
+     *        +21 maxH            (S4)
+     *        +25 max_stack_depth (U2)
+     *        +27 total_pages     (U2)
+     *   size = 29 bytes.
+     */
+
+    if (DviCommand.DVI_POST != in.readUnsignedByte())
+      throw new DviException(
+        "Format error in dvi file: unable to find post.");
+
+    {
+      int bp;
+      int num, den, mag;
+      int maxV, maxH, maxStackDepth, totalPages;
+
+      bp   = in.readInt();
+      num  = in.readInt();
+      den  = in.readInt();
+      mag  = in.readInt();
+      maxV = in.readInt();
+      maxH = in.readInt();
+      maxStackDepth = in.readUnsignedShort();
+      totalPages    = in.readUnsignedShort();
+
+      postamble = new DviPostamble(
+        bp, DviUnit.getInstance(num, den, mag),
+        maxV, maxH, maxStackDepth, totalPages);
+    }
+
+    // parse font definitions stored right after the postamble.
+
+    {
+      long buflen = postPostPointer - in.getFilePointer();
+      if (0 < buflen) {
+        if (buflen > MAX_BUFFER_LENGTH)
+          throw new DviException
+            ("Too long data after postamble.");
+
+        final byte [] buf = new byte [(int) buflen];
+        in.readFully(buf);
+
+        getDviContext().execute(
+          new ByteArrayDviData(buf),
+          new EmptyDviExecutorHandler() {
+            public void doDefineFont(int fn, DviFontSpec fs) {
+              fontTable.put(fn, fs);
+            }
+          }
+        );
+      }
+    }
+
+    // TODO: handle embedded data.
+
+    {
+      long bop = postamble.firstBackPointer();
+      long eop = postPost.postamblePointer() - 1;
+      int pageNum = postamble.totalPages();
+
+      while (bop != -1 && pageNum > 0) {
+        in.seek(bop);
+
+        if (DviCommand.DVI_BOP != in.readUnsignedByte())
+          throw new DviException(
+            "Format error in dvi file: broken bop link.");
+
+        pageNum--;
+        // REMARK: pageNum==0 corresponds to the first page.
+        pages.add(0, new DefaultDviPage(DirectFileDviDocument.this, pageNum, bop, eop));
+
+        eop = bop - 1;
+        in.seek(bop + 1 + 4 * 10);
+        bop = in.readInt();
+      }
+
+      if (pageNum != 0)
+        throw new DviException(
+          "Format error in dvi file: wrong number of pages.");
+    }
+  }
+  
+  public int getTotalPages() throws DviException {
+    return postamble.totalPages();
+  }
+
+  public DviUnit getDviUnit() {
+    return postamble.dviUnit();
+  }
+  public DviPreamble getPreamble() {
+    return preamble;
+  }
+  public DviPostamble getPostamble() {
+    return postamble;
+  }
+  public DviPostPost getPostPost() {
+    return postPost;
+  }
+  public DviFontTable getFontTable() {
+    return fontTable;
+  }
+
+  public DviInput getInput()
+  throws DviException
+  {
+    try {
+      RandomAccessFile raf = new RandomAccessFile(getFile(), "r");
+      DviRandomAccessFileInput in = new DviRandomAccessFileInput(raf);
+      return in;
+    } catch (FileNotFoundException e) {
+      throw new DviException(e);
+    }
+  }
+
+  public DviInput getInput(long start, long end) throws DviException
+  {
+    try {
+      RandomAccessFile raf = new RandomAccessFile(getFile(), "r");
+      raf.seek(start);
+      DviRandomAccessFileInput in = new DviRandomAccessFileInput(raf);
+      in.setOffset(start);
+      in.setEnd(end);
+      return in;
+    } catch (FileNotFoundException e) {
+      throw new DviException(e);
+    } catch (IOException e) {
+      throw new DviException(e);
+    }
+  }
+
+//  private DviInput getInputNIO(long start, long end)
+//  throws DviException
+//  {
+//    try {
+//      FileInputStream fis = new FileInputStream(file);
+//      FileChannel fc = fis.getChannel();
+//      MappedByteBuffer bb = fc.map(
+//        FileChannel.MapMode.READ_ONLY,
+//        start, end - start + 1
+//      );
+//      DviByteBufferInput in = new DviByteBufferInput(bb);
+//      in.setOffset(start);
+//      return in;
+//    } catch (Throwable ex) {
+//      Logger.trace(ex);
+//      return getInput(start, end);
+//    }
+//  }
+
+  public DviPage getPage(int p)
+  throws DviException
+  {
+    if (p < 0 || getTotalPages() <= p)
+      throw new IllegalArgumentException
+        ("page number out of bounds.");
+
+    return pages.get(p);
+  }
+
+  public DviPage [] getPages()
+  {
+    return pages.toArray(new DviPage [0]);
+  }
+
+  public long getDataSize() throws DviException
+  {
+    return getFile().length();
+  }
+
+  public File getFile()
+  {
+    return file;
+  }
+
+  public URL getURL() throws DviException
+  {
+    try {
+      return file.toURL();
+    } catch (MalformedURLException e) {
+      throw new DviException(e);
+    }
+  }
+}