Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.nitorcreations.dopeplugin.DopeMojo Maven / Gradle / Ivy
package com.nitorcreations.dopeplugin;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.util.PDFMergerUtility;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.imgscalr.Scalr;
import org.pegdown.Extensions;
import org.pegdown.LinkRenderer;
import org.pegdown.PegDownProcessor;
import org.pegdown.ToHtmlSerializer;
import org.pegdown.ast.RootNode;
import org.pegdown.ast.VerbatimNode;
import org.python.util.PythonInterpreter;
import com.github.jarlakxen.embedphantomjs.ExecutionTimeout;
import com.github.jarlakxen.embedphantomjs.PhantomJSReference;
import com.github.jarlakxen.embedphantomjs.executor.PhantomJSFileExecutor;
@Mojo( name = "render", defaultPhase = LifecyclePhase.COMPILE )
public class DopeMojo extends AbstractMojo {
@Parameter( defaultValue = "${project.build.directory}/classes/markdown", property = "markdownDir", required = true )
private File markdownDirectory;
@Parameter( defaultValue = "${project.build.directory}/classes/html", property = "htmlDir", required = true )
private File htmlDirectory;
private File htmlTemplate;
private File titleTemplate;
@Parameter( defaultValue = "${project.build.directory}/classes/slides", property = "buildDir", required = true )
private File slidesDirectory;
@Parameter( defaultValue = "${project.build.directory}/classes/slides-small", property = "buildDir", required = true )
private File smallSlidesDirectory;
@Parameter( defaultValue = "${project.build.directory}", property = "buildDir", required = true )
private File buildDirectory;
@Parameter( defaultValue = "${project.groupId}.css", property = "css", required = true )
private String css;
@Parameter( defaultValue = "${project.name}", property = "name", required = true )
private String name;
@Parameter( defaultValue = "${project}", required = true )
private MavenProject project;
@Parameter( defaultValue = "", property = "pngoptimizer" )
private String pngoptimizer;
@Parameter( defaultValue = "UTF-8", property = "charset" )
private String charset;
private static File renderScript;
private static File videoPositionScript;
ExecutorService service = Executors.newCachedThreadPool();
static {
try {
renderScript = extractFile("render.js", ".js");
videoPositionScript = extractFile("videoposition.js", ".js");
} catch (IOException e) {
throw new RuntimeException("Failed to create temporary resource", e);
}
}
public final class RenderHtmlTask implements Callable {
private final File out;
private final Map htmls;
private final Map notes;
private final String markdown;
private final boolean isSlide;
private final String slideName;
private final long lastModified;
public final List> children;
private RenderHtmlTask(File nextSource, Map htmls, Map notes, List> children) throws IOException {
this(new String(Files.readAllBytes(Paths.get(nextSource.toURI())), Charset.defaultCharset()),
htmls, notes, children, nextSource.getName().endsWith(".md"), nextSource.getName().replaceAll("\\.md(\\.notes)?$", ""), nextSource.lastModified());
}
private RenderHtmlTask(String markdown, Map htmls, Map notes,
List> children, boolean isSlide, String slideName, long lastModified) {
this.out = htmlDirectory;
this.markdown = markdown;
this.htmls = htmls;
this.notes = notes;
this.children = children;
this.isSlide = isSlide;
this.slideName = slideName;
this.lastModified = lastModified;
}
public Throwable call() {
try {
PegDownProcessor processor = new PegDownProcessor(Extensions.AUTOLINKS + Extensions.TABLES + Extensions.FENCED_CODE_BLOCKS);
if (isSlide) {
File htmlFinal = new File(out, slideName + ".html");
RootNode astRoot = processor.parseMarkdown(markdown.toCharArray());
String nextHtml = new JHightlihtToHtmlSerializer().toHtml(astRoot);
htmls.put(slideName, nextHtml);
if (htmlFinal.exists() && (htmlFinal.lastModified() >= lastModified)) {
return null;
}
MergeHtml m = new MergeHtml(nextHtml, slideName, htmlFinal);
m.merge();
children.add(service.submit(new RenderPngPdfTask(htmlFinal, "png")));
children.add(service.submit(new RenderPngPdfTask(htmlFinal, "pdf")));
children.add(service.submit(new VideoPositionTask(htmlFinal)));
} else {
RootNode astRoot = processor.parseMarkdown(markdown.toCharArray());
String nextHtml = new JHightlihtToHtmlSerializer().toHtml(astRoot);
notes.put(slideName, nextHtml);
}
} catch (IOException e) {
return e;
}
return null;
}
}
public final class MergeHtml {
private final String html;
private final String slideName;
private final String css;
private final File out;
private final File template;
private final File htmlFinal;
public MergeHtml(String html, String slideName, File htmlFinal) {
this.html = html;
this.slideName = slideName;
this.css = DopeMojo.this.css;
this.out = htmlDirectory;
this.template = htmlTemplate;
this.htmlFinal = htmlFinal;
}
public void merge() throws IOException {
VelocityEngine ve = new VelocityEngine();
ve.setProperty("resource.loader", "file");
ve.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
ve.setProperty("file.resource.loader.path", "");
ve.init();
Template t = ve.getTemplate(template.getAbsolutePath());
VelocityContext context = new VelocityContext();
context.put("name", name);
context.put("slideName", slideName);
context.put("css", css);
context.put("html", html);
context.put("project", project);
File nextOut = new File(out, slideName + ".html.tmp");
FileWriter w = new FileWriter(nextOut);
t.merge( context, w);
w.flush();
nextOut.renameTo(htmlFinal);
}
}
public final class RenderPngPdfTask implements Callable {
private final File slides;
private final File smallSlides;
private final File nextSource;
private final String format;
private RenderPngPdfTask(File nextSource, String format) {
this.slides = slidesDirectory;
this.smallSlides = smallSlidesDirectory;
this.nextSource = nextSource;
this.format = format;
}
@Override
public Throwable call() {
String slideName = nextSource.getName().substring(0, nextSource.getName().length() - 5);
File outFolder;
if ("png".equals(format)) {
outFolder = slides;
} else {
outFolder = buildDirectory;
}
File nextPngPdf = new File(outFolder, slideName + ".tmp." + format);
File finalPngPdf = new File(outFolder, slideName + "." + format);
if (finalPngPdf.exists() && (finalPngPdf.lastModified() >= nextSource.lastModified())) {
return null;
}
PhantomJSFileExecutor ex = new PhantomJSFileExecutor(PhantomJSReference.create().build(), new ExecutionTimeout(10, TimeUnit.SECONDS));
String output;
try {
output = ex.execute(renderScript, nextSource.getAbsolutePath(), nextPngPdf.getAbsolutePath()).get();
} catch (InterruptedException | ExecutionException e) {
return e;
}
if (output.length() == 0) {
nextPngPdf.renameTo(finalPngPdf);
if ("png".equals(format)) {
try {
Future ob = service.submit(new OptimizePngTask(finalPngPdf));
BufferedImage image = ImageIO.read(finalPngPdf);
BufferedImage smallImage =
Scalr.resize(image, Scalr.Method.QUALITY, Scalr.Mode.FIT_TO_WIDTH,
960, 0, Scalr.OP_ANTIALIAS);
File nextSmallPng = new File(smallSlides, finalPngPdf.getName() + ".tmp");
File finalSmallPng = new File(smallSlides, finalPngPdf.getName());
ImageIO.write(smallImage, "png", nextSmallPng);
nextSmallPng.renameTo(finalSmallPng);
Future os = service.submit(new OptimizePngTask(finalSmallPng));
Throwable bt = ob.get();
Throwable st = os.get();
if (bt != null) {
return bt;
} else {
return st;
}
} catch (IOException | InterruptedException | ExecutionException e) {
return e;
}
}
} else {
return new Throwable(String.format("Failed to render %s '%s'.%s: %s", format, slideName, format, output));
}
return null;
}
}
public final class VideoPositionTask implements Callable {
private final File slides;
private final File smallSlides;
private final File nextSource;
private VideoPositionTask(File nextSource) {
this.slides = slidesDirectory;
this.smallSlides = smallSlidesDirectory;
this.nextSource = nextSource;
}
@Override
public Throwable call() {
String slideName = nextSource.getName().substring(0, nextSource.getName().length() - 5);
File nextVideo = new File(slides, slideName + ".tmp.video");
File finalVideo = new File(slides, slideName + ".video");
File nextSmallVideo = new File(smallSlides, slideName + ".tmp.video");
File finalSmallVideo = new File(smallSlides, slideName + ".video");
if (finalVideo.exists() && (finalVideo.lastModified() >= nextSource.lastModified())) {
return null;
}
PhantomJSFileExecutor ex = new PhantomJSFileExecutor(PhantomJSReference.create().build(), new ExecutionTimeout(10, TimeUnit.SECONDS));
String output;
try {
output = ex.execute(videoPositionScript, nextSource.getAbsolutePath()).get();
} catch (InterruptedException | ExecutionException e) {
return e;
}
if (output.length() > 0) {
try (FileOutputStream out = new FileOutputStream(nextVideo);
FileOutputStream smallOut = new FileOutputStream(nextSmallVideo);
) {
out.write(output.getBytes(Charset.defaultCharset()));
out.flush();
smallOut.write(output.getBytes(Charset.defaultCharset()));
smallOut.flush();
} catch (IOException e) {
return e;
}
nextVideo.renameTo(finalVideo);
nextSmallVideo.renameTo(finalSmallVideo);
}
return null;
}
}
public final class OptimizePngTask implements Callable {
private final File png;
public OptimizePngTask(File png) {
this.png = png;
}
@Override
public Throwable call() {
if (pngoptimizer == null || pngoptimizer.length() == 0) {
return null;
}
VelocityEngine ve = new VelocityEngine();
ve.init();
VelocityContext context = new VelocityContext();
context.put("png", png.getAbsolutePath());
context.put("project", project);
StringWriter out = new StringWriter();
if (!ve.evaluate(context, out, "png", pngoptimizer)) {
return new RuntimeException("Failed to merge optimizer template");
}
try {
List list = new ArrayList();
Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(out.toString().trim());
while (m.find()) {
list.add(m.group(1).replace("\"", ""));
}
Process optimize = new ProcessBuilder(list).redirectErrorStream(true).start();
final InputStream is = optimize.getInputStream();
Thread pump = new Thread() {
public void start() {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
try {
while ((line = br.readLine()) != null) {
getLog().debug(line);
}
} catch (IOException e) {
}
}
};
pump.start();
if (optimize.waitFor() != 0) {
return new RuntimeException("Failed to run optimizer - check debug output for why");
}
pump.interrupt();
} catch (IOException | InterruptedException | IllegalThreadStateException e) {
return e;
}
return null;
}
}
public class IndexTemplateTask implements Callable {
private final File nextIndex;
private final Map htmls;
private final Map notes;
private final List slideNames;
private final MavenProject project;
private IndexTemplateTask(File nextIndex, Map htmls, Map notes,
List slideNames) {
this.nextIndex = nextIndex;
this.htmls = htmls;
this.notes = notes;
this.slideNames = slideNames;
this.project = DopeMojo.this.project;
}
@Override
public Throwable call() {
VelocityEngine ve = new VelocityEngine();
ve.setProperty("resource.loader", "file");
ve.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
ve.setProperty("file.resource.loader.path", "");
ve.init();
Template t = ve.getTemplate(nextIndex.getAbsolutePath());
VelocityContext context = new VelocityContext();
context.put("name", name);
context.put("htmls", htmls);
context.put("notes", notes);
context.put("slidenames", slideNames);
context.put("project", project);
context.put("css", css);
File nextOut = new File(nextIndex.getParent(), nextIndex.getName() + ".tmp");
try (FileWriter w = new FileWriter(nextOut)){
t.merge( context, w);
w.flush();
nextOut.renameTo(nextIndex);
} catch (IOException e) {
return e;
}
return null;
}
}
public final class TitleTemplateTask extends IndexTemplateTask {
private final List> children;
public TitleTemplateTask(List> children) {
super(titleTemplate, null, null, null);
this.children = children;
}
@Override
public Throwable call() {
Throwable superRes = super.call();
if (superRes == null) {
children.add(service.submit(new RenderPngPdfTask(titleTemplate, "png")));
children.add(service.submit(new RenderPngPdfTask(titleTemplate, "pdf")));
children.add(service.submit(new VideoPositionTask(titleTemplate)));
return null;
} else {
return superRes;
}
}
}
private static File extractFile(String name, String suffix) throws IOException {
File target = File.createTempFile(name.substring(0, name.length() - suffix.length()), suffix);
target.deleteOnExit();
try (FileOutputStream outStream = new FileOutputStream(target);
InputStream inStream =
DopeMojo.class.getClassLoader().getResourceAsStream(name)) {
IOUtils.copy(inStream, outStream);
return target;
}
}
public void execute() throws MojoExecutionException {
File f = markdownDirectory;
htmlTemplate = new File(htmlDirectory, "slidetemplate.html");
titleTemplate = new File(htmlDirectory, "title.html");
getLog().debug(String.format("Markdown from %s", f.getAbsolutePath()));
if ( !f.exists() ) {
return;
}
getLog().debug(String.format("HTML to %s", htmlDirectory.getAbsolutePath()));
ensureDir(htmlDirectory);
getLog().debug(String.format("Slides to %s", slidesDirectory.getAbsolutePath()));
ensureDir(slidesDirectory);
getLog().debug(String.format("Small slides to %s", smallSlidesDirectory.getAbsolutePath()));
ensureDir(smallSlidesDirectory);
final File[] sources = f.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".md") || name.endsWith(".md.notes");
}
});
getLog().info(String.format("Processing %d markdown files", sources.length));
final Map notes = new ConcurrentHashMap<>();
final Map htmls = new ConcurrentHashMap<>();
final List> execs = new ArrayList<>();
final List> children = new CopyOnWriteArrayList<>();
final ArrayList slideNames = new ArrayList<>();
for (int i=0; i -1) {
String slideId = slideName + "$" + index;
if (nextIsSlide) {
slideNames.add(slideId);
index++;
} else {
slideId = slideName + "$" + (index-1);
}
execs.add(service.submit(new RenderHtmlTask(nextMarkdown.substring(slideStart, nextStart), htmls, notes, children, nextIsSlide, slideId, nextSource.lastModified())));
nextIsSlide = !nextMarkdown.regionMatches(nextStart, "