/*
 * Fig2dev: Translate Fig code to various Devices
 * Copyright (c) 1991 by Micah Beck
 * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2015 by Brian V. Smith
 * Parts Copyright (c) 2015-2023 by Thomas Loimer
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense and/or sell copies
 * of the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */

/*
 * genemf.c: convert fig to Enhanced MetaFile
 *
 * Written by Michael Schrick (2001-03-04)
 * Adapted from gencgm.c
 *
 * Changes:
 *
 *   2004/02/10 - Added support for locale text (if iconv() is available),
 *		  arc box, open arc, rotated ellipse, picture,
 *		  dash-triple-dotted line style, and all fill patterns.
 *		  (ITOH Yasufumi)
 *
 *   2003/01/14 - Added text orientation, text alignment.  Corrected color
 *		  table and added text color.  Added transparency, fill
 *		  colors and patterns. (M. Schrick)
 *
 *   2001/10/03 - Added htof functions for big endian machines (M. Schrick)
 *
 *   2001/09/24 - Added filled polygons, circles and ellipses (M. Schrick)
 *
 *   2001/03/04 - Created from gencgm.c (M. Schrick)
 *
 * Limitations:
 *
 * - old style splines are not supported by this driver. New style
 *   (X) splines are automatically converted to polylines and thus
 *   are supported.
 *
 * - Windows 95 supports linestyles only for the thin line.  Lines with
 *   styles will converted to the thin width.  Dash-triple-dotted linestyle
 *   is not supported.
 *   Windows 98/Me don't seem to support linestyles well.
 *
 * - Windows 95/98/Me supports only 12 patterns out of FIG's 22 fill pattern.
 *   Unsupported patterns will be shown differently.
 *
 * - Windows 95/98/Me doesn't support line cap and join styles. The correct
 *   appearance of arrows depends on the cap style the viewer uses (not
 *   known at conversion time, but can be set using the -r driver option).
 *
 * - an EMF file may look quite different when viewed with different
 *   EMF capable drawing programs. This is especially so for text:
 *   the text font e.g. needs to be recognized and supported by the
 *   viewer.  Same is true for special characters in text strings, etc.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>		/* isascii() */
#endif
#include <ctype.h>
#include <math.h>
#ifdef HAVE_ICONV
#include <iconv.h>
#endif

#include "fig2dev.h"	/* includes bool.h and object.h */
//#include "object.h"
#include "genemf.h"
#include "messages.h"
#include "pi.h"
#include "readpics.h"

#define UNDEFVALUE	-100	/* UNDEFined attribute value */
#define EPSILON		1e-4	/* small floating point value */

typedef enum {EMH_RECORD, EMH_DATA} emh_flag;
static ENHMETAHEADER emh;	/* The Enhanced MetaFile header, which is the
				 * first record in the file must be updated
				 * to reflect the total number of records and
				 * the total number of bytes.  This record
				 * contains little endian data and should not
				 * be used internally without a conversion. */
/* Internal variables in host format */
static unsigned emh_nBytes;	/* Size of the metafile in bytes */
static unsigned emh_nRecords;	/* Number of records in the metafile */
static unsigned emh_nHandles;	/* Number of handles in the handle table */

/*
 * Limit maximum number of handles.
 * XXX limit to which number?
 */
static unsigned maxHandles = 50;

/* Data types for keeping track of the device context handles */
enum emfhandletype { EMFH_PEN, EMFH_BRUSH, EMFH_FONT, EMFH_MAX } type;

static unsigned lasthandle[EMFH_MAX];	/* Last device handle */

struct emfhandle {
	struct emfhandle *next;	/* This field must be first */
	struct emfhandle **prev;
	enum emfhandletype type;
	bool	is_current;	/* true: currently selected */
	unsigned	handle;
	union {
		struct pen {
			int pstyle, pwidth, prgb;
		} p;
		struct brush {
			int bstyle;
			int brgb, bpattern, bhatchbkrgb;
		} b;
		struct font {
			int ffont;
			int fsize;
			int fangle;
		} f;
	} eh_un;
};

static struct emfhandle *handles;
static struct emfhandle *latesthandle;


static int rounded_arrows;	/* If rounded_arrows is false, the position
				 * of arrows will be corrected for
				 * compensating line width effects. This
				 * correction is not needed if arrows appear
				 * rounded with the used EMF viewer.
				 * See -r driver command line option. */

/* EMF compatibility level */
static enum emfcompatlevel {
	EMF_LEVEL_WIN95,	/* Windows 95 */
	EMF_LEVEL_WIN98,	/* Windows 98/Me */
	EMF_LEVEL_WINNT	/* Windows NT/2000/XP (Windows NT 3.1 and later) */
} emflevel = EMF_LEVEL_WINNT;
const char *const emflevelname[] = { "win95", "win98", "winnt" };

typedef struct Dir {double x, y;} Dir;
#define ARROW_INDENT_DIST	0.8
#define ARROW_POINT_DIST	1.2
/* Arrows appear this much longer with projected line caps. */
#define ARROW_EXTRA_LEN(a)	((double)a->ht / (double)a->wid * a->thickness)

/* update bounding box */
#define UPDATE_BBX_X(x) \
    do { \
	if (bbx_left>(x)) bbx_left=(x); else if (bbx_right<(x)) bbx_right=(x);\
    } while (0)
#define UPDATE_BBX_Y(y) \
    do { \
	if (bbx_top>(y)) bbx_top=(y); else if (bbx_bottom<(y)) bbx_bottom=(y);\
    } while (0)


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

/*
 * fill pattern bitmaps
 */

/* pat0.xbm */
#define pat0_width 8
#define pat0_height 4
static unsigned char pat0_bits[] = {
	0x03, 0x0c, 0x30, 0xc0
};

/* pat1.xbm */
#define pat1_width 8
#define pat1_height 4
static unsigned char pat1_bits[] = {
	0xc0, 0x30, 0x0c, 0x03
};

/* pat2.xbm */
#define pat2_width 8
#define pat2_height 4
static unsigned char pat2_bits[] = {
	0x81, 0x66, 0x18, 0x66
};

