]> git.sur5r.net Git - glabels/blob - src/label-barcode.c
Added support for Maxicode barcodes using the libzint backend.
[glabels] / src / label-barcode.c
1 /*
2  *  label-barcode.c
3  *  Copyright (C) 2001-2009  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of gLabels.
6  *
7  *  gLabels is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  gLabels is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with gLabels.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "label-barcode.h"
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <pango/pangocairo.h>
28 #include "bc-backends.h"
29
30 #include "debug.h"
31
32 /*========================================================*/
33 /* Private macros and constants.                          */
34 /*========================================================*/
35
36 #define FONT_SCALE (72.0/96.0)
37 #define PI 3.141592654
38
39
40 /*========================================================*/
41 /* Private types.                                         */
42 /*========================================================*/
43
44 struct _glLabelBarcodePrivate {
45         glTextNode     *text_node;
46         gchar          *id;
47         glColorNode    *color_node;
48         gboolean        text_flag;
49         gboolean        checksum_flag;
50         guint           format_digits;
51 };
52
53
54 /*========================================================*/
55 /* Private globals.                                       */
56 /*========================================================*/
57
58
59 /*========================================================*/
60 /* Private function prototypes.                           */
61 /*========================================================*/
62
63 static void  gl_label_barcode_finalize      (GObject             *object);
64
65 static void  copy                           (glLabelObject       *dst_object,
66                                              glLabelObject       *src_object);
67
68 static void  get_size                       (glLabelObject       *object,
69                                              gdouble             *w,
70                                              gdouble             *h);
71
72 static void  set_line_color                 (glLabelObject       *object,
73                                              glColorNode         *line_color,
74                                              gboolean             checkpoint);
75
76 static glColorNode *get_line_color          (glLabelObject       *object);
77
78 static void     draw_object                 (glLabelObject       *object,
79                                              cairo_t             *cr,
80                                              gboolean             screen_flag,
81                                              glMergeRecord       *record);
82
83 static gboolean object_at                   (glLabelObject       *object,
84                                              cairo_t             *cr,
85                                              gdouble              x_pixels,
86                                              gdouble              y_pixels);
87
88
89 /*****************************************************************************/
90 /* Boilerplate object stuff.                                                 */
91 /*****************************************************************************/
92 G_DEFINE_TYPE (glLabelBarcode, gl_label_barcode, GL_TYPE_LABEL_OBJECT);
93
94
95 static void
96 gl_label_barcode_class_init (glLabelBarcodeClass *class)
97 {
98         GObjectClass       *object_class       = G_OBJECT_CLASS (class);
99         glLabelObjectClass *label_object_class = GL_LABEL_OBJECT_CLASS (class);
100
101         gl_label_barcode_parent_class = g_type_class_peek_parent (class);
102
103         label_object_class->copy           = copy;
104         label_object_class->get_size       = get_size;
105         label_object_class->set_line_color = set_line_color;
106         label_object_class->get_line_color = get_line_color;
107         label_object_class->draw_object    = draw_object;
108         label_object_class->draw_shadow    = NULL;
109         label_object_class->object_at      = object_at;
110
111         object_class->finalize = gl_label_barcode_finalize;
112 }
113
114
115 static void
116 gl_label_barcode_init (glLabelBarcode *lbc)
117 {
118         lbc->priv = g_new0 (glLabelBarcodePrivate, 1);
119         lbc->priv->text_node  = gl_text_node_new_from_text ("");
120 }
121
122
123 static void
124 gl_label_barcode_finalize (GObject *object)
125 {
126         glLabelBarcode *lbc = GL_LABEL_BARCODE (object);
127
128         g_return_if_fail (object && GL_IS_LABEL_BARCODE (object));
129
130         gl_text_node_free (&lbc->priv->text_node);
131         g_free (lbc->priv->id);
132         gl_color_node_free (&(lbc->priv->color_node));
133         g_free (lbc->priv);
134
135         G_OBJECT_CLASS (gl_label_barcode_parent_class)->finalize (object);
136 }
137
138
139 /*****************************************************************************/
140 /* NEW label "barcode" object.                                               */
141 /*****************************************************************************/
142 GObject *
143 gl_label_barcode_new (glLabel *label,
144                       gboolean checkpoint)
145 {
146         glLabelBarcode      *lbc;
147         glColorNode         *line_color_node;
148
149         lbc = g_object_new (gl_label_barcode_get_type(), NULL);
150
151         if (label != NULL)
152         {
153                 if ( checkpoint )
154                 {
155                         gl_label_checkpoint (label, _("Create barcode object"));
156                 }
157
158                 line_color_node = gl_color_node_new_default ();
159
160                 line_color_node->color = gl_label_get_default_line_color(label);
161
162                 lbc->priv->color_node = line_color_node;
163
164                 gl_label_add_object (label, GL_LABEL_OBJECT (lbc));
165                 gl_label_object_set_parent (GL_LABEL_OBJECT (lbc), label);
166         }
167
168         return G_OBJECT (lbc);
169 }
170
171
172 /*****************************************************************************/
173 /* Copy object contents.                                                     */
174 /*****************************************************************************/
175 static void
176 copy (glLabelObject *dst_object,
177       glLabelObject *src_object)
178 {
179         glLabelBarcode      *lbc     = (glLabelBarcode *)src_object;
180         glLabelBarcode      *new_lbc = (glLabelBarcode *)dst_object;
181         glTextNode          *text_node;
182         gchar               *id;
183         gboolean             text_flag;
184         gboolean             checksum_flag;
185         glColorNode         *color_node;
186         guint                format_digits;
187
188         gl_debug (DEBUG_LABEL, "START");
189
190         g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc));
191         g_return_if_fail (new_lbc && GL_IS_LABEL_BARCODE (new_lbc));
192
193         text_node = gl_label_barcode_get_data (lbc);
194         gl_label_barcode_get_props (lbc, &id, &text_flag, &checksum_flag, &format_digits);
195         color_node = get_line_color (src_object);
196
197         gl_label_barcode_set_data (new_lbc, text_node, FALSE);
198         gl_label_barcode_set_props (new_lbc, id, text_flag, checksum_flag, format_digits, FALSE);
199         set_line_color (dst_object, color_node, FALSE);
200
201         gl_color_node_free (&color_node);
202         gl_text_node_free (&text_node);
203         g_free (id);
204
205         gl_debug (DEBUG_LABEL, "END");
206 }
207
208
209 /*****************************************************************************/
210 /* Set object params.                                                        */
211 /*****************************************************************************/
212 void
213 gl_label_barcode_set_data (glLabelBarcode *lbc,
214                            glTextNode     *text_node,
215                            gboolean        checkpoint)
216 {
217         glLabel *label;
218
219         gl_debug (DEBUG_LABEL, "START");
220
221         g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc));
222
223         if (!gl_text_node_equal (lbc->priv->text_node, text_node))
224         {
225                 if ( checkpoint )
226                 {
227                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (lbc));
228                         gl_label_checkpoint (label, _("Barcode data"));
229                 }
230                 
231                 gl_text_node_free (&lbc->priv->text_node);
232                 lbc->priv->text_node = gl_text_node_dup (text_node);
233
234                 gl_label_object_emit_changed (GL_LABEL_OBJECT(lbc));
235         }
236
237         gl_debug (DEBUG_LABEL, "END");
238 }
239
240
241 void
242 gl_label_barcode_set_props (glLabelBarcode *lbc,
243                             gchar          *id,
244                             gboolean        text_flag,
245                             gboolean        checksum_flag,
246                             guint           format_digits,
247                             gboolean        checkpoint)
248 {
249         glLabel *label;
250
251         gl_debug (DEBUG_LABEL, "START");
252
253         g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc));
254
255         if ( ((lbc->priv->id == NULL) && (id != NULL))
256              || ((lbc->priv->id != NULL) && (id != NULL) && (g_ascii_strcasecmp (lbc->priv->id, id) != 0))
257              || (lbc->priv->text_flag != text_flag)
258              || (lbc->priv->checksum_flag != checksum_flag)
259              || (lbc->priv->format_digits != format_digits))
260         {
261                 if ( checkpoint )
262                 {
263                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (lbc));
264                         gl_label_checkpoint (label, _("Barcode property"));
265                 }
266
267                 lbc->priv->id               = g_strdup (id);
268                 lbc->priv->text_flag        = text_flag;
269                 lbc->priv->checksum_flag    = checksum_flag;
270                 lbc->priv->format_digits    = format_digits;
271
272                 gl_label_object_emit_changed (GL_LABEL_OBJECT(lbc));
273         }
274
275         gl_debug (DEBUG_LABEL, "END");
276 }
277
278
279 /*****************************************************************************/
280 /* Get object params.                                                        */
281 /*****************************************************************************/
282 glTextNode *
283 gl_label_barcode_get_data (glLabelBarcode *lbc)
284 {
285         g_return_val_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc), NULL);
286
287         return gl_text_node_dup (lbc->priv->text_node);
288 }
289
290
291 void
292 gl_label_barcode_get_props (glLabelBarcode *lbc,
293                             gchar          **id,
294                             gboolean       *text_flag,
295                             gboolean       *checksum_flag,
296                             guint          *format_digits)
297 {
298         g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc));
299
300         *id               = g_strdup (lbc->priv->id);
301         *text_flag        = lbc->priv->text_flag;
302         *checksum_flag    = lbc->priv->checksum_flag;
303         *format_digits    = lbc->priv->format_digits;
304 }
305
306
307 /*---------------------------------------------------------------------------*/
308 /* PRIVATE.  Get object size method.                                         */
309 /*---------------------------------------------------------------------------*/
310 static void
311 get_size (glLabelObject *object,
312           gdouble       *w,
313           gdouble       *h)
314 {
315         glLabelBarcode      *lbc = (glLabelBarcode *)object;
316         gchar               *data;
317         gdouble              w_parent, h_parent;
318         glBarcode           *gbc;
319
320         gl_debug (DEBUG_LABEL, "START");
321
322         g_return_if_fail (lbc && GL_IS_LABEL_BARCODE (lbc));
323
324         gl_label_object_get_raw_size (object, &w_parent, &h_parent);
325
326         if (lbc->priv->text_node->field_flag) {
327                 data = gl_barcode_backends_default_digits (lbc->priv->id,
328                                                            lbc->priv->format_digits);
329         } else {
330                 data = gl_text_node_expand (lbc->priv->text_node, NULL);
331         }
332
333         gbc = gl_barcode_backends_new_barcode (lbc->priv->id,
334                                                lbc->priv->text_flag,
335                                                lbc->priv->checksum_flag,
336                                                w_parent,
337                                                h_parent,
338                                                data);
339         g_free (data);
340
341         if ( gbc == NULL ) {
342                 /* Try again with default digits. */
343                 data = gl_barcode_backends_default_digits (lbc->priv->id,
344                                                   lbc->priv->format_digits);
345                 gbc = gl_barcode_backends_new_barcode (lbc->priv->id,
346                                                        lbc->priv->text_flag,
347                                                        lbc->priv->checksum_flag,
348                                                        w_parent,
349                                                        h_parent,
350                                                        data);
351                 g_free (data);
352         }
353
354         if ( gbc != NULL )
355         {
356                 *w = gbc->width;
357                 *h = gbc->height;
358         }
359         else
360         {
361                 /* If we still can't render, just set a default size. */
362                 *w = 144;
363                 *h = 72;
364         }
365
366         gl_barcode_free (&gbc);
367
368         gl_debug (DEBUG_LABEL, "END");
369 }
370
371
372 /*---------------------------------------------------------------------------*/
373 /* PRIVATE.  Set line color method.                                          */
374 /*---------------------------------------------------------------------------*/
375 static void
376 set_line_color (glLabelObject *object,
377                 glColorNode   *line_color_node,
378                 gboolean       checkpoint)
379 {
380         glLabelBarcode *lbarcode = (glLabelBarcode *)object;
381         glLabel        *label;
382
383         g_return_if_fail (lbarcode && GL_IS_LABEL_BARCODE (lbarcode));
384
385         if ( !gl_color_node_equal(lbarcode->priv->color_node, line_color_node) )
386         {
387                 if ( checkpoint )
388                 {
389                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (lbarcode));
390                         gl_label_checkpoint (label, _("Barcode data"));
391                 }
392
393                 gl_color_node_free (&(lbarcode->priv->color_node));
394                 lbarcode->priv->color_node = gl_color_node_dup (line_color_node);
395                 gl_label_object_emit_changed (GL_LABEL_OBJECT(lbarcode));
396         }
397 }
398
399
400 /*---------------------------------------------------------------------------*/
401 /* PRIVATE.  Get line color method.                                          */
402 /*---------------------------------------------------------------------------*/
403 static glColorNode*
404 get_line_color (glLabelObject *object)
405 {
406         glLabelBarcode *lbarcode = (glLabelBarcode *)object;
407
408         g_return_val_if_fail (lbarcode && GL_IS_LABEL_BARCODE (lbarcode), NULL);
409
410         return gl_color_node_dup (lbarcode->priv->color_node);
411 }
412
413
414 /*****************************************************************************/
415 /* Draw object method.                                                       */
416 /*****************************************************************************/
417 static void
418 draw_object (glLabelObject *object,
419              cairo_t       *cr,
420              gboolean       screen_flag,
421              glMergeRecord *record)
422 {
423         gdouble               x0, y0;
424         cairo_matrix_t        matrix;
425         glBarcode            *gbc;
426         glBarcodeShape       *shape;
427         glBarcodeShapeLine   *line;
428         glBarcodeShapeBox    *box;
429         glBarcodeShapeRing   *ring;
430         glBarcodeShapeHexagon *hexagon;
431         glBarcodeShapeChar   *bchar;
432         glBarcodeShapeString *bstring;
433         GList                *p;
434         gdouble               x_offset, y_offset;
435         PangoLayout          *layout;
436         PangoFontDescription *desc;
437         gchar                *text, *cstring;
438         glTextNode           *text_node;
439         gchar                *id;
440         gboolean              text_flag;
441         gboolean              checksum_flag;
442         guint                 color;
443         glColorNode          *color_node;
444         guint                 format_digits;
445         gdouble               w, h;
446         gint                  iw, ih;
447         gdouble               layout_width;
448
449         gl_debug (DEBUG_LABEL, "START");
450
451         gl_label_object_get_position (object, &x0, &y0);
452         gl_label_object_get_matrix (object, &matrix);
453
454         text_node = gl_label_barcode_get_data (GL_LABEL_BARCODE (object));
455         gl_label_barcode_get_props (GL_LABEL_BARCODE (object),
456                                     &id, &text_flag, &checksum_flag, &format_digits);
457
458         color_node = gl_label_object_get_line_color (object);
459         color = gl_color_node_expand (color_node, record);
460         if (color_node->field_flag && screen_flag)
461         {
462                 color = GL_COLOR_MERGE_DEFAULT;
463         }
464         gl_color_node_free (&color_node);
465
466         gl_label_object_get_size (object, &w, &h);
467
468         text_node = gl_label_barcode_get_data(GL_LABEL_BARCODE(object));
469         text = gl_text_node_expand (text_node, record);
470         if (text_node->field_flag && screen_flag) {
471                 text = gl_barcode_backends_default_digits (id, format_digits);
472         }
473
474         gbc = gl_barcode_backends_new_barcode (id, text_flag, checksum_flag, w, h, text);
475
476         cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (color));
477
478         if (gbc == NULL) {
479
480                 layout = pango_cairo_create_layout (cr);
481
482                 desc = pango_font_description_new ();
483                 pango_font_description_set_family (desc, GL_BARCODE_FONT_FAMILY);
484                 pango_font_description_set_size   (desc, 12 * PANGO_SCALE * FONT_SCALE);
485                 pango_layout_set_font_description (layout, desc);
486                 pango_font_description_free       (desc);
487
488                 if (text == NULL || *text == '\0')
489                 {
490                         pango_layout_set_text (layout, _("Barcode data empty"), -1);
491                 }
492                 else
493                 {
494                         pango_layout_set_text (layout, _("Invalid barcode data"), -1);
495                 }
496
497                 cairo_move_to (cr, 0, 0);
498                 pango_cairo_show_layout (cr, layout);
499
500                 g_object_unref (layout);
501
502         } else {
503
504                 for (p = gbc->shapes; p != NULL; p = p->next) {
505                         shape = (glBarcodeShape *)p->data;
506                         switch (shape->type)
507                         {
508
509                         case GL_BARCODE_SHAPE_LINE:
510                                 line = (glBarcodeShapeLine *) shape;
511
512                                 cairo_move_to (cr, line->x, line->y);
513                                 cairo_line_to (cr, line->x, line->y + line->length);
514                                 cairo_set_line_width (cr, line->width);
515                                 cairo_stroke (cr);
516
517                                 break;
518
519                         case GL_BARCODE_SHAPE_BOX:
520                                 box = (glBarcodeShapeBox *) shape;
521
522                                 cairo_rectangle (cr, box->x, box->y, box->width, box->height);
523                                 cairo_fill (cr);
524
525                                 break;
526
527                         case GL_BARCODE_SHAPE_CHAR:
528                                 bchar = (glBarcodeShapeChar *) shape;
529
530                                 layout = pango_cairo_create_layout (cr);
531
532                                 desc = pango_font_description_new ();
533                                 pango_font_description_set_family (desc, GL_BARCODE_FONT_FAMILY);
534                                 pango_font_description_set_size   (desc, bchar->fsize * PANGO_SCALE * FONT_SCALE);
535                                 pango_layout_set_font_description (layout, desc);
536                                 pango_font_description_free       (desc);
537
538                                 cstring = g_strdup_printf ("%c", bchar->c);
539                                 pango_layout_set_text (layout, cstring, -1);
540                                 g_free (cstring);
541
542                                 y_offset = 0.2 * bchar->fsize;
543
544                                 cairo_move_to (cr, bchar->x, bchar->y-y_offset);
545                                 pango_cairo_show_layout (cr, layout);
546
547                                 g_object_unref (layout);
548
549                                 break;
550
551                         case GL_BARCODE_SHAPE_STRING:
552                                 bstring = (glBarcodeShapeString *) shape;
553
554                                 layout = pango_cairo_create_layout (cr);
555
556                                 desc = pango_font_description_new ();
557                                 pango_font_description_set_family (desc, GL_BARCODE_FONT_FAMILY);
558                                 pango_font_description_set_size   (desc, bstring->fsize * PANGO_SCALE * FONT_SCALE);
559                                 pango_layout_set_font_description (layout, desc);
560                                 pango_font_description_free       (desc);
561
562                                 pango_layout_set_text (layout, bstring->string, -1);
563
564                                 pango_layout_get_size (layout, &iw, &ih);
565                                 layout_width = (gdouble)iw / (gdouble)PANGO_SCALE;
566
567                                 x_offset = layout_width / 2.0;
568                                 y_offset = 0.2 * bstring->fsize;
569
570                                 cairo_move_to (cr, (bstring->x - x_offset), (bstring->y - y_offset));
571                                 pango_cairo_show_layout (cr, layout);
572
573                                 g_object_unref (layout);
574
575                                 break;
576
577                         case GL_BARCODE_SHAPE_RING:
578                                 ring = (glBarcodeShapeRing *) shape;
579
580                                 cairo_arc (cr, ring->x, ring->y, ring->radius, 0.0, 2 * PI);
581                                 cairo_set_line_width (cr, ring->line_width);
582                                 cairo_stroke (cr);
583
584                                 break;
585
586                         case GL_BARCODE_SHAPE_HEXAGON:
587                                 hexagon = (glBarcodeShapeHexagon *) shape;
588
589                                 cairo_move_to (cr, hexagon->x, hexagon->y);
590                                 cairo_line_to (cr, hexagon->x + 1.25, hexagon->y + 0.70);
591                                 cairo_line_to (cr, hexagon->x + 1.25, hexagon->y + 2.18);
592                                 cairo_line_to (cr, hexagon->x, hexagon->y + 2.89);
593                                 cairo_line_to (cr, hexagon->x - 1.25, hexagon->y + 2.18);
594                                 cairo_line_to (cr, hexagon->x - 1.25, hexagon->y + 0.70);
595                                 cairo_close_path (cr);
596                                 cairo_fill (cr);
597
598                                 break;
599
600                         default:
601                                 g_assert_not_reached ();
602                                 break;
603
604                         }
605
606                 }
607
608                 gl_barcode_free (&gbc);
609
610         }
611
612         g_free (text);
613         gl_text_node_free (&text_node);
614         g_free (id);
615
616         gl_debug (DEBUG_LABEL, "END");
617 }
618
619
620 /*****************************************************************************/
621 /* Is object at coordinates?                                                 */
622 /*****************************************************************************/
623 static gboolean
624 object_at (glLabelObject *object,
625            cairo_t       *cr,
626            gdouble        x,
627            gdouble        y)
628 {
629         gdouble           w, h;
630
631         gl_label_object_get_size (object, &w, &h);
632
633         cairo_new_path (cr);
634         cairo_rectangle (cr, 0.0, 0.0, w, h);
635
636         if (cairo_in_fill (cr, x, y))
637         {
638                 return TRUE;
639         }
640
641         return FALSE;
642 }
643
644
645
646
647 /*
648  * Local Variables:       -- emacs
649  * mode: C                -- emacs
650  * c-basic-offset: 8      -- emacs
651  * tab-width: 8           -- emacs
652  * indent-tabs-mode: nil  -- emacs
653  * End:                   -- emacs
654  */