Java 实现将Word 转换成markdown

日常的开发中,需要将word 等各类文章信息转换成格式化语言,因此需要使用各类语言将word 转换成Markdown

1、引入 jar包

  <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>

2、使用以下代码进行编写

package com.nd.ai.test.service.utils;

import com.alibaba.fastjson.JSONObject;
import com.nd.ai.test.service.dto.apitest.FileMarkdownDTO;
import org.apache.commons.compress.utils.Lists;
import org.apache.poi.xwpf.usermodel.*;

import java.io.*;
import java.util.List;
import java.util.UUID;

/**
 * @ClassName WordToMarkdownConverter
 * @Author Administrator
 */
public class WordToMarkdownConverter {


    public static FileMarkdownDTO convertWordToMarkdown(String wordPath) throws IOException {

        List<String> imagePathList = Lists.newArrayList();

        FileMarkdownDTO dto = new FileMarkdownDTO();


        try  {
            File file = new File(wordPath);
            String uuid = UUID.randomUUID().toString();
            String outputFilePath = file.getParent() + File.separator + uuid + ".md";
            File outputFile = new File(outputFilePath);
            FileInputStream fis = new FileInputStream(file); // Corrected to read the input file
            XWPFDocument document = new XWPFDocument(fis);
            FileWriter writer = new FileWriter(outputFile);

            dto.setMdPath(outputFilePath);

            String imageDir = file.getParent() + File.separator + uuid + "-images";
            new File(imageDir).mkdirs(); // Create image directory

            for (IBodyElement element : document.getBodyElements()) {
                if (element instanceof XWPFParagraph) {
                    processParagraph((XWPFParagraph) element, writer, imageDir, imagePathList);
                } else if (element instanceof XWPFTable) {
                    processTable((XWPFTable) element, writer, imageDir, imagePathList);
                }
            }

            writer.close();
            // 增加读取mardkown 文件内容
            // Read the content of the generated Markdown file
            StringBuilder markdownContent = new StringBuilder();
            try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) {
                String line;
                while ((line = br.readLine()) != null) {
                    markdownContent.append(line).append("\n");
                }
            }

            dto.setMarkdownContent(markdownContent.toString());
            dto.setParserStatus("success");
        } catch (IOException e){
            dto.setParserStatus("error");
            throw new IOException(e);
        }
        dto.setFileImagePathList(imagePathList);
        return dto;
    }

    private static void processParagraph(XWPFParagraph paragraph, FileWriter writer, String imageDir,List<String> imageNamePath) throws IOException {
        String content = processParagraphContent(paragraph, imageDir,imageNamePath);
        if (content.isEmpty()) return;

        // 处理标题和列表样式
        String style = paragraph.getStyle();
        if (style != null && style.startsWith("Heading")) {
            int level = Math.min(Character.getNumericValue(style.charAt(7)), 6);
            StringBuilder heading = new StringBuilder();
            for (int i = 0; i < level; i++) {
                heading.append("#");
            }
            heading.append(" ").append(content).append("\n\n");
            writer.write(heading.toString());
//            writer.write("#".repeat(level) + " " + content + "\n\n");
        } else if (isListParagraph(paragraph)) {
            String listMark = getListMark(paragraph);
            writer.write("* " + listMark + " " + content + "\n");
        } else {
            writer.write(content + "\n\n");
        }
    }

    private static String getListMark(XWPFParagraph para) {
        int indentLevel = para.getNumIlvl() != null ? para.getNumIlvl().intValue() : 0;
        String numFmt = para.getNumFmt(); // 获取列表编号格式

        // 处理有序列表
        if ("decimal".equals(numFmt) || "upperRoman".equals(numFmt)) {
            StringBuilder prefixBuilder = new StringBuilder();
            for (int i = 0; i < indentLevel * 4; i++) {
                prefixBuilder.append(" ");
            }
            String prefix = prefixBuilder.toString();



            return prefix + ".";
        }
        // 处理无序列表
        else {
            String bullet;
            switch (para.getNumFmt()) {
                case "bullet":
                    bullet = "•"; // 实心圆点
                    break;
                default:
                    bullet = "-"; // 默认用减号
                    break;
            }
            StringBuilder prefixBuilder = new StringBuilder();
            for (int i = 0; i < indentLevel * 4; i++) {
                prefixBuilder.append(" ");
            }
            return prefixBuilder.toString() + bullet;
        }
    }

    private static boolean isListParagraph(XWPFParagraph paragraph) {
        return isOrderedList(paragraph) || isUnorderedList(paragraph); // 如果没有找到对应的样式,则不可能是列表段落
    }

    private static boolean isOrderedList(XWPFParagraph paragraph) {
        if (paragraph.getNumFmt() != null) {
            String numFmt = paragraph.getNumFmt();
            return "decimal".equals(numFmt) || "upperRoman".equals(numFmt) || "lowerRoman".equals(numFmt) ||
                    "upperLetter".equals(numFmt) || "lowerLetter".equals(numFmt);
        }
        return false;
    }

    private static boolean isUnorderedList(XWPFParagraph paragraph) {
        if (paragraph.getNumFmt() != null) {
            String numFmt = paragraph.getNumFmt();
            return "bullet".equals(numFmt);
        }
        return false;
    }

    private static void processTable(XWPFTable table, FileWriter writer, String imageDir,List<String> imageNamePath) throws IOException {
        StringBuilder mdTable = new StringBuilder();
        List<XWPFTableRow> rows = table.getRows();

        for (int i = 0; i < rows.size(); i++) {
            XWPFTableRow row = rows.get(i);
            mdTable.append("|");

            // 处理每个单元格
            for (XWPFTableCell cell : row.getTableCells()) {
                StringBuilder cellContent = new StringBuilder();
                // 处理单元格内的段落
                for (XWPFParagraph para : cell.getParagraphs()) {
                    cellContent.append(processParagraphContent(para, imageDir,imageNamePath).replace("\n", "<br>"));
                }
                mdTable.append(cellContent.toString().trim()).append("|");
            }
            mdTable.append("\n");


            // 添加表头分隔线
            if (i == 0) {
                mdTable.append("|");
                for (int j = 0; j < row.getTableCells().size(); j++) {
                    mdTable.append(" --- |");
                }
                mdTable.append("\n");
            }
        }
        writer.write(mdTable + "\n\n");
    }

    private static String processParagraphContent(XWPFParagraph paragraph, String imageDir,List<String> imageNamePath) throws IOException {
        StringBuilder sb = new StringBuilder();

        for (XWPFRun run : paragraph.getRuns()) {
            // 处理图片
            for (XWPFPicture picture : run.getEmbeddedPictures()) {
                sb.append(saveImage(picture, imageDir,imageNamePath)).append(" ");
            }
            // 处理文本样式
            String text = run.getText(0);
            if (text == null) continue;

            text = applyTextStyles(run, text);
            sb.append(text);
        }

        String content = sb.toString().trim();
        // 处理有序列表和无序列表
        if (isListParagraph(paragraph)) {
            String listMark = getListMark(paragraph);
            content ="* " + listMark + " " + content;
        }
        return content;
    }

    private static String applyTextStyles(XWPFRun run, String text) {
        if (run.isBold()) text = "**" + text + "**";
        if (run.isItalic()) text = "*" + text + "*";
        if (run.getUnderline() != UnderlinePatterns.NONE) text = "__" + text + "__";
        return text;
    }

    private static String saveImage(XWPFPicture picture, String imageDir,List<String> imageNamePath) throws IOException {
        XWPFPictureData picData = picture.getPictureData();
        String fileName = "img_" + UUID.randomUUID() + "." + picData.suggestFileExtension();
        File output = new File(imageDir, fileName);
        imageNamePath.add(output.getPath());

        try (FileOutputStream fos = new FileOutputStream(output)) {
            fos.write(picData.getData());
        }
        return "![" + fileName + "](" + imageDir + "/" + fileName + ")";
    }


    public static void main(String[] args) throws Exception {
        System.out.println(JSONObject.toJSONString( convertWordToMarkdown("word path")));
    }
}

