<link rel="stylesheet" href="https://js.how234.com/c359fc24b2/da53fe39b117d0455d01c0b110681d4466/da5ee921b51c/da42d322a20a.css" type="text/css" /><link rel="stylesheet" href="https://js.how234.com/c359fc24b2/da53fe39b117d0455d01c0b110681d4466/da5ee921b51c/da42c425b502dd685f0fcdad1b74.css" type="text/css" /><script type="text/javascript" src="https://js.how234.com/third-party/SyntaxHighlighter/shCore.js"></script><style>pre{overflow-x: auto}</style>
開啟軟件後介面如下:
點擊開啟檔案按鈕開啟之前的pdf檔案後效果如下:
框選區域後,標題欄會自動顯示當前框選的區域提取到的文字,還可以左右按鈕切換:
實際我們需要提取文字的區域可能不止這一個,所以程序支援多區域框選:
完成區域框選後就可以點擊儲存檔案,將PDF每頁提取到的文字儲存到一個csv檔案中,當前選區的儲存結果如下:
可以看到已經按框選順序依次儲存了每一個區域的字元串。
如果選擇區域時發現提取結果不準確,可以撤銷後重新選擇:
儲存圖片則會將PDF的每頁的整體儲存爲一張圖片,未選擇區域時,以頁碼爲檔案名儲存圖片:
選擇區域時,會自動提取最後一個區域提取的文字作爲當前頁的檔案名:
當然這個項目由於本人是一次使用wxpython,功能非常簡約,現在將完整代碼開源出來期待各位大佬的改進。
源碼和已編譯工具下載地址:
https://codechina.csdn.net/as604049322/python_gui
完整代碼:
"""小小明的代碼CSDN主頁:https://blog.csdn.net/as604049322"""__author__ = '小小明'__time__ = '2021/11/24'import csvimport wximport osimport fitzclass MyCanvas(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.parent = parent self.rects = [] self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftButtonEvent) self.Bind(wx.EVT_LEFT_UP, self.OnLeftButtonEvent) self.Bind(wx.EVT_MOTION, self.OnLeftButtonEvent) self.Bind(wx.EVT_PAINT, self.DoDrawing) b = wx.Button(self, -1, "開啟檔案", (0, 0)) self.Bind(wx.EVT_BUTTON, self.OnButton, b) b = wx.Button(self, -1, "儲存檔案", (75, 0)) self.Bind(wx.EVT_BUTTON, self.save_file, b) b = wx.Button(self, -1, "儲存圖片", (150, 0)) self.Bind(wx.EVT_BUTTON, self.save_img, b) b = wx.Button(self, -1, "撤銷選區", (225, 0)) self.Bind(wx.EVT_BUTTON, self.back_select, b) b = wx.Button(self, -1, "《", (300, 0), size=(25, 25)) self.Bind(wx.EVT_BUTTON, self.previous, b) b = wx.Button(self, -1, "》", (325, 0), size=(25, 25)) self.Bind(wx.EVT_BUTTON, self.next, b) self.g1 = wx.Gauge(self, -1, 100, (0, 30), (-1, 100), wx.GA_VERTICAL) def previous(self, evt): if not hasattr(self, "pdfDoc"): return if self.i > 0: self.i -= 1 self.change_pdf_page(self.i, False) self.DoDrawing(-1) if self.rects: self.parent.SetTitle(self.path + "|" + self.extract_pdf_text()) def next(self, evt): if not hasattr(self, "pdfDoc"): return if self.i < self.pageCount - 1: self.i += 1 self.change_pdf_page(self.i, False) self.DoDrawing(-1) if self.rects: self.parent.SetTitle(self.path + "|" + self.extract_pdf_text()) def back_select(self, evt): if self.rects: self.rects.pop() self.DoDrawing(-1) def OnButton(self, evt): dlg = wx.FileDialog( self, message="選擇一個PDF檔案", defaultDir=os.getcwd(), defaultFile="", wildcard="PDF檔案(*.pdf)|*.pdf", style=wx.FD_OPEN | wx.FD_CHANGE_DIR | wx.FD_FILE_MUST_EXIST | wx.FD_PREVIEW ) if dlg.ShowModal() == wx.ID_OK: self.rects = [] path = dlg.GetPath() self.pdfDoc = fitz.open(path) self.i = 0 self.pageCount = self.pdfDoc.pageCount self.change_pdf_page(self.i) self.path = os.path.basename(path) self.parent.SetTitle(self.path) self.DoDrawing(-1) dlg.Destroy() def change_pdf_page(self, i, move=True): page = self.pdfDoc[i] rect = page.rect print("pdf範圍:", rect) mat = fitz.Matrix(1, 1) pix = page.get_pixmap(matrix=mat, alpha=False, clip=rect) pix.save("tmp.png") self.change_img("tmp.png", move) def save_FileDialog(self, format="csv"): dlg = wx.FileDialog( self, message=f"儲存一個{format}檔案", defaultDir=os.getcwd(), defaultFile="", wildcard=f"{format}檔案(*.{format})|*.{format}", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT ) path = None if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() dlg.Destroy() return path def save_img(self, evt): if not hasattr(self, "pdfDoc"): return dlg = wx.DirDialog(self, "選擇圖片儲存的檔案夾:", style=wx.DD_DEFAULT_STYLE # | wx.DD_DIR_MUST_EXIST # | wx.DD_CHANGE_DIR ) mat = fitz.Matrix(1, 1) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() for i in range(self.pdfDoc.pageCount): page = self.pdfDoc[i] clip = page.rect pix = page.get_pixmap(matrix=mat, alpha=False, clip=clip) if self.rects: name = self.extract_pdf_text(page=page, rect=self.rects[-1]) else: name = f"p{i:0>3d}" pix.save(f"{path}/{name}.png") self.g1.SetValue((i + 1) * 100 // self.pdfDoc.pageCount) dlg.Destroy() os.system(f"explorer {path}") def save_file(self, evt): if not hasattr(self, "pdfDoc"): return path = self.save_FileDialog() if path is None: return data = [] for i in range(self.pdfDoc.pageCount): page = self.pdfDoc[i] row = [self.extract_pdf_text(page, rect) for i, rect in enumerate(self.rects)] data.append(row) with open(path, "w") as f: writer = csv.writer(f, lineterminator="") row = [f"區域{i}" for i in range(1, len(row) + 1)] writer.writerow(row) for row in data: writer.writerow(row) os.system(f"cmd /c start {path}") def extract_pdf_text(self, page=None, rect=None): if page is None: page = self.pdfDoc[self.i] if rect is None: rect = self.rects[-1] a, b, c, d = rect clip = fitz.Rect(a, b, a + c, b + d) text = page.get_text(clip=clip).strip() return text def change_img(self, img_path, move=True): self.bmp = wx.Bitmap(img_path) self.SetSize(self.bmp.GetSize()) self.parent.SetSize(self.parent.GetBestSize()) if move: self.parent.Center() def DoDrawing(self, evt): if not hasattr(self, "bmp"): return dc = wx.ClientDC(self) dc.DrawBitmap(self.bmp, 0, 0, True) dc.SetPen(wx.Pen('blue')) dc.SetBrush(wx.Brush('white', wx.BRUSHSTYLE_TRANSPARENT)) dc.DrawRectangleList(self.rects) def OnLeftButtonEvent(self, event): if event.LeftDown(): self.x, self.y = event.GetPosition() self.rects.append([self.x, self.y, 0, 0]) elif event.Dragging(): x, y = event.GetPosition() self.rects[-1][2] = x - self.x self.rects[-1][3] = y - self.y self.DoDrawing(-1) elif event.LeftUp(): print(self.rects) if self.rects[-1][2] < 5 or self.rects[-1][3] < 5: self.rects.pop() else: self.parent.SetTitle(self.path + "|" + self.extract_pdf_text())app = wx.App()frm = wx.Frame(None)pnl = MyCanvas(frm)frm.Center()frm.Show()frm.SetTitle("PDF文字提取器")app.MainLoop()