/* * label-barcode.c * Copyright (C) 2001-2009 Jim Evins . * * This file is part of gLabels. * * gLabels 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 3 of the License, or * (at your option) any later version. * * gLabels 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 gLabels. If not, see . */ #include #include "label-barcode.h" #include #include #include #include "bc-backends.h" #include "debug.h" /*========================================================*/ /* Private macros and constants. */ /*========================================================*/ #define FONT_SCALE (72.0/96.0) #define PI 3.141592654 #define GL_BARCODE_FONT_FAMILY "Sans" #define HANDLE_OUTLINE_RGBA_ARGS 0.5, 0.5, 0.5, 0.75 #define HANDLE_OUTLINE_WIDTH_PIXELS 2.0 #define SELECTION_SLOP_PIXELS 4.0 /*========================================================*/ /* Private types. */ /*========================================================*/ struct _glLabelBarcodePrivate { glTextNode *text_node; glLabelBarcodeStyle *style; glColorNode *color_node; /* Cached info. Only regenerate when text_node, * style, or raw size changed */ lglBarcode *display_gbc; gdouble w, h; }; /*========================================================*/ /* Private globals. */ /*========================================================*/ /*========================================================*/ /* Private function prototypes. */ /*========================================================*/ static void gl_label_barcode_finalize (GObject *object); static void copy (glLabelObject *dst_object, glLabelObject *src_object); static void update_barcode (glLabelBarcode *lbc); static void set_size (glLabelObject *object, gdouble w, gdouble h, gboolean checkpoint); static void get_size (glLabelObject *object, gdouble *w, gdouble *h); static void set_line_color (glLabelObject *object, glColorNode *line_color, gboolean checkpoint); static glColorNode *get_line_color (glLabelObject *object); static void draw_object (glLabelObject *object, cairo_t *cr, gboolean screen_flag, glMergeRecord *record); static gboolean object_at (glLabelObject *object, cairo_t *cr, gdouble x_pixels, gdouble y_pixels); static void draw_handles (glLabelObject *object, cairo_t *cr); static void create_alt_msg_path (cairo_t *cr, gchar *text); /*****************************************************************************/ /* Boilerplate object stuff. */ /*****************************************************************************/ G_DEFINE_TYPE (glLabelBarcode, gl_label_barcode, GL_TYPE_LABEL_OBJECT) static void gl_label_barcode_class_init (glLabelBarcodeClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); glLabelObjectClass *label_object_class = GL_LABEL_OBJECT_CLASS (class); gl_label_barcode_parent_class = g_type_class_peek_parent (class); label_object_class->copy = copy; label_object_class->set_size = set_size; label_object_class->get_size = get_size; label_object_class->set_line_color = set_line_color; label_object_class->get_line_color = get_line_color; label_object_class->draw_object = draw_object; label_object_class->draw_shadow = NULL; label_object_class->object_at = object_at; label_object_class->draw_handles = draw_handles; object_class->finalize = gl_label_barcode_finalize; } static void gl_label_barcode_init (glLabelBarcode *lbc) { lbc->priv = g_new0 (glLabelBarcodePrivate, 1); lbc->priv->text_node = gl_text_node_new_from_text (""); } static void gl_label_barcode_finalize (GObject *object) { glLabelBarcode *lbc = GL_LABEL_BARCODE (object); g_return_if_fail (object && GL_IS_LABEL_BARCODE (object)); gl_text_node_free (&lbc->priv->text_node); gl_label_barcode_style_free (lbc->priv->style); gl_color_node_free (&(lbc->priv->color_node)); lgl_barcode_free (lbc->priv->display_gbc); g_free (lbc->priv); G_OBJECT_CLASS (gl_label_barcode_parent_class)->finalize (object); } /*****************************************************************************/ /* NEW label "barcode" object. */ /*****************************************************************************/ GObject * gl_label_barcode_new (glLabel *label, gboolean checkpoint) { glLabelBarcode *lbc; glLabelBarcodeStyle *style; glColorNode *line_color_node; lbc = g_object_new (gl_label_barcode_get_type(), NULL); if (label != NULL) { if ( checkpoint ) { gl_label_checkpoint (label, _("Create barcode object")); } /* Default barcode style and properties. */ style = gl_label_barcode_style_new (); gl_label_barcode_style_set_backend_id (style, gl_barcode_backends_backend_name_to_id (NULL)); gl_label_barcode_style_set_style_id (style, gl_barcode_backends_style_name_to_id (style->backend_id, NULL)); style->text_flag = gl_barcode_backends_style_can_text (style->backend_id, style->id); style->checksum_flag = gl_barcode_backends_style_can_csum (style->backend_id, style->id); style->format_digits = gl_barcode_backends_style_get_prefered_n (style->backend_id, style->id); lbc->priv->style = style; update_barcode (lbc); line_color_node = gl_color_node_new_default (); line_color_node->color = gl_label_get_default_line_color(label); lbc->priv->color_node = line_color_node; gl_label_add_object (label, GL_LABEL_OBJECT (lbc)); gl_label_object_set_parent (GL_LABEL_OBJECT (lbc), label); } return G_OBJECT (lbc); } /*****************************************************************************/ /* Copy object contents. */ /*****************************************************************************/ static void copy (glLabelObject *dst_object, glLabelObject *src_object) { glLabelBarcode *lbc = (glLabelBarcode *)src_object; glLabelBarcode *new_lbc = (glLabelBarcode *)dst_object; glTextNode *text_node; glLabelBarcodeStyle *style; glColorNode *color_node; gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc)); g_return_if_fail (new_lbc && GL_IS_LABEL_BARCODE (new_lbc)); text_node = gl_label_barcode_get_data (lbc); style = gl_label_barcode_get_style (lbc); color_node = get_line_color (src_object); new_lbc->priv->text_node = text_node; new_lbc->priv->style = style; new_lbc->priv->color_node = color_node; update_barcode (new_lbc); gl_debug (DEBUG_LABEL, "END"); } /*****************************************************************************/ /* Set object params. */ /*****************************************************************************/ void gl_label_barcode_set_data (glLabelBarcode *lbc, const glTextNode *text_node, gboolean checkpoint) { glLabel *label; gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc)); if (!gl_text_node_equal (lbc->priv->text_node, text_node)) { if ( checkpoint ) { label = gl_label_object_get_parent (GL_LABEL_OBJECT (lbc)); gl_label_checkpoint (label, _("Barcode data")); } gl_text_node_free (&lbc->priv->text_node); lbc->priv->text_node = gl_text_node_dup (text_node); update_barcode (lbc); gl_label_object_emit_changed (GL_LABEL_OBJECT(lbc)); } gl_debug (DEBUG_LABEL, "END"); } void gl_label_barcode_set_style (glLabelBarcode *lbc, const glLabelBarcodeStyle *style, gboolean checkpoint) { glLabel *label; gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc)); if ( !gl_label_barcode_style_is_equal (style, lbc->priv->style) ) { if ( checkpoint ) { label = gl_label_object_get_parent (GL_LABEL_OBJECT (lbc)); gl_label_checkpoint (label, _("Barcode property")); } gl_label_barcode_style_free (lbc->priv->style); lbc->priv->style = gl_label_barcode_style_dup (style); update_barcode (lbc); gl_label_object_emit_changed (GL_LABEL_OBJECT(lbc)); } gl_debug (DEBUG_LABEL, "END"); } /*****************************************************************************/ /* Get object params. */ /*****************************************************************************/ glTextNode * gl_label_barcode_get_data (glLabelBarcode *lbc) { g_return_val_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc), NULL); return gl_text_node_dup (lbc->priv->text_node); } glLabelBarcodeStyle * gl_label_barcode_get_style (glLabelBarcode *lbc) { g_return_val_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc), NULL); return gl_label_barcode_style_dup (lbc->priv->style); } /*---------------------------------------------------------------------------*/ /* PRIVATE. Update cached lglBarcode. */ /*---------------------------------------------------------------------------*/ static void update_barcode (glLabelBarcode *lbc) { gdouble w_raw, h_raw; gchar *data; gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc)); gl_label_object_get_raw_size (GL_LABEL_OBJECT (lbc), &w_raw, &h_raw); lgl_barcode_free (lbc->priv->display_gbc); if (lbc->priv->text_node->field_flag) { data = gl_barcode_backends_style_default_digits (lbc->priv->style->backend_id, lbc->priv->style->id, lbc->priv->style->format_digits); } else { data = gl_text_node_expand (lbc->priv->text_node, NULL); } lbc->priv->display_gbc = gl_barcode_backends_new_barcode (lbc->priv->style->backend_id, lbc->priv->style->id, lbc->priv->style->text_flag, lbc->priv->style->checksum_flag, w_raw, h_raw, data); g_free (data); if ( lbc->priv->display_gbc == NULL ) { lglBarcode *gbc; /* Try again with default digits, but don't save -- just extract size. */ data = gl_barcode_backends_style_default_digits (lbc->priv->style->backend_id, lbc->priv->style->id, lbc->priv->style->format_digits); gbc = gl_barcode_backends_new_barcode (lbc->priv->style->backend_id, lbc->priv->style->id, lbc->priv->style->text_flag, lbc->priv->style->checksum_flag, w_raw, h_raw, data); g_free (data); if ( gbc != NULL ) { lbc->priv->w = gbc->width; lbc->priv->h = gbc->height; } else { /* If we still can't render, just set a default size. */ lbc->priv->w = 144; lbc->priv->h = 72; } lgl_barcode_free (gbc); } else { lbc->priv->w = lbc->priv->display_gbc->width; lbc->priv->h = lbc->priv->display_gbc->height; } gl_debug (DEBUG_LABEL, "END"); } /*---------------------------------------------------------------------------*/ /* PRIVATE. Set object size method. */ /*---------------------------------------------------------------------------*/ static void set_size (glLabelObject *object, gdouble w, gdouble h, gboolean checkpoint) { gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (object && GL_IS_LABEL_BARCODE (object)); gl_label_object_set_raw_size (object, w, h, checkpoint); update_barcode (GL_LABEL_BARCODE (object)); gl_debug (DEBUG_LABEL, "END"); } /*---------------------------------------------------------------------------*/ /* PRIVATE. Get object size method. */ /*---------------------------------------------------------------------------*/ static void get_size (glLabelObject *object, gdouble *w, gdouble *h) { gl_debug (DEBUG_LABEL, "START"); g_return_if_fail (object && GL_IS_LABEL_BARCODE (object)); *w = GL_LABEL_BARCODE (object)->priv->w; *h = GL_LABEL_BARCODE (object)->priv->h; gl_debug (DEBUG_LABEL, "END"); } /*---------------------------------------------------------------------------*/ /* PRIVATE. Set line color method. */ /*---------------------------------------------------------------------------*/ static void set_line_color (glLabelObject *object, glColorNode *line_color_node, gboolean checkpoint) { glLabelBarcode *lbarcode = (glLabelBarcode *)object; glLabel *label; g_return_if_fail (lbarcode && GL_IS_LABEL_BARCODE (lbarcode)); if ( !gl_color_node_equal(lbarcode->priv->color_node, line_color_node) ) { if ( checkpoint ) { label = gl_label_object_get_parent (GL_LABEL_OBJECT (lbarcode)); gl_label_checkpoint (label, _("Barcode data")); } gl_color_node_free (&(lbarcode->priv->color_node)); lbarcode->priv->color_node = gl_color_node_dup (line_color_node); gl_label_object_emit_changed (GL_LABEL_OBJECT(lbarcode)); } } /*---------------------------------------------------------------------------*/ /* PRIVATE. Get line color method. */ /*---------------------------------------------------------------------------*/ static glColorNode* get_line_color (glLabelObject *object) { glLabelBarcode *lbarcode = (glLabelBarcode *)object; g_return_val_if_fail (lbarcode && GL_IS_LABEL_BARCODE (lbarcode), NULL); return gl_color_node_dup (lbarcode->priv->color_node); } /*****************************************************************************/ /* Draw object method. */ /*****************************************************************************/ static void draw_object (glLabelObject *object, cairo_t *cr, gboolean screen_flag, glMergeRecord *record) { glLabelBarcode *lbc = (glLabelBarcode *)object; gdouble x0, y0; cairo_matrix_t matrix; lglBarcode *gbc; gchar *text; glTextNode *text_node; glLabelBarcodeStyle *style; guint color; glColorNode *color_node; gdouble w, h; gl_debug (DEBUG_LABEL, "START"); gl_label_object_get_position (object, &x0, &y0); gl_label_object_get_matrix (object, &matrix); text_node = gl_label_barcode_get_data (GL_LABEL_BARCODE (object)); style = gl_label_barcode_get_style (GL_LABEL_BARCODE (object)); color_node = gl_label_object_get_line_color (object); color = gl_color_node_expand (color_node, record); if (color_node->field_flag && screen_flag) { color = GL_COLOR_MERGE_DEFAULT; } cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (color)); if (text_node->field_flag && !screen_flag) { gl_label_object_get_raw_size (object, &w, &h); text = gl_text_node_expand (text_node, record); gbc = gl_barcode_backends_new_barcode (style->backend_id, style->id, style->text_flag, style->checksum_flag, w, h, text); g_free (text); if ( gbc != NULL ) { lgl_barcode_render_to_cairo (gbc, cr); lgl_barcode_free (gbc); } } else { if (lbc->priv->display_gbc == NULL) { create_alt_msg_path (cr, text_node->data); cairo_fill (cr); } else { lgl_barcode_render_to_cairo (lbc->priv->display_gbc, cr); } } gl_text_node_free (&text_node); gl_label_barcode_style_free (style); gl_color_node_free (&color_node); gl_debug (DEBUG_LABEL, "END"); } /*****************************************************************************/ /* Is object at coordinates? */ /*****************************************************************************/ static gboolean object_at (glLabelObject *object, cairo_t *cr, gdouble x, gdouble y) { glLabelBarcode *lbc = (glLabelBarcode *)object; gdouble w, h; glTextNode *text_node; gdouble scale_x, scale_y; gl_label_object_get_size (object, &w, &h); if ( (x >= 0) && (x <= w) && (y >= 0) && (y <= h) ) { text_node = gl_label_barcode_get_data(GL_LABEL_BARCODE(object)); if ( lbc->priv->display_gbc == NULL ) { create_alt_msg_path (cr, text_node->data); } else { lgl_barcode_render_to_cairo_path (lbc->priv->display_gbc, cr); } if (cairo_in_fill (cr, x, y)) { return TRUE; } scale_x = 1.0; scale_y = 1.0; cairo_device_to_user_distance (cr, &scale_x, &scale_y); cairo_set_line_width (cr, 2*SELECTION_SLOP_PIXELS*scale_x); if (cairo_in_stroke (cr, x, y)) { return TRUE; } if (gl_label_object_is_selected (object)) { cairo_new_path (cr); cairo_rectangle (cr, 0, 0, w, h); scale_x = 1.0; scale_y = 1.0; cairo_device_to_user_distance (cr, &scale_x, &scale_y); cairo_set_line_width (cr, (HANDLE_OUTLINE_WIDTH_PIXELS + 2*SELECTION_SLOP_PIXELS)*scale_x); if (cairo_in_stroke (cr, x, y)) { return TRUE; } } } return FALSE; } /*****************************************************************************/ /* Draw barcode style handles. */ /*****************************************************************************/ static void draw_handles (glLabelObject *object, cairo_t *cr) { gdouble w, h; gdouble scale_x, scale_y; gdouble dashes[2] = { 2, 2 }; gl_label_object_get_size (GL_LABEL_OBJECT(object), &w, &h); cairo_save (cr); cairo_rectangle (cr, 0, 0, w, h); scale_x = 1.0; scale_y = 1.0; cairo_device_to_user_distance (cr, &scale_x, &scale_y); cairo_scale (cr, scale_x, scale_y); cairo_set_dash (cr, dashes, 2, 0); cairo_set_line_width (cr, HANDLE_OUTLINE_WIDTH_PIXELS); cairo_set_source_rgba (cr, HANDLE_OUTLINE_RGBA_ARGS); cairo_stroke (cr); cairo_restore (cr); gl_label_object_draw_handles_box (object, cr); } /*****************************************************************************/ /* Create a cairo path with apropos message. */ /*****************************************************************************/ static void create_alt_msg_path (cairo_t *cr, gchar *text) { PangoLayout *layout; PangoFontDescription *desc; layout = pango_cairo_create_layout (cr); desc = pango_font_description_new (); pango_font_description_set_family (desc, GL_BARCODE_FONT_FAMILY); pango_font_description_set_size (desc, 12 * PANGO_SCALE * FONT_SCALE); pango_layout_set_font_description (layout, desc); pango_font_description_free (desc); if (text == NULL || *text == '\0') { pango_layout_set_text (layout, _("Barcode data empty"), -1); } else { pango_layout_set_text (layout, _("Invalid barcode data"), -1); } cairo_move_to (cr, 0, 0); pango_cairo_layout_path (cr, layout); g_object_unref (layout); } /*****************************************************************************/ /* Barcode style utilities. */ /*****************************************************************************/ glLabelBarcodeStyle * gl_label_barcode_style_new (void) { return g_new0 (glLabelBarcodeStyle, 1); } glLabelBarcodeStyle * gl_label_barcode_style_dup (const glLabelBarcodeStyle *style) { glLabelBarcodeStyle *style2; style2 = gl_label_barcode_style_new (); /* Shallow copy first. */ *style2 = *style; /* Now go deep. */ style2->backend_id = g_strdup (style->backend_id); style2->id = g_strdup (style->id); return style2; } void gl_label_barcode_style_free (glLabelBarcodeStyle *style) { if ( style ) { g_free (style->backend_id); g_free (style->id); g_free (style); } } gboolean gl_label_barcode_style_is_equal (const glLabelBarcodeStyle *style1, const glLabelBarcodeStyle *style2) { /* First take care of the case of either or both being NULL. */ if ( style1 == NULL ) { return ( style2 == NULL ); } else { if ( style2 == NULL ) { return FALSE; } } /* Compare field by field, bail on first difference. */ if ( style1->text_flag != style2->text_flag ) { return FALSE; } if ( style1->checksum_flag != style2->checksum_flag ) { return FALSE; } if ( style1->format_digits != style2->format_digits ) { return FALSE; } if ( style1->backend_id && style2->backend_id ) { if ( strcmp (style1->backend_id, style2->backend_id) != 0 ) { return FALSE; } } else { if ( style1->backend_id != style2->backend_id ) { return FALSE; } } if ( style1->id && style2->id ) { if ( strcmp (style1->id, style2->id) != 0 ) { return FALSE; } } else { if ( style1->id != style2->id ) { return FALSE; } } /* Passed all tests. */ return TRUE; } void gl_label_barcode_style_set_backend_id (glLabelBarcodeStyle *style, const gchar *backend_id) { style->backend_id = g_strdup (backend_id); } void gl_label_barcode_style_set_style_id (glLabelBarcodeStyle *style, const gchar *id) { style->id = g_strdup (id); } /* * Local Variables: -- emacs * mode: C -- emacs * c-basic-offset: 8 -- emacs * tab-width: 8 -- emacs * indent-tabs-mode: nil -- emacs * End: -- emacs */