rsvg-view.c 22.3 KB
Newer Older
1 2
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 sts=4 expandtab: */
Emmanuel Pacaud's avatar
Emmanuel Pacaud committed
3
/*
4 5
 * test-display: 
 *
6
 * Copyright (C) 2002-2004 Dom Lachowicz
7 8 9 10 11 12 13 14 15 16
 *
 * This program is released into the PUBLIC DOMAIN, and is meant to be a
 * useful example if how to draw a SVG image inside of a GtkWidget. This 
 * program is free software; you can redistribute it and/or modify it at your
 * will.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
 *
17
 * Simple utility to view a SVG file inside of a GtkWindow
18 19
 */

20
#include "config.h"
21
#include "rsvg-private.h"
22
#include "rsvg-size-callback.h"
23 24 25

#include <stdio.h>
#include <stdlib.h>
Dom Lachowicz's avatar
Dom Lachowicz committed
26
#include <string.h>
27
#include <locale.h>
28

29
#include <gtk/gtk.h>
30
#include <gdk/gdk.h>
31

32 33
#include "rsvg-compat.h"

Christian Persch's avatar
Christian Persch committed
34 35
#if 0 // defined (G_OS_UNIX)
#include <gio/gunixinputstream.h>
36
#endif
37

Christian Persch's avatar
Christian Persch committed
38
/* RsvgImage */
39

Christian Persch's avatar
Christian Persch committed
40 41
#define RSVG_TYPE_IMAGE (rsvg_image_get_type ())
#define RSVG_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSVG_TYPE_IMAGE, RsvgImage))
42

Christian Persch's avatar
Christian Persch committed
43 44
typedef struct _RsvgImage       RsvgImage;
typedef struct _RsvgImageClass  RsvgImageClass;
45

Christian Persch's avatar
Christian Persch committed
46 47 48 49
struct _RsvgImage {
    GtkWidget parent_instance;

    cairo_surface_t *surface; /* a cairo image surface */
50 51
};

Christian Persch's avatar
Christian Persch committed
52 53 54 55 56 57 58 59 60
struct _RsvgImageClass {
    GtkWidgetClass parent_class;
};

static GType rsvg_image_get_type (void);

static void
rsvg_image_take_surface (RsvgImage *image,
                         cairo_surface_t *surface)
61
{
Christian Persch's avatar
Christian Persch committed
62 63 64 65 66
    if (image->surface == surface)
      return;
    if (image->surface)
      cairo_surface_destroy (image->surface);
    image->surface = surface; /* adopted */
67

Christian Persch's avatar
Christian Persch committed
68 69
    gtk_widget_queue_resize (GTK_WIDGET (image));
}
70

Christian Persch's avatar
Christian Persch committed
71
G_DEFINE_TYPE (RsvgImage, rsvg_image, GTK_TYPE_WIDGET);
72

Christian Persch's avatar
Christian Persch committed
73 74 75 76 77
static void
rsvg_image_init (RsvgImage *image)
{
  gtk_widget_set_has_window (GTK_WIDGET (image), FALSE);
}
78

Christian Persch's avatar
Christian Persch committed
79 80 81 82
static void
rsvg_image_finalize (GObject *object)
{
  RsvgImage *image = RSVG_IMAGE (object);
83

Christian Persch's avatar
Christian Persch committed
84 85 86 87 88 89 90 91 92
  rsvg_image_take_surface (image, NULL);
}

static void
rsvg_image_get_preferred_width (GtkWidget *widget,
                                gint      *minimum,
                                gint      *natural)
{
  RsvgImage *image = RSVG_IMAGE (widget);
93

Christian Persch's avatar
Christian Persch committed
94 95
  *minimum = *natural = image->surface ? cairo_image_surface_get_width (image->surface) : 1;
}
96

Christian Persch's avatar
Christian Persch committed
97 98 99 100 101 102
static void
rsvg_image_get_preferred_height (GtkWidget *widget,
                                 gint      *minimum,
                                 gint      *natural)
{
  RsvgImage *image = RSVG_IMAGE (widget);
103

Christian Persch's avatar
Christian Persch committed
104
  *minimum = *natural = image->surface ? cairo_image_surface_get_height (image->surface) : 1;
105 106 107
}

static gboolean
Christian Persch's avatar
Christian Persch committed
108 109
rsvg_image_draw (GtkWidget *widget,
                 cairo_t *cr)
