ch.rasc.downloadchart.DownloadChartServlet.java Source code

Java tutorial

Introduction

Here is the source code for ch.rasc.downloadchart.DownloadChartServlet.java

Source

/**
 * Copyright 2014-2015 Ralph Schaer <ralphschaer@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ch.rasc.downloadchart;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDPixelMap;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DownloadChartServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private final static String encodingPrefix = "base64,";

    private final static ObjectMapper objectMapper = new ObjectMapper();

    private static final int DEFAULT_USER_SPACE_UNIT_DPI = 72;
    private static final float MM_TO_UNITS = 1 / (10 * 2.54f) * DEFAULT_USER_SPACE_UNIT_DPI;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String dataUrl = request.getParameter("data");
        int contentStartIndex = dataUrl.indexOf(encodingPrefix) + encodingPrefix.length();
        byte[] imageData = Base64.getDecoder().decode(dataUrl.substring(contentStartIndex));

        String format = request.getParameter("format");
        if (format == null) {
            format = "png";
        }

        String filename = request.getParameter("filename");
        if (filename == null) {
            filename = "chart";
        }

        String widthString = request.getParameter("width");
        Integer width = null;
        if (widthString != null) {
            width = Integer.valueOf(widthString);
        }

        String heightString = request.getParameter("height");
        Integer height = null;
        if (heightString != null) {
            height = Integer.valueOf(heightString);
        }

        // String scaleString = request.getParameter("scale");
        // Integer scale = null;
        // if (scaleString != null) {
        // scale = Integer.valueOf(scaleString);
        // }

        if (format.equals("png")) {
            handlePng(response, imageData, width, height, filename);
        } else if (format.equals("jpeg")) {
            JpegOptions options = readOptions(request, "jpeg", JpegOptions.class);
            handleJpg(response, imageData, width, height, filename, options);
        } else if (format.equals("gif")) {
            handleGif(response, imageData, width, height, filename);
        } else if (format.equals("pdf")) {
            PdfOptions options = readOptions(request, "pdf", PdfOptions.class);
            handlePdf(response, imageData, width, height, filename, options);
        }

        response.getOutputStream().flush();
    }

    private static <T> T readOptions(HttpServletRequest request, String requestParameter, Class<T> clazz)
            throws IOException, JsonParseException, JsonMappingException {

        String optionsString = request.getParameter(requestParameter);
        if (optionsString != null) {
            return objectMapper.readValue(optionsString, clazz);
        }
        return null;
    }

    private static void handlePng(HttpServletResponse response, byte[] imageData, Integer width, Integer height,
            String filename) throws IOException {

        response.setContentType("image/png");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + ".png\";");

        writeImage(response, imageData, width, height, "png");
    }

    private static void handleJpg(HttpServletResponse response, byte[] imageData, Integer width, Integer height,
            String filename, JpegOptions options) throws IOException {

        response.setContentType("image/jpeg");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + ".jpg\";");

        BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));

        Dimension newDimension = calculateDimension(image, width, height);
        int imgWidth = image.getWidth();
        int imgHeight = image.getHeight();
        if (newDimension != null) {
            imgWidth = newDimension.width;
            imgHeight = newDimension.height;
        }

        BufferedImage newImage = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);

        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, imgWidth, imgHeight, Color.BLACK, null);
        g.dispose();

        if (newDimension != null) {
            g.setComposite(AlphaComposite.Src);
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }

        try (ImageOutputStream ios = ImageIO.createImageOutputStream(response.getOutputStream())) {
            Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpg");
            ImageWriter writer = iter.next();
            ImageWriteParam iwp = writer.getDefaultWriteParam();
            iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            if (options != null && options.quality != null && options.quality != 0 && options.quality != 100) {
                iwp.setCompressionQuality(options.quality / 100f);
            } else {
                iwp.setCompressionQuality(1);
            }
            writer.setOutput(ios);
            writer.write(null, new IIOImage(newImage, null, null), iwp);
            writer.dispose();
        }
    }

    private static void handleGif(HttpServletResponse response, byte[] imageData, Integer width, Integer height,
            String filename) throws IOException {

        response.setContentType("image/gif");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + ".gif\";");

        writeImage(response, imageData, width, height, "gif");
    }

    private static void handlePdf(HttpServletResponse response, byte[] imageData, Integer width, Integer height,
            String filename, PdfOptions options) throws IOException {

        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + ".pdf\";");

        try (PDDocument document = new PDDocument()) {
            PDRectangle format = PDPage.PAGE_SIZE_A4;
            if (options != null) {
                if ("A3".equals(options.format)) {
                    format = PDPage.PAGE_SIZE_A3;
                } else if ("A5".equals(options.format)) {
                    format = PDPage.PAGE_SIZE_A5;
                } else if ("Letter".equals(options.format)) {
                    format = PDPage.PAGE_SIZE_LETTER;
                } else if ("Legal".equals(options.format)) {
                    format = new PDRectangle(215.9f * MM_TO_UNITS, 355.6f * MM_TO_UNITS);
                } else if ("Tabloid".equals(options.format)) {
                    format = new PDRectangle(279 * MM_TO_UNITS, 432 * MM_TO_UNITS);
                } else if (options.width != null && options.height != null) {
                    Float pdfWidth = convertToPixel(options.width);
                    Float pdfHeight = convertToPixel(options.height);
                    if (pdfWidth != null && pdfHeight != null) {
                        format = new PDRectangle(pdfWidth, pdfHeight);
                    }
                }
            }

            PDPage page = new PDPage(format);

            if (options != null && "landscape".equals(options.orientation)) {
                page.setRotation(90);
            }

            document.addPage(page);

            BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageData));

            try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {

                PDPixelMap ximage = new PDPixelMap(document, originalImage);

                Dimension newDimension = calculateDimension(originalImage, width, height);
                int imgWidth = ximage.getWidth();
                int imgHeight = ximage.getHeight();
                if (newDimension != null) {
                    imgWidth = newDimension.width;
                    imgHeight = newDimension.height;
                }

                Float border = options != null ? convertToPixel(options.border) : null;
                if (border == null) {
                    border = 0.0f;
                }

                AffineTransform transform;

                if (page.getRotation() == null) {
                    float scale = (page.getMediaBox().getWidth() - border * 2) / imgWidth;
                    if (scale < 1.0) {
                        transform = new AffineTransform(imgWidth, 0, 0, imgHeight, border,
                                page.getMediaBox().getHeight() - border - imgHeight * scale);

                        transform.scale(scale, scale);
                    } else {
                        transform = new AffineTransform(imgWidth, 0, 0, imgHeight, border,
                                page.getMediaBox().getHeight() - border - imgHeight);
                    }

                } else {
                    float scale = (page.getMediaBox().getHeight() - border * 2) / imgWidth;
                    if (scale < 1.0) {
                        transform = new AffineTransform(imgHeight, 0, 0, imgWidth, imgHeight * scale + border,
                                border);

                        transform.scale(scale, scale);
                    } else {
                        transform = new AffineTransform(imgHeight, 0, 0, imgWidth, imgHeight + border, border);
                    }

                    transform.rotate(1.0 * Math.PI / 2.0);
                }

                contentStream.drawXObject(ximage, transform);

            }

            try {
                document.save(response.getOutputStream());
            } catch (COSVisitorException e) {
                throw new IOException(e);
            }
        }
    }

    private static void writeImage(HttpServletResponse response, byte[] imageData, Integer width, Integer height,
            String formatName) throws IOException {

        BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(imageData));
        Dimension newDimension = calculateDimension(originalImage, width, height);

        if (newDimension != null) {
            int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
            BufferedImage resizedImage = new BufferedImage(newDimension.width, newDimension.height, type);
            Graphics2D g = resizedImage.createGraphics();
            g.drawImage(originalImage, 0, 0, newDimension.width, newDimension.height, null);
            g.dispose();
            g.setComposite(AlphaComposite.Src);

            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            ImageIO.write(resizedImage, formatName, response.getOutputStream());
        } else {
            if ("png".equals(formatName)) {
                response.getOutputStream().write(imageData);
            } else {
                ImageIO.write(originalImage, "gif", response.getOutputStream());
            }
        }
    }

    private static Dimension calculateDimension(BufferedImage image, Integer width, Integer height) {

        int imgWidth = image.getWidth();
        int imgHeight = image.getHeight();

        if (width == null && height != null) {
            return new Dimension(imgWidth * height / imgHeight, height);
        } else if (width != null && height == null) {
            return new Dimension(width, imgHeight * width / imgWidth);
        } else if (width != null && height != null) {
            return new Dimension(width, height);
        }

        return null;

    }

    private static Float convertToPixel(String str) {
        if (str != null) {
            if (str.endsWith("mm")) {
                return Float.parseFloat(str.substring(0, str.length() - 2)) * MM_TO_UNITS;
            } else if (str.endsWith("cm")) {
                return Float.parseFloat(str.substring(0, str.length() - 2)) * 10 * MM_TO_UNITS;
            } else if (str.endsWith("in")) {
                return Float.parseFloat(str.substring(0, str.length() - 2)) * DEFAULT_USER_SPACE_UNIT_DPI;
            } else if (str.endsWith("px")) {
                return Float.parseFloat(str.substring(0, str.length() - 2));
            }
        }

        return null;
    }
}