svg-load.c 7.51 KB
Newer Older
Kevin Cozens's avatar
Kevin Cozens committed
1 2 3 4 5
/* This file is an image processing operation for GEGL
 *
 * GEGL is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
6
 * version 3 of the License, or (at your option) any later version.
Kevin Cozens's avatar
Kevin Cozens committed
7 8 9 10 11 12 13
 *
 * GEGL 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
14
 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
Kevin Cozens's avatar
Kevin Cozens committed
15 16 17
 *
 * Copyright 2006 Kevin Cozens <kcozens@cvs.gimp.org>
 */
18 19 20 21 22

#include "config.h"
#include <glib/gi18n-lib.h>


23
#ifdef GEGL_PROPERTIES
Kevin Cozens's avatar
Kevin Cozens committed
24

25
property_file_path (path, _("File"), "")
26 27 28
  description (_("Path of file to load"))
property_uri (uri, _("URI"), "")
  description (_("URI for file to load"))
29

30 31
property_int (width, _("Width"), -1)
  description (_("Width for rendered image"))
32
property_int (height, _("Height"), -1)
33
  description (_("Height for rendered image"))
Kevin Cozens's avatar
Kevin Cozens committed
34 35 36

#else

37
#define GEGL_OP_SOURCE
38
#define GEGL_OP_NAME svg_load
39
#define GEGL_OP_C_SOURCE svg-load.c
Kevin Cozens's avatar
Kevin Cozens committed
40

41
#include <gegl-op.h>
Kevin Cozens's avatar
Kevin Cozens committed
42
#include <cairo.h>
43
#include <gegl-gio-private.h>
Kevin Cozens's avatar
Kevin Cozens committed
44 45
#include <librsvg/rsvg.h>

46 47 48 49 50 51 52 53 54 55 56 57 58 59
typedef struct
{
  GFile *file;

  RsvgHandle *handle;

  const Babl *format;

  gint width;
  gint height;
} Priv;

static void
cleanup (GeglOperation *operation)
60
{
61 62 63 64 65
  GeglProperties *o = GEGL_PROPERTIES (operation);
  Priv *p = (Priv*) o->user_data;

  if (p != NULL)
    {
66 67
      g_clear_object (&p->handle);
      g_clear_object (&p->file);
68 69 70 71

      p->width = p->height = 0;
      p->format = NULL;
    }
72
}
Kevin Cozens's avatar
Kevin Cozens committed
73

74 75
static gboolean
query_svg (GeglOperation *operation)
Kevin Cozens's avatar
Kevin Cozens committed
76
{
77 78 79
  GeglProperties *o = GEGL_PROPERTIES (operation);
  Priv *p = (Priv*) o->user_data;
  RsvgDimensionData dimentions;
80

81
  g_return_val_if_fail (p->handle != NULL, FALSE);
82

83
  rsvg_handle_get_dimensions (p->handle, &dimentions);
84

85
  p->format = babl_format ("R'G'B'A u8");
86

87 88
  p->height = dimentions.height;
  p->width = dimentions.width;
89

90 91
  return TRUE;
}
92

93 94 95 96 97 98 99 100 101 102 103 104
static gint
load_svg (GeglOperation *operation,
          GeglBuffer    *output,
          gint           width,
          gint           height)
{
    GeglProperties    *o = GEGL_PROPERTIES (operation);
    Priv              *p = (Priv*) o->user_data;
    cairo_surface_t   *surface;
    cairo_t           *cr;

    g_return_val_if_fail (p->handle != NULL, -1);
Kevin Cozens's avatar
Kevin Cozens committed
105

106
    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
Kevin Cozens's avatar
Kevin Cozens committed
107 108
    cr = cairo_create (surface);

109
    if (width != p->width || height != p->height)
110 111
      {
        cairo_scale (cr,
112 113
                     (double)width / (double)p->width,
                     (double)height / (double)p->height);
114
      }
115

116
    rsvg_handle_render_cairo (p->handle, cr);
Kevin Cozens's avatar
Kevin Cozens committed
117

118 119
    cairo_surface_flush (surface);

120
    gegl_buffer_set (output,
121 122 123 124 125
                     GEGL_RECTANGLE (0, 0, width, height),
                     0,
                     babl_format ("cairo-ARGB32"),
                     cairo_image_surface_get_data (surface),
                     cairo_image_surface_get_stride (surface));
Kevin Cozens's avatar
Kevin Cozens committed
126 127 128 129 130 131 132

    cairo_destroy (cr);
    cairo_surface_destroy (surface);

    return 0;
}

