Mercurial > hgrepos > Python2 > PyMuPDF
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/zint/backend/emf.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,835 @@ +/* emf.c - Support for Microsoft Enhanced Metafile Format */ +/* + libzint - the open source barcode library + Copyright (C) 2016-2024 Robin Stuart <rstuart114@gmail.com> + + 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. + */ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* Developed according to [MS-EMF] - v20160714, Released July 14, 2016 + * and [MS-WMF] - v20160714, Released July 14, 2016 */ + +#include <errno.h> +#include <stdio.h> +#include <assert.h> +#include <math.h> +#include "common.h" +#include "filemem.h" +#include "output.h" +#include "emf.h" + +/* Multiply truncating to 3 decimal places (avoids rounding differences on various platforms) */ +#define emf_mul3dpf(m, arg) stripf(roundf((m) * (arg) * 1000.0f) / 1000.0f) + +static int emf_count_rectangles(const struct zint_symbol *symbol) { + int rectangles = 0; + const struct zint_vector_rect *rect; + + rect = symbol->vector->rectangles; + while (rect) { + rectangles++; + rect = rect->next; + } + + return rectangles; +} + +static int emf_count_circles(const struct zint_symbol *symbol) { + int circles = 0; + const struct zint_vector_circle *circ; + + circ = symbol->vector->circles; + while (circ) { + circles++; + circ = circ->next; + } + + /* Hack for MaxiCode */ + return symbol->symbology == BARCODE_MAXICODE ? circles * 2 : circles; +} + +static int emf_count_hexagons(const struct zint_symbol *symbol) { + int hexagons = 0; + const struct zint_vector_hexagon *hex; + + hex = symbol->vector->hexagons; + while (hex) { + hexagons++; + hex = hex->next; + } + + return hexagons; +} + +static int emf_count_strings(const struct zint_symbol *symbol, float *fsize, float *fsize2, int *halign_left, + int *halign_right) { + int strings = 0; + const struct zint_vector_string *string; + + *fsize = *fsize2 = 0.0f; + *halign_left = *halign_right = 0; + + string = symbol->vector->strings; + while (string) { + /* Allow 2 font sizes */ + if (*fsize == 0.0f) { + *fsize = string->fsize; + } else if (string->fsize != *fsize && *fsize2 == 0.0f) { + *fsize2 = string->fsize; + } + /* Only 3 haligns possible and centre align always assumed used */ + if (string->halign) { /* Left or right align */ + if (string->halign == 1) { /* Left align */ + *halign_left = string->halign; + } else { /* Right align */ + *halign_right = string->halign; + } + } + strings++; + string = string->next; + } + + return strings; +} + +/* Convert UTF-8 to UTF-16LE - only needs to handle characters <= U+00FF */ +static void emf_utfle_copy(unsigned char *output, const unsigned char *input, const int length) { + int i; + int o; + + i = 0; + o = 0; + do { + if (input[i] <= 0x7f) { + /* 1 byte mode (7-bit ASCII) */ + output[o] = input[i]; + output[o + 1] = 0x00; + o += 2; + i++; + } else { + /* 2 byte mode */ + output[o] = ((input[i] & 0x1f) << 6) + (input[i + 1] & 0x3f); + output[o + 1] = 0x00; + o += 2; + i += 2; + } + } while (i < length); +} + +/* Strings length must be a multiple of 4 bytes */ +static int emf_bump_up(const int input) { + return (input + (input & 1)) << 1; +} + +static int emf_utfle_length(const unsigned char *input, const int length) { + int result = 0; + int i; + + for (i = 0; i < length; i++) { + result++; + if (input[i] >= 0x80) { /* 2 byte UTF-8 counts as one UTF-16LE character */ + i++; + } + } + + return result; +} + +INTERNAL int emf_plot(struct zint_symbol *symbol, int rotate_angle) { + int i; + struct filemem fm; + struct filemem *const fmp = &fm; + unsigned char fgred, fggrn, fgblu, bgred, bggrn, bgblu, bgalpha; + int error_number = 0; + int rectangle_count, this_rectangle; + int circle_count, this_circle; + int hexagon_count, this_hexagon; + int string_count, this_text; + int bytecount, recordcount; + float previous_diameter; + float radius, half_radius, half_sqrt3_radius; + int colours_used = 0; + int rectangle_bycolour[9] = {0}; + + int width, height; + int bounds_pxx, bounds_pxy; /* Pixels */ + int frame_cmmx, frame_cmmy; /* Hundredths of a mm, i.e. "centi-millimeters" */ + int device_pxx, device_pxy; /* Pixels */ + int mmx, mmy; /* Millimeters */ + int micronx, microny; /* Micrometers */ + const float dpmm = symbol->dpmm ? stripf(symbol->dpmm) + : ZBarcode_XdimDp_From_Scale(symbol->symbology, symbol->scale, + ZBarcode_Default_Xdim(symbol->symbology), "EMF"); + const int sideways = rotate_angle == 90 || rotate_angle == 270; + + int draw_background = 1; + int bold; + const int upcean = is_upcean(symbol->symbology); + + struct zint_vector_rect *rect; + struct zint_vector_circle *circ; + struct zint_vector_hexagon *hex; + struct zint_vector_string *string; + + /* Allow for up to 6 strings (current max 3 for UPC/EAN) */ + unsigned char *this_string[6]; + emr_exttextoutw_t text[6]; + float text_fsizes[6]; + int text_haligns[6]; + int text_bumped_lens[6]; + + emr_header_t emr_header; + emr_eof_t emr_eof; + emr_mapmode_t emr_mapmode; + emr_setworldtransform_t emr_setworldtransform; + emr_createbrushindirect_t emr_createbrushindirect_fg; + emr_createbrushindirect_t emr_createbrushindirect_bg; + emr_createbrushindirect_t emr_createbrushindirect_colour[9]; /* Used for colour symbols only */ + emr_selectobject_t emr_selectobject_fgbrush; + emr_selectobject_t emr_selectobject_bgbrush; + emr_selectobject_t emr_selectobject_colour[9]; /* Used for colour symbols only */ + emr_createpen_t emr_createpen; + emr_selectobject_t emr_selectobject_pen; + emr_rectangle_t background; + emr_settextcolor_t emr_settextcolor; + + float fsize; + emr_extcreatefontindirectw_t emr_extcreatefontindirectw; + emr_selectobject_t emr_selectobject_font; + float fsize2; + emr_extcreatefontindirectw_t emr_extcreatefontindirectw2; + emr_selectobject_t emr_selectobject_font2; + emr_settextalign_t emr_settextalign_centre; /* Centre align */ + int halign_left; /* Set if left halign used */ + emr_settextalign_t emr_settextalign_left; + int halign_right; /* Set if right halign used */ + emr_settextalign_t emr_settextalign_right; + + float current_fsize; + int current_halign; + + emr_rectangle_t *rectangle; + emr_ellipse_t *circle; + emr_polygon_t *hexagon; + + const int ih_ultra_offset = symbol->symbology == BARCODE_ULTRA ? 8 : 0; + + if (symbol->vector == NULL) { + return errtxt(ZINT_ERROR_INVALID_DATA, symbol, 643, "Vector header NULL"); + } + + (void) out_colour_get_rgb(symbol->fgcolour, &fgred, &fggrn, &fgblu, NULL /*alpha*/); + (void) out_colour_get_rgb(symbol->bgcolour, &bgred, &bggrn, &bgblu, &bgalpha); + if (bgalpha == 0) { + draw_background = 0; + } + + rectangle_count = emf_count_rectangles(symbol); + circle_count = emf_count_circles(symbol); + hexagon_count = emf_count_hexagons(symbol); + string_count = emf_count_strings(symbol, &fsize, &fsize2, &halign_left, &halign_right); + + /* Avoid sanitize runtime error by making always non-zero */ + rectangle = (emr_rectangle_t *) z_alloca(sizeof(emr_rectangle_t) * (rectangle_count ? rectangle_count : 1)); + circle = (emr_ellipse_t *) z_alloca(sizeof(emr_ellipse_t) * (circle_count ? circle_count : 1)); + hexagon = (emr_polygon_t *) z_alloca(sizeof(emr_polygon_t) * (hexagon_count ? hexagon_count : 1)); + + /* Calculate how many coloured rectangles */ + if (symbol->symbology == BARCODE_ULTRA) { + + rect = symbol->vector->rectangles; + while (rect) { + if (rect->colour == -1) { /* Foreground colour */ + if (rectangle_bycolour[0] == 0) { + colours_used++; + rectangle_bycolour[0] = 1; + } + } else { + if (rectangle_bycolour[rect->colour] == 0) { + colours_used++; + rectangle_bycolour[rect->colour] = 1; + } + } + rect = rect->next; + } + } + + width = (int) ceilf(symbol->vector->width); + height = (int) ceilf(symbol->vector->height); + + bounds_pxx = width - 1; /* Following Inkscape, bounds "inclusive-inclusive", so size 1 less */ + bounds_pxy = height - 1; + device_pxx = width; /* device */ + device_pxy = height; + + if (dpmm) { + frame_cmmx = (int) roundf(stripf(width * 100.0f / dpmm)) - 1; /* frame also "inclusive-inclusive" */ + frame_cmmy = (int) roundf(stripf(height * 100.0f / dpmm)) - 1; + mmx = (int) roundf(stripf(width / dpmm)); /* millimeters */ + mmy = (int) roundf(stripf(height / dpmm)); + micronx = (int) roundf(stripf(width * 1000.0f / dpmm)); /* micrometers */ + microny = (int) roundf(stripf(height * 1000.0f / dpmm)); + } else { /* Should only happen if `symbol->scale` zero. */ + frame_cmmx = (int) roundf(stripf(width * 100.0f)) - 1; + frame_cmmy = (int) roundf(stripf(height * 100.0f)) - 1; + mmx = (int) roundf(stripf(width)); + mmy = (int) roundf(stripf(height)); + micronx = (int) roundf(stripf(width * 1000.0f)); + microny = (int) roundf(stripf(height * 1000.0f)); + } + + /* Header */ + out_le_u32(emr_header.type, 0x00000001); /* EMR_HEADER */ + out_le_u32(emr_header.size, 108); /* Including extensions */ + out_le_i32(emr_header.emf_header.bounds.left, 0); + out_le_i32(emr_header.emf_header.bounds.top, 0); + out_le_i32(emr_header.emf_header.bounds.right, sideways ? bounds_pxy : bounds_pxx); + out_le_i32(emr_header.emf_header.bounds.bottom, sideways ? bounds_pxx : bounds_pxy); + out_le_i32(emr_header.emf_header.frame.left, 0); + out_le_i32(emr_header.emf_header.frame.top, 0); + out_le_i32(emr_header.emf_header.frame.right, sideways ? frame_cmmy : frame_cmmx); + out_le_i32(emr_header.emf_header.frame.bottom, sideways ? frame_cmmx : frame_cmmy); + out_le_u32(emr_header.emf_header.record_signature, 0x464d4520); /* ENHMETA_SIGNATURE */ + out_le_u32(emr_header.emf_header.version, 0x00010000); + out_le_u16(emr_header.emf_header.handles, (fsize2 != 0.0f ? 5 : 4) + ih_ultra_offset); /* No. of graphics objs */ + out_le_u16(emr_header.emf_header.reserved, 0x0000); + out_le_u32(emr_header.emf_header.n_description, 0); + out_le_u32(emr_header.emf_header.off_description, 0); + out_le_u32(emr_header.emf_header.n_pal_entries, 0); + out_le_u32(emr_header.emf_header.device.cx, sideways ? device_pxy : device_pxx); + out_le_u32(emr_header.emf_header.device.cy, sideways ? device_pxx : device_pxy); + out_le_u32(emr_header.emf_header.millimeters.cx, sideways ? mmy : mmx); + out_le_u32(emr_header.emf_header.millimeters.cy, sideways ? mmx : mmy); + /* HeaderExtension1 */ + out_le_u32(emr_header.emf_header.cb_pixel_format, 0x0000); /* None set */ + out_le_u32(emr_header.emf_header.off_pixel_format, 0x0000); /* None set */ + out_le_u32(emr_header.emf_header.b_open_gl, 0x0000); /* OpenGL not present */ + /* HeaderExtension2 */ + out_le_u32(emr_header.emf_header.micrometers.cx, sideways ? microny : micronx); + out_le_u32(emr_header.emf_header.micrometers.cy, sideways ? micronx : microny); + bytecount = 108; + recordcount = 1; + + out_le_u32(emr_mapmode.type, 0x00000011); /* EMR_SETMAPMODE */ + out_le_u32(emr_mapmode.size, 12); + out_le_u32(emr_mapmode.mapmode, 0x01); /* MM_TEXT */ + bytecount += 12; + recordcount++; + + if (rotate_angle) { + out_le_u32(emr_setworldtransform.type, 0x00000023); /* EMR_SETWORLDTRANSFORM */ + out_le_u32(emr_setworldtransform.size, 32); + out_le_float(emr_setworldtransform.m11, rotate_angle == 90 ? 0.0f : rotate_angle == 180 ? -1.0f : 0.0f); + out_le_float(emr_setworldtransform.m12, rotate_angle == 90 ? 1.0f : rotate_angle == 180 ? 0.0f : -1.0f); + out_le_float(emr_setworldtransform.m21, rotate_angle == 90 ? -1.0f : rotate_angle == 180 ? 0.0f : 1.0f); + out_le_float(emr_setworldtransform.m22, rotate_angle == 90 ? 0.0f : rotate_angle == 180 ? -1.0f : 0.0f); + out_le_float(emr_setworldtransform.dx, rotate_angle == 90 ? height : rotate_angle == 180 ? width : 0.0f); + out_le_float(emr_setworldtransform.dy, rotate_angle == 90 ? 0.0f : rotate_angle == 180 ? height : width); + bytecount += 32; + recordcount++; + } + + /* Create Brushes */ + out_le_u32(emr_createbrushindirect_bg.type, 0x00000027); /* EMR_CREATEBRUSHINDIRECT */ + out_le_u32(emr_createbrushindirect_bg.size, 24); + out_le_u32(emr_createbrushindirect_bg.ih_brush, 0); + out_le_u32(emr_createbrushindirect_bg.log_brush.brush_style, 0x0000); /* BS_SOLID */ + emr_createbrushindirect_bg.log_brush.color.red = bgred; + emr_createbrushindirect_bg.log_brush.color.green = bggrn; + emr_createbrushindirect_bg.log_brush.color.blue = bgblu; + emr_createbrushindirect_bg.log_brush.color.reserved = 0; + out_le_u32(emr_createbrushindirect_bg.log_brush.brush_hatch, 0x0006); /* HS_SOLIDCLR */ + bytecount += 24; + recordcount++; + + if (symbol->symbology == BARCODE_ULTRA) { + static const char ultra_chars[] = { '0', 'C', 'B', 'M', 'R', 'Y', 'G', 'K', 'W' }; + for (i = 0; i < 9; i++) { + out_le_u32(emr_createbrushindirect_colour[i].type, 0x00000027); /* EMR_CREATEBRUSHINDIRECT */ + out_le_u32(emr_createbrushindirect_colour[i].size, 24); + out_le_u32(emr_createbrushindirect_colour[i].ih_brush, 1 + i); + out_le_u32(emr_createbrushindirect_colour[i].log_brush.brush_style, 0x0000); /* BS_SOLID */ + if (i == 0) { + emr_createbrushindirect_colour[i].log_brush.color.red = fgred; + emr_createbrushindirect_colour[i].log_brush.color.green = fggrn; + emr_createbrushindirect_colour[i].log_brush.color.blue = fgblu; + } else { + out_colour_char_to_rgb(ultra_chars[i], + &emr_createbrushindirect_colour[i].log_brush.color.red, + &emr_createbrushindirect_colour[i].log_brush.color.green, + &emr_createbrushindirect_colour[i].log_brush.color.blue); + } + emr_createbrushindirect_colour[i].log_brush.color.reserved = 0; + out_le_u32(emr_createbrushindirect_colour[i].log_brush.brush_hatch, 0x0006); /* HS_SOLIDCLR */ + } + bytecount += colours_used * 24; + recordcount += colours_used; + } else { + out_le_u32(emr_createbrushindirect_fg.type, 0x00000027); /* EMR_CREATEBRUSHINDIRECT */ + out_le_u32(emr_createbrushindirect_fg.size, 24); + out_le_u32(emr_createbrushindirect_fg.ih_brush, 1); + out_le_u32(emr_createbrushindirect_fg.log_brush.brush_style, 0x0000); /* BS_SOLID */ + emr_createbrushindirect_fg.log_brush.color.red = fgred; + emr_createbrushindirect_fg.log_brush.color.green = fggrn; + emr_createbrushindirect_fg.log_brush.color.blue = fgblu; + emr_createbrushindirect_fg.log_brush.color.reserved = 0; + out_le_u32(emr_createbrushindirect_fg.log_brush.brush_hatch, 0x0006); /* HS_SOLIDCLR */ + bytecount += 24; + recordcount++; + } + + out_le_u32(emr_selectobject_bgbrush.type, 0x00000025); /* EMR_SELECTOBJECT */ + out_le_u32(emr_selectobject_bgbrush.size, 12); + emr_selectobject_bgbrush.ih_object = emr_createbrushindirect_bg.ih_brush; + bytecount += 12; + recordcount++; + + if (symbol->symbology == BARCODE_ULTRA) { + for (i = 0; i < 9; i++) { + out_le_u32(emr_selectobject_colour[i].type, 0x00000025); /* EMR_SELECTOBJECT */ + out_le_u32(emr_selectobject_colour[i].size, 12); + emr_selectobject_colour[i].ih_object = emr_createbrushindirect_colour[i].ih_brush; + } + bytecount += colours_used * 12; + recordcount += colours_used; + } else { + out_le_u32(emr_selectobject_fgbrush.type, 0x00000025); /* EMR_SELECTOBJECT */ + out_le_u32(emr_selectobject_fgbrush.size, 12); + emr_selectobject_fgbrush.ih_object = emr_createbrushindirect_fg.ih_brush; + bytecount += 12; + recordcount++; + } + + /* Create Pens */ + out_le_u32(emr_createpen.type, 0x00000026); /* EMR_CREATEPEN */ + out_le_u32(emr_createpen.size, 28); + out_le_u32(emr_createpen.ih_pen, 2 + ih_ultra_offset); + out_le_u32(emr_createpen.log_pen.pen_style, 0x00000005); /* PS_NULL */ + out_le_i32(emr_createpen.log_pen.width.x, 1); + out_le_i32(emr_createpen.log_pen.width.y, 0); /* ignored */ + emr_createpen.log_pen.color_ref.red = 0; + emr_createpen.log_pen.color_ref.green = 0; + emr_createpen.log_pen.color_ref.blue = 0; + emr_createpen.log_pen.color_ref.reserved = 0; + bytecount += 28; + recordcount++; + + out_le_u32(emr_selectobject_pen.type, 0x00000025); /* EMR_SELECTOBJECT */ + out_le_u32(emr_selectobject_pen.size, 12); + emr_selectobject_pen.ih_object = emr_createpen.ih_pen; + bytecount += 12; + recordcount++; + + if (draw_background) { + /* Make background from a rectangle */ + out_le_u32(background.type, 0x0000002b); /* EMR_RECTANGLE */ + out_le_u32(background.size, 24); + out_le_i32(background.box.top, 0); + out_le_i32(background.box.left, 0); + out_le_i32(background.box.right, width); + out_le_i32(background.box.bottom, height); + bytecount += 24; + recordcount++; + } + + /* Rectangles */ + rect = symbol->vector->rectangles; + this_rectangle = 0; + while (rect) { + out_le_u32(rectangle[this_rectangle].type, 0x0000002b); /* EMR_RECTANGLE */ + out_le_u32(rectangle[this_rectangle].size, 24); + out_le_i32(rectangle[this_rectangle].box.top, rect->y); + out_le_i32(rectangle[this_rectangle].box.bottom, stripf(rect->y + rect->height)); + out_le_i32(rectangle[this_rectangle].box.left, rect->x); + out_le_i32(rectangle[this_rectangle].box.right, stripf(rect->x + rect->width)); + this_rectangle++; + bytecount += 24; + recordcount++; + rect = rect->next; + } + + /* Circles */ + previous_diameter = radius = 0.0f; + circ = symbol->vector->circles; + this_circle = 0; + while (circ) { + /* Note using circle width the proper way, with a non-null pen of specified width and a null brush for fill, + causes various different rendering issues for LibreOffice Draw and Inkscape, so using following hack */ + if (previous_diameter != circ->diameter + circ->width) { /* Drawing MaxiCode bullseye using overlayed discs */ + previous_diameter = circ->diameter + circ->width; + radius = emf_mul3dpf(0.5f, previous_diameter); + } + out_le_u32(circle[this_circle].type, 0x0000002a); /* EMR_ELLIPSE */ + out_le_u32(circle[this_circle].size, 24); + out_le_i32(circle[this_circle].box.top, stripf(circ->y - radius)); + out_le_i32(circle[this_circle].box.bottom, stripf(circ->y + radius)); + out_le_i32(circle[this_circle].box.left, stripf(circ->x - radius)); + out_le_i32(circle[this_circle].box.right, stripf(circ->x + radius)); + this_circle++; + bytecount += 24; + recordcount++; + + if (symbol->symbology == BARCODE_MAXICODE) { /* Drawing MaxiCode bullseye using overlayed discs */ + float inner_radius = radius - circ->width; + out_le_u32(circle[this_circle].type, 0x0000002a); /* EMR_ELLIPSE */ + out_le_u32(circle[this_circle].size, 24); + out_le_i32(circle[this_circle].box.top, stripf(circ->y - inner_radius)); + out_le_i32(circle[this_circle].box.bottom, stripf(circ->y + inner_radius)); + out_le_i32(circle[this_circle].box.left, stripf(circ->x - inner_radius)); + out_le_i32(circle[this_circle].box.right, stripf(circ->x + inner_radius)); + this_circle++; + bytecount += 24; + recordcount++; + } + + circ = circ->next; + } + + /* Hexagons */ + previous_diameter = radius = half_radius = half_sqrt3_radius = 0.0f; + hex = symbol->vector->hexagons; + this_hexagon = 0; + while (hex) { + out_le_u32(hexagon[this_hexagon].type, 0x00000003); /* EMR_POLYGON */ + out_le_u32(hexagon[this_hexagon].size, 76); + out_le_u32(hexagon[this_hexagon].count, 6); + + if (previous_diameter != hex->diameter) { + previous_diameter = hex->diameter; + radius = emf_mul3dpf(0.5f, previous_diameter); + half_radius = emf_mul3dpf(0.25f, previous_diameter); + half_sqrt3_radius = emf_mul3dpf(0.43301270189221932338f, previous_diameter); + } + + /* Note rotation done via world transform */ + out_le_i32(hexagon[this_hexagon].a_points_a.x, hex->x); + out_le_i32(hexagon[this_hexagon].a_points_a.y, stripf(hex->y + radius)); + out_le_i32(hexagon[this_hexagon].a_points_b.x, stripf(hex->x + half_sqrt3_radius)); + out_le_i32(hexagon[this_hexagon].a_points_b.y, stripf(hex->y + half_radius)); + out_le_i32(hexagon[this_hexagon].a_points_c.x, stripf(hex->x + half_sqrt3_radius)); + out_le_i32(hexagon[this_hexagon].a_points_c.y, stripf(hex->y - half_radius)); + out_le_i32(hexagon[this_hexagon].a_points_d.x, hex->x); + out_le_i32(hexagon[this_hexagon].a_points_d.y, stripf(hex->y - radius)); + out_le_i32(hexagon[this_hexagon].a_points_e.x, stripf(hex->x - half_sqrt3_radius)); + out_le_i32(hexagon[this_hexagon].a_points_e.y, stripf(hex->y - half_radius)); + out_le_i32(hexagon[this_hexagon].a_points_f.x, stripf(hex->x - half_sqrt3_radius)); + out_le_i32(hexagon[this_hexagon].a_points_f.y, stripf(hex->y + half_radius)); + + hexagon[this_hexagon].bounds.top = hexagon[this_hexagon].a_points_d.y; + hexagon[this_hexagon].bounds.bottom = hexagon[this_hexagon].a_points_a.y; + hexagon[this_hexagon].bounds.left = hexagon[this_hexagon].a_points_e.x; + hexagon[this_hexagon].bounds.right = hexagon[this_hexagon].a_points_c.x; + this_hexagon++; + bytecount += 76; + recordcount++; + hex = hex->next; + } + + /* Create font records, alignment records and text color */ + if (symbol->vector->strings) { + bold = (symbol->output_options & BOLD_TEXT) && !is_upcean(symbol->symbology); + memset(&emr_extcreatefontindirectw, 0, sizeof(emr_extcreatefontindirectw)); + out_le_u32(emr_extcreatefontindirectw.type, 0x00000052); /* EMR_EXTCREATEFONTINDIRECTW */ + out_le_u32(emr_extcreatefontindirectw.size, 104); + out_le_u32(emr_extcreatefontindirectw.ih_fonts, 3 + ih_ultra_offset); + out_le_i32(emr_extcreatefontindirectw.elw.height, fsize); + out_le_i32(emr_extcreatefontindirectw.elw.width, 0); /* automatic */ + out_le_i32(emr_extcreatefontindirectw.elw.weight, bold ? 700 : 400); + emr_extcreatefontindirectw.elw.char_set = 0x00; /* ANSI_CHARSET */ + emr_extcreatefontindirectw.elw.out_precision = 0x00; /* OUT_DEFAULT_PRECIS */ + emr_extcreatefontindirectw.elw.clip_precision = 0x00; /* CLIP_DEFAULT_PRECIS */ + emr_extcreatefontindirectw.elw.pitch_and_family = 0x02 | (0x02 << 6); /* FF_SWISS | VARIABLE_PITCH */ + emf_utfle_copy(emr_extcreatefontindirectw.elw.facename, (const unsigned char *) "sans-serif", 10); + bytecount += 104; + recordcount++; + + out_le_u32(emr_selectobject_font.type, 0x00000025); /* EMR_SELECTOBJECT */ + out_le_u32(emr_selectobject_font.size, 12); + emr_selectobject_font.ih_object = emr_extcreatefontindirectw.ih_fonts; + bytecount += 12; + recordcount++; + + if (fsize2) { + memcpy(&emr_extcreatefontindirectw2, &emr_extcreatefontindirectw, sizeof(emr_extcreatefontindirectw)); + out_le_u32(emr_extcreatefontindirectw2.ih_fonts, 4 + ih_ultra_offset); + out_le_i32(emr_extcreatefontindirectw2.elw.height, fsize2); + bytecount += 104; + recordcount++; + + out_le_u32(emr_selectobject_font2.type, 0x00000025); /* EMR_SELECTOBJECT */ + out_le_u32(emr_selectobject_font2.size, 12); + emr_selectobject_font2.ih_object = emr_extcreatefontindirectw2.ih_fonts; + bytecount += 12; + recordcount++; + } + + /* Note select aligns counted below in strings loop */ + + out_le_u32(emr_settextalign_centre.type, 0x00000016); /* EMR_SETTEXTALIGN */ + out_le_u32(emr_settextalign_centre.size, 12); + out_le_u32(emr_settextalign_centre.text_alignment_mode, 0x0006 | 0x0018); /* TA_CENTER | TA_BASELINE */ + if (halign_left) { + out_le_u32(emr_settextalign_left.type, 0x00000016); /* EMR_SETTEXTALIGN */ + out_le_u32(emr_settextalign_left.size, 12); + out_le_u32(emr_settextalign_left.text_alignment_mode, 0x0000 | 0x0018); /* TA_LEFT | TA_BASELINE */ + } + if (halign_right) { + out_le_u32(emr_settextalign_right.type, 0x00000016); /* EMR_SETTEXTALIGN */ + out_le_u32(emr_settextalign_right.size, 12); + out_le_u32(emr_settextalign_right.text_alignment_mode, 0x0002 | 0x0018); /* TA_RIGHT | TA_BASELINE */ + } + + out_le_u32(emr_settextcolor.type, 0x0000018); /* EMR_SETTEXTCOLOR */ + out_le_u32(emr_settextcolor.size, 12); + emr_settextcolor.color.red = fgred; + emr_settextcolor.color.green = fggrn; + emr_settextcolor.color.blue = fgblu; + emr_settextcolor.color.reserved = 0; + bytecount += 12; + recordcount++; + } + + /* Text */ + this_text = 0; + /* Loop over font sizes so that they're grouped together, so only have to select font twice at most */ + for (i = 0, current_fsize = fsize; i < 2 && current_fsize; i++, current_fsize = fsize2) { + string = symbol->vector->strings; + current_halign = -1; + while (string) { + int utfle_len; + if (string->fsize != current_fsize) { + string = string->next; + continue; + } + text_fsizes[this_text] = string->fsize; + text_haligns[this_text] = string->halign; + if (text_haligns[this_text] != current_halign) { + current_halign = text_haligns[this_text]; + bytecount += 12; + recordcount++; + } + assert(string->length > 0); + utfle_len = emf_utfle_length(string->text, string->length); + text_bumped_lens[this_text] = emf_bump_up(utfle_len); + if (!(this_string[this_text] = (unsigned char *) malloc(text_bumped_lens[this_text]))) { + for (i = 0; i < this_text; i++) { + free(this_string[i]); + } + return errtxt(ZINT_ERROR_MEMORY, symbol, 641, "Insufficient memory for EMF string buffer"); + } + memset(this_string[this_text], 0, text_bumped_lens[this_text]); + out_le_u32(text[this_text].type, 0x00000054); /* EMR_EXTTEXTOUTW */ + out_le_u32(text[this_text].size, 76 + text_bumped_lens[this_text]); + out_le_i32(text[this_text].bounds.top, 0); /* ignored */ + out_le_i32(text[this_text].bounds.left, 0); /* ignored */ + out_le_i32(text[this_text].bounds.right, 0xffffffff); /* ignored */ + out_le_i32(text[this_text].bounds.bottom, 0xffffffff); /* ignored */ + out_le_u32(text[this_text].i_graphics_mode, 0x00000002); /* GM_ADVANCED */ + out_le_float(text[this_text].ex_scale, 1.0f); + out_le_float(text[this_text].ey_scale, 1.0f); + /* Unhack the guard whitespace `gws_left_fudge`/`gws_right_fudge` hack */ + if (upcean && string->halign == 1 && string->text[0] == '<') { + const float gws_left_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale; /* 0.5 * 2 * scale */ + out_le_i32(text[this_text].w_emr_text.reference.x, string->x + gws_left_fudge); + } else if (upcean && string->halign == 2 && string->text[0] == '>') { + const float gws_right_fudge = symbol->scale < 0.1f ? 0.1f : symbol->scale; /* 0.5 * 2 * scale */ + out_le_i32(text[this_text].w_emr_text.reference.x, string->x - gws_right_fudge); + } else { + out_le_i32(text[this_text].w_emr_text.reference.x, string->x); + } + out_le_i32(text[this_text].w_emr_text.reference.y, string->y); + out_le_u32(text[this_text].w_emr_text.chars, utfle_len); + out_le_u32(text[this_text].w_emr_text.off_string, 76); + out_le_u32(text[this_text].w_emr_text.options, 0); + out_le_i32(text[this_text].w_emr_text.rectangle.top, 0); + out_le_i32(text[this_text].w_emr_text.rectangle.left, 0); + out_le_i32(text[this_text].w_emr_text.rectangle.right, 0xffffffff); + out_le_i32(text[this_text].w_emr_text.rectangle.bottom, 0xffffffff); + out_le_u32(text[this_text].w_emr_text.off_dx, 0); + emf_utfle_copy(this_string[this_text], string->text, string->length); + bytecount += 76 + text_bumped_lens[this_text]; + recordcount++; + + this_text++; + string = string->next; + } + } + /* Suppress clang-tidy clang-analyzer-core.UndefinedBinaryOperatorResult warning */ + assert(this_text == string_count); + + /* Create EOF record */ + out_le_u32(emr_eof.type, 0x0000000e); /* EMR_EOF */ + out_le_u32(emr_eof.size, 20); /* Assuming no palette entries */ + out_le_u32(emr_eof.n_pal_entries, 0); + out_le_u32(emr_eof.off_pal_entries, 0); + emr_eof.size_last = emr_eof.size; + bytecount += 20; + recordcount++; + + if (symbol->symbology == BARCODE_MAXICODE) { + bytecount += 5 * sizeof(emr_selectobject_t); + recordcount += 5; + } + + /* Put final counts in header */ + out_le_u32(emr_header.emf_header.bytes, bytecount); + out_le_u32(emr_header.emf_header.records, recordcount); + + /* Send EMF data to file */ + if (!fm_open(fmp, symbol, "wb")) { + return errtxtf(ZINT_ERROR_FILE_ACCESS, symbol, 640, "Could not open EMF output file (%1$d: %2$s)", fmp->err, + strerror(fmp->err)); + } + + fm_write(&emr_header, sizeof(emr_header_t), 1, fmp); + + fm_write(&emr_mapmode, sizeof(emr_mapmode_t), 1, fmp); + + if (rotate_angle) { + fm_write(&emr_setworldtransform, sizeof(emr_setworldtransform_t), 1, fmp); + } + + fm_write(&emr_createbrushindirect_bg, sizeof(emr_createbrushindirect_t), 1, fmp); + + if (symbol->symbology == BARCODE_ULTRA) { + for (i = 0; i < 9; i++) { + if (rectangle_bycolour[i]) { + fm_write(&emr_createbrushindirect_colour[i], sizeof(emr_createbrushindirect_t), 1, fmp); + } + } + } else { + fm_write(&emr_createbrushindirect_fg, sizeof(emr_createbrushindirect_t), 1, fmp); + } + + fm_write(&emr_createpen, sizeof(emr_createpen_t), 1, fmp); + + if (symbol->vector->strings) { + fm_write(&emr_extcreatefontindirectw, sizeof(emr_extcreatefontindirectw_t), 1, fmp); + if (fsize2) { + fm_write(&emr_extcreatefontindirectw2, sizeof(emr_extcreatefontindirectw_t), 1, fmp); + } + } + + fm_write(&emr_selectobject_bgbrush, sizeof(emr_selectobject_t), 1, fmp); + fm_write(&emr_selectobject_pen, sizeof(emr_selectobject_t), 1, fmp); + if (draw_background) { + fm_write(&background, sizeof(emr_rectangle_t), 1, fmp); + } + + if (symbol->symbology == BARCODE_ULTRA) { + for (i = 0; i < 9; i++) { + if (rectangle_bycolour[i]) { + fm_write(&emr_selectobject_colour[i], sizeof(emr_selectobject_t), 1, fmp); + + rect = symbol->vector->rectangles; + this_rectangle = 0; + while (rect) { + if ((i == 0 && rect->colour == -1) || rect->colour == i) { + fm_write(&rectangle[this_rectangle], sizeof(emr_rectangle_t), 1, fmp); + } + this_rectangle++; + rect = rect->next; + } + } + } + } else { + fm_write(&emr_selectobject_fgbrush, sizeof(emr_selectobject_t), 1, fmp); + + /* Rectangles */ + for (i = 0; i < rectangle_count; i++) { + fm_write(&rectangle[i], sizeof(emr_rectangle_t), 1, fmp); + } + } + + /* Hexagons */ + for (i = 0; i < hexagon_count; i++) { + fm_write(&hexagon[i], sizeof(emr_polygon_t), 1, fmp); + } + + /* Circles */ + if (symbol->symbology == BARCODE_MAXICODE) { + /* Bullseye needed */ + for (i = 0; i < circle_count; i++) { + fm_write(&circle[i], sizeof(emr_ellipse_t), 1, fmp); + if (i < circle_count - 1) { + if (i % 2) { + fm_write(&emr_selectobject_fgbrush, sizeof(emr_selectobject_t), 1, fmp); + } else { + fm_write(&emr_selectobject_bgbrush, sizeof(emr_selectobject_t), 1, fmp); + } + } + } + } else { + for (i = 0; i < circle_count; i++) { + fm_write(&circle[i], sizeof(emr_ellipse_t), 1, fmp); + } + } + + /* Text */ + if (string_count > 0) { + fm_write(&emr_selectobject_font, sizeof(emr_selectobject_t), 1, fmp); + fm_write(&emr_settextcolor, sizeof(emr_settextcolor_t), 1, fmp); + } + + current_fsize = fsize; + current_halign = -1; + for (i = 0; i < string_count; i++) { + if (text_fsizes[i] != current_fsize) { + current_fsize = text_fsizes[i]; + fm_write(&emr_selectobject_font2, sizeof(emr_selectobject_t), 1, fmp); + } + if (text_haligns[i] != current_halign) { + current_halign = text_haligns[i]; + if (current_halign == 0) { + fm_write(&emr_settextalign_centre, sizeof(emr_settextalign_t), 1, fmp); + } else if (current_halign == 1) { + fm_write(&emr_settextalign_left, sizeof(emr_settextalign_t), 1, fmp); + } else { + fm_write(&emr_settextalign_right, sizeof(emr_settextalign_t), 1, fmp); + } + } + fm_write(&text[i], sizeof(emr_exttextoutw_t), 1, fmp); + fm_write(this_string[i], text_bumped_lens[i], 1, fmp); + free(this_string[i]); + } + + fm_write(&emr_eof, sizeof(emr_eof_t), 1, fmp); + + if (fm_error(fmp)) { + errtxtf(0, symbol, 644, "Incomplete write of EMF output (%1$d: %2$s)", fmp->err, strerror(fmp->err)); + (void) fm_close(fmp, symbol); + return ZINT_ERROR_FILE_WRITE; + } + + if (!fm_close(fmp, symbol)) { + return errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 941, "Failure on closing EMF output file (%1$d: %2$s)", + fmp->err, strerror(fmp->err)); + } + return error_number; +} + +/* vim: set ts=4 sw=4 et : */