110
{
Christian Persch's avatar
Christian Persch committed
111 112 113 114
  RsvgImage *image = RSVG_IMAGE (widget);

  if (image->surface == NULL)
      return FALSE;
115

Christian Persch's avatar
Christian Persch committed
116 117 118 119
  cairo_save (cr);
  cairo_set_source_surface (cr, image->surface, 0, 0);
  cairo_paint (cr);
  cairo_restore (cr);
120

Christian Persch's avatar
Christian Persch committed
121 122
  return FALSE;
}
123

Christian Persch's avatar
Christian Persch committed
124 125 126 127 128
static void
rsvg_image_class_init (RsvgImageClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
129

Christian Persch's avatar
Christian Persch committed
130 131 132 133 134
  gobject_class->finalize = rsvg_image_finalize;
  widget_class->get_preferred_width = rsvg_image_get_preferred_width;
  widget_class->get_preferred_height = rsvg_image_get_preferred_height;
  widget_class->draw = rsvg_image_draw;
}
135

Christian Persch's avatar
Christian Persch committed
136 137 138 139
static RsvgImage *
rsvg_image_new_take_surface (cairo_surface_t *surface)
{
  RsvgImage *image;
140

Christian Persch's avatar
Christian Persch committed
141 142
  image = g_object_new (RSVG_TYPE_IMAGE, NULL);
  rsvg_image_take_surface (image, surface);
143

Christian Persch's avatar
Christian Persch committed
144
  return image;
145 146
}

Christian Persch's avatar
Christian Persch committed
147 148
static cairo_surface_t *
rsvg_image_get_surface (RsvgImage *image)
149
{
Christian Persch's avatar
Christian Persch committed
150 151
  return image->surface;
}
152

Christian Persch's avatar
Christian Persch committed
153
/* Main */
154

Christian Persch's avatar
Christian Persch committed
155 156 157 158 159 160 161 162
static char *
_rsvg_basename (const char *file)
{
    if (file && *file)
        return g_path_get_basename (file);

    return NULL;
}
163

Christian Persch's avatar
Christian Persch committed
164 165 166 167 168 169 170 171 172 173 174 175 176
typedef struct _ViewerCbInfo ViewerCbInfo;
struct _ViewerCbInfo {
    GtkWidget *window;
    GtkWidget *popup_menu;
    RsvgImage *image;
    RsvgHandle *handle;
    GtkAccelGroup *accel_group;
    char *base_uri;
    char *id;
    RsvgDimensionData dimensions;
    gdouble x_zoom;
    gdouble y_zoom;
};
177

Christian Persch's avatar
Christian Persch committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191
static cairo_surface_t *
render_to_surface (ViewerCbInfo *info)
{
    int width, height;
    cairo_matrix_t matrix;
    cairo_surface_t *surface;
    cairo_t *cr;

    width = ceil ((double) info->dimensions.width * info->x_zoom);
    height = ceil ((double) info->dimensions.height * info->y_zoom);

    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
        cairo_surface_destroy (surface);
192 193 194
        return NULL;
    }

Christian Persch's avatar
Christian Persch committed
195 196 197 198 199 200 201 202
    cr = cairo_create (surface);

    cairo_matrix_init_scale (&matrix, info->x_zoom, info->y_zoom);
    cairo_transform (cr, &matrix);

    if (!rsvg_handle_render_cairo_sub (info->handle, cr, info->id)) {
        cairo_destroy (cr);
        cairo_surface_destroy (surface);
203 204 205
        return NULL;
    }

Christian Persch's avatar
Christian Persch committed
206
    cairo_destroy (cr);
207

208 209 210 211 212 213
    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
        g_printerr ("Error while rendering image: %d\n", cairo_surface_status (surface));
        cairo_surface_destroy (surface);
        return NULL;
    }

Christian Persch's avatar
Christian Persch committed
214
    return surface;
215 216
}

