山东大学软件学院创新项目实训开发日志——第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文档润色功能,并继续优化现有程序。