上文见>>
论文翻译系统核心代码
核心步骤
首先读取目标PDF中的所有文字块,将其保存在指定Json。
将目标PDF中的文字块全部删除,保留一个不包含文字块的PDF模板文件。
对Json进行处理,基于Json中的文本内容进行翻译操作,保存为新Json。
基于Json,将文本块按照原格式排版写入PDF模板文件。
extract_pdf_text_to_json函数
extract_pdf_text_to_json函数来提取PDF中的文本块。
def extract_pdf_text_to_json(input_pdf_path, output_json_path, default_fontname = "Helvetica"):
"""
从指定的PDF中提取文本信息,并将其保存为JSON文件。
:parameters:
- input_pdf_path (str): Path to the input PDF file.
- output_json_path (str): Path where the output JSON file should be saved.
"""
with fitz.open(input_pdf_path) as input_pdf:
# 初始化一个列表以保存提取的文本数据
text_data = []
# 迭代输入PDF中的每一页
for page_num in range(len(input_pdf)):
input_page = input_pdf[page_num]
text_dict = input_page.get_text("dict")
# 处理此页面上的文本块
for block in text_dict["blocks"]:
if block["type"] == 0: # 文本块类型为0
# Extract text from each line in the block
block_text = "\n".join(" ".join(span["text"] for span in line["spans"]) for line in block["lines"])
sizes = [span["size"] for line in block["lines"] for span in line["spans"]]
size_counter = collections.Counter(sizes)
common_size = size_counter.most_common(1)[0][0] if sizes else None
colors = [span["color"] for line in block["lines"] for span in line["spans"]]
color_counter = collections.Counter(colors)
common_color = color_counter.most_common(1)[0][0] if colors else None
final_color = rgb_to_fitz_color(common_color)
block_entry = {
"page": page_num,
"rect": list(fitz.Rect(block["bbox"])),
"text": block_text,
"size": common_size,
"color": final_color,
"fontname": block["fontname"] if "fontname" in block else default_fontname,
}
text_data.append(block_entry)
【更多更详尽的代码 见大模型3期】此函数不仅提取了文本内容,还保留了每个文本块的页码、位置、字体大小、颜色
等关键信息。这些信息被保存为JSON格式,为后续的翻译和重组过程提供了必要的结构数据。
remove_text_from_pdf函数
为了保持原PDF中的非文本元素(如图像、图表等),我们实现了remove_text_from_pdf函数,用来创建了一个仅包含非文本元素的PDF模板。
def remove_text_from_pdf(input_pdf_path, output_pdf_path):
"""
从PDF文件中删除所有文本,并将结果保存为PDF模板。
:parameters:
- input_pdf_path (str): Path to the input PDF file.
- output_pdf_path (str): Path where the output PDF template should be saved.
"""
doc = fitz.open(input_pdf_path)
# 遍历文档的每一页
for page in doc:
# 获取页面上的所有文字块
text_dict = page.get_text("dict")
# 遍历所有文字块
for block in text_dict["blocks"]:
if block["type"] == 0: # 文本块类型为0
# 获取文字块位置
rect = fitz.Rect(block["bbox"])
# 添加红线注释
page.add_redact_annot(rect)
# 应用并清除标记的内容,实际删除文本
page.apply_redactions()
# 保存修改后的PDF文件
doc.save(output_pdf_path)
doc.close()这个函数通过添加红线注释并应用编辑来删除所有文本,同时保留其他元素,从而创建一个可供后续使用的PDF模板。
get_blocks_text_translated函数
为了确保翻译的一致性和效率,采用了单文本块并发翻译的策略。当然也曾尝试实现过多文本块组合翻译的策略,但拆分存在一定的错误率,会出现翻译前后文本数量不一致的情况,不稳定。
def get_blocks_text_translated(blocks_text_list, model_name):
simple_translate_prompt = """
你是一名资深的翻译工作者,你的目标是帮助用户将指定文本内容翻译为中文。
你必须满足以下要求:
- 如果需要翻译的文本内容为空,则无需输出任何内容。请不要输出抱歉等任何说明和描述。
- 下面为一些翻译时参考的角度,你需要考虑这些角度来确保翻译的准确性。
- "准确性":翻译内容需要尽可能准确地表达原文的意思。
- "数字、公式、特殊符号与网址":如果翻译内容涉及到数字、公式、特殊符号与网址,你无需对数字、公式、特殊符号与网址进行翻译,仅确保数字、公式、特殊符号与网址不变即可。
- "术语":在专业领域中,很多词汇有特定的含义,需要确保这些术语被准确地翻译。
- "语境":理解原文的语境对于准确翻译非常重要。你需要需要确认具体语境。
【更多更详尽的代码 见大模型3】
下面为指定需要翻译的文本内容,你无需返回原文,无需给出任何说明和描述,仅提供最终翻译结果。
{content}
"""
# 初始化用于存储翻译后的文本块的列表
translated_blocks_text_list = []
print(blocks_text_list)
# 创建一个线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=24) as executor:
# 使用map函数并发执行get_res函数
results = executor.map(lambda block_text: get_res(simple_translate_prompt.format(content=block_text), model_name), blocks_text_list)
# 遍历结果
for result in results:
try:
# 获取翻译响应
translate_response = result.choices[0].message.content
# 将翻译后的响应添加到translated_blocks_text_list列表中
translated_blocks_text_list.append(translate_response)
except Exception as exc:
print(f"An error occurred while translating: {exc}")这个函数使用预定义的翻译提示模板,为每个文本块生成翻译请求。然后,它使用get_res函数并发调用大模型API进行翻译。这种方法既保证了翻译质量,又提高了处理效率。为了提高整体翻译效率,采用了并发处理机制。在翻译阶段,使用Python的concurrent.futures模块实现了并发API调用,显著减少了处理大型文档的时间。
update_translated_json函数
翻译完成后,我们使用update_translated_json函数将翻译结果整合到原始的JSON结构中。这个函数首先校验翻译后的文本块数量与原始文本块数量是否一致,校验通过后会将翻译后的文本更新到JSON结构中,保持了原始PDF的结构信息。
def update_translated_json(json_path, new_json_path, blocks_text_count, translated_blocks_text_list):
if len(translated_blocks_text_list) != blocks_text_count:
raise ValueError("The number of translated text blocks does not match the original text blocks.")
# 打开并加载JSON文件
with open(json_path, 'r', encoding='utf-8') as f:
text_data = json.load(f)
# 遍历每个文本块
for i, block in enumerate(text_data):
block['text'] = translated_blocks_text_list[i]
print(block['text'])
# 将更新后的文本数据保存为JSON文件
with open(new_json_path, "w", encoding='utf-8') as f2:
json.dump(text_data, f2, indent=2, ensure_ascii=False)write_text_to_pdf_from_json函数
使用write_text_to_pdf_from_json函数将翻译后的文本写入之前生成的PDF模板。
def write_text_to_pdf_from_json(input_json_path, template_pdf_path, output_pdf_path, ttf_path=None):
"""
从JSON文件中读取文本信息并将其写入指定的PDF模板中。
:parameters:
- input_json_path (str): Path to the input JSON file containing text data.
- template_pdf_path (str): Path to the template PDF file.
- output_pdf_path (str): Path where the output PDF file should be saved.
- default_fontname (str, optional): Default font name to use for the text in the output PDF. Defaults to "Helvetica".
- ttf_path (str, optional): Path to the ttf file to be used as font. If not provided, default_fontname will be used.
"""
# Load text data from the JSON file
with open(input_json_path, "r",encoding="utf-8") as json_file:
text_data = json.load(json_file)
# 使用模板文件
output_pdf = fitz.open(template_pdf_path)
# 遍历文本数据
for block in text_data:
# 如果需要,将新页面添加到输出PDF
while block["page"] >= len(output_pdf):
output_pdf.new_page()
output_page = output_pdf[block["page"]]
if ttf_path is not None:
default_fontname = "Sourcehan"
output_page.insert_font(fontname=default_fontname,fontfile=ttf_path)
rect = fitz.Rect(*block["rect"])
rect_height = block["rect"][3] - block["rect"][1]
rect_width = block["rect"][2] - block["rect"][0]
rect_gradient = rect_height / rect_width
# 计算文本块的宽高比
if rect_gradient < 10:
bbox = -1
# 尝试将文本插入矩形框,判断是否能正常插入,通过减小字体的尺寸,直到可以正常插入为止
while bbox <= 0:
bbox = output_page.insert_textbox(
rect,
block["text"],
fontname=default_fontname,
fontsize=block["size"],
color=block["color"],
align=fitz.TEXT_ALIGN_LEFT, # 左对齐
)
block["size"] -= 0.5
【更多更详尽的代码 见大模型3】
# 保存新的PDF
output_pdf.save(output_pdf_path)
output_pdf.close()该重组函数实现了以下关键功能:根据JSON中的页码信息,确保输出PDF有足够的页面;支持自定义字体文件的使用;根据文本块的位置信息,将翻译后的文本精确插入到对应位置;动态调整字体大小,以适应不同语言间可能存在的文本长度差异;处理特殊情况,如arxiv的垂直文本的旋转插入。
部分优化过程
文本写入矩形框时超出范围
问题描述:通过insert_text方法将文本信息写入PDF中,会导致文本超出原本矩形框范围。
方案一:以文本块的高度来比较,如果插入后bbox是小于0的,则表示插入是失败的,如果是大于或等于0,则表示插入成功
bbox = -1
while bbox < 0:
bbox = output_page.insert_textbox(
rect,
block["text"],
fontname=default_fontname,
fontsize=block["size"],
color=block["color"],
align=fitz.TEXT_ALIGN_LEFT, # 左对齐
)
block["size"] -= 0.5此方案仍有问题,部分标题的字会变得非常小,原因是本身一行的字会生成两行的,因此会把字变得很小。
故还需解决标题换行的问题。
如果一个block有多个行,从第二行开始,判断该行和上一行的纵坐标是否一致,如果一致则用空格进行拼接,否则用换行符进行拼接。
for i,line in enumerate(block["lines"]):
span_text = ''
for span in line["spans"]:
span_text = span_text + span["text"].strip()
if i == 0:
block_text = block_text + span_text
if i > 0:
# 解决标题换行的代码,判断当前行的纵坐标与上一行的纵坐标是否一致
if block["lines"][i-1]["spans"][-1]['origin'][1] == block["lines"][i]["spans"][0]['origin'][1]:
block_text = block_text + ' ' + span_text对纵向文本的写入问题
一般论文中的纵向文本为论文首页左侧的信息。初步方案通过判断纵向文本的高度是否大于300作为依据。
原代码:
if block["rect"][3] - block["rect"][1] < 300:部分文档还是有问题
优化方案
计算rect的高宽比,大于10的直接插入文本,小于10的则对文本进行旋转插入。
rect_height = block["rect"][3] - block["rect"][1]
rect_width = block["rect"][2] - block["rect"][0]
rect_gradient = rect_height / rect_width
# 计算文本块的宽高比
if rect_gradient < 10:
bbox = -1
while bbox < 0:
bbox = output_page.insert_textbox(
rect,
block["text"],
fontname=default_fontname,
fontsize=block["size"],
color=block["color"],
align=fitz.TEXT_ALIGN_LEFT, # 左对齐
)
block["size"] -= 0.5
【更多更详尽的代码 见大模型3】不足与优化方向
尽管该系统在文档翻译领域取得了显著成果,但仍存在一些局限性。对于复杂结构的PDF文档,如包含大量图表、公式或特殊字体的文档,解析精度可能不足。此外,虽然采用了并发翻译策略,但可能导致上下文关联的文本块之间的翻译不够连贯。对于数学公式、化学式等特殊内容,当前系统可能无法准确识别和保留其结构。在专业领域的文档翻译中,系统也可能面临无法准确理解和翻译特定术语的挑战。
基于当前系统的实现和存在的局限性,有以下几个可能的发展方向:
增强PDF解析能力:探索更先进的PDF解析库或开发专有解析模块,提高对复杂PDF文档结构和元数据的解析精度。同时,开发专门的图表和公式识别模块,确保这些特殊元素在翻译过程中得到正确处理和还原。
优化翻译质量:实现基于上下文的分块策略,确保语义相关的文本块能够一起被翻译,提高翻译的连贯性。引入Reflection机制,允许模型对自己的翻译结果进行反思和修正,进一步提高翻译质量。此外,开发专门的术语库管理系统,允许用户或组织创建和维护专属术语库,确保特定领域词汇的一致性和准确性。
扩展系统功能:支持更多语言对的翻译,如中译英、日译中等,扩大系统的应用范围。开发OCR(光学字符识别)模块,使系统能够处理扫描版PDF或图片格式的文档。实现实时翻译预览功能,允许用户在翻译过程中查看和编辑部分翻译结果。
提高系统性能:优化并发处理机制,探索更高效的任务调度算法,进一步提高系统的翻译速度。实现智能缓存机制,对常见文本段落或术语进行缓存,减少重复翻译的计算开销。探索模型压缩和量化技术,降低系统资源需求,提高翻译效率。
更多内容见七月在线「大模型项目开发线上营 第3期」
↓↓↓扫码了解详情/抢购↓↓↓
课程咨询可找苏苏老师VX:julyedukefu008或七月在线其他老师
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.