Esempio n. 1
0
static PyObject *
_wrap_gnc_html_webkit_show_data(PyObject *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = { "html", "data", "datalen", NULL };
    PyGObject *html;
    char *data;
    int datalen;
    GncHtmlPrivate *priv_html;
    GncHtmlWebkitPrivate *priv;

    //fprintf(stderr,"webkit show data called 1\n");

    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Osi:Gnc.HtmlWebkit.show_data", kwlist, &html, &data, &datalen))
        return NULL;

    //fprintf(stderr,"webkit show data called 2\n");

    // we now call the gnc-html class pointer
    //gnc_html_webkit_show_data(GNC_HTML_WEBKIT(html->obj), data, datalen);

    GncHtml* gself_html = GNC_HTML(html->obj);
    GncHtmlWebkit* gself = GNC_HTML_WEBKIT(html->obj);

    fprintf(stderr,"html self is %p\n",(void *)gself_html);
    fprintf(stderr,"html self is %d\n",GNC_IS_HTML(gself_html));
    fprintf(stderr,"html self class is %p\n",(void *)GNC_HTML_GET_CLASS(gself_html));
    fprintf(stderr,"html self show_data is %p\n",(void *)(GNC_HTML_GET_CLASS(gself_html)->show_data));

    fprintf(stderr,"webkit self is %p\n",(void *)gself);
    fprintf(stderr,"webkit self is %d\n",GNC_IS_HTML(gself));
    fprintf(stderr,"webkit self class is %p\n",(void *)GNC_HTML_WEBKIT_GET_CLASS(gself));
    // so this does not exist - only the parent class GncHtml defines the show_data!!
    //fprintf(stderr,"webkit self show_data is %p\n",(void *)(GNC_HTML_WEBKIT_GET_CLASS(gself)->show_data));

    // this appears to be the way we call should call it rather than
    // direct pointer call
    // great - inspection of code show this only displays stuff if priv->html_string is set
    // note that the dispose function frees this

    priv_html = GNC_HTML_GET_PRIVATE(gself_html);
    fprintf(stderr,"html self priv %p\n",(char *)&(gself_html->priv));
    fprintf(stderr,"html self priv is %p\n",(void *)priv_html);

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(gself);
    priv->html_string = g_strdup(data);

    fprintf(stderr,"html self html struct len %lx\n", sizeof(GncHtml));
    fprintf(stderr,"html self htmlwebkitstruct len %lx\n", sizeof(GncHtmlWebkit));
    fprintf(stderr,"webkit self priv %p\n",(char *)&(gself->priv));
    fprintf(stderr,"webkit self priv offset %llx\n",((char *)((char *)&(gself->priv)-(char *)gself)));
    fprintf(stderr,"webkit self priv is %p\n",(void *)priv);
    fprintf(stderr,"webkit self priv html_string is %p\n",(void *)&(priv->html_string));
    fprintf(stderr,"webkit self priv html_string offset %llx\n",((char *)&(priv->html_string)-(char *)priv));

    gnc_html_show_data( GNC_HTML(gself), data, datalen );

    Py_INCREF(Py_None);
    return Py_None;
}
Esempio n. 2
0
static void
impl_webkit_set_parent( GncHtml* self, GtkWindow* parent )
{
    GncHtmlWebkitPrivate* priv;

    g_return_if_fail( self != NULL );
    g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
    priv->base.parent = GTK_WIDGET(parent);
}
Esempio n. 3
0
static void
webkit_on_url_cb( WebKitWebView* web_view, gchar* title, gchar* url, gpointer data )
{
    GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
    GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);

    DEBUG( "Rollover %s", url ? url : "(null)" );
    g_free( priv->base.current_link );
    priv->base.current_link = g_strdup( url );
    if ( priv->base.flyover_cb )
    {
        (priv->base.flyover_cb)( GNC_HTML(self), url, priv->base.flyover_cb_data );
    }
}
Esempio n. 4
0
static void
impl_webkit_copy_to_clipboard( GncHtml* self )
{
    GncHtmlWebkitPrivate* priv;

    g_return_if_fail( self != NULL );
    g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
    if ( webkit_web_view_can_copy_clipboard( priv->web_view ) )
    {
        webkit_web_view_copy_clipboard( priv->web_view );
    }
}
Esempio n. 5
0
static void
impl_webkit_cancel( GncHtml* self )
{
    GncHtmlWebkitPrivate* priv;

    g_return_if_fail( self != NULL );
    g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);

    /* remove our own references to requests */
    //gnc_http_cancel_requests( priv->http );

    g_hash_table_foreach_remove( priv->base.request_info, webkit_cancel_helper, NULL );
}
Esempio n. 6
0
/********************************************************************
 * gnc_html_button_press_cb
 * mouse button callback (if any)
 ********************************************************************/