Dom Lachowicz's avatar
Dom Lachowicz committed
217
static void
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
set_window_title (ViewerCbInfo * info)
{
    char *title;
    gchar *zoom_string;

    if (info->x_zoom != info->y_zoom) {
        zoom_string = g_strdup_printf ("%4d%% : %4d%%",
                                       (gint) (info->x_zoom * 100),
                                       (gint) (info->y_zoom * 100));
    } else {
        zoom_string = g_strdup_printf ("%4d%%",
                                       (gint) (info->x_zoom * 100));
    }

    if (info->id) {
233
        title = g_strdup_printf ("%s#%s (%s) — %s",
234 235 236 237
                                 info->base_uri, info->id,
                                 zoom_string,
                                 _("SVG Viewer"));
    } else {
238
        title = g_strdup_printf ("%s (%s) — %s",
239 240 241 242 243 244 245 246 247 248 249
                                 info->base_uri,
                                 zoom_string,
                                 _("SVG Viewer"));
    }
    gtk_window_set_title (GTK_WINDOW (info->window), title);
    g_free (title);
    g_free (zoom_string);
}

static void
zoom_image (ViewerCbInfo * info, gdouble factor)
Dom Lachowicz's avatar
Dom Lachowicz committed
250
{
251 252 253
    info->x_zoom *= factor;
    info->y_zoom *= factor;

Christian Persch's avatar
Christian Persch committed
254
    rsvg_image_take_surface (info->image, render_to_surface (info));
Dom Lachowicz's avatar
Dom Lachowicz committed
255

256
    set_window_title (info);
Dom Lachowicz's avatar
Dom Lachowicz committed
257 258 259
}

static void
260
zoom_in (GObject * ignored, ViewerCbInfo * info)
Dom Lachowicz's avatar
Dom Lachowicz committed
261
{
Christian Persch's avatar
Christian Persch committed
262
    zoom_image (info, sqrt (G_SQRT2));
Dom Lachowicz's avatar
Dom Lachowicz committed
263 264 265
}

static void
266
zoom_out (GObject * ignored, ViewerCbInfo * info)
Dom Lachowicz's avatar
Dom Lachowicz committed
267
{
Christian Persch's avatar
Christian Persch committed
268
    zoom_image (info, 1. / sqrt (G_SQRT2));
269 270
}

271 272 273 274 275
static void
begin_print (GtkPrintOperation *operation,
			 GtkPrintContext   *context,
			 gpointer           user_data)
{
276
	gtk_print_operation_set_n_pages (operation, 1);
277
}
278

279
static void
280 281 282 283
draw_page (GtkPrintOperation *operation,
		   GtkPrintContext   *context,
		   gint               page_nr,
		   gpointer           user_data)
284
{
285
    ViewerCbInfo *info = (ViewerCbInfo *) user_data;
Christian Persch's avatar
Christian Persch committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    cairo_t *cr;
    gdouble page_width, page_height, page_aspect;
    gdouble width, height, aspect;
    cairo_matrix_t matrix;

    cr = gtk_print_context_get_cairo_context (context);
    page_width = gtk_print_context_get_width (context);
    page_height = gtk_print_context_get_height (context);
    page_aspect = page_width / page_height;

    // FIXMEchpe
    rsvg_handle_set_dpi_x_y (info->handle, 
                             gtk_print_context_get_dpi_x(context), 
                             gtk_print_context_get_dpi_y(context));

    width = info->dimensions.width;
    height = info->dimensions.height;
    aspect = width / height;

    if (aspect <= page_aspect) {
        width = page_height * aspect;
        height = page_height;
    } else {
        width = page_width;
        height = page_width / aspect;
    }
312

Christian Persch's avatar
Christian Persch committed
313 314 315 316 317 318 319
    cairo_save (cr);
    cairo_matrix_init_scale (&matrix, 
                             width / info->dimensions.width,
                             height / info->dimensions.height);
    cairo_transform (cr, &matrix);
    rsvg_handle_render_cairo (info->handle, cr);
    cairo_restore (cr);
320
}
321

322 323 324 325 326
static void
print_pixbuf (GObject * ignored, gpointer user_data)
{
  GtkPrintOperation *print;
  ViewerCbInfo *info = (ViewerCbInfo *) user_data;
327

328
  print = gtk_print_operation_new ();
329

330 331
  g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), info);
  g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), info);
332

333
  (void)gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
334
                                 GTK_WINDOW (info->window), NULL);
335

336
  g_object_unref (print);
337 338
}

