This example shows a PDF Viewer class, which handles things like Zoom and Scrolling. It requires python-poppler and wxPython >= 2.8.9.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | #!/usr/bin/env python
# coding: utf-8
"""
wxPDFViewer - Simple PDF Viewer using Python-Poppler and wxPython
Marcelo Fidel Fernandez - BSD License
http://www.marcelofernandez.info - marcelo.fidel.fernandez@gmail.com
"""
import wx
import wx.lib.wxcairo as wxcairo
import sys
import poppler
class PDFWindow(wx.ScrolledWindow):
""" This example class implements a PDF Viewer Window, handling Zoom and Scrolling """
MAX_SCALE = 2
MIN_SCALE = 1
SCROLLBAR_UNITS = 20 # pixels per scrollbar unit
def __init__(self, parent):
wx.ScrolledWindow.__init__(self, parent, wx.ID_ANY)
# Wrap a panel inside
self.panel = wx.Panel(self)
# Initialize variables
self.n_page = 0
self.scale = 1
self.document = None
self.n_pages = None
self.current_page = None
self.width = None
self.height = None
# Connect panel events
self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
self.panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.panel.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.panel.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
def LoadDocument(self, file):
self.document = poppler.document_new_from_file("file://" + file, None)
self.n_pages = self.document.get_n_pages()
self.current_page = self.document.get_page(self.n_page)
self.width, self.height = self.current_page.get_size()
self._UpdateSize()
def OnPaint(self, event):
dc = wx.PaintDC(self.panel)
cr = wxcairo.ContextFromDC(dc)
cr.set_source_rgb(1, 1, 1) # White background
if self.scale != 1:
cr.scale(self.scale, self.scale)
cr.rectangle(0, 0, self.width, self.height)
cr.fill()
self.current_page.render(cr)
def OnLeftDown(self, event):
self._UpdateScale(self.scale + 0.2)
def OnRightDown(self, event):
self._UpdateScale(self.scale - 0.2)
def _UpdateScale(self, new_scale):
if new_scale >= PDFWindow.MIN_SCALE and new_scale <= PDFWindow.MAX_SCALE:
self.scale = new_scale
# Obtain the current scroll position
prev_position = self.GetViewStart()
# Scroll to the beginning because I'm going to redraw all the panel
self.Scroll(0, 0)
# Redraw (calls OnPaint and such)
self.Refresh()
# Update panel Size and scrollbar config
self._UpdateSize()
# Get to the previous scroll position
self.Scroll(prev_position[0], prev_position[1])
def _UpdateSize(self):
u = PDFWindow.SCROLLBAR_UNITS
self.panel.SetSize((self.width*self.scale, self.height*self.scale))
self.SetScrollbars(u, u, (self.width*self.scale)/u, (self.height*self.scale)/u)
def OnKeyDown(self, event):
update = True
# More keycodes in http://docs.wxwidgets.org/stable/wx_keycodes.html#keycodes
keycode = event.GetKeyCode()
if keycode in (wx.WXK_PAGEDOWN, wx.WXK_SPACE):
next_page = self.n_page + 1
elif keycode == wx.WXK_PAGEUP:
next_page = self.n_page - 1
else:
update = False
if update and (next_page >= 0) and (next_page < self.n_pages):
self.n_page = next_page
self.current_page = self.document.get_page(next_page)
self.Refresh()
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, "wxPdf Viewer", size=(800,600))
self.pdfwindow = PDFWindow(self)
self.pdfwindow.LoadDocument(sys.argv[1])
self.pdfwindow.SetFocus() # To capture keyboard events
if __name__=="__main__":
app = wx.App()
f = MyFrame()
f.Show()
app.MainLoop()
|
This gives me segmentation fault on line 50: cr = wxcairo.ContextFromDC(dc)
Perhaps it works nice under valgrind. What can it be?
Hi Vinicius,
It works fine here, in my Ubuntu 11.04 installation. Could you describe what's you system environment, python and library versions?
Regards
Hi Marcelo,
I have programmed with the code you have done a full pdf reader with Python! But I need your help: I have many pdf with images inside, and the Cairo back-ends for Poppler aren't good to work with images: the scrolling is very slow. I know that there are other back-ends for drawing pdf documents: Splash. But I cannot find anything: no documentation or tutorials about it. Can you help me?
Thank you so much.
Luca
Hi Luca,
Try Evince, which uses poppler-cairo (but from C) to render.
If it is faster, I guess the problem with the slow scrolling for complex pdfs isn't in the backend but in the OnPaint() method. It redraws all the GC context instead of moving the old part and after that, just render the new one (on the top or bottom, depending of the scroll direction).
I'm accepting patches to improve this behavior. :-)
Regards, Marcelo PD: You can see the Evince source code to see how it handles this situation.
Hi Marcelo,
I know Evince, and I use it for read my pdfs. But I need to underline pdfs I have scanned, and with Evince it is impossible. Sometimes I use Xournal for my tasks, but i want to underline with straight lines, not paint on with standard Xournals tool. So I'm doing the software that I need by myself, but I don't know C, only a bit of Python. I have read that Cairo works well with vector graphic and not with raster graphic. Maybe Splash works better with raster graphic. Or maybe I have to learn C !
Luca
Luca,
I said that if Evince is faster to render that kind of pdfs when scrolling, I guess the issue is in the OnPaint() method because it uses the same library and backend than this example.
Regards
Marcelo,
I understand now! I'll see if I can find a good workaround for OnPaint() method.
I'll write again if I'll find something better.
Regards :-)
@Luca,
please share, I am also looking at making Python pdf viewer with annotations and highlighting.