]> git.sur5r.net Git - glabels/blob - libglabels/template.c
Allow for small error in comparison of dimensions.
[glabels] / libglabels / template.c
1 /*
2  *  template.c
3  *  Copyright (C) 2001-2009  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of libglabels.
6  *
7  *  libglabels is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser 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  *  libglabels 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 Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with libglabels.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "template.h"
24
25 #include <glib/gi18n.h>
26 #include <glib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <math.h>
31
32 #include "libglabels-private.h"
33
34 #include "db.h"
35 #include "paper.h"
36
37 /*===========================================*/
38 /* Private macros and constants.             */
39 /*===========================================*/
40
41 /* Allowed error when comparing dimensions. (0.5pts ~= .007in ~= .2mm) */
42 #define EPSILON 0.5
43
44 /*===========================================*/
45 /* Private types                             */
46 /*===========================================*/
47
48
49 /*===========================================*/
50 /* Private globals                           */
51 /*===========================================*/
52
53
54 /*===========================================*/
55 /* Local function prototypes                 */
56 /*===========================================*/
57
58 static gint         compare_origins              (gconstpointer           a,
59                                                   gconstpointer           b,
60                                                   gpointer                user_data);
61
62 /*===========================================*/
63 /* Functions.                                */
64 /*===========================================*/
65
66 /**
67  * lgl_template_new:
68  *   @brand:        Template brand
69  *   @part:         Template part name/number
70  *   @description:  Template descriptions
71  *   @paper_id:     Page size id
72  *   @page_width:   Page width in points, set to zero unless paper_id="Other"
73  *   @page_height:  Page height in points, set to zero unless paper_id="Other"
74  *
75  * Create a new template structure, with the given top-level attributes.  The
76  * created template will have no initial aliases, categories, or frames
77  * associated with it.  See lgl_template_add_alias(), lgl_template_add_category(),
78  * and lgl_template_add_frame() to add these.
79  *
80  * Returns: pointer to a newly allocated #lglTemplate structure.
81  *
82  */
83 lglTemplate *
84 lgl_template_new (const gchar         *brand,
85                   const gchar         *part,
86                   const gchar         *description,
87                   const gchar         *paper_id,
88                   gdouble              page_width,
89                   gdouble              page_height)
90 {
91         lglTemplate      *template;
92         lglTemplateAlias *alias;
93
94         template = g_new0 (lglTemplate,1);
95
96         template->brand       = g_strdup (brand);
97         template->part        = g_strdup (part);
98         template->description = g_strdup (description);
99         template->paper_id    = g_strdup (paper_id);
100         template->page_width  = page_width;
101         template->page_height = page_height;
102
103         /* Always include primary name in alias list. */
104         template->aliases = NULL;
105         alias = lgl_template_alias_new (brand, part);
106         lgl_template_add_alias (template, alias);
107
108         return template;
109 }
110
111
112 /**
113  * lgl_template_new_from_equiv:
114  *   @brand:        Template brand
115  *   @part:         Template part name/number
116  *   @equiv_part:   Name of equivalent part to base template on
117  *
118  * Create a new template structure based on an existing template.  The
119  * created template will be a duplicate of the original template, except with
120  * the new part name/number.
121  *
122  * Returns: pointer to a newly allocated #lglTemplate structure.
123  *
124  */
125 lglTemplate *
126 lgl_template_new_from_equiv (const gchar          *brand,
127                              const gchar          *part,
128                              const gchar          *equiv_part)
129 {
130         lglTemplate      *template;
131         GList            *p_alias;
132         lglTemplateAlias *alias;
133
134         template = lgl_db_lookup_template_from_brand_part (brand, equiv_part);
135         if (template)
136         {
137                 g_free (template->part);
138                 g_free (template->equiv_part);
139
140                 template->part       = g_strdup (part);
141                 template->equiv_part = g_strdup (equiv_part);
142
143                 for ( p_alias = template->aliases; p_alias != NULL; p_alias = p_alias->next )
144                 {
145                         alias = (lglTemplateAlias *)p_alias->data;
146                         lgl_template_alias_free (alias);
147                 }
148                 g_list_free (template->aliases);
149                 template->aliases = NULL;
150
151                 alias = lgl_template_alias_new (brand, part);
152                 lgl_template_add_alias (template, alias);
153         }
154         else
155         {
156                 g_message (_("Equivalent part (\"%s\") for \"%s\", not previously defined."),
157                            equiv_part, part);
158         }
159
160         return template;
161 }
162
163
164 /**
165  * lgl_template_get_name:
166  *   @template:  Pointer to template structure to test
167  *
168  * This function returns the name of the given template.  The name is the concetenation
169  * of the brand and part name/number.
170  *
171  * Returns:  A pointer to a newly allocated name string.  Should be freed with g_free().
172  *
173  */
174 gchar *
175 lgl_template_get_name (const lglTemplate  *template)
176 {
177         g_return_val_if_fail (template, NULL);
178
179         return g_strdup_printf ("%s %s", template->brand, template->part);
180 }
181
182
183 /**
184  * lgl_template_do_templates_match:
185  *   @template1:  Pointer to 1st template structure to test
186  *   @template2:  Pointer to 2nd template structure to test
187  *
188  * This function tests if the given templates match.  This is a simple test that only tests
189  * the brand and part name/number. It does not test if they are actually identical.
190  *
191  * Returns:  TRUE if the two templates match.
192  *
193  */
194 gboolean
195 lgl_template_do_templates_match (const lglTemplate  *template1,
196                                  const lglTemplate  *template2)
197 {
198         g_return_val_if_fail (template1, FALSE);
199         g_return_val_if_fail (template2, FALSE);
200
201         return (UTF8_EQUAL (template1->brand, template2->brand) &&
202                 UTF8_EQUAL (template1->part, template2->part));
203 }
204
205
206 /**
207  * lgl_template_does_brand_match:
208  *   @template:  Pointer to template structure to test
209  *   @brand:     Brand string
210  *
211  * This function tests if the brand of the template matches the given brand.
212  *
213  * Returns:  TRUE if the template matches the given brand.
214  *
215  */
216 gboolean
217 lgl_template_does_brand_match (const lglTemplate  *template,
218                                const gchar        *brand)
219 {
220         g_return_val_if_fail (template, FALSE);
221
222         /* NULL matches everything. */
223         if (brand == NULL)
224         {
225                 return TRUE;
226         }
227
228         return UTF8_EQUAL (template->brand, brand);
229 }
230
231
232 /**
233  * lgl_template_does_page_size_match:
234  *   @template:  Pointer to template structure to test
235  *   @paper_id:  Page size ID string
236  *
237  * This function tests if the page size of the template matches the given ID.
238  *
239  * Returns:  TRUE if the template matches the given page size ID.
240  *
241  */
242 gboolean
243 lgl_template_does_page_size_match (const lglTemplate  *template,
244                                    const gchar        *paper_id)
245 {
246         g_return_val_if_fail (template, FALSE);
247
248         /* NULL matches everything. */
249         if (paper_id == NULL)
250         {
251                 return TRUE;
252         }
253
254         return ASCII_EQUAL(paper_id, template->paper_id);
255 }
256
257
258 /**
259  * lgl_template_does_category_match:
260  *   @template:     Pointer to template structure to test
261  *   @category_id:  Category ID string
262  *
263  * This function tests if the given template belongs to the given category ID.
264  *
265  * Returns:  TRUE if the template matches the given category ID.
266  *
267  */
268 gboolean
269 lgl_template_does_category_match  (const lglTemplate  *template,
270                                    const gchar        *category_id)
271 {
272         GList *p;
273
274         g_return_val_if_fail (template, FALSE);
275
276         /* NULL matches everything. */
277         if (category_id == NULL)
278         {
279                 return TRUE;
280         }
281
282         for ( p=template->category_ids; p != NULL; p=p->next )
283         {
284                 if (ASCII_EQUAL(category_id, p->data))
285                 {
286                         return TRUE;
287                 }
288         }
289
290         return FALSE;
291 }
292
293
294 /**
295  * lgl_template_are_templates_identical:
296  *   @template1:  Pointer to 1st template structure to test
297  *   @template2:  Pointer to 2nd template structure to test
298  *
299  * This function tests if the given templates have identical size and layout properties.
300  *
301  * Returns:  TRUE if the two templates are identical.
302  *
303  */
304 gboolean
305 lgl_template_are_templates_identical (const lglTemplate   *template1,
306                                       const lglTemplate   *template2)
307 {
308         lglTemplateFrame  *frame1;
309         lglTemplateFrame  *frame2;
310         GList             *p1;
311         GList             *p2;
312         lglTemplateLayout *layout1;
313         lglTemplateLayout *layout2;
314         gboolean           match_found;
315
316
317         if (!UTF8_EQUAL (template1->paper_id, template2->paper_id) ||
318             (template1->page_width  != template2->page_width)      ||
319             (template1->page_height != template2->page_height))
320         {
321                 return FALSE;
322         }
323
324         frame1 = (lglTemplateFrame *)template1->frames->data;
325         frame2 = (lglTemplateFrame *)template2->frames->data;
326
327         if ( frame1->shape != frame2->shape )
328         {
329                 return FALSE;
330         }
331
332         switch ( frame1->shape )
333         {
334
335         case LGL_TEMPLATE_FRAME_SHAPE_RECT:
336                 if ( (fabs(frame1->rect.w - frame2->rect.w) > EPSILON) ||
337                      (fabs(frame1->rect.h - frame2->rect.h) > EPSILON) )
338                 {
339                         return FALSE;
340                 }
341                 break;
342
343         case LGL_TEMPLATE_FRAME_SHAPE_ELLIPSE:
344                 if ( (fabs(frame1->ellipse.w - frame2->ellipse.w) > EPSILON) ||
345                      (fabs(frame1->ellipse.h - frame2->ellipse.h) > EPSILON) )
346                 {
347                         return FALSE;
348                 }
349                 break;
350
351         case LGL_TEMPLATE_FRAME_SHAPE_ROUND:
352                 if ( fabs(frame1->round.r - frame2->round.r) > EPSILON )
353                 {
354                         return FALSE;
355                 }
356                 break;
357
358         case LGL_TEMPLATE_FRAME_SHAPE_CD:
359                 if ( (fabs(frame1->cd.r1 - frame2->cd.r1) > EPSILON) ||
360                      (fabs(frame1->cd.r2 - frame2->cd.r2) > EPSILON) )
361                 {
362                         return FALSE;
363                 }
364         }
365
366         for ( p1 = frame1->all.layouts; p1; p1 = p1->next )
367         {
368                 layout1 = (lglTemplateLayout *)p1->data;
369
370                 match_found = FALSE;
371                 for ( p2 = frame2->all.layouts; p2 && !match_found; p2 = p2->next )
372                 {
373                         layout2 = (lglTemplateLayout *)p2->data;
374
375                         if ( (layout1->nx == layout2->nx) &&
376                              (layout1->ny == layout2->ny) &&
377                              (fabs(layout1->x0 - layout2->x0) < EPSILON) &&
378                              (fabs(layout1->y0 - layout2->y0) < EPSILON) &&
379                              (fabs(layout1->dx - layout2->dx) < EPSILON) &&
380                              (fabs(layout1->dy - layout2->dy) < EPSILON) )
381                         {
382                                 match_found = TRUE;
383                         }
384
385                 }
386                 if ( !match_found )
387                 {
388                         return FALSE;
389                 }
390         }
391
392         return TRUE;
393 }
394
395
396 /**
397  * lgl_template_alias_new:
398  *   @brand:        Alias brand
399  *   @part:         Alias part name/number
400  *
401  * Create a new template alias structure, with the given brand and part number.
402  *
403  * Returns: pointer to a newly allocated #lglTemplateAlias structure.
404  *
405  */
406 lglTemplateAlias *
407 lgl_template_alias_new (const gchar         *brand,
408                         const gchar         *part)
409 {
410         lglTemplateAlias *alias;
411
412         alias = g_new0 (lglTemplateAlias,1);
413
414         alias->brand       = g_strdup (brand);
415         alias->part        = g_strdup (part);
416
417         return alias;
418 }
419
420
421 /**
422  * lgl_template_add_alias:
423  *   @template:  Pointer to template structure
424  *   @alias:     Alias string
425  *
426  * This function adds the given alias to a templates list of aliases.
427  *
428  */
429 void
430 lgl_template_add_alias (lglTemplate         *template,
431                         lglTemplateAlias    *alias)
432 {
433         g_return_if_fail (template);
434         g_return_if_fail (alias);
435
436         template->aliases = g_list_append (template->aliases, alias);
437 }
438  
439
440 /**
441  * lgl_template_add_frame:
442  *   @template:  Pointer to template structure
443  *   @frame:     Pointer to frame structure
444  *
445  * This function adds the given frame structure to the template.  Once added,
446  * the frame structure belongs to the given template; do not attempt to free
447  * it.
448  *
449  * Note: Currently glabels only supports a single frame per template.
450  *
451  */
452 void
453 lgl_template_add_frame (lglTemplate      *template,
454                         lglTemplateFrame *frame)
455 {
456         g_return_if_fail (template);
457         g_return_if_fail (frame);
458
459         template->frames = g_list_append (template->frames, frame);
460 }
461
462  
463 /**
464  * lgl_template_add_category:
465  *   @template:     Pointer to template structure
466  *   @category_id:  Category ID string
467  *
468  * This function adds the given category ID to a templates category list.
469  *
470  */
471 void
472 lgl_template_add_category (lglTemplate         *template,
473                            const gchar         *category_id)
474 {
475         g_return_if_fail (template);
476         g_return_if_fail (category_id);
477
478         template->category_ids = g_list_append (template->category_ids,
479                                                 g_strdup (category_id));
480 }
481
482  
483 /**
484  * lgl_template_frame_rect_new:
485  *   @id:      ID of frame.  (This should currently always be "0").
486  *   @w:       width of frame in points.
487  *   @h:       height of frame in points.
488  *   @r:       radius of rounded corners in points.  (Should be 0 for square corners.)
489  *   @x_waste: Amount of overprint to allow in the horizontal direction.
490  *   @y_waste: Amount of overprint to allow in the vertical direction.
491  *
492  * This function creates a new template frame for a rectangular label or card.
493  *
494  * Returns: Pointer to newly allocated #lglTemplateFrame structure.
495  *
496  */
497 lglTemplateFrame *
498 lgl_template_frame_rect_new  (const gchar         *id,
499                               gdouble              w,
500                               gdouble              h,
501                               gdouble              r,
502                               gdouble              x_waste,
503                               gdouble              y_waste)
504 {
505         lglTemplateFrame *frame;
506
507         frame = g_new0 (lglTemplateFrame, 1);
508
509         frame->shape = LGL_TEMPLATE_FRAME_SHAPE_RECT;
510         frame->rect.id = g_strdup (id);
511
512         frame->rect.w = w;
513         frame->rect.h = h;
514         frame->rect.r = r;
515         frame->rect.x_waste = x_waste;
516         frame->rect.y_waste = y_waste;
517
518         return frame;
519 }
520
521
522 /**
523  * lgl_template_frame_ellipse_new:
524  *   @id:      ID of frame.  (This should currently always be "0").
525  *   @w:       width of frame in points.
526  *   @h:       height of frame in points.
527  *   @waste:   Amount of overprint to allow in points.
528  *
529  * This function creates a new template frame for an elliptical label or card.
530  *
531  * Returns: Pointer to newly allocated #lglTemplateFrame structure.
532  *
533  */
534 lglTemplateFrame *
535 lgl_template_frame_ellipse_new  (const gchar         *id,
536                                  gdouble              w,
537                                  gdouble              h,
538                                  gdouble              waste)
539 {
540         lglTemplateFrame *frame;
541
542         frame = g_new0 (lglTemplateFrame, 1);
543
544         frame->shape = LGL_TEMPLATE_FRAME_SHAPE_ELLIPSE;
545         frame->ellipse.id = g_strdup (id);
546
547         frame->ellipse.w = w;
548         frame->ellipse.h = h;
549         frame->ellipse.waste = waste;
550
551         return frame;
552 }
553
554
555 /**
556  * lgl_template_frame_round_new:
557  *   @id:      ID of frame.  (This should currently always be "0").
558  *   @r:       radius of label in points.
559  *   @waste:   Amount of overprint to allow.
560  *
561  * This function creates a new template frame for a round label.
562  *
563  * Returns: Pointer to newly allocated #lglTemplateFrame structure.
564  *
565  */
566 lglTemplateFrame *
567 lgl_template_frame_round_new (const gchar         *id,
568                               gdouble              r,
569                               gdouble              waste)
570 {
571         lglTemplateFrame *frame;
572
573         frame = g_new0 (lglTemplateFrame, 1);
574
575         frame->shape = LGL_TEMPLATE_FRAME_SHAPE_ROUND;
576         frame->round.id = g_strdup (id);
577
578         frame->round.r = r;
579         frame->round.waste = waste;
580
581         return frame;
582 }
583
584                                                                                
585 /**
586  * lgl_template_frame_cd_new:
587  *   @id:      ID of frame.  (This should currently always be "0").
588  *   @r1:      outer radius of label in points.
589  *   @r2:      radius of center hole in points.
590  *   @w:       clip width of frame in points for business card CDs.  Should be 0 for no clipping.
591  *   @h:       clip height of frame in points for business card CDs.  Should be 0 for no clipping.
592  *   @waste:   Amount of overprint to allow.
593  *
594  * This function creates a new template frame for a CD/DVD label.
595  *
596  * Returns: Pointer to newly allocated #lglTemplateFrame structure.
597  *
598  */
599 lglTemplateFrame *
600 lgl_template_frame_cd_new (const gchar         *id,
601                            gdouble              r1,
602                            gdouble              r2,
603                            gdouble              w,
604                            gdouble              h,
605                            gdouble              waste)
606 {
607         lglTemplateFrame *frame;
608
609         frame = g_new0 (lglTemplateFrame, 1);
610
611         frame->shape = LGL_TEMPLATE_FRAME_SHAPE_CD;
612         frame->cd.id = g_strdup (id);
613
614         frame->cd.r1 = r1;
615         frame->cd.r2 = r2;
616         frame->cd.w  = w;
617         frame->cd.h  = h;
618         frame->cd.waste = waste;
619
620         return frame;
621 }
622
623
624 /**
625  * lgl_template_frame_get_size:
626  * @frame: #lglTemplateFrame structure to query
627  * @w: pointer to location to receive width of frame
628  * @h: pointer to location to receive height of frame
629  *
630  * Get size (width and height) of given #lglTemplateFrame in points.
631  *
632  */
633 void
634 lgl_template_frame_get_size (const lglTemplateFrame *frame,
635                              gdouble                *w,
636                              gdouble                *h)
637 {
638         g_return_if_fail (frame);
639
640         switch (frame->shape) {
641         case LGL_TEMPLATE_FRAME_SHAPE_RECT:
642                 *w = frame->rect.w;
643                 *h = frame->rect.h;
644                 break;
645         case LGL_TEMPLATE_FRAME_SHAPE_ELLIPSE:
646                 *w = frame->ellipse.w;
647                 *h = frame->ellipse.h;
648                 break;
649         case LGL_TEMPLATE_FRAME_SHAPE_ROUND:
650                 *w = 2.0 * frame->round.r;
651                 *h = 2.0 * frame->round.r;
652                 break;
653         case LGL_TEMPLATE_FRAME_SHAPE_CD:
654                 if (frame->cd.w == 0.0) {
655                         *w = 2.0 * frame->cd.r1;
656                 } else {
657                         *w = frame->cd.w;
658                 }
659                 if (frame->cd.h == 0.0) {
660                         *h = 2.0 * frame->cd.r1;
661                 } else {
662                         *h = frame->cd.h;
663                 }
664                 break;
665         default:
666                 *w = 0.0;
667                 *h = 0.0;
668                 break;
669         }
670 }
671
672
673 /**
674  * lgl_template_frame_get_n_labels:
675  * @frame: #lglTemplateFrame structure to query
676  *
677  * Get total number of labels per sheet corresponding to the given frame.
678  *
679  * Returns: number of labels per sheet.
680  *
681  */
682 gint
683 lgl_template_frame_get_n_labels (const lglTemplateFrame *frame)
684 {
685         gint               n_labels = 0;
686         GList             *p;
687         lglTemplateLayout *layout;
688
689         g_return_val_if_fail (frame, 0);
690
691         for ( p=frame->all.layouts; p != NULL; p=p->next ) {
692                 layout = (lglTemplateLayout *)p->data;
693
694                 n_labels += layout->nx * layout->ny;
695         }
696
697         return n_labels;
698 }
699
700
701 /**
702  * lgl_template_frame_get_layout_description
703  * @frame: #lglTemplateFrame structure to query
704  *
705  * Get a description of the label layout including number of labels per sheet.
706  *
707  * Returns: a newly allocation description string.
708  *
709  */
710 gchar *
711 lgl_template_frame_get_layout_description (const lglTemplateFrame *frame)
712 {
713         gint                    n_labels;
714         gchar                  *string;
715         lglTemplateLayout      *layout;
716
717         n_labels = lgl_template_frame_get_n_labels (frame);
718
719         if ( frame->all.layouts && (frame->all.layouts->next == NULL) )
720         {
721                 layout = (lglTemplateLayout *)frame->all.layouts->data;
722                 string = g_strdup_printf ("%d Ã— %d (%d %s)", layout->nx, layout->ny, n_labels, _("per sheet"));
723         }
724         else
725         {
726                 string = g_strdup_printf (_("%d %s"), n_labels, _("per sheet"));
727         }
728
729         return string;
730 }
731
732
733 /**
734  * lgl_template_frame_get_size_description
735  * @frame: #lglTemplateFrame structure to query
736  * @units: #lglUnits
737  *
738  * Get a description of the label size.
739  *
740  * Returns: a newly allocation description string.
741  *
742  */
743 gchar *
744 lgl_template_frame_get_size_description (const lglTemplateFrame *frame,
745                                          lglUnits                units)
746 {
747         const gchar               *units_string;
748         gdouble                    units_per_point;
749         gchar                     *string = NULL;
750
751         units_string    = lgl_units_get_name (units);
752         units_per_point = lgl_units_get_units_per_point (units);
753
754         switch (frame->shape) {
755         case LGL_TEMPLATE_FRAME_SHAPE_RECT:
756                 if ( units == LGL_UNITS_INCH ) {
757                         gchar *xstr, *ystr;
758
759                         xstr = lgl_str_format_fraction (frame->rect.w*units_per_point);
760                         ystr = lgl_str_format_fraction (frame->rect.h*units_per_point);
761                         string = g_strdup_printf ("%s Ã— %s %s",
762                                                   xstr, ystr, units_string);
763                         g_free (xstr);
764                         g_free (ystr);
765                 } else {
766                         string = g_strdup_printf ("%.5g Ã— %.5g %s",
767                                                   frame->rect.w*units_per_point,
768                                                   frame->rect.h*units_per_point,
769                                                   units_string);
770                 }
771                 break;
772         case LGL_TEMPLATE_FRAME_SHAPE_ELLIPSE:
773                 if ( units == LGL_UNITS_INCH ) {
774                         gchar *xstr, *ystr;
775
776                         xstr = lgl_str_format_fraction (frame->ellipse.w*units_per_point);
777                         ystr = lgl_str_format_fraction (frame->ellipse.h*units_per_point);
778                         string = g_strdup_printf ("%s Ã— %s %s",
779                                                   xstr, ystr, units_string);
780                         g_free (xstr);
781                         g_free (ystr);
782                 } else {
783                         string = g_strdup_printf ("%.5g Ã— %.5g %s",
784                                                   frame->ellipse.w*units_per_point,
785                                                   frame->ellipse.h*units_per_point,
786                                                   units_string);
787                 }
788                 break;
789         case LGL_TEMPLATE_FRAME_SHAPE_ROUND:
790                 if ( units == LGL_UNITS_INCH ) {
791                         gchar *dstr;
792
793                         dstr = lgl_str_format_fraction (2.0*frame->round.r*units_per_point);
794                         string = g_strdup_printf ("%s %s %s",
795                                                   dstr, units_string,
796                                                   _("diameter"));
797                         g_free (dstr);
798                 } else {
799                         string = g_strdup_printf ("%.5g %s %s",
800                                                   2.0*frame->round.r*units_per_point,
801                                                   units_string,
802                                                   _("diameter"));
803                 }
804                 break;
805         case LGL_TEMPLATE_FRAME_SHAPE_CD:
806                 if ( units == LGL_UNITS_INCH ) {
807                         gchar *dstr;
808
809                         dstr = lgl_str_format_fraction (2.0*frame->cd.r1*units_per_point);
810                         string = g_strdup_printf ("%s %s %s",
811                                                   dstr, units_string,
812                                                   _("diameter"));
813                         g_free (dstr);
814                 } else {
815                         string = g_strdup_printf ("%.5g %s %s",
816                                                   2.0*frame->cd.r1*units_per_point,
817                                                   units_string,
818                                                   _("diameter"));
819                 }
820                 break;
821         default:
822                 break;
823         }
824
825         return string;
826 }
827
828
829 /**
830  * lgl_template_frame_get_origins:
831  * @frame: #lglTemplateFrame structure to query
832  *
833  * Get an array of label origins for the given frame.  These origins represent the
834  * upper left hand corner of each label on a page corresponding to the given frame.
835  * The origins will be ordered geometrically left to right and then top to bottom.
836  * The array should be freed using g_free().
837  *
838  * Returns: A newly allocated array of #lglTemplateOrigin structures.
839  *
840  */
841 lglTemplateOrigin *
842 lgl_template_frame_get_origins (const lglTemplateFrame *frame)
843 {
844         gint               i_label, n_labels, ix, iy;
845         lglTemplateOrigin *origins;
846         GList             *p;
847         lglTemplateLayout *layout;
848
849         g_return_val_if_fail (frame, NULL);
850
851         n_labels = lgl_template_frame_get_n_labels (frame);
852         origins = g_new0 (lglTemplateOrigin, n_labels);
853
854         i_label = 0;
855         for ( p=frame->all.layouts; p != NULL; p=p->next ) {
856                 layout = (lglTemplateLayout *)p->data;
857
858                 for (iy = 0; iy < layout->ny; iy++) {
859                         for (ix = 0; ix < layout->nx; ix++, i_label++) {
860                                 origins[i_label].x = ix*layout->dx + layout->x0;
861                                 origins[i_label].y = iy*layout->dy + layout->y0;
862                         }
863                 }
864         }
865
866         g_qsort_with_data (origins, n_labels, sizeof(lglTemplateOrigin),
867                            compare_origins, NULL);
868
869         return origins;
870 }
871
872
873 /**
874  * lgl_template_frame_add_layout:
875  *   @frame:  Pointer to template frame to add layout to.
876  *   @layout: Pointer to layout structure to add to frame.
877  *
878  * This function adds a layout structure to the given template frame.
879  *
880  */
881 void
882 lgl_template_frame_add_layout (lglTemplateFrame   *frame,
883                                lglTemplateLayout  *layout)
884 {
885         g_return_if_fail (frame);
886         g_return_if_fail (layout);
887
888         frame->all.layouts = g_list_append (frame->all.layouts, layout);
889 }
890  
891
892 /**
893  * lgl_template_frame_add_markup:
894  *   @frame:  Pointer to template frame to add markup to.
895  *   @markup: Pointer to markup structure to add to frame.
896  *
897  * This function adds a markup structure to the given template frame.
898  *
899  */
900 void
901 lgl_template_frame_add_markup (lglTemplateFrame   *frame,
902                                lglTemplateMarkup  *markup)
903 {
904         g_return_if_fail (frame);
905         g_return_if_fail (markup);
906
907         frame->all.markups = g_list_append (frame->all.markups, markup);
908 }
909  
910
911 /**
912  * lgl_template_layout_new:
913  *   @nx:  Number of labels across.
914  *   @ny:  Number of labels down.
915  *   @x0:  X coordinate of the top-left corner of the top-left label in the layout in points.
916  *   @y0:  Y coordinate of the top-left corner of the top-left label in the layout in points.
917  *   @dx:  Horizontal pitch in points.  This is the distance from left-edge to left-edge.
918  *   @dy:  Vertical pitch in points.  This is the distance from top-edge to top-edge.
919  *
920  * This function creates a new layout structure with the given parameters.
921  *
922  * Returns: a newly allocated #lglTemplateLayout structure.
923  *
924  */
925 lglTemplateLayout *
926 lgl_template_layout_new (gint    nx,
927                          gint    ny,
928                          gdouble x0,
929                          gdouble y0,
930                          gdouble dx,
931                          gdouble dy)
932 {
933         lglTemplateLayout *layout;
934
935         layout = g_new0 (lglTemplateLayout, 1);
936
937         layout->nx = nx;
938         layout->ny = ny;
939         layout->x0 = x0;
940         layout->y0 = y0;
941         layout->dx = dx;
942         layout->dy = dy;
943
944         return layout;
945 }
946
947
948 /**
949  * lgl_template_markup_margin_new:
950  *   @size: margin size in points.
951  *
952  * This function creates a new margin markup structure.
953  *
954  * Returns: a newly allocated #lglTemplateMarkup structure.
955  *
956  */
957 lglTemplateMarkup *
958 lgl_template_markup_margin_new (gdouble size)
959 {
960         lglTemplateMarkup *markup;
961
962         markup = g_new0 (lglTemplateMarkup, 1);
963
964         markup->type        = LGL_TEMPLATE_MARKUP_MARGIN;
965         markup->margin.size = size;
966
967         return markup;
968 }
969
970
971 /**
972  * lgl_template_markup_line_new:
973  *   @x1: x coordinate of first endpoint.
974  *   @y1: y coordinate of first endpoint.
975  *   @x2: x coordinate of second endpoint.
976  *   @y2: y coordinate of second endpoint.
977  *
978  * This function creates a new line markup structure.
979  *
980  * Returns: a newly allocated #lglTemplateMarkup structure.
981  *
982  */
983 lglTemplateMarkup *
984 lgl_template_markup_line_new (gdouble x1,
985                               gdouble y1,
986                               gdouble x2,
987                               gdouble y2)
988 {
989         lglTemplateMarkup *markup;
990
991         markup = g_new0 (lglTemplateMarkup, 1);
992
993         markup->type        = LGL_TEMPLATE_MARKUP_LINE;
994         markup->line.x1     = x1;
995         markup->line.y1     = y1;
996         markup->line.x2     = x2;
997         markup->line.y2     = y2;
998
999         return markup;
1000 }
1001
1002
1003 /**
1004  * lgl_template_markup_circle_new:
1005  *   @x0: x coordinate of center of circle.
1006  *   @y0: y coordinate of center of circle.
1007  *   @r:  radius of circle.
1008  *
1009  * This function creates a new circle markup structure.
1010  *
1011  * Returns: a newly allocated #lglTemplateMarkup structure.
1012  *
1013  */
1014 lglTemplateMarkup *
1015 lgl_template_markup_circle_new (gdouble x0,
1016                                 gdouble y0,
1017                                 gdouble r)
1018 {
1019         lglTemplateMarkup *markup;
1020
1021         markup = g_new0 (lglTemplateMarkup, 1);
1022
1023         markup->type        = LGL_TEMPLATE_MARKUP_CIRCLE;
1024         markup->circle.x0   = x0;
1025         markup->circle.y0   = y0;
1026         markup->circle.r    = r;
1027
1028         return markup;
1029 }
1030
1031
1032 /**
1033  * lgl_template_markup_rect_new:
1034  *   @x1: x coordinate of top-left corner of rectangle.
1035  *   @y1: y coordinate of top-left corner of rectangle.
1036  *   @w:  width of rectangle.
1037  *   @h:  height of rectangle.
1038  *   @r:  radius of rounded corner.
1039  *
1040  * This function creates a new rectangle markup structure.
1041  *
1042  * Returns: a newly allocated #lglTemplateMarkup structure.
1043  *
1044  */
1045 lglTemplateMarkup *
1046 lgl_template_markup_rect_new (gdouble x1,
1047                               gdouble y1,
1048                               gdouble w,
1049                               gdouble h,
1050                               gdouble r)
1051 {
1052         lglTemplateMarkup *markup;
1053
1054         markup = g_new0 (lglTemplateMarkup, 1);
1055
1056         markup->type        = LGL_TEMPLATE_MARKUP_RECT;
1057         markup->rect.x1     = x1;
1058         markup->rect.y1     = y1;
1059         markup->rect.w      = w;
1060         markup->rect.h      = h;
1061         markup->rect.r      = r;
1062
1063         return markup;
1064 }
1065
1066
1067 /**
1068  * lgl_template_markup_ellipse_new:
1069  *   @x1: x coordinate of top-left corner of ellipse.
1070  *   @y1: y coordinate of top-left corner of ellipse.
1071  *   @w:  width of ellipse.
1072  *   @h:  height of ellipse.
1073  *
1074  * This function creates a new ellipse markup structure.
1075  *
1076  * Returns: a newly allocated #lglTemplateMarkup structure.
1077  *
1078  */
1079 lglTemplateMarkup *
1080 lgl_template_markup_ellipse_new (gdouble x1,
1081                                  gdouble y1,
1082                                  gdouble w,
1083                                  gdouble h)
1084 {
1085         lglTemplateMarkup *markup;
1086
1087         markup = g_new0 (lglTemplateMarkup, 1);
1088
1089         markup->type        = LGL_TEMPLATE_MARKUP_ELLIPSE;
1090         markup->ellipse.x1     = x1;
1091         markup->ellipse.y1     = y1;
1092         markup->ellipse.w      = w;
1093         markup->ellipse.h      = h;
1094
1095         return markup;
1096 }
1097
1098
1099 /**
1100  * lgl_template_dup:
1101  *   @orig_template: Template to duplicate.
1102  *
1103  * This function duplicates a template structure.
1104  *
1105  * Returns:  a newly allocated #lglTemplate structure.
1106  *
1107  */
1108 lglTemplate *
1109 lgl_template_dup (const lglTemplate *orig_template)
1110 {
1111         lglTemplate         *template;
1112         lglTemplateAlias    *alias;
1113         GList               *p;
1114         lglTemplateFrame    *frame;
1115
1116         g_return_val_if_fail (orig_template, NULL);
1117
1118         template = lgl_template_new (orig_template->brand,
1119                                      orig_template->part,
1120                                      orig_template->description,
1121                                      orig_template->paper_id,
1122                                      orig_template->page_width,
1123                                      orig_template->page_height);
1124
1125         template->equiv_part  = g_strdup (orig_template->equiv_part);
1126         template->product_url = g_strdup (orig_template->product_url);
1127
1128         for ( p=orig_template->aliases; p != NULL; p=p->next )
1129         {
1130                 alias = (lglTemplateAlias *)p->data;
1131
1132                 if ( !(UTF8_EQUAL (template->brand, alias->brand) &&
1133                        UTF8_EQUAL (template->part, alias->part)) )
1134                 {
1135                         lgl_template_add_alias (template, lgl_template_alias_dup (alias));
1136                 }
1137
1138         }
1139
1140         for ( p=orig_template->category_ids; p != NULL; p=p->next )
1141         {
1142                 lgl_template_add_category (template, p->data);
1143         }
1144
1145         for ( p=orig_template->frames; p != NULL; p=p->next )
1146         {
1147                 frame = (lglTemplateFrame *)p->data;
1148
1149                 lgl_template_add_frame (template, lgl_template_frame_dup (frame));
1150         }
1151
1152         return template;
1153 }
1154
1155
1156 /**
1157  * lgl_template_free:
1158  *   @template: Template to free.
1159  *
1160  * This function frees all memory associated with given template structure.
1161  *
1162  */
1163 void
1164 lgl_template_free (lglTemplate *template)
1165 {
1166         GList            *p;
1167         lglTemplateFrame *frame;
1168
1169         if ( template != NULL ) {
1170
1171                 g_free (template->brand);
1172                 template->brand = NULL;
1173
1174                 g_free (template->part);
1175                 template->part = NULL;
1176
1177                 g_free (template->description);
1178                 template->description = NULL;
1179
1180                 g_free (template->paper_id);
1181                 template->paper_id = NULL;
1182
1183                 for ( p=template->aliases; p != NULL; p=p->next ) {
1184
1185                         lgl_template_alias_free (p->data);
1186                         p->data = NULL;
1187
1188                 }
1189                 g_list_free (template->aliases);
1190                 template->aliases = NULL;
1191
1192                 for ( p=template->category_ids; p != NULL; p=p->next ) {
1193
1194                         g_free (p->data);
1195                         p->data = NULL;
1196
1197                 }
1198                 g_list_free (template->category_ids);
1199                 template->category_ids = NULL;
1200
1201                 for ( p=template->frames; p != NULL; p=p->next ) {
1202
1203                         frame = (lglTemplateFrame *)p->data;
1204
1205                         lgl_template_frame_free (frame);
1206                         p->data = NULL;
1207                 }
1208                 g_list_free (template->frames);
1209                 template->frames = NULL;
1210
1211                 g_free (template);
1212
1213         }
1214
1215 }
1216
1217
1218 /**
1219  * lgl_template_alias_dup:
1220  *   @orig_alias: Alias to duplicate.
1221  *
1222  * This function duplicates a template alias structure.
1223  *
1224  * Returns:  a newly allocated #lglTemplateAlias structure.
1225  *
1226  */
1227 lglTemplateAlias *
1228 lgl_template_alias_dup (const lglTemplateAlias *orig_alias)
1229 {
1230         g_return_val_if_fail (orig_alias, NULL);
1231
1232         return lgl_template_alias_new (orig_alias->brand, orig_alias->part);
1233 }
1234
1235
1236 /**
1237  * lgl_template_alias_free:
1238  *   @alias: Alias to free.
1239  *
1240  * This function frees all memory associated with given template alias structure.
1241  *
1242  */
1243 void
1244 lgl_template_alias_free (lglTemplateAlias *alias)
1245 {
1246
1247         if ( alias != NULL )
1248         {
1249                 g_free (alias->brand);
1250                 alias->brand = NULL;
1251
1252                 g_free (alias->part);
1253                 alias->part = NULL;
1254
1255                 g_free (alias);
1256         }
1257 }
1258
1259
1260 /**
1261  * lgl_template_frame_dup:
1262  *   @orig_frame: Frame to duplicate.
1263  *
1264  * This function duplicates a template frame structure.
1265  *
1266  * Returns:  a newly allocated #lglTemplateFrame structure.
1267  *
1268  */
1269 lglTemplateFrame *
1270 lgl_template_frame_dup (const lglTemplateFrame *orig_frame)
1271 {
1272         lglTemplateFrame    *frame;
1273         GList               *p;
1274         lglTemplateLayout   *layout;
1275         lglTemplateMarkup   *markup;
1276
1277         g_return_val_if_fail (orig_frame, NULL);
1278
1279         switch (orig_frame->shape) {
1280
1281         case LGL_TEMPLATE_FRAME_SHAPE_RECT:
1282                 frame =
1283                         lgl_template_frame_rect_new (orig_frame->all.id,
1284                                                      orig_frame->rect.w,
1285                                                      orig_frame->rect.h,
1286                                                      orig_frame->rect.r,
1287                                                      orig_frame->rect.x_waste,
1288                                                      orig_frame->rect.y_waste);
1289                 break;
1290
1291         case LGL_TEMPLATE_FRAME_SHAPE_ELLIPSE:
1292                 frame =
1293                         lgl_template_frame_ellipse_new (orig_frame->all.id,
1294                                                         orig_frame->ellipse.w,
1295                                                         orig_frame->ellipse.h,
1296                                                         orig_frame->ellipse.waste);
1297                 break;
1298
1299         case LGL_TEMPLATE_FRAME_SHAPE_ROUND:
1300                 frame =
1301                         lgl_template_frame_round_new (orig_frame->all.id,
1302                                                       orig_frame->round.r,
1303                                                       orig_frame->round.waste);
1304                 break;
1305
1306         case LGL_TEMPLATE_FRAME_SHAPE_CD:
1307                 frame =
1308                         lgl_template_frame_cd_new (orig_frame->all.id,
1309                                                    orig_frame->cd.r1,
1310                                                    orig_frame->cd.r2,
1311                                                    orig_frame->cd.w,
1312                                                    orig_frame->cd.h,
1313                                                    orig_frame->cd.waste);
1314                 break;
1315
1316         default:
1317                 return NULL;
1318                 break;
1319         }
1320
1321         for ( p=orig_frame->all.layouts; p != NULL; p=p->next ) {
1322
1323                 layout = (lglTemplateLayout *)p->data;
1324
1325                 lgl_template_frame_add_layout (frame, lgl_template_layout_dup (layout));
1326         }
1327
1328         for ( p=orig_frame->all.markups; p != NULL; p=p->next ) {
1329
1330                 markup = (lglTemplateMarkup *)p->data;
1331
1332                 lgl_template_frame_add_markup (frame, lgl_template_markup_dup (markup));
1333         }
1334
1335         return frame;
1336 }
1337
1338
1339 /**
1340  * lgl_template_frame_free:
1341  *   @frame: Frame to free.
1342  *
1343  * This function frees all memory associated with given template frame structure.
1344  *
1345  */
1346 void
1347 lgl_template_frame_free (lglTemplateFrame *frame)
1348 {
1349         GList                *p;
1350         lglTemplateLayout    *layout;
1351         lglTemplateMarkup    *markup;
1352
1353         if ( frame != NULL ) {
1354
1355                 g_free (frame->all.id);
1356                 frame->all.id = NULL;
1357
1358                 for ( p=frame->all.layouts; p != NULL; p=p->next ) {
1359
1360                         layout = (lglTemplateLayout *)p->data;
1361
1362                         lgl_template_layout_free (layout);
1363                         p->data = NULL;
1364                 }
1365                 g_list_free (frame->all.layouts);
1366                 frame->all.layouts = NULL;
1367
1368                 for ( p=frame->all.markups; p != NULL; p=p->next ) {
1369
1370                         markup = (lglTemplateMarkup *)p->data;
1371
1372                         lgl_template_markup_free (markup);
1373                         p->data = NULL;
1374                 }
1375                 g_list_free (frame->all.markups);
1376                 frame->all.markups = NULL;
1377
1378                 g_free (frame);
1379
1380         }
1381
1382 }
1383
1384
1385 /**
1386  * lgl_template_layout_dup:
1387  *   @orig_layout: Layout to duplicate.
1388  *
1389  * This function duplicates a template layout structure.
1390  *
1391  * Returns:  a newly allocated #lglTemplateLayout structure.
1392  *
1393  */
1394 lglTemplateLayout *
1395 lgl_template_layout_dup (const lglTemplateLayout *orig_layout)
1396 {
1397         lglTemplateLayout *layout;
1398
1399         g_return_val_if_fail (orig_layout, NULL);
1400
1401         layout = g_new0 (lglTemplateLayout, 1);
1402
1403         /* copy contents */
1404         *layout = *orig_layout;
1405
1406         return layout;
1407 }
1408
1409
1410 /**
1411  * lgl_template_layout_free:
1412  *   @layout: Layout to free.
1413  *
1414  * This function frees all memory associated with given template layout structure.
1415  *
1416  */
1417 void
1418 lgl_template_layout_free (lglTemplateLayout *layout)
1419 {
1420         g_free (layout);
1421 }
1422
1423
1424 /**
1425  * lgl_template_markup_dup:
1426  *   @orig_markup: Markup to duplicate.
1427  *
1428  * This function duplicates a template markup structure.
1429  *
1430  * Returns:  a newly allocated #lglTemplateMarkup structure.
1431  *
1432  */
1433 lglTemplateMarkup *
1434 lgl_template_markup_dup (const lglTemplateMarkup *orig_markup)
1435 {
1436         lglTemplateMarkup *markup;
1437
1438         g_return_val_if_fail (orig_markup, NULL);
1439
1440         markup = g_new0 (lglTemplateMarkup, 1);
1441
1442         *markup = *orig_markup;
1443
1444         return markup;
1445 }
1446
1447
1448 /**
1449  * lgl_template_markup_free:
1450  *   @markup: Markup to free.
1451  *
1452  * This function frees all memory associated with given template markup structure.
1453  *
1454  */
1455 void
1456 lgl_template_markup_free (lglTemplateMarkup *markup)
1457 {
1458         g_free (markup);
1459 }
1460
1461
1462 static gint
1463 compare_origins (gconstpointer a,
1464                  gconstpointer b,
1465                  gpointer      user_data)
1466 {
1467         const lglTemplateOrigin *a_origin = a, *b_origin = b;
1468
1469         if ( a_origin->y < b_origin->y ) {
1470                 return -1;
1471         } else if ( a_origin->y > b_origin->y ) {
1472                 return +1;
1473         } else {
1474                 if ( a_origin->x < b_origin->x ) {
1475                         return -1;
1476                 } else if ( a_origin->x > b_origin->x ) {
1477                         return +1;
1478                 } else {
1479                         return 0; /* hopefully 2 labels won't have the same origin */
1480                 }
1481         }
1482 }
1483
1484
1485 /**
1486  * lgl_template_print:
1487  *   @template: template
1488  *
1489  * Print template details (for debugging purposes).
1490  *
1491  */
1492 void
1493 lgl_template_print (const lglTemplate *template)
1494 {
1495         GList            *p;
1496         lglTemplateAlias *alias;
1497
1498         g_print ("---- %s( TEMPLATE=%p ) ----\n", __FUNCTION__, template);
1499
1500         g_print("brand=\"%s\", part=\"%s\", description=\"%s\"\n",
1501                 template->brand, template->part, template->description);
1502
1503         g_print("paper_id=\"%s\", page_width=%g, page_height=%g\n",
1504                 template->paper_id, template->page_width, template->page_height);
1505
1506         for (p=template->aliases; p!=NULL; p=p->next)
1507         {
1508                 alias = (lglTemplateAlias *)p->data;
1509                 g_print("Alias: brand=\"%s\", part=\"%s\"\n", alias->brand, alias->part);
1510
1511         }
1512
1513         g_print ("\n");
1514
1515 }
1516
1517
1518
1519 /*
1520  * Local Variables:       -- emacs
1521  * mode: C                -- emacs
1522  * c-basic-offset: 8      -- emacs
1523  * tab-width: 8           -- emacs
1524  * indent-tabs-mode: nil  -- emacs
1525  * End:                   -- emacs
1526  */