339
static char *
340
save_file (const char *title, const char *suggested_filename, GtkWidget * parent, int *success)
341
{
342 343 344 345 346 347 348
    GtkWidget *dialog;
    char *filename = NULL;

    *success = 0;
    dialog = gtk_file_chooser_dialog_new (title,
                                          GTK_WINDOW (parent),
                                          GTK_FILE_CHOOSER_ACTION_SAVE,
349 350
                                          _("_Cancel"), GTK_RESPONSE_CANCEL,
                                          _("_Save"), GTK_RESPONSE_ACCEPT, NULL);
351 352 353 354 355 356 357 358 359 360 361 362 363

    if (suggested_filename && *suggested_filename) {
        gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested_filename);
    }

    if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
        filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
        *success = 1;
    }

    gtk_widget_destroy (dialog);

    return filename;
364 365
}

366
static void
367 368
save_pixbuf (GObject * ignored, gpointer user_data)
{
369 370 371
    ViewerCbInfo *info = (ViewerCbInfo *) user_data;
    char *filename, *base_name, *filename_suggestion;
    int success = 0;
Christian Persch's avatar
Christian Persch committed
372
    cairo_surface_t *surface;
373 374 375 376 377 378 379 380 381 382 383 384

    base_name = _rsvg_basename (info->base_uri);
    if (base_name)
        filename_suggestion = g_strdup_printf ("%s.png", base_name);
    else
        filename_suggestion = NULL;

    filename = save_file (_("Save SVG as PNG"), filename_suggestion, info->window, &success);
    g_free (base_name);
    g_free (filename_suggestion);

    if (filename) {
Christian Persch's avatar
Christian Persch committed
385 386
        surface = rsvg_image_get_surface (info->image);
        if (cairo_surface_write_to_png (surface, filename) != CAIRO_STATUS_SUCCESS) {
387 388 389 390 391
                GtkWidget *errmsg;

                errmsg = gtk_message_dialog_new (GTK_WINDOW (info->window),
                                                 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                                 GTK_MESSAGE_WARNING,
Christian Persch's avatar
Christian Persch committed
392
                                                 GTK_BUTTONS_CLOSE, "Failed to save");
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411

                gtk_dialog_run (GTK_DIALOG (errmsg));

                gtk_widget_destroy (errmsg);
        }

        g_free (filename);
    } else if (success) {
        GtkWidget *errmsg;

        errmsg = gtk_message_dialog_new (GTK_WINDOW (info->window),
                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                         GTK_MESSAGE_WARNING,
                                         GTK_BUTTONS_CLOSE, _("No filename given"));
        gtk_window_set_transient_for (GTK_WINDOW (errmsg), GTK_WINDOW (info->window));

        gtk_dialog_run (GTK_DIALOG (errmsg));
        gtk_widget_destroy (errmsg);
    }
412 413
}

414
static void
415
copy_svg_location (GObject * ignored, gpointer user_data)
416
{
417 418
    ViewerCbInfo *info = (ViewerCbInfo *) user_data;
    GtkClipboard *clipboard = NULL;
419

420 421 422 423
    if (info->base_uri) {
        clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
        gtk_clipboard_set_text (clipboard, info->base_uri, -1);
    }
424 425
}

426
static void
427
create_popup_menu (ViewerCbInfo * info)
428
{
429 430 431 432 433 434 435
    GtkWidget *popup_menu;
    GtkWidget *menu_item;

    popup_menu = gtk_menu_new ();
    gtk_menu_set_accel_group (GTK_MENU (popup_menu), info->accel_group);

    if (info->base_uri) {
436
        menu_item = gtk_menu_item_new_with_label (_("Copy SVG location"));
437 438 439
        g_signal_connect (menu_item, "activate", G_CALLBACK (copy_svg_location), info);
        gtk_widget_show (menu_item);
        gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
Christian Persch's avatar
Christian Persch committed
440
        gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_C,
441 442 443
                                    GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
    }

444
    menu_item = gtk_menu_item_new_with_label (_("Save as PNG"));
445 446 447
    g_signal_connect (menu_item, "activate", G_CALLBACK (save_pixbuf), info);
    gtk_widget_show (menu_item);
    gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
Christian Persch's avatar
Christian Persch committed
448
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_S,
449
                                GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
450

451
    menu_item = gtk_menu_item_new_with_label (_("Print"));
452 453 454
    g_signal_connect (menu_item, "activate", G_CALLBACK (print_pixbuf), info);
    gtk_widget_show (menu_item);
    gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
Christian Persch's avatar
Christian Persch committed
455
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_P, GDK_CONTROL_MASK,
456
                                GTK_ACCEL_VISIBLE);