#if 0	/* mapped to EMF pattern */
/* pat3.xbm */
#define pat3_width 8
#define pat3_height 8
static unsigned char pat3_bits[] = {
   0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

/* pat4.xbm */
#define pat4_width 8
#define pat4_height 8
static unsigned char pat4_bits[] = {
   0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};

/* pat5.xbm */
#define pat5_width 8
#define pat5_height 8
static unsigned char pat5_bits[] = {
   0x11, 0x0a, 0x04, 0x0a, 0x11, 0xa0, 0x40, 0xa0};
#endif

/* pat6.xbm */
#define pat6_width 16
#define pat6_height 16
static unsigned char pat6_bits[] = {
	0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80,
	0x00, 0x80, 0xff, 0xff, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
	0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xff, 0xff
};

/* pat7.xbm */
#define pat7_width 16
#define pat7_height 16
static unsigned char pat7_bits[] = {
	0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};

#if 0	/* mapped to EMF pattern */
/* pat8.xbm */
#define pat8_width 8
#define pat8_height 4
static unsigned char pat8_bits[] = {
   0xff, 0x00, 0x00, 0x00};

/* pat9.xbm */
#define pat9_width 8
#define pat9_height 4
static unsigned char pat9_bits[] = {
   0x88, 0x88, 0x88, 0x88};

/* pat10.xbm */
#define pat10_width 8
#define pat10_height 4
static unsigned char pat10_bits[] = {
   0xff, 0x88, 0x88, 0x88};
#endif

/* pat11.xbm */
#define pat11_width 24
#define pat11_height 24
static unsigned char pat11_bits[] = {
	0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40,
	0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff,
	0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00,
	0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0xff, 0xff, 0xff,
	0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00,
	0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0xff, 0xff, 0xff
};

/* pat12.xbm */
#define pat12_width 24
#define pat12_height 24
static unsigned char pat12_bits[] = {
	0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20,
	0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff,
	0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00,
	0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff,
	0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00,
	0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff
};

/* pat13.xbm */
#define pat13_width 24
#define pat13_height 24
static unsigned char pat13_bits[] = {
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	0x80, 0x80, 0x81, 0x80, 0x80, 0x86, 0x80, 0x80, 0x98, 0x80, 0x80, 0xe0,
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	0x80, 0x81, 0x80, 0x80, 0x86, 0x80, 0x80, 0x98, 0x80, 0x80, 0xe0, 0x80,
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	0x81, 0x80, 0x80, 0x86, 0x80, 0x80, 0x98, 0x80, 0x80, 0xe0, 0x80, 0x80
};

/* pat14.xbm */
#define pat14_width 24
#define pat14_height 24
static unsigned char pat14_bits[] = {
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	0x80, 0xc0, 0x80, 0x80, 0xb0, 0x80, 0x80, 0x8c, 0x80, 0x80, 0x83, 0x80,
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	0x80, 0x80, 0xc0, 0x80, 0x80, 0xb0, 0x80, 0x80, 0x8c, 0x80, 0x80, 0x83,
	0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
	0xc0, 0x80, 0x80, 0xb0, 0x80, 0x80, 0x8c, 0x80, 0x80, 0x83, 0x80, 0x80
};

/* pat15.xbm */
#define pat15_width 16
#define pat15_height 8
static unsigned char pat15_bits[] = {
	0x40, 0x02, 0x30, 0x0c, 0x0e, 0x70, 0x01, 0x80, 0x02, 0x40, 0x0c, 0x30,
	0x70, 0x0e, 0x80, 0x01
};

/* pat16.xbm */
#define pat16_width 8
#define pat16_height 8
static unsigned char pat16_bits[] = {
	0x01, 0x01, 0x82, 0x6c, 0x10, 0x10, 0x28, 0xc6
};

/* pat17.xbm */
#define pat17_width 16
#define pat17_height 16
static unsigned char pat17_bits[] = {
	0xe0, 0x0f, 0x18, 0x30, 0x04, 0x40, 0x02, 0x80, 0x02, 0x80, 0x01, 0x00,
	0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
	0x02, 0x80, 0x02, 0x80, 0x04, 0x40, 0x18, 0x30
};

/* pat18.xbm */
#define pat18_width 30
#define pat18_height 18
static unsigned char pat18_bits[] = {
	0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00,
	0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00,
	0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0x3f,
	0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00,
	0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
	0x08, 0x80, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00
};

/* pat19.xbm */
#define pat19_width 16
#define pat19_height 16
static unsigned char pat19_bits[] = {
	0xe0, 0x0f, 0x10, 0x10, 0x08, 0x20, 0x04, 0x40, 0x02, 0x80, 0x01, 0x00,
	0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
	0x02, 0x80, 0x04, 0x40, 0x08, 0x20, 0x10, 0x10
};

/* pat20.xbm */
#define pat20_width 8
#define pat20_height 8
static unsigned char pat20_bits[] = {
	0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x80
};

/* pat21.xbm */
#define pat21_width 8
#define pat21_height 8
static unsigned char pat21_bits[] = {
	0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x01
};

const struct emf_bmpat {
	unsigned width, height;
	unsigned char *bits;
} emf_bm_pattern[22] = {
	{ pat0_width, pat0_height, pat0_bits },
	{ pat1_width, pat1_height, pat1_bits },
	{ pat2_width, pat2_height, pat2_bits },
	{ 0, 0, 0 },	/* mapped to EMF pattern */
	{ 0, 0, 0 },	/* mapped to EMF pattern */
	{ 0, 0, 0 },	/* mapped to EMF pattern */
	{ pat6_width, pat6_height, pat6_bits },
	{ pat7_width, pat7_height, pat7_bits },
	{ 0, 0, 0 },	/* mapped to EMF pattern */
	{ 0, 0, 0 },	/* mapped to EMF pattern */
	{ 0, 0, 0 },	/* mapped to EMF pattern */
	{ pat11_width, pat11_height, pat11_bits },
	{ pat12_width, pat12_height, pat12_bits },
	{ pat13_width, pat13_height, pat13_bits },
	{ pat14_width, pat14_height, pat14_bits },
	{ pat15_width, pat15_height, pat15_bits },
	{ pat16_width, pat16_height, pat16_bits },
	{ pat17_width, pat17_height, pat17_bits },
	{ pat18_width, pat18_height, pat18_bits },
	{ pat19_width, pat19_height, pat19_bits },
	{ pat20_width, pat20_height, pat20_bits },
	{ pat21_width, pat21_height, pat21_bits }
};

/* EMF patterns are numbered 0-5 (I use -1 for bitmap patterns) */
int emf_map_pattern[22] = { -1, -1, -1, HS_FDIAGONAL,
			 HS_BDIAGONAL, HS_DIAGCROSS, -1, -1,
			 HS_HORIZONTAL, HS_VERTICAL, HS_CROSS, -1,
			 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

static int ltFonts[] = {	/* Convert from LaTeX fonts to PostScript */
		0,		/* default */
		0,		/* Roman */
		2,		/* Bold */
		1,		/* Italic */
		4,		/* Modern */
		12,		/* Typewriter */
		};

static char *lfFaceName[] = {
		"Times",		/* Times-Roman */
		"Times",		/* Times-Italic */
		"Times",		/* Times-Bold */
		"Times",		/* Times-BoldItalic */
		"AvantGarde",		/* AvantGarde */
		"AvantGarde",		/* AvantGarde-BookOblique */
		"AvantGarde",		/* AvantGarde-Demi */
		"AvantGarde",		/* AvantGarde-DemiOblique */
		"Bookman",		/* Bookman-Light */
		"Bookman",		/* Bookman-LightItalic */
		"Bookman",		/* Bookman-Demi */
		"Bookman",		/* Bookman-DemiItalic */
		"Courier",		/* Courier */
		"Courier",		/* Courier-Oblique */
		"Courier",		/* Courier-Bold */
		"Courier",		/* Courier-BoldItalic */
		"Helvetica",		/* Helvetica */
		"Helvetica",		/* Helvetica-Oblique */
		"Helvetica",		/* Helvetica-Bold */
		"Helvetica",		/* Helvetica-BoldOblique */
		"Helvetica-Narrow",	/* Helvetica-Narrow */
		"Helvetica-Narrow",	/* Helvetica-Narrow-Oblique */
		"Helvetica-Narrow",	/* Helvetica-Narrow-Bold */
		"Helvetica-Narrow",	/* Helvetica-Narrow-BoldOblique */
		"NewCenturySchlbk",	/* NewCenturySchlbk-Roman */
		"NewCenturySchlbk",	/* NewCenturySchlbk-Italic */
		"NewCenturySchlbk",	/* NewCenturySchlbk-Bold */
		"NewCenturySchlbk",	/* NewCenturySchlbk-BoldItalic */
		"Palatino",		/* Palatino-Roman */
		"Palatino",		/* Palatino-Italic */
		"Palatino",		/* Palatino-Bold */
		"Palatino",		/* Palatino-BoldItalic */
		"Symbol",		/* Symbol */
		"ZapfChancery",		/* ZapfChancery-MediumItalic */
		"ZapfDingbats"		/* ZapfDingbats */
		};

static int	lfWeight[] = {
		FW_NORMAL,		/* Times-Roman */
		FW_NORMAL,		/* Times-Italic */
		FW_BOLD,		/* Times-Bold */
		FW_BOLD,		/* Times-BoldItalic */
		FW_NORMAL,		/* AvantGarde */
		FW_NORMAL,		/* AvantGarde-BookOblique */
		FW_DEMIBOLD,		/* AvantGarde-Demi */
		FW_DEMIBOLD,		/* AvantGarde-DemiOblique */
		FW_LIGHT,		/* Bookman-Light */
		FW_LIGHT,		/* Bookman-LightItalic */
		FW_DEMIBOLD,		/* Bookman-Demi */
		FW_DEMIBOLD,		/* Bookman-DemiItalic */
		FW_NORMAL,		/* Courier */
		FW_NORMAL,		/* Courier-Oblique */
		FW_BOLD,		/* Courier-Bold */
		FW_BOLD,		/* Courier-BoldItalic */
		FW_NORMAL,		/* Helvetica */
		FW_NORMAL,		/* Helvetica-Oblique */
		FW_BOLD,		/* Helvetica-Bold */
		FW_BOLD,		/* Helvetica-BoldOblique */
		FW_NORMAL,		/* Helvetica-Narrow */
		FW_NORMAL,		/* Helvetica-Narrow-Oblique */
		FW_BOLD,		/* Helvetica-Narrow-Bold */
		FW_BOLD,		/* Helvetica-Narrow-BoldOblique */
		FW_NORMAL,		/* NewCenturySchlbk-Roman */
		FW_NORMAL,		/* NewCenturySchlbk-Italic */
		FW_BOLD,		/* NewCenturySchlbk-Bold */
		FW_BOLD,		/* NewCenturySchlbk-BoldItalic */
		FW_NORMAL,		/* Palatino-Roman */
		FW_NORMAL,		/* Palatino-Italic */
		FW_BOLD,		/* Palatino-Bold */
		FW_BOLD,		/* Palatino-BoldItalic */
		FW_NORMAL,		/* Symbol */
		FW_NORMAL,		/* ZapfChancery-MediumItalic */
		FW_NORMAL		/* ZapfDingbats */
		};

static uchar	lfItalic[] = {
		false,			/* Times-Roman */
		true,			/* Times-Italic */
		false,			/* Times-Bold */
		true,			/* Times-BoldItalic */
		false,			/* AvantGarde */
		true,			/* AvantGarde-BookOblique */
		false,			/* AvantGarde-Demi */
		true,			/* AvantGarde-DemiOblique */
		false,			/* Bookman-Light */
		true,			/* Bookman-LightItalic */
		false,			/* Bookman-Demi */
		true,			/* Bookman-DemiItalic */
		false,			/* Courier */
		true,			/* Courier-Oblique */
		false,			/* Courier-Bold */
		true,			/* Courier-BoldItalic */
		false,			/* Helvetica */
		true,			/* Helvetica-Oblique */
		false,			/* Helvetica-Bold */
		true,			/* Helvetica-BoldOblique */
		false,			/* Helvetica-Narrow */
		true,			/* Helvetica-Narrow-Oblique */
		false,			/* Helvetica-Narrow-Bold */
		true,			/* Helvetica-Narrow-BoldOblique */
		false,			/* NewCenturySchlbk-Roman */
		true,			/* NewCenturySchlbk-Italic */
		false,			/* NewCenturySchlbk-Bold */
		true,			/* NewCenturySchlbk-BoldItalic */
		false,			/* Palatino-Roman */
		true,			/* Palatino-Italic */
		false,			/* Palatino-Bold */
		true,			/* Palatino-BoldItalic */
		false,			/* Symbol */
		false,			/* ZapfChancery-MediumItalic */
		false			/* ZapfDingbats */
		};

static uchar	lfCharSet[] = {
		ANSI_CHARSET,		/* Times-Roman */
		ANSI_CHARSET,		/* Times-Italic */
		ANSI_CHARSET,		/* Times-Bold */
		ANSI_CHARSET,		/* Times-BoldItalic */
		ANSI_CHARSET,		/* AvantGarde */
		ANSI_CHARSET,		/* AvantGarde-BookOblique */
		ANSI_CHARSET,		/* AvantGarde-Demi */
		ANSI_CHARSET,		/* AvantGarde-DemiOblique */
		ANSI_CHARSET,		/* Bookman-Light */
		ANSI_CHARSET,		/* Bookman-LightItalic */
		ANSI_CHARSET,		/* Bookman-Demi */
		ANSI_CHARSET,		/* Bookman-DemiItalic */
		ANSI_CHARSET,		/* Courier */
		ANSI_CHARSET,		/* Courier-Oblique */
		ANSI_CHARSET,		/* Courier-Bold */
		ANSI_CHARSET,		/* Courier-BoldItalic */
		ANSI_CHARSET,		/* Helvetica */
		ANSI_CHARSET,		/* Helvetica-Oblique */
		ANSI_CHARSET,		/* Helvetica-Bold */
		ANSI_CHARSET,		/* Helvetica-BoldOblique */
		ANSI_CHARSET,		/* Helvetica-Narrow */
		ANSI_CHARSET,		/* Helvetica-Narrow-Oblique */
		ANSI_CHARSET,		/* Helvetica-Narrow-Bold */
		ANSI_CHARSET,		/* Helvetica-Narrow-BoldOblique */
		ANSI_CHARSET,		/* NewCenturySchlbk-Roman */
		ANSI_CHARSET,		/* NewCenturySchlbk-Italic */
		ANSI_CHARSET,		/* NewCenturySchlbk-Bold */
		ANSI_CHARSET,		/* NewCenturySchlbk-BoldItalic */
		ANSI_CHARSET,		/* Palatino-Roman */
		ANSI_CHARSET,		/* Palatino-Italic */
		ANSI_CHARSET,		/* Palatino-Bold */
		ANSI_CHARSET,		/* Palatino-BoldItalic */
		SYMBOL_CHARSET,		/* Symbol */
		SYMBOL_CHARSET,		/* ZapfChancery-MediumItalic */
		SYMBOL_CHARSET		/* ZapfDingbats */
		};

static uchar	lfPitchAndFamily[] = {
	VARIABLE_PITCH | FF_ROMAN,	/* Times-Roman */
	VARIABLE_PITCH | FF_ROMAN,	/* Times-Italic */
	VARIABLE_PITCH | FF_ROMAN,	/* Times-Bold */
	VARIABLE_PITCH | FF_ROMAN,	/* Times-BoldItalic */
	VARIABLE_PITCH | FF_ROMAN,	/* AvantGarde */
	VARIABLE_PITCH | FF_ROMAN,	/* AvantGarde-BookOblique */
	VARIABLE_PITCH | FF_ROMAN,	/* AvantGarde-Demi */
	VARIABLE_PITCH | FF_ROMAN,	/* AvantGarde-DemiOblique */
	VARIABLE_PITCH | FF_ROMAN,	/* Bookman-Light */
	VARIABLE_PITCH | FF_ROMAN,	/* Bookman-LightItalic */
	VARIABLE_PITCH | FF_ROMAN,	/* Bookman-Demi */
	VARIABLE_PITCH | FF_ROMAN,	/* Bookman-DemiItalic */
	   FIXED_PITCH | FF_MODERN,	/* Courier */
	   FIXED_PITCH | FF_MODERN,	/* Courier-Oblique */
	   FIXED_PITCH | FF_MODERN,	/* Courier-Bold */
	   FIXED_PITCH | FF_MODERN,	/* Courier-BoldItalic */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica-Oblique */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica-Bold */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica-BoldOblique */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica-Narrow */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica-Narrow-Oblique */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica-Narrow-Bold */
	VARIABLE_PITCH | FF_ROMAN,	/* Helvetica-Narrow-BoldOblique */
	VARIABLE_PITCH | FF_ROMAN,	/* NewCenturySchlbk-Roman */
	VARIABLE_PITCH | FF_ROMAN,	/* NewCenturySchlbk-Italic */
	VARIABLE_PITCH | FF_ROMAN,	/* NewCenturySchlbk-Bold */
	VARIABLE_PITCH | FF_ROMAN,	/* NewCenturySchlbk-BoldItalic */
	VARIABLE_PITCH | FF_ROMAN,	/* Palatino-Roman */
	VARIABLE_PITCH | FF_ROMAN,	/* Palatino-Italic */
	VARIABLE_PITCH | FF_ROMAN,	/* Palatino-Bold */
	VARIABLE_PITCH | FF_ROMAN,	/* Palatino-BoldItalic */
	VARIABLE_PITCH | FF_ROMAN,	/* Symbol */
	VARIABLE_PITCH | FF_ROMAN,	/* ZapfChancery-MediumItalic */
	VARIABLE_PITCH | FF_ROMAN,	/* ZapfDingbats */
		};

#ifdef HAVE_ICONV

#define FONT_TIMES_ROMAN	0
#define FONT_TIMES_BOLD		2
#define IS_LOCALE_FONT(f) ((f) == FONT_TIMES_ROMAN || (f) == FONT_TIMES_BOLD)

#define FONT_LOCALE_ROMAN	-1
#define FONT_LOCALE_BOLD	-2

enum genemf_lang {
    LANG_DEFAULT,
    LANG_ZH_CN,
    LANG_ZH_TW,
    LANG_JA,
    LANG_KO
} figLanguage;

/* EUC codeset name for iconv(3) */
static const char *const iconvCharset[] = {
    "ASCII",	/* placeholder */
    "eucCN",
    "eucTW",
    "eucJP",
    "eucKR"
};

/* EMF codeset name for iconv(3) */
#ifdef __hpux
# define EMF_ICONV_CODESET	"ucs2"	/* convert to big endian Unicode */
# define EMF_ICONV_NEED_SWAP	1	/* and swap the byte order */
#else
# define EMF_ICONV_CODESET	"UTF-16LE"
#endif

/* localized fonts */
static const struct localefnt {
	const char *FaceName;	/* font name in EUC of the language */
	int Weight;
	uchar Italic, Charset, PitchAndFamily;
} localeFonts[2][5] = {
	{
		{ "Times", FW_NORMAL, false,
			DEFAULT_CHARSET, FIXED_PITCH|FF_DONTCARE },
		{ "SimSun", FW_NORMAL, false,
			GB2312_CHARSET, FIXED_PITCH|FF_DONTCARE },
		{ "MingLiU", FW_NORMAL, false,
			CHINESEBIG5_CHARSET, FIXED_PITCH|FF_DONTCARE },
		/* MS Mincho */
		{ "\243\315\243\323 \314\300\304\253", FW_NORMAL, false,
			SHIFTJIS_CHARSET, FIXED_PITCH|FF_DONTCARE },
		{ "Batang", FW_NORMAL, false,
			HANGUL_CHARSET, FIXED_PITCH|FF_DONTCARE }
	},
	{
		{ "Times", FW_BOLD, false,
			DEFAULT_CHARSET, FIXED_PITCH|FF_DONTCARE },
		{ "SimHei", FW_MEDIUM, false,
			GB2312_CHARSET, FIXED_PITCH|FF_DONTCARE },
		{ "MingLiU", FW_BOLD, false,
			CHINESEBIG5_CHARSET, FIXED_PITCH|FF_DONTCARE },
		/* MS Gothic */
		{ "\243\315\243\323 \245\264\245\267\245\303\245\257",
			FW_MEDIUM, false,
			SHIFTJIS_CHARSET, FIXED_PITCH|FF_DONTCARE },
		{ "Gungsuh", FW_MEDIUM, false,
			HANGUL_CHARSET, FIXED_PITCH|FF_DONTCARE }
	}
};
#endif	/* HAVE_ICONV */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/


static void delete_handle(struct emfhandle *h);
static void clear_current_handle(enum emfhandletype type);
static void select_object(unsigned handle);
static void use_handle(struct emfhandle *h);
static struct emfhandle *get_handle(enum emfhandletype type);
static int conv_color(int colorIndex);
static int conv_fill_color(int style, int color);
static int conv_fontindex(int font, int flags);
static int conv_capstyle(int cap);
static int conv_joinstyle(int join);
static int conv_linetype(int type);
static void edgeattr(int vis, int type, int width, int color, int join,int cap);
static void edgevis(int onoff);
static void bkmode(int mode);
static void bkcolor(int rgb);
static void create_brush_pattern(unsigned char *bits, int pattern);
static void fillstyle(int fill_color, int fill_style, int pen_color);
static void textfont(int font, double size, int angle);
static void warn_32bit_pos(void);
static void pos2point(F_point *P, F_pos *p);
static double arrow_length(F_arrow *a);
static void arrow(F_point *P, F_arrow *a, F_line *l, Dir *dir);
static void arc_arrow(F_pos *p, F_point *q, F_arrow *arw, F_arc *arc);
static void arc_arrow_adjust(F_point *p, F_point *arwpos, F_arc *arc, double r,
				F_arrow *arw, double adir);
static double arc_radius(F_arc *a);
static void translate(double *x, double *y, double tx, double ty);
static void rotate(double *x, double *y, double angle);
static void arc_rotate(F_point *p, double cx, double cy, double angle);
static void arcoutline(F_arc *a);
static void arcinterior(F_arc *a);
static void arc_reverse(F_arc *arc);
static void circle(F_ellipse *e);
static void ellipse(F_ellipse *e);
static void rotated_ellipse(F_ellipse *e);
static int icprod(int x1, int y1, int x2, int y2);
static int cwarc(F_arc *a);
static int direction(F_point *p, F_point *q, Dir *dir, double *dist);
static double distance(double x1, double y1, double x2, double y2);
static size_t emh_write(const void *ptr, size_t size, size_t nmemb,
			emh_flag flag);
static int is_flip(int rot, int flip);
static void encode_bitmap(F_pic *pic, int bpp, int rot);
static void picbox(F_line *l);
static void polygon(F_line *l);
static int polyline_adjust(F_point *p, F_point *q, double l);
static void polyline(F_line *l);
static void rect(F_line *l);
static void roundrect(F_line *l);
static void shape(F_line *l, int join, int cap, void (*drawshape)(F_line *));
static void shape_interior(F_line *l, void (*drawshape)(F_line *));
static void textunicode(const char *str, int *n_chars, short **utext,
		int *n_unicode);
static void text(int x, int y, RECTL *bbx, char *str);
static void textcolr(int color);
static void textalign(int align);
#ifdef HAVE_ICONV
static void moveto(int x, int y);
#endif

#ifdef HAVE_PNG_H
extern int	read_png(F_pic *pic, struct xfig_stream *restrict pic_stream,
			 int *llx, int *lly);
#endif

/* Piece of code to avoid unnecessary attribute changes */
#define chkcache(val, cachedval)	\
	if (val == cachedval)		\
		return;			\
	else				\
		cachedval = val;


/* Delete an object by handle structure. */
static void
delete_handle(struct emfhandle *h)
{
	EMRSELECTOBJECT em_so;	/* EMRDELETEOBJECT */

	/* If this is selected, select a stock object before deleting it. */
	if (h->is_current) {
		em_so.emr.iType = htofl(EMR_SELECTOBJECT);
		em_so.emr.nSize = htofl(sizeof(EMRSELECTOBJECT));
		switch (h->type) {
		case EMFH_PEN:
			em_so.ihObject = htofl(ENHMETA_STOCK_OBJECT|BLACK_PEN);
			lasthandle[EMFH_PEN] = ENHMETA_STOCK_OBJECT | BLACK_PEN;
			break;
		case EMFH_BRUSH:
			em_so.ihObject = htofl(ENHMETA_STOCK_OBJECT|WHITE_BRUSH);
			lasthandle[EMFH_BRUSH] = ENHMETA_STOCK_OBJECT | WHITE_BRUSH;
			break;
		case EMFH_FONT:
			em_so.ihObject = htofl(ENHMETA_STOCK_OBJECT|SYSTEM_FONT);
			lasthandle[EMFH_FONT] =
				ENHMETA_STOCK_OBJECT | SYSTEM_FONT;
			break;
		default:
			fprintf(stderr, "genemf: unknown handle type %d.\n",
					h->type);
			exit(1);
		}

# ifdef __EMF_DEBUG__
		fprintf(stderr, "SelectObject: %u\n", ftohl(em_so.ihObject));
# endif

		emh_write(&em_so, sizeof(EMRSELECTOBJECT), (size_t)1,
				EMH_RECORD);
		h->is_current = false;
	}

	em_so.emr.iType = htofl(EMR_DELETEOBJECT);
	em_so.emr.nSize = htofl(sizeof(EMRDELETEOBJECT));

	em_so.ihObject = htofl(h->handle);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "DeleteObject: %u\n", ftohl(em_so.ihObject));
# endif

	emh_write(&em_so, sizeof(EMRDELETEOBJECT), (size_t) 1, EMH_RECORD);
}


/* Clear current mark from objects of the specified type. */
static void
clear_current_handle(enum emfhandletype type)
{
	struct emfhandle *h;

	for (h = handles; h; h = h->next)
		if (h->is_current && h->type == type) {
			h->is_current = false;
			break;
		}
}


/* Select object by handle number. */
static void
select_object(unsigned handle)
{
	EMRSELECTOBJECT em_so;

	em_so.emr.iType = htofl(EMR_SELECTOBJECT);
	em_so.emr.nSize = htofl(sizeof(EMRSELECTOBJECT));
	em_so.ihObject = htofl(handle);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "SelectObject: %u\n", ftohl(em_so.ihObject));
# endif

	emh_write(&em_so, sizeof(EMRSELECTOBJECT), (size_t) 1, EMH_RECORD);
}


