poi-tl icon indicating copy to clipboard operation
poi-tl copied to clipboard

请教一下,是否有办法实现生成目录

Open onlylivi opened this issue 6 years ago • 36 comments

需求的报告模板中大概包括标题、目录、前言、章节(根据模板动态生成的标题+文字+表格+图片),其中目录需要根据章节的实际情况动态生成。在网上没有找到什么好的解决方案,请教一下实现思路,非常感谢。

onlylivi avatar Oct 25 '18 08:10 onlylivi

#13

没有研究过目录,如果用poi没有找到合适的API,可以找找一些其它处理word的jar包。

Sayi avatar Oct 25 '18 09:10 Sayi

我之前也是有生成目录的需求,但poi好像没有什么好用的API。 后来只能用poi ti 生成完文档后,再用Jacob去生成目录。

issac21 avatar Oct 26 '18 05:10 issac21

我之前也是有生成目录的需求,但poi好像没有什么好用的API。 后来只能用poi ti 生成完文档后,再用Jacob去生成目录。

你好,可以分享一下代码示例吗,谢谢

onlylivi avatar Oct 26 '18 05:10 onlylivi

我之前也是有生成目录的需求,但poi好像没有什么好用的API。 后来只能用poi ti 生成完文档后,再用Jacob去生成目录。

你好,可以分享一下代码示例吗,谢谢

你可以去搜搜下docx4j这个库去生成目录。网上有很多demo

ucstone avatar Oct 27 '18 13:10 ucstone

@onlylivi https://gist.github.com/issac21/c12885c032fc95efb43ce4d190eb1610 private void wordTemplateHandler(File file) { JacobWordUtil wordUtil = new JacobWordUtil(false); try { wordUtil.openDocument(file.getAbsolutePath()); /* 更新目录 */ wordUtil.updateTablesOfContents(); } catch (Exception e) { log.error("更新目录失败", e); throw new RuntimeException(file.getName() + ",文件更新目录失败:" + e.getMessage()); }finally { wordUtil.close(); } } word文档中添加一个标记 :{目录} 那么就会在那个地方生成目录。(记得添加jacob的dll文件,因为jacob是调用本地的Word程序,其实就是想当于打开word,然后点击程序上的“生成目录”)

反正感觉这样比较麻烦,一直在找更好的方法

issac21 avatar Oct 29 '18 06:10 issac21

我之前也是有生成目录的需求,但poi好像没有什么好用的API。 后来只能用poi ti 生成完文档后,再用Jacob去生成目录。

你好,可以分享一下代码示例吗,谢谢

你可以去搜搜下docx4j这个库去生成目录。网上有很多demo

好的,谢谢

onlylivi avatar Oct 29 '18 06:10 onlylivi

@onlylivi https://gist.github.com/issac21/c12885c032fc95efb43ce4d190eb1610 private void wordTemplateHandler(File file) { JacobWordUtil wordUtil = new JacobWordUtil(false); try { wordUtil.openDocument(file.getAbsolutePath()); /* 更新目录 */ wordUtil.updateTablesOfContents(); } catch (Exception e) { log.error("更新目录失败", e); throw new RuntimeException(file.getName() + ",文件更新目录失败:" + e.getMessage()); }finally { wordUtil.close(); } } word文档中添加一个标记 :{目录} 那么就会在那个地方生成目录。(记得添加jacob的dll文件,因为jacob是调用本地的Word程序,其实就是想当于打开word,然后点击程序上的“生成目录”)

反正感觉这样比较麻烦,一直在找更好的方法

我查了一下,jacob好像只能在windows平台用,真是巧了,我们的应用要部署在linux上,哎 多谢兄弟了哈

onlylivi avatar Oct 29 '18 06:10 onlylivi

我之前也是有生成目录的需求,但poi好像没有什么好用的API。 后来只能用poi ti 生成完文档后,再用Jacob去生成目录。

你好~请问你最后文档生成成功了吗? 生成的目录要根据标题来生成,想请问你一下标题是怎么生成的呢?标题可以用poi生成吗?

AnnaHuangPro avatar Nov 06 '18 12:11 AnnaHuangPro

@AnnaHuangPro 我的是本地有安装word的,我看了word自带的标题名称是“标题 1”之类的。所以代码中我直接使用了 XWPFParagraph titleParagraph = doc.insertNewParagraph(run); titleParagraph.setStyle("标题 1"); XWPFRun titleRun = titleParagraph.createRun(); titleRun.setFontSize(14); titleRun.setFontFamily("宋体"); titleRun.setText(title); 如果不的话,先把文档生成出来,再把想要的标题手动在word里设置保存好,然后把word转成xml,再去看xml中的属性是如何定义的。 很多样式的东西我不知道poi是如果去设置属性的,我就会看xml中的属性,对照着设置就可以了