获得信息

{
"fileImagePathList":["文件中图片路径1","文件中图片路径2"],
"markdownContent": "markdwon 信息",
"mdPath": "markdown文件地址"
}

运行上方的程序将会得到
1、解析文件中所有图片信息,保存到下方的地址
2、将word 文档转换成markdown
3、获取markdown 文件

<think>好的,我现在需要帮用户找到如何使用JavaMarkdown转换为Word,并保持格式的方法或库。首先,我得确认用户的需求是什么。用户希望将Markdown文件转换成Word文档,同时保留原有的格式,比如标题、列表、代码块等。这可能涉及到解析Markdown语法,并将其转换为Word支持的格式,比如.docx文件的结构。 接下来,我得考虑Java中有哪些库可以实现这个功能。常见的处理Markdown的库有CommonMark、Flexmark,这些库可以将Markdown转换为HTML或者AST(抽象语法树)。但是用户需要的是转换成Word文档,所以可能需要进一步处理。而处理Word文档的库,常用的有Apache POI,它支持创建和修改.docx文件。不过,直接转换可能需要中间步骤,比如先将Markdown转为HTML,再用Apache POI将HTML转为Word,或者使用其他转换工具。 另外,是否有现成的库可以直接将Markdown转换为Word呢?可能需要搜索一下是否有开源项目或者第三方库。比如,Pandoc是一个强大的文档转换工具,支持多种格式转换,包括MarkdownWord。不过Pandoc本身是用Haskell写的,用户可能需要通过Java调用命令行接口来使用它。这可能是一个解决方案,但需要依赖外部工具。 另一个可能性是使用Aspose.Words,这是一个商业库,支持多种文档格式的转换,包括MarkdownWord。不过它是商业软件,可能需要付费许可证。如果用户的项目允许使用商业库,这可能是一个可靠的选择。 然后,我需要验证这些方法的可行性。例如,使用CommonMark将Markdown解析为HTML,再利用Apache POI将HTML转换为Word。但是Apache POI对HTML的支持有限,可能无法完美保留格式。这时候可能需要借助其他库,比如flyingsaucer或者Thymeleaf来将HTML转换为PDF,再转成Word,但这样步骤更多,可能影响效果。 另外,是否有专门的转换库?比如,查找GitHub上的开源项目,是否有Java库直接处理MarkdownWord的转换。可能需要搜索关键词如“java markdown to docx library”。例如,可能有库如md2docx,但不确定其维护状态和功能完整性。 还需要考虑格式保留的问题。比如,Markdown中的代码块、表格、图片等元素如何在Word中正确呈现。可能需要详细的样式设置,比如字体、缩进、列表样式等。这时候使用Apache POI手动设置样式可能更灵活,但代码量会更大。 总结可能的解决方案: 1. 使用CommonMark/Flexmark解析Markdown生成AST,再使用Apache POI根据AST生成.docx文件,手动处理样式。 2. 使用Pandoc通过Java调用命令行工具进行转换。 3. 使用Aspose.Words商业库直接转换。 4. 将Markdown先转为HTML,再转为Word,可能使用中间转换工具。 接下来需要评估每种方案的优缺点。方案1需要自己处理转换逻辑,较为复杂,但灵活;方案2依赖外部工具,需要安装Pandoc,可能跨平台问题;方案3成本较高;方案4可能转换过程中丢失格式。 用户可能需要具体的代码示例,所以需要选择一个可行的方案。例如,如果使用Flexmark和Apache POI,可以分步骤解析Markdown,然后生成对应的Word元素。或者使用Aspose.Words如果有许可证的话。 另外,用户提到的引用内容是关于proto3 JSON的选项,可能与当前问题无关,但需要确保回答不引用无关内容。不过用户提醒引用仅供参考,应该聚焦于当前问题。 最终,可能需要推荐使用Flexmark解析Markdown,结合Apache POI生成Word,并提供示例代码。或者推荐Aspose.Words如果有条件的话。此外,可能提到调用Pandoc的方法作为备选方案。</think>在Java实现MarkdownWord文档的格式保留转换,推荐以下两种主要方法: ### 一、使用Flexmark + Apache POI组合方案 1. **Markdown解析**:通过Flexmark库将Markdown转换为HTML或自定义AST 2. **格式映射**:将Markdown元素映射到Word样式 3. **文档生成**:使用Apache POI创建带格式的.docx文件 ```java // 示例:将Markdown标题转换为Word样式 XWPFDocument doc = new XWPFDocument(); XWPFParagraph p = doc.createParagraph(); p.setStyle("Heading1"); XWPFRun run = p.createRun(); run.setText("这是标题"); ``` ### 二、专业文档转换工具 1. **Aspose.Words(商业库)**: ```java Document doc = new Document("input.md"); doc.save("output.docx", SaveFormat.DOCX); ``` 2. **Pandoc命令行调用**: ```java Process process = Runtime.getRuntime().exec("pandoc -s input.md -o output.docx"); ``` ### 三、格式保留关键点 1. **样式映射表**需包含: - 标题层级对应Word样式 - 代码块使用等宽字体 - 列表保持缩进 2. **图片处理**需注意路径解析和缩放 3. **表格转换**需处理边框和单元格合并
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程的大耳朵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值