]> Pileus Git - ~andy/gtk/blobdiff - modules/printbackends/file/gtkprintbackendfile.c
file printbackend: Protect a callback by GDK_THREADS_ENTER
[~andy/gtk] / modules / printbackends / file / gtkprintbackendfile.c
index a83d5bc05d643f5ac4ab6723a1b34cca6f0ee567..fdb36b6dbc75237c6ebf8d0d940493cd612ce0ef 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <config.h>
+#include "config.h"
 
 #include <unistd.h>
 #include <sys/types.h>
 #include <cairo.h>
 #include <cairo-pdf.h>
 #include <cairo-ps.h>
+#include <cairo-svg.h>
 
 #include <glib/gi18n-lib.h>
 
-#include "gtkprintoperation.h"
+#include "gtk/gtk.h"
+#include "gtk/gtkprinter-private.h"
 
-#include "gtkprintbackend.h"
 #include "gtkprintbackendfile.h"
 
-#include "gtkprinter.h"
-#include "gtkprinter-private.h"
-
 typedef struct _GtkPrintBackendFileClass GtkPrintBackendFileClass;
 
 #define GTK_PRINT_BACKEND_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_FILE, GtkPrintBackendFileClass))
@@ -67,13 +63,15 @@ typedef enum
 {
   FORMAT_PDF,
   FORMAT_PS,
+  FORMAT_SVG,
   N_FORMATS
 } OutputFormat;
 
 static const gchar* formats[N_FORMATS] =
 {
   "pdf",
-  "ps"
+  "ps",
+  "svg"
 };
 
 static GObjectClass *backend_parent_class;
@@ -104,11 +102,12 @@ static cairo_surface_t *    file_printer_create_cairo_surface      (GtkPrinter
                                                                    GIOChannel              *cache_io);
 
 static GList *              file_printer_list_papers               (GtkPrinter              *printer);
+static GtkPageSetup *       file_printer_get_default_page_size     (GtkPrinter              *printer);
 
 static void
 gtk_print_backend_file_register_type (GTypeModule *module)
 {
-  static const GTypeInfo print_backend_file_info =
+  const GTypeInfo print_backend_file_info =
   {
     sizeof (GtkPrintBackendFileClass),
     NULL,              /* base_init */
@@ -182,6 +181,7 @@ gtk_print_backend_file_class_init (GtkPrintBackendFileClass *class)
   backend_class->printer_get_settings_from_options = file_printer_get_settings_from_options;
   backend_class->printer_prepare_for_print = file_printer_prepare_for_print;
   backend_class->printer_list_papers = file_printer_list_papers;
+  backend_class->printer_get_default_page_size = file_printer_get_default_page_size;
 }
 
 /* return N_FORMATS if no explicit format in the settings */
@@ -213,13 +213,13 @@ output_file_from_settings (GtkPrintSettings *settings,
                           const gchar      *default_format)
 {
   gchar *uri = NULL;
-  
+
   if (settings)
     uri = g_strdup (gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI));
 
   if (uri == NULL)
     { 
-      const gchar *extension;
+      const gchar *extension, *basename, *output_dir;
       gchar *name, *locale_name, *path;
 
       if (default_format)
@@ -229,22 +229,57 @@ output_file_from_settings (GtkPrintSettings *settings,
           OutputFormat format;
 
           format = format_from_settings (settings);
-          extension = format == FORMAT_PS ? "ps" : "pdf";
+          switch (format)
+            {
+              default:
+              case FORMAT_PDF:
+                extension = "pdf";
+                break;
+              case FORMAT_PS:
+                extension = "ps";
+                break;
+              case FORMAT_SVG:
+                extension = "svg";
+                break;
+            }
         }
-      /* default filename used for print-to-file */ 
-      name = g_strdup_printf (_("output.%s"), extension);
+
+      basename = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME);
+      if (basename == NULL)
+        basename = _("output");
+
+      name = g_strconcat (basename, ".", extension, NULL);
+
       locale_name = g_filename_from_utf8 (name, -1, NULL, NULL, NULL);
       g_free (name);
 
       if (locale_name != NULL)
-        {
-          path = g_build_filename (g_get_current_dir (), locale_name, NULL);
-          g_free (locale_name);
+        {      
+          output_dir = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_DIR);
+          if (output_dir == NULL)
+            {
+              const gchar *document_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+
+              if (document_dir == NULL)
+                {
+                  gchar *current_dir = g_get_current_dir ();
+                  path = g_build_filename (current_dir, locale_name, NULL);
+                  g_free (current_dir);
+                }
+              else
+                path = g_build_filename (document_dir, locale_name, NULL);
+
+              uri = g_filename_to_uri (path, NULL, NULL); 
+           }
+          else
+            {
+              path = g_build_filename (output_dir, locale_name, NULL);
+              uri = g_filename_to_uri (path, NULL, NULL);
+            }
 
-          uri = g_filename_to_uri (path, NULL, NULL);
-          g_free (path);
-       }
+          g_free (path); 
+          g_free (locale_name);
+        }
     }
 
   return uri;
