1 /*
2  * render.c - Generic Rendered Format
3  *
4  * Initiall written by Sam Lown for use in gLabels. Converts encoded
5  * data into a generic internal structure of lines and characters
6  * usable in external applications.
7  */
8 
9 /*
10     libzint - the open source barcode library
11     Copyright (C) 2009-2017 Robin Stuart <rstuart114@gmail.com>
12 
13     Redistribution and use in source and binary forms, with or without
14     modification, are permitted provided that the following conditions
15     are met:
16 
17     1. Redistributions of source code must retain the above copyright
18        notice, this list of conditions and the following disclaimer.
19     2. Redistributions in binary form must reproduce the above copyright
20        notice, this list of conditions and the following disclaimer in the
21        documentation and/or other materials provided with the distribution.
22     3. Neither the name of the project nor the names of its contributors
23        may be used to endorse or promote products derived from this software
24        without specific prior written permission.
25 
26     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
30     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36     SUCH DAMAGE.
37  */
38 
39 #include <locale.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #ifdef _MSC_VER
44 #include <malloc.h>
45 #endif
46 #include "common.h"
47 
48 #define   GL_CONST  2.8346
49 
50 struct zint_render_line *render_plot_create_line(float x, float y, float width, float length);
51 int render_plot_add_line(struct zint_symbol *symbol, struct zint_render_line *line, struct zint_render_line **last_line);
52 struct zint_render_ring *render_plot_create_ring(float x, float y, float radius, float line_width);
53 int render_plot_add_ring(struct zint_symbol *symbol, struct zint_render_ring *ring, struct zint_render_ring **last_ring);
54 struct zint_render_hexagon *render_plot_create_hexagon(float x, float y);
55 int render_plot_add_hexagon(struct zint_symbol *symbol, struct zint_render_hexagon *ring, struct zint_render_hexagon **last_hexagon);
56 
57 int render_plot_add_string(struct zint_symbol *symbol, unsigned char *text, float x, float y, float fsize, float width, struct zint_render_string **last_string);
58 
render_plot(struct zint_symbol * symbol,const float width,const float height)59 int render_plot(struct zint_symbol *symbol, const float width, const float height) {
60     struct zint_render *render;
61     struct zint_render_line *line, *last_line = NULL;
62     struct zint_render_string *last_string = NULL;
63     struct zint_render_ring *ring, *last_ring = NULL;
64     struct zint_render_hexagon *hexagon, *last_hexagon = NULL;
65 
66     int i, r, block_width, latch, this_row;
67     float textpos, textwidth, large_bar_height, preset_height, row_height, row_posn = 0.0;
68     // int error_number = 0;
69     int text_offset, text_height, xoffset, yoffset, textdone, main_symbol_width_x, addon_width_x;
70     char addon[6], textpart[10];
71     int large_bar_count, symbol_lead_in, total_symbol_width_x, total_area_width_x;
72     float addon_text_posn;
73     float default_text_posn;
74     float scaler;
75     const char *locale = NULL;
76     int hide_text = 0;
77     float required_aspect;
78     float symbol_aspect = 1;
79     float x_dimension;
80     int upceanflag = 0;
81 
82     // Allocate memory for the rendered version
83     render = symbol->rendered = (struct zint_render *) malloc(sizeof (struct zint_render));
84     if (!symbol->rendered) return ZINT_ERROR_MEMORY;
85     render->lines = NULL;
86     render->strings = NULL;
87     render->rings = NULL;
88     render->hexagons = NULL;
89 
90     locale = setlocale(LC_ALL, "C");
91 
92     row_height = 0;
93     textdone = 0;
94     textpos = 0.0;
95     main_symbol_width_x = symbol->width;
96     strcpy(addon, "");
97     symbol_lead_in = 0;
98     addon_text_posn = 0.0;
99     addon_width_x = 0;
100 
101     /*
102      * Determine if there will be any addon texts and text height
103      */
104     latch = 0;
105     r = 0;
106     /* Isolate add-on text */
107     if (is_extendable(symbol->symbology)) {
108         for(i = 0; i < (int)ustrlen(symbol->text); i++) {
109             if (latch == 1) {
110                 addon[r] = symbol->text[i];
111                 r++;
112             }
113             if (symbol->text[i] == '+') {
114                 latch = 1;
115             }
116         }
117     }
118     addon[r] = '\0';
119 
120     if ((!symbol->show_hrt) || (ustrlen(symbol->text) == 0)) {
121         hide_text = 1;
122         text_height = text_offset = 0.0;
123     } else {
124         text_height = 9.0;
125         text_offset = 2.0;
126     }
127 
128 
129     /*
130      * Calculate the width of the barcode, especially if there are any extra
131      * borders or white space to add.
132      */
133 
134     while (!(module_is_set(symbol, symbol->rows - 1, symbol_lead_in))) {
135         symbol_lead_in++;
136     }
137 
138     /* Certain symbols need whitespace otherwise characters get chopped off the sides */
139     if ((((symbol->symbology == BARCODE_EANX) && (symbol->rows == 1)) || (symbol->symbology == BARCODE_EANX_CC))
140             || (symbol->symbology == BARCODE_ISBNX)) {
141         switch (ustrlen(symbol->text)) {
142             case 13: /* EAN 13 */
143             case 16:
144             case 19:
145                 if (symbol->whitespace_width == 0) {
146                     symbol->whitespace_width = 10;
147                 }
148                 main_symbol_width_x = 96 + symbol_lead_in;
149                 upceanflag = 13;
150                 break;
151             case 2:
152                 main_symbol_width_x = 22 + symbol_lead_in;
153                 upceanflag = 2;
154                 break;
155             case 5:
156                 main_symbol_width_x = 49 + symbol_lead_in;
157                 upceanflag = 5;
158                 break;
159             default:
160                 main_symbol_width_x = 68 + symbol_lead_in;
161                 upceanflag = 8;
162         }
163         switch (ustrlen(symbol->text)) {
164             case 11:
165             case 16:
166                 /* EAN-2 add-on */
167                 addon_width_x = 31;
168                 break;
169             case 14:
170             case 19:
171                 /* EAN-5 add-on */
172                 addon_width_x = 58;
173                 break;
174         }
175     }
176     else if (((symbol->symbology == BARCODE_UPCA) && (symbol->rows == 1)) || (symbol->symbology == BARCODE_UPCA_CC)) {
177         upceanflag = 12;
178         if (symbol->whitespace_width < 10) {
179             symbol->whitespace_width = 10;
180             main_symbol_width_x = 96 + symbol_lead_in;
181         }
182         switch (ustrlen(symbol->text)) {
183             case 15:
184                 /* EAN-2 add-on */
185                 addon_width_x = 31;
186                 break;
187             case 18:
188                 /* EAN-5 add-on */
189                 addon_width_x = 58;
190                 break;
191         }
192     }
193     else if (((symbol->symbology == BARCODE_UPCE) && (symbol->rows == 1)) || (symbol->symbology == BARCODE_UPCE_CC)) {
194         upceanflag = 6;
195         if (symbol->whitespace_width == 0) {
196             symbol->whitespace_width = 10;
197             main_symbol_width_x = 51 + symbol_lead_in;
198         }
199         switch (ustrlen(symbol->text)) {
200             case 11:
201                 /* EAN-2 add-on */
202                 addon_width_x = 31;
203                 break;
204             case 14:
205                 /* EAN-5 add-on */
206                 addon_width_x = 58;
207                 break;
208         }
209     }
210 
211     total_symbol_width_x = 0.0 + main_symbol_width_x + addon_width_x;
212     total_area_width_x = total_symbol_width_x + (2 * (symbol->border_width + symbol->whitespace_width));
213 
214     xoffset = symbol->border_width + symbol->whitespace_width;
215     yoffset = symbol->border_width;
216 
217     // Determine if height should be overridden
218     large_bar_count = 0;
219     preset_height = 0.0;
220     for (i = 0; i < symbol->rows; i++) {
221         preset_height += symbol->row_height[i];
222         if (symbol->row_height[i] == 0) {
223             large_bar_count++;
224         }
225     }
226 
227     if (large_bar_count == 0) {
228         required_aspect = width / height;
229         symbol_aspect = (total_symbol_width_x + (2 * xoffset)) / (preset_height + (2 * yoffset) + text_offset + text_height);
230         symbol->height = (int) preset_height;
231         if (required_aspect > symbol_aspect) {
232             /* the area is too wide */
233             scaler = height / (preset_height + (2 * yoffset) + text_offset + text_height);
234             render->width = symbol_aspect * height;
235             render->height = height;
236         } else {
237             /* the area is too high */
238             scaler = width / (total_symbol_width_x + (2 * xoffset));
239             render->width = width;
240             render->height = width / symbol_aspect;
241         }
242     } else {
243         scaler = width / (total_symbol_width_x + (2 * xoffset));
244         symbol->height = (int) ((height / scaler) - ((2 * yoffset) + text_offset + text_height));
245 
246         render->width = width;
247         render->height = height;
248     }
249     large_bar_height = (symbol->height - preset_height) / large_bar_count;
250 
251     if ((symbol->output_options & BARCODE_BOX) || (symbol->output_options & BARCODE_BIND)) {
252         default_text_posn = (symbol->height + text_offset + symbol->border_width + symbol->border_width) * scaler;
253     } else {
254         default_text_posn = (symbol->height + text_offset + symbol->border_width) * scaler;
255     }
256 
257     x_dimension = render->width / total_area_width_x;
258     x_dimension /= GL_CONST;
259 
260     /* Set minimum size of symbol */
261     /* Barcode must be at least 2mm high by 2mm across */
262     if (render->height < ((x_dimension * ((2 * symbol->border_width) + text_offset + text_height)) + 2.0) * GL_CONST) {
263         render->height = ((x_dimension * ((2 * symbol->border_width) + text_offset + text_height)) + 2.0) * GL_CONST;
264     }
265     if (render->width < (2.0 * GL_CONST)) {
266         render->width = (2.0 * GL_CONST);
267     }
268 
269     if (symbol->symbology == BARCODE_CODABAR) {
270         /* The minimum X-dimension of Codabar is 0.191mm. The minimum bar height is 5mm */
271         if (x_dimension < 0.191) {
272             render->width = 0.191 * GL_CONST * total_area_width_x;
273         }
274         if (render->height < ((x_dimension * ((2 * symbol->border_width) + text_offset + text_height)) + 5.0) * GL_CONST) {
275             render->height = ((x_dimension * ((2 * symbol->border_width) + text_offset + text_height)) + 5.0) * GL_CONST;
276         }
277     }
278     else if (symbol->symbology == BARCODE_CODE49) {
279         /* The minimum X-dimension of Code 49 is 0.191mm */
280         if (x_dimension < 0.191) {
281             render->width = 0.191 * GL_CONST * total_area_width_x;
282             render->height = render->width / symbol_aspect;
283         }
284     }
285 
286     if (upceanflag != 0) {
287         /* The X-dimension of UPC/EAN symbols is fixed at 0.330mm */
288         /* NOTE: This code will need adjustment before it correctly deals with composite symbols */
289         render->width = 0.330 * GL_CONST * total_area_width_x;
290         /* The height is also fixed */
291         switch (upceanflag) {
292             case 6:
293             case 12:
294             case 13:
295                 /* UPC-A, UPC-E and EAN-13 */
296                 /* Height of bars should be 22.85mm */
297                 render->height = ((0.330 * ((2 * symbol->border_width) + text_offset + text_height)) + 22.85) * GL_CONST;
298                 break;
299             case 8:
300                 /* EAN-8 */
301                 /* Height of bars should be 18.23mm */
302                 render->height = ((0.330 * ((2 * symbol->border_width) + text_offset + text_height)) + 18.23) * GL_CONST;
303                 break;
304             default:
305                 /* EAN-2 and EAN-5 */
306                 /* Height of bars should be 21.10mm */
307                 render->height = ((0.330 * ((2 * symbol->border_width) + text_offset + text_height)) + 21.10) * GL_CONST;
308         }
309     }
310 
311     if (symbol->symbology == BARCODE_ONECODE) {
312         /* The size of USPS Intelligent Mail barcode is fixed */
313         render->width = 0.508 * GL_CONST * total_area_width_x;
314         render->height = 4.064 * GL_CONST;
315     }
316     else if ((symbol->symbology == BARCODE_POSTNET) || (symbol->symbology == BARCODE_PLANET)) {
317         /* The size of PostNet and PLANET are fized */
318         render->width = 0.508 * GL_CONST * total_area_width_x;
319         render->height = 2.921 * GL_CONST;
320     }
321     else if (((symbol->symbology == BARCODE_AUSPOST) || (symbol->symbology == BARCODE_AUSREPLY)) ||
322             ((symbol->symbology == BARCODE_AUSROUTE) || (symbol->symbology == BARCODE_AUSREDIRECT))) {
323         /* Australia Post use the same sizes as USPS */
324         render->width = 0.508 * GL_CONST * total_area_width_x;
325         render->height = 4.064 * GL_CONST;
326     }
327     else if ((symbol->symbology == BARCODE_RM4SCC) || (symbol->symbology == BARCODE_KIX)) {
328         /* Royal Mail and KIX Code uses 22 bars per inch */
329         render->width = 0.577 * GL_CONST * total_area_width_x;
330         render->height = 5.22 * GL_CONST;
331     }
332 
333     if (symbol->symbology == BARCODE_MAXICODE) {
334         /* Maxicode is a fixed size */
335         scaler = GL_CONST; /* Converts from millimeters to the scale used by glabels */
336         render->width = 28.16 * scaler;
337         render->height = 26.86 * scaler;
338 
339         /* Central bullseye pattern */
340         ring = render_plot_create_ring(13.64 * scaler, 13.43 * scaler, 0.85 * scaler, 0.67 * scaler);
341         render_plot_add_ring(symbol, ring, &last_ring);
342         ring = render_plot_create_ring(13.64 * scaler, 13.43 * scaler, 2.20 * scaler, 0.67 * scaler);
343         render_plot_add_ring(symbol, ring, &last_ring);
344         ring = render_plot_create_ring(13.64 * scaler, 13.43 * scaler, 3.54 * scaler, 0.67 * scaler);
345         render_plot_add_ring(symbol, ring, &last_ring);
346 
347         /* Hexagons */
348         for (r = 0; r < symbol->rows; r++) {
349             for (i = 0; i < symbol->width; i++) {
350                 if (module_is_set(symbol, r, i)) {
351                     hexagon = render_plot_create_hexagon(((i * 0.88) + (r & 1 ? 1.76 : 1.32)) * scaler, ((r * 0.76) + 0.76) * scaler);
352                     render_plot_add_hexagon(symbol, hexagon, &last_hexagon);
353                 }
354             }
355         }
356 
357     } else {
358         /* everything else uses rectangles (or squares) */
359         /* Works from the bottom of the symbol up */
360         int addon_latch = 0;
361 
362         for (r = 0; r < symbol->rows; r++) {
363             this_row = r;
364             if (symbol->row_height[this_row] == 0) {
365                 row_height = large_bar_height;
366             } else {
367                 row_height = symbol->row_height[this_row];
368             }
369             row_posn = 0;
370             for (i = 0; i < r; i++) {
371                 if (symbol->row_height[i] == 0) {
372                     row_posn += large_bar_height;
373                 } else {
374                     row_posn += symbol->row_height[i];
375                 }
376             }
377             row_posn += yoffset;
378 
379             i = 0;
380             if (module_is_set(symbol, this_row, 0)) {
381                 latch = 1;
382             } else {
383                 latch = 0;
384             }
385 
386             do {
387                 block_width = 0;
388                 do {
389                     block_width++;
390                 } while (module_is_set(symbol, this_row, i + block_width) == module_is_set(symbol, this_row, i));
391                 if ((addon_latch == 0) && (r == (symbol->rows - 1)) && (i > main_symbol_width_x)) {
392                     addon_text_posn = row_posn * scaler;
393                     addon_latch = 1;
394                 }
395                 if (latch == 1) {
396                     /* a bar */
397                     if (addon_latch == 0) {
398                         line = render_plot_create_line((i + xoffset) * scaler, (row_posn) * scaler, block_width * scaler, row_height * scaler);
399                     } else {
400                         line = render_plot_create_line((i + xoffset) * scaler, (row_posn + 10.0) * scaler, block_width * scaler, (row_height - 5.0) * scaler);
401                     }
402                     latch = 0;
403 
404                     render_plot_add_line(symbol, line, &last_line);
405                 } else {
406                     /* a space */
407                     latch = 1;
408                 }
409                 i += block_width;
410 
411             } while (i < symbol->width);
412         }
413     }
414     /* That's done the actual data area, everything else is human-friendly */
415 
416 
417     /* Add the text */
418     xoffset -= symbol_lead_in;
419     row_posn = (row_posn + large_bar_height) * scaler;
420 
421     if (!hide_text) {
422         if (upceanflag == 8) {
423             /* guard bar extensions and text formatting for EAN-8 */
424             i = 0;
425             for (line = symbol->rendered->lines; line != NULL; line = line->next) {
426                 switch (i) {
427                     case 0:
428                     case 1:
429                     case 10:
430                     case 11:
431                     case 20:
432                     case 21:
433                         line->length += (5.0 * scaler);
434                         break;
435                 }
436                 i++;
437             }
438 
439             for (i = 0; i < 4; i++) {
440                 textpart[i] = symbol->text[i];
441             }
442             textpart[4] = '\0';
443             textpos = 17;
444             textwidth = 4.0 * 8.5;
445             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
446             for (i = 0; i < 4; i++) {
447                 textpart[i] = symbol->text[i + 4];
448             }
449             textpart[4] = '\0';
450             textpos = 50;
451             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
452             textdone = 1;
453             switch (strlen(addon)) {
454                 case 2:
455                     textpos = xoffset + 86;
456                     textwidth = 2.0 * 8.5;
457                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
458                     break;
459                 case 5:
460                     textpos = xoffset + 100;
461                     textwidth = 5.0 * 8.5;
462                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
463                     break;
464             }
465 
466         }
467 
468         if (upceanflag == 13) {
469             /* guard bar extensions and text formatting for EAN-13 */
470             i = 0;
471             for (line = symbol->rendered->lines; line != NULL; line = line->next) {
472                 switch (i) {
473                     case 0:
474                     case 1:
475                     case 14:
476                     case 15:
477                     case 28:
478                     case 29:
479                         line->length += (5.0 * scaler);
480                         break;
481                 }
482                 i++;
483             }
484 
485             textpart[0] = symbol->text[0];
486             textpart[1] = '\0';
487             textpos = -5; // 7
488             textwidth = 8.5;
489             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
490 
491             for (i = 0; i < 6; i++) {
492                 textpart[i] = symbol->text[i + 1];
493             }
494             textpart[6] = '\0';
495             textpos = 25;
496             textwidth = 6.0 * 8.5;
497             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
498             for (i = 0; i < 6; i++) {
499                 textpart[i] = symbol->text[i + 7];
500             }
501             textpart[6] = '\0';
502             textpos = 72;
503             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
504             textdone = 1;
505             switch (strlen(addon)) {
506                 case 2:
507                     textpos = xoffset + 114;
508                     textwidth = 2.0 * 8.5;
509                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
510                     break;
511                 case 5:
512                     textpos = xoffset + 128;
513                     textwidth = 5.0 * 8.5;
514                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
515                     break;
516             }
517         }
518 
519         if (upceanflag == 12) {
520             /* guard bar extensions and text formatting for UPCA */
521             i = 0;
522             for (line = symbol->rendered->lines; line != NULL; line = line->next) {
523                 switch (i) {
524                     case 0:
525                     case 1:
526                     case 2:
527                     case 3:
528                     case 14:
529                     case 15:
530                     case 26:
531                     case 27:
532                     case 28:
533                     case 29:
534                         line->length += (5.0 * scaler);
535                         break;
536                 }
537                 i++;
538             }
539 
540             textpart[0] = symbol->text[0];
541             textpart[1] = '\0';
542             textpos = -5;
543             textwidth = 6.2;
544             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn + (2.0 * scaler), 8.0 * scaler, textwidth * scaler, &last_string);
545             for (i = 0; i < 5; i++) {
546                 textpart[i] = symbol->text[i + 1];
547             }
548             textpart[5] = '\0';
549             textpos = 27;
550             textwidth = 5.0 * 8.5;
551             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
552             for (i = 0; i < 5; i++) {
553                 textpart[i] = symbol->text[i + 6];
554             }
555             textpos = 68;
556             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
557             textpart[0] = symbol->text[11];
558             textpart[1] = '\0';
559             textpos = 100;
560             textwidth = 6.2;
561             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn + (2.0 * scaler), 8.0 * scaler, textwidth * scaler, &last_string);
562             textdone = 1;
563             switch (strlen(addon)) {
564                 case 2:
565                     textpos = xoffset + 116;
566                     textwidth = 2.0 * 8.5;
567                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
568                     break;
569                 case 5:
570                     textpos = xoffset + 130;
571                     textwidth = 5.0 * 8.5;
572                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
573                     break;
574             }
575         }
576 
577         if (upceanflag == 6) {
578             /* guard bar extensions and text formatting for UPCE */
579             i = 0;
580             for (line = symbol->rendered->lines; line != NULL; line = line->next) {
581                 switch (i) {
582                     case 0:
583                     case 1:
584                     case 14:
585                     case 15:
586                     case 16:
587                         line->length += (5.0 * scaler);
588                         break;
589                 }
590                 i++;
591             }
592 
593             textpart[0] = symbol->text[0];
594             textpart[1] = '\0';
595             textpos = -5;
596             textwidth = 6.2;
597             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn + (2.0 * scaler), 8.0 * scaler, textwidth * scaler, &last_string);
598             for (i = 0; i < 6; i++) {
599                 textpart[i] = symbol->text[i + 1];
600             }
601             textpart[6] = '\0';
602             textpos = 24;
603             textwidth = 6.0 * 8.5;
604             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn, 11.0 * scaler, textwidth * scaler, &last_string);
605             textpart[0] = symbol->text[7];
606             textpart[1] = '\0';
607             textpos = 55;
608             textwidth = 6.2;
609             render_plot_add_string(symbol, (unsigned char *) textpart, (textpos + xoffset) * scaler, default_text_posn + (2.0 * scaler), 8.0 * scaler, textwidth * scaler, &last_string);
610             textdone = 1;
611             switch (strlen(addon)) {
612                 case 2:
613                     textpos = xoffset + 70;
614                     textwidth = 2.0 * 8.5;
615                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
616                     break;
617                 case 5:
618                     textpos = xoffset + 84;
619                     textwidth = 5.0 * 8.5;
620                     render_plot_add_string(symbol, (unsigned char *) addon, textpos * scaler, addon_text_posn * scaler, 11.0 * scaler, textwidth * scaler, &last_string);
621                     break;
622             }
623         }
624 
625         /* Put normal human readable text at the bottom (and centered) */
626         if (textdone == 0) {
627             // caculate start xoffset to center text
628             render_plot_add_string(symbol, symbol->text, ((symbol->width / 2.0) + xoffset) * scaler, default_text_posn, 9.0 * scaler, 0.0, &last_string);
629         }
630     }
631 
632     switch (symbol->symbology) {
633         case BARCODE_MAXICODE:
634             /* Do nothing! */
635             break;
636         default:
637             if ((symbol->output_options & BARCODE_BIND) != 0) {
638                 if ((symbol->rows > 1) && (is_stackable(symbol->symbology) == 1)) {
639                     /* row binding */
640                     for (r = 1; r < symbol->rows; r++) {
641                         line = render_plot_create_line(xoffset * scaler, ((r * row_height) + yoffset - 1) * scaler, symbol->width * scaler, 2.0 * scaler);
642                         render_plot_add_line(symbol, line, &last_line);
643                     }
644                 }
645             }
646             if ((symbol->output_options & BARCODE_BOX) || (symbol->output_options & BARCODE_BIND)) {
647                 line = render_plot_create_line(0, 0, (symbol->width + xoffset + xoffset) * scaler, symbol->border_width * scaler);
648                 render_plot_add_line(symbol, line, &last_line);
649                 line = render_plot_create_line(0, (symbol->height + symbol->border_width) * scaler, (symbol->width + xoffset + xoffset) * scaler, symbol->border_width * scaler);
650                 render_plot_add_line(symbol, line, &last_line);
651             }
652             if (symbol->output_options & BARCODE_BOX) {
653                 /* side bars */
654                 line = render_plot_create_line(0, 0, symbol->border_width * scaler, (symbol->height + (2 * symbol->border_width)) * scaler);
655                 render_plot_add_line(symbol, line, &last_line);
656                 line = render_plot_create_line((symbol->width + xoffset + xoffset - symbol->border_width) * scaler, 0, symbol->border_width * scaler, (symbol->height + (2 * symbol->border_width)) * scaler);
657                 render_plot_add_line(symbol, line, &last_line);
658             }
659             break;
660     }
661 
662     if (locale)
663         setlocale(LC_ALL, locale);
664 
665     return 1;
666 }
667 
668 /*
669  * Create a new line with its memory allocated ready for adding to the
670  * rendered structure.
671  *
672  * This is much quicker than writing out each line manually (in some cases!)
673  */
render_plot_create_line(float x,float y,float width,float length)674 struct zint_render_line *render_plot_create_line(float x, float y, float width, float length) {
675     struct zint_render_line *line;
676 
677     line = (struct zint_render_line*) malloc(sizeof (struct zint_render_line));
678     if (!line) return NULL;
679 
680     line->next = NULL;
681     line->x = x;
682     line->y = y;
683     line->width = width;
684     line->length = length;
685 
686     return line;
687 }
688 
689 /*
690  * Add the line to the current rendering and update the last line's
691  * next value.
692  */
render_plot_add_line(struct zint_symbol * symbol,struct zint_render_line * line,struct zint_render_line ** last_line)693 int render_plot_add_line(struct zint_symbol *symbol, struct zint_render_line *line, struct zint_render_line **last_line) {
694     if (!line) return ZINT_ERROR_MEMORY;
695     if (*last_line)
696         (*last_line)->next = line;
697     else
698         symbol->rendered->lines = line; // first line
699 
700     *last_line = line;
701     return 1;
702 }
703 
render_plot_create_ring(float x,float y,float radius,float line_width)704 struct zint_render_ring *render_plot_create_ring(float x, float y, float radius, float line_width) {
705     struct zint_render_ring *ring;
706 
707     ring = (struct zint_render_ring *) malloc(sizeof (struct zint_render_ring));
708     if (!ring) return NULL;
709     ring->next = NULL;
710     ring->x = x;
711     ring->y = y;
712     ring->radius = radius;
713     ring->line_width = line_width;
714 
715     return ring;
716 }
717 
render_plot_add_ring(struct zint_symbol * symbol,struct zint_render_ring * ring,struct zint_render_ring ** last_ring)718 int render_plot_add_ring(struct zint_symbol *symbol, struct zint_render_ring *ring, struct zint_render_ring **last_ring) {
719     if (!ring) return ZINT_ERROR_MEMORY;
720     if (*last_ring)
721         (*last_ring)->next = ring;
722     else
723         symbol->rendered->rings = ring; // first ring
724 
725     *last_ring = ring;
726     return 1;
727 }
728 
render_plot_create_hexagon(float x,float y)729 struct zint_render_hexagon *render_plot_create_hexagon(float x, float y) {
730     struct zint_render_hexagon *hexagon;
731 
732     hexagon = (struct zint_render_hexagon*) malloc(sizeof (struct zint_render_hexagon));
733     if (!hexagon) return NULL;
734     hexagon->next = NULL;
735     hexagon->x = x;
736     hexagon->y = y;
737 
738     return hexagon;
739 }
740 
render_plot_add_hexagon(struct zint_symbol * symbol,struct zint_render_hexagon * hexagon,struct zint_render_hexagon ** last_hexagon)741 int render_plot_add_hexagon(struct zint_symbol *symbol, struct zint_render_hexagon *hexagon, struct zint_render_hexagon **last_hexagon) {
742     if (!hexagon) return ZINT_ERROR_MEMORY;
743     if (*last_hexagon)
744         (*last_hexagon)->next = hexagon;
745     else
746         symbol->rendered->hexagons = hexagon; // first hexagon
747 
748     *last_hexagon = hexagon;
749     return 1;
750 }
751 
752 /*
753  * Add a string structure to the symbol.
754  * Coordinates assumed to be from top-center.
755  */
render_plot_add_string(struct zint_symbol * symbol,unsigned char * text,float x,float y,float fsize,float width,struct zint_render_string ** last_string)756 int render_plot_add_string(struct zint_symbol *symbol,
757         unsigned char *text, float x, float y, float fsize, float width,
758         struct zint_render_string **last_string) {
759     struct zint_render_string *string;
760 
761     string = (struct zint_render_string*) malloc(sizeof (struct zint_render_string));
762     string->next = NULL;
763     string->x = x;
764     string->y = y;
765     string->width = width;
766     string->fsize = fsize;
767     string->length = ustrlen(text);
768     string->text = (unsigned char*) malloc(sizeof (unsigned char) * (ustrlen(text) + 1));
769     ustrcpy(string->text, text);
770 
771     if (*last_string)
772         (*last_string)->next = string;
773     else
774         symbol->rendered->strings = string; // First character
775     *last_string = string;
776 
777     return 1;
778 }
779