最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python結合PyMuPDF手把手實現(xiàn)PDF原版式翻譯的實戰(zhàn)指南

 更新時間:2026年01月04日 08:49:17   作者:weixin_46244623  
本文提出了一種基于PyMuPDF和Ollama大模型的PDF翻譯方案,解決了傳統(tǒng)PDF翻譯中的版式錯亂,圖片丟失等問題,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下

一、背景與目標

在處理英文技術文檔、論文或說明書時,常見的 PDF 翻譯方案存在幾個痛點:

  • 翻譯后版式錯亂
  • 圖片、公式丟失
  • 代碼、URL 被誤翻譯
  • 中文字體無法正常顯示
  • 只能整頁 OCR,無法保持原始排版

本文介紹一種基于 PyMuPDF(fitz)+ Ollama 大模型的 PDF 翻譯方案,目標是:

逐頁、逐文本塊翻譯 PDF,自然語言翻譯為中文,圖片與版式完全保持不變

翻譯后的效果如下圖

二、整體技術方案

技術選型

模塊技術
PDF 解析PyMuPDF(fitz)
圖片處理PyMuPDF Pixmap
大模型推理Ollama
翻譯模型qwen2.5:7b
輸出方式重新生成 PDF(逐頁)

核心思路

整個流程可拆解為 5 個步驟:

  • 逐頁解析 PDF
  • 提取文本 span(包含字體、字號、位置)
  • 提取并復制原始圖片
  • 使用大模型翻譯自然語言文本
  • 在原坐標位置重新繪制文本,生成新 PDF

三、核心類設計:PDFTranslator

整個翻譯邏輯被封裝在 PDFTranslator 類中,職責清晰、結構合理。

必須安裝(核心依賴)

pip install pymupdf
pip install ollama
pip install pillow

完整代碼

import fitz  # PyMuPDF
import os
import tempfile
from PIL import Image
import io
import ollama


