Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zint/backend/svg.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 /* svg.c - Scalable Vector Graphics */ | |
| 2 /* | |
| 3 libzint - the open source barcode library | |
| 4 Copyright (C) 2009-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 <errno.h> | |
| 34 #include <math.h> | |
| 35 #include <stdio.h> | |
| 36 | |
| 37 #include "common.h" | |
| 38 #include "filemem.h" | |
| 39 #include "output.h" | |
| 40 #include "fonts/normal_woff2.h" | |
| 41 #include "fonts/upcean_woff2.h" | |
| 42 | |
| 43 /* Convert Ultracode rectangle colour to RGB */ | |
| 44 static void svg_pick_colour(const int colour, char colour_code[7]) { | |
| 45 const int idx = colour >= 1 && colour <= 8 ? colour - 1 : 6 /*black*/; | |
| 46 static const char rgbs[8][7] = { | |
| 47 "00ffff", /* 0: Cyan (1) */ | |
| 48 "0000ff", /* 1: Blue (2) */ | |
| 49 "ff00ff", /* 2: Magenta (3) */ | |
| 50 "ff0000", /* 3: Red (4) */ | |
| 51 "ffff00", /* 4: Yellow (5) */ | |
| 52 "00ff00", /* 5: Green (6) */ | |
| 53 "000000", /* 6: Black (7) */ | |
| 54 "ffffff", /* 7: White (8) */ | |
| 55 }; | |
| 56 strcpy(colour_code, rgbs[idx]); | |
| 57 } | |
| 58 | |
| 59 /* Convert text to use HTML entity codes */ | |
| 60 static void svg_make_html_friendly(const unsigned char *string, char *html_version) { | |
| 61 | |
| 62 for (; *string; string++) { | |
| 63 switch (*string) { | |
| 64 case '>': | |
| 65 strcpy(html_version, ">"); | |
| 66 html_version += 4; | |
| 67 break; | |
| 68 | |
| 69 case '<': | |
| 70 strcpy(html_version, "<"); | |
| 71 html_version += 4; | |
| 72 break; | |
| 73 | |
| 74 case '&': | |
| 75 strcpy(html_version, "&"); | |
| 76 html_version += 5; | |
| 77 break; | |
| 78 | |
| 79 case '"': | |
| 80 strcpy(html_version, """); | |
| 81 html_version += 6; | |
| 82 break; | |
| 83 | |
| 84 case '\'': | |
| 85 strcpy(html_version, "'"); | |
| 86 html_version += 6; | |
| 87 break; | |
| 88 | |
| 89 default: | |
| 90 *html_version++ = *string; | |
| 91 break; | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 *html_version = '\0'; | |
| 96 } | |
| 97 | |
| 98 /* Helper to output floating point attribute */ | |
| 99 static void svg_put_fattrib(const char *prefix, const int dp, const float val, struct filemem *fmp) { | |
| 100 fm_putsf(prefix, dp, val, fmp); | |
| 101 fm_putc('"', fmp); | |
| 102 } | |
| 103 | |
| 104 /* Helper to output opacity attribute attribute and close tag (maybe) */ | |
| 105 static void svg_put_opacity_close(const unsigned char alpha, const float val, const int close, struct filemem *fmp) { | |
| 106 if (alpha != 0xff) { | |
| 107 svg_put_fattrib(" opacity=\"", 3, val, fmp); | |
| 108 } | |
| 109 if (close) { | |
| 110 fm_putc('/', fmp); | |
| 111 } | |
| 112 fm_puts(">\n", fmp); | |
| 113 } | |
| 114 | |
| 115 INTERNAL int svg_plot(struct zint_symbol *symbol) { | |
| 116 static const char normal_font_family[] = "Arimo"; | |
| 117 static const char upcean_font_family[] = "OCRB"; | |
| 118 struct filemem fm; | |
| 119 struct filemem *const fmp = &fm; | |
| 120 float previous_diameter; | |
| 121 float radius, half_radius, half_sqrt3_radius; | |
| 122 int i; | |
| 123 char fgcolour_string[7]; | |
| 124 char bgcolour_string[7]; | |
| 125 unsigned char fgred, fggreen, fgblue, fg_alpha; | |
| 126 unsigned char bgred, bggreen, bgblue, bg_alpha; | |
| 127 float fg_alpha_opacity = 0.0f, bg_alpha_opacity = 0.0f; /* Suppress `-Wmaybe-uninitialized` */ | |
| 128 int bold; | |
| 129 | |
| 130 struct zint_vector_rect *rect; | |
| 131 struct zint_vector_hexagon *hex; | |
| 132 struct zint_vector_circle *circle; | |
| 133 struct zint_vector_string *string; | |
| 134 | |
| 135 char colour_code[7]; | |
| 136 int len, html_len; | |
| 137 | |
| 138 const int upcean = is_upcean(symbol->symbology); | |
| 139 char *html_string; | |
| 140 | |
| 141 (void) out_colour_get_rgb(symbol->fgcolour, &fgred, &fggreen, &fgblue, &fg_alpha); | |
| 142 if (fg_alpha != 0xff) { | |
| 143 fg_alpha_opacity = fg_alpha / 255.0f; | |
| 144 } | |
| 145 sprintf(fgcolour_string, "%02X%02X%02X", fgred, fggreen, fgblue); | |
| 146 (void) out_colour_get_rgb(symbol->bgcolour, &bgred, &bggreen, &bgblue, &bg_alpha); | |
| 147 if (bg_alpha != 0xff) { | |
| 148 bg_alpha_opacity = bg_alpha / 255.0f; | |
| 149 } | |
| 150 sprintf(bgcolour_string, "%02X%02X%02X", bgred, bggreen, bgblue); | |
| 151 | |
| 152 len = (int) ustrlen(symbol->text); | |
| 153 html_len = len + 1; | |
| 154 | |
| 155 for (i = 0; i < len; i++) { | |
| 156 switch (symbol->text[i]) { | |
| 157 case '>': | |
| 158 case '<': | |
| 159 case '"': | |
| 160 case '&': | |
| 161 case '\'': | |
| 162 html_len += 6; | |
| 163 break; | |
| 164 } | |
| 165 } | |
| 166 if (symbol->output_options & EANUPC_GUARD_WHITESPACE) { | |
| 167 html_len += 12; /* Allow for "<" & ">" */ | |
| 168 } | |
| 169 | |
| 170 html_string = (char *) z_alloca(html_len); | |
| 171 | |
| 172 /* Check for no created vector set */ | |
| 173 /* E-Mail Christian Schmitz 2019-09-10: reason unknown Ticket #164 */ | |
| 174 if (symbol->vector == NULL) { | |
| 175 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 681, "Vector header NULL"); | |
| 176 } | |
| 177 if (!fm_open(fmp, symbol, "w")) { | |
| 178 return errtxtf(ZINT_ERROR_FILE_ACCESS, symbol, 680, "Could not open SVG output file (%1$d: %2$s)", fmp->err, | |
| 179 strerror(fmp->err)); | |
| 180 } | |
| 181 | |
| 182 /* Start writing the header */ | |
| 183 fm_puts("<?xml version=\"1.0\" standalone=\"no\"?>\n" | |
| 184 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", | |
| 185 fmp); | |
| 186 fm_printf(fmp, "<svg width=\"%d\" height=\"%d\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", | |
| 187 (int) ceilf(symbol->vector->width), (int) ceilf(symbol->vector->height)); | |
| 188 fm_puts(" <desc>Zint Generated Symbol</desc>\n", fmp); | |
| 189 if ((symbol->output_options & EMBED_VECTOR_FONT) && symbol->vector->strings) { | |
| 190 /* Split into `puts()` rather than one very large `printf()` */ | |
| 191 fm_printf(fmp, " <style>@font-face {font-family:\"%s\"; src:url(data:font/woff2;base64,", | |
| 192 upcean ? "OCRB" : "Arimo"); | |
| 193 fm_puts(upcean ? upcean_woff2 : normal_woff2, fmp); | |
| 194 fm_puts(");}</style>\n", fmp); | |
| 195 } | |
| 196 fm_printf(fmp, " <g id=\"barcode\" fill=\"#%s\">\n", fgcolour_string); | |
| 197 | |
| 198 if (bg_alpha != 0) { | |
| 199 fm_printf(fmp, " <rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%s\"", | |
| 200 (int) ceilf(symbol->vector->width), (int) ceilf(symbol->vector->height), bgcolour_string); | |
| 201 svg_put_opacity_close(bg_alpha, bg_alpha_opacity, 1 /*close*/, fmp); | |
| 202 } | |
| 203 | |
| 204 if (symbol->vector->rectangles) { | |
| 205 int current_colour = 0; | |
| 206 rect = symbol->vector->rectangles; | |
| 207 fm_puts(" <path d=\"", fmp); | |
| 208 while (rect) { | |
| 209 if (current_colour && rect->colour != current_colour) { | |
| 210 fm_putc('"', fmp); | |
| 211 if (current_colour != -1) { | |
| 212 svg_pick_colour(current_colour, colour_code); | |
| 213 fm_printf(fmp, " fill=\"#%s\"", colour_code); | |
| 214 } | |
| 215 svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp); | |
| 216 fm_puts(" <path d=\"", fmp); | |
| 217 } | |
| 218 current_colour = rect->colour; | |
| 219 fm_putsf("M", 2, rect->x, fmp); | |
| 220 fm_putsf(" ", 2, rect->y, fmp); | |
| 221 fm_putsf("h", 2, rect->width, fmp); | |
| 222 fm_putsf("v", 2, rect->height, fmp); | |
| 223 fm_putsf("h-", 2, rect->width, fmp); | |
| 224 fm_puts("Z", fmp); | |
| 225 rect = rect->next; | |
| 226 } | |
| 227 fm_putc('"', fmp); | |
| 228 if (current_colour != -1) { | |
| 229 svg_pick_colour(current_colour, colour_code); | |
| 230 fm_printf(fmp, " fill=\"#%s\"", colour_code); | |
| 231 } | |
| 232 svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp); | |
| 233 } | |
| 234 | |
| 235 if (symbol->vector->hexagons) { | |
| 236 previous_diameter = radius = half_radius = half_sqrt3_radius = 0.0f; | |
| 237 hex = symbol->vector->hexagons; | |
| 238 fm_puts(" <path d=\"", fmp); | |
| 239 while (hex) { | |
| 240 if (previous_diameter != hex->diameter) { | |
| 241 previous_diameter = hex->diameter; | |
| 242 radius = 0.5f * previous_diameter; | |
| 243 half_radius = 0.25f * previous_diameter; | |
| 244 half_sqrt3_radius = 0.43301270189221932338f * previous_diameter; | |
| 245 } | |
| 246 if ((hex->rotation == 0) || (hex->rotation == 180)) { | |
| 247 fm_putsf("M", 2, hex->x, fmp); | |
| 248 fm_putsf(" ", 2, hex->y + radius, fmp); | |
| 249 fm_putsf("L", 2, hex->x + half_sqrt3_radius, fmp); | |
| 250 fm_putsf(" ", 2, hex->y + half_radius, fmp); | |
| 251 fm_putsf("L", 2, hex->x + half_sqrt3_radius, fmp); | |
| 252 fm_putsf(" ", 2, hex->y - half_radius, fmp); | |
| 253 fm_putsf("L", 2, hex->x, fmp); | |
| 254 fm_putsf(" ", 2, hex->y - radius, fmp); | |
| 255 fm_putsf("L", 2, hex->x - half_sqrt3_radius, fmp); | |
| 256 fm_putsf(" ", 2, hex->y - half_radius, fmp); | |
| 257 fm_putsf("L", 2, hex->x - half_sqrt3_radius, fmp); | |
| 258 fm_putsf(" ", 2, hex->y + half_radius, fmp); | |
| 259 } else { | |
| 260 fm_putsf("M", 2, hex->x - radius, fmp); | |
| 261 fm_putsf(" ", 2, hex->y, fmp); | |
| 262 fm_putsf("L", 2, hex->x - half_radius, fmp); | |
| 263 fm_putsf(" ", 2, hex->y + half_sqrt3_radius, fmp); | |
| 264 fm_putsf("L", 2, hex->x + half_radius, fmp); | |
| 265 fm_putsf(" ", 2, hex->y + half_sqrt3_radius, fmp); | |
| 266 fm_putsf("L", 2, hex->x + radius, fmp); | |
| 267 fm_putsf(" ", 2, hex->y, fmp); | |
| 268 fm_putsf("L", 2, hex->x + half_radius, fmp); | |
| 269 fm_putsf(" ", 2, hex->y - half_sqrt3_radius, fmp); | |
| 270 fm_putsf("L", 2, hex->x - half_radius, fmp); | |
| 271 fm_putsf(" ", 2, hex->y - half_sqrt3_radius, fmp); | |
| 272 } | |
| 273 fm_putc('Z', fmp); | |
| 274 hex = hex->next; | |
| 275 } | |
| 276 fm_putc('"', fmp); | |
| 277 svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp); | |
| 278 } | |
| 279 | |
| 280 previous_diameter = radius = 0.0f; | |
| 281 circle = symbol->vector->circles; | |
| 282 while (circle) { | |
| 283 if (previous_diameter != circle->diameter) { | |
| 284 previous_diameter = circle->diameter; | |
| 285 radius = 0.5f * previous_diameter; | |
| 286 } | |
| 287 fm_puts(" <circle", fmp); | |
| 288 svg_put_fattrib(" cx=\"", 2, circle->x, fmp); | |
| 289 svg_put_fattrib(" cy=\"", 2, circle->y, fmp); | |
| 290 svg_put_fattrib(" r=\"", circle->width ? 3 : 2, radius, fmp); | |
| 291 | |
| 292 if (circle->colour) { /* Legacy - no longer used */ | |
| 293 if (circle->width) { | |
| 294 fm_printf(fmp, " stroke=\"#%s\"", bgcolour_string); | |
| 295 svg_put_fattrib(" stroke-width=\"", 3, circle->width, fmp); | |
| 296 fm_puts(" fill=\"none\"", fmp); | |
| 297 } else { | |
| 298 fm_printf(fmp, " fill=\"#%s\"", bgcolour_string); | |
| 299 } | |
| 300 /* This doesn't work how the user is likely to expect - more work needed! */ | |
| 301 svg_put_opacity_close(bg_alpha, bg_alpha_opacity, 1 /*close*/, fmp); | |
| 302 } else { | |
| 303 if (circle->width) { | |
| 304 fm_printf(fmp, " stroke=\"#%s\"", fgcolour_string); | |
| 305 svg_put_fattrib(" stroke-width=\"", 3, circle->width, fmp); | |
| 306 fm_puts(" fill=\"none\"", fmp); | |
| 307 } | |
| 308 svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp); | |
| 309 } | |
| 310 circle = circle->next; | |
| 311 } | |
| 312 | |
| 313 bold = (symbol->output_options & BOLD_TEXT) && !upcean; | |
| 314 string = symbol->vector->strings; | |
| 315 while (string) { | |
| 316 const char *const halign = string->halign == 2 ? "end" : string->halign == 1 ? "start" : "middle"; | |
| 317 fm_puts(" <text", fmp); | |
| 318 svg_put_fattrib(" x=\"", 2, string->x, fmp); | |
| 319 svg_put_fattrib(" y=\"", 2, string->y, fmp); | |
| 320 fm_printf(fmp, " text-anchor=\"%s\"", halign); | |
| 321 if (upcean) { | |
| 322 fm_printf(fmp, " font-family=\"%s, monospace\"", upcean_font_family); | |
| 323 } else { | |
| 324 fm_printf(fmp, " font-family=\"%s, Arial, sans-serif\"", normal_font_family); | |
| 325 } | |
| 326 svg_put_fattrib(" font-size=\"", 1, string->fsize, fmp); | |
| 327 if (bold) { | |
| 328 fm_puts(" font-weight=\"bold\"", fmp); | |
| 329 } | |
| 330 if (string->rotation != 0) { | |
| 331 fm_printf(fmp, " transform=\"rotate(%d", string->rotation); | |
| 332 fm_putsf(",", 2, string->x, fmp); | |
| 333 fm_putsf(",", 2, string->y, fmp); | |
| 334 fm_puts(")\"", fmp); | |
| 335 } | |
| 336 svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 0 /*close*/, fmp); | |
| 337 svg_make_html_friendly(string->text, html_string); | |
| 338 fm_printf(fmp, " %s\n", html_string); | |
| 339 fm_puts(" </text>\n", fmp); | |
| 340 string = string->next; | |
| 341 } | |
| 342 | |
| 343 fm_puts(" </g>\n" | |
| 344 "</svg>\n", fmp); | |
| 345 | |
| 346 if (fm_error(fmp)) { | |
| 347 errtxtf(0, symbol, 682, "Incomplete write to SVG output (%1$d: %2$s)", fmp->err, strerror(fmp->err)); | |
| 348 (void) fm_close(fmp, symbol); | |
| 349 return ZINT_ERROR_FILE_WRITE; | |
| 350 } | |
| 351 | |
| 352 if (!fm_close(fmp, symbol)) { | |
| 353 return errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 684, "Failure on closing SVG output file (%1$d: %2$s)", | |
| 354 fmp->err, strerror(fmp->err)); | |
| 355 } | |
| 356 | |
| 357 return 0; | |
| 358 } | |
| 359 | |
| 360 /* vim: set ts=4 sw=4 et : */ |
