rsvg-view.c 22.4 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 22
#include "librsvg/rsvg-private.h"
#include "librsvg/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

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

Christian Persch's avatar
Christian Persch committed
36
/* RsvgImage */
37

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

Christian Persch's avatar
Christian Persch committed
41 42
typedef struct _RsvgImage       RsvgImage;
typedef struct _RsvgImageClass  RsvgImageClass;
43

Christian Persch's avatar
Christian Persch committed
44 45 46 47
struct _RsvgImage {
    GtkWidget parent_instance;

    cairo_surface_t *surface; /* a cairo image surface */
48 49
};

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

static GType rsvg_image_get_type (void);

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

Christian Persch's avatar
Christian Persch committed
66 67
    gtk_widget_queue_resize (GTK_WIDGET (image));
}
68

Christian Persch's avatar
Christian Persch committed
69
G_DEFINE_TYPE (RsvgImage, rsvg_image, GTK_TYPE_WIDGET);
70

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

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

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

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

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

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

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

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

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

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

Christian Persch's avatar
Christian Persch committed
119 120
  return FALSE;
}
121

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

Christian Persch's avatar
Christian Persch committed
128 129 130 131 132
  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;
}
133

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

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

Christian Persch's avatar
Christian Persch committed
142
  return image;
143 144
}

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

Christian Persch's avatar
Christian Persch committed
151
/* Main */
152

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

    return NULL;
}
161

Christian Persch's avatar
Christian Persch committed
162 163 164 165 166 167 168 169 170 171 172 173 174
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;
};
175

Christian Persch's avatar
Christian Persch committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189
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);
190 191 192
        return NULL;
    }

Christian Persch's avatar
Christian Persch committed
193 194 195 196 197 198 199 200
    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);
201 202 203
        return NULL;
    }

Christian Persch's avatar
Christian Persch committed
204
    cairo_destroy (cr);
205

206 207 208 209 210 211
    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
212
    return surface;
213 214
}

Dom Lachowicz's avatar
Dom Lachowicz committed
215
static void
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
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) {
231
        title = g_strdup_printf ("%s#%s (%s) — %s",
232 233 234 235
                                 info->base_uri, info->id,
                                 zoom_string,
                                 _("SVG Viewer"));
    } else {
236
        title = g_strdup_printf ("%s (%s) — %s",
237 238 239 240 241 242 243 244 245 246 247
                                 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
248
{
249 250 251
    info->x_zoom *= factor;
    info->y_zoom *= factor;

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

254
    set_window_title (info);
Dom Lachowicz's avatar
Dom Lachowicz committed
255 256 257
}

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

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

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

277
static void
278 279 280 281
draw_page (GtkPrintOperation *operation,
		   GtkPrintContext   *context,
		   gint               page_nr,
		   gpointer           user_data)
282
{
283
    ViewerCbInfo *info = (ViewerCbInfo *) user_data;
Christian Persch's avatar
Christian Persch committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
    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;
    }
310

Christian Persch's avatar
Christian Persch committed
311 312 313 314 315 316 317
    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);
318
}
319

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

326
  print = gtk_print_operation_new ();
327

328 329
  g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), info);
  g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), info);
330

331
  (void)gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
332
                                 GTK_WINDOW (info->window), NULL);
333

334
  g_object_unref (print);
335 336
}

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

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

    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;
362 363
}

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

    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
383 384
        surface = rsvg_image_get_surface (info->image);
        if (cairo_surface_write_to_png (surface, filename) != CAIRO_STATUS_SUCCESS) {
385 386 387 388 389
                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
390
                                                 GTK_BUTTONS_CLOSE, "Failed to save");
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

                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);
    }
410 411
}

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

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

424
static void
425
create_popup_menu (ViewerCbInfo * info)
426
{
427 428 429 430 431 432 433
    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) {
434
        menu_item = gtk_menu_item_new_with_label (_("Copy SVG location"));
435 436 437
        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
438
        gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_C,
439 440 441
                                    GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
    }

442
    menu_item = gtk_menu_item_new_with_label (_("Save as PNG"));
443 444 445
    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
446
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_S,
447
                                GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
448

449
    menu_item = gtk_menu_item_new_with_label (_("Print"));
450 451 452
    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
453
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_P, GDK_CONTROL_MASK,
454
                                GTK_ACCEL_VISIBLE);
455

456
    menu_item = gtk_menu_item_new_with_label (_("Zoom In"));
457 458 459
    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
