PPT/PPTX/PDF 转图片

1. 需求

在直播系统中,最开始的课件是用一个小窗口调本地的powerpoint软件打开PPT,老师在原生的PPT上进行操作,然后把这个窗口录下来,同步给学生。

问题

我没有开发win客户端,只能从现象上进行描述 1. 如果本没有powerpoint,或者装了wps,或者装的2003打不开pptx,都会导致没法继续下一步。
2. 如果中间我们的直播软件挂了,而powerpoint没有挂,需要从任务管理器杀进程。另外powerpoint挂了,下次打开可能会提示是否要继续原来的操作会导致流程中断打开也会出现问题。
3. 原来的PPT画笔工具老师反馈不好用,例如画笔的切换需要点多次才能重新调起。不能用键盘打字,只能输入。

由于有这样的问题,所以客户端同事给的结论就是:我们先将PPT转成图片,再加载到内存中显示出来。一开始是本机本地转,一来是打开变慢了;另外有些笔记本需要调版式中的长宽比例和实际宽高才能在转的时候不至于有2厘米显示不出来。另外还有装的绿色版的的powerpoint直接转不了。还有如果有wps也是转不了的。有的电脑删除了wps,装office 2007也不行,只能在装了office 2013之后才能转出图片。

所以进一步的需求,就是想用服务端来做转换,转好后存在课件信息中,以后客户端可以重复使用不用再转。另外学生也可以在web端直接打开url进行翻页。需要支持PPT,PPTX,以及PDF。

2. 做法

直接上代码吧。主要用了以下的一些工具:pdfbox,poi,batik

gradle中的设置:  
compile "external:pdfbox:1.8.10"  
compile "external:poi:3.1.3-beta1"  
compile (configuration: 'core', group :'external',name :'batik', version:'1.7.2') {}  
以下是代码,最终转成了svg

import java.awt.Dimension;  
import java.awt.Graphics2D;  
import java.awt.geom.Rectangle2D;  
import java.awt.image.BufferedImage;  
import java.awt.print.Printable;  
import java.io.*;  
import javax.imageio.ImageIO;  
import javax.xml.transform.*;  
import javax.xml.transform.dom.DOMSource;  
import javax.xml.transform.stream.StreamResult;

import org.apache.batik.dom.GenericDOMImplementation;  
import org.apache.batik.dom.svg.SVGDOMImplementation;  
import org.apache.batik.svggen.SVGGeneratorContext;  
import org.apache.batik.svggen.SVGGraphics2D;  
import org.apache.batik.transcoder.wmf.tosvg.WMFPainter;  
import org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore;  
import org.apache.pdfbox.pdmodel.PDDocument;  
import org.apache.poi.hslf.HSLFSlideShow;  
import org.apache.poi.hslf.model.Slide;  
import org.apache.poi.hslf.usermodel.SlideShow;  
import org.apache.poi.openxml4j.opc.PackagePart;  
import org.apache.poi.xslf.usermodel.*;  
import org.w3c.dom.DOMImplementation;  
import org.w3c.dom.Document;

public final class ImageConvertor {

    /**
     *
     * @param sourceFilePath
     * @param destinationDir
     * @return 最大的图片文件页码, -1表示转换失败
     * @throws Exception
     */
    public static int convert(String sourceFilePath, String destinationDir) throws Exception {
        if (sourceFilePath.endsWith("pptx")) {
            return parsePPTX(sourceFilePath, destinationDir);
        } else if (sourceFilePath.endsWith("ppt")){
            return parsePPT(sourceFilePath, destinationDir);
        } else if (sourceFilePath.endsWith("pdf")) {
            return parsePDF(sourceFilePath, destinationDir);
        } else {
            System.out.println("error: only parse pptx/ppt/pdf");
        }
        return -1;
    }


