comparison mupdf-source/thirdparty/zint/backend/vector.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /* vector.c - Creates vector image objects */
2 /*
3 libzint - the open source barcode library
4 Copyright (C) 2018-2024 Robin Stuart <rstuart114@gmail.com>
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. Neither the name of the project nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 SUCH DAMAGE.
30 */
31 /* SPDX-License-Identifier: BSD-3-Clause */
32
33 #include "common.h"
34 #include "output.h"
35 #include "zfiletypes.h"
36
37 INTERNAL int ps_plot(struct zint_symbol *symbol);
38 INTERNAL int svg_plot(struct zint_symbol *symbol);
39 INTERNAL int emf_plot(struct zint_symbol *symbol, int rotate_angle);
40
41 static int vector_add_rect(struct zint_symbol *symbol, const float x, const float y, const float width,
42 const float height, struct zint_vector_rect **last_rect) {
43 struct zint_vector_rect *rect;
44
45 if (!(rect = (struct zint_vector_rect *) malloc(sizeof(struct zint_vector_rect)))) {
46 /* NOTE: clang-tidy-20 gets confused about return value of function returning a function unfortunately,
47 so put on 2 lines (see also "postal.c" `postnet_enc()` & `planet_enc()`, same issue) */
48 errtxt(0, symbol, 691, "Insufficient memory for vector rectangle");
49 return 0;
50 }
51
52 rect->next = NULL;
53 rect->x = x;
54 rect->y = y;
55 rect->width = width;
56 rect->height = height;
57 rect->colour = -1; /* Default colour */
58
59 if (*last_rect)
60 (*last_rect)->next = rect;
61 else
62 symbol->vector->rectangles = rect; /* first rectangle */
63
64 *last_rect = rect;
65
66 return 1;
67 }
68
69 static int vector_add_hexagon(struct zint_symbol *symbol, const float x, const float y,
70 const float diameter, struct zint_vector_hexagon **last_hexagon) {
71 struct zint_vector_hexagon *hexagon;
72
73 if (!(hexagon = (struct zint_vector_hexagon *) malloc(sizeof(struct zint_vector_hexagon)))) {
74 return errtxt(0, symbol, 692, "Insufficient memory for vector hexagon");
75 }
76 hexagon->next = NULL;
77 hexagon->x = x;
78 hexagon->y = y;
79 hexagon->diameter = diameter;
80 hexagon->rotation = 0;
81
82 if (*last_hexagon)
83 (*last_hexagon)->next = hexagon;
84 else
85 symbol->vector->hexagons = hexagon; /* first hexagon */
86
87 *last_hexagon = hexagon;
88
89 return 1;
90 }
91
92 static int vector_add_circle(struct zint_symbol *symbol, const float x, const float y, const float diameter,
93 const float width, const int colour, struct zint_vector_circle **last_circle) {
94 struct zint_vector_circle *circle;
95
96 if (!(circle = (struct zint_vector_circle *) malloc(sizeof(struct zint_vector_circle)))) {
97 return errtxt(0, symbol, 693, "Insufficient memory for vector circle");
98 }
99 circle->next = NULL;
100 circle->x = x;
101 circle->y = y;
102 circle->diameter = diameter;
103 circle->width = width;
104 circle->colour = colour;
105
106 if (*last_circle)
107 (*last_circle)->next = circle;
108 else
109 symbol->vector->circles = circle; /* first circle */
110
111 *last_circle = circle;
112
113 return 1;
114 }
115
116 static int vector_add_string(struct zint_symbol *symbol, const unsigned char *text, const int length,
117 const float x, const float y, const float fsize, const float width, const int halign,
118 struct zint_vector_string **last_string) {
119 struct zint_vector_string *string;
120
121 if (!(string = (struct zint_vector_string *) malloc(sizeof(struct zint_vector_string)))) {
122 return errtxt(0, symbol, 694, "Insufficient memory for vector string");
123 }
124 string->next = NULL;
125 string->x = x;
126 string->y = y;
127 string->width = width;
128 string->fsize = fsize;
129 string->length = length == -1 ? (int) ustrlen(text) : length;
130 string->rotation = 0;
131 string->halign = halign;
132 if (!(string->text = (unsigned char *) malloc(string->length + 1))) {
133 free(string);
134 return errtxt(0, symbol, 695, "Insufficient memory for vector string text");
135 }
136 memcpy(string->text, text, string->length);
137 string->text[string->length] = '\0';
138
139 if (*last_string)
140 (*last_string)->next = string;
141 else
142 symbol->vector->strings = string; /* First text portion */
143
144 *last_string = string;
145
146 return 1;
147 }
148
149 INTERNAL void vector_free(struct zint_symbol *symbol) {
150 if (symbol->vector != NULL) {
151 struct zint_vector_rect *rect;
152 struct zint_vector_hexagon *hex;
153 struct zint_vector_circle *circle;
154 struct zint_vector_string *string;
155
156 /* Free Rectangles */
157 rect = symbol->vector->rectangles;
158 while (rect) {
159 struct zint_vector_rect *r = rect;
160 rect = rect->next;
161 free(r);
162 }
163
164 /* Free Hexagons */
165 hex = symbol->vector->hexagons;
166 while (hex) {
167 struct zint_vector_hexagon *h = hex;
168 hex = hex->next;
169 free(h);
170 }
171
172 /* Free Circles */
173 circle = symbol->vector->circles;
174 while (circle) {
175 struct zint_vector_circle *c = circle;
176 circle = circle->next;
177 free(c);
178 }
179
180 /* Free Strings */
181 string = symbol->vector->strings;
182 while (string) {
183 struct zint_vector_string *s = string;
184 string = string->next;
185 free(s->text);
186 free(s);
187 }
188
189 /* Free vector */
190 free(symbol->vector);
191 symbol->vector = NULL;
192 }
193 }
194
195 static void vector_scale(struct zint_symbol *symbol, const int file_type) {
196 struct zint_vector_rect *rect;
197 struct zint_vector_hexagon *hex;
198 struct zint_vector_circle *circle;
199 struct zint_vector_string *string;
200 float scale = symbol->scale * 2.0f;
201
202 if (scale < 0.2f) { /* Minimum vector scale 0.1 */
203 scale = 0.2f;
204 }
205
206 if ((file_type == OUT_EMF_FILE) && (symbol->symbology == BARCODE_MAXICODE)) {
207 /* Increase size to overcome limitations in EMF file format */
208 scale *= 20;
209 }
210
211 symbol->vector->width = stripf(symbol->vector->width * scale);
212 symbol->vector->height = stripf(symbol->vector->height * scale);
213
214 rect = symbol->vector->rectangles;
215 while (rect) {
216 rect->x = stripf(rect->x * scale);
217 rect->y = stripf(rect->y * scale);
218 rect->height = stripf(rect->height * scale);
219 rect->width = stripf(rect->width * scale);
220 rect = rect->next;
221 }
222
223 hex = symbol->vector->hexagons;
224 while (hex) {
225 hex->x = stripf(hex->x * scale);
226 hex->y = stripf(hex->y * scale);
227 hex->diameter = stripf(hex->diameter * scale);
228 hex = hex->next;
229 }
230
231 circle = symbol->vector->circles;
232 while (circle) {
233 circle->x = stripf(circle->x * scale);
234 circle->y = stripf(circle->y * scale);
235 circle->diameter = stripf(circle->diameter * scale);
236 circle->width = stripf(circle->width * scale);
237 circle = circle->next;
238 }
239
240 string = symbol->vector->strings;
241 while (string) {
242 string->x = stripf(string->x * scale);
243 string->y = stripf(string->y * scale);
244 string->width = stripf(string->width * scale);
245 string->fsize = stripf(string->fsize * scale);
246 string = string->next;
247 }
248 }
249
250 static void vector_rotate(struct zint_symbol *symbol, const int rotate_angle) {
251 /* Rotates the image */
252 struct zint_vector_rect *rect;
253 struct zint_vector_hexagon *hex;
254 struct zint_vector_circle *circle;
255 struct zint_vector_string *string;
256 float temp;
257
258 if (rotate_angle == 0) {
259 /* No rotation needed */
260 return;
261 }
262
263 rect = symbol->vector->rectangles;
264 while (rect) {
265 if (rotate_angle == 90) {
266 temp = rect->x;
267 rect->x = stripf(symbol->vector->height - (rect->y + rect->height));
268 rect->y = temp;
269 temp = rect->width;
270 rect->width = rect->height;
271 rect->height = temp;
272 } else if (rotate_angle == 180) {
273 rect->x = stripf(symbol->vector->width - (rect->x + rect->width));
274 rect->y = stripf(symbol->vector->height - (rect->y + rect->height));
275 } else if (rotate_angle == 270) {
276 temp = rect->x;
277 rect->x = rect->y;
278 rect->y = stripf(symbol->vector->width - (temp + rect->width));
279 temp = rect->width;
280 rect->width = rect->height;
281 rect->height = temp;
282 }
283 rect = rect->next;
284 }
285
286 hex = symbol->vector->hexagons;
287 while (hex) {
288 if (rotate_angle == 90) {
289 temp = hex->x;
290 hex->x = stripf(symbol->vector->height - hex->y);
291 hex->y = temp;
292 hex->rotation = 90;
293 } else if (rotate_angle == 180) {
294 hex->x = stripf(symbol->vector->width - hex->x);
295 hex->y = stripf(symbol->vector->height - hex->y);
296 hex->rotation = 180;
297 } else if (rotate_angle == 270) {
298 temp = hex->x;
299 hex->x = hex->y;
300 hex->y = stripf(symbol->vector->width - temp);
301 hex->rotation = 270;
302 }
303 hex = hex->next;
304 }
305
306 circle = symbol->vector->circles;
307 while (circle) {
308 if (rotate_angle == 90) {
309 temp = circle->x;
310 circle->x = stripf(symbol->vector->height - circle->y);
311 circle->y = temp;
312 } else if (rotate_angle == 180) {
313 circle->x = stripf(symbol->vector->width - circle->x);
314 circle->y = stripf(symbol->vector->height - circle->y);
315 } else if (rotate_angle == 270) {
316 temp = circle->x;
317 circle->x = circle->y;
318 circle->y = stripf(symbol->vector->width - temp);
319 }
320 circle = circle->next;
321 }
322
323 string = symbol->vector->strings;
324 while (string) {
325 if (rotate_angle == 90) {
326 temp = string->x;
327 string->x = stripf(symbol->vector->height - string->y);
328 string->y = temp;
329 string->rotation = 90;
330 } else if (rotate_angle == 180) {
331 string->x = stripf(symbol->vector->width - string->x);
332 string->y = stripf(symbol->vector->height - string->y);
333 string->rotation = 180;
334 } else if (rotate_angle == 270) {
335 temp = string->x;
336 string->x = string->y;
337 string->y = stripf(symbol->vector->width - temp);
338 string->rotation = 270;
339 }
340 string = string->next;
341 }
342
343 if ((rotate_angle == 90) || (rotate_angle == 270)) {
344 temp = symbol->vector->height;
345 symbol->vector->height = symbol->vector->width;
346 symbol->vector->width = temp;
347 }
348 }
349
350 static void vector_reduce_rectangles(struct zint_symbol *symbol) {
351 /* Looks for vertically aligned rectangles and merges them together */
352 struct zint_vector_rect *rect, *target, *prev;
353
354 rect = symbol->vector->rectangles;
355 while (rect) {
356 prev = rect;
357 target = prev->next;
358
359 while (target) {
360 if ((rect->x == target->x) && (rect->width == target->width)
361 && (stripf(rect->y + rect->height) == target->y) && (rect->colour == target->colour)) {
362 rect->height += target->height;
363 prev->next = target->next;
364 free(target);
365 } else {
366 prev = target;
367 }
368 target = prev->next;
369 }
370
371 rect = rect->next;
372 }
373 }
374
375 INTERNAL int plot_vector(struct zint_symbol *symbol, int rotate_angle, int file_type) {
376 int error_number;
377 int main_width;
378 int comp_xoffset = 0;
379 int comp_roffset = 0;
380 unsigned char addon[6];
381 int addon_len = 0;
382 int addon_gap = 0;
383 float addon_text_yposn = 0.0f;
384 float xoffset, yoffset, roffset, boffset, qz_right;
385 float textoffset;
386 int upceanflag = 0;
387 int addon_latch = 0;
388 int hide_text;
389 int i, r;
390 int block_width = 0;
391 int font_height; /* Font pixel size (so whole integers) */
392 float text_gap; /* Gap between barcode and text */
393 float guard_descent;
394 const int upcean_guard_whitespace = !(symbol->output_options & BARCODE_NO_QUIET_ZONES)
395 && (symbol->output_options & EANUPC_GUARD_WHITESPACE);
396 const int is_codablockf = symbol->symbology == BARCODE_CODABLOCKF || symbol->symbology == BARCODE_HIBC_BLOCKF;
397 const int no_extend = is_codablockf || symbol->symbology == BARCODE_DPD;
398
399 float large_bar_height;
400 int xoffset_comp;
401 const float descent = 1.32779717f; /* Arimo value for normal text (font height 7) */
402 const float descent_small = 0.948426545f; /* Arimo value for SMALL_TEXT (font height 5) */
403
404 /* For UPC/EAN only */
405 float addon_row_yposn = 0.0f; /* Suppress gcc -Wmaybe-uninitialized false positive */
406 float addon_row_height = 0.0f; /* Ditto */
407 int upcae_outside_font_height = 0; /* UPC-A/E outside digits font size */
408 const float gws_left_fudge = 0.5f; /* These make the guard whitespaces appear closer to the edge for SVG/qzint */
409 const float gws_right_fudge = 0.5f; /* (undone by EMF/EPS) */
410 /* Note using "ascender" to mean height above digits as "ascent" usually measured from baseline */
411 const float digit_ascender_factor = 0.22f; /* Assuming digit ascender height roughly 22% of font size */
412 float digit_ascender = 0.0f; /* Avoid gcc -Wmaybe-uninitialized */
413 const float antialias_fudge_factor = 0.02f;
414 float antialias_fudge = 0.0f; /* Avoid gcc -Wmaybe-uninitialized */
415 int rect_count = 0, last_row_start = 0; /* For UPC/EAN guard bars */
416
417 float dot_overspill = 0.0f;
418 float dot_offset = 0.0f;
419 float yposn;
420
421 struct zint_vector *vector;
422 struct zint_vector_rect *rect, *last_rect = NULL;
423 struct zint_vector_hexagon *last_hexagon = NULL;
424 struct zint_vector_string *last_string = NULL;
425 struct zint_vector_circle *last_circle = NULL;
426 struct zint_vector_rect **first_row_rects
427 = (struct zint_vector_rect **) z_alloca(sizeof(struct zint_vector_rect *) * (symbol->rows + 1));
428
429 memset(first_row_rects, 0, sizeof(struct zint_vector_rect *) * (symbol->rows + 1));
430
431 /* Free any previous rendering structures */
432 vector_free(symbol);
433
434 /* Sanity check colours */
435 error_number = out_check_colour_options(symbol);
436 if (error_number != 0) {
437 return error_number;
438 }
439 if (symbol->rows <= 0) {
440 return errtxt(ZINT_ERROR_INVALID_OPTION, symbol, 697, "No rows");
441 }
442
443 /* Allocate memory */
444 if (!(vector = symbol->vector = (struct zint_vector *) malloc(sizeof(struct zint_vector)))) {
445 return errtxt(ZINT_ERROR_MEMORY, symbol, 696, "Insufficient memory for vector header");
446 }
447 vector->rectangles = NULL;
448 vector->hexagons = NULL;
449 vector->circles = NULL;
450 vector->strings = NULL;
451
452 large_bar_height = out_large_bar_height(symbol, 0 /*si (scale and round)*/, NULL /*row_heights_si*/,
453 NULL /*symbol_height_si*/);
454
455 main_width = symbol->width;
456
457 if (is_composite(symbol->symbology)) {
458 while (!module_is_set(symbol, symbol->rows - 1, comp_xoffset)) {
459 comp_xoffset++;
460 }
461 }
462 if (is_upcean(symbol->symbology)) {
463 upceanflag = out_process_upcean(symbol, comp_xoffset, &main_width, addon, &addon_len, &addon_gap);
464 } else if (is_composite(symbol->symbology)) {
465 int x;
466 for (x = symbol->width - 1; x && !module_is_set(symbol, symbol->rows - 1, x); comp_roffset++, x--);
467 main_width -= comp_xoffset + comp_roffset;
468 }
469
470 hide_text = ((!symbol->show_hrt) || (ustrlen(symbol->text) == 0));
471
472 out_set_whitespace_offsets(symbol, hide_text, comp_xoffset, &xoffset, &yoffset, &roffset, &boffset, &qz_right,
473 0 /*scaler*/, NULL, NULL, NULL, NULL, NULL);
474
475 xoffset_comp = xoffset + comp_xoffset;
476
477 if ((symbol->symbology != BARCODE_MAXICODE) && (symbol->output_options & BARCODE_DOTTY_MODE)) {
478 if (symbol->dot_size < 1.0f) {
479 dot_overspill = 0.0f;
480 /* Offset (1 - dot_size) / 2 + dot_radius == (1 - dot_size + dot_size) / 2 == 1 / 2 */
481 dot_offset = 0.5f;
482 } else { /* Allow for exceeding 1X */
483 dot_overspill = symbol->dot_size - 1.0f + 0.1f; /* Fudge for anti-aliasing */
484 dot_offset = symbol->dot_size / 2.0f + 0.05f; /* Fudge for anti-aliasing */
485 }
486 }
487
488 vector->width = symbol->width + dot_overspill + (xoffset + roffset);
489
490 /* Note font sizes scaled by 2 so really twice these values */
491 if (upceanflag) {
492 /* Note BOLD_TEXT ignored for UPCEAN by svg/emf/ps/qzint */
493 font_height = symbol->output_options & SMALL_TEXT ? 7 : 10;
494 digit_ascender = font_height * digit_ascender_factor;
495 antialias_fudge = font_height * antialias_fudge_factor;
496 /* Although font size 7 (for normal) seems small it meets GS1 General Spec (GGS) Section 5.2.5:
497 "the size of the first and last digits should be reduced to a maximum width equivalent to four modules" */
498 upcae_outside_font_height = symbol->output_options & SMALL_TEXT ? 6 : 7;
499 /* Note default now 1.0 (GGS 5.2.5 "Normally the minimum is one module") but was 0.5 (absolute minimum) */
500 text_gap = symbol->text_gap - digit_ascender;
501 /* Guard bar height (none for EAN-2 and EAN-5) */
502 guard_descent = upceanflag >= 6 ? symbol->guard_descent : 0.0f;
503 } else {
504 font_height = symbol->output_options & SMALL_TEXT ? 5 : 7;
505 antialias_fudge = font_height * antialias_fudge_factor;
506 text_gap = symbol->text_gap;
507 guard_descent = 0.0f;
508 }
509
510 if (hide_text) {
511 textoffset = guard_descent;
512 } else {
513 textoffset = font_height + stripf(text_gap + antialias_fudge);
514 if (upceanflag) {
515 if (textoffset < guard_descent) {
516 textoffset = guard_descent;
517 }
518 }
519 }
520
521 vector->height = symbol->height + textoffset + dot_overspill + (yoffset + boffset);
522
523 /* Plot Maxicode symbols */
524 if (symbol->symbology == BARCODE_MAXICODE) {
525 float bull_x, bull_y, bull_d_incr, bull_width;
526 const float two_div_sqrt3 = 1.1547f; /* 2 / √3 */
527 const float sqrt3_div_two = 0.866f; /* √3 / 2 == 1.5 / √3 */
528
529 /* `hex_diameter` is short diameter, X in ISO/IEC 16023:2000 Figure 8 (same as W) */
530 const float hex_diameter = 1.0f;
531 const float hex_radius = hex_diameter / 2.0f;
532 const float hex_ydiameter = two_div_sqrt3 * hex_diameter; /* Long diameter, V in Figure 8 */
533 const float hex_yradius = hex_ydiameter / 2.0f;
534 const float yposn_offset = sqrt3_div_two * hex_diameter; /* Vertical distance between rows, Y in Figure 8 */
535
536 vector->width = 30 * hex_diameter + (xoffset + roffset);
537 /* 32 rows drawn yposn_offset apart + final hexagon */
538 vector->height = 32 * yposn_offset + hex_ydiameter + (yoffset + boffset);
539
540 /* Bullseye (ISO/IEC 16023:2000 4.2.1.1 and 4.11.4) */
541 bull_x = 14.5f * hex_diameter + xoffset; /* 14W right from leftmost centre = 14.5X */
542 bull_y = vector->height / 2.0f; /* 16Y above bottom-most centre = halfway */
543 /* Total finder diameter is 9X, so diametric increment for 5 diameters d2 to d6 is (9X - d1) / 5 */
544 bull_d_incr = (hex_diameter * 9 - hex_ydiameter) / 5.0f;
545 bull_width = bull_d_incr / 2.0f;
546
547 if (!vector_add_circle(symbol, bull_x, bull_y, hex_ydiameter + bull_d_incr * 5 - bull_width, bull_width, 0,
548 &last_circle)) return ZINT_ERROR_MEMORY;
549 if (!vector_add_circle(symbol, bull_x, bull_y, hex_ydiameter + bull_d_incr * 3 - bull_width, bull_width, 0,
550 &last_circle)) return ZINT_ERROR_MEMORY;
551 if (!vector_add_circle(symbol, bull_x, bull_y, hex_ydiameter + bull_d_incr - bull_width, bull_width, 0,
552 &last_circle)) return ZINT_ERROR_MEMORY;
553
554 /* Hexagons */
555 for (r = 0; r < symbol->rows; r++) {
556 const int odd_row = r & 1; /* Odd (reduced) row, even (full) row */
557 const float hex_yposn = r * yposn_offset + hex_yradius + yoffset;
558 const float xposn_offset = (odd_row ? hex_diameter : hex_radius) + xoffset;
559 for (i = 0; i < symbol->width - odd_row; i++) {
560 if (module_is_set(symbol, r, i)) {
561 const float hex_xposn = i * hex_diameter + xposn_offset;
562 if (!vector_add_hexagon(symbol, hex_xposn, hex_yposn, hex_diameter, &last_hexagon))
563 return ZINT_ERROR_MEMORY;
564 }
565 }
566 }
567 /* Dotty mode */
568 } else if (symbol->output_options & BARCODE_DOTTY_MODE) {
569 for (r = 0; r < symbol->rows; r++) {
570 for (i = 0; i < symbol->width; i++) {
571 if (module_is_set(symbol, r, i)) {
572 if (!vector_add_circle(symbol, i + dot_offset + xoffset, r + dot_offset + yoffset,
573 symbol->dot_size, 0, 0, &last_circle)) return ZINT_ERROR_MEMORY;
574 }
575 }
576 }
577 /* Plot rectangles - most symbols created here */
578 } else if (symbol->symbology == BARCODE_ULTRA) {
579 yposn = yoffset;
580 for (r = 0; r < symbol->rows; r++) {
581 const float row_height = symbol->row_height[r];
582
583 for (i = 0; i < symbol->width; i += block_width) {
584 const int fill = module_colour_is_set(symbol, r, i);
585 for (block_width = 1; (i + block_width < symbol->width)
586 && module_colour_is_set(symbol, r, i + block_width) == fill; block_width++);
587 if (fill) {
588 /* a colour block */
589 if (!vector_add_rect(symbol, i + xoffset, yposn, block_width, row_height, &last_rect))
590 return ZINT_ERROR_MEMORY;
591 last_rect->colour = module_colour_is_set(symbol, r, i);
592 }
593 }
594 yposn += row_height;
595 }
596
597 } else if (upceanflag >= 6) { /* UPC-E, EAN-8, UPC-A, EAN-13 */
598 yposn = yoffset;
599 for (r = 0; r < symbol->rows; r++) {
600 const float row_height = symbol->row_height[r] ? symbol->row_height[r] : large_bar_height;
601 last_row_start = rect_count;
602
603 for (i = 0; i < symbol->width; i += block_width) {
604 const int fill = module_is_set(symbol, r, i);
605 for (block_width = 1; (i + block_width < symbol->width)
606 && module_is_set(symbol, r, i + block_width) == fill; block_width++);
607
608 if ((r == (symbol->rows - 1)) && (i > main_width) && (addon_latch == 0)) {
609 addon_text_yposn = yposn + font_height - digit_ascender;
610 if (addon_text_yposn < 0.0f) {
611 addon_text_yposn = 0.0f;
612 }
613 addon_row_yposn = yposn + font_height + text_gap + antialias_fudge;
614 addon_row_height = row_height - (addon_row_yposn - yposn);
615 /* Following ISO/IEC 15420:2009 Figure 5 — UPC-A bar code symbol with 2-digit add-on (contrary to
616 GS1 General Specs v24.0 Figure 5.2.6.6-5) descends for all including UPC-A/E */
617 addon_row_height += guard_descent;
618 if (addon_row_height < 0.5f) {
619 addon_row_height = 0.5f;
620 }
621 addon_latch = 1;
622 }
623 if (fill) {
624 /* a bar */
625 if (addon_latch) {
626 if (!vector_add_rect(symbol, i + xoffset, addon_row_yposn, block_width, addon_row_height,
627 &last_rect)) return ZINT_ERROR_MEMORY;
628 } else {
629 if (!vector_add_rect(symbol, i + xoffset, yposn, block_width, row_height, &last_rect))
630 return ZINT_ERROR_MEMORY;
631 }
632 rect_count++;
633 }
634 }
635 yposn += row_height;
636 }
637
638 } else {
639 yposn = yoffset;
640 if (upceanflag && !hide_text) { /* EAN-2, EAN-5 (standalone add-ons) */
641 yposn += font_height + text_gap + antialias_fudge;
642 }
643 for (r = 0; r < symbol->rows; r++) {
644 const float row_height = symbol->row_height[r] ? symbol->row_height[r] : large_bar_height;
645
646 for (i = 0; i < symbol->width; i += block_width) {
647 const int fill = module_is_set(symbol, r, i);
648 for (block_width = 1; (i + block_width < symbol->width)
649 && module_is_set(symbol, r, i + block_width) == fill; block_width++);
650 if (fill) {
651 /* a bar */
652 if (!vector_add_rect(symbol, i + xoffset, yposn, block_width, row_height, &last_rect))
653 return ZINT_ERROR_MEMORY;
654 if (i == 0) {
655 first_row_rects[r] = last_rect;
656 }
657 }
658 }
659 yposn += row_height;
660 }
661 }
662
663 if (guard_descent && upceanflag >= 6) { /* UPC-E, EAN-8, UPC-A, EAN-13 */
664 /* Guard bar extension */
665 if (upceanflag == 6) { /* UPC-E */
666 i = 0;
667 for (rect = symbol->vector->rectangles; rect != NULL; rect = rect->next) {
668 switch (i - last_row_start) {
669 case 0:
670 case 1:
671 case 14:
672 case 15:
673 case 16:
674 rect->height += guard_descent;
675 break;
676 }
677 i++;
678 }
679 } else if (upceanflag == 8) { /* EAN-8 */
680 i = 0;
681 for (rect = symbol->vector->rectangles; rect != NULL; rect = rect->next) {
682 switch (i - last_row_start) {
683 case 0:
684 case 1:
685 case 10:
686 case 11:
687 case 20:
688 case 21:
689 rect->height += guard_descent;
690 break;
691 }
692 i++;
693 }
694 } else if (upceanflag == 12) { /* UPC-A */
695 i = 0;
696 for (rect = symbol->vector->rectangles; rect != NULL; rect = rect->next) {
697 switch (i - last_row_start) {
698 case 0:
699 case 1:
700 case 2:
701 case 3:
702 case 14:
703 case 15:
704 case 26:
705 case 27:
706 case 28:
707 case 29:
708 rect->height += guard_descent;
709 break;
710 }
711 i++;
712 }
713 } else { /* EAN-13 */
714 i = 0;
715 for (rect = symbol->vector->rectangles; rect != NULL; rect = rect->next) {
716 switch (i - last_row_start) {
717 case 0:
718 case 1:
719 case 14:
720 case 15:
721 case 28:
722 case 29:
723 rect->height += guard_descent;
724 break;
725 }
726 i++;
727 }
728 }
729 }
730
731 /* Add the text */
732
733 if (!hide_text) {
734 float textwidth;
735
736 if (upceanflag >= 6) { /* UPC-E, EAN-8, UPC-A, EAN-13 */
737
738 float text_yposn = yoffset + symbol->height + font_height + text_gap - antialias_fudge; /* Baseline */
739 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND))
740 && !(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */
741 text_yposn += symbol->border_width;
742 }
743
744 if (upceanflag == 6) { /* UPC-E */
745 float text_xposn = -(5.0f - 0.35f) + xoffset_comp;
746 textwidth = 6.2f;
747 if (!vector_add_string(symbol, symbol->text, 1, text_xposn, text_yposn, upcae_outside_font_height,
748 textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
749 text_xposn = (24.0f + 0.5f) + xoffset_comp;
750 textwidth = 6.0f * 8.5f;
751 if (!vector_add_string(symbol, symbol->text + 1, 6, text_xposn, text_yposn, font_height, textwidth,
752 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
753 /* TODO: GS1 General Specs v24.0 5.2.5 Human readable interpretation says 3X but this could cause
754 digit's righthand to touch any add-on, now that they descend, so use 2X, until clarified */
755 text_xposn = (51.0f - 0.35f) + 2.0f + xoffset_comp;
756 textwidth = 6.2f;
757 if (!vector_add_string(symbol, symbol->text + 7, 1, text_xposn, text_yposn, upcae_outside_font_height,
758 textwidth, 1 /*left align*/, &last_string)) return ZINT_ERROR_MEMORY;
759 if (addon_len) {
760 text_xposn = (addon_len == 2 ? 61.0f : 75.0f) + xoffset_comp + addon_gap;
761 textwidth = addon_len * 8.5f;
762 if (!vector_add_string(symbol, addon, addon_len, text_xposn, addon_text_yposn, font_height,
763 textwidth, 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
764 if (upcean_guard_whitespace) {
765 text_xposn = symbol->width + gws_right_fudge + qz_right + xoffset;
766 textwidth = 8.5f;
767 if (!vector_add_string(symbol, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn,
768 font_height, textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
769 }
770 }
771
772 } else if (upceanflag == 8) { /* EAN-8 */
773 float text_xposn;
774 if (upcean_guard_whitespace) {
775 text_xposn = -7.0f - gws_left_fudge + xoffset_comp;
776 textwidth = 8.5f;
777 if (!vector_add_string(symbol, (const unsigned char *) "<", 1, text_xposn, text_yposn,
778 font_height, textwidth, 1 /*left align*/, &last_string)) return ZINT_ERROR_MEMORY;
779 }
780 text_xposn = (17.0f + 0.5f) + xoffset_comp;
781 textwidth = 4.0f * 8.5f;
782 if (!vector_add_string(symbol, symbol->text, 4, text_xposn, text_yposn, font_height, textwidth,
783 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
784 text_xposn = (50.0f - 0.5f) + xoffset_comp;
785 if (!vector_add_string(symbol, symbol->text + 4, 4, text_xposn, text_yposn, font_height, textwidth,
786 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
787 if (addon_len) {
788 text_xposn = (addon_len == 2 ? 77.0f : 91.0f) + xoffset_comp + addon_gap;
789 textwidth = addon_len * 8.5f;
790 if (!vector_add_string(symbol, addon, addon_len, text_xposn, addon_text_yposn, font_height,
791 textwidth, 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
792 if (upcean_guard_whitespace) {
793 text_xposn = symbol->width + gws_right_fudge + qz_right + xoffset;
794 textwidth = 8.5f;
795 if (!vector_add_string(symbol, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn,
796 font_height, textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
797 }
798 } else if (upcean_guard_whitespace) {
799 text_xposn = symbol->width + gws_right_fudge + qz_right + xoffset;
800 textwidth = 8.5f;
801 if (!vector_add_string(symbol, (const unsigned char *) ">", 1, text_xposn, text_yposn,
802 font_height, textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
803 }
804
805 } else if (upceanflag == 12) { /* UPC-A */
806 float text_xposn = -(5.0f - 0.35f) + xoffset_comp;
807 textwidth = 6.2f;
808 if (!vector_add_string(symbol, symbol->text, 1, text_xposn, text_yposn, upcae_outside_font_height,
809 textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
810 text_xposn = 28.0f + xoffset_comp;
811 textwidth = 5.0f * 8.5f;
812 if (!vector_add_string(symbol, symbol->text + 1, 5, text_xposn, text_yposn, font_height, textwidth,
813 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
814 text_xposn = 67.0f + xoffset_comp;
815 if (!vector_add_string(symbol, symbol->text + 6, 5, text_xposn, text_yposn, font_height, textwidth,
816 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
817 /* TODO: GS1 General Specs v24.0 5.2.5 Human readable interpretation says 5X but this could cause
818 digit's righthand to touch any add-on, now that they descend, so use 4X, until clarified */
819 text_xposn = (95.0f - 0.35f) + 4.0f + xoffset_comp;
820 textwidth = 6.2f;
821 if (!vector_add_string(symbol, symbol->text + 11, 1, text_xposn, text_yposn,
822 upcae_outside_font_height, textwidth, 1 /*left align*/, &last_string)) {
823 return ZINT_ERROR_MEMORY;
824 }
825 if (addon_len) {
826 text_xposn = (addon_len == 2 ? 105.0f : 119.0f) + xoffset_comp + addon_gap;
827 textwidth = addon_len * 8.5f;
828 if (!vector_add_string(symbol, addon, addon_len, text_xposn, addon_text_yposn, font_height,
829 textwidth, 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
830 if (upcean_guard_whitespace) {
831 text_xposn = symbol->width + gws_right_fudge + qz_right + xoffset;
832 textwidth = 8.5f;
833 if (!vector_add_string(symbol, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn,
834 font_height, textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
835 }
836 }
837
838 } else { /* EAN-13 */
839 float text_xposn = -(5.0f - 0.1f) + xoffset_comp;
840 textwidth = 8.5f;
841 if (!vector_add_string(symbol, symbol->text, 1, text_xposn, text_yposn, font_height, textwidth,
842 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
843 text_xposn = (24.0f + 0.5f) + xoffset_comp;
844 textwidth = 6.0f * 8.5f;
845 if (!vector_add_string(symbol, symbol->text + 1, 6, text_xposn, text_yposn, font_height, textwidth,
846 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
847 text_xposn = (71.0f - 0.5f) + xoffset_comp;
848 if (!vector_add_string(symbol, symbol->text + 7, 6, text_xposn, text_yposn, font_height, textwidth,
849 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
850 if (addon_len) {
851 text_xposn = (addon_len == 2 ? 105.0f : 119.0f) + xoffset_comp + addon_gap;
852 textwidth = addon_len * 8.5f;
853 if (!vector_add_string(symbol, addon, addon_len, text_xposn, addon_text_yposn, font_height,
854 textwidth, 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
855 if (upcean_guard_whitespace) {
856 text_xposn = symbol->width + gws_right_fudge + qz_right + xoffset;
857 textwidth = 8.5f;
858 if (!vector_add_string(symbol, (const unsigned char *) ">", 1, text_xposn, addon_text_yposn,
859 font_height, textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
860 }
861 } else if (upcean_guard_whitespace) {
862 text_xposn = symbol->width + gws_right_fudge + qz_right + xoffset;
863 textwidth = 8.5f;
864 if (!vector_add_string(symbol, (const unsigned char *) ">", 1, text_xposn, text_yposn,
865 font_height, textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
866 }
867 }
868
869 } else if (upceanflag) { /* EAN-2, EAN-5 (standalone add-ons) */
870 /* Put at top (and centered) */
871 float text_xposn = main_width / 2.0f + xoffset;
872 float text_yposn = yoffset + font_height - digit_ascender;
873 if (symbol->border_width > 0
874 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP))) {
875 text_yposn -= symbol->border_width;
876 }
877 if (text_yposn < 0.0f) {
878 text_yposn = 0.0f;
879 }
880 addon_len = (int) ustrlen(symbol->text);
881 textwidth = addon_len * 8.5f;
882 if (!vector_add_string(symbol, symbol->text, addon_len, text_xposn, text_yposn, font_height,
883 textwidth, 0 /*centre align*/, &last_string)) return ZINT_ERROR_MEMORY;
884 if (upcean_guard_whitespace) {
885 text_xposn = symbol->width + gws_right_fudge + qz_right + xoffset;
886 textwidth = 8.5f;
887 if (!vector_add_string(symbol, (const unsigned char *) ">", 1, text_xposn, text_yposn,
888 font_height, textwidth, 2 /*right align*/, &last_string)) return ZINT_ERROR_MEMORY;
889 }
890
891 } else {
892 /* Put normal human readable text at the bottom (and centered) */
893 float text_xposn = main_width / 2.0f + xoffset_comp;
894 float text_yposn = yoffset + symbol->height + font_height + text_gap; /* Calculated to bottom of text */
895 text_yposn -= symbol->output_options & SMALL_TEXT ? descent_small : descent;
896 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND))
897 && !(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */
898 text_yposn += symbol->border_width;
899 }
900 if (!vector_add_string(symbol, symbol->text, -1, text_xposn, text_yposn, font_height, symbol->width, 0,
901 &last_string)) return ZINT_ERROR_MEMORY;
902 }
903 }
904
905 /* Separator binding for stacked barcodes */
906 if ((symbol->output_options & BARCODE_BIND) && (symbol->rows > 1) && is_stackable(symbol->symbology)) {
907 float sep_xoffset = xoffset;
908 float sep_width = symbol->width;
909 float sep_height = 1.0f, sep_yoffset, sep_half_height;
910 if (symbol->option_3 > 0 && symbol->option_3 <= 4) {
911 sep_height = symbol->option_3;
912 }
913 sep_half_height = sep_height / 2.0f;
914 sep_yoffset = yoffset - sep_half_height;
915 if (is_codablockf) {
916 /* Avoid 11-module start and 13-module stop chars */
917 sep_xoffset += 11;
918 sep_width -= 11 + 13;
919 }
920 /* Adjust original rectangles so don't overlap with separator(s) (important for RGBA) */
921 for (r = 0; r < symbol->rows; r++) {
922 for (rect = first_row_rects[r], i = 0; rect && rect != first_row_rects[r + 1]; rect = rect->next, i++) {
923 if (is_codablockf) { /* Skip start and stop chars */
924 if (i < 3) {
925 continue;
926 }
927 if ((i / 3) * 11 + 13 >= symbol->width) { /* 3 bars and 11 modules per char */
928 break;
929 }
930 }
931 if (r != 0) {
932 rect->y += sep_height - sep_half_height;
933 rect->height -= r + 1 == symbol->rows ? sep_half_height : sep_height;
934 } else {
935 rect->height -= sep_half_height;
936 }
937 if (rect->height < 0) {
938 rect->height = 0.0f;
939 /* TODO: warn? */
940 }
941 }
942 }
943 for (r = 1; r < symbol->rows; r++) {
944 const float row_height = symbol->row_height[r - 1] ? symbol->row_height[r - 1] : large_bar_height;
945 if (!vector_add_rect(symbol, sep_xoffset, (r * row_height) + sep_yoffset, sep_width, sep_height,
946 &last_rect)) return ZINT_ERROR_MEMORY;
947 }
948 }
949
950 /* Bind/box */
951 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BOX | BARCODE_BIND | BARCODE_BIND_TOP))) {
952 const int horz_outside = is_fixed_ratio(symbol->symbology);
953 float ybind_top = yoffset - symbol->border_width;
954 /* Following equivalent to yoffset + symbol->height + dot_overspill except for BARCODE_MAXICODE */
955 float ybind_bot = vector->height - textoffset - boffset;
956 if (horz_outside) {
957 ybind_top = 0;
958 ybind_bot = vector->height - symbol->border_width;
959 } else if (upceanflag == 2 || upceanflag == 5) {
960 ybind_top += textoffset;
961 ybind_bot += textoffset;
962 }
963 /* Top */
964 if (!vector_add_rect(symbol, 0.0f, ybind_top, vector->width, symbol->border_width, &last_rect))
965 return ZINT_ERROR_MEMORY;
966 if (!(symbol->output_options & BARCODE_BOX) && no_extend) {
967 /* CodaBlockF/DPD bind - does not extend over horizontal whitespace */
968 last_rect->x = xoffset;
969 last_rect->width -= xoffset + roffset;
970 }
971 /* Bottom */
972 if (!(symbol->output_options & BARCODE_BIND_TOP)) { /* Trumps BARCODE_BOX & BARCODE_BIND */
973 if (!vector_add_rect(symbol, 0.0f, ybind_bot, vector->width, symbol->border_width, &last_rect))
974 return ZINT_ERROR_MEMORY;
975 if (!(symbol->output_options & BARCODE_BOX) && no_extend) {
976 /* CodaBlockF/DPD bind - does not extend over horizontal whitespace */
977 last_rect->x = xoffset;
978 last_rect->width -= xoffset + roffset;
979 }
980 }
981 if (symbol->output_options & BARCODE_BOX) {
982 const float xbox_right = vector->width - symbol->border_width;
983 float box_top = yoffset;
984 /* Following equivalent to symbol->height except for BARCODE_MAXICODE */
985 float box_height = vector->height - textoffset - dot_overspill - yoffset - boffset;
986 if (horz_outside) {
987 box_top = symbol->border_width;
988 box_height = vector->height - symbol->border_width * 2;
989 } else if (upceanflag == 2 || upceanflag == 5) {
990 box_top += textoffset;
991 }
992 /* Left */
993 if (!vector_add_rect(symbol, 0.0f, box_top, symbol->border_width, box_height, &last_rect))
994 return ZINT_ERROR_MEMORY;
995 /* Right */
996 if (!vector_add_rect(symbol, xbox_right, box_top, symbol->border_width, box_height, &last_rect))
997 return ZINT_ERROR_MEMORY;
998 }
999 }
1000
1001 vector_reduce_rectangles(symbol);
1002
1003 vector_scale(symbol, file_type);
1004
1005 if (file_type != OUT_EMF_FILE) {
1006 /* EMF does its own rotation (with mixed results in various apps) */
1007 vector_rotate(symbol, rotate_angle);
1008 }
1009
1010 switch (file_type) {
1011 case OUT_EPS_FILE:
1012 error_number = ps_plot(symbol);
1013 break;
1014 case OUT_SVG_FILE:
1015 error_number = svg_plot(symbol);
1016 break;
1017 case OUT_EMF_FILE:
1018 error_number = emf_plot(symbol, rotate_angle);
1019 break;
1020 /* case OUT_BUFFER: No more work needed */
1021 }
1022
1023 return error_number;
1024 }
1025
1026 /* vim: set ts=4 sw=4 et : */