/* Hey Emacs this is -*- C -*- * $Id: emf.trm,v 1.102 2016/05/06 17:40:08 sfeam Exp $ */ /* GNUPLOT - emf.trm */ /*[ * Copyright 1998, 2004 * * Permission to use, copy, and distribute this software and its * documentation for any purpose with or without fee is hereby granted, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. * * Permission to modify the software is granted, but not the right to * distribute the complete modified source code. Modifications are to * be distributed as patches to the released version. Permission to * distribute binaries produced by compiling modified sources is granted, * provided you * 1. distribute the corresponding source modifications from the * released version in the form of a patch file along with the binaries, * 2. add special version identification to distinguish your version * in addition to the base release version number, * 3. provide your name and address as the primary contact for the * support of your modified version, and * 4. retain our contact information in regard to use of the base * software. * Permission to distribute the released version of the source code along * with corresponding source modifications in the form of a patch file is * granted with same provisions 2 through 4 for binary distributions. * * This software is provided "as is" without express or implied warranty * to the extent permitted by applicable law. ]*/ /* * This file is included by ../term.c and ../docs/termdoc.c. * * This terminal driver supports: * Enhanced Metafile Format * * TODO * * HISTORY * * 5.0 19-May-2014 Karl Ratzsch * - Revised point types to include pentagons * 4.6.1 04-Sep-2012 Shige Takeno * - Revised order of object handling Deselect/Delete/Create/Select * - Defer application of new dash+color style until needed for a new line segment * * 4.6.1 04-Sep-2012 Ethan A Merritt * - Fix dashed line option, but make solid lines the default * - Seek to start of output file after plotting so that successive plots will * overwrite each other cleanly rather than producing a corrupt file. * * 4.5 28-Nov-2010 Ethan A Merritt * - Use the EMR_ELLIPSE primitive to draw point types 6 and 7 * - Switch to using linecap=flat and linejoin=miter by default, * but add option "rounded/butt" to toggle this * * 4.3.1 12-Sep-2008 Ethan A Merritt * - enhanced text mode * - Two variants are here. One uses the TA_UPDATECP mode to track character position. * This works great horizontally, but I could not find a way to introduce a vertical * offset to handle subscripts and superscripts. * - The second variant tracks both x and y by estimating the character width/height. * This causes visible imperfections in the character spacing. * - Rotated enhanced text not yet supported * * 1.0.11 06-Dec-2004 Ethan A Merritt * - implement term->set_color(), term->filled_polygon(), and term->fillbox() * RGB colors supported, but not yet PM3D palettes * 1.0.10 08-Jul-2004 Hans-Bernhard Broeker * - cleaned up to match gnuplot CodeStyle conventions (one line per statement, * even in macro bodies, no meddling with assert()). * - purged K&R definitions * 1.0.9 03-Jun-2004 Stephane Barbaray , Ethan Merritt * - fixed linewidth bug * - all is now really assumed as 1024x768@96dpi, * before it was a mix between 1600x1200@120dpi and 1024x768@96dpi, * so font may now render differently than before... * - pointsize rework (size twice also now) * - HCHAR and VCHAR are more efficiently computed * 1.0.8 06-May-2004 Stephane Barbaray * - fixed to work with MS security patch (kb835732) applied, because MS introduced bugs!!! * - EMR_EXTTEXTOUTW (84) is now EMR_EXTTEXTOUTA (83) * 1.0.7 3-Feb-2003 Ethan A Merritt * - modify text and point color handling to match other terminal types * - FIXME! alignment of rotated text is not correct. * 1.0.6 25-Jul-2002 Ethan A Merritt * - generalized text rotation and justification * 1.0.5 2000/07/20 * - Handles were not freed at all, resulting to resource leaks when viewing on Windows 9x (not on NT4/W2000!!!) * 1.0.4 2000/06/28 * - Emulated dashed vectors are now looking better * - 15 colors * 8 pointstyles = 120 pointtypes * 1.0.3 2000/03/29 * - default font is now Arial 12 * - implemented options (color/mono,dashed/solid,font) * - 15 colors * 5 dashtypes = 75 linetypes * 1.0.2 2000/03/22 * - Polygon and Polyline structures are not working for Windows 9X, I * really don't know why, replaced with lineto/moveto couples... * - Texts are now displayed in GM_Compatible mode because GM_Advanced is * displaying correctly but it does not print correctly with Word97! * - Text centering now works best according to escapement/orientation * - Now there is 8 colors * 5 dashtypes = 40 linetypes * - Successfully Working on Linux Suse 6.1 (x86) * * 1.0.1 2000/03/16 * - Unicode text have be to long aligned in EMF files (exttextoutw) * - Problems with text transparence (SetBkMode was not called) * - Null brush created for *not* filling polygon * * 1.0.0 2000/03/15 * - Only tested on x86 Win32 * * AUTHOR * Stephane Barbaray * Some code based on cgm.trm * * send your comments or suggestions to (gnuplot-info@lists.sourceforge.net). */ #include "driver.h" #ifdef TERM_REGISTER register_term(emf) #endif #ifdef TERM_PROTO TERM_PUBLIC void EMF_options __PROTO((void)); TERM_PUBLIC void EMF_init __PROTO((void)); TERM_PUBLIC void EMF_reset __PROTO((void)); TERM_PUBLIC void EMF_text __PROTO((void)); TERM_PUBLIC void EMF_graphics __PROTO((void)); TERM_PUBLIC void EMF_move __PROTO((unsigned int x, unsigned int y)); TERM_PUBLIC void EMF_dashed_vector __PROTO((unsigned int ux, unsigned int uy)); TERM_PUBLIC void EMF_solid_vector __PROTO((unsigned int ux, unsigned int uy)); TERM_PUBLIC void EMF_linetype __PROTO((int linetype)); TERM_PUBLIC void EMF_dashtype __PROTO((int type, t_dashtype *custom_dash_type)); TERM_PUBLIC void EMF_linecolor __PROTO((int color)); TERM_PUBLIC void EMF_load_dashtype __PROTO((int dashtype)); TERM_PUBLIC void EMF_linewidth __PROTO((double width)); TERM_PUBLIC void EMF_put_text __PROTO((unsigned int x, unsigned int y, const char *str)); TERM_PUBLIC int EMF_text_angle __PROTO((int ang)); TERM_PUBLIC int EMF_justify_text __PROTO((enum JUSTIFY mode)); TERM_PUBLIC void EMF_point __PROTO((unsigned int x, unsigned int y, int number)); TERM_PUBLIC void EMF_set_pointsize __PROTO((double size)); TERM_PUBLIC int EMF_set_font __PROTO((const char *)); TERM_PUBLIC int EMF_make_palette __PROTO((t_sm_palette *palette)); TERM_PUBLIC void EMF_previous_palette __PROTO((void)); TERM_PUBLIC void EMF_set_color __PROTO((t_colorspec *colorspec)); TERM_PUBLIC void EMF_filled_polygon __PROTO((int, gpiPoint *)); TERM_PUBLIC void EMF_fillbox __PROTO((int, unsigned int, unsigned int, unsigned int, unsigned int)); TERM_PUBLIC void EMF_flush_dashtype __PROTO((void)); /* Enhanced text support */ TERM_PUBLIC void ENHemf_put_text __PROTO((unsigned int x, unsigned int y, const char *str)); TERM_PUBLIC void ENHemf_OPEN __PROTO((char * fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint)); TERM_PUBLIC void ENHemf_FLUSH __PROTO((void)); #undef RGB #define RGB(r,g,b) ((long) \ (((unsigned char)(r) \ | ((short) ((unsigned char) (g)) << 8)) \ | (((long) (unsigned char) (b)) << 16))) #ifndef GPMIN # define GPMIN(a,b) (a < b ? a : b) #endif #ifndef GPMAX # define GPMAX(a,b) (a > b ? a : b) #endif #define EMF_PX2HM 26.37 #define EMF_PT2HM 35.28 #define EMF_10THDEG2RAD (3.14159265359/1800) #define EMF_XMAX (1024 * EMF_PX2HM) #define EMF_YMAX (768 * EMF_PX2HM) #define EMF_HTIC (EMF_XMAX / 160) #define EMF_VTIC EMF_HTIC #define EMF_FONTNAME "Arial" #define EMF_FONTSIZE 12 #define EMF_HCHAR ((EMF_FONTSIZE * EMF_PT2HM) * 0.6) #define EMF_VCHAR ((EMF_FONTSIZE * EMF_PT2HM) * 1.3) #define EMF_LINE_TYPES 5 /* number of line types we support */ #define EMF_COLORS 15 /* number of colors we support */ #define EMF_POINTS 15 /* number of markers we support */ #define EMF_MAX_SEGMENTS 104 /* maximum # polyline coordinates */ #define EMF_HANDLE_PEN 1 #define EMF_HANDLE_FONT 2 #define EMF_HANDLE_BRUSH 3 #define EMF_HANDLE_MAX 4 /* typedef enum { EMF_PS_COSMETIC = 0x00000000, EMF_PS_ENDCAP_ROUND = 0x00000000, EMF_PS_JOIN_ROUND = 0x00000000, EMF_PS_SOLID = 0x00000000, EMF_PS_DASH = 0x00000001, EMF_PS_DOT = 0x00000002, EMF_PS_DASHDOT = 0x00000003, EMF_PS_DASHDOTDOT = 0x00000004, EMF_PS_NULL = 0x00000005, EMF_PS_INSIDEFRAME = 0x00000006, EMF_PS_USERSTYLE = 0x00000007, EMF_PS_ALTERNATE = 0x00000008, EMF_PS_ENDCAP_SQUARE = 0x00000100, EMF_PS_ENDCAP_FLAT = 0x00000200, EMF_PS_JOIN_BEVEL = 0x00001000, EMF_PS_JOIN_MITER = 0x00002000, EMF_PS_GEOMETRIC = 0x00010000 } EMF_PenStyle; */ #define EMF_STOCK_OBJECT_FLAG ((unsigned long)0x1 << 31) #define EMF_STOCK_OBJECT_WHITE_BRUSH (EMF_STOCK_OBJECT_FLAG + 0x00) #define EMF_STOCK_OBJECT_BLACK_PEN (EMF_STOCK_OBJECT_FLAG + 0x07) #define EMF_STOCK_OBJECT_DEFAULT_FONT (EMF_STOCK_OBJECT_FLAG + 0x0A) #define EMF_write_emr(type, size) { \ EMF_write_long(type); \ EMF_write_long(size); \ emf_record_count++; \ } #define EMF_write_sizel(width, height) { \ EMF_write_long(width); \ EMF_write_long(height); \ } #define EMF_write_points(x, y) { \ EMF_write_short(x); \ EMF_write_short(y); \ } #define EMF_write_pointl(x, y) { \ EMF_write_long(x); \ EMF_write_long(y); \ } #define EMF_write_rectl(left, top, right, bottom) { \ EMF_write_long(left); \ EMF_write_long(top); \ EMF_write_long(right); \ EMF_write_long(bottom); \ } #define EMF_EOF() { \ EMF_write_emr(14, 0x14); \ EMF_write_long(0); \ EMF_write_long(0x10); \ EMF_write_long(20); \ } #define EMF_SetMapMode(mode) { \ EMF_write_emr(17, 0x0C); \ EMF_write_long(mode); \ } #define EMF_SetWindowExtEx(width, height) { \ EMF_write_emr(9, 0x10); \ EMF_write_sizel(width, height); \ } #define EMF_SetWindowOrgEx(width, height) { \ EMF_write_emr(10, 0x10); \ EMF_write_sizel(width, height); \ } #define EMF_SetViewportExtEx(width, height) { \ EMF_write_emr(11, 0x10); \ EMF_write_sizel(width, height); \ } #define EMF_SetViewportOrgEx(width, height) { \ EMF_write_emr(12, 0x10); \ EMF_write_sizel(width, height); \ } #define EMF_SetTextColor(color) { \ EMF_write_emr(24, 0x0C); \ EMF_write_long(color); \ } #define EMF_MoveToEx(x,y) { \ EMF_write_emr(27, 0x10); \ EMF_write_pointl(x, y); \ } #define EMF_LineTo(x,y) { \ EMF_write_emr(54, 0x10); \ EMF_write_pointl(x, y); \ } #define EMF_CreatePen(handle, type, width, color) { \ EMF_write_emr(38, 0x1C); \ EMF_write_long(handle); \ EMF_write_long(type); \ EMF_write_long(width); \ EMF_write_long(0); \ EMF_write_long(color); \ } #define EMF_CreateBrush(handle, type, color, hatch) { \ EMF_write_emr(39, 0x18); \ EMF_write_long(handle); \ EMF_write_long(type); \ EMF_write_long(color); \ EMF_write_long(hatch); \ } #define EMF_SelectObject(handle) { \ EMF_write_emr(37, 0x0C); \ EMF_write_long(handle); \ } #define EMF_DeleteObject(handle) { \ EMF_write_emr(40, 0x0C); \ EMF_write_long(handle); \ } #define EMF_Ellipse(left,top,right,bottom) { \ EMF_write_emr(42, 0x18); \ EMF_write_rectl(left,top,right,bottom) \ } #define EMF_SetTextAlign(align) { \ EMF_write_emr(22, 0x0C); \ EMF_write_long(align); \ } #define EMF_SetBkMode(mode) { \ EMF_write_emr(18, 0x0C); \ EMF_write_long(mode); \ } #define EMF_SaveDC() { \ EMF_write_emr(33, 0x0C); \ EMF_write_long(0); \ } #define EMF_RestoreDC() { \ EMF_write_emr(34, 0x0C); \ EMF_write_long(1); \ } #define EMF_CreatePolygon(nvert) { \ EMF_write_emr(3, (7+2*nvert)*4); \ EMF_write_rectl(0,0,0,0); /* Bounds */ \ EMF_write_long(nvert); \ } /* shige */ /* Write the EMR, the header, and a single-entry colormap */ #define EMF_CreateMonoBrush(handle) { \ EMF_write_emr(93, 0x6c); \ EMF_write_long(handle); \ EMF_write_long(2); /* DIB_PAL_INDICES = use current color */ \ EMF_write_long(0x24); /* offset to DIB header */ \ EMF_write_long(0x28); /* size of DIB header */ \ EMF_write_long(0x4c); /* offset to DIB bits */ \ EMF_write_long(0x20); /* size of DIB bits */ \ EMF_write_long(0x20000000); /* start of DIB header */ \ EMF_write_long(0x28); \ EMF_write_sizel(16, 8); /* width, height */ \ EMF_write_short(1); /* must be 1 */ \ EMF_write_short(1); /* bits per pixel */ \ EMF_write_long(0); /* compression type */ \ EMF_write_long(32); /* size of image in bytes */ \ EMF_write_long(0); /* x pixels per meter */ \ EMF_write_long(0); /* y pixels per meter */ \ EMF_write_long(0); /* # entries in color table */ \ EMF_write_long(0); /* # color table entries used */ \ } #endif /* TERM_PROTO */ #ifndef TERM_PROTO_ONLY #ifdef TERM_BODY #include /* for isspace() */ #ifdef HAVE_ICONV #include #endif static int emf_posx; static int emf_posy; static int emf_record_count = 0; static int emf_linetype = 1; static int emf_dashtype = 0; static long emf_color = 0L; static long emf_textcolor = LT_UNDEFINED; static unsigned long emf_pentype = 0x2200; /* cap=flat join=miter */ static unsigned int emf_polyline[EMF_MAX_SEGMENTS]; /* stored polyline coordinates */ static unsigned int emf_graphics = FALSE; static unsigned int emf_dashed = TRUE; static unsigned int emf_monochrome = FALSE; static unsigned int emf_background = 0xffffff; /* defaults to white */ static double emf_linewidth; /* line width in plot units */ static double emf_linewidth_factor = 1.0; static double emf_dashlength = 1.0; static int emf_coords = 0; /* # polyline coordinates saved */ static char emf_fontname[255] = EMF_FONTNAME; static float emf_fontsize = EMF_FONTSIZE; static float emf_last_fontsize = -1; static char *emf_last_fontname = NULL; static enum JUSTIFY emf_justify = LEFT; static char emf_defaultfontname[255] = EMF_FONTNAME; static float emf_defaultfontsize = EMF_FONTSIZE; static int emf_vert_text = 0; /* text orientation -- nonzero for vertical */ static int emf_step_sizes[8]; /* array of currently used dash lengths in plot units */ static int emf_step_index = 0; /* index into emf_step_sizes[] */ static int emf_step = 0; /* amount of current dash not yet drawn, in plot units */ static int emf_tic, emf_tic707, emf_tic866, emf_tic500, emf_tic1241, emf_tic1077, emf_tic621, emf_tic9511, emf_tic5878, emf_tic8090, emf_tic3090; /* marker dimensions */ static TBOOLEAN emf_tweak = TRUE; /* Empirical hack to adjust character widths */ static double emf_fontscale = 1.0; static int emf_dashtype_count = 0; /* count > 0 if EMF_load_dashtype needed before drawing */ static int emf_dashpattern[8]; /* filled by EMF_dashtype */ /* shige: hatch pattern (from src/win/wgraph.c) */ #define PATTERN_BITMAP_LENGTH 16 static const unsigned char pattern_bitmaps[][PATTERN_BITMAP_LENGTH] = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* no fill */ {0xFE, 0xFE, 0x7D, 0x7D, 0xBB, 0xBB, 0xD7, 0xD7, 0xEF, 0xEF, 0xD7, 0xD7, 0xBB, 0xBB, 0x7D, 0x7D}, /* cross-hatch (1) */ {0x77, 0x77, 0xAA, 0xAA, 0xDD, 0xDD, 0xAA, 0xAA, 0x77, 0x77, 0xAA, 0xAA, 0xDD, 0xDD, 0xAA, 0xAA}, /* double cross-hatch (2) */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* solid fill (3) */ {0x7F, 0x7F, 0xBF, 0xBF, 0xDF, 0xDF, 0xEF, 0xEF, 0xF7, 0xF7, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE}, /* diagonals (4) */ {0xFE, 0xFE, 0xFD, 0xFD, 0xFB, 0xFB, 0xF7, 0xF7, 0xEF, 0xEF, 0xDF, 0xDF, 0xBF, 0xBF, 0x7F, 0x7F}, /* diagonals (5) */ {0x77, 0x77, 0x77, 0x77, 0xBB, 0xBB, 0xBB, 0xBB, 0xDD, 0xDD, 0xDD, 0xDD, 0xEE, 0xEE, 0xEE, 0xEE}, /* steep diagonals (6) */ {0xEE, 0xEE, 0xEE, 0xEE, 0xDD, 0xDD, 0xDD, 0xDD, 0xBB, 0xBB, 0xBB, 0xBB, 0x77, 0x77, 0x77, 0x77} /* steep diagonals (7) */ }; #define pattern_num (sizeof(pattern_bitmaps)/(sizeof(*pattern_bitmaps))) static void EMF_flush_polyline __PROTO((void)); static void EMF_flush_polygon __PROTO((void)); static void EMF_write_byte __PROTO((int)); static void EMF_write_short __PROTO((int)); static void EMF_write_long __PROTO((unsigned long)); static void EMF_write_float __PROTO((double)); static void EMF_setfont __PROTO((void)); #define ANSI_CHARSET 0 #define DEFAULT_CHARSET 1 #define CHINESEBIG5_CHARSET 136 #define GREEK_CHARSET 161 #define TURKISH_CHARSET 162 #define BALTIC_CHARSET 186 #define RUSSIAN_CHARSET 204 #define EASTEUROPE_CHARSET 238 #define KOI8_CHARSET 242 /* Text alignment */ #define GP_TA_NOUPDATECP 0x00 #define GP_TA_UPDATECP 0x01 #define GP_TA_LEFT 0x00 #define GP_TA_RIGHT 0x02 #define GP_TA_CENTER 0x06 #define GP_TA_TOP 0x00 #define GP_TA_BOTTOM 0x08 #define GP_TA_BASELINE 0x18 /* ExtTextOut options */ // #define ETO_NO_RECT 0x100 // #define ETO_PDY 0x2000 static void EMF_setfont() { int i, count; int bold = 400; char italic = 0, underline = 0, strikeout = 0; char font[32]; char *sub; if (!emf_graphics) return; count = GPMIN (strlen(emf_fontname), 31); if (((sub = strstr(emf_fontname, " bold")) != NULL) || ((sub = strstr(emf_fontname, " Bold")) != NULL)) { bold = 700; count = GPMIN(sub - emf_fontname, count); } if (((sub = strstr(emf_fontname, " italic")) != NULL) || ((sub = strstr(emf_fontname, " Italic")) != NULL)) { italic = 1; count = GPMIN(sub - emf_fontname, count); } if (((sub = strstr(emf_fontname, " underline")) != NULL) || ((sub = strstr(emf_fontname, " Underline")) != NULL)) { underline = 1; count = GPMIN(sub - emf_fontname, count); } if (((sub = strstr(emf_fontname, " strikeout")) != NULL) || ((sub = strstr(emf_fontname, " Strikeout")) != NULL) || ((sub = strstr(emf_fontname, " StrikeOut")) != NULL) ) { strikeout = 1; count = GPMIN(sub - emf_fontname, count); } safe_strncpy(font, emf_fontname, count + 1); EMF_SelectObject(EMF_STOCK_OBJECT_DEFAULT_FONT); EMF_DeleteObject(EMF_HANDLE_FONT); /* SB 20040506: was not complete size was 104, now it is 332 */ EMF_write_emr(82, 332); EMF_write_long(EMF_HANDLE_FONT); EMF_write_long((long) (-emf_fontsize * EMF_PT2HM * emf_fontscale)); /* height */ EMF_write_long(0); /* width */ EMF_write_long(emf_vert_text); /* escapement */ EMF_write_long(emf_vert_text); /* orientation */ EMF_write_long(bold); /* weight */ EMF_write_byte(italic); /* italic */ EMF_write_byte(underline); /* underline */ EMF_write_byte(strikeout); /* strikeout */ /* charset: could be extended? */ switch (encoding) { case S_ENC_CP1250: case S_ENC_ISO8859_2: EMF_write_byte(EASTEUROPE_CHARSET); break; case S_ENC_KOI8_R: case S_ENC_KOI8_U: EMF_write_byte(KOI8_CHARSET); break; case S_ENC_CP1254: case S_ENC_ISO8859_9: EMF_write_byte(TURKISH_CHARSET); break; case S_ENC_CP950: EMF_write_byte(CHINESEBIG5_CHARSET); break; default: EMF_write_byte(DEFAULT_CHARSET); } EMF_write_byte(0); /* out precision */ EMF_write_byte(0); /* clip precision */ EMF_write_byte(0); /* quality */ EMF_write_byte(0); /* pitch and family */ for (i = 0; i < 32; i++) { /* face name (max 32) */ EMF_write_byte((char) (i < strlen(font) ? font[i] : 0)); EMF_write_byte(0); } /* SB 20040506: modification following */ for (i = 0; i < 64; i++) { /* FULL face name (max 64) */ EMF_write_byte((char) (i < strlen(font) ? font[i] : 0)); EMF_write_byte(0); } for (i = 0; i < 32; i++) { /* style name (max 32) */ EMF_write_byte(0); EMF_write_byte(0); } EMF_write_long(0); /* version */ EMF_write_long(0); /* Style size */ EMF_write_long(0); /* Match */ EMF_write_long(0); /* reserved */ EMF_write_long(0); /* VendorId */ EMF_write_long(0); /* Culture */ for (i = 0; i < 10; i++) EMF_write_byte(0); /* Panose (ignored) */ EMF_write_byte(0); /* pad (long aligned) */ EMF_write_byte(0); /* pad (long aligned) */ /* SB 20040506: End of modification */ EMF_SelectObject(EMF_HANDLE_FONT); } static void EMF_flush_polygon() { int i = 0; if (emf_coords == 0) return; EMF_flush_dashtype(); EMF_MoveToEx(emf_polyline[i++], term->ymax - emf_polyline[i++]); while (i < emf_coords * 2) EMF_LineTo(emf_polyline[i++], term->ymax - emf_polyline[i++]); EMF_LineTo(emf_polyline[0], term->ymax - emf_polyline[1]); emf_coords = 0; } static void EMF_flush_polyline() { if (emf_coords == 0) return; EMF_flush_dashtype(); if (emf_coords <= 2) { EMF_MoveToEx(emf_polyline[0], term->ymax - emf_polyline[1]); EMF_LineTo(emf_polyline[2], term->ymax - emf_polyline[3]); } else { int i = 0; EMF_MoveToEx(emf_polyline[i++], term->ymax - emf_polyline[i++]); while (i < emf_coords * 2) EMF_LineTo(emf_polyline[i++], term->ymax - emf_polyline[i++]); } emf_coords = 0; } /* HBB 20040708: the following keep K&R argument types for now */ static void EMF_write_byte(int value) { char c = value; fwrite(&c, 1, 1, gpoutfile); } static void EMF_write_short(int value) { short actual_value = value; char c[2]; c[1] = (actual_value >> 8) & 255; /* convert to x86 order */ c[0] = actual_value & 255; fwrite(c, 1, 2, gpoutfile); } static void EMF_write_long(unsigned long value) { char c[4]; c[3] = (value >> 24) & 0xFFL; /* convert to x86 order */ c[2] = (value >> 16) & 0xFFL; c[1] = (value >> 8) & 0xFFL; c[0] = value & 0xFFL; fwrite(c, 1, 4, gpoutfile); } /* FIXME HBB 20001103: this only works as given iff 'float' is the * same format as on x86's, i.e. IEEE 4-byte floating point format */ static void EMF_write_float(double value) { char c[4]; union { long l; float f; } u; u.f = value; c[3] = (u.l >> 24) & 0xFFL; /* convert to x86 order */ c[2] = (u.l >> 16) & 0xFFL; c[1] = (u.l >> 8) & 0xFFL; c[0] = u.l & 0xFFL; fwrite(c, 1, 4, gpoutfile); } TERM_PUBLIC void EMF_options() { char *s; int emf_bgnd_rgb = 0; /* Annoying hack to handle the case of 'set termoption' after */ /* we have already initialized the terminal. */ if (!almost_equals(c_token-1, "termopt$ion")) { term->xmax = EMF_XMAX; term->ymax = EMF_YMAX; emf_monochrome = FALSE; emf_background = 0xffffff; emf_tweak = TRUE; /* Default to enhanced text */ term->put_text = ENHemf_put_text; term->flags |= TERM_ENHANCED_TEXT; } while (!END_OF_COMMAND) { if (almost_equals(c_token, "de$fault")) { strcpy(emf_defaultfontname, EMF_FONTNAME); emf_defaultfontsize = EMF_FONTSIZE; emf_monochrome = FALSE; term->flags &= ~TERM_MONOCHROME; c_token++; continue; } if (almost_equals(c_token, "m$onochrome")) { emf_monochrome = TRUE; term->flags |= TERM_MONOCHROME; c_token++; continue; } if (almost_equals(c_token, "c$olor") || almost_equals(c_token, "c$olour")) { emf_monochrome = FALSE; term->flags &= ~TERM_MONOCHROME; c_token++; continue; } if (almost_equals(c_token, "da$shed") || almost_equals(c_token, "s$olid")) { /* dashed lines always enabled in version 5 */ c_token++; continue; } if (almost_equals(c_token, "round$ed")) { emf_pentype= 0x0; c_token++; continue; } if (almost_equals(c_token, "butt")) { emf_pentype = 0x2200; c_token++; continue; } if (equals(c_token, "dl") || almost_equals(c_token, "dashl$ength")) { c_token++; emf_dashlength = real_expression(); if (emf_dashlength < 0.5) emf_dashlength = 1.0; continue; } if (equals(c_token, "lw") || almost_equals(c_token, "linew$idth")) { c_token++; emf_linewidth_factor = real_expression(); if (emf_linewidth_factor < 0.1) emf_linewidth_factor = 1.0; continue; } if (almost_equals(c_token,"enh$anced")) { c_token++; term->put_text = ENHemf_put_text; term->flags |= TERM_ENHANCED_TEXT; continue; } else if (almost_equals(c_token,"noenh$anced")) { c_token++; term->put_text = EMF_put_text; term->flags &= ~TERM_ENHANCED_TEXT; } if (almost_equals(c_token,"back$ground")) { c_token++; emf_bgnd_rgb = parse_color_name(); emf_background = RGB((emf_bgnd_rgb>>16)&0xFF, (emf_bgnd_rgb>>8)&0xFF, emf_bgnd_rgb&0xFF); } if (almost_equals(c_token,"nopro$portional")) { c_token++; emf_tweak = FALSE; } if (almost_equals(c_token, "si$ze")) { int tempxmax = 1024; int tempymax = 768; c_token++; if (!END_OF_COMMAND) { tempxmax = real_expression(); if (equals(c_token, ",")) { c_token++; tempymax = real_expression(); } } if (tempxmax > 0) term->xmax = tempxmax * EMF_PX2HM; if (tempymax > 0) term->ymax = tempymax * EMF_PX2HM; term->h_tic = term->xmax / 160; term->v_tic = term->h_tic; continue; } if (equals(c_token, "fontscale")) { c_token++; emf_fontscale = END_OF_COMMAND ? -1 : real_expression(); if (emf_fontscale <= 0) emf_fontscale = 1.0; continue; } if (equals(c_token, "font")) c_token++; /* Fall through to old-style bare font name */ if ((s = try_to_get_string())) { char *comma = strrchr(s,','); if (comma && (1 == sscanf(comma+1,"%g",&emf_defaultfontsize))) { *comma = '\0'; } if (*s) strncpy(emf_defaultfontname, s, sizeof(emf_defaultfontname)); free(s); if (isanumber(c_token)) { emf_defaultfontsize = int_expression(); } continue; } break; } /* while(!end of command) */ if (!END_OF_COMMAND) { /* We have old-style bare font size specified */ emf_defaultfontsize = int_expression(); } sprintf(term_options, "%s %s font \"%s,%g\"", emf_monochrome ? "monochrome" : "color", emf_pentype ? "butt" : "rounded", emf_defaultfontname, emf_defaultfontsize); if (term->flags & TERM_ENHANCED_TEXT) strcat(term_options, " enhanced "); if (emf_fontscale != 1.0) sprintf(&(term_options[strlen(term_options)]), " fontscale %.1f", emf_fontscale); if (term->xmax != (int)EMF_XMAX || term->ymax != (int)EMF_YMAX) sprintf(&(term_options[strlen(term_options)]), " size %d,%d ", (int)(0.5+term->xmax/EMF_PX2HM), (int)(0.5+term->ymax/EMF_PX2HM)); if (emf_linewidth_factor != 1.0) sprintf(&(term_options[strlen(term_options)]), " lw %.1f", emf_linewidth_factor); if (emf_dashlength != 1.0) sprintf(&(term_options[strlen(term_options)]), " dashlength %.1f", emf_dashlength); if (emf_bgnd_rgb) sprintf(&(term_options[strlen(term_options)]), " background \"#%06x\"", emf_bgnd_rgb); } TERM_PUBLIC void EMF_init() { emf_posx = emf_posy = 0; emf_linetype = 0; emf_vert_text = 0; emf_graphics = FALSE; } TERM_PUBLIC void EMF_graphics() { int width = 0.5 + term->xmax/EMF_PX2HM; int height = 0.5 + term->ymax/EMF_PX2HM; int mmwidth = 0.5 + (term->xmax/EMF_PX2HM) * (270./1024.); int mmheight = 0.5 + (term->ymax/EMF_PX2HM) * (200./768.); /* header start */ emf_record_count = 0; EMF_write_emr(1, 100); EMF_write_long(0); /* rclBounds */ EMF_write_long(0); EMF_write_long(term->xmax / EMF_PX2HM); EMF_write_long(term->ymax / EMF_PX2HM); EMF_write_long(0); /* rclFrame */ EMF_write_long(0); EMF_write_long(term->xmax); EMF_write_long(term->ymax); EMF_write_long(0x464D4520); /* signature */ EMF_write_long(0x00010000); /* version */ EMF_write_long(0); /* nBytes */ EMF_write_long(0); /* nRecords */ EMF_write_short(EMF_HANDLE_MAX); /* nHandles, MUST NOT BE 0 */ EMF_write_short(0); /* reserved */ EMF_write_long(0); /* descSize */ EMF_write_long(0); /* descOff */ EMF_write_long(0); /* nPalEntries */ EMF_write_long(width); /* ref dev pixwidth, default 1024 */ EMF_write_long(height); /* ref dev pixheight, default 768 */ EMF_write_long(mmwidth); /* ref dev mwidth, default 270 */ EMF_write_long(mmheight); /* ref dev mheight, default 200 */ EMF_write_long(0); /* cbPixelFormat */ EMF_write_long(0); /* offPixelFormat */ EMF_write_long(0); /* bOpenGL */ emf_graphics = TRUE; /* header end */ EMF_SetMapMode(8); /* forcing anisotropic mode */ EMF_SetWindowExtEx(term->xmax, term->ymax); /* setting logical (himetric) size */ EMF_SetViewportExtEx(term->xmax / EMF_PX2HM, term->ymax / EMF_PX2HM); /* setting device (pixel) size */ /* Paint with background color */ if (emf_background != 0xffffff) EMF_fillbox(FS_EMPTY, 0, 0, term->xmax, term->ymax); EMF_CreatePen(EMF_HANDLE_PEN, emf_pentype, 1, 0x000000); /* init default pen */ EMF_SelectObject(EMF_HANDLE_PEN); EMF_SetBkMode(1); /* transparent background for text */ EMF_CreateBrush(EMF_HANDLE_BRUSH, 1, 0, 0); /* transparent brush for polygons */ EMF_SelectObject(EMF_HANDLE_BRUSH); free(emf_last_fontname); /* invalidate any previous font */ emf_last_fontname = NULL; EMF_set_font(NULL); /* init default font */ emf_color = emf_textcolor = LT_UNDEFINED; } TERM_PUBLIC int EMF_set_font(const char *font) { /* FIXME: This condition is somehow triggered by enhanced_recursion */ if (font == emf_fontname) ; else if (font && *font) { float tempsize; int sep = strcspn(font,","); if (sep > 0) safe_strncpy(emf_fontname, font, GPMIN(sep + 1, 32)); if (sep < strlen(font) && sscanf(font+sep+1, "%f", &tempsize)) emf_fontsize = tempsize; } else { strcpy(emf_fontname, emf_defaultfontname); emf_fontsize = emf_defaultfontsize; } /* Skip redundant requests for the same font */ if (emf_last_fontname && !strcmp(emf_last_fontname, emf_fontname) && emf_last_fontsize == emf_fontsize) { return TRUE; } else { free(emf_last_fontname); emf_last_fontname = gp_strdup(emf_fontname); emf_last_fontsize = emf_fontsize; } term->h_char = 0.6 * (emf_fontsize * EMF_PT2HM * emf_fontscale); term->v_char = 1.3 * (emf_fontsize * EMF_PT2HM * emf_fontscale); EMF_setfont(); return TRUE; } TERM_PUBLIC void EMF_text() { long pos; EMF_flush_polyline(); emf_graphics = FALSE; /* shige: 08/30 2012 * FIXME: * The following command prevents export of a spurious rectangle * (not the bounding box) on some Windows systems. Why? How? */ EMF_MoveToEx(emf_polyline[0], term->ymax - emf_polyline[1]); /* writing end of metafile */ EMF_SelectObject(EMF_STOCK_OBJECT_DEFAULT_FONT); EMF_DeleteObject(EMF_HANDLE_FONT); EMF_SelectObject(EMF_STOCK_OBJECT_BLACK_PEN); EMF_DeleteObject(EMF_HANDLE_PEN); EMF_SelectObject(EMF_STOCK_OBJECT_WHITE_BRUSH); EMF_DeleteObject(EMF_HANDLE_BRUSH); EMF_EOF(); /* update the header */ pos = ftell(gpoutfile); fseek(gpoutfile, 48L, SEEK_SET); EMF_write_long(pos); EMF_write_long(emf_record_count); /* Reset to start of output file. If the user mistakenly tries to */ /* plot again into the same file, it will overwrite the original */ /* rather than corrupting it. */ /* FIXME: An alternative would be to open a new output file. */ fseek(gpoutfile, 0L, SEEK_SET); } TERM_PUBLIC void EMF_linetype(int linetype) { EMF_flush_polyline(); if (linetype == LT_NODRAW) linetype = LT_BACKGROUND; emf_linetype = linetype; EMF_linecolor(linetype); if (linetype == LT_SOLID) EMF_load_dashtype(0); if (linetype == LT_AXIS) EMF_load_dashtype(2); } TERM_PUBLIC void EMF_dashtype (int type, t_dashtype *custom_dash_type) { int i; switch (type) { case DASHTYPE_SOLID: EMF_load_dashtype(0); break; case DASHTYPE_CUSTOM: for (i = 0; i < 8; i++) emf_dashpattern[i] = custom_dash_type->pattern[i]; EMF_load_dashtype(DASHTYPE_CUSTOM); break; default: EMF_load_dashtype(type); break; } } TERM_PUBLIC void EMF_linecolor(int linecolor) { static long GPFAR color_table_data[] = { RGB(255, 0, 0), /* red */ RGB(0, 255, 0), /* green */ RGB(0, 0, 255), /* blue */ RGB(255, 0, 255), /* magenta */ RGB(0, 0, 128), /* dark blue */ RGB(128, 0, 0), /* dark red */ RGB(0, 128, 128), /* dark cyan */ RGB(0, 0, 0), /* black */ RGB(128, 128, 128), /* grey */ RGB(0, 128, 64), /* very dark cyan */ RGB(128, 128, 0), /* dark yellow */ RGB(128, 0, 128), /* dark magenta */ RGB(192, 192, 192), /* light grey */ RGB(0, 255, 255), /* cyan */ RGB(255, 255, 0) /* yellow */ }; if (linecolor == LT_BACKGROUND) emf_color = emf_background; else { linecolor = (linecolor < 0 || emf_monochrome) ? 7 : (linecolor % EMF_COLORS); emf_color = color_table_data[linecolor]; } EMF_flush_polyline(); } TERM_PUBLIC int EMF_make_palette(t_sm_palette *palette) { return 0; /* can do continous colors */ } TERM_PUBLIC void EMF_previous_palette() { /* do nothing */ } TERM_PUBLIC void EMF_set_color(t_colorspec *colorspec) { rgb255_color rgb255; EMF_flush_polyline(); if (colorspec->type == TC_LT) { EMF_linecolor(colorspec->lt); } else if (colorspec->type == TC_FRAC) { rgb255maxcolors_from_gray(colorspec->value, &rgb255); emf_color = RGB(rgb255.r, rgb255.g, rgb255.b); } else if (colorspec->type == TC_RGB) { emf_color = RGB( colorspec->lt >> 16 & 0xff, colorspec->lt >> 8 & 0xff, colorspec->lt & 0xff ); } /* else { fprintf(stderr, "unhandled colorspec type %d\n", colorspec->type); } */ /* Force reevaluation of dash type */ emf_dashtype_count++; } TERM_PUBLIC void EMF_filled_polygon(int points, gpiPoint *corners) { int i; unsigned long color = emf_color; int fillpar = corners->style >> 4; int style = corners->style & 0xf; switch (style) { case FS_EMPTY: /* fill with background color */ color = emf_background; break; case FS_PATTERN: case FS_TRANSPARENT_PATTERN: #if 1 /* fill pattern implemented as bitmaps, implementation further down */ break; #else /* pattern fill implemented as partial density */ fillpar *= 12; /* Fall through */ #endif case FS_SOLID: /* solid fill */ if (fillpar >= 0 && fillpar < 100) { double density = (double)fillpar / 100.; color = ((int)((double)((emf_color>>16)&0xff)*density) << 16) + ((int)((double)((emf_color>>8)&0xff)*density) << 8) + ((int)((double)(emf_color&0xff)*density)); color += ((int)(255.*(1.-density)) << 16) + ((int)(255.*(1.-density)) << 8) + ((int)(255.*(1.-density))); } break; default: break; } EMF_flush_dashtype(); /* MS documentation says not to delete an object while it is selected */ EMF_SelectObject(EMF_STOCK_OBJECT_BLACK_PEN); EMF_SelectObject(EMF_STOCK_OBJECT_WHITE_BRUSH); EMF_DeleteObject(EMF_HANDLE_BRUSH); if (style == FS_PATTERN || style == FS_TRANSPARENT_PATTERN) { /* Implementation of bitmapped pattern fill */ const unsigned char *pattern; pattern = pattern_bitmaps[fillpar % pattern_num]; emf_textcolor = color; EMF_SetTextColor(emf_textcolor); EMF_CreateMonoBrush(EMF_HANDLE_BRUSH); for (i = (PATTERN_BITMAP_LENGTH - 1); i >= 1; i -= 2) EMF_write_long(pattern[i] | (pattern[i-1] << 8)); } else EMF_CreateBrush(EMF_HANDLE_BRUSH, 0, color, 0); EMF_SelectObject(EMF_HANDLE_BRUSH); EMF_DeleteObject(EMF_HANDLE_PEN); EMF_CreatePen(EMF_HANDLE_PEN, emf_pentype, emf_linewidth * EMF_PX2HM, color); EMF_SelectObject(EMF_HANDLE_PEN); EMF_CreatePolygon(points); for (i=0; iymax - corners[i].y); /* Force re-evaluation of linetype next time we draw a line */ emf_linetype = LT_UNDEFINED; emf_dashtype = LT_UNDEFINED; } TERM_PUBLIC void EMF_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height) { gpiPoint corner[4]; corner[0].x = x1; corner[0].y = y1; corner[1].x = x1+width; corner[1].y = y1; corner[2].x = x1+width; corner[2].y = y1+height; corner[3].x = x1; corner[3].y = y1+height; corner->style = style; EMF_filled_polygon(4, corner); } TERM_PUBLIC void EMF_linewidth(double width) { EMF_flush_polyline(); width *= emf_linewidth_factor; if (width == emf_linewidth) return; emf_linewidth = width; /* The linewidth is applied at the same time as the dash pattern */ emf_dashtype_count++; } TERM_PUBLIC void EMF_flush_dashtype(void) { if (emf_dashtype_count > 0) { EMF_load_dashtype(emf_dashtype); emf_dashtype_count = 0; } } /* * Resets _both_ line color and dash type! */ TERM_PUBLIC void EMF_load_dashtype(int dashtype) { int i, j; double empirical_scale = 0.50; /* Each group of 8 entries in dot_length[] defines a dash pattern. Entries in each group are alternately length of line and length of whitespace, in units of 2/3 of the linewidth. */ static int dot_length[(EMF_LINE_TYPES+1) * 8] = { /* 0 - solid */ 8, 5, 8, 5, 8, 5, 8, 5, /* 1 - dashes */ 2, 4, 2, 4, 2, 4, 2, 4, /* 2 - dotted */ 8, 4, 2, 4, 8, 4, 2, 4, /* 3 - dash-dot */ 9, 4, 2, 4, 2, 0, 0, 4, /* 4 - dash-dot-dot */ 1, 1, 1, 1, 1, 1, 1, 1 /* Placeholder for custom pattern */ }; emf_dashtype = dashtype; if (dashtype >= 0) dashtype = dashtype % EMF_LINE_TYPES; if (dashtype == LT_AXIS) dashtype = 2; if (dashtype == DASHTYPE_CUSTOM) { dashtype = EMF_LINE_TYPES; /* Point to placeholder array */ for (i = 0; i < 8; i += 1) dot_length[(EMF_LINE_TYPES-1)*8 + i] = emf_dashpattern[i] * ceil(emf_linewidth * empirical_scale/2.); } if (dashtype < 1 || !emf_dashed) { /* solid mode */ EMF_SelectObject(EMF_STOCK_OBJECT_BLACK_PEN); EMF_DeleteObject(EMF_HANDLE_PEN); EMF_CreatePen(EMF_HANDLE_PEN, emf_pentype, emf_linewidth * EMF_PX2HM, emf_color); EMF_SelectObject(EMF_HANDLE_PEN); term->vector = EMF_solid_vector; } else { /* Since win32 dashed lines works only with 1 pixel linewith we must emulate */ EMF_SelectObject(EMF_STOCK_OBJECT_BLACK_PEN); EMF_DeleteObject(EMF_HANDLE_PEN); EMF_CreatePen(EMF_HANDLE_PEN, emf_pentype, emf_linewidth * EMF_PX2HM, emf_color); EMF_SelectObject(EMF_HANDLE_PEN); term->vector = EMF_dashed_vector; /* set up dash dimensions */ j = (dashtype - 1) * 8; for (i = 0; i < 8; i++, j++) { emf_step_sizes[i] = dot_length[j] * emf_dashlength * EMF_PX2HM * emf_linewidth * empirical_scale; } /* first thing drawn will be a line */ emf_step = emf_step_sizes[0]; emf_step_index = 0; } } TERM_PUBLIC void EMF_move(unsigned int x, unsigned int y) { if (x >= term->xmax || y >= term->ymax) { int_warn(NO_CARET, "emf_move: (%d,%d) out of range",x,y); x = GPMIN(x, term->xmax); y = GPMIN(y, term->ymax); } if (x == emf_posx && y == emf_posy) return; EMF_flush_polyline(); emf_posx = x; emf_posy = y; } TERM_PUBLIC void EMF_dashed_vector(unsigned int ux, unsigned int uy) { int xa, ya; int dx, dy, adx, ady; int dist; /* approximate distance in plot units from starting point to end point. */ long remain;/* approximate distance in plot units remaining to specified end point. */ if (ux >= term->xmax || uy >= term->ymax) int_warn(NO_CARET, "emf_dashed_vector: (%d,%d) out of range",ux,uy); dx = (ux - emf_posx); dy = (uy - emf_posy); adx = abs(dx); ady = abs(dy * 10); /* using the approximation sqrt(x**2 + y**2) ~ x + (5*x*x)/(12*y) when x > y. Note ordering of calculations to avoid overflow on 16 bit architectures */ if (10 * adx < ady) dist = (ady / 2 + 25 * adx / ady * adx / 6 * 5) / 5; else { if (adx == 0) return; dist = (adx * 10 + (ady / 24) * (ady / adx)) / 10; } remain = dist; xa = emf_posx; ya = emf_posy; while (remain > emf_step) { remain -= emf_step; if (emf_step_index & 1) { xa = (int) (ux - (remain * dx) / dist); ya = (int) (uy - (remain * dy) / dist); EMF_move(xa, ya); } else { EMF_solid_vector((int) (ux - (remain * dx) / dist), (int) (uy - (remain * dy) / dist)); } if (++emf_step_index >= 8) emf_step_index = 0; emf_step = emf_step_sizes[emf_step_index]; } if (emf_step_index & 1) EMF_move(ux, uy); else EMF_solid_vector(ux, uy); emf_step -= (int) remain; } TERM_PUBLIC void EMF_solid_vector(unsigned int ux, unsigned int uy) { if (ux >= term->xmax || uy >= term->ymax) int_warn(NO_CARET, "emf_solid_vector: (%d,%d) out of range",ux,uy); if (ux == emf_posx && uy == emf_posy) return; if (emf_coords * 2 > EMF_MAX_SEGMENTS - 2) EMF_flush_polyline(); if (emf_coords == 0) { emf_polyline[0] = emf_posx; emf_polyline[1] = emf_posy; emf_coords++; } emf_posx = emf_polyline[emf_coords * 2] = ux; emf_posy = emf_polyline[emf_coords * 2 + 1] = uy; emf_coords++; } TERM_PUBLIC void EMF_put_text(unsigned int x, unsigned int y, const char str[]) { int i, alen; int slen = strlen(str); int nchars = slen; #ifdef HAVE_ICONV char * wstr = 0; if (encoding == S_ENC_UTF8 || encoding == S_ENC_SJIS) { iconv_t cd; size_t wsize, wlen, mblen; const char * str_start = str; char * wstr_start; char * FromEnc; nchars = strlen(str); mblen = nchars; wlen = wsize = 2 * mblen + 2; wstr = gp_alloc(wlen, "iconv string"); wstr_start = wstr; if (encoding == S_ENC_UTF8) FromEnc = "UTF-8"; else FromEnc = "Shift_JIS"; if ((cd = iconv_open("UTF-16LE", FromEnc)) == (iconv_t)-1) int_warn(NO_CARET, "iconv_open failed"); else { if (iconv(cd, (void *)&str_start, &mblen, &wstr_start, &wlen) == (size_t)-1) int_warn(NO_CARET, "iconv failed"); iconv_close(cd); slen = wsize - wlen; nchars = slen / 2; } } #endif if (slen <=0) return; /* shige: 08/30 2012 */ alen = slen; EMF_flush_polyline(); if (emf_textcolor != emf_color) { EMF_SetTextColor(emf_color); emf_textcolor = emf_color; } /* Use ansi unless we can convert to unicode */ if (alen % 4) alen += 4 - (slen % 4); /* Structure must be long aligned! */ #ifdef HAVE_ICONV if (encoding == S_ENC_UTF8 || encoding == S_ENC_SJIS) { EMF_write_emr(84, 76 + alen + nchars * 4); /* ExtTextOutW, UTF16-LE version */ } else #endif { EMF_write_emr(83, 76 + alen + nchars * 4); /* ExtTextOutA, ANSI char version! */ } EMF_write_rectl(0, 0, 0, 0); /* bounding, never used */ EMF_write_long(1); /* GM_Compatible mode for advanced scaling */ EMF_write_float(EMF_PX2HM); /* x scale */ EMF_write_float(EMF_PX2HM); /* y scale */ /* positioning... y is recentered from bottom reference set in * text align */ EMF_write_pointl( x + (long) ((term->v_char/ 2) * sin(emf_vert_text * EMF_10THDEG2RAD)), term->ymax - y + (long) (term->v_char / 2 * cos(emf_vert_text * EMF_10THDEG2RAD))); EMF_write_long(nchars); /* true number of characters */ EMF_write_long(76); /* offset to text */ EMF_write_long(0); /* options, none */ EMF_write_rectl(0, 0, 0, 0); /* rectangle clipping not used */ EMF_write_long(0); /* offset to intercharacter spacing array */ /* can't be used since we don't know anything */ /* about the font properties being used */ #ifdef HAVE_ICONV if (encoding == S_ENC_UTF8 || encoding == S_ENC_SJIS) { for (i = 0; i < alen; i++) EMF_write_byte(i < slen ? wstr[i] : 0); /* writing text */ free(wstr); } else #endif for (i = 0; i < alen; i++) EMF_write_byte(i < slen ? str[i] : 0); /* writing text */ for (i = 0; i < nchars; i++) /* writing intercharacter spacing array (but we don't use it) */ EMF_write_long(300); /* Invalidate current position */ emf_posx = emf_posy = -2000; } TERM_PUBLIC int EMF_text_angle(int ang) { /* Win GDI rotation is scaled in tenth of degrees, so... */ switch (ang) { case 0: /* left right */ if (emf_vert_text != 0) { emf_vert_text = 0; EMF_setfont(); } break; case TEXT_VERTICAL: /* bottom up */ if (emf_vert_text != 900) { emf_vert_text = 900; EMF_setfont(); } break; default: /* the general case */ emf_vert_text = 10 * ang; EMF_setfont(); break; } return TRUE; } TERM_PUBLIC int EMF_justify_text(enum JUSTIFY mode) { int align = GP_TA_BOTTOM; emf_justify = mode; switch (mode) { case LEFT: align |= GP_TA_LEFT; break; case RIGHT: align |= GP_TA_RIGHT; break; case CENTRE: align |= GP_TA_CENTER; break; } EMF_SetTextAlign(align); return (TRUE); } TERM_PUBLIC void EMF_reset() { emf_posx = emf_posy = 0; emf_graphics = FALSE; } TERM_PUBLIC void EMF_point(unsigned int x, unsigned int y, int number) { int old_dashtype; gpiPoint corners[12]; corners->style = FS_OPAQUE; EMF_flush_polyline(); /* Calls EMF_flush_dashtype */ old_dashtype = emf_dashtype; emf_dashtype = 0; emf_dashtype_count++; /* A few special point types */ if (69 <= number && number <= 73) { int emf_color_save = emf_color; emf_color = emf_background; switch (number) { case 69: EMF_point(x,y,4); break; case 70: EMF_point(x,y,6); break; case 71: EMF_point(x,y,8); break; case 72: EMF_point(x,y,10); break; case 73: EMF_point(x,y,12); break; } emf_color = emf_color_save; switch (number) { case 69: EMF_point(x,y,3); break; case 70: EMF_point(x,y,5); break; case 71: EMF_point(x,y,7); break; case 72: EMF_point(x,y,9); break; case 73: EMF_point(x,y,11); break; } emf_dashtype = old_dashtype; emf_dashtype_count++; return; } /* draw dot */ EMF_move(x, y); EMF_solid_vector(x + 1, y); number = number % EMF_POINTS; switch (number) { case 0: /* draw plus */ EMF_move(x - emf_tic, y); EMF_solid_vector(x + emf_tic, y); EMF_move(x, y - emf_tic); EMF_solid_vector(x, y + emf_tic); break; case 1: /* draw X */ EMF_move(x - emf_tic707, y - emf_tic707); EMF_solid_vector(x + emf_tic707, y + emf_tic707); EMF_move(x - emf_tic707, y + emf_tic707); EMF_solid_vector(x + emf_tic707, y - emf_tic707); break; case 2: /* draw star (asterisk) */ EMF_move(x, y - emf_tic); EMF_solid_vector(x, y + emf_tic); EMF_move(x + emf_tic866, y - emf_tic500); EMF_solid_vector(x - emf_tic866, y + emf_tic500); EMF_move(x + emf_tic866, y + emf_tic500); EMF_solid_vector(x - emf_tic866, y - emf_tic500); break; case 3: /* draw box */ EMF_move(x - emf_tic707, y - emf_tic707); EMF_solid_vector(x + emf_tic707, y - emf_tic707); EMF_solid_vector(x + emf_tic707, y + emf_tic707); EMF_solid_vector(x - emf_tic707, y + emf_tic707); EMF_flush_polygon(); break; case 4: /* draw filled box */ corners[0].x = x - emf_tic707; corners[0].y = y - emf_tic707; corners[1].x = x + emf_tic707; corners[1].y = y - emf_tic707; corners[2].x = x + emf_tic707; corners[2].y = y + emf_tic707; corners[3].x = x - emf_tic707; corners[3].y = y + emf_tic707; EMF_filled_polygon(4, corners); break; case 5: y = term->ymax-y; /* EAM - WTF? */ EMF_SelectObject(EMF_STOCK_OBJECT_BLACK_PEN); EMF_SelectObject(EMF_STOCK_OBJECT_WHITE_BRUSH); EMF_DeleteObject(EMF_HANDLE_BRUSH); EMF_CreateBrush(EMF_HANDLE_BRUSH, 1, 0, 0); /* transparent brush */ EMF_SelectObject(EMF_HANDLE_BRUSH); EMF_DeleteObject(EMF_HANDLE_PEN); EMF_CreatePen(EMF_HANDLE_PEN, emf_pentype, emf_linewidth * EMF_PX2HM, emf_color); EMF_SelectObject(EMF_HANDLE_PEN); EMF_Ellipse((long)(x-emf_tic),(long)(y-emf_tic),(long)(x+emf_tic),(long)(y+emf_tic)); break; case 6: /* filled circle */ y = term->ymax-y; /* EAM - WTF? */ EMF_SelectObject(EMF_STOCK_OBJECT_WHITE_BRUSH); EMF_DeleteObject(EMF_HANDLE_BRUSH); EMF_CreateBrush(EMF_HANDLE_BRUSH, 0, emf_color, 0); EMF_SelectObject(EMF_HANDLE_BRUSH); EMF_SelectObject(EMF_STOCK_OBJECT_BLACK_PEN); EMF_DeleteObject(EMF_HANDLE_PEN); EMF_CreatePen(EMF_HANDLE_PEN, emf_pentype, emf_linewidth * EMF_PX2HM, emf_color); EMF_SelectObject(EMF_HANDLE_PEN); EMF_Ellipse((long)(x-emf_tic),(long)(y-emf_tic),(long)(x+emf_tic),(long)(y+emf_tic)); break; case 7: /* draw triangle (point up) */ EMF_move(x, y + emf_tic1241); EMF_solid_vector(x - emf_tic1077, y - emf_tic621); EMF_solid_vector(x + emf_tic1077, y - emf_tic621); EMF_flush_polygon(); break; case 8: /* filled triangle point up */ corners[0].x = x ; corners[0].y = y + emf_tic1241; corners[1].x = x - emf_tic1077 ; corners[1].y = y - emf_tic621; corners[2].x = x + emf_tic1077 ; corners[2].y = y - emf_tic621; EMF_filled_polygon(3, corners); break; case 9: /* draw triangle (point down) */ EMF_move(x, y - emf_tic1241); EMF_solid_vector(x - emf_tic1077, y + emf_tic621); EMF_solid_vector(x + emf_tic1077, y + emf_tic621); EMF_flush_polygon(); break; case 10: /* filled triangle point down */ corners[0].x = x ; corners[0].y = y - emf_tic1241; corners[1].x = x - emf_tic1077 ; corners[1].y = y + emf_tic621; corners[2].x = x + emf_tic1077 ; corners[2].y = y + emf_tic621; EMF_filled_polygon(3, corners); break; case 11: /* draw diamond */ EMF_move(x - emf_tic, y); EMF_solid_vector(x, y - emf_tic); EMF_solid_vector(x + emf_tic, y); EMF_solid_vector(x, y + emf_tic); EMF_flush_polygon(); break; case 12: /* filled diamond */ corners[0].x = x - emf_tic ; corners[0].y = y; corners[1].x = x ; corners[1].y = y - emf_tic; corners[2].x = x + emf_tic ; corners[2].y = y; corners[3].x = x ; corners[3].y = y + emf_tic; EMF_filled_polygon(4, corners); break; case 13: /* draw pentagon */ EMF_move(x + emf_tic5878, y + emf_tic8090); EMF_solid_vector(x - emf_tic5878, y + emf_tic8090); EMF_solid_vector(x - emf_tic9511, y - emf_tic3090); EMF_solid_vector(x, y - emf_tic); EMF_solid_vector(x + emf_tic9511, y - emf_tic3090); EMF_flush_polygon(); break; case 14: /* filled pentagon */ corners[0].x = x + emf_tic5878; corners[0].y = y + emf_tic8090; corners[1].x = x - emf_tic5878; corners[1].y = y + emf_tic8090; corners[2].x = x - emf_tic9511; corners[2].y = y - emf_tic3090; corners[3].x = x; corners[3].y = y - emf_tic; corners[4].x = x + emf_tic9511; corners[4].y = y - emf_tic3090; EMF_filled_polygon(5, corners); break; } /* end_points: */ emf_dashtype = old_dashtype; emf_dashtype_count++; } TERM_PUBLIC void EMF_set_pointsize(double size) { if (size < 0) size = 1; emf_tic = (size * term->h_tic); emf_tic707 = floor((double)emf_tic * 0.707 + 0.5); emf_tic866 = emf_tic * 13 / 15; emf_tic500 = emf_tic / 2; emf_tic1241 = emf_tic * 36 / 29; emf_tic1077 = emf_tic * 14 / 13; emf_tic9511 = emf_tic * 0.9511; emf_tic5878 = emf_tic * 0.5878; emf_tic8090 = emf_tic * 0.8090; emf_tic3090 = emf_tic * 0.3090; emf_tic621 = emf_tic * 18 / 29; } /* * Ethan A Merritt September 2008 * - Support for enhanced text mode * PROBLEMS: * - Rotated enhanced text is not handled * - The proportional spacing hack is really ugly * ETO_PDY is supposed to handle this, but pre-Vista Windows * doesn't support the flag so it's of no real use. */ static TBOOLEAN ENHemf_opened_string; /* used in determining height of processed text */ static float ENHemf_base; /* use these so that we don't over-write the current font settings */ static float ENHemf_fontsize; static char *ENHemf_font; static TBOOLEAN ENHemf_show = TRUE; static TBOOLEAN ENHemf_sizeonly = FALSE; static int ENHemf_overprint = 0; TERM_PUBLIC void ENHemf_OPEN( char *fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint) { /* If the overprint code requests a save or restore, that's all we do */ #define EMF_AVG_WID 0.8 #undef TA_UPDATECP_MODE #ifdef TA_UPDATECP_MODE if (overprint == 3) { EMF_SaveDC(); return; } else if (overprint == 4) { EMF_RestoreDC(); return; } #else static int save_x, save_y; if (overprint == 3) { save_x = emf_posx; save_y = emf_posy; return; } else if (overprint == 4) { emf_posx = save_x; emf_posy = save_y; return; } #endif if (!ENHemf_opened_string) { int i; ENHemf_opened_string = TRUE; enhanced_cur_text = &enhanced_text[0]; free(ENHemf_font); ENHemf_font = gp_strdup(fontname); for (i=0; ENHemf_font[i]; i++) if (ENHemf_font[i] == ':') ENHemf_font[i] = ' '; ENHemf_fontsize = fontsize; ENHemf_base = base * emf_fontscale; ENHemf_show = showflag; ENHemf_overprint = overprint; } } /* Write a string fragment and update the current position */ TERM_PUBLIC void ENHemf_FLUSH() { unsigned int x, y; int x_offset, y_offset; char *str; int i; int incr_x; double strl; if (ENHemf_opened_string) { *enhanced_cur_text = '\0'; ENHemf_opened_string = FALSE; x = emf_posx; y = emf_posy; if (1) { char save_font[256]; float save_fontsize = emf_fontsize; strcpy(save_font,emf_fontname); emf_fontsize = ENHemf_fontsize; EMF_set_font(ENHemf_font); emf_fontsize = save_fontsize; strcpy(emf_fontname,save_font); } str = enhanced_text; #ifndef GP_TA_UPDATEPC_MODE /* We are especially bad at guessing the width of whitespace. */ /* Best is to pile up all our errors on top of leading space. */ i = strspn(enhanced_text," "); if (i > 0) { double blank = i * term->h_char * EMF_AVG_WID; x += cos(emf_vert_text * EMF_10THDEG2RAD) * blank; y += sin(emf_vert_text * EMF_10THDEG2RAD) * blank; emf_posx = x; emf_posy = y; str += i; } #endif x_offset = sin(emf_vert_text * EMF_10THDEG2RAD) * ENHemf_base * EMF_PX2HM; y_offset = cos(emf_vert_text * EMF_10THDEG2RAD) * ENHemf_base * EMF_PX2HM; if (ENHemf_show && !ENHemf_sizeonly) EMF_put_text(x-x_offset, y+y_offset, str); if (encoding == S_ENC_UTF8) { /* The strwidth_utf8() function approximates the space occupied */ /* by a UTF8 string, taking into account that CJK characters */ /* are rougnly twice as wide as a typical ascii character. */ strl = strwidth_utf8(str); } else { /* Otherwise we assume 1 equal-width character per byte. */ strl = strlen(str); } if (emf_tweak) { /* Tweak estimated length of rendered string by counting "thin" */ /* characters and "wide" characters. */ /* In principle EMF will accept an array of char widths, but */ /* most EMF viewers don't implement this option (ETO_PDY). */ int thin = 0, wide = 0; for (i = 0; i < strlen(str); i++) { if ((encoding == S_ENC_UTF8) && ((str[i] & 0x100))) continue; if (strchr(" ijl.,;:|!()[]I-'",str[i])) thin++; if (('A' <= str[i] && str[i] <= 'Z') || strchr("mw<>",str[i])) wide++; if (strchr(" i.,;:|!'",str[i])) /* really thin */ thin++; } strl += 0.30 * wide; strl -= 0.15 * thin; } incr_x = strl * EMF_AVG_WID * term->h_char; /* Attempt to handle slanted text. Not entirely successful */ emf_posx = x + incr_x * cos(emf_vert_text * EMF_10THDEG2RAD); emf_posy = y + incr_x * sin(emf_vert_text * EMF_10THDEG2RAD); FPRINTF((stderr,"fontwidth = %d text box: %d x %d\n", (int)(EMF_AVG_WID * term->h_char), (int)(incr_x * cos(emf_vert_text * EMF_10THDEG2RAD)), (int)(incr_x * sin(emf_vert_text * EMF_10THDEG2RAD)))); if (ENHemf_overprint == 1) { emf_posx -= 0.5 * incr_x * cos(emf_vert_text * EMF_10THDEG2RAD); emf_posy -= 0.5 * incr_x * sin(emf_vert_text * EMF_10THDEG2RAD); } } } TERM_PUBLIC void ENHemf_put_text(unsigned int x, unsigned int y, const char *str) { char *original_string = (char *) str; if (str == NULL || *str == '\0') return; /* if there are no magic characters, we should just be able * punt the string to EMF_put_text() */ if (ignore_enhanced_text || !strpbrk(str, "{}^_@&~")) { /* FIXME: do something to ensure default font is selected */ EMF_put_text(x,y,str); return; } EMF_move(x,y); if (emf_textcolor != emf_color) { EMF_SetTextColor(emf_color); emf_textcolor = emf_color; } /* set up the global variables needed by enhanced_recursion() */ enhanced_fontscale = 1.0; strncpy(enhanced_escape_format,"&#x%2.2x;",sizeof(enhanced_escape_format)); ENHemf_opened_string = FALSE; ENHemf_overprint = 0; ENHemf_fontsize = emf_fontsize; if (emf_justify == RIGHT || emf_justify == CENTRE) ENHemf_sizeonly = TRUE; #ifdef UPDATECP_MODE EMF_SetTextAlign(GP_TA_BASELINE|GP_TA_LEFT|GP_TA_UPDATECP); #else EMF_SetTextAlign(GP_TA_BASELINE|GP_TA_LEFT|GP_TA_NOUPDATECP); #endif /* Set the recursion going. We say to keep going until a * closing brace, but we don't really expect to find one. * If the return value is not the nul-terminator of the * string, that can only mean that we did find an unmatched * closing brace in the string. We increment past it (else * we get stuck in an infinite loop) and try again. */ while (*(str = enhanced_recursion((char *)str, TRUE, emf_fontname, ENHemf_fontsize, 0.0, TRUE, TRUE, 0))) { (term->enhanced_flush)(); /* I think we can only get here if *str == '}' */ enh_err_check(str); if (!*++str) break; /* end of string */ /* else carry on and process the rest of the string */ } /* EAM May 2010 - 2-pass text justification */ /* We can do text justification by running the entire top level string */ /* through 2 times, with the ENHgd_sizeonly flag set the first time. */ /* After seeing where the final position is, we then offset the start */ /* point accordingly and run it again without the sizeonly flag set. */ if (emf_justify == RIGHT || emf_justify == CENTRE) { enum JUSTIFY justification = emf_justify; int x_offset = emf_posx - x; int y_offset = (emf_vert_text == 0) ? 0 : emf_posy - y; emf_justify = LEFT; ENHemf_sizeonly = FALSE; if (justification == RIGHT) ENHemf_put_text(x - x_offset, y - y_offset, original_string); else if (justification == CENTRE) ENHemf_put_text(x - x_offset/2, y - y_offset/2, original_string); emf_justify = justification; } /* Restore everything we messed with */ EMF_setfont(); free(emf_last_fontname); /* invalidate any previous font */ emf_last_fontname = NULL; ENHemf_base = 0; } #endif /* TERM_BODY */ #ifdef TERM_TABLE TERM_TABLE_START(emf_driver) "emf", "Enhanced Metafile format", EMF_XMAX, EMF_YMAX, EMF_VCHAR, EMF_HCHAR, EMF_VTIC, EMF_HTIC, EMF_options, EMF_init, EMF_reset, EMF_text, null_scale, EMF_graphics, EMF_move, EMF_solid_vector, EMF_linetype, EMF_put_text, EMF_text_angle, EMF_justify_text, EMF_point, do_arrow, EMF_set_font, EMF_set_pointsize, TERM_BINARY|TERM_CAN_DASH|TERM_LINEWIDTH|TERM_FONTSCALE, NULL, /* suspend */ NULL, /* resume */ EMF_fillbox, EMF_linewidth #ifdef USE_MOUSE , 0, 0, 0, 0, 0 /* no mouse support for emf */ #endif , EMF_make_palette, EMF_previous_palette, EMF_set_color, EMF_filled_polygon , NULL /* image */ , ENHemf_OPEN, ENHemf_FLUSH, do_enh_writec , NULL /* layer */ , NULL /* path */ , 0.0 , NULL /* hypertext */ #ifdef EAM_BOXED_TEXT , NULL #endif , NULL /* modify_plots */ , EMF_dashtype TERM_TABLE_END(emf_driver) #undef LAST_TERM #define LAST_TERM emf_driver #endif /* TERM_TABLE */ #endif /* TERM_PROTO_ONLY */ #ifdef TERM_HELP START_HELP(emf) "1 emf", "?commands set terminal emf", "?set terminal emf", "?set term emf", "?terminal emf", "?term emf", "?emf", " The `emf` terminal generates an Enhanced Metafile Format file.", " This file format is recognized by many Windows applications.", "", " Syntax:", " set terminal emf {color | monochrome}", " {enhanced {noproportional}}", " {rounded | butt}", " {linewidth } {dashlength
}", " {size XX,YY} {background }", " {font \"{,}\"}", " {fontscale }", "", " In `monochrome` mode successive line types cycle through dash patterns.", " `linewidth ` multiplies all line widths by this factor.", " `dashlength ` is useful for thick lines.", " is the name of a font; and ", " `` is the size of the font in points.", "", " The nominal size of the output image defaults to 1024x768 in arbitrary", " units. You may specify a different nominal size using the `size` option.", "", " Enhanced text mode tries to approximate proportional character spacing.", " If you are using a monospaced font, or don't like the approximation, you", " can turn off this correction using the `noproportional` option.", "", " The default settings are `color font \"Arial,12\" size 1024,768`", " Selecting `default` sets all options to their default values.", "", " Examples:", " set terminal emf 'Times Roman Italic, 12'" END_HELP(emf) #endif /* TERM_HELP */