class PDFTranslator:
    def __init__(self, source_pdf_path, model="qwen2.5:7b"):
        """
        初始化PDF翻譯器
        
        Args:
            source_pdf_path (str): 源PDF文件路徑
            model (str): Ollama模型名稱
        """
        self.source_pdf_path = source_pdf_path
        self.doc = fitz.open(source_pdf_path)
        self.model = model
        
    def extract_text_and_positions(self, page_num):
        """
        提取指定頁面的文本及其位置信息
        
        Args:
            page_num (int): 頁面編號(從0開始)
            
        Returns:
            list: 包含文本塊信息的列表
        """
        page = self.doc[page_num]
        text_blocks = []
        
        # 獲取頁面上的文本塊
        blocks = page.get_text("dict")["blocks"]
        
        for block in blocks:
            if "lines" in block:  # 文本塊
                for line in block["lines"]:
                    for span in line["spans"]:
                        text_blocks.append({
                            "text": span["text"],
                            "bbox": span["bbox"],  # 邊界框 [x0, y0, x1, y1]
                            "size": span["size"],
                            "font": span["font"],
                            "color": span["color"]
                        })
        
        return text_blocks
    
    def extract_images(self, page_num):
        """
        提取指定頁面的圖片
        
        Args:
            page_num (int): 頁面編號(從0開始)
            
        Returns:
            list: 包含圖片信息的列表
        """
        page = self.doc[page_num]
        image_list = []
        
        # 獲取頁面上的圖片
        image_list_raw = page.get_images()
        
        for img_index, img in enumerate(image_list_raw):
            xref = img[0]
            pix = fitz.Pixmap(self.doc, xref)
            
            # 如果是CMYK圖片,轉換為RGB
            if pix.n < 5:
                img_data = pix.tobytes("png")
            else:
                pix1 = fitz.Pixmap(fitz.csRGB, pix)
                img_data = pix1.tobytes("png")
                pix1 = None
            
            # 獲取圖片在頁面上的位置
            img_rects = page.get_image_rects(xref)
            
            image_info = {
                "image_data": img_data,
                "rect": img_rects[0] if img_rects else None,
                "xref": xref
            }
            
            image_list.append(image_info)
            pix = None
        
        return image_list
    
    def translate_text(self, text, dest_lang='zh'):
        """
        使用Ollama大模型翻譯文本
        
        Args:
            text (str): 要翻譯的文本
            dest_lang (str): 目標語言
            
        Returns:
            str: 翻譯后的文本
        """
        try:
            # 構建翻譯提示,明確要求將英文翻譯成中文,只返回翻譯結果,保持原文格式
            prompt = f"""請將以下英文文本翻譯成中文,保持原文的格式、空格和標點符號,只返回翻譯結果,不要添加任何解釋。

翻譯要求:
1. 所有代碼內(nèi)容(包括代碼塊、函數(shù)名、類名、變量名、命令、配置項等)必須保持原樣,不得翻譯。
2. 所有網(wǎng)址(以 http://、https://、www. 開頭的鏈接)必須保持原樣,不得翻譯。
3. 所有數(shù)字保持原樣,不得翻譯或改寫(包括整數(shù)、小數(shù)、百分比、版本號、時間、日期、編號等)。
4. 保持原文中的空格、換行、縮進等格式,確保翻譯后格式一致。
5. 僅翻譯自然語言描述性的英文文本,其余內(nèi)容全部保持不變。

英文文本如下:
{text}
"""
            
            print(f"正在翻譯文本: {text[:50]}..." if len(text) > 50 else f"正在翻譯文本: {text}")
            
            # 調(diào)用Ollama模型
            response = ollama.chat(
                model=self.model,
                messages=[
                    {
                        'role': 'user',
                        'content': prompt
                    }
                ]
            )
            
            translated = response['message']['content'].strip()
            print(f"翻譯結果: {translated[:50]}..." if len(translated) > 50 else f"翻譯結果: {translated}")
            
            return translated
        except Exception as e:
            print(f"翻譯錯誤: {e}")
            return text  # 如果翻譯失敗,返回原文本
    
    def create_translated_pdf(self, output_path, dest_lang='zh', start_page=0, end_page=None):
        """
        創(chuàng)建翻譯后的PDF文件,保持原有版面布局
        
        Args:
            output_path (str): 輸出PDF文件路徑
            dest_lang (str): 目標語言
            start_page (int): 開始頁面(從0開始)
            end_page (int): 結束頁面(從0開始,None表示到最后一頁)
        """
        # 確定頁面范圍
        if end_page is None:
            end_page = len(self.doc) - 1
        else:
            end_page = min(end_page, len(self.doc) - 1)
        
        # 處理指定范圍的頁面
        for page_num in range(start_page, end_page + 1):
            print(f"正在處理第 {page_num + 1} 頁...(共{len(self.doc)}頁)")
            
            # 創(chuàng)建新的PDF文檔
            new_doc = fitz.open()
            
            # 獲取原始頁面信息
            original_page = self.doc.load_page(page_num)
            page_width = original_page.rect.width
            page_height = original_page.rect.height
            
            # 創(chuàng)建新頁面
            new_page = new_doc.new_page(width=page_width, height=page_height)
            
            # 復制原始頁面的布局和圖片
            original_page = self.doc[page_num]
            
            # 復制圖片
            images = self.extract_images(page_num)
            for img_info in images:
                if img_info["rect"]:
                    # 插入原始圖片
                    new_page.insert_image(img_info["rect"], stream=img_info["image_data"])
            
            # 提取并翻譯文本
            text_blocks = self.extract_text_and_positions(page_num)
            
            # 為了更好地保持版面,我們需要更精確地處理文本
            for block in text_blocks:
                if block["text"].strip():  # 避免空文本
                    translated_text = self.translate_text(block["text"], dest_lang)
                    
                    # 計算文本位置
                    bbox = block["bbox"]
                    x0, y0, x1, y1 = bbox
                    
                    # 創(chuàng)建文本插入點(使用左下角作為基點)
                    # fitz使用笛卡爾坐標系,y軸向上
                    point = fitz.Point(x0, y0 + block["size"])
                    
                    # 使用text writer來保持更好的版面
                    # 首先擦除原始文本區(qū)域(可選)
                    # 然后插入翻譯后的文本
                    # 檢查文本是否包含中文字符,如果是,則使用中文字體
                    if any(ord(char) > 127 for char in translated_text):  # 包含非ASCII字符(如中文)
                        try:
                            new_page.insert_text(
                                point,
                                translated_text,
                                fontsize=block["size"],
                                fontname="china-ss",
                                color=(0, 0, 0)  # 強制使用黑色文字以確??梢?
                            )
                        except:
                            # 如果內(nèi)置字體失敗,使用默認字體
                            new_page.insert_text(
                                point,
                                translated_text,
                                fontsize=block["size"],
                                color=(0, 0, 0)  # 強制使用黑色文字以確??梢?
                            )
                    else:
                        # 如果是英文文本,使用原始字體
                        try:
                            new_page.insert_text(
                                point,
                                translated_text,
                                fontsize=block["size"],
                                fontname=block["font"],
                                color=(0, 0, 0)  # 強制使用黑色文字以確??梢?
                            )
                        except:
                            new_page.insert_text(
                                point,
                                translated_text,
                                fontsize=block["size"],
                                color=(0, 0, 0)  # 強制使用黑色文字以確??梢?
                            )
                else:
                    # 如果是空文本,直接復制原始文本(如空格等)
                    bbox = block["bbox"]
                    x0, y0, x1, y1 = bbox
                    point = fitz.Point(x0, y0 + block["size"])
                    
                    # 對于空文本塊,也檢查是否包含中文字符
                    if any(ord(char) > 127 for char in block["text"]):  # 包含非ASCII字符(如中文)
                        try:
                            new_page.insert_text(
                                point,
                                block["text"],
                                fontsize=block["size"],
                                fontname="china-ss",
                                color=(0, 0, 0)  # 強制使用黑色文字以確??梢?
                            )
                        except:
                            # 如果內(nèi)置字體失敗,使用默認字體
                            new_page.insert_text(
                                point,
                                block["text"],
                                fontsize=block["size"],
                                color=(0, 0, 0)  # 強制使用黑色文字以確保可見
                            )
                    else:
                        # 如果是英文文本,使用原始字體
                        try:
                            new_page.insert_text(
                                point,
                                block["text"],
                                fontsize=block["size"],
                                fontname=block["font"],
                                color=(0, 0, 0)  # 強制使用黑色文字以確??梢?
                            )
                        except:
                            new_page.insert_text(
                                point,
                                block["text"],
                                fontsize=block["size"],
                                color=(0, 0, 0)  # 強制使用黑色文字以確??梢?
                            )
            
            # 為每一頁創(chuàng)建單獨的PDF文件
            page_output_path = output_path.replace('.pdf', f'_page_{page_num + 1}.pdf')
            new_doc.save(page_output_path)
            new_doc.close()
            print(f"第 {page_num + 1} 頁翻譯完成,輸出文件: {page_output_path}")
        
        print(f"所有頁面翻譯完成!")
    
    def get_page_count(self):
        """
        獲取PDF總頁數(shù)
        
        Returns:
            int: PDF總頁數(shù)
        """
        return len(self.doc)
    
    def close(self):
        """
        關閉文檔
        """
        if self.doc:
            self.doc.close()