457

458
    menu_item = gtk_menu_item_new_with_label (_("Zoom In"));
459 460 461
    g_signal_connect (menu_item, "activate", G_CALLBACK (zoom_in), info);
    gtk_widget_show (menu_item);
    gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
Christian Persch's avatar
Christian Persch committed
462
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_plus,
463
                                GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
464 465
                                              
    menu_item = gtk_menu_item_new_with_label (_("Zoom Out"));
466 467 468
    g_signal_connect (menu_item, "activate", G_CALLBACK (zoom_out), info);
    gtk_widget_show (menu_item);
    gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
Christian Persch's avatar
Christian Persch committed
469
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_minus,
470 471 472
                                GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

    info->popup_menu = popup_menu;
473 474 475
}

static gint
476
button_press_event (GObject * widget, GdkEventButton * event, gpointer user_data)
477
{
478 479 480 481 482 483 484 485 486
    if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
        ViewerCbInfo *info = (ViewerCbInfo *) user_data;

        gtk_menu_popup (GTK_MENU (info->popup_menu), NULL, NULL,
                        NULL, NULL, event->button, event->time);
        return TRUE;
    }

    return FALSE;
487 488 489
}

static void
490
quit_cb (GtkWidget * win, gpointer unused)
491
{
492 493
    /* exit the main loop */
    gtk_main_quit ();
494 495
}

496 497 498 499 500 501 502 503 504 505
static GtkToolItem *
tool_button_new (const char *icon_name)
{
    GtkToolItem *toolitem;

    toolitem = gtk_tool_button_new (NULL, NULL);
    gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (toolitem), icon_name);
    return toolitem;
}

506
static void
Christian Persch's avatar
Christian Persch committed
507 508
populate_window (GtkWidget * win, 
                 ViewerCbInfo * info, 
509
                 cairo_surface_t *surface /* adopted */)
Dom Lachowicz's avatar
Dom Lachowicz committed
510
{
511 512
    GtkWidget *vbox;
    GtkWidget *scroll;
Christian Persch's avatar
Christian Persch committed
513 514
    GtkWidget *toolbar;
    GtkToolItem *toolitem;
515

516
    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
517 518
    gtk_container_add (GTK_CONTAINER (win), vbox);

Christian Persch's avatar
Christian Persch committed
519 520
    /* create a new image */
    info->image = rsvg_image_new_take_surface (surface);
521

Christian Persch's avatar
Christian Persch committed
522 523
    toolbar = gtk_toolbar_new ();
    gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
524

525
    toolitem = tool_button_new ("zoom-in");
Christian Persch's avatar
Christian Persch committed
526 527
    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, 0);
    g_signal_connect (toolitem, "clicked", G_CALLBACK (zoom_in), info);
528

529
    toolitem = tool_button_new ("zoom-out");
Christian Persch's avatar
Christian Persch committed
530 531
    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, 1);
    g_signal_connect (toolitem, "clicked", G_CALLBACK (zoom_out), info);
532

Christian Persch's avatar
Christian Persch committed
533 534 535
    scroll = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
536
    gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (info->image));
Christian Persch's avatar
Christian Persch committed
537
    gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
Dom Lachowicz's avatar
Dom Lachowicz committed
538 539
}

540
static void
Christian Persch's avatar
Christian Persch committed
541 542
view_surface (ViewerCbInfo * info, 
              cairo_surface_t *surface /* adopted */,
543
              const char *bg_color)