/* Add a handle structure to the newest position of handle list. */
static void
use_handle(struct emfhandle *h)
{

	if (h != latesthandle) {
		if (h->prev) {
			/* unlink from list */
			if (h->next)
				h->next->prev = h->prev;
			*h->prev = h->next;
		}
		if (handles == NULL) {
			handles = h;
			h->prev = &handles;
		} else
			h->prev = &latesthandle->next;
		*h->prev = h;
		h->next = NULL;
		latesthandle = h;
	}
}


/*
 * Allocate handle structure.  If the number of currently allocated handle
 * exceeds maximum limit, reuse the least recently used handle.
 */
static struct emfhandle *
get_handle(enum emfhandletype type)
{
	struct emfhandle *h;

	if (emh_nHandles >= maxHandles) {
		/* reuse handle (LRU) */
		for (h = handles; h; h = h->next) {
			if (!h->is_current) {
				/* use it */
				delete_handle(h);
				goto use;
			}
		}
		/*
		 * All handles are currently in use.
		 * Although exceeds maxHandles, create new one
		 */
		/* FALLTHROUGH */
	}

	/* allocate new handle */
	if ((h = malloc(sizeof(struct emfhandle))) == NULL) {
		perror("fig2dev: malloc");
		exit(1);
	}
	emh_nHandles++;
	h->handle = emh_nHandles;
	h->prev = NULL;
	h->next = NULL;
	h->is_current = false;
use:
	h->type = type;

	return h;
}


/* Given an index into either the standard color list or into the
 * user defined color list, return the hex RGB value of the color. */
static int
conv_color(int colorIndex)
{
	int   rgb;
	static const int rgbColors[NUM_STD_COLS] = {
		/* Black     Blue      Green     Cyan      Red       Magenta */
		0x000000, 0x0000FF, 0x00FF00, 0x00FFFF, 0xFF0000, 0xFF00FF,
		/* Yellow    White     Blue4     Blue3     Blue2     LtBlue */
		0xFFFF00, 0xFFFFFF, 0x000090, 0x0000B0, 0x0000D0, 0x87CEFF,
		/* Green4    Green3    Green2    Cyan4     Cyan3     Cyan2  */
		0x009000, 0x00B000, 0x00D000, 0x009090, 0x00B0B0, 0x00D0D0,
		/* Red4      Red3      Red2      Magenta4  Magenta3  Magenta2 */
		0x900000, 0xB00000, 0xD00000, 0x900090, 0xB000B0, 0xD000D0,
		/* Brown4    Brown3    Brown2    Pink4     Pink3     Pink2  */
		0x803000, 0xA04000, 0xC06000, 0xFF8080, 0xFFA0A0, 0xFFC0C0,
		/* Pink      Gold   */
		0xFFE0E0, 0xFFD700
	};

	if (colorIndex == DEFAULT)
		colorIndex = 0;

	if (colorIndex < NUM_STD_COLS)
		rgb = RGB(rgbColors[colorIndex] >> 16,
				(rgbColors[colorIndex] >> 8) & 0xff,
				rgbColors[colorIndex] & 0xff);
	else
		rgb = RGB(user_colors[colorIndex-NUM_STD_COLS].r,
				user_colors[colorIndex-NUM_STD_COLS].g,
				user_colors[colorIndex-NUM_STD_COLS].b);
	return rgb;
}/* end conv_color */


/* Converts fill pattern color.  The style contains the fill intensity.
 * 0 - 20 - 40 is equivalent to -1.0 - 0.0 - +1.0. */
static int
conv_fill_color(int style, int color)
{
	int   rgb, b, g, r;
	float f;

	rgb = conv_color(color);
	b = GetBValue(rgb);
	g = GetGValue(rgb);
	r = GetRValue(rgb);

	if (style <= 0) {				/* Black */

		b = 0;
		g = 0;
		r = 0;

	} else if (style < 20) {			/* Darkened */

		f = (float)style / 20.0;
		b = round(b * f);
		g = round(g * f);
		r = round(r * f);

	} else if (style == 20) {			/* Normal */

		/* : */

	} else if (style < 40) {			/* Lightened */

		f = (float)(style - 20) / 20.0;
		b = round(b + f*(255-b));
		g = round(g + f*(255-g));
		r = round(r + f*(255-r));

	} else {/* if (style >= 40) */		/* White */

		b = 0xFF;
		g = 0xFF;
		r = 0xFF;

	}/* end if (style >= 40) */

	return RGB(r, g, b);
}/* end conv_fill_color */


/* Converts Fig font index to index into font table in the pre-amble,
 * taking into account the flags. */
static int
conv_fontindex(int font, int flags)
{

	if (flags&4) {			/* PostScript fonts */
		if (font == (-1)) {
			font = 0;	/* Default PostScript is Roman */
		} else if ((font < 0) || (font >= NUM_PS_FONTS)) {
			fprintf(stderr, "Unsupported Fig PostScript "
					"font index %d.\n", font);
			font = 0;	/* Default font */
		}
	} else {			/* LaTeX fonts */
		if ((font < 0) || (font > NUM_LATEX_FONTS)) {
			fprintf(stderr, "Unsupported Fig LaTeX "
					"font index %d.\n", font);
			font = 0;	/* Default font */
		}
		else
			font = ltFonts[font]; /* Convert to PostScript fonts */
	}
	return font;
}/* end conv_fontindex */


/* Convert fig cap style to EMF line style. */
static int
conv_capstyle(int cap)
{

	switch (cap) {
	case 0:	/* Butt. */
		cap = PS_ENDCAP_FLAT;
		break;
	case 1:	/* Round. */
		cap = PS_ENDCAP_ROUND;
		break;
	case 2:	/* Projecting. */
		cap = PS_ENDCAP_SQUARE;
		break;
	default:
		fprintf(stderr, "genemf: unknown cap style %d.\n", cap);
		cap = PS_ENDCAP_FLAT;
		break;
	}
	return cap;
}/* end conv_capstyle */


/* Convert fig join style to EMF join style. */
static int
conv_joinstyle(int join)
{

	switch (join) {
	case 0:	/* Miter. */
		join = PS_JOIN_MITER;
		break;
	case 1:	/* Round. */
		join = PS_JOIN_ROUND;
		break;
	case 2:	/* Bevel. */
		join = PS_JOIN_BEVEL;
		break;
	default:
		fprintf(stderr, "genemf: unknown join style %d.\n", join);
		join = PS_JOIN_MITER;
		break;
	}
	return join;
}/* end conv_joinstyle */


/* Convert fig line style to EMF line style.  EMF knows 5 styles with
 * fortunately correspond to the first 5 fig line styles.  The triple
 * dotted fig line style is handled as a solid line here. */
static int
conv_linetype(int type)
{

	if (type < 0)
		type = -1;
	else if (type > 4)
		type %= 5;
	return type;
}/* end conv_linetype */


/* This procedure sets the pen line attributes and selects the pen into
 * the device context. */
static void
edgeattr(
	int vis,		/* visible */
	int type,		/* line type */
	int width,
	int color,
	int join,		/* join style */
	int cap)		/* cap style */
{
	unsigned handle;
	int rgb;
	EMREXTCREATEPEN em_pn;
	struct emfhandle *h = NULL;
	int style;
	unsigned styleEntry[8];	/* "- . . . " */
	int user_style = false;

	if (width == 0)
		vis = 0;

	/* if a stock object is available, use it */
	if (vis == 0) {
		handle = ENHMETA_STOCK_OBJECT | NULL_PEN;
		goto selectpen;
	}

	rgb = conv_color(color);
	style = PS_GEOMETRIC;

	switch (emflevel) {
	case EMF_LEVEL_WIN98:
		if (type >= 1 && type <= 4) {
			fprintf(stderr, "Warning: line style may be ignored\n");
		}
		break;

	case EMF_LEVEL_WIN95:
		if (type >= 1 && type <= 4) {
			if (width < 20) {
				fprintf(stderr, "Warning: this style of line is"
					       " converted to thin line\n");
				style = PS_COSMETIC;
			} else
				fprintf(stderr, "Warning: line style may be "
						"ignored\n");
		}
		break;

	default:
		break;
	}

	switch (emflevel) {
	case EMF_LEVEL_WINNT:
		if (type == 5) {
			style |= PS_USERSTYLE;
			user_style = true;
		} else
			style |= conv_linetype(type);
		break;

	default:
		if (type == 5) {
			fprintf(stderr, "Warning: dash-triple-dotted style is "
					"converted to solid style\n");
			type = 0;
		}
		style |= conv_linetype(type);
		break;
	}
	style |= conv_capstyle(cap) | conv_joinstyle(join);

	/* search for the same pen */
	for (h = latesthandle; h != (void *)&handles; h = (void *) h->prev) {
		if (h->type == EMFH_PEN && h->eh_un.p.pstyle == style &&
				h->eh_un.p.pwidth == width &&
				h->eh_un.p.prgb == rgb) {
			/* found */
			use_handle(h);
			handle = h->handle;
			goto selectpen;
		}
	}

	/* not found -- create new pen */
	h = get_handle(EMFH_PEN);
	handle = h->handle;
	use_handle(h);

	h->eh_un.p.pstyle = style;
	h->eh_un.p.pwidth = width;
	h->eh_un.p.prgb = rgb;

	memset(&em_pn, 0, sizeof(EMREXTCREATEPEN));
	em_pn.emr.iType = htofl(EMR_EXTCREATEPEN);
	em_pn.ihPen = htofl(handle);
	em_pn.offBmi  = htofl(sizeof(EMREXTCREATEPEN));
	em_pn.offBits = htofl(sizeof(EMREXTCREATEPEN));
	em_pn.elp.elpPenStyle = htofl(style);
	em_pn.elp.elpWidth = htofl(width);
	em_pn.elp.elpColor = htofl(rgb);

	if (user_style) {
		/* create dash-triple-dotted style */
		styleEntry[0] = htofl(width * 4);	/* dash */
		styleEntry[1] =				/* gap */
			styleEntry[2] =			/* dot */
			styleEntry[3] =			/* gap */
			styleEntry[4] =			/* dot */
			styleEntry[5] =			/* gap */
			styleEntry[6] =			/* dot */
			styleEntry[7] = htofl(width);	/* gap */
		em_pn.elp.elpNumEntries =
			htofl(sizeof styleEntry / sizeof styleEntry[0]);
		em_pn.emr.nSize =
			htofl(sizeof(EMREXTCREATEPEN) + sizeof styleEntry);
	} else {
		em_pn.emr.nSize = htofl(sizeof(EMREXTCREATEPEN));
	}

# ifdef __EMF_DEBUG__
	fprintf(stderr, "CreatePen (%d): style %d, width %d, rgb %x\n",
			handle, style, width, rgb);
# endif

	emh_write(&em_pn, sizeof(EMREXTCREATEPEN), (size_t) 1, EMH_RECORD);
	if (user_style)
		emh_write(&styleEntry, sizeof styleEntry, (size_t) 1, EMH_DATA);

selectpen:
	chkcache(handle, lasthandle[EMFH_PEN]);
	clear_current_handle(EMFH_PEN);
	if (h)
		h->is_current = true;
# ifdef __EMF_DEBUG__
	fprintf(stderr, "Pen:   ");
# endif
	select_object(handle);
}/* end edgeattr */


/* This procedure turns off the edge visibility. */
static void
edgevis(int onoff)
{
	edgeattr(onoff, 0, 0, 0, 0, 0);
}/* end edgevis */


static void
bkmode(int mode)
{
	static int oldbkmode = 0;
	EMRSETBKMODE em_bm;

	chkcache(mode, oldbkmode);
	memset(&em_bm, 0, sizeof(EMRSETBKMODE));
	em_bm.emr.iType = htofl(EMR_SETBKMODE);
	em_bm.emr.nSize = htofl(sizeof(EMRSETBKMODE));
	em_bm.iMode = htofl(mode);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "SetBKMode (mode): %d\n", mode);
# endif

	emh_write(&em_bm, sizeof(EMRSETBKMODE), (size_t) 1, EMH_RECORD);
}


static void
bkcolor(int rgb)
{
	static int oldbkcolor = UNDEFVALUE;
	EMRSETBKCOLOR em_bc;

	chkcache(rgb, oldbkcolor);
	memset(&em_bc, 0, sizeof(EMRSETBKCOLOR));
	em_bc.emr.iType = htofl(EMR_SETBKCOLOR);
	em_bc.emr.nSize = htofl(sizeof(EMRSETBKCOLOR));
	em_bc.crColor = htofl(rgb);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "SetBKColor (rgb): %x\n", rgb);
# endif

	emh_write(&em_bc, sizeof(EMRSETBKCOLOR), (size_t) 1, EMH_RECORD);
}


static void
create_brush_pattern(unsigned char *bits, int pattern)
{
	const struct emf_bmpat *pat = &emf_bm_pattern[pattern];
	unsigned char *b = pat->bits;
	int xbmhbytecnt;
	unsigned x;
	int y;
	unsigned byte, bit;

	xbmhbytecnt = (pat->width + 7) / 8;

	/* EMF bitmap is encoded from bottom to top */
	byte = 0;
	for (y = pat->height - 1; y >=0; y--) {
		bit = 7;
		for (x = 0; x < pat->width; x++) {
			if (b[xbmhbytecnt * y + x / 8] & (1 << x % 8))
				bits[byte] |= 1 << bit;
			if (bit-- == 0) {
				byte++;
				bit = 7;
			}
		}
		/* size of a line must be a multiple of 4 byte */
		if (bit != 7)
			byte++;
		byte = (byte + 3) & ~(unsigned)3;
	}
}


