Remove usage of GhostScript and replace it by pdftocairo and pdfseparate.

This commit is contained in:
Ghazi Triki 2017-04-19 16:30:46 +01:00
parent 106dbb3275
commit 466c931b92
14 changed files with 366 additions and 585 deletions

View File

@ -34,21 +34,11 @@ swfToolsDir=/usr/bin
# Directory where ImageMagick's convert executable is located
imageMagickDir=/usr/bin
#----------------------------------------------------
# Use fullpath to ghostscript executable since the exec names are different
# for each platform.
ghostScriptExec=/usr/bin/gs
#----------------------------------------------------
# Fonts directory passed into PDF2SWF to support highlighting of texts
# in the SWF slides.
fontsDir=/usr/share/fonts
#----------------------------------------------------
# This is a workaround for a problem converting PDF files, referenced at
# http://groups.google.com/group/comp.lang.postscript/browse_thread/thread/c2e264ca76534ce0?pli=1
noPdfMarkWorkaround=/etc/bigbluebutton/nopdfmark.ps
#----------------------------------------------------
# These will be copied in cases where the conversion process
# fails to generate a slide from the uploaded presentation

View File

@ -21,8 +21,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
">
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="documentConversionService" class="org.bigbluebutton.presentation.DocumentConversionServiceImp">
<property name="messagingService" ref="messagingService"/>
@ -37,14 +36,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="officeDocumentValidator" ref="officeDocumentValidator"/>
</bean>
<bean id="pageExtractor" class="org.bigbluebutton.presentation.imp.GhostscriptPageExtractor">
<property name="ghostscriptExec" value="${ghostScriptExec}"/>
<property name="noPdfMarkWorkaround" value="${noPdfMarkWorkaround}"/>
</bean>
<bean id="imageMagickPageConverter" class="org.bigbluebutton.presentation.imp.ImageMagickPageConverter">
<property name="imageMagickDir" value="${imageMagickDir}"/>
</bean>
<bean id="pageExtractor" class="org.bigbluebutton.presentation.imp.GhostscriptPageExtractor"/>
<bean id="png2SwfConverter" class="org.bigbluebutton.presentation.imp.Png2SwfPageConverter">
<property name="swfToolsDir" value="${swfToolsDir}"/>
@ -64,21 +56,13 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
</bean>
<bean id="pdf2SwfPageConverter" class="org.bigbluebutton.presentation.imp.Pdf2SwfPageConverter">
<property name="ghostscriptExec" value="${ghostScriptExec}"/>
<property name="swfToolsDir" value="${swfToolsDir}"/>
<property name="fontsDir" value="${fontsDir}"/>
<property name="noPdfMarkWorkaround" value="${noPdfMarkWorkaround}"/>
<property name="placementsThreshold" value="${placementsThreshold}"/>
<property name="defineTextThreshold" value="${defineTextThreshold}"/>
<property name="imageTagThreshold" value="${imageTagThreshold}"/>
</bean>
<bean id="imageConvSvc" class="org.bigbluebutton.presentation.imp.PdfPageToImageConversionService">
<property name="pageExtractor" ref="pageExtractor"/>
<property name="pdfToImageConverter" ref="imageMagickPageConverter"/>
<property name="imageToSwfConverter" ref="png2SwfConverter"/>
</bean>
<bean id="thumbCreator" class="org.bigbluebutton.presentation.imp.ThumbnailCreatorImp">
<property name="imageMagickDir" value="${imageMagickDir}"/>
<property name="blankThumbnail" value="${BLANK_THUMBNAIL}"/>
@ -98,7 +82,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<constructor-arg index="0" value="${numConversionThreads}"/>
<property name="counterService" ref="pageCounterService"/>
<property name="pageConverter" ref="pdf2SwfPageConverter"/>
<property name="pdfPageToImageConversionService" ref="imageConvSvc"/>
<property name="thumbnailCreator" ref="thumbCreator"/>
<property name="textFileCreator" ref="textFileCreator"/>
<property name="svgImageCreator" ref="svgImageCreator"/>

View File

