2019-09-11 20:14:42 +12:00
|
|
|
/* svg.c - Scalable Vector Graphics */
|
|
|
|
/*
|
|
|
|
libzint - the open source barcode library
|
- `zint_symbol->fgcolour` & `bgcolour` buffer lengths extended 10
-> 16 to allow for "C,M,Y,K" comma-separated decimal percentage
strings
- API/CLI/GUI: allow foreground/background colours to be specified
as comma-separated decimal "C,M,Y,K" strings where "C", "M" etc.
are percentages (0-100) (ticket #281, 3rd point)
- output.c: new funcs `out_colour_get_rgb()` & `out_colour_get_cmyk()`
and use in bmp/emf/gif etc.
- PCX: add alpha support
- GUI: fix fg/gbcolor icon background not being reset on zap
- GUI: Rearrange some Appearance tab inputs (Border Type <-> Width,
Show Text <-> Font, Text/Font <-> Printing Scale/Size) to flow
more naturally (hopefully)
- GUI: save button "Save As..." -> "Save..." and add icon
- CLI: add --bgcolor/colour & --fgcolor/colour synonyms
2023-01-30 08:51:11 +13:00
|
|
|
Copyright (C) 2009-2023 Robin Stuart <rstuart114@gmail.com>
|
2019-09-11 20:14:42 +12:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2022-07-15 03:01:30 +12:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause */
|
2019-09-11 20:14:42 +12:00
|
|
|
|
2021-06-10 22:15:39 +12:00
|
|
|
#include <errno.h>
|
2019-09-11 20:14:42 +12:00
|
|
|
#include <math.h>
|
2022-07-15 03:01:30 +12:00
|
|
|
#include <stdio.h>
|
2019-09-11 20:14:42 +12:00
|
|
|
|
|
|
|
#include "common.h"
|
2023-12-28 08:20:19 +13:00
|
|
|
#include "filemem.h"
|
- API: add new zint_symbol `dpmm` field for output resolution (BMP/
EMF/PCX/PNG/TIF only, i.e. excluding EPS, GIF & SVG)
- Add support for specifying scale by X-dimension and resolution
with new option `--scalexdimdp` for CLI/Tcl & new API function
`ZBarcode_Scale_From_XdimDp()` (+ `ZBarcode_XdimDp_From_Scale()`
& `ZBarcode_Default_Xdim()`) and new GUI popup; manual: document
- BMP/EMF/PCX/PNG/TIF: use new `dpmm` resolution field (for EMF
following Inkscape)
- backend_qt: add `dpmm()`, `vectorWidth()`, `vectorHeight()`,
`noPng()`, `getVersion()`, `takesGS1AIData()`, & `XdimDp` stuff
incl. new `QZintXdimDp` struct for passing around scale vars &
use in `getAsCLI()`; add comments
- Raise `scale` limit to 200 (from 100) to allow for large dpmm
- output: create directories & subdirectories as necessary for
output path using new function `out_fopen()` and use in BMP/EMF/
EPS/GIF/PCX/PNG/SVG/TIF
- DPLEIT/DPIDENT: format HRT according to (incomplete)
documentation, and set default height to 72X (from 50X)
- CODE128B renamed to CODE128AB as can use subsets A and/or B
- CODABAR: fix minimum height calc
- EMF: fix indexing of handles (zero-based not 1-based)
- GUI: fix symbology zap (previous technique of clearing and
re-loading settings without doing a sync no longer works);
fix UPCEAN guard descent enable
- MAILMARK: better error message if input < 14 characters
- GUI: add "Default" button for DAFT tracker ratio & enable/disable
various default buttons; use new `takesGS1AIData()` to
enable/disable GS1-specific checkboxes
- CLI: use new `validate_float()` to parse float options (7
significant digits allowed only, no scientific notation)
- DATAMATRIX/GRIDMATRIX/PDF417/QR/ULTRA: micro-optimize structapp
ID parse
- library/CLI: fiddle with static asserts (make CHAR_BIT sensitive,
supposedly)
- win32/README: update building libpng (assembly removed)
- README.linux: document incompatibility of Qt6 >= 6.3
- manual: expand Barcode Studio waffle
- test suite: change range separator to hyphen and allow multiple
excludes
2022-12-03 10:39:01 +13:00
|
|
|
#include "output.h"
|
2023-06-14 05:12:20 +12:00
|
|
|
#include "fonts/normal_woff2.h"
|
2023-06-12 12:25:55 +12:00
|
|
|
#include "fonts/upcean_woff2.h"
|
|
|
|
|
|
|
|
/* Convert Ultracode rectangle colour to RGB */
|
|
|
|
static void svg_pick_colour(const int colour, char colour_code[7]) {
|
|
|
|
const int idx = colour >= 1 && colour <= 8 ? colour - 1 : 6 /*black*/;
|
|
|
|
static const char rgbs[8][7] = {
|
|
|
|
"00ffff", /* 0: Cyan (1) */
|
|
|
|
"0000ff", /* 1: Blue (2) */
|
|
|
|
"ff00ff", /* 2: Magenta (3) */
|
|
|
|
"ff0000", /* 3: Red (4) */
|
|
|
|
"ffff00", /* 4: Yellow (5) */
|
|
|
|
"00ff00", /* 5: Green (6) */
|
|
|
|
"000000", /* 6: Black (7) */
|
|
|
|
"ffffff", /* 7: White (8) */
|
|
|
|
};
|
|
|
|
strcpy(colour_code, rgbs[idx]);
|
2019-12-19 07:33:18 +13:00
|
|
|
}
|
|
|
|
|
2023-06-12 12:25:55 +12:00
|
|
|
/* Convert text to use HTML entity codes */
|
|
|
|
static void svg_make_html_friendly(const unsigned char *string, char *html_version) {
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2023-06-12 12:25:55 +12:00
|
|
|
for (; *string; string++) {
|
|
|
|
switch (*string) {
|
2019-10-30 21:40:26 +13:00
|
|
|
case '>':
|
2023-06-12 12:25:55 +12:00
|
|
|
strcpy(html_version, ">");
|
|
|
|
html_version += 4;
|
2019-10-30 21:40:26 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '<':
|
2023-06-12 12:25:55 +12:00
|
|
|
strcpy(html_version, "<");
|
|
|
|
html_version += 4;
|
2019-10-30 21:40:26 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '&':
|
2023-06-12 12:25:55 +12:00
|
|
|
strcpy(html_version, "&");
|
|
|
|
html_version += 5;
|
2019-10-30 21:40:26 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '"':
|
2023-06-12 12:25:55 +12:00
|
|
|
strcpy(html_version, """);
|
|
|
|
html_version += 6;
|
2019-10-30 21:40:26 +13:00
|
|
|
break;
|
|
|
|
|
2019-11-04 00:51:12 +13:00
|
|
|
case '\'':
|
2023-06-12 12:25:55 +12:00
|
|
|
strcpy(html_version, "'");
|
|
|
|
html_version += 6;
|
2019-10-30 21:40:26 +13:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2023-06-12 12:25:55 +12:00
|
|
|
*html_version++ = *string;
|
2019-10-17 06:42:22 +13:00
|
|
|
break;
|
|
|
|
}
|
2019-10-30 21:40:26 +13:00
|
|
|
}
|
2023-06-12 12:25:55 +12:00
|
|
|
|
|
|
|
*html_version = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to output floating point attribute */
|
2023-12-28 08:20:19 +13:00
|
|
|
static void svg_put_fattrib(const char *prefix, const int dp, const float val, struct filemem *fmp) {
|
|
|
|
fm_putsf(prefix, dp, val, fmp);
|
|
|
|
fm_putc('"', fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to output opacity attribute attribute and close tag (maybe) */
|
2023-12-28 08:20:19 +13:00
|
|
|
static void svg_put_opacity_close(const unsigned char alpha, const float val, const int close, struct filemem *fmp) {
|
2023-06-12 12:25:55 +12:00
|
|
|
if (alpha != 0xff) {
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_fattrib(" opacity=\"", 3, val, fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
|
|
|
if (close) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putc('/', fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(">\n", fmp);
|
2019-10-17 06:42:22 +13:00
|
|
|
}
|
|
|
|
|
2019-12-19 13:37:55 +13:00
|
|
|
INTERNAL int svg_plot(struct zint_symbol *symbol) {
|
2023-11-20 08:39:54 +13:00
|
|
|
static const char normal_font_family[] = "Arimo";
|
|
|
|
static const char upcean_font_family[] = "OCRB";
|
2023-12-28 08:20:19 +13:00
|
|
|
struct filemem fm;
|
|
|
|
struct filemem *const fmp = &fm;
|
2019-09-11 20:14:42 +12:00
|
|
|
int error_number = 0;
|
2020-10-27 01:21:43 +13:00
|
|
|
float previous_diameter;
|
|
|
|
float radius, half_radius, half_sqrt3_radius;
|
2019-11-18 07:30:55 +13:00
|
|
|
int i;
|
2020-08-03 09:26:39 +12:00
|
|
|
char fgcolour_string[7];
|
|
|
|
char bgcolour_string[7];
|
- `zint_symbol->fgcolour` & `bgcolour` buffer lengths extended 10
-> 16 to allow for "C,M,Y,K" comma-separated decimal percentage
strings
- API/CLI/GUI: allow foreground/background colours to be specified
as comma-separated decimal "C,M,Y,K" strings where "C", "M" etc.
are percentages (0-100) (ticket #281, 3rd point)
- output.c: new funcs `out_colour_get_rgb()` & `out_colour_get_cmyk()`
and use in bmp/emf/gif etc.
- PCX: add alpha support
- GUI: fix fg/gbcolor icon background not being reset on zap
- GUI: Rearrange some Appearance tab inputs (Border Type <-> Width,
Show Text <-> Font, Text/Font <-> Printing Scale/Size) to flow
more naturally (hopefully)
- GUI: save button "Save As..." -> "Save..." and add icon
- CLI: add --bgcolor/colour & --fgcolor/colour synonyms
2023-01-30 08:51:11 +13:00
|
|
|
unsigned char fgred, fggreen, fgblue, fg_alpha;
|
|
|
|
unsigned char bgred, bggreen, bgblue, bg_alpha;
|
|
|
|
float fg_alpha_opacity = 0.0f, bg_alpha_opacity = 0.0f; /* Suppress `-Wmaybe-uninitialized` */
|
2020-10-01 00:19:12 +13:00
|
|
|
int bold;
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2019-09-11 20:14:42 +12:00
|
|
|
struct zint_vector_rect *rect;
|
|
|
|
struct zint_vector_hexagon *hex;
|
|
|
|
struct zint_vector_circle *circle;
|
|
|
|
struct zint_vector_string *string;
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2019-12-19 07:33:18 +13:00
|
|
|
char colour_code[7];
|
2021-06-10 22:15:39 +12:00
|
|
|
int len, html_len;
|
2019-12-19 07:33:18 +13:00
|
|
|
|
2023-11-20 08:39:54 +13:00
|
|
|
const int upcean = is_upcean(symbol->symbology);
|
2021-06-10 22:15:39 +12:00
|
|
|
char *html_string;
|
2019-11-18 07:30:55 +13:00
|
|
|
|
- `zint_symbol->fgcolour` & `bgcolour` buffer lengths extended 10
-> 16 to allow for "C,M,Y,K" comma-separated decimal percentage
strings
- API/CLI/GUI: allow foreground/background colours to be specified
as comma-separated decimal "C,M,Y,K" strings where "C", "M" etc.
are percentages (0-100) (ticket #281, 3rd point)
- output.c: new funcs `out_colour_get_rgb()` & `out_colour_get_cmyk()`
and use in bmp/emf/gif etc.
- PCX: add alpha support
- GUI: fix fg/gbcolor icon background not being reset on zap
- GUI: Rearrange some Appearance tab inputs (Border Type <-> Width,
Show Text <-> Font, Text/Font <-> Printing Scale/Size) to flow
more naturally (hopefully)
- GUI: save button "Save As..." -> "Save..." and add icon
- CLI: add --bgcolor/colour & --fgcolor/colour synonyms
2023-01-30 08:51:11 +13:00
|
|
|
(void) out_colour_get_rgb(symbol->fgcolour, &fgred, &fggreen, &fgblue, &fg_alpha);
|
|
|
|
if (fg_alpha != 0xff) {
|
|
|
|
fg_alpha_opacity = fg_alpha / 255.0f;
|
2020-08-03 09:26:39 +12:00
|
|
|
}
|
- `zint_symbol->fgcolour` & `bgcolour` buffer lengths extended 10
-> 16 to allow for "C,M,Y,K" comma-separated decimal percentage
strings
- API/CLI/GUI: allow foreground/background colours to be specified
as comma-separated decimal "C,M,Y,K" strings where "C", "M" etc.
are percentages (0-100) (ticket #281, 3rd point)
- output.c: new funcs `out_colour_get_rgb()` & `out_colour_get_cmyk()`
and use in bmp/emf/gif etc.
- PCX: add alpha support
- GUI: fix fg/gbcolor icon background not being reset on zap
- GUI: Rearrange some Appearance tab inputs (Border Type <-> Width,
Show Text <-> Font, Text/Font <-> Printing Scale/Size) to flow
more naturally (hopefully)
- GUI: save button "Save As..." -> "Save..." and add icon
- CLI: add --bgcolor/colour & --fgcolor/colour synonyms
2023-01-30 08:51:11 +13:00
|
|
|
sprintf(fgcolour_string, "%02X%02X%02X", fgred, fggreen, fgblue);
|
|
|
|
(void) out_colour_get_rgb(symbol->bgcolour, &bgred, &bggreen, &bgblue, &bg_alpha);
|
|
|
|
if (bg_alpha != 0xff) {
|
|
|
|
bg_alpha_opacity = bg_alpha / 255.0f;
|
2020-08-03 09:26:39 +12:00
|
|
|
}
|
- `zint_symbol->fgcolour` & `bgcolour` buffer lengths extended 10
-> 16 to allow for "C,M,Y,K" comma-separated decimal percentage
strings
- API/CLI/GUI: allow foreground/background colours to be specified
as comma-separated decimal "C,M,Y,K" strings where "C", "M" etc.
are percentages (0-100) (ticket #281, 3rd point)
- output.c: new funcs `out_colour_get_rgb()` & `out_colour_get_cmyk()`
and use in bmp/emf/gif etc.
- PCX: add alpha support
- GUI: fix fg/gbcolor icon background not being reset on zap
- GUI: Rearrange some Appearance tab inputs (Border Type <-> Width,
Show Text <-> Font, Text/Font <-> Printing Scale/Size) to flow
more naturally (hopefully)
- GUI: save button "Save As..." -> "Save..." and add icon
- CLI: add --bgcolor/colour & --fgcolor/colour synonyms
2023-01-30 08:51:11 +13:00
|
|
|
sprintf(bgcolour_string, "%02X%02X%02X", bgred, bggreen, bgblue);
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2021-06-10 22:15:39 +12:00
|
|
|
len = (int) ustrlen(symbol->text);
|
|
|
|
html_len = len + 1;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
switch (symbol->text[i]) {
|
2019-10-30 21:40:26 +13:00
|
|
|
case '>':
|
|
|
|
case '<':
|
|
|
|
case '"':
|
|
|
|
case '&':
|
2019-11-04 00:51:12 +13:00
|
|
|
case '\'':
|
|
|
|
html_len += 6;
|
2019-10-30 21:40:26 +13:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-06-12 12:25:55 +12:00
|
|
|
if (symbol->output_options & EANUPC_GUARD_WHITESPACE) {
|
|
|
|
html_len += 12; /* Allow for "<" & ">" */
|
|
|
|
}
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2022-07-15 03:01:30 +12:00
|
|
|
html_string = (char *) z_alloca(html_len);
|
2019-09-11 20:14:42 +12:00
|
|
|
|
|
|
|
/* Check for no created vector set */
|
2023-06-12 12:25:55 +12:00
|
|
|
/* E-Mail Christian Schmitz 2019-09-10: reason unknown Ticket #164 */
|
2019-09-11 20:14:42 +12:00
|
|
|
if (symbol->vector == NULL) {
|
2021-07-27 02:29:05 +12:00
|
|
|
strcpy(symbol->errtxt, "681: Vector header NULL");
|
2019-09-11 20:14:42 +12:00
|
|
|
return ZINT_ERROR_INVALID_DATA;
|
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
if (!fm_open(fmp, symbol, "w")) {
|
|
|
|
sprintf(symbol->errtxt, "680: Could not open output file (%d: %.30s)", fmp->err, strerror(fmp->err));
|
|
|
|
return ZINT_ERROR_FILE_ACCESS;
|
2019-09-11 20:14:42 +12:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Start writing the header */
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts("<?xml version=\"1.0\" standalone=\"no\"?>\n"
|
2023-06-12 12:25:55 +12:00
|
|
|
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
|
2023-12-28 08:20:19 +13:00
|
|
|
fmp);
|
|
|
|
fm_printf(fmp, "<svg width=\"%d\" height=\"%d\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n",
|
2021-07-27 02:29:05 +12:00
|
|
|
(int) ceilf(symbol->vector->width), (int) ceilf(symbol->vector->height));
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(" <desc>Zint Generated Symbol</desc>\n", fmp);
|
2023-06-14 05:12:20 +12:00
|
|
|
if ((symbol->output_options & EMBED_VECTOR_FONT) && symbol->vector->strings) {
|
2023-12-28 08:20:19 +13:00
|
|
|
/* Split into `puts()` rather than one very large `printf()` */
|
|
|
|
fm_printf(fmp, " <style>@font-face {font-family:\"%s\"; src:url(data:font/woff2;base64,",
|
|
|
|
upcean ? "OCRB" : "Arimo");
|
|
|
|
fm_puts(upcean ? upcean_woff2 : normal_woff2, fmp);
|
|
|
|
fm_puts(");}</style>\n", fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " <g id=\"barcode\" fill=\"#%s\">\n", fgcolour_string);
|
2019-09-11 20:14:42 +12:00
|
|
|
|
2020-08-03 09:26:39 +12:00
|
|
|
if (bg_alpha != 0) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " <rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%s\"",
|
2021-07-27 02:29:05 +12:00
|
|
|
(int) ceilf(symbol->vector->width), (int) ceilf(symbol->vector->height), bgcolour_string);
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_opacity_close(bg_alpha, bg_alpha_opacity, 1 /*close*/, fmp);
|
2020-08-03 09:26:39 +12:00
|
|
|
}
|
2019-09-11 20:14:42 +12:00
|
|
|
|
2023-06-12 12:25:55 +12:00
|
|
|
if (symbol->vector->rectangles) {
|
|
|
|
int current_colour = 0;
|
|
|
|
rect = symbol->vector->rectangles;
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(" <path d=\"", fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
while (rect) {
|
|
|
|
if (current_colour && rect->colour != current_colour) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putc('"', fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
if (current_colour != -1) {
|
|
|
|
svg_pick_colour(current_colour, colour_code);
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " fill=\"#%s\"", colour_code);
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp);
|
|
|
|
fm_puts(" <path d=\"", fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
|
|
|
current_colour = rect->colour;
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putsf("M", 2, rect->x, fmp);
|
|
|
|
fm_putsf(" ", 2, rect->y, fmp);
|
|
|
|
fm_putsf("h", 2, rect->width, fmp);
|
|
|
|
fm_putsf("v", 2, rect->height, fmp);
|
|
|
|
fm_putsf("h-", 2, rect->width, fmp);
|
|
|
|
fm_puts("Z", fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
rect = rect->next;
|
2020-08-03 09:26:39 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putc('"', fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
if (current_colour != -1) {
|
|
|
|
svg_pick_colour(current_colour, colour_code);
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " fill=\"#%s\"", colour_code);
|
2019-12-19 07:33:18 +13:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp);
|
2019-09-11 20:14:42 +12:00
|
|
|
}
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2023-06-12 12:25:55 +12:00
|
|
|
if (symbol->vector->hexagons) {
|
|
|
|
previous_diameter = radius = half_radius = half_sqrt3_radius = 0.0f;
|
|
|
|
hex = symbol->vector->hexagons;
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(" <path d=\"", fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
while (hex) {
|
|
|
|
if (previous_diameter != hex->diameter) {
|
|
|
|
previous_diameter = hex->diameter;
|
2023-11-20 08:39:54 +13:00
|
|
|
radius = 0.5f * previous_diameter;
|
|
|
|
half_radius = 0.25f * previous_diameter;
|
|
|
|
half_sqrt3_radius = 0.43301270189221932338f * previous_diameter;
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
|
|
|
if ((hex->rotation == 0) || (hex->rotation == 180)) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putsf("M", 2, hex->x, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y + radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x + half_sqrt3_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y + half_radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x + half_sqrt3_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y - half_radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y - radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x - half_sqrt3_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y - half_radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x - half_sqrt3_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y + half_radius, fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
} else {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putsf("M", 2, hex->x - radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x - half_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y + half_sqrt3_radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x + half_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y + half_sqrt3_radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x + radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x + half_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y - half_sqrt3_radius, fmp);
|
|
|
|
fm_putsf("L", 2, hex->x - half_radius, fmp);
|
|
|
|
fm_putsf(" ", 2, hex->y - half_sqrt3_radius, fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putc('Z', fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
hex = hex->next;
|
2020-08-03 09:26:39 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_putc('"', fmp);
|
|
|
|
svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp);
|
2019-09-11 20:14:42 +12:00
|
|
|
}
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2020-10-27 01:21:43 +13:00
|
|
|
previous_diameter = radius = 0.0f;
|
2019-09-11 20:14:42 +12:00
|
|
|
circle = symbol->vector->circles;
|
|
|
|
while (circle) {
|
2020-10-27 01:21:43 +13:00
|
|
|
if (previous_diameter != circle->diameter) {
|
|
|
|
previous_diameter = circle->diameter;
|
2023-11-20 08:39:54 +13:00
|
|
|
radius = 0.5f * previous_diameter;
|
2020-10-27 01:21:43 +13:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(" <circle", fmp);
|
|
|
|
svg_put_fattrib(" cx=\"", 2, circle->x, fmp);
|
|
|
|
svg_put_fattrib(" cy=\"", 2, circle->y, fmp);
|
|
|
|
svg_put_fattrib(" r=\"", circle->width ? 3 : 2, radius, fmp);
|
2021-06-10 22:15:39 +12:00
|
|
|
|
- `zint_symbol->fgcolour` & `bgcolour` buffer lengths extended 10
-> 16 to allow for "C,M,Y,K" comma-separated decimal percentage
strings
- API/CLI/GUI: allow foreground/background colours to be specified
as comma-separated decimal "C,M,Y,K" strings where "C", "M" etc.
are percentages (0-100) (ticket #281, 3rd point)
- output.c: new funcs `out_colour_get_rgb()` & `out_colour_get_cmyk()`
and use in bmp/emf/gif etc.
- PCX: add alpha support
- GUI: fix fg/gbcolor icon background not being reset on zap
- GUI: Rearrange some Appearance tab inputs (Border Type <-> Width,
Show Text <-> Font, Text/Font <-> Printing Scale/Size) to flow
more naturally (hopefully)
- GUI: save button "Save As..." -> "Save..." and add icon
- CLI: add --bgcolor/colour & --fgcolor/colour synonyms
2023-01-30 08:51:11 +13:00
|
|
|
if (circle->colour) { /* Legacy - no longer used */
|
2021-08-23 00:59:01 +12:00
|
|
|
if (circle->width) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " stroke=\"#%s\"", bgcolour_string);
|
|
|
|
svg_put_fattrib(" stroke-width=\"", 3, circle->width, fmp);
|
|
|
|
fm_puts(" fill=\"none\"", fmp);
|
2021-08-23 00:59:01 +12:00
|
|
|
} else {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " fill=\"#%s\"", bgcolour_string);
|
2021-08-23 00:59:01 +12:00
|
|
|
}
|
2023-06-12 12:25:55 +12:00
|
|
|
/* This doesn't work how the user is likely to expect - more work needed! */
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_opacity_close(bg_alpha, bg_alpha_opacity, 1 /*close*/, fmp);
|
2019-09-11 20:14:42 +12:00
|
|
|
} else {
|
2021-08-23 00:59:01 +12:00
|
|
|
if (circle->width) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " stroke=\"#%s\"", fgcolour_string);
|
|
|
|
svg_put_fattrib(" stroke-width=\"", 3, circle->width, fmp);
|
|
|
|
fm_puts(" fill=\"none\"", fmp);
|
2020-08-03 09:26:39 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 1 /*close*/, fmp);
|
2019-09-11 20:14:42 +12:00
|
|
|
}
|
|
|
|
circle = circle->next;
|
|
|
|
}
|
2019-10-30 21:40:26 +13:00
|
|
|
|
2023-11-20 08:39:54 +13:00
|
|
|
bold = (symbol->output_options & BOLD_TEXT) && !upcean;
|
2019-09-11 20:14:42 +12:00
|
|
|
string = symbol->vector->strings;
|
|
|
|
while (string) {
|
2021-10-21 11:05:30 +13:00
|
|
|
const char *const halign = string->halign == 2 ? "end" : string->halign == 1 ? "start" : "middle";
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(" <text", fmp);
|
|
|
|
svg_put_fattrib(" x=\"", 2, string->x, fmp);
|
|
|
|
svg_put_fattrib(" y=\"", 2, string->y, fmp);
|
|
|
|
fm_printf(fmp, " text-anchor=\"%s\"", halign);
|
2023-11-20 08:39:54 +13:00
|
|
|
if (upcean) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " font-family=\"%s, monospace\"", upcean_font_family);
|
2023-11-20 08:39:54 +13:00
|
|
|
} else {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " font-family=\"%s, Arial, sans-serif\"", normal_font_family);
|
2023-11-20 08:39:54 +13:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_fattrib(" font-size=\"", 1, string->fsize, fmp);
|
2020-10-01 00:19:12 +13:00
|
|
|
if (bold) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(" font-weight=\"bold\"", fmp);
|
2020-10-01 00:19:12 +13:00
|
|
|
}
|
2020-08-06 08:23:11 +12:00
|
|
|
if (string->rotation != 0) {
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " transform=\"rotate(%d", string->rotation);
|
|
|
|
fm_putsf(",", 2, string->x, fmp);
|
|
|
|
fm_putsf(",", 2, string->y, fmp);
|
|
|
|
fm_puts(")\"", fmp);
|
2020-08-06 08:23:11 +12:00
|
|
|
}
|
2023-12-28 08:20:19 +13:00
|
|
|
svg_put_opacity_close(fg_alpha, fg_alpha_opacity, 0 /*close*/, fmp);
|
2023-06-12 12:25:55 +12:00
|
|
|
svg_make_html_friendly(string->text, html_string);
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_printf(fmp, " %s\n", html_string);
|
|
|
|
fm_puts(" </text>\n", fmp);
|
2019-09-11 20:14:42 +12:00
|
|
|
string = string->next;
|
|
|
|
}
|
|
|
|
|
2023-12-28 08:20:19 +13:00
|
|
|
fm_puts(" </g>\n"
|
|
|
|
"</svg>\n", fmp);
|
2019-09-11 20:14:42 +12:00
|
|
|
|
2023-12-28 08:20:19 +13:00
|
|
|
if (fm_error(fmp)) {
|
|
|
|
sprintf(symbol->errtxt, "682: Incomplete write to output (%d: %.30s)", fmp->err, strerror(fmp->err));
|
|
|
|
(void) fm_close(fmp, symbol);
|
2022-12-20 05:28:15 +13:00
|
|
|
return ZINT_ERROR_FILE_WRITE;
|
|
|
|
}
|
|
|
|
|
2023-12-28 08:20:19 +13:00
|
|
|
if (!fm_close(fmp, symbol)) {
|
|
|
|
sprintf(symbol->errtxt, "684: Failure on closing output file (%d: %.30s)", fmp->err, strerror(fmp->err));
|
|
|
|
return ZINT_ERROR_FILE_WRITE;
|
2022-12-20 05:28:15 +13:00
|
|
|
}
|
|
|
|
|
2019-09-11 20:14:42 +12:00
|
|
|
return error_number;
|
|
|
|
}
|
2022-07-15 03:01:30 +12:00
|
|
|
|
|
|
|
/* vim: set ts=4 sw=4 et : */
|