Adding image with docx4j to doc file

2019-08-18 07:22发布

问题:

I'm trying to add an image to a docx file using docx4j library within Android.
I've faced to an exception:

E/AndroidRuntime(21818): java.lang.ExceptionInInitializerError
E/AndroidRuntime(21818):    at org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.newSource(AbstractImageSessionContext.java:134)
E/AndroidRuntime(21818):    at org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.needSource(AbstractImageSessionContext.java:280)
E/AndroidRuntime(21818):    at org.apache.xmlgraphics.image.loader.cache.ImageCache.needImageInfo(ImageCache.java:123)
E/AndroidRuntime(21818):    at org.apache.xmlgraphics.image.loader.ImageManager.getImageInfo(ImageManager.java:122)
E/AndroidRuntime(21818):    at org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.getImageInfo(BinaryPartAbstractImage.java:696)
E/AndroidRuntime(21818):    at org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.ensureFormatIsSupported(BinaryPartAbstractImage.java:352)
E/AndroidRuntime(21818):    at org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.ensureFormatIsSupported(BinaryPartAbstractImage.java:331)
E/AndroidRuntime(21818):    at org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart(BinaryPartAbstractImage.java:298)
E/AndroidRuntime(21818):    at org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart(BinaryPartAbstractImage.java:158)
                                ...
E/AndroidRuntime(21818): Caused by: java.lang.NoClassDefFoundError: sun.awt.AppContext
E/AndroidRuntime(21818):    at ae.javax.imageio.spi.IIORegistry.getDefaultInstance(IIORegistry.java:155)
E/AndroidRuntime(21818):    at ae.javax.imageio.ImageIO.<clinit>(ImageIO.java:65)
                                ...

It is referring to this code:

WordprocessingMLPackage wordMLPackage;
File file;
...
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, file);

I've already added all necessary libraries from AndroidDocxToHtml example (and ae-awt.jar too) to libs folder of my app.
Without images my app generates docx files perfectly.

Is there any way to solve it?
Thanks!

回答1:

I got this to work but it required overriding about 8 files from ae-awt and ae-xmlgraphics-commons. And I hardcoded it to handle jpg files only.

Remove all references to sun.security.action.LoadLibraryAction and just call System.loadLibrary("jpeg")

Remove AppContext and replace it with ThreadGroup from ImageIO, like this:

   private static synchronized CacheInfo getCacheInfo() {
        ThreadGroup group = Thread.currentThread().getThreadGroup();
        CacheInfo info = (CacheInfo) instances.get(group);

        //CacheInfo info = (CacheInfo)context.get(CacheInfo.class);
        if (info == null) {
            info = new CacheInfo();
            instances.put(group, info);
        }
        return info;
    }

Same with imageio.spi.IIORegistry

In ae-xmlgraphics-commons, made a few changes to ImageManager.java, DefaultImageContext and BinaryPartAbstractImage. The first 2 I dont remember what I did (i can't diff them easily, maybe no changes were needed), but there were many changes to BinaryPartAbstractIMage. I hardcoded the getImageInfo() to IMAGE_JPEG because of problems with the sessionContext / Context type (that somehow tried to determine the image type and call the appropriate preloader). So I made the assumption that all images are jpegs and force it to always use the JPEG preloader.

getImageInfo() ImageInfo info = new ImageInfo(url.toURI().toString(), ContentTypes.IMAGE_JPEG);

That got a warped image to appear in the doc. I still have to figure out the width/height formula to get it to embed correctly.



回答2:

For everyone, who faced this problem too.

Here are necessary steps to make docx4j works fine with images:

  1. Add missed classes from OpenJDK to appropriate packages with ae.
  2. Change references to new classes, for example sun.awt.AppContext to ae.sun.awt.AppContext.
  3. In org.apache.xmlgraphics.util.Service manually fill list with preloaders:

    private static List<String> getProviderNames(Class<?> cls, ClassLoader cl) {
        ...
        if (fillDefautsProviderNames(cls, l))
            return l;
        ...
    }
    
    private static boolean fillDefautsProviderNames(Class<?> cls, List<String> l) {
        if (cls == org.apache.xmlgraphics.image.loader.spi.ImagePreloader.class) {
            l.add("org.apache.xmlgraphics.image.loader.impl.PreloaderTIFF");
            l.add("org.apache.xmlgraphics.image.loader.impl.PreloaderGIF");
            l.add("org.apache.xmlgraphics.image.loader.impl.PreloaderJPEG");
            l.add("org.apache.xmlgraphics.image.loader.impl.PreloaderBMP");
            l.add("org.apache.xmlgraphics.image.loader.impl.PreloaderEMF");
            l.add("org.apache.xmlgraphics.image.loader.impl.PreloaderEPS");
            l.add("org.apache.xmlgraphics.image.loader.impl.imageio.PreloaderImageIO");
            return true;
        }
        return false;
    }
    
  4. Delete function displayImageInfo(ImageInfo info) in org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.

I've prepared repositories with changes: ae-awt, ae-xmlgraphics-commons, docx4j-android.

You can find compiled libs here: docx4j_images_prepared_libs.zip

Enjoy!