/* GanttProject is an opensource project management tool. Copyright (C) 2009-2012 Dmitry Barashev, GanttProject Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.ganttproject.impex.htmlpdf.itext; import java.awt.BorderLayout; import java.awt.Component; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import javax.swing.BorderFactory; import javax.swing.JPanel; import net.sourceforge.ganttproject.GPLogger; import net.sourceforge.ganttproject.IGanttProject; import net.sourceforge.ganttproject.export.ExporterBase; import net.sourceforge.ganttproject.export.ExportException; import net.sourceforge.ganttproject.export.ExporterBase.ExporterJob; import net.sourceforge.ganttproject.gui.UIFacade; import net.sourceforge.ganttproject.gui.options.OptionsPageBuilder; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.ganttproject.impex.htmlpdf.AbstractEngine; import org.ganttproject.impex.htmlpdf.ExporterToPDF; import org.ganttproject.impex.htmlpdf.Stylesheet; import org.ganttproject.impex.htmlpdf.StylesheetFactoryImpl; import org.ganttproject.impex.htmlpdf.fonts.TTFontCache; import org.osgi.service.prefs.Preferences; import biz.ganttproject.core.option.GPOptionGroup; public class ITextEngine extends AbstractEngine { private ITextStylesheet myStylesheet; private final TTFontCache myFontCache; private FontSubstitutionModel mySubstitutionModel; private Object myFontsMutex = new Object(); private boolean myFontsReady = false; private ExporterToPDF myExporter; public ITextEngine(ExporterToPDF exporter) { myExporter = exporter; myFontCache = new TTFontCache(); registerFonts(); } public List<GPOptionGroup> getSecondaryOptions() { return Arrays.asList(getSecondaryOptionsArray()); } private GPOptionGroup[] getSecondaryOptionsArray() { return ((ThemeImpl) myStylesheet).getOptions(); } public Component getCustomOptionsUI() { waitRegisterFonts(); JPanel result = new JPanel(new BorderLayout()); result.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); OptionsPageBuilder builder = new OptionsPageBuilder(); List<GPOptionGroup> options = new ArrayList<GPOptionGroup>(); options.addAll(myExporter.getSecondaryOptions()); result.add(builder.buildPlanePage(options.toArray(new GPOptionGroup[0])), BorderLayout.NORTH); result.add(createFontPanel(), BorderLayout.CENTER); return result; } public String[] getCommandLineKeys() { return new String[] { "itext" }; } private Component createFontPanel() { return new FontSubstitutionPanel(mySubstitutionModel).getComponent(); } public void setContext(IGanttProject project, UIFacade uiFacade, Preferences preferences, Stylesheet stylesheet) { super.setContext(project, uiFacade, preferences); setSelectedStylesheet(stylesheet); } public void setSelectedStylesheet(Stylesheet stylesheet) { waitRegisterFonts(); myStylesheet = (ITextStylesheet) stylesheet; if (getPreferences() != null) { Preferences node = getPreferences().node("/configuration/org.ganttproject.impex.htmlpdf/font-substitution"); mySubstitutionModel = new FontSubstitutionModel(myFontCache, myStylesheet, node); myStylesheet.setFontSubstitutionModel(mySubstitutionModel); } } public void setStylesheet(Stylesheet stylesheet) { myStylesheet = (ITextStylesheet) stylesheet; } public List<Stylesheet> getStylesheets() { StylesheetFactoryImpl factory = new StylesheetFactoryImpl() { @Override protected Stylesheet newStylesheet(URL resolvedUrl, String localizedName) { return new ThemeImpl(resolvedUrl, localizedName, getExporter(), myFontCache); } }; return factory.createStylesheets(ITextStylesheet.class); } private ExporterBase getExporter() { return myExporter; } private void registerFonts() { Thread fontReadingThread = new Thread(new Runnable() { @Override public void run() { try { // Random waiting seems silly, depending on the available // resources (CPU speed, number of processes running etc) // this might take longer or shorter... // FIXME Add some better way of determining whether the fonts can be // read already Thread.sleep(10000); GPLogger.getLogger(TTFontCache.class).info("Scanning font directories..."); } catch (InterruptedException e) { // TODO Auto-generated catch block GPLogger.logToLogger(e); } registerFontDirectories(); synchronized (ITextEngine.this.myFontsMutex) { myFontsReady = true; myFontsMutex.notifyAll(); } GPLogger.getLogger(TTFontCache.class).info("Scanning font directories completed"); } }); fontReadingThread.setPriority(Thread.MIN_PRIORITY); fontReadingThread.start(); } private void waitRegisterFonts() { while (myFontsMutex != null) { synchronized (myFontsMutex) { if (myFontsReady) { break; } try { myFontsMutex.wait(); } catch (InterruptedException e) { GPLogger.log(e); break; } } } } protected void registerFontDirectories() { myFontCache.registerDirectory(System.getProperty("java.home") + "/lib/fonts"); IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); IConfigurationElement[] configElements = extensionRegistry.getConfigurationElementsFor("org.ganttproject.impex.htmlpdf.FontDirectory"); for (int i = 0; i < configElements.length; i++) { final String dirName = configElements[i].getAttribute("name"); if (Boolean.TRUE.toString().equalsIgnoreCase(configElements[i].getAttribute("absolute"))) { myFontCache.registerDirectory(dirName); } else { String namespace = configElements[i].getDeclaringExtension().getNamespaceIdentifier(); URL dirUrl = Platform.getBundle(namespace).getResource(dirName); if (dirUrl == null) { GPLogger.getLogger(ITextEngine.class).warning( "Failed to find directory " + dirName + " in plugin " + namespace); continue; } try { URL resolvedDir = Platform.resolve(dirUrl); myFontCache.registerDirectory(resolvedDir.getPath()); } catch (IOException e) { GPLogger.getLogger(ITextEngine.class).log(Level.WARNING, e.getMessage(), e); continue; } } } } public ExporterJob[] createJobs(File outputFile, List<File> resultFiles) { waitRegisterFonts(); return new ExporterJob[] { createTransformationJob(outputFile) }; } private ExporterJob createTransformationJob(final File outputFile) { ExporterJob result = new ExporterJob("Generating PDF") { @Override protected IStatus run() { assert myStylesheet != null; OutputStream out = null; try { out = new FileOutputStream(outputFile); ((ThemeImpl) myStylesheet).run(getProject(), getUiFacade(), out); } catch (ExportException e) { throw new RuntimeException(e); } catch (FileNotFoundException e) { throw new RuntimeException(e); } finally { } return Status.OK_STATUS; } }; return result; } }