Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Gtk introspection error with weasyprint using Gtk.PrintOperation #119

Closed
BobBowles opened this Issue Aug 25, 2013 · 7 comments

Comments

Projects
None yet
3 participants

Running the following script in Python3 generates an error traceback when introspecting the print context while rendering the page:

#!/usr/bin/env python3 

import os
from gi.repository import Gtk
from weasyprint import HTML

testFile = os.path.join(os.getcwd(), 'printTestHtml.html')
pdfDocument = HTML(filename=testFile).render()

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()        
        self.init_ui()

    def init_ui(self):    
        self.set_title("Print Html Weasyprint Test")
        self.resize(230, 150)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)

        printButton = Gtk.Button('Press Me')
        self.add(printButton)
        printButton.connect('clicked', self.on_printButton_clicked)

        self.show_all()

    def on_printButton_clicked(self, widget):
        printOperation = Gtk.PrintOperation()
        printOperation.connect('begin-print', self.on_printOperation_begin_print)
        printOperation.connect('draw-page', self.on_printOperation_draw_page)
        printOperation.set_job_name('Print HTML Weasyprint Test')
        printOperation.set_n_pages(len(pdfDocument.pages))
        printOperation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
                           parent=self)

    def on_printOperation_begin_print(self, printOperation, context):
#        printOperation.set_n_pages(len(self.pdfDocument.pages))
        pass

    def on_printOperation_draw_page(self, printOperation, context, pageNo):
        cr = context.get_cairo_context()
        page = pdfDocument.pages[pageNo]
        page.paint(cr)

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

The error in the terminal window is as follows:

Traceback (most recent call last):
  File "printTestHtmlWeasyprint.py", line 42, in on_printOperation_draw_page
    cr = context.get_cairo_context()
  File "/usr/lib/python3/dist-packages/gi/types.py", line 43, in function
    return info.invoke(*args, **kwargs)
TypeError: Couldn't find conversion for foreign struct 'cairo.Context'

The error does not happen if the html is pre-processed into pdf and handled via Poppler, nor does it happen when the html rendering is handled via WebKit, as in this example:

#!/usr/bin/env python3 

import os
from gi.repository import Gtk, WebKit

testFile = 'file://' + os.path.join(os.getcwd(), 'printTestHtml.html')

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()        
        self.init_ui()

    def init_ui(self):    
        self.set_title("Print Html WebKit Test")
        self.resize(230, 150)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)

        printButton = Gtk.Button('Press Me')
        self.add(printButton)
        printButton.connect('clicked', self.on_printButton_clicked)

        self.show_all()

    def on_printButton_clicked(self, widget):
        webView = WebKit.WebView()
        webView.load_uri(testFile)
        webFrame = webView.get_main_frame()
        webFrame.print_full(Gtk.PrintOperation(),
                            Gtk.PrintOperationAction.PRINT_DIALOG)

def main():    
    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()
Member

SimonSapin commented Aug 25, 2013

I believe this is a bug in the introspection data for GtkPrintContext.get_cairo_context(): I can reproduce this even when removing any WeasyPrint related code and printing blank pages. However, a similar program works fine with PyGTK: https://gist.github.com/SimonSapin/3780988

(Note that, additionally, recent versions of WeasyPrint use cairocffi instead of pycairo, so there is a trick to convert contexts between the two.)

Member

SimonSapin commented Aug 25, 2013

(FWIW, this kind of bug is why I got fed up with GObject introspection and now use CFFI.)

I tried adding the cffi workarounds, but the script fails in the same way before it gets to them, at the point of retrieving the cairo context.

The error does not happen in another test program, where I pre-processed the html into a pdf file in a separate program, and loaded that via Poppler:

#!/usr/bin/env python3 

import os
from gi.repository import Gtk, Poppler

