山东大学软件学院创新项目实训开发日志——第11周

山东大学软件学院创新项目实训开发日志——第11周

项目名称:文档大师

项目目标:

本项目旨在开发一款基于人工智能的文档审阅工具,解决当前在文档审阅过程中人工检查效率低、遗漏错误和格式不规范等问题。

本周完成任务

一、图文混排结果导出

图文混排是一个通用功能,以下假设用户使用了文本处理功能,且生成的文本已显示在用于展示生成文本的TextView中,如下图所示(为方便演示这里直接进行了字符串填充,非其他功能效果):

在这里插入图片描述

之后点击图文混排按钮可进行图片选择:

在这里插入图片描述

选择并全部处理完成后,程序会在前述的TextView中覆盖原来的内容:

在这里插入图片描述

现在要解决的是将TextView中的内容导出为docx的问题,这里用到了类似第8周编写的保存为DOCX文件算法( https://blog.csdn.net/m0_74934901/article/details/147191118?spm=1001.2014.3001.5502 )。有所不同的是,在遍历并记录每个Span的信息时,将图片的高度设置为文本字号的5倍,并根据宽高比计算宽度。

float textSizePx = tvOutput.getTextSize();
int textHeightEMU = pixelsToEMU((int) textSizePx);
int heightEMU = textHeightEMU * 5;
int widthEMU = (int) ((float) originalWidth / originalHeight * heightEMU);

由于以上算法的实现默认SpannableString中的图片是按照下表顺序排列的,但经过图文混排功能处理后往往并非如此,因此需要将其按照SpanInfo.start进行升序排序。

spanInfoList.sort((o1, o2) -> Integer.compare(o1.start, o2.start));

下面是段落的处理,在前述算法中,所有内容(文本和图片)被写入同一个XWPFParagraph,并且通过文本中的\n字符表示换行,经测试,其在桌面端不会被解析为段落分隔符,因此要实现每一段创建一个XWPFParagraph,修改后代码如下:

1.使用string.split(“\n”)将文本分割为段落数组,每个段落对应一个XWPFParagraph。跟踪文本的全局位置(currentPos),以确保图片插入到正确的段落。

String[] paragraphs = string.split("\n");
int spanIndex = 0;
int currentPos = 0;

2.对于每个段落,首先检查是否有SpanInfo的start位于当前段落范围内。在图片前插入文本,插入图片后创建新Run继续处理后续文本。

for (String paragraphText : paragraphs) {
    XWPFParagraph paragraph = document.createParagraph();
    XWPFRun run = paragraph.createRun();
    int paragraphStart = currentPos;
    int paragraphEnd = currentPos + paragraphText.length();
    //检查是否包含图片
    boolean hasImage = false;
    for (SpanInfo spanInfo : spanInfoList) {
        if (spanInfo.start >= paragraphStart && spanInfo.start < paragraphEnd) {
            hasImage = true;
            break;
        }
    }
    //如果段落包含图片,设置为居中对齐
    if (hasImage) {
        paragraph.setAlignment(ParagraphAlignment.CENTER);
    }
    //处理当前段落中的文本和图片
    while (spanIndex < spanInfoList.size() && spanInfoList.get(spanIndex).start < paragraphEnd) {
        SpanInfo spanInfo = spanInfoList.get(spanIndex);
        //添加图片前的文本
        if (spanInfo.start > currentPos) {
            String textBeforeImage = string.substring(currentPos, spanInfo.start);
            run.setText(textBeforeImage);
            run = paragraph.createRun();
        }
        //添加图片
        try {
            run.addPicture(
                    new ByteArrayInputStream(spanInfo.imageData),
                    XWPFDocument.PICTURE_TYPE_PNG,
                    "image.png",
                    spanInfo.widthEMU,
                    spanInfo.heightEMU
            );
        } catch (Exception e) {
            Toast.makeText(this, "图片写入docx文件失败", Toast.LENGTH_LONG).show();
        }
        currentPos = spanInfo.end;
        spanIndex++;
        run = paragraph.createRun();
    }
    //添加段落后剩余的文本
    if (currentPos < paragraphEnd) {
        run.setText(paragraphText.substring(currentPos - paragraphStart));
    }
    currentPos = paragraphEnd + 1; //加上换行符
}

3.如果文本结束后仍有未插入的图片,为每个图片创建新段落。

while (spanIndex < spanInfoList.size()) {
    SpanInfo spanInfo = spanInfoList.get(spanIndex);
    XWPFParagraph paragraph = document.createParagraph();
    paragraph.setAlignment(ParagraphAlignment.CENTER);
    XWPFRun run = paragraph.createRun();
    try {
        run.addPicture(
                new ByteArrayInputStream(spanInfo.imageData),
                XWPFDocument.PICTURE_TYPE_PNG,
                "image.png",
                spanInfo.widthEMU,
                spanInfo.heightEMU
        );
    } catch (Exception e) {
        Toast.makeText(this, "图片写入docx文件失败", Toast.LENGTH_LONG).show();
    }
    spanIndex++;
}

最后使用File和FileOutputStream保存文档即可。以下是保存后的效果:

在这里插入图片描述

二、docx转换为pdf或txt格式

利用LibreOffice实现,只需要后端运行如下代码即可。

dst = request.form.get('to')
# TMP = 临时存储路径
in_path = os.path.join(TMP, f"{uid}_{filename}")
subprocess.run([
    LIBREOFFICE_EXE, #LibreOffice可执行路径
    '--headless',
    '--convert-to', dst,
    '--outdir', TMP,
    in_path
], check=True)

下阶段任务

开发AI文档润色功能,并继续优化现有程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值