#if 0 /* Not Used */
static int
gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event,
                          gpointer user_data )
{
    GncHtmlWebkit* self = GNC_HTML_WEBKIT(user_data);
    GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);

    DEBUG( "Button Press" );
    if ( priv->base.button_cb != NULL )
    {
        (priv->base.button_cb)( GNC_HTML(self), event, priv->base.button_cb_data );
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
Esempio n. 7
0
static void
gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base,
                      gpointer data )
{
    GncHtmlWebkit* self = GNC_HTML_WEBKIT(data);
    GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
    URLType type;
    gchar* location = NULL;
    gchar* label = NULL;

    DEBUG( "Setting base location to %s", base );
    type = gnc_html_parse_url( GNC_HTML(self), base, &location, &label );

    g_free( priv->base.base_location );
    g_free( label );

    priv->base.base_type = type;
    priv->base.base_location = location;
}
Esempio n. 8
0
static void
impl_webkit_reload( GncHtml* self, gboolean force_rebuild )
{
    gnc_html_history_node * n;
    GncHtmlWebkitPrivate* priv;

    g_return_if_fail( self != NULL );
    g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
    n = gnc_html_history_get_current( priv->base.history );

    if ( force_rebuild )
    {
        if ( n != NULL )
            gnc_html_show_url( self, n->type, n->location, n->label, 0 );
    }
    else
        webkit_web_view_reload( priv->web_view );
}
Esempio n. 9
0
static void
gnc_html_webkit_dispose( GObject* obj )
{
    GncHtmlWebkit* self = GNC_HTML_WEBKIT(obj);
    GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);

    if ( priv->web_view != NULL )
    {
        gtk_container_remove( GTK_CONTAINER(priv->base.container),
                              GTK_WIDGET(priv->web_view) );
        priv->web_view = NULL;
    }

    if ( priv->html_string != NULL )
    {
        g_free( priv->html_string );
        priv->html_string = NULL;
    }

    G_OBJECT_CLASS(gnc_html_webkit_parent_class)->dispose( obj );
}
Esempio n. 10
0
static void
impl_webkit_show_data( GncHtml* self, const gchar* data, int datalen )
{
    GncHtmlWebkitPrivate* priv;
#if HAVE(WEBKIT_WEB_VIEW_LOAD_URI)
#define TEMPLATE_REPORT_FILE_NAME "gnc-report-XXXXXX.html"
    int fd;
    gchar* uri;
    gchar *filename;
#endif

    g_return_if_fail( self != NULL );
    g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );

    ENTER( "datalen %d, data %20.20s", datalen, data );

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);

#if HAVE(WEBKIT_WEB_VIEW_LOAD_URI)
    /* Export the HTML to a file and load the file URI.   On Linux, this seems to get around some
       security problems (otherwise, it can complain that embedded images aren't permitted to be
       viewed because they are local resources).  On Windows, this allows the embedded images to
       be viewed (maybe for the same reason as on Linux, but I haven't found where it puts those
       messages. */
    filename = g_build_filename(g_get_tmp_dir(), TEMPLATE_REPORT_FILE_NAME, (gchar *)NULL);
    fd = g_mkstemp( filename );
    impl_webkit_export_to_file( self, filename );
    close( fd );
    uri = g_strdup_printf( "file:///%s", filename );
    g_free(filename);
    DEBUG("Loading uri '%s'", uri);
    webkit_web_view_load_uri( priv->web_view, uri );
    g_free( uri );