testFile = 'file://' + os.path.join(os.getcwd(), 'printTestPdf.pdf')
pdfDocument = Poppler.Document.new_from_file(testFile, None)

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()    
        self.init_ui()

    def init_ui(self):    
        self.set_title("Print Pdf Test")
        self.resize(230, 150)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)

        printButton = Gtk.Button('Press Me')
        self.add(printButton)
        printButton.connect('clicked', self.on_printButton_clicked)

        self.show_all()

    def on_printButton_clicked(self, widget):
        printOperation = Gtk.PrintOperation()
        printOperation.connect('begin-print', self.on_printOperation_begin_print)
        printOperation.connect('draw-page', self.on_printOperation_draw_page)
        printOperation.set_job_name('Print Pdf Test')
        printOperation.set_n_pages(pdfDocument.get_n_pages())
        printOperation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
                           parent=self)

    def on_printOperation_begin_print(self, printOperation, context):
#        printOperation.set_n_pages(pdfDocument.get_n_pages())
        pass

    def on_printOperation_draw_page(self, printOperation, context, pageNo):
        cr = context.get_cairo_context()
        page = pdfDocument.get_page(pageNo)
        page.render_for_printing(cr)

def main():
    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

You can see the code is almost identical to using weasyprint to generate the pdf directly from the html, but no errors occur in the introspection.

Member

SimonSapin commented Aug 25, 2013

I tried adding the cffi workarounds, but the script fails in the same way before it gets to them, at the point of retrieving the cairo context.

Right. It’s a separate issue and you’ll need to solve both.

The error does not happen in another test program, where I pre-processed the html into a pdf file in a separate program, and loaded that via Poppler:

I could not reproduce that. I get the same error. I also get it with the following program, which does not involve WeasyPrint at all (and should print a blank page.)

#!/usr/bin/env python3 

import os
from gi.repository import Gtk

class Example(Gtk.Window):

    def __init__(self):
        super(Example, self).__init__()
        self.init_ui()

    def init_ui(self):
        self.set_title("Print Html Weasyprint Test")
        self.resize(230, 150)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.connect("delete-event", Gtk.main_quit)

        printButton = Gtk.Button('Press Me')
        self.add(printButton)
        printButton.connect('clicked', self.on_printButton_clicked)

        self.show_all()

    def on_printButton_clicked(self, widget):
        printOperation = Gtk.PrintOperation()
        printOperation.connect('begin-print', self.on_printOperation_begin_print)
        printOperation.connect('draw-page', self.on_printOperation_draw_page)
        printOperation.set_job_name('Print HTML Weasyprint Test')
        printOperation.set_n_pages(1)
        printOperation.run(Gtk.PrintOperationAction.PRINT_DIALOG,
                           parent=self)

    def on_printOperation_begin_print(self, printOperation, context):
#        printOperation.set_n_pages(len(self.pdfDocument.pages))
        pass

    def on_printOperation_draw_page(self, printOperation, context, pageNo):
        cr = context.get_cairo_context()
        # Do nothing.

def main():

    app = Example()
    Gtk.main()

if __name__ == "__main__":    
    main()

I could not reproduce that. I get the same error. I also get it with the following program, which does not involve WeasyPrint at all (and should print a blank page.)

I found some interaction with the IDE I was using that affected how the bug appears. Using Eclipse with pyDev seems to produce reliable success/failure, but I found that, using Idle, prior runs with other code were somehow contaminating the results, producing false negatives (failures). Running from the command line is probably the most fool-proof way to get reliable information. Other than that, I agree this seems to be a particularly nasty kind of bug. It is as if there is a failure of encapsulation, maybe some global variable(s) that get fiddled with behind the scenes.

Member

SimonSapin commented Aug 26, 2013

Apparently PyGObject at that point doesn’t know how to convert a cairo_t C struct into a pycairo cairo.Context object. In the WebKit example it doesn’t need to because you never call get_cairo_context() from Python. I don’t know what’s going on with your Poppler sometimes working, but honestly I’m not very interested. GObject introspection is broken is so many way…

At this point I’m convinced that this bug is not related to WeasyPrint, and there is not much I can do to help, sorry. (Other than suggesting again to use CFFI.)

Owner

liZe commented Dec 30, 2013

@BobBowles: Is this problem solved?

@liZe liZe closed this Apr 25, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment