tralalaUlalal tralalaUlalal - 15 days ago 6
Apache Configuration Question

apache poi ppt to png emf

I would like to ask about library APACHE POI. I have a .pptx file and with the example which I found on internet I split each slide into separate images. This works great when slide containts .png, .jpeg images but as soon as I have .emf files in slide this image disappear. So I want to have the same copy of slide but with .emf file as well. Is this possible?

version: Apache POI 3.12

Thanks a lot

Answer

As mentioned in the comments, EMF is not supported out of the box ... and as FreeHep decided to have a LGPL license it's unlikely that we will include in our release.

In POI you basically have two options to provide a custom image renderer:

  • implement and register your ImageRenderer implementation in the Graphics2D context via setRenderingHint(Drawable.IMAGE_RENDERER, new MyImageRendener()). The drawback is, that it will be called for any images and you loose the default handling for bitmap/wmf images
  • or provide a custom DrawFactory which serves your own DrawPictureShape implementation - as in the example below - which only diverts to the EMF renderer when necessary
    (looking at this example, the current POI ImageRenderer handling doesn't look flexible and instead of having a global handler, it might be better to register a handler per content-type ...)

Apart of the usual POI component I've included freehep-graphics2d, freehep-graphicsbase, freehep-graphicsio, freehep-graphicsio-emf and freehep-io.

The example is far from being finished, but I guess you get at least a kick-start:

package org.apache.poi.xslf;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;

import org.apache.poi.sl.draw.DrawFactory;
import org.apache.poi.sl.draw.DrawPictureShape;
import org.apache.poi.sl.draw.Drawable;
import org.apache.poi.sl.draw.ImageRenderer;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.sl.usermodel.PictureShape;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFPictureShape;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.freehep.graphicsio.emf.EMFInputStream;
import org.freehep.graphicsio.emf.EMFRenderer;
import org.junit.Test;

public class TestEMFRender {
    private final static POILogger LOG = POILogFactory.getLogger(TestEMFRender.class);

    @Test
    public void render() throws Exception {
        XMLSlideShow ppt = getDummy();

        Dimension pgsize = ppt.getPageSize();
        BufferedImage img = new BufferedImage((int)pgsize.getWidth(), (int)pgsize.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = initGraphicsCtx(img);

        // draw stuff
        ppt.getSlides().get(0).draw(graphics);

        // save the result
        File outfile = new File("bla.png");
        ImageIO.write(img, "PNG", outfile);

        // cleanup
        graphics.dispose();
        img.flush();
        ppt.close();
    }

    static XMLSlideShow getDummy() throws IOException {
        XMLSlideShow ppt = new XMLSlideShow();
        FileInputStream fis = new FileInputStream("kiwilogo.emf");
        PictureData pd = ppt.addPicture(fis, PictureType.EMF);
        fis.close();
        XSLFSlide sl = ppt.createSlide();
        XSLFPictureShape ps = sl.createPicture(pd);
        ps.setAnchor(new Rectangle2D.Double(100, 100, 100, 100));
        return ppt;
    }

    static Graphics2D initGraphicsCtx(BufferedImage img) {
        Graphics2D graphics = img.createGraphics();
        // default rendering options
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

        // custom draw factory
        DrawFactory df = new DrawFactory(){
            public DrawPictureShape getDrawable(PictureShape<?,?> shape) {
                return new DrawPictureShapeEmf(shape);
            }
        };
        graphics.setRenderingHint(Drawable.DRAW_FACTORY, df);

        df.fixFonts(graphics);

        return graphics;
    }

    static class DrawPictureShapeEmf extends DrawPictureShape {
        public DrawPictureShapeEmf(PictureShape<?,?> shape) {
            super(shape);
        }


        @Override
        public void drawContent(Graphics2D graphics) {
            PictureData data = getShape().getPictureData();
            if(data == null) return;

            Rectangle2D anchor = getAnchor(graphics, getShape());
            Insets insets = getShape().getClipping();

            try {
                String contentType = data.getContentType();
                ImageRenderer renderer =
                    PictureType.EMF.contentType.equals(contentType)
                    ? new ImgRenderer()
                    : super.getImageRenderer(graphics, contentType);

                renderer.loadImage(data.getData(), data.getContentType());
                renderer.drawImage(graphics, anchor, insets);
            } catch (IOException e) {
                LOG.log(POILogger.ERROR, "image can't be loaded/rendered.", e);
            }
        }

    }


    static class ImgRenderer implements ImageRenderer {
        EMFRenderer emfRenderer;

        public void loadImage(InputStream data, String contentType) throws IOException {
            emfRenderer = new EMFRenderer(new EMFInputStream(data));
        }

        public void loadImage(byte[] data, String contentType) throws IOException {
            loadImage(new ByteArrayInputStream(data), contentType);
        }

        public Dimension getDimension() {
            return emfRenderer.getSize();
        }

        public void setAlpha(double alpha) {
        }

        public BufferedImage getImage() {
            return null;
        }

        public BufferedImage getImage(Dimension dim) {
            return null;
        }

        public boolean drawImage(Graphics2D ctx, Rectangle2D graphicsBounds) {
            return drawImage(ctx, graphicsBounds, null);
        }

        public boolean drawImage(Graphics2D ctx, Rectangle2D graphicsBounds, Insets clip) {
            AffineTransform at = ctx.getTransform();
            try {
                Dimension emfDim = emfRenderer.getSize();
                // scale output bounds to image bounds
                ctx.translate(graphicsBounds.getX(), graphicsBounds.getY());
                ctx.scale(graphicsBounds.getWidth()/emfDim.getWidth(), graphicsBounds.getHeight()/emfDim.getHeight());

                // TODO: handle clipping

                emfRenderer.paint(ctx);

                return true;
            } catch (RuntimeException e) {
                // TODO: logging
                return false;
            } finally {
                ctx.setTransform(at);
            }
        }

    }
}
Comments