460
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_plus,
461
                                GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
462 463
                                              
    menu_item = gtk_menu_item_new_with_label (_("Zoom Out"));
464 465 466
    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
467
    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_KEY_minus,
468 469 470
                                GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);

    info->popup_menu = popup_menu;
471 472 473
}

static gint
474
button_press_event (GObject * widget, GdkEventButton * event, gpointer user_data)
475
{
476 477 478 479 480 481 482 483 484
    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;
485 486 487
}

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

494 495 496 497 498 499 500 501 502 503
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;
}

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

514
    vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
515 516
    gtk_container_add (GTK_CONTAINER (win), vbox);

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

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

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

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

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

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

545
    /* create toplevel window and set its title */
546

547
    win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
548

549
    populate_window (win, info, surface);
Dom Lachowicz's avatar
Dom Lachowicz committed
550

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

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

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

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

566
    create_popup_menu (info);
567

568 569
    info->window = win;
    gtk_window_add_accel_group (GTK_WINDOW (win), info->accel_group);
570

571
    g_signal_connect (win, "button-press-event", G_CALLBACK (button_press_event), info);
572

573
    gtk_widget_show_all (win);
574

575
    set_window_title (info);
576 577
}

578
int
579 580
main (int argc, char **argv)
{
581
    int retval = 1;
582 583 584 585 586 587 588 589 590 591 592
    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
593
    gboolean keep_aspect_ratio = FALSE;
594
    gboolean unlimited = FALSE;
595
    char *id = NULL;
Christian Persch's avatar
Christian Persch committed
596 597 598
    GInputStream *input;
    GFile *file, *base_file;
    cairo_surface_t *surface;
599 600 601 602

    int from_stdin = 0;
    ViewerCbInfo info;

603 604
    RsvgHandleFlags flags = RSVG_HANDLE_FLAGS_NONE;

605
    char **args = NULL;
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
    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>")},
625 626
        {"id", 0, 0, G_OPTION_ARG_STRING, &id, N_("Only show one node (default: all)"),
         N_("<string>")},
Christian Persch's avatar
Christian Persch committed
627
        {"keep-aspect", 'k', 0, G_OPTION_ARG_NONE, &keep_aspect_ratio,
628
         N_("Preserve the image's aspect ratio"), NULL},
629 630
        {"unlimited", 'u', 0, G_OPTION_ARG_NONE, &unlimited,
         N_("Allow huge SVG files"), NULL},
631 632 633 634 635
        {"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}
    };

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

639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
    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
657 658 659 660
    if (args)
        n_args = g_strv_length (args);
    else
        n_args = 0;
661 662

    if ((!from_stdin) && (n_args != 1)) {
663
        g_printerr (_("No files specified, and not using --stdin\n"));
664 665 666
        return 1;
    }

667 668 669 670 671 672 673
    if (dpi_x <= 0.0) {
        dpi_x = 90.0;
    }

    if (dpi_y <= 0.0) {
        dpi_y = 90.0;
    }
674

675 676 677
    if (unlimited)
        flags |= RSVG_HANDLE_FLAG_UNLIMITED;

Christian Persch's avatar
Christian Persch committed
678 679 680 681 682 683 684 685 686
    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;
687
    } else {
Christian Persch's avatar
Christian Persch committed
688 689
        file = g_file_new_for_commandline_arg (args[0]);
        input = (GInputStream *) g_file_read (file, NULL, &err);
690

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

Christian Persch's avatar
Christian Persch committed
696
        g_object_unref (file);
697 698
    }

Christian Persch's avatar
Christian Persch committed
699 700 701 702 703
    g_strfreev (args);

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

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

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

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

727
    rsvg_handle_set_dpi_x_y (info.handle, dpi_x, dpi_y);
Christian Persch's avatar
Christian Persch committed
728
    rsvg_handle_get_dimensions (info.handle, &info.dimensions);
729

Christian Persch's avatar
Christian Persch committed
730 731 732 733 734 735 736 737 738 739
    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;
    }
740

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

747 748
    retval = 0;

749 750
    info.accel_group = gtk_accel_group_new ();

Christian Persch's avatar
Christian Persch committed
751
    view_surface (&info, surface, bg_color);
752 753 754 755

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

756
  done:
Christian Persch's avatar
Christian Persch committed
757 758 759

    g_free (info.base_uri);
    g_object_unref (info.handle);
760 761

    rsvg_cleanup ();
762

763
    return retval;
764
}