Commit 621db693 authored by Jean-Marc Ronck's avatar Jean-Marc Ronck
Browse files

Basic LaTeX support.

parents 38810e8b b51ece02
Pipeline #59357 passed with stages
in 46 seconds
......@@ -6,7 +6,7 @@
<groupId>de.monticore.lang.monticar</groupId>
<artifactId>math-pretty-printer</artifactId>
<version>0.1.0-SNAPSHOT</version>
<version>0.2.0</version>
<properties>
......@@ -18,6 +18,8 @@
<logback.version>1.1.2</logback.version>
<montimath.version>0.0.13</montimath.version>
<commons.cli.version>1.4</commons.cli.version>
<jlatexmath.version>1.0.7</jlatexmath.version>
<batik.version>1.10</batik.version>
<!-- .. Plugins ....................................................... -->
......@@ -79,6 +81,25 @@
<version>${commons.cli.version}</version>
</dependency>
<dependency>
<groupId>org.scilab.forge</groupId>
<artifactId>jlatexmath</artifactId>
<version>${jlatexmath.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-dom</artifactId>
<version>${batik.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>${batik.version}</version>
</dependency>
<!-- .. MontiCore .................................................... -->
<!-- .. MontiMath .................................................... -->
......
......@@ -2,6 +2,7 @@ package de.monticore.lang.monticar.mpp.core.application;
import de.monticore.lang.monticar.mpp.core.events.EventsService;
import de.monticore.lang.monticar.mpp.core.generator.Generator;
import de.monticore.lang.monticar.mpp.core.options.OptionsService;
import de.monticore.lang.monticar.mpp.core.visitor.ModelPathVisitor;
public class Application {
......@@ -13,8 +14,8 @@ public class Application {
public Application() {
this.eventsService = new EventsService();
this.optionsService = new OptionsService(this.eventsService);
this.generator = new Generator(this.eventsService);
this.modelPathVisitor = new ModelPathVisitor(this.eventsService);
this.generator = new Generator(this.eventsService, this.optionsService);
this.modelPathVisitor = new ModelPathVisitor(this.eventsService, this.optionsService);
}
public void start(String[] args) {
......
package de.monticore.lang.monticar.mpp.core.application;
import org.apache.commons.cli.CommandLine;
public class OptionsParsedEvent {
public final CommandLine commandLine;
public OptionsParsedEvent(CommandLine commandLine) {
this.commandLine = commandLine;
}
}
......@@ -5,40 +5,41 @@ import de.monticore.generating.GeneratorEngine;
import de.monticore.generating.GeneratorSetup;
import de.monticore.generating.templateengine.GlobalExtensionManagement;
import de.monticore.lang.math._ast.ASTMathCompilationUnit;
import de.monticore.lang.math._ast.ASTMathScript;
import de.monticore.lang.math._parser.MathParser;
import de.monticore.lang.monticar.mpp.core.application.OptionsParsedEvent;
import de.monticore.lang.monticar.mpp.core.events.EventsService;
import de.monticore.lang.monticar.mpp.core.options.OptionsParsedEvent;
import de.monticore.lang.monticar.mpp.core.options.OptionsService;
import de.monticore.lang.monticar.mpp.core.parser.MathFileParsedEvent;
import de.monticore.lang.monticar.mpp.core.parser.MathFileParser;
import de.monticore.lang.monticar.mpp.core.visitor.MathFileVisitedEvent;
import de.monticore.lang.monticar.mpp.core.visitor.ModelPathVisitedEvent;
import de.monticore.types.types._ast.ASTQualifiedName;
import org.apache.commons.cli.ParseException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
public class Generator {
protected final GeneratorHelper helper;
protected final OptionsService optionsService;
protected final MathFileParser parser;
protected Path modelPath;
protected Path outputPath;
protected GeneratorEngine generator;
protected Set<ASTMathCompilationUnit> nodes;
public Generator(EventsService eventsService) {
this.helper = new GeneratorHelper();
private int index;
private int count;
public Generator(EventsService eventsService, OptionsService optionsService) {
this.helper = new GeneratorHelper(eventsService, optionsService);
this.parser = new MathFileParser(eventsService);
this.optionsService = optionsService;
this.index = 0;
this.count = 0;
eventsService.register(this);
}
protected void prepare() {
GeneratorSetup setup = new GeneratorSetup();
File outputDirectory = this.outputPath.toFile();
File outputDirectory = this.optionsService.getOutputPath().toFile();
GlobalExtensionManagement glex = new GlobalExtensionManagement();
glex.setGlobalValue("helper", this.helper);
......@@ -47,51 +48,39 @@ public class Generator {
setup.setTracing(false);
setup.setOutputDirectory(outputDirectory);
this.nodes = new HashSet<>();
this.helper.setModelPath(this.modelPath);
this.generator = new GeneratorEngine(setup);
}
public void generateIndex() {
Path outputPath = Paths.get("index.html");
this.helper.setASTNodes(this.nodes);
this.generator.generateNoA("templates/index.ftl", outputPath);
}
public void generateScript(ASTMathCompilationUnit node) {
String packageName = node.getPackage().toString();
String outputDirectory = packageName.replace(".", File.separator);
String fileName = String.format("%s.html", node.getMathScript().getName());
Path outputPath = Paths.get(outputDirectory, fileName);
Path outputPath = Paths.get(outputDirectory, node.getMathScript().getName(), "index.html");
this.nodes.add(node);
this.generator.generate("templates/script.ftl", outputPath, node);
}
@Subscribe
public void onOptionsParsed(OptionsParsedEvent event) throws ParseException {
String mpArgument = (String)event.commandLine.getParsedOptionValue("mp");
String outArgument = (String)event.commandLine.getParsedOptionValue("out");
this.modelPath = Paths.get(mpArgument);
this.outputPath = Paths.get(outArgument);
public void onOptionsParsed(OptionsParsedEvent event) {
this.prepare();
}
@Subscribe
public void onMathFileVisited(MathFileVisitedEvent event) throws IOException {
MathParser parser = new MathParser();
String filePath = event.file.toString();
Optional<ASTMathCompilationUnit> nodeOptional = parser.parse(filePath);
nodeOptional.ifPresent(this::generateScript);
public void onMathFileVisited(MathFileVisitedEvent event) {
this.count++;
}
@Subscribe
public void onModelPathVisited(ModelPathVisitedEvent event) {
this.generateIndex();
public void onMathFileParsed(MathFileParsedEvent event) {
this.generateScript(event.ast);
this.index++;
if (this.index == this.count) this.generateIndex();
}
}
package de.monticore.lang.monticar.mpp.core.generator;
import com.google.common.eventbus.Subscribe;
import de.monticore.lang.math._ast.ASTMathCompilationUnit;
import de.monticore.lang.math._ast.ASTMathScript;
import de.monticore.lang.monticar.mpp.core.events.EventsService;
import de.monticore.lang.monticar.mpp.core.options.OptionsService;
import de.monticore.lang.monticar.mpp.core.parser.MathFileParsedEvent;
import de.monticore.lang.monticar.mpp.core.tex.SVGGenerator;
import de.monticore.lang.monticar.mpp.montimath.HTMLMathPrettyPrinter;
import de.monticore.lang.monticar.mpp.montimath.TeXHTMLMathPrettyPrinter;
import de.monticore.types.types._ast.ASTQualifiedName;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class GeneratorHelper {
protected final EventsService eventsService;
protected final OptionsService optionsService;
protected final HTMLMathPrettyPrinter htmlPrinter;
protected final TeXHTMLMathPrettyPrinter texPrinter;
protected final SVGGenerator svggenerator;
protected Path modelPath;
protected Set<ASTMathCompilationUnit> nodes;
protected List<ASTMathCompilationUnit> nodes;
public GeneratorHelper() {
public GeneratorHelper(EventsService eventsService, OptionsService optionsService) {
this.eventsService = eventsService;
this.optionsService = optionsService;
this.svggenerator = new SVGGenerator(this.optionsService);
this.htmlPrinter = new HTMLMathPrettyPrinter();
}
this.texPrinter = new TeXHTMLMathPrettyPrinter(svggenerator);
public void setModelPath(Path modelPath) {
this.modelPath = modelPath;
}
this.nodes = new ArrayList<>();
public void setASTNodes(Set<ASTMathCompilationUnit> nodes) {
this.nodes = nodes;
this.eventsService.register(this);
}
public Set<ASTMathCompilationUnit> getASTNodes() {
public List<ASTMathCompilationUnit> getASTNodes() {
return this.nodes;
}
......@@ -35,7 +48,7 @@ public class GeneratorHelper {
String packageName = packageNode.toString().replace(".", "/");
String scriptName = this.printScript(ast);
return String.format("%s/%s.html", packageName, scriptName);
return String.format("%s/%s/index.html", packageName, scriptName);
}
public String printScript(ASTMathCompilationUnit ast) {
......@@ -48,9 +61,25 @@ public class GeneratorHelper {
return this.htmlPrinter.prettyPrint(ast);
}
public String[] getLines(ASTMathCompilationUnit ast) {
public String printTeXHTML(ASTMathCompilationUnit ast) {
return this.texPrinter.prettyPrint(ast);
}
public String[] getHTMLLines(ASTMathCompilationUnit ast) {
String printedHTML = this.printHTML(ast);
return printedHTML.split("\n");
}
public String[] getTeXHTMLLines(ASTMathCompilationUnit ast) {
String printedHTML = this.printTeXHTML(ast);
return printedHTML.split("\n");
}
@Subscribe
public void onMathFileParsed(MathFileParsedEvent event) {
this.nodes.add(event.ast);
}
}
package de.monticore.lang.monticar.mpp.core.options;
import org.apache.commons.cli.ParseException;
import java.nio.file.Path;
public class OptionsParsedEvent {
public final Path modelPath;
public final Path outputPath;
public OptionsParsedEvent(Path modelPath, Path outputPath) throws ParseException {
this.modelPath = modelPath;
this.outputPath = outputPath;
}
}
package de.monticore.lang.monticar.mpp.core.application;
package de.monticore.lang.monticar.mpp.core.options;
import com.google.common.eventbus.Subscribe;
import de.monticore.lang.math._ast.ASTMathCompilationUnit;
import de.monticore.lang.monticar.mpp.core.application.ApplicationConfigureEvent;
import de.monticore.lang.monticar.mpp.core.events.EventsService;
import de.monticore.lang.monticar.mpp.core.options.OptionsParsedEvent;
import org.apache.commons.cli.*;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
public class OptionsService {
protected final EventsService eventsService;
protected final Options options;
protected Path modelPath;
protected Path outputPath;
public OptionsService(EventsService eventsService) {
this.eventsService = eventsService;
this.options = new Options();
......@@ -23,12 +33,35 @@ public class OptionsService {
"Absolute Path to the root directory of the output.");
}
public Path getModelPath() {
return this.modelPath;
}
public Path getOutputPath() {
return this.outputPath;
}
public Path getOutputPath(ASTMathCompilationUnit ast) {
String packagePath = ast.getPackage().toString().replace(".", File.separator);
String artifactName = ast.getMathScript().getName();
Path relativePath = Paths.get(packagePath, artifactName);
return this.outputPath.resolve(relativePath);
}
@Subscribe
public void onApplicationStarted(ApplicationConfigureEvent event) throws ParseException {
public void onApplicationConfigure(ApplicationConfigureEvent event) throws ParseException {
CommandLineParser parser = new DefaultParser();
CommandLine commandLine = parser.parse(this.options, event.arguments);
OptionsParsedEvent optionsEvent = new OptionsParsedEvent(commandLine);
String modelPath = (String)commandLine.getParsedOptionValue("mp");
String outputPath = (String)commandLine.getParsedOptionValue("out");
this.modelPath = Paths.get(modelPath);
this.outputPath = Paths.get(outputPath);
OptionsParsedEvent optionsEvent = new OptionsParsedEvent(this.modelPath, this.outputPath);
this.eventsService.post(optionsEvent);
}
......
package de.monticore.lang.monticar.mpp.core.parser;
import de.monticore.lang.math._ast.ASTMathCompilationUnit;
public class MathFileParsedEvent {
public final ASTMathCompilationUnit ast;
public MathFileParsedEvent(ASTMathCompilationUnit ast) {
this.ast = ast;
}
}
package de.monticore.lang.monticar.mpp.core.parser;
import com.google.common.eventbus.Subscribe;
import de.monticore.lang.math._ast.ASTMathCompilationUnit;
import de.monticore.lang.math._parser.MathParser;
import de.monticore.lang.monticar.mpp.core.events.EventsService;
import de.monticore.lang.monticar.mpp.core.visitor.MathFileVisitedEvent;
import java.io.IOException;
import java.util.Optional;
public class MathFileParser {
protected final EventsService eventsService;
protected final MathParser parser;
public MathFileParser(EventsService eventsService) {
this.eventsService = eventsService;
this.parser = new MathParser();
this.eventsService.register(this);
}
@Subscribe
public void onMathFileVisited(MathFileVisitedEvent event) throws IOException {
String filename = event.file.toString();
Optional<ASTMathCompilationUnit> astOptional = this.parser.parse(filename);
astOptional.ifPresent(ast -> this.eventsService.post(new MathFileParsedEvent(ast)));
}
}
package de.monticore.lang.monticar.mpp.core.tex;
import de.monticore.lang.math._ast.ASTMathCompilationUnit;
import de.monticore.lang.monticar.mpp.core.options.OptionsService;
import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.SVGGeneratorContext;
import org.apache.batik.svggen.SVGGraphics2D;
import org.scilab.forge.jlatexmath.DefaultTeXFont;
import org.scilab.forge.jlatexmath.TeXConstants;
import org.scilab.forge.jlatexmath.TeXFormula;
import org.scilab.forge.jlatexmath.TeXIcon;
import org.scilab.forge.jlatexmath.cyrillic.CyrillicRegistration;
import org.scilab.forge.jlatexmath.greek.GreekRegistration;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import java.awt.*;
import java.io.*;
import java.nio.file.Path;
public class SVGGenerator {
protected final OptionsService optionsService;
protected Path outputPath;
public SVGGenerator(OptionsService optionsService) {
this.optionsService = optionsService;
}
public void setContext(ASTMathCompilationUnit ast) {
this.outputPath = this.optionsService.getOutputPath(ast);
File outputDirectory = this.outputPath.toFile();
if (!outputDirectory.exists()) outputDirectory.mkdirs();
}
public String generate(String latex) throws IOException {
DOMImplementation dom = GenericDOMImplementation.getDOMImplementation();
Document document = dom.createDocument("http://www.w3.org/2000/svg", "svg", null);
SVGGeneratorContext context = SVGGeneratorContext.createDefault(document);
SVGGraphics2D graphics = new SVGGraphics2D(context, true);
DefaultTeXFont.registerAlphabet(new CyrillicRegistration());
DefaultTeXFont.registerAlphabet(new GreekRegistration());
TeXFormula formula = new TeXFormula(latex);
TeXIcon icon = formula.createTeXIcon(TeXConstants.STYLE_DISPLAY, 20, 0, new Color(206,206,206));
icon.setInsets(new Insets(0,0,0,0));
graphics.setSVGCanvasSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
graphics.setColor(new Color(37,37,37));
graphics.fillRect(0, 0, icon.getIconWidth(), icon.getIconHeight());
icon.paintIcon(null, graphics, 0, 0);
String filename = String.format("%d.svg", latex.hashCode());
Path outputPath = this.outputPath.resolve(filename);
FileOutputStream stream = new FileOutputStream(outputPath.toString());
Writer out = new OutputStreamWriter(stream, "UTF-8");
graphics.stream(out, true);
stream.flush();
stream.close();
return filename;
}
}
package de.monticore.lang.monticar.mpp.core.visitor;
import java.nio.file.Path;
import java.util.Set;
public class ModelPathVisitedEvent {
public final Path modelPath;
public final Set<Path> models;
public ModelPathVisitedEvent(Path modelPath, Set<Path> models) {
this.modelPath = modelPath;
this.models = models;
}
}
......@@ -2,35 +2,34 @@ package de.monticore.lang.monticar.mpp.core.visitor;
import com.google.common.eventbus.Subscribe;
import de.monticore.lang.monticar.mpp.core.application.ApplicationStartEvent;
import de.monticore.lang.monticar.mpp.core.application.OptionsParsedEvent;
import de.monticore.lang.monticar.mpp.core.options.OptionsParsedEvent;
import de.monticore.lang.monticar.mpp.core.events.EventsService;
import de.monticore.lang.monticar.mpp.core.options.OptionsService;
import org.apache.commons.cli.ParseException;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ModelPathVisitor extends SimpleFileVisitor<Path> {
protected final EventsService eventsService;
protected final OptionsService optionsService;
protected Path modelPath;
protected Set<Path> models;
public ModelPathVisitor(EventsService eventsService) {
public ModelPathVisitor(EventsService eventsService, OptionsService optionsService) {
this.eventsService = eventsService;
this.optionsService = optionsService;
this.eventsService.register(this);
}
protected void visit() throws IOException {
this.models = new HashSet<>();
Files.walkFileTree(this.modelPath, this);
ModelPathVisitedEvent event = new ModelPathVisitedEvent(this.modelPath, this.models);
Path modelPath = this.optionsService.getModelPath();
this.eventsService.post(event);
Files.walkFileTree(modelPath, this);
}
......@@ -41,7 +40,6 @@ public class ModelPathVisitor extends SimpleFileVisitor<Path> {
if (filePath.endsWith(".m")) {
MathFileVisitedEvent event = new MathFileVisitedEvent(file);
this.models.add(file);
this.eventsService.post(event);
}
......@@ -49,13 +47,6 @@ public class ModelPathVisitor extends SimpleFileVisitor<Path> {
}
@Subscribe
public void onOptionsParsed(OptionsParsedEvent event) throws ParseException {
String argument = (String)event.commandLine.getParsedOptionValue("mp");
this.modelPath = Paths.get(argument);
}
@Subscribe
public void onApplicationStart(ApplicationStartEvent event) throws IOException {
this.visit();
......
package de.monticore.lang.monticar.mpp.montimath;
import de.monticore.ast.ASTNode;
import de.monticore.commonexpressions._ast.*;
import de.monticore.expressions