如何使用POI将一个.docx文件中的内容复制到另一个.docx文件中,而不会丢失格式?

5
假设我有两个.docx文件,input.docx和output.docx。我需要从input.docx中选择一些内容并将其复制到output.docx中。newdoc在控制台显示其内容,似乎是正确的,但我在output.docx中没有获得任何内容,除了空白行。有谁可以提供建议吗?
InputStream is = new FileInputStream("D:\\input.docx"); 
XWPFDocument doc = new XWPFDocument(is);

List<XWPFParagraph> paras = doc.getParagraphs();  
List<XWPFRun> runs;
XWPFDocument newdoc = new XWPFDocument();                                     
for (XWPFParagraph para : paras) {  
            runs = para.getRuns();      
            if(!para.isEmpty())
            {
                XWPFParagraph newpara = newdoc.createParagraph(); 
                XWPFRun newrun = newpara.createRun();
                for (int i=0; i<runs.size(); i++) {                       
                    newrun=runs.get(i);
                    newpara.addRun(newrun);
                }
            }
        }


        List<XWPFParagraph> newparas = newdoc.getParagraphs(); 
        for (XWPFParagraph para1 : newparas) {  
            System.out.println(para1.getParagraphText());
        }// in the console, I have the correct information

        FileOutputStream fos = new FileOutputStream(new File("D:\\output.docx"));
        newdoc.write(fos);
        fos.flush();
        fos.close();

请注意,在您的问题中提到了“output.doc”而不是“.docx”,这可能只是一个笔误。 - DenisFLASH
1个回答

6
我稍微修改了您的代码,它可以在不改变文本格式的情况下复制文本。
public static void main(String[] args) {
    try {
        InputStream is = new FileInputStream("Japan.docx"); 
        XWPFDocument doc = new XWPFDocument(is);

        List<XWPFParagraph> paras = doc.getParagraphs();  

        XWPFDocument newdoc = new XWPFDocument();                                     
        for (XWPFParagraph para : paras) {  

            if (!para.getParagraphText().isEmpty()) {       
                XWPFParagraph newpara = newdoc.createParagraph();
                copyAllRunsToAnotherParagraph(para, newpara);
            }

        }

        FileOutputStream fos = new FileOutputStream(new File("newJapan.docx"));
        newdoc.write(fos);
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

// Copy all runs from one paragraph to another, keeping the style unchanged
private static void copyAllRunsToAnotherParagraph(XWPFParagraph oldPar, XWPFParagraph newPar) {
    final int DEFAULT_FONT_SIZE = 10;

    for (XWPFRun run : oldPar.getRuns()) {  
        String textInRun = run.getText(0);
        if (textInRun == null || textInRun.isEmpty()) {
            continue;
        }

        int fontSize = run.getFontSize();
        System.out.println("run text = '" + textInRun + "' , fontSize = " + fontSize); 

        XWPFRun newRun = newPar.createRun();

        // Copy text
        newRun.setText(textInRun);

        // Apply the same style
        newRun.setFontSize( ( fontSize == -1) ? DEFAULT_FONT_SIZE : run.getFontSize() );    
        newRun.setFontFamily( run.getFontFamily() );
        newRun.setBold( run.isBold() );
        newRun.setItalic( run.isItalic() );
        newRun.setStrike( run.isStrike() );
        newRun.setColor( run.getColor() );
    }   
}

仍然存在一个关于字体大小的小问题。有时POI无法确定文本运行的大小(我将其值写入控制台以跟踪它),并给出-1。当我自己设置字体大小时(例如,我在Word中选择一些段落并手动设置其字体,无论是大小还是字体系列),它能够完美地定义字体大小。但是当处理另一个由POI生成的文本时,它有时会给出-1。因此,我引入了一个默认字体大小(如上例中的10)来在POI给出-1时进行设置。
另一个问题似乎出现在Calibri字体系列中。但在我的测试中,POI默认将其设置为Arial,因此我没有像对于fontSize那样使用默认fontFamily的技巧。
其他字体属性(粗体、斜体等)都工作正常。
可能,所有这些字体问题都是因为在我的测试中文本是从.doc文件复制的缘故。如果您的输入是.doc,请在Word中打开.doc文件,然后“另存为...”并选择.docx格式。然后在您的程序中仅使用XWPFDocument而不是HWPFDocument,我想它就没问题了。

谢谢你的回答。POI并不如我预期的那么好。我想知道是否有其他方法来完成同样的事情。我用一个1.03MB、443页、221803个单词的.docx文件运行了你的代码,出现了3个问题:1.正如你所说,“-1”问题。几乎所有的字体都被识别为“-1”,因此输出结果并不完美,也丢失了数字信息(例如1.、2.、3.等)。我按照你的建议,将doc文件“另存为”为.docx文件,问题仍然存在。 - flyingmouse
  1. 处理该文档需要超过40分钟的时间(我的电脑是Inter-i7-3770,4G RAM)。我有数百个类似的文档,这将太耗时了。
  2. output.docx 无法直接打开。它显示无法打开Office Open XML文档output.docx。文件存在错误。幸运的是,我可以通过将文件恢复到新文件来打开它。
无论如何,非常感谢您的答复。我会继续寻找更好的解决方案。
- flyingmouse
@flyingmouse 谢谢你的绿色标记。但我还不完全满意,因为你仍然有很大的问题。所以让我们试着看看我们能做些什么。哎呀,40分钟?那太长了!我从来没有处理过大文件,这就是为什么我从来没有真正关心时间优化的原因。但现在时机已经成熟了;-) 上面的代码根本不完美,我会尝试进行优化。在这里我需要你的帮助:我想面对与你相同的问题,所以请告诉我你从一开始执行的操作序列:你从网站上取得.doc文件,然后呢? - DenisFLASH
@flyingmouse 关于其他技术,我只听说过Jasper Reports,但我不能确定它是否真的方便应用在这里。POI并不理想,这是真的。但是,也许问题是由于微软自己造成的,因为他们在.doc和.docx格式之间进行了巨大的改变。试一下这个技巧:将任何example.docx重命名为example.zip,然后解压缩它。你会看到一个文件结构,有很多文件,如document.xml、styles.xml等。这就是Microsoft在.docx中存储数据的方式,它与.doc完全不同。这就是为什么POI的HWPF和XWPF几乎不兼容的原因。 - DenisFLASH
@flyingmouse,你的账户很好,但能否直接发送相同的消息到我的StackOverflow个人信息部分中显示的电子邮件呢?我认为这样会更方便。 - DenisFLASH
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接