    /**
     * 解析PPT
     * @throws IOException
     */
    private static int parsePPT(String file, String destinationDir) throws IOException, TransformerException {
        FileInputStream fileInputStream = new FileInputStream(file);
        HSLFSlideShow ppt = new HSLFSlideShow(fileInputStream);
        fileInputStream.close();
        SlideShow slideShow = new SlideShow(ppt);
        Dimension pgsize = slideShow.getPageSize();
        System.out.println("pgsize=" + pgsize);
        Slide[] slide = slideShow.getSlides();
        int i;
        for(i = 0; i < slide.length; ++i) {
            // Create initial SVG DOM
            DOMImplementation domImpl = SVGDOMImplementation.getDOMImplementation();
            Document doc = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", null);
            //Use Batik SVG Graphics2D driver
            SVGGraphics2D graphics = new SVGGraphics2D(doc);
            graphics.setRenderingHint(XSLFRenderingHint.IMAGE_RENDERER, new WMFImageRender());
            graphics.setSVGCanvasSize(pgsize);
            // draw stuff. All the heavy-lifting happens here
            slide[i].draw(graphics);
            // save the result.
            String fname = destinationDir + i + ".svg";
            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(fname), "UTF-8");
            DOMSource domSource = new DOMSource(graphics.getRoot());
            StreamResult streamResult = new StreamResult(out);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer serializer = tf.newTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.transform(domSource, streamResult);
            out.flush();
            out.close();
        }
        return i;
    }


    /**
     * 解析PPTX
     * @throws IOException
     */
    private static int parsePPTX(String file, String destinationDir) throws IOException, TransformerException {
        FileInputStream fileInputStream = new FileInputStream(file);
        XMLSlideShow ppt = new XMLSlideShow(fileInputStream);
        fileInputStream.close();
        Dimension pgsize = ppt.getPageSize();
        XSLFSlide[] slide = ppt.getSlides();

        int i;
        for(i = 0; i < slide.length; ++i) {
            // Create initial SVG DOM
            DOMImplementation domImpl = SVGDOMImplementation.getDOMImplementation();
            Document doc = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", null);
            //Use Batik SVG Graphics2D driver
            SVGGraphics2D graphics = new SVGGraphics2D(doc);
            graphics.setRenderingHint(XSLFRenderingHint.IMAGE_RENDERER, new WMFImageRender());
            graphics.setSVGCanvasSize(pgsize);
            // draw stuff. All the heavy-lifting happens here
            slide[i].draw(graphics);
            // save the result.
            String fname = destinationDir + i + ".svg";
            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(fname), "UTF-8");
            DOMSource domSource = new DOMSource(graphics.getRoot());
            StreamResult streamResult = new StreamResult(out);
            TransformerFactory tf = TransformerFactory.newInstance();
            Transformer serializer = tf.newTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.transform(domSource, streamResult);
            out.flush();
            out.close();
        }
        return i;
    }


    /**
     * 解析PDF
     * @throws IOException
     */
    private static int parsePDF(String file, String destinationDir) throws Exception {
        PDDocument document = PDDocument.load(file);
        DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation();

        // Create an instance of org.w3c.dom.Document.
        String svgNS = "http://www.w3.org/2000/svg";
        Document svgDocument = domImpl.createDocument(svgNS, "svg", null);
        SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(svgDocument);
        ctx.setEmbeddedFontsOn(true);

        // save the result.
        int i;
        for(i = 0 ; i < document.getNumberOfPages() ; i++){
            String fname = destinationDir + i + ".svg";
            (new File(fname)).createNewFile();
            SVGGraphics2D svgGenerator = new SVGGraphics2D(ctx,false);
            Printable page  = document.getPrintable(i);
            page.print(svgGenerator, document.getPageFormat(i), i);
            svgGenerator.stream(fname);
        }
        return i;
    }
    /**
     * Image renderer with support for .wmf images
     */
    static class WMFImageRender extends XSLFImageRenderer {

        /**
         * Use Apache Batik to render WMF,
         * delegate all other types of images to the javax.imageio framework
         */
        @Override
        public boolean drawImage(Graphics2D graphics, XSLFPictureData data,
                                 Rectangle2D anchor) {
            try {
                // see what type of image we are
                PackagePart part = data.getPackagePart();
                String contentType = part.getContentType();
                if (contentType.equals("image/x-wmf")) {
                    WMFRecordStore currentStore = new WMFRecordStore();
                    currentStore.read(new DataInputStream(part.getInputStream()));
                    int wmfwidth = currentStore.getWidthPixels();
                    float conv = (float) anchor.getWidth() / wmfwidth;

                    // Build a painter for the Re   cordStore
                    WMFPainter painter = new WMFPainter(currentStore,
                            (int) anchor.getX(), (int) anchor.getY(), conv);
                    painter.paint(graphics);
                } else {
                    BufferedImage img = ImageIO.read(data.getPackagePart().getInputStream());
                    graphics.drawImage(img,
                            (int) anchor.getX(), (int) anchor.getY(),
                            (int) anchor.getWidth(), (int) anchor.getHeight(), null);
                }
            } catch (Exception e) {
                return false;
            }
            return true;
        }

        /**
         * Convert data form the supplied package part into a BufferedImage.
         * This method is used to create texture paint.
         */
        @Override
        public BufferedImage readImage(PackagePart packagePart) throws IOException {
            String contentType = packagePart.getContentType();
            if (contentType.equals("image/x-wmf")) {
                try {
                    WMFRecordStore currentStore = new WMFRecordStore();
                    currentStore.read(new DataInputStream(packagePart.getInputStream()));
                    int wmfwidth = currentStore.getWidthPixels();
                    int wmfheight = currentStore.getHeightPixels();

                    BufferedImage img = new BufferedImage(wmfwidth, wmfheight, BufferedImage.TYPE_INT_RGB);
                    Graphics2D graphics = img.createGraphics();

                    // Build a painter for the RecordStore
                    WMFPainter painter = new WMFPainter(currentStore, 0, 0, 1.0f);
                    painter.paint(graphics);

                    return img;
                } catch (IOException e) {
                    return null;
                }
            } else {
                return ImageIO.read(packagePart.getInputStream());
            }
        }
    }
}
comments powered by Disqus