@@ -297,16 +332,31 @@ file_printer_create_cairo_surface (GtkPrinter       *printer,
 {
   cairo_surface_t *surface;
   OutputFormat format;
+  const cairo_svg_version_t *versions;
+  int num_versions = 0;
 
   format = format_from_settings (settings);
 
-  if (format == FORMAT_PS)
-    surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height);
-  else
-    surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height);
+  switch (format)
+    {
+      default:
+      case FORMAT_PDF:
+        surface = cairo_pdf_surface_create_for_stream (_cairo_write, cache_io, width, height);
+        break;
+      case FORMAT_PS:
+        surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height);
+        break;
+      case FORMAT_SVG:
+        surface = cairo_svg_surface_create_for_stream (_cairo_write, cache_io, width, height);
+        cairo_svg_get_versions (&versions, &num_versions);
+        if (num_versions > 0)
+          cairo_svg_surface_restrict_to_version (surface, versions[num_versions - 1]);
+        break;
+    }
 
-  /* TODO: DPI from settings object? */
-  cairo_surface_set_fallback_resolution (surface, 300, 300);
+  cairo_surface_set_fallback_resolution (surface,
+                                         2.0 * gtk_print_settings_get_printer_lpi (settings),
+                                         2.0 * gtk_print_settings_get_printer_lpi (settings));
 
   return surface;
 }
@@ -315,22 +365,21 @@ typedef struct {
   GtkPrintBackend *backend;
   GtkPrintJobCompleteFunc callback;
   GtkPrintJob *job;
-  GIOChannel *target_io;
+  GFileOutputStream *target_io_stream;
   gpointer user_data;
   GDestroyNotify dnotify;
 } _PrintStreamData;
 
+/* expects GDK lock to be held */
 static void