544
{
545
    GtkWidget *win;
546

547
    /* create toplevel window and set its title */
548

549
    win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
550

551
    populate_window (win, info, surface);
Dom Lachowicz's avatar
Dom Lachowicz committed
552

553
    /* exit when 'X' is clicked */
554 555
    g_signal_connect (win, "destroy", G_CALLBACK (quit_cb), NULL);
    g_signal_connect (win, "delete_event", G_CALLBACK (quit_cb), NULL);
556

557 558 559 560
    if (bg_color && strcmp (bg_color, "none") != 0) {
        GdkRGBA rgba;

        if (gdk_rgba_parse (&rgba, bg_color)) {
Christian Persch's avatar
Christian Persch committed
561
            GtkWidget *parent_widget = gtk_widget_get_parent (GTK_WIDGET (info->image));
562

563
            gtk_widget_override_background_color (parent_widget, GTK_STATE_FLAG_NORMAL, &rgba);
564
        } else
565
            g_warning (_("Couldn't parse color '%s'"), bg_color);
566
    }
567

568
    create_popup_menu (info);
569

570 571
    info->window = win;
    gtk_window_add_accel_group (GTK_WINDOW (win), info->accel_group);
572

573
    g_signal_connect (win, "button-press-event", G_CALLBACK (button_press_event), info);
574

575
    gtk_widget_show_all (win);
576

577
    set_window_title (info);
578 579
}

