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