/**
* 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 java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.FilenameUtils;
import org.bigbluebutton.presentation.PageConverter;
import org.bigbluebutton.presentation.UploadedPresentation;
import org.bigbluebutton.presentation.handlers.Pdf2PngPageConverterHandler;
import org.bigbluebutton.presentation.handlers.Pdf2SwfPageConverterHandler;
import org.bigbluebutton.presentation.handlers.Png2SwfPageConverterHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.zaxxer.nuprocess.NuProcess;
import com.zaxxer.nuprocess.NuProcessBuilder;
public class Pdf2SwfPageConverter implements PageConverter {
private static Logger log = LoggerFactory
.getLogger(Pdf2SwfPageConverter.class);
private String SWFTOOLS_DIR;
private String fontsDir;
private long placementsThreshold;
private long defineTextThreshold;
private long imageTagThreshold;
private String convTimeout = "7s";
private int WAIT_FOR_SEC = 7;
public boolean convert(File presentation, File output, int page,
UploadedPresentation pres) {
long convertStart = System.currentTimeMillis();
String source = presentation.getAbsolutePath();
String dest = output.getAbsolutePath();
String AVM2SWF = "-T9";
// Building the command line wrapped in shell to be able to use shell
// feature like the pipe
NuProcessBuilder pb = new NuProcessBuilder(Arrays.asList("timeout",
convTimeout, "/bin/sh", "-c",
SWFTOOLS_DIR + File.separator + "pdf2swf" + " -vv " + AVM2SWF + " -F "
+ fontsDir + " -p " + String.valueOf(page) + " " + source + " -o "
+ dest
+ " | egrep 'shape id|Updating font|Drawing' | sed 's/ / /g' | cut -d' ' -f 1-3 | sort | uniq -cw 15"));
Pdf2SwfPageConverterHandler pHandler = new Pdf2SwfPageConverterHandler();
pb.setProcessListener(pHandler);
long pdf2SwfStart = System.currentTimeMillis();
NuProcess process = pb.start();
try {
process.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error(e.getMessage());
}
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.isCommandSuccessful() && destFile.exists()
&& pHandler.numberOfPlacements() < placementsThreshold
&& pHandler.numberOfTextTags() < defineTextThreshold
&& pHandler.numberOfImageTags() < imageTagThreshold) {
return true;
} else {
// We need t delete the destination file as we are starting a new
// conversion process
if (destFile.exists()) {
destFile.delete();
}
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("convertSuccess", pHandler.isCommandSuccessful());
logData.put("fileExists", destFile.exists());
logData.put("numObjectTags", pHandler.numberOfPlacements());
logData.put("numTextTags", pHandler.numberOfTextTags());
logData.put("numImageTags", pHandler.numberOfImageTags());
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.warn("Potential problem with generated SWF: data={}", logStr);
File tempPng = null;
String basePresentationame = FilenameUtils
.getBaseName(presentation.getName());
try {
tempPng = File.createTempFile(basePresentationame + "-" + page, ".png");
} catch (IOException ioException) {
// We should never fall into this if the server is correctly configured
log.error("Unable to create temporary files");
}
long pdfStart = System.currentTimeMillis();
// Step 1: Convert a PDF page to PNG using a raw pdftocairo
NuProcessBuilder pbPng = new NuProcessBuilder(
Arrays.asList("timeout", convTimeout, "pdftocairo", "-png",
"-singlefile", "-r", timedOut || twiceTotalObjects ? "72" : "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);
NuProcess processPng = pbPng.start();
try {
processPng.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error(e.getMessage());
}
long pdfEnd = System.currentTimeMillis();
log.debug("pdftocairo conversion duration: {} sec",
(pdfEnd - pdfStart) / 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,
SWFTOOLS_DIR + File.separator + "png2swf", "-o", dest, source));
Png2SwfPageConverterHandler pSwfHandler = new Png2SwfPageConverterHandler();
pbSwf.setProcessListener(pSwfHandler);
NuProcess processSwf = pbSwf.start();
try {
processSwf.waitFor(WAIT_FOR_SEC, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.error(e.getMessage());
}
long png2swfEnd = System.currentTimeMillis();
log.debug("SwfTools conversion duration: {} sec",
(png2swfEnd - png2swfStart) / 1000);
// Delete the temporary PNG and PDF files after finishing the image
// conversion
tempPng.delete();
boolean doneSwf = pSwfHandler.isCommandSuccessful();
long convertEnd = System.currentTimeMillis();
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("conversionTime(sec)", (convertEnd - convertStart) / 1000);
logStr = gson.toJson(logData);
log.debug("Problem page conversion overall duration: {} sec",
(convertEnd - convertStart) / 1000);
if (doneSwf && destFile.exists()) {
return true;
} else {
log.warn("Failed to convert: " + destFile + " does not exist.");
return false;
}
}
}
public void setSwfToolsDir(String dir) {
SWFTOOLS_DIR = dir;
}
public void setFontsDir(String dir) {
fontsDir = dir;
}
public void setPlacementsThreshold(long threshold) {
placementsThreshold = threshold;
}
public void setDefineTextThreshold(long threshold) {
defineTextThreshold = threshold;
}
public void setImageTagThreshold(long threshold) {
imageTagThreshold = threshold;
}
}