POI向word2007文件插入图片时的Bug问题

最近项目中需要生成word文件,于是找到了Apache的POI开源项目,当前版本是3.9;

当然除了POI还有其它的选择,比如java2word、jcob等,但我都没用过,好像只能在windows上运行,于是决定还是使用POI。

下载了最新版本研究半天,感觉POI和其它的Apache项目相比在文档方面要逊色很多,POI的文档极其不全,甚至连Java API docs里都没有什么描述信息,类和方法都光秃秃的,很多东西都要去猜,很是郁闷...

幸好网上有不少博客,磕磕绊绊也能大体了解如何使用。


问题描述


由于项目暂时只考虑word2007及以上版本,所以我主要关注了XWPF部分。

在向生成的word文档中插入图片时遇到了如下问题:

1、使用XWPFRun类的addPicture方法插入图片时,生成的docx文件无法打开,word报如下问题:

2、使用XWPFDocument类的方法addPictureData插入图片时虽然不报异常,word也能打开生成的文件,但图片却无法显示出来,如果将word文件用解压缩文件打开,也能看到里面包含的图片文件,但在word中无法正常显示。


问题原因


于是百度Google了一番,发现遇到此问题的人还真不少,但少有人找到正确的解决方法,而且这是POI自身的Bug,详细情况请看这里:

https://issues.apache.org/bugzilla/show_bug.cgi?id=49765


这是老外程序员向Apache提出的Bug,提出时间 2010年,奇怪的是现在都2013年了,POI也发展到3.9版本了,问题依然没有解决(至少知道3.7和3.8是没有解决,如果3.9解决了,那就是我哪地方没做对;但我把3.9版本官方提供的例子运行起来,问题依旧,想必3.9也是存在该问题),不知道是什么原因。

虽然POI没有解决,但在下面的回复中还是有人找到了原因并提供了解决方案。

在回复的第14楼,有人这样回复

https://issues.apache.org/bugzilla/show_bug.cgi?id=49765#c14

本人英文不够好,勉强能看懂一点儿。就是说在\word\document.xml文件中上面图片中的那一大串XML内容没有被正确地生成,而图片本身被添加到正确的位置\word\media\xxxx.xxx,而且引用关系也正确添加。

word2007以后文件的默认存储格式不再是二进制文件格式,而是Microsoft Office Word XML格式(Word XML格式)。这种格式基于开放打包约定(Open Packaging Conventions),Microsoft Office 97到Microsoft Office 2003中使用的二进制文件格式仍然可以作为一种保存格式来使用,但是它不是保存新文档时的默认文档。

那么,上面的问题应该就是在插入图片时部分XML没有正确生成(\word\document.xml中,见上述图片)。

而且这位高手在第15楼也给出了变通方案:就是自定义一个XWPFDocument来完成缺陷XML内容的添加。

https://issues.apache.org/bugzilla/show_bug.cgi?id=49765#c15


解决方案


自定义XWPFDocument代码如下:

package com.zyh.sample.poi;

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;

import java.io.IOException;
import java.io.InputStream;

public class CustomXWPFDocument extends XWPFDocument {
	public CustomXWPFDocument() {
		super();
	}
	
	public CustomXWPFDocument(OPCPackage opcPackage) throws IOException {
		super(opcPackage);
	}
	
    public CustomXWPFDocument(InputStream in) throws IOException {
        super(in);
    }

    public void createPicture(String blipId,int id, int width, int height) {
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        //String blipId = getAllPictures().get(id).getPackageRelationship().getId();

        
        CTInline inline = createParagraph().createRun().getCTR().addNewDrawing().addNewInline();

        String picXml = "" +
                "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
                "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "         <pic:nvPicPr>" +
                "            <pic:cNvPr id=\"" + id + "\" name=\"Generated\"/>" +
                "            <pic:cNvPicPr/>" +
                "         </pic:nvPicPr>" +
                "         <pic:blipFill>" +
                "            <a:blip r:embed=\"" + blipId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" +
                "            <a:stretch>" +
                "               <a:fillRect/>" +
                "            </a:stretch>" +
                "         </pic:blipFill>" +
                "         <pic:spPr>" +
                "            <a:xfrm>" +
                "               <a:off x=\"0\" y=\"0\"/>" +
                "               <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" +
                "            </a:xfrm>" +
                "            <a:prstGeom prst=\"rect\">" +
                "               <a:avLst/>" +
                "            </a:prstGeom>" +
                "         </pic:spPr>" +
                "      </pic:pic>" +
                "   </a:graphicData>" +
                "</a:graphic>";

        //CTGraphicalObjectData graphicData = inline.addNewGraphic().addNewGraphicData();
        XmlToken xmlToken = null;
        try {
            xmlToken = XmlToken.Factory.parse(picXml);
        } catch(XmlException xe) {
            xe.printStackTrace();
        }
        inline.set(xmlToken);
        //graphicData.set(xmlToken);

        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("Picture " + id);
        docPr.setDescr("Generated");
    }
}

使用也很简单,贴一个使用的小例子:

