doc_preview_connector.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. # ===============================================
  2. # =============== 文档预览 - 连接器 ===============
  3. # ===============================================
  4. from PySide2.QtCore import QObject, Slot, Signal
  5. from PySide2.QtGui import QPixmap, QImage
  6. import fitz # PyMuPDF
  7. from umi_log import logger
  8. from .simple_mission import SimpleMission
  9. from ..image_controller.image_provider import PixmapProvider
  10. from ..utils.call_func import CallFunc
  11. from .mission_doc import MissionDOC
  12. MinSize = 0 # 最小渲染分辨率
  13. # 文档预览连接器
  14. class DocPreviewConnector(QObject):
  15. previewImg = Signal(str) # imgID
  16. previewOcr = Signal("QVariant") # [path, page, res]
  17. # 注:信号中含多个变量可能导致崩溃?
  18. def __init__(self, *args):
  19. super().__init__(*args)
  20. self._previewMission = SimpleMission(self._previewTask) # 简单任务对象
  21. self._previewDoc = None # 当前预览的对象
  22. self._previewPath = ""
  23. self._zooms = {} # 缓存页面缩放系数
  24. # 预览PDF画面
  25. @Slot(str, int, str)
  26. def preview(self, path, page, password):
  27. page -= 1
  28. self._previewMission.addMissionList([(path, page, password)])
  29. def _previewTask(self, msn):
  30. path, page, password = msn
  31. if path == self._previewPath: # 已经加载了
  32. doc = self._previewDoc
  33. else: # 新加载
  34. try:
  35. doc = fitz.open(path)
  36. if doc.is_encrypted and not doc.authenticate(password):
  37. msg = "[Warning] is_encrypted" # 固定指令,不能改
  38. self.previewImg.emit(msg)
  39. return
  40. except Exception as e:
  41. msg = f"[Error] 打开文档失败:{path} {e}"
  42. self.previewImg.emit(msg)
  43. return
  44. self._previewDoc = doc
  45. self._previewPath = path
  46. self._zooms = {}
  47. page_count = doc.page_count
  48. if page < 0 or page > page_count:
  49. logger.error(f"页数 {page} 超出范围 0-{page_count} 。")
  50. return
  51. # 检查页面边长,如果低于阈值,则增加放大系数,以提高渲染清晰度
  52. rect = doc[page].rect
  53. w, h = abs(rect[2] - rect[0]), abs(rect[3] - rect[1])
  54. m = min(w, h)
  55. if m < MinSize:
  56. zoom = MinSize / max(m, 1)
  57. matrix = fitz.Matrix(zoom, zoom)
  58. self._zooms[page] = zoom
  59. else:
  60. matrix = fitz.Identity
  61. self._zooms[page] = 1
  62. p = doc[page].get_pixmap(matrix=matrix)
  63. # 通过 QImage fromImage 转换
  64. # 必须先使用变量提取出图像 https://github.com/pymupdf/PyMuPDF/issues/1210
  65. samples = p.samples
  66. # 必须传入 pix.stride ,否则部分格式的图像会导致崩溃
  67. qimage = QImage(samples, p.width, p.height, p.stride, QImage.Format_RGB888)
  68. qpixmap = QPixmap.fromImage(qimage)
  69. imgID = PixmapProvider.addPixmap(qpixmap)
  70. self.previewImg.emit(imgID)
  71. # 预览一页OCR内容
  72. @Slot(str, int, str, "QVariant")
  73. def ocr(self, path, page, password, argd):
  74. argd = argd.toVariant() # qml对象转python字典
  75. # if "tbpu.ignoreArea" in argd: # 删除忽略区域参数
  76. # del argd["tbpu.ignoreArea"]
  77. def _onGet(msnInfo, page_, res):
  78. # 缩放文本位置
  79. if page_ in self._zooms and res["code"] == 100:
  80. z = self._zooms[page_]
  81. for r in res["data"]:
  82. for bi in range(4):
  83. r["box"][bi][0] = r["box"][bi][0] * z
  84. r["box"][bi][1] = r["box"][bi][1] * z
  85. page_ += 1
  86. self.previewOcr.emit([path, page_, res])
  87. def _onEnd(msnInfo, msg):
  88. if not msg.startswith("[Success]"):
  89. res = {"code": 103, "data": msg}
  90. self.previewOcr.emit([path, -1, res])
  91. msnInfo = {
  92. "argd": argd,
  93. "onGet": _onGet,
  94. "onEnd": _onEnd,
  95. }
  96. MissionDOC.addMission(msnInfo, path, (page, page), password=password)
  97. # 清空缓存
  98. @Slot()
  99. def clear(self):
  100. self._previewDoc = None
  101. self._previewPath = ""