#else
    webkit_web_view_load_html_string( priv->web_view, data, BASE_URI_NAME );
#endif

    LEAVE("");
}
Esempio n. 11
0
/**************************************************************
 * gnc_html_export_to_file
 *
 * @param self GncHtmlWebkit object
 * @param filepath Where to write the HTML
 * @return TRUE if successful, FALSE if unsuccessful
 **************************************************************/
static gboolean
impl_webkit_export_to_file( GncHtml* self, const char *filepath )
{
    FILE *fh;
    GncHtmlWebkitPrivate* priv;

    g_return_val_if_fail( self != NULL, FALSE );
    g_return_val_if_fail( GNC_IS_HTML_WEBKIT(self), FALSE );
    g_return_val_if_fail( filepath != NULL, FALSE );

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
    if ( priv->html_string == NULL )
    {
        return FALSE;
    }
    fh = g_fopen( filepath, "w" );
    if ( fh != NULL )
    {
        gint written;
        gint len = strlen( priv->html_string );

        written = fwrite( priv->html_string, 1, len, fh );
        fclose (fh);

        if ( written != len )
        {
            return FALSE;
        }

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
Esempio n. 12
0
static void
impl_webkit_show_url( GncHtml* self, URLType type,
                      const gchar* location, const gchar* label,
                      gboolean new_window_hint )
{
    GncHTMLUrlCB url_handler;
    gboolean new_window;
    GncHtmlWebkitPrivate* priv;

    g_return_if_fail( self != NULL );
    g_return_if_fail( GNC_IS_HTML_WEBKIT(self) );
    g_return_if_fail( location != NULL );

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);

    /* make sure it's OK to show this URL type in this window */
    if ( new_window_hint == 0 )
    {
        if ( priv->base.urltype_cb )
        {
            new_window = !((priv->base.urltype_cb)( type ));
        }
        else
        {
            new_window = FALSE;
        }
    }
    else
    {
        new_window = TRUE;
    }

    if ( !new_window )
    {
        gnc_html_cancel( GNC_HTML(self) );
    }

    if ( gnc_html_url_handlers )
    {
        url_handler = g_hash_table_lookup( gnc_html_url_handlers, type );
    }
    else
    {
        url_handler = NULL;
    }

    if ( url_handler )
    {
        GNCURLResult result;
        gboolean ok;

        result.load_to_stream = FALSE;
        result.url_type = type;
        result.location = NULL;
        result.label = NULL;
        result.base_type = URL_TYPE_FILE;
        result.base_location = NULL;
        result.error_message = NULL;

        ok = url_handler( location, label, new_window, &result );
        if ( !ok )
        {
            if ( result.error_message )
            {
                gnc_error_dialog( priv->base.parent, "%s", result.error_message );
            }
            else
            {
                /* %s is a URL (some location somewhere). */
                gnc_error_dialog( priv->base.parent, _("There was an error accessing %s."), location );
            }

            if ( priv->base.load_cb )
            {
                priv->base.load_cb( GNC_HTML(self), result.url_type,
                                    location, label, priv->base.load_cb_data );
            }
        }
        else if ( result.load_to_stream )
        {
            gnc_html_history_node *hnode;
            const char *new_location;
            const char *new_label;

            new_location = result.location ? result.location : location;
            new_label = result.label ? result.label : label;
            hnode = gnc_html_history_node_new( result.url_type, new_location, new_label );

            gnc_html_history_append( priv->base.history, hnode );

            g_free( priv->base.base_location );
            priv->base.base_type = result.base_type;
            priv->base.base_location =
                g_strdup( extract_base_name( result.base_type, new_location ) );
            DEBUG( "resetting base location to %s",
                   priv->base.base_location ? priv->base.base_location : "(null)" );

            load_to_stream( GNC_HTML_WEBKIT(self), result.url_type,
                            new_location, new_label );

            if ( priv->base.load_cb != NULL )
            {
                priv->base.load_cb( GNC_HTML(self), result.url_type,
                                    new_location, new_label, priv->base.load_cb_data );
            }
        }

        g_free( result.location );
        g_free( result.label );
        g_free( result.base_location );
        g_free( result.error_message );

        return;
    }

    if ( g_strcmp0( type, URL_TYPE_SCHEME ) == 0 )
    {
        gnc_html_open_scm( GNC_HTML_WEBKIT(self), location, label, new_window );

    }
    else if ( g_strcmp0( type, URL_TYPE_JUMP ) == 0 )
    {
        /* Webkit jumps to the anchor on its own */
    }
    else if ( g_strcmp0( type, URL_TYPE_SECURE ) == 0 ||
              g_strcmp0( type, URL_TYPE_HTTP ) == 0 ||
              g_strcmp0( type, URL_TYPE_FILE ) == 0 )
    {

        do
        {
            if ( g_strcmp0( type, URL_TYPE_SECURE ) == 0 )
            {
                if ( !https_allowed() )
                {
                    gnc_error_dialog( priv->base.parent, "%s",
                                      _("Secure HTTP access is disabled. "
                                        "You can enable it in the Network section of "
                                        "the Preferences dialog.") );
                    break;
                }
            }

            if ( g_strcmp0( type, URL_TYPE_HTTP ) == 0 )
            {
                if ( !http_allowed() )
                {
                    gnc_error_dialog( priv->base.parent, "%s",
                                      _("Network HTTP access is disabled. "
                                        "You can enable it in the Network section of "
                                        "the Preferences dialog.") );
                    break;
                }
            }

            priv->base.base_type = type;

            if ( priv->base.base_location != NULL ) g_free( priv->base.base_location );
            priv->base.base_location = extract_base_name( type, location );

            /* FIXME : handle new_window = 1 */
            gnc_html_history_append( priv->base.history,
                                     gnc_html_history_node_new( type, location, label ) );
            load_to_stream( GNC_HTML_WEBKIT(self), type, location, label );

        }
        while ( FALSE );

    }
    else
    {
        PERR( "URLType %s not supported.", type );
    }

    if ( priv->base.load_cb != NULL )
    {
        (priv->base.load_cb)( GNC_HTML(self), type, location, label, priv->base.load_cb_data );
    }
}
Esempio n. 13
0
static void
load_to_stream( GncHtmlWebkit* self, URLType type,
                const gchar* location, const gchar* label )
{
    gchar* fdata = NULL;
    int fdata_len = 0;
    GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);

    DEBUG( "type %s, location %s, label %s", type ? type : "(null)",
           location ? location : "(null)", label ? label : "(null)");

    g_return_if_fail( self != NULL );

    if ( gnc_html_stream_handlers != NULL )
    {
        GncHTMLStreamCB stream_handler;

        stream_handler = g_hash_table_lookup( gnc_html_stream_handlers, type );
        if ( stream_handler )
        {
            gboolean ok = stream_handler( location, &fdata, &fdata_len );

            if ( ok )
            {
                fdata = fdata ? fdata : g_strdup( "" );

                // Until webkitgtk supports download requests, look for "<object classid="
                // indicating the beginning of an embedded graph.  If found, handle it
                if ( g_strstr_len( fdata, -1, "<object classid=" ) != NULL )
                {
                    gchar* new_fdata;
                    new_fdata = handle_embedded_object( self, fdata );
                    g_free( fdata );
                    fdata = new_fdata;
                }

                // Save a copy for export purposes
                if ( priv->html_string != NULL )
                {
                    g_free( priv->html_string );
                }
                priv->html_string = g_strdup( fdata );
                impl_webkit_show_data( GNC_HTML(self), fdata, strlen(fdata) );
//                webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
            }
            else
            {
                fdata = fdata ? fdata :
                        g_strdup_printf( error_404_format,
                                         _(error_404_title), _(error_404_body) );
                webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
            }

            g_free( fdata );

            if ( label )
            {
                while ( gtk_events_pending() )
                {
                    gtk_main_iteration();
                }
                /* No action required: Webkit jumps to the anchor on its own. */
            }

            return;
        }
    }

    do
    {
        if ( !g_strcmp0( type, URL_TYPE_SECURE ) ||
                !g_strcmp0( type, URL_TYPE_HTTP ) )
        {

            if ( !g_strcmp0( type, URL_TYPE_SECURE ) )
            {
                if ( !https_allowed() )
                {
                    gnc_error_dialog( priv->base.parent, "%s",
                                      _("Secure HTTP access is disabled. "
                                        "You can enable it in the Network section of "
                                        "the Preferences dialog."));
                    break;
                }
            }

            if ( !http_allowed() )
            {
                gnc_error_dialog( priv->base.parent, "%s",
                                  _("Network HTTP access is disabled. "
                                    "You can enable it in the Network section of "
                                    "the Preferences dialog."));
            }
            else
            {
                gnc_build_url( type, location, label );
            }

        }
        else
        {
            PWARN( "load_to_stream for inappropriate type\n"
                   "\turl = '%s#%s'\n",
                   location ? location : "(null)",
                   label ? label : "(null)" );
            fdata = g_strdup_printf( error_404_format,
                                     _(error_404_title), _(error_404_body) );
            webkit_web_view_load_html_string( priv->web_view, fdata, BASE_URI_NAME );
            g_free( fdata );
        }

    }
    while ( FALSE );
}
Esempio n. 14
0
/**
 * Prints the current page.
 *
 * If printing on WIN32, in order to prevent the font from being tiny, (see bug #591177),
 * A GtkPrintOperation object needs to be created so that the unit can be set, and then
 * webkit_web_frame_print_full() needs to be called to use that GtkPrintOperation.  On
 * other platforms (specifically linux - not sure about MacOSX), the version of webkit may
 * not contain the function webkit_web_frame_print_full(), so webkit_web_frame_print() is
 * called instead (the font size problem doesn't show up on linux).
 *
 * @param self HTML renderer object
 */
