diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/dvgtk/dvgt.c |
Initial commit
Diffstat (limited to 'dviware/dvgtk/dvgt.c')
-rw-r--r-- | dviware/dvgtk/dvgt.c | 3595 |
1 files changed, 3595 insertions, 0 deletions
diff --git a/dviware/dvgtk/dvgt.c b/dviware/dvgtk/dvgt.c new file mode 100644 index 0000000000..2c5b6dd3d9 --- /dev/null +++ b/dviware/dvgtk/dvgt.c @@ -0,0 +1,3595 @@ +/* dvgtk.c - main dvgtk functions + $Id: dvgt.c,v 0.2 1997/03/28 03:15:36 tjchol01 Exp $ + Authors: Andrew Trevorrow, Ian Dall, Geoffrey Tobin, Tomasz J. Cholewo + */ + +#include "dvgt.h" + +#include <kpathsea/c-ctype.h> +#include <kpathsea/proginit.h> +#include <kpathsea/tex-file.h> + +#include "defaults.h" +#include "screenio.h" +#include "options.h" +#include "unixio.h" +#include "vdu.h" +#include "dvireader.h" +#include "fontreader.h" + +/* Variables Exported, via vdu.h */ + +int DVIstatusl, windowstatusl, messagel, commandl, bottoml; +int windowh, windowv, windowwd, windowht; + + +static void DisplayPage (); +static void NextCommandLine (); +static boolean ShowCmdHelp (); + + +/*---------------------------------------------------------------------- + DECLARATIONS FOR PROCESSING USER COMMANDS + + Most commands consist of one or two characters and can be entered in + upper or lowercase. Multiple commands are processed in the order + given but we only update the window, if necessary, at the end. If a + bad command is encountered, any further commands are ignored. Some + commands can have parameters; they are all dimensions in terms of the + current units. Spaces before and after commands and parameters are + ignored. +*/ + +/* Possible commands are: */ +/* i a positive integer; display ith DVI page */ + +#define TeXpage "[" + /* start of a TeX page specification: [i0. ... .i9] */ +#define NextPage "N" + /* display next DVI page, depending on direction */ +#define Window "W" + /* move window's top left corner to given position */ +#define Up "U" + /* move window up a given amount */ +#define Down "D" + /* move window down a given amount */ +#define Left "L" + /* move window left a given amount */ +#define Right "R" + /* move window right a given amount */ +#define Hsize "H" + /* set scaledwd: window's horizontal size */ +#define Vsize "V" + /* set scaledht: window's vertical size */ +#define AutoView "A" + /* enable/disable automatic view after page selection */ +#define ZoomInOut "Z" + /* halve/double window dimensions */ +#define Terse "T" + /* display quick and nasty chars at reference points */ +#define Box "B" + /* display box outlines of glyphs */ +#define Full "F" + /* display all pixels in glyphs */ +#define Ic "I" + /* get/show dimensions in inches */ +#define Cm "C" + /* get/show dimensions in centimetres */ +#define Mm "M" + /* get/show dimensions in millimetres */ +#define PcPtPx "P" + /* get/show dimensions in picas/points/pixels */ +#define Help "?" + /* display help on available commands */ +#define Show "S" + /* display useful statistics */ +#define Quit "Q" + /* have a guess */ + +#define commprompt "Command: " + + +static String commstring; /* holds user responses */ +static int commpos; /* current position in commstring */ +static int commlen; /* length of commstring */ +static char command; /* starting character of command */ +static int maxpix; +/* maximum absolute pixel value; depends on resolution */ +/* These flags are used to handle multiple commands: */ +static boolean screenjustcleared; + /* has screen just been cleared? */ +static boolean paintDVIStatus; /* does DVI status line need updating? */ +static boolean paintWindowStatus; + /* does window status line need updating? */ +static boolean paintwindow; /* does window region need updating? */ +static boolean pageoffpaper; /* is page off paper? */ +static boolean badcommand; /* was there a bad command? */ + + +/*---------------------------------------------------------------------- + DECLARATIONS FOR DISPLAYING A PAGE + + The reference points of characters and rules on a page are stored as + pairs of horizontal and vertical paper pixel coordinates. + The paper coordinate scheme is described in detail in DVIReader. + The screen coordinate scheme is described in detail in VDU. + To update the window region, DVItoVDU maps visible paper pixels + to screen pixels using windowh and windowv to help with translation, + and windowwd and windowht to help with scaling. + What the user sees depends on the current displaymode, the current size + of the window region (scaledwd by scaledht are in paper pixels and determine + the horizontal and vertical scaling factors), and the current paper position + of the window region's top left corner; i.e., (windowleft,windowtop). + + A NOTE ON THE SCALING METHOD USED BY DVItoVDU: + We desire the following conditions when scaling paper pixels to + screen pixels: + 1. Rules/glyphs having the same top/bottom/left/right paper coordinates also + have the same screen coordinates (e.g., to ensure baselines line up). + This condition is incompatible with a rule/glyph staying the same + width and height as the window position changes! Too bad. + 2. After being scaled, visible pixel positions must not exceed the + window region's edges. In our case, only the bottom and right edges are + a problem because scaling starts at the top left corner of the window. + We use two different scaling calculations depending on + whether the h/vscalefactors are < 1.0 or not. + 3. Scaled heights and widths must be > 0 even when h/vscalefactors + approach 0. If h/vscalefactors are > 1.0 then the width/height of + paper pixels increase accordingly. +*/ + +/* terse - show quick and nasty chars at ref pts */ +/* box - show box outlines of glyphs */ +/* full - show all pixels in glyphs */ + +#define tersemode 1 +#define boxmode 2 +#define fullmode 4 +int displaymode; + +/* we get/show dimensions in currentunits */ +units currentunits; + +static int papertop, paperleft, paperbottom, paperright; + /* these define the edges of the paper */ + +static int windowtop, windowleft, windowbottom, windowright; + /* these define the current window edges */ + +static boolean allpagevisible; /* is all of page visible in window? */ +static boolean outsidepage; /* is entire window outside page? */ +static int scaledht; /* current window height in paper pixels */ +static int scaledwd; /* current window width in paper pixels */ +static double zoomfactor; /* current zoom factor - default is 2 */ +static double vscalefactor; /* windowht / scaledht */ +static double hscalefactor; /* windowwd / scaledwd */ +static ruleinfo *thisruleinfo; /* current rule info in rulelist */ +static fontinfo *unusedfont; /* first unused font in sorted fontlist */ +static fontinfo *thisfontinfo; /* current font info in sorted fontlist */ +static charinfo *thischarinfo; /* current char info in charlist */ +static int thischar; /* current index into current chartable */ +static boolean fontopen; /* is thisfontinfo->fontspec open? */ +static boolean useraborted; /* did user abort page display? */ +static boolean autoviewing; /* automatic window view enabled? */ + +static char *immed_help_strings[] = +{ + DVGT_VERSION, + "dvgt interactively views TeX-generated DVI files on some common VDUs.", + "This is the precompiled help shown if no arguments are given.", + "The options help file, `options.txt', gives more information.", + "USAGE:", + " dvgt filename[.dvi]", + " [-H x_offset] [-V y_offset] [-d dummy_pk] [-e dummy_tfm]", + " [-i] [-k kpathsea_debug_mode] [-l]", + " [-m magnification] [-r xres[,yres]]", + " [-t tfm_directory] [-x paperwd] [-y paperht]", + "", + "-H horizontal_offset (default = 0.0 in)", + "-V vertical_offset (default = 0.0 in)", + "-l (set landscape mode, instead of the default portrait mode)", + "-m magnification (default = DVI file's intrinsic magnification)", + "", + "The other parameters' default values are system dependent.", + NULL +}; + +static char *cmd_help_strings[] = +{ + "MISCELLANEOUS CHANGING PAGE DISPLAY (TOGGLES)", + "? Display this command help info T Terse - fast but inaccurate", + "S Show options, fonts and page info B Bounding Box of glyphs", + "A Toggle Auto window F Full - accurate but slow", + "^L Refresh screen", + "Q Quit from dvgt CHANGING UNITS OF DIMENSIONS", + " C u Where u is one of the units", + "SELECTING A PAGE BP, CM, IN, MM, PC, PT, PX, or SP", + "[i0. ... .i9] Select TeX page (big point, cm, inch, mm, pica,", +"i Select the ith DVI page point, paper pixel, or scaled point)", + "N Next DVI page", + "P Previous DVI page MOVING THE WINDOW", + " W h,v Move Window to given posn", +"CHANGING THE SIZE OF THE WINDOW or to (minh,minv) if no h,v ", + "H x Set Horiz. window size to x U v Move window Up by v", +" or to unscaled width if no x or by half current ht if no v", + "V y Set Vertical window size to y D v Move window Down by v", + " or to unscaled height if no y or by half cur. ht if no v", + "ZI z Zoom In by factor z L h Move window Left by h", +" or by current factor if no z or by half cur. width if no h", + "ZO z Zoom Out, as for ZI R h Move window Right by h", +"ZCI z, ZCO z Zoom wrt window center or by half cur. width if no h", + NULL +}; + +/*--------------------------------------------------------------------*/ + +static void +ClearMessageLine () +{ + /* Clear message line and move cursor to start of line. + We don't show any message here; that will usually be done + immediately after calling this routine. + */ + ClearTextLine (messagel); + MoveToTextLine (messagel); +} +/* ClearMessageLine */ +/*--------------------------------------------------------------------*/ +static void +WaitForReturn () +{ + /* DVItoVDU has just displayed an important message. + To ensure message is seen we wait for user to hit Return. + */ + + char ch; + + MesgFlush (); + do + { + ReadChar (&ch); + } + while (ch != CR); +} +/* WaitForReturn */ +/*--------------------------------------------------------------------*/ +static boolean +PrintText (char **text) +{ + int retval = false; + + if (text == (char **) NULL) + { + retval = false; /* No text to print. */ + } + else + { + /* Print each text string */ + for (; *text != (char *) NULL; text++) + { + printf ("%s", *text); + /* If there's no newline in *text, print one. */ + if (strchr (*text, '\n') == (char *) NULL) + printf ("\n"); + } + retval = true; + } + return retval; +} +/* PrintText */ +/*--------------------------------------------------------------------*/ +static boolean +ShowText (char **text) +{ + /* Show text of consecutive lines, in a way something like UNIX more. */ + int lines; + char answer; + + if (text == (char **) NULL) + { + ClearMessageLine (); + MesgString ("NULL text!"); + return false; + } + ClearScreen (); + screenjustcleared = true; + MoveToTextLine (1); + rawoutoff (); + + for (lines = 0; true; lines++) + { + if (text[lines] == (char *) NULL) + { /* end of text */ + ClearTextLine (bottoml); + MoveToTextLine (bottoml); + MesgString ("End of text,"); + MesgString (" hit RETURN key to resume page display: "); + MesgFlush (); + do + { + ReadChar (&answer); + } + while (answer != CR); + break; /* EXIT point 1 of 2 from enclosing (for) loop */ + } + if (lines >= bottoml - 2) + { + /* blank line before prompt */ + ClearTextLine (bottoml); + MoveToTextLine (bottoml); + + /* prompt */ + MesgString ("Hit RETURN key to resume page display,"); + MesgString (" or any other key for more text: "); + MesgFlush (); + + ReadChar (&answer); + if (answer == CR) + break; /* EXIT point 2 of 2 from enclosing (for) loop */ + + ClearScreen (); + screenjustcleared = true; + MoveToTextLine (1); + lines = 0; /* reset line count */ + } + MesgString (text[lines]); + MesgLine (); + } + + rawouton (); + ClearScreen (); + screenjustcleared = true; + paintDVIStatus = true; + paintWindowStatus = true; + if (currDVIpage != 0) + paintwindow = true; + + return true; +} +/* ShowText */ + +/*--------------------------------------------------------------------*/ +static boolean +ShowCmdHelp () +{ + /* Help information is displayed in lines 1 to bottoml-2. + We assume that bottoml is at least 3 and that VDU screen is at least + maxline characters wide. + */ + return ShowText (cmd_help_strings); +} +/* ShowCmdHelp */ +/*--------------------------------------------------------------------*/ +void +PadMesg () +{ + /* Pad current text line with spaces. */ + String padding; + int i; + for (i = 0; i < maxstring; i++) + padding[i] = ' '; + padding[maxstring] = '\0'; + MesgString (padding); +} +/* Pad Mesg */ +/*--------------------------------------------------------------------*/ +static void +InitWinVars () +{ + /* TeX will not generate dimensions > than about 38 feet, so we + choose an absolute limit on our dimensions to be 40 feet. + */ + + int ymaxpix = yres * (40 * 12); /* 40 ft = 40 * 12 in */ + maxpix = xres * (40 * 12); + if (ymaxpix < maxpix) + maxpix = ymaxpix; + + /* top left corner of paper is fixed at (-1",-1") */ + papertop = -yres; + paperleft = -xres; + + paperbottom = papertop + paperht - 1; + paperright = paperleft + paperwd - 1; + + /* User sees the following status values before requesting the first page. */ + + /* + FIRST STATUS LINE: # pages, DVI page, TeX page, Auto, Displaymode, Zoom. + Note that DVIReader has already counted the number of pages, + and initialized currDVIpage and currTeXpage. + */ + + autoviewing = true; /* start off enabled */ + displaymode = fullmode; /* Full mode, neither Box nor Terse mode */ + zoomfactor = 2.0; /* default factor for zooming is 2 */ + + /* + SECOND STATUS LINE: Window loc. & dims, Page loc. & dims, Unit. + */ + + windowleft = 0; /* window location */ + windowtop = 0; + scaledwd = windowwd; /* window size is initially unscaled */ + scaledht = windowht; + + minhp = 0; + minvp = 0; /* page location */ + maxhp = 0; + maxvp = 0; + hscalefactor = 1.0; /* page size is initially unscaled, too */ + vscalefactor = 1.0; + + currentunits = ic; /* inch */ + + /* WINDOW: Chars & Rules. */ + + pageempty = true; /* no page read yet, so no chars and no rules! */ +} +/* InitWinVars */ + +/*--------------------------------------------------------------------*/ +static void +UpdateDVIStatusLine () +{ + /* Show totalpages, currDVIpage, currTeXpage, direction and displaymode. */ + + int i, lastnonzero; + + ClearTextLine (DVIstatusl); + MoveToTextLine (DVIstatusl); + + MesgInt (totalpages); + MesgString (" pages"); + + MesgString (" DVI page="); + MesgInt (currDVIpage); + + MesgString (" TeX page="); + + MesgChar ('['); + + lastnonzero = 9; + while (lastnonzero > 0 && currTeXpage[lastnonzero] == 0) + { + lastnonzero--; /* find last counter with non-zero value */ + } + /* always show \count0 but don't show trailing 0 counters */ + for (i = 0; i <= lastnonzero; i++) + { + MesgInt (currTeXpage[i]); + + if (i != lastnonzero) + MesgChar ('.'); + } + MesgChar (']'); + + MesgString (" Auto="); + if (autoviewing) + MesgChar ('+'); + else + MesgChar ('-'); + + MesgString (" "); + if (displaymode & tersemode) + MesgString ("T"); + else + MesgString (" "); + + if (displaymode & boxmode) + MesgString ("B"); + else + MesgString (" "); + + if (displaymode & fullmode) + MesgString ("F"); + else + MesgString (" "); + + { + String msgstring; + /* gt - this is surely safe from overflow of msgstring - yes? */ + sprintf (msgstring, " Zoom=%.2f", zoomfactor); + MesgString (msgstring); + } + + MesgLine (); +} +/* UpdateDVIStatusLine */ + + +/*--------------------------------------------------------------------*/ +static void +WriteDimension (double precision, double res, int pixels) +{ + /* Show the given pixel dimension in terms of currentunits. */ + + double realdim = 0.0; + int fracpart; + + if (currentunits == px) + MesgInt (pixels); + else + { + switch (currentunits) + { + case ic: + realdim = (double) pixels / res; + break; + case cm: + realdim = (double) pixels / res * 2.54; + break; + case mm: + realdim = (double) pixels / res * 25.4; + break; + case bp: + realdim = (double) pixels / res * 72.0; + break; + case pc: + realdim = (double) pixels / res * 72.27 / 12.0; + break; + case pt: + realdim = (double) pixels / res * 72.27; + break; + case sp: + realdim = (double) pixels / res * 72.27 * 65536.0; + break; + case px: + return; /* can't happen */ + } /* currentunits */ + /* gt - This seems immensely complicated, for what it's doing! */ + /* show realdim to the specified precision */ + if (fabs (realdim) < 0.5 * precision) + MesgString ("0.0"); + else + { + if (realdim < 0.0) + { + MesgChar ('-'); + realdim = fabs (realdim); + } + realdim += 0.5 * precision; /* round up to specified precision */ + MesgInt ((int) realdim); /* whole part */ + MesgChar ('.'); + fracpart = (int) ((realdim - (int) realdim) / precision); + MesgInt (fracpart); + } /* if */ + } /* if */ +} + +/* WriteDimension */ + +/*--------------------------------------------------------------------*/ +static void +WriteXDim (double precision, int pixels) +{ + WriteDimension (precision, xres, pixels); +} +/* Write XDim */ +/*--------------------------------------------------------------------*/ +static void +WriteYDim (double precision, int pixels) +{ + WriteDimension (precision, yres, pixels); +} +/* Write YDim */ +/*--------------------------------------------------------------------*/ +static void +UpdateWindowStatusLine () +{ + /* Show current window location and size, page location and size, and units. */ + + double precision = 0.1; /* precision for status line */ + + ClearTextLine (windowstatusl); + MoveToTextLine (windowstatusl); + + MesgString ("Window at ("); + WriteXDim (precision, windowleft); + MesgChar (','); + WriteYDim (precision, windowtop); + MesgString (") "); + WriteXDim (precision, scaledwd); + MesgString (" by "); + WriteYDim (precision, scaledht); + + MesgString (" Page at ("); + WriteXDim (precision, minhp); + MesgChar (','); + WriteYDim (precision, minvp); + MesgString (") "); + WriteXDim (precision, maxhp - minhp + 1); + MesgString (" by "); + WriteYDim (precision, maxvp - minvp + 1); + MesgString (" "); + + switch (currentunits) + { + case ic: + MesgString ("IN"); + break; + case cm: + MesgString ("CM"); + break; + case mm: + MesgString ("MM"); + break; + case bp: + MesgString ("BP"); + break; + case pc: + MesgString ("PC"); + break; + case pt: + MesgString ("PT"); + break; + case sp: + MesgString ("SP"); + break; + case px: + MesgString ("PX"); + break; + } /* currentunits */ + MesgLine (); +} +/* UpdateWindowStatusLine */ +/*--------------------------------------------------------------------*/ +#define limit 2147483647 +/* TeX's limit = 2^31 - 1. + Should also be >= maxpix. + Note that this also defines the range of + page numbers the user can ask for! */ +#define threshold (limit / 10) /* nearing overflow */ + + +static boolean +GetInteger (char *str_in, int slen, int *pos, int *n) +{ + /* Extract an integer from given str_in starting at given pos. + pos is also used to return the position after the integer. + If no integer is found, then set n to 0 and return FALSE + (in that case, pos will only change if leading spaces were skipped). + If ABS(n) > limit then set n to sign * limit. + Valid syntax is +{digit} or -{digit} or digit{digit}. + Note that a + or - by itself is valid and sets n to 0. + */ + + String str; + + int absval, last, sign; + boolean inttoobig; + boolean intfound = false; + + memcpy (str, str_in, sizeof (String)); + + while (*pos < slen && str[*pos] == ' ') + { /* skip any leading spaces */ + (*pos)++; + } + + absval = 0; + sign = 1; + last = *pos; + inttoobig = false; + + if (*pos < slen) + { + if (str[*pos] == '-') + { + sign = -1; + last++; + } + else + { + if (str[*pos] == '+') + last++; + } + + while (last < slen && (str[last] >= '0' && str[last] <= '9')) + { + if (absval > threshold || (absval == threshold && str[last] > '7')) + inttoobig = true; + else + absval = absval * 10 + str[last] - '0'; + last++; + } + } + if (*pos == last) + { + *n = 0; + intfound = false; + } + else + { + *pos = last; + if (inttoobig) + absval = limit; + *n = sign * absval; + intfound = true; + } + + return intfound; +} /* GetInteger */ + +#undef limit +#undef threshold +/*--------------------------------------------------------------------*/ +static boolean +GetReal (char *str_in, int slen, int *pos, double *r) +{ + /* Extract a real number r from given str_in starting at given pos. + pos is also used to return the position after the real number. + If no number is found, then set r to 0.0 and return FALSE + (in that case pos will only change if leading spaces were skipped). + Valid syntax of a real number is integer[.{digit}] or .{digit} + where an integer is defined by GetInteger. + Real numbers are truncated to 4 decimal places. + Note that a sign or decimal point by itself is valid and sets r to 0.0. + */ + + String str; + int sign, intpart, fracpart, divisor; + double absreal; + + memcpy (str, str_in, strlen (str_in) + 1); + + /* GetInteger does not remember a sign by itself, so we need to check + for -ve dimensions like -.5 first. + */ + + while (*pos < slen && str[*pos] == ' ') + { /* skip any spaces */ + (*pos)++; + } + + sign = 1; + if (*pos < slen) + { + if (str[*pos] == '-') + sign = -1; + } + if (!GetInteger (str, slen, pos, &intpart)) + { + if (*pos == slen || str[*pos] != '.') + { + *r = 0.0; + return false; + } + } + /* real number is valid; + if no integer part, then intpart will be 0; + sign = +|-1 + */ + if (*pos == slen || str[*pos] != '.') + { + /* no fractional part */ + absreal = (double) abs (intpart); + } + else + { + /* extract fractional part */ + (*pos)++; /* skip over decimal point */ + divisor = 1; + fracpart = 0; + while (*pos < slen && (str[*pos] >= '0' && str[*pos] <= '9')) + { + /* only consider up to 4 decimal places */ + if (divisor < 10000) + { + divisor *= 10; + fracpart = fracpart * 10 + str[*pos] - '0'; + } + (*pos)++; + } + absreal = (double) abs (intpart) + (double) fracpart / divisor; + } + + *r = sign * absreal; + + return true; +} /* GetReal */ + + +/*--------------------------------------------------------------------*/ + +static boolean +GetDimension (double res, char *str_in, int slen, int *pos, int *n) +{ + /* Extract a dimension from given str_ starting at given pos. + n returns the corresponding number of pixels in the dimension + (which is an integer or real value in terms of currentunits); + pos is also used to return the position after the dimension. + If no dimension is found then set n to 0 and return FALSE (pos will only + change if leading spaces were skipped). + If ABS(n) > maxpix then set n to sign * maxpix. + Valid syntax of a dimension is integer[.{digit}] or .{digit} where + an integer is defined by GetInteger. + Real dimensions are truncated to 4 decimal places. + Note that a sign or decimal point by itself is valid and sets n to 0. + */ + + String str; + int sign, intdim, fracpart, divisor; + double absrealdim; + boolean intpresent, dimtoobig; + + memcpy (str, str_in, sizeof (String)); + + /* GetInteger does not remember a sign by itself, so we need to check + for -ve dimensions like -.5 first. + */ + + while (*pos < slen && str[*pos] == ' ') + { /* skip any leading spaces */ + (*pos)++; + } + + sign = 1; + if (*pos < slen) + { + if (str[*pos] == '-') + sign = -1; + } + intpresent = GetInteger (str, slen, pos, &intdim); + if (!intpresent) + { + if (*pos == slen || str[*pos] != '.') + { + *n = 0; + return false; + } + } /* if */ + /* dimension is valid; if no integer part then intdim will be 0; sign = +|-1 */ + if (*pos == slen || str[*pos] != '.') + { + /* no fractional part */ + absrealdim = abs (intdim); + } + else + { + /* extract fractional part */ + (*pos)++; /* skip over decimal point */ + divisor = 1; + fracpart = 0; + while (*pos < slen && (str[*pos] >= '0' && str[*pos] <= '9')) + { + /* only consider up to 4 decimal places */ + if (divisor < 10000) + { + divisor *= 10; + fracpart = fracpart * 10 + str[*pos] - '0'; + } + (*pos)++; + } /* while */ + absrealdim = abs (intdim) + (double) fracpart / divisor; + } /* if */ + + /* calculate n based on absrealdim, sign and currentunits */ + dimtoobig = false; + switch (currentunits) + { + case ic: + if (absrealdim > (double) maxpix / res) + dimtoobig = true; + else + *n = sign * (int) (absrealdim * res + 0.5); + break; + case cm: + if (absrealdim > (double) maxpix / res * 2.54) + dimtoobig = true; + else + *n = sign * (int) (absrealdim / 2.54 * res + 0.5); + break; + case mm: + if (absrealdim > (double) maxpix / res * 25.4) + dimtoobig = true; + else + *n = sign * (int) (absrealdim / 25.4 * res + 0.5); + break; + case bp: + if (absrealdim > (double) maxpix / res * 72.0) + dimtoobig = true; + else + *n = sign * (int) (absrealdim / 72.0 * res + 0.5); + break; + case pc: + if (absrealdim > (double) maxpix / res * (72.27 / 12.0)) + dimtoobig = true; + else + *n = sign * (int) (absrealdim / 72.27 * 12.0 * res + 0.5); + break; + case pt: + if (absrealdim > (double) maxpix / res * 72.27) + dimtoobig = true; + else + *n = sign * (int) (absrealdim / 72.27 * res + 0.5); + break; + case sp: + if (absrealdim > (double) maxpix / res * 72.27 * 65536.0) + dimtoobig = true; + else + *n = sign * (int) (absrealdim / (72.27 * 65536.0) * res + 0.5); + break; + case px: + if (absrealdim > maxpix) + dimtoobig = true; + else + *n = sign * (int) (absrealdim + 0.5); + break; + } /* currentunits */ + + if (dimtoobig) + *n = sign * maxpix; + + return true; +} +/* GetDimension */ + +/*--------------------------------------------------------------------*/ + +static boolean +GetXDim (char *str_in, int slen, int *pos, int *n) +{ + return GetDimension (xres, str_in, slen, pos, n); +} +/* GetXDim */ + +/*--------------------------------------------------------------------*/ + +static boolean +GetYDim (char *str_in, int slen, int *pos, int *n) +{ + return GetDimension (yres, str_in, slen, pos, n); +} +/* GetYDim */ + +/*--------------------------------------------------------------------*/ + +static void +BadCommandMessage () +{ + /* A bad command has just been detected and some sort of message displayed. + Note that commpos is pointing to just after the problem character. + If there are further commands then we show user what will be ignored. + */ + + int i; + + badcommand = true; + + ClearTextLine (commandl); + MoveToTextLine (commandl); + + MesgString (commprompt); + for (i = 0; i < commpos; i++) + MesgChar (commstring[i]); + MesgChar ('!'); /* put ! after the problem character */ + if (commpos < commlen) + { + MesgString (" Ignoring: "); + for (i = commpos; i < commlen; i++) + MesgChar (commstring[i]); + } + NextCommandLine (); +} +/* BadCommandMessage */ + +/*--------------------------------------------------------------------*/ + +static void +NewLocation (int newhp, int newvp) +{ + /* Change window location to given position and update window edges. + If entire window moves outside non-empty page rectangle then outsidepage + becomes TRUE and we restrict movement to just beyond the edge(s) so that + user can easily move window (via Up,Down,Left,Right) to positions + in which one or more window and page edges coincide. + Note that allpagevisible is also updated. + */ + + outsidepage = false; + if (currDVIpage != 0 && !pageempty) + { + /* check if new position puts window entirely outside edges; + if so then minimize the movement needed to keep this true */ + + if (newvp > maxvp) + { + outsidepage = true; + newvp = maxvp + 1; + } + else if (newvp <= minvp - scaledht) + { + outsidepage = true; + newvp = minvp - scaledht; + } + if (newhp > maxhp) + { + outsidepage = true; + newhp = maxhp + 1; + } + else if (newhp <= minhp - scaledwd) + { + outsidepage = true; + newhp = minhp - scaledwd; + } + } + windowtop = newvp; + windowleft = newhp; + windowbottom = windowtop + scaledht - 1; + windowright = windowleft + scaledwd - 1; + allpagevisible = (currDVIpage != 0 && !pageempty + && (minvp >= windowtop && maxvp <= windowbottom) + && (minhp >= windowleft && maxhp <= windowright)); + + /* even if pageempty or window hasn't moved we must still call DisplayPage */ + + if (currDVIpage != 0) + paintwindow = true; +} +/* NewLocation */ + +/*--------------------------------------------------------------------*/ + +static void +WindowMove () +{ + /* Syntax of Window command is W hpos,vpos where hpos and vpos are + dimensions with leading and/or trailing spaces. If hpos,vpos absent + then we move to minhp,minvp (top left corner of page rectangle). + */ + + int hpos, vpos; /* move window to this new position */ + + /* commpos is positioned after W */ + + /* seek hpos */ + if (!GetXDim (commstring, commlen, &commpos, &hpos)) + { + /* hpos,vpos absent - move to top left of page */ + NewLocation (minhp, minvp); + return; + } + /* skip any spaces before comma */ + while (commpos < commlen && commstring[commpos] == ' ') + commpos++; + + /* seek comma */ + if (commpos == commlen || commstring[commpos] != ',') + { + /* comma , (which ought to precede vpos) is missing */ + ClearMessageLine (); + MesgString ("Comma expected!"); + if (commpos < commlen) + commpos++; + BadCommandMessage (); + return; + } + commpos++; /* skip over comma */ + + /* seek vpos */ + if (GetYDim (commstring, commlen, &commpos, &vpos)) + { + NewLocation (hpos, vpos); + } + else + { + /* vpos is missing or wrongly typed */ + ClearMessageLine (); + MesgString ("Vertical coordinate expected!"); + if (commpos < commlen) + commpos++; + BadCommandMessage (); + } +} +/* WindowMove */ + +/*--------------------------------------------------------------------*/ + +static void +WindowUpDown () +{ + int amount; /* move window up/down this many pixels */ + char STR[2]; + + /* commpos is positioned after U or D */ + if (!GetYDim (commstring, commlen, &commpos, &amount)) + /* if amount absent, move by _half_ window height */ + amount = scaledht / 2; /* for odd scaleht, this is half a pixel less */ + + sprintf (STR, "%c", command); + if (!strcmp (STR, Up)) /* move Up. (Down is positive.) */ + amount = -amount; + + NewLocation (windowleft, windowtop + amount); +} +/* WindowUpDown */ + +/*--------------------------------------------------------------------*/ + +static void +WindowLeftRight () +{ + int amount; /* move window left/right this many pixels */ + char STR[2]; + + /* commpos is positioned after L or R */ + if (!GetXDim (commstring, commlen, &commpos, &amount)) + /* if amount absent, move by _half_ window width */ + amount = scaledwd / 2; /* for odd scaledwd, this is half a pixel less */ + + sprintf (STR, "%c", command); + if (!strcmp (STR, Left)) /* move Left. (Right is positive.) */ + amount = -amount; + + NewLocation (windowleft + amount, windowtop); +} +/* WindowLeftRight */ + +/*--------------------------------------------------------------------*/ + +static void +NewWindowWidth (int wd) +{ + /* Set window width to given value (> 0 and <= max dimension). */ + + scaledwd = wd; + hscalefactor = (double) windowwd / scaledwd; +} + +/* NewWindowWidth */ + +/*--------------------------------------------------------------------*/ + +static void +SetWindowWidth () +{ + /* Set horizontal size of window region to given dimension; + if <= 0, then set horizontal size to 1 pixel. + If no parameter, then use the unscaled width represented by windowwd. + */ + + int wd; + + /* commpos is positioned after H */ + if (!GetXDim (commstring, commlen, &commpos, &wd)) + { /* parameter absent */ + NewWindowWidth (windowwd); + return; + } + /* note that maximum value of wd is restricted to maxpix */ + if (wd <= 0) + wd = 1; + NewWindowWidth (wd); +} +/* SetWindowWidth */ + +/*--------------------------------------------------------------------*/ + +static void +NewWindowHeight (int ht) +{ + /* Set window height to given value (> 0 and <= max dimension). */ + + scaledht = ht; + vscalefactor = (double) windowht / scaledht; +} + +/* NewWindowHeight */ + +/*--------------------------------------------------------------------*/ + +static void +SetWindowHeight () +{ + /* Set vertical size of window region to given dimension; + if <= 0, then set vertical size to 1 pixel. + If no parameter, then use the unscaled height represented by windowht. + */ + + int ht; + + /* commpos is positioned after V */ + if (!GetYDim (commstring, commlen, &commpos, &ht)) + { /* parameter absent */ + NewWindowHeight (windowht); + return; + } + /* note that maximum value of ht is restricted to maxpix */ + if (ht <= 0) + ht = 1; + NewWindowHeight (ht); +} +/* SetWindowHeight */ + +/*--------------------------------------------------------------------*/ + +static int +ScaleHpos (int h) +{ + /* Return a scaled value for the given horizontal window coordinate. */ + + if (hscalefactor > 1.0) + return ((int) (h * hscalefactor + 0.5)); + else /* hscalefactor <= 1.0 */ + return ((int) ((h + 0.5) * hscalefactor)); +} + +/* ScaleHpos */ + +/*--------------------------------------------------------------------*/ + +static int +ScaleVpos (int v) +{ + /* Return a scaled value for the given vertical window coordinate. */ + + if (vscalefactor > 1.0) + return ((int) (v * vscalefactor + 0.5)); + else /* vscalefactor <= 1.0 */ + return ((int) ((v + 0.5) * vscalefactor)); +} + +/* ScaleVpos */ + +/*--------------------------------------------------------------------*/ + +static void +SetAutoView () +{ + /* Toggle AutoView. */ + + autoviewing = (autoviewing == false ? true : false); +} + +/* SetAutoView */ + +/*--------------------------------------------------------------------*/ + +static void +ZITL () +{ + /* Zoom In, fixing Top Left of window. */ + + /* scaledwd and scaledht are > 0 */ + NewWindowWidth ((int) ceil (scaledwd / zoomfactor)); + NewWindowHeight ((int) ceil (scaledht / zoomfactor)); +} + +/* ZITL */ + +/*--------------------------------------------------------------------*/ + +static void +ZOTL () +{ + /* Zoom out, fixing Top Left of window. */ + + /* avoid overflow *//* GT asks: what overflow? */ + + if (maxpix / zoomfactor > scaledwd) + NewWindowWidth ((int) (scaledwd * zoomfactor)); + else + NewWindowWidth (maxpix); + + if (maxpix / zoomfactor > scaledht) + NewWindowHeight ((int) (scaledht * zoomfactor)); + else + NewWindowHeight (maxpix); +} +/* ZOTL */ + +/*--------------------------------------------------------------------*/ + +static void +ZoomWindow () +{ + /* Zoom In, or Zoom Out. */ + /* For ZI, ZO, zoom relative to top left of window. */ + /* For ZCI, ZCO, zoom relative to centre of window. */ + + /* Just read a 'Z'. */ + /* Parse the rest of a ZoomInOut command and do it. + commpos is pointing to next position in commandstr + (and should be I or O). + */ + + String zs; /* Zoom command string */ + char zch2 = '\0', zch3 = '\0'; /* Zoom command characters 2 and 3 */ + double zoomf; /* Zoom Factor */ + boolean zin = true; /* flag for Zoom direction; default = Zoom in */ + boolean zc = false; /* flag for Zoom location; default = top left */ + boolean zok = true; /* flag for valid Zoom; default = valid */ + + zs[0] = 'Z'; /* Zoom commands begin with 'Z' */ + zs[1] = '\0'; /* properly terminate zs */ + + /* read second command character, which should be 'I' or 'O'. */ + if (commpos < commlen) + { + zch2 = TOUPPER (commstring[commpos]); + commpos++; + zs[1] = zch2; + zs[2] = '\0'; + } + switch (zch2) + { + case 'I': + zc = false; + zin = true; + break; + case 'O': + zc = false; + zin = false; + break; + case 'C': + if (commpos < commlen) + { + zch3 = TOUPPER (commstring[commpos]); + commpos++; + zs[2] = zch3; + zs[3] = '\0'; + } + switch (zch3) + { + case 'I': + zc = true; + zin = true; + break; + case 'O': + zc = true; + zin = false; + break; + default: + zok = false; + break; + } + break; + default: + zok = false; + break; + } + + if (!zok) + { /* invalid Z* command */ + String got; + ClearMessageLine (); + sprintf (got, "Read `%s', ", zs); + MesgString (got); + MesgString ("ZI, ZO, ZCI or ZCO expected!"); + BadCommandMessage (); + } + else + { /* valid Zoom command */ + int pos = 0; + boolean realfound = false; + + /* skip any spaces after Zoom command */ + while (commpos < commlen && commstring[commpos] == ' ') + commpos++; + + /* scan rest of command line for a number */ + /* this is unambiguous, b/c "zoom, goto new page" is silly on a line */ + /* fortunately, commstring is well terminated, with a NUL char */ + +/* GT - this is the line I once spoilt: I wasn't advancing by characters! */ +/* GT - use the real number reading code from GetDimension? */ +/* static boolean GetReal (char * str_in, int slen, int * pos, double *r) */ + +/* if (sscanf (commstring+commpos, "%lf", &zoomf) == 1) */ + + pos = 0; /* pos == new commpos - old commpos */ + realfound = GetReal (commstring + commpos, commlen - commpos, &pos, &zoomf); + commpos += pos; + + /* Set Zoom Factor, if any. */ + if (realfound) + { + if (zoomf > 1.0) + /* change Zoom Factor */ + zoomfactor = zoomf; + else + { + /* Zooms of unity or less are rejected */ + /* this covers zero, and avoids ZI/ZO confusion for user */ + String msgstring; + + /* !! GT - dangerous, as msgstring may overflow - unlikely, though */ + sprintf (msgstring, + "zoom factor must > 1, so keeping old factor of %.2f\n", + zoomfactor); + + ClearMessageLine (); + MesgString (msgstring); + + BadCommandMessage (); + } + /* gt - signal to update status line b/c of new Zoom Factor */ + paintDVIStatus = true; + } + + /* Zoom! */ + if (zc) + { /* Zoom wrt Centre */ + if (zin) + { + /* ZCI = Zoom In to Centre. Algorithm: */ + /* Down and Right to centre of window, */ + /* Zoom in to top left of new window, */ + /* Up and Left to top left of that window. */ + + NewLocation (windowleft + scaledwd / 2, windowtop + scaledht / 2); + ZITL (); + NewLocation (windowleft - scaledwd / 2, windowtop - scaledht / 2); + } + else + { + /* ZCO = Zoom Out from Centre. Algorithm: */ + /* Down and Right to centre of window, */ + /* Zoom out from top left of new window, */ + /* Up and Left to top left of that window. */ + /* DEFECT: can be altered by Zoom Out restrictions. */ + + NewLocation (windowleft + scaledwd / 2, windowtop + scaledht / 2); + ZOTL (); + NewLocation (windowleft - scaledwd / 2, windowtop - scaledht / 2); + } + } + else + { /* Zoom wrt Top Left */ + /* DEFECT: Zoom Out is restricted; see ZOTL and NewLocation code. */ + if (zin) + ZITL (); /* ZI = Zoom In to Top Left */ + else + ZOTL (); /* ZO = Zoom out from Top Left */ + } + } +} +/* ZoomWindow */ + +/*--------------------------------------------------------------------*/ + +static boolean +NextPageFound (boolean ascending) +{ + /* User has selected next page in DVI file; what they get will depend on + the current DVI page and whether we are ascending or not. + Return TRUE iff we can move to next page. + */ + + boolean found = false; + + if (currDVIpage == 1 && !ascending) + { + ClearMessageLine (); + MesgString ("You are looking at first DVI page!"); + BadCommandMessage (); + found = false; + } + else if (currDVIpage == totalpages && ascending) + { + ClearMessageLine (); + MesgString ("You are looking at last DVI page!"); + BadCommandMessage (); + found = false; + } + else + { + MoveToNextPage (ascending); /* position to next DVI page */ + found = true; + } + return found; +} +/* NextPageFound */ + +/*--------------------------------------------------------------------*/ + +static boolean +DVIPageFound (int n) +{ + /* User has selected a particular DVI page number. + Move to page n and return TRUE iff n is in 1..totalpages. + */ + + boolean found = false; + + if (n < 1 || n > totalpages) + { + ClearMessageLine (); + if (totalpages > 1) + { + MesgString ("You can only request DVI pages 1 to "); + MesgInt (totalpages); + MesgChar ('!'); + } + else + { + MesgString ("You can only request DVI page 1!"); + } + BadCommandMessage (); + found = false; + } + else + { + MoveToDVIPage (n); /* position to given DVI page */ + found = true; + } + return found; +} +/* DVIPageFound */ + +/*--------------------------------------------------------------------*/ + +static boolean +ParseTeXpage (TeXpageinfo * newTeXpage) +{ + /* Return TRUE iff TeX page specification in commstring is valid. If so then + newTeXpage will contain the appropriate information for MoveToTeXPage. + The syntax of a TeX page specification is [n{.n}] where n is any integer as + defined by GetInteger. Up to 10 integers may be given and are separated by + periods, even if absent. Trailing periods may be omitted. Spaces before + and after integers and periods are skipped. The 10 positions correspond to + the \count0, \count1, ... ,\count9 values that TeX stores with every page. + commpos is initially pointing at [. + */ + + newTeXpage->lastvalue = 0; + while (true) + { + commpos++; + + newTeXpage->present[(int) newTeXpage->lastvalue] = + GetInteger (commstring, commlen, &commpos, + &newTeXpage->value[(int) newTeXpage->lastvalue]); + + /* commpos now at commlen, space, period, non-digit or ']' */ + + while (commpos < commlen && commstring[commpos] == ' ') + { + commpos++; /* skip any spaces */ + } + if (commpos == commlen) + { /* check this first! */ + ClearMessageLine (); + MesgString ("] expected!"); + BadCommandMessage (); /* commpos at commlen */ + return false; + } + if (commstring[commpos] == ']') + { /* end of TeX page spec */ + commpos++; + break; /* escape from enclosing "infinite" while loop */ + } + if (newTeXpage->lastvalue >= 9) + { + ClearMessageLine (); + MesgString ("] expected after 10 integers!"); + commpos++; + BadCommandMessage (); + return false; + } + newTeXpage->lastvalue++; + if (commstring[commpos] == '.') + continue; + ClearMessageLine (); + MesgString ("Period, integer or ] expected!"); + commpos++; + BadCommandMessage (); + return false; + } /* while */ + + while (newTeXpage->lastvalue > 0 + && !newTeXpage->present[(int) newTeXpage->lastvalue]) + { + newTeXpage->lastvalue--; + } + return true; +} +/* ParseTeXpage */ + +/*--------------------------------------------------------------------*/ + +static boolean +TeXPageFound () +{ + /* Return TRUE iff TeX page specification is valid and exists. + If so then position to lowest matching page. + */ + + boolean found = false; + TeXpageinfo newTeXpage; + + if (ParseTeXpage (&newTeXpage)) + { /* invalid TeX page specification */ + if (MoveToTeXPage (&newTeXpage)) + found = true; /* we found lowest matching page */ + else + { + ClearMessageLine (); + MesgString ("No TeX page matches your request!"); + BadCommandMessage (); + found = false; + } + } + else + found = false; + return found; +} +/* TeXPageFound */ + +/*--------------------------------------------------------------------*/ + +static int +Min (int a, int b) +{ + /* Return the minimum value of a and b. */ + + if (a < b) + return a; + else + return b; +} +/* Min */ + +/*--------------------------------------------------------------------*/ + +static int +Max (int a, int b) +{ + /* Return the maximum value of a and b. */ + + if (a > b) + return a; + else + return b; +} +/* Max */ + +/*--------------------------------------------------------------------*/ + +static void +ProcessPage () +{ + /* We are ready to interpret the current DVI page and fill in the various data + structures imported from DVIReader. This routine will also: + set the window size and location to useful values (if autoviewing), + update pageoffpaper (after checking to see if it was TRUE for the previous + page processed as part of a multiple command string), + set screenjustcleared, paintwindow and paintWindowStatus to TRUE, + set paintDVIStatus to FALSE. + */ + + int halfht, halfwd; + + /* We check pageoffpaper here so user can type "NNNNNNNNNNNNN..." and + note ALL the pages that are off the paper, not just the last one + processed. + */ + if (pageoffpaper) + { + ClearMessageLine (); + MesgString ("Page off paper!"); + WaitForReturn (); + /* the previous page */ + } + ClearScreen (); + screenjustcleared = true; + UpdateDVIStatusLine (); + /* a MoveTo... routine has updated currDVI/TeXpage */ + paintDVIStatus = false; + InterpretPage (); /* fill in DVIReader's page data structures */ + SortFonts (&unusedfont); + /* sort fonts in order of least chars and return + pointer to first unused font */ + ClearMessageLine (); /* clear any message */ + if (pageempty) + { + minhp = 0; + maxhp = 0; + minvp = 0; + maxvp = 0; /* for window status */ + } + if (autoviewing) + { /* view as much of paper as possible, but without too much distortion */ + if ((paperwd < paperht && windowwd >= windowht) + || (paperwd == paperht && windowwd > windowht)) + { + halfht = paperht / 2; + if (paperht & 1) /* ensure bottom visible */ + halfht++; + NewWindowHeight (halfht); /* try top half of paper */ + NewWindowWidth (paperwd); + NewLocation (paperleft, papertop); /* top left corner of paper */ + if (!pageempty && outsidepage) /* try moving down */ + NewLocation (paperleft, papertop + halfht); + } + else if ((paperwd > paperht && windowwd <= windowht) + || (paperwd == paperht && windowwd < windowht)) + { + halfwd = paperwd / 2; + if (paperwd & 1) /* ensure right visible */ + halfwd++; + NewWindowHeight (paperht); + NewWindowWidth (halfwd); /* try left half of paper */ + NewLocation (paperleft, papertop); /* top left corner of paper */ + if (!pageempty && outsidepage) /* try moving right */ + NewLocation (paperleft + halfwd, papertop); + } + else + { + /* paper shape matches unscaled window shape */ + NewWindowHeight (paperht); /* try all of paper */ + NewWindowWidth (paperwd); + NewLocation (paperleft, papertop); /* top left corner of paper */ + } + } + else + { + /* not autoviewing, so use current window location and size */ + NewWindowHeight (scaledht); + NewWindowWidth (scaledwd); + NewLocation (windowleft, windowtop); + } + + /* check if part/all of page is off paper; + if so, and autoviewing is enabled, then we set window size and location + so user can just see ALL of paper AND ALL of page. + */ + pageoffpaper = (!pageempty && ((minhp < paperleft || minvp < papertop) + || (maxhp > paperright || maxvp > paperbottom))); + if (pageoffpaper && autoviewing) + { + NewWindowHeight (Max (maxvp, paperbottom) - Min (minvp, papertop) + 1); + NewWindowWidth (Max (maxhp, paperright) - Min (minhp, paperleft) + 1); + NewLocation (Min (minhp, paperleft), Min (minvp, papertop)); + } + paintWindowStatus = true; + paintwindow = true; +} +/* ProcessPage */ + +/*--------------------------------------------------------------------*/ + +static void +ChangeUnits () +{ + /* Parse a C command. + commpos is pointing to next position in commstring. + Hope that is the start of IN, CM, MM, BP, PC, PT, SP, or PX. + */ + + char nextch1, nextch2; + String cstr; + + /* skip any spaces between 'C' command and following unit */ + + while (commpos < commlen && commstring[commpos] == ' ') + commpos++; + + /* read next two characters */ + /* these should comprise a known unit of distance */ + + if (commpos < commlen) + { + nextch1 = TOUPPER (commstring[commpos]); + commpos++; + } + else + nextch1 = ' '; + + if (commpos < commlen) + { + nextch2 = TOUPPER (commstring[commpos]); + commpos++; + } + else + nextch2 = ' '; + + sprintf (cstr, "%c%c", nextch1, nextch2); + + if (!strcmp (cstr, "IN")) + currentunits = ic; + else if (!strcmp (cstr, "CM")) + currentunits = cm; + else if (!strcmp (cstr, "MM")) + currentunits = mm; + else if (!strcmp (cstr, "BP")) + currentunits = bp; + else if (!strcmp (cstr, "PC")) + currentunits = pc; + else if (!strcmp (cstr, "PT")) + currentunits = pt; + else if (!strcmp (cstr, "SP")) + currentunits = sp; + else if (!strcmp (cstr, "PX")) + currentunits = px; + else + { + ClearMessageLine (); + MesgString ("Unknown units: `"); + MesgString (cstr); + MesgString ("'. "); + switch (nextch1) + { + case 'I': + MesgString ("IN"); + break; + + case 'C': + MesgString ("CM"); + break; + + case 'M': + MesgString ("MM"); + break; + + case 'B': + MesgString ("BP"); + break; + + case 'P': + MesgString ("PC, PT or PX"); + break; + + case 'S': + MesgString ("SP"); + break; + + default: + MesgString ("IN, CM, MM, BP, PC, PT, SP or PX"); + break; + } /* command */ + + MesgString (" expected."); + BadCommandMessage (); + + } /* strcmp (cstr, UNITNAME) */ +} +/* ChangeUnits */ + +/*--------------------------------------------------------------------*/ + +static boolean +UserHitsReturn (int *linecount) +{ + /* Do a MesgLine and return TRUE iff linecount = bottoml-2 AND user hits CR. + If linecount < bottoml-2 then return FALSE; if not, and user hits + something other than CR, then prepare a new screen before returning FALSE. + */ + + char ch; + + MesgLine (); + /* test for approaching end of screen */ + if (*linecount == bottoml - 2) + { + /* prompt for next screen */ + MoveToTextLine (bottoml); + MesgString ("Hit RETURN key to resume page display,"); + MesgString (" or any other key for more: "); + MesgFlush (); + + /* read first character typed by user in response to prompt */ + ReadChar (&ch); + if (ch == CR) + { + return true; + } + ClearScreen (); + screenjustcleared = true; + MoveToTextLine (1); + *linecount = 1; + } + else + { + (*linecount)++; + } + return false; +} +/* UserHitsReturn */ + +/*--------------------------------------------------------------------*/ + +static void +WriteUnits () +{ + switch (currentunits) + { + case ic: + MesgString ("in"); + break; + case cm: + MesgString ("cm"); + break; + case mm: + MesgString ("mm"); + break; + case bp: + MesgString ("bp"); + break; + case pc: + MesgString ("pc"); + break; + case pt: + MesgString ("pt"); + break; + case sp: + MesgString ("sp"); + break; + case px: + MesgString ("px"); + break; + } /* currentunits */ +} +/* WriteUnits */ + +/*--------------------------------------------------------------------*/ + +static int +WritePtSize (int scaledsize) +{ + /* Show given font size (in DVI units), in (possibly magnified) pts. + Return length of displayed message. + */ + + double realdim; + int fracpart; + double precision = 0.1; + int len = 0; + + realdim = scaledsize / 65536.0 * (mag / 1000.0); + /* show realdim to the precision given above */ + if (fabs (realdim) < 0.5 * precision) + { + MesgChar ('0'); + ++len; + } + else + { + if (realdim < 0.0) + { + MesgChar ('-'); + ++len; + realdim = fabs (realdim); + } + realdim += 0.5 * precision; /* round up to specified precision */ + len += MesgInt ((int) realdim); /* whole part */ + fracpart = (int) ((realdim - (int) realdim) / precision); + if (fracpart > 0) + { + MesgChar ('.'); + ++len; + len += MesgInt (fracpart); + } + } + MesgString ("pt"); + len += 2; + return (len); +} +/* WritePtSize */ + +/*--------------------------------------------------------------------*/ + +static void +ShowStatistics () +{ + /* Show option values and font/character/rule/special statistics. + UserHitsReturn controls pagination and takes the place of MesgLine. + */ + + specialinfo *temp; + int linecount, fontcount; + char ch; + static char LeftStr[] = ""; + static char RightStr[] = ""; + double precision = 1.0e-3; /* precision of quantities */ + + ClearScreen (); + screenjustcleared = true; + MoveToTextLine (1); + linecount = 1; + + MesgString ("DVI file = "); + MesgString (LeftStr); + MesgString (DVIname); + MesgString (RightStr); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("VDU = "); + MesgString (LeftStr); + MesgString (vdu); + MesgString (RightStr); + if (UserHitsReturn (&linecount)) + return; + + { /* beginblock */ + String realstring; + + MesgString ("X Resolution (dpi) = "); + /* xres is unlikely to overflow realstring */ + sprintf (realstring, "%f", xres); + MesgString (realstring); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Y Resolution (dpi) = "); + /* yres is unlikely to overflow realstring */ + sprintf (realstring, "%f", yres); + MesgString (realstring); + if (UserHitsReturn (&linecount)) + return; + + } /* endblock */ + + MesgString ("Magnification = "); + MesgInt (mag); + if (mag == DVImag) + { + MesgString (" (DVI mag)"); + } + else + { + MesgString (" (DVI mag of "); + MesgInt (DVImag); + MesgString (" was overridden)"); + } + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Dummy PK font = "); + MesgString (LeftStr); + MesgString (dummy_pk); + MesgString (RightStr); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Dummy TFM metric = "); + MesgString (LeftStr); + MesgString (dummy_tfm); + MesgString (RightStr); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Horizontal offset = "); + WriteXDim (precision, hoffset); + WriteUnits (); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Vertical offset = "); + WriteYDim (precision, voffset); + WriteUnits (); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Paper wd by ht = "); + WriteXDim (precision, paperwd); + WriteUnits (); + MesgString (" by "); + WriteYDim (precision, paperht); + WriteUnits (); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Total fonts on ALL pages = "); + MesgInt (totalfonts); + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Fonts, at pt size: (if on current page, give (total chars))"); + if (UserHitsReturn (&linecount)) + return; + + MesgString + ("H: Honest, S: Substitute; K: PK, T: TFM, D: DVI."); + if (UserHitsReturn (&linecount)) + return; + + fontcount = 0; + for (thisfontinfo = fontlist; + thisfontinfo != (fontinfo *) NULL; + thisfontinfo = thisfontinfo->nextfont) + { + String msgstr; /* output string for messages */ + int len; /* length of a displayed message */ + int maxptroom = 7; /* room for point size message */ + int i; + + if (thisfontinfo->fontspeclen == 0) + { /* need to build fontspec */ + BuildFontSpec (thisfontinfo); + /* fontexists may have become TRUE */ + } + /* most font names have at most 8 characters, for ms-dos! */ + sprintf (msgstr, "%-8s", thisfontinfo->fontname); + MesgString (msgstr); + + MesgString (" "); + len = WritePtSize (thisfontinfo->scaledsize); + for (i = len; i < maxptroom; i++) + MesgChar (' '); + + MesgString ("->"); + + if (thisfontinfo->fontexists) + { + if (thisfontinfo->honest) + MesgChar ('H'); + else /* substitute */ + MesgChar ('S'); + + if (thisfontinfo->pkfont) + MesgChar ('K'); + else /* non-PostScript TFM */ + MesgChar ('T'); + + MesgString (" "); + i = thisfontinfo->fontspeclen - 50; /* try to show last 70 chars */ + if (i < 0) + i = 0; + MesgString (thisfontinfo->fontspec + i); + } + else + { + MesgChar ('S'); + MesgChar ('D'); + + MesgString (" "); + MesgString ("Terse display"); + } + + if (thisfontinfo->fontused) + { + fontcount++; + MesgString (" ("); + MesgInt (thisfontinfo->totalchars); + MesgString (")"); + } + if (UserHitsReturn (&linecount)) + return; + } + /* end (for) loop */ + + if (currDVIpage == 0) + MesgString ("You haven't selected a page yet."); + else + { + MesgString ("Total fonts on current page = "); + MesgInt (fontcount); + } + if (UserHitsReturn (&linecount)) + return; + + MesgString ("Total rules on current page = "); + MesgInt (totalrules); + + if (speciallist != (specialinfo *) NULL) + { + if (UserHitsReturn (&linecount)) + return; + + MesgString ("\\special commands on current page: "); + if (UserHitsReturn (&linecount)) + return; + + temp = speciallist; + while (temp != (specialinfo *) NULL) + { + MesgString ("At ("); + WriteXDim (precision, temp->hp); + MesgChar (','); + WriteYDim (precision, temp->vp); + MesgString ("): "); + MesgString (temp->special); + if (UserHitsReturn (&linecount)) + return; + + temp = temp->nextspecial; + } /* while */ + } /* if */ + MesgLine (); + MesgLine (); + MoveToTextLine (bottoml); + MesgString ("Hit RETURN key to resume page display: "); + MesgFlush (); + + do + { + ReadChar (&ch); + } + while (ch != CR); + +} +/* ShowStatistics */ + +/*--------------------------------------------------------------------*/ +/*--------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------*/ + +#define edgepixel '.' + /* black pixel for outlines on non-graphic VDUs; + note that VDU TeXtoASCII['.'] = '.' */ + +static void +DisplayPaperEdges () +{ + /* Display visible outlines of the imaginary sheet of paper. + Thickness of outlines = 1 screen pixel, + no matter what the h and v scaling. + */ + + int top, bot, left, right; /* visible edges of paper in paper pixels */ + int scaledtop, scaledleft; /* scaled visible edges in screen pixels */ + int scaledbot, scaledright, scaledheight; + /* scaled width and height */ + int scaledwidth; + + /* first check if any part of paper is visible */ + + if (papertop > windowbottom) + return; + + if (paperbottom < windowtop) + return; + + if (paperleft > windowright) + return; + + if (paperright < windowleft) + return; + + /* part or all of paper is visible, so return visible region */ + top = Max (papertop, windowtop); + bot = Min (paperbottom, windowbottom); + left = Max (paperleft, windowleft); + right = Min (paperright, windowright); + scaledtop = ScaleVpos (top - windowtop) + windowv; + scaledleft = ScaleHpos (left - windowleft) + windowh; + if (vscalefactor > 1.0) + scaledbot = ScaleVpos (bot - windowtop + 1) + windowv - 1; + else + scaledbot = ScaleVpos (bot - windowtop) + windowv; + if (hscalefactor > 1.0) + scaledright = ScaleHpos (right - windowleft + 1) + windowh - 1; + else + scaledright = ScaleHpos (right - windowleft) + windowh; + scaledheight = scaledbot - scaledtop + 1; + scaledwidth = scaledright - scaledleft + 1; + /* only show visible edges if they are also paper outlines */ + if (left == paperleft) + ShowRectangle (scaledleft, scaledtop, 1, scaledheight, edgepixel); + if (bot == paperbottom) + ShowRectangle (scaledleft, scaledbot, scaledwidth, 1, edgepixel); + if (top == papertop) + ShowRectangle (scaledleft, scaledtop, scaledwidth, 1, edgepixel); + if (right == paperright) + ShowRectangle (scaledright, scaledtop, 1, scaledheight, edgepixel); +} +/* DisplayPaperEdges */ + +#undef edgepixel + + +/*--------------------------------------------------------------------*/ + +static boolean +RectangleVisible (int intop, int inbot, int inleft, int inright, + int *outtop, int *outbot, int *outleft, int *outright) +{ + /* Return TRUE iff part or all of given rectangle would be visible + in the current window. Iff so, then we also return the visible + region; the input and possible output rectangles are defined by their + top, bottom, left and right edges in paper pixel coordinates. + */ + + if (allpagevisible) + { /* all of rectangle must be visible */ + *outtop = intop; + *outbot = inbot; + *outleft = inleft; + *outright = inright; + return true; + } + else if (intop > windowbottom) + return false; + else if (inbot < windowtop) + return false; + else if (inleft > windowright) + return false; + else if (inright < windowleft) + return false; + else + { + /* part or all of rectangle is visible, so return visible region */ + *outtop = Max (intop, windowtop); + *outbot = Min (inbot, windowbottom); + *outleft = Max (inleft, windowleft); + *outright = Min (inright, windowright); + return true; + } +} +/* RectangleVisible */ + +/*--------------------------------------------------------------------*/ + +#define RULEPIXEL '*' + /* black pixel for rules on non-graphic VDUs; + note that VDU sets TeXtoASCII['*'] := '*' */ + + +static void +DisplayRules () +{ + /* Display all pixels in rules, regardless of current displaymode. + Rules will be displayed in the same order as in the DVI page (essentially + top-down and left-right) because of the way DVIReader builds a rulelist. + */ + + int top, bottom, left, right; /* visible edges of rule */ + int scaledtop, scaledleft; /* scaled visible edges */ + int scaledbot, scaledright, scaledwidth, scaledheight; + /* scaled width and height */ + int thisrule; + char keyhit; /* returned by BusyRead if TRUE */ + _REC_ruletable *ruletab = (_REC_ruletable *) NULL; + + thisruleinfo = rulelist; + while (thisruleinfo != (ruleinfo *) NULL) + { + thisrule = 0; + while (thisrule < thisruleinfo->rulecount) + { + ruletab = &thisruleinfo->ruletable[thisrule]; + /* check if any part of rule is visible */ + /* vp,hp is bottom left corner of rule on page */ + if (RectangleVisible (ruletab->vp - ruletab->ht + 1, + ruletab->vp, ruletab->hp, + ruletab->hp + ruletab->wd - 1, + &top, &bottom, &left, &right)) + { /* rule edges */ + /* show all pixels in this rectangle */ + scaledtop = ScaleVpos (top - windowtop) + windowv; + scaledleft = ScaleHpos (left - windowleft) + windowh; + + if (vscalefactor > 1.0) + scaledbot = ScaleVpos (bottom - windowtop + 1) + windowv - 1; + else + scaledbot = ScaleVpos (bottom - windowtop) + windowv; + + if (hscalefactor > 1.0) + scaledright = ScaleHpos (right - windowleft + 1) + windowh - 1; + else + scaledright = ScaleHpos (right - windowleft) + windowh; + + /* v coord of top left cnr */ + scaledheight = scaledbot - scaledtop + 1; + + /* h coord of top left cnr */ + scaledwidth = scaledright - scaledleft + 1; + + ShowRectangle (scaledleft, scaledtop, scaledwidth, scaledheight, + RULEPIXEL); + + /* check keyboard after every visible rule */ + if (BusyRead (&keyhit)) + { + char STR1[2]; + keyhit = TOUPPER (keyhit); + sprintf (STR1, "%c", keyhit); + if (!strcmp (STR1, Terse)) + { + displaymode ^= tersemode; /* toggle terse mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else + { + char STR2[2]; + sprintf (STR2, "%c", keyhit); + if (!strcmp (STR2, Box)) + { + displaymode ^= boxmode; /* toggle box mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else + { + char STR3[2]; + sprintf (STR3, "%c", keyhit); + if (!strcmp (STR3, Full)) + { + displaymode ^= fullmode; /* toggle full mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else if (keyhit == CR) + { + useraborted = true; /* checked in DisplayPage */ + return; + + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + /* visible rectangle */ + thisrule++; + } /* while */ + thisruleinfo = thisruleinfo->nextrule; + } /* while */ +} +/* DisplayRules */ + + + + +/*--------------------------------------------------------------------*/ +/* Adapted from xdvi-20.0 by Paul Vojta */ +/* Keys for epsf specials */ + +static char *keytab[] = +{ + "clip", + "llx", + "lly", + "urx", + "ury", + "rwi", + "rhi", + + "hsize", + "vsize", + "hoffset", + "voffset", + "hscale", + "vscale", + "angle" +}; + +#define KEY_LLX keyval[0] +#define KEY_LLY keyval[1] +#define KEY_URX keyval[2] +#define KEY_URY keyval[3] +#define KEY_RWI keyval[4] +#define KEY_RHI keyval[5] + +#define KEY_HSIZE keyval[6] +#define KEY_VSIZE keyval[7] +#define KEY_HOFFSET keyval[8] +#define KEY_VOFFSET keyval[9] +#define KEY_HSCALE keyval[10] +#define KEY_VSCALE keyval[11] +#define KEY_ANGLE keyval[12] + +#define NKEYS (sizeof(keytab)/sizeof(*keytab)) +#define N_ARGLESS_KEYS 1 + +static int +epsf_special (char *cp, int *wd, int *ht) +{ + char *filename; + int filenamelen = 0; + int flags = 0; + double keyval[NKEYS - N_ARGLESS_KEYS]; + + /* skip to the file name */ + while (isspace (*cp)) + ++cp; + if (*cp != '=') + return 0; + do + ++cp; + while (isspace (*cp)); + + filename = cp; + if (*cp == '\'' || *cp == '"') + { + do + ++cp; + while (*cp != '\0' && *cp != *filename); /* up to closing ['"] */ + ++filename; + } + else + while (*cp != '\0' && *cp != ' ' && *cp != '\t') + ++cp; + filenamelen = cp - filename; + while (*cp == ' ' || *cp == '\t') + ++cp; + while (*cp != '\0') + { + char *p1 = cp; + int keyno; + + while (*p1 != '=' && !isspace (*p1) && *p1 != '\0') + ++p1; + for (keyno = 0;; ++keyno) + { + if (keyno >= NKEYS) + { +#if 0 + Fprintf (stderr, + "%s: unknown keyword (%*s) in \\special will be ignored\n", + prog, (int) (p1 - cp), cp); +#endif + + break; + } + if (strncmp (cp, keytab[keyno], p1 - cp) == 0) + { + if (keyno >= N_ARGLESS_KEYS) + { + while (isspace (*p1)) + ++p1; + if (*p1 == '=') + { + ++p1; + while (isspace (*p1)) + ++p1; + } + { + int keyno_tmp = keyno - N_ARGLESS_KEYS; + keyval[keyno_tmp] = atof (p1); + flags |= (1 << keyno_tmp); + } + } + break; + } + } + cp = p1; + while (!isspace (*cp) && *cp != '\0') + ++cp; + while (isspace (*cp)) + ++cp; + } + + if ((flags & 0x30) == 0x30 || ((flags & 0x30) && (flags & 0xf) == 0xf)) + { + /* convert from pt to sp and next to pixels */ + *wd = XPixelRound (65536 * 0.1 * ((flags & 0x10) ? KEY_RWI + : KEY_RHI * (KEY_URX - KEY_LLX) / (KEY_URY - KEY_LLY))); + *ht = YPixelRound (65536 * 0.1 * ((flags & 0x20) ? KEY_RHI + : KEY_RWI * (KEY_URY - KEY_LLY) / (KEY_URX - KEY_LLX))); + return 1; + } + else if ((flags & 0xc0) == 0xc0) + { + *wd = XPixelRound (65536 * ((flags & 0x400) ? KEY_HSCALE : 1.0) * KEY_HSIZE); + *ht = YPixelRound (65536 * ((flags & 0x800) ? KEY_VSCALE : 1.0) * KEY_VSIZE); + return 1; + } + return 0; +} + +/*--------------------------------------------------------------------*/ +static void +DisplaySpecials () +{ + /* Display dvips eps specials as boxes */ + + int top, bottom, left, right; /* visible edges of special */ + int scaledtop, scaledleft; /* scaled visible edges */ + int scaledbot, scaledright, scaledwidth, scaledheight; + /* scaled width and height */ + specialinfo *thisspecial; + char keyhit; /* returned by BusyRead if TRUE */ + + thisspecial = speciallist; + while (thisspecial != (specialinfo *) NULL) + { + int wd, ht; + /* Using undeclared strncasecmp just as xdvik/hypertex.c does. */ + if (strncasecmp (thisspecial->special, "psfile", 6) == 0 && + epsf_special (thisspecial->special + 6, &wd, &ht)) + { + /* check if any part of special is visible */ + /* vp,hp is bottom left corner of special on page */ + if (RectangleVisible (thisspecial->vp - ht + 1, + thisspecial->vp, + thisspecial->hp, + thisspecial->hp + wd - 1, + &top, &bottom, &left, &right)) + { /* special edges */ + /* show all pixels in this rectangle */ + scaledtop = ScaleVpos (top - windowtop) + windowv; + scaledleft = ScaleHpos (left - windowleft) + windowh; + + if (vscalefactor > 1.0) + scaledbot = ScaleVpos (bottom - windowtop + 1) + windowv - 1; + else + scaledbot = ScaleVpos (bottom - windowtop) + windowv; + + if (hscalefactor > 1.0) + scaledright = ScaleHpos (right - windowleft + 1) + windowh - 1; + else + scaledright = ScaleHpos (right - windowleft) + windowh; + /* coord of top left cnr */ + scaledheight = scaledbot - scaledtop + 1; + scaledwidth = scaledright - scaledleft + 1; + + if (left == thisspecial->hp) + ShowRectangle (scaledleft, scaledtop, 1, scaledheight, RULEPIXEL); + if (bottom == thisspecial->vp) + ShowRectangle (scaledleft, scaledbot, scaledwidth, 1, RULEPIXEL); + if (top == thisspecial->vp - ht + 1) + ShowRectangle (scaledleft, scaledtop, scaledwidth, 1, RULEPIXEL); + if (right == thisspecial->hp + wd - 1) + ShowRectangle (scaledright, scaledtop, 1, scaledheight, RULEPIXEL); + + /* check keyboard after every visible special */ + if (BusyRead (&keyhit)) + { + char STR1[2]; + keyhit = TOUPPER (keyhit); + sprintf (STR1, "%c", keyhit); + if (!strcmp (STR1, Terse)) + { + displaymode ^= tersemode; /* toggle terse mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else + { + char STR2[2]; + sprintf (STR2, "%c", keyhit); + if (!strcmp (STR2, Box)) + { + displaymode ^= boxmode; /* toggle box mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else + { + char STR3[2]; + sprintf (STR3, "%c", keyhit); + if (!strcmp (STR3, Full)) + { + displaymode ^= fullmode; /* toggle full mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else if (keyhit == CR) + { + useraborted = true; /* checked in DisplayPage */ + return; + + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } /* if */ + } + thisspecial = thisspecial->nextspecial; + } /* while */ +} +/* DisplaySpecials */ + +#undef RULEPIXEL + +/*--------------------------------------------------------------------*/ + +static boolean +PixelVisible (int hpos, int vpos) +{ + /* Return TRUE iff given paper pixel would be visible in current window. */ + + if (allpagevisible) + return true; + else if (vpos < windowtop) + return false; + else if (vpos > windowbottom) + return false; + else if (hpos < windowleft) + return false; + else if (hpos > windowright) + return false; + else + return true; +} +/* PixelVisible */ + +/*--------------------------------------------------------------------*/ + +static boolean +TerseChar () +{ + /* Display a quick and nasty representation of character, + only if ref pt visible. + Just how good the representation is, + depends on the capabilities of the VDU. + We don't bother checking + whether glyph is actually blank or non-existent. + */ + + _REC_chartable *chartab = &thischarinfo->chartable[thischar]; + + if (chartab == (_REC_chartable *) NULL) + return false; + + if (PixelVisible (chartab->hp, chartab->vp)) /* ref pt of char is visible */ + ShowChar (ScaleHpos (chartab->hp - windowleft) + windowh, + ScaleVpos (chartab->vp - windowtop) + windowv, + chartab->code); + + return true; +} +/* TerseChar */ + +/*--------------------------------------------------------------------*/ + +static boolean +BoxChar () +{ + /* Display visible box outlines of glyph. + Thickness of outlines = 1 screen pixel, + no matter what the h and v scaling. + */ + + int vpmyo, hpmxo; /* vp-yo, hp-xo: glyph's top and left edges */ + int top, bottom, left, right; /* visible edges of glyph */ + int scaledtop, scaledleft; /* scaled visible edges */ + int scaledbot, scaledright, scaledheight; + /* scaled width and height */ + int scaledwidth; + char ch; + + _REC_chartable *chartab = &thischarinfo->chartable[thischar]; + _REC_pixeltable *pixtab = (_REC_pixeltable *) NULL; + + if (chartab == (_REC_chartable *) NULL) + return false; + + pixtab = &thisfontinfo->pixelptr[chartab->code]; + if (pixtab == (_REC_pixeltable *) NULL) + return false; + + if (pixtab->mapadr != 0) + { /* glyph present and non-blank */ + /* check if any part of glyph is visible */ + + vpmyo = chartab->vp - pixtab->yo; + hpmxo = chartab->hp - pixtab->xo; + + if (RectangleVisible (vpmyo, vpmyo + pixtab->ht - 1, + hpmxo, hpmxo + pixtab->wd - 1, + &top, &bottom, &left, &right)) + { /* glyph edges */ + + scaledtop = ScaleVpos (top - windowtop) + windowv; + scaledleft = ScaleHpos (left - windowleft) + windowh; + + if (vscalefactor > 1.0) + scaledbot = ScaleVpos (bottom - windowtop + 1) + windowv - 1; + else + scaledbot = ScaleVpos (bottom - windowtop) + windowv; + + if (hscalefactor > 1.0) + scaledright = ScaleHpos (right - windowleft + 1) + windowh - 1; + else + scaledright = ScaleHpos (right - windowleft) + windowh; + + scaledheight = scaledbot - scaledtop + 1; + scaledwidth = scaledright - scaledleft + 1; + + /* Only show edges that are also glyph outlines! + Following method reduces the number of ShowRectangle calls needed for + very small boxes. + */ + ch = chartab->code; + if ((scaledheight < 3 + && (top == vpmyo && bottom == vpmyo + pixtab->ht - 1)) + || (scaledwidth < 3 + && (left == hpmxo && right == hpmxo + pixtab->wd - 1))) + { + ShowRectangle (scaledleft, scaledtop, scaledwidth, scaledheight, ch); + } + else + { + if (left == hpmxo) + ShowRectangle (scaledleft, scaledtop, 1, scaledheight, ch); + if (bottom == vpmyo + pixtab->ht - 1) + ShowRectangle (scaledleft, scaledbot, scaledwidth, 1, ch); + if (top == vpmyo) + ShowRectangle (scaledleft, scaledtop, scaledwidth, 1, ch); + if (right == hpmxo + pixtab->wd - 1) + ShowRectangle (scaledright, scaledtop, 1, scaledheight, ch); + } + } + /* visible part */ + } + return true; +} +/* BoxChar */ + +/*--------------------------------------------------------------------*/ + +static void +NotFound (char *fspec) +{ + StartText (); + ResetVDU (); /* do before message since it might erase screen! */ + FATAL1 ("Couldn't open font %s.", fspec); +} + +/* NotFound */ + +/*--------------------------------------------------------------------*/ + +#define MAXVISWORDS 100 +#define WORDSIZE 32 + +/* MAXVISWORDS * WORDSIZE = 100 * 32 = 3200 bits = maximum pixel width + of glyph! + If any fonts have glyphs wider than this, then increase MAXVISWORDS. + */ + +/* SYSDEP: BITSET is 32 bit word with elements 31,30,29,...,0 */ +typedef Word glyphrow[MAXVISWORDS]; + + +static boolean +FullChar () +{ + /* Display all pixels in a glyph using bitmap from font file. + The algorithm avoids overlapping rows when vscalefactor < 1.0. + When hscalefactor < 1.0, it is not worth the extra code to avoid + overlapping runs of 1 bits because the majority of character glyphs + have only one or two runs per row. + */ + + int vpmyo, hpmxo; /* vp-yo, hp-xo: glyph's top and left edges */ + int top, bottom, left, right; /* visible edges of glyph */ + int scaledv, scalednextv; /* scaled vertical positions for rows */ + int scaledh = 0; /* scaled horizontal positions within row */ + int scaledwidth, scaledheight; /* scaled width and height of row */ + int thisrow, thisbit; /* in paper coordinates */ + int wordsperrow; /* rows of bitmap are word aligned */ + int firstbit, lastbit; /* somewhere in 0 .. wordsperrow*WORDSIZE-1 */ + int firstword, lastword; /* somewhere in 0 .. wordsperrow-1 */ + int endword; /* = visible words in row, - 1 */ + int wordpos; /* 0 .. endword */ + int bitpos; /* (WORDSIZE-1) .. 0 */ + int i; + + glyphrow row; /* holds VISIBLE bits in one row of glyph; + possibly > one row if vscalefactor < 1.0 */ + + int_or_bptr ptr; /* pointer into bitmap - actually a "struct" */ + boolean inrun = false; /* are we in a run of black pixels in row? */ + + _REC_chartable *chartab = &thischarinfo->chartable[thischar]; + _REC_pixeltable *pixtab = &thisfontinfo->pixelptr[chartab->code]; + + if (pixtab->mapadr != 0) + { /* glyph present and non-blank */ + /* check if any part of glyph is visible */ + + vpmyo = chartab->vp - pixtab->yo; + hpmxo = chartab->hp - pixtab->xo; + if (RectangleVisible (vpmyo, vpmyo + pixtab->ht - 1, hpmxo, + hpmxo + pixtab->wd - 1, &top, &bottom, &left, &right)) + { /* glyph edges */ + if (pixtab->bitmap.UU.mptr == (Word *) NULL) + { + if (!fontopen) + { /* dimensions of bitmap */ + if (thisfontinfo->fontexists) + { + if (!OpenFontFile (thisfontinfo->fontspec)) + NotFound (thisfontinfo->fontspec); + } + else + { + NotFound (dummy_pk); /* !!! */ + } + fontopen = true; /* only open font once */ + } + /* bitmap info in font file */ + /* &pixtab->bitmap receives the starting address of bitmap */ + if (!GetBitmap (pixtab->ht, pixtab->wd, pixtab->mapadr, &pixtab->bitmap)) + return false; + } + /* Words in 1 row of bitmap */ + wordsperrow = (pixtab->wd + (WORDSIZE - 1)) / WORDSIZE; + + firstbit = left - hpmxo; /* first visible bit */ + lastbit = right - hpmxo; /* last visible bit */ + firstword = firstbit / WORDSIZE; /* first visible word */ + lastword = lastbit / WORDSIZE; /* last visible word */ + endword = lastword - firstword; + + /* set the visible words in row to 0 */ + for (i = 0; i <= endword; i++) + row[i] = 0; + /* calculate scaled v coord of first visible row */ + scaledv = ScaleVpos (top - windowtop) + windowv; + + /* only consider visible rows; thisrow := top to bottom */ + thisrow = top; + while (true) + { + /* move to first byte of first visible word in this row */ + ptr.UU.int_ = pixtab->bitmap.UU.int_ + + ((thisrow - vpmyo) * wordsperrow + firstword) * 4; + /* get row of visible words from bitmap and OR with row array */ + wordpos = 0; + while (true) + { + row[wordpos] |= *ptr.UU.bptr; /* set union */ + if (wordpos == endword) + break; /* escape from inner "infinite" loop */ + wordpos++; + ptr.UU.int_ += 4; /* next word */ + } + /* calculate scaled v coord of next row */ + scalednextv = ScaleVpos (thisrow - windowtop + 1) + windowv; + scaledheight = scalednextv - scaledv; + if (scaledheight > 0 || thisrow == bottom) + { + /* display black pixels in row, doing any h/v expansion */ + if (scaledheight < 1) /* avoid 0 */ + scaledheight = 1; + inrun = false; + /* bitpos ranges over (WORDSIZE-1)..0 */ + bitpos = (WORDSIZE - 1) - (firstbit & (WORDSIZE - 1)); + wordpos = 0; + + /* only consider visible bits; thisbit := left to right */ + thisbit = left; + while (true) + { /* bit loop */ + if ((unsigned) bitpos < WORDSIZE + && ((1 << bitpos) & row[wordpos]) != 0) + { /* start/continue run */ + if (!inrun) + { /* remember start of run */ + inrun = true; + scaledh = ScaleHpos (thisbit - windowleft) + windowh; + } + } + else if (inrun) + { + inrun = false; + scaledwidth = ScaleHpos (thisbit - windowleft) + windowh - scaledh; + if (scaledwidth < 1) /* avoid 0 */ + scaledwidth = 1; + ShowRectangle (scaledh, scaledv, scaledwidth, scaledheight, + chartab->code); + } + if (thisbit == right) /* EXIT bit loop */ + break; + if (bitpos == 0) + { + wordpos++; + bitpos = (WORDSIZE - 1); + } + else + { /* look at next bit in word */ + bitpos--; + } + thisbit++; + } /* bit loop */ + + if (inrun) + { /* show run at end of row */ + scaledwidth = ScaleHpos (thisbit - windowleft + 1) + windowh - scaledh; + if (scaledwidth < 1) /* avoid 0 */ + scaledwidth = 1; + ShowRectangle (scaledh, scaledv, scaledwidth, scaledheight, + chartab->code); + } + if (thisrow == bottom) /* EXIT row loop */ + break; + /* else reset the visible words in row to 0 */ + for (i = 0; i <= endword; i++) + row[i] = 0; + } /* if */ + scaledv = scalednextv; + thisrow++; + } /* row loop */ + + } /* if */ + /* visible part */ + } + /* 0 bit has ended run */ + return true; +} +/* FullChar */ + + +#undef WORDSIZE +#undef MAXVISWORDS +/*--------------------------------------------------------------------*/ +static void +DisplayChars () +{ + /* Display all characters on a font by font basis. How characters will be + represented depends on the current displaymode (which the user can change, + while the window is being updated, by typing the Terse/Box/Full commands). + Fonts will be displayed in order of ascending totalchars (due to SortFonts). + Characters in a font will be displayed in a top-down, left-right manner + because of the way DVIReader builds a charlist. + */ + + char keyhit; /* check for abort or mode change */ + + for (thisfontinfo = fontlist; + thisfontinfo != unusedfont; + thisfontinfo = thisfontinfo->nextfont) + { + /* SortFont makes sure we only consider used fonts */ + + fontopen = false; /* might be set in FullChar */ + + /* Some VDUs may be able to simulate the given font. + To help the VDU select appropriately sized characters, we need to + pass the scaledsize of the font (converted to unscaled paper pixels), + the overall mag, and the current h/vscalefactors. + */ + + LoadFont (thisfontinfo->fontspec, + XPixelRound (thisfontinfo->scaledsize), + mag / 1000.0, + hscalefactor, + vscalefactor); + + /* display chars in chartable */ + + for (thischarinfo = thisfontinfo->charlist; + thischarinfo != (charinfo *) NULL; + thischarinfo = thischarinfo->nextchar) + { + for (thischar = 0; thischar < thischarinfo->charcount; thischar++) + { + if (thisfontinfo->fontexists) + { + if (displaymode & fullmode) + { + if (thisfontinfo->pkfont) + FullChar (); + else + BoxChar (); + } + if (displaymode & tersemode) + { + TerseChar (); + } + if (displaymode & boxmode) + { + BoxChar (); + } + } + else + { /* font is missing! */ + if (displaymode & (fullmode | tersemode)) + { + TerseChar (); + } + if (displaymode & boxmode) + { + BoxChar (); + } + } + + /* check for abort or mode change */ + if (BusyRead (&keyhit)) + { + char STR1[2]; + keyhit = TOUPPER (keyhit); + sprintf (STR1, "%c", keyhit); + + if (!strcmp (STR1, Terse)) + { + displaymode ^= tersemode; /* toggle terse mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else if (!strcmp (STR1, Box)) + { + displaymode ^= boxmode; /* toggle box mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else if (!strcmp (STR1, Full)) + { + displaymode ^= fullmode; /* toggle full mode */ + StartText (); + UpdateDVIStatusLine (); + StartGraphics (); + } + else if (keyhit == CR) + { + if (fontopen) + CloseFontFile (); + /* no need to set useraborted; DisplayRules done first */ + return; + + } /* if */ + } /* if */ + } /* inner (for) loop */ + } /* middle (for) loop */ + if (fontopen) + CloseFontFile (); + } /* outer (for) loop */ +} +/* DisplayChars */ + +/*--------------------------------------------------------------------*/ +static void +PaperMessage (double precision) +{ + /* Called by CheckPageEdges to remind user of the paper size. */ + + WriteUnits (); + MesgString ("! (Paper "); + WriteXDim (precision, paperwd); + MesgString (" x "); + WriteYDim (precision, paperht); + MesgChar (')'); + ClearMessageLine (); + WaitForReturn (); +} + +/* PaperMessage */ + +/*--------------------------------------------------------------------*/ + +static void +CheckPageEdges () +{ + /* One or more page edges do not fall within the paper edges. + This routine is called after the page & paper have been displayed so + user can see how bad the problem is. + */ + + double precision = 1.0e-2; + + if (minhp < paperleft) + { + ClearMessageLine (); + MesgString ("Page past paper's left edge by "); + WriteXDim (precision, paperleft - minhp); + PaperMessage (precision); + } + if (maxhp > paperright) + { + ClearMessageLine (); + MesgString ("Page past paper's right edge by "); + WriteXDim (precision, maxhp - paperright); + PaperMessage (precision); + } + if (minvp < papertop) + { + ClearMessageLine (); + MesgString ("Page past paper's top edge by "); + WriteYDim (precision, papertop - minvp); + PaperMessage (precision); + } + if (maxvp > paperbottom) + { + ClearMessageLine (); + MesgString ("Page past paper's bottom edge by "); + WriteYDim (precision, maxvp - paperbottom); + PaperMessage (precision); + } +} +/* CheckPageEdges */ +/*--------------------------------------------------------------------*/ +static void +DisplayPage () +{ + /* Display page in window region based on window location and size, + and displaymode. This routine is only called if paintwindow is TRUE + after all commands have been processed. + */ + + if (screenjustcleared) + { + /* avoid doing it again */ + + if (paintDVIStatus) + UpdateDVIStatusLine (); + + if (paintWindowStatus) + UpdateWindowStatusLine (); + } + else + { + ClearScreen (); + screenjustcleared = true; + UpdateDVIStatusLine (); + UpdateWindowStatusLine (); + } + + StartGraphics (); + DisplayPaperEdges (); + StartText (); + + if (pageempty) + { + ClearMessageLine (); + MesgString ("Page is empty."); + } + else if (outsidepage) + { + if (pageoffpaper) + CheckPageEdges (); + + ClearMessageLine (); + MesgString ("Window is "); + + if (windowtop > maxvp) + { + MesgString ("below "); + if (windowleft > maxhp || windowleft <= minhp - scaledwd) + MesgString ("and "); + } + else if (windowtop <= minvp - scaledht) + { + MesgString ("above "); + if (windowleft > maxhp || windowleft <= minhp - scaledwd) + MesgString ("and "); + } + if (windowleft > maxhp) + MesgString ("to the right of "); + else if (windowleft <= minhp - scaledwd) + MesgString ("to the left of "); + + MesgString ("page."); + } + else + { + /* Page is not empty and part or all of it is visible. */ + StartGraphics (); + + useraborted = false; + DisplayRules (); + if (!useraborted) + DisplaySpecials (); + if (!useraborted) + DisplayChars (); + + StartText (); + + if (pageoffpaper) + CheckPageEdges (); /* May write messages */ + + if (allpagevisible) + { + ClearMessageLine (); + MesgString ("Entire page is visible. "); + PadMesg (); + } + } + + MesgFlush (); +} +/* DisplayPage */ +/*--------------------------------------------------------------------*/ +void +ProcessCommandLine (String commstring) +{ + /* Parse commstring, and call the appropriate command handler + for each command in commstring. + */ + int n; /* returned by GetInteger call */ + + ClearMessageLine (); /* erase message line at this stage */ + commlen = strlen (commstring); + /* ignore any trailing spaces */ + while (commlen > 0 && commstring[commlen - 1] == ' ') + commlen--; + /* terminate commstring properly */ + commstring[commlen] = '\0'; + + /* initialize flags for multiple command processing */ + badcommand = false; + paintWindowStatus = false; + paintDVIStatus = false; + paintwindow = false; + screenjustcleared = false; + pageoffpaper = false; + + commpos = 0; + while (commpos < commlen && !badcommand) + { + /* next command is defined by the next non-space character in commstring */ + while (commstring[commpos] == ' ') + commpos++; /* ignore any leading spaces */ + + command = TOUPPER (commstring[commpos]); + switch (command) + { + case 'W': /* Window move */ + commpos++; + WindowMove (); + if (!badcommand) + paintWindowStatus = true; + break; + + case 'U': /* move Up */ + case 'D': /* move Down */ + commpos++; + WindowUpDown (); + paintWindowStatus = true; + break; + + case 'L': /* move Left */ + case 'R': /* move Right */ + commpos++; + WindowLeftRight (); + paintWindowStatus = true; + break; + + case 'H': /* Horizontal dimension */ + commpos++; + SetWindowWidth (); + NewLocation (windowleft, windowtop); + paintWindowStatus = true; + break; + + case 'V': /* Vertical dimension */ + commpos++; + SetWindowHeight (); + NewLocation (windowleft, windowtop); + paintWindowStatus = true; + break; + + case 'A': /* toggle Autoview on or off */ + commpos++; + SetAutoView (); + if (!badcommand) + paintDVIStatus = true; + break; + + case 'Z': /* Zoom in or out */ + commpos++; + ZoomWindow (); + if (!badcommand) + NewLocation (windowleft, windowtop); + if (!badcommand) + paintWindowStatus = true; + break; + + case 'N': /* Next page */ + commpos++; + if (NextPageFound (true)) /* ascending */ + ProcessPage (); + break; + + case 'P': /* Previous page */ + commpos++; + if (NextPageFound (false)) /* descending */ + ProcessPage (); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* go to page with this number, if number is valid */ + + if (GetInteger (commstring, commlen, &commpos, &n)) + { + /* must be true, and commpos now after last digit */ + if (DVIPageFound (n)) + ProcessPage (); + } + else + { /* safety net */ + ClearMessageLine (); + MesgString ("Expected an Integer! Type ? for help."); + BadCommandMessage (); + } + break; + + case '[': /* go to page described by count0...count9 */ + if (TeXPageFound ()) + /* commpos incremented in ParseTeXpage */ + ProcessPage (); + break; + + case 'T': /* Terse display */ + commpos++; + displaymode ^= tersemode; /* toggle terse mode */ + paintDVIStatus = true; + if (currDVIpage != 0) + paintwindow = true; + break; + + case 'B': /* Box display */ + commpos++; + displaymode ^= boxmode; /* toggle box mode */ + paintDVIStatus = true; + if (currDVIpage != 0) + paintwindow = true; + break; + + case 'F': /* Full display */ + commpos++; + displaymode ^= fullmode; /* toggle full mode */ + paintDVIStatus = true; + if (currDVIpage != 0) + paintwindow = true; + break; + + case 'C': /* Change units */ + commpos++; + ChangeUnits (); + if (!badcommand) + paintWindowStatus = true; + break; + + case '?': /* command help */ + commpos++; + ShowCmdHelp (); + break; + + case 'S': /* file-and-page Statistics */ + commpos++; + ShowStatistics (); + ClearScreen (); + screenjustcleared = true; + paintDVIStatus = true; + paintWindowStatus = true; + if (currDVIpage != 0) + paintwindow = true; + break; + + case '\014': /* ctrl-L : refresh screen */ + commpos++; + DisplayPage (); /* unconditionally, and immediately on interpretation */ + break; + + case '\021': /* ctrl-Q : ignore flow-control continue */ + case '\023': /* ctrl-S : ignore flow-control suspend */ + /* do nothing, except advance character pointer */ + commpos++; + break; + + case 'Q': /* Quit */ + return; + + break; + + default: /* unknown command */ + commpos++; + ClearMessageLine (); + MesgString ("Unknown command! Type ? for help."); + BadCommandMessage (); + break; + } /* command */ + } /* while */ + + if (paintwindow) /* only update window after processing all commands */ + DisplayPage (); + else if (!vdu_clears_lines && (paintDVIStatus || paintWindowStatus)) + DisplayPage (); + else + { + if (paintDVIStatus) + UpdateDVIStatusLine (); + if (paintWindowStatus) + UpdateWindowStatusLine (); + } +} +/* ProcessCommandLine */ + +/*--------------------------------------------------------------------*/ + +static void +NextCommandLine () +{ + /* Prompt user for next command line, and process response */ + + ClearTextLine (commandl); + MoveToTextLine (commandl); + + MesgString (commprompt); + MesgFlush (); + + ReadString (commstring); /* read new command line */ + + ProcessCommandLine (commstring); +} + +/* NextCommandLine */ + +/*--------------------------------------------------------------------*/ +static void +Finish () +{ + /* close DVI file, reset VDU and restore terminal */ + + CloseDVIFile (); + + /*!!! ClearScreen(); */ + screenjustcleared = true; + MoveToTextLine (1); + MesgLine (); + /*!!! ResetVDU(); */ +} + +/* Finish */ + +/*--------------------------------------------------------------------*/ +void * +Malloc (size_t n) +{ + void *MallocTemp; + MallocTemp = malloc (n); + if (MallocTemp) + { + return MallocTemp; + } + else + { + StartText (); + ResetVDU (); /* do before message since it might erase screen! */ + FATAL ("Out of memory."); + return NULL; + } +} +/*--------------------------------------------------------------------*/ + +int +main (int argc, char **argv) +{ + char STR1[2]; + + kpse_set_progname (argv[0]); + kpse_init_prog ("DVGT", DEF_XRES, DEF_MFMODE, DEF_DUMMY); + kpse_set_program_enabled (kpse_pk_format, true, kpse_src_compile); + + save_init_tty (); /* save initial terminal settings, early */ + textlinewidth = 80; /* for the initial screen type */ + if (!InitOptions (argc, argv)) + { /* initialize DVIname and command options */ + /* Print the precompiled information */ + PrintText (immed_help_strings); + return 1; + } + InitScreenIO (); /* SYSDEP: sets cbreak on & echo off */ + + InitDVIReader (); + InitFontReader (); + OpenDVIFile (DVIname); /* and read DVImag etc. */ + if (mag == 0) /* use DVImag */ + mag = DVImag; + SetConversionFactors (xres, yres, mag / 1000.0); /* for DVIReader */ + InitVDU (); /* init windowwd/ht etc. */ + InitWinVars (); + StartText (); + ClearScreen (); + screenjustcleared = true; + UpdateDVIStatusLine (); + UpdateWindowStatusLine (); + MoveToTextLine (messagel); + MesgString (DVGT_VERSION); + MesgLine (); + do + { + NextCommandLine (); /* parse and execute command(s) */ + } + while (strcmp ((sprintf (STR1, "%c", command), STR1), Quit)); + Finish (); + return 0; /* OK */ +} +/* main() program of DVItoVDU */ + +/* end dvitovdu.c */ |