-file_print_cb (GtkPrintBackendFile *print_backend,
-               GError              *error,
-               gpointer            user_data)
+file_print_cb_locked (GtkPrintBackendFile *print_backend,
+                      GError              *error,
+                      gpointer            user_data)
 {
   _PrintStreamData *ps = (_PrintStreamData *) user_data;
 
-  GDK_THREADS_ENTER ();
-
-  if (ps->target_io != NULL)
-    g_io_channel_unref (ps->target_io);
+  if (ps->target_io_stream != NULL)
+    g_output_stream_close (G_OUTPUT_STREAM (ps->target_io_stream), NULL, NULL);
 
   if (ps->callback)
     ps->callback (ps->job, ps->user_data, error);
@@ -343,8 +392,18 @@ file_print_cb (GtkPrintBackendFile *print_backend,
 
   if (ps->job)
     g_object_unref (ps->job);
+
   g_free (ps);
+}
+
+static void
+file_print_cb (GtkPrintBackendFile *print_backend,
+               GError              *error,
+               gpointer            user_data)
+{
+  GDK_THREADS_ENTER ();
+
+  file_print_cb_locked (print_backend, error, user_data);
 
   GDK_THREADS_LEAVE ();
 }
@@ -362,7 +421,7 @@ file_write (GIOChannel   *source,
 
   error = NULL;
 
-  read_status = 
+  read_status =
     g_io_channel_read_chars (source,
                              buf,
                              _STREAM_MAX_CHUNK_SIZE,
@@ -373,11 +432,12 @@ file_write (GIOChannel   *source,
     {
       gsize bytes_written;
 
-      g_io_channel_write_chars (ps->target_io, 
-                                buf, 
-                               bytes_read, 
-                               &bytes_written, 
-                               &error);
+      g_output_stream_write_all (G_OUTPUT_STREAM (ps->target_io_stream),
+                                 buf,
+                                 bytes_read,
+                                 &bytes_written,
+                                 NULL,
+                                 &error);
     }
 
   if (error != NULL || read_status == G_IO_STATUS_EOF)
@@ -410,12 +470,11 @@ gtk_print_backend_file_print_stream (GtkPrintBackend        *print_backend,
                                     GDestroyNotify          dnotify)
 {
   GError *internal_error = NULL;
-  GtkPrinter *printer;
   _PrintStreamData *ps;
   GtkPrintSettings *settings;
-  gchar *uri, *filename;
+  gchar *uri;
+  GFile *file = NULL;
 
-  printer = gtk_print_job_get_printer (job);
   settings = gtk_print_job_get_settings (job);
 
   ps = g_new0 (_PrintStreamData, 1);
@@ -427,24 +486,21 @@ gtk_print_backend_file_print_stream (GtkPrintBackend        *print_backend,
 
   internal_error = NULL;
   uri = output_file_from_settings (settings, NULL);
-  filename = g_filename_from_uri (uri, NULL, &internal_error);
-  g_free (uri);
 
-  if (filename == NULL)
+  if (uri == NULL)
     goto error;
 
-  ps->target_io = g_io_channel_new_file (filename, "w", &internal_error);
-
-  g_free (filename);
+  file = g_file_new_for_uri (uri);
+  ps->target_io_stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &internal_error);
 
-  if (internal_error == NULL)
-    g_io_channel_set_encoding (ps->target_io, NULL, &internal_error);
+  g_object_unref (file);
+  g_free (uri);
 
 error:
   if (internal_error != NULL)
     {
-      file_print_cb (GTK_PRINT_BACKEND_FILE (print_backend),
-                    internal_error, ps);
+      file_print_cb_locked (GTK_PRINT_BACKEND_FILE (print_backend),
+                            internal_error, ps);
 
       g_error_free (internal_error);
       return;
@@ -465,10 +521,11 @@ gtk_print_backend_file_init (GtkPrintBackendFile *backend)
                          "name", _("Print to File"),
                          "backend", backend,
                          "is-virtual", TRUE,
+                         "accepts-pdf", TRUE,
                          NULL); 
 
   gtk_printer_set_has_details (printer, TRUE);
-  gtk_printer_set_icon_name (printer, "gtk-floppy");
+  gtk_printer_set_icon_name (printer, "document-save");
   gtk_printer_set_is_active (printer, TRUE);
 
   gtk_print_backend_add_printer (GTK_PRINT_BACKEND (backend), printer);
@@ -477,17 +534,63 @@ gtk_print_backend_file_init (GtkPrintBackendFile *backend)
   gtk_print_backend_set_list_done (GTK_PRINT_BACKEND (backend));
 }
 
+typedef struct {
+  GtkPrinter          *printer;
+  GtkPrinterOptionSet *set;
+} _OutputFormatChangedData;
+
+static void
+set_printer_format_from_option_set (GtkPrinter          *printer,
+                                   GtkPrinterOptionSet *set)
+{
+  GtkPrinterOption *format_option;
+  const gchar *value;
+  gint i;
+
+  format_option = gtk_printer_option_set_lookup (set, "output-file-format");
+  if (format_option && format_option->value)
+    {
+      value = format_option->value;
+      if (value)
+        {
+         for (i = 0; i < N_FORMATS; ++i)
+           if (strcmp (value, formats[i]) == 0)
+             break;
+
+         g_assert (i < N_FORMATS);
+
+         switch (i)
+           {
+             case FORMAT_PDF:
+               gtk_printer_set_accepts_pdf (printer, TRUE);
+               gtk_printer_set_accepts_ps (printer, FALSE);
+               break;
+             case FORMAT_PS:
+               gtk_printer_set_accepts_pdf (printer, FALSE);
+               gtk_printer_set_accepts_ps (printer, TRUE);
+               break;
+             case FORMAT_SVG:
+             default:
+               gtk_printer_set_accepts_pdf (printer, FALSE);
+               gtk_printer_set_accepts_ps (printer, FALSE);
+               break;
+           }
+       }
+    }
+}
+
 static void
 file_printer_output_file_format_changed (GtkPrinterOption    *format_option,
-                                         GtkPrinterOptionSet *set)
+                                        gpointer             user_data)
 {
   GtkPrinterOption *uri_option;
   gchar            *base = NULL;
+  _OutputFormatChangedData *data = (_OutputFormatChangedData *) user_data;
 
   if (! format_option->value)
     return;
 
-  uri_option = gtk_printer_option_set_lookup (set,
+  uri_option = gtk_printer_option_set_lookup (data->set,
                                               "gtk-main-page-custom-input");
 
   if (uri_option && uri_option->value)
@@ -527,6 +630,8 @@ file_printer_output_file_format_changed (GtkPrinterOption    *format_option,
       g_free (tmp);
       g_free (base);
     }
+
+  set_printer_format_from_option_set (data->printer, data->set);
 }
 
 static GtkPrinterOptionSet *
@@ -539,13 +644,14 @@ file_printer_get_options (GtkPrinter           *printer,
   GtkPrinterOption *option;
   const gchar *n_up[] = {"1", "2", "4", "6", "9", "16" };
   const gchar *pages_per_sheet = NULL;
-  const gchar *format_names[N_FORMATS] = { N_("PDF"), N_("Postscript") };
+  const gchar *format_names[N_FORMATS] = { N_("PDF"), N_("Postscript"), N_("SVG") };
   const gchar *supported_formats[N_FORMATS];
   gchar *display_format_names[N_FORMATS];
   gint n_formats = 0;
   OutputFormat format;
   gchar *uri;
   gint current_format = 0;
+  _OutputFormatChangedData *format_changed_data;
 
   format = format_from_settings (settings);
 
@@ -587,7 +693,20 @@ file_printer_get_options (GtkPrinter           *printer,
     }
   else
     {
-      current_format = format == FORMAT_PS ? FORMAT_PS : FORMAT_PDF;
+      switch (format)
+        {
+          default:
+          case FORMAT_PDF:
+            current_format = FORMAT_PDF;
+            break;
+          case FORMAT_PS:
+            current_format = FORMAT_PS;
+            break;
+          case FORMAT_SVG:
+            current_format = FORMAT_SVG;            
+            break;
+        }
+
       for (n_formats = 0; n_formats < N_FORMATS; ++n_formats)
         {
          supported_formats[n_formats] = formats[n_formats];
@@ -599,6 +718,7 @@ file_printer_get_options (GtkPrinter           *printer,
 
   option = gtk_printer_option_new ("gtk-main-page-custom-input", _("File"), 
                                   GTK_PRINTER_OPTION_TYPE_FILESAVE);
+  gtk_printer_option_set_activates_default (option, TRUE);
   gtk_printer_option_set (option, uri);
   g_free (uri);
   option->group = g_strdup ("GtkPrintDialogExtension");
@@ -616,9 +736,13 @@ file_printer_get_options (GtkPrinter           *printer,
       gtk_printer_option_set (option, supported_formats[current_format]);
       gtk_printer_option_set_add (set, option);
 
-      g_signal_connect (option, "changed",
-                        G_CALLBACK (file_printer_output_file_format_changed),
-                        set);
+      set_printer_format_from_option_set (printer, set);
+      format_changed_data = g_new (_OutputFormatChangedData, 1);
+      format_changed_data->printer = printer;
+      format_changed_data->set = set;
+      g_signal_connect_data (option, "changed",
+                            G_CALLBACK (file_printer_output_file_format_changed),
+                            format_changed_data, (GClosureNotify)g_free, 0);
 
       g_object_unref (option);
     }
@@ -643,6 +767,10 @@ file_printer_get_settings_from_options (GtkPrinter          *printer,
   option = gtk_printer_option_set_lookup (options, "gtk-n-up");
   if (option)
     gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value);
+
+  option = gtk_printer_option_set_lookup (options, "gtk-n-up-layout");
+  if (option)
+    gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP_LAYOUT, option->value);
 }
 
 static void
@@ -652,26 +780,34 @@ file_printer_prepare_for_print (GtkPrinter       *printer,
                                GtkPageSetup     *page_setup)
 {
   gdouble scale;
+  GtkPrintPages pages;
+  GtkPageRange *ranges;
+  gint n_ranges;
 
-  print_job->print_pages = gtk_print_settings_get_print_pages (settings);
-  print_job->page_ranges = NULL;
-  print_job->num_page_ranges = 0;
-  
-  if (print_job->print_pages == GTK_PRINT_PAGES_RANGES)
-    print_job->page_ranges =
-      gtk_print_settings_get_page_ranges (settings,
-                                         &print_job->num_page_ranges);
-  
-  print_job->collate = gtk_print_settings_get_collate (settings);
-  print_job->reverse = gtk_print_settings_get_reverse (settings);
-  print_job->num_copies = gtk_print_settings_get_n_copies (settings);
+  pages = gtk_print_settings_get_print_pages (settings);
+  gtk_print_job_set_pages (print_job, pages);
+
+  if (pages == GTK_PRINT_PAGES_RANGES)
+    ranges = gtk_print_settings_get_page_ranges (settings, &n_ranges);
+  else
+    {
+      ranges = NULL;
+      n_ranges = 0;
+    }
+
+  gtk_print_job_set_page_ranges (print_job, ranges, n_ranges);
+  gtk_print_job_set_collate (print_job, gtk_print_settings_get_collate (settings));
+  gtk_print_job_set_reverse (print_job, gtk_print_settings_get_reverse (settings));
+  gtk_print_job_set_num_copies (print_job, gtk_print_settings_get_n_copies (settings));
+  gtk_print_job_set_n_up (print_job, gtk_print_settings_get_number_up (settings));
+  gtk_print_job_set_n_up_layout (print_job, gtk_print_settings_get_number_up_layout (settings));
 
   scale = gtk_print_settings_get_scale (settings);
   if (scale != 100.0)
-    print_job->scale = scale/100.0;
+    gtk_print_job_set_scale (print_job, scale / 100.0);
 
-  print_job->page_set = gtk_print_settings_get_page_set (settings);
-  print_job->rotate_to_orientation = TRUE;
+  gtk_print_job_set_page_set (print_job, gtk_print_settings_get_page_set (settings));
+  gtk_print_job_set_rotate (print_job, TRUE);
 }
 
 static GList *
@@ -697,3 +833,11 @@ file_printer_list_papers (GtkPrinter *printer)
 
   return g_list_reverse (result);
 }
+
+static GtkPageSetup *
+file_printer_get_default_page_size (GtkPrinter *printer)
+{
+  GtkPageSetup *result = NULL;
+
+  return result;
+}