comparison mupdf-source/thirdparty/zint/backend/emf.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 /* emf.c - Support for Microsoft Enhanced Metafile Format */
2 /*
3 libzint - the open source barcode library
4 Copyright (C) 2016-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 /* Developed according to [MS-EMF] - v20160714, Released July 14, 2016
34 * and [MS-WMF] - v20160714, Released July 14, 2016 */
35
36 #include <errno.h>
37 #include <stdio.h>
38 #include <assert.h>
39 #include <math.h>
40 #include "common.h"
41 #include "filemem.h"
42 #include "output.h"
43 #include "emf.h"
44
45 /* Multiply truncating to 3 decimal places (avoids rounding differences on various platforms) */
46 #define emf_mul3dpf(m, arg) stripf(roundf((m) * (arg) * 1000.0f) / 1000.0f)
47
48 static int emf_count_rectangles(const struct zint_symbol *symbol) {
49 int rectangles = 0;
50 const struct zint_vector_rect *rect;
51
52 rect = symbol->vector->rectangles;
53 while (rect) {
54 rectangles++;
55 rect = rect->next;
56 }
57
58 return rectangles;
59 }
60
61 static int emf_count_circles(const struct zint_symbol *symbol) {
62 int circles = 0;
63 const struct zint_vector_circle *circ;
64
65 circ = symbol->vector->circles;
66 while (circ) {
67 circles++;
68 circ = circ->next;
69 }
70
71 /* Hack for MaxiCode */
72 return symbol->symbology == BARCODE_MAXICODE ? circles * 2 : circles;
73 }
74
75 static int emf_count_hexagons(const struct zint_symbol *symbol) {
76 int hexagons = 0;
77 const struct zint_vector_hexagon *hex;
78
79 hex = symbol->vector->hexagons;
80 while (hex) {
81 hexagons++;
82 hex = hex->next;
83 }
84
85 return hexagons;
86 }
87
88 static int emf_count_strings(const struct zint_symbol *symbol, float *fsize, float *fsize2, int *halign_left,
89 int *halign_right) {
90 int strings = 0;
91 const struct zint_vector_string *string;
92
93 *fsize = *fsize2 = 0.0f;
94 *halign_left = *halign_right = 0;
95
96 string = symbol->vector->strings;
97 while (string) {
98 /* Allow 2 font sizes */
99 if (*fsize == 0.0f) {
100 *fsize = string->fsize;
101 } else if (string->fsize != *fsize && *fsize2 == 0.0f) {
102 *fsize2 = string->fsize;
103 }
104 /* Only 3 haligns possible and centre align always assumed used */
105 if (string->halign) { /* Left or right align */
106 if (string->halign == 1) { /* Left align */
107 *halign_left = string->halign;
108 } else { /* Right align */
109 *halign_right = string->halign;
110 }
111 }
112 strings++;
113 string = string->next;
114 }
115
116 return strings;
117 }
118
119 /* Convert UTF-8 to UTF-16LE - only needs to handle characters <= U+00FF */
120 static void emf_utfle_copy(unsigned char *output, const unsigned char *input, const int length) {
121 int i;
122 int o;
123
124 i = 0;
125 o = 0;
126 do {
127 if (input[i] <= 0x7f) {
128 /* 1 byte mode (7-bit ASCII) */
129 output[o] = input[i];
130 output[o + 1] = 0x00;
131 o += 2;
132 i++;
133 } else {
134 /* 2 byte mode */
135 output[o] = ((input[i] & 0x1f) << 6) + (input[i + 1] & 0x3f);
136 output[o + 1] = 0x00;
137 o += 2;
138 i += 2;
139 }
140 } while (i < length);
141 }
142
143 /* Strings length must be a multiple of 4 bytes */
144 static int emf_bump_up(const int input) {
145 return (input + (input & 1)) << 1;
146 }
147
148 static int emf_utfle_length(const unsigned char *input, const int length) {
149 int result = 0;
150 int i;
151
152 for (i = 0; i < length; i++) {
153 result++;
154 if (input[i] >= 0x80) { /* 2 byte UTF-8 counts as one UTF-16LE character */
155 i++;
156 }
157 }
158
159 return result;
160 }
161
162 INTERNAL int emf_plot(struct zint_symbol *symbol, int rotate_angle) {
163 int i;
164 struct filemem fm;
165 struct filemem *const fmp = &fm;
166 unsigned char fgred, fggrn, fgblu, bgred, bggrn, bgblu, bgalpha;
167 int error_number = 0;
168 int rectangle_count, this_rectangle;
169 int circle_count, this_circle;
170 int hexagon_count, this_hexagon;
171 int string_count, this_text;
172 int bytecount, recordcount;
173 float previous_diameter;
174 float radius, half_radius, half_sqrt3_radius;
175 int colours_used = 0;
176 int rectangle_bycolour[9] = {0};
177
178 int width, height;
179 int bounds_pxx, bounds_pxy; /* Pixels */
180 int frame_cmmx, frame_cmmy; /* Hundredths of a mm, i.e. "centi-millimeters" */
181 int device_pxx, device_pxy; /* Pixels */
182 int mmx, mmy; /* Millimeters */
183 int micronx, microny; /* Micrometers */
184 const float dpmm = symbol->dpmm ? stripf(symbol->dpmm)
185 : ZBarcode_XdimDp_From_Scale(symbol->symbology, symbol->scale,
186 ZBarcode_Default_Xdim(symbol->symbology), "EMF");
187 const int sideways = rotate_angle == 90 || rotate_angle == 270;
188
189 int draw_background = 1;
190 int bold;
191 const int upcean = is_upcean(symbol->symbology);
192
193 struct zint_vector_rect *rect;
194 struct zint_vector_circle *circ;
195 struct zint_vector_hexagon *hex;
196 struct zint_vector_string *string;
197
198 /* Allow for up to 6 strings (current max 3 for UPC/EAN) */
199 unsigned char *this_string[6];
200 emr_exttextoutw_t text[6];
201 float text_fsizes[6];
202 int text_haligns[6];
203 int text_bumped_lens[6];
204
205 emr_header_t emr_header;
206 emr_eof_t emr_eof;
207 emr_mapmode_t emr_mapmode;
208 emr_setworldtransform_t emr_setworldtransform;
209 emr_createbrushindirect_t emr_createbrushindirect_fg;
210 emr_createbrushindirect_t emr_createbrushindirect_bg;
211 emr_createbrushindirect_t emr_createbrushindirect_colour[9]; /* Used for colour symbols only */
212 emr_selectobject_t emr_selectobject_fgbrush;
213 emr_selectobject_t emr_selectobject_bgbrush;
214 emr_selectobject_t emr_selectobject_colour[9]; /* Used for colour symbols only */
215 emr_createpen_t emr_createpen;
216 emr_selectobject_t emr_selectobject_pen;
217 emr_rectangle_t background;
218 emr_settextcolor_t emr_settextcolor;
219
220 float fsize;
221 emr_extcreatefontindirectw_t emr_extcreatefontindirectw;
222 emr_selectobject_t emr_selectobject_font;
223 float fsize2;
224 emr_extcreatefontindirectw_t emr_extcreatefontindirectw2;
225 emr_selectobject_t emr_selectobject_font2;
226 emr_settextalign_t emr_settextalign_centre; /* Centre align */
227 int halign_left; /* Set if left halign used */
228 emr_settextalign_t emr_settextalign_left;
229 int halign_right; /* Set if right halign used */
230 emr_settextalign_t emr_settextalign_right;
231
232 float current_fsize;
233 int current_halign;
234
235 emr_rectangle_t *rectangle;
236 emr_ellipse_t *circle;
237 emr_polygon_t *hexagon;
238
239 const int ih_ultra_offset = symbol->symbology == BARCODE_ULTRA ? 8 : 0;
240
241 if (symbol->vector == NULL) {
242 return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 643, "Vector header NULL");
243 }
244
245 (void) out_colour_get_rgb(symbol->fgcolour, &fgred, &fggrn, &fgblu, NULL /*alpha*/);
246 (void) out_colour_get_rgb(symbol->bgcolour, &bgred, &bggrn, &bgblu, &bgalpha);
247 if (bgalpha == 0) {
248 draw_background = 0;
249 }
250
251 rectangle_count = emf_count_rectangles(symbol);
252 circle_count = emf_count_circles(symbol);
253 hexagon_count = emf_count_hexagons(symbol);
254 string_count = emf_count_strings(symbol, &fsize, &fsize2, &halign_left, &halign_right);
255
256 /* Avoid sanitize runtime error by making always non-zero */
257 rectangle = (emr_rectangle_t *) z_alloca(sizeof(emr_rectangle_t) * (rectangle_count ? rectangle_count : 1));
258 circle = (emr_ellipse_t *) z_alloca(sizeof(emr_ellipse_t) * (circle_count ? circle_count : 1));
259 hexagon = (emr_polygon_t *) z_alloca(sizeof(emr_polygon_t) * (hexagon_count ? hexagon_count : 1));
260
261 /* Calculate how many coloured rectangles */
262 if (symbol->symbology == BARCODE_ULTRA) {
263
264 rect = symbol->vector->rectangles;
265 while (rect) {
266 if (rect->colour == -1) { /* Foreground colour */
267 if (rectangle_bycolour[0] == 0) {
268 colours_used++;
269 rectangle_bycolour[0] = 1;
270 }
271 } else {
272 if (rectangle_bycolour[rect->colour] == 0) {
273 colours_used++;
274 rectangle_bycolour[rect->colour] = 1;
275 }
276 }
277 rect = rect->next;
278 }
279 }
280
281 width = (int) ceilf(symbol->vector->width);
282 height = (int) ceilf(symbol->vector->height);
283
284 bounds_pxx = width - 1; /* Following Inkscape, bounds "inclusive-inclusive", so size 1 less */
285 bounds_pxy = height - 1;
286 device_pxx = width; /* device */
287 device_pxy = height;
288
289 if (dpmm) {
290 frame_cmmx = (int) roundf(stripf(width * 100.0f / dpmm)) - 1; /* frame also "inclusive-inclusive" */
291 frame_cmmy = (int) roundf(stripf(height * 100.0f / dpmm)) - 1;
292 mmx = (int) roundf(stripf(width / dpmm)); /* millimeters */
293 mmy = (int) roundf(stripf(height / dpmm));
294 micronx = (int) roundf(stripf(width * 1000.0f / dpmm)); /* micrometers */
295 microny = (int) roundf(stripf(height * 1000.0f / dpmm));
296 } else { /* Should only happen if `symbol->scale` zero. */
297 frame_cmmx = (int) roundf(stripf(width * 100.0f)) - 1;
298 frame_cmmy = (int) roundf(stripf(height * 100.0f)) - 1;
299 mmx = (int) roundf(stripf(width));
300 mmy = (int) roundf(stripf(height));
301 micronx = (int) roundf(stripf(width * 1000.0f));
302 microny = (int) roundf(stripf(height * 1000.0f));
303 }
304
305 /* Header */
306 out_le_u32(emr_header.type, 0x00000001); /* EMR_HEADER */
307 out_le_u32(emr_header.size, 108); /* Including extensions */
308 out_le_i32(emr_header.emf_header.bounds.left, 0);
309 out_le_i32(emr_header.emf_header.bounds.top, 0);
310 out_le_i32(emr_header.emf_header.bounds.right, sideways ? bounds_pxy : bounds_pxx);
311 out_le_i32(emr_header.emf_header.bounds.bottom, sideways ? bounds_pxx : bounds_pxy);
312 out_le_i32(emr_header.emf_header.frame.left, 0);
313 out_le_i32(emr_header.emf_header.frame.top, 0);
314 out_le_i32(emr_header.emf_header.frame.right, sideways ? frame_cmmy : frame_cmmx);
315 out_le_i32(emr_header.emf_header.frame.bottom, sideways ? frame_cmmx : frame_cmmy);
316 out_le_u32(emr_header.emf_header.record_signature, 0x464d4520); /* ENHMETA_SIGNATURE */
317 out_le_u32(emr_header.emf_header.version, 0x00010000);
318 out_le_u16(emr_header.emf_header.handles, (fsize2 != 0.0f ? 5 : 4) + ih_ultra_offset); /* No. of graphics objs */
319 out_le_u16(emr_header.emf_header.reserved, 0x0000);
320 out_le_u32(emr_header.emf_header.n_description, 0);
321 out_le_u32(emr_header.emf_header.off_description, 0);
322 out_le_u32(emr_header.emf_header.n_pal_entries, 0);
323 out_le_u32(emr_header.emf_header.device.cx, sideways ? device_pxy : device_pxx);
324 out_le_u32(emr_header.emf_header.device.cy, sideways ? device_pxx : device_pxy);
325 out_le_u32(emr_header.emf_header.millimeters.cx, sideways ? mmy : mmx);
326 out_le_u32(emr_header.emf_header.millimeters.cy, sideways ? mmx : mmy);
327 /* HeaderExtension1 */
328 out_le_u32(emr_header.emf_header.cb_pixel_format, 0x0000); /* None set */
329 out_le_u32(emr_header.emf_header.off_pixel_format, 0x0000); /* None set */
330 out_le_u32(emr_header.emf_header.b_open_gl, 0x0000); /* OpenGL not present */
331 /* HeaderExtension2 */
332 out_le_u32(emr_header.emf_header.micrometers.cx, sideways ? microny : micronx);
333 out_le_u32(emr_header.emf_header.micrometers.cy, sideways ? micronx : microny);
334 bytecount = 108;
335 recordcount = 1;
336
337 out_le_u32(emr_mapmode.type, 0x00000011); /* EMR_SETMAPMODE */
338 out_le_u32(emr_mapmode.size, 12);
339 out_le_u32(emr_mapmode.mapmode, 0x01); /* MM_TEXT */
340 bytecount += 12;
341 recordcount++;
342
343 if (rotate_angle) {
344 out_le_u32(emr_setworldtransform.type, 0x00000023); /* EMR_SETWORLDTRANSFORM */
345 out_le_u32(emr_setworldtransform.size, 32);
346 out_le_float(emr_setworldtransform.m11, rotate_angle == 90 ? 0.0f : rotate_angle == 180 ? -1.0f : 0.0f);
347 out_le_float(emr_setworldtransform.m12, rotate_angle == 90 ? 1.0f : rotate_angle == 180 ? 0.0f : -1.0f);
348 out_le_float(emr_setworldtransform.m21, rotate_angle == 90 ? -1.0f : rotate_angle == 180 ? 0.0f : 1.0f);
349 out_le_float(emr_setworldtransform.m22, rotate_angle == 90 ? 0.0f : rotate_angle == 180 ? -1.0f : 0.0f);
350 out_le_float(emr_setworldtransform.dx, rotate_angle == 90 ? height : rotate_angle == 180 ? width : 0.0f);
351 out_le_float(emr_setworldtransform.dy, rotate_angle == 90 ? 0.0f : rotate_angle == 180 ? height : width);
352 bytecount += 32;
353 recordcount++;
354 }
355
356 /* Create Brushes */
357 out_le_u32(emr_createbrushindirect_bg.type, 0x00000027); /* EMR_CREATEBRUSHINDIRECT */
358 out_le_u32(emr_createbrushindirect_bg.size, 24);
359 out_le_u32(emr_createbrushindirect_bg.ih_brush, 0);
360 out_le_u32(emr_createbrushindirect_bg.log_brush.brush_style, 0x0000); /* BS_SOLID */
361 emr_createbrushindirect_bg.log_brush.color.red = bgred;
362 emr_createbrushindirect_bg.log_brush.color.green = bggrn;
363 emr_createbrushindirect_bg.log_brush.color.blue = bgblu;
364 emr_createbrushindirect_bg.log_brush.color.reserved = 0;
365 out_le_u32(emr_createbrushindirect_bg.log_brush.brush_hatch, 0x0006); /* HS_SOLIDCLR */
366 bytecount += 24;
367 recordcount++;
368
369 if (symbol->symbology == BARCODE_ULTRA) {
370 static const char ultra_chars[] = { '0', 'C', 'B', 'M', 'R', 'Y', 'G', 'K', 'W' };
371 for (i = 0; i < 9; i++) {
372 out_le_u32(emr_createbrushindirect_colour[i].type, 0x00000027); /* EMR_CREATEBRUSHINDIRECT */
373 out_le_u32(emr_createbrushindirect_colour[i].size, 24);
374 out_le_u32(emr_createbrushindirect_colour[i].ih_brush, 1 + i);
375 out_le_u32(emr_createbrushindirect_colour[i].log_brush.brush_style, 0x0000); /* BS_SOLID */
376 if (i == 0) {
377 emr_createbrushindirect_colour[i].log_brush.color.red = fgred;
378 emr_createbrushindirect_colour[i].log_brush.color.green = fggrn;
379 emr_createbrushindirect_colour[i].log_brush.color.blue = fgblu;
380 } else {
381 out_colour_char_to_rgb(ultra_chars[i],
382 &emr_createbrushindirect_colour[i].log_brush.color.red,
383 &emr_createbrushindirect_colour[i].log_brush.color.green,
384 &emr_createbrushindirect_colour[i].log_brush.color.blue);
385 }
386 emr_createbrushindirect_colour[i].log_brush.color.reserved = 0;
387 out_le_u32(emr_createbrushindirect_colour[i].log_brush.brush_hatch, 0x0006); /* HS_SOLIDCLR */
388 }
389 bytecount += colours_used * 24;
390 recordcount += colours_used;
391 } else {
392 out_le_u32(emr_createbrushindirect_fg.type, 0x00000027); /* EMR_CREATEBRUSHINDIRECT */
393 out_le_u32(emr_createbrushindirect_fg.size, 24);
394 out_le_u32(emr_createbrushindirect_fg.ih_brush, 1);
395 out_le_u32(emr_createbrushindirect_fg.log_brush.brush_style, 0x0000); /* BS_SOLID */
396 emr_createbrushindirect_fg.log_brush.color.red = fgred;
397 emr_createbrushindirect_fg.log_brush.color.green = fggrn;
398 emr_createbrushindirect_fg.log_brush.color.blue = fgblu;
399 emr_createbrushindirect_fg.log_brush.color.reserved = 0;
400 out_le_u32(emr_createbrushindirect_fg.log_brush.brush_hatch, 0x0006); /* HS_SOLIDCLR */
401 bytecount += 24;
402 recordcount++;
403 }
404
405 out_le_u32(emr_selectobject_bgbrush.type, 0x00000025); /* EMR_SELECTOBJECT */
406 out_le_u32(emr_selectobject_bgbrush.size, 12);
407 emr_selectobject_bgbrush.ih_object = emr_createbrushindirect_bg.ih_brush;
408 bytecount += 12;
409 recordcount++;
410
411 if (symbol->symbology == BARCODE_ULTRA) {
412 for (i = 0; i < 9; i++) {
413 out_le_u32(emr_selectobject_colour[i].type, 0x00000025); /* EMR_SELECTOBJECT */
414 out_le_u32(emr_selectobject_colour[i].size, 12);
415 emr_selectobject_colour[i].ih_object = emr_createbrushindirect_colour[i].ih_brush;
416 }
417 bytecount += colours_used * 12;
418 recordcount += colours_used;
419 } else {
420 out_le_u32(emr_selectobject_fgbrush.type, 0x00000025); /* EMR_SELECTOBJECT */
421 out_le_u32(emr_selectobject_fgbrush.size, 12);
422 emr_selectobject_fgbrush.ih_object = emr_createbrushindirect_fg.ih_brush;
423 bytecount += 12;
424 recordcount++;
425 }
426
427 /* Create Pens */
428 out_le_u32(emr_createpen.type, 0x00000026); /* EMR_CREATEPEN */
429 out_le_u32(emr_createpen.size, 28);
430 out_le_u32(emr_createpen.ih_pen, 2 + ih_ultra_offset);
431 out_le_u32(emr_createpen.log_pen.pen_style, 0x00000005); /* PS_NULL */
432 out_le_i32(emr_createpen.log_pen.width.x, 1);
433 out_le_i32(emr_createpen.log_pen.width.y, 0); /* ignored */
434 emr_createpen.log_pen.color_ref.red = 0;
435 emr_createpen.log_pen.color_ref.green = 0;
436 emr_createpen.log_pen.color_ref.blue = 0;
437 emr_createpen.log_pen.color_ref.reserved = 0;
438 bytecount += 28;
439 recordcount++;
440
441 out_le_u32(emr_selectobject_pen.type, 0x00000025); /* EMR_SELECTOBJECT */
442 out_le_u32(emr_selectobject_pen.size, 12);
443 emr_selectobject_pen.ih_object = emr_createpen.ih_pen;
444 bytecount += 12;
445 recordcount++;
446
447 if (draw_background) {
448 /* Make background from a rectangle */
449 out_le_u32(background.type, 0x0000002b); /* EMR_RECTANGLE */
450 out_le_u32(background.size, 24);
451 out_le_i32(background.box.top, 0);
452 out_le_i32(background.box.left, 0);
453 out_le_i32(background.box.right, width);
454 out_le_i32(background.box.bottom, height);
455 bytecount += 24;
456 recordcount++;
457 }
458
459 /* Rectangles */
460 rect = symbol->vector->rectangles;
461 this_rectangle = 0;
462 while (rect) {
463 out_le_u32(rectangle[this_rectangle].type, 0x0000002b); /* EMR_RECTANGLE */
464 out_le_u32(rectangle[this_rectangle].size, 24);
465 out_le_i32(rectangle[this_rectangle].box.top, rect->y);
466 out_le_i32(rectangle[this_rectangle].box.bottom, stripf(rect->y + rect->height));
467 out_le_i32(rectangle[this_rectangle].box.left, rect->x);
468 out_le_i32(rectangle[this_rectangle].box.right, stripf(rect->x + rect->width));
469 this_rectangle++;
470 bytecount += 24;
471 recordcount++;
472 rect = rect->next;
473 }
474
475 /* Circles */
476 previous_diameter = radius = 0.0f;
477 circ = symbol->vector->circles;
478 this_circle = 0;
479 while (circ) {
480 /* Note using circle width the proper way, with a non-null pen of specified width and a null brush for fill,
481 causes various different rendering issues for LibreOffice Draw and Inkscape, so using following hack */
482 if (previous_diameter != circ->diameter + circ->width) { /* Drawing MaxiCode bullseye using overlayed discs */
483 previous_diameter = circ->diameter + circ->width;
484 radius = emf_mul3dpf(0.5f, previous_diameter);
485 }
486 out_le_u32(circle[this_circle].type, 0x0000002a); /* EMR_ELLIPSE */
487 out_le_u32(circle[this_circle].size, 24);
488 out_le_i32(circle[this_circle].box.top, stripf(circ->y - radius));
489 out_le_i32(circle[this_circle].box.bottom, stripf(circ->y + radius));
490 out_le_i32(circle[this_circle].box.left, stripf(circ->x - radius));
491 out_le_i32(circle[this_circle].box.right, stripf(circ->x + radius));
492 this_circle++;
493 bytecount += 24;
494 recordcount++;
495
496 if (symbol->symbology == BARCODE_MAXICODE) { /* Drawing MaxiCode bullseye using overlayed discs */
497 float inner_radius = radius - circ->width;
498 out_le_u32(circle[this_circle].type, 0x0000002a); /* EMR_ELLIPSE */
499 out_le_u32(circle[this_circle].size, 24);
500 out_le_i32(circle[this_circle].box.top, stripf(circ->y - inner_radius));
501 out_le_i32(circle[this_circle].box.bottom, stripf(circ->y + inner_radius));
502 out_le_i32(circle[this_circle].box.left, stripf(circ->x - inner_radius));
503 out_le_i32(circle[this_circle].box.right, stripf(circ->x + inner_radius));
504 this_circle++;
505 bytecount += 24;
506 recordcount++;
507 }
508
509 circ = circ->next;
510 }
511
512 /* Hexagons */
513 previous_diameter = radius = half_radius = half_sqrt3_radius = 0.0f;
514 hex = symbol->vector->hexagons;
515 this_hexagon = 0;
516 while (hex) {
517 out_le_u32(hexagon[this_hexagon].type, 0x00000003); /* EMR_POLYGON */
518 out_le_u32(hexagon[this_hexagon].size, 76);
519 out_le_u32(hexagon[this_hexagon].count, 6);
520
521 if (previous_diameter != hex->diameter) {
522 previous_diameter = hex->diameter;
523 radius = emf_mul3dpf(0.5f, previous_diameter);
524 half_radius = emf_mul3dpf(0.25f, previous_diameter);
525 half_sqrt3_radius = emf_mul3dpf(0.43301270189221932338f, previous_diameter);
526 }
527
528 /* Note rotation done via world transform */
529 out_le_i32(hexagon[this_hexagon].a_points_a.x, hex->x);
530 out_le_i32(hexagon[this_hexagon].a_points_a.y, stripf(hex->y + radius));
531 out_le_i32(hexagon[this_hexagon].a_points_b.x, stripf(hex->x + half_sqrt3_radius));
532 out_le_i32(hexagon[this_hexagon].a_points_b.y, stripf(hex->y + half_radius));
533 out_le_i32(hexagon[this_hexagon].a_points_c.x, stripf(hex->x + half_sqrt3_radius));
534 out_le_i32(hexagon[this_hexagon].a_points_c.y, stripf(hex->y - half_radius));
535 out_le_i32(hexagon[this_hexagon].a_points_d.x, hex->x);
536 out_le_i32(hexagon[this_hexagon].a_points_d.y, stripf(hex->y - radius));
537 out_le_i32(hexagon[this_hexagon].a_points_e.x, stripf(hex->x - half_sqrt3_radius));
538 out_le_i32(hexagon[this_hexagon].a_points_e.y, stripf(hex->y - half_radius));
539 out_le_i32(hexagon[this_hexagon].a_points_f.x, stripf(hex->x - half_sqrt3_radius));
540 out_le_i32(hexagon[this_hexagon].a_points_f.y, stripf(hex->y + half_radius));
541
542 hexagon[this_hexagon].bounds.top = hexagon[this_hexagon].a_points_d.y;
543 hexagon[this_hexagon].bounds.bottom = hexagon[this_hexagon].a_points_a.y;
544 hexagon[this_hexagon].bounds.left = hexagon[this_hexagon].a_points_e.x;
545 hexagon[this_hexagon].bounds.right = hexagon[this_hexagon].a_points_c.x;
546 this_hexagon++;
547 bytecount += 76;
548 recordcount++;
549 hex = hex->next;
550 }
551
552 /* Create font records, alignment records and text color */
553 if (symbol->vector->strings) {
554 bold = (symbol->output_options & BOLD_TEXT) && !is_upcean(symbol->symbology);
555 memset(&emr_extcreatefontindirectw, 0, sizeof(emr_extcreatefontindirectw));
556 out_le_u32(emr_extcreatefontindirectw.type, 0x00000052); /* EMR_EXTCREATEFONTINDIRECTW */
557 out_le_u32(emr_extcreatefontindirectw.size, 104);
558 out_le_u32(emr_extcreatefontindirectw.ih_fonts, 3 + ih_ultra_offset);
559 out_le_i32(emr_extcreatefontindirectw.elw.height, fsize);
560 out_le_i32(emr_extcreatefontindirectw.elw.width, 0); /* automatic */
561 out_le_i32(emr_extcreatefontindirectw.elw.weight, bold ? 700 : 400);
562 emr_extcreatefontindirectw.elw.char_set = 0x00; /* ANSI_CHARSET */
563 emr_extcreatefontindirectw.elw.out_precision = 0x00; /* OUT_DEFAULT_PRECIS */
564 emr_extcreatefontindirectw.elw.clip_precision = 0x00; /* CLIP_DEFAULT_PRECIS */
565 emr_extcreatefontindirectw.elw.pitch_and_family = 0x02 | (0x02 << 6); /* FF_SWISS | VARIABLE_PITCH */
566 emf_utfle_copy(emr_extcreatefontindirectw.elw.facename, (const unsigned char *) "sans-serif", 10);
567 bytecount += 104;
568 recordcount++;
569
570 out_le_u32(emr_selectobject_font.type, 0x00000025); /* EMR_SELECTOBJECT */
571 out_le_u32(emr_selectobject_font.size, 12);
572 emr_selectobject_font.ih_object = emr_extcreatefontindirectw.ih_fonts;
573 bytecount += 12;
574 recordcount++;
575
576 if (fsize2) {
577 memcpy(&emr_extcreatefontindirectw2, &emr_extcreatefontindirectw, sizeof(emr_extcreatefontindirectw));
578 out_le_u32(emr_extcreatefontindirectw2.ih_fonts, 4 + ih_ultra_offset);
579 out_le_i32(emr_extcreatefontindirectw2.elw.height, fsize2);
580 bytecount += 104;
581 recordcount++;
582
583 out_le_u32(emr_selectobject_font2.type, 0x00000025); /* EMR_SELECTOBJECT */
584 out_le_u32(emr_selectobject_font2.size, 12);
585 emr_selectobject_font2.ih_object = emr_extcreatefontindirectw2.ih_fonts;
586 bytecount += 12;
587 recordcount++;
588 }
589
590 /* Note select aligns counted below in strings loop */
591
592 out_le_u32(emr_settextalign_centre.type, 0x00000016); /* EMR_SETTEXTALIGN */
593 out_le_u32(emr_settextalign_centre.size, 12);
594 out_le_u32(emr_settextalign_centre.text_alignment_mode, 0x0006 | 0x0018); /* TA_CENTER | TA_BASELINE */
595 if (halign_left) {
596 out_le_u32(emr_settextalign_left.type, 0x00000016); /* EMR_SETTEXTALIGN */
597 out_le_u32(emr_settextalign_left.size, 12);
598 out_le_u32(emr_settextalign_left.text_alignment_mode, 0x0000 | 0x0018); /* TA_LEFT | TA_BASELINE */
599 }
600 if (halign_right) {
601 out_le_u32(emr_settextalign_right.type, 0x00000016); /* EMR_SETTEXTALIGN */
602 out_le_u32(emr_settextalign_right.size, 12);
603 out_le_u32(emr_settextalign_right.text_alignment_mode, 0x0002 | 0x0018); /* TA_RIGHT | TA_BASELINE */
604 }
605
606 out_le_u32(emr_settextcolor.type, 0x0000018); /* EMR_SETTEXTCOLOR */
607 out_le_u32(emr_settextcolor.size, 12);
608 emr_settextcolor.color.red = fgred;
609 emr_settextcolor.color.green = fggrn;
610 emr_settextcolor.color.blue = fgblu;
611 emr_settextcolor.color.reserved = 0;
612 bytecount += 12;
613 recordcount++;
614 }
615
616 /* Text */
617 this_text = 0;
618 /* Loop over font sizes so that they're grouped together, so only have to select font twice at most */
619 for (i = 0, current_fsize = fsize; i < 2 && current_fsize; i++, current_fsize = fsize2) {
620 string = symbol->vector->strings;
621 current_halign = -1;
622 while (string) {
623 int utfle_len;
624 if (string->fsize != current_fsize) {
625 string = string->next;
626 continue;
627 }
628 text_fsizes[this_text] = string->fsize;
629 text_haligns[this_text] = string->halign;
630 if (text_haligns[this_text] != current_halign) {
631 current_halign = text_haligns[this_text];
632 bytecount += 12;
633 recordcount++;
634 }
635 assert(string->length > 0);
636 utfle_len = emf_utfle_length(string->text, string->length);
637 text_bumped_lens[this_text] = emf_bump_up(utfle_len);
638 if (!(this_string[this_text] = (unsigned char *) malloc(text_bumped_lens[this_text]))) {
639 for (i = 0; i < this_text; i++) {
640 free(this_string[i]);
641 }
642 return errtxt(ZINT_ERROR_MEMORY, symbol, 641, "Insufficient memory for EMF string buffer");
643 }
644 memset(this_string[this_text], 0, text_bumped_lens[this_text]);
645 out_le_u32(text[this_text].type, 0x00000054); /* EMR_EXTTEXTOUTW */
646 out_le_u32(text[this_text].size, 76 + text_bumped_lens[this_text]);
647 out_le_i32(text[this_text].bounds.top, 0); /* ignored */
648 out_le_i32(text[this_text].bounds.left, 0); /* ignored */
649 out_le_i32(text[this_text].bounds.right, 0xffffffff); /* ignored */
650 out_le_i32(text[this_text].bounds.bottom, 0xffffffff); /* ignored */
651 out_le_u32(text[this_text].i_graphics_mode, 0x00000002); /* GM_ADVANCED */
652 out_le_float(text[this_text].ex_scale, 1.0f);
653 out_le_float(text[this_text].ey_scale, 1.0f);
654 /* Unhack the guard whitespace `gws_left_fudge`/`gws_right_fudge` hack */
655 if (upcean && string->halign == 1 && string->text[0] == '<') {
656 const float gws_left_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale; /* 0.5 * 2 * scale */
657 out_le_i32(text[this_text].w_emr_text.reference.x, string->x + gws_left_fudge);
658 } else if (upcean && string->halign == 2 && string->text[0] == '>') {
659 const float gws_right_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale; /* 0.5 * 2 * scale */
660 out_le_i32(text[this_text].w_emr_text.reference.x, string->x - gws_right_fudge);
661 } else {
662 out_le_i32(text[this_text].w_emr_text.reference.x, string->x);
663 }
664 out_le_i32(text[this_text].w_emr_text.reference.y, string->y);
665 out_le_u32(text[this_text].w_emr_text.chars, utfle_len);
666 out_le_u32(text[this_text].w_emr_text.off_string, 76);
667 out_le_u32(text[this_text].w_emr_text.options, 0);
668 out_le_i32(text[this_text].w_emr_text.rectangle.top, 0);
669 out_le_i32(text[this_text].w_emr_text.rectangle.left, 0);
670 out_le_i32(text[this_text].w_emr_text.rectangle.right, 0xffffffff);
671 out_le_i32(text[this_text].w_emr_text.rectangle.bottom, 0xffffffff);
672 out_le_u32(text[this_text].w_emr_text.off_dx, 0);
673 emf_utfle_copy(this_string[this_text], string->text, string->length);
674 bytecount += 76 + text_bumped_lens[this_text];
675 recordcount++;
676
677 this_text++;
678 string = string->next;
679 }
680 }
681 /* Suppress clang-tidy clang-analyzer-core.UndefinedBinaryOperatorResult warning */
682 assert(this_text == string_count);
683
684 /* Create EOF record */
685 out_le_u32(emr_eof.type, 0x0000000e); /* EMR_EOF */
686 out_le_u32(emr_eof.size, 20); /* Assuming no palette entries */
687 out_le_u32(emr_eof.n_pal_entries, 0);
688 out_le_u32(emr_eof.off_pal_entries, 0);
689 emr_eof.size_last = emr_eof.size;
690 bytecount += 20;
691 recordcount++;
692
693 if (symbol->symbology == BARCODE_MAXICODE) {
694 bytecount += 5 * sizeof(emr_selectobject_t);
695 recordcount += 5;
696 }
697
698 /* Put final counts in header */
699 out_le_u32(emr_header.emf_header.bytes, bytecount);
700 out_le_u32(emr_header.emf_header.records, recordcount);
701
702 /* Send EMF data to file */
703 if (!fm_open(fmp, symbol, "wb")) {
704 return errtxtf(ZINT_ERROR_FILE_ACCESS, symbol, 640, "Could not open EMF output file (%1$d: %2$s)", fmp->err,
705 strerror(fmp->err));
706 }
707
708 fm_write(&emr_header, sizeof(emr_header_t), 1, fmp);
709
710 fm_write(&emr_mapmode, sizeof(emr_mapmode_t), 1, fmp);
711
712 if (rotate_angle) {
713 fm_write(&emr_setworldtransform, sizeof(emr_setworldtransform_t), 1, fmp);
714 }
715
716 fm_write(&emr_createbrushindirect_bg, sizeof(emr_createbrushindirect_t), 1, fmp);
717
718 if (symbol->symbology == BARCODE_ULTRA) {
719 for (i = 0; i < 9; i++) {
720 if (rectangle_bycolour[i]) {
721 fm_write(&emr_createbrushindirect_colour[i], sizeof(emr_createbrushindirect_t), 1, fmp);
722 }
723 }
724 } else {
725 fm_write(&emr_createbrushindirect_fg, sizeof(emr_createbrushindirect_t), 1, fmp);
726 }
727
728 fm_write(&emr_createpen, sizeof(emr_createpen_t), 1, fmp);
729
730 if (symbol->vector->strings) {
731 fm_write(&emr_extcreatefontindirectw, sizeof(emr_extcreatefontindirectw_t), 1, fmp);
732 if (fsize2) {
733 fm_write(&emr_extcreatefontindirectw2, sizeof(emr_extcreatefontindirectw_t), 1, fmp);
734 }
735 }
736
737 fm_write(&emr_selectobject_bgbrush, sizeof(emr_selectobject_t), 1, fmp);
738 fm_write(&emr_selectobject_pen, sizeof(emr_selectobject_t), 1, fmp);
739 if (draw_background) {
740 fm_write(&background, sizeof(emr_rectangle_t), 1, fmp);
741 }
742
743 if (symbol->symbology == BARCODE_ULTRA) {
744 for (i = 0; i < 9; i++) {
745 if (rectangle_bycolour[i]) {
746 fm_write(&emr_selectobject_colour[i], sizeof(emr_selectobject_t), 1, fmp);
747
748 rect = symbol->vector->rectangles;
749 this_rectangle = 0;
750 while (rect) {
751 if ((i == 0 && rect->colour == -1) || rect->colour == i) {
752 fm_write(&rectangle[this_rectangle], sizeof(emr_rectangle_t), 1, fmp);
753 }
754 this_rectangle++;
755 rect = rect->next;
756 }
757 }
758 }
759 } else {
760 fm_write(&emr_selectobject_fgbrush, sizeof(emr_selectobject_t), 1, fmp);
761
762 /* Rectangles */
763 for (i = 0; i < rectangle_count; i++) {
764 fm_write(&rectangle[i], sizeof(emr_rectangle_t), 1, fmp);
765 }
766 }
767
768 /* Hexagons */
769 for (i = 0; i < hexagon_count; i++) {
770 fm_write(&hexagon[i], sizeof(emr_polygon_t), 1, fmp);
771 }
772
773 /* Circles */
774 if (symbol->symbology == BARCODE_MAXICODE) {
775 /* Bullseye needed */
776 for (i = 0; i < circle_count; i++) {
777 fm_write(&circle[i], sizeof(emr_ellipse_t), 1, fmp);
778 if (i < circle_count - 1) {
779 if (i % 2) {
780 fm_write(&emr_selectobject_fgbrush, sizeof(emr_selectobject_t), 1, fmp);
781 } else {
782 fm_write(&emr_selectobject_bgbrush, sizeof(emr_selectobject_t), 1, fmp);
783 }
784 }
785 }
786 } else {
787 for (i = 0; i < circle_count; i++) {
788 fm_write(&circle[i], sizeof(emr_ellipse_t), 1, fmp);
789 }
790 }
791
792 /* Text */
793 if (string_count > 0) {
794 fm_write(&emr_selectobject_font, sizeof(emr_selectobject_t), 1, fmp);
795 fm_write(&emr_settextcolor, sizeof(emr_settextcolor_t), 1, fmp);
796 }
797
798 current_fsize = fsize;
799 current_halign = -1;
800 for (i = 0; i < string_count; i++) {
801 if (text_fsizes[i] != current_fsize) {
802 current_fsize = text_fsizes[i];
803 fm_write(&emr_selectobject_font2, sizeof(emr_selectobject_t), 1, fmp);
804 }
805 if (text_haligns[i] != current_halign) {
806 current_halign = text_haligns[i];
807 if (current_halign == 0) {
808 fm_write(&emr_settextalign_centre, sizeof(emr_settextalign_t), 1, fmp);
809 } else if (current_halign == 1) {
810 fm_write(&emr_settextalign_left, sizeof(emr_settextalign_t), 1, fmp);
811 } else {
812 fm_write(&emr_settextalign_right, sizeof(emr_settextalign_t), 1, fmp);
813 }
814 }
815 fm_write(&text[i], sizeof(emr_exttextoutw_t), 1, fmp);
816 fm_write(this_string[i], text_bumped_lens[i], 1, fmp);
817 free(this_string[i]);
818 }
819
820 fm_write(&emr_eof, sizeof(emr_eof_t), 1, fmp);
821
822 if (fm_error(fmp)) {
823 errtxtf(0, symbol, 644, "Incomplete write of EMF output (%1$d: %2$s)", fmp->err, strerror(fmp->err));
824 (void) fm_close(fmp, symbol);
825 return ZINT_ERROR_FILE_WRITE;
826 }
827
828 if (!fm_close(fmp, symbol)) {
829 return errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 941, "Failure on closing EMF output file (%1$d: %2$s)",
830 fmp->err, strerror(fmp->err));
831 }
832 return error_number;
833 }
834
835 /* vim: set ts=4 sw=4 et : */