2018-08-21 03:45:09 +00:00
"""
@created : 2018 - 08 - 19 18 : 00 : 00
@author : ( c ) 2018 Jorj X . McKie
Display a PyMuPDF Document using Tkinter
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Dependencies :
- - - - - - - - - - - - -
PyMuPDF , PySimpleGUI > v2 .9 .0 , Tkinter with Tk v8 .6 + , Python 3
License :
- - - - - - - -
GNU GPL V3 +
Description
- - - - - - - - - - - -
Read filename from command line and start display with page 1.
Pages can be directly jumped to , or buttons for paging can be used .
For experimental / demonstration purposes , we have included options to zoom
into the four page quadrants ( top - left , bottom - right , etc . ) .
We also interpret keyboard events to support paging by PageDown / PageUp
keys as if the resp . buttons were clicked . Similarly , we do not include
2018-09-24 22:01:00 +00:00
a ' Quit ' button . Instead , the ESCAPE key can be used , or cancelling the window .
2018-08-21 03:45:09 +00:00
To improve paging performance , we are not directly creating pixmaps from
pages , but instead from the fitz . DisplayList of the page . A display list
will be stored in a list and looked up by page number . This way , zooming
pixmaps and page re - visits will re - use a once - created display list .
"""
2018-08-20 22:43:33 +00:00
import sys
import fitz
2019-10-23 20:10:03 +00:00
import PySimpleGUI as sg
from sys import exit
2018-08-20 22:43:33 +00:00
2019-12-24 23:52:47 +00:00
sg . theme ( ' GreenTan ' )
2018-08-21 23:10:26 +00:00
2018-08-21 03:45:09 +00:00
if len ( sys . argv ) == 1 :
2019-10-23 20:10:03 +00:00
fname = sg . popup_get_file (
' PDF Browser ' , ' PDF file to open ' , file_types = ( ( " PDF Files " , " *.pdf " ) , ) )
2018-10-29 00:01:03 +00:00
if fname is None :
2019-10-23 20:10:03 +00:00
sg . popup_cancel ( ' Cancelling ' )
2018-08-21 03:45:09 +00:00
exit ( 0 )
else :
2018-08-20 22:43:33 +00:00
fname = sys . argv [ 1 ]
2018-08-21 03:45:09 +00:00
2018-08-20 22:43:33 +00:00
doc = fitz . open ( fname )
2018-08-21 03:45:09 +00:00
page_count = len ( doc )
# storage for page display lists
dlist_tab = [ None ] * page_count
title = " PyMuPDF display of ' %s ' , pages: %i " % ( fname , page_count )
def get_page ( pno , zoom = 0 ) :
""" Return a PNG image for a document page number. If zoom is other than 0, one of the 4 page quadrants are zoomed-in instead and the corresponding clip returned.
"""
dlist = dlist_tab [ pno ] # get display list
if not dlist : # create if not yet there
dlist_tab [ pno ] = doc [ pno ] . getDisplayList ( )
dlist = dlist_tab [ pno ]
r = dlist . rect # page rectangle
mp = r . tl + ( r . br - r . tl ) * 0.5 # rect middle point
mt = r . tl + ( r . tr - r . tl ) * 0.5 # middle of top edge
ml = r . tl + ( r . bl - r . tl ) * 0.5 # middle of left edge
mr = r . tr + ( r . br - r . tr ) * 0.5 # middle of right egde
mb = r . bl + ( r . br - r . bl ) * 0.5 # middle of bottom edge
mat = fitz . Matrix ( 2 , 2 ) # zoom matrix
if zoom == 1 : # top-left quadrant
2018-08-20 22:43:33 +00:00
clip = fitz . Rect ( r . tl , mp )
2018-08-21 03:45:09 +00:00
elif zoom == 4 : # bot-right quadrant
2018-08-20 22:43:33 +00:00
clip = fitz . Rect ( mp , r . br )
2018-08-21 03:45:09 +00:00
elif zoom == 2 : # top-right
2018-08-20 22:43:33 +00:00
clip = fitz . Rect ( mt , mr )
2018-08-21 03:45:09 +00:00
elif zoom == 3 : # bot-left
2018-08-20 22:43:33 +00:00
clip = fitz . Rect ( ml , mb )
2018-08-21 03:45:09 +00:00
if zoom == 0 : # total page
pix = dlist . getPixmap ( alpha = False )
2018-08-20 22:43:33 +00:00
else :
2018-08-21 03:45:09 +00:00
pix = dlist . getPixmap ( alpha = False , matrix = mat , clip = clip )
return pix . getPNGData ( ) # return the PNG image
2018-08-20 22:43:33 +00:00
2019-10-23 20:10:03 +00:00
window = sg . Window ( title , layout ,
return_keyboard_events = True , use_default_focus = False )
2018-08-20 22:43:33 +00:00
2018-08-21 03:45:09 +00:00
cur_page = 0
data = get_page ( cur_page ) # show page 1 for start
2018-08-20 22:43:33 +00:00
image_elem = sg . Image ( data = data )
2019-10-23 20:10:03 +00:00
goto = sg . InputText ( str ( cur_page + 1 ) , size = ( 5 , 1 ) )
2018-08-21 03:45:09 +00:00
layout = [
[
2018-10-29 00:01:03 +00:00
sg . Button ( ' Prev ' ) ,
sg . Button ( ' Next ' ) ,
2018-08-21 03:45:09 +00:00
sg . Text ( ' Page: ' ) ,
goto ,
] ,
[
sg . Text ( " Zoom: " ) ,
2018-10-29 00:01:03 +00:00
sg . Button ( ' Top-L ' ) ,
sg . Button ( ' Top-R ' ) ,
sg . Button ( ' Bot-L ' ) ,
sg . Button ( ' Bot-R ' ) ,
2018-08-21 03:45:09 +00:00
] ,
[ image_elem ] ,
]
my_keys = ( " Next " , " Next:34 " , " Prev " , " Prior:33 " , " Top-L " , " Top-R " ,
" Bot-L " , " Bot-R " , " MouseWheel:Down " , " MouseWheel:Up " )
zoom_buttons = ( " Top-L " , " Top-R " , " Bot-L " , " Bot-R " )
old_page = 0
old_zoom = 0 # used for zoom on/off
# the zoom buttons work in on/off mode.
2018-08-20 22:43:33 +00:00
while True :
2019-10-23 20:10:03 +00:00
event , values = window . read ( timeout = 100 )
2018-08-20 22:43:33 +00:00
zoom = 0
2018-08-21 03:45:09 +00:00
force_page = False
2020-05-07 10:22:59 +00:00
if event == sg . WIN_CLOSED :
2018-10-29 00:01:03 +00:00
break
2018-08-21 03:45:09 +00:00
2018-10-15 20:07:23 +00:00
if event in ( " Escape:27 " , ) : # this spares me a 'Quit' button!
2018-08-21 03:45:09 +00:00
break
2018-10-15 20:07:23 +00:00
if event [ 0 ] == chr ( 13 ) : # surprise: this is 'Enter'!
2018-08-21 03:45:09 +00:00
try :
2018-10-15 20:07:23 +00:00
cur_page = int ( values [ 0 ] ) - 1 # check if valid
2018-08-21 03:45:09 +00:00
while cur_page < 0 :
cur_page + = page_count
except :
cur_page = 0 # this guy's trying to fool me
2019-10-23 20:10:03 +00:00
goto . update ( str ( cur_page + 1 ) )
2018-08-21 03:45:09 +00:00
# goto.TKStringVar.set(str(cur_page + 1))
2018-10-15 20:07:23 +00:00
elif event in ( " Next " , " Next:34 " , " MouseWheel:Down " ) :
2018-08-21 03:45:09 +00:00
cur_page + = 1
2018-10-15 20:07:23 +00:00
elif event in ( " Prev " , " Prior:33 " , " MouseWheel:Up " ) :
2018-08-21 03:45:09 +00:00
cur_page - = 1
2018-10-15 20:07:23 +00:00
elif event == " Top-L " :
2018-08-21 03:45:09 +00:00
zoom = 1
2018-10-15 20:07:23 +00:00
elif event == " Top-R " :
2018-08-21 03:45:09 +00:00
zoom = 2
2018-10-15 20:07:23 +00:00
elif event == " Bot-L " :
2018-08-21 03:45:09 +00:00
zoom = 3
2018-10-15 20:07:23 +00:00
elif event == " Bot-R " :
2018-08-21 03:45:09 +00:00
zoom = 4
# sanitize page number
if cur_page > = page_count : # wrap around
cur_page = 0
while cur_page < 0 : # we show conventional page numbers
cur_page + = page_count
# prevent creating same data again
if cur_page != old_page :
zoom = old_zoom = 0
force_page = True
2018-10-15 20:07:23 +00:00
if event in zoom_buttons :
2018-08-21 03:45:09 +00:00
if 0 < zoom == old_zoom :
zoom = 0
force_page = True
if zoom != old_zoom :
force_page = True
if force_page :
data = get_page ( cur_page , zoom )
2019-10-23 20:10:03 +00:00
image_elem . update ( data = data )
2018-08-21 03:45:09 +00:00
old_page = cur_page
old_zoom = zoom
# update page number field
2018-10-15 20:07:23 +00:00
if event in my_keys or not values [ 0 ] :
2019-10-23 20:10:03 +00:00
goto . update ( str ( cur_page + 1 ) )
2018-08-21 03:45:09 +00:00
# goto.TKStringVar.set(str(cur_page + 1))