issac21 avatar Nov 07 '18 01:11 issac21

@AnnaHuangPro 我的是本地有安装word的,我看了word自带的标题名称是“标题 1”之类的。所以代码中我直接使用了 XWPFParagraph titleParagraph = doc.insertNewParagraph(run); titleParagraph.setStyle("标题 1"); XWPFRun titleRun = titleParagraph.createRun(); titleRun.setFontSize(14); titleRun.setFontFamily("宋体"); titleRun.setText(title); 如果不的话,先把文档生成出来,再把想要的标题手动在word里设置保存好,然后把word转成xml,再去看xml中的属性是如何定义的。 很多样式的东西我不知道poi是如果去设置属性的,我就会看xml中的属性,对照着设置就可以了

好的 谢谢你哟~ 还想请问你一下 你用jacob生成目录成功了吗?

AnnaHuangPro avatar Nov 07 '18 02:11 AnnaHuangPro

@AnnaHuangPro 使用jacob生成目录,我是成功的。 但我的数据比较多,我有时候生成的数据有1000页,这时候生成目录就会非慢,甚至会导致word假死。

issac21 avatar Nov 07 '18 15:11 issac21

@AnnaHuangPro 使用jacob生成目录,我是成功的。 但我的数据比较多,我有时候生成的数据有1000页,这时候生成目录就会非慢,甚至会导致word假死。

嗯嗯 我生成80多页也是慢得很~ 谢谢你啦~

AnnaHuangPro avatar Nov 08 '18 03:11 AnnaHuangPro

poi有个XWPFDocument.createTOC() 提供了目录生成思路,但是没有对应页码 参看 Apache POI自动生成Word文档(带目录)

xiaobrid avatar Nov 26 '18 07:11 xiaobrid

不知作者大大是否有页码的生成思路

xiaobrid avatar Nov 26 '18 07:11 xiaobrid

@xiaobrid Sorry,暂时没有思路,有时间的确可以增加一个 TOCRenderPolicy这样的目录插件。

Sayi avatar Nov 27 '18 06:11 Sayi

可以试试这个方法:https://my.oschina.net/u/3410302/blog/3048367。apache poi原生实现。

xuanlv886 avatar May 21 '19 03:05 xuanlv886

@xuanlv886 有空可以为poi-tl提供个目录策略的pull request吗:在指定模板位置渲染目录

Sayi avatar May 21 '19 03:05 Sayi

大佬们,有解决目录生成问题吗?在windows上平台上是目录生成比较好实现,但是我现在的需求是需要兼容windows、liunx、wps、office。太难了,目前还没有发现很好的方案。

when-x avatar Dec 09 '19 01:12 when-x

@onlylivi @Sayi @issac21 @ucstone @AnnaHuangPro 有liunx平台生成word目录兼容wps、office的方案吗?万分感谢。

when-x avatar Dec 09 '19 01:12 when-x

