2019-09-11 11:14:42 +03:00
/* svg.c - Scalable Vector Graphics */
/*
libzint - the open source barcode library
2022-07-14 18:01:30 +03:00
Copyright ( C ) 2009 - 2022 Robin Stuart < rstuart114 @ gmail . com >
2019-09-11 11:14:42 +03: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-14 18:01:30 +03:00
/* SPDX-License-Identifier: BSD-3-Clause */
2019-09-11 11:14:42 +03:00
2021-06-10 13:15:39 +03:00
# include <errno.h>
2019-09-11 11:14:42 +03:00
# include <locale.h>
# include <math.h>
2022-07-14 18:01:30 +03:00
# include <stdio.h>
2019-09-11 11:14:42 +03:00
# include "common.h"
2020-10-26 15:21:43 +03:00
static void pick_colour ( int colour , char colour_code [ ] ) {
2021-06-10 13:15:39 +03:00
switch ( colour ) {
2022-07-14 18:01:30 +03:00
case 1 : /* Cyan */
2019-12-18 21:33:18 +03:00
strcpy ( colour_code , " 00ffff " ) ;
break ;
2022-07-14 18:01:30 +03:00
case 2 : /* Blue */
2019-12-18 21:33:18 +03:00
strcpy ( colour_code , " 0000ff " ) ;
break ;
2022-07-14 18:01:30 +03:00
case 3 : /* Magenta */
2019-12-18 21:33:18 +03:00
strcpy ( colour_code , " ff00ff " ) ;
break ;
2022-07-14 18:01:30 +03:00
case 4 : /* Red */
2019-12-18 21:33:18 +03:00
strcpy ( colour_code , " ff0000 " ) ;
break ;
2022-07-14 18:01:30 +03:00
case 5 : /* Yellow */
2019-12-18 21:33:18 +03:00
strcpy ( colour_code , " ffff00 " ) ;
break ;
2022-07-14 18:01:30 +03:00
case 6 : /* Green */
2019-12-18 21:33:18 +03:00
strcpy ( colour_code , " 00ff00 " ) ;
break ;
2022-07-14 18:01:30 +03:00
case 8 : /* White */
2020-08-12 14:19:26 +03:00
strcpy ( colour_code , " ffffff " ) ;
break ;
2022-07-14 18:01:30 +03:00
default : /* Black */
2019-12-18 21:33:18 +03:00
strcpy ( colour_code , " 000000 " ) ;
break ;
}
}
2021-06-10 13:15:39 +03:00
static void make_html_friendly ( unsigned char * string , char * html_version ) {
2019-10-30 11:40:26 +03:00
/* Converts text to use HTML entity codes */
2021-06-10 13:15:39 +03:00
int i , len , html_pos ;
2019-10-30 11:40:26 +03:00
html_pos = 0 ;
html_version [ html_pos ] = ' \0 ' ;
2021-06-10 13:15:39 +03:00
len = ( int ) ustrlen ( string ) ;
2019-10-30 11:40:26 +03:00
2021-06-10 13:15:39 +03:00
for ( i = 0 ; i < len ; i + + ) {
switch ( string [ i ] ) {
2019-10-30 11:40:26 +03:00
case ' > ' :
strcat ( html_version , " > " ) ;
2019-10-16 20:42:22 +03:00
html_pos + = 4 ;
2019-10-30 11:40:26 +03:00
break ;
case ' < ' :
strcat ( html_version , " < " ) ;
2019-10-16 20:42:22 +03:00
html_pos + = 4 ;
2019-10-30 11:40:26 +03:00
break ;
case ' & ' :
strcat ( html_version , " & " ) ;
2019-10-16 20:42:22 +03:00
html_pos + = 5 ;
2019-10-30 11:40:26 +03:00
break ;
case ' " ' :
strcat ( html_version , " " " ) ;
html_pos + = 6 ;
break ;
2019-11-03 14:51:12 +03:00
case ' \' ' :
strcat ( html_version , " ' " ) ;
html_pos + = 6 ;
2019-10-30 11:40:26 +03:00
break ;
default :
2019-10-16 20:42:22 +03:00
html_version [ html_pos ] = string [ i ] ;
html_pos + + ;
html_version [ html_pos ] = ' \0 ' ;
break ;
}
2019-10-30 11:40:26 +03:00
}
2019-10-16 20:42:22 +03:00
}
2019-12-19 03:37:55 +03:00
INTERNAL int svg_plot ( struct zint_symbol * symbol ) {
2019-09-11 11:14:42 +03:00
FILE * fsvg ;
int error_number = 0 ;
const char * locale = NULL ;
float ax , ay , bx , by , cx , cy , dx , dy , ex , ey , fx , fy ;
2020-10-26 15:21:43 +03:00
float previous_diameter ;
float radius , half_radius , half_sqrt3_radius ;
2019-11-17 21:30:55 +03:00
int i ;
2020-08-03 00:26:39 +03:00
char fgcolour_string [ 7 ] ;
char bgcolour_string [ 7 ] ;
int bg_alpha = 0xff ;
int fg_alpha = 0xff ;
2020-10-26 15:21:43 +03:00
float fg_alpha_opacity = 0.0f , bg_alpha_opacity = 0.0f ;
2021-10-21 01:05:30 +03:00
const char font_family [ ] = " Helvetica, sans-serif " ;
2020-09-30 14:19:12 +03:00
int bold ;
2019-10-30 11:40:26 +03:00
2019-09-11 11:14:42 +03:00
struct zint_vector_rect * rect ;
struct zint_vector_hexagon * hex ;
struct zint_vector_circle * circle ;
struct zint_vector_string * string ;
2019-10-30 11:40:26 +03:00
2019-12-18 21:33:18 +03:00
char colour_code [ 7 ] ;
2021-06-10 13:15:39 +03:00
int len , html_len ;
2019-12-18 21:33:18 +03:00
2021-06-10 13:15:39 +03:00
char * html_string ;
2019-11-17 21:30:55 +03:00
2020-08-03 00:26:39 +03:00
for ( i = 0 ; i < 6 ; i + + ) {
fgcolour_string [ i ] = symbol - > fgcolour [ i ] ;
bgcolour_string [ i ] = symbol - > bgcolour [ i ] ;
}
fgcolour_string [ 6 ] = ' \0 ' ;
bgcolour_string [ 6 ] = ' \0 ' ;
2021-06-10 13:15:39 +03:00
2020-08-03 00:26:39 +03:00
if ( strlen ( symbol - > fgcolour ) > 6 ) {
fg_alpha = ( 16 * ctoi ( symbol - > fgcolour [ 6 ] ) ) + ctoi ( symbol - > fgcolour [ 7 ] ) ;
2020-10-26 15:21:43 +03:00
if ( fg_alpha ! = 0xff ) {
fg_alpha_opacity = ( float ) ( fg_alpha / 255.0 ) ;
}
2020-08-03 00:26:39 +03:00
}
if ( strlen ( symbol - > bgcolour ) > 6 ) {
bg_alpha = ( 16 * ctoi ( symbol - > bgcolour [ 6 ] ) ) + ctoi ( symbol - > bgcolour [ 7 ] ) ;
2020-10-26 15:21:43 +03:00
if ( bg_alpha ! = 0xff ) {
bg_alpha_opacity = ( float ) ( bg_alpha / 255.0 ) ;
}
2020-08-03 00:26:39 +03:00
}
2019-10-30 11:40:26 +03:00
2021-06-10 13:15:39 +03:00
len = ( int ) ustrlen ( symbol - > text ) ;
html_len = len + 1 ;
for ( i = 0 ; i < len ; i + + ) {
switch ( symbol - > text [ i ] ) {
2019-10-30 11:40:26 +03:00
case ' > ' :
case ' < ' :
case ' " ' :
case ' & ' :
2019-11-03 14:51:12 +03:00
case ' \' ' :
html_len + = 6 ;
2019-10-30 11:40:26 +03:00
break ;
}
}
2022-07-14 18:01:30 +03:00
html_string = ( char * ) z_alloca ( html_len ) ;
2019-09-11 11:14:42 +03:00
/* Check for no created vector set */
/* E-Mail Christian Schmitz 2019-09-10: reason unknown Ticket #164*/
if ( symbol - > vector = = NULL ) {
2021-07-26 17:29:05 +03:00
strcpy ( symbol - > errtxt , " 681: Vector header NULL " ) ;
2019-09-11 11:14:42 +03:00
return ZINT_ERROR_INVALID_DATA ;
}
if ( symbol - > output_options & BARCODE_STDOUT ) {
fsvg = stdout ;
} else {
2021-07-26 17:29:05 +03:00
if ( ! ( fsvg = fopen ( symbol - > outfile , " w " ) ) ) {
sprintf ( symbol - > errtxt , " 680: Could not open output file (%d: %.30s) " , errno , strerror ( errno ) ) ;
return ZINT_ERROR_FILE_ACCESS ;
}
2019-09-11 11:14:42 +03:00
}
locale = setlocale ( LC_ALL , " C " ) ;
/* Start writing the header */
fprintf ( fsvg , " <?xml version= \" 1.0 \" standalone= \" no \" ?> \n " ) ;
fprintf ( fsvg , " <!DOCTYPE svg PUBLIC \" -//W3C//DTD SVG 1.1//EN \" \n " ) ;
fprintf ( fsvg , " \" http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd \" > \n " ) ;
2021-06-10 13:15:39 +03:00
fprintf ( fsvg , " <svg width= \" %d \" height= \" %d \" version= \" 1.1 \" \n " ,
2021-07-26 17:29:05 +03:00
( int ) ceilf ( symbol - > vector - > width ) , ( int ) ceilf ( symbol - > vector - > height ) ) ;
2019-09-11 11:14:42 +03:00
fprintf ( fsvg , " xmlns= \" http://www.w3.org/2000/svg \" > \n " ) ;
fprintf ( fsvg , " <desc>Zint Generated Symbol \n " ) ;
fprintf ( fsvg , " </desc> \n " ) ;
2020-08-03 00:26:39 +03:00
fprintf ( fsvg , " \n <g id= \" barcode \" fill= \" #%s \" > \n " , fgcolour_string ) ;
2019-09-11 11:14:42 +03:00
2020-08-03 00:26:39 +03:00
if ( bg_alpha ! = 0 ) {
2021-06-10 13:15:39 +03:00
fprintf ( fsvg , " <rect x= \" 0 \" y= \" 0 \" width= \" %d \" height= \" %d \" fill= \" #%s \" " ,
2021-07-26 17:29:05 +03:00
( int ) ceilf ( symbol - > vector - > width ) , ( int ) ceilf ( symbol - > vector - > height ) , bgcolour_string ) ;
2020-08-03 00:26:39 +03:00
if ( bg_alpha ! = 0xff ) {
2020-10-26 15:21:43 +03:00
fprintf ( fsvg , " opacity= \" %.3f \" " , bg_alpha_opacity ) ;
2020-08-03 00:26:39 +03:00
}
fprintf ( fsvg , " /> \n " ) ;
}
2019-09-11 11:14:42 +03:00
rect = symbol - > vector - > rectangles ;
while ( rect ) {
2021-06-10 13:15:39 +03:00
fprintf ( fsvg , " <rect x= \" %.2f \" y= \" %.2f \" width= \" %.2f \" height= \" %.2f \" " ,
rect - > x , rect - > y , rect - > width , rect - > height ) ;
2020-08-03 00:26:39 +03:00
if ( rect - > colour ! = - 1 ) {
2019-12-18 21:33:18 +03:00
pick_colour ( rect - > colour , colour_code ) ;
2020-08-03 00:26:39 +03:00
fprintf ( fsvg , " fill= \" #%s \" " , colour_code ) ;
}
if ( fg_alpha ! = 0xff ) {
2020-10-26 15:21:43 +03:00
fprintf ( fsvg , " opacity= \" %.3f \" " , fg_alpha_opacity ) ;
2019-12-18 21:33:18 +03:00
}
2020-08-03 00:26:39 +03:00
fprintf ( fsvg , " /> \n " ) ;
2019-09-11 11:14:42 +03:00
rect = rect - > next ;
}
2019-10-30 11:40:26 +03:00
2020-10-26 15:21:43 +03:00
previous_diameter = radius = half_radius = half_sqrt3_radius = 0.0f ;
2019-09-11 11:14:42 +03:00
hex = symbol - > vector - > hexagons ;
while ( hex ) {
2020-10-26 15:21:43 +03:00
if ( previous_diameter ! = hex - > diameter ) {
previous_diameter = hex - > diameter ;
radius = ( float ) ( 0.5 * previous_diameter ) ;
half_radius = ( float ) ( 0.25 * previous_diameter ) ;
half_sqrt3_radius = ( float ) ( 0.43301270189221932338 * previous_diameter ) ;
}
2020-08-05 23:23:11 +03:00
if ( ( hex - > rotation = = 0 ) | | ( hex - > rotation = = 180 ) ) {
2020-10-26 15:21:43 +03:00
ay = hex - > y + radius ;
by = hex - > y + half_radius ;
cy = hex - > y - half_radius ;
dy = hex - > y - radius ;
ey = hex - > y - half_radius ;
fy = hex - > y + half_radius ;
2020-08-05 23:23:11 +03:00
ax = hex - > x ;
2020-10-26 15:21:43 +03:00
bx = hex - > x + half_sqrt3_radius ;
cx = hex - > x + half_sqrt3_radius ;
2020-08-05 23:23:11 +03:00
dx = hex - > x ;
2020-10-26 15:21:43 +03:00
ex = hex - > x - half_sqrt3_radius ;
fx = hex - > x - half_sqrt3_radius ;
2020-08-05 23:23:11 +03:00
} else {
ay = hex - > y ;
2020-10-26 15:21:43 +03:00
by = hex - > y + half_sqrt3_radius ;
cy = hex - > y + half_sqrt3_radius ;
2020-08-05 23:23:11 +03:00
dy = hex - > y ;
2020-10-26 15:21:43 +03:00
ey = hex - > y - half_sqrt3_radius ;
fy = hex - > y - half_sqrt3_radius ;
ax = hex - > x - radius ;
bx = hex - > x - half_radius ;
cx = hex - > x + half_radius ;
dx = hex - > x + radius ;
ex = hex - > x + half_radius ;
fx = hex - > x - half_radius ;
2020-08-05 23:23:11 +03:00
}
2021-06-10 13:15:39 +03:00
fprintf ( fsvg , " <path d= \" M %.2f %.2f L %.2f %.2f L %.2f %.2f L %.2f %.2f L %.2f %.2f L %.2f %.2f Z \" " ,
ax , ay , bx , by , cx , cy , dx , dy , ex , ey , fx , fy ) ;
2020-08-03 00:26:39 +03:00
if ( fg_alpha ! = 0xff ) {
2020-10-26 15:21:43 +03:00
fprintf ( fsvg , " opacity= \" %.3f \" " , fg_alpha_opacity ) ;
2020-08-03 00:26:39 +03:00
}
fprintf ( fsvg , " /> \n " ) ;
2019-09-11 11:14:42 +03:00
hex = hex - > next ;
}
2019-10-30 11:40:26 +03:00
2020-10-26 15:21:43 +03:00
previous_diameter = radius = 0.0f ;
2019-09-11 11:14:42 +03:00
circle = symbol - > vector - > circles ;
while ( circle ) {
2020-10-26 15:21:43 +03:00
if ( previous_diameter ! = circle - > diameter ) {
previous_diameter = circle - > diameter ;
radius = ( float ) ( 0.5 * previous_diameter ) ;
}
2021-08-22 15:59:01 +03:00
fprintf ( fsvg , " <circle cx= \" %.2f \" cy= \" %.2f \" r= \" %.*f \" " ,
circle - > x , circle - > y , circle - > width ? 3 : 2 , radius ) ;
2021-06-10 13:15:39 +03:00
2019-09-11 11:14:42 +03:00
if ( circle - > colour ) {
2021-08-22 15:59:01 +03:00
if ( circle - > width ) {
fprintf ( fsvg , " stroke= \" #%s \" stroke-width= \" %.3f \" fill= \" none \" " , bgcolour_string , circle - > width ) ;
} else {
fprintf ( fsvg , " fill= \" #%s \" " , bgcolour_string ) ;
}
2020-08-03 00:26:39 +03:00
if ( bg_alpha ! = 0xff ) {
2022-07-14 18:01:30 +03:00
/* This doesn't work how the user is likely to expect - more work needed! */
2020-10-26 15:21:43 +03:00
fprintf ( fsvg , " opacity= \" %.3f \" " , bg_alpha_opacity ) ;
2020-08-03 00:26:39 +03:00
}
2019-09-11 11:14:42 +03:00
} else {
2021-08-22 15:59:01 +03:00
if ( circle - > width ) {
fprintf ( fsvg , " stroke= \" #%s \" stroke-width= \" %.3f \" fill= \" none \" " , fgcolour_string , circle - > width ) ;
}
2020-08-03 00:26:39 +03:00
if ( fg_alpha ! = 0xff ) {
2020-10-26 15:21:43 +03:00
fprintf ( fsvg , " opacity= \" %.3f \" " , fg_alpha_opacity ) ;
2020-08-03 00:26:39 +03:00
}
2019-09-11 11:14:42 +03:00
}
2020-08-03 00:26:39 +03:00
fprintf ( fsvg , " /> \n " ) ;
2019-09-11 11:14:42 +03:00
circle = circle - > next ;
}
2019-10-30 11:40:26 +03:00
2021-06-10 13:15:39 +03:00
bold = ( symbol - > output_options & BOLD_TEXT )
& & ( ! is_extendable ( symbol - > symbology ) | | ( symbol - > output_options & SMALL_TEXT ) ) ;
2019-09-11 11:14:42 +03:00
string = symbol - > vector - > strings ;
while ( string ) {
2021-10-21 01:05:30 +03:00
const char * const halign = string - > halign = = 2 ? " end " : string - > halign = = 1 ? " start " : " middle " ;
2020-09-30 14:19:12 +03:00
fprintf ( fsvg , " <text x= \" %.2f \" y= \" %.2f \" text-anchor= \" %s \" \n " , string - > x , string - > y , halign ) ;
fprintf ( fsvg , " font-family= \" %s \" font-size= \" %.1f \" " , font_family , string - > fsize ) ;
if ( bold ) {
fprintf ( fsvg , " font-weight= \" bold \" " ) ;
}
2020-08-03 00:26:39 +03:00
if ( fg_alpha ! = 0xff ) {
2020-10-26 15:21:43 +03:00
fprintf ( fsvg , " opacity= \" %.3f \" " , fg_alpha_opacity ) ;
2020-08-03 00:26:39 +03:00
}
2020-08-05 23:23:11 +03:00
if ( string - > rotation ! = 0 ) {
fprintf ( fsvg , " transform= \" rotate(%d,%.2f,%.2f) \" " , string - > rotation , string - > x , string - > y ) ;
}
2020-08-03 00:26:39 +03:00
fprintf ( fsvg , " > \n " ) ;
2019-10-16 20:42:22 +03:00
make_html_friendly ( string - > text , html_string ) ;
fprintf ( fsvg , " %s \n " , html_string ) ;
2019-09-11 11:14:42 +03:00
fprintf ( fsvg , " </text> \n " ) ;
string = string - > next ;
}
fprintf ( fsvg , " </g> \n " ) ;
fprintf ( fsvg , " </svg> \n " ) ;
if ( symbol - > output_options & BARCODE_STDOUT ) {
fflush ( fsvg ) ;
} else {
fclose ( fsvg ) ;
}
if ( locale )
setlocale ( LC_ALL , locale ) ;
return error_number ;
}
2022-07-14 18:01:30 +03:00
/* vim: set ts=4 sw=4 et : */