0

i'm trying to insert a EMF image into HSSFWorkbook(apache poi 3.9). But when i open the result document - i catch an error of content. I'm looking for solution of problem and found the letter on apache mail: http://mail-archives.apache.org/mod_mbox/poi-user/200710.mbox/%3C1011226198.20071009084134@dinom.ru%3E

As i understand - emf need special(!) preprocessing of data.

I looked into a EMF class (org.apache.poi.hslf.blip) and try to get rawData from it. But it doesn't help too.

Has anyone any idea about the problem

user2078222
  • 135
  • 10
  • I'm still searching for a poi solution, but if this is an option for you, it might work with [aspose](http://www.aspose.com/community/forums/thread/48412/problem-inserting-emf-images-using-memory-stream-object.aspx). If you saved a emf before with Libre Office, the extracted stream is the same as my input-emf, but if you add it with POI 3.9 it will show up under a different blip record attribute, i.e "remaining_data" ... – kiwiwings May 25 '13 at 11:41

1 Answers1

1

Ok here is a hack, which I've mixed together with the help of the following infos:

  • The source of HSSFWorkbook.addPicture
  • A bit of information of the Chicago project
  • A simple .xls with an EMF graphic from Libre Office
  • and checking my trial-and-error results via the BiffViewer

disclaimer: as this code is just a quick hack, has only be tested with one picture, contains a few copy&paste lines which I don't understand and also trial-and-error values, it would be nice, if you give some feedback, if it works in your environment ...

check the code comments for parts, which I'm not so sure.

(tested with POI 3.9, Libre Office 4.0 and MS Excel Viewer)

import java.awt.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.List;
import java.util.zip.DeflaterOutputStream;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.poi.ddf.*;
import org.apache.poi.hssf.dev.BiffViewer;
import org.apache.poi.hssf.model.InternalWorkbook;
import org.apache.poi.hssf.record.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;

@SuppressWarnings("unused")
public class HssfEmfPicture {
    public static void main(String[] args) throws Exception {
        HSSFWorkbook wb = new HSSFWorkbook();
        FileInputStream fis = new FileInputStream("src/test/resources/allianz.emf");
        byte[] img_bytes = IOUtils.toByteArray(fis);
        fis.close();

        // code taken from HSSFWorkbook.addPicture 
        Method m = HSSFWorkbook.class.getDeclaredMethod("initDrawings");
        m.setAccessible(true);
        m.invoke(wb);

        int format = HSSFWorkbook.PICTURE_TYPE_EMF;

        byte[] uid = DigestUtils.md5(img_bytes);
        EscherMetafileBlip blipRecord = new EscherMetafileBlip();
        blipRecord.setRecordId((short) (EscherMetafileBlip.RECORD_ID_START + format));
        blipRecord.setOptions(HSSFPictureData.MSOBI_EMF);
        blipRecord.setUID(uid);
        blipRecord.setCompressed(true);
        blipRecord.setPictureData(img_bytes);
        blipRecord.setUncompressedSize(img_bytes.length);

        // info of chicago project:
        // "... LZ compression algorithm in the format used by GNU Zip deflate/inflate with a 32k window ..."
        // not sure what to do, when lookup tables exceed 32k ...
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DeflaterOutputStream dos = new DeflaterOutputStream(bos);
        dos.write(img_bytes);
        dos.close();
        byte img_bytes_lz[] = bos.toByteArray();
        blipRecord.setCompressedSize(img_bytes_lz.length);

        Field field = EscherMetafileBlip.class.getDeclaredField("raw_pictureData");
        field.setAccessible(true);
        field.set(blipRecord, img_bytes_lz);

        // trial-and-error, it won't open, if this is left to 0
        field = EscherMetafileBlip.class.getDeclaredField("field_7_fFilter");
        field.setAccessible(true);
        field.set(blipRecord, (byte) -2);

        EscherBSERecord r = new EscherBSERecord();
        r.setRecordId(EscherBSERecord.RECORD_ID);
        r.setOptions((short) (0x0002 | (format << 4)));
        // libre office sets a png format for mac ... need to be tested ... 
        r.setBlipTypeMacOS((byte) format);
        r.setBlipTypeWin32((byte) format);
        r.setUid(uid);
        r.setTag((short) 0x0);
        // trial-and-error, with pngs (EscherBitmapBlip?) its 25
        // ... with emf (EscherMetafileBlip) it seems to be 58
        r.setSize(img_bytes_lz.length + 58);
        r.setRef(0);
        r.setOffset(0);
        r.setBlipRecord(blipRecord);

        field = HSSFWorkbook.class.getDeclaredField("workbook");
        field.setAccessible(true);
        InternalWorkbook iWb = (InternalWorkbook) field.get(wb);

        m = InternalWorkbook.class.getDeclaredMethod("addBSERecord", EscherBSERecord.class);
        m.setAccessible(true);
        int index = (Integer) m.invoke(iWb, r);

        // Anchor has to be valid ... otherwise the emf is not shown
        CreationHelper ch = wb.getCreationHelper();
        ClientAnchor anchor = ch.createClientAnchor();
        anchor.setCol1(2);
        anchor.setCol2(6);
        anchor.setRow1(1);
        anchor.setRow2(6);
        anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);

        HSSFSheet sheet = wb.createSheet();
        HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
        HSSFPicture pict = patriarch.createPicture(anchor, index);

        FileOutputStream fos = new FileOutputStream("hssf-emf.xls");
        wb.write(fos);
        fos.flush();
        fos.close();
    }
}
kiwiwings
  • 3,386
  • 1
  • 21
  • 57
  • 1
    I'm sure the Apache POI project would be [very welcoming of this](http://poi.apache.org/guidelines.html) if you'd be happy to contribute it! – Gagravarr May 26 '13 at 16:34
  • i'll try to fix it in apache poi but have some problem with compression of image. it's look that Excel compression algo differ from LZ http://stackoverflow.com/questions/16822986/non-symmetric-java-compression – user2078222 May 29 '13 at 20:30
  • after I posted this hack, I've tried other emf files and it worked too. The jdk deflater is also used in other areas of poi, so I think it's probably the right algo to use. In your linked question, can you try to work with files instead of byte literals, just to be sure, it's not a kind of integer to byte conversion in the literal. Which jvm version are you using? – kiwiwings May 29 '13 at 21:35
  • one more thing ... how big is your emf file? ... have you read the comment about the 32kb window (presumably 32kb lz dictionary size) – kiwiwings May 29 '13 at 21:40
  • @gagravarr finally bad weather arrived all around and I have some time to submit some patches. The POI patch can be found under [Bug #49658](https://issues.apache.org/bugzilla/show_bug.cgi?id=49658). Not sure how partial resolved issues are treated in bugzilla though – kiwiwings Sep 15 '13 at 12:32