org.eclipse.birt.report.engine.layout.pdf.emitter.ImageLayout.java Source code

Java tutorial

Introduction

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

Source

/***********************************************************************
 * Copyright (c) 2008 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.layout.pdf.emitter;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.report.engine.content.Dimension;
import org.eclipse.birt.report.engine.content.IContainerContent;
import org.eclipse.birt.report.engine.content.IContent;
import org.eclipse.birt.report.engine.content.IHyperlinkAction;
import org.eclipse.birt.report.engine.content.IImageContent;
import org.eclipse.birt.report.engine.content.IReportContent;
import org.eclipse.birt.report.engine.content.ITextContent;
import org.eclipse.birt.report.engine.content.impl.ActionContent;
import org.eclipse.birt.report.engine.content.impl.ObjectContent;
import org.eclipse.birt.report.engine.content.impl.ReportContent;
import org.eclipse.birt.report.engine.emitter.EmitterUtil;
import org.eclipse.birt.report.engine.ir.ImageItemDesign;
import org.eclipse.birt.report.engine.layout.area.IImageArea;
import org.eclipse.birt.report.engine.layout.area.impl.AreaFactory;
import org.eclipse.birt.report.engine.layout.area.impl.BlockContainerArea;
import org.eclipse.birt.report.engine.layout.area.impl.ContainerArea;
import org.eclipse.birt.report.engine.layout.area.impl.ImageArea;
import org.eclipse.birt.report.engine.layout.pdf.util.PropertyUtil;
import org.eclipse.birt.report.engine.util.FlashFile;

import com.lowagie.text.BadElementException;
import com.lowagie.text.Image;

public class ImageLayout extends Layout {

    public static final int TYPE_IMAGE_OBJECT = 0;
    public static final int TYPE_FLASH_OBJECT = 1;

    private Layout layout = null;
    private ContainerLayout parentLayout = null;

    private int objectType = TYPE_IMAGE_OBJECT;
    private boolean withFlashVars = false;

    private static HashMap<Integer, ArrayList<String>> unsupportedFormats = new HashMap<Integer, ArrayList<String>>();

    static {
        ArrayList<String> flashUnsupportedFormatList = new ArrayList<String>();
        flashUnsupportedFormatList.add("postscript");
        flashUnsupportedFormatList.add("ppt");
        unsupportedFormats.put(TYPE_FLASH_OBJECT, flashUnsupportedFormatList);
    };

    public ImageLayout(LayoutEngineContext context, ContainerLayout parentContext, IContent content) {
        super(context, parentContext, content);
        parentLayout = parentContext;
    }

    public void layout() throws BirtException {
        if (layout != null) {
            layout.layout();
        }
    }

    protected void closeLayout() throws BirtException {
        if (layout != null) {
            layout.closeLayout();
        }
    }

    protected void initialize() throws BirtException {
        checkObjectType();
        // choose the layout manager
        IImageContent imageContent = (IImageContent) content;
        Image imageObject = null;
        boolean isFlash = FlashFile.isFlash(imageContent.getMIMEType(), imageContent.getURI(),
                imageContent.getExtension());
        if (!isFlash) {
            imageObject = EmitterUtil.getImage(imageContent);
        }
        if (isOutputSupported(objectType) && (isFlash || imageObject != null)) {
            // the output format can display this kind of object and the object
            // is accessible.
            layout = new ConcreteImageLayout(context, parentLayout, content, imageObject);
        } else {
            // display the alt text.
            IReportContent report = context.getReport();
            if (report == null) {
                return;
            }
            ITextContent altTextContent = report.createTextContent(imageContent);
            altTextContent.setText(imageContent.getAltText());
            layout = new BlockTextLayout(context, parentLayout, altTextContent);
            layout.initialize();
        }
    }

    protected void checkObjectType() {
        IImageContent image = (IImageContent) content;
        String uri = image.getURI();
        String mimeType = image.getMIMEType();
        String extension = image.getExtension();
        if (FlashFile.isFlash(mimeType, uri, extension)) {
            objectType = TYPE_FLASH_OBJECT;
            ObjectContent flash = (ObjectContent) image;
            if (null != flash.getParamValueByName("flashvars")) {
                withFlashVars = true;
            }
        } else {
            objectType = TYPE_IMAGE_OBJECT;
        }
    }

    /**
     * Is the target output emitter support the object type. Currently the
     * object type can be image and flash.
     */
    private boolean isOutputSupported(int type) {
        if (withFlashVars) {
            return false;
        }
        ArrayList<String> formats = unsupportedFormats.get(type);
        if (formats != null && formats.contains(context.getFormat().toLowerCase())) {
            return false;
        } else {
            return true;
        }
    }
}