/* Computes and sets fill color for solid filled shapes (fill style 0 to 40). */
static void
fillstyle(int fill_color, int fill_style, int pen_color)
{
	unsigned handle;
	int pattern, hatch;
	int rgb, hatchbkrgb;
	int style;
	EMRCREATEBRUSHINDIRECT em_bi;
	EMRCREATEDIBPATTERNBRUSHPT em_pb;
	BITMAPINFO bmi;
	RGBQUAD hatchcolors[2];
	int width, height;
	size_t bsize;
	unsigned char *bits;
	struct emfhandle *h = NULL;

	if (fill_style < 0) {		/* Unfilled shape */
		style = BS_HOLLOW;
		rgb = 0;
		hatchbkrgb = 0;
		pattern = 0;
	} else if (fill_style <= 40) {	/* Solid filled shape */
		style = BS_SOLID;
		rgb = conv_fill_color(fill_style, fill_color);
		hatchbkrgb = 0;
		pattern = 0;
	} else /* if (fill_style > 40) */ {	/* Pattern filled shape */
		style = BS_HATCHED;
		rgb = conv_color(pen_color);
		hatchbkrgb = conv_color(fill_color);
		pattern = fill_style - 41;
	}

	if ((unsigned)pattern >
			sizeof emf_bm_pattern / sizeof emf_bm_pattern[0]) {
		fprintf(stderr, "fig2dev: emf: fill pattern %d is not "
				"supported\n", pattern);
		return;
	}

	if (style == BS_HATCHED && emf_map_pattern[pattern] != -1) {
		/*
		 * Use EMF hatch pattern.
		 * Setting of background color is not in brush.
		 */
		bkmode(OPAQUE);
		bkcolor(hatchbkrgb);

		/* The background color is now handled. */
		hatchbkrgb = 0;
	}

	/* if a stock object is available, use it */
	switch (style) {
	case BS_HOLLOW:
		handle = ENHMETA_STOCK_OBJECT | NULL_BRUSH;
		goto selectbrush;
	case BS_SOLID:
		switch (rgb) {
		case 0xffffff:
			handle = ENHMETA_STOCK_OBJECT | WHITE_BRUSH;
			goto selectbrush;
		case 0xc0c0c0:
			handle = ENHMETA_STOCK_OBJECT | LTGRAY_BRUSH;
			goto selectbrush;
		case 0x808080:
			handle = ENHMETA_STOCK_OBJECT | GRAY_BRUSH;
			goto selectbrush;
		case 0x404040:
			handle = ENHMETA_STOCK_OBJECT | DKGRAY_BRUSH;
			goto selectbrush;
		case 0x000000:
			handle = ENHMETA_STOCK_OBJECT | BLACK_BRUSH;
			goto selectbrush;
		default:
			break;
		}
		break;
	default:
		break;
	}

	/* search for the same brush */
	for (h = latesthandle; h != (void *)&handles; h = (void *) h->prev) {
		if (h->type == EMFH_BRUSH && h->eh_un.b.bstyle == style &&
				h->eh_un.b.brgb == rgb &&
				h->eh_un.b.bpattern == pattern &&
				h->eh_un.b.bhatchbkrgb == hatchbkrgb) {
			/* found */
			use_handle(h);
			handle = h->handle;
			goto selectbrush;
		}
	}

	/* not found -- create new brush */
	h = get_handle(EMFH_BRUSH);
	handle = h->handle;
	use_handle(h);

	h->eh_un.b.bstyle = style;
	h->eh_un.b.brgb = rgb;
	h->eh_un.b.bpattern = pattern;
	h->eh_un.b.bhatchbkrgb = hatchbkrgb;

	hatch = 0;
	if (style == BS_HATCHED && (hatch = emf_map_pattern[pattern]) == -1) {
		/*
		 * create a pattern brush
		 */
		memset(&em_pb, 0, sizeof(EMRCREATEDIBPATTERNBRUSHPT));
		memset(&bmi, 0, sizeof(BITMAPINFO));
		em_pb.emr.iType = htofl(EMR_CREATEDIBPATTERNBRUSHPT);
		em_pb.ihBrush = htofl(handle);
		/* em_pb.iUsage = htofl(DIB_RGB_COLORS); this is default */
		em_pb.offBmi = htofl(sizeof(EMRCREATEDIBPATTERNBRUSHPT));
		em_pb.cbBmi = htofl(sizeof(BITMAPINFO) + 2 * sizeof(RGBQUAD));
		em_pb.offBits = htofl(sizeof(EMRCREATEDIBPATTERNBRUSHPT) +
				sizeof(BITMAPINFO) + 2 * sizeof(RGBQUAD));

		width = emf_bm_pattern[pattern].width;
		height = emf_bm_pattern[pattern].height;

		if (emflevel != EMF_LEVEL_WINNT && (width > 8 || height > 8))
			fprintf(stderr, "Warning: fill pattern %d will not "
					"appear properly on Windows 95/98/Me\n",
					pattern);

		/* Windows 95/98/Me doesn't show 8x4 pat properly.
		   Convert to 8x8. */
		if (height == 4)
			height = 8;

		bsize = (width + 31) / 32 * height * 4;

		bmi.bmiHeader.biSizeImage = em_pb.cbBits =
			htofl((EMFulong) bsize);
		em_pb.emr.nSize = htofl(sizeof(EMRCREATEDIBPATTERNBRUSHPT) +
				sizeof(BITMAPINFO) + 2 * sizeof(RGBQUAD) +
				bsize);

		bmi.bmiHeader.biSize = htofl(sizeof(BITMAPINFOHEADER));
		bmi.bmiHeader.biWidth  = htofl(width);
		bmi.bmiHeader.biHeight = htofl(height);
		bmi.bmiHeader.biPlanes = bmi.bmiHeader.biBitCount = htofs(1);
		bmi.bmiHeader.biCompression = htofl(BI_RGB);  /* uncompressed */
		bmi.bmiHeader.biXPelsPerMeter =
			bmi.bmiHeader.biYPelsPerMeter = htofl(2953);/* 75dpi? */
		bmi.bmiHeader.biClrUsed = htofl(2);
		/* bmi.bmiHeader.biClrImportant = htofl(0); */

		/* background and foreground colors */
		hatchcolors[0].rgbBlue  = GetBValue(hatchbkrgb);
		hatchcolors[0].rgbGreen = GetGValue(hatchbkrgb);
		hatchcolors[0].rgbRed   = GetRValue(hatchbkrgb);
		hatchcolors[0].rgbReserved = 0;
		hatchcolors[1].rgbBlue  = GetBValue(rgb);
		hatchcolors[1].rgbGreen = GetGValue(rgb);
		hatchcolors[1].rgbRed   = GetRValue(rgb);
		hatchcolors[1].rgbReserved = 0;

		if ((bits = malloc(bsize)) == NULL) {
			perror("fig2dev: malloc");
			exit(1);
		}
		memset(bits, 0, bsize);

		create_brush_pattern(bits, pattern);

		/* Windows 95/98/Me doesn't show 8x4 pat properly.
		   Convert to 8x8. */
		if (emf_bm_pattern[pattern].height == 4)
			/* latter half */
			create_brush_pattern(bits + bsize / 2, pattern);

# ifdef __EMF_DEBUG__
		fprintf(stderr, "CreatePatternBrush (%d): "
				"pattern %d, rgb %x, bkrgb %x\n",
				handle, pattern, rgb, hatchbkrgb);
# endif

		emh_write(&em_pb, sizeof(EMRCREATEDIBPATTERNBRUSHPT), (size_t)1,
				EMH_RECORD);
		emh_write(&bmi, sizeof(BITMAPINFO), (size_t) 1, EMH_DATA);
		emh_write(hatchcolors, sizeof(RGBQUAD), (size_t) 2, EMH_DATA);
		emh_write(bits, bsize, (size_t) 1, EMH_DATA);
		free(bits);
	} else {
		memset(&em_bi, 0, sizeof(EMRCREATEBRUSHINDIRECT));
		em_bi.emr.iType = htofl(EMR_CREATEBRUSHINDIRECT);
		em_bi.emr.nSize = htofl(sizeof(EMRCREATEBRUSHINDIRECT));
		em_bi.ihBrush = htofl(handle);
		em_bi.lb.lbStyle = htofl(style);
		em_bi.lb.lbColor = htofl(rgb);
		em_bi.lb.lbHatch = htofl(hatch);

# ifdef __EMF_DEBUG__
		fprintf(stderr,"CreateBrush (%d): style %d, rgb %x, hatch %d\n",
				handle, style, rgb, hatch);
# endif

		emh_write(&em_bi, sizeof(EMRCREATEBRUSHINDIRECT), (size_t)1,
				EMH_RECORD);
	}

selectbrush:
	chkcache(handle, lasthandle[EMFH_BRUSH]);
	clear_current_handle(EMFH_BRUSH);
	if (h)
		h->is_current = true;
# ifdef __EMF_DEBUG__
	fprintf(stderr, "Brush: ");
# endif
	select_object(handle);
}/* end fillstyle */


static void
textfont(
	int font,
	double size,
	int angle)	/* tenths degree */
{
	EMREXTCREATEFONTINDIRECTW em_fn;
	short *utext;
	int    n_chars, n_unicode;
	struct emfhandle *h = NULL;
	unsigned handle;
	int fontsz;

	fontsz = -(int)(size * (1200/72.27 * 0.88/*?? font is a bit large*/) +
			0.5);

	/* search for the same font */
	for (h = latesthandle; h != (void *)&handles; h = (void *) h->prev) {
		if (h->type == EMFH_FONT && h->eh_un.f.ffont == font &&
				h->eh_un.f.fsize == fontsz &&
				h->eh_un.f.fangle == angle) {
			/* found */
			use_handle(h);
			handle = h->handle;
			goto selectfont;
		}
	}

	/* not found -- create new font */
	h = get_handle(EMFH_FONT);
	handle = h->handle;
	use_handle(h);

	h->eh_un.f.ffont = font;
	h->eh_un.f.fsize = fontsz;
	h->eh_un.f.fangle = angle;

	memset(&em_fn, 0, sizeof(EMREXTCREATEFONTINDIRECTW));
	em_fn.emr.iType = htofl(EMR_EXTCREATEFONTINDIRECTW);
	em_fn.emr.nSize = htofl(sizeof(EMREXTCREATEFONTINDIRECTW));

	em_fn.ihFont = htofl(handle);

	utext = (short *) em_fn.elfw.elfLogFont.lfFaceName;
	n_unicode = sizeof (em_fn.elfw.elfLogFont.lfFaceName);
	textunicode(
#ifdef HAVE_ICONV
			(font < 0 /* locale font */)?
				localeFonts[-1-font][figLanguage].FaceName :
#endif
				lfFaceName[font],
			&n_chars, &utext, &n_unicode);

	em_fn.elfw.elfLogFont.lfHeight = htofl(fontsz);
	em_fn.elfw.elfLogFont.lfEscapement =
		em_fn.elfw.elfLogFont.lfOrientation = htofl(angle);
#ifdef HAVE_ICONV
	if (font < 0 /* locale font */) {
		const struct localefnt *lf = &localeFonts[-1-font][figLanguage];

		em_fn.elfw.elfLogFont.lfWeight = htofl(lf->Weight);
		em_fn.elfw.elfLogFont.lfItalic = /*uchar*/lf->Italic;
		em_fn.elfw.elfLogFont.lfCharSet = /*uchar*/lf->Charset;
		em_fn.elfw.elfLogFont.lfPitchAndFamily =
			/*uchar*/lf->PitchAndFamily;
		HTOFL(em_fn.elfw.elfLogFont.lfWidth,
				(int)(size * (1200/72.27 * 0.40/*?? font is a bit wide*/) + 0.5));
	} else
#endif
	{
		em_fn.elfw.elfLogFont.lfWeight = htofl(lfWeight[font]);
		em_fn.elfw.elfLogFont.lfItalic = /*uchar*/lfItalic[font];
		em_fn.elfw.elfLogFont.lfCharSet = /*uchar*/lfCharSet[font];
		em_fn.elfw.elfLogFont.lfPitchAndFamily =
			/*uchar*/lfPitchAndFamily[font];
	}

	emh_write(&em_fn, sizeof(EMREXTCREATEFONTINDIRECTW), (size_t)1,
			EMH_RECORD);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Textfont (%d): %s  Size: %d  Weight: %d  Italic: %d  "
			"Angle: %d\n", handle,
#  ifdef HAVE_ICONV
			(font < 0 /* locale font */) ?
				localeFonts[-1-font][figLanguage].FaceName :
#  endif
				lfFaceName[font],
			-ftohl(em_fn.elfw.elfLogFont.lfHeight),
			lfWeight[font], lfItalic[font], angle);
# endif

selectfont:
	chkcache(handle, lasthandle[EMFH_FONT]);
	clear_current_handle(EMFH_FONT);
	if (h)
		h->is_current = true;
# ifdef __EMF_DEBUG__
	fprintf(stderr, "Font:  ");
# endif
	select_object(handle);
}/* end textfont */


static void
warn_32bit_pos(void)
{
	static int wgiv;

	if (emflevel != EMF_LEVEL_WINNT && !wgiv) {
		fprintf(stderr, "Warning: Coordinates exceed 16bit value.\n"
				"Some figures will be invisible on "
				"Windows 95/98/Me.\n");
		wgiv = 1;
	}
}


/* Copies coordinates of pos p to point P. */
static void pos2point(F_point *P, F_pos *p)
{

	P->x = p->x; P->y = p->y;
}/* end pos2point */


/* Returns length of the arrow. used to shorten lines/arcs at
 * an end where an arrow needs to be drawn. */
static double
arrow_length(F_arrow *a)
{
	double len;

	switch (a->type) {
	case 0:				/* stick type */
		len = ARROW_EXTRA_LEN(a);
		break;
	case 1:				/* closed triangle */
		len =  a->ht;
		break;
	case 2:				/* indented hat */
		len =  ARROW_INDENT_DIST * a->ht;
		break;
	case 3:				/* pointed hat */
		len =  ARROW_POINT_DIST * a->ht;
		break;
	default:
		len =  0.;
	}
	if (rounded_arrows)
		len -= ARROW_EXTRA_LEN(a);

	return len;
}


/* Draws an arrow ending at point p pointing into direction dir.
 * type and attributes as required by a and l.  This routine works
 * for both lines and arc (in that case l should be a F_arc *). */
static void arrow(F_point *P, F_arrow *a, F_line *l, Dir *dir)
{
	F_point s1, s2, t, p;
	EMRPOLYLINE	em_pl;		/* Little endian format */
	POINTL      aptl[4];	/* Maximum of four points required for arrows */
	POINTS      apts[4];
	unsigned    cpt;		/* Number of points in the array */
	unsigned itype, itype16;
	int bbx_top, bbx_bottom, bbx_left, bbx_right;	/* Bounding box */

	t.x = 0;
	t.y = 0;
	p = *P;
	if (!rounded_arrows) {
		/* Move the arrow backwards in order to let it end
		 * at the correct spot */
		double f = ARROW_EXTRA_LEN(a);
		p.x = round(p.x - f * dir->x);
		p.y = round(p.y - f * dir->y);
	}

	if (a->type == 0) {		/* Old style stick arrow */
		edgeattr(1, 0, (int)a->thickness, l->pen_color, 0, 0);
	} else {			/* Polygonal arrows */
		/* miter join style, butt line cap */
		edgeattr(1, 0, (int)a->thickness, l->pen_color, 0, 0);
		switch (a->style) {
		case 0:
			fillstyle(7, 20, 0);		/* Fill with white (7)*/
			break;
		case 1:
			fillstyle(l->pen_color, 20, 0); /* Use pen color to fill */
			break;
		default:
			fprintf(stderr, "Unsupported fig arrow style %d !!\n",
					a->style);
		}
	}

	/* Start the bounding box */

	bbx_left = bbx_right  = P->x;	/* Use original point for bounding */
	bbx_top  = bbx_bottom = P->y;

	itype = EMR_POLYGON;
	itype16 = EMR_POLYGON16;

	switch (a->type) {

	case 0:				/* Stick type */
		itype = EMR_POLYLINE;
		itype16 = EMR_POLYLINE16;
		cpt = 3;

		s1.x = round(p.x - a->ht * dir->x + a->wid*0.5 * dir->y);
		s1.y = round(p.y - a->ht * dir->y - a->wid*0.5 * dir->x);
		s2.x = round(p.x - a->ht * dir->x - a->wid*0.5 * dir->y);
		s2.y = round(p.y - a->ht * dir->y + a->wid*0.5 * dir->x);
		break;

	case 1:				/* Closed triangle */
		cpt = 3;

		s1.x = round(p.x - a->ht * dir->x + a->wid*0.5 * dir->y);
		s1.y = round(p.y - a->ht * dir->y - a->wid*0.5 * dir->x);
		s2.x = round(p.x - a->ht * dir->x - a->wid*0.5 * dir->y);
		s2.y = round(p.y - a->ht * dir->y + a->wid*0.5 * dir->x);
		break;

	case 2:				/* Indented hat */
		cpt = 4;

		s1.x = round(p.x - a->ht*ARROW_POINT_DIST * dir->x
				+ a->wid*0.5 * dir->y);
		s1.y = round(p.y - a->ht*ARROW_POINT_DIST * dir->y
				- a->wid*0.5 * dir->x);
		s2.x = round(p.x - a->ht*ARROW_POINT_DIST * dir->x
				- a->wid*0.5 * dir->y);
		s2.y = round(p.y - a->ht*ARROW_POINT_DIST * dir->y
				+ a->wid*0.5 * dir->x);

		t.x = round(p.x - a->ht * dir->x);
		t.y = round(p.y - a->ht * dir->y);

		UPDATE_BBX_X(t.x);
		UPDATE_BBX_Y(t.y);
		break;

	case 3:				/* Pointed hat */
		cpt = 4;

		s1.x = round(p.x - a->ht*ARROW_INDENT_DIST * dir->x
				+ a->wid*0.5 * dir->y);
		s1.y = round(p.y - a->ht*ARROW_INDENT_DIST * dir->y
				- a->wid*0.5 * dir->x);
		s2.x = round(p.x - a->ht*ARROW_INDENT_DIST * dir->x
				- a->wid*0.5 * dir->y);
		s2.y = round(p.y - a->ht*ARROW_INDENT_DIST * dir->y
				+ a->wid*0.5 * dir->x);

		t.x = round(p.x - a->ht * dir->x);
		t.y = round(p.y - a->ht * dir->y);

		UPDATE_BBX_X(t.x);
		UPDATE_BBX_Y(t.y);
		break;

	default:
		fprintf(stderr, "Unsupported fig arrow type %d.\n", a->type);
		return;

	}/* end case */

	UPDATE_BBX_X(s1.x);
	UPDATE_BBX_Y(s1.y);

	UPDATE_BBX_X(s2.x);
	UPDATE_BBX_Y(s2.y);

	em_pl.rclBounds.left   = htofl(bbx_left);	/* Store bounding box */
	em_pl.rclBounds.right  = htofl(bbx_right);
	em_pl.rclBounds.top    = htofl(bbx_top);
	em_pl.rclBounds.bottom = htofl(bbx_bottom);
	em_pl.cptl = htofl(cpt);			/* Number of points */

	if (bbx_left >= -32768 && bbx_right <= 32767
			&& bbx_top >= -32768 && bbx_bottom <= 32767) {

# ifdef __EMF_DEBUG__
		fprintf(stderr,"Arrowhead16 %d rclBounds (ltrb): %d %d %d %d\n",
				cpt, bbx_left,  bbx_top, bbx_right, bbx_bottom);
# endif

		/* use 16bit point */
		em_pl.emr.iType = htofl(itype16);

		apts[0].x = htofs(s1.x);
		apts[0].y = htofs(s1.y);
		apts[1].x = htofs(p.x);
		apts[1].y = htofs(p.y);
		apts[2].x = htofs(s2.x);
		apts[2].y = htofs(s2.y);
		apts[3].x = htofs(t.x);
		apts[3].y = htofs(t.y);

		HTOFL(em_pl.emr.nSize,
				sizeof(EMRPOLYLINE16) + cpt*sizeof(POINTS));
		emh_write(&em_pl, sizeof(EMRPOLYLINE16), (size_t)1, EMH_RECORD);
		emh_write(apts, sizeof(POINTS), (size_t)cpt, EMH_DATA);
	} else {

# ifdef __EMF_DEBUG__
		fprintf(stderr, "Arrowhead %d rclBounds (ltrb): %d %d %d %d\n",
				cpt, bbx_left,  bbx_top, bbx_right, bbx_bottom);
# endif

		/* use 32bit point */
		warn_32bit_pos();

		em_pl.emr.iType = htofl(itype);

		aptl[0].x = htofl(s1.x);
		aptl[0].y = htofl(s1.y);
		aptl[1].x = htofl(p.x);
		aptl[1].y = htofl(p.y);
		aptl[2].x = htofl(s2.x);
		aptl[2].y = htofl(s2.y);
		aptl[3].x = htofl(t.x);
		aptl[3].y = htofl(t.y);

		HTOFL(em_pl.emr.nSize,
				sizeof(EMRPOLYLINE) + cpt*sizeof(POINTL));
		emh_write(&em_pl, sizeof(EMRPOLYLINE), (size_t)1, EMH_RECORD);
		emh_write(aptl, sizeof(POINTL), (size_t)cpt, EMH_DATA);
	}
}


