使用Docx4j转PDF时空格/制表符丢失问题求助
解决方案
1. 调整setSpace("preserve")的执行顺序
VariablePrepare.prepare()会重新处理文档中的文本节点,可能覆盖你设置的空格保留属性。必须在变量准备完成后再设置文本的space属性:
InputStream isNodo = new ByteArrayInputStream(contentNodoTempl); WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(isNodo); // 先执行变量准备,再处理文本节点 VariablePrepare.prepare(wordMLPackage); findAndReplace(wordMLPackage); ByteArrayOutputStream bos = new ByteArrayOutputStream(); Docx4J.toPDF(wordMLPackage, bos); bos.flush(); isNodo.close(); return bos.toByteArray();
2. 配置FO导出参数强制保留空格和制表符
Docx4j依赖Apache FOP进行PDF转换,默认会合并连续空格。需要通过FOSettings配置参数禁用该行为:
ByteArrayOutputStream bos = new ByteArrayOutputStream(); FOSettings foSettings = Docx4J.createFOSettings(); // 配置保留连续空格和制表符 foSettings.getProperties().put("org.apache.fop.areas.inline.space-treatment", "preserve"); foSettings.getProperties().put("org.apache.fop.areas.inline.tab-treatment", "preserve"); // 使用自定义配置导出PDF Docx4J.toPDF(wordMLPackage, bos, foSettings);
3. 完善制表符的处理逻辑
制表符的保留还需要段落设置对应的制表位,否则FOP无法正确解析:
public static void findAndReplace(WordprocessingMLPackage doc){ List<Object> paragraphs = getAllElementFromObject(doc.getMainDocumentPart(), P.class); for(Object par : paragraphs){ P p = (P) par; // 为段落添加制表位(示例:1英寸宽度的左对齐制表位) if (p.getPPr() == null) { p.setPPr(Context.getWmlObjectFactory().createPPr()); } Tabs tabs = Context.getWmlObjectFactory().createTabs(); Tab tab = Context.getWmlObjectFactory().createTab(); tab.setVal(STTabJc.LEFT); tab.setPos(BigInteger.valueOf(1440)); // 1440 twips = 1英寸 tabs.getTab().add(tab); p.getPPr().setTabs(tabs); // 处理文本节点 List<Object> texts = getAllElementFromObject(p, Text.class); for(Object text : texts){ Text t = (Text)text; t.setSpace("preserve"); // 确保制表符被正确识别 t.setValue(t.getValue().replace("\t", "	")); } } }
4. 优化文本节点遍历逻辑
原遍历方法可能因节点类型判断不严谨漏掉部分Text节点,修改为更通用的递归遍历:
public static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) { List<Object> result = new ArrayList<>(); if (obj == null) return result; if (obj instanceof JAXBElement) { obj = ((JAXBElement<?>) obj).getValue(); } if (toSearch.isInstance(obj)) { result.add(obj); } if (obj instanceof ContentAccessor) { for (Object child : ((ContentAccessor) obj).getContent()) { result.addAll(getAllElementFromObject(child, toSearch)); } } return result; }
内容的提问来源于stack exchange,提问作者Guglio
相关产品推荐
相关产品推荐

