From caa0ba592597dccf7b6980e66ccd7f591c115410 Mon Sep 17 00:00:00 2001 From: Jim Evins Date: Mon, 6 Jan 2003 05:53:19 +0000 Subject: [PATCH] Added pixbuf cache and base64 modules. Pixbufs are now saved in a glabels file as a base64 encoded version of the serialized pixdata. Files are now compressed. git-svn-id: https://glabels.svn.sourceforge.net/svnroot/glabels/trunk@237 f5e0f49d-192f-0410-a22d-a8d8700d0965 --- glabels2/src/Makefile.am | 8 ++ glabels2/src/base64.c | 201 +++++++++++++++++++++++++++++ glabels2/src/base64.h | 41 ++++++ glabels2/src/debug.c | 2 + glabels2/src/debug.h | 3 + glabels2/src/glabels.c | 3 + glabels2/src/label-image.c | 129 +++++++++++-------- glabels2/src/label.c | 33 +++-- glabels2/src/label.h | 3 + glabels2/src/pixbuf-cache.c | 250 ++++++++++++++++++++++++++++++++++++ glabels2/src/pixbuf-cache.h | 50 ++++++++ glabels2/src/view.c | 26 ++-- glabels2/src/xml-label.c | 173 ++++++++++++++++++++++++- 13 files changed, 839 insertions(+), 83 deletions(-) create mode 100644 glabels2/src/base64.c create mode 100644 glabels2/src/base64.h create mode 100644 glabels2/src/pixbuf-cache.c create mode 100644 glabels2/src/pixbuf-cache.h diff --git a/glabels2/src/Makefile.am b/glabels2/src/Makefile.am index 513761fb..4f06b4de 100644 --- a/glabels2/src/Makefile.am +++ b/glabels2/src/Makefile.am @@ -118,6 +118,10 @@ glabels_SOURCES = \ xml-label.h \ xml-label-04.c \ xml-label-04.h \ + pixbuf-cache.c \ + pixbuf-cache.h \ + base64.c \ + base64.h \ merge.c \ merge.h \ merge-init.c \ @@ -208,6 +212,10 @@ glabels_batch_SOURCES = \ xml-label.h \ xml-label-04.c \ xml-label-04.h \ + pixbuf-cache.c \ + pixbuf-cache.h \ + base64.c \ + base64.h \ merge.c \ merge.h \ merge-init.c \ diff --git a/glabels2/src/base64.c b/glabels2/src/base64.c new file mode 100644 index 00000000..957b46a8 --- /dev/null +++ b/glabels2/src/base64.c @@ -0,0 +1,201 @@ +/* + * (GLABELS) Label and Business Card Creation program for GNOME + * + * base64.c: GLabels base64 encode/decode module + * + * Copyright (C) 2003 Jim Evins + * + * This module is based on base64.c from fetchmail: + * + * Copyright (C)2002 by Eric S. Raymond. + * Portions are copyrighted by Carl E. Harris and George M. Sipe. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * This base 64 encoding is defined in RFC2045 section 6.8. + */ +#include + +#include +#include + +#include "base64.h" + +/*========================================================*/ +/* Private macros and constants. */ +/*========================================================*/ + +#define LINE_LENGTH 76 /* Must be <= 76 and must be a multiple of 4 */ +#define BAD -1 + +#define DECODE64(c) (isascii(c) ? base64val[c] : BAD) + +/*========================================================*/ +/* Private types. */ +/*========================================================*/ + +/*========================================================*/ +/* Private globals. */ +/*========================================================*/ + +static const gchar base64digits[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const gchar base64val[] = { + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD, BAD,BAD,BAD,BAD, BAD,BAD,BAD, 62, BAD,BAD,BAD, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,BAD,BAD, BAD,BAD,BAD,BAD, + BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,BAD, BAD,BAD,BAD,BAD, + BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,BAD, BAD,BAD,BAD,BAD +}; + +/*========================================================*/ +/* Private function prototypes. */ +/*========================================================*/ + + +/*****************************************************************************/ +/* Encode to Base64 string. */ +/*****************************************************************************/ +gchar * +gl_base64_encode (const guchar *in, guint inlen) +{ + gchar *out, *p_out; + gint buf_size; + gint i; + + /* Calculate output buffer size */ + buf_size = 4*((inlen+2)/3); /* Encoded characters */ + buf_size += buf_size / LINE_LENGTH + 2; /* Line breaks */ + buf_size += 1; /* null termination */ + + /* Allocate output buffer */ + out = g_new0 (gchar, buf_size); + p_out=out; + + /* Now do the encoding */ + *p_out++ = '\n'; + for ( i=0; inlen >= 3; inlen-=3 ) { + + *p_out++ = base64digits[in[0] >> 2]; + *p_out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *p_out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *p_out++ = base64digits[in[2] & 0x3f]; + in += 3; + + i += 4; + if ( (i % LINE_LENGTH) == 0 ) { + *p_out++ = '\n'; + } + + } + if (inlen > 0) { + guchar fragment; + + *p_out++ = base64digits[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + if (inlen > 1) + fragment |= in[1] >> 4; + *p_out++ = base64digits[fragment]; + *p_out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; + *p_out++ = '='; + + *p_out++ = '\n'; + } + *p_out++ = '\0'; + + return out; +} + +/*****************************************************************************/ +/* Decode from a Base64 string. */ +/*****************************************************************************/ +guchar * +gl_base64_decode (const gchar *in, guint *outlen) +{ + gchar *out, *p_out; + gint buf_size; + register guchar digit1, digit2, digit3, digit4; + + /* Calculate output buffer size */ + buf_size = strlen (in) * 3 / 4; + + /* Allocate output buffer */ + out = g_new0 (gchar, buf_size); + + *outlen = 0; + p_out = out; + + /* Skip non-printable characters */ + while ( (*in == '\n') || (*in == '\r') || (*in == ' ') ) { + in ++; + } + if (!*in) { + g_free (out); + return NULL; + } + + /* Now do the decoding */ + do { + digit1 = in[0]; + if (DECODE64(digit1) == BAD) { + g_free (out); + return NULL; + } + digit2 = in[1]; + if (DECODE64(digit2) == BAD) { + g_free (out); + return NULL; + } + digit3 = in[2]; + if (digit3 != '=' && DECODE64(digit3) == BAD) { + g_free (out); + return NULL; + } + digit4 = in[3]; + if (digit4 != '=' && DECODE64(digit4) == BAD) { + g_free (out); + return NULL; + } + in += 4; + + *p_out++ = (DECODE64(digit1)<<2) | (DECODE64(digit2) >> 4); + (*outlen)++; + if (digit3 != '=') + { + *p_out++ = ((DECODE64(digit2)<<4)&0xf0) | (DECODE64(digit3)>>2); + (*outlen)++; + if (digit4 != '=') + { + *p_out++ = ((DECODE64(digit3)<<6)&0xc0) | DECODE64(digit4); + (*outlen)++; + } + } + + /* Skip non-printable characters */ + while ( (*in == '\n') || (*in == '\r') || (*in == ' ') ) { + in ++; + } + + } while (*in && digit4 != '='); + + return out; +} + diff --git a/glabels2/src/base64.h b/glabels2/src/base64.h new file mode 100644 index 00000000..a6869492 --- /dev/null +++ b/glabels2/src/base64.h @@ -0,0 +1,41 @@ +/* + * (GLABELS) Label and Business Card Creation program for GNOME + * + * base64.h: GLabels base64 encode/decode module + * + * Copyright (C) 2003 Jim Evins + * + * This module is based on base64.c from fetchmail: + * + * Copyright (C)2002 by Eric S. Raymond. + * Portions are copyrighted by Carl E. Harris and George M. Sipe. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BASE64_H__ +#define __BASE64_H__ + +G_BEGIN_DECLS + +gchar *gl_base64_encode (const guchar *in, + guint inlen); + +guchar *gl_base64_decode (const gchar *in, + guint *outlen); + +G_END_DECLS + +#endif + diff --git a/glabels2/src/debug.c b/glabels2/src/debug.c index ddf0677b..20bf9309 100644 --- a/glabels2/src/debug.c +++ b/glabels2/src/debug.c @@ -49,6 +49,7 @@ gint gl_debug_window = 0; gint gl_debug_ui = 0; gint gl_debug_media_select = 0; gint gl_debug_mini_preview = 0; +gint gl_debug_pixbuf_cache = 0; gint gl_debug_wdgt = 0; /****************************************************************************/ @@ -88,6 +89,7 @@ gl_debug (gint section, (gl_debug_ui && section == GL_DEBUG_UI) || (gl_debug_media_select && section == GL_DEBUG_MEDIA_SELECT) || (gl_debug_mini_preview && section == GL_DEBUG_MINI_PREVIEW) || + (gl_debug_pixbuf_cache && section == GL_DEBUG_PIXBUF_CACHE) || (gl_debug_wdgt && section == GL_DEBUG_WDGT) ) g_print ("%s:%d (%s) %s\n", file, line, function, msg); diff --git a/glabels2/src/debug.h b/glabels2/src/debug.h index 485b54bf..e2ac1358 100644 --- a/glabels2/src/debug.h +++ b/glabels2/src/debug.h @@ -49,6 +49,7 @@ typedef enum { GL_DEBUG_UI, GL_DEBUG_MEDIA_SELECT, GL_DEBUG_MINI_PREVIEW, + GL_DEBUG_PIXBUF_CACHE, GL_DEBUG_WDGT, } glDebugSection; @@ -69,6 +70,7 @@ extern gint gl_debug_window; extern gint gl_debug_ui; extern gint gl_debug_media_select; extern gint gl_debug_mini_preview; +extern gint gl_debug_pixbuf_cache; extern gint gl_debug_wdgt; #ifndef __GNUC__ @@ -91,6 +93,7 @@ extern gint gl_debug_wdgt; #define DEBUG_UI GL_DEBUG_UI, __FILE__, __LINE__, __FUNCTION__ #define DEBUG_MEDIA_SELECT GL_DEBUG_MEDIA_SELECT, __FILE__, __LINE__, __FUNCTION__ #define DEBUG_MINI_PREVIEW GL_DEBUG_MINI_PREVIEW, __FILE__, __LINE__, __FUNCTION__ +#define DEBUG_PIXBUF_CACHE GL_DEBUG_PIXBUF_CACHE, __FILE__, __LINE__, __FUNCTION__ #define DEBUG_WDGT GL_DEBUG_WDGT, __FILE__, __LINE__, __FUNCTION__ void gl_debug (gint section, gchar *file, diff --git a/glabels2/src/glabels.c b/glabels2/src/glabels.c index d64a8053..150a3f8e 100644 --- a/glabels2/src/glabels.c +++ b/glabels2/src/glabels.c @@ -95,6 +95,9 @@ static const struct poptOption options [] = { "debug-mini-preview", '\0', POPT_ARG_NONE, &gl_debug_mini_preview, 0, N_("Show mini preview widget debugging messages."), NULL }, + { "debug-pixbuf-cache", '\0', POPT_ARG_NONE, &gl_debug_pixbuf_cache, 0, + N_("Show pixbuf cache debugging messages."), NULL }, + { "debug-wdgt", '\0', POPT_ARG_NONE, &gl_debug_wdgt, 0, N_("Show widget debugging messages."), NULL }, diff --git a/glabels2/src/label-image.c b/glabels2/src/label-image.c index 8c43f82a..1ab49aa4 100644 --- a/glabels2/src/label-image.c +++ b/glabels2/src/label-image.c @@ -45,6 +45,8 @@ static GObjectClass *parent_class = NULL; static guint instance = 0; +static GdkPixbuf *default_pixbuf = NULL; + /*========================================================*/ /* Private function prototypes. */ /*========================================================*/ @@ -101,25 +103,36 @@ gl_label_image_class_init (glLabelImageClass *klass) static void gl_label_image_instance_init (glLabelImage *limage) { + if ( default_pixbuf == NULL ) { + default_pixbuf = + gdk_pixbuf_new_from_xpm_data ((const char **)checkerboard_xpm); + } + limage->private = g_new0 (glLabelImagePrivate, 1); limage->private->filename = g_new0 (glTextNode, 1); - limage->private->pixbuf = - gdk_pixbuf_new_from_xpm_data ((const char **)checkerboard_xpm); + limage->private->pixbuf = default_pixbuf; } static void gl_label_image_finalize (GObject *object) { - glLabelImage *limage; + glLabelObject *lobject; + glLabelImage *limage; + GHashTable *pixbuf_cache; g_return_if_fail (object && GL_IS_LABEL_IMAGE (object)); - limage = GL_LABEL_IMAGE (object); + lobject = GL_LABEL_OBJECT (object); + limage = GL_LABEL_IMAGE (object); + if (!limage->private->filename->field_flag) { + pixbuf_cache = gl_label_get_pixbuf_cache (lobject->parent); + gl_pixbuf_cache_remove_pixbuf (pixbuf_cache, + limage->private->filename->data); + } gl_text_node_free (&limage->private->filename); - g_object_unref (G_OBJECT(limage->private->pixbuf)); g_free (limage->private); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -147,9 +160,11 @@ static void copy (glLabelObject *dst_object, glLabelObject *src_object) { - glLabelImage *limage = (glLabelImage *)src_object; - glLabelImage *new_limage = (glLabelImage *)dst_object; - glTextNode *filename; + glLabelImage *limage = (glLabelImage *)src_object; + glLabelImage *new_limage = (glLabelImage *)dst_object; + glTextNode *filename; + GdkPixbuf *pixbuf; + GHashTable *pixbuf_cache; gl_debug (DEBUG_LABEL, "START"); @@ -157,6 +172,16 @@ copy (glLabelObject *dst_object, g_return_if_fail (new_limage && GL_IS_LABEL_IMAGE (new_limage)); filename = gl_label_image_get_filename (limage); + + /* Make sure destination label has data suitably cached. */ + if ( !filename->field_flag && (filename->data != NULL) ) { + pixbuf = limage->private->pixbuf; + if ( pixbuf != default_pixbuf ) { + pixbuf_cache = gl_label_get_pixbuf_cache (dst_object->parent); + gl_pixbuf_cache_add_pixbuf (pixbuf_cache, filename->data, pixbuf); + } + } + gl_label_image_set_filename (new_limage, filename); gl_text_node_free (&filename); @@ -171,62 +196,54 @@ void gl_label_image_set_filename (glLabelImage *limage, glTextNode *filename) { - GdkPixbuf *pixbuf; + glTextNode *old_filename; + GHashTable *pixbuf_cache; + GdkPixbuf *pixbuf; gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (limage && GL_IS_LABEL_IMAGE (limage)); + g_return_if_fail (filename != NULL); - if ( (filename == NULL) || filename->field_flag || (filename->data == NULL) ) { + old_filename = limage->private->filename; - gl_text_node_free (&limage->private->filename); - limage->private->filename = gl_text_node_dup(filename); - - g_object_unref (limage->private->pixbuf); - limage->private->pixbuf = - gdk_pixbuf_new_from_xpm_data ((const char **) - checkerboard_xpm); - - gl_label_object_emit_changed (GL_LABEL_OBJECT(limage)); - } else { + /* If Unchanged don't do anything */ + if ( (filename->field_flag == old_filename->field_flag) && + old_filename->data != NULL && filename->data != NULL && + !strcmp(filename->data, old_filename->data) ) + { + return; + } - if ( limage->private->filename == NULL) { + pixbuf_cache = gl_label_get_pixbuf_cache (GL_LABEL_OBJECT(limage)->parent); - limage->private->filename = gl_text_node_dup (filename); + /* Remove reference to previous pixbuf from cache, if needed. */ + if ( !old_filename->field_flag && (old_filename->data != NULL) ) { + gl_pixbuf_cache_remove_pixbuf (pixbuf_cache, old_filename->data); + } - pixbuf = gdk_pixbuf_new_from_file (filename->data, NULL); - g_object_unref (limage->private->pixbuf); - if ( pixbuf != NULL ) { - limage->private->pixbuf = pixbuf; - } else { - limage->private->pixbuf = - gdk_pixbuf_new_from_xpm_data ((const char **) - checkerboard_xpm); - } + /* Set new filename. */ + limage->private->filename = gl_text_node_dup(filename); + gl_text_node_free (&old_filename); - gl_label_object_emit_changed (GL_LABEL_OBJECT(limage)); + /* Now set the pixbuf. */ + if ( filename->field_flag || (filename->data == NULL) ) { - } else if ( (limage->private->filename->data == NULL) || - strcmp (limage->private->filename->data, filename->data) != 0) { + limage->private->pixbuf = default_pixbuf; - gl_text_node_free (&limage->private->filename); - limage->private->filename = gl_text_node_dup (filename); + } else { - pixbuf = gdk_pixbuf_new_from_file (filename->data, NULL); - g_object_unref (limage->private->pixbuf); - if ( pixbuf != NULL ) { - limage->private->pixbuf = pixbuf; - } else { - limage->private->pixbuf = - gdk_pixbuf_new_from_xpm_data ((const char **) - checkerboard_xpm); - } + pixbuf = gl_pixbuf_cache_get_pixbuf (pixbuf_cache, filename->data); - gl_label_object_emit_changed (GL_LABEL_OBJECT(limage)); + if (pixbuf != NULL) { + limage->private->pixbuf = pixbuf; + } else { + limage->private->pixbuf = default_pixbuf; } - } + gl_label_object_emit_changed (GL_LABEL_OBJECT(limage)); + gl_debug (DEBUG_LABEL, "END"); } @@ -246,13 +263,13 @@ const GdkPixbuf * gl_label_image_get_pixbuf (glLabelImage *limage, glMergeRecord *record) { - GdkPixbuf *pixbuf = NULL; - gchar *real_filename; - g_return_val_if_fail (limage && GL_IS_LABEL_IMAGE (limage), NULL); if ((record != NULL) && limage->private->filename->field_flag) { - + + GdkPixbuf *pixbuf = NULL; + gchar *real_filename; + /* Indirect filename, re-evaluate for given record. */ real_filename = gl_merge_eval_key (record, @@ -261,17 +278,17 @@ gl_label_image_get_pixbuf (glLabelImage *limage, if (real_filename != NULL) { pixbuf = gdk_pixbuf_new_from_file (real_filename, NULL); } - g_object_unref (limage->private->pixbuf); if ( pixbuf != NULL ) { - limage->private->pixbuf = pixbuf; + return pixbuf; } else { - limage->private->pixbuf = - gdk_pixbuf_new_from_xpm_data ((const char **) - checkerboard_xpm); + return default_pixbuf; } } return limage->private->pixbuf; + } + + diff --git a/glabels2/src/label.c b/glabels2/src/label.c index f4db0f4b..8d4a16f7 100644 --- a/glabels2/src/label.c +++ b/glabels2/src/label.c @@ -26,13 +26,6 @@ #include "label.h" #include "label-object.h" -#include "label-text.h" -#include "label-box.h" -#include "label-line.h" -#include "label-ellipse.h" -#include "label-image.h" -#include "label-barcode.h" -#include "template.h" #include "marshal.h" #include "util.h" @@ -48,14 +41,16 @@ struct _glLabelPrivate { - glTemplate *template; - gboolean rotate_flag; + glTemplate *template; + gboolean rotate_flag; - gchar *filename; - gboolean modified_flag; - gint untitled_instance; + gchar *filename; + gboolean modified_flag; + gint untitled_instance; - glMerge *merge; + glMerge *merge; + + GHashTable *pixbuf_cache; }; enum { @@ -179,6 +174,7 @@ gl_label_instance_init (glLabel *label) label->private = g_new0 (glLabelPrivate, 1); label->private->merge = NULL; + label->private->pixbuf_cache = gl_pixbuf_cache_new (); gl_debug (DEBUG_LABEL, "END"); } @@ -206,6 +202,8 @@ gl_label_finalize (GObject *object) g_object_unref (G_OBJECT(label->private->merge)); } + gl_pixbuf_cache_free (label->private->pixbuf_cache); + g_free (label->private); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -555,6 +553,15 @@ gl_label_get_short_name (glLabel *label) } } +/****************************************************************************/ +/* Get pixbuf cache. */ +/****************************************************************************/ +GHashTable * +gl_label_get_pixbuf_cache (glLabel *label) +{ + return label->private->pixbuf_cache; +} + /****************************************************************************/ /* Is label modified? */ /****************************************************************************/ diff --git a/glabels2/src/label.h b/glabels2/src/label.h index fbf4688c..6d639e10 100644 --- a/glabels2/src/label.h +++ b/glabels2/src/label.h @@ -26,6 +26,7 @@ #include "merge.h" #include "template.h" +#include "pixbuf-cache.h" G_BEGIN_DECLS @@ -107,6 +108,8 @@ gchar *gl_label_get_filename (glLabel *label); gchar *gl_label_get_short_name (glLabel *label); +GHashTable *gl_label_get_pixbuf_cache (glLabel *label); + gboolean gl_label_is_modified (glLabel *label); gboolean gl_label_is_untitled (glLabel *label); diff --git a/glabels2/src/pixbuf-cache.c b/glabels2/src/pixbuf-cache.c new file mode 100644 index 00000000..cc2f08e4 --- /dev/null +++ b/glabels2/src/pixbuf-cache.c @@ -0,0 +1,250 @@ +/* + * (GLABELS) Label and Business Card Creation program for GNOME + * + * pixbuf-cache.c: GLabels pixbuf cache module + * + * Copyright (C) 2003 Jim Evins . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include + +#include "pixbuf-cache.h" + +#include "debug.h" + +/*========================================================*/ +/* Private types. */ +/*========================================================*/ + +typedef struct { + gchar *key; + guint references; + GdkPixbuf *pixbuf; +} CacheRecord; + +/*========================================================*/ +/* Private globals. */ +/*========================================================*/ + +/*========================================================*/ +/* Private function prototypes. */ +/*========================================================*/ + +static void record_destroy (gpointer val); + +static void add_name_to_list (gpointer key, + gpointer val, + gpointer user_data); + + +/*---------------------------------------------------------------------------*/ +/* PRIVATE. Destroy cache record. */ +/*---------------------------------------------------------------------------*/ +static void +record_destroy (gpointer val) +{ + CacheRecord *record = (CacheRecord *)val; + + g_return_if_fail (record); + + g_free (record->key); + g_object_unref (record->pixbuf); + g_free (record); +} + +/*****************************************************************************/ +/* Create a new hash table to keep track of cached pixbufs. */ +/*****************************************************************************/ +GHashTable * +gl_pixbuf_cache_new (void) +{ + GHashTable *pixbuf_cache; + + gl_debug (DEBUG_PIXBUF_CACHE, "START"); + + pixbuf_cache = g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + record_destroy); + + gl_debug (DEBUG_PIXBUF_CACHE, "END pixbuf_cache=%p", pixbuf_cache); + + return pixbuf_cache; +} + +/*****************************************************************************/ +/* Free up previously allocated hash table and its contents. */ +/*****************************************************************************/ +void +gl_pixbuf_cache_free (GHashTable *pixbuf_cache) +{ + gl_debug (DEBUG_PIXBUF_CACHE, "START"); + + g_hash_table_destroy (pixbuf_cache); + + gl_debug (DEBUG_PIXBUF_CACHE, "END"); +} + +/*****************************************************************************/ +/* Add pixbuf to cache explicitly. */ +/*****************************************************************************/ +void +gl_pixbuf_cache_add_pixbuf (GHashTable *pixbuf_cache, + gchar *name, + GdkPixbuf *pixbuf) +{ + CacheRecord *test_record, *record; + gchar *key; + + gl_debug (DEBUG_PIXBUF_CACHE, "START"); + + test_record = g_hash_table_lookup (pixbuf_cache, name); + if (test_record != NULL) { + /* pixbuf is already in the cache. */ + gl_debug (DEBUG_PIXBUF_CACHE, "END already in cache"); + return; + } + + record = g_new0 (CacheRecord, 1); + record->key = g_strdup (name); + record->references = 0; + record->pixbuf = pixbuf; + + g_hash_table_insert (pixbuf_cache, record->key, record); + + gl_debug (DEBUG_PIXBUF_CACHE, "END"); +} + +/*****************************************************************************/ +/* Get pixbuf. If not in cache, read it and add to cache. */ +/*****************************************************************************/ +GdkPixbuf * +gl_pixbuf_cache_get_pixbuf (GHashTable *pixbuf_cache, + gchar *name) +{ + CacheRecord *record; + GdkPixbuf *pixbuf; + + gl_debug (DEBUG_PIXBUF_CACHE, "START pixbuf_cache=%p", pixbuf_cache); + + record = g_hash_table_lookup (pixbuf_cache, name); + + if (record != NULL) { + gl_debug (DEBUG_PIXBUF_CACHE, "END cached"); + record->references++; + return record->pixbuf; + } + + + pixbuf = gdk_pixbuf_new_from_file (name, NULL); + if ( pixbuf != NULL) { + record = g_new0 (CacheRecord, 1); + record->key = g_strdup (name); + record->references = 1; + record->pixbuf = pixbuf; + + g_hash_table_insert (pixbuf_cache, record->key, record); + } + + gl_debug (DEBUG_PIXBUF_CACHE, "END"); + + return pixbuf; +} + +/*****************************************************************************/ +/* Remove pixbuf, but only if no references left. */ +/*****************************************************************************/ +void +gl_pixbuf_cache_remove_pixbuf (GHashTable *pixbuf_cache, + gchar *name) +{ + CacheRecord *record; + + gl_debug (DEBUG_PIXBUF_CACHE, "START"); + + record = g_hash_table_lookup (pixbuf_cache, name); + if (record == NULL) { + gl_debug (DEBUG_PIXBUF_CACHE, "END not in cache"); + return; + } + + record->references--; + + if ( record->references == 0 ) { + g_hash_table_remove (pixbuf_cache, name); + } + + gl_debug (DEBUG_PIXBUF_CACHE, "END"); +} + +/*---------------------------------------------------------------------------*/ +/* PRIVATE. Add a name to a GList while iterating over cache. */ +/*---------------------------------------------------------------------------*/ +static void +add_name_to_list (gpointer key, + gpointer val, + gpointer user_data) +{ + gchar *name = (gchar *)key; + GList **name_list = (GList **)user_data; + + gl_debug (DEBUG_PIXBUF_CACHE, "START"); + + gl_debug (DEBUG_PIXBUF_CACHE, "adding name=%s", name); + + *name_list = g_list_append (*name_list, g_strdup(name)); + + gl_debug (DEBUG_PIXBUF_CACHE, "END"); +} + +/*****************************************************************************/ +/* Return a list of names for all pixbufs in the cache. */ +/*****************************************************************************/ +GList * +gl_pixbuf_cache_get_name_list (GHashTable *pixbuf_cache) +{ + GList *name_list = NULL; + + gl_debug (DEBUG_PIXBUF_CACHE, "START"); + + g_hash_table_foreach (pixbuf_cache, add_name_to_list, &name_list); + + gl_debug (DEBUG_PIXBUF_CACHE, "END"); + + return name_list; +} + +/*****************************************************************************/ +/* Free up a list of pixbuf names. */ +/*****************************************************************************/ +void +gl_pixbuf_cache_free_name_list (GList *name_list) +{ + GList *p_name; + + gl_debug (DEBUG_PIXBUF_CACHE, "START"); + + for (p_name = name_list; p_name != NULL; p_name = p_name->next) { + g_free (p_name->data); + p_name->data = NULL; + } + + g_list_free (name_list); + + gl_debug (DEBUG_PIXBUF_CACHE, "END"); +} + + diff --git a/glabels2/src/pixbuf-cache.h b/glabels2/src/pixbuf-cache.h new file mode 100644 index 00000000..33d76734 --- /dev/null +++ b/glabels2/src/pixbuf-cache.h @@ -0,0 +1,50 @@ +/* + * (GLABELS) Label and Business Card Creation program for GNOME + * + * pixbuf-cache.h: GLabels pixbuf cache module + * + * Copyright (C) 2003 Jim Evins . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PIXBUF_CACHE_H__ +#define __PIXBUF_CACHE_H__ + +#include +#include + +G_BEGIN_DECLS + +GHashTable *gl_pixbuf_cache_new (void); + +void gl_pixbuf_cache_free (GHashTable *pixbuf_cache); + +void gl_pixbuf_cache_add_pixbuf (GHashTable *pixbuf_cache, + gchar *name, + GdkPixbuf *pixbuf); + +GdkPixbuf *gl_pixbuf_cache_get_pixbuf (GHashTable *pixbuf_cache, + gchar *name); + +void gl_pixbuf_cache_remove_pixbuf (GHashTable *pixbuf_cache, + gchar *name); + +GList *gl_pixbuf_cache_get_name_list (GHashTable *pixbuf_cache); + +void gl_pixbuf_cache_free_name_list (GList *name_list); + +G_END_DECLS + +#endif /*__PIXBUF_CACHE_H__ */ diff --git a/glabels2/src/view.c b/glabels2/src/view.c index f502a019..13e56388 100644 --- a/glabels2/src/view.c +++ b/glabels2/src/view.c @@ -3246,27 +3246,27 @@ selection_received_cb (GtkWidget *widget, p_next = p->next; object = (glLabelObject *) p->data; - gl_label_object_set_parent (object, view->label); + newobject = gl_label_object_dup (object, view->label); gl_debug (DEBUG_VIEW, "object pasted"); - if (GL_IS_LABEL_BOX (object)) { - view_object = gl_view_box_new (GL_LABEL_BOX(object), + if (GL_IS_LABEL_BOX (newobject)) { + view_object = gl_view_box_new (GL_LABEL_BOX(newobject), view); - } else if (GL_IS_LABEL_ELLIPSE (object)) { - view_object = gl_view_ellipse_new (GL_LABEL_ELLIPSE(object), + } else if (GL_IS_LABEL_ELLIPSE (newobject)) { + view_object = gl_view_ellipse_new (GL_LABEL_ELLIPSE(newobject), view); - } else if (GL_IS_LABEL_LINE (object)) { - view_object = gl_view_line_new (GL_LABEL_LINE(object), + } else if (GL_IS_LABEL_LINE (newobject)) { + view_object = gl_view_line_new (GL_LABEL_LINE(newobject), view); - } else if (GL_IS_LABEL_IMAGE (object)) { - view_object = gl_view_image_new (GL_LABEL_IMAGE(object), + } else if (GL_IS_LABEL_IMAGE (newobject)) { + view_object = gl_view_image_new (GL_LABEL_IMAGE(newobject), view); - } else if (GL_IS_LABEL_TEXT (object)) { - view_object = gl_view_text_new (GL_LABEL_TEXT(object), + } else if (GL_IS_LABEL_TEXT (newobject)) { + view_object = gl_view_text_new (GL_LABEL_TEXT(newobject), view); - } else if (GL_IS_LABEL_BARCODE (object)) { - view_object = gl_view_barcode_new (GL_LABEL_BARCODE(object), + } else if (GL_IS_LABEL_BARCODE (newobject)) { + view_object = gl_view_barcode_new (GL_LABEL_BARCODE(newobject), view); } else { /* Should not happen! */ diff --git a/glabels2/src/xml-label.c b/glabels2/src/xml-label.c index 5aa7b6f0..293425d8 100644 --- a/glabels2/src/xml-label.c +++ b/glabels2/src/xml-label.c @@ -26,6 +26,7 @@ #include #include +#include #include "label.h" #include "label-object.h" @@ -36,6 +37,7 @@ #include "label-image.h" #include "label-barcode.h" #include "template.h" +#include "base64.h" #include "xml-label.h" #include "xml-label-04.h" #include "util.h" @@ -94,6 +96,12 @@ static glLabelObject *xml_parse_barcode_props (xmlNodePtr node, static void xml_parse_merge_fields (xmlNodePtr node, glLabel *label); +static void xml_parse_data (xmlNodePtr node, + glLabel *label); + +static void xml_parse_pixdata (xmlNodePtr node, + glLabel *label); + static xmlDocPtr xml_label_to_doc (glLabel *label, glXMLLabelStatus *status); @@ -134,6 +142,16 @@ static void xml_create_merge_fields (xmlNodePtr root, xmlNsPtr ns, glLabel *label); +static void xml_create_data (xmlNodePtr root, + xmlNsPtr ns, + glLabel *label); + +static void xml_create_pixdata (xmlNodePtr root, + xmlNsPtr ns, + glLabel *label, + gchar *name); + + /****************************************************************************/ /* Open and read label from xml file. */ /****************************************************************************/ @@ -272,6 +290,14 @@ xml_parse_label (xmlNodePtr root, label = GL_LABEL(gl_label_new ()); + /* Pass 1, extract data nodes to pre-load cache. */ + for (node = root->xmlChildrenNode; node != NULL; node = node->next) { + if (g_strcasecmp (node->name, "Data") == 0) { + xml_parse_data (node, label); + } + } + + /* Pass 2, now extract everything else. */ for (node = root->xmlChildrenNode; node != NULL; node = node->next) { if (g_strcasecmp (node->name, "Sheet") == 0) { @@ -286,9 +312,12 @@ xml_parse_label (xmlNodePtr root, xml_parse_objects (node, label); } else if (g_strcasecmp (node->name, "Merge_Fields") == 0) { xml_parse_merge_fields (node, label); + } else if (g_strcasecmp (node->name, "Data") == 0) { + /* Handled in pass 1. */ } else { if (!xmlNodeIsText (node)) { - g_warning (_("bad node = \"%s\""), node->name); + g_warning (_("bad node in Document node = \"%s\""), + node->name); } } } @@ -682,6 +711,71 @@ xml_parse_merge_fields (xmlNodePtr node, gl_debug (DEBUG_XML, "END"); } +/*--------------------------------------------------------------------------*/ +/* PRIVATE. Parse XML data tag. */ +/*--------------------------------------------------------------------------*/ +static void +xml_parse_data (xmlNodePtr node, + glLabel *label) +{ + xmlNodePtr child; + + gl_debug (DEBUG_XML, "START"); + + for (child = node->xmlChildrenNode; child != NULL; child = child->next) { + + if (g_strcasecmp (child->name, "Pixdata") == 0) { + xml_parse_pixdata (child, label); + } else { + if (!xmlNodeIsText (child)) { + g_warning (_("bad node in Data node = \"%s\""), + child->name); + } + } + } + + gl_debug (DEBUG_XML, "END"); +} + +/*--------------------------------------------------------------------------*/ +/* PRIVATE. Parse XML pixbuf data tag. */ +/*--------------------------------------------------------------------------*/ +static void +xml_parse_pixdata (xmlNodePtr node, + glLabel *label) +{ + gchar *name, *base64; + guchar *stream; + guint stream_length; + gboolean ret; + GdkPixdata *pixdata; + GdkPixbuf *pixbuf; + GHashTable *pixbuf_cache; + + gl_debug (DEBUG_XML, "START"); + + name = xmlGetProp (node, "name"); + base64 = xmlNodeGetContent (node); + + stream = gl_base64_decode (base64, &stream_length); + pixdata = g_new0 (GdkPixdata, 1); + ret = gdk_pixdata_deserialize (pixdata, stream_length, stream, NULL); + + if (ret) { + pixbuf = gdk_pixbuf_from_pixdata (pixdata, TRUE, NULL); + + pixbuf_cache = gl_label_get_pixbuf_cache (label); + gl_pixbuf_cache_add_pixbuf (pixbuf_cache, name, pixbuf); + } + + g_free (name); + g_free (base64); + g_free (stream); + g_free (pixdata); + + gl_debug (DEBUG_XML, "END"); +} + /****************************************************************************/ /* Save label to xml label file. */ /****************************************************************************/ @@ -697,6 +791,7 @@ gl_xml_label_save (glLabel *label, doc = xml_label_to_doc (label, status); + xmlSetDocCompressMode (doc, 9); xml_ret = xmlSaveFormatFile (filename, doc, TRUE); xmlFreeDoc (doc); if (xml_ret == -1) { @@ -773,6 +868,8 @@ xml_label_to_doc (glLabel *label, g_object_unref (G_OBJECT(merge)); } + xml_create_data (doc->xmlRootNode, ns, label); + gl_debug (DEBUG_XML, "END"); *status = XML_LABEL_OK; @@ -1188,3 +1285,77 @@ xml_create_merge_fields (xmlNodePtr root, gl_debug (DEBUG_XML, "END"); } +/*--------------------------------------------------------------------------*/ +/* PRIVATE. Add XML Label Data Node */ +/*--------------------------------------------------------------------------*/ +static void +xml_create_data (xmlNodePtr root, + xmlNsPtr ns, + glLabel *label) +{ + xmlNodePtr node; + GList *name_list, *p; + GHashTable *pixbuf_cache; + + gl_debug (DEBUG_XML, "START"); + + node = xmlNewChild (root, ns, "Data", NULL); + + pixbuf_cache = gl_label_get_pixbuf_cache (label); + name_list = gl_pixbuf_cache_get_name_list (pixbuf_cache); + + for (p = name_list; p != NULL; p=p->next) { + xml_create_pixdata (node, ns, label, p->data); + } + + gl_pixbuf_cache_free_name_list (name_list); + + + gl_debug (DEBUG_XML, "END"); +} + +/*--------------------------------------------------------------------------*/ +/* PRIVATE. Add XML Label Data Pixbuf Node */ +/*--------------------------------------------------------------------------*/ +static void +xml_create_pixdata (xmlNodePtr root, + xmlNsPtr ns, + glLabel *label, + gchar *name) +{ + xmlNodePtr node; + GHashTable *pixbuf_cache; + GdkPixbuf *pixbuf; + GdkPixdata *pixdata; + guchar *stream; + guint stream_length; + gchar *base64; + + gl_debug (DEBUG_XML, "START"); + + pixbuf_cache = gl_label_get_pixbuf_cache (label); + + pixbuf = gl_pixbuf_cache_get_pixbuf (pixbuf_cache, name); + if ( pixbuf != NULL ) { + + pixdata = g_new0 (GdkPixdata, 1); + gdk_pixdata_from_pixbuf (pixdata, pixbuf, TRUE); + stream = gdk_pixdata_serialize (pixdata, &stream_length); + base64 = gl_base64_encode (stream, stream_length); + + node = xmlNewChild (root, ns, "Pixdata", base64); + xmlSetProp (node, "name", name); + xmlSetProp (node, "encoding", "base64"); + + gl_pixbuf_cache_remove_pixbuf (pixbuf_cache, name); + + g_free (pixdata->pixel_data); + g_free (pixdata); + g_free (stream); + g_free (base64); + } + + + gl_debug (DEBUG_XML, "END"); +} + -- 2.39.5