/* Draws arc arrow ending at p and starting at q. */
static void
arc_arrow(F_pos *p, F_point *q, F_arrow *arw, F_arc *arc)
{
	F_point P;
	Dir dir; double d;

	if (!arw) return;

	pos2point(&P, p);
	direction(&P, q, &dir, &d);
	arrow(&P, arw, (F_line *)arc, &dir);
}

/* Replaces p by the starting point of the arc arrow ending at p. */
static void
arc_arrow_adjust(F_point *p,	/* in: end of the arc, out: fixed end of arc */
		F_point *arwpos,/* out: origin of arrow */
		F_arc *arc, double r, F_arrow *arw, double adir)
{
	double l;
	double alen, th;
	Dir dir; double dirlen;
	double d2, d3;
	F_point origpos, p1, p2, p3;
	struct dpoint {
		double x, y;
	} p4;
	double X, Y, d;
	double cx, cy;

	if (!arw) return;

	cx = arc->center.x; cy = arc->center.y;	/* arc center */
	origpos = *p;	/* save original arc end (to be used later) */

	l = arw->type != 0 ? arrow_length(arw) : arw->ht;
	arc_rotate(p, cx, cy, adir * l / (1.8 * r + l));

	/* length of arrow head */
	alen = arw->ht;
	if (!rounded_arrows)
		alen += ARROW_EXTRA_LEN(arw);

	/*
	 * fix arrow root pos
	 */

	/* if the arrow is longer than the diameter,
	   no way to fix arrow root pos */
	if (alen >= 2 * r) {
		*arwpos = *p;	/* use the fixed arc end */
		goto fix_cap;
	}

	/* make a chord with the arc and the center line of the arrow head */
	th = 2 * atan(sqrt(1 / (4 * r * r / (alen * alen) - 1)));
	*arwpos = origpos;
	arc_rotate(arwpos, cx, cy, adir * th);

	/*
	 * fix arc end pos
	 */

	/* direction from arow root to arrow top */
	if (direction(&origpos, arwpos, &dir, &dirlen) == false)
		goto fix_cap;	/* no way to fix */

	/* Calculate the center of the line of arrow top. */
	p1 = origpos;
	if (!rounded_arrows) {
		d = ARROW_EXTRA_LEN(arw);
		p1.x = round(p1.x - d * dir.x);
		p1.y = round(p1.y - d * dir.y);
	}

	/* Calculate other points of the arrow head. */
	d = p1.x - arw->ht * dir.x + arw->wid*0.5 * dir.y; p2.x = round(d);
	d = p1.y - arw->ht * dir.y - arw->wid*0.5 * dir.x; p2.y = round(d);
	d = p1.x - arw->ht * dir.x - arw->wid*0.5 * dir.y; p3.x = round(d);
	d = p1.y - arw->ht * dir.y + arw->wid*0.5 * dir.x; p3.y = round(d);

	/* Calculate distance from the center of the arc to the points. */
	d2 = distance(cx, cy, (double) p2.x, (double) p2.y);
	d3 = distance(cx, cy, (double) p3.x, (double) p3.y);

	/* Use the farther point. */
	if (d3 > d2) {
		p2 = p3;
		d2 = d3;
	}

	/* The farther point shall be out of the arc. */
	if (d2 < r)
		goto fix_cap;

	/* direction from p1 to p2 */
	if (direction(&p2, &p1, &dir, &dirlen) == false)
		goto fix_cap;

	/*
	 * Drop a perpendicular line from the arc center
	 * to the line on which p1 and p2 are.
	 */
	/*
	 * The line of the arrow head is
	 *	x = p2.x + t * dir.x,
	 *	y = p2.y + t * dir.y,  where t is an intermediate parameter.
	 *
	 * The distance^2 from the center of the arc to a point on the line is
	 * (x - cx)^2 + (y - cy)^2 = (dir.x^2 + dir.y^2) * t^2
	 *		+ 2*((p2.x - cx) * dir.x + (p2.y - cy) * dir.y) * t
	 *		+ (p2.x - cx)^2 + (p2.y - cy)^2
	 *	= t^2 + 2*(X * dir.x + Y * dir.y) * t + X^2 + Y^2
	 *			where	dir.x^2 + dir.y^2 = 1 (dir is a unit vector),
	 *				X = p2.x - cx, Y = p2.y - cy
	 *	= t^2 + 2*(X * dir.x + Y * dir.y) * t + (X * dir.x + Y * dir.y)^2
	 *		- (X * dir.x + Y * dir.y)^2
	 *		+ X^2 + Y^2
	 *	= (t + (X * dir.x + Y * dir.y))^2
	 *		- X^2*dir.x^2 - 2*X*Y*dir.x*dir.y - Y^2*dir.y^2
	 *		+ X^2*(dir.x^2 + dir.y^2) + Y^2*(dir.x^2 + dir.y^2)
	 *			where	dir.x^2 + dir.y^2 = 1
	 *	= (t + (X * dir.x + Y * dir.y))^2
	 *		+ X^2*dir.y^2 - 2*X*Y*dir.x*dir.y + Y^2*dir.x^2
	 *	= (t + (X * dir.x + Y * dir.y))^2 + (X * dir.y - Y * dir.x)^2
	 */
	X = p2.x - cx;
	Y = p2.y - cy;

	/* Calculate the pedal point of the perpendicular line. */
	d = X * dir.x + Y * dir.y;
	p4.x = p2.x - d * dir.x;
	p4.y = p2.y - d * dir.y;

	/* and the length^2 of the perpendicular line segment is */
	d = X * dir.y - Y * dir.x;
	d = d * d;

	/* length from p4 to the target point */
	d = sqrt(r * r - d);

	/* Calculate target point. */
	p4.x = p4.x + d * dir.x;
	p4.y = p4.y + d * dir.y;

	p->x = round(p4.x);
	p->y = round(p4.y);

fix_cap:
	if (arc->cap_style == 2) {
		/* Projecting cap style is a little longer.  Shorten the arc. */
		arc_rotate(p, cx, cy, adir * (double)arc->thickness / (2.0*r));
	}
}


static double
arc_radius(F_arc *a)
{

	return (distance((double)a->point[0].x, (double)a->point[0].y,
				a->center.x, a->center.y) +
			distance((double)a->point[1].x, (double)a->point[1].y,
				a->center.x, a->center.y) +
			distance((double)a->point[2].x, (double)a->point[2].y,
				a->center.x, a->center.y)) / 3.;
}


static void
translate(double *x, double *y, double tx, double ty)
{
	*x += tx; *y += ty;
}/* end translate */


/* Rotates counter clockwise around origin */
static void
rotate(double *x, double *y, double angle)
{
	double s = sin(angle), c = cos(angle), xt = *x, yt = *y;

	*x =  c * xt + s * yt;
	*y = -s * xt + c * yt;
}/* end rotate */


/* Rotates the point p counter clockwise along the arc with center c. */
static void arc_rotate(F_point *p, double cx, double cy, double angle)
{
	double x = p->x, y = p->y;

	translate(&x, &y, -cx, -cy);
	rotate(&x, &y, angle);
	translate(&x, &y, +cx, +cy);
	p->x = round(x); p->y = round(y);
}


static void
arcoutline(F_arc *a)
{
	EMRARC em_ar;
	double r = arc_radius(a);
	F_point p0, p2;
	F_point arwp0, arwp2;
	int i;

	pos2point(&p0, &a->point[0]);
	pos2point(&p2, &a->point[2]);
	if (a->type == T_OPEN_ARC && a->thickness != 0) {
		/* adjust for arrow heads */
		arc_arrow_adjust(&p0, &arwp0, a, r, a->back_arrow, +1.);
		arc_arrow_adjust(&p2, &arwp2, a, r, a->for_arrow, -1.);

		/* calculate inner product */
		if ((a->point[0].x - a->point[2].x) *
				(p0.x - p2.x) + (a->point[0].y - a->point[2].y)*
				(p0.y - p2.y) < 0) {
			/*
			 * This is probably because the arc is very short and hidden
			 * in the arrow heads.  Skip drawing the arc in this case.
			 */
			goto draw_arrows;
		}
	}

	i = (a->type == T_OPEN_ARC) ? EMR_ARC : EMR_PIE;
	em_ar.emr.iType = htofl(i);
	em_ar.emr.nSize = htofl(sizeof(EMRARC));
	HTOFL(em_ar.rclBox.left,   round(a->center.x - r));
	HTOFL(em_ar.rclBox.top,    round(a->center.y - r));
	HTOFL(em_ar.rclBox.right,  round(a->center.x + r));
	HTOFL(em_ar.rclBox.bottom, round(a->center.y + r));
	em_ar.ptlStart.x = htofl(p0.x);
	em_ar.ptlStart.y = htofl(p0.y);
	em_ar.ptlEnd.x = htofl(p2.x);
	em_ar.ptlEnd.y = htofl(p2.y);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Arc outline rclBox (ltrb): %d %d %d %d\n",
			ftohl(em_ar.rclBox.left),  ftohl(em_ar.rclBox.top),
			ftohl(em_ar.rclBox.right), ftohl(em_ar.rclBox.bottom));
# endif

	emh_write(&em_ar, sizeof(EMRARC), (size_t) 1, EMH_RECORD);

draw_arrows:
	if (a->type == T_OPEN_ARC && a->thickness != 0) {
		/* draw arrow heads */
		arc_arrow(&a->point[0], &arwp0, a->back_arrow, a);
		arc_arrow(&a->point[2], &arwp2, a->for_arrow, a);
	}
}


static void
arcinterior(F_arc *a)
{
	EMRCHORD em_ch;
	double r = arc_radius(a);

	em_ch.emr.iType = htofl(EMR_CHORD);
	em_ch.emr.nSize = htofl(sizeof(EMRCHORD));
	HTOFL(em_ch.rclBox.left,   round(a->center.x - r));
	HTOFL(em_ch.rclBox.top,    round(a->center.y - r));
	HTOFL(em_ch.rclBox.right,  round(a->center.x + r));
	HTOFL(em_ch.rclBox.bottom, round(a->center.y + r));
	em_ch.ptlStart.x = htofl(a->point[0].x);
	em_ch.ptlStart.y = htofl(a->point[0].y);
	em_ch.ptlEnd.x = htofl(a->point[2].x);
	em_ch.ptlEnd.y = htofl(a->point[2].y);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Arc interior rclBox (ltrb): %d %d %d %d\n",
			ftohl(em_ch.rclBox.left),  ftohl(em_ch.rclBox.top),
			ftohl(em_ch.rclBox.right), ftohl(em_ch.rclBox.bottom));
# endif

	emh_write(&em_ch, sizeof(EMRCHORD), (size_t) 1, EMH_RECORD);
}/* end arcinterior */


/* Reverses arc direction by swapping endpoints and arrows */
static void
arc_reverse(F_arc *arc)
{
	F_arrow *arw;
	F_pos pp;

	pp = arc->point[0]; arc->point[0] = arc->point[2]; arc->point[2] = pp;
	arw = arc->for_arrow;
	arc->for_arrow = arc->back_arrow;
	arc->back_arrow = arw;
}


static void
circle(F_ellipse *e)
{
	EMRELLIPSE em_el;

	em_el.emr.iType = htofl(EMR_ELLIPSE);
	em_el.emr.nSize = htofl(sizeof(EMRELLIPSE));
	em_el.rclBox.left   = htofl(e->center.x - e->radiuses.x);
	em_el.rclBox.top    = htofl(e->center.y - e->radiuses.x);
	em_el.rclBox.right  = htofl(e->center.x + e->radiuses.x);
	em_el.rclBox.bottom = htofl(e->center.y + e->radiuses.x);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Circle rclBox (ltrb): %d %d %d %d\n",
			ftohl(em_el.rclBox.left),  ftohl(em_el.rclBox.top),
			ftohl(em_el.rclBox.right), ftohl(em_el.rclBox.bottom));
# endif

	emh_write(&em_el, sizeof(EMRELLIPSE), (size_t) 1, EMH_RECORD);
}/* end circle */


/* non-rotated ellipses */
static void
ellipse(F_ellipse *e)
{
	EMRELLIPSE em_el;

	em_el.emr.iType = htofl(EMR_ELLIPSE);
	em_el.emr.nSize = htofl(sizeof(EMRELLIPSE));
	em_el.rclBox.left   = htofl(e->center.x - e->radiuses.x);
	em_el.rclBox.top    = htofl(e->center.y - e->radiuses.y);
	em_el.rclBox.right  = htofl(e->center.x + e->radiuses.x);
	em_el.rclBox.bottom = htofl(e->center.y + e->radiuses.y);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Ellipse rclBox (ltrb): %d %d %d %d\n",
			ftohl(em_el.rclBox.left),  ftohl(em_el.rclBox.top),
			ftohl(em_el.rclBox.right), ftohl(em_el.rclBox.bottom));
# endif

	emh_write(&em_el, sizeof(EMRELLIPSE), (size_t) 1, EMH_RECORD);
}/* end ellipse */


/* draw rotated ellipse as a polygon */
#define ELLIPSE_NPOINT	72
static void
rotated_ellipse(F_ellipse *e)
{
	F_line l;
	F_point pnt[ELLIPSE_NPOINT];
	int i;
	const double delta = 2 * M_PI / (double)ELLIPSE_NPOINT;
	double th;
	double sina = sin(e->angle), cosa = cos(e->angle);
	double ex, ey;

	for (i = 0; i < ELLIPSE_NPOINT; i++) {
		th = delta * i;
		ex = (double) e->radiuses.x * cos(th);
		ey = (double) e->radiuses.y * sin(th);
		pnt[i].x = e->center.x + (int) (cosa * ex + sina * ey);
		pnt[i].y = e->center.y + (int) (-sina * ex + cosa * ey);
		pnt[i].next = &pnt[i + 1];
	}
	pnt[ELLIPSE_NPOINT - 1].next = NULL;

	/*
	 * fabricate polygon structure
	 */

	/* copy common fields */

	l.style = e->style;
	l.thickness = e->thickness;
	l.pen_color = e->pen_color;
	l.fill_color = e->fill_color;
	l.depth = e->depth;
	l.pen = e->pen;
	l.fill_style = e->fill_style;
	l.style_val = e->style_val;

	/* setup other fields */
	l.points = pnt;
	l.type = T_POLYGON;

	/* just in case... */
	l.for_arrow = l.back_arrow = NULL;
	l.cap_style = 0;
	l.join_style = 0;
	l.radius = 0;
	l.pic = NULL;
	l.comments = NULL;
	l.next = NULL;

	shape(&l, 0, 0, polygon);
}/* end rotated_ellipse */


