Search code examples
pythontkinterreportlabbytesiotkpdfviewer

How to display PDF files from bytes with tkinter


I'm trying to make an application to display PDF files inside it. And maybe it wouldn't be so hard to make, but I have a few steps:

  1. Create a PDF file with ReportLab (there is nothing hard to get).
  2. I don't want so save this pdf file on hard disk, so I will use memory storage with BytesIO.
  3. I've found "tkpdfviewer" like package that will show PDF files inside my application.

I wrote a simple code which will show my problem.

from tkinter import *
from tkinter import ttk
import tkinter as tk
from io import BytesIO
from tkPDFViewer import tkPDFViewer as pdf
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.units import mm

main = tk.Tk()

main.title("PDF Viewer Main")

# Main window sizes
app_height = 350
app_width = 350

# Define of screen sizes
screen_width = main.winfo_screenwidth()
screen_height = main.winfo_screenheight()

# Define of left corner coordinates 
x = (screen_width - app_width) / 2
y = ((screen_height - app_height)-50) / 2

# Applying defineded parameters to root.geometry
main.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}')

main.resizable(False, False)
main.configure(background="white")

def create_pdf():
    pdf_reader = Toplevel(main)
        
    pdf_reader.title("PDF Viewer")

    # Main window sizes
    app_height = 700
    app_width = 900

    # Define of screen sizes
    screen_width = pdf_reader.winfo_screenwidth()
    screen_height = pdf_reader.winfo_screenheight()

    # Define of left corner coordinates 
    x = (screen_width - app_width) / 2
    y = ((screen_height - app_height)-50) / 2

    # Applying defineded parameters to root.geometry
    pdf_reader.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}')

    pdf_reader.resizable(False, False)
    pdf_reader.configure(background="white")
    
    buffer = BytesIO()
    
    c = canvas.Canvas(buffer, pagesize=landscape(A4), bottomup=0)
    c.drawString(400,300,"Sample PDF file")
    c.save()
    
    buffer.seek(0)
    pdf_path = buffer.getvalue()
    #print(pdf_path)
    
    # creating object of ShowPdf from tkPDFViewer.
    v1 = pdf.ShowPdf()
    
    # Adding pdf location and width and height.
    v2 = v1.pdf_view(pdf_reader, pdf_location = pdf_path, width = 50, height = 100, bar=True, load="before")
    
    # Placing Pdf in my gui.
    v2.pack()
    
    pdf_reader.mainloop()

create_btn = Button(main, text="Create PDF file", command=create_pdf)
create_btn.pack(pady=135)


main.mainloop()

But maybe my choice was wrong and "tkpdfviewer" is not a good variant to use, because it can't show PDF files from bytes? I have a following issue:

Exception in thread Thread-1 (add_img):
Traceback (most recent call last):
  File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\threading.py", line 975, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\site-packages\tkPDFViewer\tkPDFViewer.py", line 43, in add_img
    open_pdf = fitz.open(pdf_location)
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\User\AppData\Local\Programs\Python\Python311\Lib\site-packages\fitz\fitz.py", line 3925, in __init__
    raise TypeError(msg)
TypeError: bad filename

"tkpdfviewer" is running, but didn't showing anything.

Could someone help me to solve it? My idea is to create PDF file and show it without saving on computer.


Solution

  • According to @acw1668 answer I made some changes with main "tkpdfviewer" library and now it works fine!

    A few changes in code:

    This line:

    def pdf_view(self,master,width=1200,height=600,pdf_location="",bar=True,load="after"):
    

    With this line:

    def pdf_view(self,master,width=1200,height=600,mem_area="",bar=True,load="after"):
    

    In function:

    def add_img():
    

    I have changed these lines of code:

    precentage_dicide = 0
    open_pdf = fitz.open(pdf_location)
    
    for page in open_pdf:
        pix = page.getPixmap()
        pix1 = fitz.Pixmap(pix,0) if pix.alpha else pix
        img = pix1.getImageData("ppm")
    

    With this:

    precentage_dicide = 0
    open_pdf = fitz.open("pdf", mem_area)
    
    for page in open_pdf:
        pix = page.get_pixmap()
        pix1 = fitz.Pixmap(pix,0) if pix.alpha else pix
        img = pix1.tobytes("ppm")