/* Hey Emacs this is -*- C -*-
 * $Id: emf.trm,v 1.78.2.9 2013/09/17 23:40:33 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
 *
 * 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 <stephane.barbaray@compodata.com>, Ethan Merritt <merritt@u.washington.edu>
 * - 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 <stephane.barbaray@compodata.com> 
 * - 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 <merritt@u.washington.edu> 
 * - 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 <stephane.barbaray@compodata.com>
 *   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_linecolor __PROTO((int color));
TERM_PUBLIC void EMF_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 13		/* 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);			\
} 

#endif /* TERM_PROTO */

#ifndef TERM_PROTO_ONLY
#ifdef TERM_BODY

#include <ctype.h>		/* for isspace() */
#ifdef HAVE_ICONV
#include <iconv.h>
#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 = FALSE;
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 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;	/* 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_dashtype needed before drawing */

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 (c_token != 2) {
	term->xmax = EMF_XMAX;
	term->ymax = EMF_YMAX;
	emf_dashed = FALSE;
	emf_monochrome = FALSE;
	emf_background = 0xffffff;
	emf_tweak = TRUE;
    }

    while (!END_OF_COMMAND) {
	if (almost_equals(c_token, "de$fault")) {
	    strcpy(emf_defaultfontname, EMF_FONTNAME);
	    emf_defaultfontsize = EMF_FONTSIZE;
	    emf_monochrome = FALSE;
	    emf_dashed = 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")) {
	    emf_dashed = TRUE;
	    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 (almost_equals(c_token, "s$olid")) {
	    emf_dashed = FALSE;
	    c_token++;
	    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();
    }
#if (0)	/* Shige May 2013 - Does not function properly until we enter graphics mode */
    EMF_set_font(NULL);		/* set default font */
#endif
    
    sprintf(term_options, "%s %s %s font \"%s,%g\"",
	    emf_monochrome ? "monochrome" : "color",
	    emf_dashed ? "dashed" : "solid",
	    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);
    EMF_set_font(NULL);		/* init default font */

    emf_color = emf_textcolor = LT_UNDEFINED;
}

TERM_PUBLIC int 
EMF_set_font(const char *font)
{
    static float last_fontsize = -1;
    static char last_fontname[256] = {'\0'};

    /* 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 (!strcmp(last_fontname, emf_fontname) && last_fontsize == emf_fontsize) {
	return TRUE;
    } else {
	safe_strncpy(last_fontname, emf_fontname, sizeof(last_fontname));
	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();

    /* Note : separating linetype and color would have not been futile, but anyway... */

#if (0)  /* Not safe because linetype/color/pen are intermixed */
    if (linetype == emf_linetype)
	return;
#endif

    if (linetype == LT_NODRAW)
	linetype = LT_BACKGROUND;
	
    emf_linetype = linetype;

    EMF_linecolor(linetype);
    EMF_dashtype(linetype);
}

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 = emf_linetype;
    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: /* pattern fill implemented as partial density */
		fillpar *= 12;
	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);
	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, 0, color);
	EMF_SelectObject(EMF_HANDLE_PEN);

	EMF_CreatePolygon(points);
	for (i=0; i<points; i++)
	    EMF_write_pointl(corners[i].x, term->ymax - 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_dashtype(emf_dashtype);
       emf_dashtype_count = 0;	/* EMF_dashtype clears this also (shrug) */
    }
}

/*
 * Resets _both_ line color and dash type!
 */
TERM_PUBLIC void 
EMF_dashtype(int dashtype)
{
    int i, j;
    /* Each group of 8 entries in dot_length[] defines a dash
       pattern.  Entries in each group are alternately length of
       whitespace and length of line, in units of 2/3 of the
       linewidth. */
    static int dot_length[EMF_LINE_TYPES * 8] =
    {				/* 0 - solid             */
	5, 8, 5, 8, 5, 8, 5, 8,	/* 1 - dashes            */
	4, 2, 4, 2, 4, 2, 4, 2,	/* 2 - dotted            */
	4, 8, 4, 2, 4, 8, 4, 2,	/* 3 - dash-dot          */
	4, 9, 4, 2, 4, 2, 0, 0,	/* 4 - dash-dot-dot      */
    };

    emf_dashtype = dashtype;

    if (dashtype >= 0)
	dashtype = dashtype % EMF_LINE_TYPES;

    if (dashtype == LT_AXIS)
	dashtype = 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;
	}
	/* first thing drawn will be a line */
	emf_step = emf_step_sizes[1];
	emf_step_index = 1;
    }

    emf_dashtype_count = 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 specified 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)
	    EMF_solid_vector((int) (ux - (remain * dx) / dist),
			     (int) (uy - (remain * dy) / dist));
	else {
	    xa = (int) (ux - (remain * dx) / dist);
	    ya = (int) (uy - (remain * dy) / dist);
	    EMF_move(xa, ya);
	}
	if (++emf_step_index >= 8)
	    emf_step_index = 0;
	emf_step = emf_step_sizes[emf_step_index];
    }
    if (emf_step_index & 1)
	EMF_solid_vector(ux, uy);
    else
	EMF_move(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;
    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;
    }

    /* SB 20040506: offset array now included since it won't work with MS Security patch (kb835732) */
    /* SB 20040506: also changed function from unicode to ansi version */
    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); 
    /* SB 20040506: end of modification */

    /* 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, 1, 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, 1, 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;
    }
/* 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_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) {
	ENHemf_opened_string = TRUE;
	enhanced_cur_text = &enhanced_text[0];
	ENHemf_font = fontname;
	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);


	    strl = strlen(str);

	    if (emf_tweak) {
		/* Tweak estimated length of rendered string by counting "thin" */
		/* characters. In principle EMF will accept an array of char    */
		/* widths. But EMF viewers don't implement this option (ETO_PDY */
		{
		int thin = 0, wide = 0;
		for (i=0; i<strlen(str); i++) {
		    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++;
		}
		incr_x = (strl * EMF_AVG_WID 
			+ (double)(wide) * 0.3
			+ (double)(-thin) * 0.2)
			* term->h_char;
		}
	    } else {
		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_font = emf_fontname;
    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,
			ENHemf_font, 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(); /* Necessary? */
    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
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} {solid | dashed}",
"                        {enhanced {noproportional}}",
"                        {rounded | butt}",
"                        {linewidth <LW>} {dashlength <DL>}",
"                        {size XX,YY} {background <rgb_color>}",
"                        {font \"<fontname>{,<fontsize>}\"}",
"                        {fontscale <scale>}",
"",
" In `monochrome` mode successive line types cycle through dash patterns.",
" In `color` mode successive line types use successive colors, and only after",
" all 8 default colors are exhausted is the dash pattern incremented.",
" `solid` draws all curves with solid lines, overriding any dashed patterns;",
" `linewidth <factor>` multiplies all line widths by this factor.",
" `dashlength <factor>` is useful for thick lines.",
" <fontname> is the name of a font; and ",
" `<fontsize>` 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 solid font \"Arial,12\" size 1024,768`",
" Selecting `default` sets all options to their default values.",
"",
" Examples:",
"       set terminal emf 'Times Roman Italic, 12'",
"       set terminal emf dashed    # otherwise all lines are solid"
END_HELP(emf)
#endif /* TERM_HELP */