580
int
581 582
main (int argc, char **argv)
{
583
    int retval = 1;
584 585 586 587 588 589 590 591 592 593 594
    GError *err = NULL;
    GOptionContext *g_option_context;
    double x_zoom = 1.0;
    double y_zoom = 1.0;
    double dpi_x = -1.0;
    double dpi_y = -1.0;
    int width = -1;
    int height = -1;
    int bVersion = 0;
    char *bg_color = NULL;
    char *base_uri = NULL;
Christian Persch's avatar
Christian Persch committed
595
    gboolean keep_aspect_ratio = FALSE;
596
    gboolean unlimited = FALSE;
597
    char *id = NULL;
Christian Persch's avatar
Christian Persch committed
598 599 600
    GInputStream *input;
    GFile *file, *base_file;
    cairo_surface_t *surface;
601 602 603 604

    int from_stdin = 0;
    ViewerCbInfo info;

605 606
    RsvgHandleFlags flags = RSVG_HANDLE_FLAGS_NONE;

607
    char **args = NULL;
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
    gint n_args = 0;

    GOptionEntry options_table[] = {
        {"stdin", 's', 0, G_OPTION_ARG_NONE, &from_stdin, N_("Read from stdin instead of a file"),
         NULL},
        {"dpi-x", 'd', 0, G_OPTION_ARG_DOUBLE, &dpi_x, N_("Set the # of Pixels Per Inch"),
         N_("<float>")},
        {"dpi-y", 'p', 0, G_OPTION_ARG_DOUBLE, &dpi_y, N_("Set the # of Pixels Per Inch"),
         N_("<float>")},
        {"x-zoom", 'x', 0, G_OPTION_ARG_DOUBLE, &x_zoom, N_("Set the x zoom factor"),
         N_("<float>")},
        {"y-zoom", 'y', 0, G_OPTION_ARG_DOUBLE, &y_zoom, N_("Set the y zoom factor"),
         N_("<float>")},
        {"width", 'w', 0, G_OPTION_ARG_INT, &width, N_("Set the image's width"), N_("<int>")},
        {"height", 'h', 0, G_OPTION_ARG_INT, &height, N_("Set the image's height"), N_("<int>")},
        {"bg-color", 'b', 0, G_OPTION_ARG_STRING, &bg_color,
         N_("Set the image background color (default: transparent)"), N_("<string>")},
        {"base-uri", 'u', 0, G_OPTION_ARG_STRING, &base_uri, N_("Set the base URI (default: none)"),
         N_("<string>")},
627 628
        {"id", 0, 0, G_OPTION_ARG_STRING, &id, N_("Only show one node (default: all)"),
         N_("<string>")},
Christian Persch's avatar
Christian Persch committed
629
        {"keep-aspect", 'k', 0, G_OPTION_ARG_NONE, &keep_aspect_ratio,
630
         N_("Preserve the image's aspect ratio"), NULL},
631 632
        {"unlimited", 'u', 0, G_OPTION_ARG_NONE, &unlimited,
         N_("Allow huge SVG files"), NULL},
633 634 635 636 637
        {"version", 'v', 0, G_OPTION_ARG_NONE, &bVersion, N_("Show version information"), NULL},
        {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("[FILE...]")},
        {NULL}
    };

638
	/* Set the locale so that UTF-8 filenames work */
639
    setlocale(LC_ALL, "");
640

641
    RSVG_G_TYPE_INIT;
642

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
    info.window = NULL;
    info.popup_menu = NULL;

    g_option_context = g_option_context_new ("- SVG Viewer");
    g_option_context_add_main_entries (g_option_context, options_table, NULL);
    g_option_context_add_group (g_option_context, gtk_get_option_group (TRUE));
    g_option_context_set_help_enabled (g_option_context, TRUE);
    if (!g_option_context_parse (g_option_context, &argc, &argv, NULL)) {
        exit (1);
    }

    g_option_context_free (g_option_context);

    if (bVersion != 0) {
        g_message (_("rsvg-view version %s\n"), VERSION);
        return 0;
    }

Christian Persch's avatar
Christian Persch committed
661 662 663 664
    if (args)
        n_args = g_strv_length (args);
    else
        n_args = 0;
665 666

    if ((!from_stdin) && (n_args != 1)) {
667
        g_printerr (_("No files specified, and not using --stdin\n"));
668 669 670 671 672
        return 1;
    }

    rsvg_set_default_dpi_x_y (dpi_x, dpi_y);

673 674 675
    if (unlimited)
        flags |= RSVG_HANDLE_FLAG_UNLIMITED;

Christian Persch's avatar
Christian Persch committed
676 677 678 679 680 681 682 683 684
    if (from_stdin) {
#if 0 // defined (G_OS_UNIX)
        input = g_unix_input_stream_new (STDIN_FILENO, FALSE);
#else
        input = NULL;
        g_set_error_literal (&err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                             "Reading from stdin not supported");
#endif
        base_file = NULL;
685
    } else {
Christian Persch's avatar
Christian Persch committed
686 687
        file = g_file_new_for_commandline_arg (args[0]);
        input = (GInputStream *) g_file_read (file, NULL, &err);
688

Christian Persch's avatar
Christian Persch committed
689 690 691 692
        if (base_uri)
            base_file = g_file_new_for_uri (base_uri);
        else
            base_file = g_object_ref (file);
693

Christian Persch's avatar
Christian Persch committed
694
        g_object_unref (file);
695 696
    }

Christian Persch's avatar
Christian Persch committed
697 698 699 700 701
    g_strfreev (args);

    if (input == NULL) {
        g_printerr ("Failed to read input: %s\n", err->message);
        g_error_free (err);
702 703 704
        return 1;
    }

Christian Persch's avatar
Christian Persch committed
705
    info.base_uri = base_file ? g_file_get_uri (base_file) : g_strdup ("");
706
    info.id = id;
707 708
    info.x_zoom = x_zoom;
    info.y_zoom = y_zoom;
709

Christian Persch's avatar
Christian Persch committed
710 711
    info.handle = rsvg_handle_new_from_stream_sync (input, 
                                                    base_file, 
712
                                                    flags,
Christian Persch's avatar
Christian Persch committed
713 714
                                                    NULL /* cancellable */,
                                                    &err);
715 716
    if (base_file != NULL)
        g_object_unref (base_file);
717
    g_object_unref (input);
Christian Persch's avatar
Christian Persch committed
718 719 720 721 722

    if (info.handle == NULL) {
        g_printerr ("Failed to load SVG: %s\n", err->message);
        g_error_free (err);
        return 1;
723 724
    }

Christian Persch's avatar
Christian Persch committed
725
    rsvg_handle_get_dimensions (info.handle, &info.dimensions);
726

Christian Persch's avatar
Christian Persch committed
727 728 729 730 731 732 733 734 735 736
    if (width != -1) {
        info.x_zoom = (double) width / info.dimensions.width;
    } else {
        info.x_zoom = x_zoom;
    }
    if (height != -1) {
        info.y_zoom = (double) height / info.dimensions.height;
    } else {
        info.y_zoom = y_zoom;
    }
737

Christian Persch's avatar
Christian Persch committed
738 739
    surface = render_to_surface (&info);
    if (surface == NULL) {
740 741 742
        g_printerr ("Unknown error while rendering image\n");
        goto done;
    }
743

744 745
    retval = 0;

746 747
    info.accel_group = gtk_accel_group_new ();

Christian Persch's avatar
Christian Persch committed
748
    view_surface (&info, surface, bg_color);
749 750 751 752

    /* run the gtk+ main loop */
    gtk_main ();

753
  done:
Christian Persch's avatar
Christian Persch committed
754 755 756

    g_free (info.base_uri);
    g_object_unref (info.handle);
757 758

    rsvg_cleanup ();
759

760
    return retval;
761
}