PDF全组件复刻及翻译后样式缺失问题修复求助
问题修复:PDF翻译重建时缺失样式(背景色、表格网格线)
问题概述
提取PDF的页面布局、文本样式、表格、图片等所有组件,基于这些信息重建带翻译功能的新PDF,但生成的文档缺失文本背景色、表格网格线及单元格底纹。当前使用PyMuPDF(fitz)提取文本,ReportLab重建PDF。
问题根源
- 原代码仅通过注释(annotation)提取背景色,但很多PDF的文本背景、单元格底纹是通过**图形路径(Path)**实现的,并非注释
- 完全未处理表格的网格线,这类线条属于PDF中的图形元素,需要单独提取并绘制
修复后的完整代码
import os import fitz from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter, landscape, portrait from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from deep_translator import GoogleTranslator def get_page_orientation(pdf_path): doc = fitz.open(pdf_path) first_page = doc[0] width, height = first_page.rect.width, first_page.rect.height return width, height, "Landscape" if width > height else "Portrait" def convert_color(int_color): """Convert PyMuPDF integer color to RGB format (0-1 range)""" r = (int_color >> 16) & 255 g = (int_color >> 8) & 255 b = int_color & 255 return r / 255, g / 255, b / 255 def convert_fitz_color(fitz_color): """Convert PyMuPDF float color tuple (0-1) to ReportLab RGB format""" return fitz_color[0], fitz_color[1], fitz_color[2] def register_font(font_name): font_paths = { "ArialMT": "Arial.ttf", "TimesNewRomanPSMT": "TimesNewRoman.ttf", "Helvetica": "Helvetica.ttf", } if font_name not in pdfmetrics.getRegisteredFontNames(): font_path = font_paths.get(font_name) if font_path and os.path.exists(font_path): pdfmetrics.registerFont(TTFont(font_name, font_path)) else: font_name = "Helvetica" return font_name def auto_adjust_font(canvas_obj, text, x, y, width_limit, font_name="Helvetica", max_font_size=12): text = text or "" font_size = max_font_size while font_size > 6 and canvas_obj.stringWidth(text, font_name, font_size) > width_limit: font_size -= 1 canvas_obj.setFont(font_name, font_size) canvas_obj.drawString(x, y, text) def extract_text_and_colors(pdf_path): doc = fitz.open(pdf_path) extracted_data = [] for page in doc: blocks = page.get_text("dict")["blocks"] # 提取所有文本块的背景色(包括图形路径背景) page_bg_regions = [] # 获取页面中的图形路径 paths = page.get_drawings() for path in paths: # 处理填充的矩形(单元格背景、文本背景) if path["fill"] != (1,1,1) and len(path["rects"]) > 0: for rect in path["rects"]: page_bg_regions.append((fitz.Rect(rect), path["fill"])) page_data = [] for block in blocks: for line in block.get("lines", []): for span in line["spans"]: span_bbox = fitz.Rect(span["bbox"]) # 匹配当前文本块对应的背景色 bg_color = (1,1,1) for rect, color in page_bg_regions: if span_bbox.intersects(rect): bg_color = color break page_data.append( ( span["bbox"], span["text"], span["font"], span["size"], convert_color(span.get("color", 0)), bg_color ) ) extracted_data.append(page_data) return extracted_data def extract_page_graphics(pdf_path): """提取页面中的表格网格线和单元格背景图形""" doc = fitz.open(pdf_path) graphics_data = [] for page in doc: paths = page.get_drawings() page_graphics = { "cell_fills": [], # (bbox, color) "grid_lines": [] # (bbox, color, line_width) } for path in paths: # 处理单元格填充(有填充色的矩形) if path["fill"] != (1,1,1): for rect in path["rects"]: page_graphics["cell_fills"].append((rect, path["fill"])) # 处理网格线(无填充的线条/矩形边框) if path["fill"] == (1,1,1) and path["stroke"] != (1,1,1): # 处理矩形边框 for rect in path["rects"]: x0, y0, x1, y1 = rect # 上边框 page_graphics["grid_lines"].append(((x0, y1, x1, y1), path["stroke"], path["width"])) # 下边框 page_graphics["grid_lines"].append(((x0, y0, x1, y0), path["stroke"], path["width"])) # 左边框 page_graphics["grid_lines"].append(((x0, y0, x0, y1), path["stroke"], path["width"])) # 右边框 page_graphics["grid_lines"].append(((x1, y0, x1, y1), path["stroke"], path["width"])) # 处理单独的线条 for line in path["lines"]: page_graphics["grid_lines"].append((line, path["stroke"], path["width"])) graphics_data.append(page_graphics) return graphics_data def translate_text(text, target_language="fr"): if not text or not text.strip(): return "" try: translator = GoogleTranslator(source="auto", target=target_language) return translator.translate(text) or text except Exception as e: print(f"Translation error: {e}") return text def create_translated_pdf(input_pdf, output_pdf, target_language="fr"): text_data = extract_text_and_colors(input_pdf) graphics_data = extract_page_graphics(input_pdf) width, height, orientation = get_page_orientation(input_pdf) page_size = landscape(letter) if orientation == "Landscape" else portrait(letter) pdf_canvas = canvas.Canvas(output_pdf, pagesize=page_size) for page_idx, page_data in enumerate(text_data): page_graphics = graphics_data[page_idx] # 1. 先绘制单元格填充背景 for rect, color in page_graphics["cell_fills"]: x0, y0, x1, y1 = rect y0_flip = height - y1 y1_flip = height - y0 pdf_canvas.saveState() pdf_canvas.setFillColorRGB(*convert_fitz_color(color)) pdf_canvas.rect(x0, y0_flip, x1 - x0, y1_flip - y0_flip, fill=1, stroke=0) pdf_canvas.restoreState() # 2. 绘制表格网格线 for line_coords, color, line_width in page_graphics["grid_lines"]: x0, y0, x1, y1 = line_coords y0_flip = height - y0 y1_flip = height - y1 pdf_canvas.saveState() pdf_canvas.setStrokeColorRGB(*convert_fitz_color(color)) pdf_canvas.setLineWidth(line_width) pdf_canvas.line(x0, y0_flip, x1, y1_flip) pdf_canvas.restoreState() # 3. 绘制翻译后的文本 elements = filter(lambda el: el[1].strip(), page_data) for bbox, text, font_name, font_size, text_color, bg_color in elements: font_name = register_font(font_name) translated_text = translate_text(text, target_language) x, y, x1, y1 = bbox y_flip = height - y1 # 绘制文本背景(如果有) if bg_color != (1,1,1): pdf_canvas.saveState() pdf_canvas.setFillColorRGB(*convert_fitz_color(bg_color)) pdf_canvas.rect(x, y_flip, x1 - x, abs(y1 - y), fill=1, stroke=0) pdf_canvas.restoreState() # 绘制翻译文本 pdf_canvas.setFillColorRGB(*text_color) auto_adjust_font(pdf_canvas, translated_text, x, y_flip, x1 - x, font_name, font_size) pdf_canvas.showPage() pdf_canvas.save() print(f"Translated PDF saved as: {output_pdf}") def translate_pdf(input_pdf, output_pdf, target_language="fr"): create_translated_pdf(input_pdf, output_pdf, target_language) # 示例调用(替换为你的文件路径) # translate_pdf("input.pdf", "output_translated.pdf", target_language="fr")
关键修复说明
- 新增图形提取函数:
extract_page_graphics提取PDF中的路径元素,区分单元格填充背景和表格网格线 - 完善背景色提取:
extract_text_and_colors现在会匹配图形路径中的背景色,不再仅依赖注释 - 分层绘制逻辑:生成PDF时按「单元格背景 → 网格线 → 文本」的顺序绘制,确保层级正确
- 坐标转换优化:统一处理PyMuPDF和ReportLab的坐标系差异(Y轴方向相反)
内容的提问来源于stack exchange,提问作者rajank24
相关产品推荐
相关产品推荐