@ -25,7 +25,6 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.bigbluebutton.presentation.imp.PdfPageToImageConversionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,7 +36,6 @@ public class PdfToSwfSlide {
private UploadedPresentation pres;
private int page;
private PageConverter pdfToSwfConverter;
private PdfPageToImageConversionService imageConvertService;
private String BLANK_SLIDE;
private int MAX_SWF_FILE_SIZE;
@ -51,26 +49,14 @@ public class PdfToSwfSlide {
public PdfToSwfSlide createSlide() {
File presentationFile = pres.getUploadedFile();
slide = new File(presentationFile.getParent() + File.separatorChar + "slide-" + page + ".swf");
if (! pdfToSwfConverter.convert(presentationFile, slide, page, pres)) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", pres.getMeetingId());
logData.put("presId", pres.getId());
logData.put("filename", pres.getName());
logData.put("page", page);
logData.put("size(KB)", slide.length()/1024);
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.warn("Failed to convert slide: data={}", logStr);
imageConvertService.convertPageAsAnImage(presentationFile, slide, page, pres);
}
slide = new File(presentationFile.getParent() + File.separatorChar
+ "slide-" + page + ".swf");
pdfToSwfConverter.convert(presentationFile, slide, page, pres);
// If all fails, generate a blank slide.
if (!slide.exists()) {
log.warn("Failed to create slide. Creating blank slide for " + slide.getAbsolutePath());
log.warn("Failed to create slide. Creating blank slide for "
+ slide.getAbsolutePath());
generateBlankSlide();
}
@ -79,13 +65,6 @@ public class PdfToSwfSlide {
return this;
}
private boolean slideMayHaveTooManyObjects(File slide) {
// If the resulting swf file is greater than 500K, it probably contains a lot of objects
// that it becomes very slow to render on the client. Take an image snapshot instead and
// use it to generate the SWF file. (ralam Sept 2, 2009)
return slide.length() > MAX_SWF_FILE_SIZE;
}
public void generateBlankSlide() {
if (BLANK_SLIDE != null) {
Map<String, Object> logData = new HashMap<String, Object>();
@ -95,7 +74,7 @@ public class PdfToSwfSlide {
logData.put("page", page);
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.warn("Creating blank slide: data={}", logStr);
@ -108,7 +87,7 @@ public class PdfToSwfSlide {
logData.put("page", page);
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.warn("Failed to create blank slide: data={}", logStr);
}
@ -126,10 +105,6 @@ public class PdfToSwfSlide {
this.pdfToSwfConverter = converter;
}
public void setPdfPageToImageConversionService(PdfPageToImageConversionService service) {
this.imageConvertService = service;
}
public void setBlankSlide(String blankSlide) {
this.BLANK_SLIDE = blankSlide;
}

View File

@ -26,35 +26,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GhostscriptPageExtractor implements PageExtractor {
private static Logger log = LoggerFactory
.getLogger(GhostscriptPageExtractor.class);
private static Logger log = LoggerFactory
.getLogger(GhostscriptPageExtractor.class);
private String GHOSTSCRIPT_EXEC;
private String noPdfMarkWorkaround;
private String SPACE = " ";
private String SPACE = " ";
public boolean extractPage(File presentationFile, File output, int page) {
String OPTIONS = "-sDEVICE=pdfwrite -dNOPAUSE -dQUIET -dBATCH";
String FIRST_PAGE = "-dFirstPage=" + page;
String LAST_PAGE = "-dLastPage=" + page;
String DESTINATION = output.getAbsolutePath();
String OUTPUT_FILE = "-sOutputFile=" + DESTINATION;
public boolean extractPage(File presentationFile, File output, int page) {
String COMMAND = "pdfseparate -f " + page + " -l " + page + SPACE
+ presentationFile.getAbsolutePath() + SPACE + output.getAbsolutePath();
// extract that specific page and create a temp-pdf(only one page) with
// GhostScript
String COMMAND = GHOSTSCRIPT_EXEC + SPACE + OPTIONS + SPACE
+ FIRST_PAGE + SPACE + LAST_PAGE + SPACE + OUTPUT_FILE + SPACE
+ noPdfMarkWorkaround + SPACE
+ presentationFile.getAbsolutePath();
return new ExternalProcessExecutor().exec(COMMAND, 60000);
}
public void setGhostscriptExec(String exec) {
GHOSTSCRIPT_EXEC = exec;
}
public void setNoPdfMarkWorkaround(String noPdfMarkWorkaround) {
this.noPdfMarkWorkaround = noPdfMarkWorkaround;
}
log.info("Extracting page {} for document {}", page,
presentationFile.getAbsolutePath());
return new ExternalProcessExecutor().exec(COMMAND, 60000);
}
}

View File

@ -1,54 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program 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 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.presentation.imp;
import java.io.File;
import org.bigbluebutton.presentation.PageConverter;
import org.bigbluebutton.presentation.UploadedPresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ImageMagickPageConverter implements PageConverter {
private static Logger log = LoggerFactory.getLogger(ImageMagickPageConverter.class);
private String IMAGEMAGICK_DIR;
public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres){
String COMMAND = IMAGEMAGICK_DIR + "/convert -depth 8 " + presentationFile.getAbsolutePath() + " " + output.getAbsolutePath();
boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000);
if (done && output.exists()) {
return true;
} else {
log.warn("Failed to convert: " + output.getAbsolutePath() + " does not exist.");
return false;
}
}
public void setImageMagickDir(String dir) {
IMAGEMAGICK_DIR = dir;
}
}

View File

@ -33,7 +33,7 @@ public class Jpeg2SwfPageConverter implements PageConverter {
public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres){
String COMMAND = SWFTOOLS_DIR + "/jpeg2swf -o " + output.getAbsolutePath() + " " + presentationFile.getAbsolutePath();
String COMMAND = SWFTOOLS_DIR + File.separator + "jpeg2swf -o " + output.getAbsolutePath() + " " + presentationFile.getAbsolutePath();
boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000);

View File

@ -44,10 +44,8 @@ public class Pdf2SwfPageConverter implements PageConverter {
private static Logger log = LoggerFactory
.getLogger(Pdf2SwfPageConverter.class);
private String GHOSTSCRIPT_EXEC;
private String SWFTOOLS_DIR;
private String fontsDir;
private String noPdfMarkWorkaround;
private long placementsThreshold;
private long defineTextThreshold;
private long imageTagThreshold;
@ -86,13 +84,6 @@ public class Pdf2SwfPageConverter implements PageConverter {
long pdf2SwfEnd = System.currentTimeMillis();
log.debug("Pdf2Swf conversion duration: {} sec",
(pdf2SwfEnd - pdf2SwfStart) / 1000);
boolean timedOut = pdf2SwfEnd
- pdf2SwfStart >= Integer.parseInt(convTimeout.replaceFirst("s", ""))
* 1000;
boolean twiceTotalObjects = pHandler.numberOfPlacements()
+ pHandler.numberOfTextTags()
+ pHandler.numberOfImageTags() >= (placementsThreshold
+ defineTextThreshold + imageTagThreshold) * 2;
File destFile = new File(dest);
if (pHandler.isConversionSuccessful() && destFile.exists()
@ -122,7 +113,6 @@ public class Pdf2SwfPageConverter implements PageConverter {
log.warn("Potential problem with generated SWF: data={}", logStr);
File tempPdfPage = null;
File tempPng = null;
String basePresentationame = FilenameUtils
.getBaseName(presentation.getName());
@ -135,14 +125,12 @@ public class Pdf2SwfPageConverter implements PageConverter {
long gsStart = System.currentTimeMillis();
// Step 1: Convert a PDF page to PNG using a raw GhostScript command
// Step 1: Convert a PDF page to PNG using a raw pdftocairo
NuProcessBuilder pbPng = new NuProcessBuilder(Arrays.asList("timeout",
convTimeout, GHOSTSCRIPT_EXEC, "-sDEVICE=png16m", "-dNOPAUSE",
"-dQUIET", "-dBATCH",
!timedOut && !twiceTotalObjects ? "-r150" : "-r72",
"-dGraphicsAlphaBits=4", "-dTextAlphaBits=4", "-dFirstPage=" + page,
"-dLastPage=" + page, "-sOutputFile=" + tempPng.getAbsolutePath(),
noPdfMarkWorkaround, presentation.getAbsolutePath()));
convTimeout, "pdftocairo", "-png", "-singlefile", "-r", "150", "-f",
String.valueOf(page), "-l", String.valueOf(page),
presentation.getAbsolutePath(), tempPng.getAbsolutePath().substring(0,
tempPng.getAbsolutePath().lastIndexOf('.'))));
Pdf2PngPageConverterHandler pbPngHandler = new Pdf2PngPageConverterHandler();
pbPng.setProcessListener(pbPngHandler);
@ -154,12 +142,13 @@ public class Pdf2SwfPageConverter implements PageConverter {
}
long gsEnd = System.currentTimeMillis();
log.debug("Ghostscript conversion duration: {} sec",
log.debug("pdftocairo conversion duration: {} sec",
(gsEnd - gsStart) / 1000);
long png2swfStart = System.currentTimeMillis();
// Step 2: Convert a PNG image to SWF
// We need to update the file path as pdftocairo adds "-page.png"
source = tempPng.getAbsolutePath();
NuProcessBuilder pbSwf = new NuProcessBuilder(
Arrays.asList("timeout", convTimeout,
@ -174,7 +163,7 @@ public class Pdf2SwfPageConverter implements PageConverter {
}
long png2swfEnd = System.currentTimeMillis();
log.debug("ImageMagick conversion duration: {} sec",
log.debug("SwfTools conversion duration: {} sec",
(png2swfEnd - png2swfStart) / 1000);
// Delete the temporary PNG and PDF files after finishing the image
@ -225,13 +214,4 @@ public class Pdf2SwfPageConverter implements PageConverter {
public void setImageTagThreshold(long threshold) {
imageTagThreshold = threshold;
}
public void setGhostscriptExec(String exec) {
GHOSTSCRIPT_EXEC = exec;
}
public void setNoPdfMarkWorkaround(String noPdfMarkWorkaround) {
this.noPdfMarkWorkaround = noPdfMarkWorkaround;
}
}

View File

@ -40,7 +40,7 @@ public class Pdf2SwfPageCounter implements PageCounter {
public int countNumberOfPages(File presentationFile) {
int numPages = 0; //total numbers of this pdf
String COMMAND = SWFTOOLS_DIR + "/pdf2swf -I " + presentationFile.getAbsolutePath();
String COMMAND = SWFTOOLS_DIR + File.separator + "pdf2swf -I " + presentationFile.getAbsolutePath();
Timer timer = null;
Process p = null;

View File

@ -1,67 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program 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 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.presentation.imp;
import java.io.File;
import org.bigbluebutton.presentation.PageConverter;
import org.bigbluebutton.presentation.PageExtractor;
import org.bigbluebutton.presentation.UploadedPresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PdfPageToImageConversionService {
private static Logger log = LoggerFactory.getLogger(PdfPageToImageConversionService.class);
private PageExtractor extractor;
private PageConverter pdfToImageConverter;
private PageConverter imageToSwfConverter;
public boolean convertPageAsAnImage(File presentationFile, File output, int page, UploadedPresentation pres) {
File tempDir = new File(presentationFile.getParent() + File.separatorChar + "temp");
tempDir.mkdir();
File tempPdfFile = new File(tempDir.getAbsolutePath() + File.separator + "temp-" + page + ".pdf");
if (extractor.extractPage(presentationFile, tempPdfFile, page)) {
File tempPngFile = new File(tempDir.getAbsolutePath() + "/temp-" + page + ".svg");
if (pdfToImageConverter.convert(tempPdfFile, tempPngFile, 1, pres)) {
if (imageToSwfConverter.convert(tempPngFile, output, 1, pres)) {
return true;
}
}
}
return false;
}
public void setPageExtractor(PageExtractor extractor) {
this.extractor = extractor;
}
public void setPdfToImageConverter(PageConverter imageConverter) {
this.pdfToImageConverter = imageConverter;
}
public void setImageToSwfConverter(PageConverter swfConverter) {
this.imageToSwfConverter = swfConverter;
}
}

View File

@ -48,22 +48,21 @@ import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
public class PdfToSwfSlidesGenerationService {
private static Logger log = LoggerFactory.getLogger(PdfToSwfSlidesGenerationService.class);
private static Logger log = LoggerFactory
.getLogger(PdfToSwfSlidesGenerationService.class);
private SwfSlidesGenerationProgressNotifier notifier;
private PageCounterService counterService;
private PageConverter pdfToSwfConverter;
private PdfPageToImageConversionService imageConvertService;
private ExecutorService executor;
private ThumbnailCreator thumbnailCreator;
private TextFileCreator textFileCreator;
private SvgImageCreator svgImageCreator;
private long MAX_CONVERSION_TIME = 5*60*1000;
private long MAX_CONVERSION_TIME = 5 * 60 * 1000;
private String BLANK_SLIDE;
private int MAX_SWF_FILE_SIZE;
private boolean svgImagesRequired;
private final long CONVERSION_TIMEOUT = 20000000000L; // 20s
private int NUM_CONVERSION_THREADS = 2;
public PdfToSwfSlidesGenerationService(int numConversionThreads) {
executor = Executors.newFixedThreadPool(numConversionThreads);
@ -95,12 +94,15 @@ public class PdfToSwfSlidesGenerationService {
return false;
}
private void sendFailedToCountPageMessage(CountingPageException e, UploadedPresentation pres) {
private void sendFailedToCountPageMessage(CountingPageException e,
UploadedPresentation pres) {
MessageBuilder builder = new ConversionUpdateMessage.MessageBuilder(pres);
if (e.getExceptionType() == CountingPageException.ExceptionType.PAGE_COUNT_EXCEPTION) {
if (e
.getExceptionType() == CountingPageException.ExceptionType.PAGE_COUNT_EXCEPTION) {
builder.messageKey(ConversionMessageConstants.PAGE_COUNT_FAILED_KEY);
} else if (e.getExceptionType() == CountingPageException.ExceptionType.PAGE_EXCEEDED_EXCEPTION) {
} else if (e
.getExceptionType() == CountingPageException.ExceptionType.PAGE_EXCEEDED_EXCEPTION) {
builder.numberOfPages(e.getPageCount());
builder.maxNumberPages(e.getMaxNumberOfPages());
builder.messageKey(ConversionMessageConstants.PAGE_COUNT_EXCEEDED_KEY);
@ -127,13 +129,16 @@ public class PdfToSwfSlidesGenerationService {
int numPages = pres.getNumberOfPages();
List<PdfToSwfSlide> slides = setupSlides(pres, numPages);
CompletionService<PdfToSwfSlide> completionService = new ExecutorCompletionService<PdfToSwfSlide>(executor);
CompletionService<PdfToSwfSlide> completionService = new ExecutorCompletionService<PdfToSwfSlide>(
executor);
generateSlides(pres, slides, completionService);
}
private void generateSlides(UploadedPresentation pres, List<PdfToSwfSlide> slides, CompletionService<PdfToSwfSlide> completionService) {
long MAXWAIT = MAX_CONVERSION_TIME * 60 /*seconds*/ * 1000 /*millis*/;
private void generateSlides(UploadedPresentation pres,
List<PdfToSwfSlide> slides,
CompletionService<PdfToSwfSlide> completionService) {
long MAXWAIT = MAX_CONVERSION_TIME * 60 /* seconds */ * 1000 /* millis */;
int slidesCompleted = 0;
long presConvStart = System.currentTimeMillis();
@ -163,7 +168,7 @@ public class PdfToSwfSlidesGenerationService {
logData.put("page", slide.getPageNumber());
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.warn("ExecutionException while converting page: data={}", logStr);
log.error(e.getMessage());
@ -175,7 +180,7 @@ public class PdfToSwfSlidesGenerationService {
logData.put("page", slide.getPageNumber());
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.warn("InterruptedException while converting page: data={}", logStr);
Thread.currentThread().interrupt();
@ -187,7 +192,7 @@ public class PdfToSwfSlidesGenerationService {
logData.put("page", slide.getPageNumber());
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.warn("TimeoutException while converting page: data={}", logStr);
f.cancel(true);
@ -199,16 +204,16 @@ public class PdfToSwfSlidesGenerationService {
logData.put("presId", pres.getId());
logData.put("filename", pres.getName());
logData.put("page", slide.getPageNumber());
logData.put("conversionTime(sec)", (pageConvEnd - pageConvStart)/1000);
logData.put("conversionTime(sec)", (pageConvEnd - pageConvStart) / 1000);
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.debug("Page conversion duration(sec): data={}", logStr);
}
for (final PdfToSwfSlide slide : slides) {
if (! slide.isDone()){
if (!slide.isDone()) {
slide.generateBlankSlide();
@ -218,7 +223,7 @@ public class PdfToSwfSlidesGenerationService {
logData.put("filename", pres.getName());
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.warn("Creating blank slide: data={}", logStr);
@ -231,15 +236,15 @@ public class PdfToSwfSlidesGenerationService {
logData.put("meetingId", pres.getMeetingId());
logData.put("presId", pres.getId());
logData.put("filename", pres.getName());
logData.put("conversionTime(sec)", (presConvEnd - presConvStart)/1000);
logData.put("conversionTime(sec)", (presConvEnd - presConvStart) / 1000);
Gson gson = new Gson();
String logStr = gson.toJson(logData);
String logStr = gson.toJson(logData);
log.debug("Presentation conversion duration (sec): data={}", logStr);
}
private List<PdfToSwfSlide> setupSlides(UploadedPresentation pres, int numPages) {
private List<PdfToSwfSlide> setupSlides(UploadedPresentation pres,
int numPages) {
List<PdfToSwfSlide> slides = new ArrayList<PdfToSwfSlide>(numPages);
for (int page = 1; page <= numPages; page++) {
@ -247,7 +252,6 @@ public class PdfToSwfSlidesGenerationService {
slide.setBlankSlide(BLANK_SLIDE);
slide.setMaxSwfFileSize(MAX_SWF_FILE_SIZE);
slide.setPageConverter(pdfToSwfConverter);
slide.setPdfPageToImageConversionService(imageConvertService);
slides.add(slide);
}
@ -255,8 +259,6 @@ public class PdfToSwfSlidesGenerationService {
return slides;
}
public void setCounterService(PageCounterService counterService) {
this.counterService = counterService;
}
@ -265,10 +267,6 @@ public class PdfToSwfSlidesGenerationService {
this.pdfToSwfConverter = converter;
}
public void setPdfPageToImageConversionService(PdfPageToImageConversionService service) {
this.imageConvertService = service;
}
public void setBlankSlide(String blankSlide) {
this.BLANK_SLIDE = blankSlide;
}
@ -284,17 +282,21 @@ public class PdfToSwfSlidesGenerationService {
public void setThumbnailCreator(ThumbnailCreator thumbnailCreator) {
this.thumbnailCreator = thumbnailCreator;
}
public void setTextFileCreator(TextFileCreator textFileCreator) {
this.textFileCreator = textFileCreator;
}
public void setSvgImageCreator(SvgImageCreator svgImageCreator) {
this.svgImageCreator = svgImageCreator;
}
public void setMaxConversionTime(int minutes) {
MAX_CONVERSION_TIME = minutes * 60 * 1000;
}
public void setSwfSlidesGenerationProgressNotifier(SwfSlidesGenerationProgressNotifier notifier) {
public void setSwfSlidesGenerationProgressNotifier(
SwfSlidesGenerationProgressNotifier notifier) {
this.notifier = notifier;
}

View File

@ -32,7 +32,7 @@ public class Png2SwfPageConverter implements PageConverter {
private String SWFTOOLS_DIR;
public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres){
String COMMAND = SWFTOOLS_DIR + "/png2swf -o " + output.getAbsolutePath() + " " + presentationFile.getAbsolutePath();
String COMMAND = SWFTOOLS_DIR + File.separator + "png2swf -o " + output.getAbsolutePath() + " " + presentationFile.getAbsolutePath();
boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000);

View File

@ -9,113 +9,88 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SvgImageCreatorImp implements SvgImageCreator {
private static Logger log = LoggerFactory
.getLogger(SvgImageCreatorImp.class);
private static Logger log = LoggerFactory.getLogger(SvgImageCreatorImp.class);
private String IMAGEMAGICK_DIR;
private String IMAGEMAGICK_DIR;
@Override
public boolean createSvgImages(UploadedPresentation pres) {
boolean success = false;
File svgImagesPresentationDir = determineSvgImagesDirectory(pres
.getUploadedFile());
if (!svgImagesPresentationDir.exists())
svgImagesPresentationDir.mkdir();
@Override
public boolean createSvgImages(UploadedPresentation pres) {
boolean success = false;
File svgImagesPresentationDir = determineSvgImagesDirectory(
pres.getUploadedFile());
if (!svgImagesPresentationDir.exists())
svgImagesPresentationDir.mkdir();
cleanDirectory(svgImagesPresentationDir);
cleanDirectory(svgImagesPresentationDir);
try {
extractPdfPages(pres);
success = generateSvgImages(svgImagesPresentationDir, pres);
} catch (InterruptedException e) {
log.warn("Interrupted Exception while generating images.");
success = false;
try {
success = generateSvgImages(svgImagesPresentationDir, pres);
} catch (InterruptedException e) {
log.warn("Interrupted Exception while generating images.");
success = false;
}
return success;
}
private boolean generateSvgImages(File imagePresentationDir,
UploadedPresentation pres) throws InterruptedException {
String source = pres.getUploadedFile().getAbsolutePath();
String dest;
String COMMAND = "";
boolean done = true;
if (SupportedFileTypes.isImageFile(pres.getFileType())) {
dest = imagePresentationDir.getAbsolutePath() + File.separator
+ "slide1.pdf";
COMMAND = IMAGEMAGICK_DIR + File.separator + "convert " + source + " "
+ dest;
done = new ExternalProcessExecutor().exec(COMMAND, 60000);
source = imagePresentationDir.getAbsolutePath() + File.separator
+ "slide1.pdf";
dest = imagePresentationDir.getAbsolutePath() + File.separator
+ "slide1.svg";
COMMAND = "pdftocairo -rx 300 -ry 300 -svg -q -f 1 -l 1 " + source + " "
+ dest;
done = new ExternalProcessExecutor().exec(COMMAND, 60000);
} else {
for (int i = 1; i <= pres.getNumberOfPages(); i++) {
File destsvg = new File(imagePresentationDir.getAbsolutePath()
+ File.separatorChar + "slide" + i + ".svg");
COMMAND = "pdftocairo -rx 300 -ry 300 -svg -q -f " + i + " -l " + i
+ " " + File.separatorChar + source + " "
+ destsvg.getAbsolutePath();
done = new ExternalProcessExecutor().exec(COMMAND, 60000);
if (!done) {
break;
}
return success;
}
}
private void extractPdfPages(UploadedPresentation pres) {
File pdfDir = new File(pres.getUploadedFile().getParent()
+ File.separatorChar + "pdfs");
if (!pdfDir.exists())
pdfDir.mkdir();
if (SupportedFileTypes.isPdfFile(pres.getFileType())) {
for (int i = 1; i <= pres.getNumberOfPages(); i++) {
File pdfFile = new File(pdfDir.getAbsolutePath()
+ File.separatorChar + "slide" + i + ".pdf");
String COMMAND = IMAGEMAGICK_DIR
+ "/gs -sDEVICE=pdfwrite -dNOPAUSE -dQUIET -dBATCH -dFirstPage="
+ i + " -dLastPage=" + i + " -sOutputFile="
+ pdfFile.getAbsolutePath()
+ " /etc/bigbluebutton/nopdfmark.ps "
+ pres.getUploadedFile().getAbsolutePath();
new ExternalProcessExecutor().exec(COMMAND, 60000);
}
}
if (done) {
return true;
}
log.warn("Failed to create svg images: " + COMMAND);
return false;
}
private boolean generateSvgImages(File imagePresentationDir,
UploadedPresentation pres) throws InterruptedException {
String source = pres.getUploadedFile().getAbsolutePath();
String dest;
String COMMAND = "";
boolean done = true;
if (SupportedFileTypes.isImageFile(pres.getFileType())) {
dest = imagePresentationDir.getAbsolutePath() + File.separator
+ "slide1.pdf";
COMMAND = IMAGEMAGICK_DIR + "/convert " + source + " " + dest;
done = new ExternalProcessExecutor().exec(COMMAND, 60000);
private File determineSvgImagesDirectory(File presentationFile) {
return new File(presentationFile.getParent() + File.separatorChar + "svgs");
}
source = imagePresentationDir.getAbsolutePath() + File.separator
+ "slide1.pdf";
dest = imagePresentationDir.getAbsolutePath() + File.separator
+ "slide1.svg";
COMMAND = "pdftocairo -rx 300 -ry 300 -svg -q -f 1 -l 1 " + source
+ " " + dest;
done = new ExternalProcessExecutor().exec(COMMAND, 60000);
} else {
for (int i = 1; i <= pres.getNumberOfPages(); i++) {
File pdfFile = new File(imagePresentationDir.getParent()
+ File.separatorChar + "pdfs" + File.separatorChar
+ "slide" + i + ".pdf");
File destsvg = new File(imagePresentationDir.getAbsolutePath()
+ File.separatorChar + "slide" + i + ".svg");
COMMAND = "pdftocairo -rx 300 -ry 300 -svg -q -f 1 -l 1 "
+ File.separatorChar + pdfFile.getAbsolutePath() + " "
+ destsvg.getAbsolutePath();
done = new ExternalProcessExecutor().exec(COMMAND, 60000);
if (!done) {
break;
}
}
}
if (done) {
return true;
}
log.warn("Failed to create svg images: " + COMMAND);
return false;
private void cleanDirectory(File directory) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
files[i].delete();
}
}
private File determineSvgImagesDirectory(File presentationFile) {
return new File(presentationFile.getParent() + File.separatorChar
+ "svgs");
}
private void cleanDirectory(File directory) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
files[i].delete();
}
}
public void setImageMagickDir(String imageMagickDir) {
IMAGEMAGICK_DIR = imageMagickDir;
}
public void setImageMagickDir(String imageMagickDir) {
IMAGEMAGICK_DIR = imageMagickDir;
}
}

View File

@ -32,90 +32,95 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TextFileCreatorImp implements TextFileCreator {
private static Logger log = LoggerFactory.getLogger(TextFileCreatorImp.class);
private static Logger log = LoggerFactory.getLogger(TextFileCreatorImp.class);
private String IMAGEMAGICK_DIR;
private String IMAGEMAGICK_DIR;
@Override
public boolean createTextFiles(UploadedPresentation pres) {
boolean success = false;
File textfilesDir = determineTextfilesDirectory(pres.getUploadedFile());
if (! textfilesDir.exists())
textfilesDir.mkdir();
@Override
public boolean createTextFiles(UploadedPresentation pres) {
boolean success = false;
File textfilesDir = determineTextfilesDirectory(pres.getUploadedFile());
if (!textfilesDir.exists())
textfilesDir.mkdir();
cleanDirectory(textfilesDir);
cleanDirectory(textfilesDir);
try {
success = generateTextFiles(textfilesDir, pres);
} catch (InterruptedException e) {
log.warn("Interrupted Exception while generating thumbnails.");
success = false;
}
try {
success = generateTextFiles(textfilesDir, pres);
} catch (InterruptedException e) {
log.warn("Interrupted Exception while generating thumbnails.");
success = false;
}
//TODO: in case that it doesn't generated the textfile, we should create a textfile with some message
// createUnavailableTextFile
// TODO: in case that it doesn't generated the textfile, we should create a
// textfile with some message
// createUnavailableTextFile
return success;
}
return success;
}
private boolean generateTextFiles(File textfilesDir, UploadedPresentation pres) throws InterruptedException {
boolean success = true;
String source = pres.getUploadedFile().getAbsolutePath();
String dest;
String COMMAND = "";
private boolean generateTextFiles(File textfilesDir,
UploadedPresentation pres) throws InterruptedException {
boolean success = true;
String source = pres.getUploadedFile().getAbsolutePath();
String dest;
String COMMAND = "";
if(SupportedFileTypes.isImageFile(pres.getFileType())){
dest = textfilesDir.getAbsolutePath() + File.separator + "slide-1.txt";
String text = "No text could be retrieved for the slide";
if (SupportedFileTypes.isImageFile(pres.getFileType())) {
dest = textfilesDir.getAbsolutePath() + File.separator + "slide-1.txt";
String text = "No text could be retrieved for the slide";
File file = new File(dest);
Writer writer = null;
try {
writer = new BufferedWriter(new FileWriter(file));
writer.write(text);
} catch (IOException e) {
log.error("Error: " + e.getMessage());
success = false;
} finally {
try {
writer.close();
} catch (IOException e) {
log.error("Error: " + e.getMessage());
success = false;
}
}
File file = new File(dest);
Writer writer = null;
try {
writer = new BufferedWriter(new FileWriter(file));
writer.write(text);
} catch (IOException e) {
log.error("Error: " + e.getMessage());
success = false;
} finally {
try {
writer.close();
} catch (IOException e) {
log.error("Error: " + e.getMessage());
success = false;
}
}
}else{
dest = textfilesDir.getAbsolutePath() + File.separator + "slide-";
// sudo apt-get install xpdf-utils
for( int i = 1; i <= pres.getNumberOfPages(); i++){
COMMAND = IMAGEMAGICK_DIR + "/pdftotext -raw -nopgbrk -f "+ i +" -l " + i + " " + source + " " + dest + i + ".txt";
boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000);
if (!done) {
success = false;
log.warn("Failed to create textfiles: " + COMMAND);
break;
}
}
} else {
dest = textfilesDir.getAbsolutePath() + File.separator + "slide-";
// sudo apt-get install xpdf-utils
for (int i = 1; i <= pres.getNumberOfPages(); i++) {
COMMAND = IMAGEMAGICK_DIR + File.separator
+ "pdftotext -raw -nopgbrk -enc UTF-8 -f " + i + " -l " + i + " "
+ source + " " + dest + i + ".txt";
boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000);
if (!done) {
success = false;
log.warn("Failed to create textfiles: " + COMMAND);
break;
}
}
}
}
return success;
}
return success;
}
private File determineTextfilesDirectory(File presentationFile) {
return new File(presentationFile.getParent() + File.separatorChar + "textfiles");
}
private File determineTextfilesDirectory(File presentationFile) {
return new File(
presentationFile.getParent() + File.separatorChar + "textfiles");
}
private void cleanDirectory(File directory) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
files[i].delete();
}
}
private void cleanDirectory(File directory) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
files[i].delete();
}
}
public void setImageMagickDir(String imageMagickDir) {
IMAGEMAGICK_DIR = imageMagickDir;
}
public void setImageMagickDir(String imageMagickDir) {
IMAGEMAGICK_DIR = imageMagickDir;
}
}

View File

@ -32,132 +32,142 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ThumbnailCreatorImp implements ThumbnailCreator {
private static Logger log = LoggerFactory.getLogger(ThumbnailCreatorImp.class);
private static Logger log = LoggerFactory
.getLogger(ThumbnailCreatorImp.class);
private static final Pattern PAGE_NUMBER_PATTERN = Pattern.compile("(.+-thumb)-([0-9]+)(.png)");
private static final Pattern PAGE_NUMBER_PATTERN = Pattern
.compile("(.+-thumb)-([0-9]+)(.png)");
private String IMAGEMAGICK_DIR;
private String BLANK_THUMBNAIL;
private String IMAGEMAGICK_DIR;
private String BLANK_THUMBNAIL;
private static String TEMP_THUMB_NAME = "temp-thumb";
private static String TEMP_THUMB_NAME = "temp-thumb";
public boolean createThumbnails(UploadedPresentation pres){
boolean success = false;
File thumbsDir = determineThumbnailDirectory(pres.getUploadedFile());
public boolean createThumbnails(UploadedPresentation pres) {
boolean success = false;
File thumbsDir = determineThumbnailDirectory(pres.getUploadedFile());
if (! thumbsDir.exists())
thumbsDir.mkdir();
if (!thumbsDir.exists())
thumbsDir.mkdir();
cleanDirectory(thumbsDir);
cleanDirectory(thumbsDir);
try {
success = generateThumbnails(thumbsDir, pres);
} catch (InterruptedException e) {
log.warn("Interrupted Exception while generating thumbnails.");
success = false;
}
try {
success = generateThumbnails(thumbsDir, pres);
} catch (InterruptedException e) {
log.warn("Interrupted Exception while generating thumbnails.");
success = false;
}
// Create blank thumbnails for pages that failed to generate a thumbnail.
createBlankThumbnails(thumbsDir, pres.getNumberOfPages());
// Create blank thumbnails for pages that failed to generate a thumbnail.
createBlankThumbnails(thumbsDir, pres.getNumberOfPages());
renameThumbnails(thumbsDir);
renameThumbnails(thumbsDir);
return true;
}
return success;
}
private boolean generateThumbnails(File thumbsDir, UploadedPresentation pres) throws InterruptedException {
String source = pres.getUploadedFile().getAbsolutePath();
String dest;
String COMMAND = "";
if(SupportedFileTypes.isImageFile(pres.getFileType())){
dest = thumbsDir.getAbsolutePath() + File.separator + TEMP_THUMB_NAME + ".png";
COMMAND = IMAGEMAGICK_DIR + "/convert -thumbnail 150x150 " + source + " " + dest;
}else{
dest = thumbsDir.getAbsolutePath() + File.separator + "thumb-";
COMMAND = IMAGEMAGICK_DIR + "/gs -q -sDEVICE=pngalpha -dBATCH -dNOPAUSE -dNOPROMPT -dDOINTERPOLATE -dPDFFitPage -r16 -sOutputFile=" + dest +"%d.png " + source;
}
private boolean generateThumbnails(File thumbsDir, UploadedPresentation pres)
throws InterruptedException {
String source = pres.getUploadedFile().getAbsolutePath();
String dest;
String COMMAND = "";
dest = thumbsDir.getAbsolutePath() + File.separator + TEMP_THUMB_NAME;
if (SupportedFileTypes.isImageFile(pres.getFileType())) {
COMMAND = IMAGEMAGICK_DIR + File.separator + "convert -thumbnail 150x150 "
+ source + " " + dest + ".png";
} else {
COMMAND = "pdftocairo -png -scale-to 150 " + source + " " + dest;
}
boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000);
boolean done = new ExternalProcessExecutor().exec(COMMAND, 60000);
if (done) {
return true;
} else {
log.warn("Failed to create thumbnails: " + COMMAND);
}
if (done) {
return true;
} else {
log.warn("Failed to create thumbnails: " + COMMAND);
}
return false;
}
return false;
}
private File determineThumbnailDirectory(File presentationFile) {
return new File(presentationFile.getParent() + File.separatorChar + "thumbnails");
}
private File determineThumbnailDirectory(File presentationFile) {
return new File(
presentationFile.getParent() + File.separatorChar + "thumbnails");
}
private void renameThumbnails(File dir) {
/*
* If more than 1 file, filename like 'temp-thumb-X.png' else filename is 'temp-thumb.png'
*/
if (dir.list().length > 1) {
File[] files = dir.listFiles();
Matcher matcher;
for (int i = 0; i < files.length; i++) {
matcher = PAGE_NUMBER_PATTERN.matcher(files[i].getAbsolutePath());
if (matcher.matches()) {
// Path should be something like 'c:/temp/bigluebutton/presname/thumbnails/temp-thumb-1.png'
// Extract the page number. There should be 4 matches.
// 0. c:/temp/bigluebutton/presname/thumbnails/temp-thumb-1.png
// 1. c:/temp/bigluebutton/presname/thumbnails/temp-thumb
// 2. 1 ---> what we are interested in
// 3. .png
// We are interested in the second match.
int pageNum = Integer.valueOf(matcher.group(2).trim()).intValue();
String newFilename = "thumb-" + (pageNum + 1) + ".png";
File renamedFile = new File(dir.getAbsolutePath() + File.separator + newFilename);
files[i].renameTo(renamedFile);
}
}
} else if (dir.list().length == 1) {
File oldFilename = new File(dir.getAbsolutePath() + File.separator + dir.list()[0]);
String newFilename = "thumb-1.png";
File renamedFile = new File(oldFilename.getParent() + File.separator + newFilename);
oldFilename.renameTo(renamedFile);
}
}
private void renameThumbnails(File dir) {
/*
* If more than 1 file, filename like 'temp-thumb-X.png' else filename is
* 'temp-thumb.png'
*/
if (dir.list().length > 1) {
File[] files = dir.listFiles();
Matcher matcher;
for (int i = 0; i < files.length; i++) {
matcher = PAGE_NUMBER_PATTERN.matcher(files[i].getAbsolutePath());
if (matcher.matches()) {
// Path should be something like
// 'c:/temp/bigluebutton/presname/thumbnails/temp-thumb-1.png'
// Extract the page number. There should be 4 matches.
// 0. c:/temp/bigluebutton/presname/thumbnails/temp-thumb-1.png
// 1. c:/temp/bigluebutton/presname/thumbnails/temp-thumb
// 2. 1 ---> what we are interested in
// 3. .png
// We are interested in the second match.
int pageNum = Integer.valueOf(matcher.group(2).trim()).intValue();
String newFilename = "thumb-" + (pageNum) + ".png";
File renamedFile = new File(
dir.getAbsolutePath() + File.separator + newFilename);
files[i].renameTo(renamedFile);
}
}
} else if (dir.list().length == 1) {
File oldFilename = new File(
dir.getAbsolutePath() + File.separator + dir.list()[0]);
String newFilename = "thumb-1.png";
File renamedFile = new File(
oldFilename.getParent() + File.separator + newFilename);
oldFilename.renameTo(renamedFile);
}
}
private void createBlankThumbnails(File thumbsDir, int pageCount) {
File[] thumbs = thumbsDir.listFiles();
private void createBlankThumbnails(File thumbsDir, int pageCount) {
File[] thumbs = thumbsDir.listFiles();
if (thumbs.length != pageCount) {
for (int i = 0; i < pageCount; i++) {
File thumb = new File(thumbsDir.getAbsolutePath() + File.separator + TEMP_THUMB_NAME + "-" + i + ".png");
if (! thumb.exists()) {
log.info("Copying blank thumbnail for slide " + i);
copyBlankThumbnail(thumb);
}
}
}
}
if (thumbs.length != pageCount) {
for (int i = 0; i < pageCount; i++) {
File thumb = new File(thumbsDir.getAbsolutePath() + File.separator
+ TEMP_THUMB_NAME + "-" + i + ".png");
if (!thumb.exists()) {
log.info("Copying blank thumbnail for slide " + i);
copyBlankThumbnail(thumb);
}
}
}
}
private void copyBlankThumbnail(File thumb) {
try {
FileUtils.copyFile(new File(BLANK_THUMBNAIL), thumb);
} catch (IOException e) {
log.error("IOException while copying blank thumbnail.");
}
}
private void copyBlankThumbnail(File thumb) {
try {
FileUtils.copyFile(new File(BLANK_THUMBNAIL), thumb);
} catch (IOException e) {
log.error("IOException while copying blank thumbnail.");
}
}
private void cleanDirectory(File directory) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
files[i].delete();
}
}
private void cleanDirectory(File directory) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
files[i].delete();
}
}
public void setImageMagickDir(String imageMagickDir) {
IMAGEMAGICK_DIR = imageMagickDir;
}
public void setImageMagickDir(String imageMagickDir) {
IMAGEMAGICK_DIR = imageMagickDir;
}
public void setBlankThumbnail(String blankThumbnail) {
BLANK_THUMBNAIL = blankThumbnail;
}
public void setBlankThumbnail(String blankThumbnail) {
BLANK_THUMBNAIL = blankThumbnail;
}
}