133 134
static void
prepare (GeglOperation *operation)
Kevin Cozens's avatar
Kevin Cozens committed
135
{
136 137 138 139 140
  GeglProperties *o = GEGL_PROPERTIES (operation);
  Priv *p = (o->user_data) ? o->user_data : g_new0 (Priv, 1);
  GError *error = NULL;
  GInputStream *stream;
  GFile *file = NULL;
Kevin Cozens's avatar
Kevin Cozens committed
141

142
  g_assert (p != NULL);
Kevin Cozens's avatar
Kevin Cozens committed
143

144
  if (p->file != NULL && (o->uri || o->path))
Kevin Cozens's avatar
Kevin Cozens committed
145
    {
146 147 148 149 150 151 152 153 154 155 156
      if (o->uri && strlen (o->uri) > 0)
        file = g_file_new_for_uri (o->uri);
      else if (o->path && strlen (o->path) > 0)
        file = g_file_new_for_path (o->path);
      if (file != NULL)
        {
          if (!g_file_equal (p->file, file))
            cleanup (operation);
          g_object_unref (file);
        }
    }
Kevin Cozens's avatar
Kevin Cozens committed
157

158
  o->user_data = (void*) p;
Kevin Cozens's avatar
Kevin Cozens committed
159

160 161 162 163 164
  if (p->handle == NULL)
    {
      stream = gegl_gio_open_input_stream (o->uri, o->path, &p->file, &error);
      if (stream == NULL)
        {
165 166 167 168 169
          if (error)
            {
              g_warning ("%s", error->message);
              g_error_free (error);
            }
170 171 172 173 174 175 176 177 178
          cleanup (operation);
          return;
        }

      p->handle = rsvg_handle_new_from_stream_sync (stream, p->file,
                                                    RSVG_HANDLE_FLAGS_NONE,
                                                    NULL, &error);
      if (p->handle == NULL)
        {
179
          g_warning ("%s", error->message);
180 181 182 183 184 185 186 187 188 189 190 191
          g_error_free (error);
          cleanup (operation);
          return;
        }

      if (!query_svg (operation))
        {
          g_warning ("could not query SVG image file");
          cleanup (operation);
          return;
        }
    }
Kevin Cozens's avatar
Kevin Cozens committed
192

193 194
  gegl_operation_set_format (operation, "output", p->format);
}
Kevin Cozens's avatar
Kevin Cozens committed
195

196 197 198 199 200 201 202 203
static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
  GeglProperties *o = GEGL_PROPERTIES (operation);
  GeglRectangle result = { 0, 0, 0, 0 };
  Priv *p = (Priv*) o->user_data;
  gint width = o->width;
  gint height = o->height;
Kevin Cozens's avatar
Kevin Cozens committed
204

205 206 207 208
  if (p->handle != NULL)
    {
      if (width < 1)
        width = p->width;
209
      if (height < 1)
210
        height = p->height;
211

212 213
      result.width = width;
      result.height = height;
214 215
    }

216
  return result;
217 218 219 220 221
}

static gboolean
process (GeglOperation       *operation,
         GeglBuffer          *output,
222
         const GeglRectangle *result,
223
         gint                 level)
224
{
225
  GeglProperties *o = GEGL_PROPERTIES (operation);
226 227 228
  Priv *p = (Priv*) o->user_data;
  gint width = o->width;
  gint height = o->height;
229

230 231 232 233 234 235
  if (p->handle != NULL)
  {
    if (width < 1)
      width = p->width;
    if (height < 1)
      height = p->height;
236

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    if (load_svg (operation, output, width, height))
      {
        if (o->uri != NULL && strlen(o->uri) > 0)
          g_warning ("failed to render SVG from %s", o->uri);
        else
          g_warning ("failed to render SVG from %s", o->path);
        return FALSE;
      }

    return TRUE;
  }

  return FALSE;
}

static GeglRectangle
get_cached_region (GeglOperation       *operation,
                   const GeglRectangle *roi)
{
  return get_bounding_box (operation);
}

static void
finalize (GObject *object)
{
  GeglProperties *o = GEGL_PROPERTIES (object);

  if (o->user_data != NULL)
265
    {
266
      cleanup (GEGL_OPERATION (object));
Debarshi Ray's avatar
Debarshi Ray committed
267
      g_clear_pointer (&o->user_data, g_free);
268 269
    }

270
  G_OBJECT_CLASS (gegl_op_parent_class)->finalize (object);
271 272 273
}

static void
274
gegl_op_class_init (GeglOpClass *klass)
275 276 277 278
{
  GeglOperationClass       *operation_class;
  GeglOperationSourceClass *source_class;

279 280
  G_OBJECT_CLASS(klass)->finalize = finalize;

281 282 283 284
  operation_class = GEGL_OPERATION_CLASS (klass);
  source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);

  source_class->process = process;
285
  operation_class->prepare = prepare;
286
  operation_class->get_bounding_box = get_bounding_box;
287
  operation_class->get_cached_region = get_cached_region;
Kevin Cozens's avatar
Kevin Cozens committed
288

289
  gegl_operation_class_set_keys (operation_class,
290 291
    "name",         "gegl:svg-load",
    "title",        _("SVG File Loader"),
292 293 294
    "categories"  , "input",   /* not hidden because it has extra API */
    "description" , _("Load an SVG file using librsvg"),
    NULL);
Kevin Cozens's avatar
Kevin Cozens committed
295

296 297 298 299 300 301 302 303 304
  gegl_operation_handlers_register_loader (
    "image/svg+xml", "gegl:svg-load");
  gegl_operation_handlers_register_loader (
    ".svg", "gegl:svg-load");

  gegl_operation_handlers_register_loader (
    "image/svg+xml-compressed", "gegl:svg-load");
  gegl_operation_handlers_register_loader (
    ".svgz", "gegl:svg-load");
Kevin Cozens's avatar
Kevin Cozens committed
305 306 307
}

#endif