The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
/*
 * $Id: metafont.trm,v 1.14 2002/01/26 19:12:04 lhecking Exp $
 */

/* GNUPLOT - metafont.trm */

/*[
 * Copyright 1986 - 1993, 1998   Thomas Williams, Colin Kelley
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
]*/

/*
 *			  GNUPLOT -- mf.trm
 *
 *		    This terminal driver supports:
 *		       Metafont Plot Commands
 *
 * Written by : Pl Hedne
 *		Trondheim, Norway
 *		Pal.Hedne@termo.unit.no
 */

/*
 * Improvements and bug fixes by Carsten Steger:
 * - Set default plot size to 5 by 3 inches as in the latex- and eepic-
 *   drivers
 * - Fixed some bugs concerning resolution dependent output
 * - Added MF_scale function
 * - Added MF_justify_text function and modified MF_put_text function and
 *   put_text macro accordingly
 * - Modified MF_move and MF_vector to make output shorter and modified
 *   MF_text accordingly
 * - Added various linetypes by plotting dashed lines; had to modify
 *   MF_linetype and MF_vector for this
 * - Added MF_arrow function
 * - All global variables and #define'd names begin with MF_ now
 * As a consequence almost nothing of the original code by Pl Hedne remains
 * but credit goes to him for the ingenious trick of storing the character
 * images into picture variables, without which this driver would have been
 * impossible for me to write.
 *
 * 10/03/95: Converted to new terminal layout by Carsten Steger.
 */

#include "driver.h"

#ifdef TERM_REGISTER
register_term(mf)
#endif

#ifdef TERM_PROTO

#define MF_DPI (300)
/* resolution of printer we expect to use; the value itself is not
 * particularly important... it is here only for compatibility to the
 * LaTeX-driver and to get the spacing right. */

/* 5 inches wide by 3 inches high (default) */
#define MF_XSIZE 5.0
#define MF_YSIZE 3.0
#define MF_XMAX (MF_XSIZE*MF_DPI)
#define MF_YMAX (MF_YSIZE*MF_DPI)

#define MF_HTIC (5*MF_DPI/72)
#define MF_VTIC (5*MF_DPI/72)
#define MF_HCHAR (MF_DPI*53/10/72)
#define MF_VCHAR (MF_DPI*11/72)

TERM_PUBLIC void MF_init __PROTO((void));
TERM_PUBLIC void MF_graphics __PROTO((void));
TERM_PUBLIC void MF_text __PROTO((void));
TERM_PUBLIC int MF_justify_text __PROTO((enum JUSTIFY mode));
TERM_PUBLIC int MF_text_angle __PROTO((int ang));
TERM_PUBLIC void MF_linetype __PROTO((int linetype));
TERM_PUBLIC void MF_move __PROTO((unsigned int x, unsigned int y));
TERM_PUBLIC void MF_vector __PROTO((unsigned int x, unsigned int y));
TERM_PUBLIC void MF_arrow __PROTO((unsigned int sx, unsigned int sy,
				   unsigned int ex, unsigned int ey,
				   TBOOLEAN head));
TERM_PUBLIC void MF_put_text __PROTO((unsigned int x, unsigned int y, const char *str));
TERM_PUBLIC void MF_reset __PROTO((void));

#define GOT_MF_PROTO

#endif /* TERM_PROTO */


#ifndef TERM_PROTO_ONLY

#ifdef TERM_BODY


/* Plot size in inches */
static double MF_xsize = MF_XSIZE;
static double MF_ysize = MF_YSIZE;
static int MF_char_code;
static int MF_ang;
static int MF_line_type;
static enum JUSTIFY MF_justify;
static double MF_dist_left;
static int MF_is_solid;
static int MF_picked_up_pen;
/* 
 * We keep track of where we are with respect to dashed lines by using
 * the next five variables. MF_dash_index indicates which element of
 * MF_lines[..].dashlen should be used. The MF_last.. variables keep
 * track of the position of the pen.
 */
static int MF_dash_index;
static unsigned int MF_last_x, MF_last_y;