/* Integer cross product */
static int
icprod(int x1, int y1, int x2, int y2)
{

	return x1 * y2 - y1 * x2;
}/* end icprod */


/* Returns true if the arc is a clockwise arc. */
static int
cwarc(F_arc *a)
{
	int x1 = a->point[1].x - a->point[0].x,
	    y1 = a->point[1].y - a->point[0].y,
	    x2 = a->point[2].x - a->point[1].x,
	    y2 = a->point[2].y - a->point[1].y;

	return (icprod(x1,y1,x2,y2) > 0);
}/* end cwarc */


/* Computes distance and normalized direction vector from q to p.
 * returns true if the points do not coincide and false if they do. */
static int
direction(F_point *p, F_point *q, Dir *dir, double *dist)
{

	dir->x = p->x - q->x;
	dir->y = p->y - q->y;
	*dist = sqrt((dir->x) * (dir->x) + (dir->y) * (dir->y));
	if (*dist < EPSILON)
		return false;
	dir->x /= *dist;
	dir->y /= *dist;
	return true;
}/* end direction */


static double
distance(double x1, double y1, double x2, double y2)
{
	double dx = x2-x1, dy=y2-y1;

	return sqrt(dx*dx + dy*dy);
}/* end distance */


/* Write an enhanced metarecord and keep track of number of bytes written
 * and number of records written in the global header record "emh". */
static size_t
emh_write(const void *ptr, size_t size, size_t nmemb, emh_flag flag)
{
	if (flag == EMH_RECORD) emh_nRecords++;
	emh_nBytes += size * nmemb;
	return fwrite(ptr, size, nmemb, tfp);
}/* end emh_write */


static int
is_flip(int rot, int flip)
{
	return (rot == 90 || rot == 270) ? !flip : flip;
}


static void
encode_bitmap(F_pic *pic, int bpp, int rot)
{
	int img_w = pic->bit_size.x;
	int img_h = pic->bit_size.y;
	int flip;
	int i, initi, endi, diri;
	int j, initj, endj, dirj;
	int pos;
	int freebits = 32;
	unsigned bits = 0;
	unsigned char cbits[4];
	unsigned u;

	/*
	 * Note: on EMF, you must output the image
	 * from left to right and FROM BOTTOM TO TOP.
	 */
	switch (rot) {
	case 0:		diri =  1; dirj = -1; break;
	case 90:	diri =  1; dirj =  1; break;
	case 180:	diri = -1; dirj =  1; break;
	case 270:	diri = -1; dirj = -1; break;
	default:
		fprintf(stderr, "fig2dev: emf: pic rotation %d is "
				"not supported\n", rot);
		return;
		/*NOTREACHED*/
		break;
	}

	if (pic->flipped && (rot == 0 || rot == 180)) {
		diri = -diri;
		dirj = -dirj;
	}

	flip = is_flip(rot, pic->flipped);

	if (diri < 0) {
		initi = img_w - 1; endi = 0;
	} else {
		initi = 0; endi = img_w - 1;
	}
	if (dirj < 0) {
		initj = img_h - 1; endj = 0;
	} else {
		initj = 0; endj = img_h - 1;
	}

#   define SWAP(v1, v2) do { int tmp = v1; v1 = v2; v2 = tmp; } while (0)
	if (flip) {
		SWAP(initi, initj);
		SWAP(endi, endj);
		SWAP(diri, dirj);
	}
#   undef SWAP

#   define WRITEBITS	\
	do {/* MSB first */						\
		cbits[0] = bits >> 24; cbits[1] = bits >> 16;		\
		cbits[2] = bits >> 8; cbits[3] = bits;			\
		emh_write(cbits, (size_t) 4, (size_t) 1, EMH_DATA);	\
		freebits = 32;						\
		bits = 0;						\
	} while (0)

	endi += diri;
	endj += dirj;
	for (j = initj; j != endj; j += dirj) {
		for (i = initi; i != endi; i += diri) {
			/*
			 * normal: x: i, y: j
			 * flip:   x: j, y: i
			 */
			pos = flip ? i * img_w + j : j * img_w + i;

			switch (bpp) {
			case 1: case 4: case 8:
				freebits -= bpp;
				bits |= pic->bitmap[pos] << freebits;
				if (freebits == 0)
					WRITEBITS;
				break;
			case 24:
				for (u = 0; u < 3; u++) {
					freebits -= 8;
					bits |= pic->bitmap[pos*3 + u] <<
								freebits;
					if (freebits == 0)
						WRITEBITS;
				}
				break;
			default:
				/* should not happen */
				fprintf(stderr, "unsupported bpp %d\n", bpp);
				break;
			}
		}
		/* size of a line of EMF bitmap must be a multiple of 4 byte */
		if (freebits != 32)
			WRITEBITS;
	}
#   undef WRITEBITS
}


static void
picbox(F_line *l)
{
	EMRSTRETCHDIBITS em_sd;
	BITMAPINFO bmi;
	char buf[16];
	int dx, dy;
	int img_w, img_h;
	int rotation;
	size_t bsize, coltabsize;
	int bpp;
	unsigned ncol;
	static RGBQUAD coltab[256];
	unsigned u;
	int flip;
	struct xfig_stream	pic_stream;

	dx = l->points->next->next->x - l->points->x;
	dy = l->points->next->next->y - l->points->y;

	/* rotation (counter clockwise) */
	rotation = 0;
	if (dx < 0 && dy < 0)
		rotation = 180;
	else if (dx < 0 && dy >= 0)
		rotation = 270;
	else if (dy < 0 && dx >= 0)
		rotation = 90;

	init_stream(&pic_stream);

	if (open_stream(l->pic->file, &pic_stream) == NULL) {
		put_msg("fig2dev: %s: No such picture file", l->pic->file);
		free_stream(&pic_stream);
		return;
	}
	if (fread(buf, (size_t) 16, (size_t) 1, pic_stream.fp) != 1) {
		put_msg("fig2dev: %s: short read\n", l->pic->file);
		close_stream(&pic_stream);
		free_stream(&pic_stream);
		return;
	}

	memset(&em_sd, 0, sizeof(EMRSTRETCHDIBITS));
	em_sd.emr.iType = htofl(EMR_STRETCHDIBITS);

	memset(&bmi, 0, sizeof(BITMAPINFO));
	bmi.bmiHeader.biSize = htofl(sizeof(BITMAPINFOHEADER));

#ifdef HAVE_PNG_H
	if (strncmp(buf, "\211\120\116\107\015\012\032\012", 8) == 0) {
		/* png file */
		int pllx, plly;
		if (rewind_stream(&pic_stream) == NULL) {
			err_msg("fig2dev: error rewinding image file %s",
					l->pic->file);
			free_stream(&pic_stream);
			return;
		}
		if (read_png(l->pic, &pic_stream, &pllx, &plly) == 0) {
			put_msg("fig2dev: %s: illegal format", l->pic->file);
			close_stream(&pic_stream);
			free_stream(&pic_stream);
			return;
		}
	}
#endif
	close_stream(&pic_stream);
	free_stream(&pic_stream);

	if (l->pic->subtype == P_GIF || l->pic->subtype == P_PCX ||
			l->pic->subtype == P_JPEG || l->pic->subtype == P_PNG) {

		img_w = l->pic->bit_size.x;
		img_h = l->pic->bit_size.y;
		ncol = l->pic->numcols;

		flip = is_flip(rotation, l->pic->flipped);

		/* EMF bitmap requires that every scan line be
		   a multiple of 4 byte */
		if (ncol <= 2) {
			bpp = 1;
			bsize = (flip ? (img_h + 31) / 32 * img_w :
					(img_w + 31) / 32 * img_h) * 4;
			coltabsize = 2 * sizeof(RGBQUAD);
		} else if (ncol <= 16) {
			bpp = 4;
			bsize = (flip ? (img_h + 7) / 8 * img_w :
					(img_w + 7) / 8 * img_h) * 4;
			coltabsize = ncol * sizeof(RGBQUAD);
		} else if (ncol <= 256) {
			bpp = 8;
			bsize = (flip ? (img_h + 3) / 4 * img_w :
					(img_w + 3) / 4 * img_h) * 4;
			coltabsize = ncol * sizeof(RGBQUAD);
		} else {
			bpp = 24;
			bsize = (flip ? (img_h * 3 + 3) / 4 * img_w :
					(img_w * 3 + 3) / 4 * img_h) * 4;
			coltabsize = 0;
		}

# ifdef __EMF_DEBUG__
		fprintf(stderr, "PicBox: %s: %u cols, %d bpp, bsize %u, "
				"img(%d,%d), fig(%d,%d)\n", l->pic->file, ncol,
				bpp, bsize, img_w, img_h, dx, dy);
# endif

		if (dx >= 0) {
			em_sd.xDest =em_sd.rclBounds.left = htofl(l->points->x);
			em_sd.rclBounds.right = htofl(l->points->next->next->x);
			em_sd.cxDest = htofl(dx);
		} else {
			em_sd.xDest = em_sd.rclBounds.left =
				htofl(l->points->next->next->x);
			em_sd.rclBounds.right = htofl(l->points->x);
			em_sd.cxDest = htofl(-dx);
		}
		if (dy >= 0) {
			em_sd.yDest = em_sd.rclBounds.top = htofl(l->points->y);
			em_sd.rclBounds.bottom =
				htofl(l->points->next->next->y);
			em_sd.cyDest = htofl(dy);
		} else {
			em_sd.yDest = em_sd.rclBounds.top =
				htofl(l->points->next->next->y);
			em_sd.rclBounds.bottom = htofl(l->points->y);
			em_sd.cyDest = htofl(-dy);
		}

		/* em_sd.xSrc = em_sd.ySrc = htofl(0);	  already cleared */

		if (flip) {
			bmi.bmiHeader.biWidth  = em_sd.cxSrc = htofl(img_h);
			bmi.bmiHeader.biHeight = em_sd.cySrc = htofl(img_w);
		} else {
			bmi.bmiHeader.biWidth  = em_sd.cxSrc = htofl(img_w);
			bmi.bmiHeader.biHeight = em_sd.cySrc = htofl(img_h);
		}

		/* em_sd.iUsageSrc = htofl(DIB_RGB_COLORS);  this is default */
		em_sd.dwRop = htofl(SRCCOPY);

		em_sd.offBmiSrc = htofl(sizeof(EMRSTRETCHDIBITS));
		em_sd.cbBmiSrc = htofl(sizeof(BITMAPINFO) + coltabsize);
		em_sd.offBitsSrc = htofl(sizeof(EMRSTRETCHDIBITS) +
				sizeof(BITMAPINFO) + coltabsize);

		bmi.bmiHeader.biSizeImage = em_sd.cbBitsSrc = htofl(bsize);

		em_sd.emr.nSize =
			htofl(sizeof(EMRSTRETCHDIBITS) + sizeof(BITMAPINFO) +
				coltabsize + bsize);

		bmi.bmiHeader.biPlanes = htofs(1);
		bmi.bmiHeader.biBitCount = htofs(bpp);
		bmi.bmiHeader.biCompression = htofl(BI_RGB);	/* uncompressed */
		bmi.bmiHeader.biXPelsPerMeter =
			bmi.bmiHeader.biYPelsPerMeter = htofl(2953);/* 75dpi? */
		if (bpp <= 8)
			bmi.bmiHeader.biClrUsed = htofl(ncol);
		/* bmi.bmiHeader.biClrImportant = htofl(0); */

		emh_write(&em_sd, sizeof(EMRSTRETCHDIBITS), (size_t)1,
				EMH_RECORD);
		emh_write(&bmi, sizeof(BITMAPINFO), (size_t)1, EMH_DATA);

		/* color table */
		if (coltabsize) {
			coltab[0].rgbBlue = coltab[0].rgbGreen =
				coltab[0].rgbRed = 0;
			coltab[1].rgbBlue = coltab[1].rgbGreen =
				coltab[1].rgbRed = 0;
			for (u = 0; u < ncol; u++) {
				coltab[u].rgbBlue  = l->pic->cmap[BLUE][u];
				coltab[u].rgbGreen = l->pic->cmap[GREEN][u];
				coltab[u].rgbRed   = l->pic->cmap[RED][u];
			}
			emh_write(coltab, coltabsize, (size_t)1, EMH_DATA);
		}

		/* bitmap */
		encode_bitmap(l->pic, bpp, rotation);

		/* need 4 byte align,
		   but the size of EMF bitmap is 4 byte aligned */
	} else {
		put_msg("fig2dev: %s: emf: unsupported picture format",
				l->pic->file);
		return;
	}
}/* end picbox */


/* Draws polygon boundary */
static void
polygon(F_line *l)
{
	F_point *p;
	int count;
	EMRPOLYGON em_pg;	/* Polygon in little endian format */
	POINTL *aptl;
	POINTS *apts;
	int bbx_top, bbx_bottom, bbx_left, bbx_right;	/* Bounding box */
	unsigned  cpt;	/* Number of points in the array */

	/* Calculate the number of points and the bounding box. */
	if (!(p = l->points)) return;
	bbx_left = p->x;
	bbx_top  = p->y;
	bbx_right  = p->x;
	bbx_bottom = p->y;
	for (cpt = 0; p; p = p->next) {
		UPDATE_BBX_X(p->x);
		UPDATE_BBX_Y(p->y);
		cpt++;
	}

	/* Windows 95/98/Me: maximum points allowed is approx. 1360 */
	if (cpt > 1360 && emflevel != EMF_LEVEL_WINNT)
		fprintf(stderr, "Warning: polygon has too many points -- "
				"may be partially invisible\n");

	/* Store bounding box in little endian format */
	em_pg.cptl = htofl(cpt);
	em_pg.rclBounds.left = htofl(bbx_left);
	em_pg.rclBounds.top  = htofl(bbx_top);
	em_pg.rclBounds.right  = htofl(bbx_right);
	em_pg.rclBounds.bottom = htofl(bbx_bottom);

	if (bbx_left >= -32768 && bbx_right <= 32767
			&& bbx_top >= -32768 && bbx_bottom <= 32767) {

# ifdef __EMF_DEBUG__
		fprintf(stderr, "Polygon16 %d rclBounds (ltrb): %d %d %d %d\n",
				cpt, bbx_left,  bbx_top, bbx_right, bbx_bottom);
# endif

		/* use 16bit point */
		/* Fill the array with the points of the polygon */
		if ((apts = malloc(cpt * sizeof(POINTS))) == NULL) {
			perror("fig2dev: malloc");
			exit(1);
		}
		for (p=l->points, count=0; p; p=p->next, count++) {
			apts[count].x = htofs(p->x);
			apts[count].y = htofs(p->y);
		}

		em_pg.emr.iType = htofl(EMR_POLYGON16);
		em_pg.emr.nSize =
			htofl(sizeof(EMRPOLYGON16) + cpt * sizeof(POINTS));

		emh_write(&em_pg, sizeof(EMRPOLYGON16), (size_t)1, EMH_RECORD);
		emh_write(apts, sizeof(POINTS), (size_t)cpt, EMH_DATA);

		free(apts);
	} else {
# ifdef __EMF_DEBUG__
		fprintf(stderr, "Polygon %d rclBounds (ltrb): %d %d %d %d\n",
				cpt, bbx_left,  bbx_top, bbx_right, bbx_bottom);
# endif

		/* use 32bit point */
		warn_32bit_pos();

		/* Fill the array with the points of the polygon */
		if ((aptl = malloc(cpt * sizeof(POINTL))) == NULL) {
			perror("fig2dev: malloc");
			exit(1);
		}
		for (p=l->points, count=0; p; p=p->next, count++) {
			aptl[count].x = htofl(p->x);
			aptl[count].y = htofl(p->y);
		}

		em_pg.emr.iType = htofl(EMR_POLYGON);
		em_pg.emr.nSize =
			htofl(sizeof(EMRPOLYGON) + cpt * sizeof(POINTL));

		emh_write(&em_pg, sizeof(EMRPOLYGON), (size_t)1, EMH_RECORD);
		emh_write(aptl, sizeof(POINTL), (size_t)cpt, EMH_DATA);

		free(aptl);
	}
}/* end polygon */


