--- /dev/null
+/*
+ * 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);
+ }
+ }
+}