static struct {
    int solid;			/* Is the line solid? */
    float thickness;		/* Thickness of pen we are going to use */
    int dashlen[4];		/* Length of individual segments; even: line; odd: gap */
} MF_lines[10] =
{
    {
	1, 1.5, { 0, 0, 0, 0 }
    },
    {
	0, 1.0, { MF_DPI / 60, MF_DPI / 50, MF_DPI / 60, MF_DPI / 50 }
    },
    {
	1, 1.5, { 0, 0, 0, 0 }
    },
    {
	0, 1.5, { MF_DPI / 20, MF_DPI / 30, MF_DPI / 20, MF_DPI / 30 }
    },
    {
	0, 1.5, { MF_DPI / 30, MF_DPI / 20, MF_DPI / 30, MF_DPI / 20 }
    },
    {
	0, 1.5, { MF_DPI / 15, MF_DPI / 30, MF_DPI / 60, MF_DPI / 30 }
    },
    {
	0, 1.5, { MF_DPI / 30, MF_DPI / 50, MF_DPI / 30, MF_DPI / 50 }
    },
    {
	0, 1.5, { MF_DPI / 20, MF_DPI / 50, MF_DPI / 60, MF_DPI / 30 }
    },
    {
	0, 1.5, { MF_DPI / 30, MF_DPI / 50, MF_DPI / 30, MF_DPI / 30 }
    },
    {
	0, 1.5, { MF_DPI / 60, MF_DPI / 50, MF_DPI / 60, MF_DPI / 30 }
    }
    /* dash: line,     gap,      line,     gap      */
};



TERM_PUBLIC void
MF_init()
{
    MF_char_code = 0;
    MF_ang = 0;

    fputs("\
if unknown cmbase: input cmbase fi\n\n\
tracingstats:=1;\n\
picture r[];\n\
\ndef openit = openwindow currentwindow\n\
  from (0,0) to (400,800) at (-50,500) enddef;\n\
\nmode_setup;\n", gpoutfile);

    fputs("\
\n%Include next eight lines if you have problems with the mode on your system..\n\
%proofing:=0;\n\
%fontmaking:=1;\n\
%tracingtitles:=0;\n\
%pixels_per_inch:=300;\n\
%blacker:=0;\n\
%fillin:=.2;\n\
%o_correction:=.6;\n\
%fix_units;\n", gpoutfile);

    /* Next lines must be included if text support is needed (CM base used) */
    fputs("\
\ndef put_text(expr ts,xstart,ystart,rot,justification) =\n\
  begingroup\n\
    text_width:=0;text_height:=0;text_depth:=0;\n\
    for ind:=0 step 1 until length(ts)-1:\n\
      dec_num:=ASCII substring (ind,ind+1) of ts;\n\
      if unknown r[dec_num]: dec_num:=32; fi\n\
      if dec_num=32: \n\
        text_width:=text_width+wd[65];\n\
        text_height:=max(text_height,ht[65]);\n\
        text_depth:=max(text_depth,dp[65]);\n\
      elseif dec_num>=0: \n\
        text_width:=text_width+wd[dec_num];\n\
        text_height:=max(text_height,ht[dec_num]);\n\
        text_depth:=max(text_depth,dp[dec_num]);\n\
      fi\n\
    endfor\n\
    if rot=90:\n\
      if justification=1: ynext:=ystart;\n\
      elseif justification=2: ynext:=round(ystart-text_width/2);\n\
      else: ynext:=round(ystart-text_width);\n\
      fi\n\
      xnext:=xstart+(text_height-text_depth)/2;\n\
    else:\n\
      if justification=1: xnext:=xstart;\n\
      elseif justification=2: xnext:=round(xstart-text_width/2);\n\
      else: xnext:=round(xstart-text_width);\n\
      fi\n\
      ynext:=ystart-(text_height-text_depth)/2;\n\
    fi\n\
    for ind:=0 step 1 until length(ts)-1:\n\
      dec_num:=ASCII substring (ind,ind+1) of ts;\n\
      if unknown r[dec_num]: dec_num:=32; fi\n\
      if dec_num=32: \n\
        xnext:=xnext+wd[65]*cosd rot;\n\
        ynext:=ynext+wd[65]*sind rot;\n\
      elseif dec_num>=0: \n\
        currentpicture:=currentpicture+r[dec_num] shifted(xnext,ynext)\n\
          rotatedaround ((xnext,ynext),rot); \n\
        xnext:=xnext+wd[dec_num]*cosd rot;\n\
        ynext:=ynext+wd[dec_num]*sind rot;\n\
      fi\n\
    endfor\n\
  endgroup \n\
enddef;\n", gpoutfile);

    fputs("\
\ndef endchar =\n\
  r[charcode]:=currentpicture;\n\
  wd[charcode]:=w;ht[charcode]:=h;dp[charcode]:=d;\n\
  message \"Picture of charcode no.\" & decimal charcode;\n\
  endgroup;\n\
enddef;\n\
let endchar_ = endchar;\n\
let generate = relax;\n\
let roman = relax;\n", gpoutfile);

    fputs("\
input cmr10.mf\n\
if ligs>1: font_coding_scheme:=\"TeX text\";\n\
  spanish_shriek=oct\"074\"; spanish_query=oct\"076\";\n\
else: font_coding_scheme:=\n\
  if ligs=0: \"TeX typewriter text\"\n\
  else: \"TeX text without f-ligatures\" fi;\n\
  spanish_shriek=oct\"016\"; spanish_query=oct\"017\"; fi\n\
font_setup;\n\
input romanu.mf %Roman uppercase.\n\
input romanl.mf %Roman lowercase.\n\
input greeku.mf %Greek uppercase.\n\
input romand.mf %Numerals.\n\
input romanp.mf %Ampersand, question marks, currency sign.\n\
input romspl.mf %Lowercase specials (dotless \\i, ligature \\ae, etc.)\n\
input romspu.mf %Uppercase specials (\\AE, \\OE, \\O)\n\
input punct.mf %Punctuation symbols.\n\
\nminus=ASCII\"-\"; cmchar \"Minus sign\";\n\
beginarithchar(minus); \n\
  pickup rule.nib;\n\
  lft x1=hround 1.5u-eps;\n\
  x2=w-x1; y1=y2=math_axis;\n\
  draw z1--z2;	 % bar\n\
  labels(1,2); \n\
endchar;\n", gpoutfile);

    fputs("\
\ncmchar \"Period\";\n\
  numeric dot_diam#; dot_diam#:=if monospace: 5/4 fi\\ dot_size#;\n\
  define_whole_blacker_pixels(dot_diam);\n\
  beginchar(\".\",5u#,dot_diam#,0);\n\
  adjust_fit(0,0); pickup fine.nib;\n\
  pos1(dot_diam,0); pos2(dot_diam,90);\n\
  lft x1l=hround(.5w-.5dot_diam); bot y2l=0; z1=z2; dot(1,2);	% dot\n\
  penlabels(1,2);\n\
endchar;\n", gpoutfile);

    fputs("\
\ndef endchar =\n\
  % Next line should probably be removed if CM base is used\n\
  l:=0; r:=w;\n\
  %Include the next two lines if you want to\n\
  %rotate the picture 90 deg.(Portrait to Landscape)\n\
  %currentpicture:=currentpicture rotated 90 shifted (h,0);\n\
  %tmp:=charht; charht:=charwd; charwd:=tmp;\n\
  scantokens extra_endchar;\n\
  if proofing>0: makebox(proofrule); fi\n\
  chardx:=w;\n\
  shipit;\n\
  if displaying>0: makebox(screenrule); showit; fi\n\
  endgroup \n\
enddef;\n\
let endchar_ = endchar;\n\
let generate = input;\n\
let roman = roman;\n", gpoutfile);

    /* font_size must be bigger than em#/16 by METAFONT rules.
     * Therefore make it pretty big so big figures will be
     * handled correctly. Setting font_size to 72pt# lets us
     * handle characters up to 15.94 by 15.94 inches. */
    fputs("\
\n\nfont_identifier:=\"GNUPLOT\";\n\
font_size 72pt#;\n\
th#=0.4pt#; define_whole_pixels(th);\n\
\npath arrowhead;\n\
arrowhead = (-7pt,-2pt){dir30}..(-6pt,0pt)..\
{dir150}(-7pt,2pt) &\n\
  (-7pt,2pt)--(0pt,0pt)--(-7pt,-2pt) & cycle;\n", gpoutfile);
}


TERM_PUBLIC void
MF_graphics()
{
    register struct termentry *t = term;

    fprintf(gpoutfile, "\n\nbeginchar(%d,%gin#,%gin#,0);\n",
	    MF_char_code, MF_xsize, MF_ysize);
    MF_char_code++;
    fprintf(gpoutfile, "a:=w/%d;b:=h/%d;\n", t->xmax, t->ymax);
    MF_picked_up_pen = 0;
}


TERM_PUBLIC void
MF_text()
{
    fputs("endchar;\n", gpoutfile);
}


TERM_PUBLIC int
MF_justify_text(mode)
enum JUSTIFY mode;
{
    MF_justify = mode;
    return TRUE;
}


TERM_PUBLIC int
MF_text_angle(ang)
int ang;
{
    if (ang > 0)
	MF_ang = 90;
    else
	MF_ang = 0;
    return TRUE;
}


TERM_PUBLIC void
MF_linetype(linetype)
int linetype;
{
    if (linetype >= 8)
	linetype %= 8;
    linetype += 2;
    /* Only output change in pens if it actually affects the pen used */
    if ((MF_lines[linetype].thickness != MF_lines[MF_line_type].thickness) ||
	(!MF_picked_up_pen)) {
	fprintf(gpoutfile, "pickup pencircle scaled %gth;\n",
		MF_lines[linetype].thickness);
	MF_picked_up_pen = 1;
    }
    MF_line_type = linetype;
    MF_dash_index = 0;
    MF_dist_left = MF_lines[MF_line_type].dashlen[MF_dash_index];
    MF_is_solid = MF_lines[MF_line_type].solid;
}


TERM_PUBLIC void
MF_move(x, y)
unsigned int x, y;
{
    MF_last_x = x;
    MF_last_y = y;
    MF_dash_index = 0;
    MF_dist_left = MF_lines[MF_line_type].dashlen[MF_dash_index];
}


TERM_PUBLIC void
MF_vector(x, y)
unsigned int x, y;
{
    if (MF_is_solid) {
	if (x == MF_last_x && y == MF_last_y)
	    fprintf(gpoutfile, "drawdot (%da,%db);\n", x, y);
	else
	    fprintf(gpoutfile, "draw (%da,%db)--(%da,%db);\n",
		    MF_last_x, MF_last_y, x, y);
    } else {
	double dist_to_go, delta_x, delta_y, inc_x, inc_y;
	double last_x_d, last_y_d, next_x_d, next_y_d;
	unsigned int next_x, next_y;

	if (x == MF_last_x && y == MF_last_y) {
	    if (!(MF_dash_index & 1))
		fprintf(gpoutfile, "drawdot (%da,%db);\n", x, y);
	} else {
	    last_x_d = MF_last_x;
	    last_y_d = MF_last_y;
	    delta_x = x - last_x_d;
	    delta_y = y - last_y_d;
	    dist_to_go = sqrt(delta_x * delta_x + delta_y * delta_y);
	    inc_x = delta_x / dist_to_go;
	    inc_y = delta_y / dist_to_go;
	    while (MF_dist_left < dist_to_go) {
		next_x_d = last_x_d + inc_x * MF_dist_left;
		next_y_d = last_y_d + inc_y * MF_dist_left;
		next_x = floor(next_x_d + 0.5);
		next_y = floor(next_y_d + 0.5);
		/* MF_dash_index & 1 == 0 means: draw a line; otherwise just move */
		if (!(MF_dash_index & 1))
		    fprintf(gpoutfile, "draw (%da,%db)--(%da,%db);\n",
			    MF_last_x, MF_last_y, next_x, next_y);
		MF_last_x = next_x;
		MF_last_y = next_y;
		last_x_d = next_x_d;
		last_y_d = next_y_d;
		dist_to_go -= MF_dist_left;
		MF_dash_index = (MF_dash_index + 1) & 3;
		MF_dist_left = MF_lines[MF_line_type].dashlen[MF_dash_index];
	    }
	    delta_x = x - last_x_d;
	    delta_y = y - last_y_d;
	    MF_dist_left -= sqrt(delta_x * delta_x + delta_y * delta_y);
	    if (!(MF_dash_index & 1)) {
		if (x == MF_last_x && y == MF_last_y)
		    fprintf(gpoutfile, "drawdot (%da,%db);\n", x, y);
		else
		    fprintf(gpoutfile, "draw (%da,%db)--(%da,%db);\n",
			    MF_last_x, MF_last_y, x, y);
	    }
	}
    }
    MF_last_x = x;
    MF_last_y = y;
}


TERM_PUBLIC void
MF_arrow(sx, sy, ex, ey, head)
unsigned int sx, sy, ex, ey;
TBOOLEAN head;
{
    int delta_x, delta_y;

    MF_move(sx, sy);
    MF_vector(ex, ey);
    if (head) {
	delta_x = ex - sx;
	delta_y = ey - sy;
	fprintf(gpoutfile, "fill arrowhead rotated angle(%d,%d) shifted (%da,%db);\n",
		delta_x, delta_y, ex, ey);
    }
}


TERM_PUBLIC void
MF_put_text(x, y, str)
unsigned int x, y;
const char *str;
{
    int i, j = 0;
    char *text;

    /* ignore empty strings */
    if (!str || !*str)
	return;

    /* F***. why do drivers need to modify string args? */
    text = gp_strdup(str);

    for (i = 0; i < strlen(text); i++)
	if (text[i] == '"')
	    text[i] = '\'';	/* Replace " with ' */
    switch (MF_justify) {
    case LEFT:
	j = 1;
	break;
    case CENTRE:
	j = 2;
	break;
    case RIGHT:
	j = 3;
	break;
    }
    fprintf(gpoutfile, "put_text(\"%s\",%da,%db,%d,%d);\n",
	    text, x, y, MF_ang, j);
    free(text);
}


TERM_PUBLIC void
MF_reset()
{
    fputs("end.\n", gpoutfile);
}


#endif /* TERM_BODY */

#ifdef TERM_TABLE

TERM_TABLE_START(mf_driver)
    "mf", "Metafont plotting standard",
    MF_XMAX, MF_YMAX, MF_VCHAR, MF_HCHAR,
    MF_VTIC, MF_HTIC, options_null, MF_init, MF_reset,
    MF_text, null_scale, MF_graphics, MF_move, MF_vector,
    MF_linetype, MF_put_text, MF_text_angle,
    MF_justify_text, line_and_point, MF_arrow, set_font_null
TERM_TABLE_END(mf_driver)

#undef LAST_TERM
#define LAST_TERM mf_driver

#endif /* TERM_TABLE */
#endif /* TERM_PROTO_ONLY */


#ifdef TERM_HELP
START_HELP(mf)
"1 mf",
"?commands set terminal mf",
"?set terminal mf",
"?set term mf",
"?terminal mf",
"?term mf",
"?mf",
"?metafont",
" The `mf` terminal driver creates an input file to the METAFONT program.  Thus a",
" figure may be used in the TeX document in the same way as is a character.",
"",
" To use a picture in a document, the METAFONT program must be run with the",
" output file from `gnuplot` as input.  Thus, the user needs a basic knowledge",
" of the font creating process and the procedure for including a new font in a",
" document.  However, if the METAFONT program is set up properly at the local",
" site, an unexperienced user could perform the operation without much trouble.",
"",
" The text support is based on a METAFONT character set.  Currently the",
" Computer Modern Roman font set is input, but the user is in principal free to",
" choose whatever fonts he or she needs.  The METAFONT source files for the",
" chosen font must be available.  Each character is stored in a separate",
" picture variable in METAFONT.  These variables may be manipulated (rotated,",
" scaled etc.) when characters are needed.  The drawback is the interpretation",
" time in the METAFONT program.  On some machines (i.e. PC) the limited amount",
" of memory available may also cause problems if too many pictures are stored.",
"",
" The `mf` terminal has no options.",
"2 METAFONT Instructions",
"?commands set terminal mf detailed",
"?set terminal mf detailed",
"?set term mf detailed",
"?mf detailed",
"?metafont detailed",
"",
" - Set your terminal to METAFONT:",
"   set terminal mf",
" - Select an output-file, e.g.:",
"   set output \"myfigures.mf\"",
" - Create your pictures. Each picture will generate a separate character. Its",
" default size will be 5*3 inches. You can change the size by saying `set size",
" 0.5,0.5` or whatever fraction of the default size you want to have.",
"",
" - Quit `gnuplot`.",
"",
" - Generate a TFM and GF file by running METAFONT on the output of `gnuplot`.",
" Since the picture is quite large (5*3 in), you will have to use a version of",
" METAFONT that has a value of at least 150000 for memmax.  On Unix systems",
" these are conventionally installed under the name bigmf.  For the following",
" assume that the command virmf stands for a big version of METAFONT.  For",
" example:",
"",
" - Invoke METAFONT:",
"     virmf '&plain'",
" - Select the output device: At the METAFONT prompt ('*') type:",
"     \\mode:=CanonCX;     % or whatever printer you use",
" - Optionally select a magnification:",
"     mag:=1;             % or whatever you wish",
" - Input the `gnuplot`-file:",
"     input myfigures.mf",
" On a typical Unix machine there will usually be a script called \"mf\" that",
" executes virmf '&plain', so you probably can substitute mf for virmf &plain.",
" This will generate two files: mfput.tfm and mfput.$$$gf (where $$$ indicates",
" the resolution of your device).  The above can be conveniently achieved by",
" typing everything on the command line, e.g.:",
" virmf '&plain' '\\mode:=CanonCX; mag:=1; input myfigures.mf'",
" In this case the output files will be named myfigures.tfm and",
" myfigures.300gf.",
"",
" - Generate a PK file from the GF file using gftopk:",
"   gftopk myfigures.300gf myfigures.300pk",
" The name of the output file for gftopk depends on the DVI driver you use.",
" Ask your local TeX administrator about the naming conventions.  Next, either",
" install the TFM and PK files in the appropriate directories, or set your",
" environment variables properly.  Usually this involves setting TEXFONTS to",
" include the current directory and doing the same thing for the environment",
" variable that your DVI driver uses (no standard name here...).  This step is",
" necessary so that TeX will find the font metric file and your DVI driver will",
" find the PK file.",
"",
" - To include your pictures in your document you have to tell TeX the font:",
"   \\font\\gnufigs=myfigures",
" Each picture you made is stored in a single character.  The first picture is",
" character 0, the second is character 1, and so on...  After doing the above",
" step, you can use the pictures just like any other characters.  Therefore, to",
" place pictures 1 and 2 centered in your document, all you have to do is:",
"   \\centerline{\\gnufigs\\char0}",
"   \\centerline{\\gnufigs\\char1}",
" in plain TeX.  For LaTeX you can, of course, use the picture environment and",
" place the picture wherever you wish by using the \\makebox and \\put macros.",
"",
" This conversion saves you a lot of time once you have generated the font;",
" TeX handles the pictures as characters and uses minimal time to place them,",
" and the documents you make change more often than the pictures do.  It also",
" saves a lot of TeX memory.  One last advantage of using the METAFONT driver",
" is that the DVI file really remains device independent, because no \\special",
" commands are used as in the eepic and tpic drivers."
END_HELP(mf)
#endif /* TERM_HELP */