comparison mupdf-source/thirdparty/zint/backend/ps.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 /* ps.c - Post Script output */
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 <assert.h>
34 #include <errno.h>
35 #include <math.h>
36 #include <stdio.h>
37 #include "common.h"
38 #include "filemem.h"
39 #include "output.h"
40
41 /* Output Ultracode rectangle colour as PostScript setrgbcolor/setcmykcolor */
42 static void ps_put_colour(const int is_rgb, const int colour, struct filemem *const fmp) {
43 const int idx = colour >= 1 && colour <= 8 ? colour - 1 : 6 /*black*/;
44 if (is_rgb) {
45 /* Use RGB colour space */
46 static const char ps_rgbs[8][6] = {
47 "0 1 1", /* 0: Cyan (1) */
48 "0 0 1", /* 1: Blue (2) */
49 "1 0 1", /* 2: Magenta (3) */
50 "1 0 0", /* 3: Red (4) */
51 "1 1 0", /* 4: Yellow (5) */
52 "0 1 0", /* 5: Green (6) */
53 "0 0 0", /* 6: Black (7) */
54 "1 1 1", /* 7: White (8) */
55 };
56 fm_puts(ps_rgbs[idx], fmp);
57 fm_puts(" setrgbcolor\n", fmp);
58 } else {
59 static const char ps_cmyks[8][8] = {
60 "1 0 0 0", /* 0: Cyan (1) */
61 "1 1 0 0", /* 1: Blue (2) */
62 "0 1 0 0", /* 2: Magenta (3) */
63 "0 1 1 0", /* 3: Red (4) */
64 "0 0 1 0", /* 4: Yellow (5) */
65 "1 0 1 0", /* 5: Green (6) */
66 "0 0 0 1", /* 6: Black (7) */
67 "0 0 0 0", /* 7: White (8) */
68 };
69 fm_puts(ps_cmyks[idx], fmp);
70 fm_puts(" setcmykcolor\n", fmp);
71 }
72 }
73
74 /* Escape special PostScript chars. Assumes valid UTF-8-encoded ISO/IEC 8859-1 */
75 static void ps_convert(const unsigned char *string, unsigned char *ps_string) {
76 const unsigned char *s;
77 unsigned char *p = ps_string;
78
79 for (s = string; *s; s++) {
80 switch (*s) {
81 case '(':
82 case ')':
83 case '\\':
84 *p++ = '\\';
85 *p++ = *s;
86 break;
87 case 0xC2:
88 *p++ = *++s; /* C2 80-BF -> 80-BF */
89 break;
90 case 0xC3:
91 *p++ = *++s + 0x40; /* C3 80-BF -> C0-FF */
92 break;
93 default:
94 if (*s < 0x80) { /* ASCII - all other Unicode points > U+00FF ignored */
95 *p++ = *s;
96 }
97 break;
98
99 }
100 }
101 *p = '\0';
102 }
103
104 #ifdef ZINT_TEST /* Wrapper for direct testing */
105 INTERNAL void ps_convert_test(const unsigned char *string, unsigned char *ps_string) {
106 ps_convert(string, ps_string);
107 }
108 #endif
109
110 /* Helper to output RGB colour */
111 static void ps_put_rgbcolor(const float red, const float green, const float blue,
112 struct filemem *const fmp) {
113 fm_putsf("", 2, red, fmp);
114 fm_putsf(" ", 2, green, fmp);
115 fm_putsf(" ", 2, blue, fmp);
116 fm_puts(" setrgbcolor\n", fmp);
117 }
118
119 /* Helper to output CMYK colour */
120 static void ps_put_cmykcolor(const float cyan, const float magenta, const float yellow, const float black,
121 struct filemem *const fmp) {
122 fm_putsf("", 2, cyan, fmp);
123 fm_putsf(" ", 2, magenta, fmp);
124 fm_putsf(" ", 2, yellow, fmp);
125 fm_putsf(" ", 2, black, fmp);
126 fm_puts(" setcmykcolor\n", fmp);
127 }
128
129 /* Helper to output rectangle */
130 static void ps_put_rect(const struct zint_symbol *symbol, const struct zint_vector_rect *rect, const int type,
131 struct filemem *const fmp) {
132 if (type == 0 || type == 1) {
133 fm_putsf("", 2, rect->height, fmp);
134 fm_putsf(" ", 2, (symbol->vector->height - rect->y) - rect->height, fmp);
135 }
136 fm_putsf(type == 0 ? " " : type == 1 ? " I " : type == 2 ? "I " : "", 2, rect->x, fmp);
137 fm_putsf(" ", 2, rect->width, fmp);
138 fm_puts(" R\n", fmp);
139 }
140
141 /* Helper to output circle/disc */
142 static void ps_put_circle(const struct zint_symbol *symbol, const struct zint_vector_circle *circle,
143 const float radius, const int type, struct filemem *const fmp) {
144 if (circle->width) {
145 fm_putsf("", 2, circle->x, fmp);
146 fm_putsf(" ", 2, symbol->vector->height - circle->y, fmp);
147 fm_putsf(" ", 4, radius, fmp);
148 fm_putsf(" ", 4, circle->width, fmp);
149 fm_puts(" C\n", fmp);
150 } else {
151 if (type == 0 || type == 1) {
152 fm_putsf("", 2, symbol->vector->height - circle->y, fmp);
153 fm_putsf(" ", 4, radius, fmp);
154 }
155 fm_putsf(type == 0 ? " " : type == 1 ? " I " : type == 2 ? "I " : "", 2, circle->x, fmp);
156 fm_puts(" D\n", fmp);
157 }
158 }
159
160 /* Helper to count rectangles */
161 static int ps_count_rectangles(const struct zint_symbol *symbol) {
162 int rectangles = 0;
163 const struct zint_vector_rect *rect;
164
165 for (rect = symbol->vector->rectangles; rect; rect = rect->next) {
166 rectangles++;
167 }
168
169 return rectangles;
170 }
171
172 INTERNAL int ps_plot(struct zint_symbol *symbol) {
173 struct filemem fm;
174 struct filemem *const fmp = &fm;
175 unsigned char fgred, fggrn, fgblu, bgred, bggrn, bgblu, bgalpha;
176 int fgcyan, fgmagenta, fgyellow, fgblack, bgcyan, bgmagenta, bgyellow, bgblack;
177 float red_ink = 0.0f, green_ink = 0.0f, blue_ink = 0.0f; /* Suppress `-Wmaybe-uninitialized` */
178 float red_paper = 0.0f, green_paper = 0.0f, blue_paper = 0.0f;
179 float cyan_ink = 0.0f, magenta_ink = 0.0f, yellow_ink = 0.0f, black_ink = 0.0f;
180 float cyan_paper = 0.0f, magenta_paper = 0.0f, yellow_paper = 0.0f, black_paper = 0.0f;
181 float previous_diameter;
182 float radius;
183 int colour_rect_flag;
184 int type_latch;
185 int draw_background = 1;
186 struct zint_vector_rect *rect;
187 struct zint_vector_hexagon *hex;
188 struct zint_vector_circle *circle;
189 struct zint_vector_string *string;
190 int i, len;
191 int ps_len = 0;
192 int iso_latin1 = 0;
193 int have_circles_with_width = 0, have_circles_without_width = 0;
194 const int upcean = is_upcean(symbol->symbology);
195 const int is_rgb = (symbol->output_options & CMYK_COLOUR) == 0;
196
197 if (symbol->vector == NULL) {
198 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 646, "Vector header NULL");
199 }
200 if (!fm_open(fmp, symbol, "w")) {
201 return errtxtf(ZINT_ERROR_FILE_ACCESS, symbol, 645, "Could not open EPS output file (%1$d: %2$s)", fmp->err,
202 strerror(fmp->err));
203 }
204
205 if (is_rgb) {
206 (void) out_colour_get_rgb(symbol->fgcolour, &fgred, &fggrn, &fgblu, NULL /*alpha*/);
207 red_ink = fgred / 255.0f;
208 green_ink = fggrn / 255.0f;
209 blue_ink = fgblu / 255.0f;
210
211 (void) out_colour_get_rgb(symbol->bgcolour, &bgred, &bggrn, &bgblu, &bgalpha);
212 red_paper = bgred / 255.0f;
213 green_paper = bggrn / 255.0f;
214 blue_paper = bgblu / 255.0f;
215 } else {
216 (void) out_colour_get_cmyk(symbol->fgcolour, &fgcyan, &fgmagenta, &fgyellow, &fgblack, NULL /*rgb_alpha*/);
217 cyan_ink = fgcyan / 100.0f;
218 magenta_ink = fgmagenta / 100.0f;
219 yellow_ink = fgyellow / 100.0f;
220 black_ink = fgblack / 100.0f;
221
222 (void) out_colour_get_cmyk(symbol->bgcolour, &bgcyan, &bgmagenta, &bgyellow, &bgblack, &bgalpha);
223 cyan_paper = bgcyan / 100.0f;
224 magenta_paper = bgmagenta / 100.0f;
225 yellow_paper = bgyellow / 100.0f;
226 black_paper = bgblack / 100.0f;
227 }
228 if (bgalpha == 0) {
229 draw_background = 0;
230 }
231
232 for (i = 0, len = (int) ustrlen(symbol->text); i < len; i++) {
233 switch (symbol->text[i]) {
234 case '(':
235 case ')':
236 case '\\':
237 ps_len += 2;
238 break;
239 default:
240 if (!iso_latin1 && symbol->text[i] >= 0x80) {
241 iso_latin1 = 1;
242 }
243 ps_len++; /* Will overcount 2 byte UTF-8 chars */
244 break;
245 }
246 }
247
248 /* Check for circle widths */
249 for (circle = symbol->vector->circles; circle; circle = circle->next) {
250 if (circle->width) {
251 have_circles_with_width = 1;
252 if (have_circles_without_width) {
253 break;
254 }
255 } else {
256 have_circles_without_width = 1;
257 if (have_circles_with_width) {
258 break;
259 }
260 }
261 }
262
263 /* Start writing the header */
264 fm_puts("%!PS-Adobe-3.0 EPSF-3.0\n"
265 "%%Creator: Zint ", fmp);
266 #if ZINT_VERSION_BUILD
267 fm_printf(fmp, "%d.%d.%d.%d\n", ZINT_VERSION_MAJOR, ZINT_VERSION_MINOR, ZINT_VERSION_RELEASE, ZINT_VERSION_BUILD);
268 #else
269 fm_printf(fmp, "%d.%d.%d\n", ZINT_VERSION_MAJOR, ZINT_VERSION_MINOR, ZINT_VERSION_RELEASE);
270 #endif
271 fm_puts("%%Title: Zint Generated Symbol\n"
272 "%%Pages: 0\n"
273 "%%BoundingBox: 0 0 ", fmp);
274 fm_printf(fmp, "%d %d\n", (int) ceilf(symbol->vector->width), (int) ceilf(symbol->vector->height));
275 fm_puts("%%EndComments\n", fmp);
276
277 /* Definitions */
278 if (have_circles_without_width) {
279 /* Disc: y radius x D */
280 fm_puts("/D { newpath 3 1 roll 0 360 arc fill } bind def\n", fmp);
281 }
282 if (have_circles_with_width) {
283 /* Circle (ring): x y radius width C (adapted from BWIPP renmaxicode.ps) */
284 fm_puts("/C { newpath 4 1 roll 3 copy 0 360 arc closepath 4 -1 roll add 360 0 arcn closepath fill }"
285 " bind def\n", fmp);
286 }
287 if (symbol->vector->hexagons) {
288 /* Hexagon: radius half_radius half_sqrt3_radius x y */
289 if (symbol->vector->hexagons->rotation == 0 || symbol->vector->hexagons->rotation == 180) {
290 fm_puts("/H { newpath moveto 2 copy exch neg exch rmoveto 2 index neg 0 exch rlineto 2 copy neg rlineto"
291 " 2 copy rlineto 3 -1 roll 0 exch rlineto exch neg exch rlineto closepath fill }"
292 " bind def\n", fmp);
293 } else {
294 fm_puts("/H { newpath moveto 2 copy neg exch neg rmoveto 2 index 0 rlineto 2 copy exch rlineto"
295 " 2 copy neg exch rlineto 3 -1 roll neg 0 rlineto neg exch neg rlineto closepath fill }"
296 " bind def\n", fmp);
297 }
298 /* Copy r hr hsr for repeat use without having to specify them subsequently */
299 fm_puts("/J { 3 copy } bind def\n", fmp);
300 /* TODO: Save repeating x also */
301 }
302 if (symbol->vector->rectangles || draw_background) {
303 /* Rectangle: h y x w */
304 fm_puts("/R { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill }"
305 " bind def\n", fmp);
306 }
307 if (symbol->vector->rectangles || have_circles_without_width) {
308 /* Copy h y (rect) or y r (disc) for repeat use without having to specify them subsequently */
309 fm_puts("/I { 2 copy } bind def\n", fmp);
310 }
311
312 /* Now the actual representation */
313
314 /* Background */
315 if (draw_background) {
316 if (is_rgb) {
317 ps_put_rgbcolor(red_paper, green_paper, blue_paper, fmp);
318 } else {
319 ps_put_cmykcolor(cyan_paper, magenta_paper, yellow_paper, black_paper, fmp);
320 }
321
322 fm_putsf("", 2, symbol->vector->height, fmp);
323 fm_putsf(" 0 0 ", 2, symbol->vector->width, fmp); /* y x w */
324 fm_puts(" R\n", fmp);
325 }
326
327 if (symbol->symbology != BARCODE_ULTRA) {
328 if (is_rgb) {
329 ps_put_rgbcolor(red_ink, green_ink, blue_ink, fmp);
330 } else {
331 ps_put_cmykcolor(cyan_ink, magenta_ink, yellow_ink, black_ink, fmp);
332 }
333 }
334
335 /* Rectangles */
336 if (symbol->symbology == BARCODE_ULTRA) {
337 /* Group rectangles by colour */
338 const int rect_cnt = ps_count_rectangles(symbol);
339 struct zint_vector_rect **ultra_rects
340 = (struct zint_vector_rect **) z_alloca(sizeof(struct zint_vector_rect *) * (rect_cnt ? rect_cnt : 1));
341 int u_i = 0;
342 for (i = 0; i <= 8; i++) {
343 for (rect = symbol->vector->rectangles; rect; rect = rect->next) {
344 if ((i == 0 && rect->colour == -1) || rect->colour == i) {
345 ultra_rects[u_i++] = rect;
346 }
347 }
348 }
349 assert(u_i == ps_count_rectangles(symbol));
350
351 colour_rect_flag = 0;
352 type_latch = 0;
353 for (i = 0; i < u_i; i++) {
354 rect = ultra_rects[i];
355 if (i == 0 || rect->colour != ultra_rects[i - 1]->colour) {
356 if (rect->colour == -1) {
357 if (colour_rect_flag == 0) {
358 /* Set foreground colour */
359 if (is_rgb) {
360 ps_put_rgbcolor(red_ink, green_ink, blue_ink, fmp);
361 } else {
362 ps_put_cmykcolor(cyan_ink, magenta_ink, yellow_ink, black_ink, fmp);
363 }
364 colour_rect_flag = 1;
365 }
366 } else {
367 /* Set new colour */
368 ps_put_colour(is_rgb, rect->colour, fmp);
369 }
370 }
371 if (i + 1 < u_i && rect->height == ultra_rects[i + 1]->height && rect->y == ultra_rects[i + 1]->y) {
372 ps_put_rect(symbol, rect, type_latch ? 2 : 1, fmp);
373 type_latch = 1;
374 } else {
375 ps_put_rect(symbol, rect, type_latch ? 3 : 0, fmp);
376 type_latch = 0;
377 }
378 }
379 } else {
380 type_latch = 0;
381 for (rect = symbol->vector->rectangles; rect; rect = rect->next) {
382 if (rect->next && rect->height == rect->next->height && rect->y == rect->next->y) {
383 ps_put_rect(symbol, rect, type_latch ? 2 : 1, fmp);
384 type_latch = 1;
385 } else {
386 ps_put_rect(symbol, rect, type_latch ? 3 : 0, fmp);
387 type_latch = 0;
388 }
389 }
390 }
391
392 /* Hexagons */
393 previous_diameter = 0.0f;
394 for (hex = symbol->vector->hexagons; hex; hex = hex->next) {
395 float hy = symbol->vector->height - hex->y;
396 if (previous_diameter != hex->diameter) {
397 previous_diameter = hex->diameter;
398 fm_putsf("", 4, 0.5f * previous_diameter /*radius*/, fmp);
399 fm_putsf(" ", 4, 0.43301270189221932338f * previous_diameter /*half_sqrt3_radius*/, fmp);
400 fm_putsf(" ", 4, 0.25f * previous_diameter /*half_radius*/, fmp);
401 fm_putc('\n', fmp);
402 }
403 if (hex->next) {
404 fm_putsf("J ", 2, hex->x, fmp);
405 } else {
406 fm_putsf("", 2, hex->x, fmp);
407 }
408 fm_putsf(" ", 2, hy, fmp);
409 fm_puts(" H\n", fmp);
410 }
411
412 /* Circles */
413 previous_diameter = radius = 0.0f;
414 type_latch = 0;
415 for (circle = symbol->vector->circles; circle; circle = circle->next) {
416 if (previous_diameter != circle->diameter - circle->width) {
417 previous_diameter = circle->diameter - circle->width;
418 radius = 0.5f * previous_diameter;
419 }
420 if (circle->colour) { /* Legacy - no longer used */
421 /* A 'white' circle */
422 if (is_rgb) {
423 ps_put_rgbcolor(red_paper, green_paper, blue_paper, fmp);
424 } else {
425 ps_put_cmykcolor(cyan_paper, magenta_paper, yellow_paper, black_paper, fmp);
426 }
427 ps_put_circle(symbol, circle, radius, 0 /*type*/, fmp);
428 if (circle->next) {
429 if (is_rgb) {
430 ps_put_rgbcolor(red_ink, green_ink, blue_ink, fmp);
431 } else {
432 ps_put_cmykcolor(cyan_ink, magenta_ink, yellow_ink, black_ink, fmp);
433 }
434 }
435 } else {
436 /* A 'black' circle */
437 if (circle->next && circle->y == circle->next->y && circle->diameter == circle->next->diameter) {
438 ps_put_circle(symbol, circle, radius, type_latch ? 2 : 1, fmp);
439 type_latch = 1;
440 } else {
441 ps_put_circle(symbol, circle, radius, type_latch ? 3 : 0, fmp);
442 type_latch = 0;
443 }
444 }
445 }
446
447 /* Text */
448
449 string = symbol->vector->strings;
450
451 if (string) {
452 float previous_fsize = 0.0f;
453 const char *font;
454 unsigned char *ps_string = (unsigned char *) z_alloca(ps_len + 1);
455
456 if ((symbol->output_options & BOLD_TEXT) && !upcean) {
457 font = "Helvetica-Bold";
458 } else {
459 font = "Helvetica";
460 }
461 if (iso_latin1) {
462 /* Change encoding to ISO 8859-1, see Postscript Language Reference Manual 2nd Edition Example 5.6 */
463 fm_printf(fmp, "/%s findfont\n", font);
464 fm_puts("dup length dict begin\n"
465 "{1 index /FID ne {def} {pop pop} ifelse} forall\n"
466 "/Encoding ISOLatin1Encoding def\n"
467 "currentdict\n"
468 "end\n"
469 "/Helvetica-ISOLatin1 exch definefont pop\n", fmp);
470 font = "Helvetica-ISOLatin1";
471 }
472 do {
473 ps_convert(string->text, ps_string);
474 if (string->fsize != previous_fsize) {
475 fm_printf(fmp, "/%s findfont", font);
476 /* Compensate for Helvetica being smaller than Zint's OCR-B */
477 fm_putsf( " ", 2, upcean ? string->fsize * 1.07f : string->fsize, fmp);
478 fm_puts(" scalefont setfont\n", fmp);
479 previous_fsize = string->fsize;
480 }
481 /* Unhack the guard whitespace `gws_left_fudge`/`gws_right_fudge` hack */
482 if (upcean && string->halign == 1 && string->text[0] == '<') {
483 const float gws_left_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale; /* 0.5 * 2 * scale */
484 fm_putsf(" ", 2, string->x + gws_left_fudge, fmp);
485 } else if (upcean && string->halign == 2 && string->text[0] == '>') {
486 const float gws_right_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale; /* 0.5 * 2 * scale */
487 fm_putsf(" ", 2, string->x - gws_right_fudge, fmp);
488 } else {
489 fm_putsf(" ", 2, string->x, fmp);
490 }
491 fm_putsf(" ", 2, symbol->vector->height - string->y, fmp);
492 fm_puts(" moveto\n", fmp);
493 if (string->rotation != 0) {
494 fm_puts(" gsave\n", fmp);
495 fm_printf(fmp, " %d rotate\n", 360 - string->rotation);
496 }
497 if (string->halign == 0 || string->halign == 2) { /* Need width for middle or right align */
498 fm_printf(fmp, " (%s) stringwidth pop" /* Returns "width height" - discard "height" */
499 " %s 0 rmoveto\n", ps_string, string->halign == 2 ? "neg" : "-2 div");
500 }
501 fm_printf(fmp, " (%s) show\n", ps_string);
502 if (string->rotation != 0) {
503 fm_puts(" grestore\n", fmp);
504 }
505 string = string->next;
506 } while (string);
507 }
508
509 if (fm_error(fmp)) {
510 errtxtf(0, symbol, 647, "Incomplete write of EPS output (%1$d: %2$s)", fmp->err, strerror(fmp->err));
511 (void) fm_close(fmp, symbol);
512 return ZINT_ERROR_FILE_WRITE;
513 }
514
515 if (!fm_close(fmp, symbol)) {
516 return errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 649, "Failure on closing EPS output file (%1$d: %2$s)",
517 fmp->err, strerror(fmp->err));
518 }
519
520 return 0;
521 }
522
523 /* vim: set ts=4 sw=4 et : */