org.eclipse.birt.report.engine.emitter.ppt.PPTWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.birt.report.engine.emitter.ppt.PPTWriter.java

Source

/*******************************************************************************
 * Copyright (c) 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.ppt;

import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.net.QuotedPrintableCodec;
import org.eclipse.birt.report.engine.emitter.EmitterUtil;
import org.eclipse.birt.report.engine.emitter.ppt.util.PPTUtil.HyperlinkDef;
import org.eclipse.birt.report.engine.layout.emitter.util.BackgroundImageLayout;
import org.eclipse.birt.report.engine.layout.emitter.util.Position;
import org.eclipse.birt.report.engine.layout.pdf.font.FontInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.BorderInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.TextStyle;

import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UCharacter.UnicodeBlock;
import com.lowagie.text.Font;
import com.lowagie.text.pdf.BaseFont;

public class PPTWriter {

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

    /**
     * Output stream where postscript to be output.
     */
    private PrintWriter writer = null;

    protected int currentPageNum = 0;
    private int shapeCount = 0;

    protected float pageWidth, pageHeight;

    private Map<String, ImageInfo> imageInfos = new HashMap<String, ImageInfo>();

    // Holds the files' name for each page
    private Map<Integer, List<String>> fileNamesLists = new TreeMap<Integer, List<String>>();

    private QuotedPrintableCodec quotedPrintableCodec;

    public PPTWriter(OutputStream output) {
        try {
            writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), false);
        } catch (UnsupportedEncodingException e) {
            assert (false);
        }
    }

    /**
     * Creates a PPT Document.
     * @param author 
     * @param title 
     * 
     */
    public void start(String title, String author, String description, String subject) {
        if (!imageInfos.isEmpty()) {
            imageInfos.clear();
        }
        if (!fileNamesLists.isEmpty()) {
            fileNamesLists.clear();
        }

        println("MIME-Version: 1.0"); //$NON-NLS-1$
        println("Content-Type: multipart/related; boundary=\"___Actuate_Content_Boundary___\""); //$NON-NLS-1$
        println(""); //$NON-NLS-1$
        println("--___Actuate_Content_Boundary___"); //$NON-NLS-1$
        println("Content-Location: slide-show"); //$NON-NLS-1$
        println("Content-Transfer-Encoding: quoted-printable"); //$NON-NLS-1$
        println("Content-Type: text/html; charset=\"utf-8\""); //$NON-NLS-1$
        println(""); //$NON-NLS-1$
        println("<html"); //$NON-NLS-1$
        println("xmlns=3D'http://www.w3.org/TR/REC-html40'"); //$NON-NLS-1$
        println("xmlns:o=3D'urn:schemas-microsoft-com:office:office'"); //$NON-NLS-1$
        println("xmlns:p=3D'urn:schemas-microsoft-com:office:powerpoint'"); //$NON-NLS-1$
        println("xmlns:v=3D'urn:schemas-microsoft-com:vml'"); //$NON-NLS-1$
        println(">"); //$NON-NLS-1$
        println("<head>"); //$NON-NLS-1$
        println("<meta http-equiv=3D'Content-Type' content=3D'text/html; charset=3Dutf-8'>"); //$NON-NLS-1$
        println("<meta name=3D'ProgId' content=3D'PowerPoint.Slide'>"); //$NON-NLS-1$
        println("<meta name=3D'Generator' content=3D'Actuate View Server'>"); //$NON-NLS-1$
        println("<title>" + title + "</title>"); //$NON-NLS-1$
        println("<xml><o:DocumentProperties>"); //$NON-NLS-1$
        println("<o:Author>" + author + "</o:Author>"); //$NON-NLS-1$
        println("<o:Description>" + description + "</o:Description>"); //$NON-NLS-1$
        println("<o:Subject>" + subject + "</o:Subject>"); //$NON-NLS-1$
        println("</o:DocumentProperties></xml><link rel=3DFile-List href=3D'file-list'>"); //$NON-NLS-1$
        println("<link rel=3DPresentation-XML href=3D'presentation'>"); //$NON-NLS-1$
        println("</head></body></html>"); //$NON-NLS-1$
    }

    private void print(String text) {
        writer.print(text);
    }

    private void println(String text) {
        writer.println(text);
    }

    private void print(byte[] data) {
        print(new String(data));
    }

    /**
     * Closes the document.
     * 
     */
    public void end() {
        int slidesizex = (int) (Math.ceil(pageWidth * 8));
        int slidesizey = (int) (Math.ceil(pageHeight * 8));
        println("--___Actuate_Content_Boundary___"); //$NON-NLS-1$
        println("Content-Location: presentation"); //$NON-NLS-1$
        println("Content-Transfer-Encoding: quoted-printable"); //$NON-NLS-1$
        println("Content-Type: text/xml; charset=\"utf-8\""); //$NON-NLS-1$
        println(""); //$NON-NLS-1$
        println("<xml"); //$NON-NLS-1$
        println(" xmlns:o=3D'urn:schemas-microsoft-com:office:office'"); //$NON-NLS-1$
        println(" xmlns:p=3D'urn:schemas-microsoft-com:office:powerpoint'"); //$NON-NLS-1$
        println(">"); //$NON-NLS-1$
        println(("<p:presentation sizeof=3D'custom' slidesizex=3D'" + slidesizex + "' slidesizey=3D'" + slidesizey //$NON-NLS-1$//$NON-NLS-2$
                + "'>")); //$NON-NLS-1$

        for (int i = 0; i < currentPageNum; i++) {
            println(("<p:slide id=3D'" + (i + 1) + "' href=3D's" + (i + 1) + "'/>")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }

        println("</p:presentation></xml>"); //$NON-NLS-1$
        println(""); //$NON-NLS-1$
        println("--___Actuate_Content_Boundary___"); //$NON-NLS-1$
        println("Content-Location: file-list"); //$NON-NLS-1$
        println("Content-Transfer-Encoding: quoted-printable"); //$NON-NLS-1$
        println("Content-Type: text/xml; charset=\"utf-8\""); //$NON-NLS-1$
        println("<xml"); //$NON-NLS-1$
        println(" xmlns:o=3D'urn:schemas-microsoft-com:office:office'"); //$NON-NLS-1$
        println(" xmlns:p=3D'urn:schemas-microsoft-com:office:powerpoint'"); //$NON-NLS-1$
        println(">"); //$NON-NLS-1$
        println("<o:MainFile href=3D'slide-show'/>"); //$NON-NLS-1$
        println("<o:File href=3D'presentation'/>"); //$NON-NLS-1$
        println("<o:File href=3D'file-list'/>"); //$NON-NLS-1$

        for (int i = 0; i < currentPageNum; i++) {
            println(("<o:File href=3D's" + (i + 1) + "'/>")); //$NON-NLS-1$ //$NON-NLS-2$
            if (fileNamesLists.containsKey(i + 1)) {
                List<String> fileNames = fileNamesLists.get(i + 1);
                for (String fileName : fileNames) {
                    println(("<o:File href=3D\"" + fileName + "\"/>"));
                }
            }
        }

        println("</xml>"); //$NON-NLS-1$
        println(""); //$NON-NLS-1$
        println("--___Actuate_Content_Boundary___--"); //$NON-NLS-1$
        writer.close();
        writer = null;
    }

    public void endPage() {
        try {
            // Write out the image bytes
            Set<Map.Entry<String, ImageInfo>> entries = imageInfos.entrySet();
            for (Map.Entry<String, ImageInfo> entry : entries) {
                ImageInfo info = entry.getValue();
                generateImageBytes(info.imageId, info.imageData);
                println("\n");
            }
            println("</p:slide></body></html>"); //$NON-NLS-1$3
        } catch (IOException ioe) {
            logger.log(Level.WARNING, ioe.getMessage(), ioe);
        }
    }

    private void exportImageHeader(String imagekey) throws IOException {
        println("");
        println("--___Actuate_Content_Boundary___");
        ImageInfo imageInfo = imageInfos.get(imagekey);
        println("Content-Location: " + imageInfo.imageName + "");
        println("Content-Transfer-Encoding: base64");
        println("Content-Type: image/" + imageInfo.extension + "\n");
    }

    private void generateImageBytes(String imageTitle, byte[] imageData) throws IOException {
        exportImageHeader(imageTitle);

        Base64 base = new Base64();
        print(base.encode(imageData));
    }

    /**
     * Creates a new page.
     * 
     * @param page
     *            the PageArea specified from layout
     */
    public void newPage(float pageWidth, float pageHeight, Color backgroundColor) {
        currentPageNum++;
        imageInfos.clear();
        if (pageWidth > this.pageWidth) {
            this.pageWidth = pageWidth;
        }
        if (pageHeight > this.pageHeight) {
            this.pageHeight = pageHeight;
        }

        println("--___Actuate_Content_Boundary___"); //$NON-NLS-1$
        println("Content-Location: s" + currentPageNum + ""); //$NON-NLS-1$ //$NON-NLS-2$
        println("Content-Transfer-Encoding: quoted-printable"); //$NON-NLS-1$
        println("Content-Type: text/html; charset=\"utf-8\""); //$NON-NLS-1$
        println(""); //$NON-NLS-1$
        println("<html"); //$NON-NLS-1$
        println(" xmlns=3D'http://www.w3.org/TR/REC-html40'"); //$NON-NLS-1$
        println(" xmlns:o=3D'urn:schemas-microsoft-com:office:office'"); //$NON-NLS-1$
        println(" xmlns:p=3D'urn:schemas-microsoft-com:office:powerpoint'"); //$NON-NLS-1$
        println(" xmlns:v=3D'urn:schemas-microsoft-com:vml'"); //$NON-NLS-1$
        println(">"); //$NON-NLS-1$
        println("<head/><body><p:slide>"); //$NON-NLS-1$
        println("<meta http-equiv=3D'Content-Type' content=3D'text/html; charset=3Dutf-8'>"); //$NON-NLS-1$
        drawBackgroundColor(backgroundColor, 0, 0, pageWidth, pageHeight);
    }

    private String getFontName(BaseFont baseFont) {
        String[][] familyFontNames = baseFont.getFamilyFontName();
        String[] family = familyFontNames[familyFontNames.length - 1];
        return family[family.length - 1];
    }

    /**
     * Draws a chunk of text on the PPT.
     * 
     * @param text
     *            the textArea to be drawn.
     * @param textX
     *            the X position of the textArea relative to current page.
     * @param textY
     *            the Y position of the textArea relative to current page.
     * @param width
     *            the Width of the textArea
     * @param height
     *              the height of the textArea
     * @param textStyle
     *            the style of the textArea
     * @param link
     *            the hyperlink of the textArea
     */
    public void drawText(String text, float textX, float textY, float width, float height, TextStyle textStyle,
            HyperlinkDef link) {
        FontInfo fontInfo = textStyle.getFontInfo();
        Color color = textStyle.getColor();
        boolean rtl = textStyle.isRtl();

        if (fontInfo == null) {
            return;
        }

        float descend = fontInfo.getBaseFont().getFontDescriptor(BaseFont.DESCENT, fontInfo.getFontSize());
        height = height + descend * 0.6f;

        BaseFont baseFont = fontInfo.getBaseFont();
        String fontName = getFontName(baseFont);

        println("<v:shape id=3D\"_x0000_s" + (++shapeCount) + "\" type=3D\"#_x0000_t202\""); //$NON-NLS-1$ //$NON-NLS-2$
        println(" style=3D'position:absolute;left:" + textX + "pt;top:" + textY + "pt;width:" + width + "pt;height:" //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
                + height + "pt;v-text-anchor:bottom-baseline;mso-wrap-style:none;'"); //$NON-NLS-1$
        println(" filled=3D'f' stroked=3D'f'>"); //$NON-NLS-1$
        println("<v:textbox style=3D'mso-fit-shape-to-text:f;' inset=3D'0.00pt 0.00pt 0.00pt 0.00pt'/>"); //$NON-NLS-1$
        println("</v:shape>"); //$NON-NLS-1$
        println("<div v:shape=3D\"_x0000_s" + shapeCount + "\">"); //$NON-NLS-1$ //$NON-NLS-2$

        println("<div style=3D'mso-text-indent-alt:" //$NON-NLS-1$
                + 0 + ";text-align:left;'>" //$NON-NLS-1$
                + "<span style=3D'font-family:" //$NON-NLS-1$
                + fontName + ";font-size:" //$NON-NLS-1$
                + fontInfo.getFontSize() + "pt;color:#" //$NON-NLS-1$
                + getColorString(color) + ";'" + buildI18nAttributes(text, rtl) + ">"); //$NON-NLS-2$

        boolean isItalic = fontInfo != null && (fontInfo.getFontStyle() & Font.ITALIC) != 0;
        boolean isBold = fontInfo != null && (fontInfo.getFontStyle() & Font.BOLD) != 0;

        boolean isUnderline = textStyle.isUnderline();

        if (isItalic) {
            print("<i>");
        }
        if (isBold) {
            print("<b>");
        }
        if (isUnderline) {
            print("<u>");
        }
        if (link != null) {
            String hyperlink = link.getLink();
            String tooltip = link.getTooltip();
            if (hyperlink != null) {
                hyperlink = codeLink(hyperlink);
                print("<p:onmouseclick  hyperlinktype=3D\"url\" href=3D\"" + hyperlink + "\"");
                if (tooltip != null) {
                    tooltip = codeLink(tooltip);
                    print(" tips=3D\"" + tooltip + "\"");
                }
                println("/><a href=3D\"" + hyperlink
                        + "/\" target=3D\"_parent\" onclick=3D\"window.event.cancelBubble=3Dtrue;\">");
            }
        }
        print(getEscapedStr(text));
        if (link != null) {
            print("</a>");
        }
        if (isUnderline) {
            print("</u>");
        }
        if (isBold) {
            print("</b>");
        }
        if (isItalic) {
            print("</i>");
        }
        println("</span></div>"); //$NON-NLS-1$
        println("</div>"); //$NON-NLS-1$
    }

    private String codeLink(String link) {
        try {
            if (quotedPrintableCodec == null) {
                quotedPrintableCodec = new QuotedPrintableCodec();
            }
            link = quotedPrintableCodec.encode(link);
        } catch (EncoderException e) {
            logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
        }
        return link;
    }

    private String getColorString(Color color) {
        StringBuffer buffer = new StringBuffer();
        appendComponent(buffer, color.getRed());
        appendComponent(buffer, color.getGreen());
        appendComponent(buffer, color.getBlue());
        return buffer.toString();
    }

    private void appendComponent(StringBuffer buffer, int component) {
        String hex = Integer.toHexString(component);
        if (hex.length() == 1) {
            buffer.append('0');
        }
        buffer.append(hex);
    }

    public void drawImage(String imageId, byte[] imageData, String extension, float imageX, float imageY,
            float height, float width, String helpText, HyperlinkDef link) throws Exception {
        ImageInfo imageInfo = getImageInfo(imageId, imageData, extension);
        exportImageDefn(imageInfo.imageName, imageInfo.imageId, width, height, imageX, imageY, link);
    }

    private ImageInfo getImageInfo(String imageId, byte[] imageData, String extension) {
        ImageInfo imageInfo = null;
        String imageTitle = getImageTitle();
        if (imageId != null) {
            imageId = imageId + "_" + currentPageNum;
        } else {
            imageId = imageTitle;
        }
        if (imageInfos.containsKey(imageId)) {
            imageInfo = imageInfos.get(imageId);
        } else {
            String imageName = imageTitle + "." + extension;
            imageInfo = new ImageInfo(imageId, imageName, extension, imageData);
            imageInfos.put(imageId, imageInfo);
            recordFileLists(imageName);
        }
        return imageInfo;
    }

    private static class ImageInfo {
        public String imageName;
        public String extension;
        public byte[] imageData;
        public String imageId;

        public ImageInfo(String imageId, String imageName, String extension, byte[] imageData) {
            this.imageId = imageId;
            this.imageName = imageName;
            this.extension = extension;
            this.imageData = imageData;
        }
    }

    /**
     * @param imageName
     * @param imageTitle
     * @param width
     * @param height
     * @param x
     * @param y
     */
    private void exportImageDefn(String imageName, String imageTitle, double width, double height, double x,
            double y, HyperlinkDef link) {
        println("<v:shape id=3D'" + (shapeCount) + "' type=3D'#_x0000_t75'"); //$NON-NLS-1$ //$NON-NLS-2$
        if (link != null) {
            String hyperlink = link.getLink();
            String tooltip = link.getTooltip();
            if (tooltip != null) {
                tooltip = codeLink(tooltip);
                print("title=3D\"" + tooltip + "\" ");
            }
            if (hyperlink != null) {
                hyperlink = codeLink(hyperlink);
                print("href=3D\"" + hyperlink + "\" target=3D\"_parent\"");
            }
        }
        Crop crop = checkCrop(x, y, width, height);
        if (crop == null) {
            println(" style=3D'position:absolute;left:" + x + "pt;top:" + y + "pt;width:" + width + "pt;height:" //$NON-NLS-3$//$NON-NLS-4$
                    + height + "pt'"); //$NON-NLS-1$ //$NON-NLS-4$ //$NON-NLS-5$
        } else {
            ClipArea clip = clipStack.peek();
            double pX = Math.max(clip.x, x);
            double pY = Math.max(clip.y, y);
            double pWidth = Math.min(x + width, clip.x + clip.width) - pX;
            double pHeight = Math.min(y + height, clip.y + clip.height) - pY;
            println(" style=3D'position:absolute;left:" + pX + "pt;top:" + pY + "pt;width:" + pWidth + "pt;height:" //$NON-NLS-3$//$NON-NLS-4$
                    + pHeight + "pt'"); //$NON-NLS-1$ //$NON-NLS-4$ //$NON-NLS-5$
        }
        println(" filled=3D'f' stroked=3D'f'>"); //$NON-NLS-1$
        print("<v:imagedata src=3D\"" + imageName + "\" o:title=3D\"" + imageTitle + "\"");
        if (crop != null) {
            if (crop.top != 0) {
                print(" croptop=3D\"" + crop.top + "f\"");
            }
            if (crop.left != 0d) {
                print(" cropleft=3D\"" + crop.left + "f\"");
            }
            if (crop.right != 0d) {
                print(" cropright=3D\"" + crop.right + "f\"");
            }
            if (crop.bottom != 0d) {
                print(" cropbottom=3D\"" + crop.bottom + "f\"");
            }
        }
        println("/>");
        println("<o:lock v:ext=3D'" + "edit" + "' aspectratio=3D't" + "'/>");
        println("</v:shape>"); //$NON-NLS-1$
    }

    private Crop checkCrop(double x, double y, double width, double height) {
        if (clipStack.isEmpty()) {
            return null;
        }
        ClipArea clip = clipStack.peek();
        int left = 0, right = 0, top = 0, bottom = 0;
        if (x < clip.x) {
            left = (int) ((clip.x - x) * 100);
        }
        if (y < clip.y) {
            top = (int) ((clip.y - y) * 100);
        }
        if (x + width > clip.x + clip.width) {
            right = (int) (((x + width) - (clip.x + clip.width)) * 100);
        }
        if (y + height > clip.y + clip.height) {
            bottom = (int) (((y + height) - (clip.y + clip.height)) * 100);
        }
        if (left != 0 || right != 0 || top != 0 || bottom != 0) {
            return new Crop(left, right, top, bottom);
        }
        return null;
    }

    private class Crop {

        int left, right, top, bottom;

        Crop(int left, int right, int top, int bottom) {
            this.left = left;
            this.right = right;
            this.top = top;
            this.bottom = bottom;
        }
    }

    private String getImageExtension(String imageURI) {
        String rectifiedImageURI = imageURI.replace('.', '&');
        String extension = imageURI.substring(rectifiedImageURI.lastIndexOf('&') + 1).toLowerCase();

        if (extension.equals("svg")) {
            extension = "jpg";
        }
        return extension;
    }

    /*
     * Save the image name into file list of current page
     */
    private void recordFileLists(String filename) {

        if (fileNamesLists.containsKey(currentPageNum)) {
            fileNamesLists.get(currentPageNum).add(filename);
        } else {
            List<String> fileNames = new ArrayList<String>();
            fileNames.add(filename);
            fileNamesLists.put(currentPageNum, fileNames);
        }
    }

    /**
     * Draws a line from the start position to the end position with the given
     * line width, color, and style on the PPT.
     * 
     * @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 given line style
     */
    public void drawLine(double startX, double startY, double endX, double endY, double 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 || lineStyle == BorderInfo.BORDER_STYLE_NONE) //$NON-NLS-1$
        {
            return;
        }
        if (lineStyle == BorderInfo.BORDER_STYLE_SOLID || lineStyle == BorderInfo.BORDER_STYLE_DASHED
                || lineStyle == BorderInfo.BORDER_STYLE_DOTTED || lineStyle == BorderInfo.BORDER_STYLE_DOUBLE) {
            drawRawLine(startX, startY, endX, endY, width, color, lineStyle);
        } else {
            // the other line styles, e.g. 'ridge', 'outset', 'groove', 'insert'
            // is NOT supported now.
            // We look it as the default line style -- 'solid'
            drawRawLine(startX, startY, endX, endY, width, color, BorderInfo.BORDER_STYLE_SOLID);
        }
    }

    /**
     * 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
     * on the PPT. 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
     */
    private void drawRawLine(double startX, double startY, double endX, double endY, double width, Color color,
            int lineStyle) {
        boolean needflip = false;
        if (endX > startX && endY < startY || endX < startX && endY > startY) {
            needflip = true;
        }
        print("<v:line id=3D\"" + (++shapeCount) + "\""); //$NON-NLS-1$ //$NON-NLS-2$
        print(" style=3D'position:absolute");
        if (needflip) {
            print(";flip:y' from=3D\"" + startX + "pt," + endY + "pt\"");
            print(" to=3D\"" + endX + "pt," + startY + "pt\"");
        } else {
            print("' from=3D\"" + startX + "pt," + startY + "pt\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
            print(" to=3D\"" + endX + "pt," + endY + "pt\"");
        }
        print(" strokecolor=3D\"#" + getColorString(color) + "\""); //$NON-NLS-1$
        print(" strokeweight=3D\"" + width + "pt\""); //$NON-NLS-1$
        if (lineStyle == BorderInfo.BORDER_STYLE_DASHED) {
            println(">");
            println("<v:stroke dashstyle=3D\"dash\"/>");
        } else if (lineStyle == BorderInfo.BORDER_STYLE_DOTTED) {
            println(">");
            println("<v:stroke dashstyle=3D\"1 1\"/>");
        } else if (lineStyle == BorderInfo.BORDER_STYLE_DOUBLE) {
            println(">");
            println("<v:stroke linestyle=3D\"thinThin\"/>");
        } else {
            println("/>");
            return;
        }
        println("</v:line>");
    }

    /**
     * Draws the background color of the PPT.
     * 
     * @param color
     *            the color to be drawn
     * @param x
     *            the start X coordinate
     * @param y
     *            the start Y coordinate
     * @param width
     *            the width of the background dimension
     * @param height
     *            the height of the background dimension
     */
    public void drawBackgroundColor(Color color, double x, double y, double width, double height) {
        if (color == null) {
            return;
        }
        print("<v:rect id=3D\"" + (++shapeCount) + "\""); //$NON-NLS-1$ //$NON-NLS-2$
        print(" style=3D'position:absolute;left:" + x + "pt;top:" + y + "pt;width:" + width + "pt;height:" + height
                + "pt'");
        print(" fillcolor=3D\"#" + getColorString(color) + "\""); //$NON-NLS-1$
        println(" stroked=3D\"f" + "\"/>"); //$NON-NLS-1$         
    }

    /**
     * Draws the background image at the contentByteUnder of the PPT with the
     * given offset
     * 
     * @param imageURI
     *            the URI referring the image
     * @param x
     *            the start X coordinate at the PPT where the image is
     *            positioned
     * @param y
     *            the start Y coordinate at the PPT where the image is
     *            positioned
     * @param width
     *            the width of the background dimension
     * @param height
     *            the height of the background dimension
     * @param positionX
     *            the offset X percentage relating to start X
     * @param positionY
     *            the offset Y percentage relating to start Y
     * @param repeat
     *            the background-repeat property
     * @param xMode
     *            whether the horizontal position is a percentage value or not
     * @param yMode
     *            whether the vertical position is a percentage value or not
     */
    public void drawBackgroundImage(String imageURI, byte[] imageData, float x, float y, float width, float height,
            float iWidth, float iHeight, float positionX, float positionY, int repeat) {
        if (imageData == null || imageData.length == 0) {
            return;
        }
        float imageWidth = iWidth;
        float imageHeight = iHeight;
        String extension = getImageExtension(imageURI);
        try {
            org.eclipse.birt.report.engine.layout.emitter.Image image = EmitterUtil.parseImage(imageData, null,
                    null);
            imageData = image.getData();
            if (imageWidth == 0 || imageHeight == 0) {
                imageWidth = image.getWidth();
                imageHeight = image.getHeight();
            }

            ImageInfo imageInfo = getImageInfo(imageURI, imageData, extension);

            Position areaPosition = new Position(x, y);
            Position areaSize = new Position(width, height);
            Position imagePosition = new Position(x + positionX, y + positionY);
            Position imageSize = new Position(imageWidth, imageHeight);
            BackgroundImageLayout layout = new BackgroundImageLayout(areaPosition, areaSize, imagePosition,
                    imageSize);
            Collection positions = layout.getImagePositions(repeat);
            Iterator iterator = positions.iterator();
            while (iterator.hasNext()) {
                Position position = (Position) iterator.next();
                exportImageDefn(imageInfo.imageName, imageInfo.imageId, imageWidth, imageHeight, position.getX(),
                        position.getY(), null);
            }
        } catch (IOException e) {
            logger.log(Level.WARNING, e.getLocalizedMessage());
        }
    }

    private String getImageTitle() {
        return "slide" + currentPageNum + "_image" + (++shapeCount);
    }

    protected String getEscapedStr(String s) {
        StringBuffer result = null;
        int spacePos = 1;
        char[] s2char = s.toCharArray();

        for (int i = 0, max = s2char.length, delta = 0; i < max; i++) {
            char c = s2char[i];
            String replacement = null;
            if (c == ' ') {
                if (spacePos % 2 != 0 || i == max - 1) {
                    replacement = "&#160;"; //$NON-NLS-1$
                }
                spacePos++;
            } else {
                spacePos = 0;
            }

            // Filters the char not defined.
            if (!(c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD))) {
                // Ignores the illegal character.
                replacement = ""; //$NON-NLS-1$
            } else if (c == '&') {
                replacement = "&amp;"; //$NON-NLS-1$
            } else if (c == '<') {
                replacement = "&lt;"; //$NON-NLS-1$
            } else if (c == '>') {
                replacement = "&gt;"; //$NON-NLS-1$
            } else if (c == '\t') {
                replacement = " "; //$NON-NLS-1$
            } else if (c == '=') {
                replacement = "=3D";
            } else if (c >= 0x80) {
                replacement = "&#x" + Integer.toHexString(c) + ';'; //$NON-NLS-1$ 
            }
            if (replacement != null) {
                if (result == null) {
                    result = new StringBuffer(s);
                }
                result.replace(i + delta, i + delta + 1, replacement);
                delta += (replacement.length() - 1);
            }
        }
        if (result == null) {
            return s;
        }
        return result.toString();
    }

    /**
     * Builds i18n attributes.
     *
     * @param rtl
     *            Whether the string being processed is right-to-left or not.
     *
     * @author bidi_hcg
     */
    private String buildI18nAttributes(String text, boolean rtl) {
        if (text == null)
            return ""; //$NON-NLS-1$

        if (rtl) {
            for (int i = text.length(); i-- > 0;) {
                UnicodeBlock block = UCharacter.UnicodeBlock.of(text.charAt(i));
                // If there is a Hebrew or Arabic content, write the 
                // corresponding language attribute
                if (UCharacter.UnicodeBlock.HEBREW.equals(block)) {
                    return " dir=3D'rtl' lang=3D'HE-IL'"; //$NON-NLS-1$
                }
                if (UCharacter.UnicodeBlock.ARABIC.equals(block)
                        || UCharacter.UnicodeBlock.ARABIC_PRESENTATION_FORMS_A.equals(block)
                        || UCharacter.UnicodeBlock.ARABIC_PRESENTATION_FORMS_B.equals(block)
                        || UCharacter.UnicodeBlock.ARABIC_SUPPLEMENT.equals(block)) {
                    return " dir=3D'rtl' lang=3D'AR-DZ'"; //$NON-NLS-1$
                }
            }
            // If no actual RTL content was found (e.g. in case the text
            // consists of sheer neutral characters), indicate Arabic language
            return " dir=3D'rtl' lang=3D'AR-DZ'"; //$NON-NLS-1$
        } else {
            // XXX Other language attributes can be addressed as needed
            return " dir=3D'ltr' lang=3D'EN-US'"; //$NON-NLS-1$
        }
    }

    private Stack<ClipArea> clipStack = new Stack<ClipArea>();

    private class ClipArea {

        float x, y, width, height;

        ClipArea(float x, float y, float width, float height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }
    }

    public void clip(float startX, float startY, float width, float height) {
        if (clipStack.isEmpty()) {
            clipStack.push(new ClipArea(startX, startY, width, height));
        } else {
            ClipArea parent = clipStack.peek();
            float newX = Math.max(parent.x, startX);
            float newY = Math.max(parent.y, startY);
            float newWidth = Math.min(startX + width, parent.x + parent.width) - newX;
            float newHeight = Math.min(startY + height, parent.y + parent.height) - newY;
            clipStack.push(new ClipArea(newX, newY, newWidth, newHeight));
        }
    }

    public void clipEnd() {
        clipStack.pop();

    }
}