/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.xwiki.formula.internal; import java.io.File; import java.io.IOException; import java.io.OutputStream; import javax.inject.Named; import javax.inject.Singleton; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.io.output.NullOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.component.annotation.Component; import org.xwiki.formula.AbstractFormulaRenderer; import org.xwiki.formula.FormulaRenderer; import org.xwiki.formula.ImageData; import uk.ac.ed.ph.snuggletex.SnuggleEngine; import uk.ac.ed.ph.snuggletex.SnuggleInput; import uk.ac.ed.ph.snuggletex.SnuggleSession; import uk.ac.ed.ph.snuggletex.WebPageOutputOptions; import uk.ac.ed.ph.snuggletex.jeuclid.JEuclidUtilities; import uk.ac.ed.ph.snuggletex.jeuclid.SimpleMathMLImageSavingCallback; /** * Implementation of the {@link FormulaRenderer} component, which uses <a * href="http://snuggletex.sf.net/">SnuggleTeX</a> for generating the images corresponding to the rendered mathematical * formulae. The results are not as eye-pleasing as those obtained from the native TeX system, but this is a pure-java * solution, which doesn't depend on any external services or native commands. It could probably be tweaked to obtain * better results, by selecting a different font. * * @version $Id: 51b0b19c7b218d39bd71b68ab102fa0eebed6ae7 $ * @since 2.0M3 */ @Component @Named("snuggletex") @Singleton public final class SnuggleTexFormulaRenderer extends AbstractFormulaRenderer { /** Logging helper object. */ private static final Logger LOGGER = LoggerFactory.getLogger(SnuggleTexFormulaRenderer.class); /** The SnuggleTeX engine responsible for rendering the formulae. */ private SnuggleEngine engine = new SnuggleEngine(); @Override protected ImageData renderImage(String formula, boolean inline, FormulaRenderer.FontSize size, FormulaRenderer.Type type) throws IllegalArgumentException, IOException { SnuggleSession session = this.engine.createSession(); SnuggleInput input = new SnuggleInput(wrapFormula(formula, inline)); session.parseInput(input); ByteArrayOutputStream output = new ByteArrayOutputStream(); CustomMathMLImageSavingCallback callback = new CustomMathMLImageSavingCallback(output, size.getSize()); WebPageOutputOptions options = JEuclidUtilities.createWebPageOptions(false, callback); session.writeWebPage(options, new NullOutputStream()); return new ImageData(output.toByteArray(), type); } /** * Callback which writes the first generated image into a provided output stream. * * @version $Id: 51b0b19c7b218d39bd71b68ab102fa0eebed6ae7 $ */ private class CustomMathMLImageSavingCallback extends SimpleMathMLImageSavingCallback { /** The target output. The first generated image will be pushed into this stream. */ private OutputStream target; /** * Simple constructor which receives the {@link #target} output stream, and the default font size for the image. * * @param target the target output, see {@link #target} * @param size the desired font size */ CustomMathMLImageSavingCallback(OutputStream target, int size) { this.target = target; setFontSize(String.valueOf(size)); } @Override public File getImageOutputFile(int mathmlCounter) { // Not used here) return null; } @Override public OutputStream getImageOutputStream(int mathmlCounter) { // Valid input only produces 1 image, so ignore all others return mathmlCounter == 0 ? this.target : null; } @Override public String getImageURL(int mathmlCounter) { // Not needed here as we're throwing the resulting XML away return ""; } public void imageSavingFailed(Object imageFileOrOutputStream, int mathmlCounter, String contentType, Throwable exception) { // Shouldn't really happen LOGGER.error("Can't save image", exception); } } }