123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- # ===============================================
- # =============== 文档预览 - 连接器 ===============
- # ===============================================
- from PySide2.QtCore import QObject, Slot, Signal
- from PySide2.QtGui import QPixmap, QImage
- import fitz # PyMuPDF
- from umi_log import logger
- from .simple_mission import SimpleMission
- from ..image_controller.image_provider import PixmapProvider
- from ..utils.call_func import CallFunc
- from .mission_doc import MissionDOC
- MinSize = 0 # 最小渲染分辨率
- # 文档预览连接器
- class DocPreviewConnector(QObject):
- previewImg = Signal(str) # imgID
- previewOcr = Signal("QVariant") # [path, page, res]
- # 注:信号中含多个变量可能导致崩溃?
- def __init__(self, *args):
- super().__init__(*args)
- self._previewMission = SimpleMission(self._previewTask) # 简单任务对象
- self._previewDoc = None # 当前预览的对象
- self._previewPath = ""
- self._zooms = {} # 缓存页面缩放系数
- # 预览PDF画面
- @Slot(str, int, str)
- def preview(self, path, page, password):
- page -= 1
- self._previewMission.addMissionList([(path, page, password)])
- def _previewTask(self, msn):
- path, page, password = msn
- if path == self._previewPath: # 已经加载了
- doc = self._previewDoc
- else: # 新加载
- try:
- doc = fitz.open(path)
- if doc.is_encrypted and not doc.authenticate(password):
- msg = "[Warning] is_encrypted" # 固定指令,不能改
- self.previewImg.emit(msg)
- return
- except Exception as e:
- msg = f"[Error] 打开文档失败:{path} {e}"
- self.previewImg.emit(msg)
- return
- self._previewDoc = doc
- self._previewPath = path
- self._zooms = {}
- page_count = doc.page_count
- if page < 0 or page > page_count:
- logger.error(f"页数 {page} 超出范围 0-{page_count} 。")
- return
- # 检查页面边长,如果低于阈值,则增加放大系数,以提高渲染清晰度
- rect = doc[page].rect
- w, h = abs(rect[2] - rect[0]), abs(rect[3] - rect[1])
- m = min(w, h)
- if m < MinSize:
- zoom = MinSize / max(m, 1)
- matrix = fitz.Matrix(zoom, zoom)
- self._zooms[page] = zoom
- else:
- matrix = fitz.Identity
- self._zooms[page] = 1
- p = doc[page].get_pixmap(matrix=matrix)
- # 通过 QImage fromImage 转换
- # 必须先使用变量提取出图像 https://github.com/pymupdf/PyMuPDF/issues/1210
- samples = p.samples
- # 必须传入 pix.stride ,否则部分格式的图像会导致崩溃
- qimage = QImage(samples, p.width, p.height, p.stride, QImage.Format_RGB888)
- qpixmap = QPixmap.fromImage(qimage)
- imgID = PixmapProvider.addPixmap(qpixmap)
- self.previewImg.emit(imgID)
- # 预览一页OCR内容
- @Slot(str, int, str, "QVariant")
- def ocr(self, path, page, password, argd):
- argd = argd.toVariant() # qml对象转python字典
- # if "tbpu.ignoreArea" in argd: # 删除忽略区域参数
- # del argd["tbpu.ignoreArea"]
- def _onGet(msnInfo, page_, res):
- # 缩放文本位置
- if page_ in self._zooms and res["code"] == 100:
- z = self._zooms[page_]
- for r in res["data"]:
- for bi in range(4):
- r["box"][bi][0] = r["box"][bi][0] * z
- r["box"][bi][1] = r["box"][bi][1] * z
- page_ += 1
- self.previewOcr.emit([path, page_, res])
- def _onEnd(msnInfo, msg):
- if not msg.startswith("[Success]"):
- res = {"code": 103, "data": msg}
- self.previewOcr.emit([path, -1, res])
- msnInfo = {
- "argd": argd,
- "onGet": _onGet,
- "onEnd": _onEnd,
- }
- MissionDOC.addMission(msnInfo, path, (page, page), password=password)
- # 清空缓存
- @Slot()
- def clear(self):
- self._previewDoc = None
- self._previewPath = ""
|