#include <assert.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include "common.h"
#include "filemem.h"
#include "output.h"
static void ps_put_colour(const int is_rgb, const int colour, struct filemem *const fmp) {
const int idx = colour >= 1 && colour <= 8 ? colour - 1 : 6 ;
if (is_rgb) {
static const char ps_rgbs[8][6] = {
"0 1 1",
"0 0 1",
"1 0 1",
"1 0 0",
"1 1 0",
"0 1 0",
"0 0 0",
"1 1 1",
};
zint_fm_puts(ps_rgbs[idx], fmp);
zint_fm_puts(" setrgbcolor\n", fmp);
} else {
static const char ps_cmyks[8][8] = {
"1 0 0 0",
"1 1 0 0",
"0 1 0 0",
"0 1 1 0",
"0 0 1 0",
"1 0 1 0",
"0 0 0 1",
"0 0 0 0",
};
zint_fm_puts(ps_cmyks[idx], fmp);
zint_fm_puts(" setcmykcolor\n", fmp);
}
}
static void ps_convert(const unsigned char *string, unsigned char *ps_string) {
const unsigned char *s;
unsigned char *p = ps_string;
for (s = string; *s; s++) {
switch (*s) {
case '(':
case ')':
case '\\':
*p++ = '\\';
*p++ = *s;
break;
case 0xC2:
*p++ = *++s;
break;
case 0xC3:
*p++ = *++s + 0x40;
break;
default:
if (*s < 0x80) {
*p++ = *s;
}
break;
}
}
*p = '\0';
}
#ifdef ZINT_TEST
INTERNAL void zint_test_ps_convert(const unsigned char *string, unsigned char *ps_string) {
ps_convert(string, ps_string);
}
#endif
static void ps_put_rgbcolor(const float red, const float green, const float blue,
struct filemem *const fmp) {
zint_fm_putsf("", 2, red, fmp);
zint_fm_putsf(" ", 2, green, fmp);
zint_fm_putsf(" ", 2, blue, fmp);
zint_fm_puts(" setrgbcolor\n", fmp);
}
static void ps_put_cmykcolor(const float cyan, const float magenta, const float yellow, const float black,
struct filemem *const fmp) {
zint_fm_putsf("", 2, cyan, fmp);
zint_fm_putsf(" ", 2, magenta, fmp);
zint_fm_putsf(" ", 2, yellow, fmp);
zint_fm_putsf(" ", 2, black, fmp);
zint_fm_puts(" setcmykcolor\n", fmp);
}
static void ps_put_rect(const struct zint_symbol *symbol, const struct zint_vector_rect *rect, const int type,
struct filemem *const fmp) {
if (type == 0 || type == 1) {
zint_fm_putsf("", 2, rect->height, fmp);
zint_fm_putsf(" ", 2, (symbol->vector->height - rect->y) - rect->height, fmp);
}
zint_fm_putsf(type == 0 ? " " : type == 1 ? " I " : type == 2 ? "I " : "", 2, rect->x, fmp);
zint_fm_putsf(" ", 2, rect->width, fmp);
zint_fm_puts(" R\n", fmp);
}
static void ps_put_circle(const struct zint_symbol *symbol, const struct zint_vector_circle *circle,
const float radius, const int type, struct filemem *const fmp) {
if (circle->width) {
zint_fm_putsf("", 2, circle->x, fmp);
zint_fm_putsf(" ", 2, symbol->vector->height - circle->y, fmp);
zint_fm_putsf(" ", 4, radius, fmp);
zint_fm_putsf(" ", 4, circle->width, fmp);
zint_fm_puts(" C\n", fmp);
} else {
if (type == 0 || type == 1) {
zint_fm_putsf("", 2, symbol->vector->height - circle->y, fmp);
zint_fm_putsf(" ", 4, radius, fmp);
}
zint_fm_putsf(type == 0 ? " " : type == 1 ? " I " : type == 2 ? "I " : "", 2, circle->x, fmp);
zint_fm_puts(" D\n", fmp);
}
}
static int ps_count_rectangles(const struct zint_symbol *symbol) {
int rectangles = 0;
const struct zint_vector_rect *rect;
for (rect = symbol->vector->rectangles; rect; rect = rect->next) {
rectangles++;
}
return rectangles;
}
INTERNAL int zint_ps_plot(struct zint_symbol *symbol) {
struct filemem fm;
struct filemem *const fmp = &fm;
unsigned char fgred, fggrn, fgblu, bgred, bggrn, bgblu, bgalpha;
int fgcyan, fgmagenta, fgyellow, fgblack, bgcyan, bgmagenta, bgyellow, bgblack;
float red_ink = 0.0f, green_ink = 0.0f, blue_ink = 0.0f;
float red_paper = 0.0f, green_paper = 0.0f, blue_paper = 0.0f;
float cyan_ink = 0.0f, magenta_ink = 0.0f, yellow_ink = 0.0f, black_ink = 0.0f;
float cyan_paper = 0.0f, magenta_paper = 0.0f, yellow_paper = 0.0f, black_paper = 0.0f;
float previous_diameter;
float radius;
int colour_rect_flag;
int type_latch;
int draw_background = 1;
struct zint_vector_rect *rect;
struct zint_vector_hexagon *hex;
struct zint_vector_circle *circle;
struct zint_vector_string *string;
int i;
int ps_len = 0;
int iso_latin1 = 0;
int have_circles_with_width = 0, have_circles_without_width = 0;
const int is_upcean = z_is_upcean(symbol->symbology);
const int is_rgb = (symbol->output_options & CMYK_COLOUR) == 0;
if (symbol->vector == NULL) {
return z_errtxt(ZINT_ERROR_INVALID_DATA, symbol, 646, "Vector header NULL");
}
if (!zint_fm_open(fmp, symbol, "w")) {
return ZEXT z_errtxtf(ZINT_ERROR_FILE_ACCESS, symbol, 645, "Could not open EPS output file (%1$d: %2$s)",
fmp->err, strerror(fmp->err));
}
if (is_rgb) {
(void) zint_out_colour_get_rgb(symbol->fgcolour, &fgred, &fggrn, &fgblu, NULL );
red_ink = fgred / 255.0f;
green_ink = fggrn / 255.0f;
blue_ink = fgblu / 255.0f;
(void) zint_out_colour_get_rgb(symbol->bgcolour, &bgred, &bggrn, &bgblu, &bgalpha);
red_paper = bgred / 255.0f;
green_paper = bggrn / 255.0f;
blue_paper = bgblu / 255.0f;
} else {
(void) zint_out_colour_get_cmyk(symbol->fgcolour, &fgcyan, &fgmagenta, &fgyellow, &fgblack,
NULL );
cyan_ink = fgcyan / 100.0f;
magenta_ink = fgmagenta / 100.0f;
yellow_ink = fgyellow / 100.0f;
black_ink = fgblack / 100.0f;
(void) zint_out_colour_get_cmyk(symbol->bgcolour, &bgcyan, &bgmagenta, &bgyellow, &bgblack, &bgalpha);
cyan_paper = bgcyan / 100.0f;
magenta_paper = bgmagenta / 100.0f;
yellow_paper = bgyellow / 100.0f;
black_paper = bgblack / 100.0f;
}
if (bgalpha == 0) {
draw_background = 0;
}
for (i = 0; i < symbol->text_length; i++) {
switch (symbol->text[i]) {
case '(':
case ')':
case '\\':
ps_len += 2;
break;
default:
if (!iso_latin1 && symbol->text[i] >= 0x80) {
iso_latin1 = 1;
}
ps_len++;
break;
}
}
for (circle = symbol->vector->circles; circle; circle = circle->next) {
if (circle->width) {
have_circles_with_width = 1;
if (have_circles_without_width) {
break;
}
} else {
have_circles_without_width = 1;
if (have_circles_with_width) {
break;
}
}
}
zint_fm_puts("%!PS-Adobe-3.0 EPSF-3.0\n"
"%%Creator: Zint ", fmp);
#if ZINT_VERSION_BUILD
zint_fm_printf(fmp, "%d.%d.%d.%d\n", ZINT_VERSION_MAJOR, ZINT_VERSION_MINOR, ZINT_VERSION_RELEASE,
ZINT_VERSION_BUILD);
#else
zint_fm_printf(fmp, "%d.%d.%d\n", ZINT_VERSION_MAJOR, ZINT_VERSION_MINOR, ZINT_VERSION_RELEASE);
#endif
zint_fm_puts("%%Title: Zint Generated Symbol\n"
"%%Pages: 0\n"
"%%BoundingBox: 0 0 ", fmp);
zint_fm_printf(fmp, "%d %d\n", (int) ceilf(symbol->vector->width), (int) ceilf(symbol->vector->height));
zint_fm_puts("%%EndComments\n", fmp);
if (have_circles_without_width) {
zint_fm_puts("/D { newpath 3 1 roll 0 360 arc fill } bind def\n", fmp);
}
if (have_circles_with_width) {
zint_fm_puts("/C { newpath 4 1 roll 3 copy 0 360 arc closepath 4 -1 roll add 360 0 arcn closepath fill }"
" bind def\n", fmp);
}
if (symbol->vector->hexagons) {
if (symbol->vector->hexagons->rotation == 0 || symbol->vector->hexagons->rotation == 180) {
zint_fm_puts("/H { newpath moveto 2 copy exch neg exch rmoveto 2 index neg 0 exch rlineto"
" 2 copy neg rlineto 2 copy rlineto 3 -1 roll 0 exch rlineto exch neg exch rlineto"
" closepath fill } bind def\n", fmp);
} else {
zint_fm_puts("/H { newpath moveto 2 copy neg exch neg rmoveto 2 index 0 rlineto 2 copy exch rlineto"
" 2 copy neg exch rlineto 3 -1 roll neg 0 rlineto neg exch neg rlineto"
" closepath fill } bind def\n", fmp);
}
zint_fm_puts("/J { 3 copy } bind def\n", fmp);
}
if (symbol->vector->rectangles || draw_background) {
zint_fm_puts("/R { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto"
" closepath fill } bind def\n", fmp);
}
if (symbol->vector->rectangles || have_circles_without_width) {
zint_fm_puts("/I { 2 copy } bind def\n", fmp);
}
if (draw_background) {
if (is_rgb) {
ps_put_rgbcolor(red_paper, green_paper, blue_paper, fmp);
} else {
ps_put_cmykcolor(cyan_paper, magenta_paper, yellow_paper, black_paper, fmp);
}
zint_fm_putsf("", 2, symbol->vector->height, fmp);
zint_fm_putsf(" 0 0 ", 2, symbol->vector->width, fmp);
zint_fm_puts(" R\n", fmp);
}
if (symbol->symbology != BARCODE_ULTRA) {
if (is_rgb) {
ps_put_rgbcolor(red_ink, green_ink, blue_ink, fmp);
} else {
ps_put_cmykcolor(cyan_ink, magenta_ink, yellow_ink, black_ink, fmp);
}
}
if (symbol->symbology == BARCODE_ULTRA) {
const int rect_cnt = ps_count_rectangles(symbol);
struct zint_vector_rect **ultra_rects
= (struct zint_vector_rect **) z_alloca(sizeof(struct zint_vector_rect *) * (rect_cnt ? rect_cnt : 1));
int u_i = 0;
for (i = 0; i <= 8; i++) {
for (rect = symbol->vector->rectangles; rect; rect = rect->next) {
if ((i == 0 && rect->colour == -1) || rect->colour == i) {
assert(u_i < rect_cnt);
ultra_rects[u_i++] = rect;
}
}
}
assert(u_i == ps_count_rectangles(symbol));
colour_rect_flag = 0;
type_latch = 0;
for (i = 0; i < u_i; i++) {
rect = ultra_rects[i];
if (i == 0 || rect->colour != ultra_rects[i - 1]->colour) {
if (rect->colour == -1) {
if (colour_rect_flag == 0) {
if (is_rgb) {
ps_put_rgbcolor(red_ink, green_ink, blue_ink, fmp);
} else {
ps_put_cmykcolor(cyan_ink, magenta_ink, yellow_ink, black_ink, fmp);
}
colour_rect_flag = 1;
}
} else {
ps_put_colour(is_rgb, rect->colour, fmp);
}
}
if (i + 1 < u_i && rect->height == ultra_rects[i + 1]->height && rect->y == ultra_rects[i + 1]->y) {
ps_put_rect(symbol, rect, type_latch ? 2 : 1, fmp);
type_latch = 1;
} else {
ps_put_rect(symbol, rect, type_latch ? 3 : 0, fmp);
type_latch = 0;
}
}
} else {
type_latch = 0;
for (rect = symbol->vector->rectangles; rect; rect = rect->next) {
if (rect->next && rect->height == rect->next->height && rect->y == rect->next->y) {
ps_put_rect(symbol, rect, type_latch ? 2 : 1, fmp);
type_latch = 1;
} else {
ps_put_rect(symbol, rect, type_latch ? 3 : 0, fmp);
type_latch = 0;
}
}
}
previous_diameter = 0.0f;
for (hex = symbol->vector->hexagons; hex; hex = hex->next) {
float hy = symbol->vector->height - hex->y;
if (previous_diameter != hex->diameter) {
previous_diameter = hex->diameter;
zint_fm_putsf("", 4, 0.5f * previous_diameter , fmp);
zint_fm_putsf(" ", 4, 0.43301270189221932338f * previous_diameter , fmp);
zint_fm_putsf(" ", 4, 0.25f * previous_diameter , fmp);
zint_fm_putc('\n', fmp);
}
if (hex->next) {
zint_fm_putsf("J ", 2, hex->x, fmp);
} else {
zint_fm_putsf("", 2, hex->x, fmp);
}
zint_fm_putsf(" ", 2, hy, fmp);
zint_fm_puts(" H\n", fmp);
}
previous_diameter = radius = 0.0f;
type_latch = 0;
for (circle = symbol->vector->circles; circle; circle = circle->next) {
if (previous_diameter != circle->diameter - circle->width) {
previous_diameter = circle->diameter - circle->width;
radius = 0.5f * previous_diameter;
}
if (circle->colour) {
if (is_rgb) {
ps_put_rgbcolor(red_paper, green_paper, blue_paper, fmp);
} else {
ps_put_cmykcolor(cyan_paper, magenta_paper, yellow_paper, black_paper, fmp);
}
ps_put_circle(symbol, circle, radius, 0 , fmp);
if (circle->next) {
if (is_rgb) {
ps_put_rgbcolor(red_ink, green_ink, blue_ink, fmp);
} else {
ps_put_cmykcolor(cyan_ink, magenta_ink, yellow_ink, black_ink, fmp);
}
}
} else {
if (circle->next && circle->y == circle->next->y && circle->diameter == circle->next->diameter) {
ps_put_circle(symbol, circle, radius, type_latch ? 2 : 1, fmp);
type_latch = 1;
} else {
ps_put_circle(symbol, circle, radius, type_latch ? 3 : 0, fmp);
type_latch = 0;
}
}
}
string = symbol->vector->strings;
if (string) {
float previous_fsize = 0.0f;
const char *font;
unsigned char *ps_string = (unsigned char *) z_alloca(ps_len + 1);
if ((symbol->output_options & BOLD_TEXT) && !is_upcean) {
font = "Helvetica-Bold";
} else {
font = "Helvetica";
}
if (iso_latin1) {
zint_fm_printf(fmp, "/%s findfont\n", font);
zint_fm_puts("dup length dict begin\n"
"{1 index /FID ne {def} {pop pop} ifelse} forall\n"
"/Encoding ISOLatin1Encoding def\n"
"currentdict\n"
"end\n"
"/Helvetica-ISOLatin1 exch definefont pop\n", fmp);
font = "Helvetica-ISOLatin1";
}
do {
ps_convert(string->text, ps_string);
if (string->fsize != previous_fsize) {
zint_fm_printf(fmp, "/%s findfont", font);
zint_fm_putsf( " ", 2, is_upcean ? string->fsize * 1.07f : string->fsize, fmp);
zint_fm_puts(" scalefont setfont\n", fmp);
previous_fsize = string->fsize;
}
if (is_upcean && string->halign == 1 && string->text[0] == '<') {
const float gws_left_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale;
zint_fm_putsf(" ", 2, string->x + gws_left_fudge, fmp);
} else if (is_upcean && string->halign == 2 && string->text[0] == '>') {
const float gws_right_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale;
zint_fm_putsf(" ", 2, string->x - gws_right_fudge, fmp);
} else {
zint_fm_putsf(" ", 2, string->x, fmp);
}
zint_fm_putsf(" ", 2, symbol->vector->height - string->y, fmp);
zint_fm_puts(" moveto\n", fmp);
if (string->rotation != 0) {
zint_fm_puts(" gsave\n", fmp);
zint_fm_printf(fmp, " %d rotate\n", 360 - string->rotation);
}
if (string->halign == 0 || string->halign == 2) {
zint_fm_printf(fmp, " (%s) stringwidth pop"
" %s 0 rmoveto\n", ps_string, string->halign == 2 ? "neg" : "-2 div");
}
zint_fm_printf(fmp, " (%s) show\n", ps_string);
if (string->rotation != 0) {
zint_fm_puts(" grestore\n", fmp);
}
string = string->next;
} while (string);
}
if (zint_fm_error(fmp)) {
ZEXT z_errtxtf(0, symbol, 647, "Incomplete write of EPS output (%1$d: %2$s)", fmp->err, strerror(fmp->err));
(void) zint_fm_close(fmp, symbol);
return ZINT_ERROR_FILE_WRITE;
}
if (!zint_fm_close(fmp, symbol)) {
return ZEXT z_errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 649, "Failure on closing EPS output file (%1$d: %2$s)",
fmp->err, strerror(fmp->err));
}
return 0;
}