def main():
    # 使用示例
    source_pdf = "1.pdf"
    output_pdf = "translated_1.pdf"
    
    # 創(chuàng)建翻譯器實例,使用Ollama模型
    translator = PDFTranslator(source_pdf, model="qwen2.5:7b")
    
    try:
        print(f"PDF總頁數(shù): {translator.get_page_count()}")
        
        # 翻譯PDF并保持版面(只翻譯前3頁作為示例)
        translator.create_translated_pdf(
            output_path=output_pdf, 
            dest_lang='zh',
            start_page=8,
            end_page=211  # 翻譯前3頁(0-2)
        )
    finally:
        # 關閉文檔
        translator.close()


if __name__ == "__main__":
    main()

運行結果 

到此這篇關于Python結合PyMuPDF手把手實現(xiàn)PDF原版式翻譯的實戰(zhàn)指南的文章就介紹到這了,更多相關Python PyMuPDF翻譯PDF內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 基于python?win32setpixel?api?實現(xiàn)計算機圖形學相關操作(推薦)

    基于python?win32setpixel?api?實現(xiàn)計算機圖形學相關操作(推薦)

    這篇文章主要介紹了基于python?win32setpixel?api?實現(xiàn)計算機圖形學相關操作,這次的主要分為2個主要模塊,一個是實現(xiàn)畫線,畫圓的算法,還有填充的算法,以及裁剪的算法,需要的朋友可以參考下
    2021-12-12
  • 詳解使用Python寫一個向數(shù)據(jù)庫填充數(shù)據(jù)的小工具(推薦)

    詳解使用Python寫一個向數(shù)據(jù)庫填充數(shù)據(jù)的小工具(推薦)

    這篇文章主要介紹了用Python寫一個向數(shù)據(jù)庫填充數(shù)據(jù)的小工具,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • python刪除xml中的w:ascii屬性的步驟

    python刪除xml中的w:ascii屬性的步驟

    使用xml.etree.ElementTree刪除Word?XML中w:ascii屬性,需注冊命名空間并定位rFonts元素,通過del操作刪除屬性,建議配合python-docx庫,注意備份及文檔保護設置,確保修改目標樣式,本文給大家介紹python刪除xml中的w:ascii屬性的步驟,感興趣的朋友一起看看吧
    2025-06-06
  • 基于Python繪制子圖及子圖刻度的變換等的問題

    基于Python繪制子圖及子圖刻度的變換等的問題

    這篇文章主要介紹了基于Python繪制子圖及子圖刻度的變換等的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • 學習Python爬蟲的幾點建議

    學習Python爬蟲的幾點建議

    這篇文章主要介紹了學習Python爬蟲的幾點建議,對新手學習爬蟲有很大的幫助,感興趣的朋友可以了解下
    2020-08-08
  • 在pycharm中配置Anaconda以及pip源配置詳解

    在pycharm中配置Anaconda以及pip源配置詳解

    這篇文章主要介紹了在pycharm中配置Anaconda以及pip源配置詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • python 讀取數(shù)據(jù)庫并繪圖的實例

    python 讀取數(shù)據(jù)庫并繪圖的實例

    今天小編就為大家分享一篇python 讀取數(shù)據(jù)庫并繪圖的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • Pandas中Series和DataFrame的索引實現(xiàn)

    Pandas中Series和DataFrame的索引實現(xiàn)

    這篇文章主要介紹了Pandas中Series和DataFrame的索引實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-06-06
  • python基于property()函數(shù)定義屬性

    python基于property()函數(shù)定義屬性

    這篇文章主要介紹了python基于property()函數(shù)定義屬性,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • python openpyxl使用方法詳解

    python openpyxl使用方法詳解

    這篇文章主要介紹了python openpyxl使用方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-07-07

最新評論

九台市| 封开县| 潼南县| 莒南县| 太谷县| 湘潭县| 新昌县| 南部县| 冀州市| 凤山市| 广州市| 密云县| 西贡区| 宿州市| 铜梁县| 临武县| 蒲江县| 天祝| 旬阳县| 澄迈县| 招远市| 乌拉特前旗| 澄江县| 察雅县| 高唐县| 和平县| 张家川| 潍坊市| 九寨沟县| 辽宁省| 西城区| 石城县| 滨州市| 华池县| 太原市| 平泉县| 曲靖市| 宝山区| 桂平市| 高尔夫| 小金县|