OSDN Git Service

Change directory structure.
[dvibrowser/dvi2epub.git] / src / main / java / jp / sourceforge / dvibrowser / dvicore / ctx / DefaultDviContext.java
diff --git a/src/main/java/jp/sourceforge/dvibrowser/dvicore/ctx/DefaultDviContext.java b/src/main/java/jp/sourceforge/dvibrowser/dvicore/ctx/DefaultDviContext.java
new file mode 100644 (file)
index 0000000..2c8c55f
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * 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.ctx;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessControlException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+import jp.sourceforge.dvibrowser.dvicore.DviException;
+import jp.sourceforge.dvibrowser.dvicore.DviFontSpec;
+import jp.sourceforge.dvibrowser.dvicore.DviPaperSize;
+import jp.sourceforge.dvibrowser.dvicore.DviResolution;
+import jp.sourceforge.dvibrowser.dvicore.MetafontMode;
+import jp.sourceforge.dvibrowser.dvicore.api.CharacterCodeMapper;
+import jp.sourceforge.dvibrowser.dvicore.api.DevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.api.DviContext;
+import jp.sourceforge.dvibrowser.dvicore.api.DviData;
+import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutor;
+import jp.sourceforge.dvibrowser.dvicore.api.DviExecutorHandler;
+import jp.sourceforge.dvibrowser.dvicore.api.DviFont;
+import jp.sourceforge.dvibrowser.dvicore.api.FullMetrics;
+import jp.sourceforge.dvibrowser.dvicore.api.Glyph;
+import jp.sourceforge.dvibrowser.dvicore.api.SimpleMetrics;
+import jp.sourceforge.dvibrowser.dvicore.doc.DirectFileDviDocument;
+import jp.sourceforge.dvibrowser.dvicore.doc.StreamDviDocument;
+import jp.sourceforge.dvibrowser.dvicore.doc.URLDviDocument;
+import jp.sourceforge.dvibrowser.dvicore.font.DviFontResolver;
+import jp.sourceforge.dvibrowser.dvicore.font.FullMetricsResolver;
+import jp.sourceforge.dvibrowser.dvicore.font.LogicalFont;
+import jp.sourceforge.dvibrowser.dvicore.gs.GhostscriptUtils;
+import jp.sourceforge.dvibrowser.dvicore.render.BasicExecutor;
+import jp.sourceforge.dvibrowser.dvicore.render.DefaultDevicePainter;
+import jp.sourceforge.dvibrowser.dvicore.util.DviCache;
+import jp.sourceforge.dvibrowser.dvicore.util.DviUtils;
+import jp.sourceforge.dvibrowser.dvicore.util.concurrent.Computation;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ManagedProgressItem;
+import jp.sourceforge.dvibrowser.dvicore.util.progress.ProgressRecorder;
+
+
+public class DefaultDviContext
+implements DviContext
+{
+  private static final Logger LOGGER = Logger.getLogger(DefaultDviContext.class.getName());
+  
+  private static final String DVICORE_INTERNAL_FILENAME_PREFIX = "dvicore-";
+  
+  private final Map<String, Glyph> glyphCache = new DviCache<String, Glyph>(16384);
+  private final CharacterCodeMapper characterCodeMapper
+    = new SimpleJisToUnicodeMapper();
+  
+  private boolean recordResources = false;
+  private final Set<URL> resources = new TreeSet<URL>(new Comparator<URL>() {
+       public int compare(URL arg0, URL arg1) {
+               return arg0.toString().compareTo(arg1.toString());
+       }
+  });
+  
+  private final ProgressRecorder recorder = new ProgressRecorder(this) {
+    @Override
+    protected boolean removeEldestElement(ManagedProgressItem item)
+    {
+      List<ManagedProgressItem> list = getProgressItems();
+      if (list.size() > 10) {
+        return true;
+      }
+      return false;
+    }
+  };
+
+  private AsyncComputers asyncComputers;
+
+
+  public DefaultDviContext()
+  throws DviException
+  {
+    this(null);
+  }
+
+  public DefaultDviContext(Properties prop)
+  throws DviException
+  {
+    if (prop == null)
+      prop = getDefaultProperties();
+    this.prop = prop;
+    initializeComputers();
+    initializeFontMapper();
+    populatePaperSizes();
+    initializeDirs();
+  }
+  
+  protected void initializeComputers() {
+       this.setAsyncComputers(new AsyncComputers(prop));
+  }
+  
+  protected void initializeDirs()
+  throws DviException
+  {
+    File cacheDir = getCacheDirectory();
+    if (cacheDir != null && !cacheDir.exists()) {
+      if (!cacheDir.mkdirs()) {
+        throw new DviException("Failed to create cache directory: " + cacheDir);
+      }
+    }
+    File tmpDir = getTemporaryDirectory();
+    if (tmpDir != null && !tmpDir.exists()) {
+      if (!tmpDir.mkdirs()) {
+        throw new DviException("Failed to create temporary directory: " + tmpDir);
+      }
+    }
+  }
+
+  protected Properties getDefaultProperties()
+  throws DviException
+  {
+    try {
+      URL url = DefaultDviContext.class.getResource("default-context.properties");
+      Properties prop = new Properties();
+      if (null != url) {
+          prop.load(url.openStream());
+      } else {
+         LOGGER.warning("Failed to load default-context.properties");
+      }
+      return prop;
+    } catch (Exception e) {
+      throw new DviException(e);
+    }
+  }
+  
+  private final Properties prop;
+
+  public Properties getProperties()
+  {
+    return prop;
+  }
+  
+  public void execute(DviData data, DviExecutorHandler handler)
+      throws DviException
+  {
+    newDviExecutor().execute(data, handler);
+  }
+
+// TODO: delete
+//  private static final CachedComputer<String, Collection<DviFont>> dviFontComputer
+//  = new CachedComputer<String, Collection<DviFont>>
+//    (new ThreadedComputer<String, Collection<DviFont>>(1)) {
+//    @Override
+//    protected boolean removeEldestEntry(Map.Entry<String, CacheEntry<String, Collection<DviFont>>> entry)
+//    {
+//      boolean remove = getCache().size() > 64;
+//      return remove;
+//    }
+//  };
+
+  public DviFont findDviFont(LogicalFont logicalFont) throws DviException
+  {
+    Computation<String, Collection<DviFont>> computation
+      = new DviFontResolver(this, logicalFont);
+    try {
+      Collection<DviFont> fonts = getAsyncComputers().getDviFontComputer()
+        .compute(computation).get();
+      for (DviFont font : fonts) {
+        return font;
+      }
+      return null;
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+  }
+  
+  public SimpleMetrics findDviSimpleMetrics(DviFontSpec fs) throws DviException
+  {
+    return findDviFullMetrics(fs);
+  }
+
+//  private static final CachedComputer<String, Collection<FullMetrics>> fullMetricsComputer
+//  = new CachedComputer<String, Collection<FullMetrics>>
+//    (new ThreadedComputer<String, Collection<FullMetrics>>(1));
+
+  public FullMetrics findDviFullMetrics(DviFontSpec fs) throws DviException
+  {
+    Computation<String, Collection<FullMetrics>> computation = new FullMetricsResolver(
+        this, fs);
+    try {
+      Collection<FullMetrics> fonts = getAsyncComputers().getFullMetricsComputer()
+       .compute(computation).get();
+      if (fonts != null) {
+        for (FullMetrics font : fonts) {
+          return font;
+        }
+      }
+      return null;
+    } catch (InterruptedException e) {
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      throw new DviException(e);
+    }
+  }
+
+  public CharacterCodeMapper getCharacterCodeMapper(LogicalFont logicalFont) throws DviException
+  {
+    return characterCodeMapper;
+  }
+  
+  public DviContext getDviContext() { return this; }
+  
+  public Map<String, Glyph> getGlyphCache() throws DviException
+  {
+    return Collections.synchronizedMap(glyphCache);
+  }
+
+  protected void initializeFontMapper()
+  {
+  }
+  
+  public DevicePainter newDevicePainter() throws DviException
+  {
+    return new DefaultDevicePainter(this);
+  }
+
+  public DviExecutor newDviExecutor() throws DviException
+  {
+    return new BasicExecutor(this);
+  }
+  
+  public DviDocument openDviDocument(File file) throws DviException
+  {
+    return new DirectFileDviDocument(this, file);
+  }
+
+  public DviDocument openDviDocument(InputStream is) throws DviException
+  {
+    return new StreamDviDocument(this, is);
+  }
+
+  // TODO: implement async resource loading
+  public DviDocument openDviDocument(URL url) throws DviException
+  {
+    return new URLDviDocument(this, url);
+  }
+
+  public ProgressRecorder getProgressRecorder()
+  {
+    return recorder;
+  }
+
+  private static final Map<String, DviPaperSize> paperSizes = new TreeMap<String, DviPaperSize>(); 
+  
+  protected void populatePaperSizes()
+  {
+    // ISO 216 sizes
+    // TODO: implement B and C serieses. A4 Japanese, too.
+    // TOOD: outsource the configuration.
+    addPaperSize(new DviPaperSize(841.0, 1189.0, "A0"));
+    addPaperSize(new DviPaperSize(594.0, 841.0, "A1"));
+    addPaperSize(new DviPaperSize(420.0, 594.0, "A2"));
+    addPaperSize(new DviPaperSize(297.0, 420.0, "A3"));
+    addPaperSize(new DviPaperSize(210.0, 297.0, "A4"));
+    addPaperSize(new DviPaperSize(148.0, 210.0, "A5"));
+    addPaperSize(new DviPaperSize(105.0, 148.0, "A6"));
+    addPaperSize(new DviPaperSize(74.0, 105.0, "A7"));
+    addPaperSize(new DviPaperSize(52.0, 74.0, "A8"));
+    addPaperSize(new DviPaperSize(37.0, 52.0, "A9"));
+    addPaperSize(new DviPaperSize(26.0, 37.0, "A10"));
+  }
+
+  protected void addPaperSize(DviPaperSize dviPaperSize)
+  {
+    if (dviPaperSize == null) return;
+    paperSizes.put(dviPaperSize.description().toLowerCase(), dviPaperSize);
+  }
+
+  public DviPaperSize findPaperSizeByName(String name) throws DviException
+  {
+    if (name == null) return null;
+    return paperSizes.get(name.toLowerCase());
+  }
+
+  public DviPaperSize getDefaultPaperSize() throws DviException
+  {
+    return findPaperSizeByName("A4");
+  }
+
+  public DviPaperSize [] listPaperSizes() throws DviException
+  {
+    return paperSizes.values().toArray(new DviPaperSize[0]);
+  }
+
+  private static final DviResolution defaultResolution = new DviResolution(2400, 20);
+  
+  public DviResolution getDefaultResolution() throws DviException
+  {
+    return defaultResolution;
+  }
+
+// TODO: delete
+//  private static final CachedComputer<String, Collection<URL>> dviResourceComputer
+//    = new CachedComputer<String, Collection<URL>>
+//      (new ThreadedComputer<String, Collection<URL>>(1));
+  
+  public URL getDviResource(String filename) throws DviException
+  {
+    if (filename.startsWith(DVICORE_INTERNAL_FILENAME_PREFIX)) {
+      // The resource name starting with "dvicore-" are only for internal use.
+      // So there is no local file corresponding to such a filename.
+      // We answer null.
+      return null;
+    }
+    
+    Computation<String, Collection<URL>> c
+      = new FileLocationResolver(this, "/dvi/builtin", filename);
+    final Future<Collection<URL>> future
+      = getAsyncComputers().getDviResourceComputer().compute(c);
+    try {
+      final Collection<URL> list = future.get();
+      for (URL url : list) {
+        if (recordResources) {
+          LOGGER.info("resolved resource: filename=" + filename + " url=" + url);
+          resources.add(url);
+        } else {
+          LOGGER.finest("resolved resource: filename=" + filename + " url=" + url);
+        }
+        return url;
+      }
+      return null;
+    } catch (InterruptedException e) {
+      LOGGER.warning(e.toString());
+      throw new DviException(e);
+    } catch (ExecutionException e) {
+      LOGGER.warning(e.toString());
+      throw new DviException(e);
+    }
+  }
+
+  public LogicalFont mapLogicalFont(LogicalFont logicalFont)
+      throws DviException
+  {
+       LogicalFont mapped = logicalFont;
+       if (logicalFont != null) {
+           String prefix = getClass().getName();
+           String faceKey = prefix + ".fontMap." + logicalFont.fontSpec().name();
+        LOGGER.info("Face key: " + faceKey);
+           String face = getProperties().getProperty(faceKey);
+        LOGGER.info("Properties: " + getProperties());
+           if (face != null) {
+                   mapped = logicalFont.renameTo(face);
+               LOGGER.info("Rename logical font: " + logicalFont + " => " + mapped);
+           }
+       }
+    LOGGER.info("Map logical font: " + logicalFont + " => " + mapped);
+    return mapped;
+  }
+  
+  // N.B. System.getProperty() throws an exception when invoked
+  // from inside an applet.
+  private static String getSystemProperty(String key) {
+       try {
+           return System.getProperty(key);
+       } catch (AccessControlException ex) {
+               return null;
+       }
+  }
+
+  private static final String userDir = getSystemProperty("user.dir");
+  private static final String ioTmpDir = getSystemProperty("java.io.tmpdir");
+  
+  // TODO: externalize the string "dvibrowser.jar".
+  public File getApplicationHomeDir()
+  throws DviException
+  {
+       if (userDir != null) {
+           File home = new File(userDir);
+           File markFile = new File(home, "dvibrowser.jar");
+           if (markFile.exists()) {
+             // It seems that we are using DviContext from within dvibrowser.
+             return home;
+           }
+       }
+    return null;
+  }
+
+  public File getCacheDirectory() throws DviException
+  {
+    File appHome = getApplicationHomeDir();
+    if (appHome == null) {
+      File tmpDir = getTemporaryDirectory();
+      if (tmpDir != null) {
+          return new File(tmpDir, "cache");
+      }
+    } else {
+       if (userDir != null) {
+             File var = new File(userDir, "var");
+             return new File(var, "cache");
+       }
+    }
+    return null;
+  }
+
+  public File getTemporaryDirectory() throws DviException
+  {
+    File appHome = getApplicationHomeDir();
+    if (appHome == null) {
+       if (ioTmpDir != null) {
+             return new File(ioTmpDir, "dvicontext");
+       }
+    } else {
+      return new File(appHome, "tmp");
+    }
+    return null;
+  }
+
+  private volatile String [] ghostscriptExecutables = null;
+  
+  public String getExecutableName(String name) throws DviException
+  {
+    if ("gs".equals(name)) {
+      if (ghostscriptExecutables == null) {
+        // The following code might be called in pararell when we invoke
+        // this method from within multiple threads.
+        // It might be better to use some atomic classes.
+        ghostscriptExecutables = GhostscriptUtils.listGhostscriptExecutables();
+        if (ghostscriptExecutables.length == 0) {
+          LOGGER.warning("You don't seem to have a Ghostscript installed.  dvibrowser needs it to render PS, EPS, and PDF.");
+        } else {
+          LOGGER.info("Ghostscript executables found: " + DviUtils.join(" ", ghostscriptExecutables));
+        }
+      }
+      if (ghostscriptExecutables.length > 0) {
+        return ghostscriptExecutables[0];
+      }
+    }
+    
+    return name;
+  }
+
+  private final DviToolkit dviToolkit = new DviToolkit(this);
+  public DviToolkit getDviToolkit()
+  {
+    return dviToolkit;
+  }
+
+  private final MetafontMode libraryDefaultMetafontMode = MetafontMode.FALLBACK;
+  public MetafontMode getDefaultMetafontMode() throws DviException {
+    return libraryDefaultMetafontMode;
+  }
+
+       public void setRecordResources(boolean recordResources) {
+               this.recordResources = recordResources;
+       }
+
+       public boolean wantRecordResources() {
+               return recordResources;
+       }
+
+       public Set<URL> getRecordedResources() {
+               return resources;
+       }
+
+       protected void setAsyncComputers(AsyncComputers asyncComputers) {
+               this.asyncComputers = asyncComputers;
+       }
+
+       public AsyncComputers getAsyncComputers() {
+               return asyncComputers;
+       }
+}