package com.idega.graphics.image.business;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import org.xhtmlrenderer.simple.Graphics2DRenderer;
import org.xhtmlrenderer.swing.Java2DRenderer;
import org.xhtmlrenderer.util.DownscaleQuality;
import org.xhtmlrenderer.util.FSImageWriter;
import org.xhtmlrenderer.util.ImageUtil;
import org.xhtmlrenderer.util.ScalingOptions;
import org.xhtmlrenderer.util.XRLog;
import com.idega.business.IBOLookup;
import com.idega.business.IBOLookupException;
import com.idega.graphics.util.GraphicsConstants;
import com.idega.idegaweb.IWApplicationContext;
import com.idega.idegaweb.IWMainApplication;
import com.idega.idegaweb.IWMainApplicationSettings;
import com.idega.io.MemoryFileBuffer;
import com.idega.io.MemoryInputStream;
import com.idega.io.MemoryOutputStream;
import com.idega.presentation.IWContext;
import com.idega.slide.business.IWSlideService;
import com.idega.util.CoreConstants;
import com.idega.util.IOUtil;
import com.idega.util.StringUtil;
public class ImageGeneratorImpl implements ImageGenerator {
private static final Logger LOGGER = Logger.getLogger(ImageGeneratorImpl.class.getName());
private static final String EXTERNAL_SERVICE = "http://webdesignbook.net/snapper.php?url=";
private static final String IMAGE_WIDTH_PARAM = "&w=";
private static final String IMAGE_HEIGHT_PARAM = "&h=";
private static final String MIME_TYPE = "image/";
private String fileExtension = null;
private boolean isExternalService = false;
private IWSlideService service = null;
private ImageEncoder encoder = null;
private Random generator = null;
public ImageGeneratorImpl() {
generator = new Random();
fileExtension = GraphicsConstants.JPG_FILE_NAME_EXTENSION;
}
public ImageGeneratorImpl(IWContext iwc) {
this();
initializeSlideService(iwc);
initializeImageEncoder(iwc);
}
/**
* Converts ARGB (png) to RGB (jpg, gif)
*/
public BufferedImage getConvertedImageFromPNGToJPG(BufferedImage originalImage) {
if (originalImage == null) {
return null;
}
int w = originalImage.getWidth();
int h = originalImage.getHeight();
BufferedImage convertedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = convertedImage.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, w, h);
g.drawRenderedImage(originalImage, null);
g.dispose();
return convertedImage;
}
/**
* Sets new quality to image
*/
public BufferedImage getImageWithNewQuality(BufferedImage originalImage, float quality, boolean isJpgImage) {
if (originalImage == null) {
return null;
}
String tempFile = "temp_quality.";
if (isJpgImage) {
tempFile = new StringBuffer(tempFile).append(GraphicsConstants.JPG_FILE_NAME_EXTENSION).toString();
}
else {
tempFile = new StringBuffer(tempFile).append(GraphicsConstants.PNG_FILE_NAME_EXTENSION).toString();
}
File file = new File(tempFile);
if (file.exists()) {
file.delete();
} else {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
OutputStream fos = null;
try {
fos = new BufferedOutputStream(new FileOutputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
FSImageWriter imageWriter = null;
if (isJpgImage) {
imageWriter = FSImageWriter.newJpegWriter(quality);
}
else {
imageWriter = new FSImageWriter();
imageWriter.setWriteCompressionQuality(quality);
}
try {
imageWriter.write(originalImage, fos);
return ImageIO.read(file);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
file.delete();
IOUtil.close(fos);
}
}
/**
* Encodes image (from InputStream) and uploads to Slide
*/
public boolean encodeAndUploadImage(String uploadDirectory, String fileName, String mimeType, InputStream stream, int width, int height) {
//TODO use new JAI methods
MemoryFileBuffer buff = new MemoryFileBuffer();
OutputStream output = new MemoryOutputStream(buff);
InputStream is = null;
boolean result = true;
try {
getImageEncoder().encode(mimeType, stream, output, width, height);
is = new MemoryInputStream(buff);
result = uploadImage(uploadDirectory, fileName, is);
} catch (RemoteException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
IOUtil.close(stream);
IOUtil.close(output);
}
return result;
}
/**
* Generates preview of provided image (url), sets new quality and scales it to multiple images
*/
public List<BufferedImage> generatePreviews(String url, List<Dimension> dimensions, boolean isJpg, float quality) {
if (StringUtil.isEmpty(url) || dimensions == null) {
return null;
}
BufferedImage image = getImage(url, GraphicsConstants.GENERATED_IMAGE_HEIGHT, GraphicsConstants.GENERATED_IMAGE_WIDTH, isJpg); // "View" to image
if (image == null) {
return null;
}
long start = System.currentTimeMillis();
// Setting new quality
if (quality < 1) {
image = getImageWithNewQuality(image, quality, isJpg);
if (image == null) {
return null;
}
}
// Scaling image, creating multiple images
ScalingOptions options = getScalingOptions(isJpg);
@SuppressWarnings("unchecked")
List<Object> images = ImageUtil.scaleMultiple(options, image, dimensions); // Scaling generated image to other sizes
if (images == null) {
return null;
}
List<BufferedImage> allImages = new ArrayList<BufferedImage>(images.size());
Object o = null;
for (int i = 0; i < images.size(); i++) {
o = images.get(i);
if (o instanceof BufferedImage) {
allImages.add((BufferedImage) o);
}
}
isExternalService = false;
long end = System.currentTimeMillis();
LOGGER.info(new StringBuffer("Got images in ").append((end - start)).append(" ms: ").append(url).toString());
return allImages;
}
/**
* Generates preview of provided web page
*/
public boolean generatePreview(String url, String fileName, String uploadDirectory, int width, int height, boolean encode, boolean makeJpg, float quality) {
if (StringUtil.isEmpty(url) || StringUtil.isEmpty(fileName) || StringUtil.isEmpty(uploadDirectory) || !isValidInt(width) ||
!isValidInt(height)) {
return false;
}
boolean result = true;
InputStream stream = getImageInputStream(url, width, height, makeJpg, quality);
String fullName = new StringBuffer(fileName).append(".").append(getFileExtension()).toString();
if (stream == null) {
LOGGER.warning("Error getting InputStream");
return false;
}
if (isExternalService) {
return uploadImage(uploadDirectory, fullName, stream);
}
if (encode) {
result = encodeAndUploadImage(uploadDirectory, fullName, new StringBuffer(MIME_TYPE).append(getFileExtension()).toString(),
stream, width, height);
}
else {
return uploadImage(uploadDirectory, fullName, stream);
}
return result;
}
/**
* Generates preview of provided web pages
*/
public boolean generatePreview(List<String> urls, List<String> names, String uploadDirectory, int width, int height,
boolean encode, boolean makeJpg, float quality) {
if (!areValidParameters(urls, names, uploadDirectory, width, height)) {
return false;
}
boolean result = true;
for (int i = 0; i < urls.size(); i++) {
result = generatePreview(urls.get(i), names.get(i), uploadDirectory, width, height, encode, makeJpg, quality);
}
return result;
}
/**
* Scales image to provided dimensions
*/
public Image getScaledImage(InputStream imageStream, int width, int height, boolean isJpg) {
if (imageStream == null) {
return null;
}
BufferedImage originalImage = null;
try {
originalImage = ImageIO.read(imageStream);
} catch (IOException e) {
e.printStackTrace();
return null;
}
IOUtil.close(imageStream);
return getScaledImage(originalImage, width, height, isJpg);
}
/**
* Scales image to provided dimensions
*/
public Image getScaledImage(BufferedImage originalImage, int width, int height, boolean isJpg) {
if (originalImage == null) {
return null;
}
ScalingOptions scalingOptions = getScalingOptions(isJpg);
scalingOptions.setTargetDimensions(new Dimension(width, height));
return ImageUtil.getScaledInstance(scalingOptions, originalImage);
}
/**
* Creates InputStream from BufferedImage
*/
public InputStream getImageInputStream(BufferedImage image, String extension) {
if (image == null || extension == null) {
return null;
}
// Building InputStream
String fileName = new StringBuffer("temp_").append(generator.nextInt(Integer.MAX_VALUE)).append(CoreConstants.DOT).append(extension).toString();
File imageFile = new File(fileName);
if (imageFile.exists()) {
imageFile.delete();
} else {
try {
imageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
try {
ImageIO.write(image, extension, imageFile);
} catch (IOException e) {
e.printStackTrace();
return null;
}
InputStream stream = null;
try {
stream = new BufferedInputStream(new FileInputStream(imageFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
imageFile.delete();
return stream;
}
/**
* Creates InputStream from BufferedImage
*/
public InputStream getImageInputStream(Image image, String extension, boolean isJpg) {
if (image == null || extension == null) {
return null;
}
int type = BufferedImage.TYPE_INT_ARGB;
if (isJpg) {
type = BufferedImage.TYPE_INT_RGB;
}
return getImageInputStream(ImageUtil.convertToBufferedImage(image, type), extension);
}
/**
* Generates image with Flying Saucer XHTMLRenderer
*/
public BufferedImage generateImage(String urlToFile, int width, int height) {
return generateImage(urlToFile, width, height, false);
}
/**
* Generates image with Flying Saucer XHTMLRenderer
*/
public BufferedImage generateImage(String urlToFile, int width, int height, boolean isJpg) {
if (urlToFile == null) {
return null;
}
long start = System.currentTimeMillis();
LOGGER.info(new StringBuffer("Trying with XHTMLRenderer: ").append(urlToFile).toString());
String errorMessage = "Unable to generate image with XHTMLRenderer: ";
IWMainApplicationSettings settings = IWMainApplication.getDefaultIWMainApplication().getSettings();
boolean useOldGenerator = settings.getBoolean(CoreConstants.APPLICATION_PROPERTY_TO_USE_OLD_THEME_PREVIEW_GENERATOR, Boolean.FALSE);
XRLog.setLoggingEnabled(true);
XRLog.setLevel(XRLog.EXCEPTION, Level.WARNING);
BufferedImage image = null;
if (useOldGenerator) {
try {
image = Graphics2DRenderer.renderToImage(urlToFile, width, height);
} catch (Exception e) {
LOGGER.log(Level.WARNING, errorMessage.concat(urlToFile), e);
return null;
}
} else {
Java2DRenderer renderer = new Java2DRenderer(urlToFile, width, height);
renderer.setBufferedImageType(isJpg ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
try {
image = renderer.getImage();
} catch (Exception e) {
LOGGER.log(Level.WARNING, errorMessage.concat(urlToFile), e);
return null;
}
}
setFileExtension(useOldGenerator ? GraphicsConstants.PNG_FILE_NAME_EXTENSION :
isJpg ? GraphicsConstants.JPG_FILE_NAME_EXTENSION : GraphicsConstants.PNG_FILE_NAME_EXTENSION);
long end = System.currentTimeMillis();
LOGGER.info(new StringBuffer("XHTMLRenderer: success in ").append((end - start)).append(" ms: ").append(urlToFile).toString());
return image;
}
/**
* Returns URL: a link to service to read generated image
*/
public URL generateImageURLWithExternalService(String urlToFile, int width, int height) {
LOGGER.info("Trying with external service: ".concat(urlToFile));
URL url = null;
try {
url = new URL(new StringBuffer(EXTERNAL_SERVICE).append(urlToFile).append(IMAGE_WIDTH_PARAM).append(width)
.append(IMAGE_HEIGHT_PARAM).append(height).toString());
} catch (MalformedURLException e) {
LOGGER.log(Level.WARNING, "Unable to generate image with external service: ".concat(urlToFile), e);
return null;
}
LOGGER.info("External service: success: ".concat(urlToFile));
return url;
}
public String getFileExtension() {
return fileExtension;
}
/**
* Gets InputStream of generated image
* @param urlToFile - where tu find a web file
* @param width - image width
* @param height - image height
* @return InputStream or null if error
*/
private InputStream getImageInputStream(String urlToFile, int width, int height, boolean makeJpg, float quality) {
long start = System.currentTimeMillis();
List<Dimension> dimensions = new ArrayList<Dimension>(1);
dimensions.add(new Dimension(width, height));
List<BufferedImage> images = generatePreviews(urlToFile, dimensions, makeJpg, quality);
if (images == null) {
return null;
}
if (images.size() == 0) {
return null;
}
BufferedImage image = images.get(0);
if (image == null) {
return null;
}
InputStream stream = null;
stream = getImageInputStream(image, getFileExtension());
if (stream == null) {
return null;
}
long end = System.currentTimeMillis();
LOGGER.info(new StringBuffer("Got image InputStream in ").append((end - start)).append(" ms: ").append(urlToFile).toString());
return stream;
}
private void setFileExtension(String fileExtension) {
this.fileExtension = fileExtension.toLowerCase();
}
private IWSlideService getSlideService() {
initializeSlideService(IWMainApplication.getDefaultIWApplicationContext());
return service;
}
private synchronized void initializeSlideService(IWApplicationContext iwac) {
if (service == null) {
try {
service = IBOLookup.getServiceInstance(iwac, IWSlideService.class);
} catch (IBOLookupException e) {
e.printStackTrace();
}
}
}
private synchronized void initializeImageEncoder(IWApplicationContext iwac) {
if (encoder == null) {
try {
encoder = IBOLookup.getServiceInstance(iwac, ImageEncoder.class);
} catch (IBOLookupException e) {
e.printStackTrace();
}
}
}
private ImageEncoder getImageEncoder() {
initializeImageEncoder(IWMainApplication.getDefaultIWApplicationContext());
return encoder;
}
private ScalingOptions getScalingOptions(boolean isJpg) {
DownscaleQuality quality = DownscaleQuality.LOW_QUALITY;
if (isJpg) {
quality = DownscaleQuality.HIGH_QUALITY;
}
return new ScalingOptions(quality, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
}
private BufferedImage getImage(String urlToFile, int width, int height, boolean isJpg) {
BufferedImage generatedImage = generateImage(urlToFile, width, height, isJpg);
if (generatedImage == null) {
// Failed to generate image, trying with external service
URL url = generateImageURLWithExternalService(urlToFile, width, height);
try {
generatedImage = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
return null;
}
setFileExtension(GraphicsConstants.JPG_FILE_NAME_EXTENSION);
isExternalService = true;
}
return generatedImage;
}
private boolean uploadImage(String uploadDirectory, String fullName, InputStream stream) {
boolean result = true;
try {
if (!getSlideService().uploadFileAndCreateFoldersFromStringAsRoot(uploadDirectory, fullName, stream,
new StringBuffer(MIME_TYPE).append(getFileExtension()).toString(), true)) {
LOGGER.warning("Error uploading file: ".concat(fullName));
result = false;
}
} catch(RemoteException e) {
e.printStackTrace();
return false;
} finally {
IOUtil.close(stream);
}
return result;
}
private boolean areValidParameters(List<String> urls, List<String> names, String directory, int width, int height) {
if (urls == null || names == null) {
return false;
}
if (urls.size() != names.size()) {
return false;
}
if (StringUtil.isEmpty(directory)) {
return false;
}
if (!isValidInt(width) || !isValidInt(height)) {
return false;
}
return true;
}
private boolean isValidInt(int number) {
if (number > 0 && number <= Integer.MAX_VALUE) {
return true;
}
return false;
}
}