static void
impl_webkit_print( GncHtml* self, const gchar* jobname, gboolean export_pdf )
{
#if !HAVE(WEBKIT_WEB_FRAME_PRINT_FULL)
    extern void webkit_web_frame_print( WebKitWebFrame * frame );
#endif

    gchar *export_filename = NULL;
    GncHtmlWebkitPrivate* priv;
    WebKitWebFrame* frame;
#if HAVE(WEBKIT_WEB_FRAME_PRINT_FULL)
    GtkPrintOperation* op = gtk_print_operation_new();
    GError* error = NULL;
    GtkPrintSettings *print_settings;
#endif

    priv = GNC_HTML_WEBKIT_GET_PRIVATE(self);
    frame = webkit_web_view_get_main_frame( priv->web_view );

#if HAVE(WEBKIT_WEB_FRAME_PRINT_FULL)
    gnc_print_operation_init( op, jobname );
    print_settings = gtk_print_operation_get_print_settings (op);
    if (!print_settings)
    {
        print_settings = gtk_print_settings_new();
        gtk_print_operation_set_print_settings(op, print_settings);
    }
#ifdef G_OS_WIN32
    gtk_print_operation_set_unit( op, GTK_UNIT_POINTS );
#endif

    // Make sure to generate a full export filename
    if (g_str_has_suffix(jobname, ".pdf"))
    {
        export_filename = g_strdup(jobname);
    }
    else
    {
        export_filename = g_strconcat(jobname, ".pdf", NULL);
    }

    // Two different modes of operation. Either export to PDF, or run the
    // normal print dialog
    if (export_pdf)
    {
        GtkWidget *dialog;
        gint result;
        gchar *export_dirname = NULL;
        gchar* basename;

        // Before we save the PDF file, we always as the user for the export
        // file name. We will store the chosen directory in the gtk print settings
        // as well.
        dialog = gtk_file_chooser_dialog_new (_("Export to PDF File"),
                                              NULL,
                                              GTK_FILE_CHOOSER_ACTION_SAVE,
                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                              GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
                                              NULL);
        gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);

        // Does the jobname look like a valid full file path?
        basename = g_path_get_basename(jobname);
        if (strcmp(basename, jobname) != 0)
        {
            gchar *tmp_basename;
            gchar *tmp_dirname = g_path_get_dirname(jobname);

            if (g_file_test(tmp_dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
            {
                // Yes, the jobname starts with a directory name that actually
                // exists. Hence we use this as output directory.
                export_dirname = tmp_dirname;
                tmp_dirname = NULL;

                // As the prefix part of the "jobname" is the directory path, we
                // need to extract the suffix part for the filename.
                tmp_basename = g_path_get_basename(export_filename);
                g_free(export_filename);
                export_filename = tmp_basename;
            }
            g_free(tmp_dirname);
        }
        g_free(basename);

        // Set the output file name from the given jobname
        gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), export_filename);

        // Do we have a stored output directory?
        if (!export_dirname && gtk_print_settings_has_key(print_settings, GNC_GTK_PRINT_SETTINGS_EXPORT_DIR))
        {
            const char* tmp_dirname = gtk_print_settings_get(print_settings,
                                      GNC_GTK_PRINT_SETTINGS_EXPORT_DIR);
            // Only use the directory subsequently if it exists.
            if (g_file_test(tmp_dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
            {
                export_dirname = g_strdup(tmp_dirname);
            }
        }

        // If we have an already existing directory, propose it now.
        if (export_dirname)
        {
            gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), export_dirname);
        }
        g_free(export_dirname);

        result = gtk_dialog_run (GTK_DIALOG (dialog));
        // Weird. In gtk_dialog_run, the gtk code will run a fstat() on the
        // proposed new output filename, which of course fails with "file not
        // found" as this file doesn't exist. It will still show a warning output
        // in the trace file, though.

        if (result == GTK_RESPONSE_ACCEPT)
        {
            // The user pressed "Ok", so use the file name for the actual file output.
            gchar *dirname;
            char *tmp = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
            g_free(export_filename);
            export_filename = tmp;

            // Store the directory part of the file for later
            dirname = g_path_get_dirname(export_filename);
            if (g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
            {
                gtk_print_settings_set(print_settings, GNC_GTK_PRINT_SETTINGS_EXPORT_DIR, dirname);
            }
            g_free(dirname);
        }
        gtk_widget_destroy (dialog);

        if (result != GTK_RESPONSE_ACCEPT)
        {
            // User pressed cancel - no saving of the PDF file here.
            g_free(export_filename);
            g_object_unref( op );
            return;
        }

        // This function expects the full filename including (absolute?) path
        gtk_print_operation_set_export_filename(op, export_filename);

        // Run the "Export to PDF" print operation
        webkit_web_frame_print_full( frame, op, GTK_PRINT_OPERATION_ACTION_EXPORT, &error );
    }
    else
    {

        // Also store this export file name as output URI in the settings
        if (gtk_print_settings_has_key(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI))
        {
            // Get the previous output URI, extract the directory part, and
            // append the current filename.
            const gchar *olduri = gtk_print_settings_get(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI);
            gchar *dirname = g_path_get_dirname(olduri);
            gchar *newuri = (g_strcmp0(dirname, ".") == 0)
                            ? g_strdup(export_filename)
                            : g_build_filename(dirname, export_filename, NULL);
            //g_warning("olduri=%s newuri=%s", olduri, newuri);

            // This function expects the full filename including protocol, path, and name
            gtk_print_settings_set(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI, newuri);

            g_free(newuri);
            g_free(dirname);
        }
        else
        {
            // No stored output URI from the print settings, so just set our export filename
            gtk_print_settings_set(print_settings, GTK_PRINT_SETTINGS_OUTPUT_URI, export_filename);
        }

        // Run the normal printing dialog
        webkit_web_frame_print_full( frame, op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, &error );
    }

    if ( error != NULL )
    {
        GtkWidget* window = gtk_widget_get_toplevel( GTK_WIDGET(priv->web_view) );
        GtkWidget* dialog = gtk_message_dialog_new( gtk_widget_is_toplevel(window) ? GTK_WINDOW(window) : NULL,
                            GTK_DIALOG_DESTROY_WITH_PARENT,
                            GTK_MESSAGE_ERROR,
                            GTK_BUTTONS_CLOSE,
                            "%s", error->message );
        g_error_free( error );

        g_signal_connect( dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
        gtk_widget_show( dialog );
    }

    // Remember to save the printing settings after this print job
    gnc_print_operation_save_print_settings(op);
    g_object_unref( op );
    g_free(export_filename);

#else
    webkit_web_frame_print( frame );
#endif
}