class ConcreteImageLayout extends Layout {

    /** The DpiX */
    private int resolutionX = 0;
    /** The DpiY */
    private int resolutionY = 0;

    private boolean fitToContainer = false;

    private Image imageObject = null;

    protected final static int DEFAULT_WIDHT = 212000;

    protected final static int DEFAULT_HEIGHT = 130000;

    protected IImageContent image;

    protected ContainerArea root;

    private Dimension intrinsic;

    private static final String BOOKMARK_PREFIX = "javascript:catchBookmark('";

    public ConcreteImageLayout(LayoutEngineContext context, ContainerLayout parentContext, IContent content,
            Image imageObject) {
        super(context, parentContext, content);
        this.imageObject = imageObject;
        Object reportItemDesign = content.getGenerateBy();
        if (null != reportItemDesign) {
            if (reportItemDesign instanceof ImageItemDesign) {
                fitToContainer = ((ImageItemDesign) reportItemDesign).isFitToContainer();
            }
        }
    }

    /**
     * get intrinsic dimension of image in pixels. Now only support png, bmp,
     * jpg, gif.
     * 
     * @return
     * @throws IOException
     * @throws MalformedURLException
     * @throws BadElementException
     */
    protected Dimension getIntrinsicDimension(IImageContent content, Image image)
            throws BadElementException, MalformedURLException, IOException {
        if (image != null) {
            // The DPI resolution of the image.
            // the preference of the DPI setting is:
            // 1. the resolution restored in content.
            // 2. the resolution in image file.
            // 3. use the DPI in render options.
            // 4. the DPI in report designHandle.
            // 5. the JRE screen resolution.
            // 6. the default DPI (96).
            int contentResolution = content.getResolution();
            if (contentResolution != 0) {
                resolutionX = contentResolution;
                resolutionY = contentResolution;
            } else {
                resolutionX = PropertyUtil.getImageDpi(content, image.getDpiX(), context.getDpi());
                resolutionY = PropertyUtil.getImageDpi(content, image.getDpiY(), context.getDpi());
            }
            return new Dimension((int) (image.getPlainWidth() * 1000 / resolutionX * 72),
                    (int) (image.getPlainHeight() * 1000 / resolutionY * 72));
        }
        return null;
    }

    protected Dimension getSpecifiedDimension(IImageContent content, int pWidth, boolean scale) {
        Dimension dim = new Dimension(DEFAULT_WIDHT, DEFAULT_HEIGHT);
        try {
            intrinsic = getIntrinsicDimension(content, imageObject);
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getLocalizedMessage());
        }
        int specifiedWidth = getDimensionValue(content.getWidth(), resolutionX, pWidth);
        int specifiedHeight = getDimensionValue(content.getHeight(), resolutionY, 0);
        if (intrinsic == null) {
            dim.setDimension(specifiedWidth == 0 ? DEFAULT_WIDHT : specifiedWidth,
                    specifiedHeight == 0 ? DEFAULT_HEIGHT : specifiedHeight);
            return dim;
        }

        if (scale) // always does scale.
        {
            double ratio = intrinsic.getRatio();

            if (specifiedWidth > 0) {
                if (specifiedHeight > 0) {
                    dim.setDimension(specifiedWidth, specifiedHeight);
                } else {
                    dim.setDimension(specifiedWidth, (int) (specifiedWidth / ratio));
                }
            } else {
                if (specifiedHeight > 0) {
                    dim.setDimension((int) (specifiedHeight * ratio), specifiedHeight);
                } else {
                    dim.setDimension(intrinsic.getWidth(), intrinsic.getHeight());
                }
            }
        } else {
            if (specifiedWidth > 0) {
                if (specifiedHeight > 0) {
                    dim.setDimension(specifiedWidth, specifiedHeight);
                } else {
                    dim.setDimension(specifiedWidth, intrinsic.getHeight());
                }
            } else {
                if (specifiedHeight > 0) {
                    dim.setDimension(intrinsic.getWidth(), specifiedHeight);
                } else {
                    dim.setDimension(intrinsic.getWidth(), intrinsic.getHeight());
                }
            }
        }
        return dim;
    }

    public void layout() throws BirtException {
        init();
        // For inline image, the hierarchy is
        // LineArea->InlineContainer->ImageArea.
        // the root is InlineContainer, so we just need to add the root directly
        // to its parent(LineArea).
        // In LineAreaLM, the lineArea will enlarge itself to hold the root.
        // For block image, the hierarchy is BlockContainer->ImageArea
        if (PropertyUtil.isInlineElement(image)) {
            // inline image
            assert (parent instanceof IInlineStackingLayout);
            IInlineStackingLayout lineParent = (IInlineStackingLayout) parent;
            if (root.getAllocatedWidth() > parent.getCurrentMaxContentWidth()) {
                if (!lineParent.isEmptyLine()) {
                    lineParent.endLine();
                    layout();
                } else {
                    scale();
                    parent.addToRoot(root, 0);
                    return;
                }
            } else {
                scale();
                parent.addToRoot(root, 0);
                return;
            }
        } else {
            if (parent.isPageEmpty()) {
                scale();
            }
            boolean succeed = parent.addArea(root, 0);
            if (succeed) {
                return;
            } else {
                if (!parent.isPageEmpty()) {
                    parent.autoPageBreak();
                }
                scale();
                parent.addToRoot(root, parent.contextList.size() - 1);
                if (parent.isInBlockStacking) {
                    if (parent.contextList.size() > 1) {
                        parent.closeExcludingLast();
                    }
                }
            }
        }
    }

    private void scale() {
        if (!fitToContainer) {
            return;
        }
        if (root.getAllocatedWidth() > parent.getCurrentMaxContentWidth()
                || root.getAllocatedHeight() > parent.getCurrentMaxContentHeight()) {
            int maxWidth = parent.getCurrentMaxContentWidth();
            int maxHeight = parent.getCurrentMaxContentHeight();
            double ratio = (double) maxWidth / (double) maxHeight;
            if (ratio < intrinsic.getRatio()) {
                // use parent width
                root.setContentWidth(maxWidth);
                root.setContentHeight((int) (maxWidth / intrinsic.getRatio()));
                if (root.getChildren().hasNext()) {
                    // this root should only has one child.
                    ImageArea image = (ImageArea) root.getChildren().next();
                    image.setAllocatedWidth(maxWidth);
                    image.setAllocatedHeight((int) (maxWidth / intrinsic.getRatio()));
                }
            } else {
                // use parent height
                root.setContentHeight(maxHeight);
                root.setContentWidth((int) (maxHeight * intrinsic.getRatio()));
                if (root.getChildren().hasNext()) {
                    // this root should only has one child.
                    ImageArea image = (ImageArea) root.getChildren().next();
                    image.setAllocatedHeight(maxHeight);
                    image.setAllocatedWidth((int) (maxHeight * intrinsic.getRatio()));
                }
            }
        }
    }

    protected void init() {
        assert (content instanceof IImageContent);
        image = (IImageContent) content;

        if (PropertyUtil.isInlineElement(image)) {
            root = (ContainerArea) AreaFactory.createInlineContainer(image, true, true);
        } else {
            root = (ContainerArea) AreaFactory.createBlockContainer(image);
        }

        // First, the width of root is set to its parent's max available width.
        root.setAllocatedWidth(parent.getCurrentMaxContentWidth());

        Dimension contentDimension = getSpecifiedDimension(image, root.getContentWidth(), true);
        ImageArea imageArea = (ImageArea) AreaFactory.createImageArea(image);
        imageArea.setWidth(contentDimension.getWidth());
        imageArea.setHeight(contentDimension.getHeight());

        root.addChild(imageArea);
        imageArea.setPosition(root.getContentX(), root.getContentY());

        // Adjust the dimension of root.
        root.setContentWidth(imageArea.getWidth());
        root.setContentHeight(imageArea.getHeight());
        processChartLegend(image, imageArea);
    }

    /**
     * Creates legend for chart.
     * 
     * @param imageContent
     *            the image content of the chart.
     * @param imageArea
     *            the imageArea of the chart.
     */
    private void processChartLegend(IImageContent imageContent, IImageArea imageArea) {
        if (null == intrinsic) {
            return;
        }
        Object imageMapObject = imageContent.getImageMap();
        boolean hasImageMap = (imageMapObject != null) && (imageMapObject instanceof String)
                && (((String) imageMapObject).length() > 0);
        if (hasImageMap) {
            createImageMap((String) imageMapObject, imageArea);
        }
    }

    private void createImageMap(String imageMapObject, IImageArea imageArea) {
        Pattern pattern = Pattern
                .compile("<AREA[^<>]*coords=\"([\\d,]*)\" href=\"([^<>\"]*)\" target=\"([^<>\"]*)\"/>");
        Matcher matcher = pattern.matcher(imageMapObject);
        while (matcher.find()) {
            try {
                int[] area = getArea(matcher.group(1));
                String url = matcher.group(2);
                String targetWindow = matcher.group(3);
                createImageMap(area, imageArea, url, targetWindow);
            } catch (NumberFormatException e) {
                logger.log(Level.WARNING, e.getMessage(), e);
            }
        }
    }

    private void createImageMap(int[] area, IImageArea imageArea, String url, String targetWindow) {
        if (url == null) {
            return;
        }
        ActionContent link = new ActionContent();
        if (isBookmark(url)) {
            String bookmark = getBookmark(url);
            link.setBookmark(bookmark);
        } else {
            link.setHyperlink(url, targetWindow);
        }
        area = getAbsoluteArea(area, imageArea);
        createImageMapContainer(area[0], area[1], area[2], area[3], link);
    }

    /**
     * Creates an image map container, which is an empty container with an hyper
     * link.
     * 
     * @param x
     *            x coordinate of lower left corner of the container.
     * @param y
     *            y coordinate of lower left corner of the container.
     * @param width
     *            width of the container.
     * @param height
     *            height of the container.
     * @param link
     *            destination of the hyperlink.
     */
    private void createImageMapContainer(int x, int y, int width, int height, IHyperlinkAction link) {
        ReportContent reportContent = (ReportContent) image.getReportContent();
        IContainerContent mapContent = reportContent.createContainerContent();
        mapContent.setHyperlinkAction(link);
        BlockContainerArea area = (BlockContainerArea) AreaFactory.createBlockContainer(mapContent);
        area.setPosition(x, y);
        area.setWidth(width);
        area.setHeight(height);
        root.addChild(area);
    }

    /**
     * Calculates the absolute positions of image map when given the position of
     * image. The image map position is relative to the left up corner of the
     * image.
     * 
     * The argument and returned value are both 4 length integer area, the four
     * value of which are x, y of up left corner, width and height respectively.
     * 
     * @param area
     *            rectangle area of a image map.
     * @param imageArea
     *            image area of the image in which the image map is.
     * @return absolute position of the image map.
     */
    private int[] getAbsoluteArea(int[] area, IImageArea imageArea) {
        assert (intrinsic != null);
        for (int i = 0; i < 4;) {
            area[i] = getTranslatedLengthX(area[i]);
            i++;
            area[i] = getTranslatedLengthY(area[i]);
            i++;
        }
        int[] result = new int[4];
        int imageX = imageArea.getX();
        int imageY = imageArea.getY();
        int imageHeight = imageArea.getHeight();
        int imageWidth = imageArea.getWidth();
        int intrinsicWidth = intrinsic.getWidth();
        int intrinsicHeight = intrinsic.getHeight();
        float ratio = (float) imageWidth / (float) intrinsicWidth;
        result[0] = imageX + (int) (area[0] * ratio);
        result[2] = (int) (area[2] * ratio);
        ratio = (float) imageHeight / (float) intrinsicHeight;
        result[1] = imageY + (int) (area[1] * ratio);
        result[3] = (int) (area[3] * ratio);
        return result;
    }

    private int getTranslatedLengthX(int length) {
        return length * 1000 / resolutionX * 72;
    }

    private int getTranslatedLengthY(int length) {
        return length * 1000 / resolutionY * 72;
    }

    /**
     * Check if a url is of an internal bookmark.
     * 
     * @param url
     *            the url string.
     * @return true if and only if the url is of an internal bookmark.
     */
    private boolean isBookmark(String url) {
        return url.startsWith(BOOKMARK_PREFIX) && url.endsWith("')");
    }

    /**
     * Parses out bookmark name from a url for interanl bookmark.
     * 
     * @param url
     *            the url string
     * @return the bookmark name.
     */
    private String getBookmark(String url) {
        int start = url.indexOf(BOOKMARK_PREFIX) + BOOKMARK_PREFIX.length();
        int end = url.length() - 2;
        return url.substring(start, end);
    }

    /**
     * Parse the image map position from a string which is of format "x1, y1,
     * x2, y2".
     * 
     * @param string
     *            the position string.
     * @return a array which contains the x, y coordinate of left up corner,
     *         width and height in sequence.
     * 
     */
    private int[] getArea(String string) {
        String[] rawDatas = string.split(",");
        int[] area = new int[4];
        area[0] = Integer.parseInt(rawDatas[0]);
        area[1] = Integer.parseInt(rawDatas[1]);
        area[2] = Integer.parseInt(rawDatas[4]) - area[0];
        area[3] = Integer.parseInt(rawDatas[5]) - area[1];
        return area;
    }

    protected void closeLayout() {
        if (!PropertyUtil.isInlineElement(image))
            // We align inline elements (here - inline container parenting the
            // inline image) in LineLayout, but not block-level image.
            // Invoke it here, since it should not be done by ContainerLayout
            // always.
            // TODO: Check if this can be done in a neater way.
            parent.align(root);
    }

    protected void initialize() {
        // TODO Auto-generated method stub

    }

}