Skip to content

Commit 5e446fb

Browse files
committed
add pdf viewer tutorial
1 parent 7bdb6f9 commit 5e446fb

File tree

8 files changed

+245
-0
lines changed

8 files changed

+245
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
212212
- [How to Split PDF Files in Python](https://www.thepythoncode.com/article/split-pdf-files-in-python). ([code](handling-pdf-files/split-pdf))
213213
- [How to Extract Text from PDF in Python](https://www.thepythoncode.com/article/extract-text-from-pdf-in-python). ([code](handling-pdf-files/extract-text-from-pdf))
214214
- [How to Convert HTML to PDF in Python](https://www.thepythoncode.com/article/convert-html-to-pdf-in-python). ([code](handling-pdf-files/convert-html-to-pdf))
215+
- [How to Make a GUI PDF Viewer in Python](https://www.thepythoncode.com/article/make-pdf-viewer-with-tktinter-in-python). ([code](gui-programming/pdf-viewer))
215216

216217
- ### [Python for Multimedia](https://www.thepythoncode.com/topic/python-for-multimedia)
217218
- [How to Make a Screen Recorder in Python](https://www.thepythoncode.com/article/make-screen-recorder-python). ([code](general/screen-recorder))

gui-programming/pdf-viewer/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# How to Make a GUI PDF Viewer in Python](https://www.thepythoncode.com/article/make-pdf-viewer-with-tktinter-in-python)
233 Bytes
Loading

gui-programming/pdf-viewer/miner.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# this is for doing some math operations
2+
import math
3+
# this is for handling the PDF operations
4+
import fitz
5+
# importing PhotoImage from tkinter
6+
from tkinter import PhotoImage
7+
8+
9+
10+
class PDFMiner:
11+
def __init__(self, filepath):
12+
# creating the file path
13+
self.filepath = filepath
14+
# opening the pdf document
15+
self.pdf = fitz.open(self.filepath)
16+
# loading the first page of the pdf document
17+
self.first_page = self.pdf.load_page(0)
18+
# getting the height and width of the first page
19+
self.width, self.height = self.first_page.rect.width, self.first_page.rect.height
20+
# initializing the zoom values of the page
21+
zoomdict = {800:0.8, 700:0.6, 600:1.0, 500:1.0}
22+
# getting the width value
23+
width = int(math.floor(self.width / 100.0) * 100)
24+
# zooming the page
25+
self.zoom = zoomdict[width]
26+
27+
# this will get the metadata from the document like
28+
# author, name of document, number of pages
29+
def get_metadata(self):
30+
# getting metadata from the open PDF document
31+
metadata = self.pdf.metadata
32+
# getting number of pages from the open PDF document
33+
numPages = self.pdf.page_count
34+
# returning the metadata and the numPages
35+
return metadata, numPages
36+
37+
# the function for getting the page
38+
def get_page(self, page_num):
39+
# loading the page
40+
page = self.pdf.load_page(page_num)
41+
# checking if zoom is True
42+
if self.zoom:
43+
# creating a Matrix whose zoom factor is self.zoom
44+
mat = fitz.Matrix(self.zoom, self.zoom)
45+
# gets the image of the page
46+
pix = page.get_pixmap(matrix=mat)
47+
# returns the image of the page
48+
else:
49+
pix = page.get_pixmap()
50+
# a variable that holds a transparent image
51+
px1 = fitz.Pixmap(pix, 0) if pix.alpha else pix
52+
# converting the image to bytes
53+
imgdata = px1.tobytes("ppm")
54+
# returning the image data
55+
return PhotoImage(data=imgdata)
56+
57+
58+
# function to get text from the current page
59+
def get_text(self, page_num):
60+
# loading the page
61+
page = self.pdf.load_page(page_num)
62+
# getting text from the loaded page
63+
text = page.getText('text')
64+
# returning text
65+
return text
6.37 KB
Binary file not shown.
+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# importing everything from tkinter
2+
from tkinter import *
3+
# importing ttk for styling widgets from tkinter
4+
from tkinter import ttk
5+
# importing filedialog from tkinter
6+
from tkinter import filedialog as fd
7+
# importing os module
8+
import os
9+
# importing the PDFMiner class from the miner file
10+
from miner import PDFMiner
11+
12+
13+
14+
# creating a class called PDFViewer
15+
class PDFViewer:
16+
# initializing the __init__ / special method
17+
def __init__(self, master):
18+
# path for the pdf doc
19+
self.path = None
20+
# state of the pdf doc, open or closed
21+
self.fileisopen = None
22+
# author of the pdf doc
23+
self.author = None
24+
# name for the pdf doc
25+
self.name = None
26+
# the current page for the pdf
27+
self.current_page = 0
28+
# total number of pages for the pdf doc
29+
self.numPages = None
30+
# creating the window
31+
self.master = master
32+
# gives title to the main window
33+
self.master.title('PDF Viewer')
34+
# gives dimensions to main window
35+
self.master.geometry('580x520+440+180')
36+
# this disables the minimize/maximize button on the main window
37+
self.master.resizable(width = 0, height = 0)
38+
# loads the icon and adds it to the main window
39+
self.master.iconbitmap(self.master, 'pdf_file_icon.ico')
40+
# creating the menu
41+
self.menu = Menu(self.master)
42+
# adding it to the main window
43+
self.master.config(menu=self.menu)
44+
# creating a sub menu
45+
self.filemenu = Menu(self.menu)
46+
# giving the sub menu a label
47+
self.menu.add_cascade(label="File", menu=self.filemenu)
48+
# adding a two buttons to the sub menus
49+
self.filemenu.add_command(label="Open File", command=self.open_file)
50+
self.filemenu.add_command(label="Exit", command=self.master.destroy)
51+
# creating the top frame
52+
self.top_frame = ttk.Frame(self.master, width=580, height=460)
53+
# placing the frame using inside main window using grid()
54+
self.top_frame.grid(row=0, column=0)
55+
# the frame will not propagate
56+
self.top_frame.grid_propagate(False)
57+
# creating the bottom frame
58+
self.bottom_frame = ttk.Frame(self.master, width=580, height=50)
59+
# placing the frame using inside main window using grid()
60+
self.bottom_frame.grid(row=1, column=0)
61+
# the frame will not propagate
62+
self.bottom_frame.grid_propagate(False)
63+
# creating a vertical scrollbar
64+
self.scrolly = Scrollbar(self.top_frame, orient=VERTICAL)
65+
# adding the scrollbar
66+
self.scrolly.grid(row=0, column=1, sticky=(N,S))
67+
# creating a horizontal scrollbar
68+
self.scrollx = Scrollbar(self.top_frame, orient=HORIZONTAL)
69+
# adding the scrollbar
70+
self.scrollx.grid(row=1, column=0, sticky=(W, E))
71+
# creating the canvas for display the PDF pages
72+
self.output = Canvas(self.top_frame, bg='#ECE8F3', width=560, height=435)
73+
# inserting both vertical and horizontal scrollbars to the canvas
74+
self.output.configure(yscrollcommand=self.scrolly.set, xscrollcommand=self.scrollx.set)
75+
# adding the canvas
76+
self.output.grid(row=0, column=0)
77+
# configuring the horizontal scrollbar to the canvas
78+
self.scrolly.configure(command=self.output.yview)
79+
# configuring the vertical scrollbar to the canvas
80+
self.scrollx.configure(command=self.output.xview)
81+
# loading the button icons
82+
self.uparrow_icon = PhotoImage(file='uparrow.png')
83+
self.downarrow_icon = PhotoImage(file='downarrow.png')
84+
# resizing the icons to fit on buttons
85+
self.uparrow = self.uparrow_icon.subsample(3, 3)
86+
self.downarrow = self.downarrow_icon.subsample(3, 3)
87+
# creating an up button with an icon
88+
self.upbutton = ttk.Button(self.bottom_frame, image=self.uparrow, command=self.previous_page)
89+
# adding the button
90+
self.upbutton.grid(row=0, column=1, padx=(270, 5), pady=8)
91+
# creating a down button with an icon
92+
self.downbutton = ttk.Button(self.bottom_frame, image=self.downarrow, command=self.next_page)
93+
# adding the button
94+
self.downbutton.grid(row=0, column=3, pady=8)
95+
# label for displaying page numbers
96+
self.page_label = ttk.Label(self.bottom_frame, text='page')
97+
# adding the label
98+
self.page_label.grid(row=0, column=4, padx=5)
99+
100+
# function for opening pdf files
101+
def open_file(self):
102+
# open the file dialog
103+
filepath = fd.askopenfilename(title='Select a PDF file', initialdir=os.getcwd(), filetypes=(('PDF', '*.pdf'), ))
104+
# checking if the file exists
105+
if filepath:
106+
# declaring the path
107+
self.path = filepath
108+
# extracting the pdf file from the path
109+
filename = os.path.basename(self.path)
110+
# passing the path to PDFMiner
111+
self.miner = PDFMiner(self.path)
112+
# getting data and numPages
113+
data, numPages = self.miner.get_metadata()
114+
# setting the current page to 0
115+
self.current_page = 0
116+
# checking if numPages exists
117+
if numPages:
118+
# getting the title
119+
self.name = data.get('title', filename[:-4])
120+
# getting the author
121+
self.author = data.get('author', None)
122+
self.numPages = numPages
123+
# setting fileopen to True
124+
self.fileisopen = True
125+
# calling the display_page() function
126+
self.display_page()
127+
# replacing the window title with the PDF document name
128+
self.master.title(self.name)
129+
130+
# the function to display the page
131+
def display_page(self):
132+
# checking if numPages is less than current_page and if current_page is less than
133+
# or equal to 0
134+
if 0 <= self.current_page < self.numPages:
135+
# getting the page using get_page() function from miner
136+
self.img_file = self.miner.get_page(self.current_page)
137+
# inserting the page image inside the Canvas
138+
self.output.create_image(0, 0, anchor='nw', image=self.img_file)
139+
# the variable to be stringified
140+
self.stringified_current_page = self.current_page + 1
141+
# updating the page label with number of pages
142+
self.page_label['text'] = str(self.stringified_current_page) + ' of ' + str(self.numPages)
143+
# creating a region for inserting the page inside the Canvas
144+
region = self.output.bbox(ALL)
145+
# making the region to be scrollable
146+
self.output.configure(scrollregion=region)
147+
148+
# function for displaying next page
149+
def next_page(self):
150+
# checking if file is open
151+
if self.fileisopen:
152+
# checking if current_page is less than or equal to numPages-1
153+
if self.current_page <= self.numPages - 1:
154+
# updating the page with value 1
155+
self.current_page += 1
156+
# displaying the new page
157+
self.display_page()
158+
159+
# function for displaying the previous page
160+
def previous_page(self):
161+
# checking if fileisopen
162+
if self.fileisopen:
163+
# checking if current_page is greater than 0
164+
if self.current_page > 0:
165+
# decrementing the current_page by 1
166+
self.current_page -= 1
167+
# displaying the previous page
168+
self.display_page()
169+
170+
171+
172+
# creating the root winding using Tk() class
173+
root = Tk()
174+
# instantiating/creating object app for class PDFViewer
175+
app = PDFViewer(root)
176+
# calling the mainloop to run the app infinitely until user closes it
177+
root.mainloop()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PyMuPDF
472 Bytes
Loading

0 commit comments

Comments
 (0)