jacob生成目录方法 public void updateToc() { // 返回一个TablesOfContents 集合, 该集合代表指定文档中的目录表。 此为只读属性。 Dispatch tablesOfContents = Dispatch.call(doc, "TablesOfContents").toDispatch();// 整个目录区域 Variant tablesOfContent = Dispatch.call(tablesOfContents, "Item", new Variant(1)); /*更新目录,有两个方法:Update 更新域,UpdatePageNumbers 只更新页码/ Dispatch toc = tablesOfContent.toDispatch(); toc.call(toc, "Update"); } 不过只支持windows。

greenetdw avatar Feb 05 '21 02:02 greenetdw

关于TOCRenderPolicy的使用, ChessData data = new ChessData(); data.setMulu("目录"); data.setCatalog("目录"); data.setAuthor("chess"); data.setDate(new Date().toString()); ProjBase projBase = new ProjBase(); XWPFTemplate template = XWPFTemplate.compile(resource).render(data); List<MetaTemplate> elementTemplates = template.getElementTemplates(); ElementTemplate metaTemplate = (ElementTemplate)elementTemplates.get(0); TOCRenderPolicy tocRenderPolicy = new TOCRenderPolicy(); tocRenderPolicy.render( metaTemplate,data,template); template.writeToFile("chess_example.docx"); 目录会占用掉你在word文档中的第一个{{}}标签,用来固定位置

zxc678345 avatar May 20 '21 07:05 zxc678345

关于TOCRenderPolicy的使用, ChessData data = new ChessData(); data.setMulu("目录"); data.setCatalog("目录"); data.setAuthor("chess"); data.setDate(new Date().toString()); ProjBase projBase = new ProjBase(); XWPFTemplate template = XWPFTemplate.compile(resource).render(data); List<MetaTemplate> elementTemplates = template.getElementTemplates(); ElementTemplate metaTemplate = (ElementTemplate)elementTemplates.get(0); TOCRenderPolicy tocRenderPolicy = new TOCRenderPolicy(); tocRenderPolicy.render( metaTemplate,data,template); template.writeToFile("chess_example.docx"); 目录会占用掉你在word文档中的第一个{{}}标签,用来固定位置

如何生成页码?

eliasyaoyc avatar Sep 27 '21 09:09 eliasyaoyc

关于TOCRenderPolicy的使用, ChessData data = new ChessData(); data.setMulu("目录"); data.setCatalog("目录"); data.setAuthor("chess"); data.setDate(new Date().toString()); ProjBase projBase = new ProjBase(); XWPFTemplate template = XWPFTemplate.compile(resource).render(data); List<MetaTemplate> elementTemplates = template.getElementTemplates(); ElementTemplate metaTemplate = (ElementTemplate)elementTemplates.get(0); TOCRenderPolicy tocRenderPolicy = new TOCRenderPolicy(); tocRenderPolicy.render( metaTemplate,data,template); template.writeToFile("chess_example.docx"); 目录会占用掉你在word文档中的第一个{{}}标签,用来固定位置

试了一下,报异常: java.lang.ClassCastException: class com.deepoove.poi.template.ChartTemplate cannot be cast to class com.deepoove.poi.template.run.RunTemplate (com.deepoove.poi.template.ChartTemplate and com.deepoove.poi.template.run.RunTemplate are in unnamed module of loader 'app')

gzocs avatar Oct 12 '21 07:10 gzocs

问下按照代码中的示例:

 XWPFDocument doc = new XWPFDocument(new FileInputStream("E:\\workspace\\JavaProj\\poi-tl\\poi-tl\\src\\test\\resources\\animal\\test2.test"));

        XWPFStyles styles = doc.getStyles();
        System.out.println(styles);

        XWPFParagraph p = doc.createParagraph();
        XWPFRun run = p.createRun();
        run.setText("Heading 1");
        p.getCTP().addNewPPr();
        p.getCTP().getPPr().addNewPStyle();
        p.setStyle(HEADING1);

        XWPFParagraph tocPara = doc.createParagraph();
        CTP ctP = tocPara.getCTP();
        CTSimpleField toc = ctP.addNewFldSimple();
        toc.setInstr("TOC \\h");
        toc.setDirty(XWPFOnOff.ON);

        System.out.println(doc.isEnforcedUpdateFields());

         doc.enforceUpdateFields();

         doc.createTOC();

        doc.write(new FileOutputStream("CreateWordBookmark.docx"));
        doc.close();

并没有生成目录,是不是目录插件目前还不能用?

DavidDXY avatar Dec 07 '21 09:12 DavidDXY

目录渲染可以借助word多级列表与标题实现,我们可以在word模板内定义好多级列表样式,类似这样: image 并将其链接至标题样式上: image 定义好目录信息: image 当我们由模板渲染出不同的章节时,确保最后调用下 doc.enforceUpdateFields(); 当文档打开时会提示更新目录,相当于右键目录进行了“更新域”操作,之后目录和页码便会根据对应的标题渲染出来 ef17fa49934fbbced80f4a7ab5d9344

Scarange avatar Mar 17 '23 07:03 Scarange

哥,有demo实例代码吗,生成目录

zlp8013 avatar Jul 14 '23 03:07 zlp8013

目录渲染可以借助word多级列表与标题实现,我们可以在word模板内定义好多级列表样式,类似这样: image 并将其链接至标题样式上: image 定义好目录信息: image 当我们由模板渲染出不同的章节时,确保最后调用下 doc.enforceUpdateFields(); 当文档打开时会提示更新目录,相当于右键目录进行了“更新域”操作,之后目录和页码便会根据对应的标题渲染出来 ef17fa49934fbbced80f4a7ab5d9344

xd,有没有办法可以避免每次打开word都显示需要更新作用域这个问题啊QAQ

HYliangkai avatar Aug 07 '23 03:08 HYliangkai

#901

Sayi avatar Aug 07 '23 03:08 Sayi

5年了,这个目录还是无解吗?

huanlirui avatar Sep 01 '23 02:09 huanlirui

暂时还没想到解决办法,doc.enforceUpdateFields();方法是在setting.xml中添加<w:updateFields w:val="true"/> 弹窗是word弹的。 可以看ECMA-376-1:2016 这个位置 17.15.1.90 updateFields (Automatically Recalculate Fields on Open)

LiukerSun avatar Sep 01 '23 09:09 LiukerSun