/* Replaces p by the shortened starting point of the line segment. */
static int polyline_adjust(F_point *p, F_point *q, double l)
{
	double d; Dir dir;

	if (direction(p, q, &dir, &d)) {
		p->x = round(p->x - l * dir.x);
		p->y = round(p->y - l * dir.y);
		return (l < d);
	}
	fprintf(stderr,"Warning: Arrow at zero-length line segment omitted.\n");
	return true;
}/* end polyline_arrow_adjust */


/* Draws polyline boundary (with arrows if needed) */
static void
polyline(F_line *l)
{
	F_point *p, *q, p0, pn;
	Dir dir;
	double d;
	EMRPOLYLINE em_pl;	/* Polyline in little endian format */
	POINTL *aptl;
	POINTS *apts;
	double alen, seglen;

	int bbx_top, bbx_bottom, bbx_left, bbx_right;	/* Bounding box */
	unsigned cpt;	/* Number of points in the array */
	unsigned u;

	/* Calculate the number of points and the bounding box. */
	if (!(p = l->points)) return;
	bbx_left = p->x;
	bbx_top  = p->y;
	bbx_right  = p->x;
	bbx_bottom = p->y;
	for (cpt = 0, q = p; p; q = p, p = p->next) {
		UPDATE_BBX_X(p->x);
		UPDATE_BBX_Y(p->y);
		cpt++;
	}
	p0 = *l->points;	/* first point */
	pn = *q;		/* last point */

	if (cpt == 1) {
		/* Draw single point as a short line. */
		cpt = 2;
		pn.y++;
	} else {
		/*
		 * Delete line segments which may overlap arrow heads.
		 */
		if (l->back_arrow) {		/* First point with arrow */
			alen = arrow_length(l->back_arrow);
			while (cpt > 1) {
				q = p0.next;
				seglen = distance((double)p0.x, (double)p0.y, (double)q->x, (double)q->y);
				if (seglen > alen) {
					break;
				} else {
					/* delete this segment */
					cpt--;
					p0 = *q;
					alen -= seglen;
				}
			}
			if (cpt > 1)
				/* shorten line segment */
				polyline_adjust(&p0, p0.next, alen);
		}
		if (l->for_arrow) {	/* Last point with arrow */
			alen = arrow_length(l->for_arrow);
			while (cpt > 1) {
				for (p = &p0, u = cpt; --u > 0;
						q = p, p = p->next)
					;
				seglen = distance((double)p->x, (double)p->y,
						(double)q->x, (double)q->y);
				if (seglen > alen) {
					break;
				} else {
					/* delete this segment */
					cpt--;
					pn = *q;
					alen -= seglen;
				}
			}
			if (cpt > 1)
				/* shorten line segment */
				polyline_adjust(&pn, q, alen);
		}
		if (cpt <= 1)		/* if all line segments are removed, */
			goto draw_arrows;	/* skip drawing line segments */
	}

	/* Windows 95/98/Me: maximum points allowed is approx. 1360 */
	if (cpt > 1360 && emflevel != EMF_LEVEL_WINNT)
		fprintf(stderr, "Warning: polyline has too many points -- "
				"may be partially invisible\n");

	/* Store bounding box in little endian format */
	em_pl.cptl = htofl(cpt);
	em_pl.rclBounds.left = htofl(bbx_left);
	em_pl.rclBounds.top  = htofl(bbx_top);
	em_pl.rclBounds.right  = htofl(bbx_right);
	em_pl.rclBounds.bottom = htofl(bbx_bottom);

	if (bbx_left >= -32768 && bbx_right <= 32767
			&& bbx_top >= -32768 && bbx_bottom <= 32767) {

# ifdef __EMF_DEBUG__
		fprintf(stderr, "Polyline16 %d rclBounds (ltrb): %d %d %d %d\n",
				cpt, bbx_left, bbx_top, bbx_right, bbx_bottom);
# endif

		/* use 16bit point */
		/* Fill the array with the points of the polyline */
		if ((apts = malloc(cpt * sizeof(POINTS))) == NULL) {
			perror("fig2dev: malloc");
			exit(1);
		}
		for (p = &p0, u = 0; u + 1 < cpt; p= p->next, u++) {
			apts[u].x = htofs(p->x);
			apts[u].y = htofs(p->y);
		}
		apts[u].x = htofs(pn.x);
		apts[u].y = htofs(pn.y);

		em_pl.emr.iType = htofl(EMR_POLYLINE16);
		HTOFL(em_pl.emr.nSize, sizeof(EMRPOLYLINE16) +
				cpt * sizeof(POINTS));

		emh_write(&em_pl, sizeof(EMRPOLYLINE16), (size_t)1, EMH_RECORD);
		emh_write(apts, sizeof(POINTS), (size_t)cpt, EMH_DATA);
		free(apts);
	} else {

# ifdef __EMF_DEBUG__
		fprintf(stderr, "Polyline %d rclBounds (ltrb): %d %d %d %d\n",
				cpt, bbx_left,  bbx_top, bbx_right, bbx_bottom);
# endif

		/* use 32bit point */
		warn_32bit_pos();

		/* Fill the array with the points of the polyline */
		if ((aptl = malloc(cpt * sizeof(POINTL))) == NULL) {
			perror("fig2dev: malloc");
			exit(1);
		}
		for (p = &p0, u = 0; u + 1 < cpt; p= p->next, u++) {
			aptl[u].x = htofl(p->x);
			aptl[u].y = htofl(p->y);
		}
		aptl[u].x = htofl(pn.x);
		aptl[u].y = htofl(pn.y);

		em_pl.emr.iType = htofl(EMR_POLYLINE);
		HTOFL(em_pl.emr.nSize,
				sizeof(EMRPOLYLINE) + cpt * sizeof(POINTL));

		emh_write(&em_pl, sizeof(EMRPOLYLINE), (size_t)1, EMH_RECORD);
		emh_write(aptl, sizeof(POINTL), (size_t)cpt, EMH_DATA);
		free(aptl);
	}

draw_arrows:
	if (!l->points->next) {
		if (l->for_arrow || l->back_arrow)
			fprintf(stderr, "Warning: Arrow at "
					"zero-length line segment omitted.\n");
		return;
	}		/* At least two different points now */


	if (l->back_arrow) {
		p = l->points;
		q = l->points->next;
		if (direction(p, q, &dir, &d)) {
			arrow(p, l->back_arrow, l, &dir);
		}
	}

	if (l->for_arrow) {
		/* TODO: replace this by line->last[] points */
		for (q=l->points, p=l->points->next; p->next; q=p, p=p->next) {}
		/* q is the one but last point */
		if (direction(p, q, &dir, &d)) {
			arrow(p, l->for_arrow, l, &dir);
		}
	}
}/* end polyline */


static void
rect(F_line *l)
{
	EMRRECTANGLE em_rt;

	if (!l->points || !l->points->next || !l->points->next->next) {
		fprintf(stderr, "Warning: Invalid fig box omitted.\n");
		return;
	}

	em_rt.emr.iType = htofl(EMR_RECTANGLE);
	em_rt.emr.nSize = htofl(sizeof(EMRRECTANGLE));
	em_rt.rclBox.left = htofl(l->points->x);
	em_rt.rclBox.top  = htofl(l->points->y);
	em_rt.rclBox.right  = htofl(l->points->next->next->x);
	em_rt.rclBox.bottom = htofl(l->points->next->next->y);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Rectangle rclBox (ltrb): %d %d %d %d\n",
			ftohl(em_rt.rclBox.left), ftohl(em_rt.rclBox.top),
			ftohl(em_rt.rclBox.right), ftohl(em_rt.rclBox.bottom));
# endif

	emh_write(&em_rt, sizeof(EMRRECTANGLE), (size_t) 1, EMH_RECORD);
}/* end rect */


static void
roundrect(F_line *l)
{
	EMRROUNDRECT em_rr;

	if (!l->points || !l->points->next || !l->points->next->next) {
		fprintf(stderr, "Warning: Invalid fig box omitted.\n");
		return;
	}

	em_rr.emr.iType = htofl(EMR_ROUNDRECT);
	em_rr.emr.nSize = htofl(sizeof(EMRROUNDRECT));
	em_rr.rclBox.left = htofl(l->points->x);
	em_rr.rclBox.top  = htofl(l->points->y);
	em_rr.rclBox.right  = htofl(l->points->next->next->x);
	em_rr.rclBox.bottom = htofl(l->points->next->next->y);
	em_rr.szlCorner.cx = em_rr.szlCorner.cy = htofl(l->radius * 2);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "RoundRectangle rclBox (ltrb): %d %d %d %d\n",
			ftohl(em_rr.rclBox.left), ftohl(em_rr.rclBox.top),
			ftohl(em_rr.rclBox.right), ftohl(em_rr.rclBox.bottom));
# endif

	emh_write(&em_rr, sizeof(EMRROUNDRECT), (size_t) 1, EMH_RECORD);
}


/* Draws interior and outline of a simple closed shape.  Cannot be
 * used for polylines and arcs (open shapes) or for arcboxes
 * (closed but not a simple emf primitive). */
static void
shape(
	F_line *l,
	int join,		/* join style */
	int cap,		/* cap style */
	void (*drawshape)(F_line *))
{
	edgeattr(1, l->style, l->thickness, l->pen_color, join, cap);
	fillstyle(l->fill_color, l->fill_style, l->pen_color);
	drawshape(l);
}/* end shape */


/* Draws interior of a closed shape, taking into account fill color
 * and pattern.  Boundary must be drawn separately.  Used for
 * polylines, arcboxes and arcs. */
static void
shape_interior(F_line *l, void (*drawshape)(F_line *))
{

	if (l->fill_style >= 0) {
		edgevis(0);				/* Don't draw edges */
		fillstyle(l->fill_color, l->fill_style, l->pen_color);
		drawshape(l);
	}
}/* end shape_interior */


/* Converts regular strings to unicode strings.  If the utext pointer is
 * null, memory is allocated.  Note, that carriage returns are converted
 * to nulls. */
static void
textunicode(
	const char *str,
	int *n_chars,
	short **utext,		/* If *utext is null, memory is allocated */
	int *n_unicode)
{
#ifdef HAVE_ICONV
	iconv_t icd = (iconv_t) -1;
	char *src;
	char *dst, *p;
	size_t srcleft, srccnt, dstleft, dstcnt;
	int i;

	if (figLanguage != LANG_DEFAULT) {
		if ((icd = iconv_open(EMF_ICONV_CODESET,
				iconvCharset[figLanguage])) == (iconv_t)-1)
			perror("genemf: iconv");
	}

	src = (char *)str;
	srcleft = srccnt = strlen(str);
	if ((dst = (char *) *utext) == NULL) {
		dst = (char *)(*utext = malloc((srcleft + 2/*null termination + align*/)
					* sizeof(short)));
		dstleft = dstcnt = srcleft * sizeof(short);
	} else
		dstleft = dstcnt = *n_unicode;

	if (icd != (iconv_t) -1) {
		if (iconv(icd, &src, &srcleft, &dst, &dstleft) == (size_t) -1)
			fprintf(stderr, "genemf: iconv: "
					"illegal byte sequence\n");

		(void)iconv_close(icd);

		*n_chars = srccnt - srcleft;
		*n_unicode = dstcnt - dstleft;

		/* Convert '\n' to 0 */
		p = (void *)*utext;
		for (i = 0; i < *n_unicode; i += 2) {
#ifdef EMF_ICONV_NEED_SWAP
			/* swap byte order */
			char c = p[i];
			p[i] = p[i + 1];
			p[i + 1] = c;
#endif
			if (p[i] == '\n' && p[i+1] == 0)
				p[i] = 0;
		}
	} else {
		*n_chars = srccnt;
		*n_unicode = srccnt * 2;
		for (i = 0; i < (int) srccnt; ++i) {
			if (str[i] == '\n')
				(*utext)[i] = 0;
			else
				(*utext)[i] = htofs(str[i]);
		}
	}
#else	/* !defined(HAVE_ICONV) */
	int    i;

	*n_chars = strlen(str);
	*n_unicode = *n_chars * 2;
	if (*utext == NULL) {
		*utext = malloc((size_t)(*n_unicode + 4/* null + align */));
	}
	for (i = 0; i < *n_chars; ++i) {	/* Convert to unicode. */
		if (str[i] == '\n') {
			(*utext)[i] = 0;
		} else {
			(*utext)[i] = htofs(str[i]);
		}
	}
	(*utext)[*n_chars] = '\0';
#endif
}/* end textunicode */


static void
text(
	int x,
	int y,		/* reference point */
	RECTL *bbx,	/* bounding box */
	char *str)
{
	int    n_chars, n_unicode;
	short	 *utext = NULL;
	EMREXTTEXTOUTW em_tx;	/* Text structure in little endian format */

	memset(&em_tx, 0, sizeof(EMREXTTEXTOUTW));
	em_tx.emr.iType = htofl(EMR_EXTTEXTOUTW);

	em_tx.rclBounds = *bbx;

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Textout |%s| rclBounds (ltrb): %d %d %d %d\n",
			str,
			ftohl(em_tx.rclBounds.left),  ftohl(em_tx.rclBounds.top),
			ftohl(em_tx.rclBounds.right), ftohl(em_tx.rclBounds.bottom));
# endif

	em_tx.iGraphicsMode = htofl(GM_COMPATIBLE);
	em_tx.exScale.bit_uint = htofl(PIXEL_01MM_BITPATTERN);
	em_tx.eyScale.bit_uint = htofl(PIXEL_01MM_BITPATTERN);

	em_tx.emrtext.ptlReference.x = htofl(x);
	em_tx.emrtext.ptlReference.y = htofl(y);

	textunicode(str, &n_chars, &utext, &n_unicode);

	em_tx.emrtext.nChars = htofl(n_unicode / 2); /* number of unicode chars */
	em_tx.emrtext.offString = htofl(sizeof(EMREXTTEXTOUTW));

	n_unicode = (n_unicode + 4) & ~3;	/* null termination + align */
	em_tx.emr.nSize = htofl(sizeof(EMREXTTEXTOUTW) + n_unicode);

	emh_write(&em_tx, sizeof(EMREXTTEXTOUTW), (size_t) 1, EMH_RECORD);
	emh_write(utext, (size_t) 1, (size_t) n_unicode, EMH_DATA);

	free(utext);
}/* end text */


static void
textcolr(int color)
{
	static int oldcolor = UNDEFVALUE;
	EMRSETTEXTCOLOR em_tc;

	bkmode(TRANSPARENT);		/* fig doesn't have text background */
	chkcache(color, oldcolor);

	memset(&em_tc, 0, sizeof(EMRSETTEXTCOLOR));
	em_tc.emr.iType = htofl(EMR_SETTEXTCOLOR);
	em_tc.emr.nSize = htofl(sizeof(EMRSETTEXTCOLOR));
	HTOFL(em_tc.crColor, conv_color(color));

# ifdef __EMF_DEBUG__
	fprintf(stderr, "SetTextColor (rgb): %x\n", ftohl(em_tc.crColor));
# endif

	emh_write(&em_tc, sizeof(EMRSETTEXTCOLOR), (size_t) 1, EMH_RECORD);
}/* end textcolr */


static void
textalign(int align)
{
	static int oldalign = TA_LEFT|TA_TOP|TA_NOUPDATECP;/* startup default */
	EMRSETTEXTALIGN em_ta;

	chkcache(align, oldalign);
	em_ta.emr.iType = htofl(EMR_SETTEXTALIGN);
	em_ta.emr.nSize = htofl(sizeof(EMRSETTEXTALIGN));
	em_ta.iMode = htofl(align);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "SetTextAlign (mode): 0x%x\n", align);
# endif

	emh_write(&em_ta, sizeof(EMRSETTEXTALIGN), (size_t)1, EMH_RECORD);
}


#ifdef HAVE_ICONV
static void
moveto(int x, int y)
{
	EMRMOVETOEX em_mv;

	em_mv.emr.iType = htofl(EMR_MOVETOEX);
	em_mv.emr.nSize = htofl(sizeof(EMRMOVETOEX));
	em_mv.ptl.x = htofl(x);
	em_mv.ptl.y = htofl(y);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "MoveTo (x, y): %d, %d\n", x, y);
