org.eclipse.birt.report.engine.emitter.pdf.PDFPage.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.birt.report.engine.emitter.pdf.PDFPage.java

Source

/*******************************************************************************
 * Copyright (c) 2004,2007 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.report.engine.emitter.pdf;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.print.PrintTranscoder;
import org.eclipse.birt.report.engine.content.IHyperlinkAction;
import org.eclipse.birt.report.engine.content.IStyle;
import org.eclipse.birt.report.engine.emitter.EmitterUtil;
import org.eclipse.birt.report.engine.layout.emitter.AbstractPage;
import org.eclipse.birt.report.engine.layout.pdf.font.FontInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.BackgroundImageInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.BorderInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.TextStyle;
import org.eclipse.birt.report.engine.util.FlashFile;
import org.eclipse.birt.report.engine.util.SvgFile;
import org.w3c.dom.css.CSSValue;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfAction;
import com.lowagie.text.pdf.PdfAnnotation;
import com.lowagie.text.pdf.PdfBorderDictionary;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfDestination;
import com.lowagie.text.pdf.PdfFileSpecification;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfTextArray;
import com.lowagie.text.pdf.PdfWriter;

public class PDFPage extends AbstractPage {

    /**
     * The PDF Writer
     */
    protected PdfWriter writer = null;

    /**
     * ContentByte layer for PDF
     */
    protected PdfContentByte contentByte = null;

    protected static Logger logger = Logger.getLogger(PDFPage.class.getName());

    protected float containerHeight;

    protected PDFPageDevice pageDevice;

    /**
     * font size must greater than minimum font . if not,illegalArgumentException
     * will be thrown.
     */
    private static float MIN_FONT_SIZE = 1.0E-4f;

    private static Pattern PAGE_LINK_PATTERN = Pattern
            .compile("^((([a-zA-Z]:))(/(\\w[\\w ]*.*))+\\.(pdf|PDF))+#page=(\\d+)$");

    public PDFPage(int pageWidth, int pageHeight, Document document, PdfWriter writer, PDFPageDevice pageDevice) {
        super(pageWidth, pageHeight);
        this.writer = writer;
        this.pageDevice = pageDevice;
        this.containerHeight = this.pageHeight;
        Rectangle pageSize = new Rectangle(this.pageWidth, this.pageHeight);
        document.setPageSize(pageSize);
        if (!document.isOpen())
            document.open();
        else
            document.newPage();
        this.contentByte = writer.getDirectContent();
    }

    protected void clip(float startX, float startY, float width, float height) {
        startY = transformY(startY, height);
        contentByte.rectangle(startX, startY, width, height);
        contentByte.clip();
        contentByte.newPath();
    }

    protected void restoreState() {
        contentByte.restoreState();
    }

    protected void saveState() {
        contentByte.saveState();
    }

    public void dispose() {
    }

    protected void drawBackgroundColor(Color color, float x, float y, float width, float height) {
        if (null == color) {
            return;
        }
        y = transformY(y, height);
        contentByte.saveState();
        contentByte.setColorFill(color);
        contentByte.concatCTM(1, 0, 0, 1, x, y);
        contentByte.rectangle(0, 0, width, height);
        contentByte.fill();
        contentByte.restoreState();
    }

    protected void drawBackgroundImage(float x, float y, float width, float height, float imageWidth,
            float imageHeight, int repeat, String imageUrl, byte[] imageData, float offsetX, float offsetY)
            throws Exception {
        contentByte.saveState();
        clip(x, y, width, height);

        PdfTemplate image = null;
        if (imageUrl != null) {
            if (pageDevice.getImageCache().containsKey(imageUrl)) {
                image = pageDevice.getImageCache().get(imageUrl);
            }
        }
        if (image == null) {
            Image img = Image.getInstance(imageData);
            if (imageHeight == 0 || imageWidth == 0) {
                int resolutionX = img.getDpiX();
                int resolutionY = img.getDpiY();
                if (0 == resolutionX || 0 == resolutionY) {
                    resolutionX = 96;
                    resolutionY = 96;
                }
                imageWidth = img.getPlainWidth() / resolutionX * 72;
                imageHeight = img.getPlainHeight() / resolutionY * 72;
            }

            image = contentByte.createTemplate(imageWidth, imageHeight);
            image.addImage(img, imageWidth, 0, 0, imageHeight, 0, 0);

            if (imageUrl != null && image != null) {
                pageDevice.getImageCache().put(imageUrl, image);
            }
        }

        boolean xExtended = (repeat & BackgroundImageInfo.REPEAT_X) == BackgroundImageInfo.REPEAT_X;
        boolean yExtended = (repeat & BackgroundImageInfo.REPEAT_Y) == BackgroundImageInfo.REPEAT_Y;
        imageWidth = image.getWidth();
        imageHeight = image.getHeight();

        float originalX = offsetX;
        float originalY = offsetY;
        if (xExtended) {
            while (originalX > 0)
                originalX -= imageWidth;
        }
        if (yExtended) {
            while (originalY > 0)
                originalY -= imageHeight;
        }

        float startY = originalY;
        do {
            float startX = originalX;
            do {
                drawImage(image, x + startX, y + startY, imageWidth, imageHeight);
                startX += imageWidth;
            } while (startX < width && xExtended);
            startY += imageHeight;
        } while (startY < height && yExtended);
        contentByte.restoreState();
    }

    protected void drawImage(String imageId, byte[] imageData, String extension, float imageX, float imageY,
            float height, float width, String helpText, Map params) throws Exception {
        // Flash
        if (FlashFile.isFlash(null, null, extension)) {
            embedFlash(null, imageData, imageX, imageY, height, width, helpText, params);
            return;
        }

        // Cached Image
        PdfTemplate template = null;
        if (imageId != null) {
            if (pageDevice.getImageCache().containsKey(imageId)) {
                template = pageDevice.getImageCache().get(imageId);
            }
            if (template != null) {
                drawImage(template, imageX, imageY, height, width, helpText);
                return;
            }
        }

        // Not cached yet
        if (SvgFile.isSvg(null, null, extension)) {
            template = generateTemplateFromSVG(null, imageData, imageX, imageY, height, width, helpText);
        } else {
            // PNG/JPG/BMP... images:
            Image image = Image.getInstance(imageData);
            if (imageId == null) {
                // image without imageId, not able to cache.
                drawImage(image, imageX, imageY, height, width, helpText);
                return;
            }
            template = contentByte.createTemplate(width, height);
            template.addImage(image, width, 0, 0, height, 0, 0);
        }
        // Cache the image
        if (imageId != null && template != null) {
            pageDevice.getImageCache().put(imageId, template);
        }
        if (template != null) {
            drawImage(template, imageX, imageY, height, width, helpText);
        }
    }

    /**
     * @deprecated
     */
    protected void drawImage(String uri, String extension, float imageX, float imageY, float height, float width,
            String helpText, Map params) throws Exception {
        return;
    }

    /**
     * Draws a line with the line-style specified in advance from the start
     * position to the end position with the given line width, color, and style
     * at the given PDF layer. If the line-style is NOT set before invoking this
     * method, "solid" will be used as the default line-style.
     *
     * @param startX
     *            the start X coordinate of the line.
     * @param startY
     *            the start Y coordinate of the line.
     * @param endX
     *            the end X coordinate of the line.
     * @param endY
     *            the end Y coordinate of the line.
     * @param width
     *            the lineWidth
     * @param color
     *            the color of the line.
     * @param lineStyle
     *            the style of the line.
     */
    protected void drawLine(float startX, float startY, float endX, float endY, float width, Color color,
            int lineStyle) {
        // if the border does NOT have color or the line width of the border is
        // zero or the lineStyle is "none", just return.
        if (null == color || 0f == width || BorderInfo.BORDER_STYLE_NONE == lineStyle) //$NON-NLS-1$
        {
            return;
        }
        contentByte.saveState();
        if (BorderInfo.BORDER_STYLE_SOLID == lineStyle) //$NON-NLS-1$
        {
            drawRawLine(startX, startY, endX, endY, width, color, contentByte);
        } else if (BorderInfo.BORDER_STYLE_DASHED == lineStyle) //$NON-NLS-1$
        {
            contentByte.setLineDash(3 * width, 2 * width, 0f);
            drawRawLine(startX, startY, endX, endY, width, color, contentByte);
        } else if (BorderInfo.BORDER_STYLE_DOTTED == lineStyle) //$NON-NLS-1$
        {
            contentByte.setLineDash(width, width, 0f);
            drawRawLine(startX, startY, endX, endY, width, color, contentByte);
        } else if (BorderInfo.BORDER_STYLE_DOUBLE == lineStyle) //$NON-NLS-1$
        {
            return;
        }
        // the other line styles, e.g. 'ridge', 'outset', 'groove', 'inset' is
        // NOT supported now.
        // We look it as the default line style -- 'solid'
        else {
            drawRawLine(startX, startY, endX, endY, width, color, contentByte);
        }
        contentByte.restoreState();
    }

    protected void drawText(String text, float textX, float textY, float baseline, float width, float height,
            TextStyle textStyle) {
        drawText(text, textX, textY + baseline, width, height, textStyle.getFontInfo(),
                convertToPoint(textStyle.getLetterSpacing()), convertToPoint(textStyle.getWordSpacing()),
                textStyle.getColor(), textStyle.isLinethrough(), textStyle.isOverline(), textStyle.isUnderline(),
                textStyle.getAlign());
        if (textStyle.isHasHyperlink()) {
            FontInfo fontInfo = textStyle.getFontInfo();
            float lineWidth = fontInfo.getLineWidth();
            Color color = textStyle.getColor();
            drawDecorationLine(textX, textY, width, lineWidth, convertToPoint(fontInfo.getUnderlinePosition()),
                    color);
        }
    }

    private void drawText(String text, float textX, float textY, float width, float height, FontInfo fontInfo,
            float characterSpacing, float wordSpacing, Color color, boolean linethrough, boolean overline,
            boolean underline, CSSValue align) {
        drawText(text, textX, textY, fontInfo, characterSpacing, wordSpacing, color, align);
    }

    public void drawTotalPage(String text, int textX, int textY, int width, int height, TextStyle textInfo,
            float scale) {
        PdfTemplate template = pageDevice.getPDFTemplate(scale);
        if (template != null) {
            PdfContentByte tempCB = this.contentByte;
            this.containerHeight = template.getHeight();
            this.contentByte = template;
            drawText(text, textX, textY, width, height, textInfo);
            this.contentByte = tempCB;
            this.containerHeight = pageHeight;
        }
    }

    public void createBookmark(String bookmark, int x, int y, int width, int height) {
        createBookmark(bookmark, convertToPoint(x), convertToPoint(y), convertToPoint(width),
                convertToPoint(height));
    }

    private void createBookmark(String bookmark, float x, float y, float width, float height) {
        contentByte.localDestination(bookmark, new PdfDestination(PdfDestination.XYZ, -1, transformY(y), 0));
    }

    public void createHyperlink(String hyperlink, String bookmark, String targetWindow, int type, int x, int y,
            int width, int height) {
        createHyperlink(hyperlink, bookmark, targetWindow, type, convertToPoint(x), convertToPoint(y),
                convertToPoint(width), convertToPoint(height));
    }

    private void createHyperlink(String hyperlink, String bookmark, String targetWindow, int type, float x, float y,
            float width, float height) {
        y = transformY(y, height);
        writer.addAnnotation(new PdfAnnotation(writer, x, y, x + width, y + height,
                createPdfAction(hyperlink, bookmark, targetWindow, type)));
    }

    public void createTotalPageTemplate(int x, int y, int width, int height, float scale) {
        createTotalPageTemplate(convertToPoint(x), convertToPoint(y), convertToPoint(width), convertToPoint(height),
                scale);
    }

    private void createTotalPageTemplate(float x, float y, float width, float height, float scale) {
        PdfTemplate template = null;
        if (pageDevice.hasTemplate(scale)) {
            template = pageDevice.getPDFTemplate(scale);
        } else {
            template = contentByte.createTemplate(width, height);
            pageDevice.setPDFTemplate(scale, template);
        }
        y = transformY(y, height);
        contentByte.saveState();
        contentByte.addTemplate(template, x, y);
        contentByte.restoreState();
    }

    /**
     * Draws a line with the line-style specified in advance from the start
     * position to the end position with the given linewidth, color, and style
     * at the given pdf layer. If the line-style is NOT set before invoking this
     * method, "solid" will be used as the default line-style.
     *
     * @param startX
     *            the start X coordinate of the line
     * @param startY
     *            the start Y coordinate of the line
     * @param endX
     *            the end X coordinate of the line
     * @param endY
     *            the end Y coordinate of the line
     * @param width
     *            the lineWidth
     * @param color
     *            the color of the line
     * @param contentByte
     *            the given pdf layer
     */
    private void drawRawLine(float startX, float startY, float endX, float endY, float width, Color color,
            PdfContentByte contentByte) {
        startY = transformY(startY);
        endY = transformY(endY);
        contentByte.concatCTM(1, 0, 0, 1, startX, startY);

        contentByte.moveTo(0, 0);
        contentByte.lineTo(endX - startX, endY - startY);

        contentByte.setLineWidth(width);
        contentByte.setColorStroke(color);
        contentByte.stroke();
    }

    private void drawText(String text, float textX, float textY, FontInfo fontInfo, float characterSpacing,
            float wordSpacing, Color color, CSSValue align) {
        contentByte.saveState();
        // start drawing the text content
        contentByte.beginText();
        if (null != color && !Color.BLACK.equals(color)) {
            contentByte.setColorFill(color);
            contentByte.setColorStroke(color);
        }
        BaseFont font = getBaseFont(fontInfo);
        float fontSize = fontInfo.getFontSize();
        try {
            contentByte.setFontAndSize(font, fontSize);
        } catch (IllegalArgumentException e) {
            logger.log(Level.WARNING, e.getMessage());
            // close to zero , increase by one MIN_FONT_SIZE step
            contentByte.setFontAndSize(font, MIN_FONT_SIZE * 2);
        }
        if (characterSpacing != 0) {
            contentByte.setCharacterSpacing(characterSpacing);
        }
        if (wordSpacing != 0) {
            contentByte.setWordSpacing(wordSpacing);
        }
        setTextMatrix(contentByte, fontInfo, textX, transformY(textY, 0, containerHeight));
        if ((font.getFontType() == BaseFont.FONT_TYPE_TTUNI) && IStyle.JUSTIFY_VALUE.equals(align)
                && wordSpacing > 0) {
            int idx = text.indexOf(' ');
            if (idx >= 0) {
                float spaceCorrection = -wordSpacing * 1000 / fontSize;
                PdfTextArray textArray = new PdfTextArray(text.substring(0, idx));
                int lastIdx = idx;
                while ((idx = text.indexOf(' ', lastIdx + 1)) >= 0) {
                    textArray.add(spaceCorrection);
                    textArray.add(text.substring(lastIdx, idx));
                    lastIdx = idx;
                }
                textArray.add(spaceCorrection);
                textArray.add(text.substring(lastIdx));
                contentByte.showText(textArray);
            } else {
                contentByte.showText(text);
            }
        } else {
            contentByte.showText(text);
        }
        contentByte.endText();
        contentByte.restoreState();
    }

    protected BaseFont getBaseFont(FontInfo fontInfo) {
        return fontInfo.getBaseFont();
    }

    /**
     * Creates a PdfAction.
     *
     * @param hyperlink
     *            the hyperlink.
     * @param bookmark
     *            the bookmark.
     * @param target
     *            if target equals "_blank", the target will be opened in a new
     *            window, else the target will be opened in the current window.
     * @return the created PdfAction.
     */
    private PdfAction createPdfAction(String hyperlink, String bookmark, String target, int type) {
        // patch from Ales Novy
        if ("_top".equalsIgnoreCase(target) || "_parent".equalsIgnoreCase(target)
                || "_blank".equalsIgnoreCase(target) || "_self".equalsIgnoreCase(target))
        // Opens the target in a new window.
        {
            if (hyperlink == null)
                hyperlink = "";
            boolean isUrl = hyperlink.startsWith("http");
            if (!isUrl) {
                Matcher matcher = PAGE_LINK_PATTERN.matcher(hyperlink);
                if (matcher.find()) {
                    String fileName = matcher.group(1);
                    String pageNumber = matcher.group(matcher.groupCount());
                    return new PdfAction(fileName, Integer.valueOf(pageNumber));
                }
            }
            return new PdfAction(hyperlink);
        } else

        // Opens the target in the current window.
        {
            if (type == IHyperlinkAction.ACTION_BOOKMARK) {
                return PdfAction.gotoLocalPage(bookmark, false);
            } else {
                return PdfAction.gotoRemotePage(hyperlink, bookmark, false, false);
            }
        }
    }

    private void setTextMatrix(PdfContentByte cb, FontInfo fi, float x, float y) {
        cb.concatCTM(1, 0, 0, 1, x, y);
        if (!fi.getSimulation()) {
            cb.setTextMatrix(0, 0);
            return;
        }
        switch (fi.getFontStyle()) {
        case Font.ITALIC: {
            simulateItalic(cb);
            break;
        }
        case Font.BOLD: {
            simulateBold(cb, fi.getFontWeight());
            break;
        }
        case Font.BOLDITALIC: {
            simulateBold(cb, fi.getFontWeight());
            simulateItalic(cb);
            break;
        }
        }
    }

    static HashMap<Integer, Float> fontWeightLineWidthMap = new HashMap<Integer, Float>();
    static {
        fontWeightLineWidthMap.put(500, 0.1f);
        fontWeightLineWidthMap.put(600, 0.185f);
        fontWeightLineWidthMap.put(700, 0.225f);
        fontWeightLineWidthMap.put(800, 0.3f);
        fontWeightLineWidthMap.put(900, 0.5f);
    };

    private void simulateBold(PdfContentByte cb, int fontWeight) {
        cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
        if (fontWeightLineWidthMap.containsKey(fontWeight)) {
            cb.setLineWidth(fontWeightLineWidthMap.get(fontWeight));
        } else {
            cb.setLineWidth(0.225f);
        }
        cb.setTextMatrix(0, 0);
    }

    private void simulateItalic(PdfContentByte cb) {
        float beta = EmitterUtil.ITALIC_HORIZONTAL_COEFFICIENT;
        cb.setTextMatrix(1, 0, beta, 1, 0, 0);
    }

    public void showHelpText(String helpText, float x, float y, float width, float height) {
        showHelpText(x, transformY(y, height), width, height, helpText);
    }

    protected void showHelpText(float x, float y, float width, float height, String helpText) {
        Rectangle rectangle = new Rectangle(x, y, x + width, y + height);
        PdfAnnotation annotation = PdfAnnotation.createSquareCircle(writer, rectangle, helpText, true);
        PdfBorderDictionary borderStyle = new PdfBorderDictionary(0, PdfBorderDictionary.STYLE_SOLID, null);
        annotation.setBorderStyle(borderStyle);
        annotation.setFlags(288);
        writer.addAnnotation(annotation);
    }

    protected void drawImage(PdfTemplate image, float imageX, float imageY, float height, float width,
            String helpText) throws DocumentException {
        imageY = transformY(imageY, height);
        contentByte.saveState();
        contentByte.concatCTM(1, 0, 0, 1, imageX, imageY);
        float w = image.getWidth();
        float h = image.getHeight();
        contentByte.addTemplate(image, width / w, 0f / w, 0f / h, height / h, 0f, 0f);
        if (helpText != null) {
            showHelpText(imageX, imageY, width, height, helpText);
        }
        contentByte.restoreState();
    }

    private void drawImage(PdfTemplate image, float imageX, float imageY, float width, float height)
            throws DocumentException {
        drawImage(image, imageX, imageY, height, width, null);
    }

    protected void drawImage(Image image, float imageX, float imageY, float height, float width, String helpText)
            throws DocumentException {
        imageY = transformY(imageY, height);
        contentByte.saveState();
        contentByte.concatCTM(1, 0, 0, 1, imageX, imageY);
        contentByte.addImage(image, width, 0f, 0f, height, 0f, 0f);
        if (helpText != null) {
            showHelpText(imageX, imageY, width, height, helpText);
        }
        contentByte.restoreState();
    }

    protected void embedFlash(String flashPath, byte[] flashData, float x, float y, float height, float width,
            String helpText, Map params) throws IOException {
        y = transformY(y, height);
        contentByte.saveState();
        PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(writer, flashPath, helpText, flashData);
        PdfAnnotation annot = PdfAnnotation.createScreen(writer, new Rectangle(x, y, x + width, y + height),
                helpText, fs, "application/x-shockwave-flash", true);
        writer.addAnnotation(annot);
        if (helpText != null) {
            showHelpText(x, y, width, height, helpText);
        }
        contentByte.restoreState();
    }

    protected PdfTemplate generateTemplateFromSVG(String svgPath, byte[] svgData, float x, float y, float height,
            float width, String helpText) throws Exception {
        return transSVG(null, svgData, x, y, height, width, helpText);
    }

    protected PdfTemplate transSVG(String svgPath, byte[] svgData, float x, float y, float height, float width,
            String helpText) throws IOException, DocumentException {
        PdfTemplate template = contentByte.createTemplate(width, height);
        Graphics2D g2D = template.createGraphics(width, height);

        PrintTranscoder transcoder = new PrintTranscoder();
        if (null != svgData && svgData.length > 0) {
            transcoder.transcode(new TranscoderInput(new ByteArrayInputStream(svgData)), null);
        } else if (null != svgPath) {
            transcoder.transcode(new TranscoderInput(svgPath), null);
        }
        PageFormat pg = new PageFormat();
        Paper p = new Paper();
        p.setSize(width, height);
        p.setImageableArea(0, 0, width, height);
        pg.setPaper(p);
        transcoder.print(g2D, pg, 0);
        g2D.dispose();
        return template;
    }
}