public static void main(String[] args) {
	CustomXWPFDocument document = new CustomXWPFDocument();
	try {
		String picId = document.addPictureData(new FileInputStream("E:/20130325133325.png"), XWPFDocument.PICTURE_TYPE_PNG);
		document.createPicture(picId, document.getNextPicNameNumber(XWPFDocument.PICTURE_TYPE_PNG), 200, 150);
		
		FileOutputStream fos = new FileOutputStream(new File("D:\\updatedTemplate.docx"));
		document.write(fos);
		fos.close();
	} catch (InvalidFormatException e) {
		e.printStackTrace();
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

注:

1、相比较而言,国外的技术社区更活跃,还是应该多用Google(可惜有时无法访问,天朝!!!)。

2、在stackoverflow上相关帖子中见到docx4j项目,主要是针对word2007及以上(xslx和pptx也支持),有空要试一下。


  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
### 回答1: Java POI Word可以通过以下步骤插入图片: 1. 创建XWPFRun对象 2. 使用XWPFRun对象的addPicture方法插入图片 3. 设置图片的大小和位置 4. 保存Word文档 具体实现可以参考以下代码: ``` // 创建XWPFRun对象 XWPFRun run = paragraph.createRun(); // 插入图片 String imgFile = "C:/image.jpg"; int imgWidth = 300; int imgHeight = 200; try (FileInputStream fis = new FileInputStream(imgFile)) { run.addPicture(fis, XWPFDocument.PICTURE_TYPE_JPEG, imgFile, Units.toEMU(imgWidth), Units.toEMU(imgHeight)); } // 设置图片位置 run.setTextPosition(20); // 保存Word文档 FileOutputStream fos = new FileOutputStream("C:/test.docx"); doc.write(fos); fos.close(); ``` ### 回答2: Java POI是一种用于读取、创建和操作Microsoft Office文档的Java API。使用Java POI库可以轻松地从Java应用程序中创建Word文档并且将图片插入到其中。 要插入图片,首先需要添加POI库的依赖。有两种方法可以做到这一点——手动下载POI库或使用Maven。 接下来,可以通过如下代码来插入图片: ``` import java.io.FileInputStream; import java.io.FileOutputStream; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFPicture; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.xmlbeans.XmlCursor; public class InsertImageInWord { public static void main(String[] args) throws Exception { // 创建新的文档对象 XWPFDocument document = new XWPFDocument(); // 创建一个段落 XWPFParagraph paragraph = document.createParagraph(); // 创建一个文本-run XWPFRun run = paragraph.createRun(); // 将图片插入到段落中 String imageFilePath = "image.jpg"; int imageType = XWPFDocument.PICTURE_TYPE_JPEG; run.addPicture(new FileInputStream(imageFilePath), imageType, "image.jpg", 500, 500); // 保存文档到指定的位置 FileOutputStream outStream = new FileOutputStream("doc.docx"); document.write(outStream); outStream.close(); } } ``` 在上面的代码中,首先创建了一个新的XWPFDocument对象,然后创建了一个XWPFParagraph对象和一个XWPFRun对象。接下来,使用XWPFRun对象的addPicture()方法来插入图片。这个方法需要传递图片文件的输入流、图片文件类型、图片文件名称以及图片的宽度和高度。 然后,使用XWPFDocument对象的write()方法将文档写入到文件中。 总的来说,使用Java POI插入图片Word文档中非常容易。只需要创建一个新的文档对象,创建一个或多个段落,然后使用addPicture()方法将图片插入到段落中即可。 ### 回答3: POI是一个非常流行的Java API,它允许我们创建,修改和读取Microsoft Office格式文档,如Word(docx),Excel(xlsx)和PowerPoint(pptx)。在Word文档中插入图片是常见的需求,它能够提升文档的可读性和吸引力。POI提供了几种方法来实现这个目标。 第一种方式是使用XWPFParagraph实例的createRun()方法并传递一个InputStream参数,其中InputStream引用了图像文件。以下是一个简单的例子: ```java XWPFDocument doc = new XWPFDocument(); XWPFParagraph p = doc.createParagraph(); XWPFRun run = p.createRun(); InputStream image = new FileInputStream(new File("C:\\image.png")); run.addPicture(image, Document.PICTURE_TYPE_PNG, "image.png", Units.toEMU(400), Units.toEMU(300)); doc.write(new FileOutputStream(new File("C:\\image.docx"))); ``` 在上面的代码中,我们创建了一个XWPFDocument的实例,然后创建了一个XWPFParagraph的实例以及一个XWPFRun的实例,并使用addPicture()方法将图像文件添加到文档中。addPicture()方法的最后两个参数是图片的宽度和高度,我们可以使用Units类将它们从英寸转换为EMU(英制单位)。最后,我们将文档写入文件。 第二种方法是使用XWPFDocument类的createPicture()方法,该方法有多个重载,并且可以接受更复杂的图像设置。以下是使用createPicture()方法将图像插入Word文档的示例: ```java XWPFDocument doc = new XWPFDocument(); XWPFParagraph p = doc.createParagraph(); String imgFile = "C:\\image.png"; byte[] imgBytes = Files.readAllBytes(Paths.get(imgFile)); int imgFormat = XWPFDocument.PICTURE_TYPE_PNG; XWPFPicture picture = p.createPicture(doc.getNextPicNameNumber(), imgFormat, imgFile, Units.toEMU(400), Units.toEMU(300)); doc.write(new FileOutputStream(new File("C:\\image.docx"))); ``` 在上面的代码中,我们使用Files类和Paths类读取图像文件,并将其转换为byte数组。然后我们使用createPicture()方法创建XWPFPicture实例,并将图像文件添加到文档中。在createPicture()方法的最后两个参数中,我们使用Units类将图片的宽度和高度从英寸转换为EMU。 无论使用哪种方法,都可以在POI中轻松插入图像到Word文档中。如果需要更多的控制,可以参考POI的官方文档和API文档。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值