# endif

	emh_write(&em_mv, sizeof(EMRMOVETOEX), (size_t)1, EMH_RECORD);
}
#endif	/* HAVE_ICONV */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void
genemf_option(char opt, char *optarg)
{

	rounded_arrows = false;
	switch (opt) {
	case 'r':
		rounded_arrows = true;
		break;

	case 'l':
		if (strcasecmp(optarg, "win95") == 0)
			emflevel = EMF_LEVEL_WIN95;
		else if (strcasecmp(optarg, "win98") == 0)
			emflevel = EMF_LEVEL_WIN98;
		else if (strcasecmp(optarg, "winnt") == 0)
			emflevel = EMF_LEVEL_WINNT;
		else
			fprintf(stderr, "warning: unknown level %s ignored\n",
					optarg);
		break;

	case 'G':
	case 'L':
		break;

	default:
		put_msg(Err_badarg, opt, "emf");
		exit(1);
	}
}/* end genemf_option */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void
genemf_start(F_compound *objects)
{
	(void)	objects;

	EMRSETMAPMODE em_sm;
	short *uni_description = NULL;	/* Unitext description. */
	int n_chars, n_unicode;
	F_comment comm, *p;
	size_t commlen;
	short *ud;
	char *figname;

#ifdef HAVE_ICONV
	{
		char *locale;

		locale = getenv("LANG");
		if (locale == NULL) {
			fprintf(stderr, "fig2dev: LANG not defined; "
					"assuming C locale\n");
			locale = "C";
		}
		if (strncmp(locale, "zh_CN", (size_t) 5) == 0)
			figLanguage = LANG_ZH_CN;
		else if (strncmp(locale, "zh_TW", (size_t) 5) == 0)
			figLanguage = LANG_ZH_TW;
		else if (strncmp(locale, "ja", (size_t) 2) == 0)
			figLanguage = LANG_JA;
		else if (strncmp(locale, "ko", (size_t) 2) == 0)
			figLanguage = LANG_KO;
	}
#endif

	memset(&emh, 0, sizeof(ENHMETAHEADER));
	emh.iType = htofl(EMR_HEADER);

	emh.dSignature = htofl(ENHMETA_SIGNATURE);
	emh.nVersion = htofl(ENHMETA_VERSION);
	emh_nHandles = 0;
	handles = NULL;
	latesthandle = (void *) &handles;

	memset(lasthandle, 0, sizeof(lasthandle));/* Initialize the DC handles*/

	/* Create a description string. */

	if (from)
		figname=from;
	else
		figname=strdup("stdin");

	comm.next = objects->comments;
	if ((comm.comment = malloc(strlen(figname) +
					80 + sizeof PACKAGE_VERSION)) == NULL) {
		perror("fig2dev: malloc");
		exit(1);
	}
	sprintf(comm.comment,
			"Converted from %s using fig2dev %s for %s",
			figname, PACKAGE_VERSION, emflevelname[emflevel]);

	/* prescan comment strings and (over)estimate the total length */
	commlen = 0;
	for (p = &comm; p; p = p->next)
		commlen += strlen(p->comment) + 1;

	commlen = commlen * 2 + 2;	/* 2byte/char (+ align) */
	if ((uni_description = malloc(commlen)) == NULL) {
		perror("fig2dev: malloc");
		exit(1);
	}

	/* convert comment strings to unicode */
	ud = uni_description;
	for (p = &comm; p; p = p->next) {
# ifdef __EMF_DEBUG__
		fprintf(stderr, "%s\n", p->comment);
# endif
		n_unicode = commlen;
		textunicode(p->comment, &n_chars, &ud, &n_unicode);
		commlen -= n_unicode;
		ud += n_unicode/2;
		*ud++ = 0;
	}
	*ud = 0;	/* for align */
	n_unicode = (char *)ud - (char *)uni_description;  /* length in byte */

	emh.nDescription = htofl(n_unicode / 2);
	emh.offDescription = htofl(sizeof(ENHMETAHEADER));

	/* The size of the reference device in pixels.  We will define a pixel
	 * in xfig uints of 1200 dots per inch and we'll use an 250 x 200 mm
	 * or 10.24 x 8.46 inch viewing area. */

	emh.szlMillimeters.cx = htofl(260);		/* 10.24 inches wide. */
	emh.szlMillimeters.cy = htofl(215);		/*  8.46 inches high. */

	HTOFL(emh.szlDevice.cx, round(260 * 47.244094));
	HTOFL(emh.szlDevice.cy, round(215 * 47.244094));

	/* Inclusive-inclusive bounds in device units (1200 dpi) and
	 * 0.01 mm units.  Add 1/20 inch all the way around. */

	emh.rclBounds.left   = htofl(llx - 48);
	emh.rclBounds.top    = htofl(lly - 48);
	emh.rclBounds.right  = htofl(urx + 48);
	emh.rclBounds.bottom = htofl(ury + 48);

	HTOFL(emh.rclFrame.left,  floor((llx - 48) * PIXEL_01MM));
	HTOFL(emh.rclFrame.top,   floor((lly - 48) * PIXEL_01MM));
	HTOFL(emh.rclFrame.right,  ceil((urx + 48) * PIXEL_01MM));
	HTOFL(emh.rclFrame.bottom, ceil((ury + 48) * PIXEL_01MM));

# ifdef __EMF_DEBUG__
	fprintf(stderr, "rclBounds (ltrb): %d %d %d %d\n",
			ftohl(emh.rclBounds.left),  ftohl(emh.rclBounds.top),
			ftohl(emh.rclBounds.right),
			ftohl(emh.rclBounds.bottom));
	fprintf(stderr, "rclFrame  (ltrb): %d %d %d %d\n",
			ftohl(emh.rclFrame.left),  ftohl(emh.rclFrame.top),
			ftohl(emh.rclFrame.right), ftohl(emh.rclFrame.bottom));
# endif

	n_unicode = (n_unicode + 3) & ~3;	/* null termination + align */
	emh.nSize = htofl(sizeof(ENHMETAHEADER) + n_unicode);

	emh_write(&emh, sizeof(ENHMETAHEADER), (size_t) 1, EMH_RECORD);
	emh_write(uni_description, (size_t) 1, (size_t) n_unicode, EMH_DATA);

	/* The SetMapMode function sets the mapping mode of the specified
	 * device context.  The mapping mode defines the unit of measure used
	 * to transform page-space units into device-space units, and also
	 * defines the orientation of the device's x and y axes. */
	em_sm.emr.iType = htofl(EMR_SETMAPMODE);
	em_sm.emr.nSize = htofl(sizeof(EMRSETMAPMODE));

	/* MM_TEXT Each logical unit is mapped to one device pixel.  Positive
	 * x is to the right; positive y is down.  The MM_TEXT mode allows
	 * applications to work in device pixels, whose size varies from device
	 * to device. */
	em_sm.iMode = htofl(MM_TEXT);
# ifdef __EMF_DEBUG__
	fprintf(stderr, "SetMapMode: %d\n", ftohl(em_sm.iMode));
# endif
	emh_write(&em_sm, sizeof(EMRSETMAPMODE), (size_t)1, EMH_RECORD);

	free(comm.comment);
	free(uni_description);
}/* end genemf_start */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void
genemf_arc(F_arc *_a)
{
	F_arc a = *_a;

	if (cwarc(&a))
		arc_reverse(&a);	/* make counter clockwise arc */

	switch (a.type) {
	case T_OPEN_ARC:
		/* open arcs should be drawn separately */
		shape_interior((F_line *)(void *)&a,
				(void (*)(F_line *))arcinterior);
		if (a.thickness > 0) {
			edgeattr(1, a.style, a.thickness, a.pen_color, 0,
					a.cap_style);
			arcoutline(&a);
		}
		break;
	case T_PIE_WEDGE_ARC:
		shape((F_line *)(void *)&a, a.cap_style, 0,
				(void (*)(F_line *))arcoutline);
		break;
	default:
		fprintf(stderr, "Unsupported fig arc type %d.\n", a.type);
	}
}/* end genemf_arc */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void
genemf_ellipse(F_ellipse *e)
{

	switch (e->type) {
	case T_ELLIPSE_BY_RAD:
	case T_ELLIPSE_BY_DIA:
		if (fabs(e->angle) > EPSILON && fabs(sin(e->angle)) > EPSILON)
			rotated_ellipse(e);
		else
			shape((F_line *)e, 0, 0, (void (*)(F_line *))ellipse);
		break;
	case T_CIRCLE_BY_RAD:
	case T_CIRCLE_BY_DIA:
		shape((F_line *)e, 0, 0, (void (*)(F_line *))circle);
		break;
	default:
		fprintf(stderr, "Unsupported FIG ellipse type %d.\n", e->type);
	}
}/* end genemf_ellipse */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void
genemf_line(F_line *l)
{

	switch (l->type) {
	case T_POLYLINE:
		shape_interior(l, polygon);	/* draw interior */

		if (l->points->next == NULL	/* single point line */
				&& l->cap_style == 0) {	/* butt style */
			/* draw as projecting style but in smaller size */
			edgeattr(1, l->style, (l->thickness + 1) / 2,
					l->pen_color, l->join_style, 2);
		} else {
			edgeattr(1, l->style, l->thickness, l->pen_color,
					l->join_style, l->cap_style);
		}

		polyline(l);			/* draw boundary */
		break;
	case T_BOX:				/* simple closed shape */
		shape(l, l->join_style, l->cap_style, rect);
		break;
	case T_POLYGON:
		shape(l, l->join_style, l->cap_style, polygon);
		break;
	case T_ARC_BOX:
		shape(l, 0, 0, roundrect);	/* simple closed shape */
		break;
	case T_PIC_BOX:
		picbox(l);
		break;
	default:
		fprintf(stderr, "Unsupported FIG polyline type %d.\n", l->type);
		return;
	}
}/* end genemf_line */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*ARGSUSED*/
void
genemf_spline(F_spline *s)
{
	static int wgiv = 0;

	print_comments("% ", s->comments, "");
	if (!wgiv) {
		fprintf(stderr, "Warning: the EMF driver does not support (old "
				"style) FIG splines.\nSuggestion: convert your "
				"(old?) FIG image by loading it into xfig v3.2 "
				"or\nhigher and saving again.\n");
		wgiv = 1;
	}
}/* end genemf_spline */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void
genemf_text(F_text *t)
{
	int font;
	RECTL rclBounds;
	int x, y;
	double sin_theta, cos_theta;
#ifdef HAVE_ICONV
	char *s, *s1, bak;
	int nascii, neuc;
#endif

	textcolr(t->color);
	font = conv_fontindex(t->font, t->flags);  /* Convert the font index */

	/*
	 * calculate start position
	 */
	x = t->base_x;
	y = t->base_y;
	sin_theta = sin(t->angle);
	cos_theta = cos(t->angle);
	switch (t->type) {
	case T_LEFT_JUSTIFIED:
		break;
	case T_CENTER_JUSTIFIED:
		x = x + (-(t->length/2.0) * cos_theta);
		y = y + ((t->length/2.0) * sin_theta);
		break;
	case T_RIGHT_JUSTIFIED:
		x = x + (-t->length * cos_theta);
		y = y + (t->length * sin_theta);
		break;
	default:
		fprintf(stderr, "Unsupported fig text type %d.\n", t->type);
	}

	HTOFL(rclBounds.left,   round(x - t->size * sin_theta));
	HTOFL(rclBounds.top,    round(y - t->size * cos_theta));
	HTOFL(rclBounds.right,  round(x + t->length * cos_theta));
	HTOFL(rclBounds.bottom, round(y + t->length * sin_theta));

#ifdef HAVE_ICONV
	if (figLanguage != LANG_DEFAULT && IS_LOCALE_FONT(font)) {
		/* prescan text to decide if font switching is needed */
		nascii = neuc = 0;
		for (s = t->cstring; *s; ++s) {
			/*
			 * isascii(int) is defined on all integer values, man
			 * isascii(3p) a cast to unsigned char is not necessary,
			 * as for the other is* character class functions
			 * (e.g., man isalpha(3p) or (3))
			 */
			if (isascii(*s))
				++nascii;
			else
				++neuc;
		}

		if (neuc == 0)
			;		/* ASCII only */
		else if (nascii == 0) {
			/* locale font only */
			font = (font == FONT_TIMES_ROMAN) ?
				FONT_LOCALE_ROMAN : FONT_LOCALE_BOLD;
		} else {
			/*
			 * need font switching
			 */

			/*
			 * use home grown alignment
			 */
			/* use and update current position */
			textalign(TA_LEFT | TA_BASELINE | TA_UPDATECP);

			/* move to the first (left) position */
			moveto(x, y);

			for (s = t->cstring; *s; s = s1) {
				if (*s & 0x80) {
					/* EUC 2byte char */
					for (s1 = s; *s1 & 0x80; s1 += 2)
						;
					textfont((font == FONT_TIMES_ROMAN) ?
							FONT_LOCALE_ROMAN :
							FONT_LOCALE_BOLD,
					t->size,
					round(t->angle * RAD_01DEG));
				} else {
					/* ascii */
					for (s1 = s; *s1 && (*s1 & 0x80) == 0;
							s1++)
						;
					textfont(font, t->size,
						round(t->angle * RAD_01DEG));
				}
				bak = *s1;
				*s1 = '\0';

				text(0, 0, &rclBounds, s);

				*s1 = bak;
			}
			return;
		}
	}
#endif	/* HAVE_ICONV */

	/*
	 * Use alignment of EMF, which shall be better than that of home grown.
	 * Do not use current position.
	 */
	switch (t->type) {
	case T_LEFT_JUSTIFIED:
		textalign(TA_LEFT | TA_BASELINE | TA_NOUPDATECP);
		break;
	case T_CENTER_JUSTIFIED:
		textalign(TA_CENTER | TA_BASELINE | TA_NOUPDATECP);
		break;
	case T_RIGHT_JUSTIFIED:
		textalign(TA_RIGHT | TA_BASELINE | TA_NOUPDATECP);
		break;
	}

	textfont(font, t->size, round(t->angle * RAD_01DEG));
	text(t->base_x, t->base_y, &rclBounds, t->cstring);
}/* end genemf_text */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
int
genemf_end(void)
{
	EMREOF em_eof;
	struct emfhandle *h, *h1;

	/* delete all created handles */
	for (h = handles; h; h = h1) {
		delete_handle(h);
		h1 = h->next;
		free(h);
	}

	memset(&em_eof, 0, sizeof(EMREOF));
	em_eof.emr.iType = htofl(EMR_EOF);
	em_eof.emr.nSize = htofl(sizeof(EMREOF));
	em_eof.offPalEntries = htofl(sizeof(EMREOF) - sizeof(EMFulong));
	em_eof.nSizeLast = htofl(sizeof(EMREOF));
	emh_write(&em_eof, sizeof(EMREOF), (size_t) 1, EMH_RECORD);

# ifdef __EMF_DEBUG__
	fprintf(stderr, "Metafile Bytes: %d    Records: %d    Handles: %d\n",
			emh_nBytes, emh_nRecords, emh_nHandles);
# endif

	/* Rewrite the updated header record at the beginning of the file. */

	emh.nBytes   = htofl(emh_nBytes);
	emh.nRecords = htofl(emh_nRecords);
	emh_nHandles++;	/* size of handle table is last handle number + 1 */
	emh.nHandles = htofs(emh_nHandles);

	if (fseek(tfp, 0L, SEEK_SET) < 0) {
		fprintf(stderr,"fig2dev: error: fseek() failed. EMF language "
				"requires the output is seekable.\n\t\t"
				"Output to a plain file, not to a pipe.\n");
		return -1;
	}
	if (fwrite(&emh, (size_t)1, sizeof(ENHMETAHEADER), tfp) !=
							sizeof(ENHMETAHEADER) ||
			fseek(tfp, 0L, SEEK_END) < 0) {
		fprintf(stderr,"fig2dev: error: failed to write EMF header\n");
		return -1;
	}

	/* all ok */
	return 0;
}/* end genemf_end */


/*~~~~~|><|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
struct driver dev_emf = {
	genemf_option,
	genemf_start,
	gendev_nogrid,		/* TODO - Create genemf_grid */
	genemf_arc,
	genemf_ellipse,
	genemf_line,
	genemf_spline,
	genemf_text,
	genemf_end,
	INCLUDE_TEXT
};
